@@ -72,6 +72,16 @@ func New(ethone consensus.Engine) *Beacon {
7272 return & Beacon {ethone : ethone }
7373}
7474
75+ // isPostMerge reports whether the given block number is assumed to be post-merge.
76+ // Here we check the MergeNetsplitBlock to allow configuring networks with a PoW or
77+ // PoA chain for unit testing purposes.
78+ func isPostMerge (config * params.ChainConfig , blockNum uint64 , timestamp uint64 ) bool {
79+ mergedAtGenesis := config .TerminalTotalDifficulty != nil && config .TerminalTotalDifficulty .Sign () == 0
80+ return mergedAtGenesis ||
81+ config .MergeNetsplitBlock != nil && blockNum >= config .MergeNetsplitBlock .Uint64 () ||
82+ config .ShanghaiTime != nil && timestamp >= * config .ShanghaiTime
83+ }
84+
7585// Author implements consensus.Engine, returning the verified author of the block.
7686func (beacon * Beacon ) Author (header * types.Header ) (common.Address , error ) {
7787 if ! beacon .IsPoSHeader (header ) {
@@ -83,78 +93,63 @@ func (beacon *Beacon) Author(header *types.Header) (common.Address, error) {
8393// VerifyHeader checks whether a header conforms to the consensus rules of the
8494// stock Ethereum consensus engine.
8595func (beacon * Beacon ) VerifyHeader (chain consensus.ChainHeaderReader , header * types.Header ) error {
86- reached , err := IsTTDReached (chain , header .ParentHash , header .Number .Uint64 ()- 1 )
87- if err != nil {
88- return err
89- }
90- if ! reached {
91- return beacon .ethone .VerifyHeader (chain , header )
92- }
93- // Short circuit if the parent is not known
96+ // During the live merge transition, the consensus engine used the terminal
97+ // total difficulty to detect when PoW (PoA) switched to PoS. Maintaining the
98+ // total difficulty values however require applying all the blocks from the
99+ // genesis to build up the TD. This stops being a possibility if the tail of
100+ // the chain is pruned already during sync.
101+ //
102+ // One heuristic that can be used to distinguish pre-merge and post-merge
103+ // blocks is whether their *difficulty* is >0 or ==0 respectively. This of
104+ // course would mean that we cannot prove anymore for a past chain that it
105+ // truly transitioned at the correct TTD, but if we consider that ancient
106+ // point in time finalized a long time ago, there should be no attempt from
107+ // the consensus client to rewrite very old history.
108+ //
109+ // One thing that's probably not needed but which we can add to make this
110+ // verification even stricter is to enforce that the chain can switch from
111+ // >0 to ==0 TD only once by forbidding an ==0 to be followed by a >0.
112+
113+ // Verify that we're not reverting to pre-merge from post-merge
94114 parent := chain .GetHeader (header .ParentHash , header .Number .Uint64 ()- 1 )
95115 if parent == nil {
96116 return consensus .ErrUnknownAncestor
97117 }
98- // Sanity checks passed, do a proper verification
99- return beacon .verifyHeader (chain , header , parent )
100- }
101-
102- // errOut constructs an error channel with prefilled errors inside.
103- func errOut (n int , err error ) chan error {
104- errs := make (chan error , n )
105- for i := 0 ; i < n ; i ++ {
106- errs <- err
118+ if parent .Difficulty .Sign () == 0 && header .Difficulty .Sign () > 0 {
119+ return consensus .ErrInvalidTerminalBlock
120+ }
121+ // Check >0 TDs with pre-merge, --0 TDs with post-merge rules
122+ if header .Difficulty .Sign () > 0 {
123+ return beacon .ethone .VerifyHeader (chain , header )
107124 }
108- return errs
125+ return beacon . verifyHeader ( chain , header , parent )
109126}
110127
111128// splitHeaders splits the provided header batch into two parts according to
112- // the configured ttd. It requires the parent of header batch along with its
113- // td are stored correctly in chain. If ttd is not configured yet, all headers
114- // will be treated legacy PoW headers.
129+ // the difficulty field.
130+ //
115131// Note, this function will not verify the header validity but just split them.
116- func (beacon * Beacon ) splitHeaders (chain consensus.ChainHeaderReader , headers []* types.Header ) ([]* types.Header , []* types.Header , error ) {
117- // TTD is not defined yet, all headers should be in legacy format.
118- ttd := chain .Config ().TerminalTotalDifficulty
119- ptd := chain .GetTd (headers [0 ].ParentHash , headers [0 ].Number .Uint64 ()- 1 )
120- if ptd == nil {
121- return nil , nil , consensus .ErrUnknownAncestor
122- }
123- // The entire header batch already crosses the transition.
124- if ptd .Cmp (ttd ) >= 0 {
125- return nil , headers , nil
126- }
132+ func (beacon * Beacon ) splitHeaders (headers []* types.Header ) ([]* types.Header , []* types.Header ) {
127133 var (
128134 preHeaders = headers
129135 postHeaders []* types.Header
130- td = new (big.Int ).Set (ptd )
131- tdPassed bool
132136 )
133137 for i , header := range headers {
134- if tdPassed {
138+ if header . Difficulty . Sign () == 0 {
135139 preHeaders = headers [:i ]
136140 postHeaders = headers [i :]
137141 break
138142 }
139- td = td .Add (td , header .Difficulty )
140- if td .Cmp (ttd ) >= 0 {
141- // This is the last PoW header, it still belongs to
142- // the preHeaders, so we cannot split+break yet.
143- tdPassed = true
144- }
145143 }
146- return preHeaders , postHeaders , nil
144+ return preHeaders , postHeaders
147145}
148146
149147// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
150148// concurrently. The method returns a quit channel to abort the operations and
151149// a results channel to retrieve the async verifications.
152150// VerifyHeaders expect the headers to be ordered and continuous.
153151func (beacon * Beacon ) VerifyHeaders (chain consensus.ChainHeaderReader , headers []* types.Header ) (chan <- struct {}, <- chan error ) {
154- preHeaders , postHeaders , err := beacon .splitHeaders (chain , headers )
155- if err != nil {
156- return make (chan struct {}), errOut (len (headers ), err )
157- }
152+ preHeaders , postHeaders := beacon .splitHeaders (headers )
158153 if len (postHeaders ) == 0 {
159154 return beacon .ethone .VerifyHeaders (chain , headers )
160155 }
@@ -449,8 +444,7 @@ func (beacon *Beacon) SealHash(header *types.Header) common.Hash {
449444// the difficulty that a new block should have when created at time
450445// given the parent block's time and difficulty.
451446func (beacon * Beacon ) CalcDifficulty (chain consensus.ChainHeaderReader , time uint64 , parent * types.Header ) * big.Int {
452- // Transition isn't triggered yet, use the legacy rules for calculation
453- if reached , _ := IsTTDReached (chain , parent .Hash (), parent .Number .Uint64 ()); ! reached {
447+ if ! isPostMerge (chain .Config (), parent .Number .Uint64 ()+ 1 , time ) {
454448 return beacon .ethone .CalcDifficulty (chain , time , parent )
455449 }
456450 return beaconDifficulty
0 commit comments