Skip to content

fix(node): propagate ContractNotPresent to RPC layer#1475

Merged
rsporny merged 1 commit into
mainfrom
rpc-get-contract-not-present
May 8, 2026
Merged

fix(node): propagate ContractNotPresent to RPC layer#1475
rsporny merged 1 commit into
mainfrom
rpc-get-contract-not-present

Conversation

@rsporny

@rsporny rsporny commented May 7, 2026

Copy link
Copy Markdown
Contributor

Overview

PR #916 added LedgerApiError::ContractNotPresent in the runtime, but the
midnight_contractState RPC still collapsed every LedgerApiError into
the
generic UnableToGetContractState — so callers could not distinguish
"empty state" from "no such contract", which is why audit Issue AD was
re-opened as #1166.

Add StateRpcError::ContractNotPresent and route the matching ledger
error to it. All other variants still fall through to
UnableToGetContractState, so this is a strict superset of prior
behaviour. Re-export LedgerApiError from pallet-midnight so the RPC
crate can match on it without a new dependency.

Lock in the audit invariants with three e2e tests in tests/e2e:

  • ContractNotPresent for an undeployed (but well-formed) address
  • BadContractAddress and ContractNotPresent stay distinct paths
  • same address yields ContractNotPresent at block 1 and the deployed state at the post-deploy best block

Bump NUM_PRE_DEPLOY_TESTS / NUM_DEPLOY_TESTS 2→3 and the Earthfile
test-threads 4→6 so the new deploy gate doesn't deadlock.

Closes: #1166

🗹 TODO before merging

  • Ready

📌 Submission Checklist

  • All commits are signed off (git commit -s) for the DCO
  • Changes are backward-compatible (or flagged if breaking)
  • Pull request description explains why the change is needed
  • Self-reviewed the diff
  • I have included a change file, or skipped for this reason:
  • If the changes introduce a new feature, I have bumped the node minor version
  • Update documentation (if relevant)
  • Updated AGENTS.md if build commands, architecture, or workflows changed
  • No new todos introduced

🧪 Testing Evidence

Please describe any additional testing aside from CI:

  • Additional tests are provided (if possible)

🔱 Fork Strategy

  • Node Runtime Update
  • Node Client Update
  • Other:
  • N/A

Links

@rsporny rsporny requested a review from a team as a code owner May 7, 2026 12:07
@rsporny rsporny force-pushed the rpc-get-contract-not-present branch 3 times, most recently from d18d85c to 7ecf43c Compare May 7, 2026 12:29
@rsporny rsporny force-pushed the rpc-get-contract-not-present branch 2 times, most recently from e0b6905 to 3af23ba Compare May 8, 2026 10:26

@m2ux m2ux left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approving review (work-package review-mode pass).

The PR completes the audit fix correctly: it surfaces LedgerApiError::ContractNotPresent through the midnight_contractState RPC as a dedicated StateRpcError::ContractNotPresent variant. Layering is preserved (translation lives at the boundary; LedgerApiError is re-exported rather than introducing a new dep), the change is additive (strict superset of prior outcomes), and the e2e suite enforces all three audit invariants end-to-end (well-formed-but-undeployed, malformed-vs-not-present distinctness, historical-vs-current-block).

The two scope-adjacent changes (DEPLOY_SERIAL mutex and E2E_SKIP_DEPLOY_GATE env-var) are defensible — the new third deploy test surfaces the deploy-phase race they prevent, and bundling them avoids a forced ordering between this PR and a separate concurrency PR.

No blocking concerns. Three optional comments inline (C1–C3) for quality-of-life only; they should not delay merge.

Comment thread pallets/midnight/rpc/src/lib.rs
Comment thread tests/e2e/tests/lib.rs Outdated
Comment thread tests/e2e/README.md
PR #916 added LedgerApiError::ContractNotPresent in the runtime, but the
  midnight_contractState RPC still collapsed every LedgerApiError into
the
  generic UnableToGetContractState — so callers could not distinguish
  "empty state" from "no such contract", which is why audit Issue AD was
  re-opened as #1166.

  Add StateRpcError::ContractNotPresent and route the matching ledger
  error to it. All other variants still fall through to
  UnableToGetContractState, so this is a strict superset of prior
  behaviour. Re-export LedgerApiError from pallet-midnight so the RPC
  crate can match on it without a new dependency.

  Lock in the audit invariants with three e2e tests in tests/e2e:
  - ContractNotPresent for an undeployed (but well-formed) address
  - BadContractAddress and ContractNotPresent stay distinct paths
  - same address yields ContractNotPresent at block 1 and the deployed
    state at the post-deploy best block

  The third test generates DEPLOY_TX dynamically via the toolkit's
  in-process prover against the live chain rather than using the static
  .mn fixture: that fixture's intent_ttl is baked in at generation time
  and expires once chain time advances past
  genesis.tblock + global_ttl (14 days by default), so static fixtures
  can't carry the test on long-lived or live envs.

  Serialize deploy tests behind a Mutex so concurrent submissions of the
  same DEPLOY_TX don't race in the txpool, and add an
  E2E_SKIP_DEPLOY_GATE escape hatch so the test can be run standalone
  via `cargo test <name>` without the gate semaphore deadlocking on
  unrun pre-deploy tests.

  Bump NUM_PRE_DEPLOY_TESTS / NUM_DEPLOY_TESTS 2→3 and the Earthfile
  test-threads 4→6 so the new deploy gate doesn't deadlock.

Closes: #1166

Signed-off-by: Radosław Sporny <404@rspo.dev>
@rsporny rsporny force-pushed the rpc-get-contract-not-present branch from 3af23ba to 878bb73 Compare May 8, 2026 14:46
@rsporny rsporny added this pull request to the merge queue May 8, 2026
Merged via the queue into main with commit 81d4966 May 8, 2026
35 checks passed
@rsporny rsporny deleted the rpc-get-contract-not-present branch May 8, 2026 16:27
scottbuckel pushed a commit that referenced this pull request May 8, 2026
PR #916 added LedgerApiError::ContractNotPresent in the runtime, but the
  midnight_contractState RPC still collapsed every LedgerApiError into
the
  generic UnableToGetContractState — so callers could not distinguish
  "empty state" from "no such contract", which is why audit Issue AD was
  re-opened as #1166.

  Add StateRpcError::ContractNotPresent and route the matching ledger
  error to it. All other variants still fall through to
  UnableToGetContractState, so this is a strict superset of prior
  behaviour. Re-export LedgerApiError from pallet-midnight so the RPC
  crate can match on it without a new dependency.

  Lock in the audit invariants with three e2e tests in tests/e2e:
  - ContractNotPresent for an undeployed (but well-formed) address
  - BadContractAddress and ContractNotPresent stay distinct paths
  - same address yields ContractNotPresent at block 1 and the deployed
    state at the post-deploy best block

  The third test generates DEPLOY_TX dynamically via the toolkit's
  in-process prover against the live chain rather than using the static
  .mn fixture: that fixture's intent_ttl is baked in at generation time
  and expires once chain time advances past
  genesis.tblock + global_ttl (14 days by default), so static fixtures
  can't carry the test on long-lived or live envs.

  Serialize deploy tests behind a Mutex so concurrent submissions of the
  same DEPLOY_TX don't race in the txpool, and add an
  E2E_SKIP_DEPLOY_GATE escape hatch so the test can be run standalone
  via `cargo test <name>` without the gate semaphore deadlocking on
  unrun pre-deploy tests.

  Bump NUM_PRE_DEPLOY_TESTS / NUM_DEPLOY_TESTS 2→3 and the Earthfile
  test-threads 4→6 so the new deploy gate doesn't deadlock.

Closes: #1166

Signed-off-by: Radosław Sporny <404@rspo.dev>
Signed-off-by: Scott Buckel <scott.buckel@shielded.io>
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.

[Node] Issue AD: Return ContractNotPresent Instead of Default State

3 participants