Skip to content

feat(wallet): sr25519 signing standard#270

Open
nohaapav wants to merge 1 commit into
near:mainfrom
nohaapav:feat/wallet-sr25519
Open

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

Conversation

@nohaapav

@nohaapav nohaapav commented May 11, 2026

Copy link
Copy Markdown

Adds Polkadot/Substrate-compatible Sr25519 (Schnorr on Ristretto255)

Re-cuts the curve work from #171 onto the current layout, wired wallet-only.

Changes

  • defuse-crypto — new Sr25519 Curve behind a sr25519 feature
    (schnorrkel = "0.11.5" dep). Adds Sr25519PublicKey / Sr25519Signature
    and CurveType::Sr25519 = 3 / PublicKey::Sr25519 / Signature::Sr25519
    variants. Implicit account derivation:
    0x{keccak256("sr25519" || pk)[12..32]} — same Eth-Implicit schema as
    P256, distinct prefix to avoid cross-curve collisions.

  • crates/signatures/sr25519/ (new) — generic payload crate matching
    the sep53 / tip191 shape (Sr25519Payload { payload: String } +
    SignedSr25519Payload). verify reproduces the Substrate
    <Bytes>...</Bytes> envelope that every Polkadot wallet (Polkadot.js,
    Talisman, Subwallet, Nova, …) applies automatically before signing.
    Ships a real-wallet signature as a regression vector.

  • contracts/walletsr25519 feature flag, cargo-near reproducible
    build variant, and a SigningStandard<&RequestMessage> adapter. The
    proof is a JSON SignedSr25519Payload whose inner payload is the
    JSON-encoded RequestMessage; the adapter compares that to
    serde_json::to_string(msg) and calls signed.verify(). Registers
    wallet-sr25519 v1.0.0 in contract metadata.

Out of scope

References

Summary by CodeRabbit

  • New Features
    • Added Sr25519 signature support, enabling Polkadot/Substrate-compatible message signing and verification through the Schnorr algorithm

Review Change Stack

Adds Polkadot/Substrate-compatible Sr25519 (Schnorr on Ristretto255)
@coderabbitai

coderabbitai Bot commented May 11, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This pull request adds comprehensive SR25519 (Schnorr on Ristretto255) signature support to the workspace. It introduces a new crates/signatures/sr25519 crate implementing Polkadot/Substrate-compatible payload wrapping and verification, extends the crypto foundation with Sr25519 curve traits and schnorrkel integration, adds Sr25519 variants to existing PublicKey and Signature enums, and wires the signing standard into the wallet contract as an optional feature.

Changes

SR25519 Signature Support

Layer / File(s) Summary
Workspace & Manifest Setup
Cargo.toml, crates/crypto/Cargo.toml, crates/signatures/sr25519/Cargo.toml
Register crates/signatures/sr25519 workspace member with path dependency. Add schnorrkel optional dependency and sr25519 feature to crates/crypto. Define defuse-sr25519 package with runtime dependencies on defuse-crypto, impl-tools, near-sdk, and dev-dependencies on test utilities and schnorrkel.
Crypto Curve Foundation
crates/crypto/src/curve/mod.rs, crates/crypto/src/curve/sr25519.rs
Introduce Sr25519 curve type with SIGNING_CTX = b"substrate". Implement Curve::verify() using schnorrkel to validate signatures against wrapped Substrate-format messages. Add CurveType::Sr25519 = 3 variant. Create Sr25519PublicKey and Sr25519Signature newtypes with base58 serialization and FromStr support.
Public Key & Signature Type Extensions
crates/crypto/src/public_key.rs, crates/crypto/src/signature.rs
Extend PublicKey enum with Sr25519 variant (discriminant 3) and matching logic for curve_type(), data(), and implicit account ID derivation using keccak256 with "sr25519" prefix. Extend Signature enum with Sr25519 variant with matching logic for curve_type(), data(), and FromStr parsing. Add ABI schema examples and helpers for both types.
Sr25519 Payload & Verification
crates/signatures/sr25519/src/lib.rs
Define Sr25519Payload wrapping user messages with <Bytes>...</Bytes> Substrate format for signing/verification. Implement Payload trait hashing wrapped bytes via SHA-256. Add SignedSr25519Payload bundling payload, 32-byte public key, and 64-byte signature. Implement SignedPayload::verify() delegating to Sr25519::verify(). Include tests for byte-wrapping, hashing, signature round-trips, rejection of invalid signatures, and regression vector from Polkadot.js.
Wallet Contract Integration
contracts/wallet/Cargo.toml, contracts/wallet/src/signature/mod.rs, contracts/wallet/src/signature/sr25519.rs, contracts/wallet/src/contract/impl_.rs
Add defuse-sr25519 optional dependency and sr25519 feature to wallet crate, wiring to defuse-crypto/sr25519. Add reproducible build variant with contract,sr25519 features. Export sr25519 module from signature index. Implement Sr25519 signing standard struct with SigningStandard<&RequestMessage> trait that deserializes signature as SignedSr25519Payload, serializes message to JSON, validates payload match, and verifies via public key. Register conditional impl ContractImpl for Contract setting SigningStandard = crate::signature::sr25519::Sr25519.
Feature Gate Expansion
crates/crypto/src/lib.rs, crates/crypto/src/parse.rs
Expand cfg(any(...)) guards on public_key, signature, and serde modules to include sr25519 feature alongside existing ed25519, secp256k1, p256. Reformat multi-line for consistency.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • near/intents#206: Introduces wallet contract scaffolding and feature-gated ContractImpl patterns that this PR builds upon for Sr25519 integration.

Suggested reviewers

  • fusede
  • mitinarseny
  • pityjllk

Poem

🐰 The schnorrkel hops with Substrate's grace,
Sr25519 finds its place,
With bytes wrapped tight in angle-worn,
A new signing standard's born!
Sign and verify, all is well,
Polkadot's secrets we now tell! 🔐

🚥 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 PR title accurately summarizes the main change: adding sr25519 signing standard support to the wallet contract.
Docstring Coverage ✅ Passed Docstring coverage is 93.94% 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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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 and usage tips.

@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

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.

2 participants