跳到主要内容

布比区块链JavaScript智能合约是一段JavaScript代码,代码标准(ECMAScript as specified in ECMA-262)。

本章节旨在为开发者提供如何使用JavaScript编程语言来编写和部署智能合约的指南。介绍基础的编程概念、智能合约的结构,以及如何通过JavaScript与区块链平台交互。

JavaScript智能合约的优点主要包括:

  1. 广泛的使用基础:JavaScript是世界上使用最广泛的编程语言之一,许多开发者已经熟悉这种语言。这降低了学习成本,使得开发者可以更快地开始编写智能合约。
  2. 生态系统支持:由于JavaScript的普及,有大量的开发工具、库和框架可以支持智能合约的开发。这包括测试框架、安全工具和其他可以提升开发效率和合约安全的资源。

通过整合这些优势,JavaScript智能合约成为了一个吸引开发者进入区块链领域的有力工具。章节中还会通过示例代码和最佳实践来具体展示如何利用JavaScript的这些优势来构建有效且安全的智能合约。

1. 使用说明

1.1. 介绍

读者对象:本章节主要描述使用JavaScript进行布比区块链合约编写的方法,主要面向于使用JavaScript进行布比区块链的合约开发的开发者。

合约的初始化函数是 init, 执行的入口函数是 main 函数,查询接口是 query。这些函数的参数字符串 input,是调用该合约的时候指定的。

1.2. 示例

下面是一个简单的JavaScript智能合约示例:

"use strict";
function init(input)
{
/*init whatever you want*/
Chain.store('init','initValue');
}

function main(input)
{
let inputObj = JSON.parse(input);
if (inputObj.method === 'save')
{
Chain.store(inputObj.params.key,inputObj.params.value);
return;
}
Utils.assert(true, "Not valid");
}

function query(input)
{
let inputObj = JSON.parse(input);
if (inputObj.method === 'load')
{
let data = Chain.load(inputObj.params.key);
if (data === false) {
return '';
}
return data;
}
Utils.assert(true, "Not valid");
}

1.3. 部署

环境准备

  1. curl命令行工具或其他能发送http请求的工具,用于http请求;
  2. 一个节点的http接口访问权限,这里以127.0.0.1:19333为例,实际部署操作时请替换为实际IP端口;
  3. 一个已经激活的区块链账号私钥,这里以下面账号为例:
    • 账号私钥:privbwQsGDqGRYuvkFAEQdS8KWxVTA3ynzMxpwLHTtFsy1eySSPTRCmt;
    • 账号地址:adxSdqkc8ooYmthwbiCsNogfyRLH51vh4V75P。

准备工作

查询账号交易序号,使用查询命令:

curl http://127.0.0.1:19333/getAccount?address=adxSdqkc8ooYmthwbiCsNogfyRLH51vh4V75P

结果应该返回error_code为0,然后我们在result字段下查询nonce字段信息,如果在返回结果中没有nonce字段,则代表账号从未发送过交易,表示nonce为0;返回结果示例如下:

{
"error_code" : 0,
"result" : {
"address" : "adxSdqkc8ooYmthwbiCsNogfyRLH51vh4V75P",
"assets" : null,
"assets_hash" : "ad67d57ae19de8068dbcd47282146bd553fe9f684c57c8c114453863ee41abc3",
"balance" : 998989318000,
"metadatas" : null,
"metadatas_hash" : "ad67d57ae19de8068dbcd47282146bd553fe9f684c57c8c114453863ee41abc3",
"nonce" : 1,
"priv" : {
"master_weight" : 1,
"thresholds" : {
"tx_threshold" : 1
}
}
}
}

