Skip to content

Comments

Close #102#104

Merged
Ugo-X merged 3 commits intoExplore-Beyond-Innovations:mainfrom
yogesh0509:fix/indx_commitment_hash
Aug 24, 2025
Merged

Close #102#104
Ugo-X merged 3 commits intoExplore-Beyond-Innovations:mainfrom
yogesh0509:fix/indx_commitment_hash

Conversation

@yogesh0509
Copy link
Contributor

@yogesh0509 yogesh0509 commented Jul 6, 2025

Summary by CodeRabbit

  • New Features

    • More reliable handling of withdrawal history and state, with cleaner peak management for improved consistency.
  • Bug Fixes

    • Corrected leaf indexing for withdrawals, ensuring accurate indices and root hashes across single and multiple operations.
    • Improved proof verification and excluded invalid peaks to prevent inconsistencies.
    • Events now consistently report correct indices and root hashes.
  • Tests

    • Added comprehensive tests for single, multiple, and edge-case withdrawals to validate MMR state.
  • Chores

    • Removed a package manager pin to streamline tooling.

@coderabbitai
Copy link

coderabbitai bot commented Jul 6, 2025

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • 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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@JoE11-y
Copy link
Contributor

JoE11-y commented Aug 17, 2025

Hi @yogesh0509

Ready to close it out.

Just updated the cairo-lib, it was out of date, causing the compiler to throw errors sometimes.

@yogesh0509
Copy link
Contributor Author

Hi @yogesh0509

Ready to close it out.

Just updated the cairo-lib, it was out of date, causing the compiler to throw errors sometimes.

sure, I am out right now and can take a look at it on Wednesday. Is that fine?

@JoE11-y
Copy link
Contributor

JoE11-y commented Aug 18, 2025

Sounds good

@Ugo-X
Copy link
Contributor

Ugo-X commented Aug 22, 2025

Hello @yogesh0509 how are you doing? please what's the update?

@yogesh0509 yogesh0509 force-pushed the fix/indx_commitment_hash branch from 01b179f to 0764fc7 Compare August 23, 2025 05:57
@yogesh0509 yogesh0509 marked this pull request as ready for review August 23, 2025 07:15
@yogesh0509
Copy link
Contributor Author

@JoE11-y , I have updated the PR. Can you check if requirement has been completed?

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (11)
contracts/L2/tests/test_ZeroXBridgeL2.cairo (6)

95-160: Potential flakiness: commitment hash uses dynamic block timestamp.

You compute expected_hash with get_block_timestamp() after the burn. If block time advances between the burn and the computation, hashes will diverge and the assertion will fail. Either freeze the timestamp or derive the commitment_hash directly from the emitted BurnEvent.

Apply one of the following:

  • Freeze timestamp:
-    let expected_hash = PoseidonTrait::new().update_with(data_to_hash).finalize();
+    // Freeze timestamp to avoid drift
+    let ts = get_block_timestamp().into();
+    let data_to_hash = BurnData { caller: alice_addr.try_into().unwrap(), amount: burn_amount_usd, nonce: 0, time_stamp: ts };
+    let expected_hash = PoseidonTrait::new().update_with(data_to_hash).finalize();
  • Or, assert partially (user, amount, nonce) and skip recomputing the hash exactly, or fetch the commitment hash from the event payload and compare it for equality with a locally recomputed value if you set/fix timestamp first.

162-209: Test name and assertion read well; minor message nit.

assert(initial_balance - final_balance == burn_amount, 'Token balance not reduced'); works, but if it fails the message reads as if it passed. Consider a stricter message like "final balance should be initial - burn_amount".


210-239: Coverage is good; consider also negative allowance and zero-amount burns.

Insufficient balance is covered. Two useful edge cases:

  • zero-amount burn should revert (contract asserts > 0),
  • allowance insufficient even when balance is sufficient.

I can add those as extra tests if you want.


509-571: Great incremental checks; but the “expected indices” comment likely mismatches MMR indexing.

The comment says: “leaf 0→0, leaf 1→1, leaf 2→2, leaf 3→4, leaf 4→5…”. Common MMR conventions place leaves at odd positions (1,3,5,7,…) and total-node “last_pos” at 2n - popcount(n). Your comment looks inconsistent with either. Since the contract now records a computed index per commitment, please assert the actual per-leaf index returned by get_commitment_index for a few burns and update the comment to match the library’s ground truth.

Example patch to assert indices for the first 5 burns (if leaves are at 1,3,5,7,9):

@@
-    // Expected MMR indices: leaf 0 -> 0, leaf 1 -> 1, leaf 2 -> 2, leaf 3 -> 4, leaf 4 -> 5, etc.
+    // Expected MMR indices (verify with the library; typical scheme: leaves at odd positions 1,3,5,7,9,...)
@@
     let leaves_count_1 = merkle_manager.get_leaves_count();
     assert(leaves_count_1 == 1, 'Expected 1 leaf');
+    let idx1 = merkle_manager.get_commitment_index(
+        PoseidonTrait::new().update_with(BurnData { caller: alice_addr.try_into().unwrap(), amount: burn_amount_usd, nonce: 0, time_stamp: get_block_timestamp().into() }).finalize()
+    );
+    assert(idx1 == 1, 'Unexpected index for leaf 1');

If recomputing the hash is noisy, retrieve it from the BurnEvent via spy_events.


573-628: Edge cases covered; add commitment index checks along power-of-two boundaries.

Since MMR shapes change at powers of two, also assert the stored commitment indices at i = 1, 2, 3, 4, 8. This will catch off-by-one in your index computation logic.


1-1: Scarb formatting failure.

CI reports scarb fmt --check failures for this test file. Please run:

scarb fmt --write
contracts/L2/src/core/ZeroXBridgeL2.cairo (5)

403-409: Filtering zero peaks is fine; consider storing filtered peaks in storage.

You filter zeros at read time. If the MMR library returns fixed-size buffers with trailing zeros, consider storing only non-zero peaks in last_peaks to reduce storage writes/reads and avoid future callers bypassing get_last_peaks.

Possible tweak in append_withdrawal_hash:

- for i in 0..peaks.len() {
-     self.last_peaks.push(*peaks.at(i));
- }
+ for i in 0..peaks.len() {
+     let p = *peaks.at(i);
+     if p != 0 { self.last_peaks.push(p); }
+ }

436-467: Double-check what index you store: leaf position vs “last_pos”.

  • You write node_index_to_root keyed by mmr.last_pos after append. After merges, last_pos represents the last node in the structure (total nodes so far), not the appended leaf’s position.
  • Separately, you compute correct_leaf_index and store it under commitment_hash_to_index.

If downstream proof verification expects “leaf position” as the index, commitment_hash_to_index must hold the leaf’s MMR position, not last_pos. Please confirm which notion of “index” your MMRTrait::verify_proof expects; if it expects leaf position, then any consumer using node_index_to_root with last_pos might be inconsistent.

If you intend to preserve a map from leaf position to root as of append time, consider:

- self.node_index_to_root.write(mmr.last_pos, root_hash);
+ self.node_index_to_root.write(correct_leaf_index, root_hash);

And add a comment clarifying the semantics.


448-453: Peaks storage clear/update is fine; minor micro-optimization possible.

clear_peaks_storage pops one-by-one; harmless given tiny peak counts. If the storage Vec supports truncate(0) or clear(), it would cut syscalls; otherwise keep as-is.


244-286: Message nit: “Burn amount less than zero”.

The assertion is burn_amount_usd > 0, so a clearer message is “Burn amount must be greater than zero”.

-            assert(burn_amount_usd > 0, 'Burn amount less than zero');
+            assert(burn_amount_usd > 0, 'Burn amount must be greater than zero');

1-1: Scarb formatting failure.

CI reports scarb fmt --check failures for this source file. Please run:

scarb fmt --write
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between bfae1a2 and 62a57f3.

⛔ Files ignored due to path filters (2)
  • contracts/L2/package-lock.json is excluded by !**/package-lock.json
  • contracts/L2/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (3)
  • contracts/L2/package.json (0 hunks)
  • contracts/L2/src/core/ZeroXBridgeL2.cairo (3 hunks)
  • contracts/L2/tests/test_ZeroXBridgeL2.cairo (2 hunks)
💤 Files with no reviewable changes (1)
  • contracts/L2/package.json
🧰 Additional context used
🪛 GitHub Actions: L2 contracts ci
contracts/L2/tests/test_ZeroXBridgeL2.cairo

[error] 1-1: scarb fmt --check failed for Cairo test file. Formatting issues detected. Run 'scarb fmt --write' to fix.

contracts/L2/src/core/ZeroXBridgeL2.cairo

[error] 1-1: scarb fmt --check failed for Cairo source. Formatting issues detected (e.g., whitespace, newline at end). Run 'scarb fmt --write' to fix.

🔇 Additional comments (5)
contracts/L2/tests/test_ZeroXBridgeL2.cairo (1)

13-13: Good addition: importing IMerkleManager dispatcher/trait to introspect MMR state.

This enables the tests to query leaves_count, root_hash, and element_count from the bridge. No concerns here.

contracts/L2/src/core/ZeroXBridgeL2.cairo (4)

176-176: Good: initialize leaves_count in constructor.

Explicitly setting leaves_count to 0 avoids undefined state during the first append.


425-426: Switch to trait-based verify_proof looks good.

Passing peaks.span() and proof.span() from the caller aligns with trait expectations and makes the method stateless. No issues spotted.


383-427: Expose peaks cleanly via IMerkleManager; good separation.

The IMerkleManager implementation encapsulates MMR internals and surfaces root, element_count, peaks, and leaves_count. This aligns with the new tests and keeps mint/burn paths clean.


1-501: Manual Verification Needed: Align leaf_count_to_mmr_index with Upstream MMR Semantics

I wasn’t able to locate the MMR trait or its append/verify_proof implementations in our local codebase—the Cairo library (cairo_lib) is pulled in as an external dependency. To prevent subtle off-by-one errors or miscounted internal nodes in our Merkle Mountain Range, please manually confirm that our leaf_count_to_mmr_index logic exactly matches the conventions in cairo_lib/data_structures/mmr/mmr.cairo. Specifically:

  • Review the upstream MMR struct’s last_pos semantics and how append computes the new root and position.
  • Ensure MMRTrait::append’s internal-node counting aligns with our loop in leaf_count_to_mmr_index.
  • Verify that verify_proof expects the same indexing (leaf position + internal nodes) that our bridge uses.
  • Update any test expectations in contracts/L2/src/mocks/MockMerkleManager.cairo and contracts/L2/tests/test_MerkleManager.cairo if the library’s index formula differs.

Without this manual check against the external library, merging risks misaligned proofs or incorrect root lookups.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
contracts/L2/src/core/ZeroXBridgeL2.cairo (1)

486-506: MMR index helper computes last_node position, not leaf position (repeat)

This repeats an earlier concern: the current math sums internal nodes and returns 2n - popcount(n), i.e., the “last node” position after n inserts—not the nth leaf’s position. If verify_proof expects leaf positions, this will mislabel indices at every merge boundary.

Proposed fix (odd leaf positions 1,3,5,…):

-        fn leaf_count_to_mmr_index(leaf_count: felt252) -> felt252 {
-            if leaf_count == 0 {
-                return 0;
-            }
-            // For MMR, the first leaf is at index 1, second at index 2, etc.
-            // But we need to account for internal nodes
-            let mut internal_nodes: u64 = 0;
-            let mut temp: u64 = leaf_count.try_into().unwrap();
-
-            // Count internal nodes created by building the MMR
-            let mut level = 1_u64;
-            while level < temp {
-                internal_nodes += temp / (level * 2);
-                level *= 2;
-            }
-
-            // The MMR index is leaf position + internal nodes before it
-            let mmr_index = leaf_count + internal_nodes.into();
-            mmr_index
-        }
+        // Returns the MMR position for the given leaf number using the common convention:
+        // leaf #1 => 1, leaf #2 => 3, leaf #3 => 5, ...
+        fn leaf_count_to_mmr_index(leaf_count: felt252) -> felt252 {
+            if leaf_count == 0 { return 0; }
+            let n: u64 = leaf_count.try_into().unwrap();
+            let pos: u64 = 2 * n - 1;
+            pos.into()
+        }

If you go with “Option A” in append_withdrawal_hash (deriving from prev_last_pos), consider removing this helper entirely to avoid confusion.

I can open a follow-up to add property-based tests asserting index mapping for n = 1..1024 under your chosen convention.

🧹 Nitpick comments (1)
contracts/L2/src/core/ZeroXBridgeL2.cairo (1)

403-409: get_last_peaks filters zero entries; confirm zero can’t be a valid peak

Filtering out 0 cleans up legacy/placeholder state, but if Poseidon can theoretically output 0, this could drop a legitimate peak. If the MMR library guarantees non-zero node hashes, keep this. Otherwise, consider ensuring zeros are never written in the first place (your append path already repopulates from real peaks).

Would you confirm whether cairo_lib MMR node/peak hashes are guaranteed non-zero?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 62a57f3 and 2708cfa.

📒 Files selected for processing (2)
  • contracts/L2/src/core/ZeroXBridgeL2.cairo (3 hunks)
  • contracts/L2/tests/test_ZeroXBridgeL2.cairo (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • contracts/L2/tests/test_ZeroXBridgeL2.cairo
🧰 Additional context used
🪛 GitHub Actions: L2 contracts ci
contracts/L2/src/core/ZeroXBridgeL2.cairo

[warning] 482-482: Unhandled #[must_use] type core::option::Option<?8>.

🔇 Additional comments (3)
contracts/L2/src/core/ZeroXBridgeL2.cairo (3)

176-176: Good: initialize leaves_count in constructor

Explicitly setting leaves_count to 0 avoids relying on storage defaults and helps with upgrade safety.


425-426: Switch to trait-based verification is a win

Calling MMRTrait::verify_proof(@mmr, …) is cleaner and avoids method-resolution pitfalls. No issues spotted here.


433-473: The section containing leaf_count_to_mmr_index will be printed for review. Once we see its full implementation, we can confirm whether it indeed computes 2n - popcount(n) (i.e., the total node count for n leaves) and validate if adding +1 is semantically equivalent to using mmr.last_pos + 1. Please share the output so we can conclude the verification.

@Ugo-X Ugo-X merged commit 186d6f3 into Explore-Beyond-Innovations:main Aug 24, 2025
2 of 3 checks passed
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.

3 participants