github.com/tacshi/go-ethereum@v0.0.0-20230616113857-84a434e20921/consensus/beacon/consensus.go (about) 1 // Copyright 2021 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package beacon 18 19 import ( 20 "errors" 21 "fmt" 22 "math/big" 23 24 "github.com/tacshi/go-ethereum/common" 25 "github.com/tacshi/go-ethereum/consensus" 26 "github.com/tacshi/go-ethereum/consensus/misc" 27 "github.com/tacshi/go-ethereum/core/state" 28 "github.com/tacshi/go-ethereum/core/types" 29 "github.com/tacshi/go-ethereum/params" 30 "github.com/tacshi/go-ethereum/rpc" 31 "github.com/tacshi/go-ethereum/trie" 32 ) 33 34 // Proof-of-stake protocol constants. 35 var ( 36 beaconDifficulty = common.Big0 // The default block difficulty in the beacon consensus 37 beaconNonce = types.EncodeNonce(0) // The default block nonce in the beacon consensus 38 ) 39 40 // Various error messages to mark blocks invalid. These should be private to 41 // prevent engine specific errors from being referenced in the remainder of the 42 // codebase, inherently breaking if the engine is swapped out. Please put common 43 // error types into the consensus package. 44 var ( 45 errTooManyUncles = errors.New("too many uncles") 46 errInvalidNonce = errors.New("invalid nonce") 47 errInvalidUncleHash = errors.New("invalid uncle hash") 48 errInvalidTimestamp = errors.New("invalid timestamp") 49 ) 50 51 // Beacon is a consensus engine that combines the eth1 consensus and proof-of-stake 52 // algorithm. There is a special flag inside to decide whether to use legacy consensus 53 // rules or new rules. The transition rule is described in the eth1/2 merge spec. 54 // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3675.md 55 // 56 // The beacon here is a half-functional consensus engine with partial functions which 57 // is only used for necessary consensus checks. The legacy consensus engine can be any 58 // engine implements the consensus interface (except the beacon itself). 59 type Beacon struct { 60 ethone consensus.Engine // Original consensus engine used in eth1, e.g. ethash or clique 61 } 62 63 // New creates a consensus engine with the given embedded eth1 engine. 64 func New(ethone consensus.Engine) *Beacon { 65 if _, ok := ethone.(*Beacon); ok { 66 panic("nested consensus engine") 67 } 68 return &Beacon{ethone: ethone} 69 } 70 71 // Author implements consensus.Engine, returning the verified author of the block. 72 func (beacon *Beacon) Author(header *types.Header) (common.Address, error) { 73 if !beacon.IsPoSHeader(header) { 74 return beacon.ethone.Author(header) 75 } 76 return header.Coinbase, nil 77 } 78 79 // VerifyHeader checks whether a header conforms to the consensus rules of the 80 // stock Ethereum consensus engine. 81 func (beacon *Beacon) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error { 82 reached, err := IsTTDReached(chain, header.ParentHash, header.Number.Uint64()-1) 83 if err != nil { 84 return err 85 } 86 if !reached { 87 return beacon.ethone.VerifyHeader(chain, header, seal) 88 } 89 // Short circuit if the parent is not known 90 parent := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) 91 if parent == nil { 92 return consensus.ErrUnknownAncestor 93 } 94 // Sanity checks passed, do a proper verification 95 return beacon.verifyHeader(chain, header, parent) 96 } 97 98 // errOut constructs an error channel with prefilled errors inside. 99 func errOut(n int, err error) chan error { 100 errs := make(chan error, n) 101 for i := 0; i < n; i++ { 102 errs <- err 103 } 104 return errs 105 } 106 107 // splitHeaders splits the provided header batch into two parts according to 108 // the configured ttd. It requires the parent of header batch along with its 109 // td are stored correctly in chain. If ttd is not configured yet, all headers 110 // will be treated legacy PoW headers. 111 // Note, this function will not verify the header validity but just split them. 112 func (beacon *Beacon) splitHeaders(chain consensus.ChainHeaderReader, headers []*types.Header) ([]*types.Header, []*types.Header, error) { 113 // TTD is not defined yet, all headers should be in legacy format. 114 ttd := chain.Config().TerminalTotalDifficulty 115 if ttd == nil { 116 return headers, nil, nil 117 } 118 ptd := chain.GetTd(headers[0].ParentHash, headers[0].Number.Uint64()-1) 119 if ptd == nil { 120 return nil, nil, consensus.ErrUnknownAncestor 121 } 122 // The entire header batch already crosses the transition. 123 if ptd.Cmp(ttd) >= 0 { 124 return nil, headers, nil 125 } 126 var ( 127 preHeaders = headers 128 postHeaders []*types.Header 129 td = new(big.Int).Set(ptd) 130 tdPassed bool 131 ) 132 for i, header := range headers { 133 if tdPassed { 134 preHeaders = headers[:i] 135 postHeaders = headers[i:] 136 break 137 } 138 td = td.Add(td, header.Difficulty) 139 if td.Cmp(ttd) >= 0 { 140 // This is the last PoW header, it still belongs to 141 // the preHeaders, so we cannot split+break yet. 142 tdPassed = true 143 } 144 } 145 return preHeaders, postHeaders, nil 146 } 147 148 // VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers 149 // concurrently. The method returns a quit channel to abort the operations and 150 // a results channel to retrieve the async verifications. 151 // VerifyHeaders expect the headers to be ordered and continuous. 152 func (beacon *Beacon) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) { 153 preHeaders, postHeaders, err := beacon.splitHeaders(chain, headers) 154 if err != nil { 155 return make(chan struct{}), errOut(len(headers), err) 156 } 157 if len(postHeaders) == 0 { 158 return beacon.ethone.VerifyHeaders(chain, headers, seals) 159 } 160 if len(preHeaders) == 0 { 161 return beacon.verifyHeaders(chain, headers, nil) 162 } 163 // The transition point exists in the middle, separate the headers 164 // into two batches and apply different verification rules for them. 165 var ( 166 abort = make(chan struct{}) 167 results = make(chan error, len(headers)) 168 ) 169 go func() { 170 var ( 171 old, new, out = 0, len(preHeaders), 0 172 errors = make([]error, len(headers)) 173 done = make([]bool, len(headers)) 174 oldDone, oldResult = beacon.ethone.VerifyHeaders(chain, preHeaders, seals[:len(preHeaders)]) 175 newDone, newResult = beacon.verifyHeaders(chain, postHeaders, preHeaders[len(preHeaders)-1]) 176 ) 177 // Collect the results 178 for { 179 for ; done[out]; out++ { 180 results <- errors[out] 181 if out == len(headers)-1 { 182 return 183 } 184 } 185 select { 186 case err := <-oldResult: 187 if !done[old] { // skip TTD-verified failures 188 errors[old], done[old] = err, true 189 } 190 old++ 191 case err := <-newResult: 192 errors[new], done[new] = err, true 193 new++ 194 case <-abort: 195 close(oldDone) 196 close(newDone) 197 return 198 } 199 } 200 }() 201 return abort, results 202 } 203 204 // VerifyUncles verifies that the given block's uncles conform to the consensus 205 // rules of the Ethereum consensus engine. 206 func (beacon *Beacon) VerifyUncles(chain consensus.ChainReader, block *types.Block) error { 207 if !beacon.IsPoSHeader(block.Header()) { 208 return beacon.ethone.VerifyUncles(chain, block) 209 } 210 // Verify that there is no uncle block. It's explicitly disabled in the beacon 211 if len(block.Uncles()) > 0 { 212 return errTooManyUncles 213 } 214 return nil 215 } 216 217 // verifyHeader checks whether a header conforms to the consensus rules of the 218 // stock Ethereum consensus engine. The difference between the beacon and classic is 219 // (a) The following fields are expected to be constants: 220 // - difficulty is expected to be 0 221 // - nonce is expected to be 0 222 // - unclehash is expected to be Hash(emptyHeader) 223 // to be the desired constants 224 // 225 // (b) we don't verify if a block is in the future anymore 226 // (c) the extradata is limited to 32 bytes 227 func (beacon *Beacon) verifyHeader(chain consensus.ChainHeaderReader, header, parent *types.Header) error { 228 // Ensure that the header's extra-data section is of a reasonable size 229 if len(header.Extra) > 32 { 230 return fmt.Errorf("extra-data longer than 32 bytes (%d)", len(header.Extra)) 231 } 232 // Verify the seal parts. Ensure the nonce and uncle hash are the expected value. 233 if header.Nonce != beaconNonce { 234 return errInvalidNonce 235 } 236 if header.UncleHash != types.EmptyUncleHash { 237 return errInvalidUncleHash 238 } 239 // Verify the timestamp 240 if header.Time <= parent.Time { 241 return errInvalidTimestamp 242 } 243 // Verify the block's difficulty to ensure it's the default constant 244 if beaconDifficulty.Cmp(header.Difficulty) != 0 { 245 return fmt.Errorf("invalid difficulty: have %v, want %v", header.Difficulty, beaconDifficulty) 246 } 247 // Verify that the gas limit is <= 2^63-1 248 if header.GasLimit > params.MaxGasLimit { 249 return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, params.MaxGasLimit) 250 } 251 // Verify that the gasUsed is <= gasLimit 252 if header.GasUsed > header.GasLimit { 253 return fmt.Errorf("invalid gasUsed: have %d, gasLimit %d", header.GasUsed, header.GasLimit) 254 } 255 // Verify that the block number is parent's +1 256 if diff := new(big.Int).Sub(header.Number, parent.Number); diff.Cmp(common.Big1) != 0 { 257 return consensus.ErrInvalidNumber 258 } 259 // Verify the header's EIP-1559 attributes. 260 if err := misc.VerifyEip1559Header(chain.Config(), parent, header); err != nil { 261 return err 262 } 263 // Verify existence / non-existence of withdrawalsHash. 264 shanghai := chain.Config().IsShanghai(header.Time, types.DeserializeHeaderExtraInformation(header).ArbOSFormatVersion) 265 if shanghai && header.WithdrawalsHash == nil { 266 return fmt.Errorf("missing withdrawalsHash") 267 } 268 if !shanghai && header.WithdrawalsHash != nil { 269 return fmt.Errorf("invalid withdrawalsHash: have %x, expected nil", header.WithdrawalsHash) 270 } 271 return nil 272 } 273 274 // verifyHeaders is similar to verifyHeader, but verifies a batch of headers 275 // concurrently. The method returns a quit channel to abort the operations and 276 // a results channel to retrieve the async verifications. An additional parent 277 // header will be passed if the relevant header is not in the database yet. 278 func (beacon *Beacon) verifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, ancestor *types.Header) (chan<- struct{}, <-chan error) { 279 var ( 280 abort = make(chan struct{}) 281 results = make(chan error, len(headers)) 282 ) 283 go func() { 284 for i, header := range headers { 285 var parent *types.Header 286 if i == 0 { 287 if ancestor != nil { 288 parent = ancestor 289 } else { 290 parent = chain.GetHeader(headers[0].ParentHash, headers[0].Number.Uint64()-1) 291 } 292 } else if headers[i-1].Hash() == headers[i].ParentHash { 293 parent = headers[i-1] 294 } 295 if parent == nil { 296 select { 297 case <-abort: 298 return 299 case results <- consensus.ErrUnknownAncestor: 300 } 301 continue 302 } 303 err := beacon.verifyHeader(chain, header, parent) 304 select { 305 case <-abort: 306 return 307 case results <- err: 308 } 309 } 310 }() 311 return abort, results 312 } 313 314 // Prepare implements consensus.Engine, initializing the difficulty field of a 315 // header to conform to the beacon protocol. The changes are done inline. 316 func (beacon *Beacon) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error { 317 // Transition isn't triggered yet, use the legacy rules for preparation. 318 reached, err := IsTTDReached(chain, header.ParentHash, header.Number.Uint64()-1) 319 if err != nil { 320 return err 321 } 322 if !reached { 323 return beacon.ethone.Prepare(chain, header) 324 } 325 header.Difficulty = beaconDifficulty 326 return nil 327 } 328 329 // Finalize implements consensus.Engine, setting the final state on the header 330 func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal) { 331 // Finalize is different with Prepare, it can be used in both block generation 332 // and verification. So determine the consensus rules by header type. 333 if !beacon.IsPoSHeader(header) { 334 beacon.ethone.Finalize(chain, header, state, txs, uncles, nil) 335 return 336 } 337 // Withdrawals processing. 338 for _, w := range withdrawals { 339 // Convert amount from gwei to wei. 340 amount := new(big.Int).SetUint64(w.Amount) 341 amount = amount.Mul(amount, big.NewInt(params.GWei)) 342 state.AddBalance(w.Address, amount) 343 } 344 // The block reward is no longer handled here. It's done by the 345 // external consensus engine. 346 header.Root = state.IntermediateRoot(true) 347 } 348 349 // FinalizeAndAssemble implements consensus.Engine, setting the final state and 350 // assembling the block. 351 func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt, withdrawals []*types.Withdrawal) (*types.Block, error) { 352 // FinalizeAndAssemble is different with Prepare, it can be used in both block 353 // generation and verification. So determine the consensus rules by header type. 354 if !beacon.IsPoSHeader(header) { 355 return beacon.ethone.FinalizeAndAssemble(chain, header, state, txs, uncles, receipts, nil) 356 } 357 shanghai := chain.Config().IsShanghai(header.Time, types.DeserializeHeaderExtraInformation(header).ArbOSFormatVersion) 358 if shanghai { 359 // All blocks after Shanghai must include a withdrawals root. 360 if withdrawals == nil { 361 withdrawals = make([]*types.Withdrawal, 0) 362 } 363 } else { 364 if len(withdrawals) > 0 { 365 return nil, errors.New("withdrawals set before Shanghai activation") 366 } 367 } 368 // Finalize and assemble the block. 369 beacon.Finalize(chain, header, state, txs, uncles, withdrawals) 370 return types.NewBlockWithWithdrawals(header, txs, uncles, receipts, withdrawals, trie.NewStackTrie(nil)), nil 371 } 372 373 // Seal generates a new sealing request for the given input block and pushes 374 // the result into the given channel. 375 // 376 // Note, the method returns immediately and will send the result async. More 377 // than one result may also be returned depending on the consensus algorithm. 378 func (beacon *Beacon) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error { 379 if !beacon.IsPoSHeader(block.Header()) { 380 return beacon.ethone.Seal(chain, block, results, stop) 381 } 382 // The seal verification is done by the external consensus engine, 383 // return directly without pushing any block back. In another word 384 // beacon won't return any result by `results` channel which may 385 // blocks the receiver logic forever. 386 return nil 387 } 388 389 // SealHash returns the hash of a block prior to it being sealed. 390 func (beacon *Beacon) SealHash(header *types.Header) common.Hash { 391 return beacon.ethone.SealHash(header) 392 } 393 394 // CalcDifficulty is the difficulty adjustment algorithm. It returns 395 // the difficulty that a new block should have when created at time 396 // given the parent block's time and difficulty. 397 func (beacon *Beacon) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int { 398 // Transition isn't triggered yet, use the legacy rules for calculation 399 if reached, _ := IsTTDReached(chain, parent.Hash(), parent.Number.Uint64()); !reached { 400 return beacon.ethone.CalcDifficulty(chain, time, parent) 401 } 402 return beaconDifficulty 403 } 404 405 // APIs implements consensus.Engine, returning the user facing RPC APIs. 406 func (beacon *Beacon) APIs(chain consensus.ChainHeaderReader) []rpc.API { 407 return beacon.ethone.APIs(chain) 408 } 409 410 // Close shutdowns the consensus engine 411 func (beacon *Beacon) Close() error { 412 return beacon.ethone.Close() 413 } 414 415 // IsPoSHeader reports the header belongs to the PoS-stage with some special fields. 416 // This function is not suitable for a part of APIs like Prepare or CalcDifficulty 417 // because the header difficulty is not set yet. 418 func (beacon *Beacon) IsPoSHeader(header *types.Header) bool { 419 if header.Difficulty == nil { 420 panic("IsPoSHeader called with invalid difficulty") 421 } 422 return header.Difficulty.Cmp(beaconDifficulty) == 0 423 } 424 425 // InnerEngine returns the embedded eth1 consensus engine. 426 func (beacon *Beacon) InnerEngine() consensus.Engine { 427 return beacon.ethone 428 } 429 430 // SetThreads updates the mining threads. Delegate the call 431 // to the eth1 engine if it's threaded. 432 func (beacon *Beacon) SetThreads(threads int) { 433 type threaded interface { 434 SetThreads(threads int) 435 } 436 if th, ok := beacon.ethone.(threaded); ok { 437 th.SetThreads(threads) 438 } 439 } 440 441 // IsTTDReached checks if the TotalTerminalDifficulty has been surpassed on the `parentHash` block. 442 // It depends on the parentHash already being stored in the database. 443 // If the parentHash is not stored in the database a UnknownAncestor error is returned. 444 func IsTTDReached(chain consensus.ChainHeaderReader, parentHash common.Hash, parentNumber uint64) (bool, error) { 445 if chain.Config().TerminalTotalDifficulty == nil { 446 return false, nil 447 } 448 td := chain.GetTd(parentHash, parentNumber) 449 if td == nil { 450 return false, consensus.ErrUnknownAncestor 451 } 452 return td.Cmp(chain.Config().TerminalTotalDifficulty) >= 0, nil 453 }