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
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"
)
func (c *js) Exec_Create(payload *jsproto.Create, tx *types.Transaction, index int) (*types.Receipt, error) {
db := c.GetStateDB()
_, err := db.Get(calcCodeKey(payload.Name))
execer := types.ExecName("user.js." + payload.Name)
kvc := dapp.NewKVCreator(c.GetStateDB(), calcStatePrefix([]byte(execer)), nil)
_, err := kvc.GetNoPrefix(calcCodeKey(payload.Name))
if err != nil && err != types.ErrNotFound {
return nil, err
}
if err == nil {
return nil, ptypes.ErrDupName
}
r := &types.Receipt{Ty: types.ExecOk}
//code must be utf-8 encoding
r.KV = append(r.KV, &types.KeyValue{
Key: calcCodeKey(payload.Name), Value: []byte(payload.Code)})
kvc.AddNoPrefix(calcCodeKey(payload.Name), []byte(payload.Code))
jsvalue, err := c.callVM("init", &jsproto.Call{Name: payload.Name}, tx, index, nil)
if err != nil {
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
}
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 {
return nil, err
}
r := &types.Receipt{Ty: types.ExecOk}
kvs, err := parseKVS(jsvalue)
kvs, logs, err := parseJsReturn(jsvalue)
if err != nil {
return nil, err
}
r.KV = kvs
kvc.AddList(kvs)
r := &types.Receipt{Ty: types.ExecOk, KV: kvc.KVList(), Logs: logs}
return r, nil
}
package executor
import (
"github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
"github.com/33cn/plugin/plugin/dapp/js/types/jsproto"
)
......@@ -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) {
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 {
return nil, err
}
kvc.AddKVListOnly(kvs)
kvc.DelRollbackKV()
r := &types.LocalDBSet{}
kvs, err := parseKVS(jsvalue)
if err != nil {
return nil, err
}
r.KV = kvs
r.KV = kvc.KVList()
return r, nil
}
package executor
import (
"github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
"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
}
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 {
return nil, err
}
r := &types.LocalDBSet{}
kvs, err := parseKVS(jsvalue)
kvs, _, err := parseJsReturn(jsvalue)
if err != nil {
return nil, err
}
r.KV = kvs
kvc.AddList(kvs)
kvc.AddRollbackKV()
r := &types.LocalDBSet{}
r.KV = kvc.KVList()
return r, nil
}
......@@ -49,7 +49,8 @@ func (u *js) GetDriverName() string {
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)
if err != nil {
return nil, err
......@@ -59,10 +60,19 @@ func (u *js) callVM(prefix string, payload *jsproto.Call, tx *types.Transaction,
if err != nil {
return nil, err
}
loglist, err := jslogs(receiptData)
if err != nil {
return nil, err
}
vm.Set("loglist", loglist)
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)
callfunc := "callcode(context, f, args)"
callfunc := "callcode(context, f, args, loglist)"
jsvalue, err := vm.Run(callcode + string(code) + "\n" + callfunc)
if err != nil {
return nil, err
......@@ -73,6 +83,26 @@ func (u *js) callVM(prefix string, payload *jsproto.Call, tx *types.Transaction,
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 {
return &blockContext{
Height: u.GetHeight(),
......
......@@ -6,44 +6,10 @@ import (
"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"
)
//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 {
Height int64 `json:"height"`
Blocktime int64 `json:"blocktime"`
......@@ -65,30 +31,52 @@ type listdbReturn struct {
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")
if err != nil {
return nil, ptypes.ErrJsReturnKVSFormat
return nil, nil, ptypes.ErrJsReturnKVSFormat
}
if obj.Class() != "Array" {
return nil, ptypes.ErrJsReturnKVSFormat
return nil, nil, ptypes.ErrJsReturnKVSFormat
}
size, err := getInt(obj, "length")
if err != nil {
return nil, err
return nil, nil, err
}
for i := 0; i < int(size); i++ {
data, err := getObject(obj, fmt.Sprint(i))
if err != nil {
return nil, err
return nil, nil, err
}
kv, err := parseKV(data)
if err != nil {
return nil, err
return nil, nil, err
}
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) {
......
package executor
import (
"encoding/json"
"strings"
"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/stretchr/testify/assert"
)
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
}
function ExecDelLocal(context) {
function ExecLocal(context, logs) {
this.kvc = new kvcreator("local")
this.context = context
this.logs = logs
}
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) {
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) {
return {args: args, action:"execdellocal", context: this.context}
c, tx := createCodeTx("test", jscode)
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) {
data, err := json.Marshal(context)
if err != nil {
return otto.Value{}, err
func createCodeTx(name, jscode string) (*jsproto.Create, *types.Transaction) {
data := &jsproto.Create{
Code: jscode,
Name: name,
}
vm := otto.New()
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)
return data, &types.Transaction{Execer: []byte("js"), Payload: types.Encode(data)}
}
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) {
value, err := callJsFunc(&blockContext{Height: 1}, jscode, "exec_hello", `{"hello2":"world2"}`)
assert.Nil(t, err)
assert.Equal(t, true, value.IsObject())
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())
dir, ldb, kvdb := util.CreateTestDB()
defer util.CloseTestDB(dir, ldb)
e := initExec(ldb, kvdb, t)
context, err := value.Object().Get("context")
assert.Nil(t, err)
cvalue, err := context.Object().Get("height")
call, tx := callCodeTx("test", "hello", `{"hello":"world"}`)
receipt, err := e.Exec_Call(call, tx, 0)
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)
_, 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)
assert.Equal(t, true, ok)
assert.Equal(t, true, strings.Contains(err.Error(), "SyntaxError"))
_, err = callJsFunc(&blockContext{Height: 1}, jscode, "hello", `{"hello2":"world2"}`)
assert.Equal(t, true, strings.Contains(err.Error(), errInvalidFuncFormat.Error()))
_, err = callJsFunc(&blockContext{Height: 1}, jscode, "hello_hello", `{"hello2":"world2"}`)
call, tx = callCodeTx("test", "hello", `{"hello":"world"}`)
_, err = e.callVM("hello", call, tx, 0, nil)
_, ok = err.(*otto.Error)
assert.Equal(t, true, ok)
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()))
}
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 {
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 {
}
int32 ty = 3;
}
message JsLog {
string data = 1;
}
\ No newline at end of file
......@@ -2,6 +2,7 @@ package types
import (
"errors"
"reflect"
"github.com/33cn/chain33/types"
"github.com/33cn/plugin/plugin/dapp/js/types/jsproto"
......@@ -15,8 +16,7 @@ const (
//日志类型
const (
TyLogJsCreate = iota + 1
TyLogJsCall
TyLogJs = 10000
)
var (
......@@ -24,7 +24,9 @@ var (
"Create": jsActionCreate,
"Call": jsActionCall,
}
logMap = map[int64]*types.LogInfo{}
logMap = map[int64]*types.LogInfo{
TyLogJs: {Ty: reflect.TypeOf(jsproto.JsLog{}), Name: "TyLogJs"},
}
)
//JsX 插件名字
......@@ -32,9 +34,10 @@ var JsX = "js"
//错误常量
var (
ErrDupName = errors.New("ErrDupName")
ErrJsReturnNotObject = errors.New("ErrJsReturnNotObject")
ErrJsReturnKVSFormat = errors.New("ErrJsReturnKVSFormat")
ErrDupName = errors.New("ErrDupName")
ErrJsReturnNotObject = errors.New("ErrJsReturnNotObject")
ErrJsReturnKVSFormat = errors.New("ErrJsReturnKVSFormat")
ErrJsReturnLogsFormat = errors.New("ErrJsReturnLogsFormat")
)
func init() {
......
......@@ -31,7 +31,7 @@ func (m *Create) Reset() { *m = Create{} }
func (m *Create) String() string { return proto.CompactTextString(m) }
func (*Create) ProtoMessage() {}
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 {
return xxx_messageInfo_Create.Unmarshal(m, b)
......@@ -79,7 +79,7 @@ func (m *Call) Reset() { *m = Call{} }
func (m *Call) String() string { return proto.CompactTextString(m) }
func (*Call) ProtoMessage() {}
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 {
return xxx_messageInfo_Call.Unmarshal(m, b)
......@@ -135,7 +135,7 @@ func (m *JsAction) Reset() { *m = JsAction{} }
func (m *JsAction) String() string { return proto.CompactTextString(m) }
func (*JsAction) ProtoMessage() {}
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 {
return xxx_messageInfo_JsAction.Unmarshal(m, b)
......@@ -273,27 +273,67 @@ func _JsAction_OneofSizer(msg proto.Message) (n int) {
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() {
proto.RegisterType((*Create)(nil), "jsproto.Create")
proto.RegisterType((*Call)(nil), "jsproto.Call")
proto.RegisterType((*JsAction)(nil), "jsproto.JsAction")
}
func init() { proto.RegisterFile("js.proto", fileDescriptor_js_26fa46fa936181bc) }
var fileDescriptor_js_26fa46fa936181bc = []byte{
// 199 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x8e, 0xcd, 0xae, 0x82, 0x30,
0x10, 0x85, 0x81, 0xcb, 0xdf, 0x1d, 0x72, 0xaf, 0x49, 0x57, 0xc4, 0x95, 0xc1, 0x8d, 0x6e, 0x88,
0xc1, 0x27, 0x50, 0x36, 0x84, 0x65, 0xdf, 0xa0, 0xd6, 0x6a, 0x24, 0x95, 0x1a, 0x5a, 0x4c, 0x78,
0x7b, 0xd3, 0x81, 0x80, 0xbb, 0xaf, 0x73, 0xbe, 0xce, 0x1c, 0x88, 0x1b, 0x9d, 0xbf, 0x3a, 0x65,
0x14, 0x89, 0x1a, 0x8d, 0x90, 0x1d, 0x20, 0x2c, 0x3b, 0xc1, 0x8c, 0x20, 0x04, 0x7c, 0xae, 0xae,
0x22, 0x75, 0x37, 0xee, 0xee, 0x97, 0x22, 0xdb, 0x59, 0xcb, 0x9e, 0x22, 0xf5, 0xc6, 0x99, 0xe5,
0xac, 0x06, 0xbf, 0x64, 0x52, 0xce, 0x99, 0xbb, 0x64, 0x64, 0x0d, 0xf1, 0xad, 0x6f, 0xf9, 0xd7,
0x9f, 0xf9, 0x6d, 0x7d, 0xd6, 0xdd, 0x75, 0xfa, 0x33, 0xfa, 0x96, 0x33, 0x0d, 0x71, 0xad, 0x4f,
0xdc, 0x3c, 0x54, 0x4b, 0xf6, 0x10, 0x72, 0x6c, 0x82, 0x1b, 0x93, 0x62, 0x95, 0x4f, 0x1d, 0xf3,
0xb1, 0x60, 0xe5, 0xd0, 0x49, 0x20, 0x5b, 0xf0, 0x39, 0x93, 0x12, 0x4f, 0x24, 0xc5, 0xdf, 0x22,
0x32, 0x29, 0x2b, 0x87, 0x62, 0x48, 0xfe, 0xc1, 0x33, 0x03, 0x5e, 0x0b, 0xa8, 0x67, 0x86, 0x73,
0x04, 0xc1, 0x9b, 0xc9, 0x5e, 0x5c, 0x42, 0x94, 0x8f, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x35,
0x5c, 0xc0, 0x76, 0x0e, 0x01, 0x00, 0x00,
proto.RegisterType((*JsLog)(nil), "jsproto.JsLog")
}
func init() { proto.RegisterFile("js.proto", fileDescriptor_js_25eaf89ab172ea83) }
var fileDescriptor_js_25eaf89ab172ea83 = []byte{
// 215 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x8f, 0xcf, 0x4e, 0x86, 0x30,
0x10, 0xc4, 0x3f, 0x90, 0x7f, 0x2e, 0x51, 0x93, 0x9e, 0x88, 0x5e, 0x4c, 0xbd, 0xe8, 0x85, 0x18,
0x7c, 0x02, 0xe5, 0x42, 0x88, 0xa7, 0xbe, 0xc1, 0x5a, 0x2a, 0x91, 0x54, 0x6a, 0x68, 0x31, 0xe1,
0xed, 0x4d, 0x17, 0x84, 0xef, 0xf6, 0xeb, 0xce, 0x74, 0x67, 0x16, 0xb2, 0xc1, 0x96, 0x3f, 0x93,
0x71, 0x86, 0xa5, 0x83, 0x25, 0xe0, 0xcf, 0x90, 0xd4, 0x93, 0x42, 0xa7, 0x18, 0x83, 0x48, 0x9a,
0x4e, 0x15, 0xc1, 0x7d, 0xf0, 0x78, 0x29, 0x88, 0xfd, 0x6c, 0xc4, 0x6f, 0x55, 0x84, 0xeb, 0xcc,
0x33, 0x6f, 0x21, 0xaa, 0x51, 0xeb, 0x5d, 0x0b, 0x0e, 0x8d, 0xdd, 0x42, 0xf6, 0x39, 0x8f, 0xf2,
0xec, 0xcf, 0xfe, 0xf6, 0x7e, 0x9c, 0x7a, 0x5b, 0x5c, 0xac, 0x7e, 0xcf, 0xdc, 0x42, 0xd6, 0xda,
0x57, 0xe9, 0xbe, 0xcc, 0xc8, 0x9e, 0x20, 0x91, 0xd4, 0x84, 0x36, 0xe6, 0xd5, 0x4d, 0xb9, 0x75,
0x2c, 0xd7, 0x82, 0xcd, 0x49, 0x6c, 0x06, 0xf6, 0x00, 0x91, 0x44, 0xad, 0x29, 0x22, 0xaf, 0xae,
0x0e, 0x23, 0x6a, 0xdd, 0x9c, 0x04, 0x89, 0xec, 0x1a, 0x42, 0xb7, 0x50, 0x5a, 0x2c, 0x42, 0xb7,
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 {
//KVCreator 创建KV的辅助工具
type KVCreator struct {
kvs []*types.KeyValue
kvdb db.KV
kvs []*types.KeyValue
kvdb db.KV
autorollback bool
prefix []byte
rollbackkey []byte
rollbackkvs []*types.KeyValue
}
//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 {
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})
if set {
c.kvdb.Set(key, value)
......@@ -36,17 +81,110 @@ func (c *KVCreator) add(key, value []byte, set bool) *KVCreator {
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
func (c *KVCreator) Add(key, value []byte) *KVCreator {
return c.add(key, value, true)
}
//AddKV only add KV
func (c *KVCreator) AddKV(key, value []byte) *KVCreator {
//AddNoPrefix 不自动添加prefix
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)
}
//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列表
func (c *KVCreator) KVList() []*types.KeyValue {
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 (
TyLogExecActive = 10
TyLogGenesisTransfer = 11
TyLogGenesisDeposit = 12
TyLogRollback = 13
)
//SystemLog 系统log日志
......@@ -116,6 +117,7 @@ var SystemLog = map[int64]*LogInfo{
TyLogExecActive: {reflect.TypeOf(ReceiptExecAccountTransfer{}), "LogExecActive"},
TyLogGenesisTransfer: {reflect.TypeOf(ReceiptAccountTransfer{}), "LogGenesisTransfer"},
TyLogGenesisDeposit: {reflect.TypeOf(ReceiptAccountTransfer{}), "LogGenesisDeposit"},
TyLogRollback: {reflect.TypeOf(LocalDBSet{}), "LogRollback"},
}
//exec type
......
......@@ -108,4 +108,4 @@ message PruneData {
//用于存储db Pool数据的Value
message StoreValuePool {
repeated bytes values = 1;
}
\ No newline at end of file
}
\ No newline at end of file
......@@ -17,6 +17,7 @@ import (
"testing"
"unicode"
"github.com/33cn/chain33/common"
"github.com/33cn/chain33/common/address"
"github.com/33cn/chain33/common/crypto"
"github.com/33cn/chain33/common/db"
......@@ -428,3 +429,27 @@ func CloseTestDB(dir string, dbm db.DB) {
os.RemoveAll(dir)
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