From 6abceb9d1e3a6c3682172a09be109e6d1c5b32aa Mon Sep 17 00:00:00 2001 From: sklppy88 Date: Sun, 14 Apr 2024 11:49:45 +0000 Subject: [PATCH 1/2] initial fix --- .../aztec/src/state_vars/shared_mutable.nr | 2 + .../shared_mutable_private_getter.nr | 68 +++++++++++++++++++ noir-projects/noir-contracts/Nargo.toml | 1 + .../contracts/state_vars_contract/Nargo.toml | 9 +++ .../contracts/state_vars_contract/src/main.nr | 26 +++++++ .../end-to-end/src/e2e_state_vars.test.ts | 36 +++++++++- 6 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable_private_getter.nr create mode 100644 noir-projects/noir-contracts/contracts/state_vars_contract/Nargo.toml create mode 100644 noir-projects/noir-contracts/contracts/state_vars_contract/src/main.nr diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable.nr index fa61113bc78..533639390d8 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable.nr @@ -1,4 +1,6 @@ mod shared_mutable; mod scheduled_value_change; +mod shared_mutable_private_getter; use shared_mutable::SharedMutable; +use shared_mutable_private_getter::SharedMutablePrivateGetter; diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable_private_getter.nr b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable_private_getter.nr new file mode 100644 index 00000000000..ac49f207c85 --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable_private_getter.nr @@ -0,0 +1,68 @@ +use dep::protocol_types::{hash::pedersen_hash, traits::FromField, address::AztecAddress}; + +use crate::context::{PrivateContext, Context}; +use crate::history::public_storage::public_storage_historical_read; +use crate::public_storage; +use crate::state_vars::{storage::Storage, shared_mutable::scheduled_value_change::ScheduledValueChange}; + +struct SharedMutablePrivateGetter { + context: PrivateContext, + // The contract address of the contract we want to read from + contract_address: AztecAddress, + // The storage slot where the SharedMutable is stored on the other contract + storage_slot: Field, +} + +impl SharedMutablePrivateGetter { + pub fn new( + context: PrivateContext, + contract_address: AztecAddress, + storage_slot: Field, + ) -> Self { + assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); + assert(contract_address.to_field() != 0, "Contract address cannot be 0"); + Self { context, contract_address, storage_slot } + } + + pub fn get_current_value_in_private(self) -> T where T: FromField { + let mut context = self.context; + + let (scheduled_value_change, historical_block_number) = self.historical_read_from_public_storage(context); + let block_horizon = scheduled_value_change.get_block_horizon(historical_block_number); + + // We prevent this transaction from being included in any block after the block horizon, ensuring that the + // historical public value matches the current one, since it can only change after the horizon. + context.set_tx_max_block_number(block_horizon); + scheduled_value_change.get_current_at(historical_block_number) + } + + fn historical_read_from_public_storage( + self, + context: PrivateContext + ) -> (ScheduledValueChange, u32) where T: FromField { + let derived_slot = self.get_derived_storage_slot(); + + // Ideally the following would be simply public_storage::read_historical, but we can't implement that yet. + let mut raw_fields = [0; 3]; + for i in 0..3 { + raw_fields[i] = public_storage_historical_read( + context, + derived_slot + i as Field, + self.contract_address + ); + } + + let scheduled_value: ScheduledValueChange = ScheduledValueChange::deserialize(raw_fields); + let historical_block_number = context.historical_header.global_variables.block_number as u32; + + (scheduled_value, historical_block_number) + } + + fn get_derived_storage_slot(self) -> Field { + // Since we're actually storing three values (a ScheduledValueChange struct), we hash the storage slot to get a + // unique location in which we can safely store as much data as we need. This could be removed if we informed + // the slot allocator of how much space we need so that proper padding could be added. + // See https://github.com/AztecProtocol/aztec-packages/issues/5492 + pedersen_hash([self.storage_slot, 0], 0) + } +} diff --git a/noir-projects/noir-contracts/Nargo.toml b/noir-projects/noir-contracts/Nargo.toml index b3d1c57c752..7cf2286b66e 100644 --- a/noir-projects/noir-contracts/Nargo.toml +++ b/noir-projects/noir-contracts/Nargo.toml @@ -34,6 +34,7 @@ members = [ "contracts/schnorr_hardcoded_account_contract", "contracts/schnorr_single_key_account_contract", "contracts/slow_tree_contract", + "contracts/state_vars_contract", "contracts/stateful_test_contract", "contracts/test_contract", "contracts/token_contract", diff --git a/noir-projects/noir-contracts/contracts/state_vars_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/state_vars_contract/Nargo.toml new file mode 100644 index 00000000000..7bcf33b2ecf --- /dev/null +++ b/noir-projects/noir-contracts/contracts/state_vars_contract/Nargo.toml @@ -0,0 +1,9 @@ +[package] +name = "state_vars_contract" +authors = [""] +compiler_version = ">=0.25.0" +type = "contract" + +[dependencies] +aztec = { path = "../../../aztec-nr/aztec" } +authwit = { path = "../../../aztec-nr/authwit" } diff --git a/noir-projects/noir-contracts/contracts/state_vars_contract/src/main.nr b/noir-projects/noir-contracts/contracts/state_vars_contract/src/main.nr new file mode 100644 index 00000000000..7cbf988006b --- /dev/null +++ b/noir-projects/noir-contracts/contracts/state_vars_contract/src/main.nr @@ -0,0 +1,26 @@ +contract StateVars { + use dep::aztec::prelude::{ + AztecAddress, FunctionSelector, NoteHeader, NoteGetterOptions, NoteViewerOptions, + PrivateContext, Map, PublicMutable, PublicImmutable, PrivateMutable, PrivateImmutable, + PrivateSet, SharedImmutable + }; + use dep::aztec::protocol_types::traits::{Deserialize, Serialize}; + + use dep::aztec::{ + state_vars::shared_mutable::SharedMutablePrivateGetter, + context::{PublicContext, Context}, + log::{emit_unencrypted_log_from_private}, + }; + + #[aztec(private)] + fn test_shared_mutable_private_getter( + contract_address_to_read: AztecAddress, + storage_slot_of_shared_mutable: Field, + ) { + // It's a bit wonky because we need to know the delay for get_current_value_in_private to work correctly + let test: SharedMutablePrivateGetter = SharedMutablePrivateGetter::new(context, contract_address_to_read, storage_slot_of_shared_mutable); + let authorized = test.get_current_value_in_private(); + + emit_unencrypted_log_from_private(&mut context, authorized); + } +} diff --git a/yarn-project/end-to-end/src/e2e_state_vars.test.ts b/yarn-project/end-to-end/src/e2e_state_vars.test.ts index 56783ceee2e..8b32a070774 100644 --- a/yarn-project/end-to-end/src/e2e_state_vars.test.ts +++ b/yarn-project/end-to-end/src/e2e_state_vars.test.ts @@ -1,5 +1,5 @@ -import { type Wallet } from '@aztec/aztec.js'; -import { DocsExampleContract } from '@aztec/noir-contracts.js'; +import { AztecAddress, Fr, type PXE, type Wallet } from '@aztec/aztec.js'; +import { AuthContract, DocsExampleContract, StateVarsContract } from '@aztec/noir-contracts.js'; import { jest } from '@jest/globals'; @@ -8,6 +8,7 @@ import { setup } from './fixtures/utils.js'; const TIMEOUT = 100_000; describe('e2e_state_vars', () => { + let pxe: PXE; jest.setTimeout(TIMEOUT); let wallet: Wallet; @@ -19,7 +20,7 @@ describe('e2e_state_vars', () => { const RANDOMNESS = 2n; beforeAll(async () => { - ({ teardown, wallet } = await setup()); + ({ teardown, wallet, pxe} = await setup(2)); contract = await DocsExampleContract.deploy(wallet).send().deployed(); }, 30_000); @@ -217,4 +218,33 @@ describe('e2e_state_vars', () => { expect(randomness).toEqual(RANDOMNESS); }); }); + + describe('SharedMutablePrivateGetter', () => { + let authContract: AuthContract; + let stateVarsContract: StateVarsContract; + + const delay = async (blocks: number) => { + for (let i = 0; i < blocks; i++) { + await authContract.methods.get_authorized().send().wait(); + } + }; + + beforeAll(async () => { + stateVarsContract = await StateVarsContract.deploy(wallet).send().deployed(); + authContract = await AuthContract.deploy(wallet, wallet.getAddress()).send().deployed(); + }, 30_000); + + it('should set authorized in auth contract', async () => { + await authContract.withWallet(wallet).methods.set_authorized(AztecAddress.fromField(new Fr(6969696969))).send().wait(); + }); + + it('checks authorized from auth contract from state vars contract', async () => { + await delay(5); + + const { txHash } = await stateVarsContract.methods.test_shared_mutable_private_getter(authContract.address, 2).send().wait(); + + const rawLogs = await pxe.getUnencryptedLogs({ txHash }); + expect(Fr.fromBuffer(rawLogs.logs[0].log.data)).toEqual(new Fr(6969696969)); + }); + }); }); From e0c137c2335b3b992de2efffd20d29392553c6b8 Mon Sep 17 00:00:00 2001 From: sklppy88 Date: Mon, 15 Apr 2024 15:38:32 +0000 Subject: [PATCH 2/2] yarn fmt --- .../end-to-end/src/e2e_state_vars.test.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/yarn-project/end-to-end/src/e2e_state_vars.test.ts b/yarn-project/end-to-end/src/e2e_state_vars.test.ts index 8b32a070774..03ca49a0771 100644 --- a/yarn-project/end-to-end/src/e2e_state_vars.test.ts +++ b/yarn-project/end-to-end/src/e2e_state_vars.test.ts @@ -20,7 +20,7 @@ describe('e2e_state_vars', () => { const RANDOMNESS = 2n; beforeAll(async () => { - ({ teardown, wallet, pxe} = await setup(2)); + ({ teardown, wallet, pxe } = await setup(2)); contract = await DocsExampleContract.deploy(wallet).send().deployed(); }, 30_000); @@ -228,20 +228,27 @@ describe('e2e_state_vars', () => { await authContract.methods.get_authorized().send().wait(); } }; - + beforeAll(async () => { stateVarsContract = await StateVarsContract.deploy(wallet).send().deployed(); authContract = await AuthContract.deploy(wallet, wallet.getAddress()).send().deployed(); }, 30_000); it('should set authorized in auth contract', async () => { - await authContract.withWallet(wallet).methods.set_authorized(AztecAddress.fromField(new Fr(6969696969))).send().wait(); + await authContract + .withWallet(wallet) + .methods.set_authorized(AztecAddress.fromField(new Fr(6969696969))) + .send() + .wait(); }); it('checks authorized from auth contract from state vars contract', async () => { await delay(5); - const { txHash } = await stateVarsContract.methods.test_shared_mutable_private_getter(authContract.address, 2).send().wait(); + const { txHash } = await stateVarsContract.methods + .test_shared_mutable_private_getter(authContract.address, 2) + .send() + .wait(); const rawLogs = await pxe.getUnencryptedLogs({ txHash }); expect(Fr.fromBuffer(rawLogs.logs[0].log.data)).toEqual(new Fr(6969696969));