Fix Engine API simulator

This commit is contained in:
jangko 2023-11-05 14:45:56 +07:00
parent 7de6199ba3
commit 9cf3c71a57
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
6 changed files with 76 additions and 70 deletions

View File

@ -22,6 +22,7 @@ type
EngineSpec* = ref object of BaseSpec EngineSpec* = ref object of BaseSpec
ttd*: int64 ttd*: int64
chainFile*: string chainFile*: string
enableConfigureCLMock*: bool
method withMainFork*(tc: EngineSpec, fork: EngineFork): BaseSpec {.base.} = method withMainFork*(tc: EngineSpec, fork: EngineFork): BaseSpec {.base.} =
doAssert(false, "withMainFork not implemented") doAssert(false, "withMainFork not implemented")

View File

@ -14,7 +14,8 @@ import
eth/common/eth_types_rlp, eth/common/eth_types_rlp,
./engine_spec, ./engine_spec,
../cancun/customizer, ../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. # 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. # 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" % [ "Invalid Missing Ancestor Syncing ReOrg, $1, EmptyTxs=$2, CanonicalReOrg=$3, Invalid P$4" % [
$cs.invalidField, $cs.emptyTransactions, $cs.reOrgFromCanonical, $cs.invalidIndex] $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 = method execute(cs: InvalidMissingAncestorReOrgSyncTest, env: TestEnv): bool =
var sec = env.addEngine(true, cs.reOrgFromCanonical) var sec = env.addEngine(true, cs.reOrgFromCanonical)
@ -311,13 +315,13 @@ method execute(cs: InvalidMissingAncestorReOrgSyncTest, env: TestEnv): bool =
s.expectStatusEither([PayloadExecutionStatus.valid, PayloadExecutionStatus.syncing]) s.expectStatusEither([PayloadExecutionStatus.valid, PayloadExecutionStatus.syncing])
else: else:
debugEcho "i: ", i, " cs.invalidIndex: ", cs.invalidIndex let invalidBlock = executableDataToBlock(shadow.payloads[i])
doAssert(false, "Should not happen") testCond sec.client.setBlock(invalidBlock, shadow.payloads[i-1].blockNumber, shadow.payloads[i-1].stateRoot):
#invalidBlock, err = ExecutableDataToBlock(*shadow.payloads[i]) fatal "TEST ISSUE - Failed to set invalid block"
#if err = secondaryClient.SetBlock(invalidBlock, shadow.payloads[i-1].blockNumber, shadow.payloads[i-1].StateRoot); err != nil ( info "Invalid block successfully set",
# fatal "TEST ISSUE - Failed to set invalid block: " err) idx=i,
#) msg=payloadValidStr,
#info "Invalid block successfully set %d (%s): " i, payloadValidStr, invalidBlock.Hash()) hash=invalidBlock.header.blockHash.short
# Check that the second node has the correct head # Check that the second node has the correct head
var res = sec.client.latestHeader() var res = sec.client.latestHeader()
@ -348,76 +352,64 @@ method execute(cs: InvalidMissingAncestorReOrgSyncTest, env: TestEnv): bool =
number=head.blockNumber number=head.blockNumber
# If we are syncing through p2p, we need to keep polling until the client syncs the missing payloads # If we are syncing through p2p, we need to keep polling until the client syncs the missing payloads
#[for ( while true:
r = env.engine.client.newPayload(shadow.payloads[shadow.n]) let version = env.engine.version(shadow.payloads[shadow.n].timestamp)
info "Response from main client: " r.Status) let r = env.engine.client.newPayload(version, shadow.payloads[shadow.n])
s = env.engine.client.forkchoiceUpdated(ForkchoiceStateV1( info "Response from main client", status=r.get.status
headblockHash: shadow.payloads[shadow.n].blockHash,
), nil, shadow.payloads[shadow.n].timestamp)
info "Response from main client fcu: " s.Response.PayloadStatus)
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 # 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) # side chain that is immediately prior to the invalid payload (or zero if parent is PoW)
var lvh common.Hash var lvh: Web3Hash
if shadow.cAHeight != 0 || cs.invalidIndex != 1 ( if shadow.cAHeight != 0 or cs.invalidIndex != 1:
# Parent is NOT Proof of Work # Parent is NOT Proof of Work
lvh = shadow.payloads[cs.invalidIndex-1].blockHash lvh = shadow.payloads[cs.invalidIndex-1].blockHash
)
r.expectLatestValidHash(lvh) r.expectLatestValidHash(lvh)
# Response on ForkchoiceUpdated should be the same # Response on ForkchoiceUpdated should be the same
s.expectPayloadStatus(PayloadExecutionStatus.invalid) s.expectPayloadStatus(PayloadExecutionStatus.invalid)
s.expectLatestValidHash(lvh) s.expectLatestValidHash(lvh)
break break
elif test.PayloadStatus(r.Status.Status) == PayloadExecutionStatus.valid ( elif r.get.status == PayloadExecutionStatus.valid:
ctx, cancel = context.WithTimeout(t.TestContext, globals.RPCTimeout) let res = env.engine.client.latestHeader()
defer cancel() testCond res.isOk:
latestBlock, err = t.Eth.BlockByNumber(ctx, nil) fatal "Unable to get latest block: ", msg=res.error
if err != nil (
fatal "Unable to get latest block: " err)
)
# Print last shadow.n blocks, for debugging # Print last shadow.n blocks, for debugging
k = latestBlock.blockNumber().Int64() - int64(shadow.n) let latestNumber = res.get.blockNumber.truncate(int64)
if k < 0 ( var k = latestNumber - int64(shadow.n)
k = 0 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)
)
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 ( fatal "Client returned VALID on an invalid chain", status=r.get.status
case <-time.After(time.Second): return false
continue
case <-t.TimeoutContext.Done():
fatal "Timeout waiting for main client to detect invalid chain", t.TestName)
)
)
if !cs.reOrgFromCanonical ( if not cs.reOrgFromCanonical:
# We need to send the canonical chain to the main client here # We need to send the canonical chain to the main client here
for i = env.clMock.firstPoSBlockNumber.Uint64(); i <= env.clMock.latestExecutedPayload.blockNumber; i++ ( let start = env.clMock.firstPoSBlockNumber.get
if payload, ok = env.clMock.executedPayloadHistory[i]; ok ( let stop = env.clMock.latestExecutedPayload.blockNumber.uint64
r = env.engine.client.newPayload(payload) 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) r.expectStatus(PayloadExecutionStatus.valid)
)
)
)
# Resend the latest correct fcU # 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() 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 return true
)) ))

