Commit aa055772 authored by kingwang's avatar kingwang Committed by 33cn

update chain33 08/12

parent 34c30c13
...@@ -40,8 +40,6 @@ var ( ...@@ -40,8 +40,6 @@ var (
HashToParaSeqPrefix = []byte("HashToParaSeq:") HashToParaSeqPrefix = []byte("HashToParaSeq:")
LastParaSequence = []byte("LastParaSequence") LastParaSequence = []byte("LastParaSequence")
storeLog = chainlog.New("submodule", "store") storeLog = chainlog.New("submodule", "store")
AddBlock int64 = 1
DelBlock int64 = 2
) )
//GetLocalDBKeyList 获取本地键值列表 //GetLocalDBKeyList 获取本地键值列表
...@@ -590,7 +588,7 @@ func (bs *BlockStore) SaveBlock(storeBatch dbm.Batch, blockdetail *types.BlockDe ...@@ -590,7 +588,7 @@ func (bs *BlockStore) SaveBlock(storeBatch dbm.Batch, blockdetail *types.BlockDe
if bs.saveSequence || bs.isParaChain { if bs.saveSequence || bs.isParaChain {
//存储记录block序列执行的type add //存储记录block序列执行的type add
lastSequence, err = bs.saveBlockSequence(storeBatch, hash, height, AddBlock, sequence) lastSequence, err = bs.saveBlockSequence(storeBatch, hash, height, types.AddBlock, sequence)
if err != nil { if err != nil {
storeLog.Error("SaveBlock SaveBlockSequence", "height", height, "hash", common.ToHex(hash), "error", err) storeLog.Error("SaveBlock SaveBlockSequence", "height", height, "hash", common.ToHex(hash), "error", err)
return lastSequence, err return lastSequence, err
...@@ -619,7 +617,7 @@ func (bs *BlockStore) DelBlock(storeBatch dbm.Batch, blockdetail *types.BlockDet ...@@ -619,7 +617,7 @@ func (bs *BlockStore) DelBlock(storeBatch dbm.Batch, blockdetail *types.BlockDet
if bs.saveSequence || bs.isParaChain { if bs.saveSequence || bs.isParaChain {
//存储记录block序列执行的type del //存储记录block序列执行的type del
lastSequence, err := bs.saveBlockSequence(storeBatch, hash, height, DelBlock, sequence) lastSequence, err := bs.saveBlockSequence(storeBatch, hash, height, types.DelBlock, sequence)
if err != nil { if err != nil {
storeLog.Error("DelBlock SaveBlockSequence", "height", height, "hash", common.ToHex(hash), "error", err) storeLog.Error("DelBlock SaveBlockSequence", "height", height, "hash", common.ToHex(hash), "error", err)
return lastSequence, err return lastSequence, err
...@@ -1029,7 +1027,7 @@ func (bs *BlockStore) saveBlockSequence(storeBatch dbm.Batch, hash []byte, heigh ...@@ -1029,7 +1027,7 @@ func (bs *BlockStore) saveBlockSequence(storeBatch dbm.Batch, hash []byte, heigh
sequenceBytes := types.Encode(&types.Int64{Data: newSequence}) sequenceBytes := types.Encode(&types.Int64{Data: newSequence})
// hash->seq 只记录add block时的hash和seq对应关系 // hash->seq 只记录add block时的hash和seq对应关系
if Type == AddBlock { if Type == types.AddBlock {
storeBatch.Set(calcHashToSequenceKey(hash, bs.isParaChain), sequenceBytes) storeBatch.Set(calcHashToSequenceKey(hash, bs.isParaChain), sequenceBytes)
} }
...@@ -1054,7 +1052,7 @@ func (bs *BlockStore) saveBlockSequence(storeBatch dbm.Batch, hash []byte, heigh ...@@ -1054,7 +1052,7 @@ func (bs *BlockStore) saveBlockSequence(storeBatch dbm.Batch, hash []byte, heigh
// hash->seq 只记录add block时的hash和seq对应关系 // hash->seq 只记录add block时的hash和seq对应关系
sequenceBytes := types.Encode(&types.Int64{Data: mainSeq}) sequenceBytes := types.Encode(&types.Int64{Data: mainSeq})
if Type == AddBlock { if Type == types.AddBlock {
storeBatch.Set(calcHashToMainSequenceKey(hash), sequenceBytes) storeBatch.Set(calcHashToMainSequenceKey(hash), sequenceBytes)
} }
storeBatch.Set(LastSequence, sequenceBytes) storeBatch.Set(LastSequence, sequenceBytes)
...@@ -1301,7 +1299,7 @@ func (bs *BlockStore) CreateSequences(batchSize int64) { ...@@ -1301,7 +1299,7 @@ func (bs *BlockStore) CreateSequences(batchSize int64) {
// seq->hash // seq->hash
var blockSequence types.BlockSequence var blockSequence types.BlockSequence
blockSequence.Hash = header.Hash blockSequence.Hash = header.Hash
blockSequence.Type = AddBlock blockSequence.Type = types.AddBlock
BlockSequenceByte, err := proto.Marshal(&blockSequence) BlockSequenceByte, err := proto.Marshal(&blockSequence)
if err != nil { if err != nil {
storeLog.Error("CreateSequences Marshal BlockSequence", "height", i, "hash", common.ToHex(header.Hash), "error", err) storeLog.Error("CreateSequences Marshal BlockSequence", "height", i, "hash", common.ToHex(header.Hash), "error", err)
......
...@@ -601,7 +601,7 @@ func testGetBlockSequences(t *testing.T, chain *blockchain.BlockChain) { ...@@ -601,7 +601,7 @@ func testGetBlockSequences(t *testing.T, chain *blockchain.BlockChain) {
Sequences, err := chain.GetBlockSequences(&reqBlock) Sequences, err := chain.GetBlockSequences(&reqBlock)
if err == nil && Sequences != nil { if err == nil && Sequences != nil {
for _, sequence := range Sequences.Items { for _, sequence := range Sequences.Items {
if sequence.Type != blockchain.AddBlock { if sequence.Type != types.AddBlock {
t.Error("testGetBlockSequences sequence type check error") t.Error("testGetBlockSequences sequence type check error")
} }
} }
......
...@@ -23,22 +23,29 @@ func (chain *BlockChain) GetParaTxByTitle(seq *types.ReqParaTxByTitle) (*types.P ...@@ -23,22 +23,29 @@ func (chain *BlockChain) GetParaTxByTitle(seq *types.ReqParaTxByTitle) (*types.P
return nil, err return nil, err
} }
//获取区块的seq信息 //对获取区块的起始和结束值做校验,最多一次取1000个区块,防止取的数据过大导致内存异常
if seq.End > seq.Start && (seq.End-seq.Start > types.MaxBlockCountPerTime) {
seq.End = seq.Start + types.MaxBlockCountPerTime - 1
}
var reqHashes types.ReqHashes
var sequences *types.BlockSequences
if seq.IsSeq {
req := &types.ReqBlocks{Start: seq.Start, End: seq.End, IsDetail: false, Pid: []string{}} req := &types.ReqBlocks{Start: seq.Start, End: seq.End, IsDetail: false, Pid: []string{}}
sequences, err := chain.GetBlockSequences(req) sequences, err = chain.GetBlockSequences(req)
if err != nil { if err != nil {
filterlog.Error("GetParaTxByTitle:GetBlockSequences", "err", err.Error()) filterlog.Error("GetParaTxByTitle:GetBlockSequences", "err", err.Error())
return nil, err return nil, err
} }
//通过区块hash获取区块信息
var reqHashes types.ReqHashes
for _, item := range sequences.Items { for _, item := range sequences.Items {
if item != nil { if item != nil {
reqHashes.Hashes = append(reqHashes.Hashes, item.GetHash()) reqHashes.Hashes = append(reqHashes.Hashes, item.GetHash())
} }
} }
} else {
reqHashes = chain.getBlockHashes(seq.Start, seq.End)
}
//通过区块hash获取区块信息
blocks, err := chain.GetBlockByHashes(reqHashes.Hashes) blocks, err := chain.GetBlockByHashes(reqHashes.Hashes)
if err != nil { if err != nil {
filterlog.Error("GetParaTxByTitle:GetBlockByHashes", "err", err) filterlog.Error("GetParaTxByTitle:GetBlockByHashes", "err", err)
...@@ -51,8 +58,12 @@ func (chain *BlockChain) GetParaTxByTitle(seq *types.ReqParaTxByTitle) (*types.P ...@@ -51,8 +58,12 @@ func (chain *BlockChain) GetParaTxByTitle(seq *types.ReqParaTxByTitle) (*types.P
for i, block := range blocks.Items { for i, block := range blocks.Items {
if block != nil { if block != nil {
paraTx = block.FilterParaTxsByTitle(seq.Title) paraTx = block.FilterParaTxsByTitle(seq.Title)
if seq.IsSeq {
paraTx.Type = sequences.Items[i].GetType() paraTx.Type = sequences.Items[i].GetType()
} else { } else {
paraTx.Type = types.AddBlock
}
} else {
paraTx = nil paraTx = nil
} }
paraTxs.Items = append(paraTxs.Items, paraTx) paraTxs.Items = append(paraTxs.Items, paraTx)
...@@ -60,13 +71,24 @@ func (chain *BlockChain) GetParaTxByTitle(seq *types.ReqParaTxByTitle) (*types.P ...@@ -60,13 +71,24 @@ func (chain *BlockChain) GetParaTxByTitle(seq *types.ReqParaTxByTitle) (*types.P
return &paraTxs, err return &paraTxs, err
} }
//checkInputParam 入参检测,主要检测seq的end的值已经title是否合法 //checkInputParam 入参检测,主要检测req的end的值以及title是否合法
func (chain *BlockChain) checkInputParam(seq *types.ReqParaTxByTitle) error { func (chain *BlockChain) checkInputParam(req *types.ReqParaTxByTitle) error {
var err error
var lastblock int64
//入参数校验 if req.GetStart() > req.GetEnd() {
blockLastSeq, err := chain.blockStore.LoadBlockLastSequence() chainlog.Error("checkInputParam input must Start <= End:", "Start", req.Start, "End", req.End)
if err != nil || seq.End > blockLastSeq || blockLastSeq < 0 || !strings.HasPrefix(seq.Title, types.ParaKeyX) { return types.ErrEndLessThanStartHeight
filterlog.Error("checkInputParam", "blockLastSeq", blockLastSeq, "seq", seq, "err", err) }
//需要区分是通过seq/height来获取平行链交易
if req.IsSeq {
lastblock, err = chain.blockStore.LoadBlockLastSequence()
} else {
lastblock = chain.GetBlockHeight()
}
if err != nil || req.End > lastblock || lastblock < 0 || !strings.HasPrefix(req.Title, types.ParaKeyX) {
filterlog.Error("checkInputParam", "lastblock", lastblock, "req", req, "err", err)
return types.ErrInvalidParam return types.ErrInvalidParam
} }
return nil return nil
......
...@@ -142,17 +142,36 @@ func TestGetParaTxByTitle(t *testing.T) { ...@@ -142,17 +142,36 @@ func TestGetParaTxByTitle(t *testing.T) {
time.Sleep(sendTxWait) time.Sleep(sendTxWait)
} }
var req types.ReqParaTxByTitle var req types.ReqParaTxByTitle
//通过seq获取para交易
req.Start = 0 req.Start = 0
req.End = curheight req.End = curheight
req.Title = "user.p.hyb." req.Title = "user.p.hyb."
testgetParaTxByTitle(t, blockchain, &req, false, false, nil) req.IsSeq = true
testgetParaTxByTitle(t, blockchain, &req, 0)
//通过height获取para交易
req.IsSeq = false
testgetParaTxByTitle(t, blockchain, &req, 0)
//通过height获取para交易
req.IsSeq = false
req.End = curheight + 1
testgetParaTxByTitle(t, blockchain, &req, 1)
chainlog.Info("TestGetParaTxByTitle end --------------------") chainlog.Info("TestGetParaTxByTitle end --------------------")
} }
func testgetParaTxByTitle(t *testing.T, blockchain *blockchain.BlockChain, req *types.ReqParaTxByTitle, isGroup bool, haveMainTx bool, hashs []string) { func testgetParaTxByTitle(t *testing.T, blockchain *blockchain.BlockChain, req *types.ReqParaTxByTitle, flag int) {
count := req.End - req.Start + 1
ParaTxDetails, err := blockchain.GetParaTxByTitle(req) ParaTxDetails, err := blockchain.GetParaTxByTitle(req)
if flag == 0 {
require.NoError(t, err) require.NoError(t, err)
}
if flag == 1 {
assert.Equal(t, err, types.ErrInvalidParam)
return
}
itemsLen := len(ParaTxDetails.Items)
assert.Equal(t, count, int64(itemsLen))
for i, txDetail := range ParaTxDetails.Items { for i, txDetail := range ParaTxDetails.Items {
if txDetail != nil { if txDetail != nil {
......
...@@ -481,6 +481,14 @@ func (chain *BlockChain) delParaChainBlockDetail(msg *queue.Message) { ...@@ -481,6 +481,14 @@ func (chain *BlockChain) delParaChainBlockDetail(msg *queue.Message) {
func (chain *BlockChain) addParaChainBlockDetail(msg *queue.Message) { func (chain *BlockChain) addParaChainBlockDetail(msg *queue.Message) {
parablockDetail := msg.Data.(*types.ParaChainBlockDetail) parablockDetail := msg.Data.(*types.ParaChainBlockDetail)
//根据配置chain.cfgBatchSync和parablockDetail.IsSync
//来决定写数据库时是否需要刷盘,主要是为了同步阶段提高执行区块的效率
if !parablockDetail.IsSync && !chain.cfgBatchSync {
atomic.CompareAndSwapInt32(&chain.isbatchsync, 1, 0)
} else {
atomic.CompareAndSwapInt32(&chain.isbatchsync, 0, 1)
}
chainlog.Debug("EventAddParaChainBlockDetail", "height", parablockDetail.Blockdetail.Block.Height, "hash", common.HashHex(parablockDetail.Blockdetail.Block.Hash())) chainlog.Debug("EventAddParaChainBlockDetail", "height", parablockDetail.Blockdetail.Block.Height, "hash", common.HashHex(parablockDetail.Blockdetail.Block.Hash()))
// 平行链上P2P模块关闭,不用广播区块 // 平行链上P2P模块关闭,不用广播区块
blockDetail, err := chain.ProcAddParaChainBlockMsg(false, parablockDetail, "self") blockDetail, err := chain.ProcAddParaChainBlockMsg(false, parablockDetail, "self")
......
...@@ -10,11 +10,18 @@ import ( ...@@ -10,11 +10,18 @@ import (
) )
//GetBlockByHashes 通过blockhash 获取对应的block信息 //GetBlockByHashes 通过blockhash 获取对应的block信息
//从数据库获取区块不能太多,防止内存异常。一次最多获取100M区块数据从数据库
func (chain *BlockChain) GetBlockByHashes(hashes [][]byte) (respblocks *types.BlockDetails, err error) { func (chain *BlockChain) GetBlockByHashes(hashes [][]byte) (respblocks *types.BlockDetails, err error) {
var blocks types.BlockDetails var blocks types.BlockDetails
size := 0
for _, hash := range hashes { for _, hash := range hashes {
block, err := chain.LoadBlockByHash(hash) block, err := chain.LoadBlockByHash(hash)
if err == nil && block != nil { if err == nil && block != nil {
size += block.Size()
if size > types.MaxBlockSizePerTime {
chainlog.Error("GetBlockByHashes:overflow", "MaxBlockSizePerTime", types.MaxBlockSizePerTime)
return &blocks, nil
}
blocks.Items = append(blocks.Items, block) blocks.Items = append(blocks.Items, block)
} else { } else {
blocks.Items = append(blocks.Items, nil) blocks.Items = append(blocks.Items, nil)
...@@ -264,3 +271,18 @@ func (chain *BlockChain) ProcAddBlockMsg(broadcast bool, blockdetail *types.Bloc ...@@ -264,3 +271,18 @@ func (chain *BlockChain) ProcAddBlockMsg(broadcast bool, blockdetail *types.Bloc
chainlog.Debug("ProcAddBlockMsg result:", "height", blockdetail.Block.Height, "ismain", ismain, "isorphan", isorphan, "hash", common.ToHex(blockdetail.Block.Hash()), "err", err) chainlog.Debug("ProcAddBlockMsg result:", "height", blockdetail.Block.Height, "ismain", ismain, "isorphan", isorphan, "hash", common.ToHex(blockdetail.Block.Hash()), "err", err)
return blockdetail, err return blockdetail, err
} }
//getBlockHashes 获取指定height区间对应的blockhashes
func (chain *BlockChain) getBlockHashes(startheight, endheight int64) types.ReqHashes {
var reqHashes types.ReqHashes
for i := startheight; i <= endheight; i++ {
hash, err := chain.blockStore.GetBlockHashByHeight(i)
if hash == nil || err != nil {
storeLog.Error("getBlockHashesByHeight", "height", i, "error", err)
reqHashes.Hashes = append(reqHashes.Hashes, nil)
} else {
reqHashes.Hashes = append(reqHashes.Hashes, hash)
}
}
return reqHashes
}
...@@ -50,6 +50,16 @@ func (chain *BlockChain) Rollback() { ...@@ -50,6 +50,16 @@ func (chain *BlockChain) Rollback() {
if err != nil { if err != nil {
panic(fmt.Sprintln("rollback LoadBlockByHeight err :", err)) panic(fmt.Sprintln("rollback LoadBlockByHeight err :", err))
} }
if chain.cfg.RollbackSave { //本地保存临时区块
lastHeightSave := false
if i == startHeight {
lastHeightSave = true
}
err = chain.WriteBlockToDbTemp(blockdetail.Block, lastHeightSave)
if err != nil {
panic(fmt.Sprintln("rollback WriteBlockToDbTemp fail", "height", blockdetail.Block.Height, "error ", err))
}
}
sequence := int64(-1) sequence := int64(-1)
if chain.isParaChain { if chain.isParaChain {
// 获取平行链的seq // 获取平行链的seq
...@@ -64,17 +74,6 @@ func (chain *BlockChain) Rollback() { ...@@ -64,17 +74,6 @@ func (chain *BlockChain) Rollback() {
} }
// 删除storedb中的状态高度 // 删除storedb中的状态高度
chain.sendDelStore(blockdetail.Block.StateHash, blockdetail.Block.Height) chain.sendDelStore(blockdetail.Block.StateHash, blockdetail.Block.Height)
// 删除之后,本地保存临时区块
if chain.cfg.RollbackSave {
lastHeightSave := false
if i == startHeight {
lastHeightSave = true
}
err = chain.WriteBlockToDbTemp(blockdetail.Block, lastHeightSave)
if err != nil {
panic(fmt.Sprintln("rollback WriteBlockToDbTemp fail", "height", blockdetail.Block.Height, "error ", err))
}
}
chainlog.Info("chain rollback ", "height: ", i, "blockheight", blockdetail.Block.Height, "hash", common.ToHex(blockdetail.Block.Hash()), "state hash", common.ToHex(blockdetail.Block.StateHash)) chainlog.Info("chain rollback ", "height: ", i, "blockheight", blockdetail.Block.Height, "hash", common.ToHex(blockdetail.Block.Hash()), "state hash", common.ToHex(blockdetail.Block.StateHash))
} }
} }
......
...@@ -5,9 +5,12 @@ ...@@ -5,9 +5,12 @@
package blockchain package blockchain
import ( import (
"fmt"
"math/rand" "math/rand"
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/assert"
) )
func TestTask(t *testing.T) { func TestTask(t *testing.T) {
...@@ -69,27 +72,18 @@ func TestTaskTimeOut(t *testing.T) { ...@@ -69,27 +72,18 @@ func TestTaskTimeOut(t *testing.T) {
t.Log("task not start") t.Log("task not start")
return return
} }
defer task.Cancel()
timeoutHeight := make(chan int64, 1)
timeoutcb := func(height int64) { timeoutcb := func(height int64) {
timeOutProc(height) fmt.Println("timeout:height", height)
timeoutHeight <- height
} }
task.Start(1, 10, nil, timeoutcb) task.Start(1, 11, nil, timeoutcb)
perm := rand.Perm(10) perm := rand.Perm(10)
for i := 0; i < len(perm); i++ { for i := 0; i < len(perm); i++ {
time.Sleep(time.Millisecond * 10)
task.Done(int64(perm[i]) + 1) task.Done(int64(perm[i]) + 1)
if i < len(perm)-1 && !task.InProgress() {
task.Cancel()
t.Log("task not done, but InProgress is false")
return
}
if i == len(perm)-1 && task.InProgress() {
task.Cancel()
t.Log("task is done, but InProgress is true")
return
} }
} h := <-timeoutHeight
} assert.Equal(t, h, int64(11))
func timeOutProc(height int64) {
chainlog.Info("timeOutProc", "height", height)
} }
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package commands package commands
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"os" "os"
...@@ -27,15 +28,16 @@ func StatCmd() *cobra.Command { ...@@ -27,15 +28,16 @@ func StatCmd() *cobra.Command {
} }
cmd.AddCommand( cmd.AddCommand(
GetTotalCoinsCmd(), getTotalCoinsCmd(),
GetExecBalanceCmd(), getExecBalanceCmd(),
totalFeeCmd(),
) )
return cmd return cmd
} }
// GetTotalCoinsCmd get total coins // getTotalCoinsCmd get total coins
func GetTotalCoinsCmd() *cobra.Command { func getTotalCoinsCmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "total_coins", Use: "total_coins",
Short: "Get total amount of a token (default: bty of current height)", Short: "Get total amount of a token (default: bty of current height)",
...@@ -57,43 +59,32 @@ func totalCoins(cmd *cobra.Command, args []string) { ...@@ -57,43 +59,32 @@ func totalCoins(cmd *cobra.Command, args []string) {
height, _ := cmd.Flags().GetInt64("height") height, _ := cmd.Flags().GetInt64("height")
actual, _ := cmd.Flags().GetString("actual") actual, _ := cmd.Flags().GetString("actual")
if height == -1 {
rpc, err := jsonclient.NewJSONClient(rpcAddr) rpc, err := jsonclient.NewJSONClient(rpcAddr)
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
return return
} }
var res rpctypes.Header
err = rpc.Call("Chain33.GetLastHeader", nil, &res)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
height = res.Height
}
// 获取高度statehash var stateHashHex string
params := rpctypes.BlockParam{ if height < 0 {
Start: height,
End: height,
//Isdetail: false,
Isdetail: true,
}
rpc, err := jsonclient.NewJSONClient(rpcAddr) header, err := getLastBlock(rpc)
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
return return
} }
height = header.Height
var res rpctypes.BlockDetails stateHashHex = header.StateHash
err = rpc.Call("Chain33.GetBlocks", params, &res) } else {
blocks, err := getBlocks(height, height, rpc)
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintf(os.Stderr, "GetBlocksErr:%s", err.Error())
return return
} }
stateHashHex = blocks.Items[0].Block.StateHash
}
stateHash, err := common.FromHex(res.Items[0].Block.StateHash) stateHash, err := common.FromHex(stateHashHex)
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
return return
...@@ -104,32 +95,14 @@ func totalCoins(cmd *cobra.Command, args []string) { ...@@ -104,32 +95,14 @@ func totalCoins(cmd *cobra.Command, args []string) {
resp := commandtypes.GetTotalCoinsResult{} resp := commandtypes.GetTotalCoinsResult{}
if symbol == "bty" { if symbol == "bty" {
//查询高度blockhash //查询历史总手续费
params := types.ReqInt{Height: height} fee, err := queryTotalFeeWithHeight(height, rpc)
var res1 rpctypes.ReplyHash
err = rpc.Call("Chain33.GetBlockHash", params, &res1)
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
return return
} }
blockHash, err := common.FromHex(res1.Hash) resp.TxCount = fee.TxCount
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
//查询手续费
key := append([]byte("TotalFeeKey:"), blockHash...)
params2 := types.LocalDBGet{Keys: [][]byte{key}}
var res2 types.TotalFee
err = rpc.Call("Chain33.QueryTotalFee", params2, &res2)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
resp.TxCount = res2.TxCount
var issueCoins int64 var issueCoins int64
//只适用bty主网计算 //只适用bty主网计算
if height < 2270000 { if height < 2270000 {
...@@ -137,7 +110,7 @@ func totalCoins(cmd *cobra.Command, args []string) { ...@@ -137,7 +110,7 @@ func totalCoins(cmd *cobra.Command, args []string) {
} else { //挖矿产量降低30->8 } else { //挖矿产量降低30->8
issueCoins = 22*2269999 + height*8 issueCoins = 22*2269999 + height*8
} }
totalAmount = (317430000+issueCoins)*types.Coin - res2.Fee totalAmount = (317430000+issueCoins)*types.Coin - fee.Fee
resp.TotalAmount = strconv.FormatFloat(float64(totalAmount)/float64(types.Coin), 'f', 4, 64) resp.TotalAmount = strconv.FormatFloat(float64(totalAmount)/float64(types.Coin), 'f', 4, 64)
} else { } else {
var req types.ReqString var req types.ReqString
...@@ -210,8 +183,8 @@ func totalCoins(cmd *cobra.Command, args []string) { ...@@ -210,8 +183,8 @@ func totalCoins(cmd *cobra.Command, args []string) {
fmt.Println(string(data)) fmt.Println(string(data))
} }
// GetExecBalanceCmd get exec-addr balance of specific addr // getExecBalanceCmd get exec-addr balance of specific addr
func GetExecBalanceCmd() *cobra.Command { func getExecBalanceCmd() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "exec_balance", Use: "exec_balance",
Short: "Get the exec amount of a token of one address (default: all exec-addr bty of current height of one addr)", Short: "Get the exec amount of a token of one address (default: all exec-addr bty of current height of one addr)",
...@@ -238,43 +211,30 @@ func execBalance(cmd *cobra.Command, args []string) { ...@@ -238,43 +211,30 @@ func execBalance(cmd *cobra.Command, args []string) {
execAddr, _ := cmd.Flags().GetString("exec_addr") execAddr, _ := cmd.Flags().GetString("exec_addr")
height, _ := cmd.Flags().GetInt64("height") height, _ := cmd.Flags().GetInt64("height")
if height == -1 {
rpc, err := jsonclient.NewJSONClient(rpcAddr) rpc, err := jsonclient.NewJSONClient(rpcAddr)
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
return return
} }
var res rpctypes.Header var stateHashHex string
err = rpc.Call("Chain33.GetLastHeader", nil, &res) if height < 0 {
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
height = res.Height
}
// 获取高度statehash
params := rpctypes.BlockParam{
Start: height,
End: height,
//Isdetail: false,
Isdetail: true,
}
rpc, err := jsonclient.NewJSONClient(rpcAddr) header, err := getLastBlock(rpc)
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
return return
} }
stateHashHex = header.StateHash
var res rpctypes.BlockDetails } else {
err = rpc.Call("Chain33.GetBlocks", params, &res) blocks, err := getBlocks(height, height, rpc)
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintf(os.Stderr, "GetBlocksErr:%s", err.Error())
return return
} }
stateHashHex = blocks.Items[0].Block.StateHash
}
stateHash, err := common.FromHex(res.Items[0].Block.StateHash) stateHash, err := common.FromHex(stateHashHex)
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
return return
...@@ -293,7 +253,6 @@ func execBalance(cmd *cobra.Command, args []string) { ...@@ -293,7 +253,6 @@ func execBalance(cmd *cobra.Command, args []string) {
ExecAddr: []byte(execAddr), ExecAddr: []byte(execAddr),
Execer: exec, Execer: exec,
} }
reqParam.StateHash = stateHash
if len(execAddr) > 0 { if len(execAddr) > 0 {
reqParam.Count = 1 //由于精确匹配一条记录,所以这里设定为1 reqParam.Count = 1 //由于精确匹配一条记录,所以这里设定为1
...@@ -366,3 +325,152 @@ func convertReplyToResult(reply *types.ReplyGetExecBalance, result *commandtypes ...@@ -366,3 +325,152 @@ func convertReplyToResult(reply *types.ReplyGetExecBalance, result *commandtypes
result.ExecBalances = append(result.ExecBalances, item) result.ExecBalances = append(result.ExecBalances, item)
} }
} }
// totalFeeCmd query total fee command
func totalFeeCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "total_fee",
Short: "query total transaction fee, support specific block height interval [start, end]",
Run: totalFee,
}
cmd.Flags().Int64P("start_height", "s", 0, "start block height, default 0")
cmd.Flags().Int64P("end_height", "e", -1, "end block height, default current block height")
return cmd
}
func totalFee(cmd *cobra.Command, args []string) {
rpcAddr, _ := cmd.Flags().GetString("rpc_laddr")
start, _ := cmd.Flags().GetInt64("start_height")
end, _ := cmd.Flags().GetInt64("end_height")
var startFeeAmount, endFeeAmount int64
rpc, err := jsonclient.NewJSONClient(rpcAddr)
if err != nil {
fmt.Fprintf(os.Stderr, "NewJsonClientErr:%s\n", err.Error())
return
}
if start < 0 {
start = 0
}
if start > 0 {
totalFee, err := queryTotalFeeWithHeight(start-1, rpc)
if err != nil {
fmt.Fprintf(os.Stderr, "QueryStartFeeErr:%s\n", err.Error())
return
}
startFeeAmount = totalFee.Fee
}
if end < 0 {
//last block fee
currentHeight, totalFee, err := queryCurrentTotalFee(rpc)
if err != nil {
fmt.Fprintf(os.Stderr, "QueryCurrentTotalFeeErr:%s\n", err.Error())
return
}
endFeeAmount = totalFee.Fee
end = currentHeight
} else {
totalFee, err := queryTotalFeeWithHeight(end, rpc)
if err != nil {
fmt.Fprintf(os.Stderr, "QueryEndFeeErr:%s\n", err.Error())
return
}
endFeeAmount = totalFee.Fee
}
fee := endFeeAmount - startFeeAmount
if fee < 0 {
fee = 0
}
resp := fmt.Sprintf(`{"startHeight":%d,"endHeight":%d, "totalFee":%s}`, start, end, commandtypes.FormatAmountValue2Display(fee))
buf := &bytes.Buffer{}
err = json.Indent(buf, []byte(resp), "", " ")
if err != nil {
fmt.Fprintf(os.Stderr, "JsonIndentResultErr:%s\n", err.Error())
return
}
fmt.Println(buf.String())
}
//get last block header
func getLastBlock(rpc *jsonclient.JSONClient) (*rpctypes.Header, error) {
res := &rpctypes.Header{}
err := rpc.Call("Chain33.GetLastHeader", nil, &res)
if err != nil {
return nil, err
}
return res, nil
}
//get block hash with height
func getBlockHash(height int64, rpc *jsonclient.JSONClient) (string, error) {
params := types.ReqInt{Height: height}
var res rpctypes.ReplyHash
err := rpc.Call("Chain33.GetBlockHash", params, &res)
if err != nil {
return "", err
}
return res.Hash, nil
}
func queryCurrentTotalFee(rpc *jsonclient.JSONClient) (int64, *types.TotalFee, error) {
header, err := getLastBlock(rpc)
if err != nil {
return 0, nil, err
}
fee, err := queryTotalFeeWithHash(header.Hash, rpc)
if err != nil {
return 0, nil, err
}
return header.Height, fee, nil
}
func queryTotalFeeWithHeight(height int64, rpc *jsonclient.JSONClient) (*types.TotalFee, error) {
hash, err := getBlockHash(height, rpc)
if err != nil {
return nil, err
}
fee, err := queryTotalFeeWithHash(hash, rpc)
if err != nil {
return nil, err
}
return fee, nil
}
func queryTotalFeeWithHash(blockHash string, rpc *jsonclient.JSONClient) (*types.TotalFee, error) {
hash, err := common.FromHex(blockHash)
if err != nil {
return nil, err
}
//查询手续费
params := types.LocalDBGet{Keys: [][]byte{hash[:]}}
res := &types.TotalFee{}
err = rpc.Call("Chain33.QueryTotalFee", params, &res)
if err != nil {
return nil, err
}
return res, nil
}
func getBlocks(start, end int64, rpc *jsonclient.JSONClient) (*rpctypes.BlockDetails, error) {
// 获取blocks
params := rpctypes.BlockParam{
Start: start,
End: end,
//Isdetail: false,
Isdetail: true,
}
res := &rpctypes.BlockDetails{}
err := rpc.Call("Chain33.GetBlocks", params, &res)
if err != nil {
return nil, err
}
return res, nil
}
...@@ -171,14 +171,14 @@ func addWalletListTxsFlags(cmd *cobra.Command) { ...@@ -171,14 +171,14 @@ func addWalletListTxsFlags(cmd *cobra.Command) {
cmd.Flags().Int32P("count", "c", 0, "number of transactions") cmd.Flags().Int32P("count", "c", 0, "number of transactions")
cmd.MarkFlagRequired("count") cmd.MarkFlagRequired("count")
cmd.Flags().Int32P("direction", "d", 1, "query direction (0: pre page, 1: next page)") cmd.Flags().Int32P("direction", "d", 0, "query direction (0: pre page, 1: next page)")
} }
func walletListTxs(cmd *cobra.Command, args []string) { func walletListTxs(cmd *cobra.Command, args []string) {
rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr") rpcLaddr, _ := cmd.Flags().GetString("rpc_laddr")
txHash, _ := cmd.Flags().GetString("from") txHash, _ := cmd.Flags().GetString("from")
count, _ := cmd.Flags().GetInt32("count") count, _ := cmd.Flags().GetInt32("count")
direction, _ := cmd.Flags().GetInt32("dir") direction, _ := cmd.Flags().GetInt32("direction")
params := rpctypes.ReqWalletTransactionList{ params := rpctypes.ReqWalletTransactionList{
FromTx: txHash, FromTx: txHash,
Count: count, Count: count,
......
...@@ -116,6 +116,9 @@ func NewTree(db dbm.DB, sync bool) *Tree { ...@@ -116,6 +116,9 @@ func NewTree(db dbm.DB, sync bool) *Tree {
// 使能情况下非空创建当前整tree的缓存 // 使能情况下非空创建当前整tree的缓存
if enableMemTree && memTree == nil { if enableMemTree && memTree == nil {
memTree = NewTreeMap(50 * 10000) memTree = NewTreeMap(50 * 10000)
if tkCloseCacheLen == 0 {
tkCloseCacheLen = 10 * 10000
}
tkCloseCache = NewTreeARC(int(tkCloseCacheLen)) tkCloseCache = NewTreeARC(int(tkCloseCacheLen))
} }
return &Tree{ return &Tree{
......
...@@ -244,3 +244,8 @@ func (blockDetail *BlockDetail) filterParaTxGroup(tx *Transaction, index int) ([ ...@@ -244,3 +244,8 @@ func (blockDetail *BlockDetail) filterParaTxGroup(tx *Transaction, index int) ([
} }
return txDetails, endIdx return txDetails, endIdx
} }
// Size 获取blockDetail的Size
func (blockDetail *BlockDetail) Size() int {
return Size(blockDetail)
}
...@@ -1531,9 +1531,11 @@ func (m *BlockSequences) GetItems() []*BlockSequence { ...@@ -1531,9 +1531,11 @@ func (m *BlockSequences) GetItems() []*BlockSequence {
//平行链区块详细信息 //平行链区块详细信息
// blockdetail : 区块详细信息 // blockdetail : 区块详细信息
// sequence :区块序列号 // sequence :区块序列号
// isSync:写数据库时是否需要刷盘
type ParaChainBlockDetail struct { type ParaChainBlockDetail struct {
Blockdetail *BlockDetail `protobuf:"bytes,1,opt,name=blockdetail,proto3" json:"blockdetail,omitempty"` Blockdetail *BlockDetail `protobuf:"bytes,1,opt,name=blockdetail,proto3" json:"blockdetail,omitempty"`
Sequence int64 `protobuf:"varint,2,opt,name=sequence,proto3" json:"sequence,omitempty"` Sequence int64 `protobuf:"varint,2,opt,name=sequence,proto3" json:"sequence,omitempty"`
IsSync bool `protobuf:"varint,3,opt,name=isSync,proto3" json:"isSync,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:"-"`
...@@ -1578,6 +1580,13 @@ func (m *ParaChainBlockDetail) GetSequence() int64 { ...@@ -1578,6 +1580,13 @@ func (m *ParaChainBlockDetail) GetSequence() int64 {
return 0 return 0
} }
func (m *ParaChainBlockDetail) GetIsSync() bool {
if m != nil {
return m.IsSync
}
return false
}
// 定义para交易结构 // 定义para交易结构
type ParaTxDetails struct { type ParaTxDetails struct {
Items []*ParaTxDetail `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"` Items []*ParaTxDetail `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"`
...@@ -1749,6 +1758,7 @@ type ReqParaTxByTitle struct { ...@@ -1749,6 +1758,7 @@ type ReqParaTxByTitle struct {
Start int64 `protobuf:"varint,1,opt,name=start,proto3" json:"start,omitempty"` Start int64 `protobuf:"varint,1,opt,name=start,proto3" json:"start,omitempty"`
End int64 `protobuf:"varint,2,opt,name=end,proto3" json:"end,omitempty"` End int64 `protobuf:"varint,2,opt,name=end,proto3" json:"end,omitempty"`
Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"` Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"`
IsSeq bool `protobuf:"varint,4,opt,name=isSeq,proto3" json:"isSeq,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:"-"`
...@@ -1800,6 +1810,13 @@ func (m *ReqParaTxByTitle) GetTitle() string { ...@@ -1800,6 +1810,13 @@ func (m *ReqParaTxByTitle) GetTitle() string {
return "" return ""
} }
func (m *ReqParaTxByTitle) GetIsSeq() bool {
if m != nil {
return m.IsSeq
}
return false
}
func init() { func init() {
proto.RegisterType((*Header)(nil), "types.Header") proto.RegisterType((*Header)(nil), "types.Header")
proto.RegisterType((*Block)(nil), "types.Block") proto.RegisterType((*Block)(nil), "types.Block")
...@@ -1839,86 +1856,87 @@ func init() { ...@@ -1839,86 +1856,87 @@ func init() {
func init() { proto.RegisterFile("blockchain.proto", fileDescriptor_e9ac6287ce250c9a) } func init() { proto.RegisterFile("blockchain.proto", fileDescriptor_e9ac6287ce250c9a) }
var fileDescriptor_e9ac6287ce250c9a = []byte{ var fileDescriptor_e9ac6287ce250c9a = []byte{
// 1287 bytes of a gzipped FileDescriptorProto // 1307 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x57, 0xdd, 0x6e, 0x1b, 0xc5, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x57, 0x5f, 0x6f, 0x1b, 0x45,
0x17, 0xd7, 0xfa, 0x23, 0xb1, 0x8f, 0xed, 0xfc, 0xd3, 0xf9, 0x1b, 0x64, 0x45, 0x40, 0xdd, 0xa1, 0x10, 0xd7, 0xf9, 0x4f, 0x62, 0x8f, 0x9d, 0x90, 0x2e, 0x06, 0x59, 0x11, 0x50, 0x77, 0x29, 0xc5,
0x14, 0x53, 0x8a, 0x8b, 0x1a, 0x54, 0x2a, 0x04, 0x12, 0x24, 0x45, 0x6a, 0x9a, 0x52, 0xc2, 0xc4, 0x94, 0xe2, 0xa2, 0x06, 0x95, 0x0a, 0x81, 0x04, 0x49, 0x91, 0x9a, 0xa6, 0x94, 0xb0, 0x71, 0xf3,
0xcd, 0x05, 0x77, 0xdb, 0xf5, 0x24, 0x5e, 0xd5, 0xde, 0xdd, 0xec, 0xcc, 0x1a, 0x9b, 0x47, 0x40, 0xc0, 0xdb, 0xf5, 0xbc, 0x89, 0x57, 0xb5, 0xef, 0x2e, 0xb7, 0x7b, 0xc1, 0xe6, 0x89, 0x67, 0x24,
0xe2, 0x11, 0x78, 0x01, 0xc4, 0x3b, 0xf1, 0x2a, 0xe8, 0x9c, 0x99, 0xf1, 0x7e, 0x90, 0x14, 0x7a, 0x3e, 0x02, 0x5f, 0x00, 0xf1, 0x9d, 0xf8, 0x2a, 0x68, 0x66, 0x77, 0x7d, 0x77, 0x26, 0x2d, 0xf4,
0xc9, 0xdd, 0x9c, 0xaf, 0x39, 0xbf, 0xf9, 0xcd, 0x9c, 0x73, 0x76, 0x61, 0xf7, 0xe5, 0x3c, 0x0e, 0x91, 0xb7, 0x9d, 0x3f, 0xbb, 0xf3, 0x9b, 0xdf, 0xee, 0xcc, 0xdc, 0xc1, 0xce, 0xf3, 0x59, 0x12,
0x5e, 0x05, 0x33, 0x3f, 0x8c, 0xc6, 0x49, 0x1a, 0xeb, 0x98, 0x35, 0xf5, 0x3a, 0x91, 0x6a, 0xef, 0xbd, 0x88, 0xa6, 0xa1, 0x8a, 0x47, 0x69, 0x96, 0x98, 0x84, 0x35, 0xcd, 0x32, 0x95, 0x7a, 0xf7,
0x86, 0x4e, 0xfd, 0x48, 0xf9, 0x81, 0x0e, 0x63, 0x6b, 0xd9, 0xeb, 0x06, 0xf1, 0x62, 0xe1, 0x24, 0x9a, 0xc9, 0xc2, 0x58, 0x87, 0x91, 0x51, 0x89, 0xb3, 0xec, 0x76, 0xa3, 0x64, 0x3e, 0xf7, 0x12,
0xfe, 0x47, 0x0d, 0xb6, 0x9e, 0x48, 0x7f, 0x2a, 0x53, 0x36, 0x80, 0xed, 0xa5, 0x4c, 0x55, 0x18, 0xff, 0xb3, 0x06, 0x1b, 0x8f, 0x64, 0x38, 0x91, 0x19, 0xeb, 0xc3, 0xe6, 0xa5, 0xcc, 0xb4, 0x4a,
0x47, 0x03, 0x6f, 0xe8, 0x8d, 0xea, 0xc2, 0x89, 0xec, 0x3d, 0x80, 0xc4, 0x4f, 0x65, 0xa4, 0x9f, 0xe2, 0x7e, 0x30, 0x08, 0x86, 0x75, 0xe1, 0x45, 0xf6, 0x1e, 0x40, 0x1a, 0x66, 0x32, 0x36, 0x8f,
0xf8, 0x6a, 0x36, 0xa8, 0x0d, 0xbd, 0x51, 0x57, 0x14, 0x34, 0xec, 0x6d, 0xd8, 0xd2, 0x2b, 0xb2, 0x42, 0x3d, 0xed, 0xd7, 0x06, 0xc1, 0xb0, 0x2b, 0x4a, 0x1a, 0xf6, 0x36, 0x6c, 0x98, 0x05, 0xd9,
0xd5, 0xc9, 0x66, 0x25, 0xf6, 0x0e, 0xb4, 0x95, 0xf6, 0xb5, 0x24, 0x53, 0x83, 0x4c, 0xb9, 0x02, 0xea, 0x64, 0x73, 0x12, 0x7b, 0x07, 0xda, 0xda, 0x84, 0x46, 0x92, 0xa9, 0x41, 0xa6, 0x42, 0x81,
0xa3, 0x66, 0x32, 0xbc, 0x98, 0xe9, 0x41, 0x93, 0xd2, 0x59, 0x09, 0xa3, 0xe8, 0x38, 0x93, 0x70, 0xbb, 0xa6, 0x52, 0x9d, 0x4f, 0x4d, 0xbf, 0x49, 0xe1, 0x9c, 0x84, 0xbb, 0x28, 0x9d, 0xb1, 0x9a,
0x21, 0x07, 0x5b, 0x64, 0xca, 0x15, 0x88, 0x52, 0xaf, 0x0e, 0xe3, 0x2c, 0xd2, 0x83, 0xb6, 0x41, 0xcb, 0xfe, 0x06, 0x99, 0x0a, 0x05, 0xa2, 0x34, 0x8b, 0x83, 0x24, 0x8f, 0x4d, 0xbf, 0x6d, 0x51,
0x69, 0x45, 0xc6, 0xa0, 0x31, 0xc3, 0x44, 0x40, 0x89, 0x68, 0x8d, 0xc8, 0xa7, 0xe1, 0xf9, 0x79, 0x3a, 0x91, 0x31, 0x68, 0x4c, 0x31, 0x10, 0x50, 0x20, 0x5a, 0x23, 0xf2, 0x89, 0x3a, 0x3b, 0x53,
0x18, 0x64, 0x73, 0xbd, 0x1e, 0x74, 0x86, 0xde, 0xa8, 0x27, 0x0a, 0x1a, 0x36, 0x86, 0xb6, 0x0a, 0x51, 0x3e, 0x33, 0xcb, 0x7e, 0x67, 0x10, 0x0c, 0xb7, 0x44, 0x49, 0xc3, 0x46, 0xd0, 0xd6, 0xea,
0x2f, 0x22, 0x5f, 0x67, 0xa9, 0x1c, 0xb4, 0x86, 0xde, 0xa8, 0xf3, 0x60, 0x77, 0x4c, 0xd4, 0x8d, 0x3c, 0x0e, 0x4d, 0x9e, 0xc9, 0x7e, 0x6b, 0x10, 0x0c, 0x3b, 0xf7, 0x76, 0x46, 0x44, 0xdd, 0xe8,
0x4f, 0x9d, 0x5e, 0xe4, 0x2e, 0xfc, 0xcf, 0x1a, 0x34, 0x0f, 0x10, 0xcb, 0x7f, 0x84, 0xad, 0x7f, 0xc4, 0xeb, 0x45, 0xe1, 0xc2, 0xff, 0xaa, 0x41, 0x73, 0x1f, 0xb1, 0xfc, 0x4f, 0xd8, 0xfa, 0xb7,
0x3a, 0xff, 0x1e, 0xb4, 0x16, 0x7e, 0x18, 0x51, 0xca, 0x2e, 0xa5, 0xdc, 0xc8, 0x18, 0x4b, 0x6b, 0xfc, 0x77, 0xa1, 0x35, 0x0f, 0x55, 0x4c, 0x21, 0xbb, 0x14, 0x72, 0x25, 0xe3, 0x5e, 0x5a, 0xdb,
0x93, 0xb5, 0x47, 0x5b, 0x17, 0x34, 0x6f, 0xca, 0x1d, 0xbb, 0x0d, 0x75, 0xbd, 0x52, 0x83, 0xed, 0xa8, 0x5b, 0x74, 0x74, 0x49, 0xf3, 0xba, 0xdc, 0xb1, 0x9b, 0x50, 0x37, 0x0b, 0xdd, 0xdf, 0x1c,
0x61, 0x7d, 0xd4, 0x79, 0xc0, 0xac, 0xe7, 0x24, 0x7f, 0x9f, 0x02, 0xcd, 0xfc, 0x1e, 0x6c, 0x11, 0xd4, 0x87, 0x9d, 0x7b, 0xcc, 0x79, 0x8e, 0x8b, 0xf7, 0x29, 0xd0, 0xcc, 0xef, 0xc0, 0x06, 0x11,
0xc1, 0x8a, 0x71, 0x68, 0x86, 0x5a, 0x2e, 0xd4, 0xc0, 0xa3, 0x88, 0xae, 0x8d, 0x20, 0xab, 0x30, 0xac, 0x19, 0x87, 0xa6, 0x32, 0x72, 0xae, 0xfb, 0x01, 0xed, 0xe8, 0xba, 0x1d, 0x64, 0x15, 0xd6,
0x26, 0xfe, 0x14, 0x80, 0xe4, 0x53, 0x79, 0x79, 0x78, 0x80, 0x2f, 0x20, 0xf2, 0x17, 0x92, 0x2e, 0xc4, 0x1f, 0x03, 0x90, 0x7c, 0x22, 0x2f, 0x0e, 0xf6, 0xf1, 0x05, 0xc4, 0xe1, 0x5c, 0xd2, 0x85,
0xa4, 0x2d, 0x68, 0xcd, 0x76, 0xa1, 0xfe, 0x42, 0x3c, 0xa3, 0x6b, 0x68, 0x0b, 0x5c, 0x22, 0x93, 0xb4, 0x05, 0xad, 0xd9, 0x0e, 0xd4, 0x9f, 0x89, 0x27, 0x74, 0x0d, 0x6d, 0x81, 0x4b, 0x64, 0x52,
0x32, 0x0a, 0xe2, 0xa9, 0x24, 0xfe, 0xdb, 0xc2, 0x4a, 0xfc, 0x21, 0x74, 0xf2, 0xbd, 0x14, 0xfb, 0xc6, 0x51, 0x32, 0x91, 0xc4, 0x7f, 0x5b, 0x38, 0x89, 0xdf, 0x87, 0x4e, 0x71, 0x96, 0x66, 0x1f,
0xb0, 0x9c, 0xfe, 0x46, 0x31, 0x3d, 0xb9, 0x38, 0x0c, 0x09, 0xb4, 0x9c, 0x12, 0xb3, 0x45, 0xd9, 0x56, 0xc3, 0x5f, 0x2b, 0x87, 0x27, 0x17, 0x8f, 0x21, 0x85, 0x96, 0x57, 0x62, 0xb4, 0x38, 0x9f,
0xc2, 0xbe, 0x08, 0x5c, 0xb2, 0x3b, 0x50, 0x57, 0xf2, 0x92, 0xf2, 0x77, 0x1e, 0xf4, 0x2b, 0x9b, 0xbb, 0x17, 0x81, 0x4b, 0x76, 0x0b, 0xea, 0x5a, 0x5e, 0x50, 0xfc, 0xce, 0xbd, 0xde, 0xda, 0x21,
0x64, 0x32, 0x0a, 0xa4, 0x40, 0x07, 0x76, 0x17, 0xb6, 0xa6, 0x52, 0xfb, 0xe1, 0x9c, 0x50, 0xe5, 0xb9, 0x8c, 0x23, 0x29, 0xd0, 0x81, 0xdd, 0x86, 0x8d, 0x89, 0x34, 0xa1, 0x9a, 0x11, 0xaa, 0x82,
0x04, 0x91, 0xeb, 0x63, 0xb2, 0x08, 0xeb, 0xc1, 0x3f, 0x85, 0xb6, 0xdb, 0x41, 0xb1, 0xf7, 0xa1, 0x20, 0x72, 0x7d, 0x48, 0x16, 0xe1, 0x3c, 0xf8, 0xa7, 0xd0, 0xf6, 0x27, 0x68, 0xf6, 0x3e, 0x34,
0xa1, 0xe4, 0xa5, 0x83, 0xf9, 0xbf, 0x4a, 0x06, 0x41, 0x46, 0xfe, 0xb5, 0xc5, 0x78, 0x12, 0x4e, 0xb4, 0xbc, 0xf0, 0x30, 0xdf, 0x58, 0x8b, 0x20, 0xc8, 0xc8, 0xbf, 0x76, 0x18, 0x8f, 0xd5, 0x04,
0x11, 0x63, 0x12, 0x4e, 0x2d, 0x49, 0xb8, 0x44, 0xa6, 0xe9, 0xc9, 0x58, 0x94, 0x15, 0xa6, 0xc9, 0x31, 0xa6, 0x6a, 0xe2, 0x48, 0xc2, 0x25, 0x32, 0x4d, 0x4f, 0xc6, 0xa1, 0x5c, 0x63, 0x9a, 0x4c,
0xc4, 0x1f, 0x41, 0xb7, 0x00, 0x45, 0xb1, 0x51, 0x99, 0x9e, 0xab, 0xe0, 0x5a, 0x7e, 0xc6, 0xb0, 0xfc, 0x01, 0x74, 0x4b, 0x50, 0x34, 0x1b, 0x56, 0xe9, 0xb9, 0x0a, 0xae, 0xe3, 0x67, 0x04, 0x9b,
0x6d, 0x3a, 0x0c, 0x62, 0x2d, 0x05, 0xf5, 0x6c, 0x90, 0x31, 0x3b, 0xff, 0x27, 0x00, 0xd6, 0xff, 0xb6, 0xc3, 0x20, 0xd6, 0xca, 0xa6, 0x2d, 0xb7, 0xc9, 0x9a, 0xbd, 0xff, 0x23, 0x00, 0xe7, 0x7f,
0x6a, 0xb4, 0x23, 0xd8, 0x9e, 0x19, 0xbb, 0xc5, 0xbb, 0x53, 0xda, 0x46, 0x09, 0x67, 0xe6, 0x33, 0x35, 0xda, 0x21, 0x6c, 0x4e, 0xad, 0xdd, 0xe1, 0xdd, 0xae, 0x1c, 0xa3, 0x85, 0x37, 0xf3, 0x29,
0xe8, 0x11, 0x9e, 0xef, 0x97, 0x32, 0x5d, 0x86, 0xf2, 0x27, 0x76, 0x0b, 0x1a, 0x68, 0xa3, 0xdd, 0x6c, 0x11, 0x9e, 0xef, 0x2f, 0x65, 0x76, 0xa9, 0xe4, 0x4f, 0xec, 0x06, 0x34, 0xd0, 0x46, 0xa7,
0xfe, 0x96, 0x9e, 0x4c, 0xc5, 0xfe, 0x52, 0x2b, 0xf7, 0x97, 0x3d, 0x68, 0x99, 0x4a, 0x95, 0x6a, 0xfd, 0x23, 0x3c, 0x99, 0xca, 0xfd, 0xa5, 0x56, 0xed, 0x2f, 0xbb, 0xd0, 0xb2, 0x95, 0x2a, 0x75,
0x50, 0x1f, 0xd6, 0xb1, 0x56, 0x9c, 0xcc, 0x7f, 0xf7, 0xec, 0xe3, 0x31, 0x47, 0xcf, 0x19, 0xf5, 0xbf, 0x3e, 0xa8, 0x63, 0xad, 0x78, 0x99, 0xff, 0x11, 0xb8, 0xc7, 0x63, 0x53, 0x2f, 0x18, 0x0d,
0xae, 0x65, 0x94, 0x8d, 0xa1, 0x95, 0xca, 0x40, 0x86, 0x89, 0xc6, 0x83, 0x14, 0x49, 0x14, 0x46, 0x5e, 0xca, 0x28, 0x1b, 0x41, 0x2b, 0x93, 0x91, 0x54, 0xa9, 0xc1, 0x44, 0xca, 0x24, 0x0a, 0xab,
0xfd, 0xd8, 0xd7, 0xbe, 0xd8, 0xf8, 0xb0, 0x9b, 0x50, 0x3b, 0x3e, 0xa3, 0xcc, 0xf9, 0x35, 0x1f, 0x7e, 0x18, 0x9a, 0x50, 0xac, 0x7c, 0xd8, 0x75, 0xa8, 0x1d, 0x9d, 0x52, 0xe4, 0xe2, 0x9a, 0x8f,
0xcb, 0xf5, 0x99, 0x3f, 0xcf, 0xa4, 0xa8, 0x1d, 0x9f, 0xb1, 0x3b, 0xb0, 0x93, 0xa4, 0x72, 0x79, 0xe4, 0xf2, 0x34, 0x9c, 0xe5, 0x52, 0xd4, 0x8e, 0x4e, 0xd9, 0x2d, 0xd8, 0x4e, 0x33, 0x79, 0x79,
0xaa, 0x7d, 0x9d, 0xa9, 0x42, 0x17, 0xa9, 0x68, 0xf9, 0x43, 0x68, 0x09, 0xb7, 0xe9, 0xdd, 0x02, 0x62, 0x42, 0x93, 0xeb, 0x52, 0x17, 0x59, 0xd3, 0xf2, 0xfb, 0xd0, 0x12, 0xfe, 0xd0, 0xdb, 0x25,
0x08, 0x73, 0x29, 0x3b, 0x65, 0x10, 0x39, 0x00, 0xfe, 0x14, 0xda, 0x27, 0x69, 0xb8, 0xf4, 0x83, 0x10, 0xf6, 0x52, 0xb6, 0xab, 0x20, 0x0a, 0x00, 0xfc, 0x31, 0xb4, 0x8f, 0x33, 0x75, 0x19, 0x46,
0xf5, 0xf1, 0x19, 0xfb, 0x0a, 0x93, 0x59, 0x61, 0x12, 0xbf, 0x92, 0x91, 0x0d, 0x7f, 0xcb, 0x86, 0xcb, 0xa3, 0x53, 0xf6, 0x15, 0x06, 0x73, 0xc2, 0x38, 0x79, 0x21, 0x63, 0xb7, 0xfd, 0x2d, 0xb7,
0x9f, 0x94, 0x8c, 0xa2, 0xe2, 0xcc, 0xd7, 0xb0, 0x53, 0xf6, 0x60, 0x7d, 0x68, 0x6a, 0xbb, 0x0f, 0xfd, 0xb8, 0x62, 0x14, 0x6b, 0xce, 0x7c, 0x09, 0xdb, 0x55, 0x0f, 0xd6, 0x83, 0xa6, 0x71, 0xe7,
0x5e, 0xb5, 0x11, 0xcc, 0x75, 0x1c, 0x45, 0x53, 0xb9, 0xa2, 0xeb, 0x68, 0x0a, 0x27, 0x9a, 0x36, 0xe0, 0x55, 0x5b, 0xc1, 0x5e, 0xc7, 0x61, 0x3c, 0x91, 0x0b, 0xba, 0x8e, 0xa6, 0xf0, 0xa2, 0x6d,
0x3a, 0x2b, 0xb5, 0x51, 0x6a, 0xf9, 0x86, 0xa6, 0xc6, 0xb5, 0x34, 0x71, 0x05, 0x7d, 0x77, 0xfc, 0xa3, 0xd3, 0x4a, 0x1b, 0xa5, 0x96, 0x6f, 0x69, 0x6a, 0xbc, 0x94, 0x26, 0xae, 0xa1, 0xe7, 0xd3,
0x6f, 0xa2, 0x69, 0x7e, 0xa2, 0x8f, 0x4b, 0x54, 0x78, 0x85, 0x70, 0xe7, 0x5e, 0xb8, 0x8c, 0x31, 0xff, 0x26, 0x9e, 0x14, 0x19, 0x7d, 0x5c, 0xa1, 0x22, 0x28, 0x6d, 0xf7, 0xee, 0xa5, 0xcb, 0x18,
0xb4, 0x37, 0x27, 0xb2, 0xcf, 0x70, 0xb7, 0x7a, 0x72, 0x91, 0xbb, 0xf0, 0x11, 0x30, 0xbb, 0xcb, 0x41, 0x7b, 0x95, 0x91, 0x7b, 0x86, 0x3b, 0xeb, 0x99, 0x8b, 0xc2, 0x85, 0x0f, 0x81, 0xb9, 0x53,
0xe1, 0x4c, 0x06, 0xaf, 0x26, 0xab, 0x67, 0xa1, 0xa2, 0x91, 0x25, 0xd3, 0xd4, 0x30, 0xdf, 0x16, 0x0e, 0xa6, 0x32, 0x7a, 0x31, 0x5e, 0x3c, 0x51, 0x9a, 0x46, 0x96, 0xcc, 0x32, 0xcb, 0x7c, 0x5b,
0xb4, 0xe6, 0x6b, 0xe8, 0x1c, 0xe2, 0x20, 0x37, 0x17, 0xc6, 0x6e, 0x43, 0x2f, 0xc8, 0x52, 0x1a, 0xd0, 0x9a, 0x2f, 0xa1, 0x73, 0x80, 0x83, 0xdc, 0x5e, 0x18, 0xbb, 0x09, 0x5b, 0x51, 0x9e, 0xd1,
0x1e, 0xa6, 0x11, 0x9b, 0xde, 0x52, 0x56, 0xb2, 0x21, 0x74, 0x16, 0x72, 0x91, 0xc4, 0xf1, 0xfc, 0xf0, 0xb0, 0x8d, 0xd8, 0xf6, 0x96, 0xaa, 0x92, 0x0d, 0xa0, 0x33, 0x97, 0xf3, 0x34, 0x49, 0x66,
0x34, 0xfc, 0x59, 0xda, 0x97, 0x5b, 0x54, 0x31, 0x0e, 0xdd, 0x85, 0xba, 0xf8, 0x21, 0x93, 0x99, 0x27, 0xea, 0x67, 0xe9, 0x5e, 0x6e, 0x59, 0xc5, 0x38, 0x74, 0xe7, 0xfa, 0xfc, 0x87, 0x5c, 0xe6,
0x24, 0x97, 0x3a, 0xb9, 0x94, 0x74, 0xdc, 0x87, 0xb6, 0x90, 0x97, 0xb6, 0xfd, 0xf6, 0xa1, 0xa9, 0x92, 0x5c, 0xea, 0xe4, 0x52, 0xd1, 0xf1, 0x10, 0xda, 0x42, 0x5e, 0xb8, 0xf6, 0xdb, 0x83, 0xa6,
0xb4, 0x9f, 0xba, 0x84, 0x46, 0xc0, 0x72, 0x94, 0xd1, 0xd4, 0x26, 0xc0, 0x25, 0x96, 0x45, 0xa8, 0x36, 0x61, 0xe6, 0x03, 0x5a, 0x01, 0xcb, 0x51, 0xc6, 0x13, 0x17, 0x00, 0x97, 0x58, 0x16, 0x4a,
0x1e, 0xe7, 0xad, 0xab, 0x25, 0x36, 0xb2, 0x2b, 0xde, 0x06, 0x1d, 0x0f, 0x97, 0xfc, 0x16, 0x74, 0x3f, 0x2c, 0x5a, 0x57, 0x4b, 0xac, 0x64, 0x5f, 0xbc, 0x0d, 0x4a, 0x0f, 0x97, 0xfc, 0x06, 0x74,
0xbe, 0x2b, 0xa0, 0x62, 0xd0, 0x50, 0x88, 0xc6, 0xe4, 0xa0, 0x35, 0xbf, 0x0b, 0xbb, 0x42, 0x26, 0xbe, 0x2b, 0xa1, 0x62, 0xd0, 0xd0, 0x88, 0xc6, 0xc6, 0xa0, 0x35, 0xbf, 0x0d, 0x3b, 0x42, 0xa6,
0xf3, 0x35, 0xe1, 0xb0, 0xe7, 0xcb, 0xa7, 0x9f, 0x57, 0x9c, 0x7e, 0xfc, 0x37, 0xcf, 0xb6, 0xc2, 0xb3, 0x25, 0xe1, 0x70, 0xf9, 0x15, 0xd3, 0x2f, 0x28, 0x4f, 0x3f, 0xfe, 0x7b, 0xe0, 0x5a, 0xe1,
0x83, 0x78, 0xba, 0x76, 0x13, 0xc6, 0x7b, 0xed, 0x84, 0x79, 0xe3, 0xba, 0x2b, 0xce, 0xc8, 0xfa, 0x7e, 0x32, 0x59, 0xfa, 0x09, 0x13, 0xbc, 0x72, 0xc2, 0xbc, 0x76, 0xdd, 0x95, 0x67, 0x64, 0xfd,
0x6b, 0x67, 0x64, 0xa3, 0x3a, 0x23, 0xf9, 0x3d, 0x80, 0x23, 0x75, 0xe8, 0x67, 0x17, 0x33, 0xfd, 0x95, 0x33, 0xb2, 0xb1, 0x3e, 0x23, 0xf9, 0x1d, 0x80, 0x43, 0x7d, 0x10, 0xe6, 0xe7, 0x53, 0xf3,
0x22, 0x41, 0xef, 0x23, 0x15, 0x90, 0x94, 0x25, 0x74, 0x92, 0x96, 0x28, 0x68, 0xf8, 0x23, 0xd8, 0x2c, 0x45, 0xef, 0x43, 0x1d, 0x91, 0x94, 0xa7, 0x94, 0x49, 0x4b, 0x94, 0x34, 0xfc, 0x01, 0x6c,
0x39, 0x52, 0xcf, 0x75, 0x72, 0x48, 0xcd, 0x7b, 0x1d, 0x05, 0x58, 0xd2, 0xa1, 0x8a, 0x74, 0x12, 0x1f, 0xea, 0xa7, 0x26, 0x3d, 0xa0, 0xe6, 0xbd, 0x8c, 0x23, 0x2c, 0x69, 0xa5, 0x63, 0x93, 0x46,
0xd0, 0x9d, 0xac, 0xa3, 0xc0, 0x46, 0x55, 0xb4, 0xfc, 0x57, 0x0f, 0x7a, 0xf4, 0x6a, 0xbe, 0x5d, 0x74, 0x27, 0xcb, 0x38, 0x72, 0xbb, 0xd6, 0xb4, 0xfc, 0xb7, 0x00, 0xb6, 0xe8, 0xd5, 0x7c, 0xbb,
0xc9, 0x20, 0xd3, 0x71, 0x8a, 0x8c, 0x4d, 0xd3, 0x70, 0x29, 0x53, 0x5b, 0x4f, 0x56, 0xc2, 0xd3, 0x90, 0x51, 0x6e, 0x92, 0x0c, 0x19, 0x9b, 0x64, 0xea, 0x52, 0x66, 0xae, 0x9e, 0x9c, 0x84, 0xd9,
0x9c, 0x67, 0x51, 0xf0, 0x1c, 0xe7, 0xa4, 0x19, 0x8a, 0x1b, 0xb9, 0xfc, 0x05, 0x52, 0xaf, 0x7e, 0x9c, 0xe5, 0x71, 0xf4, 0x14, 0xe7, 0xa4, 0x1d, 0x8a, 0x2b, 0xb9, 0xfa, 0x05, 0x52, 0x5f, 0xff,
0x81, 0xf4, 0xa1, 0x99, 0xf8, 0xa9, 0xbf, 0xb0, 0x5d, 0xc5, 0x08, 0xa8, 0x95, 0x2b, 0x9d, 0xfa, 0x02, 0xe9, 0x41, 0x33, 0x0d, 0xb3, 0x70, 0xee, 0xba, 0x8a, 0x15, 0x50, 0x2b, 0x17, 0x26, 0x0b,
0xf4, 0x59, 0xd2, 0x15, 0x46, 0xe0, 0x9f, 0xdb, 0xce, 0xeb, 0x66, 0x1c, 0x5e, 0x34, 0xed, 0xea, 0xe9, 0xb3, 0xa4, 0x2b, 0xac, 0xc0, 0x3f, 0x77, 0x9d, 0xd7, 0xcf, 0x38, 0xbc, 0x68, 0x3a, 0x35,
0x99, 0x8f, 0x33, 0xda, 0x90, 0x41, 0x63, 0xb2, 0x4e, 0xdc, 0x6b, 0xa5, 0x35, 0xff, 0x12, 0x76, 0xb0, 0x1f, 0x67, 0x74, 0x20, 0x83, 0xc6, 0x78, 0x99, 0xfa, 0xd7, 0x4a, 0x6b, 0xfe, 0x25, 0x6c,
0x4a, 0x81, 0xd8, 0xa1, 0x4a, 0x33, 0xe3, 0xea, 0x11, 0x6a, 0x47, 0xc7, 0x0c, 0xfa, 0x27, 0x7e, 0x57, 0x36, 0x62, 0x87, 0xaa, 0xcc, 0x8c, 0xab, 0x47, 0xa8, 0x1b, 0x1d, 0xbf, 0x04, 0xd0, 0x3b,
0xea, 0x13, 0x13, 0xc5, 0x76, 0xfc, 0x19, 0x74, 0xa8, 0xe7, 0xda, 0x09, 0xeb, 0x5d, 0x3b, 0x61, 0x0e, 0xb3, 0x90, 0xa8, 0x28, 0xf7, 0xe3, 0xcf, 0xa0, 0x43, 0x4d, 0xd7, 0x8d, 0xd8, 0xe0, 0xa5,
0x8b, 0x6e, 0x48, 0x95, 0xb2, 0x09, 0x2c, 0xc6, 0x8d, 0xcc, 0xbf, 0x80, 0x1e, 0x66, 0x9a, 0xac, 0x23, 0xb6, 0xec, 0x86, 0x5c, 0x69, 0x17, 0xc1, 0x81, 0x5c, 0xc9, 0xc8, 0xaf, 0xd2, 0x78, 0x47,
0xdc, 0x3c, 0xfc, 0xa8, 0x0c, 0xf3, 0xff, 0xae, 0x19, 0x14, 0x9c, 0x1c, 0xca, 0x15, 0x74, 0x8b, 0xee, 0xd1, 0x3b, 0x89, 0x7f, 0x01, 0x5b, 0x88, 0x60, 0xbc, 0xf0, 0x83, 0xf2, 0xa3, 0x2a, 0xfe,
0x6a, 0xe4, 0x01, 0x9d, 0x5d, 0x11, 0xe0, 0x9a, 0x7d, 0x80, 0x0f, 0x1e, 0xc7, 0x92, 0x6d, 0x2e, 0x37, 0x7d, 0x97, 0x28, 0x39, 0x79, 0xf8, 0x0b, 0xe8, 0x96, 0xd5, 0x48, 0x10, 0x3a, 0xfb, 0xea,
0x95, 0x59, 0x65, 0x8d, 0xec, 0x13, 0x68, 0x6b, 0x07, 0xa1, 0x32, 0x1a, 0x36, 0x59, 0x73, 0x0f, 0xc0, 0x35, 0xfb, 0x00, 0x2b, 0x01, 0xe7, 0x95, 0xeb, 0x3a, 0x6b, 0x43, 0xcc, 0x19, 0xd9, 0x27,
0xfe, 0x8b, 0x07, 0xad, 0x4d, 0xda, 0x3e, 0x34, 0x43, 0x6a, 0xac, 0x1e, 0x7d, 0x16, 0x1a, 0x81, 0xd0, 0x36, 0x1e, 0xc2, 0xda, 0xcc, 0x58, 0x45, 0x2d, 0x3c, 0xf8, 0xaf, 0x01, 0xb4, 0x56, 0x61,
0x71, 0xa8, 0xe9, 0x95, 0x4d, 0x7a, 0x55, 0x09, 0xd5, 0xf4, 0x8a, 0xdd, 0x83, 0x6d, 0x5b, 0x1d, 0x7b, 0xd0, 0x54, 0xd4, 0x71, 0x03, 0xfa, 0x5e, 0xb4, 0x02, 0xe3, 0x50, 0x33, 0x0b, 0x17, 0xf4,
0x95, 0x8f, 0x95, 0x62, 0x01, 0x39, 0x17, 0x7c, 0x89, 0x49, 0x1a, 0xc7, 0xe7, 0x8a, 0xfa, 0x40, 0xaa, 0xda, 0xaa, 0x99, 0x05, 0xbb, 0x03, 0x9b, 0xae, 0x6c, 0xd6, 0xbe, 0x62, 0xca, 0x95, 0xe5,
0x57, 0x58, 0x89, 0x9f, 0x60, 0x9d, 0x5f, 0x1a, 0x26, 0x0e, 0xd6, 0x93, 0x50, 0xcf, 0xe5, 0xbf, 0x5d, 0x90, 0xc2, 0x34, 0x4b, 0x92, 0x33, 0x4d, 0x0d, 0xa2, 0x2b, 0x9c, 0xc4, 0xcf, 0xb0, 0x01,
0x6e, 0x3a, 0x38, 0x2c, 0x30, 0xc0, 0x7e, 0xc2, 0x19, 0xe1, 0xe0, 0xe6, 0x8f, 0xef, 0x5e, 0x84, 0x5c, 0x58, 0x26, 0xf6, 0x97, 0x63, 0x65, 0x66, 0xf2, 0x3f, 0x77, 0x23, 0x9c, 0x22, 0xb8, 0xc1,
0x7a, 0x96, 0xbd, 0x1c, 0x07, 0xf1, 0xe2, 0xfe, 0xfe, 0x7e, 0x10, 0xdd, 0xa7, 0x7f, 0xa2, 0xfd, 0x7d, 0xdb, 0x59, 0x81, 0x32, 0xd2, 0x27, 0xf2, 0x82, 0x9e, 0x6e, 0x4b, 0x58, 0x61, 0xff, 0xfa,
0xfd, 0xfb, 0x84, 0xef, 0xe5, 0x16, 0xfd, 0xf4, 0xec, 0xff, 0x15, 0x00, 0x00, 0xff, 0xff, 0x00, 0x8f, 0xef, 0x9e, 0x2b, 0x33, 0xcd, 0x9f, 0x8f, 0xa2, 0x64, 0x7e, 0x77, 0x6f, 0x2f, 0x8a, 0xef,
0x7f, 0xd3, 0xa9, 0x30, 0x0d, 0x00, 0x00, 0xd2, 0x2f, 0xd4, 0xde, 0xde, 0x5d, 0x42, 0xfd, 0x7c, 0x83, 0xfe, 0x91, 0xf6, 0xfe, 0x0e, 0x00,
0x00, 0xff, 0xff, 0x56, 0x6a, 0x91, 0x86, 0x5f, 0x0d, 0x00, 0x00,
} }
...@@ -61,7 +61,10 @@ const ( ...@@ -61,7 +61,10 @@ const (
Float1E4 float64 = 10000.0 Float1E4 float64 = 10000.0
AirDropMinIndex uint32 = 100000000 //通过钱包的seed生成一个空投地址,最小index索引 AirDropMinIndex uint32 = 100000000 //通过钱包的seed生成一个空投地址,最小index索引
AirDropMaxIndex uint32 = 101000000 //通过钱包的seed生成一个空投地址,最大index索引 AirDropMaxIndex uint32 = 101000000 //通过钱包的seed生成一个空投地址,最大index索引
MaxBlockCountPerTime int64 = 1000 //从数据库中一次性获取block的最大数 1000个
MaxBlockSizePerTime = 100000000 //从数据库中一次性获取block的最大size100M
AddBlock int64 = 1
DelBlock int64 = 2
) )
//全局账户私钥/公钥 //全局账户私钥/公钥
......
...@@ -203,9 +203,11 @@ message BlockSequences { ...@@ -203,9 +203,11 @@ message BlockSequences {
//平行链区块详细信息 //平行链区块详细信息
// blockdetail : 区块详细信息 // blockdetail : 区块详细信息
// sequence :区块序列号 // sequence :区块序列号
// isSync:写数据库时是否需要刷盘
message ParaChainBlockDetail { message ParaChainBlockDetail {
BlockDetail blockdetail = 1; BlockDetail blockdetail = 1;
int64 sequence = 2; int64 sequence = 2;
bool isSync = 3;
} }
// 定义para交易结构 // 定义para交易结构
...@@ -239,4 +241,5 @@ message ReqParaTxByTitle { ...@@ -239,4 +241,5 @@ message ReqParaTxByTitle {
int64 start = 1; int64 start = 1;
int64 end = 2; int64 end = 2;
string title = 3; string title = 3;
bool isSeq = 4;
} }
...@@ -202,10 +202,16 @@ func (store *Store) GetTxDetailByIter(TxList *types.ReqWalletTransactionList) (* ...@@ -202,10 +202,16 @@ func (store *Store) GetTxDetailByIter(TxList *types.ReqWalletTransactionList) (*
} }
var txbytes [][]byte var txbytes [][]byte
//FromTx是空字符串时。默认从最新的交易开始取count个 //FromTx是空字符串时,
//Direction == 0从最新的交易开始倒序取count个
//Direction == 1从最早的交易开始正序取count个
if len(TxList.FromTx) == 0 { if len(TxList.FromTx) == 0 {
list := store.NewListHelper() list := store.NewListHelper()
if TxList.Direction == 0 {
txbytes = list.IteratorScanFromLast(CalcTxKey(""), TxList.Count) txbytes = list.IteratorScanFromLast(CalcTxKey(""), TxList.Count)
} else {
txbytes = list.IteratorScanFromFirst(CalcTxKey(""), TxList.Count)
}
if len(txbytes) == 0 { if len(txbytes) == 0 {
storelog.Error("GetTxDetailByIter IteratorScanFromLast does not exist tx!") storelog.Error("GetTxDetailByIter IteratorScanFromLast does not exist tx!")
return nil, types.ErrTxNotExist return nil, types.ErrTxNotExist
......
...@@ -415,9 +415,11 @@ func testProcImportPrivKey(t *testing.T, wallet *Wallet) { ...@@ -415,9 +415,11 @@ func testProcImportPrivKey(t *testing.T, wallet *Wallet) {
func testProcWalletTxList(t *testing.T, wallet *Wallet) { func testProcWalletTxList(t *testing.T, wallet *Wallet) {
println("TestProcWalletTxList begin") println("TestProcWalletTxList begin")
//倒序获取最新的三笔交易
txList := &types.ReqWalletTransactionList{ txList := &types.ReqWalletTransactionList{
Count: 3, Count: 3,
Direction: 1, Direction: 0,
FromTx: []byte(""), FromTx: []byte(""),
} }
msg := wallet.client.NewMessage("wallet", types.EventWalletTransactionList, txList) msg := wallet.client.NewMessage("wallet", types.EventWalletTransactionList, txList)
...@@ -427,14 +429,26 @@ func testProcWalletTxList(t *testing.T, wallet *Wallet) { ...@@ -427,14 +429,26 @@ func testProcWalletTxList(t *testing.T, wallet *Wallet) {
walletTxDetails := resp.GetData().(*types.WalletTxDetails) walletTxDetails := resp.GetData().(*types.WalletTxDetails)
var FromTxstr string var FromTxstr string
index := make([]int64, 3)
if len(walletTxDetails.TxDetails) != 3 { if len(walletTxDetails.TxDetails) != 3 {
t.Error("testProcWalletTxList failed") t.Error("testProcWalletTxList failed")
} }
println("TestProcWalletTxList dir last-------") println("TestProcWalletTxList dir last-------")
for _, walletTxDetail := range walletTxDetails.TxDetails { for i, walletTxDetail := range walletTxDetails.TxDetails {
println("TestProcWalletTxList", "Direction", txList.Direction, "WalletTxDetail", walletTxDetail.String()) println("TestProcWalletTxList", "Direction", txList.Direction, "WalletTxDetail", walletTxDetail.String())
index[i] = walletTxDetail.GetHeight()*100000 + walletTxDetail.GetIndex()
FromTxstr = fmt.Sprintf("%018d", walletTxDetail.GetHeight()*100000+walletTxDetail.GetIndex()) FromTxstr = fmt.Sprintf("%018d", walletTxDetail.GetHeight()*100000+walletTxDetail.GetIndex())
} }
//倒序index值的判断,index[0]>index[1]>index[2]
if index[0] <= index[1] {
println("TestProcWalletTxList", "index[0]", index[0], "index[1]", index[1])
t.Error("testProcWalletTxList:Reverse check fail!")
}
if index[1] <= index[2] {
println("TestProcWalletTxList", "index[1]", index[1], "index[2]", index[2])
t.Error("testProcWalletTxList:Reverse check fail!")
}
txList.Direction = 1 txList.Direction = 1
txList.Count = 2 txList.Count = 2
...@@ -466,6 +480,34 @@ func testProcWalletTxList(t *testing.T, wallet *Wallet) { ...@@ -466,6 +480,34 @@ func testProcWalletTxList(t *testing.T, wallet *Wallet) {
for _, walletTxDetail := range walletTxDetails.TxDetails { for _, walletTxDetail := range walletTxDetails.TxDetails {
println("TestProcWalletTxList", "Direction", txList.Direction, "WalletTxDetail", walletTxDetail.String()) println("TestProcWalletTxList", "Direction", txList.Direction, "WalletTxDetail", walletTxDetail.String())
} }
//正序获取最早的三笔交易
txList = &types.ReqWalletTransactionList{
Count: 3,
Direction: 1,
FromTx: []byte(""),
}
msg = wallet.client.NewMessage("wallet", types.EventWalletTransactionList, txList)
wallet.client.Send(msg, true)
resp, err = wallet.client.Wait(msg)
require.NoError(t, err)
walletTxDetails = resp.GetData().(*types.WalletTxDetails)
if len(walletTxDetails.TxDetails) != 3 {
t.Error("testProcWalletTxList failed")
}
for i, walletTxDetail := range walletTxDetails.TxDetails {
index[i] = walletTxDetail.GetHeight()*100000 + walletTxDetail.GetIndex()
}
//正序index值的判断,index[0]<index[1]<index[2]
if index[0] >= index[1] {
println("TestProcWalletTxList", "index[0]", index[0], "index[1]", index[1])
t.Error("testProcWalletTxList:positive check fail!")
}
if index[1] >= index[2] {
println("TestProcWalletTxList", "index[1]", index[1], "index[2]", index[2])
t.Error("testProcWalletTxList:positive check fail!")
}
println("TestProcWalletTxList end") println("TestProcWalletTxList end")
println("--------------------------") println("--------------------------")
} }
......
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