Commit e2d4fe05 authored by vipwzw's avatar vipwzw

update_chain33_1212

parent cc8b634f
...@@ -84,7 +84,7 @@ git merge upstream/master ...@@ -84,7 +84,7 @@ git merge upstream/master
git fetch upstream git fetch upstream
git checkout master git checkout master
git merge upstream/master git merge upstream/master
git branch -a "fixbug_ci" git branch -b "fixbug_ci"
``` ```
* 开发完成后, push 到 `vipwzw/chain33` * 开发完成后, push 到 `vipwzw/chain33`
......
...@@ -207,6 +207,9 @@ func (chain *BlockChain) SetQueueClient(client queue.Client) { ...@@ -207,6 +207,9 @@ func (chain *BlockChain) SetQueueClient(client queue.Client) {
go chain.ProcRecvMsg() go chain.ProcRecvMsg()
} }
//Wait for ready
func (chain *BlockChain) Wait() {}
//GetStore only used for test //GetStore only used for test
func (chain *BlockChain) GetStore() *BlockStore { func (chain *BlockChain) GetStore() *BlockStore {
return chain.blockStore return chain.blockStore
......
...@@ -8,7 +8,7 @@ set -o pipefail ...@@ -8,7 +8,7 @@ set -o pipefail
# os: ubuntu16.04 x64 # os: ubuntu16.04 x64
#chain33 dapp autotest root directory #chain33 dapp autotest root directory
declare -a Chain33AutoTestDirs=("system" "plugin" "vendor/github.com/33cn/chain33/system") declare -a Chain33AutoTestDirs=("system" "plugin" "vendor/github.com/33cn/chain33/system" "vendor/github.com/33cn/plugin/plugin")
#copy auto test to specific directory #copy auto test to specific directory
# check args # check args
......
...@@ -64,10 +64,24 @@ certFile="cert.pem" ...@@ -64,10 +64,24 @@ certFile="cert.pem"
keyFile="key.pem" keyFile="key.pem"
[mempool] [mempool]
name="timeline"
poolCacheSize=10240 poolCacheSize=10240
minTxFee=1000000 minTxFee=100000
maxTxNumPerAccount=10000 maxTxNumPerAccount=10000
[mempool.sub.timeline]
poolCacheSize=10240
minTxFee=100000
maxTxNumPerAccount=10000
[mempool.sub.trade]
poolCacheSize=10240
minTxFee=100000
maxTxNumPerAccount=10000
timeParam=1 #时间占价格比例
priceConstant=1 #一个合适的常量
pricePower=0 #手续费占常量比例
[consensus] [consensus]
name="solo" name="solo"
minerstart=true minerstart=true
......
...@@ -64,10 +64,24 @@ certFile="cert.pem" ...@@ -64,10 +64,24 @@ certFile="cert.pem"
keyFile="key.pem" keyFile="key.pem"
[mempool] [mempool]
name="timeline"
poolCacheSize=10240 poolCacheSize=10240
minTxFee=100000 minTxFee=100000
maxTxNumPerAccount=10000 maxTxNumPerAccount=10000
[mempool.sub.timeline]
poolCacheSize=10240
minTxFee=100000
maxTxNumPerAccount=10000
[mempool.sub.trade]
poolCacheSize=10240
minTxFee=100000
maxTxNumPerAccount=10000
timeParam=1 #时间占价格比例
priceConstant=1 #一个合适的常量
pricePower=0 #手续费占常量比例
[consensus] [consensus]
name="solo" name="solo"
minerstart=true minerstart=true
......
...@@ -18,13 +18,24 @@ import ( ...@@ -18,13 +18,24 @@ import (
var addrSeed = []byte("address seed bytes for public key") var addrSeed = []byte("address seed bytes for public key")
var addressCache *lru.Cache var addressCache *lru.Cache
var checkAddressCache *lru.Cache var checkAddressCache *lru.Cache
var multisignCache *lru.Cache
var multiCheckAddressCache *lru.Cache
var errVersion = errors.New("check version error")
//MaxExecNameLength 执行器名最大长度 //MaxExecNameLength 执行器名最大长度
const MaxExecNameLength = 100 const MaxExecNameLength = 100
//NormalVer 普通地址的版本号
const NormalVer byte = 0
//MultiSignVer 多重签名地址的版本号
const MultiSignVer byte = 5
func init() { func init() {
multisignCache, _ = lru.New(10240)
addressCache, _ = lru.New(10240) addressCache, _ = lru.New(10240)
checkAddressCache, _ = lru.New(10240) checkAddressCache, _ = lru.New(10240)
multiCheckAddressCache, _ = lru.New(10240)
} }
//ExecPubKey 计算公钥 //ExecPubKey 计算公钥
...@@ -44,12 +55,23 @@ func ExecAddress(name string) string { ...@@ -44,12 +55,23 @@ func ExecAddress(name string) string {
if value, ok := addressCache.Get(name); ok { if value, ok := addressCache.Get(name); ok {
return value.(string) return value.(string)
} }
addr := PubKeyToAddress(ExecPubkey(name)) addr := GetExecAddress(name)
addrstr := addr.String() addrstr := addr.String()
addressCache.Add(name, addrstr) addressCache.Add(name, addrstr)
return addrstr return addrstr
} }
//MultiSignAddress create a multi sign address
func MultiSignAddress(pubkey []byte) string {
if value, ok := multisignCache.Get(string(pubkey)); ok {
return value.(string)
}
addr := HashToAddress(MultiSignVer, pubkey)
addrstr := addr.String()
multisignCache.Add(string(pubkey), addrstr)
return addrstr
}
//ExecPubkey 计算公钥 //ExecPubkey 计算公钥
func ExecPubkey(name string) []byte { func ExecPubkey(name string) []byte {
if len(name) > MaxExecNameLength { if len(name) > MaxExecNameLength {
...@@ -64,35 +86,27 @@ func ExecPubkey(name string) []byte { ...@@ -64,35 +86,27 @@ func ExecPubkey(name string) []byte {
//GetExecAddress 获取地址 //GetExecAddress 获取地址
func GetExecAddress(name string) *Address { func GetExecAddress(name string) *Address {
if len(name) > MaxExecNameLength { hash := ExecPubkey(name)
panic("name too long")
}
var bname [200]byte
buf := append(bname[:0], addrSeed...)
buf = append(buf, []byte(name)...)
hash := common.Sha2Sum(buf)
addr := PubKeyToAddress(hash[:]) addr := PubKeyToAddress(hash[:])
return addr return addr
} }
//PubKeyToAddress 公钥转为地址 //PubKeyToAddress 公钥转为地址
func PubKeyToAddress(in []byte) *Address { func PubKeyToAddress(in []byte) *Address {
return HashToAddress(NormalVer, in)
}
//HashToAddress hash32 to address
func HashToAddress(version byte, in []byte) *Address {
a := new(Address) a := new(Address)
a.Pubkey = make([]byte, len(in)) a.Pubkey = make([]byte, len(in))
copy(a.Pubkey[:], in[:]) copy(a.Pubkey[:], in[:])
a.Version = 0 a.Version = version
a.Hash160 = common.Rimp160AfterSha256(in) a.Hash160 = common.Rimp160AfterSha256(in)
return a return a
} }
//CheckAddress 检查地址 func checkAddress(ver byte, addr string) (e error) {
func CheckAddress(addr string) (e error) {
if value, ok := checkAddressCache.Get(addr); ok {
if value == nil {
return nil
}
return value.(error)
}
dec := base58.Decode(addr) dec := base58.Decode(addr)
if dec == nil { if dec == nil {
e = errors.New("Cannot decode b58 string '" + addr + "'") e = errors.New("Cannot decode b58 string '" + addr + "'")
...@@ -110,6 +124,34 @@ func CheckAddress(addr string) (e error) { ...@@ -110,6 +124,34 @@ func CheckAddress(addr string) (e error) {
e = errors.New("Address Checksum error") e = errors.New("Address Checksum error")
} }
} }
if dec[0] != ver {
e = errVersion
}
return e
}
//CheckMultiSignAddress 检查多重签名地址的有效性
func CheckMultiSignAddress(addr string) (e error) {
if value, ok := multiCheckAddressCache.Get(addr); ok {
if value == nil {
return nil
}
return value.(error)
}
e = checkAddress(MultiSignVer, addr)
multiCheckAddressCache.Add(addr, e)
return
}
//CheckAddress 检查地址
func CheckAddress(addr string) (e error) {
if value, ok := checkAddressCache.Get(addr); ok {
if value == nil {
return nil
}
return value.(error)
}
e = checkAddress(NormalVer, addr)
checkAddressCache.Add(addr, e) checkAddressCache.Add(addr, e)
return return
} }
......
...@@ -12,27 +12,42 @@ import ( ...@@ -12,27 +12,42 @@ import (
"time" "time"
"github.com/33cn/chain33/common/crypto" "github.com/33cn/chain33/common/crypto"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
_ "github.com/33cn/chain33/system/crypto/init" _ "github.com/33cn/chain33/system/crypto/init"
) )
func TestAddress(t *testing.T) { func genkey() crypto.PrivKey {
c, err := crypto.New("secp256k1") c, err := crypto.New("secp256k1")
if err != nil { if err != nil {
t.Error(err) panic(err)
return
} }
key, err := c.GenKey() key, err := c.GenKey()
if err != nil { if err != nil {
t.Error(err) panic(err)
return
} }
return key
}
func TestAddress(t *testing.T) {
key := genkey()
t.Logf("%X", key.Bytes()) t.Logf("%X", key.Bytes())
addr := PubKeyToAddress(key.PubKey().Bytes()) addr := PubKeyToAddress(key.PubKey().Bytes())
t.Log(addr) t.Log(addr)
} }
func TestMultiSignAddress(t *testing.T) {
key := genkey()
addr1 := MultiSignAddress(key.PubKey().Bytes())
addr := MultiSignAddress(key.PubKey().Bytes())
assert.Equal(t, addr1, addr)
err := CheckAddress(addr)
assert.Equal(t, errVersion, err)
err = CheckMultiSignAddress(addr)
assert.Nil(t, err)
t.Log(addr)
}
func TestPubkeyToAddress(t *testing.T) { func TestPubkeyToAddress(t *testing.T) {
pubkey := "024a17b0c6eb3143839482faa7e917c9b90a8cfe5008dff748789b8cea1a3d08d5" pubkey := "024a17b0c6eb3143839482faa7e917c9b90a8cfe5008dff748789b8cea1a3d08d5"
b, err := hex.DecodeString(pubkey) b, err := hex.DecodeString(pubkey)
...@@ -61,6 +76,14 @@ func TestCheckAddress(t *testing.T) { ...@@ -61,6 +76,14 @@ func TestCheckAddress(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
} }
func TestExecAddress(t *testing.T) {
assert.Equal(t, "16htvcBNSEA7fZhAdLJphDwQRQJaHpyHTp", ExecAddress("ticket"))
assert.Equal(t, "16htvcBNSEA7fZhAdLJphDwQRQJaHpyHTp", ExecAddress("ticket"))
addr, err := NewAddrFromString(ExecAddress("ticket"))
assert.Nil(t, err)
assert.Equal(t, addr.Version, NormalVer)
}
func BenchmarkExecAddress(b *testing.B) { func BenchmarkExecAddress(b *testing.B) {
start := time.Now().UnixNano() / 1000000 start := time.Now().UnixNano() / 1000000
fmt.Println(start) fmt.Println(start)
......
package listmap
import (
"container/list"
"github.com/33cn/chain33/types"
)
//ListMap list 和 map 组合的一个数据结构体
type ListMap struct {
m map[string]*list.Element
l *list.List
}
//New 创建一个新的数据结构
func New() *ListMap {
return &ListMap{
m: make(map[string]*list.Element),
l: list.New(),
}
}
//Size 结构中的item 数目
func (lm *ListMap) Size() int {
return len(lm.m)
}
//Exist 是否存在这个元素
func (lm *ListMap) Exist(key string) bool {
_, ok := lm.m[key]
return ok
}
//GetItem 通过key 获取这个 item
func (lm *ListMap) GetItem(key string) (interface{}, error) {
item, ok := lm.m[key]
if !ok {
return nil, types.ErrNotFound
}
return item.Value, nil
}
//Push 在队伍尾部插入
func (lm *ListMap) Push(key string, value interface{}) {
if elm, ok := lm.m[key]; ok {
elm.Value = value
return
}
elm := lm.l.PushBack(value)
lm.m[key] = elm
}
//GetTop 获取队列头部的数据
func (lm *ListMap) GetTop() interface{} {
elm := lm.l.Front()
if elm == nil {
return nil
}
return elm.Value
}
//Remove 删除某个key
func (lm *ListMap) Remove(key string) interface{} {
if elm, ok := lm.m[key]; ok {
value := lm.l.Remove(elm)
delete(lm.m, key)
return value
}
return nil
}
//Walk 遍历整个结构,如果cb 返回false 那么停止遍历
func (lm *ListMap) Walk(cb func(value interface{}) bool) {
for e := lm.l.Front(); e != nil; e = e.Next() {
if cb == nil {
return
}
if !cb(e.Value) {
return
}
}
}
package listmap
import (
"testing"
"github.com/33cn/chain33/types"
"github.com/stretchr/testify/assert"
)
func TestListMap(t *testing.T) {
l := New()
l.Push("1", "1")
assert.Equal(t, 1, l.Size())
l.Push("1", "11")
assert.Equal(t, 1, l.Size())
value, err := l.GetItem("1")
assert.Equal(t, err, nil)
assert.Equal(t, "11", value.(string))
l.Remove("1")
assert.Equal(t, 0, l.Size())
assert.Equal(t, false, l.Exist("1"))
v := l.GetTop()
assert.Equal(t, nil, v)
_, err = l.GetItem("11")
assert.Equal(t, types.ErrNotFound, err)
l.Push("1", "11")
assert.Equal(t, true, l.Exist("1"))
l.Push("2", "2")
assert.Equal(t, "11", l.GetTop().(string))
var data [2]string
i := 0
l.Walk(func(value interface{}) bool {
data[i] = value.(string)
i++
return true
})
assert.Equal(t, data[0], "11")
assert.Equal(t, data[1], "2")
var data2 [2]string
i = 0
l.Walk(func(value interface{}) bool {
data2[i] = value.(string)
i++
return false
})
assert.Equal(t, data2[0], "11")
assert.Equal(t, data2[1], "")
l.Walk(nil)
l.Remove("xxxx")
}
// 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 mempool
import (
"container/list"
"github.com/33cn/chain33/types"
)
//--------------------------------------------------------------------------------
// Module txCache
type txCache struct {
size int
txMap map[string]*list.Element
txList *list.List
txFrontTen []*types.Transaction
accMap map[string][]*types.Transaction
}
// Item 为Mempool中包装交易的数据结构
type Item struct {
value *types.Transaction
priority int64
enterTime int64
}
// NewTxCache初始化txCache
func newTxCache(cacheSize int64) *txCache {
return &txCache{
size: int(cacheSize),
txMap: make(map[string]*list.Element, cacheSize),
txList: list.New(),
txFrontTen: make([]*types.Transaction, 0),
accMap: make(map[string][]*types.Transaction),
}
}
// txCache.TxNumOfAccount返回账户在Mempool中交易数量
func (cache *txCache) TxNumOfAccount(addr string) int64 {
return int64(len(cache.accMap[addr]))
}
// txCache.Exists判断txCache中是否存在给定tx
func (cache *txCache) Exists(hash []byte) bool {
_, exists := cache.txMap[string(hash)]
return exists
}
// txCache.Push把给定tx添加到txCache;如果tx已经存在txCache中或Mempool已满则返回对应error
func (cache *txCache) Push(tx *types.Transaction) error {
hash := tx.Hash()
if cache.Exists(hash) {
addedItem, ok := cache.txMap[string(hash)].Value.(*Item)
if !ok {
mlog.Error("mempoolItemError", "item", cache.txMap[string(hash)].Value)
return types.ErrTxExist
}
addedTime := addedItem.enterTime
if types.Now().Unix()-addedTime < mempoolDupResendInterval {
return types.ErrTxExist
}
// 超过2分钟之后的重发交易返回nil,再次发送给P2P,但是不再次加入mempool
// 并修改其enterTime,以避免该交易一直在节点间被重发
newEnterTime := types.Now().Unix()
resendItem := &Item{value: tx, priority: tx.Fee, enterTime: newEnterTime}
newItem := cache.txList.InsertAfter(resendItem, cache.txMap[string(hash)])
cache.txList.Remove(cache.txMap[string(hash)])
cache.txMap[string(hash)] = newItem
// ------------------
return nil
}
if cache.txList.Len() >= cache.size {
return types.ErrMemFull
}
it := &Item{value: tx, priority: tx.Fee, enterTime: types.Now().Unix()}
txElement := cache.txList.PushBack(it)
cache.txMap[string(hash)] = txElement
// 账户交易数量
accountAddr := tx.From()
cache.accMap[accountAddr] = append(cache.accMap[accountAddr], tx)
if len(cache.txFrontTen) >= 10 {
cache.txFrontTen = cache.txFrontTen[len(cache.txFrontTen)-9:]
}
cache.txFrontTen = append(cache.txFrontTen, tx)
return nil
}
// txCache.GetLatestTx返回最新十条加入到txCache的交易
func (cache *txCache) GetLatestTx() []*types.Transaction {
return cache.txFrontTen
}
// txCache.Remove移除txCache中给定tx
func (cache *txCache) Remove(hash []byte) {
value := cache.txList.Remove(cache.txMap[string(hash)])
delete(cache.txMap, string(hash))
// 账户交易数量减1
if value == nil {
return
}
tx := value.(*Item).value
addr := tx.From()
if cache.TxNumOfAccount(addr) > 0 {
cache.AccountTxNumDecrease(addr, hash)
}
}
// txCache.Size返回txCache中已存tx数目
func (cache *txCache) Size() int {
return cache.txList.Len()
}
// txCache.SetSize用来设置Mempool容量
func (cache *txCache) SetSize(newSize int) {
if cache.txList.Len() > 0 {
panic("only can set a empty size")
}
cache.size = newSize
}
// txCache.GetAccTxs用来获取对应账户地址(列表)中的全部交易详细信息
func (cache *txCache) GetAccTxs(addrs *types.ReqAddrs) *types.TransactionDetails {
res := &types.TransactionDetails{}
for _, addr := range addrs.Addrs {
if value, ok := cache.accMap[addr]; ok {
for _, v := range value {
txAmount, err := v.Amount()
if err != nil {
// continue
txAmount = 0
}
res.Txs = append(res.Txs,
&types.TransactionDetail{
Tx: v,
Amount: txAmount,
Fromaddr: addr,
ActionName: v.ActionName(),
})
}
}
}
return res
}
// txCache.AccountTxNumDecrease根据交易哈希删除对应账户的对应交易
func (cache *txCache) AccountTxNumDecrease(addr string, hash []byte) {
if value, ok := cache.accMap[addr]; ok {
for i, t := range value {
if string(t.Hash()) == string(hash) {
cache.accMap[addr] = append(cache.accMap[addr][:i], cache.accMap[addr][i+1:]...)
if len(cache.accMap[addr]) == 0 {
delete(cache.accMap, addr)
}
return
}
}
}
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package mempool package mempool
//mempool 模块的功能:实现交易暂存的功能。主要是解决共识模块可能比rpc模块速度慢的问题。 //交易打包排序相关的模块
//模块的接口的设计: //模块功能:模块主要的功能是实现共识排序的功能,包括完整的共识的实现。
//接口设计:
// 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 mempool 实现交易暂存的功能
package mempool package mempool
import ( import (
"sync"
"sync/atomic"
"time"
"github.com/33cn/chain33/common"
clog "github.com/33cn/chain33/common/log"
log "github.com/33cn/chain33/common/log/log15"
"github.com/33cn/chain33/queue" "github.com/33cn/chain33/queue"
"github.com/33cn/chain33/system/mempool"
"github.com/33cn/chain33/types" "github.com/33cn/chain33/types"
"github.com/hashicorp/golang-lru"
) )
// SetLogLevel set log level // New new mempool queue module
func SetLogLevel(level string) { func New(cfg *types.Mempool, sub map[string][]byte) queue.Module {
clog.SetLogLevel(level) con, err := mempool.Load(cfg.Name)
}
// DisableLog disableLog
func DisableLog() {
mlog.SetHandler(log.DiscardHandler())
}
// Mempool mempool module
type Mempool struct {
proxyMtx sync.Mutex
cache *txCache
in chan queue.Message
out <-chan queue.Message
client queue.Client
header *types.Header
minFee int64
addedTxs *lru.Cache
sync bool
cfg *types.MemPool
poolHeader chan struct{}
isclose int32
wg sync.WaitGroup
done chan struct{}
removeBlockTicket *time.Ticker
}
// New new mempool
func New(cfg *types.MemPool) *Mempool {
pool := &Mempool{}
initConfig(cfg)
pool.cache = newTxCache(poolCacheSize)
pool.in = make(chan queue.Message)
pool.out = make(<-chan queue.Message)
pool.done = make(chan struct{})
pool.minFee = cfg.MinTxFee
pool.addedTxs, _ = lru.New(mempoolAddedTxSize)
pool.cfg = cfg
pool.poolHeader = make(chan struct{}, 2)
pool.removeBlockTicket = time.NewTicker(time.Minute)
return pool
}
func initConfig(cfg *types.MemPool) {
if cfg.PoolCacheSize > 0 {
poolCacheSize = cfg.PoolCacheSize
}
if cfg.MaxTxNumPerAccount > 0 {
maxTxNumPerAccount = cfg.MaxTxNumPerAccount
}
}
// Resize 设置Mempool容量
func (mem *Mempool) Resize(size int) {
mem.proxyMtx.Lock()
mem.cache.SetSize(size)
mem.proxyMtx.Unlock()
}
func (mem *Mempool) isClose() bool {
return atomic.LoadInt32(&mem.isclose) == 1
}
// SetMinFee 设置最小交易费用
func (mem *Mempool) SetMinFee(fee int64) {
mem.proxyMtx.Lock()
mem.minFee = fee
mem.proxyMtx.Unlock()
}
// GetMinFee 获取最小交易费用
func (mem *Mempool) GetMinFee() int64 {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
return mem.minFee
}
// Height 获取区块高度
func (mem *Mempool) Height() int64 {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
if mem.header == nil {
return -1
}
return mem.header.GetHeight()
}
// BlockTime 获取区块时间
func (mem *Mempool) BlockTime() int64 {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
if mem.header == nil {
return 0
}
return mem.header.BlockTime
}
// Size 返回Mempool中txCache大小
func (mem *Mempool) Size() int {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
return mem.cache.Size()
}
// TxNumOfAccount 返回账户在Mempool中交易数量
func (mem *Mempool) TxNumOfAccount(addr string) int64 {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
return mem.cache.TxNumOfAccount(addr)
}
// GetTxList 从txCache中返回给定数目的tx
func (mem *Mempool) GetTxList(hashList *types.TxHashList) []*types.Transaction {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
minSize := hashList.GetCount()
dupMap := make(map[string]bool)
for i := 0; i < len(hashList.GetHashes()); i++ {
dupMap[string(hashList.GetHashes()[i])] = true
}
var result []*types.Transaction
i := 0
txlist := mem.cache.txList
for v := txlist.Front(); v != nil; v = v.Next() {
if v.Value.(*Item).value.IsExpire(mem.header.GetHeight(), mem.header.GetBlockTime()) {
continue
} else {
tx := v.Value.(*Item).value
if _, ok := dupMap[string(tx.Hash())]; ok {
continue
}
result = append(result, tx)
i++
if i == int(minSize) {
break
}
}
}
return result
}
// RemoveExpiredAndDuplicateMempoolTxs 删除过期交易然后复制并返回Mempool内交易
func (mem *Mempool) RemoveExpiredAndDuplicateMempoolTxs() []*types.Transaction {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
var result []*types.Transaction
for _, v := range mem.cache.txMap {
item := v.Value.(*Item)
hash := item.value.Hash()
if types.Now().Unix()-item.enterTime >= mempoolExpiredInterval {
// 清理滞留Mempool中超过10分钟的交易
mem.cache.Remove(hash)
} else if item.value.IsExpire(mem.header.GetHeight(), mem.header.GetBlockTime()) {
// 清理过期的交易
mem.cache.Remove(hash)
} else {
result = append(result, item.value)
}
}
return result
}
// RemoveTxsOfBlock 移除Mempool中已被Blockchain打包的tx
func (mem *Mempool) RemoveTxsOfBlock(block *types.Block) bool {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
for _, tx := range block.Txs {
hash := tx.Hash()
mem.addedTxs.Add(string(hash), nil)
exist := mem.cache.Exists(hash)
if exist {
mem.cache.Remove(hash)
}
}
return true
}
// RemoveTxs 从Mempool中删除给定Hash的txs
func (mem *Mempool) RemoveTxs(hashList *types.TxHashList) error {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
for _, hash := range hashList.Hashes {
exist := mem.cache.Exists(hash)
if exist {
mem.cache.Remove(hash)
}
}
return nil
}
// DelBlock 将回退的区块内的交易重新加入mempool中
func (mem *Mempool) DelBlock(block *types.Block) {
if len(block.Txs) <= 0 {
return
}
blkTxs := block.Txs
header := mem.GetHeader()
for i := 0; i < len(blkTxs); i++ {
tx := blkTxs[i]
//当前包括ticket和平行链的第一笔挖矿交易,统一actionName为miner
if i == 0 && tx.ActionName() == types.MinerAction {
continue
}
groupCount := int(tx.GetGroupCount())
if groupCount > 1 && i+groupCount <= len(blkTxs) {
group := types.Transactions{Txs: blkTxs[i : i+groupCount]}
tx = group.Tx()
i = i + groupCount - 1
}
err := tx.Check(header.GetHeight(), mem.minFee)
if err != nil {
continue
}
if !mem.checkExpireValid(tx) {
continue
}
mem.addedTxs.Remove(string(tx.Hash()))
mem.PushTx(tx)
}
}
// PushTx 将交易推入Mempool,并返回结果(error)
func (mem *Mempool) PushTx(tx *types.Transaction) error {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
err := mem.cache.Push(tx)
return err
}
// GetLatestTx 返回最新十条加入到Mempool的交易
func (mem *Mempool) GetLatestTx() []*types.Transaction {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
return mem.cache.GetLatestTx()
}
// ReTrySend 每隔两分钟运行一次ReTry
func (mem *Mempool) ReTrySend() {
for {
time.Sleep(time.Minute * 2)
mem.ReTry()
}
}
// ReTry 检查Mempool,将未过期的交易重发送给P2P
func (mem *Mempool) ReTry() {
var result []*types.Transaction
mem.proxyMtx.Lock()
for _, v := range mem.cache.txMap {
if types.Now().Unix()-v.Value.(*Item).enterTime >= mempoolReSendInterval {
result = append(result, v.Value.(*Item).value)
}
}
mem.proxyMtx.Unlock()
if len(result) > 0 {
mlog.Debug("retry send tx...")
}
for _, tx := range result {
mem.SendTxToP2P(tx)
}
}
// RemoveBlockedTxs 每隔1分钟清理一次已打包的交易
func (mem *Mempool) RemoveBlockedTxs() {
defer mem.wg.Done()
defer mlog.Info("RemoveBlockedTxs quit")
if mem.client == nil {
panic("client not bind message queue.")
}
for {
select {
case <-mem.removeBlockTicket.C:
if mem.isClose() {
return
}
txs := mem.RemoveExpiredAndDuplicateMempoolTxs()
var checkHashList types.TxHashList
for _, tx := range txs {
hash := tx.Hash()
checkHashList.Hashes = append(checkHashList.Hashes, hash)
}
if len(checkHashList.Hashes) == 0 {
continue
}
// 发送Hash过后的交易列表给blockchain模块
hashList := mem.client.NewMessage("blockchain", types.EventTxHashList, &checkHashList)
err := mem.client.Send(hashList, true)
if err != nil {
mlog.Error("blockchain closed", "err", err.Error())
return
}
dupTxList, err := mem.client.Wait(hashList)
if err != nil {
mlog.Error("blockchain get txhashlist err", "err", err)
continue
}
// 取出blockchain返回的重复交易列表
dupTxs := dupTxList.GetData().(*types.TxHashList).Hashes
if len(dupTxs) == 0 {
continue
}
mem.proxyMtx.Lock()
for _, t := range dupTxs {
txValue, exists := mem.cache.txMap[string(t)]
if exists {
mem.addedTxs.Add(string(t), nil)
mem.cache.Remove(txValue.Value.(*Item).value.Hash())
}
}
mem.proxyMtx.Unlock()
case <-mem.done:
return
}
}
}
// GetHeader 获取Mempool.header
func (mem *Mempool) GetHeader() *types.Header {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
return mem.header
}
// GetLastHeader 获取LastHeader的height和blockTime
func (mem *Mempool) GetLastHeader() (interface{}, error) {
if mem.client == nil {
panic("client not bind message queue.")
}
msg := mem.client.NewMessage("blockchain", types.EventGetLastHeader, nil)
err := mem.client.Send(msg, true)
if err != nil { if err != nil {
mlog.Error("blockchain closed", "err", err.Error()) panic("Unsupported mempool type:" + cfg.Name + " " + err.Error())
return nil, err
}
return mem.client.Wait(msg)
}
// GetAccTxs 用来获取对应账户地址(列表)中的全部交易详细信息
func (mem *Mempool) GetAccTxs(addrs *types.ReqAddrs) *types.TransactionDetails {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
return mem.cache.GetAccTxs(addrs)
}
// SendTxToP2P 向"p2p"发送消息
func (mem *Mempool) SendTxToP2P(tx *types.Transaction) {
if mem.client == nil {
panic("client not bind message queue.")
}
msg := mem.client.NewMessage("p2p", types.EventTxBroadcast, tx)
mem.client.Send(msg, false)
mlog.Debug("tx sent to p2p", "tx.Hash", common.ToHex(tx.Hash()))
}
// CheckExpireValid 检查交易过期有效性,过期返回false,未过期返回true
func (mem *Mempool) CheckExpireValid(msg queue.Message) (bool, error) {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
if mem.header == nil {
return false, types.ErrHeaderNotSet
} }
tx := msg.GetData().(types.TxGroup).Tx() subcfg, ok := sub[cfg.Name]
ok := mem.checkExpireValid(tx)
if !ok { if !ok {
return ok, types.ErrTxExpire subcfg = nil
}
return ok, nil
}
func (mem *Mempool) checkExpireValid(tx *types.Transaction) bool {
if tx.IsExpire(mem.header.GetHeight(), mem.header.GetBlockTime()) {
return false
}
if tx.Expire > 1000000000 && tx.Expire < types.Now().Unix()+int64(time.Minute/time.Second) {
return false
}
return true
}
// Close 关闭Mempool
func (mem *Mempool) Close() {
if mem.isClose() {
return
}
atomic.StoreInt32(&mem.isclose, 1)
close(mem.done)
mem.client.Close()
mem.removeBlockTicket.Stop()
mlog.Info("mempool module closing")
mem.wg.Wait()
mlog.Info("mempool module closed")
}
// checkTxListRemote 发送消息给执行模块检查交易
func (mem *Mempool) checkTxListRemote(txlist *types.ExecTxList) (*types.ReceiptCheckTxList, error) {
if mem.client == nil {
panic("client not bind message queue.")
}
msg := mem.client.NewMessage("execs", types.EventCheckTx, txlist)
err := mem.client.Send(msg, true)
if err != nil {
mlog.Error("execs closed", "err", err.Error())
return nil, err
} }
msg, err = mem.client.Wait(msg) obj := con(cfg, subcfg)
if err != nil { return obj
return nil, err
}
return msg.GetData().(*types.ReceiptCheckTxList), nil
}
// pollLastHeader在初始化后循环获取LastHeader,直到获取成功后,返回
func (mem *Mempool) pollLastHeader() {
defer mem.wg.Done()
defer func() {
mlog.Info("pollLastHeader quit")
mem.poolHeader <- struct{}{}
}()
for {
if mem.isClose() {
return
}
lastHeader, err := mem.GetLastHeader()
if err != nil {
mlog.Error(err.Error())
time.Sleep(time.Second)
continue
}
h := lastHeader.(queue.Message).Data.(*types.Header)
mem.setHeader(h)
return
}
}
// setHeader设置Mempool.header
func (mem *Mempool) setHeader(h *types.Header) {
mem.proxyMtx.Lock()
mem.header = h
mem.proxyMtx.Unlock()
}
// WaitPollLastHeader 等待获取到最新高度
func (mem *Mempool) WaitPollLastHeader() {
<-mem.poolHeader
//wait sync
<-mem.poolHeader
}
func (mem *Mempool) setSync(status bool) {
mem.proxyMtx.Lock()
mem.sync = status
mem.proxyMtx.Unlock()
}
// SetSync 设置Mempool同步状态
func (mem *Mempool) SetSync(status bool) {
mem.setSync(status)
}
// Mempool.isSync检查Mempool是否同步完成
func (mem *Mempool) isSync() bool {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
return mem.sync
}
// Mempool.getSync获取Mempool同步状态
func (mem *Mempool) getSync() {
defer func() {
mlog.Info("getsync quit")
mem.poolHeader <- struct{}{}
}()
defer mem.wg.Done()
if mem.isSync() {
return
}
if mem.cfg.ForceAccept {
mem.setSync(true)
}
for {
if mem.isClose() {
return
}
if mem.client == nil {
panic("client not bind message queue.")
}
msg := mem.client.NewMessage("blockchain", types.EventIsSync, nil)
mem.client.Send(msg, true)
resp, err := mem.client.Wait(msg)
if err != nil {
time.Sleep(time.Second)
continue
}
if resp.GetData().(*types.IsCaughtUp).GetIscaughtup() {
mem.setSync(true)
return
}
time.Sleep(time.Second)
continue
}
}
func (mem *Mempool) checkSign(data queue.Message) queue.Message {
tx, ok := data.GetData().(types.TxGroup)
if ok && tx.CheckSign() {
return data
}
mlog.Error("wrong tx", "err", types.ErrSign)
data.Data = types.ErrSign
return data
}
func (mem *Mempool) pipeLine() <-chan queue.Message {
//check sign
step1 := func(data queue.Message) queue.Message {
if data.Err() != nil {
return data
}
return mem.checkSign(data)
}
chs := make([]<-chan queue.Message, processNum)
for i := 0; i < processNum; i++ {
chs[i] = step(mem.done, mem.in, step1)
}
out1 := merge(mem.done, chs)
//checktx remote
step2 := func(data queue.Message) queue.Message {
if data.Err() != nil {
return data
}
return mem.checkTxRemote(data)
}
chs2 := make([]<-chan queue.Message, processNum)
for i := 0; i < processNum; i++ {
chs2[i] = step(mem.done, out1, step2)
}
return merge(mem.done, chs2)
}
// SetQueueClient set client queue, for recv msg
func (mem *Mempool) SetQueueClient(client queue.Client) {
mem.client = client
mem.client.Sub("mempool")
mem.wg.Add(1)
go mem.pollLastHeader()
mem.wg.Add(1)
go mem.getSync()
// go mem.ReTrySend()
// 从badChan读取坏消息,并回复错误信息
mem.out = mem.pipeLine()
mlog.Info("mempool piple line start")
mem.wg.Add(1)
go func() {
defer mlog.Info("piple line quit")
defer mem.wg.Done()
for m := range mem.out {
if m.Err() != nil {
m.Reply(mem.client.NewMessage("rpc", types.EventReply,
&types.Reply{Msg: []byte(m.Err().Error())}))
} else {
mem.SendTxToP2P(m.GetData().(types.TxGroup).Tx())
m.Reply(mem.client.NewMessage("rpc", types.EventReply, &types.Reply{IsOk: true}))
}
}
println("mem.out close")
}()
mem.wg.Add(1)
go mem.RemoveBlockedTxs()
mem.wg.Add(1)
go func() {
defer mlog.Info("mempool message recv quit")
defer mem.wg.Done()
defer close(mem.in)
for msg := range mem.client.Recv() {
mlog.Debug("mempool recv", "msgid", msg.ID, "msg", types.GetEventName(int(msg.Ty)))
beg := types.Now()
switch msg.Ty {
case types.EventTx:
if !mem.isSync() {
msg.Reply(mem.client.NewMessage("", types.EventReply, &types.Reply{Msg: []byte(types.ErrNotSync.Error())}))
mlog.Error("wrong tx", "err", types.ErrNotSync.Error())
} else {
checkedMsg := mem.CheckTxs(msg)
select {
case mem.in <- checkedMsg:
case <-mem.done:
}
}
case types.EventGetMempool:
// 消息类型EventGetMempool:获取Mempool内所有交易
msg.Reply(mem.client.NewMessage("rpc", types.EventReplyTxList,
&types.ReplyTxList{Txs: mem.RemoveExpiredAndDuplicateMempoolTxs()}))
case types.EventTxList:
// 消息类型EventTxList:获取Mempool中一定数量交易
hashList := msg.GetData().(*types.TxHashList)
if hashList.Count <= 0 {
msg.Reply(mem.client.NewMessage("", types.EventReplyTxList, types.ErrSize))
mlog.Error("not an valid size", "msg", msg)
} else {
txList := mem.GetTxList(hashList)
msg.Reply(mem.client.NewMessage("", types.EventReplyTxList, &types.ReplyTxList{Txs: txList}))
}
case types.EventDelTxList:
// 消息类型EventDelTxList:获取Mempool中一定数量交易,并把这些交易从Mempool中删除
hashList := msg.GetData().(*types.TxHashList)
if len(hashList.GetHashes()) == 0 {
msg.ReplyErr("EventDelTxList", types.ErrSize)
} else {
err := mem.RemoveTxs(hashList)
msg.ReplyErr("EventDelTxList", err)
}
case types.EventAddBlock:
// 消息类型EventAddBlock:将添加到区块内的交易从Mempool中删除
block := msg.GetData().(*types.BlockDetail).Block
if block.Height > mem.Height() || (block.Height == 0 && mem.Height() == 0) {
header := &types.Header{}
header.BlockTime = block.BlockTime
header.Height = block.Height
header.StateHash = block.StateHash
mem.setHeader(header)
}
mem.RemoveTxsOfBlock(block)
case types.EventGetMempoolSize:
// 消息类型EventGetMempoolSize:获取Mempool大小
memSize := int64(mem.Size())
msg.Reply(mem.client.NewMessage("rpc", types.EventMempoolSize,
&types.MempoolSize{Size: memSize}))
case types.EventGetLastMempool:
// 消息类型EventGetLastMempool:获取最新十条加入到Mempool的交易
txList := mem.GetLatestTx()
msg.Reply(mem.client.NewMessage("rpc", types.EventReplyTxList,
&types.ReplyTxList{Txs: txList}))
case types.EventDelBlock:
// 回滚区块,把该区块内交易重新加回Mempool
block := msg.GetData().(*types.BlockDetail).Block
if block.Height != mem.GetHeader().GetHeight() {
continue
}
lastHeader, err := mem.GetLastHeader()
if err != nil {
mlog.Error(err.Error())
continue
}
h := lastHeader.(queue.Message).Data.(*types.Header)
mem.setHeader(h)
mem.DelBlock(block)
case types.EventGetAddrTxs:
// 获取Mempool中对应账户(组)所有交易
addrs := msg.GetData().(*types.ReqAddrs)
txlist := mem.GetAccTxs(addrs)
msg.Reply(mem.client.NewMessage("", types.EventReplyAddrTxs, txlist))
default:
}
mlog.Debug("mempool", "cost", types.Since(beg), "msg", types.GetEventName(int(msg.Ty)))
}
}()
} }
...@@ -76,6 +76,9 @@ func New(cfg *types.P2P) *P2p { ...@@ -76,6 +76,9 @@ func New(cfg *types.P2P) *P2p {
return p2p return p2p
} }
//Wait wait for ready
func (network *P2p) Wait() {}
func (network *P2p) isClose() bool { func (network *P2p) isClose() bool {
return atomic.LoadInt32(&network.closed) == 1 return atomic.LoadInt32(&network.closed) == 1
} }
......
...@@ -43,6 +43,8 @@ type Client interface { ...@@ -43,6 +43,8 @@ type Client interface {
// Module be used for module interface // Module be used for module interface
type Module interface { type Module interface {
SetQueueClient(client Client) SetQueueClient(client Client)
//wait for ready
Wait()
Close() Close()
} }
......
...@@ -47,6 +47,9 @@ func (c *channelClient) CreateRawTransaction(param *types.CreateTx) ([]byte, err ...@@ -47,6 +47,9 @@ func (c *channelClient) CreateRawTransaction(param *types.CreateTx) ([]byte, err
if param.IsToken { if param.IsToken {
execer = types.ExecName("token") execer = types.ExecName("token")
} }
if param.Execer != "" {
execer = param.Execer
}
return types.CallCreateTx(execer, "", param) return types.CallCreateTx(execer, "", param)
} }
......
...@@ -88,23 +88,6 @@ func testCreateRawTransactionCoinTransfer(t *testing.T) { ...@@ -88,23 +88,6 @@ func testCreateRawTransactionCoinTransfer(t *testing.T) {
Note: []byte("note"), Note: []byte("note"),
} }
//v := &cty.CoinsAction_Transfer{
// Transfer:&cty.CoinsTransfer{
// Amount:ctx.Amount,
// Note:ctx.To,
// },
//}
//transfer := &cty.CoinsAction{
// Value:v,
// Ty:cty.CoinsActionTransfer,
//}
//
//tx := &types.Transaction{
// Execer:[]byte("coins"),
// Payload:types.Encode(transfer),
// To:ctx.To,
//}
client := newTestChannelClient() client := newTestChannelClient()
txHex, err := client.CreateRawTransaction(&ctx) txHex, err := client.CreateRawTransaction(&ctx)
assert.Nil(t, err) assert.Nil(t, err)
......
...@@ -38,7 +38,8 @@ func (g *Grpc) CreateRawTransaction(ctx context.Context, in *pb.CreateTx) (*pb.U ...@@ -38,7 +38,8 @@ func (g *Grpc) CreateRawTransaction(ctx context.Context, in *pb.CreateTx) (*pb.U
// CreateTransaction create transaction of grpc // CreateTransaction create transaction of grpc
func (g *Grpc) CreateTransaction(ctx context.Context, in *pb.CreateTxIn) (*pb.UnsignTx, error) { func (g *Grpc) CreateTransaction(ctx context.Context, in *pb.CreateTxIn) (*pb.UnsignTx, error) {
exec := pb.LoadExecutorType(string(in.Execer)) execer := pb.ExecName(string(in.Execer))
exec := pb.LoadExecutorType(execer)
if exec == nil { if exec == nil {
log.Error("callExecNewTx", "Error", "exec not found") log.Error("callExecNewTx", "Error", "exec not found")
return nil, pb.ErrNotSupport return nil, pb.ErrNotSupport
...@@ -52,7 +53,7 @@ func (g *Grpc) CreateTransaction(ctx context.Context, in *pb.CreateTxIn) (*pb.Un ...@@ -52,7 +53,7 @@ func (g *Grpc) CreateTransaction(ctx context.Context, in *pb.CreateTxIn) (*pb.Un
if err != nil { if err != nil {
return nil, err return nil, err
} }
reply, err := pb.CallCreateTx(string(in.Execer), in.ActionName, msg) reply, err := pb.CallCreateTx(execer, in.ActionName, msg)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -20,12 +20,26 @@ import ( ...@@ -20,12 +20,26 @@ import (
) )
// CreateRawTransaction create rawtransaction by jrpc // CreateRawTransaction create rawtransaction by jrpc
func (c *Chain33) CreateRawTransaction(in *types.CreateTx, result *interface{}) error { func (c *Chain33) CreateRawTransaction(in *rpctypes.CreateTx, result *interface{}) error {
reply, err := c.cli.CreateRawTransaction(in) if in == nil {
log.Error("CreateRawTransaction", "Error", types.ErrInvalidParam)
return types.ErrInvalidParam
}
inpb := &types.CreateTx{
To: in.To,
Amount: in.Amount,
Fee: in.Fee,
Note: []byte(in.Note),
IsWithdraw: in.IsWithdraw,
IsToken: in.IsToken,
TokenSymbol: in.TokenSymbol,
ExecName: in.ExecName,
Execer: in.Execer,
}
reply, err := c.cli.CreateRawTransaction(inpb)
if err != nil { if err != nil {
return err return err
} }
*result = hex.EncodeToString(reply) *result = hex.EncodeToString(reply)
return nil return nil
} }
...@@ -404,50 +418,6 @@ func (c *Chain33) ImportPrivkey(in types.ReqWalletImportPrivkey, result *interfa ...@@ -404,50 +418,6 @@ func (c *Chain33) ImportPrivkey(in types.ReqWalletImportPrivkey, result *interfa
// SendToAddress send to address of coins // SendToAddress send to address of coins
func (c *Chain33) SendToAddress(in types.ReqWalletSendToAddress, result *interface{}) error { func (c *Chain33) SendToAddress(in types.ReqWalletSendToAddress, result *interface{}) error {
log.Debug("Rpc SendToAddress", "Tx", in)
if types.IsPara() {
createTx := &types.CreateTx{
To: in.GetTo(),
Amount: in.GetAmount(),
Fee: 1e5,
Note: in.GetNote(),
IsWithdraw: false,
IsToken: true,
TokenSymbol: in.GetTokenSymbol(),
ExecName: types.ExecName("token"),
}
tx, err := c.cli.CreateRawTransaction(createTx)
if err != nil {
log.Debug("ParaChain CreateRawTransaction", "Error", err.Error())
return err
}
//不需要自己去导出私钥,signRawTx 里面只需带入公钥地址,也回优先去查出相应的私钥,前提是私钥已经导入
reqSignRawTx := &types.ReqSignRawTx{
Addr: in.From,
Privkey: "",
TxHex: hex.EncodeToString(tx),
Expire: "300s",
Index: 0,
Token: "",
}
replySignRawTx, err := c.cli.SignRawTx(reqSignRawTx)
if err != nil {
log.Debug("ParaChain SignRawTx", "Error", err.Error())
return err
}
rawParm := rpctypes.RawParm{
Token: "",
Data: replySignRawTx.GetTxHex(),
}
var txHash interface{}
err = forwardTranToMainNet(rawParm, &txHash)
if err != nil {
log.Debug("ParaChain forwardTranToMainNet", "Error", err.Error())
return err
}
*result = &rpctypes.ReplyHash{Hash: txHash.(string)}
return nil
}
reply, err := c.cli.WalletSendToAddress(&in) reply, err := c.cli.WalletSendToAddress(&in)
if err != nil { if err != nil {
log.Debug("SendToAddress", "Error", err.Error()) log.Debug("SendToAddress", "Error", err.Error())
...@@ -1120,7 +1090,7 @@ func (c *Chain33) CreateTransaction(in *rpctypes.CreateTxIn, result *interface{} ...@@ -1120,7 +1090,7 @@ func (c *Chain33) CreateTransaction(in *rpctypes.CreateTxIn, result *interface{}
if in == nil { if in == nil {
return types.ErrInvalidParam return types.ErrInvalidParam
} }
btx, err := types.CallCreateTxJSON(in.Execer, in.ActionName, in.Payload) btx, err := types.CallCreateTxJSON(types.ExecName(in.Execer), in.ActionName, in.Payload)
if err != nil { if err != nil {
return err return err
} }
......
...@@ -387,11 +387,11 @@ func TestChain33_CreateRawTransaction(t *testing.T) { ...@@ -387,11 +387,11 @@ func TestChain33_CreateRawTransaction(t *testing.T) {
assert.Nil(t, testResult) assert.Nil(t, testResult)
assert.NotNil(t, err) assert.NotNil(t, err)
tx := &types.CreateTx{ tx := &rpctypes.CreateTx{
To: "qew", To: "184wj4nsgVxKyz2NhM3Yb5RK5Ap6AFRFq2",
Amount: 10, Amount: 10,
Fee: 1, Fee: 1,
Note: []byte("12312"), Note: "12312",
IsWithdraw: false, IsWithdraw: false,
IsToken: false, IsToken: false,
TokenSymbol: "", TokenSymbol: "",
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package rpc package rpc
import ( import (
"encoding/hex"
"errors" "errors"
"testing" "testing"
"time" "time"
...@@ -114,10 +115,33 @@ func TestJSONClient_Call(t *testing.T) { ...@@ -114,10 +115,33 @@ func TestJSONClient_Call(t *testing.T) {
err = jsonClient.Call("Chain33.IsNtpClockSync", &types.ReqNil{}, &retNtp) err = jsonClient.Call("Chain33.IsNtpClockSync", &types.ReqNil{}, &retNtp)
assert.Nil(t, err) assert.Nil(t, err)
assert.True(t, retNtp) assert.True(t, retNtp)
testCreateTxCoins(t, jsonClient)
server.Close() server.Close()
mock.AssertExpectationsForObjects(t, api) mock.AssertExpectationsForObjects(t, api)
} }
func testCreateTxCoins(t *testing.T, jsonClient *jsonclient.JSONClient) {
req := &rpctypes.CreateTx{
To: "184wj4nsgVxKyz2NhM3Yb5RK5Ap6AFRFq2",
Amount: 10,
Fee: 1,
Note: "12312",
IsWithdraw: false,
IsToken: false,
TokenSymbol: "",
ExecName: types.ExecName("coins"),
}
var res string
err := jsonClient.Call("Chain33.CreateRawTransaction", req, &res)
assert.Nil(t, err)
txbytes, err := hex.DecodeString(res)
assert.Nil(t, err)
var tx types.Transaction
err = types.Decode(txbytes, &tx)
assert.Nil(t, err)
assert.Equal(t, "184wj4nsgVxKyz2NhM3Yb5RK5Ap6AFRFq2", tx.To)
}
func TestGrpc_Call(t *testing.T) { func TestGrpc_Call(t *testing.T) {
rpcCfg = new(types.RPC) rpcCfg = new(types.RPC)
rpcCfg.GrpcBindAddr = "127.0.0.1:8101" rpcCfg.GrpcBindAddr = "127.0.0.1:8101"
......
...@@ -360,3 +360,16 @@ type ExecAccount struct { ...@@ -360,3 +360,16 @@ type ExecAccount struct {
type ExecNameParm struct { type ExecNameParm struct {
ExecName string `json:"execname"` ExecName string `json:"execname"`
} }
//CreateTx 为了简化Note 的创建过程,在json rpc 中,note 采用string 格式
type CreateTx struct {
To string `json:"to,omitempty"`
Amount int64 `json:"amount,omitempty"`
Fee int64 `json:"fee,omitempty"`
Note string `json:"note,omitempty"`
IsWithdraw bool `json:"isWithdraw,omitempty"`
IsToken bool `json:"isToken,omitempty"`
TokenSymbol string `json:"tokenSymbol,omitempty"`
ExecName string `json:"execName,omitempty"` //TransferToExec and Withdraw 的执行器
Execer string `json:"execer,omitempty"` //执行器名称
}
...@@ -105,6 +105,9 @@ func (bc *BaseClient) InitMiner() { ...@@ -105,6 +105,9 @@ func (bc *BaseClient) InitMiner() {
bc.once.Do(bc.minerstartCB) bc.once.Do(bc.minerstartCB)
} }
//Wait wait for ready
func (bc *BaseClient) Wait() {}
//SetQueueClient 设置客户端队列 //SetQueueClient 设置客户端队列
func (bc *BaseClient) SetQueueClient(c queue.Client) { func (bc *BaseClient) SetQueueClient(c queue.Client) {
bc.InitClient(c, func() { bc.InitClient(c, func() {
...@@ -334,6 +337,10 @@ func buildHashList(deltx []*types.Transaction) *types.TxHashList { ...@@ -334,6 +337,10 @@ func buildHashList(deltx []*types.Transaction) *types.TxHashList {
//WriteBlock 向blockchain写区块 //WriteBlock 向blockchain写区块
func (bc *BaseClient) WriteBlock(prev []byte, block *types.Block) error { func (bc *BaseClient) WriteBlock(prev []byte, block *types.Block) error {
//保存block的原始信息用于删除mempool中的错误交易
rawtxs := make([]*types.Transaction, len(block.Txs))
copy(rawtxs, block.Txs)
blockdetail := &types.BlockDetail{Block: block} blockdetail := &types.BlockDetail{Block: block}
msg := bc.client.NewMessage("blockchain", types.EventAddBlockDetail, blockdetail) msg := bc.client.NewMessage("blockchain", types.EventAddBlockDetail, blockdetail)
bc.client.Send(msg, true) bc.client.Send(msg, true)
...@@ -343,7 +350,7 @@ func (bc *BaseClient) WriteBlock(prev []byte, block *types.Block) error { ...@@ -343,7 +350,7 @@ func (bc *BaseClient) WriteBlock(prev []byte, block *types.Block) error {
} }
blockdetail = resp.GetData().(*types.BlockDetail) blockdetail = resp.GetData().(*types.BlockDetail)
//从mempool 中删除错误的交易 //从mempool 中删除错误的交易
deltx := diffTx(block.Txs, blockdetail.Block.Txs) deltx := diffTx(rawtxs, blockdetail.Block.Txs)
if len(deltx) > 0 { if len(deltx) > 0 {
bc.delMempoolTx(deltx) bc.delMempoolTx(deltx)
} }
......
...@@ -12,6 +12,7 @@ import ( ...@@ -12,6 +12,7 @@ import (
//加载系统内置store, 不要依赖plugin //加载系统内置store, 不要依赖plugin
_ "github.com/33cn/chain33/system/dapp/init" _ "github.com/33cn/chain33/system/dapp/init"
_ "github.com/33cn/chain33/system/mempool/init"
_ "github.com/33cn/chain33/system/store/init" _ "github.com/33cn/chain33/system/store/init"
) )
......
...@@ -293,7 +293,7 @@ func getblockbyhashs(cmd *cobra.Command, args []string) { ...@@ -293,7 +293,7 @@ func getblockbyhashs(cmd *cobra.Command, args []string) {
Hashes: hashesArr, Hashes: hashesArr,
} }
var res types.BlockDetails var res rpctypes.BlockDetails
ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.GetBlockByHashes", params, &res) ctx := jsonclient.NewRPCCtx(rpcLaddr, "Chain33.GetBlockByHashes", params, &res)
//ctx.SetResultCb(parseQueryTxsByHashesRes) //ctx.SetResultCb(parseQueryTxsByHashesRes)
ctx.Run() ctx.Run()
......
...@@ -9,5 +9,6 @@ import ( ...@@ -9,5 +9,6 @@ import (
_ "github.com/33cn/chain33/system/consensus/init" //register consensus init package _ "github.com/33cn/chain33/system/consensus/init" //register consensus init package
_ "github.com/33cn/chain33/system/crypto/init" _ "github.com/33cn/chain33/system/crypto/init"
_ "github.com/33cn/chain33/system/dapp/init" _ "github.com/33cn/chain33/system/dapp/init"
_ "github.com/33cn/chain33/system/mempool/init"
_ "github.com/33cn/chain33/system/store/init" _ "github.com/33cn/chain33/system/store/init"
) )
package mempool
import (
"github.com/33cn/chain33/common/listmap"
"github.com/33cn/chain33/types"
)
//AccountTxIndex 账户和交易索引
type AccountTxIndex struct {
maxperaccount int
accMap map[string]*listmap.ListMap
}
//NewAccountTxIndex 创建一个新的索引
func NewAccountTxIndex(maxperaccount int) *AccountTxIndex {
return &AccountTxIndex{
maxperaccount: maxperaccount,
accMap: make(map[string]*listmap.ListMap),
}
}
// TxNumOfAccount 返回账户在Mempool中交易数量
func (cache *AccountTxIndex) TxNumOfAccount(addr string) int {
if _, ok := cache.accMap[addr]; ok {
return cache.accMap[addr].Size()
}
return 0
}
// GetAccTxs 用来获取对应账户地址(列表)中的全部交易详细信息
func (cache *AccountTxIndex) GetAccTxs(addrs *types.ReqAddrs) *types.TransactionDetails {
res := &types.TransactionDetails{}
for _, addr := range addrs.Addrs {
if value, ok := cache.accMap[addr]; ok {
value.Walk(func(val interface{}) bool {
v := val.(*types.Transaction)
txAmount, err := v.Amount()
if err != nil {
txAmount = 0
}
res.Txs = append(res.Txs,
&types.TransactionDetail{
Tx: v,
Amount: txAmount,
Fromaddr: addr,
ActionName: v.ActionName(),
})
return true
})
}
}
return res
}
//Remove 根据交易哈希删除对应账户的对应交易
func (cache *AccountTxIndex) Remove(tx *types.Transaction) {
addr := tx.From()
if lm, ok := cache.accMap[addr]; ok {
lm.Remove(string(tx.Hash()))
if lm.Size() == 0 {
delete(cache.accMap, addr)
}
}
}
// Push push transaction to AccountTxIndex
func (cache *AccountTxIndex) Push(tx *types.Transaction) error {
addr := tx.From()
_, ok := cache.accMap[addr]
if !ok {
cache.accMap[addr] = listmap.New()
}
if cache.accMap[addr].Size() >= cache.maxperaccount {
return types.ErrManyTx
}
cache.accMap[addr].Push(string(tx.Hash()), tx)
return nil
}
//CanPush 是否可以push 进 account index
func (cache *AccountTxIndex) CanPush(tx *types.Transaction) bool {
if item, ok := cache.accMap[tx.From()]; ok {
return item.Size() < cache.maxperaccount
}
return true
}
// 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 mempool
import (
"sync"
"sync/atomic"
"time"
"github.com/33cn/chain33/common"
log "github.com/33cn/chain33/common/log/log15"
"github.com/33cn/chain33/queue"
"github.com/33cn/chain33/types"
)
var mlog = log.New("module", "mempool.base")
//Mempool mempool 基础类
type Mempool struct {
proxyMtx sync.Mutex
in chan queue.Message
out <-chan queue.Message
client queue.Client
header *types.Header
sync bool
cfg *types.Mempool
poolHeader chan struct{}
isclose int32
wg sync.WaitGroup
done chan struct{}
removeBlockTicket *time.Ticker
cache *txCache
}
//GetSync 判断是否mempool 同步
func (mem *Mempool) getSync() bool {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
return mem.sync
}
//NewMempool 新建mempool 实例
func NewMempool(cfg *types.Mempool) *Mempool {
pool := &Mempool{}
if cfg.MaxTxNumPerAccount == 0 {
cfg.MaxTxNumPerAccount = maxTxNumPerAccount
}
if cfg.MaxTxLast == 0 {
cfg.MaxTxLast = maxTxLast
}
if cfg.PoolCacheSize == 0 {
cfg.PoolCacheSize = poolCacheSize
}
pool.in = make(chan queue.Message)
pool.out = make(<-chan queue.Message)
pool.done = make(chan struct{})
pool.cfg = cfg
pool.poolHeader = make(chan struct{}, 2)
pool.removeBlockTicket = time.NewTicker(time.Minute)
pool.cache = newCache(cfg.MaxTxNumPerAccount, cfg.MaxTxLast)
return pool
}
//Close 关闭mempool
func (mem *Mempool) Close() {
if mem.isClose() {
return
}
atomic.StoreInt32(&mem.isclose, 1)
close(mem.done)
if mem.client != nil {
mem.client.Close()
}
mem.removeBlockTicket.Stop()
mlog.Info("mempool module closing")
mem.wg.Wait()
mlog.Info("mempool module closed")
}
//SetQueueClient 初始化mempool模块
func (mem *Mempool) SetQueueClient(client queue.Client) {
mem.client = client
mem.client.Sub("mempool")
mem.wg.Add(1)
go mem.pollLastHeader()
mem.wg.Add(1)
go mem.checkSync()
mem.wg.Add(1)
go mem.removeBlockedTxs()
mem.wg.Add(1)
go mem.eventProcess()
}
// Size 返回mempool中txCache大小
func (mem *Mempool) Size() int {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
return mem.cache.Size()
}
// SetMinFee 设置最小交易费用
func (mem *Mempool) SetMinFee(fee int64) {
mem.proxyMtx.Lock()
mem.cfg.MinTxFee = fee
mem.proxyMtx.Unlock()
}
//SetQueueCache 设置排队策略
func (mem *Mempool) SetQueueCache(qcache QueueCache) {
mem.cache.SetQueueCache(qcache)
}
// GetTxList 从txCache中返回给定数目的tx
func (mem *Mempool) getTxList(filterList *types.TxHashList) (txs []*types.Transaction) {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
count := filterList.GetCount()
dupMap := make(map[string]bool)
for i := 0; i < len(filterList.GetHashes()); i++ {
dupMap[string(filterList.GetHashes()[i])] = true
}
return mem.filterTxList(count, dupMap)
}
func (mem *Mempool) filterTxList(count int64, dupMap map[string]bool) (txs []*types.Transaction) {
height := mem.header.GetHeight()
blocktime := mem.header.GetBlockTime()
mem.cache.Walk(int(count), func(tx *Item) bool {
if dupMap != nil {
if _, ok := dupMap[string(tx.Value.Hash())]; ok {
return true
}
}
if isExpired(tx, height, blocktime) {
return true
}
txs = append(txs, tx.Value)
return true
})
return txs
}
// RemoveTxs 从mempool中删除给定Hash的txs
func (mem *Mempool) RemoveTxs(hashList *types.TxHashList) error {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
for _, hash := range hashList.Hashes {
exist := mem.cache.Exist(string(hash))
if exist {
mem.cache.Remove(string(hash))
}
}
return nil
}
// PushTx 将交易推入mempool,并返回结果(error)
func (mem *Mempool) PushTx(tx *types.Transaction) error {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
err := mem.cache.Push(tx)
return err
}
// setHeader设置mempool.header
func (mem *Mempool) setHeader(h *types.Header) {
mem.proxyMtx.Lock()
mem.header = h
mem.proxyMtx.Unlock()
}
// GetHeader 获取Mempool.header
func (mem *Mempool) GetHeader() *types.Header {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
return mem.header
}
//IsClose 判断是否mempool 关闭
func (mem *Mempool) isClose() bool {
return atomic.LoadInt32(&mem.isclose) == 1
}
// GetLastHeader 获取LastHeader的height和blockTime
func (mem *Mempool) GetLastHeader() (interface{}, error) {
if mem.client == nil {
panic("client not bind message queue.")
}
msg := mem.client.NewMessage("blockchain", types.EventGetLastHeader, nil)
err := mem.client.Send(msg, true)
if err != nil {
mlog.Error("blockchain closed", "err", err.Error())
return nil, err
}
return mem.client.Wait(msg)
}
// GetAccTxs 用来获取对应账户地址(列表)中的全部交易详细信息
func (mem *Mempool) GetAccTxs(addrs *types.ReqAddrs) *types.TransactionDetails {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
return mem.cache.GetAccTxs(addrs)
}
// TxNumOfAccount 返回账户在mempool中交易数量
func (mem *Mempool) TxNumOfAccount(addr string) int64 {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
return int64(mem.cache.TxNumOfAccount(addr))
}
// GetLatestTx 返回最新十条加入到mempool的交易
func (mem *Mempool) GetLatestTx() []*types.Transaction {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
return mem.cache.GetLatestTx()
}
// pollLastHeader在初始化后循环获取LastHeader,直到获取成功后,返回
func (mem *Mempool) pollLastHeader() {
defer mem.wg.Done()
defer func() {
mlog.Info("pollLastHeader quit")
mem.poolHeader <- struct{}{}
}()
for {
if mem.isClose() {
return
}
lastHeader, err := mem.GetLastHeader()
if err != nil {
mlog.Error(err.Error())
time.Sleep(time.Second)
continue
}
h := lastHeader.(queue.Message).Data.(*types.Header)
mem.setHeader(h)
return
}
}
func (mem *Mempool) removeExpired() {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
mem.cache.removeExpiredTx(mem.header.GetHeight(), mem.header.GetBlockTime())
}
// removeBlockedTxs 每隔1分钟清理一次已打包的交易
func (mem *Mempool) removeBlockedTxs() {
defer mem.wg.Done()
defer mlog.Info("RemoveBlockedTxs quit")
if mem.client == nil {
panic("client not bind message queue.")
}
for {
select {
case <-mem.removeBlockTicket.C:
if mem.isClose() {
return
}
mem.removeExpired()
case <-mem.done:
return
}
}
}
// RemoveTxsOfBlock 移除mempool中已被Blockchain打包的tx
func (mem *Mempool) RemoveTxsOfBlock(block *types.Block) bool {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
for _, tx := range block.Txs {
hash := tx.Hash()
exist := mem.cache.Exist(string(hash))
if exist {
mem.cache.Remove(string(hash))
}
}
return true
}
// Mempool.DelBlock将回退的区块内的交易重新加入mempool中
func (mem *Mempool) delBlock(block *types.Block) {
if len(block.Txs) <= 0 {
return
}
blkTxs := block.Txs
header := mem.GetHeader()
for i := 0; i < len(blkTxs); i++ {
tx := blkTxs[i]
//当前包括ticket和平行链的第一笔挖矿交易,统一actionName为miner
if i == 0 && tx.ActionName() == types.MinerAction {
continue
}
groupCount := int(tx.GetGroupCount())
if groupCount > 1 && i+groupCount <= len(blkTxs) {
group := types.Transactions{Txs: blkTxs[i : i+groupCount]}
tx = group.Tx()
i = i + groupCount - 1
}
err := tx.Check(header.GetHeight(), mem.cfg.MinTxFee)
if err != nil {
continue
}
if !mem.checkExpireValid(tx) {
continue
}
mem.PushTx(tx)
}
}
// Height 获取区块高度
func (mem *Mempool) Height() int64 {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
if mem.header == nil {
return -1
}
return mem.header.GetHeight()
}
// Wait wait mempool ready
func (mem *Mempool) Wait() {
<-mem.poolHeader
//wait sync
<-mem.poolHeader
}
// SendTxToP2P 向"p2p"发送消息
func (mem *Mempool) sendTxToP2P(tx *types.Transaction) {
if mem.client == nil {
panic("client not bind message queue.")
}
msg := mem.client.NewMessage("p2p", types.EventTxBroadcast, tx)
mem.client.Send(msg, false)
mlog.Debug("tx sent to p2p", "tx.Hash", common.ToHex(tx.Hash()))
}
// Mempool.checkSync检查并获取mempool同步状态
func (mem *Mempool) checkSync() {
defer func() {
mlog.Info("getsync quit")
mem.poolHeader <- struct{}{}
}()
defer mem.wg.Done()
if mem.getSync() {
return
}
if mem.cfg.ForceAccept {
mem.setSync(true)
}
for {
if mem.isClose() {
return
}
if mem.client == nil {
panic("client not bind message queue.")
}
msg := mem.client.NewMessage("blockchain", types.EventIsSync, nil)
err := mem.client.Send(msg, true)
if err != nil {
time.Sleep(time.Second)
continue
}
resp, err := mem.client.Wait(msg)
if err != nil {
time.Sleep(time.Second)
continue
}
if resp.GetData().(*types.IsCaughtUp).GetIscaughtup() {
mem.setSync(true)
return
}
time.Sleep(time.Second)
continue
}
}
func (mem *Mempool) setSync(status bool) {
mem.proxyMtx.Lock()
mem.sync = status
mem.proxyMtx.Unlock()
}
// 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 mempool
import (
"github.com/33cn/chain33/types"
)
//QueueCache 排队交易处理
type QueueCache interface {
Exist(hash string) bool
GetItem(hash string) (*Item, error)
Push(tx *Item) error
Remove(hash string) error
Size() int
Walk(count int, cb func(tx *Item) bool)
}
// Item 为Mempool中包装交易的数据结构
type Item struct {
Value *types.Transaction
Priority int64
EnterTime int64
}
//TxCache 管理交易cache 包括账户索引,最后的交易,排队策略缓存
type txCache struct {
*AccountTxIndex
*LastTxCache
qcache QueueCache
}
//NewTxCache init accountIndex and last cache
func newCache(maxTxPerAccount int64, sizeLast int64) *txCache {
return &txCache{
AccountTxIndex: NewAccountTxIndex(int(maxTxPerAccount)),
LastTxCache: NewLastTxCache(int(sizeLast)),
}
}
//SetQueueCache set queue cache , 这个接口可以扩展
func (cache *txCache) SetQueueCache(qcache QueueCache) {
cache.qcache = qcache
}
//Remove 移除txCache中给定tx
func (cache *txCache) Remove(hash string) {
item, err := cache.qcache.GetItem(hash)
if err != nil {
return
}
tx := item.Value
cache.qcache.Remove(hash)
cache.AccountTxIndex.Remove(tx)
cache.LastTxCache.Remove(tx)
}
//Exist 是否存在
func (cache *txCache) Exist(hash string) bool {
if cache.qcache == nil {
return false
}
return cache.qcache.Exist(hash)
}
//Size cache tx num
func (cache *txCache) Size() int {
if cache.qcache == nil {
return 0
}
return cache.qcache.Size()
}
//Walk iter all txs
func (cache *txCache) Walk(count int, cb func(tx *Item) bool) {
if cache.qcache == nil {
return
}
cache.qcache.Walk(count, cb)
}
//RemoveTxs 删除一组交易
func (cache *txCache) RemoveTxs(txs []string) {
for _, t := range txs {
cache.Remove(t)
}
}
//Push 存入交易到cache 中
func (cache *txCache) Push(tx *types.Transaction) error {
if !cache.AccountTxIndex.CanPush(tx) {
return types.ErrManyTx
}
item := &Item{Value: tx, Priority: tx.Fee, EnterTime: types.Now().Unix()}
err := cache.qcache.Push(item)
if err != nil {
return err
}
cache.AccountTxIndex.Push(tx)
cache.LastTxCache.Push(tx)
return nil
}
func (cache *txCache) removeExpiredTx(height, blocktime int64) {
var txs []string
cache.qcache.Walk(0, func(tx *Item) bool {
if isExpired(tx, height, blocktime) {
txs = append(txs, string(tx.Value.Hash()))
}
return true
})
cache.RemoveTxs(txs)
}
//判断交易是否过期
func isExpired(item *Item, height, blockTime int64) bool {
if types.Now().Unix()-item.EnterTime >= mempoolExpiredInterval {
return true
}
if item.Value.IsExpire(height, blockTime) {
return true
}
return false
}
// 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 mempool package mempool
import ( import (
"errors" "errors"
"time"
"github.com/33cn/chain33/common/address" "github.com/33cn/chain33/common/address"
"github.com/33cn/chain33/queue" "github.com/33cn/chain33/queue"
"github.com/33cn/chain33/types" "github.com/33cn/chain33/types"
"github.com/33cn/chain33/util"
) )
// Mempool.CheckTxList初步检查并筛选交易消息 // CheckExpireValid 检查交易过期有效性,过期返回false,未过期返回true
func (mem *Mempool) CheckExpireValid(msg queue.Message) (bool, error) {
mem.proxyMtx.Lock()
defer mem.proxyMtx.Unlock()
if mem.header == nil {
return false, types.ErrHeaderNotSet
}
tx := msg.GetData().(types.TxGroup).Tx()
ok := mem.checkExpireValid(tx)
if !ok {
return ok, types.ErrTxExpire
}
return ok, nil
}
// checkTxListRemote 发送消息给执行模块检查交易
func (mem *Mempool) checkTxListRemote(txlist *types.ExecTxList) (*types.ReceiptCheckTxList, error) {
if mem.client == nil {
panic("client not bind message queue.")
}
msg := mem.client.NewMessage("execs", types.EventCheckTx, txlist)
err := mem.client.Send(msg, true)
if err != nil {
mlog.Error("execs closed", "err", err.Error())
return nil, err
}
msg, err = mem.client.Wait(msg)
if err != nil {
return nil, err
}
return msg.GetData().(*types.ReceiptCheckTxList), nil
}
func (mem *Mempool) checkExpireValid(tx *types.Transaction) bool {
if tx.IsExpire(mem.header.GetHeight(), mem.header.GetBlockTime()) {
return false
}
if tx.Expire > 1000000000 && tx.Expire < types.Now().Unix()+int64(time.Minute/time.Second) {
return false
}
return true
}
// CheckTx 初步检查并筛选交易消息
func (mem *Mempool) checkTx(msg queue.Message) queue.Message { func (mem *Mempool) checkTx(msg queue.Message) queue.Message {
tx := msg.GetData().(types.TxGroup).Tx() tx := msg.GetData().(types.TxGroup).Tx()
// 检查接收地址是否合法 // 检查接收地址是否合法
...@@ -20,13 +61,7 @@ func (mem *Mempool) checkTx(msg queue.Message) queue.Message { ...@@ -20,13 +61,7 @@ func (mem *Mempool) checkTx(msg queue.Message) queue.Message {
msg.Data = types.ErrInvalidAddress msg.Data = types.ErrInvalidAddress
return msg return msg
} }
// 检查交易是否为重复交易 // 检查交易账户在mempool中是否存在过多交易
if mem.addedTxs.Contains(string(tx.Hash())) {
msg.Data = types.ErrDupTx
return msg
}
// 检查交易账户在Mempool中是否存在过多交易
from := tx.From() from := tx.From()
if mem.TxNumOfAccount(from) >= maxTxNumPerAccount { if mem.TxNumOfAccount(from) >= maxTxNumPerAccount {
msg.Data = types.ErrManyTx msg.Data = types.ErrManyTx
...@@ -42,7 +77,7 @@ func (mem *Mempool) checkTx(msg queue.Message) queue.Message { ...@@ -42,7 +77,7 @@ func (mem *Mempool) checkTx(msg queue.Message) queue.Message {
} }
// CheckTxs 初步检查并筛选交易消息 // CheckTxs 初步检查并筛选交易消息
func (mem *Mempool) CheckTxs(msg queue.Message) queue.Message { func (mem *Mempool) checkTxs(msg queue.Message) queue.Message {
// 判断消息是否含有nil交易 // 判断消息是否含有nil交易
if msg.GetData() == nil { if msg.GetData() == nil {
msg.Data = types.ErrEmptyTx msg.Data = types.ErrEmptyTx
...@@ -52,7 +87,7 @@ func (mem *Mempool) CheckTxs(msg queue.Message) queue.Message { ...@@ -52,7 +87,7 @@ func (mem *Mempool) CheckTxs(msg queue.Message) queue.Message {
txmsg := msg.GetData().(*types.Transaction) txmsg := msg.GetData().(*types.Transaction)
//普通的交易 //普通的交易
tx := types.NewTransactionCache(txmsg) tx := types.NewTransactionCache(txmsg)
err := tx.Check(header.GetHeight(), mem.minFee) err := tx.Check(header.GetHeight(), mem.cfg.MinTxFee)
if err != nil { if err != nil {
msg.Data = err msg.Data = err
return msg return msg
...@@ -79,12 +114,12 @@ func (mem *Mempool) CheckTxs(msg queue.Message) queue.Message { ...@@ -79,12 +114,12 @@ func (mem *Mempool) CheckTxs(msg queue.Message) queue.Message {
return msg return msg
} }
// Mempool.checkTxList检查账户余额是否足够,并加入到Mempool,成功则传入goodChan,若加入Mempool失败则传入badChan //checkTxList 检查账户余额是否足够,并加入到Mempool,成功则传入goodChan,若加入Mempool失败则传入badChan
func (mem *Mempool) checkTxRemote(msg queue.Message) queue.Message { func (mem *Mempool) checkTxRemote(msg queue.Message) queue.Message {
tx := msg.GetData().(types.TxGroup) tx := msg.GetData().(types.TxGroup)
txlist := &types.ExecTxList{} txlist := &types.ExecTxList{}
txlist.Txs = append(txlist.Txs, tx.Tx()) txlist.Txs = append(txlist.Txs, tx.Tx())
//检查是否重复
lastheader := mem.GetHeader() lastheader := mem.GetHeader()
txlist.BlockTime = lastheader.BlockTime txlist.BlockTime = lastheader.BlockTime
txlist.Height = lastheader.Height txlist.Height = lastheader.Height
...@@ -92,6 +127,16 @@ func (mem *Mempool) checkTxRemote(msg queue.Message) queue.Message { ...@@ -92,6 +127,16 @@ func (mem *Mempool) checkTxRemote(msg queue.Message) queue.Message {
// 增加这个属性,在执行器中会使用到 // 增加这个属性,在执行器中会使用到
txlist.Difficulty = uint64(lastheader.Difficulty) txlist.Difficulty = uint64(lastheader.Difficulty)
txlist.IsMempool = true txlist.IsMempool = true
//add check dup tx
newtxs, err := util.CheckDupTx(mem.client, txlist.Txs, txlist.Height)
if err != nil {
msg.Data = err
return msg
}
if len(newtxs) != len(txlist.Txs) {
msg.Data = types.ErrDupTx
return msg
}
result, err := mem.checkTxListRemote(txlist) result, err := mem.checkTxListRemote(txlist)
if err != nil { if err != nil {
msg.Data = err msg.Data = err
......
...@@ -6,25 +6,18 @@ package mempool ...@@ -6,25 +6,18 @@ package mempool
import ( import (
"runtime" "runtime"
//log "github.com/33cn/chain33/common/log/log15"
log "github.com/33cn/chain33/common/log/log15"
) )
var ( var (
mlog = log.New("module", "mempool") poolCacheSize int64 = 10240 // mempool容量
poolCacheSize int64 = 10240 // mempool容量 mempoolExpiredInterval int64 = 600 // mempool内交易过期时间,10分钟
mempoolExpiredInterval int64 = 600 // mempool内交易过期时间,10分钟 maxTxNumPerAccount int64 = 100 // TODO 每个账户在mempool中最大交易数量,10
mempoolReSendInterval int64 = 60 // mempool内交易重发时间,1分钟 maxTxLast int64 = 10
mempoolDupResendInterval int64 = 120 // mempool重复交易可再次发送间隔,120秒 processNum int
mempoolAddedTxSize = 102400 // 已添加过的交易缓存大小
maxTxNumPerAccount int64 = 100 // TODO 每个账户在mempool中最大交易数量,10
processNum int
) )
// TODO // TODO
func init() { func init() {
processNum = runtime.NumCPU() processNum = runtime.NumCPU()
//if processNum >= 2 {
//processNum -= 1
//}
} }
// 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 mempool
//mempool 模块的功能:实现交易暂存的功能。主要是解决共识模块可能比rpc模块速度慢的问题。
//模块的接口的设计:
package mempool
import (
"github.com/33cn/chain33/queue"
"github.com/33cn/chain33/types"
)
func (mem *Mempool) reply() {
defer mlog.Info("piple line quit")
defer mem.wg.Done()
for m := range mem.out {
if m.Err() != nil {
m.Reply(mem.client.NewMessage("rpc", types.EventReply,
&types.Reply{IsOk: false, Msg: []byte(m.Err().Error())}))
} else {
mem.sendTxToP2P(m.GetData().(types.TxGroup).Tx())
m.Reply(mem.client.NewMessage("rpc", types.EventReply, &types.Reply{IsOk: true, Msg: nil}))
}
}
}
func (mem *Mempool) pipeLine() <-chan queue.Message {
//check sign
step1 := func(data queue.Message) queue.Message {
if data.Err() != nil {
return data
}
return mem.checkSign(data)
}
chs := make([]<-chan queue.Message, processNum)
for i := 0; i < processNum; i++ {
chs[i] = step(mem.done, mem.in, step1)
}
out1 := merge(mem.done, chs)
//checktx remote
step2 := func(data queue.Message) queue.Message {
if data.Err() != nil {
return data
}
return mem.checkTxRemote(data)
}
chs2 := make([]<-chan queue.Message, processNum)
for i := 0; i < processNum; i++ {
chs2[i] = step(mem.done, out1, step2)
}
return merge(mem.done, chs2)
}
// 处理其他模块的消息
func (mem *Mempool) eventProcess() {
defer mem.wg.Done()
defer close(mem.in)
//event process
mem.out = mem.pipeLine()
mlog.Info("mempool piple line start")
mem.wg.Add(1)
go mem.reply()
for msg := range mem.client.Recv() {
mlog.Debug("mempool recv", "msgid", msg.ID, "msg", types.GetEventName(int(msg.Ty)))
beg := types.Now()
switch msg.Ty {
case types.EventTx:
mem.eventTx(msg)
case types.EventGetMempool:
// 消息类型EventGetMempool:获取mempool内所有交易
mem.eventGetMempool(msg)
case types.EventTxList:
// 消息类型EventTxList:获取mempool中一定数量交易
mem.eventTxList(msg)
case types.EventDelTxList:
// 消息类型EventDelTxList:获取mempool中一定数量交易,并把这些交易从mempool中删除
mem.eventDelTxList(msg)
case types.EventAddBlock:
// 消息类型EventAddBlock:将添加到区块内的交易从mempool中删除
mem.eventAddBlock(msg)
case types.EventGetMempoolSize:
// 消息类型EventGetMempoolSize:获取mempool大小
mem.eventGetMempoolSize(msg)
case types.EventGetLastMempool:
// 消息类型EventGetLastMempool:获取最新十条加入到mempool的交易
mem.eventGetLastMempool(msg)
case types.EventDelBlock:
// 回滚区块,把该区块内交易重新加回mempool
mem.eventDelBlock(msg)
case types.EventGetAddrTxs:
// 获取mempool中对应账户(组)所有交易
mem.eventGetAddrTxs(msg)
default:
}
mlog.Debug("mempool", "cost", types.Since(beg), "msg", types.GetEventName(int(msg.Ty)))
}
}
//EventTx 初步筛选后存入mempool
func (mem *Mempool) eventTx(msg queue.Message) {
if !mem.getSync() {
msg.Reply(mem.client.NewMessage("", types.EventReply, &types.Reply{Msg: []byte(types.ErrNotSync.Error())}))
mlog.Error("wrong tx", "err", types.ErrNotSync.Error())
} else {
checkedMsg := mem.checkTxs(msg)
select {
case mem.in <- checkedMsg:
case <-mem.done:
}
}
}
// EventGetMempool 获取Mempool内所有交易
func (mem *Mempool) eventGetMempool(msg queue.Message) {
msg.Reply(mem.client.NewMessage("rpc", types.EventReplyTxList,
&types.ReplyTxList{Txs: mem.filterTxList(0, nil)}))
}
// EventDelTxList 获取Mempool中一定数量交易,并把这些交易从Mempool中删除
func (mem *Mempool) eventDelTxList(msg queue.Message) {
hashList := msg.GetData().(*types.TxHashList)
if len(hashList.GetHashes()) == 0 {
msg.ReplyErr("EventDelTxList", types.ErrSize)
} else {
err := mem.RemoveTxs(hashList)
msg.ReplyErr("EventDelTxList", err)
}
}
// EventTxList 获取mempool中一定数量交易
func (mem *Mempool) eventTxList(msg queue.Message) {
hashList := msg.GetData().(*types.TxHashList)
if hashList.Count <= 0 {
msg.Reply(mem.client.NewMessage("", types.EventReplyTxList, types.ErrSize))
mlog.Error("not an valid size", "msg", msg)
} else {
txList := mem.getTxList(hashList)
msg.Reply(mem.client.NewMessage("", types.EventReplyTxList, &types.ReplyTxList{Txs: txList}))
}
}
// EventAddBlock 将添加到区块内的交易从mempool中删除
func (mem *Mempool) eventAddBlock(msg queue.Message) {
block := msg.GetData().(*types.BlockDetail).Block
if block.Height > mem.Height() || (block.Height == 0 && mem.Height() == 0) {
header := &types.Header{}
header.BlockTime = block.BlockTime
header.Height = block.Height
header.StateHash = block.StateHash
mem.setHeader(header)
}
mem.RemoveTxsOfBlock(block)
}
// EventGetMempoolSize 获取mempool大小
func (mem *Mempool) eventGetMempoolSize(msg queue.Message) {
memSize := int64(mem.Size())
msg.Reply(mem.client.NewMessage("rpc", types.EventMempoolSize,
&types.MempoolSize{Size: memSize}))
}
// EventGetLastMempool 获取最新十条加入到mempool的交易
func (mem *Mempool) eventGetLastMempool(msg queue.Message) {
txList := mem.GetLatestTx()
msg.Reply(mem.client.NewMessage("rpc", types.EventReplyTxList,
&types.ReplyTxList{Txs: txList}))
}
// EventDelBlock 回滚区块,把该区块内交易重新加回mempool
func (mem *Mempool) eventDelBlock(msg queue.Message) {
block := msg.GetData().(*types.BlockDetail).Block
if block.Height != mem.GetHeader().GetHeight() {
return
}
lastHeader, err := mem.GetLastHeader()
if err != nil {
mlog.Error(err.Error())
return
}
h := lastHeader.(queue.Message).Data.(*types.Header)
mem.setHeader(h)
mem.delBlock(block)
}
// eventGetAddrTxs 获取mempool中对应账户(组)所有交易
func (mem *Mempool) eventGetAddrTxs(msg queue.Message) {
addrs := msg.GetData().(*types.ReqAddrs)
txlist := mem.GetAccTxs(addrs)
msg.Reply(mem.client.NewMessage("", types.EventReplyAddrTxs, txlist))
}
func (mem *Mempool) checkSign(data queue.Message) queue.Message {
tx, ok := data.GetData().(types.TxGroup)
if ok && tx.CheckSign() {
return data
}
mlog.Error("wrong tx", "err", types.ErrSign)
data.Data = types.ErrSign
return data
}
// 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 init
import (
_ "github.com/33cn/chain33/system/mempool/timeline" //最简单的排队模式,按照时间
)
package mempool
import (
"github.com/33cn/chain33/common/listmap"
"github.com/33cn/chain33/types"
)
//LastTxCache 最后放入cache的交易
type LastTxCache struct {
max int
l *listmap.ListMap
}
//NewLastTxCache 创建最后交易的cache
func NewLastTxCache(size int) *LastTxCache {
return &LastTxCache{
max: size,
l: listmap.New(),
}
}
//GetLatestTx 返回最新十条加入到txCache的交易
func (cache *LastTxCache) GetLatestTx() (txs []*types.Transaction) {
cache.l.Walk(func(v interface{}) bool {
txs = append(txs, v.(*types.Transaction))
return true
})
return txs
}
//Remove remove tx of last cache
func (cache *LastTxCache) Remove(tx *types.Transaction) {
cache.l.Remove(string(tx.Hash()))
}
//Push tx into LastTxCache
func (cache *LastTxCache) Push(tx *types.Transaction) {
if cache.l.Size() >= cache.max {
v := cache.l.GetTop()
if v != nil {
cache.Remove(v.(*types.Transaction))
}
}
cache.l.Push(string(tx.Hash()), tx)
}
// 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 mempool
import (
"github.com/33cn/chain33/queue"
"github.com/33cn/chain33/types"
)
//Create 创建一个mempool模块
type Create func(cfg *types.Mempool, sub []byte) queue.Module
var regMempool = make(map[string]Create)
//Reg 注册一个create
func Reg(name string, create Create) {
if create == nil {
panic("Mempool: Register driver is nil")
}
if _, dup := regMempool[name]; dup {
panic("Mempool: Register called twice for driver " + name)
}
regMempool[name] = create
}
//Load 加载一个create
func Load(name string) (create Create, err error) {
if driver, ok := regMempool[name]; ok {
return driver, nil
}
return nil, types.ErrNotFound
}
...@@ -14,12 +14,17 @@ import ( ...@@ -14,12 +14,17 @@ import (
"github.com/33cn/chain33/common/address" "github.com/33cn/chain33/common/address"
"github.com/33cn/chain33/common/crypto" "github.com/33cn/chain33/common/crypto"
"github.com/33cn/chain33/common/limits" "github.com/33cn/chain33/common/limits"
"github.com/33cn/chain33/common/log"
"github.com/33cn/chain33/executor" "github.com/33cn/chain33/executor"
"github.com/33cn/chain33/queue" "github.com/33cn/chain33/queue"
"github.com/33cn/chain33/store" "github.com/33cn/chain33/store"
_ "github.com/33cn/chain33/system" _ "github.com/33cn/chain33/system/consensus/init"
_ "github.com/33cn/chain33/system/crypto/init"
cty "github.com/33cn/chain33/system/dapp/coins/types" cty "github.com/33cn/chain33/system/dapp/coins/types"
_ "github.com/33cn/chain33/system/dapp/init"
_ "github.com/33cn/chain33/system/store/init"
"github.com/33cn/chain33/types" "github.com/33cn/chain33/types"
"github.com/stretchr/testify/assert"
) )
//----------------------------- data for testing --------------------------------- //----------------------------- data for testing ---------------------------------
...@@ -63,10 +68,6 @@ var blk = &types.Block{ ...@@ -63,10 +68,6 @@ var blk = &types.Block{
Txs: []*types.Transaction{tx3, tx5}, Txs: []*types.Transaction{tx3, tx5},
} }
func mergeList(done <-chan struct{}, cs ...<-chan queue.Message) <-chan queue.Message {
return merge(done, cs)
}
func init() { func init() {
err := limits.SetLimits() err := limits.SetLimits()
if err != nil { if err != nil {
...@@ -74,14 +75,7 @@ func init() { ...@@ -74,14 +75,7 @@ func init() {
} }
random = rand.New(rand.NewSource(types.Now().UnixNano())) random = rand.New(rand.NewSource(types.Now().UnixNano()))
queue.DisableLog() queue.DisableLog()
// DisableLog() // 不输出任何log log.SetLogLevel("err") // 输出WARN(含)以下log
// SetLogLevel("debug") // 输出DBUG(含)以下log
// SetLogLevel("info") // 输出INFO(含)以下log
SetLogLevel("info") // 输出WARN(含)以下log
// SetLogLevel("eror") // 输出EROR(含)以下log
// SetLogLevel("crit") // 输出CRIT(含)以下log
// SetLogLevel("") // 输出所有log
// maxTxNumPerAccount = 10000
mainPriv = getprivkey("CC38546E9E659D15E6B4893F0AB32A06D103931A8230B0BDE71459D2B27D6944") mainPriv = getprivkey("CC38546E9E659D15E6B4893F0AB32A06D103931A8230B0BDE71459D2B27D6944")
tx1.Sign(types.SECP256K1, privKey) tx1.Sign(types.SECP256K1, privKey)
tx2.Sign(types.SECP256K1, privKey) tx2.Sign(types.SECP256K1, privKey)
...@@ -98,7 +92,6 @@ func init() { ...@@ -98,7 +92,6 @@ func init() {
tx13.Sign(types.SECP256K1, privKey) tx13.Sign(types.SECP256K1, privKey)
tx14.Sign(types.SECP256K1, privKey) tx14.Sign(types.SECP256K1, privKey)
tx15.Sign(types.SECP256K1, privKey) tx15.Sign(types.SECP256K1, privKey)
} }
func getprivkey(key string) crypto.PrivKey { func getprivkey(key string) crypto.PrivKey {
...@@ -119,7 +112,8 @@ func getprivkey(key string) crypto.PrivKey { ...@@ -119,7 +112,8 @@ func getprivkey(key string) crypto.PrivKey {
func initEnv3() (queue.Queue, queue.Module, queue.Module, *Mempool) { func initEnv3() (queue.Queue, queue.Module, queue.Module, *Mempool) {
var q = queue.New("channel") var q = queue.New("channel")
cfg, sub := types.InitCfg("../cmd/chain33/chain33.test.toml") cfg, sub := types.InitCfg("../../cmd/chain33/chain33.test.toml")
types.Init(cfg.Title, cfg)
cfg.Consensus.Minerstart = false cfg.Consensus.Minerstart = false
chain := blockchain.New(cfg.BlockChain) chain := blockchain.New(cfg.BlockChain)
chain.SetQueueClient(q.Client()) chain.SetQueueClient(q.Client())
...@@ -130,43 +124,45 @@ func initEnv3() (queue.Queue, queue.Module, queue.Module, *Mempool) { ...@@ -130,43 +124,45 @@ func initEnv3() (queue.Queue, queue.Module, queue.Module, *Mempool) {
types.SetMinFee(0) types.SetMinFee(0)
s := store.New(cfg.Store, sub.Store) s := store.New(cfg.Store, sub.Store)
s.SetQueueClient(q.Client()) s.SetQueueClient(q.Client())
mem := New(cfg.MemPool) mem := NewMempool(cfg.Mempool)
mem.SetQueueCache(NewSimpleQueue(int(cfg.Mempool.PoolCacheSize)))
mem.SetQueueClient(q.Client()) mem.SetQueueClient(q.Client())
mem.setSync(true) mem.Wait()
mem.WaitPollLastHeader()
return q, chain, s, mem return q, chain, s, mem
} }
func initEnv2(size int) (queue.Queue, *Mempool) { func initEnv2(size int) (queue.Queue, *Mempool) {
var q = queue.New("channel") var q = queue.New("channel")
cfg, _ := types.InitCfg("../cmd/chain33/chain33.test.toml") cfg, _ := types.InitCfg("../../cmd/chain33/chain33.test.toml")
types.Init(cfg.Title, cfg)
blockchainProcess(q) blockchainProcess(q)
execProcess(q) execProcess(q)
mem := New(cfg.MemPool) cfg.Mempool.PoolCacheSize = int64(size)
mem := NewMempool(cfg.Mempool)
mem.SetQueueCache(NewSimpleQueue(size))
mem.SetQueueClient(q.Client()) mem.SetQueueClient(q.Client())
mem.setSync(true) mem.setSync(true)
if size > 0 {
mem.Resize(size)
}
mem.SetMinFee(0) mem.SetMinFee(0)
mem.WaitPollLastHeader() mem.Wait()
return q, mem return q, mem
} }
func initEnv(size int) (queue.Queue, *Mempool) { func initEnv(size int) (queue.Queue, *Mempool) {
if size == 0 {
size = 100
}
var q = queue.New("channel") var q = queue.New("channel")
cfg, _ := types.InitCfg("../cmd/chain33/chain33.test.toml") cfg, _ := types.InitCfg("../../cmd/chain33/chain33.test.toml")
types.Init(cfg.Title, cfg)
blockchainProcess(q) blockchainProcess(q)
execProcess(q) execProcess(q)
mem := New(cfg.MemPool) cfg.Mempool.PoolCacheSize = int64(size)
mem := NewMempool(cfg.Mempool)
mem.SetQueueCache(NewSimpleQueue(size))
mem.SetQueueClient(q.Client()) mem.SetQueueClient(q.Client())
mem.setSync(true) mem.setSync(true)
if size > 0 {
mem.Resize(size)
}
mem.SetMinFee(types.GInt("MinFee")) mem.SetMinFee(types.GInt("MinFee"))
mem.WaitPollLastHeader() mem.Wait()
return q, mem return q, mem
} }
...@@ -211,21 +207,19 @@ func TestAddEmptyTx(t *testing.T) { ...@@ -211,21 +207,19 @@ func TestAddEmptyTx(t *testing.T) {
} }
func TestAddTx(t *testing.T) { func TestAddTx(t *testing.T) {
q, mem := initEnv(0) q, mem := initEnv(1)
defer q.Close() defer q.Close()
defer mem.Close() defer mem.Close()
msg := mem.client.NewMessage("mempool", types.EventTx, tx2) msg := mem.client.NewMessage("mempool", types.EventTx, tx2)
mem.client.Send(msg, true) mem.client.Send(msg, true)
mem.client.Wait(msg) mem.client.Wait(msg)
if mem.Size() != 1 { if mem.Size() != 1 {
t.Error("TestAddTx failed") t.Error("TestAddTx failed")
} }
} }
func TestAddDuplicatedTx(t *testing.T) { func TestAddDuplicatedTx(t *testing.T) {
q, mem := initEnv(0) q, mem := initEnv(100)
defer q.Close() defer q.Close()
defer mem.Close() defer mem.Close()
...@@ -330,47 +324,16 @@ func add10Tx(client queue.Client) error { ...@@ -330,47 +324,16 @@ func add10Tx(client queue.Client) error {
if err != nil { if err != nil {
return err return err
} }
txs := []*types.Transaction{tx5, tx6, tx7, tx8, tx9, tx10}
msg5 := client.NewMessage("mempool", types.EventTx, tx5) for _, tx := range txs {
msg6 := client.NewMessage("mempool", types.EventTx, tx6) msg := client.NewMessage("mempool", types.EventTx, tx)
msg7 := client.NewMessage("mempool", types.EventTx, tx7) client.Send(msg, true)
msg8 := client.NewMessage("mempool", types.EventTx, tx8) _, err = client.Wait(msg)
msg9 := client.NewMessage("mempool", types.EventTx, tx9) if err != nil {
msg10 := client.NewMessage("mempool", types.EventTx, tx10) return err
}
client.Send(msg5, true)
_, err = client.Wait(msg5)
if err != nil {
return err
}
client.Send(msg6, true)
_, err = client.Wait(msg6)
if err != nil {
return err
}
client.Send(msg7, true)
_, err = client.Wait(msg7)
if err != nil {
return err
}
client.Send(msg8, true)
_, err = client.Wait(msg8)
if err != nil {
return err
}
client.Send(msg9, true)
_, err = client.Wait(msg9)
if err != nil {
return err
} }
return nil
client.Send(msg10, true)
_, err = client.Wait(msg10)
return err
} }
func TestGetTxList(t *testing.T) { func TestGetTxList(t *testing.T) {
...@@ -471,8 +434,8 @@ func TestAddMoreTxThanPoolSize(t *testing.T) { ...@@ -471,8 +434,8 @@ func TestAddMoreTxThanPoolSize(t *testing.T) {
mem.client.Send(msg5, true) mem.client.Send(msg5, true)
mem.client.Wait(msg5) mem.client.Wait(msg5)
if mem.Size() != 4 || mem.cache.Exists(tx5.Hash()) { if mem.Size() != 4 || mem.cache.Exist(string(tx5.Hash())) {
t.Error("TestAddMoreTxThanPoolSize failed", mem.Size(), mem.cache.Exists(tx5.Hash())) t.Error("TestAddMoreTxThanPoolSize failed", mem.Size(), mem.cache.Exist(string(tx5.Hash())))
} }
} }
...@@ -513,30 +476,18 @@ func TestAddBlockedTx(t *testing.T) { ...@@ -513,30 +476,18 @@ func TestAddBlockedTx(t *testing.T) {
msg1 := mem.client.NewMessage("mempool", types.EventTx, tx3) msg1 := mem.client.NewMessage("mempool", types.EventTx, tx3)
err := mem.client.Send(msg1, true) err := mem.client.Send(msg1, true)
if err != nil { assert.Nil(t, err)
t.Error(err)
return
}
_, err = mem.client.Wait(msg1) _, err = mem.client.Wait(msg1)
if err != nil { assert.Nil(t, err)
t.Error(err)
return
}
blkDetail := &types.BlockDetail{Block: blk} blkDetail := &types.BlockDetail{Block: blk}
msg2 := mem.client.NewMessage("mempool", types.EventAddBlock, blkDetail) msg2 := mem.client.NewMessage("mempool", types.EventAddBlock, blkDetail)
mem.client.Send(msg2, false) mem.client.Send(msg2, false)
msg3 := mem.client.NewMessage("mempool", types.EventTx, tx3) msg3 := mem.client.NewMessage("mempool", types.EventTx, tx3)
err = mem.client.Send(msg3, true) err = mem.client.Send(msg3, true)
if err != nil { assert.Nil(t, err)
t.Error(err)
return
}
resp, err := mem.client.Wait(msg3) resp, err := mem.client.Wait(msg3)
if err != nil { assert.Nil(t, err)
t.Error(err)
return
}
if string(resp.GetData().(*types.Reply).GetMsg()) != types.ErrDupTx.Error() { if string(resp.GetData().(*types.Reply).GetMsg()) != types.ErrDupTx.Error() {
t.Error("TestAddBlockedTx failed") t.Error("TestAddBlockedTx failed")
} }
...@@ -553,7 +504,7 @@ func TestDuplicateMempool(t *testing.T) { ...@@ -553,7 +504,7 @@ func TestDuplicateMempool(t *testing.T) {
t.Error("add tx error", err.Error()) t.Error("add tx error", err.Error())
return return
} }
assert.Equal(t, mem.Size(), 10)
msg := mem.client.NewMessage("mempool", types.EventGetMempool, nil) msg := mem.client.NewMessage("mempool", types.EventGetMempool, nil)
mem.client.Send(msg, true) mem.client.Send(msg, true)
...@@ -674,6 +625,23 @@ func TestCheckExpire2(t *testing.T) { ...@@ -674,6 +625,23 @@ func TestCheckExpire2(t *testing.T) {
} }
} }
func TestCheckExpire3(t *testing.T) {
q, mem := initEnv(0)
defer q.Close()
defer mem.Close()
// add tx
err := add4Tx(mem.client)
if err != nil {
t.Error("add tx error", err.Error())
return
}
mem.setHeader(&types.Header{Height: 50, BlockTime: 1e9 + 1})
assert.Equal(t, mem.Size(), 4)
mem.removeExpired()
assert.Equal(t, mem.Size(), 3)
}
func TestWrongToAddr(t *testing.T) { func TestWrongToAddr(t *testing.T) {
q, mem := initEnv(0) q, mem := initEnv(0)
defer q.Close() defer q.Close()
...@@ -773,9 +741,7 @@ func TestAddTxGroup(t *testing.T) { ...@@ -773,9 +741,7 @@ func TestAddTxGroup(t *testing.T) {
q, mem := initEnv(0) q, mem := initEnv(0)
defer q.Close() defer q.Close()
defer mem.Close() defer mem.Close()
//copytx //copytx
ctx2 := *tx2 ctx2 := *tx2
ctx3 := *tx3 ctx3 := *tx3
ctx4 := *tx4 ctx4 := *tx4
...@@ -790,10 +756,9 @@ func TestAddTxGroup(t *testing.T) { ...@@ -790,10 +756,9 @@ func TestAddTxGroup(t *testing.T) {
} }
func BenchmarkMempool(b *testing.B) { func BenchmarkMempool(b *testing.B) {
q, mem := initEnv(0) q, mem := initEnv(10240)
defer q.Close() defer q.Close()
defer mem.Close() defer mem.Close()
maxTxNumPerAccount = 100000 maxTxNumPerAccount = 100000
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
to, _ := genaddress() to, _ := genaddress()
...@@ -813,6 +778,7 @@ func BenchmarkMempool(b *testing.B) { ...@@ -813,6 +778,7 @@ func BenchmarkMempool(b *testing.B) {
} }
func blockchainProcess(q queue.Queue) { func blockchainProcess(q queue.Queue) {
dup := make(map[string]bool)
go func() { go func() {
client := q.Client() client := q.Client()
client.Sub("blockchain") client.Sub("blockchain")
...@@ -821,6 +787,17 @@ func blockchainProcess(q queue.Queue) { ...@@ -821,6 +787,17 @@ func blockchainProcess(q queue.Queue) {
msg.Reply(client.NewMessage("", types.EventHeader, &types.Header{Height: 1, BlockTime: 1})) msg.Reply(client.NewMessage("", types.EventHeader, &types.Header{Height: 1, BlockTime: 1}))
} else if msg.Ty == types.EventIsSync { } else if msg.Ty == types.EventIsSync {
msg.Reply(client.NewMessage("", types.EventReplyIsSync, &types.IsCaughtUp{Iscaughtup: true})) msg.Reply(client.NewMessage("", types.EventReplyIsSync, &types.IsCaughtUp{Iscaughtup: true}))
} else if msg.Ty == types.EventTxHashList {
txs := msg.Data.(*types.TxHashList)
var hashlist [][]byte
for _, hash := range txs.Hashes {
if dup[string(hash)] {
hashlist = append(hashlist, hash)
continue
}
dup[string(hash)] = true
}
msg.Reply(client.NewMessage("consensus", types.EventTxHashListReply, &types.TxHashList{Hashes: hashlist}))
} }
} }
}() }()
......
...@@ -101,3 +101,7 @@ func BenchmarkStepMerge(b *testing.B) { ...@@ -101,3 +101,7 @@ func BenchmarkStepMerge(b *testing.B) {
} }
close(done) close(done)
} }
func mergeList(done <-chan struct{}, cs ...<-chan queue.Message) <-chan queue.Message {
return merge(done, cs)
}
// 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 mempool
import (
"github.com/33cn/chain33/common/listmap"
"github.com/33cn/chain33/types"
)
//SimpleQueue 简单队列模式(默认提供一个队列,便于测试)
type SimpleQueue struct {
txList *listmap.ListMap
maxsize int
}
//NewSimpleQueue 创建队列
func NewSimpleQueue(cacheSize int) *SimpleQueue {
return &SimpleQueue{
txList: listmap.New(),
maxsize: cacheSize,
}
}
//Exist 是否存在
func (cache *SimpleQueue) Exist(hash string) bool {
return cache.txList.Exist(hash)
}
//GetItem 获取数据通过 key
func (cache *SimpleQueue) GetItem(hash string) (*Item, error) {
item, err := cache.txList.GetItem(hash)
if err != nil {
return nil, err
}
return item.(*Item), nil
}
// Push 把给定tx添加到SimpleQueue;如果tx已经存在SimpleQueue中或Mempool已满则返回对应error
func (cache *SimpleQueue) Push(tx *Item) error {
hash := tx.Value.Hash()
if cache.Exist(string(hash)) {
return types.ErrTxExist
}
if cache.txList.Size() >= cache.maxsize {
return types.ErrMemFull
}
cache.txList.Push(string(hash), tx)
return nil
}
// Remove 删除数据
func (cache *SimpleQueue) Remove(hash string) error {
cache.txList.Remove(hash)
return nil
}
// Size 数据总数
func (cache *SimpleQueue) Size() int {
return cache.txList.Size()
}
// Walk 遍历整个队列
func (cache *SimpleQueue) Walk(count int, cb func(value *Item) bool) {
i := 0
cache.txList.Walk(func(item interface{}) bool {
if !cb(item.(*Item)) {
return false
}
i++
return i != count
})
}
// 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 mempool
import (
"testing"
"github.com/33cn/chain33/types"
"github.com/stretchr/testify/assert"
)
func TestCache(t *testing.T) {
cache := NewSimpleQueue(1)
tx := &types.Transaction{Payload: []byte("123")}
hash := string(tx.Hash())
assert.Equal(t, false, cache.Exist(hash))
item1 := &Item{Value: tx, Priority: tx.Fee, EnterTime: types.Now().Unix()}
err := cache.Push(item1)
assert.Nil(t, err)
assert.Equal(t, true, cache.Exist(hash))
it, err := cache.GetItem(hash)
assert.Nil(t, err)
assert.Equal(t, item1, it)
_, err = cache.GetItem(hash + ":")
assert.Equal(t, types.ErrNotFound, err)
err = cache.Push(item1)
assert.Equal(t, types.ErrTxExist, err)
tx2 := &types.Transaction{Payload: []byte("1234")}
item2 := &Item{Value: tx2, Priority: tx.Fee, EnterTime: types.Now().Unix()}
err = cache.Push(item2)
assert.Equal(t, types.ErrMemFull, err)
cache.Remove(hash)
assert.Equal(t, 0, cache.Size())
//push to item
cache = NewSimpleQueue(2)
cache.Push(item1)
cache.Push(item2)
assert.Equal(t, 2, cache.Size())
var data [2]*Item
i := 0
cache.Walk(1, func(value *Item) bool {
data[i] = value
i++
return true
})
assert.Equal(t, 1, i)
assert.Equal(t, data[0], item1)
i = 0
cache.Walk(2, func(value *Item) bool {
data[i] = value
i++
return true
})
assert.Equal(t, 2, i)
assert.Equal(t, data[0], item1)
assert.Equal(t, data[1], item2)
i = 0
cache.Walk(2, func(value *Item) bool {
data[i] = value
i++
return false
})
assert.Equal(t, 1, i)
}
// 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 timeline
import (
"github.com/33cn/chain33/queue"
drivers "github.com/33cn/chain33/system/mempool"
"github.com/33cn/chain33/types"
)
func init() {
drivers.Reg("timeline", New)
}
type subConfig struct {
PoolCacheSize int64 `json:"poolCacheSize"`
}
//New 创建timeline cache 结构的 mempool
func New(cfg *types.Mempool, sub []byte) queue.Module {
c := drivers.NewMempool(cfg)
var subcfg subConfig
types.MustDecode(sub, &subcfg)
if subcfg.PoolCacheSize == 0 {
subcfg.PoolCacheSize = cfg.PoolCacheSize
}
c.SetQueueCache(drivers.NewSimpleQueue(int(subcfg.PoolCacheSize)))
return c
}
// 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 timeline
import (
"encoding/json"
"testing"
"github.com/33cn/chain33/system/mempool"
"github.com/33cn/chain33/types"
)
func TestNewMempool(t *testing.T) {
sub, _ := json.Marshal(&subConfig{PoolCacheSize: 2})
module := New(&types.Mempool{}, sub)
mem := module.(*mempool.Mempool)
mem.Close()
}
...@@ -84,6 +84,9 @@ func (store *BaseStore) SetQueueClient(c queue.Client) { ...@@ -84,6 +84,9 @@ func (store *BaseStore) SetQueueClient(c queue.Client) {
}() }()
} }
//Wait wait for basestore ready
func (store *BaseStore) Wait() {}
func (store *BaseStore) processMessage(msg queue.Message) { func (store *BaseStore) processMessage(msg queue.Message) {
client := store.qclient client := store.qclient
if msg.Ty == types.EventStoreSet { if msg.Ty == types.EventStoreSet {
...@@ -216,14 +219,11 @@ func (t *StorelistQuery) IterateCallBack(key, value []byte) bool { ...@@ -216,14 +219,11 @@ func (t *StorelistQuery) IterateCallBack(key, value []byte) bool {
return false return false
} }
return false return false
} }
return false return false
} }
slog.Error("StoreListReply.IterateCallBack unsupported mode", "mode", t.Mode) slog.Error("StoreListReply.IterateCallBack unsupported mode", "mode", t.Mode)
return true return true
} }
func cloneByte(v []byte) []byte { func cloneByte(v []byte) []byte {
......
...@@ -11,7 +11,7 @@ type Config struct { ...@@ -11,7 +11,7 @@ type Config struct {
Log *Log `protobuf:"bytes,2,opt,name=log" json:"log,omitempty"` Log *Log `protobuf:"bytes,2,opt,name=log" json:"log,omitempty"`
Store *Store `protobuf:"bytes,3,opt,name=store" json:"store,omitempty"` Store *Store `protobuf:"bytes,3,opt,name=store" json:"store,omitempty"`
Consensus *Consensus `protobuf:"bytes,5,opt,name=consensus" json:"consensus,omitempty"` Consensus *Consensus `protobuf:"bytes,5,opt,name=consensus" json:"consensus,omitempty"`
MemPool *MemPool `protobuf:"bytes,6,opt,name=memPool" json:"memPool,omitempty"` Mempool *Mempool `protobuf:"bytes,6,opt,name=mempool" json:"memPool,omitempty"`
BlockChain *BlockChain `protobuf:"bytes,7,opt,name=blockChain" json:"blockChain,omitempty"` BlockChain *BlockChain `protobuf:"bytes,7,opt,name=blockChain" json:"blockChain,omitempty"`
Wallet *Wallet `protobuf:"bytes,8,opt,name=wallet" json:"wallet,omitempty"` Wallet *Wallet `protobuf:"bytes,8,opt,name=wallet" json:"wallet,omitempty"`
P2P *P2P `protobuf:"bytes,9,opt,name=p2p" json:"p2p,omitempty"` P2P *P2P `protobuf:"bytes,9,opt,name=p2p" json:"p2p,omitempty"`
...@@ -52,12 +52,14 @@ type Log struct { ...@@ -52,12 +52,14 @@ type Log struct {
CallerFunction bool `protobuf:"varint,10,opt,name=callerFunction" json:"callerFunction,omitempty"` CallerFunction bool `protobuf:"varint,10,opt,name=callerFunction" json:"callerFunction,omitempty"`
} }
// MemPool 配置 // Mempool 配置
type MemPool struct { type Mempool struct {
PoolCacheSize int64 `protobuf:"varint,1,opt,name=poolCacheSize" json:"poolCacheSize,omitempty"` Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
MinTxFee int64 `protobuf:"varint,2,opt,name=minTxFee" json:"minTxFee,omitempty"` PoolCacheSize int64 `protobuf:"varint,1,opt,name=poolCacheSize" json:"poolCacheSize,omitempty"`
ForceAccept bool `protobuf:"varint,3,opt,name=forceAccept" json:"forceAccept,omitempty"` MinTxFee int64 `protobuf:"varint,2,opt,name=minTxFee" json:"minTxFee,omitempty"`
MaxTxNumPerAccount int64 `protobuf:"varint,4,opt,name=maxTxNumPerAccount" json:"maxTxNumPerAccount,omitempty"` ForceAccept bool `protobuf:"varint,3,opt,name=forceAccept" json:"forceAccept,omitempty"`
MaxTxNumPerAccount int64 `protobuf:"varint,4,opt,name=maxTxNumPerAccount" json:"maxTxNumPerAccount,omitempty"`
MaxTxLast int64 `protobuf:"varint,4,opt,name=maxTxLast" json:"maxTxLast,omitempty"`
} }
// Consensus 配置 // Consensus 配置
......
...@@ -225,6 +225,13 @@ func S(key string, value interface{}) { ...@@ -225,6 +225,13 @@ func S(key string, value interface{}) {
setChainConfig(key, value) setChainConfig(key, value)
} }
//SetTitleOnlyForTest set title only for test use
func SetTitleOnlyForTest(ti string) {
mu.Lock()
defer mu.Unlock()
title = ti
}
// Init 初始化 // Init 初始化
func Init(t string, cfg *Config) { func Init(t string, cfg *Config) {
mu.Lock() mu.Lock()
...@@ -243,7 +250,7 @@ func Init(t string, cfg *Config) { ...@@ -243,7 +250,7 @@ func Init(t string, cfg *Config) {
} else { } else {
setTestNet(cfg.TestNet) setTestNet(cfg.TestNet)
} }
if cfg.Exec.MinExecFee > cfg.MemPool.MinTxFee || cfg.MemPool.MinTxFee > cfg.Wallet.MinFee { if cfg.Exec.MinExecFee > cfg.Mempool.MinTxFee || cfg.Mempool.MinTxFee > cfg.Wallet.MinFee {
panic("config must meet: wallet.minFee >= mempool.minTxFee >= exec.minExecFee") panic("config must meet: wallet.minFee >= mempool.minTxFee >= exec.minExecFee")
} }
setMinFee(cfg.Exec.MinExecFee) setMinFee(cfg.Exec.MinExecFee)
...@@ -303,7 +310,6 @@ func SetMinFee(fee int64) { ...@@ -303,7 +310,6 @@ func SetMinFee(fee int64) {
} }
func isPara() bool { func isPara() bool {
//user.p.guodun.
return strings.Count(title, ".") == 3 && strings.HasPrefix(title, ParaKeyX) return strings.Count(title, ".") == 3 && strings.HasPrefix(title, ParaKeyX)
} }
...@@ -524,6 +530,7 @@ type subModule struct { ...@@ -524,6 +530,7 @@ type subModule struct {
Exec map[string]interface{} Exec map[string]interface{}
Consensus map[string]interface{} Consensus map[string]interface{}
Wallet map[string]interface{} Wallet map[string]interface{}
Mempool map[string]interface{}
} }
func readFile(path string) string { func readFile(path string) string {
...@@ -548,6 +555,7 @@ func parseSubModule(cfg *subModule) (*ConfigSubModule, error) { ...@@ -548,6 +555,7 @@ func parseSubModule(cfg *subModule) (*ConfigSubModule, error) {
subcfg.Exec = parseItem(cfg.Exec) subcfg.Exec = parseItem(cfg.Exec)
subcfg.Consensus = parseItem(cfg.Consensus) subcfg.Consensus = parseItem(cfg.Consensus)
subcfg.Wallet = parseItem(cfg.Wallet) subcfg.Wallet = parseItem(cfg.Wallet)
subcfg.Mempool = parseItem(cfg.Mempool)
return &subcfg, nil return &subcfg, nil
} }
......
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
) )
var slash = []byte("-") var slash = []byte("-")
var sharp = []byte("#")
//Debug 调试开关 //Debug 调试开关
var Debug = false var Debug = false
......
...@@ -109,8 +109,8 @@ func CallExecNewTx(execName, action string, param interface{}) ([]byte, error) { ...@@ -109,8 +109,8 @@ func CallExecNewTx(execName, action string, param interface{}) ([]byte, error) {
return FormatTxEncode(execName, tx) return FormatTxEncode(execName, tx)
} }
// CallCreateTx 构造交易信息 //CallCreateTransaction 创建一个交易
func CallCreateTx(execName, action string, param Message) ([]byte, error) { func CallCreateTransaction(execName, action string, param Message) (*Transaction, error) {
exec := LoadExecutorType(execName) exec := LoadExecutorType(execName)
if exec == nil { if exec == nil {
tlog.Error("CallCreateTx", "Error", "exec not found") tlog.Error("CallCreateTx", "Error", "exec not found")
...@@ -121,9 +121,13 @@ func CallCreateTx(execName, action string, param Message) ([]byte, error) { ...@@ -121,9 +121,13 @@ func CallCreateTx(execName, action string, param Message) ([]byte, error) {
tlog.Error("CallCreateTx", "Error", "param in nil") tlog.Error("CallCreateTx", "Error", "param in nil")
return nil, ErrInvalidParam return nil, ErrInvalidParam
} }
tx, err := exec.Create(action, param) return exec.Create(action, param)
}
// CallCreateTx 构造交易信息
func CallCreateTx(execName, action string, param Message) ([]byte, error) {
tx, err := CallCreateTransaction(execName, action, param)
if err != nil { if err != nil {
tlog.Error("CallCreateTx", "Error", err)
return nil, err return nil, err
} }
return FormatTxEncode(execName, tx) return FormatTxEncode(execName, tx)
......
...@@ -132,5 +132,4 @@ func TestCallCreateTx(t *testing.T) { ...@@ -132,5 +132,4 @@ func TestCallCreateTx(t *testing.T) {
assert.Equal(t, tx.Execer, []byte("manage")) assert.Equal(t, tx.Execer, []byte("manage"))
fee, _ = tx.GetRealFee(GInt("MinFee")) fee, _ = tx.GetRealFee(GInt("MinFee"))
assert.Equal(t, tx.Fee, fee) assert.Equal(t, tx.Fee, fee)
} }
...@@ -91,7 +91,7 @@ func (f *Forks) GetFork(title, key string) int64 { ...@@ -91,7 +91,7 @@ func (f *Forks) GetFork(title, key string) int64 {
if title == "local" { if title == "local" {
panic("title not exisit -> " + title) panic("title not exisit -> " + title)
} else { } else {
tlog.Error("getfork title not exisit -> " + title) tlog.Error("getfork title not exisit -> ", "title", title, "key", key)
} }
return MaxHeight return MaxHeight
} }
......
...@@ -49,6 +49,7 @@ message CreateTx { ...@@ -49,6 +49,7 @@ message CreateTx {
bool isToken = 6; bool isToken = 6;
string tokenSymbol = 7; string tokenSymbol = 7;
string execName = 8; string execName = 8;
string execer = 9;
} }
message CreateTransactionGroup { message CreateTransactionGroup {
......
...@@ -10,4 +10,5 @@ type ConfigSubModule struct { ...@@ -10,4 +10,5 @@ type ConfigSubModule struct {
Exec map[string][]byte Exec map[string][]byte
Consensus map[string][]byte Consensus map[string][]byte
Wallet map[string][]byte Wallet map[string][]byte
Mempool map[string][]byte
} }
...@@ -338,6 +338,7 @@ type CreateTx struct { ...@@ -338,6 +338,7 @@ type CreateTx struct {
IsToken bool `protobuf:"varint,6,opt,name=isToken,proto3" json:"isToken,omitempty"` IsToken bool `protobuf:"varint,6,opt,name=isToken,proto3" json:"isToken,omitempty"`
TokenSymbol string `protobuf:"bytes,7,opt,name=tokenSymbol,proto3" json:"tokenSymbol,omitempty"` TokenSymbol string `protobuf:"bytes,7,opt,name=tokenSymbol,proto3" json:"tokenSymbol,omitempty"`
ExecName string `protobuf:"bytes,8,opt,name=execName,proto3" json:"execName,omitempty"` ExecName string `protobuf:"bytes,8,opt,name=execName,proto3" json:"execName,omitempty"`
Execer string `protobuf:"bytes,9,opt,name=execer,proto3" json:"execer,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"` XXX_sizecache int32 `json:"-"`
...@@ -424,6 +425,13 @@ func (m *CreateTx) GetExecName() string { ...@@ -424,6 +425,13 @@ func (m *CreateTx) GetExecName() string {
return "" return ""
} }
func (m *CreateTx) GetExecer() string {
if m != nil {
return m.Execer
}
return ""
}
type CreateTransactionGroup struct { type CreateTransactionGroup struct {
Txs []string `protobuf:"bytes,1,rep,name=txs,proto3" json:"txs,omitempty"` Txs []string `protobuf:"bytes,1,rep,name=txs,proto3" json:"txs,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
...@@ -2000,87 +2008,87 @@ func init() { ...@@ -2000,87 +2008,87 @@ func init() {
func init() { proto.RegisterFile("transaction.proto", fileDescriptor_2cc4e03d2c28c490) } func init() { proto.RegisterFile("transaction.proto", fileDescriptor_2cc4e03d2c28c490) }
var fileDescriptor_2cc4e03d2c28c490 = []byte{ var fileDescriptor_2cc4e03d2c28c490 = []byte{
// 1300 bytes of a gzipped FileDescriptorProto // 1306 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x57, 0xdd, 0x8e, 0x13, 0xc7, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x57, 0xdd, 0x6e, 0x13, 0x47,
0x12, 0xd6, 0xcc, 0xd8, 0x5e, 0xbb, 0x6c, 0x38, 0xec, 0x08, 0x81, 0x85, 0x38, 0xb0, 0xa7, 0xc5, 0x14, 0xd6, 0xae, 0x7f, 0x62, 0x1f, 0x1b, 0x4a, 0x56, 0x28, 0x58, 0x88, 0x42, 0x3a, 0xa2, 0x12,
0x91, 0x10, 0x42, 0x5e, 0x69, 0x97, 0xbb, 0x73, 0xa4, 0x04, 0xd8, 0x08, 0xd0, 0x02, 0x49, 0x1a, 0x42, 0xc8, 0x91, 0x12, 0xee, 0x5a, 0xa9, 0x05, 0x52, 0x01, 0x0a, 0xd0, 0x76, 0x30, 0x50, 0xb5,
0x03, 0x51, 0x12, 0x45, 0xea, 0x1d, 0xd7, 0xda, 0x1d, 0xec, 0x69, 0xef, 0x4c, 0x7b, 0x19, 0xbf, 0x55, 0xa5, 0xc9, 0xfa, 0xc4, 0x9e, 0xc6, 0xde, 0x71, 0x76, 0xc7, 0x61, 0xfd, 0x02, 0xbd, 0x69,
0x40, 0x6e, 0x92, 0xbb, 0x3c, 0x52, 0x5e, 0x20, 0x8f, 0x14, 0x75, 0x75, 0xf7, 0x4c, 0x7b, 0x7f, 0xef, 0xfa, 0x48, 0x7d, 0x81, 0x3e, 0x46, 0x1f, 0xa3, 0x9a, 0x33, 0x33, 0xbb, 0xe3, 0xfc, 0x20,
0x10, 0x17, 0x91, 0x72, 0xd7, 0x5f, 0x75, 0xb9, 0x7e, 0xbf, 0xaa, 0x1e, 0xc3, 0xb6, 0x2e, 0x44, 0x2e, 0x2a, 0xf5, 0x6e, 0xbe, 0x33, 0xc7, 0xe7, 0xf7, 0x3b, 0x67, 0xc7, 0xb0, 0xa9, 0x73, 0x91,
0x5e, 0x8a, 0x4c, 0x4b, 0x95, 0x8f, 0x96, 0x85, 0xd2, 0x2a, 0x6d, 0xeb, 0xf5, 0x12, 0xcb, 0x5b, 0x15, 0x22, 0xd5, 0x52, 0x65, 0xc3, 0x45, 0xae, 0xb4, 0x4a, 0x5a, 0x7a, 0xb5, 0xc0, 0xe2, 0x66,
0x83, 0x4c, 0x2d, 0x16, 0x5e, 0xc8, 0x5e, 0xc1, 0x95, 0xc7, 0x65, 0x89, 0xba, 0x7c, 0x86, 0x39, 0x3f, 0x55, 0xf3, 0xb9, 0x17, 0xb2, 0x97, 0x70, 0xe5, 0x51, 0x51, 0xa0, 0x2e, 0x9e, 0x62, 0x86,
0x96, 0xb2, 0x4c, 0x6f, 0x40, 0x47, 0x2c, 0xd4, 0x2a, 0xd7, 0xc3, 0x78, 0x27, 0xba, 0x9f, 0x70, 0x85, 0x2c, 0x92, 0x2d, 0x68, 0x8b, 0xb9, 0x5a, 0x66, 0x7a, 0x10, 0x6f, 0x47, 0xf7, 0x1a, 0xdc,
0x87, 0xd2, 0x7b, 0x70, 0xa5, 0x40, 0xbd, 0x2a, 0xf2, 0xc7, 0x93, 0x49, 0x81, 0x65, 0x39, 0x4c, 0xa1, 0xe4, 0x2e, 0x5c, 0xc9, 0x51, 0x2f, 0xf3, 0xec, 0xd1, 0x78, 0x9c, 0x63, 0x51, 0x0c, 0x1a,
0x76, 0xa2, 0xfb, 0x3d, 0xbe, 0x29, 0x64, 0xbf, 0x45, 0x70, 0xdd, 0xda, 0x1b, 0x1b, 0xff, 0xc7, 0xdb, 0xd1, 0xbd, 0x2e, 0x5f, 0x17, 0xb2, 0x3f, 0x22, 0xb8, 0x6e, 0xed, 0x8d, 0x8c, 0xff, 0x23,
0x58, 0x8c, 0xd5, 0x57, 0x15, 0x66, 0xe9, 0x6d, 0xe8, 0x65, 0x4a, 0xe6, 0x5a, 0x7d, 0xc0, 0x7c, 0xcc, 0x47, 0xea, 0x9b, 0x12, 0xd3, 0xe4, 0x16, 0x74, 0x53, 0x25, 0x33, 0xad, 0x8e, 0x31, 0x1b,
0x18, 0xd1, 0x4f, 0x1b, 0xc1, 0xa5, 0x4e, 0x53, 0x68, 0xe5, 0x4a, 0x23, 0xf9, 0x1a, 0x70, 0x3a, 0x44, 0xf4, 0xd3, 0x5a, 0x70, 0xa9, 0xd3, 0x04, 0x9a, 0x99, 0xd2, 0x48, 0xbe, 0xfa, 0x9c, 0xce,
0xa7, 0xb7, 0xa0, 0x8b, 0x15, 0x66, 0xaf, 0xc5, 0x02, 0x87, 0x2d, 0x32, 0x54, 0xe3, 0xf4, 0x2a, 0xc9, 0x4d, 0xe8, 0x60, 0x89, 0xe9, 0x2b, 0x31, 0xc7, 0x41, 0x93, 0x0c, 0x55, 0x38, 0xb9, 0x0a,
0xc4, 0x5a, 0x0d, 0xdb, 0x24, 0x8d, 0xb5, 0x62, 0xbf, 0x44, 0x70, 0xd5, 0x86, 0xf3, 0x5e, 0xea, 0xb1, 0x56, 0x83, 0x16, 0x49, 0x63, 0xad, 0xd8, 0x6f, 0x11, 0x5c, 0xb5, 0xe1, 0xbc, 0x93, 0x7a,
0xd9, 0xa4, 0x10, 0x1f, 0xff, 0xa1, 0x40, 0x7e, 0xf6, 0x71, 0xf8, 0xb2, 0xfc, 0x8d, 0x71, 0x58, 0x3a, 0xce, 0xc5, 0xfb, 0xff, 0x29, 0x90, 0x5f, 0x7d, 0x1c, 0xbe, 0x2c, 0xff, 0x61, 0x1c, 0xd6,
0x5f, 0xad, 0xda, 0xd7, 0x21, 0xb4, 0xc9, 0x97, 0x51, 0x36, 0x01, 0x39, 0xeb, 0x74, 0x36, 0x86, 0x57, 0xb3, 0xf2, 0x75, 0x00, 0x2d, 0xf2, 0x65, 0x94, 0x4d, 0x40, 0xce, 0x3a, 0x9d, 0x8d, 0xe1,
0xcb, 0xf5, 0xe2, 0x48, 0xcd, 0xc9, 0x70, 0x8f, 0x3b, 0x14, 0x38, 0x4c, 0x42, 0x87, 0xec, 0xcf, 0x62, 0x35, 0x3f, 0x54, 0x33, 0x32, 0xdc, 0xe5, 0x0e, 0x05, 0x0e, 0x1b, 0xa1, 0x43, 0xf6, 0x4f,
0x08, 0xba, 0x4f, 0x0b, 0x14, 0x1a, 0xc7, 0x95, 0xf3, 0x14, 0x79, 0x4f, 0x97, 0x46, 0x79, 0x0d, 0x04, 0x9d, 0x27, 0x39, 0x0a, 0x8d, 0xa3, 0xd2, 0x79, 0x8a, 0xbc, 0xa7, 0x4b, 0xa3, 0xbc, 0x06,
0x92, 0x63, 0x44, 0x67, 0xc9, 0x1c, 0xeb, 0xb8, 0x5b, 0x41, 0xdc, 0x77, 0x00, 0x64, 0xdd, 0x17, 0x8d, 0x23, 0x44, 0x67, 0xc9, 0x1c, 0xab, 0xb8, 0x9b, 0x41, 0xdc, 0xb7, 0x01, 0x64, 0xd5, 0x17,
0xaa, 0x55, 0x97, 0x07, 0x92, 0x74, 0x08, 0x5b, 0xb2, 0x1c, 0x53, 0x7d, 0x3a, 0x74, 0xe9, 0x61, 0xaa, 0x55, 0x87, 0x07, 0x92, 0x64, 0x00, 0x1b, 0xb2, 0x18, 0x51, 0x7d, 0xda, 0x74, 0xe9, 0x61,
0xba, 0x03, 0x7d, 0x2a, 0xd3, 0x1b, 0x9b, 0xc9, 0x16, 0x05, 0x14, 0x8a, 0x36, 0x7a, 0xd3, 0xdd, 0xb2, 0x0d, 0x3d, 0x2a, 0xd3, 0x6b, 0x9b, 0xc9, 0x06, 0x05, 0x14, 0x8a, 0xd6, 0x7a, 0xd3, 0x39,
0xec, 0x0d, 0x7b, 0x00, 0x37, 0x5c, 0x46, 0xcd, 0x88, 0x3c, 0x2b, 0xd4, 0x6a, 0x69, 0xe2, 0xd6, 0xd3, 0x9b, 0x2d, 0x68, 0x9b, 0x33, 0xe6, 0x83, 0xae, 0x2d, 0x81, 0x45, 0xec, 0x3e, 0x6c, 0xb9,
0x55, 0x39, 0x8c, 0x76, 0x92, 0xfb, 0x3d, 0x6e, 0x8e, 0xec, 0x0e, 0x74, 0xdf, 0xe6, 0xa5, 0x9c, 0x4c, 0xeb, 0xd1, 0x79, 0x9a, 0xab, 0xe5, 0xc2, 0xe4, 0xa3, 0xcb, 0x62, 0x10, 0x6d, 0x37, 0xee,
0xe6, 0xe3, 0xca, 0xe4, 0x30, 0x11, 0x5a, 0x50, 0xfe, 0x03, 0x4e, 0x67, 0xa6, 0xa0, 0xff, 0x5a, 0x75, 0xb9, 0x39, 0xb2, 0xdb, 0xd0, 0x79, 0x93, 0x15, 0x72, 0x92, 0x8d, 0x4a, 0x93, 0xdb, 0x58,
0x3d, 0x11, 0x73, 0x91, 0x67, 0xa6, 0x40, 0xd7, 0xa1, 0xad, 0xab, 0xe7, 0x58, 0xb9, 0x1a, 0x59, 0x68, 0x41, 0x75, 0xe9, 0x73, 0x3a, 0x33, 0x05, 0xbd, 0x57, 0xea, 0xb1, 0x98, 0x89, 0x2c, 0x35,
0x60, 0x12, 0x59, 0x8a, 0xb5, 0x19, 0x11, 0x57, 0x74, 0x0f, 0xe9, 0xa6, 0x90, 0xa7, 0x1f, 0x70, 0x85, 0xbb, 0x0e, 0x2d, 0x5d, 0x3e, 0xc3, 0xd2, 0xd5, 0xce, 0x02, 0x93, 0xe0, 0x42, 0xac, 0xcc,
0xed, 0xc6, 0xc9, 0x43, 0x53, 0x5a, 0xac, 0x96, 0xb2, 0xf0, 0xd4, 0x72, 0x88, 0xfd, 0x04, 0xdd, 0xe8, 0xb8, 0x66, 0x78, 0x48, 0x37, 0xb9, 0x3c, 0x3d, 0xc6, 0x95, 0x1b, 0x33, 0x0f, 0x6d, 0xf0,
0x37, 0x72, 0x9a, 0xe3, 0x64, 0x5c, 0x19, 0x9d, 0x15, 0x05, 0xe7, 0x42, 0x72, 0xc8, 0x04, 0x4a, 0x0b, 0x99, 0x7b, 0xca, 0x39, 0xc4, 0x7e, 0x81, 0xce, 0x6b, 0x39, 0xc9, 0x70, 0x3c, 0x2a, 0x8d,
0xd2, 0xd8, 0x06, 0x4a, 0xb2, 0x1b, 0xd0, 0x59, 0xae, 0x8e, 0xbc, 0xa3, 0x01, 0x77, 0x88, 0x5a, 0xce, 0x92, 0x82, 0x73, 0x21, 0x39, 0x64, 0x02, 0x25, 0x69, 0x6c, 0x03, 0x25, 0xd9, 0x16, 0xb4,
0xba, 0x26, 0x1f, 0x6d, 0x1e, 0xeb, 0x35, 0xfb, 0x35, 0x86, 0x7e, 0x50, 0x17, 0x1b, 0x07, 0x66, 0x17, 0xcb, 0x43, 0xef, 0xa8, 0xcf, 0x1d, 0xa2, 0x56, 0xaf, 0xc8, 0x47, 0x8b, 0xc7, 0x7a, 0xc5,
0x58, 0x78, 0x1f, 0x16, 0xb9, 0x9c, 0xe6, 0x4a, 0x4c, 0x9c, 0x1b, 0x0f, 0xd3, 0x11, 0xf4, 0x8c, 0x7e, 0x8f, 0xa1, 0x17, 0xd4, 0x25, 0x28, 0xa2, 0xf3, 0x61, 0x91, 0xcb, 0x69, 0xa6, 0xc4, 0xd8,
0x47, 0xa1, 0x57, 0x85, 0xa5, 0x40, 0x7f, 0xef, 0xda, 0x88, 0x56, 0xcf, 0xe8, 0x8d, 0x97, 0xf3, 0xb9, 0xf1, 0x30, 0x19, 0x42, 0xd7, 0x78, 0x14, 0x7a, 0x99, 0x5b, 0x6a, 0xf4, 0x76, 0xaf, 0x0d,
0x46, 0xc5, 0x93, 0xa5, 0xd5, 0x90, 0xa5, 0xc9, 0xbd, 0x6d, 0x69, 0x65, 0x91, 0xa9, 0x6e, 0xae, 0x69, 0x25, 0x0d, 0x5f, 0x7b, 0x39, 0xaf, 0x55, 0x3c, 0x89, 0x9a, 0x35, 0x89, 0xea, 0xdc, 0x5b,
0xf2, 0x0c, 0x89, 0x0e, 0x09, 0xb7, 0xc0, 0x91, 0x72, 0xab, 0x26, 0xe5, 0x1d, 0x80, 0xa9, 0xe9, 0x96, 0x6e, 0x16, 0x99, 0xea, 0x66, 0x2a, 0x4b, 0x91, 0x68, 0xd2, 0xe0, 0x16, 0x38, 0xb2, 0x6e,
0xe6, 0x53, 0x22, 0x66, 0x97, 0x32, 0x0b, 0x24, 0xc6, 0xfa, 0x0c, 0xc5, 0x04, 0x8b, 0x61, 0xcf, 0x54, 0x64, 0xbd, 0x0d, 0x30, 0x31, 0xdd, 0x7c, 0x42, 0x84, 0xed, 0x50, 0x66, 0x81, 0xc4, 0x58,
0x66, 0x64, 0x11, 0x51, 0x14, 0x2b, 0x3d, 0x04, 0x47, 0x51, 0xac, 0x34, 0x7b, 0x04, 0x83, 0xa0, 0x9f, 0xa2, 0x18, 0x3b, 0x5a, 0xf4, 0xb9, 0x43, 0x44, 0x5d, 0x2c, 0xf5, 0x00, 0x1c, 0x75, 0xb1,
0x18, 0x65, 0x7a, 0xaf, 0x21, 0x48, 0x7f, 0x2f, 0x75, 0x59, 0x05, 0x1a, 0x96, 0x34, 0x5f, 0xc0, 0xd4, 0xec, 0x21, 0xf4, 0x83, 0x62, 0x14, 0xc9, 0xdd, 0x9a, 0x20, 0xbd, 0xdd, 0xc4, 0x65, 0x15,
0x15, 0x2e, 0xf3, 0x69, 0x9d, 0x6d, 0x3a, 0x82, 0xb6, 0xd4, 0xb8, 0xf0, 0x3f, 0x1c, 0xba, 0x1f, 0x68, 0x58, 0xd2, 0x7c, 0x05, 0x57, 0xb8, 0xcc, 0x26, 0x55, 0xb6, 0xc9, 0x10, 0x5a, 0x52, 0xe3,
0x6e, 0x28, 0xbd, 0xd0, 0xb8, 0xe0, 0x56, 0x8d, 0xbd, 0x80, 0xed, 0x73, 0x77, 0x41, 0x07, 0x8d, 0xdc, 0xff, 0x70, 0xe0, 0x7e, 0xb8, 0xa6, 0xf4, 0x5c, 0xe3, 0x9c, 0x5b, 0x35, 0xf6, 0x1c, 0x36,
0x95, 0xa6, 0x83, 0xb7, 0xc3, 0x7a, 0xc7, 0x74, 0xd5, 0x08, 0xd8, 0xb7, 0xd0, 0x6b, 0xe2, 0xb0, 0xcf, 0xdd, 0x05, 0x1d, 0x34, 0x56, 0xea, 0x0e, 0xde, 0x0a, 0xeb, 0x1d, 0xd3, 0x55, 0x2d, 0x60,
0xcd, 0x8e, 0x7c, 0xb3, 0x03, 0x93, 0xf1, 0x06, 0x29, 0x6e, 0x9f, 0x6d, 0xe1, 0x86, 0xc9, 0x1f, 0xdf, 0x43, 0xb7, 0x8e, 0xc3, 0x36, 0x3b, 0xf2, 0xcd, 0x0e, 0x4c, 0xc6, 0x6b, 0xa4, 0xb8, 0x75,
0x61, 0x60, 0xc8, 0xfb, 0xf5, 0x29, 0x16, 0xa7, 0x12, 0x69, 0x4e, 0x0b, 0xcc, 0xe4, 0xa9, 0xe3, 0xb6, 0x85, 0x6b, 0x26, 0x7f, 0x86, 0xbe, 0x21, 0xef, 0xb7, 0xa7, 0x98, 0x9f, 0x4a, 0xa4, 0xf9,
0x48, 0xc2, 0x3d, 0x34, 0x37, 0x47, 0x76, 0x36, 0xdc, 0x82, 0xf0, 0xd0, 0xdc, 0xe8, 0xea, 0x69, 0xcd, 0x31, 0x95, 0xa7, 0x8e, 0x23, 0x0d, 0xee, 0xa1, 0xb9, 0x39, 0xb4, 0xb3, 0xe1, 0x16, 0x87,
0xb0, 0x6f, 0x3c, 0x64, 0xbf, 0x47, 0xb0, 0xc5, 0xf1, 0x84, 0xc6, 0x23, 0x85, 0x96, 0x30, 0x53, 0x87, 0xe6, 0x46, 0x97, 0x4f, 0x82, 0x3d, 0xe4, 0x21, 0xfb, 0x33, 0x82, 0x0d, 0x8e, 0x27, 0x34,
0xe3, 0x16, 0x98, 0x70, 0xb2, 0xe3, 0xb9, 0x98, 0x92, 0xc1, 0x36, 0xa7, 0xb3, 0x21, 0x46, 0x56, 0x1e, 0x09, 0x34, 0x85, 0x99, 0x1a, 0xb7, 0xd8, 0x84, 0x93, 0x1d, 0xcd, 0xc4, 0x84, 0x0c, 0xb6,
0xdb, 0x6a, 0x73, 0x0b, 0x4c, 0x16, 0x13, 0x59, 0x20, 0x35, 0xc6, 0x31, 0xbc, 0x11, 0x58, 0x1a, 0x38, 0x9d, 0x0d, 0x31, 0xd2, 0xca, 0x56, 0x8b, 0x5b, 0x60, 0xb2, 0x18, 0xcb, 0x1c, 0xa9, 0x31,
0xc8, 0xe9, 0x4c, 0x7b, 0x92, 0x59, 0x64, 0x6c, 0xc9, 0x7c, 0x82, 0x95, 0x27, 0x19, 0x01, 0xf6, 0x8e, 0xe1, 0xb5, 0xc0, 0xd2, 0x40, 0x4e, 0xa6, 0xda, 0x93, 0xcc, 0x22, 0x63, 0x4b, 0x66, 0x63,
0x1d, 0x00, 0xc7, 0x93, 0x6f, 0x0a, 0x79, 0x2a, 0xb2, 0x75, 0xe3, 0x2f, 0xba, 0xd4, 0x5f, 0x7c, 0x2c, 0x3d, 0xc9, 0x08, 0xb0, 0x1f, 0x00, 0x38, 0x9e, 0x7c, 0x97, 0xcb, 0x53, 0x91, 0xae, 0x6a,
0xb9, 0xbf, 0x24, 0xf4, 0xc7, 0x6e, 0x42, 0xfb, 0x39, 0x56, 0x6e, 0xb9, 0x56, 0xf5, 0x72, 0xad, 0x7f, 0xd1, 0xa5, 0xfe, 0xe2, 0xcb, 0xfd, 0x35, 0x42, 0x7f, 0xec, 0x06, 0xb4, 0x9e, 0x61, 0xe9,
0xd8, 0x0a, 0xfa, 0x1c, 0x97, 0xf3, 0xf5, 0xb8, 0x7a, 0x91, 0x1f, 0x2b, 0x93, 0xf7, 0x4c, 0x94, 0x96, 0x6e, 0x59, 0x2d, 0xdd, 0x92, 0x2d, 0xa1, 0xc7, 0x71, 0x31, 0x5b, 0x8d, 0xca, 0xe7, 0xd9,
0x33, 0xbf, 0x7d, 0xcc, 0x39, 0xb0, 0x19, 0x5f, 0x9c, 0x43, 0x12, 0xe4, 0x90, 0xde, 0x83, 0x8e, 0x91, 0x32, 0x79, 0x4f, 0x45, 0x31, 0xf5, 0xdb, 0xc7, 0x9c, 0x03, 0x9b, 0xf1, 0xc5, 0x39, 0x34,
0xa0, 0x37, 0x68, 0xd8, 0x22, 0x1a, 0x0e, 0x1c, 0x0d, 0xe9, 0xb1, 0xe0, 0xee, 0x8e, 0xfd, 0x07, 0x82, 0x1c, 0x92, 0xbb, 0xd0, 0x16, 0xf4, 0x6d, 0x1a, 0x34, 0x89, 0x86, 0x7d, 0x47, 0x43, 0xfa,
0x7a, 0x1c, 0x4f, 0xc6, 0xd5, 0x4b, 0x59, 0xea, 0xcd, 0x44, 0x13, 0x97, 0x28, 0xdb, 0xaf, 0x23, 0x88, 0x70, 0x77, 0xc7, 0x3e, 0x83, 0x2e, 0xc7, 0x93, 0x51, 0xf9, 0x42, 0x16, 0x7a, 0x3d, 0xd1,
0x23, 0xa5, 0xcf, 0x1b, 0x0a, 0x0e, 0x30, 0xae, 0x9e, 0x8b, 0x72, 0x46, 0xbf, 0x31, 0x91, 0x8b, 0x86, 0x4b, 0x94, 0xed, 0x55, 0x91, 0x91, 0xd2, 0xc7, 0x0d, 0x05, 0x07, 0x18, 0x95, 0xcf, 0x44,
0x72, 0x86, 0xa5, 0x27, 0xb3, 0x45, 0x8d, 0xc3, 0x38, 0x70, 0x18, 0x2c, 0x84, 0x64, 0x27, 0x69, 0x31, 0xa5, 0xdf, 0x98, 0xc8, 0x45, 0x31, 0xc5, 0xc2, 0x93, 0xd9, 0xa2, 0xda, 0x61, 0x1c, 0x38,
0x16, 0x02, 0xfb, 0x3f, 0x0c, 0x82, 0x12, 0x95, 0xe9, 0x43, 0xc3, 0x2a, 0x3a, 0x9e, 0x89, 0x26, 0x0c, 0x16, 0x42, 0x63, 0xbb, 0x51, 0x2f, 0x04, 0xf6, 0x25, 0xf4, 0x83, 0x12, 0x15, 0xc9, 0x03,
0xd0, 0xe2, 0x5e, 0x85, 0x8d, 0x4c, 0x4f, 0x33, 0x94, 0x4b, 0xfd, 0x52, 0x4d, 0xcf, 0xcd, 0xc6, 0xc3, 0x2a, 0x3a, 0x9e, 0x89, 0x26, 0xd0, 0xe2, 0x5e, 0x85, 0x0d, 0x4d, 0x4f, 0x53, 0x94, 0x0b,
0x35, 0x48, 0xe6, 0x6a, 0xea, 0x06, 0xc3, 0x1c, 0x99, 0x30, 0xc4, 0x24, 0xfd, 0x73, 0xca, 0x77, 0xfd, 0x42, 0x4d, 0xce, 0xcd, 0xc6, 0x35, 0x68, 0xcc, 0xd4, 0xc4, 0x0d, 0x86, 0x39, 0x32, 0x61,
0x21, 0x3e, 0x7c, 0x47, 0xc3, 0xd7, 0xdf, 0xfb, 0x97, 0xf3, 0x79, 0x88, 0xeb, 0x77, 0x62, 0xbe, 0x88, 0x49, 0xfa, 0xe7, 0x94, 0xef, 0x40, 0x7c, 0xf0, 0x96, 0x86, 0xaf, 0xb7, 0xfb, 0x89, 0xf3,
0x42, 0x1e, 0x1f, 0xbe, 0x4b, 0xff, 0x0b, 0xad, 0xb9, 0x9a, 0x96, 0x14, 0x7f, 0x7f, 0x6f, 0xbb, 0x79, 0x80, 0xab, 0xb7, 0x62, 0xb6, 0x44, 0x1e, 0x1f, 0xbc, 0x4d, 0x3e, 0x87, 0xe6, 0x4c, 0x4d,
0x0e, 0xcb, 0xbb, 0xe7, 0x74, 0xcd, 0x0e, 0x4c, 0x65, 0x49, 0x76, 0x20, 0xb4, 0x38, 0xe7, 0xe6, 0x0a, 0x8a, 0xbf, 0xb7, 0xbb, 0x59, 0x85, 0xe5, 0xdd, 0x73, 0xba, 0x66, 0xfb, 0xa6, 0xb2, 0x24,
0x33, 0xad, 0x98, 0x37, 0x7b, 0x5c, 0x71, 0x2c, 0x57, 0x73, 0x1d, 0x70, 0x24, 0xba, 0x98, 0x23, 0xdb, 0x17, 0x5a, 0x9c, 0x73, 0xf3, 0x91, 0x56, 0xfe, 0x8e, 0xa0, 0x33, 0x2a, 0x39, 0x16, 0xcb,
0x96, 0xa9, 0x8e, 0x23, 0x8c, 0x48, 0x68, 0xb7, 0xf6, 0x45, 0xad, 0x8c, 0x75, 0x95, 0x3e, 0x82, 0x99, 0x0e, 0x38, 0x12, 0x5d, 0xcc, 0x11, 0xcb, 0x54, 0xc7, 0x11, 0x46, 0x24, 0xb4, 0x5b, 0xfb,
0x7e, 0x61, 0x5d, 0x4e, 0x84, 0x7b, 0xd2, 0xc3, 0x4a, 0xd7, 0xe1, 0xf3, 0x50, 0xcd, 0x4c, 0xc7, 0xa2, 0x56, 0xc6, 0xba, 0x4c, 0x1e, 0x42, 0x2f, 0xb7, 0x2e, 0xc7, 0xc2, 0x7d, 0xea, 0xc3, 0x4a,
0xd1, 0x5c, 0x65, 0x1f, 0xb4, 0x5c, 0xf8, 0xbd, 0xde, 0x08, 0xcc, 0xd2, 0xb6, 0x1e, 0xe8, 0xc5, 0x57, 0xe1, 0xf3, 0x50, 0xcd, 0x4c, 0xc7, 0xe1, 0x4c, 0xa5, 0xc7, 0x5a, 0xce, 0xfd, 0x5e, 0xaf,
0xee, 0xd0, 0x10, 0x04, 0x12, 0xf6, 0x47, 0x0c, 0xdb, 0x41, 0x1c, 0x07, 0xa8, 0x85, 0x9c, 0xbb, 0x05, 0x66, 0x69, 0x5b, 0x0f, 0xf4, 0x25, 0x6f, 0xd3, 0x10, 0x04, 0x12, 0xf6, 0x57, 0x0c, 0x9b,
0x68, 0xa3, 0x4f, 0x46, 0xfb, 0x90, 0xb6, 0x93, 0x09, 0x83, 0x32, 0xbd, 0x38, 0x52, 0xaf, 0x42, 0x41, 0x1c, 0xfb, 0xa8, 0x85, 0x9c, 0xb9, 0x68, 0xa3, 0x0f, 0x46, 0xfb, 0x80, 0xb6, 0x93, 0x09,
0x1b, 0xb1, 0x50, 0xea, 0xd8, 0xd6, 0xd8, 0x6c, 0x44, 0x42, 0x41, 0x15, 0x5b, 0x17, 0x57, 0xb1, 0x83, 0x32, 0xbd, 0x38, 0x52, 0xaf, 0x42, 0x1b, 0x31, 0x57, 0xea, 0xc8, 0xd6, 0xd8, 0x6c, 0x44,
0x1d, 0x4e, 0xda, 0x46, 0xae, 0x9d, 0xb3, 0xb9, 0x36, 0x5f, 0x4d, 0x5b, 0x1b, 0x5f, 0x4d, 0xb7, 0x42, 0x41, 0x15, 0x9b, 0x17, 0x57, 0xb1, 0x15, 0x4e, 0xda, 0x5a, 0xae, 0xed, 0xb3, 0xb9, 0xd6,
0xa0, 0x7b, 0x5c, 0xa8, 0x05, 0x6d, 0x3c, 0xf7, 0xcd, 0xe2, 0xf1, 0x99, 0xfa, 0xf4, 0xce, 0xd6, 0xaf, 0xa9, 0x8d, 0xb5, 0xd7, 0xd4, 0x4d, 0xe8, 0x1c, 0xe5, 0x6a, 0x4e, 0x1b, 0xcf, 0xbd, 0x65,
0x27, 0x98, 0x6d, 0xf8, 0xc4, 0x6c, 0x7f, 0x09, 0xe9, 0xb9, 0x22, 0x96, 0xe9, 0x83, 0x70, 0x7e, 0x3c, 0x3e, 0x53, 0x9f, 0xee, 0xd9, 0xfa, 0x04, 0xb3, 0x0d, 0x1f, 0x98, 0xed, 0xaf, 0x21, 0x39,
0x87, 0xe7, 0xcb, 0x68, 0xf5, 0xec, 0x14, 0xef, 0x40, 0xd7, 0x2d, 0x67, 0x9a, 0x55, 0x13, 0x9b, 0x57, 0xc4, 0x22, 0xb9, 0x1f, 0xce, 0xef, 0xe0, 0x7c, 0x19, 0xad, 0x9e, 0x9d, 0xe2, 0x6d, 0xe8,
0xff, 0x5e, 0xb2, 0x80, 0xed, 0xc2, 0x4d, 0x8e, 0x27, 0x07, 0x98, 0xa9, 0x09, 0x72, 0xf1, 0x31, 0xb8, 0xe5, 0x4c, 0xb3, 0x6a, 0x62, 0xf3, 0xef, 0x25, 0x0b, 0xd8, 0x0e, 0xdc, 0xe0, 0x78, 0xb2,
0xfc, 0x96, 0xb8, 0xf0, 0xeb, 0x88, 0xfd, 0x0f, 0x7a, 0x6f, 0x4b, 0x2c, 0xde, 0x17, 0x52, 0xd3, 0x8f, 0xa9, 0x1a, 0x23, 0x17, 0xef, 0xc3, 0xb7, 0xc4, 0x85, 0xaf, 0x23, 0xf6, 0x05, 0x74, 0xdf,
0x13, 0xaf, 0xd5, 0x52, 0x66, 0xb5, 0x8a, 0x01, 0xe6, 0xb5, 0xc8, 0x54, 0xae, 0xd1, 0xed, 0x85, 0x14, 0x98, 0xbf, 0xcb, 0xa5, 0xa6, 0x4f, 0xbc, 0x56, 0x0b, 0x99, 0x56, 0x2a, 0x06, 0x98, 0xaf,
0x1e, 0xf7, 0x90, 0xfd, 0x00, 0xfd, 0xb7, 0xcb, 0x69, 0x21, 0x26, 0xf8, 0x0a, 0xb5, 0x30, 0x25, 0x45, 0xaa, 0x32, 0x8d, 0x6e, 0x2f, 0x74, 0xb9, 0x87, 0xec, 0x27, 0xe8, 0xbd, 0x59, 0x4c, 0x72,
0xa4, 0x0e, 0xc8, 0x7c, 0x4a, 0x16, 0xba, 0xbc, 0xc6, 0xc6, 0xc8, 0x29, 0x16, 0xa5, 0x5f, 0xce, 0x31, 0xc6, 0x97, 0xa8, 0x85, 0x29, 0x21, 0x75, 0x40, 0x66, 0x13, 0xb2, 0xd0, 0xe1, 0x15, 0x36,
0x3d, 0xee, 0xe1, 0x65, 0xab, 0xf9, 0xc9, 0xdd, 0xef, 0xff, 0x3d, 0x95, 0x7a, 0xb6, 0x3a, 0x1a, 0x46, 0x4e, 0x31, 0x2f, 0xfc, 0x72, 0xee, 0x72, 0x0f, 0x2f, 0x5b, 0xcd, 0x8f, 0xef, 0xfc, 0xf8,
0x65, 0x6a, 0xb1, 0xbb, 0xbf, 0x9f, 0xe5, 0xbb, 0xd9, 0x4c, 0xc8, 0x7c, 0x7f, 0x7f, 0x97, 0x8a, 0xe9, 0x44, 0xea, 0xe9, 0xf2, 0x70, 0x98, 0xaa, 0xf9, 0xce, 0xde, 0x5e, 0x9a, 0xed, 0xa4, 0x53,
0x74, 0xd4, 0xa1, 0xff, 0x50, 0xfb, 0x7f, 0x05, 0x00, 0x00, 0xff, 0xff, 0x44, 0x92, 0x22, 0xa5, 0x21, 0xb3, 0xbd, 0xbd, 0x1d, 0x2a, 0xd2, 0x61, 0x9b, 0xfe, 0x5b, 0xed, 0xfd, 0x1b, 0x00, 0x00,
0x6d, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xc0, 0xaa, 0xbd, 0xba, 0x85, 0x0d, 0x00, 0x00,
} }
...@@ -41,6 +41,9 @@ type TxGroup interface { ...@@ -41,6 +41,9 @@ type TxGroup interface {
//ExecName 执行器name //ExecName 执行器name
func ExecName(name string) string { func ExecName(name string) string {
if len(name) > 1 && name[0] == '#' {
return name[1:]
}
if IsParaExecName(name) { if IsParaExecName(name) {
return name return name
} }
...@@ -61,7 +64,7 @@ func IsAllowExecName(name []byte, execer []byte) bool { ...@@ -61,7 +64,7 @@ func IsAllowExecName(name []byte, execer []byte) bool {
return false return false
} }
// name中不允许有 "-" // name中不允许有 "-"
if bytes.Contains(name, slash) { if bytes.Contains(name, slash) || bytes.Contains(name, sharp) {
return false return false
} }
if !bytes.Equal(name, execer) && !bytes.Equal(name, GetRealExecName(execer)) { if !bytes.Equal(name, execer) && !bytes.Equal(name, GetRealExecName(execer)) {
...@@ -435,6 +438,9 @@ func MustPBToJSON(req Message) []byte { ...@@ -435,6 +438,9 @@ func MustPBToJSON(req Message) []byte {
// MustDecode 数据是否已经编码 // MustDecode 数据是否已经编码
func MustDecode(data []byte, v interface{}) { func MustDecode(data []byte, v interface{}) {
if data == nil {
return
}
err := json.Unmarshal(data, v) err := json.Unmarshal(data, v)
if err != nil { if err != nil {
panic(err) panic(err)
......
...@@ -5,5 +5,48 @@ ...@@ -5,5 +5,48 @@
package types_test package types_test
import ( import (
"testing"
"github.com/33cn/chain33/common/address"
_ "github.com/33cn/chain33/system" _ "github.com/33cn/chain33/system"
"github.com/33cn/chain33/types"
"github.com/stretchr/testify/assert"
) )
//how to create transafer for para
func TestCallCreateTxPara(t *testing.T) {
ti := types.GetTitle()
defer types.SetTitleOnlyForTest(ti)
types.SetTitleOnlyForTest("user.p.sto.")
req := &types.CreateTx{
To: "184wj4nsgVxKyz2NhM3Yb5RK5Ap6AFRFq2",
Amount: 10,
Fee: 1,
Note: []byte("12312"),
IsWithdraw: false,
IsToken: false,
TokenSymbol: "",
ExecName: types.ExecName("coins"),
}
assert.True(t, types.IsPara())
tx, err := types.CallCreateTransaction("coins", "", req)
assert.Nil(t, err)
tx, err = types.FormatTx("coins", tx)
assert.Nil(t, err)
assert.Equal(t, "coins", string(tx.Execer))
assert.Equal(t, address.ExecAddress("coins"), tx.To)
tx, err = types.FormatTx(types.ExecName("coins"), tx)
assert.Nil(t, err)
assert.Equal(t, "user.p.sto.coins", string(tx.Execer))
assert.Equal(t, address.ExecAddress("user.p.sto.coins"), tx.To)
}
func TestExecName(t *testing.T) {
assert.Equal(t, types.ExecName("coins"), "coins")
ti := types.GetTitle()
defer types.SetTitleOnlyForTest(ti)
types.SetTitleOnlyForTest("user.p.sto.")
assert.Equal(t, types.ExecName("coins"), "user.p.sto.coins")
//#在exec前面加一个 # 表示不重写执行器
assert.Equal(t, types.ExecName("#coins"), "coins")
}
...@@ -46,6 +46,12 @@ func TestAllowExecName(t *testing.T) { ...@@ -46,6 +46,12 @@ func TestAllowExecName(t *testing.T) {
isok = IsAllowExecName([]byte("coins"), []byte("user.p.guodun.user.coins")) isok = IsAllowExecName([]byte("coins"), []byte("user.p.guodun.user.coins"))
assert.Equal(t, isok, true) assert.Equal(t, isok, true)
isok = IsAllowExecName([]byte("#coins"), []byte("user.p.guodun.user.coins"))
assert.Equal(t, isok, false)
isok = IsAllowExecName([]byte("coins-"), []byte("user.p.guodun.user.coins"))
assert.Equal(t, isok, false)
} }
func BenchmarkExecName(b *testing.B) { func BenchmarkExecName(b *testing.B) {
......
...@@ -130,7 +130,7 @@ func RunChain33(name string) { ...@@ -130,7 +130,7 @@ func RunChain33(name string) {
q := queue.New("channel") q := queue.New("channel")
log.Info("loading mempool module") log.Info("loading mempool module")
mem := mempool.New(cfg.MemPool) mem := mempool.New(cfg.Mempool, sub.Mempool)
mem.SetQueueClient(q.Client()) mem.SetQueueClient(q.Client())
log.Info("loading execs module") log.Info("loading execs module")
......
...@@ -26,7 +26,7 @@ var rootCmd = &cobra.Command{ ...@@ -26,7 +26,7 @@ var rootCmd = &cobra.Command{
var sendCmd = &cobra.Command{ var sendCmd = &cobra.Command{
Use: "send", Use: "send",
Short: "Send transaction in one move", Short: "Send transaction in one step",
Run: func(cmd *cobra.Command, args []string) {}, Run: func(cmd *cobra.Command, args []string) {},
} }
......
...@@ -105,6 +105,22 @@ func checkTxDupInner(txs []*types.TransactionCache) (ret []*types.TransactionCac ...@@ -105,6 +105,22 @@ func checkTxDupInner(txs []*types.TransactionCache) (ret []*types.TransactionCac
return ret return ret
} }
//CheckDupTx : check use txs []*types.Transaction and not []*types.TransactionCache
func CheckDupTx(client queue.Client, txs []*types.Transaction, height int64) (transactions []*types.Transaction, err error) {
txcache := make([]*types.TransactionCache, len(txs))
for i := 0; i < len(txcache); i++ {
txcache[i] = &types.TransactionCache{Transaction: txs[i]}
}
cache, err := CheckTxDup(client, txcache, height)
if err != nil {
return nil, err
}
for i := 0; i < len(cache); i++ {
transactions = append(transactions, cache[i].Transaction)
}
return transactions, nil
}
//CheckTxDup : check whether the tx is duplicated within the while chain //CheckTxDup : check whether the tx is duplicated within the while chain
func CheckTxDup(client queue.Client, txs []*types.TransactionCache, height int64) (transactions []*types.TransactionCache, err error) { func CheckTxDup(client queue.Client, txs []*types.TransactionCache, height int64) (transactions []*types.TransactionCache, err error) {
var checkHashList types.TxHashList var checkHashList types.TxHashList
......
...@@ -68,6 +68,7 @@ jrpcFuncWhitelist=["*"] ...@@ -68,6 +68,7 @@ jrpcFuncWhitelist=["*"]
grpcFuncWhitelist=["*"] grpcFuncWhitelist=["*"]
[mempool] [mempool]
name="timeline"
poolCacheSize=10240 poolCacheSize=10240
minTxFee=100000 minTxFee=100000
maxTxNumPerAccount=10000 maxTxNumPerAccount=10000
......
...@@ -57,7 +57,7 @@ type Chain33Mock struct { ...@@ -57,7 +57,7 @@ type Chain33Mock struct {
client queue.Client client queue.Client
api client.QueueProtocolAPI api client.QueueProtocolAPI
chain *blockchain.BlockChain chain *blockchain.BlockChain
mem *mempool.Mempool mem queue.Module
cs queue.Module cs queue.Module
exec *executor.Executor exec *executor.Executor
wallet queue.Module wallet queue.Module
...@@ -103,10 +103,10 @@ func newWithConfig(cfg *types.Config, sub *types.ConfigSubModule, mockapi client ...@@ -103,10 +103,10 @@ func newWithConfig(cfg *types.Config, sub *types.ConfigSubModule, mockapi client
mock.cs.SetQueueClient(q.Client()) mock.cs.SetQueueClient(q.Client())
lognode.Info("init consensus " + cfg.Consensus.Name) lognode.Info("init consensus " + cfg.Consensus.Name)
mock.mem = mempool.New(cfg.MemPool) mock.mem = mempool.New(cfg.Mempool, sub.Mempool)
mock.mem.SetQueueClient(q.Client()) mock.mem.SetQueueClient(q.Client())
mock.mem.Wait()
lognode.Info("init mempool") lognode.Info("init mempool")
mock.mem.WaitPollLastHeader()
if cfg.P2P.Enable { if cfg.P2P.Enable {
mock.network = p2p.New(cfg.P2P) mock.network = p2p.New(cfg.P2P)
mock.network.SetQueueClient(q.Client()) mock.network.SetQueueClient(q.Client())
...@@ -169,7 +169,7 @@ func (mock *Chain33Mock) GetBlockChain() *blockchain.BlockChain { ...@@ -169,7 +169,7 @@ func (mock *Chain33Mock) GetBlockChain() *blockchain.BlockChain {
func setFee(cfg *types.Config, fee int64) { func setFee(cfg *types.Config, fee int64) {
cfg.Exec.MinExecFee = fee cfg.Exec.MinExecFee = fee
cfg.MemPool.MinTxFee = fee cfg.Mempool.MinTxFee = fee
cfg.Wallet.MinFee = fee cfg.Wallet.MinFee = fee
if fee == 0 { if fee == 0 {
cfg.Exec.IsFree = true cfg.Exec.IsFree = true
...@@ -394,6 +394,9 @@ func (m *mockP2P) SetQueueClient(client queue.Client) { ...@@ -394,6 +394,9 @@ func (m *mockP2P) SetQueueClient(client queue.Client) {
}() }()
} }
//Wait for ready
func (m *mockP2P) Wait() {}
//Close : //Close :
func (m *mockP2P) Close() { func (m *mockP2P) Close() {
} }
...@@ -119,6 +119,9 @@ func New(cfg *types.Wallet, sub map[string][]byte) *Wallet { ...@@ -119,6 +119,9 @@ func New(cfg *types.Wallet, sub map[string][]byte) *Wallet {
return wallet return wallet
} }
//Wait for wallet ready
func (wallet *Wallet) Wait() {}
// RegisterMineStatusReporter 向钱包注册状态回报 // RegisterMineStatusReporter 向钱包注册状态回报
func (wallet *Wallet) RegisterMineStatusReporter(reporter wcom.MineStatusReport) error { func (wallet *Wallet) RegisterMineStatusReporter(reporter wcom.MineStatusReport) error {
if reporter == nil { if reporter == nil {
......
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