Unverified Commit 8030c504 authored by 33cn's avatar 33cn Committed by GitHub

Merge pull request #198 from vipwzw/js

Js(增加table 和 account的支持)
parents 86b5aa01 52221b3b
<script src="runtime.js"></script>
<script src="test.js"></script>
<script>
//demo database function
var statedb = {}
var localdb = {}
function getlocaldb(key) {
return localdb[key]
}
function setlocaldb(kvs) {
for (var i = 0; i < kvs.length; i++) {
localdb[kvs[i].key] = kvs[i].value
}
}
function listdb(prefix, key, count, direction) {
var i = 0
var data = []
for (k in localdb) {
if (k.startsWith(prefix) && typeof localdb[k] == "string") {
i++
data.push({key: k, value: localdb[k]})
if (i == count) {
break
}
}
}
return data
}
function getstatedb(key) {
return statedb[key]
}
function setstatedb(kvs) {
for (var i = 0; i < kvs.length; i++) {
statedb[kvs[i].key] = kvs[i].value
}
}
var ret = callcode("{}", "execlocal_hello", "{}", [])
console.log(ret)
</script>
\ No newline at end of file
all:
./gen.sh
\ No newline at end of file
package executor
import (
"github.com/33cn/chain33/account"
"github.com/33cn/chain33/common/address"
"github.com/33cn/chain33/types"
"github.com/robertkrimen/otto"
)
/*
//chain33 相关的账户操作函数 (操作某个execer 下面的 symbol)
function Account(execer, symbol) {
this.execer = execer
this.symbol = symbol
}
func Receipt(kvs, logs) {
this.kvs = kvs
this.logs = logs
}
var obj = new Account(execer, symbol)
//init 函数才能使用的两个函数(或者增发)
genesis_init(obj, addr string, amount int64)
genesis_init_exec(obj, execer, addr string, amount int64)
load_account(obj, addr) Account
get_balance(obj, execer, addr) Account
transfer(obj, from, to, amount) Receipt
transfer_to_exec(obj, execer, addr, amount) Receipt
withdraw(obj, execer, addr, amount) Receipt
exec_frozen(obj, execer, addr, amount) Receipt
exec_active(obj, execer, addr, amount) Receipt
exec_deposit(obj, execer, addr, amount) Receipt
exec_withdraw(obj, execer, addr, amount) Receipt
exec_transfer(obj, execer, from, to, amount) Receipt
*/
func (u *js) registerAccountFunc(vm *otto.Otto) {
u.genesisInitExecFunc(vm)
u.genesisInitFunc(vm)
u.loadAccountFunc(vm)
u.getBalanceFunc(vm)
u.transferFunc(vm)
u.transferToExecFunc(vm)
u.withdrawFunc(vm)
u.execActiveFunc(vm)
u.execDepositFunc(vm)
u.execFrozenFunc(vm)
u.execTransferFunc(vm)
u.execWithdrawFunc(vm)
}
func (u *js) getAccount(args otto.Value) (*account.DB, error) {
if !args.IsObject() {
return nil, types.ErrInvalidParam
}
obj := args.Object()
execer, err := getString(obj, "execer")
if err != nil {
return nil, err
}
symbol, err := getString(obj, "symbol")
if err != nil {
return nil, err
}
return account.NewAccountDB(execer, symbol, u.GetStateDB())
}
func (u *js) genesisInitExecFunc(vm *otto.Otto) {
vm.Set("genesis_init_exec", func(call otto.FunctionCall) otto.Value {
acc, err := u.getAccount(call.Argument(0))
if err != nil {
return errReturn(vm, err)
}
execer, err := call.Argument(1).ToString()
if err != nil {
return errReturn(vm, err)
}
addr, err := call.Argument(2).ToString()
if err != nil {
return errReturn(vm, err)
}
if err := address.CheckAddress(addr); err != nil {
return errReturn(vm, err)
}
amount, err := call.Argument(3).ToInteger()
if err != nil {
return errReturn(vm, err)
}
receipt, err := acc.GenesisInitExec(addr, amount, address.ExecAddress(execer))
if err != nil {
return errReturn(vm, err)
}
return receiptReturn(vm, receipt)
})
}
func (u *js) genesisInitFunc(vm *otto.Otto) {
vm.Set("genesis_init", func(call otto.FunctionCall) otto.Value {
acc, err := u.getAccount(call.Argument(0))
if err != nil {
return errReturn(vm, err)
}
addr, err := call.Argument(1).ToString()
if err != nil {
return errReturn(vm, err)
}
if err := address.CheckAddress(addr); err != nil {
return errReturn(vm, err)
}
amount, err := call.Argument(2).ToInteger()
if err != nil {
return errReturn(vm, err)
}
receipt, err := acc.GenesisInit(addr, amount)
if err != nil {
return errReturn(vm, err)
}
return receiptReturn(vm, receipt)
})
}
func (u *js) loadAccountFunc(vm *otto.Otto) {
vm.Set("load_account", func(call otto.FunctionCall) otto.Value {
acc, err := u.getAccount(call.Argument(0))
if err != nil {
return errReturn(vm, err)
}
addr, err := call.Argument(1).ToString()
if err != nil {
return errReturn(vm, err)
}
if err := address.CheckAddress(addr); err != nil {
return errReturn(vm, err)
}
account := acc.LoadAccount(addr)
return accountReturn(vm, account)
})
}
func (u *js) getBalanceFunc(vm *otto.Otto) {
vm.Set("get_balance", func(call otto.FunctionCall) otto.Value {
acc, err := u.getAccount(call.Argument(0))
if err != nil {
return errReturn(vm, err)
}
execer, err := call.Argument(1).ToString()
if err != nil {
return errReturn(vm, err)
}
addr, err := call.Argument(2).ToString()
if err != nil {
return errReturn(vm, err)
}
if err := address.CheckAddress(addr); err != nil {
return errReturn(vm, err)
}
account, err := acc.GetBalance(u.GetAPI(), &types.ReqBalance{
Addresses: []string{addr},
Execer: execer,
})
if err != nil {
return errReturn(vm, err)
}
if len(account) == 0 {
return accountReturn(vm, &types.Account{})
}
return accountReturn(vm, account[0])
})
}
func (u *js) transferFunc(vm *otto.Otto) {
vm.Set("transfer", func(call otto.FunctionCall) otto.Value {
acc, err := u.getAccount(call.Argument(0))
if err != nil {
return errReturn(vm, err)
}
from, err := call.Argument(1).ToString()
if err != nil {
return errReturn(vm, err)
}
if err := address.CheckAddress(from); err != nil {
return errReturn(vm, err)
}
to, err := call.Argument(2).ToString()
if err != nil {
return errReturn(vm, err)
}
if err := address.CheckAddress(to); err != nil {
return errReturn(vm, err)
}
amount, err := call.Argument(3).ToInteger()
if err != nil {
return errReturn(vm, err)
}
receipt, err := acc.Transfer(from, to, amount)
if err != nil {
return errReturn(vm, err)
}
return receiptReturn(vm, receipt)
})
}
func (u *js) transferToExecFunc(vm *otto.Otto) {
vm.Set("transfer_to_exec", func(call otto.FunctionCall) otto.Value {
acc, err := u.getAccount(call.Argument(0))
if err != nil {
return errReturn(vm, err)
}
execer, err := call.Argument(1).ToString()
if err != nil {
return errReturn(vm, err)
}
from, err := call.Argument(2).ToString()
if err != nil {
return errReturn(vm, err)
}
if err := address.CheckAddress(from); err != nil {
return errReturn(vm, err)
}
amount, err := call.Argument(3).ToInteger()
if err != nil {
return errReturn(vm, err)
}
receipt, err := acc.TransferToExec(from, address.ExecAddress(execer), amount)
if err != nil {
return errReturn(vm, err)
}
return receiptReturn(vm, receipt)
})
}
func (u *js) withdrawFunc(vm *otto.Otto) {
vm.Set("withdraw", func(call otto.FunctionCall) otto.Value {
acc, err := u.getAccount(call.Argument(0))
if err != nil {
return errReturn(vm, err)
}
execer, err := call.Argument(1).ToString()
if err != nil {
return errReturn(vm, err)
}
addr, err := call.Argument(2).ToString()
if err != nil {
return errReturn(vm, err)
}
if err := address.CheckAddress(addr); err != nil {
return errReturn(vm, err)
}
amount, err := call.Argument(3).ToInteger()
if err != nil {
return errReturn(vm, err)
}
receipt, err := acc.TransferWithdraw(address.ExecAddress(execer), addr, amount)
if err != nil {
return errReturn(vm, err)
}
return receiptReturn(vm, receipt)
})
}
func (u *js) execFrozenFunc(vm *otto.Otto) {
vm.Set("exec_frozen", func(call otto.FunctionCall) otto.Value {
acc, err := u.getAccount(call.Argument(0))
if err != nil {
return errReturn(vm, err)
}
execer, err := call.Argument(1).ToString()
if err != nil {
return errReturn(vm, err)
}
addr, err := call.Argument(2).ToString()
if err != nil {
return errReturn(vm, err)
}
if err := address.CheckAddress(addr); err != nil {
return errReturn(vm, err)
}
amount, err := call.Argument(3).ToInteger()
if err != nil {
return errReturn(vm, err)
}
receipt, err := acc.ExecFrozen(address.ExecAddress(execer), addr, amount)
if err != nil {
return errReturn(vm, err)
}
return receiptReturn(vm, receipt)
})
}
func (u *js) execActiveFunc(vm *otto.Otto) {
vm.Set("exec_active", func(call otto.FunctionCall) otto.Value {
acc, err := u.getAccount(call.Argument(0))
if err != nil {
return errReturn(vm, err)
}
execer, err := call.Argument(1).ToString()
if err != nil {
return errReturn(vm, err)
}
addr, err := call.Argument(2).ToString()
if err != nil {
return errReturn(vm, err)
}
if err := address.CheckAddress(addr); err != nil {
return errReturn(vm, err)
}
amount, err := call.Argument(3).ToInteger()
if err != nil {
return errReturn(vm, err)
}
receipt, err := acc.ExecActive(address.ExecAddress(execer), addr, amount)
if err != nil {
return errReturn(vm, err)
}
return receiptReturn(vm, receipt)
})
}
func (u *js) execDepositFunc(vm *otto.Otto) {
vm.Set("exec_deposit", func(call otto.FunctionCall) otto.Value {
acc, err := u.getAccount(call.Argument(0))
if err != nil {
return errReturn(vm, err)
}
execer, err := call.Argument(1).ToString()
if err != nil {
return errReturn(vm, err)
}
addr, err := call.Argument(2).ToString()
if err != nil {
return errReturn(vm, err)
}
if err := address.CheckAddress(addr); err != nil {
return errReturn(vm, err)
}
amount, err := call.Argument(3).ToInteger()
if err != nil {
return errReturn(vm, err)
}
receipt, err := acc.ExecDeposit(address.ExecAddress(execer), addr, amount)
if err != nil {
return errReturn(vm, err)
}
return receiptReturn(vm, receipt)
})
}
func (u *js) execWithdrawFunc(vm *otto.Otto) {
vm.Set("exec_withdraw", func(call otto.FunctionCall) otto.Value {
acc, err := u.getAccount(call.Argument(0))
if err != nil {
return errReturn(vm, err)
}
execer, err := call.Argument(1).ToString()
if err != nil {
return errReturn(vm, err)
}
addr, err := call.Argument(2).ToString()
if err != nil {
return errReturn(vm, err)
}
if err := address.CheckAddress(addr); err != nil {
return errReturn(vm, err)
}
amount, err := call.Argument(3).ToInteger()
if err != nil {
return errReturn(vm, err)
}
receipt, err := acc.ExecWithdraw(address.ExecAddress(execer), addr, amount)
if err != nil {
return errReturn(vm, err)
}
return receiptReturn(vm, receipt)
})
}
func (u *js) execTransferFunc(vm *otto.Otto) {
vm.Set("exec_transfer", func(call otto.FunctionCall) otto.Value {
acc, err := u.getAccount(call.Argument(0))
if err != nil {
return errReturn(vm, err)
}
execer, err := call.Argument(1).ToString()
if err != nil {
return errReturn(vm, err)
}
from, err := call.Argument(2).ToString()
if err != nil {
return errReturn(vm, err)
}
if err := address.CheckAddress(from); err != nil {
return errReturn(vm, err)
}
to, err := call.Argument(3).ToString()
if err != nil {
return errReturn(vm, err)
}
if err := address.CheckAddress(to); err != nil {
return errReturn(vm, err)
}
amount, err := call.Argument(4).ToInteger()
if err != nil {
return errReturn(vm, err)
}
receipt, err := acc.ExecTransfer(from, to, address.ExecAddress(execer), amount)
if err != nil {
return errReturn(vm, err)
}
return receiptReturn(vm, receipt)
})
}
package executor
//系统内置代码
var callcode = `
var tojson = JSON.stringify
//table warp
function table(kvc, config, defaultvalue) {
var ret = table_new(config, defaultvalue)
if (ret.err) {
throw new Error(ret.err)
}
this.kvc = kvc
this.id = ret.id
this.config = config
this.defaultvalue = defaultvalue
}
function isstring(obj) {
return typeof obj === "string"
}
table.prototype.add = function(obj) {
if (!isstring(obj)) {
obj = tojson(obj)
}
var ret = table_add(this.id, obj)
return ret.err
}
table.prototype.replace = function(obj) {
if (!isstring(obj)) {
obj = tojson(obj)
}
var ret = table_replace(this.id, obj)
return ret.err
}
table.prototype.del = function(obj) {
if (!isstring(obj)) {
obj = tojson(obj)
}
var ret = table_del(this.id, obj)
return ret.err
}
table.prototype.save = function() {
var ret = table_save(this.id)
if (!this.kvc) {
this.kvc.save(ret)
}
return ret
}
table.prototype.close = function() {
var ret = table_close(this.id)
return ret.err
}
//account warp
function account(kvc, execer, symbol) {
this.execer = execer
this.symbol = symbol
this.kvc = kvc
}
account.prototype.genesisInit = function(addr, amount) {
var ret = genesis_init(this, addr, amount)
if (!this.kvc) {
this.kvc.save(ret)
}
return ret.err
}
account.prototype.execGenesisInit = function(execer, addr, amount) {
var ret = genesis_init_exec(this, execer, addr, amount)
if (!this.kvc) {
this.kvc.save(ret)
}
return ret.err
}
account.prototype.getBalance = function(addr) {
return load_account(this, addr)
}
account.prototype.execGetBalance = function(execer, addr) {
return get_balance(this, execer, addr)
}
//本合约转移资产,或者转移到其他合约,或者从其他合约取回资产
account.prototype.transfer = function(from, to, amount) {
var ret = transfer(this, from, to, amount)
if (!this.kvc) {
this.kvc.save(ret)
}
return ret.err
}
account.prototype.transferToExec = function(execer, from, amount) {
var ret = transfer_to_exec(this, execer, from, amount)
if (!this.kvc) {
this.kvc.save(ret)
}
return ret.err
}
account.prototype.withdrawFromExec = function(execer, to, amount) {
var ret = withdraw(this, execer, to, amount)
if (!this.kvc) {
this.kvc.save(ret)
}
return ret.err
}
//管理其他合约的资产转移到这个合约中
account.prototype.execActive = function(execer, addr, amount) {
var ret = exec_active(this, execer, addr, amount)
if (!this.kvc) {
this.kvc.save(ret)
}
return ret.err
}
account.prototype.execFrozen = function(execer, addr, amount) {
var ret = exec_frozen(this, execer, addr, amount)
if (!this.kvc) {
this.kvc.save(ret)
}
return ret.err
}
account.prototype.execDeposit = function(execer, addr, amount) {
var ret = exec_deposit(this, execer, addr, amount)
if (!this.kvc) {
this.kvc.save(ret)
}
return ret.err
}
account.prototype.execWithdraw = function(execer, addr, amount) {
var ret = exec_withdraw(this, execer, addr, amount)
if (!this.kvc) {
this.kvc.save(ret)
}
return ret.err
}
account.prototype.execTransfer = function(execer, from, to, amount) {
var ret = exec_transfer(this, execer, from, to, amount)
if (!this.kvc) {
this.kvc.save(ret)
}
return ret.err
}
function kvcreator(dbtype) {
this.data = {}
this.kvs = []
......@@ -12,30 +161,30 @@ function kvcreator(dbtype) {
this.getloal = getlocaldb
this.list = listdb
if (dbtype == "exec" || dbtype == "init") {
this.get = getstatedb
this.get = this.getstatedb
} else if (dbtype == "local") {
this.get = getlocaldb
this.get = this.getlocaldb
} else if (dbtype == "query") {
this.get = getlocaldb
this.get = this.getlocaldb
} else {
throw new Error("chain33.js: dbtype error")
}
}
kvcreator.prototype.add = function(k, v) {
kvcreator.prototype.add = function(k, v, prefix) {
if (typeof v != "string") {
v = JSON.stringify(v)
}
this.data[k] = v
this.kvs.push({key:k, value: v})
this.kvs.push({key:k, value: v, prefix: !!prefix})
}
kvcreator.prototype.get = function(k) {
kvcreator.prototype.get = function(k, prefix) {
var v
if (this.data[k]) {
v = this.data[k]
} else {
var dbvalue = this.get(k)
var dbvalue = this.get(k, !!prefix)
if (dbvalue.err != "") {
return null
}
......@@ -47,6 +196,21 @@ kvcreator.prototype.get = function(k) {
return JSON.parse(v)
}
kvcreator.prototype.save = function(receipt) {
if (Array.isArray(receipt.logs)) {
for (var i = 0; i < receipt.logs.length; i++) {
var item = receipt.logs[i]
this.addlog(item.log, item.ty, item.format)
}
}
if (Array.isArray(receipt.kvs)) {
for (var i = 0; i < receipt.kvs.length; i++) {
var item = receipt.kvs[i]
this.add(item.key, item.value, item.prefix)
}
}
}
kvcreator.prototype.listvalue = function(prefix, key, count, direction) {
var dbvalues = this.list(prefix, key, count, direction)
if (dbvalues.err != "") {
......@@ -63,14 +227,20 @@ kvcreator.prototype.listvalue = function(prefix, key, count, direction) {
return objlist
}
kvcreator.prototype.addlog = function(log) {
kvcreator.prototype.addlog = function(log, ty, format) {
if (this.type != "exec") {
throw new Error("local or query can't set log")
}
if (typeof v != "string") {
if (!isstring(log)) {
log = JSON.stringify(log)
}
this.logs.push(log)
}
if (!ty) {
ty = 0
}
if (!format) {
format = "json"
}
this.logs.push({log:log, ty: ty, format: format})
}
kvcreator.prototype.receipt = function() {
......@@ -113,3 +283,55 @@ function callcode(context, f, args, loglist) {
//Long
!function(t,i){"object"==typeof exports&&"object"==typeof module?module.exports=i():"function"==typeof define&&define.amd?define([],i):"object"==typeof exports?exports.Long=i():t.Long=i()}("undefined"!=typeof self?self:this,function(){return function(t){function i(h){if(n[h])return n[h].exports;var e=n[h]={i:h,l:!1,exports:{}};return t[h].call(e.exports,e,e.exports,i),e.l=!0,e.exports}var n={};return i.m=t,i.c=n,i.d=function(t,n,h){i.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:h})},i.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return i.d(n,"a",n),n},i.o=function(t,i){return Object.prototype.hasOwnProperty.call(t,i)},i.p="",i(i.s=0)}([function(t,i){function n(t,i,n){this.low=0|t,this.high=0|i,this.unsigned=!!n}function h(t){return!0===(t&&t.__isLong__)}function e(t,i){var n,h,e;return i?(t>>>=0,(e=0<=t&&t<256)&&(h=l[t])?h:(n=r(t,(0|t)<0?-1:0,!0),e&&(l[t]=n),n)):(t|=0,(e=-128<=t&&t<128)&&(h=f[t])?h:(n=r(t,t<0?-1:0,!1),e&&(f[t]=n),n))}function s(t,i){if(isNaN(t))return i?p:m;if(i){if(t<0)return p;if(t>=c)return q}else{if(t<=-w)return _;if(t+1>=w)return E}return t<0?s(-t,i).neg():r(t%d|0,t/d|0,i)}function r(t,i,h){return new n(t,i,h)}function o(t,i,n){if(0===t.length)throw Error("empty string");if("NaN"===t||"Infinity"===t||"+Infinity"===t||"-Infinity"===t)return m;if("number"==typeof i?(n=i,i=!1):i=!!i,(n=n||10)<2||36<n)throw RangeError("radix");var h;if((h=t.indexOf("-"))>0)throw Error("interior hyphen");if(0===h)return o(t.substring(1),i,n).neg();for(var e=s(a(n,8)),r=m,u=0;u<t.length;u+=8){var g=Math.min(8,t.length-u),f=parseInt(t.substring(u,u+g),n);if(g<8){var l=s(a(n,g));r=r.mul(l).add(s(f))}else r=r.mul(e),r=r.add(s(f))}return r.unsigned=i,r}function u(t,i){return"number"==typeof t?s(t,i):"string"==typeof t?o(t,i):r(t.low,t.high,"boolean"==typeof i?i:t.unsigned)}t.exports=n;var g=null;try{g=new WebAssembly.Instance(new WebAssembly.Module(new Uint8Array([0,97,115,109,1,0,0,0,1,13,2,96,0,1,127,96,4,127,127,127,127,1,127,3,7,6,0,1,1,1,1,1,6,6,1,127,1,65,0,11,7,50,6,3,109,117,108,0,1,5,100,105,118,95,115,0,2,5,100,105,118,95,117,0,3,5,114,101,109,95,115,0,4,5,114,101,109,95,117,0,5,8,103,101,116,95,104,105,103,104,0,0,10,191,1,6,4,0,35,0,11,36,1,1,126,32,0,173,32,1,173,66,32,134,132,32,2,173,32,3,173,66,32,134,132,126,34,4,66,32,135,167,36,0,32,4,167,11,36,1,1,126,32,0,173,32,1,173,66,32,134,132,32,2,173,32,3,173,66,32,134,132,127,34,4,66,32,135,167,36,0,32,4,167,11,36,1,1,126,32,0,173,32,1,173,66,32,134,132,32,2,173,32,3,173,66,32,134,132,128,34,4,66,32,135,167,36,0,32,4,167,11,36,1,1,126,32,0,173,32,1,173,66,32,134,132,32,2,173,32,3,173,66,32,134,132,129,34,4,66,32,135,167,36,0,32,4,167,11,36,1,1,126,32,0,173,32,1,173,66,32,134,132,32,2,173,32,3,173,66,32,134,132,130,34,4,66,32,135,167,36,0,32,4,167,11])),{}).exports}catch(t){}n.prototype.__isLong__,Object.defineProperty(n.prototype,"__isLong__",{value:!0}),n.isLong=h;var f={},l={};n.fromInt=e,n.fromNumber=s,n.fromBits=r;var a=Math.pow;n.fromString=o,n.fromValue=u;var d=4294967296,c=d*d,w=c/2,v=e(1<<24),m=e(0);n.ZERO=m;var p=e(0,!0);n.UZERO=p;var y=e(1);n.ONE=y;var b=e(1,!0);n.UONE=b;var N=e(-1);n.NEG_ONE=N;var E=r(-1,2147483647,!1);n.MAX_VALUE=E;var q=r(-1,-1,!0);n.MAX_UNSIGNED_VALUE=q;var _=r(0,-2147483648,!1);n.MIN_VALUE=_;var B=n.prototype;B.toInt=function(){return this.unsigned?this.low>>>0:this.low},B.toNumber=function(){return this.unsigned?(this.high>>>0)*d+(this.low>>>0):this.high*d+(this.low>>>0)},B.toString=function(t){if((t=t||10)<2||36<t)throw RangeError("radix");if(this.isZero())return"0";if(this.isNegative()){if(this.eq(_)){var i=s(t),n=this.div(i),h=n.mul(i).sub(this);return n.toString(t)+h.toInt().toString(t)}return"-"+this.neg().toString(t)}for(var e=s(a(t,6),this.unsigned),r=this,o="";;){var u=r.div(e),g=r.sub(u.mul(e)).toInt()>>>0,f=g.toString(t);if(r=u,r.isZero())return f+o;for(;f.length<6;)f="0"+f;o=""+f+o}},B.getHighBits=function(){return this.high},B.getHighBitsUnsigned=function(){return this.high>>>0},B.getLowBits=function(){return this.low},B.getLowBitsUnsigned=function(){return this.low>>>0},B.getNumBitsAbs=function(){if(this.isNegative())return this.eq(_)?64:this.neg().getNumBitsAbs();for(var t=0!=this.high?this.high:this.low,i=31;i>0&&0==(t&1<<i);i--);return 0!=this.high?i+33:i+1},B.isZero=function(){return 0===this.high&&0===this.low},B.eqz=B.isZero,B.isNegative=function(){return!this.unsigned&&this.high<0},B.isPositive=function(){return this.unsigned||this.high>=0},B.isOdd=function(){return 1==(1&this.low)},B.isEven=function(){return 0==(1&this.low)},B.equals=function(t){return h(t)||(t=u(t)),(this.unsigned===t.unsigned||this.high>>>31!=1||t.high>>>31!=1)&&(this.high===t.high&&this.low===t.low)},B.eq=B.equals,B.notEquals=function(t){return!this.eq(t)},B.neq=B.notEquals,B.ne=B.notEquals,B.lessThan=function(t){return this.comp(t)<0},B.lt=B.lessThan,B.lessThanOrEqual=function(t){return this.comp(t)<=0},B.lte=B.lessThanOrEqual,B.le=B.lessThanOrEqual,B.greaterThan=function(t){return this.comp(t)>0},B.gt=B.greaterThan,B.greaterThanOrEqual=function(t){return this.comp(t)>=0},B.gte=B.greaterThanOrEqual,B.ge=B.greaterThanOrEqual,B.compare=function(t){if(h(t)||(t=u(t)),this.eq(t))return 0;var i=this.isNegative(),n=t.isNegative();return i&&!n?-1:!i&&n?1:this.unsigned?t.high>>>0>this.high>>>0||t.high===this.high&&t.low>>>0>this.low>>>0?-1:1:this.sub(t).isNegative()?-1:1},B.comp=B.compare,B.negate=function(){return!this.unsigned&&this.eq(_)?_:this.not().add(y)},B.neg=B.negate,B.add=function(t){h(t)||(t=u(t));var i=this.high>>>16,n=65535&this.high,e=this.low>>>16,s=65535&this.low,o=t.high>>>16,g=65535&t.high,f=t.low>>>16,l=65535&t.low,a=0,d=0,c=0,w=0;return w+=s+l,c+=w>>>16,w&=65535,c+=e+f,d+=c>>>16,c&=65535,d+=n+g,a+=d>>>16,d&=65535,a+=i+o,a&=65535,r(c<<16|w,a<<16|d,this.unsigned)},B.subtract=function(t){return h(t)||(t=u(t)),this.add(t.neg())},B.sub=B.subtract,B.multiply=function(t){if(this.isZero())return m;if(h(t)||(t=u(t)),g){return r(g.mul(this.low,this.high,t.low,t.high),g.get_high(),this.unsigned)}if(t.isZero())return m;if(this.eq(_))return t.isOdd()?_:m;if(t.eq(_))return this.isOdd()?_:m;if(this.isNegative())return t.isNegative()?this.neg().mul(t.neg()):this.neg().mul(t).neg();if(t.isNegative())return this.mul(t.neg()).neg();if(this.lt(v)&&t.lt(v))return s(this.toNumber()*t.toNumber(),this.unsigned);var i=this.high>>>16,n=65535&this.high,e=this.low>>>16,o=65535&this.low,f=t.high>>>16,l=65535&t.high,a=t.low>>>16,d=65535&t.low,c=0,w=0,p=0,y=0;return y+=o*d,p+=y>>>16,y&=65535,p+=e*d,w+=p>>>16,p&=65535,p+=o*a,w+=p>>>16,p&=65535,w+=n*d,c+=w>>>16,w&=65535,w+=e*a,c+=w>>>16,w&=65535,w+=o*l,c+=w>>>16,w&=65535,c+=i*d+n*a+e*l+o*f,c&=65535,r(p<<16|y,c<<16|w,this.unsigned)},B.mul=B.multiply,B.divide=function(t){if(h(t)||(t=u(t)),t.isZero())throw Error("division by zero");if(g){if(!this.unsigned&&-2147483648===this.high&&-1===t.low&&-1===t.high)return this;return r((this.unsigned?g.div_u:g.div_s)(this.low,this.high,t.low,t.high),g.get_high(),this.unsigned)}if(this.isZero())return this.unsigned?p:m;var i,n,e;if(this.unsigned){if(t.unsigned||(t=t.toUnsigned()),t.gt(this))return p;if(t.gt(this.shru(1)))return b;e=p}else{if(this.eq(_)){if(t.eq(y)||t.eq(N))return _;if(t.eq(_))return y;return i=this.shr(1).div(t).shl(1),i.eq(m)?t.isNegative()?y:N:(n=this.sub(t.mul(i)),e=i.add(n.div(t)))}if(t.eq(_))return this.unsigned?p:m;if(this.isNegative())return t.isNegative()?this.neg().div(t.neg()):this.neg().div(t).neg();if(t.isNegative())return this.div(t.neg()).neg();e=m}for(n=this;n.gte(t);){i=Math.max(1,Math.floor(n.toNumber()/t.toNumber()));for(var o=Math.ceil(Math.log(i)/Math.LN2),f=o<=48?1:a(2,o-48),l=s(i),d=l.mul(t);d.isNegative()||d.gt(n);)i-=f,l=s(i,this.unsigned),d=l.mul(t);l.isZero()&&(l=y),e=e.add(l),n=n.sub(d)}return e},B.div=B.divide,B.modulo=function(t){if(h(t)||(t=u(t)),g){return r((this.unsigned?g.rem_u:g.rem_s)(this.low,this.high,t.low,t.high),g.get_high(),this.unsigned)}return this.sub(this.div(t).mul(t))},B.mod=B.modulo,B.rem=B.modulo,B.not=function(){return r(~this.low,~this.high,this.unsigned)},B.and=function(t){return h(t)||(t=u(t)),r(this.low&t.low,this.high&t.high,this.unsigned)},B.or=function(t){return h(t)||(t=u(t)),r(this.low|t.low,this.high|t.high,this.unsigned)},B.xor=function(t){return h(t)||(t=u(t)),r(this.low^t.low,this.high^t.high,this.unsigned)},B.shiftLeft=function(t){return h(t)&&(t=t.toInt()),0==(t&=63)?this:t<32?r(this.low<<t,this.high<<t|this.low>>>32-t,this.unsigned):r(0,this.low<<t-32,this.unsigned)},B.shl=B.shiftLeft,B.shiftRight=function(t){return h(t)&&(t=t.toInt()),0==(t&=63)?this:t<32?r(this.low>>>t|this.high<<32-t,this.high>>t,this.unsigned):r(this.high>>t-32,this.high>=0?0:-1,this.unsigned)},B.shr=B.shiftRight,B.shiftRightUnsigned=function(t){return h(t)&&(t=t.toInt()),0==(t&=63)?this:t<32?r(this.low>>>t|this.high<<32-t,this.high>>>t,this.unsigned):32===t?r(this.high,0,this.unsigned):r(this.high>>>t-32,0,this.unsigned)},B.shru=B.shiftRightUnsigned,B.shr_u=B.shiftRightUnsigned,B.rotateLeft=function(t){var i;return h(t)&&(t=t.toInt()),0==(t&=63)?this:32===t?r(this.high,this.low,this.unsigned):t<32?(i=32-t,r(this.low<<t|this.high>>>i,this.high<<t|this.low>>>i,this.unsigned)):(t-=32,i=32-t,r(this.high<<t|this.low>>>i,this.low<<t|this.high>>>i,this.unsigned))},B.rotl=B.rotateLeft,B.rotateRight=function(t){var i;return h(t)&&(t=t.toInt()),0==(t&=63)?this:32===t?r(this.high,this.low,this.unsigned):t<32?(i=32-t,r(this.high<<i|this.low>>>t,this.low<<i|this.high>>>t,this.unsigned)):(t-=32,i=32-t,r(this.low<<i|this.high>>>t,this.high<<i|this.low>>>t,this.unsigned))},B.rotr=B.rotateRight,B.toSigned=function(){return this.unsigned?r(this.low,this.high,!1):this},B.toUnsigned=function(){return this.unsigned?this:r(this.low,this.high,!0)},B.toBytes=function(t){return t?this.toBytesLE():this.toBytesBE()},B.toBytesLE=function(){var t=this.high,i=this.low;return[255&i,i>>>8&255,i>>>16&255,i>>>24,255&t,t>>>8&255,t>>>16&255,t>>>24]},B.toBytesBE=function(){var t=this.high,i=this.low;return[t>>>24,t>>>16&255,t>>>8&255,255&t,i>>>24,i>>>16&255,i>>>8&255,255&i]},n.fromBytes=function(t,i,h){return h?n.fromBytesLE(t,i):n.fromBytesBE(t,i)},n.fromBytesLE=function(t,i){return new n(t[0]|t[1]<<8|t[2]<<16|t[3]<<24,t[4]|t[5]<<8|t[6]<<16|t[7]<<24,i)},n.fromBytesBE=function(t,i){return new n(t[4]<<24|t[5]<<16|t[6]<<8|t[7],t[0]<<24|t[1]<<16|t[2]<<8|t[3],i)}}])});
`
var jscode = `
//数据结构设计
//kvlist [{key:"key1", value:"value1"},{key:"key2", value:"value2"}]
//log 设计 {json data}
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()
}
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) {
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
......@@ -8,7 +8,10 @@ import (
)
func (c *js) Exec_Create(payload *jsproto.Create, tx *types.Transaction, index int) (*types.Receipt, error) {
execer := types.ExecName("user.js." + payload.Name)
execer := types.ExecName("user." + ptypes.JsX + "." + payload.Name)
if string(tx.Execer) != execer {
return nil, types.ErrExecNameNotMatch
}
c.prefix = calcStatePrefix([]byte(execer))
kvc := dapp.NewKVCreator(c.GetStateDB(), c.prefix, nil)
_, err := kvc.GetNoPrefix(calcCodeKey(payload.Name))
......@@ -23,28 +26,31 @@ func (c *js) Exec_Create(payload *jsproto.Create, tx *types.Transaction, index i
if err != nil {
return nil, err
}
kvs, logs, err := parseJsReturn(jsvalue)
kvs, logs, err := parseJsReturn(c.prefix, jsvalue)
if err != nil {
return nil, err
}
kvc.AddList(kvs)
kvc.AddListNoPrefix(kvs)
r := &types.Receipt{Ty: types.ExecOk, KV: kvc.KVList(), Logs: logs}
return r, nil
}
func (c *js) Exec_Call(payload *jsproto.Call, tx *types.Transaction, index int) (*types.Receipt, error) {
execer := types.ExecName("user.js." + payload.Name)
execer := types.ExecName("user." + ptypes.JsX + "." + payload.Name)
if string(tx.Execer) != execer {
return nil, types.ErrExecNameNotMatch
}
c.prefix = calcStatePrefix([]byte(execer))
kvc := dapp.NewKVCreator(c.GetStateDB(), c.prefix, nil)
jsvalue, err := c.callVM("exec", payload, tx, index, nil)
if err != nil {
return nil, err
}
kvs, logs, err := parseJsReturn(jsvalue)
kvs, logs, err := parseJsReturn(c.prefix, jsvalue)
if err != nil {
return nil, err
}
kvc.AddList(kvs)
kvc.AddListNoPrefix(kvs)
r := &types.Receipt{Ty: types.ExecOk, KV: kvc.KVList(), Logs: logs}
return r, nil
}
......@@ -3,6 +3,7 @@ package executor
import (
"github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
ptypes "github.com/33cn/plugin/plugin/dapp/js/types"
"github.com/33cn/plugin/plugin/dapp/js/types/jsproto"
)
......@@ -12,7 +13,7 @@ func (c *js) ExecDelLocal_Create(payload *jsproto.Create, tx *types.Transaction,
func (c *js) ExecDelLocal_Call(payload *jsproto.Call, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
krollback := calcRollbackKey(tx.Hash())
execer := types.ExecName("user.js." + payload.Name)
execer := types.ExecName("user." + ptypes.JsX + "." + payload.Name)
c.prefix = calcLocalPrefix([]byte(execer))
kvc := dapp.NewKVCreator(c.GetLocalDB(), c.prefix, krollback)
kvs, err := kvc.GetRollbackKVList()
......
......@@ -3,6 +3,7 @@ package executor
import (
"github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
ptypes "github.com/33cn/plugin/plugin/dapp/js/types"
"github.com/33cn/plugin/plugin/dapp/js/types/jsproto"
)
......@@ -12,18 +13,18 @@ func (c *js) ExecLocal_Create(payload *jsproto.Create, tx *types.Transaction, re
func (c *js) ExecLocal_Call(payload *jsproto.Call, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
k := calcRollbackKey(tx.Hash())
execer := types.ExecName("user.js." + payload.Name)
execer := types.ExecName("user." + ptypes.JsX + "." + payload.Name)
c.prefix = calcLocalPrefix([]byte(execer))
kvc := dapp.NewKVCreator(c.GetLocalDB(), c.prefix, k)
jsvalue, err := c.callVM("execlocal", payload, tx, index, receiptData)
if err != nil {
return nil, err
}
kvs, _, err := parseJsReturn(jsvalue)
kvs, _, err := parseJsReturn(c.prefix, jsvalue)
if err != nil {
return nil, err
}
kvc.AddList(kvs)
kvc.AddListNoPrefix(kvs)
kvc.AddRollbackKV()
r := &types.LocalDBSet{}
r.KV = kvc.KVList()
......
#!/bin/sh
{
printf 'package executor\n\nvar callcode = `\n'
cat "runtime.js"
printf '`\n'
printf 'var jscode = `\n'
cat "test.js"
printf '`\n'
printf 'var _ = jscode\n'
} >const.go
......@@ -2,6 +2,7 @@ package executor
import (
"encoding/json"
"fmt"
"github.com/33cn/chain33/common"
drivers "github.com/33cn/chain33/system/dapp"
......@@ -25,6 +26,7 @@ func init() {
panic(err)
}
execaddressFunc(basevm)
registerTableFunc(basevm)
}
//Init 插件初始化
......@@ -104,6 +106,12 @@ func (u *js) callVM(prefix string, payload *jsproto.Call, tx *types.Transaction,
return jsvalue.Object(), nil
}
type jslogInfo struct {
Log string `json:"log"`
Ty int32 `json:"ty"`
Format string `json:"format"`
}
func jslogs(receiptData *types.ReceiptData) ([]string, error) {
data := make([]string, 0)
if receiptData == nil {
......@@ -111,6 +119,7 @@ func jslogs(receiptData *types.ReceiptData) ([]string, error) {
}
for i := 0; i < len(receiptData.Logs); i++ {
logitem := receiptData.Logs[i]
//只传递 json格式的日子,不传递 二进制的日志
if logitem.Ty != ptypes.TyLogJs {
continue
}
......@@ -119,7 +128,11 @@ func jslogs(receiptData *types.ReceiptData) ([]string, error) {
if err != nil {
return nil, err
}
data = append(data, jslog.Data)
item, err := json.Marshal(&jslogInfo{Log: jslog.Data, Ty: receiptData.Ty, Format: "json"})
if err != nil {
return nil, err
}
data = append(data, string(item))
}
return data, nil
}
......@@ -141,14 +154,21 @@ func (u *js) getContext(tx *types.Transaction, index int64) *blockContext {
}
}
func (u *js) getstatedbFunc(vm *otto.Otto, name string) {
func (u *js) statedbFunc(vm *otto.Otto, name string) {
prefix, _ := calcAllPrefix(name)
vm.Set("getstatedb", func(call otto.FunctionCall) otto.Value {
key, err := call.Argument(0).ToString()
if err != nil {
return errReturn(vm, err)
}
v, err := u.getstatedb(string(prefix) + key)
hasprefix, err := call.Argument(1).ToBoolean()
if err != nil {
return errReturn(vm, err)
}
if !hasprefix {
key = string(prefix) + key
}
v, err := u.getstatedb(key)
if err != nil {
return errReturn(vm, err)
}
......@@ -156,14 +176,21 @@ func (u *js) getstatedbFunc(vm *otto.Otto, name string) {
})
}
func (u *js) getlocaldbFunc(vm *otto.Otto, name string) {
func (u *js) localdbFunc(vm *otto.Otto, name string) {
_, prefix := calcAllPrefix(name)
vm.Set("getlocaldb", func(call otto.FunctionCall) otto.Value {
key, err := call.Argument(0).ToString()
if err != nil {
return errReturn(vm, err)
}
v, err := u.getlocaldb(string(prefix) + key)
hasprefix, err := call.Argument(1).ToBoolean()
if err != nil {
return errReturn(vm, err)
}
if !hasprefix {
key = string(prefix) + key
}
v, err := u.getlocaldb(key)
if err != nil {
return errReturn(vm, err)
}
......@@ -173,7 +200,7 @@ func (u *js) getlocaldbFunc(vm *otto.Otto, name string) {
func (u *js) execnameFunc(vm *otto.Otto, name string) {
vm.Set("execname", func(call otto.FunctionCall) otto.Value {
return okReturn(vm, name)
return okReturn(vm, types.ExecName("user.js."+name))
})
}
......@@ -225,10 +252,12 @@ func (u *js) createVM(name string, tx *types.Transaction, index int) (*otto.Otto
vm = cachevm.Copy()
}
vm.Set("context", string(data))
u.getstatedbFunc(vm, name)
u.getlocaldbFunc(vm, name)
u.statedbFunc(vm, name)
u.localdbFunc(vm, name)
u.listdbFunc(vm, name)
u.execnameFunc(vm, name)
u.registerAccountFunc(vm)
u.newTableFunc(vm, name)
return vm, nil
}
......@@ -244,13 +273,23 @@ func listReturn(vm *otto.Otto, value []string) otto.Value {
return newObject(vm).setValue("value", value).value()
}
func receiptReturn(vm *otto.Otto, receipt *types.Receipt) otto.Value {
kvs := createKVObject(vm, receipt.KV)
logs := createLogsObject(vm, receipt.Logs)
return newObject(vm).setValue("kvs", kvs).setValue("logs", logs).value()
}
type object struct {
vm *otto.Otto
obj *otto.Object
}
func newObject(vm *otto.Otto) *object {
obj, err := vm.Object("({})")
return newObjectString(vm, "({})")
}
func newObjectString(vm *otto.Otto, value string) *object {
obj, err := vm.Object(value)
if err != nil {
panic(err)
}
......@@ -281,6 +320,53 @@ func (o *object) value() otto.Value {
return v
}
// Allow 允许哪些交易在本命执行器执行
func (u *js) Allow(tx *types.Transaction, index int) error {
err := u.DriverBase.Allow(tx, index)
if err == nil {
return nil
}
//增加新的规则:
//主链: user.jsvm.xxx 执行 jsvm 合约
//平行链: user.p.guodun.user.jsvm.xxx 执行 jsvm 合约
exec := types.GetParaExec(tx.Execer)
if u.AllowIsUserDot2(exec) {
return nil
}
return types.ErrNotAllow
}
func createKVObject(vm *otto.Otto, kvs []*types.KeyValue) otto.Value {
obj := newObjectString(vm, "([])")
for i := 0; i < len(kvs); i++ {
item := newObject(vm).setValue("key", string(kvs[i].Key))
item.setValue("value", string(kvs[i].Value))
item.setValue("prefix", true)
obj.setValue(fmt.Sprint(i), item)
}
return obj.value()
}
func createLogsObject(vm *otto.Otto, logs []*types.ReceiptLog) otto.Value {
obj := newObjectString(vm, "([])")
for i := 0; i < len(logs); i++ {
item := newObject(vm).setValue("ty", logs[i].Ty)
item.setValue("log", string(logs[i].Log))
item.setValue("format", "proto")
obj.setValue(fmt.Sprint(i), item)
}
return obj.value()
}
func accountReturn(vm *otto.Otto, acc *types.Account) otto.Value {
obj := newObject(vm)
obj.setValue("currency", acc.Currency)
obj.setValue("balance", acc.Balance)
obj.setValue("frozen", acc.Frozen)
obj.setValue("addr", acc.Addr)
return obj.value()
}
func (u *js) getstatedb(key string) (value string, err error) {
s, err := u.GetStateDB().Get([]byte(key))
value = string(s)
......
......@@ -24,7 +24,7 @@ type blockContext struct {
Index int64 `json:"index"`
}
func parseJsReturn(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) {
//kvs
obj, err := getObject(jsvalue, "kvs")
if err != nil {
......@@ -42,7 +42,7 @@ func parseJsReturn(jsvalue *otto.Object) (kvlist []*types.KeyValue, logs []*type
if err != nil {
return nil, nil, err
}
kv, err := parseKV(data)
kv, err := parseKV(prefix, data)
if err != nil {
return nil, nil, err
}
......@@ -61,13 +61,34 @@ func parseJsReturn(jsvalue *otto.Object) (kvlist []*types.KeyValue, logs []*type
return nil, nil, err
}
for i := 0; i < int(size); i++ {
data, err := getString(obj, fmt.Sprint(i))
data, err := getObject(obj, fmt.Sprint(i))
if err != nil {
return nil, nil, err
}
//
logdata, err := getString(data, "log")
if err != nil {
return nil, nil, err
}
format, err := getString(data, "format")
if err != nil {
return nil, nil, err
}
ty, err := getInt(data, "ty")
if err != nil {
return nil, nil, err
}
l := &types.ReceiptLog{
Ty: ptypes.TyLogJs, Log: types.Encode(&jsproto.JsLog{Data: data})}
logs = append(logs, l)
if format == "json" {
l := &types.ReceiptLog{
Ty: ptypes.TyLogJs, Log: types.Encode(&jsproto.JsLog{Data: logdata})}
logs = append(logs, l)
} else {
l := &types.ReceiptLog{
Ty: int32(ty),
Log: []byte(logdata),
}
logs = append(logs, l)
}
}
return kvlist, logs, nil
}
......@@ -80,6 +101,14 @@ func getString(data *otto.Object, key string) (string, error) {
return v.ToString()
}
func getBool(data *otto.Object, key string) (bool, error) {
v, err := data.Get(key)
if err != nil {
return false, err
}
return v.ToBoolean()
}
func getInt(data *otto.Object, key string) (int64, error) {
v, err := data.Get(key)
if err != nil {
......@@ -99,7 +128,7 @@ func getObject(data *otto.Object, key string) (*otto.Object, error) {
return v.Object(), nil
}
func parseKV(data *otto.Object) (kv *types.KeyValue, err error) {
func parseKV(prefix []byte, data *otto.Object) (kv *types.KeyValue, err error) {
key, err := getString(data, "key")
if err != nil {
return nil, err
......@@ -108,6 +137,13 @@ func parseKV(data *otto.Object) (kv *types.KeyValue, err error) {
if err != nil {
return nil, err
}
hasprefix, err := getBool(data, "prefix")
if err != nil {
return nil, err
}
if !hasprefix {
key = string(prefix) + key
}
return &types.KeyValue{Key: []byte(key), Value: []byte(value)}, nil
}
......@@ -143,6 +179,8 @@ func rewriteString(dat map[string]interface{}) map[string]interface{} {
return dat
}
const maxjsint int64 = 9007199254740991
func jssafe(n json.Number) interface{} {
if strings.Contains(string(n), ".") { //float
return n
......@@ -152,7 +190,7 @@ func jssafe(n json.Number) interface{} {
return n
}
//javascript can not parse
if i >= 9007199254740991 || i <= -9007199254740991 {
if i >= maxjsint || i <= -maxjsint {
return string(n)
}
return n
......
......@@ -39,7 +39,7 @@ func createCodeTx(name, jscode string) (*jsproto.Create, *types.Transaction) {
Code: jscode,
Name: name,
}
return data, &types.Transaction{Execer: []byte("js"), Payload: types.Encode(data)}
return data, &types.Transaction{Execer: []byte("user." + ptypes.JsX + "." + name), Payload: types.Encode(data)}
}
func callCodeTx(name, f, args string) (*jsproto.Call, *types.Transaction) {
......@@ -48,7 +48,7 @@ func callCodeTx(name, f, args string) (*jsproto.Call, *types.Transaction) {
Name: name,
Args: args,
}
return data, &types.Transaction{Execer: []byte("js"), Payload: types.Encode(data)}
return data, &types.Transaction{Execer: []byte("user." + ptypes.JsX + "." + name), Payload: types.Encode(data)}
}
func TestCallcode(t *testing.T) {
......@@ -66,7 +66,7 @@ func TestCallcode(t *testing.T) {
err = json.Unmarshal(receipt.KV[2].Value, &data)
assert.Nil(t, err)
assert.Equal(t, uint64(1), data.Difficulty)
assert.Equal(t, "js", data.DriverName)
assert.Equal(t, ptypes.JsX, data.DriverName)
assert.Equal(t, int64(1), data.Height)
assert.Equal(t, int64(0), data.Index)
......@@ -76,12 +76,12 @@ func TestCallcode(t *testing.T) {
assert.Equal(t, string(kvset.KV[0].Value), `{"hello":"world"}`)
assert.Equal(t, string(kvset.KV[1].Value), "execlocal")
//test log is ok
assert.Equal(t, string(kvset.KV[2].Value), `[{"key1":"value1"},{"key2":"value2"}]`)
assert.Equal(t, string(kvset.KV[2].Value), `[{"format":"json","log":"{\"key1\":\"value1\"}","ty":0},{"format":"json","log":"{\"key2\":\"value2\"}","ty":0}]`)
//test context
err = json.Unmarshal(kvset.KV[3].Value, &data)
assert.Nil(t, err)
assert.Equal(t, uint64(1), data.Difficulty)
assert.Equal(t, "js", data.DriverName)
assert.Equal(t, "jsvm", data.DriverName)
assert.Equal(t, int64(1), data.Height)
assert.Equal(t, int64(0), data.Index)
......@@ -91,7 +91,7 @@ func TestCallcode(t *testing.T) {
err = json.Unmarshal([]byte(jsondata.(*jsproto.QueryResult).Data), &data)
assert.Nil(t, err)
assert.Equal(t, uint64(1), data.Difficulty)
assert.Equal(t, "js", data.DriverName)
assert.Equal(t, "jsvm", data.DriverName)
assert.Equal(t, int64(1), data.Height)
assert.Equal(t, int64(0), data.Index)
//call rollback
......@@ -100,7 +100,7 @@ func TestCallcode(t *testing.T) {
util.SaveKVList(ldb, kvset.KV)
assert.Equal(t, 5, len(kvset.KV))
for i := 0; i < len(kvset.KV); i++ {
assert.Equal(t, kvset.KV[i].Value, []byte(nil))
assert.Equal(t, string(kvset.KV[i].Value), "")
}
}
......@@ -138,7 +138,7 @@ func TestBigInt(t *testing.T) {
call, tx := callCodeTx("test", "hello", s)
data, err := e.callVM("exec", call, tx, 0, nil)
assert.Nil(t, err)
kvs, _, err := parseJsReturn(data)
kvs, _, err := parseJsReturn([]byte("user.jsvm.test"), data)
assert.Nil(t, err)
assert.Equal(t, `{"balance":"9223372036854775807","balance1":"-9223372036854775808","balance2":9007199254740990,"balance3":-9007199254740990}`, string(kvs[0].Value))
}
......@@ -177,8 +177,8 @@ func TestRewriteJSON(t *testing.T) {
func TestCalcLocalPrefix(t *testing.T) {
assert.Equal(t, calcLocalPrefix([]byte("a")), []byte("LODB-a-"))
assert.Equal(t, calcStatePrefix([]byte("a")), []byte("mavl-a-"))
assert.Equal(t, calcCodeKey("a"), []byte("mavl-js-code-a"))
assert.Equal(t, calcRollbackKey([]byte("a")), []byte("LODB-js-rollback-a"))
assert.Equal(t, calcCodeKey("a"), []byte("mavl-jsvm-code-a"))
assert.Equal(t, calcRollbackKey([]byte("a")), []byte("LODB-jsvm-rollback-a"))
}
func TestCacheMemUsage(t *testing.T) {
......@@ -206,55 +206,3 @@ func printMemUsage() {
func bToMb(b uint64) uint64 {
return b / 1024 / 1024
}
var jscode = `
//数据结构设计
//kvlist [{key:"key1", value:"value1"},{key:"key2", value:"value2"}]
//log 设计 {json data}
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()
}
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) {
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)
}
`
package executor
import "github.com/33cn/chain33/types"
import (
"github.com/33cn/chain33/types"
ptypes "github.com/33cn/plugin/plugin/dapp/js/types"
)
func calcLocalPrefix(execer []byte) []byte {
s := append([]byte("LODB-"), execer...)
......@@ -15,16 +18,16 @@ func calcStatePrefix(execer []byte) []byte {
}
func calcAllPrefix(name string) ([]byte, []byte) {
execer := types.ExecName("user.js." + name)
execer := types.ExecName("user." + ptypes.JsX + "." + name)
state := calcStatePrefix([]byte(execer))
local := calcLocalPrefix([]byte(execer))
return state, local
}
func calcCodeKey(name string) []byte {
return append([]byte("mavl-js-code-"), []byte(name)...)
return append([]byte("mavl-"+ptypes.JsX+"-code-"), []byte(name)...)
}
func calcRollbackKey(hash []byte) []byte {
return append([]byte("LODB-js-rollback-"), hash...)
return append([]byte("LODB-"+ptypes.JsX+"-rollback-"), hash...)
}
......@@ -18,45 +18,6 @@ func execaddressFunc(vm *otto.Otto) {
}
/*
//chain33 相关的账户操作函数 (操作某个execer 下面的 symbol)
function Account(execer, symbol) {
this.execer = execer
this.symbol = symbol
}
func Receipt(kvs, logs) {
this.kvs = kvs
this.logs = logs
}
var obj = new Account(execer, symbol)
//init 函数才能使用的两个函数(或者增发)
genesis_init(obj, addr string, amount int64)
genesis_exec_init(obj, execer, addr string, amount int64)
load_account(obj, addr) Account
get_balance(obj, addr, execer) Account
transfer(obj, from, to, amount) Receipt
transfer_to_exec(obj, execer, addr, amount) Receipt
withdraw(obj, execer, addr, amount) Receipt
exec_frozen(obj, addr) Receipt
exec_active(obj, addr) Receipt
exec_transfer(obj, from, to, amount) Receipt
exec_deposit(obj, addr, amount) Receipt
exec_withdraw(obj, addr, amount) Receipt
//table
//要开发一个适合json的table, row 就是一个 js object
//handle := new_table(config)
//table_add(handle, row)
//table_replace(handle, row)
//table_del(handle, row)
//table_savekvs(handle)
//table_close(handle)
//join table 的操作(接口完全相同)
//handle3 := new_table(newcofifg{config1, config2})
//获取系统随机数的接口
//randnum
......
var tojson = JSON.stringify
//table warp
function table(kvc, config, defaultvalue) {
var ret = table_new(config, defaultvalue)
if (ret.err) {
throw new Error(ret.err)
}
this.kvc = kvc
this.id = ret.id
this.config = config
this.defaultvalue = defaultvalue
}
function isstring(obj) {
return typeof obj === "string"
}
table.prototype.add = function(obj) {
if (!isstring(obj)) {
obj = tojson(obj)
}
var ret = table_add(this.id, obj)
return ret.err
}
table.prototype.replace = function(obj) {
if (!isstring(obj)) {
obj = tojson(obj)
}
var ret = table_replace(this.id, obj)
return ret.err
}
table.prototype.del = function(obj) {
if (!isstring(obj)) {
obj = tojson(obj)
}
var ret = table_del(this.id, obj)
return ret.err
}
table.prototype.save = function() {
var ret = table_save(this.id)
if (!this.kvc) {
this.kvc.save(ret)
}
return ret
}
table.prototype.close = function() {
var ret = table_close(this.id)
return ret.err
}
function JoinTable(lefttable, righttable, index) {
if (this.lefttable.kvc != this.righttable.kvc) {
throw new Error("the kvc of left and right must same")
}
this.lefttable = lefttable
this.righttable = righttable
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
}
JoinTable.prototype.Save = function() {
var ret = table_save(this.id)
if (!this.kvc) {
this.kvc.save(ret)
}
return ret
}
JoinTable.prototype.close = function() {
table_close(this.lefttable.id)
table_close(this.righttable.id)
var ret = table_close(this.id)
return ret.err
}
//account warp
function account(kvc, execer, symbol) {
this.execer = execer
this.symbol = symbol
this.kvc = kvc
}
account.prototype.genesisInit = function(addr, amount) {
var ret = genesis_init(this, addr, amount)
if (!this.kvc) {
this.kvc.save(ret)
}
return ret.err
}
account.prototype.execGenesisInit = function(execer, addr, amount) {
var ret = genesis_init_exec(this, execer, addr, amount)
if (!this.kvc) {
this.kvc.save(ret)
}
return ret.err
}
account.prototype.getBalance = function(addr) {
return load_account(this, addr)
}
account.prototype.execGetBalance = function(execer, addr) {
return get_balance(this, execer, addr)
}
//本合约转移资产,或者转移到其他合约,或者从其他合约取回资产
account.prototype.transfer = function(from, to, amount) {
var ret = transfer(this, from, to, amount)
if (!this.kvc) {
this.kvc.save(ret)
}
return ret.err
}
account.prototype.transferToExec = function(execer, from, amount) {
var ret = transfer_to_exec(this, execer, from, amount)
if (!this.kvc) {
this.kvc.save(ret)
}
return ret.err
}
account.prototype.withdrawFromExec = function(execer, to, amount) {
var ret = withdraw(this, execer, to, amount)
if (!this.kvc) {
this.kvc.save(ret)
}
return ret.err
}
//管理其他合约的资产转移到这个合约中
account.prototype.execActive = function(execer, addr, amount) {
var ret = exec_active(this, execer, addr, amount)
if (!this.kvc) {
this.kvc.save(ret)
}
return ret.err
}
account.prototype.execFrozen = function(execer, addr, amount) {
var ret = exec_frozen(this, execer, addr, amount)
if (!this.kvc) {
this.kvc.save(ret)
}
return ret.err
}
account.prototype.execDeposit = function(execer, addr, amount) {
var ret = exec_deposit(this, execer, addr, amount)
if (!this.kvc) {
this.kvc.save(ret)
}
return ret.err
}
account.prototype.execWithdraw = function(execer, addr, amount) {
var ret = exec_withdraw(this, execer, addr, amount)
if (!this.kvc) {
this.kvc.save(ret)
}
return ret.err
}
account.prototype.execTransfer = function(execer, from, to, amount) {
var ret = exec_transfer(this, execer, from, to, amount)
if (!this.kvc) {
this.kvc.save(ret)
}
return ret.err
}
function kvcreator(dbtype) {
this.data = {}
this.kvs = []
this.logs = []
this.type = dbtype
this.getstate = getstatedb
this.getloal = getlocaldb
this.list = listdb
if (dbtype == "exec" || dbtype == "init") {
this.get = this.getstatedb
} else if (dbtype == "local") {
this.get = this.getlocaldb
} else if (dbtype == "query") {
this.get = this.getlocaldb
} else {
throw new Error("chain33.js: dbtype error")
}
}
kvcreator.prototype.add = function(k, v, prefix) {
if (typeof v != "string") {
v = JSON.stringify(v)
}
this.data[k] = v
this.kvs.push({key:k, value: v, prefix: !!prefix})
}
kvcreator.prototype.get = function(k, prefix) {
var v
if (this.data[k]) {
v = this.data[k]
} else {
var dbvalue = this.get(k, !!prefix)
if (dbvalue.err != "") {
return null
}
v = dbvalue.value
}
if (!v) {
return null
}
return JSON.parse(v)
}
kvcreator.prototype.save = function(receipt) {
if (Array.isArray(receipt.logs)) {
for (var i = 0; i < receipt.logs.length; i++) {
var item = receipt.logs[i]
this.addlog(item.log, item.ty, item.format)
}
}
if (Array.isArray(receipt.kvs)) {
for (var i = 0; i < receipt.kvs.length; i++) {
var item = receipt.kvs[i]
this.add(item.key, item.value, item.prefix)
}
}
}
kvcreator.prototype.listvalue = function(prefix, key, count, direction) {
var dbvalues = this.list(prefix, key, count, direction)
if (dbvalues.err != "") {
return []
}
var values = dbvalues.value
if (!values || values.length == 0) {
return []
}
var objlist = []
for (var i = 0; i < values.length; i++) {
objlist.push(JSON.parse(values[i]))
}
return objlist
}
kvcreator.prototype.addlog = function(log, ty, format) {
if (this.type != "exec") {
throw new Error("local or query can't set log")
}
if (!isstring(log)) {
log = JSON.stringify(log)
}
if (!ty) {
ty = 0
}
if (!format) {
format = "json"
}
this.logs.push({log:log, ty: ty, format: format})
}
kvcreator.prototype.receipt = function() {
return {kvs: this.kvs, logs: this.logs}
}
function callcode(context, f, args, loglist) {
if (f == "init") {
return Init(JSON.parse(context))
}
var farr = f.split("_", 2)
if (farr.length != 2) {
throw new Error("chain33.js: invalid function name format")
}
var prefix = farr[0]
var funcname = farr[1]
var runobj = {}
var logs = []
if (!Array.isArray(loglist)) {
throw new Error("chain33.js: loglist must be array")
}
for (var i = 0; i < loglist.length; i++) {
logs.push(JSON.parse(loglist[i]))
}
if (prefix == "exec") {
runobj = new Exec(JSON.parse(context))
} else if (prefix == "execlocal") {
runobj = new ExecLocal(JSON.parse(context), logs)
} else if (prefix == "query") {
runobj = new Query(JSON.parse(context))
} else {
throw new Error("chain33.js: invalid function prefix format")
}
var arg = JSON.parse(args)
if (typeof runobj[funcname] != "function") {
throw new Error("chain33.js: invalid function name not found")
}
return runobj[funcname](arg)
}
//Long
!function(t,i){"object"==typeof exports&&"object"==typeof module?module.exports=i():"function"==typeof define&&define.amd?define([],i):"object"==typeof exports?exports.Long=i():t.Long=i()}("undefined"!=typeof self?self:this,function(){return function(t){function i(h){if(n[h])return n[h].exports;var e=n[h]={i:h,l:!1,exports:{}};return t[h].call(e.exports,e,e.exports,i),e.l=!0,e.exports}var n={};return i.m=t,i.c=n,i.d=function(t,n,h){i.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:h})},i.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return i.d(n,"a",n),n},i.o=function(t,i){return Object.prototype.hasOwnProperty.call(t,i)},i.p="",i(i.s=0)}([function(t,i){function n(t,i,n){this.low=0|t,this.high=0|i,this.unsigned=!!n}function h(t){return!0===(t&&t.__isLong__)}function e(t,i){var n,h,e;return i?(t>>>=0,(e=0<=t&&t<256)&&(h=l[t])?h:(n=r(t,(0|t)<0?-1:0,!0),e&&(l[t]=n),n)):(t|=0,(e=-128<=t&&t<128)&&(h=f[t])?h:(n=r(t,t<0?-1:0,!1),e&&(f[t]=n),n))}function s(t,i){if(isNaN(t))return i?p:m;if(i){if(t<0)return p;if(t>=c)return q}else{if(t<=-w)return _;if(t+1>=w)return E}return t<0?s(-t,i).neg():r(t%d|0,t/d|0,i)}function r(t,i,h){return new n(t,i,h)}function o(t,i,n){if(0===t.length)throw Error("empty string");if("NaN"===t||"Infinity"===t||"+Infinity"===t||"-Infinity"===t)return m;if("number"==typeof i?(n=i,i=!1):i=!!i,(n=n||10)<2||36<n)throw RangeError("radix");var h;if((h=t.indexOf("-"))>0)throw Error("interior hyphen");if(0===h)return o(t.substring(1),i,n).neg();for(var e=s(a(n,8)),r=m,u=0;u<t.length;u+=8){var g=Math.min(8,t.length-u),f=parseInt(t.substring(u,u+g),n);if(g<8){var l=s(a(n,g));r=r.mul(l).add(s(f))}else r=r.mul(e),r=r.add(s(f))}return r.unsigned=i,r}function u(t,i){return"number"==typeof t?s(t,i):"string"==typeof t?o(t,i):r(t.low,t.high,"boolean"==typeof i?i:t.unsigned)}t.exports=n;var g=null;try{g=new WebAssembly.Instance(new WebAssembly.Module(new Uint8Array([0,97,115,109,1,0,0,0,1,13,2,96,0,1,127,96,4,127,127,127,127,1,127,3,7,6,0,1,1,1,1,1,6,6,1,127,1,65,0,11,7,50,6,3,109,117,108,0,1,5,100,105,118,95,115,0,2,5,100,105,118,95,117,0,3,5,114,101,109,95,115,0,4,5,114,101,109,95,117,0,5,8,103,101,116,95,104,105,103,104,0,0,10,191,1,6,4,0,35,0,11,36,1,1,126,32,0,173,32,1,173,66,32,134,132,32,2,173,32,3,173,66,32,134,132,126,34,4,66,32,135,167,36,0,32,4,167,11,36,1,1,126,32,0,173,32,1,173,66,32,134,132,32,2,173,32,3,173,66,32,134,132,127,34,4,66,32,135,167,36,0,32,4,167,11,36,1,1,126,32,0,173,32,1,173,66,32,134,132,32,2,173,32,3,173,66,32,134,132,128,34,4,66,32,135,167,36,0,32,4,167,11,36,1,1,126,32,0,173,32,1,173,66,32,134,132,32,2,173,32,3,173,66,32,134,132,129,34,4,66,32,135,167,36,0,32,4,167,11,36,1,1,126,32,0,173,32,1,173,66,32,134,132,32,2,173,32,3,173,66,32,134,132,130,34,4,66,32,135,167,36,0,32,4,167,11])),{}).exports}catch(t){}n.prototype.__isLong__,Object.defineProperty(n.prototype,"__isLong__",{value:!0}),n.isLong=h;var f={},l={};n.fromInt=e,n.fromNumber=s,n.fromBits=r;var a=Math.pow;n.fromString=o,n.fromValue=u;var d=4294967296,c=d*d,w=c/2,v=e(1<<24),m=e(0);n.ZERO=m;var p=e(0,!0);n.UZERO=p;var y=e(1);n.ONE=y;var b=e(1,!0);n.UONE=b;var N=e(-1);n.NEG_ONE=N;var E=r(-1,2147483647,!1);n.MAX_VALUE=E;var q=r(-1,-1,!0);n.MAX_UNSIGNED_VALUE=q;var _=r(0,-2147483648,!1);n.MIN_VALUE=_;var B=n.prototype;B.toInt=function(){return this.unsigned?this.low>>>0:this.low},B.toNumber=function(){return this.unsigned?(this.high>>>0)*d+(this.low>>>0):this.high*d+(this.low>>>0)},B.toString=function(t){if((t=t||10)<2||36<t)throw RangeError("radix");if(this.isZero())return"0";if(this.isNegative()){if(this.eq(_)){var i=s(t),n=this.div(i),h=n.mul(i).sub(this);return n.toString(t)+h.toInt().toString(t)}return"-"+this.neg().toString(t)}for(var e=s(a(t,6),this.unsigned),r=this,o="";;){var u=r.div(e),g=r.sub(u.mul(e)).toInt()>>>0,f=g.toString(t);if(r=u,r.isZero())return f+o;for(;f.length<6;)f="0"+f;o=""+f+o}},B.getHighBits=function(){return this.high},B.getHighBitsUnsigned=function(){return this.high>>>0},B.getLowBits=function(){return this.low},B.getLowBitsUnsigned=function(){return this.low>>>0},B.getNumBitsAbs=function(){if(this.isNegative())return this.eq(_)?64:this.neg().getNumBitsAbs();for(var t=0!=this.high?this.high:this.low,i=31;i>0&&0==(t&1<<i);i--);return 0!=this.high?i+33:i+1},B.isZero=function(){return 0===this.high&&0===this.low},B.eqz=B.isZero,B.isNegative=function(){return!this.unsigned&&this.high<0},B.isPositive=function(){return this.unsigned||this.high>=0},B.isOdd=function(){return 1==(1&this.low)},B.isEven=function(){return 0==(1&this.low)},B.equals=function(t){return h(t)||(t=u(t)),(this.unsigned===t.unsigned||this.high>>>31!=1||t.high>>>31!=1)&&(this.high===t.high&&this.low===t.low)},B.eq=B.equals,B.notEquals=function(t){return!this.eq(t)},B.neq=B.notEquals,B.ne=B.notEquals,B.lessThan=function(t){return this.comp(t)<0},B.lt=B.lessThan,B.lessThanOrEqual=function(t){return this.comp(t)<=0},B.lte=B.lessThanOrEqual,B.le=B.lessThanOrEqual,B.greaterThan=function(t){return this.comp(t)>0},B.gt=B.greaterThan,B.greaterThanOrEqual=function(t){return this.comp(t)>=0},B.gte=B.greaterThanOrEqual,B.ge=B.greaterThanOrEqual,B.compare=function(t){if(h(t)||(t=u(t)),this.eq(t))return 0;var i=this.isNegative(),n=t.isNegative();return i&&!n?-1:!i&&n?1:this.unsigned?t.high>>>0>this.high>>>0||t.high===this.high&&t.low>>>0>this.low>>>0?-1:1:this.sub(t).isNegative()?-1:1},B.comp=B.compare,B.negate=function(){return!this.unsigned&&this.eq(_)?_:this.not().add(y)},B.neg=B.negate,B.add=function(t){h(t)||(t=u(t));var i=this.high>>>16,n=65535&this.high,e=this.low>>>16,s=65535&this.low,o=t.high>>>16,g=65535&t.high,f=t.low>>>16,l=65535&t.low,a=0,d=0,c=0,w=0;return w+=s+l,c+=w>>>16,w&=65535,c+=e+f,d+=c>>>16,c&=65535,d+=n+g,a+=d>>>16,d&=65535,a+=i+o,a&=65535,r(c<<16|w,a<<16|d,this.unsigned)},B.subtract=function(t){return h(t)||(t=u(t)),this.add(t.neg())},B.sub=B.subtract,B.multiply=function(t){if(this.isZero())return m;if(h(t)||(t=u(t)),g){return r(g.mul(this.low,this.high,t.low,t.high),g.get_high(),this.unsigned)}if(t.isZero())return m;if(this.eq(_))return t.isOdd()?_:m;if(t.eq(_))return this.isOdd()?_:m;if(this.isNegative())return t.isNegative()?this.neg().mul(t.neg()):this.neg().mul(t).neg();if(t.isNegative())return this.mul(t.neg()).neg();if(this.lt(v)&&t.lt(v))return s(this.toNumber()*t.toNumber(),this.unsigned);var i=this.high>>>16,n=65535&this.high,e=this.low>>>16,o=65535&this.low,f=t.high>>>16,l=65535&t.high,a=t.low>>>16,d=65535&t.low,c=0,w=0,p=0,y=0;return y+=o*d,p+=y>>>16,y&=65535,p+=e*d,w+=p>>>16,p&=65535,p+=o*a,w+=p>>>16,p&=65535,w+=n*d,c+=w>>>16,w&=65535,w+=e*a,c+=w>>>16,w&=65535,w+=o*l,c+=w>>>16,w&=65535,c+=i*d+n*a+e*l+o*f,c&=65535,r(p<<16|y,c<<16|w,this.unsigned)},B.mul=B.multiply,B.divide=function(t){if(h(t)||(t=u(t)),t.isZero())throw Error("division by zero");if(g){if(!this.unsigned&&-2147483648===this.high&&-1===t.low&&-1===t.high)return this;return r((this.unsigned?g.div_u:g.div_s)(this.low,this.high,t.low,t.high),g.get_high(),this.unsigned)}if(this.isZero())return this.unsigned?p:m;var i,n,e;if(this.unsigned){if(t.unsigned||(t=t.toUnsigned()),t.gt(this))return p;if(t.gt(this.shru(1)))return b;e=p}else{if(this.eq(_)){if(t.eq(y)||t.eq(N))return _;if(t.eq(_))return y;return i=this.shr(1).div(t).shl(1),i.eq(m)?t.isNegative()?y:N:(n=this.sub(t.mul(i)),e=i.add(n.div(t)))}if(t.eq(_))return this.unsigned?p:m;if(this.isNegative())return t.isNegative()?this.neg().div(t.neg()):this.neg().div(t).neg();if(t.isNegative())return this.div(t.neg()).neg();e=m}for(n=this;n.gte(t);){i=Math.max(1,Math.floor(n.toNumber()/t.toNumber()));for(var o=Math.ceil(Math.log(i)/Math.LN2),f=o<=48?1:a(2,o-48),l=s(i),d=l.mul(t);d.isNegative()||d.gt(n);)i-=f,l=s(i,this.unsigned),d=l.mul(t);l.isZero()&&(l=y),e=e.add(l),n=n.sub(d)}return e},B.div=B.divide,B.modulo=function(t){if(h(t)||(t=u(t)),g){return r((this.unsigned?g.rem_u:g.rem_s)(this.low,this.high,t.low,t.high),g.get_high(),this.unsigned)}return this.sub(this.div(t).mul(t))},B.mod=B.modulo,B.rem=B.modulo,B.not=function(){return r(~this.low,~this.high,this.unsigned)},B.and=function(t){return h(t)||(t=u(t)),r(this.low&t.low,this.high&t.high,this.unsigned)},B.or=function(t){return h(t)||(t=u(t)),r(this.low|t.low,this.high|t.high,this.unsigned)},B.xor=function(t){return h(t)||(t=u(t)),r(this.low^t.low,this.high^t.high,this.unsigned)},B.shiftLeft=function(t){return h(t)&&(t=t.toInt()),0==(t&=63)?this:t<32?r(this.low<<t,this.high<<t|this.low>>>32-t,this.unsigned):r(0,this.low<<t-32,this.unsigned)},B.shl=B.shiftLeft,B.shiftRight=function(t){return h(t)&&(t=t.toInt()),0==(t&=63)?this:t<32?r(this.low>>>t|this.high<<32-t,this.high>>t,this.unsigned):r(this.high>>t-32,this.high>=0?0:-1,this.unsigned)},B.shr=B.shiftRight,B.shiftRightUnsigned=function(t){return h(t)&&(t=t.toInt()),0==(t&=63)?this:t<32?r(this.low>>>t|this.high<<32-t,this.high>>>t,this.unsigned):32===t?r(this.high,0,this.unsigned):r(this.high>>>t-32,0,this.unsigned)},B.shru=B.shiftRightUnsigned,B.shr_u=B.shiftRightUnsigned,B.rotateLeft=function(t){var i;return h(t)&&(t=t.toInt()),0==(t&=63)?this:32===t?r(this.high,this.low,this.unsigned):t<32?(i=32-t,r(this.low<<t|this.high>>>i,this.high<<t|this.low>>>i,this.unsigned)):(t-=32,i=32-t,r(this.high<<t|this.low>>>i,this.low<<t|this.high>>>i,this.unsigned))},B.rotl=B.rotateLeft,B.rotateRight=function(t){var i;return h(t)&&(t=t.toInt()),0==(t&=63)?this:32===t?r(this.high,this.low,this.unsigned):t<32?(i=32-t,r(this.high<<i|this.low>>>t,this.low<<i|this.high>>>t,this.unsigned)):(t-=32,i=32-t,r(this.low<<i|this.high>>>t,this.high<<i|this.low>>>t,this.unsigned))},B.rotr=B.rotateRight,B.toSigned=function(){return this.unsigned?r(this.low,this.high,!1):this},B.toUnsigned=function(){return this.unsigned?this:r(this.low,this.high,!0)},B.toBytes=function(t){return t?this.toBytesLE():this.toBytesBE()},B.toBytesLE=function(){var t=this.high,i=this.low;return[255&i,i>>>8&255,i>>>16&255,i>>>24,255&t,t>>>8&255,t>>>16&255,t>>>24]},B.toBytesBE=function(){var t=this.high,i=this.low;return[t>>>24,t>>>16&255,t>>>8&255,255&t,i>>>24,i>>>16&255,i>>>8&255,255&i]},n.fromBytes=function(t,i,h){return h?n.fromBytesLE(t,i):n.fromBytesBE(t,i)},n.fromBytesLE=function(t,i){return new n(t[0]|t[1]<<8|t[2]<<16|t[3]<<24,t[4]|t[5]<<8|t[6]<<16|t[7]<<24,i)},n.fromBytesBE=function(t,i){return new n(t[4]<<24|t[5]<<16|t[6]<<8|t[7],t[0]<<24|t[1]<<16|t[2]<<8|t[3],i)}}])});
package executor
import (
"encoding/json"
"fmt"
"strings"
"sync"
"sync/atomic"
"github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/common/db/table"
"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/robertkrimen/otto"
)
//json 表格相关的实现
//用户可以存入一个json字符串
//json 定义index 以及 format
/* table ->
{
"#tablename" : "table1",
"#primary" : "abc",
"abc" : "%18d",
"index1" : "%s",
"index2" : "%s",
}
默认值配置
{
"abc" : 0,
"index1" : "",
"index2" : "",
}
*/
var globalTableHandle sync.Map
var globalHanldeID int64
//NewTable 创建一个新的表格, 返回handle
func (u *js) newTable(name, config, defaultvalue string) (id int64, err error) {
for {
id = atomic.AddInt64(&globalHanldeID, 1) % maxjsint
if _, ok := globalTableHandle.Load(id); ok {
continue
}
if id < 0 {
atomic.StoreInt64(&globalHanldeID, 0)
continue
}
break
}
row, err := NewJSONRow(config, defaultvalue)
if err != nil {
return 0, err
}
var kvdb db.KV
var prefix []byte
if row.config["#db"] == "localdb" {
kvdb = u.GetLocalDB()
_, prefix = calcAllPrefix(name)
} else if row.config["#db"] == "statedb" {
kvdb = u.GetStateDB()
prefix, _ = calcAllPrefix(name)
} else {
return 0, ptypes.ErrDBType
}
var indexs []string
for k := range row.config {
if k[0] == '#' {
continue
}
indexs = append(indexs, k)
}
opt := &table.Option{
Prefix: string(prefix),
Name: row.config["#tablename"],
Primary: row.config["#primary"],
Index: indexs,
}
t, err := table.NewTable(row, kvdb, opt)
if err != nil {
return 0, err
}
globalTableHandle.Store(id, t)
return id, nil
}
func (u *js) newTableFunc(vm *otto.Otto, name string) {
vm.Set("table_new", func(call otto.FunctionCall) otto.Value {
config, err := call.Argument(0).ToString()
if err != nil {
return errReturn(vm, err)
}
defaultvalue, err := call.Argument(1).ToString()
if err != nil {
return errReturn(vm, err)
}
id, err := u.newTable(name, config, defaultvalue)
if err != nil {
return errReturn(vm, err)
}
return newObject(vm).setValue("id", id).value()
})
}
//CloseTable 关闭表格释放内存
func closeTable(id int64) error {
_, ok := globalTableHandle.Load(id)
if !ok {
return types.ErrNotFound
}
globalTableHandle.Delete(id)
return nil
}
func getTable(id int64) (*table.Table, error) {
if value, ok := globalTableHandle.Load(id); ok {
return value.(*table.Table), nil
}
return nil, types.ErrNotFound
}
func getSaver(id int64) (saver, error) {
if value, ok := globalTableHandle.Load(id); ok {
return value.(saver), nil
}
return nil, types.ErrNotFound
}
func registerTableFunc(vm *otto.Otto) {
tableAddFunc(vm)
tableReplaceFunc(vm)
tableDelFunc(vm)
tableCloseFunc(vm)
tableSave(vm)
tableJoinFunc(vm)
}
func tableAddFunc(vm *otto.Otto) {
vm.Set("table_add", func(call otto.FunctionCall) otto.Value {
id, err := call.Argument(0).ToInteger()
if err != nil {
return errReturn(vm, err)
}
tab, err := getTable(id)
if err != nil {
return errReturn(vm, err)
}
json, err := call.Argument(1).ToString()
if err != nil {
return errReturn(vm, err)
}
err = tab.Add(&jsproto.JsLog{Data: json})
if err != nil {
return errReturn(vm, err)
}
return okReturn(vm, "ok")
})
}
func tableReplaceFunc(vm *otto.Otto) {
vm.Set("table_replace", func(call otto.FunctionCall) otto.Value {
id, err := call.Argument(0).ToInteger()
if err != nil {
return errReturn(vm, err)
}
tab, err := getTable(id)
if err != nil {
return errReturn(vm, err)
}
json, err := call.Argument(1).ToString()
if err != nil {
return errReturn(vm, err)
}
err = tab.Replace(&jsproto.JsLog{Data: json})
if err != nil {
return errReturn(vm, err)
}
return okReturn(vm, "ok")
})
}
func tableDelFunc(vm *otto.Otto) {
vm.Set("table_del", func(call otto.FunctionCall) otto.Value {
id, err := call.Argument(0).ToInteger()
if err != nil {
return errReturn(vm, err)
}
tab, err := getTable(id)
if err != nil {
return errReturn(vm, err)
}
row, err := call.Argument(1).ToString()
if err != nil {
return errReturn(vm, err)
}
err = tab.DelRow(&jsproto.JsLog{Data: row})
if err != nil {
return errReturn(vm, err)
}
return okReturn(vm, "ok")
})
}
type saver interface {
Save() (kvs []*types.KeyValue, err error)
}
func tableSave(vm *otto.Otto) {
vm.Set("table_save", func(call otto.FunctionCall) otto.Value {
id, err := call.Argument(0).ToInteger()
if err != nil {
return errReturn(vm, err)
}
tab, err := getSaver(id)
if err != nil {
return errReturn(vm, err)
}
kvs, err := tab.Save()
if err != nil {
return errReturn(vm, err)
}
return newObject(vm).setValue("kvs", createKVObject(vm, kvs)).value()
})
}
func tableCloseFunc(vm *otto.Otto) {
vm.Set("table_close", func(call otto.FunctionCall) otto.Value {
id, err := call.Argument(0).ToInteger()
if err != nil {
return errReturn(vm, err)
}
err = closeTable(id)
if err != nil {
return errReturn(vm, err)
}
return okReturn(vm, "ok")
})
}
func tableJoinFunc(vm *otto.Otto) {
vm.Set("new_join_table", func(call otto.FunctionCall) otto.Value {
left, err := call.Argument(0).ToInteger()
if err != nil {
return errReturn(vm, err)
}
lefttab, err := getTable(left)
if err != nil {
return errReturn(vm, err)
}
right, err := call.Argument(1).ToInteger()
if err != nil {
return errReturn(vm, err)
}
righttab, err := getTable(right)
if err != nil {
return errReturn(vm, err)
}
index, err := call.Argument(2).ToString()
if err != nil {
return errReturn(vm, err)
}
join, err := table.NewJoinTable(lefttab, righttab, strings.Split(index, ","))
if err != nil {
return errReturn(vm, err)
}
var id int64
for {
id = atomic.AddInt64(&globalHanldeID, 1) % maxjsint
if _, ok := globalTableHandle.Load(id); ok {
continue
}
if id < 0 {
atomic.StoreInt64(&globalHanldeID, 0)
continue
}
break
}
globalTableHandle.Store(id, join)
return newObject(vm).setValue("id", id).value()
})
}
/*
table
要开发一个适合json的table, row 就是一个 js object
handle := new_table(config, defaultvalue)
table_add(handle, row)
table_replace(handle, row)
table_del(handle, row)
table_save(handle)
table_close(handle)
handle := new_join_table(left, right, listofjoinindex)
*/
//join table 的操作(接口完全相同)
//handle3 := new_table(newcofifg{config1, config2})
//JSONRow meta
type JSONRow struct {
*jsproto.JsLog
config map[string]string
data map[string]interface{}
}
//NewJSONRow 创建一个row
func NewJSONRow(config string, defaultvalue string) (*JSONRow, error) {
row := &JSONRow{}
row.config = make(map[string]string)
err := json.Unmarshal([]byte(config), row.config)
if err != nil {
return nil, err
}
row.JsLog = &jsproto.JsLog{Data: defaultvalue}
err = row.parse()
if err != nil {
return nil, err
}
return row, nil
}
//CreateRow 创建一行
func (row *JSONRow) CreateRow() *table.Row {
return &table.Row{Data: &jsproto.JsLog{}}
}
func (row *JSONRow) parse() error {
row.data = make(map[string]interface{})
return json.Unmarshal([]byte(row.JsLog.Data), row.data)
}
//SetPayload 设置行的内容
func (row *JSONRow) SetPayload(data types.Message) error {
if rowdata, ok := data.(*jsproto.JsLog); ok {
row.JsLog = rowdata
return row.parse()
}
return types.ErrTypeAsset
}
//Get value of row
func (row *JSONRow) Get(key string) ([]byte, error) {
if format, ok := row.config[key]; ok {
if data, ok := row.data[key]; ok {
return []byte(fmt.Sprintf(format, data)), nil
}
}
return nil, types.ErrNotFound
}
//数据结构设计
//kvlist [{key:"key1", value:"value1"},{key:"key2", value:"value2"}]
//log 设计 {json data}
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()
}
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) {
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)
}
......@@ -30,7 +30,7 @@ var (
)
//JsX 插件名字
var JsX = "js"
var JsX = "jsvm"
//错误常量
var (
......@@ -44,6 +44,9 @@ var (
ErrInvalidFuncPrefix = errors.New("chain33.js: invalid function prefix format")
//ErrFuncNotFound 函数没有找到
ErrFuncNotFound = errors.New("chain33.js: invalid function name not found")
ErrSymbolName = errors.New("chain33.js: ErrSymbolName")
ErrExecerName = errors.New("chain33.js: ErrExecerName")
ErrDBType = errors.New("chain33.js: ErrDBType")
)
func init() {
......
......@@ -9,31 +9,44 @@ import (
"github.com/golang/protobuf/proto"
)
func safeAdd(balance, amount int64) (int64, error) {
if balance+amount < amount || balance+amount > types.MaxTokenBalance {
return balance, types.ErrAmount
}
return balance + amount, nil
}
// GenesisInit 生成创世地址账户收据
func (acc *DB) GenesisInit(addr string, amount int64) (*types.Receipt, error) {
func (acc *DB) GenesisInit(addr string, amount int64) (receipt *types.Receipt, err error) {
accTo := acc.LoadAccount(addr)
copyto := *accTo
accTo.Balance = accTo.GetBalance() + amount
accTo.Balance, err = safeAdd(accTo.GetBalance(), amount)
if err != nil {
return nil, err
}
receiptBalanceTo := &types.ReceiptAccountTransfer{
Prev: &copyto,
Current: accTo,
}
acc.SaveAccount(accTo)
receipt := acc.genesisReceipt(accTo, receiptBalanceTo)
receipt = acc.genesisReceipt(accTo, receiptBalanceTo)
return receipt, nil
}
// GenesisInitExec 生成创世地址执行器账户收据
func (acc *DB) GenesisInitExec(addr string, amount int64, execaddr string) (*types.Receipt, error) {
func (acc *DB) GenesisInitExec(addr string, amount int64, execaddr string) (receipt *types.Receipt, err error) {
accTo := acc.LoadAccount(execaddr)
copyto := *accTo
accTo.Balance = accTo.GetBalance() + amount
accTo.Balance, err = safeAdd(accTo.GetBalance(), amount)
if err != nil {
return nil, err
}
receiptBalanceTo := &types.ReceiptAccountTransfer{
Prev: &copyto,
Current: accTo,
}
acc.SaveAccount(accTo)
receipt := acc.genesisReceipt(accTo, receiptBalanceTo)
receipt = acc.genesisReceipt(accTo, receiptBalanceTo)
receipt2, err := acc.ExecDeposit(addr, execaddr, amount)
if err != nil {
panic(err)
......
......@@ -395,8 +395,9 @@ func (table *Table) Del(primaryKey []byte) error {
return err
}
if incache {
rowty := row.Ty
table.delRowCache(row)
if row.Ty == Add {
if rowty == Add {
return nil
}
}
......@@ -407,6 +408,15 @@ func (table *Table) Del(primaryKey []byte) error {
return nil
}
//DelRow 删除一行
func (table *Table) DelRow(data types.Message) error {
primaryKey, err := table.primaryKey(data)
if err != nil {
return err
}
return table.Del(primaryKey)
}
//getDataKey data key 构造
func (table *Table) getDataKey(primaryKey []byte) []byte {
return append([]byte(table.dataprefix), primaryKey...)
......
......@@ -300,7 +300,7 @@ func TestDel(t *testing.T) {
//save 然后从列表中读取
kvs, err := table.Save()
assert.Nil(t, err)
assert.Equal(t, len(kvs), 6)
assert.Equal(t, 3, len(kvs))
//save to database
util.SaveKVList(ldb, kvs)
//printKV(kvs)
......
......@@ -105,6 +105,14 @@ func (c *KVCreator) AddNoPrefix(key, value []byte) *KVCreator {
return c.addnoprefix(key, value, true)
}
//AddListNoPrefix only add KVList
func (c *KVCreator) AddListNoPrefix(list []*types.KeyValue) *KVCreator {
for _, kv := range list {
c.addnoprefix(kv.Key, kv.Value, true)
}
return c
}
//AddList only add KVList
func (c *KVCreator) AddList(list []*types.KeyValue) *KVCreator {
for _, kv := range list {
......
......@@ -28,15 +28,21 @@ func TestKVCreator(t *testing.T) {
{Key: []byte("l1"), Value: []byte("vl1")},
{Key: []byte("l2"), Value: []byte("vl2")},
})
creator.AddListNoPrefix([]*types.KeyValue{
{Key: []byte("l1"), Value: []byte("vl1")},
{Key: []byte("l2"), Value: []byte("vl2")},
})
creator.Add([]byte("c1"), nil)
creator.Add([]byte("l2"), nil)
creator.AddRollbackKV()
assert.Equal(t, 7, len(creator.KVList()))
assert.Equal(t, 9, len(creator.KVList()))
util.SaveKVList(ldb, creator.KVList())
kvs, err := creator.GetRollbackKVList()
assert.Nil(t, err)
assert.Equal(t, 6, len(kvs))
assert.Equal(t, []byte("b"), kvs[5].Value)
assert.Equal(t, 8, len(kvs))
assert.Equal(t, []byte("b"), kvs[7].Value)
assert.Equal(t, []byte(nil), kvs[6].Value)
assert.Equal(t, []byte(nil), kvs[5].Value)
assert.Equal(t, []byte(nil), kvs[4].Value)
assert.Equal(t, []byte(nil), kvs[3].Value)
assert.Equal(t, []byte(nil), kvs[2].Value)
......
......@@ -130,7 +130,12 @@ func RunChain33(name string) {
q := queue.New("channel")
log.Info("loading mempool module")
mem := mempool.New(cfg.Mempool, sub.Mempool)
var mem queue.Module
if !types.IsPara() {
mem = mempool.New(cfg.Mempool, sub.Mempool)
} else {
mem = &util.MockModule{Key: "mempool"}
}
mem.SetQueueClient(q.Client())
log.Info("loading execs module")
......@@ -150,12 +155,15 @@ func RunChain33(name string) {
cs := consensus.New(cfg.Consensus, sub.Consensus)
cs.SetQueueClient(q.Client())
var network *p2p.P2p
if cfg.P2P.Enable {
log.Info("loading p2p module")
log.Info("loading p2p module")
var network queue.Module
if cfg.P2P.Enable && !types.IsPara() {
network = p2p.New(cfg.P2P)
network.SetQueueClient(q.Client())
} else {
network = &util.MockModule{Key: "p2p"}
}
network.SetQueueClient(q.Client())
//jsonrpc, grpc, channel 三种模式
rpcapi := rpc.New(cfg.RPC)
rpcapi.SetQueueClient(q.Client())
......@@ -169,10 +177,8 @@ func RunChain33(name string) {
chain.Close()
log.Info("begin close mempool module")
mem.Close()
if cfg.P2P.Enable {
log.Info("begin close P2P module")
network.Close()
}
log.Info("begin close P2P module")
network.Close()
log.Info("begin close execs module")
exec.Close()
log.Info("begin close store module")
......
......@@ -453,3 +453,25 @@ func PrintKV(kvs []*types.KeyValue) {
fmt.Printf("KV %d %s(%s)\n", i, string(kvs[i].Key), common.ToHex(kvs[i].Value))
}
}
// MockModule struct
type MockModule struct {
Key string
}
// SetQueueClient method
func (m *MockModule) SetQueueClient(client queue.Client) {
go func() {
client.Sub(m.Key)
for msg := range client.Recv() {
msg.Reply(client.NewMessage(m.Key, types.EventReply, &types.Reply{IsOk: false,
Msg: []byte(fmt.Sprintf("mock %s module not handle message %v", m.Key, msg.Ty))}))
}
}()
}
// Wait for ready
func (m *MockModule) Wait() {}
// Close method
func (m *MockModule) Close() {}
......@@ -8,7 +8,7 @@ import (
"testing"
"github.com/33cn/chain33/common/address"
"github.com/33cn/chain33/queue"
"github.com/33cn/chain33/types"
"github.com/stretchr/testify/assert"
......@@ -187,3 +187,20 @@ func TestDB(t *testing.T) {
assert.Nil(t, err)
assert.Equal(t, value, []byte("b"))
}
func TestMockModule(t *testing.T) {
q := queue.New("channel")
client := q.Client()
memKey := "mempool"
mem := &MockModule{Key: memKey}
mem.SetQueueClient(client)
msg := client.NewMessage(memKey, types.EventTx, &types.Transaction{})
client.Send(msg, true)
resp, err := client.Wait(msg)
assert.Nil(t, err)
reply, ok := resp.GetData().(*types.Reply)
assert.Equal(t, ok, true)
assert.Equal(t, reply.GetIsOk(), false)
assert.Equal(t, reply.GetMsg(), []byte("mock mempool module not handle message 1"))
}
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