diff --git a/consensus/beacon/consensus.go b/consensus/beacon/consensus.go index b485833a02..4ceaa9a452 100644 --- a/consensus/beacon/consensus.go +++ b/consensus/beacon/consensus.go @@ -61,7 +61,8 @@ var ( // is only used for necessary consensus checks. The legacy consensus engine can be any // engine implements the consensus interface (except the beacon itself). type Beacon struct { - ethone consensus.Engine // Original consensus engine used in eth1, e.g. ethash or clique + ethone consensus.Engine // Original consensus engine used in eth1, e.g. ethash or clique + ttdblock *uint64 // Merge block-number for testchain generation without TTDs } // New creates a consensus engine with the given embedded eth1 engine. @@ -72,6 +73,16 @@ func New(ethone consensus.Engine) *Beacon { return &Beacon{ethone: ethone} } +// TestingTTDBlock is a replacement mechanism for TTD-based pre-/post-merge +// splitting. With chain history deletion, TD calculations become impossible. +// This is fine for progressing the live chain, but to be able to generate test +// chains, we do need a split point. This method supports setting an explicit +// block number to use as the splitter *for testing*, instead of having to keep +// the notion of TDs in the client just for testing. +func (beacon *Beacon) TestingTTDBlock(number uint64) { + beacon.ttdblock = &number +} + // Author implements consensus.Engine, returning the verified author of the block. func (beacon *Beacon) Author(header *types.Header) (common.Address, error) { if !beacon.IsPoSHeader(header) { @@ -83,8 +94,8 @@ func (beacon *Beacon) Author(header *types.Header) (common.Address, error) { // VerifyHeader checks whether a header conforms to the consensus rules of the // stock Ethereum consensus engine. func (beacon *Beacon) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header) error { - // During the live merge transision, the consensus engine used the terminal - // total difficulty to detect when PoW (PoA) switched to PoS. Maintainig the + // During the live merge transition, the consensus engine used the terminal + // total difficulty to detect when PoW (PoA) switched to PoS. Maintaining the // total difficulty values however require applying all the blocks from the // genesis to build up the TD. This stops being a possibility if the tail of // the chain is pruned already during sync. @@ -92,7 +103,7 @@ func (beacon *Beacon) VerifyHeader(chain consensus.ChainHeaderReader, header *ty // One heuristic that can be used to distinguis pre-merge and post-merge // blocks is whether their *difficulty* is >0 or ==0 respectively. This of // course would mean that we cannot prove anymore for a past chain that it - // truly transisioned at the correct TTD, but if we consider that ancient + // truly transitioned at the correct TTD, but if we consider that ancient // point in time finalized a long time ago, there should be no attempt from // the consensus client to rewrite very old history. // @@ -130,34 +141,16 @@ func errOut(n int, err error) chan error { // will be treated legacy PoW headers. // Note, this function will not verify the header validity but just split them. func (beacon *Beacon) splitHeaders(chain consensus.ChainHeaderReader, headers []*types.Header) ([]*types.Header, []*types.Header, error) { - // TTD is not defined yet, all headers should be in legacy format. - ttd := chain.Config().TerminalTotalDifficulty - ptd := chain.GetTd(headers[0].ParentHash, headers[0].Number.Uint64()-1) - if ptd == nil { - return nil, nil, consensus.ErrUnknownAncestor - } - // The entire header batch already crosses the transition. - if ptd.Cmp(ttd) >= 0 { - return nil, headers, nil - } var ( preHeaders = headers postHeaders []*types.Header - td = new(big.Int).Set(ptd) - tdPassed bool ) for i, header := range headers { - if tdPassed { + if header.Difficulty.Sign() == 0 { preHeaders = headers[:i] postHeaders = headers[i:] break } - td = td.Add(td, header.Difficulty) - if td.Cmp(ttd) >= 0 { - // This is the last PoW header, it still belongs to - // the preHeaders, so we cannot split+break yet. - tdPassed = true - } } return preHeaders, postHeaders, nil } @@ -350,12 +343,15 @@ func (beacon *Beacon) verifyHeaders(chain consensus.ChainHeaderReader, headers [ // Prepare implements consensus.Engine, initializing the difficulty field of a // header to conform to the beacon protocol. The changes are done inline. func (beacon *Beacon) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error { - // Transition isn't triggered yet, use the legacy rules for preparation. - reached, err := IsTTDReached(chain, header.ParentHash, header.Number.Uint64()-1) - if err != nil { - return err - } - if !reached { + // The beacon engine requires access to total difficulties to be able to + // seal pre-merge and post-merge blocks. With the transition to removing + // old blocks, TDs become unaccessible, thus making TTD based pre-/post- + // merge decisions impossible. + // + // We do not need to seal non-merge blocks anymore live, but we do need + // to be able to generate test chains, thus we're reverting to a testing- + // settable field to direct that. + if beacon.ttdblock != nil && *beacon.ttdblock >= header.Number.Uint64() { return beacon.ethone.Prepare(chain, header) } header.Difficulty = beaconDifficulty @@ -465,8 +461,15 @@ func (beacon *Beacon) SealHash(header *types.Header) common.Hash { // the difficulty that a new block should have when created at time // given the parent block's time and difficulty. func (beacon *Beacon) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int { - // Transition isn't triggered yet, use the legacy rules for calculation - if reached, _ := IsTTDReached(chain, parent.Hash(), parent.Number.Uint64()); !reached { + // The beacon engine requires access to total difficulties to be able to + // seal pre-merge and post-merge blocks. With the transition to removing + // old blocks, TDs become unaccessible, thus making TTD based pre-/post- + // merge decisions impossible. + // + // We do not need to seal non-merge blocks anymore live, but we do need + // to be able to generate test chains, thus we're reverting to a testing- + // settable field to direct that. + if beacon.ttdblock != nil && *beacon.ttdblock > parent.Number.Uint64() { return beacon.ethone.CalcDifficulty(chain, time, parent) } return beaconDifficulty @@ -507,14 +510,3 @@ func (beacon *Beacon) SetThreads(threads int) { th.SetThreads(threads) } } - -// IsTTDReached checks if the TotalTerminalDifficulty has been surpassed on the `parentHash` block. -// It depends on the parentHash already being stored in the database. -// If the parentHash is not stored in the database a UnknownAncestor error is returned. -func IsTTDReached(chain consensus.ChainHeaderReader, parentHash common.Hash, parentNumber uint64) (bool, error) { - td := chain.GetTd(parentHash, parentNumber) - if td == nil { - return false, consensus.ErrUnknownAncestor - } - return td.Cmp(chain.Config().TerminalTotalDifficulty) >= 0, nil -} diff --git a/consensus/consensus.go b/consensus/consensus.go index ff76d31f55..c59b9a4744 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -45,9 +45,6 @@ type ChainHeaderReader interface { // GetHeaderByHash retrieves a block header from the database by its hash. GetHeaderByHash(hash common.Hash) *types.Header - - // GetTd retrieves the total difficulty from the database by hash and number. - GetTd(hash common.Hash, number uint64) *big.Int } // ChainReader defines a small collection of methods needed to access the local diff --git a/core/chain_makers.go b/core/chain_makers.go index 5298874a40..d52554a307 100644 --- a/core/chain_makers.go +++ b/core/chain_makers.go @@ -726,7 +726,3 @@ func (cm *chainMaker) GetHeader(hash common.Hash, number uint64) *types.Header { func (cm *chainMaker) GetBlock(hash common.Hash, number uint64) *types.Block { return cm.blockByNumber(number) } - -func (cm *chainMaker) GetTd(hash common.Hash, number uint64) *big.Int { - return nil // not supported -} diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go index b1348d07f9..0bff95a065 100644 --- a/eth/catalyst/api_test.go +++ b/eth/catalyst/api_test.go @@ -66,6 +66,8 @@ func generateMergeChain(n int, merged bool) (*core.Genesis, []*types.Block) { if merged { config.TerminalTotalDifficulty = common.Big0 engine = beaconConsensus.NewFaker() + } else { + engine.(*beaconConsensus.Beacon).TestingTTDBlock(uint64(n)) } genesis := &core.Genesis{ Config: &config, @@ -101,7 +103,6 @@ func generateMergeChain(n int, merged bool) (*core.Genesis, []*types.Block) { } config.TerminalTotalDifficulty = totalDifficulty } - return genesis, blocks } diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go index fdba2e584b..185224d834 100644 --- a/eth/gasprice/gasprice_test.go +++ b/eth/gasprice/gasprice_test.go @@ -25,7 +25,6 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus/beacon" "github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/core" @@ -148,7 +147,10 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, cancunBlock *big.Int, pe config.LondonBlock = londonBlock config.ArrowGlacierBlock = londonBlock config.GrayGlacierBlock = londonBlock - var engine consensus.Engine = beacon.New(ethash.NewFaker()) + + engine := beacon.New(ethash.NewFaker()) + engine.TestingTTDBlock(testHead + 1) + td := params.GenesisDifficulty.Uint64() if cancunBlock != nil { diff --git a/eth/protocols/eth/peer.go b/eth/protocols/eth/peer.go index 58e1baf721..31a35eb186 100644 --- a/eth/protocols/eth/peer.go +++ b/eth/protocols/eth/peer.go @@ -18,7 +18,6 @@ package eth import ( "math/rand" - "sync" mapset "github.com/deckarep/golang-set/v2" "github.com/ethereum/go-ethereum/common" @@ -59,7 +58,6 @@ type Peer struct { resDispatch chan *response // Dispatch channel to fulfil pending requests and untrack them term chan struct{} // Termination channel to stop the broadcasters - lock sync.RWMutex // Mutex protecting the internal fields } // NewPeer creates a wrapper for a network connection and negotiated protocol diff --git a/eth/tracers/internal/tracetest/supply_test.go b/eth/tracers/internal/tracetest/supply_test.go index 6f06b7c0d5..d918f5aca4 100644 --- a/eth/tracers/internal/tracetest/supply_test.go +++ b/eth/tracers/internal/tracetest/supply_test.go @@ -544,9 +544,8 @@ func TestSupplySelfdestructItselfAndRevert(t *testing.T) { } func testSupplyTracer(t *testing.T, genesis *core.Genesis, gen func(*core.BlockGen)) ([]supplyInfo, *core.BlockChain, error) { - var ( - engine = beacon.New(ethash.NewFaker()) - ) + engine := beacon.New(ethash.NewFaker()) + engine.TestingTTDBlock(1) traceOutputPath := filepath.ToSlash(t.TempDir()) traceOutputFilename := path.Join(traceOutputPath, "supply.jsonl") diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 0303a0a6ea..f00022e3de 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -565,12 +565,6 @@ func (b testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.R receipts := rawdb.ReadReceipts(b.db, hash, header.Number.Uint64(), header.Time, b.chain.Config()) return receipts, nil } -func (b testBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int { - if b.pending != nil && hash == b.pending.Hash() { - return nil - } - return big.NewInt(1) -} func (b testBackend) GetEVM(ctx context.Context, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockContext *vm.BlockContext) *vm.EVM { if vmConfig == nil { vmConfig = b.chain.GetVMConfig() diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go index 7172fc883f..7355c2463c 100644 --- a/internal/ethapi/transaction_args_test.go +++ b/internal/ethapi/transaction_args_test.go @@ -369,7 +369,6 @@ func (b *backendMock) GetReceipts(ctx context.Context, hash common.Hash) (types. func (b *backendMock) GetLogs(ctx context.Context, blockHash common.Hash, number uint64) ([][]*types.Log, error) { return nil, nil } -func (b *backendMock) GetTd(ctx context.Context, hash common.Hash) *big.Int { return nil } func (b *backendMock) GetEVM(ctx context.Context, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM { return nil }