Commit bcdffcf1 authored by vipwzw's avatar vipwzw

add js test

parent 5a3e37b1
package executor
import "errors"
//ErrInvalidFuncFormat 错误的函数调用格式(没有_)
var errInvalidFuncFormat = errors.New("chain33.js: invalid function name format")
//ErrInvalidFuncPrefix not exec_ execloal execdellocal
var errInvalidFuncPrefix = errors.New("chain33.js: invalid function prefix format")
//ErrFuncNotFound 函数没有找到
var errFuncNotFound = errors.New("chain33.js: invalid function name not found")
var callcode = `
function kvcreator(dbtype) {
this.data = {}
this.kvs = []
this.logs = []
if (dbtype == "exec" || dbtype == "init") {
this.get = getstatedb
} else if (dbtype == "local") {
this.get = getlocaldb
this.list = listdb
}
}
kvcreator.prototype.add = function(k, v) {
var data = JSON.stringify(v)
this.data[k] = data
this.kvs.push({key:k, value: data})
}
kvcreator.prototype.get = function(k) {
var v
if (this.data[k]) {
v = this.data[k]
} else {
v = this.get(k)
}
if (!v) {
return null
}
return JSON.parse(v)
}
kvcreator.prototype.listvalue = function(prefix, key, count, direction) {
var values = this.list(prefix, key, count, direction)
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) {
if (this.list) {
throw new Error("local or dellocal can't set log")
}
this.logs.push(JSON.stringify(log))
}
kvcreator.prototype.receipt = function() {
return {kvs: this.kvs, logs: this.logs}
}
function callcode(context, f, args, loglist) {
if (f == "init") {
return Init(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 {
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)
}
`
package executor package executor
import ( import (
"github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types" "github.com/33cn/chain33/types"
ptypes "github.com/33cn/plugin/plugin/dapp/js/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) Exec_Create(payload *jsproto.Create, tx *types.Transaction, index int) (*types.Receipt, error) { func (c *js) Exec_Create(payload *jsproto.Create, tx *types.Transaction, index int) (*types.Receipt, error) {
db := c.GetStateDB() execer := types.ExecName("user.js." + payload.Name)
_, err := db.Get(calcCodeKey(payload.Name)) kvc := dapp.NewKVCreator(c.GetStateDB(), calcStatePrefix([]byte(execer)), nil)
_, err := kvc.GetNoPrefix(calcCodeKey(payload.Name))
if err != nil && err != types.ErrNotFound { if err != nil && err != types.ErrNotFound {
return nil, err return nil, err
} }
if err == nil { if err == nil {
return nil, ptypes.ErrDupName return nil, ptypes.ErrDupName
} }
r := &types.Receipt{Ty: types.ExecOk} kvc.AddNoPrefix(calcCodeKey(payload.Name), []byte(payload.Code))
//code must be utf-8 encoding jsvalue, err := c.callVM("init", &jsproto.Call{Name: payload.Name}, tx, index, nil)
r.KV = append(r.KV, &types.KeyValue{ if err != nil {
Key: calcCodeKey(payload.Name), Value: []byte(payload.Code)}) return nil, err
}
kvs, logs, err := parseJsReturn(jsvalue)
if err != nil {
return nil, err
}
kvc.AddList(kvs)
r := &types.Receipt{Ty: types.ExecOk, KV: kvc.KVList(), Logs: logs}
return r, nil return r, nil
} }
func (c *js) Exec_Call(payload *jsproto.Call, tx *types.Transaction, index int) (*types.Receipt, error) { func (c *js) Exec_Call(payload *jsproto.Call, tx *types.Transaction, index int) (*types.Receipt, error) {
jsvalue, err := c.callVM("exec", payload, tx, index) execer := types.ExecName("user.js." + payload.Name)
kvc := dapp.NewKVCreator(c.GetStateDB(), []byte(execer), nil)
jsvalue, err := c.callVM("exec", payload, tx, index, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
r := &types.Receipt{Ty: types.ExecOk} kvs, logs, err := parseJsReturn(jsvalue)
kvs, err := parseKVS(jsvalue)
if err != nil { if err != nil {
return nil, err return nil, err
} }
r.KV = kvs kvc.AddList(kvs)
r := &types.Receipt{Ty: types.ExecOk, KV: kvc.KVList(), Logs: logs}
return r, nil return r, nil
} }
package executor package executor
import ( import (
"github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types" "github.com/33cn/chain33/types"
"github.com/33cn/plugin/plugin/dapp/js/types/jsproto" "github.com/33cn/plugin/plugin/dapp/js/types/jsproto"
) )
...@@ -10,15 +11,15 @@ func (c *js) ExecDelLocal_Create(payload *jsproto.Create, tx *types.Transaction, ...@@ -10,15 +11,15 @@ 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) { func (c *js) ExecDelLocal_Call(payload *jsproto.Call, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
jsvalue, err := c.callVM("execdellocal", payload, tx, index) krollback := calcRollbackKey(tx.Hash())
kvc := dapp.NewKVCreator(c.GetLocalDB(), calcLocalPrefix(tx.Execer), krollback)
kvs, err := kvc.GetRollbackKVList()
if err != nil { if err != nil {
return nil, err return nil, err
} }
kvc.AddKVListOnly(kvs)
kvc.DelRollbackKV()
r := &types.LocalDBSet{} r := &types.LocalDBSet{}
kvs, err := parseKVS(jsvalue) r.KV = kvc.KVList()
if err != nil {
return nil, err
}
r.KV = kvs
return r, nil return r, nil
} }
package executor package executor
import ( import (
"github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types" "github.com/33cn/chain33/types"
"github.com/33cn/plugin/plugin/dapp/js/types/jsproto" "github.com/33cn/plugin/plugin/dapp/js/types/jsproto"
) )
...@@ -10,15 +11,19 @@ func (c *js) ExecLocal_Create(payload *jsproto.Create, tx *types.Transaction, re ...@@ -10,15 +11,19 @@ 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) { func (c *js) ExecLocal_Call(payload *jsproto.Call, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
jsvalue, err := c.callVM("execlocal", payload, tx, index) k := calcRollbackKey(tx.Hash())
kvc := dapp.NewKVCreator(c.GetLocalDB(), calcLocalPrefix(tx.Execer), k)
jsvalue, err := c.callVM("execlocal", payload, tx, index, receiptData)
if err != nil { if err != nil {
return nil, err return nil, err
} }
r := &types.LocalDBSet{} kvs, _, err := parseJsReturn(jsvalue)
kvs, err := parseKVS(jsvalue)
if err != nil { if err != nil {
return nil, err return nil, err
} }
r.KV = kvs kvc.AddList(kvs)
kvc.AddRollbackKV()
r := &types.LocalDBSet{}
r.KV = kvc.KVList()
return r, nil return r, nil
} }
...@@ -49,7 +49,8 @@ func (u *js) GetDriverName() string { ...@@ -49,7 +49,8 @@ func (u *js) GetDriverName() string {
return driverName return driverName
} }
func (u *js) callVM(prefix string, payload *jsproto.Call, tx *types.Transaction, index int) (*otto.Object, error) { func (u *js) callVM(prefix string, payload *jsproto.Call, tx *types.Transaction,
index int, receiptData *types.ReceiptData) (*otto.Object, error) {
vm, err := u.createVM(tx, index) vm, err := u.createVM(tx, index)
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -59,10 +60,19 @@ func (u *js) callVM(prefix string, payload *jsproto.Call, tx *types.Transaction, ...@@ -59,10 +60,19 @@ func (u *js) callVM(prefix string, payload *jsproto.Call, tx *types.Transaction,
if err != nil { if err != nil {
return nil, err return nil, err
} }
loglist, err := jslogs(receiptData)
if err != nil {
return nil, err
}
vm.Set("loglist", loglist)
vm.Set("code", code) vm.Set("code", code)
vm.Set("f", prefix+"_"+payload.Funcname) if prefix == "init" {
vm.Set("f", "init")
} else {
vm.Set("f", prefix+"_"+payload.Funcname)
}
vm.Set("args", payload.Args) vm.Set("args", payload.Args)
callfunc := "callcode(context, f, args)" callfunc := "callcode(context, f, args, loglist)"
jsvalue, err := vm.Run(callcode + string(code) + "\n" + callfunc) jsvalue, err := vm.Run(callcode + string(code) + "\n" + callfunc)
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -73,6 +83,26 @@ func (u *js) callVM(prefix string, payload *jsproto.Call, tx *types.Transaction, ...@@ -73,6 +83,26 @@ func (u *js) callVM(prefix string, payload *jsproto.Call, tx *types.Transaction,
return jsvalue.Object(), nil return jsvalue.Object(), nil
} }
func jslogs(receiptData *types.ReceiptData) ([]string, error) {
data := make([]string, 0)
if receiptData == nil {
return data, nil
}
for i := 0; i < len(receiptData.Logs); i++ {
logitem := receiptData.Logs[i]
if logitem.Ty != ptypes.TyLogJs {
continue
}
var jslog jsproto.JsLog
err := types.Decode(logitem.Log, &jslog)
if err != nil {
return nil, err
}
data = append(data, jslog.Data)
}
return data, nil
}
func (u *js) getContext(tx *types.Transaction, index int64) *blockContext { func (u *js) getContext(tx *types.Transaction, index int64) *blockContext {
return &blockContext{ return &blockContext{
Height: u.GetHeight(), Height: u.GetHeight(),
......
...@@ -6,44 +6,10 @@ import ( ...@@ -6,44 +6,10 @@ import (
"github.com/33cn/chain33/types" "github.com/33cn/chain33/types"
ptypes "github.com/33cn/plugin/plugin/dapp/js/types" ptypes "github.com/33cn/plugin/plugin/dapp/js/types"
"github.com/33cn/plugin/plugin/dapp/js/types/jsproto"
"github.com/robertkrimen/otto" "github.com/robertkrimen/otto"
) )
//ErrInvalidFuncFormat 错误的函数调用格式(没有_)
var errInvalidFuncFormat = errors.New("chain33.js: invalid function name format")
//ErrInvalidFuncPrefix not exec_ execloal execdellocal
var errInvalidFuncPrefix = errors.New("chain33.js: invalid function prefix format")
//ErrFuncNotFound 函数没有找到
var errFuncNotFound = errors.New("chain33.js: invalid function name not found")
var callcode = `
function callcode(context, f, args) {
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 = {}
if (prefix == "exec") {
runobj = new Exec(JSON.parse(context))
} else if (prefix == "execlocal") {
runobj = new ExecLocal(JSON.parse(context))
} else if (prefix == "execdellocal") {
runobj = new ExecDelLocal(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)
}
`
type blockContext struct { type blockContext struct {
Height int64 `json:"height"` Height int64 `json:"height"`
Blocktime int64 `json:"blocktime"` Blocktime int64 `json:"blocktime"`
...@@ -65,30 +31,52 @@ type listdbReturn struct { ...@@ -65,30 +31,52 @@ type listdbReturn struct {
Err string `json:"err"` Err string `json:"err"`
} }
func parseKVS(jsvalue *otto.Object) (kvlist []*types.KeyValue, err error) { func parseJsReturn(jsvalue *otto.Object) (kvlist []*types.KeyValue, logs []*types.ReceiptLog, err error) {
//kvs
obj, err := getObject(jsvalue, "kvs") obj, err := getObject(jsvalue, "kvs")
if err != nil { if err != nil {
return nil, ptypes.ErrJsReturnKVSFormat return nil, nil, ptypes.ErrJsReturnKVSFormat
} }
if obj.Class() != "Array" { if obj.Class() != "Array" {
return nil, ptypes.ErrJsReturnKVSFormat return nil, nil, ptypes.ErrJsReturnKVSFormat
} }
size, err := getInt(obj, "length") size, err := getInt(obj, "length")
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
for i := 0; i < int(size); i++ { for i := 0; i < int(size); i++ {
data, err := getObject(obj, fmt.Sprint(i)) data, err := getObject(obj, fmt.Sprint(i))
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
kv, err := parseKV(data) kv, err := parseKV(data)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
kvlist = append(kvlist, kv) kvlist = append(kvlist, kv)
} }
return kvlist, nil //logs
obj, err = getObject(jsvalue, "logs")
if err != nil {
return nil, nil, ptypes.ErrJsReturnLogsFormat
}
if obj.Class() != "Array" {
return nil, nil, ptypes.ErrJsReturnLogsFormat
}
size, err = getInt(obj, "length")
if err != nil {
return nil, nil, err
}
for i := 0; i < int(size); i++ {
data, err := getString(obj, fmt.Sprint(i))
if err != nil {
return nil, nil, err
}
l := &types.ReceiptLog{
Ty: ptypes.TyLogJs, Log: types.Encode(&jsproto.JsLog{Data: data})}
logs = append(logs, l)
}
return kvlist, logs, nil
} }
func getString(data *otto.Object, key string) (string, error) { func getString(data *otto.Object, key string) (string, error) {
......
package executor package executor
import ( import (
"encoding/json"
"strings" "strings"
"testing" "testing"
"time"
"github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/types"
"github.com/33cn/chain33/util"
"github.com/33cn/plugin/plugin/dapp/js/types/jsproto"
"github.com/robertkrimen/otto" "github.com/robertkrimen/otto"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
var jscode = ` var jscode = `
function Exec(context) { //数据结构设计
this.context = context //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 ExecLocal(context) { function Exec(context) {
this.kvc = new kvcreator("exec")
this.context = context this.context = context
} }
function ExecDelLocal(context) { function ExecLocal(context, logs) {
this.kvc = new kvcreator("local")
this.context = context this.context = context
this.logs = logs
} }
Exec.prototype.hello = function(args) { Exec.prototype.hello = function(args) {
return {args: args, action:"exec", context: this.context} 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) { ExecLocal.prototype.hello = function(args) {
return {args: args, action:"execlocal", context: this.context} this.kvc.add("args", args)
this.kvc.add("action", "exec")
this.kvc.add("log", this.logs)
this.kvc.add("context", this.context)
return this.kvc.receipt()
} }
`
func initExec(ldb db.DB, kvdb db.KVDB, t *testing.T) *js {
e := newjs().(*js)
e.SetEnv(1, time.Now().Unix(), 1)
e.SetLocalDB(kvdb)
e.SetStateDB(kvdb)
ExecDelLocal.prototype.hello = function(args) { c, tx := createCodeTx("test", jscode)
return {args: args, action:"execdellocal", context: this.context} receipt, err := e.Exec_Create(c, tx, 0)
assert.Nil(t, err)
util.SaveKVList(ldb, receipt.KV)
return e
} }
`
func callJsFunc(context *blockContext, code string, f string, args string) (otto.Value, error) { func createCodeTx(name, jscode string) (*jsproto.Create, *types.Transaction) {
data, err := json.Marshal(context) data := &jsproto.Create{
if err != nil { Code: jscode,
return otto.Value{}, err Name: name,
} }
vm := otto.New() return data, &types.Transaction{Execer: []byte("js"), Payload: types.Encode(data)}
vm.Set("context", string(data))
vm.Set("code", code)
vm.Set("f", f)
vm.Set("args", args)
callfunc := "callcode(context, f, args)"
return vm.Run(callcode + code + "\n" + callfunc)
} }
func callCodeTx(name, f, args string) (*jsproto.Call, *types.Transaction) {
data := &jsproto.Call{
Funcname: f,
Name: name,
Args: args,
}
return data, &types.Transaction{Execer: []byte("js"), Payload: types.Encode(data)}
}
func TestCallcode(t *testing.T) { func TestCallcode(t *testing.T) {
value, err := callJsFunc(&blockContext{Height: 1}, jscode, "exec_hello", `{"hello2":"world2"}`) dir, ldb, kvdb := util.CreateTestDB()
assert.Nil(t, err) defer util.CloseTestDB(dir, ldb)
assert.Equal(t, true, value.IsObject()) e := initExec(ldb, kvdb, t)
action, err := value.Object().Get("action")
assert.Nil(t, err)
assert.Equal(t, "exec", action.String())
args, err := value.Object().Get("args")
assert.Nil(t, err)
arg, err := args.Object().Get("hello2")
assert.Nil(t, err)
assert.Equal(t, "world2", arg.String())
context, err := value.Object().Get("context") call, tx := callCodeTx("test", "hello", `{"hello":"world"}`)
assert.Nil(t, err) receipt, err := e.Exec_Call(call, tx, 0)
cvalue, err := context.Object().Get("height")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "1", cvalue.String()) util.SaveKVList(ldb, receipt.KV)
util.PrintKV(receipt.KV)
}
func TestCallError(t *testing.T) {
dir, ldb, kvdb := util.CreateTestDB()
defer util.CloseTestDB(dir, ldb)
e := initExec(ldb, kvdb, t)
//test call error(invalid json input) //test call error(invalid json input)
_, err = callJsFunc(&blockContext{Height: 1}, jscode, "exec_hello", `{hello2":"world2"}`) call, tx := callCodeTx("test", "hello", `{hello":"world"}`)
_, err := e.callVM("exec", call, tx, 0, nil)
_, ok := err.(*otto.Error) _, ok := err.(*otto.Error)
assert.Equal(t, true, ok) assert.Equal(t, true, ok)
assert.Equal(t, true, strings.Contains(err.Error(), "SyntaxError")) assert.Equal(t, true, strings.Contains(err.Error(), "SyntaxError"))
_, err = callJsFunc(&blockContext{Height: 1}, jscode, "hello", `{"hello2":"world2"}`) call, tx = callCodeTx("test", "hello", `{"hello":"world"}`)
assert.Equal(t, true, strings.Contains(err.Error(), errInvalidFuncFormat.Error())) _, err = e.callVM("hello", call, tx, 0, nil)
_, err = callJsFunc(&blockContext{Height: 1}, jscode, "hello_hello", `{"hello2":"world2"}`) _, ok = err.(*otto.Error)
assert.Equal(t, true, ok)
assert.Equal(t, true, strings.Contains(err.Error(), errInvalidFuncPrefix.Error())) assert.Equal(t, true, strings.Contains(err.Error(), errInvalidFuncPrefix.Error()))
_, err = callJsFunc(&blockContext{Height: 1}, jscode, "exec_hello2", `{"hello2":"world2"}`)
call, tx = callCodeTx("test", "hello2", `{"hello":"world"}`)
_, err = e.callVM("exec", call, tx, 0, nil)
_, ok = err.(*otto.Error)
assert.Equal(t, true, ok)
assert.Equal(t, true, strings.Contains(err.Error(), errFuncNotFound.Error())) assert.Equal(t, true, strings.Contains(err.Error(), errFuncNotFound.Error()))
} }
package executor package executor
func calcLocalPrefix(execer []byte) []byte {
s := append([]byte("LODB-"), execer...)
s = append(s, byte('-'))
return s
}
func calcStatePrefix(execer []byte) []byte {
s := append([]byte("mavl-"), execer...)
s = append(s, byte('-'))
return s
}
func calcCodeKey(name string) []byte { func calcCodeKey(name string) []byte {
return append([]byte("mavl-js-code-"), []byte(name)...) return append([]byte("mavl-js-code-"), []byte(name)...)
} }
func calcRollbackKey(hash []byte) []byte {
return append([]byte("LODB-js-rollback-"), hash...)
}
...@@ -21,3 +21,7 @@ message JsAction { ...@@ -21,3 +21,7 @@ message JsAction {
} }
int32 ty = 3; int32 ty = 3;
} }
message JsLog {
string data = 1;
}
\ No newline at end of file
...@@ -2,6 +2,7 @@ package types ...@@ -2,6 +2,7 @@ package types
import ( import (
"errors" "errors"
"reflect"
"github.com/33cn/chain33/types" "github.com/33cn/chain33/types"
"github.com/33cn/plugin/plugin/dapp/js/types/jsproto" "github.com/33cn/plugin/plugin/dapp/js/types/jsproto"
...@@ -15,8 +16,7 @@ const ( ...@@ -15,8 +16,7 @@ const (
//日志类型 //日志类型
const ( const (
TyLogJsCreate = iota + 1 TyLogJs = 10000
TyLogJsCall
) )
var ( var (
...@@ -24,7 +24,9 @@ var ( ...@@ -24,7 +24,9 @@ var (
"Create": jsActionCreate, "Create": jsActionCreate,
"Call": jsActionCall, "Call": jsActionCall,
} }
logMap = map[int64]*types.LogInfo{} logMap = map[int64]*types.LogInfo{
TyLogJs: {Ty: reflect.TypeOf(jsproto.JsLog{}), Name: "TyLogJs"},
}
) )
//JsX 插件名字 //JsX 插件名字
...@@ -32,9 +34,10 @@ var JsX = "js" ...@@ -32,9 +34,10 @@ var JsX = "js"
//错误常量 //错误常量
var ( var (
ErrDupName = errors.New("ErrDupName") ErrDupName = errors.New("ErrDupName")
ErrJsReturnNotObject = errors.New("ErrJsReturnNotObject") ErrJsReturnNotObject = errors.New("ErrJsReturnNotObject")
ErrJsReturnKVSFormat = errors.New("ErrJsReturnKVSFormat") ErrJsReturnKVSFormat = errors.New("ErrJsReturnKVSFormat")
ErrJsReturnLogsFormat = errors.New("ErrJsReturnLogsFormat")
) )
func init() { func init() {
......
...@@ -31,7 +31,7 @@ func (m *Create) Reset() { *m = Create{} } ...@@ -31,7 +31,7 @@ func (m *Create) Reset() { *m = Create{} }
func (m *Create) String() string { return proto.CompactTextString(m) } func (m *Create) String() string { return proto.CompactTextString(m) }
func (*Create) ProtoMessage() {} func (*Create) ProtoMessage() {}
func (*Create) Descriptor() ([]byte, []int) { func (*Create) Descriptor() ([]byte, []int) {
return fileDescriptor_js_26fa46fa936181bc, []int{0} return fileDescriptor_js_25eaf89ab172ea83, []int{0}
} }
func (m *Create) XXX_Unmarshal(b []byte) error { func (m *Create) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Create.Unmarshal(m, b) return xxx_messageInfo_Create.Unmarshal(m, b)
...@@ -79,7 +79,7 @@ func (m *Call) Reset() { *m = Call{} } ...@@ -79,7 +79,7 @@ func (m *Call) Reset() { *m = Call{} }
func (m *Call) String() string { return proto.CompactTextString(m) } func (m *Call) String() string { return proto.CompactTextString(m) }
func (*Call) ProtoMessage() {} func (*Call) ProtoMessage() {}
func (*Call) Descriptor() ([]byte, []int) { func (*Call) Descriptor() ([]byte, []int) {
return fileDescriptor_js_26fa46fa936181bc, []int{1} return fileDescriptor_js_25eaf89ab172ea83, []int{1}
} }
func (m *Call) XXX_Unmarshal(b []byte) error { func (m *Call) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Call.Unmarshal(m, b) return xxx_messageInfo_Call.Unmarshal(m, b)
...@@ -135,7 +135,7 @@ func (m *JsAction) Reset() { *m = JsAction{} } ...@@ -135,7 +135,7 @@ func (m *JsAction) Reset() { *m = JsAction{} }
func (m *JsAction) String() string { return proto.CompactTextString(m) } func (m *JsAction) String() string { return proto.CompactTextString(m) }
func (*JsAction) ProtoMessage() {} func (*JsAction) ProtoMessage() {}
func (*JsAction) Descriptor() ([]byte, []int) { func (*JsAction) Descriptor() ([]byte, []int) {
return fileDescriptor_js_26fa46fa936181bc, []int{2} return fileDescriptor_js_25eaf89ab172ea83, []int{2}
} }
func (m *JsAction) XXX_Unmarshal(b []byte) error { func (m *JsAction) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_JsAction.Unmarshal(m, b) return xxx_messageInfo_JsAction.Unmarshal(m, b)
...@@ -273,27 +273,67 @@ func _JsAction_OneofSizer(msg proto.Message) (n int) { ...@@ -273,27 +273,67 @@ func _JsAction_OneofSizer(msg proto.Message) (n int) {
return n return n
} }
type JsLog struct {
Data string `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *JsLog) Reset() { *m = JsLog{} }
func (m *JsLog) String() string { return proto.CompactTextString(m) }
func (*JsLog) ProtoMessage() {}
func (*JsLog) Descriptor() ([]byte, []int) {
return fileDescriptor_js_25eaf89ab172ea83, []int{3}
}
func (m *JsLog) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_JsLog.Unmarshal(m, b)
}
func (m *JsLog) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_JsLog.Marshal(b, m, deterministic)
}
func (dst *JsLog) XXX_Merge(src proto.Message) {
xxx_messageInfo_JsLog.Merge(dst, src)
}
func (m *JsLog) XXX_Size() int {
return xxx_messageInfo_JsLog.Size(m)
}
func (m *JsLog) XXX_DiscardUnknown() {
xxx_messageInfo_JsLog.DiscardUnknown(m)
}
var xxx_messageInfo_JsLog proto.InternalMessageInfo
func (m *JsLog) GetData() string {
if m != nil {
return m.Data
}
return ""
}
func init() { func init() {
proto.RegisterType((*Create)(nil), "jsproto.Create") proto.RegisterType((*Create)(nil), "jsproto.Create")
proto.RegisterType((*Call)(nil), "jsproto.Call") proto.RegisterType((*Call)(nil), "jsproto.Call")
proto.RegisterType((*JsAction)(nil), "jsproto.JsAction") proto.RegisterType((*JsAction)(nil), "jsproto.JsAction")
} proto.RegisterType((*JsLog)(nil), "jsproto.JsLog")
}
func init() { proto.RegisterFile("js.proto", fileDescriptor_js_26fa46fa936181bc) }
func init() { proto.RegisterFile("js.proto", fileDescriptor_js_25eaf89ab172ea83) }
var fileDescriptor_js_26fa46fa936181bc = []byte{
// 199 bytes of a gzipped FileDescriptorProto var fileDescriptor_js_25eaf89ab172ea83 = []byte{
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x8e, 0xcd, 0xae, 0x82, 0x30, // 215 bytes of a gzipped FileDescriptorProto
0x10, 0x85, 0x81, 0xcb, 0xdf, 0x1d, 0x72, 0xaf, 0x49, 0x57, 0xc4, 0x95, 0xc1, 0x8d, 0x6e, 0x88, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x8f, 0xcf, 0x4e, 0x86, 0x30,
0xc1, 0x27, 0x50, 0x36, 0x84, 0x65, 0xdf, 0xa0, 0xd6, 0x6a, 0x24, 0x95, 0x1a, 0x5a, 0x4c, 0x78, 0x10, 0xc4, 0x3f, 0x90, 0x7f, 0x2e, 0x51, 0x93, 0x9e, 0x88, 0x5e, 0x4c, 0xbd, 0xe8, 0x85, 0x18,
0x7b, 0xd3, 0x81, 0x80, 0xbb, 0xaf, 0x73, 0xbe, 0xce, 0x1c, 0x88, 0x1b, 0x9d, 0xbf, 0x3a, 0x65, 0x7c, 0x02, 0xe5, 0x42, 0x88, 0xa7, 0xbe, 0xc1, 0x5a, 0x2a, 0x91, 0x54, 0x6a, 0x68, 0x31, 0xe1,
0x14, 0x89, 0x1a, 0x8d, 0x90, 0x1d, 0x20, 0x2c, 0x3b, 0xc1, 0x8c, 0x20, 0x04, 0x7c, 0xae, 0xae, 0xed, 0x4d, 0x17, 0x84, 0xef, 0xf6, 0xeb, 0xce, 0x74, 0x67, 0x16, 0xb2, 0xc1, 0x96, 0x3f, 0x93,
0x22, 0x75, 0x37, 0xee, 0xee, 0x97, 0x22, 0xdb, 0x59, 0xcb, 0x9e, 0x22, 0xf5, 0xc6, 0x99, 0xe5, 0x71, 0x86, 0xa5, 0x83, 0x25, 0xe0, 0xcf, 0x90, 0xd4, 0x93, 0x42, 0xa7, 0x18, 0x83, 0x48, 0x9a,
0xac, 0x06, 0xbf, 0x64, 0x52, 0xce, 0x99, 0xbb, 0x64, 0x64, 0x0d, 0xf1, 0xad, 0x6f, 0xf9, 0xd7, 0x4e, 0x15, 0xc1, 0x7d, 0xf0, 0x78, 0x29, 0x88, 0xfd, 0x6c, 0xc4, 0x6f, 0x55, 0x84, 0xeb, 0xcc,
0x9f, 0xf9, 0x6d, 0x7d, 0xd6, 0xdd, 0x75, 0xfa, 0x33, 0xfa, 0x96, 0x33, 0x0d, 0x71, 0xad, 0x4f, 0x33, 0x6f, 0x21, 0xaa, 0x51, 0xeb, 0x5d, 0x0b, 0x0e, 0x8d, 0xdd, 0x42, 0xf6, 0x39, 0x8f, 0xf2,
0xdc, 0x3c, 0x54, 0x4b, 0xf6, 0x10, 0x72, 0x6c, 0x82, 0x1b, 0x93, 0x62, 0x95, 0x4f, 0x1d, 0xf3, 0xec, 0xcf, 0xfe, 0xf6, 0x7e, 0x9c, 0x7a, 0x5b, 0x5c, 0xac, 0x7e, 0xcf, 0xdc, 0x42, 0xd6, 0xda,
0xb1, 0x60, 0xe5, 0xd0, 0x49, 0x20, 0x5b, 0xf0, 0x39, 0x93, 0x12, 0x4f, 0x24, 0xc5, 0xdf, 0x22, 0x57, 0xe9, 0xbe, 0xcc, 0xc8, 0x9e, 0x20, 0x91, 0xd4, 0x84, 0x36, 0xe6, 0xd5, 0x4d, 0xb9, 0x75,
0x32, 0x29, 0x2b, 0x87, 0x62, 0x48, 0xfe, 0xc1, 0x33, 0x03, 0x5e, 0x0b, 0xa8, 0x67, 0x86, 0x73, 0x2c, 0xd7, 0x82, 0xcd, 0x49, 0x6c, 0x06, 0xf6, 0x00, 0x91, 0x44, 0xad, 0x29, 0x22, 0xaf, 0xae,
0x04, 0xc1, 0x9b, 0xc9, 0x5e, 0x5c, 0x42, 0x94, 0x8f, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x35, 0x0e, 0x23, 0x6a, 0xdd, 0x9c, 0x04, 0x89, 0xec, 0x1a, 0x42, 0xb7, 0x50, 0x5a, 0x2c, 0x42, 0xb7,
0x5c, 0xc0, 0x76, 0x0e, 0x01, 0x00, 0x00, 0xbc, 0xa5, 0x10, 0xff, 0xa2, 0x9e, 0x15, 0xbf, 0x83, 0xb8, 0xb5, 0xef, 0xa6, 0xf7, 0x8d, 0x3a,
0x74, 0xf8, 0x7f, 0x81, 0xe7, 0x8f, 0x84, 0x36, 0xbd, 0xfc, 0x05, 0x00, 0x00, 0xff, 0xff, 0xdb,
0x0d, 0xa5, 0xc0, 0x2b, 0x01, 0x00, 0x00,
} }
...@@ -19,16 +19,61 @@ func HeightIndexStr(height, index int64) string { ...@@ -19,16 +19,61 @@ func HeightIndexStr(height, index int64) string {
//KVCreator 创建KV的辅助工具 //KVCreator 创建KV的辅助工具
type KVCreator struct { type KVCreator struct {
kvs []*types.KeyValue kvs []*types.KeyValue
kvdb db.KV kvdb db.KV
autorollback bool
prefix []byte
rollbackkey []byte
rollbackkvs []*types.KeyValue
} }
//NewKVCreator 创建创建者 //NewKVCreator 创建创建者
func NewKVCreator(kv db.KV) *KVCreator { //注意: 自动回滚可能会严重影响系统性能
return &KVCreator{kvdb: kv} func NewKVCreator(kv db.KV, prefix []byte, rollbackkey []byte) *KVCreator {
return &KVCreator{
kvdb: kv,
prefix: prefix,
rollbackkey: rollbackkey,
autorollback: rollbackkey != nil,
}
}
func (c *KVCreator) addPrefix(key []byte) []byte {
newkey := append([]byte{}, c.prefix...)
return append(newkey, key...)
} }
func (c *KVCreator) add(key, value []byte, set bool) *KVCreator { func (c *KVCreator) add(key, value []byte, set bool) *KVCreator {
if c.prefix != nil {
key = c.addPrefix(key)
}
return c.addnoprefix(key, value, set)
}
func (c *KVCreator) addnoprefix(key, value []byte, set bool) *KVCreator {
if c.autorollback {
prev, err := c.kvdb.Get(key)
//数据库发生错误,直接panic (再执行器中会recover)
if err != nil && err != types.ErrNotFound {
panic(err)
}
if value == nil { //del
//不需要做任何处理, 也不用加入 kvs
if err == types.ErrNotFound {
return c
}
rb := &types.KeyValue{Key: key, Value: prev}
c.rollbackkvs = append(c.rollbackkvs, rb)
} else {
if err == types.ErrNotFound { //add
rb := &types.KeyValue{Key: key}
c.rollbackkvs = append(c.rollbackkvs, rb)
} else { //update
rb := &types.KeyValue{Key: key, Value: prev}
c.rollbackkvs = append(c.rollbackkvs, rb)
}
}
}
c.kvs = append(c.kvs, &types.KeyValue{Key: key, Value: value}) c.kvs = append(c.kvs, &types.KeyValue{Key: key, Value: value})
if set { if set {
c.kvdb.Set(key, value) c.kvdb.Set(key, value)
...@@ -36,17 +81,110 @@ func (c *KVCreator) add(key, value []byte, set bool) *KVCreator { ...@@ -36,17 +81,110 @@ func (c *KVCreator) add(key, value []byte, set bool) *KVCreator {
return c return c
} }
//Get 从KV中获取 value
func (c *KVCreator) Get(key []byte) ([]byte, error) {
if c.prefix != nil {
newkey := c.addPrefix(key)
return c.kvdb.Get(newkey)
}
return c.kvdb.Get(key)
}
//GetNoPrefix 从KV中获取 value, 不自动添加前缀
func (c *KVCreator) GetNoPrefix(key []byte) ([]byte, error) {
return c.kvdb.Get(key)
}
//Add add and set to kvdb //Add add and set to kvdb
func (c *KVCreator) Add(key, value []byte) *KVCreator { func (c *KVCreator) Add(key, value []byte) *KVCreator {
return c.add(key, value, true) return c.add(key, value, true)
} }
//AddKV only add KV //AddNoPrefix 不自动添加prefix
func (c *KVCreator) AddKV(key, value []byte) *KVCreator { func (c *KVCreator) AddNoPrefix(key, value []byte) *KVCreator {
return c.addnoprefix(key, value, true)
}
//AddList only add KVList
func (c *KVCreator) AddList(list []*types.KeyValue) *KVCreator {
for _, kv := range list {
c.add(kv.Key, kv.Value, true)
}
return c
}
//AddKVOnly only add KV(can't auto rollback)
func (c *KVCreator) AddKVOnly(key, value []byte) *KVCreator {
if c.autorollback {
panic("autorollback open, AddKVOnly not allow")
}
return c.add(key, value, false) return c.add(key, value, false)
} }
//AddKVListOnly only add KVList (can't auto rollback)
func (c *KVCreator) AddKVListOnly(list []*types.KeyValue) *KVCreator {
if c.autorollback {
panic("autorollback open, AddKVListOnly not allow")
}
for _, kv := range list {
c.add(kv.Key, kv.Value, false)
}
return c
}
//KVList 读取所有的kv列表 //KVList 读取所有的kv列表
func (c *KVCreator) KVList() []*types.KeyValue { func (c *KVCreator) KVList() []*types.KeyValue {
return c.kvs return c.kvs
} }
//AddRollbackKV 添加回滚数据到 KV
func (c *KVCreator) AddRollbackKV() {
v := types.Encode(c.rollbackLog())
c.kvs = append(c.kvs, &types.KeyValue{Key: c.rollbackkey, Value: v})
}
//DelRollbackKV 删除rollback kv
func (c *KVCreator) DelRollbackKV() {
c.kvs = append(c.kvs, &types.KeyValue{Key: c.rollbackkey})
}
//GetRollbackKVList 获取 rollback 到 Key and Vaue
func (c *KVCreator) GetRollbackKVList() ([]*types.KeyValue, error) {
data, err := c.kvdb.Get(c.rollbackkey)
if err != nil {
return nil, err
}
var rollbacklog types.ReceiptLog
err = types.Decode(data, &rollbacklog)
if err != nil {
return nil, err
}
return c.parseRollback(&rollbacklog)
}
//rollbackLog rollback log
func (c *KVCreator) rollbackLog() *types.ReceiptLog {
data := types.Encode(&types.LocalDBSet{KV: c.rollbackkvs})
return &types.ReceiptLog{Ty: types.TyLogRollback, Log: data}
}
//ParseRollback 解析rollback的数据
func (c *KVCreator) parseRollback(log *types.ReceiptLog) ([]*types.KeyValue, error) {
var data types.LocalDBSet
if log.Ty != types.TyLogRollback {
return nil, types.ErrInvalidParam
}
err := types.Decode(log.Log, &data)
if err != nil {
return nil, err
}
return data.KV, nil
}
//AddToLogs add not empty log to logs
func (c *KVCreator) AddToLogs(logs []*types.ReceiptLog) []*types.ReceiptLog {
if len(c.rollbackkvs) == 0 {
return logs
}
return append(logs, c.rollbackLog())
}
...@@ -100,6 +100,7 @@ const ( ...@@ -100,6 +100,7 @@ const (
TyLogExecActive = 10 TyLogExecActive = 10
TyLogGenesisTransfer = 11 TyLogGenesisTransfer = 11
TyLogGenesisDeposit = 12 TyLogGenesisDeposit = 12
TyLogRollback = 13
) )
//SystemLog 系统log日志 //SystemLog 系统log日志
...@@ -116,6 +117,7 @@ var SystemLog = map[int64]*LogInfo{ ...@@ -116,6 +117,7 @@ var SystemLog = map[int64]*LogInfo{
TyLogExecActive: {reflect.TypeOf(ReceiptExecAccountTransfer{}), "LogExecActive"}, TyLogExecActive: {reflect.TypeOf(ReceiptExecAccountTransfer{}), "LogExecActive"},
TyLogGenesisTransfer: {reflect.TypeOf(ReceiptAccountTransfer{}), "LogGenesisTransfer"}, TyLogGenesisTransfer: {reflect.TypeOf(ReceiptAccountTransfer{}), "LogGenesisTransfer"},
TyLogGenesisDeposit: {reflect.TypeOf(ReceiptAccountTransfer{}), "LogGenesisDeposit"}, TyLogGenesisDeposit: {reflect.TypeOf(ReceiptAccountTransfer{}), "LogGenesisDeposit"},
TyLogRollback: {reflect.TypeOf(LocalDBSet{}), "LogRollback"},
} }
//exec type //exec type
......
...@@ -108,4 +108,4 @@ message PruneData { ...@@ -108,4 +108,4 @@ message PruneData {
//用于存储db Pool数据的Value //用于存储db Pool数据的Value
message StoreValuePool { message StoreValuePool {
repeated bytes values = 1; repeated bytes values = 1;
} }
\ No newline at end of file \ No newline at end of file
...@@ -17,6 +17,7 @@ import ( ...@@ -17,6 +17,7 @@ import (
"testing" "testing"
"unicode" "unicode"
"github.com/33cn/chain33/common"
"github.com/33cn/chain33/common/address" "github.com/33cn/chain33/common/address"
"github.com/33cn/chain33/common/crypto" "github.com/33cn/chain33/common/crypto"
"github.com/33cn/chain33/common/db" "github.com/33cn/chain33/common/db"
...@@ -428,3 +429,27 @@ func CloseTestDB(dir string, dbm db.DB) { ...@@ -428,3 +429,27 @@ func CloseTestDB(dir string, dbm db.DB) {
os.RemoveAll(dir) os.RemoveAll(dir)
dbm.Close() dbm.Close()
} }
//SaveKVList 保存kvs to database
func SaveKVList(kvdb db.DB, kvs []*types.KeyValue) {
//printKV(kvs)
batch := kvdb.NewBatch(true)
for i := 0; i < len(kvs); i++ {
if kvs[i].Value == nil {
batch.Delete(kvs[i].Key)
continue
}
batch.Set(kvs[i].Key, kvs[i].Value)
}
err := batch.Write()
if err != nil {
panic(err)
}
}
//PrintKV 打印KVList
func PrintKV(kvs []*types.KeyValue) {
for i := 0; i < len(kvs); i++ {
fmt.Printf("KV %d %s(%s)\n", i, string(kvs[i].Key), common.ToHex(kvs[i].Value))
}
}
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