-
Notifications
You must be signed in to change notification settings - Fork 19
Add ADR-36 signatures support #195
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
📝 WalkthroughWalkthroughAdds a new adr36 crate providing ADR-36 payload and signed-payload types, integrates it into the workspace, exposes Adr36 payloads in core, implements extraction and verification paths, and updates MultiPayload to include an Adr36 variant with tests. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Adr36 as adr36::lib
participant Signer
participant Core
participant Secp as Secp256k1
Client->>Adr36: create Adr36Payload(message, signer)
Adr36->>Adr36: prehash() (Amino JSON → SHA-256)
Client->>Signer: request sign(prehash)
Signer->>Adr36: return signature
Adr36->>Core: submit SignedAdr36Payload (MultiPayload::Adr36)
Core->>Secp: verify(signature, payload.hash())
Secp-->>Core: verification result (pubkey / None)
Core->>Client: return verification outcome / extracted JSON
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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. Comment |
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
🤖 Fix all issues with AI agents
In `@adr36/Cargo.toml`:
- Around line 1-19: The Cargo.toml declares an unused dependency
defuse-near-utils; remove the defuse-near-utils workspace entry from the
[dependencies] section (delete the line containing "defuse-near-utils.workspace
= true") so the crate no longer depends on it and then run cargo check to
confirm no missing imports.
In `@adr36/src/lib.rs`:
- Around line 118-119: Rename the test function symbol test_expected_sh256_hash
to the correctly spelled test_expected_sha256_hash; update the #[test] function
declaration for test_expected_sh256_hash to fn test_expected_sha256_hash() and
adjust any references or calls to that test name (if any) so they match the new
identifier to ensure the test runs and naming is consistent.
🧹 Nitpick comments (2)
adr36/src/lib.rs (2)
93-101: Consider extracting sharedfix_v_in_signaturehelper.This function is noted as copy-paste from
erc191/lib.rs. Consider extracting it to a shared location indefuse-cryptoto avoid duplication and ensure consistent behavior across payload types.
141-153: Consider strengthening the negative test assertion.The current test uses
assert_ne!which only verifies the returned public key doesn't matchREFERENCE_PUBKEY. If the goal is to verify that a wrong signature fails verification entirely, consider also checking thatverify()returnsNoneor a different public key.More explicit negative test
#[test] fn test_reference_signature_verification_fails() { let payload = Adr36Payload::new( REFERENCE_MESSAGE.to_string(), REFERENCE_SECP256K1_SIGNER.to_string(), ); let signature = fix_v_in_signature(WRONG_REFERENCE_SIGNATURE); - assert_ne!( - SignedAdr36Payload { payload, signature }.verify(), - Some(REFERENCE_PUBKEY) - ); + let result = SignedAdr36Payload { payload, signature }.verify(); + // Verify either verification fails entirely (None) or recovers a different key + assert!( + result.is_none() || result != Some(REFERENCE_PUBKEY), + "Expected verification to fail or recover different pubkey" + ); }
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (7)
Cargo.tomladr36/Cargo.tomladr36/src/lib.rscore/Cargo.tomlcore/src/payload/adr36.rscore/src/payload/mod.rscore/src/payload/multi.rs
🧰 Additional context used
🧬 Code graph analysis (2)
core/src/payload/adr36.rs (2)
core/src/payload/mod.rs (2)
extract_defuse_payload(43-43)extract_defuse_payload(50-52)core/src/payload/multi.rs (1)
extract_defuse_payload(106-117)
adr36/src/lib.rs (2)
core/src/payload/multi.rs (2)
hash(67-78)verify(85-96)crypto/src/signature.rs (1)
sig(159-159)
⏰ 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 (15)
Cargo.toml (1)
5-5: LGTM!The workspace member and path dependency are correctly added following the existing alphabetical ordering and patterns.
Also applies to: 49-49
core/Cargo.toml (1)
10-10: LGTM!The dependency is correctly added using the workspace pattern, consistent with other payload-related dependencies.
core/src/payload/mod.rs (1)
1-1: LGTM!Module declaration correctly placed in alphabetical order before
erc191.core/src/payload/multi.rs (3)
5-5: LGTM!Import correctly added for the new ADR-36 payload type.
76-76: LGTM!The
Adr36variant is correctly integrated into all three trait implementations:
Payload::hash()delegates to the inner payload's hashSignedPayload::verify()maps toPublicKey::Secp256k1(correct for Cosmos SDK which uses secp256k1)ExtractDefusePayload::extract_defuse_payload()delegates extractionThe pattern is consistent with other payload types.
Also applies to: 94-94, 115-115
54-58: LGTM!The
Adr36variant is well-documented with references to the ADR-36 specification and Keplr wallet implementation docs. The ADR-36 prehash construction (base64-encoded message within a standard JSON structure), signature verification using secp256k1, and comprehensive test coverage with reference vectors from the cosmos-sdk all confirm the implementation is correct and spec-compliant.core/src/payload/adr36.rs (2)
5-15: LGTM!The delegation pattern for
SignedAdr36Payloadis consistent with how other signed payload types work in the codebase. The#[inline]hint is appropriate for this thin wrapper.
17-26: LGTM!The JSON parsing from
self.messageis the correct approach for ADR-36 payloads, where the message field contains the serializedDefusePayload. This aligns with theprehash()implementation inadr36/src/lib.rsthat base64-encodes the message.adr36/src/lib.rs (7)
7-20: LGTM!Good documentation with links to the ADR-36 specification and Keplr usage docs. The Bech32 address format explanation for the
signerfield is helpful for maintainability.
22-26: LGTM!The
const fnconstructor is appropriate here. Input validation (e.g., Bech32 format) can reasonably be deferred to signature verification time.
54-59: LGTM!The SHA-256 hash of the prehashed bytes is correct per ADR-36 specification.
61-68: LGTM!The
SignedAdr36Payloadstructure correctly wraps the payload with a Secp256k1 signature, using theAsCurveserde wrapper for proper serialization.
70-75: LGTM!Correctly delegates to the inner payload's hash, which is the standard pattern for signed payloads.
77-84: LGTM!The
SignedPayloadimplementation correctly uses Secp256k1 verification. The integration withMultiPayloadincore/src/payload/multi.rs(line 94) properly maps the result toPublicKey::Secp256k1.
28-51: No action required. The JSON key ordering is correct.The
serde_json::json!macro usesBTreeMapby default, which automatically sorts keys in lexicographic order. This is the correct behavior for ADR-36's canonical JSON serialization requirement. The keys in the prehash JSON are already properly ordered:account_number,chain_id,fee,memo,msgs,sequence. Nopreserve_orderfeature is needed; the default behavior already produces deterministic, sorted output for correct signature verification.Likely an incorrect or invalid review comment.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
pityjllk
left a comment
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.
lgtm
mitinarseny
left a comment
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.
@kuksag thanks for the PR! Overall, looks good to me, see just a minor comment below
| { | ||
| "type": "sign/MsgSignData", | ||
| "value": { | ||
| "data": STANDARD.encode(self.message.as_bytes()), |
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.
According to docs:
It's applications developers decision how Data should be treated, by treated we mean the serialization and deserialization process and the Object Data should represent.
Since we already represent self.message as String, does it make sense to avoid base64 encoding here and embed message as-is?
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.
Good catch. I'm gonna say I've overlooked that, because I was referencing implementations of the most used wallets on Cosmos SDK, and they wrap a message into base64:
- keplr
https://github.com/chainapsis/keplr-wallet/blob/59b2e18122dc2ec3b12d3005fec709e4bcc885f8/packages/cosmos/src/adr-36/amino.ts#L78 - leap wallet
https://github.com/leapwallet/cosmos-extension/blob/d6747ff73851d8e958dfec9ef1da4809100d694a/packages/wallet-provider/src/provider/core.ts#L295
To comply with the standard and to have something working in practise, we can verify the signature against both b64(message) (as of right now) and plaindata.
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.
Ah, ok, if that's a convention across ecosystem - I'm ok with leaving it in current state, i.e. always encode as base64
Summary by CodeRabbit
New Features
Tests
Chores
✏️ Tip: You can customize this high-level summary in your review settings.