Fix Engine API simulator
This commit is contained in:
parent
7de6199ba3
commit
9cf3c71a57
|
@ -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")
|
||||||
|
|
|
@ -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
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue