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..2f4f4339481 --- /dev/null +++ b/noir-projects/aztec-nr/aztec/src/state_vars/shared_mutable/shared_mutable_private_getter.nr @@ -0,0 +1,70 @@ +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 + other_contract_address: AztecAddress, + // The storage slot where the SharedMutable is stored on the other contract + storage_slot: Field, +} + +// We have this as a view-only interface to reading Shared Mutables in other contracts. +// Currently the Shared Mutable does not support this. We can adapt SharedMutable at a later date +impl SharedMutablePrivateGetter { + pub fn new( + context: PrivateContext, + other_contract_address: AztecAddress, + storage_slot: Field, + ) -> Self { + assert(storage_slot != 0, "Storage slot 0 not allowed. Storage slots must start from 1."); + assert(other_contract_address.to_field() != 0, "Other contract address cannot be 0"); + Self { context, other_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.other_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 73e8bb9295e..b3d1c57c752 100644 --- a/noir-projects/noir-contracts/Nargo.toml +++ b/noir-projects/noir-contracts/Nargo.toml @@ -24,6 +24,7 @@ members = [ "contracts/escrow_contract", "contracts/gas_token_contract", "contracts/import_test_contract", + "contracts/key_registry_contract", "contracts/inclusion_proofs_contract", "contracts/lending_contract", "contracts/parent_contract", diff --git a/noir-projects/noir-contracts/contracts/key_registry_contract/Nargo.toml b/noir-projects/noir-contracts/contracts/key_registry_contract/Nargo.toml new file mode 100644 index 00000000000..cae3a7ead7e --- /dev/null +++ b/noir-projects/noir-contracts/contracts/key_registry_contract/Nargo.toml @@ -0,0 +1,9 @@ +[package] +name = "key_registry_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/key_registry_contract/src/main.nr b/noir-projects/noir-contracts/contracts/key_registry_contract/src/main.nr new file mode 100644 index 00000000000..8f2810f3dbf --- /dev/null +++ b/noir-projects/noir-contracts/contracts/key_registry_contract/src/main.nr @@ -0,0 +1,118 @@ +contract KeyRegistry { + use dep::std::hash::poseidon2::Poseidon2::hash as poseidon2_hash; + use dep::authwit::auth::assert_current_call_valid_authwit_public; + + use dep::aztec::{ + context::gas::GasOpts, + state_vars::{ + SharedMutable, + Map + }, + protocol_types::{ + abis::function_selector::FunctionSelector, + contract_class_id::ContractClassId, + address::{ + AztecAddress, + EthAddress, + PublicKeysHash, + PartialAddress, + }, + constants::{ + GENERATOR_INDEX__CONTRACT_ADDRESS_V1, + GENERATOR_INDEX__PUBLIC_KEYS_HASH + }, + traits::{ + Serialize, + Deserialize, + } + }, + }; + + global KEY_ROTATION_DELAY = 5; + + #[aztec(storage)] + struct Storage { + // We are not supporting rotating / changing keys other than the nullifier public in the registry at the moment, but will in the future. + // Uncomment lines below to enable that functionality + nullifier_public_key_registry: Map>, + // incoming_public_key_registry: Map>, + // outgoing_public_key_registry: Map>, + // tagging_public_key_registry: Map>, + } + + #[aztec(public)] + fn rotate_nullifier_public_key( + address: AztecAddress, + new_nullifier_public_key: Field, + ) { + assert( + new_nullifier_public_key != 0, + "New nullifier public key must be non-zero" + ); + + if (!address.eq(context.msg_sender())) { + assert_current_call_valid_authwit_public(&mut context, address); + } + + let nullifier_key_registry = storage.nullifier_public_key_registry.at(address); + + nullifier_key_registry.schedule_value_change(new_nullifier_public_key); + } + + #[aztec(public)] + fn register( + address: AztecAddress, + partial_address: PartialAddress, + nullifier_public_key: Field, + incoming_public_key: Field, + outgoing_public_key: Field, + tagging_public_key: Field, + ) { + assert( + (nullifier_public_key != 0) & + (incoming_public_key != 0) & + (outgoing_public_key != 0) & + (tagging_public_key != 0), + "All public keys must be non-zero" + ); + + // TODO (ek): Do it below after refactoring all public_keys_hash_elemtns + // let public_keys_hash = PublicKeysHash::compute(nullifier_public_key, tagging_public_key, incoming_public_key, outgoing_public_key); + // let address = AztecAddress::compute(public_keys_hash, partial_address); + // We could also pass in original_public_keys_hash instead of computing it here, if all we need the original one is for being able to prove ownership of address + let public_keys_hash = poseidon2_hash([ + nullifier_public_key, + incoming_public_key, + outgoing_public_key, + tagging_public_key, + GENERATOR_INDEX__PUBLIC_KEYS_HASH, + ], + 5 + ); + + let computed_address = AztecAddress::from_field( + poseidon2_hash([ + partial_address.to_field(), + public_keys_hash.to_field(), + GENERATOR_INDEX__CONTRACT_ADDRESS_V1 as Field, + ], + 3 + ) + ); + + assert(computed_address.eq(address), "Computed address does not match supplied address"); + + let nullifier_key_registry = storage.nullifier_public_key_registry.at(address); + // We are not supporting rotating / changing keys other than the nullifier public in the registry at the moment, but will in the future. + // Uncomment lines below to enable that functionality + // let incoming_key_registry = storage.incoming_public_key_registry.at(address); + // let outgoing_key_registry = storage.outgoing_public_key_registry.at(address); + // let tagging_key_registry = storage.taggin_public_key_registry.at(address); + + nullifier_key_registry.schedule_value_change(nullifier_public_key); + // We are not supporting rotating / changing keys other than the nullifier public in the registry at the moment, but will in the future. + // Uncomment lines below to enable that functionality // incoming_key_registry.schedule_value_change(new_incoming_public_key); + // outgoing_key_registry.schedule_value_change(new_outgoing_public_key); + // tagging_key_registry.schedule_value_change(new_tagging_public_key); + } +} diff --git a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr index c8ea069d730..92e8ad6490c 100644 --- a/noir-projects/noir-contracts/contracts/test_contract/src/main.nr +++ b/noir-projects/noir-contracts/contracts/test_contract/src/main.nr @@ -9,6 +9,9 @@ contract Test { abis::private_circuit_public_inputs::PrivateCircuitPublicInputs, constants::{MAX_NOTE_HASH_READ_REQUESTS_PER_CALL, MAX_NOTES_PER_PAGE}, traits::Serialize }; + + use dep::aztec::state_vars::shared_mutable::SharedMutablePrivateGetter; + // docs:start:unencrypted_import use dep::aztec::prelude::emit_unencrypted_log; // docs:end:unencrypted_import @@ -387,6 +390,42 @@ contract Test { constant.value } + #[aztec(private)] + fn test_shared_mutable_private_getter_for_registry_contract( + contract_address_to_read: AztecAddress, + storage_slot_of_shared_mutable: Field, + address_to_get_in_registry: AztecAddress, + ) { + // We have to derive this slot to get the location of the shared mutable inside the Map + let derived_slot = dep::aztec::hash::pedersen_hash([storage_slot_of_shared_mutable, address_to_get_in_registry.to_field()], 0); + // It's a bit wonky because we need to know the delay for get_current_value_in_private to work correctly + let registry_private_getter: SharedMutablePrivateGetter = SharedMutablePrivateGetter::new(context, contract_address_to_read, derived_slot); + let nullifier_public_key = registry_private_getter.get_current_value_in_private(); + + emit_unencrypted_log_from_private(&mut context, nullifier_public_key); + } + + #[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); + } + + #[aztec(public)] + fn delay() { + // We use this as a util function to "mine a block" + dep::aztec::log::emit_unencrypted_log( + &mut context, + "dummy" + ); + } + // Purely exists for testing unconstrained fn get_random(kinda_seed: Field) -> pub Field { kinda_seed * unsafe_rand() diff --git a/noir-projects/noir-protocol-circuits/crates/types/src/address/partial_address.nr b/noir-projects/noir-protocol-circuits/crates/types/src/address/partial_address.nr index df67b7f6dc2..359fae794ba 100644 --- a/noir-projects/noir-protocol-circuits/crates/types/src/address/partial_address.nr +++ b/noir-projects/noir-protocol-circuits/crates/types/src/address/partial_address.nr @@ -4,9 +4,11 @@ use crate::{ aztec_address::AztecAddress }, constants::GENERATOR_INDEX__PARTIAL_ADDRESS, contract_class_id::ContractClassId, - hash::pedersen_hash, traits::ToField + hash::pedersen_hash, traits::{ToField, FromField, Serialize, Deserialize} }; +global PARTIAL_ADDRESS_LENGTH = 1; + // Partial address struct PartialAddress { inner : Field @@ -18,6 +20,18 @@ impl ToField for PartialAddress { } } +impl Serialize for PartialAddress { + fn serialize(self: Self) -> [Field; PARTIAL_ADDRESS_LENGTH] { + [self.to_field()] + } +} + +impl Deserialize for PartialAddress { + fn deserialize(fields: [Field; PARTIAL_ADDRESS_LENGTH]) -> Self { + PartialAddress { inner: fields[0] } + } +} + impl PartialAddress { pub fn from_field(field: Field) -> Self { Self { inner: field } diff --git a/yarn-project/end-to-end/src/composed/cli_docs_sandbox.test.ts b/yarn-project/end-to-end/src/composed/cli_docs_sandbox.test.ts index 3b0a8e20400..446330a461e 100644 --- a/yarn-project/end-to-end/src/composed/cli_docs_sandbox.test.ts +++ b/yarn-project/end-to-end/src/composed/cli_docs_sandbox.test.ts @@ -117,6 +117,7 @@ FPCContractArtifact GasTokenContractArtifact ImportTestContractArtifact InclusionProofsContractArtifact +KeyRegistryContractArtifact LendingContractArtifact MultiCallEntrypointContractArtifact ParentContractArtifact diff --git a/yarn-project/end-to-end/src/e2e_key_registry.test.ts b/yarn-project/end-to-end/src/e2e_key_registry.test.ts new file mode 100644 index 00000000000..37f6ac8cea5 --- /dev/null +++ b/yarn-project/end-to-end/src/e2e_key_registry.test.ts @@ -0,0 +1,261 @@ +import { type AccountWallet, AztecAddress, Fr, type PXE } from '@aztec/aztec.js'; +import { GeneratorIndex } from '@aztec/circuits.js'; +import { poseidon2Hash } from '@aztec/foundation/crypto'; +import { KeyRegistryContract, TestContract } from '@aztec/noir-contracts.js'; + +import { jest } from '@jest/globals'; + +import { publicDeployAccounts, setup } from './fixtures/utils.js'; + +const TIMEOUT = 100_000; + +describe('SharedMutablePrivateGetter', () => { + let keyRegistry: KeyRegistryContract; + let testContract: TestContract; + let pxe: PXE; + jest.setTimeout(TIMEOUT); + + let wallets: AccountWallet[]; + + let teardown: () => Promise; + + beforeAll(async () => { + ({ teardown, pxe, wallets } = await setup(2)); + testContract = await TestContract.deploy(wallets[0]).send().deployed(); + keyRegistry = await KeyRegistryContract.deploy(wallets[0]).send().deployed(); + + await publicDeployAccounts(wallets[0], wallets.slice(0, 2)); + }, 120_000); + + const delay = async (blocks: number) => { + for (let i = 0; i < blocks; i++) { + await testContract.methods.delay().send().wait(); + } + }; + + afterAll(() => teardown()); + + describe('failure cases', () => { + let accountAddedToRegistry: AztecAddress; + + describe('should fail registering with bad input', () => { + const partialAddress = new Fr(69); + + const masterNullifierPublicKey = new Fr(12); + const masterIncomingViewingPublicKey = new Fr(34); + const masterOutgoingViewingPublicKey = new Fr(56); + const masterTaggingPublicKey = new Fr(78); + + // TODO(#5726): use computePublicKeysHash function + const publicKeysHash = poseidon2Hash([ + masterNullifierPublicKey, + masterIncomingViewingPublicKey, + masterOutgoingViewingPublicKey, + masterTaggingPublicKey, + GeneratorIndex.PUBLIC_KEYS_HASH, + ]); + + // We hash the partial address and the public keys hash to get the account address + // TODO(#5726): Move the following line to AztecAddress class? + accountAddedToRegistry = poseidon2Hash([partialAddress, publicKeysHash, GeneratorIndex.CONTRACT_ADDRESS_V1]); + + it('should fail registering with mismatched address', async () => { + const mismatchedAddress = Fr.random(); + + await expect( + keyRegistry + .withWallet(wallets[0]) + .methods.register( + AztecAddress.fromField(mismatchedAddress), + partialAddress, + masterNullifierPublicKey, + masterIncomingViewingPublicKey, + masterOutgoingViewingPublicKey, + masterTaggingPublicKey, + ) + .send() + .wait(), + ).rejects.toThrow('Computed address does not match supplied address'); + }); + + it('should fail registering with mismatched nullifier public key', async () => { + const mismatchedMasterNullifierPublicKey = Fr.random(); + + await expect( + keyRegistry + .withWallet(wallets[0]) + .methods.register( + AztecAddress.fromField(accountAddedToRegistry), + partialAddress, + mismatchedMasterNullifierPublicKey, + masterIncomingViewingPublicKey, + masterOutgoingViewingPublicKey, + masterTaggingPublicKey, + ) + .send() + .wait(), + ).rejects.toThrow('Computed address does not match supplied address'); + }); + }); + + describe('should fail when rotating keys with bad input', () => { + it('should fail when trying to rotate setting a 0 key', async () => { + await expect( + keyRegistry + .withWallet(wallets[0]) + .methods.rotate_nullifier_public_key(wallets[0].getAddress(), new Fr(0)) + .send() + .wait(), + ).rejects.toThrow('New nullifier public key must be non-zero'); + }); + + it('should fail when trying to rotate for another address without authwit', async () => { + await expect( + keyRegistry + .withWallet(wallets[0]) + .methods.rotate_nullifier_public_key(wallets[1].getAddress(), new Fr(2)) + .send() + .wait(), + ).rejects.toThrow('Assertion failed: Message not authorized by account'); + }); + }); + }); + + describe('key registration flow', () => { + let accountAddedToRegistry: AztecAddress; + + it('should generate and register with original keys', async () => { + const partialAddress = new Fr(69); + + const masterNullifierPublicKey = new Fr(12); + const masterIncomingViewingPublicKey = new Fr(34); + const masterOutgoingViewingPublicKey = new Fr(56); + const masterTaggingPublicKey = new Fr(78); + + const publicKeysHash = poseidon2Hash([ + masterNullifierPublicKey, + masterIncomingViewingPublicKey, + masterOutgoingViewingPublicKey, + masterTaggingPublicKey, + GeneratorIndex.PUBLIC_KEYS_HASH, + ]); + + // We hash the partial address and the public keys hash to get the account address + // TODO(#5726): Move the following line to AztecAddress class? + accountAddedToRegistry = poseidon2Hash([partialAddress, publicKeysHash, GeneratorIndex.CONTRACT_ADDRESS_V1]); + + await keyRegistry + .withWallet(wallets[0]) + .methods.register( + AztecAddress.fromField(accountAddedToRegistry), + partialAddress, + masterNullifierPublicKey, + masterIncomingViewingPublicKey, + masterOutgoingViewingPublicKey, + masterTaggingPublicKey, + ) + .send() + .wait(); + }); + + it('checks our registry contract from test contract and fails because the address has not been registered yet', async () => { + const { txHash } = await testContract.methods + .test_shared_mutable_private_getter_for_registry_contract(keyRegistry.address, 1, accountAddedToRegistry) + .send() + .wait(); + + const rawLogs = await pxe.getUnencryptedLogs({ txHash }); + expect(Fr.fromBuffer(rawLogs.logs[0].log.data)).toEqual(Fr.ZERO); + }); + + it('checks our registry contract from test contract and finds the address and associated nullifier public key after a delay', async () => { + await delay(5); + + const { txHash } = await testContract.methods + .test_shared_mutable_private_getter_for_registry_contract(keyRegistry.address, 1, accountAddedToRegistry) + .send() + .wait(); + + const rawLogs = await pxe.getUnencryptedLogs({ txHash }); + + expect(Fr.fromBuffer(rawLogs.logs[0].log.data)).toEqual(new Fr(12)); + }); + }); + + describe('key rotation flow', () => { + it('we rotate the nullifier key', async () => { + // This changes + const newMasterNullifierPublicKey = new Fr(910); + + await keyRegistry + .withWallet(wallets[0]) + .methods.rotate_nullifier_public_key(wallets[0].getAddress(), newMasterNullifierPublicKey) + .send() + .wait(); + }); + + it("checks our registry contract from test contract and finds our old public key because the key rotation hasn't been applied yet", async () => { + const { txHash } = await testContract.methods + .test_shared_mutable_private_getter_for_registry_contract(keyRegistry.address, 1, wallets[0].getAddress()) + .send() + .wait(); + + const rawLogs = await pxe.getUnencryptedLogs({ txHash }); + expect(Fr.fromBuffer(rawLogs.logs[0].log.data)).toEqual(new Fr(0)); + }); + + it('checks our registry contract from test contract and finds the new nullifier public key that has been rotated', async () => { + await delay(5); + + const { txHash } = await testContract.methods + .test_shared_mutable_private_getter_for_registry_contract(keyRegistry.address, 1, wallets[0].getAddress()) + .send() + .wait(); + + const rawLogs = await pxe.getUnencryptedLogs({ txHash }); + + expect(Fr.fromBuffer(rawLogs.logs[0].log.data)).toEqual(new Fr(910)); + }); + }); + + describe('key rotation flow with authwit', () => { + it('wallet 0 lets wallet 1 call rotate_nullifier_public_key on his behalf with a pre-defined new public key', async () => { + // This changes + const newMasterNullifierPublicKey = new Fr(420); + + const action = keyRegistry + .withWallet(wallets[1]) + .methods.rotate_nullifier_public_key(wallets[0].getAddress(), newMasterNullifierPublicKey); + + await wallets[0] + .setPublicAuthWit({ caller: wallets[1].getCompleteAddress().address, action }, true) + .send() + .wait(); + + await action.send().wait(); + }); + + it("checks our registry contract from test contract and finds our old public key because the key rotation hasn't been applied yet", async () => { + const { txHash } = await testContract.methods + .test_shared_mutable_private_getter_for_registry_contract(keyRegistry.address, 1, wallets[0].getAddress()) + .send() + .wait(); + + const rawLogs = await pxe.getUnencryptedLogs({ txHash }); + expect(Fr.fromBuffer(rawLogs.logs[0].log.data)).toEqual(new Fr(910)); + }); + + it('checks our registry contract from test contract and finds the new nullifier public key that has been rotated', async () => { + await delay(5); + + const { txHash } = await testContract.methods + .test_shared_mutable_private_getter_for_registry_contract(keyRegistry.address, 1, wallets[0].getAddress()) + .send() + .wait(); + + const rawLogs = await pxe.getUnencryptedLogs({ txHash }); + + expect(Fr.fromBuffer(rawLogs.logs[0].log.data)).toEqual(new Fr(420)); + }); + }); +}); 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 f13ee0bd420..8a6ed6dc23e 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, TestContract } 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); @@ -222,4 +223,53 @@ describe('e2e_state_vars', () => { expect(randomness).toEqual(RANDOMNESS); }); }); + + describe('SharedMutablePrivateGetter', () => { + let authContract: AuthContract; + let testContract: TestContract; + + const delay = async (blocks: number) => { + for (let i = 0; i < blocks; i++) { + await authContract.methods.get_authorized().send().wait(); + } + }; + + beforeAll(async () => { + testContract = await TestContract.deploy(wallet).send().deployed(); + // We use the auth contract here because has a nice, clear, simple implementation of the Shared Mutable, + // and we will need to read from it to test our private getter. + authContract = await AuthContract.deploy(wallet, wallet.getAddress()).send().deployed(); + + // We set the authorized value here, knowing there will be some delay before the value change takes place + await authContract + .withWallet(wallet) + .methods.set_authorized(AztecAddress.fromField(new Fr(6969696969))) + .send() + .wait(); + }, 30_000); + + it("checks authorized in auth contract from test contract and finds the old value because the change hasn't been applied yet", async () => { + const { txHash } = await testContract.methods + .test_shared_mutable_private_getter(authContract.address, 2) + .send() + .wait(); + + // The function above emits an unencrypted log as a means of returning the data + const rawLogs = await pxe.getUnencryptedLogs({ txHash }); + expect(Fr.fromBuffer(rawLogs.logs[0].log.data)).toEqual(new Fr(0)); + }); + + it('checks authorized in auth contract from test contract and finds the correctly set value', async () => { + await delay(5); + + const { txHash } = await testContract.methods + .test_shared_mutable_private_getter(authContract.address, 2) + .send() + .wait(); + + // The function above emits an unencrypted log as a means of returning the data + const rawLogs = await pxe.getUnencryptedLogs({ txHash }); + expect(Fr.fromBuffer(rawLogs.logs[0].log.data)).toEqual(new Fr(6969696969)); + }); + }); }); diff --git a/yarn-project/tsconfig.json b/yarn-project/tsconfig.json index 95f7ccc5cee..eafb807f772 100644 --- a/yarn-project/tsconfig.json +++ b/yarn-project/tsconfig.json @@ -16,7 +16,7 @@ "resolveJsonModule": true, "composite": true, "skipLibCheck": true, - "noImplicitOverride": true, + "noImplicitOverride": true }, "references": [ { "path": "accounts/tsconfig.json" },