Skip to content

Commit 3afd667

Browse files
MariusVanDerWijdenjagdeep sidhu
authored and
jagdeep sidhu
committedMay 31, 2022
eth/catalyst: fix edge case in NewPayload (ethereum#24955)
Fixes an issue where we would accept a NewPayload where the grandparent is already post ttd, and the parent still has a Difficulty
1 parent 77005be commit 3afd667

File tree

2 files changed

+62
-13
lines changed

2 files changed

+62
-13
lines changed
 

‎eth/catalyst/api.go

+14-5
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,14 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, pa
138138
log.Error("TDs unavailable for TTD check", "number", block.NumberU64(), "hash", update.HeadBlockHash, "td", td, "parent", block.ParentHash(), "ptd", ptd)
139139
return beacon.STATUS_INVALID, errors.New("TDs unavailable for TDD check")
140140
}
141-
if td.Cmp(ttd) < 0 || (block.NumberU64() > 0 && ptd.Cmp(ttd) > 0) {
141+
if td.Cmp(ttd) < 0 {
142142
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)))
143143
return beacon.ForkChoiceResponse{PayloadStatus: beacon.INVALID_TERMINAL_BLOCK, PayloadID: nil}, nil
144144
}
145+
if block.NumberU64() > 0 && ptd.Cmp(ttd) >= 0 {
146+
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)))
147+
return beacon.ForkChoiceResponse{PayloadStatus: beacon.INVALID_TERMINAL_BLOCK, PayloadID: nil}, nil
148+
}
145149
}
146150

