Commit 131919e2 authored by 张振华's avatar 张振华

Merge branch 'master' into guess

parents 074b7990 4dfc0689
......@@ -12,6 +12,7 @@ chain33_raft-1
build/datadir
build/bityuan*
build/para
build/cli
build/execblock
build/relayd
build/relayd.toml
......
Title="chain33"
TestNet=true
FixTime=false
version="6.0.181228"
[log]
# 日志级别,支持debug(dbug)/info/warn/error(eror)/crit
......
......@@ -600,7 +600,16 @@ FOR_LOOP:
} else if typeID == ttypes.VoteSetMaj23ID {
tmp := msg.Msg.(*tmtypes.VoteSetMaj23Msg)
pc.myState.SetPeerMaj23(tmp.Height, int(tmp.Round), byte(tmp.Type), pc.id, tmp.BlockID)
myVotes := pc.myState.GetPrevotesState(tmp.Height, int(tmp.Round), tmp.BlockID)
var myVotes *ttypes.BitArray
switch byte(tmp.Type) {
case ttypes.VoteTypePrevote:
myVotes = pc.myState.GetPrevotesState(tmp.Height, int(tmp.Round), tmp.BlockID)
case ttypes.VoteTypePrecommit:
myVotes = pc.myState.GetPrecommitsState(tmp.Height, int(tmp.Round), tmp.BlockID)
default:
tendermintlog.Error("Bad VoteSetBitsMessage field Type", "type", byte(tmp.Type))
return
}
if myVotes != nil && myVotes.TendermintBitArray != nil {
voteSetBitMsg := &tmtypes.VoteSetBitsMsg{
Height: tmp.Height,
......@@ -617,7 +626,16 @@ FOR_LOOP:
} else if typeID == ttypes.VoteSetBitsID {
tmp := msg.Msg.(*tmtypes.VoteSetBitsMsg)
if pc.myState.Height == tmp.Height {
myVotes := pc.myState.GetPrevotesState(tmp.Height, int(tmp.Round), tmp.BlockID)
var myVotes *ttypes.BitArray
switch byte(tmp.Type) {
case ttypes.VoteTypePrevote:
myVotes = pc.myState.GetPrevotesState(tmp.Height, int(tmp.Round), tmp.BlockID)
case ttypes.VoteTypePrecommit:
myVotes = pc.myState.GetPrecommitsState(tmp.Height, int(tmp.Round), tmp.BlockID)
default:
tendermintlog.Error("Bad VoteSetBitsMessage field Type", "type", byte(tmp.Type))
return
}
pc.state.ApplyVoteSetBitsMessage(tmp, myVotes)
} else {
pc.state.ApplyVoteSetBitsMessage(tmp, nil)
......@@ -1059,7 +1077,8 @@ func (ps *PeerConnState) PickVoteToSend(votes ttypes.VoteSetReader) (vote *ttype
}
if index, ok := votes.BitArray().Sub(psVotes).PickRandom(); ok {
tendermintlog.Debug("PickVoteToSend", "height", height, "index", index, "type", voteType, "selfVotes", votes.BitArray().String(),
tendermintlog.Debug("PickVoteToSend", "peer(H/R)", fmt.Sprintf("%v/%v", ps.Height, ps.Round),
"vote(H/R)", fmt.Sprintf("%v/%v", height, round), "type", voteType, "selfVotes", votes.BitArray().String(),
"peerVotes", psVotes.String(), "peerip", ps.ip.String())
ps.setHasVote(height, round, voteType, index)
return votes.GetByIndex(index), true
......@@ -1211,9 +1230,11 @@ func (ps *PeerConnState) ApplyNewRoundStepMessage(msg *tmtypes.NewRoundStepMsg)
ps.Step = ttypes.RoundStepType(msg.Step)
ps.StartTime = startTime
tendermintlog.Debug("ApplyNewRoundStepMessage", "peerip", ps.ip.String(), "peer(H/R)", fmt.Sprintf("%v/%v", psHeight, psRound),
"msg(H/R/S)", fmt.Sprintf("%v/%v/%v", msg.Height, msg.Round, ps.Step))
if psHeight != msg.Height || psRound != int(msg.Round) {
tendermintlog.Debug("Reset Proposal, Prevotes, Precommits", "peerip", ps.ip.String(), "peer(H/R)", fmt.Sprintf("%v/%v", psHeight, psRound),
"msg(H/R)", fmt.Sprintf("%v/%v", msg.Height, msg.Round))
tendermintlog.Debug("Reset Proposal, Prevotes, Precommits", "peerip", ps.ip.String(), "peer(H/R)", fmt.Sprintf("%v/%v", psHeight, psRound))
ps.Proposal = false
ps.ProposalBlock = false
ps.ProposalPOLRound = -1
......@@ -1227,10 +1248,11 @@ func (ps *PeerConnState) ApplyNewRoundStepMessage(msg *tmtypes.NewRoundStepMsg)
// Preserve psCatchupCommit!
// NOTE: We prefer to use prs.Precommits if
// pr.Round matches pr.CatchupCommitRound.
tendermintlog.Debug("Reset Precommits to CatchupCommit", "peerip", ps.ip.String(), "peer(H/R)", fmt.Sprintf("%v/%v", psHeight, psRound))
ps.Precommits = psCatchupCommit
}
if psHeight != msg.Height {
tendermintlog.Debug("Reset LastCommit, CatchupCommit", "peerip", ps.ip.String())
tendermintlog.Debug("Reset LastCommit, CatchupCommit", "peerip", ps.ip.String(), "peer(H/R)", fmt.Sprintf("%v/%v", psHeight, psRound))
// Shift Precommits to LastCommit.
if psHeight+1 == msg.Height && psRound == int(msg.LastCommitRound) {
ps.LastCommitRound = int(msg.LastCommitRound)
......
......@@ -217,8 +217,8 @@ func (client *Client) setTicket(tlist *ty.ReplyTicketList, privmap map[string]cr
func (client *Client) flushTicket() error {
//list accounts
tickets, privs, err := client.getTickets()
if err == types.ErrWalletIsLocked {
tlog.Error("flushTicket error", "err", "wallet is locked")
if err == types.ErrWalletIsLocked || err == ty.ErrNoTicket {
tlog.Error("flushTicket error", "err", err.Error())
client.setTicket(nil, nil)
return nil
}
......
......@@ -8,9 +8,11 @@ import (
_ "github.com/33cn/plugin/plugin/dapp/game" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/guess" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/hashlock" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/js" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/lottery" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/multisig" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/norm" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/oracle" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/paracross" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/pokerbull" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/privacy" //auto gen
......
all:
chmod +x ./build.sh
./build.sh $(OUT) $(FLAG)
\ No newline at end of file
#!/bin/sh
strpwd=$(pwd)
strcmd=${strpwd##*dapp/}
strapp=${strcmd%/cmd*}
OUT_DIR="${1}/$strapp"
#FLAG=$2
mkdir -p "${OUT_DIR}"
cp ./build/* "${OUT_DIR}"
<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
This diff is collapsed.
This diff is collapsed.
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) {
execer := types.ExecName("user." + ptypes.JsX + "." + payload.Name)
if string(tx.Execer) != ptypes.JsX {
return nil, types.ErrExecNameNotMatch
}
c.prefix = calcStatePrefix([]byte(execer))
kvc := dapp.NewKVCreator(c.GetStateDB(), c.prefix, nil)
_, err := kvc.GetNoPrefix(calcCodeKey(payload.Name))
if err != nil && err != types.ErrNotFound {
return nil, err
}
if err == nil {
return nil, ptypes.ErrDupName
}
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(c.prefix, jsvalue)
if err != nil {
return nil, err
}
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." + 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(c.prefix, jsvalue)
if err != nil {
return nil, err
}
kvc.AddListNoPrefix(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"
ptypes "github.com/33cn/plugin/plugin/dapp/js/types"
"github.com/33cn/plugin/plugin/dapp/js/types/jsproto"
)
func (c *js) ExecDelLocal_Create(payload *jsproto.Create, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return &types.LocalDBSet{}, nil
}
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." + ptypes.JsX + "." + payload.Name)
c.prefix = calcLocalPrefix([]byte(execer))
kvc := dapp.NewKVCreator(c.GetLocalDB(), c.prefix, krollback)
kvs, err := kvc.GetRollbackKVList()
if err != nil {
return nil, err
}
for _, kv := range kvs {
kvc.AddNoPrefix(kv.Key, kv.Value)
}
kvc.DelRollbackKV()
r := &types.LocalDBSet{}
r.KV = kvc.KVList()
return r, nil
}
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) ExecLocal_Create(payload *jsproto.Create, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return &types.LocalDBSet{}, nil
}
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." + 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(c.prefix, jsvalue)
if err != nil {
return nil, err
}
kvc.AddListNoPrefix(kvs)
kvc.AddRollbackKV()
r := &types.LocalDBSet{}
r.KV = kvc.KVList()
return r, nil
}
#!/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
This diff is collapsed.
package executor
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"strings"
"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"
)
type blockContext struct {
Height int64 `json:"height"`
Blocktime int64 `json:"blocktime"`
DriverName string `json:"driverName"`
Name string `json:"name"`
Curname string `json:"curname"`
Difficulty uint64 `json:"difficulty"`
TxHash string `json:"txhash"`
Index int64 `json:"index"`
}
func parseJsReturn(prefix []byte, jsvalue *otto.Object) (kvlist []*types.KeyValue, logs []*types.ReceiptLog, err error) {
//kvs
obj, err := getObject(jsvalue, "kvs")
if err != nil {
return nil, nil, ptypes.ErrJsReturnKVSFormat
}
if obj.Class() != "Array" {
return nil, nil, ptypes.ErrJsReturnKVSFormat
}
size, err := getInt(obj, "length")
if err != nil {
return nil, nil, err
}
for i := 0; i < int(size); i++ {
data, err := getObject(obj, fmt.Sprint(i))
if err != nil {
return nil, nil, err
}
kv, err := parseKV(prefix, data)
if err != nil {
return nil, nil, err
}
kvlist = append(kvlist, kv)
}
//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 := 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
}
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
}
func getString(data *otto.Object, key string) (string, error) {
v, err := data.Get(key)
if err != nil {
return "", err
}
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 {
return 0, err
}
return v.ToInteger()
}
func getObject(data *otto.Object, key string) (*otto.Object, error) {
v, err := data.Get(key)
if err != nil {
return nil, err
}
if !v.IsObject() {
return nil, errors.New("chain33.js object get key " + key + " is not object")
}
return v.Object(), nil
}
func parseKV(prefix []byte, data *otto.Object) (kv *types.KeyValue, err error) {
key, err := getString(data, "key")
if err != nil {
return nil, err
}
value, err := getString(data, "value")
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
}
func rewriteJSON(data []byte) ([]byte, error) {
dat := make(map[string]interface{})
d := json.NewDecoder(bytes.NewBuffer(data))
d.UseNumber()
if err := d.Decode(&dat); err != nil {
return nil, err
}
dat = rewriteString(dat)
return json.Marshal(dat)
}
func rewriteString(dat map[string]interface{}) map[string]interface{} {
for k, v := range dat {
if n, ok := v.(json.Number); ok {
dat[k] = jssafe(n)
} else if arr, ok := v.([]interface{}); ok {
for i := 0; i < len(arr); i++ {
v := arr[i]
if n, ok := v.(json.Number); ok {
arr[i] = jssafe(n)
}
}
dat[k] = arr
} else if d, ok := v.(map[string]interface{}); ok {
dat[k] = rewriteString(d)
} else {
dat[k] = v
}
}
return dat
}
const maxjsint int64 = 9007199254740991
func jssafe(n json.Number) interface{} {
if strings.Contains(string(n), ".") { //float
return n
}
i, err := n.Int64()
if err != nil {
return n
}
//javascript can not parse
if i >= maxjsint || i <= -maxjsint {
return string(n)
}
return n
}
package executor
import (
"encoding/json"
"fmt"
"math"
"runtime"
"strings"
"testing"
"time"
"github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/types"
"github.com/33cn/chain33/util"
ptypes "github.com/33cn/plugin/plugin/dapp/js/types"
"github.com/33cn/plugin/plugin/dapp/js/types/jsproto"
"github.com/robertkrimen/otto"
"github.com/stretchr/testify/assert"
)
func init() {
Init(ptypes.JsX, nil)
}
func initExec(ldb db.DB, kvdb db.KVDB, t assert.TestingT) *js {
e := newjs().(*js)
e.SetEnv(1, time.Now().Unix(), 1)
e.SetLocalDB(kvdb)
e.SetStateDB(kvdb)
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 createCodeTx(name, jscode string) (*jsproto.Create, *types.Transaction) {
data := &jsproto.Create{
Code: jscode,
Name: name,
}
return data, &types.Transaction{Execer: []byte(ptypes.JsX), 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("user." + ptypes.JsX + "." + name), Payload: types.Encode(data)}
}
func TestCallcode(t *testing.T) {
dir, ldb, kvdb := util.CreateTestDB()
defer util.CloseTestDB(dir, ldb)
e := initExec(ldb, kvdb, t)
call, tx := callCodeTx("test", "hello", `{"hello":"world"}`)
receipt, err := e.Exec_Call(call, tx, 0)
assert.Nil(t, err)
util.SaveKVList(ldb, receipt.KV)
assert.Equal(t, string(receipt.KV[0].Value), `{"hello":"world"}`)
assert.Equal(t, string(receipt.KV[1].Value), "exec")
var data blockContext
err = json.Unmarshal(receipt.KV[2].Value, &data)
assert.Nil(t, err)
assert.Equal(t, uint64(1), data.Difficulty)
assert.Equal(t, ptypes.JsX, data.DriverName)
assert.Equal(t, int64(1), data.Height)
assert.Equal(t, int64(0), data.Index)
kvset, err := e.ExecLocal_Call(call, tx, &types.ReceiptData{Logs: receipt.Logs}, 0)
assert.Nil(t, err)
util.SaveKVList(ldb, kvset.KV)
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), `[{"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, "jsvm", data.DriverName)
assert.Equal(t, int64(1), data.Height)
assert.Equal(t, int64(0), data.Index)
//call query
jsondata, err := e.Query_Query(call)
assert.Nil(t, err)
err = json.Unmarshal([]byte(jsondata.(*jsproto.QueryResult).Data), &data)
assert.Nil(t, err)
assert.Equal(t, uint64(1), data.Difficulty)
assert.Equal(t, "jsvm", data.DriverName)
assert.Equal(t, int64(1), data.Height)
assert.Equal(t, int64(0), data.Index)
//call rollback
kvset, err = e.ExecDelLocal_Call(call, tx, &types.ReceiptData{Logs: receipt.Logs}, 0)
assert.Nil(t, err)
util.SaveKVList(ldb, kvset.KV)
assert.Equal(t, 5, len(kvset.KV))
for i := 0; i < len(kvset.KV); i++ {
assert.Equal(t, string(kvset.KV[i].Value), "")
}
}
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)
call, tx := callCodeTx("test", "hello", `{hello":"world"}`)
_, err := e.callVM("exec", call, tx, 0, nil)
_, ok := err.(*otto.Error)
assert.Equal(t, false, ok)
assert.Equal(t, true, strings.Contains(err.Error(), "invalid character 'h'"))
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(), ptypes.ErrInvalidFuncPrefix.Error()))
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(), ptypes.ErrFuncNotFound.Error()))
}
//数字非常大的数字的处理
func TestBigInt(t *testing.T) {
dir, ldb, kvdb := util.CreateTestDB()
defer util.CloseTestDB(dir, ldb)
e := initExec(ldb, kvdb, t)
//test call error(invalid json input)
s := fmt.Sprintf(`{"balance":%d,"balance1":%d,"balance2":%d,"balance3":%d}`, math.MaxInt64, math.MinInt64, 9007199254740990, -9007199254740990)
call, tx := callCodeTx("test", "hello", s)
data, err := e.callVM("exec", call, tx, 0, nil)
assert.Nil(t, err)
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))
}
func BenchmarkBigInt(b *testing.B) {
dir, ldb, kvdb := util.CreateTestDB()
defer util.CloseTestDB(dir, ldb)
e := initExec(ldb, kvdb, b)
//test call error(invalid json input)
s := fmt.Sprintf(`{"balance":%d,"balance1":%d,"balance2":%d,"balance3":%d}`, math.MaxInt64, math.MinInt64, 9007199254740990, -9007199254740990)
call, tx := callCodeTx("test", "hello", s)
b.StartTimer()
for i := 0; i < b.N; i++ {
_, err := e.callVM("exec", call, tx, 0, nil)
assert.Nil(b, err)
}
}
func TestRewriteJSON(t *testing.T) {
s := fmt.Sprintf(`{"balance":%d,"balance1":%d,"balance2":%d,"balance3":%d}`, math.MaxInt64, math.MinInt64, 9007199254740990, -9007199254740990)
quota := fmt.Sprintf(`{"balance":"%d","balance1":"%d","balance2":%d,"balance3":%d}`, math.MaxInt64, math.MinInt64, 9007199254740990, -9007199254740990)
data, err := rewriteJSON([]byte(s))
assert.Nil(t, err)
assert.Equal(t, quota, string(data))
data2 := make(map[string]interface{})
data2["ints"] = []int64{math.MaxInt64, math.MinInt64, 9007199254740990, -9007199254740990, 1, 0}
data2["float"] = []float64{1.1, 1000000000000000000000000000, 10000000000000000}
json1, err := json.Marshal(data2)
assert.Nil(t, err)
//assert.Equal(t, `{"float":[1.1,1100000000000000000000,-1100000000000000000000],"ints":[9223372036854775807,-9223372036854775808,9007199254740990,-9007199254740990,1,0]}`, string(json1))
json2, err := rewriteJSON(json1)
assert.Nil(t, err)
assert.Equal(t, string(json2), `{"float":[1.1,1e+27,"10000000000000000"],"ints":["9223372036854775807","-9223372036854775808",9007199254740990,-9007199254740990,1,0]}`)
}
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-jsvm-code-a"))
assert.Equal(t, calcRollbackKey([]byte("a")), []byte("LODB-jsvm-rollback-a"))
}
func TestCacheMemUsage(t *testing.T) {
dir, ldb, kvdb := util.CreateTestDB()
defer util.CloseTestDB(dir, ldb)
e := initExec(ldb, kvdb, t)
vm, err := e.createVM("test", nil, 0)
assert.Nil(t, err)
vms := make([]*otto.Otto, 1024)
for i := 0; i < 1024; i++ {
vms[i] = vm.Copy()
}
printMemUsage()
}
func printMemUsage() {
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("Alloc = %v MiB", bToMb(m.Alloc))
fmt.Printf("\tTotalAlloc = %v MiB", bToMb(m.TotalAlloc))
fmt.Printf("\tSys = %v MiB", bToMb(m.Sys))
fmt.Printf("\tNumGC = %v\n", m.NumGC)
}
func bToMb(b uint64) uint64 {
return b / 1024 / 1024
}
package executor
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...)
s = append(s, byte('-'))
return s
}
func calcStatePrefix(execer []byte) []byte {
s := append([]byte("mavl-"), execer...)
s = append(s, byte('-'))
return s
}
func calcAllPrefix(name string) ([]byte, []byte) {
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-"+ptypes.JsX+"-code-"), []byte(name)...)
}
func calcRollbackKey(hash []byte) []byte {
return append([]byte("LODB-"+ptypes.JsX+"-rollback-"), hash...)
}
package executor
import (
"fmt"
"github.com/33cn/chain33/types"
"github.com/33cn/plugin/plugin/dapp/js/types/jsproto"
)
func (c *js) Query_Query(payload *jsproto.Call) (types.Message, error) {
jsvalue, err := c.callVM("query", payload, nil, 0, nil)
if err != nil {
fmt.Println("query", err)
return nil, err
}
str, err := getString(jsvalue, "result")
if err != nil {
fmt.Println("result", err)
return nil, err
}
return &jsproto.QueryResult{Data: str}, nil
}
package executor_test
import (
"testing"
rpctypes "github.com/33cn/chain33/rpc/types"
"github.com/33cn/chain33/types"
"github.com/33cn/chain33/util/testnode"
ptypes "github.com/33cn/plugin/plugin/dapp/js/types"
"github.com/33cn/plugin/plugin/dapp/js/types/jsproto"
"github.com/stretchr/testify/assert"
_ "github.com/33cn/chain33/system"
_ "github.com/33cn/plugin/plugin"
)
func TestJsVM(t *testing.T) {
mocker := testnode.New("--free--", nil)
defer mocker.Close()
mocker.Listen()
//开始部署合约, 测试阶段任何人都可以部署合约
//后期需要加上权限控制
//1. 部署合约
create := &jsproto.Create{
Code: jscode,
Name: "test",
}
req := &rpctypes.CreateTxIn{
Execer: ptypes.JsX,
ActionName: "Create",
Payload: types.MustPBToJSON(create),
}
var txhex string
err := mocker.GetJSONC().Call("Chain33.CreateTransaction", req, &txhex)
assert.Nil(t, err)
hash, err := mocker.SendAndSign(mocker.GetHotKey(), txhex)
assert.Nil(t, err)
txinfo, err := mocker.WaitTx(hash)
assert.Nil(t, err)
assert.Equal(t, txinfo.Receipt.Ty, int32(2))
//2. 调用 hello 函数
call := &jsproto.Call{
Funcname: "hello",
Name: "test",
Args: "{}",
}
req = &rpctypes.CreateTxIn{
Execer: "user." + ptypes.JsX + ".test",
ActionName: "Call",
Payload: types.MustPBToJSON(call),
}
err = mocker.GetJSONC().Call("Chain33.CreateTransaction", req, &txhex)
assert.Nil(t, err)
hash, err = mocker.SendAndSign(mocker.GetHotKey(), txhex)
assert.Nil(t, err)
txinfo, err = mocker.WaitTx(hash)
assert.Nil(t, err)
assert.Equal(t, txinfo.Receipt.Ty, int32(2))
//3. query 函数查询
call = &jsproto.Call{
Funcname: "hello",
Name: "test",
Args: "{}",
}
query := &rpctypes.Query4Jrpc{
Execer: "user." + ptypes.JsX + ".test",
FuncName: "Query",
Payload: types.MustPBToJSON(call),
}
var queryresult jsproto.QueryResult
err = mocker.GetJSONC().Call("Chain33.Query", query, &queryresult)
assert.Nil(t, err)
t.Log(queryresult.Data)
}
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) {
return tojson({hello:"wzw"})
}
`
package executor
import (
"github.com/33cn/chain33/common/address"
"github.com/robertkrimen/otto"
)
//让 js 具有访问 区块链 的一些能力
func execaddressFunc(vm *otto.Otto) {
vm.Set("execaddress", func(call otto.FunctionCall) otto.Value {
key, err := call.Argument(0).ToString()
if err != nil {
return errReturn(vm, err)
}
addr := address.ExecAddress(key)
return okReturn(vm, addr)
})
}
/*
//获取系统随机数的接口
//randnum
//获取前一个区块hash的接口
//prev_blockhash()
*/
This diff is collapsed.
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)
}
package unfreeze
import (
"github.com/33cn/chain33/pluginmgr"
"github.com/33cn/plugin/plugin/dapp/js/executor"
ptypes "github.com/33cn/plugin/plugin/dapp/js/types"
)
func init() {
pluginmgr.Register(&pluginmgr.PluginBase{
Name: ptypes.JsX,
ExecName: executor.GetName(),
Exec: executor.Init,
Cmd: nil,
RPC: nil,
})
}
all:
./create_protobuf.sh
#!/bin/sh
protoc --go_out=plugins=grpc:../types/jsproto/ ./*.proto
syntax = "proto3";
package jsproto;
// create action
message Create {
string code = 1;
string name = 2;
}
// call action
message Call {
string name = 1; //exec name
string funcname = 2; //call function name
string args = 3; //json args
}
message JsAction {
oneof value {
Create create = 1;
Call call = 2;
}
int32 ty = 3;
}
message JsLog {
string data = 1;
}
message QueryResult {
string data = 1;
}
\ No newline at end of file
package types
import (
"errors"
"reflect"
"github.com/33cn/chain33/types"
"github.com/33cn/plugin/plugin/dapp/js/types/jsproto"
)
// action for executor
const (
jsActionCreate = 0
jsActionCall = 1
)
//日志类型
const (
TyLogJs = 10000
)
var (
typeMap = map[string]int32{
"Create": jsActionCreate,
"Call": jsActionCall,
}
logMap = map[int64]*types.LogInfo{
TyLogJs: {Ty: reflect.TypeOf(jsproto.JsLog{}), Name: "TyLogJs"},
}
)
//JsX 插件名字
var JsX = "jsvm"
//错误常量
var (
ErrDupName = errors.New("ErrDupName")
ErrJsReturnNotObject = errors.New("ErrJsReturnNotObject")
ErrJsReturnKVSFormat = errors.New("ErrJsReturnKVSFormat")
ErrJsReturnLogsFormat = errors.New("ErrJsReturnLogsFormat")
//ErrInvalidFuncFormat 错误的函数调用格式(没有_)
ErrInvalidFuncFormat = errors.New("chain33.js: invalid function name format")
//ErrInvalidFuncPrefix not exec_ execloal_ query_
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() {
types.AllowUserExec = append(types.AllowUserExec, []byte(JsX))
types.RegistorExecutor(JsX, NewType())
}
//JsType 类型
type JsType struct {
types.ExecTypeBase
}
//NewType 新建一个plugin 类型
func NewType() *JsType {
c := &JsType{}
c.SetChild(c)
return c
}
//GetPayload 获取 交易构造
func (t *JsType) GetPayload() types.Message {
return &jsproto.JsAction{}
}
//GetTypeMap 获取类型映射
func (t *JsType) GetTypeMap() map[string]int32 {
return typeMap
}
//GetLogMap 获取日志映射
func (t *JsType) GetLogMap() map[int64]*types.LogInfo {
return logMap
}
This diff is collapsed.
/*
* Copyright Fuzamei Corp. 2018 All Rights Reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
package commands
import (
"fmt"
"strconv"
"strings"
"time"
"github.com/33cn/chain33/rpc/jsonclient"
rpctypes "github.com/33cn/chain33/rpc/types"
"github.com/33cn/chain33/types"
oraclety "github.com/33cn/plugin/plugin/dapp/oracle/types"
"github.com/spf13/cobra"
)
// OracleCmd 预言机命令行
func OracleCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "oracle",
Short: "oracle management",
Args: cobra.MinimumNArgs(1),
}
cmd.AddCommand(
OraclePublishEventRawTxCmd(),
OracleAbortEventRawTxCmd(),
OraclePrePublishResultRawTxCmd(),
OracleAbortPrePubResultRawTxCmd(),
OraclePublishResultRawTxCmd(),
OracleQueryRawTxCmd(),
)
return cmd
}
// OraclePublishEventRawTxCmd 发布事件
func OraclePublishEventRawTxCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "publish_event",
Short: "publish a new event",
Run: publishEvent,
}
addPublishEventFlags(cmd)
return cmd
}
func addPublishEventFlags(cmd *cobra.Command) {
cmd.Flags().StringP("type", "t", "", "event type, such as \"football\"")
cmd.MarkFlagRequired("type")
cmd.Flags().StringP("subtype", "s", "", "event subtype, such as \"Premier League\"")
cmd.MarkFlagRequired("subtype")
cmd.Flags().StringP("time", "m", "", "time that event result may be shown, such as \"2019-01-21 15:30:00\"")
cmd.MarkFlagRequired("time")
cmd.Flags().StringP("content", "c", "", "event content, such as '{\"team1\":\"ChelSea\", \"team2\":\"Manchester\",\"resultType\":\"score\"}'")
cmd.MarkFlagRequired("content")
cmd.Flags().StringP("introduction", "i", "", "event introduction, such as \"guess the sore result of football game between ChelSea and Manchester in 2019-01-21 14:00:00\"")
cmd.MarkFlagRequired("introduction")
}
func publishEvent(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
ty, _ := cmd.Flags().GetString("type")
subType, _ := cmd.Flags().GetString("subtype")
introduction, _ := cmd.Flags().GetString("introduction")
timeString, _ := cmd.Flags().GetString("time")
content, _ := cmd.Flags().GetString("content")
layout := "2006-01-02 15:04:05"
t, err := time.Parse(layout, timeString)
if err != nil {
fmt.Printf("time error:%v\n", err.Error())
return
}
params := &rpctypes.CreateTxIn{
Execer: types.ExecName(oraclety.OracleX),
ActionName: oraclety.CreateEventPublishTx,
Payload: []byte(fmt.Sprintf("{\"type\":\"%s\",\"subType\":\"%s\",\"time\":%d, \"content\":\"%s\", \"introduction\":\"%s\"}", ty, subType, t.Unix(), content, introduction)),
}
var res string
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.CreateTransaction", params, &res)
ctx.RunWithoutMarshal()
}
// OracleAbortEventRawTxCmd 取消发布事件
func OracleAbortEventRawTxCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "abort_publish_event",
Short: "abort publish the event",
Run: abortPublishEvent,
}
addAbortPublishEventFlags(cmd)
return cmd
}
func addAbortPublishEventFlags(cmd *cobra.Command) {
cmd.Flags().StringP("eventID", "e", "", "eventID")
cmd.MarkFlagRequired("eventID")
}
func abortPublishEvent(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
eventID, _ := cmd.Flags().GetString("eventID")
params := &rpctypes.CreateTxIn{
Execer: types.ExecName(oraclety.OracleX),
ActionName: oraclety.CreateAbortEventPublishTx,
Payload: []byte(fmt.Sprintf("{\"eventID\":\"%s\"}", eventID)),
}
var res string
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.CreateTransaction", params, &res)
ctx.RunWithoutMarshal()
}
// OraclePrePublishResultRawTxCmd 预发布结果
func OraclePrePublishResultRawTxCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "prepublish_result",
Short: "pre publish result of a new event",
Run: prePublishResult,
}
addPrePublishResultFlags(cmd)
return cmd
}
func addPrePublishResultFlags(cmd *cobra.Command) {
cmd.Flags().StringP("eventID", "e", "", "eventID")
cmd.MarkFlagRequired("eventID")
cmd.Flags().StringP("source", "s", "", "source where result from")
cmd.MarkFlagRequired("source")
cmd.Flags().StringP("result", "r", "", "result string")
cmd.MarkFlagRequired("result")
}
func prePublishResult(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
eventID, _ := cmd.Flags().GetString("eventID")
source, _ := cmd.Flags().GetString("source")
result, _ := cmd.Flags().GetString("result")
params := &rpctypes.CreateTxIn{
Execer: types.ExecName(oraclety.OracleX),
ActionName: oraclety.CreatePrePublishResultTx,
Payload: []byte(fmt.Sprintf("{\"eventID\":\"%s\", \"source\":\"%s\", \"result\":\"%s\"}", eventID, source, result)),
}
var res string
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.CreateTransaction", params, &res)
ctx.RunWithoutMarshal()
}
// OracleAbortPrePubResultRawTxCmd 取消预发布的事件结果
func OracleAbortPrePubResultRawTxCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "abort_result",
Short: "abort result pre-published before",
Run: abortPrePubResult,
}
addAbortPrePubResultFlags(cmd)
return cmd
}
func addAbortPrePubResultFlags(cmd *cobra.Command) {
cmd.Flags().StringP("eventID", "e", "", "eventID")
cmd.MarkFlagRequired("eventID")
}
func abortPrePubResult(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
eventID, _ := cmd.Flags().GetString("eventID")
params := &rpctypes.CreateTxIn{
Execer: types.ExecName(oraclety.OracleX),
ActionName: oraclety.CreateAbortResultPrePublishTx,
Payload: []byte(fmt.Sprintf("{\"eventID\":\"%s\"}", eventID)),
}
var res string
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.CreateTransaction", params, &res)
ctx.RunWithoutMarshal()
}
// OraclePublishResultRawTxCmd 发布事件结果
func OraclePublishResultRawTxCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "publish_result",
Short: "publish final result event",
Run: publishResult,
}
addPublishResultFlags(cmd)
return cmd
}
func addPublishResultFlags(cmd *cobra.Command) {
cmd.Flags().StringP("eventID", "e", "", "eventID")
cmd.MarkFlagRequired("eventID")
cmd.Flags().StringP("source", "s", "", "source where result from")
cmd.MarkFlagRequired("source")
cmd.Flags().StringP("result", "r", "", "result string, such as \"{\"team1\":3, \"team2\":2}\"")
cmd.MarkFlagRequired("result")
}
func publishResult(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
eventID, _ := cmd.Flags().GetString("eventID")
source, _ := cmd.Flags().GetString("source")
result, _ := cmd.Flags().GetString("result")
params := &rpctypes.CreateTxIn{
Execer: types.ExecName(oraclety.OracleX),
ActionName: oraclety.CreateResultPublishTx,
Payload: []byte(fmt.Sprintf("{\"eventID\":\"%s\", \"source\":\"%s\", \"result\":\"%s\"}", eventID, source, result)),
}
var res string
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.CreateTransaction", params, &res)
ctx.RunWithoutMarshal()
}
// OracleQueryRawTxCmd 查询事件
func OracleQueryRawTxCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "query",
Short: "query event and event status",
Run: oracleQuery,
}
addOracleQueryFlags(cmd)
return cmd
}
func addOracleQueryFlags(cmd *cobra.Command) {
cmd.Flags().StringP("last_eventID", "l", "", "last eventID, to get next page data")
cmd.MarkFlagRequired("last_eventID")
cmd.Flags().StringP("type", "t", "", "event type, such as \"football\"")
cmd.MarkFlagRequired("type")
cmd.Flags().StringP("status", "s", "", "status, number 1-5")
cmd.MarkFlagRequired("status")
cmd.Flags().StringP("addr", "a", "", "address of event creator")
cmd.MarkFlagRequired("addr")
cmd.Flags().StringP("eventIDs", "d", "", "eventIDs, used for query eventInfo, use comma between many ids")
cmd.MarkFlagRequired("eventIDs")
}
func oracleQuery(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
lastEventID, _ := cmd.Flags().GetString("last_eventID")
eventIDs, _ := cmd.Flags().GetString("eventIDs")
ty, _ := cmd.Flags().GetString("type")
statusStr, _ := cmd.Flags().GetString("status")
status, _ := strconv.ParseInt(statusStr, 10, 32)
addr, _ := cmd.Flags().GetString("addr")
var params rpctypes.Query4Jrpc
params.Execer = oraclety.OracleX
req := &oraclety.QueryEventID{
Status: int32(status),
Addr: addr,
Type: ty,
EventID: lastEventID,
}
params.Payload = types.MustPBToJSON(req)
if eventIDs != "" {
params.FuncName = oraclety.FuncNameQueryOracleListByIDs
var eIDs []string
ids := strings.Split(eventIDs, ",")
eIDs = append(eIDs, ids...)
req := &oraclety.QueryOracleInfos{EventID: eIDs}
params.Payload = types.MustPBToJSON(req)
var res oraclety.ReplyOracleStatusList
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.Query", params, &res)
ctx.Run()
} else if statusStr != "" {
if status < 0 || status > 5 {
fmt.Println("Error: status must be 1-5")
cmd.Help()
return
} else if addr != "" {
params.FuncName = oraclety.FuncNameQueryEventIDByAddrAndStatus
} else if ty != "" {
params.FuncName = oraclety.FuncNameQueryEventIDByTypeAndStatus
} else {
params.FuncName = oraclety.FuncNameQueryEventIDByStatus
}
var res oraclety.ReplyEventIDs
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.Query", params, &res)
ctx.Run()
} else {
fmt.Println("Error: requeres at least one of eventID, eventIDs, status")
cmd.Help()
}
}
/*
* Copyright Fuzamei Corp. 2018 All Rights Reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
package executor
/*
数据发布合约流程
1 由管理员通过manage合约增加问题发布者信息,数据预发布者信息,数据最终发布者信息
2 问题发布者发布问题
2.1 问题发布者撤销问题
3 数据预发布者预发布数据
3.1 数据预发布者撤销预发布数据
4 数据最终发布者发布数据
5 通过查询接口查询问题结果
*/
/*
* Copyright Fuzamei Corp. 2018 All Rights Reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
package executor
import (
"github.com/33cn/chain33/types"
oty "github.com/33cn/plugin/plugin/dapp/oracle/types"
)
func (o *oracle) Exec_EventPublish(payload *oty.EventPublish, tx *types.Transaction, index int) (*types.Receipt, error) {
action := newOracleAction(o, tx, index)
return action.eventPublish(payload)
}
func (o *oracle) Exec_EventAbort(payload *oty.EventAbort, tx *types.Transaction, index int) (*types.Receipt, error) {
action := newOracleAction(o, tx, index)
return action.eventAbort(payload)
}
func (o *oracle) Exec_ResultPrePublish(payload *oty.ResultPrePublish, tx *types.Transaction, index int) (*types.Receipt, error) {
action := newOracleAction(o, tx, index)
return action.resultPrePublish(payload)
}
func (o *oracle) Exec_ResultAbort(payload *oty.ResultAbort, tx *types.Transaction, index int) (*types.Receipt, error) {
action := newOracleAction(o, tx, index)
return action.resultAbort(payload)
}
func (o *oracle) Exec_ResultPublish(payload *oty.ResultPublish, tx *types.Transaction, index int) (*types.Receipt, error) {
action := newOracleAction(o, tx, index)
return action.resultPublish(payload)
}
/*
* Copyright Fuzamei Corp. 2018 All Rights Reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
package executor
import (
"github.com/33cn/chain33/types"
oty "github.com/33cn/plugin/plugin/dapp/oracle/types"
)
func (o *oracle) execDelLocal(receipt *types.ReceiptData) (*types.LocalDBSet, error) {
set := &types.LocalDBSet{}
table := oty.NewTable(o.GetLocalDB())
for _, item := range receipt.Logs {
var oraclelog oty.ReceiptOracle
err := types.Decode(item.Log, &oraclelog)
if err != nil {
return nil, err
}
//回滚时如果状态为EventPublished则删除记录,否则回滚至上一状态
if oraclelog.Status == oty.EventPublished {
err = table.Del([]byte(oraclelog.EventID))
if err != nil {
return nil, err
}
} else {
oraclelog.Status = oraclelog.PreStatus
err = table.Replace(&oraclelog)
if err != nil {
return nil, err
}
}
kvs, err := table.Save()
if err != nil {
return nil, err
}
set.KV = append(set.KV, kvs...)
}
return set, nil
}
func (o *oracle) ExecDelLocal_EventPublish(payload *oty.EventPublish, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return o.execDelLocal(receiptData)
}
func (o *oracle) ExecDelLocal_EventAbort(payload *oty.EventAbort, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return o.execDelLocal(receiptData)
}
func (o *oracle) ExecDelLocal_ResultPrePublish(payload *oty.ResultPrePublish, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return o.execDelLocal(receiptData)
}
func (o *oracle) ExecDelLocal_ResultAbort(payload *oty.ResultAbort, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return o.execDelLocal(receiptData)
}
func (o *oracle) ExecDelLocal_ResultPublish(payload *oty.ResultPublish, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return o.execDelLocal(receiptData)
}
/*
* Copyright Fuzamei Corp. 2018 All Rights Reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
package executor
import (
"github.com/33cn/chain33/types"
oty "github.com/33cn/plugin/plugin/dapp/oracle/types"
)
func (o *oracle) execLocal(receipt *types.ReceiptData) (*types.LocalDBSet, error) {
set := &types.LocalDBSet{}
if receipt.GetTy() != types.ExecOk {
return set, nil
}
table := oty.NewTable(o.GetLocalDB())
for _, item := range receipt.Logs {
if item.Ty >= oty.TyLogEventPublish && item.Ty <= oty.TyLogResultPublish {
var oraclelog oty.ReceiptOracle
err := types.Decode(item.Log, &oraclelog)
if err != nil {
return nil, err
}
err = table.Replace(&oraclelog)
if err != nil {
return nil, err
}
kvs, err := table.Save()
if err != nil {
return nil, err
}
set.KV = append(set.KV, kvs...)
}
}
return set, nil
}
func (o *oracle) ExecLocal_EventPublish(payload *oty.EventPublish, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return o.execLocal(receiptData)
}
func (o *oracle) ExecLocal_EventAbort(payload *oty.EventAbort, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return o.execLocal(receiptData)
}
func (o *oracle) ExecLocal_ResultPrePublish(payload *oty.ResultPrePublish, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return o.execLocal(receiptData)
}
func (o *oracle) ExecLocal_ResultAbort(payload *oty.ResultAbort, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return o.execLocal(receiptData)
}
func (o *oracle) ExecLocal_ResultPublish(payload *oty.ResultPublish, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
return o.execLocal(receiptData)
}
/*
* Copyright Fuzamei Corp. 2018 All Rights Reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
package executor
import (
log "github.com/33cn/chain33/common/log/log15"
drivers "github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
oty "github.com/33cn/plugin/plugin/dapp/oracle/types"
)
var olog = log.New("module", "execs.oracle")
var driverName = oty.OracleX
// Init 执行器初始化
func Init(name string, sub []byte) {
drivers.Register(newOracle().GetName(), newOracle, types.GetDappFork(driverName, "Enable"))
}
// GetName 获取oracle执行器名
func GetName() string {
return newOracle().GetName()
}
func newOracle() drivers.Driver {
t := &oracle{}
t.SetChild(t)
t.SetExecutorType(types.LoadExecutorType(driverName))
return t
}
func init() {
ety := types.LoadExecutorType(driverName)
ety.InitFuncList(types.ListMethod(&oracle{}))
}
// oracle driver
type oracle struct {
drivers.DriverBase
}
func (ora *oracle) GetDriverName() string {
return oty.OracleX
}
This diff is collapsed.
/*
* Copyright Fuzamei Corp. 2018 All Rights Reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
package executor
import (
"github.com/33cn/chain33/types"
oty "github.com/33cn/plugin/plugin/dapp/oracle/types"
)
//从statedb 读取原始数据
func (o *oracle) Query_QueryOraclesByIDs(in *oty.QueryOracleInfos) (types.Message, error) {
return getOracleLisByIDs(o.GetStateDB(), in)
}
//通过状态查询ids
func (o *oracle) Query_QueryEventIDsByStatus(in *oty.QueryEventID) (types.Message, error) {
eventIds, err := getEventIDListByStatus(o.GetLocalDB(), in.Status, in.EventID)
if err != nil {
return nil, err
}
return eventIds, nil
}
//通过状态 和 地址查询
func (o *oracle) Query_QueryEventIDsByAddrAndStatus(in *oty.QueryEventID) (types.Message, error) {
eventIds, err := getEventIDListByAddrAndStatus(o.GetLocalDB(), in.Addr, in.Status, in.EventID)
if err != nil {
return nil, err
}
return eventIds, nil
}
//通过类型和状态查询
func (o *oracle) Query_QueryEventIDsByTypeAndStatus(in *oty.QueryEventID) (types.Message, error) {
eventIds, err := getEventIDListByTypeAndStatus(o.GetLocalDB(), in.Type, in.Status, in.EventID)
if err != nil {
return nil, err
}
return eventIds, nil
}
/*
* Copyright Fuzamei Corp. 2018 All Rights Reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
package oracle
import (
"github.com/33cn/chain33/pluginmgr"
"github.com/33cn/plugin/plugin/dapp/oracle/commands"
"github.com/33cn/plugin/plugin/dapp/oracle/executor"
"github.com/33cn/plugin/plugin/dapp/oracle/types"
)
func init() {
pluginmgr.Register(&pluginmgr.PluginBase{
Name: types.OracleX,
ExecName: executor.GetName(),
Exec: executor.Init,
Cmd: commands.OracleCmd,
//RPC: rpc.Init,
})
}
all:
sh ./create_protobuf.sh
#!/bin/sh
protoc --go_out=plugins=grpc:../types ./*.proto --proto_path=. --proto_path="../../../../vendor/github.com/33cn/chain33/types/proto/"
syntax = "proto3";
package types;
//事件
message OracleStatus {
string eventID = 1; //事件ID
string addr = 2; //发布者地址
string type = 3; //游戏类别
string subType = 4; //游戏子类别
int64 time = 5; //结果公布参考时间
string content = 6; //事件内容
string introduction = 7; //事件描述
EventStatus status = 8; //操作状态
string source = 9; //数据来源
string result = 10; //事件结果
EventStatus preStatus=11; //上次操作后状态及操作者地址
}
// action
message OracleAction {
oneof value {
EventPublish eventPublish = 1;
EventAbort eventAbort = 2;
ResultPrePublish resultPrePublish = 3;
ResultPublish resultPublish = 4;
ResultAbort resultAbort = 5;
}
int32 Ty = 7;
}
message EventStatus {
string opAddr = 1; //修改事件状态的地址
int32 status = 2; //事件状态
}
message EventPublish {
string type = 2; //游戏类别
string subType = 3; //游戏子类别
int64 time = 4; //结果公布参考时间
string content = 5; //事件内容
string introduction = 6; //事件描述
}
message EventAbort {
string eventID = 2; //发布事件的ID
}
message ResultPrePublish {
string eventID = 2; //发布事件的ID
string source = 3; //数据来源
string result = 4; //发布数据
}
message ResultPublish {
string eventID = 2; //发布事件的ID
string source = 3; //数据来源
string result = 4; //发布数据
}
message ResultAbort {
string eventID = 2; //发布事件的ID
}
// localDB
message EventRecord {
string eventID = 1; //发布的事件的ID
}
message QueryOracleInfos {
repeated string eventID = 1; //发布的事件的ID
}
message ReplyEventIDs {
repeated string eventID = 1; //发布事件的ID
}
message QueryEventID {
int32 status = 1; //事件状态
string addr = 2; //事件发布者的地址
string type = 3; //事件类型
string eventID = 4; //事件ID
}
message ReceiptOracle {
string eventID = 1; //发布事件ID
int32 status = 2; //事件状态
string addr = 3; //事件发布者的地址
string type = 4; //事件类型
int32 preStatus = 6;//事件的前一个状态
}
message ReplyOracleStatusList {
repeated OracleStatus status = 1; //状态集
}
\ No newline at end of file
/*
* Copyright Fuzamei Corp. 2018 All Rights Reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
package rpc_test
import (
"fmt"
"strings"
"testing"
"time"
"github.com/33cn/chain33/rpc/jsonclient"
rpctypes "github.com/33cn/chain33/rpc/types"
"github.com/33cn/chain33/types"
"github.com/33cn/chain33/util/testnode"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
commonlog "github.com/33cn/chain33/common/log"
_ "github.com/33cn/chain33/system"
_ "github.com/33cn/plugin/plugin"
oty "github.com/33cn/plugin/plugin/dapp/oracle/types"
)
func init() {
commonlog.SetLogLevel("error")
}
func TestJRPCChannel(t *testing.T) {
// 启动RPCmocker
mocker := testnode.New("--notset--", nil)
defer func() {
mocker.Close()
}()
mocker.Listen()
jrpcClient := mocker.GetJSONC()
assert.NotNil(t, jrpcClient)
testCases := []struct {
fn func(*testing.T, *jsonclient.JSONClient) error
}{
{fn: testPublishEventRawCmd},
{fn: testAbortEventRawTxCmd},
{fn: testPrePublishResultRawTxCmd},
{fn: testAbortPrePubResultRawTxCmd},
{fn: testPublishResultRawTxCmd},
}
for index, testCase := range testCases {
err := testCase.fn(t, jrpcClient)
if err == nil {
continue
}
assert.NotEqualf(t, err, types.ErrActionNotSupport, "test index %d", index)
if strings.Contains(err.Error(), "rpc: can't find") {
assert.FailNowf(t, err.Error(), "test index %d", index)
}
}
testCases = []struct {
fn func(*testing.T, *jsonclient.JSONClient) error
}{
{fn: testQueryOracleListByIDsRawTxCmd},
{fn: testQueryEventIDByAddrAndStatusRawTxCmd},
{fn: testQueryEventIDByTypeAndStatusRawTxCmd},
{fn: testQueryEventIDByStatusRawTxCmd},
}
result := []error{
oty.ErrParamNeedIDs,
oty.ErrParamStatusInvalid,
types.ErrNotFound,
types.ErrNotFound,
}
for index, testCase := range testCases {
err := testCase.fn(t, jrpcClient)
assert.Equal(t, result[index], err, fmt.Sprint(index))
}
}
func testPublishEventRawCmd(t *testing.T, jrpc *jsonclient.JSONClient) error {
timeStr := "2019-01-21 15:30:00"
layout := "2006-01-02 15:04:05"
ti, err := time.Parse(layout, timeStr)
if err != nil {
fmt.Printf("time error:%v\n", err.Error())
return errors.Errorf("time error:%v\n", err.Error())
}
payload := &oty.EventPublish{
Type: "football",
SubType: "Premier League",
Time: ti.Unix(),
Content: "{\"team1\":\"ChelSea\", \"team2\":\"Manchester\",\"resultType\":\"score\"}",
Introduction: "guess the sore result of football game between ChelSea and Manchester in 2019-01-21 14:00:00",
}
params := &rpctypes.CreateTxIn{
Execer: types.ExecName(oty.OracleX),
ActionName: oty.CreateEventPublishTx,
Payload: types.MustPBToJSON(payload),
}
var res string
return jrpc.Call("Chain33.CreateTransaction", params, &res)
}
func testAbortEventRawTxCmd(t *testing.T, jrpc *jsonclient.JSONClient) error {
payload := &oty.EventAbort{EventID: "123"}
params := &rpctypes.CreateTxIn{
Execer: types.ExecName(oty.OracleX),
ActionName: oty.CreateAbortEventPublishTx,
Payload: types.MustPBToJSON(payload),
}
var res string
return jrpc.Call("Chain33.CreateTransaction", params, &res)
}
func testPrePublishResultRawTxCmd(t *testing.T, jrpc *jsonclient.JSONClient) error {
payload := &oty.ResultPrePublish{
EventID: "123",
Source: "新浪体育",
Result: "{\"team1\":3, \"team2\":2}",
}
params := &rpctypes.CreateTxIn{
Execer: types.ExecName(oty.OracleX),
ActionName: oty.CreatePrePublishResultTx,
Payload: types.MustPBToJSON(payload),
}
var res string
return jrpc.Call("Chain33.CreateTransaction", params, &res)
}
func testAbortPrePubResultRawTxCmd(t *testing.T, jrpc *jsonclient.JSONClient) error {
payload := &oty.EventAbort{EventID: "123"}
params := &rpctypes.CreateTxIn{
Execer: types.ExecName(oty.OracleX),
ActionName: oty.CreateAbortResultPrePublishTx,
Payload: types.MustPBToJSON(payload),
}
var res string
return jrpc.Call("Chain33.CreateTransaction", params, &res)
}
func testPublishResultRawTxCmd(t *testing.T, jrpc *jsonclient.JSONClient) error {
payload := &oty.ResultPrePublish{
EventID: "123",
Source: "新浪体育",
Result: "{\"team1\":3, \"team2\":2}",
}
params := &rpctypes.CreateTxIn{
Execer: types.ExecName(oty.OracleX),
ActionName: oty.CreateResultPublishTx,
Payload: types.MustPBToJSON(payload),
}
var res string
return jrpc.Call("Chain33.CreateTransaction", params, &res)
}
func testQueryOracleListByIDsRawTxCmd(t *testing.T, jrpc *jsonclient.JSONClient) error {
var rep interface{}
var params rpctypes.Query4Jrpc
req := &oty.QueryOracleInfos{}
params.Execer = oty.OracleX
params.FuncName = oty.FuncNameQueryOracleListByIDs
params.Payload = types.MustPBToJSON(req)
rep = &oty.ReplyOracleStatusList{}
return jrpc.Call("Chain33.Query", params, rep)
}
func testQueryEventIDByAddrAndStatusRawTxCmd(t *testing.T, jrpc *jsonclient.JSONClient) error {
var rep interface{}
var params rpctypes.Query4Jrpc
req := &oty.QueryEventID{}
params.Execer = oty.OracleX
params.FuncName = oty.FuncNameQueryEventIDByAddrAndStatus
params.Payload = types.MustPBToJSON(req)
rep = &oty.ReplyEventIDs{}
return jrpc.Call("Chain33.Query", params, rep)
}
func testQueryEventIDByTypeAndStatusRawTxCmd(t *testing.T, jrpc *jsonclient.JSONClient) error {
var rep interface{}
var params rpctypes.Query4Jrpc
req := &oty.QueryEventID{
Type: "football",
Status: 1,
Addr: "",
}
params.Execer = oty.OracleX
params.FuncName = oty.FuncNameQueryEventIDByTypeAndStatus
params.Payload = types.MustPBToJSON(req)
rep = &oty.ReplyEventIDs{}
return jrpc.Call("Chain33.Query", params, rep)
}
func testQueryEventIDByStatusRawTxCmd(t *testing.T, jrpc *jsonclient.JSONClient) error {
var rep interface{}
var params rpctypes.Query4Jrpc
req := &oty.QueryEventID{
Status: 1,
Type: "",
Addr: "",
}
params.Execer = oty.OracleX
params.FuncName = oty.FuncNameQueryEventIDByStatus
params.Payload = types.MustPBToJSON(req)
rep = &oty.ReplyEventIDs{}
return jrpc.Call("Chain33.Query", params, rep)
}
This diff is collapsed.
/*
* Copyright Fuzamei Corp. 2018 All Rights Reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
package types
import "errors"
var (
// OracleX oracle name
OracleX = "oracle"
)
// oracle action type
const (
ActionEventPublish = iota + 1 //事件发布
ActionResultPrePublish
ActionResultPublish
ActionEventAbort
ActionResultAbort
)
// oracle status
const (
NoEvent = iota
EventPublished
EventAborted
ResultPrePublished
ResultAborted
ResultPublished
)
// log type define
const (
TyLogEventPublish = 810
TyLogEventAbort = 811
TyLogResultPrePublish = 812
TyLogResultAbort = 813
TyLogResultPublish = 814
)
// executor action and function define
const (
// FuncNameQueryOracleListByIDs 根据ids查询OracleStatus
FuncNameQueryOracleListByIDs = "QueryOraclesByIDs"
// FuncNameQueryEventIDByStatus 根据状态查询eventID
FuncNameQueryEventIDByStatus = "QueryEventIDsByStatus"
// FuncNameQueryEventIDByAddrAndStatus 根据创建者地址和状态查询eventID
FuncNameQueryEventIDByAddrAndStatus = "QueryEventIDsByAddrAndStatus"
// FuncNameQueryEventIDByTypeAndStatus 根据事件类型和状态查询eventID
FuncNameQueryEventIDByTypeAndStatus = "QueryEventIDsByTypeAndStatus"
// CreateEventPublishTx 创建发布事件交易
CreateEventPublishTx = "EventPublish"
// CreateAbortEventPublishTx 创建取消发布事件交易
CreateAbortEventPublishTx = "EventAbort"
// CreatePrePublishResultTx 创建预发布事件结果交易
CreatePrePublishResultTx = "ResultPrePublish"
// CreateAbortResultPrePublishTx 创建取消预发布的事件结果交易
CreateAbortResultPrePublishTx = "ResultAbort"
// CreateResultPublishTx 创建预发布事件结果交易
CreateResultPublishTx = "ResultPublish"
)
// query param define
const (
// ListDESC 降序
ListDESC = int32(0)
// DefaultCount 默认一次取多少条记录
DefaultCount = int32(20)
)
// Errors for oracle
var (
ErrTimeMustBeFuture = errors.New("ErrTimeMustBeFuture")
ErrNoPrivilege = errors.New("ErrNoPrivilege")
ErrOracleRepeatHash = errors.New("ErrOracleRepeatHash")
ErrEventIDNotFound = errors.New("ErrEventIDNotFound")
ErrEventAbortNotAllowed = errors.New("ErrEventAbortNotAllowed")
ErrResultPrePublishNotAllowed = errors.New("ErrResultPrePublishNotAllowed")
ErrPrePublishAbortNotAllowed = errors.New("ErrPrePublishAbortNotAllowed")
ErrResultPublishNotAllowed = errors.New("ErrResultPublishNotAllowed")
ErrParamNeedIDs = errors.New("ErrParamNeedIDs")
ErrParamStatusInvalid = errors.New("ErrParamStatusInvalid")
ErrParamAddressMustnotEmpty = errors.New("ErrParamAddressMustnotEmpty")
ErrParamTypeMustNotEmpty = errors.New("ErrParamTypeMustNotEmpty")
)
This diff is collapsed.
package types
import (
"fmt"
"github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/common/db/table"
"github.com/33cn/chain33/types"
)
/*
table struct
data: oracle
index: addr,status,index,type
*/
var opt = &table.Option{
Prefix: "LODB",
Name: "oracle",
Primary: "eventid",
Index: []string{"status", "addr_status", "type_status"},
}
//NewTable 新建表
func NewTable(kvdb db.KV) *table.Table {
rowmeta := NewOracleRow()
table, err := table.NewTable(rowmeta, kvdb, opt)
if err != nil {
panic(err)
}
return table
}
//OracleRow table meta 结构
type OracleRow struct {
*ReceiptOracle
}
//NewOracleRow 新建一个meta 结构
func NewOracleRow() *OracleRow {
return &OracleRow{ReceiptOracle: &ReceiptOracle{}}
}
//CreateRow 新建数据行(注意index 数据一定也要保存到数据中,不能就保存eventid)
func (tx *OracleRow) CreateRow() *table.Row {
return &table.Row{Data: &ReceiptOracle{}}
}
//SetPayload 设置数据
func (tx *OracleRow) SetPayload(data types.Message) error {
if txdata, ok := data.(*ReceiptOracle); ok {
tx.ReceiptOracle = txdata
return nil
}
return types.ErrTypeAsset
}
//Get 按照indexName 查询 indexValue
func (tx *OracleRow) Get(key string) ([]byte, error) {
if key == "eventid" {
return []byte(tx.EventID), nil
} else if key == "status" {
return []byte(fmt.Sprintf("%2d", tx.Status)), nil
} else if key == "addr_status" {
return []byte(fmt.Sprintf("%s:%2d", tx.Addr, tx.Status)), nil
} else if key == "type_status" {
return []byte(fmt.Sprintf("%s:%2d", tx.Type, tx.Status)), nil
}
return nil, types.ErrNotFound
}
/*
* Copyright Fuzamei Corp. 2018 All Rights Reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
package types
import (
"reflect"
"github.com/33cn/chain33/types"
)
func init() {
// init executor type
types.AllowUserExec = append(types.AllowUserExec, []byte(OracleX))
types.RegistorExecutor(OracleX, NewType())
types.RegisterDappFork(OracleX, "Enable", 0)
}
// OracleType 预言机执行器类型
type OracleType struct {
types.ExecTypeBase
}
// NewType 创建执行器类型
func NewType() *OracleType {
c := &OracleType{}
c.SetChild(c)
return c
}
// GetName 获取执行器名称
func (o *OracleType) GetName() string {
return OracleX
}
// GetPayload 获取oracle action
func (o *OracleType) GetPayload() types.Message {
return &OracleAction{}
}
// GetTypeMap 获取类型map
func (o *OracleType) GetTypeMap() map[string]int32 {
return map[string]int32{
"EventPublish": ActionEventPublish,
"EventAbort": ActionEventAbort,
"ResultPrePublish": ActionResultPrePublish,
"ResultAbort": ActionResultAbort,
"ResultPublish": ActionResultPublish,
}
}
// GetLogMap 获取日志map
func (o *OracleType) GetLogMap() map[int64]*types.LogInfo {
return map[int64]*types.LogInfo{
TyLogEventPublish: {Ty: reflect.TypeOf(ReceiptOracle{}), Name: "LogEventPublish"},
TyLogEventAbort: {Ty: reflect.TypeOf(ReceiptOracle{}), Name: "LogEventAbort"},
TyLogResultPrePublish: {Ty: reflect.TypeOf(ReceiptOracle{}), Name: "LogResultPrePublish"},
TyLogResultAbort: {Ty: reflect.TypeOf(ReceiptOracle{}), Name: "LogResultAbort"},
TyLogResultPublish: {Ty: reflect.TypeOf(ReceiptOracle{}), Name: "LogResultPublish"},
}
}
......@@ -16,6 +16,7 @@ import (
"github.com/33cn/chain33/queue"
"github.com/33cn/chain33/store"
"github.com/33cn/chain33/types"
"github.com/33cn/chain33/util"
"github.com/33cn/chain33/util/testnode"
"github.com/33cn/chain33/wallet"
"github.com/33cn/chain33/wallet/bipwallet"
......@@ -79,7 +80,7 @@ func (mock *testDataMock) init() {
func (mock *testDataMock) initMember() {
var q = queue.New("channel")
cfg, sub := testnode.GetDefaultConfig()
util.ResetDatadir(cfg, "$TEMP/")
wallet := wallet.New(cfg.Wallet, sub.Wallet)
wallet.SetQueueClient(q.Client())
mock.modules = append(mock.modules, wallet)
......
......@@ -15,6 +15,7 @@ import (
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/types"
"github.com/33cn/chain33/util"
"github.com/33cn/chain33/util/testnode"
pt "github.com/33cn/plugin/plugin/dapp/privacy/types"
"github.com/stretchr/testify/assert"
......@@ -22,6 +23,7 @@ import (
func createStore(t *testing.T) *privacyStore {
cfg, _ := testnode.GetDefaultConfig()
util.ResetDatadir(cfg, "$TEMP/")
cfgWallet := cfg.Wallet
walletStoreDB := dbm.NewDB("wallet", cfgWallet.Driver, cfgWallet.DbPath, cfgWallet.DbCache)
store := newStore(walletStoreDB)
......@@ -56,11 +58,6 @@ func TestPrivacyStore(t *testing.T) {
testStore_moveFTXO2UTXO(t)
testStore_unsetUTXO(t)
testStore_moveSTXO2FTXO(t)
testStore_moveFTXO2UTXOWhenFTXOExpire(t)
}
func testStore_moveFTXO2UTXOWhenFTXOExpire(t *testing.T) {
}
func testStore_moveSTXO2FTXO(t *testing.T) {
......
......@@ -10,6 +10,7 @@ import (
"fmt"
"os"
"github.com/33cn/chain33/common"
"github.com/33cn/chain33/rpc/jsonclient"
rpctypes "github.com/33cn/chain33/rpc/types"
"github.com/33cn/chain33/types"
......@@ -102,11 +103,17 @@ func CloseTicketCmd() *cobra.Command {
Short: "Close tickets",
Run: closeTicket,
}
addCloseBindAddr(cmd)
return cmd
}
func addCloseBindAddr(cmd *cobra.Command) {
cmd.Flags().StringP("miner_addr", "m", "", "miner address (optional)")
}
func closeTicket(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
bindAddr, _ := cmd.Flags().GetString("miner_addr")
status, err := getWalletStatus(rpcLaddr)
if err != nil {
fmt.Fprintln(os.Stderr, err)
......@@ -119,13 +126,17 @@ func closeTicket(cmd *cobra.Command, args []string) {
return
}
tClose := &ty.TicketClose{
MinerAddress: bindAddr,
}
var res types.ReplyHashes
rpc, err := jsonclient.NewJSONClient(rpcLaddr)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
err = rpc.Call("ticket.CloseTickets", nil, &res)
err = rpc.Call("ticket.CloseTickets", tClose, &res)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
......@@ -135,7 +146,12 @@ func closeTicket(cmd *cobra.Command, args []string) {
return
}
data, err := json.MarshalIndent(res, "", " ")
var tx txHashes
for _, hash := range res.Hashes {
tx.Hashes = append(tx.Hashes, common.ToHex(hash))
}
data, err := json.MarshalIndent(tx, "", " ")
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
......
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package commands
type txHashes struct {
Hashes []string `json:"hashes"`
}
......@@ -79,6 +79,7 @@ message TicketGenesis {
message TicketClose {
repeated string ticketId = 1;
string minerAddress = 2;
}
message TicketList {
......
......@@ -77,8 +77,9 @@ func (g *channelClient) GetTicketCount(ctx context.Context, in *types.ReqNil) (*
}
// CloseTickets close ticket
func (g *channelClient) CloseTickets(ctx context.Context, in *types.ReqNil) (*types.ReplyHashes, error) {
data, err := g.ExecWalletFunc(ty.TicketX, "CloseTickets", &types.ReqNil{})
func (g *channelClient) CloseTickets(ctx context.Context, in *ty.TicketClose) (*types.ReplyHashes, error) {
inn := *in
data, err := g.ExecWalletFunc(ty.TicketX, "CloseTickets", &inn)
if err != nil {
return nil, err
}
......@@ -107,8 +108,8 @@ func (c *Jrpc) GetTicketCount(in *types.ReqNil, result *int64) error {
}
// CloseTickets close ticket
func (c *Jrpc) CloseTickets(in *types.ReqNil, result *interface{}) error {
resp, err := c.cli.CloseTickets(context.Background(), &types.ReqNil{})
func (c *Jrpc) CloseTickets(in *ty.TicketClose, result *interface{}) error {
resp, err := c.cli.CloseTickets(context.Background(), in)
if err != nil {
return err
}
......
......@@ -90,7 +90,7 @@ func TestSetAutoMining(t *testing.T) {
func testCloseTicketsOK(t *testing.T) {
api := &mocks.QueueProtocolAPI{}
g := newGrpc(api)
var in = new(types.ReqNil)
var in = &ty.TicketClose{}
api.On("ExecWalletFunc", "ticket", "CloseTickets", in).Return(&types.ReplyHashes{}, nil)
data, err := g.CloseTickets(context.Background(), in)
assert.Nil(t, err, "the error should be nil")
......
This diff is collapsed.
......@@ -10,9 +10,9 @@ import (
)
// On_CloseTickets close ticket
func (policy *ticketPolicy) On_CloseTickets(req *types.ReqNil) (types.Message, error) {
func (policy *ticketPolicy) On_CloseTickets(req *ty.TicketClose) (types.Message, error) {
operater := policy.getWalletOperate()
reply, err := policy.forceCloseTicket(operater.GetBlockHeight() + 1)
reply, err := policy.forceCloseTicket(operater.GetBlockHeight()+1, req.MinerAddress)
if err != nil {
bizlog.Error("onCloseTickets", "forceCloseTicket error", err.Error())
} else {
......
......@@ -298,18 +298,29 @@ func (policy *ticketPolicy) checkNeedFlushTicket(tx *types.Transaction, receipt
return policy.needFlushTicket(tx, receipt)
}
func (policy *ticketPolicy) forceCloseTicket(height int64) (*types.ReplyHashes, error) {
return policy.forceCloseAllTicket(height)
func (policy *ticketPolicy) forceCloseTicket(height int64, minerAddr string) (*types.ReplyHashes, error) {
return policy.forceCloseAllTicket(height, minerAddr)
}
func (policy *ticketPolicy) forceCloseAllTicket(height int64) (*types.ReplyHashes, error) {
func (policy *ticketPolicy) forceCloseAllTicket(height int64, minerAddr string) (*types.ReplyHashes, error) {
var tlistMiner []*ty.Ticket
var err error
// get miner addr's tickets if any
if minerAddr != "" {
tlistMiner, err = policy.getForceCloseTickets(minerAddr)
if err != nil {
return nil, err
}
}
keys, err := policy.getWalletOperate().GetAllPrivKeys()
if err != nil {
return nil, err
}
var hashes types.ReplyHashes
for _, key := range keys {
hash, err := policy.forceCloseTicketsByAddr(height, key)
hash, err := policy.forceCloseTicketsByAddr(height, key, tlistMiner)
if err != nil {
bizlog.Error("forceCloseAllTicket", "forceCloseTicketsByAddr error", err)
continue
......@@ -334,8 +345,10 @@ func (policy *ticketPolicy) getTickets(addr string, status int32) ([]*ty.Ticket,
return reply.Tickets, nil
}
func (policy *ticketPolicy) forceCloseTicketsByAddr(height int64, priv crypto.PrivKey) ([]byte, error) {
addr := address.PubKeyToAddress(priv.PubKey().Bytes()).String()
func (policy *ticketPolicy) getForceCloseTickets(addr string) ([]*ty.Ticket, error) {
if addr == "" {
return nil, nil
}
tlist1, err1 := policy.getTickets(addr, 1)
if err1 != nil && err1 != types.ErrNotFound {
return nil, err1
......@@ -344,7 +357,23 @@ func (policy *ticketPolicy) forceCloseTicketsByAddr(height int64, priv crypto.Pr
if err2 != nil && err2 != types.ErrNotFound {
return nil, err1
}
tlist := append(tlist1, tlist2...)
return append(tlist1, tlist2...), nil
}
func (policy *ticketPolicy) forceCloseTicketsByAddr(height int64, priv crypto.PrivKey, tListMiner []*ty.Ticket) ([]byte, error) {
addr := address.PubKeyToAddress(priv.PubKey().Bytes()).String()
tlist, err := policy.getForceCloseTickets(addr)
if err != nil {
return nil, err
}
for _, ticket := range tListMiner {
if ticket.ReturnAddress == addr {
tlist = append(tlist, ticket)
}
}
var ids []string
var tl []*ty.Ticket
now := types.Now().Unix()
......
......@@ -14,10 +14,12 @@ import (
"github.com/stretchr/testify/require"
_ "github.com/33cn/chain33/system"
"github.com/33cn/chain33/types"
_ "github.com/33cn/plugin/plugin"
)
func Test_WalletTicket(t *testing.T) {
minerAddr := "12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv"
t.Log("Begin wallet ticket test")
cfg, sub := testnode.GetDefaultConfig()
......@@ -26,7 +28,7 @@ func Test_WalletTicket(t *testing.T) {
defer mock33.Close()
err := mock33.WaitHeight(0)
assert.Nil(t, err)
msg, err := mock33.GetAPI().Query(ty.TicketX, "TicketList", &ty.TicketList{Addr: "12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv", Status: 1})
msg, err := mock33.GetAPI().Query(ty.TicketX, "TicketList", &ty.TicketList{Addr: minerAddr, Status: 1})
assert.Nil(t, err)
ticketList := msg.(*ty.ReplyTicketList)
assert.NotNil(t, ticketList)
......@@ -37,5 +39,11 @@ func Test_WalletTicket(t *testing.T) {
header, err := mock33.GetAPI().GetLastHeader()
require.Equal(t, err, nil)
require.Equal(t, header.Height >= 2, true)
in := &ty.TicketClose{MinerAddress: minerAddr}
msg, err = mock33.GetAPI().ExecWalletFunc(ty.TicketX, "CloseTickets", in)
assert.Nil(t, err)
hashes := msg.(*types.ReplyHashes)
assert.NotNil(t, hashes)
t.Log("End wallet ticket test")
}
......@@ -133,14 +133,13 @@ func calcOnesBuyOrderPrefixStatus(addr string, status int32) []byte {
// 特定帐号下的订单
// 这里状态进行转化, 分成 状态和类型, 状态三种, 类型 两种
// on: Onsale Onbuy
// on: OnSale OnBuy
// done: Soldout boughtOut
// revoke: RevokeSell RevokeBuy
// buy/sell 两种类型
// 目前页面是按addr, 状态来
//
func calcOnesOrderKey(addr string, status int32, ty int32, height int64, key string) []byte {
return []byte(fmt.Sprintf(orderASTHK+"%s:%d:%d:%010d:%s", addr, status, ty, height, key))
return []byte(fmt.Sprintf(orderASTHK+"%s:%d:%010d:%d:%s", addr, status, height, ty, key))
}
func calcOnesOrderPrefixStatus(addr string, status int32) []byte {
......
......@@ -254,7 +254,7 @@ func (t *trade) replyReplySellOrderfromID(key []byte) *pty.ReplySellOrder {
return sellOrder2reply(sellorder)
}
} else { // txhash as key
txResult, err := getTx(key, t.GetLocalDB())
txResult, err := getTx(key, t.GetLocalDB(), t.GetAPI())
tradelog.Debug("GetOnesSellOrder ", "load txhash", string(key))
if err != nil {
return nil
......@@ -272,7 +272,7 @@ func (t *trade) replyReplyBuyOrderfromID(key []byte) *pty.ReplyBuyOrder {
return buyOrder2reply(buyOrder)
}
} else { // txhash as key
txResult, err := getTx(key, t.GetLocalDB())
txResult, err := getTx(key, t.GetLocalDB(), t.GetAPI())
tradelog.Debug("replyReplyBuyOrderfromID ", "load txhash", string(key))
if err != nil {
return nil
......@@ -601,7 +601,7 @@ func (t *trade) loadOrderFromKey(key []byte) *pty.ReplyTradeOrder {
tradelog.Debug("trade Query", "id", string(key), "check-prefix", sellIDPrefix)
if strings.HasPrefix(string(key), sellIDPrefix) {
txHash := strings.Replace(string(key), sellIDPrefix, "0x", 1)
txResult, err := getTx([]byte(txHash), t.GetLocalDB())
txResult, err := getTx([]byte(txHash), t.GetLocalDB(), t.GetAPI())
tradelog.Debug("loadOrderFromKey ", "load txhash", txResult)
if err != nil {
return nil
......@@ -618,7 +618,7 @@ func (t *trade) loadOrderFromKey(key []byte) *pty.ReplyTradeOrder {
return reply
} else if strings.HasPrefix(string(key), buyIDPrefix) {
txHash := strings.Replace(string(key), buyIDPrefix, "0x", 1)
txResult, err := getTx([]byte(txHash), t.GetLocalDB())
txResult, err := getTx([]byte(txHash), t.GetLocalDB(), t.GetAPI())
tradelog.Debug("loadOrderFromKey ", "load txhash", txResult)
if err != nil {
return nil
......@@ -633,7 +633,7 @@ func (t *trade) loadOrderFromKey(key []byte) *pty.ReplyTradeOrder {
reply.Status = buyOrder.Status
return reply
}
txResult, err := getTx(key, t.GetLocalDB())
txResult, err := getTx(key, t.GetLocalDB(), t.GetAPI())
tradelog.Debug("loadOrderFromKey ", "load txhash", string(key))
if err != nil {
return nil
......@@ -644,7 +644,7 @@ func (t *trade) loadOrderFromKey(key []byte) *pty.ReplyTradeOrder {
func (t *trade) GetOnesOrderWithStatus(req *pty.ReqAddrAssets) (types.Message, error) {
fromKey := []byte("")
if len(req.FromKey) != 0 {
order := t.loadOrderFromKey(fromKey)
order := t.loadOrderFromKey([]byte(req.FromKey))
if order == nil {
tradelog.Error("GetOnesOrderWithStatus", "key not exist", req.FromKey)
return nil, types.ErrInvalidParam
......
......@@ -10,6 +10,7 @@ import (
"strconv"
"github.com/33cn/chain33/account"
"github.com/33cn/chain33/client"
"github.com/33cn/chain33/common"
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/system/dapp"
......@@ -110,22 +111,23 @@ func getSellOrderFromID(sellID []byte, db dbm.KV) (*pty.SellOrder, error) {
return &sellOrder, nil
}
func getTx(txHash []byte, db dbm.KV) (*types.TxResult, error) {
func getTx(txHash []byte, db dbm.KV, api client.QueueProtocolAPI) (*types.TxResult, error) {
hash, err := common.FromHex(string(txHash))
if err != nil {
return nil, err
}
value, err := db.Get(hash)
value, err := api.QueryTx(&types.ReqHash{Hash: hash})
if err != nil {
tradelog.Error("getTx", "Failed to get value from db with getTx", string(txHash))
return nil, err
}
var txResult types.TxResult
err = types.Decode(value, &txResult)
if err != nil {
tradelog.Error("getTx", "Failed to decode TxResult", string(txHash))
return nil, err
txResult := types.TxResult{
Height: value.Height,
Index: int32(value.Index),
Tx: value.Tx,
Receiptdate: value.Receipt,
Blocktime: value.Blocktime,
ActionName: value.ActionName,
}
return &txResult, nil
}
......
......@@ -474,7 +474,7 @@ func (db *Database) Commit(node common.Hash, report bool) error {
// db.lock.RUnlock()
// return err
//}
//if batch.ValueSize() > ethdb.IdealBatchSize {
//if batch.ValueLen() > ethdb.IdealBatchSize {
// if err := batch.Write(); err != nil {
// return err
// }
......
......@@ -525,7 +525,7 @@ func (db *Database) dereference(child common.Hash, parent common.Hash) {
// // db.lock.RUnlock()
// // return err
// //}
// //if batch.ValueSize() > ethdb.IdealBatchSize {
// //if batch.ValueLen() > ethdb.IdealBatchSize {
// // if err := batch.Write(); err != nil {
// // db.lock.RUnlock()
// // return err
......@@ -545,7 +545,7 @@ func (db *Database) dereference(child common.Hash, parent common.Hash) {
// // return err
// //}
// //// If we exceeded the ideal batch size, commit and reset
// //if batch.ValueSize() >= ethdb.IdealBatchSize {
// //if batch.ValueLen() >= ethdb.IdealBatchSize {
// // if err := batch.Write(); err != nil {
// // log.Error("Failed to write flush list to disk", "err", err)
// // db.lock.RUnlock()
......@@ -625,7 +625,7 @@ func (db *Database) Commit(node common.Hash, report bool) error {
// db.lock.RUnlock()
// return err
//}
//if batch.ValueSize() > ethdb.IdealBatchSize {
//if batch.ValueLen() > ethdb.IdealBatchSize {
// if err := batch.Write(); err != nil {
// return err
// }
......@@ -693,7 +693,7 @@ func (db *Database) commit(hash common.Hash, batch dbm.Batch) error {
//println(hex.EncodeToString(hash[:]), len(node.rlp()))
batch.Set(hash[:], node.rlp())
// If we've reached an optimal batch size, commit and start over
if batch.ValueSize() >= IdealBatchSize {
if batch.ValueLen() >= IdealBatchSize {
if err := batch.Write(); err != nil {
return err
}
......
......@@ -10,6 +10,7 @@ datadir*
.idea
.vscode
cmd/chain33/chain33
cmd/tools/tools
build/cert.pem
build/key.pem
build/chain33*
......
......@@ -117,7 +117,7 @@ fmt: fmt_proto fmt_shell ## go fmt
.PHONY: fmt_proto fmt_shell
fmt_proto: ## go fmt protobuf file
#@find . -name '*.proto' -not -path "./vendor/*" | xargs clang-format -i
@find . -name '*.proto' -not -path "./vendor/*" | xargs clang-format -i
fmt_shell: ## check shell file
find . -name '*.sh' -not -path "./vendor/*" | xargs shfmt -w -s -i 4 -ci -bn
......
......@@ -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)
......
......@@ -47,6 +47,7 @@ func GetLocalDBKeyList() [][]byte {
return [][]byte{
blockLastHeight, bodyPerfix, LastSequence, headerPerfix, heightToHeaderPerfix,
hashPerfix, tdPerfix, heightToHashKeyPerfix, seqToHashKey, HashToSeqPerfix,
seqCBPrefix, seqCBLastNumPrefix,
}
}
......@@ -1057,3 +1058,49 @@ func (bs *BlockStore) SetUpgradeMeta(meta *types.UpgradeMeta) error {
storeLog.Info("SetUpgradeMeta", "meta", meta)
return bs.db.SetSync(version.LocalDBMeta, verByte)
}
//isRecordBlockSequence配置的合法性检测
func (bs *BlockStore) isRecordBlockSequenceValid() {
storeLog.Error("isRecordBlockSequenceValid")
lastHeight := bs.Height()
lastSequence, err := bs.LoadBlockLastSequence()
if err != nil {
if err != types.ErrHeightNotExist {
storeLog.Error("isRecordBlockSequenceValid", "LoadBlockLastSequence err", err)
panic(err)
}
}
//使能isRecordBlockSequence时的检测
if isRecordBlockSequence {
//中途开启isRecordBlockSequence报错
if lastSequence == -1 && lastHeight != -1 {
storeLog.Error("isRecordBlockSequenceValid", "lastHeight", lastHeight, "lastSequence", lastSequence)
panic("isRecordBlockSequence is true must Synchronizing data from zero block")
}
//lastSequence 必须大于等于lastheight
if lastHeight > lastSequence {
storeLog.Error("isRecordBlockSequenceValid", "lastHeight", lastHeight, "lastSequence", lastSequence)
panic("lastSequence must greater than or equal to lastHeight")
}
//通过lastSequence获取对应的blockhash != lastHeader.hash 报错
if lastSequence != -1 {
blockSequence, err := bs.GetBlockSequence(lastSequence)
if err != nil {
storeLog.Error("isRecordBlockSequenceValid", "lastSequence", lastSequence, "GetBlockSequence err", err)
panic(err)
}
lastHeader := bs.LastHeader()
if !bytes.Equal(lastHeader.Hash, blockSequence.Hash) {
storeLog.Error("isRecordBlockSequenceValid:", "lastHeight", lastHeight, "lastSequence", lastSequence, "lastHeader.Hash", common.ToHex(lastHeader.Hash), "blockSequence.Hash", common.ToHex(blockSequence.Hash))
panic("The hash values of lastSequence and lastHeight are different.")
}
}
return
}
//去使能isRecordBlockSequence时的检测
if lastSequence != -1 {
storeLog.Error("isRecordBlockSequenceValid", "lastSequence", lastSequence)
panic("can not disable isRecordBlockSequence")
}
}
......@@ -222,6 +222,10 @@ func (chain *BlockChain) GetOrphanPool() *OrphanPool {
//InitBlockChain 区块链初始化
func (chain *BlockChain) InitBlockChain() {
//isRecordBlockSequence配置的合法性检测
if !chain.cfg.IsParaChain {
chain.blockStore.isRecordBlockSequenceValid()
}
//先缓存最新的128个block信息到cache中
curheight := chain.GetBlockHeight()
if types.IsEnable("TxHeight") {
......@@ -402,9 +406,10 @@ func (chain *BlockChain) InitIndexAndBestView() {
height = 0
}
for ; height <= curheight; height++ {
header, _ := chain.blockStore.GetBlockHeaderByHeight(height)
header, err := chain.blockStore.GetBlockHeaderByHeight(height)
if header == nil {
return
chainlog.Error("InitIndexAndBestView GetBlockHeaderByHeight", "height", height, "err", err)
panic("InitIndexAndBestView fail!")
}
newNode := newBlockNodeByHeader(false, header, "self", -1)
......
......@@ -80,7 +80,7 @@ func (chain *BlockChain) reIndexOne(height int64) error {
panic(err)
}
if height%1000 == 0 {
chainlog.Info("reindex -> ", "height", height)
chainlog.Info("reindex -> ", "height", height, "lastheight", chain.GetBlockHeight())
}
//保存tx信息到db中(newbatch, blockdetail)
err = chain.blockStore.AddTxs(newbatch, blockdetail)
......
......@@ -93,6 +93,9 @@ func (chain *BlockChain) ProcAddBlockSeqCB(cb *types.BlockSeqCB) error {
return types.ErrInvalidParam
}
if !isRecordBlockSequence {
return types.ErrRecordBlockSequence
}
if chain.blockStore.seqCBNum() >= MaxSeqCB && !chain.blockStore.isSeqCBExist(cb.Name) {
return types.ErrTooManySeqCB
}
......
......@@ -2,9 +2,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Package commands 扫描chain33项目下plugin中所有的插件,根据扫描到的结果重新更新共识、执行器和数据操作的初始化文件 init.go
*/
package commands
import (
......@@ -19,7 +16,7 @@ import (
func UpdateInitCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "updateinit",
Short: "Update chain33 plugin consensus、dapp、store init.go file",
Short: "Update chain33 plugin consensus、dapp、store、mempool init.go file",
Run: updateInit,
}
cmd.Flags().StringP("path", "p", "plugin", "path of plugin")
......
# type字段仅支持 consensus dapp store
[blackwhite]
type = "dapp"
gitrepo = "gitlab.33.cn/sanghg/blackwhite"
# type字段仅支持 consensus dapp store mempool
[mempool-price]
type = "mempool"
gitrepo = "github.com/33cn/plugin/plugin/mempool/price"
version=""
syntax = "proto3";
package accounts;
message DemoAction {
oneof value {
DemoCreate create = 1;
DemoRun play = 2;
DemoClose show = 3;
}
int32 ty = 6;
}
message DemoCreate {
[ consensus - ticket ] string name = 1;
}
message DemoRun {
string name = 1;
}
message DemoClose {
string name = 1;
}
\ No newline at end of file
......@@ -51,5 +51,11 @@ Package main chain33开发者工具,主要提供以下功能:
-n --name 执行器的项目名和类名,必填参数
-p --propfile 导入执行器类型的proto3协议模板,如果不填默认为config/执行器名称.proto
-t --templatepath 生成执行器项目的模板文件,不填默认为config/template下的所有文件
更新初始化文件:
扫描指定path目录下所有的插件,根据扫描到的结果重新更新consensus、dapp和、store、mempool的初始化文件 init.go
使用方式:./tools updateinit -p $(YourPluginPath)
例子:./tools updateinit -p /GOPATH/src/github.com/33cn/chain33/cmd/tools/plugin
*/
package main
......@@ -11,7 +11,8 @@ import (
"github.com/33cn/chain33/cmd/tools/tasks"
"github.com/33cn/chain33/cmd/tools/types"
"github.com/33cn/chain33/util"
"github.com/33cn/chain33/cmd/tools/util"
sysutil "github.com/33cn/chain33/util"
"github.com/pkg/errors"
)
......@@ -56,7 +57,7 @@ func (ad *advanceCreateExecProjStrategy) initMember() {
ad.execName = v
}
if v, err := ad.getParam(types.KeyActionName); err == nil {
ad.actionName, _ = util.MakeStringToUpper(v, 0, 1)
ad.actionName, _ = sysutil.MakeStringToUpper(v, 0, 1)
}
if v, err := ad.getParam(types.KeyProtobufFile); err == nil {
ad.propFile = v
......
......@@ -295,6 +295,7 @@ clean:
@rm -rf plugin/dapp/init
@rm -rf plugin/crypto/init
@rm -rf plugin/store/init
@rm -rf plugin/mempool/init
`
// 生成 .travis.yml 文件模板
......@@ -308,7 +309,7 @@ go:
// 生成 plugin/plugin.toml的文件模板
CpftPluginToml = `
# type字段仅支持 consensus dapp store
# type字段仅支持 consensus dapp store mempool
[dapp-ticket]
gitrepo = "github.com/33cn/plugin/plugin/dapp/ticket"
......@@ -326,6 +327,12 @@ gitrepo = "github.com/33cn/plugin/plugin/dapp/token"
[dapp-trade]
gitrepo = "github.com/33cn/plugin/plugin/dapp/trade"
[mempool-price]
gitrepo = "github.com/33cn/plugin/plugin/mempool/price"
[mempool-score]
gitrepo = "github.com/33cn/plugin/plugin/mempool/score"
`
// 项目 cli/main.go 文件模板
CpftCliMain = `package main
......
......@@ -14,7 +14,7 @@ import (
"os/exec"
"strings"
"github.com/33cn/chain33/util"
"github.com/33cn/chain33/cmd/tools/util"
"github.com/BurntSushi/toml"
)
......@@ -23,6 +23,7 @@ const (
consensusFolderName = "consensus"
storeFolderName = "store"
cryptoFolderName = "crypto"
mempoolFolderName = "mempool"
)
type pluginConfigItem struct {
......@@ -91,6 +92,7 @@ func (im *importPackageStrategy) initData() error {
consensusItems := make([]*pluginItem, 0)
storeItems := make([]*pluginItem, 0)
cryptoItems := make([]*pluginItem, 0)
mempoolItems := make([]*pluginItem, 0)
//read current plugin dir
//(分成两级,并且去掉了 init 目录)
......@@ -141,6 +143,8 @@ func (im *importPackageStrategy) initData() error {
storeItems = append(storeItems, item)
case cryptoFolderName:
cryptoItems = append(cryptoItems, item)
case mempoolFolderName:
mempoolItems = append(mempoolItems, item)
default:
fmt.Printf("type %s is not supported.\n", cfgItem.Type)
return errors.New("config error")
......@@ -150,6 +154,7 @@ func (im *importPackageStrategy) initData() error {
im.items[consensusFolderName] = consensusItems
im.items[storeFolderName] = storeItems
im.items[cryptoFolderName] = cryptoItems
im.items[mempoolFolderName] = mempoolItems
im.projRootPath = ""
im.projPluginPath, _ = im.getParam("path")
return nil
......@@ -201,11 +206,11 @@ func (im *importPackageStrategy) generateImportFile() error {
importStrs := map[string]string{}
for name, plugins := range im.items {
for _, item := range plugins {
importStrs[name] += fmt.Sprintf("\r\n_ \"%s\" //auto gen", item.gitRepo)
importStrs[name] += fmt.Sprintf("\n_ \"%s\" //auto gen", item.gitRepo)
}
}
for key, value := range importStrs {
content := fmt.Sprintf("package init\r\n\r\nimport(%s\r\n)", value)
content := fmt.Sprintf("package init\n\nimport(%s\n)", value)
initFile := fmt.Sprintf("%s/%s/init/init.go", im.projPluginPath, key)
util.MakeDir(initFile)
......
......@@ -11,16 +11,18 @@ import (
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/33cn/chain33/cmd/tools/tasks"
)
type updateInitStrategy struct {
strategyBasic
consRootPath string
dappRootPath string
storeRootPath string
cryptoRootPath string
consRootPath string
dappRootPath string
storeRootPath string
cryptoRootPath string
mempoolRootPath string
}
func (up *updateInitStrategy) Run() error {
......@@ -35,12 +37,15 @@ func (up *updateInitStrategy) Run() error {
func (up *updateInitStrategy) initMember() error {
path, err := up.getParam("path")
packname, _ := up.getParam("packname")
gopath := os.Getenv("GOPATH")
if err != nil || path == "" {
gopath := os.Getenv("GOPATH")
if len(gopath) > 0 {
path = filepath.Join(gopath, "/src/github.com/33cn/chain33/plugin/")
}
}
if packname == "" {
packname = strings.Replace(path, gopath+"/src/", "", 1)
}
if len(path) == 0 {
return errors.New("Chain33 Plugin Not Existed")
}
......@@ -48,10 +53,12 @@ func (up *updateInitStrategy) initMember() error {
up.dappRootPath = fmt.Sprintf("%s/dapp/", path)
up.storeRootPath = fmt.Sprintf("%s/store/", path)
up.cryptoRootPath = fmt.Sprintf("%s/crypto/", path)
up.mempoolRootPath = fmt.Sprintf("%s/mempool/", path)
mkdir(up.consRootPath)
mkdir(up.dappRootPath)
mkdir(up.storeRootPath)
mkdir(up.cryptoRootPath)
mkdir(up.mempoolRootPath)
buildInit(path, packname)
return nil
}
......@@ -76,7 +83,9 @@ import (
_ "${packname}/crypto/init" //crypto init
_ "${packname}/dapp/init" //dapp init
_ "${packname}/store/init" //store init
)`)
_ "${packname}/mempool/init" //mempool init
)
`)
data = bytes.Replace(data, []byte("${packname}"), []byte(packname), -1)
ioutil.WriteFile(path, data, 0666)
}
......@@ -114,6 +123,9 @@ func (up *updateInitStrategy) buildTask() tasks.Task {
&tasks.UpdateInitFileTask{
Folder: up.cryptoRootPath,
},
&tasks.UpdateInitFileTask{
Folder: up.mempoolRootPath,
},
)
task := taskSlice[0]
......
......@@ -5,7 +5,7 @@
package tasks
import (
"github.com/33cn/chain33/util"
"github.com/33cn/chain33/cmd/tools/util"
)
// CheckFileExistedTask 检测文件是否存在
......
......@@ -11,7 +11,7 @@ import (
"strings"
"github.com/33cn/chain33/cmd/tools/types"
"github.com/33cn/chain33/util"
"github.com/33cn/chain33/cmd/tools/util"
)
//CopyTemplateToOutputTask ...
......
......@@ -10,7 +10,8 @@ import (
"strings"
"github.com/33cn/chain33/cmd/tools/types"
"github.com/33cn/chain33/util"
"github.com/33cn/chain33/cmd/tools/util"
sysutil "github.com/33cn/chain33/util"
)
type actionInfoItem struct {
......@@ -112,7 +113,7 @@ func (c *CreateDappSourceTask) readActionMemberNames() error {
memberType := strings.Replace(member[1], " ", "", -1)
memberName := strings.Replace(member[2], " ", "", -1)
// 根据proto生成pb.go的规则,成员变量首字母必须大写
memberName, _ = util.MakeStringToUpper(memberName, 0, 1)
memberName, _ = sysutil.MakeStringToUpper(memberName, 0, 1)
c.actionInfos = append(c.actionInfos, &actionInfoItem{
memberName: memberName,
memberType: memberType,
......@@ -183,11 +184,11 @@ func (c *CreateDappSourceTask) createExecDelLocalFile() error {
// 组成规则是 TyLog+ActionName + ActionMemberName
func (c *CreateDappSourceTask) buildActionLogTypeText() (text string, err error) {
items := fmt.Sprintf("TyLog%sUnknown = iota\r\n", c.ExecuteName)
items := fmt.Sprintf("TyLog%sUnknown = iota\n", c.ExecuteName)
for _, info := range c.actionInfos {
items += fmt.Sprintf("TyLog%s%s\r\n", c.ExecuteName, info.memberName)
items += fmt.Sprintf("TyLog%s%s\n", c.ExecuteName, info.memberName)
}
text = fmt.Sprintf("const (\r\n%s)\r\n", items)
text = fmt.Sprintf("const (\n%s)\n", items)
return
}
......@@ -195,9 +196,9 @@ func (c *CreateDappSourceTask) buildActionLogTypeText() (text string, err error)
func (c *CreateDappSourceTask) buildActionIDText() (text string, err error) {
var items string
for index, info := range c.actionInfos {
items += fmt.Sprintf("%sAction%s = %d\r\n", c.ExecuteName, info.memberName, index)
items += fmt.Sprintf("%sAction%s = %d\n", c.ExecuteName, info.memberName, index)
}
text = fmt.Sprintf("const (\r\n%s)\r\n", items)
text = fmt.Sprintf("const (\n%s)\n", items)
return
}
......@@ -205,15 +206,15 @@ func (c *CreateDappSourceTask) buildActionIDText() (text string, err error) {
func (c *CreateDappSourceTask) buildLogMapText() (text string, err error) {
var items string
for _, info := range c.actionInfos {
items += fmt.Sprintf("\"%s\": %sAction%s,\r\n", info.memberName, c.ExecuteName, info.memberName)
items += fmt.Sprintf("\"%s\": %sAction%s,\n", info.memberName, c.ExecuteName, info.memberName)
}
text = fmt.Sprintf("map[string]int32{\r\n%s}", items)
text = fmt.Sprintf("map[string]int32{\n%s}", items)
return
}
// 返回 map[string]*types.LogInfo
func (c *CreateDappSourceTask) buidTypeMapText() (text string, err error) {
text = fmt.Sprintf("map[int64]*types.LogInfo{\r\n}")
text = fmt.Sprintf("map[int64]*types.LogInfo{\n}")
return
}
......
......@@ -10,7 +10,7 @@ import (
"fmt"
"strings"
"github.com/33cn/chain33/util"
"github.com/33cn/chain33/cmd/tools/util"
)
// CreateFileFromStrTemplateTask 从指定的模板字符串中创建目标文件的任务
......
......@@ -10,7 +10,7 @@ import (
"strings"
"github.com/33cn/chain33/cmd/tools/types"
"github.com/33cn/chain33/util"
"github.com/33cn/chain33/cmd/tools/util"
)
// ReplaceTargetTask 替换指定目录下所有文件的标志性文字
......
......@@ -9,7 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/33cn/chain33/util"
"github.com/33cn/chain33/cmd/tools/util"
)
func TestReplaceTarget(t *testing.T) {
......
......@@ -7,9 +7,12 @@ package tasks
import (
"errors"
"fmt"
"io/ioutil"
"os"
"os/exec"
"strings"
"github.com/33cn/chain33/util"
"github.com/33cn/chain33/cmd/tools/util"
)
type itemData struct {
......@@ -54,6 +57,17 @@ func (up *UpdateInitFileTask) Execute() error {
func (up *UpdateInitFileTask) init() error {
up.initFile = fmt.Sprintf("%sinit/init.go", up.Folder)
up.itemDatas = make([]*itemData, 0)
gopath := os.Getenv("GOPATH")
if len(gopath) == 0 {
return errors.New("GOPATH Not Existed")
}
// 获取所有文件
files, _ := ioutil.ReadDir(up.Folder)
for _, file := range files {
if file.IsDir() && file.Name() != "init" {
up.itemDatas = append(up.itemDatas, &itemData{strings.Replace(up.Folder, gopath+"/src/", "", 1) + file.Name()})
}
}
return nil
}
......@@ -63,9 +77,9 @@ func (up *UpdateInitFileTask) genInitFile() error {
}
var importStr, content string
for _, item := range up.itemDatas {
importStr += fmt.Sprintf("_ \"%s\"\r\n", item.path)
importStr += fmt.Sprintf("_ \"%s\"\n", item.path)
}
content = fmt.Sprintf("package init \r\n\r\nimport (\r\n%s)", importStr)
content = fmt.Sprintf("package init \n\nimport (\n%s)\n", importStr)
util.DeleteFile(up.initFile)
_, err := util.WriteStringToFile(up.initFile, content)
......
......@@ -85,7 +85,8 @@ type Batch interface {
Set(key, value []byte)
Delete(key []byte)
Write() error
ValueSize() int // amount of data in the batch
ValueSize() int // size of data in the batch
ValueLen() int // amount of data in the batch
Reset() // Reset resets the batch for reuse
}
......
......@@ -279,24 +279,28 @@ type GoBadgerDBBatch struct {
batch *badger.Txn
//wop *opt.WriteOptions
size int
len int
}
//NewBatch new
func (db *GoBadgerDB) NewBatch(sync bool) Batch {
batch := db.db.NewTransaction(true)
return &GoBadgerDBBatch{db, batch, 0}
return &GoBadgerDBBatch{db, batch, 0, 0}
}
//Set set
func (mBatch *GoBadgerDBBatch) Set(key, value []byte) {
mBatch.batch.Set(key, value)
mBatch.size += len(value)
mBatch.size += len(key)
mBatch.len += len(value)
}
//Delete 设置
func (mBatch *GoBadgerDBBatch) Delete(key []byte) {
mBatch.batch.Delete(key)
mBatch.size++
mBatch.size += len(key)
mBatch.len++
}
//Write 写入
......@@ -315,10 +319,16 @@ func (mBatch *GoBadgerDBBatch) ValueSize() int {
return mBatch.size
}
//ValueLen batch数量
func (mBatch *GoBadgerDBBatch) ValueLen() int {
return mBatch.len
}
//Reset 重置
func (mBatch *GoBadgerDBBatch) Reset() {
if nil != mBatch.db && nil != mBatch.db.db {
mBatch.batch = mBatch.db.db.NewTransaction(true)
}
mBatch.size = 0
mBatch.len = 0
}
......@@ -234,23 +234,27 @@ type goLevelDBBatch struct {
batch *leveldb.Batch
wop *opt.WriteOptions
size int
len int
}
//NewBatch new
func (db *GoLevelDB) NewBatch(sync bool) Batch {
batch := new(leveldb.Batch)
wop := &opt.WriteOptions{Sync: sync}
return &goLevelDBBatch{db, batch, wop, 0}
return &goLevelDBBatch{db, batch, wop, 0, 0}
}
func (mBatch *goLevelDBBatch) Set(key, value []byte) {
mBatch.batch.Put(key, value)
mBatch.size += len(key)
mBatch.size += len(value)
mBatch.len += len(value)
}
func (mBatch *goLevelDBBatch) Delete(key []byte) {
mBatch.batch.Delete(key)
mBatch.size++
mBatch.size += len(key)
mBatch.len++
}
func (mBatch *goLevelDBBatch) Write() error {
......@@ -266,7 +270,13 @@ func (mBatch *goLevelDBBatch) ValueSize() int {
return mBatch.size
}
//ValueLen batch数量
func (mBatch *goLevelDBBatch) ValueLen() int {
return mBatch.len
}
func (mBatch *goLevelDBBatch) Reset() {
mBatch.batch.Reset()
mBatch.len = 0
mBatch.size = 0
}
......@@ -253,6 +253,7 @@ type memBatch struct {
db *GoMemDB
writes []kv
size int
len int
}
//NewBatch new
......@@ -264,11 +265,14 @@ func (b *memBatch) Set(key, value []byte) {
//println("-b-", string(key)[0:4], common.ToHex(key))
b.writes = append(b.writes, kv{CopyBytes(key), CopyBytes(value)})
b.size += len(value)
b.size += len(key)
b.len += len(value)
}
func (b *memBatch) Delete(key []byte) {
b.writes = append(b.writes, kv{CopyBytes(key), CopyBytes(nil)})
b.size++
b.size += len(key)
b.len++
}
func (b *memBatch) Write() error {
......@@ -291,7 +295,13 @@ func (b *memBatch) ValueSize() int {
return b.size
}
//ValueLen batch数量
func (b *memBatch) ValueLen() int {
return b.len
}
func (b *memBatch) Reset() {
b.writes = b.writes[:0]
b.size = 0
b.len = 0
}
......@@ -456,6 +456,7 @@ type PegasusBatch struct {
table pegasus.TableConnector
batchset map[string][]byte
batchdel map[string][]byte
size int
}
//NewBatch new
......@@ -467,6 +468,8 @@ func (db *PegasusDB) NewBatch(sync bool) Batch {
func (db *PegasusBatch) Set(key, value []byte) {
db.batchset[string(key)] = value
delete(db.batchdel, string(key))
db.size += len(value)
db.size += len(key)
}
//Delete 删除
......@@ -474,6 +477,7 @@ func (db *PegasusBatch) Delete(key []byte) {
db.batchset[string(key)] = []byte("")
delete(db.batchset, string(key))
db.batchdel[string(key)] = key
db.size += len(key)
}
// 注意本方法的实现逻辑,因为ssdb没有提供删除和更新同时进行的批量操作;
......@@ -556,6 +560,11 @@ func (db *PegasusBatch) Write() error {
//ValueSize value批长度
func (db *PegasusBatch) ValueSize() int {
return db.size
}
//ValueLen batch数量
func (db *PegasusBatch) ValueLen() int {
return len(db.batchset)
}
......@@ -563,6 +572,7 @@ func (db *PegasusBatch) ValueSize() int {
func (db *PegasusBatch) Reset() {
db.batchset = make(map[string][]byte)
db.batchdel = make(map[string][]byte)
db.size = 0
}
func getHashKey(key []byte) []byte {
......
......@@ -457,6 +457,7 @@ type ssDBBatch struct {
db *GoSSDB
batchset map[string][]byte
batchdel map[string]bool
size int
}
//NewBatch new
......@@ -467,12 +468,15 @@ func (db *GoSSDB) NewBatch(sync bool) Batch {
func (db *ssDBBatch) Set(key, value []byte) {
db.batchset[string(key)] = value
delete(db.batchdel, string(key))
db.size += len(value)
db.size += len(key)
}
func (db *ssDBBatch) Delete(key []byte) {
db.batchset[string(key)] = []byte{}
delete(db.batchset, string(key))
db.batchdel[string(key)] = true
db.size += len(key)
}
// 注意本方法的实现逻辑,因为ssdb没有提供删除和更新同时进行的批量操作;
......@@ -507,10 +511,16 @@ func (db *ssDBBatch) Write() error {
}
func (db *ssDBBatch) ValueSize() int {
return db.size
}
//ValueLen batch数量
func (db *ssDBBatch) ValueLen() int {
return len(db.batchset)
}
func (db *ssDBBatch) Reset() {
db.batchset = make(map[string][]byte)
db.batchdel = make(map[string]bool)
db.size = 0
}
......@@ -22,7 +22,7 @@ type Count struct {
//NewCount 创建一个计数器
func NewCount(prefix string, name string, kvdb db.KV) *Count {
keydata := []byte(prefix + "#" + name)
keydata := []byte(prefix + sep + name)
return &Count{
prefix: prefix,
name: name,
......
......@@ -7,12 +7,13 @@ package table
import (
"testing"
"github.com/33cn/chain33/util"
"github.com/stretchr/testify/assert"
)
func TestCount(t *testing.T) {
dir, leveldb, kvdb := getdb()
defer dbclose(dir, leveldb)
dir, ldb, kvdb := util.CreateTestDB()
defer util.CloseTestDB(dir, ldb)
count := NewCount("prefix", "name#hello", kvdb)
count.Inc()
count.Dec()
......@@ -22,7 +23,7 @@ func TestCount(t *testing.T) {
assert.Equal(t, i, int64(1))
kvs, err := count.Save()
assert.Nil(t, err)
setKV(leveldb, kvs)
util.SaveKVList(ldb, kvs)
count = NewCount("prefix", "name#hello", kvdb)
i, err = count.Get()
......
......@@ -14,4 +14,5 @@ var (
ErrTooManyIndex = errors.New("ErrTooManyIndex")
ErrTablePrefixOrTableName = errors.New("ErrTablePrefixOrTableName")
ErrDupPrimaryKey = errors.New("ErrDupPrimaryKey")
ErrNilValue = errors.New("ErrNilValue")
)
This diff is collapsed.
package table
import (
"fmt"
"testing"
protodata "github.com/33cn/chain33/common/db/table/proto"
"github.com/33cn/chain33/util"
"github.com/golang/protobuf/proto"
"github.com/stretchr/testify/assert"
"github.com/33cn/chain33/types"
)
func TestJoin(t *testing.T) {
dir, ldb, kvdb := util.CreateTestDB()
defer util.CloseTestDB(dir, ldb)
table1, err := NewTable(NewGameRow(), kvdb, optgame)
assert.Nil(t, err)
table2, err := NewTable(NewGameAddrRow(), kvdb, optgameaddr)
assert.Nil(t, err)
tablejoin, err := NewJoinTable(table2, table1, []string{"addr#status", "#status"})
assert.Nil(t, err)
assert.Equal(t, tablejoin.GetLeft(), table2) //table2
assert.Equal(t, tablejoin.GetRight(), table1) //table1
assert.Equal(t, tablejoin.MustGetTable("gameaddr"), table2) //table2
assert.Equal(t, tablejoin.MustGetTable("game"), table1) //table1
rightdata := &protodata.Game{GameID: "gameid1", Status: 1}
tablejoin.MustGetTable("game").Replace(rightdata)
leftdata := &protodata.GameAddr{GameID: "gameid1", Addr: "addr1", Txhash: "hash1"}
tablejoin.MustGetTable("gameaddr").Replace(leftdata)
kvs, err := tablejoin.Save()
assert.Nil(t, err)
assert.Equal(t, 7, len(kvs))
util.SaveKVList(ldb, kvs)
//query table
//每个表的查询,用 tablejoin.MustGetTable("gameaddr")
//join query 用 tablejoin.Query
rows, err := tablejoin.ListIndex("addr#status", JoinKey([]byte("addr1"), []byte("1")), nil, 0, 0)
assert.Nil(t, err)
assert.Equal(t, 1, len(rows))
assert.Equal(t, true, proto.Equal(rows[0].Data.(*JoinData).Left, leftdata))
assert.Equal(t, true, proto.Equal(rows[0].Data.(*JoinData).Right, rightdata))
rows, err = tablejoin.ListIndex("#status", JoinKey(nil, []byte("1")), nil, 0, 0)
assert.Nil(t, err)
assert.Equal(t, 1, len(rows))
assert.Equal(t, true, proto.Equal(rows[0].Data.(*JoinData).Left, leftdata))
assert.Equal(t, true, proto.Equal(rows[0].Data.(*JoinData).Right, rightdata))
rightdata = &protodata.Game{GameID: "gameid1", Status: 2}
tablejoin.MustGetTable("game").Replace(rightdata)
kvs, err = tablejoin.Save()
assert.Nil(t, err)
assert.Equal(t, 7, len(kvs))
util.SaveKVList(ldb, kvs)
rows, err = tablejoin.ListIndex("addr#status", JoinKey([]byte("addr1"), []byte("2")), nil, 0, 0)
assert.Nil(t, err)
assert.Equal(t, 1, len(rows))
assert.Equal(t, true, proto.Equal(rows[0].Data.(*JoinData).Left, leftdata))
assert.Equal(t, true, proto.Equal(rows[0].Data.(*JoinData).Right, rightdata))
rows, err = tablejoin.ListIndex("#status", JoinKey(nil, []byte("2")), nil, 0, 0)
assert.Nil(t, err)
assert.Equal(t, 1, len(rows))
assert.Equal(t, true, proto.Equal(rows[0].Data.(*JoinData).Left, leftdata))
assert.Equal(t, true, proto.Equal(rows[0].Data.(*JoinData).Right, rightdata))
rightdata = &protodata.Game{GameID: "gameid1", Status: 2}
tablejoin.MustGetTable("game").Replace(rightdata)
kvs, err = tablejoin.Save()
assert.Nil(t, err)
assert.Equal(t, 0, len(kvs))
leftdata = &protodata.GameAddr{GameID: "gameid1", Addr: "addr2", Txhash: "hash1"}
tablejoin.MustGetTable("gameaddr").Replace(leftdata)
kvs, err = tablejoin.Save()
assert.Nil(t, err)
assert.Equal(t, 5, len(kvs))
util.SaveKVList(ldb, kvs)
//改回到全部是1的情况
rightdata = &protodata.Game{GameID: "gameid1", Status: 1}
tablejoin.MustGetTable("game").Replace(rightdata)
leftdata = &protodata.GameAddr{GameID: "gameid1", Addr: "addr1", Txhash: "hash1"}
tablejoin.MustGetTable("gameaddr").Replace(leftdata)
kvs, err = tablejoin.Save()
assert.Nil(t, err)
assert.Equal(t, 10, len(kvs))
util.SaveKVList(ldb, kvs)
rows, err = tablejoin.ListIndex("addr#status", JoinKey([]byte("addr1"), []byte("1")), nil, 0, 0)
assert.Nil(t, err)
assert.Equal(t, 1, len(rows))
assert.Equal(t, true, proto.Equal(rows[0].Data.(*JoinData).Left, leftdata))
assert.Equal(t, true, proto.Equal(rows[0].Data.(*JoinData).Right, rightdata))
rows, err = tablejoin.ListIndex("#status", JoinKey(nil, []byte("1")), nil, 0, 0)
assert.Nil(t, err)
assert.Equal(t, 1, len(rows))
assert.Equal(t, true, proto.Equal(rows[0].Data.(*JoinData).Left, leftdata))
assert.Equal(t, true, proto.Equal(rows[0].Data.(*JoinData).Right, rightdata))
}
/*
table game
data: Game
index: addr,status,index,type
*/
var optgame = &Option{
Prefix: "LODB",
Name: "game",
Primary: "gameID",
Index: []string{"status"},
}
//GameRow table meta 结构
type GameRow struct {
*protodata.Game
}
//NewGameRow 新建一个meta 结构
func NewGameRow() *GameRow {
return &GameRow{Game: &protodata.Game{}}
}
//CreateRow 新建数据行(注意index 数据一定也要保存到数据中,不能就保存eventid)
func (tx *GameRow) CreateRow() *Row {
return &Row{Data: &protodata.Game{}}
}
//SetPayload 设置数据
func (tx *GameRow) SetPayload(data types.Message) error {
if txdata, ok := data.(*protodata.Game); ok {
tx.Game = txdata
return nil
}
return types.ErrTypeAsset
}
//Get 按照indexName 查询 indexValue
func (tx *GameRow) Get(key string) ([]byte, error) {
if key == "gameID" {
return []byte(tx.GameID), nil
} else if key == "status" {
return []byte(fmt.Sprint(tx.Status)), nil
}
return nil, types.ErrNotFound
}
/*
table struct
data: GameAddr
index: addr,status,index,type
*/
var optgameaddr = &Option{
Prefix: "LODB",
Name: "gameaddr",
Primary: "txhash",
Index: []string{"gameID", "addr"},
}
//GameAddrRow table meta 结构
type GameAddrRow struct {
*protodata.GameAddr
}
//NewGameAddrRow 新建一个meta 结构
func NewGameAddrRow() *GameAddrRow {
return &GameAddrRow{GameAddr: &protodata.GameAddr{}}
}
//CreateRow 新建数据行(注意index 数据一定也要保存到数据中,不能就保存eventid)
func (tx *GameAddrRow) CreateRow() *Row {
return &Row{Data: &protodata.GameAddr{}}
}
//SetPayload 设置数据
func (tx *GameAddrRow) SetPayload(data types.Message) error {
if txdata, ok := data.(*protodata.GameAddr); ok {
tx.GameAddr = txdata
return nil
}
return types.ErrTypeAsset
}
//Get 按照indexName 查询 indexValue
func (tx *GameAddrRow) Get(key string) ([]byte, error) {
if key == "gameID" {
return []byte(tx.GameID), nil
} else if key == "addr" {
return []byte(tx.Addr), nil
} else if key == "txhash" {
return []byte(tx.Txhash), nil
}
return nil, types.ErrNotFound
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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