-
Notifications
You must be signed in to change notification settings - Fork 19
BIP-322: Bitcoin Generic Message Signing #120
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
base: main
Are you sure you want to change the base?
Conversation
WalkthroughAdd a new workspace crate defuse-bip322 implementing BIP-322 message verification (address parsing, minimal Bitcoin types, tx builders, hashing, signature parsing/recovery, verification), wire it into core payloads and tests, extend near-utils digest API, update manifests and .gitignore, and add Python validation scripts and test stubs. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Core as core::MultiPayload
participant B322 as defuse_bip322::SignedBip322Payload
participant Hasher as Bip322MessageHasher
participant Tx as bip322::transaction
participant Sig as Bip322Signature
participant Ver as bip322::verification
Client->>Core: verify()
Core->>B322: delegate verify()
B322->>Hasher: compute_bip322_message_hash(message)
Hasher-->>B322: message_hash
B322->>Tx: create_to_spend(address, message_hash)
Tx-->>B322: to_spend
B322->>Tx: create_to_sign(to_spend)
Tx-->>B322: to_sign
B322->>Sig: extract_public_key(&message_hash, &address)
Sig->>Ver: validate_pubkey_matches_address(pubkey, address)
Ver-->>Sig: bool
Sig-->>B322: Option<PublicKey>
B322-->>Core: verification result
Core-->>Client: result
sequenceDiagram
participant App
participant Core as core::MultiPayload
App->>Core: hash()
alt variant == Bip322
Core-->>App: SignedBip322Payload.hash()
else
Core-->>App: other variant.hash()
end
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Suggested reviewers
Poem
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. 📜 Recent review detailsConfiguration used: CodeRabbit UI 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ 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). (8)
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (6)
core/Cargo.toml (1)
10-10: New crate wired into the core crate
Non-optionaldefuse-bip322means every downstream consumer now builds this crate. If build size or compile time is a concern, consider gating it behind a feature (similar to other*_connectcrates). Otherwise LGTM.bip340/src/lib.rs (1)
1-4: Crate root minimal but complete
Re-exporting all items from thedoubleandtaggedmodules is fine for a small utility crate. If the public surface grows, consider explicit exports to avoid namespace pollution.core/src/payload/multi.rs (1)
56-57: Add documentation for BIP-322 variant.The TODO comment indicates missing documentation for the new BIP-322 variant. This should be completed to maintain consistency with other well-documented variants.
- // TODO: docs - Bip322(SignedBip322Payload), + /// BIP-322: The standard for Bitcoin generic message signing. + /// For more details, refer to [BIP-322](https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki). + Bip322(SignedBip322Payload),Would you like me to open an issue to track this documentation task?
bip322/src/der.rs (2)
49-53: Add validation for canonical DER length encoding.The current implementation doesn't validate against non-canonical length encodings. According to DER rules, the length should be encoded in the shortest possible form. For example, a length of 127 should use short form (0x7F), not long form (0x81 0x7F).
Consider adding validation to reject non-canonical encodings:
let mut length = 0usize; for &byte in bytes.iter().take(len_bytes + 1).skip(1) { length = (length << 8) | usize::from(byte); } + +// Validate canonical encoding - no leading zeros except for single zero byte +if len_bytes > 1 && bytes[1] == 0 { + return None; // Non-canonical: leading zero +} + +// Validate minimal encoding - could have used short form +if len_bytes == 1 && length <= 127 { + return None; // Non-canonical: should use short form +}
245-273: Consider adding test cases for edge conditions.The test suite is comprehensive but could benefit from additional edge cases:
#[test] fn test_parse_der_ecdsa_signature_trailing_data() { // Signature with extra bytes after valid content let invalid_der = vec![ 0x30, 0x06, // SEQUENCE, length 6 0x02, 0x01, 0x01, // INTEGER, length 1, value 0x01 (R) 0x02, 0x01, 0x02, // INTEGER, length 1, value 0x02 (S) 0xFF, // Extra byte ]; assert_eq!(parse_der_ecdsa_signature(&invalid_der), None); } #[test] fn test_parse_der_length_non_canonical() { // Length 127 encoded in long form (should use short form) let non_canonical = vec![0x81, 0x7F]; // Currently passes but should fail with canonical validation assert_eq!(parse_der_length(&non_canonical), Some((127, 2))); }bip322/README.md (1)
139-154: Example code references undefined function.The example uses
witness_from_signature_data()which is not defined in the codebase. Consider providing a complete example or clearly marking this as pseudo-code.// Create a BIP-322 payload +// Note: This is a simplified example. In practice, you would parse the witness +// data from the actual BIP-322 signature format. let payload = SignedBip322Payload { address: "bc1q9vza2e8x573nczrlzms0wvx3gsqjx7vavgkx0l".parse()?, message: "Hello Bitcoin!".to_string(), - signature: witness_from_signature_data(signature_bytes), + signature: Witness::from_stack(vec![signature_bytes, public_key_bytes]), };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (19)
.gitignore(1 hunks)Cargo.toml(2 hunks)bip322/Cargo.toml(1 hunks)bip322/README.md(1 hunks)bip322/src/bitcoin_minimal.rs(1 hunks)bip322/src/der.rs(1 hunks)bip322/src/error.rs(1 hunks)bip322/tests/integration_test.rs(1 hunks)bip340/Cargo.toml(1 hunks)bip340/src/double.rs(1 hunks)bip340/src/lib.rs(1 hunks)bip340/src/tagged.rs(1 hunks)core/Cargo.toml(2 hunks)core/src/payload/bip322.rs(1 hunks)core/src/payload/mod.rs(1 hunks)core/src/payload/multi.rs(5 hunks)tests/Cargo.toml(1 hunks)tests/src/tests/defuse/mod.rs(2 hunks)tests/src/utils/crypto.rs(3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
bip340/src/lib.rs (1)
bip340/src/tagged.rs (2)
tagged(5-5)tagged(9-12)
core/src/payload/bip322.rs (2)
core/src/payload/mod.rs (2)
extract_defuse_payload(51-51)extract_defuse_payload(58-60)core/src/payload/multi.rs (1)
extract_defuse_payload(105-116)
bip322/src/error.rs (1)
bip322/src/bitcoin_minimal.rs (1)
fmt(594-602)
⏰ 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). (8)
- GitHub Check: Build
- GitHub Check: Shared security analysis / Static Analysis (intents-poa-factory, contract, near, poa-factory, 1, ., 1.18, false, --filter-pa...
- GitHub Check: Shared security analysis / Static Analysis (intents-poa-token, contract, near, poa-token, 1, ., 1.18, false, --filter-paths ...
- GitHub Check: Shared security analysis / Static Analysis (intents-contract, contract, near, defuse, 1, ., 1.18, false, --filter-paths "nod...
- GitHub Check: Shared / Static Analysis (intents-poa-token, contract, near, poa-token, 1, ., 1.18, false, --filter-paths ...
- GitHub Check: Shared / Static Analysis (intents-contract, contract, near, defuse, 1, ., 1.18, false, --filter-paths "nod...
- GitHub Check: Shared / Static Analysis (intents-poa-factory, contract, near, poa-factory, 1, ., 1.18, false, --filter-pa...
- GitHub Check: Security Audit - report
🔇 Additional comments (32)
.gitignore (1)
3-3: Good addition to ignore IDE metadata
Adding.ideahelps keep JetBrains project files out of version control. No concerns here.tests/Cargo.toml (1)
13-13: Dependency name looks correct, but double-check feature flags
defuse-bip322is added with default features. If the crate exposes heavy optional features (e.g.crypto,abi) that you don't need in tests, considerdefault-features = falseto trim build time.core/src/payload/mod.rs (1)
1-1: Module registered—verify public re-exports are in place
pub mod bip322;compiles only ifcore/src/payload/bip322.rs(or amod.rssubdir) exists and re-exports the necessary symbols. Just a reminder to ensure the file is part of the commit.core/Cargo.toml (1)
40-40: Consistent ABI feature wiring
"defuse-bip322/abi"correctly added to theabifeature set. All good.Cargo.toml (1)
6-7: LGTM! Workspace configuration is properly structured.The new
bip322andbip340workspace members are correctly added with consistent naming and proper path references in the dependencies section.Also applies to: 45-46
tests/src/tests/defuse/mod.rs (2)
151-151: LGTM! New signing standard variant properly added.The
Bip322variant is correctly added to theSigningStandardenum.
129-140: sign_bip322 Implementation ConfirmedI located the
sign_bip322implementation intests/src/utils/crypto.rs(around lines 71–74). It returns a dummySignedBip322Payloadfor testing—consistent with how other signing methods are stubbed in tests. No further changes are needed.bip340/Cargo.toml (1)
1-18: LGTM! Well-structured crate manifest.The manifest properly uses workspace-level configuration and includes appropriate dependencies. The minimal dependency footprint with
digestfor the main functionality andsha2/testing tools only for development is well-designed.core/src/payload/bip322.rs (1)
6-17: LGTM! Clean and consistent payload extraction implementation.The implementation correctly follows the established pattern for payload extraction, parsing the JSON message field with proper error handling. The similarity to ERC-191 (as noted in the comment) ensures consistency across the codebase.
bip340/src/tagged.rs (2)
3-13: LGTM! Correct BIP-340 tagged hash implementation.The implementation properly follows the BIP-340 specification by computing the tag digest once and then feeding it twice to initialize a new digest instance. The generic approach using the
Digesttrait provides good flexibility.
22-32: LGTM! Comprehensive test validates the implementation.The test correctly verifies that the
taggedmethod produces the same result as manually chaining the tag digest twice, ensuring the implementation matches the BIP-340 specification.bip322/Cargo.toml (2)
20-20: Good architectural decision documented.The comment about using NEAR SDK host functions exclusively for cryptographic operations is an important architectural constraint that ensures compatibility with the NEAR runtime environment.
17-18: All external dependencies are up-to-date and free of known vulnerabilitiesThe
bs58 = "0.5"requirement covers the latest 0.5.1 release, andbech32 = "0.11"matches the current 0.11.0 version. A query against the Rust security advisories shows no reported issues for either crate.• No changes needed in bip322/Cargo.toml (lines 17–18).
tests/src/utils/crypto.rs (3)
7-8: LGTM: Clean imports for BIP-322 integration.The imports are well-organized and bring in the necessary BIP-322 types for the test utility implementation.
17-17: LGTM: Appropriate signature for BIP-322 test method.The method signature taking a
Stringmessage and returningSignedBip322Payloadis appropriate for BIP-322's message-based signing approach.
71-91: Well-implemented dummy BIP-322 signer for testing.The implementation correctly creates a dummy BIP-322 signature structure with:
- Proper P2WPKH address format
- Hardcoded but valid pubkey hash for consistency
- Empty witness (appropriate for testing)
- Clear documentation that this is for testing only
The approach is sound for test utilities where actual cryptographic signing isn't required.
core/src/payload/multi.rs (4)
1-1: LGTM: Proper import for BIP-322 integration.The import of
SignedBip322Payloadis correctly placed and necessary for the new enum variant.
75-75: LGTM: Consistent trait implementation for BIP-322.The
Payloadtrait implementation correctly delegates thehash()method to the innerSignedBip322Payload, maintaining consistency with other variants.
93-93: LGTM: Correct public key type mapping for Bitcoin.The
SignedPayloadtrait implementation correctly maps BIP-322 verification results toPublicKey::Secp256k1, which is appropriate for Bitcoin's cryptographic scheme.
114-114: LGTM: Complete trait implementation coverage.The
ExtractDefusePayloadtrait implementation properly handles the BIP-322 variant, ensuring consistent behavior across all payload extraction scenarios.bip340/src/double.rs (3)
1-4: LGTM: Clean generic double hashing abstraction.The
Double<D>struct provides a clean generic wrapper for double hashing functionality. The implementation approach using composition is sound.
47-49: LGTM: Good use of known test vectors.The test validates the double SHA-256 implementation against a known expected output for empty input, which is a good cryptographic testing practice.
26-30: Double hashing implementation verified for Bitcoin SHA-256d
Thefinalize_intomethod inbip340/src/double.rs(lines 26–30) correctly performs a double SHA-256 and matches the known empty-input test vector (5df6e0e2761359d…4c9456). No further changes required.bip322/tests/integration_test.rs (5)
1-12: Excellent test documentation and scope definition.The comprehensive header documentation clearly explains the integration test objectives and scope, making it easy to understand what aspects of BIP-322 integration are being validated.
20-23: Smart compile-time trait verification approach.The
verify_traits_implementedhelper function provides a clean way to verify trait implementations at compile time, ensuring that BIP-322 properly implements the required traits.
36-60: Thorough test of Defuse payload extraction integration.The test properly validates that BIP-322 can carry JSON-encoded Defuse payloads and that the
ExtractDefusePayloadtrait works correctly. The test approach is sound - it doesn't require valid signatures since it's testing payload extraction, not verification.
86-124: Comprehensive validation of core trait implementations.This test thoroughly validates the
PayloadandSignedPayloadtrait implementations with excellent coverage:
- Hash length validation (32 bytes)
- Non-zero hash verification
- Hash determinism testing
- Message sensitivity verification
- Proper error handling for empty signatures
The test logic is sound and covers all critical aspects of the trait implementations.
136-226: Excellent MultiPayload integration testing.This test provides comprehensive validation of BIP-322 integration within the
MultiPayloadenum:
- Proper enum variant wrapping
- Correct delegation of hash and verification methods
- Hash consistency between direct and wrapped calls
- Pattern matching validation
ExtractDefusePayloadtrait functionality through the enum wrapperThe test coverage is thorough and validates all critical integration points.
bip322/src/der.rs (1)
79-136: Well-implemented DER ECDSA signature parser.The function correctly implements full ASN.1 DER parsing with comprehensive validation:
- Proper tag validation (SEQUENCE 0x30, INTEGER 0x02)
- Length consistency checks
- Bounds validation at each step
This implementation follows best practices for secure parsing.
bip322/src/error.rs (1)
1-321: Excellent comprehensive error handling design.The error module demonstrates best practices:
- Well-categorized error types with clear separation of concerns
- Rich contextual information for debugging (indices, hex strings, descriptions)
- Consistent Display implementations with helpful messages
- Proper std::error::Error trait implementations
This level of detail will greatly aid in debugging and integration.
bip322/src/bitcoin_minimal.rs (2)
54-88: Correct implementation of Bitcoin cryptographic primitives.The
double_sha256andhash160functions correctly implement Bitcoin's standard hash functions using NEAR SDK host functions. This approach is optimal for gas efficiency on NEAR.
315-369: Script construction is correct but has the same zero-array fallback issue.The
script_pubkey()method correctly constructs Bitcoin scripts for each address type, following the proper opcode sequences. However, it suffers from the same issue of using zero-filled arrays as fallbacks for missing data.Once the
to_address_data()issue is fixed, this method should also be updated to handle missing data properly.
There was a problem hiding this 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
♻️ Duplicate comments (4)
bip322/src/der.rs (1)
160-220: The total length validation concern has been properly addressed.The implementation now includes comprehensive validation (lines 209-217) that ensures:
- The actual content length matches the declared total length
- No trailing data exists after the signature
- The total bytes consumed match the input length
This addresses the previous review comment about preventing malformed signatures with trailing garbage data.
bip322/src/bitcoin_minimal.rs (3)
286-321: The zero-filled array issue has been properly addressed.The
to_address_data()method now correctly returnsResult<AddressData, AddressError>and usesok_or(AddressError::MissingRequiredData)instead ofunwrap_orwith zero arrays. This ensures missing cryptographic data is handled explicitly and safely.
831-908: Witness data serialization has been properly implemented.The
consensus_encodemethod now correctly serializes witness data:
- Detects witness data presence (line 836)
- Writes witness marker and flag bytes (0x00, 0x01) when needed
- Serializes witness stacks for all inputs after the outputs
This addresses the previous concern about missing witness data serialization for segwit transactions.
996-1111: BIP-143 segwit v0 sighash algorithm has been correctly implemented.The
segwit_v0_encode_signing_data_tomethod now properly implements the BIP-143 specification:
- Writes all 10 required fields in the correct order
- Uses helper methods to compute hashPrevouts, hashSequence, and hashOutputs
- Correctly serializes sighash type as a 4-byte little-endian integer
- Includes proper bounds checking for input_index
This addresses the previous concern about incorrect BIP-143 implementation.
🧹 Nitpick comments (2)
bip322/src/der.rs (1)
210-210: Fix formatting issue.Run
cargo fmtto fix the formatting issue on this line.bip322/src/bitcoin_minimal.rs (1)
833-833: Fix formatting issues throughout the file.Run
cargo fmtto fix the formatting issues at multiple lines flagged by static analysis.Also applies to: 888-888, 1035-1035, 1066-1066, 1079-1079, 1091-1091
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
bip322/README.md(1 hunks)bip322/src/bitcoin_minimal.rs(1 hunks)bip322/src/der.rs(1 hunks)core/src/payload/multi.rs(5 hunks)
✅ Files skipped from review due to trivial changes (1)
- bip322/README.md
🚧 Files skipped from review as they are similar to previous changes (1)
- core/src/payload/multi.rs
🧰 Additional context used
🪛 GitHub Check: Format
bip322/src/der.rs
[warning] 210-210:
Diff in /home/runner/work/intents/intents/bip322/src/der.rs
bip322/src/bitcoin_minimal.rs
[warning] 1091-1091:
Diff in /home/runner/work/intents/intents/bip322/src/bitcoin_minimal.rs
[warning] 1079-1079:
Diff in /home/runner/work/intents/intents/bip322/src/bitcoin_minimal.rs
[warning] 1066-1066:
Diff in /home/runner/work/intents/intents/bip322/src/bitcoin_minimal.rs
[warning] 1035-1035:
Diff in /home/runner/work/intents/intents/bip322/src/bitcoin_minimal.rs
[warning] 888-888:
Diff in /home/runner/work/intents/intents/bip322/src/bitcoin_minimal.rs
[warning] 833-833:
Diff in /home/runner/work/intents/intents/bip322/src/bitcoin_minimal.rs
⏰ 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). (7)
- GitHub Check: Shared / Static Analysis (intents-contract, contract, near, defuse, 1, ., 1.18, false, --filter-paths "nod...
- GitHub Check: Shared / Static Analysis (intents-poa-factory, contract, near, poa-factory, 1, ., 1.18, false, --filter-pa...
- GitHub Check: Shared / Static Analysis (intents-poa-token, contract, near, poa-token, 1, ., 1.18, false, --filter-paths ...
- GitHub Check: Shared security analysis / Static Analysis (intents-poa-factory, contract, near, poa-factory, 1, ., 1.18, false, --filter-pa...
- GitHub Check: Shared security analysis / Static Analysis (intents-poa-token, contract, near, poa-token, 1, ., 1.18, false, --filter-paths ...
- GitHub Check: Shared security analysis / Static Analysis (intents-contract, contract, near, defuse, 1, ., 1.18, false, --filter-paths "nod...
- GitHub Check: Security Audit - report
🔇 Additional comments (5)
bip322/src/der.rs (3)
31-65: LGTM! Well-implemented DER length parsing with proper validation.The function correctly handles both short and long form DER length encoding with comprehensive validation for canonical and minimal encoding rules.
88-145: Robust DER ECDSA signature parsing with comprehensive validation.The function properly validates the complete DER structure and ensures no trailing data is accepted by checking that
pos + total_len == der_bytes.len()at line 109.
222-355: Excellent test coverage for DER parsing functions.The tests comprehensively cover:
- Valid and invalid DER length encodings
- Canonical encoding validation
- Trailing data detection for both parsers
- Various malformed signature structures
Particularly good to see specific tests for the trailing data issue mentioned in previous reviews.
bip322/src/bitcoin_minimal.rs (2)
54-88: Well-implemented cryptographic functions using NEAR SDK.Both
double_sha256andhash160correctly implement Bitcoin's standard hash functions using NEAR SDK's optimized host functions for gas efficiency.
391-562: Comprehensive and correct Bitcoin address parsing.The implementation properly handles all supported address types with full validation:
- Base58Check decoding with checksum verification for P2PKH/P2SH
- Bech32 decoding for segwit addresses
- Proper version byte and length validation
- Clear error handling for each failure mode
There was a problem hiding this 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)
bip322/src/der.rs (1)
289-300: Consider adding tests with realistic ECDSA signature sizes.The current tests use minimal values (R=0x01, S=0x02) which are good for structural validation. Consider adding tests with realistic Bitcoin signature sizes where R and S are typically 32-33 bytes each, to ensure the parsing handles real-world signatures correctly.
Example test case:
#[test] fn test_parse_der_ecdsa_signature_realistic_size() { // Realistic 71-byte DER signature (common in Bitcoin) let realistic_der = vec![ 0x30, 0x45, // SEQUENCE, length 69 0x02, 0x21, // INTEGER, length 33 (R with leading zero for sign) 0x00, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0x02, 0x20, // INTEGER, length 32 (S) 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x11, 0x22, ]; let result = parse_der_ecdsa_signature(&realistic_der); assert!(result.is_some()); let (r, s) = result.unwrap(); assert_eq!(r.len(), 33); assert_eq!(s.len(), 32); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
bip322/src/bitcoin_minimal.rs(1 hunks)bip322/src/der.rs(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- bip322/src/bitcoin_minimal.rs
⏰ 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). (8)
- GitHub Check: Build
- GitHub Check: Shared / Static Analysis (intents-poa-token, contract, near, poa-token, 1, ., 1.18, false, --filter-paths ...
- GitHub Check: Shared / Static Analysis (intents-contract, contract, near, defuse, 1, ., 1.18, false, --filter-paths "nod...
- GitHub Check: Shared / Static Analysis (intents-poa-factory, contract, near, poa-factory, 1, ., 1.18, false, --filter-pa...
- GitHub Check: Shared security analysis / Static Analysis (intents-poa-factory, contract, near, poa-factory, 1, ., 1.18, false, --filter-pa...
- GitHub Check: Shared security analysis / Static Analysis (intents-contract, contract, near, defuse, 1, ., 1.18, false, --filter-paths "nod...
- GitHub Check: Shared security analysis / Static Analysis (intents-poa-token, contract, near, poa-token, 1, ., 1.18, false, --filter-paths ...
- GitHub Check: Security Audit - report
There was a problem hiding this 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
🧹 Nitpick comments (2)
bip322/src/lib.rs (1)
607-2563: LGTM! Comprehensive test coverage with excellent edge case handling.The test suite thoroughly covers address parsing, transaction structure, error conditions, Unicode handling, and network validation. The inclusion of official BIP-322 test vectors is particularly valuable.
Consider adding integration tests with real Bitcoin signatures to validate the complete signing and verification flow end-to-end. This could be done by:
- Using known test vectors with real signatures from BIP-322 reference implementations
- Or integrating with a Bitcoin signing library in tests to generate valid signatures
Example test structure:
#[test] fn test_real_signature_verification() { // Test vector from BIP-322 reference implementation let test_vector = SignedBip322Payload { address: "bc1q...".parse().unwrap(), message: "Hello World".to_string(), signature: Witness::from_stack(vec![ hex!("30440220..."), // Real DER signature hex!("02..."), // Real public key ]), }; let recovered_pubkey = test_vector.verify(); assert!(recovered_pubkey.is_some(), "Real signature should verify"); // Verify the recovered pubkey matches expected }bip322/src/bitcoin_minimal.rs (1)
351-406: Consider returning Result from script_pubkey() to avoid panics.The method uses
expect()calls that could panic if required data is missing. While the documentation states to callto_address_data()first, this creates a potential footgun.Consider making this method return
Result<ScriptBuf, AddressError>:-pub fn script_pubkey(&self) -> ScriptBuf { +pub fn script_pubkey(&self) -> Result<ScriptBuf, AddressError> { match self.address_type { AddressType::P2PKH => { - let pubkey_hash = self.pubkey_hash.expect("P2PKH address missing pubkey_hash"); + let pubkey_hash = self.pubkey_hash.ok_or(AddressError::MissingRequiredData)?; // ... rest of implementation - ScriptBuf { inner: script } + Ok(ScriptBuf { inner: script }) } // ... other cases } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (5)
bip322/Cargo.toml(1 hunks)bip322/README.md(1 hunks)bip322/src/bitcoin_minimal.rs(1 hunks)bip322/src/error.rs(1 hunks)bip322/src/lib.rs(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- bip322/Cargo.toml
- bip322/README.md
- bip322/src/error.rs
⏰ 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). (9)
- GitHub Check: Build
- GitHub Check: Security Audit - deny
- GitHub Check: Shared security analysis / Static Analysis (intents-poa-token, contract, near, poa-token, 1, ., 1.18, false, --filter-paths ...
- GitHub Check: Shared security analysis / Static Analysis (intents-poa-factory, contract, near, poa-factory, 1, ., 1.18, false, --filter-pa...
- GitHub Check: Shared security analysis / Static Analysis (intents-contract, contract, near, defuse, 1, ., 1.18, false, --filter-paths "nod...
- GitHub Check: Shared / Static Analysis (intents-poa-token, contract, near, poa-token, 1, ., 1.18, false, --filter-paths ...
- GitHub Check: Shared / Static Analysis (intents-poa-factory, contract, near, poa-factory, 1, ., 1.18, false, --filter-pa...
- GitHub Check: Shared / Static Analysis (intents-contract, contract, near, defuse, 1, ., 1.18, false, --filter-paths "nod...
- GitHub Check: Security Audit - report
🔇 Additional comments (19)
bip322/src/lib.rs (7)
1-16: LGTM! Well-organized imports with proper feature gating.The module structure and imports are clean and appropriate for BIP-322 implementation on NEAR.
18-41: LGTM! Well-structured payload definition with comprehensive documentation.The witness format documentation clearly explains the expected structure for each address type, which is crucial for proper usage.
43-53: LGTM! Clean implementation of the Payload trait.The pattern matching correctly delegates to address-specific hash methods.
55-66: LGTM! Consistent implementation of the SignedPayload trait.The verification routing mirrors the hash computation pattern appropriately.
174-307: LGTM! Transaction creation follows BIP-322 specification accurately.The implementation correctly creates the virtual transactions with proper version markers, input/output structures, and cross-references.
309-341: LGTM! Correct implementation of BIP-322 tagged message hash.The tagged hash implementation properly follows the BIP-340 pattern with domain separation.
512-605: Move helper methods out of test module.These methods are needed for the main implementation (script validation) and should not be in the test module.
Move these helper methods to the main
impl SignedBip322Payloadblock:-#[cfg(test)] -mod tests { - // ... test imports ... - - impl SignedBip322Payload { - fn execute_redeem_script(redeem_script: &[u8], pubkey_bytes: &[u8]) -> bool { - // ... implementation ... - } - // ... other helper methods ... - } +impl SignedBip322Payload { + fn execute_redeem_script(redeem_script: &[u8], pubkey_bytes: &[u8]) -> bool { + // ... implementation ... + } + + fn execute_witness_script(witness_script: &[u8], pubkey_bytes: &[u8]) -> bool { + // ... implementation ... + } + + fn verify_pubkey_matches_address(&self, pubkey_bytes: &[u8]) -> bool { + // ... implementation ... + } + + fn is_valid_public_key_format(pubkey_bytes: &[u8]) -> bool { + // ... implementation ... + } + + fn compute_pubkey_hash160(pubkey_bytes: &[u8]) -> [u8; 20] { + hash160(pubkey_bytes) + } +} +#[cfg(test)] +mod tests {Likely an incorrect or invalid review comment.
bip322/src/bitcoin_minimal.rs (12)
1-38: Excellent module documentation and appropriate imports.The comprehensive documentation clearly explains the design principles, supported address types, and integration with NEAR SDK. The imports are well-chosen for the specific functionality needed.
39-83: Well-implemented NEAR SDK integration with digest traits.The NearSha256 implementation correctly integrates NEAR's host functions with the digest crate's trait system, enabling compatibility with BIP340 functionality while maintaining gas efficiency.
84-111: Correct HASH160 implementation using NEAR SDK.The function correctly implements Bitcoin's HASH160 algorithm (RIPEMD160(SHA256(data))) using NEAR's optimized host functions, with excellent documentation.
112-260: Well-designed address structures with comprehensive documentation.The Address struct and related types are thoughtfully designed with appropriate use of Option types for different address formats. The documentation clearly explains each field's purpose and usage.
318-343: Excellent error handling for missing cryptographic data.The
to_address_data()method correctly addresses the previous security concern by returning proper errors instead of using zero-filled arrays as fallbacks. This ensures missing cryptographic data is handled explicitly and safely.
442-583: Comprehensive and secure address parsing implementation.The parsing logic correctly validates all supported address formats with proper checksum verification, length validation, and network validation. The use of NearDoubleSha256 for Base58Check validation and the bech32 crate for segwit addresses ensures correctness.
590-716: Excellent error handling and Bech32 validation.The comprehensive AddressError enum provides specific error types for different failure modes. The decode_bech32 function correctly validates witness program constraints per BIP-141 and ensures mainnet-only operation.
717-846: Standard Bitcoin transaction structures.The transaction structures correctly follow Bitcoin's format with appropriate field types and minimal implementation suitable for BIP-322 verification.
853-931: Complete transaction encoding with witness support.The consensus encoding implementation correctly serializes both legacy and witness transaction formats, properly including witness marker/flag bytes and witness data serialization as required by BIP-322.
1038-1089: Correct BIP-143 segwit v0 sighash implementation.The sighash preimage encoding now correctly follows the BIP-143 specification with proper field ordering, double SHA-256 computations for hash fields, and compact size encoding. This addresses the previous review concern about incorrect implementation.
1095-1133: Well-implemented BIP-143 hash computation methods.The helper methods correctly compute hashPrevouts, hashSequence, and hashOutputs using double SHA-256 as specified in BIP-143, with proper data serialization and error handling.
1149-1212: Comprehensive test coverage for core functionality.The tests properly verify NEAR SDK integration, BIP340 compatibility, and address parsing with appropriate test vectors and parameterized testing using rstest.
There was a problem hiding this 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
♻️ Duplicate comments (4)
bip322/src/lib.rs (4)
68-172: Critical: Incorrect sighash algorithm for P2PKH and P2SH addresses.This is a duplicate of a previous critical issue. The implementation uses segwit v0 sighash algorithm for all address types, but P2PKH and P2SH should use the legacy sighash algorithm. The
compute_message_hashmethod (lines 353-377) only implements segwit v0 sighash computation.
422-441: Critical: Missing redeem script validation for P2SH addresses.This is a duplicate of a previous critical issue. The P2SH verification extracts the redeem script but never validates it. According to BIP-322, you must verify that
HASH160(redeem_script) == script_hashfrom the address and execute the redeem script to validate the signature.
443-463: Critical: Missing witness script validation for P2WSH addresses.This is a duplicate of a previous critical issue. The witness script is extracted but never validated or executed. You need to validate that the witness script hash matches the address and execute the script according to BIP-322.
465-491: Critical: Limited signature format support.This is a duplicate of a previous critical issue. The implementation only accepts raw 64-byte signatures and recovery IDs 0-1, but Bitcoin signatures are typically in DER format and recovery IDs should range from 0-3.
🧹 Nitpick comments (4)
bip322/src/lib.rs (2)
943-949: Fix unnecessary closure usage.The static analysis tool correctly identified an unnecessary closure. The
unwrap_or_elsecan be replaced withunwrap_orsince you're providing a constant value, not calling a function.- address: "bc1q9vza2e8x573nczrlzms0wvx3gsqjx7vavgkx0l" - .parse() - .unwrap_or_else(|_| Address { - address_type: AddressType::P2WPKH, - pubkey_hash: Some([1u8; 20]), - witness_program: None, - }), + address: "bc1q9vza2e8x573nczrlzms0wvx3gsqjx7vavgkx0l" + .parse() + .unwrap_or(Address { + address_type: AddressType::P2WPKH, + pubkey_hash: Some([1u8; 20]), + witness_program: None, + }),
512-605: Note: Limited script execution capabilities.The
execute_redeem_scriptandexecute_witness_scriptmethods only support simple P2PKH-style scripts. This is documented as a limitation with plans for "full Bitcoin script interpreter" in the future. For production use, consider implementing more comprehensive script validation.bip322/src/bitcoin_minimal.rs (2)
344-399: Consider making script_pubkey() return Result to avoid panics.While the documentation states to call
to_address_data()first, theexpect()calls inscript_pubkey()could still panic if callers don't follow this convention.Consider returning
Result<ScriptBuf, AddressError>:-pub fn script_pubkey(&self) -> ScriptBuf { +pub fn script_pubkey(&self) -> Result<ScriptBuf, AddressError> { match self.address_type { AddressType::P2PKH => { - let pubkey_hash = self.pubkey_hash.expect("P2PKH address missing pubkey_hash"); + let pubkey_hash = self.pubkey_hash.ok_or(AddressError::MissingRequiredData)?; // ... rest of implementation - ScriptBuf { inner: script } + Ok(ScriptBuf { inner: script }) } // ... similar changes for other variants } }This would provide consistent error handling throughout the API.
1138-1200: Good test coverage for core functionality.The existing tests properly verify NEAR SDK integration, BIP340 compatibility, and basic address parsing. The use of
rstestfor parameterized testing is excellent.Consider adding tests for:
- Error conditions in address parsing (invalid checksums, wrong lengths)
- Edge cases in transaction encoding
- Sighash computation with different transaction structures
- Script generation for all address types
This would provide more comprehensive coverage of the module's functionality.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
bip322/src/bitcoin_minimal.rs(1 hunks)bip322/src/lib.rs(1 hunks)bip322/tests/integration_test.rs(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- bip322/tests/integration_test.rs
🧰 Additional context used
🪛 GitHub Check: Check
bip322/src/lib.rs
[failure] 943-943:
unnecessary closure used to substitute value for Result::Err
⏰ 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). (7)
- GitHub Check: Shared / Static Analysis (intents-poa-token, contract, near, poa-token, 1, ., 1.18, false, --filter-paths ...
- GitHub Check: Shared / Static Analysis (intents-contract, contract, near, defuse, 1, ., 1.18, false, --filter-paths "nod...
- GitHub Check: Shared / Static Analysis (intents-poa-factory, contract, near, poa-factory, 1, ., 1.18, false, --filter-pa...
- GitHub Check: Shared security analysis / Static Analysis (intents-contract, contract, near, defuse, 1, ., 1.18, false, --filter-paths "nod...
- GitHub Check: Shared security analysis / Static Analysis (intents-poa-token, contract, near, poa-token, 1, ., 1.18, false, --filter-paths ...
- GitHub Check: Shared security analysis / Static Analysis (intents-poa-factory, contract, near, poa-factory, 1, ., 1.18, false, --filter-pa...
- GitHub Check: Security Audit - report
🔇 Additional comments (9)
bip322/src/lib.rs (3)
18-66: LGTM: Well-structured BIP-322 payload implementation.The struct definition is properly documented and annotated. The trait implementations correctly dispatch verification and hashing based on address type, which is essential for BIP-322 compliance.
174-351: LGTM: Correct BIP-322 transaction construction.The transaction creation methods properly implement the BIP-322 specification:
- Uses version 0 as BIP-322 marker
- Creates proper "to_spend" transaction with tagged message hash
- Creates "to_sign" transaction that references "to_spend"
- Implements BIP-322 tagged hash correctly following BIP-340 pattern
- Uses OP_RETURN for provably unspendable output
607-2543: Excellent: Comprehensive test coverage.The test suite demonstrates exceptional thoroughness with:
- Gas benchmarking for NEAR blockchain optimization
- BIP-322 official test vectors for specification compliance
- Comprehensive error handling and edge cases
- Unicode message support testing
- Network interoperability restrictions
- Cross-address type verification security
This level of testing significantly improves confidence in the implementation quality.
bip322/src/bitcoin_minimal.rs (6)
39-82: Well-designed NEAR SDK digest integration.The implementation correctly bridges NEAR SDK's
env::sha256_array()with the standarddigestcrate traits, enabling compatibility with BIP340'sDoublewrapper for Bitcoin's double SHA-256. The buffer-based approach is standard for digest implementations.
104-110: Correct HASH160 implementation using NEAR SDK.The implementation correctly follows Bitcoin's HASH160 algorithm (RIPEMD160(SHA256(data))) using NEAR SDK host functions for gas efficiency. This is essential for address generation and validation.
311-336: Excellent error handling for missing cryptographic data.The implementation correctly addresses the previous security concern by returning
Result<AddressData, AddressError>instead of using zero-filled arrays as fallbacks. This ensures missing cryptographic data is handled explicitly and safely.
435-573: Comprehensive and secure address parsing implementation.The parsing logic correctly handles all supported address types with proper validation:
- Base58Check validation with checksum verification for P2PKH/P2SH
- Bech32 validation with witness program length checks for P2WPKH/P2WSH
- Network validation (mainnet only)
- Detailed error reporting
The implementation follows Bitcoin standards precisely and provides robust error handling.
842-920: Proper transaction serialization with witness support.The implementation correctly addresses the previous concern about missing witness data serialization. The encoding now properly:
- Detects witness data presence
- Includes witness marker/flag bytes when needed
- Serializes witness stacks for each input
- Follows Bitcoin consensus format
This is essential for BIP-322 with segwit addresses.
1027-1123: Correct BIP-143 sighash implementation.The implementation now properly follows the BIP-143 specification for segwit v0 sighash computation. It correctly writes the preimage format with all required fields in the proper order and uses the correct helper functions for computing hashPrevouts, hashSequence, and hashOutputs. This addresses the previous review concern about incorrect sighash algorithm implementation.
There was a problem hiding this 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
♻️ Duplicate comments (4)
bip322/src/lib.rs (4)
71-175: Critical: Incorrect sighash algorithm still not fixed.All hash computation methods delegate to the same
compute_message_hashfunction (lines 360-384), which only implements segwit v0 sighash algorithm. This is incorrect for P2PKH and P2SH addresses, which should use the legacy sighash algorithm according to BIP-322.The issue identified in previous reviews has not been addressed.
360-384: Critical: Sighash algorithm issue persists.The
compute_message_hashmethod still only implements segwit v0 sighash algorithm for all address types. This is the same critical issue identified in previous reviews that remains unaddressed.P2PKH and P2SH addresses require legacy sighash algorithm, not segwit v0.
463-483: Critical: P2WSH witness script validation still missing.The witness script is extracted (line 472) but never validated or executed. This is the same critical security issue identified in previous reviews that remains unaddressed.
According to BIP-322, you must:
- Verify that
SHA256(witness_script)matches the witness program- Execute the witness script to validate the signature
485-511: Critical: Limited signature format support persists.The signature recovery method still has the same limitations identified in previous reviews:
- Only accepts raw 64-byte signatures (line 492), not DER format
- Only tries recovery IDs 0-1 (line 497), should be 0-3 for Bitcoin standard
This limits compatibility with real Bitcoin signatures.
🧹 Nitpick comments (1)
bip322/src/bitcoin_minimal.rs (1)
1138-1200: Good foundation but consider expanding test coverage.The existing tests properly validate core functionality including NEAR SDK SHA256 compatibility, BIP340 tagged hashing, and basic address parsing. However, consider adding tests for:
- Error conditions in address parsing (invalid checksums, wrong lengths, unsupported formats)
- Transaction encoding/decoding round-trips
- Sighash computation with known test vectors
- Edge cases in witness program validation
- Script pubkey generation for all address types
The current tests provide a solid foundation, but more comprehensive coverage would increase confidence in the implementation's correctness across all supported scenarios.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
bip322/src/bitcoin_minimal.rs(1 hunks)bip322/src/lib.rs(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
bip322/src/lib.rs (3)
near-utils/src/digest.rs (1)
digest(44-47)bip322/src/bitcoin_minimal.rs (16)
hash160(104-110)new(51-53)new(274-276)new(716-718)new(751-753)new(963-965)new(1004-1006)len(278-280)len(724-726)from_str(431-568)script_pubkey(344-395)is_empty(282-284)is_empty(720-722)is_p2wsh(249-251)is_p2wpkh(245-247)from_stack(291-293)core/src/payload/multi.rs (2)
hash(67-78)verify(85-96)
⏰ 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). (9)
- GitHub Check: Build
- GitHub Check: Security Audit - deny
- GitHub Check: Shared security analysis / Static Analysis (intents-contract, contract, near, defuse, 1, ., 1.18, false, --filter-paths "nod...
- GitHub Check: Shared security analysis / Static Analysis (intents-poa-token, contract, near, poa-token, 1, ., 1.18, false, --filter-paths ...
- GitHub Check: Shared security analysis / Static Analysis (intents-poa-factory, contract, near, poa-factory, 1, ., 1.18, false, --filter-pa...
- GitHub Check: Shared / Static Analysis (intents-contract, contract, near, defuse, 1, ., 1.18, false, --filter-paths "nod...
- GitHub Check: Shared / Static Analysis (intents-poa-token, contract, near, poa-token, 1, ., 1.18, false, --filter-paths ...
- GitHub Check: Shared / Static Analysis (intents-poa-factory, contract, near, poa-factory, 1, ., 1.18, false, --filter-pa...
- GitHub Check: Security Audit - report
🔇 Additional comments (10)
bip322/src/lib.rs (6)
1-19: LGTM!The module structure and imports are well-organized and comprehensive. The conditional compilation attributes for ABI features and the re-exports from submodules are properly handled.
20-43: LGTM!The
SignedBip322Payloadstruct is well-defined with clear documentation of the witness format requirements for different address types. The serde attributes and conditional compilation are properly configured.
45-69: LGTM!The trait implementations use a clean delegation pattern based on address type, which properly separates concerns and makes the code maintainable. The error handling with
expectis appropriate since address validation should have occurred during construction.
177-357: LGTM on transaction creation structure!The BIP-322 transaction creation follows the specification correctly:
to_spendtransaction properly embeds the tagged message hashto_signtransaction correctly references theto_spendoutput- Tagged hash implementation uses proper domain separation
- Transaction structure matches BIP-322 requirements
429-461: Good improvement on P2SH validation!The P2SH signature verification now properly:
- Validates that the redeem script hash matches the address (lines 441-444)
- Executes the redeem script to verify it's a supported pattern (lines 447-450)
This addresses the critical issue from previous reviews regarding missing redeem script validation.
555-2601: Excellent comprehensive test suite!The test coverage is outstanding with:
- Gas benchmarking for performance validation
- Address parsing and validation tests for all Bitcoin address types
- Error handling tests for malformed inputs
- Unicode message handling tests
- Network interoperability tests ensuring mainnet-only operation
- Transaction serialization tests for both witness and legacy formats
- Cross-address-type verification tests
The tests demonstrate thorough consideration of edge cases and security boundaries.
bip322/src/bitcoin_minimal.rs (4)
39-110: LGTM! Well-designed NEAR SDK cryptographic integration.The
NearSha256implementation correctly integrates with NEAR's host functions while maintaining compatibility with thedigestcrate traits and BIP340. Thehash160function properly implements Bitcoin's standard RIPEMD160(SHA256(data)) using NEAR SDK functions. This design optimizes for NEAR's gas model while ensuring cryptographic correctness.
402-701: Excellent comprehensive address parsing implementation.The
FromStrimplementation forAddresscorrectly handles all major Bitcoin address types (P2PKH, P2SH, P2WPKH, P2WSH) with proper validation:
- Base58Check decoding with version byte and checksum validation for legacy addresses
- Bech32 decoding with HRP validation and witness program length checks for segwit addresses
- Comprehensive error handling that provides specific failure reasons
- Mainnet-only validation as intended for the MVP scope
The previous security concern about zero-filled fallbacks has been properly addressed with explicit error handling.
838-949: LGTM! Complete Bitcoin transaction serialization implementation.The
Encodableimplementation forTransactioncorrectly follows Bitcoin's consensus format including:
- Proper witness transaction serialization with marker/flag bytes (0x00, 0x01)
- Correct field ordering and encoding (version, inputs, outputs, witness data, locktime)
- Compact size encoding for variable-length fields
- Support for both legacy and witness transaction formats
The previous issue with missing witness data serialization has been properly addressed. This implementation ensures compatibility with Bitcoin's transaction format standards.
1027-1123: Excellent BIP-143 sighash implementation.The
segwit_v0_encode_signing_data_tomethod correctly implements the BIP-143 sighash preimage format with all required fields in proper order:
- Transaction version, hashPrevouts, hashSequence
- Specific input's outpoint, scriptCode with compact size prefix
- Amount, input sequence, hashOutputs
- Locktime and sighash_type as 4-byte little-endian
The helper functions properly compute the required hash values using double SHA256. Input bounds checking prevents out-of-bounds access. The previous issue with incorrect BIP-143 implementation has been resolved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
🧹 Nitpick comments (24)
bip322/src/error.rs (2)
40-41: Document theUnsupportedWitnessVersionvariant for consistency and clarityAll other variants have helpful doc comments. Add a brief description and reference to BIPs 173/350 for v0 vs. v1+ rules.
- UnsupportedWitnessVersion, + /// Unsupported witness version (e.g., v1+ which requires Bech32m per BIP-350). + /// See BIP-173/BIP-350 for valid combinations and encoding rules. + UnsupportedWitnessVersion,
52-61: Minor copy edit: capitalize “Bech32” in error messageMatches common usage and your other capitalization patterns.
- Self::InvalidBech32 => write!(f, "Invalid bech32 encoding"), + Self::InvalidBech32 => write!(f, "Invalid Bech32 encoding"),bip322/Cargo.toml (1)
18-22: Confirm necessity of runtimebase64
base64 = "0.22"is included in both dependencies and dev-dependencies. If the crate itself doesn’t usebase64at runtime (only tests/tools do), consider moving it exclusively to[dev-dependencies]to keep the runtime surface smaller.If helpful, I can scan the crate for
base64usage and propose a tidy-up.bip322/validate_unisat_vector.py (6)
15-29: Strengthen Bech32 parsing and avoid bare except
- Validate witness version (expect 0 for P2WPKH/P2WSH).
- Support both 20 (P2WPKH) and 32 (P2WSH) lengths if desired.
- Replace bare
except:withexcept Exception as e:and log for visibility.def parse_bech32_address(address): """Parse bech32 address to get witness program""" # Simple bech32 parsing (for P2WPKH) import bech32 try: hrp, data = bech32.bech32_decode(address) - if hrp == 'bc' and data: - # Convert from 5-bit to 8-bit - decoded = bech32.convertbits(data[1:], 5, 8, False) - if decoded and len(decoded) == 20: - return bytes(decoded) - except: - pass + if hrp != 'bc' or not data: + return None + version = data[0] + if version != 0: + # Only v0 (Bech32) supported in this simplified parser + return None + # Convert from 5-bit to 8-bit + decoded = bech32.convertbits(data[1:], 5, 8, False) + if not decoded: + return None + # Accept P2WPKH (20) or P2WSH (32) witness program sizes + if len(decoded) in (20, 32): + return bytes(decoded) + except Exception as e: + print(f"bech32 parse error: {e}") return None
6-10: Remove unused import
unhexlifyis unused.-import base64 -import hashlib -from binascii import hexlify, unhexlify +import base64 +import hashlib +from binascii import hexlify
101-124: Stub returns True; consider at least checking HASH160 when availableThis function currently always returns True, which can mask invalid cases. If
hashlib.new('ripemd160')is available, you can compute HASH160 to compare with the witness program. Otherwise, make the placeholder explicit with a FIXME to avoid accidental reliance.I can provide a small fallback-based implementation that uses
hashlib.new('ripemd160')when present and degrades gracefully otherwise.
147-159: Avoid f-strings without placeholdersThis f-string has no placeholders, which linters flag.
- print(f"✓ Address parsed successfully") + print("✓ Address parsed successfully")
165-179: Remove or use the unused variable and avoid unused imports for availability checks
recovered_pubkeyis assigned but not used.- For availability checks, prefer
importlib.util.find_specover importing modules you don’t use (avoids F401).- recovered_pubkey = recover_pubkey_from_signature(message_hash, signature_bytes) + _ = recover_pubkey_from_signature(message_hash, signature_bytes) @@ - try: - import bech32 - print("✓ bech32 library available") - except ImportError: - print("✗ bech32 library not available - run: pip install bech32") + import importlib.util + if importlib.util.find_spec("bech32"): + print("✓ bech32 library available") + else: + print("✗ bech32 library not available - run: pip install bech32") @@ - try: - import ecdsa - print("✓ ecdsa library available") - except ImportError: - print("✗ ecdsa library not available - run: pip install ecdsa") + if importlib.util.find_spec("ecdsa"): + print("✓ ecdsa library available") + else: + print("✗ ecdsa library not available - run: pip install ecdsa")
54-100: Placeholder recovery routineThe recovery function prints diagnostics and returns None. That’s fine for an exploratory script, but call it out as a deliberate stub to avoid confusion. If you want parity with the comprehensive script, consider factoring common bits or guarding with a “--verbose” flag.
Happy to help replace this with a concrete secp256k1 recovery using
coincurveorbitcoinlibif acceptable for your environment.bip322/validate_unisat_comprehensive.py (4)
15-29: Harden Bech32 parsing and avoid bare exceptAs in the vector script, validate witness version and length, and avoid bare
except.def parse_bech32_address(address): """Parse bech32 address to get witness program""" import bech32 try: hrp, data = bech32.bech32_decode(address) - if hrp == 'bc' and data: - # Convert from 5-bit to 8-bit - decoded = bech32.convertbits(data[1:], 5, 8, False) - if decoded and len(decoded) == 20: - return bytes(decoded) - except: - pass + if hrp != 'bc' or not data: + return None + version = data[0] + if version != 0: + return None + # Convert from 5-bit to 8-bit + decoded = bech32.convertbits(data[1:], 5, 8, False) + if not decoded: + return None + if len(decoded) in (20, 32): + return bytes(decoded) + except Exception as e: + print(f"bech32 parse error: {e}") return None
6-10: Remove unused import
unhexlifyis unused.-import base64 -import hashlib -from binascii import hexlify, unhexlify +import base64 +import hashlib +from binascii import hexlify
53-180: Trim unused imports and variables in recovery path
possible_public_keys_from_signatureandKeyare imported but unused.pointis assigned but not used.- Consider consolidating recovery logic or gating heavy debug under a verbosity switch.
- import ecdsa - from ecdsa.curves import SECP256k1 - from ecdsa.ecdsa import possible_public_keys_from_signature + import ecdsa + from ecdsa.curves import SECP256k1 @@ - from bitcoinlib.encoding import hash160 - from bitcoinlib.keys import Key + from bitcoinlib.encoding import hash160 @@ - point = Point(SECP256k1.curve, x, y, order) + _ = Point(SECP256k1.curve, x, y, order)
201-206: Avoid f-strings without placeholdersMinor linter cleanup.
- print(f"✓ Address parsed successfully") - print(f"Expected witness program: {hexlify(witness_program).decode()}") + print("✓ Address parsed successfully") + print(f"Expected witness program: {hexlify(witness_program).decode()}")bip322/src/transaction.rs (2)
49-54: Nit: clarify the comment about script length; it’s 1 opcode + 1 push length + 32 bytesThe comment says “2 opcodes + 32 bytes message hash” but the second byte is the push length (0x20), not an opcode.
Apply this diff to make the comment precise:
- let mut script = Vec::with_capacity(34); // 2 opcodes + 32 bytes message hash - script.push(OP_0); // Push empty stack item - script.push(32u8); // Push 32 bytes + let mut script = Vec::with_capacity(34); // OP_0 + push(32) + 32-byte message hash + script.push(OP_0); // push empty stack item + script.push(32u8); // next 32 bytes are pushed as data
154-161: Txid vs wtxid nuance; consider documenting or enforcing no-witness serializationThe txid in Bitcoin is the double-SHA256 of the non-witness serialization. With your current approach, if a Transaction ever carries non-empty witnesses,
consensus_encodemay produce the segwit-serialization (marker/flag + witness), yielding a wtxid instead of a txid. In this module you only build empty-witness transactions, so it’s fine, but the helper is generic and could be reused.At minimum, document that
txmust have empty witnesses or implement a non-witness encoder to make it bulletproof.Apply this diff to document the constraint:
-/// Computes the transaction ID (TXID) by double SHA256 hashing the serialized transaction. +/// Computes the transaction ID (TXID) by double SHA256 hashing the serialized transaction. +/// NOTE: Assumes no witness data is present in `tx`. If any input carries a non-empty witness, +/// this would hash the segwit serialization (wtxid) rather than the legacy txid.bip322/src/verification.rs (1)
98-107: Rename local buffer to avoid shadowing thecompressedparameterThe local
compressedVec shadows thecompressed: boolparameter. Rename to improve readability and avoid confusion.Apply this diff:
- let mut compressed = Vec::with_capacity(33); - compressed.push(0x02); - compressed.extend_from_slice(&raw_pubkey[..32]); + let mut comp_buf = Vec::with_capacity(33); + comp_buf.push(0x02); + comp_buf.extend_from_slice(&raw_pubkey[..32]); let mut response = Vec::with_capacity(2); - response.push(defuse_near_utils::digest::Hash160::digest(&compressed).into()); + response.push(defuse_near_utils::digest::Hash160::digest(&comp_buf).into()); - compressed.as_mut_slice()[0] = 0x03; - response.push(defuse_near_utils::digest::Hash160::digest(&compressed).into()); + comp_buf.as_mut_slice()[0] = 0x03; + response.push(defuse_near_utils::digest::Hash160::digest(&comp_buf).into());bip322/src/lib.rs (1)
65-76: API naming mismatch:with_compact_signaturedoes not enforce compact-onlyThe constructor parses either compact or full signatures via
from_str. Either tighten the API to ensure compact-only, or rename the constructor to avoid confusion.Two options:
- Enforce compact-only:
- let signature = Bip322Signature::from_str(signature_base64)?; + let signature = Bip322Signature::from_str(signature_base64)?; + if !matches!(signature, Bip322Signature::Compact { .. }) { + return Err(Bip322Error::InvalidFormat); + }
- Or rename the constructor (requires call-site updates):
- pub fn with_compact_signature( + pub fn with_parsed_signature(bip322/src/hashing.rs (1)
145-154: P2WSH not supported here; accept witness script input instead of panickingPanicking on P2WSH will be surprising if higher layers ever request a P2WSH sighash. Consider accepting the witness script as an argument (or wiring through from signature parsing) and computing the BIP-143 preimage accordingly. At minimum, document the unsupported state.
Minimal doc update:
- Address::P2WSH { .. } => { + Address::P2WSH { .. } => { // For P2WSH, the scriptCode must be the witness script itself. // It is not derivable from the address; you'll need the script provided. - // If you don't support general P2WSH here, you can return a hash that will - // never verify, or panic with a clear message. + // This helper currently does not support providing the witness script. + // Upstream should compute segwit v0 sighash with the witness script + // available from the signature and not call this path. panic!("compute_segwit_v0_sighash: P2WSH requires the witness script (not derivable from address)") }Preferred signature change (follow-up PR):
- Add a variant
compute_segwit_v0_sighash_with_script(to_spend, to_sign, witness_script: &[u8]).- Dispatch to it when
Address::P2WSHis used.bip322/src/tests.rs (1)
335-353: Test description mismatch: 65-byte is a valid compact signature lengthThe test name/message imply an invalid signature length, but a 65-byte (r||s||recid) is the expected compact size. The failure here is due to signature content, not its length. Adjust wording to avoid confusion.
Apply this diff:
- fn test_signature_verification_invalid_signature_length() { + fn test_signature_verification_invalid_signature_content() { @@ - let invalid_signature = [0u8; 65]; // Valid 65-byte signature (but empty, so will fail) + let invalid_signature = [0u8; 65]; // Valid length, invalid content (all zeros) @@ - "Invalid signature length should fail verification" + "Invalid signature content should fail verification"bip322/src/signature.rs (2)
85-87: Be more tolerant to base64 input variants (no padding).Wallets sometimes produce base64 without padding. Consider a fallback to
STANDARD_NO_PADbefore failing.- let decoded = general_purpose::STANDARD.decode(s)?; + let decoded = match general_purpose::STANDARD.decode(s) { + Ok(b) => b, + Err(e) => { + // tolerate missing padding + general_purpose::STANDARD_NO_PAD + .decode(s) + .map_err(|_| Bip322Error::InvalidBase64(e))? + } + };
88-96: Avoid panicking in FromStr; return a typed error for invalid 65-byte input.Even if length-checked,
expectis unnecessary in parsing untrusted data. ReturnInvalidCompactSignatureinstead of panicking.- if decoded.len() == 65 { - let sig_bytes: [u8; 65] = decoded.try_into().expect("Invalid signature length"); // Should never fail - return Ok(Bip322Signature::Compact { - signature: sig_bytes, - }); - } + if decoded.len() == 65 { + let mut sig_bytes = [0u8; 65]; + sig_bytes.copy_from_slice(&decoded); + return Ok(Bip322Signature::Compact { signature: sig_bytes }); + }bip322/src/bitcoin_minimal.rs (3)
15-17: Doc mismatch: code supports P2SH/P2WSH but docs say “supports only P2PKH/P2WPKH”.Keep the module-level docs in sync with current capabilities to avoid confusion.
Suggested edit:
- Replace “MVP Focus: Supports only P2PKH and P2WPKH” with a note that P2SH and P2WSH are also supported now.
Also applies to: 23-24
425-431: Same here: avoidconst fnforScriptBuf::new().For consistency and portability, make this a regular constructor.
-impl ScriptBuf { - pub const fn new() -> Self { - Self { inner: Vec::new() } - } +impl ScriptBuf { + pub fn new() -> Self { + Self { inner: Vec::new() } + }
393-395: Avoidunwrap()in address parsing; map errors explicitly.Parsing “bc” is infallible, but
unwrap()is unnecessary in critical parsing code.- if hrp != Hrp::parse("bc").unwrap() { + if Hrp::parse("bc").map_err(|_| AddressError::InvalidBech32)? != hrp { return Err(AddressError::InvalidBech32); }
📜 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.
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (16)
.gitignore(1 hunks)Cargo.toml(2 hunks)bip322/Cargo.toml(1 hunks)bip322/src/bitcoin_minimal.rs(1 hunks)bip322/src/error.rs(1 hunks)bip322/src/hashing.rs(1 hunks)bip322/src/lib.rs(1 hunks)bip322/src/signature.rs(1 hunks)bip322/src/tests.rs(1 hunks)bip322/src/transaction.rs(1 hunks)bip322/src/verification.rs(1 hunks)bip322/validate_unisat_comprehensive.py(1 hunks)bip322/validate_unisat_vector.py(1 hunks)near-utils/src/digest.rs(3 hunks)tests/Cargo.toml(1 hunks)tests/src/utils/crypto.rs(3 hunks)
✅ Files skipped from review due to trivial changes (1)
- .gitignore
🚧 Files skipped from review as they are similar to previous changes (3)
- Cargo.toml
- tests/src/utils/crypto.rs
- tests/Cargo.toml
🧰 Additional context used
🧬 Code Graph Analysis (9)
bip322/src/tests.rs (5)
bip322/src/transaction.rs (3)
compute_tx_id(155-161)create_to_sign(101-141)create_to_spend(32-76)bip322/src/bitcoin_minimal.rs (6)
new(140-142)new(425-427)new(456-458)from_str(229-351)all_zeros(439-441)from_byte_array(443-445)bip322/src/signature.rs (1)
from_str(84-98)bip322/src/hashing.rs (1)
compute_bip322_message_hash(35-41)bip322/src/lib.rs (2)
hash(46-48)verify(54-57)
bip322/validate_unisat_comprehensive.py (1)
bip322/validate_unisat_vector.py (4)
parse_bech32_address(15-29)bitcoin_message_hash(31-52)recover_pubkey_from_signature(54-99)main(129-184)
bip322/src/hashing.rs (2)
near-utils/src/digest.rs (3)
digest(143-146)tagged(118-118)tagged(122-125)bip322/src/bitcoin_minimal.rs (1)
from_bytes(429-431)
bip322/validate_unisat_vector.py (1)
bip322/validate_unisat_comprehensive.py (3)
parse_bech32_address(15-28)recover_pubkey_from_signature(53-179)main(181-229)
bip322/src/transaction.rs (2)
near-utils/src/digest.rs (1)
digest(143-146)bip322/src/bitcoin_minimal.rs (6)
new(140-142)new(425-427)new(456-458)all_zeros(439-441)from_bytes(429-431)from_byte_array(443-445)
bip322/src/lib.rs (3)
core/src/payload/multi.rs (2)
hash(67-78)verify(85-96)bip322/src/signature.rs (1)
from_str(84-98)bip322/src/bitcoin_minimal.rs (1)
from_str(229-351)
bip322/src/signature.rs (5)
bip322/src/transaction.rs (2)
create_to_sign(101-141)create_to_spend(32-76)bip322/src/bitcoin_minimal.rs (4)
from(841-845)new(140-142)new(425-427)new(456-458)bip322/src/verification.rs (2)
validate_pubkey_matches_address(23-33)validate_compressed_pubkey_matches_address(48-81)bip322/src/hashing.rs (1)
compute_bip322_message_hash(35-41)near-utils/src/digest.rs (1)
digest(143-146)
bip322/src/verification.rs (1)
near-utils/src/digest.rs (1)
digest(143-146)
bip322/src/bitcoin_minimal.rs (1)
bip322/src/signature.rs (2)
from_str(84-98)from(76-78)
🪛 Ruff (0.12.2)
bip322/validate_unisat_comprehensive.py
8-8: binascii.unhexlify imported but unused
Remove unused import: binascii.unhexlify
(F401)
26-26: Do not use bare except
(E722)
56-56: ecdsa imported but unused; consider using importlib.util.find_spec to test for availability
(F401)
58-58: ecdsa.ecdsa.possible_public_keys_from_signature imported but unused; consider using importlib.util.find_spec to test for availability
(F401)
84-84: bitcoinlib.keys.Key imported but unused; consider using importlib.util.find_spec to test for availability
(F401)
135-135: Local variable point is assigned to but never used
Remove assignment to unused variable point
(F841)
204-204: f-string without any placeholders
Remove extraneous f prefix
(F541)
bip322/validate_unisat_vector.py
8-8: binascii.unhexlify imported but unused
Remove unused import: binascii.unhexlify
(F401)
27-27: Do not use bare except
(E722)
57-57: ecdsa imported but unused; consider using importlib.util.find_spec to test for availability
(F401)
58-58: ecdsa.curves.SECP256k1 imported but unused; consider using importlib.util.find_spec to test for availability
(F401)
59-59: ecdsa.ellipticcurve.Point imported but unused; consider using importlib.util.find_spec to test for availability
(F401)
120-120: Local variable sha256_hash is assigned to but never used
Remove assignment to unused variable sha256_hash
(F841)
154-154: f-string without any placeholders
Remove extraneous f prefix
(F541)
165-165: Local variable recovered_pubkey is assigned to but never used
Remove assignment to unused variable recovered_pubkey
(F841)
169-169: bech32 imported but unused; consider using importlib.util.find_spec to test for availability
(F401)
175-175: ecdsa imported but unused; consider using importlib.util.find_spec to test for availability
(F401)
🪛 GitHub Check: Format
bip322/src/hashing.rs
[warning] 147-147:
Diff in /home/runner/work/intents/intents/bip322/src/hashing.rs
[warning] 4-4:
Diff in /home/runner/work/intents/intents/bip322/src/hashing.rs
bip322/src/lib.rs
[warning] 52-52:
Diff in /home/runner/work/intents/intents/bip322/src/lib.rs
[warning] 44-44:
Diff in /home/runner/work/intents/intents/bip322/src/lib.rs
[warning] 14-14:
Diff in /home/runner/work/intents/intents/bip322/src/lib.rs
bip322/src/signature.rs
[warning] 102-102:
Diff in /home/runner/work/intents/intents/bip322/src/signature.rs
bip322/src/bitcoin_minimal.rs
[warning] 294-294:
Diff in /home/runner/work/intents/intents/bip322/src/bitcoin_minimal.rs
[warning] 257-257:
Diff in /home/runner/work/intents/intents/bip322/src/bitcoin_minimal.rs
[warning] 190-190:
Diff in /home/runner/work/intents/intents/bip322/src/bitcoin_minimal.rs
[warning] 41-41:
Diff in /home/runner/work/intents/intents/bip322/src/bitcoin_minimal.rs
⏰ 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). (7)
- GitHub Check: Shared security analysis / Static Analysis (intents-contract, contract, near, defuse, 1, ., 1.18, false, --filter-paths "nod...
- GitHub Check: Shared security analysis / Static Analysis (intents-poa-token, contract, near, poa-token, 1, ., 1.18, false, --filter-paths ...
- GitHub Check: Shared security analysis / Static Analysis (intents-poa-factory, contract, near, poa-factory, 1, ., 1.18, false, --filter-pa...
- GitHub Check: Shared / Static Analysis (intents-poa-token, contract, near, poa-token, 1, ., 1.18, false, --filter-paths ...
- GitHub Check: Shared / Static Analysis (intents-contract, contract, near, defuse, 1, ., 1.18, false, --filter-paths "nod...
- GitHub Check: Shared / Static Analysis (intents-poa-factory, contract, near, poa-factory, 1, ., 1.18, false, --filter-pa...
- GitHub Check: Security Audit - report
🔇 Additional comments (8)
near-utils/src/digest.rs (4)
34-70: Hash160 implementation looks solid and aligns with Bitcoin semanticsUses NEAR host functions for SHA-256 and RIPEMD-160, exposes digest traits correctly, and tests validate behavior. Good addition for BIP-322 address/pubkey flows.
71-107: Double digest wrapper is correct and efficientForwarding Update to inner digest and using finalize_fixed for the first pass before hashing again is clean. Generic design keeps it reusable (e.g., DoubleSha256).
109-126: TaggedDigest blanket impl matches tagged hashing specTag hashing as tag_hash || tag_hash || msg is the right construction. Tests confirm expected behavior.
168-181: Nice tests coverage for new utilitiesTest cases for Hash160, DoubleSha256, and tagged hashing provide good confidence. No issues spotted.
bip322/src/signature.rs (2)
195-233: Witness parsing with varint and DoS limits looks solid.Good defensive checks on witness count and item sizes, and clean varint decoding. This should be robust against malformed inputs.
412-432: Recovery-ID handling: confirm env::ecrecover semantics.You map 27–34 into 0–3 and set the malleability flag to
true. Please ensure:
- NEAR env::ecrecover expects
vin 0–3 (not 27/28), and- The malleability flag usage aligns with your security requirements.
Do you want me to add targeted unit tests with known vectors (including compressed/uncompressed flags in the header) to assert recovery correctness across these cases?
bip322/src/bitcoin_minimal.rs (2)
230-351: Base58 parsing looks correct and checksums are validated.P2PKH and P2SH branches are implemented with proper length checks, version checks, and Base58Check checksum verification. Good.
139-147:const fnwithVecis not portable; removeconstto avoid compilation issues.
Vec::new()in aconst fnisn’t guaranteed to be allowed on stable compilers, andconst fn from_stack(Vec<...>)provides no benefit. Use non-const constructors.-impl TransactionWitness { - pub const fn new() -> Self { - Self { stack: Vec::new() } - } - - pub const fn from_stack(stack: Vec<Vec<u8>>) -> Self { - Self { stack } - } -} +impl TransactionWitness { + pub fn new() -> Self { + Self { stack: Vec::new() } + } + + pub fn from_stack(stack: Vec<Vec<u8>>) -> Self { + Self { stack } + } +}Likely an incorrect or invalid review comment.
bip322/src/verification.rs
Outdated
| computed_pubkey_hash[0] == witness_program.program.as_slice() | ||
| || computed_pubkey_hash[1] == witness_program.program.as_slice() | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix equality in P2WPKH validation (raw recovered pubkey path)
As above, compare slices explicitly to avoid array-to-slice pitfalls.
Apply this diff:
- computed_pubkey_hash[0] == witness_program.program.as_slice()
- || computed_pubkey_hash[1] == witness_program.program.as_slice()
+ &computed_pubkey_hash[0][..] == witness_program.program.as_slice()
+ || &computed_pubkey_hash[1][..] == witness_program.program.as_slice()📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| computed_pubkey_hash[0] == witness_program.program.as_slice() | |
| || computed_pubkey_hash[1] == witness_program.program.as_slice() | |
| } | |
| &computed_pubkey_hash[0][..] == witness_program.program.as_slice() | |
| || &computed_pubkey_hash[1][..] == witness_program.program.as_slice() | |
| } |
🤖 Prompt for AI Agents
In bip322/src/verification.rs around lines 156 to 158, the code compares array
elements to a slice using equality which can cause array-to-slice mismatches;
change the comparisons to compare slices explicitly by calling .as_slice() on
the computed_pubkey_hash entries so the condition becomes
computed_pubkey_hash[0].as_slice() == witness_program.program.as_slice() ||
computed_pubkey_hash[1].as_slice() == witness_program.program.as_slice().
There was a problem hiding this 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
♻️ Duplicate comments (3)
bip322/src/signature.rs (2)
373-387: Do not return zero-filled placeholder keys for compressed pubkeysFabricating
[0u8; 64]on “validation success” is dangerous and can leak fake key material downstream. ReturnNoneunless you can produce a real key.Apply this diff:
ParsedPublicKey::Compressed(compressed) => { // Validate compressed public key against address if crate::verification::validate_compressed_pubkey_matches_address( compressed, address, ) { - // Validation succeeded, but we cannot provide uncompressed format - // This indicates a successful verification but inability to decompress - // For now, we'll create a placeholder uncompressed key to indicate success - // TODO: Implement proper decompression or change API to accept compressed keys - Some([0u8; 64]) // Placeholder indicating successful validation + // We should not fabricate key bytes. + // TODO: either decompress or change API to accept compressed keys. + None } else { None } }
450-473: Fix Bitcoin Signed Message hash: missing CompactSize(prefix) before the prefixWithout writing the CompactSize length of the prefix prior to the prefix, standard wallet signatures won’t verify.
Apply this diff:
// Create the full message with prefix and length let mut full_message = Vec::new(); - full_message.extend_from_slice(prefix); + // Write CompactSize length of the prefix first + Self::encode_varint(prefix.len() as u64, &mut full_message); + full_message.extend_from_slice(prefix); // Add message length as proper varint Self::encode_varint(message_bytes.len() as u64, &mut full_message);bip322/src/bitcoin_minimal.rs (1)
517-595: Consensus encoding useswrite(...); switch towrite_all(...)to avoid partial writes
Write::writeis allowed to perform partial writes. For consensus serialization this is unsafe and can produce truncated encodings depending on the writer.Apply the pattern below throughout this function (illustrative subset):
- // Version (4 bytes, little-endian) - len += writer.write(&self.version.to_le_bytes())?; + // Version (4 bytes, little-endian) + writer.write_all(&self.version.to_le_bytes())?; + len += 4; @@ - if has_witness { - len += writer.write(&[0x00])?; // Marker byte - len += writer.write(&[0x01])?; // Flag byte - } + if has_witness { + writer.write_all(&[0x00, 0x01])?; // Marker and flag + len += 2; + } @@ - len += writer.write(&input.previous_output.txid.0)?; - len += writer.write(&input.previous_output.vout.to_le_bytes())?; + writer.write_all(&input.previous_output.txid.0)?; + writer.write_all(&input.previous_output.vout.to_le_bytes())?; + len += 32 + 4; @@ - len += writer.write(&input.script_sig.inner)?; + writer.write_all(&input.script_sig.inner)?; + len += input.script_sig.inner.len(); @@ - len += writer.write(&input.sequence.to_le_bytes())?; + writer.write_all(&input.sequence.to_le_bytes())?; + len += 4; @@ - len += writer.write(&output.value.to_le_bytes())?; + writer.write_all(&output.value.to_le_bytes())?; + len += 8; @@ - len += writer.write(&output.script_pubkey.inner)?; + writer.write_all(&output.script_pubkey.inner)?; + len += output.script_pubkey.inner.len(); @@ - len += writer.write(witness_item)?; + writer.write_all(witness_item)?; + len += witness_item.len(); @@ - len += writer.write(&self.lock_time.to_le_bytes())?; + writer.write_all(&self.lock_time.to_le_bytes())?; + len += 4;Alternatively, drop manual
lentracking and returnOk(()), since deterministic lengths can be recomputed by callers when needed.
🧹 Nitpick comments (4)
tests/src/utils/crypto.rs (1)
16-17: Prefer borrowing over owning for message parameter in test SignerTaking
message: &stravoids an allocation and matches other signer methods that accept payloads by reference/value. Low impact in tests but cleaner.bip322/src/signature.rs (1)
84-98: Be explicit about accepted compact formats and recovery IDsCurrently only 65-byte base64 signatures are accepted as “compact”. Many wallets emit DER-encoded signatures. If wallet compatibility is a goal, consider supporting DER + recovery-ID derivation, or clarify that only Bitcoin Core-style 65-byte compact signatures are accepted.
Would you like a follow-up patch to support DER parsing and recovery ID search (0..=3)?
bip322/src/bitcoin_minimal.rs (2)
11-23: Docs out of date: module already supports P2SH and P2WSHThe “MVP Focus” section claims only P2PKH/P2WPKH support, but the code implements P2SH and P2WSH too. Update to reflect current capabilities.
386-416: Bech32 HRP validation: avoid unwrap and use a constant HRPMinor: replace
Hrp::parse("bc").unwrap()with a compile-time parse or checked parse once to avoid potential panics in future refactors.Example:
// top-level const MAINNET_HRP: Hrp = Hrp::parse_unchecked("bc"); // ... if hrp != MAINNET_HRP { return Err(AddressError::InvalidBech32); }
📜 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.
⛔ Files ignored due to path filters (1)
bip322/unisat-failure.pngis excluded by!**/*.png
📒 Files selected for processing (9)
bip322/src/bitcoin_minimal.rs(1 hunks)bip322/src/hashing.rs(1 hunks)bip322/src/lib.rs(1 hunks)bip322/src/signature.rs(1 hunks)bip322/src/tests.rs(1 hunks)bip322/src/transaction.rs(1 hunks)bip322/src/verification.rs(1 hunks)near-utils/src/digest.rs(3 hunks)tests/src/utils/crypto.rs(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- bip322/src/transaction.rs
- bip322/src/hashing.rs
- bip322/src/tests.rs
- bip322/src/verification.rs
🧰 Additional context used
🧬 Code Graph Analysis (3)
bip322/src/lib.rs (3)
core/src/payload/multi.rs (2)
hash(67-78)verify(85-96)bip322/src/signature.rs (1)
from_str(84-98)bip322/src/bitcoin_minimal.rs (1)
from_str(227-351)
bip322/src/signature.rs (4)
bip322/src/transaction.rs (2)
create_to_sign(101-141)create_to_spend(32-76)bip322/src/bitcoin_minimal.rs (5)
from(841-845)from_str(227-351)new(139-141)new(425-427)new(456-458)bip322/src/verification.rs (2)
validate_pubkey_matches_address(23-37)validate_compressed_pubkey_matches_address(52-90)bip322/src/hashing.rs (2)
compute_message_hash(61-74)compute_bip322_message_hash(38-44)
bip322/src/bitcoin_minimal.rs (2)
near-utils/src/digest.rs (1)
digest(146-149)bip322/src/signature.rs (2)
from_str(84-98)from(76-78)
⏰ 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). (8)
- GitHub Check: Check
- GitHub Check: Shared security analysis / Static Analysis (intents-poa-factory, contract, near, poa-factory, 1, ., 1.18, false, --filter-pa...
- GitHub Check: Shared / Static Analysis (intents-poa-factory, contract, near, poa-factory, 1, ., 1.18, false, --filter-pa...
- GitHub Check: Shared security analysis / Static Analysis (intents-contract, contract, near, defuse, 1, ., 1.18, false, --filter-paths "nod...
- GitHub Check: Shared security analysis / Static Analysis (intents-poa-token, contract, near, poa-token, 1, ., 1.18, false, --filter-paths ...
- GitHub Check: Shared / Static Analysis (intents-poa-token, contract, near, poa-token, 1, ., 1.18, false, --filter-paths ...
- GitHub Check: Shared / Static Analysis (intents-contract, contract, near, defuse, 1, ., 1.18, false, --filter-paths "nod...
- GitHub Check: Security Audit - report
🔇 Additional comments (9)
near-utils/src/digest.rs (9)
1-4: Imports look correct and scoped appropriatelyThe selected traits and const sizes are exactly what's used below. No unnecessary imports spotted.
37-70: HASH160 implementation (RIPEMD160(SHA256(data))) is correct and leverages NEAR host functionsThe two-pass hashing matches Bitcoin’s HASH160. Using env::{sha256_array,ripemd160_array} aligns with the stated gas-efficiency goal.
74-98: GenericDouble<D>wrapper design looks good
- Update proxies to the inner digest as expected.
- Output size is correctly inherited from
D.- Using a fresh default hasher for the second pass mirrors standard double-hash semantics.
Once the
chain_updatefix is applied (see above), this is solid.
112-123: Tagged hashing API fits BIP-style domain separationThe
TaggedDigesttrait and default impl viaSelf::digest(tag)-> prepend tag hash twice are idiomatic and match BIP-340/322 tagged hashing construction.
131-135:DoubleSha256alias is appropriate and clearThe type alias concisely ties the generic wrapper to the NEAR-backed
Sha256.
151-159: HASH160 test correctly mirrors the two-pass constructionThe expected value matches RIPEMD160(SHA256(random_bytes)) via the same NEAR host functions.
161-169: Double-SHA256 test validates both passes correctlyThe test structure is correct and ensures the wrapper composes two host-function-backed passes.
171-187: Tagged hashing test is accurateBuilding expected = SHA256(tag_hash || tag_hash || data) matches the implementation of
TaggedDigest::tagged. Good coverage.
124-130: No accidental usages of DoubleSha256::tagged foundI ran a repo-wide search for any calls to
DoubleSha256::tagged(...)(and more genericDouble<…>::tagged(...)) and found no matches. There’s no evidence that the blanketimpl<D: Digest> TaggedDigest for Dis being invoked onDoubleSha256in the current codebase.No further action needed here.
There was a problem hiding this 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
♻️ Duplicate comments (4)
bip322/src/verification.rs (4)
58-66: Fix array-to-slice comparison and validate P2WPKH witness program format.Current comparison uses [u8; 20] == &[u8], which is brittle. Also validate segwit v0 20-byte program.
- Address::P2WPKH { witness_program } => { - let computed_hash: [u8; 20] = - defuse_near_utils::digest::Hash160::digest(compressed_pubkey).into(); - computed_hash == witness_program.program.as_slice() - } + Address::P2WPKH { witness_program } => { + // Validate program: v0 and 20 bytes + if witness_program.version != 0 || witness_program.program.len() != 20 { + return false; + } + let computed_hash: [u8; 20] = + defuse_near_utils::digest::Hash160::digest(compressed_pubkey).into(); + &computed_hash[..] == witness_program.program.as_slice() + }
82-88: Fix array-to-slice comparison and validate P2WSH witness program format.Ensure v0/32-byte program and compare slices.
- Address::P2WSH { witness_program } => { + Address::P2WSH { witness_program } => { + if witness_program.version != 0 || witness_program.program.len() != 32 { + return false; + } let pubkey_hash: [u8; 20] = defuse_near_utils::digest::Hash160::digest(compressed_pubkey).into(); let witness_script = build_witness_script(&pubkey_hash); let computed_script_hash = env::sha256_array(&witness_script); - computed_script_hash == witness_program.program.as_slice() + &computed_script_hash[..] == witness_program.program.as_slice() }
165-167: Fix P2WPKH raw recovered pubkey path: slice comparison and program validation.Add v0/20-byte check and compare as slices.
- computed_pubkey_hash[0] == witness_program.program.as_slice() - || computed_pubkey_hash[1] == witness_program.program.as_slice() + if witness_program.version != 0 || witness_program.program.len() != 20 { + return false; + } + &computed_pubkey_hash[0][..] == witness_program.program.as_slice() + || &computed_pubkey_hash[1][..] == witness_program.program.as_slice()
205-223: Fix slice comparisons and add P2WSH format checks (raw pubkey path).Align with compressed path: validate (v0, 32 bytes) and compare slices.
- let computed_script_hash = env::sha256_array(&witness_script); - if computed_script_hash == witness_program.program.as_slice() { + if witness_program.version != 0 || witness_program.program.len() != 32 { + return false; + } + let computed_script_hash = env::sha256_array(&witness_script); + if &computed_script_hash[..] == witness_program.program.as_slice() { return true; } @@ - let computed_script_hash = env::sha256_array(&witness_script); - if computed_script_hash == witness_program.program.as_slice() { + let computed_script_hash = env::sha256_array(&witness_script); + if &computed_script_hash[..] == witness_program.program.as_slice() { return true; } @@ - let computed_script_hash = env::sha256_array(&witness_script); - computed_script_hash == witness_program.program.as_slice() + let computed_script_hash = env::sha256_array(&witness_script); + &computed_script_hash[..] == witness_program.program.as_slice()
🧹 Nitpick comments (6)
bip322/README.md (1)
75-87: Clarify P2SH support scope (legacy vs nested SegWit).For P2SH, verification depends on the redeem script, which is not derivable from the address alone. The code path currently cannot infer nested P2SH-P2WPKH from the address and needs data from the signature/witness to compute the correct sighash. Please add a short note in README explaining that:
- P2SH-P2WPKH is supported via full signatures (redeem script contained within the signature).
- Legacy P2SH redeem scripts are only supported where the redeem script can be reconstructed (e.g., P2PKH template), otherwise caller must provide it.
bip322/src/hashing.rs (2)
94-107: P2SH with legacy sighash likely incorrect scriptCode.compute_legacy_sighash() uses to_spend.output[0].script_pubkey as script_code. For P2SH with legacy signing, the scriptCode should be the redeem script, not the P2SH scriptPubKey. If you keep this function generic, add a note/assertion preventing its use for P2SH, or accept an explicit script_code parameter.
- let script_code = &to_spend.output.first().expect("to_spend should have output").script_pubkey; + let script_code = &to_spend + .output + .first() + .expect("to_spend should have output") + .script_pubkey; + // Note: For P2SH legacy sighash, script_code must be the redeem script (not the P2SH script_pubkey). + // Callers should pass the correct script_code via a context-aware API.
102-105: Avoid panics in library code; return errors instead.The .expect(...) calls will panic on malformed inputs. Prefer returning a Result from the hash computation functions and propagating errors to callers, especially since this can run in contract contexts where panics are costly.
Also applies to: 169-171
bip322/src/tests.rs (3)
296-316: Test name/comment mismatch: not actually testing “wrong witness type”.The test constructs a P2PKH address with a zeroed compact signature. It doesn’t construct a mismatched witness type. Consider renaming the test or updating the scenario to build a full-format signature with an incompatible witness.
-fn test_signature_verification_wrong_witness_type() { +fn test_signature_verification_invalid_compact_signature() {
340-361: Misleading test name: length is valid; content is invalid.65 bytes is the correct length for a compact signature. Either rename the test or provide an invalid-length signature (e.g., 64 bytes) to actually test length handling.
- fn test_signature_verification_invalid_signature_length() { + fn test_signature_verification_invalid_signature() { @@ - let invalid_signature = [0u8; 65]; // Valid 65-byte signature (but empty, so will fail) + // Invalid: wrong size to exercise length validation + let invalid_signature = [0u8; 64];
535-576: Strengthen “working vectors” test to fail on unexpected failures.Currently failures only log and don’t fail the test. If these are meant to be “working”, assert success so regressions are caught. If you want a softer guardrail, at least count failures and assert they’re zero.
- match payload.verify() { - Some(_pubkey) => { - println!("✓ Working vector {i} verified successfully"); - } - None => { - println!( - "✗ Working vector {i} failed verification (might need implementation fixes)" - ); - // Don't panic here since we might have implementation issues to fix - } - } + assert!( + payload.verify().is_some(), + "Working vector {i} failed verification: {}", + vector.description + );
📜 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.
📒 Files selected for processing (9)
bip322/README.md(1 hunks)bip322/src/bitcoin_minimal.rs(1 hunks)bip322/src/hashing.rs(1 hunks)bip322/src/lib.rs(1 hunks)bip322/src/signature.rs(1 hunks)bip322/src/tests.rs(1 hunks)bip322/src/transaction.rs(1 hunks)bip322/src/verification.rs(1 hunks)tests/src/utils/crypto.rs(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
- tests/src/utils/crypto.rs
- bip322/src/transaction.rs
- bip322/src/lib.rs
- bip322/src/signature.rs
- bip322/src/bitcoin_minimal.rs
🧰 Additional context used
🧬 Code Graph Analysis (3)
bip322/src/tests.rs (5)
bip322/src/transaction.rs (2)
compute_tx_id(151-157)create_to_sign(101-137)bip322/src/bitcoin_minimal.rs (6)
new(144-146)new(436-438)new(467-469)from_str(232-356)all_zeros(450-452)from_byte_array(454-456)bip322/src/signature.rs (1)
from_str(84-98)bip322/src/hashing.rs (1)
compute_bip322_message_hash(38-44)bip322/src/lib.rs (2)
hash(45-48)verify(54-60)
bip322/src/hashing.rs (3)
near-utils/src/digest.rs (3)
digest(146-149)tagged(121-121)tagged(125-128)bip322/src/signature.rs (1)
compute_message_hash(434-450)bip322/src/bitcoin_minimal.rs (1)
from_bytes(440-442)
bip322/src/verification.rs (1)
near-utils/src/digest.rs (1)
digest(146-149)
🪛 LanguageTool
bip322/README.md
[grammar] ~5-~5: There might be a mistake here.
Context: ...EAR blockchain ecosystem. ## 🎯 Purpose This module provides **complete BIP-322 s...
(QB_NEW_EN)
[grammar] ~12-~12: There might be a mistake here.
Context: ... using only NEAR SDK host functions - 📋 Wide Coverage: Supports most major Bi...
(QB_NEW_EN)
[grammar] ~13-~13: There might be a mistake here.
Context: ...types (P2PKH, P2SH, P2WPKH, P2WSH) - ⚡ Gas Optimized: Minimal gas consumption...
(QB_NEW_EN)
[grammar] ~14-~14: There might be a mistake here.
Context: ...gh efficient NEAR SDK integration - 🔒 Security Focused: Comprehensive valida...
(QB_NEW_EN)
[grammar] ~15-~15: There might be a mistake here.
Context: ...ation with proper error handling - 🧪 Well Tested: Extensive test suite with ...
(QB_NEW_EN)
[grammar] ~17-~17: There might be a mistake here.
Context: ...fficial BIP-322 reference vectors ## 🏗️ Architecture ### Core Components - **`...
(QB_NEW_EN)
[grammar] ~17-~17: There might be a mistake here.
Context: ...2 reference vectors ## 🏗️ Architecture ### Core Components - lib.rs: Main `Si...
(QB_NEW_EN)
[grammar] ~21-~21: There might be a mistake here.
Context: ...nd SignedPayload trait implementations - signature.rs: BIP-322 signature parsing and verifica...
(QB_NEW_EN)
[grammar] ~22-~22: There might be a mistake here.
Context: ...signature parsing and verification logic - bitcoin_minimal.rs: Minimal Bitcoin types optimized for BI...
(QB_NEW_EN)
[grammar] ~23-~23: There might be a mistake here.
Context: ...P-322 (transactions, addresses, scripts) - hashing.rs: BIP-322 message hash computation with ...
(QB_NEW_EN)
[grammar] ~24-~24: There might be a mistake here.
Context: ...h computation with proper tagged hashing - transaction.rs: BIP-322 "to_spend" and "to_sign" trans...
(QB_NEW_EN)
[grammar] ~25-~25: There might be a mistake here.
Context: ..." and "to_sign" transaction construction - verification.rs: Address validation and public key reco...
(QB_NEW_EN)
[grammar] ~42-~42: There might be a mistake here.
Context: ...se64 signature decoding ## 🚀 Usage rust use defuse_bip322::SignedBip322Payload; use defuse_crypto::SignedPayload; // Parse and verify a BIP-322 signature let payload = SignedBip322Payload { address: "bc1q9vza2e8x573nczrlzms0wvx3gsqjx7vavgkx0l".parse()?, message: "Hello Bitcoin!".to_string(), signature: "AkcwRAIgeGl4sSPd7zEIvhxdN8GgP4vgSqA8TdyPMeIpCF4gqgE4AiBsjQd0D1OFxdnHQPNOI1YdGlBD6kEOGRnHhcAkHnxUcAH=".parse()?, }; // Verify signature and extract public key if let Some(public_key) = payload.verify() { println!("✅ Valid BIP-322 signature!"); println!("🔑 Public key: {:?}", public_key); } else { println!("❌ Invalid signature"); } ``` ## 📊 Supported Features ### ✅ Address Type...
(QB_NEW_EN)
[grammar] ~64-~64: There might be a mistake here.
Context: ...ature"); } ``` ## 📊 Supported Features ### ✅ Address Types (Mainnet Only) | Type | ...
(QB_NEW_EN)
[grammar] ~68-~68: There might be a mistake here.
Context: ...) | Type | Format | Example | Support | |------|--------|---------|---------| | ...
(QB_NEW_EN)
[grammar] ~69-~69: There might be a mistake here.
Context: ... | |------|--------|---------|---------| | P2PKH | Legacy addresses starting ...
(QB_NEW_EN)
[grammar] ~70-~70: There might be a mistake here.
Context: ...Gefi2DMPTfTL5SLmv7DivfNa` | ✅ Complete | | P2SH | Script addresses starting w...
(QB_NEW_EN)
[grammar] ~71-~71: There might be a mistake here.
Context: ...iAE6uzMj2ZifT9YgRrkSgzQX` | ✅ Complete | | P2WPKH | Bech32 addresses starting...
(QB_NEW_EN)
[grammar] ~72-~72: There might be a mistake here.
Context: ...rlzms0wvx3gsqjx7vavgkx0l` | ✅ Complete | | P2WSH | Bech32 script addresses (3...
(QB_NEW_EN)
[grammar] ~77-~77: There might be a mistake here.
Context: ...: 65-byte compact format (P2PKH, P2WPKH) - Full Signatures: Complete BIP-322 witn...
(QB_NEW_EN)
[grammar] ~78-~78: There might be a mistake here.
Context: ...P-322 witness stack format (P2SH, P2WSH) - Automatic Detection: Parses both forma...
(QB_NEW_EN)
[grammar] ~83-~83: There might be a mistake here.
Context: ...-signed-message" tagged hash computation - Transaction Structure: Correct "to_spe...
(QB_NEW_EN)
[grammar] ~84-~84: There might be a mistake here.
Context: ..." and "to_sign" transaction construction - Witness Handling: Complete witness sta...
(QB_NEW_EN)
[grammar] ~85-~85: There might be a mistake here.
Context: ...ete witness stack parsing and validation - Address Validation: Full address forma...
(QB_NEW_EN)
[grammar] ~88-~88: There might be a mistake here.
Context: ...n ## 🔍 Discovered Issues & Limitations During implementation and testing, severa...
(QB_NEW_EN)
[grammar] ~96-~96: There might be a mistake here.
Context: ...e not currently supported. Details: - P2TR uses Taproot (BIP-341) with differe...
(QB_NEW_EN)
[grammar] ~108-~108: There might be a mistake here.
Context: ...s compressed 33-byte keys. Details: - NEAR SDK ecrecover returns 64-byte unc...
(QB_NEW_EN)
[grammar] ~111-~111: There might be a mistake here.
Context: ...hem. Implementation of the decompression inside contract is computationally inten...
(QB_NEW_EN)
[grammar] ~112-~112: There might be a mistake here.
Context: ...es not provide a way to uncompress keys. - See TODO at `bip322/src/signature.rs:384...
(QB_NEW_EN)
[grammar] ~115-~115: There might be a mistake here.
Context: ...signature.rs:384` Current Behavior: - Compressed key validation works correctl...
(QB_NEW_EN)
[grammar] ~133-~133: There might be a mistake here.
Context: ...tion Results** (see validation scripts): 1. Python Verification: External Bitcoin ...
(QB_NEW_EN)
[grammar] ~138-~138: There might be a mistake here.
Context: ...to the given address Evidence: See unisat-failure.png - screenshot showing verif...
(QB_NEW_EN)
[grammar] ~140-~140: There might be a mistake here.
Context: ... appears to be invalid, possibly due to: - Incorrect signature generation by the wa...
(QB_NEW_EN)
[grammar] ~141-~141: There might be a mistake here.
Context: ...rrect signature generation by the wallet - Wrong message format during signing - Co...
(QB_NEW_EN)
[grammar] ~142-~142: There might be a mistake here.
Context: ...et - Wrong message format during signing - Copy/paste errors in the test vector **...
(QB_NEW_EN)
[grammar] ~147-~147: There might be a mistake here.
Context: ...ted as expecting failure. ## 🧪 Testing The module includes comprehensive testing...
(QB_NEW_EN)
[grammar] ~153-~153: There might be a mistake here.
Context: ...g, message hashing, transaction building - Integration Tests: End-to-end signatur...
(QB_NEW_EN)
[grammar] ~154-~154: There might be a mistake here.
Context: ...-to-end signature verification workflows - Reference Vectors: Official BIP-322 te...
(QB_NEW_EN)
[grammar] ~155-~155: There might be a mistake here.
Context: ...-322 test vectors from the specification - Edge Cases: Invalid signatures, malfor...
(QB_NEW_EN)
[grammar] ~160-~160: There might be a mistake here.
Context: .../29 tests passing** (98.6% success rate) - 1 test ignored (invalid Unisat vector) -...
(QB_NEW_EN)
[grammar] ~161-~161: There might be a mistake here.
Context: ...- 1 test ignored (invalid Unisat vector) - All official BIP-322 reference vectors p...
(QB_NEW_EN)
[grammar] ~166-~166: There might be a mistake here.
Context: ...valid cases ## 📄 Standards Compliance - ✅ BIP-322: Complete implementation of G...
(QB_NEW_EN)
[grammar] ~168-~168: There might be a mistake here.
Context: ...ntation of Generic Signed Message Format - ✅ BIP-143: Segwit transaction digest a...
(QB_NEW_EN)
[grammar] ~169-~169: There might be a mistake here.
Context: ...3**: Segwit transaction digest algorithm - ✅ Base58Check: Legacy address encoding...
(QB_NEW_EN)
[grammar] ~170-~170: There might be a mistake here.
Context: ...*: Legacy address encoding (P2PKH, P2SH) - ✅ Bech32: Segwit address encoding (P2W...
(QB_NEW_EN)
[grammar] ~173-~173: There might be a mistake here.
Context: ...oding (P2WPKH, P2WSH) ## 🤝 Integration This module integrates seamlessly with th...
(QB_NEW_EN)
⏰ 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). (8)
- GitHub Check: Check
- GitHub Check: Shared security analysis / Static Analysis (intents-poa-token, contract, near, poa-token, 1, ., 1.18, false, --filter-paths ...
- GitHub Check: Shared security analysis / Static Analysis (intents-poa-factory, contract, near, poa-factory, 1, ., 1.18, false, --filter-pa...
- GitHub Check: Shared security analysis / Static Analysis (intents-contract, contract, near, defuse, 1, ., 1.18, false, --filter-paths "nod...
- GitHub Check: Shared / Static Analysis (intents-poa-token, contract, near, poa-token, 1, ., 1.18, false, --filter-paths ...
- GitHub Check: Shared / Static Analysis (intents-contract, contract, near, defuse, 1, ., 1.18, false, --filter-paths "nod...
- GitHub Check: Shared / Static Analysis (intents-poa-factory, contract, near, poa-factory, 1, ., 1.18, false, --filter-pa...
- GitHub Check: Security Audit - report
🔇 Additional comments (5)
bip322/README.md (1)
66-74: P2WSH “Complete” claim is inconsistent with code (hashing panics for P2WSH).In the code, compute_segwit_v0_sighash() explicitly panics for P2WSH because the witness script is required and not derivable from the address. Either implement a P2WSH path that accepts/provides the witness script to the hasher, or adjust the README to state “Partial” support with a clear note that the witness script must be supplied by the caller (e.g., via the full signature).
Suggested README tweak:
-| **P2WSH** | Bech32 script addresses (32-byte) | `bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3` | ✅ Complete | +| **P2WSH** | Bech32 script addresses (32-byte) | `bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3` | ⚠️ Partial (requires witness script) |Would you like me to implement a minimal API change to pass the witness script into the hasher and update the verification path accordingly?
bip322/src/hashing.rs (1)
124-159: P2WSH path currently panics; provide a non-panicking API or accept witness script.The P2WSH arm panics because the witness script is not derivable from the address. If README claims P2WSH support, you should:
- Accept the witness script (from the full signature) and construct scriptCode accordingly, or
- Return a Result and surface a clear Unsupported/Invalid error instead of panicking.
Minimal patch to return a Result (propagate up), if you opt not to extend the context now:
-pub fn compute_segwit_v0_sighash( +pub fn compute_segwit_v0_sighash( to_spend: &Transaction, to_sign: &Transaction, address: &Address, -) -> near_sdk::CryptoHash { +) -> near_sdk::CryptoHash { // ... - Address::P2WSH { .. } => { - panic!( - "compute_segwit_v0_sighash: P2WSH requires the witness script (not derivable from address)" - ) - } + Address::P2WSH { .. } => { + // TODO: Plumb witness script via an extended API; for now, unreachable + unreachable!("P2WSH requires witness script; use the extended API passing scriptCode"); + }Preferably, adopt the context-based API from the previous comment to truly support P2WSH.
bip322/src/verification.rs (1)
169-195: P2SH assumptions differ between raw and compressed paths; document or generalize.
- validate_p2sh_address() (raw path) assumes P2SH-P2PKH (redeem script = P2PKH template).
- validate_compressed_pubkey_matches_address() (compressed path) assumes P2SH-P2WPKH (redeem script = v0 P2WPKH program).
This inconsistency can confuse future maintenance and lead to silent failures for other P2SH scripts. Either:
- Explicitly document accepted P2SH variants and reject others, or
- Accept the redeem script (or witness program) from the signature and validate generically.
bip322/src/tests.rs (2)
439-446: Vector expectation contradicts documented Unisat failure.This vector is marked invalid in README and in ignored tests, but here it’s expected to verify. Flip expected_verification to false or exclude it from “working” vectors.
- expected_verification: true, - description: "P2WPKH JSON message (working example)", + expected_verification: false, + description: "P2WPKH JSON message (Unisat vector; known invalid)",
730-742: These reference tests implicitly require correct P2SH-P2WPKH/P2WSH handling in hashing.Given the hasher currently selects legacy sighash for all P2SH and panics for P2WSH, these tests will fail unless the verification path bypasses compute_message_hash() or passes the correct scriptCode. Ensure signature.rs uses a context-aware hasher (see hashing.rs comment) or otherwise provides the necessary script data.
Also applies to: 744-757, 759-772, 774-787, 789-802
There was a problem hiding this 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
♻️ Duplicate comments (9)
bip322/README.md (1)
11-16: Clarify dependency claim to avoid misleading readers"Zero-dependency cryptography" can be interpreted as “no dependencies,” yet the crate uses bs58, bech32, and base64 (as correctly listed below). Recommend rewording to “No external crypto libraries” and call out minimal, non-crypto encoding deps.
Apply this diff:
- - **🛡️ Production Ready**: Zero-dependency cryptography using only NEAR SDK host functions + - **🛡️ Production Ready**: No external crypto libraries — all cryptography uses NEAR SDK host functionsbip322/src/verification.rs (4)
156-167: Validate witness program format and compare slices in P2WPKH (recovered key path)Add version/length checks and avoid array-to-slice equality pitfalls.
Apply this diff:
fn validate_p2wpkh_address( recovered_pubkey: &[u8; 64], witness_program: &crate::bitcoin_minimal::WitnessProgram, ) -> bool { + // Enforce segwit v0 P2WPKH (20-byte program) + if witness_program.version != 0 || witness_program.program.len() != 20 { + return false; + } // P2WPKH addresses always use compressed public keys, so two possibilities, // depending on the y coordinate parity let computed_pubkey_hash = compute_pubkey_hash160_all(recovered_pubkey, true); - computed_pubkey_hash[0] == witness_program.program.as_slice() - || computed_pubkey_hash[1] == witness_program.program.as_slice() + &computed_pubkey_hash[0][..] == witness_program.program.as_slice() + || &computed_pubkey_hash[1][..] == witness_program.program.as_slice() }
197-223: Validate witness program format and compare slices in P2WSH (recovered key path)Ensure v0/32-byte program and slice-to-slice comparisons to prevent accidental matches.
Apply this diff:
fn validate_p2wsh_address( recovered_pubkey: &[u8; 64], witness_program: &crate::bitcoin_minimal::WitnessProgram, ) -> bool { + // Enforce segwit v0 P2WSH (32-byte program) + if witness_program.version != 0 || witness_program.program.len() != 32 { + return false; + } // Try uncompressed first let pubkey_hash = compute_pubkey_hash160_all(recovered_pubkey, false); let witness_script = build_witness_script(&pubkey_hash[0]); let computed_script_hash = env::sha256_array(&witness_script); - if computed_script_hash == witness_program.program.as_slice() { + if &computed_script_hash[..] == witness_program.program.as_slice() { return true; } // Try compressed next let pubkey_hash = compute_pubkey_hash160_all(recovered_pubkey, true); let witness_script = build_witness_script(&pubkey_hash[0]); let computed_script_hash = env::sha256_array(&witness_script); - if computed_script_hash == witness_program.program.as_slice() { + if &computed_script_hash[..] == witness_program.program.as_slice() { return true; } let witness_script = build_witness_script(&pubkey_hash[1]); let computed_script_hash = env::sha256_array(&witness_script); - computed_script_hash == witness_program.program.as_slice() + &computed_script_hash[..] == witness_program.program.as_slice() }
101-121: P2PKH/uncompressed hash160 is wrong (missing 0x04 prefix)Bitcoin’s uncompressed pubkey serialization is 65 bytes: 0x04 || X(32) || Y(32). Hashing the bare 64-byte X||Y yields the wrong hash160 and breaks P2PKH matches for uncompressed keys.
Apply this diff:
fn compute_pubkey_hash160_all(raw_pubkey: &[u8; 64], compressed: bool) -> Vec<[u8; 20]> { @@ - vec![defuse_near_utils::digest::Hash160::digest(raw_pubkey).into()] + // Uncompressed serialization: 0x04 || X(32) || Y(32) + let mut uncompressed = Vec::with_capacity(65); + uncompressed.push(0x04); + uncompressed.extend_from_slice(raw_pubkey); + vec![defuse_near_utils::digest::Hash160::digest(&uncompressed).into()] }
56-89: Strengthen P2WPKH/P2WSH checks for compressed key path (validate version/length; compare slices)Currently compares arrays to slices directly and doesn’t validate witness program version/length. Add explicit checks and slice-to-slice equality.
Apply this diff:
pub fn validate_compressed_pubkey_matches_address( compressed_pubkey: &[u8; 33], address: &Address, ) -> bool { match address { Address::P2PKH { pubkey_hash } => { let computed_hash: [u8; 20] = defuse_near_utils::digest::Hash160::digest(compressed_pubkey).into(); - computed_hash == *pubkey_hash + computed_hash == *pubkey_hash } Address::P2WPKH { witness_program } => { + // Enforce segwit v0 P2WPKH format (20-byte program) + if witness_program.version != 0 || witness_program.program.len() != 20 { + return false; + } let computed_hash: [u8; 20] = defuse_near_utils::digest::Hash160::digest(compressed_pubkey).into(); - computed_hash == witness_program.program.as_slice() + &computed_hash[..] == witness_program.program.as_slice() } Address::P2SH { script_hash } => { let pubkey_hash: [u8; 20] = defuse_near_utils::digest::Hash160::digest(compressed_pubkey).into(); @@ let computed_script_hash: [u8; 20] = defuse_near_utils::digest::Hash160::digest(&witness_program).into(); computed_script_hash == *script_hash } Address::P2WSH { witness_program } => { + // Enforce segwit v0 P2WSH format (32-byte program) + if witness_program.version != 0 || witness_program.program.len() != 32 { + return false; + } let pubkey_hash: [u8; 20] = defuse_near_utils::digest::Hash160::digest(compressed_pubkey).into(); let witness_script = build_witness_script(&pubkey_hash); let computed_script_hash = env::sha256_array(&witness_script); - computed_script_hash == witness_program.program.as_slice() + &computed_script_hash[..] == witness_program.program.as_slice() } } }bip322/src/signature.rs (3)
38-45: Doc mismatch: Compact uses classic Bitcoin message hashing, not BIP-340 tagged hashImplementation hashes using “Bitcoin Signed Message” double-SHA256, not BIP-340 tagged hash. Update docs for accuracy.
Apply this diff:
/// Simple/Compact signature format (65 bytes: recovery + r + s). /// - /// This is the standard Bitcoin message signing format used by most wallets. - /// For BIP-322 simple signatures, the message is hashed directly with BIP-340 - /// tagged hash, not through transaction construction. + /// This is the standard Bitcoin message signing format used by most wallets. + /// For compact signatures, the message is hashed using the classic Bitcoin + /// Signed Message format (double SHA256 over + /// CompactSize(len(prefix)) + prefix + CompactSize(message.len()) + message), + /// not through transaction construction.
452-478: Bitcoin Signed Message hash missing CompactSize length of the prefix (signatures won’t verify)The hash must include CompactSize(len("Bitcoin Signed Message:\n")) before the prefix. Without it, standard compact signatures from wallets won’t verify.
Apply this diff:
/// Compute standard Bitcoin message signing hash. /// /// This follows the Bitcoin Core format: - /// Hash = SHA256(SHA256("Bitcoin Signed Message:\n" + varint(message.len()) + message)) + /// Hash = SHA256(SHA256(CompactSize(len(prefix)) + prefix + /// + CompactSize(message.len()) + message)) fn compute_bitcoin_message_hash(message: &str) -> [u8; 32] { use defuse_near_utils::digest::DoubleSha256; use digest::Digest; // Bitcoin message signing format let prefix = b"Bitcoin Signed Message:\n"; let message_bytes = message.as_bytes(); // Create the full message with prefix and length let mut full_message = Vec::new(); - full_message.extend_from_slice(prefix); + // Add CompactSize(len(prefix)) followed by the prefix + Self::encode_varint( + u64::try_from(prefix.len()).unwrap_or(0), + &mut full_message, + ); + full_message.extend_from_slice(prefix); // Add message length as proper varint Self::encode_varint( u64::try_from(message_bytes.len()).unwrap_or(0), &mut full_message, ); full_message.extend_from_slice(message_bytes); // Double SHA256 hash DoubleSha256::digest(&full_message).into() }
374-386: Do not return zero-filled “placeholder” keys; remove fabricated key materialReturning [0u8; 64] on success for compressed keys is unsafe and can be misused as valid key material. Either decompress or return None; consider evolving the API to return a typed enum later.
Apply this diff:
ParsedPublicKey::Compressed(compressed) => { // Validate compressed public key against address if crate::verification::validate_compressed_pubkey_matches_address( compressed, address, ) { - // Validation succeeded, but we cannot provide uncompressed format - // This indicates a successful verification but inability to decompress - // For now, we'll create a placeholder uncompressed key to indicate success - // TODO: Implement proper decompression or change API to accept compressed keys - Some([0u8; 64]) // Placeholder indicating successful validation + // Validation succeeded, but we cannot provide uncompressed format. + // TODO: Implement decompression or change API to return a typed enum + None } else { None } }Note: This will impact tests that currently expect Some(..) for compressed keys. If you prefer, I can draft a follow-up PR to introduce:
- RecoveredKey enum { Uncompressed([u8;64]), Compressed([u8;33]) }
- Wire it through SignedPayload::PublicKey.
bip322/src/bitcoin_minimal.rs (1)
527-605: Use write_all and deterministic length accounting in consensus encodingWrite can be partial; consensus serialization should use write_all and explicit byte counts. Prevents truncated encodings with non-buffered writers.
Apply this diff pattern throughout the function:
- // Version (4 bytes, little-endian) - len += writer.write(&self.version.to_le_bytes())?; + // Version (4 bytes, little-endian) + writer.write_all(&self.version.to_le_bytes())?; + len += 4; @@ - if has_witness { - len += writer.write(&[0x00])?; // Marker byte - len += writer.write(&[0x01])?; // Flag byte - } + if has_witness { + writer.write_all(&[0x00, 0x01])?; // Marker + flag + len += 2; + } @@ - len += writer.write(&input.previous_output.txid.0)?; - len += writer.write(&input.previous_output.vout.to_le_bytes())?; + writer.write_all(&input.previous_output.txid.0)?; + writer.write_all(&input.previous_output.vout.to_le_bytes())?; + len += 32 + 4; @@ - len += writer.write(&input.script_sig.inner)?; + writer.write_all(&input.script_sig.inner)?; + len += input.script_sig.inner.len(); @@ - len += writer.write(&input.sequence.to_le_bytes())?; + writer.write_all(&input.sequence.to_le_bytes())?; + len += 4; @@ - len += writer.write(&output.value.to_le_bytes())?; + writer.write_all(&output.value.to_le_bytes())?; + len += 8; @@ - len += writer.write(&output.script_pubkey.inner)?; + writer.write_all(&output.script_pubkey.inner)?; + len += output.script_pubkey.inner.len(); @@ - len += writer.write(witness_item)?; + writer.write_all(witness_item)?; + len += witness_item.len(); @@ - len += writer.write(&self.lock_time.to_le_bytes())?; + writer.write_all(&self.lock_time.to_le_bytes())?; + len += 4;Optional: drop len tracking and return Ok(()) — callers rarely need the byte count.
</blockquote></details> </blockquote></details> <details> <summary>🧹 Nitpick comments (2)</summary><blockquote> <details> <summary>bip322/src/tests.rs (1)</summary><blockquote> `340-361`: **Test name/message are misleading (length is valid; content is invalid)** This test feeds a 65-byte signature (valid compact length) filled with zeros. The failure is due to invalid signature data, not length. Rename for clarity to avoid confusion when triaging failures. Apply this diff: ```diff - fn test_signature_verification_invalid_signature_length() { + fn test_signature_verification_invalid_signature() { @@ - let invalid_signature = [0u8; 65]; // Valid 65-byte signature (but empty, so will fail) + let invalid_signature = [0u8; 65]; // Valid length, invalid content (all zeros), should fail @@ - "Invalid signature length should fail verification" + "Invalid signature should fail verification"bip322/src/bitcoin_minimal.rs (1)
13-17: Docs out of sync with implementation (P2SH/P2WSH are already supported)Design principles say “Supports only P2PKH and P2WPKH,” but this module implements P2SH and P2WSH too. Update to reflect current scope.
Apply this diff:
-//! 3. **MVP Focus**: Supports only P2PKH and P2WPKH for Phase 2-3 +//! 3. **MVP Focus**: Supports P2PKH, P2SH, P2WPKH, and P2WSH (mainnet)
📜 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.
📒 Files selected for processing (9)
bip322/README.md(1 hunks)bip322/src/bitcoin_minimal.rs(1 hunks)bip322/src/hashing.rs(1 hunks)bip322/src/lib.rs(1 hunks)bip322/src/signature.rs(1 hunks)bip322/src/tests.rs(1 hunks)bip322/src/transaction.rs(1 hunks)bip322/src/verification.rs(1 hunks)tests/src/utils/crypto.rs(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
- bip322/src/transaction.rs
- tests/src/utils/crypto.rs
- bip322/src/hashing.rs
- bip322/src/lib.rs
🧰 Additional context used
🧬 Code Graph Analysis (4)
bip322/src/tests.rs (5)
bip322/src/transaction.rs (2)
compute_tx_id(151-157)create_to_sign(101-137)bip322/src/bitcoin_minimal.rs (5)
new(144-146)new(436-438)new(467-469)from_str(232-356)from_byte_array(454-456)bip322/src/signature.rs (1)
from_str(84-98)bip322/src/hashing.rs (1)
compute_bip322_message_hash(38-44)bip322/src/lib.rs (2)
hash(45-48)verify(54-60)
bip322/src/signature.rs (4)
bip322/src/transaction.rs (2)
create_to_sign(101-137)create_to_spend(32-76)bip322/src/bitcoin_minimal.rs (4)
from(851-855)new(144-146)new(436-438)new(467-469)bip322/src/verification.rs (2)
validate_pubkey_matches_address(23-37)validate_compressed_pubkey_matches_address(52-90)bip322/src/hashing.rs (2)
compute_message_hash(61-74)compute_bip322_message_hash(38-44)
bip322/src/verification.rs (1)
near-utils/src/digest.rs (1)
digest(146-149)
bip322/src/bitcoin_minimal.rs (2)
near-utils/src/digest.rs (1)
digest(146-149)bip322/src/signature.rs (2)
from_str(84-98)from(76-78)
🪛 LanguageTool
bip322/README.md
[grammar] ~5-~5: There might be a mistake here.
Context: ...EAR blockchain ecosystem. ## 🎯 Purpose This module provides **complete BIP-322 s...
(QB_NEW_EN)
[grammar] ~12-~12: There might be a mistake here.
Context: ... using only NEAR SDK host functions - 📋 Wide Coverage: Supports most major Bi...
(QB_NEW_EN)
[grammar] ~13-~13: There might be a mistake here.
Context: ...types (P2PKH, P2SH, P2WPKH, P2WSH) - ⚡ Gas Optimized: Minimal gas consumption...
(QB_NEW_EN)
[grammar] ~14-~14: There might be a mistake here.
Context: ...gh efficient NEAR SDK integration - 🔒 Security Focused: Comprehensive valida...
(QB_NEW_EN)
[grammar] ~15-~15: There might be a mistake here.
Context: ...ation with proper error handling - 🧪 Well Tested: Extensive test suite with ...
(QB_NEW_EN)
[grammar] ~17-~17: There might be a mistake here.
Context: ...fficial BIP-322 reference vectors ## 🏗️ Architecture ### Core Components - **`...
(QB_NEW_EN)
[grammar] ~17-~17: There might be a mistake here.
Context: ...2 reference vectors ## 🏗️ Architecture ### Core Components - lib.rs: Main `Si...
(QB_NEW_EN)
[grammar] ~21-~21: There might be a mistake here.
Context: ...nd SignedPayload trait implementations - signature.rs: BIP-322 signature parsing and verifica...
(QB_NEW_EN)
[grammar] ~22-~22: There might be a mistake here.
Context: ...signature parsing and verification logic - bitcoin_minimal.rs: Minimal Bitcoin types optimized for BI...
(QB_NEW_EN)
[grammar] ~23-~23: There might be a mistake here.
Context: ...P-322 (transactions, addresses, scripts) - hashing.rs: BIP-322 message hash computation with ...
(QB_NEW_EN)
[grammar] ~24-~24: There might be a mistake here.
Context: ...h computation with proper tagged hashing - transaction.rs: BIP-322 "to_spend" and "to_sign" trans...
(QB_NEW_EN)
[grammar] ~25-~25: There might be a mistake here.
Context: ..." and "to_sign" transaction construction - verification.rs: Address validation and public key reco...
(QB_NEW_EN)
[grammar] ~42-~42: There might be a mistake here.
Context: ...se64 signature decoding ## 🚀 Usage rust use defuse_bip322::SignedBip322Payload; use defuse_crypto::SignedPayload; // Parse and verify a BIP-322 signature let payload = SignedBip322Payload { address: "bc1q9vza2e8x573nczrlzms0wvx3gsqjx7vavgkx0l".parse()?, message: "Hello Bitcoin!".to_string(), signature: "AkcwRAIgeGl4sSPd7zEIvhxdN8GgP4vgSqA8TdyPMeIpCF4gqgE4AiBsjQd0D1OFxdnHQPNOI1YdGlBD6kEOGRnHhcAkHnxUcAH=".parse()?, }; // Verify signature and extract public key if let Some(public_key) = payload.verify() { println!("✅ Valid BIP-322 signature!"); println!("🔑 Public key: {:?}", public_key); } else { println!("❌ Invalid signature"); } ``` ## 📊 Supported Features ### ✅ Address Type...
(QB_NEW_EN)
[grammar] ~64-~64: There might be a mistake here.
Context: ...ature"); } ``` ## 📊 Supported Features ### ✅ Address Types (Mainnet Only) | Type | ...
(QB_NEW_EN)
[grammar] ~68-~68: There might be a mistake here.
Context: ...) | Type | Format | Example | Support | |------|--------|---------|---------| | ...
(QB_NEW_EN)
[grammar] ~69-~69: There might be a mistake here.
Context: ... | |------|--------|---------|---------| | P2PKH | Legacy addresses starting ...
(QB_NEW_EN)
[grammar] ~70-~70: There might be a mistake here.
Context: ...Gefi2DMPTfTL5SLmv7DivfNa` | ✅ Complete | | P2SH | Script addresses starting w...
(QB_NEW_EN)
[grammar] ~71-~71: There might be a mistake here.
Context: ...iAE6uzMj2ZifT9YgRrkSgzQX` | ✅ Complete | | P2WPKH | Bech32 addresses starting...
(QB_NEW_EN)
[grammar] ~72-~72: There might be a mistake here.
Context: ...rlzms0wvx3gsqjx7vavgkx0l` | ✅ Complete | | P2WSH | Bech32 script addresses (3...
(QB_NEW_EN)
[grammar] ~77-~77: There might be a mistake here.
Context: ...: 65-byte compact format (P2PKH, P2WPKH) - Full Signatures: Complete BIP-322 witn...
(QB_NEW_EN)
[grammar] ~78-~78: There might be a mistake here.
Context: ...P-322 witness stack format (P2SH, P2WSH) - Automatic Detection: Parses both forma...
(QB_NEW_EN)
[grammar] ~83-~83: There might be a mistake here.
Context: ...-signed-message" tagged hash computation - Transaction Structure: Correct "to_spe...
(QB_NEW_EN)
[grammar] ~84-~84: There might be a mistake here.
Context: ..." and "to_sign" transaction construction - Witness Handling: Complete witness sta...
(QB_NEW_EN)
[grammar] ~85-~85: There might be a mistake here.
Context: ...ete witness stack parsing and validation - Address Validation: Full address forma...
(QB_NEW_EN)
[grammar] ~88-~88: There might be a mistake here.
Context: ...n ## 🔍 Discovered Issues & Limitations During implementation and testing, severa...
(QB_NEW_EN)
[grammar] ~96-~96: There might be a mistake here.
Context: ...e not currently supported. Details: - P2TR uses Taproot (BIP-341) with differe...
(QB_NEW_EN)
[grammar] ~108-~108: There might be a mistake here.
Context: ...s compressed 33-byte keys. Details: - NEAR SDK ecrecover returns 64-byte unc...
(QB_NEW_EN)
[grammar] ~111-~111: There might be a mistake here.
Context: ...hem. Implementation of the decompression inside contract is computationally inten...
(QB_NEW_EN)
[grammar] ~112-~112: There might be a mistake here.
Context: ...es not provide a way to uncompress keys. - See TODO at `bip322/src/signature.rs:384...
(QB_NEW_EN)
[grammar] ~115-~115: There might be a mistake here.
Context: ...signature.rs:384` Current Behavior: - Compressed key validation works correctl...
(QB_NEW_EN)
[grammar] ~133-~133: There might be a mistake here.
Context: ...tion Results** (see validation scripts): 1. Python Verification: External Bitcoin ...
(QB_NEW_EN)
[grammar] ~138-~138: There might be a mistake here.
Context: ...to the given address Evidence: See unisat-failure.png - screenshot showing verif...
(QB_NEW_EN)
[grammar] ~140-~140: There might be a mistake here.
Context: ... appears to be invalid, possibly due to: - Incorrect signature generation by the wa...
(QB_NEW_EN)
[grammar] ~141-~141: There might be a mistake here.
Context: ...rrect signature generation by the wallet - Wrong message format during signing - Co...
(QB_NEW_EN)
[grammar] ~142-~142: There might be a mistake here.
Context: ...et - Wrong message format during signing - Copy/paste errors in the test vector **...
(QB_NEW_EN)
[grammar] ~147-~147: There might be a mistake here.
Context: ...ted as expecting failure. ## 🧪 Testing The module includes comprehensive testing...
(QB_NEW_EN)
[grammar] ~153-~153: There might be a mistake here.
Context: ...g, message hashing, transaction building - Integration Tests: End-to-end signatur...
(QB_NEW_EN)
[grammar] ~154-~154: There might be a mistake here.
Context: ...-to-end signature verification workflows - Reference Vectors: Official BIP-322 te...
(QB_NEW_EN)
[grammar] ~155-~155: There might be a mistake here.
Context: ...-322 test vectors from the specification - Edge Cases: Invalid signatures, malfor...
(QB_NEW_EN)
[grammar] ~160-~160: There might be a mistake here.
Context: .../29 tests passing** (98.6% success rate) - 1 test ignored (invalid Unisat vector) -...
(QB_NEW_EN)
[grammar] ~161-~161: There might be a mistake here.
Context: ...- 1 test ignored (invalid Unisat vector) - All official BIP-322 reference vectors p...
(QB_NEW_EN)
[grammar] ~166-~166: There might be a mistake here.
Context: ...valid cases ## 📄 Standards Compliance - ✅ BIP-322: Complete implementation of G...
(QB_NEW_EN)
[grammar] ~168-~168: There might be a mistake here.
Context: ...ntation of Generic Signed Message Format - ✅ BIP-143: Segwit transaction digest a...
(QB_NEW_EN)
[grammar] ~169-~169: There might be a mistake here.
Context: ...3**: Segwit transaction digest algorithm - ✅ Base58Check: Legacy address encoding...
(QB_NEW_EN)
[grammar] ~170-~170: There might be a mistake here.
Context: ...*: Legacy address encoding (P2PKH, P2SH) - ✅ Bech32: Segwit address encoding (P2W...
(QB_NEW_EN)
[grammar] ~173-~173: There might be a mistake here.
Context: ...oding (P2WPKH, P2WSH) ## 🤝 Integration This module integrates seamlessly with th...
(QB_NEW_EN)
⏰ 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). (8)
- GitHub Check: Check
- GitHub Check: Shared security analysis / Static Analysis (intents-contract, contract, near, defuse, 1, ., 1.18, false, --filter-paths "nod...
- GitHub Check: Shared security analysis / Static Analysis (intents-poa-factory, contract, near, poa-factory, 1, ., 1.18, false, --filter-pa...
- GitHub Check: Shared security analysis / Static Analysis (intents-poa-token, contract, near, poa-token, 1, ., 1.18, false, --filter-paths ...
- GitHub Check: Shared / Static Analysis (intents-poa-factory, contract, near, poa-factory, 1, ., 1.18, false, --filter-pa...
- GitHub Check: Shared / Static Analysis (intents-poa-token, contract, near, poa-token, 1, ., 1.18, false, --filter-paths ...
- GitHub Check: Shared / Static Analysis (intents-contract, contract, near, defuse, 1, ., 1.18, false, --filter-paths "nod...
- GitHub Check: Security Audit - report
There was a problem hiding this 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 (12)
bip322/README.md (1)
36-40: Dependencies section accurately reflects minimal non-crypto deps. LGTM.Good call-out separating NEAR-hosted crypto from minimal address/encoding crates. This resolves the earlier ambiguity around “zero external dependencies.”
bip322/validate_unisat_vector.py (1)
31-52: Fix Bitcoin signed-message hash: missing varint(len(prefix)).Per the Bitcoin “signmessage” format: hash = SHA256(SHA256(varint(len(prefix)) || prefix || varint(len(msg)) || msg)). The current code omits varint(len(prefix)), producing a non-standard hash.
Apply this diff:
@@ - # Create varint length encoding - msg_len = len(message_bytes) - if msg_len < 253: - len_bytes = bytes([msg_len]) - elif msg_len <= 0xFFFF: - len_bytes = bytes([0xFD]) + msg_len.to_bytes(2, 'little') - elif msg_len <= 0xFFFFFFFF: - len_bytes = bytes([0xFE]) + msg_len.to_bytes(4, 'little') - else: - len_bytes = bytes([0xFF]) + msg_len.to_bytes(8, 'little') + # Create varint length encoding for prefix and message + prefix_len = len(prefix) + if prefix_len < 253: + prefix_len_bytes = bytes([prefix_len]) + elif prefix_len <= 0xFFFF: + prefix_len_bytes = bytes([0xFD]) + prefix_len.to_bytes(2, 'little') + elif prefix_len <= 0xFFFFFFFF: + prefix_len_bytes = bytes([0xFE]) + prefix_len.to_bytes(4, 'little') + else: + prefix_len_bytes = bytes([0xFF]) + prefix_len.to_bytes(8, 'little') + + msg_len = len(message_bytes) + if msg_len < 253: + len_bytes = bytes([msg_len]) + elif msg_len <= 0xFFFF: + len_bytes = bytes([0xFD]) + msg_len.to_bytes(2, 'little') + elif msg_len <= 0xFFFFFFFF: + len_bytes = bytes([0xFE]) + msg_len.to_bytes(4, 'little') + else: + len_bytes = bytes([0xFF]) + msg_len.to_bytes(8, 'little') @@ - full_message = prefix + len_bytes + message_bytes + full_message = prefix_len_bytes + prefix + len_bytes + message_bytesbip322/validate_unisat_comprehensive.py (1)
30-51: Fix Bitcoin signed-message hash: missing varint(len(prefix)).Same issue as the lightweight script. Include varint-encoded prefix length before the prefix.
Apply this diff:
@@ - # Create varint length encoding - msg_len = len(message_bytes) - if msg_len < 253: - len_bytes = bytes([msg_len]) - elif msg_len <= 0xFFFF: - len_bytes = bytes([0xFD]) + msg_len.to_bytes(2, 'little') - elif msg_len <= 0xFFFFFFFF: - len_bytes = bytes([0xFE]) + msg_len.to_bytes(4, 'little') - else: - len_bytes = bytes([0xFF]) + msg_len.to_bytes(8, 'little') + # Create varint length encoding for prefix and message + prefix_len = len(prefix) + if prefix_len < 253: + prefix_len_bytes = bytes([prefix_len]) + elif prefix_len <= 0xFFFF: + prefix_len_bytes = bytes([0xFD]) + prefix_len.to_bytes(2, 'little') + elif prefix_len <= 0xFFFFFFFF: + prefix_len_bytes = bytes([0xFE]) + prefix_len.to_bytes(4, 'little') + else: + prefix_len_bytes = bytes([0xFF]) + prefix_len.to_bytes(8, 'little') + + msg_len = len(message_bytes) + if msg_len < 253: + len_bytes = bytes([msg_len]) + elif msg_len <= 0xFFFF: + len_bytes = bytes([0xFD]) + msg_len.to_bytes(2, 'little') + elif msg_len <= 0xFFFFFFFF: + len_bytes = bytes([0xFE]) + msg_len.to_bytes(4, 'little') + else: + len_bytes = bytes([0xFF]) + msg_len.to_bytes(8, 'little') @@ - full_message = prefix + len_bytes + message_bytes + full_message = prefix_len_bytes + prefix + len_bytes + message_bytesnear-utils/src/digest.rs (1)
103-107: Compile fix: use chain_update instead of chain (digest 0.10).
digest::Digestprovideschain_update, notchain. As written, this won’t compile.fn finalize_into(self, out: &mut digest::Output<Self>) { - D::default() - .chain(self.0.finalize_fixed()) - .finalize_into(out); + D::default() + .chain_update(self.0.finalize_fixed()) + .finalize_into(out); }bip322/src/verification.rs (5)
101-121: Incorrect Hash160 for uncompressed pubkeys (missing 0x04 prefix).Bitcoin uncompressed pubkeys are serialized as 65 bytes: 0x04 || X || Y. Hashing raw 64 bytes will not match P2PKH addresses.
fn compute_pubkey_hash160_all(raw_pubkey: &[u8; 64], compressed: bool) -> Vec<[u8; 20]> { @@ - vec![defuse_near_utils::digest::Hash160::digest(raw_pubkey).into()] + // Uncompressed serialization: 0x04 || X || Y + let mut uncompressed = Vec::with_capacity(65); + uncompressed.push(0x04); + uncompressed.extend_from_slice(raw_pubkey); + vec![defuse_near_utils::digest::Hash160::digest(&uncompressed).into()] }
56-66: Fix slice equality and validate P2WPKH program format.Compare slices explicitly and enforce witness version 0 with 20-byte program length.
- Address::P2WPKH { witness_program } => { - let computed_hash: [u8; 20] = - defuse_near_utils::digest::Hash160::digest(compressed_pubkey).into(); - computed_hash == witness_program.program.as_slice() - } + Address::P2WPKH { witness_program } => { + // v0 and 20-byte program + if witness_program.version != 0 || witness_program.program.len() != 20 { + return false; + } + let computed_hash: [u8; 20] = + defuse_near_utils::digest::Hash160::digest(compressed_pubkey).into(); + &computed_hash[..] == witness_program.program.as_slice() + }
82-88: Fix slice equality and validate P2WSH program format.As above, enforce v0 and 32-byte program and compare slices.
- Address::P2WSH { witness_program } => { + Address::P2WSH { witness_program } => { + if witness_program.version != 0 || witness_program.program.len() != 32 { + return false; + } let pubkey_hash: [u8; 20] = defuse_near_utils::digest::Hash160::digest(compressed_pubkey).into(); let witness_script = build_witness_script(&pubkey_hash); let computed_script_hash = env::sha256_array(&witness_script); - computed_script_hash == witness_program.program.as_slice() + &computed_script_hash[..] == witness_program.program.as_slice() }
156-167: P2WPKH: enforce witness format and fix equality.Validate v0/20-bytes and compare slices.
- let computed_pubkey_hash = compute_pubkey_hash160_all(recovered_pubkey, true); - - computed_pubkey_hash[0] == witness_program.program.as_slice() - || computed_pubkey_hash[1] == witness_program.program.as_slice() + if witness_program.version != 0 || witness_program.program.len() != 20 { + return false; + } + let computed_pubkey_hash = compute_pubkey_hash160_all(recovered_pubkey, true); + &computed_pubkey_hash[0][..] == witness_program.program.as_slice() + || &computed_pubkey_hash[1][..] == witness_program.program.as_slice()
197-223: P2WSH: enforce witness format and fix equality.Validate v0/32-bytes and use slice equality in all branches.
- let computed_script_hash = env::sha256_array(&witness_script); - - if computed_script_hash == witness_program.program.as_slice() { + let computed_script_hash = env::sha256_array(&witness_script); + if witness_program.version != 0 || witness_program.program.len() != 32 { + return false; + } + if &computed_script_hash[..] == witness_program.program.as_slice() { return true; } @@ - let computed_script_hash = env::sha256_array(&witness_script); - if computed_script_hash == witness_program.program.as_slice() { + let computed_script_hash = env::sha256_array(&witness_script); + if &computed_script_hash[..] == witness_program.program.as_slice() { return true; } @@ - let computed_script_hash = env::sha256_array(&witness_script); - computed_script_hash == witness_program.program.as_slice() + let computed_script_hash = env::sha256_array(&witness_script); + &computed_script_hash[..] == witness_program.program.as_slice()bip322/src/signature.rs (3)
37-42: Doc incorrect: Compact path does not use BIP-340 tagged hash.The Compact variant uses classic “Bitcoin Signed Message” double-SHA256, not BIP-340 tagged hashing.
- /// This is the standard Bitcoin message signing format used by most wallets. - /// For BIP-322 simple signatures, the message is hashed directly with BIP-340 - /// tagged hash, not through transaction construction. + /// This is the standard Bitcoin message signing format used by most wallets. + /// Compact signatures hash the message using the classic Bitcoin Signed Message + /// format (double SHA256 over CompactSize(prefix) + prefix + + /// CompactSize(message.len) + message), not through transaction construction.
452-479: Bitcoin Signed Message hash is incorrect (missing prefix length varint).The standard requires CompactSize(len(prefix)) before the prefix. Without it, signatures from standard wallets won’t verify.
- /// This follows the Bitcoin Core format: - /// Hash = SHA256(SHA256("Bitcoin Signed Message:\n" + varint(message.len()) + message)) + /// This follows the Bitcoin Core format: + /// Hash = SHA256(SHA256(varint(len(prefix)) + prefix + /// + varint(message.len()) + message)) fn compute_bitcoin_message_hash(message: &str) -> [u8; 32] { @@ - // Create the full message with prefix and length + // Create the full message with prefix length, prefix and message length let mut full_message = Vec::new(); - full_message.extend_from_slice(prefix); + // Write CompactSize of prefix length first + Self::encode_varint(prefix.len() as u64, &mut full_message); + full_message.extend_from_slice(prefix); @@ - // Double SHA256 hash + // Double SHA256 hash DoubleSha256::digest(&full_message).into() }
375-389: Do not fabricate zeroed keys; returnNonefor compressed key case.Returning
[0u8; 64]on a valid compressed-key path is dangerous and can leak fake key material to callers. Validate and returnNoneuntil proper decompression or a typed return is implemented.- ParsedPublicKey::Compressed(compressed) => { - // Validate compressed public key against address - if crate::verification::validate_compressed_pubkey_matches_address( - compressed, address, - ) { - // Validation succeeded, but we cannot provide uncompressed format - // This indicates a successful verification but inability to decompress - // For now, we'll create a placeholder uncompressed key to indicate success - // TODO: Implement proper decompression or change API to accept compressed keys - Some([0u8; 64]) // Placeholder indicating successful validation - } else { - None - } - } + ParsedPublicKey::Compressed(compressed) => { + if crate::verification::validate_compressed_pubkey_matches_address( + compressed, address, + ) { + // Verified, but we cannot materialize an uncompressed key in this API + // TODO: add a typed return (compressed/uncompressed) or decompress properly + None + } else { + None + } + }
🧹 Nitpick comments (16)
bip322/README.md (2)
55-61: Clarify verify() return semantics given compressed key limitation.Usage prints a recovered public key, but the Limitations section states compressed keys currently return a placeholder [0u8; 64]. Add a note here (or adjust the example) to avoid implying a usable public key is always returned.
66-74: Make “Mainnet Only” constraints explicit (HRP/version).Consider stating explicitly that only HRP "bc" and segwit v0 (P2WPKH, P2WSH) are supported. If testnet/regtest aren’t supported yet, adding a short note here will preempt confusion.
bip322/validate_unisat_vector.py (5)
27-29: Avoid bare except; catch Exception explicitly.Using bare except obscures real errors and complicates debugging.
- except: - pass + except Exception: + return None
8-8: Remove unused import.
unhexlifyisn’t used.-from binascii import hexlify, unhexlify +from binascii import hexlify
154-154: Remove f-string without placeholders.- print(f"✓ Address parsed successfully") + print("✓ Address parsed successfully")
167-178: Check library availability without importing to avoid F401.Use importlib.util.find_spec instead of importing modules you don’t use.
- try: - import bech32 - print("✓ bech32 library available") - except ImportError: - print("✗ bech32 library not available - run: pip install bech32") - - try: - import ecdsa - print("✓ ecdsa library available") - except ImportError: - print("✗ ecdsa library not available - run: pip install ecdsa") + import importlib.util + if importlib.util.find_spec("bech32") is not None: + print("✓ bech32 library available") + else: + print("✗ bech32 library not available - run: pip install bech32") + if importlib.util.find_spec("ecdsa") is not None: + print("✓ ecdsa library available") + else: + print("✗ ecdsa library not available - run: pip install ecdsa")
101-124: Address validation is a stub; consider computing HASH160.Returning True unconditionally can be misleading. If feasible, compute RIPEMD160(SHA256(pubkey)) and compare to the P2WPKH witness program. If RIPEMD160 isn’t available, gate the check and emit a clear message.
bip322/validate_unisat_comprehensive.py (4)
26-27: Avoid bare except; catch Exception explicitly.- except: - pass + except Exception: + return None
8-8: Remove unused import.
unhexlifyisn’t used.-from binascii import hexlify, unhexlify +from binascii import hexlify
81-170: Manual ECDSA key recovery is error-prone; prefer proven libraries.The ad‑hoc point reconstruction and parity handling is fragile and easy to get subtly wrong. If possible, use a library that exposes compact signature recovery (e.g., coincurve or libsecp256k1 bindings). Keep the current code as a debug path only.
204-204: Remove f-string without placeholders.- print(f"✓ Address parsed successfully") + print("✓ Address parsed successfully")core/src/payload/bip322.rs (1)
12-14: Implementextract_defuse_payloadinSignedBip322Payloadto match other payloadsThe
DefusePayload<T>struct is defined as:pub struct DefusePayload<T> { pub signer_id: AccountId, pub verifying_contract: AccountId, pub deadline: Deadline, pub nonce: Nonce, #[serde(flatten)] pub message: T, }Most
Signed*Payloadtypes simply parse a JSON string intoDefusePayload<T>:impl<T> ExtractDefusePayload<T> for SignedWebAuthnPayload where T: DeserializeOwned, { type Error = serde_json::Error; #[inline] fn extract_defuse_payload(self) -> Result<DefusePayload<T>, Self::Error> { serde_json::from_str(&self.payload) } }For BIP-322, however, the envelope fields (
signer_id,verifying_contract,deadline,nonce) live onSignedBip322Payloaditself, and onlymessageis JSON‐encoded. You should:
- Parse
self.messageintoT.- Construct and return a
DefusePayload<T>usingself.signer_id,self.verifying_contract,self.deadline,self.nonce, and the parsed message.File: core/src/payload/bip322.rs impl<T> ExtractDefusePayload<T> for SignedBip322Payload where T: DeserializeOwned, { type Error = serde_json::Error; - fn extract_defuse_payload(self) -> Result<super::DefusePayload<T>, Self::Error> { - todo!() - } + fn extract_defuse_payload(self) -> Result<super::DefusePayload<T>, Self::Error> { + // Deserialize the inner message JSON into T + let msg: T = serde_json::from_str(&self.message)?; + + // Wrap envelope fields and the deserialized message + Ok(super::DefusePayload { + signer_id: self.signer_id, + verifying_contract: self.verifying_contract, + deadline: self.deadline, + nonce: self.nonce, + message: msg, + }) + } }bip322/src/lib.rs (1)
43-47: Avoid recomputing message hash inverify().
hash()andverify()both compute the same hash. Consider passinghash()into verification to avoid duplication.impl SignedPayload for SignedBip322Payload { type PublicKey = <Secp256k1 as Curve>::PublicKey; fn verify(&self) -> Option<Self::PublicKey> { - let message_hash = self - .signature - .compute_message_hash(&self.message, &self.address); - self.signature - .extract_public_key(&message_hash, &self.address) + let message_hash = self.hash(); + self.signature.extract_public_key(&message_hash, &self.address) } }Also applies to: 53-59
bip322/src/tests.rs (1)
339-361: Rename or fix assertion message: failure is due to invalid signature, not length.The test name and message mention “Invalid signature length” but the provided signature is 65 bytes; the verification fails because the signature content is invalid.
- fn test_signature_verification_invalid_signature_length() { + fn test_signature_verification_invalid_signature() { @@ - assert!( - result.is_none(), - "Invalid signature length should fail verification" - ); + assert!(result.is_none(), "Invalid signature should fail verification");bip322/src/verification.rs (2)
143-154: P2PKH: use slice equality consistently.Array-to-slice comparisons can be brittle; compare slices explicitly.
- if uncompressed_hash[0] == *expected_pubkey_hash { + if &uncompressed_hash[0][..] == expected_pubkey_hash { return true; } @@ - compressed_hash[0] == *expected_pubkey_hash || compressed_hash[1] == *expected_pubkey_hash + &compressed_hash[0][..] == expected_pubkey_hash || &compressed_hash[1][..] == expected_pubkey_hash
169-195: P2SH: fix slice equality for script-hash comparisons.Ensure comparisons are slice-to-slice for consistency.
- if computed_script_hash == *expected_script_hash { + if &computed_script_hash[..] == expected_script_hash { return true; } @@ - if computed_script_hash == *expected_script_hash { + if &computed_script_hash[..] == expected_script_hash { return true; } @@ - computed_script_hash == *expected_script_hash + &computed_script_hash[..] == expected_script_hash
📜 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.
⛔ Files ignored due to path filters (2)
Cargo.lockis excluded by!**/*.lockbip322/unisat-failure.pngis excluded by!**/*.png
📒 Files selected for processing (22)
.gitignore(1 hunks)Cargo.toml(3 hunks)bip322/Cargo.toml(1 hunks)bip322/README.md(1 hunks)bip322/src/bitcoin_minimal.rs(1 hunks)bip322/src/error.rs(1 hunks)bip322/src/hashing.rs(1 hunks)bip322/src/lib.rs(1 hunks)bip322/src/signature.rs(1 hunks)bip322/src/tests.rs(1 hunks)bip322/src/transaction.rs(1 hunks)bip322/src/verification.rs(1 hunks)bip322/validate_unisat_comprehensive.py(1 hunks)bip322/validate_unisat_vector.py(1 hunks)core/Cargo.toml(2 hunks)core/src/payload/bip322.rs(1 hunks)core/src/payload/mod.rs(1 hunks)core/src/payload/multi.rs(5 hunks)near-utils/src/digest.rs(3 hunks)tests/Cargo.toml(1 hunks)tests/src/tests/defuse/mod.rs(2 hunks)tests/src/utils/crypto.rs(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (12)
- tests/Cargo.toml
- bip322/src/transaction.rs
- bip322/src/error.rs
- core/src/payload/multi.rs
- bip322/src/hashing.rs
- tests/src/tests/defuse/mod.rs
- tests/src/utils/crypto.rs
- Cargo.toml
- .gitignore
- bip322/src/bitcoin_minimal.rs
- core/Cargo.toml
- bip322/Cargo.toml
🧰 Additional context used
🧬 Code Graph Analysis (7)
bip322/validate_unisat_comprehensive.py (1)
bip322/validate_unisat_vector.py (4)
parse_bech32_address(15-29)bitcoin_message_hash(31-52)recover_pubkey_from_signature(54-99)main(129-184)
bip322/src/lib.rs (1)
core/src/payload/multi.rs (2)
hash(67-78)verify(85-96)
core/src/payload/bip322.rs (2)
core/src/payload/mod.rs (2)
extract_defuse_payload(51-51)extract_defuse_payload(58-60)core/src/payload/multi.rs (1)
extract_defuse_payload(106-117)
bip322/src/signature.rs (4)
bip322/src/transaction.rs (2)
create_to_sign(101-137)create_to_spend(32-76)bip322/src/bitcoin_minimal.rs (4)
from(851-855)new(144-146)new(436-438)new(467-469)bip322/src/verification.rs (2)
validate_pubkey_matches_address(23-37)validate_compressed_pubkey_matches_address(52-90)bip322/src/hashing.rs (2)
compute_message_hash(61-74)compute_bip322_message_hash(38-44)
bip322/validate_unisat_vector.py (1)
bip322/validate_unisat_comprehensive.py (4)
parse_bech32_address(15-28)bitcoin_message_hash(30-51)recover_pubkey_from_signature(53-179)main(181-229)
near-utils/src/digest.rs (1)
bip322/src/bitcoin_minimal.rs (5)
default(138-140)default(430-432)new(144-146)new(436-438)new(467-469)
bip322/src/tests.rs (5)
bip322/src/transaction.rs (3)
compute_tx_id(151-157)create_to_sign(101-137)create_to_spend(32-76)bip322/src/bitcoin_minimal.rs (6)
new(144-146)new(436-438)new(467-469)from_str(232-356)all_zeros(450-452)from_byte_array(454-456)bip322/src/signature.rs (1)
from_str(84-98)bip322/src/hashing.rs (1)
compute_bip322_message_hash(38-44)bip322/src/lib.rs (2)
hash(44-47)verify(53-59)
🪛 Ruff (0.12.2)
bip322/validate_unisat_comprehensive.py
8-8: binascii.unhexlify imported but unused
Remove unused import: binascii.unhexlify
(F401)
26-26: Do not use bare except
(E722)
56-56: ecdsa imported but unused; consider using importlib.util.find_spec to test for availability
(F401)
58-58: ecdsa.ecdsa.possible_public_keys_from_signature imported but unused; consider using importlib.util.find_spec to test for availability
(F401)
84-84: bitcoinlib.keys.Key imported but unused; consider using importlib.util.find_spec to test for availability
(F401)
135-135: Local variable point is assigned to but never used
Remove assignment to unused variable point
(F841)
204-204: f-string without any placeholders
Remove extraneous f prefix
(F541)
bip322/validate_unisat_vector.py
8-8: binascii.unhexlify imported but unused
Remove unused import: binascii.unhexlify
(F401)
27-27: Do not use bare except
(E722)
57-57: ecdsa imported but unused; consider using importlib.util.find_spec to test for availability
(F401)
58-58: ecdsa.curves.SECP256k1 imported but unused; consider using importlib.util.find_spec to test for availability
(F401)
59-59: ecdsa.ellipticcurve.Point imported but unused; consider using importlib.util.find_spec to test for availability
(F401)
120-120: Local variable sha256_hash is assigned to but never used
Remove assignment to unused variable sha256_hash
(F841)
154-154: f-string without any placeholders
Remove extraneous f prefix
(F541)
165-165: Local variable recovered_pubkey is assigned to but never used
Remove assignment to unused variable recovered_pubkey
(F841)
169-169: bech32 imported but unused; consider using importlib.util.find_spec to test for availability
(F401)
175-175: ecdsa imported but unused; consider using importlib.util.find_spec to test for availability
(F401)
🪛 LanguageTool
bip322/README.md
[grammar] ~5-~5: There might be a mistake here.
Context: ...EAR blockchain ecosystem. ## 🎯 Purpose This module provides **complete BIP-322 s...
(QB_NEW_EN)
[grammar] ~12-~12: There might be a mistake here.
Context: ... using only NEAR SDK host functions - 📋 Wide Coverage: Supports most major Bi...
(QB_NEW_EN)
[grammar] ~13-~13: There might be a mistake here.
Context: ...types (P2PKH, P2SH, P2WPKH, P2WSH) - ⚡ Gas Optimized: Minimal gas consumption...
(QB_NEW_EN)
[grammar] ~14-~14: There might be a mistake here.
Context: ...gh efficient NEAR SDK integration - 🔒 Security Focused: Comprehensive valida...
(QB_NEW_EN)
[grammar] ~15-~15: There might be a mistake here.
Context: ...ation with proper error handling - 🧪 Well Tested: Extensive test suite with ...
(QB_NEW_EN)
[grammar] ~17-~17: There might be a mistake here.
Context: ...fficial BIP-322 reference vectors ## 🏗️ Architecture ### Core Components - **`...
(QB_NEW_EN)
[grammar] ~17-~17: There might be a mistake here.
Context: ...2 reference vectors ## 🏗️ Architecture ### Core Components - lib.rs: Main `Si...
(QB_NEW_EN)
[grammar] ~21-~21: There might be a mistake here.
Context: ...nd SignedPayload trait implementations - signature.rs: BIP-322 signature parsing and verifica...
(QB_NEW_EN)
[grammar] ~22-~22: There might be a mistake here.
Context: ...signature parsing and verification logic - bitcoin_minimal.rs: Minimal Bitcoin types optimized for BI...
(QB_NEW_EN)
[grammar] ~23-~23: There might be a mistake here.
Context: ...P-322 (transactions, addresses, scripts) - hashing.rs: BIP-322 message hash computation with ...
(QB_NEW_EN)
[grammar] ~24-~24: There might be a mistake here.
Context: ...h computation with proper tagged hashing - transaction.rs: BIP-322 "to_spend" and "to_sign" trans...
(QB_NEW_EN)
[grammar] ~25-~25: There might be a mistake here.
Context: ..." and "to_sign" transaction construction - verification.rs: Address validation and public key reco...
(QB_NEW_EN)
[grammar] ~42-~42: There might be a mistake here.
Context: ...se64 signature decoding ## 🚀 Usage rust use defuse_bip322::SignedBip322Payload; use defuse_crypto::SignedPayload; // Parse and verify a BIP-322 signature let payload = SignedBip322Payload { address: "bc1q9vza2e8x573nczrlzms0wvx3gsqjx7vavgkx0l".parse()?, message: "Hello Bitcoin!".to_string(), signature: "AkcwRAIgeGl4sSPd7zEIvhxdN8GgP4vgSqA8TdyPMeIpCF4gqgE4AiBsjQd0D1OFxdnHQPNOI1YdGlBD6kEOGRnHhcAkHnxUcAH=".parse()?, }; // Verify signature and extract public key if let Some(public_key) = payload.verify() { println!("✅ Valid BIP-322 signature!"); println!("🔑 Public key: {:?}", public_key); } else { println!("❌ Invalid signature"); } ``` ## 📊 Supported Features ### ✅ Address Type...
(QB_NEW_EN)
[grammar] ~64-~64: There might be a mistake here.
Context: ...ature"); } ``` ## 📊 Supported Features ### ✅ Address Types (Mainnet Only) | Type | ...
(QB_NEW_EN)
[grammar] ~68-~68: There might be a mistake here.
Context: ...) | Type | Format | Example | Support | |------|--------|---------|---------| | ...
(QB_NEW_EN)
[grammar] ~69-~69: There might be a mistake here.
Context: ... | |------|--------|---------|---------| | P2PKH | Legacy addresses starting ...
(QB_NEW_EN)
[grammar] ~70-~70: There might be a mistake here.
Context: ...Gefi2DMPTfTL5SLmv7DivfNa` | ✅ Complete | | P2SH | Script addresses starting w...
(QB_NEW_EN)
[grammar] ~71-~71: There might be a mistake here.
Context: ...iAE6uzMj2ZifT9YgRrkSgzQX` | ✅ Complete | | P2WPKH | Bech32 addresses starting...
(QB_NEW_EN)
[grammar] ~72-~72: There might be a mistake here.
Context: ...rlzms0wvx3gsqjx7vavgkx0l` | ✅ Complete | | P2WSH | Bech32 script addresses (3...
(QB_NEW_EN)
[grammar] ~77-~77: There might be a mistake here.
Context: ...: 65-byte compact format (P2PKH, P2WPKH) - Full Signatures: Complete BIP-322 witn...
(QB_NEW_EN)
[grammar] ~78-~78: There might be a mistake here.
Context: ...P-322 witness stack format (P2SH, P2WSH) - Automatic Detection: Parses both forma...
(QB_NEW_EN)
[grammar] ~83-~83: There might be a mistake here.
Context: ...-signed-message" tagged hash computation - Transaction Structure: Correct "to_spe...
(QB_NEW_EN)
[grammar] ~84-~84: There might be a mistake here.
Context: ..." and "to_sign" transaction construction - Witness Handling: Complete witness sta...
(QB_NEW_EN)
[grammar] ~85-~85: There might be a mistake here.
Context: ...ete witness stack parsing and validation - Address Validation: Full address forma...
(QB_NEW_EN)
[grammar] ~88-~88: There might be a mistake here.
Context: ...n ## 🔍 Discovered Issues & Limitations During implementation and testing, severa...
(QB_NEW_EN)
[grammar] ~96-~96: There might be a mistake here.
Context: ...e not currently supported. Details: - P2TR uses Taproot (BIP-341) with differe...
(QB_NEW_EN)
[grammar] ~108-~108: There might be a mistake here.
Context: ...s compressed 33-byte keys. Details: - NEAR SDK ecrecover returns 64-byte unc...
(QB_NEW_EN)
[grammar] ~111-~111: There might be a mistake here.
Context: ...hem. Implementation of the decompression inside contract is computationally inten...
(QB_NEW_EN)
[grammar] ~112-~112: There might be a mistake here.
Context: ...es not provide a way to uncompress keys. - See TODO at `bip322/src/signature.rs:384...
(QB_NEW_EN)
[grammar] ~115-~115: There might be a mistake here.
Context: ...signature.rs:384` Current Behavior: - Compressed key validation works correctl...
(QB_NEW_EN)
[grammar] ~133-~133: There might be a mistake here.
Context: ...tion Results** (see validation scripts): 1. Python Verification: External Bitcoin ...
(QB_NEW_EN)
[grammar] ~138-~138: There might be a mistake here.
Context: ...to the given address Evidence: See unisat-failure.png - screenshot showing verif...
(QB_NEW_EN)
[grammar] ~140-~140: There might be a mistake here.
Context: ... appears to be invalid, possibly due to: - Incorrect signature generation by the wa...
(QB_NEW_EN)
[grammar] ~141-~141: There might be a mistake here.
Context: ...rrect signature generation by the wallet - Wrong message format during signing - Co...
(QB_NEW_EN)
[grammar] ~142-~142: There might be a mistake here.
Context: ...et - Wrong message format during signing - Copy/paste errors in the test vector **...
(QB_NEW_EN)
[grammar] ~147-~147: There might be a mistake here.
Context: ...ted as expecting failure. ## 🧪 Testing The module includes comprehensive testing...
(QB_NEW_EN)
[grammar] ~153-~153: There might be a mistake here.
Context: ...g, message hashing, transaction building - Integration Tests: End-to-end signatur...
(QB_NEW_EN)
[grammar] ~154-~154: There might be a mistake here.
Context: ...-to-end signature verification workflows - Reference Vectors: Official BIP-322 te...
(QB_NEW_EN)
[grammar] ~155-~155: There might be a mistake here.
Context: ...-322 test vectors from the specification - Edge Cases: Invalid signatures, malfor...
(QB_NEW_EN)
[grammar] ~160-~160: There might be a mistake here.
Context: .../29 tests passing** (98.6% success rate) - 1 test ignored (invalid Unisat vector) -...
(QB_NEW_EN)
[grammar] ~161-~161: There might be a mistake here.
Context: ...- 1 test ignored (invalid Unisat vector) - All official BIP-322 reference vectors p...
(QB_NEW_EN)
[grammar] ~166-~166: There might be a mistake here.
Context: ...valid cases ## 📄 Standards Compliance - ✅ BIP-322: Complete implementation of G...
(QB_NEW_EN)
[grammar] ~168-~168: There might be a mistake here.
Context: ...ntation of Generic Signed Message Format - ✅ BIP-143: Segwit transaction digest a...
(QB_NEW_EN)
[grammar] ~169-~169: There might be a mistake here.
Context: ...3**: Segwit transaction digest algorithm - ✅ Base58Check: Legacy address encoding...
(QB_NEW_EN)
[grammar] ~170-~170: There might be a mistake here.
Context: ...*: Legacy address encoding (P2PKH, P2SH) - ✅ Bech32: Segwit address encoding (P2W...
(QB_NEW_EN)
[grammar] ~173-~173: There might be a mistake here.
Context: ...oding (P2WPKH, P2WSH) ## 🤝 Integration This module integrates seamlessly with th...
(QB_NEW_EN)
⏰ 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). (8)
- GitHub Check: Check
- GitHub Check: Shared / Static Analysis (intents-poa-factory, contract, near, poa-factory, 1, ., 1.18, false, --filter-pa...
- GitHub Check: Shared / Static Analysis (intents-poa-token, contract, near, poa-token, 1, ., 1.18, false, --filter-paths ...
- GitHub Check: Shared / Static Analysis (intents-contract, contract, near, defuse, 1, ., 1.18, false, --filter-paths "nod...
- GitHub Check: Shared security analysis / Static Analysis (intents-contract, contract, near, defuse, 1, ., 1.18, false, --filter-paths "nod...
- GitHub Check: Shared security analysis / Static Analysis (intents-poa-token, contract, near, poa-token, 1, ., 1.18, false, --filter-paths ...
- GitHub Check: Shared security analysis / Static Analysis (intents-poa-factory, contract, near, poa-factory, 1, ., 1.18, false, --filter-pa...
- GitHub Check: Security Audit - report
🔇 Additional comments (6)
near-utils/src/digest.rs (3)
37-73: Hash160 implementation via NEAR host functions looks solid.Correct composition RIPEMD160(SHA256(data)) with host calls; trait impls and output size (U20) align with Bitcoin expectations.
112-129: TaggedDigest is correctly implemented (tag||tag||data).Good reusable primitive for domain-separated hashing.
161-169: DoubleSha256 test matches expected host behavior. LGTM.core/src/payload/mod.rs (1)
1-1: Expose bip322 module in payload namespace. LGTM.This enables the new payload type without affecting existing modules.
bip322/src/lib.rs (1)
42-60: LGTM: cohesive API and clean integration into defuse-crypto traits.The
SignedBip322Payloadstruct and itsPayload/SignedPayloadimpls are consistent and align withMultiPayloaddispatch. Public re-exports are tidy and helpful.bip322/src/signature.rs (1)
404-428: Sanity-check: recovery ID range and ecrecover usage look correct.Header 27–34 mapped to v=0–3 with compressed/uncompressed handling is consistent with Bitcoin compact signatures, and
env::ecrecovercall shape matches.
There was a problem hiding this 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
♻️ Duplicate comments (1)
bip322/src/tests.rs (1)
370-381: Fix test intent mismatch: comment says “expects failure” but helper asserts success (Unisat).This is the same issue previously flagged and still present. The test claims the vector is invalid and expects failure, but it calls a helper that unwraps on success.
Apply this diff to assert failure inline:
@@ - test_parse_bip322_payload(address, signature, "unisat"); + // Inline the logic to assert failure, instead of delegating to the success-helper + use crate::Bip322Signature; + let bip322_signature = Bip322Signature::from_str(signature).expect("parse base64"); + let payload = SignedBip322Payload { + address: address.parse().unwrap(), + message: MESSAGE.to_string(), + signature: bip322_signature, + }; + assert!( + payload.verify().is_none(), + "Expected verification failure for unisat" + );
🧹 Nitpick comments (8)
bip322/src/tests.rs (8)
382-395: Clarify helper semantics to avoid mis-useThis helper unwraps on success. A short doc comment will prevent using it in failure-expected tests.
@@ - fn test_parse_bip322_payload(address: &str, signature: &str, info_message: &str) { + /// Helper that asserts the BIP-322 payload verifies successfully. + fn test_parse_bip322_payload(address: &str, signature: &str, info_message: &str) {
295-316: Rename to match what’s tested (not “wrong witness type”)This test uses an empty 65-byte compact signature with a P2PKH address. It’s not actually asserting a “wrong witness type” scenario; it’s asserting an invalid/empty compact signature fails.
@@ - fn test_signature_verification_wrong_witness_type() { + fn test_signature_verification_empty_compact_signature() { @@ - // Create a P2PKH address but use P2WPKH witness - should fail + // Create a P2PKH address and use an empty/invalid compact signature - should failIf you want to explicitly test “wrong witness/address type,” consider parsing a known-good Full (P2WPKH) signature and verifying it against a P2PKH address (or vice versa), asserting failure.
340-361: Rename and adjust assertion message: it’s not a length testThe constructed signature is a valid-length (65-byte) compact signature filled with zeros. The failure is due to invalid contents, not length.
@@ - fn test_signature_verification_invalid_signature_length() { + fn test_signature_verification_invalid_signature_contents() { @@ - assert!( - result.is_none(), - "Invalid signature length should fail verification" - ); + assert!( + result.is_none(), + "Invalid/empty compact signature should fail verification" + );
248-261: Assert OP_RETURN script in to_sign output to fully validate structureYou already check value = 0. Adding a script check makes the test stricter and aligns with the implementation emitting OP_RETURN.
@@ - // Verify output is OP_RETURN (unspendable) - let output = &to_sign.output[0]; - assert_eq!(output.value, 0, "Output value should be zero"); + // Verify output is OP_RETURN (unspendable) + let output = &to_sign.output[0]; + assert_eq!(output.value, 0, "Output value should be zero"); + // OP_RETURN opcode is 0x6a + assert_eq!( + output.script_pubkey.as_bytes(), + &[0x6a], + "Output script_pubkey should be OP_RETURN" + );
132-143: setup_test_env likely unnecessary in message hashing testsThese hashing tests don’t appear to require a NEAR VM context. Dropping setup_test_env() will simplify and speed the tests.
Also applies to: 144-158, 160-179, 181-194
110-125: Optionally assert specific error variants for invalid addressesYou have the expected variants listed but don’t assert them. If AddressError exposes variants publicly, consider using matches! to assert specific errors. This catches regressions in parsing behavior.
Example:
for (addr_str, expected) in invalid_addresses { let err = Address::from_str(addr_str).unwrap_err(); match expected { AddressError::UnsupportedFormat => assert!(matches!(err, AddressError::UnsupportedFormat)), AddressError::InvalidLength => assert!(matches!(err, AddressError::InvalidLength)), AddressError::InvalidBech32 => assert!(matches!(err, AddressError::InvalidBech32 | AddressError::InvalidWitnessProgram)), _ => {} } }
535-576: Consider making “working vectors” a strict test once implementation settlesCurrently failures just log. To ensure interoperability doesn’t silently regress, consider asserting that all working vectors verify (or gate strictness behind a cargo feature).
928-928: Run cargo fmt on this fileThe formatter check flagged multiple diffs. Running cargo fmt will normalize style and avoid CI noise.
Also applies to: 938-938, 949-949, 959-959, 970-970, 980-980, 990-990, 1000-1000, 1011-1011, 1021-1021
📜 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.
📒 Files selected for processing (1)
bip322/src/tests.rs(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
bip322/src/tests.rs (5)
bip322/src/transaction.rs (3)
compute_tx_id(151-157)create_to_sign(101-137)create_to_spend(32-76)bip322/src/bitcoin_minimal.rs (6)
new(144-146)new(436-438)new(467-469)from_str(232-356)all_zeros(450-452)from_byte_array(454-456)bip322/src/signature.rs (1)
from_str(84-98)bip322/src/hashing.rs (1)
compute_bip322_message_hash(38-44)bip322/src/lib.rs (2)
hash(44-47)verify(53-59)
🪛 GitHub Check: Format
bip322/src/tests.rs
[warning] 1021-1021:
Diff in /home/runner/work/intents/intents/bip322/src/tests.rs
[warning] 1011-1011:
Diff in /home/runner/work/intents/intents/bip322/src/tests.rs
[warning] 1000-1000:
Diff in /home/runner/work/intents/intents/bip322/src/tests.rs
[warning] 990-990:
Diff in /home/runner/work/intents/intents/bip322/src/tests.rs
[warning] 980-980:
Diff in /home/runner/work/intents/intents/bip322/src/tests.rs
[warning] 970-970:
Diff in /home/runner/work/intents/intents/bip322/src/tests.rs
[warning] 959-959:
Diff in /home/runner/work/intents/intents/bip322/src/tests.rs
[warning] 949-949:
Diff in /home/runner/work/intents/intents/bip322/src/tests.rs
[warning] 938-938:
Diff in /home/runner/work/intents/intents/bip322/src/tests.rs
[warning] 928-928:
Diff in /home/runner/work/intents/intents/bip322/src/tests.rs
⏰ 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). (7)
- GitHub Check: Shared security analysis / Static Analysis (intents-poa-token, contract, near, poa-token, 1, ., 1.18, false, --filter-paths ...
- GitHub Check: Shared security analysis / Static Analysis (intents-poa-factory, contract, near, poa-factory, 1, ., 1.18, false, --filter-pa...
- GitHub Check: Shared security analysis / Static Analysis (intents-contract, contract, near, defuse, 1, ., 1.18, false, --filter-paths "nod...
- GitHub Check: Shared / Static Analysis (intents-poa-factory, contract, near, poa-factory, 1, ., 1.18, false, --filter-pa...
- GitHub Check: Shared / Static Analysis (intents-contract, contract, near, defuse, 1, ., 1.18, false, --filter-paths "nod...
- GitHub Check: Shared / Static Analysis (intents-poa-token, contract, near, poa-token, 1, ., 1.18, false, --filter-paths ...
- GitHub Check: Security Audit - report
🔇 Additional comments (2)
bip322/src/tests.rs (2)
1-10: Great breadth and structure of the BIP-322 test suiteComprehensive coverage across address parsing, message hashing, tx building, signature parsing, official vectors, and wallet-generated vectors. This will pay off in interoperability.
439-446: Resolve inconsistent expectations for Unisat P2WPKH test vectorTwo tests use the same address/signature (bc1qyt6gau643sm52hvej4n4qr34h3878ahs209s27 + H6Gjb…DU=) but contradict each other:
- Earlier in bip322/src/tests.rs (around line 373), the
test_parse_bip322_payload(address, signature, "unisat")vector is documented to expect failure.- Later in the
Bip322TestVectorlist (lines 439–446), the same address/signature is marked withexpected_verification: true.Please confirm the correct BIP-322 outcome for Unisat’s signing method and hash domain:
- If it should fail, change
expected_verificationtofalse(or remove the vector).- If it should succeed, update the earlier test’s comment/assertion to expect success.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (2)
bip322/src/tests.rs (2)
382-395: Helper only asserts success; rename or make it accept an expected outcomeThis helper always unwraps success and is incompatible with tests that expect failure. Either (a) rename to clarify intent (e.g.,
assert_valid_bip322_signature) or (b) add ashould_verify: boolparameter.Example refactor:
-fn test_parse_bip322_payload(address: &str, signature: &str, info_message: &str) { +fn test_parse_bip322_payload( + address: &str, + signature: &str, + info_message: &str, + should_verify: bool, +) { use crate::Bip322Signature; let bip322_signature = Bip322Signature::from_str(signature) .expect("Should parse signature from base64 string"); - let _pubkey = SignedBip322Payload { + let payload = SignedBip322Payload { address: address.parse().unwrap(), message: MESSAGE.to_string(), signature: bip322_signature, - } - .verify() - .unwrap_or_else(|| panic!("Expected valid signature for {info_message}")); + }; + let verified = payload.verify().is_some(); + assert!( + verified == should_verify, + "Expected verification {} for {}", + if should_verify { "success" } else { "failure" }, + info_message + ); }
370-381: Fix test intent mismatch: this test expects failure but calls the success helperThe comment says this vector is invalid and should fail, but the code calls a helper that asserts success. Inline the failure assertion (or make the helper accept an expected result) so intent matches behavior.
Apply this diff:
@@ fn test_parse_signed_bip322_payload_unisat_wallet() { // This test vector appears to be invalid - the signature does not verify against the address // Testing confirmed that neither Bitcoin message signing nor BIP-322 hashing produces // a public key that matches the given address. This test case expects failure. let address = "bc1qyt6gau643sm52hvej4n4qr34h3878ahs209s27"; let signature = "H6Gjb7ArwmAtbS7urzjT1IS+GfGLhz5XgSvu2c863K0+RcxgOFDoD7Uo+Z44CK7NcCLY1tc9eeudsYlM2zCNYDU="; - test_parse_bip322_payload(address, signature, "unisat"); + // Inline the logic to assert failure, instead of delegating to the success-helper + use crate::Bip322Signature; + let bip322_signature = Bip322Signature::from_str(signature).expect("parse base64"); + let payload = SignedBip322Payload { + address: address.parse().unwrap(), + message: MESSAGE.to_string(), + signature: bip322_signature, + }; + assert!( + payload.verify().is_none(), + "Expected verification failure for unisat" + ); }
🧹 Nitpick comments (4)
bip322/src/tests.rs (4)
339-361: Misleading test name/message: it’s invalid content, not lengthThis uses a 65-byte (correct length) all-zero signature. Rename the test and tweak the assertion message for clarity.
Apply this diff:
- fn test_signature_verification_invalid_signature_length() { + fn test_signature_verification_invalid_signature_content() { @@ - let invalid_signature = [0u8; 65]; // Valid 65-byte signature (but empty, so will fail) + let empty_signature = [0u8; 65]; // Correct length but invalid content (all zeros) @@ - signature: crate::Bip322Signature::Compact { - signature: invalid_signature, - }, + signature: crate::Bip322Signature::Compact { signature: empty_signature }, @@ - assert!( - result.is_none(), - "Invalid signature length should fail verification" - ); + assert!(result.is_none(), "Invalid signature content should fail verification");
295-316: Clarify test intent: not “wrong witness type” but “unsupported compact signature for BIP-322”The test constructs a P2PKH payload with a compact signature. For BIP-322, full witness-style signatures are expected. Rename for precision.
Apply this diff:
- fn test_signature_verification_wrong_witness_type() { + fn test_signature_verification_unsupported_compact_signature() { @@ - // Create a P2PKH address but use P2WPKH witness - should fail + // Create a P2PKH address but use a compact signature (non-BIP-322) – should fail @@ - assert!( - result.is_none(), - "Wrong witness type should fail verification" - ); + assert!(result.is_none(), "Unsupported signature format should fail verification");
110-125: Optionally assert exact error kinds for invalid addressesYou already enumerate expected error types but don’t assert them. Matching the concrete error variants would harden the tests.
Example:
let cases = vec![ ("", AddressError::UnsupportedFormat), ("invalid", AddressError::UnsupportedFormat), ("1", AddressError::InvalidLength), ("bc1", AddressError::InvalidBech32), ]; for (s, expected) in cases { match Address::from_str(s) { Err(e) => assert!(matches!(e, _ if e == expected), "Expected {expected:?}, got {e:?} for '{s}'"), Ok(_) => panic!("Expected error for '{s}'"), } }
258-261: Strengthen OP_RETURN assertionYou assert the output value is zero; consider also asserting the script starts with OP_RETURN (0x6a) to pin down intent. This improves regression resistance without affecting readability.
📜 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.
📒 Files selected for processing (1)
bip322/src/tests.rs(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
bip322/src/tests.rs (5)
bip322/src/transaction.rs (3)
compute_tx_id(151-157)create_to_sign(101-137)create_to_spend(32-76)bip322/src/bitcoin_minimal.rs (6)
new(144-146)new(436-438)new(467-469)from_str(232-356)all_zeros(450-452)from_byte_array(454-456)bip322/src/signature.rs (1)
from_str(84-98)bip322/src/hashing.rs (1)
compute_bip322_message_hash(38-44)bip322/src/lib.rs (2)
hash(44-47)verify(53-59)
⏰ 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). (8)
- GitHub Check: Check
- GitHub Check: Shared security analysis / Static Analysis (intents-poa-token, contract, near, poa-token, 1, ., 1.18, false, --filter-paths ...
- GitHub Check: Shared security analysis / Static Analysis (intents-poa-factory, contract, near, poa-factory, 1, ., 1.18, false, --filter-pa...
- GitHub Check: Shared security analysis / Static Analysis (intents-contract, contract, near, defuse, 1, ., 1.18, false, --filter-paths "nod...
- GitHub Check: Shared / Static Analysis (intents-contract, contract, near, defuse, 1, ., 1.18, false, --filter-paths "nod...
- GitHub Check: Shared / Static Analysis (intents-poa-factory, contract, near, poa-factory, 1, ., 1.18, false, --filter-pa...
- GitHub Check: Shared / Static Analysis (intents-poa-token, contract, near, poa-token, 1, ., 1.18, false, --filter-paths ...
- GitHub Check: Security Audit - report
🔇 Additional comments (1)
bip322/src/tests.rs (1)
689-703: LGTM: Official message-hash vectors validatedGreat to see exact matches against the BIP-322 reference hashes for empty and “Hello World” messages.
Summary by CodeRabbit
New Features
Documentation
Tests
Chores
New Utilities