部署流程

  1. 准备交易内容模板,将下面的交易内容保存到tx-deploy-js.json文件,注意:需要把nonce参数+1,表示账号的下一笔交易;

    {
    "items": [
    {
    "transaction_json": {
    "source_address": "adxSdqkc8ooYmthwbiCsNogfyRLH51vh4V75P",
    "nonce": 2,
    "fee_limit":1500000000,
    "gas_price":1000,
    "operations": [{
    "type": 1,
    "create_account":{
    "contract":{
    "owner": "adxSdqkc8ooYmthwbiCsNogfyRLH51vh4V75P",
    "type": 0,
    "payload": ""
    },
    "init_balance": 10000000,
    "init_input" : "",
    "priv":{
    "master_weight": 0,
    "thresholds": {
    "tx_threshold": 1
    }
    }
    }
    }]
    },
    "private_keys": ["privbwQsGDqGRYuvkFAEQdS8KWxVTA3ynzMxpwLHTtFsy1eySSPTRCmt"]
    }
    ]
    }
  2. 整理交易提交内容,打开 https://jsonformatter.org/json-stringify-online 网站,把JavaScript示例合约内容粘贴在左侧,点击JSON Stringify按钮,右侧会生成转义字符串,示例如下:

    "\"use strict\";\r\nfunction init(input)\r\n{\r\n    /*init whatever you want*/\r\n    Chain.store('init','initValue');\r\n}\r\n\r\nfunction main(input)\r\n{\r\n    let inputObj = JSON.parse(input);\r\n    if (inputObj.method === 'save')\r\n    {\r\n        Chain.store(inputObj.params.key,inputObj.params.value);\r\n        return;\r\n    }\r\n    Utils.assert(true, \"Not valid\");\r\n}\r\n\r\nfunction query(input)\r\n{\r\n    let inputObj = JSON.parse(input);\r\n    if (inputObj.method === 'load')\r\n    {\r\n        let data = Chain.load(inputObj.params.key);\r\n        if (data === false) {\r\n            return '';\r\n        }\r\n        return data;\r\n    }\r\n    Utils.assert(true, \"Not valid\");\r\n}"

    将上述字符串替换tx-deploy-js.json文件中的payload对应的数据部分,然后另存为tx-deploy-js-send.json文件,替换后的内容示例如下:

    {
    "items": [
    {
    "transaction_json": {
    "source_address": "adxSdqkc8ooYmthwbiCsNogfyRLH51vh4V75P",
    "nonce": 2,
    "fee_limit":1500000000,
    "gas_price":1000,
    "operations": [{
    "type": 1,
    "create_account":{
    "contract":{
    "owner": "adxSdqkc8ooYmthwbiCsNogfyRLH51vh4V75P",
    "type": 0,
    "payload": "\"use strict\";\r\nfunction init(input)\r\n{\r\n /*init whatever you want*/\r\n Chain.store('init','initValue');\r\n}\r\n\r\nfunction main(input)\r\n{\r\n let inputObj = JSON.parse(input);\r\n if (inputObj.method === 'save')\r\n {\r\n Chain.store(inputObj.params.key,inputObj.params.value);\r\n return;\r\n }\r\n Utils.assert(true, \"Not valid\");\r\n}\r\n\r\nfunction query(input)\r\n{\r\n let inputObj = JSON.parse(input);\r\n if (inputObj.method === 'load')\r\n {\r\n let data = Chain.load(inputObj.params.key);\r\n if (data === false) {\r\n return '';\r\n }\r\n return data;\r\n }\r\n Utils.assert(true, \"Not valid\");\r\n}"
    },
    "init_balance": 10000000,
    "init_input" : "",
    "priv":{
    "master_weight": 0,
    "thresholds": {
    "tx_threshold": 1
    }
    }
    }
    }]
    },
    "private_keys": ["privbwQsGDqGRYuvkFAEQdS8KWxVTA3ynzMxpwLHTtFsy1eySSPTRCmt"]
    }
    ]
    }

    提交交易,把上一步整理好的tx-deploy-js-send.json文件作为POST请求的参数提交到节点:

    curl -X "POST" --data-binary "@tx-deploy-js-send.json" http://127.0.0.1:19333/submitTransaction

    示例返回:

    {
    "results" : [
    {
    "error_code" : 0,
    "error_desc" : "",
    "hash" : "1b9eb37d9a4047381feffe04795eadc391bec9f719e1f9016d551a6cd5a109d3"
    }
    ],
    "success_count" : 1
    }

    提交成功返回交易交易1b9eb37d9a4047381feffe04795eadc391bec9f719e1f9016d551a6cd5a109d3

  3. 查询交易状态,向节点提交交易查询请求:

    curl http://127.0.0.1:19333/getTransactionHistory?hash=1b9eb37d9a4047381feffe04795eadc391bec9f719e1f9016d551a6cd5a109d3

    示例返回:

    {"error_code":0,"result":{"total_count":1,"transactions":[{"actual_fee":1001062000,"close_time":1712752134736008,"error_code":0,"error_desc":"[{\"contract_address\":\"adxSTDkyymx2aumaG167ma1YdPtxgorVe5mxx\",\"contract_evm_address\":\"0x01fc7e852ae563cab387bf7034a16f04891b0ddc\",\"operation_index\":0}]","hash":"1b9eb37d9a4047381feffe04795eadc391bec9f719e1f9016d551a6cd5a109d3","ledger_seq":201,"signatures":[{"public_key":"b0011ced68075723ec3bd5db6f7fb4faba33f00f33e374a53806253160bc8f708e8933530369","sign_data":"cb4ca805c14ec7ce2d263f396e0b8280beeedfeada9c057cb3ed067c506f14b444a33cd79a396201f3601f8439178d454955ace0bd1a214433b90d62f871e903"},{"public_key":"b00132c3d1244e0be4d711fb12d5b3d24abeb60f11e8c61c5efe28ad20b793d7fb2402f819e9","sign_data":"79bcd021d90d2411779814311c6927ede6a83bc70f61971ad81f3f3a9581eea924fade599f94fa6dfad7c614ed252c97e5163b2f5877226d8f5f973de5ad3101"}],"transaction":{"fee_limit":1500000000,"gas_price":1000,"nonce":2,"operations":[{"create_account":{"contract":{"owner":"adxSdqkc8ooYmthwbiCsNogfyRLH51vh4V75P","payload":"\"use strict\";\r\nfunction init(input)\r\n{\r\n    /*init whatever you want*/\r\n    Chain.store('init','initValue');\r\n}\r\n\r\nfunction main(input)\r\n{\r\n    let inputObj = JSON.parse(input);\r\n    if (inputObj.method === 'save')\r\n    {\r\n        Chain.store(inputObj.params.key,inputObj.params.value);\r\n        return;\r\n    }\r\n    Utils.assert(true, \"Not valid\");\r\n}\r\n\r\nfunction query(input)\r\n{\r\n    let inputObj = JSON.parse(input);\r\n    if (inputObj.method === 'load')\r\n    {\r\n        let data = Chain.load(inputObj.params.key);\r\n        if (data === false) {\r\n            return '';\r\n        }\r\n        return data;\r\n    }\r\n    Utils.assert(true, \"Not valid\");\r\n}"},"init_balance":10000000,"priv":{"thresholds":{"tx_threshold":1}}},"type":1}],"source_address":"adxSdqkc8ooYmthwbiCsNogfyRLH51vh4V75P"},"tx_size":1062}]}}

    交易执行成功的话,可以在error_desc下的contract_address字段找到新创建的合约账号地址adxSTDkyymx2aumaG167ma1YdPtxgorVe5mxx,实际的合约地址以实际交易返回结果为准。

