119 lines
3.5 KiB
Nim
119 lines
3.5 KiB
Nim
# nimbus-eth1
|
|
# Copyright (c) 2024 Status Research & Development GmbH
|
|
# Licensed under either of
|
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
|
# http://www.apache.org/licenses/LICENSE-2.0)
|
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
|
# http://opensource.org/licenses/MIT)
|
|
# at your option. This file may not be copied, modified, or distributed
|
|
# except according to those terms.
|
|
|
|
{.push raises: [].}
|
|
|
|
import
|
|
eth/common,
|
|
results,
|
|
".."/[aristo_desc, aristo_get, aristo_utils, aristo_serialise]
|
|
|
|
const
|
|
ChainRlpNodesNoEntry* = {
|
|
PartChnLeafPathMismatch, PartChnExtPfxMismatch, PartChnBranchVoidEdge}
|
|
## Partial path errors that can be used to proof that a path does
|
|
## not exists.
|
|
|
|
TrackRlpNodesNoEntry* = {PartTrkLinkExpected, PartTrkLeafPfxMismatch}
|
|
## This is the opposite of `ChainRlpNodesNoEntry` when verifying that a
|
|
## node does not exist.
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Public functions
|
|
# ------------------------------------------------------------------------------
|
|
|
|
proc chainRlpNodes*(
|
|
db: AristoDbRef;
|
|
rvid: RootedVertexID;
|
|
path: NibblesBuf,
|
|
chain: var seq[seq[byte]];
|
|
): Result[void,AristoError] =
|
|
## Inspired by the `getBranchAux()` function from `hexary.nim`
|
|
let
|
|
(vtx,_) = ? db.getVtxRc rvid
|
|
node = vtx.toNode(rvid.root, db).valueOr:
|
|
return err(PartChnNodeConvError)
|
|
|
|
# Save rpl encoded node(s)
|
|
chain &= node.to(seq[seq[byte]])
|
|
|
|
# Follow up child node
|
|
case vtx.vType:
|
|
of Leaf:
|
|
if path != vtx.pfx:
|
|
err(PartChnLeafPathMismatch)
|
|
else:
|
|
ok()
|
|
|
|
of Branch:
|
|
let nChewOff = sharedPrefixLen(vtx.pfx, path)
|
|
if nChewOff != vtx.pfx.len:
|
|
err(PartChnExtPfxMismatch)
|
|
elif path.len == nChewOff:
|
|
err(PartChnBranchPathExhausted)
|
|
else:
|
|
let
|
|
nibble = path[nChewOff]
|
|
rest = path.slice(nChewOff+1)
|
|
if not vtx.bVid(nibble).isValid:
|
|
return err(PartChnBranchVoidEdge)
|
|
# Recursion!
|
|
db.chainRlpNodes((rvid.root,vtx.bVid(nibble)), rest, chain)
|
|
|
|
|
|
proc trackRlpNodes*(
|
|
chain: openArray[seq[byte]];
|
|
topKey: HashKey;
|
|
path: NibblesBuf;
|
|
start = false;
|
|
): Result[seq[byte],AristoError]
|
|
{.gcsafe, raises: [RlpError]} =
|
|
## Verify rlp-encoded node chain created by `chainRlpNodes()`.
|
|
if path.len == 0:
|
|
return err(PartTrkEmptyPath)
|
|
|
|
# Verify key against rlp-node
|
|
let digest = chain[0].digestTo(HashKey)
|
|
if start:
|
|
if topKey.to(Hash32) != digest.to(Hash32):
|
|
return err(PartTrkFollowUpKeyMismatch)
|
|
else:
|
|
if topKey != digest:
|
|
return err(PartTrkFollowUpKeyMismatch)
|
|
|
|
var
|
|
node = rlpFromBytes chain[0]
|
|
nChewOff = 0
|
|
link: seq[byte]
|
|
|
|
# Decode rlp-node and prepare for recursion
|
|
case node.listLen
|
|
of 2:
|
|
let (isLeaf, segm) = NibblesBuf.fromHexPrefix node.listElem(0).toBytes
|
|
nChewOff = sharedPrefixLen(path, segm)
|
|
link = node.listElem(1).toBytes # link or payload
|
|
if isLeaf:
|
|
if nChewOff == path.len:
|
|
return ok(link)
|
|
return err(PartTrkLeafPfxMismatch)
|
|
of 17:
|
|
nChewOff = 1
|
|
link = node.listElem(path[0].int).toBytes
|
|
else:
|
|
return err(PartTrkGarbledNode)
|
|
|
|
let nextKey = HashKey.fromBytes(link).valueOr:
|
|
return err(PartTrkLinkExpected)
|
|
chain.toOpenArray(1,chain.len-1).trackRlpNodes(nextKey, path.slice nChewOff)
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# End
|
|
# ------------------------------------------------------------------------------
|