diff --git a/docs/docs/developers/contracts/references/history_lib_reference.md b/docs/docs/developers/contracts/references/history_lib_reference.md index 5928a54275b5..cc592a6760ab 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 +## Public 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 `public_storage_historical_read`). -### prove_public_value_inclusion +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_at` takes 4 parameters: +### public_storage_historical_read -| 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 | +`public_storage_historical_read_at` takes 4 parameters: -`prove_public_value_inclusion` takes 3 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 | -| 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 | +`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 | ## 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/docs/docs/misc/migration_notes.md b/docs/docs/misc/migration_notes.md index 1e1af122f26d..f3ae905b012a 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::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 `public_storage_historical_read` instead: + +```diff +- let value = read_storage(); +- prove_public_value_inclusion(value, 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 `public_storage_historical_read` instead: + +```diff +- prove_public_value_inclusion(value, storage_slot, contract_address, context); ++ assert(public_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 { diff --git a/noir-projects/aztec-nr/aztec/src/history.nr b/noir-projects/aztec-nr/aztec/src/history.nr index 4fe5f6281cf0..9a50fd95bdbc 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 public_storage; diff --git a/noir-projects/aztec-nr/aztec/src/history/public_storage.nr b/noir-projects/aztec-nr/aztec/src/history/public_storage.nr new file mode 100644 index 000000000000..ff1fe6c01df6 --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/history/public_storage.nr @@ -0,0 +1,67 @@ +use dep::protocol_types::{ + 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}; + +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], + GENERATOR_INDEX__PUBLIC_LEAF_INDEX + ); + + // 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 + ); + + // 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` + // 1. The value is the same as the one in the witness + // 2. The value was never initialized and is zero + let is_less_than_slot = full_field_less_than(preimage.slot, public_value_leaf_slot); + let is_next_greater_than = full_field_less_than(public_value_leaf_slot, preimage.next_slot); + 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); + + let value = if is_in_range { + 0 + } else { + assert_eq(preimage.slot, public_value_leaf_slot, "Public data slot doesn't match witness"); + preimage.value + }; + + // 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" + ); + + value +} + +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 +) -> Field { + _public_storage_historical_read(storage_slot, contract_address, context.historical_header) +} + +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 +) -> Field { + let header = context.get_header_at(block_number); + + _public_storage_historical_read(storage_slot, contract_address, header) +} 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 deleted file mode 100644 index 47dbae54b6d7..000000000000 --- a/noir-projects/aztec-nr/aztec/src/history/public_value_inclusion.nr +++ /dev/null @@ -1,78 +0,0 @@ -use dep::protocol_types::{ - 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}}; - -fn _public_value_inclusion( - value: Field, - storage_slot: Field, - contract_address: AztecAddress, - header: Header -) { - // 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 - 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 - let preimage = witness.leaf_preimage; - - // Here we have two cases. Code based on same checks in `validate_public_data_reads` in `base_rollup_inputs` - // 1. The value is the same as the one in the witness - // 2. The value was never initialized and is zero - let is_less_than_slot = full_field_less_than(preimage.slot, public_value_leaf_slot); - let is_next_greater_than = full_field_less_than(public_value_leaf_slot, preimage.next_slot); - 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"); - } 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"); - } - - // 5) Prove that the leaf we validated is in the public data tree - 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. -} - -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 - ); -} - -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 -) { - let header = context.get_header_at(block_number); - - _public_value_inclusion(value, 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 bdcaeaae60e0..a44a605c3fd9 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::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}}; @@ -54,20 +54,15 @@ 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] = + public_storage_historical_read( + (*private_context), + self.storage_slot + i as Field, + (*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 9fc57911d51b..d425ad10d516 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} + public_storage::{public_storage_historical_read, public_storage_historical_read_at} }; // docs:end:imports // docs:start:value_note_imports @@ -200,39 +200,40 @@ 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 ) { - prove_public_value_inclusion_at( - 0, - storage.public_unused_value.storage_slot, - context.this_address(), - block_number, - context + assert_eq( + public_storage_historical_read_at( + context, + storage.public_unused_value.storage_slot, + context.this_address(), + block_number + ), 0 ); } #[aztec(private)] - fn test_public_value_inclusion( - public_value: Field, + 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 ) { - if (use_block_number) { - prove_public_value_inclusion_at( - public_value, + let actual = if (use_block_number) { + public_storage_historical_read_at( + context, storage.public_value.storage_slot, context.this_address(), - block_number, - context - ); + block_number + ) } else { - prove_public_value_inclusion( - public_value, + public_storage_historical_read( + context, storage.public_value.storage_slot, - context.this_address(), - context - ); - } + context.this_address() + ) + }; + + 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 fcb4959f8fc6..850b349fa58a 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('Public value does not match the witness'); - await expect( - contract.methods.test_public_value_inclusion(randomPublicValue, false, 0n).send().wait(), - ).rejects.toThrow('Public value does not match the witness'); + 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 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) + INITIAL_L2_BLOCK_NUM; + }; });