From 9cf3c71a57f4501ab38ed73b2de61f61c0a25994 Mon Sep 17 00:00:00 2001 From: jangko Date: Sun, 5 Nov 2023 14:45:56 +0700 Subject: [PATCH] Fix Engine API simulator --- .../nodocker/engine/engine/engine_spec.nim | 1 + .../engine/engine/invalid_ancestor.nim | 108 ++++++++---------- .../nodocker/engine/engine/reorg.nim | 15 ++- .../nodocker/engine/engine_client.nim | 3 + .../nodocker/engine/engine_tests.nim | 16 ++- hive_integration/nodocker/engine/types.nim | 3 + 6 files changed, 76 insertions(+), 70 deletions(-) diff --git a/hive_integration/nodocker/engine/engine/engine_spec.nim b/hive_integration/nodocker/engine/engine/engine_spec.nim index a98ef1c66..311e38112 100644 --- a/hive_integration/nodocker/engine/engine/engine_spec.nim +++ b/hive_integration/nodocker/engine/engine/engine_spec.nim @@ -22,6 +22,7 @@ type EngineSpec* = ref object of BaseSpec ttd*: int64 chainFile*: string + enableConfigureCLMock*: bool method withMainFork*(tc: EngineSpec, fork: EngineFork): BaseSpec {.base.} = doAssert(false, "withMainFork not implemented") diff --git a/hive_integration/nodocker/engine/engine/invalid_ancestor.nim b/hive_integration/nodocker/engine/engine/invalid_ancestor.nim index 32d9d2d39..a0a2f41ae 100644 --- a/hive_integration/nodocker/engine/engine/invalid_ancestor.nim +++ b/hive_integration/nodocker/engine/engine/invalid_ancestor.nim @@ -14,7 +14,8 @@ import eth/common/eth_types_rlp, ./engine_spec, ../cancun/customizer, - ../../../../nimbus/utils/utils + ../../../../nimbus/utils/utils, + ../../../../nimbus/beacon/payload_conv # Attempt to re-org to a chain which at some point contains an unknown payload which is also invalid. # Then reveal the invalid payload and expect that the client rejects it and rejects forkchoice updated calls to this chain. @@ -190,6 +191,9 @@ method getName(cs: InvalidMissingAncestorReOrgSyncTest): string = "Invalid Missing Ancestor Syncing ReOrg, $1, EmptyTxs=$2, CanonicalReOrg=$3, Invalid P$4" % [ $cs.invalidField, $cs.emptyTransactions, $cs.reOrgFromCanonical, $cs.invalidIndex] +proc executableDataToBlock(ex: ExecutableData): EthBlock = + ethBlock(ex.basePayload, true, ex.beaconRoot) + method execute(cs: InvalidMissingAncestorReOrgSyncTest, env: TestEnv): bool = var sec = env.addEngine(true, cs.reOrgFromCanonical) @@ -311,13 +315,13 @@ method execute(cs: InvalidMissingAncestorReOrgSyncTest, env: TestEnv): bool = s.expectStatusEither([PayloadExecutionStatus.valid, PayloadExecutionStatus.syncing]) else: - debugEcho "i: ", i, " cs.invalidIndex: ", cs.invalidIndex - doAssert(false, "Should not happen") - #invalidBlock, err = ExecutableDataToBlock(*shadow.payloads[i]) - #if err = secondaryClient.SetBlock(invalidBlock, shadow.payloads[i-1].blockNumber, shadow.payloads[i-1].StateRoot); err != nil ( - # fatal "TEST ISSUE - Failed to set invalid block: " err) - #) - #info "Invalid block successfully set %d (%s): " i, payloadValidStr, invalidBlock.Hash()) + let invalidBlock = executableDataToBlock(shadow.payloads[i]) + testCond sec.client.setBlock(invalidBlock, shadow.payloads[i-1].blockNumber, shadow.payloads[i-1].stateRoot): + fatal "TEST ISSUE - Failed to set invalid block" + info "Invalid block successfully set", + idx=i, + msg=payloadValidStr, + hash=invalidBlock.header.blockHash.short # Check that the second node has the correct head var res = sec.client.latestHeader() @@ -348,76 +352,64 @@ method execute(cs: InvalidMissingAncestorReOrgSyncTest, env: TestEnv): bool = number=head.blockNumber # If we are syncing through p2p, we need to keep polling until the client syncs the missing payloads - #[for ( - r = env.engine.client.newPayload(shadow.payloads[shadow.n]) - info "Response from main client: " r.Status) - s = env.engine.client.forkchoiceUpdated(ForkchoiceStateV1( - headblockHash: shadow.payloads[shadow.n].blockHash, - ), nil, shadow.payloads[shadow.n].timestamp) - info "Response from main client fcu: " s.Response.PayloadStatus) + while true: + let version = env.engine.version(shadow.payloads[shadow.n].timestamp) + let r = env.engine.client.newPayload(version, shadow.payloads[shadow.n]) + info "Response from main client", status=r.get.status - if r.Status.Status == PayloadExecutionStatus.invalid ( + let fcu = ForkchoiceStateV1( + headblockHash: shadow.payloads[shadow.n].blockHash, + ) + let s = env.engine.client.forkchoiceUpdated(version, fcu) + info "Response from main client fcu", status=s.get.payloadStatus.status + + if r.get.status == PayloadExecutionStatus.invalid: # We also expect that the client properly returns the LatestValidHash of the block on the # side chain that is immediately prior to the invalid payload (or zero if parent is PoW) - var lvh common.Hash - if shadow.cAHeight != 0 || cs.invalidIndex != 1 ( + var lvh: Web3Hash + if shadow.cAHeight != 0 or cs.invalidIndex != 1: # Parent is NOT Proof of Work lvh = shadow.payloads[cs.invalidIndex-1].blockHash - ) + r.expectLatestValidHash(lvh) # Response on ForkchoiceUpdated should be the same s.expectPayloadStatus(PayloadExecutionStatus.invalid) s.expectLatestValidHash(lvh) break - elif test.PayloadStatus(r.Status.Status) == PayloadExecutionStatus.valid ( - ctx, cancel = context.WithTimeout(t.TestContext, globals.RPCTimeout) - defer cancel() - latestBlock, err = t.Eth.BlockByNumber(ctx, nil) - if err != nil ( - fatal "Unable to get latest block: " err) - ) + elif r.get.status == PayloadExecutionStatus.valid: + let res = env.engine.client.latestHeader() + testCond res.isOk: + fatal "Unable to get latest block: ", msg=res.error # Print last shadow.n blocks, for debugging - k = latestBlock.blockNumber().Int64() - int64(shadow.n) - if k < 0 ( - k = 0 - ) - for ; k <= latestBlock.blockNumber().Int64(); k++ ( - ctx, cancel = context.WithTimeout(t.TestContext, globals.RPCTimeout) - defer cancel() - latestBlock, err = t.Eth.BlockByNumber(ctx, big.NewInt(k)) - if err != nil ( - fatal "Unable to get block %d: " k, err) - ) - js, _ = json.MarshalIndent(latestBlock.Header(), "", " ") - info "Block %d: %s", t.TestName, k, js) - ) + let latestNumber = res.get.blockNumber.truncate(int64) + var k = latestNumber - int64(shadow.n) + if k < 0: k = 0 - fatal "Client returned VALID on an invalid chain: " r.Status) - ) + while k <= latestNumber: + let res = env.engine.client.headerByNumber(k.uint64) + testCond res.isOk: + fatal "Unable to get block", number=k, msg=res.error + inc k - select ( - case <-time.After(time.Second): - continue - case <-t.TimeoutContext.Done(): - fatal "Timeout waiting for main client to detect invalid chain", t.TestName) - ) - ) + fatal "Client returned VALID on an invalid chain", status=r.get.status + return false - if !cs.reOrgFromCanonical ( + if not cs.reOrgFromCanonical: # We need to send the canonical chain to the main client here - for i = env.clMock.firstPoSBlockNumber.Uint64(); i <= env.clMock.latestExecutedPayload.blockNumber; i++ ( - if payload, ok = env.clMock.executedPayloadHistory[i]; ok ( - r = env.engine.client.newPayload(payload) + let start = env.clMock.firstPoSBlockNumber.get + let stop = env.clMock.latestExecutedPayload.blockNumber.uint64 + for i in start..stop: + if env.clMock.executedPayloadHistory.hasKey(i): + let payload = env.clMock.executedPayloadHistory[i] + let r = env.engine.client.newPayload(payload) r.expectStatus(PayloadExecutionStatus.valid) - ) - ) - ) # Resend the latest correct fcU - r = env.engine.client.forkchoiceUpdated(env.clMock.latestForkchoice, nil, env.clMock.latestPayloadBuilt.timestamp) + let version = env.engine.version(env.clMock.latestPayloadBuilt.timestamp) + let r = env.engine.client.forkchoiceUpdated(version, env.clMock.latestForkchoice) r.expectNoError() - # After this point, the CL Mock will send the next payload of the canonical chain]# + # After this point, the CL Mock will send the next payload of the canonical chain return true )) diff --git a/hive_integration/nodocker/engine/engine/reorg.nim b/hive_integration/nodocker/engine/engine/reorg.nim index c13e64989..f5f879402 100644 --- a/hive_integration/nodocker/engine/engine/reorg.nim +++ b/hive_integration/nodocker/engine/engine/reorg.nim @@ -135,7 +135,10 @@ method getName(cs: TransactionReOrgTest): string = name.add ", " & $cs.scenario return name -func txHash(shadow: ShadowTx): common.Hash256 = +proc txHash(shadow: ShadowTx): common.Hash256 = + if shadow.tx.isNone: + error "SHADOW TX IS NONE" + return shadow.tx.get.rlpHash # Test transaction status after a forkchoiceUpdated re-orgs to an alternative hash where a transaction is not present @@ -199,11 +202,11 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool = 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 - let tx = shadow.sendTransaction(i) + shadow.tx = some(shadow.sendTransaction(i)) # Get the receipt - let receipt = env.engine.client.txReceipt(tx.rlpHash) - testCond receipt.isOk: + let receipt = env.engine.client.txReceipt(shadow.txHash) + testCond receipt.isErr: fatal "Receipt obtained before tx included in block" return true @@ -272,7 +275,7 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool = if shadow.tx.isSome: # Get the receipt let receipt = env.engine.client.txReceipt(shadow.txHash) - testCond receipt.isOk: + testCond receipt.isErr: fatal "Receipt obtained before tx included in block (NewPayload)" return true , @@ -350,7 +353,7 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool = # Get the receipt let receipt = env.engine.client.txReceipt(shadow.txHash) - testCond receipt.isErr: + testCond receipt.isOk: fatal "Receipt not obtained after tx included in block" return true diff --git a/hive_integration/nodocker/engine/engine_client.nim b/hive_integration/nodocker/engine/engine_client.nim index 66ed828f2..a3ef094e9 100644 --- a/hive_integration/nodocker/engine/engine_client.nim +++ b/hive_integration/nodocker/engine/engine_client.nim @@ -599,3 +599,6 @@ template expectStorageEqual*(res: Result[UInt256, string], account: EthAddress, if res.get != expectedValue: return err("invalid wd storage at $1 is $2, expect $3" % [ account.toHex, $res.get, $expectedValue]) + +proc setBlock*(client: RpcClient, blk: EthBlock, blockNumber: Web3Quantity, stateRoot: Web3Hash): bool = + return true diff --git a/hive_integration/nodocker/engine/engine_tests.nim b/hive_integration/nodocker/engine/engine_tests.nim index 67df03f34..b7473f0a7 100644 --- a/hive_integration/nodocker/engine/engine_tests.nim +++ b/hive_integration/nodocker/engine/engine_tests.nim @@ -58,7 +58,8 @@ proc specExecute(ws: BaseSpec): bool = let env = TestEnv.new(conf) env.engine.setRealTTD() env.setupCLMock() - #cs.configureCLMock(env.clMock) + if cs.enableConfigureCLMock: + cs.configureCLMock(env.clMock) result = cs.execute(env) env.close() @@ -113,6 +114,7 @@ proc makeEngineTest*(): seq[EngineSpec] = invalidIndex: invalidIndex, invalidField: InvalidStateRoot, emptyTransactions: emptyTxs, + enableConfigureCLMock: true ) # Invalid Payload Tests @@ -300,16 +302,19 @@ proc makeEngineTest*(): seq[EngineSpec] = result.add ReOrgBackFromSyncingTest( slotsToSafe: 32, slotsToFinalized: 64, + enableConfigureCLMock: true, ) result.add ReOrgPrevValidatedPayloadOnSideChainTest( slotsToSafe: 32, slotsToFinalized: 64, + enableConfigureCLMock: true, ) result.add SafeReOrgToSideChainTest( slotsToSafe: 1, slotsToFinalized: 2, + enableConfigureCLMock: true, ) # Re-org a transaction out of a block, or into a new block @@ -336,6 +341,7 @@ proc makeEngineTest*(): seq[EngineSpec] = timeoutSeconds: 60, transactionPerPayload: 1, reOrgDepth: 5, + enableConfigureCLMock: true, ) result.add ReOrgBackToCanonicalTest( @@ -345,9 +351,9 @@ proc makeEngineTest*(): seq[EngineSpec] = transactionPerPayload: 50, reOrgDepth: 10, executeSidePayloadOnReOrg: true, + enableConfigureCLMock: true, ) -#[ const invalidReorgList = [ InvalidStateRoot, @@ -390,6 +396,7 @@ proc makeEngineTest*(): seq[EngineSpec] = reOrgFromCanonical: reOrgFromCanonical, emptyTransactions: true, invalidIndex: invalidIndex, + enableConfigureCLMock: true, ) result.add InvalidMissingAncestorReOrgSyncTest( @@ -399,12 +406,9 @@ proc makeEngineTest*(): seq[EngineSpec] = invalidField: invalidField, reOrgFromCanonical: reOrgFromCanonical, invalidIndex: invalidIndex, + enableConfigureCLMock: true, ) - -]# - - proc fillEngineTests*(): seq[TestDesc] = let list = makeEngineTest() for x in list: diff --git a/hive_integration/nodocker/engine/types.nim b/hive_integration/nodocker/engine/types.nim index aa8deeb7f..23a66733f 100644 --- a/hive_integration/nodocker/engine/types.nim +++ b/hive_integration/nodocker/engine/types.nim @@ -320,6 +320,9 @@ func blockHash*(x: ExecutableData): auto = func blockNumber*(x: ExecutableData): auto = x.basePayload.blockNumber +func stateRoot*(x: ExecutableData): auto = + x.basePayload.stateRoot + proc `parentHash=`*(x: var ExecutableData, val: auto) = x.basePayload.parentHash = val