nimbus-eth1/hive_integration/nodocker/engine/engine_tests.nim

422 lines
12 KiB
Nim

# Nimbus
# Copyright (c) 2023 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
# http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or
# http://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed except
# according to those terms.
import
eth/common/eth_types,
./engine/engine_spec,
./types,
./test_env,
./base_spec,
./cancun/customizer,
../../nimbus/common/chain_config
import
./engine/suggested_fee_recipient,
./engine/payload_attributes,
./engine/payload_execution,
./engine/invalid_ancestor,
./engine/invalid_payload,
./engine/prev_randao,
./engine/payload_id,
./engine/forkchoice,
./engine/versioning,
./engine/bad_hash,
./engine/fork_id,
./engine/reorg,
./engine/misc,
./engine/rpc
proc getGenesis(cs: EngineSpec, param: NetworkParams) =
# Set the terminal total difficulty
let realTTD = param.genesis.difficulty + cs.ttd.u256
param.config.terminalTotalDifficulty = some(realTTD)
if param.genesis.difficulty <= realTTD:
param.config.terminalTotalDifficultyPassed = some(true)
# Set the genesis timestamp if provided
if cs.genesisTimestamp != 0:
param.genesis.timestamp = cs.genesisTimestamp.EthTime
proc specExecute(ws: BaseSpec): bool =
let
cs = EngineSpec(ws)
forkConfig = ws.getForkConfig()
if forkConfig.isNil:
echo "because fork configuration is not possible, skip test: ", cs.getName()
return true
let conf = envConfig(forkConfig)
cs.getGenesis(conf.networkParams)
let env = TestEnv.new(conf)
env.engine.setRealTTD()
env.setupCLMock()
if cs.enableConfigureCLMock:
cs.configureCLMock(env.clMock)
result = cs.execute(env)
env.close()
# Execution specification reference:
# https://github.com/ethereum/execution-apis/blob/main/src/engine/specification.md
# Register all test combinations for Paris
proc makeEngineTest*(): seq[EngineSpec] =
# Misc Tests
# Pre-merge & merge fork occur at block 1, post-merge forks occur at block 2
result.add NonZeroPreMergeFork(forkHeight: 2)
# Payload Attributes Tests
block:
let list = [
InvalidPayloadAttributesTest(
description: "Zero timestamp",
customizer: BasePayloadAttributesCustomizer(
timestamp: some(0'u64),
),
),
InvalidPayloadAttributesTest(
description: "Parent timestamp",
customizer: TimestampDeltaPayloadAttributesCustomizer(
timestampDelta: -1,
),
),
]
for x in list:
result.add x
let y = x.clone()
y.syncing = true
result.add y
# Invalid Transaction ChainID Tests
result.add InvalidTxChainIDTest(
txType: some(TxLegacy),
)
result.add InvalidTxChainIDTest(
txType: some(TxEip1559),
)
# Invalid Ancestor Re-Org Tests (Reveal Via NewPayload)
for invalidIndex in [1, 9, 10]:
for emptyTxs in [false, true]:
result.add InvalidMissingAncestorReOrgTest(
slotsToSafe: 32,
slotsToFinalized: 64,
sidechainLength: 10,
invalidIndex: invalidIndex,
invalidField: InvalidStateRoot,
emptyTransactions: emptyTxs,
enableConfigureCLMock: true
)
# Invalid Payload Tests
const
invalidPayloadBlockFields = [
InvalidParentHash,
InvalidStateRoot,
InvalidReceiptsRoot,
InvalidNumber,
InvalidGasLimit,
InvalidGasUsed,
InvalidTimestamp,
InvalidPrevRandao,
RemoveTransaction,
]
for invalidField in invalidPayloadBlockFields:
for syncing in [false, true]:
if invalidField == InvalidStateRoot:
result.add InvalidPayloadTestCase(
invalidField: invalidField,
syncing: syncing,
emptyTransactions: true,
)
result.add InvalidPayloadTestCase(
invalidField: invalidField,
syncing: syncing,
)
# Register bad hash tests
for syncing in [false, true]:
for sidechain in [false, true]:
result.add BadHashOnNewPayload(
syncing: syncing,
sidechain: sidechain,
)
# Parent hash == block hash tests
result.add ParentHashOnNewPayload(syncing: false)
result.add ParentHashOnNewPayload(syncing: true)
result.add PayloadBuildAfterInvalidPayloadTest(
invalidField: InvalidStateRoot,
)
const forkchoiceStateField = [
HeadBlockHash,
SafeBlockHash,
FinalizedBlockHash,
]
# Register ForkchoiceUpdate tests
for field in forkchoiceStateField:
result.add InconsistentForkchoiceTest(field: field)
result.add ForkchoiceUpdatedUnknownBlockHashTest(field: field)
# PrevRandao opcode tests
result.add PrevRandaoTransactionTest(
txType: some(TxLegacy)
)
result.add PrevRandaoTransactionTest(
txType: some(TxEip1559),
)
# Suggested Fee Recipient Tests
result.add SuggestedFeeRecipientTest(
txType: some(TxLegacy),
transactionCount: 20,
)
result.add SuggestedFeeRecipientTest(
txType: some(TxEip1559),
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 = [
LatestOnNewPayload,
LatestOnHeadBlockHash,
SafeOnSafeBlockHash,
FinalizedOnFinalizedBlockHash,
]
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,
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
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,
enableConfigureCLMock: true,
)
result.add ReOrgBackToCanonicalTest(
slotsToSafe: 32,
slotsToFinalized: 64,
timeoutSeconds: 120,
transactionPerPayload: 50,
reOrgDepth: 10,
executeSidePayloadOnReOrg: true,
enableConfigureCLMock: true,
)
const
invalidReorgList = [
InvalidStateRoot,
InvalidReceiptsRoot,
# TODO: InvalidNumber, Test is causing a panic on the secondary node, disabling for now.
InvalidGasLimit,
InvalidGasUsed,
InvalidTimestamp,
# TODO: InvalidPrevRandao, Test consistently fails with Failed to set invalid block: missing trie node.
RemoveTransaction,
InvalidTransactionSignature,
InvalidTransactionNonce,
InvalidTransactionGas,
InvalidTransactionGasPrice,
InvalidTransactionValue,
# InvalidOmmers, Unsupported now
]
eightList = [
InvalidReceiptsRoot,
InvalidGasLimit,
InvalidGasUsed,
InvalidTimestamp,
InvalidPrevRandao
]
# Invalid Ancestor Re-Org Tests (Reveal Via Sync)
for invalidField in invalidReorgList:
for reOrgFromCanonical in [false, true]:
var invalidIndex = 9
if invalidField in eightList:
invalidIndex = 8
if invalidField == InvalidStateRoot:
result.add InvalidMissingAncestorReOrgSyncTest(
timeoutSeconds: 60,
slotsToSafe: 32,
slotsToFinalized: 64,
invalidField: invalidField,
reOrgFromCanonical: reOrgFromCanonical,
emptyTransactions: true,
invalidIndex: invalidIndex,
enableConfigureCLMock: true,
)
result.add InvalidMissingAncestorReOrgSyncTest(
timeoutSeconds: 60,
slotsToSafe: 32,
slotsToFinalized: 64,
invalidField: invalidField,
reOrgFromCanonical: reOrgFromCanonical,
invalidIndex: invalidIndex,
enableConfigureCLMock: true,
)
proc fillEngineTests*(): seq[TestDesc] =
let list = makeEngineTest()
for x in list:
result.add TestDesc(
name: x.getName(),
run: specExecute,
spec: x,
)
let engineTestList* = fillEngineTests()