Skip to content

Commit

Permalink
Fix Engine API simulator
Browse files Browse the repository at this point in the history
  • Loading branch information
jangko committed Nov 5, 2023
1 parent 7de6199 commit 9cf3c71
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 69 deletions.
1 change: 1 addition & 0 deletions hive_integration/nodocker/engine/engine/engine_spec.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
106 changes: 49 additions & 57 deletions hive_integration/nodocker/engine/engine/invalid_ancestor.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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(
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

let fcu = ForkchoiceStateV1(
headblockHash: shadow.payloads[shadow.n].blockHash,
), nil, shadow.payloads[shadow.n].timestamp)
info "Response from main client fcu: " s.Response.PayloadStatus)
)
let s = env.engine.client.forkchoiceUpdated(version, fcu)
info "Response from main client fcu", status=s.get.payloadStatus.status

if r.Status.Status == PayloadExecutionStatus.invalid (
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
))

Expand Down
15 changes: 9 additions & 6 deletions hive_integration/nodocker/engine/engine/reorg.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
,
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions hive_integration/nodocker/engine/engine_client.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
16 changes: 10 additions & 6 deletions hive_integration/nodocker/engine/engine_tests.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -113,6 +114,7 @@ proc makeEngineTest*(): seq[EngineSpec] =
invalidIndex: invalidIndex,
invalidField: InvalidStateRoot,
emptyTransactions: emptyTxs,
enableConfigureCLMock: true
)

# Invalid Payload Tests
Expand Down Expand Up @@ -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
Expand All @@ -336,6 +341,7 @@ proc makeEngineTest*(): seq[EngineSpec] =
timeoutSeconds: 60,
transactionPerPayload: 1,
reOrgDepth: 5,
enableConfigureCLMock: true,
)

result.add ReOrgBackToCanonicalTest(
Expand All @@ -345,9 +351,9 @@ proc makeEngineTest*(): seq[EngineSpec] =
transactionPerPayload: 50,
reOrgDepth: 10,
executeSidePayloadOnReOrg: true,
enableConfigureCLMock: true,
)

#[
const
invalidReorgList = [
InvalidStateRoot,
Expand Down Expand Up @@ -390,6 +396,7 @@ proc makeEngineTest*(): seq[EngineSpec] =
reOrgFromCanonical: reOrgFromCanonical,
emptyTransactions: true,
invalidIndex: invalidIndex,
enableConfigureCLMock: true,
)

result.add InvalidMissingAncestorReOrgSyncTest(
Expand All @@ -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:
Expand Down
3 changes: 3 additions & 0 deletions hive_integration/nodocker/engine/types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down

0 comments on commit 9cf3c71

Please sign in to comment.