Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Note
|
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Description check | 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 | 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.
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.
8eb1cfe to
c327bbd
Compare
c327bbd to
6b36fb8
Compare
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
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 | 🟠 MajorKeep
StorageResultvalid only forStorageAtchecks.Line 390–393 currently allows
StarknetResponse::StorageResult(_)forContractNonce,ChainId, andClassHashAtContractAddresstoo. 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::Nonestill preserves caller-supplied proof facts in the stored transaction.This branch only clears
sn_api_transaction.tx.proof_facts. Line 41 then rebuilds the persistedinvoke_transactionfrom the originalbroadcasted_invoke_transaction, soget_transaction_*can still surface user-providedproof_factswhenIncludeProofFactsis 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 fordeclared_classesin the unrelated-address branch.This branch verifies address-scoped fields are empty, but it doesn’t check that address-agnostic
declared_classesare 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 infilter_by_address.Line 94–97 currently performs repeated linear
containsscans onVec<ContractAddress>, which scales poorly as inputs grow. Converting addresses once to aHashSetkeeps 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, andProvingError -> wildcardwould 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>forinner().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
11for 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 betweenNoneand emptyVecinclone_with_proof_facts.The method uses
unwrap_or_default()which convertsNonetoSome(vec![]). This changes the semantics:None(no proof facts provided) becomesSome([])(explicitly empty proof facts). If this distinction matters for downstream processing, consider preservingNone:💡 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_eventsscales withevents × 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
StateMapsfields. IfStateMapsusesHashMapinternally, 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 usingIndexMap.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
⛔ Files ignored due to path filters (7)
Cargo.lockis excluded by!**/*.lockcrates/starknet-devnet-server/test_data/spec/0.10.1/edit_spec_instructions.yamlis excluded by!crates/starknet-devnet-server/test_data/spec/**crates/starknet-devnet-server/test_data/spec/0.10.1/starknet_api_openrpc.jsonis excluded by!crates/starknet-devnet-server/test_data/spec/**crates/starknet-devnet-server/test_data/spec/0.10.1/starknet_executables.jsonis excluded by!crates/starknet-devnet-server/test_data/spec/**crates/starknet-devnet-server/test_data/spec/0.10.1/starknet_trace_api_openrpc.jsonis excluded by!crates/starknet-devnet-server/test_data/spec/**crates/starknet-devnet-server/test_data/spec/0.10.1/starknet_write_api.jsonis excluded by!crates/starknet-devnet-server/test_data/spec/**crates/starknet-devnet-server/test_data/spec/0.10.1/starknet_ws_api.jsonis excluded by!crates/starknet-devnet-server/test_data/spec/**
📒 Files selected for processing (84)
Cargo.tomlcrates/starknet-devnet-core/Cargo.tomlcrates/starknet-devnet-core/src/constants.rscrates/starknet-devnet-core/src/error.rscrates/starknet-devnet-core/src/starknet/add_invoke_transaction.rscrates/starknet-devnet-core/src/starknet/events.rscrates/starknet-devnet-core/src/starknet/mod.rscrates/starknet-devnet-core/src/starknet/proofs.rscrates/starknet-devnet-core/src/starknet/starknet_config.rscrates/starknet-devnet-core/src/starknet/state_update.rscrates/starknet-devnet-core/src/state/mod.rscrates/starknet-devnet-core/src/state/state_diff.rscrates/starknet-devnet-core/src/utils.rscrates/starknet-devnet-server/src/api/endpoints.rscrates/starknet-devnet-server/src/api/endpoints_ws.rscrates/starknet-devnet-server/src/api/error.rscrates/starknet-devnet-server/src/api/json_rpc_handler.rscrates/starknet-devnet-server/src/api/mod.rscrates/starknet-devnet-server/src/api/models/json_rpc_request.rscrates/starknet-devnet-server/src/api/models/json_rpc_response.rscrates/starknet-devnet-server/src/api/models/mod.rscrates/starknet-devnet-server/src/api/spec_reader/mod.rscrates/starknet-devnet-server/src/subscribe.rscrates/starknet-devnet-types/Cargo.tomlcrates/starknet-devnet-types/src/lib.rscrates/starknet-devnet-types/src/rpc.rscrates/starknet-devnet-types/src/rpc/block.rscrates/starknet-devnet-types/src/rpc/felt.rscrates/starknet-devnet-types/src/rpc/proof.rscrates/starknet-devnet-types/src/rpc/state.rscrates/starknet-devnet-types/src/rpc/transactions.rscrates/starknet-devnet-types/src/rpc/transactions/broadcasted_invoke_transaction_v3.rscrates/starknet-devnet-types/src/rpc/transactions/invoke_transaction_v3.rscrates/starknet-devnet/src/cli.rscrates/starknet-devnet/src/main.rstests/integration/Cargo.tomltests/integration/abort_blocks.rstests/integration/accepting_blocks_on_l1.rstests/integration/account_impersonation.rstests/integration/account_selection.rstests/integration/advancing_time.rstests/integration/advancing_time_on_fork.rstests/integration/balance.rstests/integration/blocks_generation.rstests/integration/call.rstests/integration/common/background_devnet.rstests/integration/common/utils.rstests/integration/deploy.rstests/integration/dump_and_load.rstests/integration/estimate_fee.rstests/integration/estimate_message_fee.rstests/integration/fork.rstests/integration/gas_modification.rstests/integration/general_integration_tests.rstests/integration/general_rpc_tests.rstests/integration/get_block_txs_count.rstests/integration/get_block_with_txs_and_receipts.rstests/integration/get_class.rstests/integration/get_class_hash_at.rstests/integration/get_events.rstests/integration/get_transaction_by_block_id_and_index.rstests/integration/get_transaction_by_hash.rstests/integration/lib.rstests/integration/messaging.rstests/integration/minting.rstests/integration/old_state.rstests/integration/prove_transaction.rstests/integration/restart.rstests/integration/restrictive_mode.rstests/integration/simulate_transactions.rstests/integration/subscription_to_blocks.rstests/integration/subscription_to_events.rstests/integration/subscription_to_new_tx_receipts.rstests/integration/subscription_to_new_txs.rstests/integration/subscription_to_reorg.rstests/integration/subscription_to_tx_status.rstests/integration/subscription_with_invalid_block_id.rstests/integration/trace.rstests/integration/transaction_handling.rstests/integration/v3_transactions.rstests/integration/websocket.rswebsite/docs/api.mdwebsite/docs/proofs.mdwebsite/docs/running/cli.md
| 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); | ||
| } |
There was a problem hiding this comment.
🧩 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.
| 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.
| 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 | ||
| ); |
There was a problem hiding this comment.
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".
| SimulateTransactionsResult::TransactionsWithInitialReads { | ||
| simulated_transactions, | ||
| initial_reads: _, | ||
| } => simulated_transactions, | ||
| _ => unreachable!("With return initial reads flag"), | ||
| }; |
There was a problem hiding this comment.
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.
website/docs/proofs.md
Outdated
| | 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 | |
There was a problem hiding this comment.
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.
Usage related changes
Development related changes
Checklist:
./scripts/format.sh./scripts/clippy_check.sh./scripts/check_unused_deps.sh./scripts/check_spelling.sh./website/README.mdSummary by CodeRabbit
Release Notes
--proof-modeCLI flag for transaction proof generation and verification.starknet_proveTransactionRPC endpoint for generating transaction proofs.