nimbus-eth2/beacon_chain/ssz/navigator.nim
Zahary Karadjov 22591deced Safer testnet restarts; Working CLI queries for inspecting the genesis states
When the connect_to_testnet script is invoked it will first verify that
the genesis file of the testnet hasn't changed. If it has changed, any
previously created database associated with the testnet will be erased.

To facilitate this, the genesis file of each network is written to the
data folder of the beacon node. The beacon node will refuse to start if
it detects a discrepancy between the data folder and any state snapshot
specified on the command-line.

Since the testnet sharing spec requires us to use SSZ snapshots, the Json
support is now phased out. To help with the transition and to preserve the
functionality of the multinet scripts, the beacon node now supports a CLI
query command that can extract any data from the genesis state. This is
based on new developments in the SSZ navigators.
2019-11-11 23:29:36 +00:00

128 lines
4.2 KiB
Nim

import
stew/objects, stew/ranges/ptr_arith,
./types, ./bytes_reader
type
MemRange* = object
startAddr*: ptr byte
length*: int
SszNavigator*[T] = object
m: MemRange
func sszMount*(data: openarray[byte], T: type): SszNavigator[T] =
let startAddr = unsafeAddr data[0]
SszNavigator[T](m: MemRange(startAddr: startAddr, length: data.len))
template sszMount*(data: MemRange, T: type): SszNavigator[T] =
SszNavigator[T](m: data)
template getMemRange*(n: SszNavigator): MemRange =
# Please note that this accessor was created intentionally.
# We don't want to expose the `m` field, because the navigated
# type may have a field by that name. We wan't any dot field
# access to be redirected to the navigated type.
# For this reason, this template should always be used with
# the function call syntax `getMemRange(n)`.
n.m
template checkBounds(m: MemRange, offset: int) =
if offset > m.length:
raise newException(MalformedSszError, "Malformed SSZ")
template toOpenArray(m: MemRange): auto =
makeOpenArray(m.startAddr, m.length)
func navigateToField*[T](n: SszNavigator[T],
fieldName: static string,
FieldType: type): SszNavigator[FieldType] =
mixin toSszType
type SszFieldType = type toSszType(default FieldType)
const boundingOffsets = getFieldBoundingOffsets(T, fieldName)
checkBounds(n.m, boundingOffsets[1])
when isFixedSize(SszFieldType):
SszNavigator[FieldType](m: MemRange(
startAddr: shift(n.m.startAddr, boundingOffsets[0]),
length: boundingOffsets[1] - boundingOffsets[0]))
else:
template readOffset(offset): int =
int fromSszBytes(uint32, makeOpenArray(shift(n.m.startAddr, offset),
sizeof(uint32)))
let
startOffset = readOffset boundingOffsets[0]
endOffset = when boundingOffsets[1] == -1: n.m.length
else: readOffset boundingOffsets[1]
if endOffset < startOffset or endOffset > n.m.length:
raise newException(MalformedSszError, "Incorrect offset values")
SszNavigator[FieldType](m: MemRange(
startAddr: shift(n.m.startAddr, startOffset),
length: endOffset - startOffset))
template `.`*[T](n: SszNavigator[T], field: untyped): auto =
type RecType = T
type FieldType = type(default(RecType).field)
navigateToField(n, astToStr(field), FieldType)
func indexVarSizeList(m: MemRange, idx: int): MemRange =
template readOffset(pos): int =
int fromSszBytes(uint32, makeOpenArray(shift(m.startAddr, pos), offsetSize))
let offsetPos = offsetSize * idx
checkBounds(m, offsetPos + offsetSize)
let firstOffset = readOffset 0
let listLen = firstOffset div offsetSize
if idx >= listLen:
# TODO: Use a RangeError here?
# This would require the user to check the `len` upfront
raise newException(MalformedSszError, "Indexing past the end")
let elemPos = readOffset offsetPos
checkBounds(m, elemPos)
let endPos = if idx < listLen - 1:
let nextOffsetPos = offsetPos + offsetSize
# TODO. Is there a way to remove this bounds check?
checkBounds(m, nextOffsetPos + offsetSize)
readOffset(offsetPos + nextOffsetPos)
else:
m.length
MemRange(startAddr: m.startAddr.shift(elemPos), length: endPos - elemPos)
template indexList(n, idx, T: untyped): untyped =
type R = T
mixin toSszType
type ElemType = type toSszType(default R)
when isFixedSize(ElemType):
const elemSize = fixedPortionSize(ElemType)
let elemPos = idx * elemSize
checkBounds(n.m, elemPos + elemSize)
SszNavigator[R](m: MemRange(startAddr: shift(n.m.startAddr, elemPos),
length: elemSize))
else:
SszNavigator[R](m: indexVarSizeList(n.m, idx))
template `[]`*[T](n: SszNavigator[seq[T]], idx: int): SszNavigator[T] =
indexList n, idx, T
template `[]`*[R, T](n: SszNavigator[array[R, T]], idx: int): SszNavigator[T] =
indexList(n, idx, T)
func `[]`*[T](n: SszNavigator[T]): T =
mixin toSszType, fromSszBytes
type SszRepr = type(toSszType default(T))
when type(SszRepr) is type(T):
readSszValue(toOpenArray(n.m), T)
else:
fromSszBytes(T, toOpenArray(n.m))
converter derefNavigator*[T](n: SszNavigator[T]): T =
n[]