implement eip2718: transaction type envelope

also add more test cases for both tx and receipt
rlp encoding decoding
This commit is contained in:
jangko 2021-05-14 21:48:21 +07:00
parent 8890175b6a
commit 61d5327f55
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
16 changed files with 3606 additions and 261 deletions

View File

@ -8,11 +8,8 @@ export
type type
Hash256* = MDigest[256] Hash256* = MDigest[256]
EthTime* = Time EthTime* = Time
VMWord* = UInt256 VMWord* = UInt256
BlockNonce* = array[8, byte] BlockNonce* = array[8, byte]
AccountNonce* = uint64 AccountNonce* = uint64
Blob* = seq[byte] Blob* = seq[byte]
@ -33,23 +30,61 @@ type
ForkID* = tuple[crc: uint32, nextFork: uint64] ForkID* = tuple[crc: uint32, nextFork: uint64]
# EIP 2364/2124 # EIP 2364/2124
BlockNumber* = UInt256
StorageKey* = array[32, byte]
# beware that although in some cases
# chainId have identical value to networkId
# they are separate entity
ChainId* = distinct uint64
Account* = object Account* = object
nonce*: AccountNonce nonce*: AccountNonce
balance*: UInt256 balance*: UInt256
storageRoot*: Hash256 storageRoot*: Hash256
codeHash*: Hash256 codeHash*: Hash256
Transaction* = object LegacyTx* = object
accountNonce*: AccountNonce nonce* : AccountNonce
gasPrice*: GasInt gasPrice*: GasInt
gasLimit*: GasInt gasLimit*: GasInt
to* {.rlpCustomSerialization.}: EthAddress to* {.rlpCustomSerialization.}: EthAddress
value*: UInt256 value* : UInt256
payload*: Blob payload* : Blob
V*: byte V* : int64
R*, S*: UInt256 R*, S* : UInt256
isContractCreation* {.rlpIgnore.}: bool isContractCreation* {.rlpIgnore.}: bool
AccessPair* = object
address* : EthAddress
storageKeys*: seq[StorageKey]
AccessList* = seq[AccessPair]
AccessListTx* = object
chainId* {.rlpCustomSerialization.}: ChainId
nonce* : AccountNonce
gasPrice* : GasInt
gasLimit* : GasInt
to* {.rlpCustomSerialization.}: EthAddress
value* : UInt256
payload* : Blob
accessList*: AccessList
V* : int64
R*, S* : UInt256
isContractCreation* {.rlpIgnore.}: bool
TxType* = enum
LegacyTxType
AccessListTxType
Transaction* = object
case txType*: TxType
of LegacyTxType:
legacyTx*: LegacyTx
of AccessListTxType:
accessListTx*: AccessListTx
TransactionStatus* = enum TransactionStatus* = enum
Unknown, Unknown,
Queued, Queued,
@ -61,8 +96,6 @@ type
status*: TransactionStatus status*: TransactionStatus
data*: Blob data*: Blob
BlockNumber* = UInt256
BlockHeader* = object BlockHeader* = object
parentHash*: Hash256 parentHash*: Hash256
ommersHash*: Hash256 ommersHash*: Hash256
@ -96,24 +129,33 @@ type
else: else:
status*: bool status*: bool
Receipt* = object LegacyReceipt* = object
stateRootOrStatus*: HashOrStatus stateRootOrStatus*: HashOrStatus
cumulativeGasUsed*: GasInt cumulativeGasUsed*: GasInt
bloom*: BloomFilter bloom*: BloomFilter
logs*: seq[Log] logs* : seq[Log]
AccessList* = object AccessListReceipt* = object
# XXX: Specify the structure of this status*: bool
cumulativeGasUsed*: GasInt
bloom* : BloomFilter
logs* : seq[Log]
ShardTransaction* = object ReceiptType* = enum
chain*: uint LegacyReceiptType
shard*: uint AccessListReceiptType
to*: EthAddress
data*: Blob Receipt* = object
gas*: GasInt case receiptType*: ReceiptType
accessList*: AccessList of LegacyReceiptType:
code*: Blob legacyReceipt*: LegacyReceipt
salt*: Hash256 of AccessListReceiptType:
accessListReceipt*: AccessListReceipt
EthBlock* = object
header*: BlockHeader
txs* : seq[Transaction]
uncles*: seq[BlockHeader]
CollationHeader* = object CollationHeader* = object
shard*: uint shard*: uint
@ -232,17 +274,17 @@ proc hashOrStatus*(hash: Hash256): HashOrStatus =
proc hashOrStatus*(status: bool): HashOrStatus = proc hashOrStatus*(status: bool): HashOrStatus =
HashOrStatus(isHash: false, status: status) HashOrStatus(isHash: false, status: status)
proc hasStatus*(rec: Receipt): bool {.inline.} = proc hasStatus*(rec: LegacyReceipt): bool {.inline.} =
rec.stateRootOrStatus.isHash == false rec.stateRootOrStatus.isHash == false
proc hasStateRoot*(rec: Receipt): bool {.inline.} = proc hasStateRoot*(rec: LegacyReceipt): bool {.inline.} =
rec.stateRootOrStatus.isHash == true rec.stateRootOrStatus.isHash == true
proc stateRoot*(rec: Receipt): Hash256 {.inline.} = proc stateRoot*(rec: LegacyReceipt): Hash256 {.inline.} =
doAssert(rec.hasStateRoot) doAssert(rec.hasStateRoot)
rec.stateRootOrStatus.hash rec.stateRootOrStatus.hash
proc status*(rec: Receipt): int {.inline.} = proc status*(rec: LegacyReceipt): int {.inline.} =
doAssert(rec.hasStatus) doAssert(rec.hasStatus)
if rec.stateRootOrStatus.status: 1 else: 0 if rec.stateRootOrStatus.status: 1 else: 0
@ -250,7 +292,7 @@ proc status*(rec: Receipt): int {.inline.} =
# Rlp serialization: # Rlp serialization:
# #
proc read*(rlp: var Rlp, T: typedesc[StUint]): T {.inline.} = proc read*(rlp: var Rlp, T: type StUint): T {.inline.} =
if rlp.isBlob: if rlp.isBlob:
let bytes = rlp.toBytes let bytes = rlp.toBytes
if bytes.len > 0: if bytes.len > 0:
@ -275,7 +317,7 @@ proc append*(rlpWriter: var RlpWriter, value: StUint) =
else: else:
rlpWriter.append(value.truncate(int)) rlpWriter.append(value.truncate(int))
proc read*(rlp: var Rlp, T: typedesc[Stint]): T {.inline.} = proc read*(rlp: var Rlp, T: type Stint): T {.inline.} =
# The Ethereum Yellow Paper defines the RLP serialization only # The Ethereum Yellow Paper defines the RLP serialization only
# for unsigned integers: # for unsigned integers:
{.fatal: "RLP serialization of signed integers is not allowed".} {.fatal: "RLP serialization of signed integers is not allowed".}
@ -287,20 +329,53 @@ proc append*(rlpWriter: var RlpWriter, value: Stint) =
{.fatal: "RLP serialization of signed integers is not allowed".} {.fatal: "RLP serialization of signed integers is not allowed".}
discard discard
proc read*(rlp: var Rlp, t: var Transaction, _: type EthAddress): EthAddress {.inline.} = type
TxTypes = LegacyTx | AccessListTx
proc read*(rlp: var Rlp, t: var TxTypes, _: type EthAddress): EthAddress {.inline.} =
if rlp.blobLen != 0: if rlp.blobLen != 0:
result = rlp.read(EthAddress) result = rlp.read(EthAddress)
else: else:
t.isContractCreation = true t.isContractCreation = true
rlp.skipElem() rlp.skipElem()
proc append*(rlpWriter: var RlpWriter, t: Transaction, a: EthAddress) {.inline.} = proc append*(rlpWriter: var RlpWriter, t: TxTypes, a: EthAddress) {.inline.} =
if t.isContractCreation: if t.isContractCreation:
rlpWriter.append("") rlpWriter.append("")
else: else:
rlpWriter.append(a) rlpWriter.append(a)
proc read*(rlp: var Rlp, T: typedesc[HashOrStatus]): T {.inline.} = proc read*(rlp: var Rlp, t: var AccessListTx, _: type ChainId): ChainId {.inline.} =
rlp.read(uint64).ChainId
proc append*(rlpWriter: var RlpWriter, t: AccessListTx, a: ChainId) {.inline.} =
rlpWriter.append(a.uint64)
proc append*(rlpWriter: var RlpWriter, tx: Transaction) =
if tx.txType == LegacyTxType:
rlpWriter.append(tx.legacyTx)
else:
var rw = initRlpWriter()
rw.append(1)
rw.append(tx.accessListTx)
rlpWriter.append(rw.finish())
proc read*(rlp: var Rlp, T: type Transaction): T =
if rlp.isList:
result = Transaction(
txType: LegacyTxType,
legacyTx: rlp.read(LegacyTx)
)
else:
let bytes = rlp.read(Blob)
var rr = rlpFromBytes(bytes)
assert(rr.read(int) == 1)
result = Transaction(
txType: AccessListTxType,
accessListTx: rr.read(AccessListTx)
)
proc read*(rlp: var Rlp, T: type HashOrStatus): T {.inline.} =
if rlp.isBlob() and (rlp.blobLen() == 32 or rlp.blobLen() == 1): if rlp.isBlob() and (rlp.blobLen() == 32 or rlp.blobLen() == 1):
if rlp.blobLen == 1: if rlp.blobLen == 1:
result = hashOrStatus(rlp.read(uint8) == 1) result = hashOrStatus(rlp.read(uint8) == 1)
@ -334,7 +409,31 @@ proc append*(rlpWriter: var RlpWriter, value: HashOrStatus) {.inline.} =
else: else:
rlpWriter.append(if value.status: 1'u8 else: 0'u8) rlpWriter.append(if value.status: 1'u8 else: 0'u8)
proc read*(rlp: var Rlp, T: typedesc[Time]): T {.inline.} = proc append*(rlpWriter: var RlpWriter, rec: Receipt) =
if rec.receiptType == LegacyReceiptType:
rlpWriter.append(rec.legacyReceipt)
else:
var rw = initRlpWriter()
rw.append(1)
rw.append(rec.accessListReceipt)
rlpWriter.append(rw.finish())
proc read*(rlp: var Rlp, T: type Receipt): T =
if rlp.isList:
result = Receipt(
receiptType: LegacyReceiptType,
legacyReceipt: rlp.read(LegacyReceipt)
)
else:
let bytes = rlp.read(Blob)
var rr = rlpFromBytes(bytes)
assert(rr.read(int) == 1)
result = Receipt(
receiptType: AccessListReceiptType,
accessListReceipt: rr.read(AccessListReceipt)
)
proc read*(rlp: var Rlp, T: type Time): T {.inline.} =
result = fromUnix(rlp.read(int64)) result = fromUnix(rlp.read(int64))
proc append*(rlpWriter: var RlpWriter, value: HashOrNum) = proc append*(rlpWriter: var RlpWriter, value: HashOrNum) =
@ -344,7 +443,7 @@ proc append*(rlpWriter: var RlpWriter, value: HashOrNum) =
else: else:
rlpWriter.append(value.number) rlpWriter.append(value.number)
proc read*(rlp: var Rlp, T: typedesc[HashOrNum]): T = proc read*(rlp: var Rlp, T: type HashOrNum): T =
if rlp.blobLen == 32: if rlp.blobLen == 32:
result = HashOrNum(isHash: true, hash: rlp.read(Hash256)) result = HashOrNum(isHash: true, hash: rlp.read(Hash256))
else: else:

View File

@ -2,8 +2,8 @@ import
nimcrypto/keccak, nimcrypto/keccak,
".."/[common, rlp, keys] ".."/[common, rlp, keys]
proc initTransaction*(nonce: AccountNonce, gasPrice, gasLimit: GasInt, to: EthAddress, proc initLegacyTx*(nonce: AccountNonce, gasPrice, gasLimit: GasInt, to: EthAddress,
value: UInt256, payload: Blob, V: byte, R, S: UInt256, isContractCreation = false): Transaction = value: UInt256, payload: Blob, V: uint64, R, S: UInt256, isContractCreation = false): LegacyTx =
result.accountNonce = nonce result.accountNonce = nonce
result.gasPrice = gasPrice result.gasPrice = gasPrice
result.gasLimit = gasLimit result.gasLimit = gasLimit
@ -40,7 +40,7 @@ proc append(rlpWriter: var RlpWriter, t: TransHashObj, a: EthAddress) {.inline.}
const const
EIP155_CHAIN_ID_OFFSET* = 35 EIP155_CHAIN_ID_OFFSET* = 35
func rlpEncode*(transaction: Transaction): auto = func rlpEncode*(transaction: LegacyTx): auto =
# Encode transaction without signature # Encode transaction without signature
return rlp.encode(TransHashObj( return rlp.encode(TransHashObj(
accountNonce: transaction.accountNonce, accountNonce: transaction.accountNonce,
@ -52,7 +52,7 @@ func rlpEncode*(transaction: Transaction): auto =
mIsContractCreation: transaction.isContractCreation mIsContractCreation: transaction.isContractCreation
)) ))
func rlpEncodeEIP155*(tx: Transaction): auto = func rlpEncodeEIP155*(tx: LegacyTx): auto =
let V = (tx.V.int - EIP155_CHAIN_ID_OFFSET) div 2 let V = (tx.V.int - EIP155_CHAIN_ID_OFFSET) div 2
# Encode transaction without signature # Encode transaction without signature
return rlp.encode(Transaction( return rlp.encode(Transaction(
@ -63,11 +63,11 @@ func rlpEncodeEIP155*(tx: Transaction): auto =
value: tx.value, value: tx.value,
payload: tx.payload, payload: tx.payload,
isContractCreation: tx.isContractCreation, isContractCreation: tx.isContractCreation,
V: V.byte, V: V.uint64,
R: 0.u256, R: 0.u256,
S: 0.u256 S: 0.u256
)) ))
func txHashNoSignature*(tx: Transaction): Hash256 = func txHashNoSignature*(tx: LegacyTx): Hash256 =
# Hash transaction without signature # Hash transaction without signature
return keccak256.digest(if tx.V.int >= EIP155_CHAIN_ID_OFFSET: tx.rlpEncodeEIP155 else: tx.rlpEncode) return keccak256.digest(if tx.V.int >= EIP155_CHAIN_ID_OFFSET: tx.rlpEncodeEIP155 else: tx.rlpEncode)

View File

@ -0,0 +1,42 @@
{
"rlp": "f90263f901f5a0151b04645af991f513d5e11f8ed62e12b73f080af9c7a6a3d98fd6b1503f23faa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940100000000000000000000000000000000000000a0968408b8a2f170bc78230cc2fac3881120a93f7392cf26b609566d8648abfd79a0e8137f2b67ac4680d8103f8b1cd7c05c49f56e4df464b0a79253679f38df9ab4a07f53535270d749f41b9d783aa1abcfceb73cc2a16ecde789cd3bc97a42fbda2ab901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000018347e7c48262740a80a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f868f866800a8301e24194000000000000000000000000000000000000aaaa808086f2ded8deec88a0e7545c5664e63873d7aeb4bb5f92152c3366a9095c9140e5f8453af0998194bea04d24143ac9bc97c0aa07a7074c12ab60484d83d3b447b1e5e6ba5cbf421675bfc0",
"json": {
"header": {
"parentHash": "0x151b04645af991f513d5e11f8ed62e12b73f080af9c7a6a3d98fd6b1503f23fa",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"miner": "0x0100000000000000000000000000000000000000",
"stateRoot": "0x968408b8a2f170bc78230cc2fac3881120a93f7392cf26b609566d8648abfd79",
"transactionsRoot": "0xe8137f2b67ac4680d8103f8b1cd7c05c49f56e4df464b0a79253679f38df9ab4",
"receiptsRoot": "0x7f53535270d749f41b9d783aa1abcfceb73cc2a16ecde789cd3bc97a42fbda2a",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"difficulty": "0x20000",
"number": "0x1",
"gasLimit": "0x47e7c4",
"gasUsed": "0x6274",
"timestamp": "0xa",
"extraData": "0x",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"hash": "0x9aa9cdb2afc4ebf893c32b67dedf1dea4291673bf4e80d3227a4e7f8d0feaf68"
},
"transactions": [
{
"type": "0x0",
"chainId": null,
"nonce": "0x0",
"gasPrice": "0xa",
"gas": "0x1e241",
"to": "0x000000000000000000000000000000000000aaaa",
"value": "0x0",
"input": "0x",
"accessList": null,
"v": "0xf2ded8deec88",
"r": "0xe7545c5664e63873d7aeb4bb5f92152c3366a9095c9140e5f8453af0998194be",
"s": "0x4d24143ac9bc97c0aa07a7074c12ab60484d83d3b447b1e5e6ba5cbf421675bf",
"hash": "0x7e122173adcc02b9a3c94da426f65c7d46e724e8e3fdb16be73d5e0405e280b6"
}
],
"uncles": null,
"receipts": null
}
}

View File

@ -0,0 +1,65 @@
{
"rlp": "f9032df901f5a09aa9cdb2afc4ebf893c32b67dedf1dea4291673bf4e80d3227a4e7f8d0feaf68a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940100000000000000000000000000000000000000a0fb70f6c0512e7979903607bba8a4f1b18c2dd1060a8eff1b777a2182d05af524a0dfcad019a36acdf0f3fac6b3db02bf1d70012a0bd3eadabea87b856be9a6f1cca0a7a57ba954834e69b55d9115305f14f7faff9beb83443f7bb5526585848c6aefb901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000028347e7c482dd201480a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f90131b8c701f8c486796f6c6f7632010a8301e24194000000000000000000000000000000000000aaaa8080f85bf859940000000000000000000000000000000000000000f842a00000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000001a0a9efa6b5fd3c2f210c4ec7232058d8dd30c93a456b4f6074429fb09bbd73eb4fa070632eff8bf0933381917c9161a98494e34fc7b836ee150445cae9ab151d267bf866020a8301e24194000000000000000000000000000000000000aaaa808086f2ded8deec88a055a39da3ce02c75e976ad84f608771080811a5ef322c6b6536c362f716c60985a018ab5df4198436eacd0e991f59bdeb8da9a75c678067db6b1a4d07881555c7a8c0",
"json": {
"header": {
"parentHash": "0x9aa9cdb2afc4ebf893c32b67dedf1dea4291673bf4e80d3227a4e7f8d0feaf68",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"miner": "0x0100000000000000000000000000000000000000",
"stateRoot": "0xfb70f6c0512e7979903607bba8a4f1b18c2dd1060a8eff1b777a2182d05af524",
"transactionsRoot": "0xdfcad019a36acdf0f3fac6b3db02bf1d70012a0bd3eadabea87b856be9a6f1cc",
"receiptsRoot": "0xa7a57ba954834e69b55d9115305f14f7faff9beb83443f7bb5526585848c6aef",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"difficulty": "0x20000",
"number": "0x2",
"gasLimit": "0x47e7c4",
"gasUsed": "0xdd20",
"timestamp": "0x14",
"extraData": "0x",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"hash": "0xe1807e7366284bc72d34c8f02318f81c6598454dbff4c6cdd71e872cd8d5748c"
},
"transactions": [
{
"type": "0x1",
"chainId": "0x796f6c6f7632",
"nonce": "0x1",
"gasPrice": "0xa",
"gas": "0x1e241",
"to": "0x000000000000000000000000000000000000aaaa",
"value": "0x0",
"input": "0x",
"accessList": [
{
"address": "0x0000000000000000000000000000000000000000",
"storageKeys": [
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000"
]
}
],
"v": "0x1",
"r": "0xa9efa6b5fd3c2f210c4ec7232058d8dd30c93a456b4f6074429fb09bbd73eb4f",
"s": "0x70632eff8bf0933381917c9161a98494e34fc7b836ee150445cae9ab151d267b",
"hash": "0x1916413ac8d1d5ee4eda786e5fe29509a109910f800894e0e18eb84abb7564cb"
},
{
"type": "0x0",
"chainId": null,
"nonce": "0x2",
"gasPrice": "0xa",
"gas": "0x1e241",
"to": "0x000000000000000000000000000000000000aaaa",
"value": "0x0",
"input": "0x",
"accessList": null,
"v": "0xf2ded8deec88",
"r": "0x55a39da3ce02c75e976ad84f608771080811a5ef322c6b6536c362f716c60985",
"s": "0x18ab5df4198436eacd0e991f59bdeb8da9a75c678067db6b1a4d07881555c7a8",
"hash": "0x54c6cfb51cbbe2c562e17a0d6ee1324f1b50013a9b9d47589cedcf0ec4ac342a"
}
],
"uncles": null,
"receipts": null
}
}

