Skip to content

feat(wallet): sr25519 signing standard#270

Open
nohaapav wants to merge 7 commits into
near:mainfrom
nohaapav:feat/wallet-sr25519
Open

feat(wallet): sr25519 signing standard#270
nohaapav wants to merge 7 commits into
near:mainfrom
nohaapav:feat/wallet-sr25519

Conversation

@nohaapav

@nohaapav nohaapav commented May 11, 2026

Copy link
Copy Markdown

Summary

Adds Polkadot/Substrate-compatible Sr25519 (Schnorr on Ristretto255) support to the wallet contract, aligned with the current post-#314 codebase structure.

Changes

defuse-crypto — new sr25519 feature gates a Sr25519 Curve impl backed by schnorrkel with the Substrate b"substrate" signing context. Sr25519PublicKey and Sr25519Signature follow the same shape as the existing Ed25519PublicKey / Ed25519Signature: borsh/serde/schemars/arbitrary derives, fmt::TypedCurve base58 encoding under the sr25519: prefix, and safe TryFrom conversions to/from raw schnorrkel types.

defuse-wallet-sr25519 (new crate at contracts/wallet/signatures/sr25519/) — a wallet variant crate mirroring defuse-wallet-ed25519:

  • WalletSr25519: SignatureSchema verifies proofs over <Bytes>...</Bytes>-wrapped canonical RequestMessage::hash() bytes. That's exactly what Polkadot.js Extension, Talisman, Subwallet, and other Substrate wallets produce via signRaw.
  • WalletSigner<WalletSr25519> is implemented for schnorrkel::Keypair behind the signer feature — clients can sign requests with the SDK just like ed25519.
  • wallet! macro invocation exposes the contract entrypoint under wallet-sr25519 v1.0.0 with the standard reproducible-build metadata.

Wiring — new workspace member + path alias, added to Makefile's CONTRACT_CRATES allowlist so make defuse-wallet-sr25519 / make check-contracts/defuse-wallet-sr25519 work.

Verification

  • cargo fmt --check clean
  • cargo clippy --workspace --all-targets -- -D warnings clean
  • cargo clippy -p defuse-wallet-sr25519 --target wasm32-unknown-unknown --features contract -- -D warnings clean
  • cargo test -p defuse-wallet-sr25519 --features abi,contract,signer — 2 passing (round-trip + wrong-message-rejected)
  • cargo test -p defuse-crypto sr25519 module — 3 passing, including a real Polkadot.js Extension signature kept as regression vector (address 167y8dsUr7kaM1FNoCtXWy2unEnjGHiN7ML3vawR6Nwywbci, message "Hello from Intents!")
  • make check-contracts/defuse-wallet-sr25519 clean

Test plan

  • Curve-level: valid signature verifies, bit-flipped signature rejected, wrong-message signature rejected
  • Wallet schema round-trip via schnorrkel::Keypair + WalletSr25519::verify
  • Wasm target compiles with just the contract feature (ZBA-relevant)
  • End-to-end integration test with defuse-tests — omitted for now; requires reproducible-build wasm artifact (res/defuse-wallet-sr25519.wasm) which will land after this PR ships and a real Polkadot.js flow is exercised

Notes for reviewers

  • UX tradeoff: users sign the 32-byte SHA3-256 request hash wrapped in <Bytes>...</Bytes>, not a human-readable JSON payload. Same UX as the ed25519 variant.
  • No native NEAR host function: unlike env::ed25519_verify, sr25519 verification runs in-WASM via schnorrkel. Wasm size / gas cost impact should be verified before mainnet deployment.

Summary by CodeRabbit

  • New Features
    • Added Sr25519 cryptographic support, including signature verification, signing, key and signature handling, and optional formatting.
    • Added a new Sr25519 wallet signature contract with versioned wallet metadata.
    • Added a wallet signer that creates proofs for request messages.
    • Added build and validation support for the new wallet contract.
  • Tests
    • Added coverage for valid signatures, invalid messages, malformed proofs, and altered signatures.

@coderabbitai

coderabbitai Bot commented May 11, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@mitinarseny, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 10 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ea36a856-fcd1-48db-bb30-705b1df41e4c

📥 Commits

