diff --git a/eth/common/eth_types_rlp.nim b/eth/common/eth_types_rlp.nim index 9572161..1a12cd9 100644 --- a/eth/common/eth_types_rlp.nim +++ b/eth/common/eth_types_rlp.nim @@ -317,8 +317,16 @@ proc append*(rlpWriter: var RlpWriter, t: Time) {.inline.} = proc append*(w: var RlpWriter, h: BlockHeader) = var len = 15 if h.fee.isSome: inc len - if h.withdrawalsRoot.isSome: inc len - if h.excessDataGas.isSome: inc len + if h.withdrawalsRoot.isSome: + if h.fee.isNone: + raise newException(RlpError, "baseFee expected") + inc len + if h.excessDataGas.isSome: + if h.fee.isNone: + raise newException(RlpError, "baseFee expected") + if h.withdrawalsRoot.isNone: + raise newException(RlpError, "withdrawalsRoot expected") + inc len w.startList(len) for k, v in fieldPairs(h): when v isnot Option: @@ -364,17 +372,20 @@ proc append*(w: var RlpWriter, b: BlockBody) = if b.withdrawals.isSome: w.append(b.withdrawals.unsafeGet) -# Is there a better way of doing this? We have tests that call -# rlp.readRecordType(BlockBody, false), so I overrode -# `readRecordType` as well as `read`. --Adam proc readRecordType*(rlp: var Rlp, T: type BlockBody, wrappedInList: bool): BlockBody = if not wrappedInList: result.transactions = rlp.read(seq[Transaction]) result.uncles = rlp.read(seq[BlockHeader]) - # Is this the right thing to do here? I don't really - # understand what wrappedInList is used for. --Adam + + const + # If in the future Withdrawal have optional fields + # we should put it into consideration + wdFieldsCount = rlpFieldsCount(Withdrawal) + result.withdrawals = - if rlp.hasData: + if rlp.hasData and + rlp.isList and + rlp.listLen == wdFieldsCount: some(rlp.read(seq[Withdrawal])) else: none[seq[Withdrawal]]() diff --git a/tests/rlp/all_tests.nim b/tests/rlp/all_tests.nim index 6ccc10c..3328d98 100644 --- a/tests/rlp/all_tests.nim +++ b/tests/rlp/all_tests.nim @@ -2,4 +2,5 @@ import ./test_api_usage, ./test_common, ./test_json_suite, - ./test_object_serialization + ./test_object_serialization, + ./test_rlp_codec diff --git a/tests/rlp/rlps/blocks_1024_td_135112316.rlp b/tests/rlp/rlps/blocks_1024_td_135112316.rlp new file mode 100644 index 0000000..ae6648a Binary files /dev/null and b/tests/rlp/rlps/blocks_1024_td_135112316.rlp differ diff --git a/tests/rlp/rlps/blocks_10_td_1971072_1.rlp b/tests/rlp/rlps/blocks_10_td_1971072_1.rlp new file mode 100644 index 0000000..4463174 Binary files /dev/null and b/tests/rlp/rlps/blocks_10_td_1971072_1.rlp differ diff --git a/tests/rlp/rlps/blocks_10_td_1971072_2.rlp b/tests/rlp/rlps/blocks_10_td_1971072_2.rlp new file mode 100644 index 0000000..3a977d9 Binary files /dev/null and b/tests/rlp/rlps/blocks_10_td_1971072_2.rlp differ diff --git a/tests/rlp/rlps/blocks_10_td_1971072_3.rlp b/tests/rlp/rlps/blocks_10_td_1971072_3.rlp new file mode 100644 index 0000000..181d9fa Binary files /dev/null and b/tests/rlp/rlps/blocks_10_td_1971072_3.rlp differ diff --git a/tests/rlp/rlps/blocks_10_td_1971072_4.rlp b/tests/rlp/rlps/blocks_10_td_1971072_4.rlp new file mode 100644 index 0000000..5966fd9 Binary files /dev/null and b/tests/rlp/rlps/blocks_10_td_1971072_4.rlp differ diff --git a/tests/rlp/rlps/blocks_10_td_1971072_5.rlp b/tests/rlp/rlps/blocks_10_td_1971072_5.rlp new file mode 100644 index 0000000..0019014 Binary files /dev/null and b/tests/rlp/rlps/blocks_10_td_1971072_5.rlp differ diff --git a/tests/rlp/rlps/blocks_1_td_196416.rlp b/tests/rlp/rlps/blocks_1_td_196416.rlp new file mode 100644 index 0000000..e162948 Binary files /dev/null and b/tests/rlp/rlps/blocks_1_td_196416.rlp differ diff --git a/tests/rlp/rlps/blocks_1_td_196608.rlp b/tests/rlp/rlps/blocks_1_td_196608.rlp new file mode 100644 index 0000000..2c6c47b Binary files /dev/null and b/tests/rlp/rlps/blocks_1_td_196608.rlp differ diff --git a/tests/rlp/rlps/blocks_1_td_196704.rlp b/tests/rlp/rlps/blocks_1_td_196704.rlp new file mode 100644 index 0000000..772bf39 Binary files /dev/null and b/tests/rlp/rlps/blocks_1_td_196704.rlp differ diff --git a/tests/rlp/rlps/blocks_2_td_393120.rlp b/tests/rlp/rlps/blocks_2_td_393120.rlp new file mode 100644 index 0000000..82c0f49 Binary files /dev/null and b/tests/rlp/rlps/blocks_2_td_393120.rlp differ diff --git a/tests/rlp/rlps/blocks_2_td_393504.rlp b/tests/rlp/rlps/blocks_2_td_393504.rlp new file mode 100644 index 0000000..d3c1ef1 Binary files /dev/null and b/tests/rlp/rlps/blocks_2_td_393504.rlp differ diff --git a/tests/rlp/test_rlp_codec.nim b/tests/rlp/test_rlp_codec.nim new file mode 100644 index 0000000..6edffff --- /dev/null +++ b/tests/rlp/test_rlp_codec.nim @@ -0,0 +1,117 @@ +{.used.} + +import + std/[os, strutils], + stew/[io2, results], + unittest2, + ../../eth/[rlp, common] + +type + # trick the rlp decoder + # so we can separate the body and header + EthHeader = object + header: BlockHeader + +proc importBlock(blocksRlp: openArray[byte]): bool = + var + # the encoded rlp can contains one or more blocks + rlp = rlpFromBytes(blocksRlp) + + while rlp.hasData: + let + header = rlp.read(EthHeader).header + body = rlp.readRecordType(BlockBody, false) + + true + +proc runTest(importFile: string): bool = + let res = io2.readAllBytes(importFile) + if res.isErr: + echo "failed to import", importFile + return + + importBlock(res.get) + +suite "Partial EthBlock read using rlp.read and rlp.readRecordType": + for filename in walkDirRec("tests/rlp/rlps"): + if not filename.endsWith(".rlp"): + continue + test filename: + check runTest(filename) + +func `==`(a, b: ChainId): bool = + a.uint == b.uint + +template roundTrip(blk: EthBlock) = + let bytes = rlp.encode(blk) + let blk2 = rlp.decode(bytes, EthBlock) + check blk2 == blk + +template roundTrip(h: BlockHeader) = + let bytes = rlp.encode(h) + let h2 = rlp.decode(bytes, BlockHeader) + check h2 == h + +suite "BlockHeader roundtrip test": + test "Empty header": + let h = BlockHeader() + roundTrip(h) + + test "Header with gas": + let h = BlockHeader(gasLimit: 10.GasInt, gasUsed: 11.GasInt) + roundTrip(h) + + test "Header + some(baseFee)": + let h = BlockHeader(fee: some(1.u256)) + roundTrip(h) + + test "Header + none(baseFee) + some(withdrawalsRoot)": + let h = BlockHeader(withdrawalsRoot: some(Hash256())) + expect RlpError: + roundTrip(h) + + test "Header + none(baseFee) + some(withdrawalsRoot) + some(excessDataGas)": + let h = BlockHeader( + withdrawalsRoot: some(Hash256()), + excessDataGas: some(1.u256) + ) + expect RlpError: + roundTrip(h) + + test "Header + none(baseFee) + none(withdrawalsRoot) + some(excessDataGas)": + let h = BlockHeader( + excessDataGas: some(1.u256) + ) + expect RlpError: + roundTrip(h) + + test "Header + some(baseFee) + none(withdrawalsRoot) + some(excessDataGas)": + let h = BlockHeader( + fee: some(2.u256), + excessDataGas: some(1.u256) + ) + expect RlpError: + roundTrip(h) + + test "Header + some(baseFee) + some(withdrawalsRoot)": + let h = BlockHeader( + fee: some(2.u256), + withdrawalsRoot: some(Hash256()) + ) + roundTrip(h) + + test "Header + some(baseFee) + some(withdrawalsRoot) + some(excessDataGas)": + let h = BlockHeader( + fee: some(2.u256), + withdrawalsRoot: some(Hash256()), + excessDataGas: some(1.u256) + ) + +suite "EthBlock roundtrip test": + test "Empty EthBlock": + let blk = EthBlock() + roundTrip(blk) + + test "EthBlock with withdrawals": + let blk = EthBlock(withdrawals: some(@[Withdrawal()])) + roundTrip(blk)