github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/protocol/block.go (about)

     1  package protocol
     2  
     3  import (
     4  	log "github.com/sirupsen/logrus"
     5  
     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"
    11  )
    12  
    13  var (
    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")
    19  )
    20  
    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 {
    24  		return true
    25  	}
    26  
    27  	return c.orphanManage.BlockExist(hash)
    28  }
    29  
    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)
    33  }
    34  
    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)
    38  	if err != nil {
    39  		return nil, errors.Wrap(err, "can't find block in given height")
    40  	}
    41  
    42  	return c.store.GetBlock(hash)
    43  }
    44  
    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)
    48  }
    49  
    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)
    53  	if err != nil {
    54  		return nil, errors.Wrap(err, "can't find block header in given height")
    55  	}
    56  
    57  	return c.store.GetBlockHeader(hash)
    58  }
    59  
    60  func (c *Chain) calcReorganizeChain(beginAttach *types.BlockHeader, beginDetach *types.BlockHeader) ([]*types.BlockHeader, []*types.BlockHeader, error) {
    61  	var err error
    62  	var attachBlockHeaders []*types.BlockHeader
    63  	var detachBlockHeaders []*types.BlockHeader
    64  
    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...)
    69  		}
    70  
    71  		if detachRollBack = attachBlockHeader.Height <= detachBlockHeader.Height; detachRollBack {
    72  			detachBlockHeaders = append(detachBlockHeaders, detachBlockHeader)
    73  		}
    74  
    75  		if attachRollback {
    76  			attachBlockHeader, err = c.store.GetBlockHeader(&attachBlockHeader.PreviousBlockHash)
    77  			if err != nil {
    78  				return nil, nil, err
    79  			}
    80  		}
    81  
    82  		if detachRollBack {
    83  			detachBlockHeader, err = c.store.GetBlockHeader(&detachBlockHeader.PreviousBlockHash)
    84  			if err != nil {
    85  				return nil, nil, err
    86  			}
    87  		}
    88  	}
    89  	return attachBlockHeaders, detachBlockHeaders, nil
    90  }
    91  
    92  func (c *Chain) reorganizeChain(blockHeader *types.BlockHeader) error {
    93  	attachNodes, detachNodes, err := c.calcReorganizeChain(blockHeader, c.bestBlockHeader)
    94  	if err != nil {
    95  		return err
    96  	}
    97  
    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)
   104  		if err != nil {
   105  			return err
   106  		}
   107  
   108  		detachBlock := types.MapBlock(b)
   109  		if err := c.store.GetTransactionsUtxo(utxoView, detachBlock.Transactions); err != nil {
   110  			return err
   111  		}
   112  
   113  		if err := utxoView.DetachBlock(detachBlock); err != nil {
   114  			return err
   115  		}
   116  
   117  		if err := contractView.DetachBlock(b); err != nil {
   118  			return err
   119  		}
   120  
   121  		for _, tx := range b.Transactions[1:] {
   122  			txsToRestore[tx.ID] = tx
   123  		}
   124  		log.WithFields(log.Fields{"module": logModule, "height": detachNode.Height, "hash": hash.String()}).Debug("detach from mainchain")
   125  	}
   126  
   127  	txsToRemove := map[bc.Hash]*types.Tx{}
   128  	for _, attachNode := range attachNodes {
   129  		hash := attachNode.Hash()
   130  		b, err := c.store.GetBlock(&hash)
   131  		if err != nil {
   132  			return err
   133  		}
   134  
   135  		attachBlock := types.MapBlock(b)
   136  		if err := c.store.GetTransactionsUtxo(utxoView, attachBlock.Transactions); err != nil {
   137  			return err
   138  		}
   139  
   140  		if err := utxoView.ApplyBlock(attachBlock); err != nil {
   141  			return err
   142  		}
   143  
   144  		if err := contractView.ApplyBlock(b); err != nil {
   145  			return err
   146  		}
   147  
   148  		for _, tx := range b.Transactions[1:] {
   149  			if _, ok := txsToRestore[tx.ID]; !ok {
   150  				txsToRemove[tx.ID] = tx
   151  			} else {
   152  				delete(txsToRestore, tx.ID)
   153  			}
   154  		}
   155  
   156  		log.WithFields(log.Fields{"module": logModule, "height": attachNode.Height, "hash": hash.String()}).Debug("attach from mainchain")
   157  	}
   158  
   159  	if err := c.setState(blockHeader, attachNodes, utxoView, contractView); err != nil {
   160  		return err
   161  	}
   162  
   163  	for txHash := range txsToRemove {
   164  		c.txPool.RemoveTransaction(&txHash)
   165  	}
   166  
   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")
   173  		}
   174  	}
   175  
   176  	if len(txsToRestore) > 0 {
   177  		log.WithFields(log.Fields{"module": logModule, "num": len(txsToRestore)}).Debug("restore txs back to pool")
   178  	}
   179  
   180  	return nil
   181  }
   182  
   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)
   186  	if err != nil {
   187  		return err
   188  	}
   189  
   190  	checkpoint, err := c.PrevCheckpointByPrevHash(&block.PreviousBlockHash)
   191  	if err != nil {
   192  		return err
   193  	}
   194  
   195  	if err := validation.ValidateBlock(block, parent, checkpoint, c.ProgramConverter); err != nil {
   196  		return errors.Sub(ErrBadBlock, err)
   197  	}
   198  
   199  	if _, err := c.casper.ApplyBlock(block); err != nil {
   200  		return err
   201  	}
   202  
   203  	if err := c.store.SaveBlock(block); err != nil {
   204  		return err
   205  	}
   206  
   207  	blockHash := block.Hash()
   208  	c.orphanManage.Delete(&blockHash)
   209  	return nil
   210  }
   211  
   212  func (c *Chain) saveSubBlock(block *types.Block) {
   213  	blockHash := block.Hash()
   214  	prevOrphans, ok := c.orphanManage.GetPrevOrphans(&blockHash)
   215  	if !ok {
   216  		return
   217  	}
   218  
   219  	for _, prevOrphan := range prevOrphans {
   220  		orphanBlock, ok := c.orphanManage.Get(prevOrphan)
   221  		if !ok {
   222  			log.WithFields(log.Fields{"module": logModule, "hash": prevOrphan.String()}).Warning("saveSubBlock fail to get block from orphanManage")
   223  			continue
   224  		}
   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")
   227  			continue
   228  		}
   229  
   230  		c.saveSubBlock(orphanBlock)
   231  	}
   232  }
   233  
   234  type processBlockResponse struct {
   235  	isOrphan bool
   236  	err      error
   237  }
   238  
   239  type processBlockMsg struct {
   240  	block *types.Block
   241  	reply chan processBlockResponse
   242  }
   243  
   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}
   248  	response := <-reply
   249  	return response.isOrphan, response.err
   250  }
   251  
   252  func (c *Chain) blockProcessor() {
   253  	for {
   254  		select {
   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)
   260  		}
   261  	}
   262  }
   263  
   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
   270  	}
   271  
   272  	if _, err := c.store.GetBlockHeader(&block.PreviousBlockHash); err != nil {
   273  		c.orphanManage.Add(block)
   274  		return true, nil
   275  	}
   276  
   277  	if err := c.saveBlock(block); err != nil {
   278  		return false, err
   279  	}
   280  
   281  	c.saveSubBlock(block)
   282  	bestHash := c.casper.BestChain()
   283  	return false, c.tryReorganize(bestHash)
   284  }
   285  
   286  func (c *Chain) tryReorganize(bestHash bc.Hash) error {
   287  	if c.bestBlockHeader.Hash() == bestHash {
   288  		return nil
   289  	}
   290  
   291  	blockHeader, err := c.GetHeaderByHash(&bestHash)
   292  	if err != nil {
   293  		return err
   294  	}
   295  
   296  	log.WithFields(log.Fields{"module": logModule, "bestHash": bestHash.String()}).Info("start to reorganize chain")
   297  	return c.reorganizeChain(blockHeader)
   298  }