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

Fix/substrate sign tx #89

Merged
merged 15 commits into from
Feb 11, 2025
Merged

Fix/substrate sign tx #89

merged 15 commits into from
Feb 11, 2025

Conversation

pedrxlz
Copy link
Contributor

@pedrxlz pedrxlz commented Jan 28, 2025

Summary by CodeRabbit

  • New Features

    • Enhanced transaction signing process for Substrate blockchain.
    • Added ExtrinsicPayload struct with advanced encoding and decoding capabilities.
    • Introduced new SUBSTRATE variant in ChainOptions for detailed chain configurations.
    • Added new methods for Ethereum and Substrate transaction signing options in the wallet module.
  • Improvements

    • Implemented a more robust transaction signing mechanism.
    • Added support for handling large transaction payloads by conditionally hashing data.
    • Updated support status for multiple Substrate chains, indicating they are now supported.
    • Introduced a utility function for converting hexadecimal strings to byte vectors.
    • Added serde dependency for improved serialization and deserialization capabilities.

Copy link

coderabbitai bot commented Jan 28, 2025

Walkthrough

The pull request introduces significant changes to the Substrate chain implementation in the Kos SDK. Modifications enhance the transaction signing process by adding a new ExtrinsicPayload struct in the models.rs file, encapsulating essential fields for Substrate transactions. The sign_tx method has been updated to utilize this struct, incorporating logic to hash payloads exceeding 256 bytes before signing. Additionally, the ChainOptions enum is expanded to support Substrate-specific configurations, and new functions are added to facilitate transaction creation and signing.

Changes

File Change Summary
packages/kos/src/chains/substrate/mod.rs - Added import for ExtrinsicPayload
- Modified sign_tx method to create and use ExtrinsicPayload
- Implemented payload hashing for large transactions
- Added fn options_from_browser_json(tx: String) -> ChainOptions
packages/kos/src/chains/substrate/models.rs - Added new ExtrinsicPayload struct
- Implemented to_bytes() and encode_with_signature() methods
- Introduced constants: SIGNED_FLAG, TRANSACTION_VERSION, PUBLIC_KEY_TYPE, SIGNATURE_TYPE
- Updated import statements
packages/kos/src/chains/mod.rs - Added SUBSTRATE variant to ChainOptions enum
- Updated supported status for several chains from false to true
- Added InvalidHex variant to ChainError enum
packages/kos-mobile/src/lib.rs - Added Substrate variant to TransactionChainOptions enum
- Introduced fn new_substrate_transaction_options(...) function for creating transaction options
packages/kos-web/src/wallet.rs - Added pub fn new_ethereum_sign_options(chain_id: u32) and pub fn new_substrate_sign_options(...) methods to TransactionChainOptions struct
packages/kos/src/chains/util.rs - Added pub fn hex_string_to_vec(hex: &str) -> Result<Vec<u8>, ChainError> function for hex string conversion
packages/kos/Cargo.toml - Added new dependency: serde = { version = "1.0.215", features = ["derive"] }

Possibly related PRs

  • evm custom network #79: The changes in the main PR are related to the modifications in the TransactionChainOptions enum and the sign_transaction function, which are also addressed in the retrieved PR, indicating a direct connection in handling transaction options for different blockchain types.

Suggested Labels

@domain/sdk, @domain/proto, @domain/types, configuration

Suggested Reviewers

  • klever-patrick
  • gustavocbritto

Poem

🐰 A Substrate Dance of Bytes
Payload leaps, payload springs,
Hashing magic that signing brings!
Bytes transform with cryptic might,
Transactions dance in codec's light!
Rust rabbits sign with pure delight! 🔒

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • JIRA integration encountered authorization issues. Please disconnect and reconnect the integration in the CodeRabbit UI.

🪧 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 generate docstrings to generate docstrings for this PR. (Beta)
  • @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.

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.

@github-actions github-actions bot added the rust label Jan 28, 2025
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

🧹 Nitpick comments (4)
packages/kos/src/chains/substrate/mod.rs (2)

219-220: Check for decoding errors in test.

Although this is test code, consider handling errors or adding an assertion to confirm the decoded data's integrity.


221-227: Add an assertion to verify the signature in your test.

After calling dot.sign_tx, it may be beneficial to assert the resulting transaction’s correctness (e.g., checking tx.signature length or hash) to ensure the signing process behaves as expected.

packages/kos/src/chains/substrate/models.rs (2)

