github.com/Bytom/bytom@v1.1.2-0.20210127130405-ae40204c0b09/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  	return c.index.BlockExist(hash) || c.orphanManage.BlockExist(hash)
    24  }
    25  
    26  // GetBlockByHash return a block by given hash
    27  func (c *Chain) GetBlockByHash(hash *bc.Hash) (*types.Block, error) {
    28  	return c.store.GetBlock(hash)
    29  }
    30  
    31  // GetBlockByHeight return a block header by given height
    32  func (c *Chain) GetBlockByHeight(height uint64) (*types.Block, error) {
    33  	node := c.index.NodeByHeight(height)
    34  	if node == nil {
    35  		return nil, errors.New("can't find block in given height")
    36  	}
    37  	return c.store.GetBlock(&node.Hash)
    38  }
    39  
    40  // GetHeaderByHash return a block header by given hash
    41  func (c *Chain) GetHeaderByHash(hash *bc.Hash) (*types.BlockHeader, error) {
    42  	node := c.index.GetNode(hash)
    43  	if node == nil {
    44  		return nil, errors.New("can't find block header in given hash")
    45  	}
    46  	return node.BlockHeader(), nil
    47  }
    48  
    49  // GetHeaderByHeight return a block header by given height
    50  func (c *Chain) GetHeaderByHeight(height uint64) (*types.BlockHeader, error) {
    51  	node := c.index.NodeByHeight(height)
    52  	if node == nil {
    53  		return nil, errors.New("can't find block header in given height")
    54  	}
    55  	return node.BlockHeader(), nil
    56  }
    57  
    58  func (c *Chain) calcReorganizeNodes(node *state.BlockNode) ([]*state.BlockNode, []*state.BlockNode) {
    59  	var attachNodes []*state.BlockNode
    60  	var detachNodes []*state.BlockNode
    61  
    62  	attachNode := node
    63  	for c.index.NodeByHeight(attachNode.Height) != attachNode {
    64  		attachNodes = append([]*state.BlockNode{attachNode}, attachNodes...)
    65  		attachNode = attachNode.Parent
    66  	}
    67  
    68  	detachNode := c.bestNode
    69  	for detachNode != attachNode {
    70  		detachNodes = append(detachNodes, detachNode)
    71  		detachNode = detachNode.Parent
    72  	}
    73  	return attachNodes, detachNodes
    74  }
    75  
    76  func (c *Chain) connectBlock(block *types.Block) (err error) {
    77  	bcBlock := types.MapBlock(block)
    78  	if bcBlock.TransactionStatus, err = c.store.GetTransactionStatus(&bcBlock.ID); err != nil {
    79  		return err
    80  	}
    81  
    82  	utxoView := state.NewUtxoViewpoint()
    83  	if err := c.store.GetTransactionsUtxo(utxoView, bcBlock.Transactions); err != nil {
    84  		return err
    85  	}
    86  	if err := utxoView.ApplyBlock(bcBlock, bcBlock.TransactionStatus); err != nil {
    87  		return err
    88  	}
    89  
    90  	node := c.index.GetNode(&bcBlock.ID)
    91  	if err := c.setState(node, utxoView); err != nil {
    92  		return err
    93  	}
    94  
    95  	for _, tx := range block.Transactions {
    96  		c.txPool.RemoveTransaction(&tx.Tx.ID)
    97  	}
    98  	return nil
    99  }
   100  
   101  func (c *Chain) reorganizeChain(node *state.BlockNode) error {
   102  	attachNodes, detachNodes := c.calcReorganizeNodes(node)
   103  	utxoView := state.NewUtxoViewpoint()
   104  
   105  	txsToRestore := map[bc.Hash]*types.Tx{}
   106  	for _, detachNode := range detachNodes {
   107  		b, err := c.store.GetBlock(&detachNode.Hash)
   108  		if err != nil {
   109  			return err
   110  		}
   111  
   112  		detachBlock := types.MapBlock(b)
   113  		if err := c.store.GetTransactionsUtxo(utxoView, detachBlock.Transactions); err != nil {
   114  			return err
   115  		}
   116  		txStatus, err := c.GetTransactionStatus(&detachBlock.ID)
   117  		if err != nil {
   118  			return err
   119  		}
   120  		if err := utxoView.DetachBlock(detachBlock, txStatus); err != nil {
   121  			return err
   122  		}
   123  
   124  		for _, tx := range b.Transactions {
   125  			txsToRestore[tx.ID] = tx
   126  		}
   127  		log.WithFields(log.Fields{"module": logModule, "height": node.Height, "hash": node.Hash.String()}).Debug("detach from mainchain")
   128  	}
   129  
   130  	txsToRemove := map[bc.Hash]*types.Tx{}
   131  	for _, attachNode := range attachNodes {
   132  		b, err := c.store.GetBlock(&attachNode.Hash)
   133  		if err != nil {
   134  			return err
   135  		}
   136  
   137  		attachBlock := types.MapBlock(b)
   138  		if err := c.store.GetTransactionsUtxo(utxoView, attachBlock.Transactions); err != nil {
   139  			return err
   140  		}
   141  		txStatus, err := c.GetTransactionStatus(&attachBlock.ID)
   142  		if err != nil {
   143  			return err
   144  		}
   145  		if err := utxoView.ApplyBlock(attachBlock, txStatus); err != nil {
   146  			return err
   147  		}
   148  
   149  		for _, tx := range b.Transactions {
   150  			if _, ok := txsToRestore[tx.ID]; !ok {
   151  				txsToRemove[tx.ID] = tx
   152  			} else {
   153  				delete(txsToRestore, tx.ID)
   154  			}
   155  		}
   156  
   157  		log.WithFields(log.Fields{"module": logModule, "height": node.Height, "hash": node.Hash.String()}).Debug("attach from mainchain")
   158  	}
   159  
   160  	if err := c.setState(node, utxoView); err != nil {
   161  		return err
   162  	}
   163  
   164  	for txHash := range txsToRemove {
   165  		c.txPool.RemoveTransaction(&txHash)
   166  	}
   167  
   168  	for _, tx := range txsToRestore {
   169  		// the number of restored Tx should be very small or most of time ZERO
   170  		// Error returned from validation is ignored, tx could still be lost if validation fails.
   171  		// TODO: adjust tx timestamp so that it won't starve in pool.
   172  		if _, err := c.ValidateTx(tx); err != nil {
   173  			log.WithFields(log.Fields{"module": logModule, "tx_id": tx.Tx.ID.String(), "error": err}).Info("restore tx fail")
   174  		}
   175  	}
   176  
   177  	if len(txsToRestore) > 0 {
   178  		log.WithFields(log.Fields{"module": logModule, "num": len(txsToRestore)}).Debug("restore txs back to pool")
   179  	}
   180  
   181  	return nil
   182  }
   183  
   184  // SaveBlock will validate and save block into storage
   185  func (c *Chain) saveBlock(block *types.Block) error {
   186  	bcBlock := types.MapBlock(block)
   187  	parent := c.index.GetNode(&block.PreviousBlockHash)
   188  
   189  	if err := validation.ValidateBlock(bcBlock, parent); err != nil {
   190  		return errors.Sub(ErrBadBlock, err)
   191  	}
   192  	if err := c.store.SaveBlock(block, bcBlock.TransactionStatus); err != nil {
   193  		return err
   194  	}
   195  
   196  	c.orphanManage.Delete(&bcBlock.ID)
   197  	node, err := state.NewBlockNode(&block.BlockHeader, parent)
   198  	if err != nil {
   199  		return err
   200  	}
   201  
   202  	c.index.AddNode(node)
   203  	return nil
   204  }
   205  
   206  func (c *Chain) saveSubBlock(block *types.Block) *types.Block {
   207  	blockHash := block.Hash()
   208  	prevOrphans, ok := c.orphanManage.GetPrevOrphans(&blockHash)
   209  	if !ok {
   210  		return block
   211  	}
   212  
   213  	bestBlock := block
   214  	for _, prevOrphan := range prevOrphans {
   215  		orphanBlock, ok := c.orphanManage.Get(prevOrphan)
   216  		if !ok {
   217  			log.WithFields(log.Fields{"module": logModule, "hash": prevOrphan.String()}).Warning("saveSubBlock fail to get block from orphanManage")
   218  			continue
   219  		}
   220  		if err := c.saveBlock(orphanBlock); err != nil {
   221  			log.WithFields(log.Fields{"module": logModule, "hash": prevOrphan.String(), "height": orphanBlock.Height}).Warning("saveSubBlock fail to save block")
   222  			continue
   223  		}
   224  
   225  		if subBestBlock := c.saveSubBlock(orphanBlock); subBestBlock.Height > bestBlock.Height {
   226  			bestBlock = subBestBlock
   227  		}
   228  	}
   229  	return bestBlock
   230  }
   231  
   232  type processBlockResponse struct {
   233  	isOrphan bool
   234  	err      error
   235  }
   236  
   237  type processBlockMsg struct {
   238  	block *types.Block
   239  	reply chan processBlockResponse
   240  }
   241  
   242  // ProcessBlock is the entry for chain update
   243  func (c *Chain) ProcessBlock(block *types.Block) (bool, error) {
   244  	reply := make(chan processBlockResponse, 1)
   245  	c.processBlockCh <- &processBlockMsg{block: block, reply: reply}
   246  	response := <-reply
   247  	return response.isOrphan, response.err
   248  }
   249  
   250  func (c *Chain) blockProcesser() {
   251  	for msg := range c.processBlockCh {
   252  		isOrphan, err := c.processBlock(msg.block)
   253  		msg.reply <- processBlockResponse{isOrphan: isOrphan, err: err}
   254  	}
   255  }
   256  
   257  // ProcessBlock is the entry for handle block insert
   258  func (c *Chain) processBlock(block *types.Block) (bool, error) {
   259  	blockHash := block.Hash()
   260  	if c.BlockExist(&blockHash) {
   261  		log.WithFields(log.Fields{"module": logModule, "hash": blockHash.String(), "height": block.Height}).Info("block has been processed")
   262  		return c.orphanManage.BlockExist(&blockHash), nil
   263  	}
   264  
   265  	if parent := c.index.GetNode(&block.PreviousBlockHash); parent == nil {
   266  		c.orphanManage.Add(block)
   267  		return true, nil
   268  	}
   269  
   270  	if err := c.saveBlock(block); err != nil {
   271  		return false, err
   272  	}
   273  
   274  	bestBlock := c.saveSubBlock(block)
   275  	bestBlockHash := bestBlock.Hash()
   276  	bestNode := c.index.GetNode(&bestBlockHash)
   277  
   278  	if bestNode.Parent == c.bestNode {
   279  		log.WithFields(log.Fields{"module": logModule}).Debug("append block to the end of mainchain")
   280  		return false, c.connectBlock(bestBlock)
   281  	}
   282  
   283  	if bestNode.Height > c.bestNode.Height && bestNode.WorkSum.Cmp(c.bestNode.WorkSum) >= 0 {
   284  		log.WithFields(log.Fields{"module": logModule}).Debug("start to reorganize chain")
   285  		return false, c.reorganizeChain(bestNode)
   286  	}
   287  	return false, nil
   288  }