Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(common) Allow adding signed data #141

Merged
merged 8 commits into from
Oct 22, 2024
Merged

feat(common) Allow adding signed data #141

merged 8 commits into from
Oct 22, 2024

Conversation

jns-ps
Copy link
Contributor

@jns-ps jns-ps commented Oct 15, 2024

Introduces a new operation type, AddData, allowing users to add arbitrary data to accounts. The added data can optionally be signed as well.

Summary by CodeRabbit

  • New Features

    • Introduced a new operation type, AddData, allowing users to add arbitrary signed data to accounts.
    • Added methods for adding both unsigned and signed data to accounts in the testing framework.
  • Bug Fixes

    • Enhanced signature verification for the new AddData operation to ensure data integrity.
  • Documentation

    • Updated test cases to reflect the integration of the new AddData operation, ensuring functionality remains intact.

@jns-ps jns-ps self-assigned this Oct 15, 2024
crates/common/src/hashchain.rs Outdated Show resolved Hide resolved
crates/common/src/hashchain.rs Outdated Show resolved Hide resolved
crates/common/src/operation.rs Outdated Show resolved Hide resolved
crates/common/src/operation.rs Outdated Show resolved Hide resolved
@jns-ps jns-ps force-pushed the add-signed-data branch 2 times, most recently from 9e29688 to 84ffa15 Compare October 16, 2024 09:30
@jns-ps jns-ps requested a review from distractedm1nd October 16, 2024 09:34
@jns-ps jns-ps marked this pull request as ready for review October 16, 2024 09:34
Copy link
Contributor

coderabbitai bot commented Oct 16, 2024

Walkthrough

The pull request introduces several modifications to the Operation enum and related structures, including the addition of a new variant AddData and its associated arguments. Key structures and methods have been renamed or updated to ensure consistency and clarity. New methods for adding signed and unsigned data to accounts have been added, along with updates to the process_operation method in the KeyDirectoryTree struct to handle the new operation. Validation and signature verification processes have also been enhanced to accommodate the new functionality.

Changes

File Path Change Summary
crates/common/src/operation.rs - Added enum variant: AddData(AddDataArgs)
- Added struct: AddDataArgs
- Renamed struct: SignatureBundleHashchainSignatureBundle
- Added method: new_add_signed_data(...)
- Updated method signatures for empty_with_idx, new_add_key, and new_revoke_key to use usize instead of u64
- Updated validate and verify_user_signature methods for AddData.
crates/common/src/test_utils.rs - Added methods: add_unsigned_data_to_account and add_signed_data_to_account in TestTreeState
- Added private method: add_data_to_account.
crates/common/src/tree.rs - Updated process_operation method to handle Operation::AddData in KeyDirectoryTree and SnarkableTree.
crates/common/src/hashchain.rs - Enhanced verify_last_entry to support AddData
- Updated get_valid_keys and validate_new_operation methods for AddData
- Added method: verify_signature_at_key_idx.
crates/node_types/prover/src/prover/mod.rs - Updated validate_and_queue_update to include validation for Operation::AddData.

Possibly related PRs

  • feat(sp1): hashchain signature verification in sp1 circuit #125: The changes in this PR involve modifications to the Hashchain struct and its methods, which are directly related to the new AddData operation introduced in the main PR, as both involve updates to the handling and validation of operations within the hashchain.

🐰 In the garden where data grows,
A new variant blooms, as everyone knows.
With keys and signatures, we add with glee,
Operations flourish, as bright as can be!
So hop along, let’s celebrate this day,
For in our code, new magic finds its way! 🌼✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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.

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 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
Contributor

@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: 4

🧹 Outside diff range and nitpick comments (5)
crates/common/src/test_utils.rs (1)

127-144: LGTM with suggestions for improvement

The new add_signed_data_to_account method is well-structured and consistent with other methods in the TestTreeState struct. However, there are a few suggestions for improvement:

  1. Error Handling: Consider replacing unwrap() calls with the ? operator to propagate errors instead of potentially panicking. This would make the function more robust and easier to debug.

  2. Documentation: Add a documentation comment explaining the purpose and usage of this method.

Here's a suggested implementation with improved error handling and documentation:

/// Adds signed data to the specified account's hashchain.
///
/// # Arguments
/// * `data` - The data to be signed and added
/// * `account` - The account to which the signed data will be added
///
/// # Returns
/// * `Ok(())` if the operation was successful
/// * `Err` if there was an issue retrieving the signing key or performing the operation
pub fn add_signed_data_to_account(
    &mut self,
    data: &[u8],
    account: &mut TestAccount,
) -> Result<()> {
    let signing_key = self.signing_keys.get(&account.hashchain.id)
        .ok_or_else(|| anyhow!("Signing key not found for account"))?;
    let signature = signing_key.sign(data);

    let op = Operation::new_add_signed_data(
        account.hashchain.id.clone(),
        data.to_vec(),
        signature,
        0,
    )?;

    account.hashchain.perform_operation(op)?;
    Ok(())
}

This implementation improves error handling and adds a helpful documentation comment.

crates/common/src/hashchain.rs (1)

134-134: Clarify handling of AddSignedData in valid keys construction.

In the loop within verify_last_entry, the Operation::AddSignedData(_) variant is matched but left with an empty block. If AddSignedData operations do not influence the set of valid keys, consider adding a comment to explicitly state that no action is required. This enhances code readability and helps future maintainers understand the control flow.

crates/common/src/operation.rs (3)

Line range hint 320-331: Missing Validation for Empty value in AddSignedData

The validate method checks for empty id and signature.signature fields but does not validate if args.value is empty. An empty value might not be meaningful in this context.

Consider adding a validation check for args.value to ensure it's not empty.

Apply this diff to add the validation:

             if signature.signature.is_empty() {
                 return Err(GeneralError::MissingArgumentError(
                     "signature is empty".to_string(),
                 )
                 .into());
+            }
+
+            if args.value.is_empty() {
+                return Err(GeneralError::MissingArgumentError(
+                    "value is empty".to_string(),
+                )
+                .into());
             }

86-95: Improve Documentation for AddSignedDataArgs

Currently, each field within AddSignedDataArgs is documented, but the struct itself lacks a comprehensive description.

Consider adding a struct-level comment to explain the purpose of AddSignedDataArgs and how it should be used.

Apply this diff to enhance the documentation:

 #[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
+/// Arguments for adding signed data to an account.
 pub struct AddSignedDataArgs {

218-218: Clarify Return Value in get_public_key

In the get_public_key method, the AddSignedData variant returns None:

Operation::RegisterService(_) | Operation::AddSignedData(_) => None,

If AddSignedData operations may involve a public key, consider whether it would be appropriate to return it here. Alternatively, update the documentation to clarify why None is returned for this variant.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 0ea9158 and 84ffa15.

📒 Files selected for processing (5)
  • crates/common/src/hashchain.rs (6 hunks)
  • crates/common/src/operation.rs (9 hunks)
  • crates/common/src/test_utils.rs (1 hunks)
  • crates/common/src/tree.rs (3 hunks)
  • crates/node_types/prover/src/prover.rs (1 hunks)
🧰 Additional context used
🔇 Additional comments (8)
crates/common/src/hashchain.rs (3)

233-249: verify_signature_at_key_idx method correctly encapsulates signature verification.

The new verify_signature_at_key_idx method enhances modularity by encapsulating the signature verification logic. It also provides detailed error messages, including the invalid key and the set of valid keys, which aids in debugging.


293-296: Ensure proper key index handling in validate_new_operation.

In validate_new_operation, the inclusion of Operation::AddSignedData in the signature verification block is appropriate. Verify that the key_idx provided in signature.key_idx correctly corresponds to the intended key in the hashchain, and that key revocation status is accurately checked.

You can run the following script to confirm that key_idx values are used consistently:


175-181: Verify the message used for signature verification in AddSignedData.

When handling Operation::AddSignedData(args) in verify_last_entry, the message used for signature verification is args.value. Ensure that this is the intended data for signature verification. If the signature should cover the entire operation or a specific serialization, consider adjusting the message accordingly for consistency with other operations.

Run the following script to check where AddSignedData is used and how args.value is constructed:

✅ Verification successful

Verified: args.value is correctly used for signature verification in AddSignedData.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all usages of AddSignedData and inspect args.value

# Search for AddSignedData usages and display surrounding context
rg --type rust -A 5 -B 2 'Operation::AddSignedData\('

Length of output: 7892


Script:

#!/bin/bash
# Description: Locate the definition of AddSignedDataArgs and inspect assignments to `value`

# Find the definition of AddSignedDataArgs
rg --type rust 'struct AddSignedDataArgs' -A 10

# Find all assignments to `value` within AddSignedDataArgs
rg --type rust 'AddSignedDataArgs.*value\s*='

Length of output: 777


Script:

#!/bin/bash
# Description: Find all instantiations of AddSignedDataArgs and inspect the value field

# Find all instances where AddSignedDataArgs is instantiated
rg --type rust 'AddSignedDataArgs\s*{' -A 5

# Optionally, display the lines where AddSignedDataArgs is constructed with their surrounding context
rg --type rust 'AddSignedDataArgs\s*{' -A 5 -B 2

Length of output: 308


Script:

#!/bin/bash
# Description: Locate all instantiations of AddSignedDataArgs and inspect the value field

# Corrected regex to find struct instantiations with escaped {
rg --type rust 'AddSignedDataArgs\s*\{' -A 5 -B 2

# Additionally, search for builder patterns or other instantiation methods if applicable
rg --type rust 'AddSignedDataArgs::new\s*\(' -A 3 -B 2

Length of output: 3680

crates/common/src/operation.rs (2)

276-283: Consistent Handling of Signatures in without_signature Method

In the without_signature method, for AddSignedData, the signature field is replaced with an empty signature but retains the key_idx:

signature: SignatureBundle {
    key_idx: args.signature.key_idx,
    signature: Vec::new(),
},

Ensure that this approach is consistent with other operations and that retaining key_idx without the signature does not introduce any unintended side effects.


192-203: ⚠️ Potential issue

Missing Signature Generation in new_add_signed_data

The new_add_signed_data method constructs an AddSignedData operation but does not generate a signature for args.value. This means the signature field must be provided externally, which might not be the intended behavior.

Consider generating the signature within this method to encapsulate the operation fully and prevent misuse.

Apply this diff to generate the signature within the method:

 pub fn new_add_signed_data(
     id: String,
     value: Vec<u8>,
-    signature: Vec<u8>,
     key_idx: usize,
 ) -> Result<Self> {
+    let signature = signing_key.sign(&value).to_vec();
     Ok(Operation::AddSignedData(AddSignedDataArgs {
         id,
         value,
         signature: SignatureBundle { key_idx, signature },
     }))
 }

Likely invalid or redundant comment.

crates/common/src/tree.rs (3)

19-20: Importing AddSignedDataArgs into scope

The addition of AddSignedDataArgs to the imports from the operation module ensures that the necessary structures are available for handling the new AddSignedData operation.


378-379: Confirm consistent handling of AddSignedData in process_operation

The code adds Operation::AddSignedData(AddSignedDataArgs { id, .. }) to the match arms processed similarly to AddKey and RevokeKey. Ensure that AddSignedData requires the same processing steps as the other operations, and that all necessary validation and updates to the hashchain are correctly implemented for this operation.


714-716: Test correctly covers addition of signed data to account

The test test_multiple_inserts_and_updates now includes adding signed data to account2 using add_signed_data_to_account. This expands the test coverage to include the new functionality and verifies that accounts can handle signed data correctly.

crates/node_types/prover/src/prover.rs Outdated Show resolved Hide resolved
crates/common/src/operation.rs Show resolved Hide resolved
crates/common/src/operation.rs Show resolved Hide resolved
crates/common/src/operation.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@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

🧹 Outside diff range and nitpick comments (5)
crates/common/src/test_utils.rs (2)

135-142: LGTM with a suggestion: Consider parameterizing the signing key.

The method provides a clear interface for adding signed data to an account. However, creating a new random signing key for each call might not be suitable for all test scenarios. Consider allowing the caller to optionally provide a signing key, falling back to a random key if not provided. This would increase the flexibility of the method for various test cases.

Example:

pub fn add_signed_data_to_account(
    &mut self,
    data: &[u8],
    account: &mut TestAccount,
    signing_key: Option<&SigningKey>,
) -> Result<()> {
    let signing_key = signing_key.cloned().unwrap_or_else(create_mock_signing_key);
    self.add_data_to_account(data, account, Some(&signing_key))
}

144-167: LGTM with a minor suggestion: Improve error handling.

The implementation correctly handles both signed and unsigned data cases and properly creates and executes the operation on the hashchain. However, there's room for improvement in error handling:

  1. The unwrap() call on line 165 could be replaced with proper error handling to provide more context if an error occurs.

Consider updating the code as follows:

account.hashchain.perform_operation(op)
    .map_err(|e| anyhow!("Failed to perform operation on hashchain: {}", e))?;

This change would provide more context in case of an error and propagate it up the call stack, allowing for better error handling and debugging in test scenarios.

crates/node_types/prover/src/prover/mod.rs (1)

459-459: LGTM! Consider adding a comment for clarity.

The addition of Operation::AddData(_) to this match arm is correct and consistent with the existing logic. It ensures that AddData operations are validated against the existing hashchain, including signature checks, similar to AddKey and RevokeKey operations.

Consider adding a brief comment explaining why these specific operations require validation against the existing hashchain. This would improve code readability and maintainability. For example:

// These operations modify existing accounts and require validation against the current hashchain
Operation::AddKey(_) | Operation::RevokeKey(_) | Operation::AddData(_) => {
    // ... (existing code)
}
crates/common/src/tree.rs (2)

725-735: LGTM: Comprehensive test coverage for new data operations

The additions to the test_multiple_inserts_and_updates test case are well-structured and cover both unsigned and signed data operations. This ensures good test coverage for the new functionality.

Consider adding assertions to verify the content of the added data in the final state of account2.hashchain. This would provide an extra layer of validation for the new operations.


390-391: Enhance AddData implementation and provide more context

The addition of the AddData operation is a good start, but there are a few areas that could use clarification or enhancement:

  1. Could you provide more context on how the data is actually added to the hashchain? This implementation detail is not visible in the current file.
  2. Consider adding a clear distinction between signed and unsigned data in the main processing logic, not just in the tests.
  3. Implement or clarify the verification process for added data, especially for signed data.
  4. Update the documentation to reflect these new operations and their specific behaviors.

These enhancements will make the implementation more robust and easier to understand for other developers.

Also applies to: 725-735

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 84ffa15 and c253341.

📒 Files selected for processing (5)
  • crates/common/src/hashchain.rs (7 hunks)
  • crates/common/src/operation.rs (10 hunks)
  • crates/common/src/test_utils.rs (2 hunks)
  • crates/common/src/tree.rs (3 hunks)
  • crates/node_types/prover/src/prover/mod.rs (1 hunks)
🧰 Additional context used
📓 Learnings (1)
crates/common/src/operation.rs (1)
Learnt from: distractedm1nd
PR: deltadevsde/prism#141
File: crates/common/src/operation.rs:311-313
Timestamp: 2024-10-16T12:03:50.680Z
Learning: When implementing `AddSignedData` in `crates/common/src/operation.rs`, ensure that signatures cover the entire operation (including metadata like account ID) to prevent replay attacks or unauthorized data modifications. Including a `value_signature: Option<(VerifyingKey, Signature)>` allows for externally signed data while ensuring proper validation.
🔇 Additional comments (14)
crates/common/src/test_utils.rs (2)

127-133: LGTM: Clear and concise implementation for adding unsigned data.

The method provides a straightforward public interface for adding unsigned data to an account. It correctly delegates the implementation to the private helper method add_data_to_account.


126-167: Overall assessment: Well-implemented feature addition with minor improvement opportunities.

The new methods add_unsigned_data_to_account, add_signed_data_to_account, and the private helper add_data_to_account are well-implemented and enhance the test utilities by adding support for signed and unsigned data operations. These additions align well with the existing codebase and follow similar patterns.

Key points:

  1. The public methods provide clear interfaces for adding signed and unsigned data.
  2. The private helper method correctly handles both cases and interacts properly with the hashchain.
  3. There are opportunities for minor improvements in flexibility (parameterizing the signing key) and error handling.

These changes significantly expand the testing capabilities of the TestTreeState struct, allowing for more comprehensive testing of data addition operations in the key directory tree implementation.

crates/common/src/operation.rs (6)

16-20: LGTM: New operation type added and comment improved

The addition of the AddData variant to the Operation enum aligns with the PR objective of allowing signed data to be added. The updated comment for AddKey also improves clarity.


45-52: LGTM: New SignatureBundle struct added

The new SignatureBundle struct is well-designed for handling external signatures, which aligns with the PR objective of allowing signed data to be added.


95-106: LGTM: AddDataArgs struct added with proper signature coverage

The new AddDataArgs struct is well-designed for the AddData operation. The inclusion of both value_signature and op_signature allows for proper signature coverage, preventing replay attacks and unauthorized data modifications as discussed in the learnings.


203-229: LGTM: new_add_signed_data method added

The new_add_signed_data method is well-implemented and consistent with the PR objective. It properly handles both the operation signature and the optional value signature, aligning with the security considerations discussed in the learnings.


329-345: LGTM: Proper signature verification for AddData operation

The verify_user_signature method has been updated to handle the new AddData operation correctly. It verifies both the operation signature and the optional value signature, which aligns with the security considerations mentioned in the learnings. The handling of cases where no value signature is provided is also appropriate.


29-31: ⚠️ Potential issue

Consider using a fixed-size integer for key_idx

The change from u64 to usize for key_idx might introduce portability issues across different platforms (32-bit vs 64-bit). Consider using a fixed-size integer type like u64 to ensure consistent behavior across all platforms.

 pub struct HashchainSignatureBundle {
     /// Index of the key in the hashchain
-    pub key_idx: usize,
+    pub key_idx: u64,

Likely invalid or redundant comment.

crates/common/src/tree.rs (2)

19-20: LGTM: New imports and Operation enum update

The addition of AddDataArgs import and the inclusion of AddData in the Operation enum are consistent with the new functionality for adding data. These changes align well with the described modifications in the AI-generated summary.


390-391: Verify identical handling for different operations

The inclusion of Operation::AddData in the match arm is correct and aligns with the new functionality. However, the identical handling for AddKey, RevokeKey, and AddData operations raises a question:

Are there any operation-specific checks or validations that should be performed for AddData that differ from AddKey or RevokeKey?

Please review if any additional checks or different logic should be implemented for the AddData operation.

crates/common/src/hashchain.rs (4)

240-258: Well-structured extraction of signature verification logic

The new verify_signature_at_key_idx method cleanly encapsulates the signature verification process, enhancing code maintainability and readability by avoiding duplication.


302-308: Consistent pattern matching for signature extraction

Using pattern matching to extract the signature from AddKey, RevokeKey, and AddData operations improves code clarity and reduces redundancy.


134-134: Confirm necessity of handling AddData in prior entries

In the loop over previous entries within verify_last_entry, the Operation::AddData case currently performs no action:

Operation::AddData(_) => {}

Please confirm that no additional validation or state updates are required for AddData operations in prior entries.


213-215: 🛠️ Refactor suggestion

Avoid unnecessary cloning in get_valid_keys method

Cloning self.entries creates an unnecessary copy of the data, which could impact performance. Consider iterating over references instead:

- for entry in self.entries.clone() {
+ for entry in &self.entries {

This change eliminates the need to clone the entire entries vector.

Likely invalid or redundant comment.

crates/common/src/operation.rs Show resolved Hide resolved
crates/common/src/tree.rs Show resolved Hide resolved
@jns-ps jns-ps merged commit fa6fd7b into main Oct 22, 2024
5 checks passed
@jns-ps jns-ps deleted the add-signed-data branch October 22, 2024 09:49
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