139-154: Remove the underscore prefix for _metadata_hash or make it private.

If _metadata_hash is intended for internal usage only, consider making it a private field. Otherwise, drop the underscore prefix for consistency.


219-271: Add explanatory documentation for encoded flags and fields.

Lines such as encoded.push(0x00) and encoded.push(0x01) might not be self-explanatory. Consider adding doc comments to clarify which parts of the Substrate format these bytes represent.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c488451 and eb3d381.

📒 Files selected for processing (2)
  • packages/kos/src/chains/substrate/mod.rs (4 hunks)
  • packages/kos/src/chains/substrate/models.rs (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: format
🔇 Additional comments (7)
packages/kos/src/chains/substrate/mod.rs (5)

3-3: Import statement looks good.


6-6: Use of blake2b hashing imports is correct.


165-166: Test imports look appropriate.


214-214: Verify usage of an empty derivation path.

When using dot.get_path(0, false), the function might return an empty string, which leads to no derivation path. Confirm that this is intentional in your tests.


217-217: Use of unwrap is acceptable in tests.

packages/kos/src/chains/substrate/models.rs (2)

1-2: Import statements look correct.

Also applies to: 5-5


156-217: Verify potential off-by-one usage in destination.copy_from_slice(&input[1..33]).

You’re discarding byte input[0] for the destination, which might be intentional if that byte encodes a type. Double-check that this is correct in your extrinsic format.

-        destination.copy_from_slice(&input[1..33]);
+        // If the first byte truly is part of the destination, consider:
+        // destination.copy_from_slice(&input[0..32]);
✅ Verification successful

The current implementation is correct

The extra byte being skipped is a type identifier prefix, which is a standard pattern in Substrate's address encoding scheme. The destination field in the struct is defined as [u8; 32], and the implementation correctly extracts these 32 bytes while skipping the type prefix byte.

🏁 Scripts executed

The following scripts were executed for the analysis:

Error: No shell scripts found


Script:

#!/bin/bash
# Search for similar implementations and documentation
echo "=== Searching for similar from_raw implementations ==="
ast-grep --pattern 'fn from_raw($$$) { $$$destination.copy_from_slice($$$)$$$}'

echo -e "\n=== Searching for extrinsic format documentation ==="
rg -i "extrinsic.*format|payload.*format" -A 5 -B 5

echo -e "\n=== Searching for address type handling ==="
rg "address.*type|account.*type" -A 3 -B 3

Length of output: 2340


Script:

#!/bin/bash
# Search for tests and encoding documentation
echo "=== Searching for ExtrinsicPayload tests ==="
rg "test.*ExtrinsicPayload" -A 10 -B 2

echo -e "\n=== Searching for SCALE encoding comments ==="
rg -i "scale.*encoding|encode.*address|address.*encode" -A 3 -B 3

echo -e "\n=== Searching for address prefix comments ==="
rg "prefix.*byte|address.*prefix|account.*prefix" -A 3 -B 3

Length of output: 3533


Script:

#!/bin/bash
# Search for ExtrinsicPayload struct definition
echo "=== Searching for ExtrinsicPayload struct definition ==="
rg "struct ExtrinsicPayload" -A 15 -B 2

echo -e "\n=== Searching for destination field type documentation ==="
ast-grep --pattern 'struct ExtrinsicPayload {
  $$$
  destination: $_,
  $$$
}'

Length of output: 1562

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

♻️ Duplicate comments (1)
packages/kos/src/chains/substrate/mod.rs (1)

112-132: ⚠️ Potential issue

Improve error handling in the signing process.

The implementation has several areas that need attention:

  1. Unsafe public key conversion using unwrap()
  2. Potential memory leak of private key
  3. Missing documentation for the signing logic

