More Engine API tests

This commit is contained in:
jangko 2023-11-01 18:09:49 +07:00
parent 682b160777
commit 69254e614f
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
11 changed files with 306 additions and 288 deletions

View File

@ -10,10 +10,12 @@
import import
std/strutils, std/strutils,
./engine_spec chronicles,
./engine_spec,
../cancun/customizer
type type
ForkchoiceStateField = enum ForkchoiceStateField* = enum
HeadblockHash = "Head" HeadblockHash = "Head"
SafeblockHash = "Safe" SafeblockHash = "Safe"
FinalizedblockHash = "Finalized" FinalizedblockHash = "Finalized"
@ -22,14 +24,17 @@ type
InconsistentForkchoiceTest* = ref object of EngineSpec InconsistentForkchoiceTest* = ref object of EngineSpec
field*: ForkchoiceStateField field*: ForkchoiceStateField
Shadow = ref object
canon: seq[ExecutableData]
alt: seq[ExecutableData]
method withMainFork(cs: InconsistentForkchoiceTest, fork: EngineFork): BaseSpec = method withMainFork(cs: InconsistentForkchoiceTest, fork: EngineFork): BaseSpec =
var res = cs.clone() var res = cs.clone()
res.mainFork = fork res.mainFork = fork
return res return res
method getName(cs: InconsistentForkchoiceTest): string = method getName(cs: InconsistentForkchoiceTest): string =
return "Inconsistent %s in ForkchoiceState", cs.Field) return "Inconsistent $1 in ForkchoiceState" % [$cs.field]
)
# Send an inconsistent ForkchoiceState with a known payload that belongs to a side chain as head, safe or finalized. # Send an inconsistent ForkchoiceState with a known payload that belongs to a side chain as head, safe or finalized.
method execute(cs: InconsistentForkchoiceTest, env: TestEnv): bool = method execute(cs: InconsistentForkchoiceTest, env: TestEnv): bool =
@ -37,55 +42,59 @@ method execute(cs: InconsistentForkchoiceTest, env: TestEnv): bool =
let ok = waitFor env.clMock.waitForTTD() let ok = waitFor env.clMock.waitForTTD()
testCond ok testCond ok
shadow.canon = make([]*ExecutableData, 0) var shadow = Shadow()
shadow.alt = make([]*ExecutableData, 0)
# Produce blocks before starting the test # Produce blocks before starting the test
env.clMock.produceBlocks(3, BlockProcessCallbacks( let pbRes = env.clMock.produceBlocks(3, BlockProcessCallbacks(
onGetPayload: proc(): bool = onGetPayload: proc(): bool =
# Generate and send an alternative side chain # Generate and send an alternative side chain
customData = CustomPayloadData() var customData = CustomPayloadData(
customData.ExtraData = &([]byte(0x01)) extraData: some(@[0x01.byte])
if len(shadow.alt) > 0 (
customData.parentHash = &shadow.alt[len(shadow.alt)-1].blockHash
) )
alternativePayload, err = customData.CustomizePayload(env.clMock.latestPayloadBuilt)
if err != nil ( if shadow.alt.len > 0:
fatal "Unable to construct alternative payload: %v", t.TestName, err) customData.parentHash = some(ethHash shadow.alt[^1].blockHash)
)
shadow.alt = append(shadow.alt, alternativePayload) let altPayload = customData.customizePayload(env.clMock.latestExecutableData)
latestCanonicalPayload = env.clMock.latestPayloadBuilt shadow.alt.add altPayload
shadow.canon = append(shadow.canon, &latestCanonicalPayload) shadow.canon.add env.clMock.latestExecutableData
# Send the alternative payload # Send the alternative payload
r = env.engine.client.newPayload(alternativePayload) let version = env.engine.version(altPayload.timestamp)
r.expectStatusEither(PayloadExecutionStatus.valid, test.Accepted) let r = env.engine.client.newPayload(version, altPayload)
), r.expectStatusEither([PayloadExecutionStatus.valid, PayloadExecutionStatus.accepted])
return true
)) ))
testCond pbRes
# Send the invalid ForkchoiceStates # Send the invalid ForkchoiceStates
inconsistentFcU = ForkchoiceStateV1( var inconsistentFcU = ForkchoiceStateV1(
headblockHash: shadow.canon[len(shadow.alt)-1].blockHash, headblockHash: shadow.canon[len(shadow.alt)-1].blockHash,
safeblockHash: shadow.canon[len(shadow.alt)-2].blockHash, safeblockHash: shadow.canon[len(shadow.alt)-2].blockHash,
finalizedblockHash: shadow.canon[len(shadow.alt)-3].blockHash, finalizedblockHash: shadow.canon[len(shadow.alt)-3].blockHash,
) )
switch cs.Field (
case HeadblockHash: case cs.field
of HeadblockHash:
inconsistentFcU.headblockHash = shadow.alt[len(shadow.alt)-1].blockHash inconsistentFcU.headblockHash = shadow.alt[len(shadow.alt)-1].blockHash
case SafeblockHash: of SafeblockHash:
inconsistentFcU.safeblockHash = shadow.alt[len(shadow.canon)-2].blockHash inconsistentFcU.safeblockHash = shadow.alt[len(shadow.canon)-2].blockHash
case FinalizedblockHash: of FinalizedblockHash:
inconsistentFcU.finalizedblockHash = shadow.alt[len(shadow.canon)-3].blockHash inconsistentFcU.finalizedblockHash = shadow.alt[len(shadow.canon)-3].blockHash
)
r = env.engine.client.forkchoiceUpdated(inconsistentFcU, nil, env.clMock.latestPayloadBuilt.timestamp) let version = env.engine.version(env.clMock.latestPayloadBuilt.timestamp)
var r = env.engine.client.forkchoiceUpdated(version, inconsistentFcU)
r.expectError() r.expectError()
# Return to the canonical chain # Return to the canonical chain
r = env.engine.client.forkchoiceUpdated(env.clMock.latestForkchoice, nil, env.clMock.latestPayloadBuilt.timestamp) r = env.engine.client.forkchoiceUpdated(version, env.clMock.latestForkchoice)
r.expectPayloadStatus(PayloadExecutionStatus.valid) r.expectPayloadStatus(PayloadExecutionStatus.valid)
) return true
type type
ForkchoiceUpdatedUnknownblockHashTest* = ref object of EngineSpec ForkchoiceUpdatedUnknownblockHashTest* = ref object of EngineSpec
field: ForkchoiceStateField field*: ForkchoiceStateField
method withMainFork(cs: ForkchoiceUpdatedUnknownblockHashTest, fork: EngineFork): BaseSpec = method withMainFork(cs: ForkchoiceUpdatedUnknownblockHashTest, fork: EngineFork): BaseSpec =
var res = cs.clone() var res = cs.clone()
@ -93,8 +102,7 @@ method withMainFork(cs: ForkchoiceUpdatedUnknownblockHashTest, fork: EngineFork)
return res return res
method getName(cs: ForkchoiceUpdatedUnknownblockHashTest): string = method getName(cs: ForkchoiceUpdatedUnknownblockHashTest): string =
return "Unknown %sblockHash", cs.Field) return "Unknown $1blockHash" % [$cs.field]
)
# Send an inconsistent ForkchoiceState with a known payload that belongs to a side chain as head, safe or finalized. # Send an inconsistent ForkchoiceState with a known payload that belongs to a side chain as head, safe or finalized.
method execute(cs: ForkchoiceUpdatedUnknownblockHashTest, env: TestEnv): bool = method execute(cs: ForkchoiceUpdatedUnknownblockHashTest, env: TestEnv): bool =
@ -103,67 +111,66 @@ method execute(cs: ForkchoiceUpdatedUnknownblockHashTest, env: TestEnv): bool =
testCond ok testCond ok
# Produce blocks before starting the test # Produce blocks before starting the test
env.clMock.produceBlocks(5, BlockProcessCallbacks()) testCond env.clMock.produceBlocks(5, BlockProcessCallbacks())
# Generate a random block hash # Generate a random block hash
randomblockHash = common.Hash256() let randomblockHash = Web3Hash.randomBytes()
randomBytes(randomblockHash[:])
if cs.Field == HeadblockHash ( if cs.field == HeadblockHash:
let fcu = ForkchoiceStateV1(
forkchoiceStateUnknownHeadHash = ForkchoiceStateV1(
headblockHash: randomblockHash, headblockHash: randomblockHash,
safeblockHash: env.clMock.latestForkchoice.safeblockHash, safeblockHash: env.clMock.latestForkchoice.safeblockHash,
finalizedblockHash: env.clMock.latestForkchoice.finalizedblockHash, finalizedblockHash: env.clMock.latestForkchoice.finalizedblockHash,
) )
t.Logf("INFO (%v) forkchoiceStateUnknownHeadHash: %v\n", t.TestName, forkchoiceStateUnknownHeadHash) info "forkchoiceStateUnknownHeadHash",
head=fcu.headblockHash.short,
safe=fcu.safeblockHash.short,
final=fcu.finalizedblockHash.short
# Execution specification:: # Execution specification::
# - (payloadStatus: (status: SYNCING, latestValidHash: null, validationError: null), payloadId: null) # - (payloadStatus: (status: SYNCING, latestValidHash: null, validationError: null), payloadId: null)
# if forkchoiceState.headblockHash references an unknown payload or a payload that can't be validated # if forkchoiceState.headblockHash references an unknown payload or a payload that can't be validated
# because requisite data for the validation is missing # because requisite data for the validation is missing
r = env.engine.client.forkchoiceUpdated(forkchoiceStateUnknownHeadHash, nil, env.clMock.latestExecutedPayload.timestamp) let version = env.engine.version(env.clMock.latestExecutedPayload.timestamp)
var r = env.engine.client.forkchoiceUpdated(version, fcu)
r.expectPayloadStatus(PayloadExecutionStatus.syncing) r.expectPayloadStatus(PayloadExecutionStatus.syncing)
payloadAttributes = env.clMock.latestPayloadAttributes var payloadAttributes = env.clMock.latestPayloadAttributes
payloadAttributes.timestamp += 1 payloadAttributes.timestamp = w3Qty(payloadAttributes.timestamp, 1)
# Test again using PayloadAttributes, should also return SYNCING and no PayloadID # Test again using PayloadAttributes, should also return SYNCING and no PayloadID
r = env.engine.client.forkchoiceUpdated(forkchoiceStateUnknownHeadHash, r = env.engine.client.forkchoiceUpdated(version, fcu, some(payloadAttributes))
&payloadAttributes, env.clMock.latestExecutedPayload.timestamp)
r.expectPayloadStatus(PayloadExecutionStatus.syncing) r.expectPayloadStatus(PayloadExecutionStatus.syncing)
r.ExpectPayloadID(nil) r.expectPayloadID(none(PayloadID))
else: else:
env.clMock.produceSingleBlock(BlockProcessCallbacks( let pbRes = env.clMock.produceSingleBlock(BlockProcessCallbacks(
# Run test after a new payload has been broadcast # Run test after a new payload has been broadcast
onNewPayloadBroadcast: proc(): bool = onNewPayloadBroadcast: proc(): bool =
var fcu = ForkchoiceStateV1(
forkchoiceStateRandomHash = ForkchoiceStateV1(
headblockHash: env.clMock.latestExecutedPayload.blockHash, headblockHash: env.clMock.latestExecutedPayload.blockHash,
safeblockHash: env.clMock.latestForkchoice.safeblockHash, safeblockHash: env.clMock.latestForkchoice.safeblockHash,
finalizedblockHash: env.clMock.latestForkchoice.finalizedblockHash, finalizedblockHash: env.clMock.latestForkchoice.finalizedblockHash,
) )
if cs.Field == SafeblockHash ( if cs.field == SafeblockHash:
forkchoiceStateRandomHash.safeblockHash = randomblockHash fcu.safeblockHash = randomblockHash
elif cs.Field == FinalizedblockHash ( elif cs.field == FinalizedblockHash:
forkchoiceStateRandomHash.finalizedblockHash = randomblockHash fcu.finalizedblockHash = randomblockHash
)
r = env.engine.client.forkchoiceUpdated(forkchoiceStateRandomHash, nil, env.clMock.latestExecutedPayload.timestamp) let version = env.engine.version(env.clMock.latestExecutedPayload.timestamp)
var r = env.engine.client.forkchoiceUpdated(version, fcu)
r.expectError() r.expectError()
payloadAttributes = env.clMock.latestPayloadAttributes var payloadAttributes = env.clMock.latestPayloadAttributes
payloadAttributes.Random = common.Hash256() payloadAttributes.prevRandao = w3Hash()
payloadAttributes.SuggestedFeeRecipient = common.Address() payloadAttributes.suggestedFeeRecipient = w3Address()
# Test again using PayloadAttributes, should also return INVALID and no PayloadID # Test again using PayloadAttributes, should also return INVALID and no PayloadID
r = env.engine.client.forkchoiceUpdated(forkchoiceStateRandomHash, r = env.engine.client.forkchoiceUpdated(version, fcu, some(payloadAttributes))
&payloadAttributes, env.clMock.latestExecutedPayload.timestamp)
r.expectError() r.expectError()
return true
),
)) ))
) testCond pbRes
)
return true

View File

@ -350,7 +350,7 @@ method execute(cs: NewPayloadOnSyncingClientTest, env: TestEnv): bool =
onGetPayload: proc(): bool = onGetPayload: proc(): bool =
# Send the new payload from the second client to the first, it won't be able to validate it # Send the new payload from the second client to the first, it won't be able to validate it
r = env.engine.client.newPayload(env.clMock.latestPayloadBuilt) r = env.engine.client.newPayload(env.clMock.latestPayloadBuilt)
r.expectStatusEither(test.Accepted, PayloadExecutionStatus.syncing) r.expectStatusEither(PayloadExecutionStatus.accepted, PayloadExecutionStatus.syncing)
r.expectLatestValidHash(nil) r.expectLatestValidHash(nil)
# Send the forkchoiceUpdated with a reference to the valid payload on the SYNCING client. # Send the forkchoiceUpdated with a reference to the valid payload on the SYNCING client.

View File

@ -10,11 +10,26 @@
import import
std/strutils, std/strutils,
eth/common,
chronicles,
./engine_spec ./engine_spec
type type
PrevRandaoTransactionTest* = ref object of EngineSpec PrevRandaoTransactionTest* = ref object of EngineSpec
blockCount int blockCount*: int
Shadow = ref object
startBlockNumber: uint64
blockCount: int
currentTxIndex: int
txs: seq[Transaction]
proc checkPrevRandaoValue(client: RpcClient, expectedPrevRandao: common.Hash256, blockNumber: uint64): bool =
let storageKey = blockNumber.u256
let r = client.storageAt(prevRandaoContractAddr, storageKey)
let expected = UInt256.fromBytesBE(expectedPrevRandao.data)
r.expectStorageEqual(expected)
return true
method withMainFork(cs: PrevRandaoTransactionTest, fork: EngineFork): BaseSpec = method withMainFork(cs: PrevRandaoTransactionTest, fork: EngineFork): BaseSpec =
var res = cs.clone() var res = cs.clone()
@ -22,66 +37,56 @@ method withMainFork(cs: PrevRandaoTransactionTest, fork: EngineFork): BaseSpec =
return res return res
method getName(cs: PrevRandaoTransactionTest): string = method getName(cs: PrevRandaoTransactionTest): string =
return "PrevRandao Opcode Transactions Test (%s)", cs.txType) "PrevRandao Opcode Transactions Test ($1)" % [$cs.txType]
)
method execute(cs: PrevRandaoTransactionTest, env: TestEnv): bool = method execute(cs: PrevRandaoTransactionTest, env: TestEnv): bool =
let ok = waitFor env.clMock.waitForTTD() let ok = waitFor env.clMock.waitForTTD()
testCond ok testCond ok
# Create a single block to not having to build on top of genesis # Create a single block to not having to build on top of genesis
env.clMock.produceSingleBlock(BlockProcessCallbacks()) testCond env.clMock.produceSingleBlock(BlockProcessCallbacks())
startBlockNumber = env.clMock.latestHeader.blockNumber.Uint64() + 1
var shadow = Shadow(
startBlockNumber: env.clMock.latestHeader.blockNumber.truncate(uint64) + 1,
# Send transactions in PoS, the value of the storage in these blocks must match the prevRandao value # Send transactions in PoS, the value of the storage in these blocks must match the prevRandao value
var ( blockCount: 10,
blockCount = 10 currentTxIndex: 0,
currentTxIndex = 0
txs = make([]typ.Transaction, 0)
) )
if cs.blockCount > 0 (
blockCount = cs.blockCount if cs.blockCount > 0:
) shadow.blockCount = cs.blockCount
env.clMock.produceBlocks(blockCount, BlockProcessCallbacks(
let pbRes = env.clMock.produceBlocks(shadow.blockCount, BlockProcessCallbacks(
onPayloadProducerSelected: proc(): bool = onPayloadProducerSelected: proc(): bool =
tx, err = env.sendNextTx( let tc = BaseTx(
t.TestContext, recipient: some(prevRandaoContractAddr),
t.Engine, amount: 0.u256,
&BaseTx(
recipient: prevRandaoContractAddr,
amount: big0,
payload: nil,
txType: cs.txType, txType: cs.txType,
gasLimit: 75000, gasLimit: 75000,
),
) )
if err != nil ( let tx = env.makeNextTx(tc)
fatal "Error trying to send transaction: %v", t.TestName, err) let ok = env.sendTx(tx)
) testCond ok:
txs = append(txs, tx) fatal "Error trying to send transaction"
currentTxIndex++
), shadow.txs.add(tx)
inc shadow.currentTxIndex
return true
,
onForkchoiceBroadcast: proc(): bool = onForkchoiceBroadcast: proc(): bool =
# Check the transaction tracing, which is client specific # Check the transaction tracing, which is client specific
expectedPrevRandao = env.clMock.prevRandaoHistory[env.clMock.latestHeader.blockNumber.Uint64()+1] let expectedPrevRandao = env.clMock.prevRandaoHistory[env.clMock.latestHeader.blockNumber.truncate(uint64)+1]
ctx, cancel = context.WithTimeout(t.TestContext, globals.RPCTimeout) let res = debugPrevRandaoTransaction(env.engine.client, shadow.txs[shadow.currentTxIndex-1], expectedPrevRandao)
defer cancel() testCond res.isOk:
if err = DebugPrevRandaoTransaction(ctx, t.Client.RPC(), t.Client.Type, txs[currentTxIndex-1], fatal "Error during transaction tracing", msg=res.error
&expectedPrevRandao); err != nil (
fatal "Error during transaction tracing: %v", t.TestName, err) return true
)
),
)) ))
testCond pbRes
for i = uint64(startBlockNumber); i <= env.clMock.latestExecutedPayload.blockNumber; i++ ( for i in shadow.startBlockNumber..env.clMock.latestExecutedPayload.blockNumber.uint64:
checkPrevRandaoValue(t, env.clMock.prevRandaoHistory[i], i) if not checkPrevRandaoValue(env.engine.client, env.clMock.prevRandaoHistory[i], i):
) fatal "wrong prev randao", index=i
) return false
func checkPrevRandaoValue(t *test.Env, expectedPrevRandao common.Hash, blockNumber uint64) ( return true
storageKey = common.Hash256()
storageKey[31] = byte(blockNumber)
r = env.engine.client.TestStorageAt(globals.PrevRandaoContractAddr, storageKey, nil)
r.ExpectStorageEqual(expectedPrevRandao)
)

View File

@ -603,7 +603,7 @@ method execute(cs: ReOrgBackFromSyncingTest, env: TestEnv): bool =
# Re-org to the unavailable sidechain in the middle of block production # Re-org to the unavailable sidechain in the middle of block production
# to be able to re-org back to the canonical chain # to be able to re-org back to the canonical chain
r = env.engine.client.newPayload(sidechainPayloads[len(sidechainPayloads)-1]) r = env.engine.client.newPayload(sidechainPayloads[len(sidechainPayloads)-1])
r.expectStatusEither(PayloadExecutionStatus.syncing, test.Accepted) r.expectStatusEither(PayloadExecutionStatus.syncing, PayloadExecutionStatus.accepted)
r.expectLatestValidHash(nil) r.expectLatestValidHash(nil)
# We are going to send one of the alternative payloads and fcU to it # We are going to send one of the alternative payloads and fcU to it
forkchoiceUpdatedBack = api.ForkchoiceStateV1( forkchoiceUpdatedBack = api.ForkchoiceStateV1(
@ -805,7 +805,7 @@ method execute(cs: SafeReOrgToSideChainTest, env: TestEnv): bool =
onGetpayload: proc(): bool = onGetpayload: proc(): bool =
for _, p = range sidechainPayloads ( for _, p = range sidechainPayloads (
r = env.engine.client.newPayload(p) r = env.engine.client.newPayload(p)
r.expectStatusEither(PayloadExecutionStatus.valid, test.Accepted) r.expectStatusEither(PayloadExecutionStatus.valid, PayloadExecutionStatus.accepted)
) )
r = env.engine.client.forkchoiceUpdated(api.ForkchoiceStateV1( r = env.engine.client.forkchoiceUpdated(api.ForkchoiceStateV1(
headBlockHash: sidechainPayloads[1].blockHash, headBlockHash: sidechainPayloads[1].blockHash,

View File

@ -13,7 +13,7 @@ import
./engine_spec ./engine_spec
type type
BlockStatusRPCcheckType = enum BlockStatusRPCcheckType* = enum
LatestOnNewPayload = "Latest Block on NewPayload" LatestOnNewPayload = "Latest Block on NewPayload"
LatestOnHeadblockHash = "Latest Block on HeadblockHash Update" LatestOnHeadblockHash = "Latest Block on HeadblockHash Update"
SafeOnSafeblockHash = "Safe Block on SafeblockHash Update" SafeOnSafeblockHash = "Safe Block on SafeblockHash Update"
@ -21,7 +21,7 @@ type
type type
BlockStatus* = ref object of EngineSpec BlockStatus* = ref object of EngineSpec
checkType: BlockStatusRPCcheckType checkType*: BlockStatusRPCcheckType
# TODO: Syncing bool # TODO: Syncing bool
method withMainFork(cs: BlockStatus, fork: EngineFork): BaseSpec = method withMainFork(cs: BlockStatus, fork: EngineFork): BaseSpec =
@ -46,7 +46,7 @@ method execute(cs: BlockStatus, env: TestEnv): bool =
else: else:
number = Finalized number = Finalized
p = env.engine.client.TestHeaderByNumber(number) p = env.engine.client.headerByNumber(number)
p.expectError() p.expectError()
) )
@ -59,22 +59,19 @@ method execute(cs: BlockStatus, env: TestEnv): bool =
let tc = BaseTx( let tc = BaseTx(
recipient: &ZeroAddr, recipient: &ZeroAddr,
amount: 1.u256, amount: 1.u256,
payload: nil,
txType: cs.txType, txType: cs.txType,
gasLimit: 75000, gasLimit: 75000,
ForkConfig: t.ForkConfig,
), ),
tx, err = env.sendNextTx( let ok = env.sendNextTx(tc)
) testCond ok:
if err != nil (
fatal "Error trying to send transaction: %v", err) fatal "Error trying to send transaction: %v", err)
) )
), ),
) )
switch b.checkType ( case b.checkType (
case LatestOnNewPayload: of LatestOnNewPayload:
callbacks.onGetPayload = proc(): bool callbacks.onGetPayload = proc(): bool
r = env.engine.client.latestHeader() r = env.engine.client.latestHeader()
r.expectHash(env.clMock.latestForkchoice.headblockHash) r.expectHash(env.clMock.latestForkchoice.headblockHash)
@ -88,27 +85,22 @@ method execute(cs: BlockStatus, env: TestEnv): bool =
# Check that the receipt for the transaction we just sent is still not available # Check that the receipt for the transaction we just sent is still not available
q = env.engine.client.txReceipt(tx.Hash()) q = env.engine.client.txReceipt(tx.Hash())
q.expectError() q.expectError()
) of LatestOnHeadblockHash:
case LatestOnHeadblockHash:
callbacks.onForkchoiceBroadcast = proc(): bool callbacks.onForkchoiceBroadcast = proc(): bool
r = env.engine.client.latestHeader() r = env.engine.client.latestHeader()
r.expectHash(env.clMock.latestForkchoice.headblockHash) r.expectHash(env.clMock.latestForkchoice.headblockHash)
s = env.engine.client.txReceipt(tx.Hash()) s = env.engine.client.txReceipt(tx.Hash())
s.ExpectTransactionHash(tx.Hash()) s.ExpectTransactionHash(tx.Hash())
) of SafeOnSafeblockHash:
case SafeOnSafeblockHash:
callbacks.onSafeBlockChange = proc(): bool callbacks.onSafeBlockChange = proc(): bool
r = env.engine.client.TestHeaderByNumber(Safe) r = env.engine.client.headerByNumber(Safe)
r.expectHash(env.clMock.latestForkchoice.safeblockHash) r.expectHash(env.clMock.latestForkchoice.safeblockHash)
) of FinalizedOnFinalizedblockHash:
case FinalizedOnFinalizedblockHash:
callbacks.onFinalizedBlockChange = proc(): bool callbacks.onFinalizedBlockChange = proc(): bool
r = env.engine.client.TestHeaderByNumber(Finalized) r = env.engine.client.headerByNumber(Finalized)
r.expectHash(env.clMock.latestForkchoice.finalizedblockHash) r.expectHash(env.clMock.latestForkchoice.finalizedblockHash)
)
)
# Perform the test # Perform the test
env.clMock.produceSingleBlock(callbacks) env.clMock.produceSingleBlock(callbacks)
)

View File

@ -10,11 +10,13 @@
import import
std/strutils, std/strutils,
./engine_spec chronicles,
./engine_spec,
../../../../nimbus/transaction
type type
SuggestedFeeRecipientTest* = ref object of EngineSpec SuggestedFeeRecipientTest* = ref object of EngineSpec
transactionCount: int transactionCount*: int
method withMainFork(cs: SuggestedFeeRecipientTest, fork: EngineFork): BaseSpec = method withMainFork(cs: SuggestedFeeRecipientTest, fork: EngineFork): BaseSpec =
var res = cs.clone() var res = cs.clone()
@ -38,54 +40,55 @@ method execute(cs: SuggestedFeeRecipientTest, env: TestEnv): bool =
txRecipient = EthAddress.randomBytes() txRecipient = EthAddress.randomBytes()
# Send multiple transactions # Send multiple transactions
for i = 0; i < cs.transactionCount; i++ ( for i in 0..<cs.transactionCount:
_, err = env.sendNextTx( let tc = BaseTx(
t.TestContext, recipient: some(txRecipient),
t.Engine, amount: 0.u256,
&BaseTx(
recipient: &txRecipient,
amount: big0,
payload: nil,
txType: cs.txType, txType: cs.txType,
gasLimit: 75000, gasLimit: 75000,
),
)
if err != nil (
fatal "Error trying to send transaction: %v", t.TestName, err)
)
) )
let ok = env.sendNextTx(env.engine, tc)
testCond ok:
fatal "Error trying to send transaction"
# Produce the next block with the fee recipient set # Produce the next block with the fee recipient set
env.clMock.nextFeeRecipient = feeRecipient env.clMock.nextFeeRecipient = feeRecipient
env.clMock.produceSingleBlock(BlockProcessCallbacks()) testCond env.clMock.produceSingleBlock(BlockProcessCallbacks())
# Calculate the fees and check that they match the balance of the fee recipient # Calculate the fees and check that they match the balance of the fee recipient
r = env.engine.client.TestBlockByNumber(Head) let r = env.engine.client.latestblock()
r.ExpecttransactionCountEqual(cs.transactionCount) testCond r.isOk:
r.ExpectCoinbase(feeRecipient) error "cannot get latest header", msg=r.error
blockIncluded = r.Block
feeRecipientFees = big.NewInt(0) let blockIncluded = r.get
for _, tx = range blockIncluded.Transactions() (
effGasTip, err = tx.EffectiveGasTip(blockIncluded.BaseFee())
if err != nil (
fatal "unable to obtain EffectiveGasTip: %v", t.TestName, err)
)
ctx, cancel = context.WithTimeout(t.TestContext, globals.RPCTimeout)
defer cancel()
receipt, err = t.Eth.TransactionReceipt(ctx, tx.Hash())
if err != nil (
fatal "unable to obtain receipt: %v", t.TestName, err)
)
feeRecipientFees = feeRecipientFees.Add(feeRecipientFees, effGasTip.Mul(effGasTip, big.NewInt(int64(receipt.GasUsed))))
)
s = env.engine.client.TestBalanceAt(feeRecipient, nil) testCond blockIncluded.txs.len == cs.transactionCount:
error "expect transactions", get=blockIncluded.txs.len, expect=cs.transactionCount
testCond feeRecipient == blockIncluded.header.coinbase:
error "expect coinbase",
get=blockIncluded.header.coinbase,
expect=feeRecipient
var feeRecipientFees = 0.u256
for tx in blockIncluded.txs:
let effGasTip = tx.effectiveGasTip(blockIncluded.header.fee)
let r = env.engine.client.txReceipt(tx.rlpHash)
testCond r.isOk:
fatal "unable to obtain receipt", msg=r.error
let receipt = r.get
feeRecipientFees = feeRecipientFees + effGasTip.u256 * receipt.gasUsed.u256
var s = env.engine.client.balanceAt(feeRecipient)
s.expectBalanceEqual(feeRecipientFees) s.expectBalanceEqual(feeRecipientFees)
# Produce another block without txns and get the balance again # Produce another block without txns and get the balance again
env.clMock.nextFeeRecipient = feeRecipient env.clMock.nextFeeRecipient = feeRecipient
env.clMock.produceSingleBlock(BlockProcessCallbacks()) testCond env.clMock.produceSingleBlock(BlockProcessCallbacks())
s = env.engine.client.TestBalanceAt(feeRecipient, nil) s = env.engine.client.balanceAt(feeRecipient)
s.expectBalanceEqual(feeRecipientFees) s.expectBalanceEqual(feeRecipientFees)
) return true

View File

@ -437,16 +437,18 @@ proc latestHeader*(client: RpcClient): Result[common.BlockHeader, string] =
return err("failed to get latest blockHeader") return err("failed to get latest blockHeader")
return ok(res.get.toBlockHeader) return ok(res.get.toBlockHeader)
proc latestBlock*(client: RpcClient, output: var common.EthBlock): Result[void, string] = proc latestBlock*(client: RpcClient): Result[common.EthBlock, string] =
wrapTry: wrapTry:
let res = waitFor client.eth_getBlockByNumber("latest", true) let res = waitFor client.eth_getBlockByNumber("latest", true)
if res.isNone: if res.isNone:
return err("failed to get latest blockHeader") return err("failed to get latest blockHeader")
let blk = res.get() let blk = res.get()
output.header = toBlockHeader(blk) let output = EthBlock(
output.txs = toTransactions(blk.transactions) header: toBlockHeader(blk),
output.withdrawals = toWithdrawals(blk.withdrawals) txs: toTransactions(blk.transactions),
return ok() withdrawals: toWithdrawals(blk.withdrawals),
)
return ok(output)
proc namedHeader*(client: RpcClient, name: string): Result[common.BlockHeader, string] = proc namedHeader*(client: RpcClient, name: string): Result[common.BlockHeader, string] =
wrapTry: wrapTry:

View File

@ -18,11 +18,20 @@ import
../../nimbus/common/chain_config ../../nimbus/common/chain_config
import import
./engine/misc, ./engine/suggested_fee_recipient,
./engine/payload_attributes, ./engine/payload_attributes,
#./engine/payload_execution,
./engine/invalid_ancestor, ./engine/invalid_ancestor,
./engine/invalid_payload, ./engine/invalid_payload,
./engine/bad_hash ./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) = proc getGenesis(cs: EngineSpec, param: NetworkParams) =
# Set the terminal total difficulty # Set the terminal total difficulty
@ -160,7 +169,48 @@ proc makeEngineTest*(): seq[EngineSpec] =
invalidField: InvalidStateRoot, 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,
)
# Register RPC tests
#[let blockStatusRPCCheckType = [
LatestOnNewPayload,
LatestOnHeadBlockHash,
SafeOnSafeBlockHash,
FinalizedOnFinalizedBlockHash,
]
for field in blockStatusRPCCheckType:
result.add BlockStatus(checkType: field)
const const
invalidReorgList = [ invalidReorgList = [
InvalidStateRoot, InvalidStateRoot,
@ -215,32 +265,6 @@ proc makeEngineTest*(): seq[EngineSpec] =
) )
]# ]#
#[ #[
# Register RPC tests
for _, field := range []BlockStatusRPCCheckType(
LatestOnNewPayload,
LatestOnHeadBlockHash,
SafeOnSafeBlockHash,
FinalizedOnFinalizedBlockHash,
) (
result.add BlockStatus(CheckType: field))
)
# Register ForkchoiceUpdate tests
for _, field := range []ForkchoiceStateField(
HeadBlockHash,
SafeBlockHash,
FinalizedBlockHash,
) (
result.add
InconsistentForkchoiceTest(
Field: field,
),
ForkchoiceUpdatedUnknownBlockHashTest(
Field: field,
),
)
)
# Payload ID Tests # Payload ID Tests
for _, payloadAttributeFieldChange := range []PayloadAttributesFieldChange( for _, payloadAttributeFieldChange := range []PayloadAttributesFieldChange(
PayloadAttributesIncreaseTimestamp, PayloadAttributesIncreaseTimestamp,
@ -399,36 +423,6 @@ proc makeEngineTest*(): seq[EngineSpec] =
), ),
) )
# Suggested Fee Recipient Tests
result.add
SuggestedFeeRecipientTest(
BaseSpec: test.BaseSpec(
txType: some( TxLegacy,
),
TransactionCount: 20,
),
SuggestedFeeRecipientTest(
BaseSpec: test.BaseSpec(
txType: some( TxEip1559,
),
TransactionCount: 20,
),
)
# PrevRandao opcode tests
result.add
PrevRandaoTransactionTest(
BaseSpec: test.BaseSpec(
txType: some( TxLegacy,
),
),
PrevRandaoTransactionTest(
BaseSpec: test.BaseSpec(
txType: some( TxEip1559,
),
),
)
# Fork ID Tests # Fork ID Tests
for genesisTimestamp := uint64(0); genesisTimestamp <= 1; genesisTimestamp++ ( for genesisTimestamp := uint64(0); genesisTimestamp <= 1; genesisTimestamp++ (
for forkTime := uint64(0); forkTime <= 2; forkTime++ ( for forkTime := uint64(0); forkTime <= 2; forkTime++ (

View File

@ -127,6 +127,9 @@ proc makeTxs*(env: TestEnv, tc: BaseTx, num: int): seq[Transaction] =
for _ in 0..<num: for _ in 0..<num:
result.add env.sender.makeNextTx(tc) result.add env.sender.makeNextTx(tc)
proc makeNextTx*(env: TestEnv, tc: BaseTx): Transaction =
env.sender.makeNextTx(tc)
proc sendNextTx*(env: TestEnv, eng: EngineEnv, tc: BaseTx): bool = proc sendNextTx*(env: TestEnv, eng: EngineEnv, tc: BaseTx): bool =
env.sender.sendNextTx(eng.client, tc) env.sender.sendNextTx(eng.client, tc)

View File

@ -228,6 +228,18 @@ template expectHash*(res: untyped, hash: common.Hash256) =
testCond s.blockHash == hash: testCond s.blockHash == hash:
error "Unexpected expectHash", expect=hash.short, get=s.blockHash.short error "Unexpected expectHash", expect=hash.short, get=s.blockHash.short
template expectStorageEqual*(res: untyped, expectedValue: UInt256) =
testCond res.isOk:
error "expectStorageEqual", msg=res.error
testCond res.get == expectedValue:
error "invalid storage", get=res.get, expect=expectedValue
template expectBalanceEqual*(res: untyped, expectedBalance: UInt256) =
testCond res.isOk:
error "expectBalanceEqual", msg=res.error
testCond res.get == expectedBalance:
error "invalid balance", expect=expectedBalance, get=res.get
func timestamp*(x: ExecutableData): auto = func timestamp*(x: ExecutableData): auto =
x.basePayload.timestamp x.basePayload.timestamp

View File

@ -26,9 +26,9 @@ proc execute*(ws: BlockValueSpec, env: TestEnv): bool =
testCond WDBaseSpec(ws).execute(env) testCond WDBaseSpec(ws).execute(env)
# Get the latest block and the transactions included # Get the latest block and the transactions included
var blk: EthBlock let b = env.client.latestBlock()
let b = env.client.latestBlock(blk)
b.expectNoError() b.expectNoError()
let blk = b.get
var totalValue: UInt256 var totalValue: UInt256
testCond blk.txs.len > 0: testCond blk.txs.len > 0: