Commit 112b9f75 authored by lilinleeli1234's avatar lilinleeli1234

Merge branch 'master' of https://github.com/lynAzrael/plugin

parents 5bace73a b7699804
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}"
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cmd
package command
import (
"fmt"
......
......@@ -2,12 +2,12 @@ package js
import (
"github.com/33cn/chain33/pluginmgr"
"github.com/33cn/plugin/plugin/dapp/js/cmd"
"github.com/33cn/plugin/plugin/dapp/js/executor"
ptypes "github.com/33cn/plugin/plugin/dapp/js/types"
// init auto test
_ "github.com/33cn/plugin/plugin/dapp/js/autotest"
"github.com/33cn/plugin/plugin/dapp/js/command"
)
func init() {
......@@ -15,7 +15,7 @@ func init() {
Name: ptypes.JsX,
ExecName: executor.GetName(),
Exec: executor.Init,
Cmd: cmd.JavaScriptCmd,
Cmd: command.JavaScriptCmd,
RPC: nil,
})
}
......@@ -5,6 +5,7 @@
package executor
import (
"fmt"
"sort"
log "github.com/33cn/chain33/common/log/log15"
......@@ -61,25 +62,31 @@ func (lott *Lottery) GetDriverName() string {
return pty.LotteryX
}
func (lott *Lottery) findLotteryBuyRecords(key []byte) (*pty.LotteryBuyRecords, error) {
count := lott.GetLocalDB().PrefixCount(key)
llog.Error("findLotteryBuyRecords", "count", count)
values, err := lott.GetLocalDB().List(key, nil, int32(count), 0)
if err != nil {
return nil, err
}
func (lott *Lottery) findLotteryBuyRecords(prefix []byte) (*pty.LotteryBuyRecords, error) {
count := 0
var key []byte
var records pty.LotteryBuyRecords
for _, value := range values {
var record pty.LotteryBuyRecord
err := types.Decode(value, &record)
for {
values, err := lott.GetLocalDB().List(prefix, key, DefultCount, 0)
if err != nil {
continue
return nil, err
}
for _, value := range values {
var record pty.LotteryBuyRecord
err := types.Decode(value, &record)
if err != nil {
continue
}
records.Records = append(records.Records, &record)
}
count += len(values)
if len(values) < int(DefultCount) {
break
}
records.Records = append(records.Records, &record)
key = []byte(fmt.Sprintf("%s:%18d", prefix, records.Records[count-1].Index))
}
llog.Info("findLotteryBuyRecords", "count", count)
return &records, nil
}
......
......@@ -94,7 +94,7 @@ func (a *action) MultiSigAccCreate(accountCreate *mty.MultiSigAccCreate) (*types
//通过创建交易的txhash生成一个唯一的多重签名合约 NewAddrFromString
addr := address.MultiSignAddress(a.txhash)
//账户去重校验
multiSig, err := getMultiSigAccount(a.localdb, addr)
multiSig, err := getMultiSigAccFromDb(a.db, addr)
if err == nil && multiSig != nil {
return nil, mty.ErrAccountHasExist
}
......
......@@ -36,13 +36,11 @@ asset-transfer 分两种, 主链转出, 主链转入
1. 用户主链paracross合约帐号, balance -
1. 某平行链paracross合约帐号, balance +
* 平行链(如果上面步骤失败,, 平行链会过滤掉这个交易)
1. 平行链中paracross合约帐号 balance +
1. 平行链中 用户paracross合约帐号 balance +
主链转入 withdraw
* 平行链
1. 平行链中 用户paracross合约帐号 balance -
1. 平行链中paracross合约帐号 balance -
* 主链(上面步骤失败, 不进行操作)
1. commit 交易共识时执行
1. 某平行链paracross合约帐号, balance -
......
......@@ -51,11 +51,17 @@ func (*signatureMock) Equals(crypto.Signature) bool {
return true
}
func formatByte32(b []byte) []byte {
var b32 [32]byte
copy(b32[:], b)
return b32[:]
}
type privKeyMock struct {
}
func (mock *privKeyMock) Bytes() []byte {
return []byte("1234")
return formatByte32([]byte("1234"))
}
func (mock *privKeyMock) Sign(msg []byte) crypto.Signature {
......@@ -82,7 +88,7 @@ func TestNewPrivacy(t *testing.T) {
}
func test_RecoverOnetimePriKey(t *testing.T) {
R := []byte("1234")
R := formatByte32([]byte("1234"))
pkm := privKeyMock{}
privKey, err := RecoverOnetimePriKey(R, &pkm, &pkm, 0)
assert.Nil(t, err)
......
#!/usr/bin/env bash
strpwd=$(pwd)
strcmd=${strpwd##*dapp/}
strapp=${strcmd%/cmd*}
#strpwd=$(pwd)
#strcmd=${strpwd##*dapp/}
#strapp=${strcmd%/cmd*}
#
#OUT_DIR="${1}/$strapp"
#SRC_RELAYD=github.com/33cn/plugin/plugin/dapp/relay/cmd/relayd
#FLAG=$2
OUT_DIR="${1}/$strapp"
SRC_RELAYD=github.com/33cn/plugin/plugin/dapp/relay/cmd/relayd
FLAG=$2
# shellcheck disable=SC2086
go build -i ${FLAG} -v -o "${OUT_DIR}/relayd" "${SRC_RELAYD}"
cp ./relayd/relayd.toml "${OUT_DIR}/relayd.toml"
cp ./build/* "${OUT_DIR}"
# shellcheck disable=SC2086,1072
#go build -i ${FLAG} -v -o "${OUT_DIR}/relayd" "${SRC_RELAYD}"
#cp ./relayd/relayd.toml "${OUT_DIR}/relayd.toml"
#cp ./build/* "${OUT_DIR}"
echo "ignore"
......@@ -170,6 +170,7 @@ function relay_test() {
coinaddr=$(${1} tx query -s "${buy_hash}" | jq -r ".receipt.logs[2].log.coinAddr")
if [ "${coinaddr}" != "1Am9UTGfdnxabvcywYG2hvzr6qK8T3oUZT" ]; then
${1} tx query -s "${buy_hash}"
echo "wrong create order to coinaddr"
exit 1
fi
......
......@@ -18,6 +18,8 @@ type tokenAutoTest struct {
TransferCaseArr []autotest.TransferCase `toml:"TransferCase,omitempty"`
WithdrawCaseArr []autotest.WithdrawCase `toml:"WithdrawCase,omitempty"`
TokenRevokeCaseArr []TokenRevokeCase `toml:"TokenRevokeCase,omitempty"`
TokenMintCaseArr []TokenMintCase `toml:"TokenMintCase,omitempty"`
TokenBurnCaseArr []TokenBurnCase `toml:"TokenBurnCase,omitempty"`
}
func init() {
......
......@@ -7,7 +7,7 @@ command = "account import_key -k 0xc21d38be90493512a5c2417d565269a8b23ce8152010e
[[TokenPreCreateCase]]
id = "tokenPre"
command = "send token precreate -f 0.01 -i testToken -n testToken -s TC -a 12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv -t 100000 -p 1 -k 12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv"
command = "send token precreate -c 1 -f 0.01 -i testToken -n testToken -s TC -a 12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv -t 100000 -p 1 -k 12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv"
dep = ["transForPrecreate", "import1"]
[[TokenPreCreateCase]]
......@@ -21,6 +21,19 @@ id = "tokenFinish"
command = "send token finish -a 12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv -f 0.01 -s TC -k 0xc34b5d9d44ac7b754806f761d3d4d2c4fe5214f6b074c19f069c4f5c2a29c8cc"
dep = ["tokenPre"]
[[TokenMintCase]]
id = "tokenMint"
command = "send token mint -a 100 -s TC -k 12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv"
dep = ["tokenFinish"]
amount = "100"
checkItem = ["balance"]
[[TokenBurnCase]]
id = "tokenBurn"
command = "send token burn -a 50 -s TC -k 12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv"
dep = ["tokenMint"]
amount = "50"
checkItem = ["balance"]
#send to token for precreate
[[TransferCase]]
......
......@@ -6,6 +6,8 @@ package autotest
import (
"github.com/33cn/chain33/cmd/autotest/types"
"strconv"
)
// TokenPreCreateCase token precreatecase command
......@@ -59,3 +61,109 @@ func (testCase *TokenFinishCreateCase) SendCommand(packID string) (types.PackFun
return types.DefaultSend(testCase, &TokenFinishCreatePack{}, packID)
}
// TokenMintCase token mint case
type TokenMintCase struct {
types.BaseCase
Amount string `toml:"amount"`
}
// TokenMintPack token mint pack command
type TokenMintPack struct {
types.BaseCasePack
}
// SendCommand send command function of tokenfinishcreatecase
func (testCase *TokenMintCase) SendCommand(packID string) (types.PackFunc, error) {
return types.DefaultSend(testCase, &TokenMintPack{}, packID)
}
// GetCheckHandlerMap get check handle for map
func (pack *TokenMintPack) GetCheckHandlerMap() interface{} {
funcMap := make(types.CheckHandlerMapDiscard, 2)
funcMap["balance"] = pack.checkBalance
return funcMap
}
func (pack *TokenMintPack) checkBalance(txInfo map[string]interface{}) bool {
logArr := txInfo["receipt"].(map[string]interface{})["logs"].([]interface{})
logTokenBurn := logArr[1].(map[string]interface{})["log"].(map[string]interface{})
logAccBurn := logArr[2].(map[string]interface{})["log"].(map[string]interface{})
interCase := pack.TCase.(*TokenMintCase)
amount1, _ := strconv.ParseInt(interCase.Amount, 10, 64)
amount := amount1 * 1e8
pack.FLog.Info("MintDetails", "TestID", pack.PackID,
"TokenPrev", logTokenBurn["prev"].(map[string]interface{})["total"].(string),
"TokenCurr", logTokenBurn["current"].(map[string]interface{})["total"].(string),
"AccPrev", logAccBurn["prev"].(map[string]interface{})["balance"].(string),
"AccCurr", logAccBurn["current"].(map[string]interface{})["balance"].(string),
"amount", amount1)
totalCurrent := parseInt64(logTokenBurn["current"].(map[string]interface{})["total"])
totalPrev := parseInt64(logTokenBurn["prev"].(map[string]interface{})["total"])
accCurrent := parseInt64(logAccBurn["current"].(map[string]interface{})["balance"])
accPrev := parseInt64(logAccBurn["prev"].(map[string]interface{})["balance"])
return totalCurrent-amount == totalPrev && accCurrent-amount == accPrev
}
// TokenBurnCase token mint case
type TokenBurnCase struct {
types.BaseCase
Amount string `toml:"amount"`
}
// TokenBurnPack token mint pack command
type TokenBurnPack struct {
types.BaseCasePack
}
// SendCommand send command function
func (testCase *TokenBurnCase) SendCommand(packID string) (types.PackFunc, error) {
return types.DefaultSend(testCase, &TokenBurnPack{}, packID)
}
// GetCheckHandlerMap get check handle for map
func (pack *TokenBurnPack) GetCheckHandlerMap() interface{} {
funcMap := make(types.CheckHandlerMapDiscard, 2)
funcMap["balance"] = pack.checkBalance
return funcMap
}
func (pack *TokenBurnPack) checkBalance(txInfo map[string]interface{}) bool {
logArr := txInfo["receipt"].(map[string]interface{})["logs"].([]interface{})
logTokenBurn := logArr[1].(map[string]interface{})["log"].(map[string]interface{})
logAccBurn := logArr[2].(map[string]interface{})["log"].(map[string]interface{})
interCase := pack.TCase.(*TokenBurnCase)
amount1, _ := strconv.ParseInt(interCase.Amount, 10, 64)
amount := amount1 * 1e8
pack.FLog.Info("BurnDetails", "TestID", pack.PackID,
"TokenPrev", logTokenBurn["prev"].(map[string]interface{})["total"].(string),
"TokenCurr", logTokenBurn["current"].(map[string]interface{})["total"].(string),
"AccPrev", logAccBurn["prev"].(map[string]interface{})["balance"].(string),
"AccCurr", logAccBurn["current"].(map[string]interface{})["balance"].(string),
"amount", amount1)
totalCurrent := parseInt64(logTokenBurn["current"].(map[string]interface{})["total"])
totalPrev := parseInt64(logTokenBurn["prev"].(map[string]interface{})["total"])
accCurrent := parseInt64(logAccBurn["current"].(map[string]interface{})["balance"])
accPrev := parseInt64(logAccBurn["prev"].(map[string]interface{})["balance"])
return totalCurrent+amount == totalPrev && accCurrent+amount == accPrev
}
func parseInt64(s interface{}) int64 {
i, _ := strconv.ParseInt(s.(string), 10, 64)
return i
}
......@@ -42,6 +42,9 @@ func TokenCmd() *cobra.Command {
CreateRawTokenFinishTxCmd(),
CreateRawTokenRevokeTxCmd(),
CreateTokenTransferExecCmd(),
CreateRawTokenMintTxCmd(),
CreateRawTokenBurnTxCmd(),
GetTokenLogsCmd(),
)
return cmd
......@@ -465,3 +468,118 @@ func tokenRevoke(cmd *cobra.Command, args []string) {
ctx := jsonclient.NewRPCCtx(rpcLaddr, "token.CreateRawTokenRevokeTx", params, nil)
ctx.RunWithoutMarshal()
}
// CreateRawTokenMintTxCmd create raw token mintage transaction
func CreateRawTokenMintTxCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "mint",
Short: "Create a mint token transaction",
Run: tokenMint,
}
addTokenMintFlags(cmd)
return cmd
}
func addTokenMintFlags(cmd *cobra.Command) {
cmd.Flags().StringP("symbol", "s", "", "token symbol")
cmd.MarkFlagRequired("symbol")
cmd.Flags().Float64P("amount", "a", 0, "amount of mintage")
cmd.MarkFlagRequired("amount")
cmd.Flags().Float64P("fee", "f", 0, "token transaction fee")
}
func tokenMint(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
symbol, _ := cmd.Flags().GetString("symbol")
amount, _ := cmd.Flags().GetFloat64("amount")
params := &tokenty.TokenMint{
Symbol: symbol,
Amount: int64((amount+0.000001)*1e4) * 1e4,
}
ctx := jsonclient.NewRPCCtx(rpcLaddr, "token.CreateRawTokenMintTx", params, nil)
ctx.RunWithoutMarshal()
}
// CreateRawTokenBurnTxCmd create raw token burn transaction
func CreateRawTokenBurnTxCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "burn",
Short: "Create a burn token transaction",
Run: tokenBurn,
}
addTokenBurnFlags(cmd)
return cmd
}
func addTokenBurnFlags(cmd *cobra.Command) {
cmd.Flags().StringP("symbol", "s", "", "token symbol")
cmd.MarkFlagRequired("symbol")
cmd.Flags().Float64P("amount", "a", 0, "amount of burn")
cmd.MarkFlagRequired("amount")
cmd.Flags().Float64P("fee", "f", 0, "token transaction fee")
}
func tokenBurn(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
symbol, _ := cmd.Flags().GetString("symbol")
amount, _ := cmd.Flags().GetFloat64("amount")
params := &tokenty.TokenBurn{
Symbol: symbol,
Amount: int64((amount+0.000001)*1e4) * 1e4,
}
ctx := jsonclient.NewRPCCtx(rpcLaddr, "token.CreateRawTokenBurnTx", params, nil)
ctx.RunWithoutMarshal()
}
// GetTokenLogsCmd get logs of token
func GetTokenLogsCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "get_token_logs",
Short: "Get logs of token",
Run: getTokenLogs,
}
getTokenLogsFlags(cmd)
return cmd
}
func getTokenLogs(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
paraName, _ := cmd.Flags().GetString("paraName")
symbol, _ := cmd.Flags().GetString("symbol")
var params rpctypes.Query4Jrpc
params.Execer = getRealExecName(paraName, "token")
params.FuncName = "GetTokenHistory"
params.Payload = types.MustPBToJSON(&types.ReqString{Data: symbol})
rpc, err := jsonclient.NewJSONClient(rpcLaddr)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
var res tokenty.ReplyTokenLogs
err = rpc.Call("Chain33.Query", params, &res)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
data, err := json.MarshalIndent(res, "", " ")
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
fmt.Println(string(data))
}
func getTokenLogsFlags(cmd *cobra.Command) {
cmd.Flags().StringP("symbol", "s", "", "token symbol")
cmd.MarkFlagRequired("symbol")
}
......@@ -69,3 +69,13 @@ func (t *token) Exec_TransferToExec(payload *types.AssetsTransferToExec, tx *typ
}
return t.ExecTransWithdraw(db, tx, &tokenAction, index)
}
func (t *token) Exec_TokenMint(payload *tokenty.TokenMint, tx *types.Transaction, index int) (*types.Receipt, error) {
action := newTokenAction(t, "", tx)
return action.mint(payload)
}
func (t *token) Exec_TokenBurn(payload *tokenty.TokenBurn, tx *types.Transaction, index int) (*types.Receipt, error) {
action := newTokenAction(t, "", tx)
return action.burn(payload)
}
......@@ -5,6 +5,7 @@
package executor
import (
"github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
tokenty "github.com/33cn/plugin/plugin/dapp/token/types"
)
......@@ -107,6 +108,19 @@ func (t *token) ExecDelLocal_TokenFinishCreate(payload *tokenty.TokenFinishCreat
var set []*types.KeyValue
set = append(set, &types.KeyValue{Key: prepareKey, Value: types.Encode(localToken)})
set = append(set, &types.KeyValue{Key: key, Value: nil})
table := NewLogsTable(t.GetLocalDB())
txIndex := dapp.HeightIndexStr(t.GetHeight(), int64(index))
err = table.Del([]byte(txIndex))
if err != nil {
return nil, err
}
kv, err := table.Save()
if err != nil {
return nil, err
}
set = append(set, kv...)
return &types.LocalDBSet{KV: set}, nil
}
......@@ -123,3 +137,53 @@ func (t *token) ExecDelLocal_TokenRevokeCreate(payload *tokenty.TokenRevokeCreat
set = append(set, &types.KeyValue{Key: prepareKey, Value: types.Encode(localToken)})
return &types.LocalDBSet{KV: set}, nil
}
func (t *token) ExecDelLocal_TokenMint(payload *tokenty.TokenMint, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
localToken, err := loadLocalToken(payload.Symbol, tx.From(), tokenty.TokenStatusCreated, t.GetLocalDB())
if err != nil {
return nil, err
}
localToken = resetMint(localToken, t.GetHeight(), t.GetBlockTime(), payload.Amount)
key := calcTokenStatusKeyLocal(payload.Symbol, tx.From(), tokenty.TokenStatusCreated)
var set []*types.KeyValue
set = append(set, &types.KeyValue{Key: key, Value: types.Encode(localToken)})
table := NewLogsTable(t.GetLocalDB())
txIndex := dapp.HeightIndexStr(t.GetHeight(), int64(index))
err = table.Del([]byte(txIndex))
if err != nil {
return nil, err
}
kv, err := table.Save()
if err != nil {
return nil, err
}
set = append(set, kv...)
return &types.LocalDBSet{KV: set}, nil
}
func (t *token) ExecDelLocal_TokenBurn(payload *tokenty.TokenBurn, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
localToken, err := loadLocalToken(payload.Symbol, tx.From(), tokenty.TokenStatusCreated, t.GetLocalDB())
if err != nil {
return nil, err
}
localToken = resetBurn(localToken, t.GetHeight(), t.GetBlockTime(), payload.Amount)
key := calcTokenStatusKeyLocal(payload.Symbol, tx.From(), tokenty.TokenStatusCreated)
var set []*types.KeyValue
set = append(set, &types.KeyValue{Key: key, Value: types.Encode(localToken)})
table := NewLogsTable(t.GetLocalDB())
txIndex := dapp.HeightIndexStr(t.GetHeight(), int64(index))
err = table.Del([]byte(txIndex))
if err != nil {
return nil, err
}
kv, err := table.Save()
if err != nil {
return nil, err
}
set = append(set, kv...)
return &types.LocalDBSet{KV: set}, nil
}
......@@ -5,7 +5,10 @@
package executor
import (
"encoding/hex"
"github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
tokenty "github.com/33cn/plugin/plugin/dapp/token/types"
)
......@@ -107,6 +110,19 @@ func (t *token) ExecLocal_TokenFinishCreate(payload *tokenty.TokenFinishCreate,
set = append(set, &types.KeyValue{Key: key, Value: types.Encode(localToken)})
kv := AddTokenToAssets(payload.Owner, t.GetLocalDB(), payload.Symbol)
set = append(set, kv...)
table := NewLogsTable(t.GetLocalDB())
txIndex := dapp.HeightIndexStr(t.GetHeight(), int64(index))
err = table.Add(&tokenty.LocalLogs{Symbol: payload.Symbol, TxIndex: txIndex, ActionType: tokenty.TokenActionFinishCreate, TxHash: hex.EncodeToString(tx.Hash())})
if err != nil {
return nil, err
}
kv, err = table.Save()
if err != nil {
return nil, err
}
set = append(set, kv...)
return &types.LocalDBSet{KV: set}, nil
}
......@@ -182,6 +198,16 @@ func setRevoked(t *tokenty.LocalToken, height, time int64) *tokenty.LocalToken {
return t
}
func setMint(t *tokenty.LocalToken, height, time, amount int64) *tokenty.LocalToken {
t.Total = t.Total + amount
return t
}
func setBurn(t *tokenty.LocalToken, height, time, amount int64) *tokenty.LocalToken {
t.Total = t.Total - amount
return t
}
func resetCreated(t *tokenty.LocalToken) *tokenty.LocalToken {
t.CreatedTime = 0
t.CreatedHeight = 0
......@@ -195,3 +221,63 @@ func resetRevoked(t *tokenty.LocalToken) *tokenty.LocalToken {
t.Status = tokenty.TokenStatusPreCreated
return t
}
func resetMint(t *tokenty.LocalToken, height, time, amount int64) *tokenty.LocalToken {
t.Total = t.Total - amount
return t
}
func resetBurn(t *tokenty.LocalToken, height, time, amount int64) *tokenty.LocalToken {
t.Total = t.Total + amount
return t
}
func (t *token) ExecLocal_TokenMint(payload *tokenty.TokenMint, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
localToken, err := loadLocalToken(payload.Symbol, tx.From(), tokenty.TokenStatusCreated, t.GetLocalDB())
if err != nil {
return nil, err
}
localToken = setMint(localToken, t.GetHeight(), t.GetBlockTime(), payload.Amount)
var set []*types.KeyValue
key := calcTokenStatusKeyLocal(payload.Symbol, tx.From(), tokenty.TokenStatusCreated)
set = append(set, &types.KeyValue{Key: key, Value: types.Encode(localToken)})
table := NewLogsTable(t.GetLocalDB())
txIndex := dapp.HeightIndexStr(t.GetHeight(), int64(index))
err = table.Add(&tokenty.LocalLogs{Symbol: payload.Symbol, TxIndex: txIndex, ActionType: tokenty.TokenActionMint, TxHash: "0x" + hex.EncodeToString(tx.Hash())})
if err != nil {
return nil, err
}
kv, err := table.Save()
if err != nil {
return nil, err
}
set = append(set, kv...)
return &types.LocalDBSet{KV: set}, nil
}
func (t *token) ExecLocal_TokenBurn(payload *tokenty.TokenBurn, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
localToken, err := loadLocalToken(payload.Symbol, tx.From(), tokenty.TokenStatusCreated, t.GetLocalDB())
if err != nil {
return nil, err
}
localToken = setBurn(localToken, t.GetHeight(), t.GetBlockTime(), payload.Amount)
var set []*types.KeyValue
key := calcTokenStatusKeyLocal(payload.Symbol, tx.From(), tokenty.TokenStatusCreated)
set = append(set, &types.KeyValue{Key: key, Value: types.Encode(localToken)})
table := NewLogsTable(t.GetLocalDB())
txIndex := dapp.HeightIndexStr(t.GetHeight(), int64(index))
err = table.Add(&tokenty.LocalLogs{Symbol: payload.Symbol, TxIndex: txIndex, ActionType: tokenty.TokenActionBurn, TxHash: "0x" + hex.EncodeToString(tx.Hash())})
if err != nil {
return nil, err
}
kv, err := table.Save()
if err != nil {
return nil, err
}
set = append(set, kv...)
return &types.LocalDBSet{KV: set}, 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 executor
// 记录token 的更改记录,
// 包含创建完成, 铸币, 以后可能包含燃烧等
import (
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/common/db/table"
"github.com/33cn/chain33/types"
pty "github.com/33cn/plugin/plugin/dapp/token/types"
)
var opt_logs_table = &table.Option{
Prefix: "LODB-token",
Name: "logs",
Primary: "txIndex",
Index: []string{
"symbol",
},
}
// LogsRow row
type LogsRow struct {
*pty.LocalLogs
}
// NewOrderRow create row
func NewOrderRow() *LogsRow {
return &LogsRow{LocalLogs: nil}
}
// CreateRow create row
func (r *LogsRow) CreateRow() *table.Row {
return &table.Row{Data: &pty.LocalLogs{}}
}
// SetPayload set payload
func (r *LogsRow) SetPayload(data types.Message) error {
if d, ok := data.(*pty.LocalLogs); ok {
r.LocalLogs = d
return nil
}
return types.ErrTypeAsset
}
// Get get index key
func (r *LogsRow) Get(key string) ([]byte, error) {
switch key {
case "txIndex":
return []byte(r.TxIndex), nil
case "symbol":
return []byte(r.Symbol), nil
default:
return nil, types.ErrNotFound
}
}
// NewLogsTable create table
func NewLogsTable(kvdb dbm.KV) *table.Table {
rowMeta := NewOrderRow()
err := rowMeta.SetPayload(&pty.LocalLogs{})
if err != nil {
panic(err)
}
t, err := table.NewTable(rowMeta, kvdb, opt_logs_table)
if err != nil {
panic(err)
}
return t
}
func list(db dbm.KVDB, indexName string, data *pty.LocalLogs, count, direction int32) ([]*table.Row, error) {
query := NewLogsTable(db).GetQuery(db)
var primary []byte
if len(data.TxIndex) > 0 {
primary = []byte(data.TxIndex)
}
cur := &LogsRow{LocalLogs: data}
index, err := cur.Get(indexName)
if err != nil {
tokenlog.Error("query List failed", "key", string(primary), "param", data, "err", err)
return nil, err
}
tokenlog.Debug("query List dbg", "indexName", indexName, "index", string(index), "primary", primary, "count", count, "direction", direction)
rows, err := query.ListIndex(indexName, index, primary, count, direction)
if err != nil {
tokenlog.Error("query List failed", "key", string(primary), "param", data, "err", err)
return nil, err
}
if len(rows) == 0 {
return nil, types.ErrNotFound
}
return rows, nil
}
......@@ -69,3 +69,25 @@ func (t *token) Query_GetTxByToken(in *tokenty.ReqTokenTx) (types.Message, error
}
return t.getTxByToken(in)
}
// Query_GetTokenHistory 获取token 的变更历史
func (t *token) Query_GetTokenHistory(in *types.ReqString) (types.Message, error) {
if in == nil {
return nil, types.ErrInvalidParam
}
rows, err := list(t.GetLocalDB(), "symbol", &tokenty.LocalLogs{Symbol: in.Data}, -1, 0)
if err != nil {
tokenlog.Error("Query_GetTokenHistory", "err", err)
return nil, err
}
var replys tokenty.ReplyTokenLogs
for _, row := range rows {
o, ok := row.Data.(*tokenty.LocalLogs)
if !ok {
tokenlog.Error("Query_GetTokenHistory", "err", "bad row type")
return nil, types.ErrTypeAsset
}
replys.Logs = append(replys.Logs, o)
}
return &replys, nil
}
......@@ -157,6 +157,7 @@ func TestPrecreate(t *testing.T) {
Total: tokenAmount,
Price: tokenPrice,
Owner: addr,
Category: pty.CategoryMintBurnSupport,
}
precreate := &pty.TokenAction{
Ty: pty.TokenActionPreCreate,
......@@ -312,6 +313,78 @@ func TestQueryAsset(t *testing.T) {
}
func TestTokenMint(t *testing.T) {
if !isMainNetTest {
return
}
fmt.Println("TestTokenMint start")
defer fmt.Println("TestTokenMint end")
v := &pty.TokenAction_TokenMint{TokenMint: &pty.TokenMint{Symbol: tokenSym, Amount: transAmount}}
transfer := &pty.TokenAction{Value: v, Ty: pty.ActionTransfer}
tx := &types.Transaction{Execer: []byte(execName), Payload: types.Encode(transfer), Fee: fee, To: addrexec}
tx.Nonce = r.Int63()
tx.Sign(types.SECP256K1, privkey)
reply, err := mainClient.SendTransaction(context.Background(), tx)
if err != nil {
fmt.Println("err", err)
t.Error(err)
return
}
if !reply.IsOk {
fmt.Println("err = ", reply.GetMsg())
t.Error(ErrTest)
return
}
if !waitTx(tx.Hash()) {
t.Error(ErrTest)
return
}
}
func TestQueryTokenLogs(t *testing.T) {
if !isParaNetTest {
return
}
fmt.Println("TestQueryTokenLogs start")
defer fmt.Println("TestQueryTokenLogs end")
var req types.ChainExecutor
req.Driver = execName
req.FuncName = "GetTokenHistory"
req.Param = types.Encode(&types.ReqString{Data: tokenSym})
reply, err := paraClient.QueryChain(context.Background(), &req)
if err != nil {
fmt.Println(err)
t.Error(err)
return
}
if !reply.IsOk {
fmt.Println("Query reply err")
t.Error(ErrTest)
return
}
var res pty.ReplyTokenLogs
err = types.Decode(reply.Msg, &res)
if err != nil {
t.Error(err)
return
}
assert.Equal(t, 2, len(res.Logs))
for _, l := range res.Logs {
fmt.Println(l.Symbol)
fmt.Println(l.TxHash)
fmt.Println(l.TxIndex)
fmt.Println(l.ActionType)
}
}
//***************************************************
//**************common actions for Test**************
//***************************************************
......
package executor
import (
"testing"
"github.com/33cn/chain33/account"
"github.com/33cn/chain33/common/address"
"github.com/33cn/chain33/types"
"github.com/33cn/chain33/util"
"github.com/33cn/chain33/common"
"github.com/33cn/chain33/common/crypto"
dbm "github.com/33cn/chain33/common/db"
pty "github.com/33cn/plugin/plugin/dapp/token/types"
"github.com/stretchr/testify/assert"
//"github.com/33cn/chain33/types/jsonpb"
)
type execEnv struct {
blockTime int64
blockHeight int64
difficulty uint64
}
var (
Symbol = "TEST"
AssetExecToken = "token"
AssetExecPara = "paracross"
PrivKeyA = "0x6da92a632ab7deb67d38c0f6560bcfed28167998f6496db64c258d5e8393a81b" // 1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4
PrivKeyB = "0x19c069234f9d3e61135fefbeb7791b149cdf6af536f26bebb310d4cd22c3fee4" // 1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR
PrivKeyC = "0x7a80a1f75d7360c6123c32a78ecf978c1ac55636f87892df38d8b85a9aeff115" // 1NLHPEcbTWWxxU3dGUZBhayjrCHD3psX7k
PrivKeyD = "0xcacb1f5d51700aea07fca2246ab43b0917d70405c65edea9b5063d72eb5c6b71" // 1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs
Nodes = [][]byte{
[]byte("1KSBd17H7ZK8iT37aJztFB22XGwsPTdwE4"),
[]byte("1JRNjdEqp4LJ5fqycUBm9ayCKSeeskgMKR"),
[]byte("1NLHPEcbTWWxxU3dGUZBhayjrCHD3psX7k"),
[]byte("1MCftFynyvG2F4ED5mdHYgziDxx6vDrScs"),
}
)
func TestToken(t *testing.T) {
types.SetTitleOnlyForTest("chain33")
tokenTotal := int64(10000 * 1e8)
tokenBurn := int64(10 * 1e8)
tokenMint := int64(20 * 1e8)
total := int64(100000)
accountA := types.Account{
Balance: total,
Frozen: 0,
Addr: string(Nodes[0]),
}
accountB := types.Account{
Balance: total,
Frozen: 0,
Addr: string(Nodes[1]),
}
execAddr := address.ExecAddress(pty.TokenX)
stateDB, _ := dbm.NewGoMemDB("1", "2", 100)
_, _, kvdb := util.CreateTestDB()
accA, _ := account.NewAccountDB(AssetExecPara, Symbol, stateDB)
accA.SaveExecAccount(execAddr, &accountA)
accB, _ := account.NewAccountDB(AssetExecPara, Symbol, stateDB)
accB.SaveExecAccount(execAddr, &accountB)
env := execEnv{
10,
types.GetDappFork(pty.TokenX, pty.ForkTokenSymbolWithNumberX),
1539918074,
}
// set config key
item := &types.ConfigItem{
Key: "mavl-manage-token-blacklist",
Value: &types.ConfigItem_Arr{
Arr: &types.ArrayConfig{Value: []string{"bty"}},
},
}
stateDB.Set([]byte(item.Key), types.Encode(item))
item2 := &types.ConfigItem{
Key: "mavl-manage-token-finisher",
Value: &types.ConfigItem_Arr{
Arr: &types.ArrayConfig{Value: []string{string(Nodes[0])}},
},
}
stateDB.Set([]byte(item2.Key), types.Encode(item2))
// create token
// 创建
//ty := pty.TokenType{}
p1 := &pty.TokenPreCreate{
Name: Symbol,
Symbol: Symbol,
Introduction: Symbol,
Total: tokenTotal,
Price: 0,
Owner: string(Nodes[0]),
Category: pty.CategoryMintBurnSupport,
}
//v, _ := types.PBToJSON(p1)
createTx, err := types.CallCreateTransaction(pty.TokenX, "TokenPreCreate", p1)
if err != nil {
t.Error("RPC_Default_Process", "err", err)
}
createTx, err = signTx(createTx, PrivKeyA)
if err != nil {
t.Error("RPC_Default_Process sign", "err", err)
}
exec := newToken()
exec.SetStateDB(stateDB)
exec.SetLocalDB(kvdb)
exec.SetEnv(env.blockHeight, env.blockTime, env.difficulty)
receipt, err := exec.Exec(createTx, int(1))
assert.Nil(t, err)
assert.NotNil(t, receipt)
t.Log(receipt)
for _, kv := range receipt.KV {
stateDB.Set(kv.Key, kv.Value)
}
receiptDate := &types.ReceiptData{Ty: receipt.Ty, Logs: receipt.Logs}
set, err := exec.ExecLocal(createTx, receiptDate, int(1))
assert.Nil(t, err)
assert.NotNil(t, set)
p2 := &pty.TokenFinishCreate{
Symbol: Symbol,
Owner: string(Nodes[0]),
}
//v, _ := types.PBToJSON(p1)
createTx2, err := types.CallCreateTransaction(pty.TokenX, "TokenFinishCreate", p2)
if err != nil {
t.Error("RPC_Default_Process", "err", err)
}
createTx2, err = signTx(createTx2, PrivKeyA)
if err != nil {
t.Error("RPC_Default_Process sign", "err", err)
}
exec.SetEnv(env.blockHeight+1, env.blockTime+1, env.difficulty)
receipt, err = exec.Exec(createTx2, int(1))
assert.Nil(t, err)
assert.NotNil(t, receipt)
//t.Log(receipt)
for _, kv := range receipt.KV {
stateDB.Set(kv.Key, kv.Value)
}
accDB, _ := account.NewAccountDB(pty.TokenX, Symbol, stateDB)
accChcek := accDB.LoadAccount(string(Nodes[0]))
assert.Equal(t, tokenTotal, accChcek.Balance)
receiptDate = &types.ReceiptData{Ty: receipt.Ty, Logs: receipt.Logs}
set, err = exec.ExecLocal(createTx2, receiptDate, int(1))
assert.Nil(t, err)
assert.NotNil(t, set)
// mint burn
p3 := &pty.TokenMint{
Symbol: Symbol,
Amount: tokenMint,
}
//v, _ := types.PBToJSON(p1)
createTx3, err := types.CallCreateTransaction(pty.TokenX, "TokenMint", p3)
if err != nil {
t.Error("RPC_Default_Process", "err", err)
}
createTx3, err = signTx(createTx3, PrivKeyA)
if err != nil {
t.Error("RPC_Default_Process sign", "err", err)
}
exec.SetEnv(env.blockHeight+2, env.blockTime+2, env.difficulty)
receipt, err = exec.Exec(createTx3, int(1))
assert.Nil(t, err)
assert.NotNil(t, receipt)
//t.Log(receipt)
for _, kv := range receipt.KV {
stateDB.Set(kv.Key, kv.Value)
}
accChcek = accDB.LoadAccount(string(Nodes[0]))
assert.Equal(t, tokenTotal+tokenMint, accChcek.Balance)
receiptDate = &types.ReceiptData{Ty: receipt.Ty, Logs: receipt.Logs}
set, err = exec.ExecLocal(createTx3, receiptDate, int(1))
assert.Nil(t, err)
assert.NotNil(t, set)
p4 := &pty.TokenBurn{
Symbol: Symbol,
Amount: tokenBurn,
}
//v, _ := types.PBToJSON(p1)
createTx4, err := types.CallCreateTransaction(pty.TokenX, "TokenBurn", p4)
if err != nil {
t.Error("RPC_Default_Process", "err", err)
}
createTx4, err = signTx(createTx4, PrivKeyA)
if err != nil {
t.Error("RPC_Default_Process sign", "err", err)
}
exec.SetEnv(env.blockHeight+1, env.blockTime+1, env.difficulty)
receipt, err = exec.Exec(createTx4, int(1))
assert.Nil(t, err)
assert.NotNil(t, receipt)
//t.Log(receipt)
for _, kv := range receipt.KV {
stateDB.Set(kv.Key, kv.Value)
}
accChcek = accDB.LoadAccount(string(Nodes[0]))
assert.Equal(t, tokenTotal+tokenMint-tokenBurn, accChcek.Balance)
receiptDate = &types.ReceiptData{Ty: receipt.Ty, Logs: receipt.Logs}
set, err = exec.ExecLocal(createTx4, receiptDate, int(1))
assert.Nil(t, err)
assert.NotNil(t, set)
}
func signTx(tx *types.Transaction, hexPrivKey string) (*types.Transaction, error) {
signType := types.SECP256K1
c, err := crypto.New(types.GetSignName(pty.TokenX, signType))
if err != nil {
return tx, err
}
bytes, err := common.FromHex(hexPrivKey[:])
if err != nil {
return tx, err
}
privKey, err := c.PrivKeyFromBytes(bytes)
if err != nil {
return tx, err
}
tx.Sign(int32(signType), privKey)
return tx, nil
}
......@@ -40,7 +40,10 @@ func newTokenDB(preCreate *pty.TokenPreCreate, creator string, height int64) *to
func (t *tokenDB) save(db dbm.KV, key []byte) {
set := t.getKVSet(key)
for i := 0; i < len(set); i++ {
db.Set(set[i].GetKey(), set[i].Value)
err := db.Set(set[i].GetKey(), set[i].Value)
if err != nil {
panic(err)
}
}
}
......@@ -59,6 +62,48 @@ func (t *tokenDB) getKVSet(key []byte) (kvset []*types.KeyValue) {
return kvset
}
func loadTokenDB(db dbm.KV, symbol string) (*tokenDB, error) {
token, err := db.Get(calcTokenKey(symbol))
if err != nil {
tokenlog.Error("tokendb load ", "Can't get token form db for token", symbol)
return nil, pty.ErrTokenNotExist
}
var t pty.Token
err = types.Decode(token, &t)
if err != nil {
tokenlog.Error("tokendb load", "Can't decode token info", symbol)
return nil, err
}
return &tokenDB{t}, nil
}
func (t *tokenDB) mint(db dbm.KV, addr string, amount int64) ([]*types.KeyValue, []*types.ReceiptLog, error) {
if t.token.Owner != addr {
return nil, nil, types.ErrNotAllow
}
if t.token.Total+amount > types.MaxTokenBalance {
return nil, nil, types.ErrAmount
}
prevToken := t.token
t.token.Total += amount
kvs := append(t.getKVSet(calcTokenKey(t.token.Symbol)), t.getKVSet(calcTokenAddrNewKeyS(t.token.Symbol, t.token.Owner))...)
logs := []*types.ReceiptLog{{Ty: pty.TyLogTokenMint, Log: types.Encode(&pty.ReceiptTokenAmount{Prev: &prevToken, Current: &t.token})}}
return kvs, logs, nil
}
func (t *tokenDB) burn(db dbm.KV, amount int64) ([]*types.KeyValue, []*types.ReceiptLog, error) {
if t.token.Total < amount {
return nil, nil, types.ErrNoBalance
}
prevToken := t.token
t.token.Total -= amount
kvs := append(t.getKVSet(calcTokenKey(t.token.Symbol)), t.getKVSet(calcTokenAddrNewKeyS(t.token.Symbol, t.token.Owner))...)
logs := []*types.ReceiptLog{{Ty: pty.TyLogTokenBurn, Log: types.Encode(&pty.ReceiptTokenAmount{Prev: &prevToken, Current: &t.token})}}
return kvs, logs, nil
}
func getTokenFromDB(db dbm.KV, symbol string, owner string) (*pty.Token, error) {
key := calcTokenAddrKeyS(symbol, owner)
value, err := db.Get(key)
......@@ -468,3 +513,87 @@ func validSymbolWithHeight(cs []byte, height int64) bool {
}
return validSymbolOriginal(cs)
}
// 铸币不可控, 也是麻烦。 2选1
// 1. 谁可以发起
// 2. 是否需要审核 这个会增加管理的成本
// 现在实现选择 1
func (action *tokenAction) mint(mint *pty.TokenMint) (*types.Receipt, error) {
if mint == nil {
return nil, types.ErrInvalidParam
}
if mint.GetAmount() < 0 || mint.GetAmount() > types.MaxTokenBalance || mint.GetSymbol() == "" {
return nil, types.ErrInvalidParam
}
tokendb, err := loadTokenDB(action.db, mint.GetSymbol())
if err != nil {
return nil, err
}
if tokendb.token.Category&pty.CategoryMintBurnSupport == 0 {
tokenlog.Error("Can't mint category", "category", tokendb.token.Category, "support", pty.CategoryMintBurnSupport)
return nil, types.ErrNotSupport
}
kvs, logs, err := tokendb.mint(action.db, action.fromaddr, mint.Amount)
if err != nil {
tokenlog.Error("token mint ", "symbol", mint.GetSymbol(), "error", err, "from", action.fromaddr, "owner", tokendb.token.Owner)
return nil, err
}
tokenAccount, err := account.NewAccountDB("token", mint.GetSymbol(), action.db)
if err != nil {
return nil, err
}
tokenlog.Debug("mint", "token.Owner", mint.Symbol, "token.GetTotal()", mint.Amount)
receipt, err := tokenAccount.Mint(action.fromaddr, mint.Amount)
if err != nil {
return nil, err
}
logs = append(logs, receipt.Logs...)
kvs = append(kvs, receipt.KV...)
return &types.Receipt{Ty: types.ExecOk, KV: kvs, Logs: logs}, nil
}
func (action *tokenAction) burn(burn *pty.TokenBurn) (*types.Receipt, error) {
if burn == nil {
return nil, types.ErrInvalidParam
}
if burn.GetAmount() < 0 || burn.GetAmount() > types.MaxTokenBalance || burn.GetSymbol() == "" {
return nil, types.ErrInvalidParam
}
tokendb, err := loadTokenDB(action.db, burn.GetSymbol())
if err != nil {
return nil, err
}
if tokendb.token.Category&pty.CategoryMintBurnSupport == 0 {
tokenlog.Error("Can't burn category", "category", tokendb.token.Category, "support", pty.CategoryMintBurnSupport)
return nil, types.ErrNotSupport
}
kvs, logs, err := tokendb.burn(action.db, burn.Amount)
if err != nil {
tokenlog.Error("token burn ", "symbol", burn.GetSymbol(), "error", err, "from", action.fromaddr, "owner", tokendb.token.Owner)
return nil, err
}
tokenAccount, err := account.NewAccountDB("token", burn.GetSymbol(), action.db)
if err != nil {
return nil, err
}
tokenlog.Debug("burn", "token.Owner", burn.Symbol, "token.GetTotal()", burn.Amount)
receipt, err := tokenAccount.Burn(action.fromaddr, burn.Amount)
if err != nil {
return nil, err
}
logs = append(logs, receipt.Logs...)
kvs = append(kvs, receipt.KV...)
return &types.Receipt{Ty: types.ExecOk, KV: kvs, Logs: logs}, nil
}
......@@ -172,7 +172,10 @@ func updateAddrReciver(cachedb dbm.KVDB, token string, addr string, amount int64
} else {
recv -= amount
}
setAddrReciver(cachedb, token, addr, recv)
err = setAddrReciver(cachedb, token, addr, recv)
if err != nil {
return nil, err
}
//keyvalue
return getAddrReciverKV(token, addr, recv), nil
}
......@@ -15,6 +15,8 @@ message TokenAction {
AssetsWithdraw withdraw = 5;
AssetsGenesis genesis = 6;
AssetsTransferToExec transferToExec = 8;
TokenMint tokenMint = 9;
TokenBurn tokenBurn = 10;
}
int32 Ty = 7;
}
......@@ -40,6 +42,16 @@ message TokenRevokeCreate {
string owner = 2;
}
message TokenMint {
string symbol = 1;
int64 amount = 2;
}
message TokenBurn {
string symbol = 1;
int64 amount = 2;
}
// state db
message Token {
string name = 1;
......@@ -60,6 +72,11 @@ message ReceiptToken {
int32 status = 3;
}
message ReceiptTokenAmount {
Token prev = 1;
Token current = 2;
}
// local
message LocalToken {
string name = 1;
......@@ -82,6 +99,13 @@ message LocalToken {
int32 category = 17;
}
message LocalLogs {
string symbol = 1;
string txIndex = 2;
int32 actionType = 3;
string txHash = 4;
}
// query
message ReqTokens {
bool queryAll = 1;
......@@ -142,6 +166,10 @@ message ReqTokenTx {
string addr = 7;
}
message ReplyTokenLogs {
repeated LocalLogs logs = 1;
}
service token {
// token 对外提供服务的接口
//区块链接口
......
......@@ -121,3 +121,29 @@ func (c *Jrpc) CreateRawTokenRevokeTx(param *tokenty.TokenRevokeCreate, result *
*result = hex.EncodeToString(data)
return nil
}
// CreateRawTokenMintTx 创建未签名的mint Token交易
func (c *Jrpc) CreateRawTokenMintTx(param *tokenty.TokenMint, result *interface{}) error {
if param == nil || param.Symbol == "" || param.Amount <= 0 {
return types.ErrInvalidParam
}
data, err := types.CallCreateTx(types.ExecName(tokenty.TokenX), "TokenMint", param)
if err != nil {
return err
}
*result = hex.EncodeToString(data)
return nil
}
// CreateRawTokenBurnTx 创建未签名的 burn Token交易
func (c *Jrpc) CreateRawTokenBurnTx(param *tokenty.TokenBurn, result *interface{}) error {
if param == nil || param.Symbol == "" || param.Amount <= 0 {
return types.ErrInvalidParam
}
data, err := types.CallCreateTx(types.ExecName(tokenty.TokenX), "TokenBurn", param)
if err != nil {
return err
}
*result = hex.EncodeToString(data)
return nil
}
......@@ -19,6 +19,10 @@ const (
TokenActionRevokeCreate = 9
// TokenActionTransferToExec for token transfer to exec
TokenActionTransferToExec = 11
// TokenActionMint for token mint
TokenActionMint = 12
// TokenActionBurn for token burn
TokenActionBurn = 13
)
// token status
......@@ -72,6 +76,10 @@ const (
TyLogTokenGenesisTransfer = 321
// TyLogTokenGenesisDeposit log for token genesis deposit
TyLogTokenGenesisDeposit = 322
// TyLogTokenMint log for token mint
TyLogTokenMint = 323
// TyLogTokenBurn log for token burn
TyLogTokenBurn = 324
)
const (
......@@ -82,3 +90,8 @@ const (
// TokenIntroLenLimit token introduction length limit
TokenIntroLenLimit = 1024
)
const (
// CategoryMintBurnSupport support mint & burn
CategoryMintBurnSupport = 1 << iota
)
This diff is collapsed.
......@@ -57,6 +57,8 @@ func (t *TokenType) GetTypeMap() map[string]int32 {
"TokenFinishCreate": TokenActionFinishCreate,
"TokenRevokeCreate": TokenActionRevokeCreate,
"TransferToExec": TokenActionTransferToExec,
"TokenMint": TokenActionMint,
"TokenBurn": TokenActionBurn,
}
}
......@@ -75,6 +77,8 @@ func (t *TokenType) GetLogMap() map[int64]*types.LogInfo {
TyLogPreCreateToken: {Ty: reflect.TypeOf(ReceiptToken{}), Name: "LogPreCreateToken"},
TyLogFinishCreateToken: {Ty: reflect.TypeOf(ReceiptToken{}), Name: "LogFinishCreateToken"},
TyLogRevokeCreateToken: {Ty: reflect.TypeOf(ReceiptToken{}), Name: "LogRevokeCreateToken"},
TyLogTokenMint: {Ty: reflect.TypeOf(ReceiptTokenAmount{}), Name: "LogMintToken"},
TyLogTokenBurn: {Ty: reflect.TypeOf(ReceiptTokenAmount{}), Name: "LogBurnToken"},
}
}
......
package init
import (
_ "github.com/33cn/plugin/plugin/store/kvdb" //auto gen
_ "github.com/33cn/plugin/plugin/store/kvmvcc" //auto gen
_ "github.com/33cn/plugin/plugin/store/mpt" //auto gen
_ "github.com/33cn/plugin/plugin/store/kvdb" //auto gen
_ "github.com/33cn/plugin/plugin/store/kvmvcc" //auto gen
_ "github.com/33cn/plugin/plugin/store/kvmvccmavl" //auto gen
_ "github.com/33cn/plugin/plugin/store/mpt" //auto gen
)
// 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 kvmvccmavl kvmvcc+mavl接口
package kvmvccmavl
import (
"bytes"
"errors"
"fmt"
"sync"
"sync/atomic"
"time"
dbm "github.com/33cn/chain33/common/db"
clog "github.com/33cn/chain33/common/log"
log "github.com/33cn/chain33/common/log/log15"
"github.com/33cn/chain33/queue"
drivers "github.com/33cn/chain33/system/store"
"github.com/33cn/chain33/types"
"github.com/hashicorp/golang-lru"
)
var (
kmlog = log.New("module", "kvmvccMavl")
// ErrStateHashLost ...
ErrStateHashLost = errors.New("ErrStateHashLost")
kvmvccMavlFork int64 = 200 * 10000
isDelMavlData = false
delMavlDataHeight = kvmvccMavlFork + 10000
delMavlDataState int32
wg sync.WaitGroup
quit bool
)
const (
cacheSize = 2048 //可以缓存2048个roothash, height对
batchDataSize = 1024 * 1024 * 1
delMavlStateStart = 1
delMavlStateEnd = 0
)
// SetLogLevel set log level
func SetLogLevel(level string) {
clog.SetLogLevel(level)
}
// DisableLog disable log output
func DisableLog() {
kmlog.SetHandler(log.DiscardHandler())
}
func init() {
drivers.Reg("kvmvccmavl", New)
}
// KVmMavlStore provide kvmvcc and mavl store interface implementation
type KVmMavlStore struct {
*drivers.BaseStore
*KVMVCCStore
*MavlStore
cache *lru.Cache
}
type subKVMVCCConfig struct {
EnableMVCCIter bool `json:"enableMVCCIter"`
EnableMavlPrune bool `json:"enableMavlPrune"`
PruneHeight int32 `json:"pruneHeight"`
}
type subMavlConfig struct {
EnableMavlPrefix bool `json:"enableMavlPrefix"`
EnableMVCC bool `json:"enableMVCC"`
EnableMavlPrune bool `json:"enableMavlPrune"`
PruneHeight int32 `json:"pruneHeight"`
}
type subConfig struct {
EnableMVCCIter bool `json:"enableMVCCIter"`
EnableMavlPrefix bool `json:"enableMavlPrefix"`
EnableMVCC bool `json:"enableMVCC"`
EnableMavlPrune bool `json:"enableMavlPrune"`
PruneHeight int32 `json:"pruneHeight"`
}
// New construct KVMVCCStore module
func New(cfg *types.Store, sub []byte) queue.Module {
bs := drivers.NewBaseStore(cfg)
var kvms *KVmMavlStore
var subcfg subConfig
var subKVMVCCcfg subKVMVCCConfig
var subMavlcfg subMavlConfig
if sub != nil {
types.MustDecode(sub, &subcfg)
subKVMVCCcfg.EnableMVCCIter = subcfg.EnableMVCCIter
subKVMVCCcfg.EnableMavlPrune = subcfg.EnableMavlPrune
subKVMVCCcfg.PruneHeight = subcfg.PruneHeight
subMavlcfg.EnableMavlPrefix = subcfg.EnableMavlPrefix
subMavlcfg.EnableMVCC = subcfg.EnableMVCC
subMavlcfg.EnableMavlPrune = subcfg.EnableMavlPrune
subMavlcfg.PruneHeight = subcfg.PruneHeight
}
cache, err := lru.New(cacheSize)
if err != nil {
panic("new KVmMavlStore fail")
}
kvms = &KVmMavlStore{bs, NewKVMVCC(&subKVMVCCcfg, bs.GetDB()),
NewMavl(&subMavlcfg, bs.GetDB()), cache}
// 查询是否已经删除mavl
_, err = bs.GetDB().Get(genDelMavlKey(mvccPrefix))
if err == nil {
isDelMavlData = true
}
bs.SetChild(kvms)
return kvms
}
// Close the KVmMavlStore module
func (kvmMavls *KVmMavlStore) Close() {
quit = true
wg.Wait()
kvmMavls.KVMVCCStore.Close()
kvmMavls.MavlStore.Close()
kvmMavls.BaseStore.Close()
kmlog.Info("store kvmMavls closed")
}
// Set kvs with statehash to KVmMavlStore
func (kvmMavls *KVmMavlStore) Set(datas *types.StoreSet, sync bool) ([]byte, error) {
if datas.Height < kvmvccMavlFork {
hash, err := kvmMavls.MavlStore.Set(datas, sync)
if err != nil {
return hash, err
}
_, err = kvmMavls.KVMVCCStore.Set(datas, hash, sync)
if err != nil {
return hash, err
}
if err == nil {
kvmMavls.cache.Add(string(hash), datas.Height)
}
return hash, err
}
// 仅仅做kvmvcc
hash, err := kvmMavls.KVMVCCStore.Set(datas, nil, sync)
if err == nil {
kvmMavls.cache.Add(string(hash), datas.Height)
}
// 删除Mavl数据
if datas.Height > delMavlDataHeight && !isDelMavlData && !isDelMavling() {
wg.Add(1)
go DelMavl(kvmMavls.GetDB())
}
return hash, err
}
// Get kvs with statehash from KVmMavlStore
func (kvmMavls *KVmMavlStore) Get(datas *types.StoreGet) [][]byte {
return kvmMavls.KVMVCCStore.Get(datas)
}
// MemSet set kvs to the mem of KVmMavlStore module and return the StateHash
func (kvmMavls *KVmMavlStore) MemSet(datas *types.StoreSet, sync bool) ([]byte, error) {
if datas.Height < kvmvccMavlFork {
hash, err := kvmMavls.MavlStore.MemSet(datas, sync)
if err != nil {
return hash, err
}
_, err = kvmMavls.KVMVCCStore.MemSet(datas, hash, sync)
if err != nil {
return hash, err
}
if err == nil {
kvmMavls.cache.Add(string(hash), datas.Height)
}
return hash, err
}
// 仅仅做kvmvcc
hash, err := kvmMavls.KVMVCCStore.MemSet(datas, nil, sync)
if err == nil {
kvmMavls.cache.Add(string(hash), datas.Height)
}
// 删除Mavl数据
if datas.Height > delMavlDataHeight && !isDelMavlData && !isDelMavling() {
wg.Add(1)
go DelMavl(kvmMavls.GetDB())
}
return hash, err
}
// Commit kvs in the mem of KVmMavlStore module to state db and return the StateHash
func (kvmMavls *KVmMavlStore) Commit(req *types.ReqHash) ([]byte, error) {
if value, ok := kvmMavls.cache.Get(string(req.Hash)); ok {
if value.(int64) < kvmvccMavlFork {
hash, err := kvmMavls.MavlStore.Commit(req)
if err != nil {
return hash, err
}
_, err = kvmMavls.KVMVCCStore.Commit(req)
return hash, err
}
return kvmMavls.KVMVCCStore.Commit(req)
}
return kvmMavls.KVMVCCStore.Commit(req)
}
// Rollback kvs in the mem of KVmMavlStore module and return the StateHash
func (kvmMavls *KVmMavlStore) Rollback(req *types.ReqHash) ([]byte, error) {
if value, ok := kvmMavls.cache.Get(string(req.Hash)); ok {
if value.(int64) < kvmvccMavlFork {
hash, err := kvmMavls.MavlStore.Rollback(req)
if err != nil {
return hash, err
}
_, err = kvmMavls.KVMVCCStore.Rollback(req)
return hash, err
}
return kvmMavls.KVMVCCStore.Rollback(req)
}
return kvmMavls.KVMVCCStore.Rollback(req)
}
// IterateRangeByStateHash travel with Prefix by StateHash to get the latest version kvs.
func (kvmMavls *KVmMavlStore) IterateRangeByStateHash(statehash []byte, start []byte, end []byte, ascending bool, fn func(key, value []byte) bool) {
if value, ok := kvmMavls.cache.Get(string(statehash)); ok {
if value.(int64) < kvmvccMavlFork {
kvmMavls.MavlStore.IterateRangeByStateHash(statehash, start, end, ascending, fn)
return
}
kvmMavls.KVMVCCStore.IterateRangeByStateHash(statehash, start, end, ascending, fn)
return
}
kvmMavls.KVMVCCStore.IterateRangeByStateHash(statehash, start, end, ascending, fn)
}
// ProcEvent handles supported events
func (kvmMavls *KVmMavlStore) ProcEvent(msg *queue.Message) {
msg.ReplyErr("KVmMavlStore", types.ErrActionNotSupport)
}
// Del set kvs to nil with StateHash
func (kvmMavls *KVmMavlStore) Del(req *types.StoreDel) ([]byte, error) {
if req.Height < kvmvccMavlFork {
hash, err := kvmMavls.MavlStore.Del(req)
if err != nil {
return hash, err
}
_, err = kvmMavls.KVMVCCStore.Del(req)
if err != nil {
return hash, err
}
if err == nil {
kvmMavls.cache.Remove(string(req.StateHash))
}
return hash, err
}
// 仅仅做kvmvcc
hash, err := kvmMavls.KVMVCCStore.Del(req)
if err == nil {
kvmMavls.cache.Remove(string(req.StateHash))
}
return hash, err
}
// DelMavl 数据库中mavl数据清除
// 达到kvmvccMavlFork + 100000 后触发清除
func DelMavl(db dbm.DB) {
defer wg.Done()
setDelMavl(delMavlStateStart)
defer setDelMavl(delMavlStateEnd)
isDel := delMavlData(db)
if isDel {
isDelMavlData = true
kmlog.Info("DelMavl success")
}
}
func delMavlData(db dbm.DB) bool {
it := db.Iterator(nil, nil, true)
defer it.Close()
batch := db.NewBatch(true)
for it.Rewind(); it.Valid(); it.Next() {
if quit {
return false
}
if !bytes.HasPrefix(it.Key(), mvccPrefix) { // 将非mvcc的mavl数据全部删除
batch.Delete(it.Key())
if batch.ValueSize() > batchDataSize {
batch.Write()
batch.Reset()
time.Sleep(time.Millisecond * 100)
}
}
}
batch.Set(genDelMavlKey(mvccPrefix), []byte(""))
batch.Write()
return true
}
func genDelMavlKey(prefix []byte) []byte {
delMavl := "--delMavlData--"
return []byte(fmt.Sprintf("%s%s", string(prefix), delMavl))
}
func isDelMavling() bool {
return atomic.LoadInt32(&delMavlDataState) == 1
}
func setDelMavl(state int32) {
atomic.StoreInt32(&delMavlDataState, state)
}
This diff is collapsed.
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 kvmvccmavl
import (
"sync"
"github.com/33cn/chain33/common"
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/queue"
"github.com/33cn/chain33/system/store/mavl/db"
"github.com/33cn/chain33/types"
)
// MavlStore mavl store struct
type MavlStore struct {
db dbm.DB
trees *sync.Map
enableMavlPrefix bool
enableMVCC bool
enableMavlPrune bool
pruneHeight int32
}
// NewMavl new mavl store module
func NewMavl(sub *subMavlConfig, db dbm.DB) *MavlStore {
var subcfg subMavlConfig
if sub != nil {
subcfg.EnableMavlPrefix = sub.EnableMavlPrefix
subcfg.EnableMVCC = sub.EnableMVCC
subcfg.EnableMavlPrune = sub.EnableMavlPrune
subcfg.PruneHeight = sub.PruneHeight
}
mavls := &MavlStore{db, &sync.Map{}, subcfg.EnableMavlPrefix, subcfg.EnableMVCC, subcfg.EnableMavlPrune, subcfg.PruneHeight}
mavl.EnableMavlPrefix(subcfg.EnableMavlPrefix)
mavl.EnableMVCC(subcfg.EnableMVCC)
mavl.EnablePrune(subcfg.EnableMavlPrune)
mavl.SetPruneHeight(int(subcfg.PruneHeight))
return mavls
}
// Close close mavl store
func (mavls *MavlStore) Close() {
mavl.ClosePrune()
kmlog.Info("store mavl closed")
}
// Set set k v to mavl store db; sync is true represent write sync
func (mavls *MavlStore) Set(datas *types.StoreSet, sync bool) ([]byte, error) {
return mavl.SetKVPair(mavls.db, datas, sync)
}
// Get get values by keys
func (mavls *MavlStore) Get(datas *types.StoreGet) [][]byte {
var tree *mavl.Tree
var err error
values := make([][]byte, len(datas.Keys))
search := string(datas.StateHash)
if data, ok := mavls.trees.Load(search); ok {
tree = data.(*mavl.Tree)
} else {
tree = mavl.NewTree(mavls.db, true)
//get接口也应该传入高度
//tree.SetBlockHeight(datas.Height)
err = tree.Load(datas.StateHash)
kmlog.Debug("store mavl get tree", "err", err, "StateHash", common.ToHex(datas.StateHash))
}
if err == nil {
for i := 0; i < len(datas.Keys); i++ {
_, value, exit := tree.Get(datas.Keys[i])
if exit {
values[i] = value
}
}
}
return values
}
// MemSet set keys values to memcory mavl, return root hash and error
func (mavls *MavlStore) MemSet(datas *types.StoreSet, sync bool) ([]byte, error) {
beg := types.Now()
defer func() {
kmlog.Info("MemSet", "cost", types.Since(beg))
}()
if len(datas.KV) == 0 {
kmlog.Info("store mavl memset,use preStateHash as stateHash for kvset is null")
mavls.trees.Store(string(datas.StateHash), nil)
return datas.StateHash, nil
}
tree := mavl.NewTree(mavls.db, sync)
tree.SetBlockHeight(datas.Height)
err := tree.Load(datas.StateHash)
if err != nil {
return nil, err
}
for i := 0; i < len(datas.KV); i++ {
tree.Set(datas.KV[i].Key, datas.KV[i].Value)
}
hash := tree.Hash()
mavls.trees.Store(string(hash), tree)
return hash, nil
}
// Commit convert memcory mavl to storage db
func (mavls *MavlStore) Commit(req *types.ReqHash) ([]byte, error) {
beg := types.Now()
defer func() {
kmlog.Info("Commit", "cost", types.Since(beg))
}()
tree, ok := mavls.trees.Load(string(req.Hash))
if !ok {
kmlog.Error("store mavl commit", "err", types.ErrHashNotFound)
return nil, types.ErrHashNotFound
}
if tree == nil {
kmlog.Info("store mavl commit,do nothing for kvset is null")
mavls.trees.Delete(string(req.Hash))
return req.Hash, nil
}
hash := tree.(*mavl.Tree).Save()
if hash == nil {
kmlog.Error("store mavl commit", "err", types.ErrHashNotFound)
return nil, types.ErrDataBaseDamage
}
mavls.trees.Delete(string(req.Hash))
return req.Hash, nil
}
// Rollback 回退将缓存的mavl树删除掉
func (mavls *MavlStore) Rollback(req *types.ReqHash) ([]byte, error) {
beg := types.Now()
defer func() {
kmlog.Info("Rollback", "cost", types.Since(beg))
}()
_, ok := mavls.trees.Load(string(req.Hash))
if !ok {
kmlog.Error("store mavl rollback", "err", types.ErrHashNotFound)
return nil, types.ErrHashNotFound
}
mavls.trees.Delete(string(req.Hash))
return req.Hash, nil
}
// IterateRangeByStateHash 迭代实现功能; statehash:当前状态hash, start:开始查找的key, end: 结束的key, ascending:升序,降序, fn 迭代回调函数
func (mavls *MavlStore) IterateRangeByStateHash(statehash []byte, start []byte, end []byte, ascending bool, fn func(key, value []byte) bool) {
mavl.IterateRangeByStateHash(mavls.db, statehash, start, end, ascending, fn)
}
// ProcEvent not support message
func (mavls *MavlStore) ProcEvent(msg queue.Message) {
msg.ReplyErr("Store", types.ErrActionNotSupport)
}
// Del ...
func (mavls *MavlStore) Del(req *types.StoreDel) ([]byte, error) {
//not support
return nil, nil
}
......@@ -11,7 +11,7 @@ matrix:
- name: check_fmt
sudo: require
go:
- "1.9"
- "1.11.x"
- master
install:
- go get -u golang.org/x/tools/cmd/goimports
......
......@@ -472,3 +472,40 @@ func (acc *DB) mintReceipt(kv []*types.KeyValue, receipt proto.Message) *types.R
Logs: []*types.ReceiptLog{log1},
}
}
// Burn 然收
func (acc *DB) Burn(addr string, amount int64) (*types.Receipt, error) {
if !types.CheckAmount(amount) {
return nil, types.ErrAmount
}
accTo := acc.LoadAccount(addr)
if accTo.Balance < amount {
return nil, types.ErrNoBalance
}
copyAcc := *accTo
accTo.Balance = accTo.Balance - amount
receipt := &types.ReceiptAccountBurn{
Prev: &copyAcc,
Current: accTo,
}
kv := acc.GetKVSet(accTo)
acc.SaveKVSet(kv)
return acc.burnReceipt(kv, receipt), nil
}
func (acc *DB) burnReceipt(kv []*types.KeyValue, receipt proto.Message) *types.Receipt {
ty := int32(types.TyLogBurn)
log1 := &types.ReceiptLog{
Ty: ty,
Log: types.Encode(receipt),
}
return &types.Receipt{
Ty: types.ExecOk,
KV: kv,
Logs: []*types.ReceiptLog{log1},
}
}
......@@ -591,3 +591,13 @@ func TestDB_Mint(t *testing.T) {
t.Logf("Token mint addr balance [%d]", tokenCoin.LoadAccount(addr1).Balance)
require.Equal(t, int64(1000*1e8+10*1e8), tokenCoin.LoadAccount(addr1).Balance)
}
func TestDB_Burn(t *testing.T) {
_, tokenCoin := GenerAccDb()
tokenCoin.GenerAccData()
_, err := tokenCoin.Burn(addr1, 10*1e8)
require.NoError(t, err)
t.Logf("Token mint addr balance [%d]", tokenCoin.LoadAccount(addr1).Balance)
require.Equal(t, int64(1000*1e8-10*1e8), tokenCoin.LoadAccount(addr1).Balance)
}
......@@ -90,6 +90,10 @@ enableMVCC=false
enableMavlPrune=false
# 裁剪高度间隔
pruneHeight=10000
# 是否使能mavl数据载入内存
enableMemTree=false
# 是否使能mavl叶子节点数据载入内存
enableMemVal=false
[wallet]
# walletdb路径
......
......@@ -144,6 +144,10 @@ enableMavlPrefix=false
enableMVCC=false
enableMavlPrune=false
pruneHeight=10000
# 是否使能mavl数据载入内存
enableMemTree=false
# 是否使能mavl叶子节点数据载入内存
enableMemVal=false
[wallet]
minFee=1000000
......
......@@ -210,6 +210,10 @@ enableMVCC=false
enableMavlPrune=false
# 裁剪高度间隔
pruneHeight=10000
# 是否使能mavl数据载入内存
enableMemTree=false
# 是否使能mavl叶子节点数据载入内存
enableMemVal=false
[wallet]
# 交易发送最低手续费,单位0.00000001BTY(1e-8),默认100000,即0.001BTY
......
......@@ -203,11 +203,24 @@ func (e *executor) Exec(tx *types.Transaction, index int) (*types.Receipt, error
if err := drivers.CheckAddress(tx.GetRealToAddr(), e.height); err != nil {
return nil, err
}
if e.localDB != nil {
e.localDB.(*LocalDB).DisableWrite()
if exec.ExecutorOrder() != drivers.ExecLocalSameTime {
e.localDB.(*LocalDB).DisableRead()
}
defer func() {
e.localDB.(*LocalDB).EnableWrite()
if exec.ExecutorOrder() != drivers.ExecLocalSameTime {
e.localDB.(*LocalDB).EnableRead()
}
}()
}
//第一步先检查 CheckTx
if err := exec.CheckTx(tx, index); err != nil {
return nil, err
}
return exec.Exec(tx, index)
r, err := exec.Exec(tx, index)
return r, err
}
func (e *executor) execLocal(tx *types.Transaction, r *types.ReceiptData, index int) (*types.LocalDBSet, error) {
......
......@@ -312,7 +312,10 @@ func (demo *demoApp) Exec(tx *types.Transaction, index int) (receipt *types.Rece
}
if seterrkey {
println("set err key value")
demo.GetLocalDB().Set([]byte("key1"), []byte("value1"))
err = demo.GetLocalDB().Set([]byte("key1"), []byte("value1"))
if err != nil {
return nil, err
}
}
receipt = &types.Receipt{Ty: types.ExecOk}
receipt.KV = append(receipt.KV, &types.KeyValue{
......@@ -401,11 +404,14 @@ func TestExecLocalSameTime0(t *testing.T) {
return
}
for i, receipt := range detail.Receipts {
assert.Equal(t, receipt.GetTy(), int32(2), fmt.Sprint(i))
if i == 0 {
assert.Equal(t, receipt.GetTy(), int32(2), fmt.Sprint(i))
}
if i >= 1 {
assert.Equal(t, receipt.GetTy(), int32(1), fmt.Sprint(i))
fmt.Println(receipt)
assert.Equal(t, len(receipt.Logs), 2)
assert.Equal(t, receipt.Logs[1].Ty, int32(0))
assert.Equal(t, receipt.Logs[1].Ty, int32(1))
}
}
}
......
......@@ -5,6 +5,7 @@
package executor
import (
"fmt"
"testing"
"time"
......@@ -198,5 +199,6 @@ func TestExecutorErrAPIEnv(t *testing.T) {
msg := queue.NewMessage(0, "", 1, txlist)
exec.procExecTxList(msg)
_, err := exec.client.WaitTimeout(msg, 100*time.Second)
fmt.Println(err)
assert.Equal(t, true, api.IsAPIEnvError(err))
}
......@@ -12,15 +12,17 @@ import (
//数据的get set 主要经过 cache
//如果需要进行list, 那么把get set 的内容加入到 后端数据库
type LocalDB struct {
cache map[string][]byte
txcache map[string][]byte
keys []string
intx bool
hasbegin bool
kvs []*types.KeyValue
txid *types.Int64
client queue.Client
api client.QueueProtocolAPI
cache map[string][]byte
txcache map[string][]byte
keys []string
intx bool
hasbegin bool
kvs []*types.KeyValue
txid *types.Int64
client queue.Client
api client.QueueProtocolAPI
disableread bool
disablewrite bool
}
//NewLocalDB 创建一个新的LocalDB
......@@ -41,6 +43,26 @@ func NewLocalDB(cli queue.Client) db.KVDB {
}
}
//DisableRead 禁止读取LocalDB数据库
func (l *LocalDB) DisableRead() {
l.disableread = true
}
//DisableWrite 禁止写LocalDB数据库
func (l *LocalDB) DisableWrite() {
l.disablewrite = true
}
//EnableRead 启动读取LocalDB数据库
func (l *LocalDB) EnableRead() {
l.disableread = false
}
//EnableWrite 启动写LocalDB数据库
func (l *LocalDB) EnableWrite() {
l.disablewrite = false
}
func (l *LocalDB) resetTx() {
l.intx = false
l.txcache = nil
......@@ -128,6 +150,9 @@ func (l *LocalDB) Rollback() {
//Get 获取key
func (l *LocalDB) Get(key []byte) ([]byte, error) {
if l.disableread {
return nil, types.ErrDisableRead
}
skey := string(key)
if l.intx && l.txcache != nil {
if value, ok := l.txcache[skey]; ok {
......@@ -160,6 +185,9 @@ func (l *LocalDB) Get(key []byte) ([]byte, error) {
//Set 获取key
func (l *LocalDB) Set(key []byte, value []byte) error {
if l.disablewrite {
return types.ErrDisableWrite
}
skey := string(key)
if l.intx {
if l.txcache == nil {
......@@ -176,6 +204,9 @@ func (l *LocalDB) Set(key []byte, value []byte) error {
// List 从数据库中查询数据列表
func (l *LocalDB) List(prefix, key []byte, count, direction int32) ([][]byte, error) {
if l.disableread {
return nil, types.ErrDisableRead
}
err := l.save()
if err != nil {
return nil, err
......
......@@ -18,6 +18,34 @@ func TestLocalDBGet(t *testing.T) {
testDBGet(t, db)
}
func TestLocalDBEnable(t *testing.T) {
mock33 := testnode.New("", nil)
defer mock33.Close()
db := executor.NewLocalDB(mock33.GetClient())
ldb := db.(*executor.LocalDB)
defer ldb.Close()
_, err := ldb.Get([]byte("hello"))
assert.Equal(t, err, types.ErrNotFound)
ldb.DisableRead()
_, err = ldb.Get([]byte("hello"))
assert.Equal(t, err, types.ErrDisableRead)
_, err = ldb.List(nil, nil, 0, 0)
assert.Equal(t, err, types.ErrDisableRead)
ldb.EnableRead()
_, err = ldb.Get([]byte("hello"))
assert.Equal(t, err, types.ErrNotFound)
_, err = ldb.List(nil, nil, 0, 0)
assert.Equal(t, err, nil)
ldb.DisableWrite()
err = ldb.Set([]byte("hello"), nil)
assert.Equal(t, err, types.ErrDisableWrite)
ldb.EnableWrite()
err = ldb.Set([]byte("hello"), nil)
assert.Equal(t, err, nil)
}
func BenchmarkLocalDBGet(b *testing.B) {
mock33 := testnode.New("", nil)
defer mock33.Close()
......
......@@ -20,7 +20,7 @@ var (
}
logmap = map[int64]*types.LogInfo{
// 这里reflect.TypeOf类型必须是proto.Message类型,且是交易的回持结构
TyLogModifyConfig: {reflect.TypeOf(types.ReceiptConfig{}), "LogModifyConfig"},
TyLogModifyConfig: {Ty: reflect.TypeOf(types.ReceiptConfig{}), Name: "LogModifyConfig"},
}
)
......
// 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 mavl
import (
"fmt"
"time"
"github.com/33cn/chain33/common"
dbm "github.com/33cn/chain33/common/db"
farm "github.com/dgryski/go-farm"
"github.com/hashicorp/golang-lru"
)
// MemTreeOpera memtree操作接口
type MemTreeOpera interface {
Add(key, value interface{})
Get(key interface{}) (value interface{}, ok bool)
Delete(key interface{})
Contains(key interface{}) bool
Len() int
}
// TreeMap map形式memtree
type TreeMap struct {
mpCache map[interface{}]interface{}
}
// NewTreeMap new mem tree
func NewTreeMap(size int) *TreeMap {
mp := &TreeMap{}
mp.mpCache = make(map[interface{}]interface{}, size)
return mp
}
// Add 添加元素
func (tm *TreeMap) Add(key, value interface{}) {
if _, ok := tm.mpCache[key]; ok {
delete(tm.mpCache, key)
return
}
tm.mpCache[key] = value
}
// Get 获取元素
func (tm *TreeMap) Get(key interface{}) (value interface{}, ok bool) {
if value, ok := tm.mpCache[key]; ok {
return value, ok
}
return nil, false
}
// Delete 删除元素
func (tm *TreeMap) Delete(key interface{}) {
if _, ok := tm.mpCache[key]; ok {
delete(tm.mpCache, key)
}
}
// Contains 查看是否包含元素
func (tm *TreeMap) Contains(key interface{}) bool {
if _, ok := tm.mpCache[key]; ok {
return true
}
return false
}
// Len 元素长度
func (tm *TreeMap) Len() int {
return len(tm.mpCache)
}
// TreeARC lru的mem tree
type TreeARC struct {
arcCache *lru.ARCCache
}
// NewTreeARC new lru mem tree
func NewTreeARC(size int) *TreeARC {
ma := &TreeARC{}
ma.arcCache, _ = lru.NewARC(size)
return ma
}
// Add 添加元素
func (ta *TreeARC) Add(key, value interface{}) {
if ta.arcCache.Contains(key) {
ta.arcCache.Remove(key)
return
}
ta.arcCache.Add(key, value)
}
// Get 获取元素
func (ta *TreeARC) Get(key interface{}) (value interface{}, ok bool) {
return ta.arcCache.Get(key)
}
// Delete 删除元素
func (ta *TreeARC) Delete(key interface{}) {
ta.arcCache.Remove(key)
}
// Contains 查看是否包含元素
func (ta *TreeARC) Contains(key interface{}) bool {
return ta.arcCache.Contains(key)
}
// Len 元素长度
func (ta *TreeARC) Len() int {
return ta.arcCache.Len()
}
// LoadTree2MemDb 从数据库中载入mem tree
func LoadTree2MemDb(db dbm.DB, hash []byte, mp map[uint64]struct{}) {
nDb := newNodeDB(db, true)
node, err := nDb.getLightNode(nil, hash)
if err != nil {
fmt.Println("err", err)
return
}
pri := ""
if len(node.hash) > 32 {
pri = string(node.hash[:16])
}
treelog.Info("hash node", "hash pri", pri, "hash", common.ToHex(node.hash), "height", node.height)
start := time.Now()
leftHash := make([]byte, len(node.leftHash))
copy(leftHash, node.leftHash)
rightHash := make([]byte, len(node.rightHash))
copy(rightHash, node.rightHash)
mp[farm.Hash64(node.hash)] = struct{}{}
node.loadNodeInfo(nDb, mp)
end := time.Now()
treelog.Info("hash node", "cost time", end.Sub(start), "node count", len(mp))
PrintMemStats(1)
}
func (node *Node) loadNodeInfo(db *nodeDB, mp map[uint64]struct{}) {
if node.height == 0 {
//trMem.Add(Hash64(node.hash), &hashNode{leftHash: node.leftHash, rightHash: node.rightHash})
leftHash := make([]byte, len(node.leftHash))
copy(leftHash, node.leftHash)
rightHash := make([]byte, len(node.rightHash))
copy(rightHash, node.rightHash)
mp[farm.Hash64(node.hash)] = struct{}{}
return
}
if node.leftHash != nil {
left, err := db.getLightNode(nil, node.leftHash)
if err != nil {
return
}
//trMem.Add(Hash64(left.hash), &hashNode{leftHash: left.leftHash, rightHash: left.rightHash})
leftHash := make([]byte, len(left.leftHash))
copy(leftHash, left.leftHash)
rightHash := make([]byte, len(left.rightHash))
copy(rightHash, left.rightHash)
mp[farm.Hash64(left.hash)] = struct{}{}
left.loadNodeInfo(db, mp)
}
if node.rightHash != nil {
right, err := db.getLightNode(nil, node.rightHash)
if err != nil {
return
}
//trMem.Add(Hash64(right.hash), &hashNode{leftHash: right.leftHash, rightHash: right.rightHash})
leftHash := make([]byte, len(right.leftHash))
copy(leftHash, right.leftHash)
rightHash := make([]byte, len(right.rightHash))
copy(rightHash, right.rightHash)
mp[farm.Hash64(right.hash)] = struct{}{}
right.loadNodeInfo(db, mp)
}
}
func (ndb *nodeDB) getLightNode(t *Tree, hash []byte) (*Node, error) {
// Doesn't exist, load from db.
var buf []byte
buf, err := ndb.db.Get(hash)
if len(buf) == 0 || err != nil {
return nil, ErrNodeNotExist
}
node, err := MakeNode(buf, t)
if err != nil {
panic(fmt.Sprintf("Error reading IAVLNode. bytes: %X error: %v", buf, err))
}
node.hash = hash
node.key = nil
node.value = nil
return node, nil
}
func copyBytes(b []byte) (copiedBytes []byte) {
if b == nil {
return nil
}
copiedBytes = make([]byte, len(b))
copy(copiedBytes, b)
return copiedBytes
}
......@@ -11,6 +11,7 @@ import (
"github.com/33cn/chain33/common"
"github.com/33cn/chain33/types"
farm "github.com/dgryski/go-farm"
"github.com/golang/protobuf/proto"
)
......@@ -499,6 +500,9 @@ func removeOrphan(t *Tree, node *Node) {
if t.ndb == nil {
return
}
if enableMemTree && t != nil {
t.obsoleteNode[uintkey(farm.Hash64(node.hash))] = struct{}{}
}
t.ndb.RemoveNode(t, node)
}
......
......@@ -15,6 +15,7 @@ import (
dbm "github.com/33cn/chain33/common/db"
log "github.com/33cn/chain33/common/log/log15"
"github.com/33cn/chain33/types"
farm "github.com/dgryski/go-farm"
"github.com/golang/protobuf/proto"
"github.com/hashicorp/golang-lru"
)
......@@ -37,6 +38,10 @@ var (
// 当前树的最大高度
maxBlockHeight int64
heightMtx sync.Mutex
//
enableMemTree bool
memTree MemTreeOpera
enableMemVal bool
)
// EnableMavlPrefix 使能mavl加前缀
......@@ -49,23 +54,53 @@ func EnableMVCC(enable bool) {
enableMvcc = enable
}
// EnableMemTree 使能mavl数据载入内存
func EnableMemTree(enable bool) {
enableMemTree = enable
}
// EnableMemVal 使能mavl叶子节点数据载入内存
func EnableMemVal(enable bool) {
enableMemVal = enable
}
type memNode struct {
data [][]byte //顺序为lefthash, righthash, key, value
Height int32
Size int32
}
type uintkey uint64
// Tree merkle avl tree
type Tree struct {
root *Node
ndb *nodeDB
blockHeight int64
// 树更新之后,废弃的节点(更新缓存中节点先对旧节点进行删除然后再更新)
obsoleteNode map[uintkey]struct{}
updateNode map[uintkey]*memNode
}
// NewTree 新建一个merkle avl 树
func NewTree(db dbm.DB, sync bool) *Tree {
if db == nil {
// In-memory IAVLTree
return &Tree{}
return &Tree{
obsoleteNode: make(map[uintkey]struct{}),
updateNode: make(map[uintkey]*memNode),
}
}
// Persistent IAVLTree
ndb := newNodeDB(db, sync)
// 使能情况下非空创建当前整tree的缓存
if enableMemTree && memTree == nil {
memTree = NewTreeMap(50 * 10000)
}
return &Tree{
ndb: ndb,
ndb: ndb,
obsoleteNode: make(map[uintkey]struct{}),
updateNode: make(map[uintkey]*memNode),
}
}
......@@ -116,13 +151,17 @@ func (t *Tree) Has(key []byte) bool {
func (t *Tree) Set(key []byte, value []byte) (updated bool) {
//treelog.Info("IAVLTree.Set", "key", key, "value",value)
if t.root == nil {
t.root = NewNode(key, value)
t.root = NewNode(copyBytes(key), copyBytes(value))
return false
}
t.root, updated = t.root.set(t, key, value)
t.root, updated = t.root.set(t, copyBytes(key), copyBytes(value))
return updated
}
func (t *Tree) getObsoleteNode() map[uintkey]struct{} {
return t.obsoleteNode
}
// Hash 计算tree 的roothash
func (t *Tree) Hash() []byte {
if t.root == nil {
......@@ -148,6 +187,17 @@ func (t *Tree) Save() []byte {
if enablePrune {
t.root.saveRootHash(t)
}
// 更新memTree
if enableMemTree && memTree != nil {
for k := range t.obsoleteNode {
memTree.Delete(k)
}
for k, v := range t.updateNode {
memTree.Add(k, v)
}
treelog.Debug("Tree.Save", "memTree len", memTree.Len(), "tree height", t.blockHeight)
}
beg := types.Now()
err := t.ndb.Commit()
treelog.Info("tree.commit", "cost", types.Since(beg))
......@@ -395,6 +445,13 @@ func (ndb *nodeDB) GetNode(t *Tree, hash []byte) (*Node, error) {
return elem.(*Node), nil
}
}
//从memtree中获取
if enableMemTree {
node, err := getNode4MemTree(hash)
if err == nil {
return node, nil
}
}
// Doesn't exist, load from db.
var buf []byte
buf, err := ndb.db.Get(hash)
......@@ -409,6 +466,10 @@ func (ndb *nodeDB) GetNode(t *Tree, hash []byte) (*Node, error) {
node.hash = hash
node.persisted = true
ndb.cacheNode(node)
// Save node hashInt64 to memTree
if enableMemTree {
updateGlobalMemTree(node)
}
return node, nil
}
......@@ -459,6 +520,85 @@ func (ndb *nodeDB) SaveNode(t *Tree, node *Node) {
ndb.cacheNode(node)
delete(ndb.orphans, string(node.hash))
//treelog.Debug("SaveNode", "hash", node.hash, "height", node.height, "value", node.value)
// Save node hashInt64 to localmem
if enableMemTree {
updateLocalMemTree(t, node)
}
}
func getNode4MemTree(hash []byte) (*Node, error) {
if memTree == nil {
return nil, ErrNodeNotExist
}
elem, ok := memTree.Get(uintkey(farm.Hash64(hash)))
if ok {
sn := elem.(*memNode)
node := &Node{
height: sn.Height,
size: sn.Size,
hash: hash,
persisted: true,
}
node.leftHash = sn.data[0]
node.rightHash = sn.data[1]
node.key = sn.data[2]
if len(sn.data) == 4 {
node.value = sn.data[3]
}
return node, nil
}
return nil, ErrNodeNotExist
}
// Save node hashInt64 to memTree
func updateGlobalMemTree(node *Node) {
if node == nil || memTree == nil {
return
}
if !enableMemVal && node.height == 0 {
return
}
memN := &memNode{
Height: node.height,
Size: node.size,
}
if node.height == 0 {
memN.data = make([][]byte, 4)
memN.data[3] = node.value
} else {
memN.data = make([][]byte, 3)
}
memN.data[0] = node.leftHash
memN.data[1] = node.rightHash
memN.data[2] = node.key
memTree.Add(uintkey(farm.Hash64(node.hash)), memN)
}
// Save node hashInt64 to localmem
func updateLocalMemTree(t *Tree, node *Node) {
if t == nil || node == nil {
return
}
if !enableMemVal && node.height == 0 {
return
}
if t.updateNode != nil {
memN := &memNode{
Height: node.height,
Size: node.size,
}
if node.height == 0 {
memN.data = make([][]byte, 4)
memN.data[3] = node.value
} else {
memN.data = make([][]byte, 3)
}
memN.data[0] = node.leftHash
memN.data[1] = node.rightHash
memN.data[2] = node.key
t.updateNode[uintkey(farm.Hash64(node.hash))] = memN
//treelog.Debug("Tree.SaveNode", "store struct size", unsafe.Sizeof(store), "byte size", len(storenode), "height", node.height)
}
}
//cache缓存节点
......
......@@ -15,6 +15,8 @@ import (
"sync"
"testing"
"unsafe"
. "github.com/33cn/chain33/common"
"github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/common/log"
......@@ -1255,6 +1257,89 @@ func TestDelLeafCountKV(t *testing.T) {
}
}
func TestGetObsoleteNode(t *testing.T) {
dir, err := ioutil.TempDir("", "datastore")
require.NoError(t, err)
t.Log(dir)
defer os.Remove(dir)
EnableMemTree(true)
EnableMemVal(true)
defer EnableMemTree(false)
defer EnableMemVal(false)
db := db.NewDB("mavltree", "leveldb", dir, 100)
tree := NewTree(db, true)
type record struct {
key string
value string
}
records := []record{
{"abc", "abc"},
{"low", "low"},
{"fan", "fan"},
}
for _, r := range records {
updated := tree.Set([]byte(r.key), []byte(r.value))
if updated {
t.Error("should have not been updated")
}
}
hash := tree.Save()
obs := tree.getObsoleteNode()
require.Equal(t, 0, len(obs))
mp := make(map[uint64]struct{})
LoadTree2MemDb(db, hash, mp)
tree1 := NewTree(db, true)
tree1.Load(hash)
records1 := []record{
{"abc", "abc1"},
{"low", "low1"},
{"fan", "fan1"},
}
for _, r := range records1 {
tree1.Set([]byte(r.key), []byte(r.value))
}
hash1 := tree1.Save()
obs = tree1.getObsoleteNode()
mp1 := make(map[uint64]struct{})
LoadTree2MemDb(db, hash1, mp1)
require.Equal(t, len(mp), len(obs)) //做了全部更新,因此旧节点全部删除
for ob := range obs {
_, ok := mp[uint64(ob)]
if !ok {
require.Error(t, fmt.Errorf("should exist"))
}
}
tree2 := NewTree(db, true)
tree2.Load(hash)
records2 := []record{
{"fan", "fan1"},
{"foo", "foo"},
{"foobaz", "foobaz"},
{"good", "good"},
}
for _, r := range records2 {
tree2.Set([]byte(r.key), []byte(r.value))
}
hash2 := tree2.Save()
obs = tree2.getObsoleteNode()
mp2 := make(map[uint64]struct{})
LoadTree2MemDb(db, hash2, mp2)
//require.Equal(t, 0, len(obs))
for ob := range obs {
_, ok := mp[uint64(ob)]
if !ok {
require.Error(t, fmt.Errorf("should exist"))
}
}
}
func TestPruningFirstLevelNode(t *testing.T) {
dir, err := ioutil.TempDir("", "datastore")
require.NoError(t, err)
......@@ -1899,3 +1984,64 @@ func saveBlock(dbm db.DB, height int64, hash []byte, txN int64, mvcc bool) (newH
}
return newHash, nil
}
func TestSize1(t *testing.T) {
type storeNode struct {
Key []byte
Value []byte
LeftHash []byte
RightHash []byte
Height int32
Size int32
}
type storeNode1 struct {
Key [][]byte
Height int32
Size int32
}
a := types.StoreNode{}
b := storeNode{}
var c []byte
d := storeNode1{}
//arcmp := NewTreeMap(350 * 10000)
//if arcmp == nil {
// return
//}
//for i := 0; i < 300*10000; i++ {
// data := &storeNode{
// Key: []byte("12345678901234567890123456789012"),
// Value: []byte("12345678901234567890123456789012"),
// LeftHash: []byte("12345678901234567890123456789012"),
// RightHash: []byte("12345678901234567890123456789012"),
// //Key: copyBytes([]byte("12345678901234567890123456789012")),
// //Value: copyBytes([]byte("12345678901234567890123456789012")),
// //LeftHash: copyBytes([]byte("12345678901234567890123456789012")),
// //RightHash: copyBytes([]byte("12345678901234567890123456789012")),
// Height: 123,
// Size: 123,
// }
// arcmp.Add(int64(i), data)
//}
//for i := 0; i < 100*10000; i++ {
// data := &storeNode1{}
// data.Height = 123
// data.Size = 123
// d.Key = make([][]byte, 4)
// //d.Key[0] = []byte("12345678901234567890123456789012")
// //d.Key[1] = []byte("12345678901234567890123456789012")
// //d.Key[2] = []byte("12345678901234567890123456789012")
// //d.Key[3] = []byte("12345678901234567890123456789012")
//
// d.Key[0] = copyBytes([]byte("12345678901234567890123456789012"))
// d.Key[1] = copyBytes([]byte("12345678901234567890123456789012"))
// d.Key[2] = copyBytes([]byte("12345678901234567890123456789012"))
// d.Key[3] = copyBytes([]byte("12345678901234567890123456789012"))
// arcmp.Add(int64(i), data)
//}
PrintMemStats(1)
fmt.Println(unsafe.Sizeof(a), unsafe.Sizeof(b), unsafe.Sizeof(c), unsafe.Sizeof(d), len(d.Key), cap(d.Key))
}
......@@ -37,6 +37,8 @@ type Store struct {
enableMVCC bool
enableMavlPrune bool
pruneHeight int32
enableMemTree bool
enableMemVal bool
}
func init() {
......@@ -51,6 +53,10 @@ type subConfig struct {
EnableMavlPrune bool `json:"enableMavlPrune"`
// 裁剪高度间隔
PruneHeight int32 `json:"pruneHeight"`
// 是否使能内存树
EnableMemTree bool `json:"enableMemTree"`
// 是否使能内存树中叶子节点
EnableMemVal bool `json:"enableMemVal"`
}
// New new mavl store module
......@@ -60,15 +66,20 @@ func New(cfg *types.Store, sub []byte) queue.Module {
if sub != nil {
types.MustDecode(sub, &subcfg)
}
mavls := &Store{bs, &sync.Map{}, subcfg.EnableMavlPrefix, subcfg.EnableMVCC, subcfg.EnableMavlPrune, subcfg.PruneHeight}
mavls := &Store{bs, &sync.Map{}, subcfg.EnableMavlPrefix, subcfg.EnableMVCC,
subcfg.EnableMavlPrune, subcfg.PruneHeight, subcfg.EnableMemTree, subcfg.EnableMemVal}
mavls.enableMavlPrefix = subcfg.EnableMavlPrefix
mavls.enableMVCC = subcfg.EnableMVCC
mavls.enableMavlPrune = subcfg.EnableMavlPrune
mavls.pruneHeight = subcfg.PruneHeight
mavls.enableMemTree = subcfg.EnableMemTree
mavls.enableMemVal = subcfg.EnableMemVal
mavl.EnableMavlPrefix(mavls.enableMavlPrefix)
mavl.EnableMVCC(mavls.enableMVCC)
mavl.EnablePrune(mavls.enableMavlPrune)
mavl.SetPruneHeight(int(mavls.pruneHeight))
mavl.EnableMemTree(mavls.enableMemTree)
mavl.EnableMemVal(mavls.enableMemVal)
bs.SetChild(mavls)
return mavls
}
......
......@@ -248,6 +248,53 @@ func (m *ReceiptAccountMint) GetCurrent() *Account {
return nil
}
type ReceiptAccountBurn struct {
Prev *Account `protobuf:"bytes,1,opt,name=prev,proto3" json:"prev,omitempty"`
Current *Account `protobuf:"bytes,2,opt,name=current,proto3" json:"current,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ReceiptAccountBurn) Reset() { *m = ReceiptAccountBurn{} }
func (m *ReceiptAccountBurn) String() string { return proto.CompactTextString(m) }
func (*ReceiptAccountBurn) ProtoMessage() {}
func (*ReceiptAccountBurn) Descriptor() ([]byte, []int) {
return fileDescriptor_8e28828dcb8d24f0, []int{4}
}
func (m *ReceiptAccountBurn) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ReceiptAccountBurn.Unmarshal(m, b)
}
func (m *ReceiptAccountBurn) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ReceiptAccountBurn.Marshal(b, m, deterministic)
}
func (m *ReceiptAccountBurn) XXX_Merge(src proto.Message) {
xxx_messageInfo_ReceiptAccountBurn.Merge(m, src)
}
func (m *ReceiptAccountBurn) XXX_Size() int {
return xxx_messageInfo_ReceiptAccountBurn.Size(m)
}
func (m *ReceiptAccountBurn) XXX_DiscardUnknown() {
xxx_messageInfo_ReceiptAccountBurn.DiscardUnknown(m)
}
var xxx_messageInfo_ReceiptAccountBurn proto.InternalMessageInfo
func (m *ReceiptAccountBurn) GetPrev() *Account {
if m != nil {
return m.Prev
}
return nil
}
func (m *ReceiptAccountBurn) GetCurrent() *Account {
if m != nil {
return m.Current
}
return nil
}
//查询一个地址列表在某个执行器中余额
type ReqBalance struct {
//地址列表
......@@ -266,7 +313,7 @@ func (m *ReqBalance) Reset() { *m = ReqBalance{} }
func (m *ReqBalance) String() string { return proto.CompactTextString(m) }
func (*ReqBalance) ProtoMessage() {}
func (*ReqBalance) Descriptor() ([]byte, []int) {
return fileDescriptor_8e28828dcb8d24f0, []int{4}
return fileDescriptor_8e28828dcb8d24f0, []int{5}
}
func (m *ReqBalance) XXX_Unmarshal(b []byte) error {
......@@ -334,7 +381,7 @@ func (m *Accounts) Reset() { *m = Accounts{} }
func (m *Accounts) String() string { return proto.CompactTextString(m) }
func (*Accounts) ProtoMessage() {}
func (*Accounts) Descriptor() ([]byte, []int) {
return fileDescriptor_8e28828dcb8d24f0, []int{5}
return fileDescriptor_8e28828dcb8d24f0, []int{6}
}
func (m *Accounts) XXX_Unmarshal(b []byte) error {
......@@ -374,7 +421,7 @@ func (m *ExecAccount) Reset() { *m = ExecAccount{} }
func (m *ExecAccount) String() string { return proto.CompactTextString(m) }
func (*ExecAccount) ProtoMessage() {}
func (*ExecAccount) Descriptor() ([]byte, []int) {
return fileDescriptor_8e28828dcb8d24f0, []int{6}
return fileDescriptor_8e28828dcb8d24f0, []int{7}
}
func (m *ExecAccount) XXX_Unmarshal(b []byte) error {
......@@ -421,7 +468,7 @@ func (m *AllExecBalance) Reset() { *m = AllExecBalance{} }
func (m *AllExecBalance) String() string { return proto.CompactTextString(m) }
func (*AllExecBalance) ProtoMessage() {}
func (*AllExecBalance) Descriptor() ([]byte, []int) {
return fileDescriptor_8e28828dcb8d24f0, []int{7}
return fileDescriptor_8e28828dcb8d24f0, []int{8}
}
func (m *AllExecBalance) XXX_Unmarshal(b []byte) error {
......@@ -473,7 +520,7 @@ func (m *ReqAllExecBalance) Reset() { *m = ReqAllExecBalance{} }
func (m *ReqAllExecBalance) String() string { return proto.CompactTextString(m) }
func (*ReqAllExecBalance) ProtoMessage() {}
func (*ReqAllExecBalance) Descriptor() ([]byte, []int) {
return fileDescriptor_8e28828dcb8d24f0, []int{8}
return fileDescriptor_8e28828dcb8d24f0, []int{9}
}
func (m *ReqAllExecBalance) XXX_Unmarshal(b []byte) error {
......@@ -534,6 +581,7 @@ func init() {
proto.RegisterType((*ReceiptExecAccountTransfer)(nil), "types.ReceiptExecAccountTransfer")
proto.RegisterType((*ReceiptAccountTransfer)(nil), "types.ReceiptAccountTransfer")
proto.RegisterType((*ReceiptAccountMint)(nil), "types.ReceiptAccountMint")
proto.RegisterType((*ReceiptAccountBurn)(nil), "types.ReceiptAccountBurn")
proto.RegisterType((*ReqBalance)(nil), "types.ReqBalance")
proto.RegisterType((*Accounts)(nil), "types.Accounts")
proto.RegisterType((*ExecAccount)(nil), "types.ExecAccount")
......@@ -544,32 +592,33 @@ func init() {
func init() { proto.RegisterFile("account.proto", fileDescriptor_8e28828dcb8d24f0) }
var fileDescriptor_8e28828dcb8d24f0 = []byte{
// 427 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x54, 0xcd, 0x6a, 0x14, 0x41,
0x10, 0xa6, 0xf7, 0x27, 0x9b, 0xa9, 0xd5, 0x80, 0x7d, 0x08, 0x4d, 0x30, 0x38, 0xf6, 0x69, 0x0e,
0xb2, 0x0b, 0x8e, 0x2f, 0x90, 0x80, 0xe0, 0x45, 0x84, 0xd6, 0x53, 0x2e, 0xd2, 0xd3, 0xa9, 0x75,
0x07, 0x27, 0x3d, 0x93, 0xee, 0x5e, 0x71, 0x7d, 0x00, 0x5f, 0x43, 0xf0, 0x49, 0xa5, 0x6b, 0x7b,
0xb2, 0xb3, 0x06, 0x25, 0x07, 0xc1, 0xdb, 0xd4, 0xf7, 0x55, 0xd5, 0xf7, 0x75, 0x55, 0x31, 0xf0,
0x58, 0x1b, 0xd3, 0x6e, 0x6c, 0x58, 0x74, 0xae, 0x0d, 0x2d, 0x9f, 0x86, 0x6d, 0x87, 0x5e, 0x7e,
0x86, 0xd9, 0xc5, 0x0e, 0xe7, 0x67, 0x70, 0x6c, 0x36, 0xce, 0xa1, 0x35, 0x5b, 0xc1, 0x72, 0x56,
0x4c, 0xd5, 0x5d, 0xcc, 0x05, 0xcc, 0x2a, 0xdd, 0x68, 0x6b, 0x50, 0x8c, 0x72, 0x56, 0x8c, 0x55,
0x1f, 0xf2, 0x53, 0x38, 0x5a, 0xb9, 0xf6, 0x1b, 0x5a, 0x31, 0x26, 0x22, 0x45, 0x9c, 0xc3, 0x44,
0x5f, 0x5f, 0x3b, 0x31, 0xc9, 0x59, 0x91, 0x29, 0xfa, 0x96, 0xdf, 0x19, 0x9c, 0x29, 0x34, 0x58,
0x77, 0xe1, 0xf5, 0x57, 0x34, 0x49, 0xf8, 0x83, 0xd3, 0xd6, 0xaf, 0xd0, 0x45, 0x03, 0x18, 0xe1,
0x58, 0xc6, 0xa8, 0xec, 0x2e, 0xe6, 0x12, 0x26, 0x9d, 0xc3, 0x2f, 0xa4, 0x3e, 0x7f, 0x79, 0xb2,
0x20, 0xf7, 0x8b, 0xd4, 0x41, 0x11, 0xc7, 0x0b, 0x98, 0xed, 0x0c, 0x07, 0xf2, 0x72, 0x3f, 0xad,
0xa7, 0xe5, 0x0a, 0x4e, 0x93, 0x8f, 0xdf, 0x3d, 0xf4, 0x3a, 0xec, 0x61, 0x3a, 0xa3, 0xbf, 0xeb,
0x54, 0xc0, 0x0f, 0x75, 0xde, 0xd6, 0x36, 0xfc, 0x63, 0x8d, 0x9f, 0x0c, 0x40, 0xe1, 0xed, 0x65,
0xda, 0xc7, 0x53, 0xc8, 0xe2, 0xac, 0xd1, 0x7b, 0xf4, 0x82, 0xe5, 0xe3, 0x22, 0x53, 0x7b, 0x20,
0x6e, 0x2b, 0x8e, 0x14, 0x1d, 0x75, 0xcd, 0x54, 0x8a, 0x62, 0x95, 0x0f, 0x3a, 0xe0, 0x1b, 0xed,
0xd7, 0x34, 0xbc, 0x4c, 0xed, 0x01, 0x7e, 0x0e, 0xa0, 0xbd, 0xc7, 0xf0, 0x31, 0x66, 0xa7, 0x8d,
0x66, 0x84, 0xc4, 0x35, 0xf2, 0xe7, 0xf0, 0x68, 0x47, 0xfb, 0xed, 0x4d, 0xd5, 0x36, 0x62, 0x4a,
0x09, 0x73, 0xc2, 0xde, 0x13, 0x24, 0x5f, 0xc0, 0x71, 0x32, 0xee, 0x79, 0x0e, 0x63, 0x6d, 0x0c,
0x79, 0xbb, 0xff, 0xac, 0x48, 0xc9, 0x77, 0x30, 0x1f, 0xdc, 0xc7, 0xc0, 0x34, 0x3b, 0x30, 0x5d,
0xc0, 0x2c, 0xdd, 0xf4, 0x9f, 0x66, 0x94, 0x68, 0x79, 0x05, 0x27, 0x17, 0x4d, 0x13, 0x7b, 0xf6,
0x63, 0xea, 0xcf, 0x93, 0xed, 0xcf, 0x93, 0xbf, 0x3a, 0x90, 0x15, 0x23, 0x32, 0xc8, 0x53, 0xcf,
0x01, 0xa3, 0x86, 0x69, 0xf2, 0x07, 0x83, 0x27, 0x0a, 0x6f, 0x1f, 0xd0, 0xff, 0x3f, 0x0d, 0xff,
0xf2, 0xd9, 0xd5, 0xf9, 0xa7, 0x3a, 0xac, 0x37, 0xd5, 0xc2, 0xb4, 0x37, 0xcb, 0xb2, 0x34, 0x76,
0x69, 0xd6, 0xba, 0xb6, 0x65, 0xb9, 0xa4, 0xb7, 0x55, 0x47, 0xf4, 0x4b, 0x28, 0x7f, 0x05, 0x00,
0x00, 0xff, 0xff, 0x1b, 0xce, 0xc2, 0x14, 0x23, 0x04, 0x00, 0x00,
// 434 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x54, 0xcd, 0x6e, 0x13, 0x31,
0x10, 0x96, 0xf3, 0xd3, 0x74, 0x27, 0x50, 0x09, 0x1f, 0x2a, 0xab, 0xa2, 0x62, 0xf1, 0x69, 0x0f,
0x28, 0x91, 0x58, 0x5e, 0xa0, 0x91, 0x90, 0xb8, 0x20, 0x24, 0xc3, 0xa9, 0x17, 0xe4, 0x75, 0x27,
0x24, 0x22, 0xf5, 0x6e, 0x6d, 0x2f, 0x22, 0x3c, 0x00, 0xaf, 0x81, 0xc4, 0x93, 0x22, 0x4f, 0xbc,
0xcd, 0x86, 0x0a, 0xd4, 0x03, 0xa8, 0xb7, 0x9d, 0xef, 0x1b, 0xcf, 0xf7, 0xd9, 0x33, 0xb3, 0xf0,
0x58, 0x1b, 0x53, 0xb7, 0x36, 0xcc, 0x1a, 0x57, 0x87, 0x9a, 0x8f, 0xc3, 0xb6, 0x41, 0x2f, 0x3f,
0xc3, 0xe4, 0x62, 0x87, 0xf3, 0x33, 0x38, 0x36, 0xad, 0x73, 0x68, 0xcd, 0x56, 0xb0, 0x9c, 0x15,
0x63, 0x75, 0x1b, 0x73, 0x01, 0x93, 0x4a, 0x6f, 0xb4, 0x35, 0x28, 0x06, 0x39, 0x2b, 0x86, 0xaa,
0x0b, 0xf9, 0x29, 0x1c, 0x2d, 0x5d, 0xfd, 0x0d, 0xad, 0x18, 0x12, 0x91, 0x22, 0xce, 0x61, 0xa4,
0xaf, 0xae, 0x9c, 0x18, 0xe5, 0xac, 0xc8, 0x14, 0x7d, 0xcb, 0xef, 0x0c, 0xce, 0x14, 0x1a, 0x5c,
0x37, 0xe1, 0xf5, 0x57, 0x34, 0x49, 0xf8, 0x83, 0xd3, 0xd6, 0x2f, 0xd1, 0x45, 0x03, 0x18, 0xe1,
0x78, 0x8c, 0xd1, 0xb1, 0xdb, 0x98, 0x4b, 0x18, 0x35, 0x0e, 0xbf, 0x90, 0xfa, 0xf4, 0xe5, 0xc9,
0x8c, 0xdc, 0xcf, 0x52, 0x05, 0x45, 0x1c, 0x2f, 0x60, 0xb2, 0x33, 0x1c, 0xc8, 0xcb, 0xdd, 0xb4,
0x8e, 0x96, 0x4b, 0x38, 0x4d, 0x3e, 0x7e, 0xf7, 0xd0, 0xe9, 0xb0, 0xfb, 0xe9, 0x0c, 0xfe, 0xae,
0x53, 0x01, 0x3f, 0xd4, 0x79, 0xbb, 0xb6, 0xe1, 0x7f, 0x6b, 0x2c, 0x5a, 0x67, 0xff, 0xb1, 0xc6,
0x4f, 0x06, 0xa0, 0xf0, 0x66, 0x91, 0x7a, 0xfe, 0x14, 0xb2, 0xd8, 0x4f, 0xf4, 0x1e, 0xbd, 0x60,
0xf9, 0xb0, 0xc8, 0xd4, 0x1e, 0x88, 0x13, 0x11, 0xdb, 0x86, 0x8e, 0xaa, 0x66, 0x2a, 0x45, 0xf1,
0x94, 0x0f, 0x3a, 0xe0, 0x1b, 0xed, 0x57, 0xd4, 0xa0, 0x4c, 0xed, 0x01, 0x7e, 0x0e, 0xa0, 0xbd,
0xc7, 0xf0, 0x31, 0x66, 0xa7, 0xa9, 0xc9, 0x08, 0x89, 0xa3, 0xc2, 0x9f, 0xc3, 0xa3, 0x1d, 0xed,
0xb7, 0xd7, 0x55, 0xbd, 0x11, 0x63, 0x4a, 0x98, 0x12, 0xf6, 0x9e, 0x20, 0xf9, 0x02, 0x8e, 0x93,
0x71, 0xcf, 0x73, 0x18, 0x6a, 0x63, 0xc8, 0xdb, 0xdd, 0x6b, 0x45, 0x4a, 0xbe, 0x83, 0x69, 0x6f,
0x06, 0x7b, 0xa6, 0xd9, 0x81, 0xe9, 0x02, 0x26, 0x69, 0x6f, 0xfe, 0xf4, 0x46, 0x89, 0x96, 0x97,
0x70, 0x72, 0xb1, 0xd9, 0xc4, 0x9a, 0xdd, 0x33, 0x75, 0x2b, 0xc0, 0xf6, 0x2b, 0xc0, 0x5f, 0x1d,
0xc8, 0x8a, 0x01, 0x19, 0xe4, 0xa9, 0x66, 0x8f, 0x51, 0xfd, 0x34, 0xf9, 0x83, 0xc1, 0x13, 0x85,
0x37, 0xf7, 0xa8, 0xff, 0x40, 0x8f, 0xbf, 0x78, 0x76, 0x79, 0xfe, 0x69, 0x1d, 0x56, 0x6d, 0x35,
0x33, 0xf5, 0xf5, 0xbc, 0x2c, 0x8d, 0x9d, 0x9b, 0x95, 0x5e, 0xdb, 0xb2, 0x9c, 0xd3, 0xdd, 0xaa,
0x23, 0xfa, 0xed, 0x94, 0xbf, 0x02, 0x00, 0x00, 0xff, 0xff, 0x57, 0xdf, 0x86, 0xd3, 0x87, 0x04,
0x00, 0x00,
}
......@@ -104,6 +104,7 @@ const (
TyLogGenesisDeposit = 12
TyLogRollback = 13
TyLogMint = 14
TyLogBurn = 15
)
//SystemLog 系统log日志
......@@ -121,6 +122,8 @@ var SystemLog = map[int64]*LogInfo{
TyLogGenesisTransfer: {reflect.TypeOf(ReceiptAccountTransfer{}), "LogGenesisTransfer"},
TyLogGenesisDeposit: {reflect.TypeOf(ReceiptAccountTransfer{}), "LogGenesisDeposit"},
TyLogRollback: {reflect.TypeOf(LocalDBSet{}), "LogRollback"},
TyLogMint: {reflect.TypeOf(ReceiptAccountMint{}), "LogMint"},
TyLogBurn: {reflect.TypeOf(ReceiptAccountBurn{}), "LogBurn"},
}
//exec type
......
......@@ -181,4 +181,7 @@ var (
ErrHeightOverflow = errors.New("ErrHeightOverflow")
ErrRecordBlockSequence = errors.New("ErrRecordBlockSequence")
ErrExecPanic = errors.New("ErrExecPanic")
ErrDisableWrite = errors.New("ErrDisableWrite")
ErrDisableRead = errors.New("ErrDisableRead")
)
......@@ -41,6 +41,11 @@ message ReceiptAccountMint {
Account current = 2;
}
message ReceiptAccountBurn {
Account prev = 1;
Account current = 2;
}
//查询一个地址列表在某个执行器中余额
message ReqBalance {
//地址列表
......
......@@ -205,25 +205,17 @@ func ReportErrEventToFront(logger log.Logger, client queue.Client, frommodule st
//DelDupKey 删除重复的key
func DelDupKey(kvs []*types.KeyValue) []*types.KeyValue {
dupindex := make(map[string]int)
hasdup := false
for i, kv := range kvs {
n := 0
for _, kv := range kvs {
skey := string(kv.Key)
if index, ok := dupindex[skey]; ok {
hasdup = true
kvs[index] = nil
}
dupindex[skey] = i
}
//没有重复的情况下,不需要重新处理
if !hasdup {
return kvs
}
index := 0
for _, kv := range kvs {
if kv != nil {
//重复的key 替换老的key
kvs[index] = kv
index++
} else {
dupindex[skey] = n
kvs[n] = kv
n++
}
}
return kvs[0:index]
return kvs[0:n]
}
......@@ -225,8 +225,9 @@ func ExecBlock(client queue.Client, prevStateRoot []byte, block *types.Block, er
//通过consensus module 再次检查
ulog.Debug("ExecBlock", "height------->", block.Height, "ntx", len(block.Txs))
beg := types.Now()
beg2 := beg
defer func() {
ulog.Info("ExecBlock", "height", block.Height, "ntx", len(block.Txs), "writebatchsync", sync, "cost", types.Since(beg))
ulog.Info("ExecBlock", "height", block.Height, "ntx", len(block.Txs), "writebatchsync", sync, "cost", types.Since(beg2))
}()
if errReturn && block.Height > 0 && !block.CheckSign() {
......@@ -330,7 +331,6 @@ func ExecBlock(client queue.Client, prevStateRoot []byte, block *types.Block, er
}
}
ulog.Info("ExecBlock", "CheckBlock", types.Since(beg))
beg = types.Now()
// 写数据库失败时需要及时返回错误,防止错误数据被写入localdb中CHAIN33-567
err = ExecKVSetCommit(client, block.StateHash)
if err != nil {
......
......@@ -144,8 +144,8 @@ func TestDelDupKey(t *testing.T) {
{Key: []byte("hello"), Value: []byte("world2")},
}
result := []*types.KeyValue{
{Key: []byte("hello1"), Value: []byte("world")},
{Key: []byte("hello"), Value: []byte("world2")},
{Key: []byte("hello1"), Value: []byte("world")},
}
kvs = DelDupKey(kvs)
assert.Equal(t, kvs, result)
......@@ -173,6 +173,7 @@ func BenchmarkDelDupKey(b *testing.B) {
kvs = append(kvs, &types.KeyValue{Key: key, Value: value})
}
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
testkv := make([]*types.KeyValue, len(kvs))
copy(testkv, kvs)
......
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