Skip to content

Starknet v0.14.2 and RPC v0.10.1#909

Merged
3alpha merged 25 commits intomainfrom
starknet-v0.14.2
Mar 31, 2026
Merged

Starknet v0.14.2 and RPC v0.10.1#909
3alpha merged 25 commits intomainfrom
starknet-v0.14.2

Conversation

@3alpha
Copy link
Copy Markdown
Collaborator

@3alpha 3alpha commented Feb 16, 2026

Usage related changes

Development related changes

Checklist:

  • Checked out the contribution guidelines
  • Applied formatting - ./scripts/format.sh
  • No linter errors - ./scripts/clippy_check.sh
  • No unused dependencies - ./scripts/check_unused_deps.sh
  • No spelling errors - ./scripts/check_spelling.sh
  • Performed code self-review
  • Rebased to the latest commit of the target branch (or merged it into my branch)
    • Once you make the PR reviewable, please avoid force-pushing
  • Updated the docs if needed - ./website/README.md
  • Linked the issues resolvable by this PR - linking info
  • Updated the tests if needed; all passing - execution info

Summary by CodeRabbit

Release Notes

  • New Features
    • Added configurable proof modes (devnet, none, full) via --proof-mode CLI flag for transaction proof generation and verification.
    • Introduced starknet_proveTransaction RPC endpoint for generating transaction proofs.
    • Added response flags to control transaction output, including proof facts and storage metadata.
    • Implemented multi-address filtering for event queries.
    • Added storage last-update-block tracking for state queries.
    • Enhanced simulations and traces with initial reads data.
    • Updated to Starknet version 0.14.2.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 16, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 151575b1-8b2e-47d8-9aee-ec0f4e92d85e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Note

.coderabbit.yaml has unrecognized properties

CodeRabbit is using all valid settings from your configuration. Unrecognized properties (listed below) have been ignored and may indicate typos or deprecated fields that can be removed.

⚠️ Parsing warnings (1)
Validation error: Unrecognized key(s) in object: 'auto_resolve_threads'
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

This PR introduces transaction proof generation and verification capabilities for Starknet Devnet, with deterministic mock proofs via Pedersen hashing. It extends the RPC specification to version 0.10.1, adds a proof-mode configuration option (devnet/none/full), and updates Starknet version to 0.14.2. Major changes include: multi-address event filtering replacing single-address filters, storage metadata tracking with last-update-block recording, response flags for controlling proof_facts and initial-reads inclusion in API responses, new prove_transaction endpoint, subscription enhancements with tags and multi-address support, and dependency version bumps across blockifier, cairo-vm, and cairo-lang crates. The changes span core logic, API endpoints, type definitions, and comprehensive test coverage.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • 0xSpaceShard/starknet-devnet#847: Modifies overlapping server API surface including json_rpc_handler, endpoints, models, and subscribe modules with similar public type/handler changes.
  • 0xSpaceShard/starknet-devnet#830: Modifies event-fetching and subscription code paths including get_events/get_unlimited_events and websocket subscription/notification handling.
  • 0xSpaceShard/starknet-devnet#838: Touches subscription/notification logic in subscribe.rs and websocket endpoints that this PR also modifies.

Suggested labels

enhancement

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description follows the required template structure with sections for 'Usage related changes' and 'Development related changes', and includes a complete checklist with all items marked as completed. However, both content sections are empty/minimal, lacking substantive detail about what changes were made. Fill in the 'Usage related changes' and 'Development related changes' sections with specific details about features, API modifications, version upgrades, and testing improvements to help reviewers understand the impact of this PR.
Docstring Coverage ⚠️ Warning Docstring coverage is 64.53% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly references the main changes: Starknet v0.14.2 upgrade and RPC v0.10.1 specification update, which align with the extensive version bumps and RPC spec changes throughout the changeset.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch starknet-v0.14.2
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Tip

CodeRabbit can generate a title for your PR based on the changes with custom instructions.

Set the reviews.auto_title_instructions setting to generate a title for your PR based on the changes in the PR with custom instructions.

@3alpha 3alpha marked this pull request as ready for review March 13, 2026 13:48
@3alpha
Copy link
Copy Markdown
Collaborator Author

3alpha commented Mar 13, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 13, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
crates/starknet-devnet-server/src/api/spec_reader/mod.rs (1)

386-393: ⚠️ Potential issue | 🟠 Major

Keep StorageResult valid only for StorageAt checks.

Line 390–393 currently allows StarknetResponse::StorageResult(_) for ContractNonce, ChainId, and ClassHashAtContractAddress too. That weakens this test and can hide incorrect response wiring for non-storage RPCs.

🐛 Proposed fix
-                StarknetSpecRequest::ContractNonce(_)
-                | StarknetSpecRequest::ChainId
-                | StarknetSpecRequest::ClassHashAtContractAddress(_)
-                | StarknetSpecRequest::StorageAt(_) => {
-                    assert!(matches!(
-                        sn_response,
-                        StarknetResponse::Felt(_) | StarknetResponse::StorageResult(_)
-                    ));
-                }
+                StarknetSpecRequest::ContractNonce(_)
+                | StarknetSpecRequest::ChainId
+                | StarknetSpecRequest::ClassHashAtContractAddress(_) => {
+                    assert!(matches!(sn_response, StarknetResponse::Felt(_)));
+                }
+                StarknetSpecRequest::StorageAt(_) => {
+                    assert!(matches!(
+                        sn_response,
+                        StarknetResponse::Felt(_) | StarknetResponse::StorageResult(_)
+                    ));
+                }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/starknet-devnet-server/src/api/spec_reader/mod.rs` around lines 386 -
393, The test currently allows StarknetResponse::StorageResult(_) for multiple
request variants; restrict StorageResult to only StarknetSpecRequest::StorageAt
and ensure the other variants (ContractNonce, ChainId,
ClassHashAtContractAddress) assert only StarknetResponse::Felt(_). Update the
match arm handling StarknetSpecRequest::ContractNonce,
StarknetSpecRequest::ChainId, StarknetSpecRequest::ClassHashAtContractAddress,
and StarknetSpecRequest::StorageAt so that StorageResult is checked exclusively
in the StorageAt branch and the non-storage branches assert
matches!(sn_response, StarknetResponse::Felt(_)).
crates/starknet-devnet-core/src/starknet/add_invoke_transaction.rs (1)

32-45: ⚠️ Potential issue | 🟠 Major

ProofMode::None still preserves caller-supplied proof facts in the stored transaction.

This branch only clears sn_api_transaction.tx.proof_facts. Line 41 then rebuilds the persisted invoke_transaction from the original broadcasted_invoke_transaction, so get_transaction_* can still surface user-provided proof_facts when IncludeProofFacts is requested. Please sanitize the stored RPC transaction too, or derive it from the already-cleared payload.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/starknet-devnet-core/src/starknet/add_invoke_transaction.rs` around
lines 32 - 45, The stored RPC transaction still retains caller-supplied proof
facts because you clear tx_v3.proof_facts on sn_api_transaction but then rebuild
invoke_transaction from broadcasted_invoke_transaction; instead rebuild or
sanitize the persisted invoke_transaction from the already-cleared
sn_api_transaction.tx (i.e. use the modified tx_v3 to construct the
Transaction::Invoke/InvokeTransactionV3 instance) or explicitly clear
proof_facts on the value used to create invoke_transaction (reference
sn_api_transaction, tx_v3.proof_facts, broadcasted_invoke_transaction,
invoke_transaction, Transaction::Invoke, InvokeTransactionV3::new) so
persisted/get_transaction_* responses cannot expose user-supplied proof_facts.
🧹 Nitpick comments (10)
tests/integration/subscription_to_blocks.rs (1)

397-397: Add a tracked removal target for this temporary ignore.

Line 397 removes coverage for an important fork-range validation path; a bare TODO is easy to miss. Please include a linked issue/milestone in the comment so re-enabling is enforceable.

I can draft a short issue template with clear unignore criteria if helpful.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/integration/subscription_to_blocks.rs` at line 397, The #[ignore]
attribute on the test in tests/integration/subscription_to_blocks.rs is a
temporary suppression and needs a tracked removal target; replace the bare TODO
with a one-line comment directly above the #[ignore] that references a specific
issue/milestone (e.g. "TODO: Remove after mainnet release — tracked at
ISSUE-123: <link>") so the ignore is discoverable and enforceable, and include
an unignore criteria or milestone date in that comment.
crates/starknet-devnet-core/src/starknet/state_update.rs (1)

124-145: Add an assertion for declared_classes in the unrelated-address branch.

This branch verifies address-scoped fields are empty, but it doesn’t check that address-agnostic declared_classes are preserved. Adding that assertion will prevent silent regressions in filtering semantics.

Proposed test assertion
         let other_diff = filtered_by_other.get_state_diff();
+        assert_eq!(
+            other_diff.declared_classes.len(),
+            full_diff.declared_classes.len(),
+            "Declared classes should remain unchanged by address filtering"
+        );
         assert!(other_diff.nonces.is_empty(), "Unrelated address should have no nonces");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/starknet-devnet-core/src/starknet/state_update.rs` around lines 124 -
145, The unrelated-address test branch misses asserting that address-agnostic
declared_classes are preserved; update the assertion block around
filtered_by_other/get_state_diff() (other_diff) to also assert that
other_diff.declared_classes equals the original declared classes (or is not
empty as appropriate) so declared_classes are not filtered out by
block_state_update/BlockId::Tag(BlockTag::Latest) when passing
unrelated_address; reference other_diff.declared_classes and ensure the test
verifies declared_classes remain present and unchanged.
crates/starknet-devnet-types/src/rpc/state.rs (1)

92-97: Use a set for membership checks in filter_by_address.

Line 94–97 currently performs repeated linear contains scans on Vec<ContractAddress>, which scales poorly as inputs grow. Converting addresses once to a HashSet keeps filtering predictable and faster.

