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 }