2020-04-21 11:04:18 +00:00
|
|
|
import
|
2020-04-22 04:21:39 +00:00
|
|
|
faststreams/input_stream, eth/[common, rlp], stint, stew/endians2,
|
|
|
|
eth/trie/[db, trie_defs], nimcrypto/[keccak, hash],
|
|
|
|
./witness_types, stew/byteutils
|
2020-04-20 11:23:22 +00:00
|
|
|
|
|
|
|
type
|
2020-04-22 04:21:39 +00:00
|
|
|
DB = TrieDatabaseRef
|
|
|
|
|
2020-04-20 11:23:22 +00:00
|
|
|
TreeBuilder = object
|
2020-04-22 04:21:39 +00:00
|
|
|
data: seq[byte]
|
|
|
|
pos: int
|
|
|
|
#input: InputStream
|
|
|
|
db: DB
|
|
|
|
root: KeccakHash
|
|
|
|
|
|
|
|
# InputStream is unstable, so we hack our own inputstream
|
|
|
|
#proc initTreeBuilder*(input: InputStream, db: DB): TreeBuilder =
|
|
|
|
#result.input = input
|
|
|
|
#result.db = db
|
|
|
|
#result.root = emptyRlpHash
|
|
|
|
|
|
|
|
proc initTreeBuilder*(input: openArray[byte], db: DB): TreeBuilder =
|
|
|
|
result.data = @input
|
|
|
|
result.db = db
|
|
|
|
result.root = emptyRlpHash
|
|
|
|
|
|
|
|
func rootHash*(t: TreeBuilder): KeccakHash {.inline.} =
|
|
|
|
t.root
|
|
|
|
|
|
|
|
proc writeNode(t: var TreeBuilder, n: openArray[byte]): KeccakHash =
|
|
|
|
result = keccak(n)
|
|
|
|
t.db.put(result.data, n)
|
|
|
|
|
|
|
|
proc readByte(t: var TreeBuilder): byte =
|
|
|
|
if t.pos < t.data.len:
|
|
|
|
result = t.data[t.pos]
|
|
|
|
inc t.pos
|
|
|
|
|
|
|
|
template len(t: TreeBuilder): int =
|
|
|
|
t.data.len
|
|
|
|
|
|
|
|
proc peek(t: TreeBuilder): byte =
|
|
|
|
if t.pos + 1 < t.data.len:
|
|
|
|
result = t.data[t.pos + 1]
|
|
|
|
|
|
|
|
template read(t: var TreeBuilder, len: int): auto =
|
|
|
|
let pos = t.pos
|
|
|
|
inc(t.pos, len)
|
|
|
|
toOpenArray(t.data, pos, pos + len - 1)
|
2020-04-20 11:23:22 +00:00
|
|
|
|
2020-04-22 04:21:39 +00:00
|
|
|
proc readU32(t: var TreeBuilder): int =
|
|
|
|
# TODO: what if the value overflow int32.high?
|
|
|
|
result = fromBytesLE(uint32, t.read(4)).int
|
2020-04-20 11:23:22 +00:00
|
|
|
|
2020-04-22 04:21:39 +00:00
|
|
|
proc toAddress(r: var EthAddress, x: openArray[byte]) {.inline.} =
|
|
|
|
r[0..19] = x[0..19]
|
2020-04-20 11:23:22 +00:00
|
|
|
|
2020-04-22 04:21:39 +00:00
|
|
|
proc toKeccak(r: var KeccakHash, x: openArray[byte]) {.inline.} =
|
|
|
|
r.data[0..31] = x[0..31]
|
2020-04-20 11:23:22 +00:00
|
|
|
|
2020-04-22 04:21:39 +00:00
|
|
|
proc toKeccak(x: openArray[byte]): KeccakHash {.inline.} =
|
|
|
|
result.data[0..31] = x[0..31]
|
2020-04-20 11:23:22 +00:00
|
|
|
|
2020-04-22 04:21:39 +00:00
|
|
|
proc branchNode(t: var TreeBuilder, depth: int, has16Elem: bool = true): KeccakHash
|
|
|
|
proc extensionNode(t: var TreeBuilder, depth: int): KeccakHash
|
|
|
|
proc accountNode(t: var TreeBuilder, depth: int): KeccakHash
|
|
|
|
proc accountStorageLeafNode(t: var TreeBuilder, depth: int): KeccakHash
|
|
|
|
proc hashNode(t: var TreeBuilder): KeccakHash
|
2020-04-20 11:23:22 +00:00
|
|
|
|
2020-04-22 04:21:39 +00:00
|
|
|
proc treeNode*(t: var TreeBuilder, depth: int = 0, accountMode = false): KeccakHash =
|
2020-04-20 11:23:22 +00:00
|
|
|
assert(depth < 64)
|
|
|
|
let nodeType = TrieNodeType(t.readByte)
|
2020-04-22 04:21:39 +00:00
|
|
|
|
2020-04-20 11:23:22 +00:00
|
|
|
case nodeType
|
2020-04-22 04:21:39 +00:00
|
|
|
of BranchNodeType: result = t.branchNode(depth)
|
|
|
|
of Branch17NodeType: result = t.branchNode(depth, false)
|
|
|
|
of ExtensionNodeType: result = t.extensionNode(depth)
|
2020-04-20 11:23:22 +00:00
|
|
|
of AccountNodeType:
|
|
|
|
if accountMode:
|
|
|
|
# parse account storage leaf node
|
2020-04-22 04:21:39 +00:00
|
|
|
result = t.accountStorageLeafNode(depth)
|
2020-04-20 11:23:22 +00:00
|
|
|
else:
|
2020-04-22 04:21:39 +00:00
|
|
|
result = t.accountNode(depth)
|
|
|
|
of HashNodeType: result = t.hashNode()
|
2020-04-20 11:23:22 +00:00
|
|
|
|
2020-04-22 04:21:39 +00:00
|
|
|
proc branchNode(t: var TreeBuilder, depth: int, has16Elem: bool): KeccakHash =
|
2020-04-20 11:23:22 +00:00
|
|
|
assert(depth < 64)
|
|
|
|
let mask = constructBranchMask(t.readByte, t.readByte)
|
2020-04-22 04:21:39 +00:00
|
|
|
var r = initRlpList(17)
|
|
|
|
|
2020-04-20 11:23:22 +00:00
|
|
|
for i in 0 ..< 16:
|
|
|
|
if mask.branchMaskBitIsSet(i):
|
2020-04-22 04:21:39 +00:00
|
|
|
r.append t.treeNode(depth+1)
|
|
|
|
else:
|
|
|
|
r.append ""
|
2020-04-20 11:23:22 +00:00
|
|
|
|
2020-04-22 04:21:39 +00:00
|
|
|
template safePeek(t: var TreeBuilder): int =
|
|
|
|
if t.len == 0 or has16Elem:
|
|
|
|
-1
|
|
|
|
else:
|
|
|
|
t.peek().int
|
|
|
|
|
|
|
|
# add the 17th elem
|
|
|
|
let nodeType = t.safePeek()
|
|
|
|
if nodeType == AccountNodeType.int:
|
|
|
|
r.append accountNode(t, depth+1)
|
|
|
|
elif nodeType == HashNodeType.int:
|
|
|
|
r.append hashNode(t)
|
|
|
|
else:
|
|
|
|
# anything else is empty
|
|
|
|
r.append ""
|
|
|
|
|
|
|
|
result = keccak(r.finish)
|
|
|
|
|
2020-04-22 04:43:37 +00:00
|
|
|
func hexPrefix(r: var RlpWriter, x: openArray[byte], nibblesLen: int) =
|
|
|
|
var bytes: array[33, byte]
|
2020-04-22 04:21:39 +00:00
|
|
|
if (nibblesLen mod 2) == 0:
|
2020-04-22 04:43:37 +00:00
|
|
|
bytes[0] = 0.byte
|
|
|
|
var i = 1
|
2020-04-22 04:21:39 +00:00
|
|
|
for y in x:
|
2020-04-22 04:43:37 +00:00
|
|
|
bytes[i] = y
|
|
|
|
inc i
|
2020-04-22 04:21:39 +00:00
|
|
|
else:
|
2020-04-22 04:43:37 +00:00
|
|
|
bytes[0] = 0b0001_0000.byte or (x[0] shr 4)
|
2020-04-22 04:21:39 +00:00
|
|
|
var last = nibblesLen div 2
|
|
|
|
for i in 1..last:
|
2020-04-22 04:43:37 +00:00
|
|
|
bytes[i] = (x[i-1] shl 4) or (x[i] shr 4)
|
|
|
|
|
|
|
|
r.append toOpenArray(bytes, 0, nibblesLen div 2)
|
2020-04-22 04:21:39 +00:00
|
|
|
|
|
|
|
proc extensionNode(t: var TreeBuilder, depth: int): KeccakHash =
|
2020-04-20 11:23:22 +00:00
|
|
|
assert(depth < 63)
|
|
|
|
let nibblesLen = int(t.readByte)
|
|
|
|
assert(nibblesLen < 65)
|
2020-04-22 04:21:39 +00:00
|
|
|
var r = initRlpList(2)
|
2020-04-22 04:43:37 +00:00
|
|
|
r.hexPrefix(t.read(nibblesLen div 2 + nibblesLen mod 2), nibblesLen)
|
2020-04-20 11:23:22 +00:00
|
|
|
|
|
|
|
assert(depth + nibblesLen < 65)
|
|
|
|
let nodeType = TrieNodeType(t.readByte)
|
2020-04-22 04:21:39 +00:00
|
|
|
|
2020-04-20 11:23:22 +00:00
|
|
|
case nodeType
|
2020-04-22 04:21:39 +00:00
|
|
|
of BranchNodeType: r.append t.branchNode(depth + nibblesLen)
|
|
|
|
of Branch17NodeType: r.append t.branchNode(depth + nibblesLen, false)
|
|
|
|
of HashNodeType: r.append t.hashNode()
|
2020-04-20 11:23:22 +00:00
|
|
|
else: raise newException(ValueError, "wrong type during parsing child of extension node")
|
|
|
|
|
2020-04-22 04:21:39 +00:00
|
|
|
result = keccak(r.finish)
|
2020-04-20 11:23:22 +00:00
|
|
|
|
2020-04-22 04:21:39 +00:00
|
|
|
proc accountNode(t: var TreeBuilder, depth: int): KeccakHash =
|
2020-04-20 11:23:22 +00:00
|
|
|
assert(depth < 65)
|
2020-04-22 04:21:39 +00:00
|
|
|
let len = t.readU32()
|
|
|
|
t.writeNode(t.read(len))
|
|
|
|
|
|
|
|
#[let nodeType = AccountType(t.readByte)
|
2020-04-20 11:23:22 +00:00
|
|
|
let nibblesLen = 64 - depth
|
2020-04-22 04:21:39 +00:00
|
|
|
let pathNibbles = @(t.read(nibblesLen div 2 + nibblesLen mod 2))
|
|
|
|
let address = toAddress(t.read(20))
|
|
|
|
let balance = UInt256.fromBytesBE(t.read(32), false)
|
2020-04-20 11:23:22 +00:00
|
|
|
# TODO: why nonce must be 32 bytes, isn't 64 bit uint enough?
|
2020-04-22 04:21:39 +00:00
|
|
|
let nonce = UInt256.fromBytesBE(t.read(32), false)
|
2020-04-20 11:23:22 +00:00
|
|
|
if nodeType == ExtendedAccountType:
|
|
|
|
let codeLen = t.readU32()
|
2020-04-22 04:21:39 +00:00
|
|
|
let code = @(t.read(codeLen))
|
2020-04-20 11:23:22 +00:00
|
|
|
# switch to account storage parsing mode
|
|
|
|
# and reset the depth
|
2020-04-22 04:21:39 +00:00
|
|
|
t.treeNode(0, accountMode = true)]#
|
2020-04-20 11:23:22 +00:00
|
|
|
|
2020-04-22 04:21:39 +00:00
|
|
|
proc accountStorageLeafNode(t: var TreeBuilder, depth: int): KeccakHash =
|
2020-04-20 11:23:22 +00:00
|
|
|
assert(depth < 65)
|
|
|
|
let nibblesLen = 64 - depth
|
2020-04-22 04:21:39 +00:00
|
|
|
let pathNibbles = @(t.read(nibblesLen div 2 + nibblesLen mod 2))
|
|
|
|
let key = @(t.read(32))
|
|
|
|
let val = @(t.read(32))
|
2020-04-21 09:13:43 +00:00
|
|
|
|
2020-04-22 04:21:39 +00:00
|
|
|
proc hashNode(t: var TreeBuilder): KeccakHash =
|
|
|
|
result.toKeccak(t.read(32))
|