View File

@ -0,0 +1,102 @@
{
"rlp": "f904b1f901f6a0e1807e7366284bc72d34c8f02318f81c6598454dbff4c6cdd71e872cd8d5748ca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940100000000000000000000000000000000000000a025e5b4b7668760c0aa01d44c061d77e24b53f6d247d3e1d85a91e23646c1d679a0be53a36dd90786b4cd30fb0f3677ad3ce5f804a5615872a7b0512b6be82639a2a076946d9f034a6657c6832a6ad4df3ef0ddf3fa9f3f8be98ea64880fb23bd23e3b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000038347e7c48301883c1e80a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f902b4b9012301f9011f86796f6c6f7632030a8301e24194000000000000000000000000000000000000aaaa8080f8b6f859940000000000000000000000000000000000000000f842a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000f859940100000000000000000000000000000000000000f842a00000000000000000000000000000000000000000000000000000000000000000a0010000000000000000000000000000000000000000000000000000000000000001a0635c44b8110989ef84c77de11a377f19cbd84660559ed76b37ba0f7fdee1fb40a012e1576d168301614720c68f1a9e9d5ed20e23622dab6c3587b11177155d59f4b9012301f9011f86796f6c6f7632040a8301e24194000000000000000000000000000000000000aaaa8080f8b6f859940000000000000000000000000000000000000000f842a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000f859940100000000000000000000000000000000000000f842a00000000000000000000000000000000000000000000000000000000000000000a0010000000000000000000000000000000000000000000000000000000000000001a00380e446bf3992ee1df44b8177a11e948dc52cc3faa8eae059a52b7183126c0fa05206f0f35648d3b8f23092d4d7bdccb926967d840a269cf65b706a8a359b9029f866050a8301e24194000000000000000000000000000000000000aaaa808086f2ded8deec87a0ea5de382dbf422aa1bd156547a2bc3a9357d745a68cdc9203b14812307068567a0388f7425b873ae204d43d91f4997ca41d2af6759be49538b174a06f67d5741b5c0",
"json": {
"header": {
"parentHash": "0xe1807e7366284bc72d34c8f02318f81c6598454dbff4c6cdd71e872cd8d5748c",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"miner": "0x0100000000000000000000000000000000000000",
"stateRoot": "0x25e5b4b7668760c0aa01d44c061d77e24b53f6d247d3e1d85a91e23646c1d679",
"transactionsRoot": "0xbe53a36dd90786b4cd30fb0f3677ad3ce5f804a5615872a7b0512b6be82639a2",
"receiptsRoot": "0x76946d9f034a6657c6832a6ad4df3ef0ddf3fa9f3f8be98ea64880fb23bd23e3",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"difficulty": "0x20000",
"number": "0x3",
"gasLimit": "0x47e7c4",
"gasUsed": "0x1883c",
"timestamp": "0x1e",
"extraData": "0x",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"hash": "0x1615129d2aa7b437cd40e3aa1a5794af9e40b07b9b017014e4123cefeef785ce"
},
"transactions": [
{
"type": "0x1",
"chainId": "0x796f6c6f7632",
"nonce": "0x3",
"gasPrice": "0xa",
"gas": "0x1e241",
"to": "0x000000000000000000000000000000000000aaaa",
"value": "0x0",
"input": "0x",
"accessList": [
{
"address": "0x0000000000000000000000000000000000000000",
"storageKeys": [
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"address": "0x0100000000000000000000000000000000000000",
"storageKeys": [
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0100000000000000000000000000000000000000000000000000000000000000"
]
}
],
"v": "0x1",
"r": "0x635c44b8110989ef84c77de11a377f19cbd84660559ed76b37ba0f7fdee1fb40",
"s": "0x12e1576d168301614720c68f1a9e9d5ed20e23622dab6c3587b11177155d59f4",
"hash": "0x54471f503901f2c2fe392e43df0377a11f9a1cd9cde0ec5c68c89eac1723e02a"
},
{
"type": "0x1",
"chainId": "0x796f6c6f7632",
"nonce": "0x4",
"gasPrice": "0xa",
"gas": "0x1e241",
"to": "0x000000000000000000000000000000000000aaaa",
"value": "0x0",
"input": "0x",
"accessList": [
{
"address": "0x0000000000000000000000000000000000000000",
"storageKeys": [
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"address": "0x0100000000000000000000000000000000000000",
"storageKeys": [
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0100000000000000000000000000000000000000000000000000000000000000"
]
}
],
"v": "0x1",
"r": "0x380e446bf3992ee1df44b8177a11e948dc52cc3faa8eae059a52b7183126c0f",
"s": "0x5206f0f35648d3b8f23092d4d7bdccb926967d840a269cf65b706a8a359b9029",
"hash": "0xe3a1354e84d22ebbca0c55751937b075083f0f0e3a47c65fc7249d86f872f268"
},
{
"type": "0x0",
"chainId": null,
"nonce": "0x5",
"gasPrice": "0xa",
"gas": "0x1e241",
"to": "0x000000000000000000000000000000000000aaaa",
"value": "0x0",
"input": "0x",
"accessList": null,
"v": "0xf2ded8deec87",
"r": "0xea5de382dbf422aa1bd156547a2bc3a9357d745a68cdc9203b14812307068567",
"s": "0x388f7425b873ae204d43d91f4997ca41d2af6759be49538b174a06f67d5741b5",
"hash": "0x44b1e9999ac02a3b0daf1391be16bb00a262079706418697094bab5f37b522d7"
}
],
"uncles": null,
"receipts": null
}
}

View File

@ -0,0 +1,153 @@
{
"rlp": "f906ebf901f6a01615129d2aa7b437cd40e3aa1a5794af9e40b07b9b017014e4123cefeef785cea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940100000000000000000000000000000000000000a0e2c895d53740e942c84623e1612189e72eea9e767b150cc0d8802b490f2574b2a060b6fa13cd50ea3732f59eb726368639dea2006ab4b67dc5730ea27b0cd75732a058ac726e0fc55cd550539c38baf3f6023615e2657ceedccc21ce7edd6516bc31b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000083020000048347e7c4830263c82880a00000000000000000000000000000000000000000000000000000000000000000880000000000000000f904eeb9017f01f9017b86796f6c6f7632060a8301e24194000000000000000000000000000000000000aaaa8080f90111f859940000000000000000000000000000000000000000f842a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000f859940100000000000000000000000000000000000000f842a00000000000000000000000000000000000000000000000000000000000000000a00100000000000000000000000000000000000000000000000000000000000000f859940200000000000000000000000000000000000000f842a00000000000000000000000000000000000000000000000000000000000000000a0020000000000000000000000000000000000000000000000000000000000000001a0587b3fd59a84469be7c918ae73047141027d27c47cc63f3cf13c6161bf92efaaa0453d3cd2dffd1f048a58fb1046917327f5e95cc8aedc47f82d41bffebfe31bbdb9017f01f9017b86796f6c6f7632070a8301e24194000000000000000000000000000000000000aaaa8080f90111f859940000000000000000000000000000000000000000f842a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000f859940100000000000000000000000000000000000000f842a00000000000000000000000000000000000000000000000000000000000000000a00100000000000000000000000000000000000000000000000000000000000000f859940200000000000000000000000000000000000000f842a00000000000000000000000000000000000000000000000000000000000000000a0020000000000000000000000000000000000000000000000000000000000000080a0b1944f4797bf4da4d041a66be9c41c317b40d158894103ccd50de5e4ca174b4aa079d6834c013b40d0039ca6755cecc72938461e5b8cd8d3619ddcdfb15c15b866b9017f01f9017b86796f6c6f7632080a8301e24194000000000000000000000000000000000000aaaa8080f90111f859940000000000000000000000000000000000000000f842a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000f859940100000000000000000000000000000000000000f842a00000000000000000000000000000000000000000000000000000000000000000a00100000000000000000000000000000000000000000000000000000000000000f859940200000000000000000000000000000000000000f842a00000000000000000000000000000000000000000000000000000000000000000a0020000000000000000000000000000000000000000000000000000000000000001a0793cd38309611e0117269275243fc3761fe233dc371918e450b547c70132c033a07276d4b20788938ca0ff7e4e921aec7739103dc73d3b953f47947ef194720757f866090a8301e24194000000000000000000000000000000000000aaaa808086f2ded8deec88a0a6528544064aef10d166e16d3968ae83aa29161beb57f2756dfd1151682adbe7a0507fe2d475ffab209e718345a3ad9fb20fee6e15a8c4980df3ed8ff0a714367ac0",
"json": {
"header": {
"parentHash": "0x1615129d2aa7b437cd40e3aa1a5794af9e40b07b9b017014e4123cefeef785ce",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"miner": "0x0100000000000000000000000000000000000000",
"stateRoot": "0xe2c895d53740e942c84623e1612189e72eea9e767b150cc0d8802b490f2574b2",
"transactionsRoot": "0x60b6fa13cd50ea3732f59eb726368639dea2006ab4b67dc5730ea27b0cd75732",
"receiptsRoot": "0x58ac726e0fc55cd550539c38baf3f6023615e2657ceedccc21ce7edd6516bc31",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"difficulty": "0x20000",
"number": "0x4",
"gasLimit": "0x47e7c4",
"gasUsed": "0x263c8",
"timestamp": "0x28",
"extraData": "0x",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"hash": "0xc6340ac0a1b76cae78479a4bc6211031550719fa9b58ae42d773dce7ff7409ce"
},
"transactions": [
{
"type": "0x1",
"chainId": "0x796f6c6f7632",
"nonce": "0x6",
"gasPrice": "0xa",
"gas": "0x1e241",
"to": "0x000000000000000000000000000000000000aaaa",
"value": "0x0",
"input": "0x",
"accessList": [
{
"address": "0x0000000000000000000000000000000000000000",
"storageKeys": [
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"address": "0x0100000000000000000000000000000000000000",
"storageKeys": [
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0100000000000000000000000000000000000000000000000000000000000000"
]
},
{
"address": "0x0200000000000000000000000000000000000000",
"storageKeys": [
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0200000000000000000000000000000000000000000000000000000000000000"
]
}
],
"v": "0x1",
"r": "0x587b3fd59a84469be7c918ae73047141027d27c47cc63f3cf13c6161bf92efaa",
"s": "0x453d3cd2dffd1f048a58fb1046917327f5e95cc8aedc47f82d41bffebfe31bbd",
"hash": "0xbc9b06b5d31e6e257a3c25048e935ba433c68fdd5fa5d0ddd726d5ab7356aafd"
},
{
"type": "0x1",
"chainId": "0x796f6c6f7632",
"nonce": "0x7",
"gasPrice": "0xa",
"gas": "0x1e241",
"to": "0x000000000000000000000000000000000000aaaa",
"value": "0x0",
"input": "0x",
"accessList": [
{
"address": "0x0000000000000000000000000000000000000000",
"storageKeys": [
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"address": "0x0100000000000000000000000000000000000000",
"storageKeys": [
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0100000000000000000000000000000000000000000000000000000000000000"
]
},
{
"address": "0x0200000000000000000000000000000000000000",
"storageKeys": [
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0200000000000000000000000000000000000000000000000000000000000000"
]
}
],
"v": "0x0",
"r": "0xb1944f4797bf4da4d041a66be9c41c317b40d158894103ccd50de5e4ca174b4a",
"s": "0x79d6834c013b40d0039ca6755cecc72938461e5b8cd8d3619ddcdfb15c15b866",
"hash": "0x474ec2766068f9e9d0548aca3769ca406fdde21ba87c254d9230c18be35fe99a"
},
{
"type": "0x1",
"chainId": "0x796f6c6f7632",
"nonce": "0x8",
"gasPrice": "0xa",
"gas": "0x1e241",
"to": "0x000000000000000000000000000000000000aaaa",
"value": "0x0",
"input": "0x",
"accessList": [
{
"address": "0x0000000000000000000000000000000000000000",
"storageKeys": [
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"address": "0x0100000000000000000000000000000000000000",
"storageKeys": [
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0100000000000000000000000000000000000000000000000000000000000000"
]
},
{
"address": "0x0200000000000000000000000000000000000000",
"storageKeys": [
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0200000000000000000000000000000000000000000000000000000000000000"
]
}
],
"v": "0x1",
"r": "0x793cd38309611e0117269275243fc3761fe233dc371918e450b547c70132c033",
"s": "0x7276d4b20788938ca0ff7e4e921aec7739103dc73d3b953f47947ef194720757",
"hash": "0xe46963eb63cd75a120990ca597532020bc2dcefc4588025c0d50901681a7aae9"
},
{
"type": "0x0",
"chainId": null,
"nonce": "0x9",
"gasPrice": "0xa",
"gas": "0x1e241",
"to": "0x000000000000000000000000000000000000aaaa",
"value": "0x0",
"input": "0x",
"accessList": null,
"v": "0xf2ded8deec88",
"r": "0xa6528544064aef10d166e16d3968ae83aa29161beb57f2756dfd1151682adbe7",
"s": "0x507fe2d475ffab209e718345a3ad9fb20fee6e15a8c4980df3ed8ff0a714367a",
"hash": "0x61fc16fea6b57d81f269e733cdd15d994bbc10dc6e9b0acbedbe32e6fe70a06c"
}
],
"uncles": null,
"receipts": null
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,39 @@
{
"config": {
"chainId": 133519467574834,
"homesteadBlock": 0,
"daoForkSupport": true,
"eip150Block": 0,
"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
"yoloV2Block": 0,
"clique": {
"period": 15,
"epoch": 30000
}
},
"nonce": "0x0",
"timestamp": "0x0",
"extraData": "0x",
"gasLimit": "0x0",
"difficulty": null,
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x0000000000000000000000000000000000000000",
"alloc": {
"000000000000000000000000000000000000aaaa": {
"code": "0x58585454",
"balance": "0x0"
},
"71562b71999873db5b286df957af199ec94617f7": {
"balance": "0x3b9aca00"
}
},
"number": "0x0",
"gasUsed": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}

View File

@ -9,205 +9,209 @@ proc q(s: string): string = "\"" & s & "\""
proc i(s: string): string = s.replace(" ").replace("\n") proc i(s: string): string = s.replace(" ").replace("\n")
proc inspectMatch(r: Rlp, s: string): bool = r.inspect.i == s.i proc inspectMatch(r: Rlp, s: string): bool = r.inspect.i == s.i
test "empty bytes are not a proper RLP": proc suite() =
var rlp = rlpFromBytes seq[byte](@[]) suite "test api usage":
test "empty bytes are not a proper RLP":
var rlp = rlpFromBytes seq[byte](@[])
check: check:
not rlp.hasData not rlp.hasData
not rlp.isBlob not rlp.isBlob
not rlp.isList not rlp.isList
not rlp.isEmpty not rlp.isEmpty
expect Exception: expect Exception:
rlp.skipElem rlp.skipElem
expect Exception: expect Exception:
discard rlp.getType discard rlp.getType
expect Exception: expect Exception:
for e in rlp: for e in rlp:
discard e.getType discard e.getType
test "you cannot finish a list without appending enough elements": test "you cannot finish a list without appending enough elements":
var writer = initRlpList(3) var writer = initRlpList(3)
writer.append "foo" writer.append "foo"
writer.append "bar" writer.append "bar"
expect Defect: expect Defect:
discard writer.finish discard writer.finish
test "encode/decode object": test "encode/decode object":
type type
MyEnum = enum MyEnum = enum
foo, foo,
bar bar
MyObj = object MyObj = object
a: array[3, char] a: array[3, char]
b: int b: int
c: MyEnum c: MyEnum
var input: MyObj var input: MyObj
input.a = ['e', 't', 'h'] input.a = ['e', 't', 'h']
input.b = 63 input.b = 63
input.c = bar input.c = bar
var writer = initRlpWriter() var writer = initRlpWriter()
writer.append(input) writer.append(input)
let bytes = writer.finish() let bytes = writer.finish()
var rlp = rlpFromBytes(bytes) var rlp = rlpFromBytes(bytes)
var output = rlp.read(MyObj) var output = rlp.read(MyObj)
check: check:
input == output input == output
test "encode and decode lists": test "encode and decode lists":
var writer = initRlpList(3) var writer = initRlpList(3)
writer.append "foo" writer.append "foo"
writer.append ["bar", "baz"] writer.append ["bar", "baz"]
writer.append [30, 40, 50] writer.append [30, 40, 50]
var var
bytes = writer.finish bytes = writer.finish
rlp = rlpFromBytes bytes rlp = rlpFromBytes bytes
check: check:
bytes.toHex == "d183666f6fc8836261728362617ac31e2832" bytes.toHex == "d183666f6fc8836261728362617ac31e2832"
rlp.inspectMatch """ rlp.inspectMatch """
{ {
"foo" "foo"
{ {
"bar" "bar"
"baz" "baz"
} }
{ {
byte 30 byte 30
byte 40 byte 40
byte 50 byte 50
} }
} }
""" """
bytes = encodeList(6000, bytes = encodeList(6000,
"Lorem ipsum dolor sit amet", "Lorem ipsum dolor sit amet",
"Donec ligula tortor, egestas eu est vitae") "Donec ligula tortor, egestas eu est vitae")
rlp = rlpFromBytes bytes rlp = rlpFromBytes bytes
check: check:
rlp.listLen == 3 rlp.listLen == 3
rlp.listElem(0).toInt(int) == 6000 rlp.listElem(0).toInt(int) == 6000
rlp.listElem(1).toString == "Lorem ipsum dolor sit amet" rlp.listElem(1).toString == "Lorem ipsum dolor sit amet"
rlp.listElem(2).toString == "Donec ligula tortor, egestas eu est vitae" rlp.listElem(2).toString == "Donec ligula tortor, egestas eu est vitae"
# test creating RLPs from other RLPs # test creating RLPs from other RLPs
var list = rlpFromBytes encodeList(rlp.listELem(1), rlp.listELem(0)) var list = rlpFromBytes encodeList(rlp.listELem(1), rlp.listELem(0))
# test that iteration with enterList/skipElem works as expected # test that iteration with enterList/skipElem works as expected
doAssert list.enterList # We already know that we are working with a list doAssert list.enterList # We already know that we are working with a list
check list.toString == "Lorem ipsum dolor sit amet" check list.toString == "Lorem ipsum dolor sit amet"
list.skipElem list.skipElem
check list.toInt(int32) == 6000.int32 check list.toInt(int32) == 6000.int32
var intVar: int var intVar: int
list >> intVar list >> intVar
check intVar == 6000 check intVar == 6000
check(not list.hasData) check(not list.hasData)
expect Exception: list.skipElem expect Exception: list.skipElem
test "toBytes": test "toBytes":
let rlp = rlpFromHex("f2cb847f000001827666827666a040ef02798f211da2e8173d37f255be908871ae65060dbb2f77fb29c0421447f4845ab90b50") let rlp = rlpFromHex("f2cb847f000001827666827666a040ef02798f211da2e8173d37f255be908871ae65060dbb2f77fb29c0421447f4845ab90b50")
let tok = rlp.listElem(1).toBytes() let tok = rlp.listElem(1).toBytes()
check: check:
tok.len == 32 tok.len == 32
tok.toHex == "40ef02798f211da2e8173d37f255be908871ae65060dbb2f77fb29c0421447f4" tok.toHex == "40ef02798f211da2e8173d37f255be908871ae65060dbb2f77fb29c0421447f4"
test "nested lists": test "nested lists":
let listBytes = encode([[1, 2, 3], [5, 6, 7]]) let listBytes = encode([[1, 2, 3], [5, 6, 7]])
let listRlp = rlpFromBytes listBytes let listRlp = rlpFromBytes listBytes
let sublistRlp0 = listRlp.listElem(0) let sublistRlp0 = listRlp.listElem(0)
let sublistRlp1 = listRlp.listElem(1) let sublistRlp1 = listRlp.listElem(1)
check sublistRlp0.listElem(0).toInt(int) == 1 check sublistRlp0.listElem(0).toInt(int) == 1
check sublistRlp0.listElem(1).toInt(int) == 2 check sublistRlp0.listElem(1).toInt(int) == 2
check sublistRlp0.listElem(2).toInt(int) == 3 check sublistRlp0.listElem(2).toInt(int) == 3
check sublistRlp1.listElem(0).toInt(int) == 5 check sublistRlp1.listElem(0).toInt(int) == 5
check sublistRlp1.listElem(1).toInt(int) == 6 check sublistRlp1.listElem(1).toInt(int) == 6
check sublistRlp1.listElem(2).toInt(int) == 7 check sublistRlp1.listElem(2).toInt(int) == 7
test "encoding length": test "encoding length":
let listBytes = encode([1,2,3,4,5]) let listBytes = encode([1,2,3,4,5])
let listRlp = rlpFromBytes listBytes let listRlp = rlpFromBytes listBytes
check listRlp.listLen == 5 check listRlp.listLen == 5
let emptyListBytes = encode "" let emptyListBytes = encode ""
check emptyListBytes.len == 1 check emptyListBytes.len == 1
let emptyListRlp = rlpFromBytes emptyListBytes let emptyListRlp = rlpFromBytes emptyListBytes
check emptyListRlp.blobLen == 0 check emptyListRlp.blobLen == 0
test "basic decoding": test "basic decoding":
var rlp1 = rlpFromHex("856d6f6f7365") var rlp1 = rlpFromHex("856d6f6f7365")
var rlp2 = rlpFromHex("0x856d6f6f7365") var rlp2 = rlpFromHex("0x856d6f6f7365")
check: check:
rlp1.inspect == q"moose" rlp1.inspect == q"moose"
rlp2.inspect == q"moose" rlp2.inspect == q"moose"
test "malformed/truncated RLP": test "malformed/truncated RLP":
var rlp = rlpFromHex("b8056d6f6f7365") var rlp = rlpFromHex("b8056d6f6f7365")
expect MalformedRlpError: expect MalformedRlpError:
discard rlp.inspect discard rlp.inspect
test "encode byte arrays": test "encode byte arrays":
var b1 = [byte(1), 2, 5, 7, 8] var b1 = [byte(1), 2, 5, 7, 8]
var b2 = [byte(6), 8, 12, 123] var b2 = [byte(6), 8, 12, 123]
var b3 = @[byte(122), 56, 65, 12] var b3 = @[byte(122), 56, 65, 12]
let rlp = rlpFromBytes(encode((b1, b2, b3))) let rlp = rlpFromBytes(encode((b1, b2, b3)))
check: check:
rlp.listLen == 3 rlp.listLen == 3
rlp.listElem(0).toBytes() == b1 rlp.listElem(0).toBytes() == b1
rlp.listElem(1).toBytes() == b2 rlp.listElem(1).toBytes() == b2
rlp.listElem(2).toBytes() == b3 rlp.listElem(2).toBytes() == b3
# The first byte here is the length of the datum (132 - 128 => 4) # The first byte here is the length of the datum (132 - 128 => 4)
$(rlp.listElem(1).rawData) == "[132, 6, 8, 12, 123]" $(rlp.listElem(1).rawData) == "[132, 6, 8, 12, 123]"
test "empty byte arrays": test "empty byte arrays":
var var
rlp = rlpFromBytes rlp.encode("") rlp = rlpFromBytes rlp.encode("")
b = rlp.toBytes b = rlp.toBytes
check $b == "@[]" check $b == "@[]"
test "encode/decode floats": test "encode/decode floats":
for f in [high(float64), low(float64), 0.1, 122.23, for f in [high(float64), low(float64), 0.1, 122.23,
103487315.128934, 103487315.128934,
1943935743563457201.391754032785692, 1943935743563457201.391754032785692,
0, -0, 0, -0,
Inf, NegInf, NaN]: Inf, NegInf, NaN]:
template isNaN(n): bool = template isNaN(n): bool =
classify(n) == fcNaN classify(n) == fcNaN
template chk(input) = template chk(input) =
let restored = decode(encode(input), float64) let restored = decode(encode(input), float64)
check restored == input or (input.isNaN and restored.isNaN) check restored == input or (input.isNaN and restored.isNaN)
chk f chk f
chk -f chk -f
test "invalid enum": test "invalid enum":
type type
MyEnum = enum MyEnum = enum
foo, foo,
bar bar
var writer = initRlpWriter() var writer = initRlpWriter()
writer.append(2) writer.append(2)
writer.append(-1) writer.append(-1)
let bytes = writer.finish() let bytes = writer.finish()
var rlp = rlpFromBytes(bytes) var rlp = rlpFromBytes(bytes)
expect RlpTypeMismatch: expect RlpTypeMismatch:
discard rlp.read(MyEnum) discard rlp.read(MyEnum)
rlp.skipElem() rlp.skipElem()
expect RlpTypeMismatch: expect RlpTypeMismatch:
discard rlp.read(MyEnum) discard rlp.read(MyEnum)
suite()

View File

@ -1,7 +1,8 @@
{.used.} {.used.}
import import
std/unittest, std/[unittest, os, json],
stew/byteutils,
../../eth/[common, rlp] ../../eth/[common, rlp]
proc `==`(a, b: HashOrStatus): bool = proc `==`(a, b: HashOrStatus): bool =
@ -12,29 +13,92 @@ proc `==`(a, b: HashOrStatus): bool =
else: else:
result = result and a.status == b.status result = result and a.status == b.status
suite "rlp encoding": func `==`(a, b: ChainId): bool =
a.uint64 == b.uint64
test "receipt roundtrip": func `==`(a, b: Transaction): bool =
var a, b, c, d: Receipt if a.txType != b.txType:
return false
if a.txType == LegacyTxType:
return a.legacyTx == b.legacyTx
else:
return a.accessListTx == b.accessListTx
var hash = rlpHash(a) func `==`(a, b: Receipt): bool =
a.stateRootOrStatus = hashOrStatus(hash) if a.receiptType != b.receiptType:
a.cumulativeGasUsed = 21000 return false
a.logs = @[] if a.receiptType == LegacyReceiptType:
return a.legacyReceipt == b.legacyReceipt
else:
return a.accessListReceipt == b.accessListReceipt
b.stateRootOrStatus = hashOrStatus(true) proc loadFile(x: int) =
b.cumulativeGasUsed = 52000 let fileName = "tests" / "rlp" / "eip2718" / "acl_block_" & $x & ".json"
b.logs = @[] test fileName:
let n = json.parseFile(fileName)
let data = n["rlp"].getStr()
var bytes = hexToSeqByte(data)
var blk = rlp.decode(bytes, EthBlock)
var x = rlp.encode(a) let rlpbytes = rlp.encode(blk)
var y = rlp.encode(b) var blk2 = rlp.decode(rlpbytes, EthBlock)
check blk == blk2
check bytes == rlpbytes
c = x.decode(Receipt) proc suite1() =
d = y.decode(Receipt) suite "rlp encoding":
check c == a test "receipt roundtrip":
check d == b let a = Receipt(
receiptType: LegacyReceiptType,
legacyReceipt: LegacyReceipt(
stateRootOrStatus: hashOrStatus(true),
cumulativeGasUsed: 51000
)
)
check c.hasStateRoot let hash = rlpHash(a)
check c.stateRoot == hash let b = Receipt(
check d.hasStatus receiptType: LegacyReceiptType,
check d.status == 1 legacyReceipt: LegacyReceipt(
stateRootOrStatus: hashOrStatus(hash),
cumulativeGasUsed: 21000
)
)
let abytes = rlp.encode(a)
let bbytes = rlp.encode(b)
let aa = rlp.decode(abytes, Receipt)
let bb = rlp.decode(bbytes, Receipt)
check aa == a
check bb == b
test "access list receipt":
let a = Receipt(
receiptType: AccessListReceiptType,
accessListReceipt: AccessListReceipt(
status: true
)
)
let b = Receipt(
receiptType: AccessListReceiptType,
accessListReceipt: AccessListReceipt(
status: false,
cumulativeGasUsed: 21000
)
)
let abytes = rlp.encode(a)
let bbytes = rlp.encode(b)
let aa = rlp.decode(abytes, Receipt)
let bb = rlp.decode(bbytes, Receipt)
check aa == a
check bb == b
proc suite2() =
suite "eip 2718 transaction":
for i in 0..<10:
loadFile(i)
suite1()
suite2()

View File

@ -43,41 +43,45 @@ proc read*(rlp: var Rlp, holder: var CustomSerialized, T: type Foo): Foo =
result.y = newString(rlp.read(int)) result.y = newString(rlp.read(int))
holder.ignored = rlp.read(int) * 2 holder.ignored = rlp.read(int) * 2
test "encoding and decoding an object": proc suite() =
var originalBar = Bar(b: "abracadabra", suite "object serialization":
f: Foo(x: 5'u64, y: "hocus pocus", z: @[100, 200, 300])) test "encoding and decoding an object":
var originalBar = Bar(b: "abracadabra",
f: Foo(x: 5'u64, y: "hocus pocus", z: @[100, 200, 300]))
var bytes = encode(originalBar) var bytes = encode(originalBar)
var r = rlpFromBytes(bytes) var r = rlpFromBytes(bytes)
var restoredBar = r.read(Bar) var restoredBar = r.read(Bar)
check: check:
originalBar == restoredBar originalBar == restoredBar
var t1 = Transaction(time: getTime(), amount: 1000, sender: "Alice", receiver: "Bob") var t1 = Transaction(time: getTime(), amount: 1000, sender: "Alice", receiver: "Bob")
bytes = encode(t1) bytes = encode(t1)
var t2 = bytes.decode(Transaction) var t2 = bytes.decode(Transaction)
check: check:
bytes.toHex == "cd85416c69636583426f628203e8" # verifies that Alice comes first bytes.toHex == "cd85416c69636583426f628203e8" # verifies that Alice comes first
t2.time == default(Time) t2.time == default(Time)
t2.sender == "Alice" t2.sender == "Alice"
t2.receiver == "Bob" t2.receiver == "Bob"
t2.amount == 1000 t2.amount == 1000
test "custom field serialization": test "custom field serialization":
var origVal = CustomSerialized(customFoo: Foo(x: 10'u64, y: "y", z: @[]), ignored: 5) var origVal = CustomSerialized(customFoo: Foo(x: 10'u64, y: "y", z: @[]), ignored: 5)
var bytes = encode(origVal) var bytes = encode(origVal)
var r = rlpFromBytes(bytes) var r = rlpFromBytes(bytes)
var restored = r.read(CustomSerialized) var restored = r.read(CustomSerialized)
check: check:
origVal.customFoo.x == restored.customFoo.x origVal.customFoo.x == restored.customFoo.x
origVal.customFoo.y.len == restored.customFoo.y.len origVal.customFoo.y.len == restored.customFoo.y.len
restored.ignored == 10 restored.ignored == 10
test "RLP fields count": test "RLP fields count":
check: check:
Bar.rlpFieldsCount == 2 Bar.rlpFieldsCount == 2
Foo.rlpFieldsCount == 3 Foo.rlpFieldsCount == 3
Transaction.rlpFieldsCount == 3 Transaction.rlpFieldsCount == 3
suite()