Reviewing files that changed from the base of the PR and between 77ba1d6 and a1a44eb.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • Cargo.toml
  • contracts/wallet/signatures/sr25519/Cargo.toml
  • contracts/wallet/signatures/sr25519/src/lib.rs
  • contracts/wallet/signatures/sr25519/src/signer.rs
  • crates/crypto/Cargo.toml
  • crates/crypto/src/sr25519.rs
📝 Walkthrough

Walkthrough

Adds workspace wiring, schnorrkel-backed SR25519 crypto types and verification, and a new wallet contract supporting signing, proof validation, optional contract registration, formatting, and tests.

Changes

SR25519 Wallet Signature Support

Layer / File(s) Summary
Workspace and feature setup
Cargo.toml, Makefile, crates/crypto/Cargo.toml, crates/crypto/src/lib.rs, contracts/wallet/signatures/sr25519/Cargo.toml
Registers the wallet crate, adds schnorrkel dependencies and feature gates, and includes generated contract build and check targets.
SR25519 crypto adapter
crates/crypto/src/sr25519.rs
Adds curve verification and signing, typed key and signature wrappers, schnorrkel conversions, base58 formatting, and regression tests.
Wallet signature contract
contracts/wallet/signatures/sr25519/src/lib.rs, contracts/wallet/signatures/sr25519/src/signer.rs, contracts/wallet/signatures/sr25519/src/contract.rs
Adds canonical request signing and verification, the generic wallet signer wrapper, contract registration, and round-trip and negative tests.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WalletSr25519Signer
  participant RequestMessage
  participant schnorrkel_Keypair
  participant WalletSr25519
  WalletSr25519Signer->>RequestMessage: hash request
  WalletSr25519Signer->>schnorrkel_Keypair: sign wrapped request hash
  schnorrkel_Keypair-->>WalletSr25519Signer: return signature proof
  WalletSr25519Signer->>WalletSr25519: verify proof
  WalletSr25519->>RequestMessage: hash request
  WalletSr25519-->>WalletSr25519Signer: return verification result
Loading

Possibly related PRs

  • near/intents#226: Both PRs update Makefile-driven contract target generation.
  • near/intents#315: Both add SR25519 signing through the shared Signer abstraction.

Suggested reviewers: fusede, mitinarseny

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding Sr25519 wallet signing support.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with 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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
crates/crypto/src/public_key.rs (1)

100-112: ⚡ Quick win

Lock the sr25519 implicit-account mapping down with a test vector.

This branch introduces a new externally visible address-derivation rule, but the test table below still has no sr25519 case. Please add at least one fixed to_implicit_account_id assertion for an sr25519: key so the "sr25519" prefix and hash slice stay stable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/crypto/src/public_key.rs` around lines 100 - 112, Add a test vector
asserting the new sr25519 implicit-account mapping so the "sr25519" prefix and
keccak slice remain stable: locate the unit tests that assert
to_implicit_account_id (the table of test cases used for other curve variants)
and add at least one entry that constructs a PublicKey::Sr25519 with a fixed
byte sequence and asserts its to_implicit_account_id equals the expected "0x..."
hex string computed from keccak256([b"sr25519", pk].concat())[12..32]; reference
the PublicKey::Sr25519 enum variant and the to_implicit_account_id behavior in
the assertion.
crates/crypto/src/signature.rs (1)

113-114: ⚡ Quick win

Add sr25519 parser regression cases.

The new CurveType::Sr25519 branch is not covered by the parse_ok / parse_invalid_length rstests below, so this path is easier to break than the existing curves.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/crypto/src/signature.rs` around lines 113 - 114, The new
CurveType::Sr25519 branch (in signature.rs) lacks rstest coverage; add
regression tests mirroring the existing parse_ok and parse_invalid_length cases
for other curves to exercise checked_base58_decode_array and the
CurveType::Sr25519 mapping. Create a parse_ok test entry with a valid sr25519
base58 string that decodes to the expected byte array mapped to
CurveType::Sr25519, and a parse_invalid_length entry with a base58 string of
wrong length to assert the parser rejects it; follow the same test
names/structure used for the other curves so checked_base58_decode_array and the
CurveType::Sr25519 branch are exercised.
crates/signatures/sr25519/src/lib.rs (1)

53-67: ⚡ Quick win

Add a JSON round-trip test for the proof shape.