View File

@ -135,7 +135,10 @@ method getName(cs: TransactionReOrgTest): string =
name.add ", " & $cs.scenario name.add ", " & $cs.scenario
return name 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 shadow.tx.get.rlpHash
# Test transaction status after a forkchoiceUpdated re-orgs to an alternative hash where a transaction is not present # 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: if cs.scenario != TransactionReOrgScenarioReOrgBackIn:
# At this point we can broadcast the transaction and it will be included in the next payload # 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 # Data is the key where a `1` will be stored
let tx = shadow.sendTransaction(i) shadow.tx = some(shadow.sendTransaction(i))
# Get the receipt # Get the receipt
let receipt = env.engine.client.txReceipt(tx.rlpHash) let receipt = env.engine.client.txReceipt(shadow.txHash)
testCond receipt.isOk: testCond receipt.isErr:
fatal "Receipt obtained before tx included in block" fatal "Receipt obtained before tx included in block"
return true return true
@ -272,7 +275,7 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool =
if shadow.tx.isSome: if shadow.tx.isSome:
# Get the receipt # Get the receipt
let receipt = env.engine.client.txReceipt(shadow.txHash) let receipt = env.engine.client.txReceipt(shadow.txHash)
testCond receipt.isOk: testCond receipt.isErr:
fatal "Receipt obtained before tx included in block (NewPayload)" fatal "Receipt obtained before tx included in block (NewPayload)"
return true return true
, ,
@ -350,7 +353,7 @@ method execute(cs: TransactionReOrgTest, env: TestEnv): bool =
# Get the receipt # Get the receipt
let receipt = env.engine.client.txReceipt(shadow.txHash) let receipt = env.engine.client.txReceipt(shadow.txHash)
testCond receipt.isErr: testCond receipt.isOk:
fatal "Receipt not obtained after tx included in block" fatal "Receipt not obtained after tx included in block"
return true return true

View File

@ -599,3 +599,6 @@ template expectStorageEqual*(res: Result[UInt256, string], account: EthAddress,
if res.get != expectedValue: if res.get != expectedValue:
return err("invalid wd storage at $1 is $2, expect $3" % [ return err("invalid wd storage at $1 is $2, expect $3" % [
account.toHex, $res.get, $expectedValue]) account.toHex, $res.get, $expectedValue])
proc setBlock*(client: RpcClient, blk: EthBlock, blockNumber: Web3Quantity, stateRoot: Web3Hash): bool =
return true

View File

@ -58,7 +58,8 @@ proc specExecute(ws: BaseSpec): bool =
let env = TestEnv.new(conf) let env = TestEnv.new(conf)
env.engine.setRealTTD() env.engine.setRealTTD()
env.setupCLMock() env.setupCLMock()
#cs.configureCLMock(env.clMock) if cs.enableConfigureCLMock:
cs.configureCLMock(env.clMock)
result = cs.execute(env) result = cs.execute(env)
env.close() env.close()
@ -113,6 +114,7 @@ proc makeEngineTest*(): seq[EngineSpec] =
invalidIndex: invalidIndex, invalidIndex: invalidIndex,
invalidField: InvalidStateRoot, invalidField: InvalidStateRoot,
emptyTransactions: emptyTxs, emptyTransactions: emptyTxs,
enableConfigureCLMock: true
) )
# Invalid Payload Tests # Invalid Payload Tests
@ -300,16 +302,19 @@ proc makeEngineTest*(): seq[EngineSpec] =
result.add ReOrgBackFromSyncingTest( result.add ReOrgBackFromSyncingTest(
slotsToSafe: 32, slotsToSafe: 32,
slotsToFinalized: 64, slotsToFinalized: 64,
enableConfigureCLMock: true,
) )
result.add ReOrgPrevValidatedPayloadOnSideChainTest( result.add ReOrgPrevValidatedPayloadOnSideChainTest(
slotsToSafe: 32, slotsToSafe: 32,
slotsToFinalized: 64, slotsToFinalized: 64,
enableConfigureCLMock: true,
) )
result.add SafeReOrgToSideChainTest( result.add SafeReOrgToSideChainTest(
slotsToSafe: 1, slotsToSafe: 1,
slotsToFinalized: 2, slotsToFinalized: 2,
enableConfigureCLMock: true,
) )
# Re-org a transaction out of a block, or into a new block # Re-org a transaction out of a block, or into a new block
@ -336,6 +341,7 @@ proc makeEngineTest*(): seq[EngineSpec] =
timeoutSeconds: 60, timeoutSeconds: 60,
transactionPerPayload: 1, transactionPerPayload: 1,
reOrgDepth: 5, reOrgDepth: 5,
enableConfigureCLMock: true,
) )
result.add ReOrgBackToCanonicalTest( result.add ReOrgBackToCanonicalTest(
@ -345,9 +351,9 @@ proc makeEngineTest*(): seq[EngineSpec] =
transactionPerPayload: 50, transactionPerPayload: 50,
reOrgDepth: 10, reOrgDepth: 10,
executeSidePayloadOnReOrg: true, executeSidePayloadOnReOrg: true,
enableConfigureCLMock: true,
) )
#[
const const
invalidReorgList = [ invalidReorgList = [
InvalidStateRoot, InvalidStateRoot,
@ -390,6 +396,7 @@ proc makeEngineTest*(): seq[EngineSpec] =
reOrgFromCanonical: reOrgFromCanonical, reOrgFromCanonical: reOrgFromCanonical,
emptyTransactions: true, emptyTransactions: true,
invalidIndex: invalidIndex, invalidIndex: invalidIndex,
enableConfigureCLMock: true,
) )
result.add InvalidMissingAncestorReOrgSyncTest( result.add InvalidMissingAncestorReOrgSyncTest(
@ -399,12 +406,9 @@ proc makeEngineTest*(): seq[EngineSpec] =
invalidField: invalidField, invalidField: invalidField,
reOrgFromCanonical: reOrgFromCanonical, reOrgFromCanonical: reOrgFromCanonical,
invalidIndex: invalidIndex, invalidIndex: invalidIndex,
enableConfigureCLMock: true,
) )
]#
proc fillEngineTests*(): seq[TestDesc] = proc fillEngineTests*(): seq[TestDesc] =
let list = makeEngineTest() let list = makeEngineTest()
for x in list: for x in list:

View File

@ -320,6 +320,9 @@ func blockHash*(x: ExecutableData): auto =
func blockNumber*(x: ExecutableData): auto = func blockNumber*(x: ExecutableData): auto =
x.basePayload.blockNumber x.basePayload.blockNumber
func stateRoot*(x: ExecutableData): auto =
x.basePayload.stateRoot
proc `parentHash=`*(x: var ExecutableData, val: auto) = proc `parentHash=`*(x: var ExecutableData, val: auto) =
x.basePayload.parentHash = val x.basePayload.parentHash = val