github.com/bigzoro/my_simplechain@v0.0.0-20240315012955-8ad0a2a29bb9/consensus/raft/speculative_chain.go (about)

     1  package raft
     2  
     3  import (
     4  	"github.com/bigzoro/my_simplechain/common"
     5  	"github.com/bigzoro/my_simplechain/core/types"
     6  	"github.com/bigzoro/my_simplechain/log"
     7  
     8  	mapset "github.com/deckarep/golang-set"
     9  	"gopkg.in/oleiade/lane.v1"
    10  )
    11  
    12  // The speculative chain represents blocks that we have minted which haven't been accepted into the chain yet, building
    13  // on each other in a chain. It has three basic operations:
    14  // * add new block to end
    15  // * accept / remove oldest block
    16  // * unwind / remove invalid blocks to the end
    17  //
    18  // Additionally:
    19  // * clear state when we stop minting
    20  // * set the parent when we're not minting (so it's always current)
    21  type SpeculativeChain struct {
    22  	head                       *types.Block
    23  	unappliedBlocks            *lane.Deque
    24  	expectedInvalidBlockHashes mapset.Set // This is thread-safe. This set is referred to as our "guard" below.
    25  	proposedTxes               mapset.Set // This is thread-safe.
    26  }
    27  
    28  type AddressTxes map[common.Address]types.Transactions
    29  
    30  func NewSpeculativeChain() *SpeculativeChain {
    31  	return &SpeculativeChain{
    32  		head:                       nil,
    33  		unappliedBlocks:            lane.NewDeque(),
    34  		expectedInvalidBlockHashes: mapset.NewSet(),
    35  		proposedTxes:               mapset.NewSet(),
    36  	}
    37  }
    38  
    39  func (chain *SpeculativeChain) Head() *types.Block {
    40  	return chain.head
    41  }
    42  
    43  func (chain *SpeculativeChain) Clear(block *types.Block) {
    44  	chain.head = block
    45  	chain.unappliedBlocks = lane.NewDeque()
    46  	chain.expectedInvalidBlockHashes.Clear()
    47  	chain.proposedTxes.Clear()
    48  }
    49  
    50  // Append a new speculative block
    51  func (chain *SpeculativeChain) Extend(block *types.Block) {
    52  	chain.head = block
    53  	chain.recordProposedTransactions(block.Transactions())
    54  	chain.unappliedBlocks.Append(block)
    55  }
    56  
    57  // Set the parent of the speculative chain
    58  //
    59  // Note: This is only called when not minter
    60  func (chain *SpeculativeChain) SetHead(block *types.Block) {
    61  	chain.head = block
    62  }
    63  
    64  // Accept this block, removing it from the speculative chain
    65  func (chain *SpeculativeChain) Accept(acceptedBlock *types.Block) {
    66  	earliestProposedI := chain.unappliedBlocks.Shift()
    67  	var earliestProposed *types.Block
    68  	if nil != earliestProposedI {
    69  		earliestProposed = earliestProposedI.(*types.Block)
    70  	}
    71  
    72  	// There are three possible scenarios:
    73  	// 1. We don't have a record of this block (or any proposed blocks), meaning someone else minted it and we should
    74  	//    add it as the new head of our speculative chain. New blocks from the old leader are still coming in.
    75  	// 2. This block was the first outstanding one we proposed.
    76  	// 3. This block is different from the block we proposed, (also) meaning new blocks are still coming in from the old
    77  	//    leader, but unlike the first scenario, we need to clear all of the speculative chain state because the
    78  	//    `acceptedBlock` takes precedence over our speculative state.
    79  	if earliestProposed == nil {
    80  		chain.head = acceptedBlock
    81  	} else if expectedBlock := earliestProposed.Hash() == acceptedBlock.Hash(); expectedBlock {
    82  		// Remove the txes in this accepted block from our blacklist.
    83  		chain.removeProposedTxes(acceptedBlock)
    84  	} else {
    85  		log.Info("Another node minted; Clearing speculative state", "block", acceptedBlock.Hash())
    86  
    87  		chain.Clear(acceptedBlock)
    88  	}
    89  }
    90  
    91  // Remove all blocks in the chain from the specified one until the end
    92  func (chain *SpeculativeChain) UnwindFrom(invalidHash common.Hash, headBlock *types.Block) {
    93  
    94  	// check our "guard" to see if this is a (descendant) block we're
    95  	// expected to be ruled invalid. if we find it, remove from the guard
    96  	if chain.expectedInvalidBlockHashes.Contains(invalidHash) {
    97  		log.Info("Removing expected-invalid block from guard.", "block", invalidHash)
    98  
    99  		chain.expectedInvalidBlockHashes.Remove(invalidHash)
   100  
   101  		return
   102  	}
   103  
   104  	// pop from the RHS repeatedly, updating minter.parent each time. if not
   105  	// our block, add to guard. in all cases, call removeProposedTxes
   106  	for {
   107  		currBlockI := chain.unappliedBlocks.Pop()
   108  
   109  		if nil == currBlockI {
   110  			log.Info("(Popped all blocks from queue.)")
   111  
   112  			break
   113  		}
   114  
   115  		currBlock := currBlockI.(*types.Block)
   116  
   117  		log.Info("Popped block from queue RHS.", "block", currBlock.Hash())
   118  
   119  		// Maintain invariant: the parent always points the last speculative block or the head of the blockchain
   120  		// if there are not speculative blocks.
   121  		if speculativeParentI := chain.unappliedBlocks.Last(); nil != speculativeParentI {
   122  			chain.head = speculativeParentI.(*types.Block)
   123  		} else {
   124  			chain.head = headBlock
   125  		}
   126  
   127  		chain.removeProposedTxes(currBlock)
   128  
   129  		if currBlock.Hash() != invalidHash {
   130  			log.Info("Haven't yet found block; adding descendent to guard.\n", "invalid block", invalidHash, "descendant", currBlock.Hash())
   131  
   132  			chain.expectedInvalidBlockHashes.Add(currBlock.Hash())
   133  		} else {
   134  			break
   135  		}
   136  	}
   137  }
   138  
   139  // We keep track of txes we've put in all newly-mined blocks since the last
   140  // ChainHeadEvent, and filter them out so that we don't try to create blocks
   141  // with the same transactions. This is necessary because the TX pool will keep
   142  // supplying us these transactions until they are in the chain (after having
   143  // flown through raft).
   144  func (chain *SpeculativeChain) recordProposedTransactions(txes types.Transactions) {
   145  	txHashIs := make([]interface{}, len(txes))
   146  	for i, tx := range txes {
   147  		txHashIs[i] = tx.Hash()
   148  	}
   149  	for _, i := range txHashIs {
   150  		chain.proposedTxes.Add(i)
   151  	}
   152  }
   153  
   154  // Removes txes in block from our "blacklist" of "proposed tx" hashes. When we
   155  // create a new block and use txes from the tx pool, we ignore those that we
   156  // have already used ("proposed"), but that haven't yet officially made it into
   157  // the chain yet.
   158  //
   159  // It's important to remove hashes from this blacklist (once we know we don't
   160  // need them in there anymore) so that it doesn't grow endlessly.
   161  func (chain *SpeculativeChain) removeProposedTxes(block *types.Block) {
   162  	minedTxes := block.Transactions()
   163  	minedTxInterfaces := make([]interface{}, len(minedTxes))
   164  	for i, tx := range minedTxes {
   165  		minedTxInterfaces[i] = tx.Hash()
   166  	}
   167  
   168  	// NOTE: we are using a thread-safe Set here, so it's fine if we access this
   169  	// here and in mintNewBlock concurrently. using a finer-grained set-specific
   170  	// lock here is preferable, because mintNewBlock holds its locks for a
   171  	// nontrivial amount of time.
   172  	for _, i := range minedTxInterfaces {
   173  		chain.proposedTxes.Remove(i)
   174  	}
   175  }
   176  
   177  func (chain *SpeculativeChain) WithoutProposedTxes2(addrTxes AddressTxes) AddressTxes {
   178  	newMap := make(AddressTxes)
   179  
   180  	for addr, txes := range addrTxes {
   181  		filteredTxes := make(types.Transactions, 0)
   182  		for _, tx := range txes {
   183  			if !chain.proposedTxes.Contains(tx.Hash()) {
   184  				filteredTxes = append(filteredTxes, tx)
   185  			}
   186  		}
   187  		if len(filteredTxes) > 0 {
   188  			newMap[addr] = filteredTxes
   189  		}
   190  	}
   191  
   192  	return newMap
   193  }
   194  
   195  func (chain *SpeculativeChain) WithoutProposedTxes(txes types.Transactions) types.Transactions {
   196  	filteredTxes := make(types.Transactions, 0, txes.Len())
   197  	for _, tx := range txes {
   198  		if !chain.proposedTxes.Contains(tx.Hash()) {
   199  			filteredTxes = append(filteredTxes, tx)
   200  		}
   201  	}
   202  	return filteredTxes
   203  }