mirror of https://github.com/status-im/nim-eth.git
implement EIP-1559 Transaction and BlockHeader
This commit is contained in:
parent
601fa7ff66
commit
a10d301085
|
@ -4,7 +4,7 @@ import
|
||||||
../rlp, ../trie/[trie_defs, db]
|
../rlp, ../trie/[trie_defs, db]
|
||||||
|
|
||||||
export
|
export
|
||||||
stint, read, append, KeccakHash, rlp
|
stint, read, append, KeccakHash, rlp, options
|
||||||
|
|
||||||
type
|
type
|
||||||
Hash256* = MDigest[256]
|
Hash256* = MDigest[256]
|
||||||
|
@ -44,46 +44,31 @@ type
|
||||||
storageRoot*: Hash256
|
storageRoot*: Hash256
|
||||||
codeHash*: Hash256
|
codeHash*: Hash256
|
||||||
|
|
||||||
LegacyTx* = object
|
|
||||||
nonce* : AccountNonce
|
|
||||||
gasPrice*: GasInt
|
|
||||||
gasLimit*: GasInt
|
|
||||||
to* {.rlpCustomSerialization.}: EthAddress
|
|
||||||
value* : UInt256
|
|
||||||
payload* : Blob
|
|
||||||
V* : int64
|
|
||||||
R*, S* : UInt256
|
|
||||||
isContractCreation* {.rlpIgnore.}: bool
|
|
||||||
|
|
||||||
AccessPair* = object
|
AccessPair* = object
|
||||||
address* : EthAddress
|
address* : EthAddress
|
||||||
storageKeys*: seq[StorageKey]
|
storageKeys*: seq[StorageKey]
|
||||||
|
|
||||||
AccessList* = seq[AccessPair]
|
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
|
TxType* = enum
|
||||||
LegacyTxType
|
TxLegacy
|
||||||
AccessListTxType
|
TxEip2930
|
||||||
|
TxEip1559
|
||||||
|
|
||||||
Transaction* = object
|
Transaction* = object
|
||||||
case txType*: TxType
|
txType* : TxType # EIP-2718
|
||||||
of LegacyTxType:
|
chainId* : ChainId # EIP-2930
|
||||||
legacyTx*: LegacyTx
|
nonce* : AccountNonce
|
||||||
of AccessListTxType:
|
gasPrice* : GasInt
|
||||||
accessListTx*: AccessListTx
|
maxPriorityFee*: GasInt # EIP-1559
|
||||||
|
maxFee* : GasInt # EIP-1559
|
||||||
|
gasLimit* : GasInt
|
||||||
|
to* : Option[EthAddress]
|
||||||
|
value* : UInt256
|
||||||
|
payload* : Blob
|
||||||
|
accessList* : AccessList # EIP-2930
|
||||||
|
V* : int64
|
||||||
|
R*, S* : UInt256
|
||||||
|
|
||||||
TransactionStatus* = enum
|
TransactionStatus* = enum
|
||||||
Unknown,
|
Unknown,
|
||||||
|
@ -112,6 +97,8 @@ type
|
||||||
extraData*: Blob
|
extraData*: Blob
|
||||||
mixDigest*: Hash256
|
mixDigest*: Hash256
|
||||||
nonce*: BlockNonce
|
nonce*: BlockNonce
|
||||||
|
# `baseFee` is the get/set of `fee`
|
||||||
|
fee*: Option[UInt256] # EIP-1559
|
||||||
|
|
||||||
BlockBody* = object
|
BlockBody* = object
|
||||||
transactions*{.rlpCustomSerialization.}: seq[Transaction]
|
transactions*{.rlpCustomSerialization.}: seq[Transaction]
|
||||||
|
@ -122,35 +109,21 @@ type
|
||||||
topics*: seq[Topic]
|
topics*: seq[Topic]
|
||||||
data*: Blob
|
data*: Blob
|
||||||
|
|
||||||
HashOrStatus* = object
|
# easily convertible between
|
||||||
case isHash*: bool
|
# ReceiptType and TxType
|
||||||
of true:
|
ReceiptType* = TxType
|
||||||
hash*: Hash256
|
# LegacyReceipt = TxLegacy
|
||||||
else:
|
# Eip2930Receipt = TxEip2930
|
||||||
status*: bool
|
# Eip1559Receipt = TxEip1559
|
||||||
|
|
||||||
LegacyReceipt* = object
|
|
||||||
stateRootOrStatus*: HashOrStatus
|
|
||||||
cumulativeGasUsed*: GasInt
|
|
||||||
bloom*: BloomFilter
|
|
||||||
logs* : seq[Log]
|
|
||||||
|
|
||||||
AccessListReceipt* = object
|
|
||||||
status*: bool
|
|
||||||
cumulativeGasUsed*: GasInt
|
|
||||||
bloom* : BloomFilter
|
|
||||||
logs* : seq[Log]
|
|
||||||
|
|
||||||
ReceiptType* = enum
|
|
||||||
LegacyReceiptType
|
|
||||||
AccessListReceiptType
|
|
||||||
|
|
||||||
Receipt* = object
|
Receipt* = object
|
||||||
case receiptType*: ReceiptType
|
receiptType* : ReceiptType
|
||||||
of LegacyReceiptType:
|
isHash* : bool # hash or status
|
||||||
legacyReceipt*: LegacyReceipt
|
status* : bool # EIP-658
|
||||||
of AccessListReceiptType:
|
hash* : Hash256
|
||||||
accessListReceipt*: AccessListReceipt
|
cumulativeGasUsed*: GasInt
|
||||||
|
bloom* : BloomFilter
|
||||||
|
logs* : seq[Log]
|
||||||
|
|
||||||
EthBlock* = object
|
EthBlock* = object
|
||||||
header*: BlockHeader
|
header*: BlockHeader
|
||||||
|
@ -222,6 +195,11 @@ type
|
||||||
OK
|
OK
|
||||||
Error
|
Error
|
||||||
|
|
||||||
|
const
|
||||||
|
LegacyReceipt* = TxLegacy
|
||||||
|
Eip2930Receipt* = TxEip2930
|
||||||
|
Eip1559Receipt* = TxEip1559
|
||||||
|
|
||||||
when BlockNumber is int64:
|
when BlockNumber is int64:
|
||||||
## The goal of these templates is to make it easier to switch
|
## The goal of these templates is to make it easier to switch
|
||||||
## the block number type to a different representation
|
## the block number type to a different representation
|
||||||
|
@ -256,6 +234,16 @@ else:
|
||||||
template u256*(n: BlockNumber): UInt256 =
|
template u256*(n: BlockNumber): UInt256 =
|
||||||
n
|
n
|
||||||
|
|
||||||
|
# EIP-1559 conveniences
|
||||||
|
func baseFee*(h: BlockHeader | BlockHeaderRef): UInt256 =
|
||||||
|
if h.fee.isSome:
|
||||||
|
h.fee.get()
|
||||||
|
else:
|
||||||
|
0.u256
|
||||||
|
|
||||||
|
template `baseFee=`*(h: BlockHeader | BlockHeaderRef, data: UInt256) =
|
||||||
|
h.fee = some(data)
|
||||||
|
|
||||||
func toBlockNonce*(n: uint64): BlockNonce =
|
func toBlockNonce*(n: uint64): BlockNonce =
|
||||||
n.toBytesBE()
|
n.toBytesBE()
|
||||||
|
|
||||||
|
@ -268,25 +256,24 @@ proc newAccount*(nonce: AccountNonce = 0, balance: UInt256 = 0.u256): Account =
|
||||||
result.storageRoot = emptyRlpHash
|
result.storageRoot = emptyRlpHash
|
||||||
result.codeHash = blankStringHash
|
result.codeHash = blankStringHash
|
||||||
|
|
||||||
proc hashOrStatus*(hash: Hash256): HashOrStatus =
|
proc hasStatus*(rec: Receipt): bool {.inline.} =
|
||||||
HashOrStatus(isHash: true, hash: hash)
|
rec.isHash == false
|
||||||
|
|
||||||
proc hashOrStatus*(status: bool): HashOrStatus =
|
proc hasStateRoot*(rec: Receipt): bool {.inline.} =
|
||||||
HashOrStatus(isHash: false, status: status)
|
rec.isHash == true
|
||||||
|
|
||||||
proc hasStatus*(rec: LegacyReceipt): bool {.inline.} =
|
proc stateRoot*(rec: Receipt): Hash256 {.inline.} =
|
||||||
rec.stateRootOrStatus.isHash == false
|
|
||||||
|
|
||||||
proc hasStateRoot*(rec: LegacyReceipt): bool {.inline.} =
|
|
||||||
rec.stateRootOrStatus.isHash == true
|
|
||||||
|
|
||||||
proc stateRoot*(rec: LegacyReceipt): Hash256 {.inline.} =
|
|
||||||
doAssert(rec.hasStateRoot)
|
doAssert(rec.hasStateRoot)
|
||||||
rec.stateRootOrStatus.hash
|
rec.hash
|
||||||
|
|
||||||
proc status*(rec: LegacyReceipt): int {.inline.} =
|
template contractCreation*(tx: Transaction): bool =
|
||||||
doAssert(rec.hasStatus)
|
tx.to.isNone
|
||||||
if rec.stateRootOrStatus.status: 1 else: 0
|
|
||||||
|
func destination*(tx: Transaction): EthAddress =
|
||||||
|
# use getRecipient if you also want to get
|
||||||
|
# the contract address
|
||||||
|
if tx.to.isSome:
|
||||||
|
return tx.to.get
|
||||||
|
|
||||||
#
|
#
|
||||||
# Rlp serialization:
|
# Rlp serialization:
|
||||||
|
@ -329,52 +316,132 @@ 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
|
||||||
|
|
||||||
type
|
proc append*[T](w: var RlpWriter, val: Option[T]) =
|
||||||
TxTypes* = LegacyTx | AccessListTx
|
if val.isSome:
|
||||||
|
w.append(val.get())
|
||||||
|
else:
|
||||||
|
w.append("")
|
||||||
|
|
||||||
proc read*(rlp: var Rlp, t: var TxTypes, _: type EthAddress): EthAddress {.inline.} =
|
proc appendTxLegacy(w: var RlpWriter, tx: Transaction) =
|
||||||
|
w.startList(9)
|
||||||
|
w.append(tx.nonce)
|
||||||
|
w.append(tx.gasPrice)
|
||||||
|
w.append(tx.gasLimit)
|
||||||
|
w.append(tx.to)
|
||||||
|
w.append(tx.value)
|
||||||
|
w.append(tx.payload)
|
||||||
|
w.append(tx.V)
|
||||||
|
w.append(tx.R)
|
||||||
|
w.append(tx.S)
|
||||||
|
|
||||||
|
proc appendTxEip2930(w: var RlpWriter, tx: Transaction) =
|
||||||
|
w.append(1)
|
||||||
|
w.startList(11)
|
||||||
|
w.append(tx.chainId.uint64)
|
||||||
|
w.append(tx.nonce)
|
||||||
|
w.append(tx.gasPrice)
|
||||||
|
w.append(tx.gasLimit)
|
||||||
|
w.append(tx.to)
|
||||||
|
w.append(tx.value)
|
||||||
|
w.append(tx.payload)
|
||||||
|
w.append(tx.accessList)
|
||||||
|
w.append(tx.V)
|
||||||
|
w.append(tx.R)
|
||||||
|
w.append(tx.S)
|
||||||
|
|
||||||
|
proc appendTxEip1559(w: var RlpWriter, tx: Transaction) =
|
||||||
|
w.append(2)
|
||||||
|
w.startList(12)
|
||||||
|
w.append(tx.chainId.uint64)
|
||||||
|
w.append(tx.nonce)
|
||||||
|
w.append(tx.maxPriorityFee)
|
||||||
|
w.append(tx.maxFee)
|
||||||
|
w.append(tx.gasLimit)
|
||||||
|
w.append(tx.to)
|
||||||
|
w.append(tx.value)
|
||||||
|
w.append(tx.payload)
|
||||||
|
w.append(tx.accessList)
|
||||||
|
w.append(tx.V)
|
||||||
|
w.append(tx.R)
|
||||||
|
w.append(tx.S)
|
||||||
|
|
||||||
|
proc append*(w: var RlpWriter, tx: Transaction) =
|
||||||
|
case tx.txType
|
||||||
|
of TxLegacy:
|
||||||
|
w.appendTxLegacy(tx)
|
||||||
|
of TxEip2930:
|
||||||
|
w.appendTxEip2930(tx)
|
||||||
|
of TxEip1559:
|
||||||
|
w.appendTxEip1559(tx)
|
||||||
|
|
||||||
|
template read[T](rlp: var Rlp, val: var T)=
|
||||||
|
val = rlp.read(type val)
|
||||||
|
|
||||||
|
proc read[T](rlp: var Rlp, val: var Option[T])=
|
||||||
if rlp.blobLen != 0:
|
if rlp.blobLen != 0:
|
||||||
result = rlp.read(EthAddress)
|
val = some(rlp.read(T))
|
||||||
else:
|
else:
|
||||||
t.isContractCreation = true
|
rlp.skipElem
|
||||||
rlp.skipElem()
|
|
||||||
|
|
||||||
proc append*(rlpWriter: var RlpWriter, t: TxTypes, a: EthAddress) {.inline.} =
|
proc readTxLegacy(rlp: var Rlp, tx: var Transaction)=
|
||||||
if t.isContractCreation:
|
tx.txType = TxLegacy
|
||||||
rlpWriter.append("")
|
rlp.tryEnterList()
|
||||||
else:
|
rlp.read(tx.nonce)
|
||||||
rlpWriter.append(a)
|
rlp.read(tx.gasPrice)
|
||||||
|
rlp.read(tx.gasLimit)
|
||||||
|
rlp.read(tx.to)
|
||||||
|
rlp.read(tx.value)
|
||||||
|
rlp.read(tx.payload)
|
||||||
|
rlp.read(tx.V)
|
||||||
|
rlp.read(tx.R)
|
||||||
|
rlp.read(tx.S)
|
||||||
|
|
||||||
proc read*(rlp: var Rlp, t: var AccessListTx, _: type ChainId): ChainId {.inline.} =
|
proc readTxEip2930(rlp: var Rlp, tx: var Transaction)=
|
||||||
rlp.read(uint64).ChainId
|
tx.txType = TxEip2930
|
||||||
|
rlp.tryEnterList()
|
||||||
|
tx.chainId = rlp.read(uint64).ChainId
|
||||||
|
rlp.read(tx.nonce)
|
||||||
|
rlp.read(tx.gasPrice)
|
||||||
|
rlp.read(tx.gasLimit)
|
||||||
|
rlp.read(tx.to)
|
||||||
|
rlp.read(tx.value)
|
||||||
|
rlp.read(tx.payload)
|
||||||
|
rlp.read(tx.accessList)
|
||||||
|
rlp.read(tx.V)
|
||||||
|
rlp.read(tx.R)
|
||||||
|
rlp.read(tx.S)
|
||||||
|
|
||||||
proc append*(rlpWriter: var RlpWriter, t: AccessListTx, a: ChainId) {.inline.} =
|
proc readTxEip1559(rlp: var Rlp, tx: var Transaction)=
|
||||||
rlpWriter.append(a.uint64)
|
tx.txType = TxEip1559
|
||||||
|
rlp.tryEnterList()
|
||||||
proc append*(rlpWriter: var RlpWriter, tx: Transaction) =
|
tx.chainId = rlp.read(uint64).ChainId
|
||||||
if tx.txType == LegacyTxType:
|
rlp.read(tx.nonce)
|
||||||
rlpWriter.append(tx.legacyTx)
|
rlp.read(tx.maxPriorityFee)
|
||||||
else:
|
rlp.read(tx.maxFee)
|
||||||
# EIP 2718/2930
|
rlp.read(tx.gasLimit)
|
||||||
rlpWriter.append(1)
|
rlp.read(tx.to)
|
||||||
rlpWriter.append(tx.accessListTx)
|
rlp.read(tx.value)
|
||||||
|
rlp.read(tx.payload)
|
||||||
|
rlp.read(tx.accessList)
|
||||||
|
rlp.read(tx.V)
|
||||||
|
rlp.read(tx.R)
|
||||||
|
rlp.read(tx.S)
|
||||||
|
|
||||||
proc read*(rlp: var Rlp, T: type Transaction): T =
|
proc read*(rlp: var Rlp, T: type Transaction): T =
|
||||||
if rlp.isList:
|
if rlp.isList:
|
||||||
return Transaction(
|
rlp.readTxLegacy(result)
|
||||||
txType: LegacyTxType,
|
return
|
||||||
legacyTx: rlp.read(LegacyTx)
|
|
||||||
)
|
|
||||||
|
|
||||||
# EIP 2718/2930
|
# EIP 2718
|
||||||
let txType = rlp.read(int)
|
let txType = rlp.read(int)
|
||||||
if txType != 1:
|
if txType notin {1, 2}:
|
||||||
raise newException(UnsupportedRlpError,
|
raise newException(UnsupportedRlpError,
|
||||||
"TxType expect 1 got " & $txType)
|
"TxType expect 1 or 2 got " & $txType)
|
||||||
return Transaction(
|
|
||||||
txType: AccessListTxType,
|
if TxType(txType) == TxEip2930:
|
||||||
accessListTx: rlp.read(AccessListTx)
|
rlp.readTxEip2930(result)
|
||||||
)
|
else:
|
||||||
|
rlp.readTxEip1559(result)
|
||||||
|
|
||||||
proc read*(rlp: var Rlp, t: var (EthBlock | BlockBody), _: type seq[Transaction]): seq[Transaction] {.inline.} =
|
proc read*(rlp: var Rlp, t: var (EthBlock | BlockBody), _: type seq[Transaction]): seq[Transaction] {.inline.} =
|
||||||
# EIP 2718/2930: we have to override this field
|
# EIP 2718/2930: we have to override this field
|
||||||
|
@ -395,21 +462,11 @@ proc append*(rlpWriter: var RlpWriter, blk: EthBlock | BlockBody, txs: seq[Trans
|
||||||
# not rlp(txType, txPayload) -> two list elem, wrong!
|
# not rlp(txType, txPayload) -> two list elem, wrong!
|
||||||
rlpWriter.startList(txs.len)
|
rlpWriter.startList(txs.len)
|
||||||
for tx in txs:
|
for tx in txs:
|
||||||
if tx.txType == LegacyTxType:
|
if tx.txType == TxLegacy:
|
||||||
rlpWriter.append(tx)
|
rlpWriter.append(tx)
|
||||||
else:
|
else:
|
||||||
rlpWriter.append(rlp.encode(tx))
|
rlpWriter.append(rlp.encode(tx))
|
||||||
|
|
||||||
proc read*(rlp: var Rlp, T: type HashOrStatus): T {.inline.} =
|
|
||||||
if rlp.isBlob() and (rlp.blobLen() == 32 or rlp.blobLen() == 1):
|
|
||||||
if rlp.blobLen == 1:
|
|
||||||
result = hashOrStatus(rlp.read(uint8) == 1)
|
|
||||||
else:
|
|
||||||
result = hashOrStatus(rlp.read(Hash256))
|
|
||||||
else:
|
|
||||||
raise newException(RlpTypeMismatch,
|
|
||||||
"HashOrStatus expected, but the source RLP is not a blob of right size.")
|
|
||||||
|
|
||||||
func init*(T: type BlockHashOrNumber, str: string): T
|
func init*(T: type BlockHashOrNumber, str: string): T
|
||||||
{.raises: [ValueError, Defect].} =
|
{.raises: [ValueError, Defect].} =
|
||||||
if str.startsWith "0x":
|
if str.startsWith "0x":
|
||||||
|
@ -428,36 +485,45 @@ func `$`*(x: BlockHashOrNumber): string =
|
||||||
else:
|
else:
|
||||||
$x.number
|
$x.number
|
||||||
|
|
||||||
proc append*(rlpWriter: var RlpWriter, value: HashOrStatus) {.inline.} =
|
proc append*(w: var RlpWriter, rec: Receipt) =
|
||||||
if value.isHash:
|
if rec.receiptType in {Eip2930Receipt, Eip1559Receipt}:
|
||||||
rlpWriter.append(value.hash)
|
w.append(rec.receiptType.int)
|
||||||
else:
|
|
||||||
rlpWriter.append(if value.status: 1'u8 else: 0'u8)
|
|
||||||
|
|
||||||
proc append*(rlpWriter: var RlpWriter, rec: Receipt) =
|
w.startList(4)
|
||||||
if rec.receiptType == LegacyReceiptType:
|
if rec.isHash:
|
||||||
rlpWriter.append(rec.legacyReceipt)
|
w.append(rec.hash)
|
||||||
else:
|
else:
|
||||||
# EIP 2718/2930
|
w.append(rec.status.uint8)
|
||||||
rlpWriter.append(1)
|
|
||||||
rlpWriter.append(rec.accessListReceipt)
|
w.append(rec.cumulativeGasUsed)
|
||||||
|
w.append(rec.bloom)
|
||||||
|
w.append(rec.logs)
|
||||||
|
|
||||||
proc read*(rlp: var Rlp, T: type Receipt): T =
|
proc read*(rlp: var Rlp, T: type Receipt): T =
|
||||||
if rlp.isList:
|
if rlp.isList:
|
||||||
return Receipt(
|
result.receiptType = LegacyReceipt
|
||||||
receiptType: LegacyReceiptType,
|
else:
|
||||||
legacyReceipt: rlp.read(LegacyReceipt)
|
# EIP 2718
|
||||||
)
|
|
||||||
|
|
||||||
# EIP 2718/2930
|
|
||||||
let recType = rlp.read(int)
|
let recType = rlp.read(int)
|
||||||
if recType != 1:
|
if recType notin {1, 2}:
|
||||||
raise newException(UnsupportedRlpError,
|
raise newException(UnsupportedRlpError,
|
||||||
"TxType expect 1 got " & $recType)
|
"TxType expect 1 or 2 got " & $recType)
|
||||||
return Receipt(
|
result.receiptType = ReceiptType(recType)
|
||||||
receiptType: AccessListReceiptType,
|
|
||||||
accessListReceipt: rlp.read(AccessListReceipt)
|
rlp.tryEnterList()
|
||||||
)
|
if rlp.isBlob and rlp.blobLen in {0, 1}:
|
||||||
|
result.isHash = false
|
||||||
|
result.status = rlp.read(uint8) == 1
|
||||||
|
elif rlp.isBlob and rlp.blobLen == 32:
|
||||||
|
result.isHash = true
|
||||||
|
result.hash = rlp.read(Hash256)
|
||||||
|
else:
|
||||||
|
raise newException(RlpTypeMismatch,
|
||||||
|
"HashOrStatus expected, but the source RLP is not a blob of right size.")
|
||||||
|
|
||||||
|
rlp.read(result.cumulativeGasUsed)
|
||||||
|
rlp.read(result.bloom)
|
||||||
|
rlp.read(result.logs)
|
||||||
|
|
||||||
proc read*(rlp: var Rlp, T: type Time): T {.inline.} =
|
proc read*(rlp: var Rlp, T: type Time): T {.inline.} =
|
||||||
result = fromUnix(rlp.read(int64))
|
result = fromUnix(rlp.read(int64))
|
||||||
|
@ -478,6 +544,30 @@ proc read*(rlp: var Rlp, T: type HashOrNum): T =
|
||||||
proc append*(rlpWriter: var RlpWriter, t: Time) {.inline.} =
|
proc append*(rlpWriter: var RlpWriter, t: Time) {.inline.} =
|
||||||
rlpWriter.append(t.toUnix())
|
rlpWriter.append(t.toUnix())
|
||||||
|
|
||||||
|
proc append*(w: var RlpWriter, h: BlockHeader) =
|
||||||
|
w.startList(if h.fee.isSome: 16 else: 15)
|
||||||
|
for k, v in fieldPairs(h):
|
||||||
|
when k != "fee":
|
||||||
|
w.append(v)
|
||||||
|
if h.fee.isSome:
|
||||||
|
w.append(h.fee.get())
|
||||||
|
|
||||||
|
proc read*(rlp: var Rlp, T: type BlockHeader): T =
|
||||||
|
let len = rlp.listLen
|
||||||
|
|
||||||
|
if len notin {15, 16}:
|
||||||
|
raise newException(UnsupportedRlpError,
|
||||||
|
"BlockHeader elems should be 15 or 16 got " & $len)
|
||||||
|
|
||||||
|
rlp.tryEnterList()
|
||||||
|
for k, v in fieldPairs(result):
|
||||||
|
when k != "fee":
|
||||||
|
v = rlp.read(type v)
|
||||||
|
|
||||||
|
if len == 16:
|
||||||
|
# EIP-1559
|
||||||
|
result.baseFee = rlp.read(UInt256)
|
||||||
|
|
||||||
proc rlpHash*[T](v: T): Hash256 =
|
proc rlpHash*[T](v: T): Hash256 =
|
||||||
keccak256.digest(rlp.encode(v))
|
keccak256.digest(rlp.encode(v))
|
||||||
|
|
||||||
|
|
|
@ -2,118 +2,76 @@ import
|
||||||
nimcrypto/keccak,
|
nimcrypto/keccak,
|
||||||
".."/[common, rlp, keys]
|
".."/[common, rlp, keys]
|
||||||
|
|
||||||
proc initLegacyTx*(nonce: AccountNonce, gasPrice, gasLimit: GasInt, to: EthAddress,
|
|
||||||
value: UInt256, payload: Blob, V: int64, R, S: UInt256, isContractCreation = false): LegacyTx =
|
|
||||||
result.nonce = nonce
|
|
||||||
result.gasPrice = gasPrice
|
|
||||||
result.gasLimit = gasLimit
|
|
||||||
result.to = to
|
|
||||||
result.value = value
|
|
||||||
result.payload = payload
|
|
||||||
result.V = V
|
|
||||||
result.R = R
|
|
||||||
result.S = S
|
|
||||||
result.isContractCreation = isContractCreation
|
|
||||||
|
|
||||||
type
|
|
||||||
LegacyUnsignedTx* = object
|
|
||||||
nonce*: AccountNonce
|
|
||||||
gasPrice*: GasInt
|
|
||||||
gasLimit*: GasInt
|
|
||||||
to* {.rlpCustomSerialization.}: EthAddress
|
|
||||||
value*: UInt256
|
|
||||||
payload*: Blob
|
|
||||||
isContractCreation* {.rlpIgnore.}: bool
|
|
||||||
|
|
||||||
AccessListUnsignedTx* = object
|
|
||||||
chainId* {.rlpCustomSerialization.}: ChainId
|
|
||||||
nonce* : AccountNonce
|
|
||||||
gasPrice* : GasInt
|
|
||||||
gasLimit* : GasInt
|
|
||||||
to* {.rlpCustomSerialization.}: EthAddress
|
|
||||||
value* : UInt256
|
|
||||||
payload* : Blob
|
|
||||||
accessList*: AccessList
|
|
||||||
isContractCreation* {.rlpIgnore.}: bool
|
|
||||||
|
|
||||||
UnsignedTxTypes* = LegacyUnsignedTx | AccessListUnsignedTx
|
|
||||||
|
|
||||||
proc read*(rlp: var Rlp, t: var UnsignedTxTypes, _: type EthAddress): EthAddress {.inline.} =
|
|
||||||
if rlp.blobLen != 0:
|
|
||||||
result = rlp.read(EthAddress)
|
|
||||||
else:
|
|
||||||
t.isContractCreation = true
|
|
||||||
|
|
||||||
proc append*(rlpWriter: var RlpWriter, t: UnsignedTxTypes, a: EthAddress) {.inline.} =
|
|
||||||
if t.isContractCreation:
|
|
||||||
rlpWriter.append("")
|
|
||||||
else:
|
|
||||||
rlpWriter.append(a)
|
|
||||||
|
|
||||||
proc read*(rlp: var Rlp, t: var AccessListUnsignedTx, _: type ChainId): ChainId {.inline.} =
|
|
||||||
rlp.read(uint64).ChainId
|
|
||||||
|
|
||||||
proc append*(rlpWriter: var RlpWriter, t: AccessListUnsignedTx, a: ChainId) {.inline.} =
|
|
||||||
rlpWriter.append(a.uint64)
|
|
||||||
|
|
||||||
const
|
const
|
||||||
EIP155_CHAIN_ID_OFFSET* = 35'i64
|
EIP155_CHAIN_ID_OFFSET* = 35'i64
|
||||||
|
|
||||||
func rlpEncode*(tx: LegacyTx): auto =
|
func rlpEncodeLegacy(tx: Transaction): auto =
|
||||||
# Encode transaction without signature
|
var w = initRlpWriter()
|
||||||
return rlp.encode(LegacyUnsignedTx(
|
w.startList(6)
|
||||||
nonce: tx.nonce,
|
w.append(tx.nonce)
|
||||||
gasPrice: tx.gasPrice,
|
w.append(tx.gasPrice)
|
||||||
gasLimit: tx.gasLimit,
|
w.append(tx.gasLimit)
|
||||||
to: tx.to,
|
w.append(tx.to)
|
||||||
value: tx.value,
|
w.append(tx.value)
|
||||||
payload: tx.payload,
|
w.append(tx.payload)
|
||||||
isContractCreation: tx.isContractCreation
|
w.finish()
|
||||||
))
|
|
||||||
|
|
||||||
func rlpEncodeEIP155*(tx: LegacyTx): auto =
|
func rlpEncodeEip155(tx: Transaction): auto =
|
||||||
let V = (tx.V - EIP155_CHAIN_ID_OFFSET) div 2
|
let chainId = (tx.V - EIP155_CHAIN_ID_OFFSET) div 2
|
||||||
# Encode transaction without signature
|
var w = initRlpWriter()
|
||||||
return rlp.encode(LegacyTx(
|
w.startList(9)
|
||||||
nonce: tx.nonce,
|
w.append(tx.nonce)
|
||||||
gasPrice: tx.gasPrice,
|
w.append(tx.gasPrice)
|
||||||
gasLimit: tx.gasLimit,
|
w.append(tx.gasLimit)
|
||||||
to: tx.to,
|
w.append(tx.to)
|
||||||
value: tx.value,
|
w.append(tx.value)
|
||||||
payload: tx.payload,
|
w.append(tx.payload)
|
||||||
isContractCreation: tx.isContractCreation,
|
w.append(chainId)
|
||||||
V: V,
|
w.append(0)
|
||||||
R: 0.u256,
|
w.append(0)
|
||||||
S: 0.u256
|
w.finish()
|
||||||
))
|
|
||||||
|
|
||||||
func rlpEncode*(tx: AccessListTx): auto =
|
func rlpEncodeEip2930(tx: Transaction): auto =
|
||||||
# EIP 2718/2930
|
var w = initRlpWriter()
|
||||||
let unsignedTx = AccessListUnsignedTx(
|
w.append(1)
|
||||||
chainId: tx.chainId,
|
w.startList(8)
|
||||||
nonce: tx.nonce,
|
w.append(tx.chainId.uint64)
|
||||||
gasPrice: tx.gasPrice,
|
w.append(tx.nonce)
|
||||||
gasLimit: tx.gasLimit,
|
w.append(tx.gasPrice)
|
||||||
to: tx.to,
|
w.append(tx.gasLimit)
|
||||||
value: tx.value,
|
w.append(tx.to)
|
||||||
payload: tx.payload,
|
w.append(tx.value)
|
||||||
accessList: tx.accessList,
|
w.append(tx.payload)
|
||||||
isContractCreation: tx.isContractCreation
|
w.append(tx.accessList)
|
||||||
)
|
w.finish()
|
||||||
var rw = initRlpWriter()
|
|
||||||
rw.append(1)
|
|
||||||
rw.append(unsignedTx)
|
|
||||||
rw.finish()
|
|
||||||
|
|
||||||
func txHashNoSignature*(tx: LegacyTx): Hash256 =
|
func rlpEncodeEip1559(tx: Transaction): auto =
|
||||||
# Hash transaction without signature
|
var w = initRlpWriter()
|
||||||
keccak256.digest(if tx.V >= EIP155_CHAIN_ID_OFFSET: tx.rlpEncodeEIP155 else: tx.rlpEncode)
|
w.append(2)
|
||||||
|
w.startList(9)
|
||||||
|
w.append(tx.chainId.uint64)
|
||||||
|
w.append(tx.nonce)
|
||||||
|
w.append(tx.maxPriorityFee)
|
||||||
|
w.append(tx.maxFee)
|
||||||
|
w.append(tx.gasLimit)
|
||||||
|
w.append(tx.to)
|
||||||
|
w.append(tx.value)
|
||||||
|
w.append(tx.payload)
|
||||||
|
w.append(tx.accessList)
|
||||||
|
w.finish()
|
||||||
|
|
||||||
func txHashNoSignature*(tx: AccessListTx): Hash256 =
|
func rlpEncode*(tx: Transaction): auto =
|
||||||
keccak256.digest(tx.rlpEncode)
|
case tx.txType
|
||||||
|
of TxLegacy:
|
||||||
|
if tx.V >= EIP155_CHAIN_ID_OFFSET:
|
||||||
|
tx.rlpEncodeEIP155
|
||||||
|
else:
|
||||||
|
tx.rlpEncodeLegacy
|
||||||
|
of TxEip2930:
|
||||||
|
tx.rlpEncodeEip2930
|
||||||
|
of TxEip1559:
|
||||||
|
tx.rlpEncodeEip1559
|
||||||
|
|
||||||
func txHashNoSignature*(tx: Transaction): Hash256 =
|
func txHashNoSignature*(tx: Transaction): Hash256 =
|
||||||
if tx.txType == LegacyTxType:
|
# Hash transaction without signature
|
||||||
txHashNoSignature(tx.legacyTx)
|
keccak256.digest(rlpEncode(tx))
|
||||||
else:
|
|
||||||
txHashNoSignature(tx.accessListTx)
|
|
||||||
|
|
|
@ -9,33 +9,9 @@ type
|
||||||
EthHeader = object
|
EthHeader = object
|
||||||
header: BlockHeader
|
header: BlockHeader
|
||||||
|
|
||||||
proc `==`(a, b: HashOrStatus): bool =
|
|
||||||
result = a.isHash == b.isHash
|
|
||||||
if not result: return
|
|
||||||
if a.isHash:
|
|
||||||
result = result and a.hash == b.hash
|
|
||||||
else:
|
|
||||||
result = result and a.status == b.status
|
|
||||||
|
|
||||||
func `==`(a, b: ChainId): bool =
|
func `==`(a, b: ChainId): bool =
|
||||||
a.uint64 == b.uint64
|
a.uint64 == b.uint64
|
||||||
|
|
||||||
func `==`(a, b: Transaction): bool =
|
|
||||||
if a.txType != b.txType:
|
|
||||||
return false
|
|
||||||
if a.txType == LegacyTxType:
|
|
||||||
return a.legacyTx == b.legacyTx
|
|
||||||
else:
|
|
||||||
return a.accessListTx == b.accessListTx
|
|
||||||
|
|
||||||
func `==`(a, b: Receipt): bool =
|
|
||||||
if a.receiptType != b.receiptType:
|
|
||||||
return false
|
|
||||||
if a.receiptType == LegacyReceiptType:
|
|
||||||
return a.legacyReceipt == b.legacyReceipt
|
|
||||||
else:
|
|
||||||
return a.accessListReceipt == b.accessListReceipt
|
|
||||||
|
|
||||||
proc loadFile(x: int) =
|
proc loadFile(x: int) =
|
||||||
let fileName = "tests" / "rlp" / "eip2718" / "acl_block_" & $x & ".json"
|
let fileName = "tests" / "rlp" / "eip2718" / "acl_block_" & $x & ".json"
|
||||||
test fileName:
|
test fileName:
|
||||||
|
@ -62,21 +38,19 @@ proc suite1() =
|
||||||
suite "rlp encoding":
|
suite "rlp encoding":
|
||||||
test "receipt roundtrip":
|
test "receipt roundtrip":
|
||||||
let a = Receipt(
|
let a = Receipt(
|
||||||
receiptType: LegacyReceiptType,
|
receiptType: LegacyReceipt,
|
||||||
legacyReceipt: LegacyReceipt(
|
isHash: false,
|
||||||
stateRootOrStatus: hashOrStatus(true),
|
status: false,
|
||||||
cumulativeGasUsed: 51000
|
cumulativeGasUsed: 51000
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
let hash = rlpHash(a)
|
let hash = rlpHash(a)
|
||||||
let b = Receipt(
|
let b = Receipt(
|
||||||
receiptType: LegacyReceiptType,
|
receiptType: LegacyReceipt,
|
||||||
legacyReceipt: LegacyReceipt(
|
isHash: true,
|
||||||
stateRootOrStatus: hashOrStatus(hash),
|
hash: hash,
|
||||||
cumulativeGasUsed: 21000
|
cumulativeGasUsed: 21000
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
let abytes = rlp.encode(a)
|
let abytes = rlp.encode(a)
|
||||||
let bbytes = rlp.encode(b)
|
let bbytes = rlp.encode(b)
|
||||||
|
@ -85,21 +59,17 @@ proc suite1() =
|
||||||
check aa == a
|
check aa == a
|
||||||
check bb == b
|
check bb == b
|
||||||
|
|
||||||
test "access list receipt":
|
test "EIP 2930 receipt":
|
||||||
let a = Receipt(
|
let a = Receipt(
|
||||||
receiptType: AccessListReceiptType,
|
receiptType: Eip2930Receipt,
|
||||||
accessListReceipt: AccessListReceipt(
|
|
||||||
status: true
|
status: true
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
let b = Receipt(
|
let b = Receipt(
|
||||||
receiptType: AccessListReceiptType,
|
receiptType: Eip2930Receipt,
|
||||||
accessListReceipt: AccessListReceipt(
|
|
||||||
status: false,
|
status: false,
|
||||||
cumulativeGasUsed: 21000
|
cumulativeGasUsed: 21000
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
let abytes = rlp.encode(a)
|
let abytes = rlp.encode(a)
|
||||||
let bbytes = rlp.encode(b)
|
let bbytes = rlp.encode(b)
|
||||||
|
@ -113,5 +83,11 @@ proc suite2() =
|
||||||
for i in 0..<10:
|
for i in 0..<10:
|
||||||
loadFile(i)
|
loadFile(i)
|
||||||
|
|
||||||
|
test "rlp roundtrip EIP1559":
|
||||||
|
var h: BlockHeader
|
||||||
|
let xy = rlp.encode(h)
|
||||||
|
let hh = rlp.decode(xy, BlockHeader)
|
||||||
|
check h == hh
|
||||||
|
|
||||||
suite1()
|
suite1()
|
||||||
suite2()
|
suite2()
|
Loading…
Reference in New Issue