forked from stellar/rs-soroban-poseidon
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlib.rs
More file actions
197 lines (186 loc) · 5.83 KB
/
lib.rs
File metadata and controls
197 lines (186 loc) · 5.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
#![no_std]
use soroban_sdk::{
bytesn,
crypto::{bls12_381::Fr as BlsScalar, BnScalar},
symbol_short, Env, Symbol, Vec, U256,
};
pub(crate) mod poseidon;
pub(crate) mod poseidon2;
#[cfg(test)]
mod tests;
pub use poseidon::{PoseidonConfig, PoseidonSponge};
pub use poseidon2::{Poseidon2Config, Poseidon2Sponge};
pub trait Field {
fn symbol() -> Symbol;
/// Returns the field modulus. Inputs to Poseidon/Poseidon2 must be less than this value.
fn modulus(env: &Env) -> U256;
}
impl Field for BnScalar {
fn symbol() -> Symbol {
symbol_short!("BN254")
}
fn modulus(env: &Env) -> U256 {
// BN254 scalar field modulus
U256::from_be_bytes(
env,
&bytesn!(
env,
0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
)
.into(),
)
}
}
impl Field for BlsScalar {
fn symbol() -> Symbol {
symbol_short!("BLS12_381")
}
fn modulus(env: &Env) -> U256 {
// BLS12-381 scalar field modulus
U256::from_be_bytes(
env,
&bytesn!(
env,
0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001
)
.into(),
)
}
}
/// Computes a Poseidon hash matching circom's
/// [implementation](https://github.com/iden3/circomlib/blob/master/circuits/poseidon.circom).
///
/// # Type Parameters
///
/// - `T`: State size. Must equal `inputs.len() + 1` (rate = T-1, capacity = 1).
/// - `F`: Field type. Use [`BnScalar`] for BN254 or [`BlsScalar`] for BLS12-381.
///
/// # Supported Configurations
///
/// - BN254: `T` ∈ {2, 3, 4, 5, 6} (i.e., 1–5 inputs)
/// - BLS12-381: `T` ∈ {2, 3, 4, 5, 6} (i.e., 1–5 inputs)
///
/// # Panics
///
/// - if `inputs.len() != T - 1`
/// - if any input value ≥ the field modulus (inputs must be valid field elements)
///
/// # Example
///
/// ```
/// use soroban_sdk::{bytesn, crypto::BnScalar, vec, Env, U256};
/// use soroban_poseidon::poseidon_hash;
///
/// let env = Env::default();
///
/// // Hash two field elements (t=3)
/// let inputs = vec![
/// &env,
/// U256::from_u32(&env, 1),
/// U256::from_u32(&env, 2),
/// ];
/// let hash = poseidon_hash::<3, BnScalar>(&env, &inputs);
///
/// // Matches circom's Poseidon([1, 2])
/// let expected = U256::from_be_bytes(
/// &env,
/// &bytesn!(&env, 0x115cc0f5e7d690413df64c6b9662e9cf2a3617f2743245519e19607a4417189a).into(),
/// );
/// assert_eq!(hash, expected);
/// ```
///
/// # Repeated Hashing
///
/// For repeated hashing, create a [`PoseidonSponge`] once and call
/// `compute_hash()` multiple times. This reuses the pre-initialized parameters
/// (MDS matrix and round constants), but each hash computation is independent,
/// i.e. the sponge state is reset between calls:
///
/// ```
/// # use soroban_sdk::{crypto::BnScalar, vec, Env, U256};
/// # use soroban_poseidon::PoseidonSponge;
/// # let env = Env::default();
/// let mut sponge = PoseidonSponge::<3, BnScalar>::new(&env);
///
/// let inputs1 = vec![&env, U256::from_u32(&env, 1), U256::from_u32(&env, 2)];
/// let inputs2 = vec![&env, U256::from_u32(&env, 3), U256::from_u32(&env, 4)];
///
/// let h1 = sponge.compute_hash(&inputs1); // fresh hash
/// let h2 = sponge.compute_hash(&inputs2); // another fresh hash (state was reset)
/// ```
pub fn poseidon_hash<const T: u32, F: Field>(env: &Env, inputs: &Vec<U256>) -> U256
where
PoseidonSponge<T, F>: PoseidonConfig<T, F>,
{
let mut sponge = PoseidonSponge::<T, F>::new(env);
sponge.compute_hash(inputs)
}
/// Computes a Poseidon2 hash matching noir's
/// [implementation](https://github.com/noir-lang/noir/blob/master/noir_stdlib/src/hash/poseidon2.nr).
///
/// # Type Parameters
///
/// - `T`: State size. Must be ≥ `inputs.len() + 1`. Common usage is `T=4`
/// (rate=3) matching noir's default.
/// - `F`: Field type. Use [`BnScalar`] for BN254 or [`BlsScalar`] for
/// BLS12-381.
///
/// # Supported Configurations
///
/// - BN254: `T` ∈ {2, 3, 4} (i.e., rate = 1, 2, or 3)
/// - BLS12-381: `T` ∈ {2, 3, 4} (i.e., rate = 1, 2, or 3)
///
/// # Panics
///
/// - if `inputs.len() > T - 1` (rate exceeded)
/// - if any input value ≥ the field modulus (inputs must be valid field elements)
///
/// # Capacity Initialization
///
/// The capacity element is initialized to `inputs.len() << 64`, matching noir's
/// Poseidon2 implementation.
///
/// # Example
///
/// ```
/// use soroban_sdk::{crypto::BnScalar, vec, Env, U256};
/// use soroban_poseidon::poseidon2_hash;
///
/// let env = Env::default();
///
/// // Hash three field elements (t=4, rate=3)
/// let inputs = vec![
/// &env,
/// U256::from_u32(&env, 1),
/// U256::from_u32(&env, 2),
/// U256::from_u32(&env, 3),
/// ];
/// let hash = poseidon2_hash::<4, BnScalar>(&env, &inputs);
/// ```
///
/// # Repeated Hashing
///
/// For repeated hashing, create a [`Poseidon2Sponge`] once and call
/// `compute_hash()` multiple times. This reuses the pre-initialized parameters
/// (diagonal matrix and round constants), but each hash computation is
/// independent, i.e. the sponge state is reset between calls:
///
/// ```
/// # use soroban_sdk::{crypto::BnScalar, vec, Env, U256};
/// # use soroban_poseidon::Poseidon2Sponge;
/// # let env = Env::default();
/// let mut sponge = Poseidon2Sponge::<4, BnScalar>::new(&env);
///
/// let inputs1 = vec![&env, U256::from_u32(&env, 1), U256::from_u32(&env, 2)];
/// let inputs2 = vec![&env, U256::from_u32(&env, 3), U256::from_u32(&env, 4)];
///
/// let h1 = sponge.compute_hash(&inputs1); // fresh hash
/// let h2 = sponge.compute_hash(&inputs2); // another fresh hash (state was reset)
/// ```
pub fn poseidon2_hash<const T: u32, F: Field>(env: &Env, inputs: &Vec<U256>) -> U256
where
Poseidon2Sponge<T, F>: Poseidon2Config<T, F>,
{
let mut sponge = Poseidon2Sponge::<T, F>::new(env);
sponge.compute_hash(inputs)
}