Implement EIP-7702 Transaction and Receipt (#731)

* Implement EIP-7702 Transaction and Receipt
This commit is contained in:
andri lim 2024-09-13 13:37:43 +07:00 committed by GitHub
parent 59715353db
commit 4eecab27ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 228 additions and 4 deletions

View File

@ -80,12 +80,21 @@ type
TxEip2930 # 1
TxEip1559 # 2
TxEip4844 # 3
TxEip7702 # 4
NetworkPayload* = ref object
blobs* : seq[NetworkBlob]
commitments* : seq[KzgCommitment]
proofs* : seq[KzgProof]
Authorization* = object
chainId*: ChainId
address*: EthAddress
nonce*: AccountNonce
yParity*: uint64
R*: UInt256
S*: UInt256
Transaction* = object
txType* : TxType # EIP-2718
chainId* : ChainId # EIP-2930
@ -100,6 +109,7 @@ type
accessList* : AccessList # EIP-2930
maxFeePerBlobGas*: UInt256 # EIP-4844
versionedHashes*: VersionedHashes # EIP-4844
authorizationList*: seq[Authorization]# EIP-7702
V* : uint64
R*, S* : UInt256
@ -198,6 +208,7 @@ type
# Eip2930Receipt = TxEip2930
# Eip1559Receipt = TxEip1559
# Eip4844Receipt = TxEip4844
# Eip7702Receipt = TxEip7702
Receipt* = object
receiptType* : ReceiptType
@ -244,6 +255,7 @@ const
Eip2930Receipt* = TxEip2930
Eip1559Receipt* = TxEip1559
Eip4844Receipt* = TxEip4844
Eip7702Receipt* = TxEip7702
EMPTY_ROOT_HASH* = "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".toDigest
EMPTY_UNCLE_HASH* = "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347".toDigest

View File

@ -128,6 +128,31 @@ proc appendTxEip4844(w: var RlpWriter, tx: Transaction) =
w.append(tx.R)
w.append(tx.S)
proc append*(w: var RlpWriter, x: Authorization) =
w.startList(6)
w.append(x.chainId.uint64)
w.append(x.address)
w.append(x.nonce)
w.append(x.yParity)
w.append(x.R)
w.append(x.S)
proc appendTxEip7702(w: var RlpWriter, tx: Transaction) =
w.startList(13)
w.append(tx.chainId.uint64)
w.append(tx.nonce)
w.append(tx.maxPriorityFeePerGas)
w.append(tx.maxFeePerGas)
w.append(tx.gasLimit)
w.append(tx.to)
w.append(tx.value)
w.append(tx.payload)
w.append(tx.accessList)
w.append(tx.authorizationList)
w.append(tx.V)
w.append(tx.R)
w.append(tx.S)
proc appendTxPayload(w: var RlpWriter, tx: Transaction) =
case tx.txType
of TxLegacy:
@ -138,6 +163,8 @@ proc appendTxPayload(w: var RlpWriter, tx: Transaction) =
w.appendTxEip1559(tx)
of TxEip4844:
w.appendTxEip4844(tx)
of TxEip7702:
w.appendTxEip7702(tx)
proc append*(w: var RlpWriter, tx: Transaction) =
if tx.txType != TxLegacy:
@ -229,6 +256,32 @@ proc readTxEip4844(rlp: var Rlp, tx: var Transaction) =
rlp.read(tx.R)
rlp.read(tx.S)
proc read*(rlp: var Rlp, T: type Authorization): T =
rlp.tryEnterList()
result.chainId = rlp.read(uint64).ChainId
rlp.read(result.address)
rlp.read(result.nonce)
rlp.read(result.yParity)
rlp.read(result.R)
rlp.read(result.S)
proc readTxEip7702(rlp: var Rlp, tx: var Transaction) =
tx.txType = TxEip7702
rlp.tryEnterList()
tx.chainId = rlp.read(uint64).ChainId
rlp.read(tx.nonce)
rlp.read(tx.maxPriorityFeePerGas)
rlp.read(tx.maxFeePerGas)
rlp.read(tx.gasLimit)
rlp.read(tx.to)
rlp.read(tx.value)
rlp.read(tx.payload)
rlp.read(tx.accessList)
rlp.read(tx.authorizationList)
rlp.read(tx.V)
rlp.read(tx.R)
rlp.read(tx.S)
proc readTxType(rlp: var Rlp): TxType =
if rlp.isList:
raise newException(RlpTypeMismatch,
@ -271,6 +324,8 @@ proc readTxPayload(rlp: var Rlp, tx: var Transaction, txType: TxType) =
rlp.readTxEip1559(tx)
of TxEip4844:
rlp.readTxEip4844(tx)
of TxEip7702:
rlp.readTxEip7702(tx)
proc readTxTyped(rlp: var Rlp, tx: var Transaction) =
let txType = rlp.readTxType()
@ -377,7 +432,7 @@ proc append*(
rlpWriter.append(rlp.encode(tx))
proc append*(w: var RlpWriter, rec: Receipt) =
if rec.receiptType in {Eip2930Receipt, Eip1559Receipt, Eip4844Receipt}:
if rec.receiptType in {Eip2930Receipt, Eip1559Receipt, Eip4844Receipt, Eip7702Receipt}:
w.append(rec.receiptType.uint)
w.startList(4)
@ -420,7 +475,7 @@ proc readReceiptTyped(rlp: var Rlp, receipt: var Receipt) =
var txVal: ReceiptType
if checkedEnumAssign(txVal, recType):
case txVal:
of Eip2930Receipt, Eip1559Receipt, Eip4844Receipt:
of Eip2930Receipt, Eip1559Receipt, Eip4844Receipt, Eip7702Receipt:
receipt.receiptType = txVal
of LegacyReceipt:
# The legacy type should not be used here.

View File

@ -78,6 +78,22 @@ func rlpEncodeEip4844(tx: Transaction): auto =
w.append(tx.versionedHashes)
w.finish()
func rlpEncodeEip7702(tx: Transaction): auto =
var w = initRlpWriter()
w.append(TxEip7702)
w.startList(10)
w.append(tx.chainId.uint64)
w.append(tx.nonce)
w.append(tx.maxPriorityFeePerGas)
w.append(tx.maxFeePerGas)
w.append(tx.gasLimit)
w.append(tx.to)
w.append(tx.value)
w.append(tx.payload)
w.append(tx.accessList)
w.append(tx.authorizationList)
w.finish()
func rlpEncode*(tx: Transaction): auto =
case tx.txType
of TxLegacy:
@ -91,6 +107,8 @@ func rlpEncode*(tx: Transaction): auto =
tx.rlpEncodeEip1559
of TxEip4844:
tx.rlpEncodeEip4844
of TxEip7702:
tx.rlpEncodeEip7702
func txHashNoSignature*(tx: Transaction): Hash256 =
# Hash transaction without signature

View File

@ -12,4 +12,5 @@ import
test_eth_types,
test_eth_types_rlp,
test_common,
test_eip4844
test_eip4844,
test_eip7702

View File

@ -145,7 +145,7 @@ proc suite2() =
let _ = rlp.decode(receiptBytes, Receipt)
test "Receipts EIP-2718 encoding - invalid - unsupported tx type":
let receiptBytes: seq[byte] = @[0x04]
let receiptBytes: seq[byte] = @[0x05]
expect UnsupportedRlpError:
let _ = rlp.decode(receiptBytes, Receipt)

View File

@ -0,0 +1,138 @@
# Nimbus
# Copyright (c) 2024 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
# http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or
# http://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed except
# according to those terms.
{.used.}
import
stew/byteutils,
results,
unittest2,
../../eth/common,
../../eth/rlp,
../../eth/common/transaction,
../../eth/keys
const
recipient = hexToByteArray[20]("095e7baea6a6c7c4c2dfeb977efac326af552d87")
zeroG1 = hexToByteArray[48]("0xc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
source = hexToByteArray[20]("0x0000000000000000000000000000000000000001")
storageKey= default(StorageKey)
accesses = @[AccessPair(address: source, storageKeys: @[storageKey])]
abcdef = hexToSeqByte("abcdef")
authList = @[Authorization(
chainID: 1.ChainId,
address: source,
nonce: 2.AccountNonce,
yParity: 3,
R: 4.u256,
S: 5.u256
)]
proc tx0(i: int): Transaction =
Transaction(
txType: TxEip7702,
chainId: 1.ChainId,
nonce: i.AccountNonce,
maxPriorityFeePerGas: 2.GasInt,
maxFeePerGas: 3.GasInt,
gasLimit: 4.GasInt,
to: Opt.some recipient,
value: 5.u256,
payload: abcdef,
accessList: accesses,
authorizationList: authList
)
func `==`(a, b: ChainId): bool =
a.uint64 == b.uint64
template roundTrip(txFunc: untyped, i: int) =
let tx = txFunc(i)
let bytes = rlp.encode(tx)
let tx2 = rlp.decode(bytes, Transaction)
let bytes2 = rlp.encode(tx2)
check bytes == bytes2
template read[T](rlp: var Rlp, val: var T) =
val = rlp.read(type val)
proc read[T](rlp: var Rlp, val: var Opt[T]) =
if rlp.blobLen != 0:
val = Opt.some(rlp.read(T))
else:
rlp.skipElem
proc readTx(rlp: var Rlp, tx: var Transaction) =
rlp.tryEnterList()
tx.chainId = rlp.read(uint64).ChainId
rlp.read(tx.nonce)
rlp.read(tx.maxPriorityFeePerGas)
rlp.read(tx.maxFeePerGas)
rlp.read(tx.gasLimit)
rlp.read(tx.to)
rlp.read(tx.value)
rlp.read(tx.payload)
rlp.read(tx.accessList)
rlp.read(tx.authorizationList)
proc decodeTxEip7702(bytes: openArray[byte]): Transaction =
var rlp = rlpFromBytes(bytes)
result.txType = TxType(rlp.getByteValue)
rlp.position += 1
readTx(rlp, result)
suite "Transaction EIP-7702 tests":
test "Tx RLP roundtrip":
roundTrip(tx0, 1)
test "Tx Sign":
const
keyHex = "63b508a03c3b5937ceb903af8b1b0c191012ef6eb7e9c3fb7afa94e5d214d376"
var
tx = tx0(2)
let
privateKey = PrivateKey.fromHex(keyHex).valueOr:
echo "ERROR: ", error
quit(QuitFailure)
rlpTx = rlpEncode(tx)
sig = sign(privateKey, rlpTx).toRaw
tx.V = sig[64].uint64
tx.R = UInt256.fromBytesBE(sig[0..31])
tx.S = UInt256.fromBytesBE(sig[32..63])
let
bytes = rlp.encode(tx)
decodedTx = rlp.decode(bytes, Transaction)
decodedNoSig = decodeTxEip7702(rlpTx)
var
expectedTx = tx0(2)
check expectedTx == decodedNoSig
expectedTx.V = tx.V
expectedTx.R = tx.R
expectedTx.S = tx.S
check expectedTx == tx
test "Receipt RLP roundtrip":
let rec = Receipt(
receiptType: Eip7702Receipt,
isHash: false,
status: false,
cumulativeGasUsed: 100.GasInt)
let bytes = rlp.encode(rec)
let zz = rlp.decode(bytes, Receipt)
let bytes2 = rlp.encode(zz)
check bytes2 == bytes