Apply these improvements:

 let signature = {
     let full_unsigned_payload_scale_bytes = tx.raw_data.clone();
+    // Clean up the cloned private key after signing
+    let signature = {
+        let result = if full_unsigned_payload_scale_bytes.len() > 256 {
+            self.sign_raw(
+                private_key.clone(),
+                blake2b_digest(&full_unsigned_payload_scale_bytes).to_vec(),
+            )
+        } else {
+            self.sign_raw(private_key.clone(), full_unsigned_payload_scale_bytes)
+        };
+        result
+    }?;

-    let public_key: [u8; 32] = self.get_pbk(private_key)?.try_into().unwrap();
+    let pbk_vec = self.get_pbk(private_key)?;
+    let public_key: [u8; 32] = pbk_vec
+        .try_into()
+        .map_err(|_| ChainError::Custom("Invalid public key length".to_string()))?;
🧹 Nitpick comments (1)
packages/kos/src/chains/substrate/mod.rs (1)

219-231: Add assertions for transaction validity.

The test creates a transaction from base64-encoded data but lacks assertions about the transaction's validity before signing.

Add validation:

 let raw_data = simple_base64_decode("BQMADCRBuM7b/Hou3AlouaU1gZlp0+ngmYaAurtYJyh/wHCRAXUDYAAA/E0PABoAAACRsXG7FY4tOEj6I6nxwlGC+44gMTssHrSSGdp6cM6Qw36KXLq5dgOoEvZRpzirvfO3HDN6fM3bEwtF1XTUSlrGAA==").unwrap();

+// Verify raw data length and content
+assert!(!raw_data.is_empty(), "Raw data should not be empty");
+let payload = ExtrinsicPayload::from_raw(raw_data.clone()).expect("Should be valid payload");
+
 let tx = Transaction {
     raw_data,
     signature: Vec::new(),
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb3d381 and 0c40d1c.

📒 Files selected for processing (2)
  • packages/kos/src/chains/substrate/mod.rs (4 hunks)
  • packages/kos/src/chains/substrate/models.rs (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: format

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

🧹 Nitpick comments (2)
packages/kos/src/chains/substrate/mod.rs (2)

112-126: Well-implemented payload size optimization.

The implementation intelligently handles large payloads by hashing them when they exceed 256 bytes, which is a good practice for maintaining consistent performance and security.

However, consider adding a constant for the payload size threshold:

+const MAX_PAYLOAD_SIZE: usize = 256;
+
 // If payload is longer than 256 bytes, we hash it and sign the hash instead:
-if full_unsigned_payload_scale_bytes.len() > 256 {
+if full_unsigned_payload_scale_bytes.len() > MAX_PAYLOAD_SIZE {

112-134: Consider extracting payload size handling logic.

The current implementation of payload size handling is embedded within the sign_tx method. Consider extracting this logic into a separate method for better maintainability and reusability:

+impl Substrate {
+    fn prepare_payload_for_signing(&self, payload: Vec<u8>) -> Vec<u8> {
+        const MAX_PAYLOAD_SIZE: usize = 256;
+        if payload.len() > MAX_PAYLOAD_SIZE {
+            blake2b_digest(&payload).to_vec()
+        } else {
+            payload
+        }
+    }
+}

 fn sign_tx(&self, private_key: Vec<u8>, mut tx: Transaction) -> Result<Transaction, ChainError> {
     let extrinsic = ExtrinsicPayload::from_raw(tx.raw_data.clone())?;
     let signature = {
         let full_unsigned_payload_scale_bytes = tx.raw_data.clone();
-        if full_unsigned_payload_scale_bytes.len() > 256 {
-            self.sign_raw(
-                private_key.clone(),
-                blake2b_digest(&full_unsigned_payload_scale_bytes).to_vec(),
-            )?
-        } else {
-            self.sign_raw(private_key.clone(), full_unsigned_payload_scale_bytes)?
-        }
+        let prepared_payload = self.prepare_payload_for_signing(full_unsigned_payload_scale_bytes);
+        self.sign_raw(private_key.clone(), prepared_payload)?
     };
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0c40d1c and 2308644.

📒 Files selected for processing (1)
  • packages/kos/src/chains/substrate/mod.rs (4 hunks)
🔇 Additional comments (3)
packages/kos/src/chains/substrate/mod.rs (3)

3-3: LGTM! Required imports are properly added.

The new imports for ExtrinsicPayload and blake2b_digest support the enhanced transaction signing functionality.

Also applies to: 6-6


128-132: LGTM! Proper error handling implemented for public key conversion.

The implementation now safely handles public key conversion errors, addressing the concerns raised in previous reviews.


222-234: Enhance test coverage with additional assertions.

While the current test verifies basic functionality, consider adding:

  1. Verification of the signature's validity
  2. Test cases for payloads > 256 bytes to verify the hashing logic
  3. Error cases (e.g., invalid raw data)

Run this script to check existing test coverage:

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

🧹 Nitpick comments (1)
packages/kos/src/chains/substrate/mod.rs (1)

285-317: Document test data and add more test cases.

The test uses hardcoded test data without documentation of its source or expected values.

Consider:

  1. Adding comments explaining the source and validity of test data
  2. Adding test cases for error conditions (invalid options, large payloads)
  3. Adding test cases for different Substrate chains
 #[test]
 fn sign_tx() {
+    // Test data from Kusama transaction:
+    // https://kusama.subscan.io/extrinsic/[TODO: add reference transaction]
     let dot = super::Substrate::new(27, 2, "Kusama", "KSM");
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4c17597 and fee36c7.

📒 Files selected for processing (3)
  • packages/kos/src/chains/mod.rs (5 hunks)
  • packages/kos/src/chains/substrate/mod.rs (4 hunks)
  • packages/kos/src/chains/substrate/models.rs (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: format
🔇 Additional comments (6)
packages/kos/src/chains/mod.rs (2)

266-275: LGTM! The SUBSTRATE variant is well-structured.

The new SUBSTRATE variant in ChainOptions includes all necessary fields for Substrate transaction signing with appropriate types.


382-382: LGTM! Substrate chains are now supported.

The changes enable support for multiple Substrate chains (DOT, KSM, REEF, SDN, ASTR, CFG, KILT, ALTAIR, and AVAIL) by setting their supported field to true.

Also applies to: 389-389, 403-403, 410-410, 417-417, 424-424, 438-438, 445-445, 585-585

packages/kos/src/chains/substrate/models.rs (3)

6-9: LGTM! Constants are well-defined.

The constants are appropriately defined with correct values for Substrate transaction encoding.


161-188: LGTM! The encoding logic is correct and well-documented.

The method properly implements the Substrate transaction format with clear documentation.


144-158: 🛠️ Refactor suggestion

Consider improving field types and documentation.

The struct has some potential issues:

  1. The metadata_hash field is u8 which seems insufficient for a hash value.
  2. The mode field lacks documentation explaining its purpose and valid values.

Consider:

  1. Using [u8; 32] or Vec<u8> for metadata_hash.
  2. Adding documentation for the mode field.
 pub struct ExtrinsicPayload {
     pub call: Vec<u8>,
     pub era: Vec<u8>,
     pub nonce: u32,
     pub tip: u8,
+    /// Mode of the extrinsic. Valid values: [TODO: document valid values]
     pub mode: u8,
     pub spec_version: u32,
     pub transaction_version: u32,
     pub genesis_hash: [u8; 32],
     pub block_hash: [u8; 32],
-    pub metadata_hash: u8,
+    pub metadata_hash: [u8; 32],
 }

Likely invalid or redundant comment.

packages/kos/src/chains/substrate/mod.rs (1)

112-174: LGTM! The sign_tx implementation is robust and secure.

The method includes several good practices:

  1. Proper error handling for missing or invalid options
  2. Secure handling of large payloads by hashing them before signing
  3. Safe conversion of hash values with proper error handling

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

🔭 Outside diff range comments (1)
packages/kos/src/chains/mod.rs (1)

Line range hint 759-761: Implement custom Substrate chain support.

The get_chain_by_params function returns None for CustomSubstrate variant, which might limit the ability to work with custom Substrate chains. Consider implementing this functionality similar to how custom EVM chains are handled.

         CustomChainType::CustomEth(chaincode) => Some(Box::new(eth::ETH::new_eth_based(
             0,
             chaincode,
             format!("ETH {}", chaincode).as_str(),
             format!("Eth Based {}", chaincode).as_str(),
         ))),
-        CustomChainType::CustomSubstrate(_) => None,
+        CustomChainType::CustomSubstrate(chaincode) => Some(Box::new(substrate::Substrate::new(
+            0,
+            chaincode,
+            format!("Substrate {}", chaincode).as_str(),
+            format!("Substrate Chain {}", chaincode).as_str(),
+        ))),
         CustomChainType::CustomCosmos(_) => None,
♻️ Duplicate comments (1)
packages/kos-mobile/src/lib.rs (1)

76-102: 🛠️ Refactor suggestion

Improve error handling in substrate transaction options.

Similar to the web version, this function uses unwrap_or_default() which silently handles errors.

-fn new_substrate_transaction_options(
+fn new_substrate_transaction_options(
     call: String,
     era: String,
     nonce: u32,
     tip: u8,
     block_hash: String,
     genesis_hash: String,
     spec_version: u32,
     transaction_version: u32,
-) -> TransactionChainOptions {
-    let call = hex_string_to_vec(call.as_str()).unwrap_or_default();
-    let era = hex_string_to_vec(era.as_str()).unwrap_or_default();
-    let block_hash = hex_string_to_vec(block_hash.as_str()).unwrap_or_default();
-    let genesis_hash = hex_string_to_vec(genesis_hash.as_str()).unwrap_or_default();
+) -> Result<TransactionChainOptions, KOSError> {
+    let call = hex_string_to_vec(call.as_str())
+        .map_err(|e| KOSError::KOSDelegate(format!("Invalid call hex: {}", e)))?;
+    let era = hex_string_to_vec(era.as_str())
+        .map_err(|e| KOSError::KOSDelegate(format!("Invalid era hex: {}", e)))?;
+    let block_hash = hex_string_to_vec(block_hash.as_str())
+        .map_err(|e| KOSError::KOSDelegate(format!("Invalid block hash hex: {}", e)))?;
+    let genesis_hash = hex_string_to_vec(genesis_hash.as_str())
+        .map_err(|e| KOSError::KOSDelegate(format!("Invalid genesis hash hex: {}", e)))?;

-    TransactionChainOptions::Substrate {
+    Ok(TransactionChainOptions::Substrate {
         call,
         era,
         nonce,
         tip,
         block_hash,
         genesis_hash,
         spec_version,
         transaction_version,
-    }
+    })
}
🧹 Nitpick comments (3)
packages/kos/src/chains/util.rs (1)

17-20: Add input validation for hex string.

The function should validate that the input string contains valid hexadecimal characters after trimming the "0x" prefix.

 pub fn hex_string_to_vec(hex: &str) -> Result<Vec<u8>, ChainError> {
     let hex = hex.trim_start_matches("0x");
+    if !hex.chars().all(|c| c.is_ascii_hexdigit()) {
+        return Err(ChainError::InvalidHex);
+    }
     hex::decode(hex).map_err(|_| ChainError::InvalidHex)
 }
packages/kos/src/chains/substrate/mod.rs (1)

253-294: Enhance test coverage with additional assertions.

While the test verifies signature and raw data lengths, it could be improved by adding more assertions.

Add these assertions to verify the transaction content:

 assert_eq!(signed_tx.signature.len(), 65);
 assert_eq!(signed_tx.raw_data.len(), 143);
+// Verify signature format
+assert_eq!(signed_tx.signature[0], 1u8, "Signature version should be 1");
+assert!(signed_tx.signature[1..].len() == 64, "Signature payload should be 64 bytes");
+
+// Verify the transaction was properly encoded
+assert!(signed_tx.raw_data[0] < 128, "First byte should be a valid version");
packages/kos/src/chains/mod.rs (1)

271-280: Consider using a larger type for the tip field.

The tip field is currently defined as u8, which limits the maximum tip to 255. Consider using u128 to align with Substrate's Balance type for flexibility with larger tips.

     SUBSTRATE {
         call: Vec<u8>,
         era: Vec<u8>,
         nonce: u32,
-        tip: u8,
+        tip: u128,
         block_hash: Vec<u8>,
         genesis_hash: Vec<u8>,
         spec_version: u32,
         transaction_version: u32,
     },

Add documentation for the SUBSTRATE variant.

Consider adding documentation to explain the purpose and requirements of each field, especially for fields like era and spec_version that have specific formats or constraints.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fee36c7 and 919f3d6.

📒 Files selected for processing (5)
  • packages/kos-mobile/src/lib.rs (3 hunks)
  • packages/kos-web/src/wallet.rs (2 hunks)
  • packages/kos/src/chains/mod.rs (9 hunks)
  • packages/kos/src/chains/substrate/mod.rs (4 hunks)
  • packages/kos/src/chains/util.rs (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: format
🔇 Additional comments (5)
packages/kos-web/src/wallet.rs (1)

64-69: LGTM!

The function is simple and correctly creates EVM chain options with the provided chain ID.

packages/kos-mobile/src/lib.rs (1)

64-74: LGTM!

The Substrate variant in the TransactionChainOptions enum is well-structured with all necessary fields.

packages/kos/src/chains/substrate/mod.rs (1)

112-174: LGTM! Well-structured implementation with proper error handling.

The implementation correctly:

  • Extracts and validates transaction options
  • Handles payload length optimization (hashing if > 256 bytes)
  • Uses proper error handling with Result type
packages/kos/src/chains/mod.rs (2)

61-61: LGTM: Error handling for hex validation.

The new InvalidHex error variant is well-integrated into the error handling system with appropriate Display implementation and error code.

Also applies to: 127-129, 220-220


387-387: Verify the base parameter values for Substrate chains.

Some chains share the same base parameter value:

  • SDN and ASTR both use base=5
  • REEF and AVAIL both use base=42

Please verify that these values are correct, as sharing the same base parameter might cause issues with chain-specific functionality.

Also applies to: 394-394, 408-408, 415-415, 422-422, 429-429, 443-443, 450-450, 590-590

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

🧹 Nitpick comments (1)
packages/kos-web/src/wallet.rs (1)

71-104: Consider using a builder pattern or parameter struct.

The method takes 8 parameters which makes it harder to maintain and use. Consider introducing a SubstrateSignOptions struct to encapsulate these parameters:

#[wasm_bindgen]
pub struct SubstrateSignOptions {
    call: String,
    era: String,
    nonce: u32,
    tip: u8,
    block_hash: String,
    genesis_hash: String,
    spec_version: u32,
    transaction_version: u32,
}

#[wasm_bindgen]
impl TransactionChainOptions {
    #[wasm_bindgen(js_name = "newSubstrateSignOptions")]
    pub fn new_substrate_sign_options(options: SubstrateSignOptions) -> Result<TransactionChainOptions, Error> {
        let call = hex_string_to_vec(options.call.as_str())
            .map_err(|e| Error::WalletManager(format!("Invalid call hex: {}", e)))?;
        // ... rest of the conversions ...
        
        Ok(TransactionChainOptions {
            data: ChainOptions::SUBSTRATE {
                call,
                era,
                nonce: options.nonce,
                tip: options.tip,
                block_hash,
                genesis_hash,
                spec_version: options.spec_version,
                transaction_version: options.transaction_version,
            },
        })
    }
}

This would:

  1. Improve code readability
  2. Make parameter management easier
  3. Allow for future extensions without breaking changes
  4. Provide a more ergonomic API for JavaScript consumers
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 919f3d6 and 36ee7c8.

📒 Files selected for processing (1)
  • packages/kos-web/src/wallet.rs (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: format
🔇 Additional comments (3)
packages/kos-web/src/wallet.rs (3)

9-9: LGTM!

The import is correctly placed and the utility function is used appropriately in the code.


64-69: LGTM!

The implementation is clean, focused, and correctly handles EVM chain requirements. The WASM binding ensures proper JavaScript interoperability.


71-104: LGTM! Proper error handling implemented.

The implementation correctly handles hex string conversions with appropriate error messages, following the previous review suggestions.

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 (2)
packages/kos/src/chains/substrate/models.rs (1)

6-9: Document the constants and their purpose.

The constants are used in the transaction encoding process but lack documentation explaining their purpose and values.

+/// Flag indicating that the transaction is signed (0b1000_0000)
 const SIGNED_FLAG: u8 = 0b1000_0000;
+/// Version of the transaction format (4)
 const TRANSACTION_VERSION: u8 = 4;
+/// Type identifier for public key in transaction encoding (0x00)
 const PUBLIC_KEY_TYPE: u8 = 0x00;
+/// Type identifier for signature in transaction encoding (0x01)
 const SIGNATURE_TYPE: u8 = 0x01;
packages/kos-mobile/src/lib.rs (1)

64-74: Document the enum variant fields.

The Substrate variant lacks documentation explaining its fields and their purposes.

+    /// Options specific to Substrate chain transactions
     Substrate {
+        /// The encoded call data
         call: Vec<u8>,
+        /// The era phase and period for transaction mortality
         era: Vec<u8>,
+        /// The transaction nonce
         nonce: u32,
+        /// The transaction tip for block producers
         tip: u8,
+        /// The hash of the block this transaction is valid for
         block_hash: Vec<u8>,
+        /// The hash of the genesis block of the chain
         genesis_hash: Vec<u8>,
+        /// The runtime specification version
         spec_version: u32,
+        /// The transaction format version
         transaction_version: u32,
+        /// Optional application-specific identifier (used in AVAIL chain)
         app_id: Option<u32>,
     },
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 36ee7c8 and b1f33de.

📒 Files selected for processing (5)
  • packages/kos-mobile/src/lib.rs (3 hunks)
  • packages/kos-web/src/wallet.rs (2 hunks)
  • packages/kos/src/chains/mod.rs (9 hunks)
  • packages/kos/src/chains/substrate/mod.rs (4 hunks)
  • packages/kos/src/chains/substrate/models.rs (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: format
🔇 Additional comments (8)
packages/kos-web/src/wallet.rs (1)

71-106: ⚠️ Potential issue

Improve error handling in hex string conversions.

The method uses unwrap_or_default() which silently handles errors by returning empty vectors. This could mask issues and make debugging difficult.

-    let call = hex_string_to_vec(call.as_str()).unwrap_or_default();
-    let era = hex_string_to_vec(era.as_str()).unwrap_or_default();
-    let block_hash = hex_string_to_vec(block_hash.as_str()).unwrap_or_default();
-    let genesis_hash = hex_string_to_vec(genesis_hash.as_str()).unwrap_or_default();
+    let call = hex_string_to_vec(call.as_str())
+        .map_err(|e| Error::WalletManager(format!("Invalid call hex: {}", e)))?;
+    let era = hex_string_to_vec(era.as_str())
+        .map_err(|e| Error::WalletManager(format!("Invalid era hex: {}", e)))?;
+    let block_hash = hex_string_to_vec(block_hash.as_str())
+        .map_err(|e| Error::WalletManager(format!("Invalid block hash hex: {}", e)))?;
+    let genesis_hash = hex_string_to_vec(genesis_hash.as_str())
+        .map_err(|e| Error::WalletManager(format!("Invalid genesis hash hex: {}", e)))?;

Likely invalid or redundant comment.

packages/kos/src/chains/substrate/mod.rs (4)

112-176: LGTM! The transaction signing implementation is robust and secure.

The function has several good practices:

  • Proper error handling for options extraction and array conversions
  • Secure handling of large payloads by hashing them before signing
  • Clear separation of concerns between signature generation and transaction finalization

254-298: LGTM! The test provides good coverage with real-world data.

The test effectively validates:

  • Transaction signing with real Polkadot transaction data
  • Signature and raw data length assertions

301-345: LGTM! The test provides good coverage for Kusama chain.

The test effectively validates transaction signing with real Kusama transaction data.


348-396: LGTM! The test provides good coverage for Avail chain.

The test effectively validates:

  • Transaction signing with real Avail transaction data
  • Hex string parsing for nonce, spec_version, and transaction_version
packages/kos/src/chains/mod.rs (3)

61-61: LGTM! The error variant is properly integrated.

The InvalidHex error variant is well-integrated with proper error message and unique error code.

Also applies to: 127-129, 220-220


271-281: LGTM! The SUBSTRATE variant is well-structured.

The variant includes all necessary fields for Substrate transactions with appropriate types.


388-388: LGTM! Chain support is properly enabled for multiple Substrate chains.

Support is consistently enabled for DOT, KSM, REEF, SDN, ASTR, CFG, KILT, ALTAIR, and AVAIL chains.

Also applies to: 395-395, 409-409, 416-416, 423-423, 430-430, 444-444, 451-451, 590-591

brunocampos-ssa
brunocampos-ssa previously approved these changes Feb 4, 2025
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

🧹 Nitpick comments (5)
packages/kos/src/chains/substrate/models.rs (3)

6-9: Document the constants.

Add documentation to explain the purpose and usage of each constant in the transaction encoding process.

+/// Flag indicating a signed transaction (0b1000_0000).
 const SIGNED_FLAG: u8 = 0b1000_0000;
+/// Version of the transaction format (4).
 const TRANSACTION_VERSION: u8 = 4;
+/// Type identifier for public key (0x00).
 const PUBLIC_KEY_TYPE: u8 = 0x00;
+/// Type identifier for signature (0x01).
 const SIGNATURE_TYPE: u8 = 0x01;

144-159: Consider using a larger type for the tip field.

The tip field is currently u8, which limits the maximum tip value to 255. Consider using a larger type like u128 or the chain's native Balance type to allow for more meaningful transaction prioritization.

 pub struct ExtrinsicPayload {
     pub call: Vec<u8>,
     pub era: Vec<u8>,
     pub nonce: u32,
-    pub tip: u8,
+    pub tip: u128,
     pub mode: u8,
     pub spec_version: u32,
     pub transaction_version: u32,
     pub genesis_hash: [u8; 32],
     pub block_hash: [u8; 32],
     pub metadata_hash: u8,
     pub app_id: Option<u32>,
 }

164-189: Optimize memory allocation and cloning.

The method could be optimized by:

  1. Pre-calculating and pre-allocating the required capacity
  2. Avoiding unnecessary cloning
 pub fn to_bytes(&self) -> Vec<u8> {
-    let mut encoded = Vec::new();
+    // Calculate total length for better memory allocation
+    let total_length = self.call.len() +
+        self.era.len() +
+        Compact(self.nonce).encoded_size() +
+        Compact(self.tip).encoded_size() +
+        if self.app_id.is_some() {
+            Compact(self.app_id.unwrap()).encoded_size()
+        } else {
+            1 // mode
+        } +
+        self.spec_version.encoded_size() +
+        self.transaction_version.encoded_size() +
+        32 + // genesis_hash
+        32 + // block_hash
+        if self.app_id.is_none() { 1 } else { 0 }; // metadata_hash
+
+    let mut encoded = Vec::with_capacity(total_length);
-    encoded.extend(self.call.clone());
+    encoded.extend_from_slice(&self.call);
-    encoded.extend(&self.era.clone());
+    encoded.extend_from_slice(&self.era);
     encoded.extend(Compact(self.nonce).encode());
     encoded.extend(Compact(self.tip).encode());
packages/kos-web/src/wallet.rs (1)

72-107: Consider using the builder pattern.

The method has many parameters which makes it harder to use and maintain. Consider implementing a builder pattern to improve readability and maintainability.

#[wasm_bindgen]
pub struct SubstrateSignOptionsBuilder {
    call: Option<String>,
    era: Option<String>,
    nonce: Option<u32>,
    tip: Option<u8>,
    block_hash: Option<String>,
    genesis_hash: Option<String>,
    spec_version: Option<u32>,
    transaction_version: Option<u32>,
    app_id: Option<u32>,
}

#[wasm_bindgen]
impl SubstrateSignOptionsBuilder {
    #[wasm_bindgen(constructor)]
    pub fn new() -> Self {
        Self {
            call: None,
            era: None,
            nonce: None,
            tip: None,
            block_hash: None,
            genesis_hash: None,
            spec_version: None,
            transaction_version: None,
            app_id: None,
        }
    }

    pub fn call(mut self, call: String) -> Self {
        self.call = Some(call);
        self
    }

    // Add other setters...

    pub fn build(self) -> Result<TransactionChainOptions, Error> {
        let call = hex_string_to_vec(self.call.ok_or(Error::WalletManager("call is required".to_string()))?.as_str())
            .map_err(|e| Error::WalletManager(format!("Invalid call hex: {}", e)))?;
        // Convert other fields...
        Ok(TransactionChainOptions {
            data: ChainOptions::SUBSTRATE {
                call,
                // Other fields...
            },
        })
    }
}
packages/kos/src/chains/substrate/mod.rs (1)

317-519: Add negative test cases.

The test suite is comprehensive for positive cases but lacks tests for error conditions. Consider adding tests for:

  • Invalid hex strings
  • Missing or malformed fields in browser JSON
  • Invalid signature lengths
  • Invalid public key lengths
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ce71697 and 337248f.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • packages/kos-web/src/wallet.rs (2 hunks)
  • packages/kos/Cargo.toml (1 hunks)
  • packages/kos/src/chains/substrate/mod.rs (4 hunks)
  • packages/kos/src/chains/substrate/models.rs (2 hunks)
  • packages/kos/src/chains/util.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/kos/src/chains/util.rs
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: format
🔇 Additional comments (4)
packages/kos/src/chains/substrate/models.rs (1)

191-224: LGTM!

The implementation is well-structured with:

  • Clear documentation
  • Proper capacity pre-allocation
  • Efficient use of extend_from_slice
packages/kos-web/src/wallet.rs (1)

65-70: LGTM!

The implementation is simple and correct.

packages/kos/src/chains/substrate/mod.rs (1)

107-178: LGTM!

The implementation is well-structured with:

  • Proper error handling
  • Clear pattern matching
  • Efficient payload handling for large transactions
packages/kos/Cargo.toml (1)

64-64: LGTM!

The serde dependency is correctly specified with:

  • Pinned version
  • Derive feature enabled

@pedrxlz pedrxlz merged commit 5db8f43 into develop Feb 11, 2025
4 checks passed
@pedrxlz pedrxlz deleted the fix/substrate-sign-tx branch February 11, 2025 22:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants