mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 01:43:47 +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
|
||||
// stock Ethereum consensus engine.
|
||||
func (beacon *Beacon) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header) error {
|
||||
reached, err := IsTTDReached(chain, header.ParentHash, header.Number.Uint64()-1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !reached {
|
||||
return beacon.ethone.VerifyHeader(chain, header)
|
||||
}
|
||||
// Short circuit if the parent is not known
|
||||
// During the live merge transision, the consensus engine used the terminal
|
||||
// total difficulty to detect when PoW (PoA) switched to PoS. Maintainig 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.
|
||||
//
|
||||
// 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
|
||||
// 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)
|
||||
if parent == nil {
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// attempt to push us back to before the merge.
|
||||
if block.Difficulty().BitLen() > 0 || block.NumberU64() == 0 {
|
||||
var (
|
||||
td = api.eth.BlockChain().GetTd(update.HeadBlockHash, block.NumberU64())
|
||||
ptd = api.eth.BlockChain().GetTd(block.ParentHash(), block.NumberU64()-1)
|
||||
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 block.Difficulty().BitLen() > 0 && block.NumberU64() > 0 {
|
||||
ph := api.eth.BlockChain().GetHeader(block.ParentHash(), block.NumberU64()-1)
|
||||
if ph == nil {
|
||||
return engine.STATUS_INVALID, errors.New("parent unavailable for difficulty check")
|
||||
}
|
||||
if td.Cmp(ttd) < 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 {
|
||||
if ph.Difficulty.Sign() == 0 && block.Difficulty().Sign() > 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
|
||||
}
|
||||
|
|
@ -901,21 +892,6 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
|
|||
if parent == 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() {
|
||||
log.Warn("Invalid timestamp", "parent", block.Time(), "block", block.Time())
|
||||
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) {
|
||||
genesis, blocks := generateMergeChain(10, false)
|
||||
// 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
|
||||
// newPayLoad and forkchoiceUpdate. This is to test that the api behaves
|
||||
// well even of the caller is not being 'serial'.
|
||||
|
|
|
|||
|
|
@ -65,7 +65,6 @@ type backend interface {
|
|||
SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription
|
||||
CurrentHeader() *types.Header
|
||||
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
|
||||
GetTd(ctx context.Context, hash common.Hash) *big.Int
|
||||
Stats() (pending int, queued int)
|
||||
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 {
|
||||
// Gather the block infos from the local blockchain
|
||||
var (
|
||||
td *big.Int
|
||||
txs []txStats
|
||||
uncles []*types.Header
|
||||
)
|
||||
|
|
@ -644,8 +642,6 @@ func (s *Service) assembleBlockStats(header *types.Header) *blockStats {
|
|||
if block == nil {
|
||||
return nil
|
||||
}
|
||||
td = fullBackend.GetTd(context.Background(), header.Hash())
|
||||
|
||||
txs = make([]txStats, len(block.Transactions()))
|
||||
for i, tx := range block.Transactions() {
|
||||
txs[i].Hash = tx.Hash()
|
||||
|
|
@ -656,7 +652,6 @@ func (s *Service) assembleBlockStats(header *types.Header) *blockStats {
|
|||
if header == nil {
|
||||
header = s.backend.CurrentHeader()
|
||||
}
|
||||
td = s.backend.GetTd(context.Background(), header.Hash())
|
||||
txs = []txStats{}
|
||||
}
|
||||
// Assemble and return the block stats
|
||||
|
|
@ -671,7 +666,7 @@ func (s *Service) assembleBlockStats(header *types.Header) *blockStats {
|
|||
GasUsed: header.GasUsed,
|
||||
GasLimit: header.GasLimit,
|
||||
Diff: header.Difficulty.String(),
|
||||
TotalDiff: td.String(),
|
||||
TotalDiff: "0", // unknown post-merge with pruned chain tail
|
||||
Txs: txs,
|
||||
TxHash: header.TxHash,
|
||||
Root: header.Root,
|
||||
|
|
|
|||
Loading…
Reference in a new issue