1.4. 调用

前置条件

  1. curl命令行工具,用于http请求;
  2. 一个节点的http接口访问权限,这里以127.0.0.1:19333为例,实际部署操作时请替换为实际IP端口;
  3. 一个已经激活的区块链账号私钥,这里以下面账号为例:
    • 账号私钥:privbwQsGDqGRYuvkFAEQdS8KWxVTA3ynzMxpwLHTtFsy1eySSPTRCmt;
    • 账号地址:adxSdqkc8ooYmthwbiCsNogfyRLH51vh4V75P。

准备工作

查询账号交易序号,使用查询命令:

curl http://127.0.0.1:19333/getAccount?address=adxSdqkc8ooYmthwbiCsNogfyRLH51vh4V75P

结果应该返回error_code为0,然后我们在result字段下查询nonce字段信息,如果在返回结果中没有nonce字段,则代表账号从未发送过交易,表示nonce为0;返回结果示例如下:

{
"error_code" : 0,
"result" : {
"address" : "adxSdqkc8ooYmthwbiCsNogfyRLH51vh4V75P",
"assets" : null,
"assets_hash" : "ad67d57ae19de8068dbcd47282146bd553fe9f684c57c8c114453863ee41abc3",
"balance" : 997978256000,
"metadatas" : null,
"metadatas_hash" : "ad67d57ae19de8068dbcd47282146bd553fe9f684c57c8c114453863ee41abc3",
"nonce" : 2,
"priv" : {
"master_weight" : 1,
"thresholds" : {
"tx_threshold" : 1
}
}
}
}

