Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
G
Geth-Modification
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
张蕾
Geth-Modification
Commits
fcf3d004
Unverified
Commit
fcf3d004
authored
Jan 03, 2023
by
Péter Szilágyi
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
eth, les: polish forkid a bit, fix races and transition validation
parent
d0211578
Changes
6
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
339 additions
and
357 deletions
+339
-357
forkid.go
core/forkid/forkid.go
+62
-66
forkid_test.go
core/forkid/forkid_test.go
+269
-284
headerchain.go
core/headerchain.go
+4
-4
handler.go
eth/handler.go
+1
-1
discovery.go
eth/protocols/eth/discovery.go
+2
-1
server_handler.go
les/server_handler.go
+1
-1
No files found.
core/forkid/forkid.go
View file @
fcf3d004
...
...
@@ -45,6 +45,12 @@ var (
ErrLocalIncompatibleOrStale
=
errors
.
New
(
"local incompatible or needs update"
)
)
// timestampThreshold is the Ethereum mainnet genesis timestamp. It is used to
// differentiate if a forkid.next field is a block number or a timestamp. Whilst
// very hacky, something's needed to split the validation during the transition
// period (block forks -> time forks).
const
timestampThreshold
=
1438269973
// Blockchain defines all necessary method to build a forkID.
type
Blockchain
interface
{
// Config retrieves the chain's fork configuration.
...
...
@@ -72,8 +78,8 @@ func NewID(config *params.ChainConfig, genesis common.Hash, head, time uint64) I
hash
:=
crc32
.
ChecksumIEEE
(
genesis
[
:
])
// Calculate the current fork checksum and the next fork block
forks
,
forksByTime
:=
gatherForks
(
config
)
for
_
,
fork
:=
range
forks
{
forks
ByBlock
,
forksByTime
:=
gatherForks
(
config
)
for
_
,
fork
:=
range
forks
ByBlock
{
if
fork
<=
head
{
// Fork already passed, checksum the previous hash and the fork number
hash
=
checksumUpdate
(
hash
,
fork
)
...
...
@@ -81,26 +87,26 @@ func NewID(config *params.ChainConfig, genesis common.Hash, head, time uint64) I
}
return
ID
{
Hash
:
checksumToBytes
(
hash
),
Next
:
fork
}
}
var
next
uint64
for
_
,
fork
:=
range
forksByTime
{
if
time
>=
fork
{
// Fork
passed, checksum previous hash and fork time
if
fork
<=
time
{
// Fork
already passed, checksum the previous hash and fork timestamp
hash
=
checksumUpdate
(
hash
,
fork
)
continue
}
next
=
fork
break
return
ID
{
Hash
:
checksumToBytes
(
hash
),
Next
:
fork
}
}
return
ID
{
Hash
:
checksumToBytes
(
hash
),
Next
:
next
}
return
ID
{
Hash
:
checksumToBytes
(
hash
),
Next
:
0
}
}
// NewIDWithChain calculates the Ethereum fork ID from an existing chain instance.
func
NewIDWithChain
(
chain
Blockchain
)
ID
{
head
:=
chain
.
CurrentHeader
()
return
NewID
(
chain
.
Config
(),
chain
.
Genesis
()
.
Hash
(),
chain
.
CurrentHeader
()
.
Number
.
Uint64
(),
chain
.
CurrentHeader
()
.
Time
,
head
.
Number
.
Uint64
(),
head
.
Time
,
)
}
...
...
@@ -111,7 +117,8 @@ func NewFilter(chain Blockchain) Filter {
chain
.
Config
(),
chain
.
Genesis
()
.
Hash
(),
func
()
(
uint64
,
uint64
)
{
return
chain
.
CurrentHeader
()
.
Number
.
Uint64
(),
chain
.
CurrentHeader
()
.
Time
head
:=
chain
.
CurrentHeader
()
return
head
.
Number
.
Uint64
(),
head
.
Time
},
)
}
...
...
@@ -128,23 +135,23 @@ func NewStaticFilter(config *params.ChainConfig, genesis common.Hash) Filter {
func
newFilter
(
config
*
params
.
ChainConfig
,
genesis
common
.
Hash
,
headfn
func
()
(
uint64
,
uint64
))
Filter
{
// Calculate the all the valid fork hash and fork next combos
var
(
forks
,
forksByTime
=
gatherForks
(
config
)
sums
=
make
([][
4
]
byte
,
len
(
forks
)
+
len
(
forksByTime
)
+
1
)
// 0th is the genesis
forksByBlock
,
forksByTime
=
gatherForks
(
config
)
forks
=
append
(
append
([]
uint64
{},
forksByBlock
...
),
forksByTime
...
)
sums
=
make
([][
4
]
byte
,
len
(
forks
)
+
1
)
// 0th is the genesis
)
allForks
:=
append
(
forks
,
forksByTime
...
)
hash
:=
crc32
.
ChecksumIEEE
(
genesis
[
:
])
sums
[
0
]
=
checksumToBytes
(
hash
)
for
i
,
fork
:=
range
allF
orks
{
for
i
,
fork
:=
range
f
orks
{
hash
=
checksumUpdate
(
hash
,
fork
)
sums
[
i
+
1
]
=
checksumToBytes
(
hash
)
}
// Add two sentries to simplify the fork checks and don't require special
// casing the last one.
forks
=
append
(
forks
,
math
.
MaxUint64
)
// Last fork will never be passed
if
len
(
forksByTime
)
==
0
{
forks
=
append
(
forks
,
math
.
MaxUint64
)
// In purely block based forks, avoid the sentry spilling into timestapt territory
forksByBlock
=
append
(
forksByBlock
,
math
.
MaxUint64
)
// Last fork will never be passed
}
forksByTime
=
append
(
forksByTime
,
math
.
MaxUint64
)
// Last fork will never be passed
// Create a validator that will filter out incompatible chains
return
func
(
id
ID
)
error
{
// Run the fork checksum validation ruleset:
...
...
@@ -166,14 +173,24 @@ func newFilter(config *params.ChainConfig, genesis common.Hash, headfn func() (u
// the remote, but at this current point in time we don't have enough
// information.
// 4. Reject in all other cases.
verify
:=
func
(
index
int
,
headOrTime
uint64
)
error
{
block
,
time
:=
headfn
()
for
i
,
fork
:=
range
forks
{
// Pick the head comparison based on fork progression
head
:=
block
if
i
>=
len
(
forksByBlock
)
{
head
=
time
}
// If our head is beyond this fork, continue to the next (we have a dummy
// fork of maxuint64 as the last item to always fail this check eventually).
if
head
>=
fork
{
continue
}
// Found the first unpassed fork block, check if our current state matches
// the remote checksum (rule #1).
if
sums
[
i
ndex
]
==
id
.
Hash
{
if
sums
[
i
]
==
id
.
Hash
{
// Fork checksum matched, check if a remote future fork block already passed
// locally without the local node being aware of it (rule #1a).
if
id
.
Next
>
0
&&
headOrTime
>=
id
.
Next
{
if
id
.
Next
>
0
&&
(
head
>=
id
.
Next
||
(
id
.
Next
>
timestampThreshold
&&
time
>=
id
.
Next
))
{
return
ErrLocalIncompatibleOrStale
}
// Haven't passed locally a remote-only fork, accept the connection (rule #1b).
...
...
@@ -181,10 +198,10 @@ func newFilter(config *params.ChainConfig, genesis common.Hash, headfn func() (u
}
// The local and remote nodes are in different forks currently, check if the
// remote checksum is a subset of our local forks (rule #2).
for
j
:=
0
;
j
<
i
ndex
;
j
++
{
for
j
:=
0
;
j
<
i
;
j
++
{
if
sums
[
j
]
==
id
.
Hash
{
// Remote checksum is a subset, validate based on the announced next fork
if
allF
orks
[
j
]
!=
id
.
Next
{
if
f
orks
[
j
]
!=
id
.
Next
{
return
ErrRemoteStale
}
return
nil
...
...
@@ -192,7 +209,7 @@ func newFilter(config *params.ChainConfig, genesis common.Hash, headfn func() (u
}
// Remote chain is not a subset of our local one, check if it's a superset by
// any chance, signalling that we're simply out of sync (rule #3).
for
j
:=
i
ndex
+
1
;
j
<
len
(
sums
);
j
++
{
for
j
:=
i
+
1
;
j
<
len
(
sums
);
j
++
{
if
sums
[
j
]
==
id
.
Hash
{
// Yay, remote checksum is a superset, ignore upcoming forks
return
nil
...
...
@@ -201,27 +218,6 @@ func newFilter(config *params.ChainConfig, genesis common.Hash, headfn func() (u
// No exact, subset or superset match. We are on differing chains, reject.
return
ErrLocalIncompatibleOrStale
}
head
,
time
:=
headfn
()
// Verify forks by block
for
i
,
fork
:=
range
forks
{
// If our head is beyond this fork, continue to the next (we have a dummy
// fork of maxuint64 as the last item to always fail this check eventually).
if
head
>=
fork
{
continue
}
return
verify
(
i
,
head
)
}
// Verify forks by time
for
i
,
fork
:=
range
forksByTime
{
// If our head is beyond this fork, continue to the next (we have a dummy
// fork of maxuint64 as the last item to always fail this check eventually).
if
time
>=
fork
{
continue
}
return
verify
(
len
(
forks
)
+
i
,
time
)
}
log
.
Error
(
"Impossible fork ID validation"
,
"id"
,
id
)
return
nil
// Something's very wrong, accept rather than reject
}
...
...
@@ -242,45 +238,45 @@ func checksumToBytes(hash uint32) [4]byte {
return
blob
}
// gatherForks gathers all the known forks and creates a sorted list out of them.
// gatherForks gathers all the known forks and creates two sorted lists out of
// them, one for the block number based forks and the second for the timestamps.
func
gatherForks
(
config
*
params
.
ChainConfig
)
([]
uint64
,
[]
uint64
)
{
// Gather all the fork block numbers via reflection
kind
:=
reflect
.
TypeOf
(
params
.
ChainConfig
{})
conf
:=
reflect
.
ValueOf
(
config
)
.
Elem
()
var
forks
[]
uint64
var
forksByTime
[]
uint64
var
(
forksByBlock
[]
uint64
forksByTime
[]
uint64
)
for
i
:=
0
;
i
<
kind
.
NumField
();
i
++
{
// Fetch the next field and skip non-fork rules
field
:=
kind
.
Field
(
i
)
time
:=
false
if
!
strings
.
HasSuffix
(
field
.
Name
,
"Block"
)
{
if
!
strings
.
HasSuffix
(
field
.
Name
,
"Time
"
)
{
time
:=
strings
.
HasSuffix
(
field
.
Name
,
"Time"
)
if
!
time
&&
!
strings
.
HasSuffix
(
field
.
Name
,
"Block
"
)
{
continue
}
time
=
true
}
if
field
.
Type
!=
reflect
.
TypeOf
(
new
(
big
.
Int
))
{
continue
}
// Extract the fork rule block number and aggregate it
// Extract the fork rule block number
or timestamp
and aggregate it
rule
:=
conf
.
Field
(
i
)
.
Interface
()
.
(
*
big
.
Int
)
if
rule
!=
nil
{
if
time
{
forksByTime
=
append
(
forksByTime
,
rule
.
Uint64
())
}
else
{
forks
=
append
(
forks
,
rule
.
Uint64
())
forks
ByBlock
=
append
(
forksByBlock
,
rule
.
Uint64
())
}
}
}
sort
.
Slice
(
forks
,
func
(
i
,
j
int
)
bool
{
return
forks
[
i
]
<
forks
[
j
]
})
sort
.
Slice
(
forksByBlock
,
func
(
i
,
j
int
)
bool
{
return
forksByBlock
[
i
]
<
forksByBlock
[
j
]
})
sort
.
Slice
(
forksByTime
,
func
(
i
,
j
int
)
bool
{
return
forksByTime
[
i
]
<
forksByTime
[
j
]
})
// Deduplicate
block numb
ers applying multiple forks
for
i
:=
1
;
i
<
len
(
forks
);
i
++
{
if
forks
[
i
]
==
forks
[
i
-
1
]
{
forks
=
append
(
forks
[
:
i
],
forks
[
i
+
1
:
]
...
)
// Deduplicate
fork identifi
ers applying multiple forks
for
i
:=
1
;
i
<
len
(
forks
ByBlock
);
i
++
{
if
forks
ByBlock
[
i
]
==
forksByBlock
[
i
-
1
]
{
forks
ByBlock
=
append
(
forksByBlock
[
:
i
],
forksByBlock
[
i
+
1
:
]
...
)
i
--
}
}
...
...
@@ -291,11 +287,11 @@ func gatherForks(config *params.ChainConfig) ([]uint64, []uint64) {
}
}
// Skip any forks in block 0, that's the genesis ruleset
if
len
(
forks
)
>
0
&&
forks
[
0
]
==
0
{
forks
=
forks
[
1
:
]
if
len
(
forks
ByBlock
)
>
0
&&
forksByBlock
[
0
]
==
0
{
forks
ByBlock
=
forksByBlock
[
1
:
]
}
if
len
(
forksByTime
)
>
0
&&
forksByTime
[
0
]
==
0
{
forksByTime
=
forksByTime
[
1
:
]
}
return
forks
,
forksByTime
return
forks
ByBlock
,
forksByTime
}
core/forkid/forkid_test.go
View file @
fcf3d004
This diff is collapsed.
Click to expand it.
core/headerchain.go
View file @
fcf3d004
...
...
@@ -584,11 +584,11 @@ func (hc *HeaderChain) setHead(headBlock uint64, headTime uint64, updateFn Updat
origin
=
true
)
done
:=
func
(
header
*
types
.
Header
)
bool
{
if
headBlock
!=
0
||
headTime
==
0
{
return
header
.
Number
.
Uint64
()
<=
headBlock
}
if
headTime
>
0
{
return
header
.
Time
<=
headTime
}
return
header
.
Number
.
Uint64
()
<=
headBlock
}
for
hdr
:=
hc
.
CurrentHeader
();
hdr
!=
nil
&&
!
done
(
hdr
);
hdr
=
hc
.
CurrentHeader
()
{
num
:=
hdr
.
Number
.
Uint64
()
...
...
@@ -611,7 +611,7 @@ func (hc *HeaderChain) setHead(headBlock uint64, headTime uint64, updateFn Updat
newHead
,
force
:=
updateFn
(
markerBatch
,
parent
)
if
force
&&
((
headTime
>
0
&&
newHead
.
Time
<
headTime
)
||
(
headTime
==
0
&&
newHead
.
Number
.
Uint64
()
<
headBlock
))
{
log
.
Warn
(
"Force rewinding till ancient limit"
,
"head"
,
newHead
.
Number
.
Uint64
())
headBlock
,
headTime
=
newHead
.
Number
.
Uint64
(),
0
headBlock
,
headTime
=
newHead
.
Number
.
Uint64
(),
0
// Target timestamp passed, continue rewind in block mode (cleaner)
}
}
// Update head header then.
...
...
eth/handler.go
View file @
fcf3d004
...
...
@@ -331,7 +331,7 @@ func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error {
number
=
head
.
Number
.
Uint64
()
td
=
h
.
chain
.
GetTd
(
hash
,
number
)
)
forkID
:=
forkid
.
NewID
(
h
.
chain
.
Config
(),
h
.
chain
.
Genesis
()
.
Hash
(),
h
.
chain
.
CurrentHeader
()
.
Number
.
Uint64
(),
h
.
chain
.
CurrentHeader
()
.
Time
)
forkID
:=
forkid
.
NewID
(
h
.
chain
.
Config
(),
genesis
.
Hash
(),
number
,
head
.
Time
)
if
err
:=
peer
.
Handshake
(
h
.
networkID
,
td
,
hash
,
genesis
.
Hash
(),
forkID
,
h
.
forkFilter
);
err
!=
nil
{
peer
.
Log
()
.
Debug
(
"Ethereum handshake failed"
,
"err"
,
err
)
return
err
...
...
eth/protocols/eth/discovery.go
View file @
fcf3d004
...
...
@@ -59,7 +59,8 @@ func StartENRUpdater(chain *core.BlockChain, ln *enode.LocalNode) {
// currentENREntry constructs an `eth` ENR entry based on the current state of the chain.
func
currentENREntry
(
chain
*
core
.
BlockChain
)
*
enrEntry
{
head
:=
chain
.
CurrentHeader
()
return
&
enrEntry
{
ForkID
:
forkid
.
NewID
(
chain
.
Config
(),
chain
.
Genesis
()
.
Hash
(),
chain
.
CurrentHeader
()
.
Number
.
Uint64
(),
chain
.
CurrentHeader
()
.
Time
),
ForkID
:
forkid
.
NewID
(
chain
.
Config
(),
chain
.
Genesis
()
.
Hash
(),
head
.
Number
.
Uint64
(),
head
.
Time
),
}
}
les/server_handler.go
View file @
fcf3d004
...
...
@@ -117,7 +117,7 @@ func (h *serverHandler) handle(p *clientPeer) error {
hash
=
head
.
Hash
()
number
=
head
.
Number
.
Uint64
()
td
=
h
.
blockchain
.
GetTd
(
hash
,
number
)
forkID
=
forkid
.
NewID
(
h
.
blockchain
.
Config
(),
h
.
blockchain
.
Genesis
()
.
Hash
(),
h
.
blockchain
.
CurrentBlock
()
.
NumberU64
(),
h
.
blockchain
.
CurrentBlock
()
.
Time
()
)
forkID
=
forkid
.
NewID
(
h
.
blockchain
.
Config
(),
h
.
blockchain
.
Genesis
()
.
Hash
(),
number
,
head
.
Time
)
)
if
err
:=
p
.
Handshake
(
td
,
hash
,
number
,
h
.
blockchain
.
Genesis
()
.
Hash
(),
forkID
,
h
.
forkFilter
,
h
.
server
);
err
!=
nil
{
p
.
Log
()
.
Debug
(
"Light Ethereum handshake failed"
,
"err"
,
err
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment