Skip to content

Comments

chore:verify recipient signature via snip12#130

Open
mubarak23 wants to merge 1 commit intomainfrom
ft_cancel_signature
Open

chore:verify recipient signature via snip12#130
mubarak23 wants to merge 1 commit intomainfrom
ft_cancel_signature

Conversation

@mubarak23
Copy link
Contributor

@mubarak23 mubarak23 commented Jul 29, 2025

  • this is a preliminary implementation for recipient signature verification via snip12

Summary by CodeRabbit

  • New Features

    • Introduced cryptographic signature verification for mutual stream cancellation, enhancing security for cancellation actions.
  • Bug Fixes

    • Added a new error message for invalid recipient signatures to provide clearer feedback during cancellation attempts.

@mubarak23 mubarak23 requested a review from Utilitycoder July 29, 2025 10:38
@mubarak23 mubarak23 self-assigned this Jul 29, 2025
@coderabbitai
Copy link

coderabbitai bot commented Jul 29, 2025

Walkthrough

A new error constant for invalid recipient signatures is added to the error definitions. In the payment stream contract, cryptographic signature verification logic is introduced for mutual stream cancellation, including new internal functions for message hashing and ECDSA signature checking. These functions are defined but not yet invoked in the main cancellation workflow.

Changes

Cohort / File(s) Change Summary
Error Constant Addition
src/base/errors.cairo
Added new error constant INVALID_RECIPIENT_SIGNATURE with message 'Error: No recipient signature'.
Mutual Cancel Signature Verification
src/payment_stream.cairo
Added imports for cryptographic signature verification; defined get_message_hash and mutual_cancel internal functions for ECDSA-based mutual cancellation; extended error enum with INVALID_RECIPIENT_SIGNATURE; placeholder for future integration in cancellation logic.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant PaymentStreamContract
    participant CryptoLib

    User->>PaymentStreamContract: Request mutual_cancel(stream_id, sig_r, sig_s, recipient_pubkey)
    PaymentStreamContract->>CryptoLib: get_message_hash(stream_id, approved)
    CryptoLib-->>PaymentStreamContract: message_hash
    PaymentStreamContract->>CryptoLib: verify_ecdsa_signature(message_hash, sig_r, sig_s, recipient_pubkey)
    CryptoLib-->>PaymentStreamContract: is_valid
    alt Signature valid
        PaymentStreamContract-->>User: Proceed with mutual cancellation
    else Signature invalid
        PaymentStreamContract-->>User: Revert with INVALID_RECIPIENT_SIGNATURE error
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~15 minutes

Poem

A hop, a skip, a cryptic leap—
New checks for streams before they sleep.
With hashes, keys, and signatures true,
The contract guards what streams may do.
If rabbits signed, their streams would flow,
But without a pawprint—no go!
🐇✨

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

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

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.
    • Explain this complex logic.
    • 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. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • 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 src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

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

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

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

Documentation and Community

  • 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.

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

🧹 Nitpick comments (1)
src/base/errors.cairo (1)

142-143: Clarify the error message to match the constant name

The error constant INVALID_RECIPIENT_SIGNATURE suggests signature validation failure, but the message says "No recipient signature" which implies absence. Consider updating for clarity:

-    pub const INVALID_RECIPIENT_SIGNATURE: felt252 = 'Error: No recipient signature';
+    pub const INVALID_RECIPIENT_SIGNATURE: felt252 = 'Error: Invalid recipient signature';
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f15de56 and 4edfe41.

📒 Files selected for processing (2)
  • src/base/errors.cairo (1 hunks)
  • src/payment_stream.cairo (5 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: tests

Comment on lines +19 to +22
// use openzeppelin::utils::cryptography::interface::ISRC6;
use openzeppelin::utils::cryptography::snip12::{
OffchainMessageHash, SNIP12Metadata, StructHash,
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove unused imports and implement proper SNIP12 message hashing

The SNIP12 imports (OffchainMessageHash, SNIP12Metadata, StructHash) are imported but not used. The current get_message_hash implementation uses basic Pedersen hashing instead of SNIP12's structured message hashing with domain separation.

For proper SNIP12 compliance, either:

  1. Remove the unused imports if not implementing SNIP12
  2. Implement proper SNIP12 message hashing with domain separation
🤖 Prompt for AI Agents
In src/payment_stream.cairo around lines 19 to 22, the SNIP12 imports are unused
and the get_message_hash function uses basic Pedersen hashing instead of SNIP12
structured hashing. To fix this, either remove the unused SNIP12 imports if you
do not plan to implement SNIP12 message hashing, or update get_message_hash to
use SNIP12's OffchainMessageHash or StructHash with domain separation to comply
with SNIP12 standards.

Comment on lines +323 to +330
fn get_message_hash(self: @ContractState, stream_id: u256, approved: felt252) -> felt252 {
let mut hash_state = PedersenTrait::new(0);
hash_state = hash_state.update('CancelStreamMessage');
hash_state = hash_state.update(stream_id.low.into());
hash_state = hash_state.update(stream_id.high.into());
hash_state = hash_state.update(approved);
hash_state.finalize()
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add replay protection and domain separation to message hash

The current message hash implementation has critical security issues:

  1. No replay protection: The same signature can be reused multiple times
  2. No domain separation: Signatures could be valid across different contracts
  3. Not SNIP12 compliant: Despite the PR title, this doesn't follow SNIP12 standard

Consider adding:

  • A nonce or timestamp for replay protection
  • Contract address for domain separation
  • Proper SNIP12 structured hashing
🤖 Prompt for AI Agents
In src/payment_stream.cairo around lines 323 to 330, the get_message_hash
function lacks replay protection, domain separation, and SNIP12 compliance. To
fix this, include a nonce or timestamp parameter in the hash inputs to prevent
replay attacks, add the contract address to the hash to ensure domain
separation, and restructure the hashing logic to follow the SNIP12 standard
format for message hashing. Update the function signature and hash computation
accordingly to incorporate these elements.

Comment on lines +323 to +345
fn get_message_hash(self: @ContractState, stream_id: u256, approved: felt252) -> felt252 {
let mut hash_state = PedersenTrait::new(0);
hash_state = hash_state.update('CancelStreamMessage');
hash_state = hash_state.update(stream_id.low.into());
hash_state = hash_state.update(stream_id.high.into());
hash_state = hash_state.update(approved);
hash_state.finalize()
}


fn mutual_cancel(
self: @ContractState,
stream_id: u256,
sig_r: felt252,
sig_s: felt252,
recipient_pubkey: felt252,
) -> bool {
let message_hash = self
.get_message_hash(stream_id, '1'); // 1 recipient has approve the cancellation
let is_valid = check_ecdsa_signature(message_hash, recipient_pubkey, sig_r, sig_s);
assert(is_valid, INVALID_RECIPIENT_SIGNATURE);
return true;
}
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify test coverage for signature verification

The new signature verification functions lack test coverage. Please ensure comprehensive tests for:

  • Valid signature verification
  • Invalid signature rejection
  • Replay attack prevention
  • Edge cases (wrong stream_id, wrong recipient, etc.)

🏁 Script executed:

#!/bin/bash
# Search for tests related to mutual cancellation or signature verification
echo "Searching for signature verification tests..."
fd -e cairo -e rs test | xargs rg -l "mutual_cancel|recipient.*signature|snip12" | head -20

echo -e "\nSearching for any test files that might test the payment stream cancel functionality..."
fd -e cairo test | xargs rg -l "cancel.*stream|StreamCanceled" | head -10

Length of output: 532


Add comprehensive tests for mutual_cancel signature verification

I didn’t find any existing tests covering the mutual_cancel flow or ECDSA signature checks. Please add or extend tests in your suite to cover:

  • Valid signature acceptance for mutual_cancel
  • Rejection of invalid signatures (wrong r, s, or public key)
  • Replay-attack prevention (same signature cannot be reused)
  • Edge cases (incorrect stream_id, unauthorized recipient, zero/overflow values)

Suggested locations:

  • tests/test_payment_stream.cairo – extend with mutual_cancel scenarios
  • (Optional) a new file, e.g. tests/test_signature_verification.cairo, if you prefer to isolate signature tests
🤖 Prompt for AI Agents
In src/payment_stream.cairo around lines 323 to 345, there are no tests covering
the mutual_cancel function and its ECDSA signature verification. Add
comprehensive tests in tests/test_payment_stream.cairo or a new test file like
tests/test_signature_verification.cairo to cover valid signature acceptance,
rejection of invalid signatures (altered r, s, or public key), replay attack
prevention by ensuring the same signature cannot be reused, and edge cases such
as incorrect stream_id, unauthorized recipient, and zero or overflow values.
These tests should call mutual_cancel with various inputs and assert expected
success or failure outcomes.

Comment on lines +333 to +345
fn mutual_cancel(
self: @ContractState,
stream_id: u256,
sig_r: felt252,
sig_s: felt252,
recipient_pubkey: felt252,
) -> bool {
let message_hash = self
.get_message_hash(stream_id, '1'); // 1 recipient has approve the cancellation
let is_valid = check_ecdsa_signature(message_hash, recipient_pubkey, sig_r, sig_s);
assert(is_valid, INVALID_RECIPIENT_SIGNATURE);
return true;
}
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Fix signature verification logic and validate recipient

Several issues with the mutual_cancel implementation:

  1. Hardcoded approval value: Always passes '1' for approved, making the parameter meaningless
  2. Missing recipient validation: Should verify that recipient_pubkey matches the stream recipient's public key
  3. Redundant return type: Function returns bool but always returns true (reverts on failure)
  4. Not integrated: Function is defined but never called

🏁 Script executed:

#!/bin/bash
# Check if mutual_cancel is called anywhere in the codebase
echo "Searching for mutual_cancel function calls..."
rg "mutual_cancel" --type cairo -A 3 -B 3

Length of output: 172


Here's a refined check to locate any invocations of mutual_cancel in the repo:


🏁 Script executed:

#!/bin/bash
echo "Searching for calls to mutual_cancel..."
rg -n "mutual_cancel(" .

Length of output: 194


Let’s retry locating any calls (escaping the parenthesis):


🏁 Script executed:

#!/bin/bash
echo "Searching for calls to mutual_cancel..."
rg -n 'mutual_cancel\(' .

Length of output: 173


Revise and integrate mutual_cancel in the cancellation workflow

The new mutual_cancel at src/payment_stream.cairo:333–345 needs the following fixes before merging:

  • Ensure it’s actually invoked
    • No references found beyond its definition—either call it from your cancel function or remove it.
  • Remove or parameterize the hard-coded approval flag
    • Passing '1' into get_message_hash makes the “approved” argument meaningless. Use a dynamic flag or drop the parameter.
  • Validate the signer against the stored stream recipient
    • Lookup the recipient’s public key in state and assert that it matches recipient_pubkey before signature verification.
  • Simplify the return type
    • Since the function either returns true or reverts, change its signature to -> () (no return value) to avoid redundant booleans.
🤖 Prompt for AI Agents
In src/payment_stream.cairo around lines 333 to 345, the mutual_cancel function
is defined but never called, so integrate it by invoking it from the main cancel
function or remove it if unused. Replace the hard-coded '1' approval flag in
get_message_hash with a dynamic parameter or remove it if unnecessary. Add a
check to retrieve the stored recipient public key from the contract state and
assert it matches the recipient_pubkey argument before verifying the signature.
Finally, change the function signature to return no value (-> ()) since it
either succeeds or reverts, eliminating the redundant boolean return.

Comment on lines 1004 to +1006
fn cancel(ref self: ContractState, stream_id: u256) {
// check for mutual cancel approval via SNIP12

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Incomplete implementation - signature verification not integrated

The comment indicates intent to check mutual cancellation approval, but the mutual_cancel function is never called. This means:

  1. The PR objective of "verify recipient signature via snip12" is not achieved
  2. Stream cancellation still works without recipient approval
  3. The new signature verification code is dead code

Either:

  • Complete the implementation by calling mutual_cancel with appropriate parameters
  • Remove the incomplete code until ready for full implementation

Would you like me to help implement the complete signature verification flow in the cancel function?

🤖 Prompt for AI Agents
In src/payment_stream.cairo around lines 1004 to 1006, the cancel function
comments mention checking mutual cancel approval via SNIP12, but the
mutual_cancel function is never called, leaving signature verification
unimplemented and allowing cancellation without recipient approval. To fix this,
integrate a call to mutual_cancel within the cancel function, passing the
necessary parameters such as the stream_id and signature data, to enforce
recipient signature verification before allowing cancellation. Alternatively, if
the feature is not ready, remove the incomplete mutual_cancel related code to
avoid dead code and confusion.

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