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}