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

     1  package hotstuff
     2  
     3  import (
     4  	"errors"
     5  	"math/big"
     6  
     7  	"github.com/bigzoro/my_simplechain/common"
     8  	"github.com/bigzoro/my_simplechain/common/hexutil"
     9  	"github.com/bigzoro/my_simplechain/consensus"
    10  	"github.com/bigzoro/my_simplechain/core/state"
    11  	"github.com/bigzoro/my_simplechain/core/types"
    12  	"github.com/bigzoro/my_simplechain/rlp"
    13  	"github.com/bigzoro/my_simplechain/rpc"
    14  	"golang.org/x/crypto/sha3"
    15  )
    16  
    17  var (
    18  	uncleHash = types.CalcUncleHash(nil) // Always Keccak256(RLP([])) as uncles are meaningless outside of PoW.
    19  
    20  	nonceZero = hexutil.MustDecode("0x0000000000000000") // zero nonce
    21  )
    22  
    23  var (
    24  	// errUnknownBlock is returned when the list of signers is requested for a block
    25  	// that is not part of the local blockchain.
    26  	errUnknownBlock = errors.New("unknown block")
    27  
    28  	// errInvalidDifficulty is returned if the difficulty is not equal to the difference
    29  	// in view between the verified header and its parent header
    30  	errInvalidDifficulty = errors.New("invalid difficulty")
    31  
    32  	// errInvalidUncleHash is returned if a block contains an non-empty uncle list.
    33  	errInvalidUncleHash = errors.New("non empty uncle hash")
    34  
    35  	// errInvalidTimestamp is returned if the timestamp of a block is lower than
    36  	// the previous block's timestamp
    37  	errInvalidTimestamp = errors.New("invalid timestamp")
    38  
    39  	// errInvalidMixDigest is returned if a block's mix digest is non-zero.
    40  	errInvalidMixDigest = errors.New("non-zero mix digest")
    41  
    42  	// errInvalidNonce is returned if a nonce value is lower than the parent's one.
    43  	errInvalidNonce = errors.New("invalid nonce")
    44  
    45  	// errInvalidSnapshot is returned if there is inconsistent snapshot between the
    46  	// initial snapshot and the snapshot after adding/removing replicas
    47  	errInvalidSnapshot = errors.New("invalid snapshot")
    48  )
    49  
    50  // Author implements consensus.Engine.Author and retrieves the Ethereum
    51  // address of the account that minted the given block.
    52  func (lg *Legal) Author(header *types.Header) (common.Address, error) {
    53  	return header.Coinbase, nil
    54  }
    55  
    56  // VerifyHeader implements consensus.Engine.VerifyHeader and checks whether
    57  // a header conforms to the consensus rules of the Hotstuff engine. Verifying
    58  // the seal is done here.
    59  func (lg *Legal) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error {
    60  	return lg.verifyHeader(chain, header, nil)
    61  }
    62  
    63  // VerifyHeaders implements consensus.Engine.VerifyHeaders, it is similar to
    64  // VerifyHeader, but verifies a batch of headers concurrently. The method
    65  // returns a quit channel to abort the operations and a results channel to
    66  // retrieve the async verifications (the order is that of the input slice).
    67  func (lg *Legal) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
    68  	abort := make(chan struct{})
    69  	results := make(chan error, len(headers))
    70  
    71  	go func() {
    72  		for i, header := range headers {
    73  			err := lg.verifyHeader(chain, header, headers[:i])
    74  
    75  			select {
    76  			case <-abort:
    77  				return
    78  			case results <- err:
    79  			}
    80  		}
    81  	}()
    82  	return abort, results
    83  }
    84  
    85  // VerifyUncles implements consensus.Engine.VerifyUncles and verifies that
    86  // the given block's uncles conform to the consensus rules of Hotstuff engine.
    87  func (lg *Legal) VerifyUncles(ChainReader consensus.ChainReader, block *types.Block) error {
    88  	if len(block.Uncles()) > 0 {
    89  		return errors.New("uncles not allowed")
    90  	}
    91  	return nil
    92  }
    93  
    94  // VerifySeal checks whether the crypto seal on a header is valid according to
    95  // the consensus rules of the given engine.
    96  func (lg *Legal) VerifySeal(ChainReader consensus.ChainReader, header *types.Header) error {
    97  	return lg.verifySeal(header, ChainReader.GetHeader(header.ParentHash, header.Number.Uint64()-1))
    98  }
    99  
   100  // Prepare implements consensus.Engine.Prepare and does nothing.
   101  func (lg *Legal) Prepare(chain consensus.ChainReader, header *types.Header) error {
   102  	header.Difficulty = new(big.Int)
   103  	return nil
   104  }
   105  
   106  // Finalize implements consensus.Engine.Finalize.
   107  func (lg *Legal) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
   108  	uncles []*types.Header, receipts []*types.Receipt) error {
   109  	header.Root = state.IntermediateRoot(true)
   110  	header.UncleHash = types.CalcUncleHash(nil)
   111  	return nil
   112  }
   113  
   114  // FinalizeAndAssemble implements consensus.Engine.FinalizeAndAssemble.
   115  func (lg *Legal) FinalizeAndAssemble(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
   116  	uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
   117  	header.Root = state.IntermediateRoot(true)
   118  	header.UncleHash = types.CalcUncleHash(nil)
   119  
   120  	// Assemble and return the final block for sealing.
   121  	return types.NewBlock(header, txs, nil, receipts), nil
   122  }
   123  
   124  // Seal implements consensus.Engine.Seal and does nothing.
   125  func (lg *Legal) Seal(chain consensus.ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
   126  	return nil
   127  }
   128  
   129  // SealHash implements consensus.Engine.SealHash, returns the hash of a block
   130  // prior to it being sealed.
   131  func (lg *Legal) SealHash(header *types.Header) (hash common.Hash) {
   132  	hashObject := sha3.NewLegacyKeccak256()
   133  	rlp.Encode(hashObject, []interface{}{
   134  		header.ParentHash,
   135  		header.UncleHash,
   136  		header.Coinbase,
   137  		header.Root,
   138  		header.TxHash,
   139  		header.ReceiptHash,
   140  		header.Bloom,
   141  		header.Number,
   142  		header.GasLimit,
   143  		header.GasUsed,
   144  		header.MixDigest,
   145  	})
   146  	hashObject.Sum(hash[:0])
   147  	return hash
   148  }
   149  
   150  // CalcDifficulty implements consensus.Engine.CalcDifficulty.
   151  func (lg *Legal) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int {
   152  	return new(big.Int)
   153  }
   154  
   155  // APIs implements consensus.Engine.APIs.
   156  func (lg *Legal) APIs(chain consensus.ChainReader) []rpc.API {
   157  	return nil
   158  }
   159  
   160  // Close implements consensus.Engine.Close.
   161  func (lg *Legal) Close() error {
   162  	return nil
   163  }
   164  
   165  func (lg *Legal) verifyHeader(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error {
   166  	if header.Number == nil {
   167  		return errUnknownBlock
   168  	}
   169  	number := header.Number.Uint64()
   170  
   171  	var parent *types.Header
   172  	if len(parents) > 0 {
   173  		parent = parents[len(parents)-1]
   174  	} else {
   175  		parent = chain.GetHeader(header.ParentHash, number-1)
   176  	}
   177  	// Basicly check if the header is extended from its parent.
   178  	if parent == nil || parent.Number.Uint64() != number-1 || parent.Hash() != header.ParentHash {
   179  		return consensus.ErrInvalidNumber
   180  	}
   181  
   182  	// The block's difficulty represents the number of consensus rounds a block needs to
   183  	// go through before being committed, ensuring that they are strictly equal.
   184  	if header.Difficulty == nil || header.Difficulty.Sign() <= 0 || header.Difficulty.Uint64() != header.Nonce.Uint64()-parent.Nonce.Uint64() {
   185  		return errInvalidDifficulty
   186  	}
   187  
   188  	// Ensure that the block doesn't contain any uncles which are meaningless in Hotstuff.
   189  	if header.UncleHash != uncleHash {
   190  		return errInvalidUncleHash
   191  	}
   192  
   193  	if parent.Time >= header.Time {
   194  		return errInvalidTimestamp
   195  	}
   196  
   197  	// Ensure that the mix digest is zero as we don't have fork protection currently.
   198  	if header.MixDigest != (common.Hash{}) {
   199  		return errInvalidMixDigest
   200  	}
   201  
   202  	return lg.verifySeal(header, parent)
   203  }
   204  
   205  func (lg *Legal) verifySeal(header *types.Header, parent *types.Header) error {
   206  	// Recovery the parent block snapshot
   207  	parentSnapHash, _ := extractSnapshot(parent, true)
   208  	snap, err := lg.snapshot(parentSnapHash)
   209  	if err != nil {
   210  		return err
   211  	}
   212  
   213  	parentReplicaEvent, _ := extractReplicaEvent(parent, true)
   214  	if parentReplicaEvent != nil {
   215  		snap = parentReplicaEvent.apply(snap)
   216  		if err := lg.store(snap); err != nil {
   217  			return err
   218  		}
   219  	}
   220  
   221  	snapHash, sig, ev, err := extract(header)
   222  	if err != nil {
   223  		return err
   224  	}
   225  
   226  	if snapHash != snap.Hash() {
   227  		return errInvalidSnapshot
   228  	}
   229  
   230  	if ev != nil {
   231  		if err := ev.verify(snap, header.Number.Uint64()); err != nil {
   232  			return err
   233  		}
   234  	}
   235  
   236  	// Verify block holds a valid quorum certificate.
   237  	return sig.Verify(snap, legacyQuorumCertDigest(header.Nonce.Uint64()-1, header.ParentHash.Bytes()).Bytes())
   238  }
   239  
   240  // Prepare implements consensus.Engine.Prepare, preparing all the consensus fields of the
   241  // header for running the transactions on top. Furthermore, the consensus state of the Council
   242  // will be updated and enter a new consensus round to generate quorum certificates more quickly
   243  // if there are changes in the passed head header.
   244  func (c *Council) Prepare(chain consensus.ChainReader, header *types.Header) error {
   245  	// The preparing header from miner is always based on the chain head block, so
   246  	// there is no need to check if the header is based on the latest block.
   247  	number := header.Number.Uint64()
   248  	parent := chain.GetHeader(header.ParentHash, number-1)
   249  
   250  	var stateChanged bool
   251  	c.base.mux.RLock()
   252  	if header.ParentHash != c.base.block {
   253  		stateChanged = true
   254  	}
   255  	c.base.mux.RUnlock()
   256  
   257  	if stateChanged {
   258  		c.newHead <- parent // Update the consensus state of the Council.
   259  	}
   260  
   261  	header.Difficulty = new(big.Int)
   262  
   263  	return nil
   264  }
   265  
   266  // Seal generates a new sealing request for the given input block and pushes
   267  // the result into the given channel.
   268  //
   269  // Note, the method returns immediately and will send the result async. More
   270  // than one result may also be returned depending on the consensus algorithm.
   271  func (c *Council) Seal(chain consensus.ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
   272  	header := block.Header()
   273  	number := header.Number.Uint64()
   274  	parent := chain.GetHeader(header.ParentHash, number-1)
   275  	parentview := parent.Nonce.Uint64()
   276  
   277  	// Use a buffered channel to ensure that the first subscription of an existing
   278  	// quorum certificate is not missed when subscribing.
   279  	certc := make(chan *cert, 1)
   280  	go func() {
   281  		// defer c.taskmngr.optimal.unsub(certc)
   282  
   283  		for {
   284  			select {
   285  			case cert, ok := <-certc:
   286  				if !ok {
   287  					log.Debug("Discards sealing for consensus state changed")
   288  					return
   289  				}
   290  				if cert.blockBased != header.ParentHash {
   291  					log.Error("Bad certificate on wrong source", "want", header.ParentHash, "receive", cert.blockBased)
   292  					return
   293  				}
   294  
   295  				// Set the value of header nonce field to the view.
   296  				header.Nonce = types.EncodeNonce(cert.view + 1)
   297  
   298  				// Set the value of the header difficulty field to the difference between the current
   299  				// view and the view of the parent block.
   300  				header.Difficulty = new(big.Int).SetUint64(cert.view - parentview + 1)
   301  
   302  				ev := c.navigation.commit(cert.snap)
   303  				if ev != nil {
   304  					if err := ev.verify(cert.snap, number); err != nil {
   305  						log.Debug("Replica event validation", "id", ev.id, "error", err)
   306  						ev = nil
   307  					}
   308  				}
   309  
   310  				// Embed the quorum certificate into the header's extra field.
   311  				extra, err := seal(cert.snap.Hash(), cert.signature, ev)
   312  				if err != nil {
   313  					log.Debug("Failed sealing", "error", err)
   314  					return
   315  				}
   316  				header.Extra = extra
   317  
   318  				select {
   319  				case results <- block.WithSeal(header):
   320  				default:
   321  				}
   322  
   323  			case <-stop:
   324  				return
   325  			}
   326  		}
   327  	}()
   328  
   329  	c.poller.optimal.Subscribe(certc, header.ParentHash)
   330  
   331  	return nil
   332  }
   333  
   334  // APIs implements Hotstuff engine apis.
   335  func (c *Council) APIs(chain consensus.ChainReader) []rpc.API {
   336  	return []rpc.API{{
   337  		Namespace: "hotstuff",
   338  		Version:   "1.0",
   339  		Service:   &API{chain: chain, council: c},
   340  		Public:    false,
   341  	}}
   342  }