2020-04-20 18:14:39 +00:00
|
|
|
{.used.}
|
|
|
|
|
2019-02-05 12:01:10 +00:00
|
|
|
import
|
2021-12-11 18:12:55 +00:00
|
|
|
std/[math, strutils],
|
|
|
|
unittest2,
|
2021-04-06 11:33:24 +00:00
|
|
|
stew/byteutils,
|
2023-04-19 12:26:41 +00:00
|
|
|
../../eth/[common, rlp]
|
2019-02-05 12:01:10 +00:00
|
|
|
|
|
|
|
proc q(s: string): string = "\"" & s & "\""
|
|
|
|
proc i(s: string): string = s.replace(" ").replace("\n")
|
|
|
|
proc inspectMatch(r: Rlp, s: string): bool = r.inspect.i == s.i
|
|
|
|
|
2023-04-19 12:26:41 +00:00
|
|
|
proc `==`(a,b: ChainId): bool {.borrow.}
|
|
|
|
## helper for ` test_calcBlockBodyTranscode()`
|
|
|
|
|
|
|
|
proc test_blockBodyTranscode() =
|
|
|
|
## RLP encode/decode a list of `BlockBody` objects. Note that tere is/was a
|
|
|
|
## problem in `eth/common/eth_types_rlp.append()` for `BlockBody` encoding.
|
|
|
|
let blkSeq = @[
|
|
|
|
BlockBody(
|
|
|
|
transactions: @[
|
|
|
|
Transaction(nonce: 1)]),
|
|
|
|
BlockBody(
|
|
|
|
uncles: @[
|
2024-09-29 08:52:19 +00:00
|
|
|
BlockHeader(nonce: BlockNonce([0x20u8,0,0,0,0,0,0,0]))]),
|
2023-04-19 12:26:41 +00:00
|
|
|
BlockBody(),
|
|
|
|
BlockBody(
|
|
|
|
transactions: @[
|
|
|
|
Transaction(nonce: 3),
|
|
|
|
Transaction(nonce: 4)])]
|
|
|
|
|
|
|
|
let trBlkSeq = blkSeq.encode.decode(typeof blkSeq)
|
|
|
|
|
|
|
|
check trBlkSeq.len == blkSeq.len
|
|
|
|
for n in 0 ..< min(trBlkSeq.len, trBlkSeq.len):
|
|
|
|
check (n, trBlkSeq[n]) == (n, blkSeq[n])
|
|
|
|
|
2021-12-11 18:12:55 +00:00
|
|
|
suite "test api usage":
|
|
|
|
test "empty bytes are not a proper RLP":
|
|
|
|
var rlp = rlpFromBytes seq[byte](@[])
|
|
|
|
|
|
|
|
check:
|
|
|
|
not rlp.hasData
|
|
|
|
not rlp.isBlob
|
|
|
|
not rlp.isList
|
|
|
|
not rlp.isEmpty
|
|
|
|
|
|
|
|
expect AssertionDefect:
|
|
|
|
rlp.skipElem
|
|
|
|
|
|
|
|
test "you cannot finish a list without appending enough elements":
|
|
|
|
var writer = initRlpList(3)
|
|
|
|
writer.append "foo"
|
|
|
|
writer.append "bar"
|
|
|
|
|
2023-02-20 19:52:49 +00:00
|
|
|
expect AssertionDefect:
|
2021-12-11 18:12:55 +00:00
|
|
|
discard writer.finish
|
|
|
|
|
|
|
|
test "encode/decode object":
|
|
|
|
type
|
|
|
|
MyEnum = enum
|
|
|
|
foo,
|
|
|
|
bar
|
|
|
|
|
|
|
|
MyObj = object
|
|
|
|
a: array[3, char]
|
2024-09-30 16:32:36 +00:00
|
|
|
b: uint64
|
2021-12-11 18:12:55 +00:00
|
|
|
c: MyEnum
|
|
|
|
|
2024-09-30 16:32:36 +00:00
|
|
|
IntObj = object
|
|
|
|
v: int
|
|
|
|
|
2021-12-11 18:12:55 +00:00
|
|
|
var input: MyObj
|
|
|
|
input.a = ['e', 't', 'h']
|
|
|
|
input.b = 63
|
|
|
|
input.c = bar
|
|
|
|
|
2024-09-30 16:32:36 +00:00
|
|
|
|
2021-12-11 18:12:55 +00:00
|
|
|
var writer = initRlpWriter()
|
|
|
|
writer.append(input)
|
2024-09-30 16:32:36 +00:00
|
|
|
|
|
|
|
check:
|
|
|
|
not compiles(writer.append(default(IntObj)))
|
|
|
|
|
2021-12-11 18:12:55 +00:00
|
|
|
let bytes = writer.finish()
|
|
|
|
var rlp = rlpFromBytes(bytes)
|
|
|
|
|
|
|
|
var output = rlp.read(MyObj)
|
|
|
|
check:
|
|
|
|
input == output
|
|
|
|
|
|
|
|
test "encode and decode lists":
|
|
|
|
var writer = initRlpList(3)
|
|
|
|
writer.append "foo"
|
|
|
|
writer.append ["bar", "baz"]
|
2024-09-30 16:32:36 +00:00
|
|
|
writer.append [uint64 30, 40, 50]
|
2021-12-11 18:12:55 +00:00
|
|
|
|
|
|
|
var
|
|
|
|
bytes = writer.finish
|
|
|
|
rlp = rlpFromBytes bytes
|
|
|
|
|
|
|
|
check:
|
|
|
|
bytes.toHex == "d183666f6fc8836261728362617ac31e2832"
|
|
|
|
rlp.inspectMatch """
|
|
|
|
{
|
|
|
|
"foo"
|
2021-05-14 14:48:21 +00:00
|
|
|
{
|
2021-12-11 18:12:55 +00:00
|
|
|
"bar"
|
|
|
|
"baz"
|
2021-05-14 14:48:21 +00:00
|
|
|
}
|
2021-12-11 18:12:55 +00:00
|
|
|
{
|
|
|
|
byte 30
|
|
|
|
byte 40
|
|
|
|
byte 50
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"""
|
|
|
|
|
2024-09-30 16:32:36 +00:00
|
|
|
bytes = encodeList(uint64 6000,
|
2021-12-11 18:12:55 +00:00
|
|
|
"Lorem ipsum dolor sit amet",
|
|
|
|
"Donec ligula tortor, egestas eu est vitae")
|
|
|
|
|
|
|
|
rlp = rlpFromBytes bytes
|
|
|
|
check:
|
|
|
|
rlp.listLen == 3
|
2024-09-30 16:32:36 +00:00
|
|
|
rlp.listElem(0).toInt(uint64) == 6000
|
2021-12-11 18:12:55 +00:00
|
|
|
rlp.listElem(1).toString == "Lorem ipsum dolor sit amet"
|
|
|
|
rlp.listElem(2).toString == "Donec ligula tortor, egestas eu est vitae"
|
|
|
|
|
|
|
|
# test creating RLPs from other RLPs
|
2021-12-20 12:14:50 +00:00
|
|
|
var list = rlpFromBytes encodeList(rlp.listElem(1), rlp.listElem(0))
|
2021-12-11 18:12:55 +00:00
|
|
|
|
|
|
|
# test that iteration with enterList/skipElem works as expected
|
|
|
|
doAssert list.enterList # We already know that we are working with a list
|
|
|
|
check list.toString == "Lorem ipsum dolor sit amet"
|
|
|
|
list.skipElem
|
|
|
|
|
2024-09-30 16:32:36 +00:00
|
|
|
check list.toInt(uint32) == 6000.uint32
|
|
|
|
var intVar: uint32
|
2021-12-11 18:12:55 +00:00
|
|
|
list >> intVar
|
|
|
|
check intVar == 6000
|
|
|
|
|
|
|
|
check(not list.hasData)
|
|
|
|
expect AssertionDefect: list.skipElem
|
|
|
|
|
2023-04-19 12:26:41 +00:00
|
|
|
test "encode and decode block body":
|
|
|
|
test_blockBodyTranscode()
|
|
|
|
|
2021-12-11 18:12:55 +00:00
|
|
|
test "toBytes":
|
|
|
|
let rlp = rlpFromHex("f2cb847f000001827666827666a040ef02798f211da2e8173d37f255be908871ae65060dbb2f77fb29c0421447f4845ab90b50")
|
|
|
|
let tok = rlp.listElem(1).toBytes()
|
|
|
|
check:
|
|
|
|
tok.len == 32
|
|
|
|
tok.toHex == "40ef02798f211da2e8173d37f255be908871ae65060dbb2f77fb29c0421447f4"
|
|
|
|
|
|
|
|
test "nested lists":
|
2024-09-30 16:32:36 +00:00
|
|
|
let listBytes = encode([[uint64 1, 2, 3], [uint64 5, 6, 7]])
|
2021-12-11 18:12:55 +00:00
|
|
|
let listRlp = rlpFromBytes listBytes
|
|
|
|
let sublistRlp0 = listRlp.listElem(0)
|
|
|
|
let sublistRlp1 = listRlp.listElem(1)
|
2024-09-30 16:32:36 +00:00
|
|
|
check sublistRlp0.listElem(0).toInt(uint64) == 1
|
|
|
|
check sublistRlp0.listElem(1).toInt(uint64) == 2
|
|
|
|
check sublistRlp0.listElem(2).toInt(uint64) == 3
|
|
|
|
check sublistRlp1.listElem(0).toInt(uint64) == 5
|
|
|
|
check sublistRlp1.listElem(1).toInt(uint64) == 6
|
|
|
|
check sublistRlp1.listElem(2).toInt(uint64) == 7
|
2021-12-11 18:12:55 +00:00
|
|
|
|
|
|
|
test "encoding length":
|
2024-09-30 16:32:36 +00:00
|
|
|
let listBytes = encode([uint64 1,2,3,4,5])
|
2021-12-11 18:12:55 +00:00
|
|
|
let listRlp = rlpFromBytes listBytes
|
|
|
|
check listRlp.listLen == 5
|
|
|
|
|
|
|
|
let emptyListBytes = encode ""
|
|
|
|
check emptyListBytes.len == 1
|
|
|
|
let emptyListRlp = rlpFromBytes emptyListBytes
|
|
|
|
check emptyListRlp.blobLen == 0
|
|
|
|
|
|
|
|
test "basic decoding":
|
|
|
|
var rlp1 = rlpFromHex("856d6f6f7365")
|
|
|
|
var rlp2 = rlpFromHex("0x856d6f6f7365")
|
|
|
|
|
|
|
|
check:
|
|
|
|
rlp1.inspect == q"moose"
|
|
|
|
rlp2.inspect == q"moose"
|
|
|
|
|
|
|
|
test "malformed/truncated RLP":
|
|
|
|
var rlp = rlpFromHex("b8056d6f6f7365")
|
|
|
|
expect MalformedRlpError:
|
|
|
|
discard rlp.inspect
|
|
|
|
|
|
|
|
test "encode byte arrays":
|
|
|
|
var b1 = [byte(1), 2, 5, 7, 8]
|
|
|
|
var b2 = [byte(6), 8, 12, 123]
|
|
|
|
var b3 = @[byte(122), 56, 65, 12]
|
|
|
|
|
|
|
|
let rlp = rlpFromBytes(encode((b1, b2, b3)))
|
|
|
|
check:
|
|
|
|
rlp.listLen == 3
|
|
|
|
rlp.listElem(0).toBytes() == b1
|
|
|
|
rlp.listElem(1).toBytes() == b2
|
|
|
|
rlp.listElem(2).toBytes() == b3
|
|
|
|
|
|
|
|
# The first byte here is the length of the datum (132 - 128 => 4)
|
|
|
|
$(rlp.listElem(1).rawData) == "[132, 6, 8, 12, 123]"
|
|
|
|
|
|
|
|
test "empty byte arrays":
|
|
|
|
var
|
|
|
|
rlp = rlpFromBytes rlp.encode("")
|
|
|
|
b = rlp.toBytes
|
|
|
|
check $b == "@[]"
|
|
|
|
|
|
|
|
test "invalid enum":
|
|
|
|
type
|
|
|
|
MyEnum = enum
|
2022-11-14 14:49:37 +00:00
|
|
|
foo = 0x00,
|
|
|
|
bar = 0x01
|
|
|
|
|
|
|
|
var writer = initRlpWriter()
|
2024-09-30 16:32:36 +00:00
|
|
|
writer.append(byte 1) # valid
|
|
|
|
writer.append(byte 2) # invalid
|
2024-05-26 07:58:24 +00:00
|
|
|
writer.append(cast[uint64](-1)) # invalid
|
2022-11-14 14:49:37 +00:00
|
|
|
let bytes = writer.finish()
|
|
|
|
var rlp = rlpFromBytes(bytes)
|
|
|
|
|
|
|
|
check rlp.read(MyEnum) == bar
|
|
|
|
|
|
|
|
expect RlpTypeMismatch:
|
|
|
|
discard rlp.read(MyEnum)
|
|
|
|
rlp.skipElem()
|
|
|
|
|
|
|
|
expect RlpTypeMismatch:
|
|
|
|
discard rlp.read(MyEnum)
|
|
|
|
|
|
|
|
test "invalid enum - enum with hole":
|
|
|
|
type
|
|
|
|
MyEnum = enum
|
|
|
|
foo = 0x00,
|
|
|
|
bar = 0x01,
|
|
|
|
baz = 0x0100
|
2021-12-11 18:12:55 +00:00
|
|
|
|
|
|
|
var writer = initRlpWriter()
|
2024-09-30 16:32:36 +00:00
|
|
|
writer.append(1'u64) # valid
|
|
|
|
writer.append(2'u64) # invalid - enum hole value
|
|
|
|
writer.append(256'u64) # valid
|
|
|
|
writer.append(257'u64) # invalid - too large
|
2021-12-11 18:12:55 +00:00
|
|
|
let bytes = writer.finish()
|
|
|
|
var rlp = rlpFromBytes(bytes)
|
2022-11-14 14:49:37 +00:00
|
|
|
|
|
|
|
check rlp.read(MyEnum) == bar
|
|
|
|
|
2021-12-11 18:12:55 +00:00
|
|
|
expect RlpTypeMismatch:
|
|
|
|
discard rlp.read(MyEnum)
|
|
|
|
rlp.skipElem()
|
2022-11-14 14:49:37 +00:00
|
|
|
|
|
|
|
check rlp.read(MyEnum) == baz
|
|
|
|
|
2021-12-11 18:12:55 +00:00
|
|
|
expect RlpTypeMismatch:
|
|
|
|
discard rlp.read(MyEnum)
|
2022-11-14 14:49:37 +00:00
|
|
|
rlp.skipElem()
|