nimbus-eth1/nimbus/db/aristo/aristo_part/part_chain_rlp.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
# ------------------------------------------------------------------------------