OSDN Git Service

edit code while reviewing the code (#255)
authorPaladz <yzhu101@uottawa.ca>
Wed, 10 Jul 2019 07:48:10 +0000 (15:48 +0800)
committerGitHub <noreply@github.com>
Wed, 10 Jul 2019 07:48:10 +0000 (15:48 +0800)
* edit code while reviewing the code

* change the if err statement

* clean the db part code

* delete the unused reqid code

13 files changed:
api/transact.go
blockchain/rpc/rpc.go
database/store.go
database/utxo_view.go
database/utxo_view_test.go
net/http/reqid/reqid.go [deleted file]
proposal/blockproposer/blockproposer.go
protocol/bbft.go
protocol/consensus_node_manager.go
protocol/protocol.go
protocol/validation/test/tx_ugly_test.go
protocol/validation/tx.go
protocol/validation/tx_test.go

index 76e6d53..67d4429 100644 (file)
@@ -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)
        }
index 9875ec5..0710c93 100644 (file)
@@ -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)
index 94f0918..6abd41f 100644 (file)
@@ -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 {
index f05dafd..987a23d 100644 (file)
@@ -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
                }
index b9b2663..0d6bc82 100644 (file)
@@ -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 (file)
index f850269..0000000
+++ /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) {
-       })
-}
index 32105db..0b568cf 100644 (file)
@@ -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
                }
 
index a34e703..70b1d81 100644 (file)
@@ -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
-}
index 961a35d..930e42d 100644 (file)
@@ -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)
+}
index 80cb53a..38cab47 100644 (file)
@@ -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
index 7243450..5d9a210 100644 (file)
@@ -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,
                },
        }
 
index 6d248e5..8a5bf14 100644 (file)
@@ -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
index 4fd3a66..c87f394 100644 (file)
@@ -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),