mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 09:53:48 +00:00
consensus, eth/catalyst: remove TD checks and enforcement
This commit is contained in:
parent
a840e9b59f
commit
776d8ec1c1
4 changed files with 31 additions and 126 deletions
|
|
@ -83,19 +83,35 @@ 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 {
|
||||||
reached, err := IsTTDReached(chain, header.ParentHash, header.Number.Uint64()-1)
|
// During the live merge transision, the consensus engine used the terminal
|
||||||
if err != nil {
|
// total difficulty to detect when PoW (PoA) switched to PoS. Maintainig the
|
||||||
return err
|
// 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
|
||||||
if !reached {
|
// the chain is pruned already during sync.
|
||||||
return beacon.ethone.VerifyHeader(chain, header)
|
//
|
||||||
}
|
// One heuristic that can be used to distinguis pre-merge and post-merge
|
||||||
// Short circuit if the parent is not known
|
// 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
|
||||||
|
// point in time finalized a long time ago, there should be no attempt from
|
||||||
|
// the consensus client to rewrite very old history.
|
||||||
|
//
|
||||||
|
// One thing that's probably not needed but which we can add to make this
|
||||||
|
// verification even stricter is to enforce that the chain can switch from
|
||||||
|
// >0 to ==0 TD only once by forbidding an ==0 to be followed by a >0.
|
||||||
|
|
||||||
|
// Verify that we're not reverting to pre-merge from post-merge
|
||||||
parent := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1)
|
parent := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1)
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
return consensus.ErrUnknownAncestor
|
return consensus.ErrUnknownAncestor
|
||||||
}
|
}
|
||||||
// Sanity checks passed, do a proper verification
|
if parent.Difficulty.Sign() == 0 && header.Difficulty.Sign() > 0 {
|
||||||
|
return consensus.ErrInvalidTerminalBlock
|
||||||
|
}
|
||||||
|
// Check >0 TDs with pre-merge, --0 TDs with post-merge rules
|
||||||
|
if header.Difficulty.Sign() > 0 {
|
||||||
|
return beacon.ethone.VerifyHeader(chain, header)
|
||||||
|
}
|
||||||
return beacon.verifyHeader(chain, header, parent)
|
return beacon.verifyHeader(chain, header, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -361,21 +361,12 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
|
||||||
}
|
}
|
||||||
// Block is known locally, just sanity check that the beacon client does not
|
// Block is known locally, just sanity check that the beacon client does not
|
||||||
// attempt to push us back to before the merge.
|
// attempt to push us back to before the merge.
|
||||||
if block.Difficulty().BitLen() > 0 || block.NumberU64() == 0 {
|
if block.Difficulty().BitLen() > 0 && block.NumberU64() > 0 {
|
||||||
var (
|
ph := api.eth.BlockChain().GetHeader(block.ParentHash(), block.NumberU64()-1)
|
||||||
td = api.eth.BlockChain().GetTd(update.HeadBlockHash, block.NumberU64())
|
if ph == nil {
|
||||||
ptd = api.eth.BlockChain().GetTd(block.ParentHash(), block.NumberU64()-1)
|
return engine.STATUS_INVALID, errors.New("parent unavailable for difficulty check")
|
||||||
ttd = api.eth.BlockChain().Config().TerminalTotalDifficulty
|
|
||||||
)
|
|
||||||
if td == nil || (block.NumberU64() > 0 && ptd == nil) {
|
|
||||||
log.Error("TDs unavailable for TTD check", "number", block.NumberU64(), "hash", update.HeadBlockHash, "td", td, "parent", block.ParentHash(), "ptd", ptd)
|
|
||||||
return engine.STATUS_INVALID, errors.New("TDs unavailable for TDD check")
|
|
||||||
}
|
}
|
||||||
if td.Cmp(ttd) < 0 {
|
if ph.Difficulty.Sign() == 0 && block.Difficulty().Sign() > 0 {
|
||||||
log.Error("Refusing beacon update to pre-merge", "number", block.NumberU64(), "hash", update.HeadBlockHash, "diff", block.Difficulty(), "age", common.PrettyAge(time.Unix(int64(block.Time()), 0)))
|
|
||||||
return engine.ForkChoiceResponse{PayloadStatus: engine.INVALID_TERMINAL_BLOCK, PayloadID: nil}, nil
|
|
||||||
}
|
|
||||||
if block.NumberU64() > 0 && ptd.Cmp(ttd) >= 0 {
|
|
||||||
log.Error("Parent block is already post-ttd", "number", block.NumberU64(), "hash", update.HeadBlockHash, "diff", block.Difficulty(), "age", common.PrettyAge(time.Unix(int64(block.Time()), 0)))
|
log.Error("Parent block is already post-ttd", "number", block.NumberU64(), "hash", update.HeadBlockHash, "diff", block.Difficulty(), "age", common.PrettyAge(time.Unix(int64(block.Time()), 0)))
|
||||||
return engine.ForkChoiceResponse{PayloadStatus: engine.INVALID_TERMINAL_BLOCK, PayloadID: nil}, nil
|
return engine.ForkChoiceResponse{PayloadStatus: engine.INVALID_TERMINAL_BLOCK, PayloadID: nil}, nil
|
||||||
}
|
}
|
||||||
|
|
@ -901,21 +892,6 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
return api.delayPayloadImport(block), nil
|
return api.delayPayloadImport(block), nil
|
||||||
}
|
}
|
||||||
// We have an existing parent, do some sanity checks to avoid the beacon client
|
|
||||||
// triggering too early
|
|
||||||
var (
|
|
||||||
ptd = api.eth.BlockChain().GetTd(parent.Hash(), parent.NumberU64())
|
|
||||||
ttd = api.eth.BlockChain().Config().TerminalTotalDifficulty
|
|
||||||
gptd = api.eth.BlockChain().GetTd(parent.ParentHash(), parent.NumberU64()-1)
|
|
||||||
)
|
|
||||||
if ptd.Cmp(ttd) < 0 {
|
|
||||||
log.Warn("Ignoring pre-merge payload", "number", params.Number, "hash", params.BlockHash, "td", ptd, "ttd", ttd)
|
|
||||||
return engine.INVALID_TERMINAL_BLOCK, nil
|
|
||||||
}
|
|
||||||
if parent.Difficulty().BitLen() > 0 && gptd != nil && gptd.Cmp(ttd) >= 0 {
|
|
||||||
log.Error("Ignoring pre-merge parent block", "number", params.Number, "hash", params.BlockHash, "td", ptd, "ttd", ttd)
|
|
||||||
return engine.INVALID_TERMINAL_BLOCK, nil
|
|
||||||
}
|
|
||||||
if block.Time() <= parent.Time() {
|
if block.Time() <= parent.Time() {
|
||||||
log.Warn("Invalid timestamp", "parent", block.Time(), "block", block.Time())
|
log.Warn("Invalid timestamp", "parent", block.Time(), "block", block.Time())
|
||||||
return api.invalid(errors.New("invalid timestamp"), parent.Header()), nil
|
return api.invalid(errors.New("invalid timestamp"), parent.Header()), nil
|
||||||
|
|
|
||||||
|
|
@ -164,24 +164,6 @@ func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetHeadBeforeTotalDifficulty(t *testing.T) {
|
|
||||||
genesis, blocks := generateMergeChain(10, false)
|
|
||||||
n, ethservice := startEthService(t, genesis, blocks)
|
|
||||||
defer n.Close()
|
|
||||||
|
|
||||||
api := NewConsensusAPI(ethservice)
|
|
||||||
fcState := engine.ForkchoiceStateV1{
|
|
||||||
HeadBlockHash: blocks[5].Hash(),
|
|
||||||
SafeBlockHash: common.Hash{},
|
|
||||||
FinalizedBlockHash: common.Hash{},
|
|
||||||
}
|
|
||||||
if resp, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
|
|
||||||
t.Errorf("fork choice updated should not error: %v", err)
|
|
||||||
} else if resp.PayloadStatus.Status != engine.INVALID_TERMINAL_BLOCK.Status {
|
|
||||||
t.Errorf("fork choice updated before total terminal difficulty should be INVALID")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEth2PrepareAndGetPayload(t *testing.T) {
|
func TestEth2PrepareAndGetPayload(t *testing.T) {
|
||||||
genesis, blocks := generateMergeChain(10, false)
|
genesis, blocks := generateMergeChain(10, false)
|
||||||
// We need to properly set the terminal total difficulty
|
// We need to properly set the terminal total difficulty
|
||||||
|
|
@ -902,70 +884,6 @@ func TestInvalidBloom(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewPayloadOnInvalidTerminalBlock(t *testing.T) {
|
|
||||||
genesis, preMergeBlocks := generateMergeChain(100, false)
|
|
||||||
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
|
||||||
defer n.Close()
|
|
||||||
api := NewConsensusAPI(ethservice)
|
|
||||||
|
|
||||||
// Test parent already post TTD in FCU
|
|
||||||
parent := preMergeBlocks[len(preMergeBlocks)-2]
|
|
||||||
fcState := engine.ForkchoiceStateV1{
|
|
||||||
HeadBlockHash: parent.Hash(),
|
|
||||||
SafeBlockHash: common.Hash{},
|
|
||||||
FinalizedBlockHash: common.Hash{},
|
|
||||||
}
|
|
||||||
resp, err := api.ForkchoiceUpdatedV1(fcState, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("error sending forkchoice, err=%v", err)
|
|
||||||
}
|
|
||||||
if resp.PayloadStatus != engine.INVALID_TERMINAL_BLOCK {
|
|
||||||
t.Fatalf("error sending invalid forkchoice, invalid status: %v", resp.PayloadStatus.Status)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test parent already post TTD in NewPayload
|
|
||||||
args := &miner.BuildPayloadArgs{
|
|
||||||
Parent: parent.Hash(),
|
|
||||||
Timestamp: parent.Time() + 1,
|
|
||||||
Random: crypto.Keccak256Hash([]byte{byte(1)}),
|
|
||||||
FeeRecipient: parent.Coinbase(),
|
|
||||||
}
|
|
||||||
payload, err := api.eth.Miner().BuildPayload(args, false)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("error preparing payload, err=%v", err)
|
|
||||||
}
|
|
||||||
data := *payload.Resolve().ExecutionPayload
|
|
||||||
// We need to recompute the blockhash, since the miner computes a wrong (correct) blockhash
|
|
||||||
txs, _ := decodeTransactions(data.Transactions)
|
|
||||||
header := &types.Header{
|
|
||||||
ParentHash: data.ParentHash,
|
|
||||||
UncleHash: types.EmptyUncleHash,
|
|
||||||
Coinbase: data.FeeRecipient,
|
|
||||||
Root: data.StateRoot,
|
|
||||||
TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
|
|
||||||
ReceiptHash: data.ReceiptsRoot,
|
|
||||||
Bloom: types.BytesToBloom(data.LogsBloom),
|
|
||||||
Difficulty: common.Big0,
|
|
||||||
Number: new(big.Int).SetUint64(data.Number),
|
|
||||||
GasLimit: data.GasLimit,
|
|
||||||
GasUsed: data.GasUsed,
|
|
||||||
Time: data.Timestamp,
|
|
||||||
BaseFee: data.BaseFeePerGas,
|
|
||||||
Extra: data.ExtraData,
|
|
||||||
MixDigest: data.Random,
|
|
||||||
}
|
|
||||||
block := types.NewBlockWithHeader(header).WithBody(types.Body{Transactions: txs})
|
|
||||||
data.BlockHash = block.Hash()
|
|
||||||
// Send the new payload
|
|
||||||
resp2, err := api.NewPayloadV1(data)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("error sending NewPayload, err=%v", err)
|
|
||||||
}
|
|
||||||
if resp2 != engine.INVALID_TERMINAL_BLOCK {
|
|
||||||
t.Fatalf("error sending invalid forkchoice, invalid status: %v", resp.PayloadStatus.Status)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestSimultaneousNewBlock does several parallel inserts, both as
|
// TestSimultaneousNewBlock does several parallel inserts, both as
|
||||||
// newPayLoad and forkchoiceUpdate. This is to test that the api behaves
|
// newPayLoad and forkchoiceUpdate. This is to test that the api behaves
|
||||||
// well even of the caller is not being 'serial'.
|
// well even of the caller is not being 'serial'.
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,6 @@ type backend interface {
|
||||||
SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription
|
SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription
|
||||||
CurrentHeader() *types.Header
|
CurrentHeader() *types.Header
|
||||||
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
|
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
|
||||||
GetTd(ctx context.Context, hash common.Hash) *big.Int
|
|
||||||
Stats() (pending int, queued int)
|
Stats() (pending int, queued int)
|
||||||
SyncProgress() ethereum.SyncProgress
|
SyncProgress() ethereum.SyncProgress
|
||||||
}
|
}
|
||||||
|
|
@ -628,7 +627,6 @@ func (s *Service) reportBlock(conn *connWrapper, header *types.Header) error {
|
||||||
func (s *Service) assembleBlockStats(header *types.Header) *blockStats {
|
func (s *Service) assembleBlockStats(header *types.Header) *blockStats {
|
||||||
// Gather the block infos from the local blockchain
|
// Gather the block infos from the local blockchain
|
||||||
var (
|
var (
|
||||||
td *big.Int
|
|
||||||
txs []txStats
|
txs []txStats
|
||||||
uncles []*types.Header
|
uncles []*types.Header
|
||||||
)
|
)
|
||||||
|
|
@ -644,8 +642,6 @@ func (s *Service) assembleBlockStats(header *types.Header) *blockStats {
|
||||||
if block == nil {
|
if block == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
td = fullBackend.GetTd(context.Background(), header.Hash())
|
|
||||||
|
|
||||||
txs = make([]txStats, len(block.Transactions()))
|
txs = make([]txStats, len(block.Transactions()))
|
||||||
for i, tx := range block.Transactions() {
|
for i, tx := range block.Transactions() {
|
||||||
txs[i].Hash = tx.Hash()
|
txs[i].Hash = tx.Hash()
|
||||||
|
|
@ -656,7 +652,6 @@ func (s *Service) assembleBlockStats(header *types.Header) *blockStats {
|
||||||
if header == nil {
|
if header == nil {
|
||||||
header = s.backend.CurrentHeader()
|
header = s.backend.CurrentHeader()
|
||||||
}
|
}
|
||||||
td = s.backend.GetTd(context.Background(), header.Hash())
|
|
||||||
txs = []txStats{}
|
txs = []txStats{}
|
||||||
}
|
}
|
||||||
// Assemble and return the block stats
|
// Assemble and return the block stats
|
||||||
|
|
@ -671,7 +666,7 @@ func (s *Service) assembleBlockStats(header *types.Header) *blockStats {
|
||||||
GasUsed: header.GasUsed,
|
GasUsed: header.GasUsed,
|
||||||
GasLimit: header.GasLimit,
|
GasLimit: header.GasLimit,
|
||||||
Diff: header.Difficulty.String(),
|
Diff: header.Difficulty.String(),
|
||||||
TotalDiff: td.String(),
|
TotalDiff: "0", // unknown post-merge with pruned chain tail
|
||||||
Txs: txs,
|
Txs: txs,
|
||||||
TxHash: header.TxHash,
|
TxHash: header.TxHash,
|
||||||
Root: header.Root,
|
Root: header.Root,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue