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)
}
...@@ -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