Repositioning blob hash validation in newPayload of engine API (#2141)

This commit is contained in:
andri lim 2024-04-20 02:43:13 +07:00 committed by GitHub
parent 7d9e1d8607
commit 6694e240d7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 110 additions and 83 deletions

View File

@ -425,7 +425,7 @@ proc customizePayload*(cust: CustomPayloadData, data: ExecutableData): Executabl
if cust.versionedHashesCustomizer.isNil.not:
doAssert(data.versionedHashes.isSome)
result.versionedHashes = cust.versionedHashesCustomizer.getVersionedHashes(data.versionedHashes.get)
# Base new payload directive call cust.
# Used as base to other customizers.
type
@ -615,22 +615,22 @@ proc generateInvalidPayload*(sender: TxSender, data: ExecutableData, payloadFiel
excessBlobGas: some(modExcessBlobGas),
)
of InvalidVersionedHashesVersion:
doAssert(data.versionedHashes.isNone, "no versioned hashes available for modification")
doAssert(data.versionedHashes.isSome, "no versioned hashes available for modification")
customPayloadMod = CustomPayloadData(
versionedHashesCustomizer: IncreaseVersionVersionedHashes(),
)
of InvalidVersionedHashes:
doAssert(data.versionedHashes.isNone, "no versioned hashes available for modification")
doAssert(data.versionedHashes.isSome, "no versioned hashes available for modification")
customPayloadMod = CustomPayloadData(
versionedHashesCustomizer: CorruptVersionedHashes(),
)
of IncompleteVersionedHashes:
doAssert(data.versionedHashes.isNone, "no versioned hashes available for modification")
doAssert(data.versionedHashes.isSome, "no versioned hashes available for modification")
customPayloadMod = CustomPayloadData(
versionedHashesCustomizer: RemoveVersionedHash(),
)
of ExtraVersionedHashes:
doAssert(data.versionedHashes.isNone, "no versioned hashes available for modification")
doAssert(data.versionedHashes.isSome, "no versioned hashes available for modification")
customPayloadMod = CustomPayloadData(
versionedHashesCustomizer: ExtraVersionedHash(),
)

View File

@ -1,5 +1,5 @@
# Nimbus
# Copyright (c) 2023 Status Research & Development GmbH
# Copyright (c) 2023-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)

View File

@ -1865,7 +1865,7 @@ proc makeCancunTest(): seq[EngineSpec] =
mainFork: ForkCancun,
fieldModification: t,
)
#[
# Invalid Payload Tests
const
invalidFields = [
@ -1911,7 +1911,6 @@ proc makeCancunTest(): seq[EngineSpec] =
invalidDetectedOnSync: invalidDetectedOnSync,
nilLatestValidHash : nilLatestValidHash,
)
]#
# Invalid Transaction ChainID Tests
result.add InvalidTxChainIDTest(

View File

@ -22,11 +22,16 @@ import
type
BaseTx* = object of RootObj
recipient*: Option[EthAddress]
gasLimit* : GasInt
amount* : UInt256
payload* : seq[byte]
txType* : Option[TxType]
recipient* : Option[EthAddress]
gasLimit* : GasInt
amount* : UInt256
payload* : seq[byte]
txType* : Option[TxType]
gasTip* : GasInt
gasFee* : GasInt
blobGasFee*: UInt256
blobCount* : int
blobID* : BlobID
BigInitcodeTx* = object of BaseTx
initcodeLength*: int
@ -35,11 +40,6 @@ type
# Blob transaction creator
BlobTx* = object of BaseTx
gasFee* : GasInt
gasTip* : GasInt
blobGasFee*: UInt256
blobID* : BlobID
blobCount* : int
TestAccount* = object
key* : PrivateKey
@ -77,6 +77,7 @@ const
TestAccountCount = 1000
gasPrice* = 30.gwei
gasTipPrice* = 1.gwei
blobGasPrice* = 1.gwei
func toAddress(key: PrivateKey): EthAddress =
toKeyPair(key).pubkey.toCanonicalAddress()
@ -129,37 +130,75 @@ proc getTxType(tc: BaseTx, nonce: uint64): TxType =
else:
tc.txType.get
proc makeTx(params: MakeTxParams, tc: BaseTx): Transaction =
const
gasFeeCap = gasPrice
gasTipCap = gasTipPrice
proc makeTxOfType(params: MakeTxParams, tc: BaseTx): Transaction =
let
gasFeeCap = if tc.gasFee != 0.GasInt: tc.gasFee
else: gasPrice
gasTipCap = if tc.gasTip != 0.GasInt: tc.gasTip
else: gasTipPrice
let txType = tc.getTxType(params.nonce)
case txType
of TxLegacy:
Transaction(
txType : TxLegacy,
nonce : params.nonce,
to : tc.recipient,
value : tc.amount,
gasLimit: tc.gasLimit,
gasPrice: gasPrice,
payload : tc.payload
)
of TxEip1559:
Transaction(
txType : TxEIP1559,
nonce : params.nonce,
gasLimit: tc.gasLimit,
maxFee : gasFeeCap,
maxPriorityFee: gasTipCap,
to : tc.recipient,
value : tc.amount,
payload : tc.payload,
chainId : params.chainId
)
of TxEip4844:
doAssert(tc.recipient.isSome, "recipient must be some")
let
blobCount = if tc.blobCount != 0: tc.blobCount
else: MAX_BLOBS_PER_BLOCK
blobFeeCap = if tc.blobGasFee != 0.u256: tc.blobGasFee
else: blobGasPrice.u256
# Need tx wrap data that will pass blob verification
var blobData = blobDataGenerator(tc.blobID, blobCount)
#tc.blobID += BlobID(blobCount)
Transaction(
txType : TxEIP4844,
nonce : params.nonce,
chainId : params.chainId,
maxFee : gasFeeCap,
maxPriorityFee: gasTipCap,
gasLimit: tc.gasLimit,
to : tc.recipient,
value : tc.amount,
payload : tc.payload,
#AccessList: tc.AccessList,
maxFeePerBlobGas: blobFeeCap,
versionedHashes: system.move(blobData.hashes),
networkPayload: NetworkPayload(
blobs: system.move(blobData.blobs),
commitments: system.move(blobData.commitments),
proofs: system.move(blobData.proofs),
)
)
else:
doAssert(false, "unsupported tx type")
Transaction()
proc makeTx(params: MakeTxParams, tc: BaseTx): Transaction =
# Build the transaction depending on the specified type
let tx = if txType == TxLegacy:
Transaction(
txType : TxLegacy,
nonce : params.nonce,
to : tc.recipient,
value : tc.amount,
gasLimit: tc.gasLimit,
gasPrice: gasPrice,
payload : tc.payload
)
else:
Transaction(
txType : TxEIP1559,
nonce : params.nonce,
gasLimit: tc.gasLimit,
maxFee : gasFeeCap,
maxPriorityFee: gasTipCap,
to : tc.recipient,
value : tc.amount,
payload : tc.payload,
chainId : params.chainId
)
let tx = makeTxOfType(params, tc)
signTransaction(tx, params.key, params.chainId, eip155 = true)
proc makeTx(params: MakeTxParams, tc: BigInitcodeTx): Transaction =
@ -263,13 +302,13 @@ proc makeTx*(params: MakeTxParams, tc: BlobTx): Transaction =
let data = blobDataGenerator(tc.blobID, tc.blobCount)
doAssert(tc.recipient.isSome, "nil recipient address")
# Collect fields for transaction
let
gasFeeCap = if tc.gasFee != 0.GasInt: tc.gasFee
else: gasPrice
gasTipCap = if tc.gasTip != 0.GasInt: tc.gasTip
else: gasTipPrice
# Collect fields for transaction
let unsignedTx = Transaction(
txType : TxEip4844,
chainId : params.chainId,

View File

@ -8,11 +8,6 @@
# those terms.
import
std/[options, typetraits],
eth/common,
./web3_eth_conv,
./beacon_engine,
web3/execution_types,
./api_handler/api_utils,
./api_handler/api_getpayload,
./api_handler/api_getbodies,
@ -24,25 +19,6 @@ import
# Public functions
# ------------------------------------------------------------------------------
{.push gcsafe, raises:[CatchableError].}
func validateVersionedHashed*(payload: ExecutionPayload,
expected: openArray[Web3Hash]): bool =
var versionedHashes: seq[common.Hash256]
for x in payload.transactions:
let tx = rlp.decode(distinctBase(x), Transaction)
versionedHashes.add tx.versionedHashes
if versionedHashes.len != expected.len:
return false
for i, x in expected:
if distinctBase(x) != versionedHashes[i].data:
return false
true
{.pop.}
export
invalidStatus,
getPayload,

View File

@ -19,6 +19,21 @@ import
{.push gcsafe, raises:[CatchableError].}
func validateVersionedHashed(payload: ExecutionPayload,
expected: openArray[Web3Hash]): bool =
var versionedHashes: seq[common.Hash256]
for x in payload.transactions:
let tx = rlp.decode(distinctBase(x), Transaction)
versionedHashes.add tx.versionedHashes
if versionedHashes.len != expected.len:
return false
for i, x in expected:
if distinctBase(x) != versionedHashes[i].data:
return false
true
template validateVersion(com, timestamp, version, apiVersion) =
if apiVersion == Version.V4:
if not com.isPragueOrLater(timestamp):
@ -83,6 +98,7 @@ template validatePayload(apiVersion, version, payload) =
proc newPayload*(ben: BeaconEngineRef,
apiVersion: Version,
payload: ExecutionPayload,
versionedHashes = none(seq[Web3Hash]),
beaconRoot = none(Web3Hash)): PayloadStatusV1 =
trace "Engine API request received",
@ -90,7 +106,7 @@ proc newPayload*(ben: BeaconEngineRef,
number = payload.blockNumber,
hash = payload.blockHash
if apiVersion == Version.V3:
if apiVersion >= Version.V3:
if beaconRoot.isNone:
raise invalidParams("newPayloadV3 expect beaconRoot but got none")
@ -108,6 +124,12 @@ proc newPayload*(ben: BeaconEngineRef,
header.validateBlockHash(blockHash, version).isOkOr:
return error
if apiVersion >= Version.V3:
if versionedHashes.isNone:
raise invalidParams("newPayload" & $apiVersion &
" expect blobVersionedHashes but got none")
if not validateVersionedHashed(payload, versionedHashes.get):
return invalidStatus(header.parentHash, "invalid blob versionedHashes")
# If we already have the block locally, ignore the entire execution and just
# return a fake success.

View File

@ -13,8 +13,7 @@ import
web3/[conversions, execution_types],
../beacon/api_handler,
../beacon/beacon_engine,
../beacon/web3_eth_conv,
../beacon/api_handler/api_utils
../beacon/web3_eth_conv
{.push raises: [].}
@ -53,20 +52,12 @@ proc setupEngineAPI*(engine: BeaconEngineRef, server: RpcServer) =
server.rpc("engine_newPayloadV3") do(payload: ExecutionPayload,
expectedBlobVersionedHashes: Option[seq[Web3Hash]],
parentBeaconBlockRoot: Option[Web3Hash]) -> PayloadStatusV1:
if expectedBlobVersionedHashes.isNone:
raise invalidParams("newPayloadV3 expect blobVersionedHashes but got none")
if not validateVersionedHashed(payload, expectedBlobVersionedHashes.get):
return invalidStatus()
return engine.newPayload(Version.V3, payload, parentBeaconBlockRoot)
return engine.newPayload(Version.V3, payload, expectedBlobVersionedHashes, parentBeaconBlockRoot)
server.rpc("engine_newPayloadV4") do(payload: ExecutionPayload,
expectedBlobVersionedHashes: Option[seq[Web3Hash]],
parentBeaconBlockRoot: Option[Web3Hash]) -> PayloadStatusV1:
if expectedBlobVersionedHashes.isNone:
raise invalidParams("newPayloadV4 expect blobVersionedHashes but got none")
if not validateVersionedHashed(payload, expectedBlobVersionedHashes.get):
return invalidStatus()
return engine.newPayload(Version.V4, payload, parentBeaconBlockRoot)
return engine.newPayload(Version.V4, payload, expectedBlobVersionedHashes, parentBeaconBlockRoot)
server.rpc("engine_getPayloadV1") do(payloadId: PayloadID) -> ExecutionPayloadV1:
return engine.getPayload(Version.V1, payloadId).executionPayload.V1