Skip to content

Commit 9fe12d1

Browse files
authored
Stateless: Speed up witness building by caching nodes when fetching proofs (#3553)
* Disable state root checks. * Cache rlp trie nodes when building multiple slot proofs. * Use array instead of seq as NodesCache value.
1 parent bbefd40 commit 9fe12d1

File tree

8 files changed

+91
-57
lines changed

8 files changed

+91
-57
lines changed

execution_chain/db/aristo/aristo_desc.nim

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,17 @@ type
6161
## `sTab[]` tables must correspond to a hash entry held on the `kMap[]`
6262
## tables. So a corresponding zero value or missing entry produces an
6363
## inconsistent state that must be resolved.
64-
db*: AristoDbRef ## Database descriptor
65-
parent*: AristoTxRef ## Previous transaction
64+
db*: AristoDbRef ## Database descriptor
65+
parent*: AristoTxRef ## Previous transaction
6666

67-
sTab*: Table[RootedVertexID,VertexRef] ## Structural vertex table
68-
kMap*: Table[RootedVertexID,HashKey] ## Merkle hash key mapping
69-
vTop*: VertexID ## Last used vertex ID
67+
sTab*: Table[RootedVertexID, VertexRef] ## Structural vertex table
68+
kMap*: Table[RootedVertexID, HashKey] ## Merkle hash key mapping
69+
vTop*: VertexID ## Last used vertex ID
7070

71-
accLeaves*: Table[Hash32, AccLeafRef] ## Account path -> VertexRef
72-
stoLeaves*: Table[Hash32, StoLeafRef] ## Storage path -> VertexRef
71+
accLeaves*: Table[Hash32, AccLeafRef] ## Account path -> VertexRef
72+
stoLeaves*: Table[Hash32, StoLeafRef] ## Storage path -> VertexRef
7373

74-
blockNumber*: Opt[uint64] ## Block number set when checkpointing the frame
74+
blockNumber*: Opt[uint64] ## Block number set when checkpointing the frame
7575

7676
snapshot*: Snapshot
7777
## Optional snapshot containing the cumulative changes from ancestors and

execution_chain/db/aristo/aristo_desc/desc_structural.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ type
7070
## Combined record for a *traditional* ``Merkle Patricia Tree` node merged
7171
## with a structural `VertexRef` type object.
7272
vtx*: VertexRef
73-
key*: array[16,HashKey] ## Merkle hash/es for vertices
73+
key*: array[16, HashKey] ## Merkle hash/es for vertices
7474

7575
# ----------------------
7676

execution_chain/db/aristo/aristo_proof.nim

Lines changed: 59 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
## Aristo DB -- Create and verify MPT proofs
1212
## ===========================================================
1313
##
14+
1415
{.push raises: [].}
1516

1617
import
@@ -33,20 +34,34 @@ const
3334
## This is the opposite of `ChainRlpNodesNoEntry` when verifying that a
3435
## node does not exist.
3536

37+
type
38+
NodesCache = Table[RootedVertexID, array[2, seq[byte]]]
39+
## Caches up to two rlp encoded trie nodes in each value
40+
41+
template appendNodes(chain: var seq[seq[byte]], nodePair: array[2, seq[byte]]) =
42+
chain.add(nodePair[0])
43+
if nodePair[1].len() > 0:
44+
chain.add(nodePair[1])
45+
3646
proc chainRlpNodes(
37-
db: AristoTxRef;
38-
rvid: RootedVertexID;
47+
db: AristoTxRef,
48+
rvid: RootedVertexID,
3949
path: NibblesBuf,
40-
chain: var seq[seq[byte]];
41-
): Result[void,AristoError] =
50+
chain: var seq[seq[byte]],
51+
nodesCache: var NodesCache): Result[void, AristoError] =
4252
## Inspired by the `getBranchAux()` function from `hexary.nim`
43-
let
44-
(vtx,_) = ? db.getVtxRc rvid
45-
node = vtx.toNode(rvid.root, db).valueOr:
53+
let (vtx, _) = ?db.getVtxRc(rvid)
54+
55+
nodesCache.withValue(rvid, value):
56+
chain.appendNodes(value[])
57+
do:
58+
let node = vtx.toNode(rvid.root, db).valueOr:
4659
return err(PartChnNodeConvError)
4760

48-
# Save rpl encoded node(s)
49-
chain &= node.to(seq[seq[byte]])
61+
# Save rpl encoded node(s)
62+
let rlpNodes = node.to(array[2, seq[byte]])
63+
nodesCache[rvid] = rlpNodes
64+
chain.appendNodes(rlpNodes)
5065

5166
# Follow up child node
5267
case vtx.vType:
@@ -70,15 +85,15 @@ proc chainRlpNodes(
7085
if not vtx.bVid(nibble).isValid:
7186
return err(PartChnBranchVoidEdge)
7287
# Recursion!
73-
db.chainRlpNodes((rvid.root,vtx.bVid(nibble)), rest, chain)
88+
db.chainRlpNodes((rvid.root,vtx.bVid(nibble)), rest, chain, nodesCache)
7489

7590

7691
proc trackRlpNodes(
7792
chain: openArray[seq[byte]];
7893
topKey: HashKey;
7994
path: NibblesBuf;
8095
start = false;
81-
): Result[seq[byte],AristoError]
96+
): Result[seq[byte], AristoError]
8297
{.gcsafe, raises: [RlpError]} =
8398
## Verify rlp-encoded node chain created by `chainRlpNodes()`.
8499
if path.len == 0:
@@ -126,7 +141,8 @@ proc makeProof(
126141
db: AristoTxRef;
127142
root: VertexID;
128143
path: NibblesBuf;
129-
): Result[(seq[seq[byte]],bool), AristoError] =
144+
nodesCache: var NodesCache;
145+
): Result[(seq[seq[byte]], bool), AristoError] =
130146
## This function returns a chain of rlp-encoded nodes along the argument
131147
## path `(root,path)` followed by a `true` value if the `path` argument
132148
## exists in the database. If the argument `path` is not on the database,
@@ -135,7 +151,7 @@ proc makeProof(
135151
## Errors will only be returned for invalid paths.
136152
##
137153
var chain: seq[seq[byte]]
138-
let rc = db.chainRlpNodes((root,root), path, chain)
154+
let rc = db.chainRlpNodes((root,root), path, chain, nodesCache)
139155
if rc.isOk:
140156
ok((chain, true))
141157
elif rc.error in ChainRlpNodesNoEntry:
@@ -146,29 +162,54 @@ proc makeProof(
146162
proc makeAccountProof*(
147163
db: AristoTxRef;
148164
accPath: Hash32;
149-
): Result[(seq[seq[byte]],bool), AristoError] =
150-
db.makeProof(STATE_ROOT_VID, NibblesBuf.fromBytes accPath.data)
165+
): Result[(seq[seq[byte]], bool), AristoError] =
166+
var nodesCache: NodesCache
167+
db.makeProof(STATE_ROOT_VID, NibblesBuf.fromBytes accPath.data, nodesCache)
151168

152169
proc makeStorageProof*(
153170
db: AristoTxRef;
154171
accPath: Hash32;
155172
stoPath: Hash32;
156-
): Result[(seq[seq[byte]],bool), AristoError] =
173+
): Result[(seq[seq[byte]], bool), AristoError] =
157174
## Note that the function returns an error unless
158175
## the argument `accPath` is valid.
159176
let vid = db.fetchStorageID(accPath).valueOr:
160177
if error == FetchPathStoRootMissing:
161178
return ok((@[],false))
162179
return err(error)
163-
db.makeProof(vid, NibblesBuf.fromBytes stoPath.data)
180+
var nodesCache: NodesCache
181+
db.makeProof(vid, NibblesBuf.fromBytes stoPath.data, nodesCache)
182+
183+
proc makeStorageProofs*(
184+
db: AristoTxRef;
185+
accPath: Hash32;
186+
stoPaths: openArray[Hash32];
187+
): Result[seq[seq[seq[byte]]], AristoError] =
188+
## Note that the function returns an error unless
189+
## the argument `accPath` is valid.
190+
let vid = db.fetchStorageID(accPath).valueOr:
191+
if error == FetchPathStoRootMissing:
192+
let emptyProofs = newSeq[seq[seq[byte]]](stoPaths.len())
193+
return ok(emptyProofs)
194+
return err(error)
195+
196+
var
197+
nodesCache: NodesCache
198+
proofs = newSeqOfCap[seq[seq[byte]]](stoPaths.len())
199+
200+
for stoPath in stoPaths:
201+
let (proof, _) = ?db.makeProof(vid, NibblesBuf.fromBytes stoPath.data, nodesCache)
202+
proofs.add(proof)
203+
204+
ok(proofs)
164205

165206
# ----------
166207

167208
proc verifyProof*(
168209
chain: openArray[seq[byte]];
169210
root: Hash32;
170211
path: Hash32;
171-
): Result[Opt[seq[byte]],AristoError] =
212+
): Result[Opt[seq[byte]], AristoError] =
172213
## Variant of `partUntwigGeneric()`.
173214
try:
174215
let

execution_chain/db/aristo/aristo_serialise.nim

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ proc toRlpBytes*(acc: AristoAccount, key: HashKey): seq[byte] =
2727
codeHash: acc.codeHash,
2828
)
2929

30-
proc to*(node: NodeRef, T: type seq[seq[byte]]): T =
30+
proc to*(node: NodeRef, T: type array[2, seq[byte]]): T =
3131
## Convert the argument pait `w` to a single or a double item list item of
3232
## `<rlp-encoded-node>` type entries. Only in case of a combined extension
3333
## and branch vertex argument, there will be a double item list result.
@@ -51,27 +51,26 @@ proc to*(node: NodeRef, T: type seq[seq[byte]]): T =
5151
wrx.append node.vtx.pfx.toHexPrefix(isleaf = false).data()
5252
wrx.append brHash
5353

