devnet-5: Move EIP-7702 Authorization validation to authority func (#2999)

* Move EIP-7702 Authorization validation to authority func

If the authorization is invalid the transaction itself is still valid,
the invalid authorization will be skipped.

* Fix copyright year
This commit is contained in:
andri lim 2025-01-15 09:18:25 +07:00 committed by GitHub
parent 288ee28077
commit 265d794583
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 93 additions and 13 deletions

View File

@ -1,5 +1,5 @@
# Nimbus
# Copyright (c) 2023-2024 Status Research & Development GmbH
# Copyright (c) 2023-2025 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)
@ -34,6 +34,7 @@ type
blobGasFee*: UInt256
blobCount* : int
blobID* : BlobID
authorizationList*: seq[Authorization]
BigInitcodeTx* = object of BaseTx
initcodeLength*: int
@ -75,6 +76,7 @@ type
data* : Opt[seq[byte]]
chainId* : Opt[ChainId]
signature* : Opt[CustSig]
auth* : Opt[Authorization]
const
TestAccountCount = 1000
@ -203,6 +205,21 @@ proc makeTxOfType(params: MakeTxParams, tc: BaseTx): PooledTransaction =
proofs: blobData.proofs.mapIt(KzgProof it.bytes),
)
)
of TxEip7702:
PooledTransaction(
tx: Transaction(
txType : TxEip7702,
nonce : params.nonce,
gasLimit: tc.gasLimit,
maxFeePerGas: gasFeeCap,
maxPriorityFeePerGas: gasTipCap,
to : tc.recipient,
value : tc.amount,
payload : tc.payload,
chainId : params.chainId,
authorizationList: tc.authorizationList,
)
)
else:
raiseAssert "unsupported tx type"
@ -433,7 +450,7 @@ proc customizeTransaction*(sender: TxSender,
if custTx.chainId.isSome:
modTx.chainId = custTx.chainId.get
if baseTx.txType in {TxEip1559, TxEip4844}:
if baseTx.txType in {TxEip1559, TxEip4844, TxEip7702}:
if custTx.gasPriceOrGasFeeCap.isSome:
modTx.maxFeePerGas = custTx.gasPriceOrGasFeeCap.get.GasInt
@ -449,6 +466,11 @@ proc customizeTransaction*(sender: TxSender,
doAssert(baseTx.txType == TxEip4844)
modTx.maxFeePerBlobGas = custTx.blobGas.get
if custTx.auth.isSome:
doAssert(baseTx.txType == TxEip7702)
doAssert(baseTx.authorizationList.len > 0)
modTx.authorizationList[0] = custTx.auth.get
if custTx.signature.isSome:
let signature = custTx.signature.get
modTx.V = signature.V
@ -458,3 +480,17 @@ proc customizeTransaction*(sender: TxSender,
modTx.signature = modTx.sign(acc.key, eip155 = true)
modTx
proc makeAuth*(sender: TxSender, acc: TestAccount, nonce: AccountNonce): Authorization =
var auth = Authorization(
chainId: sender.chainId,
address: acc.address,
nonce: nonce,
)
let hash = auth.rlpHashForSigning()
let sig = sign(acc.key, SkMessage(hash.data))
let raw = sig.toRaw()
auth.r = UInt256.fromBytesBE(raw.toOpenArray(0, 31))
auth.s = UInt256.fromBytesBE(raw.toOpenArray(32, 63))
auth.v = raw[64].uint64

View File

@ -1,5 +1,5 @@
# Nimbus
# Copyright (c) 2024 Status Research & Development GmbH
# Copyright (c) 2024-2025 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)
@ -12,6 +12,7 @@
import
../evm/code_bytes,
../constants,
results,
stew/assign2,
eth/common/eth_types,
@ -26,6 +27,16 @@ const
PER_EMPTY_ACCOUNT_COST* = 25000
func authority*(auth: Authorization): Opt[Address] =
const SECP256K1halfN = SECPK1_N div 2
if auth.v > 1'u64:
# auth.v must be 0 or 1
return Opt.none(Address)
if auth.s > SECP256K1halfN:
# auth.s must be <= SECP256K1N/2
return Opt.none(Address)
let sigHash = rlpHashForSigning(auth)
var bytes: array[65, byte]

View File

@ -1,5 +1,5 @@
# Nimbus
# Copyright (c) 2018-2024 Status Research & Development GmbH
# Copyright (c) 2018-2025 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)
@ -273,15 +273,6 @@ proc validateTxBasic*(
if tx.authorizationList.len == 0:
return err("invalid tx: authorization list must not empty")
const SECP256K1halfN = SECPK1_N div 2
for auth in tx.authorizationList:
if auth.v > 1'u64:
return err("invalid tx: auth.v must be 0 or 1")
if auth.s > SECP256K1halfN:
return err("invalid tx: auth.s must be <= SECP256K1N/2")
ok()
proc validateTransaction*(

View File

@ -572,5 +572,47 @@ proc txPoolMain*() =
inc count
check count == hs.len
test "EIP-7702 transaction before Prague":
let
acc = mx.getAccount(24)
auth = mx.makeAuth(acc, 0)
tc = BaseTx(
txType: Opt.some(TxEip7702),
gasLimit: 75000,
recipient: Opt.some(recipient214),
amount: amount,
authorizationList: @[auth],
)
tx = mx.makeTx(tc, 0)
xp.checkAddTx(tx, txErrorBasicValidation)
test "EIP-7702 transaction invalid auth signature":
let
env = initEnv(Prague)
xp = env.xp
mx = env.sender
acc = mx.getAccount(25)
auth = mx.makeAuth(acc, 0)
tc = BaseTx(
txType: Opt.some(TxEip7702),
gasLimit: 75000,
recipient: Opt.some(recipient214),
amount: amount,
authorizationList: @[auth],
)
ptx = mx.makeTx(tc, 0)
# invalid auth
var invauth = auth
invauth.v = 3.uint64
let
ctx = CustomTx(auth: Opt.some(invauth))
tx = mx.customizeTransaction(acc, ptx.tx, ctx)
xp.checkAddTx(tx)
# invalid auth, but the tx itself still valid
xp.checkImportBlock(1, 0)
when isMainModule:
txPoolMain()