The wallet consumes this type over JSON, but the tests only construct it in Rust. A serde fixture here would lock down flatten plus AsCurve<Sr25519> and catch wire-format regressions early.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/signatures/sr25519/src/lib.rs` around lines 53 - 67, Add a serde JSON
round-trip test for the SignedSr25519Payload shape to ensure flatten +
AsCurve<Sr25519> wire format stays stable: write a unit test that constructs a
SignedSr25519Payload (using Sr25519 keypair/signature utilities from this
crate), serialize it to JSON, compare against a checked-in JSON fixture (or
write and then deserialize the fixture), then deserialize back and assert
equality with the original; place the test near other sr25519 tests and
reference the SignedSr25519Payload type, the payload field, public_key and
signature fields to validate the flattened structure is preserved across
serialization and deserialization.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/crypto/src/public_key.rs`:
- Around line 100-112: Add a test vector asserting the new sr25519
implicit-account mapping so the "sr25519" prefix and keccak slice remain stable:
locate the unit tests that assert to_implicit_account_id (the table of test
cases used for other curve variants) and add at least one entry that constructs
a PublicKey::Sr25519 with a fixed byte sequence and asserts its
to_implicit_account_id equals the expected "0x..." hex string computed from
keccak256([b"sr25519", pk].concat())[12..32]; reference the PublicKey::Sr25519
enum variant and the to_implicit_account_id behavior in the assertion.

In `@crates/crypto/src/signature.rs`:
- Around line 113-114: The new CurveType::Sr25519 branch (in signature.rs) lacks
rstest coverage; add regression tests mirroring the existing parse_ok and
parse_invalid_length cases for other curves to exercise
checked_base58_decode_array and the CurveType::Sr25519 mapping. Create a
parse_ok test entry with a valid sr25519 base58 string that decodes to the
expected byte array mapped to CurveType::Sr25519, and a parse_invalid_length
entry with a base58 string of wrong length to assert the parser rejects it;
follow the same test names/structure used for the other curves so
checked_base58_decode_array and the CurveType::Sr25519 branch are exercised.

In `@crates/signatures/sr25519/src/lib.rs`:
- Around line 53-67: Add a serde JSON round-trip test for the
SignedSr25519Payload shape to ensure flatten + AsCurve<Sr25519> wire format
stays stable: write a unit test that constructs a SignedSr25519Payload (using
Sr25519 keypair/signature utilities from this crate), serialize it to JSON,
compare against a checked-in JSON fixture (or write and then deserialize the
fixture), then deserialize back and assert equality with the original; place the
test near other sr25519 tests and reference the SignedSr25519Payload type, the
payload field, public_key and signature fields to validate the flattened
structure is preserved across serialization and deserialization.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 22a67744-4b64-4108-bee2-8c424481ad86

📥 Commits

Reviewing files that changed from the base of the PR and between 1823243 and f8b3799.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (14)
  • Cargo.toml
  • contracts/wallet/Cargo.toml
  • contracts/wallet/src/contract/impl_.rs
  • contracts/wallet/src/signature/mod.rs
  • contracts/wallet/src/signature/sr25519.rs
  • crates/crypto/Cargo.toml
  • crates/crypto/src/curve/mod.rs
  • crates/crypto/src/curve/sr25519.rs
  • crates/crypto/src/lib.rs
  • crates/crypto/src/parse.rs
  • crates/crypto/src/public_key.rs
  • crates/crypto/src/signature.rs
  • crates/signatures/sr25519/Cargo.toml
  • crates/signatures/sr25519/src/lib.rs

…etto255)

support:

- `defuse-crypto` gains an `sr25519` feature and a `Sr25519` `Curve`
  impl backed by `schnorrkel` with the Substrate `"substrate"` signing
  context.
- `defuse-wallet-sr25519` is a new wallet variant that verifies
  proofs over `<Bytes>...</Bytes>`-wrapped canonical `RequestMessage`
  hashes — matching what Polkadot.js Extension, Talisman, and other
  Substrate wallets produce via `signRaw`.
- A `WalletSigner<WalletSr25519>` implementation is provided for
  `schnorrkel::Keypair` behind the `signer` feature.
@nohaapav
nohaapav force-pushed the feat/wallet-sr25519 branch from f8b3799 to 488d16b Compare July 14, 2026 09:47
nohaapav added 2 commits July 19, 2026 16:01
Adapts the sr25519 wallet variant to the new `Signer<C>` trait pattern
introduced by near#315.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants