Skip to content

Commit 9b392b4

Browse files
authored
Merge pull request #49 from Quantus-Network/hash-updates
feat: poseidon2 integration
2 parents ddd07bb + fae34b3 commit 9b392b4

File tree

9 files changed

+93
-79
lines changed

9 files changed

+93
-79
lines changed

Cargo.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

voting/src/lib.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ impl CircuitFragment for VoteCircuitData {
123123
fn circuit(targets: &Self::Targets, builder: &mut CircuitBuilder<F, D>) {
124124
// --- 1. Merkle Proof Verification ---
125125
let leaf_hash_targets = builder
126-
.hash_n_to_hash_no_pad::<plonky2::hash::poseidon::PoseidonHash>(
126+
.hash_n_to_hash_no_pad_p2::<plonky2::hash::poseidon2::Poseidon2Hash>(
127127
targets.private_key.elements.to_vec(),
128128
);
129129
let mut current_hash_targets = leaf_hash_targets;
@@ -159,7 +159,9 @@ impl CircuitFragment for VoteCircuitData {
159159
combined_elements.extend(&right_elements);
160160

161161
let parent_hash_candidacy = builder
162-
.hash_n_to_hash_no_pad::<plonky2::hash::poseidon::PoseidonHash>(combined_elements);
162+
.hash_n_to_hash_no_pad_p2::<plonky2::hash::poseidon2::Poseidon2Hash>(
163+
combined_elements,
164+
);
163165

164166
let mut next_hash_elements = Vec::with_capacity(DIGEST_NUM_FIELD_ELEMENTS);
165167
for k in 0..DIGEST_NUM_FIELD_ELEMENTS {
@@ -184,7 +186,7 @@ impl CircuitFragment for VoteCircuitData {
184186
nullifier_input_elements.extend_from_slice(&targets.proposal_id.elements);
185187

186188
let computed_nullifier_targets = builder
187-
.hash_n_to_hash_no_pad::<plonky2::hash::poseidon::PoseidonHash>(
189+
.hash_n_to_hash_no_pad_p2::<plonky2::hash::poseidon2::Poseidon2Hash>(
188190
nullifier_input_elements,
189191
);
190192

@@ -265,7 +267,7 @@ mod voting_tests {
265267
use super::*;
266268
use plonky2::{
267269
field::types::Field,
268-
hash::poseidon::PoseidonHash,
270+
hash::poseidon2::Poseidon2Hash,
269271
iop::witness::PartialWitness,
270272
plonk::{circuit_data::CircuitConfig, config::Hasher},
271273
};
@@ -275,11 +277,11 @@ mod voting_tests {
275277
};
276278

277279
fn compute_nullifier(private_key: &PrivateKey, proposal_id: &Digest) -> Digest {
278-
let pk_hash = PoseidonHash::hash_no_pad(private_key).elements;
280+
let pk_hash = Poseidon2Hash::hash_no_pad(private_key).elements;
279281
let mut input = [F::ZERO; 8];
280282
input[..4].copy_from_slice(&pk_hash);
281283
input[4..].copy_from_slice(proposal_id);
282-
PoseidonHash::hash_no_pad(&input).elements
284+
Poseidon2Hash::hash_no_pad(&input).elements
283285
}
284286

285287
fn create_test_inputs() -> VoteCircuitData {
@@ -291,7 +293,7 @@ mod voting_tests {
291293
];
292294
let leaves: Vec<Digest> = private_keys_for_tree
293295
.iter()
294-
.map(|bytes| PoseidonHash::hash_no_pad(&digest_bytes_to_felts(*bytes)).elements)
296+
.map(|bytes| Poseidon2Hash::hash_no_pad(&digest_bytes_to_felts(*bytes)).elements)
295297
.collect();
296298

297299
// Build the Merkle tree level by level
@@ -306,7 +308,7 @@ mod voting_tests {
306308
let mut combined = [F::ZERO; 8];
307309
combined[..4].copy_from_slice(&current_level[i]);
308310
combined[4..].copy_from_slice(&current_level[i + 1]);
309-
next_level.push(PoseidonHash::hash_no_pad(&combined).elements);
311+
next_level.push(Poseidon2Hash::hash_no_pad(&combined).elements);
310312
} else {
311313
next_level.push(current_level[i]);
312314
}

wormhole/circuit/src/nullifier.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::codec::ByteCodec;
1414
use crate::codec::FieldElementCodec;
1515
use crate::inputs::CircuitInputs;
1616
use plonky2::{
17-
hash::{hash_types::HashOutTarget, poseidon::PoseidonHash},
17+
hash::{hash_types::HashOutTarget, poseidon2::Poseidon2Hash},
1818
iop::{
1919
target::Target,
2020
witness::{PartialWitness, WitnessWrite},
@@ -75,8 +75,8 @@ impl Nullifier {
7575
preimage.extend(secret);
7676
preimage.extend(transfer_count);
7777

78-
let inner_hash = PoseidonHash::hash_no_pad(&preimage).elements;
79-
let outer_hash = PoseidonHash::hash_no_pad(&inner_hash).elements;
78+
let inner_hash = Poseidon2Hash::hash_no_pad(&preimage).elements;
79+
let outer_hash = Poseidon2Hash::hash_no_pad(&inner_hash).elements;
8080
let hash = Digest::from(outer_hash);
8181

8282
Self {
@@ -251,9 +251,9 @@ impl CircuitFragment for Nullifier {
251251
preimage.extend(transfer_count);
252252

253253
// Compute the `generated_account` by double-hashing the preimage (salt + secret).
254-
let inner_hash = builder.hash_n_to_hash_no_pad::<PoseidonHash>(preimage);
254+
let inner_hash = builder.hash_n_to_hash_no_pad_p2::<Poseidon2Hash>(preimage);
255255
let computed_hash =
256-
builder.hash_n_to_hash_no_pad::<PoseidonHash>(inner_hash.elements.to_vec());
256+
builder.hash_n_to_hash_no_pad_p2::<Poseidon2Hash>(inner_hash.elements.to_vec());
257257

258258
// Assert that hashes are equal.
259259
builder.connect_hashes(computed_hash, hash);

wormhole/circuit/src/storage_proof/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use zk_circuits_common::{
2020
pub mod leaf;
2121

2222
pub const MAX_PROOF_LEN: usize = 20;
23-
pub const PROOF_NODE_MAX_SIZE_F: usize = 188; // Should match the felt preimage max set on poseidon-resonance crate.
23+
pub const PROOF_NODE_MAX_SIZE_F: usize = 189; // Should match the felt preimage max set on poseidon-resonance crate.
2424
pub const PROOF_NODE_MAX_SIZE_B: usize = 256;
2525
pub const FELTS_PER_AMOUNT: usize = 2;
2626

@@ -147,7 +147,7 @@ impl CircuitFragment for StorageProof {
147147
}: &Self::Targets,
148148
builder: &mut CircuitBuilder<F, D>,
149149
) {
150-
use plonky2::hash::poseidon::PoseidonHash;
150+
use plonky2::hash::poseidon2::Poseidon2Hash;
151151
use zk_circuits_common::gadgets::is_const_less_than;
152152

153153
let leaf_targets_32_bit = leaf_inputs.collect_32_bit_targets();
@@ -158,7 +158,7 @@ impl CircuitFragment for StorageProof {
158158

159159
// Calculate the leaf inputs hash.
160160
let leaf_inputs_hash =
161-
builder.hash_n_to_hash_no_pad::<PoseidonHash>(leaf_inputs.collect_to_vec());
161+
builder.hash_n_to_hash_no_pad_p2::<Poseidon2Hash>(leaf_inputs.collect_to_vec());
162162

163163
// constant 2^32 for (lo + hi * 2^32) reconstruction
164164
let two_pow_32 = builder.constant(F::from_canonical_u64(1u64 << 32));
@@ -177,7 +177,7 @@ impl CircuitFragment for StorageProof {
177177
let is_leaf_node = builder.is_equal(i_t, proof_len);
178178

179179
// Compute the hash of this node and compare it against the previous hash.
180-
let computed_hash = builder.hash_n_to_hash_no_pad::<PoseidonHash>(node.clone());
180+
let computed_hash = builder.hash_n_to_hash_no_pad_p2::<Poseidon2Hash>(node.clone());
181181
for y in 0..4 {
182182
let diff = builder.sub(computed_hash.elements[y], prev_hash.elements[y]);
183183
let result = builder.mul(diff, is_proof_node.target);

wormhole/circuit/src/unspendable_account.rs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use alloc::vec::Vec;
22
use core::mem::size_of;
33

44
use plonky2::{
5-
hash::{hash_types::HashOutTarget, poseidon::PoseidonHash},
5+
hash::{hash_types::HashOutTarget, poseidon2::Poseidon2Hash},
66
iop::witness::{PartialWitness, WitnessWrite},
77
plonk::{circuit_builder::CircuitBuilder, config::Hasher},
88
};
@@ -46,12 +46,8 @@ impl UnspendableAccount {
4646
}
4747

4848
// Hash twice to get the account id.
49-
let inner_hash = PoseidonHash::hash_no_pad(&preimage).elements;
50-
let outer_hash = PoseidonHash::hash_no_pad(&inner_hash).elements;
51-
// println!(
52-
// "accountId: {:?}",
53-
// hex::encode(*digest_felts_to_bytes(outer_hash))
54-
// );
49+
let inner_hash = Poseidon2Hash::hash_no_pad(&preimage).elements;
50+
let outer_hash = Poseidon2Hash::hash_no_pad(&inner_hash).elements;
5551
let account_id = Digest::from(outer_hash);
5652

5753
Self {
@@ -184,9 +180,9 @@ impl CircuitFragment for UnspendableAccount {
184180
preimage.extend(secret.elements.iter());
185181

186182
// Compute the `generated_account` by double-hashing the preimage (salt + secret).
187-
let inner_hash = builder.hash_n_to_hash_no_pad::<PoseidonHash>(preimage.clone());
183+
let inner_hash = builder.hash_n_to_hash_no_pad_p2::<Poseidon2Hash>(preimage.clone());
188184
let generated_account =
189-
builder.hash_n_to_hash_no_pad::<PoseidonHash>(inner_hash.elements.to_vec());
185+
builder.hash_n_to_hash_no_pad_p2::<Poseidon2Hash>(inner_hash.elements.to_vec());
190186

191187
// Assert that hashes are equal.
192188
builder.connect_hashes(generated_account, account_id);

wormhole/example/src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use plonky2::hash::poseidon::PoseidonHash;
1+
use plonky2::hash::poseidon2::Poseidon2Hash;
22
use plonky2::plonk::circuit_data::CircuitConfig;
33
use plonky2::plonk::config::Hasher;
44
use plonky2::plonk::proof::ProofWithPublicInputs;
@@ -26,7 +26,7 @@ fn main() -> anyhow::Result<()> {
2626
leaf_inputs_felts.extend_from_slice(&funding_account.0);
2727
leaf_inputs_felts.extend_from_slice(&unspendable_account);
2828
leaf_inputs_felts.extend_from_slice(&u128_to_felts(funding_amount));
29-
let leaf_inputs_hash = PoseidonHash::hash_no_pad(&leaf_inputs_felts);
29+
let leaf_inputs_hash = Poseidon2Hash::hash_no_pad(&leaf_inputs_felts);
3030
let root_hash = digest_felts_to_bytes(leaf_inputs_hash.elements);
3131

3232
let exit_account_id = 8226349481601990196u64;

wormhole/tests/src/circuit/circuit_data_tests.rs

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use anyhow::Context;
22
use anyhow::Result;
33
use plonky2::field::types::Field;
4-
use plonky2::hash::poseidon::PoseidonHash;
4+
use plonky2::hash::poseidon2::Poseidon2Hash;
55
use plonky2::plonk::circuit_data::CircuitConfig;
66
use plonky2::plonk::config::{Hasher, PoseidonGoldilocksConfig};
77
use plonky2::util::serialization::{DefaultGateSerializer, DefaultGeneratorSerializer};
@@ -10,17 +10,22 @@ use serde::de::DeserializeOwned;
1010
use std::path::{Path, PathBuf};
1111
use std::process::Command;
1212
use std::{env, fs};
13+
use test_helpers::storage_proof::TestInputs;
14+
use test_helpers::DEFAULT_FUNDING_ACCOUNT;
15+
use test_helpers::DEFAULT_SECRETS;
1316
use wormhole_circuit::circuit::circuit_logic::WormholeCircuit;
1417
use wormhole_circuit::circuit::{circuit_data_from_bytes, circuit_data_to_bytes};
1518
use wormhole_circuit::inputs::{CircuitInputs, PrivateCircuitInputs, PublicCircuitInputs};
1619
use wormhole_circuit::nullifier::Nullifier;
20+
use wormhole_circuit::storage_proof::leaf::LeafInputs;
1721
use wormhole_circuit::storage_proof::ProcessedStorageProof;
1822
use wormhole_circuit::substrate_account::SubstrateAccount;
1923
use wormhole_circuit::unspendable_account::UnspendableAccount;
2024
use wormhole_prover::WormholeProver;
2125
use wormhole_verifier::WormholeVerifier;
2226
use zk_circuits_common::circuit::{TransferProofJson, D, F};
23-
use zk_circuits_common::utils::u64_to_felts;
27+
use zk_circuits_common::utils::felts_to_u128;
28+
use zk_circuits_common::utils::felts_to_u64;
2429
use zk_circuits_common::utils::BytesDigest;
2530
use zk_circuits_common::utils::{digest_felts_to_bytes, u128_to_felts};
2631

@@ -140,33 +145,42 @@ fn test_prover_and_verifier_from_file_e2e() -> Result<()> {
140145
let verifier = WormholeVerifier::new_from_files(&verifier_path, &common_path)?;
141146

142147
// Create inputs
143-
let funding_account = SubstrateAccount::new(&[2u8; 32])?;
144-
let secret = BytesDigest::try_from([1u8; 32]).unwrap();
145-
let unspendable_account = UnspendableAccount::from_secret(secret).account_id;
146-
let funding_amount = 1000u128;
147-
let transfer_count = 0u64;
148+
let leaf_inputs = LeafInputs::test_inputs_0();
149+
let secret =
150+
BytesDigest::try_from(hex::decode(DEFAULT_SECRETS[0]).unwrap().as_slice()).unwrap();
151+
let funding_account = leaf_inputs.funding_account;
152+
// let secret = BytesDigest::try_from([1u8; 32]).unwrap();
153+
let unspendable_account = leaf_inputs.to_account;
154+
let funding_amount = leaf_inputs.funding_amount;
155+
let transfer_count = leaf_inputs.transfer_count;
148156

149157
let mut leaf_inputs_felts = Vec::new();
150-
leaf_inputs_felts.extend(&u64_to_felts(transfer_count));
151-
leaf_inputs_felts.extend_from_slice(&funding_account.0);
152-
leaf_inputs_felts.extend_from_slice(&unspendable_account);
153-
leaf_inputs_felts.extend_from_slice(&u128_to_felts(funding_amount));
158+
leaf_inputs_felts.extend(transfer_count);
159+
leaf_inputs_felts.extend_from_slice(funding_account.as_slice());
160+
leaf_inputs_felts.extend_from_slice(unspendable_account.as_slice());
161+
leaf_inputs_felts.extend(funding_amount);
154162

155-
let leaf_inputs_hash = PoseidonHash::hash_no_pad(&leaf_inputs_felts);
163+
let transfer_count = felts_to_u64(transfer_count).unwrap();
164+
let funding_amount = felts_to_u128(funding_amount).unwrap();
165+
166+
let leaf_inputs_hash = Poseidon2Hash::hash_no_pad(&leaf_inputs_felts);
156167
let root_hash: [u8; 32] = *digest_felts_to_bytes(leaf_inputs_hash.elements);
157168

169+
// print the hex of the root hash
170+
println!("root_hash: {}", hex::encode(root_hash));
171+
158172
let exit_account = SubstrateAccount::new(&[2u8; 32])?;
159173
let inputs = CircuitInputs {
160174
private: PrivateCircuitInputs {
161175
secret,
162176
funding_account: (*funding_account).into(),
163177
storage_proof: ProcessedStorageProof::new(vec![], vec![]).unwrap(),
164-
unspendable_account: (unspendable_account).into(),
178+
unspendable_account: (*unspendable_account).into(),
165179
transfer_count,
166180
},
167181
public: PublicCircuitInputs {
168182
funding_amount,
169-
nullifier: Nullifier::from_preimage(secret, 0).hash.into(),
183+
nullifier: Nullifier::from_preimage(secret, transfer_count).hash.into(),
170184
root_hash: root_hash.try_into().unwrap(),
171185
exit_account: (*exit_account).into(),
172186
},
@@ -269,10 +283,7 @@ fn test_prover_and_verifier_fuzzing() -> Result<()> {
269283
ProcessedStorageProof::new(storage_proof_bytes, proof_json.indices.clone())
270284
.context("failed to build ProcessedStorageProof")?;
271285

272-
let funding_account = SubstrateAccount::new(&[
273-
223, 23, 232, 59, 97, 108, 223, 113, 2, 89, 54, 39, 126, 65, 248, 106, 156, 219, 7,
274-
123, 213, 197, 228, 118, 177, 81, 61, 77, 23, 89, 200, 80,
275-
])?; // Alice test account from dev node.
286+
let funding_account = SubstrateAccount::new(&DEFAULT_FUNDING_ACCOUNT)?;
276287
let secret = BytesDigest::try_from(secret.as_slice()).unwrap();
277288
let unspendable_account = UnspendableAccount::from_secret(secret).account_id;
278289

wormhole/tests/src/circuit/unspendable_account_tests.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,20 @@ use zk_circuits_common::{
1010

1111
#[cfg(test)]
1212
const SECRETS: [&str; 5] = [
13-
"cd94df2e3c38a87f3e429b62af022dbe4363143811219d80037e8798b2ec9229",
14-
"8b680b2421968a0c1d3cff6f3408e9d780157ae725724a78c3bc0998d1ac8194",
13+
"4c8587bd422e01d961acdc75e7d66f6761b7af7c9b1864a492f369c9d6724f05",
14+
"c6034553e5556630d24a593d2c92de9f1ede81d48f0fb3371764462cc3594b3f",
1515
"87f5fc11df0d12f332ccfeb92ddd8995e6c11709501a8b59c2aaf9eefee63ec1",
1616
"ef69da4e3aa2a6f15b3a9eec5e481f17260ac812faf1e685e450713327c3ab1c",
1717
"9aa84f99ef2de22e3070394176868df41d6a148117a36132d010529e19b018b7",
1818
];
1919

2020
#[cfg(test)]
2121
const ADDRESSES: [&str; 5] = [
22-
"b209bdf6636fd7a3a224b9e62dde4acf7a93ecc7d19f618990e34bdeae8e1455",
23-
"aebdf7b4136139bbda4d8b5b4cfe3726dfdd64c842e16f79ad8033f8044c3b7e",
24-
"f5fc29c796b56aeabc3d3d9bd113d6b958f434b0919e207d81c3ded261331677",
25-
"c18c0dfb3f71945ea7cf1ecfdd110a6ed1c2d0cdde5db0b2d05c60e14bc2da83",
26-
"96d45bf29b88b160511748dba781606b10e1f5f9dfdc9d7350e7d57676f65e43",
22+
"4d38abc959eb7e11526fd632c73d47e8945972fa3d9ce3d62532d5f386353993",
23+
"8213d62e0104abe36482ef26346e0d5cd1d7511b22e4b03c770ca2c687b0ed04",
24+
"7c281f0265adab691f06195b30deb4d133477a363355c584143827210b19bb09",
25+
"5511b416ec05918b6fbc78fbd61d2575be3bd9d5f931b0f2438f7f5f7d46ae6e",
26+
"ae18069d04d3fb4b3eb1fb41d6b5bf51b1bad41ff95d067b65116a1f5a68ba09",
2727
];
2828

2929
#[cfg(test)]
@@ -49,6 +49,8 @@ fn preimage_matches_right_address() {
4949
for (secret, address) in SECRETS.iter().zip(ADDRESSES) {
5050
let decoded_secret: [u8; 32] = hex::decode(secret).unwrap().try_into().unwrap();
5151
let decoded_address = hex::decode(address).unwrap();
52+
// print the decoded address
53+
println!("decoded_address: {:?}", decoded_address);
5254
let unspendable_account =
5355
UnspendableAccount::from_secret(decoded_secret.try_into().unwrap());
5456

0 commit comments

Comments
 (0)