From 19f313d891b352e5dca683356fb25b8361a16b2c Mon Sep 17 00:00:00 2001 From: jangko Date: Fri, 3 Nov 2023 09:24:51 +0700 Subject: [PATCH] More Engine API tests --- hive_integration/nodocker/engine/clmock.nim | 2 +- .../nodocker/engine/engine/fork_id.nim | 71 +-- .../engine/engine/invalid_ancestor.nim | 4 + .../engine/engine/payload_execution.nim | 409 ++++++------- .../nodocker/engine/engine/payload_id.nim | 86 +-- .../nodocker/engine/engine/prev_randao.nim | 10 +- .../nodocker/engine/engine/reorg.nim | 578 +++++++++--------- .../nodocker/engine/engine/rpc.nim | 95 +-- .../nodocker/engine/engine/versioning.nim | 57 +- .../nodocker/engine/engine_sim.nim | 8 +- .../nodocker/engine/engine_tests.nim | 353 +++++------ hive_integration/nodocker/engine/helper.nim | 12 +- hive_integration/nodocker/engine/test_env.nim | 6 + hive_integration/nodocker/engine/types.nim | 43 +- 14 files changed, 866 insertions(+), 868 deletions(-) diff --git a/hive_integration/nodocker/engine/clmock.nim b/hive_integration/nodocker/engine/clmock.nim index 607fed39d..226d94913 100644 --- a/hive_integration/nodocker/engine/clmock.nim +++ b/hive_integration/nodocker/engine/clmock.nim @@ -72,7 +72,7 @@ type latestForkchoice* : ForkchoiceStateV1 # Merge related - firstPoSBlockNumber : Option[uint64] + firstPoSBlockNumber* : Option[uint64] ttdReached* : bool transitionPayloadTimestamp: Option[int] chainTotalDifficulty : UInt256 diff --git a/hive_integration/nodocker/engine/engine/fork_id.nim b/hive_integration/nodocker/engine/engine/fork_id.nim index fd1d5169a..a6c700b65 100644 --- a/hive_integration/nodocker/engine/engine/fork_id.nim +++ b/hive_integration/nodocker/engine/engine/fork_id.nim @@ -10,11 +10,13 @@ import std/strutils, - ./engine_spec + chronicles, + ./engine_spec, + ../../../../nimbus/common/hardforks type ForkIDSpec* = ref object of EngineSpec - produceBlocksBeforePeering: int + produceBlocksBeforePeering*: int method withMainFork(cs: ForkIDSpec, fork: EngineFork): BaseSpec = var res = cs.clone() @@ -22,15 +24,25 @@ method withMainFork(cs: ForkIDSpec, fork: EngineFork): BaseSpec = return res method getName(cs: ForkIDSpec): string = - name = "Fork ID: Genesis at %d, %s at %d", cs.GetGenesistimestamp(), cs.mainFork, cs.ForkTime) - if cs.previousForkTime != 0 ( - name += ", %s at %d", cs.mainFork.PreviousFork(), cs.previousForkTime) - ) - if cs.produceBlocksBeforePeering > 0 ( - name += ", Produce %d blocks before peering", cs.produceBlocksBeforePeering) - ) + var name = "Fork ID: Genesis at $1, $2 at $3" % [$cs.getGenesistimestamp(), $cs.mainFork, $cs.forkTime] + if cs.previousForkTime != 0: + name.add ", $1 at $2" % [$cs.mainFork.pred, $cs.previousForkTime] + + if cs.produceBlocksBeforePeering > 0: + name.add ", Produce $1 blocks before peering" % [$cs.produceBlocksBeforePeering] + return name -) + +method getForkConfig*(cs: ForkIDSpec): ChainConfig = + let forkConfig = procCall getForkConfig(BaseSpec(cs)) + if forkConfig.isNil: + return nil + + # Merge fork happen at block 0 + let mainFork = cs.getMainFork() + if mainFork == ForkParis: + forkConfig.mergeForkBlock = some(0.u256) + return forkConfig method execute(cs: ForkIDSpec, env: TestEnv): bool = # Wait until TTD is reached by this client @@ -38,41 +50,8 @@ method execute(cs: ForkIDSpec, env: TestEnv): bool = testCond ok # Produce blocks before starting the test if required - env.clMock.produceBlocks(cs.produceBlocksBeforePeering, BlockProcessCallbacks()) + testCond env.clMock.produceBlocks(cs.produceBlocksBeforePeering, BlockProcessCallbacks()) # Get client index's enode - engine = t.Engine - conn, err = devp2p.PeerEngineClient(engine, t.CLMock) - if err != nil ( - fatal "Error peering engine client: %v", err) - ) - defer conn.Close() - info "Connected to client, remote public key: %s", conn.RemoteKey()) - - # Sleep - await sleepAsync(1 * time.Second) - - # Timeout value for all requests - timeout = 20 * time.Second - - # Send a ping request to verify that we are not immediately disconnected - pingReq = &devp2p.Ping() - if size, err = conn.Write(pingReq); err != nil ( - fatal "Could not write to connection: %v", err) - else: - info "Wrote %d bytes to conn", size) - ) - - # Finally wait for the pong response - msg, err = conn.WaitForResponse(timeout, 0) - if err != nil ( - fatal "Error waiting for response: %v", err) - ) - switch msg = msg.(type) ( - case *devp2p.Pong: - info "Received pong response: %v", msg) - default: - fatal "Unexpected message type: %v", err) - ) - -) + let engine = env.addEngine() + return true diff --git a/hive_integration/nodocker/engine/engine/invalid_ancestor.nim b/hive_integration/nodocker/engine/engine/invalid_ancestor.nim index 3531fbfa5..520a71211 100644 --- a/hive_integration/nodocker/engine/engine/invalid_ancestor.nim +++ b/hive_integration/nodocker/engine/engine/invalid_ancestor.nim @@ -240,6 +240,10 @@ method execute(cs: InvalidMissingAncestorReOrgSyncTest, env: TestEnv): bool = # Append the common ancestor shadow.payloads.add env.clMock.latestExecutableData + if not cs.reOrgFromCanonical: + # Add back the original client before side chain production + env.cLMock.addEngine(env.engine) + # Produce blocks but at the same time create an side chain which contains an invalid payload at some point (INV_P) # CommonAncestor◄─▲── P1 ◄─ P2 ◄─ P3 ◄─ ... ◄─ Pn # │ diff --git a/hive_integration/nodocker/engine/engine/payload_execution.nim b/hive_integration/nodocker/engine/engine/payload_execution.nim index 462a8ce32..3433d98b7 100644 --- a/hive_integration/nodocker/engine/engine/payload_execution.nim +++ b/hive_integration/nodocker/engine/engine/payload_execution.nim @@ -10,6 +10,9 @@ import std/strutils, + eth/common, + chronicles, + ../cancun/customizer, ./engine_spec type @@ -37,45 +40,49 @@ method execute(cs: ReExecutePayloadTest, env: TestEnv): bool = let pbRes = env.clMock.produceBlocks(payloadReExecCount, BlockProcessCallbacks( onPayloadProducerSelected: proc(): bool = # Send at least one transaction per payload - _, err = env.sendNextTx( - env.clMock.nextBlockProducer, - BaseTx( - txType: cs.txType, - gasLimit: 75000, - ), + let tc = BaseTx( + txType: cs.txType, + gasLimit: 75000, ) - if err != nil ( - fatal "Error trying to send transaction: %v", t.TestName, err) - ) - ), + let ok = env.sendNextTx(env.clMock.nextBlockProducer, tc) + testCond ok: + fatal "Error trying to send transaction" + return true + , onGetPayload: proc(): bool = # Check that the transaction was included - if len(env.clMock.latestPayloadBuilt.transactions) == 0 ( - fatal "Client failed to include the expected transaction in payload built", t.TestName) - ) - ), + testCond len(env.clMock.latestPayloadBuilt.transactions) != 0: + fatal "Client failed to include the expected transaction in payload built" + return true )) + testCond pbRes + # Re-execute the payloads - r = env.engine.client.blockNumber() + let r = env.engine.client.blockNumber() r.expectNoError() - lastBlock = r.blockNumber - info "Started re-executing payloads at block: %v", t.TestName, lastBlock) + let lastBlock = r.get + info "Started re-executing payloads at block", number=lastBlock - for i = lastBlock - uint64(payloadReExecCount) + 1; i <= lastBlock; i++ ( - payload, found = env.clMock.executedPayloadHistory[i] - if !found ( - fatal "(test issue) Payload with index %d does not exist", i) - ) + let start = lastBlock - uint64(payloadReExecCount) + 1 - r = env.engine.client.newPayload(payload) + for i in start..lastBlock: + doAssert env.clMock.executedPayloadHistory.hasKey(i) + let payload = env.clMock.executedPayloadHistory[i] + let r = env.engine.client.newPayload(payload) r.expectStatus(PayloadExecutionStatus.valid) r.expectLatestValidHash(payload.blockHash) - ) -) + + return true type InOrderPayloadExecutionTest* = ref object of EngineSpec + Shadow = ref object + recipient: EthAddress + amountPerTx: UInt256 + txPerPayload: int + payloadCount: int + txsIncluded: int method withMainFork(cs: InOrderPayloadExecutionTest, fork: EngineFork): BaseSpec = var res = cs.clone() @@ -92,93 +99,93 @@ method execute(cs: InOrderPayloadExecutionTest, env: TestEnv): bool = testCond ok # Send a single block to allow sending newer transaction types on the payloads - env.clMock.produceSingleBlock(BlockProcessCallbacks()) + testCond env.clMock.produceSingleBlock(BlockProcessCallbacks()) # First prepare payloads on a first client, which will also contain multiple transactions # We will be also verifying that the transactions are correctly interpreted in the canonical chain, # prepare a random account to receive funds. - recipient = EthAddress.randomBytes() - amountPerTx = big.NewInt(1000) - txPerPayload = 20 - payloadCount = 10 - txsIncluded = 0 + var shadow = Shadow( + recipient: EthAddress.randomBytes(), + amountPerTx: 1000.u256, + txPerPayload: 20, + payloadCount: 10, + txsIncluded: 0, + ) - env.clMock.produceBlocks(payloadCount, BlockProcessCallbacks( + let pbRes = env.clMock.produceBlocks(shadow.payloadCount, BlockProcessCallbacks( # We send the transactions after we got the Payload ID, before the CLMocker gets the prepared Payload onPayloadProducerSelected: proc(): bool = - _, err = env.sendNextTxs( - env.clMock.nextBlockProducer, - BaseTx( - recipient: &recipient, - amount: amountPerTx, - txType: cs.txType, - gasLimit: 75000, - ), - uint64(txPerPayload), + let tc = BaseTx( + recipient: some(shadow.recipient), + amount: shadow.amountPerTx, + txType: cs.txType, + gasLimit: 75000, ) - if err != nil ( - fatal "Error trying to send transaction: %v", t.TestName, err) - ) - ), + let ok = env.sendNextTxs(env.clMock.nextBlockProducer, tc, shadow.txPerPayload) + testCond ok: + fatal "Error trying to send transaction" + return true + , onGetPayload: proc(): bool = - if len(env.clMock.latestPayloadBuilt.Transactions) < (txPerPayload / 2) ( - fatal "Client failed to include all the expected transactions in payload built: %d < %d", t.TestName, len(env.clMock.latestPayloadBuilt.Transactions), (txPerPayload / 2)) - ) - txsIncluded += len(env.clMock.latestPayloadBuilt.Transactions) - ), + if len(env.clMock.latestPayloadBuilt.transactions) < (shadow.txPerPayload div 2): + fatal "Client failed to include all the expected transactions in payload built" + + shadow.txsIncluded += len(env.clMock.latestPayloadBuilt.transactions) + return true )) - expectedBalance = amountPerTx.Mul(amountPerTx, big.NewInt(int64(txsIncluded))) + testCond pbRes + let expectedBalance = shadow.amountPerTx * shadow.txsIncluded.u256 # Check balance on this first client - r = env.engine.client.balanceAt(recipient, nil) + let r = env.engine.client.balanceAt(shadow.recipient) r.expectBalanceEqual(expectedBalance) # Start a second client to send newPayload consecutively without fcU let sec = env.addEngine(false, false) # Send the forkchoiceUpdated with the latestExecutedPayload hash, we should get SYNCING back - fcU = ForkchoiceStateV1( + let fcU = ForkchoiceStateV1( headblockHash: env.clMock.latestExecutedPayload.blockHash, safeblockHash: env.clMock.latestExecutedPayload.blockHash, finalizedblockHash: env.clMock.latestExecutedPayload.blockHash, ) - s = sec.client.forkchoiceUpdated(fcU, nil, env.clMock.latestExecutedPayload.timestamp) + var version = sec.version(env.clMock.latestExecutedPayload.timestamp) + var s = sec.client.forkchoiceUpdated(version, fcU) s.expectPayloadStatus(PayloadExecutionStatus.syncing) - s.expectLatestValidHash(nil) - s.ExpectNoValidationError() + s.expectLatestValidHash() + s.expectNoValidationError() # Send all the payloads in the increasing order - for k = env.clMock.firstPoSBlockNumber.Uint64(); k <= env.clMock.latestExecutedPayload.blockNumber; k++ ( - payload = env.clMock.executedPayloadHistory[k] - - s = sec.client.newPayload(payload) + let start = env.clMock.firstPoSBlockNumber.get + for k in start..env.clMock.latestExecutedPayload.blockNumber.uint64: + let payload = env.clMock.executedPayloadHistory[k] + let s = sec.client.newPayload(payload) s.expectStatus(PayloadExecutionStatus.valid) s.expectLatestValidHash(payload.blockHash) - ) - - s = sec.client.forkchoiceUpdated(fcU, nil, env.clMock.latestExecutedPayload.timestamp) + version = sec.version(env.clMock.latestExecutedPayload.timestamp) + s = sec.client.forkchoiceUpdated(version, fcU) s.expectPayloadStatus(PayloadExecutionStatus.valid) s.expectLatestValidHash(fcU.headblockHash) - s.ExpectNoValidationError() + s.expectNoValidationError() # At this point we should have our funded account balance equal to the expected value. - q = sec.client.balanceAt(recipient, nil) + let q = sec.client.balanceAt(shadow.recipient) q.expectBalanceEqual(expectedBalance) # Add the client to the CLMocker - env.clMock.addEngine(secondaryClient) + env.clMock.addEngine(sec) # Produce a single block on top of the canonical chain, all clients must accept this - env.clMock.produceSingleBlock(BlockProcessCallbacks()) + testCond env.clMock.produceSingleBlock(BlockProcessCallbacks()) # Head must point to the latest produced payload - p = sec.client.TestHeaderByNumber(nil) - p.expectHash(env.clMock.latestExecutedPayload.blockHash) -) + let p = sec.client.latestHeader() + p.expectHash(ethHash env.clMock.latestExecutedPayload.blockHash) + return true type MultiplePayloadsExtendingCanonicalChainTest* = ref object of EngineSpec @@ -194,12 +201,10 @@ method withMainFork(cs: MultiplePayloadsExtendingCanonicalChainTest, fork: Engin return res method getName(cs: MultiplePayloadsExtendingCanonicalChainTest): string = - name = "Multiple New Payloads Extending Canonical Chain" - if s.SetHeadToFirstPayloadReceived ( - name += " (FcU to first payload received)" - ) - return name -) + var name = "Multiple New Payloads Extending Canonical Chain" + if cs.setHeadToFirstPayloadReceived: + name.add " (FcU to first payload received)" + name # Consecutive Payload Execution: Secondary client should be able to set the forkchoiceUpdated to payloads received consecutively method execute(cs: MultiplePayloadsExtendingCanonicalChainTest, env: TestEnv): bool = @@ -208,57 +213,48 @@ method execute(cs: MultiplePayloadsExtendingCanonicalChainTest, env: TestEnv): b testCond ok # Produce blocks before starting the test - env.clMock.produceBlocks(5, BlockProcessCallbacks()) + testCond env.clMock.produceBlocks(5, BlockProcessCallbacks()) - callbacks = BlockProcessCallbacks( + var callbacks = BlockProcessCallbacks( # We send the transactions after we got the Payload ID, before the CLMocker gets the prepared Payload onPayloadProducerSelected: proc(): bool = let recipient = EthAddress.randomBytes() - _, err = env.sendNextTx( - env.clMock.nextBlockProducer, - BaseTx( - recipient: &recipient, - txType: cs.txType, - gasLimit: 75000, - ), + let tc = BaseTx( + recipient: some(recipient), + txType: cs.txType, + gasLimit: 75000, ) - if err != nil ( - fatal "Error trying to send transaction: %v", t.TestName, err) - ) - ), + let ok = env.sendNextTx(env.clMock.nextBlockProducer, tc) + testCond ok: + fatal "Error trying to send transaction" + return true ) - reExecFunc = proc(): bool = - payloadCount = 80 - if cs.payloadCount > 0 ( + let reExecFunc = proc(): bool = + var payloadCount = 80 + if cs.payloadCount > 0: payloadCount = cs.payloadCount - ) - basePayload = env.clMock.latestPayloadBuilt + let basePayload = env.clMock.latestExecutableData # Check that the transaction was included - if len(basePayload.Transactions) == 0 ( - fatal "Client failed to include the expected transaction in payload built", t.TestName) - ) + testCond len(basePayload.basePayload.transactions)> 0: + fatal "Client failed to include the expected transaction in payload built" # Fabricate and send multiple new payloads by changing the PrevRandao field - for i = 0; i < payloadCount; i++ ( - newPrevRandao = common.Hash256.randomBytes() - customizer = CustomPayloadData( - prevRandao: &newPrevRandao, + for i in 0.. 0: + fatal "Client failed to include the expected transaction in payload built" + return true )) - - previousPayload = env.clMock.latestPayloadBuilt + testCond true + shadow.previousPayload = env.clMock.latestPayloadBuilt # Send the fcU to set it to syncing mode - r = env.engine.client.forkchoiceUpdated(env.clMock.latestForkchoice, nil, env.clMock.latestHeader.Time) + let version = env.engine.version(env.clMock.latestHeader.timestamp) + let r = env.engine.client.forkchoiceUpdated(version, env.clMock.latestForkchoice) r.expectPayloadStatus(PayloadExecutionStatus.syncing) - env.clMock.produceSingleBlock(BlockProcessCallbacks( + pbREs = env.clMock.produceSingleBlock(BlockProcessCallbacks( onPayloadProducerSelected: proc(): bool = # Send at least one transaction per payload - _, err = env.sendNextTx( - env.clMock.nextBlockProducer, - BaseTx( - recipient: &recipient, - txType: cs.txType, - gasLimit: 75000, - ), + let tc = BaseTx( + recipient: some(shadow.recipient), + txType: cs.txType, + gasLimit: 75000, ) - if err != nil ( - fatal "Error trying to send transaction: %v", t.TestName, err) - ) - ), + let ok = env.sendNextTx(env.clMock.nextBlockProducer, tc) + testCond ok: + fatal "Error trying to send transaction" + return true + , # Run test after the new payload has been obtained onGetPayload: proc(): bool = # Send the new payload from the second client to the first, it won't be able to validate it - r = env.engine.client.newPayload(env.clMock.latestPayloadBuilt) - r.expectStatusEither(PayloadExecutionStatus.accepted, PayloadExecutionStatus.syncing) - r.expectLatestValidHash(nil) + let r = env.engine.client.newPayload(env.clMock.latestPayloadBuilt) + r.expectStatusEither([PayloadExecutionStatus.accepted, PayloadExecutionStatus.syncing]) + r.expectLatestValidHash() # Send the forkchoiceUpdated with a reference to the valid payload on the SYNCING client. - var ( - random = common.Hash256() - suggestedFeeRecipient = common.Address() + var + random = w3Hash() + suggestedFeeRecipient = w3Address() + + let customizer = BasePayloadAttributesCustomizer( + prevRandao: some(ethHash random), + suggestedFeerecipient: some(ethAddr suggestedFeeRecipient), ) - payloadAttributesCustomizer = &BasePayloadAttributesCustomizer( - Random: &random, - SuggestedFeerecipient: &suggestedFeeRecipient, - ) - newPayloadAttributes, err = payloadAttributesCustomizer.GetPayloadAttributes(env.clMock.latestPayloadAttributes) - if err != nil ( - fatal "Unable to customize payload attributes: %v", t.TestName, err) - ) - s = env.engine.client.forkchoiceUpdated(ForkchoiceStateV1( + + let newAttr = customizer.getPayloadAttributes(env.clMock.latestPayloadAttributes) + var fcu = ForkchoiceStateV1( headblockHash: env.clMock.latestPayloadBuilt.blockHash, safeblockHash: env.clMock.latestPayloadBuilt.blockHash, finalizedblockHash: env.clMock.latestPayloadBuilt.blockHash, - ), newPayloadAttributes, env.clMock.latestPayloadBuilt.timestamp) + ) + + var version = env.engine.version(env.clMock.latestPayloadBuilt.timestamp) + var s = env.engine.client.forkchoiceUpdated(version, fcu, some(newAttr)) s.expectPayloadStatus(PayloadExecutionStatus.syncing) # Send the previous payload to be able to continue - p = env.engine.client.newPayload(previousPayload) + var p = env.engine.client.newPayload(shadow.previousPayload) p.expectStatus(PayloadExecutionStatus.valid) - p.expectLatestValidHash(previousPayload.blockHash) + p.expectLatestValidHash(shadow.previousPayload.blockHash) # Send the new payload again @@ -384,14 +379,16 @@ method execute(cs: NewPayloadOnSyncingClientTest, env: TestEnv): bool = p.expectStatus(PayloadExecutionStatus.valid) p.expectLatestValidHash(env.clMock.latestPayloadBuilt.blockHash) - s = env.engine.client.forkchoiceUpdated(ForkchoiceStateV1( + fcu = ForkchoiceStateV1( headblockHash: env.clMock.latestPayloadBuilt.blockHash, safeblockHash: env.clMock.latestPayloadBuilt.blockHash, finalizedblockHash: env.clMock.latestPayloadBuilt.blockHash, - ), nil, env.clMock.latestPayloadBuilt.timestamp) + ) + version = env.engine.version(env.clMock.latestPayloadBuilt.timestamp) + s = env.engine.client.forkchoiceUpdated(version, fcu) s.expectPayloadStatus(PayloadExecutionStatus.valid) - return true + return true )) testCond pbRes @@ -415,59 +412,57 @@ method execute(cs: NewPayloadWithMissingFcUTest, env: TestEnv): bool = testCond ok # Get last genesis block hash - genesisHash = env.engine.client.latestHeader().Header.Hash() + let res = env.engine.client.latestHeader() + let genesisHash = res.get.blockHash # Produce blocks on the main client, these payloads will be replayed on the secondary client. - env.clMock.produceBlocks(5, BlockProcessCallbacks( + let pbRes = env.clMock.produceBlocks(5, BlockProcessCallbacks( onPayloadProducerSelected: proc(): bool = - var recipient common.Address - randomBytes(recipient[:]) + let recipient = common.EthAddress.randomBytes() + let tc = BaseTx( + recipient: some(recipient), + txType: cs.txType, + gasLimit: 75000, + ) # Send at least one transaction per payload - _, err = env.sendNextTx( - env.clMock.nextBlockProducer, - BaseTx( - recipient: &recipient, - txType: cs.txType, - gasLimit: 75000, - ), - ) - if err != nil ( - fatal "Error trying to send transaction: %v", t.TestName, err) - ) - ), + let ok = env.sendNextTx(env.clMock.nextBlockProducer, tc) + testCond ok: + fatal "Error trying to send transaction" + return true + , onGetPayload: proc(): bool = # Check that the transaction was included - if len(env.clMock.latestPayloadBuilt.Transactions) == 0 ( - fatal "Client failed to include the expected transaction in payload built", t.TestName) - ) - ), + testCond len(env.clMock.latestPayloadBuilt.transactions) > 0: + fatal "Client failed to include the expected transaction in payload built" + return true )) + testCond pbRes var sec = env.addEngine() - + let start = env.clMock.firstPoSBlockNumber.get # Send each payload in the correct order but skip the ForkchoiceUpdated for each - for i = env.clMock.firstPoSBlockNumber.Uint64(); i <= env.clMock.latestHeadNumber.Uint64(); i++ ( - payload = env.clMock.executedPayloadHistory[i] - p = sec.newPayload(payload) + for i in start..env.clMock.latestHeadNumber.uint64: + let payload = env.clMock.executedPayloadHistory[i] + let p = sec.client.newPayload(payload) p.expectStatus(PayloadExecutionStatus.valid) p.expectLatestValidHash(payload.blockHash) - ) # Verify that at this point, the client's head still points to the last non-PoS block - r = sec.latestHeader() + let r = sec.client.latestHeader() r.expectHash(genesisHash) # Verify that the head correctly changes after the last ForkchoiceUpdated - fcU = ForkchoiceStateV1( - headblockHash: env.clMock.executedPayloadHistory[env.clMock.latestHeadNumber.Uint64()].blockHash, - safeblockHash: env.clMock.executedPayloadHistory[env.clMock.latestHeadNumber.Uint64()-1].blockHash, - finalizedblockHash: env.clMock.executedPayloadHistory[env.clMock.latestHeadNumber.Uint64()-2].blockHash, + let fcU = ForkchoiceStateV1( + headblockHash: env.clMock.executedPayloadHistory[env.clMock.latestHeadNumber.uint64].blockHash, + safeblockHash: env.clMock.executedPayloadHistory[env.clMock.latestHeadNumber.uint64-1].blockHash, + finalizedblockHash: env.clMock.executedPayloadHistory[env.clMock.latestHeadNumber.uint64-2].blockHash, ) - p = sec.forkchoiceUpdated(fcU, nil, env.clMock.latestHeader.Time) + let version = sec.version(env.clMock.latestHeader.timestamp) + let p = sec.client.forkchoiceUpdated(version, fcU) p.expectPayloadStatus(PayloadExecutionStatus.valid) p.expectLatestValidHash(fcU.headblockHash) # Now the head should've changed to the latest PoS block - s = sec.latestHeader() - s.expectHash(fcU.headblockHash) -) + let s = sec.client.latestHeader() + s.expectHash(ethHash fcU.headblockHash) + return true diff --git a/hive_integration/nodocker/engine/engine/payload_id.nim b/hive_integration/nodocker/engine/engine/payload_id.nim index da211a836..285a00f44 100644 --- a/hive_integration/nodocker/engine/engine/payload_id.nim +++ b/hive_integration/nodocker/engine/engine/payload_id.nim @@ -9,7 +9,8 @@ # according to those terms. import - std/strutils, + std/[strutils, typetraits], + chronicles, ./engine_spec type @@ -36,6 +37,16 @@ method withMainFork(cs: UniquePayloadIDTest, fork: EngineFork): BaseSpec = method getName(cs: UniquePayloadIDTest): string = "Unique Payload ID - " & $cs.fieldModification +func plusOne(x: FixedBytes[32]): FixedBytes[32] = + var z = x.bytes + z[0] = z[0] + 1.byte + FixedBytes[32](z) + +func plusOne(x: Address): Address = + var z = distinctBase x + z[0] = z[0] + 1.byte + Address(z) + # Check that the payload id returned on a forkchoiceUpdated call is different # when the attributes change method execute(cs: UniquePayloadIDTest, env: TestEnv): bool = @@ -43,56 +54,63 @@ method execute(cs: UniquePayloadIDTest, env: TestEnv): bool = let ok = waitFor env.clMock.waitForTTD() testCond ok - env.clMock.produceSingleBlock(BlockProcessCallbacks( + let pbRes = env.clMock.produceSingleBlock(BlockProcessCallbacks( onPayloadAttributesGenerated: proc(): bool = - payloadAttributes = env.clMock.latestPayloadAttributes - case cs.fieldModification ( + var attr = env.clMock.latestPayloadAttributes + case cs.fieldModification of PayloadAttributesIncreasetimestamp: - payloadAttributes.timestamp += 1 + attr.timestamp = w3Qty(attr.timestamp, 1) of PayloadAttributesRandom: - payloadAttributes.Random[0] = payloadAttributes.Random[0] + 1 + attr.prevRandao = attr.prevRandao.plusOne of PayloadAttributesSuggestedFeerecipient: - payloadAttributes.SuggestedFeeRecipient[0] = payloadAttributes.SuggestedFeeRecipient[0] + 1 + attr.suggestedFeeRecipient = attr.suggestedFeeRecipient.plusOne of PayloadAttributesAddWithdrawal: - newWithdrawal = &types.Withdrawal() - payloadAttributes.Withdrawals = append(payloadAttributes.Withdrawals, newWithdrawal) + let newWithdrawal = WithdrawalV1() + var wd = attr.withdrawals.get + wd.add newWithdrawal + attr.withdrawals = some(wd) of PayloadAttributesRemoveWithdrawal: - payloadAttributes.Withdrawals = payloadAttributes.Withdrawals[1:] + var wd = attr.withdrawals.get + wd.delete(0) + attr.withdrawals = some(wd) of PayloadAttributesModifyWithdrawalAmount, PayloadAttributesModifyWithdrawalIndex, PayloadAttributesModifyWithdrawalValidator, PayloadAttributesModifyWithdrawalAddress: - if len(payloadAttributes.Withdrawals) == 0 ( - fatal "Cannot modify withdrawal when there are no withdrawals") - ) - modifiedWithdrawal = *payloadAttributes.Withdrawals[0] - case cs.fieldModification ( + testCond attr.withdrawals.isSome: + fatal "Cannot modify withdrawal when there are no withdrawals" + var wds = attr.withdrawals.get + testCond wds.len > 0: + fatal "Cannot modify withdrawal when there are no withdrawals" + + var wd = wds[0] + case cs.fieldModification of PayloadAttributesModifyWithdrawalAmount: - modifiedWithdrawal.Amount += 1 + wd.amount = w3Qty(wd.amount, 1) of PayloadAttributesModifyWithdrawalIndex: - modifiedWithdrawal.Index += 1 + wd.index = w3Qty(wd.index, 1) of PayloadAttributesModifyWithdrawalValidator: - modifiedWithdrawal.Validator += 1 + wd.validatorIndex = w3Qty(wd.validatorIndex, 1) of PayloadAttributesModifyWithdrawalAddress: - modifiedWithdrawal.Address[0] = modifiedWithdrawal.Address[0] + 1 - ) - payloadAttributes.Withdrawals = append(types.Withdrawals(&modifiedWithdrawal), payloadAttributes.Withdrawals[1:]...) + wd.address = wd.address.plusOne + else: + fatal "Unknown field change", field=cs.fieldModification + return false + + wds[0] = wd + attr.withdrawals = some(wds) of PayloadAttributesParentBeaconRoot: - if payloadAttributes.BeaconRoot == nil ( - fatal "Cannot modify parent beacon root when there is no parent beacon root") - ) - newBeaconRoot = *payloadAttributes.BeaconRoot - newBeaconRoot[0] = newBeaconRoot[0] + 1 - payloadAttributes.BeaconRoot = &newBeaconRoot - default: - fatal "Unknown field change: %s", cs.fieldModification) - ) + testCond attr.parentBeaconBlockRoot.isSome: + fatal "Cannot modify parent beacon root when there is no parent beacon root" + let newBeaconRoot = attr.parentBeaconBlockRoot.get.plusOne + attr.parentBeaconBlockRoot = some(newBeaconRoot) # Request the payload with the modified attributes and add the payload ID to the list of known IDs let version = env.engine.version(env.clMock.latestHeader.timestamp) - let r = env.engine.client.forkchoiceUpdated(version, env.clMock.latestForkchoice, some(payloadAttributes)) + let r = env.engine.client.forkchoiceUpdated(version, env.clMock.latestForkchoice, some(attr)) r.expectNoError() - env.clMock.addPayloadID(env.engine, r.get.payloadID.get) - ), + testCond env.clMock.addPayloadID(env.engine, r.get.payloadID.get) + return true )) -) + testCond pbRes + return true diff --git a/hive_integration/nodocker/engine/engine/prev_randao.nim b/hive_integration/nodocker/engine/engine/prev_randao.nim index b4cc854f0..7460ffd85 100644 --- a/hive_integration/nodocker/engine/engine/prev_randao.nim +++ b/hive_integration/nodocker/engine/engine/prev_randao.nim @@ -12,7 +12,8 @@ import std/strutils, eth/common, chronicles, - ./engine_spec + ./engine_spec, + ../helper type PrevRandaoTransactionTest* = ref object of EngineSpec @@ -24,13 +25,6 @@ type currentTxIndex: int txs: seq[Transaction] -proc checkPrevRandaoValue(client: RpcClient, expectedPrevRandao: common.Hash256, blockNumber: uint64): bool = - let storageKey = blockNumber.u256 - let r = client.storageAt(prevRandaoContractAddr, storageKey) - let expected = UInt256.fromBytesBE(expectedPrevRandao.data) - r.expectStorageEqual(expected) - return true - method withMainFork(cs: PrevRandaoTransactionTest, fork: EngineFork): BaseSpec = var res = cs.clone() res.mainFork = fork diff --git a/hive_integration/nodocker/engine/engine/reorg.nim b/hive_integration/nodocker/engine/engine/reorg.nim index 0626e117e..dac7a0429 100644 --- a/hive_integration/nodocker/engine/engine/reorg.nim +++ b/hive_integration/nodocker/engine/engine/reorg.nim @@ -10,9 +10,11 @@ import std/strutils, + eth/common, chronicles, ./engine_spec, - ../cancun/customizer + ../cancun/customizer, + ../helper type SidechainReOrgTest* = ref object of EngineSpec @@ -37,79 +39,74 @@ method execute(cs: SidechainReOrgTest, env: TestEnv): bool = # Produce two payloads, send fcU with first payload, check transaction outcome, then reorg, check transaction outcome again # This single transaction will change its outcome based on the payload - tx, err = t.sendNextTx( - t.TestContext, - t.Engine, - BaseTx( - recipient: &globals.PrevRandaoContractAddr, - amount: big0, - payload: nil, - txType: cs.txType, - gasLimit: 75000, - ), + let tc = BaseTx( + recipient: some(prevRandaoContractAddr), + txType: cs.txType, + gasLimit: 75000, ) - if err != nil ( - fatal "Error trying to send transaction: %v", t.TestName, err) - ) - info "sent tx %v", t.TestName, tx.Hash()) + let ok2 = env.sendNextTx(env.engine, tc) + testCond ok2: + fatal "Error trying to send transaction" - env.clMock.produceSingleBlock(BlockProcessCallbacks( + info "sent tx" + + let pbRes = env.clMock.produceSingleBlock(BlockProcessCallbacks( onNewPayloadBroadcast: proc(): bool = # At this point the CLMocker has a payload that will result in a specific outcome, # we can produce an alternative payload, send it, fcU to it, and verify the changes - alternativePrevRandao = common.Hash() - rand.Read(alternativePrevRandao[:]) - timestamp = env.clMock.latestPayloadBuilt.timestamp + 1 - payloadAttributes, err = (BasePayloadAttributesCustomizer( - Timestamp: ×tamp, - Random: &alternativePrevRandao, - )).getPayloadAttributes(env.clMock.LatestPayloadAttributes) - if err != nil ( - fatal "Unable to customize payload attributes: %v", t.TestName, err) + let alternativePrevRandao = common.Hash256.randomBytes() + let timestamp = w3Qty(env.clMock.latestPayloadBuilt.timestamp, 1) + let customizer = BasePayloadAttributesCustomizer( + timestamp: some(timestamp.uint64), + prevRandao: some(alternativePrevRandao), ) - r = env.engine.client.forkchoiceUpdated( - &env.clMock.latestForkchoice, - payloadAttributes, - env.clMock.latestPayloadBuilt.timestamp, - ) + let attr = customizer.getPayloadAttributes(env.clMock.latestPayloadAttributes) + + var version = env.engine.version(env.clMock.latestPayloadBuilt.timestamp) + let r = env.engine.client.forkchoiceUpdated(version, env.clMock.latestForkchoice, some(attr)) r.expectNoError() - await sleepAsync(env.clMock.PayloadProductionClientDelay) + let period = chronos.seconds(env.clMock.payloadProductionClientDelay) + waitFor sleepAsync(period) - g = env.engine.client.getPayload(r.Response.PayloadID, payloadAttributes) + version = env.engine.version(attr.timestamp) + let g = env.engine.client.getPayload(r.get.payloadID.get, version) g.expectNoError() - alternativePayload = g.Payload - if len(alternativePayload.Transactions) == 0 ( - fatal "alternative payload does not contain the prevRandao opcode tx", t.TestName) - ) - s = env.engine.client.newPayload(alternativePayload) + let alternativePayload = g.get.executionPayload + testCond len(alternativePayload.transactions) > 0: + fatal "alternative payload does not contain the prevRandao opcode tx" + + let s = env.engine.client.newPayload(alternativePayload) s.expectStatus(PayloadExecutionStatus.valid) s.expectLatestValidHash(alternativePayload.blockHash) # We sent the alternative payload, fcU to it - p = env.engine.client.forkchoiceUpdated(api.ForkchoiceStateV1( + let fcu = ForkchoiceStateV1( headBlockHash: alternativePayload.blockHash, safeBlockHash: env.clMock.latestForkchoice.safeBlockHash, finalizedBlockHash: env.clMock.latestForkchoice.finalizedBlockHash, - ), nil, alternativePayload.timestamp) + ) + version = env.engine.version(alternativePayload.timestamp) + let p = env.engine.client.forkchoiceUpdated(version, fcu) p.expectPayloadStatus(PayloadExecutionStatus.valid) # PrevRandao should be the alternative prevRandao we sent - checkPrevRandaoValue(t, alternativePrevRandao, alternativePayload.blockNumber) - ), + testCond checkPrevRandaoValue(env.engine.client, alternativePrevRandao, alternativePayload.blockNumber.uint64) + return true )) + testCond pbRes # The reorg actually happens after the CLMocker continues, # verify here that the reorg was successful - latestBlockNum = env.clMock.LatestHeadNumber.Uint64() - checkPrevRandaoValue(t, env.clMock.PrevRandaoHistory[latestBlockNum], latestBlockNum) - -) + let latestBlockNum = env.clMock.latestHeadNumber.uint64 + testCond checkPrevRandaoValue(env.engine.client, env.clMock.prevRandaoHistory[latestBlockNum], latestBlockNum) + return true # Test performing a re-org that involves removing or modifying a transaction type TransactionReOrgScenario = enum + TransactionNoScenario TransactionReOrgScenarioReOrgOut = "Re-Org Out" TransactionReOrgScenarioReOrgBackIn = "Re-Org Back In" TransactionReOrgScenarioReOrgDifferentBlock = "Re-Org to Different Block" @@ -117,8 +114,8 @@ type type TransactionReOrgTest* = ref object of EngineSpec - TransactionCount int - Scenario TransactionReOrgScenario + transactionCount*: int + scenario*: TransactionReOrgScenario method withMainFork(cs: TransactionReOrgTest, fork: EngineFork): BaseSpec = var res = cs.clone() @@ -126,10 +123,9 @@ method withMainFork(cs: TransactionReOrgTest, fork: EngineFork): BaseSpec = return res method getName(cs: TransactionReOrgTest): string = - name = "Transaction Re-Org" - if s.Scenario != "" ( - name = fmt.Sprintf("%s, %s", name, s.Scenario) - ) + var name = "Transaction Re-Org" + if cs.scenario != TransactionNoScenario: + name.add ", " & $cs.scenario return name # Test transaction status after a forkchoiceUpdated re-orgs to an alternative hash where a transaction is not present @@ -140,11 +136,11 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool = # Produce blocks before starting the test (So we don't try to reorg back to the genesis block) testCond env.clMock.produceBlocks(5, BlockProcessCallbacks()) - +#[ # Create transactions that modify the state in order to check after the reorg. var ( err error - txCount = spec.TransactionCount + txCount = cs.transactionCount sstoreContractAddr = common.HexToAddress("0000000000000000000000000000000000000317") ) @@ -158,7 +154,6 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool = data = common.LeftPadBytes([]byte(byte(i)), 32) t.Logf("transactionReorg, i=%v, data=%v\n", i, data) return t.sendNextTx( - t.TestContext, t.Engine, BaseTx( recipient: &sstoreContractAddr, @@ -173,7 +168,7 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool = ) var ( - altPayload ExecutableData + shadow.payload ExecutableData nextTx Transaction tx Transaction ) @@ -184,7 +179,7 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool = env.clMock.produceSingleBlock(BlockProcessCallbacks( OnPayloadAttributesGenerated: proc(): bool = # At this point we have not broadcast the transaction. - if spec.Scenario == TransactionReOrgScenarioReOrgOut ( + if cs.scenario == TransactionReOrgScenarioReOrgOut ( # Any payload we get should not contain any payloadAttributes = env.clMock.LatestPayloadAttributes rand.Read(payloadAttributes.Random[:]) @@ -195,25 +190,25 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool = ) g = env.engine.client.getPayload(r.Response.PayloadID, payloadAttributes) g.expectNoError() - altPayload = &g.Payload + shadow.payload = &g.Payload - if len(altPayload.Transactions) != 0 ( - fatal "Empty payload contains transactions: %v", t.TestName, altPayload) + if len(shadow.payload.transactions) != 0 ( + fatal "Empty payload contains transactions: %v", t.TestName, shadow.payload) ) ) - if spec.Scenario != TransactionReOrgScenarioReOrgBackIn ( + if cs.scenario != TransactionReOrgScenarioReOrgBackIn ( # At this point we can broadcast the transaction and it will be included in the next payload # Data is the key where a `1` will be stored tx, err = sendTransaction(i) - if err != nil ( + testCond ok: fatal "Error trying to send transaction: %v", t.TestName, err) ) # Get the receipt ctx, cancel = context.WithTimeout(t.TestContext, globals.RPCTimeout) defer cancel() - receipt, _ = t.Eth.TransactionReceipt(ctx, tx.Hash()) + receipt, _ = t.Eth.TransactionReceipt(ctx, shadow.txHash) if receipt != nil ( fatal "Receipt obtained before tx included in block: %v", t.TestName, receipt) ) @@ -222,36 +217,36 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool = ), onGetpayload: proc(): bool = # Check that indeed the payload contains the transaction - if tx != nil ( + if shadow.tx.isSome: if !TransactionInPayload(env.clMock.latestPayloadBuilt, tx) ( fatal "Payload built does not contain the transaction: %v", t.TestName, env.clMock.latestPayloadBuilt) ) ) - if spec.Scenario == TransactionReOrgScenarioReOrgDifferentBlock || spec.Scenario == TransactionReOrgScenarioNewPayloadOnRevert ( + if cs.scenario == TransactionReOrgScenarioReOrgDifferentBlock || cs.scenario == TransactionReOrgScenarioNewPayloadOnRevert ( # Create side payload with different hash var err error customizer = &CustomPayloadData( extraData: &([]byte(0x01)), ) - altPayload, err = customizer.customizePayload(env.clMock.latestPayloadBuilt) - if err != nil ( + shadow.payload, err = customizer.customizePayload(env.clMock.latestPayloadBuilt) + testCond ok: fatal "Error creating reorg payload %v", err) ) - if altPayload.parentHash != env.clMock.latestPayloadBuilt.parentHash ( - fatal "Incorrect parent hash for payloads: %v != %v", t.TestName, altPayload.parentHash, env.clMock.latestPayloadBuilt.parentHash) + if shadow.payload.parentHash != env.clMock.latestPayloadBuilt.parentHash ( + fatal "Incorrect parent hash for payloads: %v != %v", t.TestName, shadow.payload.parentHash, env.clMock.latestPayloadBuilt.parentHash) ) - if altPayload.blockHash == env.clMock.latestPayloadBuilt.blockHash ( - fatal "Incorrect hash for payloads: %v == %v", t.TestName, altPayload.blockHash, env.clMock.latestPayloadBuilt.blockHash) + if shadow.payload.blockHash == env.clMock.latestPayloadBuilt.blockHash ( + fatal "Incorrect hash for payloads: %v == %v", t.TestName, shadow.payload.blockHash, env.clMock.latestPayloadBuilt.blockHash) ) - elif spec.Scenario == TransactionReOrgScenarioReOrgBackIn ( + elif cs.scenario == TransactionReOrgScenarioReOrgBackIn ( # At this point we broadcast the transaction and request a new payload from the client that must # contain the transaction. # Since we are re-orging out and back in on the next block, the verification of this transaction # being included happens on the next block - nextTx, err = sendTransaction(i) - if err != nil ( + shadow.nextTx, err = sendTransaction(i) + testCond ok: fatal "Error trying to send transaction: %v", t.TestName, err) ) @@ -275,7 +270,7 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool = g = env.engine.client.getPayload(f.Response.PayloadID, env.clMock.LatestPayloadAttributes) g.expectNoError() - if !TransactionInPayload(g.Payload, nextTx) ( + if !TransactionInPayload(g.Payload, shadow.nextTx) ( fatal "Payload built does not contain the transaction: %v", t.TestName, g.Payload) ) @@ -291,59 +286,59 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool = ) ), onNewPayloadBroadcast: proc(): bool = - if tx != nil ( + if shadow.tx.isSome: # Get the receipt ctx, cancel = context.WithTimeout(t.TestContext, globals.RPCTimeout) defer cancel() - receipt, _ = t.Eth.TransactionReceipt(ctx, tx.Hash()) + receipt, _ = t.Eth.TransactionReceipt(ctx, shadow.txHash) if receipt != nil ( fatal "Receipt obtained before tx included in block (NewPayload): %v", t.TestName, receipt) ) ) ), onForkchoiceBroadcast: proc(): bool = - if spec.Scenario != TransactionReOrgScenarioReOrgBackIn ( + if cs.scenario != TransactionReOrgScenarioReOrgBackIn ( # Transaction is now in the head of the canonical chain, re-org and verify it's removed # Get the receipt - txt = env.engine.client.txReceipt(tx.Hash()) + txt = env.engine.client.txReceipt(shadow.txHash) txt.expectBlockHash(env.clMock.latestForkchoice.headBlockHash) - if altPayload.parentHash != env.clMock.latestPayloadBuilt.parentHash ( - fatal "Incorrect parent hash for payloads: %v != %v", t.TestName, altPayload.parentHash, env.clMock.latestPayloadBuilt.parentHash) + if shadow.payload.parentHash != env.clMock.latestPayloadBuilt.parentHash ( + fatal "Incorrect parent hash for payloads: %v != %v", t.TestName, shadow.payload.parentHash, env.clMock.latestPayloadBuilt.parentHash) ) - if altPayload.blockHash == env.clMock.latestPayloadBuilt.blockHash ( - fatal "Incorrect hash for payloads: %v == %v", t.TestName, altPayload.blockHash, env.clMock.latestPayloadBuilt.blockHash) + if shadow.payload.blockHash == env.clMock.latestPayloadBuilt.blockHash ( + fatal "Incorrect hash for payloads: %v == %v", t.TestName, shadow.payload.blockHash, env.clMock.latestPayloadBuilt.blockHash) ) - if altPayload == nil ( + if shadow.payload == nil ( fatal "No payload to re-org to", t.TestName) ) - r = env.engine.client.newPayload(altPayload) + r = env.engine.client.newPayload(shadow.payload) r.expectStatus(PayloadExecutionStatus.valid) - r.expectLatestValidHash(altPayload.blockHash) + r.expectLatestValidHash(shadow.payload.blockHash) s = env.engine.client.forkchoiceUpdated(api.ForkchoiceStateV1( - headBlockHash: altPayload.blockHash, + headBlockHash: shadow.payload.blockHash, safeBlockHash: env.clMock.latestForkchoice.safeBlockHash, finalizedBlockHash: env.clMock.latestForkchoice.finalizedBlockHash, - ), nil, altPayload.timestamp) + ), nil, shadow.payload.timestamp) s.expectPayloadStatus(PayloadExecutionStatus.valid) p = env.engine.client.headerByNumber(Head) - p.expectHash(altPayload.blockHash) + p.expectHash(shadow.payload.blockHash) - txt = env.engine.client.txReceipt(tx.Hash()) - if spec.Scenario == TransactionReOrgScenarioReOrgOut ( + txt = env.engine.client.txReceipt(shadow.txHash) + if cs.scenario == TransactionReOrgScenarioReOrgOut ( if txt.Receipt != nil ( receiptJson, _ = json.MarshalIndent(txt.Receipt, "", " ") fatal "Receipt was obtained when the tx had been re-org'd out: %s", t.TestName, receiptJson) ) - elif spec.Scenario == TransactionReOrgScenarioReOrgDifferentBlock || spec.Scenario == TransactionReOrgScenarioNewPayloadOnRevert ( - txt.expectBlockHash(altPayload.blockHash) + elif cs.scenario == TransactionReOrgScenarioReOrgDifferentBlock || cs.scenario == TransactionReOrgScenarioNewPayloadOnRevert ( + txt.expectBlockHash(shadow.payload.blockHash) ) # Re-org back - if spec.Scenario == TransactionReOrgScenarioNewPayloadOnRevert ( + if cs.scenario == TransactionReOrgScenarioNewPayloadOnRevert ( r = env.engine.client.newPayload(env.clMock.latestPayloadBuilt) r.expectStatus(PayloadExecutionStatus.valid) r.expectLatestValidHash(env.clMock.latestPayloadBuilt.blockHash) @@ -351,21 +346,21 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool = env.clMock.BroadcastForkchoiceUpdated(env.clMock.latestForkchoice, nil, 1) ) - if tx != nil ( + if shadow.tx.isSome: # Now it should be back with main payload - txt = env.engine.client.txReceipt(tx.Hash()) + txt = env.engine.client.txReceipt(shadow.txHash) txt.expectBlockHash(env.clMock.latestForkchoice.headBlockHash) - if spec.Scenario != TransactionReOrgScenarioReOrgBackIn ( + if cs.scenario != TransactionReOrgScenarioReOrgBackIn ( tx = nil ) ) - if spec.Scenario == TransactionReOrgScenarioReOrgBackIn && i > 0 ( + if cs.scenario == TransactionReOrgScenarioReOrgBackIn && i > 0 ( # Reasoning: Most of the clients do not re-add blob transactions to the pool # after a re-org, so we need to wait until the next tx is sent to actually # verify. - tx = nextTx + tx = shadow.nextTx ) ), @@ -373,7 +368,7 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool = ) - if tx != nil ( + if shadow.tx.isSome: # Produce one last block and verify that the block contains the transaction env.clMock.produceSingleBlock(BlockProcessCallbacks( onForkchoiceBroadcast: proc(): bool = @@ -383,7 +378,7 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool = # Get the receipt ctx, cancel = context.WithTimeout(t.TestContext, globals.RPCTimeout) defer cancel() - receipt, _ = t.Eth.TransactionReceipt(ctx, tx.Hash()) + receipt, _ = t.Eth.TransactionReceipt(ctx, shadow.txHash) if receipt == nil ( fatal "Receipt not obtained after tx included in block: %v", t.TestName, receipt) ) @@ -393,17 +388,18 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool = ) ) +]# # Test that performing a re-org back into a previous block of the canonical chain does not produce errors and the chain # is still capable of progressing. type ReOrgBackToCanonicalTest* = ref object of EngineSpec # Depth of the re-org to back in the canonical chain - ReOrgDepth uint64 + reOrgDepth*: int # Number of transactions to send on each payload - TransactionPerPayload uint64 + transactionPerPayload*: int # Whether to execute a sidechain payload on the re-org - ExecuteSidePayloadOnReOrg bool + executeSidePayloadOnReOrg*: bool method withMainFork(cs: ReOrgBackToCanonicalTest, fork: EngineFork): BaseSpec = var res = cs.clone() @@ -411,19 +407,16 @@ method withMainFork(cs: ReOrgBackToCanonicalTest, fork: EngineFork): BaseSpec = return res method getName(cs: ReOrgBackToCanonicalTest): string = - name = fmt.Sprintf("Re-Org Back into Canonical Chain, Depth=%d", s.ReOrgDepth) + var name = "Re-Org Back into Canonical Chain, Depth=" & $cs.reOrgDepth - if s.ExecuteSidePayloadOnReOrg ( - name += ", Execute Side Payload on Re-Org" - ) + if cs.executeSidePayloadOnReOrg: + name.add ", Execute Side Payload on Re-Org" return name proc getDepth(cs: ReOrgBackToCanonicalTest): int = - if s.ReOrgDepth == 0 ( + if cs.reOrgDepth == 0: return 3 - ) - return s.ReOrgDepth -) + return cs.reOrgDepth # Test that performing a re-org back into a previous block of the canonical chain does not produce errors and the chain # is still capable of progressing. @@ -433,25 +426,25 @@ method execute(cs: ReOrgBackToCanonicalTest, env: TestEnv): bool = testCond ok # Check the CLMock configured safe and finalized - if env.clMock.slotsToSafe.Cmp(new(big.Int).SetUint64(spec.ReOrgDepth)) <= 0 ( - fatal "[TEST ISSUE] CLMock configured slots to safe less than re-org depth: %v <= %v", t.TestName, env.clMock.slotsToSafe, spec.ReOrgDepth) - ) - if env.clMock.slotsToFinalized.Cmp(new(big.Int).SetUint64(spec.ReOrgDepth)) <= 0 ( - fatal "[TEST ISSUE] CLMock configured slots to finalized less than re-org depth: %v <= %v", t.TestName, env.clMock.slotsToFinalized, spec.ReOrgDepth) - ) + testCond env.clMock.slotsToSafe > cs.reOrgDepth: + fatal "[TEST ISSUE] CLMock configured slots to safe less than re-org depth" + + testCond env.clMock.slotsToFinalized > cs.reOrgDepth: + fatal "[TEST ISSUE] CLMock configured slots to finalized less than re-org depth" # Produce blocks before starting the test (So we don't try to reorg back to the genesis block) testCond env.clMock.produceBlocks(5, BlockProcessCallbacks()) +#[ # We are going to reorg back to a previous hash several times previousHash = env.clMock.latestForkchoice.headBlockHash previousTimestamp = env.clMock.latestPayloadBuilt.timestamp - if spec.ExecuteSidePayloadOnReOrg ( + if cs.executeSidePayloadOnReOrg ( var ( - sidePayload ExecutableData - sidePayloadParentForkchoice api.ForkchoiceStateV1 - sidePayloadParentTimestamp uint64 + shadow.payload ExecutableData + shadow.parentForkchoice api.ForkchoiceStateV1 + shadow.parentTimestamp uint64 ) env.clMock.produceSingleBlock(BlockProcessCallbacks( OnPayloadAttributesGenerated: proc(): bool = @@ -464,13 +457,13 @@ method execute(cs: ReOrgBackToCanonicalTest, env: TestEnv): bool = ) g = env.engine.client.getPayload(r.Response.PayloadID, payloadAttributes) g.expectNoError() - sidePayload = &g.Payload - sidePayloadParentForkchoice = env.clMock.latestForkchoice - sidePayloadParentTimestamp = env.clMock.latestHeader.Time + shadow.payload = &g.Payload + shadow.parentForkchoice = env.clMock.latestForkchoice + shadow.parentTimestamp = env.clMock.latestHeader.Time ), )) # Continue producing blocks until we reach the depth of the re-org - testCond env.clMock.produceBlocks(int(spec.GetDepth()-1), BlockProcessCallbacks( + testCond env.clMock.produceBlocks(int(cs.GetDepth()-1), BlockProcessCallbacks( onPayloadProducerSelected: proc(): bool = # Send a transaction on each payload of the canonical chain var err error @@ -485,9 +478,9 @@ method execute(cs: ReOrgBackToCanonicalTest, env: TestEnv): bool = gasLimit: 75000, ForkConfig: t.ForkConfig, ), - spec.TransactionPerPayload, + cs.transactionPerPayload, ) - if err != nil ( + testCond ok: fatal "Error trying to send transactions: %v", t.TestName, err) ) ), @@ -498,19 +491,19 @@ method execute(cs: ReOrgBackToCanonicalTest, env: TestEnv): bool = onGetpayload: proc(): bool = # We are about to execute the new payload of the canonical chain, re-org back to # the side payload - f = env.engine.client.forkchoiceUpdated(sidePayloadParentForkchoice, nil, sidePayloadParentTimestamp) + f = env.engine.client.forkchoiceUpdated(shadow.parentForkchoice, nil, shadow.parentTimestamp) f.expectPayloadStatus(PayloadExecutionStatus.valid) - f.expectLatestValidHash(sidePayloadParentForkchoice.headBlockHash) + f.expectLatestValidHash(shadow.parentForkchoice.headBlockHash) # Execute the side payload - n = env.engine.client.newPayload(sidePayload) + n = env.engine.client.newPayload(shadow.payload) n.expectStatus(PayloadExecutionStatus.valid) - n.expectLatestValidHash(sidePayload.blockHash) + n.expectLatestValidHash(shadow.payload.blockHash) # At this point the next canonical payload will be executed by the CL mock, so we can # continue producing blocks ), )) else: - testCond env.clMock.produceBlocks(int(spec.GetDepth()), BlockProcessCallbacks( + testCond env.clMock.produceBlocks(int(cs.GetDepth()), BlockProcessCallbacks( onForkchoiceBroadcast: proc(): bool = # Send a fcU with the headBlockHash pointing back to the previous block forkchoiceUpdatedBack = api.ForkchoiceStateV1( @@ -533,21 +526,22 @@ method execute(cs: ReOrgBackToCanonicalTest, env: TestEnv): bool = # Verify that the client is pointing to the latest payload sent r = env.engine.client.headerByNumber(Head) r.expectHash(env.clMock.latestPayloadBuilt.blockHash) - ) +]# type ReOrgBackFromSyncingTest* = ref object of EngineSpec + Shadow = ref object + payloads: seq[ExecutableData] + method withMainFork(cs: ReOrgBackFromSyncingTest, fork: EngineFork): BaseSpec = var res = cs.clone() res.mainFork = fork return res method getName(cs: ReOrgBackFromSyncingTest): string = - name = "Re-Org Back to Canonical Chain From Syncing Chain" - return name -) + "Re-Org Back to Canonical Chain From Syncing Chain" # Test that performs a re-org back to the canonical chain after re-org to syncing/unavailable chain. method execute(cs: ReOrgBackFromSyncingTest, env: TestEnv): bool = @@ -556,72 +550,71 @@ method execute(cs: ReOrgBackFromSyncingTest, env: TestEnv): bool = testCond ok # Produce an alternative chain - sidechainPayloads = make([]ExecutableData, 0) - testCond env.clMock.produceBlocks(10, BlockProcessCallbacks( + var shadow = Shadow() + var pbRes = env.clMock.produceBlocks(10, BlockProcessCallbacks( onPayloadProducerSelected: proc(): bool = # Send a transaction on each payload of the canonical chain - var err error - _, err = t.sendNextTx( - t.TestContext, - t.Engine, - BaseTx( - recipient: &ZeroAddr, - amount: big1, - payload: nil, - txType: cs.txType, - gasLimit: 75000, - ), + let tc = BaseTx( + recipient: some(ZeroAddr), + amount: 1.u256, + txType: cs.txType, + gasLimit: 75000, ) - if err != nil ( - fatal "Error trying to send transactions: %v", t.TestName, err) - ) - ), + let ok = env.sendNextTx(env.engine, tc) + testCond ok: + fatal "Error trying to send transactions" + return true + , onGetpayload: proc(): bool = # Check that at least one transaction made it into the payload - if len(env.clMock.latestPayloadBuilt.Transactions) == 0 ( - fatal "No transactions in payload: %v", t.TestName, env.clMock.latestPayloadBuilt) - ) + testCond len(env.clMock.latestPayloadBuilt.transactions) > 0: + fatal "No transactions in payload" + # Generate an alternative payload by simply adding extraData to the block - altParentHash = env.clMock.latestPayloadBuilt.parentHash - if len(sidechainPayloads) > 0 ( - altParentHash = sidechainPayloads[len(sidechainPayloads)-1].blockHash + var altParentHash = env.clMock.latestPayloadBuilt.parentHash + if len(shadow.payloads) > 0: + altParentHash = shadow.payloads[^1].blockHash + + let customizer = CustomPayloadData( + parentHash: some(ethHash altParentHash), + extraData: some(@[0x01.byte]), ) - customizer = &CustomPayloadData( - parentHash: &altParentHash, - extraData: &([]byte(0x01)), - ) - altPayload, err = customizer.customizePayload(env.clMock.latestPayloadBuilt) - if err != nil ( - fatal "Unable to customize payload: %v", t.TestName, err) - ) - sidechainPayloads = append(sidechainPayloads, altPayload) - ), + + let payload = customizer.customizePayload(env.clMock.latestExecutableData) + shadow.payloads.add payload + return true )) - env.clMock.produceSingleBlock(BlockProcessCallbacks( + testCond pbRes + + pbRes = env.clMock.produceSingleBlock(BlockProcessCallbacks( onGetpayload: proc(): bool = # Re-org to the unavailable sidechain in the middle of block production # to be able to re-org back to the canonical chain - r = env.engine.client.newPayload(sidechainPayloads[len(sidechainPayloads)-1]) - r.expectStatusEither(PayloadExecutionStatus.syncing, PayloadExecutionStatus.accepted) - r.expectLatestValidHash(nil) + var version = env.engine.version(shadow.payloads[^1].timestamp) + let r = env.engine.client.newPayload(version, shadow.payloads[^1]) + r.expectStatusEither([PayloadExecutionStatus.syncing, PayloadExecutionStatus.accepted]) + r.expectLatestValidHash() + # We are going to send one of the alternative payloads and fcU to it - forkchoiceUpdatedBack = api.ForkchoiceStateV1( - headBlockHash: sidechainPayloads[len(sidechainPayloads)-1].blockHash, + let fcu = ForkchoiceStateV1( + headBlockHash: shadow.payloads[^1].blockHash, safeBlockHash: env.clMock.latestForkchoice.safeBlockHash, finalizedBlockHash: env.clMock.latestForkchoice.finalizedBlockHash, ) # It is only expected that the client does not produce an error and the CL Mocker is able to progress after the re-org - s = env.engine.client.forkchoiceUpdated(forkchoiceUpdatedBack, nil, sidechainPayloads[len(sidechainPayloads)-1].timestamp) - s.expectLatestValidHash(nil) + version = env.engine.version(shadow.payloads[^1].timestamp) + let s = env.engine.client.forkchoiceUpdated(version, fcu) + s.expectLatestValidHash() s.expectPayloadStatus(PayloadExecutionStatus.syncing) # After this, the CLMocker will continue and try to re-org to canonical chain once again # CLMocker will fail the test if this is not possible, so nothing left to do. - ), + return true )) -) + testCond pbRes + return true type ReOrgPrevValidatedPayloadOnSideChainTest* = ref object of EngineSpec @@ -632,9 +625,15 @@ method withMainFork(cs: ReOrgPrevValidatedPayloadOnSideChainTest, fork: EngineFo return res method getName(cs: ReOrgPrevValidatedPayloadOnSideChainTest): string = - name = "Re-org to Previously Validated Sidechain Payload" - return name -) + "Re-org to Previously Validated Sidechain Payload" + +func toSeq(x: string): seq[byte] = + for z in x: + result.add z.byte + +func ethAddress(a, b: int): EthAddress = + result[0] = a.byte + result[1] = b.byte # Test that performs a re-org to a previously validated payload on a side chain. method execute(cs: ReOrgPrevValidatedPayloadOnSideChainTest, env: TestEnv): bool = @@ -645,99 +644,90 @@ method execute(cs: ReOrgPrevValidatedPayloadOnSideChainTest, env: TestEnv): bool # Produce blocks before starting the test testCond env.clMock.produceBlocks(5, BlockProcessCallbacks()) - var ( - sidechainPayloads = make([]ExecutableData, 0) - sidechainPayloadCount = 5 - ) + var shadow = Shadow() # Produce a canonical chain while at the same time generate a side chain to which we will re-org. - testCond env.clMock.produceBlocks(sidechainPayloadCount, BlockProcessCallbacks( + var pbRes = env.clMock.produceBlocks(5, BlockProcessCallbacks( onPayloadProducerSelected: proc(): bool = # Send a transaction on each payload of the canonical chain - var err error - _, err = t.sendNextTx( - t.TestContext, - t.Engine, - BaseTx( - recipient: &ZeroAddr, - amount: big1, - payload: nil, - txType: cs.txType, - gasLimit: 75000, - ForkConfig: t.ForkConfig, - ), + let tc = BaseTx( + recipient: some(ZeroAddr), + amount: 1.u256, + txType: cs.txType, + gasLimit: 75000, ) - if err != nil ( - fatal "Error trying to send transactions: %v", t.TestName, err) - ) - ), + let ok = env.sendNextTx(env.engine, tc) + testCond ok: + fatal "Error trying to send transactions" + return true + , onGetpayload: proc(): bool = # Check that at least one transaction made it into the payload - if len(env.clMock.latestPayloadBuilt.Transactions) == 0 ( - fatal "No transactions in payload: %v", t.TestName, env.clMock.latestPayloadBuilt) - ) - # The side chain will consist simply of the same payloads with extra data appended - extraData = []byte("side") - customData = CustomPayloadData( - extraData: &extraData, - ) - if len(sidechainPayloads) > 0 ( - customData.parentHash = &sidechainPayloads[len(sidechainPayloads)-1].blockHash - ) - altPayload, err = customData.customizePayload(env.clMock.latestPayloadBuilt) - if err != nil ( - fatal "Unable to customize payload: %v", t.TestName, err) - ) - sidechainPayloads = append(sidechainPayloads, altPayload) + testCond len(env.clMock.latestPayloadBuilt.transactions) > 0: + fatal "No transactions in payload" - r = env.engine.client.newPayload(altPayload) + # The side chain will consist simply of the same payloads with extra data appended + var customData = CustomPayloadData( + extraData: some(toSeq("side")), + ) + + if len(shadow.payloads) > 0: + customData.parentHash = some(ethHash shadow.payloads[^1].blockHash) + + let payload = customData.customizePayload(env.clMock.latestExecutableData) + shadow.payloads.add payload + + let version = env.engine.version(payload.timestamp) + let r = env.engine.client.newPayload(version, payload) r.expectStatus(PayloadExecutionStatus.valid) - r.expectLatestValidHash(altPayload.blockHash) - ), + r.expectLatestValidHash(payload.blockHash) + return true )) + testCond pbRes + # Attempt to re-org to one of the sidechain payloads, but not the leaf, # and also build a new payload from this sidechain. - env.clMock.produceSingleBlock(BlockProcessCallbacks( + pbRes = env.clMock.produceSingleBlock(BlockProcessCallbacks( onGetpayload: proc(): bool = - var ( - prevRandao = common.Hash() - suggestedFeeRecipient = common.Address(0x12, 0x34) - ) - rand.Read(prevRandao[:]) - payloadAttributesCustomizer = &BasePayloadAttributesCustomizer( - Random: &prevRandao, - SuggestedFeerecipient: &suggestedFeeRecipient, + var + prevRandao = common.Hash256.randomBytes() + suggestedFeeRecipient = ethAddress(0x12, 0x34) + + let payloadAttributesCustomizer = BasePayloadAttributesCustomizer( + prevRandao: some(prevRandao), + suggestedFeerecipient: some(suggestedFeeRecipient), ) - reOrgPayload = sidechainPayloads[len(sidechainPayloads)-2] - reOrgPayloadAttributes = sidechainPayloads[len(sidechainPayloads)-1].PayloadAttributes - - newPayloadAttributes, err = payloadAttributesCustomizer.getPayloadAttributes(reOrgPayloadAttributes) - if err != nil ( - fatal "Unable to customize payload attributes: %v", t.TestName, err) - ) - - r = env.engine.client.forkchoiceUpdated(api.ForkchoiceStateV1( + let reOrgPayload = shadow.payloads[^2] + let reOrgPayloadAttributes = shadow.payloads[^1].attr + let newPayloadAttributes = payloadAttributesCustomizer.getPayloadAttributes(reOrgPayloadAttributes) + let fcu = ForkchoiceStateV1( headBlockHash: reOrgPayload.blockHash, safeBlockHash: env.clMock.latestForkchoice.safeBlockHash, finalizedBlockHash: env.clMock.latestForkchoice.finalizedBlockHash, - ), newPayloadAttributes, reOrgPayload.timestamp) + ) + + var version = env.engine.version(reOrgPayload.timestamp) + let r = env.engine.client.forkchoiceUpdated(version, fcu, some(newPayloadAttributes)) r.expectPayloadStatus(PayloadExecutionStatus.valid) r.expectLatestValidHash(reOrgPayload.blockHash) - p = env.engine.client.getPayload(r.Response.PayloadID, newPayloadAttributes) + version = env.engine.version(newPayloadAttributes.timestamp) + let p = env.engine.client.getPayload(r.get.payloadID.get, version) p.expectPayloadParentHash(reOrgPayload.blockHash) - s = env.engine.client.newPayload(p.Payload) + let payload = p.get.executionPayload + let s = env.engine.client.newPayload(payload) s.expectStatus(PayloadExecutionStatus.valid) - s.expectLatestValidHash(p.Payload.blockHash) + s.expectLatestValidHash(payload.blockHash) # After this, the CLMocker will continue and try to re-org to canonical chain once again # CLMocker will fail the test if this is not possible, so nothing left to do. - ), + return true )) -) + testCond pbRes + return true type SafeReOrgToSideChainTest* = ref object of EngineSpec @@ -748,9 +738,7 @@ method withMainFork(cs: SafeReOrgToSideChainTest, fork: EngineFork): BaseSpec = return res method getName(cs: SafeReOrgToSideChainTest): string = - name = "Safe Re-Org to Side Chain" - return name -) + "Safe Re-Org to Side Chain" # Test that performs a re-org of the safe block to a side chain. method execute(cs: SafeReOrgToSideChainTest, env: TestEnv): bool = @@ -759,70 +747,74 @@ method execute(cs: SafeReOrgToSideChainTest, env: TestEnv): bool = testCond ok # Produce an alternative chain - sidechainPayloads = make([]ExecutableData, 0) + var shadow = Shadow() - if s.slotsToSafe.Uint64() != 1 ( - fatal "[TEST ISSUE] CLMock configured slots to safe not equal to 1: %v", t.TestName, s.slotsToSafe) - ) - if s.slotsToFinalized.Uint64() != 2 ( - fatal "[TEST ISSUE] CLMock configured slots to finalized not equal to 2: %v", t.TestName, s.slotsToFinalized) - ) + testCond cs.slotsToSafe == 1: + fatal "[TEST ISSUE] CLMock configured slots to safe not equal to 1" + + testCond cs.slotsToFinalized == 2: + fatal "[TEST ISSUE] CLMock configured slots to finalized not equal to 2" # Produce three payloads `P1`, `P2`, `P3`, along with the side chain payloads `P2'`, `P3'` # First payload is finalized so no alternative payload - env.clMock.produceSingleBlock(BlockProcessCallbacks()) + testCond env.clMock.produceSingleBlock(BlockProcessCallbacks()) + testCond env.clMock.produceBlocks(2, BlockProcessCallbacks( onGetpayload: proc(): bool = # Generate an alternative payload by simply adding extraData to the block - altParentHash = env.clMock.latestPayloadBuilt.parentHash - if len(sidechainPayloads) > 0 ( - altParentHash = sidechainPayloads[len(sidechainPayloads)-1].blockHash + var altParentHash = env.clMock.latestPayloadBuilt.parentHash + if len(shadow.payloads) > 0: + altParentHash = shadow.payloads[^1].blockHash + + let customizer = CustomPayloadData( + parentHash: some(ethHash altParentHash), + extraData: some(@[0x01.byte]), ) - customizer = &CustomPayloadData( - parentHash: &altParentHash, - extraData: &([]byte(0x01)), - ) - altPayload, err = customizer.customizePayload(env.clMock.latestPayloadBuilt) - if err != nil ( - fatal "Unable to customize payload: %v", t.TestName, err) - ) - sidechainPayloads = append(sidechainPayloads, altPayload) - ), + + let payload = customizer.customizePayload(env.clMock.latestExecutableData) + shadow.payloads.add payload + return true )) # Verify current state of labels - head = env.engine.client.headerByNumber(Head) - head.expectHash(env.clMock.latestPayloadBuilt.blockHash) + let head = env.engine.client.namedHeader(Head) + head.expectHash(ethHash env.clMock.latestPayloadBuilt.blockHash) - safe = env.engine.client.headerByNumber(Safe) - safe.expectHash(env.clMock.executedPayloadHistory[2].blockHash) + let safe = env.engine.client.namedHeader(Safe) + safe.expectHash(ethHash env.clMock.executedPayloadHistory[2].blockHash) - finalized = env.engine.client.headerByNumber(Finalized) - finalized.expectHash(env.clMock.executedPayloadHistory[1].blockHash) + let finalized = env.engine.client.namedHeader(Finalized) + finalized.expectHash(ethHash env.clMock.executedPayloadHistory[1].blockHash) # Re-org the safe/head blocks to point to the alternative side chain - env.clMock.produceSingleBlock(BlockProcessCallbacks( + let pbRes = env.clMock.produceSingleBlock(BlockProcessCallbacks( onGetpayload: proc(): bool = - for _, p = range sidechainPayloads ( - r = env.engine.client.newPayload(p) - r.expectStatusEither(PayloadExecutionStatus.valid, PayloadExecutionStatus.accepted) - ) - r = env.engine.client.forkchoiceUpdated(api.ForkchoiceStateV1( - headBlockHash: sidechainPayloads[1].blockHash, - safeBlockHash: sidechainPayloads[0].blockHash, + for p in shadow.payloads: + let version = env.engine.version(p.timestamp) + let r = env.engine.client.newPayload(version, p) + r.expectStatusEither([PayloadExecutionStatus.valid, PayloadExecutionStatus.accepted]) + + let fcu = ForkchoiceStateV1( + headBlockHash: shadow.payloads[1].blockHash, + safeBlockHash: shadow.payloads[0].blockHash, finalizedBlockHash: env.clMock.executedPayloadHistory[1].blockHash, - ), nil, sidechainPayloads[1].timestamp) + ) + + let version = env.engine.version(shadow.payloads[1].timestamp) + let r = env.engine.client.forkchoiceUpdated(version, fcu) r.expectPayloadStatus(PayloadExecutionStatus.valid) - head = env.engine.client.headerByNumber(Head) - head.expectHash(sidechainPayloads[1].blockHash) + let head = env.engine.client.namedHeader(Head) + head.expectHash(ethHash shadow.payloads[1].blockHash) - safe = env.engine.client.headerByNumber(Safe) - safe.expectHash(sidechainPayloads[0].blockHash) + let safe = env.engine.client.namedHeader(Safe) + safe.expectHash(ethHash shadow.payloads[0].blockHash) - finalized = env.engine.client.headerByNumber(Finalized) - finalized.expectHash(env.clMock.executedPayloadHistory[1].blockHash) + let finalized = env.engine.client.namedHeader(Finalized) + finalized.expectHash(ethHash env.clMock.executedPayloadHistory[1].blockHash) - ), + return true )) -) + + testCond pbRes + return true diff --git a/hive_integration/nodocker/engine/engine/rpc.nim b/hive_integration/nodocker/engine/engine/rpc.nim index e9f6f441a..1537943d4 100644 --- a/hive_integration/nodocker/engine/engine/rpc.nim +++ b/hive_integration/nodocker/engine/engine/rpc.nim @@ -10,6 +10,8 @@ import std/strutils, + eth/common, + chronicles, ./engine_spec type @@ -24,13 +26,16 @@ type checkType*: BlockStatusRPCcheckType # TODO: Syncing bool + Shadow = ref object + txHash: common.Hash256 + method withMainFork(cs: BlockStatus, fork: EngineFork): BaseSpec = var res = cs.clone() res.mainFork = fork return res method getName(cs: BlockStatus): string = - "RPC" & $b.checkType + "RPC " & $cs.checkType # Test to verify Block information available at the Eth RPC after NewPayload/ForkchoiceUpdated method execute(cs: BlockStatus, env: TestEnv): bool = @@ -38,69 +43,69 @@ method execute(cs: BlockStatus, env: TestEnv): bool = let ok = waitFor env.clMock.waitForTTD() testCond ok - case b.checkType - of SafeOnSafeblockHash, FinalizedOnFinalizedblockHash: - var number *big.Int - if b.checkType == SafeOnSafeblockHash: + if cs.checkType in [SafeOnSafeblockHash, FinalizedOnFinalizedblockHash]: + var number = Finalized + if cs.checkType == SafeOnSafeblockHash: number = Safe - else: - number = Finalized - p = env.engine.client.headerByNumber(number) + let p = env.engine.client.namedHeader(number) p.expectError() - ) # Produce blocks before starting the test - env.clMock.produceBlocks(5, BlockProcessCallbacks()) + testCond env.clMock.produceBlocks(5, BlockProcessCallbacks()) - var tx typ.Transaction - callbacks = BlockProcessCallbacks( + var shadow = Shadow() + var callbacks = BlockProcessCallbacks( onPayloadProducerSelected: proc(): bool = let tc = BaseTx( - recipient: &ZeroAddr, - amount: 1.u256, - txType: cs.txType, - gasLimit: 75000, - ), - - let ok = env.sendNextTx(tc) - testCond ok: - fatal "Error trying to send transaction: %v", err) + recipient: some(ZeroAddr), + amount: 1.u256, + txType: cs.txType, + gasLimit: 75000, ) - ), + + let tx = env.makeNextTx(tc) + shadow.txHash = tx.rlpHash + let ok = env.sendTx(tx) + testCond ok: + fatal "Error trying to send transaction" + return true ) - case b.checkType ( + case cs.checkType of LatestOnNewPayload: - callbacks.onGetPayload = proc(): bool - r = env.engine.client.latestHeader() - r.expectHash(env.clMock.latestForkchoice.headblockHash) + callbacks.onGetPayload = proc(): bool = + let r = env.engine.client.namedHeader(Head) + r.expectHash(ethHash env.clMock.latestForkchoice.headblockHash) - s = env.engine.client.TestBlockNumber() - s.ExpectNumber(env.clMock.latestHeadNumber.Uint64()) + let s = env.engine.client.blockNumber() + s.expectNumber(env.clMock.latestHeadNumber.uint64) - p = env.engine.client.latestHeader() - p.expectHash(env.clMock.latestForkchoice.headblockHash) + let p = env.engine.client.namedHeader(Head) + p.expectHash(ethHash env.clMock.latestForkchoice.headblockHash) # Check that the receipt for the transaction we just sent is still not available - q = env.engine.client.txReceipt(tx.Hash()) + let q = env.engine.client.txReceipt(shadow.txHash) q.expectError() + return true of LatestOnHeadblockHash: - callbacks.onForkchoiceBroadcast = proc(): bool - r = env.engine.client.latestHeader() - r.expectHash(env.clMock.latestForkchoice.headblockHash) - - s = env.engine.client.txReceipt(tx.Hash()) - s.ExpectTransactionHash(tx.Hash()) + callbacks.onForkchoiceBroadcast = proc(): bool = + let r = env.engine.client.namedHeader(Head) + r.expectHash(ethHash env.clMock.latestForkchoice.headblockHash) + let s = env.engine.client.txReceipt(shadow.txHash) + s.expectTransactionHash(shadow.txHash) + return true of SafeOnSafeblockHash: - callbacks.onSafeBlockChange = proc(): bool - r = env.engine.client.headerByNumber(Safe) - r.expectHash(env.clMock.latestForkchoice.safeblockHash) + callbacks.onSafeBlockChange = proc(): bool = + let r = env.engine.client.namedHeader(Safe) + r.expectHash(ethHash env.clMock.latestForkchoice.safeblockHash) + return true of FinalizedOnFinalizedblockHash: - callbacks.onFinalizedBlockChange = proc(): bool - r = env.engine.client.headerByNumber(Finalized) - r.expectHash(env.clMock.latestForkchoice.finalizedblockHash) + callbacks.onFinalizedBlockChange = proc(): bool = + let r = env.engine.client.namedHeader(Finalized) + r.expectHash(ethHash env.clMock.latestForkchoice.finalizedblockHash) + return true # Perform the test - env.clMock.produceSingleBlock(callbacks) - + testCond env.clMock.produceSingleBlock(callbacks) + return true diff --git a/hive_integration/nodocker/engine/engine/versioning.nim b/hive_integration/nodocker/engine/engine/versioning.nim index f0135d2ad..7a1aa5b5e 100644 --- a/hive_integration/nodocker/engine/engine/versioning.nim +++ b/hive_integration/nodocker/engine/engine/versioning.nim @@ -11,6 +11,8 @@ # Test versioning of the Engine API methods import std/strutils, + chronicles, + ../cancun/customizer, ./engine_spec type @@ -25,7 +27,10 @@ method withMainFork(cs: EngineNewPayloadVersionTest, fork: EngineFork): BaseSpec # when the timestamp payload attribute does not match the upgraded/downgraded version. type ForkchoiceUpdatedOnPayloadRequestTest* = ref object of EngineSpec - ForkchoiceUpdatedCustomizer + name*: string + about*: string + forkchoiceUpdatedCustomizer*: ForkchoiceUpdatedCustomizer + payloadAttributesCustomizer*: PayloadAttributesCustomizer method withMainFork(cs: ForkchoiceUpdatedOnPayloadRequestTest, fork: EngineFork): BaseSpec = var res = cs.clone() @@ -33,43 +38,35 @@ method withMainFork(cs: ForkchoiceUpdatedOnPayloadRequestTest, fork: EngineFork) return res method getName(cs: ForkchoiceUpdatedOnPayloadRequestTest): string = - return "ForkchoiceUpdated Version on Payload Request: " + cs.BaseSpec.GetName() + "ForkchoiceUpdated Version on Payload Request: " & cs.name method execute(cs: ForkchoiceUpdatedOnPayloadRequestTest, env: TestEnv): bool = # Wait until TTD is reached by this client - let ok = waitFor env.clMockWaitForTTD() + let ok = waitFor env.clMock.waitForTTD() testCond ok - env.clMock.produceSingleBlock(clmock.BlockProcessCallbacks( + let pbRes = env.clMock.produceSingleBlock(clmock.BlockProcessCallbacks( onPayloadAttributesGenerated: proc(): bool = - var ( - payloadAttributes = &env.clMockLatestPayloadAttributes - expectedStatus test.PayloadStatus = PayloadExecutionStatus.valid - expectedError *int - err error - ) - cs.SetEngineAPIVersionResolver(t.ForkConfig) - testEngine = t.TestEngine.WithEngineAPIVersionResolver(cs.ForkchoiceUpdatedCustomizer) - payloadAttributes, err = cs.GetPayloadAttributes(payloadAttributes) - if err != nil ( - t.Fatalf("FAIL: Error getting custom payload attributes: %v", err) - ) - expectedError, err = cs.GetExpectedError() - if err != nil ( - t.Fatalf("FAIL: Error getting custom expected error: %v", err) - ) - if cs.GetExpectInvalidStatus() ( - expectedStatus = PayloadExecutionStatus.invalid - ) + var + attr = env.clMock.latestPayloadAttributes + expectedStatus = PayloadExecutionStatus.valid - r = env.engine.client.forkchoiceUpdated(env.clMockLatestForkchoice, payloadAttributes, env.clMockLatestHeader.Time) - r.ExpectationDescription = cs.Expectation - if expectedError != nil ( - r.expectErrorCode(*expectedError) + attr = cs.payloadAttributesCustomizer.getPayloadAttributes(attr) + + let expectedError = cs.forkchoiceUpdatedCustomizer.getExpectedError() + if cs.forkchoiceUpdatedCustomizer.getExpectInvalidStatus(): + expectedStatus = PayloadExecutionStatus.invalid + + cs.forkchoiceUpdatedCustomizer.setEngineAPIVersionResolver(env.engine.com) + let version = cs.forkchoiceUpdatedCustomizer.forkchoiceUpdatedVersion(env.clMock.latestHeader.timestamp.uint64) + let r = env.engine.client.forkchoiceUpdated(version, env.clMock.latestForkchoice, some(attr)) + #r.ExpectationDescription = cs.Expectation + if expectedError != 0: + r.expectErrorCode(expectedError) else: r.expectNoError() r.expectPayloadStatus(expectedStatus) - ) - ), + return true )) -) + testCond pbRes + return true diff --git a/hive_integration/nodocker/engine/engine_sim.nim b/hive_integration/nodocker/engine/engine_sim.nim index b0d6f058b..b0da8ce92 100644 --- a/hive_integration/nodocker/engine/engine_sim.nim +++ b/hive_integration/nodocker/engine/engine_sim.nim @@ -24,10 +24,10 @@ import ./cancun_tests proc combineTests(): seq[TestDesc] = - #result.add wdTestList - #result.add ecTestList - #result.add authTestList - #result.add engineTestList + result.add wdTestList + result.add ecTestList + result.add authTestList + result.add engineTestList result.add cancunTestList let diff --git a/hive_integration/nodocker/engine/engine_tests.nim b/hive_integration/nodocker/engine/engine_tests.nim index 59aa78d26..67df03f34 100644 --- a/hive_integration/nodocker/engine/engine_tests.nim +++ b/hive_integration/nodocker/engine/engine_tests.nim @@ -20,18 +20,18 @@ import import ./engine/suggested_fee_recipient, ./engine/payload_attributes, - #./engine/payload_execution, + ./engine/payload_execution, ./engine/invalid_ancestor, ./engine/invalid_payload, ./engine/prev_randao, - #./engine/payload_id, + ./engine/payload_id, ./engine/forkchoice, - #./engine/versioning, + ./engine/versioning, ./engine/bad_hash, - #./engine/fork_id, - #./engine/reorg, - ./engine/misc - #./engine/rpc + ./engine/fork_id, + ./engine/reorg, + ./engine/misc, + ./engine/rpc proc getGenesis(cs: EngineSpec, param: NetworkParams) = # Set the terminal total difficulty @@ -63,17 +63,7 @@ proc specExecute(ws: BaseSpec): bool = env.close() # Execution specification reference: -# https:#github.com/ethereum/execution-apis/blob/main/src/engine/specification.md - -#[var ( - big0 = new(big.Int) - big1 = u256(1) - Head *big.Int # Nil - Pending = u256(-2) - Finalized = u256(-3) - Safe = u256(-4) -) -]# +# https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md # Register all test combinations for Paris proc makeEngineTest*(): seq[EngineSpec] = @@ -200,8 +190,87 @@ proc makeEngineTest*(): seq[EngineSpec] = transactionCount: 20, ) + # Payload Execution Tests + result.add ReExecutePayloadTest() + result.add InOrderPayloadExecutionTest() + result.add MultiplePayloadsExtendingCanonicalChainTest( + setHeadToFirstPayloadReceived: true, + ) + + result.add MultiplePayloadsExtendingCanonicalChainTest( + setHeadToFirstPayloadReceived: false, + ) + + result.add NewPayloadOnSyncingClientTest() + result.add NewPayloadWithMissingFcUTest() + + const invalidPayloadBlockField = [ + InvalidTransactionSignature, + InvalidTransactionNonce, + InvalidTransactionGasPrice, + InvalidTransactionGasTipPrice, + InvalidTransactionGas, + InvalidTransactionValue, + InvalidTransactionChainID, + ] + + # Invalid Transaction Payload Tests + for invalidField in invalidPayloadBlockField: + let invalidDetectedOnSync = invalidField == InvalidTransactionChainID + for syncing in [false, true]: + if invalidField != InvalidTransactionGasTipPrice: + for testTxType in [TxLegacy, TxEip1559]: + result.add InvalidPayloadTestCase( + txType: some(testTxType), + invalidField: invalidField, + syncing: syncing, + invalidDetectedOnSync: invalidDetectedOnSync, + ) + else: + result.add InvalidPayloadTestCase( + txType: some(TxEip1559), + invalidField: invalidField, + syncing: syncing, + invalidDetectedOnSync: invalidDetectedOnSync, + ) + + const payloadAttributesFieldChange = [ + PayloadAttributesIncreaseTimestamp, + PayloadAttributesRandom, + PayloadAttributesSuggestedFeeRecipient, + ] + + # Payload ID Tests + for payloadAttributeFieldChange in payloadAttributesFieldChange: + result.add UniquePayloadIDTest( + fieldModification: payloadAttributeFieldChange, + ) + + # Endpoint Versions Tests + # Early upgrade of ForkchoiceUpdated when requesting a payload + result.add ForkchoiceUpdatedOnPayloadRequestTest( + name: "Early upgrade", + about: """ + Early upgrade of ForkchoiceUpdated when requesting a payload. + The test sets the fork height to 1, and the block timestamp increments to 2 + seconds each block. + CL Mock prepares the payload attributes for the first block, which should contain + the attributes of the next fork. + The test then reduces the timestamp by 1, but still uses the next forkchoice updated + version, which should result in UNSUPPORTED_FORK_ERROR error. + """, + forkHeight: 1, + blockTimestampIncrement: 2, + forkchoiceUpdatedCustomizer: UpgradeForkchoiceUpdatedVersion( + expectedError: engineApiUnsupportedFork, + ), + payloadAttributescustomizer: TimestampDeltaPayloadAttributesCustomizer( + timestampDelta: -1, + ), + ) + # Register RPC tests - #[let blockStatusRPCCheckType = [ + let blockStatusRPCCheckType = [ LatestOnNewPayload, LatestOnHeadBlockHash, SafeOnSafeBlockHash, @@ -211,6 +280,74 @@ proc makeEngineTest*(): seq[EngineSpec] = for field in blockStatusRPCCheckType: result.add BlockStatus(checkType: field) + # Fork ID Tests + for genesisTimestamp in 0..1: + for forkTime in 0..2: + for prevForkTime in 0..forkTime: + for currentBlock in 0..1: + result.add ForkIDSpec( + mainFork: ForkParis, + genesistimestamp: genesisTimestamp, + forkTime: forkTime.uint64, + previousForkTime: prevForkTime.uint64, + produceBlocksBeforePeering: currentBlock, + ) + + # Re-org using the Engine API tests + + # Sidechain re-org tests + result.add SidechainReOrgTest() + result.add ReOrgBackFromSyncingTest( + slotsToSafe: 32, + slotsToFinalized: 64, + ) + + result.add ReOrgPrevValidatedPayloadOnSideChainTest( + slotsToSafe: 32, + slotsToFinalized: 64, + ) + + result.add SafeReOrgToSideChainTest( + slotsToSafe: 1, + slotsToFinalized: 2, + ) + + # Re-org a transaction out of a block, or into a new block + result.add TransactionReOrgTest( + scenario: TransactionReOrgScenarioReOrgOut, + ) + + result.add TransactionReOrgTest( + scenario: TransactionReOrgScenarioReOrgDifferentBlock, + ) + + result.add TransactionReOrgTest( + scenario: TransactionReOrgScenarioNewPayloadOnRevert, + ) + + result.add TransactionReOrgTest( + scenario: TransactionReOrgScenarioReOrgBackIn, + ) + + # Re-Org back into the canonical chain tests + result.add ReOrgBackToCanonicalTest( + slotsToSafe: 10, + slotsToFinalized: 20, + timeoutSeconds: 60, + transactionPerPayload: 1, + reOrgDepth: 5, + ) + + result.add ReOrgBackToCanonicalTest( + slotsToSafe: 32, + slotsToFinalized: 64, + timeoutSeconds: 120, + transactionPerPayload: 50, + reOrgDepth: 10, + executeSidePayloadOnReOrg: true, + ) + +#[ const invalidReorgList = [ InvalidStateRoot, @@ -263,186 +400,8 @@ proc makeEngineTest*(): seq[EngineSpec] = reOrgFromCanonical: reOrgFromCanonical, invalidIndex: invalidIndex, ) -]# -#[ - # Payload ID Tests - for _, payloadAttributeFieldChange := range []PayloadAttributesFieldChange( - PayloadAttributesIncreaseTimestamp, - PayloadAttributesRandom, - PayloadAttributesSuggestedFeeRecipient, - ) ( - result.add UniquePayloadIDTest( - FieldModification: payloadAttributeFieldChange, - )) - ) - - # Endpoint Versions Tests - # Early upgrade of ForkchoiceUpdated when requesting a payload - result.add - ForkchoiceUpdatedOnPayloadRequestTest( - BaseSpec: test.BaseSpec( - Name: "Early upgrade", - About: ` - Early upgrade of ForkchoiceUpdated when requesting a payload. - The test sets the fork height to 1, and the block timestamp increments to 2 - seconds each block. - CL Mock prepares the payload attributes for the first block, which should contain - the attributes of the next fork. - The test then reduces the timestamp by 1, but still uses the next forkchoice updated - version, which should result in UNSUPPORTED_FORK_ERROR error. - `, - forkHeight: 1, - BlockTimestampIncrement: 2, - ), - ForkchoiceUpdatedcustomizer: UpgradeForkchoiceUpdatedVersion( - ForkchoiceUpdatedcustomizer: BaseForkchoiceUpdatedCustomizer( - PayloadAttributescustomizer: TimestampDeltaPayloadAttributesCustomizer( - PayloadAttributescustomizer: BasePayloadAttributesCustomizer(), - TimestampDelta: -1, - ), - ExpectedError: globals.UNSUPPORTED_FORK_ERROR, - ), - ), - ), - ) - - # Payload Execution Tests - result.add - ReExecutePayloadTest(), - InOrderPayloadExecutionTest(), - MultiplePayloadsExtendingCanonicalChainTest( - SetHeadToFirstPayloadReceived: true, - ), - MultiplePayloadsExtendingCanonicalChainTest( - SetHeadToFirstPayloadReceived: false, - ), - NewPayloadOnSyncingClientTest(), - NewPayloadWithMissingFcUTest(), - ) - - # Invalid Transaction Payload Tests - for _, invalidField := range []InvalidPayloadBlockField( - InvalidTransactionSignature, - InvalidTransactionNonce, - InvalidTransactionGasPrice, - InvalidTransactionGasTipPrice, - InvalidTransactionGas, - InvalidTransactionValue, - InvalidTransactionChainID, - ) ( - invalidDetectedOnSync := invalidField == InvalidTransactionChainID - for _, syncing in [false, true) ( - if invalidField != InvalidTransactionGasTipPrice ( - for _, testTxType := range []TestTransactionType(TxLegacy, TxEip1559) ( - result.add InvalidPayloadTestCase( - BaseSpec: test.BaseSpec( - txType: some( testTxType, - ), - InvalidField: invalidField, - Syncing: syncing, - InvalidDetectedOnSync: invalidDetectedOnSync, - )) - ) - ) else ( - result.add InvalidPayloadTestCase( - BaseSpec: test.BaseSpec( - txType: some( TxEip1559, - ), - InvalidField: invalidField, - Syncing: syncing, - InvalidDetectedOnSync: invalidDetectedOnSync, - )) - ) - ) - - ) - - # Re-org using the Engine API tests - - # Sidechain re-org tests - result.add - SidechainReOrgTest(), - ReOrgBackFromSyncingTest( - BaseSpec: test.BaseSpec( - slotsToSafe: u256(32), - slotsToFinalized: u256(64), - ), - ), - ReOrgPrevValidatedPayloadOnSideChainTest( - BaseSpec: test.BaseSpec( - slotsToSafe: u256(32), - slotsToFinalized: u256(64), - ), - ), - SafeReOrgToSideChainTest( - BaseSpec: test.BaseSpec( - slotsToSafe: u256(1), - slotsToFinalized: u256(2), - ), - ), - ) - - // Re-org a transaction out of a block, or into a new block - result.add - TransactionReOrgTest{ - Scenario: TransactionReOrgScenarioReOrgOut, - }, - TransactionReOrgTest{ - Scenario: TransactionReOrgScenarioReOrgDifferentBlock, - }, - TransactionReOrgTest{ - Scenario: TransactionReOrgScenarioNewPayloadOnRevert, - }, - TransactionReOrgTest{ - Scenario: TransactionReOrgScenarioReOrgBackIn, - }, - ) - - # Re-Org back into the canonical chain tests - result.add - ReOrgBackToCanonicalTest( - BaseSpec: test.BaseSpec( - slotsToSafe: u256(10), - slotsToFinalized: u256(20), - TimeoutSeconds: 60, - ), - TransactionPerPayload: 1, - ReOrgDepth: 5, - ), - ReOrgBackToCanonicalTest( - BaseSpec: test.BaseSpec( - slotsToSafe: u256(32), - slotsToFinalized: u256(64), - TimeoutSeconds: 120, - ), - TransactionPerPayload: 50, - ReOrgDepth: 10, - ExecuteSidePayloadOnReOrg: true, - ), - ) - - # Fork ID Tests - for genesisTimestamp := uint64(0); genesisTimestamp <= 1; genesisTimestamp++ ( - for forkTime := uint64(0); forkTime <= 2; forkTime++ ( - for prevForkTime := uint64(0); prevForkTime <= forkTime; prevForkTime++ ( - for currentBlock := 0; currentBlock <= 1; currentBlock++ ( - result.add - ForkIDSpec( - BaseSpec: test.BaseSpec( - MainFork: config.Paris, - Genesistimestamp: pUint64(genesisTimestamp), - ForkTime: forkTime, - PreviousForkTime: prevForkTime, - ), - ProduceBlocksBeforePeering: currentBlock, - ), - ) - ) - ) - ) - ) ]# diff --git a/hive_integration/nodocker/engine/helper.nim b/hive_integration/nodocker/engine/helper.nim index 2251ddae9..64ae78275 100644 --- a/hive_integration/nodocker/engine/helper.nim +++ b/hive_integration/nodocker/engine/helper.nim @@ -10,11 +10,21 @@ import eth/[common, rlp], + chronicles, ../../../nimbus/beacon/execution_types, - ../../../nimbus/beacon/web3_eth_conv + ../../../nimbus/beacon/web3_eth_conv, + ./engine_client, + ./types 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 + +proc checkPrevRandaoValue*(client: RpcClient, expectedPrevRandao: common.Hash256, blockNumber: uint64): bool = + let storageKey = blockNumber.u256 + let r = client.storageAt(prevRandaoContractAddr, storageKey) + let expected = UInt256.fromBytesBE(expectedPrevRandao.data) + r.expectStorageEqual(expected) + return true diff --git a/hive_integration/nodocker/engine/test_env.nim b/hive_integration/nodocker/engine/test_env.nim index aef61a9ca..da9c4ce44 100644 --- a/hive_integration/nodocker/engine/test_env.nim +++ b/hive_integration/nodocker/engine/test_env.nim @@ -133,6 +133,12 @@ proc makeNextTx*(env: TestEnv, tc: BaseTx): Transaction = proc sendNextTx*(env: TestEnv, eng: EngineEnv, tc: BaseTx): bool = env.sender.sendNextTx(eng.client, tc) +proc sendNextTxs*(env: TestEnv, eng: EngineEnv, tc: BaseTx, num: int): bool = + for i in 0..