X-Git-Url: http://git.osdn.net/view?p=bytom%2Fvapor.git;a=blobdiff_plain;f=protocol%2Fprotocol.go;h=56c85fb4ab4c56a71ab558cf3bb001187429aa86;hp=f3f4fc262b4f6fb7e0bdf3f843c64268447931b8;hb=1337be95f74a1d2a1a7316737efde413f29bcb2f;hpb=04e99309457c70c11b0538e38b6a0b68d5b64a87 diff --git a/protocol/protocol.go b/protocol/protocol.go index f3f4fc26..56c85fb4 100644 --- a/protocol/protocol.go +++ b/protocol/protocol.go @@ -5,36 +5,47 @@ import ( log "github.com/sirupsen/logrus" - "github.com/vapor/config" - "github.com/vapor/event" - "github.com/vapor/protocol/bc" - "github.com/vapor/protocol/bc/types" - "github.com/vapor/protocol/state" + "github.com/bytom/vapor/common" + "github.com/bytom/vapor/config" + "github.com/bytom/vapor/event" + "github.com/bytom/vapor/protocol/bc" + "github.com/bytom/vapor/protocol/bc/types" + "github.com/bytom/vapor/protocol/state" ) -const maxProcessBlockChSize = 1024 +const ( + maxProcessBlockChSize = 1024 + maxKnownTxs = 32768 // Maximum transactions hashes to keep in the known list (prevent DOS) +) // Chain provides functions for working with the Bytom block chain. type Chain struct { - index *state.BlockIndex orphanManage *OrphanManage txPool *TxPool store Store - bbft *bbft processBlockCh chan *processBlockMsg - cond sync.Cond - bestNode *state.BlockNode - bestIrreversibleNode *state.BlockNode + signatureCache *common.Cache + eventDispatcher *event.Dispatcher + + cond sync.Cond + bestBlockHeader *types.BlockHeader // the last block on current main chain + lastIrrBlockHeader *types.BlockHeader // the last irreversible block + + knownTxs *common.OrderedSet } // NewChain returns a new Chain using store as the underlying storage. func NewChain(store Store, txPool *TxPool, eventDispatcher *event.Dispatcher) (*Chain, error) { + knownTxs, _ := common.NewOrderedSet(maxKnownTxs) c := &Chain{ - orphanManage: NewOrphanManage(), - txPool: txPool, - store: store, - processBlockCh: make(chan *processBlockMsg, maxProcessBlockChSize), + orphanManage: NewOrphanManage(), + txPool: txPool, + store: store, + signatureCache: common.NewCache(maxSignatureCacheSize), + eventDispatcher: eventDispatcher, + processBlockCh: make(chan *processBlockMsg, maxProcessBlockChSize), + knownTxs: knownTxs, } c.cond.L = new(sync.Mutex) @@ -47,13 +58,15 @@ func NewChain(store Store, txPool *TxPool, eventDispatcher *event.Dispatcher) (* } var err error - if c.index, err = store.LoadBlockIndex(storeStatus.Height); err != nil { + c.bestBlockHeader, err = c.store.GetBlockHeader(storeStatus.Hash) + if err != nil { return nil, err } - c.bestNode = c.index.GetNode(storeStatus.Hash) - c.index.SetMainChain(c.bestNode) - c.bbft = newBbft(store, c.index, c.orphanManage, eventDispatcher) + c.lastIrrBlockHeader, err = c.store.GetBlockHeader(storeStatus.IrreversibleHash) + if err != nil { + return nil, err + } go c.blockProcesser() return c, nil } @@ -77,56 +90,109 @@ func (c *Chain) initChainStatus() error { return err } - node, err := state.NewBlockNode(&genesisBlock.BlockHeader, nil) - if err != nil { - return err - } - return c.store.SaveChainStatus(node, node, utxoView, map[uint64]*state.VoteResult{}) + consensusResults := []*state.ConsensusResult{&state.ConsensusResult{ + Seq: 0, + NumOfVote: make(map[string]uint64), + CoinbaseReward: make(map[string]uint64), + BlockHash: genesisBlock.Hash(), + BlockHeight: 0, + }} + + genesisBlockHeader := &genesisBlock.BlockHeader + return c.store.SaveChainStatus(genesisBlockHeader, genesisBlockHeader, []*types.BlockHeader{genesisBlockHeader}, utxoView, consensusResults) } // BestBlockHeight returns the current height of the blockchain. func (c *Chain) BestBlockHeight() uint64 { c.cond.L.Lock() defer c.cond.L.Unlock() - return c.bestNode.Height + return c.bestBlockHeader.Height } -// BestBlockHash return the hash of the chain tail block +// BestBlockHash return the hash of the main chain tail block func (c *Chain) BestBlockHash() *bc.Hash { c.cond.L.Lock() defer c.cond.L.Unlock() - return &c.bestNode.Hash + bestHash := c.bestBlockHeader.Hash() + return &bestHash +} + +// LastIrreversibleHeader returns the chain last irreversible block header +func (c *Chain) LastIrreversibleHeader() *types.BlockHeader { + c.cond.L.Lock() + defer c.cond.L.Unlock() + return c.lastIrrBlockHeader } -// BestBlockHeader returns the chain tail block +// BestBlockHeader returns the chain best block header func (c *Chain) BestBlockHeader() *types.BlockHeader { - node := c.index.BestNode() - return node.BlockHeader() + c.cond.L.Lock() + defer c.cond.L.Unlock() + return c.bestBlockHeader } // InMainChain checks wheather a block is in the main chain func (c *Chain) InMainChain(hash bc.Hash) bool { - return c.index.InMainchain(hash) + blockHeader, err := c.store.GetBlockHeader(&hash) + if err != nil { + return false + } + + blockHash, err := c.store.GetMainChainHash(blockHeader.Height) + if err != nil { + log.WithFields(log.Fields{"module": logModule, "height": blockHeader.Height}).Debug("not contain block hash in main chain for specified height") + return false + } + return *blockHash == hash } -// This function must be called with mu lock in above level -func (c *Chain) setState(node *state.BlockNode, irreversibleNode *state.BlockNode, view *state.UtxoViewpoint, voteMap map[uint64]*state.VoteResult) error { - if err := c.store.SaveChainStatus(node, irreversibleNode, view, voteMap); err != nil { - return err +// trace back to the tail of the chain from the given block header +func (c *Chain) traceLongestChainTail(blockHeader *types.BlockHeader) (*types.BlockHeader, error) { + longestTail, workQueue := blockHeader, []*types.BlockHeader{blockHeader} + + for ; len(workQueue) > 0; workQueue = workQueue[1:] { + currentHeader := workQueue[0] + currentHash := currentHeader.Hash() + hashes, err := c.store.GetBlockHashesByHeight(currentHeader.Height + 1) + if err != nil { + return nil, err + } + + for _, h := range hashes { + if header, err := c.store.GetBlockHeader(h); err != nil { + return nil, err + } else if header.PreviousBlockHash == currentHash { + if longestTail.Height < header.Height { + longestTail = header + } + workQueue = append(workQueue, header) + } + } } + return longestTail, nil +} - c.cond.L.Lock() - defer c.cond.L.Unlock() +func (c *Chain) hasSeenTx(tx *types.Tx) bool { + return c.knownTxs.Has(tx.ID.String()) +} - if err := c.bbft.UpdateConsensusNodes(node.Height); err != nil { +func (c *Chain) markTransactions(txs ...*types.Tx) { + for _, tx := range txs { + c.knownTxs.Add(tx.ID.String()) + } +} + +// This function must be called with mu lock in above level +func (c *Chain) setState(blockHeader, irrBlockHeader *types.BlockHeader, mainBlockHeaders []*types.BlockHeader, view *state.UtxoViewpoint, consensusResults []*state.ConsensusResult) error { + if err := c.store.SaveChainStatus(blockHeader, irrBlockHeader, mainBlockHeaders, view, consensusResults); err != nil { return err } - c.index.SetMainChain(node) - c.bestNode = node - c.bestIrreversibleNode = irreversibleNode + c.bestBlockHeader = blockHeader + c.lastIrrBlockHeader = irrBlockHeader - log.WithFields(log.Fields{"module": logModule, "height": c.bestNode.Height, "hash": c.bestNode.Hash.String()}).Debug("chain best status has been update") + blockHash := blockHeader.Hash() + log.WithFields(log.Fields{"module": logModule, "height": blockHeader.Height, "hash": blockHash.String()}).Debug("chain best status has been update") c.cond.Broadcast() return nil } @@ -137,7 +203,7 @@ func (c *Chain) BlockWaiter(height uint64) <-chan struct{} { go func() { c.cond.L.Lock() defer c.cond.L.Unlock() - for c.bestNode.Height < height { + for c.bestBlockHeader.Height < height { c.cond.Wait() } ch <- struct{}{}