X-Git-Url: http://git.osdn.net/view?p=bytom%2Fvapor.git;a=blobdiff_plain;f=protocol%2Fblock.go;h=ab690bac12f28c4d416d3c2512c55b5d757352df;hp=8d87804234cb4f59996b851ab3b5d222de5c2a43;hb=HEAD;hpb=eea317c609eee0bb184a6cdfdda39d9f336f92c4 diff --git a/protocol/block.go b/protocol/block.go index 8d878042..ab690bac 100644 --- a/protocol/block.go +++ b/protocol/block.go @@ -3,13 +3,11 @@ package protocol import ( log "github.com/sirupsen/logrus" - "github.com/vapor/config" - "github.com/vapor/errors" - "github.com/vapor/event" - "github.com/vapor/protocol/bc" - "github.com/vapor/protocol/bc/types" - "github.com/vapor/protocol/state" - "github.com/vapor/protocol/validation" + "github.com/bytom/vapor/errors" + "github.com/bytom/vapor/protocol/bc" + "github.com/bytom/vapor/protocol/bc/types" + "github.com/bytom/vapor/protocol/state" + "github.com/bytom/vapor/protocol/validation" ) var ( @@ -17,13 +15,15 @@ var ( ErrBadBlock = errors.New("invalid block") // ErrBadStateRoot is returned when the computed assets merkle root // disagrees with the one declared in a block header. - ErrBadStateRoot = errors.New("invalid state merkle root") - errBelowIrreversibleBlock = errors.New("the height of block below the height of irreversible block") + ErrBadStateRoot = errors.New("invalid state merkle root") ) // BlockExist check is a block in chain or orphan func (c *Chain) BlockExist(hash *bc.Hash) bool { - return c.index.BlockExist(hash) || c.orphanManage.BlockExist(hash) + if _, err := c.store.GetBlockHeader(hash); err == nil { + return true + } + return c.orphanManage.BlockExist(hash) } // GetBlockByHash return a block by given hash @@ -31,53 +31,62 @@ func (c *Chain) GetBlockByHash(hash *bc.Hash) (*types.Block, error) { return c.store.GetBlock(hash) } -// GetBlockByHeight return a block header by given height +// GetBlockByHeight return a block by given height func (c *Chain) GetBlockByHeight(height uint64) (*types.Block, error) { - node := c.index.NodeByHeight(height) - if node == nil { - return nil, errors.New("can't find block in given height") + hash, err := c.store.GetMainChainHash(height) + if err != nil { + return nil, errors.Wrap(err, "can't find block in given height") } - return c.store.GetBlock(&node.Hash) + return c.store.GetBlock(hash) } // GetHeaderByHash return a block header by given hash func (c *Chain) GetHeaderByHash(hash *bc.Hash) (*types.BlockHeader, error) { - node := c.index.GetNode(hash) - if node == nil { - return nil, errors.New("can't find block header in given hash") - } - return node.BlockHeader(), nil + return c.store.GetBlockHeader(hash) } // GetHeaderByHeight return a block header by given height func (c *Chain) GetHeaderByHeight(height uint64) (*types.BlockHeader, error) { - node := c.index.NodeByHeight(height) - if node == nil { - return nil, errors.New("can't find block header in given height") + hash, err := c.store.GetMainChainHash(height) + if err != nil { + return nil, errors.Wrap(err, "can't find block header in given height") } - return node.BlockHeader(), nil + return c.store.GetBlockHeader(hash) } -func (c *Chain) calcReorganizeNodes(node *state.BlockNode) ([]*state.BlockNode, []*state.BlockNode) { - var attachNodes []*state.BlockNode - var detachNodes []*state.BlockNode +func (c *Chain) calcReorganizeChain(beginAttach *types.BlockHeader, beginDetach *types.BlockHeader) ([]*types.BlockHeader, []*types.BlockHeader, error) { + var err error + var attachBlockHeaders []*types.BlockHeader + var detachBlockHeaders []*types.BlockHeader - attachNode := node - for c.index.NodeByHeight(attachNode.Height) != attachNode { - attachNodes = append([]*state.BlockNode{attachNode}, attachNodes...) - attachNode = attachNode.Parent - } + for attachBlockHeader, detachBlockHeader := beginAttach, beginDetach; detachBlockHeader.Hash() != attachBlockHeader.Hash(); { + var attachRollback, detachRollBack bool + if attachRollback = attachBlockHeader.Height >= detachBlockHeader.Height; attachRollback { + attachBlockHeaders = append([]*types.BlockHeader{attachBlockHeader}, attachBlockHeaders...) + } + + if detachRollBack = attachBlockHeader.Height <= detachBlockHeader.Height; detachRollBack { + detachBlockHeaders = append(detachBlockHeaders, detachBlockHeader) + } + + if attachRollback { + attachBlockHeader, err = c.store.GetBlockHeader(&attachBlockHeader.PreviousBlockHash) + if err != nil { + return nil, nil, err + } + } - detachNode := c.bestNode - for detachNode != attachNode { - detachNodes = append(detachNodes, detachNode) - detachNode = detachNode.Parent + if detachRollBack { + detachBlockHeader, err = c.store.GetBlockHeader(&detachBlockHeader.PreviousBlockHash) + if err != nil { + return nil, nil, err + } + } } - return attachNodes, detachNodes + return attachBlockHeaders, detachBlockHeaders, nil } func (c *Chain) connectBlock(block *types.Block) (err error) { - irreversibleNode := c.bestIrreversibleNode bcBlock := types.MapBlock(block) if bcBlock.TransactionStatus, err = c.store.GetTransactionStatus(&bcBlock.ID); err != nil { return err @@ -91,20 +100,35 @@ func (c *Chain) connectBlock(block *types.Block) (err error) { return err } - voteResult, err := c.consensusNodeManager.getBestVoteResult() + consensusResult, err := c.getBestConsensusResult() if err != nil { return err } - if err := voteResult.ApplyBlock(block); err != nil { + + if err := consensusResult.ApplyBlock(block); err != nil { return err } - node := c.index.GetNode(&bcBlock.ID) - if c.isIrreversible(node) && block.Height > irreversibleNode.Height { - irreversibleNode = node + for _, p := range c.subProtocols { + if err := c.syncProtocolStatus(p); err != nil { + return errors.Wrap(err, p.Name(), "sync sub protocol status") + } + + if err := p.ApplyBlock(block); err != nil { + return errors.Wrap(err, p.Name(), "sub protocol connect block") + } } - if err := c.setState(node, irreversibleNode, utxoView, []*state.VoteResult{voteResult}); err != nil { + if err := c.applyBlockSign(&block.BlockHeader); err != nil { + return err + } + + irrBlockHeader := c.lastIrrBlockHeader + if c.isIrreversible(&block.BlockHeader) && block.Height > irrBlockHeader.Height { + irrBlockHeader = &block.BlockHeader + } + + if err := c.setState(&block.BlockHeader, irrBlockHeader, []*types.BlockHeader{&block.BlockHeader}, utxoView, []*state.ConsensusResult{consensusResult}); err != nil { return err } @@ -114,45 +138,158 @@ func (c *Chain) connectBlock(block *types.Block) (err error) { return nil } -func (c *Chain) reorganizeChain(node *state.BlockNode) error { - attachNodes, detachNodes := c.calcReorganizeNodes(node) +func (c *Chain) detachBlock(detachBlockHeader *types.BlockHeader, consensusResult *state.ConsensusResult, utxoView *state.UtxoViewpoint) (*types.Block, error) { + detachHash := detachBlockHeader.Hash() + block, err := c.store.GetBlock(&detachHash) + if err != nil { + return block, err + } + + detachBlock := types.MapBlock(block) + if err := consensusResult.DetachBlock(block); err != nil { + return block, err + } + + if err := c.store.GetTransactionsUtxo(utxoView, detachBlock.Transactions); err != nil { + return block, err + } + + txStatus, err := c.GetTransactionStatus(&detachBlock.ID) + if err != nil { + return block, err + } + + if err := utxoView.DetachBlock(detachBlock, txStatus); err != nil { + return block, err + } + + for _, p := range c.subProtocols { + if err := p.DetachBlock(block); err != nil { + return block, errors.Wrap(err, p.Name(), "sub protocol detach block") + } + } + + log.WithFields(log.Fields{"module": logModule, "height": detachBlockHeader.Height, "hash": detachHash.String()}).Debug("detach from mainchain") + return block, nil +} + +func (c *Chain) syncSubProtocols() error { + for _, p := range c.subProtocols { + if err := c.syncProtocolStatus(p); err != nil { + return errors.Wrap(err, p.Name(), "sync sub protocol status") + } + } + return nil +} + +// Rollback rollback the chain from one blockHeight to targetBlockHeight +// WARNING: we recommend to use this only in commond line +func (c *Chain) Rollback(targetHeight uint64) error { + c.cond.L.Lock() + defer c.cond.L.Unlock() + utxoView := state.NewUtxoViewpoint() - voteResults := []*state.VoteResult{} - irreversibleNode := c.bestIrreversibleNode - voteResult, err := c.consensusNodeManager.getBestVoteResult() + consensusResult, err := c.getBestConsensusResult() if err != nil { return err } - for _, detachNode := range detachNodes { - b, err := c.store.GetBlock(&detachNode.Hash) + if err = c.syncSubProtocols(); err != nil { + return err + } + + targetBlockHeader, err := c.GetHeaderByHeight(targetHeight) + if err != nil { + return err + } + + _, deletedBlockHeaders, err := c.calcReorganizeChain(targetBlockHeader, c.bestBlockHeader) + if err != nil { + return err + } + + deletedBlocks := []*types.Block{} + for _, deletedBlockHeader := range deletedBlockHeaders { + block, err := c.detachBlock(deletedBlockHeader, consensusResult, utxoView) if err != nil { return err } - detachBlock := types.MapBlock(b) - if err := c.store.GetTransactionsUtxo(utxoView, detachBlock.Transactions); err != nil { + deletedBlocks = append(deletedBlocks, block) + } + + setIrrBlockHeader := c.lastIrrBlockHeader + if c.lastIrrBlockHeader.Height > targetBlockHeader.Height { + setIrrBlockHeader = targetBlockHeader + } + + startSeq := state.CalcVoteSeq(c.bestBlockHeader.Height) + if err = c.setState(targetBlockHeader, setIrrBlockHeader, nil, utxoView, []*state.ConsensusResult{consensusResult.Fork()}); err != nil { + return err + } + + for _, block := range deletedBlocks { + hashes, err := c.store.GetBlockHashesByHeight(block.Height) + if err != nil{ return err } - txStatus, err := c.GetTransactionStatus(&detachBlock.ID) - if err != nil { - return err + for _, hash := range hashes{ + block, err := c.store.GetBlock(hash) + if err != nil{ + return err + } + + if err := c.store.DeleteBlock(block); err != nil{ + return err + } } + } - if err := utxoView.DetachBlock(detachBlock, txStatus); err != nil { + endSeq := state.CalcVoteSeq(targetHeight) + for nowSeq := startSeq; nowSeq > endSeq; nowSeq-- { + if err := c.store.DeleteConsensusResult(nowSeq); err != nil { return err } + } + + return nil +} + +func (c *Chain) reorganizeChain(blockHeader *types.BlockHeader) error { + attachBlockHeaders, detachBlockHeaders, err := c.calcReorganizeChain(blockHeader, c.bestBlockHeader) + if err != nil { + return err + } - if err := voteResult.DetachBlock(b); err != nil { + utxoView := state.NewUtxoViewpoint() + consensusResults := []*state.ConsensusResult{} + consensusResult, err := c.getBestConsensusResult() + if err != nil { + return err + } + + if err = c.syncSubProtocols(); err != nil { + return err + } + + txsToRestore := map[bc.Hash]*types.Tx{} + for _, detachBlockHeader := range detachBlockHeaders { + b, err := c.detachBlock(detachBlockHeader, consensusResult, utxoView) + if err != nil { return err } - log.WithFields(log.Fields{"module": logModule, "height": node.Height, "hash": node.Hash.String()}).Debug("detach from mainchain") + for _, tx := range b.Transactions { + txsToRestore[tx.ID] = tx + } } - for _, attachNode := range attachNodes { - b, err := c.store.GetBlock(&attachNode.Hash) + txsToRemove := map[bc.Hash]*types.Tx{} + irrBlockHeader := c.lastIrrBlockHeader + for _, attachBlockHeader := range attachBlockHeaders { + attachHash := attachBlockHeader.Hash() + b, err := c.store.GetBlock(&attachHash) if err != nil { return err } @@ -171,26 +308,67 @@ func (c *Chain) reorganizeChain(node *state.BlockNode) error { return err } - if err := voteResult.ApplyBlock(b); err != nil { + if err := consensusResult.ApplyBlock(b); err != nil { + return err + } + + for _, p := range c.subProtocols { + if err := p.ApplyBlock(b); err != nil { + return errors.Wrap(err, p.Name(), "sub protocol attach block") + } + } + + if consensusResult.IsFinalize() { + consensusResults = append(consensusResults, consensusResult.Fork()) + } + + if err := c.applyBlockSign(attachBlockHeader); err != nil { return err } - if voteResult.IsFinalize() { - voteResults = append(voteResults, voteResult.Fork()) + if c.isIrreversible(attachBlockHeader) && attachBlockHeader.Height > irrBlockHeader.Height { + irrBlockHeader = attachBlockHeader } - if c.isIrreversible(attachNode) && attachNode.Height > irreversibleNode.Height { - irreversibleNode = attachNode + for _, tx := range b.Transactions { + if _, ok := txsToRestore[tx.ID]; !ok { + txsToRemove[tx.ID] = tx + } else { + delete(txsToRestore, tx.ID) + } } - log.WithFields(log.Fields{"module": logModule, "height": node.Height, "hash": node.Hash.String()}).Debug("attach from mainchain") + blockHash := blockHeader.Hash() + log.WithFields(log.Fields{"module": logModule, "height": blockHeader.Height, "hash": blockHash.String()}).Debug("attach from mainchain") } - if detachNodes[len(detachNodes)-1].Height <= c.bestIrreversibleNode.Height && irreversibleNode.Height <= c.bestIrreversibleNode.Height { - return errors.New("rollback block below the height of irreversible block") + if len(detachBlockHeaders) > 0 && detachBlockHeaders[len(detachBlockHeaders)-1].Height <= c.lastIrrBlockHeader.Height && irrBlockHeader.Height <= c.lastIrrBlockHeader.Height { + log.WithField("module", logModule).Warn("rollback block below the height of irreversible block") + return nil } - voteResults = append(voteResults, voteResult.Fork()) - return c.setState(node, irreversibleNode, utxoView, voteResults) + + consensusResults = append(consensusResults, consensusResult.Fork()) + if err := c.setState(blockHeader, irrBlockHeader, attachBlockHeaders, utxoView, consensusResults); err != nil { + return err + } + + for txHash := range txsToRemove { + c.txPool.RemoveTransaction(&txHash) + } + + for _, tx := range txsToRestore { + // the number of restored Tx should be very small or most of time ZERO + // Error returned from validation is ignored, tx could still be lost if validation fails. + // TODO: adjust tx timestamp so that it won't starve in pool. + if _, err := c.validateTx(tx, blockHeader); err != nil { + log.WithFields(log.Fields{"module": logModule, "tx_id": tx.Tx.ID.String(), "error": err}).Info("restore tx fail") + } + } + + if len(txsToRestore) > 0 { + log.WithFields(log.Fields{"module": logModule, "num": len(txsToRestore)}).Debug("restore txs back to pool") + } + return nil } // SaveBlock will validate and save block into storage @@ -199,35 +377,37 @@ func (c *Chain) saveBlock(block *types.Block) error { return errors.Sub(ErrBadBlock, err) } - parent := c.index.GetNode(&block.PreviousBlockHash) - bcBlock := types.MapBlock(block) - if err := validation.ValidateBlock(bcBlock, parent); err != nil { - return errors.Sub(ErrBadBlock, err) - } - - signature, err := c.SignBlock(block) + parent, err := c.store.GetBlockHeader(&block.PreviousBlockHash) if err != nil { - return errors.Sub(ErrBadBlock, err) + return err } - if err := c.store.SaveBlock(block, bcBlock.TransactionStatus); err != nil { + consensusResult, err := c.GetConsensusResultByHash(&block.PreviousBlockHash) + if err != nil { return err } - c.orphanManage.Delete(&bcBlock.ID) - node, err := state.NewBlockNode(&block.BlockHeader, parent) + rewards, err := consensusResult.GetCoinbaseRewards(parent.Height) if err != nil { return err } - c.index.AddNode(node) + bcBlock := types.MapBlock(block) + if err := validation.ValidateBlock(bcBlock, parent, rewards); err != nil { + return errors.Sub(ErrBadBlock, err) + } - if len(signature) != 0 { - xPub := config.CommonConfig.PrivateKey().XPub() - if err := c.eventDispatcher.Post(event.BlockSignatureEvent{BlockHash: block.Hash(), Signature: signature, XPub: xPub[:]}); err != nil { - return err + for _, p := range c.subProtocols { + if err := p.ValidateBlock(block, bcBlock.TransactionStatus.GetVerifyStatus()); err != nil { + return errors.Wrap(err, "sub protocol save block") } } + + if err := c.store.SaveBlock(block, bcBlock.TransactionStatus); err != nil { + return err + } + + c.orphanManage.Delete(&bcBlock.ID) return nil } @@ -286,11 +466,13 @@ func (c *Chain) blockProcesser() { func (c *Chain) processBlock(block *types.Block) (bool, error) { blockHash := block.Hash() if c.BlockExist(&blockHash) { - log.WithFields(log.Fields{"module": logModule, "hash": blockHash.String(), "height": block.Height}).Info("block has been processed") + log.WithFields(log.Fields{"module": logModule, "hash": blockHash.String(), "height": block.Height}).Debug("block has been processed") return c.orphanManage.BlockExist(&blockHash), nil } - if parent := c.index.GetNode(&block.PreviousBlockHash); parent == nil { + c.markTransactions(block.Transactions...) + + if _, err := c.store.GetBlockHeader(&block.PreviousBlockHash); err != nil { c.orphanManage.Add(block) return true, nil } @@ -300,19 +482,18 @@ func (c *Chain) processBlock(block *types.Block) (bool, error) { } bestBlock := c.saveSubBlock(block) - bestBlockHash := bestBlock.Hash() - bestNode := c.index.GetNode(&bestBlockHash) + bestBlockHeader := &bestBlock.BlockHeader c.cond.L.Lock() defer c.cond.L.Unlock() - if bestNode.Parent == c.bestNode { + if bestBlockHeader.PreviousBlockHash == c.bestBlockHeader.Hash() { log.WithFields(log.Fields{"module": logModule}).Debug("append block to the end of mainchain") return false, c.connectBlock(bestBlock) } - if bestNode.Height > c.bestNode.Height { + if bestBlockHeader.Height > c.bestBlockHeader.Height { log.WithFields(log.Fields{"module": logModule}).Debug("start to reorganize chain") - return false, c.reorganizeChain(bestNode) + return false, c.reorganizeChain(bestBlockHeader) } return false, nil }