Skip to content

Commit 9641cac

Browse files
karalabefjl
authored andcommitted
core/forkid: add two clauses for more precise validation (#20220)
1 parent e306304 commit 9641cac

File tree

2 files changed

+21
-3
lines changed

2 files changed

+21
-3
lines changed

core/forkid/forkid.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,10 +120,13 @@ func newFilter(config *params.ChainConfig, genesis common.Hash, headfn func() ui
120120
// Create a validator that will filter out incompatible chains
121121
return func(id ID) error {
122122
// Run the fork checksum validation ruleset:
123-
// 1. If local and remote FORK_CSUM matches, connect.
123+
// 1. If local and remote FORK_CSUM matches, compare local head to FORK_NEXT.
124124
// The two nodes are in the same fork state currently. They might know
125125
// of differing future forks, but that's not relevant until the fork
126126
// triggers (might be postponed, nodes might be updated to match).
127+
// 1a. A remotely announced but remotely not passed block is already passed
128+
// locally, disconnect, since the chains are incompatible.
129+
// 1b. No remotely announced fork; or not yet passed locally, connect.
127130
// 2. If the remote FORK_CSUM is a subset of the local past forks and the
128131
// remote FORK_NEXT matches with the locally following fork block number,
129132
// connect.
@@ -145,7 +148,12 @@ func newFilter(config *params.ChainConfig, genesis common.Hash, headfn func() ui
145148
// Found the first unpassed fork block, check if our current state matches
146149
// the remote checksum (rule #1).
147150
if sums[i] == id.Hash {
148-
// Yay, fork checksum matched, ignore any upcoming fork
151+
// Fork checksum matched, check if a remote future fork block already passed
152+
// locally without the local node being aware of it (rule #1a).
153+
if id.Next > 0 && head >= id.Next {
154+
return ErrLocalIncompatibleOrStale
155+
}
156+
// Haven't passed locally a remote-only fork, accept the connection (rule #1b).
149157
return nil
150158
}
151159
// The local and remote nodes are in different forks currently, check if the

core/forkid/forkid_test.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ func TestValidation(t *testing.T) {
151151

152152
// Local is mainnet Petersburg, remote announces Byzantium + knowledge about Petersburg. Remote
153153
// is simply out of sync, accept.
154-
{7987396, ID{Hash: checksumToBytes(0x668db0af), Next: 7280000}, nil},
154+
{7987396, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}, nil},
155155

156156
// Local is mainnet Petersburg, remote announces Spurious + knowledge about Byzantium. Remote
157157
// is definitely out of sync. It may or may not need the Petersburg update, we don't know yet.
@@ -178,6 +178,16 @@ func TestValidation(t *testing.T) {
178178

179179
// Local is mainnet Petersburg, remote is Rinkeby Petersburg.
180180
{7987396, ID{Hash: checksumToBytes(0xafec6b27), Next: 0}, ErrLocalIncompatibleOrStale},
181+
182+
// Local is mainnet Petersburg, far in the future. Remote announces Gopherium (non existing fork)
183+
// at some future block 88888888, for itself, but past block for local. Local is incompatible.
184+
//
185+
// This case detects non-upgraded nodes with majority hash power (typical Ropsten mess).
186+
{88888888, ID{Hash: checksumToBytes(0x668db0af), Next: 88888888}, ErrLocalIncompatibleOrStale},
187+
188+
// Local is mainnet Byzantium. Remote is also in Byzantium, but announces Gopherium (non existing
189+
// fork) at block 7279999, before Petersburg. Local is incompatible.
190+
{7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: 7279999}, ErrLocalIncompatibleOrStale},
181191
}
182192
for i, tt := range tests {
183193
filter := newFilter(params.MainnetChainConfig, params.MainnetGenesisHash, func() uint64 { return tt.head })

0 commit comments

Comments
 (0)