Skip to main content

mina_signer/
lib.rs

1#![deny(missing_docs)]
2#![deny(unsafe_code)]
3#![deny(clippy::all)]
4#![deny(clippy::pedantic)]
5#![deny(clippy::nursery)]
6#![doc = include_str!("../README.md")]
7#![no_std]
8
9extern crate alloc;
10
11use alloc::{
12    string::{String, ToString},
13    vec,
14    vec::Vec,
15};
16use ark_ec::AffineRepr;
17use core::cmp::{Eq, PartialEq};
18pub use keypair::Keypair;
19pub use mina_curves::pasta::Pallas as CurvePoint;
20use mina_hasher::{DomainParameter, Hashable};
21pub use pubkey::{CompressedPubKey, PubKey};
22pub use schnorr::Schnorr;
23pub use seckey::SecKey;
24use serde::{Deserialize, Serialize};
25pub use signature::Signature;
26
27pub mod keypair;
28pub mod pubkey;
29pub mod schnorr;
30pub mod seckey;
31pub mod signature;
32
33/// Base field element type
34pub type BaseField = <CurvePoint as AffineRepr>::BaseField;
35
36/// Scalar field element type
37pub type ScalarField = <CurvePoint as AffineRepr>::ScalarField;
38
39/// Mina network (or blockchain) identifier
40#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
41pub enum NetworkId {
42    /// Id for all testnets
43    TESTNET = 0x00,
44
45    /// Id for mainnet
46    MAINNET = 0x01,
47}
48
49impl From<NetworkId> for u8 {
50    fn from(id: NetworkId) -> Self {
51        id as Self
52    }
53}
54
55impl NetworkId {
56    /// Convert the network ID to its domain string representation for
57    /// cryptographic hashing.
58    ///
59    /// This is used in the `Hashable` trait's `domain_string` method to provide
60    /// domain separation for signature hashing.
61    ///
62    /// Returns:
63    /// - `"MinaSignatureMainnet"` for `NetworkId::MAINNET`
64    /// - `"CodaSignature"` for `NetworkId::TESTNET`
65    #[must_use]
66    pub fn into_domain_string(self) -> String {
67        match self {
68            Self::MAINNET => "MinaSignatureMainnet".to_string(),
69            Self::TESTNET => "CodaSignature".to_string(),
70        }
71    }
72}
73
74impl DomainParameter for NetworkId {
75    fn into_bytes(self) -> Vec<u8> {
76        vec![self as u8]
77    }
78}
79
80/// Nonce derivation mode for signature generation.
81///
82/// Controls how the deterministic nonce is derived during signing.
83/// Different transaction types require different nonce derivation methods.
84///
85/// These modes correspond to the `Message.Legacy` and `Message.Chunked` modules
86/// in the OCaml Mina implementation (`src/lib/crypto/signature_lib/schnorr.ml`).
87#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
88pub enum NonceMode {
89    /// Legacy nonce derivation for user commands.
90    ///
91    /// Use this mode for legacy Mina transactions (user commands) such as
92    /// payments and delegations. This corresponds to `Message.Legacy` in
93    /// the OCaml implementation.
94    ///
95    /// Uses direct byte serialization (`ROInput.to_bytes()`) for nonce derivation.
96    Legacy,
97
98    /// Chunked nonce derivation for zkApp transactions.
99    ///
100    /// Use this mode for zkApp transactions. This mode is compatible with
101    /// o1js and uses field packing for nonce derivation. This corresponds
102    /// to `Message.Chunked` in the OCaml implementation.
103    ///
104    /// Uses field packing (`ROInput.to_fields()`) then bit conversion for
105    /// nonce derivation.
106    Chunked,
107}
108
109/// Interface for signed objects
110///
111/// Signer interface for signing [`Hashable`] inputs and verifying
112/// [`Signatures`](Signature) using [`Keypairs`](Keypair) and
113/// [`PubKeys`](PubKey)
114pub trait Signer<H: Hashable> {
115    /// Sign `input` (see [`Hashable`]) using keypair `kp` and return the
116    /// corresponding signature.
117    ///
118    /// # Parameters
119    ///
120    /// * `kp` - The keypair to use for signing
121    /// * `input` - The message to sign (must implement [`Hashable`])
122    /// * `nonce_mode` - Controls nonce derivation method:
123    ///   - [`NonceMode::Legacy`]: For user commands (payments, delegations)
124    ///   - [`NonceMode::Chunked`]: For zkApp transactions (o1js compatible)
125    ///
126    /// # Returns
127    ///
128    /// A [`Signature`] over the input message.
129    fn sign(&mut self, kp: &Keypair, input: &H, nonce_mode: NonceMode) -> Signature;
130
131    /// Verify that the signature `sig` on `input` (see [`Hashable`]) is signed
132    /// with the secret key corresponding to `pub_key`.
133    /// Return `true` if the signature is valid and `false` otherwise.
134    fn verify(&mut self, sig: &Signature, pub_key: &PubKey, input: &H) -> bool;
135}
136
137/// Create a legacy signer context with domain parameters initialized with
138/// `domain_param`
139///
140/// **Example**
141///
142/// ```
143/// #[path = "../tests/transaction.rs"]
144/// mod transaction;
145/// use mina_signer::{NetworkId, self, Signer};
146/// use transaction::Transaction;
147///
148/// let mut ctx = mina_signer::create_legacy::<Transaction>(NetworkId::TESTNET);
149/// ```
150pub fn create_legacy<H: 'static + Hashable>(domain_param: H::D) -> impl Signer<H> {
151    schnorr::create_legacy::<H>(domain_param)
152}
153
154/// Create a kimchi signer context for `ZkApp` signing (Berkeley upgrade)
155/// with domain parameters initialized with `domain_param`
156///
157/// **Example**
158///
159/// ```
160/// #[path = "../tests/transaction.rs"]
161/// mod transaction;
162/// use mina_signer::{NetworkId, self, Signer};
163/// use transaction::Transaction;
164///
165/// let mut ctx = mina_signer::create_kimchi::<Transaction>(NetworkId::TESTNET);
166/// ```
167pub fn create_kimchi<H: 'static + Hashable>(domain_param: H::D) -> impl Signer<H> {
168    schnorr::create_kimchi::<H>(domain_param)
169}