54-
result.add wrx.finish()
55-
result.add brData
54+
[wrx.finish(), brData]
5655
else:
5756
# Do for pure branch node
58-
result.add brData
57+
[brData, @[]]
5958
of AccLeaf:
6059
let vtx = AccLeafRef(node.vtx)
6160
var wr = initRlpWriter()
6261
wr.startList(2)
6362
wr.append vtx.pfx.toHexPrefix(isleaf = true).data()
6463
wr.append vtx.account.toRlpBytes(node.key[0])
6564

66-
result.add (wr.finish())
65+
[wr.finish(), @[]]
6766
of StoLeaf:
6867
let vtx = StoLeafRef(node.vtx)
6968
var wr = initRlpWriter()
7069
wr.startList(2)
7170
wr.append vtx.pfx.toHexPrefix(isleaf = true).data()
7271
wr.append rlp.encode vtx.stoData
7372

74-
result.add (wr.finish())
73+
[wr.finish(), @[]]
7574

7675
proc digestTo*(node: NodeRef; T: type HashKey): T =
7776
## Convert the argument `node` to the corresponding Merkle hash key. Note

execution_chain/db/aristo/aristo_utils.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ proc toNode*(
2525
vtx: VertexRef; # Vertex to convert
2626
root: VertexID; # Sub-tree root the `vtx` belongs to
2727
db: AristoTxRef; # Database
28-
): Result[NodeRef,seq[VertexID]] =
28+
): Result[NodeRef, seq[VertexID]] =
2929
## Convert argument the vertex `vtx` to a node type. Missing Merkle hash
3030
## keys are searched for on the argument database `db`.
3131
##

execution_chain/db/core_db/base.nim

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -309,11 +309,11 @@ proc getStateRoot*(acc: CoreDbTxRef): CoreDbRc[Hash32] =
309309

310310
# ------------ storage ---------------
311311

312-
proc slotProof*(
312+
proc slotProofs*(
313313
acc: CoreDbTxRef;
314314
accPath: Hash32;
315-
stoPath: Hash32;
316-
): CoreDbRc[(seq[seq[byte]],bool)] =
315+
stoPaths: openArray[Hash32];
316+
): CoreDbRc[seq[seq[seq[byte]]]] =
317317
## On the storage MPT related to the argument account `acPath`, collect the
318318
## nodes along the `stoPath` interpreted as path. Return these path nodes as
319319
## a chain of rlp-encoded blobs followed by a bool value which is `true` if
@@ -324,7 +324,7 @@ proc slotProof*(
324324
## Note that the function always returns an error unless the `accPath` is
325325
## valid.
326326
##
327-
let rc = acc.aTx.makeStorageProof(accPath, stoPath).valueOr:
327+
let rc = acc.aTx.makeStorageProofs(accPath, stoPaths).valueOr:
328328
return err(error.toError("", ProofCreate))
329329

330330
ok(rc)

execution_chain/db/ledger.nim

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -886,30 +886,23 @@ proc getAccountProof*(ac: LedgerRef, address: Address): seq[seq[byte]] =
886886
accProof[0]
887887

888888
proc getStorageProof*(ac: LedgerRef, address: Address, slots: openArray[UInt256]): seq[seq[seq[byte]]] =
889-
var storageProof = newSeqOfCap[seq[seq[byte]]](slots.len)
890-
891889
let
892890
addressHash = address.toAccountKey
893891
accountExists = ac.txFrame.hasPath(addressHash).valueOr:
894892
raiseAssert "Call to hasPath failed: " & $$error
895893

894+
if not accountExists:
895+
let emptyProofs = newSeq[seq[seq[byte]]](slots.len)
896+
return emptyProofs
897+
898+
var slotKeys: seq[Hash32]
896899
for slot in slots:
897-
if not accountExists:
898-
storageProof.add(@[])
899-
continue
900+
let slotKey = ac.slots.get(slot).valueOr:
901+
slot.toBytesBE().keccak256()
902+
slotKeys.add(slotKey)
900903

901-
let
902-
slotKey = ac.slots.get(slot).valueOr:
903-
slot.toBytesBE.keccak256
904-
slotProof = ac.txFrame.slotProof(addressHash, slotKey).valueOr:
905-
if error.aErr == FetchPathNotFound:
906-
storageProof.add(@[])
907-
continue
908-
else:
909-
raiseAssert "Failed to get slot proof: " & $$error
910-
storageProof.add(slotProof[0])
911-
912-
storageProof
904+
ac.txFrame.slotProofs(addressHash, slotKeys).valueOr:
905+
raiseAssert "Failed to get slot proof: " & $$error
913906

914907
# ------------------------------------------------------------------------------
915908
# Public virtual read-only methods

execution_chain/stateless/witness_generation.nim

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,10 @@ proc build*(
8080
preStateLedger: LedgerRef,
8181
ledger: LedgerRef,
8282
parent: Header,
83-
header: Header): T =
83+
header: Header,
84+
validateStateRoot = false): T =
8485

85-
if parent.number > 0:
86+
if validateStateRoot and parent.number > 0:
8687
doAssert preStateLedger.getStateRoot() == parent.stateRoot
8788

8889
var witness = Witness.build(ledger.getWitnessKeys(), preStateLedger)

0 commit comments

Comments
 (0)