nimbus-eth1/hive_integration/nodocker/engine/helper.nim

341 lines
11 KiB
Nim

import
std/[typetraits, times],
nimcrypto/sysrand,
eth/[common, rlp, keys],
json_rpc/[rpcclient],
../../../nimbus/transaction,
../../../nimbus/utils/utils,
../../../nimbus/beacon/execution_types,
../../../nimbus/beacon/web3_eth_conv
type
ExecutableData* = object
parentHash* : common.Hash256
feeRecipient* : EthAddress
stateRoot* : common.Hash256
receiptsRoot* : common.Hash256
logsBloom* : BloomFilter
prevRandao* : common.Hash256
number* : uint64
gasLimit* : GasInt
gasUsed* : GasInt
timestamp* : EthTime
extraData* : common.Blob
baseFeePerGas*: UInt256
blockHash* : common.Hash256
transactions* : seq[Transaction]
withdrawals* : Option[seq[Withdrawal]]
blobGasUsed* : Option[uint64]
excessBlobGas*: Option[uint64]
CustomPayload* = object
parentHash* : Option[common.Hash256]
feeRecipient* : Option[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[EthTime]
extraData* : Option[common.Blob]
baseFeePerGas*: Option[UInt256]
blockHash* : Option[common.Hash256]
transactions* : Option[seq[Transaction]]
withdrawals* : Option[seq[Withdrawal]]
blobGasUsed* : Option[uint64]
excessBlobGas*: Option[uint64]
beaconRoot* : Option[common.Hash256]
removeWithdrawals*: bool
InvalidPayloadField* = enum
InvalidParentHash
InvalidStateRoot
InvalidReceiptsRoot
InvalidNumber
InvalidGasLimit
InvalidGasUsed
InvalidTimestamp
InvalidPrevRandao
RemoveTransaction
InvalidTransactionSignature
InvalidTransactionNonce
InvalidTransactionGas
InvalidTransactionGasPrice
InvalidTransactionValue
SignatureVal = object
V: int64
R: UInt256
S: UInt256
CustomTx = object
nonce : Option[AccountNonce]
gasPrice: Option[GasInt]
gasLimit: Option[GasInt]
to : Option[EthAddress]
value : Option[UInt256]
data : Option[seq[byte]]
sig : Option[SignatureVal]
proc customizePayload*(basePayload: ExecutableData, customData: CustomPayload): ExecutionPayload =
let txs = if customData.transactions.isSome:
customData.transactions.get
else:
basePayload.transactions
let txRoot = calcTxRoot(txs)
let wdRoot = if customData.withdrawals.isSome:
some(calcWithdrawalsRoot(customData.withdrawals.get))
elif basePayload.withdrawals.isSome:
some(calcWithdrawalsRoot(basePayload.withdrawals.get))
else:
none(common.Hash256)
var customHeader = common.BlockHeader(
parentHash: basePayload.parentHash,
ommersHash: EMPTY_UNCLE_HASH,
coinbase: basePayload.feeRecipient,
stateRoot: basePayload.stateRoot,
txRoot: txRoot,
receiptRoot: basePayload.receiptsRoot,
bloom: basePayload.logsBloom,
difficulty: 0.u256,
blockNumber: basePayload.number.toBlockNumber,
gasLimit: basePayload.gasLimit,
gasUsed: basePayload.gasUsed,
timestamp: basePayload.timestamp,
extraData: basePayload.extraData,
mixDigest: basePayload.prevRandao,
nonce: default(BlockNonce),
fee: some(basePayload.baseFeePerGas),
withdrawalsRoot: wdRoot,
blobGasUsed: basePayload.blobGasUsed,
excessBlobGas: basePayload.excessBlobGas,
)
# Overwrite custom information
if customData.parentHash.isSome:
customHeader.parentHash = customData.parentHash.get
if customData.feeRecipient.isSome:
customHeader.coinbase = customData.feeRecipient.get
if customData.stateRoot.isSome:
customHeader.stateRoot = customData.stateRoot.get
if customData.receiptsRoot.isSome:
customHeader.receiptRoot = customData.receiptsRoot.get
if customData.logsBloom.isSome:
customHeader.bloom = customData.logsBloom.get
if customData.prevRandao.isSome:
customHeader.mixDigest = customData.prevRandao.get
if customData.number.isSome:
customHeader.blockNumber = toBlockNumber(customData.number.get)
if customData.gasLimit.isSome:
customHeader.gasLimit = customData.gasLimit.get
if customData.gasUsed.isSome:
customHeader.gasUsed = customData.gasUsed.get
if customData.timestamp.isSome:
customHeader.timestamp = customData.timestamp.get
if customData.extraData.isSome:
customHeader.extraData = customData.extraData.get
if customData.baseFeePerGas.isSome:
customHeader.baseFee = customData.baseFeePerGas.get
if customData.blobGasUsed.isSome:
customHeader.blobGasUsed = customData.blobGasUsed
if customData.excessBlobGas.isSome:
customHeader.excessBlobGas = customData.excessBlobGas
if customData.beaconRoot.isSome:
customHeader.parentBeaconBlockRoot = customData.beaconRoot
# Return the new payload
result = ExecutionPayload(
parentHash: w3Hash customHeader.parentHash,
feeRecipient: w3Addr customHeader.coinbase,
stateRoot: w3Hash customHeader.stateRoot,
receiptsRoot: w3Hash customHeader.receiptRoot,
logsBloom: w3Bloom customHeader.bloom,
prevRandao: w3PrevRandao customHeader.mixDigest,
blockNumber: w3Qty customHeader.blockNumber,
gasLimit: w3Qty customHeader.gasLimit,
gasUsed: w3Qty customHeader.gasUsed,
timestamp: w3Qty customHeader.timestamp,
extraData: w3ExtraData customHeader.extraData,
baseFeePerGas: customHeader.baseFee,
blockHash: w3Hash customHeader.blockHash,
blobGasUsed: w3Qty customHeader.blobGasUsed,
excessBlobGas: w3Qty customHeader.excessBlobGas,
)
for tx in txs:
let txData = rlp.encode(tx)
result.transactions.add TypedTransaction(txData)
let wds = if customData.withdrawals.isSome:
customData.withdrawals
elif basePayload.withdrawals.isSome:
basePayload.withdrawals
else:
none(seq[Withdrawal])
if wds.isSome and customData.removeWithdrawals.not:
result.withdrawals = some(w3Withdrawals(wds.get))
proc toExecutableData*(payload: ExecutionPayload): ExecutableData =
result = ExecutableData(
parentHash : ethHash payload.parentHash,
feeRecipient : distinctBase payload.feeRecipient,
stateRoot : ethHash payload.stateRoot,
receiptsRoot : ethHash payload.receiptsRoot,
logsBloom : distinctBase payload.logsBloom,
prevRandao : ethHash payload.prevRandao,
number : uint64 payload.blockNumber,
gasLimit : GasInt payload.gasLimit,
gasUsed : GasInt payload.gasUsed,
timestamp : ethTime payload.timestamp,
extraData : distinctBase payload.extraData,
baseFeePerGas : payload.baseFeePerGas,
blockHash : ethHash payload.blockHash,
blobGasUsed : u64 payload.blobGasUsed,
excessBlobGas : u64 payload.excessBlobGas,
transactions : ethTxs payload.transactions,
withdrawals : ethWithdrawals payload.withdrawals,
)
proc customizePayload*(basePayload: ExecutionPayload, customData: CustomPayload): ExecutionPayload =
customizePayload(basePayload.toExecutableData, customData)
proc customizeTx(baseTx: Transaction, vaultKey: PrivateKey, customTx: CustomTx): Transaction =
# Create a modified transaction base, from the base transaction and customData mix
var modTx = Transaction(
txType : TxLegacy,
nonce : baseTx.nonce,
gasPrice: baseTx.gasPrice,
gasLimit: baseTx.gasLimit,
to : baseTx.to,
value : baseTx.value,
payload : baseTx.payload
)
if customTx.nonce.isSome:
modTx.nonce = customTx.nonce.get
if customTx.gasPrice.isSome:
modTx.gasPrice = customTx.gasPrice.get
if customTx.gasLimit.isSome:
modTx.gasLimit = customTx.gasLimit.get
if customTx.to.isSome:
modTx.to = customTx.to
if customTx.value.isSome:
modTx.value = customTx.value.get
if customTx.data.isSome:
modTx.payload = customTx.data.get
if customTx.sig.isSome:
let sig = customTx.sig.get
modTx.V = sig.V
modTx.R = sig.R
modTx.S = sig.S
modTx
else:
# If a custom signature was not specified, simply sign the transaction again
let chainId = baseTx.chainId
signTransaction(modTx, vaultKey, chainId, eip155 = true)
proc modifyHash(x: common.Hash256): common.Hash256 =
result = x
result.data[^1] = byte(255 - x.data[^1].int)
proc generateInvalidPayload*(basePayload: ExecutableData,
payloadField: InvalidPayloadField,
vaultKey: PrivateKey): ExecutionPayload =
var customPayload: CustomPayload
case payloadField
of InvalidParentHash:
customPayload.parentHash = some(modifyHash(basePayload.parentHash))
of InvalidStateRoot:
customPayload.stateRoot = some(modifyHash(basePayload.stateRoot))
of InvalidReceiptsRoot:
customPayload.receiptsRoot = some(modifyHash(basePayload.receiptsRoot))
of InvalidNumber:
customPayload.number = some(basePayload.number - 1'u64)
of InvalidGasLimit:
customPayload.gasLimit = some(basePayload.gasLimit * 2)
of InvalidGasUsed:
customPayload.gasUsed = some(basePayload.gasUsed - 1)
of InvalidTimestamp:
customPayload.timestamp = some(basePayload.timestamp - 1.seconds)
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
customPayload.prevRandao = some(randomHash)
of RemoveTransaction:
let emptyTxs: seq[Transaction] = @[]
customPayload.transactions = some(emptyTxs)
of InvalidTransactionSignature,
InvalidTransactionNonce,
InvalidTransactionGas,
InvalidTransactionGasPrice,
InvalidTransactionValue:
doAssert(basePayload.transactions.len != 0, "No transactions available for modification")
var baseTx = basePayload.transactions[0]
var customTx: CustomTx
case payloadField
of InvalidTransactionSignature:
let sig = SignatureVal(
V: baseTx.V,
R: baseTx.R - 1.u256,
S: baseTx.S
)
customTx.sig = some(sig)
of InvalidTransactionNonce:
customTx.nonce = some(baseTx.nonce - 1)
of InvalidTransactionGas:
customTx.gasLimit = some(0.GasInt)
of InvalidTransactionGasPrice:
customTx.gasPrice = some(0.GasInt)
of InvalidTransactionValue:
# Vault account initially has 0x123450000000000000000, so this value should overflow
customTx.value = some(UInt256.fromHex("0x123450000000000000001"))
else:
discard
let modTx = customizeTx(baseTx, vaultKey, customTx)
customPayload.transactions = some(@[modTx])
customizePayload(basePayload, customPayload)
proc generateInvalidPayload*(basePayload: ExecutionPayload,
payloadField: InvalidPayloadField,
vaultKey = default(PrivateKey)): ExecutionPayload =
generateInvalidPayload(basePayload.toExecutableData, payloadField, vaultKey)
proc txInPayload*(payload: ExecutionPayload, txHash: common.Hash256): bool =
for txBytes in payload.transactions:
let currTx = rlp.decode(common.Blob txBytes, Transaction)
if rlpHash(currTx) == txHash:
return true