From abec188a925dc3906f46071b2d05ba5a425f7d9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Thu, 21 Mar 2024 03:11:47 +0000 Subject: [PATCH 1/7] Adjust public value inclusion api --- .../src/history/public_value_inclusion.nr | 32 ++++++++----------- .../aztec/src/state_vars/shared_immutable.nr | 18 ++++------- .../inclusion_proofs_contract/src/main.nr | 27 ++++++++-------- .../src/e2e_inclusion_proofs_contract.test.ts | 4 +-- 4 files changed, 36 insertions(+), 45 deletions(-) diff --git a/noir-projects/aztec-nr/aztec/src/history/public_value_inclusion.nr b/noir-projects/aztec-nr/aztec/src/history/public_value_inclusion.nr index 47dbae54b6d..786e63b586b 100644 --- a/noir-projects/aztec-nr/aztec/src/history/public_value_inclusion.nr +++ b/noir-projects/aztec-nr/aztec/src/history/public_value_inclusion.nr @@ -1,17 +1,16 @@ use dep::protocol_types::{ - constants::GENERATOR_INDEX__PUBLIC_LEAF_INDEX, hash::pedersen_hash, address::{AztecAddress}, + constants::GENERATOR_INDEX__PUBLIC_LEAF_INDEX, hash::pedersen_hash, address::AztecAddress, header::Header, utils::field::full_field_less_than }; use dep::std::merkle::compute_merkle_root; -use crate::{context::PrivateContext, oracle::get_public_data_witness::{get_public_data_witness}}; +use crate::{context::PrivateContext, oracle::get_public_data_witness::get_public_data_witness}; fn _public_value_inclusion( - value: Field, storage_slot: Field, contract_address: AztecAddress, header: Header -) { +) -> Field { // 1) Compute the leaf slot by siloing the storage slot with the contract address let public_value_leaf_slot = pedersen_hash( [contract_address.to_field(), storage_slot], @@ -35,12 +34,12 @@ fn _public_value_inclusion( let is_max = ((preimage.next_index == 0) & (preimage.next_slot == 0)); let is_in_range = is_less_than_slot & (is_next_greater_than | is_max); - if is_in_range { - assert_eq(value, 0, "Non-existant public data leaf value is non-zero"); + let value = if is_in_range { + 0 } else { assert_eq(preimage.slot, public_value_leaf_slot, "Public data slot don't match witness"); - assert_eq(preimage.value, value, "Public value does not match the witness"); - } + preimage.value + }; // 5) Prove that the leaf we validated is in the public data tree assert( @@ -49,30 +48,25 @@ fn _public_value_inclusion( ); // --> Now we have traversed the trees all the way up to archive root and that way verified that a specific // `value` was really set in a given contract storage slot at block `block_number` in public data tree. + + value } pub fn prove_public_value_inclusion( - value: Field, // The value that we want to prove is in the public data tree storage_slot: Field, // The storage slot in which the value is stored contract_address: AztecAddress, // The contract we want to look into context: PrivateContext -) { - _public_value_inclusion( - value, - storage_slot, - contract_address, - context.historical_header - ); +) -> Field { + _public_value_inclusion(storage_slot, contract_address, context.historical_header) } pub fn prove_public_value_inclusion_at( - value: Field, // The value that we want to prove is in the public data tree storage_slot: Field, // The storage slot in which the value is stored contract_address: AztecAddress, // The contract we want to look into block_number: u32, // The block at which we'll prove that the note exists context: PrivateContext -) { +) -> Field { let header = context.get_header_at(block_number); - _public_value_inclusion(value, storage_slot, contract_address, header); + _public_value_inclusion(storage_slot, contract_address, header) } diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr index bdcaeaae60e..91c4bd884f1 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr @@ -54,20 +54,16 @@ impl SharedImmutable { assert(self.context.public.is_none(), "Private read only supported in private functions"); let private_context = self.context.private.unwrap(); - // Read the value from storage (using the public tree) - let fields = storage_read(self.storage_slot); + let mut fields = [0; T_SERIALIZED_LEN]; // Loop over the fields and prove their inclusion in the public tree for i in 0..fields.len() { - // TODO: Update membership proofs to use current header (Requires #4179) - // Currently executing unnecessary computation: - // - a membership proof of the value in the public tree of the header - prove_public_value_inclusion( - fields[i], - self.storage_slot + i as Field, - (*private_context).this_address(), - (*private_context) - ) + fields[i] = + prove_public_value_inclusion( + self.storage_slot + i as Field, + (*private_context).this_address(), + (*private_context) + ); } T::deserialize(fields) } diff --git a/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr b/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr index 9fc57911d51..99c48d39531 100644 --- a/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr @@ -202,37 +202,38 @@ contract InclusionProofs { #[aztec(private)] fn test_public_unused_value_inclusion(block_number: u32 // The block at which we'll prove that the public value exists ) { - prove_public_value_inclusion_at( - 0, - storage.public_unused_value.storage_slot, - context.this_address(), - block_number, - context + assert_eq( + prove_public_value_inclusion_at( + storage.public_unused_value.storage_slot, + context.this_address(), + block_number, + context + ), 0 ); } #[aztec(private)] fn test_public_value_inclusion( - public_value: Field, + expected: Field, use_block_number: bool, block_number: u32 // The block at which we'll prove that the public value exists ) { - if (use_block_number) { + let actual = if (use_block_number) { prove_public_value_inclusion_at( - public_value, storage.public_value.storage_slot, context.this_address(), block_number, context - ); + ) } else { prove_public_value_inclusion( - public_value, storage.public_value.storage_slot, context.this_address(), context - ); - } + ) + }; + + assert_eq(actual, expected, "Actual public value does not match expected"); } // Proves that a contract was publicly deployed and/or initialized at block `block_number`. diff --git a/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts index fcb4959f8fc..c619dd0749c 100644 --- a/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts @@ -205,10 +205,10 @@ describe('e2e_inclusion_proofs_contract', () => { const randomPublicValue = Fr.random(); await expect( contract.methods.test_public_value_inclusion(randomPublicValue, true, blockNumber).send().wait(), - ).rejects.toThrow('Public value does not match the witness'); + ).rejects.toThrow('Actual public value does not match expected'); await expect( contract.methods.test_public_value_inclusion(randomPublicValue, false, 0n).send().wait(), - ).rejects.toThrow('Public value does not match the witness'); + ).rejects.toThrow('Actual public value does not match expected'); }); it('proves existence of uninitialized public value', async () => { From 1495394aea1527dd7766ae87e5828d8f27429de6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Thu, 21 Mar 2024 18:04:41 +0000 Subject: [PATCH 2/7] rename public_value_inclusion to storage --- noir-projects/aztec-nr/aztec/src/history.nr | 2 +- .../aztec/src/history/{public_value_inclusion.nr => storage.nr} | 0 noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr | 2 +- .../contracts/inclusion_proofs_contract/src/main.nr | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename noir-projects/aztec-nr/aztec/src/history/{public_value_inclusion.nr => storage.nr} (100%) diff --git a/noir-projects/aztec-nr/aztec/src/history.nr b/noir-projects/aztec-nr/aztec/src/history.nr index 4fe5f6281cf..095d8fa6f88 100644 --- a/noir-projects/aztec-nr/aztec/src/history.nr +++ b/noir-projects/aztec-nr/aztec/src/history.nr @@ -3,4 +3,4 @@ mod note_inclusion; mod note_validity; mod nullifier_inclusion; mod nullifier_non_inclusion; -mod public_value_inclusion; +mod storage; diff --git a/noir-projects/aztec-nr/aztec/src/history/public_value_inclusion.nr b/noir-projects/aztec-nr/aztec/src/history/storage.nr similarity index 100% rename from noir-projects/aztec-nr/aztec/src/history/public_value_inclusion.nr rename to noir-projects/aztec-nr/aztec/src/history/storage.nr diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr index 91c4bd884f1..c7ad1431900 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr @@ -1,5 +1,5 @@ use crate::{ - context::Context, history::public_value_inclusion::prove_public_value_inclusion, + context::Context, history::storage::prove_public_value_inclusion, oracle::{storage::{storage_read, storage_write}}, state_vars::storage::Storage }; use dep::protocol_types::{constants::INITIALIZATION_SLOT_SEPARATOR, traits::{Deserialize, Serialize}}; diff --git a/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr b/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr index 99c48d39531..012a992e359 100644 --- a/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr @@ -20,7 +20,7 @@ contract InclusionProofs { prove_note_is_nullified_at }, nullifier_non_inclusion::{prove_note_not_nullified, prove_note_not_nullified_at}, - public_value_inclusion::{prove_public_value_inclusion, prove_public_value_inclusion_at} + storage::{prove_public_value_inclusion, prove_public_value_inclusion_at} }; // docs:end:imports // docs:start:value_note_imports From a19a44dbd3fb1c1ab4b96ae46146b0983f18119c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Thu, 21 Mar 2024 20:41:49 +0000 Subject: [PATCH 3/7] Update naming --- .../references/history_lib_reference.md | 124 +++++++++--------- .../aztec-nr/aztec/src/history/storage.nr | 29 ++-- .../aztec/src/state_vars/shared_immutable.nr | 5 +- .../inclusion_proofs_contract/src/main.nr | 14 +- .../src/e2e_inclusion_proofs_contract.test.ts | 31 +++-- 5 files changed, 99 insertions(+), 104 deletions(-) diff --git a/docs/docs/developers/contracts/references/history_lib_reference.md b/docs/docs/developers/contracts/references/history_lib_reference.md index 5928a54275b..c145f52f801 100644 --- a/docs/docs/developers/contracts/references/history_lib_reference.md +++ b/docs/docs/developers/contracts/references/history_lib_reference.md @@ -4,7 +4,7 @@ title: History Reference -## Note inclusion +## Note inclusion Note inclusion proves that a note existed (its hash was included in a note hash tree) at a specific block number. There exists a version that tests for note inclusion at current block number. It is recommended to use this version whenever possible to reduce cost. @@ -12,11 +12,11 @@ Note inclusion proves that a note existed (its hash was included in a note hash `prove_note_inclusion_at` takes 3 parameters: -| Name | Type | Description | -|-----------------|------------------------|-----------------------------------------------------| -| note_with_header| Note | The note you are proving inclusion for | -| block_number | u32 | Block number for proving note's existence | -| context | PrivateContext | Private context | +| Name | Type | Description | +| ---------------- | -------------- | ----------------------------------------- | +| note_with_header | Note | The note you are proving inclusion for | +| block_number | u32 | Block number for proving note's existence | +| context | PrivateContext | Private context | ## prove_note_commitment_inclusion @@ -24,10 +24,10 @@ A **commitment**, also referred to as a **note hash** is a public acknowledgment `prove_note_commitment_inclusion` takes 2 parameters: -| Name | Type | Description | -|-----------------|------------------------|-----------------------------------------------------| -| note_with_header| Note | The note you are proving inclusion for | -| context | PrivateContext | Private context | +| Name | Type | Description | +| ---------------- | -------------- | -------------------------------------- | +| note_with_header | Note | The note you are proving inclusion for | +| context | PrivateContext | Private context | ## Note validity @@ -37,18 +37,18 @@ This proves that a note exists and has not been nullified at a specified block. `prove_note_validity_at` takes 3 parameters: -| Name | Type | Description | -|-----------------|------------------------|-----------------------------------------------------| -| note_with_header| Note | The note you are proving inclusion for | -| block_number | u32 | Block number for proving note's existence | -| context | PrivateContext | Private context | +| Name | Type | Description | +| ---------------- | -------------- | ----------------------------------------- | +| note_with_header | Note | The note you are proving inclusion for | +| block_number | u32 | Block number for proving note's existence | +| context | PrivateContext | Private context | `prove_note_validity` takes 2 parameters: -| Name | Type | Description | -|-----------------|------------------------|-----------------------------------------------------| -| note_with_header| Note | The note you are proving inclusion for | -| context | PrivateContext | Private context | +| Name | Type | Description | +| ---------------- | -------------- | -------------------------------------- | +| note_with_header | Note | The note you are proving inclusion for | +| context | PrivateContext | Private context | ## Nullifier inclusion @@ -58,18 +58,18 @@ This proves that a nullifier was included in a certain block (can be used to pro `prove_nullifier_inclusion_at` takes 3 parameters: -| Name | Type | Description | -|-----------------|------------------------|-----------------------------------------------------| -| nullifier | Field | The nullifier you are proving inclusion for | -| block_number | u32 | Block number for proving note's existence | -| context | PrivateContext | Private context | +| Name | Type | Description | +| ------------ | -------------- | ------------------------------------------- | +| nullifier | Field | The nullifier you are proving inclusion for | +| block_number | u32 | Block number for proving note's existence | +| context | PrivateContext | Private context | `prove_nullifier_inclusion` takes 2 parameters: -| Name | Type | Description | -|-----------------|------------------------|-----------------------------------------------------| -| nullifier | Field | The nullifier you are proving inclusion for | -| context | PrivateContext | Private context | +| Name | Type | Description | +| --------- | -------------- | ------------------------------------------- | +| nullifier | Field | The nullifier you are proving inclusion for | +| context | PrivateContext | Private context | ### prove_note_is_nullified_at / prove_note_is_nullified @@ -83,45 +83,47 @@ This proves that a nullifier was not included in a certain block (can be used to `prove_nullifier_not_included_at` takes 3 parameters: -| Name | Type | Description | -|-----------------|------------------------|-----------------------------------------------------| -| nullifier | Field | The nullifier you are proving inclusion for | -| block_number | u32 | Block number for proving note's existence | -| context | PrivateContext | Private context | +| Name | Type | Description | +| ------------ | -------------- | ------------------------------------------- | +| nullifier | Field | The nullifier you are proving inclusion for | +| block_number | u32 | Block number for proving note's existence | +| context | PrivateContext | Private context | `prove_nullifier_not_included` takes 2 parameters: -| Name | Type | Description | -|-----------------|------------------------|-----------------------------------------------------| -| nullifier | Field | The nullifier you are proving inclusion for | -| context | PrivateContext | Private context | +| Name | Type | Description | +| --------- | -------------- | ------------------------------------------- | +| nullifier | Field | The nullifier you are proving inclusion for | +| context | PrivateContext | Private context | ### prove_note_not_nullified_at / prove_note_not_nullified Instead of passing the nullifier, you can check that a note has not been nullified by passing the note. -## Public value inclusion +## Storage historical reads -This proves that a public value exists at a certain block. +These return the value stored in a public storage slot of a given contract at the end of the execution of a certain block (the latest one if using `storage_historical_read`). + +Note that it is never possible to read the _current_ value in a public storage slot in private since private execution is local and by definition always works on _historical_ state. ### prove_public_value_inclusion -`prove_public_value_inclusion_at` takes 4 parameters: +`storage_historical_read_at` takes 4 parameters: -| Name | Type | Description | -|-----------------|------------------------|-----------------------------------------------------| -| value | Field | The public value you are proving inclusion for | -| storage_slot | Field | Storage slot the value exists in | -| block_number | u32 | Block number for proving value's existence | -| context | PrivateContext | Private context | +| Name | Type | Description | +| ---------------- | -------------- | ---------------------------------------- | +| storage_slot | Field | Storage slot | +| contract_address | AztecAddress | The contract that owns the storage slot | +| block_number | u32 | Historical block number in which to read | +| context | PrivateContext | Private context | -`prove_public_value_inclusion` takes 3 parameters: +`storage_historical_read` takes 3 parameters: -| Name | Type | Description | -|-----------------|------------------------|-----------------------------------------------------| -| value | Field | The public value you are proving inclusion for | -| storage_slot | Field | Storage slot the value exists in | -| context | PrivateContext | Private context | +| Name | Type | Description | +| ---------------- | -------------- | --------------------------------------- | +| storage_slot | Field | Storage slot | +| contract_address | AztecAddress | The contract that owns the storage slot | +| context | PrivateContext | Private context | ## Contract inclusion @@ -131,15 +133,15 @@ This proves that a contract exists in, ie had been deployed before or in, a cert `prove_contract_inclusion_at` takes 7 parameters: -| Name | Type | Description | -|---------------------------|-----------------|-------------------------------------------------------| -| deployer_public_key | GrumpkinPoint | Public key of the contract deployer | -| contract_address_salt | Field | Unique identifier for the contract's address | -| function_tree_root | Field | Root of the contract's function tree | -| constructor_hash | Field | Hash of the contract's constructor | -| portal_contract_address | EthAddress | Ethereum address of the associated portal contract | -| block_number | u32 | Block number for proof verification | -| context | PrivateContext | Private context | +| Name | Type | Description | +| ----------------------- | -------------- | -------------------------------------------------- | +| deployer_public_key | GrumpkinPoint | Public key of the contract deployer | +| contract_address_salt | Field | Unique identifier for the contract's address | +| function_tree_root | Field | Root of the contract's function tree | +| constructor_hash | Field | Hash of the contract's constructor | +| portal_contract_address | EthAddress | Ethereum address of the associated portal contract | +| block_number | u32 | Block number for proof verification | +| context | PrivateContext | Private context | If there is no associated portal contract, you can use a zero Ethereum address: diff --git a/noir-projects/aztec-nr/aztec/src/history/storage.nr b/noir-projects/aztec-nr/aztec/src/history/storage.nr index 786e63b586b..8a98e490719 100644 --- a/noir-projects/aztec-nr/aztec/src/history/storage.nr +++ b/noir-projects/aztec-nr/aztec/src/history/storage.nr @@ -6,24 +6,20 @@ use dep::std::merkle::compute_merkle_root; use crate::{context::PrivateContext, oracle::get_public_data_witness::get_public_data_witness}; -fn _public_value_inclusion( - storage_slot: Field, - contract_address: AztecAddress, - header: Header -) -> Field { +fn _storage_historical_read(storage_slot: Field, contract_address: AztecAddress, header: Header) -> Field { // 1) Compute the leaf slot by siloing the storage slot with the contract address let public_value_leaf_slot = pedersen_hash( [contract_address.to_field(), storage_slot], GENERATOR_INDEX__PUBLIC_LEAF_INDEX ); - // 3) Get the membership witness of the slot + // 2) Get the membership witness of the slot let witness = get_public_data_witness( header.global_variables.block_number as u32, public_value_leaf_slot ); - // 4) Check that the witness matches the corresponding public_value + // 3) Extract the value from the witness leaf and check that the storage slot is correct let preimage = witness.leaf_preimage; // Here we have two cases. Code based on same checks in `validate_public_data_reads` in `base_rollup_inputs` @@ -41,32 +37,31 @@ fn _public_value_inclusion( preimage.value }; - // 5) Prove that the leaf we validated is in the public data tree + // 4) Prove that the leaf exists in the public data tree. Note that `hash` returns not just the hash of the value + // but also the metadata (slot, next index and next slot). assert( header.state.partial.public_data_tree.root == compute_merkle_root(preimage.hash(), witness.index, witness.path), "Proving public value inclusion failed" ); - // --> Now we have traversed the trees all the way up to archive root and that way verified that a specific - // `value` was really set in a given contract storage slot at block `block_number` in public data tree. value } -pub fn prove_public_value_inclusion( - storage_slot: Field, // The storage slot in which the value is stored +pub fn storage_historical_read( + storage_slot: Field, // The storage slot to read contract_address: AztecAddress, // The contract we want to look into context: PrivateContext ) -> Field { - _public_value_inclusion(storage_slot, contract_address, context.historical_header) + _storage_historical_read(storage_slot, contract_address, context.historical_header) } -pub fn prove_public_value_inclusion_at( - storage_slot: Field, // The storage slot in which the value is stored +pub fn storage_historical_read_at( + storage_slot: Field, // The storage slot to read contract_address: AztecAddress, // The contract we want to look into - block_number: u32, // The block at which we'll prove that the note exists + block_number: u32, // The block number at the end of which we'll read the value context: PrivateContext ) -> Field { let header = context.get_header_at(block_number); - _public_value_inclusion(storage_slot, contract_address, header) + _storage_historical_read(storage_slot, contract_address, header) } diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr index c7ad1431900..5798ec291a7 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr @@ -1,5 +1,5 @@ use crate::{ - context::Context, history::storage::prove_public_value_inclusion, + context::Context, history::storage::storage_historical_read, oracle::{storage::{storage_read, storage_write}}, state_vars::storage::Storage }; use dep::protocol_types::{constants::INITIALIZATION_SLOT_SEPARATOR, traits::{Deserialize, Serialize}}; @@ -56,10 +56,9 @@ impl SharedImmutable { let mut fields = [0; T_SERIALIZED_LEN]; - // Loop over the fields and prove their inclusion in the public tree for i in 0..fields.len() { fields[i] = - prove_public_value_inclusion( + storage_historical_read( self.storage_slot + i as Field, (*private_context).this_address(), (*private_context) diff --git a/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr b/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr index 012a992e359..de80d4778bf 100644 --- a/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr @@ -20,7 +20,7 @@ contract InclusionProofs { prove_note_is_nullified_at }, nullifier_non_inclusion::{prove_note_not_nullified, prove_note_not_nullified_at}, - storage::{prove_public_value_inclusion, prove_public_value_inclusion_at} + storage::{storage_historical_read, storage_historical_read_at} }; // docs:end:imports // docs:start:value_note_imports @@ -200,10 +200,10 @@ contract InclusionProofs { } #[aztec(private)] - fn test_public_unused_value_inclusion(block_number: u32 // The block at which we'll prove that the public value exists + fn test_storage_historical_read_unset_slot(block_number: u32 // The block at which we'll read the public storage value ) { assert_eq( - prove_public_value_inclusion_at( + storage_historical_read_at( storage.public_unused_value.storage_slot, context.this_address(), block_number, @@ -213,20 +213,20 @@ contract InclusionProofs { } #[aztec(private)] - fn test_public_value_inclusion( + fn test_storage_historical_read( expected: Field, use_block_number: bool, - block_number: u32 // The block at which we'll prove that the public value exists + block_number: u32 // The block at which we'll read the public storage value ) { let actual = if (use_block_number) { - prove_public_value_inclusion_at( + storage_historical_read_at( storage.public_value.storage_slot, context.this_address(), block_number, context ) } else { - prove_public_value_inclusion( + storage_historical_read( storage.public_value.storage_slot, context.this_address(), context diff --git a/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts index c619dd0749c..6142f58d70d 100644 --- a/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts @@ -190,30 +190,25 @@ describe('e2e_inclusion_proofs_contract', () => { }); }); - describe('public value existence at a slot', () => { - it('proves an existence of a public value in private context', async () => { + describe('historical storage reads', () => { + it('reads a historical public value in private context', async () => { // Choose random block number between deployment and current block number to test archival node const blockNumber = await getRandomBlockNumberSinceDeployment(); - await contract.methods.test_public_value_inclusion(publicValue, true, blockNumber).send().wait(); - await contract.methods.test_public_value_inclusion(publicValue, false, 0n).send().wait(); + await contract.methods.test_storage_historical_read(publicValue, true, blockNumber).send().wait(); + await contract.methods.test_storage_historical_read(publicValue, false, 0n).send().wait(); }); - it('public value existence failure case', async () => { - // Choose random block number between first block and current block number to test archival node - const blockNumber = await getRandomBlockNumber(); - const randomPublicValue = Fr.random(); - await expect( - contract.methods.test_public_value_inclusion(randomPublicValue, true, blockNumber).send().wait(), - ).rejects.toThrow('Actual public value does not match expected'); - await expect( - contract.methods.test_public_value_inclusion(randomPublicValue, false, 0n).send().wait(), - ).rejects.toThrow('Actual public value does not match expected'); + it('reads an older (unset) public value', async () => { + const blockNumber = getRandomBlockNumberBeforeDeployment(); + await contract.methods.test_storage_historical_read(0, true, blockNumber).send().wait(); }); - it('proves existence of uninitialized public value', async () => { + it('reads a historical unset public value in private context', async () => { + // This test scenario is interesting because the codepath for storage values that were never set is different + // (since they're don't exist in the tree). const blockNumber = await getRandomBlockNumber(); - await contract.methods.test_public_unused_value_inclusion(blockNumber).send().wait(); + await contract.methods.test_storage_historical_read_unset_slot(blockNumber).send().wait(); }); }); @@ -297,4 +292,8 @@ describe('e2e_inclusion_proofs_contract', () => { const getRandomBlockNumber = async () => { return deploymentBlockNumber + randomInt((await pxe.getBlockNumber()) - INITIAL_L2_BLOCK_NUM); }; + + const getRandomBlockNumberBeforeDeployment = () => { + return randomInt(deploymentBlockNumber - INITIAL_L2_BLOCK_NUM); + }; }); From df8c32c48476b426d91514ba4ad0862cc9967539 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Thu, 21 Mar 2024 21:08:27 +0000 Subject: [PATCH 4/7] Add migration notes --- .../references/history_lib_reference.md | 2 +- docs/docs/misc/migration_notes.md | 44 ++++++++++++++++--- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/docs/docs/developers/contracts/references/history_lib_reference.md b/docs/docs/developers/contracts/references/history_lib_reference.md index c145f52f801..8d635d3625a 100644 --- a/docs/docs/developers/contracts/references/history_lib_reference.md +++ b/docs/docs/developers/contracts/references/history_lib_reference.md @@ -106,7 +106,7 @@ These return the value stored in a public storage slot of a given contract at th Note that it is never possible to read the _current_ value in a public storage slot in private since private execution is local and by definition always works on _historical_ state. -### prove_public_value_inclusion +### storage_historical_read `storage_historical_read_at` takes 4 parameters: diff --git a/docs/docs/misc/migration_notes.md b/docs/docs/misc/migration_notes.md index 1e1af122f26..400a3a0d0d5 100644 --- a/docs/docs/misc/migration_notes.md +++ b/docs/docs/misc/migration_notes.md @@ -6,8 +6,31 @@ keywords: [sandbox, cli, aztec, notes, migration, updating, upgrading] Aztec is in full-speed development. Literally every version breaks compatibility with the previous ones. This page attempts to target errors and difficulties you might encounter when upgrading, and how to resolve them. +## TBD + +### [Aztec.nr] Public storage historical read API improvement + +`history::public_value_inclusion::prove_public_value_inclusion` has been renamed to `history::storage::storage_historical_read`, and it's API changed slightly. Instead of receiving a `value` parameter it now returns the historical value stored at that slot. + +If you were using an oracle to get the value to pass to `prove_public_value_inclusion`, drop the oracle and use the return value from `storage_historical_read` instead: + +```diff +- let value = read_storage(); +- prove_public_value_inclusion(value, storage_slot, contract_address, context); ++ let value = storage_historical_read(storage_slot, contract_address, context); +``` + +If you were proving historical existence of a value you got via some other constrained means, perform an assertion against the return value of `storage_historical_read` instead: + +```diff +- prove_public_value_inclusion(value, storage_slot, contract_address, context); ++ assert(storage_historical_read(storage_slot, contract_address, context) == value); +``` + ## 0.30.0 + ### [AztecJS] Simplify authwit syntax + ```diff - const messageHash = computeAuthWitMessageHash(accounts[1].address, action.request()); - await wallets[0].setPublicAuth(messageHash, true).send().wait(); @@ -30,7 +53,7 @@ Also note some of the naming changes: ### [Aztec.nr] Automatic NoteInterface implementation and selector changes -Implementing a note required a fair amount of boilerplate code, which has been substituted by the `#[aztec(note)]` attribute. +Implementing a note required a fair amount of boilerplate code, which has been substituted by the `#[aztec(note)]` attribute. ```diff + #[aztec(note)] @@ -107,7 +130,7 @@ impl NoteInterface for AddressNote { Automatic note (de)serialization implementation also means it is now easier to filter notes using `NoteGetterOptions.select` via the `::properties()` helper: -Before: +Before: ```rust let options = NoteGetterOptions::new().select(0, amount, Option::none()).select(1, owner.to_field(), Option::none()).set_limit(1); @@ -139,7 +162,7 @@ Before this version, every contract was required to have exactly one `constructo To signal that a function can be used to **initialize** a contract, you must now decorate it with the `#[aztec(initializer)]` attribute. Initializers are regular functions that set an "initialized" flag (a nullifier) for the contract. A contract can only be initialized once, and contract functions can only be called after the contract has been initialized, much like a constructor. However, if a contract defines no initializers, it can be called at any time. Additionally, you can define as many initializer functions in a contract as you want, both private and public. -To migrate from current code, simply add an initializer attribute to your constructor functions. +To migrate from current code, simply add an initializer attribute to your constructor functions. ```diff + #[aztec(initializer)] @@ -165,6 +188,7 @@ context.static_call_public_function(targetContractAddress, targetSelector, args) A new `prelude` module to include common Aztec modules and types. This simplifies dependency syntax. For example: + ```rust use dep::aztec::protocol_types::address::AztecAddress; use dep::aztec::{ @@ -172,7 +196,9 @@ use dep::aztec::{ state_vars::Map }; ``` + Becomes: + ```rust use dep::aztec::prelude::{AztecAddress, NoteHeader, PrivateContext, Map}; use dep::aztec::context::Context; @@ -190,6 +216,7 @@ The prelude consists of The `internal` keyword is now removed from Noir, and is replaced by an `aztec(internal)` attribute in the function. The resulting behavior is exactly the same: these functions will only be callable from within the same contract. Before: + ```rust #[aztec(private)] internal fn double(input: Field) -> Field { @@ -198,6 +225,7 @@ internal fn double(input: Field) -> Field { ``` After: + ```rust #[aztec(private)] #[aztec(internal)] @@ -207,26 +235,30 @@ fn double(input: Field) -> Field { ``` ### [Aztec.nr] No SafeU120 anymore! + Noir now have overflow checks by default. So we don't need SafeU120 like libraries anymore. You can replace it with `U128` instead -Before: +Before: + ``` SafeU120::new(0) ``` Now: + ``` U128::from_integer(0) ``` + ### [Aztec.nr] `compute_note_hash_and_nullifier` is now autogenerated Historically developers have been required to include a `compute_note_hash_and_nullifier` function in each of their contracts. This function is now automatically generated, and all instances of it in contract code can be safely removed. It is possible to provide a user-defined implementation, in which case auto-generation will be skipped (though there are no known use cases for this). -### [Aztec.nr] Updated naming of state variable wrappers +### [Aztec.nr] Updated naming of state variable wrappers We have decided to change the naming of our state variable wrappers because the naming was not clear. The changes are as follows: @@ -254,6 +286,7 @@ Furthermore, the `caller` parameter of the "authwits" have been moved "further o For most contracts, this won't be changing much, but for the account contract, it will require a few changes. Before: + ```rust #[aztec(public)] fn is_valid_public(message_hash: Field) -> Field { @@ -269,6 +302,7 @@ fn is_valid(message_hash: Field) -> Field { ``` After: + ```rust #[aztec(private)] fn spend_private_authwit(inner_hash: Field) -> Field { From 33cd4144213eeb95c95ad4fa0abfeaee47765520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Fri, 22 Mar 2024 14:01:58 -0300 Subject: [PATCH 5/7] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jan Beneš --- docs/docs/misc/migration_notes.md | 2 +- noir-projects/aztec-nr/aztec/src/history/storage.nr | 2 +- .../end-to-end/src/e2e_inclusion_proofs_contract.test.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/docs/misc/migration_notes.md b/docs/docs/misc/migration_notes.md index 400a3a0d0d5..35c50b926a2 100644 --- a/docs/docs/misc/migration_notes.md +++ b/docs/docs/misc/migration_notes.md @@ -10,7 +10,7 @@ Aztec is in full-speed development. Literally every version breaks compatibility ### [Aztec.nr] Public storage historical read API improvement -`history::public_value_inclusion::prove_public_value_inclusion` has been renamed to `history::storage::storage_historical_read`, and it's API changed slightly. Instead of receiving a `value` parameter it now returns the historical value stored at that slot. +`history::public_value_inclusion::prove_public_value_inclusion` has been renamed to `history::storage::storage_historical_read`, and its API changed slightly. Instead of receiving a `value` parameter it now returns the historical value stored at that slot. If you were using an oracle to get the value to pass to `prove_public_value_inclusion`, drop the oracle and use the return value from `storage_historical_read` instead: diff --git a/noir-projects/aztec-nr/aztec/src/history/storage.nr b/noir-projects/aztec-nr/aztec/src/history/storage.nr index 8a98e490719..91b1a16f885 100644 --- a/noir-projects/aztec-nr/aztec/src/history/storage.nr +++ b/noir-projects/aztec-nr/aztec/src/history/storage.nr @@ -33,7 +33,7 @@ fn _storage_historical_read(storage_slot: Field, contract_address: AztecAddress, let value = if is_in_range { 0 } else { - assert_eq(preimage.slot, public_value_leaf_slot, "Public data slot don't match witness"); + assert_eq(preimage.slot, public_value_leaf_slot, "Public data slot doesn't match witness"); preimage.value }; diff --git a/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts index 6142f58d70d..850b349fa58 100644 --- a/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts +++ b/yarn-project/end-to-end/src/e2e_inclusion_proofs_contract.test.ts @@ -206,7 +206,7 @@ describe('e2e_inclusion_proofs_contract', () => { it('reads a historical unset public value in private context', async () => { // This test scenario is interesting because the codepath for storage values that were never set is different - // (since they're don't exist in the tree). + // (since they don't exist in the tree). const blockNumber = await getRandomBlockNumber(); await contract.methods.test_storage_historical_read_unset_slot(blockNumber).send().wait(); }); @@ -294,6 +294,6 @@ describe('e2e_inclusion_proofs_contract', () => { }; const getRandomBlockNumberBeforeDeployment = () => { - return randomInt(deploymentBlockNumber - INITIAL_L2_BLOCK_NUM); + return randomInt(deploymentBlockNumber - INITIAL_L2_BLOCK_NUM) + INITIAL_L2_BLOCK_NUM; }; }); From 5233db448c2a7215c49cad22b2260cf7a1efe38b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Fri, 22 Mar 2024 17:08:50 +0000 Subject: [PATCH 6/7] rename storage to public storage --- .../references/history_lib_reference.md | 14 ++++++------- docs/docs/misc/migration_notes.md | 10 +++++----- noir-projects/aztec-nr/aztec/src/history.nr | 2 +- .../history/{storage.nr => public_storage.nr} | 18 ++++++++--------- .../aztec/src/state_vars/shared_immutable.nr | 8 ++++---- .../inclusion_proofs_contract/src/main.nr | 20 +++++++++---------- 6 files changed, 36 insertions(+), 36 deletions(-) rename noir-projects/aztec-nr/aztec/src/history/{storage.nr => public_storage.nr} (79%) diff --git a/docs/docs/developers/contracts/references/history_lib_reference.md b/docs/docs/developers/contracts/references/history_lib_reference.md index 8d635d3625a..cc592a6760a 100644 --- a/docs/docs/developers/contracts/references/history_lib_reference.md +++ b/docs/docs/developers/contracts/references/history_lib_reference.md @@ -100,30 +100,30 @@ This proves that a nullifier was not included in a certain block (can be used to Instead of passing the nullifier, you can check that a note has not been nullified by passing the note. -## Storage historical reads +## Public storage historical reads -These return the value stored in a public storage slot of a given contract at the end of the execution of a certain block (the latest one if using `storage_historical_read`). +These return the value stored in a public storage slot of a given contract at the end of the execution of a certain block (the latest one if using `public_storage_historical_read`). Note that it is never possible to read the _current_ value in a public storage slot in private since private execution is local and by definition always works on _historical_ state. -### storage_historical_read +### public_storage_historical_read -`storage_historical_read_at` takes 4 parameters: +`public_storage_historical_read_at` takes 4 parameters: | Name | Type | Description | | ---------------- | -------------- | ---------------------------------------- | +| context | PrivateContext | Private context | | storage_slot | Field | Storage slot | | contract_address | AztecAddress | The contract that owns the storage slot | | block_number | u32 | Historical block number in which to read | -| context | PrivateContext | Private context | -`storage_historical_read` takes 3 parameters: +`public_storage_historical_read` takes 3 parameters. `block_number` is implicitly the historical block number from the context: | Name | Type | Description | | ---------------- | -------------- | --------------------------------------- | +| context | PrivateContext | Private context | | storage_slot | Field | Storage slot | | contract_address | AztecAddress | The contract that owns the storage slot | -| context | PrivateContext | Private context | ## Contract inclusion diff --git a/docs/docs/misc/migration_notes.md b/docs/docs/misc/migration_notes.md index 35c50b926a2..f3ae905b012 100644 --- a/docs/docs/misc/migration_notes.md +++ b/docs/docs/misc/migration_notes.md @@ -10,21 +10,21 @@ Aztec is in full-speed development. Literally every version breaks compatibility ### [Aztec.nr] Public storage historical read API improvement -`history::public_value_inclusion::prove_public_value_inclusion` has been renamed to `history::storage::storage_historical_read`, and its API changed slightly. Instead of receiving a `value` parameter it now returns the historical value stored at that slot. +`history::public_value_inclusion::prove_public_value_inclusion` has been renamed to `history::public_storage::public_storage_historical_read`, and its API changed slightly. Instead of receiving a `value` parameter it now returns the historical value stored at that slot. -If you were using an oracle to get the value to pass to `prove_public_value_inclusion`, drop the oracle and use the return value from `storage_historical_read` instead: +If you were using an oracle to get the value to pass to `prove_public_value_inclusion`, drop the oracle and use the return value from `public_storage_historical_read` instead: ```diff - let value = read_storage(); - prove_public_value_inclusion(value, storage_slot, contract_address, context); -+ let value = storage_historical_read(storage_slot, contract_address, context); ++ let value = public_storage_historical_read(storage_slot, contract_address, context); ``` -If you were proving historical existence of a value you got via some other constrained means, perform an assertion against the return value of `storage_historical_read` instead: +If you were proving historical existence of a value you got via some other constrained means, perform an assertion against the return value of `public_storage_historical_read` instead: ```diff - prove_public_value_inclusion(value, storage_slot, contract_address, context); -+ assert(storage_historical_read(storage_slot, contract_address, context) == value); ++ assert(public_storage_historical_read(storage_slot, contract_address, context) == value); ``` ## 0.30.0 diff --git a/noir-projects/aztec-nr/aztec/src/history.nr b/noir-projects/aztec-nr/aztec/src/history.nr index 095d8fa6f88..9a50fd95bdb 100644 --- a/noir-projects/aztec-nr/aztec/src/history.nr +++ b/noir-projects/aztec-nr/aztec/src/history.nr @@ -3,4 +3,4 @@ mod note_inclusion; mod note_validity; mod nullifier_inclusion; mod nullifier_non_inclusion; -mod storage; +mod public_storage; diff --git a/noir-projects/aztec-nr/aztec/src/history/storage.nr b/noir-projects/aztec-nr/aztec/src/history/public_storage.nr similarity index 79% rename from noir-projects/aztec-nr/aztec/src/history/storage.nr rename to noir-projects/aztec-nr/aztec/src/history/public_storage.nr index 91b1a16f885..ff1fe6c01df 100644 --- a/noir-projects/aztec-nr/aztec/src/history/storage.nr +++ b/noir-projects/aztec-nr/aztec/src/history/public_storage.nr @@ -6,7 +6,7 @@ use dep::std::merkle::compute_merkle_root; use crate::{context::PrivateContext, oracle::get_public_data_witness::get_public_data_witness}; -fn _storage_historical_read(storage_slot: Field, contract_address: AztecAddress, header: Header) -> Field { +fn _public_storage_historical_read(storage_slot: Field, contract_address: AztecAddress, header: Header) -> Field { // 1) Compute the leaf slot by siloing the storage slot with the contract address let public_value_leaf_slot = pedersen_hash( [contract_address.to_field(), storage_slot], @@ -47,21 +47,21 @@ fn _storage_historical_read(storage_slot: Field, contract_address: AztecAddress, value } -pub fn storage_historical_read( +pub fn public_storage_historical_read( + context: PrivateContext, storage_slot: Field, // The storage slot to read - contract_address: AztecAddress, // The contract we want to look into - context: PrivateContext + contract_address: AztecAddress // The contract we want to look into ) -> Field { - _storage_historical_read(storage_slot, contract_address, context.historical_header) + _public_storage_historical_read(storage_slot, contract_address, context.historical_header) } -pub fn storage_historical_read_at( +pub fn public_storage_historical_read_at( + context: PrivateContext, storage_slot: Field, // The storage slot to read contract_address: AztecAddress, // The contract we want to look into - block_number: u32, // The block number at the end of which we'll read the value - context: PrivateContext + block_number: u32 // The block number at the end of which we'll read the value ) -> Field { let header = context.get_header_at(block_number); - _storage_historical_read(storage_slot, contract_address, header) + _public_storage_historical_read(storage_slot, contract_address, header) } diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr index 5798ec291a7..33b16603e14 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr @@ -1,5 +1,5 @@ use crate::{ - context::Context, history::storage::storage_historical_read, + context::Context, history::public_storage::storage_historical_read, oracle::{storage::{storage_read, storage_write}}, state_vars::storage::Storage }; use dep::protocol_types::{constants::INITIALIZATION_SLOT_SEPARATOR, traits::{Deserialize, Serialize}}; @@ -58,10 +58,10 @@ impl SharedImmutable { for i in 0..fields.len() { fields[i] = - storage_historical_read( + public_storage_historical_read( + (*private_context), self.storage_slot + i as Field, - (*private_context).this_address(), - (*private_context) + (*private_context).this_address() ); } T::deserialize(fields) diff --git a/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr b/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr index de80d4778bf..d425ad10d51 100644 --- a/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/inclusion_proofs_contract/src/main.nr @@ -20,7 +20,7 @@ contract InclusionProofs { prove_note_is_nullified_at }, nullifier_non_inclusion::{prove_note_not_nullified, prove_note_not_nullified_at}, - storage::{storage_historical_read, storage_historical_read_at} + public_storage::{public_storage_historical_read, public_storage_historical_read_at} }; // docs:end:imports // docs:start:value_note_imports @@ -203,11 +203,11 @@ contract InclusionProofs { fn test_storage_historical_read_unset_slot(block_number: u32 // The block at which we'll read the public storage value ) { assert_eq( - storage_historical_read_at( + public_storage_historical_read_at( + context, storage.public_unused_value.storage_slot, context.this_address(), - block_number, - context + block_number ), 0 ); } @@ -219,17 +219,17 @@ contract InclusionProofs { block_number: u32 // The block at which we'll read the public storage value ) { let actual = if (use_block_number) { - storage_historical_read_at( + public_storage_historical_read_at( + context, storage.public_value.storage_slot, context.this_address(), - block_number, - context + block_number ) } else { - storage_historical_read( + public_storage_historical_read( + context, storage.public_value.storage_slot, - context.this_address(), - context + context.this_address() ) }; From bea5055a2fe020b0f515e14be49375e44ac6af6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Venturo?= Date: Fri, 22 Mar 2024 17:35:16 +0000 Subject: [PATCH 7/7] Fix import --- noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr index 33b16603e14..a44a605c3fd 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr @@ -1,5 +1,5 @@ use crate::{ - context::Context, history::public_storage::storage_historical_read, + context::Context, history::public_storage::public_storage_historical_read, oracle::{storage::{storage_read, storage_write}}, state_vars::storage::Storage }; use dep::protocol_types::{constants::INITIALIZATION_SLOT_SEPARATOR, traits::{Deserialize, Serialize}};