# JavaScript 语言DApp开发

- 使用JavaScript语言进行DApp开发，首先要按照一定格式编写一个合约，然后可以使用cli调试工具或者调用JSONRPC接口将合约部署到Chain33上，通过调用相应的方法完成想要的操作。

## 1.1 用JavaScript写一个自己的合约

> 一般js文件包含如下规则:
> 定义Init(context)函数：该函数在部署合约时会被调用，作用是为一个合约准备资源，主要是存储对象kvcreator和上下文信息context。
> 定义Exec对象的xxxx方法：将一些数据写入stateDB，并传递一些需要记录在表中的log信息。
> 定义ExecLocal对象的xxxx：将log信息写入表或写入一些数据到localDB。
> 定义Query对象的xxxx方法：查询表中的数据或者localDB中的数据。
>
> 基于chain33的框架，通过jsvm的Call调用一个方法会依次执行对应的Exec对象和ExecLocal对象的同名方法，比如Call的payload.funcname为”NewGame”，就会依次执行Exec对象的NewGame方法和ExecLocal对象的NewGame方法。

下面结合[game.js](https://github.com/33cn/plugin/blob/master/plugin/dapp/js/executor/game.js)具体介绍一下

> game.js定义了一种猜数字的游戏，游戏规则：
> 庄家出一个 0 - 10 的数字 hash(随机数 + 9) (一共的赔偿金额)。 对应NewGame方法。
> 用户可以猜这个数字，多个用户都可以猜测。对应Guess方法
> 开奖。对应CloseGame方法
> 下面仅以NewGame方法举例，另外两个方法可以参考上面链接的代码。

#### 1.1.1 定义存储结构

为了存储上述信息，需要创建三张表，一张GameLocalTable，一张MatchLocalTable，一张MatchGameTable，其中MatchGameTable是基于上述两表的连接表，类似于关系数据库的join操作产生的临时表。
创建一张表包含config和defaultvalue两部分（创建连接表只需调用JoinTable方法即可）config设置表结构，包含表属性配置项（用#开头）和列属性配置两类，表属性有表名，主键和存储db类型三个。defaultvalue用于设置列属性的默认值。
下面是创建GameLocalTable的代码：

```js
function GameLocalTable(kvc) {
    this.config = {
        "#tablename" : "game",//定义表名
        "#primary" : "gameid", //定义主键
        "#db" : "localdb",            //定义存储的db类型，一般为localdb
        "gameid"    : "%018d",  //表主键，游戏id，可以用交易总索引(txID())定义
        "status" : "%d",               //游戏状态：0-未开始，1-开始，2-关闭
        "hash" : "%s",                  //交易hash
        "addr" : "%s",                  //创建交易者的地址
    }
    this.defaultvalue = {
        "gameid" : 0,
        "status" : 0,
        "hash" : "",
        "addr" : "",
    }
    return new Table(kvc, this.config, this.defaultvalue) 
}
```

下面是创建MatchLocalTable的代码：

```js
function MatchLocalTable(kvc) {
    this.config = {
        "#tablename" : "match",   //定义表名
        "#primary" : "id",                  //定义主键
        "#db" : "localdb",                  //定义存储的db类型，一般为localdb
        "id"    : "%018d",                   //表主键，可以用交易总索引(txID())定义
        "gameid" : "%018d",           //表外键，对应GameLocalTable表主键
        "hash" : "%s",                         //交易hash
        "addr" : "%s",                          //参与游戏的猜数字者的地址
    }
    this.defaultvalue = {
        "id" : 0,
        "gameid" : 0,
        "hash" : "",
        "addr" : "",
    }
    return new Table(kvc, this.config, this.defaultvalue)  
}
```

下面是创建MatchGameTable的代码：

```js
function MatchGameTable(kvc) {
    //参数lefttable是使用外键的表，数据量相对较大，参数index是需要查询连接表的字段名
    return new JoinTable(MatchLocalTable(kvc), GameLocalTable(kvc), "addr#status")
}
```

#### 1.1.2 Init函数

```js
function Init(context) {
    this.kvc = new kvcreator("init")//创建init类型对象，用于存储key,value值
    this.context = context //保存上下文信息
    return this.kvc.receipt()
}
```

#### 1.1.3 Exec的xxxx方法

此方法可以通过chain33-cli调试工具或JSONRPC的call方法调用到，xxxx与payload.funcname值一致。主要作用是通过kvc.add()将一些信息写入stateDB，通过kvc.addlog()传递log信息。

```js
Exec.prototype.NewGame = function(args) {
    //
    var game = {__type__ : "game"}
    game.gameid = this.txID()
    game.height = this.context.height
    game.randhash = args.randhash
    game.bet = args.bet
    game.hash = this.context.txhash
    game.obet = game.bet
    game.addr = this.context.from
    game.status = 1 //open
    //最大值是 9000万,否则js到 int 会溢出
    if (game.bet < 10 * COINS || game.bet > 10000000 * COINS) {
        throwerr("bet low than 10 or hight than 10000000")
    }
    if (this.kvc.get(game.randhash)) { //如果randhash 已经被使用了
        throwerr("dup rand hash")
    }
    var err = this.acc.execFrozen(this.name, this.context.from, game.bet)    //冻结庄家的奖池
    throwerr(err)
    this.kvc.add(game.gameid, game)  //存储key:gameid与value:game到stateDB
    this.kvc.add(game.randhash, "ok")//存储key:randhash与value:"ok"到stateDB
    this.kvc.addlog(game)  //传递game对象到ExecLocal.prototype.NewGame
    return this.kvc.receipt()
}
```

#### 1.1.4 ExecLocal的xxxx方法

此方法将对应的Exec的xxxx方法传递的log信息写入到对应的表中，或者调用kvc.add()直接将数据写入stateDB。

```js
Exec.prototype.NewGame = function(args) {
    //
    var game = {__type__ : "game"}
    game.gameid = this.txID()
    game.height = this.context.height
    game.randhash = args.randhash
    game.bet = args.bet
    game.hash = this.context.txhash
    game.obet = game.bet
    game.addr = this.context.from
    game.status = 1 //open
    //最大值是 9000万,否则js到 int 会溢出
    if (game.bet < 10 * COINS || game.bet > 10000000 * COINS) {
        throwerr("bet low than 10 or hight than 10000000")
    }
    if (this.kvc.get(game.randhash)) { //如果randhash 已经被使用了
        throwerr("dup rand hash")
    }
    var err = this.acc.execFrozen(this.name, this.context.from, game.bet)    //冻结庄家的奖池
    throwerr(err)
    this.kvc.add(game.gameid, game)  //存储key:gameid与value:game到stateDB
    this.kvc.add(game.randhash, "ok")//存储key:randhash与value:"ok"到stateDB
    this.kvc.addlog(game)  //传递game对象到ExecLocal.prototype.NewGame
    return this.kvc.receipt()
}
```

#### 1.1.5 Query的xxxx方法

此方法可以通过chain33-cli调试工具或JSONRPC的query方法调用到，xxxx与payload.funcname值一致，实现按照传入参数查询数据的功能，可以通过表进行查询，也可以直接查询localDB。

```js
//提供按照游戏创建者地址查询game信息
Query.prototype.ListGameByAddr = function(args) {
    var local = GameLocalTable(this.kvc)
    var q = local.query("addr", args.addr, args.primaryKey, args.count, args.direction)
    return querytojson(q)
}
```

## 1.2 使用cli调试工具部署智能合约

chain33-cli是我们的调试程序，使用chain33-cli命令可以方便的完成一些操作，下面演示使用chain33-cli命令部署智能合约到chain33链上以及调用智能合约中的方法。

------

> **step1:**使用chain33-cli命令将智能合约部署到chain33链上：

```
./chain33-cli send jsvm create -c "合约代码文件的路径" -n "合约名" -k "合约创建者的私钥或者地址"
root@ubuntu055-3:/home/lcj0# ./chain33-cli send jsvm create -c "/home/hugo/game.js" -n game -k 14KEKbYtKKQm4wMthSK9J4La4nAiidGozt
```

------

> **step2：**上面的合约代码部署成功后，会生成一个交易哈希，然后使用此哈希查询交易：

```
./chain33-cli tx query_hash -s "交易哈希"
root@ubuntu055-3:/home/lcj0# ./chain33-cli tx query_hash -s 0xd4cfd1606ea1d9382b3334351e3e741cad8a68b4e7b38a4b02c981203865cb03
```

- 在返回的结果可以看到如下：其中name 合约名称，涉及到合约转账或取回操作时要使用此名称；code是js文件内容；

```json
...
"execer": "jsvm",
"payload": {
    "create": {
        "code": "...js文件内容...",
        "name": "game"
    },
    "ty": 0
},
...
```

------

> **step3:**向合约充值

由于game合约需要创建游戏者(庄家)首先抵押一些资产做奖池，所以需要先充值到合约中，通过命令行充100 BTY到合约中，注意-e 参数必须是”user.jsvm.合约名”的形式。

```
./chain33-cli send coins send_exec -a BTY个数 -e "合约名" -n "附言" -k "合约创建者的私钥或者地址"
./chain33-cli send coins send_exec -a 100 -e "user.jsvm.game" -n "12312" -k 14KEKbYtKKQm4wMthSK9J4La4nAiidGozt
```

> 使用查询交易命令查询充值是否成功。

```
**./chain33-cli tx query_hash -s 0xa627e07e3ba7c47fd2e16812b34f82d6e07c6c074d2043abdb6b2d57dc9b4d4b
```
```json
...
    "ty": 8,
    "tyName": "LogExecDeposit",
    "log": {
        "execAddr": "15mjHYNvPkSMrp3SQxtczXL6cinMAEdeKs",
        "prev": {
            "currency": 0,
            "balance": "0",
            "frozen": "0",
            "addr": "14KEKbYtKKQm4wMthSK9J4La4nAiidGozt"
        },
        "current": {
            "currency": 0,
            "balance": "10000000000",
            "frozen": "0",
            "addr": "14KEKbYtKKQm4wMthSK9J4La4nAiidGozt"
        }
    }
 ...
```

可以看到current.balance为100* 1e8

------

> **step4:**充值完成后，调用合约中的NewGame方法创建游戏。

```
./chain33-cli send jsvm call -n "合约名" -f  "方法名" -a "以json格式表示的参数列表" -k "合约创建者的私钥或者地址"
./chain33-cli send jsvm call -n game -f NewGame -a "{\"bet\":1500000000, \"randhash\":\"0x5d2ac15654aa1859af80d1dfa5d7a5775c359fe3a49abce03796f4fbc313b57f\"}" -k 14KEKbYtKKQm4wMthSK9J4La4nAiidGozt
```

> 使用查询交易命令查询是否创建成功。

```json
...
    "ty": 9,
    "tyName": "LogExecFrozen",
    "log": {
        "execAddr": "15mjHYNvPkSMrp3SQxtczXL6cinMAEdeKs",
        "prev": {
            "currency": 0,
            "balance": "10000000000",
            "frozen": "0",
            "addr": "14KEKbYtKKQm4wMthSK9J4La4nAiidGozt"
        },
        "current": {
            "currency": 0,
            "balance": "8500000000",
            "frozen": "1500000000",
            "addr": "14KEKbYtKKQm4wMthSK9J4La4nAiidGozt"
        }
    },
...
```

可以看到冻结量：current.frozen为15 *1e8，剩余量：current.balance为85* 1e8

```json
...
    "ty": 10000,
    "tyName": "TyLogJs",
    "log": {
        "data": "{\"__type__\":\"game\",\"addr\":\"14KEKbYtKKQm4wMthSK9J4La4nAiidGozt\",\"bet\":1500000000,\"gameid\":4100000,\"hash\":\"0x2b3e9d1bd4dfcd6766f6aacc366d8e360e9896de1cc4da0501b30d696a506d2a\",\"height\":41,\"obet\":1500000000,\"randhash\":\"0x5d2ac15654aa1859af80d1dfa5d7a5775c359fe3a49abce03796f4fbc313b57f\",\"status\":1}"
    },
...
```

可以看到游戏创建成功了，gameid为4100000。然后可以使用同样的方法，调用合约中的Guess方法和CloseGame方法来猜数字和开奖，不在此赘述。

------

> **step5:** 调用查询接口列出由某个地址创建的游戏

```
./chain33-cli jsvm query -a "以json格式表示的参数列表" -f "方法名" -n "合约名"
./chain33-cli jsvm query -a "{\"addr\":\"14KEKbYtKKQm4wMthSK9J4La4nAiidGozt\", \"count\":20}" -f "ListGameByAddr" -n game
```

返回内容如下：

```json
{
    "data": "[{\"left\":{\"__type__\":\"game\",\"addr\":\"14KEKbYtKKQm4wMthSK9J4La4nAiidGozt\",\"bet\":1500000000,\"gameid\":4100000,\"hash\":\"0x2b3e9d1bd4dfcd6766f6aacc366d8e360e9896de1cc4da0501b30d696a506d2a\",\"height\":41,\"obet\":1500000000,\"randhash\":\"0x5d2ac15654aa1859af80d1dfa5d7a5775c359fe3a49abce03796f4fbc313b57f\",\"status\":1}}]"
}
```

> 可以使用相同的方法调用ListMatchByAddr方法，列举出某地址创建的处于某种状态的游戏，由于用到连表查询，需要先通过方法JoinKey查到结果，再将结果作为参数，调用ListMatchByAddr方法获得最终结果。

## 1.3 调用JSONRPC接口部署智能合约

#### 1.3.1 部署一个jsvm合约 CreateTransaction

> 这里创建的是原始交易，除此之外，还需要进行交易的签名以及发送操作
> 如果合约中要使用数字货币，在调用合约前需要先向合约充值

**请求报文：**

```json
{
    "jsonrpc":"2.0",
    "method": "Chain33.CreateTransaction",
  "params": [
    {
      "execer": "string",
      "actionName": "string",
      "payload": {
        "code": "string",
        "name":"string"
      }
    }
  ],
  "id":int32,
}
```

**参数说明：**

| 参数         | 类型   | 是否必填 | 说明                       |
| :----------- | :----- | :------- | :------------------------- |
| execer       | string | 是       | 执行器名称，这里固定为jsvm |
| actionName   | string | 是       | 操作名称，这里固定为Create |
| payload.code | string | 是       | javascript代码             |
| payload.name | string | 是       | 合约名                     |

**响应报文：**

```json
{
    "id":int32,
    "result":"string",
    "error":null
}
```

**参数说明：**

| 参数   | 类型   | 说明                   |
| :----- | :----- | :--------------------- |
| result | string | 交易内容十六进制字符串 |

**示例：**
Request1 部署合约交易:

```json
{
  "method": "Chain33.CreateTransaction",
  "params": [
    {
      "execer": "jsvm",
      "actionName": "Create",
      "payload": {
        "code": "//........................\n//............: ............... 0 - 10 ......... hash(......... + 9) (.....................) NewGame()\n//............................................................ Guess()\n//...... CloseGame()\nfunction Init(context) {\n    this.kvc = new kvcreator(\"init\")\n    this.context = context\n    return this.kvc.receipt()\n}\n\nvar MIN_WAIT_BLOCK = 2\nvar RAND_MAX = 10\n\nfunction ExecInit() {\n    this.acc = new account(this.kvc, \"coins\", \"bty\")\n}\n\nExec.prototype.NewGame = function(args) {\n    var game = {__type__ : \"game\"}\n    game.gameid = this.txID()\n    game.height = this.context.height\n    game.randhash = args.randhash\n    game.bet = args.bet\n    game.hash = this.context.txhash\n    game.obet = game.bet\n    game.addr = this.context.from\n    game.status = 1 //open\n    //............ 9000...,......js... int .........\n    if (game.bet < 10 * COINS || game.bet > 10000000 * COINS) {\n        throwerr(\"bet low than 10 or hight than 10000000\")\n    }\n    if (this.kvc.get(game.randhash)) { //......randhash ..................\n        throwerr(\"dup rand hash\")\n    }\n    var err = this.acc.execFrozen(this.name, this.context.from, game.bet)\n    throwerr(err)\n    this.kvc.add(game.gameid, game)\n    this.kvc.add(game.randhash, \"ok\")\n    this.kvc.addlog(game)\n    return this.kvc.receipt()\n}\n\nExec.prototype.Guess = function(args) {\n    var match = {__type__ : \"match\"}\n    match.gameid = args.gameid\n    match.bet = args.bet\n    match.id = this.txID()\n    match.addr = this.context.from\n    match.hash = this.context.txhash\n    match.num = args.num\n    var game = this.kvc.get(match.gameid)\n    if (!game) {\n        throwerr(\"guess: game id not found\")\n    }\n    if (game.status != 1) {\n        throwerr(\"guess: game status not open\")\n    }\n    if (this.context.from == game.addr) {\n        throwerr(\"guess: game addr and match addr is same\")\n    }\n    if (match.bet < 1 * COINS || match.bet > game.bet / RAND_MAX) {\n        throwerr(\"match bet litte than 1 or big than game.bet/10\")\n    }\n    var err = this.acc.execFrozen(this.name, this.context.from, match.bet)\n    console.log(this.name, this.context.from, err)\n    throwerr(err)\n    this.kvc.add(match.id, match)\n    this.kvc.addlog(match)\n    return this.kvc.receipt()\n}\n\nExec.prototype.CloseGame = function(args) {\n    var local = MatchLocalTable(this.kvc)\n    var game = this.kvc.get(args.gameid)\n    if (!game) {\n        throwerr(\"game id not found\")\n    }\n    var querykey = local.get(\"gameid\", args)\n    var matches = local.query(\"gameid\", querykey, \"\", 0, 1)\n    if (!matches) {\n        matches = []\n    }\n    var n = -1\n    for (var i = 0; i < RAND_MAX; i++) {\n        if (Sha256(args.randstr + i) == game.randhash) {\n            n = i\n        }\n    }\n    if (n == -1) {\n        throwerr(\"err rand str\")\n    }\n    //.........................................................\n    if (this.context.height - game.height < MIN_WAIT_BLOCK) {\n        throwerr(\"close game must wait \"+MIN_WAIT_BLOCK+\" block\")\n    }\n    for (var i = 0; i < matches.length; i++) {\n        var match = matches[i].left\n        if (match.num == n) {\n            //................................................................................................ this\n            win.call(this, game, match)\n        } else {\n            fail.call(this, game, match)\n        }\n    }\n    if (game.bet > 0) {\n        var err = this.acc.execActive(this.name, game.addr, game.bet)\n        throwerr(err)\n        game.bet = 0\n    }\n    game.status = 2\n    this.kvc.add(game.gameid, game)\n    this.kvc.addlog(game)\n    return this.kvc.receipt()\n}\n\nfunction win(game, match) {\n    var amount = (RAND_MAX - 1) * match.bet\n    if (game.bet - amount < 0) {\n        amount = game.bet\n    }\n    var err \n    if (amount > 0) {\n        err = this.acc.execTransFrozenToActive(this.name, game.addr, match.addr, amount)\n        throwerr(err, \"execTransFrozenToActive\")\n        game.bet -= amount\n    }\n    err = this.acc.execActive(this.name, match.addr, match.bet)\n    throwerr(err, \"execActive\")\n}\n\nfunction fail(game, match) {\n    var amount = match.bet\n    err = this.acc.execTransFrozenToFrozen(this.name, match.addr, game.addr, amount)\n    throwerr(err)\n    game.bet += amount\n}\n\nExec.prototype.ForceCloseGame = function(args) {\n    var local = new MatchLocalTable(this.kvc)\n    var game = this.kvc.get(args.id)\n    if (!game) {\n        throwerr(\"game id not found\")\n    }\n    var matches = local.getmath(args.id)\n    if (!matches) {\n        matches = []\n    }\n    if (this.context.height - game.height < 100) {\n        throwerr(\"force close game must wait 100 block\")\n    }\n    for (var i = 0; i < matches.length; i++) {\n        var match = matches[i]\n        win.call(this.kvc, game, match)\n    }\n    if (game.bet > 0) {\n        var err = this.acc.execActive(this.name, game.addr, game.bet)\n        throwerr(err)\n        game.bet = 0\n    }\n    game.status = 2\n    this.kvc.add(game.gameid, game)\n    this.kvc.addlog(game)\n    return this.kvc.receipt()\n}\n\nExecLocal.prototype.NewGame = function(args) {\n    return localprocess.call(this, args)\n}\n\nExecLocal.prototype.Guess = function(args) {\n    return localprocess.call(this, args)\n}\n\nExecLocal.prototype.CloseGame = function(args) {\n    return localprocess.call(this, args)\n}\n\nExecLocal.prototype.ForceCloseGame = function(args) {\n    return localprocess.call(this, args)\n}\n\nfunction localprocess(args) {\n    var local = MatchGameTable(this.kvc)\n    local.addlogs(this.logs)\n    local.save()\n    return this.kvc.receipt()\n}\n\nQuery.prototype.ListGameByAddr = function(args) {\n    var local = GameLocalTable(this.kvc)\n    var q = local.query(\"addr\", args.addr, args.primaryKey, args.count, args.direction)\n    return querytojson(q)\n}\n\nQuery.prototype.ListMatchByAddr = function(args) {\n    var local = MatchGameTable(this.kvc)\n    var q= local.query(\"addr#status\", args[\"addr#status\"], args.primaryKey, args.count, args.direction)\n    return querytojson(q)\n}\n\nfunction GameLocalTable(kvc) {\n    this.config = {\n        \"#tablename\" : \"game\",\n        \"#primary\" : \"gameid\",\n        \"#db\" : \"localdb\",\n        \"gameid\"    : \"%018d\",\n        \"status\" : \"%d\",\n        \"hash\" : \"%s\",\n        \"addr\" : \"%s\",\n    }\n    this.defaultvalue = {\n        \"gameid\" : 0,\n        \"status\" : 0,\n        \"hash\" : \"\",\n        \"addr\" : \"\",\n    }\n    return new Table(kvc, this.config, this.defaultvalue) \n}\n\nfunction MatchLocalTable(kvc) {\n    this.config = {\n        \"#tablename\" : \"match\",\n        \"#primary\" : \"id\",\n        \"#db\" : \"localdb\",\n        \"id\"    : \"%018d\",\n        \"gameid\" : \"%018d\",\n        \"hash\" : \"%s\",\n        \"addr\" : \"%s\",\n    }\n    this.defaultvalue = {\n        \"id\" : 0,\n        \"gameid\" : 0,\n        \"hash\" : \"\",\n        \"addr\" : \"\",\n    }\n    return new Table(kvc, this.config, this.defaultvalue)  \n}\n\nfunction MatchGameTable(kvc) {\n    return new JoinTable(MatchLocalTable(kvc), GameLocalTable(kvc), \"addr#status\")\n}\n",
        "name": "game"
      }
    }
  ],
  "id": 0
}
```

#### 1.3.2 调用创建的jsvm合约的方法 CreateTransaction

**请求报文：**

```json
{
    "jsonrpc":"2.0",
    "id":0,
    "method": "Chain33.CreateTransaction",
    "params": [
        {
        "execer": "string",
        "actionName": "string",
        "payload": {
            "name": "string",
            "funcname": "string",
            "args": "string"
        }
        }
    ],
}
```

**参数说明：**

| 参数             | 类型   | 说明                               |
| :--------------- | :----- | :--------------------------------- |
| execer           | string | 合约名字，固定格式为user.jsvm.xxxx |
| actionName       | string | 调用，这里固定为Call               |
| payload.name     | string | 合约名字，之前定义的名字           |
| payload.funcname | string | 调用的方法名                       |
| payload.args     | string | 参数列表                           |

**响应报文：**

```json
{
    "id":int32,
    "result":"string",
    "error":null
}
```

**参数说明：**

| 参数   | 类型   | 说明                   |
| :----- | :----- | :--------------------- |
| result | string | 交易内容十六进制字符串 |

**示例：**
Request：

```json
{
    "jsonrpc":"2.0",
    "id":0,
    "method": "Chain33.CreateTransaction",
    "params": [
        {
        "execer": "user.jsvm.game",
        "actionName": "Call",
        "payload": {
            "name": "game",
            "funcname": "NewGame",
            "args": "{\"bet\":10000000000, \"randhash\":\"0x5d2ac15654aa1859af80d1dfa5d7a5775c359fe3a49abce03796f4fbc313b57f\"}"
        }
        }
    ],
}
```

Response:

```json
{
  "id": 0,
  "result": "0a0e757365722e6a73766d2e67616d651279180112750a0467616d6512074e657747616d651a647b22626574223a31303030303030303030302c202272616e6468617368223a22307835643261633135363534616131383539616638306431646661356437613537373563333539666533613439616263653033373936663466626333313362353766227d20a08d0630ae97b68eead9ec99083a2231356d6a48594e76506b534d72703353517874637a584c3663696e4d414564654b73",
  "error": null
}
```

#### 1.3.3 查询创建的jsvm合约

**请求报文：**

```json
{
  "jsonrpc":"2.0",
  "method": "Chain33.Query",
  "params": [
    {
      "execer": "string",
      "funcName": "string",
      "payload": {
        "name": "string",
        "funcname": "string",
        "args": "{\"addr\":\"14KEKbYtKKQm4wMthSK9J4La4nAiidGozt\", \"count\":20}"
      }
    }
  ],
  "id": 0
}
```

**参数说明：**

| 参数             | 类型   | 是否必填 | 说明                                                 |
| :--------------- | :----- | :------- | :--------------------------------------------------- |
| execer           | string | 是       | 执行器名称，这里固定为jsvm                           |
| funcName         | string | 是       | 操作名称，这里固定为Query                            |
| payload.name     | string | 是       | 合约名                                               |
| payload.funcname | string | 是       | javascript中定义的查询方法名                         |
| payload.args     | string | 是       | javascript中定义的查询方法的参数列表，用json格式表示 |

**响应报文：**

```json
{
  "id": int32,
  "result": {
    "data": "string"
  },
  "error": null
}
```

**参数说明：**

| 参数        | 类型说明 |          |
| :---------- | :------- | -------- |
| result.data | string   | 查询结果 |

**示例：**
Request：

```json
  "method": "Chain33.Query",
  "params": [
    {
      "execer": "jsvm",
      "funcName": "Query",
      "payload": {
        "name": "game",
        "funcname": "ListGameByAddr",
        "args": "{\"addr\":\"14KEKbYtKKQm4wMthSK9J4La4nAiidGozt\", \"count\":20}"
      }
    }
  ],
  "id": 0
}
```

Response：

```json
{
  "id": 0,
  "result": {
    "data": "[{\"left\":{\"__type__\":\"game\",\"addr\":\"14KEKbYtKKQm4wMthSK9J4La4nAiidGozt\",\"bet\":10000000000,\"gameid\":121400000,\"hash\":\"0xd3894f7ab60a535d8de47f8a5e06d7c127a22bfe9ff8f1e00971fae19091126f\",\"height\":1214,\"obet\":10000000000,\"randhash\":\"0x5d2ac15654aa1859af80d1dfa5d7a5775c359fe3a49abce03796f4fbc313b57f\",\"status\":1}}]"
  },
  "error": null
}
```

