Repositioning blob hash validation in newPayload of engine API (#2141)
This commit is contained in:
parent
7d9e1d8607
commit
6694e240d7
|
@ -615,22 +615,22 @@ proc generateInvalidPayload*(sender: TxSender, data: ExecutableData, payloadFiel
|
||||||
excessBlobGas: some(modExcessBlobGas),
|
excessBlobGas: some(modExcessBlobGas),
|
||||||
)
|
)
|
||||||
of InvalidVersionedHashesVersion:
|
of InvalidVersionedHashesVersion:
|
||||||
doAssert(data.versionedHashes.isNone, "no versioned hashes available for modification")
|
doAssert(data.versionedHashes.isSome, "no versioned hashes available for modification")
|
||||||
customPayloadMod = CustomPayloadData(
|
customPayloadMod = CustomPayloadData(
|
||||||
versionedHashesCustomizer: IncreaseVersionVersionedHashes(),
|
versionedHashesCustomizer: IncreaseVersionVersionedHashes(),
|
||||||
)
|
)
|
||||||
of InvalidVersionedHashes:
|
of InvalidVersionedHashes:
|
||||||
doAssert(data.versionedHashes.isNone, "no versioned hashes available for modification")
|
doAssert(data.versionedHashes.isSome, "no versioned hashes available for modification")
|
||||||
customPayloadMod = CustomPayloadData(
|
customPayloadMod = CustomPayloadData(
|
||||||
versionedHashesCustomizer: CorruptVersionedHashes(),
|
versionedHashesCustomizer: CorruptVersionedHashes(),
|
||||||
)
|
)
|
||||||
of IncompleteVersionedHashes:
|
of IncompleteVersionedHashes:
|
||||||
doAssert(data.versionedHashes.isNone, "no versioned hashes available for modification")
|
doAssert(data.versionedHashes.isSome, "no versioned hashes available for modification")
|
||||||
customPayloadMod = CustomPayloadData(
|
customPayloadMod = CustomPayloadData(
|
||||||
versionedHashesCustomizer: RemoveVersionedHash(),
|
versionedHashesCustomizer: RemoveVersionedHash(),
|
||||||
)
|
)
|
||||||
of ExtraVersionedHashes:
|
of ExtraVersionedHashes:
|
||||||
doAssert(data.versionedHashes.isNone, "no versioned hashes available for modification")
|
doAssert(data.versionedHashes.isSome, "no versioned hashes available for modification")
|
||||||
customPayloadMod = CustomPayloadData(
|
customPayloadMod = CustomPayloadData(
|
||||||
versionedHashesCustomizer: ExtraVersionedHash(),
|
versionedHashesCustomizer: ExtraVersionedHash(),
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Nimbus
|
# Nimbus
|
||||||
# Copyright (c) 2023 Status Research & Development GmbH
|
# Copyright (c) 2023-2024 Status Research & Development GmbH
|
||||||
# Licensed under either of
|
# Licensed under either of
|
||||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0)
|
# http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
|
|
@ -1865,7 +1865,7 @@ proc makeCancunTest(): seq[EngineSpec] =
|
||||||
mainFork: ForkCancun,
|
mainFork: ForkCancun,
|
||||||
fieldModification: t,
|
fieldModification: t,
|
||||||
)
|
)
|
||||||
#[
|
|
||||||
# Invalid Payload Tests
|
# Invalid Payload Tests
|
||||||
const
|
const
|
||||||
invalidFields = [
|
invalidFields = [
|
||||||
|
@ -1911,7 +1911,6 @@ proc makeCancunTest(): seq[EngineSpec] =
|
||||||
invalidDetectedOnSync: invalidDetectedOnSync,
|
invalidDetectedOnSync: invalidDetectedOnSync,
|
||||||
nilLatestValidHash : nilLatestValidHash,
|
nilLatestValidHash : nilLatestValidHash,
|
||||||
)
|
)
|
||||||
]#
|
|
||||||
|
|
||||||
# Invalid Transaction ChainID Tests
|
# Invalid Transaction ChainID Tests
|
||||||
result.add InvalidTxChainIDTest(
|
result.add InvalidTxChainIDTest(
|
||||||
|
|
|
@ -27,6 +27,11 @@ type
|
||||||
amount* : UInt256
|
amount* : UInt256
|
||||||
payload* : seq[byte]
|
payload* : seq[byte]
|
||||||
txType* : Option[TxType]
|
txType* : Option[TxType]
|
||||||
|
gasTip* : GasInt
|
||||||
|
gasFee* : GasInt
|
||||||
|
blobGasFee*: UInt256
|
||||||
|
blobCount* : int
|
||||||
|
blobID* : BlobID
|
||||||
|
|
||||||
BigInitcodeTx* = object of BaseTx
|
BigInitcodeTx* = object of BaseTx
|
||||||
initcodeLength*: int
|
initcodeLength*: int
|
||||||
|
@ -35,11 +40,6 @@ type
|
||||||
|
|
||||||
# Blob transaction creator
|
# Blob transaction creator
|
||||||
BlobTx* = object of BaseTx
|
BlobTx* = object of BaseTx
|
||||||
gasFee* : GasInt
|
|
||||||
gasTip* : GasInt
|
|
||||||
blobGasFee*: UInt256
|
|
||||||
blobID* : BlobID
|
|
||||||
blobCount* : int
|
|
||||||
|
|
||||||
TestAccount* = object
|
TestAccount* = object
|
||||||
key* : PrivateKey
|
key* : PrivateKey
|
||||||
|
@ -77,6 +77,7 @@ const
|
||||||
TestAccountCount = 1000
|
TestAccountCount = 1000
|
||||||
gasPrice* = 30.gwei
|
gasPrice* = 30.gwei
|
||||||
gasTipPrice* = 1.gwei
|
gasTipPrice* = 1.gwei
|
||||||
|
blobGasPrice* = 1.gwei
|
||||||
|
|
||||||
func toAddress(key: PrivateKey): EthAddress =
|
func toAddress(key: PrivateKey): EthAddress =
|
||||||
toKeyPair(key).pubkey.toCanonicalAddress()
|
toKeyPair(key).pubkey.toCanonicalAddress()
|
||||||
|
@ -129,15 +130,16 @@ proc getTxType(tc: BaseTx, nonce: uint64): TxType =
|
||||||
else:
|
else:
|
||||||
tc.txType.get
|
tc.txType.get
|
||||||
|
|
||||||
proc makeTx(params: MakeTxParams, tc: BaseTx): Transaction =
|
proc makeTxOfType(params: MakeTxParams, tc: BaseTx): Transaction =
|
||||||
const
|
let
|
||||||
gasFeeCap = gasPrice
|
gasFeeCap = if tc.gasFee != 0.GasInt: tc.gasFee
|
||||||
gasTipCap = gasTipPrice
|
else: gasPrice
|
||||||
|
gasTipCap = if tc.gasTip != 0.GasInt: tc.gasTip
|
||||||
|
else: gasTipPrice
|
||||||
|
|
||||||
let txType = tc.getTxType(params.nonce)
|
let txType = tc.getTxType(params.nonce)
|
||||||
|
case txType
|
||||||
# Build the transaction depending on the specified type
|
of TxLegacy:
|
||||||
let tx = if txType == TxLegacy:
|
|
||||||
Transaction(
|
Transaction(
|
||||||
txType : TxLegacy,
|
txType : TxLegacy,
|
||||||
nonce : params.nonce,
|
nonce : params.nonce,
|
||||||
|
@ -147,7 +149,7 @@ proc makeTx(params: MakeTxParams, tc: BaseTx): Transaction =
|
||||||
gasPrice: gasPrice,
|
gasPrice: gasPrice,
|
||||||
payload : tc.payload
|
payload : tc.payload
|
||||||
)
|
)
|
||||||
else:
|
of TxEip1559:
|
||||||
Transaction(
|
Transaction(
|
||||||
txType : TxEIP1559,
|
txType : TxEIP1559,
|
||||||
nonce : params.nonce,
|
nonce : params.nonce,
|
||||||
|
@ -159,7 +161,44 @@ proc makeTx(params: MakeTxParams, tc: BaseTx): Transaction =
|
||||||
payload : tc.payload,
|
payload : tc.payload,
|
||||||
chainId : params.chainId
|
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 = makeTxOfType(params, tc)
|
||||||
signTransaction(tx, params.key, params.chainId, eip155 = true)
|
signTransaction(tx, params.key, params.chainId, eip155 = true)
|
||||||
|
|
||||||
proc makeTx(params: MakeTxParams, tc: BigInitcodeTx): Transaction =
|
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)
|
let data = blobDataGenerator(tc.blobID, tc.blobCount)
|
||||||
doAssert(tc.recipient.isSome, "nil recipient address")
|
doAssert(tc.recipient.isSome, "nil recipient address")
|
||||||
|
|
||||||
# Collect fields for transaction
|
|
||||||
let
|
let
|
||||||
gasFeeCap = if tc.gasFee != 0.GasInt: tc.gasFee
|
gasFeeCap = if tc.gasFee != 0.GasInt: tc.gasFee
|
||||||
else: gasPrice
|
else: gasPrice
|
||||||
gasTipCap = if tc.gasTip != 0.GasInt: tc.gasTip
|
gasTipCap = if tc.gasTip != 0.GasInt: tc.gasTip
|
||||||
else: gasTipPrice
|
else: gasTipPrice
|
||||||
|
|
||||||
|
# Collect fields for transaction
|
||||||
let unsignedTx = Transaction(
|
let unsignedTx = Transaction(
|
||||||
txType : TxEip4844,
|
txType : TxEip4844,
|
||||||
chainId : params.chainId,
|
chainId : params.chainId,
|
||||||
|
|
|
@ -8,11 +8,6 @@
|
||||||
# those terms.
|
# those terms.
|
||||||
|
|
||||||
import
|
import
|
||||||
std/[options, typetraits],
|
|
||||||
eth/common,
|
|
||||||
./web3_eth_conv,
|
|
||||||
./beacon_engine,
|
|
||||||
web3/execution_types,
|
|
||||||
./api_handler/api_utils,
|
./api_handler/api_utils,
|
||||||
./api_handler/api_getpayload,
|
./api_handler/api_getpayload,
|
||||||
./api_handler/api_getbodies,
|
./api_handler/api_getbodies,
|
||||||
|
@ -24,25 +19,6 @@ import
|
||||||
# Public functions
|
# 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
|
export
|
||||||
invalidStatus,
|
invalidStatus,
|
||||||
getPayload,
|
getPayload,
|
||||||
|
|
|
@ -19,6 +19,21 @@ import
|
||||||
|
|
||||||
{.push gcsafe, raises:[CatchableError].}
|
{.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) =
|
template validateVersion(com, timestamp, version, apiVersion) =
|
||||||
if apiVersion == Version.V4:
|
if apiVersion == Version.V4:
|
||||||
if not com.isPragueOrLater(timestamp):
|
if not com.isPragueOrLater(timestamp):
|
||||||
|
@ -83,6 +98,7 @@ template validatePayload(apiVersion, version, payload) =
|
||||||
proc newPayload*(ben: BeaconEngineRef,
|
proc newPayload*(ben: BeaconEngineRef,
|
||||||
apiVersion: Version,
|
apiVersion: Version,
|
||||||
payload: ExecutionPayload,
|
payload: ExecutionPayload,
|
||||||
|
versionedHashes = none(seq[Web3Hash]),
|
||||||
beaconRoot = none(Web3Hash)): PayloadStatusV1 =
|
beaconRoot = none(Web3Hash)): PayloadStatusV1 =
|
||||||
|
|
||||||
trace "Engine API request received",
|
trace "Engine API request received",
|
||||||
|
@ -90,7 +106,7 @@ proc newPayload*(ben: BeaconEngineRef,
|
||||||
number = payload.blockNumber,
|
number = payload.blockNumber,
|
||||||
hash = payload.blockHash
|
hash = payload.blockHash
|
||||||
|
|
||||||
if apiVersion == Version.V3:
|
if apiVersion >= Version.V3:
|
||||||
if beaconRoot.isNone:
|
if beaconRoot.isNone:
|
||||||
raise invalidParams("newPayloadV3 expect beaconRoot but got none")
|
raise invalidParams("newPayloadV3 expect beaconRoot but got none")
|
||||||
|
|
||||||
|
@ -108,6 +124,12 @@ proc newPayload*(ben: BeaconEngineRef,
|
||||||
header.validateBlockHash(blockHash, version).isOkOr:
|
header.validateBlockHash(blockHash, version).isOkOr:
|
||||||
return error
|
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
|
# If we already have the block locally, ignore the entire execution and just
|
||||||
# return a fake success.
|
# return a fake success.
|
||||||
|
|
|
@ -13,8 +13,7 @@ import
|
||||||
web3/[conversions, execution_types],
|
web3/[conversions, execution_types],
|
||||||
../beacon/api_handler,
|
../beacon/api_handler,
|
||||||
../beacon/beacon_engine,
|
../beacon/beacon_engine,
|
||||||
../beacon/web3_eth_conv,
|
../beacon/web3_eth_conv
|
||||||
../beacon/api_handler/api_utils
|
|
||||||
|
|
||||||
{.push raises: [].}
|
{.push raises: [].}
|
||||||
|
|
||||||
|
@ -53,20 +52,12 @@ proc setupEngineAPI*(engine: BeaconEngineRef, server: RpcServer) =
|
||||||
server.rpc("engine_newPayloadV3") do(payload: ExecutionPayload,
|
server.rpc("engine_newPayloadV3") do(payload: ExecutionPayload,
|
||||||
expectedBlobVersionedHashes: Option[seq[Web3Hash]],
|
expectedBlobVersionedHashes: Option[seq[Web3Hash]],
|
||||||
parentBeaconBlockRoot: Option[Web3Hash]) -> PayloadStatusV1:
|
parentBeaconBlockRoot: Option[Web3Hash]) -> PayloadStatusV1:
|
||||||
if expectedBlobVersionedHashes.isNone:
|
return engine.newPayload(Version.V3, payload, expectedBlobVersionedHashes, parentBeaconBlockRoot)
|
||||||
raise invalidParams("newPayloadV3 expect blobVersionedHashes but got none")
|
|
||||||
if not validateVersionedHashed(payload, expectedBlobVersionedHashes.get):
|
|
||||||
return invalidStatus()
|
|
||||||
return engine.newPayload(Version.V3, payload, parentBeaconBlockRoot)
|
|
||||||
|
|
||||||
server.rpc("engine_newPayloadV4") do(payload: ExecutionPayload,
|
server.rpc("engine_newPayloadV4") do(payload: ExecutionPayload,
|
||||||
expectedBlobVersionedHashes: Option[seq[Web3Hash]],
|
expectedBlobVersionedHashes: Option[seq[Web3Hash]],
|
||||||
parentBeaconBlockRoot: Option[Web3Hash]) -> PayloadStatusV1:
|
parentBeaconBlockRoot: Option[Web3Hash]) -> PayloadStatusV1:
|
||||||
if expectedBlobVersionedHashes.isNone:
|
return engine.newPayload(Version.V4, payload, expectedBlobVersionedHashes, parentBeaconBlockRoot)
|
||||||
raise invalidParams("newPayloadV4 expect blobVersionedHashes but got none")
|
|
||||||
if not validateVersionedHashed(payload, expectedBlobVersionedHashes.get):
|
|
||||||
return invalidStatus()
|
|
||||||
return engine.newPayload(Version.V4, payload, parentBeaconBlockRoot)
|
|
||||||
|
|
||||||
server.rpc("engine_getPayloadV1") do(payloadId: PayloadID) -> ExecutionPayloadV1:
|
server.rpc("engine_getPayloadV1") do(payloadId: PayloadID) -> ExecutionPayloadV1:
|
||||||
return engine.getPayload(Version.V1, payloadId).executionPayload.V1
|
return engine.getPayload(Version.V1, payloadId).executionPayload.V1
|
||||||
|
|
Loading…
Reference in New Issue