Commit 7b849e3c authored by 张振华's avatar 张振华

Merge branch 'master' into guess

parents 03065e62 69aba101
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cmd
import (
"fmt"
"io/ioutil"
"os"
"github.com/33cn/chain33/rpc/jsonclient"
rpctypes "github.com/33cn/chain33/rpc/types"
"github.com/33cn/chain33/types"
jsty "github.com/33cn/plugin/plugin/dapp/js/types"
"github.com/33cn/plugin/plugin/dapp/js/types/jsproto"
//"github.com/gojson"
"github.com/spf13/cobra"
)
//JavaScriptCmd :
func JavaScriptCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "jsvm",
Short: "Java Script VM contract",
Args: cobra.MinimumNArgs(1),
}
cmd.AddCommand(
JavaScriptCreateCmd(),
JavaScriptCallCmd(),
JavaScriptQueryCmd(),
)
return cmd
}
// JavaScriptCreateCmd :
func JavaScriptCreateCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "create",
Short: "create java script contract",
Run: createJavaScriptContract,
}
createJavaScriptContractFlags(cmd)
return cmd
}
func createJavaScriptContractFlags(cmd *cobra.Command) {
cmd.Flags().StringP("code", "c", "", "path of js file,it must always be in utf-8.")
cmd.MarkFlagRequired("code")
cmd.Flags().StringP("name", "n", "", "contract name")
cmd.MarkFlagRequired("name")
}
func createJavaScriptContract(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
patch, _ := cmd.Flags().GetString("code")
name, _ := cmd.Flags().GetString("name")
codestr, err := ioutil.ReadFile(patch)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
create := &jsproto.Create{
Code: string(codestr),
Name: name,
}
params := &rpctypes.CreateTxIn{
Execer: jsty.JsX,
ActionName: "Create",
Payload: types.MustPBToJSON(create),
}
var res string
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.CreateTransaction", params, &res)
ctx.RunWithoutMarshal()
}
// JavaScriptCallCmd :
func JavaScriptCallCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "call",
Short: "call java script contract",
Run: callJavaScript,
}
callJavaScriptFlags(cmd)
return cmd
}
func callJavaScriptFlags(cmd *cobra.Command) {
cmd.Flags().StringP("name", "n", "", "java script contract name")
cmd.MarkFlagRequired("name")
cmd.Flags().StringP("funcname", "f", "", "java script contract funcname")
cmd.MarkFlagRequired("funcname")
cmd.Flags().StringP("args", "a", "", "json str of args")
}
func callJavaScript(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
name, _ := cmd.Flags().GetString("name")
funcname, _ := cmd.Flags().GetString("funcname")
input, _ := cmd.Flags().GetString("args")
call := &jsproto.Call{
Name: name,
Funcname: funcname,
Args: input,
}
params := &rpctypes.CreateTxIn{
Execer: "user." + jsty.JsX + "." + name,
ActionName: "Call",
Payload: types.MustPBToJSON(call),
}
var res string
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.CreateTransaction", params, &res)
ctx.RunWithoutMarshal()
}
//JavaScriptQueryCmd :
func JavaScriptQueryCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "query",
Short: "query java script contract",
Run: queryJavaScript,
}
queryJavaScriptFlags(cmd)
return cmd
}
func queryJavaScriptFlags(cmd *cobra.Command) {
cmd.Flags().StringP("name", "n", "", "java script contract name")
cmd.MarkFlagRequired("name")
cmd.Flags().StringP("funcname", "f", "", "java script contract funcname")
cmd.MarkFlagRequired("funcname")
cmd.Flags().StringP("args", "a", "", "json str of args")
}
func queryJavaScript(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
name, _ := cmd.Flags().GetString("name")
funcname, _ := cmd.Flags().GetString("funcname")
input, _ := cmd.Flags().GetString("args")
var params rpctypes.Query4Jrpc
var rep interface{}
req := &jsproto.Call{
Name: name,
Funcname: funcname,
Args: input,
}
params.Execer = jsty.JsX
params.FuncName = "Query"
params.Payload = types.MustPBToJSON(req)
rep = &jsproto.QueryResult{}
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.Query", params, rep)
ctx.Run()
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package js
/*
Java Script VM contract
cli 命令行
Available Commands:
call call java script contract
create create java script contract
query query java script contract
cli jsvm create
Flags:
-c, --code string path of js file,it must always be in utf-8.
-h, --help help for create
-n, --name string contract name
cli jsvm call
Flags:
-a, --args string json str of args
-f, --funcname string java script contract funcname
-h, --help help for call
-n, --name string java script contract name
cli jsvm query
Flags:
-a, --args string json str of args
-f, --funcname string java script contract funcname
-h, --help help for query
-n, --name string java script contract name
测试步骤:
第一步:创建钱包
cli seed save -p heyubin -s "voice leisure mechanic tape cluster grunt receive joke nurse between monkey lunch save useful cruise"
cli wallet unlock -p heyubin
cli account import_key -l miner -k CC38546E9E659D15E6B4893F0AB32A06D103931A8230B0BDE71459D2B27D6944
第二步:创建名为test的js合约,合约代码使用test.js代码文件(必须是utf-8格式)
cli send jsvm create -c "../plugin/dapp/js/executor/test.js" -n test -k 14KEKbYtKKQm4wMthSK9J4La4nAiidGozt
第三步:调用test合约的hello函数
cli send jsvm call -a "{\"hello\": \"world\"}" -f hello -n test -k 14KEKbYtKKQm4wMthSK9J4La4nAiidGozt
第四步:query test合约hello函数
cli jsvm query -a "{\"hello\": \"world\"}" -f hello -n test
*/
<script src="runtime.js"></script> <script src="runtime.js"></script>
<script src="test.js"></script> <script src="game.js"></script>
<script> <script>
//demo database function //demo database function
var statedb = {} var statedb = {}
...@@ -34,11 +34,15 @@ function getstatedb(key) { ...@@ -34,11 +34,15 @@ function getstatedb(key) {
return statedb[key] return statedb[key]
} }
function execname() {
return "user.jsvm.test"
}
function setstatedb(kvs) { function setstatedb(kvs) {
for (var i = 0; i < kvs.length; i++) { for (var i = 0; i < kvs.length; i++) {
statedb[kvs[i].key] = kvs[i].value statedb[kvs[i].key] = kvs[i].value
} }
} }
var ret = callcode("{}", "execlocal_hello", "{}", []) var ret = callcode("{}", "exec_NewGame", "{}", [])
console.log(ret) console.log(ret)
</script> </script>
\ No newline at end of file
...@@ -282,7 +282,7 @@ func (u *js) execFrozenFunc(vm *otto.Otto) { ...@@ -282,7 +282,7 @@ func (u *js) execFrozenFunc(vm *otto.Otto) {
if err != nil { if err != nil {
return errReturn(vm, err) return errReturn(vm, err)
} }
receipt, err := acc.ExecFrozen(address.ExecAddress(execer), addr, amount) receipt, err := acc.ExecFrozen(addr, address.ExecAddress(execer), amount)
if err != nil { if err != nil {
return errReturn(vm, err) return errReturn(vm, err)
} }
...@@ -311,7 +311,7 @@ func (u *js) execActiveFunc(vm *otto.Otto) { ...@@ -311,7 +311,7 @@ func (u *js) execActiveFunc(vm *otto.Otto) {
if err != nil { if err != nil {
return errReturn(vm, err) return errReturn(vm, err)
} }
receipt, err := acc.ExecActive(address.ExecAddress(execer), addr, amount) receipt, err := acc.ExecActive(addr, address.ExecAddress(execer), amount)
if err != nil { if err != nil {
return errReturn(vm, err) return errReturn(vm, err)
} }
...@@ -340,7 +340,7 @@ func (u *js) execDepositFunc(vm *otto.Otto) { ...@@ -340,7 +340,7 @@ func (u *js) execDepositFunc(vm *otto.Otto) {
if err != nil { if err != nil {
return errReturn(vm, err) return errReturn(vm, err)
} }
receipt, err := acc.ExecDeposit(address.ExecAddress(execer), addr, amount) receipt, err := acc.ExecDeposit(addr, address.ExecAddress(execer), amount)
if err != nil { if err != nil {
return errReturn(vm, err) return errReturn(vm, err)
} }
......
...@@ -3,14 +3,15 @@ package executor ...@@ -3,14 +3,15 @@ package executor
var callcode = ` var callcode = `
var tojson = JSON.stringify var tojson = JSON.stringify
//table warp //table warp
function table(kvc, config, defaultvalue) { function Table(kvc, config, defaultvalue) {
var ret = table_new(config, defaultvalue) var ret = table_new(tojson(config), tojson(defaultvalue))
if (ret.err) { if (ret.err) {
throw new Error(ret.err) throw new Error(ret.err)
} }
this.kvc = kvc this.kvc = kvc
this.id = ret.id this.id = ret.id
this.config = config this.config = config
this.name = config["#tablename"]
this.defaultvalue = defaultvalue this.defaultvalue = defaultvalue
} }
...@@ -18,7 +19,7 @@ function isstring(obj) { ...@@ -18,7 +19,7 @@ function isstring(obj) {
return typeof obj === "string" return typeof obj === "string"
} }
table.prototype.add = function(obj) { Table.prototype.add = function(obj) {
if (!isstring(obj)) { if (!isstring(obj)) {
obj = tojson(obj) obj = tojson(obj)
} }
...@@ -26,7 +27,57 @@ table.prototype.add = function(obj) { ...@@ -26,7 +27,57 @@ table.prototype.add = function(obj) {
return ret.err return ret.err
} }
table.prototype.replace = function(obj) { Table.prototype.joinkey = function(left, right) {
return table_joinkey(left, right)
}
Table.prototype.get = function(key, row) {
if (!isstring(row)) {
row = tojson(row)
}
var ret = table_get(this.id, key, row)
if (ret.err) {
throwerr(ret.err)
}
return ret.value
}
function query_list(indexName, prefix, primaryKey, count, direction) {
if (count !== 0 && !count) {
count = 20
}
if (!direction) {
direction = 0
}
if (!primaryKey) {
primaryKey = ""
}
if (!prefix) {
prefix = ""
}
if (!indexName) {
indexName = ""
}
var q = table_query(this.id, indexName, prefix, primaryKey, count, direction)
if (q.err) {
return null
}
for (var i = 0; i < q.length; i++) {
if (q[i].left) {
q[i].left = JSON.parse(q[i].left)
}
if (q[i].right) {
q[i].right = JSON.parse(q[i].right)
}
}
return q
}
Table.prototype.query = function(indexName, prefix, primaryKey, count, direction) {
return query_list.call(this, indexName, prefix, primaryKey, count, direction)
}
Table.prototype.replace = function(obj) {
if (!isstring(obj)) { if (!isstring(obj)) {
obj = tojson(obj) obj = tojson(obj)
} }
...@@ -34,7 +85,7 @@ table.prototype.replace = function(obj) { ...@@ -34,7 +85,7 @@ table.prototype.replace = function(obj) {
return ret.err return ret.err
} }
table.prototype.del = function(obj) { Table.prototype.del = function(obj) {
if (!isstring(obj)) { if (!isstring(obj)) {
obj = tojson(obj) obj = tojson(obj)
} }
...@@ -42,7 +93,7 @@ table.prototype.del = function(obj) { ...@@ -42,7 +93,7 @@ table.prototype.del = function(obj) {
return ret.err return ret.err
} }
table.prototype.save = function() { Table.prototype.save = function() {
var ret = table_save(this.id) var ret = table_save(this.id)
if (!this.kvc) { if (!this.kvc) {
this.kvc.save(ret) this.kvc.save(ret)
...@@ -50,11 +101,89 @@ table.prototype.save = function() { ...@@ -50,11 +101,89 @@ table.prototype.save = function() {
return ret return ret
} }
table.prototype.close = function() { Table.prototype.close = function() {
var ret = table_close(this.id) var ret = table_close(this.id)
return ret.err return ret.err
} }
function JoinTable(lefttable, righttable, index) {
this.lefttable = lefttable
this.righttable = righttable
if (this.lefttable.kvc != this.righttable.kvc) {
throw new Error("the kvc of left and right must same")
}
this.index = index
var ret = new_join_table(this.lefttable.id, this.righttable.id, index)
if (ret.err) {
throw new Error(ret.err)
}
this.id = ret.id
this.kvc = this.lefttable.kvc
}
function print(obj) {
if (typeof obj === "string") {
console.log(obj)
return
}
console.log(tojson(obj))
}
JoinTable.prototype.save = function() {
var ret = table_save(this.id)
if (this.kvc) {
this.kvc.save(ret)
}
return ret
}
JoinTable.prototype.get = function(key, row) {
if (!isstring(row)) {
row = tojson(row)
}
var ret = table_get(this.id, key, row)
if (ret.err) {
throwerr(ret.err)
}
return ret.value
}
JoinTable.prototype.query = function(indexName, prefix, primaryKey, count, direction) {
return query_list.call(this, indexName, prefix, primaryKey, count, direction)
}
function querytojson(data) {
if (!data) {
return "[]"
}
return tojson(data)
}
JoinTable.prototype.close = function() {
table_close(this.lefttable.id)
table_close(this.righttable.id)
var ret = table_close(this.id)
return ret.err
}
JoinTable.prototype.addlogs = function(data) {
var err
for (var i = 0; i < data.length; i++) {
if (data[i].format != "json") {
continue
}
var log = JSON.parse(data[i].log)
if (log.__type__ == this.lefttable.name) {
err = this.lefttable.replace(data[i].log)
throwerr(err)
}
if (log.__type__ == this.righttable.name) {
err = this.righttable.replace(data[i].log)
throwerr(err)
}
}
}
//account warp //account warp
function account(kvc, execer, symbol) { function account(kvc, execer, symbol) {
this.execer = execer this.execer = execer
...@@ -64,7 +193,7 @@ function account(kvc, execer, symbol) { ...@@ -64,7 +193,7 @@ function account(kvc, execer, symbol) {
account.prototype.genesisInit = function(addr, amount) { account.prototype.genesisInit = function(addr, amount) {
var ret = genesis_init(this, addr, amount) var ret = genesis_init(this, addr, amount)
if (!this.kvc) { if (this.kvc) {
this.kvc.save(ret) this.kvc.save(ret)
} }
return ret.err return ret.err
...@@ -72,7 +201,7 @@ account.prototype.genesisInit = function(addr, amount) { ...@@ -72,7 +201,7 @@ account.prototype.genesisInit = function(addr, amount) {
account.prototype.execGenesisInit = function(execer, addr, amount) { account.prototype.execGenesisInit = function(execer, addr, amount) {
var ret = genesis_init_exec(this, execer, addr, amount) var ret = genesis_init_exec(this, execer, addr, amount)
if (!this.kvc) { if (this.kvc) {
this.kvc.save(ret) this.kvc.save(ret)
} }
return ret.err return ret.err
...@@ -89,7 +218,7 @@ account.prototype.execGetBalance = function(execer, addr) { ...@@ -89,7 +218,7 @@ account.prototype.execGetBalance = function(execer, addr) {
//本合约转移资产,或者转移到其他合约,或者从其他合约取回资产 //本合约转移资产,或者转移到其他合约,或者从其他合约取回资产
account.prototype.transfer = function(from, to, amount) { account.prototype.transfer = function(from, to, amount) {
var ret = transfer(this, from, to, amount) var ret = transfer(this, from, to, amount)
if (!this.kvc) { if (this.kvc) {
this.kvc.save(ret) this.kvc.save(ret)
} }
return ret.err return ret.err
...@@ -97,7 +226,7 @@ account.prototype.transfer = function(from, to, amount) { ...@@ -97,7 +226,7 @@ account.prototype.transfer = function(from, to, amount) {
account.prototype.transferToExec = function(execer, from, amount) { account.prototype.transferToExec = function(execer, from, amount) {
var ret = transfer_to_exec(this, execer, from, amount) var ret = transfer_to_exec(this, execer, from, amount)
if (!this.kvc) { if (this.kvc) {
this.kvc.save(ret) this.kvc.save(ret)
} }
return ret.err return ret.err
...@@ -105,7 +234,7 @@ account.prototype.transferToExec = function(execer, from, amount) { ...@@ -105,7 +234,7 @@ account.prototype.transferToExec = function(execer, from, amount) {
account.prototype.withdrawFromExec = function(execer, to, amount) { account.prototype.withdrawFromExec = function(execer, to, amount) {
var ret = withdraw(this, execer, to, amount) var ret = withdraw(this, execer, to, amount)
if (!this.kvc) { if (this.kvc) {
this.kvc.save(ret) this.kvc.save(ret)
} }
return ret.err return ret.err
...@@ -114,7 +243,7 @@ account.prototype.withdrawFromExec = function(execer, to, amount) { ...@@ -114,7 +243,7 @@ account.prototype.withdrawFromExec = function(execer, to, amount) {
//管理其他合约的资产转移到这个合约中 //管理其他合约的资产转移到这个合约中
account.prototype.execActive = function(execer, addr, amount) { account.prototype.execActive = function(execer, addr, amount) {
var ret = exec_active(this, execer, addr, amount) var ret = exec_active(this, execer, addr, amount)
if (!this.kvc) { if (this.kvc) {
this.kvc.save(ret) this.kvc.save(ret)
} }
return ret.err return ret.err
...@@ -122,7 +251,7 @@ account.prototype.execActive = function(execer, addr, amount) { ...@@ -122,7 +251,7 @@ account.prototype.execActive = function(execer, addr, amount) {
account.prototype.execFrozen = function(execer, addr, amount) { account.prototype.execFrozen = function(execer, addr, amount) {
var ret = exec_frozen(this, execer, addr, amount) var ret = exec_frozen(this, execer, addr, amount)
if (!this.kvc) { if (this.kvc) {
this.kvc.save(ret) this.kvc.save(ret)
} }
return ret.err return ret.err
...@@ -130,7 +259,7 @@ account.prototype.execFrozen = function(execer, addr, amount) { ...@@ -130,7 +259,7 @@ account.prototype.execFrozen = function(execer, addr, amount) {
account.prototype.execDeposit = function(execer, addr, amount) { account.prototype.execDeposit = function(execer, addr, amount) {
var ret = exec_deposit(this, execer, addr, amount) var ret = exec_deposit(this, execer, addr, amount)
if (!this.kvc) { if (this.kvc) {
this.kvc.save(ret) this.kvc.save(ret)
} }
return ret.err return ret.err
...@@ -138,7 +267,7 @@ account.prototype.execDeposit = function(execer, addr, amount) { ...@@ -138,7 +267,7 @@ account.prototype.execDeposit = function(execer, addr, amount) {
account.prototype.execWithdraw = function(execer, addr, amount) { account.prototype.execWithdraw = function(execer, addr, amount) {
var ret = exec_withdraw(this, execer, addr, amount) var ret = exec_withdraw(this, execer, addr, amount)
if (!this.kvc) { if (this.kvc) {
this.kvc.save(ret) this.kvc.save(ret)
} }
return ret.err return ret.err
...@@ -146,12 +275,47 @@ account.prototype.execWithdraw = function(execer, addr, amount) { ...@@ -146,12 +275,47 @@ account.prototype.execWithdraw = function(execer, addr, amount) {
account.prototype.execTransfer = function(execer, from, to, amount) { account.prototype.execTransfer = function(execer, from, to, amount) {
var ret = exec_transfer(this, execer, from, to, amount) var ret = exec_transfer(this, execer, from, to, amount)
if (!this.kvc) { if (this.kvc) {
this.kvc.save(ret) this.kvc.save(ret)
} }
return ret.err return ret.err
} }
//from frozen -> to active
account.prototype.execTransFrozenToActive = function(execer, from, to, amount) {
var err
err = this.execActive(execer, from, amount)
if (err) {
return err
}
err = this.execTransfer(execer, from, to, amount)
}
//from frozen -> to frozen
account.prototype.execTransFrozenToFrozen = function(execer, from, to, amount) {
var err
err = this.execActive(execer, from, amount)
if (err) {
return err
}
err = this.execTransfer(execer, from, to, amount)
if (err) {
return err
}
return this.execFrozen(execer, to, amount)
}
account.prototype.execTransActiveToFrozen = function(execer, from, to, amount) {
var err
err = this.execTransfer(execer, from, to, amount)
if (err) {
return err
}
return this.execFrozen(execer, to, amount)
}
COINS = 100000000
function kvcreator(dbtype) { function kvcreator(dbtype) {
this.data = {} this.data = {}
this.kvs = [] this.kvs = []
...@@ -161,11 +325,11 @@ function kvcreator(dbtype) { ...@@ -161,11 +325,11 @@ function kvcreator(dbtype) {
this.getloal = getlocaldb this.getloal = getlocaldb
this.list = listdb this.list = listdb
if (dbtype == "exec" || dbtype == "init") { if (dbtype == "exec" || dbtype == "init") {
this.get = this.getstatedb this.getdb = this.getstate
} else if (dbtype == "local") { } else if (dbtype == "local") {
this.get = this.getlocaldb this.getdb = this.getlocal
} else if (dbtype == "query") { } else if (dbtype == "query") {
this.get = this.getlocaldb this.getdb = this.getlocal
} else { } else {
throw new Error("chain33.js: dbtype error") throw new Error("chain33.js: dbtype error")
} }
...@@ -184,8 +348,8 @@ kvcreator.prototype.get = function(k, prefix) { ...@@ -184,8 +348,8 @@ kvcreator.prototype.get = function(k, prefix) {
if (this.data[k]) { if (this.data[k]) {
v = this.data[k] v = this.data[k]
} else { } else {
var dbvalue = this.get(k, !!prefix) var dbvalue = this.getdb(k, !!prefix)
if (dbvalue.err != "") { if (dbvalue.err) {
return null return null
} }
v = dbvalue.value v = dbvalue.value
...@@ -213,7 +377,7 @@ kvcreator.prototype.save = function(receipt) { ...@@ -213,7 +377,7 @@ kvcreator.prototype.save = function(receipt) {
kvcreator.prototype.listvalue = function(prefix, key, count, direction) { kvcreator.prototype.listvalue = function(prefix, key, count, direction) {
var dbvalues = this.list(prefix, key, count, direction) var dbvalues = this.list(prefix, key, count, direction)
if (dbvalues.err != "") { if (dbvalues.err) {
return [] return []
} }
var values = dbvalues.value var values = dbvalues.value
...@@ -247,6 +411,73 @@ kvcreator.prototype.receipt = function() { ...@@ -247,6 +411,73 @@ kvcreator.prototype.receipt = function() {
return {kvs: this.kvs, logs: this.logs} return {kvs: this.kvs, logs: this.logs}
} }
function GetExecName() {
var exec = execname()
if (exec.err) {
return ""
}
return exec.value
}
function ExecAddress(name) {
var addr = execaddress(name)
if (addr.err) {
return ""
}
console.log(addr.value)
return addr.value
}
function Sha256(data) {
var hash = sha256(data)
if (hash.err) {
return ""
}
return hash.value
}
function Exec(context) {
this.kvc = new kvcreator("exec")
this.context = context
this.name = GetExecName()
if (typeof ExecInit === "function") {
ExecInit.call(this)
}
}
Exec.prototype.txID = function() {
return this.context.height * 100000 + this.context.index
}
function ExecLocal(context, logs) {
this.kvc = new kvcreator("local")
this.context = context
this.logs = logs
this.name = GetExecName()
if (typeof ExecLocalInit === "function") {
ExecLocalInit.call(this)
}
}
function Query(context) {
this.kvc = new kvcreator("query")
this.context = context
this.name = GetExecName()
if (typeof QueryInit === "function") {
QueryInit.call(this)
}
}
Query.prototype.JoinKey = function(args) {
return table_joinkey(args.left, args.right).value
}
function throwerr(err, msg) {
if (err) {
throw new Error(err + ":" + msg)
}
}
function callcode(context, f, args, loglist) { function callcode(context, f, args, loglist) {
if (f == "init") { if (f == "init") {
return Init(JSON.parse(context)) return Init(JSON.parse(context))
...@@ -276,7 +507,7 @@ function callcode(context, f, args, loglist) { ...@@ -276,7 +507,7 @@ function callcode(context, f, args, loglist) {
} }
var arg = JSON.parse(args) var arg = JSON.parse(args)
if (typeof runobj[funcname] != "function") { if (typeof runobj[funcname] != "function") {
throw new Error("chain33.js: invalid function name not found") throw new Error("chain33.js: invalid function name not found->" + funcname)
} }
return runobj[funcname](arg) return runobj[funcname](arg)
} }
...@@ -285,8 +516,6 @@ function callcode(context, f, args, loglist) { ...@@ -285,8 +516,6 @@ function callcode(context, f, args, loglist) {
` `
var jscode = ` var jscode = `
//数据结构设计 //数据结构设计
//kvlist [{key:"key1", value:"value1"},{key:"key2", value:"value2"}]
//log 设计 {json data}
function Init(context) { function Init(context) {
this.kvc = new kvcreator("init") this.kvc = new kvcreator("init")
this.context = context this.context = context
...@@ -295,22 +524,6 @@ function Init(context) { ...@@ -295,22 +524,6 @@ function Init(context) {
return this.kvc.receipt() return this.kvc.receipt()
} }
function Exec(context) {
this.kvc = new kvcreator("exec")
this.context = context
}
function ExecLocal(context, logs) {
this.kvc = new kvcreator("local")
this.context = context
this.logs = logs
}
function Query(context) {
this.kvc = new kvcreator("query")
this.context = context
}
Exec.prototype.hello = function(args) { Exec.prototype.hello = function(args) {
this.kvc.add("args", args) this.kvc.add("args", args)
this.kvc.add("action", "exec") this.kvc.add("action", "exec")
...@@ -335,3 +548,246 @@ Query.prototype.hello = function(args) { ...@@ -335,3 +548,246 @@ Query.prototype.hello = function(args) {
} }
` `
var _ = jscode var _ = jscode
var gamecode = `
//简单的猜数字游戏
//游戏规则: 庄家出一个 0 - 10 的数字 hash(随机数 + 9) (一共的赔偿金额) NewGame()
//用户可以猜这个数字,多个用户都可以猜测。 Guess()
//开奖 CloseGame()
function Init(context) {
this.kvc = new kvcreator("init")
this.context = context
return this.kvc.receipt()
}
var MIN_WAIT_BLOCK = 2
var RAND_MAX = 10
function ExecInit() {
this.acc = new account(this.kvc, "coins", "bty")
}
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)
this.kvc.add(game.randhash, "ok")
this.kvc.addlog(game)
return this.kvc.receipt()
}
Exec.prototype.Guess = function(args) {
var match = {__type__ : "match"}
match.gameid = args.gameid
match.bet = args.bet
match.id = this.txID()
match.addr = this.context.from
match.hash = this.context.txhash
match.num = args.num
var game = this.kvc.get(match.gameid)
if (!game) {
throwerr("guess: game id not found")
}
if (game.status != 1) {
throwerr("guess: game status not open")
}
if (this.context.from == game.addr) {
throwerr("guess: game addr and match addr is same")
}
if (match.bet < 1 * COINS || match.bet > game.bet / RAND_MAX) {
throwerr("match bet litte than 1 or big than game.bet/10")
}
var err = this.acc.execFrozen(this.name, this.context.from, match.bet)
console.log(this.name, this.context.from, err)
throwerr(err)
this.kvc.add(match.id, match)
this.kvc.addlog(match)
return this.kvc.receipt()
}
Exec.prototype.CloseGame = function(args) {
var local = MatchLocalTable(this.kvc)
var game = this.kvc.get(args.gameid)
if (!game) {
throwerr("game id not found")
}
var querykey = local.get("gameid", args)
var matches = local.query("gameid", querykey, "", 0, 1)
if (!matches) {
matches = []
}
var n = -1
for (var i = 0; i < RAND_MAX; i++) {
if (Sha256(args.randstr + i) == game.randhash) {
n = i
}
}
if (n == -1) {
throwerr("err rand str")
}
//必须可以让用户可以有一个区块的竞猜时间
if (this.context.height - game.height < MIN_WAIT_BLOCK) {
throwerr("close game must wait "+MIN_WAIT_BLOCK+" block")
}
for (var i = 0; i < matches.length; i++) {
var match = matches[i].left
if (match.num == n) {
//不能随便添加辅助函数,因为可以被外界调用到,所以辅助函数都是传递 this
win.call(this, game, match)
} else {
fail.call(this, game, match)
}
}
if (game.bet > 0) {
var err = this.acc.execActive(this.name, game.addr, game.bet)
throwerr(err)
game.bet = 0
}
game.status = 2
this.kvc.add(game.gameid, game)
this.kvc.addlog(game)
return this.kvc.receipt()
}
function win(game, match) {
var amount = (RAND_MAX - 1) * match.bet
if (game.bet - amount < 0) {
amount = game.bet
}
var err
if (amount > 0) {
err = this.acc.execTransFrozenToActive(this.name, game.addr, match.addr, amount)
throwerr(err, "execTransFrozenToActive")
game.bet -= amount
}
err = this.acc.execActive(this.name, match.addr, match.bet)
throwerr(err, "execActive")
}
function fail(game, match) {
var amount = match.bet
err = this.acc.execTransFrozenToFrozen(this.name, match.addr, game.addr, amount)
throwerr(err)
game.bet += amount
}
Exec.prototype.ForceCloseGame = function(args) {
var local = new MatchLocalTable(this.kvc)
var game = this.kvc.get(args.id)
if (!game) {
throwerr("game id not found")
}
var matches = local.getmath(args.id)
if (!matches) {
matches = []
}
if (this.context.height - game.height < 100) {
throwerr("force close game must wait 100 block")
}
for (var i = 0; i < matches.length; i++) {
var match = matches[i]
win.call(this.kvc, game, match)
}
if (game.bet > 0) {
var err = this.acc.execActive(this.name, game.addr, game.bet)
throwerr(err)
game.bet = 0
}
game.status = 2
this.kvc.add(game.gameid, game)
this.kvc.addlog(game)
return this.kvc.receipt()
}
ExecLocal.prototype.NewGame = function(args) {
return localprocess.call(this, args)
}
ExecLocal.prototype.Guess = function(args) {
return localprocess.call(this, args)
}
ExecLocal.prototype.CloseGame = function(args) {
return localprocess.call(this, args)
}
ExecLocal.prototype.ForceCloseGame = function(args) {
return localprocess.call(this, args)
}
function localprocess(args) {
var local = MatchGameTable(this.kvc)
local.addlogs(this.logs)
local.save()
return this.kvc.receipt()
}
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)
}
Query.prototype.ListMatchByAddr = function(args) {
var local = MatchGameTable(this.kvc)
var q= local.query("addr#status", args["addr#status"], args.primaryKey, args.count, args.direction)
return querytojson(q)
}
function GameLocalTable(kvc) {
this.config = {
"#tablename" : "game",
"#primary" : "gameid",
"#db" : "localdb",
"gameid" : "%018d",
"status" : "%d",
"hash" : "%s",
"addr" : "%s",
}
this.defaultvalue = {
"gameid" : 0,
"status" : 0,
"hash" : "",
"addr" : "",
}
return new Table(kvc, this.config, this.defaultvalue)
}
function MatchLocalTable(kvc) {
this.config = {
"#tablename" : "match",
"#primary" : "id",
"#db" : "localdb",
"id" : "%018d",
"gameid" : "%018d",
"hash" : "%s",
"addr" : "%s",
}
this.defaultvalue = {
"id" : 0,
"gameid" : 0,
"hash" : "",
"addr" : "",
}
return new Table(kvc, this.config, this.defaultvalue)
}
function MatchGameTable(kvc) {
return new JoinTable(MatchLocalTable(kvc), GameLocalTable(kvc), "addr#status")
}`
var _ = gamecode
package executor_test
var jscode = `
//数据结构设计
function Init(context) {
this.kvc = new kvcreator("init")
this.context = context
this.kvc.add("action", "init")
this.kvc.add("context", this.context)
return this.kvc.receipt()
}
Exec.prototype.hello = function(args) {
this.kvc.add("args", args)
this.kvc.add("action", "exec")
this.kvc.add("context", this.context)
this.kvc.addlog({"key1": "value1"})
this.kvc.addlog({"key2": "value2"})
return this.kvc.receipt()
}
ExecLocal.prototype.hello = function(args) {
this.kvc.add("args", args)
this.kvc.add("action", "execlocal")
this.kvc.add("log", this.logs)
this.kvc.add("context", this.context)
return this.kvc.receipt()
}
//return a json string
Query.prototype.hello = function(args) {
var obj = getlocaldb("context")
return tojson(obj)
}
`
var _ = jscode
var gamecode = `
//简单的猜数字游戏
//游戏规则: 庄家出一个 0 - 10 的数字 hash(随机数 + 9) (一共的赔偿金额) NewGame()
//用户可以猜这个数字,多个用户都可以猜测。 Guess()
//开奖 CloseGame()
function Init(context) {
this.kvc = new kvcreator("init")
this.context = context
return this.kvc.receipt()
}
var MIN_WAIT_BLOCK = 2
var RAND_MAX = 10
function ExecInit() {
this.acc = new account(this.kvc, "coins", "bty")
}
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)
this.kvc.add(game.randhash, "ok")
this.kvc.addlog(game)
return this.kvc.receipt()
}
Exec.prototype.Guess = function(args) {
var match = {__type__ : "match"}
match.gameid = args.gameid
match.bet = args.bet
match.id = this.txID()
match.addr = this.context.from
match.hash = this.context.txhash
match.num = args.num
var game = this.kvc.get(match.gameid)
if (!game) {
throwerr("guess: game id not found")
}
if (game.status != 1) {
throwerr("guess: game status not open")
}
if (this.context.from == game.addr) {
throwerr("guess: game addr and match addr is same")
}
if (match.bet < 1 * COINS || match.bet > game.bet / RAND_MAX) {
throwerr("match bet litte than 1 or big than game.bet/10")
}
var err = this.acc.execFrozen(this.name, this.context.from, match.bet)
console.log(this.name, this.context.from, err)
throwerr(err)
this.kvc.add(match.id, match)
this.kvc.addlog(match)
return this.kvc.receipt()
}
Exec.prototype.CloseGame = function(args) {
var local = MatchLocalTable(this.kvc)
var game = this.kvc.get(args.gameid)
if (!game) {
throwerr("game id not found")
}
var querykey = local.get("gameid", args)
var matches = local.query("gameid", querykey, "", 0, 1)
if (!matches) {
matches = []
}
var n = -1
for (var i = 0; i < RAND_MAX; i++) {
if (Sha256(args.randstr + i) == game.randhash) {
n = i
}
}
if (n == -1) {
throwerr("err rand str")
}
//必须可以让用户可以有一个区块的竞猜时间
if (this.context.height - game.height < MIN_WAIT_BLOCK) {
throwerr("close game must wait "+MIN_WAIT_BLOCK+" block")
}
for (var i = 0; i < matches.length; i++) {
var match = matches[i].left
if (match.num == n) {
//不能随便添加辅助函数,因为可以被外界调用到,所以辅助函数都是传递 this
win.call(this, game, match)
} else {
fail.call(this, game, match)
}
}
if (game.bet > 0) {
var err = this.acc.execActive(this.name, game.addr, game.bet)
throwerr(err)
game.bet = 0
}
game.status = 2
this.kvc.add(game.gameid, game)
this.kvc.addlog(game)
return this.kvc.receipt()
}
function win(game, match) {
var amount = (RAND_MAX - 1) * match.bet
if (game.bet - amount < 0) {
amount = game.bet
}
var err
if (amount > 0) {
err = this.acc.execTransFrozenToActive(this.name, game.addr, match.addr, amount)
throwerr(err, "execTransFrozenToActive")
game.bet -= amount
}
err = this.acc.execActive(this.name, match.addr, match.bet)
throwerr(err, "execActive")
}
function fail(game, match) {
var amount = match.bet
err = this.acc.execTransFrozenToFrozen(this.name, match.addr, game.addr, amount)
throwerr(err)
game.bet += amount
}
Exec.prototype.ForceCloseGame = function(args) {
var local = new MatchLocalTable(this.kvc)
var game = this.kvc.get(args.id)
if (!game) {
throwerr("game id not found")
}
var matches = local.getmath(args.id)
if (!matches) {
matches = []
}
if (this.context.height - game.height < 100) {
throwerr("force close game must wait 100 block")
}
for (var i = 0; i < matches.length; i++) {
var match = matches[i]
win.call(this.kvc, game, match)
}
if (game.bet > 0) {
var err = this.acc.execActive(this.name, game.addr, game.bet)
throwerr(err)
game.bet = 0
}
game.status = 2
this.kvc.add(game.gameid, game)
this.kvc.addlog(game)
return this.kvc.receipt()
}
ExecLocal.prototype.NewGame = function(args) {
return localprocess.call(this, args)
}
ExecLocal.prototype.Guess = function(args) {
return localprocess.call(this, args)
}
ExecLocal.prototype.CloseGame = function(args) {
return localprocess.call(this, args)
}
ExecLocal.prototype.ForceCloseGame = function(args) {
return localprocess.call(this, args)
}
function localprocess(args) {
var local = MatchGameTable(this.kvc)
local.addlogs(this.logs)
local.save()
return this.kvc.receipt()
}
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)
}
Query.prototype.ListMatchByAddr = function(args) {
var local = MatchGameTable(this.kvc)
var q= local.query("addr#status", args["addr#status"], args.primaryKey, args.count, args.direction)
return querytojson(q)
}
function GameLocalTable(kvc) {
this.config = {
"#tablename" : "game",
"#primary" : "gameid",
"#db" : "localdb",
"gameid" : "%018d",
"status" : "%d",
"hash" : "%s",
"addr" : "%s",
}
this.defaultvalue = {
"gameid" : 0,
"status" : 0,
"hash" : "",
"addr" : "",
}
return new Table(kvc, this.config, this.defaultvalue)
}
function MatchLocalTable(kvc) {
this.config = {
"#tablename" : "match",
"#primary" : "id",
"#db" : "localdb",
"id" : "%018d",
"gameid" : "%018d",
"hash" : "%s",
"addr" : "%s",
}
this.defaultvalue = {
"id" : 0,
"gameid" : 0,
"hash" : "",
"addr" : "",
}
return new Table(kvc, this.config, this.defaultvalue)
}
function MatchGameTable(kvc) {
return new JoinTable(MatchLocalTable(kvc), GameLocalTable(kvc), "addr#status")
}`
var _ = gamecode
//简单的猜数字游戏
//游戏规则: 庄家出一个 0 - 10 的数字 hash(随机数 + 9) (一共的赔偿金额) NewGame()
//用户可以猜这个数字,多个用户都可以猜测。 Guess()
//开奖 CloseGame()
function Init(context) {
this.kvc = new kvcreator("init")
this.context = context
return this.kvc.receipt()
}
var MIN_WAIT_BLOCK = 2
var RAND_MAX = 10
function ExecInit() {
this.acc = new account(this.kvc, "coins", "bty")
}
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)
this.kvc.add(game.randhash, "ok")
this.kvc.addlog(game)
return this.kvc.receipt()
}
Exec.prototype.Guess = function(args) {
var match = {__type__ : "match"}
match.gameid = args.gameid
match.bet = args.bet
match.id = this.txID()
match.addr = this.context.from
match.hash = this.context.txhash
match.num = args.num
var game = this.kvc.get(match.gameid)
if (!game) {
throwerr("guess: game id not found")
}
if (game.status != 1) {
throwerr("guess: game status not open")
}
if (this.context.from == game.addr) {
throwerr("guess: game addr and match addr is same")
}
if (match.bet < 1 * COINS || match.bet > game.bet / RAND_MAX) {
throwerr("match bet litte than 1 or big than game.bet/10")
}
var err = this.acc.execFrozen(this.name, this.context.from, match.bet)
console.log(this.name, this.context.from, err)
throwerr(err)
this.kvc.add(match.id, match)
this.kvc.addlog(match)
return this.kvc.receipt()
}
Exec.prototype.CloseGame = function(args) {
var local = MatchLocalTable(this.kvc)
var game = this.kvc.get(args.gameid)
if (!game) {
throwerr("game id not found")
}
var querykey = local.get("gameid", args)
var matches = local.query("gameid", querykey, "", 0, 1)
if (!matches) {
matches = []
}
var n = -1
for (var i = 0; i < RAND_MAX; i++) {
if (Sha256(args.randstr + i) == game.randhash) {
n = i
}
}
if (n == -1) {
throwerr("err rand str")
}
//必须可以让用户可以有一个区块的竞猜时间
if (this.context.height - game.height < MIN_WAIT_BLOCK) {
throwerr("close game must wait "+MIN_WAIT_BLOCK+" block")
}
for (var i = 0; i < matches.length; i++) {
var match = matches[i].left
if (match.num == n) {
//不能随便添加辅助函数,因为可以被外界调用到,所以辅助函数都是传递 this
win.call(this, game, match)
} else {
fail.call(this, game, match)
}
}
if (game.bet > 0) {
var err = this.acc.execActive(this.name, game.addr, game.bet)
throwerr(err)
game.bet = 0
}
game.status = 2
this.kvc.add(game.gameid, game)
this.kvc.addlog(game)
return this.kvc.receipt()
}
function win(game, match) {
var amount = (RAND_MAX - 1) * match.bet
if (game.bet - amount < 0) {
amount = game.bet
}
var err
if (amount > 0) {
err = this.acc.execTransFrozenToActive(this.name, game.addr, match.addr, amount)
throwerr(err, "execTransFrozenToActive")
game.bet -= amount
}
err = this.acc.execActive(this.name, match.addr, match.bet)
throwerr(err, "execActive")
}
function fail(game, match) {
var amount = match.bet
err = this.acc.execTransFrozenToFrozen(this.name, match.addr, game.addr, amount)
throwerr(err)
game.bet += amount
}
Exec.prototype.ForceCloseGame = function(args) {
var local = new MatchLocalTable(this.kvc)
var game = this.kvc.get(args.id)
if (!game) {
throwerr("game id not found")
}
var matches = local.getmath(args.id)
if (!matches) {
matches = []
}
if (this.context.height - game.height < 100) {
throwerr("force close game must wait 100 block")
}
for (var i = 0; i < matches.length; i++) {
var match = matches[i]
win.call(this.kvc, game, match)
}
if (game.bet > 0) {
var err = this.acc.execActive(this.name, game.addr, game.bet)
throwerr(err)
game.bet = 0
}
game.status = 2
this.kvc.add(game.gameid, game)
this.kvc.addlog(game)
return this.kvc.receipt()
}
ExecLocal.prototype.NewGame = function(args) {
return localprocess.call(this, args)
}
ExecLocal.prototype.Guess = function(args) {
return localprocess.call(this, args)
}
ExecLocal.prototype.CloseGame = function(args) {
return localprocess.call(this, args)
}
ExecLocal.prototype.ForceCloseGame = function(args) {
return localprocess.call(this, args)
}
function localprocess(args) {
var local = MatchGameTable(this.kvc)
local.addlogs(this.logs)
local.save()
return this.kvc.receipt()
}
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)
}
Query.prototype.ListMatchByAddr = function(args) {
var local = MatchGameTable(this.kvc)
var q= local.query("addr#status", args["addr#status"], args.primaryKey, args.count, args.direction)
return querytojson(q)
}
function GameLocalTable(kvc) {
this.config = {
"#tablename" : "game",
"#primary" : "gameid",
"#db" : "localdb",
"gameid" : "%018d",
"status" : "%d",
"hash" : "%s",
"addr" : "%s",
}
this.defaultvalue = {
"gameid" : 0,
"status" : 0,
"hash" : "",
"addr" : "",
}
return new Table(kvc, this.config, this.defaultvalue)
}
function MatchLocalTable(kvc) {
this.config = {
"#tablename" : "match",
"#primary" : "id",
"#db" : "localdb",
"id" : "%018d",
"gameid" : "%018d",
"hash" : "%s",
"addr" : "%s",
}
this.defaultvalue = {
"id" : 0,
"gameid" : 0,
"hash" : "",
"addr" : "",
}
return new Table(kvc, this.config, this.defaultvalue)
}
function MatchGameTable(kvc) {
return new JoinTable(MatchLocalTable(kvc), GameLocalTable(kvc), "addr#status")
}
\ No newline at end of file
...@@ -8,4 +8,19 @@ ...@@ -8,4 +8,19 @@
cat "test.js" cat "test.js"
printf '`\n' printf '`\n'
printf 'var _ = jscode\n' printf 'var _ = jscode\n'
printf 'var gamecode = `\n'
cat "game.js"
printf '`\n'
printf 'var _ = gamecode\n'
} >const.go } >const.go
{
printf 'package executor_test\n\nvar jscode = `\n'
cat "test.js"
printf '`\n'
printf 'var _ = jscode\n'
printf 'var gamecode = `\n'
cat "game.js"
printf '`\n'
printf 'var _ = gamecode\n'
} >const_test.go
...@@ -3,7 +3,7 @@ package executor ...@@ -3,7 +3,7 @@ package executor
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "sync"
"sync/atomic" "sync/atomic"
"github.com/33cn/chain33/common" "github.com/33cn/chain33/common"
...@@ -28,7 +28,7 @@ func init() { ...@@ -28,7 +28,7 @@ func init() {
panic(err) panic(err)
} }
execaddressFunc(basevm) execaddressFunc(basevm)
registerTableFunc(basevm) sha256Func(basevm)
} }
var isinit int64 var isinit int64
...@@ -48,7 +48,9 @@ func Init(name string, sub []byte) { ...@@ -48,7 +48,9 @@ func Init(name string, sub []byte) {
type js struct { type js struct {
drivers.DriverBase drivers.DriverBase
prefix []byte prefix []byte
globalTableHandle sync.Map
globalHanldeID int64
} }
func newjs() drivers.Driver { func newjs() drivers.Driver {
...@@ -173,6 +175,7 @@ func (u *js) getContext(tx *types.Transaction, index int64) *blockContext { ...@@ -173,6 +175,7 @@ func (u *js) getContext(tx *types.Transaction, index int64) *blockContext {
Difficulty: u.GetDifficulty(), Difficulty: u.GetDifficulty(),
TxHash: common.ToHex(hash[:]), TxHash: common.ToHex(hash[:]),
Index: index, Index: index,
From: tx.From(),
} }
} }
...@@ -222,7 +225,7 @@ func (u *js) localdbFunc(vm *otto.Otto, name string) { ...@@ -222,7 +225,7 @@ func (u *js) localdbFunc(vm *otto.Otto, name string) {
func (u *js) execnameFunc(vm *otto.Otto, name string) { func (u *js) execnameFunc(vm *otto.Otto, name string) {
vm.Set("execname", func(call otto.FunctionCall) otto.Value { vm.Set("execname", func(call otto.FunctionCall) otto.Value {
return okReturn(vm, types.ExecName("user.js."+name)) return okReturn(vm, types.ExecName("user."+ptypes.JsX+"."+name))
}) })
} }
...@@ -279,7 +282,7 @@ func (u *js) createVM(name string, tx *types.Transaction, index int) (*otto.Otto ...@@ -279,7 +282,7 @@ func (u *js) createVM(name string, tx *types.Transaction, index int) (*otto.Otto
u.listdbFunc(vm, name) u.listdbFunc(vm, name)
u.execnameFunc(vm, name) u.execnameFunc(vm, name)
u.registerAccountFunc(vm) u.registerAccountFunc(vm)
u.newTableFunc(vm, name) u.registerTableFunc(vm, name)
return vm, nil return vm, nil
} }
...@@ -358,26 +361,28 @@ func (u *js) Allow(tx *types.Transaction, index int) error { ...@@ -358,26 +361,28 @@ func (u *js) Allow(tx *types.Transaction, index int) error {
return types.ErrNotAllow return types.ErrNotAllow
} }
func createKVObject(vm *otto.Otto, kvs []*types.KeyValue) otto.Value { func createKVObject(vm *otto.Otto, kvs []*types.KeyValue) []interface{} {
obj := newObjectString(vm, "([])") data := make([]interface{}, len(kvs))
for i := 0; i < len(kvs); i++ { for i := 0; i < len(kvs); i++ {
item := newObject(vm).setValue("key", string(kvs[i].Key)) item := make(map[string]interface{})
item.setValue("value", string(kvs[i].Value)) item["key"] = string(kvs[i].Key)
item.setValue("prefix", true) item["value"] = string(kvs[i].Value)
obj.setValue(fmt.Sprint(i), item) item["prefix"] = true
data[i] = item
} }
return obj.value() return data
} }
func createLogsObject(vm *otto.Otto, logs []*types.ReceiptLog) otto.Value { func createLogsObject(vm *otto.Otto, logs []*types.ReceiptLog) []interface{} {
obj := newObjectString(vm, "([])") data := make([]interface{}, len(logs))
for i := 0; i < len(logs); i++ { for i := 0; i < len(logs); i++ {
item := newObject(vm).setValue("ty", logs[i].Ty) item := make(map[string]interface{})
item.setValue("log", string(logs[i].Log)) item["ty"] = logs[i].Ty
item.setValue("format", "proto") item["log"] = string(logs[i].Log)
obj.setValue(fmt.Sprint(i), item) item["format"] = "proto"
data[i] = item
} }
return obj.value() return data
} }
func accountReturn(vm *otto.Otto, acc *types.Account) otto.Value { func accountReturn(vm *otto.Otto, acc *types.Account) otto.Value {
......
...@@ -22,6 +22,7 @@ type blockContext struct { ...@@ -22,6 +22,7 @@ type blockContext struct {
Difficulty uint64 `json:"difficulty"` Difficulty uint64 `json:"difficulty"`
TxHash string `json:"txhash"` TxHash string `json:"txhash"`
Index int64 `json:"index"` Index int64 `json:"index"`
From string `json:"from"`
} }
func parseJsReturn(prefix []byte, jsvalue *otto.Object) (kvlist []*types.KeyValue, logs []*types.ReceiptLog, err error) { func parseJsReturn(prefix []byte, jsvalue *otto.Object) (kvlist []*types.KeyValue, logs []*types.ReceiptLog, err error) {
......
...@@ -22,12 +22,12 @@ func init() { ...@@ -22,12 +22,12 @@ func init() {
Init(ptypes.JsX, nil) Init(ptypes.JsX, nil)
} }
func initExec(ldb db.DB, kvdb db.KVDB, t assert.TestingT) *js { func initExec(ldb db.DB, kvdb db.KVDB, code string, t assert.TestingT) *js {
e := newjs().(*js) e := newjs().(*js)
e.SetEnv(1, time.Now().Unix(), 1) e.SetEnv(1, time.Now().Unix(), 1)
e.SetLocalDB(kvdb) e.SetLocalDB(kvdb)
e.SetStateDB(kvdb) e.SetStateDB(kvdb)
c, tx := createCodeTx("test", jscode) c, tx := createCodeTx("test", code)
receipt, err := e.Exec_Create(c, tx, 0) receipt, err := e.Exec_Create(c, tx, 0)
assert.Nil(t, err) assert.Nil(t, err)
util.SaveKVList(ldb, receipt.KV) util.SaveKVList(ldb, receipt.KV)
...@@ -54,7 +54,7 @@ func callCodeTx(name, f, args string) (*jsproto.Call, *types.Transaction) { ...@@ -54,7 +54,7 @@ func callCodeTx(name, f, args string) (*jsproto.Call, *types.Transaction) {
func TestCallcode(t *testing.T) { func TestCallcode(t *testing.T) {
dir, ldb, kvdb := util.CreateTestDB() dir, ldb, kvdb := util.CreateTestDB()
defer util.CloseTestDB(dir, ldb) defer util.CloseTestDB(dir, ldb)
e := initExec(ldb, kvdb, t) e := initExec(ldb, kvdb, jscode, t)
call, tx := callCodeTx("test", "hello", `{"hello":"world"}`) call, tx := callCodeTx("test", "hello", `{"hello":"world"}`)
receipt, err := e.Exec_Call(call, tx, 0) receipt, err := e.Exec_Call(call, tx, 0)
...@@ -107,7 +107,7 @@ func TestCallcode(t *testing.T) { ...@@ -107,7 +107,7 @@ func TestCallcode(t *testing.T) {
func TestCallError(t *testing.T) { func TestCallError(t *testing.T) {
dir, ldb, kvdb := util.CreateTestDB() dir, ldb, kvdb := util.CreateTestDB()
defer util.CloseTestDB(dir, ldb) defer util.CloseTestDB(dir, ldb)
e := initExec(ldb, kvdb, t) e := initExec(ldb, kvdb, jscode, t)
//test call error(invalid json input) //test call error(invalid json input)
call, tx := callCodeTx("test", "hello", `{hello":"world"}`) call, tx := callCodeTx("test", "hello", `{hello":"world"}`)
_, err := e.callVM("exec", call, tx, 0, nil) _, err := e.callVM("exec", call, tx, 0, nil)
...@@ -132,7 +132,7 @@ func TestCallError(t *testing.T) { ...@@ -132,7 +132,7 @@ func TestCallError(t *testing.T) {
func TestBigInt(t *testing.T) { func TestBigInt(t *testing.T) {
dir, ldb, kvdb := util.CreateTestDB() dir, ldb, kvdb := util.CreateTestDB()
defer util.CloseTestDB(dir, ldb) defer util.CloseTestDB(dir, ldb)
e := initExec(ldb, kvdb, t) e := initExec(ldb, kvdb, jscode, t)
//test call error(invalid json input) //test call error(invalid json input)
s := fmt.Sprintf(`{"balance":%d,"balance1":%d,"balance2":%d,"balance3":%d}`, math.MaxInt64, math.MinInt64, 9007199254740990, -9007199254740990) s := fmt.Sprintf(`{"balance":%d,"balance1":%d,"balance2":%d,"balance3":%d}`, math.MaxInt64, math.MinInt64, 9007199254740990, -9007199254740990)
call, tx := callCodeTx("test", "hello", s) call, tx := callCodeTx("test", "hello", s)
...@@ -146,7 +146,7 @@ func TestBigInt(t *testing.T) { ...@@ -146,7 +146,7 @@ func TestBigInt(t *testing.T) {
func BenchmarkBigInt(b *testing.B) { func BenchmarkBigInt(b *testing.B) {
dir, ldb, kvdb := util.CreateTestDB() dir, ldb, kvdb := util.CreateTestDB()
defer util.CloseTestDB(dir, ldb) defer util.CloseTestDB(dir, ldb)
e := initExec(ldb, kvdb, b) e := initExec(ldb, kvdb, jscode, b)
//test call error(invalid json input) //test call error(invalid json input)
s := fmt.Sprintf(`{"balance":%d,"balance1":%d,"balance2":%d,"balance3":%d}`, math.MaxInt64, math.MinInt64, 9007199254740990, -9007199254740990) s := fmt.Sprintf(`{"balance":%d,"balance1":%d,"balance2":%d,"balance3":%d}`, math.MaxInt64, math.MinInt64, 9007199254740990, -9007199254740990)
call, tx := callCodeTx("test", "hello", s) call, tx := callCodeTx("test", "hello", s)
...@@ -184,7 +184,7 @@ func TestCalcLocalPrefix(t *testing.T) { ...@@ -184,7 +184,7 @@ func TestCalcLocalPrefix(t *testing.T) {
func TestCacheMemUsage(t *testing.T) { func TestCacheMemUsage(t *testing.T) {
dir, ldb, kvdb := util.CreateTestDB() dir, ldb, kvdb := util.CreateTestDB()
defer util.CloseTestDB(dir, ldb) defer util.CloseTestDB(dir, ldb)
e := initExec(ldb, kvdb, t) e := initExec(ldb, kvdb, jscode, t)
vm, err := e.createVM("test", nil, 0) vm, err := e.createVM("test", nil, 0)
assert.Nil(t, err) assert.Nil(t, err)
vms := make([]*otto.Otto, 1024) vms := make([]*otto.Otto, 1024)
......
...@@ -4,10 +4,13 @@ import ( ...@@ -4,10 +4,13 @@ import (
"fmt" "fmt"
"github.com/33cn/chain33/types" "github.com/33cn/chain33/types"
ptypes "github.com/33cn/plugin/plugin/dapp/js/types"
"github.com/33cn/plugin/plugin/dapp/js/types/jsproto" "github.com/33cn/plugin/plugin/dapp/js/types/jsproto"
) )
func (c *js) Query_Query(payload *jsproto.Call) (types.Message, error) { func (c *js) Query_Query(payload *jsproto.Call) (types.Message, error) {
execer := types.ExecName("user." + ptypes.JsX + "." + payload.Name)
c.prefix = calcLocalPrefix([]byte(execer))
jsvalue, err := c.callVM("query", payload, nil, 0, nil) jsvalue, err := c.callVM("query", payload, nil, 0, nil)
if err != nil { if err != nil {
fmt.Println("query", err) fmt.Println("query", err)
......
package executor_test package executor_test
import ( import (
"encoding/json"
"fmt"
"math/rand"
"testing" "testing"
"time"
"github.com/33cn/chain33/common"
"github.com/33cn/chain33/common/address"
rpctypes "github.com/33cn/chain33/rpc/types" rpctypes "github.com/33cn/chain33/rpc/types"
"github.com/33cn/chain33/types" "github.com/33cn/chain33/types"
"github.com/33cn/chain33/util/testnode" "github.com/33cn/chain33/util/testnode"
...@@ -14,6 +20,10 @@ import ( ...@@ -14,6 +20,10 @@ import (
_ "github.com/33cn/plugin/plugin" _ "github.com/33cn/plugin/plugin"
) )
func init() {
rand.Seed(time.Now().UnixNano())
}
func TestJsVM(t *testing.T) { func TestJsVM(t *testing.T) {
mocker := testnode.New("--free--", nil) mocker := testnode.New("--free--", nil)
defer mocker.Close() defer mocker.Close()
...@@ -75,53 +85,204 @@ func TestJsVM(t *testing.T) { ...@@ -75,53 +85,204 @@ func TestJsVM(t *testing.T) {
t.Log(queryresult.Data) t.Log(queryresult.Data)
} }
var jscode = ` func TestJsGame(t *testing.T) {
//数据结构设计 contractName := "test1"
//kvlist [{key:"key1", value:"value1"},{key:"key2", value:"value2"}] mocker := testnode.New("--free--", nil)
//log 设计 {json data} defer mocker.Close()
function Init(context) { mocker.Listen()
this.kvc = new kvcreator("init") err := mocker.SendHot()
this.context = context assert.Nil(t, err)
this.kvc.add("action", "init") //开始部署合约, 测试阶段任何人都可以部署合约
this.kvc.add("context", this.context) //后期需要加上权限控制
return this.kvc.receipt() //1. 部署合约
} create := &jsproto.Create{
Code: gamecode,
function Exec(context) { Name: contractName,
this.kvc = new kvcreator("exec") }
this.context = context req := &rpctypes.CreateTxIn{
} Execer: ptypes.JsX,
ActionName: "Create",
Payload: types.MustPBToJSON(create),
}
var txhex string
err = mocker.GetJSONC().Call("Chain33.CreateTransaction", req, &txhex)
assert.Nil(t, err)
hash, err := mocker.SendAndSign(mocker.GetHotKey(), txhex)
assert.Nil(t, err)
txinfo, err := mocker.WaitTx(hash)
assert.Nil(t, err)
assert.Equal(t, txinfo.Receipt.Ty, int32(2))
block := mocker.GetLastBlock()
balance := mocker.GetAccount(block.StateHash, mocker.GetHotAddress()).Balance
assert.Equal(t, balance, 10000*types.Coin)
//2.1 充值到合约
reqtx := &rpctypes.CreateTx{
To: address.ExecAddress("user.jsvm." + contractName),
Amount: 100 * types.Coin,
Note: "12312",
IsWithdraw: false,
IsToken: false,
TokenSymbol: "",
ExecName: "user.jsvm." + contractName,
}
err = mocker.GetJSONC().Call("Chain33.CreateRawTransaction", reqtx, &txhex)
assert.Nil(t, err)
hash, err = mocker.SendAndSign(mocker.GetHotKey(), txhex)
assert.Nil(t, err)
txinfo, err = mocker.WaitTx(hash)
assert.Nil(t, err)
assert.Equal(t, txinfo.Receipt.Ty, int32(2))
block = mocker.GetLastBlock()
balance = mocker.GetExecAccount(block.StateHash, "user.jsvm."+contractName, mocker.GetHotAddress()).Balance
assert.Equal(t, 100*types.Coin, balance)
function ExecLocal(context, logs) { reqtx = &rpctypes.CreateTx{
this.kvc = new kvcreator("local") To: address.ExecAddress("user.jsvm." + contractName),
this.context = context Amount: 100 * types.Coin,
this.logs = logs Note: "12312",
} IsWithdraw: false,
IsToken: false,
TokenSymbol: "",
ExecName: "user.jsvm." + contractName,
}
err = mocker.GetJSONC().Call("Chain33.CreateRawTransaction", reqtx, &txhex)
assert.Nil(t, err)
hash, err = mocker.SendAndSign(mocker.GetGenesisKey(), txhex)
assert.Nil(t, err)
txinfo, err = mocker.WaitTx(hash)
assert.Nil(t, err)
assert.Equal(t, txinfo.Receipt.Ty, int32(2))
block = mocker.GetLastBlock()
balance = mocker.GetExecAccount(block.StateHash, "user.jsvm."+contractName, mocker.GetGenesisAddress()).Balance
assert.Equal(t, 100*types.Coin, balance)
t.Log(mocker.GetGenesisAddress())
//2.2 调用 hello 函数(随机数,用nonce)
privhash := common.Sha256(mocker.GetHotKey().Bytes())
nonce := rand.Int63()
num := rand.Int63() % 10
realhash := common.ToHex(common.Sha256([]byte(string(privhash) + ":" + fmt.Sprint(nonce))))
myhash := common.ToHex(common.Sha256([]byte(realhash + fmt.Sprint(num))))
function Query(context) { call := &jsproto.Call{
this.kvc = new kvcreator("query") Funcname: "NewGame",
this.context = context Name: contractName,
} Args: fmt.Sprintf(`{"bet": %d, "randhash" : "%s"}`, 100*types.Coin, myhash),
}
req = &rpctypes.CreateTxIn{
Execer: "user." + ptypes.JsX + "." + contractName,
ActionName: "Call",
Payload: types.MustPBToJSON(call),
}
err = mocker.GetJSONC().Call("Chain33.CreateTransaction", req, &txhex)
assert.Nil(t, err)
hash, err = mocker.SendAndSignNonce(mocker.GetHotKey(), txhex, nonce)
assert.Nil(t, err)
txinfo, err = mocker.WaitTx(hash)
assert.Nil(t, err)
assert.Equal(t, txinfo.Receipt.Ty, int32(2))
gameid := txinfo.Height*100000 + txinfo.Index
//2.3 guess a number (win)
call = &jsproto.Call{
Funcname: "Guess",
Name: contractName,
Args: fmt.Sprintf(`{"bet": %d, "gameid" : "%d", "num" : %d}`, 1*types.Coin, gameid, num),
}
req = &rpctypes.CreateTxIn{
Execer: "user." + ptypes.JsX + "." + contractName,
ActionName: "Call",
Payload: types.MustPBToJSON(call),
}
err = mocker.GetJSONC().Call("Chain33.CreateTransaction", req, &txhex)
assert.Nil(t, err)
hash, err = mocker.SendAndSignNonce(mocker.GetGenesisKey(), txhex, nonce)
assert.Nil(t, err)
txinfo, err = mocker.WaitTx(hash)
assert.Nil(t, err)
assert.Equal(t, txinfo.Receipt.Ty, int32(2))
Exec.prototype.hello = function(args) { //2.4 guess a num (failed)
this.kvc.add("args", args) call = &jsproto.Call{
this.kvc.add("action", "exec") Funcname: "Guess",
this.kvc.add("context", this.context) Name: contractName,
this.kvc.addlog({"key1": "value1"}) Args: fmt.Sprintf(`{"bet": %d, "gameid" : "%d", "num" : %d}`, 1*types.Coin, gameid, num+1),
this.kvc.addlog({"key2": "value2"}) }
return this.kvc.receipt() req = &rpctypes.CreateTxIn{
} Execer: "user." + ptypes.JsX + "." + contractName,
ActionName: "Call",
Payload: types.MustPBToJSON(call),
}
err = mocker.GetJSONC().Call("Chain33.CreateTransaction", req, &txhex)
assert.Nil(t, err)
t.Log(mocker.GetHotAddress())
hash, err = mocker.SendAndSignNonce(mocker.GetGenesisKey(), txhex, nonce)
assert.Nil(t, err)
txinfo, err = mocker.WaitTx(hash)
assert.Nil(t, err)
assert.Equal(t, txinfo.Receipt.Ty, int32(2))
ExecLocal.prototype.hello = function(args) { //2.5 close the game
this.kvc.add("args", args) call = &jsproto.Call{
this.kvc.add("action", "execlocal") Funcname: "CloseGame",
this.kvc.add("log", this.logs) Name: contractName,
this.kvc.add("context", this.context) Args: fmt.Sprintf(`{"gameid":%d, "randstr":"%s"}`, gameid, realhash),
return this.kvc.receipt() }
} req = &rpctypes.CreateTxIn{
Execer: "user." + ptypes.JsX + "." + contractName,
ActionName: "Call",
Payload: types.MustPBToJSON(call),
}
err = mocker.GetJSONC().Call("Chain33.CreateTransaction", req, &txhex)
assert.Nil(t, err)
t.Log(mocker.GetHotAddress())
hash, err = mocker.SendAndSignNonce(mocker.GetHotKey(), txhex, nonce)
assert.Nil(t, err)
txinfo, err = mocker.WaitTx(hash)
assert.Nil(t, err)
assert.Equal(t, txinfo.Receipt.Ty, int32(2))
//3.1 query game 函数查询
call = &jsproto.Call{
Funcname: "ListGameByAddr",
Name: contractName,
Args: fmt.Sprintf(`{"addr":"%s", "count" : 20}`, txinfo.Tx.From),
}
query := &rpctypes.Query4Jrpc{
Execer: "user." + ptypes.JsX + "." + contractName,
FuncName: "Query",
Payload: types.MustPBToJSON(call),
}
var queryresult jsproto.QueryResult
err = mocker.GetJSONC().Call("Chain33.Query", query, &queryresult)
assert.Nil(t, err)
t.Log(queryresult.Data)
//return a json string //3.2 query match -> status 函数
Query.prototype.hello = function(args) { call = &jsproto.Call{
return tojson({hello:"wzw"}) Funcname: "JoinKey",
Name: contractName,
Args: fmt.Sprintf(`{"left":"%s", "right" : "%s"}`, mocker.GetGenesisAddress(), "2"),
}
query = &rpctypes.Query4Jrpc{
Execer: "user." + ptypes.JsX + "." + contractName,
FuncName: "Query",
Payload: types.MustPBToJSON(call),
}
err = mocker.GetJSONC().Call("Chain33.Query", query, &queryresult)
assert.Nil(t, err)
joinkey := queryresult.Data
reqjson := make(map[string]interface{})
reqjson["addr#status"] = joinkey
reqdata, _ := json.Marshal(reqjson)
call = &jsproto.Call{
Funcname: "ListMatchByAddr",
Name: contractName,
Args: string(reqdata),
}
query = &rpctypes.Query4Jrpc{
Execer: "user." + ptypes.JsX + "." + contractName,
FuncName: "Query",
Payload: types.MustPBToJSON(call),
}
err = mocker.GetJSONC().Call("Chain33.Query", query, &queryresult)
assert.Nil(t, err)
t.Log(queryresult.Data)
} }
`
package executor package executor
import ( import (
"github.com/33cn/chain33/common"
"github.com/33cn/chain33/common/address" "github.com/33cn/chain33/common/address"
"github.com/robertkrimen/otto" "github.com/robertkrimen/otto"
) )
...@@ -17,6 +18,17 @@ func execaddressFunc(vm *otto.Otto) { ...@@ -17,6 +18,17 @@ func execaddressFunc(vm *otto.Otto) {
}) })
} }
func sha256Func(vm *otto.Otto) {
vm.Set("sha256", func(call otto.FunctionCall) otto.Value {
key, err := call.Argument(0).ToString()
if err != nil {
return errReturn(vm, err)
}
var hash = common.Sha256([]byte(key))
return okReturn(vm, common.ToHex(hash))
})
}
/* /*
//获取系统随机数的接口 //获取系统随机数的接口
//randnum //randnum
......
var tojson = JSON.stringify var tojson = JSON.stringify
//table warp //table warp
function table(kvc, config, defaultvalue) { function Table(kvc, config, defaultvalue) {
var ret = table_new(config, defaultvalue) var ret = table_new(tojson(config), tojson(defaultvalue))
if (ret.err) { if (ret.err) {
throw new Error(ret.err) throw new Error(ret.err)
} }
this.kvc = kvc this.kvc = kvc
this.id = ret.id this.id = ret.id
this.config = config this.config = config
this.name = config["#tablename"]
this.defaultvalue = defaultvalue this.defaultvalue = defaultvalue
} }
...@@ -15,7 +16,7 @@ function isstring(obj) { ...@@ -15,7 +16,7 @@ function isstring(obj) {
return typeof obj === "string" return typeof obj === "string"
} }
table.prototype.add = function(obj) { Table.prototype.add = function(obj) {
if (!isstring(obj)) { if (!isstring(obj)) {
obj = tojson(obj) obj = tojson(obj)
} }
...@@ -23,7 +24,57 @@ table.prototype.add = function(obj) { ...@@ -23,7 +24,57 @@ table.prototype.add = function(obj) {
return ret.err return ret.err
} }
table.prototype.replace = function(obj) { Table.prototype.joinkey = function(left, right) {
return table_joinkey(left, right)
}
Table.prototype.get = function(key, row) {
if (!isstring(row)) {
row = tojson(row)
}
var ret = table_get(this.id, key, row)
if (ret.err) {
throwerr(ret.err)
}
return ret.value
}
function query_list(indexName, prefix, primaryKey, count, direction) {
if (count !== 0 && !count) {
count = 20
}
if (!direction) {
direction = 0
}
if (!primaryKey) {
primaryKey = ""
}
if (!prefix) {
prefix = ""
}
if (!indexName) {
indexName = ""
}
var q = table_query(this.id, indexName, prefix, primaryKey, count, direction)
if (q.err) {
return null
}
for (var i = 0; i < q.length; i++) {
if (q[i].left) {
q[i].left = JSON.parse(q[i].left)
}
if (q[i].right) {
q[i].right = JSON.parse(q[i].right)
}
}
return q
}
Table.prototype.query = function(indexName, prefix, primaryKey, count, direction) {
return query_list.call(this, indexName, prefix, primaryKey, count, direction)
}
Table.prototype.replace = function(obj) {
if (!isstring(obj)) { if (!isstring(obj)) {
obj = tojson(obj) obj = tojson(obj)
} }
...@@ -31,7 +82,7 @@ table.prototype.replace = function(obj) { ...@@ -31,7 +82,7 @@ table.prototype.replace = function(obj) {
return ret.err return ret.err
} }
table.prototype.del = function(obj) { Table.prototype.del = function(obj) {
if (!isstring(obj)) { if (!isstring(obj)) {
obj = tojson(obj) obj = tojson(obj)
} }
...@@ -39,7 +90,7 @@ table.prototype.del = function(obj) { ...@@ -39,7 +90,7 @@ table.prototype.del = function(obj) {
return ret.err return ret.err
} }
table.prototype.save = function() { Table.prototype.save = function() {
var ret = table_save(this.id) var ret = table_save(this.id)
if (!this.kvc) { if (!this.kvc) {
this.kvc.save(ret) this.kvc.save(ret)
...@@ -47,17 +98,17 @@ table.prototype.save = function() { ...@@ -47,17 +98,17 @@ table.prototype.save = function() {
return ret return ret
} }
table.prototype.close = function() { Table.prototype.close = function() {
var ret = table_close(this.id) var ret = table_close(this.id)
return ret.err return ret.err
} }
function JoinTable(lefttable, righttable, index) { function JoinTable(lefttable, righttable, index) {
this.lefttable = lefttable
this.righttable = righttable
if (this.lefttable.kvc != this.righttable.kvc) { if (this.lefttable.kvc != this.righttable.kvc) {
throw new Error("the kvc of left and right must same") throw new Error("the kvc of left and right must same")
} }
this.lefttable = lefttable
this.righttable = righttable
this.index = index this.index = index
var ret = new_join_table(this.lefttable.id, this.righttable.id, index) var ret = new_join_table(this.lefttable.id, this.righttable.id, index)
if (ret.err) { if (ret.err) {
...@@ -67,14 +118,44 @@ function JoinTable(lefttable, righttable, index) { ...@@ -67,14 +118,44 @@ function JoinTable(lefttable, righttable, index) {
this.kvc = this.lefttable.kvc this.kvc = this.lefttable.kvc
} }
JoinTable.prototype.Save = function() { function print(obj) {
if (typeof obj === "string") {
console.log(obj)
return
}
console.log(tojson(obj))
}
JoinTable.prototype.save = function() {
var ret = table_save(this.id) var ret = table_save(this.id)
if (!this.kvc) { if (this.kvc) {
this.kvc.save(ret) this.kvc.save(ret)
} }
return ret return ret
} }
JoinTable.prototype.get = function(key, row) {
if (!isstring(row)) {
row = tojson(row)
}
var ret = table_get(this.id, key, row)
if (ret.err) {
throwerr(ret.err)
}
return ret.value
}
JoinTable.prototype.query = function(indexName, prefix, primaryKey, count, direction) {
return query_list.call(this, indexName, prefix, primaryKey, count, direction)
}
function querytojson(data) {
if (!data) {
return "[]"
}
return tojson(data)
}
JoinTable.prototype.close = function() { JoinTable.prototype.close = function() {
table_close(this.lefttable.id) table_close(this.lefttable.id)
table_close(this.righttable.id) table_close(this.righttable.id)
...@@ -82,6 +163,24 @@ JoinTable.prototype.close = function() { ...@@ -82,6 +163,24 @@ JoinTable.prototype.close = function() {
return ret.err return ret.err
} }
JoinTable.prototype.addlogs = function(data) {
var err
for (var i = 0; i < data.length; i++) {
if (data[i].format != "json") {
continue
}
var log = JSON.parse(data[i].log)
if (log.__type__ == this.lefttable.name) {
err = this.lefttable.replace(data[i].log)
throwerr(err)
}
if (log.__type__ == this.righttable.name) {
err = this.righttable.replace(data[i].log)
throwerr(err)
}
}
}
//account warp //account warp
function account(kvc, execer, symbol) { function account(kvc, execer, symbol) {
this.execer = execer this.execer = execer
...@@ -91,7 +190,7 @@ function account(kvc, execer, symbol) { ...@@ -91,7 +190,7 @@ function account(kvc, execer, symbol) {
account.prototype.genesisInit = function(addr, amount) { account.prototype.genesisInit = function(addr, amount) {
var ret = genesis_init(this, addr, amount) var ret = genesis_init(this, addr, amount)
if (!this.kvc) { if (this.kvc) {
this.kvc.save(ret) this.kvc.save(ret)
} }
return ret.err return ret.err
...@@ -99,7 +198,7 @@ account.prototype.genesisInit = function(addr, amount) { ...@@ -99,7 +198,7 @@ account.prototype.genesisInit = function(addr, amount) {
account.prototype.execGenesisInit = function(execer, addr, amount) { account.prototype.execGenesisInit = function(execer, addr, amount) {
var ret = genesis_init_exec(this, execer, addr, amount) var ret = genesis_init_exec(this, execer, addr, amount)
if (!this.kvc) { if (this.kvc) {
this.kvc.save(ret) this.kvc.save(ret)
} }
return ret.err return ret.err
...@@ -116,7 +215,7 @@ account.prototype.execGetBalance = function(execer, addr) { ...@@ -116,7 +215,7 @@ account.prototype.execGetBalance = function(execer, addr) {
//本合约转移资产,或者转移到其他合约,或者从其他合约取回资产 //本合约转移资产,或者转移到其他合约,或者从其他合约取回资产
account.prototype.transfer = function(from, to, amount) { account.prototype.transfer = function(from, to, amount) {
var ret = transfer(this, from, to, amount) var ret = transfer(this, from, to, amount)
if (!this.kvc) { if (this.kvc) {
this.kvc.save(ret) this.kvc.save(ret)
} }
return ret.err return ret.err
...@@ -124,7 +223,7 @@ account.prototype.transfer = function(from, to, amount) { ...@@ -124,7 +223,7 @@ account.prototype.transfer = function(from, to, amount) {
account.prototype.transferToExec = function(execer, from, amount) { account.prototype.transferToExec = function(execer, from, amount) {
var ret = transfer_to_exec(this, execer, from, amount) var ret = transfer_to_exec(this, execer, from, amount)
if (!this.kvc) { if (this.kvc) {
this.kvc.save(ret) this.kvc.save(ret)
} }
return ret.err return ret.err
...@@ -132,7 +231,7 @@ account.prototype.transferToExec = function(execer, from, amount) { ...@@ -132,7 +231,7 @@ account.prototype.transferToExec = function(execer, from, amount) {
account.prototype.withdrawFromExec = function(execer, to, amount) { account.prototype.withdrawFromExec = function(execer, to, amount) {
var ret = withdraw(this, execer, to, amount) var ret = withdraw(this, execer, to, amount)
if (!this.kvc) { if (this.kvc) {
this.kvc.save(ret) this.kvc.save(ret)
} }
return ret.err return ret.err
...@@ -141,7 +240,7 @@ account.prototype.withdrawFromExec = function(execer, to, amount) { ...@@ -141,7 +240,7 @@ account.prototype.withdrawFromExec = function(execer, to, amount) {
//管理其他合约的资产转移到这个合约中 //管理其他合约的资产转移到这个合约中
account.prototype.execActive = function(execer, addr, amount) { account.prototype.execActive = function(execer, addr, amount) {
var ret = exec_active(this, execer, addr, amount) var ret = exec_active(this, execer, addr, amount)
if (!this.kvc) { if (this.kvc) {
this.kvc.save(ret) this.kvc.save(ret)
} }
return ret.err return ret.err
...@@ -149,7 +248,7 @@ account.prototype.execActive = function(execer, addr, amount) { ...@@ -149,7 +248,7 @@ account.prototype.execActive = function(execer, addr, amount) {
account.prototype.execFrozen = function(execer, addr, amount) { account.prototype.execFrozen = function(execer, addr, amount) {
var ret = exec_frozen(this, execer, addr, amount) var ret = exec_frozen(this, execer, addr, amount)
if (!this.kvc) { if (this.kvc) {
this.kvc.save(ret) this.kvc.save(ret)
} }
return ret.err return ret.err
...@@ -157,7 +256,7 @@ account.prototype.execFrozen = function(execer, addr, amount) { ...@@ -157,7 +256,7 @@ account.prototype.execFrozen = function(execer, addr, amount) {
account.prototype.execDeposit = function(execer, addr, amount) { account.prototype.execDeposit = function(execer, addr, amount) {
var ret = exec_deposit(this, execer, addr, amount) var ret = exec_deposit(this, execer, addr, amount)
if (!this.kvc) { if (this.kvc) {
this.kvc.save(ret) this.kvc.save(ret)
} }
return ret.err return ret.err
...@@ -165,7 +264,7 @@ account.prototype.execDeposit = function(execer, addr, amount) { ...@@ -165,7 +264,7 @@ account.prototype.execDeposit = function(execer, addr, amount) {
account.prototype.execWithdraw = function(execer, addr, amount) { account.prototype.execWithdraw = function(execer, addr, amount) {
var ret = exec_withdraw(this, execer, addr, amount) var ret = exec_withdraw(this, execer, addr, amount)
if (!this.kvc) { if (this.kvc) {
this.kvc.save(ret) this.kvc.save(ret)
} }
return ret.err return ret.err
...@@ -173,12 +272,47 @@ account.prototype.execWithdraw = function(execer, addr, amount) { ...@@ -173,12 +272,47 @@ account.prototype.execWithdraw = function(execer, addr, amount) {
account.prototype.execTransfer = function(execer, from, to, amount) { account.prototype.execTransfer = function(execer, from, to, amount) {
var ret = exec_transfer(this, execer, from, to, amount) var ret = exec_transfer(this, execer, from, to, amount)
if (!this.kvc) { if (this.kvc) {
this.kvc.save(ret) this.kvc.save(ret)
} }
return ret.err return ret.err
} }
//from frozen -> to active
account.prototype.execTransFrozenToActive = function(execer, from, to, amount) {
var err
err = this.execActive(execer, from, amount)
if (err) {
return err
}
err = this.execTransfer(execer, from, to, amount)
}
//from frozen -> to frozen
account.prototype.execTransFrozenToFrozen = function(execer, from, to, amount) {
var err
err = this.execActive(execer, from, amount)
if (err) {
return err
}
err = this.execTransfer(execer, from, to, amount)
if (err) {
return err
}
return this.execFrozen(execer, to, amount)
}
account.prototype.execTransActiveToFrozen = function(execer, from, to, amount) {
var err
err = this.execTransfer(execer, from, to, amount)
if (err) {
return err
}
return this.execFrozen(execer, to, amount)
}
COINS = 100000000
function kvcreator(dbtype) { function kvcreator(dbtype) {
this.data = {} this.data = {}
this.kvs = [] this.kvs = []
...@@ -188,11 +322,11 @@ function kvcreator(dbtype) { ...@@ -188,11 +322,11 @@ function kvcreator(dbtype) {
this.getloal = getlocaldb this.getloal = getlocaldb
this.list = listdb this.list = listdb
if (dbtype == "exec" || dbtype == "init") { if (dbtype == "exec" || dbtype == "init") {
this.get = this.getstatedb this.getdb = this.getstate
} else if (dbtype == "local") { } else if (dbtype == "local") {
this.get = this.getlocaldb this.getdb = this.getlocal
} else if (dbtype == "query") { } else if (dbtype == "query") {
this.get = this.getlocaldb this.getdb = this.getlocal
} else { } else {
throw new Error("chain33.js: dbtype error") throw new Error("chain33.js: dbtype error")
} }
...@@ -211,8 +345,8 @@ kvcreator.prototype.get = function(k, prefix) { ...@@ -211,8 +345,8 @@ kvcreator.prototype.get = function(k, prefix) {
if (this.data[k]) { if (this.data[k]) {
v = this.data[k] v = this.data[k]
} else { } else {
var dbvalue = this.get(k, !!prefix) var dbvalue = this.getdb(k, !!prefix)
if (dbvalue.err != "") { if (dbvalue.err) {
return null return null
} }
v = dbvalue.value v = dbvalue.value
...@@ -240,7 +374,7 @@ kvcreator.prototype.save = function(receipt) { ...@@ -240,7 +374,7 @@ kvcreator.prototype.save = function(receipt) {
kvcreator.prototype.listvalue = function(prefix, key, count, direction) { kvcreator.prototype.listvalue = function(prefix, key, count, direction) {
var dbvalues = this.list(prefix, key, count, direction) var dbvalues = this.list(prefix, key, count, direction)
if (dbvalues.err != "") { if (dbvalues.err) {
return [] return []
} }
var values = dbvalues.value var values = dbvalues.value
...@@ -274,6 +408,73 @@ kvcreator.prototype.receipt = function() { ...@@ -274,6 +408,73 @@ kvcreator.prototype.receipt = function() {
return {kvs: this.kvs, logs: this.logs} return {kvs: this.kvs, logs: this.logs}
} }
function GetExecName() {
var exec = execname()
if (exec.err) {
return ""
}
return exec.value
}
function ExecAddress(name) {
var addr = execaddress(name)
if (addr.err) {
return ""
}
console.log(addr.value)
return addr.value
}
function Sha256(data) {
var hash = sha256(data)
if (hash.err) {
return ""
}
return hash.value
}
function Exec(context) {
this.kvc = new kvcreator("exec")
this.context = context
this.name = GetExecName()
if (typeof ExecInit === "function") {
ExecInit.call(this)
}
}
Exec.prototype.txID = function() {
return this.context.height * 100000 + this.context.index
}
function ExecLocal(context, logs) {
this.kvc = new kvcreator("local")
this.context = context
this.logs = logs
this.name = GetExecName()
if (typeof ExecLocalInit === "function") {
ExecLocalInit.call(this)
}
}
function Query(context) {
this.kvc = new kvcreator("query")
this.context = context
this.name = GetExecName()
if (typeof QueryInit === "function") {
QueryInit.call(this)
}
}
Query.prototype.JoinKey = function(args) {
return table_joinkey(args.left, args.right).value
}
function throwerr(err, msg) {
if (err) {
throw new Error(err + ":" + msg)
}
}
function callcode(context, f, args, loglist) { function callcode(context, f, args, loglist) {
if (f == "init") { if (f == "init") {
return Init(JSON.parse(context)) return Init(JSON.parse(context))
...@@ -303,7 +504,7 @@ function callcode(context, f, args, loglist) { ...@@ -303,7 +504,7 @@ function callcode(context, f, args, loglist) {
} }
var arg = JSON.parse(args) var arg = JSON.parse(args)
if (typeof runobj[funcname] != "function") { if (typeof runobj[funcname] != "function") {
throw new Error("chain33.js: invalid function name not found") throw new Error("chain33.js: invalid function name not found->" + funcname)
} }
return runobj[funcname](arg) return runobj[funcname](arg)
} }
......
package executor package executor
import ( import (
"bytes"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"regexp"
"strconv"
"strings" "strings"
"sync"
"sync/atomic"
"github.com/33cn/chain33/common/db" "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/common/db/table" "github.com/33cn/chain33/common/db/table"
...@@ -34,22 +36,11 @@ import ( ...@@ -34,22 +36,11 @@ import (
"index2" : "", "index2" : "",
} }
*/ */
var globalTableHandle sync.Map
var globalHanldeID int64
//NewTable 创建一个新的表格, 返回handle //NewTable 创建一个新的表格, 返回handle
func (u *js) newTable(name, config, defaultvalue string) (id int64, err error) { func (u *js) newTable(name, config, defaultvalue string) (id int64, err error) {
for { u.globalHanldeID++
id = atomic.AddInt64(&globalHanldeID, 1) % maxjsint id = u.globalHanldeID
if _, ok := globalTableHandle.Load(id); ok {
continue
}
if id < 0 {
atomic.StoreInt64(&globalHanldeID, 0)
continue
}
break
}
row, err := NewJSONRow(config, defaultvalue) row, err := NewJSONRow(config, defaultvalue)
if err != nil { if err != nil {
return 0, err return 0, err
...@@ -73,7 +64,7 @@ func (u *js) newTable(name, config, defaultvalue string) (id int64, err error) { ...@@ -73,7 +64,7 @@ func (u *js) newTable(name, config, defaultvalue string) (id int64, err error) {
indexs = append(indexs, k) indexs = append(indexs, k)
} }
opt := &table.Option{ opt := &table.Option{
Prefix: string(prefix), Prefix: strings.Trim(string(prefix), "-"),
Name: row.config["#tablename"], Name: row.config["#tablename"],
Primary: row.config["#primary"], Primary: row.config["#primary"],
Index: indexs, Index: indexs,
...@@ -82,7 +73,7 @@ func (u *js) newTable(name, config, defaultvalue string) (id int64, err error) { ...@@ -82,7 +73,7 @@ func (u *js) newTable(name, config, defaultvalue string) (id int64, err error) {
if err != nil { if err != nil {
return 0, err return 0, err
} }
globalTableHandle.Store(id, t) u.globalTableHandle.Store(id, t)
return id, nil return id, nil
} }
...@@ -105,45 +96,64 @@ func (u *js) newTableFunc(vm *otto.Otto, name string) { ...@@ -105,45 +96,64 @@ func (u *js) newTableFunc(vm *otto.Otto, name string) {
} }
//CloseTable 关闭表格释放内存 //CloseTable 关闭表格释放内存
func closeTable(id int64) error { func (u *js) closeTable(id int64) error {
_, ok := globalTableHandle.Load(id) _, ok := u.globalTableHandle.Load(id)
if !ok { if !ok {
return types.ErrNotFound return types.ErrNotFound
} }
globalTableHandle.Delete(id) u.globalTableHandle.Delete(id)
return nil return nil
} }
func getTable(id int64) (*table.Table, error) { func (u *js) getTable(id int64) (*table.Table, error) {
if value, ok := globalTableHandle.Load(id); ok { if value, ok := u.globalTableHandle.Load(id); ok {
return value.(*table.Table), nil return value.(*table.Table), nil
} }
return nil, types.ErrNotFound return nil, types.ErrNotFound
} }
func getSaver(id int64) (saver, error) { func (u *js) getTabler(id int64) (tabler, error) {
if value, ok := globalTableHandle.Load(id); ok { if value, ok := u.globalTableHandle.Load(id); ok {
return value.(saver), nil return value.(tabler), nil
} }
return nil, types.ErrNotFound return nil, types.ErrNotFound
} }
func registerTableFunc(vm *otto.Otto) { func (u *js) registerTableFunc(vm *otto.Otto, name string) {
tableAddFunc(vm) u.newTableFunc(vm, name)
tableReplaceFunc(vm) u.tableAddFunc(vm)
tableDelFunc(vm) u.tableReplaceFunc(vm)
tableCloseFunc(vm) u.tableDelFunc(vm)
tableSave(vm) u.tableCloseFunc(vm)
tableJoinFunc(vm) u.tableSave(vm)
u.tableJoinFunc(vm)
u.tableQueryFunc(vm)
u.tableGetFunc(vm)
u.tableJoinKeyFunc(vm)
} }
func tableAddFunc(vm *otto.Otto) { func (u *js) tableJoinKeyFunc(vm *otto.Otto) {
vm.Set("table_joinkey", func(call otto.FunctionCall) otto.Value {
left, err := call.Argument(0).ToString()
if err != nil {
return errReturn(vm, err)
}
right, err := call.Argument(1).ToString()
if err != nil {
return errReturn(vm, err)
}
key := table.JoinKey([]byte(left), []byte(right))
return okReturn(vm, string(key))
})
}
func (u *js) tableAddFunc(vm *otto.Otto) {
vm.Set("table_add", func(call otto.FunctionCall) otto.Value { vm.Set("table_add", func(call otto.FunctionCall) otto.Value {
id, err := call.Argument(0).ToInteger() id, err := call.Argument(0).ToInteger()
if err != nil { if err != nil {
return errReturn(vm, err) return errReturn(vm, err)
} }
tab, err := getTable(id) tab, err := u.getTable(id)
if err != nil { if err != nil {
return errReturn(vm, err) return errReturn(vm, err)
} }
...@@ -159,13 +169,13 @@ func tableAddFunc(vm *otto.Otto) { ...@@ -159,13 +169,13 @@ func tableAddFunc(vm *otto.Otto) {
}) })
} }
func tableReplaceFunc(vm *otto.Otto) { func (u *js) tableReplaceFunc(vm *otto.Otto) {
vm.Set("table_replace", func(call otto.FunctionCall) otto.Value { vm.Set("table_replace", func(call otto.FunctionCall) otto.Value {
id, err := call.Argument(0).ToInteger() id, err := call.Argument(0).ToInteger()
if err != nil { if err != nil {
return errReturn(vm, err) return errReturn(vm, err)
} }
tab, err := getTable(id) tab, err := u.getTable(id)
if err != nil { if err != nil {
return errReturn(vm, err) return errReturn(vm, err)
} }
...@@ -181,13 +191,13 @@ func tableReplaceFunc(vm *otto.Otto) { ...@@ -181,13 +191,13 @@ func tableReplaceFunc(vm *otto.Otto) {
}) })
} }
func tableDelFunc(vm *otto.Otto) { func (u *js) tableDelFunc(vm *otto.Otto) {
vm.Set("table_del", func(call otto.FunctionCall) otto.Value { vm.Set("table_del", func(call otto.FunctionCall) otto.Value {
id, err := call.Argument(0).ToInteger() id, err := call.Argument(0).ToInteger()
if err != nil { if err != nil {
return errReturn(vm, err) return errReturn(vm, err)
} }
tab, err := getTable(id) tab, err := u.getTable(id)
if err != nil { if err != nil {
return errReturn(vm, err) return errReturn(vm, err)
} }
...@@ -203,17 +213,47 @@ func tableDelFunc(vm *otto.Otto) { ...@@ -203,17 +213,47 @@ func tableDelFunc(vm *otto.Otto) {
}) })
} }
type saver interface { func (u *js) tableGetFunc(vm *otto.Otto) {
vm.Set("table_get", func(call otto.FunctionCall) otto.Value {
id, err := call.Argument(0).ToInteger()
if err != nil {
return errReturn(vm, err)
}
tab, err := u.getTable(id)
if err != nil {
return errReturn(vm, err)
}
key, err := call.Argument(1).ToString()
if err != nil {
return errReturn(vm, err)
}
row, err := call.Argument(2).ToString()
if err != nil {
return errReturn(vm, err)
}
meta := tab.GetMeta()
meta.SetPayload(&jsproto.JsLog{Data: row})
result, err := meta.Get(key)
if err != nil {
return errReturn(vm, err)
}
return okReturn(vm, string(result))
})
}
type tabler interface {
GetMeta() table.RowMeta
ListIndex(indexName string, prefix []byte, primaryKey []byte, count, direction int32) (rows []*table.Row, err error)
Save() (kvs []*types.KeyValue, err error) Save() (kvs []*types.KeyValue, err error)
} }
func tableSave(vm *otto.Otto) { func (u *js) tableSave(vm *otto.Otto) {
vm.Set("table_save", func(call otto.FunctionCall) otto.Value { vm.Set("table_save", func(call otto.FunctionCall) otto.Value {
id, err := call.Argument(0).ToInteger() id, err := call.Argument(0).ToInteger()
if err != nil { if err != nil {
return errReturn(vm, err) return errReturn(vm, err)
} }
tab, err := getSaver(id) tab, err := u.getTabler(id)
if err != nil { if err != nil {
return errReturn(vm, err) return errReturn(vm, err)
} }
...@@ -225,13 +265,13 @@ func tableSave(vm *otto.Otto) { ...@@ -225,13 +265,13 @@ func tableSave(vm *otto.Otto) {
}) })
} }
func tableCloseFunc(vm *otto.Otto) { func (u *js) tableCloseFunc(vm *otto.Otto) {
vm.Set("table_close", func(call otto.FunctionCall) otto.Value { vm.Set("table_close", func(call otto.FunctionCall) otto.Value {
id, err := call.Argument(0).ToInteger() id, err := call.Argument(0).ToInteger()
if err != nil { if err != nil {
return errReturn(vm, err) return errReturn(vm, err)
} }
err = closeTable(id) err = u.closeTable(id)
if err != nil { if err != nil {
return errReturn(vm, err) return errReturn(vm, err)
} }
...@@ -239,13 +279,95 @@ func tableCloseFunc(vm *otto.Otto) { ...@@ -239,13 +279,95 @@ func tableCloseFunc(vm *otto.Otto) {
}) })
} }
func tableJoinFunc(vm *otto.Otto) { func (u *js) tableQueryFunc(vm *otto.Otto) {
vm.Set("table_query", func(call otto.FunctionCall) otto.Value {
id, err := call.Argument(0).ToInteger()
if err != nil {
return errReturn(vm, err)
}
tab, err := u.getTabler(id)
if err != nil {
return errReturn(vm, err)
}
//参数
//List(indexName string, data types.Message, primaryKey []byte, count, direction int32) (rows []*Row, err error)
indexName, err := call.Argument(1).ToString()
if err != nil {
return errReturn(vm, err)
}
prefix, err := call.Argument(2).ToString()
if err != nil {
return errReturn(vm, err)
}
primaryKey, err := call.Argument(3).ToString()
if err != nil {
return errReturn(vm, err)
}
count, err := call.Argument(4).ToInteger()
if err != nil {
return errReturn(vm, err)
}
direction, err := call.Argument(5).ToInteger()
if err != nil {
return errReturn(vm, err)
}
bprefix := []byte(prefix)
if prefix == "" {
bprefix = nil
}
bprimaryKey := []byte(primaryKey)
if primaryKey == "" {
bprimaryKey = nil
}
rows, err := tab.ListIndex(indexName, bprefix, bprimaryKey, int32(count), int32(direction))
if err != nil {
return errReturn(vm, err)
}
_, isjoin := tab.(*table.JoinTable)
querylist := make([]interface{}, len(rows))
for i := 0; i < len(rows); i++ {
if isjoin {
joindata, ok := rows[i].Data.(*table.JoinData)
if !ok {
return errReturn(vm, errors.New("jointable has no joindata"))
}
leftdata, ok := joindata.Left.(*jsproto.JsLog)
if !ok {
return errReturn(vm, errors.New("leftdata is not JsLog"))
}
rightdata, ok := joindata.Right.(*jsproto.JsLog)
if !ok {
return errReturn(vm, errors.New("rightdata is not jslog"))
}
obj := make(map[string]interface{})
obj["left"] = leftdata.Data
obj["right"] = rightdata.Data
querylist[i] = obj
} else {
leftdata, ok := rows[i].Data.(*jsproto.JsLog)
if !ok {
return errReturn(vm, errors.New("data is not JsLog"))
}
obj := make(map[string]interface{})
obj["left"] = leftdata.Data
querylist[i] = obj
}
}
retvalue, err := vm.ToValue(querylist)
if err != nil {
return errReturn(vm, err)
}
return retvalue
})
}
func (u *js) tableJoinFunc(vm *otto.Otto) {
vm.Set("new_join_table", func(call otto.FunctionCall) otto.Value { vm.Set("new_join_table", func(call otto.FunctionCall) otto.Value {
left, err := call.Argument(0).ToInteger() left, err := call.Argument(0).ToInteger()
if err != nil { if err != nil {
return errReturn(vm, err) return errReturn(vm, err)
} }
lefttab, err := getTable(left) lefttab, err := u.getTable(left)
if err != nil { if err != nil {
return errReturn(vm, err) return errReturn(vm, err)
} }
...@@ -253,7 +375,7 @@ func tableJoinFunc(vm *otto.Otto) { ...@@ -253,7 +375,7 @@ func tableJoinFunc(vm *otto.Otto) {
if err != nil { if err != nil {
return errReturn(vm, err) return errReturn(vm, err)
} }
righttab, err := getTable(right) righttab, err := u.getTable(right)
if err != nil { if err != nil {
return errReturn(vm, err) return errReturn(vm, err)
} }
...@@ -266,18 +388,9 @@ func tableJoinFunc(vm *otto.Otto) { ...@@ -266,18 +388,9 @@ func tableJoinFunc(vm *otto.Otto) {
return errReturn(vm, err) return errReturn(vm, err)
} }
var id int64 var id int64
for { u.globalHanldeID++
id = atomic.AddInt64(&globalHanldeID, 1) % maxjsint id = u.globalHanldeID
if _, ok := globalTableHandle.Load(id); ok { u.globalTableHandle.Store(id, join)
continue
}
if id < 0 {
atomic.StoreInt64(&globalHanldeID, 0)
continue
}
break
}
globalTableHandle.Store(id, join)
return newObject(vm).setValue("id", id).value() return newObject(vm).setValue("id", id).value()
}) })
} }
...@@ -299,18 +412,25 @@ handle := new_join_table(left, right, listofjoinindex) ...@@ -299,18 +412,25 @@ handle := new_join_table(left, right, listofjoinindex)
//JSONRow meta //JSONRow meta
type JSONRow struct { type JSONRow struct {
*jsproto.JsLog *jsproto.JsLog
config map[string]string config map[string]string
data map[string]interface{} data map[string]interface{}
lastdata types.Message
isint *regexp.Regexp
isfloat *regexp.Regexp
defaultvalue string
} }
//NewJSONRow 创建一个row //NewJSONRow 创建一个row
func NewJSONRow(config string, defaultvalue string) (*JSONRow, error) { func NewJSONRow(config string, defaultvalue string) (*JSONRow, error) {
row := &JSONRow{} row := &JSONRow{}
row.isint = regexp.MustCompile(`%\d*d`)
row.isfloat = regexp.MustCompile(`%[\.\d]*f`)
row.config = make(map[string]string) row.config = make(map[string]string)
err := json.Unmarshal([]byte(config), row.config) err := json.Unmarshal([]byte(config), &row.config)
if err != nil { if err != nil {
return nil, err return nil, err
} }
row.defaultvalue = defaultvalue
row.JsLog = &jsproto.JsLog{Data: defaultvalue} row.JsLog = &jsproto.JsLog{Data: defaultvalue}
err = row.parse() err = row.parse()
if err != nil { if err != nil {
...@@ -321,16 +441,22 @@ func NewJSONRow(config string, defaultvalue string) (*JSONRow, error) { ...@@ -321,16 +441,22 @@ func NewJSONRow(config string, defaultvalue string) (*JSONRow, error) {
//CreateRow 创建一行 //CreateRow 创建一行
func (row *JSONRow) CreateRow() *table.Row { func (row *JSONRow) CreateRow() *table.Row {
return &table.Row{Data: &jsproto.JsLog{}} return &table.Row{Data: &jsproto.JsLog{Data: row.defaultvalue}}
} }
func (row *JSONRow) parse() error { func (row *JSONRow) parse() error {
row.data = make(map[string]interface{}) row.data = make(map[string]interface{})
return json.Unmarshal([]byte(row.JsLog.Data), row.data) d := json.NewDecoder(bytes.NewBufferString(row.JsLog.Data))
d.UseNumber()
return d.Decode(&row.data)
} }
//SetPayload 设置行的内容 //SetPayload 设置行的内容
func (row *JSONRow) SetPayload(data types.Message) error { func (row *JSONRow) SetPayload(data types.Message) error {
if row.lastdata == data {
return nil
}
row.lastdata = data
if rowdata, ok := data.(*jsproto.JsLog); ok { if rowdata, ok := data.(*jsproto.JsLog); ok {
row.JsLog = rowdata row.JsLog = rowdata
return row.parse() return row.parse()
...@@ -340,10 +466,34 @@ func (row *JSONRow) SetPayload(data types.Message) error { ...@@ -340,10 +466,34 @@ func (row *JSONRow) SetPayload(data types.Message) error {
//Get value of row //Get value of row
func (row *JSONRow) Get(key string) ([]byte, error) { func (row *JSONRow) Get(key string) ([]byte, error) {
v, err := row.get(key)
return v, err
}
func (row *JSONRow) get(key string) ([]byte, error) {
if format, ok := row.config[key]; ok { if format, ok := row.config[key]; ok {
if data, ok := row.data[key]; ok { if data, ok := row.data[key]; ok {
if row.isint.Match([]byte(format)) { //int
s := fmt.Sprint(data)
num, err := strconv.ParseInt(s, 10, 64)
if err != nil {
return nil, err
}
return []byte(fmt.Sprintf(format, num)), nil
} else if row.isfloat.Match([]byte(format)) { //float
s := fmt.Sprint(data)
num, err := strconv.ParseFloat(s, 64)
if err != nil {
return nil, err
}
return []byte(fmt.Sprintf(format, num)), nil
} else { //string
if n, ok := data.(json.Number); ok {
data = n.String()
}
}
return []byte(fmt.Sprintf(format, data)), nil return []byte(fmt.Sprintf(format, data)), nil
} }
} }
return nil, types.ErrNotFound return nil, errors.New("get key " + key + "from data err")
} }
//数据结构设计 //数据结构设计
//kvlist [{key:"key1", value:"value1"},{key:"key2", value:"value2"}]
//log 设计 {json data}
function Init(context) { function Init(context) {
this.kvc = new kvcreator("init") this.kvc = new kvcreator("init")
this.context = context this.context = context
...@@ -9,22 +7,6 @@ function Init(context) { ...@@ -9,22 +7,6 @@ function Init(context) {
return this.kvc.receipt() return this.kvc.receipt()
} }
function Exec(context) {
this.kvc = new kvcreator("exec")
this.context = context
}
function ExecLocal(context, logs) {
this.kvc = new kvcreator("local")
this.context = context
this.logs = logs
}
function Query(context) {
this.kvc = new kvcreator("query")
this.context = context
}
Exec.prototype.hello = function(args) { Exec.prototype.hello = function(args) {
this.kvc.add("args", args) this.kvc.add("args", args)
this.kvc.add("action", "exec") this.kvc.add("action", "exec")
......
package unfreeze package js
import ( import (
"github.com/33cn/chain33/pluginmgr" "github.com/33cn/chain33/pluginmgr"
"github.com/33cn/plugin/plugin/dapp/js/cmd"
"github.com/33cn/plugin/plugin/dapp/js/executor" "github.com/33cn/plugin/plugin/dapp/js/executor"
ptypes "github.com/33cn/plugin/plugin/dapp/js/types" ptypes "github.com/33cn/plugin/plugin/dapp/js/types"
) )
...@@ -11,7 +12,7 @@ func init() { ...@@ -11,7 +12,7 @@ func init() {
Name: ptypes.JsX, Name: ptypes.JsX,
ExecName: executor.GetName(), ExecName: executor.GetName(),
Exec: executor.Init, Exec: executor.Init,
Cmd: nil, Cmd: cmd.JavaScriptCmd,
RPC: nil, RPC: nil,
}) })
} }
...@@ -2,4 +2,6 @@ ...@@ -2,4 +2,6 @@
while :; do while :; do
./chain33-cli net time ./chain33-cli net time
#nc -vz localhost 8805
sleep 1
done done
...@@ -189,4 +189,9 @@ superManager=[ ...@@ -189,4 +189,9 @@ superManager=[
"1Bsg9j6gW83sShoee1fZAt9TkUjcrCgA9S", "1Bsg9j6gW83sShoee1fZAt9TkUjcrCgA9S",
"12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv", "12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv",
"1Q8hGLfoGe63efeWa8fJ4Pnukhkngt6poK" "1Q8hGLfoGe63efeWa8fJ4Pnukhkngt6poK"
] ]
\ No newline at end of file
[health]
listenAddr="localhost:8805"
checkInterval=1
unSyncMaxTimes=2
\ No newline at end of file
...@@ -312,6 +312,16 @@ func (table *Table) getPrimaryFromData(data types.Message) (primaryKey []byte, e ...@@ -312,6 +312,16 @@ func (table *Table) getPrimaryFromData(data types.Message) (primaryKey []byte, e
return return
} }
//ListIndex list table index
func (table *Table) ListIndex(indexName string, prefix []byte, primaryKey []byte, count, direction int32) (rows []*Row, err error) {
kvdb, ok := table.kvdb.(db.KVDB)
if !ok {
return nil, errors.New("list only support KVDB interface")
}
query := &Query{table: table, kvdb: kvdb}
return query.ListIndex(indexName, prefix, primaryKey, count, direction)
}
//Replace 如果有重复的,那么替换 //Replace 如果有重复的,那么替换
func (table *Table) Replace(data types.Message) error { func (table *Table) Replace(data types.Message) error {
if err := table.checkIndex(data); err != nil { if err := table.checkIndex(data); err != nil {
...@@ -602,8 +612,15 @@ func (table *Table) getModify(row, oldrow *Row, index string) ([]byte, []byte, b ...@@ -602,8 +612,15 @@ func (table *Table) getModify(row, oldrow *Row, index string) ([]byte, []byte, b
return indexkey, oldkey, true, nil return indexkey, oldkey, true, nil
} }
//GetQuery 获取查询结构 //GetQuery 获取查询结构(允许传入 kvdb 为nil)
func (table *Table) GetQuery(kvdb db.KVDB) *Query { func (table *Table) GetQuery(kvdb db.KVDB) *Query {
if kvdb == nil {
var ok bool
kvdb, ok = table.kvdb.(db.KVDB)
if !ok {
return nil
}
}
return &Query{table: table, kvdb: kvdb} return &Query{table: table, kvdb: kvdb}
} }
...@@ -611,6 +628,11 @@ func (table *Table) getMeta() RowMeta { ...@@ -611,6 +628,11 @@ func (table *Table) getMeta() RowMeta {
return table.meta return table.meta
} }
//GetMeta 获取meta
func (table *Table) GetMeta() RowMeta {
return table.getMeta()
}
func (table *Table) getOpt() *Option { func (table *Table) getOpt() *Option {
return table.opt return table.opt
} }
...@@ -90,64 +90,82 @@ func (store *BaseStore) Wait() {} ...@@ -90,64 +90,82 @@ func (store *BaseStore) Wait() {}
func (store *BaseStore) processMessage(msg queue.Message) { func (store *BaseStore) processMessage(msg queue.Message) {
client := store.qclient client := store.qclient
if msg.Ty == types.EventStoreSet { if msg.Ty == types.EventStoreSet {
datas := msg.GetData().(*types.StoreSetWithSync) go func() {
hash, err := store.child.Set(datas.Storeset, datas.Sync) datas := msg.GetData().(*types.StoreSetWithSync)
if err != nil { hash, err := store.child.Set(datas.Storeset, datas.Sync)
msg.Reply(client.NewMessage("", types.EventStoreSetReply, err)) if err != nil {
return msg.Reply(client.NewMessage("", types.EventStoreSetReply, err))
} return
msg.Reply(client.NewMessage("", types.EventStoreSetReply, &types.ReplyHash{Hash: hash})) }
msg.Reply(client.NewMessage("", types.EventStoreSetReply, &types.ReplyHash{Hash: hash}))
}()
} else if msg.Ty == types.EventStoreGet { } else if msg.Ty == types.EventStoreGet {
datas := msg.GetData().(*types.StoreGet) go func() {
values := store.child.Get(datas) datas := msg.GetData().(*types.StoreGet)
msg.Reply(client.NewMessage("", types.EventStoreGetReply, &types.StoreReplyValue{Values: values})) values := store.child.Get(datas)
msg.Reply(client.NewMessage("", types.EventStoreGetReply, &types.StoreReplyValue{Values: values}))
}()
} else if msg.Ty == types.EventStoreMemSet { //只是在内存中set 一下,并不改变状态 } else if msg.Ty == types.EventStoreMemSet { //只是在内存中set 一下,并不改变状态
datas := msg.GetData().(*types.StoreSetWithSync) go func() {
hash, err := store.child.MemSet(datas.Storeset, datas.Sync) datas := msg.GetData().(*types.StoreSetWithSync)
if err != nil { hash, err := store.child.MemSet(datas.Storeset, datas.Sync)
msg.Reply(client.NewMessage("", types.EventStoreSetReply, err)) println("EventStoreMemSet", string(hash))
return if err != nil {
} msg.Reply(client.NewMessage("", types.EventStoreSetReply, err))
msg.Reply(client.NewMessage("", types.EventStoreSetReply, &types.ReplyHash{Hash: hash})) return
}
msg.Reply(client.NewMessage("", types.EventStoreSetReply, &types.ReplyHash{Hash: hash}))
}()
} else if msg.Ty == types.EventStoreCommit { //把内存中set 的交易 commit } else if msg.Ty == types.EventStoreCommit { //把内存中set 的交易 commit
req := msg.GetData().(*types.ReqHash) go func() {
hash, err := store.child.Commit(req) req := msg.GetData().(*types.ReqHash)
if hash == nil { println("EventStoreCommit", string(req.Hash))
msg.Reply(client.NewMessage("", types.EventStoreCommit, types.ErrHashNotFound)) hash, err := store.child.Commit(req)
if err == types.ErrDataBaseDamage { //如果是数据库写失败,需要上报给用户 if hash == nil {
go util.ReportErrEventToFront(slog, client, "store", "wallet", err) msg.Reply(client.NewMessage("", types.EventStoreCommit, types.ErrHashNotFound))
if err == types.ErrDataBaseDamage { //如果是数据库写失败,需要上报给用户
go util.ReportErrEventToFront(slog, client, "store", "wallet", err)
}
} else {
msg.Reply(client.NewMessage("", types.EventStoreCommit, &types.ReplyHash{Hash: hash}))
} }
} else { }()
msg.Reply(client.NewMessage("", types.EventStoreCommit, &types.ReplyHash{Hash: hash}))
}
} else if msg.Ty == types.EventStoreRollback { } else if msg.Ty == types.EventStoreRollback {
req := msg.GetData().(*types.ReqHash) go func() {
hash, err := store.child.Rollback(req) req := msg.GetData().(*types.ReqHash)
if err != nil { hash, err := store.child.Rollback(req)
msg.Reply(client.NewMessage("", types.EventStoreRollback, types.ErrHashNotFound)) if err != nil {
} else { msg.Reply(client.NewMessage("", types.EventStoreRollback, types.ErrHashNotFound))
msg.Reply(client.NewMessage("", types.EventStoreRollback, &types.ReplyHash{Hash: hash})) } else {
} msg.Reply(client.NewMessage("", types.EventStoreRollback, &types.ReplyHash{Hash: hash}))
}
}()
} else if msg.Ty == types.EventStoreGetTotalCoins { } else if msg.Ty == types.EventStoreGetTotalCoins {
req := msg.GetData().(*types.IterateRangeByStateHash) go func() {
resp := &types.ReplyGetTotalCoins{} req := msg.GetData().(*types.IterateRangeByStateHash)
resp.Count = req.Count resp := &types.ReplyGetTotalCoins{}
store.child.IterateRangeByStateHash(req.StateHash, req.Start, req.End, true, resp.IterateRangeByStateHash) resp.Count = req.Count
msg.Reply(client.NewMessage("", types.EventGetTotalCoinsReply, resp)) store.child.IterateRangeByStateHash(req.StateHash, req.Start, req.End, true, resp.IterateRangeByStateHash)
msg.Reply(client.NewMessage("", types.EventGetTotalCoinsReply, resp))
}()
} else if msg.Ty == types.EventStoreDel { } else if msg.Ty == types.EventStoreDel {
req := msg.GetData().(*types.StoreDel) go func() {
hash, err := store.child.Del(req) req := msg.GetData().(*types.StoreDel)
if err != nil { hash, err := store.child.Del(req)
msg.Reply(client.NewMessage("", types.EventStoreDel, types.ErrHashNotFound)) if err != nil {
} else { msg.Reply(client.NewMessage("", types.EventStoreDel, types.ErrHashNotFound))
msg.Reply(client.NewMessage("", types.EventStoreDel, &types.ReplyHash{Hash: hash})) } else {
} msg.Reply(client.NewMessage("", types.EventStoreDel, &types.ReplyHash{Hash: hash}))
}
}()
} else if msg.Ty == types.EventStoreList { } else if msg.Ty == types.EventStoreList {
req := msg.GetData().(*types.StoreList) go func() {
query := NewStoreListQuery(store.child, req) req := msg.GetData().(*types.StoreList)
msg.Reply(client.NewMessage("", types.EventStoreListReply, query.Run())) query := NewStoreListQuery(store.child, req)
msg.Reply(client.NewMessage("", types.EventStoreListReply, query.Run()))
}()
} else { } else {
store.child.ProcEvent(msg) go store.child.ProcEvent(msg)
} }
} }
......
...@@ -45,9 +45,8 @@ func EnableMVCC(enable bool) { ...@@ -45,9 +45,8 @@ func EnableMVCC(enable bool) {
// Tree merkle avl tree // Tree merkle avl tree
type Tree struct { type Tree struct {
root *Node root *Node
ndb *nodeDB ndb *nodeDB
//batch *nodeBatch
blockHeight int64 blockHeight int64
} }
...@@ -135,7 +134,9 @@ func (t *Tree) Save() []byte { ...@@ -135,7 +134,9 @@ func (t *Tree) Save() []byte {
if t.ndb != nil { if t.ndb != nil {
saveNodeNo := t.root.save(t) saveNodeNo := t.root.save(t)
treelog.Debug("Tree.Save", "saveNodeNo", saveNodeNo, "tree height", t.blockHeight) treelog.Debug("Tree.Save", "saveNodeNo", saveNodeNo, "tree height", t.blockHeight)
beg := types.Now()
err := t.ndb.Commit() err := t.ndb.Commit()
treelog.Info("tree.commit", "cost", types.Since(beg))
if err != nil { if err != nil {
return nil return nil
} }
...@@ -268,10 +269,6 @@ type nodeDB struct { ...@@ -268,10 +269,6 @@ type nodeDB struct {
orphans map[string]struct{} orphans map[string]struct{}
} }
type nodeBatch struct {
batch dbm.Batch
}
func newNodeDB(db dbm.DB, sync bool) *nodeDB { func newNodeDB(db dbm.DB, sync bool) *nodeDB {
ndb := &nodeDB{ ndb := &nodeDB{
cache: db.GetCache(), cache: db.GetCache(),
...@@ -312,11 +309,6 @@ func (ndb *nodeDB) GetNode(t *Tree, hash []byte) (*Node, error) { ...@@ -312,11 +309,6 @@ func (ndb *nodeDB) GetNode(t *Tree, hash []byte) (*Node, error) {
return node, nil return node, nil
} }
// GetBatch get db batch handle
func (ndb *nodeDB) GetBatch(sync bool) *nodeBatch {
return &nodeBatch{ndb.db.NewBatch(sync)}
}
// 获取叶子节点的所有父节点 // 获取叶子节点的所有父节点
func getHashNode(node *Node) (hashs [][]byte) { func getHashNode(node *Node) (hashs [][]byte) {
for { for {
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
package mavl package mavl
import ( import (
"sync"
"github.com/33cn/chain33/common" "github.com/33cn/chain33/common"
clog "github.com/33cn/chain33/common/log" clog "github.com/33cn/chain33/common/log"
log "github.com/33cn/chain33/common/log/log15" log "github.com/33cn/chain33/common/log/log15"
...@@ -13,7 +15,6 @@ import ( ...@@ -13,7 +15,6 @@ import (
drivers "github.com/33cn/chain33/system/store" drivers "github.com/33cn/chain33/system/store"
mavl "github.com/33cn/chain33/system/store/mavl/db" mavl "github.com/33cn/chain33/system/store/mavl/db"
"github.com/33cn/chain33/types" "github.com/33cn/chain33/types"
lru "github.com/hashicorp/golang-lru"
) )
var mlog = log.New("module", "mavl") var mlog = log.New("module", "mavl")
...@@ -31,8 +32,7 @@ func DisableLog() { ...@@ -31,8 +32,7 @@ func DisableLog() {
// Store mavl store struct // Store mavl store struct
type Store struct { type Store struct {
*drivers.BaseStore *drivers.BaseStore
trees map[string]*mavl.Tree trees *sync.Map
cache *lru.Cache
enableMavlPrefix bool enableMavlPrefix bool
enableMVCC bool enableMVCC bool
enableMavlPrune bool enableMavlPrune bool
...@@ -57,10 +57,7 @@ func New(cfg *types.Store, sub []byte) queue.Module { ...@@ -57,10 +57,7 @@ func New(cfg *types.Store, sub []byte) queue.Module {
if sub != nil { if sub != nil {
types.MustDecode(sub, &subcfg) types.MustDecode(sub, &subcfg)
} }
mavls := &Store{bs, make(map[string]*mavl.Tree), nil, subcfg.EnableMavlPrefix, subcfg.EnableMVCC, subcfg.EnableMavlPrune, subcfg.PruneHeight} mavls := &Store{bs, &sync.Map{}, subcfg.EnableMavlPrefix, subcfg.EnableMVCC, subcfg.EnableMavlPrune, subcfg.PruneHeight}
mavls.cache, _ = lru.New(10)
//使能前缀mavl以及MVCC
mavls.enableMavlPrefix = subcfg.EnableMavlPrefix mavls.enableMavlPrefix = subcfg.EnableMavlPrefix
mavls.enableMVCC = subcfg.EnableMVCC mavls.enableMVCC = subcfg.EnableMVCC
mavls.enableMavlPrune = subcfg.EnableMavlPrune mavls.enableMavlPrune = subcfg.EnableMavlPrune
...@@ -91,18 +88,13 @@ func (mavls *Store) Get(datas *types.StoreGet) [][]byte { ...@@ -91,18 +88,13 @@ func (mavls *Store) Get(datas *types.StoreGet) [][]byte {
var err error var err error
values := make([][]byte, len(datas.Keys)) values := make([][]byte, len(datas.Keys))
search := string(datas.StateHash) search := string(datas.StateHash)
if data, ok := mavls.cache.Get(search); ok { if data, ok := mavls.trees.Load(search); ok {
tree = data.(*mavl.Tree) tree = data.(*mavl.Tree)
} else if data, ok := mavls.trees[search]; ok {
tree = data
} else { } else {
tree = mavl.NewTree(mavls.GetDB(), true) tree = mavl.NewTree(mavls.GetDB(), true)
//get接口也应该传入高度 //get接口也应该传入高度
//tree.SetBlockHeight(datas.Height) //tree.SetBlockHeight(datas.Height)
err = tree.Load(datas.StateHash) err = tree.Load(datas.StateHash)
if err == nil {
mavls.cache.Add(search, tree)
}
mlog.Debug("store mavl get tree", "err", err, "StateHash", common.ToHex(datas.StateHash)) mlog.Debug("store mavl get tree", "err", err, "StateHash", common.ToHex(datas.StateHash))
} }
if err == nil { if err == nil {
...@@ -118,9 +110,13 @@ func (mavls *Store) Get(datas *types.StoreGet) [][]byte { ...@@ -118,9 +110,13 @@ func (mavls *Store) Get(datas *types.StoreGet) [][]byte {
// MemSet set keys values to memcory mavl, return root hash and error // MemSet set keys values to memcory mavl, return root hash and error
func (mavls *Store) MemSet(datas *types.StoreSet, sync bool) ([]byte, error) { func (mavls *Store) MemSet(datas *types.StoreSet, sync bool) ([]byte, error) {
beg := types.Now()
defer func() {
mlog.Info("MemSet", "cost", types.Since(beg))
}()
if len(datas.KV) == 0 { if len(datas.KV) == 0 {
mlog.Info("store mavl memset,use preStateHash as stateHash for kvset is null") mlog.Info("store mavl memset,use preStateHash as stateHash for kvset is null")
mavls.trees[string(datas.StateHash)] = nil mavls.trees.Store(string(datas.StateHash), nil)
return datas.StateHash, nil return datas.StateHash, nil
} }
tree := mavl.NewTree(mavls.GetDB(), sync) tree := mavl.NewTree(mavls.GetDB(), sync)
...@@ -133,44 +129,47 @@ func (mavls *Store) MemSet(datas *types.StoreSet, sync bool) ([]byte, error) { ...@@ -133,44 +129,47 @@ func (mavls *Store) MemSet(datas *types.StoreSet, sync bool) ([]byte, error) {
tree.Set(datas.KV[i].Key, datas.KV[i].Value) tree.Set(datas.KV[i].Key, datas.KV[i].Value)
} }
hash := tree.Hash() hash := tree.Hash()
mavls.trees[string(hash)] = tree mavls.trees.Store(string(hash), tree)
if len(mavls.trees) > 1000 {
mlog.Error("too many trees in cache")
}
return hash, nil return hash, nil
} }
// Commit convert memcory mavl to storage db // Commit convert memcory mavl to storage db
func (mavls *Store) Commit(req *types.ReqHash) ([]byte, error) { func (mavls *Store) Commit(req *types.ReqHash) ([]byte, error) {
tree, ok := mavls.trees[string(req.Hash)] beg := types.Now()
defer func() {
mlog.Info("Commit", "cost", types.Since(beg))
}()
tree, ok := mavls.trees.Load(string(req.Hash))
if !ok { if !ok {
mlog.Error("store mavl commit", "err", types.ErrHashNotFound) mlog.Error("store mavl commit", "err", types.ErrHashNotFound)
return nil, types.ErrHashNotFound return nil, types.ErrHashNotFound
} }
if tree == nil { if tree == nil {
mlog.Info("store mavl commit,do nothing for kvset is null") mlog.Info("store mavl commit,do nothing for kvset is null")
delete(mavls.trees, string(req.Hash)) mavls.trees.Delete(string(req.Hash))
return req.Hash, nil return req.Hash, nil
} }
hash := tree.(*mavl.Tree).Save()
hash := tree.Save()
if hash == nil { if hash == nil {
mlog.Error("store mavl commit", "err", types.ErrHashNotFound) mlog.Error("store mavl commit", "err", types.ErrHashNotFound)
return nil, types.ErrDataBaseDamage return nil, types.ErrDataBaseDamage
} }
delete(mavls.trees, string(req.Hash)) mavls.trees.Delete(string(req.Hash))
return req.Hash, nil return req.Hash, nil
} }
// Rollback 回退将缓存的mavl树删除掉 // Rollback 回退将缓存的mavl树删除掉
func (mavls *Store) Rollback(req *types.ReqHash) ([]byte, error) { func (mavls *Store) Rollback(req *types.ReqHash) ([]byte, error) {
_, ok := mavls.trees[string(req.Hash)] beg := types.Now()
defer func() {
mlog.Info("Rollback", "cost", types.Since(beg))
}()
_, ok := mavls.trees.Load(string(req.Hash))
if !ok { if !ok {
mlog.Error("store mavl rollback", "err", types.ErrHashNotFound) mlog.Error("store mavl rollback", "err", types.ErrHashNotFound)
return nil, types.ErrHashNotFound return nil, types.ErrHashNotFound
} }
delete(mavls.trees, string(req.Hash)) mavls.trees.Delete(string(req.Hash))
return req.Hash, nil return req.Hash, nil
} }
......
...@@ -6,21 +6,22 @@ package types ...@@ -6,21 +6,22 @@ package types
// Config 配置信息 // Config 配置信息
type Config struct { type Config struct {
Title string `protobuf:"bytes,1,opt,name=title" json:"title,omitempty"` Title string `protobuf:"bytes,1,opt,name=title" json:"title,omitempty"`
Version string `protobuf:"bytes,1,opt,name=version" json:"version,omitempty"` Version string `protobuf:"bytes,1,opt,name=version" json:"version,omitempty"`
Log *Log `protobuf:"bytes,2,opt,name=log" json:"log,omitempty"` Log *Log `protobuf:"bytes,2,opt,name=log" json:"log,omitempty"`
Store *Store `protobuf:"bytes,3,opt,name=store" json:"store,omitempty"` Store *Store `protobuf:"bytes,3,opt,name=store" json:"store,omitempty"`
Consensus *Consensus `protobuf:"bytes,5,opt,name=consensus" json:"consensus,omitempty"` Consensus *Consensus `protobuf:"bytes,5,opt,name=consensus" json:"consensus,omitempty"`
Mempool *Mempool `protobuf:"bytes,6,opt,name=mempool" json:"memPool,omitempty"` Mempool *Mempool `protobuf:"bytes,6,opt,name=mempool" json:"memPool,omitempty"`
BlockChain *BlockChain `protobuf:"bytes,7,opt,name=blockChain" json:"blockChain,omitempty"` BlockChain *BlockChain `protobuf:"bytes,7,opt,name=blockChain" json:"blockChain,omitempty"`
Wallet *Wallet `protobuf:"bytes,8,opt,name=wallet" json:"wallet,omitempty"` Wallet *Wallet `protobuf:"bytes,8,opt,name=wallet" json:"wallet,omitempty"`
P2P *P2P `protobuf:"bytes,9,opt,name=p2p" json:"p2p,omitempty"` P2P *P2P `protobuf:"bytes,9,opt,name=p2p" json:"p2p,omitempty"`
RPC *RPC `protobuf:"bytes,10,opt,name=rpc" json:"rpc,omitempty"` RPC *RPC `protobuf:"bytes,10,opt,name=rpc" json:"rpc,omitempty"`
Exec *Exec `protobuf:"bytes,11,opt,name=exec" json:"exec,omitempty"` Exec *Exec `protobuf:"bytes,11,opt,name=exec" json:"exec,omitempty"`
TestNet bool `protobuf:"varint,12,opt,name=testNet" json:"testNet,omitempty"` TestNet bool `protobuf:"varint,12,opt,name=testNet" json:"testNet,omitempty"`
FixTime bool `protobuf:"varint,13,opt,name=fixTime" json:"fixTime,omitempty"` FixTime bool `protobuf:"varint,13,opt,name=fixTime" json:"fixTime,omitempty"`
Pprof *Pprof `protobuf:"bytes,14,opt,name=pprof" json:"pprof,omitempty"` Pprof *Pprof `protobuf:"bytes,14,opt,name=pprof" json:"pprof,omitempty"`
Fork *ForkList `protobuf:"bytes,15,opt,name=fork" json:"fork,omitempty"` Fork *ForkList `protobuf:"bytes,15,opt,name=fork" json:"fork,omitempty"`
Health *HealthCheck `protobuf:"bytes,16,opt,name=health" json:"health,omitempty"`
} }
// ForkList fork列表配置 // ForkList fork列表配置
...@@ -165,3 +166,10 @@ type Exec struct { ...@@ -165,3 +166,10 @@ type Exec struct {
type Pprof struct { type Pprof struct {
ListenAddr string `protobuf:"bytes,1,opt,name=listenAddr" json:"listenAddr,omitempty"` ListenAddr string `protobuf:"bytes,1,opt,name=listenAddr" json:"listenAddr,omitempty"`
} }
// HealthCheck 配置
type HealthCheck struct {
ListenAddr string `protobuf:"bytes,1,opt,name=listenAddr" json:"listenAddr,omitempty"`
CheckInterval uint32 `protobuf:"varint,2,opt,name=checkInterval" json:"checkInterval,omitempty"`
UnSyncMaxTimes uint32 `protobuf:"varint,3,opt,name=unSyncMaxTimes" json:"unSyncMaxTimes,omitempty"`
}
...@@ -171,6 +171,9 @@ func RunChain33(name string) { ...@@ -171,6 +171,9 @@ func RunChain33(name string) {
log.Info("loading wallet module") log.Info("loading wallet module")
walletm := wallet.New(cfg.Wallet, sub.Wallet) walletm := wallet.New(cfg.Wallet, sub.Wallet)
walletm.SetQueueClient(q.Client()) walletm.SetQueueClient(q.Client())
health := util.NewHealthCheckServer(q.Client())
health.Start(cfg.Health)
defer func() { defer func() {
//close all module,clean some resource //close all module,clean some resource
log.Info("begin close blockchain module") log.Info("begin close blockchain module")
...@@ -189,6 +192,8 @@ func RunChain33(name string) { ...@@ -189,6 +192,8 @@ func RunChain33(name string) {
rpcapi.Close() rpcapi.Close()
log.Info("begin close wallet module") log.Info("begin close wallet module")
walletm.Close() walletm.Close()
log.Info("begin close health module")
health.Close()
log.Info("begin close queue module") log.Info("begin close queue module")
q.Close() q.Close()
......
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package util
import (
"net"
"time"
"sync"
"github.com/33cn/chain33/client"
log "github.com/33cn/chain33/common/log/log15"
"github.com/33cn/chain33/queue"
"github.com/33cn/chain33/types"
)
var (
listenAddr = "localhost:8805"
unSyncMaxTimes uint32 = 6 //max 6 times
checkInterval uint32 = 5 // 5s
)
// HealthCheckServer a node's health check server
type HealthCheckServer struct {
api client.QueueProtocolAPI
l net.Listener
quit chan struct{}
wg sync.WaitGroup
}
// Close NewHealthCheckServer close
func (s *HealthCheckServer) Close() {
close(s.quit)
s.wg.Wait()
log.Info("healthCheck quit")
}
// NewHealthCheckServer new json rpcserver object
func NewHealthCheckServer(c queue.Client) *HealthCheckServer {
h := &HealthCheckServer{}
h.api, _ = client.New(c, nil)
h.quit = make(chan struct{})
return h
}
// Start HealthCheckServer start
func (s *HealthCheckServer) Start(cfg *types.HealthCheck) {
if cfg != nil {
if cfg.ListenAddr != "" {
listenAddr = cfg.ListenAddr
}
if cfg.CheckInterval != 0 {
checkInterval = cfg.CheckInterval
}
if cfg.UnSyncMaxTimes != 0 {
unSyncMaxTimes = cfg.UnSyncMaxTimes
}
}
log.Info("healthCheck start ", "addr", listenAddr, "inter", checkInterval, "times", unSyncMaxTimes)
s.wg.Add(1)
go s.healthCheck()
}
func (s *HealthCheckServer) listen(on bool) error {
if on {
listener, err := net.Listen("tcp", listenAddr)
if err != nil {
return err
}
s.l = listener
log.Info("healthCheck listen open")
return nil
}
if s.l != nil {
err := s.l.Close()
if err != nil {
return err
}
log.Info("healthCheck listen close")
s.l = nil
}
return nil
}
func (s *HealthCheckServer) getHealth(sync bool) (bool, error) {
reply, err := s.api.IsSync()
if err != nil {
return false, err
}
peerList, err := s.api.PeerInfo()
if err != nil {
return false, err
}
log.Info("healthCheck tick", "peers", len(peerList.Peers), "isSync", reply.IsOk, "sync", sync)
return len(peerList.Peers) > 1 && reply.IsOk, nil
}
func (s *HealthCheckServer) healthCheck() {
ticker := time.NewTicker(time.Second * time.Duration(checkInterval))
defer ticker.Stop()
defer s.wg.Done()
var sync bool
var unSyncTimes uint32
for {
select {
case <-s.quit:
if s.l != nil {
s.l.Close()
}
if s.api != nil {
s.api.Close()
}
return
case <-ticker.C:
health, err := s.getHealth(sync)
if err != nil {
continue
}
//sync
if health {
if !sync {
err = s.listen(true)
if err != nil {
log.Error("healthCheck ", "listen open err", err.Error())
continue
}
sync = true
}
unSyncTimes = 0
} else {
if sync {
if unSyncTimes >= unSyncMaxTimes {
err = s.listen(false)
if err != nil {
log.Error("healthCheck ", "listen close err", err.Error())
continue
}
sync = false
}
unSyncTimes++
}
}
}
}
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package util
import (
"testing"
"time"
"github.com/33cn/chain33/client/mocks"
"github.com/33cn/chain33/queue"
"github.com/33cn/chain33/types"
"github.com/stretchr/testify/assert"
)
func TestStart(t *testing.T) {
q := queue.New("channel")
health := NewHealthCheckServer(q.Client())
api := new(mocks.QueueProtocolAPI)
reply := &types.Reply{IsOk: true}
api.On("IsSync").Return(reply, nil)
peer1 := &types.Peer{Addr: "addr1"}
peer2 := &types.Peer{Addr: "addr2"}
peers := &types.PeerList{Peers: []*types.Peer{peer1, peer2}}
api.On("PeerInfo").Return(peers, nil)
api.On("Close").Return()
health.api = api
cfg, _ := types.InitCfg("../cmd/chain33/chain33.test.toml")
health.Start(cfg.Health)
time.Sleep(time.Second * 3)
health.Close()
time.Sleep(time.Second * 1)
}
func TestGetHealth(t *testing.T) {
api := new(mocks.QueueProtocolAPI)
reply := &types.Reply{IsOk: true}
api.On("IsSync").Return(reply, nil).Once()
peer2 := &types.Peer{Addr: "addr2"}
peerlist := &types.PeerList{Peers: []*types.Peer{peer2}}
api.On("PeerInfo").Return(peerlist, nil).Once()
q := queue.New("channel")
health := NewHealthCheckServer(q.Client())
health.api = api
ret, err := health.getHealth(true)
assert.Nil(t, err)
assert.Equal(t, false, ret)
}
...@@ -205,6 +205,27 @@ func (mock *Chain33Mock) SendAndSign(priv crypto.PrivKey, hextx string) ([]byte, ...@@ -205,6 +205,27 @@ func (mock *Chain33Mock) SendAndSign(priv crypto.PrivKey, hextx string) ([]byte,
return reply.GetMsg(), nil return reply.GetMsg(), nil
} }
//SendAndSignNonce 用外部传入的nonce 重写nonce
func (mock *Chain33Mock) SendAndSignNonce(priv crypto.PrivKey, hextx string, nonce int64) ([]byte, error) {
txbytes, err := hex.DecodeString(hextx)
if err != nil {
return nil, err
}
tx := &types.Transaction{}
err = types.Decode(txbytes, tx)
if err != nil {
return nil, err
}
tx.Nonce = nonce
tx.Fee = 1e6
tx.Sign(types.SECP256K1, priv)
reply, err := mock.api.SendTx(tx)
if err != nil {
return nil, err
}
return reply.GetMsg(), nil
}
func newWalletRealize(qAPI client.QueueProtocolAPI) { func newWalletRealize(qAPI client.QueueProtocolAPI) {
seed := &types.SaveSeedByPw{ seed := &types.SaveSeedByPw{
Seed: "subject hamster apple parent vital can adult chapter fork business humor pen tiger void elephant", Seed: "subject hamster apple parent vital can adult chapter fork business humor pen tiger void elephant",
......
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