Add BeaconSync reorg tests (#1782)

* Add BeaconSync reorg tests

* Fix redefinition error in tx_sender.nim
This commit is contained in:
andri lim 2023-09-30 19:20:29 +07:00 committed by GitHub
parent 2f2c5127ea
commit 501d8a369a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 733 additions and 566 deletions

View File

@ -75,11 +75,6 @@ type
onSafeBlockChange * : proc(): bool {.gcsafe.} onSafeBlockChange * : proc(): bool {.gcsafe.}
onFinalizedBlockChange* : proc(): bool {.gcsafe.} onFinalizedBlockChange* : proc(): bool {.gcsafe.}
GetPayloadResponse = object
executionPayload: ExecutionPayload
blockValue: Option[UInt256]
blobsBundle: Option[BlobsBundleV1]
func latestPayloadNumber*(h: Table[uint64, ExecutionPayload]): uint64 = func latestPayloadNumber*(h: Table[uint64, ExecutionPayload]): uint64 =
result = 0'u64 result = 0'u64
for n, _ in h: for n, _ in h:
@ -99,17 +94,21 @@ func latestWithdrawalsIndex*(h: Table[uint64, ExecutionPayload]): uint64 =
func client(cl: CLMocker): RpcClient = func client(cl: CLMocker): RpcClient =
cl.clients.first.client cl.clients.first.client
proc init(cl: CLMocker, clients: ClientPool, com: CommonRef) = proc init(cl: CLMocker, eng: EngineEnv, com: CommonRef) =
cl.clients = clients cl.clients = ClientPool()
cl.clients.add eng
cl.com = com cl.com = com
cl.slotsToSafe = 1 cl.slotsToSafe = 1
cl.slotsToFinalized = 2 cl.slotsToFinalized = 2
cl.payloadProductionClientDelay = 1 cl.payloadProductionClientDelay = 1
cl.headerHistory[0] = com.genesisHeader() cl.headerHistory[0] = com.genesisHeader()
proc newClMocker*(clients: ClientPool, com: CommonRef): CLMocker = proc newClMocker*(eng: EngineEnv, com: CommonRef): CLMocker =
new result new result
result.init(clients, com) result.init(eng, com)
proc addEngine*(cl: CLMocker, eng: EngineEnv) =
cl.clients.add eng
proc waitForTTD*(cl: CLMocker): Future[bool] {.async.} = proc waitForTTD*(cl: CLMocker): Future[bool] {.async.} =
let ttd = cl.com.ttd() let ttd = cl.com.ttd()
@ -193,32 +192,6 @@ func isCancun(cl: CLMocker, timestamp: Quantity): bool =
let ts = fromUnix(timestamp.int64) let ts = fromUnix(timestamp.int64)
cl.com.isCancunOrLater(ts) cl.com.isCancunOrLater(ts)
func V1(attr: Option[PayloadAttributes]): Option[PayloadAttributesV1] =
if attr.isNone:
return none(PayloadAttributesV1)
some(attr.get.V1)
when false:
func V2(attr: Option[PayloadAttributes]): Option[PayloadAttributesV2] =
if attr.isNone:
return none(PayloadAttributesV2)
some(attr.get.V2)
func V3(attr: Option[PayloadAttributes]): Option[PayloadAttributesV3] =
if attr.isNone:
return none(PayloadAttributesV3)
some(attr.get.V3)
proc fcu(cl: CLMocker, version: Version,
update: ForkchoiceStateV1,
attr: Option[PayloadAttributes]):
Result[ForkchoiceUpdatedResponse, string] =
let client = cl.nextBlockProducer.client
case version
of Version.V1: client.forkchoiceUpdatedV1(update, attr.V1)
of Version.V2: client.forkchoiceUpdatedV2(update, attr)
of Version.V3: client.forkchoiceUpdatedV3(update, attr)
# Picks the next payload producer from the set of clients registered # Picks the next payload producer from the set of clients registered
proc pickNextPayloadProducer(cl: CLMocker): bool = proc pickNextPayloadProducer(cl: CLMocker): bool =
doAssert cl.clients.len != 0 doAssert cl.clients.len != 0
@ -274,7 +247,8 @@ proc requestNextPayload(cl: CLMocker): bool =
cl.prevRandaoHistory[number] = nextPrevRandao cl.prevRandaoHistory[number] = nextPrevRandao
let version = cl.latestPayloadAttributes.version let version = cl.latestPayloadAttributes.version
let res = cl.fcu(version, cl.latestForkchoice, some(cl.latestPayloadAttributes)) let client = cl.nextBlockProducer.client
let res = client.forkchoiceUpdated(version, cl.latestForkchoice, some(cl.latestPayloadAttributes))
if res.isErr: if res.isErr:
error "CLMocker: Could not send forkchoiceUpdated", version=version, msg=res.error error "CLMocker: Could not send forkchoiceUpdated", version=version, msg=res.error
return false return false
@ -299,32 +273,11 @@ proc getPayload(cl: CLMocker, payloadId: PayloadID): Result[GetPayloadResponse,
let ts = cl.latestPayloadAttributes.timestamp let ts = cl.latestPayloadAttributes.timestamp
let client = cl.nextBlockProducer.client let client = cl.nextBlockProducer.client
if cl.isCancun(ts): if cl.isCancun(ts):
let res = client.getPayloadV3(payloadId) client.getPayload(payloadId, Version.V3)
if res.isErr: elif cl.isShanghai(ts):
return err(res.error) client.getPayload(payloadId, Version.V2)
let x = res.get else:
return ok(GetPayloadResponse( client.getPayload(payloadId, Version.V1)
executionPayload: executionPayload(x.executionPayload),
blockValue: some(x.blockValue),
blobsBundle: some(x.blobsBundle)
))
if cl.isShanghai(ts):
let res = client.getPayloadV2(payloadId)
if res.isErr:
return err(res.error)
let x = res.get
return ok(GetPayloadResponse(
executionPayload: executionPayload(x.executionPayload),
blockValue: some(x.blockValue)
))
let res = client.getPayloadV1(payloadId)
if res.isErr:
return err(res.error)
return ok(GetPayloadResponse(
executionPayload: executionPayload(res.get),
))
proc getNextPayload(cl: CLMocker): bool = proc getNextPayload(cl: CLMocker): bool =
let res = cl.getPayload(cl.nextPayloadID) let res = cl.getPayload(cl.nextPayloadID)
@ -451,7 +404,8 @@ proc broadcastNextNewPayload(cl: CLMocker): bool =
proc broadcastForkchoiceUpdated(cl: CLMocker, proc broadcastForkchoiceUpdated(cl: CLMocker,
update: ForkchoiceStateV1): Result[ForkchoiceUpdatedResponse, string] = update: ForkchoiceStateV1): Result[ForkchoiceUpdatedResponse, string] =
let version = cl.latestExecutedPayload.version let version = cl.latestExecutedPayload.version
cl.fcu(version, update, none(PayloadAttributes)) let client = cl.nextBlockProducer.client
client.forkchoiceUpdated(version, update, none(PayloadAttributes))
proc broadcastLatestForkchoice(cl: CLMocker): bool = proc broadcastLatestForkchoice(cl: CLMocker): bool =
let res = cl.broadcastForkchoiceUpdated(cl.latestForkchoice) let res = cl.broadcastForkchoiceUpdated(cl.latestForkchoice)
@ -480,7 +434,6 @@ proc broadcastLatestForkchoice(cl: CLMocker): bool =
return true return true
proc produceSingleBlock*(cl: CLMocker, cb: BlockProcessCallbacks): bool {.gcsafe.} = proc produceSingleBlock*(cl: CLMocker, cb: BlockProcessCallbacks): bool {.gcsafe.} =
doAssert(cl.ttdReached) doAssert(cl.ttdReached)

View File

@ -19,9 +19,6 @@ type
slotsToFinalized*: int slotsToFinalized*: int
slotsToSafe*: int slotsToSafe*: int
const
prevRandaoContractAddr = hexToByteArray[20]("0000000000000000000000000000000000000316")
template testNP(res, cond: untyped, validHash = none(common.Hash256)) = template testNP(res, cond: untyped, validHash = none(common.Hash256)) =
testCond res.isOk testCond res.isOk
let s = res.get() let s = res.get()

View File

@ -11,7 +11,9 @@ import
import web3/engine_api as web3_engine_api import web3/engine_api as web3_engine_api
export execution_types export
execution_types,
rpcclient
type type
Hash256 = eth_types.Hash256 Hash256 = eth_types.Hash256
@ -43,20 +45,6 @@ proc forkchoiceUpdatedV1*(client: RpcClient,
wrapTrySimpleRes: wrapTrySimpleRes:
client.engine_forkchoiceUpdatedV1(update, payloadAttributes) client.engine_forkchoiceUpdatedV1(update, payloadAttributes)
#proc forkchoiceUpdatedV2*(client: RpcClient,
# update: ForkchoiceStateV1,
# payloadAttributes = none(PayloadAttributesV2)):
# Result[ForkchoiceUpdatedResponse, string] =
# wrapTrySimpleRes:
# client.engine_forkchoiceUpdatedV2(update, payloadAttributes)
#proc forkchoiceUpdatedV3*(client: RpcClient,
# update: ForkchoiceStateV1,
# payloadAttributes = none(PayloadAttributesV3)):
# Result[ForkchoiceUpdatedResponse, string] =
# wrapTrySimpleRes:
# client.engine_forkchoiceUpdatedV3(update, payloadAttributes)
proc forkchoiceUpdatedV2*(client: RpcClient, proc forkchoiceUpdatedV2*(client: RpcClient,
update: ForkchoiceStateV1, update: ForkchoiceStateV1,
payloadAttributes = none(PayloadAttributes)): payloadAttributes = none(PayloadAttributes)):
@ -83,6 +71,50 @@ proc getPayloadV3*(client: RpcClient, payloadId: PayloadID): Result[GetPayloadV3
wrapTrySimpleRes: wrapTrySimpleRes:
client.engine_getPayloadV3(payloadId) client.engine_getPayloadV3(payloadId)
proc getPayload*(client: RpcClient,
payloadId: PayloadID,
version: Version): Result[GetPayloadResponse, string] =
if version == Version.V3:
let x = client.getPayloadV3(payloadId).valueOr:
return err(error)
ok(GetPayloadResponse(
executionPayload: executionPayload(x.executionPayload),
blockValue: some(x.blockValue),
blobsBundle: some(x.blobsBundle)
))
elif version == Version.V2:
let x = client.getPayloadV2(payloadId).valueOr:
return err(error)
ok(GetPayloadResponse(
executionPayload: executionPayload(x.executionPayload),
blockValue: some(x.blockValue)
))
else:
let x = client.getPayloadV1(payloadId).valueOr:
return err(error)
ok(GetPayloadResponse(
executionPayload: executionPayload(x),
))
proc forkchoiceUpdated*(client: RpcClient,
update: ForkchoiceStateV1,
attr: PayloadAttributes):
Result[ForkchoiceUpdatedResponse, string] =
case attr.version
of Version.V1: client.forkchoiceUpdatedV1(update, some attr.V1)
of Version.V2: client.forkchoiceUpdatedV2(update, some attr)
of Version.V3: client.forkchoiceUpdatedV3(update, some attr)
proc forkchoiceUpdated*(client: RpcClient,
version: Version,
update: ForkchoiceStateV1,
attr = none(PayloadAttributes)):
Result[ForkchoiceUpdatedResponse, string] =
case version
of Version.V1: client.forkchoiceUpdatedV1(update, attr.V1)
of Version.V2: client.forkchoiceUpdatedV2(update, attr)
of Version.V3: client.forkchoiceUpdatedV3(update, attr)
proc newPayloadV1*(client: RpcClient, proc newPayloadV1*(client: RpcClient,
payload: ExecutionPayloadV1): payload: ExecutionPayloadV1):
Result[PayloadStatusV1, string] = Result[PayloadStatusV1, string] =
@ -110,6 +142,15 @@ proc newPayloadV3*(client: RpcClient,
wrapTrySimpleRes: wrapTrySimpleRes:
client.engine_newPayloadV3(payload, versionedHashes, parentBeaconBlockRoot) client.engine_newPayloadV3(payload, versionedHashes, parentBeaconBlockRoot)
proc newPayload*(client: RpcClient,
payload: ExecutionPayload,
version: Version):
Result[PayloadStatusV1, string] =
if version == Version.V1:
client.newPayloadV1(payload.V1)
else:
client.newPayloadV2(payload.V2)
proc exchangeCapabilities*(client: RpcClient, proc exchangeCapabilities*(client: RpcClient,
methods: seq[string]): methods: seq[string]):
Result[seq[string], string] = Result[seq[string], string] =

View File

@ -1,14 +1,13 @@
import import
std/[os, math], std/os,
eth/keys, eth/keys,
eth/p2p as eth_p2p, eth/p2p as eth_p2p,
chronos, chronos,
json_rpc/[rpcserver, rpcclient], json_rpc/[rpcserver, rpcclient],
stew/[results, byteutils], stew/[results],
../../../nimbus/[ ../../../nimbus/[
config, config,
constants, constants,
transaction,
core/sealer, core/sealer,
core/chain, core/chain,
core/tx_pool, core/tx_pool,
@ -20,25 +19,12 @@ import
beacon/beacon_engine, beacon/beacon_engine,
common common
], ],
../../../tests/test_helpers, ../../../tests/test_helpers
./engine_client
export export
results results
type type
BaseTx* = object of RootObj
recipient*: Option[EthAddress]
gasLimit* : GasInt
amount* : UInt256
payload* : seq[byte]
txType* : Option[TxType]
BigInitcodeTx* = object of BaseTx
initcodeLength*: int
padByte* : uint8
initcode* : seq[byte]
EngineEnv* = ref object EngineEnv* = ref object
conf : NimbusConf conf : NimbusConf
com : CommonRef com : CommonRef
@ -46,8 +32,6 @@ type
server : RpcHttpServer server : RpcHttpServer
sealer : SealingEngineRef sealer : SealingEngineRef
ttd : DifficultyInt ttd : DifficultyInt
tx : Transaction
nonce : uint64
client : RpcHttpClient client : RpcHttpClient
sync : BeaconSyncRef sync : BeaconSyncRef
@ -56,12 +40,8 @@ const
genesisFile = baseFolder & "/init/genesis.json" genesisFile = baseFolder & "/init/genesis.json"
sealerKey = baseFolder & "/init/sealer.key" sealerKey = baseFolder & "/init/sealer.key"
chainFolder = baseFolder & "/chains" chainFolder = baseFolder & "/chains"
# This is the account that sends vault funding transactions.
vaultAddr* = hexToByteArray[20]("0xcf49fda3be353c69b41ed96333cd24302da4556f")
jwtSecret = "0x7365637265747365637265747365637265747365637265747365637265747365" jwtSecret = "0x7365637265747365637265747365637265747365637265747365637265747365"
proc makeCom*(conf: NimbusConf): CommonRef = proc makeCom*(conf: NimbusConf): CommonRef =
CommonRef.new( CommonRef.new(
newCoreDbRef LegacyDbMemory, newCoreDbRef LegacyDbMemory,
@ -189,98 +169,3 @@ func node*(env: EngineEnv): ENode =
proc connect*(env: EngineEnv, node: ENode) = proc connect*(env: EngineEnv, node: ENode) =
waitFor env.node.connectToNode(node) waitFor env.node.connectToNode(node)
func gwei(n: int64): GasInt {.compileTime.} =
GasInt(n * (10 ^ 9))
proc getTxType(tc: BaseTx, nonce: uint64): TxType =
if tc.txType.isNone:
if nonce mod 2 == 0:
TxLegacy
else:
TxEIP1559
else:
tc.txType.get
proc makeTx*(env: EngineEnv, vaultKey: PrivateKey, tc: BaseTx, nonce: AccountNonce): Transaction =
const
gasPrice = 30.gwei
gasTipPrice = 1.gwei
gasFeeCap = gasPrice
gasTipCap = gasTipPrice
let chainId = env.conf.networkParams.config.chainId
let txType = tc.getTxType(nonce)
# Build the transaction depending on the specified type
let tx = if txType == TxLegacy:
Transaction(
txType : TxLegacy,
nonce : nonce,
to : tc.recipient,
value : tc.amount,
gasLimit: tc.gasLimit,
gasPrice: gasPrice,
payload : tc.payload
)
else:
Transaction(
txType : TxEIP1559,
nonce : nonce,
gasLimit: tc.gasLimit,
maxFee : gasFeeCap,
maxPriorityFee: gasTipCap,
to : tc.recipient,
value : tc.amount,
payload : tc.payload,
chainId : chainId
)
signTransaction(tx, vaultKey, chainId, eip155 = true)
proc makeTx*(env: EngineEnv, vaultKey: PrivateKey, tc: var BigInitcodeTx, nonce: AccountNonce): Transaction =
if tc.payload.len == 0:
# Prepare initcode payload
if tc.initcode.len != 0:
doAssert(tc.initcode.len <= tc.initcodeLength, "invalid initcode (too big)")
tc.payload = tc.initcode
while tc.payload.len < tc.initcodeLength:
tc.payload.add tc.padByte
doAssert(tc.recipient.isNone, "invalid configuration for big contract tx creator")
env.makeTx(vaultKey, tc.BaseTx, nonce)
proc sendNextTx*(env: EngineEnv, vaultKey: PrivateKey, tc: BaseTx): bool =
env.tx = env.makeTx(vaultKey, tc, env.nonce)
inc env.nonce
let rr = env.client.sendTransaction(env.tx)
if rr.isErr:
error "Unable to send transaction", msg=rr.error
return false
return true
proc sendTx*(env: EngineEnv, vaultKey: PrivateKey, tc: BaseTx, nonce: AccountNonce): bool =
env.tx = env.makeTx(vaultKey, tc, nonce)
let rr = env.client.sendTransaction(env.tx)
if rr.isErr:
error "Unable to send transaction", msg=rr.error
return false
return true
proc sendTx*(env: EngineEnv, vaultKey: PrivateKey, tc: BigInitcodeTx, nonce: AccountNonce): bool =
env.tx = env.makeTx(vaultKey, tc, nonce)
let rr = env.client.sendTransaction(env.tx)
if rr.isErr:
error "Unable to send transaction", msg=rr.error
return false
return true
proc sendTx*(env: EngineEnv, tx: Transaction): bool =
env.tx = tx
let rr = env.client.sendTransaction(env.tx)
if rr.isErr:
error "Unable to send transaction", msg=rr.error
return false
return true

View File

@ -7,13 +7,15 @@ import
./clmock, ./clmock,
./engine_client, ./engine_client,
./client_pool, ./client_pool,
./engine_env ./engine_env,
./tx_sender
export export
clmock, clmock,
engine_client, engine_client,
client_pool, client_pool,
engine_env engine_env,
tx_sender
type type
TestEnv* = ref object TestEnv* = ref object
@ -23,26 +25,18 @@ type
port : int port : int
rpcPort : int rpcPort : int
clients : ClientPool clients : ClientPool
sender : TxSender
clMock* : CLMocker clMock* : CLMocker
vaultKey : PrivateKey
const
vaultKeyHex = "63b508a03c3b5937ceb903af8b1b0c191012ef6eb7e9c3fb7afa94e5d214d376"
proc makeEnv(conf: NimbusConf): TestEnv = proc makeEnv(conf: NimbusConf): TestEnv =
let env = TestEnv( TestEnv(
conf : conf, conf : conf,
port : 30303, port : 30303,
rpcPort: 8545, rpcPort: 8545,
clients: ClientPool(), clients: ClientPool(),
sender : TxSender.new(conf.networkParams),
) )
env.vaultKey = PrivateKey.fromHex(vaultKeyHex).valueOr:
echo error
quit(QuitFailure)
env
proc addEngine(env: TestEnv, conf: var NimbusConf): EngineEnv = proc addEngine(env: TestEnv, conf: var NimbusConf): EngineEnv =
conf.tcpPort = Port env.port conf.tcpPort = Port env.port
conf.udpPort = Port env.port conf.udpPort = Port env.port
@ -85,47 +79,58 @@ func engine*(env: TestEnv): EngineEnv =
env.clients.first env.clients.first
proc setupCLMock*(env: TestEnv) = proc setupCLMock*(env: TestEnv) =
env.clmock = newCLMocker(env.clients, env.engine.com) env.clmock = newCLMocker(env.engine, env.engine.com)
proc addEngine*(env: TestEnv): EngineEnv = proc addEngine*(env: TestEnv, addToCL: bool = true): EngineEnv =
doAssert(env.clMock.isNil.not)
var conf = env.conf # clone the conf var conf = env.conf # clone the conf
let eng = env.addEngine(conf) let eng = env.addEngine(conf)
eng.connect(env.engine.node) eng.connect(env.engine.node)
if addToCL:
env.clMock.addEngine(eng)
eng eng
proc makeTx*(env: TestEnv, eng: EngineEnv, tc: BaseTx, nonce: AccountNonce): Transaction = proc makeTx*(env: TestEnv, tc: BaseTx, nonce: AccountNonce): Transaction =
eng.makeTx(env.vaultKey, tc, nonce) env.sender.makeTx(tc, nonce)
proc makeTx*(env: TestEnv, eng: EngineEnv, tc: var BigInitcodeTx, nonce: AccountNonce): Transaction = proc makeTx*(env: TestEnv, tc: BigInitcodeTx, nonce: AccountNonce): Transaction =
eng.makeTx(env.vaultKey, tc, nonce) env.sender.makeTx(tc, nonce)
proc makeTxs*(env: TestEnv, tc: BaseTx, num: int): seq[Transaction] =
result = newSeqOfCap[Transaction](num)
for _ in 0..<num:
result.add env.sender.makeNextTx(tc)
proc sendNextTx*(env: TestEnv, eng: EngineEnv, tc: BaseTx): bool = proc sendNextTx*(env: TestEnv, eng: EngineEnv, tc: BaseTx): bool =
eng.sendNextTx(env.vaultKey, tc) env.sender.sendNextTx(eng.client, tc)
proc sendTx*(env: TestEnv, eng: EngineEnv, tc: BaseTx, nonce: AccountNonce): bool = proc sendTx*(env: TestEnv, eng: EngineEnv, tc: BaseTx, nonce: AccountNonce): bool =
eng.sendTx(env.vaultKey, tc, nonce) env.sender.sendTx(eng.client, tc, nonce)
proc sendTx*(env: TestEnv, eng: EngineEnv, tc: BigInitcodeTx, nonce: AccountNonce): bool = proc sendTx*(env: TestEnv, eng: EngineEnv, tc: BigInitcodeTx, nonce: AccountNonce): bool =
eng.sendTx(env.vaultKey, tc, nonce) env.sender.sendTx(eng.client, tc, nonce)
proc sendTxs*(env: TestEnv, eng: EngineEnv, txs: openArray[Transaction]): bool =
proc makeTx*(env: TestEnv, tc: BaseTx, nonce: AccountNonce): Transaction = for tx in txs:
env.engine.makeTx(env.vaultKey, tc, nonce) if not sendTx(eng.client, tx):
return false
proc makeTx*(env: TestEnv, tc: var BigInitcodeTx, nonce: AccountNonce): Transaction = true
env.engine.makeTx(env.vaultKey, tc, nonce)
proc sendNextTx*(env: TestEnv, tc: BaseTx): bool = proc sendNextTx*(env: TestEnv, tc: BaseTx): bool =
env.engine.sendNextTx(env.vaultKey, tc) let client = env.engine.client
env.sender.sendNextTx(client, tc)
proc sendTx*(env: TestEnv, tc: BaseTx, nonce: AccountNonce): bool = proc sendTx*(env: TestEnv, tc: BaseTx, nonce: AccountNonce): bool =
env.engine.sendTx(env.vaultKey, tc, nonce) let client = env.engine.client
env.sender.sendTx(client, tc, nonce)
proc sendTx*(env: TestEnv, tc: BigInitcodeTx, nonce: AccountNonce): bool = proc sendTx*(env: TestEnv, tc: BigInitcodeTx, nonce: AccountNonce): bool =
env.engine.sendTx(env.vaultKey, tc, nonce) let client = env.engine.client
env.sender.sendTx(client, tc, nonce)
proc sendTx*(env: TestEnv, tx: Transaction): bool = proc sendTx*(env: TestEnv, tx: Transaction): bool =
env.engine.sendTx(tx) let client = env.engine.client
sendTx(client, tx)
proc verifyPoWProgress*(env: TestEnv, lastBlockHash: common.Hash256): bool = proc verifyPoWProgress*(env: TestEnv, lastBlockHash: common.Hash256): bool =
let res = waitFor env.client.verifyPoWProgress(lastBlockHash) let res = waitFor env.client.verifyPoWProgress(lastBlockHash)

View File

@ -0,0 +1,216 @@
import
std/[tables, math],
eth/keys,
stew/endians2,
nimcrypto/sha2,
chronicles,
./engine_client,
../../../nimbus/transaction,
../../../nimbus/common
type
BaseTx* = object of RootObj
recipient*: Option[EthAddress]
gasLimit* : GasInt
amount* : UInt256
payload* : seq[byte]
txType* : Option[TxType]
BigInitcodeTx* = object of BaseTx
initcodeLength*: int
padByte* : uint8
initcode* : seq[byte]
TestAccount = object
key : PrivateKey
address: EthAddress
index : int
TxSender* = ref object
accounts: seq[TestAccount]
nonceMap: Table[EthAddress, uint64]
txSent : int
chainId : ChainID
MakeTxParams* = object
chainId*: ChainID
key* : PrivateKey
nonce* : AccountNonce
const
TestAccountCount = 1000
func toAddress(key: PrivateKey): EthAddress =
toKeyPair(key).pubkey.toCanonicalAddress()
proc createAccount(idx: int): TestAccount =
let
seed = toBytesBE(idx.uint64)
seedHash = sha256.digest(seed)
result.index = idx
result.key = PrivateKey.fromRaw(seedHash.data).valueOr:
echo error
quit(QuitFailure)
result.address = toAddress(result.key)
proc createAccounts(sender: TxSender) =
for i in 0..<TestAccountCount:
sender.accounts.add createAccount(i.int)
proc getNextAccount(sender: TxSender): TestAccount =
sender.accounts[sender.txSent mod sender.accounts.len]
proc getNextNonce(sender: TxSender, address: EthAddress): uint64 =
let nonce = sender.nonceMap.getOrDefault(address, 0'u64)
sender.nonceMap[address] = nonce + 1
nonce
proc fillBalance(sender: TxSender, params: NetworkParams) =
for x in sender.accounts:
params.genesis.alloc[x.address] = GenesisAccount(
balance: UInt256.fromHex("0x123450000000000000000"),
)
proc new*(_: type TxSender, params: NetworkParams): TxSender =
result = TxSender(chainId: params.config.chainId)
result.createAccounts()
result.fillBalance(params)
func gwei(n: int64): GasInt {.compileTime.} =
GasInt(n * (10 ^ 9))
proc getTxType(tc: BaseTx, nonce: uint64): TxType =
if tc.txType.isNone:
if nonce mod 2 == 0:
TxLegacy
else:
TxEIP1559
else:
tc.txType.get
proc makeTx(params: MakeTxParams, tc: BaseTx): Transaction =
const
gasPrice = 30.gwei
gasTipPrice = 1.gwei
gasFeeCap = gasPrice
gasTipCap = gasTipPrice
let txType = tc.getTxType(params.nonce)
# 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
)
signTransaction(tx, params.key, params.chainId, eip155 = true)
proc makeTx(params: MakeTxParams, tc: BigInitcodeTx): Transaction =
var tx = tc
if tx.payload.len == 0:
# Prepare initcode payload
if tx.initcode.len != 0:
doAssert(tx.initcode.len <= tx.initcodeLength, "invalid initcode (too big)")
tx.payload = tx.initcode
while tx.payload.len < tx.initcodeLength:
tx.payload.add tx.padByte
doAssert(tx.recipient.isNone, "invalid configuration for big contract tx creator")
params.makeTx(tx.BaseTx)
proc makeTx*(sender: TxSender, tc: BaseTx, nonce: AccountNonce): Transaction =
let acc = sender.getNextAccount()
let params = MakeTxParams(
chainId: sender.chainId,
key: acc.key,
nonce: nonce
)
params.makeTx(tc)
proc makeTx*(sender: TxSender, tc: BigInitcodeTx, nonce: AccountNonce): Transaction =
let acc = sender.getNextAccount()
let params = MakeTxParams(
chainId: sender.chainId,
key: acc.key,
nonce: nonce
)
params.makeTx(tc)
proc makeNextTx*(sender: TxSender, tc: BaseTx): Transaction =
let
acc = sender.getNextAccount()
nonce = sender.getNextNonce(acc.address)
params = MakeTxParams(
chainId: sender.chainId,
key: acc.key,
nonce: nonce
)
params.makeTx(tc)
proc sendNextTx*(sender: TxSender, client: RpcClient, tc: BaseTx): bool =
let tx = sender.makeNextTx(tc)
let rr = client.sendTransaction(tx)
if rr.isErr:
error "Unable to send transaction", msg=rr.error
return false
return true
proc sendTx*(sender: TxSender, client: RpcClient, tc: BaseTx, nonce: AccountNonce): bool =
let
acc = sender.getNextAccount()
params = MakeTxParams(
chainId: sender.chainId,
key: acc.key,
nonce: nonce
)
tx = params.makeTx(tc)
let rr = client.sendTransaction(tx)
if rr.isErr:
error "Unable to send transaction", msg=rr.error
return false
return true
proc sendTx*(sender: TxSender, client: RpcClient, tc: BigInitcodeTx, nonce: AccountNonce): bool =
let
acc = sender.getNextAccount()
params = MakeTxParams(
chainId: sender.chainId,
key: acc.key,
nonce: nonce
)
tx = params.makeTx(tc)
let rr = client.sendTransaction(tx)
if rr.isErr:
error "Unable to send transaction", msg=rr.error
return false
return true
proc sendTx*(client: RpcClient, tx: Transaction): bool =
let rr = client.sendTransaction(tx)
if rr.isErr:
error "Unable to send transaction", msg=rr.error
return false
return true

View File

@ -1,6 +1,7 @@
import import
std/[options, typetraits, strutils], std/[options, typetraits, strutils],
eth/common, eth/common,
stew/byteutils,
web3/ethtypes, web3/ethtypes,
web3/engine_api_types, web3/engine_api_types,
../../../nimbus/beacon/execution_types, ../../../nimbus/beacon/execution_types,
@ -19,6 +20,7 @@ type
const const
DefaultTimeout* = 60 # seconds DefaultTimeout* = 60 # seconds
DefaultSleep* = 1 DefaultSleep* = 1
prevRandaoContractAddr* = hexToByteArray[20]("0000000000000000000000000000000000000316")
template testCond*(expr: untyped) = template testCond*(expr: untyped) =
if not (expr): if not (expr):
@ -85,6 +87,16 @@ template expectStatus*(res, cond: untyped) =
testCond s.status == PayloadExecutionStatus.cond: testCond s.status == PayloadExecutionStatus.cond:
error "Unexpected newPayload status", expect=PayloadExecutionStatus.cond, get=s.status error "Unexpected newPayload status", expect=PayloadExecutionStatus.cond, get=s.status
template expectStatusEither*(res, cond1, cond2: untyped) =
testCond res.isOk:
error "Unexpected newPayload error", msg=res.error
let s = res.get()
testCond s.status == PayloadExecutionStatus.cond1 or s.status == PayloadExecutionStatus.cond2:
error "Unexpected newPayload status",
expect1=PayloadExecutionStatus.cond1,
expect2=PayloadExecutionStatus.cond2,
get=s.status
template expectWithdrawalsRoot*(res: untyped, h: common.BlockHeader, wdRoot: Option[common.Hash256]) = template expectWithdrawalsRoot*(res: untyped, h: common.BlockHeader, wdRoot: Option[common.Hash256]) =
testCond res.isOk: testCond res.isOk:
error "Unexpected error", msg=res.error error "Unexpected error", msg=res.error

View File

@ -3,7 +3,7 @@ import
withdrawals/wd_block_value_spec, withdrawals/wd_block_value_spec,
withdrawals/wd_max_init_code_spec, withdrawals/wd_max_init_code_spec,
#withdrawals/wd_payload_body_spec, #withdrawals/wd_payload_body_spec,
#withdrawals/wd_reorg_spec, withdrawals/wd_reorg_spec,
withdrawals/wd_sync_spec, withdrawals/wd_sync_spec,
./types, ./types,
./test_env ./test_env
@ -24,7 +24,72 @@ proc specExecute[T](ws: BaseSpec): bool =
let wdTestList* = [ let wdTestList* = [
#Re-Org tests #Re-Org tests
#[TestDesc( TestDesc(
name: "Withdrawals Fork on Block 1 - 8 Block Re-Org, Sync",
about: "Tests a 8 block re-org using NewPayload. Re-org does not change withdrawals fork height",
run: specExecute[ReorgSpec],
spec: ReorgSpec(
slotsToSafe: u256(32),
slotsToFinalized: u256(64),
timeoutSeconds: 300,
wdForkHeight: 1, # Genesis is Pre-Withdrawals
wdBlockCount: MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK,
wdPerBlock: MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK,
reOrgBlockCount: 8,
reOrgViaSync: true,
)),
TestDesc(
name: "Withdrawals Fork on Block 8 - 10 Block Re-Org Sync",
about: " Tests a 10 block re-org using sync",
# Re-org does not change withdrawals fork height, but changes
# the payload at the height of the fork
run: specExecute[ReorgSpec],
spec: ReorgSpec(
slotsToSafe: u256(32),
slotsToFinalized: u256(64),
timeoutSeconds: 300,
wdForkHeight: 8, # Genesis is Pre-Withdrawals
wdBlockCount: 8,
wdPerBlock: MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK,
reOrgBlockCount: 10,
reOrgViaSync: true,
)),
TestDesc(
name: "Withdrawals Fork on Canonical Block 8 / Side Block 7 - 10 Block Re-Org Sync",
about: "Tests a 10 block re-org using sync",
# Sidechain reaches withdrawals fork at a lower block height
# than the canonical chain
run: specExecute[ReorgSpec],
spec: ReorgSpec(
slotsToSafe: u256(32),
slotsToFinalized: u256(64),
timeoutSeconds: 300,
wdForkHeight: 8, # Genesis is Pre-Withdrawals
wdBlockCount: 8,
wdPerBlock: MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK,
reOrgBlockCount: 10,
reOrgViaSync: true,
sidechaintimeIncrements: 2,
)),
TestDesc(
name: "Withdrawals Fork on Canonical Block 8 / Side Block 9 - 10 Block Re-Org Sync",
about: "Tests a 10 block re-org using sync",
# Sidechain reaches withdrawals fork at a higher block height
# than the canonical chain
run: specExecute[ReorgSpec],
spec: ReorgSpec(
slotsToSafe: u256(32),
slotsToFinalized: u256(64),
timeoutSeconds: 300,
wdForkHeight: 8, # Genesis is Pre-Withdrawals
wdBlockCount: 8,
wdPerBlock: MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK,
timeIncrements: 2,
reOrgBlockCount: 10,
reOrgViaSync: true,
sidechaintimeIncrements: 1,
)),
TestDesc(
name: "Withdrawals Fork on Block 1 - 1 Block Re-Org", name: "Withdrawals Fork on Block 1 - 1 Block Re-Org",
about: "Tests a simple 1 block re-org", about: "Tests a simple 1 block re-org",
run: specExecute[ReorgSpec], run: specExecute[ReorgSpec],
@ -52,20 +117,6 @@ let wdTestList* = [
reOrgBlockCount: 8, reOrgBlockCount: 8,
reOrgViaSync: false, reOrgViaSync: false,
)), )),
TestDesc(
name: "Withdrawals Fork on Block 1 - 8 Block Re-Org, Sync",
about: "Tests a 8 block re-org using NewPayload. Re-org does not change withdrawals fork height",
run: specExecute[ReorgSpec],
spec: ReorgSpec(
slotsToSafe: u256(32),
slotsToFinalized: u256(64),
timeoutSeconds: 300,
wdForkHeight: 1, # Genesis is Pre-Withdrawals
wdBlockCount: MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK,
wdPerBlock: MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK,
reOrgBlockCount: 8,
reOrgViaSync: true,
)),
TestDesc( TestDesc(
name: "Withdrawals Fork on Block 8 - 10 Block Re-Org NewPayload", name: "Withdrawals Fork on Block 8 - 10 Block Re-Org NewPayload",
about: "Tests a 10 block re-org using NewPayload\n" & about: "Tests a 10 block re-org using NewPayload\n" &
@ -82,22 +133,6 @@ let wdTestList* = [
reOrgBlockCount: 10, reOrgBlockCount: 10,
reOrgViaSync: false, reOrgViaSync: false,
)), )),
TestDesc(
name: "Withdrawals Fork on Block 8 - 10 Block Re-Org Sync",
about: " Tests a 10 block re-org using sync",
# Re-org does not change withdrawals fork height, but changes
# the payload at the height of the fork
run: specExecute[ReorgSpec],
spec: ReorgSpec(
slotsToSafe: u256(32),
slotsToFinalized: u256(64),
timeoutSeconds: 300,
wdForkHeight: 8, # Genesis is Pre-Withdrawals
wdBlockCount: 8,
wdPerBlock: MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK,
reOrgBlockCount: 10,
reOrgViaSync: true,
)),
TestDesc( TestDesc(
name: "Withdrawals Fork on Canonical Block 8 / Side Block 7 - 10 Block Re-Org", name: "Withdrawals Fork on Canonical Block 8 / Side Block 7 - 10 Block Re-Org",
about: "Tests a 10 block re-org using NewPayload", about: "Tests a 10 block re-org using NewPayload",
@ -115,23 +150,6 @@ let wdTestList* = [
reOrgViaSync: false, reOrgViaSync: false,
sidechaintimeIncrements: 2, sidechaintimeIncrements: 2,
)), )),
TestDesc(
name: "Withdrawals Fork on Canonical Block 8 / Side Block 7 - 10 Block Re-Org Sync",
about: "Tests a 10 block re-org using sync",
# Sidechain reaches withdrawals fork at a lower block height
# than the canonical chain
run: specExecute[ReorgSpec],
spec: ReorgSpec(
slotsToSafe: u256(32),
slotsToFinalized: u256(64),
timeoutSeconds: 300,
wdForkHeight: 8, # Genesis is Pre-Withdrawals
wdBlockCount: 8,
wdPerBlock: MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK,
reOrgBlockCount: 10,
reOrgViaSync: true,
sidechaintimeIncrements: 2,
)),
TestDesc( TestDesc(
name: "Withdrawals Fork on Canonical Block 8 / Side Block 9 - 10 Block Re-Org", name: "Withdrawals Fork on Canonical Block 8 / Side Block 9 - 10 Block Re-Org",
about: "Tests a 10 block re-org using NewPayload", about: "Tests a 10 block re-org using NewPayload",
@ -150,24 +168,6 @@ let wdTestList* = [
reOrgViaSync: false, reOrgViaSync: false,
sidechaintimeIncrements: 1, sidechaintimeIncrements: 1,
)), )),
TestDesc(
name: "Withdrawals Fork on Canonical Block 8 / Side Block 9 - 10 Block Re-Org Sync",
about: "Tests a 10 block re-org using sync",
# Sidechain reaches withdrawals fork at a higher block height
# than the canonical chain
run: specExecute[ReorgSpec],
spec: ReorgSpec(
slotsToSafe: u256(32),
slotsToFinalized: u256(64),
timeoutSeconds: 300,
wdForkHeight: 8, # Genesis is Pre-Withdrawals
wdBlockCount: 8,
wdPerBlock: MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK,
timeIncrements: 2,
reOrgBlockCount: 10,
reOrgViaSync: true,
sidechaintimeIncrements: 1,
)),]#
# Sync Tests # Sync Tests
TestDesc( TestDesc(

View File

@ -32,8 +32,8 @@ type
skipBaseVerifications*: bool # For code reuse of the base spec procedure skipBaseVerifications*: bool # For code reuse of the base spec procedure
WithdrawalsForBlock = object WithdrawalsForBlock = object
wds: seq[Withdrawal] wds*: seq[Withdrawal]
nextIndex: int nextIndex*: int
const const
GenesisTimestamp = 0x1234 GenesisTimestamp = 0x1234
@ -46,13 +46,13 @@ const
] ]
# Get the per-block timestamp increments configured for this test # Get the per-block timestamp increments configured for this test
func getBlockTimeIncrements(ws: WDBaseSpec): int = func getBlockTimeIncrements*(ws: WDBaseSpec): int =
if ws.timeIncrements == 0: if ws.timeIncrements == 0:
return 1 return 1
ws.timeIncrements ws.timeIncrements
# Timestamp delta between genesis and the withdrawals fork # Timestamp delta between genesis and the withdrawals fork
func getWithdrawalsGenesisTimeDelta(ws: WDBaseSpec): int = func getWithdrawalsGenesisTimeDelta*(ws: WDBaseSpec): int =
ws.wdForkHeight * ws.getBlockTimeIncrements() ws.wdForkHeight * ws.getBlockTimeIncrements()
# Calculates Shanghai fork timestamp given the amount of blocks that need to be # Calculates Shanghai fork timestamp given the amount of blocks that need to be
@ -93,7 +93,7 @@ func addUnconditionalBytecode(g: Genesis, start, stop: UInt256) =
) )
acc = acc + 1 acc = acc + 1
func getWithdrawableAccountCount(ws: WDBaseSpec):int = func getWithdrawableAccountCount*(ws: WDBaseSpec):int =
if ws.wdAbleAccountCount == 0: if ws.wdAbleAccountCount == 0:
# Withdraw to MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK accounts by default # Withdraw to MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK accounts by default
return MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK return MAINNET_MAX_WITHDRAWAL_COUNT_PER_BLOCK
@ -163,7 +163,7 @@ func getGenesis*(ws: WDBaseSpec, param: NetworkParams): NetworkParams =
param param
func getTransactionCountPerPayload(ws: WDBaseSpec): int = func getTransactionCountPerPayload*(ws: WDBaseSpec): int =
ws.txPerBlock.get(16) ws.txPerBlock.get(16)
proc verifyContractsStorage(ws: WDBaseSpec, env: TestEnv): Result[void, string] = proc verifyContractsStorage(ws: WDBaseSpec, env: TestEnv): Result[void, string] =
@ -201,11 +201,11 @@ func getPreWithdrawalsBlockCount*(ws: WDBaseSpec): int =
ws.wdForkHeight - 1 ws.wdForkHeight - 1
# Number of payloads to be produced (pre and post withdrawals) during the entire test # Number of payloads to be produced (pre and post withdrawals) during the entire test
func getTotalPayloadCount(ws: WDBaseSpec): int = func getTotalPayloadCount*(ws: WDBaseSpec): int =
ws.getPreWithdrawalsBlockCount() + ws.wdBlockCount ws.getPreWithdrawalsBlockCount() + ws.wdBlockCount
# Generates a list of withdrawals based on current configuration # Generates a list of withdrawals based on current configuration
func generateWithdrawalsForBlock(ws: WDBaseSpec, nextIndex: int, startAccount: UInt256): WithdrawalsForBlock = func generateWithdrawalsForBlock*(ws: WDBaseSpec, nextIndex: int, startAccount: UInt256): WithdrawalsForBlock =
let let
differentAccounts = ws.getWithdrawableAccountCount() differentAccounts = ws.getWithdrawableAccountCount()

