Commit 3e7bd2a1 authored by Hugo's avatar Hugo

update

parent 0c84ad7b
## 1 jsvm 接口 [TOC]
## 1 jsvm 接口
[TOC] [TOC]
## 1 JavaScript 语言DApp开发
### 1.1 部署一个jsvm合约 CreateTransaction * 使用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
function localprocess(args) {
var local = MatchGameTable(this.kvc)
local.addlogs(this.logs) //操作join表,达到自动更新关联表的目的
local.save() //保存数据
return this.kvc.receipt()
}
ExecLocal.prototype.NewGame = function(args) {
return localprocess.call(this, args)
}
```
#### 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
> 这里创建的是原始交易,除此之外,还需要进行交易的签名以及发送操作 > 这里创建的是原始交易,除此之外,还需要进行交易的签名以及发送操作
> 如果合约中要使用数字货币,在调用合约前需要先向合约充值 > 如果合约中要使用数字货币,在调用合约前需要先向合约充值
...@@ -67,7 +335,7 @@ Request1 部署合约交易: ...@@ -67,7 +335,7 @@ Request1 部署合约交易:
} }
``` ```
### 1.2 调用创建的jsvm合约的方法 CreateTransaction #### 1.3.2 调用创建的jsvm合约的方法 CreateTransaction
**请求报文:** **请求报文:**
```json ```json
...@@ -145,7 +413,7 @@ Response: ...@@ -145,7 +413,7 @@ Response:
} }
``` ```
### 1.3 查询创建的jsvm合约 #### 1.3.3 查询创建的jsvm合约
**请求报文:** **请求报文:**
...@@ -225,139 +493,4 @@ Response: ...@@ -225,139 +493,4 @@ Response:
}, },
"error": null "error": null
} }
```
### 1.4 如何用JavaScript写一个自己的合约
>一般js文件包含如下规则:
>定义Init(context)函数:该函数在部署合约时会被调用,作用是为一个合约准备资源,主要是存储对象kvcreator和上下文信息。
>定义Exec对象的xxxx方法:将一些数据写入stateDB,并传递一些需要记录在表中的log信息
>定义ExecLocal对象的xxxx:将log信息写入表或写入一些数据到localDB
>定义Query对象的xxxx方法:查询表中的数据或者
>基于chain33的框架,通过上面1.2的Call调用将会依次执行对应的Exec.的xxxx方法和ExecLocal的xxxx方法,比如Call的payload.funcname为"NewGame",就会依次执行Exec的NewGame方法和ExecLocal的NewGame方法。
下面结合game.js具体介绍一下
>game.js定义了一种猜数字的游戏,游戏规则:
>庄家出一个 0 - 10 的数字 hash(随机数 + 9) (一共的赔偿金额)。 对应NewGame方法。
>用户可以猜这个数字,多个用户都可以猜测。对应Guess方法
>开奖。对应CloseGame方法
>注意:像上述三个的方法,Exec对象和ExecLocal对象要分别实现的一个同名的方法。
#### 1.4.1 定义存储结构
为了存储上述信息,需要创建三张表,一张GameLocalTable,一张MatchLocalTable,一张MatchGameTable,其中MatchGameTable是基于上述两表的连接表类似于关系数据库的join操作产生的临时表。
创建一张表包含config和defaultvalue两部分,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.4.2 Init函数
```js
function Init(context) {
this.kvc = new kvcreator("init")//创建init类型对象,用于存储key,value值
this.context = context //保存上下文信息
return this.kvc.receipt()
}
```
#### 1.4.3 Exec的xxxx方法
此方法可以通过上面1.2的方法调用到,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.4.4 ExecLocal的xxxx方法
此方法将对应的Exec的xxxx方法传递的log信息写入到对应的表中,或者调用kvc.add()直接将数据写入stateDB。
```js
function localprocess(args) {
var local = MatchGameTable(this.kvc)
local.addlogs(this.logs) //操作join表,达到自动更新关联表的目的
local.save() //保存数据
return this.kvc.receipt()
}
ExecLocal.prototype.NewGame = function(args) {
return localprocess.call(this, args)
}
```
#### 1.4.5 Query的xxxx方法
此方法可以通过上面1.3的方法调用到,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)
}
``` ```
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment