Skip to content

[Fix] Wait for chain head and eth "latest" in createAndFinalizeBlock()#1855

Merged
librelois merged 7 commits intomasterfrom
artur/utils-ts
Mar 4, 2026
Merged

[Fix] Wait for chain head and eth "latest" in createAndFinalizeBlock()#1855
librelois merged 7 commits intomasterfrom
artur/utils-ts

Conversation

@arturgontijo
Copy link
Copy Markdown
Collaborator

Summary

Fixes flaky ts-tests (e.g. test-block, receipt/latest-block consistency) caused by createAndFinalizeBlock() returning before the new block was visible on the eth RPC.

Background

The node exposes "latest" and eth_blockNumber as the latest indexed canonical block (mapping-sync), which can lag behind the chain head. If we only wait for the block by number and don’t ensure the chain head has advanced and that "latest" has caught up, tests can observe the previous block (e.g. getBlockNumber() === 1 instead of 2) and fail or become flaky.

Changes

  • createAndFinalizeBlock()

    • Record prevNumber from chain_getHeader before engine_createBlock.
    • After create, poll chain_getHeader until the head number is > prevNumber (10s timeout, 50ms interval) instead of a single read, so we don’t rely on a possibly stale head.
    • Call waitForBlock(web3, head.number, 10_000) so the block is indexed and visible via eth_getBlockByNumber.
    • Poll eth_blockNumber until it equals the new block number (10s timeout), then return, so callers that use "latest" or getBlockNumber() see the block we just created.
  • Timeouts for head advance, waitForBlock, and eth_blockNumber sync are set to 10s to stay stable when the full test suite runs under load.

Testing

  • npm run test-sql should pass consistently when run in isolation and as part of the full suite.

@arturgontijo arturgontijo self-assigned this Mar 3, 2026
@arturgontijo arturgontijo requested a review from sorpaas as a code owner March 3, 2026 13:40
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 3, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The utility now derives the block hash from response.result.hash, polls chain_getHeader(blockHash) until a header with number appears (10s timeout), parses that hex number as expectedNumber, then polls eth_blockNumber until the RPC head >= expectedNumber (10s timeout), including last-error context.

Changes

Cohort / File(s) Summary
Polling & synchronization
ts-tests/tests/util.ts
Use response.result.hash null-safely; add a header-poll loop for chain_getHeader(blockHash) until header.number (per-attempt headerLastError, 10s headerTimeout, 50ms retry); parse hex header number to expectedNumber; poll eth_blockNumber until >= expectedNumber (per-attempt rpcLastError, 10s rpcSyncTimeout); enrich error messages and adjust wait logic.

Sequence Diagram(s)

sequenceDiagram
    participant TestUtil as Test Util
    participant Chain as Chain Service
    participant EthRPC as Ethereum RPC

    TestUtil->>Chain: submit tx / create block -> receive `response.result.hash`
    TestUtil->>Chain: poll `chain_getHeader(blockHash)` until `header.number` present (<=10s)
    alt header.number available
        Chain-->>TestUtil: header (hex `number`)
        TestUtil->>TestUtil: parse header.number -> expectedNumber
        TestUtil->>EthRPC: poll `eth_blockNumber` until >= expectedNumber (<=10s)
        EthRPC-->>TestUtil: eth_blockNumber >= expectedNumber
    else timeout / errors
        Chain-->>TestUtil: headerLastError
        EthRPC-->>TestUtil: rpcLastError
        TestUtil-->>TestUtil: throw error including last observed errors
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: fixing createAndFinalizeBlock() to wait for chain head and eth 'latest', which directly addresses the core problem of test flakiness.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, providing background context, detailed change explanations, and expected outcomes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch artur/utils-ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@ts-tests/tests/util.ts`:
- Around line 102-107: The loop that polls eth_blockNumber uses strict equality
which can miss cases where the node advanced past the expected block; in the
polling logic that calls customRequest and computes n from current.result (using
rpcStart, rpcSyncTimeout, expectedNumber), change the comparison from n ===
expectedNumber to n >= expectedNumber so the function returns when the chain has
reached or passed the expected block height; keep the rest of the loop and
timeout behavior unchanged.
- Around line 91-93: The timeout path currently only checks that head.number is
present, not that it advanced beyond prevNumber; update the conditional in the
timeout/check block that uses head and prevNumber (the one after createBlock) to
assert that head.number > prevNumber (or compare as BigInt/Number if head.number
is string) and throw the existing error if it has not advanced, ensuring the
test waits for an increased head rather than merely a truthy value.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 30d3b06 and 417c413.

📒 Files selected for processing (1)
  • ts-tests/tests/util.ts

Comment thread ts-tests/tests/util.ts Outdated
Comment thread ts-tests/tests/util.ts
Copy link
Copy Markdown
Member

@librelois librelois left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New polling loops fail fast on transient transport errors instead of retrying, which can make createAndFinalizeBlock flakier under brief RPC hiccups.
Evidence: both new loops call customRequest(...) without try/catch at ts-tests/tests/util.ts (line 84) and ts-tests/tests/util.ts (line 104); customRequest rejects on provider send errors at ts-tests/tests/util.ts (line 19).
Impact: a single transient provider error now aborts the helper, whereas surrounding retry logic suggests this path is expected to be eventually consistent.

@arturgontijo
Copy link
Copy Markdown
Collaborator Author

@librelois try-catch added at 81ddbe1

Copy link
Copy Markdown
Member

@librelois librelois left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createAndFinalizeBlock can still return before the newly created block is indexed if the pre-call head snapshot is stale.
Evidence: ts-tests/tests/util.ts (line 71) ts-tests/tests/util.ts (line 89), ts-tests/tests/util.ts (line 104).
Why: the logic only checks chain_getHeader > prevNumber. If prevNumber was already behind real tip, the loop can break on an older block (not the one from this engine_createBlock), and subsequent waits validate the wrong height.

Copy link
Copy Markdown
Member

@librelois librelois left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createAndFinalizeBlock no longer explicitly waits for eth_getBlockByNumber visibility before returning, despite its comment saying it does.
Evidence: ts-tests/tests/util.ts lines 68-71 and 104-125.
Risk: returning on eth_blockNumber >= expectedNumber can still race with mapping-sync availability, causing intermittent failures in tests that immediately read block details.

Suggestion:

Re-introduce an explicit mapping-sync wait before return (for example waitForBlock(web3, "0x" + expectedNumber.toString(16), 10_000)), or prove/document that eth_blockNumber is strictly gated on mapping-sync in this stack.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@ts-tests/tests/util.ts`:
- Around line 74-77: The code currently assumes response is non-null and
accesses response.result, which will throw if customRequest returned undefined;
update the guard to check response itself and the nested hash (e.g. if
(!response || !response.result?.hash) { throw new Error(`Unexpected response:
${JSON.stringify(response)}`); }) and then assign blockHash from
response.result.hash (const blockHash = response.result.hash as string) so the
intended error path is used instead of an uncaught property access error.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 407ee29 and 6cc46d2.

📒 Files selected for processing (1)
  • ts-tests/tests/util.ts

Comment thread ts-tests/tests/util.ts Outdated
librelois and others added 2 commits March 3, 2026 19:35
Refactor ts-tests/tests/test-contract-storage.ts to remove timing-sensitive transaction flow in the SSTORE gas-cost test.

Replace callback-based contract.methods().send() sequencing with signed raw tx submission.
Seal a block explicitly after each tx and wait for receipt availability with bounded polling.
Add a per-test timeout (30s) and remove unused helper/wallet setup.
Keep existing gas-cost assertions unchanged; only test execution reliability is improved.
@librelois librelois merged commit 3d9a55e into master Mar 4, 2026
7 checks passed
@librelois librelois deleted the artur/utils-ts branch March 4, 2026 11:39
arturgontijo added a commit to moonbeam-foundation/frontier that referenced this pull request Mar 4, 2026
…()` (polkadot-evm#1855)

* createAndFinalizeBlock() must wait for the target block

* Coderabbit review

* Wrap customRequest() in try-catch

* Get blockNumber from engine_createBlock result.hash

* Get waitForBlock() back

* test(ts-tests): make contract storage SSTORE test deterministic

Refactor ts-tests/tests/test-contract-storage.ts to remove timing-sensitive transaction flow in the SSTORE gas-cost test.

Replace callback-based contract.methods().send() sequencing with signed raw tx submission.
Seal a block explicitly after each tx and wait for receipt availability with bounded polling.
Add a per-test timeout (30s) and remove unused helper/wallet setup.
Keep existing gas-cost assertions unchanged; only test execution reliability is improved.

* Make test-selfdestruct deterministic

---------

Co-authored-by: Eloïs <c@elo.tf>
manuelmauro added a commit to moonbeam-foundation/frontier that referenced this pull request Mar 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants