Skip to content

Commit

Permalink
Merge 19b726a into aea5cc7
Browse files Browse the repository at this point in the history
  • Loading branch information
AztecBot authored Sep 20, 2024
2 parents aea5cc7 + 19b726a commit cf7dabb
Show file tree
Hide file tree
Showing 16 changed files with 245 additions and 127 deletions.
2 changes: 1 addition & 1 deletion .aztec-sync-commit
Original file line number Diff line number Diff line change
@@ -1 +1 @@
bcec12dbf79b658406dc21083f8fdeef8962085e
4f6941288b38c2eeb6b1956a1cb3cd01b21051dd
19 changes: 3 additions & 16 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion acvm-repo/acir/codegen/acir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ namespace Program {

struct ToRadix {
Program::MemoryAddress input;
uint32_t radix;
Program::MemoryAddress radix;
Program::HeapArray output;
bool output_bits;

Expand Down
5 changes: 4 additions & 1 deletion acvm-repo/bn254_blackbox_solver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ mod schnorr;
use ark_ec::AffineRepr;
pub use embedded_curve_ops::{embedded_curve_add, multi_scalar_mul};
pub use generator::generators::derive_generators;
pub use poseidon2::{field_from_hex, poseidon2_permutation, Poseidon2Config, POSEIDON2_CONFIG};
pub use poseidon2::{
field_from_hex, poseidon2_permutation, poseidon_hash, Poseidon2Config, Poseidon2Sponge,
POSEIDON2_CONFIG,
};

// Temporary hack, this ensure that we always use a bn254 field here
// without polluting the feature flags of the `acir_field` crate.
Expand Down
84 changes: 84 additions & 0 deletions acvm-repo/bn254_blackbox_solver/src/poseidon2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,75 @@ impl<'a> Poseidon2<'a> {
}
}

/// Performs a poseidon hash with a sponge construction equivalent to the one in poseidon2.nr
pub fn poseidon_hash(inputs: &[FieldElement]) -> Result<FieldElement, BlackBoxResolutionError> {
let two_pow_64 = 18446744073709551616_u128.into();
let iv = FieldElement::from(inputs.len()) * two_pow_64;
let mut sponge = Poseidon2Sponge::new(iv, 3);
for input in inputs.iter() {
sponge.absorb(*input)?;
}
sponge.squeeze()
}

pub struct Poseidon2Sponge<'a> {
rate: usize,
poseidon: Poseidon2<'a>,
squeezed: bool,
cache: Vec<FieldElement>,
state: Vec<FieldElement>,
}

impl<'a> Poseidon2Sponge<'a> {
pub fn new(iv: FieldElement, rate: usize) -> Poseidon2Sponge<'a> {
let mut result = Poseidon2Sponge {
cache: Vec::with_capacity(rate),
state: vec![FieldElement::zero(); rate + 1],
squeezed: false,
rate,
poseidon: Poseidon2::new(),
};
result.state[rate] = iv;
result
}

fn perform_duplex(&mut self) -> Result<(), BlackBoxResolutionError> {
// zero-pad the cache
for _ in self.cache.len()..self.rate {
self.cache.push(FieldElement::zero());
}
// add the cache into sponge state
for i in 0..self.rate {
self.state[i] += self.cache[i];
}
self.state = self.poseidon.permutation(&self.state, 4)?;
Ok(())
}

pub fn absorb(&mut self, input: FieldElement) -> Result<(), BlackBoxResolutionError> {
assert!(!self.squeezed);
if self.cache.len() == self.rate {
// If we're absorbing, and the cache is full, apply the sponge permutation to compress the cache
self.perform_duplex()?;
self.cache = vec![input];
} else {
// If we're absorbing, and the cache is not full, add the input into the cache
self.cache.push(input);
}
Ok(())
}

pub fn squeeze(&mut self) -> Result<FieldElement, BlackBoxResolutionError> {
assert!(!self.squeezed);
// If we're in absorb mode, apply sponge permutation to compress the cache.
self.perform_duplex()?;
self.squeezed = true;

// Pop one item off the top of the permutation and return it.
Ok(self.state[0])
}
}

#[cfg(test)]
mod test {
use acir::AcirField;
Expand All @@ -562,4 +631,19 @@ mod test {
];
assert_eq!(result, expected_result);
}

#[test]
fn hash_smoke_test() {
let fields = [
FieldElement::from(1u128),
FieldElement::from(2u128),
FieldElement::from(3u128),
FieldElement::from(4u128),
];
let result = super::poseidon_hash(&fields).expect("should hash successfully");
assert_eq!(
result,
field_from_hex("130bf204a32cac1f0ace56c78b731aa3809f06df2731ebcf6b3464a15788b1b9"),
);
}
}
2 changes: 1 addition & 1 deletion acvm-repo/brillig/src/black_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ pub enum BlackBoxOp {
},
ToRadix {
input: MemoryAddress,
radix: u32,
radix: MemoryAddress,
output: HeapArray,
output_bits: bool,
},
Expand Down
6 changes: 5 additions & 1 deletion acvm-repo/brillig_vm/src/black_box.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,9 +369,13 @@ pub(crate) fn evaluate_black_box<F: AcirField, Solver: BlackBoxFunctionSolver<F>
}
BlackBoxOp::ToRadix { input, radix, output, output_bits } => {
let input: F = *memory.read(*input).extract_field().expect("ToRadix input not a field");
let radix = memory
.read(*radix)
.expect_integer_with_bit_size(IntegerBitSize::U32)
.expect("ToRadix opcode's radix bit size does not match expected bit size 32");

let mut input = BigUint::from_bytes_be(&input.to_be_bytes());
let radix = BigUint::from(*radix);
let radix = BigUint::from_bytes_be(&radix.to_be_bytes());

let mut limbs: Vec<MemoryValue<F>> = Vec::with_capacity(output.size);

Expand Down
4 changes: 3 additions & 1 deletion aztec_macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ acvm.workspace = true
noirc_frontend.workspace = true
noirc_errors.workspace = true
iter-extended.workspace = true
bn254_blackbox_solver.workspace = true
convert_case = "0.6.0"
im.workspace = true
regex = "1.10"
tiny-keccak = { version = "2.0.0", features = ["keccak"] }
itertools = "^0.10"
hex.workspace = true
57 changes: 47 additions & 10 deletions aztec_macros/src/transforms/contract_interface.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use acvm::acir::AcirField;

use bn254_blackbox_solver::poseidon_hash;
use noirc_errors::Location;
use noirc_frontend::ast::{Documented, Ident, NoirFunction, UnresolvedTypeData};
use noirc_frontend::{
Expand All @@ -9,7 +10,7 @@ use noirc_frontend::{
Type,
};

use tiny_keccak::{Hasher, Keccak};
use itertools::Itertools;

use crate::utils::parse_utils::parse_program;
use crate::utils::{
Expand Down Expand Up @@ -251,7 +252,7 @@ pub fn generate_contract_interface(
module_name,
stubs.iter().map(|(src, _)| src.to_owned()).collect::<Vec<String>>().join("\n"),
if has_storage_layout { storage_layout_getter.clone() } else { "".to_string() },
if has_storage_layout { format!("#[contract_library_method]\n{}", storage_layout_getter) } else { "".to_string() }
if has_storage_layout { format!("#[contract_library_method]\n{}", storage_layout_getter) } else { "".to_string() }
);

let (contract_interface_ast, errors) = parse_program(&contract_interface, empty_spans);
Expand Down Expand Up @@ -296,14 +297,8 @@ fn compute_fn_signature_hash(fn_name: &str, parameters: &[Type]) -> u32 {
fn_name,
parameters.iter().map(signature_of_type).collect::<Vec<_>>().join(",")
);
let mut keccak = Keccak::v256();
let mut result = [0u8; 32];
keccak.update(signature.as_bytes());
keccak.finalize(&mut result);
// Take the first 4 bytes of the hash and convert them to an integer
// If you change the following value you have to change NUM_BYTES_PER_NOTE_TYPE_ID in l1_note_payload.ts as well
let num_bytes_per_note_type_id = 4;
u32::from_be_bytes(result[0..num_bytes_per_note_type_id].try_into().unwrap())

hash_to_selector(&signature)
}

// Updates the function signatures in the contract interface with the actual ones, replacing the placeholder.
Expand Down Expand Up @@ -429,3 +424,45 @@ pub fn update_fn_signatures_in_contract_interface(
}
Ok(())
}

fn poseidon2_hash_bytes(inputs: Vec<u8>) -> FieldElement {
let fields: Vec<_> = inputs
.into_iter()
.chunks(31)
.into_iter()
.map(|bytes_chunk| {
let mut chunk_as_vec: Vec<u8> = bytes_chunk.collect();
chunk_as_vec.extend(std::iter::repeat(0).take(32 - chunk_as_vec.len()));
// Build a little endian field element
chunk_as_vec.reverse();
FieldElement::from_be_bytes_reduce(&chunk_as_vec)
})
.collect();

poseidon_hash(&fields).expect("Poseidon hash failed")
}

pub(crate) fn hash_to_selector(inputs: &str) -> u32 {
let hash = poseidon2_hash_bytes(inputs.as_bytes().to_vec()).to_be_bytes();
// Take the last 4 bytes of the hash and convert them to an integer
// If you change the following value you have to change NUM_BYTES_PER_NOTE_TYPE_ID in l1_note_payload.ts as well
let num_bytes_per_note_type_id = 4;
u32::from_be_bytes(hash[(32 - num_bytes_per_note_type_id)..32].try_into().unwrap())
}

#[cfg(test)]
mod test {
use crate::transforms::contract_interface::hash_to_selector;

#[test]
fn test_selector_is_valid() {
let selector = hash_to_selector("IS_VALID()");
assert_eq!(hex::encode(selector.to_be_bytes()), "73cdda47");
}

#[test]
fn test_long_selector() {
let selector = hash_to_selector("foo_and_bar_and_baz_and_foo_bar_baz_and_bar_foo");
assert_eq!(hex::encode(selector.to_be_bytes()), "7590a997");
}
}
13 changes: 3 additions & 10 deletions aztec_macros/src/transforms/note_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ use noirc_frontend::{

use acvm::AcirField;
use regex::Regex;
// TODO(#7165): nuke the following dependency from here and Cargo.toml
use tiny_keccak::{Hasher, Keccak};

use crate::utils::parse_utils::parse_program;
use crate::{
Expand All @@ -28,6 +26,8 @@ use crate::{
},
};

use super::contract_interface::hash_to_selector;

// Automatic implementation of most of the methods in the NoteInterface trait, guiding the user with meaningful error messages in case some
// methods must be implemented manually.
pub fn generate_note_interface_impl(
Expand Down Expand Up @@ -751,14 +751,7 @@ fn generate_note_deserialize_content_source(
// Utility function to generate the note type id as a Field
fn compute_note_type_id(note_type: &str) -> u32 {
// TODO(#4519) Improve automatic note id generation and assignment
let mut keccak = Keccak::v256();
let mut result = [0u8; 32];
keccak.update(note_type.as_bytes());
keccak.finalize(&mut result);
// Take the first 4 bytes of the hash and convert them to an integer
// If you change the following value you have to change NUM_BYTES_PER_NOTE_TYPE_ID in l1_note_payload.ts as well
let num_bytes_per_note_type_id = 4;
u32::from_be_bytes(result[0..num_bytes_per_note_type_id].try_into().unwrap())
hash_to_selector(note_type)
}

pub fn inject_note_exports(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,11 @@ impl<F: AcirField + DebugToString, Registers: RegisterAllocator> BrilligContext<

let heap_array = self.codegen_brillig_array_to_heap_array(target_array);

let radix_var = self.make_constant_instruction(F::from(radix as u128), 32);

self.black_box_op_instruction(BlackBoxOp::ToRadix {
input: source_field.address,
radix,
radix: radix_var.address,
output: heap_array,
output_bits,
});
Expand All @@ -91,5 +93,6 @@ impl<F: AcirField + DebugToString, Registers: RegisterAllocator> BrilligContext<
self.deallocate_single_addr(items_len);
}
self.deallocate_register(heap_array.pointer);
self.deallocate_register(radix_var.address);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use super::{
brillig_variable::{BrilligArray, BrilligVariable, BrilligVector, SingleAddrVariable},
debug_show::DebugToString,
registers::RegisterAllocator,
BrilligContext, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE,
BrilligContext, ReservedRegisters, BRILLIG_MEMORY_ADDRESSING_BIT_SIZE,
};

impl<F: AcirField + DebugToString, Registers: RegisterAllocator> BrilligContext<F, Registers> {
Expand All @@ -20,9 +20,12 @@ impl<F: AcirField + DebugToString, Registers: RegisterAllocator> BrilligContext<
pointer_register: MemoryAddress,
size: usize,
) {
let size_register = self.make_usize_constant_instruction(size.into());
self.codegen_allocate_mem(pointer_register, size_register.address);
self.deallocate_single_addr(size_register);
self.load_free_memory_pointer_instruction(pointer_register);
self.codegen_usize_op_in_place(
ReservedRegisters::free_memory_pointer(),
BrilligBinaryOp::Add,
size,
);
}

/// Allocates an array of size contained in size_register and stores the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -786,7 +786,7 @@ fn to_le_radix(
};

// Decompose the integer into its radix digits in little endian form.
let decomposed_integer = compute_to_radix(value, radix);
let decomposed_integer = compute_to_radix_le(value, radix);
let decomposed_integer = vecmap(0..limb_count as usize, |i| match decomposed_integer.get(i) {
Some(digit) => Value::U8(*digit),
None => Value::U8(0),
Expand All @@ -797,7 +797,7 @@ fn to_le_radix(
))
}

fn compute_to_radix(field: FieldElement, radix: u32) -> Vec<u8> {
fn compute_to_radix_le(field: FieldElement, radix: u32) -> Vec<u8> {
let bit_size = u32::BITS - (radix - 1).leading_zeros();
let radix_big = BigUint::from(radix);
assert_eq!(BigUint::from(2u128).pow(bit_size), radix_big, "ICE: Radix must be a power of 2");
Expand Down
Loading

0 comments on commit cf7dabb

Please sign in to comment.