Commit fbbf641a authored by 陈德海's avatar 陈德海

merge

parent 5b5e8941
......@@ -12,10 +12,10 @@ import (
"sync"
"time"
log "github.com/33cn/chain33/common/log/log15"
//"github.com/33cn/chain33/common"
"encoding/hex"
log "github.com/33cn/chain33/common/log/log15"
"github.com/33cn/chain33/client/api"
"github.com/33cn/chain33/common"
"github.com/33cn/chain33/common/crypto"
......@@ -733,7 +733,10 @@ func (client *client) WriteBlock(prev []byte, paraBlock *types.Block, seq int64)
parablockDetail := &types.ParaChainBlockDetail{Blockdetail: blockDetail, Sequence: seq}
msg := client.GetQueueClient().NewMessage("blockchain", types.EventAddParaChainBlockDetail, parablockDetail)
client.GetQueueClient().Send(msg, true)
err := client.GetQueueClient().Send(msg, true)
if err != nil {
return err
}
resp, err := client.GetQueueClient().Wait(msg)
if err != nil {
return err
......@@ -760,7 +763,10 @@ func (client *client) DelBlock(block *types.Block, seq int64) error {
panic("Parachain attempt to Delete GenesisBlock !")
}
msg := client.GetQueueClient().NewMessage("blockchain", types.EventGetBlocks, &types.ReqBlocks{Start: start, End: start, IsDetail: true, Pid: []string{""}})
client.GetQueueClient().Send(msg, true)
err := client.GetQueueClient().Send(msg, true)
if err != nil {
return err
}
resp, err := client.GetQueueClient().Wait(msg)
if err != nil {
return err
......@@ -769,7 +775,10 @@ func (client *client) DelBlock(block *types.Block, seq int64) error {
parablockDetail := &types.ParaChainBlockDetail{Blockdetail: blocks.Items[0], Sequence: seq}
msg = client.GetQueueClient().NewMessage("blockchain", types.EventDelParaChainBlockDetail, parablockDetail)
client.GetQueueClient().Send(msg, true)
err = client.GetQueueClient().Send(msg, true)
if err != nil {
return err
}
resp, err = client.GetQueueClient().Wait(msg)
if err != nil {
return err
......
......@@ -551,7 +551,11 @@ out:
break out
case <-time.NewTimer(time.Second * 2).C:
msg := client.paraClient.GetQueueClient().NewMessage("wallet", types.EventDumpPrivkey, req)
client.paraClient.GetQueueClient().Send(msg, true)
err := client.paraClient.GetQueueClient().Send(msg, true)
if err != nil {
plog.Error("para commit send msg", "err", err.Error())
break out
}
resp, err := client.paraClient.GetQueueClient().Wait(msg)
if err != nil {
plog.Error("para commit msg sign to wallet", "err", err.Error())
......
......@@ -434,7 +434,8 @@ func (evl EvidenceEnvelopeList) Hash() []byte {
default:
left := evl[:(len(evl)+1)/2].Hash()
right := evl[(len(evl)+1)/2:].Hash()
return merkle.GetHashFromTwoHash(left, right)
cache := make([]byte, len(left)+len(right))
return merkle.GetHashFromTwoHash(cache, left, right)
}
}
......
......@@ -79,7 +79,8 @@ func (evl EvidenceList) Hash() []byte {
default:
left := evl[:(len(evl)+1)/2].Hash()
right := evl[(len(evl)+1)/2:].Hash()
return merkle.GetHashFromTwoHash(left, right)
cache := make([]byte, len(left)+len(right))
return merkle.GetHashFromTwoHash(cache, left, right)
}
}
......
......@@ -485,6 +485,9 @@ func (client *Client) searchTargetTicket(parent, block *types.Block) (*ty.Ticket
client.ticketmu.Lock()
defer client.ticketmu.Unlock()
for ticketID, ticket := range client.ticketsMap {
if client.IsClosed() {
return nil, nil, nil, nil, "", nil
}
if ticket == nil {
tlog.Warn("Client searchTargetTicket ticket is nil", "ticketID", ticketID)
continue
......@@ -668,6 +671,10 @@ func (client *Client) updateBlock(block *types.Block, txHashList [][]byte) (*typ
// CreateBlock ticket create block func
func (client *Client) CreateBlock() {
for {
if client.IsClosed() {
tlog.Info("create block stop")
break
}
if !client.IsMining() || !(client.IsCaughtUp() || client.Cfg.ForceMining) {
tlog.Debug("createblock.ismining is disable or client is caughtup is false")
time.Sleep(time.Second)
......
......@@ -21,8 +21,11 @@ import (
"github.com/stretchr/testify/assert"
)
// 执行: go test -cover
func TestTicket(t *testing.T) {
testTicket(t)
}
func testTicket(t *testing.T) {
mock33 := testnode.New("testdata/chain33.cfg.toml", nil)
defer mock33.Close()
mock33.Listen()
......@@ -61,7 +64,7 @@ func TestTicket(t *testing.T) {
status, err = mock33.GetAPI().GetWalletStatus()
assert.Nil(t, err)
assert.Equal(t, true, status.IsAutoMining)
err = mock33.WaitHeight(100)
err = mock33.WaitHeight(50)
assert.Nil(t, err)
//查询票是否自动close,并且购买了新的票
req := &types.ReqWalletTransactionList{Count: 1000}
......
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Package compiler wraps the Solidity compiler executable (solc).
package compiler
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os/exec"
"regexp"
"strconv"
"strings"
)
var versionRegexp = regexp.MustCompile(`([0-9]+)\.([0-9]+)\.([0-9]+)`)
// Contract contains information about a compiled contract, alongside its code and runtime code.
type Contract struct {
Code string `json:"code"`
RuntimeCode string `json:"runtime-code"`
Info ContractInfo `json:"info"`
}
// ContractInfo contains information about a compiled contract, including access
// to the ABI definition, source mapping, user and developer docs, and metadata.
//
// Depending on the source, language version, compiler version, and compiler
// options will provide information about how the contract was compiled.
type ContractInfo struct {
Source string `json:"source"`
Language string `json:"language"`
LanguageVersion string `json:"languageVersion"`
CompilerVersion string `json:"compilerVersion"`
CompilerOptions string `json:"compilerOptions"`
SrcMap string `json:"srcMap"`
SrcMapRuntime string `json:"srcMapRuntime"`
AbiDefinition interface{} `json:"abiDefinition"`
UserDoc interface{} `json:"userDoc"`
DeveloperDoc interface{} `json:"developerDoc"`
Metadata string `json:"metadata"`
}
// Solidity contains information about the solidity compiler.
type Solidity struct {
Path, Version, FullVersion string
Major, Minor, Patch int
}
// --combined-output format
type solcOutput struct {
Contracts map[string]struct {
BinRuntime string `json:"bin-runtime"`
SrcMapRuntime string `json:"srcmap-runtime"`
Bin, SrcMap, Abi, Devdoc, Userdoc, Metadata string
}
Version string
}
func (s *Solidity) makeArgs() []string {
p := []string{
"--combined-json", "bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc",
"--optimize", // code optimizer switched on
}
if s.Major > 0 || s.Minor > 4 || s.Patch > 6 {
p[1] += ",metadata"
}
return p
}
// SolidityVersion runs solc and parses its version output.
func SolidityVersion(solc string) (*Solidity, error) {
if solc == "" {
solc = "solc"
}
var out bytes.Buffer
cmd := exec.Command(solc, "--version")
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
return nil, err
}
matches := versionRegexp.FindStringSubmatch(out.String())
if len(matches) != 4 {
return nil, fmt.Errorf("can't parse solc version %q", out.String())
}
s := &Solidity{Path: cmd.Path, FullVersion: out.String(), Version: matches[0]}
if s.Major, err = strconv.Atoi(matches[1]); err != nil {
return nil, err
}
if s.Minor, err = strconv.Atoi(matches[2]); err != nil {
return nil, err
}
if s.Patch, err = strconv.Atoi(matches[3]); err != nil {
return nil, err
}
return s, nil
}
// CompileSolidityString builds and returns all the contracts contained within a source string.
func CompileSolidityString(solc, source string) (map[string]*Contract, error) {
if len(source) == 0 {
return nil, errors.New("solc: empty source string")
}
s, err := SolidityVersion(solc)
if err != nil {
return nil, err
}
args := append(s.makeArgs(), "--")
cmd := exec.Command(s.Path, append(args, "-")...)
cmd.Stdin = strings.NewReader(source)
return s.run(cmd, source)
}
// CompileSolidity compiles all given Solidity source files.
func CompileSolidity(solc string, sourcefiles ...string) (map[string]*Contract, error) {
if len(sourcefiles) == 0 {
return nil, errors.New("solc: no source files")
}
source, err := slurpFiles(sourcefiles)
if err != nil {
return nil, err
}
s, err := SolidityVersion(solc)
if err != nil {
return nil, err
}
args := append(s.makeArgs(), "--")
cmd := exec.Command(s.Path, append(args, sourcefiles...)...)
return s.run(cmd, source)
}
func (s *Solidity) run(cmd *exec.Cmd, source string) (map[string]*Contract, error) {
var stderr, stdout bytes.Buffer
cmd.Stderr = &stderr
cmd.Stdout = &stdout
if err := cmd.Run(); err != nil {
return nil, fmt.Errorf("solc: %v\n%s", err, stderr.Bytes())
}
return ParseCombinedJSON(stdout.Bytes(), source, s.Version, s.Version, strings.Join(s.makeArgs(), " "))
}
// ParseCombinedJSON takes the direct output of a solc --combined-output run and
// parses it into a map of string contract name to Contract structs. The
// provided source, language and compiler version, and compiler options are all
// passed through into the Contract structs.
//
// The solc output is expected to contain ABI, source mapping, user docs, and dev docs.
//
// Returns an error if the JSON is malformed or missing data, or if the JSON
// embedded within the JSON is malformed.
func ParseCombinedJSON(combinedJSON []byte, source string, languageVersion string, compilerVersion string, compilerOptions string) (map[string]*Contract, error) {
var output solcOutput
if err := json.Unmarshal(combinedJSON, &output); err != nil {
return nil, err
}
// Compilation succeeded, assemble and return the contracts.
contracts := make(map[string]*Contract)
for name, info := range output.Contracts {
// Parse the individual compilation results.
var abi interface{}
if err := json.Unmarshal([]byte(info.Abi), &abi); err != nil {
return nil, fmt.Errorf("solc: error reading abi definition (%v)", err)
}
var userdoc interface{}
if err := json.Unmarshal([]byte(info.Userdoc), &userdoc); err != nil {
return nil, fmt.Errorf("solc: error reading user doc: %v", err)
}
var devdoc interface{}
if err := json.Unmarshal([]byte(info.Devdoc), &devdoc); err != nil {
return nil, fmt.Errorf("solc: error reading dev doc: %v", err)
}
contracts[name] = &Contract{
Code: "0x" + info.Bin,
RuntimeCode: "0x" + info.BinRuntime,
Info: ContractInfo{
Source: source,
Language: "Solidity",
LanguageVersion: languageVersion,
CompilerVersion: compilerVersion,
CompilerOptions: compilerOptions,
SrcMap: info.SrcMap,
SrcMapRuntime: info.SrcMapRuntime,
AbiDefinition: abi,
UserDoc: userdoc,
DeveloperDoc: devdoc,
Metadata: info.Metadata,
},
}
}
return contracts, nil
}
func slurpFiles(files []string) (string, error) {
var concat bytes.Buffer
for _, file := range files {
content, err := ioutil.ReadFile(file)
if err != nil {
return "", err
}
concat.Write(content)
}
return concat.String(), nil
}
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package compiler
import (
"os/exec"
"testing"
)
const (
testSource = `
pragma solidity >= 0.4.18;
contract test {
/// @notice Will multiply ` + "`a`" + ` by 7.
function multiply(uint a) public returns(uint d) {
return a * 7;
}
}
`
)
func skipWithoutSolc(t *testing.T) {
if _, err := exec.LookPath("solc"); err != nil {
t.Skip(err)
}
}
func TestCompiler(t *testing.T) {
skipWithoutSolc(t)
contracts, err := CompileSolidityString("", testSource)
if err != nil {
t.Fatalf("error compiling source. result is %v: %v", contracts, err)
}
if len(contracts) != 1 {
t.Errorf("one contract expected, got %d", len(contracts))
}
c, ok := contracts["test"]
if !ok {
c, ok = contracts["<stdin>:test"]
if !ok {
t.Fatal("info for contract 'test' not present in result")
}
}
if c.Code == "" {
t.Error("empty code")
}
if c.Info.Source != testSource {
t.Error("wrong source")
}
if c.Info.CompilerVersion == "" {
t.Error("empty version")
}
}
func TestCompileError(t *testing.T) {
skipWithoutSolc(t)
contracts, err := CompileSolidityString("", testSource[4:])
if err == nil {
t.Errorf("error expected compiling source. got none. result %v", contracts)
}
t.Logf("error: %v", err)
}
......@@ -11,6 +11,8 @@ import (
"os"
"time"
"github.com/33cn/plugin/plugin/dapp/evm/commands/compiler"
"strings"
"strconv"
......@@ -203,10 +205,12 @@ func createContractCmd() *cobra.Command {
func addCreateContractFlags(cmd *cobra.Command) {
addCommonFlags(cmd)
cmd.MarkFlagRequired("input")
cmd.Flags().StringP("alias", "s", "", "human readable contract alias name")
cmd.Flags().StringP("abi", "b", "", "bind the abi data")
cmd.Flags().StringP("sol", "", "", "sol file path")
cmd.Flags().StringP("solc", "", "solc", "solc compiler")
}
func createContract(cmd *cobra.Command, args []string) {
......@@ -219,15 +223,50 @@ func createContract(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
paraName, _ := cmd.Flags().GetString("paraName")
abi, _ := cmd.Flags().GetString("abi")
sol, _ := cmd.Flags().GetString("sol")
solc, _ := cmd.Flags().GetString("solc")
feeInt64 := uint64(fee*1e4) * 1e4
bCode, err := common.FromHex(code)
if err != nil {
fmt.Fprintln(os.Stderr, "parse evm code error", err)
if !strings.EqualFold(sol, "") && !strings.EqualFold(code, "") && !strings.EqualFold(abi, "") {
fmt.Fprintln(os.Stderr, "--sol, --code and --abi shouldn't be used at the same time.")
return
}
action := evmtypes.EVMContractAction{Amount: 0, Code: bCode, GasLimit: 0, GasPrice: 0, Note: note, Alias: alias, Abi: abi}
var action evmtypes.EVMContractAction
if !strings.EqualFold(sol, "") {
if _, err := os.Stat(sol); os.IsNotExist(err) {
fmt.Fprintln(os.Stderr, "Sol file is not exist.")
return
}
contracts, err := compiler.CompileSolidity(solc, sol)
if err != nil {
fmt.Fprintln(os.Stderr, "Failed to build Solidity contract", err)
return
}
if len(contracts) > 1 {
fmt.Fprintln(os.Stderr, "There are too many contracts in the sol file.")
return
}
for _, contract := range contracts {
abi, _ := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse
bCode, err := common.FromHex(contract.Code)
if err != nil {
fmt.Fprintln(os.Stderr, "parse evm code error", err)
return
}
action = evmtypes.EVMContractAction{Amount: 0, Code: bCode, GasLimit: 0, GasPrice: 0, Note: note, Alias: alias, Abi: string(abi)}
}
} else {
bCode, err := common.FromHex(code)
if err != nil {
fmt.Fprintln(os.Stderr, "parse evm code error", err)
return
}
action = evmtypes.EVMContractAction{Amount: 0, Code: bCode, GasLimit: 0, GasPrice: 0, Note: note, Alias: alias, Abi: abi}
}
data, err := createEvmTx(&action, types.ExecName(paraName+"evm"), caller, address.ExecAddress(types.ExecName(paraName+"evm")), expire, rpcLaddr, feeInt64)
......
// 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 autotest
import (
"github.com/33cn/chain33/cmd/autotest/types"
)
// JsCreateCase token createcase command
type JsCreateCase struct {
types.BaseCase
}
// JsCreatePack defines create package command
type JsCreatePack struct {
types.BaseCasePack
}
// SendCommand defines send command function of tokenprecreatecase
func (testCase *JsCreateCase) SendCommand(packID string) (types.PackFunc, error) {
return types.DefaultSend(testCase, &JsCreatePack{}, packID)
}
// 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 autotest
import (
"reflect"
"github.com/33cn/chain33/cmd/autotest/types"
)
type jsAutoTest struct {
SimpleCaseArr []types.SimpleCase `toml:"SimpleCase,omitempty"`
JSCreateCaseArr []JsCreateCase `toml:"jsCreateCase,omitempty"`
}
func init() {
types.RegisterAutoTest(jsAutoTest{})
}
func (config jsAutoTest) GetName() string {
return "js"
}
func (config jsAutoTest) GetTestConfigType() reflect.Type {
return reflect.TypeOf(config)
}
# 1Q8hGLfoGe63efeWa8fJ4Pnukhkngt6poK manage addr
# 0xc34b5d9d44ac7b754806f761d3d4d2c4fe5214f6b074c19f069c4f5c2a29c8cc
[[jsCreateCase]]
id = "create1"
command = "send jsvm create -c ../../../plugin/dapp/js/executor/game.js -n hello -k 14KEKbYtKKQm4wMthSK9J4La4nAiidGozt"
fail = true
[[jsCreateCase]]
id = "create2"
command = "send jsvm create -c ../../../plugin/dapp/js/executor/game.js -n hello -k 14KEKbYtKKQm4wMthSK9J4La4nAiidGozt"
dep = ["create1", "configJS"]
[[SimpleCase]]
id = "configJS"
dep = ["create1"]
command = "send config config_tx -c js-creator -o add -v 14KEKbYtKKQm4wMthSK9J4La4nAiidGozt -k 0xc34b5d9d44ac7b754806f761d3d4d2c4fe5214f6b074c19f069c4f5c2a29c8cc"
\ No newline at end of file
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"
......
// 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 (
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/types"
ptypes "github.com/33cn/plugin/plugin/dapp/js/types"
)
func getManageKey(key string, db dbm.KV) ([]byte, error) {
manageKey := types.ManageKey(key)
return db.Get([]byte(manageKey))
}
func checkPriv(addr, key string, db dbm.KV) error {
value, err := getManageKey(key, db)
if err != nil {
return err
}
if value == nil {
return ptypes.ErrJsCreator
}
var item types.ConfigItem
err = types.Decode(value, &item)
if err != nil {
return err
}
for _, op := range item.GetArr().Value {
if op == addr {
return nil
}
}
return ptypes.ErrJsCreator
}
......@@ -8,13 +8,18 @@ import (
)
func (c *js) Exec_Create(payload *jsproto.Create, tx *types.Transaction, index int) (*types.Receipt, error) {
err := checkPriv(tx.From(), ptypes.JsCreator, c.GetStateDB())
if err != nil {
return nil, err
}
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))
_, err = kvc.GetNoPrefix(calcCodeKey(payload.Name))
if err != nil && err != types.ErrNotFound {
return nil, err
}
......
......@@ -37,6 +37,17 @@ func initExec(ldb db.DB, kvdb db.KVDB, code string, t assert.TestingT) *js {
e.SetLocalDB(kvdb)
e.SetStateDB(kvdb)
c, tx := createCodeTx("test", code)
// set config key
item := &types.ConfigItem{
Key: "mavl-manage-js-creator",
Addr: tx.From(),
Value: &types.ConfigItem_Arr{
Arr: &types.ArrayConfig{Value: []string{tx.From()}},
},
}
kvdb.Set([]byte(item.Key), types.Encode(item))
receipt, err := e.Exec_Create(c, tx, 0)
assert.Nil(t, err)
util.SaveKVList(ldb, receipt.KV)
......
......@@ -30,6 +30,8 @@ func TestJsVM(t *testing.T) {
mocker := testnode.NewWithConfig(cfg, sub, nil)
defer mocker.Close()
mocker.Listen()
configCreator(mocker, t)
//开始部署合约, 测试阶段任何人都可以部署合约
//后期需要加上权限控制
//1. 部署合约
......@@ -94,6 +96,9 @@ func TestJsGame(t *testing.T) {
mocker.Listen()
err := mocker.SendHot()
assert.Nil(t, err)
// 需要配置
configCreator(mocker, t)
//开始部署合约, 测试阶段任何人都可以部署合约
//后期需要加上权限控制
//1. 部署合约
......@@ -288,3 +293,26 @@ func TestJsGame(t *testing.T) {
assert.Nil(t, err)
t.Log(queryresult.Data)
}
func configCreator(mocker *testnode.Chain33Mock, t *testing.T) {
// 需要配置
addr := address.PubKeyToAddress(mocker.GetHotKey().PubKey().Bytes()).String()
creator := &types.ModifyConfig{
Key: "js-creator",
Op: "add",
Value: addr,
Addr: addr,
}
cfgReq := &rpctypes.CreateTxIn{
Execer: "manage",
ActionName: "Modify",
Payload: types.MustPBToJSON(creator),
}
var cfgtxhex string
err := mocker.GetJSONC().Call("Chain33.CreateTransaction", cfgReq, &cfgtxhex)
assert.Nil(t, err)
hash1, err := mocker.SendAndSign(mocker.GetHotKey(), cfgtxhex)
assert.Nil(t, err)
_, err = mocker.WaitTx(hash1)
assert.Nil(t, err)
}
......@@ -2,9 +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() {
......@@ -12,7 +15,7 @@ func init() {
Name: ptypes.JsX,
ExecName: executor.GetName(),
Exec: executor.Init,
Cmd: cmd.JavaScriptCmd,
Cmd: command.JavaScriptCmd,
RPC: nil,
})
}
......@@ -19,6 +19,9 @@ const (
TyLogJs = 10000
)
// JsCreator 配置项 创建js合约的管理员
const JsCreator = "js-creator"
var (
typeMap = map[string]int32{
"Create": jsActionCreate,
......@@ -47,6 +50,8 @@ var (
ErrSymbolName = errors.New("chain33.js: ErrSymbolName")
ErrExecerName = errors.New("chain33.js: ErrExecerName")
ErrDBType = errors.New("chain33.js: ErrDBType")
// ErrJsCreator
ErrJsCreator = errors.New("ErrJsCreator")
)
func init() {
......
......@@ -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
}
......
......@@ -103,17 +103,6 @@ dep = ["trans1"]
fail = true
[[PubToPrivCase]]
id = "failpub2priv2"
command = "send privacy pub2priv -a 603 -p d45063357ab3454cfb7ee87db8f9ca2772d11bd82d4f681ce00ae44fe5929e8fd964867dae1b19b265b7f68d4ad965cfd4a0aaf71580344adf9fce28cfebadab -k 12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv"
from = "12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv"
to = "1D9xKRnLvV2zMtSxSx33ow1GF4pcbLcNRt"
amount = "603"
checkItem = ["balance", "utxo"]
dep = ["pub2priv1", "pub2priv2", "pub2priv3"]
fail = true
#priv2priv
[[PrivToPrivCase]]
......
......@@ -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
}
......@@ -108,7 +108,7 @@ func (t *token) getAccountTokenAssets(req *tokenty.ReqAccountTokenAssets) (types
return nil, err
}
var acc1 *types.Account
if req.Execer == "trade" {
if req.Execer != "" {
execaddress := address.ExecAddress(req.Execer)
acc1 = acc.LoadExecAccount(req.Address, execaddress)
} else if req.Execer == t.GetName() {
......
......@@ -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"},
}
}
......
......@@ -26,7 +26,7 @@ function unfreeze_test() {
tx_hash=$(${CLI} send unfreeze create fix_amount -a 0.01 -e coins -s bty -b ${beneficiary} -p 20 -t 2 -k ${owner_key})
block_wait "${CLI}" 2
unfreeze_id=$(${CLI} tx query -s "${tx_hash}" | jq ".receipt.logs[2].log.current.unfreezeID")
echo "$unfreeze_id"
echo "${unfreeze_id}"
unfreeze_id2=${unfreeze_id#\"mavl-unfreeze-}
uid=${unfreeze_id2%\"}
......@@ -51,8 +51,9 @@ function unfreeze_test() {
${CLI} send unfreeze terminate --id "${uid}" -k "${owner_key}"
block_wait "${CLI}" 2
remaining=$(${CLI} unfreeze show --id "${uid}" | jq ".remaining")
if [ "${remaining}" != '"0"' ]; then
echo "terminate failed, expect remaining 0, result ${remaining}"
remainingNum=$(echo "$remaining" | awk '{print int($0)}')
if [ "100000000" -lt "${remainingNum}" ]; then
echo "terminate failed, expect remaining < 100000000, result ${remaining}"
exit 1
fi
echo "==================== unfreeze test end"
......
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
}
......@@ -9,6 +9,8 @@ datadir*
*.bak
.idea
.vscode
*.pprof
*.test
cmd/chain33/chain33
cmd/tools/tools
build/cert.pem
......
......@@ -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
......
......@@ -16,7 +16,6 @@ package account
//8. gen a private key -> private key to address (bitcoin likes)
import (
"fmt"
"strings"
"github.com/33cn/chain33/client"
......@@ -37,6 +36,7 @@ type DB struct {
execAccountKeyPerfix []byte
execer string
symbol string
accountKeyBuffer []byte
}
// NewCoinsAccount 新建账户
......@@ -64,6 +64,8 @@ func NewAccountDB(execer string, symbol string, db dbm.KV) (*DB, error) {
func newAccountDB(prefix string) *DB {
acc := &DB{}
acc.accountKeyPerfix = []byte(prefix)
acc.accountKeyBuffer = make([]byte, 0, len(acc.accountKeyPerfix)+64)
acc.accountKeyBuffer = append(acc.accountKeyBuffer, acc.accountKeyPerfix...)
acc.execAccountKeyPerfix = append([]byte(prefix), []byte("exec-")...)
//alog.Warn("NewAccountDB", "prefix", prefix, "key1", string(acc.accountKeyPerfix), "key2", string(acc.execAccountKeyPerfix))
return acc
......@@ -75,9 +77,15 @@ func (acc *DB) SetDB(db dbm.KV) *DB {
return acc
}
func (acc *DB) accountReadKey(addr string) []byte {
acc.accountKeyBuffer = acc.accountKeyBuffer[0:len(acc.accountKeyPerfix)]
acc.accountKeyBuffer = append(acc.accountKeyBuffer, []byte(addr)...)
return acc.accountKeyBuffer
}
// LoadAccount 根据地址载入账户
func (acc *DB) LoadAccount(addr string) *types.Account {
value, err := acc.db.Get(acc.AccountKey(addr))
value, err := acc.db.Get(acc.accountReadKey(addr))
if err != nil {
return &types.Account{Addr: addr}
}
......@@ -127,10 +135,11 @@ func (acc *DB) Transfer(from, to string, amount int64) (*types.Receipt, error) {
Prev: &copyto,
Current: accTo,
}
acc.SaveAccount(accFrom)
acc.SaveAccount(accTo)
return acc.transferReceipt(accFrom, accTo, receiptBalanceFrom, receiptBalanceTo), nil
fromkv := acc.GetKVSet(accFrom)
tokv := acc.GetKVSet(accTo)
acc.SaveKVSet(fromkv)
acc.SaveKVSet(tokv)
return acc.transferReceipt(fromkv, tokv, receiptBalanceFrom, receiptBalanceTo), nil
}
return nil, types.ErrNoBalance
......@@ -147,12 +156,12 @@ func (acc *DB) depositBalance(execaddr string, amount int64) (*types.Receipt, er
Prev: &copyacc,
Current: acc1,
}
acc.SaveAccount(acc1)
kv := acc.GetKVSet(acc1)
acc.SaveKVSet(kv)
log1 := &types.ReceiptLog{
Ty: int32(types.TyLogDeposit),
Log: types.Encode(receiptBalance),
}
kv := acc.GetKVSet(acc1)
return &types.Receipt{
Ty: types.ExecOk,
KV: kv,
......@@ -160,7 +169,7 @@ func (acc *DB) depositBalance(execaddr string, amount int64) (*types.Receipt, er
}, nil
}
func (acc *DB) transferReceipt(accFrom, accTo *types.Account, receiptFrom, receiptTo proto.Message) *types.Receipt {
func (acc *DB) transferReceipt(fromkv, tokv []*types.KeyValue, receiptFrom, receiptTo proto.Message) *types.Receipt {
ty := int32(types.TyLogTransfer)
log1 := &types.ReceiptLog{
Ty: ty,
......@@ -170,8 +179,9 @@ func (acc *DB) transferReceipt(accFrom, accTo *types.Account, receiptFrom, recei
Ty: ty,
Log: types.Encode(receiptTo),
}
kv := acc.GetKVSet(accFrom)
kv = append(kv, acc.GetKVSet(accTo)...)
kv := make([]*types.KeyValue, 0, len(fromkv)+len(tokv))
kv = append(kv, fromkv...)
kv = append(kv, tokv...)
return &types.Receipt{
Ty: types.ExecOk,
KV: kv,
......@@ -187,13 +197,21 @@ func (acc *DB) SaveAccount(acc1 *types.Account) {
}
}
//SaveKVSet 保存Key Value set
func (acc *DB) SaveKVSet(set []*types.KeyValue) {
for i := 0; i < len(set); i++ {
acc.db.Set(set[i].GetKey(), set[i].Value)
}
}
// GetKVSet 将账户数据转为数据库存储kv
func (acc *DB) GetKVSet(acc1 *types.Account) (kvset []*types.KeyValue) {
value := types.Encode(acc1)
kvset = append(kvset, &types.KeyValue{
kvset = make([]*types.KeyValue, 1)
kvset[0] = &types.KeyValue{
Key: acc.AccountKey(acc1.Addr),
Value: value,
})
}
return kvset
}
......@@ -218,17 +236,18 @@ func (acc *DB) LoadAccountsDB(addrs []string) (accs []*types.Account, err error)
// AccountKey return the key of address in DB
func (acc *DB) AccountKey(address string) (key []byte) {
key = make([]byte, 0, len(acc.accountKeyPerfix)+len(address))
key = append(key, acc.accountKeyPerfix...)
key = append(key, []byte(address)...)
return key
}
func symbolPrefix(execer string, symbol string) string {
return fmt.Sprintf("mavl-%s-%s-", execer, symbol)
return "mavl-" + execer + "-" + symbol + "-"
}
func symbolExecPrefix(execer string, symbol string) string {
return fmt.Sprintf("mavl-%s-%s-exec", execer, symbol)
return "mavl-" + execer + "-" + symbol + "-exec"
}
// GetTotalCoins 获取代币总量
......@@ -415,3 +434,78 @@ func genPrefixEdge(prefix []byte) (r []byte) {
return r
}
// Mint 铸币
func (acc *DB) Mint(addr string, amount int64) (*types.Receipt, error) {
if !types.CheckAmount(amount) {
return nil, types.ErrAmount
}
accTo := acc.LoadAccount(addr)
balance, err := safeAdd(accTo.Balance, amount)
if err != nil {
return nil, err
}
copyAcc := *accTo
accTo.Balance = balance
receipt := &types.ReceiptAccountMint{
Prev: &copyAcc,
Current: accTo,
}
kv := acc.GetKVSet(accTo)
acc.SaveKVSet(kv)
return acc.mintReceipt(kv, receipt), nil
}
func (acc *DB) mintReceipt(kv []*types.KeyValue, receipt proto.Message) *types.Receipt {
ty := int32(types.TyLogMint)
log1 := &types.ReceiptLog{
Ty: ty,
Log: types.Encode(receipt),
}
return &types.Receipt{
Ty: types.ExecOk,
KV: kv,
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},
}
}
......@@ -554,3 +554,50 @@ func TestGetExecBalance2(t *testing.T) {
assert.Equal(t, 1, len(reply.Items))
*/
}
func TestGetBalance(t *testing.T) {
accCoin := NewCoinsAccount()
addr := "1JmFaA6unrCFYEWPGRi7uuXY1KthTJxJEP"
fmt.Println("-------------TestGetExecBalance2---test case1---")
api := new(mocks.QueueProtocolAPI)
in := &types.ReqBalance{}
in.Addresses = append(in.Addresses, addr)
api.On("StoreList", mock.Anything).Return(&types.StoreListReply{}, nil)
api.On("GetLastHeader", mock.Anything).Return(&types.Header{StateHash: []byte("111111111111111111111")}, nil)
api.On("StoreGet", mock.Anything).Return(&types.StoreReplyValue{Values: make([][]byte, 1)}, nil)
_, err := accCoin.GetBalance(api, in)
assert.Nil(t, err)
fmt.Println("-------------TestGetExecBalance2---test case2---")
in.StateHash = "111111111111111111111"
_, err = accCoin.GetBalance(api, in)
assert.Nil(t, err)
fmt.Println("-------------TestGetExecBalance2---test case3---")
in.Execer = "coins"
//api.On("StoreList", mock.Anything).Return(nil, types.ErrInvalidParam)
_, err = accCoin.GetBalance(api, in)
t.Log(err)
assert.Nil(t, err)
}
func TestDB_Mint(t *testing.T) {
_, tokenCoin := GenerAccDb()
tokenCoin.GenerAccData()
_, err := tokenCoin.Mint(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)
}
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)
}
......@@ -52,6 +52,7 @@ func (acc *DB) GetExecKVSet(execaddr string, acc1 *types.Account) (kvset []*type
}
func (acc *DB) execAccountKey(address, execaddr string) (key []byte) {
key = make([]byte, 0, len(acc.execAccountKeyPerfix)+len(execaddr)+len(address)+1)
key = append(key, acc.execAccountKeyPerfix...)
key = append(key, []byte(execaddr)...)
key = append(key, []byte(":")...)
......
......@@ -5,9 +5,6 @@
package blockchain
import (
"bytes"
"github.com/33cn/chain33/common/merkle"
"github.com/33cn/chain33/queue"
"github.com/33cn/chain33/types"
"github.com/33cn/chain33/util"
......@@ -15,127 +12,5 @@ import (
//执行区块将变成一个私有的函数
func execBlock(client queue.Client, prevStateRoot []byte, block *types.Block, errReturn bool, sync bool) (*types.BlockDetail, []*types.Transaction, error) {
//发送执行交易给execs模块
//通过consensus module 再次检查
chainlog.Debug("ExecBlock", "height------->", block.Height, "ntx", len(block.Txs))
beg := types.Now()
defer func() {
chainlog.Info("ExecBlock", "height", block.Height, "ntx", len(block.Txs), "writebatchsync", sync, "cost", types.Since(beg))
}()
if errReturn && block.Height > 0 && !block.CheckSign() {
//block的来源不是自己的mempool,而是别人的区块
return nil, nil, types.ErrSign
}
//tx交易去重处理, 这个地方要查询数据库,需要一个更快的办法
cacheTxs := types.TxsToCache(block.Txs)
oldtxscount := len(cacheTxs)
var err error
cacheTxs, err = util.CheckTxDup(client, cacheTxs, block.Height)
if err != nil {
return nil, nil, err
}
newtxscount := len(cacheTxs)
if oldtxscount != newtxscount && errReturn {
return nil, nil, types.ErrTxDup
}
chainlog.Debug("ExecBlock", "prevtx", oldtxscount, "newtx", newtxscount)
block.TxHash = merkle.CalcMerkleRootCache(cacheTxs)
block.Txs = types.CacheToTxs(cacheTxs)
//println("1")
receipts, err := util.ExecTx(client, prevStateRoot, block)
if err != nil {
return nil, nil, err
}
var maplist = make(map[string]*types.KeyValue)
var kvset []*types.KeyValue
var deltxlist = make(map[int]bool)
var rdata []*types.ReceiptData //save to db receipt log
for i := 0; i < len(receipts.Receipts); i++ {
receipt := receipts.Receipts[i]
if receipt.Ty == types.ExecErr {
chainlog.Error("exec tx err", "err", receipt)
if errReturn { //认为这个是一个错误的区块
return nil, nil, types.ErrBlockExec
}
deltxlist[i] = true
continue
}
rdata = append(rdata, &types.ReceiptData{Ty: receipt.Ty, Logs: receipt.Logs})
//处理KV
kvs := receipt.KV
for _, kv := range kvs {
if item, ok := maplist[string(kv.Key)]; ok {
item.Value = kv.Value //更新item 的value
} else {
maplist[string(kv.Key)] = kv
kvset = append(kvset, kv)
}
}
}
//check TxHash
calcHash := merkle.CalcMerkleRoot(block.Txs)
if errReturn && !bytes.Equal(calcHash, block.TxHash) {
return nil, nil, types.ErrCheckTxHash
}
block.TxHash = calcHash
//删除无效的交易
var deltx []*types.Transaction
if len(deltxlist) > 0 {
var newtx []*types.Transaction
for i := 0; i < len(block.Txs); i++ {
if deltxlist[i] {
deltx = append(deltx, block.Txs[i])
} else {
newtx = append(newtx, block.Txs[i])
}
}
block.Txs = newtx
block.TxHash = merkle.CalcMerkleRoot(block.Txs)
}
var detail types.BlockDetail
calcHash, err = util.ExecKVMemSet(client, prevStateRoot, block.Height, kvset, sync)
if err != nil {
return nil, nil, err
}
//println("2")
if errReturn && !bytes.Equal(block.StateHash, calcHash) {
err = util.ExecKVSetRollback(client, calcHash)
if err != nil {
chainlog.Error("execBlock-->ExecKVSetRollback", "err", err)
}
if len(rdata) > 0 {
for i, rd := range rdata {
rd.OutputReceiptDetails(block.Txs[i].Execer, chainlog)
}
}
return nil, nil, types.ErrCheckStateHash
}
block.StateHash = calcHash
detail.Block = block
detail.Receipts = rdata
if detail.Block.Height > 0 {
err := util.CheckBlock(client, &detail)
if err != nil {
chainlog.Debug("CheckBlock-->", "err=", err)
return nil, deltx, err
}
}
//println("3")
//save to db
// 写数据库失败时需要及时返回错误,防止错误数据被写入localdb中CHAIN33-567
err = util.ExecKVSetCommit(client, block.StateHash)
if err != nil {
return nil, nil, err
}
detail.KV = kvset
detail.PrevStatusHash = prevStateRoot
//get receipts
//save kvset and get state hash
//ulog.Debug("blockdetail-->", "detail=", detail)
//println("4")
return &detail, deltx, nil
return util.ExecBlock(client, prevStateRoot, block, errReturn, sync, true)
}
......@@ -7,7 +7,6 @@ import (
"github.com/33cn/chain33/types"
)
func (chain *BlockChain) procLocalDB(msgtype int64, msg *queue.Message, reqnum chan struct{}) bool {
switch msgtype {
case types.EventLocalGet:
......
......@@ -226,6 +226,10 @@ func (chain *BlockChain) ProcGetBlockDetailsMsg(requestblock *types.ReqBlocks) (
//ProcAddBlockMsg 处理从peer对端同步过来的block消息
func (chain *BlockChain) ProcAddBlockMsg(broadcast bool, blockdetail *types.BlockDetail, pid string) (*types.BlockDetail, error) {
beg := types.Now()
defer func() {
chainlog.Info("ProcAddBlockMsg", "cost", types.Since(beg))
}()
block := blockdetail.Block
if block == nil {
chainlog.Error("ProcAddBlockMsg input block is null")
......
......@@ -25,7 +25,7 @@ func TestReindex(t *testing.T) {
chain := mock33.GetBlockChain()
db := chain.GetDB()
kvs := getAllKeys(db)
assert.Equal(t, len(kvs), 20)
assert.Equal(t, len(kvs), 22)
defer mock33.Close()
txs := util.GenCoinsTxs(mock33.GetGenesisKey(), 10)
for i := 0; i < len(txs); i++ {
......
......@@ -23,57 +23,96 @@ callerFile = false
callerFunction = false
[blockchain]
# 数据库文件目录
dbPath="datadir"
# 数据库缓存大小
dbCache=64
# 同步区块批量写数据库时,是否需要立即写磁盘,非固态硬盘的电脑可以设置为false,以提高性能
batchsync=false
# 是否记录添加或者删除区块的序列,若节点作为主链节点,为平行链节点提供服务,需要设置为true
isRecordBlockSequence=false
enableTxQuickIndex=false
[p2p]
# P2P服务监听端口号
port=13802
# 种子节点,格式为ip:port,多个节点以逗号分隔,如seeds=["10.0.0.1:13802","10.0.0.2:13802","10.0.0.3:13802"]
seeds=[]
# 是否为种子节点
isSeed=false
# 是否使用内置的种子节点
innerSeedEnable=true
# 是否使用Github获取种子节点
useGithub=true
# 最多的接入节点个数
innerBounds=300
# 数据库文件目录
dbPath="datadir/addrbook"
# 数据库缓存大小
dbCache=4
# GRPC请求日志文件
grpcLogFile="grpc33.log"
[rpc]
# jrpc绑定地址
jrpcBindAddr="localhost:8801"
# grpc绑定地址
grpcBindAddr="localhost:8802"
# 白名单列表,允许访问的IP地址,默认是“*”,允许所有IP访问
whitelist=["127.0.0.1"]
# jrpc方法请求白名单,默认是“*”,允许访问所有RPC方法
jrpcFuncWhitelist=["*"]
# grpc方法请求白名单,默认是“*”,允许访问所有RPC方法
grpcFuncWhitelist=["*"]
# 是否开启https
enableTLS=false
# 证书文件,证书和私钥文件可以用cli工具生成
certFile="cert.pem"
# 私钥文件
keyFile="key.pem"
[mempool]
# 每个账户在mempool中得最大交易数量,默认100
maxTxNumPerAccount=100
[store]
# 数据文件存储路径
dbPath="datadir/mavltree"
# Cache大小
dbCache=128
[store.sub.mavl]
# 是否使能mavl加前缀
enableMavlPrefix=false
# 是否使能MVCC,如果mavl中enableMVCC为true此处必须为true
enableMVCC=false
# 是否使能mavl数据裁剪
enableMavlPrune=false
# 裁剪高度间隔
pruneHeight=10000
# 是否使能mavl数据载入内存
enableMemTree=false
# 是否使能mavl叶子节点数据载入内存
enableMemVal=false
[wallet]
# walletdb路径
dbPath="wallet"
# walletdb缓存大小
dbCache=16
[wallet.sub.ticket]
# 是否关闭ticket自动挖矿,默认false
minerdisable=false
# 允许购买ticket挖矿的白名单地址,默认配置“*”,允许所有地址购买
minerwhitelist=["*"]
[exec]
#是否开启stat插件
enableStat=false
#是否开启MVCC插件
enableMVCC=false
[exec.sub.token]
#是否保存token交易信息
saveTokenTxList=false
......@@ -144,6 +144,10 @@ enableMavlPrefix=false
enableMVCC=false
enableMavlPrune=false
pruneHeight=10000
# 是否使能mavl数据载入内存
enableMemTree=false
# 是否使能mavl叶子节点数据载入内存
enableMemVal=false
[wallet]
minFee=1000000
......
......@@ -24,55 +24,91 @@ callerFile = false
callerFunction = false
[blockchain]
# 缓存区块的个数
defCacheSize=128
# 同步区块时一次最多申请获取的区块个数
maxFetchBlockNum=128
# 向对端节点请求同步区块的时间间隔
timeoutSeconds=5
batchBlockNum=128
# 使用的数据库类型
driver="leveldb"
# 数据库文件目录
dbPath="datadir"
# 数据库缓存大小
dbCache=64
isStrongConsistency=false
# 是否为单节点
singleMode=true
# 同步区块批量写数据库时,是否需要立即写磁盘,非固态硬盘的电脑可以设置为false,以提高性能
batchsync=false
# 是否记录添加或者删除区块的序列,若节点作为主链节点,为平行链节点提供服务,需要设置为true
isRecordBlockSequence=true
# 是否为平行链节点
isParaChain=false
enableTxQuickIndex=false
[p2p]
# P2P服务监听端口号
port=13802
# 种子节点,格式为ip:port,多个节点以逗号分隔,如seeds=["10.0.0.1:13802","10.0.0.2:13802","10.0.0.3:13802"]
seeds=[]
# 是否启动P2P服务
enable=true
# 是否为种子节点
isSeed=false
# 是否作为服务端,对外提供服务
serverStart=true
# 是否使用内置的种子节点
innerSeedEnable=true
# 是否使用Github获取种子节点
useGithub=true
# 最多的接入节点个数
innerBounds=300
msgCacheSize=10240
# 使用的数据库类型
driver="leveldb"
# 使用的数据库类型
dbPath="datadir/addrbook"
# 数据库缓存大小
dbCache=4
# GRPC请求日志文件
grpcLogFile="grpc33.log"
[rpc]
# jrpc绑定地址
jrpcBindAddr="localhost:8801"
# grpc绑定地址
grpcBindAddr="localhost:8802"
# 白名单列表,允许访问的IP地址,默认是“*”,允许所有IP访问
whitelist=["127.0.0.1"]
# jrpc方法请求白名单,默认是“*”,允许访问所有RPC方法
jrpcFuncWhitelist=["*"]
# grpc方法请求白名单,默认是“*”,允许访问所有RPC方法
grpcFuncWhitelist=["*"]
# 是否开启https
enableTLS=true
# 证书文件,证书和私钥文件可以用cli工具生成
certFile="cert.pem"
# 私钥文件
keyFile="key.pem"
[mempool]
# mempool队列名称,可配,timeline,score,price
name="timeline"
# mempool缓存容量大小,默认10240
poolCacheSize=10240
# 最小得交易手续费用,这个没有默认值,必填,一般是100000
minTxFee=100000
# 每个账户在mempool中得最大交易数量,默认100
maxTxNumPerAccount=100
[mempool.sub.timeline]
# mempool缓存容量大小,默认10240
poolCacheSize=10240
[mempool.sub.score]
# mempool缓存容量大小,默认10240
poolCacheSize=10240
timeParam=1 #时间占价格比例
priceConstant=1544 #手续费相对于时间的一个合适的常量,取当前unxi时间戳前四位数,排序时手续费高1e-5~=快1s
......@@ -82,24 +118,39 @@ pricePower=1 #常量比例
poolCacheSize=10240
[consensus]
#共识名,可选项有solo,ticket,raft,tendermint,para
name="solo"
#是否开启挖矿,开启挖矿才能创建区块
minerstart=true
#创世区块时间(UTC时间)
genesisBlockTime=1514533394
#创世交易地址
genesis="14KEKbYtKKQm4wMthSK9J4La4nAiidGozt"
[mver.consensus]
#基金账户地址
fundKeyAddr = "1BQXS6TxaYYG5mADaWij4AxhZZUTpw95a5"
#用户回报
coinReward = 18
#发展基金回报
coinDevFund = 12
#ticket价格
ticketPrice = 10000
#挖矿难度
powLimitBits = "0x1f00ffff"
#每次调整难度的最大的范围,如果设置成 4 的话,范围是 (1/4 - 4),一次不能增加 4倍以上的难度,或者难度减少为 原来的 1/4 ,这个参数,是为了难度不会突然爆增加或者减少
retargetAdjustmentFactor = 4
#表示如果区块时间大于当前时间 16s ,那么区块就会判定为无效区块。
futureBlockTime = 16
#ticket冻结时长
ticketFrozenTime = 5 #5s only for test
ticketWithdrawTime = 10 #10s only for test
ticketMinerWaitTime = 2 #2s only for test
#区块包含最多交易数
maxTxNumber = 1600 #160
#调整挖矿难度的间隔,(ps:难度不是每个区块都调整的,而是每隔 targetTimespan / targetTimePerBlock 块调整一次)
targetTimespan = 2304
#每个区块打包的目标时间
targetTimePerBlock = 16
[mver.consensus.ForkChainParamV1]
......@@ -139,38 +190,64 @@ returnAddr="1KcCVZLSQYRUwE5EXTsAoQs9LuJW6xwfQa"
count=10000
[store]
# 数据存储格式名称,目前支持mavl,kvdb,kvmvcc,mpt
name="mavl"
# 数据存储驱动类别,目前支持leveldb,goleveldb,memdb,gobadgerdb,ssdb,pegasus
driver="leveldb"
# 数据文件存储路径
dbPath="datadir/mavltree"
# Cache大小
dbCache=128
# 数据库版本
localdbVersion="1.0.0"
[store.sub.mavl]
# 是否使能mavl加前缀
enableMavlPrefix=false
# 是否使能MVCC,如果mavl中enableMVCC为true此处必须为true
enableMVCC=false
# 是否使能mavl数据裁剪
enableMavlPrune=false
# 裁剪高度间隔
pruneHeight=10000
# 是否使能mavl数据载入内存
enableMemTree=false
# 是否使能mavl叶子节点数据载入内存
enableMemVal=false
[wallet]
# 交易发送最低手续费,单位0.00000001BTY(1e-8),默认100000,即0.001BTY
minFee=100000
# walletdb驱动名,支持leveldb/memdb/gobadgerdb/ssdb/pegasus
driver="leveldb"
# walletdb路径
dbPath="wallet"
# walletdb缓存大小
dbCache=16
# 钱包发送交易签名方式
signType="secp256k1"
[wallet.sub.ticket]
# 是否关闭ticket自动挖矿,默认false
minerdisable=false
# 允许购买ticket挖矿的白名单地址,默认配置“*”,允许所有地址购买
minerwhitelist=["*"]
[exec]
#执行器执行是否免费
isFree=false
#执行器执行所需最小费用,低于Mempool和Wallet设置的MinFee,在minExecFee = 0 的情况下,isFree = true才会生效
minExecFee=100000
#是否开启stat插件
enableStat=false
#是否开启MVCC插件
enableMVCC=false
alias=["token1:token","token2:token","token3:token"]
[exec.sub.token]
#是否保存token交易信息
saveTokenTxList=true
#token审批人地址
tokenApprs = [
"1Bsg9j6gW83sShoee1fZAt9TkUjcrCgA9S",
"1Q8hGLfoGe63efeWa8fJ4Pnukhkngt6poK",
......@@ -189,9 +266,11 @@ cryptoPath="authdir/crypto"
signType="auth_ecdsa"
[exec.sub.relay]
#relay执行器保存BTC头执行权限地址
genesis="14KEKbYtKKQm4wMthSK9J4La4nAiidGozt"
[exec.sub.manage]
#manage执行器超级管理员地址
superManager=[
"1Bsg9j6gW83sShoee1fZAt9TkUjcrCgA9S",
"12qyocayNF7Lv6C9qW4avxs2E7U41fKSfv",
......
......@@ -28,6 +28,10 @@ wget 127.0.0.1:8866 --no-proxy --post-data='{"id" : 1 , "method" : "ShowMinerAcc
1. 是否有某个挖矿帐号, 一个小时挖矿所得为0, 可能是挖矿机器出故障了
1. 增涨不到预期的50%
1. 不再监控这个点: 在预期能挖到币的两倍时间间隔内, 挖到的币为0。 挖矿有波动, 在进行预警的几天里, 这个有很多误报。
1. 数据获得失败报警
1. result 里面 null
1. 监控机器 heartbeat 表明监控机器没有宕机
```
{
"id" : 1,
......
......@@ -17,6 +17,7 @@ import (
var addrSeed = []byte("address seed bytes for public key")
var addressCache *lru.Cache
var pubkeyCache *lru.Cache
var checkAddressCache *lru.Cache
var multisignCache *lru.Cache
var multiCheckAddressCache *lru.Cache
......@@ -38,6 +39,7 @@ const MultiSignVer byte = 5
func init() {
multisignCache, _ = lru.New(10240)
pubkeyCache, _ = lru.New(10240)
addressCache, _ = lru.New(10240)
checkAddressCache, _ = lru.New(10240)
multiCheckAddressCache, _ = lru.New(10240)
......@@ -101,6 +103,16 @@ func PubKeyToAddress(in []byte) *Address {
return HashToAddress(NormalVer, in)
}
//PubKeyToAddr 公钥转为地址
func PubKeyToAddr(in []byte) string {
if value, ok := pubkeyCache.Get(string(in)); ok {
return value.(string)
}
addr := HashToAddress(NormalVer, in).String()
pubkeyCache.Add(string(in), addr)
return addr
}
//HashToAddress hash32 to address
func HashToAddress(version byte, in []byte) *Address {
a := new(Address)
......
......@@ -12,10 +12,9 @@ import (
"time"
"github.com/33cn/chain33/common/crypto"
_ "github.com/33cn/chain33/system/crypto/init"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
_ "github.com/33cn/chain33/system/crypto/init"
)
func genkey() crypto.PrivKey {
......@@ -105,3 +104,8 @@ func BenchmarkExecAddress(b *testing.B) {
duration = end - start
fmt.Println("duration without cache:", strconv.FormatInt(duration, 10))
}
func TestExecPubKey(t *testing.T) {
pubkey := ExecPubKey("test")
assert.True(t, len(pubkey) == 32)
}
......@@ -13,11 +13,37 @@ import (
"github.com/stretchr/testify/require"
)
func TestGet(t *testing.T) {
require := require.New(t)
name := crypto.GetName(1)
require.Equal("secp256k1", name)
name = crypto.GetName(2)
require.Equal("ed25519", name)
name = crypto.GetName(3)
require.Equal("sm2", name)
ty := crypto.GetType("secp256k1")
require.True(ty == 1)
ty = crypto.GetType("ed25519")
require.True(ty == 2)
ty = crypto.GetType("sm2")
require.True(ty == 3)
}
func TestRipemd160(t *testing.T) {
require := require.New(t)
b := crypto.Ripemd160([]byte("test"))
require.NotNil(b)
}
func TestAll(t *testing.T) {
testCrypto(t, "ed25519")
testFromBytes(t, "ed25519")
testCrypto(t, "secp256k1")
testFromBytes(t, "secp256k1")
testCrypto(t, "sm2")
testFromBytes(t, "sm2")
}
func testFromBytes(t *testing.T, name string) {
......
......@@ -24,6 +24,8 @@ import (
"os"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
const (
......@@ -341,3 +343,23 @@ func Example_mac() {
fmt.Printf("%x\n", h)
// ProtoToJson: 78de2974bd2711d5549ffd32b753ef0f5fa80a0db2556db60f0987eb8a9218ff
}
func TestNew(t *testing.T) {
d28 := Sum224(nil)
assert.True(t, len(d28) == 28)
d32 := Sum256(nil)
assert.True(t, len(d32) == 32)
d48 := Sum384(nil)
assert.True(t, len(d48) == 48)
d64 := Sum512(nil)
assert.True(t, len(d64) == 64)
d32 = KeccakSum256(nil)
assert.True(t, len(d32) == 32)
d32 = KeccakSum512(nil)
assert.True(t, len(d32) == 32)
}
......@@ -105,21 +105,12 @@ func Sha3(b []byte) []byte {
return data[:]
}
func sha2Hash(b []byte, out []byte) {
s := sha256.New()
s.Write(b[:])
tmp := s.Sum(nil)
s.Reset()
s.Write(tmp)
copy(out[:], s.Sum(nil))
}
// Sha2Sum Returns hash: SHA256( SHA256( data ) )
// Where possible, using ShaHash() should be a bit faster
func Sha2Sum(b []byte) []byte {
out := make([]byte, 32)
sha2Hash(b, out[:])
return out[:]
tmp := sha256.Sum256(b)
tmp = sha256.Sum256(tmp[:])
return tmp[:]
}
func rimpHash(in []byte, out []byte) {
......
......@@ -7,8 +7,9 @@ package merkle
import (
"bytes"
"crypto/sha256"
"runtime"
"github.com/33cn/chain33/common"
"github.com/33cn/chain33/types"
)
......@@ -48,6 +49,140 @@ known ways of changing the transactions without affecting the merkle
root.
*/
/*GetMerkleRoot This implements a constant-space merkle root/path calculator, limited to 2^32 leaves. */
//flage =1 只计算roothash flage =2 只计算branch flage =3 计算roothash 和 branch
func getMerkleRoot(hashes [][]byte) []byte {
cache := make([]byte, 64)
level := 0
for len(hashes) > 1 {
if len(hashes)&1 != 0 { //奇数
hashes = append(hashes, hashes[len(hashes)-1])
}
index := 0
for i := 0; i < len(hashes); i += 2 {
hashes[index] = GetHashFromTwoHash(cache, hashes[i], hashes[i+1])
index++
}
level++
hashes = hashes[0:index]
}
if len(hashes) == 0 {
return nil
}
return hashes[0]
}
func log2(data int) int {
level := 1
if data <= 0 {
return 0
}
for {
data = data / 2
if data <= 1 {
return level
}
level++
}
}
func pow2(d int) (p int) {
if d <= 0 {
return 1
}
p = 1
for i := 0; i < d; i++ {
p *= 2
}
return p
}
func calcLevel(n int) int {
if n == 1 {
return 1
}
level := 0
for n > 1 {
if n&1 != 0 {
n++
}
n = n / 2
level++
}
return level
}
func getMerkleRootPad(hashes [][]byte, step int) []byte {
level1 := calcLevel(len(hashes))
level2 := log2(step)
var root []byte
cache := make([]byte, 64)
if len(hashes) == 1 {
root = GetHashFromTwoHash(cache, hashes[0], hashes[0])
} else {
root = getMerkleRoot(hashes)
}
for i := 0; i < level2-level1; i++ {
root = GetHashFromTwoHash(cache, root, root)
}
return root
}
type childstate struct {
hash []byte
index int
}
//GetMerkleRoot 256构成一个组,进行计算
// n * step = hashes
// (hashes / n)
func GetMerkleRoot(hashes [][]byte) []byte {
ncpu := runtime.NumCPU()
if len(hashes) <= 80 || ncpu <= 1 {
return getMerkleRoot(hashes)
}
step := log2(len(hashes) / ncpu)
if step < 1 {
step = 1
}
step = pow2(step)
if step > 256 {
step = 256
}
ch := make(chan *childstate, 10)
//pad to step
rem := len(hashes) % step
l := len(hashes) / step
if rem != 0 {
l++
}
for i := 0; i < l; i++ {
end := (i + 1) * step
if end > len(hashes) {
end = len(hashes)
}
child := hashes[i*step : end]
go func(index int, h [][]byte) {
var subhash []byte
if len(h) != step {
subhash = getMerkleRootPad(h, step)
} else {
subhash = getMerkleRoot(h)
}
ch <- &childstate{
hash: subhash,
index: index,
}
}(i, child)
}
childlist := make([][]byte, l)
for i := 0; i < l; i++ {
sub := <-ch
childlist[sub.index] = sub.hash
}
return getMerkleRoot(childlist)
}
/*Computation This implements a constant-space merkle root/path calculator, limited to 2^32 leaves. */
//flage =1 只计算roothash flage =2 只计算branch flage =3 计算roothash 和 branch
func Computation(leaves [][]byte, flage int, branchpos uint32) (roothash []byte, mutated bool, pbranch [][]byte) {
......@@ -66,6 +201,7 @@ func Computation(leaves [][]byte, flage int, branchpos uint32) (roothash []byte,
var matchlevel uint32 = 0xff
mutated = false
var matchh bool
cache := make([]byte, 64)
for count, h = range leaves {
if (uint32(count) == branchpos) && (flage&2) != 0 {
......@@ -89,7 +225,7 @@ func Computation(leaves [][]byte, flage int, branchpos uint32) (roothash []byte,
mutated = true
}
//计算inner[level] + h 的hash值
h = GetHashFromTwoHash(inner[level], h)
h = GetHashFromTwoHash(cache, inner[level], h)
}
inner[level] = h
if matchh {
......@@ -105,7 +241,7 @@ func Computation(leaves [][]byte, flage int, branchpos uint32) (roothash []byte,
if (flage&2) != 0 && matchh {
branch = append(branch, h)
}
h = GetHashFromTwoHash(h, h)
h = GetHashFromTwoHash(cache, h, h)
count += (1 << level)
level++
// And propagate the result upwards accordingly.
......@@ -118,7 +254,7 @@ func Computation(leaves [][]byte, flage int, branchpos uint32) (roothash []byte,
matchh = true
}
}
h = GetHashFromTwoHash(inner[level], h)
h = GetHashFromTwoHash(cache, inner[level], h)
level++
}
}
......@@ -126,29 +262,13 @@ func Computation(leaves [][]byte, flage int, branchpos uint32) (roothash []byte,
}
//GetHashFromTwoHash 计算左右节点hash的父hash
func GetHashFromTwoHash(left []byte, right []byte) []byte {
func GetHashFromTwoHash(parent []byte, left []byte, right []byte) []byte {
if left == nil || right == nil {
return nil
}
leftlen := len(left)
rightlen := len(right)
parent := make([]byte, leftlen+rightlen)
copy(parent, left)
copy(parent[leftlen:], right)
hash := sha256.Sum256(parent)
parenthash := sha256.Sum256(hash[:])
return parenthash[:]
}
//GetMerkleRoot 获取merkle roothash
func GetMerkleRoot(leaves [][]byte) (roothash []byte) {
if leaves == nil {
return nil
}
proothash, _, _ := Computation(leaves, 1, 0)
return proothash
copy(parent[32:], right)
return common.Sha2Sum(parent)
}
//GetMerkleBranch 获取指定txindex的branch position 从0开始
......@@ -160,11 +280,12 @@ func GetMerkleBranch(leaves [][]byte, position uint32) [][]byte {
//GetMerkleRootFromBranch 通过branch 获取对应的roothash 用于指定txhash的proof证明
func GetMerkleRootFromBranch(merkleBranch [][]byte, leaf []byte, Index uint32) []byte {
hash := leaf
hashcache := make([]byte, 64)
for _, branch := range merkleBranch {
if (Index & 1) != 0 {
hash = GetHashFromTwoHash(branch, hash)
hash = GetHashFromTwoHash(hashcache, branch, hash)
} else {
hash = GetHashFromTwoHash(hash, branch)
hash = GetHashFromTwoHash(hashcache, hash, branch)
}
Index >>= 1
}
......
......@@ -6,7 +6,12 @@ package merkle
import (
"bytes"
"crypto/sha256"
"fmt"
"testing"
"github.com/33cn/chain33/common"
"github.com/stretchr/testify/assert"
)
//测试两个交易的roothash以及branch.获取bitcoin的99997 block作为验证
......@@ -248,3 +253,135 @@ func Test_SixTxMerkle(t *testing.T) {
}
}
}
func BenchmarkHashTwo(b *testing.B) {
b.ReportAllocs()
left := common.GetRandBytes(32, 32)
right := common.GetRandBytes(32, 32)
for i := 0; i < b.N; i++ {
getHashFromTwoHash(left, right)
}
}
func BenchmarkHashTwo2(b *testing.B) {
b.ReportAllocs()
left := common.GetRandBytes(32, 32)
right := common.GetRandBytes(32, 32)
cache := make([]byte, 64)
for i := 0; i < b.N; i++ {
GetHashFromTwoHash(cache, left, right)
}
}
//原来的版本更快,这个方案只是做一个性能测试的对比
func getHashFromTwoHash(left []byte, right []byte) []byte {
if left == nil || right == nil {
return nil
}
h := sha256.New()
h.Write(left)
h.Write(right)
hash1 := h.Sum(nil)
h.Reset()
h.Write(hash1)
return h.Sum(nil)
}
//优化办法:
//1. 减少内存分配
//2. 改进算法
var benchlen = 100000
func BenchmarkGetMerkelRoot(b *testing.B) {
b.ReportAllocs()
var hashlist [][]byte
for i := 0; i < benchlen; i++ {
key := common.GetRandBytes(32, 32)
hashlist = append(hashlist, key)
}
var prevroot []byte
for i := 0; i < b.N; i++ {
calc := make([][]byte, len(hashlist))
copy(calc, hashlist)
newroot := GetMerkleRoot(calc)
if prevroot != nil && !bytes.Equal(prevroot, newroot) {
b.Error("root is not the same")
}
prevroot = newroot
}
}
func BenchmarkGetMerkelRoot2(b *testing.B) {
b.ReportAllocs()
var hashlist [][]byte
for i := 0; i < benchlen; i++ {
key := common.GetRandBytes(32, 32)
hashlist = append(hashlist, key)
}
var prevroot []byte
for i := 0; i < b.N; i++ {
calc := make([][]byte, len(hashlist))
copy(calc, hashlist)
newroot, _, _ := Computation(calc, 1, 0)
if prevroot != nil && !bytes.Equal(prevroot, newroot) {
b.Error("root is not the same")
}
prevroot = newroot
}
}
func TestGetMerkelRoot1(t *testing.T) {
for i := 0; i < 2000; i++ {
ok := testGetMerkelRoot1(t, i)
if !ok {
t.Error("calc merkel root error", i)
return
}
}
}
func testGetMerkelRoot1(t *testing.T, testlen int) bool {
var hashlist [][]byte
for i := 0; i < testlen; i++ {
key := sha256.Sum256([]byte(fmt.Sprint(i)))
hashlist = append(hashlist, key[:])
}
hash1 := GetMerkleRoot(hashlist)
hashlist = nil
for i := 0; i < testlen; i++ {
key := sha256.Sum256([]byte(fmt.Sprint(i)))
hashlist = append(hashlist, key[:])
}
hash2 := getMerkleRoot(hashlist)
if !bytes.Equal(hash1, hash2) {
println("failed1")
return false
}
hashlist = nil
for i := 0; i < testlen; i++ {
key := sha256.Sum256([]byte(fmt.Sprint(i)))
hashlist = append(hashlist, key[:])
}
hash3, _, _ := Computation(hashlist, 1, 0)
if !bytes.Equal(hash1, hash3) {
println("failed2")
return false
}
return true
}
func TestLog2(t *testing.T) {
assert.Equal(t, log2(0), 0)
assert.Equal(t, log2(1), 1)
assert.Equal(t, log2(2), 1)
assert.Equal(t, log2(3), 1)
assert.Equal(t, log2(4), 2)
assert.Equal(t, log2(5), 2)
assert.Equal(t, log2(6), 2)
assert.Equal(t, log2(7), 2)
assert.Equal(t, log2(8), 3)
assert.Equal(t, log2(256), 8)
}
......@@ -75,5 +75,6 @@ func TestWalk(t *testing.T) {
assert.Equal(t, "222", iter.First().Value.(string))
assert.Equal(t, int64(1), iter.Last().Score)
assert.Equal(t, "111", iter.Last().Value.(string))
l.Print()
}
......@@ -32,6 +32,11 @@ func testDBGet(t *testing.T, db dbm.KV) {
v, err = db.Get([]byte("k1"))
assert.Nil(t, err)
assert.Equal(t, v, []byte("v11"))
stateDb := db.(*StateDB)
vs, err := stateDb.BatchGet([][]byte{[]byte("k1")})
assert.NoError(t, err)
assert.Equal(t, [][]byte{[]byte("v11")}, vs)
}
func TestStateDBTxGetOld(t *testing.T) {
......
......@@ -32,6 +32,7 @@ type executor struct {
gcli types.Chain33Client
execapi api.ExecutorAPI
receipts []*types.ReceiptData
execCache map[string]drivers.Driver
}
type executorCtx struct {
......@@ -60,6 +61,7 @@ func newExecutor(ctx *executorCtx, exec *Executor, localdb dbm.KVDB, txs []*type
receipts: receipts,
api: exec.qclient,
gcli: exec.grpccli,
execCache: make(map[string]drivers.Driver),
}
e.coinsAccount.SetDB(e.stateDB)
return e
......@@ -103,17 +105,18 @@ func (e *executor) processFee(tx *types.Transaction) (*types.Receipt, error) {
copyfrom := *accFrom
accFrom.Balance = accFrom.GetBalance() - tx.Fee
receiptBalance := &types.ReceiptAccountTransfer{Prev: &copyfrom, Current: accFrom}
e.coinsAccount.SaveAccount(accFrom)
return e.cutFeeReceipt(accFrom, receiptBalance), nil
set := e.coinsAccount.GetKVSet(accFrom)
e.coinsAccount.SaveKVSet(set)
return e.cutFeeReceipt(set, receiptBalance), nil
}
return nil, types.ErrNoBalance
}
func (e *executor) cutFeeReceipt(acc *types.Account, receiptBalance proto.Message) *types.Receipt {
func (e *executor) cutFeeReceipt(kvset []*types.KeyValue, receiptBalance proto.Message) *types.Receipt {
feelog := &types.ReceiptLog{Ty: types.TyLogFee, Log: types.Encode(receiptBalance)}
return &types.Receipt{
Ty: types.ExecPack,
KV: e.coinsAccount.GetKVSet(acc),
KV: kvset,
Logs: append([]*types.ReceiptLog{}, feelog),
}
}
......@@ -190,7 +193,6 @@ func (e *executor) execCheckTx(tx *types.Transaction, index int) error {
return types.ErrBalanceLessThanTenTimesFee
}
}
e.setEnv(exec)
return exec.CheckTx(tx, index)
}
......@@ -201,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) {
......@@ -219,8 +234,14 @@ func (e *executor) execDelLocal(tx *types.Transaction, r *types.ReceiptData, ind
}
func (e *executor) loadDriver(tx *types.Transaction, index int) (c drivers.Driver) {
exec := drivers.LoadDriverAllow(tx, index, e.height)
ename := string(tx.Execer)
exec, ok := e.execCache[ename]
if ok {
return exec
}
exec = drivers.LoadDriverAllow(tx, index, e.height)
e.setEnv(exec)
e.execCache[ename] = exec
return exec
}
......@@ -297,7 +318,6 @@ func (e *executor) execFee(tx *types.Transaction, index int) (*types.Receipt, er
feelog := &types.Receipt{Ty: types.ExecPack}
execer := string(tx.Execer)
ex := e.loadDriver(tx, index)
e.setEnv(ex)
//执行器名称 和 pubkey 相同,费用从内置的执行器中扣除,但是checkTx 中要过
//默认checkTx 中对这样的交易会返回
if bytes.Equal(address.ExecPubkey(execer), tx.GetSignature().GetPubkey()) {
......
......@@ -130,6 +130,14 @@ func (exec *Executor) SetQueueClient(qcli queue.Client) {
}
func (exec *Executor) procExecQuery(msg *queue.Message) {
//panic 处理
defer func() {
if r := recover(); r != nil {
elog.Error("panic error", "err", r)
msg.Reply(exec.client.NewMessage("", types.EventReceipts, types.ErrExecPanic))
return
}
}()
header, err := exec.qclient.GetLastHeader()
if err != nil {
msg.Reply(exec.client.NewMessage("", types.EventBlockChainQuery, err))
......@@ -169,6 +177,14 @@ func (exec *Executor) procExecQuery(msg *queue.Message) {
}
func (exec *Executor) procExecCheckTx(msg *queue.Message) {
//panic 处理
defer func() {
if r := recover(); r != nil {
elog.Error("panic error", "err", r)
msg.Reply(exec.client.NewMessage("", types.EventReceipts, types.ErrExecPanic))
return
}
}()
datas := msg.GetData().(*types.ExecTxList)
ctx := &executorCtx{
stateHash: datas.StateHash,
......@@ -205,6 +221,14 @@ func (exec *Executor) procExecCheckTx(msg *queue.Message) {
}
func (exec *Executor) procExecTxList(msg *queue.Message) {
//panic 处理
defer func() {
if r := recover(); r != nil {
elog.Error("panic error", "err", r)
msg.Reply(exec.client.NewMessage("", types.EventReceipts, types.ErrExecPanic))
return
}
}()
datas := msg.GetData().(*types.ExecTxList)
ctx := &executorCtx{
stateHash: datas.StateHash,
......@@ -279,6 +303,14 @@ func (exec *Executor) procExecTxList(msg *queue.Message) {
}
func (exec *Executor) procExecAddBlock(msg *queue.Message) {
//panic 处理
defer func() {
if r := recover(); r != nil {
elog.Error("panic error", "err", r)
msg.Reply(exec.client.NewMessage("", types.EventReceipts, types.ErrExecPanic))
return
}
}()
datas := msg.GetData().(*types.BlockDetail)
b := datas.Block
ctx := &executorCtx{
......@@ -341,6 +373,14 @@ func (exec *Executor) procExecAddBlock(msg *queue.Message) {
}
func (exec *Executor) procExecDelBlock(msg *queue.Message) {
//panic 处理
defer func() {
if r := recover(); r != nil {
elog.Error("panic error", "err", r)
msg.Reply(exec.client.NewMessage("", types.EventReceipts, types.ErrExecPanic))
return
}
}()
datas := msg.GetData().(*types.BlockDetail)
b := datas.Block
ctx := &executorCtx{
......@@ -408,4 +448,7 @@ func (exec *Executor) procExecDelBlock(msg *queue.Message) {
// Close close executor
func (exec *Executor) Close() {
elog.Info("exec module closed")
if exec.client != nil {
exec.client.Close()
}
}
......@@ -188,7 +188,7 @@ func TestExecBlock2(t *testing.T) {
txs := util.GenCoinsTxs(genkey, 2)
block2 := util.CreateNewBlock(block, txs)
detail, _, err := util.ExecBlock(mock33.GetClient(), block.StateHash, block2, false, true)
detail, _, err := util.ExecBlock(mock33.GetClient(), block.StateHash, block2, false, true, false)
if err != nil {
t.Error(err)
return
......@@ -205,7 +205,7 @@ func TestExecBlock2(t *testing.T) {
go func() {
txs := util.GenCoinsTxs(genkey, 2)
block3 := util.CreateNewBlock(block2, txs)
detail, _, err := util.ExecBlock(mock33.GetClient(), block2.StateHash, block3, false, true)
detail, _, err := util.ExecBlock(mock33.GetClient(), block2.StateHash, block3, false, true, false)
assert.Nil(t, err)
for _, Receipt := range detail.Receipts {
if Receipt.GetTy() != 2 {
......@@ -234,7 +234,7 @@ func TestSameTx(t *testing.T) {
newblock.Txs = append(newblock.Txs, newblock.Txs[2])
newblock.TxHash = merkle.CalcMerkleRoot(newblock.Txs)
assert.Equal(t, hash1, newblock.TxHash)
_, _, err := util.ExecBlock(mock33.GetClient(), nil, newblock, true, true)
_, _, err := util.ExecBlock(mock33.GetClient(), nil, newblock, true, true, false)
assert.Equal(t, types.ErrTxDup, err)
//情况2
......@@ -244,15 +244,17 @@ func TestSameTx(t *testing.T) {
newblock.Txs = append(newblock.Txs, newblock.Txs[4:]...)
newblock.TxHash = merkle.CalcMerkleRoot(newblock.Txs)
assert.Equal(t, hash1, newblock.TxHash)
_, _, err = util.ExecBlock(mock33.GetClient(), nil, newblock, true, true)
_, _, err = util.ExecBlock(mock33.GetClient(), nil, newblock, true, true, false)
assert.Equal(t, types.ErrTxDup, err)
}
func TestExecBlock(t *testing.T) {
mock33 := newMockNode()
defer mock33.Close()
block := util.CreateNoneBlock(mock33.GetGenesisKey(), 10)
util.ExecBlock(mock33.GetClient(), nil, block, false, true)
mock33.WaitHeight(0)
block0 := mock33.GetBlock(0)
block := util.CreateCoinsBlock(mock33.GetGenesisKey(), 10)
util.ExecBlock(mock33.GetClient(), block0.StateHash, block, false, true, false)
}
//区块执行性能更好的一个测试
......@@ -264,6 +266,7 @@ func TestExecBlock(t *testing.T) {
//区块执行新能测试
func BenchmarkExecBlock(b *testing.B) {
b.ReportAllocs()
mock33 := newMockNode()
defer mock33.Close()
block := util.CreateCoinsBlock(mock33.GetGenesisKey(), 10000)
......@@ -273,7 +276,7 @@ func BenchmarkExecBlock(b *testing.B) {
assert.Equal(b, int64(10000000000000000), account.Balance)
b.ResetTimer()
for i := 0; i < b.N; i++ {
util.ExecBlock(mock33.GetClient(), block0.StateHash, block, false, true)
util.ExecBlock(mock33.GetClient(), block0.StateHash, block, false, true, false)
}
}
......@@ -309,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{
......@@ -362,7 +368,7 @@ func TestExecLocalSameTime1(t *testing.T) {
txs = append(txs, util.CreateTxWithExecer(priv1, "demo2"))
txs = append(txs, util.CreateTxWithExecer(priv1, "demo2"))
block2 := util.CreateNewBlock(block, txs)
detail, _, err := util.ExecBlock(mock33.GetClient(), block.StateHash, block2, false, true)
detail, _, err := util.ExecBlock(mock33.GetClient(), block.StateHash, block2, false, true, false)
if err != nil {
t.Error(err)
return
......@@ -392,17 +398,20 @@ func TestExecLocalSameTime0(t *testing.T) {
txs = append(txs, util.CreateTxWithExecer(priv1, "demo2"))
txs = append(txs, util.CreateTxWithExecer(priv1, "demo2"))
block2 := util.CreateNewBlock(block, txs)
detail, _, err := util.ExecBlock(mock33.GetClient(), block.StateHash, block2, false, true)
detail, _, err := util.ExecBlock(mock33.GetClient(), block.StateHash, block2, false, true, false)
if err != nil {
t.Error(err)
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))
}
}
}
......@@ -425,7 +434,7 @@ func TestExecLocalSameTimeSetErrKey(t *testing.T) {
txs = append(txs, util.CreateTxWithExecer(priv1, "demo2"))
txs = append(txs, util.CreateTxWithExecer(priv1, "demo2"))
block2 := util.CreateNewBlock(block, txs)
detail, _, err := util.ExecBlock(mock33.GetClient(), block.StateHash, block2, false, true)
detail, _, err := util.ExecBlock(mock33.GetClient(), block.StateHash, block2, false, true, false)
if err != nil {
t.Error(err)
return
......
......@@ -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()
......
package executor
import (
"testing"
"time"
"github.com/33cn/chain33/types"
"github.com/33cn/chain33/util"
"github.com/stretchr/testify/assert"
)
func TestPlugin(t *testing.T) {
dir, ldb, kvdb := util.CreateTestDB()
defer util.CloseTestDB(dir, ldb)
ctx := &executorCtx{
stateHash: nil,
height: 1,
blocktime: time.Now().Unix(),
difficulty: 1,
mainHash: nil,
parentHash: nil,
}
var txs []*types.Transaction
addr, priv := util.Genaddress()
tx := util.CreateCoinsTx(priv, addr, types.Coin)
tx.Sign(types.SECP256K1, priv)
txs = append(txs, tx)
for _, plugin := range globalPlugins {
detail := &types.BlockDetail{
Block: &types.Block{Txs: txs},
Receipts: []*types.ReceiptData{{}},
}
executor := newExecutor(ctx, &Executor{}, kvdb, txs, nil)
_, _, err := plugin.CheckEnable(executor, false)
assert.NoError(t, err)
kvs, err := plugin.ExecLocal(executor, detail)
assert.NoError(t, err)
for _, kv := range kvs {
err = kvdb.Set(kv.Key, kv.Value)
assert.NoError(t, err)
}
_, err = plugin.ExecDelLocal(executor, detail)
assert.NoError(t, err)
}
}
func TestPluginBase(t *testing.T) {
base := new(pluginBase)
dir, ldb, kvdb := util.CreateTestDB()
defer util.CloseTestDB(dir, ldb)
ctx := &executorCtx{
stateHash: nil,
height: 0,
blocktime: time.Now().Unix(),
difficulty: 1,
mainHash: nil,
parentHash: nil,
}
executor := newExecutor(ctx, &Executor{}, kvdb, nil, nil)
_, _, err := base.checkFlag(executor, nil, true)
assert.NoError(t, err)
k := []byte("test")
v := types.Encode(&types.Int64{})
err = kvdb.Set(k, v)
assert.NoError(t, err)
_, _, err = base.checkFlag(executor, k, true)
assert.NoError(t, err)
}
......@@ -128,7 +128,10 @@ func (s *StateDB) get(key []byte) ([]byte, error) {
}
query := &types.StoreGet{StateHash: s.stateHash, Keys: [][]byte{key}}
msg := s.client.NewMessage("store", types.EventStoreGet, query)
s.client.Send(msg, true)
err := s.client.Send(msg, true)
if err != nil {
return nil, err
}
resp, err := s.client.Wait(msg)
if err != nil {
panic(err) //no happen for ever
......
......@@ -176,6 +176,9 @@ func (client *client) Close() {
client.wg.Wait()
atomic.StoreInt32(&client.isClosed, 1)
close(client.Recv())
for msg := range client.Recv() {
msg.Reply(client.NewMessage(msg.Topic, msg.Ty, types.ErrChannelClosed))
}
}
// CloseQueue 关闭消息队列
......
......@@ -10,6 +10,7 @@ import (
"time"
"github.com/33cn/chain33/types"
"github.com/stretchr/testify/assert"
)
func init() {
......@@ -187,11 +188,17 @@ func TestClientClose(t *testing.T) {
}()
msg := client.NewMessage("mempool", types.EventTx, "hello")
err := client.Send(msg, true)
if err == types.ErrChannelClosed {
return
}
if err != nil { //chan is closed
t.Error(err)
return
}
_, err = client.Wait(msg)
if err == types.ErrChannelClosed {
return
}
if err != nil {
t.Error(err)
return
......@@ -213,6 +220,44 @@ func TestPrintMessage(t *testing.T) {
t.Log(msg)
}
func TestMessage_ReplyErr(t *testing.T) {
q := New("channel")
assert.Equal(t, "channel", q.Name())
//接收消息
go func() {
client := q.Client()
client.Sub("mempool")
for msg := range client.Recv() {
if msg.Data == nil {
msg.ReplyErr("test", fmt.Errorf("test error"))
break
}
msg.Reply(NewMessage(0, "mempool", types.EventReply, types.Reply{IsOk: true, Msg: []byte("test ok")}))
}
}()
//发送消息
go func() {
client := q.Client()
msg := client.NewMessage("mempool", types.EventTx, "hello")
err := client.Send(msg, true)
if err != nil { //chan is closed
t.Error(err)
return
}
msg = client.NewMessage("mempool", types.EventTx, nil)
err = client.Send(msg, true)
if err != nil {
t.Error(err)
return
}
client.CloseQueue()
}()
q.Start()
}
func TestChanSubCallback(t *testing.T) {
q := New("channel")
client := q.Client()
......@@ -423,3 +468,53 @@ func BenchmarkChanSubCallback2(b *testing.B) {
client.Reply(msg)
}
}
func TestChannelClose(t *testing.T) {
//send timeout and recv timeout
q := New("channel")
//mempool
done := make(chan struct{}, 1)
go func() {
client := q.Client()
client.Sub("mempool")
for {
select {
case msg := <-client.Recv():
if msg == nil {
return
}
if msg.Ty == types.EventTx {
msg.Reply(client.NewMessage("mempool", types.EventReply, types.Reply{IsOk: true, Msg: []byte("word")}))
}
case <-done:
client.Close()
return
}
}
}()
client := q.Client()
go q.Start()
//rpc 模块 会向其他模块发送消息,自己本身不需要订阅消息
go func() {
done <- struct{}{}
}()
for i := 0; i < 10000; i++ {
msg := client.NewMessage("mempool", types.EventTx, "hello")
err := client.SendTimeout(msg, true, 0)
if err == types.ErrChannelClosed {
return
}
if err != nil {
t.Error(err)
return
}
_, err = client.Wait(msg)
if err == types.ErrChannelClosed {
return
}
if err != nil {
t.Error(err)
}
}
}
......@@ -5,9 +5,9 @@
package rpc
import (
"testing"
"encoding/hex"
"fmt"
"testing"
"github.com/33cn/chain33/account"
"github.com/33cn/chain33/client/mocks"
......@@ -67,6 +67,12 @@ func testCreateRawTransactionTo(t *testing.T) {
client := newTestChannelClient()
rawtx, err := client.CreateRawTransaction(&tx)
assert.NoError(t, err)
reqDecode := &types.ReqDecodeRawTransaction{TxHex: hex.EncodeToString(rawtx)}
_, err = client.DecodeRawTransaction(reqDecode)
assert.NoError(t, err)
assert.Nil(t, err)
var mytx types.Transaction
err = types.Decode(rawtx, &mytx)
......@@ -343,21 +349,30 @@ func TestChannelClient_GetBalance(t *testing.T) {
testChannelClient_GetBalanceOther(t)
}
// func TestChannelClient_GetTotalCoins(t *testing.T) {
// client := newTestChannelClient()
// data, err := client.GetTotalCoins(nil)
// assert.NotNil(t, err)
// assert.Nil(t, data)
//
// // accountdb =
// token := &types.ReqGetTotalCoins{
// Symbol: "CNY",
// StateHash: []byte("1234"),
// StartKey: []byte("sad"),
// Count: 1,
// Execer: "coin",
// }
// data, err = client.GetTotalCoins(token)
// assert.NotNil(t, data)
// assert.Nil(t, err)
// }
func TestChannelClient_GetTotalCoins(t *testing.T) {
client := new(channelClient)
api := new(mocks.QueueProtocolAPI)
client.Init(&qmock.Client{}, api)
api.On("StoreGetTotalCoins", mock.Anything).Return(&types.ReplyGetTotalCoins{}, nil)
_, err := client.GetTotalCoins(&types.ReqGetTotalCoins{})
assert.NoError(t, err)
// accountdb =
//token := &types.ReqGetTotalCoins{
// Symbol: "CNY",
// StateHash: []byte("1234"),
// StartKey: []byte("sad"),
// Count: 1,
// Execer: "coin",
//}
//data, err = client.GetTotalCoins(token)
//assert.NotNil(t, data)
//assert.Nil(t, err)
}
func TestChannelClient_CreateNoBalanceTransaction(t *testing.T) {
client := new(channelClient)
in := &types.NoBalanceTx{}
_, err := client.CreateNoBalanceTransaction(in)
assert.NoError(t, err)
}
......@@ -103,3 +103,21 @@ func TestNewParaClient(t *testing.T) {
assert.Nil(t, err)
assert.Equal(t, reply.Hash, []byte("hello"))
}
func TestNewMainChainClient(t *testing.T) {
grpcClient1, err := grpcclient.NewMainChainClient("")
assert.Nil(t, err)
grpcClient2, err := grpcclient.NewMainChainClient("")
assert.Nil(t, err)
if grpcClient1 != grpcClient2 {
t.Error("grpc client is the same")
}
grpcClient3, err := grpcclient.NewMainChainClient("127.0.0.1")
assert.Nil(t, err)
grpcClient4, err := grpcclient.NewMainChainClient("127.0.0.1")
assert.Nil(t, err)
if grpcClient3 == grpcClient4 {
t.Error("grpc client is not the same")
}
}
package grpcclient
import (
"sync"
"github.com/33cn/chain33/types"
"google.golang.org/grpc"
)
......@@ -8,8 +10,17 @@ import (
// paraChainGrpcRecSize 平行链receive最大100M
const paraChainGrpcRecSize = 100 * 1024 * 1024
var mu sync.Mutex
var defaultClient types.Chain33Client
//NewMainChainClient 创建一个平行链的 主链 grpc chain33 客户端
func NewMainChainClient(grpcaddr string) (types.Chain33Client, error) {
mu.Lock()
defer mu.Unlock()
if grpcaddr == "" && defaultClient != nil {
return defaultClient, nil
}
paraRemoteGrpcClient := types.Conf("config.consensus.sub.para").GStr("ParaRemoteGrpcClient")
if grpcaddr != "" {
paraRemoteGrpcClient = grpcaddr
......@@ -24,5 +35,8 @@ func NewMainChainClient(grpcaddr string) (types.Chain33Client, error) {
return nil, err
}
grpcClient := types.NewChain33Client(conn)
if grpcaddr == "" {
defaultClient = grpcClient
}
return grpcClient, nil
}
......@@ -1069,3 +1069,99 @@ func TestReWriteRawTx(t *testing.T) {
assert.Equal(t, int64(130000000000), tx.Expire)
assert.Equal(t, in.To, tx.To)
}
func TestGrpc_CreateNoBalanceTransaction(t *testing.T) {
_, err := g.CreateNoBalanceTransaction(getOkCtx(), &pb.NoBalanceTx{})
assert.NoError(t, err)
}
func TestGrpc_CreateRawTransaction(t *testing.T) {
_, err := g.CreateRawTransaction(getOkCtx(), &pb.CreateTx{})
assert.NoError(t, err)
}
func TestGrpc_CreateTransaction(t *testing.T) {
_, err := g.CreateTransaction(getOkCtx(), &pb.CreateTxIn{Execer: []byte("coins")})
assert.Equal(t, err, types.ErrActionNotSupport)
}
func TestGrpc_CreateRawTxGroup(t *testing.T) {
_, err := g.CreateRawTxGroup(getOkCtx(), &pb.CreateTransactionGroup{})
assert.Equal(t, types.ErrTxGroupCountLessThanTwo, err)
}
func TestGrpc_SendRawTransaction(t *testing.T) {
transfer := &types.Transaction{
Execer: []byte(types.ExecName("ticket")),
}
payload := types.Encode(transfer)
qapi.On("SendTx", mock.Anything).Return(nil, nil)
var param = &types.SignedTx{
Unsign: payload,
Sign: []byte("123"),
Pubkey: []byte("123"),
Ty: 1,
}
_, err := g.SendRawTransaction(getOkCtx(), param)
assert.NoError(t, err)
}
func TestGrpc_GetAddrOverview(t *testing.T) {
_, err := g.GetAddrOverview(getOkCtx(), &types.ReqAddr{})
assert.Equal(t, err, types.ErrInvalidAddress)
}
func TestGrpc_GetBalance(t *testing.T) {
qapi.On("StoreGet", mock.Anything).Return(nil, types.ErrInvalidParam)
_, err := g.GetBalance(getOkCtx(), &types.ReqBalance{})
assert.Equal(t, err, types.ErrInvalidParam)
}
func TestGrpc_GetAllExecBalance(t *testing.T) {
_, err := g.GetAllExecBalance(getOkCtx(), &pb.ReqAllExecBalance{})
assert.Equal(t, err, types.ErrInvalidAddress)
}
func TestGrpc_QueryConsensus(t *testing.T) {
qapi.On("QueryConsensus", mock.Anything).Return(&types.ReqString{Data: "test"}, nil)
_, err := g.QueryConsensus(getOkCtx(), &pb.ChainExecutor{})
assert.NoError(t, err)
}
func TestGrpc_ExecWallet(t *testing.T) {
qapi.On("ExecWallet", mock.Anything).Return(&types.ReqString{Data: "test"}, nil)
_, err := g.ExecWallet(getOkCtx(), &pb.ChainExecutor{})
assert.NoError(t, err)
}
func TestGrpc_GetLastBlockSequence(t *testing.T) {
qapi.On("GetLastBlockSequence", mock.Anything).Return(nil, nil)
_, err := g.GetLastBlockSequence(getOkCtx(), &types.ReqNil{})
assert.NoError(t, err)
}
func TestGrpc_GetBlockByHashes(t *testing.T) {
qapi.On("GetBlockByHashes", mock.Anything, mock.Anything, mock.Anything).Return(nil, nil)
_, err := g.GetBlockByHashes(getOkCtx(), &types.ReqHashes{})
assert.NoError(t, err)
}
func TestGrpc_GetSequenceByHash(t *testing.T) {
qapi.On("GetSequenceByHash", mock.Anything, mock.Anything, mock.Anything).Return(nil, nil)
_, err := g.GetSequenceByHash(getOkCtx(), &pb.ReqHash{})
assert.NoError(t, err)
}
func TestGrpc_SignRawTx(t *testing.T) {
qapi.On("SignRawTx", mock.Anything, mock.Anything, mock.Anything).Return(nil, nil)
_, err := g.SignRawTx(getOkCtx(), &types.ReqSignRawTx{})
assert.NoError(t, err)
}
func TestGrpc_QueryRandNum(t *testing.T) {
qapi.On("Query", mock.Anything, mock.Anything, mock.Anything).Return(&pb.ReplyHash{Hash: []byte("test")}, nil)
_, err := g.QueryRandNum(getOkCtx(), &pb.ReqRandHash{})
assert.NoError(t, err)
}
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