♻️ Proposed refactor
 pub fn filter_by_address(&self, contract_addresses: Vec<ContractAddress>) -> Self {
+    let address_set: std::collections::HashSet<_> = contract_addresses.into_iter().collect();
     let mut result = self.clone();
-    result.deployed_contracts.retain(|c| contract_addresses.contains(&c.address));
-    result.storage_diffs.retain(|s| contract_addresses.contains(&s.address));
-    result.nonces.retain(|n| contract_addresses.contains(&n.contract_address));
-    result.replaced_classes.retain(|r| contract_addresses.contains(&r.contract_address));
+    result.deployed_contracts.retain(|c| address_set.contains(&c.address));
+    result.storage_diffs.retain(|s| address_set.contains(&s.address));
+    result.nonces.retain(|n| address_set.contains(&n.contract_address));
+    result.replaced_classes.retain(|r| address_set.contains(&r.contract_address));
     result
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/starknet-devnet-types/src/rpc/state.rs` around lines 92 - 97,
filter_by_address currently does repeated Vec::contains scans on
contract_addresses which is O(n*m); convert contract_addresses into a HashSet at
the start of the function and use set membership checks for filtering. In
practice: inside filter_by_address, create a HashSet<ContractAddress> from the
incoming contract_addresses and then call retain on deployed_contracts,
storage_diffs, nonces, and replaced_classes using set.contains(&c.address) /
set.contains(&s.address) / set.contains(&n.contract_address) /
set.contains(&r.contract_address) respectively to make the filters O(1) per
check.
crates/starknet-devnet-server/src/api/error.rs (1)

88-91: Please pin the new proof error mappings with unit tests.

This file already locks down most public RPC errors with focused assertions, but the new proof-specific branches are still unpinned. A small test each for ProofVerificationFailed -> 69, InvalidProofFacts -> 55 + reason data, and ProvingError -> wildcard would make future spec/code drift much easier to catch.

Also applies to: 231-235, 279-308

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/starknet-devnet-server/src/api/error.rs` around lines 88 - 91, Add
unit tests that pin the new proof-related RPC error mappings: assert that
ProofVerificationFailed maps to error code 69; assert that InvalidProofFacts
maps to code 55 and that its returned payload includes the provided reason data;
and assert that ProvingError serializes to the generic/wildcard error mapping
(i.e., does not collide with the pinned numeric codes). Place these tests
alongside the existing public RPC error tests in the same test module in
error.rs, using the same helper/serializer used by other tests to produce the
RPC error representation so the assertions target the exact serialized code and
payload.
crates/starknet-devnet-types/src/rpc/proof.rs (1)

15-17: Consider returning &[u32] instead of &Vec<u32> for inner().

Returning a slice (&[u32]) is more idiomatic and flexible than returning &Vec<u32>, as it doesn't expose the underlying container type:

♻️ Suggested improvement
-    pub fn inner(&self) -> &Vec<u32> {
+    pub fn inner(&self) -> &[u32] {
         &self.0
     }

This is a minor API polish suggestion; the current implementation works correctly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/starknet-devnet-types/src/rpc/proof.rs` around lines 15 - 17, The
method inner() currently returns &Vec<u32>, which unnecessarily exposes the
concrete container; change its signature to return a slice (&[u32]) and return
&self.0[..] (or self.0.as_slice()) so callers get a borrowed slice instead of a
&Vec<u32>; update the inner() definition accordingly to return &[u32] while
keeping the same receiver and behavior.
tests/integration/common/utils.rs (2)

177-192: Consider extracting hardcoded gas parameters to constants.

The gas values (5_000_000, 1_000_000, 2_500_000_000) are test-specific but could benefit from named constants for clarity and easier adjustment if protocol parameters change.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/integration/common/utils.rs` around lines 177 - 192, Extract the
hardcoded gas numbers into named constants and use them where tx_l1_gas,
tx_l1_data_gas, and tx_l2_gas are defined before calling
account.execute_v3(...). Replace the inline literals (5_000_000, 1_000_000,
2_500_000_000) with descriptive constants like DEFAULT_TEST_TX_L1_GAS,
DEFAULT_TEST_TX_L1_DATA_GAS, and DEFAULT_TEST_TX_L2_GAS (declared at the top of
the test module or file), then leave the call that builds prepared_for_prove and
execute_v3(tx_calls.clone()) unchanged except for using those constants.

199-201: Consider documenting why exactly 11 blocks are created.

The magic number 11 for block creation lacks explanation. If this is a protocol requirement (e.g., proof validity window), a comment would improve maintainability. If arbitrary, consider extracting to a named constant.

// Example improvement:
const PROOF_VALIDITY_BLOCKS: usize = 11; // TODO: Document the reason for this value
for _ in 0..PROOF_VALIDITY_BLOCKS {
    devnet.create_block().await.unwrap();
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/integration/common/utils.rs` around lines 199 - 201, The loop calling
devnet.create_block().await.unwrap() uses a magic number 11; replace it with a
named constant (e.g., PROOF_VALIDITY_BLOCKS or BLOCKS_TO_FINALIZE) and add a
brief comment explaining why 11 is required (protocol window, finality, etc.),
then update the for _ in 0..11 to for _ in 0..PROOF_VALIDITY_BLOCKS so the
intent is clear and maintainable.
crates/starknet-devnet-types/src/rpc/transactions/invoke_transaction_v3.rs (1)

55-57: Consider the semantic difference between None and empty Vec in clone_with_proof_facts.

The method uses unwrap_or_default() which converts None to Some(vec![]). This changes the semantics: None (no proof facts provided) becomes Some([]) (explicitly empty proof facts). If this distinction matters for downstream processing, consider preserving None:

💡 Suggested alternative if distinction matters
 pub fn clone_with_proof_facts(&self) -> Self {
-    Self { proof_facts: Some(self.proof_facts.clone().unwrap_or_default()), ..self.clone() }
+    Self { proof_facts: self.proof_facts.clone(), ..self.clone() }
 }

If the intent is to always ensure Some(...) when this method is called, the current implementation is correct.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/starknet-devnet-types/src/rpc/transactions/invoke_transaction_v3.rs`
around lines 55 - 57, The current clone_with_proof_facts method converts a None
proof_facts into Some(empty Vec) via unwrap_or_default(), changing the semantic
meaning; to preserve the original Option semantics, set proof_facts to
self.proof_facts.clone() instead of
Some(self.proof_facts.clone().unwrap_or_default()), otherwise if the intent
really is to always return Some(...) leave as-is but add a comment clarifying
that None should be normalized to Some([]) for downstream logic; refer to the
clone_with_proof_facts function and the proof_facts field when making this
change.
crates/starknet-devnet-core/src/starknet/events.rs (1)

112-113: Avoid linear address scans in the hot path.

This helper now walks the whole address list for every event. With multi-address filters, get_events scales with events × addresses; normalizing the filter once per request into a set-like structure would keep lookups cheap.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/starknet-devnet-core/src/starknet/events.rs` around lines 112 - 113,
The current address check in the address_condition match walks the entire
addresses list for every event, causing O(events × addresses) behavior; change
the request-level preprocessing so that the incoming addresses (the addresses
parameter used in get_events) are normalized once into a HashSet (or similar
O(1) lookup structure) and then use set.contains(&event.from_address) in the
address_condition branch instead of address_list.contains(...); update the code
paths that build the filter to accept the precomputed set (or an enum wrapper
for None vs HashSet) so the per-event logic in get_events stays a constant-time
lookup.
crates/starknet-devnet-types/src/rpc/transactions.rs (1)

1133-1168: Consider ordering for determinism.

The conversion iterates over StateMaps fields. If StateMaps uses HashMap internally, the iteration order is non-deterministic. This is fine for informational API responses, but if these initial reads are ever used for proof verification or hashing where deterministic ordering matters, consider sorting the entries or using IndexMap.

Example: Sort storage entries for deterministic output
         storage: value
             .storage
             .iter()
             .map(|s| InitialReadsStorageEntry {
                 contract_address: s.0.0.into(),
                 key: (*s.0.1).into(),
                 value: *s.1,
             })
-            .collect(),
+            .collect::<Vec<_>>()
+            .tap_mut(|v| v.sort_by(|a, b| {
+                (&a.contract_address, &a.key).cmp(&(&b.contract_address, &b.key))
+            })),

Apply similar sorting to other fields if determinism is required.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/starknet-devnet-types/src/rpc/transactions.rs` around lines 1133 -
1168, The conversion impl From<StateMaps> for InitialReads iterates
HashMap-backed StateMaps fields (storage, nonces, class_hashes,
declared_contracts) which yields non-deterministic order; to make InitialReads
deterministic, sort each mapped collection before collecting (e.g., sort storage
entries by (contract_address, key), nonces by contract_address, class_hashes by
contract_address, declared_contracts by class_hash) or use an ordered map
(IndexMap) in StateMaps; update the mapping in InitialReads::from to produce
sorted Vecs for storage, nonces, class_hashes, and declared_contracts so the
output order is stable for hashing/proofs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/starknet-devnet-core/src/starknet/mod.rs`:
- Around line 333-369: The code currently swallows errors from
blockifier::blockifier::block::pre_process_block and self.commit_diff by only
logging and continuing; instead propagate those errors to abort block
generation: change the handling in the block preprocessing section so that if
pre_process_block returns Err you return Err (or map to the function's error
type) immediately rather than just tracing::error, and do the same for
self.commit_diff, ensuring you do not advance block generation or mutate
pre_confirmed_state/pre_confirmed_state_diff when these calls fail; reference
the pre_process_block call and the commit_diff method to locate the changes and
propagate the errors up the call stack.
- Around line 491-500: The code currently calls task::block_in_place when
Handle::try_current() succeeds, which panics on Tokio's single-threaded test
runtime; instead, change the branching so you do NOT call task::block_in_place
at all — if Handle::try_current() returns Ok(_) then create a new multi-thread
Runtime via tokio::runtime::Runtime::new() and use
rt.block_on(commitments_future) (or prefer Runtime::new().unwrap().block_on) to
execute commitments_future; keep the existing fallback that creates a Runtime
but remove usage of task::block_in_place and its branch, referencing
Handle::try_current, task::block_in_place, commitments_future, and
tokio::runtime::Runtime::new in your changes.

In `@crates/starknet-devnet-core/src/starknet/proofs.rs`:
- Around line 115-145: verify_proof currently only checks that the
caller-supplied proof_facts match the proof (using proof_to_felt and
Pedersen::hash_array) so an attacker can forge arbitrary proof_facts; instead
derive the expected proof_facts from authoritative state (recompute the
transaction hash and block context) inside verify_proof or accept the canonical
transaction/block identifiers and recompute the inputs yourself (do not use the
caller-supplied proof_facts directly), then convert the proof via proof_to_felt
and compare Pedersen::hash_array(&expected_inputs) to the stored commitment
(last_field) and return false on any mismatch; update the function signature or
lookup logic to obtain the authoritative transaction and block data, reference
verify_proof, proof_facts, proof, proof_to_felt and Pedersen::hash_array when
making the change.

In `@crates/starknet-devnet/src/main.rs`:
- Around line 268-277: The for-loop that preloads recent fork blocks skips block
fork_block_number - 1 because it uses an exclusive range (for i in start..end)
while end is set to fork_block_number.saturating_sub(1); change the loop to an
inclusive range (for i in start..=end) so all blocks from start through
fork_block_number - 1 are fetched into the blocks HashMap, leaving the existing
separate insertion of fork_block_number intact; update the loop that calls
get_block_hash(BlockId::Number(i), json_rpc_client).await? accordingly.

In `@tests/integration/get_events.rs`:
- Around line 429-431: Create or link a tracked issue in your project tracker
for re-enabling the ignored integration tests marked "// TODO: Remove after
mainnet release" and reference that issue ID in each test TODO comment;
specifically, create/attach an issue for the tests including
get_events_from_forked_devnet_when_last_queried_block_on_origin in get_events.rs
and the other ignored tests in get_events.rs (the ones at the blocks around
lines 496 and 533) plus the ignored test in subscription_to_blocks.rs, then
update each test's TODO comment to include the issue number/URL and add the
issue to the release checklist or project board so they will be revisited after
mainnet release.

In `@tests/integration/prove_transaction.rs`:
- Around line 232-240: The assertions around error_message currently accept
generic failure strings; tighten them to only assert the proof-rejection path by
checking for the dedicated "Invalid proof" surface (e.g., replace the assert!
block that inspects error_message with a single assertion that
error_message.contains("Invalid proof") or, if a typed error is available, match
the specific error variant for invalid proof); update both occurrences that use
the error_message/assert! pattern so they no longer allow "Account validation
failed" or "Transaction execution error".

In `@tests/integration/simulate_transactions.rs`:
- Around line 683-688: The test currently discards initial_reads when matching
SimulateTransactionsResult::TransactionsWithInitialReads (using
simulated_transactions and initial_reads) so it won't catch incorrect or empty
initial read data; update the assertion to also validate initial_reads (e.g.,
assert!(!initial_reads.is_empty()) or assert that it contains at least one
expected read entry or expected key/value) after matching that variant, keeping
the existing use of simulated_transactions and referencing the
SimulateTransactionsResult::TransactionsWithInitialReads pattern and the
initial_reads binding.

In `@website/docs/proofs.md`:
- Around line 21-25: The table uses the ambiguous name addInvokeTransaction;
update the docs to use the exact JSON‑RPC method name
starknet_addInvokeTransaction everywhere in the row describing how proofs are
treated (and any adjacent text in the same table), keeping the rest of the
wording intact and preserving references to starknet_proveTransaction; ensure
the table cell replaces "addInvokeTransaction" with
"starknet_addInvokeTransaction" for consistency with the API docs.

---

Outside diff comments:
In `@crates/starknet-devnet-core/src/starknet/add_invoke_transaction.rs`:
- Around line 32-45: The stored RPC transaction still retains caller-supplied
proof facts because you clear tx_v3.proof_facts on sn_api_transaction but then
rebuild invoke_transaction from broadcasted_invoke_transaction; instead rebuild
or sanitize the persisted invoke_transaction from the already-cleared
sn_api_transaction.tx (i.e. use the modified tx_v3 to construct the
Transaction::Invoke/InvokeTransactionV3 instance) or explicitly clear
proof_facts on the value used to create invoke_transaction (reference
sn_api_transaction, tx_v3.proof_facts, broadcasted_invoke_transaction,
invoke_transaction, Transaction::Invoke, InvokeTransactionV3::new) so
persisted/get_transaction_* responses cannot expose user-supplied proof_facts.

In `@crates/starknet-devnet-server/src/api/spec_reader/mod.rs`:
- Around line 386-393: The test currently allows
StarknetResponse::StorageResult(_) for multiple request variants; restrict
StorageResult to only StarknetSpecRequest::StorageAt and ensure the other
variants (ContractNonce, ChainId, ClassHashAtContractAddress) assert only
StarknetResponse::Felt(_). Update the match arm handling
StarknetSpecRequest::ContractNonce, StarknetSpecRequest::ChainId,
StarknetSpecRequest::ClassHashAtContractAddress, and
StarknetSpecRequest::StorageAt so that StorageResult is checked exclusively in
the StorageAt branch and the non-storage branches assert matches!(sn_response,
StarknetResponse::Felt(_)).

---

Nitpick comments:
In `@crates/starknet-devnet-core/src/starknet/events.rs`:
- Around line 112-113: The current address check in the address_condition match
walks the entire addresses list for every event, causing O(events × addresses)
behavior; change the request-level preprocessing so that the incoming addresses
(the addresses parameter used in get_events) are normalized once into a HashSet
(or similar O(1) lookup structure) and then use
set.contains(&event.from_address) in the address_condition branch instead of
address_list.contains(...); update the code paths that build the filter to
accept the precomputed set (or an enum wrapper for None vs HashSet) so the
per-event logic in get_events stays a constant-time lookup.

In `@crates/starknet-devnet-core/src/starknet/state_update.rs`:
- Around line 124-145: The unrelated-address test branch misses asserting that
address-agnostic declared_classes are preserved; update the assertion block
around filtered_by_other/get_state_diff() (other_diff) to also assert that
other_diff.declared_classes equals the original declared classes (or is not
empty as appropriate) so declared_classes are not filtered out by
block_state_update/BlockId::Tag(BlockTag::Latest) when passing
unrelated_address; reference other_diff.declared_classes and ensure the test
verifies declared_classes remain present and unchanged.

In `@crates/starknet-devnet-server/src/api/error.rs`:
- Around line 88-91: Add unit tests that pin the new proof-related RPC error
mappings: assert that ProofVerificationFailed maps to error code 69; assert that
InvalidProofFacts maps to code 55 and that its returned payload includes the
provided reason data; and assert that ProvingError serializes to the
generic/wildcard error mapping (i.e., does not collide with the pinned numeric
codes). Place these tests alongside the existing public RPC error tests in the
same test module in error.rs, using the same helper/serializer used by other
tests to produce the RPC error representation so the assertions target the exact
serialized code and payload.

In `@crates/starknet-devnet-types/src/rpc/proof.rs`:
- Around line 15-17: The method inner() currently returns &Vec<u32>, which
unnecessarily exposes the concrete container; change its signature to return a
slice (&[u32]) and return &self.0[..] (or self.0.as_slice()) so callers get a
borrowed slice instead of a &Vec<u32>; update the inner() definition accordingly
to return &[u32] while keeping the same receiver and behavior.

In `@crates/starknet-devnet-types/src/rpc/state.rs`:
- Around line 92-97: filter_by_address currently does repeated Vec::contains
scans on contract_addresses which is O(n*m); convert contract_addresses into a
HashSet at the start of the function and use set membership checks for
filtering. In practice: inside filter_by_address, create a
HashSet<ContractAddress> from the incoming contract_addresses and then call
retain on deployed_contracts, storage_diffs, nonces, and replaced_classes using
set.contains(&c.address) / set.contains(&s.address) /
set.contains(&n.contract_address) / set.contains(&r.contract_address)
respectively to make the filters O(1) per check.

In `@crates/starknet-devnet-types/src/rpc/transactions.rs`:
- Around line 1133-1168: The conversion impl From<StateMaps> for InitialReads
iterates HashMap-backed StateMaps fields (storage, nonces, class_hashes,
declared_contracts) which yields non-deterministic order; to make InitialReads
deterministic, sort each mapped collection before collecting (e.g., sort storage
entries by (contract_address, key), nonces by contract_address, class_hashes by
contract_address, declared_contracts by class_hash) or use an ordered map
(IndexMap) in StateMaps; update the mapping in InitialReads::from to produce
sorted Vecs for storage, nonces, class_hashes, and declared_contracts so the
output order is stable for hashing/proofs.

In `@crates/starknet-devnet-types/src/rpc/transactions/invoke_transaction_v3.rs`:
- Around line 55-57: The current clone_with_proof_facts method converts a None
proof_facts into Some(empty Vec) via unwrap_or_default(), changing the semantic
meaning; to preserve the original Option semantics, set proof_facts to
self.proof_facts.clone() instead of
Some(self.proof_facts.clone().unwrap_or_default()), otherwise if the intent
really is to always return Some(...) leave as-is but add a comment clarifying
that None should be normalized to Some([]) for downstream logic; refer to the
clone_with_proof_facts function and the proof_facts field when making this
change.

In `@tests/integration/common/utils.rs`:
- Around line 177-192: Extract the hardcoded gas numbers into named constants
and use them where tx_l1_gas, tx_l1_data_gas, and tx_l2_gas are defined before
calling account.execute_v3(...). Replace the inline literals (5_000_000,
1_000_000, 2_500_000_000) with descriptive constants like
DEFAULT_TEST_TX_L1_GAS, DEFAULT_TEST_TX_L1_DATA_GAS, and DEFAULT_TEST_TX_L2_GAS
(declared at the top of the test module or file), then leave the call that
builds prepared_for_prove and execute_v3(tx_calls.clone()) unchanged except for
using those constants.
- Around line 199-201: The loop calling devnet.create_block().await.unwrap()
uses a magic number 11; replace it with a named constant (e.g.,
PROOF_VALIDITY_BLOCKS or BLOCKS_TO_FINALIZE) and add a brief comment explaining
why 11 is required (protocol window, finality, etc.), then update the for _ in
0..11 to for _ in 0..PROOF_VALIDITY_BLOCKS so the intent is clear and
maintainable.

In `@tests/integration/subscription_to_blocks.rs`:
- Line 397: The #[ignore] attribute on the test in
tests/integration/subscription_to_blocks.rs is a temporary suppression and needs
a tracked removal target; replace the bare TODO with a one-line comment directly
above the #[ignore] that references a specific issue/milestone (e.g. "TODO:
Remove after mainnet release — tracked at ISSUE-123: <link>") so the ignore is
discoverable and enforceable, and include an unignore criteria or milestone date
in that comment.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 02bb3951-b4fe-4466-afe2-3babe7d9675c

📥 Commits

Reviewing files that changed from the base of the PR and between 8d694fb and 76059f6.

⛔ Files ignored due to path filters (7)
  • Cargo.lock is excluded by !**/*.lock
  • crates/starknet-devnet-server/test_data/spec/0.10.1/edit_spec_instructions.yaml is excluded by !crates/starknet-devnet-server/test_data/spec/**
  • crates/starknet-devnet-server/test_data/spec/0.10.1/starknet_api_openrpc.json is excluded by !crates/starknet-devnet-server/test_data/spec/**
  • crates/starknet-devnet-server/test_data/spec/0.10.1/starknet_executables.json is excluded by !crates/starknet-devnet-server/test_data/spec/**
  • crates/starknet-devnet-server/test_data/spec/0.10.1/starknet_trace_api_openrpc.json is excluded by !crates/starknet-devnet-server/test_data/spec/**
  • crates/starknet-devnet-server/test_data/spec/0.10.1/starknet_write_api.json is excluded by !crates/starknet-devnet-server/test_data/spec/**
  • crates/starknet-devnet-server/test_data/spec/0.10.1/starknet_ws_api.json is excluded by !crates/starknet-devnet-server/test_data/spec/**
📒 Files selected for processing (84)
  • Cargo.toml
  • crates/starknet-devnet-core/Cargo.toml
  • crates/starknet-devnet-core/src/constants.rs
  • crates/starknet-devnet-core/src/error.rs
  • crates/starknet-devnet-core/src/starknet/add_invoke_transaction.rs
  • crates/starknet-devnet-core/src/starknet/events.rs
  • crates/starknet-devnet-core/src/starknet/mod.rs
  • crates/starknet-devnet-core/src/starknet/proofs.rs
  • crates/starknet-devnet-core/src/starknet/starknet_config.rs
  • crates/starknet-devnet-core/src/starknet/state_update.rs
  • crates/starknet-devnet-core/src/state/mod.rs
  • crates/starknet-devnet-core/src/state/state_diff.rs
  • crates/starknet-devnet-core/src/utils.rs
  • crates/starknet-devnet-server/src/api/endpoints.rs
  • crates/starknet-devnet-server/src/api/endpoints_ws.rs
  • crates/starknet-devnet-server/src/api/error.rs
  • crates/starknet-devnet-server/src/api/json_rpc_handler.rs
  • crates/starknet-devnet-server/src/api/mod.rs
  • crates/starknet-devnet-server/src/api/models/json_rpc_request.rs
  • crates/starknet-devnet-server/src/api/models/json_rpc_response.rs
  • crates/starknet-devnet-server/src/api/models/mod.rs
  • crates/starknet-devnet-server/src/api/spec_reader/mod.rs
  • crates/starknet-devnet-server/src/subscribe.rs
  • crates/starknet-devnet-types/Cargo.toml
  • crates/starknet-devnet-types/src/lib.rs
  • crates/starknet-devnet-types/src/rpc.rs
  • crates/starknet-devnet-types/src/rpc/block.rs
  • crates/starknet-devnet-types/src/rpc/felt.rs
  • crates/starknet-devnet-types/src/rpc/proof.rs
  • crates/starknet-devnet-types/src/rpc/state.rs
  • crates/starknet-devnet-types/src/rpc/transactions.rs
  • crates/starknet-devnet-types/src/rpc/transactions/broadcasted_invoke_transaction_v3.rs
  • crates/starknet-devnet-types/src/rpc/transactions/invoke_transaction_v3.rs
  • crates/starknet-devnet/src/cli.rs
  • crates/starknet-devnet/src/main.rs
  • tests/integration/Cargo.toml
  • tests/integration/abort_blocks.rs
  • tests/integration/accepting_blocks_on_l1.rs
  • tests/integration/account_impersonation.rs
  • tests/integration/account_selection.rs
  • tests/integration/advancing_time.rs
  • tests/integration/advancing_time_on_fork.rs
  • tests/integration/balance.rs
  • tests/integration/blocks_generation.rs
  • tests/integration/call.rs
  • tests/integration/common/background_devnet.rs
  • tests/integration/common/utils.rs
  • tests/integration/deploy.rs
  • tests/integration/dump_and_load.rs
  • tests/integration/estimate_fee.rs
  • tests/integration/estimate_message_fee.rs
  • tests/integration/fork.rs
  • tests/integration/gas_modification.rs
  • tests/integration/general_integration_tests.rs
  • tests/integration/general_rpc_tests.rs
  • tests/integration/get_block_txs_count.rs
  • tests/integration/get_block_with_txs_and_receipts.rs
  • tests/integration/get_class.rs
  • tests/integration/get_class_hash_at.rs
  • tests/integration/get_events.rs
  • tests/integration/get_transaction_by_block_id_and_index.rs
  • tests/integration/get_transaction_by_hash.rs
  • tests/integration/lib.rs
  • tests/integration/messaging.rs
  • tests/integration/minting.rs
  • tests/integration/old_state.rs
  • tests/integration/prove_transaction.rs
  • tests/integration/restart.rs
  • tests/integration/restrictive_mode.rs
  • tests/integration/simulate_transactions.rs
  • tests/integration/subscription_to_blocks.rs
  • tests/integration/subscription_to_events.rs
  • tests/integration/subscription_to_new_tx_receipts.rs
  • tests/integration/subscription_to_new_txs.rs
  • tests/integration/subscription_to_reorg.rs
  • tests/integration/subscription_to_tx_status.rs
  • tests/integration/subscription_with_invalid_block_id.rs
  • tests/integration/trace.rs
  • tests/integration/transaction_handling.rs
  • tests/integration/v3_transactions.rs
  • tests/integration/websocket.rs
  • website/docs/api.md
  • website/docs/proofs.md
  • website/docs/running/cli.md

Comment on lines +268 to +277
let start = fork_block_number.saturating_sub(STORED_BLOCK_HASH_BUFFER);
let end = fork_block_number.saturating_sub(1);

let mut blocks = HashMap::new();

for i in start..end {
tracing::info!("Fetching block {:?} into forking struct", i);
let (num, hash) = get_block_hash(BlockId::Number(i), json_rpc_client).await?;
blocks.insert(num, hash);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify loop bounds in fork preload logic
fd main.rs | xargs rg -n -C2 'let start =|let end =|for i in start\.\.end|for i in start\.\.fork_block_number|blocks\.insert\(fork_block_number'

Repository: 0xSpaceShard/starknet-devnet

Length of output: 674


Fix off-by-one when preloading recent fork blocks.

The loop at line 273 uses an exclusive range with end = fork_block_number - 1, which excludes block fork_block_number - 1 from being fetched. Only block fork_block_number is inserted separately at line 278, leaving a gap in the preloaded blocks.

🐛 Proposed fix
     let start = fork_block_number.saturating_sub(STORED_BLOCK_HASH_BUFFER);
-    let end = fork_block_number.saturating_sub(1);
 
     let mut blocks = HashMap::new();
 
-    for i in start..end {
+    for i in start..fork_block_number {
         tracing::info!("Fetching block {:?} into forking struct", i);
         let (num, hash) = get_block_hash(BlockId::Number(i), json_rpc_client).await?;
         blocks.insert(num, hash);
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let start = fork_block_number.saturating_sub(STORED_BLOCK_HASH_BUFFER);
let end = fork_block_number.saturating_sub(1);
let mut blocks = HashMap::new();
for i in start..end {
tracing::info!("Fetching block {:?} into forking struct", i);
let (num, hash) = get_block_hash(BlockId::Number(i), json_rpc_client).await?;
blocks.insert(num, hash);
}
let start = fork_block_number.saturating_sub(STORED_BLOCK_HASH_BUFFER);
let mut blocks = HashMap::new();
for i in start..fork_block_number {
tracing::info!("Fetching block {:?} into forking struct", i);
let (num, hash) = get_block_hash(BlockId::Number(i), json_rpc_client).await?;
blocks.insert(num, hash);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/starknet-devnet/src/main.rs` around lines 268 - 277, The for-loop that
preloads recent fork blocks skips block fork_block_number - 1 because it uses an
exclusive range (for i in start..end) while end is set to
fork_block_number.saturating_sub(1); change the loop to an inclusive range (for
i in start..=end) so all blocks from start through fork_block_number - 1 are
fetched into the blocks HashMap, leaving the existing separate insertion of
fork_block_number intact; update the loop that calls
get_block_hash(BlockId::Number(i), json_rpc_client).await? accordingly.

Comment on lines +232 to +240
let error_message = error.to_string();

assert!(
error_message.contains("Invalid proof")
|| error_message.contains("Account validation failed")
|| error_message.contains("Transaction execution error"),
"Expected proof rejection, got: {}",
error_message
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Narrow these rejection assertions to the proof-failure path.

Both tests currently accept generic "Account validation failed" / "Transaction execution error" strings, so an unrelated fee, nonce, or execution failure could still make them pass. Please assert the dedicated invalid-proof surface here instead of broad catch-alls.

Also applies to: 345-352

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/integration/prove_transaction.rs` around lines 232 - 240, The
assertions around error_message currently accept generic failure strings;
tighten them to only assert the proof-rejection path by checking for the
dedicated "Invalid proof" surface (e.g., replace the assert! block that inspects
error_message with a single assertion that error_message.contains("Invalid
proof") or, if a typed error is available, match the specific error variant for
invalid proof); update both occurrences that use the error_message/assert!
pattern so they no longer allow "Account validation failed" or "Transaction
execution error".

Comment on lines +683 to +688
SimulateTransactionsResult::TransactionsWithInitialReads {
simulated_transactions,
initial_reads: _,
} => simulated_transactions,
_ => unreachable!("With return initial reads flag"),
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Assert the returned initial_reads, not just the variant.

initial_reads is discarded here, so this still passes if the flag only changes the response shape but returns empty or incorrect data. Please validate at least one expected read so the test covers the new behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/integration/simulate_transactions.rs` around lines 683 - 688, The test
currently discards initial_reads when matching
SimulateTransactionsResult::TransactionsWithInitialReads (using
simulated_transactions and initial_reads) so it won't catch incorrect or empty
initial read data; update the assertion to also validate initial_reads (e.g.,
assert!(!initial_reads.is_empty()) or assert that it contains at least one
expected read entry or expected key/value) after matching that variant, keeping
the existing use of simulated_transactions and referencing the
SimulateTransactionsResult::TransactionsWithInitialReads pattern and the
initial_reads binding.

Comment on lines +21 to +25
| Mode | CLI value | What `starknet_proveTransaction` does | How `addInvokeTransaction` treats `proof` and `proof_facts` |
| ------ | ------------------ | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ |
| Full | `full` | Not implemented yet | Rejects with unsupported action |
| Devnet | `devnet` (default) | Returns a deterministic mock proof + proof facts | If both fields are present, verifies them; if one is missing or verification fails, rejects; if both are absent, accepts |
| None | `none` | Disabled / unsupported | Ignores incoming `proof` and `proof_facts` for invoke txs |
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use exact JSON-RPC method name in the mode table.

addInvokeTransaction is ambiguous; docs should use starknet_addInvokeTransaction for consistency with the rest of the API docs.

✏️ Suggested doc fix
-| Mode   | CLI value          | What `starknet_proveTransaction` does            | How `addInvokeTransaction` treats `proof` and `proof_facts`                                                              |
+| Mode   | CLI value          | What `starknet_proveTransaction` does            | How `starknet_addInvokeTransaction` treats `proof` and `proof_facts`                                                     |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@website/docs/proofs.md` around lines 21 - 25, The table uses the ambiguous
name addInvokeTransaction; update the docs to use the exact JSON‑RPC method name
starknet_addInvokeTransaction everywhere in the row describing how proofs are
treated (and any adjacent text in the same table), keeping the rest of the
wording intact and preserving references to starknet_proveTransaction; ensure
the table cell replaces "addInvokeTransaction" with
"starknet_addInvokeTransaction" for consistency with the API docs.

@3alpha 3alpha merged commit 2557d9a into main Mar 31, 2026
4 checks passed
@3alpha 3alpha deleted the starknet-v0.14.2 branch March 31, 2026 10:06
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.

1 participant