Skip to content

Commit 32419e3

Browse files
authored
Adjust and activate post-merge pre-capella proofs for block headers (#3118)
Only activated post-merge proofs pre capella/shanghai.
1 parent cc254bc commit 32419e3

19 files changed

+196
-128
lines changed

fluffy/eth_data/yaml_eth_types.nim

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# fluffy
2-
# Copyright (c) 2024 Status Research & Development GmbH
2+
# Copyright (c) 2024-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).
@@ -12,16 +12,16 @@ import ssz_serialization/types, stew/byteutils
1212
type
1313
YamlTestProofBellatrix* = object
1414
execution_block_header*: string # Not part of the actual proof
15-
beacon_block_proof*: array[11, string]
15+
execution_block_proof*: array[11, string]
1616
beacon_block_root*: string
17-
historical_roots_proof*: array[14, string]
17+
beacon_block_proof*: array[14, string]
1818
slot*: uint64
1919

2020
YamlTestProof* = object
2121
execution_block_header*: string # Not part of the actual proof
22-
beacon_block_proof*: array[11, string]
22+
execution_block_proof*: array[11, string]
2323
beacon_block_root*: string
24-
historical_summaries_proof*: array[13, string]
24+
beacon_block_proof*: array[13, string]
2525
slot*: uint64
2626

2727
proc fromHex*[n](T: type array[n, Digest], a: array[n, string]): T =

fluffy/network/history/content/content_values.nim

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ const
3030

3131
type
3232
## BlockHeader types
33-
HistoricalHashesAccumulatorProof* = array[15, Digest]
34-
3533
BlockHeaderWithProof* = object
3634
header*: ByteList[MAX_HEADER_LENGTH] # RLP data
3735
proof*: ByteList[MAX_HEADER_PROOF_LENGTH]

fluffy/network/history/history_network.nim

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import
1414
eth/trie/ordered_trie,
1515
eth/common/[hashes, headers_rlp, blocks_rlp, receipts_rlp, transactions_rlp],
1616
eth/p2p/discoveryv5/[protocol, enr],
17+
beacon_chain/spec/presets,
1718
../../common/common_types,
1819
../../database/content_db,
1920
../../network_metadata,
@@ -27,7 +28,7 @@ from eth/common/accounts import EMPTY_ROOT_HASH
2728
logScope:
2829
topics = "portal_hist"
2930

30-
export blocks_rlp
31+
export blocks_rlp, presets
3132

3233
const pingExtensionCapabilities = {CapabilitiesType, HistoryRadiusType}
3334

@@ -36,8 +37,8 @@ type
3637
portalProtocol*: PortalProtocol
3738
contentDB*: ContentDB
3839
contentQueue*: AsyncQueue[(Opt[NodeId], ContentKeysList, seq[seq[byte]])]
39-
accumulator*: FinishedHistoricalHashesAccumulator
40-
historicalRoots*: HistoricalRoots
40+
cfg*: RuntimeConfig
41+
accumulators*: HistoryAccumulators
4142
processContentLoop: Future[void]
4243
statusLogLoop: Future[void]
4344
contentRequestRetries: int
@@ -146,7 +147,9 @@ proc getVerifiedBlockHeader*(
146147
debug "Failed fetching block header with proof from the network"
147148
return Opt.none(Header)
148149

149-
header = validateCanonicalHeaderBytes(headerContent.content, id, n.accumulator).valueOr:
150+
header = validateCanonicalHeaderBytes(
151+
headerContent.content, id, n.accumulators, n.cfg
152+
).valueOr:
150153
n.portalProtocol.banNode(
151154
headerContent.receivedFrom.id, NodeBanDurationContentLookupFailedValidation
152155
)
@@ -304,7 +307,7 @@ proc validateContent(
304307
case contentKey.contentType
305308
of blockHeader:
306309
let _ = validateCanonicalHeaderBytes(
307-
content, contentKey.blockHeaderKey.blockHash, n.accumulator
310+
content, contentKey.blockHeaderKey.blockHash, n.accumulators, n.cfg
308311
).valueOr:
309312
return err("Failed validating block header: " & error)
310313

@@ -327,7 +330,7 @@ proc validateContent(
327330
ok()
328331
of blockNumber:
329332
let _ = validateCanonicalHeaderBytes(
330-
content, contentKey.blockNumberKey.blockNumber, n.accumulator
333+
content, contentKey.blockNumberKey.blockNumber, n.accumulators, n.cfg
331334
).valueOr:
332335
return err("Failed validating block header: " & error)
333336

@@ -341,7 +344,8 @@ proc new*(
341344
baseProtocol: protocol.Protocol,
342345
contentDB: ContentDB,
343346
streamManager: StreamManager,
344-
accumulator: FinishedHistoricalHashesAccumulator,
347+
cfg: RuntimeConfig,
348+
accumulator: FinishedHistoricalHashesAccumulator = loadAccumulator(),
345349
historicalRoots: HistoricalRoots = loadHistoricalRoots(),
346350
bootstrapRecords: openArray[Record] = [],
347351
portalConfig: PortalProtocolConfig = defaultPortalProtocolConfig,
@@ -370,8 +374,10 @@ proc new*(
370374
portalProtocol: portalProtocol,
371375
contentDB: contentDB,
372376
contentQueue: contentQueue,
373-
accumulator: accumulator,
374-
historicalRoots: historicalRoots,
377+
cfg: cfg,
378+
accumulators: HistoryAccumulators(
379+
historicalHashes: accumulator, historicalRoots: historicalRoots
380+
),
375381
contentRequestRetries: contentRequestRetries,
376382
)
377383

@@ -432,7 +438,8 @@ proc statusLogLoop(n: HistoryNetwork) {.async: (raises: []).} =
432438
proc start*(n: HistoryNetwork) =
433439
info "Starting Portal execution history network",
434440
protocolId = n.portalProtocol.protocolId,
435-
accumulatorRoot = hash_tree_root(n.accumulator)
441+
historicalHashesAccumulatorRoot = hash_tree_root(n.accumulators.historicalHashes),
442+
historiricalRootsRoot = hash_tree_root(n.accumulators.historicalRoots)
436443

437444
n.portalProtocol.start()
438445

fluffy/network/history/history_validation.nim

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,23 @@
1010
import
1111
chronos/timer,
1212
eth/trie/ordered_trie,
13+
beacon_chain/spec/presets,
1314
../../network_metadata,
1415
./history_type_conversions,
15-
./validation/historical_hashes_accumulator
16+
./validation/[
17+
historical_hashes_accumulator, block_proof_historical_roots,
18+
block_proof_historical_summaries,
19+
]
1620

1721
from eth/common/eth_types_rlp import rlpHash
1822

1923
export historical_hashes_accumulator
2024

25+
type HistoryAccumulators* = object
26+
historicalHashes*: FinishedHistoricalHashesAccumulator
27+
historicalRoots*: HistoricalRoots
28+
historicalSummaries*: HistoricalSummaries
29+
2130
func validateHeader(header: Header, blockHash: Hash32): Result[void, string] =
2231
if not (header.rlpHash() == blockHash):
2332
err("Header hash does not match")
@@ -50,32 +59,57 @@ func validateHeaderBytes*(
5059
ok(header)
5160

5261
func verifyBlockHeaderProof*(
53-
a: FinishedHistoricalHashesAccumulator,
62+
a: HistoryAccumulators,
5463
header: Header,
5564
proof: ByteList[MAX_HEADER_PROOF_LENGTH],
65+
cfg: RuntimeConfig,
5666
): Result[void, string] =
5767
let timestamp = Moment.init(header.timestamp.int64, Second)
5868

5969
if isShanghai(chainConfig, timestamp):
60-
# TODO: Add verification post merge based on historical_summaries
61-
err("Shanghai block verification not implemented")
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
73+
74+
# let proof = decodeSsz(proof.asSeq(), BlockProofHistoricalSummaries).valueOr:
75+
# return err("Failed decoding historical_summaries based block proof: " & error)
76+
77+
# if a.historicalSummaries.verifyProof(
78+
# proof, Digest(data: header.rlpHash().data), cfg
79+
# ):
80+
# ok()
81+
# else:
82+
# err("Block proof verification failed (historical_summaries)")
83+
err("Shanghai block proof verification not yet activated")
6284
elif isPoSBlock(chainConfig, header.number):
63-
# TODO: Add verification post merge based on historical_roots
64-
err("PoS block verification not implemented")
85+
let proof = decodeSsz(proof.asSeq(), BlockProofHistoricalRoots).valueOr:
86+
return err("Failed decoding historical_roots based block proof: " & error)
87+
88+
if a.historicalRoots.verifyProof(proof, Digest(data: header.rlpHash().data)):
89+
ok()
90+
else:
91+
err("Block proof verification failed (historical roots)")
6592
else:
6693
let accumulatorProof = decodeSsz(proof.asSeq(), HistoricalHashesAccumulatorProof).valueOr:
67-
return err("Failed decoding accumulator proof: " & error)
94+
return
95+
err("Failed decoding historical hashes accumulator based block proof: " & error)
6896

69-
a.verifyAccumulatorProof(header, accumulatorProof)
97+
if a.historicalHashes.verifyProof(header, accumulatorProof):
98+
ok()
99+
else:
100+
err("Block proof verification failed (historical hashes accumulator)")
70101

71102
func validateCanonicalHeaderBytes*(
72-
bytes: openArray[byte], id: uint64 | Hash32, a: FinishedHistoricalHashesAccumulator
103+
bytes: openArray[byte],
104+
id: uint64 | Hash32,
105+
accumulators: HistoryAccumulators,
106+
cfg: RuntimeConfig,
73107
): Result[Header, string] =
74108
let headerWithProof = decodeSsz(bytes, BlockHeaderWithProof).valueOr:
75109
return err("Failed decoding header with proof: " & error)
76110
let header = ?validateHeaderBytes(headerWithProof.header.asSeq(), id)
77111

78-
?a.verifyBlockHeaderProof(header, headerWithProof.proof)
112+
?accumulators.verifyBlockHeaderProof(header, headerWithProof.proof, cfg)
79113

80114
ok(header)
81115

fluffy/network/history/validation/block_proof_historical_roots.nim

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
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).
@@ -78,10 +78,12 @@ import
7878
ssz_serialization,
7979
ssz_serialization/[proofs, merkleization],
8080
beacon_chain/spec/eth2_ssz_serialization,
81+
beacon_chain/spec/ssz_codec,
8182
beacon_chain/spec/datatypes/bellatrix,
83+
beacon_chain/spec/forks,
8284
./block_proof_common
8385

84-
export block_proof_common
86+
export block_proof_common, ssz_codec
8587

8688
type
8789
BeaconBlockProofHistoricalRoots* = array[14, Digest]
@@ -93,6 +95,8 @@ type
9395
executionBlockProof*: ExecutionBlockProof
9496
slot*: Slot
9597

98+
HistoricalRoots* = HashList[Digest, Limit HISTORICAL_ROOTS_LIMIT]
99+
96100
func getHistoricalRootsIndex*(slot: Slot): uint64 =
97101
slot div SLOTS_PER_HISTORICAL_ROOT
98102

@@ -149,7 +153,7 @@ func verifyProof*(
149153
verify_merkle_multiproof(@[blockHeaderRoot], proof, @[gIndex], historicalRoot)
150154

151155
func verifyProof*(
152-
historical_roots: HashList[Eth2Digest, Limit HISTORICAL_ROOTS_LIMIT],
156+
historical_roots: HistoricalRoots,
153157
proof: BlockProofHistoricalRoots,
154158
blockHash: Digest,
155159
): bool =

fluffy/network/history/validation/block_proof_historical_summaries.nim

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Fluffy
2-
# Copyright (c) 2023-2024 Status Research & Development GmbH
2+
# Copyright (c) 2023-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).
@@ -28,11 +28,12 @@ import
2828
ssz_serialization,
2929
ssz_serialization/[proofs, merkleization],
3030
beacon_chain/spec/eth2_ssz_serialization,
31-
beacon_chain/spec/presets,
31+
beacon_chain/spec/ssz_codec,
3232
beacon_chain/spec/datatypes/capella,
33+
beacon_chain/spec/forks,
3334
./block_proof_common
3435

35-
export block_proof_common
36+
export block_proof_common, ssz_codec
3637

3738
type
3839
BeaconBlockProofHistoricalRoots* = array[13, Digest]
@@ -44,6 +45,8 @@ type
4445
executionBlockProof*: ExecutionBlockProof
4546
slot*: Slot
4647

48+
HistoricalSummaries* = HashList[HistoricalSummary, Limit HISTORICAL_ROOTS_LIMIT]
49+
4750
template `[]`(x: openArray[Eth2Digest], chunk: Limit): Eth2Digest =
4851
# Nim 2.0 requires arrays to be indexed by the same type they're declared with.
4952
# Both HistoricalBatch.block_roots and HistoricalBatch.state_roots
@@ -104,7 +107,7 @@ func verifyProof*(
104107
verify_merkle_multiproof(@[blockHeaderRoot], proof, @[gIndex], historicalRoot)
105108

106109
func verifyProof*(
107-
historical_summaries: HashList[HistoricalSummary, Limit HISTORICAL_ROOTS_LIMIT],
110+
historical_summaries: HistoricalSummaries,
108111
proof: BlockProofHistoricalSummaries,
109112
blockHash: Digest,
110113
cfg: RuntimeConfig,

fluffy/network/history/validation/historical_hashes_accumulator.nim

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ type
6969
historicalEpochs*: List[Bytes32, int(MAX_HISTORICAL_EPOCHS)]
7070
currentEpoch*: EpochRecord
7171

72+
HistoricalHashesAccumulatorProof* = array[15, Digest]
73+
7274
Bytes32 = common_types.Bytes32
7375

7476
func init*(T: type HistoricalHashesAccumulator): T =
@@ -146,8 +148,10 @@ func isPreMerge*(blockNumber: uint64): bool =
146148
func isPreMerge*(header: Header): bool =
147149
isPreMerge(header.number)
148150

149-
func verifyProof(
150-
a: FinishedHistoricalHashesAccumulator, header: Header, proof: openArray[Digest]
151+
func verifyProof*(
152+
a: FinishedHistoricalHashesAccumulator,
153+
header: Header,
154+
proof: HistoricalHashesAccumulatorProof,
151155
): bool =
152156
let
153157
epochIndex = getEpochIndex(header)
@@ -160,21 +164,6 @@ func verifyProof(
160164

161165
verify_merkle_multiproof(@[leave], proof, @[gIndex], epochRecordHash)
162166

163-
func verifyAccumulatorProof*(
164-
a: FinishedHistoricalHashesAccumulator,
165-
header: Header,
166-
proof: HistoricalHashesAccumulatorProof,
167-
): Result[void, string] =
168-
if header.isPreMerge():
169-
# Note: The proof is typed with correct depth, so no check on this is
170-
# required here.
171-
if a.verifyProof(header, proof):
172-
ok()
173-
else:
174-
err("Proof verification failed")
175-
else:
176-
err("Cannot verify post merge header with accumulator proof")
177-
178167
func buildProof*(
179168
header: Header, epochRecord: EpochRecord | EpochRecordCached
180169
): Result[HistoricalHashesAccumulatorProof, string] =

fluffy/portal_node.nim

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Fluffy
2-
# Copyright (c) 2024 Status Research & Development GmbH
2+
# Copyright (c) 2024-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).
@@ -137,6 +137,7 @@ proc new*(
137137
discovery,
138138
contentDB,
139139
streamManager,
140+
networkData.metadata.cfg,
140141
accumulator,
141142
bootstrapRecords = bootstrapRecords,
142143
portalConfig = config.portalConfig,

fluffy/tests/history_network_tests/test_block_proof_historical_roots_vectors.nim

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Fluffy
2-
# Copyright (c) 2024 Status Research & Development GmbH
2+
# Copyright (c) 2024-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).
@@ -33,10 +33,10 @@ suite "History Block Proofs - Historical Roots - Test Vectors":
3333

3434
blockHash = Digest.fromHex(testProof.execution_block_header)
3535
blockProof = BlockProofHistoricalRoots(
36-
beaconBlockProof:
37-
array[14, Digest].fromHex(testProof.historical_roots_proof),
36+
beaconBlockProof: array[14, Digest].fromHex(testProof.beacon_block_proof),
3837
beaconBlockRoot: Digest.fromHex(testProof.beacon_block_root),
39-
executionBlockProof: array[11, Digest].fromHex(testProof.beacon_block_proof),
38+
executionBlockProof:
39+
array[11, Digest].fromHex(testProof.execution_block_proof),
4040
slot: Slot(testProof.slot),
4141
)
4242

fluffy/tests/history_network_tests/test_block_proof_historical_summaries.nim

Lines changed: 2 additions & 2 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).
@@ -112,7 +112,7 @@ suite "History Block Proofs - Historical Summaries":
112112
for i in blocksToTest:
113113
let beaconBlock = blocks[i].message
114114

115-
let res = buildProof(beaconBlock)
115+
let res = block_proof_historical_summaries.buildProof(beaconBlock)
116116
check res.isOk()
117117
let proof = res.get()
118118

0 commit comments

Comments
 (0)