-
Notifications
You must be signed in to change notification settings - Fork 108
/
beacon_chain_block_proof_bellatrix.nim
181 lines (159 loc) · 6.84 KB
/
beacon_chain_block_proof_bellatrix.nim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# Fluffy
# Copyright (c) 2022-2024 Status Research & Development GmbH
# Licensed and distributed under either of
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
# This is a PoC of how execution block headers in the Portal history network
# could be proven to be part of the canonical chain by means of a proof that
# exists out a chain of proofs.
#
# To verify this proof you need access to the BeaconState field
# historical_roots and the block hash of the execution block.
#
# The chain traverses from proving that the block hash is the one of the
# ExecutionPayload in the BeaconBlockBody, to proving that this BeaconBlockBody
# is the one that is rooted in the BeaconBlockHeader, to proving that this
# BeaconBlockHeader is rooted in the historical_roots.
#
# TODO: The middle proof is perhaps a bit silly as it doesn't win much space
# compared to just providing the BeaconHeader.
#
# Requirements:
#
# For building the proofs:
# - Node that has access to all the beacon chain data (state and blocks) and
# - it will be required to rebuild the HistoricalBatches.
#
# For verifying the proofs:
# - As mentioned, the historical_roots field of the state is required. This
# is currently in no way available over any of the consensus layer libp2p
# protocols. Thus a light client cannot really be build using these proofs,
# which makes it rather useless for now.
# - The historical_roots could be put available on the network together with
# a proof against the right state root. An example of this can be seen in
# ./fluffy/network/history/experimental/beacon_chain_historical_roots.nim
#
# Caveat:
#
# Roots in historical_roots are only added every `SLOTS_PER_HISTORICAL_ROOT`
# slots. Recent blocks that are not part of a historical_root cannot be proven
# through this mechanism. They need to be directly looked up in the block_roots
# BeaconState field.
#
# Alternative:
#
# This PoC is written with the idea of keeping execution BlockHeaders and
# BlockBodies available in the Portal history network in the same way post-merge
# as it is pre-merge. One could also simply decide to store the BeaconBlocks or
# BeaconBlockHeaders and BeaconBlockBodies directly. And get the execution
# payloads from there. This would require only 1 (or two, depending on what you
# store) of the proofs and might be more convenient if you want to / need to
# store the beacon data also on the network. It would require some rebuilding
# the structure of the Execution BlockHeader.
#
# Alternative ii:
#
# Verifying a specific block could also be done by making use of the
# LightClientUpdates. Picking the closest update, and walking back blocks from
# that block to the specific block. How much data is required to download would
# depend on the location of the block, but it could be quite significant.
# Of course, this again could be thrown in some accumulator, but that would
# then be required to be stored on the state to make it easy verifiable.
# A PoC of this process would be nice and it could be more useful for a system
# like the Nimbus verified proxy.
#
#
# The usage of this PoC can be seen in
# ./fluffy/tests/test_beacon_chain_block_proof.nim
#
# TODO: Probably needs to make usage of forks instead of just bellatrix.
#
{.push raises: [].}
import
results,
ssz_serialization,
ssz_serialization/[proofs, merkleization],
beacon_chain/spec/eth2_ssz_serialization,
beacon_chain/spec/datatypes/bellatrix,
./beacon_chain_block_proof_common
export beacon_chain_block_proof_common
type
HistoricalRootsProof* = array[14, Digest]
BeaconChainBlockProof* = object
# Total size (8 + 1 + 3 + 1 + 14) * 32 bytes + 4 bytes = 868 bytes
beaconBlockBodyProof*: BeaconBlockBodyProof
beaconBlockBodyRoot*: Digest
beaconBlockHeaderProof*: BeaconBlockHeaderProof
beaconBlockHeaderRoot*: Digest
historicalRootsProof*: HistoricalRootsProof
slot*: Slot
func getHistoricalRootsIndex*(slot: Slot): uint64 =
slot div SLOTS_PER_HISTORICAL_ROOT
func getHistoricalRootsIndex*(blockHeader: BeaconBlockHeader): uint64 =
getHistoricalRootsIndex(blockHeader.slot)
# Builds proof to be able to verify that a BeaconBlock root is part of the
# HistoricalBatch for given root.
func buildProof*(
batch: HistoricalBatch, blockRootIndex: uint64
): Result[HistoricalRootsProof, string] =
# max list size * 2 is start point of leaves
let gIndex = GeneralizedIndex(2 * SLOTS_PER_HISTORICAL_ROOT + blockRootIndex)
var proof: HistoricalRootsProof
?batch.build_proof(gIndex, proof)
ok(proof)
func buildProof*(
batch: HistoricalBatch,
blockHeader: BeaconBlockHeader,
blockBody: bellatrix.TrustedBeaconBlockBody | bellatrix.BeaconBlockBody,
): Result[BeaconChainBlockProof, string] =
let
blockRootIndex = getBlockRootsIndex(blockHeader)
beaconBlockBodyProof = ?blockBody.buildProof()
beaconBlockHeaderProof = ?blockHeader.buildProof()
historicalRootsProof = ?batch.buildProof(blockRootIndex)
ok(
BeaconChainBlockProof(
beaconBlockBodyProof: beaconBlockBodyProof,
beaconBlockBodyRoot: hash_tree_root(blockBody),
beaconBlockHeaderProof: beaconBlockHeaderProof,
beaconBlockHeaderRoot: hash_tree_root(blockHeader),
historicalRootsProof: historicalRootsProof,
slot: blockHeader.slot,
)
)
func verifyProof*(
blockHash: Digest, proof: BeaconBlockBodyProof, blockBodyRoot: Digest
): bool =
let
gIndexTopLevel = (1 * 1 * 16 + 9)
gIndex = GeneralizedIndex(gIndexTopLevel * 1 * 16 + 12)
verify_merkle_multiproof(@[blockHash], proof, @[gIndex], blockBodyRoot)
func verifyProof*(
blockBodyRoot: Digest, proof: BeaconBlockHeaderProof, blockHeaderRoot: Digest
): bool =
let gIndex = GeneralizedIndex(12)
verify_merkle_multiproof(@[blockBodyRoot], proof, @[gIndex], blockHeaderRoot)
func verifyProof*(
blockHeaderRoot: Digest,
proof: HistoricalRootsProof,
historicalRoot: Digest,
blockRootIndex: uint64,
): bool =
let gIndex = GeneralizedIndex(2 * SLOTS_PER_HISTORICAL_ROOT + blockRootIndex)
verify_merkle_multiproof(@[blockHeaderRoot], proof, @[gIndex], historicalRoot)
func verifyProof*(
historical_roots: HashList[Eth2Digest, Limit HISTORICAL_ROOTS_LIMIT],
proof: BeaconChainBlockProof,
blockHash: Digest,
): bool =
let
historicalRootsIndex = getHistoricalRootsIndex(proof.slot)
blockRootIndex = getBlockRootsIndex(proof.slot)
blockHash.verifyProof(proof.beaconBlockBodyProof, proof.beaconBlockBodyRoot) and
proof.beaconBlockBodyRoot.verifyProof(
proof.beaconBlockHeaderProof, proof.beaconBlockHeaderRoot
) and
proof.beaconBlockHeaderRoot.verifyProof(
proof.historicalRootsProof, historical_roots[historicalRootsIndex], blockRootIndex
)