Skip to content

Commit 32d9bfc

Browse files
authored
Merge branch 'master' into eip4444-wip
2 parents 15c2401 + bd2365d commit 32d9bfc

File tree

8 files changed

+226
-41
lines changed

8 files changed

+226
-41
lines changed

fluffy/network/history/history_validation.nim

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,21 @@ func verifyBlockHeaderProof*(
6666
): Result[void, string] =
6767
let timestamp = Moment.init(header.timestamp.int64, Second)
6868

69-
if isShanghai(chainConfig, timestamp):
70-
# Note: currently disabled
71-
# - No effective means to get historical summaries yet over the network
72-
# - Proof is currently not as per spec, as we prefer to use SSZ Vectors
69+
# Note: Capella onwards currently disabled
70+
# - No effective means to get historical summaries yet over the network
71+
# - Proof is currently not as per spec, as we prefer to use SSZ Vectors
72+
if isCancun(chainConfig, timestamp):
73+
# let proof = decodeSsz(proof.asSeq(), BlockProofHistoricalSummariesDeneb).valueOr:
74+
# return err("Failed decoding historical_summaries based block proof: " & error)
7375

76+
# if a.historicalSummaries.verifyProof(
77+
# proof, Digest(data: header.rlpHash().data), cfg
78+
# ):
79+
# ok()
80+
# else:
81+
# err("Block proof verification failed (historical_summaries)")
82+
err("Cancun block proof verification not yet activated")
83+
elif isShanghai(chainConfig, timestamp):
7484
# let proof = decodeSsz(proof.asSeq(), BlockProofHistoricalSummaries).valueOr:
7585
# return err("Failed decoding historical_summaries based block proof: " & error)
7686

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,36 @@
11
# Fluffy
2-
# Copyright (c) 2022-2024 Status Research & Development GmbH
2+
# Copyright (c) 2022-2025 Status Research & Development GmbH
33
# Licensed and distributed under either of
44
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
55
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
66
# at your option. This file may not be copied, modified, or distributed except according to those terms.
77

88
{.push raises: [].}
99

10-
import results, beacon_chain/spec/presets, beacon_chain/spec/forks
10+
import results, stew/bitops2, beacon_chain/spec/presets, beacon_chain/spec/forks
1111

12-
type ExecutionBlockProof* = array[11, Digest]
12+
const
13+
# BeaconBlock level:
14+
# - 8 as there are 5 fields
15+
# - 4 as index (pos) of field is 4
16+
gIndexTopLevel = (1 * 1 * 8 + 4)
17+
# BeaconBlockBody level:
18+
# - 16 as there are 10 fields
19+
# - 9 as index (pos) of field is 9
20+
gIndexMidLevel = (gIndexTopLevel * 1 * 16 + 9)
21+
# ExecutionPayload level:
22+
# - 16 as there are 14 fields
23+
# - 12 as pos of field is 12
24+
EXECUTION_BLOCK_HASH_GINDEX* = GeneralizedIndex(gIndexMidLevel * 1 * 16 + 12)
25+
# ExecutionPayload Deneb level:
26+
# - 32 as there are 17 fields
27+
# - 12 as pos of field is 12
28+
EXECUTION_BLOCK_HASH_GINDEX_DENEB* = GeneralizedIndex(gIndexMidLevel * 1 * 32 + 12)
29+
30+
type
31+
ExecutionBlockProof* = array[log2trunc(EXECUTION_BLOCK_HASH_GINDEX), Digest]
32+
ExecutionBlockProofDeneb* =
33+
array[log2trunc(EXECUTION_BLOCK_HASH_GINDEX_DENEB), Digest]
1334

