nimbus-eth1/hive_integration/nodocker/engine/cancun/customizer.nim

684 lines
25 KiB
Nim

import
std/[options, strutils, typetraits, random],
nimcrypto/sysrand,
stew/byteutils,
./blobs,
../tx_sender,
../../../../nimbus/constants,
../../../../nimbus/utils/utils,
../../../../nimbus/common as nimbus_common,
../../../../nimbus/beacon/web3_eth_conv,
../../../../nimbus/beacon/payload_conv,
../../../../nimbus/beacon/execution_types
type
EngineAPIVersionResolver* = ref object of RootRef
com: CommonRef
method setEngineAPIVersionResolver*(cust: EngineAPIVersionResolver, v: CommonRef) {.base.} =
cust.com = v
method forkchoiceUpdatedVersion*(cust: EngineAPIVersionResolver,
headTimestamp: uint64, payloadAttributesTimestamp: Option[uint64]): Version {.base.} =
let ts = if payloadAttributesTimestamp.isNone: headTimestamp.EthTime
else: payloadAttributesTimestamp.get().EthTime
if cust.com.isCancunOrLater(ts):
Version.V3
elif cust.com.isShanghaiOrLater(ts):
Version.V2
else:
Version.V1
method newPayloadVersion*(cust: EngineAPIVersionResolver, timestamp: uint64): Version {.base.} =
let ts = timestamp.EthTime
if cust.com.isCancunOrLater(ts):
Version.V3
elif cust.com.isShanghaiOrLater(ts):
Version.V2
else:
Version.V1
method getPayloadVersion*(cust: EngineAPIVersionResolver, timestamp: uint64): Version {.base.} =
let ts = timestamp.EthTime
if cust.com.isCancunOrLater(ts):
Version.V3
elif cust.com.isShanghaiOrLater(ts):
Version.V2
else:
Version.V1
type
GetPayloadCustomizer* = ref object of EngineAPIVersionResolver
method getPayloadID*(cust: GetPayloadCustomizer,
basePayloadID: PayloadID): PayloadID {.base.} =
doAssert(false, "getPayloadID unimplemented")
method getExpectedError*(cust: GetPayloadCustomizer): int {.base.} =
doAssert(false, "getExpectedError unimplemented")
type
BaseGetPayloadCustomizer* = ref object of GetPayloadCustomizer
customPayloadID: Option[PayloadID]
expectedError : int
method getPayloadID(cust: BaseGetPayloadCustomizer,
basePayloadID: PayloadID): PayloadID =
if cust.customPayloadID.isSome:
return cust.customPayloadID.get
return basePayloadID
method getExpectedError(cust: BaseGetPayloadCustomizer): int =
cust.expectedError
type
UpgradegetPayloadVersion* = ref object of GetPayloadCustomizer
method getPayloadVersion(cust: UpgradegetPayloadVersion, timestamp: uint64): Version =
let version = procCall getPayloadVersion(cust.GetPayloadCustomizer, timestamp)
doAssert(version != Version.high, "cannot upgrade version " & $Version.high)
version.succ
type
DowngradegetPayloadVersion* = ref object of GetPayloadCustomizer
method getPayloadVersion(cust: DowngradegetPayloadVersion, timestamp: uint64): Version =
let version = procCall getPayloadVersion(cust.GetPayloadCustomizer, timestamp)
doAssert(version != Version.V1, "cannot downgrade version 1")
version.pred
type
PayloadAttributesCustomizer* = ref object of GetPayloadCustomizer
method getPayloadAttributes*(cust: PayloadAttributesCustomizer, basePayloadAttributes: PayloadAttributes): PayloadAttributes {.base.} =
doAssert(false, "getPayloadAttributes unimplemented")
type
BasePayloadAttributesCustomizer* = ref object of PayloadAttributesCustomizer
timestamp : Option[uint64]
prevRandao : Option[common.Hash256]
suggestedFeeRecipient : Option[common.EthAddress]
withdrawals : Option[seq[Withdrawal]]
removeWithdrawals : bool
beaconRoot : Option[common.Hash256]
removeBeaconRoot : bool
method getPayloadAttributes(cust: BasePayloadAttributesCustomizer, basePayloadAttributes: PayloadAttributes): PayloadAttributes =
var customPayloadAttributes = PayloadAttributes(
timestamp: basePayloadAttributes.timestamp,
prevRandao: basePayloadAttributes.prevRandao,
suggestedFeeRecipient: basePayloadAttributes.suggestedFeeRecipient,
withdrawals: basePayloadAttributes.withdrawals,
parentBeaconBlockRoot: basePayloadAttributes.parentBeaconBlockRoot,
)
if cust.timestamp.isSome:
customPayloadAttributes.timestamp = w3Qty cust.timestamp.get
if cust.prevRandao.isSome:
customPayloadAttributes.prevRandao = w3Hash cust.prevRandao.get
if cust.suggestedFeeRecipient.isSome:
customPayloadAttributes.suggestedFeeRecipient = w3Addr cust.suggestedFeeRecipient.get
if cust.removeWithdrawals:
customPayloadAttributes.withdrawals = none(seq[WithdrawalV1])
elif cust.withdrawals.isSome:
customPayloadAttributes.withdrawals = w3Withdrawals cust.withdrawals
if cust.removeBeaconRoot:
customPayloadAttributes.parentBeaconBlockRoot = none(Web3Hash)
elif cust.beaconRoot.isSome:
customPayloadAttributes.parentBeaconBlockRoot = w3Hash cust.beaconRoot
return customPayloadAttributes
type
TimestampDeltaPayloadAttributesCustomizer* = ref object of BasePayloadAttributesCustomizer
timestampDelta: uint64
method getPayloadAttributes(cust: TimestampDeltaPayloadAttributesCustomizer, basePayloadAttributes: PayloadAttributes): PayloadAttributes =
var customPayloadAttributes = procCall getPayloadAttributes(cust.BasePayloadAttributesCustomizer, basePayloadAttributes)
customPayloadAttributes.timestamp = w3Qty(customPayloadAttributes.timestamp, cust.timestampDelta)
return customPayloadAttributes
type
ForkchoiceUpdatedCustomizer* = ref object of BasePayloadAttributesCustomizer
method getForkchoiceState*(cust: ForkchoiceUpdatedCustomizer,
baseForkchoiceUpdate: ForkchoiceStateV1): ForkchoiceStateV1 {.base.} =
doAssert(false, "getForkchoiceState unimplemented")
method getExpectInvalidStatus*(cust: ForkchoiceUpdatedCustomizer): bool {.base.} =
doAssert(false, "getExpectInvalidStatus unimplemented")
# Customizer that makes no modifications to the forkchoice directive call.
# Used as base to other customizers.
type
BaseForkchoiceUpdatedCustomizer* = ref object of ForkchoiceUpdatedCustomizer
expectedError : int
expectInvalidStatus: bool
method getPayloadAttributes(cust: BaseForkchoiceUpdatedCustomizer, basePayloadAttributes: PayloadAttributes): PayloadAttributes =
var customPayloadAttributes = procCall getPayloadAttributes(cust.BasePayloadAttributesCustomizer, basePayloadAttributes)
return customPayloadAttributes
method getForkchoiceState(cust: BaseForkchoiceUpdatedCustomizer, baseForkchoiceUpdate: ForkchoiceStateV1): ForkchoiceStateV1 =
return baseForkchoiceUpdate
method getExpectedError(cust: BaseForkchoiceUpdatedCustomizer): int =
return cust.expectedError
method getExpectInvalidStatus(cust: BaseForkchoiceUpdatedCustomizer): bool =
return cust.expectInvalidStatus
# Customizer that upgrades the version of the forkchoice directive call to the next version.
type
UpgradeforkchoiceUpdatedVersion* = ref object of BaseForkchoiceUpdatedCustomizer
method forkchoiceUpdatedVersion(cust: UpgradeforkchoiceUpdatedVersion, headTimestamp: uint64, payloadAttributesTimestamp: Option[uint64]): Version =
let version = procCall forkchoiceUpdatedVersion(EngineAPIVersionResolver(cust), headTimestamp, payloadAttributesTimestamp)
doAssert(version != Version.high, "cannot upgrade version " & $Version.high)
version.succ
# Customizer that downgrades the version of the forkchoice directive call to the previous version.
type
DowngradeforkchoiceUpdatedVersion* = ref object of BaseForkchoiceUpdatedCustomizer
method forkchoiceUpdatedVersion(cust: DowngradeforkchoiceUpdatedVersion, headTimestamp: uint64, payloadAttributesTimestamp: Option[uint64]): Version =
let version = procCall forkchoiceUpdatedVersion(EngineAPIVersionResolver(cust), headTimestamp, payloadAttributesTimestamp)
doAssert(version != Version.V1, "cannot downgrade version 1")
version.pred
type
VersionedHashRef* = ref object of RootRef
blobs*: seq[BlobID]
hashVersions*: seq[byte]
proc getVersionedHashes*(v: VersionedHashRef): seq[common.Hash256] =
if v.blobs.len == 0:
return @[]
result = newSeq[common.Hash256](v.blobs.len)
var version: byte
for i, blobID in v.blobs:
if v.hashVersions.len > i:
version = v.hashVersions[i]
result[i] = blobID.getVersionedHash(version)
proc description*(v: VersionedHashRef): string =
result = "VersionedHashes: "
for x in v.blobs:
result.add x.toHex
if v.hashVersions.len > 0:
result.add " with versions "
result.add v.hashVersions.toHex
type
VersionedHashesCustomizer* = ref object of RootRef
IncreaseVersionVersionedHashes* = ref object of VersionedHashesCustomizer
method getVersionedHashes*(cust: VersionedHashesCustomizer, baseVersionedHashes: openArray[common.Hash256]): seq[common.Hash256] {.base.} =
doAssert(false, "getVersionedHashes unimplemented")
method getVersionedHashes(cust: IncreaseVersionVersionedHashes, baseVersionedHashes: openArray[common.Hash256]): seq[common.Hash256] =
doAssert(baseVersionedHashes.len > 0, "no versioned hashes available for modification")
result = newSeq[common.Hash256](baseVersionedHashes.len)
for i, h in baseVersionedHashes:
result[i] = h
result[i].data[0] = result[i].data[0] + 1
type
CorruptVersionedHashes* = ref object of VersionedHashesCustomizer
method getVersionedHashes(cust: CorruptVersionedHashes, baseVersionedHashes: openArray[common.Hash256]): seq[common.Hash256] =
doAssert(baseVersionedHashes.len > 0, "no versioned hashes available for modification")
result = newSeq[common.Hash256](baseVersionedHashes.len)
for i, h in baseVersionedHashes:
result[i] = h
result[i].data[h.data.len-1] = result[i].data[h.data.len-1] + 1
type
RemoveVersionedHash* = ref object of VersionedHashesCustomizer
method getVersionedHashes(cust: RemoveVersionedHash, baseVersionedHashes: openArray[common.Hash256]): seq[common.Hash256] =
doAssert(baseVersionedHashes.len > 0, "no versioned hashes available for modification")
result = newSeq[common.Hash256](baseVersionedHashes.len - 1)
for i, h in baseVersionedHashes:
if i < baseVersionedHashes.len-1:
result[i] = h
result[i].data[h.data.len-1] = result[i].data[h.data.len-1] + 1
type
ExtraVersionedHash* = ref object of VersionedHashesCustomizer
method getVersionedHashes(cust: ExtraVersionedHash, baseVersionedHashes: openArray[common.Hash256]): seq[common.Hash256] =
result = newSeq[common.Hash256](baseVersionedHashes.len + 1)
for i, h in baseVersionedHashes:
result[i] = h
var extraHash: common.Hash256
doAssert randomBytes(extraHash.data) == 32
extraHash.data[0] = VERSIONED_HASH_VERSION_KZG
result[^1] = extraHash
type
PayloadCustomizer* = ref object of EngineAPIVersionResolver
ExecutableData* = object
basePayload*: ExecutionPayload
beaconRoot* : Option[common.Hash256]
attr* : PayloadAttributes
versionedHashes*: seq[common.Hash256]
method customizePayload(cust: PayloadCustomizer, data: ExecutableData): ExecutableData {.base.} =
doAssert(false, "customizePayload unimplemented")
method getTimestamp(cust: PayloadCustomizer, basePayload: ExecutionPayload): uint64 {.base.} =
doAssert(false, "getTimestamp unimplemented")
type
NewPayloadCustomizer* = ref object of PayloadCustomizer
method getExpectedError(cust: NewPayloadCustomizer): int {.base.} =
doAssert(false, "getExpectedError unimplemented")
method getExpectInvalidStatus(cust: NewPayloadCustomizer): bool {.base.} =
doAssert(false, "getExpectInvalidStatus unimplemented")
type
CustomPayloadData = object
parentHash* : Option[common.Hash256]
feeRecipient* : Option[common.EthAddress]
stateRoot* : Option[common.Hash256]
receiptsRoot* : Option[common.Hash256]
logsBloom* : Option[BloomFilter]
prevRandao* : Option[common.Hash256]
number* : Option[uint64]
gasLimit* : Option[GasInt]
gasUsed* : Option[GasInt]
timestamp* : Option[uint64]
extraData* : Option[common.Blob]
baseFeePerGas* : Option[UInt256]
blockHash* : Option[common.Hash256]
transactions* : Option[seq[Transaction]]
withdrawals* : Option[seq[Withdrawal]]
removeWithdrawals* : bool
blobGasUsed* : Option[uint64]
removeBlobGasUsed* : bool
excessBlobGas* : Option[uint64]
removeExcessBlobGas* : bool
parentBeaconRoot* : Option[common.Hash256]
removeParentBeaconRoot* : bool
versionedHashesCustomizer*: VersionedHashesCustomizer
func getTimestamp*(cust: CustomPayloadData, basePayload: ExecutionPayload): uint64 =
if cust.timestamp.isSome:
return cust.timestamp.get
return basePayload.timestamp.uint64
# Construct a customized payload by taking an existing payload as base and mixing it CustomPayloadData
# blockHash is calculated automatically.
proc customizePayload*(cust: CustomPayloadData, data: ExecutableData): ExecutableData =
var customHeader = blockHeader(data.basePayload, data.beaconRoot)
if cust.transactions.isSome:
customHeader.txRoot = calcTxRoot(cust.transactions.get)
# Overwrite custom information
if cust.parentHash.isSome:
customHeader.parentHash = cust.parentHash.get
if cust.feeRecipient.isSome:
customHeader.coinbase = cust.feeRecipient.get
if cust.stateRoot.isSome:
customHeader.stateRoot = cust.stateRoot.get
if cust.receiptsRoot.isSome:
customHeader.receiptRoot = cust.receiptsRoot.get
if cust.logsBloom.isSome:
customHeader.bloom = cust.logsBloom.get
if cust.prevRandao.isSome:
customHeader.mixDigest = cust.prevRandao.get
if cust.number.isSome:
customHeader.blockNumber = cust.number.get.u256
if cust.gasLimit.isSome:
customHeader.gasLimit = cust.gasLimit.get
if cust.gasUsed.isSome:
customHeader.gasUsed = cust.gasUsed.get
if cust.timestamp.isSome:
customHeader.timestamp = cust.timestamp.get.EthTime
if cust.extraData.isSome:
customHeader.extraData = cust.extraData.get
if cust.baseFeePerGas.isSome:
customHeader.fee = cust.baseFeePerGas
if cust.removeWithdrawals:
customHeader.withdrawalsRoot = none(common.Hash256)
elif cust.withdrawals.isSome:
let h = calcWithdrawalsRoot(cust.withdrawals.get)
customHeader.withdrawalsRoot = some(h)
if cust.removeBlobGasUsed:
customHeader.blobGasUsed = none(uint64)
elif cust.blobGasUsed.isSome:
customHeader.blobGasUsed = cust.blobGasUsed
if cust.removeExcessBlobGas:
customHeader.excessBlobGas = none(uint64)
elif cust.excessBlobGas.isSome:
customHeader.excessBlobGas = cust.excessBlobGas
if cust.removeParentBeaconRoot:
customHeader.parentBeaconBlockRoot = none(common.Hash256)
elif cust.parentBeaconRoot.isSome:
customHeader.parentBeaconBlockRoot = cust.parentBeaconRoot
var blk = EthBlock(
header: customHeader,
)
if cust.removeWithdrawals:
blk.withdrawals = none(seq[Withdrawal])
elif cust.withdrawals.isSome:
blk.withdrawals = cust.withdrawals
elif data.basePayload.withdrawals.isSome:
blk.withdrawals = ethWithdrawals data.basePayload.withdrawals
result = ExecutableData(
basePayload : executionPayload(blk),
beaconRoot : blk.header.parentBeaconBlockRoot,
attr : data.attr,
versionedHashes: data.versionedHashes,
)
if cust.versionedHashesCustomizer.isNil.not:
result.versionedHashes = cust.versionedHashesCustomizer.getVersionedHashes(data.versionedHashes)
# Base new payload directive call cust.
# Used as base to other customizers.
type
BaseNewPayloadVersionCustomizer* = ref object of NewPayloadCustomizer
payloadCustomizer* : CustomPayloadData
expectedError* : int
expectInvalidStatus*: bool
method customizePayload(cust: BaseNewPayloadVersionCustomizer, data: ExecutableData): ExecutableData =
cust.payloadCustomizer.customizePayload(data)
method getExpectedError(cust: BaseNewPayloadVersionCustomizer): int =
cust.expectedError
method getExpectInvalidStatus(cust: BaseNewPayloadVersionCustomizer): bool =
cust.expectInvalidStatus
# Customizer that upgrades the version of the payload to the next version.
type
UpgradeNewPayloadVersion* = ref object of NewPayloadCustomizer
method newPayloadVersion(cust: UpgradeNewPayloadVersion, timestamp: uint64): Version =
let version = procCall newPayloadVersion(EngineAPIVersionResolver(cust), timestamp)
doAssert(version != Version.high, "cannot upgrade version " & $Version.high)
version.succ
# Customizer that downgrades the version of the payload to the previous version.
type
DowngradeNewPayloadVersion* = ref object of NewPayloadCustomizer
method newPayloadVersion(cust: DowngradeNewPayloadVersion, timestamp: uint64): Version =
let version = procCall newPayloadVersion(EngineAPIVersionResolver(cust), timestamp)
doAssert(version != Version.V1, "cannot downgrade version 1")
version.pred
proc customizePayloadTransactions*(data: ExecutableData, customTransactions: openArray[Transaction]): ExecutableData =
let cpd = CustomPayloadData(
transactions: some(@customTransactions),
)
customizePayload(cpd, data)
proc `$`*(cust: CustomPayloadData): string =
var fieldList = newSeq[string]()
if cust.parentHash.isSome:
fieldList.add "parentHash=" & cust.parentHash.get.short
if cust.feeRecipient.isSome:
fieldList.add "Coinbase=" & $cust.feeRecipient.get
if cust.stateRoot.isSome:
fieldList.add "stateRoot=" & cust.stateRoot.get.short
if cust.receiptsRoot.isSome:
fieldList.add "receiptsRoot=" & cust.receiptsRoot.get.short
if cust.logsBloom.isSome:
fieldList.add "logsBloom=" & cust.logsBloom.get.toHex
if cust.prevRandao.isSome:
fieldList.add "prevRandao=" & cust.prevRandao.get.short
if cust.number.isSome:
fieldList.add "Number=" & $cust.number.get
if cust.gasLimit.isSome:
fieldList.add "gasLimit=" & $cust.gasLimit.get
if cust.gasUsed.isSome:
fieldList.add "gasUsed=" & $cust.gasUsed.get
if cust.timestamp.isSome:
fieldList.add "timestamp=" & $cust.timestamp.get
if cust.extraData.isSome:
fieldList.add "extraData=" & cust.extraData.get.toHex
if cust.baseFeePerGas.isSome:
fieldList.add "baseFeePerGas=" & $cust.baseFeePerGas.get
if cust.transactions.isSome:
fieldList.add "transactions=" & $cust.transactions.get.len
if cust.withdrawals.isSome:
fieldList.add "withdrawals=" & $cust.withdrawals.get.len
fieldList.join(", ")
type
InvalidPayloadBlockField* = enum
InvalidParentHash
InvalidStateRoot
InvalidReceiptsRoot
InvalidNumber
InvalidGasLimit
InvalidGasUsed
InvalidTimestamp
InvalidPrevRandao
RemoveTransaction
InvalidTransactionSignature
InvalidTransactionNonce
InvalidTransactionGas
InvalidTransactionGasPrice
InvalidTransactionValue
InvalidTransactionGasTipPrice
InvalidTransactionChainID
InvalidParentBeaconBlockRoot
InvalidExcessBlobGas
InvalidBlobGasUsed
InvalidBlobCountGasUsed
InvalidVersionedHashesVersion
InvalidVersionedHashes
IncompleteVersionedHashes
ExtraVersionedHashes
InvalidWithdrawals
func scramble(data: Web3Hash): Option[common.Hash256] =
var h = ethHash data
h.data[^1] = byte(255 - h.data[^1])
some(h)
func scramble(data: common.Hash256): Option[common.Hash256] =
var h = data
h.data[^1] = byte(255 - h.data[^1])
some(h)
# This function generates an invalid payload by taking a base payload and modifying the specified field such that it ends up being invalid.
# One small consideration is that the payload needs to contain transactions and specially transactions using the PREVRANDAO opcode for all the fields to be compatible with this function.
proc generateInvalidPayload*(sender: TxSender, data: ExecutableData, payloadField: InvalidPayloadBlockField): ExecutableData =
var customPayloadMod: CustomPayloadData
let basePayload = data.basePayload
case payloadField
of InvalidParentHash:
customPayloadMod = CustomPayloadData(
parentHash: scramble(basePayload.parentHash),
)
of InvalidStateRoot:
customPayloadMod = CustomPayloadData(
stateRoot: scramble(basePayload.stateRoot),
)
of InvalidReceiptsRoot:
customPayloadMod = CustomPayloadData(
receiptsRoot: scramble(basePayload.receiptsRoot),
)
of InvalidNumber:
let modNumber = basePayload.blockNumber.uint64 - 1
customPayloadMod = CustomPayloadData(
number: some(modNumber),
)
of InvalidGasLimit:
let modGasLimit = basePayload.gasLimit.GasInt * 2
customPayloadMod = CustomPayloadData(
gasLimit: some(modGasLimit),
)
of InvalidGasUsed:
let modGasUsed = basePayload.gasUsed.GasInt - 1
customPayloadMod = CustomPayloadData(
gasUsed: some(modGasUsed),
)
of InvalidTimestamp:
let modTimestamp = basePayload.timestamp.uint64 - 1
customPayloadMod = CustomPayloadData(
timestamp: some(modTimestamp),
)
of InvalidPrevRandao:
# This option potentially requires a transaction that uses the PREVRANDAO opcode.
# Otherwise the payload will still be valid.
var randomHash: common.Hash256
doAssert randomBytes(randomHash.data) == 32
customPayloadMod = CustomPayloadData(
prevRandao: some(randomHash),
)
of InvalidParentBeaconBlockRoot:
doAssert(data.beaconRoot.isSome,
"no parent beacon block root available for modification")
customPayloadMod = CustomPayloadData(
parentBeaconRoot: scramble(data.beaconRoot.get),
)
of InvalidBlobGasUsed:
doAssert(basePayload.blobGasUsed.isSome, "no blob gas used available for modification")
let modBlobGasUsed = basePayload.blobGasUsed.get.uint64 + 1
customPayloadMod = CustomPayloadData(
blobGasUsed: some(modBlobGasUsed),
)
of InvalidBlobCountGasUsed:
doAssert(basePayload.blobGasUsed.isSome, "no blob gas used available for modification")
let modBlobGasUsed = basePayload.blobGasUsed.get.uint64 + GAS_PER_BLOB
customPayloadMod = CustomPayloadData(
blobGasUsed: some(modBlobGasUsed),
)
of InvalidExcessBlobGas:
doAssert(basePayload.excessBlobGas.isSome, "no excess blob gas available for modification")
let modExcessBlobGas = basePayload.excessBlobGas.get.uint64 + 1
customPayloadMod = CustomPayloadData(
excessBlobGas: some(modExcessBlobGas),
)
of InvalidVersionedHashesVersion:
doAssert(data.versionedHashes.len > 0, "no versioned hashes available for modification")
customPayloadMod = CustomPayloadData(
versionedHashesCustomizer: IncreaseVersionVersionedHashes(),
)
of InvalidVersionedHashes:
doAssert(data.versionedHashes.len > 0, "no versioned hashes available for modification")
customPayloadMod = CustomPayloadData(
versionedHashesCustomizer: CorruptVersionedHashes(),
)
of IncompleteVersionedHashes:
doAssert(data.versionedHashes.len > 0, "no versioned hashes available for modification")
customPayloadMod = CustomPayloadData(
versionedHashesCustomizer: RemoveVersionedHash(),
)
of ExtraVersionedHashes:
doAssert(data.versionedHashes.len > 0, "no versioned hashes available for modification")
customPayloadMod = CustomPayloadData(
versionedHashesCustomizer: ExtraVersionedHash(),
)
of InvalidWithdrawals:
# These options are not supported yet.
# TODO: Implement
doAssert(false, "invalid payload field not supported yet: " & $payloadField)
of RemoveTransaction:
let emptyTxs = newSeq[Transaction]()
customPayloadMod = CustomPayloadData(
transactions: some(emptyTxs),
)
of InvalidTransactionSignature,
InvalidTransactionNonce,
InvalidTransactionGas,
InvalidTransactionGasPrice,
InvalidTransactionGasTipPrice,
InvalidTransactionValue,
InvalidTransactionChainID:
doAssert(basePayload.transactions.len > 0, "no transactions available for modification")
let baseTx = rlp.decode(distinctBase basePayload.transactions[0], Transaction)
var custTx: CustomTransactionData
case payloadField
of InvalidTransactionSignature:
custTx.signature = some(baseTx.R - 1.u256)
of InvalidTransactionNonce:
custTx.nonce = some(baseTx.nonce - 1)
of InvalidTransactionGas:
custTx.gas = some(0.GasInt)
of InvalidTransactionGasPrice:
custTx.gasPriceOrGasFeeCap = some(0.GasInt)
of InvalidTransactionGasTipPrice:
custTx.gasTipCap = some(gasTipPrice.GasInt * 2.GasInt)
of InvalidTransactionValue:
# Vault account initially has 0x123450000000000000000, so this value should overflow
custTx.value = some(UInt256.fromHex("0x123450000000000000001"))
of InvalidTransactionChainID:
custTx.chainId = some(ChainId(baseTx.chainId.uint64 + 1))
else: discard
let modifiedTx = sender.customizeTransaction(baseTx, custTx)
customPayloadMod = CustomPayloadData(
transactions: some(@[modifiedTx]),
)
customPayloadMod.customizePayload(data)
# Generates an alternative withdrawals list that contains the same
# amounts and accounts, but the order in the list is different, so
# stateRoot of the resulting payload should be the same.
proc randomizeWithdrawalsOrder(src: openArray[Withdrawal]): seq[Withdrawal] =
result = @src
result.shuffle