View File

@ -1,11 +1,15 @@
import import
std/tables,
stint, stint,
chronos, chronos,
chronicles, chronicles,
eth/common,
./wd_base_spec, ./wd_base_spec,
./wd_history,
../test_env, ../test_env,
../engine_client, ../engine_client,
../types ../types,
../../../nimbus/beacon/web3_eth_conv
# Withdrawals re-org spec: # Withdrawals re-org spec:
# Specifies a withdrawals test where the withdrawals re-org can happen # Specifies a withdrawals test where the withdrawals re-org can happen
@ -13,311 +17,315 @@ import
# withdrawals block. # withdrawals block.
type type
ReorgSpec* = ref object of WDBaseSpec ReorgSpec* = ref object of WDBaseSpec
reOrgBlockCount* : uint64 # How many blocks the re-org will replace, including the head # How many blocks the re-org will replace, including the head
reOrgViaSync* : bool # Whether the client should fetch the sidechain by syncing from the secondary client reOrgBlockCount* : int
sidechainTimeIncrements*: uint64 # Whether the client should fetch the sidechain by syncing from the secondary client
reOrgViaSync* : bool
sidechainTimeIncrements*: int
slotsToSafe* : UInt256 slotsToSafe* : UInt256
slotsToFinalized* : UInt256 slotsToFinalized* : UInt256
timeoutSeconds* : int timeoutSeconds* : int
#[
func (ws *WithdrawalsReorgSpec) GetSidechainSplitHeight() uint64 {
if ws.ReOrgBlockCount > ws.getTotalPayloadCount() {
panic("invalid payload/re-org configuration")
return ws.getTotalPayloadCount() + 1 - ws.ReOrgBlockCount Sidechain = ref object
startAccount: UInt256
nextIndex : int
wdHistory : WDHistory
sidechain : Table[uint64, ExecutionPayload]
payloadId : PayloadID
height : uint64
attr : Option[PayloadAttributes]
func (ws *WithdrawalsReorgSpec) GetSidechainBlockTimeIncrements() uint64 { Canonical = ref object
if ws.SidechainTimeIncrements == 0 { startAccount: UInt256
nextIndex : int
proc getSidechainSplitHeight(ws: ReorgSpec): int =
doAssert(ws.reOrgBlockCount <= ws.getTotalPayloadCount())
return ws.getTotalPayloadCount() + 1 - ws.reOrgBlockCount
proc getSidechainBlockTimeIncrements(ws: ReorgSpec): int=
if ws.sidechainTimeIncrements == 0:
return ws.getBlockTimeIncrements() return ws.getBlockTimeIncrements()
ws.sidechainTimeIncrements
return ws.SidechainTimeIncrements proc getSidechainWdForkHeight(ws: ReorgSpec): int =
if ws.getSidechainBlockTimeIncrements() != ws.getBlockTimeIncrements():
# Block timestamp increments in both chains are different so need to
# calculate different heights, only if split happens before fork.
# We cannot split by having two different genesis blocks.
doAssert(ws.getSidechainSplitHeight() != 0, "invalid sidechain split height")
func (ws *WithdrawalsReorgSpec) GetSidechainWithdrawalsForkHeight() uint64 { if ws.getSidechainSplitHeight() <= ws.wdForkHeight:
if ws.getSidechainBlockTimeIncrements() != ws.getBlockTimeIncrements() {
# Block timestamp increments in both chains are different so need to calculate different heights, only if split happens before fork
if ws.getSidechainSplitHeight() == 0 {
# We cannot split by having two different genesis blocks.
panic("invalid sidechain split height")
if ws.getSidechainSplitHeight() <= ws.WithdrawalsForkHeight {
# We need to calculate the height of the fork on the sidechain # We need to calculate the height of the fork on the sidechain
sidechainSplitBlockTimestamp := ((ws.getSidechainSplitHeight() - 1) * ws.getBlockTimeIncrements()) let sidechainSplitBlocktimestamp = (ws.getSidechainSplitHeight() - 1) * ws.getBlockTimeIncrements()
remainingTime := (ws.getWithdrawalsGenesisTimeDelta() - sidechainSplitBlockTimestamp) let remainingTime = ws.getWithdrawalsGenesisTimeDelta() - sidechainSplitBlocktimestamp
if remainingTime == 0 { if remainingTime == 0 :
return ws.getSidechainSplitHeight() return ws.getSidechainSplitHeight()
return ((remainingTime - 1) / ws.SidechainTimeIncrements) + ws.getSidechainSplitHeight() return ((remainingTime - 1) div ws.sidechainTimeIncrements) + ws.getSidechainSplitHeight()
return ws.WithdrawalsForkHeight return ws.wdForkHeight
]#
proc execute*(ws: ReorgSpec, t: TestEnv): bool = proc execute*(ws: ReorgSpec, env: TestEnv): bool =
testCond waitFor t.clMock.waitForTTD() result = true
testCond waitFor env.clMock.waitForTTD()
return true
#[
# Spawn a secondary client which will produce the sidechain # Spawn a secondary client which will produce the sidechain
secondaryEngine, err := hive_rpc.HiveRPCEngineStarter{}.StartClient(t.T, t.TestContext, t.Genesis, t.ClientParams, t.ClientFiles, t.Engine) let sec = env.addEngine(addToCL = false)
if err != nil {
error "Unable to spawn a secondary client: %v", t.TestName, err)
}
secondaryEngineTest := test.NewTestEngineClient(t, secondaryEngine)
# t.clMock.AddEngineClient(secondaryEngine)
var ( var
canonicalStartAccount = big.NewInt(0x1000) canonical = Canonical(
canonicalNextIndex = uint64(0) startAccount: u256(0x1000),
sidechainStartAccount = new(big.Int).SetBit(common.Big0, 160, 1) nextIndex : 0,
sidechainNextIndex = uint64(0) )
sidechainwdHistory = make(wdHistory) sidechain = Sidechain(
sidechain = make(map[uint64]*typ.ExecutableData) startAccount: 1.u256 shl 160,
sidechainPayloadId *beacon.PayloadID nextIndex : 0,
) wdHistory : WDHistory(),
sidechain : initTable[uint64, ExecutionPayload]()
)
# Sidechain withdraws on the max account value range 0xffffffffffffffffffffffffffffffffffffffff # Sidechain withdraws on the max account value range 0xffffffffffffffffffffffffffffffffffffffff
sidechainStartAccount.Sub(sidechainStartAccount, big.NewInt(int64(ws.getWithdrawableAccountCount())+1)) sidechain.startAccount -= u256(ws.getWithdrawableAccountCount()+1)
t.clMock.ProduceBlocks(int(ws.getPreWithdrawalsBlockCount()+ws.WithdrawalsBlockCount), clmock.BlockProcessCallbacks{ let numBlocks = ws.getPreWithdrawalsBlockCount()+ws.wdBlockCount
OnPayloadProducerSelected: proc(): bool = let pbRes = env.clMock.produceBlocks(numBlocks, BlockProcessCallbacks(
t.clMock.NextWithdrawals = nil onPayloadProducerSelected: proc(): bool =
env.clMock.nextWithdrawals = none(seq[WithdrawalV1])
if t.clMock.CurrentPayloadNumber >= ws.WithdrawalsForkHeight { if env.clMock.currentPayloadNumber >= ws.wdForkHeight.uint64:
# Prepare some withdrawals # Prepare some withdrawals
t.clMock.NextWithdrawals, canonicalNextIndex = ws.GenerateWithdrawalsForBlock(canonicalNextIndex, canonicalStartAccount) let wfb = ws.generateWithdrawalsForBlock(canonical.nextIndex, canonical.startAccount)
ws.wdHistory[t.clMock.CurrentPayloadNumber] = t.clMock.NextWithdrawals env.clMock.nextWithdrawals = some(w3Withdrawals wfb.wds)
} canonical.nextIndex = wfb.nextIndex
ws.wdHistory.put(env.clMock.currentPayloadNumber, wfb.wds)
if t.clMock.CurrentPayloadNumber >= ws.getSidechainSplitHeight() { if env.clMock.currentPayloadNumber >= ws.getSidechainSplitHeight().uint64:
# We have split # We have split
if t.clMock.CurrentPayloadNumber >= ws.getSidechainWithdrawalsForkHeight() { if env.clMock.currentPayloadNumber >= ws.getSidechainWdForkHeight().uint64:
# And we are past the withdrawals fork on the sidechain # And we are past the withdrawals fork on the sidechain
sidechainwdHistory[t.clMock.CurrentPayloadNumber], sidechainNextIndex = ws.GenerateWithdrawalsForBlock(sidechainNextIndex, sidechainStartAccount) let wfb = ws.generateWithdrawalsForBlock(sidechain.nextIndex, sidechain.startAccount)
} # else nothing to do sidechain.wdHistory.put(env.clMock.currentPayloadNumber, wfb.wds)
} else { sidechain.nextIndex = wfb.nextIndex
# We have not split else:
sidechainwdHistory[t.clMock.CurrentPayloadNumber] = t.clMock.NextWithdrawals if env.clMock.nextWithdrawals.isSome:
sidechainNextIndex = canonicalNextIndex let wds = ethWithdrawals env.clMock.nextWithdrawals.get()
} sidechain.wdHistory.put(env.clMock.currentPayloadNumber, wds)
sidechain.nextIndex = canonical.nextIndex
}, return true
OnRequestNextPayload: proc(): bool = ,
onRequestNextPayload: proc(): bool =
# Send transactions to be included in the payload # Send transactions to be included in the payload
txs, err := helper.SendNextTransactions( let txs = env.makeTxs(
t.TestContext, BaseTx(
t.clMock.NextBlockProducer, recipient: some(prevRandaoContractAddr),
&helper.BaseTransactionCreator{ amount: 1.u256,
Recipient: &globals.PrevRandaoContractAddr, txType: ws.txType,
Amount: common.Big1, gasLimit: 75000.GasInt,
Payload: nil, ),
TxType: t.TestTransactionType, ws.getTransactionCountPerPayload()
GasLimit: 75000,
},
ws.getTransactionCountPerPayload(),
) )
if err != nil {
error "Error trying to send transactions: %v", t.TestName, err) testCond env.sendTxs(env.clMock.nextBlockProducer, txs):
} error "Error trying to send transaction"
# Error will be ignored here since the tx could have been already relayed # Error will be ignored here since the tx could have been already relayed
secondaryEngine.SendTransactions(t.TestContext, txs...) discard env.sendTxs(sec, txs)
if t.clMock.CurrentPayloadNumber >= ws.getSidechainSplitHeight() { if env.clMock.currentPayloadNumber >= ws.getSidechainSplitHeight().uint64:
# Also request a payload from the sidechain # Also request a payload from the sidechain
fcU := beacon.ForkchoiceStateV1{ var fcState = ForkchoiceStateV1(
HeadBlockHash: t.clMock.latestForkchoice.HeadBlockHash, headBlockHash: env.clMock.latestForkchoice.headBlockHash,
} )
if t.clMock.CurrentPayloadNumber > ws.getSidechainSplitHeight() { if env.clMock.currentPayloadNumber > ws.getSidechainSplitHeight().uint64:
if lastSidePayload, ok := sidechain[t.clMock.CurrentPayloadNumber-1]; !ok { let lastSidePayload = sidechain.sidechain[env.clMock.currentPayloadNumber-1]
panic("sidechain payload not found") fcState.headBlockHash = lastSidePayload.blockHash
} else {
fcU.HeadBlockHash = lastSidePayload.BlockHash
}
}
var version int var attr = PayloadAttributes(
pAttributes := typ.PayloadAttributes{ prevRandao: env.clMock.latestPayloadAttributes.prevRandao,
Random: t.clMock.latestPayloadAttributes.Random, suggestedFeeRecipient: env.clMock.latestPayloadAttributes.suggestedFeeRecipient,
SuggestedFeeRecipient: t.clMock.latestPayloadAttributes.SuggestedFeeRecipient, )
}
if t.clMock.CurrentPayloadNumber > ws.getSidechainSplitHeight() { if env.clMock.currentPayloadNumber > ws.getSidechainSplitHeight().uint64:
pAttributes.Timestamp = sidechain[t.clMock.CurrentPayloadNumber-1].Timestamp + uint64(ws.getSidechainBlockTimeIncrements()) attr.timestamp = w3Qty(sidechain.sidechain[env.clMock.currentPayloadNumber-1].timestamp, ws.getSidechainBlockTimeIncrements())
} else if t.clMock.CurrentPayloadNumber == ws.getSidechainSplitHeight() { elif env.clMock.currentPayloadNumber == ws.getSidechainSplitHeight().uint64:
pAttributes.Timestamp = t.clMock.latestHeader.Time + uint64(ws.getSidechainBlockTimeIncrements()) attr.timestamp = w3Qty(env.clMock.latestHeader.timestamp, ws.getSidechainBlockTimeIncrements())
} else { else:
pAttributes.Timestamp = t.clMock.latestPayloadAttributes.Timestamp attr.timestamp = env.clMock.latestPayloadAttributes.timestamp
}
if t.clMock.CurrentPayloadNumber >= ws.getSidechainWithdrawalsForkHeight() { if env.clMock.currentPayloadNumber >= ws.getSidechainwdForkHeight().uint64:
# Withdrawals # Withdrawals
version = 2 let rr = sidechain.wdHistory.get(env.clMock.currentPayloadNumber)
pAttributes.Withdrawals = sidechainwdHistory[t.clMock.CurrentPayloadNumber] testCond rr.isOk:
} else { error "sidechain wd", msg=rr.error
# No withdrawals
version = 1
}
info "Requesting sidechain payload %d: %v", t.TestName, t.clMock.CurrentPayloadNumber, pAttributes) attr.withdrawals = some(w3Withdrawals rr.get)
r := secondaryEngineTest.forkchoiceUpdated(&fcU, &pAttributes, version) info "Requesting sidechain payload",
number=env.clMock.currentPayloadNumber
sidechain.attr = some(attr)
let r = sec.client.forkchoiceUpdated(fcState, attr)
r.expectNoError() r.expectNoError()
r.expectPayloadStatus(test.Valid) r.testFCU(valid)
if r.Response.PayloadID == nil { testCond r.get().payloadID.isSome:
error "Unable to get a payload ID on the sidechain", t.TestName) error "Unable to get a payload ID on the sidechain"
} sidechain.payloadId = r.get().payloadID.get()
sidechainPayloadId = r.Response.PayloadID
} return true
}, ,
OnGetPayload: proc(): bool = onGetPayload: proc(): bool =
var ( var
version int payload: ExecutionPayload
payload *typ.ExecutableData
) if env.clMock.latestPayloadBuilt.blockNumber.uint64 >= ws.getSidechainSplitHeight().uint64:
if t.clMock.CurrentPayloadNumber >= ws.getSidechainWithdrawalsForkHeight() {
version = 2
} else {
version = 1
}
if t.clMock.latestPayloadBuilt.Number >= ws.getSidechainSplitHeight() {
# This payload is built by the secondary client, hence need to manually fetch it here # This payload is built by the secondary client, hence need to manually fetch it here
r := secondaryEngineTest.getPayload(sidechainPayloadId, version) doAssert(sidechain.attr.isSome)
let version = sidechain.attr.get().version
let r = sec.client.getPayload(sidechain.payloadId, version)
r.expectNoError() r.expectNoError()
payload = &r.Payload payload = r.get().executionPayload
sidechain[payload.Number] = payload sidechain.sidechain[payload.blockNumber.uint64] = payload
} else { else:
# This block is part of both chains, simply forward it to the secondary client # This block is part of both chains, simply forward it to the secondary client
payload = &t.clMock.latestPayloadBuilt payload = env.clMock.latestPayloadBuilt
}
r := secondaryEngineTest.newPayload(payload, nil, nil, version) let r = sec.client.newPayload(payload, payload.version)
r.expectStatus(test.Valid) r.expectStatus(valid)
p := secondaryEngineTest.forkchoiceUpdated(
&beacon.ForkchoiceStateV1{ let fcState = ForkchoiceStateV1(
HeadBlockHash: payload.BlockHash, headBlockHash: payload.blockHash,
},
nil,
version,
) )
p.expectPayloadStatus(test.Valid) let p = sec.client.forkchoiceUpdated(payload.version, fcState)
}, p.testFCU(valid)
}) return true
))
testCond pbRes
sidechainHeight := t.clMock.latestExecutedPayload.Number sidechain.height = env.clMock.latestExecutedPayload.blockNumber.uint64
if ws.WithdrawalsForkHeight < ws.getSidechainWithdrawalsForkHeight() { if ws.wdForkHeight < ws.getSidechainwdForkHeight():
# This means the canonical chain forked before the sidechain. # This means the canonical chain forked before the sidechain.
# Therefore we need to produce more sidechain payloads to reach # Therefore we need to produce more sidechain payloads to reach
# at least`ws.WithdrawalsBlockCount` withdrawals payloads produced on # at least`ws.WithdrawalsBlockCount` withdrawals payloads produced on
# the sidechain. # the sidechain.
for i := uint64(0); i < ws.getSidechainWithdrawalsForkHeight()-ws.WithdrawalsForkHeight; i++ { let height = ws.getSidechainwdForkHeight()-ws.wdForkHeight
sidechainwdHistory[sidechainHeight+1], sidechainNextIndex = ws.GenerateWithdrawalsForBlock(sidechainNextIndex, sidechainStartAccount) for i in 0..<height:
pAttributes := typ.PayloadAttributes{ let
Timestamp: sidechain[sidechainHeight].Timestamp + ws.getSidechainBlockTimeIncrements(), wfb = ws.generateWithdrawalsForBlock(sidechain.nextIndex, sidechain.startAccount)
Random: t.clMock.latestPayloadAttributes.Random,
SuggestedFeeRecipient: t.clMock.latestPayloadAttributes.SuggestedFeeRecipient, sidechain.wdHistory.put(sidechain.height+1, wfb.wds)
Withdrawals: sidechainwdHistory[sidechainHeight+1], sidechain.nextIndex = wfb.nextIndex
}
r := secondaryEngineTest.forkchoiceUpdatedV2(&beacon.ForkchoiceStateV1{ let wds = sidechain.wdHistory.get(sidechain.height+1).valueOr:
HeadBlockHash: sidechain[sidechainHeight].BlockHash, echo "get wd history error ", error
}, &pAttributes) return false
r.expectPayloadStatus(test.Valid)
time.Sleep(time.Second) let
p := secondaryEngineTest.getPayloadV2(r.Response.PayloadID) attr = PayloadAttributes(
timestamp: w3Qty(sidechain.sidechain[sidechain.height].timestamp, ws.getSidechainBlockTimeIncrements()),
prevRandao: env.clMock.latestPayloadAttributes.prevRandao,
suggestedFeeRecipient: env.clMock.latestPayloadAttributes.suggestedFeeRecipient,
withdrawals: some(w3Withdrawals wds),
)
fcState = ForkchoiceStateV1(
headBlockHash: sidechain.sidechain[sidechain.height].blockHash,
)
let r = sec.client.forkchoiceUpdatedV2(fcState, some(attr))
r.testFCU(valid)
let p = sec.client.getPayloadV2(r.get().payloadID.get)
p.expectNoError() p.expectNoError()
s := secondaryEngineTest.newPayloadV2(&p.Payload)
s.expectStatus(test.Valid) let z = p.get()
q := secondaryEngineTest.forkchoiceUpdatedV2( let s = sec.client.newPayloadV2(z.executionPayload)
&beacon.ForkchoiceStateV1{ s.expectStatus(valid)
HeadBlockHash: p.Payload.BlockHash,
}, let fs = ForkchoiceStateV1(headBlockHash: z.executionPayload.blockHash)
nil,
) let q = sec.client.forkchoiceUpdatedV2(fs)
q.expectPayloadStatus(test.Valid) q.testFCU(valid)
sidechainHeight++
sidechain[sidechainHeight] = &p.Payload inc sidechain.height
} sidechain.sidechain[sidechain.height] = executionPayload(z.executionPayload)
}
# Check the withdrawals on the latest # Check the withdrawals on the latest
ws.wdHistory.VerifyWithdrawals( let res = ws.wdHistory.verifyWithdrawals(sidechain.height, none(UInt256), env.client)
sidechainHeight, testCond res.isOk
nil,
t.TestEngine,
)
if ws.ReOrgViaSync { if ws.reOrgViaSync:
# Send latest sidechain payload as NewPayload + FCU and wait for sync # Send latest sidechain payload as NewPayload + FCU and wait for sync
loop: let
for { payload = sidechain.sidechain[sidechain.height]
r := t.rpcClient.newPayloadV2(sidechain[sidechainHeight]) sideHash = sidechain.sidechain[sidechain.height].blockHash
sleep = DefaultSleep
period = chronos.seconds(sleep)
var loop = 0
if ws.timeoutSeconds == 0:
ws.timeoutSeconds = DefaultTimeout
while loop < ws.timeoutSeconds:
let r = env.client.newPayloadV2(payload.V2)
r.expectNoError() r.expectNoError()
p := t.rpcClient.forkchoiceUpdatedV2( let fcState = ForkchoiceStateV1(headBlockHash: sideHash)
&beacon.ForkchoiceStateV1{ let p = env.client.forkchoiceUpdatedV2(fcState)
HeadBlockHash: sidechain[sidechainHeight].BlockHash,
},
nil,
)
p.expectNoError() p.expectNoError()
if p.Response.PayloadStatus.Status == test.Invalid {
error "Primary client invalidated side chain", t.TestName) let status = p.get().payloadStatus.status
} if status == PayloadExecutionStatus.invalid:
select { error "Primary client invalidated side chain"
case <-t.TimeoutContext.Done(): return false
error "Timeout waiting for sync", t.TestName)
case <-time.After(time.Second): var header: common.BlockHeader
b := t.rpcClient.BlockByNumber(nil) let b = env.client.latestHeader(header)
if b.Block.Hash() == sidechain[sidechainHeight].BlockHash { testCond b.isOk
# sync successful if header.blockHash == ethHash(sidehash):
break loop # sync successful
} break
}
} waitFor sleepAsync(period)
} else { loop += sleep
else:
# Send all payloads one by one to the primary client # Send all payloads one by one to the primary client
for payloadNumber := ws.getSidechainSplitHeight(); payloadNumber <= sidechainHeight; payloadNumber++ { var payloadNumber = ws.getSidechainSplitHeight()
payload, ok := sidechain[payloadNumber] while payloadNumber.uint64 <= sidechain.height:
if !ok { let payload = sidechain.sidechain[payloadNumber.uint64]
error "Invalid payload %d requested.", t.TestName, payloadNumber) var version = Version.V1
} if payloadNumber >= ws.getSidechainwdForkHeight():
var version int version = Version.V2
if payloadNumber >= ws.getSidechainWithdrawalsForkHeight() {
version = 2 info "Sending sidechain",
} else { payloadNumber,
version = 1 hash=payload.blockHash.short,
} parentHash=payload.parentHash.short
info "Sending sidechain payload %d, hash=%s, parent=%s", t.TestName, payloadNumber, payload.BlockHash, payload.ParentHash)
r := t.rpcClient.newPayload(payload, nil, nil, version) let r = env.client.newPayload(payload, version)
r.expectStatusEither(test.Valid, test.Accepted) r.expectStatusEither(valid, accepted)
p := t.rpcClient.forkchoiceUpdated(
&beacon.ForkchoiceStateV1{ let fcState = ForkchoiceStateV1(headBlockHash: payload.blockHash)
HeadBlockHash: payload.BlockHash, let p = env.client.forkchoiceUpdated(version, fcState)
}, p.testFCU(valid)
nil, inc payloadNumber
version,
)
p.expectPayloadStatus(test.Valid)
}
}
# Verify withdrawals changed # Verify withdrawals changed
sidechainwdHistory.VerifyWithdrawals( let r2 = sidechain.wdHistory.verifyWithdrawals(sidechain.height, none(UInt256), env.client)
sidechainHeight, testCond r2.isOk
nil,
t.TestEngine,
)
# Verify all balances of accounts in the original chain didn't increase # Verify all balances of accounts in the original chain didn't increase
# after the fork. # after the fork.
# We are using different accounts credited between the canonical chain # We are using different accounts credited between the canonical chain
# and the fork. # and the fork.
# We check on `latest`. # We check on `latest`.
ws.wdHistory.VerifyWithdrawals( let r3 = ws.wdHistory.verifyWithdrawals(uint64(ws.wdForkHeight-1), none(UInt256), env.client)
ws.WithdrawalsForkHeight-1, testCond r3.isOk
nil,
t.TestEngine,
)
# Re-Org back to the canonical chain # Re-Org back to the canonical chain
r := t.rpcClient.forkchoiceUpdatedV2(&beacon.ForkchoiceStateV1{ let fcState = ForkchoiceStateV1(headBlockHash: env.clMock.latestPayloadBuilt.blockHash)
HeadBlockHash: t.clMock.latestPayloadBuilt.BlockHash, let r = env.client.forkchoiceUpdatedV2(fcState)
}, nil) r.testFCU(valid)
r.expectPayloadStatus(test.Valid)
]#

View File

@ -52,6 +52,11 @@ type
Option[PayloadAttributesV2] | Option[PayloadAttributesV2] |
Option[PayloadAttributesV3] Option[PayloadAttributesV3]
GetPayloadResponse* = object
executionPayload*: ExecutionPayload
blockValue*: Option[UInt256]
blobsBundle*: Option[BlobsBundleV1]
Version* {.pure.} = enum Version* {.pure.} = enum
V1 V1
V2 V2
@ -59,21 +64,27 @@ type
func version*(payload: ExecutionPayload): Version = func version*(payload: ExecutionPayload): Version =
if payload.blobGasUsed.isSome and payload.excessBlobGas.isSome: if payload.blobGasUsed.isSome and payload.excessBlobGas.isSome:
return Version.V3 Version.V3
elif payload.withdrawals.isSome:
if payload.withdrawals.isSome: Version.V2
return Version.V2 else:
Version.V1
Version.V1
func version*(attr: PayloadAttributes): Version = func version*(attr: PayloadAttributes): Version =
if attr.parentBeaconBlockRoot.isSome: if attr.parentBeaconBlockRoot.isSome:
return Version.V3 Version.V3
elif attr.withdrawals.isSome:
Version.V2
else:
Version.V1
if attr.withdrawals.isSome: func version*(res: GetPayloadResponse): Version =
return Version.V2 if res.blobsBundle.isSome:
Version.V3
Version.V1 elif res.blockValue.isSome:
Version.V2
else:
Version.V1
func V1V2*(attr: PayloadAttributes): PayloadAttributesV1OrV2 = func V1V2*(attr: PayloadAttributes): PayloadAttributesV1OrV2 =
PayloadAttributesV1OrV2( PayloadAttributesV1OrV2(
@ -107,6 +118,22 @@ func V3*(attr: PayloadAttributes): PayloadAttributesV3 =
parentBeaconBlockRoot: attr.parentBeaconBlockRoot.get parentBeaconBlockRoot: attr.parentBeaconBlockRoot.get
) )
func V1*(attr: Option[PayloadAttributes]): Option[PayloadAttributesV1] =
if attr.isNone:
return none(PayloadAttributesV1)
some(attr.get.V1)
when false:
func V2*(attr: Option[PayloadAttributes]): Option[PayloadAttributesV2] =
if attr.isNone:
return none(PayloadAttributesV2)
some(attr.get.V2)
func V3*(attr: Option[PayloadAttributes]): Option[PayloadAttributesV3] =
if attr.isNone:
return none(PayloadAttributesV3)
some(attr.get.V3)
func payloadAttributes*(attr: PayloadAttributesV1): PayloadAttributes = func payloadAttributes*(attr: PayloadAttributesV1): PayloadAttributes =
PayloadAttributes( PayloadAttributes(
timestamp: attr.timestamp, timestamp: attr.timestamp,
@ -308,3 +335,19 @@ func executionPayload*(p: ExecutionPayloadV1OrV2): ExecutionPayload =
transactions: p.transactions, transactions: p.transactions,
withdrawals: p.withdrawals withdrawals: p.withdrawals
) )
func V1*(res: GetPayloadResponse): ExecutionPayloadV1 =
res.executionPayload.V1
func V2*(res: GetPayloadResponse): GetPayloadV2Response =
GetPayloadV2Response(
executionPayload: res.executionPayload.V1V2,
blockValue: res.blockValue.get
)
func V3*(res: GetPayloadResponse): GetPayloadV3Response =
GetPayloadV3Response(
executionPayload: res.executionPayload.V3,
blockValue: res.blockValue.get,
blobsBundle: res.blobsBundle.get
)

View File

@ -21,7 +21,7 @@ const
# maxTrackedHeaders is the maximum number of executed payloads the execution # maxTrackedHeaders is the maximum number of executed payloads the execution
# engine tracks before evicting old ones. Ideally we should only ever track # engine tracks before evicting old ones. Ideally we should only ever track
# the latest one; but have a slight wiggle room for non-ideal conditions. # the latest one; but have a slight wiggle room for non-ideal conditions.
MaxTrackedHeaders = 10 MaxTrackedHeaders = 96
type type
QueueItem[T] = object QueueItem[T] = object

View File

@ -56,7 +56,7 @@ proc `$`*(x: Web3Quantity): string =
proc `$`*(x: Web3Address): string = proc `$`*(x: Web3Address): string =
distinctBase(x).toHex distinctBase(x).toHex
proc short*(x: Web3Hash): string = proc short*(x: Web3Hash): string =
let z = common.Hash256(data: distinctBase x) let z = common.Hash256(data: distinctBase x)
short(z) short(z)
@ -168,6 +168,9 @@ func w3Qty*(x: common.EthTime): Web3Quantity =
func w3Qty*(x: common.EthTime, y: int): Web3Quantity = func w3Qty*(x: common.EthTime, y: int): Web3Quantity =
Web3Quantity(x.toUnix + y.int64) Web3Quantity(x.toUnix + y.int64)
func w3Qty*(x: Web3Quantity, y: int): Web3Quantity =
Web3Quantity(x.uint64 + y.uint64)
func w3Qty*(x: Option[uint64]): Option[Web3Quantity] = func w3Qty*(x: Option[uint64]): Option[Web3Quantity] =
if x.isNone: none(Web3Quantity) if x.isNone: none(Web3Quantity)
else: some(Web3Quantity x.get) else: some(Web3Quantity x.get)