调用流程

  1. 准备合约调用参数

    {
    "method":"save",
    "params":{
    "key":"testKey",
    "value":"testValue"
    }
    }

    打开 https://www.json.cn/jsonzip/ 网站, 把上述调用参数复制到输入框中,点击压缩并转义,生成如下字符串:

    {\"method\":\"save\",\"params\":{\"key\":\"testKey\",\"value\":\"testValue\"}}
  2. 准备交易内容,上一步得到的调用参数填入input参数字段,将下面的交易内容保存到tx-call-js.json文件,注意:需要把nonce参数+1,表示账号的下一笔交易;

    {
    "items": [
    {
    "transaction_json": {
    "source_address": "adxSdqkc8ooYmthwbiCsNogfyRLH51vh4V75P",
    "nonce": 3,
    "fee_limit":1500000000,
    "gas_price":1000,
    "operations": [{
    "pay_coin": {
    "amount": 0,
    "dest_address": "adxSTDkyymx2aumaG167ma1YdPtxgorVe5mxx",
    "input": "{\"method\":\"save\",\"params\":{\"key\":\"testKey\",\"value\":\"testValue\"}}"
    },
    "type": 7
    }]
    },
    "private_keys": ["privbwQsGDqGRYuvkFAEQdS8KWxVTA3ynzMxpwLHTtFsy1eySSPTRCmt"]
    }
    ]
    }
  3. 提交交易,把上一步整理好的tx-call-js.json文件作为POST请求的参数提交到节点

    curl -X "POST" --data-binary "@tx-call-js.json" http://127.0.0.1:19333/submitTransaction

    示例返回:

    {
    "results" : [
    {
    "error_code" : 0,
    "error_desc" : "",
    "hash" : "21adbf23961c49858fd344317eaf788d11366682e752ef81ce58dfe12fe75f1c"
    }
    ],
    "success_count" : 1
    }

    提交成功返回交易交易21adbf23961c49858fd344317eaf788d11366682e752ef81ce58dfe12fe75f1c

  4. 查询交易状态,向节点提交交易查询请求:

    curl http://127.0.0.1:19333/getTransactionHistory?hash=21adbf23961c49858fd344317eaf788d11366682e752ef81ce58dfe12fe75f1c

    示例返回:

    {"error_code":0,"result":{"total_count":1,"transactions":[{"actual_fee":458000,"close_time":1712754893172708,"error_code":0,"error_desc":"","hash":"21adbf23961c49858fd344317eaf788d11366682e752ef81ce58dfe12fe75f1c","ledger_seq":247,"signatures":[{"public_key":"b0011ced68075723ec3bd5db6f7fb4faba33f00f33e374a53806253160bc8f708e8933530369","sign_data":"7e5d2ae975faf43bd0f18240230a95eac068e7032ae13e62bc4a710d3ab2d6f08699d6cdf533aebafd93a2ff8da6db330c5eb45d6db627988a750066c0e7a104"},{"public_key":"b00132c3d1244e0be4d711fb12d5b3d24abeb60f11e8c61c5efe28ad20b793d7fb2402f819e9","sign_data":"ac942a193c5e57b35c9fc1460b920d6c042a1687539b6227ebf3f2885086940bda25ecff7bdefbefd9cd5472744041118046a293da73398187804d15d72d2003"}],"transaction":{"fee_limit":1500000000,"gas_price":1000,"nonce":3,"operations":[{"pay_coin":{"dest_address":"adxSTDkyymx2aumaG167ma1YdPtxgorVe5mxx","input":"{\"method\":\"save\",\"params\":{\"key\":\"testKey\",\"value\":\"testValue\"}}"},"type":7}],"source_address":"adxSdqkc8ooYmthwbiCsNogfyRLH51vh4V75P"},"tx_size":458}]}}

    交易执行成功的话,返回数据的error_code应该为0。

1.5. 查询

前置条件

  1. curl命令行工具,用于http请求;
  2. 一个节点的http接口访问权限,这里以127.0.0.1:19333为例,实际部署操作时请替换为实际IP端口;

查询流程

  1. 准备合约查询参数

    {
    "method":"load",
    "params":{
    "key":"testKey"
    }
    }

    打开 https://www.json.cn/jsonzip/ 网站, 把上述调用参数复制到输入框中,点击压缩并转义,生成如下字符串:

    {\"method\":\"load\",\"params\":{\"key\":\"testKey\"}}
  2. 准备参数内容模板,上一步得到的合约查询参数填入input字段,将下面的交易内容保存到tx-query-js.json文件;

    {
    "contract_address" : "adxSTDkyymx2aumaG167ma1YdPtxgorVe5mxx",
    "code" : "",
    "input" : "{\"method\":\"load\",\"params\":{\"key\":\"testKey\"}}",
    "contract_balance" : "0",
    "fee_limit" : 100000000000000000,
    "gas_price": 1000,
    "opt_type" : 2,
    "source_address" : ""
    }
  3. 提交交易,把上一步整理好的tx-query-js.json文件作为POST请求的参数提交到节点

    curl -X "POST" --data-binary "@tx-query-js.json" http://127.0.0.1:19333/callContract

    返回结果示例:

    {
    "error_code" : 0,
    "error_desc" : "",
    "result" : {
    "logs" : {
    "0-adxSTDkyymx2aumaG167ma1YdPtxgorVe5mxx" : null
    },
    "query_rets" : [
    {
    "result" : {
    "type" : "string",
    "value" : "testValue"
    }
    }
    ],
    "stat" : {
    "apply_time" : 2012,
    "memory_usage" : 1832920,
    "stack_usage" : 16,
    "step" : 109
    },
    "txs" : null
    }
    }

    调用合约返回的数据在query_rets字段下。

2. 语法介绍

2.1. 接口对象

布比区块链智能合约内提供了全局对象 ChainUtils, 这两个对象提供了多样的方法和变量,可以获取区块链的一些信息,也可驱动账号发起所有交易,除了设置门限和权重这两种类型的操作。

注意,自定义的变量不要与内置对象重复,否则会造成不可控的数据错误。

使用方法如下:

对象.方法(变量)

示例:

  • 获取账号余额:Chain.getBalance('adxSZ7HNR1Nk8D8wm6pkcWNb6rnMZncyTVHSb');
  • 打印日志:Utils.log('hello');
  • 当前区块号:Chain.block.number;

读写权限:

对象里的每个函数都有固定的只读或者可写权限

只读权限是指不会写数据到区块链的接口函数,比如获取余额 Chain.getBalance

可写权限是指会写数据到区块链的接口函数,比如转账 Chain.payCoin

在编写智能合约的时候,需要注意的是不同的入口函数拥有不同的调用权限

initmain 能调用所有的内置函数

query 只能调用只读权限的函数,否则在调试或者执行过程中会提示接口未定义

返回值介绍:

所有内部函数的调用,如果失败则返回 false 或者直接抛出异常执行终止,成功则为其他对象。如果遇到参数错误,会在错误描述中提示参数位置出错,这里的位置指参数的索引号,即从 0 开始计数。例如 parameter 1 表示第 2 个参数错误。如下例子:

Chain.issueAsset("CNY", 10000);
/*
错误描述:
Contract execute error,issueAsset parameter 1 should be a string

指第 2 个参数应该为字符串
*/

2.2. 异常处理

  • JavaScript异常

    当合约运行中出现未捕获的JavaScript异常时,处理规定:

    1. 本次合约执行失败,合约中做的所有交易都不会生效。
    2. 触发本次合约的这笔交易为失败。错误代码为151
  • 执行交易失败

    合约中可以执行多个交易,只要有一个交易失败,就会抛出异常,导致整个交易失败

3. 合约API

3.1. Chain.load

获取合约账号的metadata信息

Chain.load(metadata_key);

  • metadata_key: metadata的key
let value = Chain.load('abc');
/*
权限:只读
返回:成功返回字符串,如 'values', 失败返回false
*/

即可得到本合约账号中自定数据的abc的值

3.2. Chain.store

存储合约账号的metadata信息

Chain.store(metadata_key, metadata_value);

  • metadata_key: metadata 的 key
  • metadata_key: metadata 的 value
Chain.store('abc', 'values');
/*
权限:可写
返回:成功返回true, 失败抛异常
*/

3.3. Chain.del

删除合约账号的metadata信息

Chain.del(metadata_key);

  • metadata_key: metadata的key
Chain.del('abc');
/*
权限:可写
返回:成功返回true, 失败抛异常
*/

即可删除本合约账号中自定数据的abc的值

3.4. Chain.getBlockHash

获取区块信息

Chain.getBlockHash(offset_seq);

  • offset_seq: 距离最后一个区块的偏移量,范围:[0,1024)

    例如

    let ledger = Chain.getBlockHash(4);
    /*
    权限:只读
    返回:成功返回字符串,如 'c2f6892eb934d56076a49f8b01aeb3f635df3d51aaed04ca521da3494451afb3',失败返回 false
    */

3.5. Chain.tlog

输出交易日志

Chain.tlog(topic,args...);

  • tlog会产生一笔交易写在区块上

  • topic: 日志主题,必须为字符串类型,参数长度(0,128]

  • args...: 最多可以包含5个参数,参数类型可以是字符串、数值或者布尔类型,每个参数长度(0,1024]

    例如

    Chain.tlog('transfer',sender +' transfer 1000',true);
    /*
    权限:可写
    返回:成功返回 true,失败抛异常
    */

3.6. Chain.getAccountMetadata

获取指定账号的metadata

Chain.getAccountMetadata(account_address, metadata_key);

  • account_address: 账号地址

  • metadata_key: metadata的key

    例如

    let value = Chain.getAccountMetadata('adxSZ7HNR1Nk8D8wm6pkcWNb6rnMZncyTVHSb', 'abc');

    /*
    权限:只读
    返回:成功返回字符串,如 'values', 失败返回false
    */

3.7. Chain.getBalance

获取账号coin amount

Chain.getBalance(address);

  • address: 账号地址

    例如

    let balance = Chain.getBalance('adxSZ7HNR1Nk8D8wm6pkcWNb6rnMZncyTVHSb');
    /*
    权限:只读
    返回:字符串格式数字 '9999111100000'
    */

3.8. Chain.getAccountAsset

获取某个账号的资产信息

Chain.getAccountAsset(account_address, asset_key);

  • account_address: 账号地址

  • asset_key: 资产属性

    例如

    let asset_key =
    {
    'issuer' : 'adxSZ7HNR1Nk8D8wm6pkcWNb6rnMZncyTVHSb',
    'code' : 'CNY'
    };
    let bar = Chain.getAccountAsset('adxSZ7HNR1Nk8D8wm6pkcWNb6rnMZncyTVHSb', asset_key);

    /*
    权限:只读
    返回:成功返回资产数字如'10000',失败返回 false
    */

3.9. Chain.getAccountPrivilege

获取某个账号的权限信息

Chain.getAccountPrivilege(account_address);

  • account_address: 账号地址

    例如

    let privilege = Chain.getAccountPrivilege('adxSZ7HNR1Nk8D8wm6pkcWNb6rnMZncyTVHSb');

    /*
    权限:只读
    返回:成功返回权限json字符串如'{"master_weight":1,"thresholds":{"tx_threshold":1}}',失败返回 falses
    */

3.10. Chain.getContractProperty

获取合约账号属性

Chain.getContractProperty(contract_address);

  • contract_address: 合约地址

    例如

    let value = Chain.getContractProperty('adxSonEfJM33jCpLHru87dcfeRfB9kdqdzzat');

    /*
    权限:只读
    返回:成功返回JSON对象,如 {"type":0, "length" : 416}, type 指合约类型, length 指合约代码长度,如果该账户不是合约则,length 为0.
    失败返回false
    */

3.11. Chain.payCoin

转账

Chain.payCoin(address, amount[, input], [, metadata]);

  • address: 发送Gas的目标地址

  • amount: 发送Gas的数量

  • input: 可选,合约参数,如果用户未填入,默认为空字符串

  • metadata: 可选,转账备注,调用GET /getTransactionHistory方法查询交易结果时,显示为十六进制字符串,需要转换为明文。

    注意,如果提供metadata参数,那么也必须提供input参数,否则内置接口无法区分该参数是谁,因为两者都是可选的。如果没有input,可以传入空字符串""占位,以防止内置接口将metadata参数误认为inut参数。

    例如

    Chain.payCoin("adxSZ7HNR1Nk8D8wm6pkcWNb6rnMZncyTVHSb", "10000", "", "vote reward");
    /*
    权限:可写
    返回:成功返回 true,失败抛异常
    */

3.12. Chain.issueAsset

发行资产

Chain.issueAsset(code, amount);

  • code: 资产代码

  • amount: 发行资产数量

    例如

    Chain.issueAsset("CNY", "10000");
    /*
    权限:可写
    返回:成功返回 true,失败抛异常
    */

3.13. Chain.payAsset

转移资产

Chain.payAsset(address, issuer, code, amount[, input]);

  • address: 转移资产的目标地址

  • issuer: 资产发行方

  • code: 资产代码

  • amount: 转移资产的数量

  • input: 可选,合约参数,如果用户未填入,默认为空字符串

  • metadata: 可选,转账备注,调用GET /getTransactionHistory方法查询交易结果时,显示为十六进制字符串,需要转换为明文。

    注意,如果提供metadata参数,那么也必须提供input参数,否则内置接口无法区分该参数是谁,因为两者都是可选的。如果不需要input,可以传入空字符串""占位,以防止内置接口将metadata参数误认为inut参数。

    例如

    Chain.payAsset("adxSZ7HNR1Nk8D8wm6pkcWNb6rnMZncyTVHSb", "adxSio3m7C7D84GzgmtTXanUNMwPezys9azQB", "CNY", "10000", "", "Trading certificate");
    /*
    权限:可写
    返回:成功返回 true,失败抛异常
    */

3.14. Chain.delegateCall

委托调用

Chain.delegateCall(contractAddress, input);

  • contractAddress: 被调用的合约地址。

  • input:调用参数。

    Chain.delegateCall 函数会触发被调用的合约main函数入口,并且把当前合约的执行环境赋予被调用的合约。如合约A委托调用合约B,即执行B(main入口)的代码,读写A的数据。

    例如

    let ret = Chain.delegateCall('adxSd6Qg8UFayWWuzLjMwJUt6f7oTz5GJEjDj''{}');
    /*
    权限:可写
    返回:成功会返回被委托者合约main函数执行的结果,失败抛出异常
    */

3.15. Chain.delegateQuery

委托查询

Chain.delegateQuery(contractAddress, input);

  • contractAddress: 被调用的合约地址。

  • input:调用参数。

    Chain.delegateQuery 函数会触发被调用的合约query函数入口,且把当前合约的执行环境赋予被调用的合约。如合约A委托查询合约B,即执行B(query入口)的代码,读取A的数据。

    例如

    let ret = Chain.delegateQuery('adxSd6Qg8UFayWWuzLjMwJUt6f7oTz5GJEjDj'"");
    /*
    权限:只读
    返回:调用成功则返回JSON对象 {"result":"4"},其中 result 字段的值即查询的具体结果,调用失败返回JSON对象 {"error":true} 。
    */

3.16. Chain.contractCall

调用合约

Chain.contractCall(contractAddress, asset, amount, input);

  • contractAddress: 被调用的合约地址。

  • asset : 资产类别,true代表Gas,对象{"issue": adxxxx, "code" : USDT} 代表资产。

  • amount: 资产数量。

  • input:调用参数。

    Chain.contractCall 函数会触发被调用的合约main函数入口。

    例如

    let ret = Chain.contractCall('adxSd6Qg8UFayWWuzLjMwJUt6f7oTz5GJEjDj'true, toBaseUnit("10"), "");
    /*
    权限:可写
    返回:如果目标账户为普通账户,则返回true,如果目标账户为合约,成功会返回被委托者合约main函数执行的结果,调用失败则抛出异常
    */

3.17. Chain.contractQuery

查询合约

Chain.contractQuery(contractAddress, input);

  • contractAddress: 被调用的合约地址。

  • input:调用参数。

    Chain.contractQuery 会调用合约的查询接口

    例如

    let ret = Chain.contractQuery('adxSd6Qg8UFayWWuzLjMwJUt6f7oTz5GJEjDj'"");
    /*
    权限:只读
    返回:调用成功则返回JSON对象 {"result":"xxx"},其中 result 字段的值即查询的具体结果,调用失败返回JSON对象 {"error":true}。
    */

3.18. Chain.contractCreate

创建合约

Chain.contractCreate(balance, type, code, input);

  • balance: 字符串类型,转移给被创建的合约的资产。

  • type : 整型,0代表javascript。

  • code: 字符串类型, 合约代码。

  • input:init函数初始化参数。

    Chain.contractCreate 创建合约。

    例如

    let ret = Chain.contractCreate(toBaseUnit("10"), 0, "'use strict';function init(input){return input;} function main(input){return input;} function query(input){return input;} ", "");
    /*
    权限:可写
    返回:创建成功返回合约地址字符串,失败则抛出异常
    */

3.19. Chain.block

区块信息。

当前区块时间戳

Chain.block.timestamp

当前交易执行时候所在的区块时间戳。

当前区块高度

Chain.block.number

当前交易执行时候所在的区块高度。

3.20. Chain.tx

交易信息。

交易是用户签名的那笔交易信息。

交易的发起者

Chain.tx.initiator

交易最原始的发起者,即交易的费用付款者。

交易的触发者

Chain.tx.sender

交易最原始的触发者,即交易里触发合约执行的操作的账户。 例如某账号发起了一笔交易,该交易中有个操作是调用合约Y(该操作的source_address是x),那么合约Y执行过程中,sender的值就是x账号的地址。

let bar = Chain.tx.sender;
/*
那么bar的值是x的账号地址。
*/

交易的gas价格

Chain.tx.gasPrice

交易签名里的gas价格。

交易的哈希值

Chain.tx.hash

交易的hash值

交易的限制费用

Chain.tx.feeLimit

3.21. Chain.msg

消息是在交易里触发智能合约执行产生的信息。在触发的合约执行的过程中,交易信息不会被改变,消息会发生变化。例如在合约中调用contractCallcontractQuery的时候,消息会变化。

消息的发起者

Chain.msg.initiator

本消息的原始的发起者账号。

消息的触发者

Chain.msg.sender

本次消息的触发者账号。

例如某账号发起了一笔交易,该交易中有个操作是调用合约Y(该操作的source_address是x),那么合约Y执行过程中,sender的值就是x账号的地址。

let bar = Chain.msg.sender;
/*
那么bar的值是x的账号地址。
*/

本次支付操作的Gas

Chain.msg.coinAmount

本次支付操作的资产

Chain.msg.asset

为对象类型{"amount": 1000, "key" : {"issuer": "adxSZ7HNR1Nk8D8wm6pkcWNb6rnMZncyTVHSb", "code":"CNY"}}

本次交易里的发起者的nonce值

Chain.msg.nonce。即Chain.msg.initiator账号的 nonce值。

触发本次合约调用的操作的序号

Chain.msg.operationIndex

该值等于触发本次合约的操作的序号。

例如某账号A发起了一笔交易tx0,tx0中第0(从0开始计数)个操作是给某个合约账户转移资产(调用合约), 那么Chain.msg.operationIndex的值就是0。

let bar = Chain.msg.operationIndex;
/* bar 是一个非负整数*/

3.22. Chain.thisAddress

当前合约账号的地址

Chain.thisAddress

该值等于该合约账号的地址。

例如账号x发起了一笔交易调用合约Y,本次执行过程中,该值就是Y合约账号的地址。

let bar = Chain.thisAddress;
/*
bar的值是Y合约的账号地址。
*/

3.23. Utils.log

输出日志

Utils.log(info);

  • info: 日志内容

    例如

    let ret = Utils.log('hello');
    /*
    权限:只读
    返回:成功无返回值,会在对应的合约执行进程里,输出一段Trace级别的日志,如 V8contract log[adxSZ7HNR1Nk8D8wm6pkcWNb6rnMZncyTVHSb:hello];失败返回 false。
    */

3.24. Utils.stoI64Check

字符串数字合法性检查

Utils.stoI64Check(strNumber);

  • strNumber:字符串数字参数

    例如

    let ret = Utils.stoI64Check('12345678912345');
    /*
    权限:只读
    返回:成功返回 true,失败返回 false
    */

3.25. Utils.int64Add

64位加法

Utils.int64Add(left_value, right_value);

  • left_value: 左值

  • right_value:右值

    例如

    let ret = Utils.int64Add('12345678912345', 1);
    /*
    权限:只读
    返回:成功返回字符串 '12345678912346', 失败抛异常
    */

3.26. Utils.int64Sub

64位减法

Utils.int64Sub(left_value, right_value);

  • left_value: 左值

  • right_value:右值

    例如

    let ret = Utils.int64Sub('12345678912345', 1);
    /*
    权限:只读
    返回:成功返回字符串 '123456789123464',失败抛异常
    */

3.27. Utils.int64Mul

64位乘法

Utils.int64Mul(left_value, right_value);

  • left_value: 左值

  • right_value:右值

    例如

    let ret = Utils.int64Mul('12345678912345', 2);
    /*
    权限:只读
    返回:成功返回字符串 '24691357824690',失败抛异常
    */

3.28. Utils.int64Mod

64位取模

Utils.int64Mod(left_value, right_value);

  • left_value: 左值

  • right_value:右值

    例如

    let ret = Utils.int64Mod('12345678912345', 2);
    /*
    权限:只读
    返回:成功返回字符串 '1',失败抛异常
    */

3.29. Utils.int64Div

64位除法

Utils.int64Div(left_value, right_value);

  • left_value: 左值

  • right_value:右值

    例如

    let ret = Utils.int64Div('12345678912345', 2);
    /*
    权限:只读
    返回:成功返回 '6172839456172',失败抛异常
    */

3.30. Utils.int64Compare

64位比较

Utils.int64Compare(left_value, right_value);

  • 返回值 1:左值大于右值,0:等于,-1 :小于

  • left_value: 左值

  • right_value:右值

    例如

    let ret = Utils.int64Compare('12345678912345', 2);
    /*
    权限:只读
    返回:成功返回数字 1(左值大于右值),失败抛异常
    */

3.31. Utils.assert

断言

Utils.assert(condition[, message]);

  • condition: 断言变量

  • message: 可选,失败时抛出异常的消息

    例如

    Utils.assert(1===1, "Not valid");
    /*
    权限:只读
    返回:成功返回 true,失败抛异常
    */

3.32. Utils.sha256

sha256计算

Utils.sha256(data[, dataType]);

  • data: 待计算hash的原始数据,根据dataType不同,填不同格式的数据。

  • dataType:data 的数据类型,整数,可选字段,默认为0。0:base16编码后的字符串,如"61626364";1:普通原始字符串,如"abcd";2:base64编码后的字符串,如"YWJjZA=="。如果对二进制数据hash计算,建议使用base16或者base64编码。

  • 返回值: 成功会hash之后的base16编码后的字符串,失败会返回 false

    例如

    let ret = Utils.sha256('61626364');
    /*
    权限:只读
    功能:对
    返回:成功返回64个字节的base16格式字符串 '88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589',失败返回false
    */

3.33. Utils.ecVerify

校验签名是否合法

Utils.ecVerify(signedData, publicKey,blobData [, blobDataType]);

  • signedData: 签名数据,base16编码的字符串。

  • publicKey:公钥,base16编码的字符串。

  • blobData:原始数据,根据blobDataType,填不同格式的数据。

  • blobDataType:blobData的数据类型,整数,可选字段,默认为0。0:base16编码后的字符串,如"61626364";1:普通原始字符串,如"abcd";2:base64编码后的字符串,如"YWJjZA=="。如果对二进制数据校验,建议使用base16或者base64编码。

  • 返回值: 成功会返回true,失败会返回 false

    例如

    let ret = Utils.ecVerify('3471aceac411975bb83a22d7a0f0499b4bfcb504e937d29bb11ea263b5f657badb40714850a1209a0940d1ccbcfc095c4b2d38a7160a824a6f9ba11f743ad80a', 'b0014e28b305b56ae3062b2cee32ea5b9f3eccd6d738262c656b56af14a3823b76c2a4adda3c', 'abcd', 1);
    /*
    权限:只读
    返回:成功会返回true,失败会返回 false
    */

3.34. Utils.toBaseUnit

变换单位

Utils.toBaseUnit(value);

  • 返回值: 成功会返回乘以 10^8 的字符串,失败会返回 false

  • value: 被转换的数字,只能传入字符串,可以包含小数点,且小数点之后最多保留 8 位数字

    例如

    let ret = Utils.toBaseUnit('12345678912');
    /*
    权限:只读
    返回:成功返回字符串 '1234567891200000000',失败抛异常
    */

3.35. Utils.addressCheck

地址合法性检查

Utils.addressCheck(address);

  • address 地址参数,字符串

    例如

    let ret = Utils.addressCheck('adxSio3m7C7D84GzgmtTXanUNMwPezys9azQB');
    /*
    权限:只读
    返回:成功返回 true,失败返回 false
    */

3.36. Utils.toAddress

公钥转地址

Utils.toAddress(public_key);

  • public_key 公钥,base16编码的字符串

  • 成功,返回账号地址;失败返回false

    例如

    let ret = Utils.toAddress('b0014e067cdae290c47a558cd0438e6361d11b2cf48863be1cde030fe0a41ae23eff8e1533a1');
    /*
    权限:只读
    返回:成功返回 "adxSTGmZNHScBaoFUEfDVJs1zXk9KfYLbgkYL",失败返回false
    */

3.37. Utils.bpRangeProofVerify

范围证明验证

Utils.bpRangeProofVerify(commit, proof);

  • commit,66位Pedersen承诺字符串

  • proof,范围证明字符串,长度不定

  • 成功返回true;失败返回false

    例如

    let ret = Utils.bpRangeProofVerify('097ff10254037114cae985f044189a3faa044ac32c26ffadc0f08dc4b81a536638', '410905f651c9448e8a4e6db10c0bc23b2b09cf37ee2b6909b0e9178cf89b371584ea8533ed9648a942feb6b797bbdd8e9cd49ab8a86e6bf89f7e3b2c35c01818ad9006329e8dc4c9e335d60021b13c33c8aa8bd471908be4ce74a4cdfe968006abc3819e9e7d0aba261b947ed9bd8706d439707e783e34323c7c24b7c3dd722b66546f5c0d7c66d6c5a7390cc4cd1ed4ef53a0b1db3778a56b0ff418bfcababd966ae82cf0f44c29ba27f9eaa0a9ca1b4c913ddb7e42453666ccfbaad55f981b8cbede5023705cfeb08a26b323ea05b06ba3853cbfe02315c335f742c6a4ca290ca3d31a619534a38fc0754a6b52b9b66e69088a6265a0e68914b1a230bbd6d742875aac47259be01d60fff5c301c552a66b81152b2b2d7dca9064cde271690e30a7cae5fee6b89e0bd7a5c571d561f65a7d34cb4f5aba49fbeef91132e229fdf38ca61c2c737d3055ea143d0e5732b43ade6d7cf66269c7b3333f1ee9a295fc7f5d3cf47770d9a15651d176fa2792e39ad4bdacb14eb13657faf5eed43f3620a09441a5032c4919744692844a214455204662e9f7f80c88a569ab1a1986186f35a0eefd53d7be56d1150ed6a0cb030a10de35d0cb25738d66234646998b70dc6210ec8985c9bda183dd0a0d985c1bf1255afc326fd86029bc88e2db0433061a0bf6be107d6ddebb2516843b9c6a56f9e03a420e4665bc013f7e6a497412015d4fed2cf9895c756857d5434799e20fbf8feef4473c778bd06fc997ea9772bec1d0cc30870580575017239cef0f693922c12a1ab850628302f970d07caaa54196c5ed59eefaaad981dfeeef4efe06d5ab3634b8e9882416cde960dee25f22701faf63086771d2987e3b300659278cc9e8543ee81c9f70ebbe2897f1020c609b3fe1667358b5998dbf089720cecc401810acbb48864262065c7742c5cd2b83b76aafc6ff2428e51a66e89ca1b1cf552bb8f8a261fb2e91e49ea68fc2db1c00a079bf2b0b39c9f43be25d31136399666ff05dcbbbc98d47358a5052c0d3cd113f9f0a3fe6838babd160411f22d6a187f73b69e45f042149e7e1e879a8b75c6ac4dfa4bbb473f22f89738f163f2a64efb20a0bb67e16acd06791080cc5246d48887a158f8b');
    /*
    权限:只读
    返回:成功返回true,失败返回false
    */

3.38. Utils.pedersenTallyVerify

输入输出相等验证

Utils.pedersenTallyVerify(input_commits, output_commits, excess_msg, excess_sig);

  • input_commits, 输入pedersen承诺列表,Json数组

  • output_commits, 输出pedersen承诺列表,Json数组

  • excess_msg, 待签名消息,32位字符串,无编码要求

  • excess_sig, 交易核(输入减输出余项)签名结果字符串

  • 成功返回true;失败返回false

    例如

    let ret = Utils.pedersenTallyVerify(['08aab3b3c403129887d1f5932565c0fd9ef7b8d562de6eb0b6eb64c1b7396d268a'], ['08a64f2ed7be3940dfb5b76b450418524df48837595c98698dbe191708cb911c40', '09c28bb504d0c4e4ea4f19e3041a90b608aada114fab1786175ffaefd5fe6c0d3b'], 'b649bd965a5126984cc785cbeb1ef053', '304402202345c4e1f4efd8bfdb77681e7d638f9a8f29f473e47a698c56eeab3fe3bc278502206bbdf0db0e04bafa4a88c0382b9b600e72976fceeaa9129cb257a82a89ee52fb');
    /*
    权限:只读
    返回:成功返回true,失败返回false
    */

3.39. Utils.HexToDec

16进制转10进制字符串

Utils.HexToDec(hex_str);

  • hex_str: 16进制格式的字符串

  • 返回值: 成功返回十进制的字符串,失败会返回 false

    例如

    let ret = Utils.hexToDec('aeb61626364');
    /*
    权限:只读
    功能:对
    返回:成功返回10进制格式字符串 '12006067430244',失败返回false
    */