From bf4c1228c22db6f5c8ad9956a081f1d87576ed35 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 26 Aug 2024 18:27:18 +0000 Subject: [PATCH 1/3] update to keccak for brillig code size --- noir_stdlib/src/hash/keccak.nr | 86 +++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 32 deletions(-) diff --git a/noir_stdlib/src/hash/keccak.nr b/noir_stdlib/src/hash/keccak.nr index bb8a9cc2ce2..ca9d8763770 100644 --- a/noir_stdlib/src/hash/keccak.nr +++ b/noir_stdlib/src/hash/keccak.nr @@ -1,19 +1,32 @@ +use crate::collections::vec::Vec; +use crate::runtime::is_unconstrained; + global LIMBS_PER_BLOCK = 17; //BLOCK_SIZE / 8; global NUM_KECCAK_LANES = 25; global BLOCK_SIZE = 136; //(1600 - BITS * 2) / WORD_SIZE; global WORD_SIZE = 8; -use crate::collections::vec::Vec; - #[foreign(keccakf1600)] fn keccakf1600(input: [u64; 25]) -> [u64; 25] {} #[no_predicates] -pub(crate) fn keccak256(mut input: [u8; N], message_size: u32) -> [u8; 32] { +pub(crate) fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] { assert(N >= message_size); - for i in 0..N { - if i >= message_size { - input[i] = 0; + let mut block_bytes = [0; BLOCK_SIZE]; + // for i in 0..N { + // if i < message_size { + // block_bytes[i] = input[i]; + // } + // } + if is_unconstrained() { + for i in 0..message_size { + block_bytes[i] = input[i]; + } + } else { + for i in 0..N { + if i < message_size { + block_bytes[i] = input[i]; + } } } @@ -24,11 +37,6 @@ pub(crate) fn keccak256(mut input: [u8; N], message_size: u32) -> [u let real_max_blocks = (message_size + BLOCK_SIZE) / BLOCK_SIZE; let real_blocks_bytes = real_max_blocks * BLOCK_SIZE; - let mut block_bytes = [0; BLOCK_SIZE]; - for i in 0..N { - block_bytes[i] = input[i]; - } - block_bytes[message_size] = 1; block_bytes[real_blocks_bytes - 1] = 0x80; @@ -36,28 +44,28 @@ pub(crate) fn keccak256(mut input: [u8; N], message_size: u32) -> [u // means we need to swap our byte ordering let num_limbs = max_blocks * LIMBS_PER_BLOCK; //max_blocks_length / WORD_SIZE; for i in 0..num_limbs { - let mut temp = [0; 8]; - for j in 0..8 { - temp[j] = block_bytes[8*i+j]; + let mut temp = [0; WORD_SIZE]; + let word_size_times_i = WORD_SIZE * i; + for j in 0..WORD_SIZE { + temp[j] = block_bytes[word_size_times_i+j]; } - for j in 0..8 { - block_bytes[8 * i + j] = temp[7 - j]; + for j in 0..WORD_SIZE { + block_bytes[word_size_times_i + j] = temp[7 - j]; } } - let byte_size = max_blocks_length; + let mut sliced_buffer = Vec::new(); - for _i in 0..num_limbs { - sliced_buffer.push(0); - } // populate a vector of 64-bit limbs from our byte array for i in 0..num_limbs { + let word_size_times_i = i * WORD_SIZE; + let ws_times_i_plus_7 = word_size_times_i + 7; let mut sliced = 0; - if (i * WORD_SIZE + WORD_SIZE > byte_size) { - let slice_size = byte_size - (i * WORD_SIZE); + if (word_size_times_i + WORD_SIZE > max_blocks_length) { + let slice_size = max_blocks_length - word_size_times_i; let byte_shift = (WORD_SIZE - slice_size) * 8; let mut v = 1; for k in 0..slice_size { - sliced += v * (block_bytes[i * WORD_SIZE+7-k] as Field); + sliced += v * (block_bytes[ws_times_i_plus_7-k] as Field); v *= 256; } let w = 1 << (byte_shift as u8); @@ -65,22 +73,20 @@ pub(crate) fn keccak256(mut input: [u8; N], message_size: u32) -> [u } else { let mut v = 1; for k in 0..WORD_SIZE { - sliced += v * (block_bytes[i * WORD_SIZE+7-k] as Field); + sliced += v * (block_bytes[ws_times_i_plus_7-k] as Field); v *= 256; } } - sliced_buffer.set(i, sliced as u64); + + sliced_buffer.push(sliced as u64); } //2. sponge_absorb - let num_blocks = max_blocks; let mut state : [u64;NUM_KECCAK_LANES]= [0; NUM_KECCAK_LANES]; - let mut under_block = true; - for i in 0..num_blocks { - if i == real_max_blocks { - under_block = false; - } - if under_block { + // When in an unconstrained runtime we can take advantage of runtime loop bounds, + // thus allowing us to simplify the loop body. + if is_unconstrained() { + for i in 0..real_max_blocks { if (i == 0) { for j in 0..LIMBS_PER_BLOCK { state[j] = sliced_buffer.get(j); @@ -92,6 +98,22 @@ pub(crate) fn keccak256(mut input: [u8; N], message_size: u32) -> [u } state = keccakf1600(state); } + } else { + // `real_max_blocks` is guaranteed to at least be `1` + // We peel out the first block as to avoid a conditional inside of the loop. + // Otherwise, a dynamic predicate can cause a blowup in a constrained runtime. + for j in 0..LIMBS_PER_BLOCK { + state[j] = sliced_buffer.get(j); + } + state = keccakf1600(state); + for i in 1..max_blocks { + if i < real_max_blocks { + for j in 0..LIMBS_PER_BLOCK { + state[j] = state[j] ^ sliced_buffer.get(i * LIMBS_PER_BLOCK + j); + } + state = keccakf1600(state); + } + } } //3. sponge_squeeze From cf6d3703f90f27cde96c65f8e1ecbc0a968d0977 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 26 Aug 2024 18:28:02 +0000 Subject: [PATCH 2/3] reduce old comment --- noir_stdlib/src/hash/keccak.nr | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/noir_stdlib/src/hash/keccak.nr b/noir_stdlib/src/hash/keccak.nr index ca9d8763770..cd63a1326e2 100644 --- a/noir_stdlib/src/hash/keccak.nr +++ b/noir_stdlib/src/hash/keccak.nr @@ -13,11 +13,6 @@ fn keccakf1600(input: [u64; 25]) -> [u64; 25] {} pub(crate) fn keccak256(input: [u8; N], message_size: u32) -> [u8; 32] { assert(N >= message_size); let mut block_bytes = [0; BLOCK_SIZE]; - // for i in 0..N { - // if i < message_size { - // block_bytes[i] = input[i]; - // } - // } if is_unconstrained() { for i in 0..message_size { block_bytes[i] = input[i]; @@ -45,7 +40,7 @@ pub(crate) fn keccak256(input: [u8; N], message_size: u32) -> [u8; 3 let num_limbs = max_blocks * LIMBS_PER_BLOCK; //max_blocks_length / WORD_SIZE; for i in 0..num_limbs { let mut temp = [0; WORD_SIZE]; - let word_size_times_i = WORD_SIZE * i; + let word_size_times_i = WORD_SIZE*i; for j in 0..WORD_SIZE { temp[j] = block_bytes[word_size_times_i+j]; } @@ -53,7 +48,7 @@ pub(crate) fn keccak256(input: [u8; N], message_size: u32) -> [u8; 3 block_bytes[word_size_times_i + j] = temp[7 - j]; } } - + let mut sliced_buffer = Vec::new(); // populate a vector of 64-bit limbs from our byte array for i in 0..num_limbs { @@ -112,7 +107,7 @@ pub(crate) fn keccak256(input: [u8; N], message_size: u32) -> [u8; 3 state[j] = state[j] ^ sliced_buffer.get(i * LIMBS_PER_BLOCK + j); } state = keccakf1600(state); - } + } } } From 6b614e238e0c2437d2035828571cd7c1e5e2dbf3 Mon Sep 17 00:00:00 2001 From: Maxim Vezenov Date: Mon, 26 Aug 2024 18:55:15 +0000 Subject: [PATCH 3/3] nargo fmt --- noir_stdlib/src/hash/keccak.nr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/noir_stdlib/src/hash/keccak.nr b/noir_stdlib/src/hash/keccak.nr index cd63a1326e2..0c31d238f66 100644 --- a/noir_stdlib/src/hash/keccak.nr +++ b/noir_stdlib/src/hash/keccak.nr @@ -40,7 +40,7 @@ pub(crate) fn keccak256(input: [u8; N], message_size: u32) -> [u8; 3 let num_limbs = max_blocks * LIMBS_PER_BLOCK; //max_blocks_length / WORD_SIZE; for i in 0..num_limbs { let mut temp = [0; WORD_SIZE]; - let word_size_times_i = WORD_SIZE*i; + let word_size_times_i = WORD_SIZE * i; for j in 0..WORD_SIZE { temp[j] = block_bytes[word_size_times_i+j]; } @@ -48,7 +48,7 @@ pub(crate) fn keccak256(input: [u8; N], message_size: u32) -> [u8; 3 block_bytes[word_size_times_i + j] = temp[7 - j]; } } - + let mut sliced_buffer = Vec::new(); // populate a vector of 64-bit limbs from our byte array for i in 0..num_limbs { @@ -107,7 +107,7 @@ pub(crate) fn keccak256(input: [u8; N], message_size: u32) -> [u8; 3 state[j] = state[j] ^ sliced_buffer.get(i * LIMBS_PER_BLOCK + j); } state = keccakf1600(state); - } + } } }