View File

@ -114,12 +114,17 @@ proc txFeesCovered(xp: TxPoolRef; item: TxItemRef): bool =
return false return false
true true
import stew/byteutils
import ../../../utils/debug
proc txCostInBudget(xp: TxPoolRef; item: TxItemRef): bool = proc txCostInBudget(xp: TxPoolRef; item: TxItemRef): bool =
## Check whether the worst case expense is covered by the price budget, ## Check whether the worst case expense is covered by the price budget,
let let
balance = xp.chain.getBalance(item.sender) balance = xp.chain.getBalance(item.sender)
gasCost = item.tx.gasCost gasCost = item.tx.gasCost
if balance < gasCost: if balance < gasCost:
debugEcho "nonce: ", item.tx.nonce, " ", balance, " ", gasCost, " ", item.sender.toHex
debugEcho debug(item.tx)
debug "invalid tx: not enough cash for gas", debug "invalid tx: not enough cash for gas",
available = balance, available = balance,
require = gasCost require = gasCost

View File

@ -18,7 +18,6 @@ import
".."/[protocol, sync_desc], ".."/[protocol, sync_desc],
./worker_desc, ./worker_desc,
./skeleton_main, ./skeleton_main,
./skeleton_utils,
./beacon_impl ./beacon_impl
logScope: logScope: