4 log "github.com/sirupsen/logrus"
6 "github.com/bytom/bytom/errors"
7 "github.com/bytom/bytom/protocol/bc"
8 "github.com/bytom/bytom/protocol/bc/types"
9 "github.com/bytom/bytom/protocol/state"
10 "github.com/bytom/bytom/protocol/validation"
14 // ErrBadBlock is returned when a block is invalid.
15 ErrBadBlock = errors.New("invalid block")
16 // ErrBadStateRoot is returned when the computed assets merkle root
17 // disagrees with the one declared in a block header.
18 ErrBadStateRoot = errors.New("invalid state merkle root")
21 // BlockExist check is a block in chain or orphan
22 func (c *Chain) BlockExist(hash *bc.Hash) bool {
23 if _, err := c.store.GetBlockHeader(hash); err == nil {
27 return c.orphanManage.BlockExist(hash)
30 // GetBlockByHash return a block by given hash
31 func (c *Chain) GetBlockByHash(hash *bc.Hash) (*types.Block, error) {
32 return c.store.GetBlock(hash)
35 // GetBlockByHeight return a block header by given height
36 func (c *Chain) GetBlockByHeight(height uint64) (*types.Block, error) {
37 hash, err := c.store.GetMainChainHash(height)
39 return nil, errors.Wrap(err, "can't find block in given height")
42 return c.store.GetBlock(hash)
45 // GetHeaderByHash return a block header by given hash
46 func (c *Chain) GetHeaderByHash(hash *bc.Hash) (*types.BlockHeader, error) {
47 return c.store.GetBlockHeader(hash)
50 // GetHeaderByHeight return a block header by given height
51 func (c *Chain) GetHeaderByHeight(height uint64) (*types.BlockHeader, error) {
52 hash, err := c.store.GetMainChainHash(height)
54 return nil, errors.Wrap(err, "can't find block header in given height")
57 return c.store.GetBlockHeader(hash)
60 func (c *Chain) calcReorganizeChain(beginAttach *types.BlockHeader, beginDetach *types.BlockHeader) ([]*types.BlockHeader, []*types.BlockHeader, error) {
62 var attachBlockHeaders []*types.BlockHeader
63 var detachBlockHeaders []*types.BlockHeader
65 for attachBlockHeader, detachBlockHeader := beginAttach, beginDetach; detachBlockHeader.Hash() != attachBlockHeader.Hash(); {
66 var attachRollback, detachRollBack bool
67 if attachRollback = attachBlockHeader.Height >= detachBlockHeader.Height; attachRollback {
68 attachBlockHeaders = append([]*types.BlockHeader{attachBlockHeader}, attachBlockHeaders...)
71 if detachRollBack = attachBlockHeader.Height <= detachBlockHeader.Height; detachRollBack {
72 detachBlockHeaders = append(detachBlockHeaders, detachBlockHeader)
76 attachBlockHeader, err = c.store.GetBlockHeader(&attachBlockHeader.PreviousBlockHash)
83 detachBlockHeader, err = c.store.GetBlockHeader(&detachBlockHeader.PreviousBlockHash)
89 return attachBlockHeaders, detachBlockHeaders, nil
92 func (c *Chain) reorganizeChain(blockHeader *types.BlockHeader) error {
93 attachNodes, detachNodes, err := c.calcReorganizeChain(blockHeader, c.bestBlockHeader)
98 utxoView := state.NewUtxoViewpoint()
99 contractView := state.NewContractViewpoint()
100 txsToRestore := map[bc.Hash]*types.Tx{}
101 for _, detachNode := range detachNodes {
102 hash := detachNode.Hash()
103 b, err := c.store.GetBlock(&hash)
108 detachBlock := types.MapBlock(b)
109 if err := c.store.GetTransactionsUtxo(utxoView, detachBlock.Transactions); err != nil {
113 if err := utxoView.DetachBlock(detachBlock); err != nil {
117 if err := contractView.DetachBlock(b); err != nil {
121 for _, tx := range b.Transactions[1:] {
122 txsToRestore[tx.ID] = tx
124 log.WithFields(log.Fields{"module": logModule, "height": detachNode.Height, "hash": hash.String()}).Debug("detach from mainchain")
127 txsToRemove := map[bc.Hash]*types.Tx{}
128 for _, attachNode := range attachNodes {
129 hash := attachNode.Hash()
130 b, err := c.store.GetBlock(&hash)
135 attachBlock := types.MapBlock(b)
136 if err := c.store.GetTransactionsUtxo(utxoView, attachBlock.Transactions); err != nil {
140 if err := utxoView.ApplyBlock(attachBlock); err != nil {
144 if err := contractView.ApplyBlock(b); err != nil {
148 for _, tx := range b.Transactions[1:] {
149 if _, ok := txsToRestore[tx.ID]; !ok {
150 txsToRemove[tx.ID] = tx
152 delete(txsToRestore, tx.ID)
156 log.WithFields(log.Fields{"module": logModule, "height": attachNode.Height, "hash": hash.String()}).Debug("attach from mainchain")
159 if err := c.setState(blockHeader, attachNodes, utxoView, contractView); err != nil {
163 for txHash := range txsToRemove {
164 c.txPool.RemoveTransaction(&txHash)
167 for _, tx := range txsToRestore {
168 // the number of restored Tx should be very small or most of time ZERO
169 // Error returned from validation is ignored, tx could still be lost if validation fails.
170 // TODO: adjust tx timestamp so that it won't starve in pool.
171 if _, err := c.ValidateTx(tx); err != nil {
172 log.WithFields(log.Fields{"module": logModule, "tx_id": tx.Tx.ID.String(), "error": err}).Info("restore tx fail")
176 if len(txsToRestore) > 0 {
177 log.WithFields(log.Fields{"module": logModule, "num": len(txsToRestore)}).Debug("restore txs back to pool")
183 // SaveBlock will validate and save block into storage
184 func (c *Chain) saveBlock(block *types.Block) error {
185 parent, err := c.store.GetBlockHeader(&block.PreviousBlockHash)
190 checkpoint, err := c.PrevCheckpointByPrevHash(&block.PreviousBlockHash)
195 if err := validation.ValidateBlock(block, parent, checkpoint, c.ProgramConverter); err != nil {
196 return errors.Sub(ErrBadBlock, err)
199 if _, err := c.casper.ApplyBlock(block); err != nil {
203 if err := c.store.SaveBlock(block); err != nil {
207 blockHash := block.Hash()
208 c.orphanManage.Delete(&blockHash)
212 func (c *Chain) saveSubBlock(block *types.Block) {
213 blockHash := block.Hash()
214 prevOrphans, ok := c.orphanManage.GetPrevOrphans(&blockHash)
219 for _, prevOrphan := range prevOrphans {
220 orphanBlock, ok := c.orphanManage.Get(prevOrphan)
222 log.WithFields(log.Fields{"module": logModule, "hash": prevOrphan.String()}).Warning("saveSubBlock fail to get block from orphanManage")
225 if err := c.saveBlock(orphanBlock); err != nil {
226 log.WithFields(log.Fields{"module": logModule, "hash": prevOrphan.String(), "height": orphanBlock.Height}).Warning("saveSubBlock fail to save block")
230 c.saveSubBlock(orphanBlock)
234 type processBlockResponse struct {
239 type processBlockMsg struct {
241 reply chan processBlockResponse
244 // ProcessBlock is the entry for chain update
245 func (c *Chain) ProcessBlock(block *types.Block) (bool, error) {
246 reply := make(chan processBlockResponse, 1)
247 c.processBlockCh <- &processBlockMsg{block: block, reply: reply}
249 return response.isOrphan, response.err
252 func (c *Chain) blockProcessor() {
255 case msg := <-c.processBlockCh:
256 isOrphan, err := c.processBlock(msg.block)
257 msg.reply <- processBlockResponse{isOrphan: isOrphan, err: err}
258 case msg := <-c.casper.RollbackCh():
259 msg.Reply <- c.tryReorganize(msg.BestHash)
264 // ProcessBlock is the entry for handle block insert
265 func (c *Chain) processBlock(block *types.Block) (bool, error) {
266 blockHash := block.Hash()
267 if c.BlockExist(&blockHash) && c.bestBlockHeader.Height >= block.Height {
268 log.WithFields(log.Fields{"module": logModule, "hash": blockHash.String(), "height": block.Height}).Info("block has been processed")
269 return c.orphanManage.BlockExist(&blockHash), nil
272 if _, err := c.store.GetBlockHeader(&block.PreviousBlockHash); err != nil {
273 c.orphanManage.Add(block)
277 if err := c.saveBlock(block); err != nil {
281 c.saveSubBlock(block)
282 bestHash := c.casper.BestChain()
283 return false, c.tryReorganize(bestHash)
286 func (c *Chain) tryReorganize(bestHash bc.Hash) error {
287 if c.bestBlockHeader.Hash() == bestHash {
291 blockHeader, err := c.GetHeaderByHash(&bestHash)
296 log.WithFields(log.Fields{"module": logModule, "bestHash": bestHash.String()}).Info("start to reorganize chain")
297 return c.reorganizeChain(blockHeader)