diff --git a/README.md b/README.md index 87de527..17cc288 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,11 @@ Hash functions: * [x] Hash Two to One (For use with Merkle Proofs) * [x] Merkle Proof Gadget * [x] Delta Merkle Proof Gadget +- [x] Sha256-192 (Sha256 Truncated to 192 Bits) + * [x] Hash Arbitrary Length Data + * [x] Hash Two to One (For use with Merkle Proofs) + * [x] Merkle Proof Gadget + * [x] Delta Merkle Proof Gadget - [x] Keccak256 * [x] Hash Arbitrary Length Data * [ ] Hash Two to One (For use with Merkle Proofs) diff --git a/src/hash/merkle_utils.rs b/src/hash/merkle_utils.rs index c37dde5..490e493 100644 --- a/src/hash/merkle_utils.rs +++ b/src/hash/merkle_utils.rs @@ -1,8 +1,10 @@ -use plonky2::hash::hash_types::RichField; +use std::fmt::Display; + +use plonky2::{hash::hash_types::{RichField, HashOut}}; use serde::{Deserialize, Serialize}; use serde_with::serde_as; -use super::{sha256::WitnessHashSha2, sha256_merkle::{MerkleProofSha256Gadget, DeltaMerkleProofSha256Gadget}, WitnessHash}; +use super::{sha256::WitnessHashSha2, sha256_merkle::{MerkleProofSha256Gadget, DeltaMerkleProofSha256Gadget}, WitnessHash, sha256_truncated_merkle::{MerkleProofTruncatedSha256Gadget, DeltaMerkleProofTruncatedSha256Gadget}}; #[serde_as] #[derive(Serialize, Deserialize, PartialEq, Clone, Copy)] @@ -18,6 +20,80 @@ impl Hash256 { } } + +impl Display for Hash256 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", hex::encode(&self.0)) + } +} +#[serde_as] +#[derive(Serialize, Deserialize, PartialEq, Clone, Copy)] +pub struct Hash192(#[serde_as(as = "serde_with::hex::Hex")] pub [u8; 24]); + +impl Hash192 { + pub fn from_str(s: &str) -> Result { + let bytes = hex::decode(s).unwrap(); + assert_eq!(bytes.len(), 24); + let mut array = [0u8; 24]; + array.copy_from_slice(&bytes); + Ok(Self(array)) + } +} + +impl Display for Hash192 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", hex::encode(&self.0)) + } +} +fn read_u48_in_field_from_bytes(bytes: &[u8; 24], index: usize) -> F { + // leave as non-canonical incase of field with a prime <= 48 bits + F::from_noncanonical_u64((bytes[index] as u64) << 40 + | (bytes[index+1] as u64) << 32 + | (bytes[index+2] as u64) << 24 + | (bytes[index+3] as u64) << 16 + | (bytes[index+4] as u64) << 8 + | (bytes[index+5] as u64)) +} +impl From<&Hash192> for HashOut { + fn from(bytes: &Hash192) -> Self { + HashOut { elements: [ + read_u48_in_field_from_bytes(&bytes.0, 0), + read_u48_in_field_from_bytes(&bytes.0, 6), + read_u48_in_field_from_bytes(&bytes.0, 12), + read_u48_in_field_from_bytes(&bytes.0, 18), + ] } + } +} + +impl Hash192 { + pub fn to_hash_out(&self) -> HashOut { + HashOut { elements: [ + read_u48_in_field_from_bytes(&self.0, 0), + read_u48_in_field_from_bytes(&self.0, 6), + read_u48_in_field_from_bytes(&self.0, 12), + read_u48_in_field_from_bytes(&self.0, 18), + ] } + } + pub fn from_hash_out(hash: HashOut)->Self { + let mut bytes = [0u8; 24]; + for i in 0..4 { + let element = hash.elements[i].to_canonical_u64(); + bytes[i*6] = (element >> 40) as u8; + bytes[i*6+1] = (element >> 32) as u8; + bytes[i*6+2] = (element >> 24) as u8; + bytes[i*6+3] = (element >> 16) as u8; + bytes[i*6+4] = (element >> 8) as u8; + bytes[i*6+5] = element as u8; + } + Self(bytes) + } +} +impl From for HashOut { + fn from(hash192: Hash192) -> Self { + hash192.to_hash_out() + } +} + #[serde_as] #[derive(Serialize, Deserialize, PartialEq, Clone)] pub struct MerkleProof { @@ -88,6 +164,9 @@ pub fn verify_delta_merkle_proof>( pub type MerkleProof256 = MerkleProof; pub type DeltaMerkleProof256 = DeltaMerkleProof; +pub type MerkleProof192 = MerkleProof; +pub type DeltaMerkleProof192 = DeltaMerkleProof; + impl MerkleProofSha256Gadget { pub fn set_witness_from_proof>( &self, @@ -102,8 +181,6 @@ impl MerkleProofSha256Gadget { } } - - impl DeltaMerkleProofSha256Gadget { pub fn set_witness_from_proof>( &self, @@ -118,3 +195,32 @@ impl DeltaMerkleProofSha256Gadget { } } } + +impl MerkleProofTruncatedSha256Gadget { + pub fn set_witness_from_proof>( + &self, + witness: &mut W, + merkle_proof: &MerkleProof192, + ) { + witness.set_hash192_target(&self.value, &merkle_proof.value.0); + witness.set_target(self.index, F::from_noncanonical_u64(merkle_proof.index)); + for (i, sibling) in self.siblings.iter().enumerate() { + witness.set_hash192_target(sibling, &merkle_proof.siblings[i].0); + } + } +} + +impl DeltaMerkleProofTruncatedSha256Gadget { + pub fn set_witness_from_proof>( + &self, + witness: &mut W, + merkle_proof: &DeltaMerkleProof192, + ) { + witness.set_hash192_target(&self.old_value, &merkle_proof.old_value.0); + witness.set_hash192_target(&self.new_value, &merkle_proof.new_value.0); + witness.set_target(self.index, F::from_noncanonical_u64(merkle_proof.index)); + for (i, sibling) in self.siblings.iter().enumerate() { + witness.set_hash192_target(sibling, &merkle_proof.siblings[i].0); + } + } +} diff --git a/src/hash/mod.rs b/src/hash/mod.rs index 90db853..cddd341 100644 --- a/src/hash/mod.rs +++ b/src/hash/mod.rs @@ -1,7 +1,9 @@ pub mod keccak256; pub mod sha256; +pub mod sha256_truncated; pub mod types; pub use types::*; pub mod merkle_utils; -pub mod sha256_merkle; \ No newline at end of file +pub mod sha256_merkle; +pub mod sha256_truncated_merkle; \ No newline at end of file diff --git a/src/hash/sha256.rs b/src/hash/sha256.rs index 1293270..337f308 100644 --- a/src/hash/sha256.rs +++ b/src/hash/sha256.rs @@ -46,6 +46,7 @@ pub trait CircuitBuilderHashSha2, const D: usize> { fn hash_sha256(&mut self, hash: &HashInputTarget) -> HashOutputTarget; fn sha256_input_padding(&mut self, target: &HashInputTarget, padding_len: u64); + fn hash_sha256_u32(&mut self, data: &[U32Target]) -> Hash256Target; fn two_to_one_sha256(&mut self, left: Hash256Target, right: Hash256Target) -> Hash256Target; } @@ -70,7 +71,7 @@ pub const K32: [u32; 64] = [ ]; // (a rrot r1) xor (a rrot r2) xor (a rsh s3) -fn sigma, const D: usize>( +pub fn sigma, const D: usize>( builder: &mut CircuitBuilder, a: U32Target, r1: u8, @@ -85,7 +86,7 @@ fn sigma, const D: usize>( } // (a rrot r1) xor (a rrot r2) xor (a rrot r3) -fn big_sigma, const D: usize>( +pub fn big_sigma, const D: usize>( builder: &mut CircuitBuilder, a: U32Target, r1: u8, @@ -100,7 +101,7 @@ fn big_sigma, const D: usize>( } // (e and f) xor ((not e) and g) -fn ch, const D: usize>( +pub fn ch, const D: usize>( builder: &mut CircuitBuilder, e: U32Target, f: U32Target, @@ -117,7 +118,7 @@ fn ch, const D: usize>( // (a and b) xor (a and c) xor (b and c) // = (a and (b xor c)) xor (b and c) // we can calculate (b xor c), (b and c) in a single op -fn maj, const D: usize>( +pub fn maj, const D: usize>( builder: &mut CircuitBuilder, a: U32Target, b: U32Target, @@ -131,6 +132,146 @@ fn maj, const D: usize>( builder.and_xor_b32_to_u32(abc, b_and_c).1 } +pub fn sha256_start_state, const D: usize>( + builder: &mut CircuitBuilder, +) -> [U32Target; 8] { + [ + builder.constant_u32(H256_256[0]), + builder.constant_u32(H256_256[1]), + builder.constant_u32(H256_256[2]), + builder.constant_u32(H256_256[3]), + builder.constant_u32(H256_256[4]), + builder.constant_u32(H256_256[5]), + builder.constant_u32(H256_256[6]), + builder.constant_u32(H256_256[7]), + ] +} + +pub fn sha256_round_constants, const D: usize>( + builder: &mut CircuitBuilder, +) -> [U32Target; 64] { + core::array::from_fn(|i| builder.constant_u32(K32[i])) +} + +pub fn sha256_digest_block, const D: usize>( + builder: &mut CircuitBuilder, + state: &mut [U32Target], + block_data: &[U32Target], + k256: &[U32Target], +) { + let mut a = state[0]; + let mut b = state[1]; + let mut c = state[2]; + let mut d = state[3]; + let mut e = state[4]; + let mut f = state[5]; + let mut g = state[6]; + let mut h = state[7]; + + let mut w = [ + block_data[0], + block_data[1], + block_data[2], + block_data[3], + block_data[4], + block_data[5], + block_data[6], + block_data[7], + block_data[8], + block_data[9], + block_data[10], + block_data[11], + block_data[12], + block_data[13], + block_data[14], + block_data[15], + ]; + + for i in 0..64 { + // Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array + if i >= 16 { + let s0 = sigma(builder, w[(i + 1) & 0xf], 7, 18, 3); + let s1 = sigma(builder, w[(i + 14) & 0xf], 17, 19, 10); + w[i & 0xf] = builder + .add_many_u32(&[s0, s1, w[(i + 9) & 0xf], w[i & 0xf]]) + .0; + } + + // Compression function main loop + let big_s1_e = big_sigma(builder, e, 6, 11, 25); + let ch_efg = ch(builder, e, f, g); + let temp1 = builder + .add_many_u32(&[h, big_s1_e, ch_efg, k256[i], w[i & 0xf]]) + .0; + + let big_s0_a = big_sigma(builder, a, 2, 13, 22); + let maj_abc = maj(builder, a, b, c); + let temp2 = builder.add_u32_lo(big_s0_a, maj_abc); + + h = g; + g = f; + f = e; + e = builder.add_u32_lo(d, temp1); + d = c; + c = b; + b = a; + a = builder.add_u32_lo(temp1, temp2); // add_many_u32 of 3 elements is the same + } + + // Add the compressed chunk to the current hash value + state[0] = builder.add_u32_lo(state[0], a); + state[1] = builder.add_u32_lo(state[1], b); + state[2] = builder.add_u32_lo(state[2], c); + state[3] = builder.add_u32_lo(state[3], d); + state[4] = builder.add_u32_lo(state[4], e); + state[5] = builder.add_u32_lo(state[5], f); + state[6] = builder.add_u32_lo(state[6], g); + state[7] = builder.add_u32_lo(state[7], h); +} + +fn sha256_digest_u32_array, const D: usize>( + builder: &mut CircuitBuilder, + data: &[U32Target], +) -> Hash256Target { + let mut state = sha256_start_state(builder); + let round_constants = sha256_round_constants(builder); + let standard_rounds = data.len() / 16; + for i in 0..standard_rounds { + sha256_digest_block( + builder, + &mut state, + &data[i * 16..i * 16 + 16], + &round_constants, + ); + } + let remaining = data.len() - standard_rounds * 16; + let zero = builder.zero_u32(); + if remaining <= 13 { + let mut block_data = [zero; 16]; + for i in 0..remaining { + block_data[i] = data[standard_rounds * 16 + i]; + } + block_data[remaining] = builder.constant_u32(0x80000000); + let len_bits = (data.len() as u64) * 32; + block_data[14] = builder.constant_u32((len_bits >> 32) as u32); + block_data[15] = builder.constant_u32((len_bits & 0xffffffff) as u32); + + sha256_digest_block(builder, &mut state, &block_data, &round_constants); + } else { + let mut block_data = [zero; 32]; + for i in 0..remaining { + block_data[i] = data[standard_rounds * 16 + i]; + } + block_data[remaining] = builder.constant_u32(0x80000000); + let len_bits = (data.len() as u64) * 32; + block_data[30] = builder.constant_u32((len_bits >> 32) as u32); + block_data[31] = builder.constant_u32((len_bits & 0xffffffff) as u32); + sha256_digest_block(builder, &mut state, &block_data[0..16], &round_constants); + sha256_digest_block(builder, &mut state, &block_data[16..32], &round_constants); + } + state +} + impl, const D: usize> CircuitBuilderHashSha2 for CircuitBuilder { @@ -168,20 +309,10 @@ impl, const D: usize> CircuitBuilderHashSha2 let input_bits = hash.input_bits; let block_num = input_bits / 512; - let mut state = Vec::::new(); - - // Initialize hash values: - // (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19) - for item in &H256_256 { - state.push(self.constant_u32(*item)); - } - + let mut state = sha256_start_state(self); // Initialize array of round constants: // (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311) - let mut k256 = Vec::new(); - for item in &K32 { - k256.push(self.constant_u32(*item)); - } + let k256 = sha256_round_constants(self); // Pre-processing (Padding) // Padding is done by the Witness when setting the input value to the target @@ -189,55 +320,7 @@ impl, const D: usize> CircuitBuilderHashSha2 // Process the message in successive 512-bit chunks for blk in 0..block_num { let mut w: [U32Target; 16] = input[blk * 16..blk * 16 + 16].try_into().unwrap(); - - // Initialize working variables to current hash value - let mut a = state[0]; - let mut b = state[1]; - let mut c = state[2]; - let mut d = state[3]; - let mut e = state[4]; - let mut f = state[5]; - let mut g = state[6]; - let mut h = state[7]; - - for i in 0..64 { - // Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array - if i >= 16 { - let s0 = sigma(self, w[(i + 1) & 0xf], 7, 18, 3); - let s1 = sigma(self, w[(i + 14) & 0xf], 17, 19, 10); - w[i & 0xf] = self.add_many_u32(&[s0, s1, w[(i + 9) & 0xf], w[i & 0xf]]).0; - } - - // Compression function main loop - let big_s1_e = big_sigma(self, e, 6, 11, 25); - let ch_efg = ch(self, e, f, g); - let temp1 = self - .add_many_u32(&[h, big_s1_e, ch_efg, k256[i], w[i & 0xf]]) - .0; - - let big_s0_a = big_sigma(self, a, 2, 13, 22); - let maj_abc = maj(self, a, b, c); - let temp2 = self.add_u32_lo(big_s0_a, maj_abc); - - h = g; - g = f; - f = e; - e = self.add_u32_lo(d, temp1); - d = c; - c = b; - b = a; - a = self.add_u32_lo(temp1, temp2); // add_many_u32 of 3 elements is the same - } - - // Add the compressed chunk to the current hash value - state[0] = self.add_u32_lo(state[0], a); - state[1] = self.add_u32_lo(state[1], b); - state[2] = self.add_u32_lo(state[2], c); - state[3] = self.add_u32_lo(state[3], d); - state[4] = self.add_u32_lo(state[4], e); - state[5] = self.add_u32_lo(state[5], f); - state[6] = self.add_u32_lo(state[6], g); - state[7] = self.add_u32_lo(state[7], h); + sha256_digest_block(self, &mut state, &mut w, &k256); } // Produce the final hash value (big-endian) @@ -247,6 +330,10 @@ impl, const D: usize> CircuitBuilderHashSha2 output } + fn hash_sha256_u32(&mut self, data: &[U32Target]) -> Hash256Target { + sha256_digest_u32_array(self, data) + } + // https://en.wikipedia.org/wiki/SHA-2#Pseudocode fn two_to_one_sha256(&mut self, left: Hash256Target, right: Hash256Target) -> Hash256Target { let mut state: Hash256Target = [ @@ -262,224 +349,117 @@ impl, const D: usize> CircuitBuilderHashSha2 // Initialize array of round constants: // (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311) - let mut k256 = Vec::new(); - for item in &K32 { - k256.push(self.constant_u32(*item)); - } + let k256 = sha256_round_constants(self); // Pre-processing (Padding) // Padding is done by the Witness when setting the input value to the target - // Process the 512-bit message - let mut w: [U32Target; 16] = [ + // block 1 data (left and right) + let w: [U32Target; 16] = [ left[0], left[1], left[2], left[3], left[4], left[5], left[6], left[7], right[0], right[1], right[2], right[3], right[4], right[5], right[6], right[7], ]; + // digest block 1 + sha256_digest_block(self, &mut state, &w, &k256); - // Initialize working variables to current hash value - let mut a = state[0]; - let mut b = state[1]; - let mut c = state[2]; - let mut d = state[3]; - let mut e = state[4]; - let mut f = state[5]; - let mut g = state[6]; - let mut h = state[7]; - - for i in 0..64 { - // Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array - if i >= 16 { - let s0 = sigma(self, w[(i + 1) & 0xf], 7, 18, 3); - let s1 = sigma(self, w[(i + 14) & 0xf], 17, 19, 10); - w[i & 0xf] = self.add_many_u32(&[s0, s1, w[(i + 9) & 0xf], w[i & 0xf]]).0; - } - - // Compression function main loop - let big_s1_e = big_sigma(self, e, 6, 11, 25); - let ch_efg = ch(self, e, f, g); - let temp1 = self - .add_many_u32(&[h, big_s1_e, ch_efg, k256[i], w[i & 0xf]]) - .0; - - let big_s0_a = big_sigma(self, a, 2, 13, 22); - let maj_abc = maj(self, a, b, c); - let temp2 = self.add_u32_lo(big_s0_a, maj_abc); - - h = g; - g = f; - f = e; - e = self.add_u32_lo(d, temp1); - d = c; - c = b; - b = a; - a = self.add_u32_lo(temp1, temp2); // add_many_u32 of 3 elements is the same - } - - // Add the compressed chunk to the current hash value - state[0] = self.add_u32_lo(state[0], a); - state[1] = self.add_u32_lo(state[1], b); - state[2] = self.add_u32_lo(state[2], c); - state[3] = self.add_u32_lo(state[3], d); - state[4] = self.add_u32_lo(state[4], e); - state[5] = self.add_u32_lo(state[5], f); - state[6] = self.add_u32_lo(state[6], g); - state[7] = self.add_u32_lo(state[7], h); - - // round 2 let zero = self.constant_u32(0); let cx80 = self.constant_u32(0x80000000); let c512 = self.constant_u32(512); - let mut w: [U32Target; 16] = [ - cx80, - zero, - zero, - zero, - zero, - zero, - zero, - zero, - zero, - zero, - zero, - zero, - zero, - zero, - zero, - c512, + // block 2 (padding/length in bits) + let w: [U32Target; 16] = [ + cx80, zero, zero, zero, zero, zero, zero, zero, zero, zero, zero, zero, zero, zero, + zero, c512, ]; - // Initialize working variables to current hash value - let mut a = state[0]; - let mut b = state[1]; - let mut c = state[2]; - let mut d = state[3]; - let mut e = state[4]; - let mut f = state[5]; - let mut g = state[6]; - let mut h = state[7]; - - for i in 0..64 { - // Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array - if i >= 16 { - let s0 = sigma(self, w[(i + 1) & 0xf], 7, 18, 3); - let s1 = sigma(self, w[(i + 14) & 0xf], 17, 19, 10); - w[i & 0xf] = self.add_many_u32(&[s0, s1, w[(i + 9) & 0xf], w[i & 0xf]]).0; - } - - // Compression function main loop - let big_s1_e = big_sigma(self, e, 6, 11, 25); - let ch_efg = ch(self, e, f, g); - let temp1 = self - .add_many_u32(&[h, big_s1_e, ch_efg, k256[i], w[i & 0xf]]) - .0; - - let big_s0_a = big_sigma(self, a, 2, 13, 22); - let maj_abc = maj(self, a, b, c); - let temp2 = self.add_u32_lo(big_s0_a, maj_abc); - - h = g; - g = f; - f = e; - e = self.add_u32_lo(d, temp1); - d = c; - c = b; - b = a; - a = self.add_u32_lo(temp1, temp2); // add_many_u32 of 3 elements is the same - } - - // Add the compressed chunk to the current hash value - state[0] = self.add_u32_lo(state[0], a); - state[1] = self.add_u32_lo(state[1], b); - state[2] = self.add_u32_lo(state[2], c); - state[3] = self.add_u32_lo(state[3], d); - state[4] = self.add_u32_lo(state[4], e); - state[5] = self.add_u32_lo(state[5], f); - state[6] = self.add_u32_lo(state[6], g); - state[7] = self.add_u32_lo(state[7], h); + // digest block 2 + sha256_digest_block(self, &mut state, &w, &k256); state } } #[cfg(test)] mod tests { + use std::time::Instant; + use hex; + use num::BigUint; use plonky2::iop::witness::PartialWitness; use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::circuit_data::CircuitConfig; use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; use sha2::{Digest, Sha256}; + use crate::biguint::CircuitBuilderBiguint; + use crate::hash::merkle_utils::Hash256; use crate::hash::sha256::{CircuitBuilderHashSha2, WitnessHashSha2}; use crate::hash::{CircuitBuilderHash, WitnessHash}; - use crate::hash::merkle_utils::{Hash256}; const SHA256_BLOCK: usize = 512; #[test] fn test_sha256_two_to_one() { - let tests = [ [ - "44205ea3a71ee1cbd02eef7b084a409450c21d11a3b41769f02bb3e2dd89d5e2", - "8ecf785b86dd1715d4c193f280a118b82200742f102bf1e59a4a65194a126f03", - "a452e23aab1e4baae2e3da7c66da43954038e6505dc5b1cb24c8b5d95cf7634c" + "44205ea3a71ee1cbd02eef7b084a409450c21d11a3b41769f02bb3e2dd89d5e2", + "8ecf785b86dd1715d4c193f280a118b82200742f102bf1e59a4a65194a126f03", + "a452e23aab1e4baae2e3da7c66da43954038e6505dc5b1cb24c8b5d95cf7634c", ], [ - "42f584ee07afb6754770ea07fc7f498cb7200ba89eb67361a7f2564612040cd3", - "09e0ed078a0113619c033eec41b65e3168394dc377998bc13481b5f1942f7119", - "2096622ca7f5aeda8d4c9a9cd4523e1bb9ea09e661f092f515c0c2cbaadcc2c6" + "42f584ee07afb6754770ea07fc7f498cb7200ba89eb67361a7f2564612040cd3", + "09e0ed078a0113619c033eec41b65e3168394dc377998bc13481b5f1942f7119", + "2096622ca7f5aeda8d4c9a9cd4523e1bb9ea09e661f092f515c0c2cbaadcc2c6", ], [ - "8560e7d4c6e014b01b70bf5e1e2ffaa1e4115c9d21eb685b796b172872b71150", - "3d38f5e8fc6c4612f27932b009bea0fd41a99c30af7a14a1e5316d9bbd5a4df6", - "eab6fce22d0679c304d7419cf0746552921b31245d715171a5ec7c9caf81f084" + "8560e7d4c6e014b01b70bf5e1e2ffaa1e4115c9d21eb685b796b172872b71150", + "3d38f5e8fc6c4612f27932b009bea0fd41a99c30af7a14a1e5316d9bbd5a4df6", + "eab6fce22d0679c304d7419cf0746552921b31245d715171a5ec7c9caf81f084", ], [ - "7c909a4734e36fd67e11cd97a9a4222795672690f3eb081a2dd43a413ba6490c", - "39a08a837c5bfef00ebb6e3b72f7fc5a8275f13fb5d5a86f03541ebf5ee8edec", - "f537f1e2ac17a2af3524b7e3fc81ca88adcee65906236dab22250e071924e527" + "7c909a4734e36fd67e11cd97a9a4222795672690f3eb081a2dd43a413ba6490c", + "39a08a837c5bfef00ebb6e3b72f7fc5a8275f13fb5d5a86f03541ebf5ee8edec", + "f537f1e2ac17a2af3524b7e3fc81ca88adcee65906236dab22250e071924e527", ], [ - "130151db7ac8036300c80c58a37de8119719ce60600b6e009d09df3a71d5f741", - "a6bf923dbbcaae29701d82e0a1492ffe388aa14bd3e6ffbfa834aa9b23ad154a", - "e70822e27d35acff57fc210d451aba171285025ac2fa77911e893427a8430b25" + "130151db7ac8036300c80c58a37de8119719ce60600b6e009d09df3a71d5f741", + "a6bf923dbbcaae29701d82e0a1492ffe388aa14bd3e6ffbfa834aa9b23ad154a", + "e70822e27d35acff57fc210d451aba171285025ac2fa77911e893427a8430b25", ], [ - "9992ff1b7ff438d5132b2b5ddd875c10ca62bcb46f681ef228548abdcd6db5c1", - "4080eca86a5ea164518fc7426dc793ce5c9f95831bc8a97b2f06bc53722c78bb", - "1bdbe0e67971989362b44c66f7ff26eea7d6c7f5f791d91e96bfa46a6934b97b" + "9992ff1b7ff438d5132b2b5ddd875c10ca62bcb46f681ef228548abdcd6db5c1", + "4080eca86a5ea164518fc7426dc793ce5c9f95831bc8a97b2f06bc53722c78bb", + "1bdbe0e67971989362b44c66f7ff26eea7d6c7f5f791d91e96bfa46a6934b97b", ], [ - "2a6f3577676eb6493d62268cf402f39f432490f8ca64d2323eab7ffb8fa5e239", - "a004b81f69f9b6694fad09f0193e9120789d4e870681f436a97a2eef9089a3e2", - "3dd8900540834a3fe28407796f128a21dd4c947b6b991ed14d6167ae4fc29cc3" + "2a6f3577676eb6493d62268cf402f39f432490f8ca64d2323eab7ffb8fa5e239", + "a004b81f69f9b6694fad09f0193e9120789d4e870681f436a97a2eef9089a3e2", + "3dd8900540834a3fe28407796f128a21dd4c947b6b991ed14d6167ae4fc29cc3", ], [ - "7b4e5361bddc8029f76c3fead78e0a0a49e02dd40666cdff03ea40609de3c8d9", - "bf7b76a80a3a70151640263f13bb62f72d66f0075f03b64e51aaec781b36d8c9", - "809cf278ede0e210b29e7ce57b12a058d5d1f78be62a16df0c301995be7e7a5d" + "7b4e5361bddc8029f76c3fead78e0a0a49e02dd40666cdff03ea40609de3c8d9", + "bf7b76a80a3a70151640263f13bb62f72d66f0075f03b64e51aaec781b36d8c9", + "809cf278ede0e210b29e7ce57b12a058d5d1f78be62a16df0c301995be7e7a5d", ], [ - "a52ae0c843df054f6a9489a743f293a74b7fe21f14bff5d35e9c9ec4fe336522", - "e3e6379804432520b7eba2a7b46d0b016a4025f32da7cb8aa0003aaf57dab15c", - "f56647e8f500efaafe8aaaf9a90b142685896cba145a06a6bc9853d9765079b8" + "a52ae0c843df054f6a9489a743f293a74b7fe21f14bff5d35e9c9ec4fe336522", + "e3e6379804432520b7eba2a7b46d0b016a4025f32da7cb8aa0003aaf57dab15c", + "f56647e8f500efaafe8aaaf9a90b142685896cba145a06a6bc9853d9765079b8", ], [ - "386d9d8e6851f030ac2f510b6a8ebcc2f00e16a9cc7b7707d7d65f8a95ae82f3", - "bb2b56422cd46210f5ab0c53527e8bf7ef71ad723a77a2cba0d990da15c9bde8", - "d4d029cc7fbc6eba897d5659bb4d0298f9d3609c383526de67ab15b26fa95ad2" + "386d9d8e6851f030ac2f510b6a8ebcc2f00e16a9cc7b7707d7d65f8a95ae82f3", + "bb2b56422cd46210f5ab0c53527e8bf7ef71ad723a77a2cba0d990da15c9bde8", + "d4d029cc7fbc6eba897d5659bb4d0298f9d3609c383526de67ab15b26fa95ad2", ], [ - "6e326b458d8bbef8b5a592e939d8bfa2dffb769a5f616034fb0cbf1267d4a600", - "d5b60f7116771c9033a32bd2ccd22912d97bd3cf30d526fdcaff9f1bc9453397", - "6c915b5095aca9df36491281c04a4f127b9fd81b4362742f07314d945b44582a" + "6e326b458d8bbef8b5a592e939d8bfa2dffb769a5f616034fb0cbf1267d4a600", + "d5b60f7116771c9033a32bd2ccd22912d97bd3cf30d526fdcaff9f1bc9453397", + "6c915b5095aca9df36491281c04a4f127b9fd81b4362742f07314d945b44582a", ], [ - "4af3eaf1108b48e0df66988876570f2044db09a0cad061da7d2448871fc52cb6", - "cf5c4c57391fa60fbd613b2bdd5ddb5da9435239d073f2cdd265d0788e0b9cec", - "54a342f852b7d41a5aab4a6a73cfc9adbc3b5fc42303627dbd604eede98e334f" - ] - ]; + "4af3eaf1108b48e0df66988876570f2044db09a0cad061da7d2448871fc52cb6", + "cf5c4c57391fa60fbd613b2bdd5ddb5da9435239d073f2cdd265d0788e0b9cec", + "54a342f852b7d41a5aab4a6a73cfc9adbc3b5fc42303627dbd604eede98e334f", + ], + ]; // build circuit once const D: usize = 2; @@ -493,33 +473,121 @@ mod tests { let expected_output_target = builder.add_virtual_hash256_target(); let output_target = builder.two_to_one_sha256(left_target, right_target); builder.connect_hash256(output_target, expected_output_target); - + let num_gates = builder.num_gates(); // let copy_constraints = builder.copy_constraints.len(); let copy_constraints = ""; let data = builder.build::(); println!( - "sha256_two_to_one num_gates={}, copy_constraints={}, quotient_degree_factor={}", + "two_to_one_sha256 num_gates={}, copy_constraints={}, quotient_degree_factor={}", num_gates, copy_constraints, data.common.quotient_degree_factor ); - - for t in tests { let left = Hash256::from_str(t[0]).unwrap(); let right = Hash256::from_str(t[1]).unwrap(); let expected_output = Hash256::from_str(t[2]).unwrap(); - - // test circuit let mut pw = PartialWitness::new(); pw.set_hash256_target(&left_target, &left.0); pw.set_hash256_target(&right_target, &right.0); pw.set_hash256_target(&expected_output_target, &expected_output.0); - + + let start = Instant::now(); let proof = data.prove(pw).unwrap(); - // println!("sha256 proof.public_inputs =\n{:08x?}", proof.public_inputs); + let end = start.elapsed(); + println!("two_to_one_sha256 proved in {}ms", end.as_millis()); + assert!(data.verify(proof).is_ok()); + } + } + + #[test] + fn test_sha256_long_arbitrary_length() { + let tests = [ + [ + "600D54000000000000000000000000000000000000000000000000000000000077F1040000000000000000000000000000000000000000000000000000000000", + "9E05820FB000642E0F36AD7696F92D95C965CB27A8DC093D81A0D37B260A0F8E", + ], + ]; + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + let input = hex::decode(tests[0][0]).unwrap(); + let output = hex::decode(tests[0][1]).unwrap(); + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(config); + let preimage_target = builder.add_virtual_biguint_target(input.len() / 4); + let expected_output_target = builder.add_virtual_hash256_target(); + + let hash_output = builder.hash_sha256_u32(&preimage_target.limbs); + builder.connect_hash256(hash_output, expected_output_target); + + let num_gates = builder.num_gates(); + let data = builder.build::(); + println!( + "sha256 ({} bytes) num_gates={}, quotient_degree_factor={}", + input.len(), + num_gates, + data.common.quotient_degree_factor + ); + let mut pw = PartialWitness::new(); + pw.set_biguint_u32_be_target(&preimage_target, &BigUint::from_bytes_le(&input)); + pw.set_hash256_target(&expected_output_target, &output); + + let start_time = std::time::Instant::now(); + let proof = data.prove(pw).unwrap(); + let duration_ms = start_time.elapsed().as_millis(); + println!("sha256 ({} bytes) proved in {}ms", input.len(), duration_ms); + + assert!(data.verify(proof).is_ok()); + } + + #[test] + fn test_sha256_arbitrary_length() { + let tests = [ + [ + "600D54000000000000000000000000000000000000000000000000000000000077F1040000000000000000000000000000000000000000000000000000000000", + "9E05820FB000642E0F36AD7696F92D95C965CB27A8DC093D81A0D37B260A0F8E", + ], + [ + "3718CEB4122437AE80D343DFB857F4D016CA3072A05797AC24AE59918EA63C68CF97BD867F78455176EEE0709A9A59EF768E0C6D8A22BCD57ADBB3FB74A0A331F66D7E55CA3786E7C2AB91951F9A1C617CA32B34D395C745E8C15A90766735116E20A45ACA7E4BD37B7F46660E345415C758712EB9493B98C62CAD9B325B1927F7248B773E18D4E4B1D40675B3EFE7528914AD4BEDDB3BADBE05568AE539A6A308D4D2C453C726B34E84E5A6DDC5EED70026BDF5828B7A556342EFC1D8187A4BC7228D0654CB57BB", + "E1B79FB8A21D1C1438C85BBC81250C112C3126E1935E1C8EF7B8880046B7604B", + ], + ]; + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + + for t in tests { + // build circuit for each test + let input = hex::decode(t[0]).unwrap(); + let output = hex::decode(t[1]).unwrap(); + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(config); + let preimage_target = builder.add_virtual_biguint_target(input.len() / 4); + let expected_output_target = builder.add_virtual_hash256_target(); + + let hash_output = builder.hash_sha256_u32(&preimage_target.limbs); + builder.connect_hash256(hash_output, expected_output_target); + + let num_gates = builder.num_gates(); + let data = builder.build::(); + println!( + "sha256 ({} bytes) num_gates={}, quotient_degree_factor={}", + input.len(), + num_gates, + data.common.quotient_degree_factor + ); + let mut pw = PartialWitness::new(); + pw.set_biguint_u32_be_target(&preimage_target, &BigUint::from_bytes_le(&input)); + pw.set_hash256_target(&expected_output_target, &output); + + let start_time = std::time::Instant::now(); + let proof = data.prove(pw).unwrap(); + let duration_ms = start_time.elapsed().as_millis(); + println!("sha256 ({} bytes) proved in {}ms", input.len(), duration_ms); + assert!(data.verify(proof).is_ok()); } } @@ -566,7 +634,10 @@ mod tests { pw.set_sha256_input_target(&hash_target, &input); pw.set_sha256_output_target(&hash_output, &output); + let start_time = std::time::Instant::now(); let proof = data.prove(pw).unwrap(); + let duration_ms = start_time.elapsed().as_millis(); + println!("sha256 ({} bytes) proved in {}ms", input.len(), duration_ms); // println!("sha256 proof.public_inputs =\n{:08x?}", proof.public_inputs); assert!(data.verify(proof).is_ok()); } diff --git a/src/hash/sha256_merkle.rs b/src/hash/sha256_merkle.rs index 92d24a9..a730a4c 100644 --- a/src/hash/sha256_merkle.rs +++ b/src/hash/sha256_merkle.rs @@ -216,10 +216,10 @@ mod tests { pw.set_hash256_target(&expected_root_target, &proof.root.0); let start_time = std::time::Instant::now(); - let proof = data.prove(pw).unwrap(); let duration_ms = start_time.elapsed().as_millis(); println!("proved in {}ms", duration_ms); + assert!(data.verify(proof).is_ok()); } diff --git a/src/hash/sha256_truncated.rs b/src/hash/sha256_truncated.rs new file mode 100644 index 0000000..d40dfc3 --- /dev/null +++ b/src/hash/sha256_truncated.rs @@ -0,0 +1,249 @@ +use plonky2::field::extension::Extendable; +use plonky2::hash::hash_types::{RichField, HashOutTarget}; +use plonky2::plonk::circuit_builder::CircuitBuilder; + +use crate::hash::CircuitBuilderHash; +use crate::u32::arithmetic_u32::{CircuitBuilderU32, U32Target}; + +use super::sha256::{ + big_sigma, ch, maj, sha256_round_constants, sha256_start_state, sigma, CircuitBuilderHashSha2, +}; +use super::{Hash192Target}; + +pub trait CircuitBuilderTruncatedSha2, const D: usize> { + fn truncated_sha256(&mut self, data: &[U32Target]) -> Hash192Target; + fn two_to_one_truncated_sha256( + &mut self, + left: Hash192Target, + right: Hash192Target, + ) -> Hash192Target; + fn truncated_sha256_hash_out(&mut self, data: &[U32Target]) -> HashOutTarget; + fn two_to_one_truncated_sha256_hash_out( + &mut self, + left: HashOutTarget, + right: HashOutTarget, + ) -> HashOutTarget; +} + +impl, const D: usize> CircuitBuilderTruncatedSha2 + for CircuitBuilder +{ + fn truncated_sha256(&mut self, data: &[U32Target]) -> Hash192Target { + let result = self.hash_sha256_u32(data); + [ + result[0], + result[1], + result[2], + result[3], + result[4], + result[5], + ] + } + + fn two_to_one_truncated_sha256( + &mut self, + left: Hash192Target, + right: Hash192Target, + ) -> Hash192Target { + let state = sha256_start_state(self); + let k256 = sha256_round_constants(self); + + let mut a = state[0]; + let mut b = state[1]; + let mut c = state[2]; + let mut d = state[3]; + let mut e = state[4]; + let mut f = state[5]; + let mut g = state[6]; + let mut h = state[7]; + + let zero = self.constant_u32(0); + let cx80 = self.constant_u32(0x80000000); + let c384 = self.constant_u32(384); // 192+192 + // Process the 384-bit message + let mut w: [U32Target; 16] = [ + left[0], left[1], left[2], left[3], left[4], left[5], right[0], right[1], right[2], + right[3], right[4], right[5], cx80, zero, zero, c384, + ]; + + for i in 0..64 { + // Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array + if i >= 16 { + let s0 = sigma(self, w[(i + 1) & 0xf], 7, 18, 3); + let s1 = sigma(self, w[(i + 14) & 0xf], 17, 19, 10); + w[i & 0xf] = self.add_many_u32(&[s0, s1, w[(i + 9) & 0xf], w[i & 0xf]]).0; + } + + // Compression function main loop + let big_s1_e = big_sigma(self, e, 6, 11, 25); + let ch_efg = ch(self, e, f, g); + let temp1 = self + .add_many_u32(&[h, big_s1_e, ch_efg, k256[i], w[i & 0xf]]) + .0; + + let big_s0_a = big_sigma(self, a, 2, 13, 22); + let maj_abc = maj(self, a, b, c); + let temp2 = self.add_u32_lo(big_s0_a, maj_abc); + + h = g; + g = f; + f = e; + e = self.add_u32_lo(d, temp1); + d = c; + c = b; + b = a; + a = self.add_u32_lo(temp1, temp2); // add_many_u32 of 3 elements is the same + } + + // Add the compressed chunk to the current hash value + [ + self.add_u32_lo(state[0], a), + self.add_u32_lo(state[1], b), + self.add_u32_lo(state[2], c), + self.add_u32_lo(state[3], d), + self.add_u32_lo(state[4], e), + self.add_u32_lo(state[5], f), + ] + } + + fn truncated_sha256_hash_out(&mut self, data: &[U32Target]) -> HashOutTarget { + let result = self.truncated_sha256(data); + self.hash192_to_hash_out(result) + } + + fn two_to_one_truncated_sha256_hash_out( + &mut self, + left: HashOutTarget, + right: HashOutTarget, + ) -> HashOutTarget { + let left_192 = self.hash_out_to_hash192(left); + let right_192 = self.hash_out_to_hash192(right); + + + let result = self.two_to_one_truncated_sha256(left_192, right_192); + self.hash192_to_hash_out(result) + } +} + + + +#[cfg(test)] +mod tests { + use std::time::Instant; + + use plonky2::iop::witness::PartialWitness; + use plonky2::plonk::circuit_builder::CircuitBuilder; + use plonky2::plonk::circuit_data::CircuitConfig; + use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + + use crate::hash::merkle_utils::{Hash192}; + use crate::hash::sha256_truncated::CircuitBuilderTruncatedSha2; + use crate::hash::{CircuitBuilderHash, WitnessHash}; + + + #[test] + fn test_truncated_sha256_two_to_one() { + let tests = [ + [ + "AFCD4BF774D5F2D3FEED035DB36A740A446864124673227D", + "BA14D0B2ABF1D369F5BE1730997F9E77540A0C435133064F", + "461075FD679C0863B99A0E3498EA3CACD0A007DEA9DBF572" + ], + [ + "43FB6415E33BBE716607A4B9A78B6625E5A776D8A37B185A", + "F0214D64F35C43C30D92E812DECC4658A4EDAD0850228E99", + "D660BADE6D0BC67250ED87D0EEEA427D5B6B9E7B4CAAC6EE" + ], + [ + "04340EC34E8C8E38152B45CD9BBDC305FE595E198722E7BF", + "A3895951755CE2125584A94B588E66F3C1F64D8B9D50F6BD", + "08844DE6B4C4D009A12E79A196292F1C6E52AF610D9D3044" + ], + [ + "58464F7B9347A7573FD8F7FA0155A9E3556EC47C1A7A5F19", + "B54136795B6E3E479B3A4526ED8CC15B9729AD3EABA1CBAE", + "002D28DAC7C0194F4BC15BE3DF0D3CBC885523F3FAAED111" + ], + [ + "9506324C7EFCC3863BA92D5871AA182D2B1E63D93EE960AD", + "279C6B519973869ADD01CF6D0249BE105C27AF6297EE5C32", + "8349F52C81D5C7FA81AE1880158E65C8BB1514BC23A03EB6" + ], + [ + "91CE20D131F9ACBDE60CB946AA1A54901C9E71E93EC4B2AD", + "027E9C142D0E0225F137E13955EF406BE18B6245DE0DE80A", + "59E5A7FED5282B26FB7E4751B22B8CEF568B4C4666435D37" + ], + [ + "88BE9D9F512FD105DBABD7C25849BABAE1E5882A7FC1DADA", + "D6F6F3BAE52B7D4A6C8AADDB01FAF0D0911EAF0DFA7121C9", + "96D7429D88AC980A57459EC9FBD92BB9DD253C178E0C2416" + ], + [ + "C6E60652FAFFE4B4D62B84B4F904150397B39E5B99944B29", + "A0C6245E473D897A6C7C46DBACDCDEF4ECACF28694862D65", + "7B54D7CC33C2354268B263DBAF74E89D68480FF102315002" + ], + [ + "877908AD071DD2C83C7E31B1F2926B76DBA622F0C3FF5511", + "938944FC9145B545465E3ED63DE4227D2A57712972C7533B", + "E98BA96CCBD96AFC0593A17A0225E42C244C4A7B9661F532" + ], + [ + "B0DC39A30D1F5992DFD0B022DA2FED3F047466832ECA1FBE", + "3F90165ED38AF49C8DC66DE4A79B8620A7AA8893E9B38630", + "B6D9032CBA848F5416DA5F6395E02F477AE58F608F360026" + ], + [ + "C63897AFB82F3A2D138EFA68C1C52D796C7676017CD2601E", + "7AA288423DE03FF0E403D696454E29895757A7F5BFF37B78", + "63F674231847641F9E323932AD0AF2C3D1EE356E9500CDCD" + ], + [ + "7A1606CEA3FC417C4014C6FDB5C37C469285E3E9E33FF6DC", + "E924482BCCAA627C2C78D9767794E23F7FA41ECFF69C3E24", + "4D41A244BD66D71D3998A0DD1345D3D3C679A0EAB340C8C7" + ] + ]; + + // build circuit once + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(config); + let left_target = builder.add_virtual_hash192_target(); + let right_target = builder.add_virtual_hash192_target(); + let expected_output_target = builder.add_virtual_hash192_target(); + let output_target = builder.two_to_one_truncated_sha256(left_target, right_target); + builder.connect_hash192(output_target, expected_output_target); + + let num_gates = builder.num_gates(); + // let copy_constraints = builder.copy_constraints.len(); + let copy_constraints = ""; + let data = builder.build::(); + println!( + "two_to_one_truncated_sha256 num_gates={}, copy_constraints={}, quotient_degree_factor={}", + num_gates, copy_constraints, data.common.quotient_degree_factor + ); + + for t in tests { + let left = Hash192::from_str(t[0]).unwrap(); + let right = Hash192::from_str(t[1]).unwrap(); + let expected_output = Hash192::from_str(t[2]).unwrap(); + + // test circuit + let mut pw = PartialWitness::new(); + pw.set_hash192_target(&left_target, &left.0); + pw.set_hash192_target(&right_target, &right.0); + pw.set_hash192_target(&expected_output_target, &expected_output.0); + let start = Instant::now(); + let proof = data.prove(pw).unwrap(); + let end = start.elapsed(); + println!("two_to_one_truncated_sha256 proved in {}ms", end.as_millis()); + assert!(data.verify(proof).is_ok()); + } + } + +} diff --git a/src/hash/sha256_truncated_merkle.rs b/src/hash/sha256_truncated_merkle.rs new file mode 100644 index 0000000..4ff09f8 --- /dev/null +++ b/src/hash/sha256_truncated_merkle.rs @@ -0,0 +1,563 @@ +use plonky2::field::extension::Extendable; +use plonky2::hash::hash_types::RichField; +use plonky2::iop::target::{BoolTarget, Target}; +use plonky2::plonk::circuit_builder::CircuitBuilder; + + +use super::sha256::{WitnessHashSha2}; +use super::sha256_truncated::CircuitBuilderTruncatedSha2; +use super::{CircuitBuilderHash, Hash192Target, WitnessHash}; + +pub fn compute_merkle_root, const D: usize>( + builder: &mut CircuitBuilder, + index_bits: &Vec, + value: Hash192Target, + siblings: &Vec, +) -> Hash192Target { + let mut current = value; + for (i, sibling) in siblings.iter().enumerate() { + let bit = index_bits[i]; + + let left = builder.select_hash192( bit, *sibling, current); + let right = builder.select_hash192(bit, current, *sibling); + current = builder.two_to_one_truncated_sha256(left, right); + } + current +} + +pub struct MerkleProofTruncatedSha256Gadget { + pub root: Hash192Target, + pub value: Hash192Target, + pub siblings: Vec, + pub index: Target, +} + +impl MerkleProofTruncatedSha256Gadget { + pub fn add_virtual_to, const D: usize>( + builder: &mut CircuitBuilder, + height: usize, + ) -> Self { + let siblings: Vec = (0..height) + .map(|_| builder.add_virtual_hash192_target()) + .collect(); + + let value = builder.add_virtual_hash192_target(); + let index = builder.add_virtual_target(); + let index_bits = builder.split_le(index, height); + let root = compute_merkle_root(builder, &index_bits, value, &siblings); + + Self { + root, + value, + siblings, + index, + } + } + + pub fn set_witness>( + &self, + witness: &mut W, + index: u64, + value: &[u8; 24], + siblings: &Vec<[u8; 24]>, + ) { + witness.set_hash192_target(&self.value, value); + witness.set_target(self.index, F::from_noncanonical_u64(index)); + for (i, sibling) in self.siblings.iter().enumerate() { + witness.set_hash192_target(sibling, &siblings[i]); + } + } +} + +pub struct DeltaMerkleProofTruncatedSha256Gadget { + pub old_root: Hash192Target, + pub old_value: Hash192Target, + + pub new_root: Hash192Target, + pub new_value: Hash192Target, + + pub siblings: Vec, + pub index: Target, +} + +impl DeltaMerkleProofTruncatedSha256Gadget { + pub fn add_virtual_to, const D: usize>( + builder: &mut CircuitBuilder, + height: usize, + ) -> Self { + let siblings: Vec = (0..height) + .map(|_| builder.add_virtual_hash192_target()) + .collect(); + + let old_value = builder.add_virtual_hash192_target(); + let new_value = builder.add_virtual_hash192_target(); + let index = builder.add_virtual_target(); + let index_bits = builder.split_le(index, height); + let old_root = compute_merkle_root(builder, &index_bits, old_value, &siblings); + let new_root = compute_merkle_root(builder, &index_bits, new_value, &siblings); + + Self { + old_root, + old_value, + new_root, + new_value, + siblings, + index, + } + } + + pub fn set_witness>( + &self, + witness: &mut W, + index: u64, + old_value: &[u8; 24], + new_value: &[u8; 24], + siblings: &Vec<[u8; 24]>, + ) { + witness.set_hash192_target(&self.old_value, old_value); + witness.set_hash192_target(&self.new_value, new_value); + witness.set_target(self.index, F::from_noncanonical_u64(index)); + for (i, sibling) in self.siblings.iter().enumerate() { + witness.set_hash192_target(sibling, &siblings[i]); + } + } +} +pub fn compute_merkle_root_truncated_sha256, const D: usize>(builder: &mut CircuitBuilder, leaves: &[Hash192Target]) -> Hash192Target{ + if (leaves.len() as f64).log2().ceil() != (leaves.len() as f64).log2().floor(){ + panic!("The length of the merkle tree's leaves array must be a power of 2 (2^n)"); + } + let num_levels = (leaves.len() as f64).log2().ceil() as usize; + let mut current = leaves.to_vec(); + for _ in 0..num_levels{ + let tmp = current.chunks_exact(2).map(|f| builder.two_to_one_truncated_sha256(f[0], f[1])).collect(); + current = tmp; + + } + current[0] +} +#[cfg(test)] +mod tests { + + use crate::hash::sha256_truncated_merkle::{DeltaMerkleProofTruncatedSha256Gadget, MerkleProofTruncatedSha256Gadget}; + use crate::hash::{CircuitBuilderHash, WitnessHash}; + use crate::hash::merkle_utils::{MerkleProof192, DeltaMerkleProof192}; + use plonky2::iop::witness::PartialWitness; + use plonky2::plonk::circuit_builder::CircuitBuilder; + use plonky2::plonk::circuit_data::CircuitConfig; + use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; + const SMALL_MERKLE_PROOFS: &str = r#" + [ + { + "root": "8ce47831fa9e8fc6dc2631cc676d8392313f7228bf2628d0", + "siblings": [ + "93a7b526b4539b1519c03dc81b1f92ed31acbdadb8c930be", + "81201c69c29f02d7015cc2564db231c5ac5d07c24bbd06a4", + "e4c0e62041f91b6c90bd2832182b3a52b1c334f398f0246c", + "f1dde9ea14d2ee93786d7d5e8f04d5ac19143e6d3cf21ada", + "da0d5558f7797e3a37b1ee3c6fe1da0fac6589ef0ec85121" + ], + "index": 15, + "value": "6c652a08d06186cdf550f67eda642202cbe4c747716ed550" + }, + { + "root": "5313e65c747b7c7aa81b5fefedf42b10c9333327cb07af34", + "siblings": [ + "000000000000000000000000000000000000000000000000", + "fe46d376e67c2254694c347d0afe236b75336490394afb0d", + "e99a7762a88398781a5033622920f7ab6718257eb19ccca9", + "b7df18f989925b15e03bec732cf46af8e0c2a9c6050e9dfb", + "5befbf2e8a1a141cf6133d8e8129f32e78ac61062f9a239f" + ], + "index": 29, + "value": "000000000000000000000000000000000000000000000000" + }, + { + "root": "ced84c2700c11e72495a35caa606019609794dda50caaddd", + "siblings": [ + "77ee6c1b0b7e95bbca3ff6a34445b079668467912bb47500", + "9bfd545a14fc6572c898eb9200a84b2e454b972b3905cbc0", + "1e09e6cb66cec2c8a92a4fc345376be6d6b4c4c0570ed781" + ], + "index": 4, + "value": "43c8b70308380de1224d64d7196760ab2a06f47085ff13df" + }, + { + "root": "ced84c2700c11e72495a35caa606019609794dda50caaddd", + "siblings": [ + "521a640c1caafc21a4bd6b48ced3d60c3aeacf2ab5ea09ba", + "14a1c68279ad393d4bd7625b065bf17c10fe13ca0f27f492", + "e854c3850894f7a5a7cc74cffaa7d8d646972f5b4d6ee8b9" + ], + "index": 1, + "value": "f071d559a4d8f6014f9ad03f7b0121a01518341694529897" + }, + { + "root": "79666b3e60a7f629ab7622a0f5df5fd84005a288134d959b", + "siblings": [ + "000000000000000000000000000000000000000000000000", + "c2a103ad1e2e923fc1993318a63ea714eda3630d4680dd78", + "68a957640671cd2073f5b043482bf9e35d02339bcd5c03e9", + "a88c2f54b0bc3e9d50f58c1d4811cf266af5b0cface893bc" + ], + "index": 6, + "value": "8fb2d850018d9f09aabe74babdd26395c52ba6c36bcc27ed" + }, + { + "root": "79666b3e60a7f629ab7622a0f5df5fd84005a288134d959b", + "siblings": [ + "be4b9875a18800889c3e66552b6397a6b25db7652563f299", + "f94127607bd87b313aebb372b36cba913d95fca15e6d74a1", + "1da273ba38b033fa58b4754e8d689f0c2b6a12026cc511fd", + "a88c2f54b0bc3e9d50f58c1d4811cf266af5b0cface893bc" + ], + "index": 0, + "value": "e12a01166536ec83377a1ce0918a6c5a0b89ff823f4062cf" + }, + { + "root": "dc7429c8e343c23fbaabc16b83739454632ed633311a7aef", + "siblings": [ + "42c3ae0c727ee637ee564133dfbbedbcf2555f5f1534b4cd", + "4ee11667fe4b4b5947959535fa03e1489ee13c09fd2794e9", + "9ea75858f5c3b2538398d172aecec24232c52fcd63ae9fff" + ], + "index": 6, + "value": "6bbe9138924947c94509a6d0f7390e98dca63a38d326bfdf" + }, + { + "root": "dc7429c8e343c23fbaabc16b83739454632ed633311a7aef", + "siblings": [ + "3027eac7dc2a14ef779a04e66203ce16ae3bf95472b766a8", + "813eb55b59a858f2b28d926102a30fa6aa0468091e6d9988", + "c1f1a1d1638e9761f1677268b53e38bb44c036ae116a9bed" + ], + "index": 1, + "value": "a9611ac7ab506b57cee7c38b3b83715eaa08aa0d77bcf61f" + }, + { + "root": "a8c4553513d62785d0765b29d53cfa46fea878378f95c7e9", + "siblings": [ + "4c264204a6eb3a88d3e3cbfa06f0a4b486f01055cb82c10e", + "321d0799406db01521c341d23bd7314968b5cba8b3d46eb8" + ], + "index": 3, + "value": "0ec33d7d3c23c18a4144b88201f4cde556cae5dfb1916891" + }, + { + "root": "a8c4553513d62785d0765b29d53cfa46fea878378f95c7e9", + "siblings": [ + "30a6ef35e30168d832e97cd4fed35220a2f3009a21a430a9", + "c5b3c3bb728f1c6c4021ba924602a843ea6b7df205793664" + ], + "index": 1, + "value": "bdeaea5c0c08869c53a36a8e7644f7ebebf6e32be2e5cfcb" + }, + { + "root": "28bbb2970f9a64051670779baf00d3db0ccb613a484a4fc5", + "siblings": [ + "c296afe32a478134777b6b0588a2c9b1f86a3a0bfa55c625", + "6b4c3568c45cc4d3026388c71be5411466b491148788fd23", + "f783c89a5c67bdc98466dae1ec56b0efba582892780e20d0" + ], + "index": 3, + "value": "cc10f18b06dd6365366690550c48c947fd8144c11cc976be" + }, + { + "root": "28bbb2970f9a64051670779baf00d3db0ccb613a484a4fc5", + "siblings": [ + "f76a0a71f724c37b8eed0be4280b9c2b668b53dc862a5ceb", + "7815cc410f24f77e9384d63e8efc187e86bb4d8187b70050", + "409e63f8d1c7c2ca2da8a7d2c56ece3aa40eb0cd420b261f" + ], + "index": 5, + "value": "e1a9ddd171e191ed2e9e34bbb3a1b2e665e65b750bdfa6b7" + }, + { + "root": "afce7d182834e48a88d45db6c3833ad51ceb60e5e5caa5e2", + "siblings": [ + "f2f55ffbf696831576323d0bfac6710073e8ce7694eae99f", + "d52c0c15507beee68123f8dd38a28eccc1f56de03388cfa0", + "4e0fac824e2dfbbca047d85c6f05c523e3db2e31ac6d75d6" + ], + "index": 4, + "value": "775aca6a322df3887dc19fe7da4b262b9dc02c3650630bf3" + }, + { + "root": "afce7d182834e48a88d45db6c3833ad51ceb60e5e5caa5e2", + "siblings": [ + "e6aeed73c5843735fb3ac567abd4dbcfb894f7c5659b5e86", + "ee53fc721f076d76ab64c995aa23d4a54382d82f27954b15", + "943e3435a20752cc53ce03215c535e8b9c21016a7016df71" + ], + "index": 0, + "value": "1de9f130254ec6a8ee3a8df8a539a205b3509ab0e3bff6cf" + }, + { + "root": "be224af255dd6bf53d56738040a8207bf8bb98027b59ae61", + "siblings": [ + "b9f754b505eff11d424c1e6983a0db68b0c54b0bb6ae1059", + "cc9181ae4127c5c8ee4b3d1adae560e09d3293acccd9b7e5", + "4819814478941d48d8921a7b289b79c2709622517f826f56", + "66341692f46741c611a709596135b09f4842ecca593f08a3" + ], + "index": 14, + "value": "380f2b167ce130efcd797b77fb3eba3e18539a7bf3fe8772" + }, + { + "root": "be224af255dd6bf53d56738040a8207bf8bb98027b59ae61", + "siblings": [ + "64fb394242203a7771adfeed453965654d3397f6c797ea13", + "4ec9ff57795e4ccb7507c7f0e98fd18a9b47ad911df71c11", + "e9cb96ba853ed70ffe9c3a0ddbf4ccc43770a278a01938db", + "489c8649984fe624e6eee7588b8bde493c05e486e73023da" + ], + "index": 1, + "value": "8bd918726e6167d1db01ba26a54cfc2b2d50c126381955ad" + } + ] + "#; + + const SMALL_DELTA_MERKLE_PROOFS: &str = r#" + [ + { + "index": 0, + "siblings": [ + "000000000000000000000000000000000000000000000000", + "df99695b9d441940e4e1adf727dc1c48d06f54afc9b2ffa6" + ], + "old_root": "8bb06fd2062223553c51e18f916a7ea2b3e519f400c4ddf6", + "old_value": "000000000000000000000000000000000000000000000000", + "new_value": "4b4b4ee152393271516423e92114bee4079859d0738e2e68", + "new_root": "0e3aa6291d830f230dbe50907d496f4688527c6ad7fef833" + }, + { + "index": 2, + "siblings": [ + "29337a6b91d34765a42d322b50c5fc673f9a8155dada38d5", + "946660c9cd5c7116aa281248fdbf20c510ac65a85cea7e89" + ], + "old_root": "c35efd846a566c448964eaddbb0a0021abf18bc89476f631", + "old_value": "467ee0cb54eb282384b15acd57563d70839f6a9353655657", + "new_value": "9bdad0b5266866eb4ff6a4a5c89dbed940ccacf10db82d1f", + "new_root": "f4c133e368fea599142cbf639a67d5870c06eefd671fb284" + }, + { + "index": 16, + "siblings": [ + "a636b48f4ba58651e9f5ffa75917a28d6a92805d7707246c", + "17b0761f87b081d5cf10757ccc89f12be355c70e2e29df28", + "5b253a40fe440e2c1240afc1bc1ebdb355fce135f0d99097", + "5a5c27f6767ebefeeb36acf5cb5bc978a52a9256e71e44b2", + "34be2b390cd59779ee74f2942d2ba3f12176d07bb746b2af" + ], + "old_root": "adf4d0fd2a199895ae9193fdaab7d36ec080e75e420b160f", + "old_value": "644108dd19e5a1f405c56fd6df1c6ff1c610b7d42561af2f", + "new_value": "98cbbdaaa136ff0c42c34c07fcd876355df87052b6da63b9", + "new_root": "de664d9bf989519d291363272520bb5d216fd20688e2b1b0" + }, + { + "index": 0, + "siblings": [ + "000000000000000000000000000000000000000000000000", + "17b0761f87b081d5cf10757ccc89f12be355c70e2e29df28", + "5b253a40fe440e2c1240afc1bc1ebdb355fce135f0d99097", + "f88fc9dc701329cbc8b855d67d4ad10e02190fdfeb78e6bb", + "c277b62cd67e797e7b5c7142c392ca47a3730b399df2a0f4" + ], + "old_root": "58b53b7ec1ae789137bff540511a52b5a666c6bd9efb4b53", + "old_value": "000000000000000000000000000000000000000000000000", + "new_value": "045ace8b8f1a87385c3907241b5741e4b443ccb75e38b63f", + "new_root": "c442c624e211aeb65a0e20750c9d7255c99e1fdff2b1dca8" + }, + { + "index": 6, + "siblings": [ + "000000000000000000000000000000000000000000000000", + "17b0761f87b081d5cf10757ccc89f12be355c70e2e29df28", + "c205db5acaeee273bf4a36c2b9bb786d60400daaaab97077", + "7f3640fc38c1888f8bcb4fd91074f16fb99e4479bc8aa6c8", + "98e9ecb812ba1e96b20606c27b8baa95ac514cf8328c2d8f" + ], + "old_root": "e91eb054d74f257cf29734126e9f5fe1190f1f25cbf00e6a", + "old_value": "5b181ca38a95331682ed666298af3a1773d21208ef4571d6", + "new_value": "9935a6359038a6750966c9e2dd0327c6cf2f0d7ffe5110b8", + "new_root": "75d66f9f33bb0a68cec9ded2b140c60800fa9a8941cabc87" + }, + { + "index": 23, + "siblings": [ + "bcee8bc9cf701c33085580d858b6c765ec6172db34cfa62d", + "e6680586a8925814396a8611a8a14e5c6a739cac0b24b374", + "dbed913941a5d383bfe8f22c8ac0aab233ce2f4dda92ad9d", + "8a67de69396927030e297acdea71f1a5bdf026dc373f618f", + "119e81ebc51feea64117661eb263678b9a2acda0e363a704" + ], + "old_root": "9befd18443b5a9ff4d056fc460f35d4e9fcb7ecb2a81742d", + "old_value": "abbfbf7fa78e9fc73267cf4ad0254cb9c4a35c7006b26b3b", + "new_value": "4b8ad08f621022e95cb7e57522ebee76c77902d0f2c88a05", + "new_root": "61cea17b1e4e18cb3d1f970b2a9765c76e84f80202dffefc" + }, + { + "index": 3, + "siblings": [ + "000000000000000000000000000000000000000000000000", + "537509bc887735cd284909580e34e43c8aa3c46d8387c02d", + "9ff7cf34f94da25dfb5e354b0776f964e9fecf0de81e2509", + "cacb3ca2589221d459911a0e7bff3f0a62e7c85016e239bb", + "53464025934ba9b24b98b99e1337f106a29aa94a404aab2f" + ], + "old_root": "13a0c9fa7de022658f8c29dcb8c3ef6aeaa5c0e82ea07d41", + "old_value": "000000000000000000000000000000000000000000000000", + "new_value": "b9b94fe36a7188868d7392f98d776c3daa9cc2eff31e9d8f", + "new_root": "545f9896f215d23ea516851799412c2d0fd41a079a3c75c7" + }, + { + "index": 25, + "siblings": [ + "b5bbc9ca920933ceb3157330fbd9bd8edf919efdb8dfe7c8", + "a470094ab070ab1ce6fc218b5d593d247649d96be90d93ef", + "167ee6a38a2e85b19f00973df3960aa3badf70af7e3b9d23", + "feb9ea7befe2cf08484efb33e8485cc6cfb617b01fa1b505", + "13af4f8d184975534e42840c30d0a2804ba0037d0fe6a333" + ], + "old_root": "d30ef4068a30505322dc4576f2a4b701f5b76dc87f83f10a", + "old_value": "000000000000000000000000000000000000000000000000", + "new_value": "fa8828e6d55156099c625f3144404003415074d48e27f90d", + "new_root": "4a9a6660a3b067e8afa015c80bbdcc2359e90e2569b7bc66" + }, + { + "index": 15, + "siblings": [ + "a6c8ac63d731870f83d045771fa54a551dc1ec90e973b3d6", + "5032aa0ed7644cfabb2cf73de88333901529e8dca34fa39c", + "5b253a40fe440e2c1240afc1bc1ebdb355fce135f0d99097", + "b50764d3cc0bc182c92679736f9fba7d47c14485af1234aa", + "7ce1decb1e7d9eb29cb5000fa5d42ff5e0c08bc91d47677d" + ], + "old_root": "8539ae1b0468cd7b8738f79531b28481e17fa5d794809eac", + "old_value": "3bf9c5a574d3897537c6068dfc474ecb72a1024a61cb6d11", + "new_value": "5c4c7561bcf9cf2f0e376d226335dd506af9baed711222d1", + "new_root": "7ea8cdb533fa66abaa413e60a5e93f38e0b808204a42b340" + }, + { + "index": 6, + "siblings": [ + "000000000000000000000000000000000000000000000000", + "10e359b3140973a869c54130413af692214a3434853475ca", + "fea97c7b334d36f915a6664d5ca24a9d6b8a7f99c4106c9f", + "2e1b582854d0d65efc1c38ee2ae12bf8bf68d3579703ec6b", + "7ce1decb1e7d9eb29cb5000fa5d42ff5e0c08bc91d47677d" + ], + "old_root": "7ea8cdb533fa66abaa413e60a5e93f38e0b808204a42b340", + "old_value": "ea932d6176fdbc36b6ff23d181ae07074517ceb6fbdc283d", + "new_value": "e0f5e8fe395b2b7c7fbd13c31c9203551f51845be96ba07e", + "new_root": "779452dad24be67db48d6ef8d5a4badfc040f1daa164c72e" + }, + { + "index": 13, + "siblings": [ + "297d38dcc9c0a1db5d0a82a2a04d4a32fa52ba843a1aa732", + "bedf6a6fc5b02b1d73661400155c1b249514deac6d4deb57", + "b288da51d73ea51f75ab1bfd61e0fe006dc646a1f102e3b8", + "6004dbf8bd6cf6b8483adc0bcd5e86fe8935f15f00059e8e" + ], + "old_root": "cd9c40176fd925daf613c897b9c121f26a315003f826308b", + "old_value": "57598afea9d52df5e9fab747fa91e87357965670fc6b08a4", + "new_value": "7055dc8053550062d278ee5da44c64d1e201e8bddec0a4a3", + "new_root": "36091ce0057a596db89a6c3a6678c1b2fe05d1be78da0b88" + }, + { + "index": 5, + "siblings": [ + "bbfe8a0bba40939c061e2153b2b59e2d25a3a2f0cbaabcdb", + "ce586b1ce6f10beeacc41857c223d3a2fc108da3050a5ed6", + "5b253a40fe440e2c1240afc1bc1ebdb355fce135f0d99097", + "fea5eb2983a9bded7d01cd0332c3aa66718954b5e6e5b851" + ], + "old_root": "7a9061a251cae7c17305150835fe1e7d21dc0803cf42455a", + "old_value": "000000000000000000000000000000000000000000000000", + "new_value": "305c9a542e6840fdeadafeb72efbf6e9b17d23635b4e4cf0", + "new_root": "eeaee4014b5be08e28eec7adefd38b37888a4623f29f5997" + } + ] + "#; + + #[test] + fn test_verify_small_merkle_proofs() { + // build circuit once + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + + + let parsed_proofs: Vec = serde_json::from_str(SMALL_MERKLE_PROOFS).unwrap(); + for proof in parsed_proofs { + + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(config); + + let merkle_proof_gadget = MerkleProofTruncatedSha256Gadget::add_virtual_to(&mut builder, proof.siblings.len()); + let expected_root_target = builder.add_virtual_hash192_target(); + builder.connect_hash192(expected_root_target, merkle_proof_gadget.root); + let num_gates = builder.num_gates(); + let data = builder.build::(); + println!( + "MerkleProofTruncatedSha256Gadget (height = {}) circuit num_gates={}, quotient_degree_factor={}", + proof.siblings.len(), num_gates, data.common.quotient_degree_factor + ); + + let mut pw = PartialWitness::new(); + merkle_proof_gadget.set_witness_from_proof(&mut pw, &proof); + pw.set_hash192_target(&expected_root_target, &proof.root.0); + + let start_time = std::time::Instant::now(); + + let proof = data.prove(pw).unwrap(); + let duration_ms = start_time.elapsed().as_millis(); + println!("proved in {}ms", duration_ms); + assert!(data.verify(proof).is_ok()); + } + } + + + #[test] + fn test_verify_small_delta_merkle_proofs() { + // build circuit once + const D: usize = 2; + type C = PoseidonGoldilocksConfig; + type F = >::F; + + + let parsed_proofs: Vec = serde_json::from_str(SMALL_DELTA_MERKLE_PROOFS).unwrap(); + for proof in parsed_proofs { + + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(config); + + let delta_merkle_proof_gadget = DeltaMerkleProofTruncatedSha256Gadget::add_virtual_to(&mut builder, proof.siblings.len()); + let expected_old_root_target = builder.add_virtual_hash192_target(); + let expected_new_root_target = builder.add_virtual_hash192_target(); + builder.connect_hash192(expected_old_root_target, delta_merkle_proof_gadget.old_root); + builder.connect_hash192(expected_new_root_target, delta_merkle_proof_gadget.new_root); + let num_gates = builder.num_gates(); + let data = builder.build::(); + println!( + "DeltaMerkleProofTruncatedSha256Gadget (height = {}) circuit num_gates={}, quotient_degree_factor={}", + proof.siblings.len(), num_gates, data.common.quotient_degree_factor + ); + + let mut pw = PartialWitness::new(); + delta_merkle_proof_gadget.set_witness_from_proof(&mut pw, &proof); + pw.set_hash192_target(&expected_old_root_target, &proof.old_root.0); + pw.set_hash192_target(&expected_new_root_target, &proof.new_root.0); + + let start_time = std::time::Instant::now(); + + let proof = data.prove(pw).unwrap(); + let duration_ms = start_time.elapsed().as_millis(); + println!("proved in {}ms", duration_ms); + assert!(data.verify(proof).is_ok()); + } + } + + +} diff --git a/src/hash/types.rs b/src/hash/types.rs index 3ce3043..fe39416 100644 --- a/src/hash/types.rs +++ b/src/hash/types.rs @@ -1,7 +1,7 @@ use num::BigUint; use plonky2::field::extension::Extendable; use plonky2::field::types::PrimeField64; -use plonky2::hash::hash_types::RichField; +use plonky2::hash::hash_types::{RichField, HashOutTarget}; use plonky2::iop::target::BoolTarget; use plonky2::iop::witness::Witness; use plonky2::plonk::circuit_builder::CircuitBuilder; @@ -13,6 +13,7 @@ use crate::u32::witness::WitnessU32; pub type Hash256Target = [U32Target; 8]; +pub type Hash192Target = [U32Target; 6]; #[derive(Clone, Debug)] pub struct HashTarget { pub input_bits: usize, @@ -47,7 +48,9 @@ pub trait WitnessHash: Witness { fn set_hash_output_le_target(&mut self, target: &HashOutputTarget, value: &[u8]); fn set_hash_blocks_target(&mut self, target: &HashInputTarget, num_blocks: usize); - fn set_hash256_target(&mut self, target: &Hash256Target, value: &[u8; 32]); + + fn set_hash256_target(&mut self, target: &Hash256Target, value: &[u8]); + fn set_hash192_target(&mut self, target: &Hash192Target, value: &[u8]); } impl, F: PrimeField64> WitnessHash for T { @@ -86,7 +89,7 @@ impl, F: PrimeField64> WitnessHash for T { } } - fn set_hash256_target(&mut self, target: &Hash256Target, value: &[u8; 32]) { + fn set_hash256_target(&mut self, target: &Hash256Target, value: &[u8]) { self.set_u32_target(target[0], read_u32_be_at(value, 0)); self.set_u32_target(target[1], read_u32_be_at(value, 4)); self.set_u32_target(target[2], read_u32_be_at(value, 8)); @@ -96,6 +99,15 @@ impl, F: PrimeField64> WitnessHash for T { self.set_u32_target(target[6], read_u32_be_at(value, 24)); self.set_u32_target(target[7], read_u32_be_at(value, 28)); } + + fn set_hash192_target(&mut self, target: &Hash192Target, value: &[u8]) { + self.set_u32_target(target[0], read_u32_be_at(value, 0)); + self.set_u32_target(target[1], read_u32_be_at(value, 4)); + self.set_u32_target(target[2], read_u32_be_at(value, 8)); + self.set_u32_target(target[3], read_u32_be_at(value, 12)); + self.set_u32_target(target[4], read_u32_be_at(value, 16)); + self.set_u32_target(target[5], read_u32_be_at(value, 20)); + } } pub trait CircuitBuilderHash, const D: usize> { @@ -131,8 +143,18 @@ pub trait CircuitBuilderHash, const D: usize> { blocks_input_bits: usize, output_bits: usize, ) -> HashTarget; + fn add_virtual_hash256_target(&mut self) -> Hash256Target; fn connect_hash256(&mut self, x: Hash256Target, y: Hash256Target); + fn select_hash256(&mut self, b: BoolTarget, x: Hash256Target, y: Hash256Target) -> Hash256Target; + + fn add_virtual_hash192_target(&mut self) -> Hash192Target; + fn connect_hash192(&mut self, x: Hash192Target, y: Hash192Target); + fn select_hash192(&mut self, b: BoolTarget, x: Hash192Target, y: Hash192Target) -> Hash192Target; + + + fn hash192_to_hash_out(&mut self, x: Hash192Target) -> HashOutTarget; + fn hash_out_to_hash192(&mut self, x: HashOutTarget) -> Hash192Target; } impl, const D: usize> CircuitBuilderHash @@ -242,5 +264,85 @@ impl, const D: usize> CircuitBuilderHash self.connect_u32(x[6], y[6]); self.connect_u32(x[7], y[7]); } + + fn select_hash256(&mut self, b: BoolTarget, x: Hash256Target, y: Hash256Target) -> Hash256Target { + [ + self.select_u32(b, x[0], y[0]), + self.select_u32(b, x[1], y[1]), + self.select_u32(b, x[2], y[2]), + self.select_u32(b, x[3], y[3]), + self.select_u32(b, x[4], y[4]), + self.select_u32(b, x[5], y[5]), + self.select_u32(b, x[6], y[6]), + self.select_u32(b, x[7], y[7]), + ] + } + + fn add_virtual_hash192_target(&mut self) -> Hash192Target { + [ + self.add_virtual_u32_target(), + self.add_virtual_u32_target(), + self.add_virtual_u32_target(), + + self.add_virtual_u32_target(), + self.add_virtual_u32_target(), + self.add_virtual_u32_target(), + ] + } + + fn connect_hash192(&mut self, x: Hash192Target, y: Hash192Target) { + self.connect_u32(x[0], y[0]); + self.connect_u32(x[1], y[1]); + self.connect_u32(x[2], y[2]); + self.connect_u32(x[3], y[3]); + self.connect_u32(x[4], y[4]); + self.connect_u32(x[5], y[5]); + } + + fn select_hash192(&mut self, b: BoolTarget, x: Hash192Target, y: Hash192Target) -> Hash192Target { + [ + self.select_u32(b, x[0], y[0]), + self.select_u32(b, x[1], y[1]), + self.select_u32(b, x[2], y[2]), + self.select_u32(b, x[3], y[3]), + self.select_u32(b, x[4], y[4]), + self.select_u32(b, x[5], y[5]), + ] + } + + fn hash192_to_hash_out(&mut self, x: Hash192Target) -> HashOutTarget { + let shift_16 = self.constant(F::from_canonical_u64(1<<16)); + let shift_32 = self.constant(F::from_canonical_u64(1<<32)); + let x1_parts = self.split_low_high(x[1].0, 16, 32); + let x4_parts = self.split_low_high(x[4].0, 16, 32); + + HashOutTarget { + elements: [ + self.mul_add(x[0].0, shift_16, x1_parts.1), + self.mul_add(x1_parts.0, shift_32, x[2].0), + self.mul_add(x[3].0, shift_16, x4_parts.1), + self.mul_add(x4_parts.0, shift_32, x[5].0), + ] + } + } + + fn hash_out_to_hash192(&mut self, x: HashOutTarget) -> Hash192Target { + let (x_1_high_16, x_0) = self.split_low_high(x.elements[0], 16, 48); + let (x_2, x_1_low_16) = self.split_low_high(x.elements[1], 32, 48); + let (x_4_high_16, x_3) = self.split_low_high(x.elements[2], 16, 48); + let (x_5, x_4_low_16) = self.split_low_high(x.elements[3], 32, 48); + + let shift_16 = self.constant(F::from_canonical_u64(1<<16)); + [ + U32Target(x_0), + U32Target(self.mul_add(x_1_high_16, shift_16, x_1_low_16)), + U32Target(x_2), + U32Target(x_3), + U32Target(self.mul_add(x_4_high_16, shift_16, x_4_low_16)), + U32Target(x_5), + ] + } + + } diff --git a/src/u32/gadgets/arithmetic_u32.rs b/src/u32/gadgets/arithmetic_u32.rs index 4f33baf..d5566c5 100644 --- a/src/u32/gadgets/arithmetic_u32.rs +++ b/src/u32/gadgets/arithmetic_u32.rs @@ -5,7 +5,7 @@ use core::marker::PhantomData; use plonky2::field::extension::Extendable; use plonky2::hash::hash_types::RichField; use plonky2::iop::generator::{GeneratedValues, SimpleGenerator}; -use plonky2::iop::target::Target; +use plonky2::iop::target::{Target, BoolTarget}; use plonky2::iop::witness::{PartitionWitness, Witness}; use plonky2::plonk::circuit_builder::CircuitBuilder; @@ -60,6 +60,9 @@ pub trait CircuitBuilderU32, const D: usize> { // Returns x - y - borrow, as a pair (result, borrow), where borrow is 0 or 1 depending on whether borrowing from the next digit is required (iff y + borrow > x). fn sub_u32(&mut self, x: U32Target, y: U32Target, borrow: U32Target) -> (U32Target, U32Target); + + // Selects `x` or `y` based on `b`, i.e., this returns `if b { x } else { y }`. + fn select_u32(&mut self, b: BoolTarget, x: U32Target, y: U32Target) -> U32Target; } impl, const D: usize> CircuitBuilderU32 @@ -234,6 +237,10 @@ impl, const D: usize> CircuitBuilderU32 (output_result, output_borrow) } + + fn select_u32(&mut self, b: BoolTarget, x: U32Target, y: U32Target) -> U32Target { + U32Target(self.select(b, x.0, y.0)) + } } #[derive(Debug)]