1435
func getBlockRootsIndex*(slot: Slot): uint64 =
1536
slot mod SLOTS_PER_HISTORICAL_ROOT
@@ -20,33 +41,35 @@ func getBlockRootsIndex*(beaconBlock: SomeForkyBeaconBlock): uint64 =
2041
# Builds proof to be able to verify that the EL block hash is part of the
2142
# CL BeaconBlock for given root.
2243
func buildProof*(
23-
beaconBlock: SomeForkyBeaconBlock
44+
beaconBlock:
45+
bellatrix.TrustedBeaconBlock | bellatrix.BeaconBlock | capella.TrustedBeaconBlock |
46+
capella.BeaconBlock
2447
): Result[ExecutionBlockProof, string] =
25-
let
26-
# BeaconBlock level:
27-
# - 8 as there are 5 fields
28-
# - 4 as index (pos) of field is 4
29-
gIndexTopLevel = (1 * 1 * 8 + 4)
30-
# BeaconBlockBody level:
31-
# - 16 as there are 10 fields
32-
# - 9 as index (pos) of field is 9
33-
gIndexMidLevel = (gIndexTopLevel * 1 * 16 + 9)
34-
# ExecutionPayload level:
35-
# - 16 as there are 14 fields
36-
# - 12 as pos of field is 12
37-
gIndex = GeneralizedIndex(gIndexMidLevel * 1 * 16 + 12)
38-
3948
var proof: ExecutionBlockProof
40-
?beaconBlock.build_proof(gIndex, proof)
49+
?beaconBlock.build_proof(EXECUTION_BLOCK_HASH_GINDEX, proof)
50+
51+
ok(proof)
52+
53+
func buildProof*(
54+
beaconBlock:
55+
deneb.TrustedBeaconBlock | deneb.BeaconBlock | electra.TrustedBeaconBlock |
56+
electra.BeaconBlock
57+
): Result[ExecutionBlockProofDeneb, string] =
58+
var proof: ExecutionBlockProofDeneb
59+
?beaconBlock.build_proof(EXECUTION_BLOCK_HASH_GINDEX_DENEB, proof)
4160

4261
ok(proof)
4362

4463
func verifyProof*(
4564
blockHash: Digest, proof: ExecutionBlockProof, blockRoot: Digest
4665
): bool =
47-
let
48-
gIndexTopLevel = (1 * 1 * 8 + 4)
49-
gIndexMidLevel = (gIndexTopLevel * 1 * 16 + 9)
50-
gIndex = GeneralizedIndex(gIndexMidLevel * 1 * 16 + 12)
66+
verify_merkle_multiproof(
67+
@[blockHash], proof, @[EXECUTION_BLOCK_HASH_GINDEX], blockRoot
68+
)
5169

52-
verify_merkle_multiproof(@[blockHash], proof, @[gIndex], blockRoot)
70+
func verifyProof*(
71+
blockHash: Digest, proof: ExecutionBlockProofDeneb, blockRoot: Digest
72+
): bool =
73+
verify_merkle_multiproof(
74+
@[blockHash], proof, @[EXECUTION_BLOCK_HASH_GINDEX_DENEB], blockRoot
75+
)

fluffy/network/history/validation/block_proof_historical_roots.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ type
8989
BeaconBlockProofHistoricalRoots* = array[14, Digest]
9090

9191
BlockProofHistoricalRoots* = object
92-
# Total size (11 + 1 + 14) * 32 bytes + 4 bytes = 836 bytes
92+
# Total size (14 + 1 + 11) * 32 bytes + 4 bytes = 836 bytes
9393
beaconBlockProof*: BeaconBlockProofHistoricalRoots
9494
beaconBlockRoot*: Digest
9595
executionBlockProof*: ExecutionBlockProof

fluffy/network/history/validation/block_proof_historical_summaries.nim

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,22 @@ import
3636
export block_proof_common, ssz_codec
3737

3838
type
39-
BeaconBlockProofHistoricalRoots* = array[13, Digest]
39+
BeaconBlockProofHistoricalSummaries* = array[13, Digest]
4040

4141
BlockProofHistoricalSummaries* = object
42-
# Total size (11 + 1 + 13) * 32 bytes + 4 bytes = 804 bytes
43-
beaconBlockProof*: BeaconBlockProofHistoricalRoots
42+
# Total size (13 + 1 + 11) * 32 bytes + 4 bytes = 804 bytes
43+
beaconBlockProof*: BeaconBlockProofHistoricalSummaries
4444
beaconBlockRoot*: Digest
4545
executionBlockProof*: ExecutionBlockProof
4646
slot*: Slot
4747

48+
BlockProofHistoricalSummariesDeneb* = object
49+
# Total size (13 + 1 + 12) * 32 bytes + 4 bytes = 836 bytes
50+
beaconBlockProof*: BeaconBlockProofHistoricalSummaries
51+
beaconBlockRoot*: Digest
52+
executionBlockProof*: ExecutionBlockProofDeneb
53+
slot*: Slot
54+
4855
HistoricalSummaries* = HashList[HistoricalSummary, Limit HISTORICAL_ROOTS_LIMIT]
4956

5057
template `[]`(x: openArray[Eth2Digest], chunk: Limit): Eth2Digest =
@@ -67,11 +74,11 @@ func getHistoricalSummariesIndex*(
6774
# block_roots for given root.
6875
func buildProof*(
6976
blockRoots: array[SLOTS_PER_HISTORICAL_ROOT, Eth2Digest], blockRootIndex: uint64
70-
): Result[BeaconBlockProofHistoricalRoots, string] =
77+
): Result[BeaconBlockProofHistoricalSummaries, string] =
7178
# max list size * 1 is start point of leaves
7279
let gIndex = GeneralizedIndex(SLOTS_PER_HISTORICAL_ROOT + blockRootIndex)
7380

74-
var proof: BeaconBlockProofHistoricalRoots
81+
var proof: BeaconBlockProofHistoricalSummaries
7582
?blockRoots.build_proof(gIndex, proof)
7683

7784
ok(proof)
@@ -96,9 +103,29 @@ func buildProof*(
96103
)
97104
)
98105

106+
func buildProof*(
107+
blockRoots: array[SLOTS_PER_HISTORICAL_ROOT, Eth2Digest],
108+
beaconBlock:
109+
deneb.TrustedBeaconBlock | deneb.BeaconBlock | electra.TrustedBeaconBlock |
110+
electra.BeaconBlock,
111+
): Result[BlockProofHistoricalSummariesDeneb, string] =
112+
let
113+
blockRootIndex = getBlockRootsIndex(beaconBlock)
114+
executionBlockProof = ?beaconBlock.buildProof()
115+
beaconBlockProof = ?blockRoots.buildProof(blockRootIndex)
116+
117+
ok(
118+
BlockProofHistoricalSummariesDeneb(
119+
beaconBlockRoot: hash_tree_root(beaconBlock),
120+
beaconBlockProof: beaconBlockProof,
121+
executionBlockProof: executionBlockProof,
122+
slot: beaconBlock.slot,
123+
)
124+
)
125+
99126
func verifyProof*(
100127
blockHeaderRoot: Digest,
101-
proof: BeaconBlockProofHistoricalRoots,
128+
proof: BeaconBlockProofHistoricalSummaries,
102129
historicalRoot: Digest,
103130
blockRootIndex: uint64,
104131
): bool =
@@ -108,7 +135,7 @@ func verifyProof*(
108135

109136
func verifyProof*(
110137
historical_summaries: HistoricalSummaries,
111-
proof: BlockProofHistoricalSummaries,
138+
proof: BlockProofHistoricalSummaries | BlockProofHistoricalSummariesDeneb,
112139
blockHash: Digest,
113140
cfg: RuntimeConfig,
114141
): bool =

fluffy/network_metadata.nim

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Nimbus
2-
# Copyright (c) 2022-2024 Status Research & Development GmbH
2+
# Copyright (c) 2022-2025 Status Research & Development GmbH
33
# Licensed and distributed under either of
44
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
55
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
@@ -73,6 +73,7 @@ type
7373
mergeNetsplitBlock*: uint64
7474
shanghaiTime*: Opt[Moment]
7575
cancunTime*: Opt[Moment]
76+
pragueTime*: Opt[Moment]
7677

7778
const
7879
# Allow this to be adjusted at compile time for testing. If more constants
@@ -82,8 +83,9 @@ const
8283

8384
chainConfig* = ChainConfig(
8485
mergeNetsplitBlock: mergeBlockNumber,
85-
shanghaiTime: Opt.some(Moment.init(1681338455'i64, Second)),
86-
cancunTime: Opt.none(Moment),
86+
shanghaiTime: Opt.some(Moment.init(1_681_338_455'i64, Second)),
87+
cancunTime: Opt.some(Moment.init(1_710_338_135'i64, Second)),
88+
pragueTime: Opt.some(Moment.init(1_740_434_112'i64, Second)),
8789
)
8890

8991
func isTimestampForked(forkTime: Opt[Moment], timestamp: Moment): bool =
@@ -100,3 +102,6 @@ func isShanghai*(c: ChainConfig, timestamp: Moment): bool =
100102

101103
func isCancun*(c: ChainConfig, timestamp: Moment): bool =
102104
isTimestampForked(c.cancunTime, timestamp)
105+
106+
func isPrague*(c: ChainConfig, timestamp: Moment): bool =
107+
isTimestampForked(c.pragueTime, timestamp)

fluffy/tests/history_network_tests/all_history_network_tests.nim

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Nimbus
2-
# Copyright (c) 2024 Status Research & Development GmbH
2+
# Copyright (c) 2024-2025 Status Research & Development GmbH
33
# Licensed under either of
44
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
55
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
@@ -15,4 +15,5 @@ import
1515
./test_block_proof_historical_roots,
1616
./test_block_proof_historical_roots_vectors,
1717
./test_block_proof_historical_summaries,
18+
./test_block_proof_historical_summaries_deneb,
1819
./test_block_proof_historical_summaries_vectors

fluffy/tests/history_network_tests/test_block_proof_historical_summaries.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ suite "History Block Proofs - Historical Summaries":
6969

7070
# One more slot to hit second SLOTS_PER_HISTORICAL_ROOT, hitting first
7171
# historical_summary.
72-
blocks.add(addTestBlock(state[], cache, cfg = cfg).capellaData)
72+
discard addTestBlock(state[], cache, cfg = cfg)
7373

7474
# Starts from the block after genesis.
7575
const blocksToTest = [
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# Nimbus
2+
# Copyright (c) 2025 Status Research & Development GmbH
3+
# Licensed and distributed under either of
4+
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
5+
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
6+
# at your option. This file may not be copied, modified, or distributed except according to those terms.
7+
8+
{.used.}
9+
10+
{.push raises: [].}
11+
12+
import
13+
unittest2,
14+
beacon_chain/spec/forks,
15+
beacon_chain/spec/datatypes/deneb,
16+
beacon_chain /../ tests/testblockutil,
17+
# Mock helpers
18+
beacon_chain /../ tests/mocking/mock_genesis,
19+
../../network/history/validation/block_proof_historical_summaries
20+
21+
suite "History Block Proofs - Historical Summaries - Deneb":
22+
let
23+
cfg = block:
24+
var res = defaultRuntimeConfig
25+
res.ALTAIR_FORK_EPOCH = GENESIS_EPOCH
26+
res.BELLATRIX_FORK_EPOCH = GENESIS_EPOCH
27+
res.CAPELLA_FORK_EPOCH = GENESIS_EPOCH
28+
res.DENEB_FORK_EPOCH = GENESIS_EPOCH
29+
res
30+
state = newClone(initGenesisState(cfg = cfg))
31+
var cache = StateCache()
32+
33+
var blocks: seq[deneb.SignedBeaconBlock]
34+
35+
# Note:
36+
# Adding 8192*2 blocks. First block is genesis block and not one of these.
37+
# Then one extra block is needed to get the historical roots, block
38+
# roots and state roots processed.
39+
# index i = 0 is second block.
40+
# index i = 8190 is 8192th block and last one that is part of the first
41+
# historical root
42+
43+
# genesis + 8191 slots, next one will be capella fork
44+
for i in 0 ..< SLOTS_PER_HISTORICAL_ROOT - 1:
45+
blocks.add(addTestBlock(state[], cache, cfg = cfg).denebData)
46+
47+
# One more slot to hit second SLOTS_PER_HISTORICAL_ROOT, hitting first
48+
# historical_summary.
49+
discard addTestBlock(state[], cache, cfg = cfg)
50+
51+
# Starts from the block after genesis.
52+
const blocksToTest = [
53+
0'u64,
54+
1,
55+
2,
56+
3,
57+
SLOTS_PER_HISTORICAL_ROOT div 2,
58+
SLOTS_PER_HISTORICAL_ROOT - 3,
59+
SLOTS_PER_HISTORICAL_ROOT - 2,
60+
]
61+
62+
test "BeaconBlockProofHistoricalSummaries for BeaconBlock":
63+
let blockRoots = getStateField(state[], block_roots).data
64+
65+
withState(state[]):
66+
when consensusFork >= ConsensusFork.Capella:
67+
let historical_summaries = forkyState.data.historical_summaries
68+
69+
# for i in 0..<(SLOTS_PER_HISTORICAL_ROOT - 1): # Test all blocks
70+
for i in blocksToTest:
71+
let
72+
beaconBlock = blocks[i].message
73+
historicalRootsIndex = getHistoricalSummariesIndex(beaconBlock.slot, cfg)
74+
blockRootIndex = getBlockRootsIndex(beaconBlock.slot)
75+
76+
let res = buildProof(blockRoots, blockRootIndex)
77+
check res.isOk()
78+
let proof = res.get()
79+
80+
check verifyProof(
81+
blocks[i].root,
82+
proof,
83+
historical_summaries[historicalRootsIndex].block_summary_root,
84+
blockRootIndex,
85+
)
86+
87+
test "ExecutionBlockProof for Execution BlockHeader":
88+
# for i in 0..<(SLOTS_PER_HISTORICAL_ROOT - 1): # Test all blocks
89+
for i in blocksToTest:
90+
let beaconBlock = blocks[i].message
91+
92+
let res = block_proof_historical_summaries.buildProof(beaconBlock)
93+
check res.isOk()
94+
let proof = res.get()
95+
96+
let leave = beaconBlock.body.execution_payload.block_hash
97+
check verifyProof(leave, proof, blocks[i].root)
98+
99+
test "BlockProofHistoricalSummaries for Execution BlockHeader":
100+
let blockRoots = getStateField(state[], block_roots).data
101+
102+
withState(state[]):
103+
when consensusFork >= ConsensusFork.Capella:
104+
let historical_summaries = forkyState.data.historical_summaries
105+
106+
# for i in 0..<(SLOTS_PER_HISTORICAL_ROOT - 1): # Test all blocks
107+
for i in blocksToTest:
108+
let
109+
beaconBlock = blocks[i].message
110+
# Normally we would have an execution BlockHeader that holds this
111+
# value, but we skip the creation of that header for now and just take
112+
# the blockHash from the execution payload.
113+
blockHash = beaconBlock.body.execution_payload.block_hash
114+
115+
let proofRes = buildProof(blockRoots, beaconBlock)
116+
check proofRes.isOk()
117+
let proof = proofRes.get()
118+
119+
check verifyProof(historical_summaries, proof, blockHash, cfg)

0 commit comments

Comments
 (0)