From 62547a472940eb74c2eb7bcc5c6d3048554924f5 Mon Sep 17 00:00:00 2001 From: Paladz Date: Wed, 10 Jul 2019 15:48:10 +0800 Subject: [PATCH] edit code while reviewing the code (#255) * edit code while reviewing the code * change the if err statement * clean the db part code * delete the unused reqid code --- api/transact.go | 7 +- blockchain/rpc/rpc.go | 2 - database/store.go | 26 ++-- database/utxo_view.go | 4 +- database/utxo_view_test.go | 2 +- net/http/reqid/reqid.go | 96 -------------- proposal/blockproposer/blockproposer.go | 4 +- protocol/bbft.go | 215 +++++++++++++------------------ protocol/consensus_node_manager.go | 109 +++++++++------- protocol/protocol.go | 3 +- protocol/validation/test/tx_ugly_test.go | 4 +- protocol/validation/tx.go | 43 +++++-- protocol/validation/tx_test.go | 6 +- 13 files changed, 202 insertions(+), 319 deletions(-) delete mode 100644 net/http/reqid/reqid.go diff --git a/api/transact.go b/api/transact.go index 76e6d537..67d4429f 100644 --- a/api/transact.go +++ b/api/transact.go @@ -11,7 +11,6 @@ import ( "github.com/vapor/account" "github.com/vapor/blockchain/txbuilder" "github.com/vapor/errors" - "github.com/vapor/net/http/reqid" "github.com/vapor/protocol/bc" "github.com/vapor/protocol/bc/types" ) @@ -89,8 +88,7 @@ func (a *API) buildSingle(ctx context.Context, req *BuildRequest) (*txbuilder.Te // POST /build-transaction func (a *API) build(ctx context.Context, buildReqs *BuildRequest) Response { - subctx := reqid.NewSubContext(ctx, reqid.New()) - tmpl, err := a.buildSingle(subctx, buildReqs) + tmpl, err := a.buildSingle(ctx, buildReqs) if err != nil { return NewErrorResponse(err) } @@ -178,8 +176,7 @@ func (a *API) buildTxs(ctx context.Context, req *BuildRequest) ([]*txbuilder.Tem // POST /build-chain-transactions func (a *API) buildChainTxs(ctx context.Context, buildReqs *BuildRequest) Response { - subctx := reqid.NewSubContext(ctx, reqid.New()) - tmpls, err := a.buildTxs(subctx, buildReqs) + tmpls, err := a.buildTxs(ctx, buildReqs) if err != nil { return NewErrorResponse(err) } diff --git a/blockchain/rpc/rpc.go b/blockchain/rpc/rpc.go index 9875ec5a..0710c93c 100644 --- a/blockchain/rpc/rpc.go +++ b/blockchain/rpc/rpc.go @@ -13,7 +13,6 @@ import ( "github.com/vapor/errors" "github.com/vapor/net/http/httperror" - "github.com/vapor/net/http/reqid" ) // Bytom-specific header fields @@ -112,7 +111,6 @@ func (c *Client) CallRaw(ctx context.Context, path string, request interface{}) } // Propagate our request ID so that we can trace a request across nodes. - req.Header.Add("Request-ID", reqid.FromContext(ctx)) req.Header.Set("Content-Type", "application/json") req.Header.Set("User-Agent", c.userAgent()) req.Header.Set(HeaderBlockchainID, c.BlockchainID) diff --git a/database/store.go b/database/store.go index 94f09186..6abd41fb 100644 --- a/database/store.go +++ b/database/store.go @@ -35,18 +35,8 @@ const ( consensusResult ) -var ( - blockStoreKey = []byte{blockStore} - blockHashesPrefix = []byte{blockHashes, colon} - blockHeaderPrefix = []byte{blockHeader, colon} - blockTransactonsPrefix = []byte{blockTransactons, colon} - mainChainIndexPrefix = []byte{mainChainIndex, colon} - txStatusPrefix = []byte{txStatus, colon} - consensusResultPrefix = []byte{consensusResult, colon} -) - func loadBlockStoreStateJSON(db dbm.DB) *protocol.BlockStoreState { - bytes := db.Get(blockStoreKey) + bytes := db.Get([]byte{blockStore}) if bytes == nil { return nil } @@ -69,31 +59,31 @@ type Store struct { func calcMainChainIndexPrefix(height uint64) []byte { buf := [8]byte{} binary.BigEndian.PutUint64(buf[:], height) - return append(mainChainIndexPrefix, buf[:]...) + return append([]byte{mainChainIndex, colon}, buf[:]...) } func calcBlockHashesPrefix(height uint64) []byte { buf := [8]byte{} binary.BigEndian.PutUint64(buf[:], height) - return append(blockHashesPrefix, buf[:]...) + return append([]byte{blockHashes, colon}, buf[:]...) } func calcBlockHeaderKey(hash *bc.Hash) []byte { - return append(blockHeaderPrefix, hash.Bytes()...) + return append([]byte{blockHeader, colon}, hash.Bytes()...) } func calcBlockTransactionsKey(hash *bc.Hash) []byte { - return append(blockTransactonsPrefix, hash.Bytes()...) + return append([]byte{blockTransactons, colon}, hash.Bytes()...) } func calcTxStatusKey(hash *bc.Hash) []byte { - return append(txStatusPrefix, hash.Bytes()...) + return append([]byte{txStatus, colon}, hash.Bytes()...) } func calcConsensusResultKey(seq uint64) []byte { buf := [8]byte{} binary.BigEndian.PutUint64(buf[:], seq) - return append(consensusResultPrefix, buf[:]...) + return append([]byte{consensusResult, colon}, buf[:]...) } // GetBlockHeader return the block header by given hash @@ -361,7 +351,7 @@ func (s *Store) SaveChainStatus(blockHeader, irrBlockHeader *types.BlockHeader, if err != nil { return err } - batch.Set(blockStoreKey, bytes) + batch.Set([]byte{blockStore}, bytes) // save main chain blockHeaders for _, bh := range mainBlockHeaders { diff --git a/database/utxo_view.go b/database/utxo_view.go index f05dafda..987a23d8 100644 --- a/database/utxo_view.go +++ b/database/utxo_view.go @@ -72,12 +72,12 @@ func getUtxo(db dbm.DB, hash *bc.Hash) (*storage.UtxoEntry, error) { func saveUtxoView(batch dbm.Batch, view *state.UtxoViewpoint) error { for key, entry := range view.Entries { - if (entry.Type == storage.CrosschainUTXOType) && (!entry.Spent) { + if entry.Type == storage.CrosschainUTXOType && !entry.Spent { batch.Delete(calcUtxoKey(&key)) continue } - if (entry.Type == storage.NormalUTXOType || entry.Type == storage.VoteUTXOType) && (entry.Spent) { + if entry.Type == storage.NormalUTXOType && entry.Spent { batch.Delete(calcUtxoKey(&key)) continue } diff --git a/database/utxo_view_test.go b/database/utxo_view_test.go index b9b2663a..0d6bc82a 100644 --- a/database/utxo_view_test.go +++ b/database/utxo_view_test.go @@ -57,7 +57,7 @@ func TestSaveUtxoView(t *testing.T) { { hash: bc.Hash{V0: 6}, utxoEntry: storage.NewUtxoEntry(storage.VoteUTXOType, 0, true), - exist: false, + exist: true, }, { hash: bc.Hash{V0: 7}, diff --git a/net/http/reqid/reqid.go b/net/http/reqid/reqid.go deleted file mode 100644 index f850269e..00000000 --- a/net/http/reqid/reqid.go +++ /dev/null @@ -1,96 +0,0 @@ -// Package reqid creates request IDs and stores them in Contexts. -package reqid - -import ( - "context" - "crypto/rand" - "encoding/hex" - "net/http" - - log "github.com/sirupsen/logrus" -) - -// key is an unexported type for keys defined in this package. -// This prevents collisions with keys defined in other packages. -type key int - -const ( - // reqIDKey is the key for request IDs in Contexts. It is - // unexported; clients use NewContext and FromContext - // instead of using this key directly. - reqIDKey key = iota - // subReqIDKey is the key for sub-request IDs in Contexts. It is - // unexported; clients use NewSubContext and FromSubContext - // instead of using this key directly. - subReqIDKey - // coreIDKey is the key for Chain-Core-ID request header field values. - // It is only for statistics; don't use it for authorization. - coreIDKey - // pathKey is the key for the request path being handled. - pathKey - logModule = "reqid" -) - -// New generates a random request ID. -func New() string { - // Given n IDs of length b bits, the probability that there will be a collision is bounded by - // the number of pairs of IDs multiplied by the probability that any pair might collide: - // p ≤ n(n - 1)/2 * 1/(2^b) - // - // We assume an upper bound of 1000 req/sec, which means that in a week there will be - // n = 1000 * 604800 requests. If l = 10, b = 8*10, then p ≤ 1.512e-7, which is a suitably - // low probability. - l := 10 - b := make([]byte, l) - _, err := rand.Read(b) - if err != nil { - log.WithFields(log.Fields{"module": logModule, "error": err}).Info("error making reqID") - } - return hex.EncodeToString(b) -} - -// NewContext returns a new Context that carries reqid. -// It also adds a log prefix to print the request ID using -// package bytom/log. -func NewContext(ctx context.Context, reqid string) context.Context { - ctx = context.WithValue(ctx, reqIDKey, reqid) - return ctx -} - -// FromContext returns the request ID stored in ctx, -// if any. -func FromContext(ctx context.Context) string { - reqID, _ := ctx.Value(reqIDKey).(string) - return reqID -} - -// CoreIDFromContext returns the Chain-Core-ID stored in ctx, -// or the empty string. -func CoreIDFromContext(ctx context.Context) string { - id, _ := ctx.Value(coreIDKey).(string) - return id -} - -// PathFromContext returns the HTTP path stored in ctx, -// or the empty string. -func PathFromContext(ctx context.Context) string { - path, _ := ctx.Value(pathKey).(string) - return path -} - -func NewSubContext(ctx context.Context, reqid string) context.Context { - ctx = context.WithValue(ctx, subReqIDKey, reqid) - return ctx -} - -// FromSubContext returns the sub-request ID stored in ctx, -// if any. -func FromSubContext(ctx context.Context) string { - subReqID, _ := ctx.Value(subReqIDKey).(string) - return subReqID -} - -func Handler(handler http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - }) -} diff --git a/proposal/blockproposer/blockproposer.go b/proposal/blockproposer/blockproposer.go index 32105db3..0b568cf1 100644 --- a/proposal/blockproposer/blockproposer.go +++ b/proposal/blockproposer/blockproposer.go @@ -64,13 +64,13 @@ func (b *BlockProposer) generateBlocks() { nextBlockTime += consensus.BlockTimeInterval } - isBlocker, err := b.chain.IsBlocker(&bestBlockHash, xpubStr, nextBlockTime) + blocker, err := b.chain.GetBlocker(&bestBlockHash, nextBlockTime) if err != nil { log.WithFields(log.Fields{"module": logModule, "error": err, "pubKey": xpubStr}).Error("fail on check is next blocker") continue } - if !isBlocker { + if xpubStr != blocker { continue } diff --git a/protocol/bbft.go b/protocol/bbft.go index a34e7030..70b1d810 100644 --- a/protocol/bbft.go +++ b/protocol/bbft.go @@ -28,6 +28,44 @@ func signCacheKey(blockHash, pubkey string) string { return fmt.Sprintf("%s:%s", blockHash, pubkey) } +func (c *Chain) checkDoubleSign(bh *types.BlockHeader, xPub string) error { + blockHashes, err := c.store.GetBlockHashesByHeight(bh.Height) + if err != nil { + return err + } + + for _, blockHash := range blockHashes { + if *blockHash == bh.Hash() { + continue + } + + blockHeader, err := c.store.GetBlockHeader(blockHash) + if err != nil { + return err + } + + consensusNode, err := c.getConsensusNode(&blockHeader.PreviousBlockHash, xPub) + if err == errNotFoundConsensusNode { + continue + } else if err != nil { + return err + } + + if blockHeader.BlockWitness.Get(consensusNode.Order) != nil { + return errDoubleSignBlock + } + } + return nil +} + +func (c *Chain) checkNodeSign(bh *types.BlockHeader, consensusNode *state.ConsensusNode, signature []byte) error { + if !consensusNode.XPub.Verify(bh.Hash().Bytes(), signature) { + return errInvalidSignature + } + + return c.checkDoubleSign(bh, consensusNode.XPub.String()) +} + func (c *Chain) isIrreversible(blockHeader *types.BlockHeader) bool { consensusNodes, err := c.getConsensusNodes(&blockHeader.PreviousBlockHash) if err != nil { @@ -44,56 +82,33 @@ func (c *Chain) isIrreversible(blockHeader *types.BlockHeader) bool { return signCount > len(consensusNodes)*2/3 } -// GetConsensusResultByHash return vote result by block hash -func (c *Chain) GetConsensusResultByHash(blockHash *bc.Hash) (*state.ConsensusResult, error) { - blockHeader, err := c.store.GetBlockHeader(blockHash) - if err != nil { - return nil, err - } - return c.getConsensusResult(state.CalcVoteSeq(blockHeader.Height), blockHeader) -} - -// IsBlocker returns whether the consensus node is a blocker at the specified time -func (c *Chain) IsBlocker(prevBlockHash *bc.Hash, pubKey string, timeStamp uint64) (bool, error) { - xPub, err := c.GetBlocker(prevBlockHash, timeStamp) - if err != nil { - return false, err - } - return xPub == pubKey, nil -} - -// ProcessBlockSignature process the received block signature messages -// return whether a block become irreversible, if so, the chain module must update status -func (c *Chain) ProcessBlockSignature(signature, xPub []byte, blockHash *bc.Hash) error { - xpubStr := hex.EncodeToString(xPub[:]) - blockHeader, _ := c.store.GetBlockHeader(blockHash) - - // save the signature if the block is not exist - if blockHeader == nil { - cacheKey := signCacheKey(blockHash.String(), xpubStr) - c.signatureCache.Add(cacheKey, signature) - return nil - } - - consensusNode, err := c.getConsensusNode(&blockHeader.PreviousBlockHash, xpubStr) - if err != nil { +func (c *Chain) updateBlockSignature(blockHeader *types.BlockHeader, nodeOrder uint64, signature []byte) error { + blockHeader.Set(nodeOrder, signature) + if err := c.store.SaveBlockHeader(blockHeader); err != nil { return err } - if blockHeader.BlockWitness.Get(consensusNode.Order) != nil { + if !c.isIrreversible(blockHeader) || blockHeader.Height <= c.lastIrrBlockHeader.Height { return nil } - c.cond.L.Lock() - defer c.cond.L.Unlock() - if err := c.checkNodeSign(blockHeader, consensusNode, signature); err != nil { - return err - } + if c.InMainChain(blockHeader.Hash()) { + if err := c.store.SaveChainStatus(c.bestBlockHeader, blockHeader, []*types.BlockHeader{}, state.NewUtxoViewpoint(), []*state.ConsensusResult{}); err != nil { + return err + } - if err := c.updateBlockSignature(blockHeader, consensusNode.Order, signature); err != nil { - return err + c.lastIrrBlockHeader = blockHeader + } else { + // block is on a forked chain + log.WithFields(log.Fields{"module": logModule}).Info("majority votes received on forked chain") + tail, err := c.traceLongestChainTail(blockHeader) + if err != nil { + return err + } + + return c.reorganizeChain(tail) } - return c.eventDispatcher.Post(event.BlockSignatureEvent{BlockHash: *blockHash, Signature: signature, XPub: xPub}) + return nil } // validateSign verify the signatures of block, and return the number of correct signature @@ -105,14 +120,15 @@ func (c *Chain) validateSign(block *types.Block) error { return err } + blocker, err := c.GetBlocker(&block.PreviousBlockHash, block.Timestamp) + if err != nil { + return err + } + hasBlockerSign := false blockHash := block.Hash() for pubKey, node := range consensusNodeMap { - if len(block.Witness) <= int(node.Order) { - continue - } - - if block.Get(node.Order) == nil { + if block.BlockWitness.Get(node.Order) == nil { cachekey := signCacheKey(blockHash.String(), pubKey) if signature, ok := c.signatureCache.Get(cachekey); ok { block.Set(node.Order, signature.([]byte)) @@ -123,21 +139,15 @@ func (c *Chain) validateSign(block *types.Block) error { if err := c.checkNodeSign(&block.BlockHeader, node, block.Get(node.Order)); err == errDoubleSignBlock { log.WithFields(log.Fields{"module": logModule, "blockHash": blockHash.String(), "pubKey": pubKey}).Warn("the consensus node double sign the same height of different block") - block.Delete(node.Order) + block.BlockWitness.Delete(node.Order) continue } else if err != nil { return err } - isBlocker, err := c.IsBlocker(&block.PreviousBlockHash, pubKey, block.Timestamp) - if err != nil { - return err - } - - if isBlocker { + if blocker == pubKey { hasBlockerSign = true } - } if !hasBlockerSign { @@ -146,40 +156,38 @@ func (c *Chain) validateSign(block *types.Block) error { return nil } -func (c *Chain) checkNodeSign(bh *types.BlockHeader, consensusNode *state.ConsensusNode, signature []byte) error { - if !consensusNode.XPub.Verify(bh.Hash().Bytes(), signature) { - return errInvalidSignature +// ProcessBlockSignature process the received block signature messages +// return whether a block become irreversible, if so, the chain module must update status +func (c *Chain) ProcessBlockSignature(signature, xPub []byte, blockHash *bc.Hash) error { + xpubStr := hex.EncodeToString(xPub[:]) + blockHeader, _ := c.store.GetBlockHeader(blockHash) + + // save the signature if the block is not exist + if blockHeader == nil { + cacheKey := signCacheKey(blockHash.String(), xpubStr) + c.signatureCache.Add(cacheKey, signature) + return nil } - blockHashes, err := c.store.GetBlockHashesByHeight(bh.Height) + consensusNode, err := c.getConsensusNode(&blockHeader.PreviousBlockHash, xpubStr) if err != nil { return err } - for _, blockHash := range blockHashes { - if *blockHash == bh.Hash() { - continue - } - - blockHeader, err := c.store.GetBlockHeader(blockHash) - if err != nil { - return err - } - - consensusNode, err := c.getConsensusNode(&blockHeader.PreviousBlockHash, consensusNode.XPub.String()) - if err != nil && err != errNotFoundConsensusNode { - return err - } + if blockHeader.BlockWitness.Get(consensusNode.Order) != nil { + return nil + } - if err == errNotFoundConsensusNode { - continue - } + c.cond.L.Lock() + defer c.cond.L.Unlock() + if err := c.checkNodeSign(blockHeader, consensusNode, signature); err != nil { + return err + } - if blockHeader.BlockWitness.Get(consensusNode.Order) != nil { - return errDoubleSignBlock - } + if err := c.updateBlockSignature(blockHeader, consensusNode.Order, signature); err != nil { + return err } - return nil + return c.eventDispatcher.Post(event.BlockSignatureEvent{BlockHash: *blockHash, Signature: signature, XPub: xPub}) } // SignBlock signing the block if current node is consensus node @@ -193,24 +201,12 @@ func (c *Chain) SignBlock(block *types.Block) ([]byte, error) { return nil, err } - //check double sign in same block height - blockHashes, err := c.store.GetBlockHashesByHeight(block.Height) - if err != nil { + if err := c.checkDoubleSign(&block.BlockHeader, node.XPub.String()); err == errDoubleSignBlock { + return nil, nil + } else if err != nil { return nil, err } - for _, hash := range blockHashes { - blockHeader, err := c.store.GetBlockHeader(hash) - if err != nil { - return nil, err - } - - // Has already signed the same height block - if blockHeader.BlockWitness.Get(node.Order) != nil { - return nil, nil - } - } - signature := block.Get(node.Order) if len(signature) == 0 { signature = xprv.Sign(block.Hash().Bytes()) @@ -218,32 +214,3 @@ func (c *Chain) SignBlock(block *types.Block) ([]byte, error) { } return signature, nil } - -func (c *Chain) updateBlockSignature(blockHeader *types.BlockHeader, nodeOrder uint64, signature []byte) error { - blockHeader.Set(nodeOrder, signature) - if err := c.store.SaveBlockHeader(blockHeader); err != nil { - return err - } - - if !c.isIrreversible(blockHeader) || blockHeader.Height <= c.lastIrrBlockHeader.Height { - return nil - } - - if c.InMainChain(blockHeader.Hash()) { - if err := c.store.SaveChainStatus(c.bestBlockHeader, blockHeader, []*types.BlockHeader{}, state.NewUtxoViewpoint(), []*state.ConsensusResult{}); err != nil { - return err - } - - c.lastIrrBlockHeader = blockHeader - } else { - // block is on a forked chain - log.WithFields(log.Fields{"module": logModule}).Info("majority votes received on forked chain") - tail, err := c.traceLongestChainTail(blockHeader) - if err != nil { - return err - } - - return c.reorganizeChain(tail) - } - return nil -} diff --git a/protocol/consensus_node_manager.go b/protocol/consensus_node_manager.go index 961a35d4..930e42db 100644 --- a/protocol/consensus_node_manager.go +++ b/protocol/consensus_node_manager.go @@ -13,41 +13,10 @@ var ( errNotFoundBlockNode = errors.New("can not find block node") ) -func (c *Chain) getConsensusNode(prevBlockHash *bc.Hash, pubkey string) (*state.ConsensusNode, error) { - consensusNodeMap, err := c.getConsensusNodes(prevBlockHash) - if err != nil { - return nil, err - } - - node, exist := consensusNodeMap[pubkey] - if !exist { - return nil, errNotFoundConsensusNode - } - return node, nil -} - -// GetBlocker return blocker by specified timestamp -func (c *Chain) GetBlocker(prevBlockHash *bc.Hash, timeStamp uint64) (string, error) { - consensusNodeMap, err := c.getConsensusNodes(prevBlockHash) - if err != nil { - return "", err - } - - prevVoteRoundLastBlock, err := c.getPrevRoundLastBlock(prevBlockHash) - if err != nil { - return "", err - } - - startTimestamp := prevVoteRoundLastBlock.Timestamp + consensus.BlockTimeInterval - order := getBlockerOrder(startTimestamp, timeStamp, uint64(len(consensusNodeMap))) - for xPub, consensusNode := range consensusNodeMap { - if consensusNode.Order == order { - return xPub, nil - } - } - - // impossible occur - return "", errors.New("can not find blocker by given timestamp") +func (c *Chain) getBestConsensusResult() (*state.ConsensusResult, error) { + bestBlockHeader := c.bestBlockHeader + seq := state.CalcVoteSeq(bestBlockHeader.Height) + return c.getConsensusResult(seq, bestBlockHeader) } func getBlockerOrder(startTimestamp, blockTimestamp, numOfConsensusNode uint64) uint64 { @@ -59,19 +28,17 @@ func getBlockerOrder(startTimestamp, blockTimestamp, numOfConsensusNode uint64) return (blockTimestamp - lastRoundStartTime) / (consensus.BlockNumEachNode * consensus.BlockTimeInterval) } -func (c *Chain) getPrevRoundLastBlock(prevBlockHash *bc.Hash) (*types.BlockHeader, error) { - blockHeader, err := c.store.GetBlockHeader(prevBlockHash) +func (c *Chain) getConsensusNode(prevBlockHash *bc.Hash, pubkey string) (*state.ConsensusNode, error) { + consensusNodeMap, err := c.getConsensusNodes(prevBlockHash) if err != nil { - return nil, errNotFoundBlockNode + return nil, err } - for blockHeader.Height%consensus.RoundVoteBlockNums != 0 { - blockHeader, err = c.store.GetBlockHeader(&blockHeader.PreviousBlockHash) - if err != nil { - return nil, err - } + node, exist := consensusNodeMap[pubkey] + if !exist { + return nil, errNotFoundConsensusNode } - return blockHeader, nil + return node, nil } func (c *Chain) getConsensusNodes(prevBlockHash *bc.Hash) (map[string]*state.ConsensusNode, error) { @@ -99,12 +66,6 @@ func (c *Chain) getConsensusNodes(prevBlockHash *bc.Hash) (map[string]*state.Con return consensusResult.ConsensusNodes() } -func (c *Chain) getBestConsensusResult() (*state.ConsensusResult, error) { - bestBlockHeader := c.bestBlockHeader - seq := state.CalcVoteSeq(bestBlockHeader.Height) - return c.getConsensusResult(seq, bestBlockHeader) -} - // getConsensusResult return the vote result // seq represent the sequence of vote // blockHeader represent the chain in which the result of the vote is located @@ -122,6 +83,21 @@ func (c *Chain) getConsensusResult(seq uint64, blockHeader *types.BlockHeader) ( return consensusResult, nil } +func (c *Chain) getPrevRoundLastBlock(prevBlockHash *bc.Hash) (*types.BlockHeader, error) { + blockHeader, err := c.store.GetBlockHeader(prevBlockHash) + if err != nil { + return nil, errNotFoundBlockNode + } + + for blockHeader.Height%consensus.RoundVoteBlockNums != 0 { + blockHeader, err = c.store.GetBlockHeader(&blockHeader.PreviousBlockHash) + if err != nil { + return nil, err + } + } + return blockHeader, nil +} + func (c *Chain) reorganizeConsensusResult(consensusResult *state.ConsensusResult, blockHeader *types.BlockHeader) error { mainChainBlockHeader, err := c.store.GetBlockHeader(&consensusResult.BlockHash) if err != nil { @@ -158,3 +134,36 @@ func (c *Chain) reorganizeConsensusResult(consensusResult *state.ConsensusResult } return nil } + +// GetBlocker return blocker by specified timestamp +func (c *Chain) GetBlocker(prevBlockHash *bc.Hash, timeStamp uint64) (string, error) { + consensusNodeMap, err := c.getConsensusNodes(prevBlockHash) + if err != nil { + return "", err + } + + prevVoteRoundLastBlock, err := c.getPrevRoundLastBlock(prevBlockHash) + if err != nil { + return "", err + } + + startTimestamp := prevVoteRoundLastBlock.Timestamp + consensus.BlockTimeInterval + order := getBlockerOrder(startTimestamp, timeStamp, uint64(len(consensusNodeMap))) + for xPub, consensusNode := range consensusNodeMap { + if consensusNode.Order == order { + return xPub, nil + } + } + + // impossible occur + return "", errors.New("can not find blocker by given timestamp") +} + +// GetConsensusResultByHash return vote result by block hash +func (c *Chain) GetConsensusResultByHash(blockHash *bc.Hash) (*state.ConsensusResult, error) { + blockHeader, err := c.store.GetBlockHeader(blockHash) + if err != nil { + return nil, err + } + return c.getConsensusResult(state.CalcVoteSeq(blockHeader.Height), blockHeader) +} diff --git a/protocol/protocol.go b/protocol/protocol.go index 80cb53a1..38cab475 100644 --- a/protocol/protocol.go +++ b/protocol/protocol.go @@ -143,10 +143,9 @@ func (c *Chain) InMainChain(hash bc.Hash) bool { func (c *Chain) traceLongestChainTail(blockHeader *types.BlockHeader) (*types.BlockHeader, error) { longestTail, workQueue := blockHeader, []*types.BlockHeader{blockHeader} - for len(workQueue) > 0 { + for ; len(workQueue) > 0; workQueue = workQueue[1:] { currentHeader := workQueue[0] currentHash := currentHeader.Hash() - workQueue = workQueue[1:] hashes, err := c.store.GetBlockHashesByHeight(currentHeader.Height + 1) if err != nil { return nil, err diff --git a/protocol/validation/test/tx_ugly_test.go b/protocol/validation/test/tx_ugly_test.go index 72434500..5d9a210b 100644 --- a/protocol/validation/test/tx_ugly_test.go +++ b/protocol/validation/test/tx_ugly_test.go @@ -560,7 +560,7 @@ func TestValidateUglyTx(t *testing.T) { }, }, err: true, - gasValid: true, + gasValid: false, }, { category: "double spend", @@ -587,7 +587,7 @@ func TestValidateUglyTx(t *testing.T) { }, }, err: true, - gasValid: true, + gasValid: false, }, } diff --git a/protocol/validation/tx.go b/protocol/validation/tx.go index 6d248e54..8a5bf14f 100644 --- a/protocol/validation/tx.go +++ b/protocol/validation/tx.go @@ -15,7 +15,6 @@ import ( const ( validateWorkerNum = 32 - ruleAA = 142500 ) // validate transaction error @@ -265,16 +264,31 @@ func checkValid(vs *validationState, e bc.Entry) (err error) { return errors.New("incorrect asset_id while checking CrossChainInput") } - code := config.FederationWScript(config.CommonConfig) prog := &bc.Program{ VmVersion: e.ControlProgram.VmVersion, - Code: code, + Code: config.FederationWScript(config.CommonConfig), } if _, err := vm.Verify(NewTxVMContext(vs, e, prog, e.WitnessArguments), consensus.DefaultGasCredit); err != nil { return errors.Wrap(err, "checking cross-chain input control program") } + eq, err := mainchainOutput.Source.Value.Equal(e.WitnessDestination.Value) + if err != nil { + return err + } + + if !eq { + return errors.WithDetailf( + ErrMismatchedValue, + "previous output is for %d unit(s) of %x, spend wants %d unit(s) of %x", + mainchainOutput.Source.Value.Amount, + mainchainOutput.Source.Value.AssetId.Bytes(), + e.WitnessDestination.Value.Amount, + e.WitnessDestination.Value.AssetId.Bytes(), + ) + } + vs2 := *vs vs2.destPos = 0 if err = checkValidDest(&vs2, e.WitnessDestination); err != nil { @@ -329,6 +343,7 @@ func checkValid(vs *validationState, e bc.Entry) (err error) { if err != nil { return errors.Wrap(err, "getting vetoInput prevout") } + if len(voteOutput.Vote) != 64 { return ErrVotePubKey } @@ -538,7 +553,7 @@ func checkValidDest(vs *validationState, vd *bc.ValueDestination) error { func checkInputID(tx *bc.Tx, blockHeight uint64) error { for _, id := range tx.InputIDs { - if blockHeight >= ruleAA && id.IsZero() { + if id.IsZero() { return ErrEmptyInputIDs } } @@ -589,26 +604,29 @@ type validateTxWork struct { block *bc.Block } -type validateTxResult struct { +// ValidateTxResult is the result of async tx validate +type ValidateTxResult struct { i int gasStatus *GasState err error } -func (r *validateTxResult) GetGasState() *GasState { +// GetGasState return the gasStatus +func (r *ValidateTxResult) GetGasState() *GasState { return r.gasStatus } -func (r *validateTxResult) GetError() error { +// GetError return the err +func (r *ValidateTxResult) GetError() error { return r.err } -func validateTxWorker(workCh chan *validateTxWork, resultCh chan *validateTxResult, closeCh chan struct{}, wg *sync.WaitGroup) { +func validateTxWorker(workCh chan *validateTxWork, resultCh chan *ValidateTxResult, closeCh chan struct{}, wg *sync.WaitGroup) { for { select { case work := <-workCh: gasStatus, err := ValidateTx(work.tx, work.block) - resultCh <- &validateTxResult{i: work.i, gasStatus: gasStatus, err: err} + resultCh <- &ValidateTxResult{i: work.i, gasStatus: gasStatus, err: err} case <-closeCh: wg.Done() return @@ -616,12 +634,13 @@ func validateTxWorker(workCh chan *validateTxWork, resultCh chan *validateTxResu } } -func ValidateTxs(txs []*bc.Tx, block *bc.Block) []*validateTxResult { +// ValidateTxs validates txs in async mode +func ValidateTxs(txs []*bc.Tx, block *bc.Block) []*ValidateTxResult { txSize := len(txs) //init the goroutine validate worker var wg sync.WaitGroup workCh := make(chan *validateTxWork, txSize) - resultCh := make(chan *validateTxResult, txSize) + resultCh := make(chan *ValidateTxResult, txSize) closeCh := make(chan struct{}) for i := 0; i <= validateWorkerNum && i < txSize; i++ { wg.Add(1) @@ -634,7 +653,7 @@ func ValidateTxs(txs []*bc.Tx, block *bc.Block) []*validateTxResult { } //collect validate results - results := make([]*validateTxResult, txSize) + results := make([]*ValidateTxResult, txSize) for i := 0; i < txSize; i++ { result := <-resultCh results[result.i] = result diff --git a/protocol/validation/tx_test.go b/protocol/validation/tx_test.go index 4fd3a66a..c87f3945 100644 --- a/protocol/validation/tx_test.go +++ b/protocol/validation/tx_test.go @@ -231,8 +231,8 @@ func TestOverflow(t *testing.T) { txInputs := make([]*types.TxInput, 0, len(inputs)) txOutputs := make([]*types.TxOutput, 0, len(outputs)) - for _, amount := range inputs { - txInput := types.NewSpendInput(nil, *sourceID, *consensus.BTMAssetID, amount, 0, ctrlProgram) + for i, amount := range inputs { + txInput := types.NewSpendInput(nil, *sourceID, *consensus.BTMAssetID, amount, uint64(i), ctrlProgram) txInputs = append(txInputs, txInput) } @@ -712,8 +712,8 @@ func TestCoinbase(t *testing.T) { types.MapTx(&types.TxData{ SerializedSize: 1, Inputs: []*types.TxInput{ - types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp), types.NewCoinbaseInput(nil), + types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp), }, Outputs: []*types.TxOutput{ types.NewIntraChainOutput(*consensus.BTMAssetID, 888, cp), -- 2.11.0