147151
if rawdb.ReadCanonicalHash(api.eth.ChainDb(), block.NumberU64()) != update.HeadBlockHash {
@@ -295,11 +299,16 @@ func (api *ConsensusAPI) NewPayloadV1(params beacon.ExecutableDataV1) (beacon.Pa
295299
// We have an existing parent, do some sanity checks to avoid the beacon client
296300
// triggering too early
297301
var (
298-
td = api.eth.BlockChain().GetTd(parent.Hash(), parent.NumberU64())
299-
ttd = api.eth.BlockChain().Config().TerminalTotalDifficulty
302+
ptd = api.eth.BlockChain().GetTd(parent.Hash(), parent.NumberU64())
303+
ttd = api.eth.BlockChain().Config().TerminalTotalDifficulty
304+
gptd = api.eth.BlockChain().GetTd(parent.ParentHash(), parent.NumberU64()-1)
300305
)
301-
if td.Cmp(ttd) < 0 {
302-
log.Warn("Ignoring pre-merge payload", "number", params.Number, "hash", params.BlockHash, "td", td, "ttd", ttd)
306+
if ptd.Cmp(ttd) < 0 {
307+
log.Warn("Ignoring pre-merge payload", "number", params.Number, "hash", params.BlockHash, "td", ptd, "ttd", ttd)
308+
return beacon.INVALID_TERMINAL_BLOCK, nil
309+
}
310+
if parent.Difficulty().BitLen() > 0 && gptd != nil && gptd.Cmp(ttd) >= 0 {
311+
log.Error("Ignoring pre-merge parent block", "number", params.Number, "hash", params.BlockHash, "td", ptd, "ttd", ttd)
303312
return beacon.INVALID_TERMINAL_BLOCK, nil
304313
}
305314
if block.Time() <= parent.Time() {

‎eth/catalyst/api_test.go

+48-8
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,6 @@ func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan co
205205
func TestInvalidPayloadTimestamp(t *testing.T) {
206206
genesis, preMergeBlocks := generatePreMergeChain(10)
207207
n, ethservice := startEthService(t, genesis, preMergeBlocks)
208-
ethservice.Merger().ReachTTD()
209208
defer n.Close()
210209
var (
211210
api = NewConsensusAPI(ethservice)
@@ -250,7 +249,6 @@ func TestInvalidPayloadTimestamp(t *testing.T) {
250249
func TestEth2NewBlock(t *testing.T) {
251250
genesis, preMergeBlocks := generatePreMergeChain(10)
252251
n, ethservice := startEthService(t, genesis, preMergeBlocks)
253-
ethservice.Merger().ReachTTD()
254252
defer n.Close()
255253

256254
var (
@@ -427,7 +425,6 @@ func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block)
427425
func TestFullAPI(t *testing.T) {
428426
genesis, preMergeBlocks := generatePreMergeChain(10)
429427
n, ethservice := startEthService(t, genesis, preMergeBlocks)
430-
ethservice.Merger().ReachTTD()
431428
defer n.Close()
432429
var (
433430
parent = ethservice.BlockChain().CurrentBlock()
@@ -480,7 +477,6 @@ func setupBlocks(t *testing.T, ethservice *eth.Ethereum, n int, parent *types.Bl
480477
func TestExchangeTransitionConfig(t *testing.T) {
481478
genesis, preMergeBlocks := generatePreMergeChain(10)
482479
n, ethservice := startEthService(t, genesis, preMergeBlocks)
483-
ethservice.Merger().ReachTTD()
484480
defer n.Close()
485481
var (
486482
api = NewConsensusAPI(ethservice)
@@ -543,7 +539,6 @@ CommonAncestor◄─▲── P1 ◄── P2 ◄─ P3 ◄─ ... ◄─ Pn
543539
func TestNewPayloadOnInvalidChain(t *testing.T) {
544540
genesis, preMergeBlocks := generatePreMergeChain(10)
545541
n, ethservice := startEthService(t, genesis, preMergeBlocks)
546-
ethservice.Merger().ReachTTD()
547542
defer n.Close()
548543

549544
var (
@@ -618,7 +613,6 @@ func assembleBlock(api *ConsensusAPI, parentHash common.Hash, params *beacon.Pay
618613
func TestEmptyBlocks(t *testing.T) {
619614
genesis, preMergeBlocks := generatePreMergeChain(10)
620615
n, ethservice := startEthService(t, genesis, preMergeBlocks)
621-
ethservice.Merger().ReachTTD()
622616
defer n.Close()
623617

624618
commonAncestor := ethservice.BlockChain().CurrentBlock()
@@ -734,8 +728,6 @@ func TestTrickRemoteBlockCache(t *testing.T) {
734728
genesis, preMergeBlocks := generatePreMergeChain(10)
735729
nodeA, ethserviceA := startEthService(t, genesis, preMergeBlocks)
736730
nodeB, ethserviceB := startEthService(t, genesis, preMergeBlocks)
737-
ethserviceA.Merger().ReachTTD()
738-
ethserviceB.Merger().ReachTTD()
739731
defer nodeA.Close()
740732
defer nodeB.Close()
741733
for nodeB.Server().NodeInfo().Ports.Listener == 0 {
@@ -794,3 +786,51 @@ func TestTrickRemoteBlockCache(t *testing.T) {
794786
time.Sleep(100 * time.Millisecond)
795787
}
796788
}
789+
790+
func TestNewPayloadOnInvalidTerminalBlock(t *testing.T) {
791+
genesis, preMergeBlocks := generatePreMergeChain(100)
792+
fmt.Println(genesis.Config.TerminalTotalDifficulty)
793+
genesis.Config.TerminalTotalDifficulty = preMergeBlocks[0].Difficulty() //.Sub(genesis.Config.TerminalTotalDifficulty, preMergeBlocks[len(preMergeBlocks)-1].Difficulty())
794+
795+
fmt.Println(genesis.Config.TerminalTotalDifficulty)
796+
n, ethservice := startEthService(t, genesis, preMergeBlocks)
797+
defer n.Close()
798+
799+
var (
800+
api = NewConsensusAPI(ethservice)
801+
parent = preMergeBlocks[len(preMergeBlocks)-1]
802+
)
803+
804+
// Test parent already post TTD in FCU
805+
fcState := beacon.ForkchoiceStateV1{
806+
HeadBlockHash: parent.Hash(),
807+
SafeBlockHash: common.Hash{},
808+
FinalizedBlockHash: common.Hash{},
809+
}
810+
resp, err := api.ForkchoiceUpdatedV1(fcState, nil)
811+
if err != nil {
812+
t.Fatalf("error sending forkchoice, err=%v", err)
813+
}
814+
if resp.PayloadStatus != beacon.INVALID_TERMINAL_BLOCK {
815+
t.Fatalf("error sending invalid forkchoice, invalid status: %v", resp.PayloadStatus.Status)
816+
}
817+
818+
// Test parent already post TTD in NewPayload
819+
params := beacon.PayloadAttributesV1{
820+
Timestamp: parent.Time() + 1,
821+
Random: crypto.Keccak256Hash([]byte{byte(1)}),
822+
SuggestedFeeRecipient: parent.Coinbase(),
823+
}
824+
empty, err := api.eth.Miner().GetSealingBlockSync(parent.Hash(), params.Timestamp, params.SuggestedFeeRecipient, params.Random, true)
825+
if err != nil {
826+
t.Fatalf("error preparing payload, err=%v", err)
827+
}
828+
data := *beacon.BlockToExecutableData(empty)
829+
resp2, err := api.NewPayloadV1(data)
830+
if err != nil {
831+
t.Fatalf("error sending NewPayload, err=%v", err)
832+
}
833+
if resp2 != beacon.INVALID_TERMINAL_BLOCK {
834+
t.Fatalf("error sending invalid forkchoice, invalid status: %v", resp.PayloadStatus.Status)
835+
}
836+
}

0 commit comments

Comments
 (0)
Please sign in to comment.