# JS SDK使用说明
Java Script SDK(以下简称：JS SDK) 是业务端与区块链平台沟通的桥梁，提供了基础的API功能：包括提交交易、账户操作、部署和调用合约、各类查询操作、交易模拟执行、监听事件等。JS SDK同时实现了通过TLS和HTTPS协议与区块链平台交互，统一了API接口使用方式。因此，JS SDK可集成运行在nodejs环境，也可以运行在浏览器、web应用等环境，更加的灵活、方便。
# 1. 开发简介
JS SDK 的使用方式简单，而且对环境兼容友好，选择使用TLS协议需要依赖nodejs，使用HTTPS时可直接在浏览器环境集成使用。

# 2. 应用示例
## 2.1 环境准备
- 安装 nodejs，[地址](https://nodejs.org/en/) (推荐版本: v10.11.0及以上版本)

## 2.2 快速开始
快速创建一个访问区块链的实例`chain`，并使用`QueryLastBlock`验证与区块链节点的连接状况：
```javascript
const Chain = require("@alipay/mychain/index.node") //在node环境使用tls协议
const fs = require("fs")

const accountKey = fs.readFileSync("./certs/user.pem", { encoding: "utf8" })
const accountPassword = "123abc"  //需要替换为自定义的user.pem密码

const keyInfo = Chain.utils.getKeyInfo(accountKey, accountPassword)
//可打印私钥和公钥，使用16进制
//console.log('private key:', keyInfo.privateKey.toString('hex'))
//console.log('public key:', keyInfo.publicKey.toString('hex'))

const passphrase = "123abc" //需要替换为自定义的client.key密码
//配置选项
let opt = {
  host: '127.0.0.1',    //目标区块链网络节点的IP
  port: 18130,          //端口号
  timeout: 30000,       //连接超时时间配置
  cert: fs.readFileSync("./certs/client.crt", { encoding: "utf8" }),
  ca: fs.readFileSync("./certs/ca.crt", { encoding: "utf8" }),
  key: fs.readFileSync("./certs/client.key", { encoding: "utf8" }),
  userPublicKey: keyInfo.publicKey,
  userPrivateKey: keyInfo.privateKey,
  userRecoverPublicKey: keyInfo.publicKey,
  userRecoverPrivateKey: keyInfo.privateKey,
  passphrase: passphrase
}

//初始化一个连接实例
const chain = Chain(opt)

//调用API查询最新的一个区块数据
chain.ctr.QueryLastBlock({}, (err, data) => {
  console.log('raw data:', data)									 //区块结构数据
  console.log('block hash:', data.block.block_header.hash)			 //区块哈希
  console.log('block number:', data.block.block_header.block_number) //区块高度
})

```
运行正确结果参考：
```json
raw data: { msg_type: 58,
  sequence: 1,
  return_code: 0,
  group_id: '0x0000000000000000000000000000000000000000',
  block:
   { block_header:
      { hash:
         '0xe99d8958a45e8c87a7b10efc259828f06fe083995be52997f5d310f2b6d073a6',
        version: 2,
        block_number: 84265,
        parent_hash:
         '0x918b263a8e6c6fff594b89570970ef4bef24cf93aeed5347f7b250b070857c4b',
        transaction_root:
         '0x0000000000000000000000000000000000000000000000000000000000000000',
        receipt_root:
         '0x0000000000000000000000000000000000000000000000000000000000000000',
        state_root:
         '0x9f71cb9ce960e5637bad6da5be8daa2d7e557942208f241a60589b2de98e6c71',
        gas_used: 0,
        timestamp: 1547382477852,
        log_bloom:
         '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' },
     block_body:
      { transaction_list: [],
        receipt_list: [],
        consensus_proof:
         '0xf8f2f8c9b8419746989382c1613c6c3ce98bf79ac92a8d69952c22f4064194869c53b4075d517cfc98eda861212687e49b186c08d196770bd356762fa2a88d0288c0556f271a01b84138a97446a75c76a31d24880c343407bd7bc24608685c494240ac603cad62201a01a423718661b24e517ee6f6b2fee6d356b57833305860700cca81b0238f870400b841eaf508392d9098a3e7fb46f6f7aa4b53311e5a0d13e300d02025af7453ea130a6c27407c1da254578cae80ed498d4807587883f837c1716b0be8ae02cf955d6000e61e83014929a0e200d8bee723d820022d5c5a1f8fe6b521c0a4fff0b5ec03a7c6061276d68b58' } },
  api: 'QueryLastBlock' }
block hash: 0xe99d8958a45e8c87a7b10efc259828f06fe083995be52997f5d310f2b6d073a6
block number: 84265

```
>**注意错误码说明**：运行结果示例中包含一个字段`return_code: 0`，此字段结果为'0'，表明执行成功，否则其值为`错误码`，在后文介绍的交易回执（receipt）中`result`字段也是类似含义，详细错误码信息可参考[错误码](https://yuque.antfin-inc.com/docs/share/045e268d-d925-4bf1-b62b-161e26c8d2ba)

### 2.2.1 引用JS SDK说明
在使用JS SDK时，不同运行环境，引用的方式稍有不同：
- node环境：
```javascript
const Chain = require("@alipay/mychain/index.node")
```
- Web环境：
```javascript
const Chain = require("@alipay/mychain")
```

### 2.2.2 配置选项
在初始化与区块链连接的示例之前，可以做一些选项配置，具体配置项的说明如下：
>注意：如果不特别说明配置选项的数据类型，默认为string类型

| 选项 | 必选| 配置说明 | 配置举例说明 |
| -----| ----- | ----- | ----- |
| host |  true | 区块链节点的ip或者host name <br>在使用tls时是ip地址 <br>使用https时是host name | '127.0.0.1'， <br>'https://www.alipay.com'或'https://127.0.0.1' |
| port |  true | 区块链节点开放连接的端口号，类型为：number | 18130 |
| clients |  false | 可设置多个host:port，作为主节点(首个为主节点)，次节点备份，当主节点出现连接问题，SDK会切换到列表其它节点重试连接 <br>如果配置此参数，可不用设置上面host和port参数 | [{host:'127.0.0.1',port:18130},{host:'127.0.0.2',port:18130}] |
| timeout | true | 与区块链节点连接的超时时间配置，单位毫秒，类型为：number | 30000 |
| ca | true |目标合约链的根证书 | 在BaaS平台申请通过后下载 |
| cert |true | 客户端证书文件 | 在BaaS平台申请通过后下载 |
| key | true |客户端生成的私钥文件，用于生成证书请求文件，进而申请证书 | 使用BaaS提供密钥生成工具生成 |
| userPublicKey | true |账户公钥，字符串内容为16进制 | '0x971c77d38bf220572fe8294d7884b184adeeb9bac4d61c1b3e1e924575e526152145763eaba40c8713c82cc2961fba98f71c8b93984d4e3d10b2ff53ea039551' |
| userPrivateKey |true | 账户私钥，字符串内容为16进制 | '0x4015e39643765014b874dbd35a53f1a01418c66f7c47da35f3a84122c743d9a3' |
| userRecoverPublicKey |true | 账户恢复公钥，字符串内容为16进制 | '0xb961f6a1a43b9e7aa368be8d093ed7bec2d0ff85ff7646ec968d86bd546151efbd037cfe09933684b5c98978a1593074cdff465de3a3620089f5634911bf7b2e' |
| userRecoverPrivateKey | true |账户恢复私钥，字符串内容为16进制 | '0x44a973e5286f1d3513561360bb0214235425b942a4649c7d371f780ca1ee0e80' |
| passphrase | true | tls或https链接的client.key文件的自定义密码 | ‘123abc’ |
| checkServerIdentity | false |针对tls协议的配置，类型为：boolean，含义为是否启用对服务端hostname的检查，<br>也就是对比服务端证书的`CN`字段与hostname是否匹配，默认值为：false| false |
| tx_querycount | false |对于交易类型，提交交易后会调用'QueryTransactionReceipt'查询收据，此配置可设定retry的次数，类型为：number，默认值为：3| 5 |
| tx_querytime | false |对于交易类型，提交交易后会调用'QueryTransactionReceipt'查询收据，此配置可设定retry的时间间隔，类型为：number，单位为：毫秒，默认值为：3000 | 200 |


# 3. 数据模型
在JS SDK中，有很多数据结构与合约平台的数据结构保持统一，比如账户、合约、区块、交易、收据等。

## 3.1 账户模型

下面是账号的基本参数及说明：

| 参数             | 类型        | 说明                                 |
| -------------- | --------- | ---------------------------------- |
| identity       | string    | 账户的标识id，16进制表示的字符串        |
| balance        | BigNumber，特别类型，[详情参考](https://github.com/MikeMcl/bignumber.js) | 余额                   |
| auth_map       | string    | 账户或者合约的公钥和权重值              |
| recover_key    | string    | 恢复公钥，用于帐户私钥丢失的情况，16进制表示的字符串          |
| recover_time   | number    | 上次成功恢复的时间                     |
| status         | number    | 状态， 0：NORMAL；1：FREEZE；2：RECOVERING |
| encryption_key | string    | 加密公钥，用来加密智能合约中的交易金额，16进制表示的字符串    |            

>示例:

```json
{ identity:
   '0xc60a9d48105950a0cca07a4c6320b98c303ad42d694a634529e8e1a0a16fcdb5',
  balance: 1000000000000000000,
  recover_key:
   '0x61df14a4625bd997eeae2ebcffe58aa95ab3fcd1d7660f83b5fe34a6e08915beae36d84f960f6baca008b67c919feae0f29c262b43e985b477f92626ed2b6f6a',
  recover_time: 0,
  status: 0,
  code: '',
  storage_root:
   '0x76be8b528d0075f7aae98d6fa57a6d3c83ae480a8469e668d7b0af968995ac71',
  code_hash:
   '0x0000000000000000000000000000000000000000000000000000000000000000',
  encryption_key: '',
  version: '2',
  auth_map:
   [ { auth_key:
        '0x61df14a4625bd997eeae2ebcffe58aa95ab3fcd1d7660f83b5fe34a6e08915beae36d84f960f6baca008b67c919feae0f29c262b43e985b477f92626ed2b6f6a',
       auth_weight: 100 } ] }
```

## 3.2 合约模型

下面是合约的基本参数及说明：

| 参数             | 类型        | 说明                                 |
| -------------- | --------- | ---------------------------------- |
| identity       | string    | 合约的标识id，16进制表示的字符串        |
| balance        | BigNumber，特别类型，[详情参考](https://github.com/MikeMcl/bignumber.js) | 余额                     |
| auth_map       | string    | 账户或者合约的公钥和权重值                      |
| recover_key    | string    | 恢复公钥，用于帐户私钥丢失的情况，16进制表示的字符串 |
| recover_time   | number    | 上次成功恢复的时间                          |
| status         | number    | 状态， 0：NORMAL；1：FREEZE；2：RECOVERING |
| encryption_key | string    | 加密公钥，用来加密智能合约中的交易金额，16进制表示的字符串    | 
| storage_root   | string    | 世界状态的默克尔哈希根，16进制表示的字符串      |
| code_hash      | string    | 合约代码哈希，16进制表示的字符串              |
| code           | string    | 合约代码，16进制表示的字符串    |

>示例：

```json
{ identity:
   '0xe784481df592b4f72360db588d04fc55c4fa800c867a1ca7bb9f470ddf805b6d',
  balance: 0,
  recover_key:
   '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
  recover_time: 0,
  status: 0,
  code:
   '0x016080604052600436106100825763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416630d15fd7781146100875780632f265cf7146100ae578063392e6678146100dc5780637021939f14610108578063a9a981a314610120578063b13c744b14610135578063cc9ab2671461014d575b600080fd5b34801561009357600080fd5b5061009c610167565b60408051918252519081900360200190f35b3480156100ba57600080fd5b506100c660043561016d565b6040805160ff9092168252519081900360200190f35b3480156100e857600080fd5b506100f4600435610199565b604080519115158252519081900360200190f35b34801561011457600080fd5b506100c660043561024e565b34801561012c57600080fd5b5061009c610263565b34801561014157600080fd5b5061009c600435610269565b34801561015957600080fd5b50610165600435610288565b005b60035481565b600061017882610199565b151561018357600080fd5b5060009081526001602052604090205460ff1690565b6000805b60005481101561020f5760008054849190839081106101b857fe5b600091825260209091200154141561020757604080516001815290517f2b766bfa48dbb99822ac647fffc163dc74b7857beedb5ec6782ed9826453db049181900360200190a160019150610248565b60010161019d565b604080516000815290517f2b766bfa48dbb99822ac647fffc163dc74b7857beedb5ec6782ed9826453db049181900360200190a1600091505b50919050565b60016020526000908152604090205460ff1681565b60025481565b600080548290811061027757fe5b600091825260209091200154905081565b61029181610199565b151561029c57600080fd5b600081815260016020818152604092839020805460ff80821685011660ff19909116179055600380549092019091558151838152339181019190915281517f690156027e055e69a001816111c1abd4287fa897e929662c9ad6108a84fe2523929181900390910190a1505600a165627a7a72305820f4f856c692cb5ff1f3f493855d83e03251ad0399970bdf9119597e2593fd68d00029',
  storage_root:
   '0xd76c14b0f7f99cd297110d1d711a56a70d0ee758174e957874d3354dfb6fc5f4',
  code_hash:
   '0xf2798023afd1207ca4ac57ad2ecb84b032b0279d27294a6e2c57dfffb3eadeb5',
  encryption_key: '',
  acc_version: 2,
  auth_map:
   [ { auth_key:
        '0x61df14a4625bd997eeae2ebcffe58aa95ab3fcd1d7660f83b5fe34a6e08915beae36d84f960f6baca008b67c919feae0f29c262b43e985b477f92626ed2b6f6a',
       auth_weight: 100 } ] }

```


## 3.3 交易模型

下面是交易的基本参数及说明：

| 参数          | 类型                                | 说明                                       |
| ----------- | --------------------------------- | ---------------------------------------- |
| hash        | string                              | 交易的哈希，由排除signature字段的所有字段构成              |
| type        | number                          | 交易的类型                                    |
| timestamp   | number                          | 交易的时间戳                                   |
| nonce       | number                          | 防止重放攻击                                   |
| period      | number                          | 单位是毫秒，事务开始或结束的时间。为未来扩展                   |
| from        | string                          | 交易的发送者                                   |
| to          | string                          | 交易的接受者                                   |
| value       | number                          | 转账金额                                     |
| gas         | number                          | 交易执行的消耗费用                                |
| data        | string                             | 见交易对象data编码方式                            |
| group_id    | string                              | 交易在一个group中执行                            |
| signature   | string       | 签名，使用一个或者多个私钥对hash加签                     |
| extensions | string | 交易扩展字段，目前JS SDK尚不支持 |


## 3.4 收据模型

下面是收据的基本参数及说明：

| 参数       | 类型                    | 说明             |
| -------- | --------------------- | -------------- |
| result   | number                | 交易结果           |
| gas_used | number              | 交易执行的消耗费用      |
| logs     | Array     | 交易执行的日志集合      |
| output   | BigNumber                 | 合约的ouptut      |
| offset   | number    | 解析合约output的偏移量 |



## 3.5 日志模型

下面是收据的基本参数及说明：

| 参数       | 类型                       | 说明      |
| -------- | ------------------------ | ------- |
| from     | string                 | 交易的发送者  |
| to       | string                 | 交易的接受者  |
| topics   | Array                  | 订阅的主题   |
| log_data | Array                  | 交易产生的日志 |



## 3.6 区块模型

区块模型分为三个部分，分别是区块、区块头、区块体。

下面是区块的基本参数及说明：

| 参数     | 类型                           | 说明   |
| ------ | ---------------------------- | ---- |
| block_header | object | 区块头  |
| block_body   | object   | 区块体  |

下面是区块头`block_header`的基本参数及说明：

| 参数               | 类型       | 说明               |
| ---------------- | -------- | ---------------- |
| hash             | string     | 区块头的哈希           |
| version          | number | 版本，兼容性区分             |
| number           | number | 区块号              |
| parent_hash      | string     | 上一区块哈希           |
| transaction_root | string     | 区块体中的交易构成的默克尔哈希根 |
| receipt_root     | string     | 区块体中的收据构成的默克尔哈希根 |
| state_root       | string     | 世界状态的默克尔哈希根      |
| gas_used         | number | 交易执行的总消耗量        |
| timestamp        | number | 时间戳              |
| log_bloom        | string | 日志布隆过滤器          |

下面是区块体`block_body`的基本参数及说明：

| 参数              | 类型         | 说明    |
| ---------------- | -------------| ------  |
| transaction_list | Array        | 交易列表 |
| receipt_list     | Array        | 收据列表 |
| consensus_proof  | string       | 共识证明 |
  
>示例：

```json
block:
   { block_header:
      { hash:
         '0xf3f53c5ec6ede0bd24a2e36914cd5fbc3c2a7de80d677efdd704459a7f5f9879',
        version: 2,
        block_number: 86117,
        parent_hash:
         '0xe252fe8333dce2bacc9ec1764d03b41dea30cc0f02d3064dc2537f0db63110b8',
        transaction_root:
         '0x0000000000000000000000000000000000000000000000000000000000000000',
        receipt_root:
         '0x0000000000000000000000000000000000000000000000000000000000000000',
        state_root:
         '0xb849f08a17fd0cdd97b14c3531f18d9c82b9411ad9e97bc8812f05349496aa30',
        gas_used: 0,
        timestamp: 1547388041992,
        log_bloom:
         '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' },
     block_body:
      { transaction_list: [],
        receipt_list: [],
        consensus_proof:
         '0xf8f2f8c9b84154f46c7c6a4bd7b4da1350de36d6b7cd24e595d82b6cbc93c8b161a653e9a9db25e9aad5ed219873a44ba86dd812df0ff0813840af198703eedd2446517d761f01b841563d53272fa6627a32192f28747e1cffd541a3da206fa7f788311e30fcc0272d2c65982f341be4e9a9d8a0139e1b346b8ca835a3f710a58a00bb1f52ae7cba5b01b841db26385c9d5e8f2759c5ac72dc90befd3e2a0b6ad0ac25bd7ea0737025c07a776d34fb84625dce19d8c82450205034737414ed14999fbfa28653ee62547bd77201e61e83015065a048c1edc05bc2f69c32e61b6a79b39880474bf23fdb2466a0de3a88cdae42c649' } 
  }
```
  
# 4. 接口说明
JS SDK多数接口都以相同的回调方式返回结果，对于合约相关的返回方式稍有不同。
#### 回调返回值
- 普通接口返回值

| 参数        |  类型           | 说明                |
| ---------- |  -----------   | ----------------- |
| err        | string   | 使用的账户名，用此账户来创建新账户  |
| data       | object   | 交易回执的结果 |

- 合约部署和调用接口返回值

| 参数        |  类型           | 说明                |
| ---------- |  -----------   | ----------------- |
| err        | string   | 错误信息，如果为undefined，则说明无错误  |
| output     | 不确定    | 如果是合约部署，此字段为合约的bytecode，如果是合约调用此字段与合约方法返回值类型相同  |
| data       | object   | 交易回执的结果  |

以上`data`字段内容根据不同的接口返回内容不同，具体请查看接口返回值。

## 4.1 环境接口

### 4.1.1 环境实例初始化

1. 引入JS SDK
>使用TLS协议，基于node：

```java script
const Chain = require('@alipay/mychain/index.node')
```

2. 创建连接实例
>以node环境为例说明

```java script
const Chain = require("@alipay/mychain/index.node") //在node环境使用tls协议
const fs = require("fs")

const accountKey = fs.readFileSync("./certs/user.pem", { encoding: "utf8" })
const accountPassword = "123abc"  //需要替换为自定义的user.pem密码
const keyInfo = Chain.utils.getKeyInfo(accountKey, accountPassword)

const passphrase = "123abc" //需要替换为自定义的client.key密码
//配置选项
let opt = {
  host: '127.0.0.1',    //目标区块链网络节点的IP
  port: 18130,          //端口号
  timeout: 30000,       //连接超时时间配置
  cert: fs.readFileSync("./certs/client.crt", { encoding: "utf8" }),
  ca: fs.readFileSync("./certs/ca.crt", { encoding: "utf8" }),
  key: fs.readFileSync("./certs/client.key", { encoding: "utf8" }),
  userPublicKey: keyInfo.publicKey,
  userPrivateKey: keyInfo.privateKey,
  userRecoverPublicKey: keyInfo.publicKey,
  userRecoverPrivateKey: keyInfo.privateKey,
  passphrase: passphrase
}

//初始化一个连接实例
const chain = Chain(opt)
```

## 4.2 账户接口
### 4.2.1 创建账户
- CreateAccount
>在链上创建一个新的账户

###### 请求参数
>将以下参数整体封装为object传入

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| from       | true  | string   | 使用的账户名，用此账户来创建新账户        |
| to         | true  | string   | 创建的新账户名  |
| data       | true  | object   | 数据内容，包含了创建账户需要的数据  |

> data字段内容

| 字段             | 必选    | 类型       | 说明                |
| --------------- | ----- | ----------- | -----------------  |
| recover_key     | false  | string     | 内容为‘0x’开头的16进制，长度为64(不含‘0x’)          |
| auth_key        | false | string      | 内容为‘0x’开头的16进制，长度为64(不含‘0x’)        |
| auth_weight     | false | string      | 目前JS SDK支持一个auth key，此值应设为100 |         |

###### 使用示例
>使用已有public key创建账户

```java script
chain.ctr.CreateAccount({
  from: 'Tester001',
  to: 'Tester002',
  data: {
    recover_key: '0xf5e50510a04a3f659a0e89f2063f79f8c1aed5ddaab6420ac47700020d9889dc14dae4dc9843c88d8222167095d9e6ce052e8a19cbc737c3f3cddf66409dbb0a',
    auth_key: '0xf5e50510a04a3f659a0e89f2063f79f8c1aed5ddaab6420ac47700020d9889dc14dae4dc9843c88d8222167095d9e6ce052e8a19cbc737c3f3cddf66409dbb0a',
    auth_weight: 100
  }
}, (err, data) => {
  console.log(data)
})
```
>使用新创建的key创建账户

```java script
  const newKey = Chain.utils.generateECKey();
  console.log('newKey priKey:', newKey.privateKey.toString('hex'))
  console.log('newKey pubKey:', newKey.publicKey.toString('hex'))

  chain.ctr.CreateAccount({
    from: 'Tester001',
    to: 'Tester003',
    data: {
      recover_key: '0x'+ newKey.publicKey.toString('hex'),
      auth_key: '0x'+ newKey.publicKey.toString('hex'),
      auth_weight: 100
    }
  }, (err, data) => {
    console.log(data)
  })
```

### 4.2.2 转账
- TransferBalance
>从一个账户转账到另一个账户

###### 请求参数
>将以下参数整体封装为object传入

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| from       | true  | string   | 使用的账户名       |
| to         | true  | string   | 转账的目标账户名  |
| value      | true  | number   | 转账额度  |


###### 使用示例

```java script
  chain.ctr.TransferBalance({
        from: 'Tester001',
        to: 'Tester002',
        value: 0
      }, (err, data) => {
        console.log(data)
      })
```
### 4.2.3 设置恢复公钥
- SetRecoverkey
>配置一个账户的恢复公钥

###### 请求参数
>将以下参数整体封装为object传入

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| from       | true  | string   | 需要配置的当前账户名        |
| data       | true  | object    | 数据字段  |

> data字段内容

| 字段             | 必选    | 类型       | 说明                |
| --------------- | ----- | ----------- | -----------------  |
| recover_key     | true  | string     | 内容为‘0x’开头的16进制，长度为64(不含‘0x’) |

###### 使用示例

```java script
  chain.ctr.SetRecoverkey({
        from: account,
        data: {
          recover_key: publicKey
        }
      }, (err, data) => {
        console.log(data)
      })
```
### 4.2.4 预重置公钥
- PreResetPubKey
>预重置一个账户的公钥

###### 请求参数
>将以下参数整体封装为object传入

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| from       | true  | string   | 需要配置的当前账户名        |

###### 使用示例

```java script
  chain.ctr.PreResetPubKey({
	   from: 'Tester001'
	 }, (err, data) => {
	   console.log(data)
	 })
```
### 4.2.5 重置公钥
- ResetPubKey
>重置一个账户的公钥

###### 请求参数
>将以下参数整体封装为object传入

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| from       | true  | string   | 需要配置的当前账户名        |
| data       | true  | object    | 数据字段  |

> data字段内容

| 字段             | 必选    | 类型       | 说明                |
| --------------- | ----- | ----------- | -----------------  |
| auth_key     | true  | string     | 内容为‘0x’开头的16进制，长度为64(不含‘0x’) |
| auth_weight  | true  | number     | 权重值，为：100，当前JS SDK支持一个auth key，因此权重值为100，未来支持多auth key，权重值和为100 |

###### 使用示例

```java script
chain.ctr.ResetPubKey({
  from: 'Tester001',
  data: {
    auth_key:'0xf5e50510a04a3f659a0e89f2063f79f8c1aed5ddaab6420ac47700020d9889dc14dae4dc9843c88d8222167095d9e6ce052e8a19cbc737c3f3cddf66409dbb0a',
    auth_weight: 100,
  }
}, (err, data) => {
  console.log(data)
})
```

### 4.2.6 更新权重
- UpdateAuthMap
>更新一个账户的公钥的权重值

###### 请求参数
>将以下参数整体封装为object传入

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| from       | true  | string   | 需要配置的当前账户名        |
| data       | true  | object    | 数据字段  |

> data字段内容

| 字段             | 必选    | 类型       | 说明                |
| --------------- | ----- | ----------- | -----------------  |
| auth_key     | true  | string     | 内容为‘0x’开头的16进制，长度为64(不含‘0x’) |
| auth_weight  | true  | number     | 权重值，为：100，当前JS SDK支持一个auth key，因此权重值为100，未来支持多auth key，权重值和为100 |

###### 使用示例

```java script
chain.ctr.UpdateAuthMap({
  from: 'Tester001',
  data: {
    auth_key:'0xf5e50510a04a3f659a0e89f2063f79f8c1aed5ddaab6420ac47700020d9889dc14dae4dc9843c88d8222167095d9e6ce052e8a19cbc737c3f3cddf66409dbb0a',
    auth_weight: 100,
  }
}, (err, data) => {
  console.log(data)
})
```

## 4.3 合约接口
### 4.3.1 构造合约实例
- contract
>构造一个合约示例，后续可部署、调用合约

###### 参数

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| name       | true  | string   | 目标合约名字，此名字计算哈希后即是此合约的identity  |
| abi        | true  | string   | abi定义的json格式字符串  |
| vmType     | false | string   | 由于合约平台支持Solidity和C++ 两种合约语言，当需要初始化一个C++ 合约实例时，可配置此参数为：chain.WASM，默认配置为chain.EVM |

>**注意**：关于C++ 合约支持的类型和详细细节，请参考[C++ 合约开发](https://yuque.antfin-inc.com/docs/share/df6d58d6-cbb6-4cd2-9cee-a7629578c872)

>注意：C++ 合约（编译为wasm字节码）只在[蚂蚁区块链创新大赛](https://dc.cloud.alipay.com/index#/blockchain)中可试用，正式生产环境暂未发布。

###### 使用示例-1

```java script
const solc = require('@alipay/solc')
const contract = fs.readFileSync('./CreditManager.sol', {encoding: 'ascii'})
// 第二个参数设定为"1"，会开启编译优化 optimiser
const output = solc.compile(contract, 1)
const abi = JSON.parse(output.contracts[':CreditManager'].interface)
const bytecode = output.contracts[':CreditManager'].bytecode

// 带上时间戳，防止合约名计算哈希后已存在
const contractName = 'contract'+Date.now()
// 初始化一个合约实例
let myContract = chain.ctr.contract(contractName, abi)

```

###### 使用示例-2：初始化一个C++合约，使用wasm字节码

```java script
const wasm_code = fs.readFileSync('./contract/base.wasm')
const abi = fs.readFileSync('./contract/base.abi')
const bytecode = '0x'+wasm_code.toString('hex')

// 带上时间戳，防止合约名计算哈希后已存在
const contractName = 'contract'+Date.now()
// 初始化一个合约实例
let myContract = chain.ctr.contract(contractName, abi, chain.WASM)

```


### 4.3.2 部署合约
- new
>部署一个合约

###### 请求参数

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| bytecode       | true  | string   | 目标合约的字节码，为16进制表示，无需‘0x’作为前缀  |
| data           | true  | object    | 包含‘from’，‘parameters’等配置  |

> data字段内容

| 字段             | 必选    | 类型       | 说明                |
| --------------- | ----- | ----------- | -----------------  |
| from       | true  | string   | 需要配置的当前账户名        |
| parameters | true  | Array   | 如果合约包含初始化函数，并且此函数需要参数列表，可以通过parameters传递 |

###### 使用示例

```java script
const solc = require('@alipay/solc')
const contract = fs.readFileSync('./CreditManager.sol', {encoding: 'ascii'})
// 第二个参数设定为"1"，会开启编译优化 optimiser
const output = solc.compile(contract, 1)
const abi = JSON.parse(output.contracts[':CreditManager'].interface)
const bytecode = output.contracts[':CreditManager'].bytecode

// 带上时间戳，防止合约名计算哈希后已存在
const contractName = 'contract'+Date.now()
// 初始化一个合约实例
let myContract = chain.ctr.contract(contractName, abi) 
// 部署合约，可传递初始化函数需要的参数
myContract.new(bytecode, {
  from: 'Tester001',
  // parameters: [param1, param2]
}, (err, contract, data) => {
  console.log(data)
})

```

### 4.3.3 调用合约
- 直接使用目标合约方法名
> 完成合约方法调用

###### 请求参数

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| paramter       | false  |  不确定   | 参考具体的合约方法参数列表，参数个数不确定  |
| data           | true  | object    | 包含‘from’配置  |

> data字段内容

| 字段             | 必选    | 类型       | 说明                |
| --------------- | ----- | ----------- | -----------------  |
| from            | true  | string   | 需要配置的当前账户名        |

###### 使用示例-1
>在部署合约的回调函数中，调用合约方法

```java script
myContract.new(bytecode, {
  from: 'Tester001',
  // parameters: [param1, param2]
}, (err, contract, data) => {
  console.log(data)
  // 调用合约方法‘Issue’，Issue方法需要两个参数：第一个参数是一个账户identity，第二个参数是一个数值
  myContract.Issue('0xc60a9d48105950a0cca07a4c6320b98c303ad42d694a634529e8e1a0a16fcdb5', 100 , { from: 'Tester001' }, (err, output, data) => {
    console.log('output is:', output)
  })
})
```
###### 使用示例-2
>在部署合约之后，调用合约方法：此方式连续写多个合约调用，会并发调用，如果有调用依赖，请使用回调。

```java script
myContract.new(bytecode, {
  from: 'Tester001',
  // parameters: [param1, param2]
}, (err, contract, data) => {
  console.log(data)
})

// 调用合约方法‘Issue’
myContract.Issue('0xc60a9d48105950a0cca07a4c6320b98c303ad42d694a634529e8e1a0a16fcdb5', 100 , { from: 'Tester001' }, (err, output, data) => {
  console.log('output is:', output)
})
```

### 4.3.4 升级合约
- update
> 对一个链上已存在合约进行升级：请谨慎使用合约升级功能，合约升级需要兼容新旧合约的数据存储，正常不建议使用。

###### 请求参数

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| bytecode       | true  | string   | 目标合约的`runtime`字节码，为16进制表示，无需‘0x’作为前缀  |
| data           | true  | object    | 空的object  |


###### 使用示例
>在部署合约的回调函数中，调用合约方法

```java script
myContract.update(bytecode, {}
, (err, contract, data) => {
  console.log('contract update result:', data)
})
```
>**注意**：升级合约时，传入的bytecode字节码是`runtime`字节码，使用二进制solc编译工具的`--bin-runtime`参数可以直接得到，是正常`--bin`参数编译的字节码的子集。`runtime`字节码通过本地执行合约部署操作也可获取到，下面有具体介绍。

###### 通过本地合约部署获取`runtime`字节码用于升级合约：（可直接使用solc-js）
solc-js工具在JS代码中默认使用`--bin`参数编译合约得到字节码，此字节码不能直接用于合约升级，但是通过一次“本地合约部署”之后即可得到`runtime`字节码用于合约升级使用。

###### 使用示例
>使用solc-js来完成合约升级，步骤如下：
solc-js编译合约A -> 部署合约A -> 调用合约A -> 修改合约代码得到合约A' -> solc-js编译合约A' -> 本地部署合约A'获取`runtime`字节码 -> 使用A'`runtime`字节码升级合约A -> 调用合约A验证升级情况

```java script
const solc = require('@alipay/solc')
const contract = fs.readFileSync('./CreditManager.sol', {encoding: 'ascii'})
// 第二个参数设定为"1"，会开启编译优化 optimiser
const output = solc.compile(contract, 1)
const abi = JSON.parse(output.contracts[':CreditManager'].interface)
const bytecode = output.contracts[':CreditManager'].bytecode

console.log('bytecode:', bytecode)
// 带上时间戳，防止合约名计算哈希后已存在
let contractName = 'contract'+Date.now()
let myContract = chain.ctr.contract(contractName, abi)

// 使用solc-js编译结果字节码升级合约说明
myContract.new(bytecode, {
  from: 'Tester001',
}, (err, contract, data) => {
  console.log('contract new:', data)
  myContract.Issue('0xc60a9d48105950a0cca07a4c6320b98c303ad42d694a634529e8e1a0a16fcdb5', 100 , { from: 'Tester001' }, (err, output, data) => {
    console.log('output1 is:', output)

    //改动合约CreditManager.sol计算逻辑(注意不可改动合约存储定义)，然后得到新的CreditManagerPro.sol合约
    //改动内容为：Issue方法内`credit[account] += value;`改为`credit[account] += value * 2;`
    const contract_pro = fs.readFileSync('./CreditManagerPro.sol', {encoding: 'ascii'})
    // 第二个参数设定为"1"，会开启编译优化 optimiser
    const output_pro = solc.compile(contract_pro, 1)
    const abi_pro = JSON.parse(output_pro.contracts[':CreditManager'].interface)
    const bytecode_pro = output_pro.contracts[':CreditManager'].bytecode
    myContract = chain.ctr.contract(contractName, abi_pro)

    myContract.new(bytecode_pro, {
      from: 'Tester001',
      local: true,  //本地执行合约部署，目的为了模拟合约部署获取`runtime`字节码
      block_number: 0 //使用创世区块本地执行，防止合约id冲突
    }, (err, contract, data) => {
      console.log('local contract new result:', data)

	  // 使用本地执行合约部署得到的`runtime`字节码升级合约
      myContract.update(data.receipt.output.replace('0x' + chain.EVM, ''), {
      }, (err, contract, data) => {
        console.log('contract update result:', data)

        // 调用升级后的合约，发放积分
        myContract.Issue('0xc60a9d48105950a0cca07a4c6320b98c303ad42d694a634529e8e1a0a16fcdb5', 100 , { from: 'Tester001' }, (err, output, data) => {
          console.log('output2 is:', output.toString())

	      // 查看目标账户一共被发放的积分结果，升级合约成功的话结果为：300
          myContract.Query('0xc60a9d48105950a0cca07a4c6320b98c303ad42d694a634529e8e1a0a16fcdb5', { from: 'Tester001' }, (err, output, data) => {
            console.log('output3 is:', output.toString())
          })
        })
      })
    })
  })


})
```
###### 使用示例执行结果：部分结果
```
...
contract update result: { msg_type: 63,
  sequence: 7,
  return_code: 0,
  group_id: '0x0000000000000000000000000000000000000000',
  receipt:
   { result: 0,
     gas_used: 28610,
     log_entry: [ [Object] ],
     output: '' },
  block_number: 86114,
  transaction_index: 0,
  api: 'QueryTransactionReceipt',
  txhash:
   '0x40a72b99ec64fca0e1cc5025920de086a2523b4761ea5020c0f43a5240dc754a' }
output2 is: true
output3 is: 300
```

## 4.4 查询接口
合约平台提供了很多查询接口，查询的对象主要围绕：区块、交易、账户和合约。
### 4.4.1 查询区块头
- QueryBlockHeader
>查询指定的区块头，可按照区块高度`number`或者区块哈希`hash`查询目标区块头

###### 请求参数

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| block_number    | false  | number   |  目标区块高度值  |
| hash            | false  | string   |  目标`区块`的哈希值，为16进制表示，需‘0x’作为前缀 |
>**注意**：以上两个参数必须使用其中一个

###### 使用示例-1：按照区块高度查询

```java script
chain.ctr.QueryBlockHeader({
  block_number: 30, // 实际按需赋值区块高度
}, (err, data) => {
  console.log(data)
})
```

###### 使用示例-2：按照区块哈希查询

```java script
chain.ctr.QueryBlockHeader({
  hash: '0xe6eebd5b7a6eff8f0af9ac151160e59cc7ec98429822400fc78906c3895bc1aa', // 实际按需赋值哈希值
}, (err, data) => {
  console.log(data)
})
```
### 4.4.2 查询区块
- QueryLastBlock
>查询最后一个区块

###### 无需请求参数

###### 使用示例

```java script
chain.ctr.QueryLastBlock({}, (err, data) => {
  console.log(data)
})
```

- QueryBlock
>查询指定的区块，可按照区块高度`number`或者区块哈希`hash`查询目标区块

###### 请求参数

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| block_number    | false  | number   |  目标区块高度值  |
| hash            | false  | string   |  目标`区块`的哈希值，为16进制表示，需‘0x’作为前缀 |
>**注意**：以上两个参数必须使用其中一个

###### 使用示例-1：按照区块高度查询

```java script
chain.ctr.QueryBlock({
  block_number: 30, // 实际按需赋值区块高度
}, (err, data) => {
  console.log(data)
})
```

###### 使用示例-2：按照区块哈希查询

```java script
chain.ctr.QueryBlock({
  hash: '0xe6eebd5b7a6eff8f0af9ac151160e59cc7ec98429822400fc78906c3895bc1aa', // 实际按需赋值哈希值
}, (err, data) => {
  console.log(data)
})
```

### 4.4.3 查询交易
- QueryTransaction
>查询指定的交易，通过交易哈希查询

###### 请求参数

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| hash            | true  | string   |  目标`交易`的哈希值，为16进制表示，需‘0x’作为前缀 |

###### 使用示例

```java script
chain.ctr.QueryTransaction({
  hash: '0xf66621372a13c813fe978c060e11a93c28b75f90d2200b9fa700f21433a96a77' //按需配置
}, (err, data) => {
  console.log('QueryTransaction:', data)
})
```

### 4.4.4 查询收据
- QueryTransactionReceipt
>查询指定的交易收据，通过交易哈希查询

###### 请求参数

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| hash            | true  | string   |  目标`交易`的哈希值，为16进制表示，需‘0x’作为前缀 |

###### 使用示例

```java script
chain.ctr.QueryTransactionReceipt({
  hash: '0xf66621372a13c813fe978c060e11a93c28b75f90d2200b9fa700f21433a96a77' //按需配置
}, (err, data) => {
  console.log('QueryTransactionReceipt:', data)
})

```

### 4.4.5 查询账户
- QueryAccount
>查询指定账户，需给定目标账户的name，接口会通过计算传入的name哈希值作为目标账户identity；也可以直接给定目标账户的identity的16进制字符串，且以'0x'为前缀

###### 请求参数

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| from            | true  | string   |  目标账户name（非'0x'前缀）或identity的16进制表示（以'0x'为前缀），如果是name：接口会通过计算name的哈希得到此账户identity |

###### 使用示例-1：使用账户name

```java script
chain.ctr.QueryAccount({
  from: 'Tester001'
}, (err, data) => {
  console.log('QueryAccount:', data)
})

```

###### 使用示例-2：使用账户identity的16进制表示

```java script
chain.ctr.QueryAccount({
  from: '0xc60a9d48105950a0cca07a4c6320b98c303ad42d694a634529e8e1a0a16fcdb5'
}, (err, data) => {
  console.log('QueryAccount:', data)
})

```

### 4.4.6 查询合约账户
- QueryContract
>查询指定合约账户，需给定目标合约name，接口会通过计算传入的name哈希值作为目标合约identity；也可以直接给定目标合约的identity的16进制字符串，且以'0x'为前缀

###### 请求参数
>将以下参数整体封装为object传入

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| from            | true  | string   |  目标合约name（非'0x'前缀）或identity的16进制表示（以'0x'为前缀），如果是name：接口会通过计算name的哈希得到此合约 |

###### 使用示例

```java script
contractName = 'contract'+Date.now()
let myContract = chain.ctr.contract(contractName, abi)

myContract.new(bytecode, {
  from: 'Tester001'
}, (err, contract, data) => {
  console.log('contract deploy result:', data)

  // 查询刚部署的合约
  chain.ctr.QueryContract({
    from: contractName
  }, (err, data) => {
    console.log('QueryContract:', data)
  })
})
```

## 4.5 本地执行接口
本地执行为了查询或者执行合约计算，仅在合约链节点本地进行执行，而不会进行广播参与共识。本地执行的交易参数与真实参与共识的交易参数完全相同。
相比于真实交易，本地执行交易会更快、效率更高，使用此接口要明确知道此交易类似于模拟合约执行，其执行结果不会参与共识、也不会最终进入存储。

### 4.5.1 本地执行普通交易
- LocalTransaction
>合约链节点本地执行交易，并不广播参与共识

###### 请求参数
>将以下参数整体封装为object传入

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| apiName         | true  |   string    | 目标执行交易的接口名 |
| block_number    | true  |   number    | 目标交易模拟执行所在区块高度，注意：如果本地执行合约调用，此区块高度需`不低于`部署合约交易所在区块的高度 |
| 其它参数         | true  | 待定         | 其他参数按照指定的apiName的接口按需传递，比如：‘CreateAccount’，需要按照‘CreateAccount’接口的参数传递，可使用示例 |


###### 使用示例
>本地执行创建账户的交易

```java script
chain.ctr.LocalTransaction({
  apiName: 'CreateAccount',
  block_number: 40,
  from: 'Tester001',
  to: 'Tester001' + Date.now(),
  data: {
    recover_key: '0xf5e50510a04a3f659a0e89f2063f79f8c1aed5ddaab6420ac47700020d9889dc14dae4dc9843c88d8222167095d9e6ce052e8a19cbc737c3f3cddf66409dbb0a',
    auth_key: '0xf5e50510a04a3f659a0e89f2063f79f8c1aed5ddaab6420ac47700020d9889dc14dae4dc9843c88d8222167095d9e6ce052e8a19cbc737c3f3cddf66409dbb0a',
    auth_weight: 100
  }
}, (err, data) => {
  console.log(data)
  console.log(data.receipt.log_entry)

})
```

### 4.5.2 本地执行合约相关交易
>只需在合约相关操作方法(new，合约方法调用，update)的参数列表中增加配置参数，即可实现本地执行

###### 需增加的请求参数

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| local         | true  |   bool    | 启用本地执行合约操作 |
| block_number    | true  |   number    | 目标交易模拟执行所在区块高度，注意：如果本地执行合约调用，此区块高度需`不低于`部署合约交易所在区块的高度 |

###### 使用示例
>本地执行合约方法调用

```java script
myContract.new(bytecode, {
  from: 'Tester001'
}, (err, contract, data) => {
  console.log('contract deploy result:', data)
  myContract.Issue('0xc60a9d48105950a0cca07a4c6320b98c303ad42d694a634529e8e1a0a16fcdb5', 100 , {
        local: true,
        block_number: data.block_number, // 注意：如果此处使用 `data.block_number - 1` 就会调用失败，错误码为：120
        from: 'Tester001'
      }, (err, output, data) => {
        console.log('contract call data:', data)
        console.log('contract call output:', output)
      })
})
```

## 4.6 原生存证接口
### 4.6.1 原生存证交易
为了方便存证场景的开发，合约平台提供了原生存证交易接口，可实现存证目的。
>注意：目前`TEE硬件隐私合约链`不支持原生存证交易接口。

- NativeDepositData
>原生存证交易，将目标的数据在合约链上存证

###### 请求参数
>将以下参数整体封装为object传入

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| from       | true  | string   | 当前交易使用的账户        |
| to         | true  | string   | 存证的目标账户  |
| data       | true  | object   | 存证数据内容  |

> data字段内容

| 字段             | 必选    | 类型       | 说明                |
| --------------- | ----- | ----------- | -----------------  |
| payload     | true  | string     | 内容为‘0x’开头的16进制，存证数据序列化为16进制的内容 |

>**注意**：存证交易的数据payload大小会有一定的上限限制，此限制为合约链的一个配置选项，通常默认限制为：1MB，实际根据合约链的配置情况而定。

###### 使用示例
>存证数据，以及存证后的查询

```java script
chain.ctr.NativeDepositData({
  from: 'Tester001',
  to: 'Tester001',
  data: {
    payload: '0x1234'  //存证的数据内容，被序列化为16进制
  }
}, (err, data) => {
  
  //存证后，如果需要查询存证数据，可通过QueryTransaction传入存证交易的hash查询
  chain.ctr.QueryTransaction({
    hash: data.txhash
  }, (err, data) => {
    console.log('TX data:', data)
  })

  //通过QueryTransaction查询到存证交易，证明交易发生，通过QueryTransactionReceipt可以验证交易成功（return_code为0），共识后进入区块。
  chain.ctr.QueryTransactionReceipt({
    hash: data.txhash
  }, (err, data) => {
    console.log('Receipt data:', data)
  })
})
```

###### 使用示例执行结果
```json
TX data: { msg_type: 62,
  sequence: 4,
  return_code: 0,
  group_id: '0x0000000000000000000000000000000000000000',
  tx:
   { hash:
      '0x0c9987c9568a402e16fff5f4b2d0056ad6289a5b61109be947f1c7d25ccaf7dd',
     transaction_type: 40,
     timestamp: 1551861396256,
     nonce: '64',
     period: 100,
     from:
      '0xc60a9d48105950a0cca07a4c6320b98c303ad42d694a634529e8e1a0a16fcdb5',
     to:
      '0xc60a9d48105950a0cca07a4c6320b98c303ad42d694a634529e8e1a0a16fcdb5',
     value: 0,
     gas: 10000000,
     group_id: '0x0000000000000000000000000000000000000000',
     version: 2,
     data: '0x1234',
     signature:
      [ '0x73a83b1e4b497222c890c9f602bb8bf48c0ed647bd8455d10b5d39886c3341ff2342a197225306ea2ac181c18ef5912fb07dd1319238469e257afa44326a9d0900' ],
     extensions: [] },
  block_number: 269873,
  transaction_index: 0,
  api: 'QueryTransaction' }
Receipt data: { msg_type: 63,
  sequence: 5,
  return_code: 0,
  group_id: '0x0000000000000000000000000000000000000000',
  receipt:
   { result: 0,
     gas_used: 20020,
     log_entry: [ [Object] ],
     output: '' },
  block_number: 269873,
  transaction_index: 0,
  api: 'QueryTransactionReceipt' }
```

## 4.7 事件接口
### 4.7.1 订阅账户事件
- event.account
>订阅账号事件

###### 请求参数
>将以下参数整体封装为object传入

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| to         | true  | string   | 目标账户 |


###### 使用示例

```java script
// 创建事件实例
const accountEvent = chain.event.account({
  to: 'Tester001'
}, (err, data) => {
  console.log(data)
})

// 注册事件回调
accountEvent.on((err, data) => {
  console.log(data)
})

// 更新用户recover_key，触发事件
chain.ctr.SetRecoverkey({
  from: 'Tester001',
  data: {
    recover_key: '0xf5e50510a04a3f659a0e89f2063f79f8c1aed5ddaab6420ac47700020d9889dc14dae4dc9843c88d8222167095d9e6ce052e8a19cbc737c3f3cddf66409dbb0a'
  }
}, (err, data) => {})
```
### 4.7.2 取消订阅账户事件
- accountEvent.close
>取消订阅账号事件

###### 无需请求参数

###### 使用示例

```java script
// 创建事件实例
const accountEvent = chain.event.account({
  to: 'Tester001'
}, (err, data) => {
  console.log(data)
})

// 取消事件
accountEvent.close((err, data) => {
  console.log(data)
})
```
### 4.7.3 订阅合约事件
- event.contract
>订阅合约事件

###### 请求参数
>将以下参数整体封装为object传入

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| to         | true  | string   | 目标合约名称/hash |


###### 使用示例

```java script
// 创建事件实例
const contractEvent = chain.event.contract({
  to: 'first'
}, (err, data) => {
  console.log(data)
})

// 注册事件回调
contractEvent.on((err, data) => {
  console.log(data)
})

// 更新合约code，触发事件
chain.ctr.contract('first').update(bytecode, {}, (err, contract, data) => {})
```
### 4.7.4 取消订阅合约事件
- contractEvent.close
>取消订阅账号事件

###### 无需请求参数

###### 使用示例

```java script
// 创建事件实例
const contractEvent = chain.event.contract({
  to: 'first'
}, (err, data) => {
  console.log(data)
})

// 取消事件
contractEvent.close((err, data) => {
  console.log(data)
})
```
### 4.7.5 订阅主题事件
- event.topic
>订阅合约事件

###### 请求参数
>将以下参数整体封装为object传入

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| to         | true  | string   | 目标主题 |


###### 使用示例

```java script
// 创建事件实例
const topicEvent = chain.event.topic({
  to: 'create_account'
}, (err, data) => {
  console.log(data)
})

// 注册事件回调
topicEvent.on((err, data) => {
  console.log(data)
})

// 执行CreateAccount，触发create_account主题事件
chain.ctr.CreateAccount({
  from: 'Tester001',
  to: 'Tester002',
  data: {
    recover_key: '0xf5e50510a04a3f659a0e89f2063f79f8c1aed5ddaab6420ac47700020d9889dc14dae4dc9843c88d8222167095d9e6ce052e8a19cbc737c3f3cddf66409dbb0a',
    auth_key: '0xf5e50510a04a3f659a0e89f2063f79f8c1aed5ddaab6420ac47700020d9889dc14dae4dc9843c88d8222167095d9e6ce052e8a19cbc737c3f3cddf66409dbb0a',
    auth_weight: 100
  }
}, (err, data) => {})
```
### 4.7.6 取消订阅主题事件
- topicEvent.close
>取消订阅主题事件

###### 无需请求参数

###### 使用示例

```java script
// 创建事件实例
const topicEvent = chain.event.topic({
  to: 'create_account'
}, (err, data) => {
  console.log(data)
})

// 取消事件
topicEvent.close((err, data) => {
  console.log(data)
})
```
### 4.7.7 订阅区块事件
- event.block
>订阅合约事件

###### 无需请求参数

###### 使用示例

```java script
// 创建事件实例
const blockEvent = chain.event.block({}, (err, data) => {
  console.log(data)
})

// 注册事件回调，出块时触发事件
blockEvent.on((err, data) => {
  console.log(data)
})
```
### 4.7.8 取消订阅区块事件
- blockEvent.close
>取消订阅主题事件

###### 无需请求参数

###### 使用示例

```java script
// 创建事件实例
const blockEvent = chain.event.block({}, (err, data) => {
  console.log(data)
})

// 取消事件
blockEvent.close((err, data) => {
  console.log(data)
})
```

## 4.8 工具类辅助方法
为了方便使用，JS SDK提供了辅助工具类方法：
1. 可直接通过使用Chain全局对象、或者使用Chain初始化的实例chain使用：
```javascript
Chain.utils.getHash('Tester001') //通过账户name，计算得到账户的identity
```
2. 也可以通过单独引入Util使用：
```javascript
const Util = require('@alipay/mychain/lib/ant3/util')
Util.getHash('Tester001')
```

- getHash
>给定字符串计算hash，通常用来得到identity

###### 请求参数

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| msg             | true  | string   |  目标字符串，作为计算hash的输入 |
| not0x           | false | bool     |  返回结果的16进制表示是否以'0x'为前缀，true：不以'0x'为前缀，false或不指定：以'0x'为前缀 |

###### 返回值

| 返回字段   | 字段类型 | 说明                       |
| ------ | ---- | ------------------------ |
| result | string  | 16进制的字符串表示，根据参数的限定是否以'0x'为前缀 |

###### 使用示例

```java script
const accountId = Chain.utils.getHash('Tester001')
```

- toDecimal
>将十六进制转换成十进制，可传入BigNumber对象

###### 请求参数

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| value             | true  | string/BigNumber   |  目标参数 |

###### 返回值

| 返回字段   | 字段类型 | 说明                       |
| ------ | ---- | ------------------------ |
| result | number  | 10进制number |

###### 使用示例

```java script
const num = Chain.utils.toDecimal('0x64')
```

- toUtf8
>将十六进制转换成utf8字符串

###### 请求参数

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| value             | true  | string   |  16进制字符串 |

###### 返回值

| 返回字段   | 字段类型 | 说明                       |
| ------ | ---- | ------------------------ |
| result | string  | utf8字符串 |

###### 使用示例

```java script
const str = Chain.utils.toUtf8('0xe89a82e89a81e58cbae59d97e993be')
```

- getKeyInfo
>获取账户信息

###### 请求参数

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| value             | true  | string   |  账户文件(user.pem，参考：2.2.4 准备合约链的账户)读取内容；或者16进制（以'0x'为前缀）私钥字符串 |
| passphrase             | false  | string   |  账户文件(user.pem)的密码，如果value参数使用的是16进制私钥字符串，则不需要配置此密码 |

###### 返回值

| 返回字段   | 字段类型 | 说明                       |
| ------ | ---- | ------------------------ |
| result | object  | 秘钥对象 |
| result.privateKey | Buffer  | 私钥Buffer |
| result.publicKey | Buffer  | 公钥Buffer |

###### 使用示例-1

```java script
const result = Chain.utils.getKeyInfo('0x2768d0e80eb904560aa0a3ff4fc96b2f4d1e5d1fe293cc69c07808765ab20c29')
```

###### 使用示例-2

```java script
const accountKey = fs.readFileSync("./certs/user.pem", { encoding: "utf8" })
const accountPassword = "123456"
  
const result = Chain.utils.getKeyInfo(accountKey, accountPassword)
```

- decryptAESWithPassword
>TEE硬件隐私合约链，解密Receipt的output和log方法

###### 请求参数

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| output             | true  | string   |  需要解密的数据，比如合约方法返回的output，16进制（以'0x'为前缀） |
| aesKey             | true  | string或Buffer   |  此参数将作为一个password形式与目标加密的交易hash一起计算生成最终的aes对称密钥，如果使用string类型，会区分前缀是否包含'0x'来解释内容，包含'0x'则使用16进制读取，否则按照ascii编码读取 |
| hash             | true  | string   |  交易hash |

###### 返回值

| 返回字段   | 字段类型 | 说明                       |
| ------ | ---- | ------------------------ |
| result | string  | 解密之后的output |

###### 使用示例：也可以参考：“5.1 合约相关的加密交易”小节相关示例

```java script
const result = Chain.utils.decryptAESWithPassword('0x83568fd7f92d39e42523d59305c383bb24fc7a404f37d0b95d622246fa198037f91ae16edb071dec3a96c919d714c7fe99cc6c6da025376e53aa946c', '0x1c4f2919963e8dc040cfddf7d27227de', '0xf91ae16edb071dec3a96c9196f8505f6d49f4ae79707c10ac73455a95e04e03d')
```

- decryptTXWithAES
>TEE硬件隐私合约链，解密Transaction方法

###### 请求参数

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| tx             | true  | string   |  需要解密的交易数据，16进制（以'0x'为前缀） |
| aesKey             | true  | string或Buffer   |  此参数将作为一个password形式与目标加密的交易hash一起计算生成最终的aes对称密钥，如果使用string类型，会区分前缀是否包含'0x'来解释内容，包含'0x'则使用16进制读取，否则按照ascii编码读取 |
| hash             | true  | string   |  目标交易hash |

###### 返回值

| 返回字段   | 字段类型 | 说明                       |
| ------ | ---- | ------------------------ |
| result | object  | 解密之后的交易对象 |

###### 使用示例：也可以参考：“5.1 合约相关的加密交易”小节相关示例

```java script
const result = Chain.utils.decryptTXWithAES('0x00011660bbb61f5a66ab5cf78910831c41ed6b9eafb60fe3d14fa9a5a8fe4e800b30a3a8d8d6a4e8cbfc511e8f967509acf9d08c96c62da111b4886f0a3a0441dcc2887fd88243aa671a0f2fc08822d1c9d99db571b7d81207809e1f81629b38e9184221570bb1ef265d4f91d1967219fd51127712c60ae2990feaa2f4d021e71af5073de40a092de84609aead81882686595d4cf09672ef6aee28ed3894f6eb9d2b80000a4c02795d64785191bb7f076b9d2bb3552734fb0c2335f81424053f066a50242812aa92ecf82a212770d1adcb37717d01980bc7898258301d9b86344e5b338efa675e8ed7baaf6e03d464469eb24fb369444e29d08e573a287b02946e4c1dbd8e38a74a5c64f6c3e17bd8ac48626f4c240d96d5b7a1262f5034f838dd6811418cdf33eb6f1660664ea620318ec28f36b4860257a3d0b926788036e48a46aa84dbfe968e1def8a62170c286ceb5e4ef44856af6810678210ac7851f2e251aa28a4f0682d1880c27cdad697f53cc7a6d4acb1378e01b1aed9919a01f1b7c538441baaf4e1e90df9814f6da0c7989e9c56e218098f6700281bfc92f96f95c4f711c03174bbe7b0f0697ee70cbe772b53676b09d7f72547a81453021617c5e79e6484bd29c1304dcffece04cb1debfca235f513cf3f1a20326d8007038666f0f9259e5ec4af94b0cdebc1bd1f894e44eca006714cb2e2065979214f7223c6c5c9073350faa60dd6bdf3dae72c09358167adadb56cd7e0fd1d065a97d6a6c7c23132ab7357ff48e9ed913f95aff1442d0d1878', '0x1c4f2919963e8dc040cfddf7d27227de', '0xb9fc5d963d094e03072c5cad233efc80023a7a9bd68a4bca98e80c99214aa68e')
```

- generateAESKey
>TEE硬件隐私合约链，通过指定的aesKey和交易hash派生目标交易的最终aes密钥，一般用与分享单个交易aes密钥使用

###### 请求参数

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| aesKey             | true  | string或Buffer   |  此参数将作为一个password形式与目标加密的交易hash一起计算生成最终的aes对称密钥，如果使用string类型，会区分前缀是否包含'0x'来解释内容，包含'0x'则使用16进制读取，否则按照ascii编码读取 |
| hash             | true  | string   |  交易hash |

###### 返回值

| 返回字段   | 字段类型 | 说明                       |
| ------ | ---- | ------------------------ |
| result | string  | 为目标交易派生出的最终aes密钥 |

###### 使用示例

```java script
// case-1 result(hex): b2bafab38c678a4519aa9822aa6c4e53
console.log('AES Key:', Chain.utils.generateAESKey('0x3b1fa6e5c8a3df729bdb6bf415150ddd', '0x6f3f709fcce4509ca4c2e009d9be668d9f0cc9c91247180f9b6d1011cb20291c').toString('hex'))

// case-2 result(hex): 0b985d88bb3b7c932028512bcd1eb10e
console.log('AES Key:', Chain.utils.generateAESKey('1234abc', '0x6f3f709fcce4509ca4c2e009d9be668d9f0cc9c91247180f9b6d1011cb20291c').toString('hex'))

```

###### 请求参数
>将以下参数整体封装为object传入

| 参数              | 必选    | 类型          | 说明                |
| --------------- | ----- | ----------- | ----------------- |
| bytecode       | true  | string   | 目标合约的字节码，为16进制表示  |
| data           | true  | object    | 包含‘from’，‘parameters’等配置  |

> data字段内容

| 字段             | 必选    | 类型       | 说明                |
| --------------- | ----- | ----------- | -----------------  |
| encrypt         | true  |  bool       | 说明此交易是否要加密，true：加密；false/不指定：不加密 |
| rsaPublicKey    | true  | string      | 目标TEE合约链环境的节点RSA公钥， 从BaaS平台TEE合约链下载 |
| aesKey          | true  | string或Buffer| 此参数将作为一个password形式与目标加密的交易hash一起计算生成最终的aes对称密钥，如果使用string类型，会区分前缀是否包含'0x'来解释内容，包含'0x'则使用16进制读取，否则按照ascii编码读取 |
| from       | true  | string   | 需要配置的当前账户名        |
| parameters | true  | Array   | 如果合约包含初始化函数，并且此函数需要参数列表，可以通过parameters传递 |

>**注意**：相比于普通的合约方法`new` 增加了加密需要的3个参数：encrypt，rsaPublicKey，aesKey。类似的，合约方法调用、合约升级也是增加3个参数配置而已，其它参数配置与非加密使用方式一致。

>**注意**：其中aesKey参数将作为一个password形式与目标加密的交易hash一起计算，生成最终的aes对称密钥，因此每个加密交易由于hash不同，即使用相同的aesKey，最终生成的aes对称密钥也不同，这样生成方式便于交易发送者未来对部分交易的最终aes密钥进行分享，而不需要分享原始的aesKey。

###### 综合使用示例：加密合约部署、加密合约调用、加密合约升级

```java script
const solc = require('@alipay/solc')
const contract = fs.readFileSync('./CreditManager.sol', {encoding: 'ascii'})
// 第二个参数设定为"1"，会开启编译优化 optimiser
const output = solc.compile(contract, 1)
const abi = JSON.parse(output.contracts[':CreditManager'].interface)
const bytecode = output.contracts[':CreditManager'].bytecode

// 读取TEE合约链节点的公钥文件‘tee_rsa_public_key.pem’
let rsa2048 = {
  public: fs.readFileSync('./certs/tee_rsa_public_key.pem')
}
// 自定义的aes密码，此密码与加密交易的hash联合计算生成最终的aes密钥
let aes_key = '0x1c4f2919963e8dc040cfddf7d27227de'

contractName = 'contract'+Date.now()
let myContract = chain.ctr.contract(contractName, abi)

// 加密部署合约，保护隐私
myContract.new(bytecode, {
  from: 'Tester001',
  encrypt: true,
  rsaPublicKey: rsa2048.public,
  aesKey: aes_key,

}, (err, contract, data) => {
  console.log('TEE contract deploy result:', data)
  // 加密合约调用、保护隐私
  myContract.Issue('0xc60a9d48105950a0cca07a4c6320b98c303ad42d694a634529e8e1a0a16fcdb5', 100 ,
      { from: 'Tester001',
        encrypt: true,
        rsaPublicKey: rsa2048.public,
        aesKey: aes_key,
      }, (err, output, data) => {
        console.log('TEE contract output encrypted:', output)
        console.log('TEE contract output decrypted:', Util.decryptAESWithPassword(output, aes_key, data.txhash))
		
		// 解密TEE的加密交易
		chain.ctr.QueryTransaction({
          hash: data.txhash
        }, (err, data) => {
          console.log('TEE raw TX data:', data)
          console.log('TEE TX data decrypted:', Util.decryptTXWithAES(data.tx.data, aes_key, data.tx.hash))
        })
      })
})

// 加密升级合约，注意：实际升级合约需要使用二进制solc工具通过`--bin-runtime`参数编译的bytecode，而不是直接使用solc-js输出的完整bytecode
myContract.update(bytecode, {
  encrypt: true,
  rsaPublicKey: rsa2048.public,
  aesKey: aes_key,
}, (err, contract, data) => {
  console.log('TEE contract update result:', data)
})

```

### 5.2 加密本地交易
相比于普通的本地交易接口，需要加密时，同样增加以下配置参数即可，其它参数配置不变：

###### 需增加的请求参数
| 参数             | 必选    | 类型       | 说明                |
| --------------- | ----- | ----------- | -----------------  |
| local         | true  |   bool    | 启用本地执行合约操作 |
| block_number    | true  |   number    | 目标交易模拟执行所在区块高度，注意：如果本地执行合约调用，此区块高度需`不低于`部署合约交易所在区块的高度 |
| encrypt         | true  |  bool       | 说明此交易是否要加密，true：加密；false/不指定：不加密 |
| rsaPublicKey    | true  | string      | 目标TEE合约链环境的节点RSA公钥， 从BaaS平台TEE合约链下载 |
| aesKey          | true  | string或Buffer| 此参数将作为一个password形式与目标加密的交易hash一起计算生成最终的aes对称密钥，如果使用string类型，会区分前缀是否包含'0x'来解释内容，包含'0x'则使用16进制读取，否则按照ascii编码读取 |

###### 使用示例：合约相关操作

```java script
const solc = require('@alipay/solc')
const contract = fs.readFileSync('./CreditManager.sol', {encoding: 'ascii'})
// 第二个参数设定为"1"，会开启编译优化 optimiser
const output = solc.compile(contract, 1)
const abi = JSON.parse(output.contracts[':CreditManager'].interface)
const bytecode = output.contracts[':CreditManager'].bytecode

// 读取TEE合约链节点的公钥文件‘tee_rsa_public_key.pem’
let rsa2048 = {
  public: fs.readFileSync('./certs/tee_rsa_public_key.pem')
}
// 自定义的aes密码，此密码与加密交易的hash联合计算生成最终的aes密钥
let aes_key = '0x1c4f2919963e8dc040cfddf7d27227de'

// 正常交易部署合约
myContract.new(bytecode, {
  from: 'Tester001',
  encrypt: true,
  rsaPublicKey: rsa2048.public,
  aesKey: aes_key,

}, (err, contract, data) => {
  // 加密本地执行合约方法 Issue
  myContract.Issue('0xc60a9d48105950a0cca07a4c6320b98c303ad42d694a634529e8e1a0a16fcdb5', 100 ,
      { from: 'Tester001',
        local:true,  // 启用本地执行交易
        block_number: data.block_number,  // 指定区块高度
        encrypt: true,
        rsaPublicKey: rsa2048.public,
        aesKey: aes_key,
      }, (err, output, data) => {
        console.log('TEE contract output encrypted:', output)
        console.log('TEE contract output decrypted:', Util.decryptAESWithPassword(output, aes_key, data.hash))
		console.log('Receipt log:', data.receipt.log_entry)
      })
})
```

###### 使用示例执行结果
>**注意**：Receipt log 部分打印的结果中`from`和`to`字段的内容为全0的16进制，这是`TEE合约链`做的隐私保护处理，是正常结果。

```json
TEE contract output encrypted: 0x0aeff7aab0f009e14e16bb5c470df13508493fd2cf6ff1a87e7297390779856d6e8dc3efce73f87d652c20eedc945766b1bb8dfcc7e7bc64d1b2a368
TEE contract output decrypted: 0x0000000000000000000000000000000000000000000000000000000000000001
Receipt log: [ { from:
     '0x0000000000000000000000000000000000000000000000000000000000000000',
    to:
     '0x0000000000000000000000000000000000000000000000000000000000000000',
    topics: [ 'call_contract' ],
    log_data: '' },
  { from:
     '0x0000000000000000000000000000000000000000000000000000000000000000',
    to:
     '0x0000000000000000000000000000000000000000000000000000000000000000',
    topics:
     [ '51997c2e47b39f91064106ab1e7b0477b7d16bcf7f7e67def1a3ce39e11e5099',
       'c60a9d48105950a0cca07a4c6320b98c303ad42d694a634529e8e1a0a16fcdb5',
       '0000000000000000000000000000000000000000000000000000000000000064' ],
    log_data: '' } ]
```