consensus, core, eth, internal: further removal of td

This commit is contained in:
Péter Szilágyi 2024-11-12 00:53:36 +07:00 committed by Gary Rong
parent 89a5e1a29b
commit 6539c399ae
9 changed files with 42 additions and 64 deletions

View file

@ -61,7 +61,8 @@ var (
// is only used for necessary consensus checks. The legacy consensus engine can be any // is only used for necessary consensus checks. The legacy consensus engine can be any
// engine implements the consensus interface (except the beacon itself). // engine implements the consensus interface (except the beacon itself).
type Beacon struct { 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. // 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} 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. // Author implements consensus.Engine, returning the verified author of the block.
func (beacon *Beacon) Author(header *types.Header) (common.Address, error) { func (beacon *Beacon) Author(header *types.Header) (common.Address, error) {
if !beacon.IsPoSHeader(header) { 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 // VerifyHeader checks whether a header conforms to the consensus rules of the
// stock Ethereum consensus engine. // stock Ethereum consensus engine.
func (beacon *Beacon) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header) error { func (beacon *Beacon) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header) error {
// During the live merge transision, the consensus engine used the terminal // During the live merge transition, the consensus engine used the terminal
// total difficulty to detect when PoW (PoA) switched to PoS. Maintainig the // total difficulty to detect when PoW (PoA) switched to PoS. Maintaining the
// total difficulty values however require applying all the blocks from 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 // genesis to build up the TD. This stops being a possibility if the tail of
// the chain is pruned already during sync. // 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 // 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 // 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 // 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 // point in time finalized a long time ago, there should be no attempt from
// the consensus client to rewrite very old history. // 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. // will be treated legacy PoW headers.
// Note, this function will not verify the header validity but just split them. // 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) { 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 ( var (
preHeaders = headers preHeaders = headers
postHeaders []*types.Header postHeaders []*types.Header
td = new(big.Int).Set(ptd)
tdPassed bool
) )
for i, header := range headers { for i, header := range headers {
if tdPassed { if header.Difficulty.Sign() == 0 {
preHeaders = headers[:i] preHeaders = headers[:i]
postHeaders = headers[i:] postHeaders = headers[i:]
break 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 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 // Prepare implements consensus.Engine, initializing the difficulty field of a
// header to conform to the beacon protocol. The changes are done inline. // header to conform to the beacon protocol. The changes are done inline.
func (beacon *Beacon) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error { func (beacon *Beacon) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
// Transition isn't triggered yet, use the legacy rules for preparation. // The beacon engine requires access to total difficulties to be able to
reached, err := IsTTDReached(chain, header.ParentHash, header.Number.Uint64()-1) // seal pre-merge and post-merge blocks. With the transition to removing
if err != nil { // old blocks, TDs become unaccessible, thus making TTD based pre-/post-
return err // merge decisions impossible.
} //
if !reached { // 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) return beacon.ethone.Prepare(chain, header)
} }
header.Difficulty = beaconDifficulty 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 // the difficulty that a new block should have when created at time
// given the parent block's time and difficulty. // given the parent block's time and difficulty.
func (beacon *Beacon) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int { 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 // The beacon engine requires access to total difficulties to be able to
if reached, _ := IsTTDReached(chain, parent.Hash(), parent.Number.Uint64()); !reached { // 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 beacon.ethone.CalcDifficulty(chain, time, parent)
} }
return beaconDifficulty return beaconDifficulty
@ -507,14 +510,3 @@ func (beacon *Beacon) SetThreads(threads int) {
th.SetThreads(threads) 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
}

View file

@ -45,9 +45,6 @@ type ChainHeaderReader interface {
// GetHeaderByHash retrieves a block header from the database by its hash. // GetHeaderByHash retrieves a block header from the database by its hash.
GetHeaderByHash(hash common.Hash) *types.Header 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 // ChainReader defines a small collection of methods needed to access the local

View file

@ -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 { func (cm *chainMaker) GetBlock(hash common.Hash, number uint64) *types.Block {
return cm.blockByNumber(number) return cm.blockByNumber(number)
} }
func (cm *chainMaker) GetTd(hash common.Hash, number uint64) *big.Int {
return nil // not supported
}

View file

@ -66,6 +66,8 @@ func generateMergeChain(n int, merged bool) (*core.Genesis, []*types.Block) {
if merged { if merged {
config.TerminalTotalDifficulty = common.Big0 config.TerminalTotalDifficulty = common.Big0
engine = beaconConsensus.NewFaker() engine = beaconConsensus.NewFaker()
} else {
engine.(*beaconConsensus.Beacon).TestingTTDBlock(uint64(n))
} }
genesis := &core.Genesis{ genesis := &core.Genesis{
Config: &config, Config: &config,
@ -101,7 +103,6 @@ func generateMergeChain(n int, merged bool) (*core.Genesis, []*types.Block) {
} }
config.TerminalTotalDifficulty = totalDifficulty config.TerminalTotalDifficulty = totalDifficulty
} }
return genesis, blocks return genesis, blocks
} }

View file

@ -25,7 +25,6 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "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/beacon"
"github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core" "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.LondonBlock = londonBlock
config.ArrowGlacierBlock = londonBlock config.ArrowGlacierBlock = londonBlock
config.GrayGlacierBlock = 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() td := params.GenesisDifficulty.Uint64()
if cancunBlock != nil { if cancunBlock != nil {

View file

@ -18,7 +18,6 @@ package eth
import ( import (
"math/rand" "math/rand"
"sync"
mapset "github.com/deckarep/golang-set/v2" mapset "github.com/deckarep/golang-set/v2"
"github.com/ethereum/go-ethereum/common" "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 resDispatch chan *response // Dispatch channel to fulfil pending requests and untrack them
term chan struct{} // Termination channel to stop the broadcasters 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 // NewPeer creates a wrapper for a network connection and negotiated protocol

View file

@ -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) { 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()) traceOutputPath := filepath.ToSlash(t.TempDir())
traceOutputFilename := path.Join(traceOutputPath, "supply.jsonl") traceOutputFilename := path.Join(traceOutputPath, "supply.jsonl")

View file

@ -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()) receipts := rawdb.ReadReceipts(b.db, hash, header.Number.Uint64(), header.Time, b.chain.Config())
return receipts, nil 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 { func (b testBackend) GetEVM(ctx context.Context, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockContext *vm.BlockContext) *vm.EVM {
if vmConfig == nil { if vmConfig == nil {
vmConfig = b.chain.GetVMConfig() vmConfig = b.chain.GetVMConfig()

View file

@ -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) { func (b *backendMock) GetLogs(ctx context.Context, blockHash common.Hash, number uint64) ([][]*types.Log, error) {
return nil, nil 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 { func (b *backendMock) GetEVM(ctx context.Context, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM {
return nil return nil
} }