Skip to content

Commit

Permalink
feat: remove unnecessary branching in keccak impl (#6133)
Browse files Browse the repository at this point in the history
# Description

## Problem\*

Resolves <!-- Link to GitHub Issue -->

## Summary\*

This PR simplifies the keccak256 implementation by removing a branch
which we'll never actually go down.

When converting from bytes to u64 limbs we have an if statement with the
condition `if (limb_start + WORD_SIZE > max_blocks_length)`.

We can show that the maximum value of the LHS is exactly
`max_blocks_length` and so this branch will never trigger (however
brillig cannot know that so it's preserved in the bytecode).

```
num_limbs * WORD_SIZE = max_blocks * LIMBS_PER_BLOCK * WORD_SIZE
                      = (max_blocks_length / BLOCK_SIZE_IN_BYTES) * LIMBS_PER_BLOCK * WORD_SIZE // using definition of `max_blocks`
                      = max_blocks_length  * (LIMBS_PER_BLOCK * WORD_SIZE / BLOCK_SIZE_IN_BYTES) 
                      = max_blocks_length  * (LIMBS_PER_BLOCK / LIMBS_PER_BLOCK) // using definition of `LIMBS_PER_BLOCK` 
                      = max_blocks_length
```

## Additional Context



## Documentation\*

Check one:
- [x] No documentation needed.
- [ ] Documentation included in this PR.
- [ ] **[For Experimental Features]** Documentation to be submitted in a
separate PR.

# PR Checklist\*

- [x] I have tested the changes locally.
- [x] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
  • Loading branch information
TomAFrench authored Sep 24, 2024
1 parent 9b26650 commit 9c69dce
Showing 1 changed file with 14 additions and 26 deletions.
40 changes: 14 additions & 26 deletions noir_stdlib/src/hash/keccak.nr
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
use crate::collections::vec::Vec;
use crate::runtime::is_unconstrained;

global LIMBS_PER_BLOCK = 17; //BLOCK_SIZE / 8;
global BLOCK_SIZE_IN_BYTES: u32 = 136; //(1600 - BITS * 2) / WORD_SIZE;
global WORD_SIZE: u32 = 8; // Limbs are made up of u64s so 8 bytes each.
global LIMBS_PER_BLOCK: u32 = BLOCK_SIZE_IN_BYTES / WORD_SIZE;
global NUM_KECCAK_LANES: u32 = 25;
global BLOCK_SIZE = 136; //(1600 - BITS * 2) / WORD_SIZE;
global WORD_SIZE = 8;

#[foreign(keccakf1600)]
fn keccakf1600(input: [u64; 25]) -> [u64; 25] {}

#[no_predicates]
pub(crate) fn keccak256<let N: u32>(input: [u8; N], message_size: u32) -> [u8; 32] {
assert(N >= message_size);
let mut block_bytes = [0; BLOCK_SIZE];
let mut block_bytes = [0; BLOCK_SIZE_IN_BYTES];
if is_unconstrained() {
for i in 0..message_size {
block_bytes[i] = input[i];
Expand All @@ -26,38 +26,26 @@ pub(crate) fn keccak256<let N: u32>(input: [u8; N], message_size: u32) -> [u8; 3
}

//1. format_input_lanes
let max_blocks = (N + BLOCK_SIZE) / BLOCK_SIZE;
let max_blocks = (N + BLOCK_SIZE_IN_BYTES) / BLOCK_SIZE_IN_BYTES;
//maximum number of bytes to hash
let max_blocks_length = (BLOCK_SIZE * (max_blocks));
let real_max_blocks = (message_size + BLOCK_SIZE) / BLOCK_SIZE;
let real_blocks_bytes = real_max_blocks * BLOCK_SIZE;
let max_blocks_length = (BLOCK_SIZE_IN_BYTES * max_blocks);
let real_max_blocks = (message_size + BLOCK_SIZE_IN_BYTES) / BLOCK_SIZE_IN_BYTES;
let real_blocks_bytes = real_max_blocks * BLOCK_SIZE_IN_BYTES;

block_bytes[message_size] = 1;
block_bytes[real_blocks_bytes - 1] = 0x80;

let num_limbs = max_blocks * LIMBS_PER_BLOCK; //max_blocks_length / WORD_SIZE;
let mut sliced_buffer = Vec::new();
// populate a vector of 64-bit limbs from our byte array
let num_limbs = max_blocks_length / WORD_SIZE;
let mut sliced_buffer = Vec::new();
for i in 0..num_limbs {
let limb_start = WORD_SIZE * i;

let mut sliced = 0;
if (limb_start + WORD_SIZE > max_blocks_length) {
let slice_size = max_blocks_length - limb_start;
let byte_shift = (WORD_SIZE - slice_size) * 8;
let mut v = 1;
for k in 0..slice_size {
sliced += v * (block_bytes[limb_start+k] as Field);
v *= 256;
}
let w = 1 << (byte_shift as u8);
sliced *= w as Field;
} else {
let mut v = 1;
for k in 0..WORD_SIZE {
sliced += v * (block_bytes[limb_start+k] as Field);
v *= 256;
}
let mut v = 1;
for k in 0..WORD_SIZE {
sliced += v * (block_bytes[limb_start+k] as Field);
v *= 256;
}

sliced_buffer.push(sliced as u64);
Expand Down

0 comments on commit 9c69dce

Please sign in to comment.