diff --git a/noir-projects/aztec-nr/aztec/src/context/unconstrained_context.nr b/noir-projects/aztec-nr/aztec/src/context/unconstrained_context.nr index b36691616e01..eedd97aa8d66 100644 --- a/noir-projects/aztec-nr/aztec/src/context/unconstrained_context.nr +++ b/noir-projects/aztec-nr/aztec/src/context/unconstrained_context.nr @@ -1,4 +1,5 @@ -use dep::protocol_types::address::AztecAddress; +use dep::protocol_types::{address::AztecAddress, traits::Deserialize}; +use crate::oracle::storage::{raw_storage_read, storage_read}; struct UnconstrainedContext { block_number: u32, @@ -35,6 +36,14 @@ impl UnconstrainedContext { fn chain_id(self) -> Field { self.chain_id } + + unconstrained fn raw_storage_read(self: Self, storage_slot: Field) -> [Field; N] { + storage_read(self.this_address(), storage_slot, self.block_number()) + } + + unconstrained fn storage_read(self, storage_slot: Field) -> T where T: Deserialize { + T::deserialize(self.raw_storage_read(storage_slot)) + } } #[oracle(getContractAddress)] diff --git a/noir-projects/aztec-nr/aztec/src/oracle/storage.nr b/noir-projects/aztec-nr/aztec/src/oracle/storage.nr index 92925f889fad..b4a6b1f91024 100644 --- a/noir-projects/aztec-nr/aztec/src/oracle/storage.nr +++ b/noir-projects/aztec-nr/aztec/src/oracle/storage.nr @@ -1,42 +1,58 @@ -use dep::protocol_types::traits::Deserialize; +use dep::protocol_types::{address::AztecAddress, traits::Deserialize}; #[oracle(storageRead)] -unconstrained fn storage_read_oracle(storage_slot: Field, length: Field) -> [Field; N] {} - -unconstrained pub fn raw_storage_read(storage_slot: Field) -> [Field; N] { - storage_read_oracle(storage_slot, N) +unconstrained fn storage_read_oracle( + address: Field, + storage_slot: Field, + block_number: Field, + length: Field +) -> [Field; N] {} + +unconstrained pub fn raw_storage_read( + address: AztecAddress, + storage_slot: Field, + block_number: u32 +) -> [Field; N] { + storage_read_oracle(address.to_field(), storage_slot, block_number as Field, N) } -unconstrained pub fn storage_read(storage_slot: Field) -> T where T: Deserialize { - T::deserialize(raw_storage_read(storage_slot)) +unconstrained pub fn storage_read( + address: AztecAddress, + storage_slot: Field, + block_number: u32 +) -> T where T: Deserialize { + T::deserialize(raw_storage_read(address, storage_slot, block_number)) } mod tests { use crate::oracle::storage::{raw_storage_read, storage_read}; + use dep::protocol_types::address::AztecAddress; use std::test::OracleMock; use crate::test::mocks::mock_struct::MockStruct; + global address = AztecAddress::from_field(29); + global slot = 7; + global block_number = 17; + #[test] fn test_raw_storage_read() { - let slot = 7; let written = MockStruct { a: 13, b: 42 }; - let _ = OracleMock::mock("storageRead").with_params((slot, 2)).returns(written.serialize()); + let _ = OracleMock::mock("storageRead").returns(written.serialize()); - let read: [Field; 2] = raw_storage_read(slot); + let read: [Field; 2] = raw_storage_read(address, slot, block_number); assert_eq(read[0], 13); assert_eq(read[1], 42); } #[test] fn test_storage_read() { - let slot = 7; let written = MockStruct { a: 13, b: 42 }; - let _ = OracleMock::mock("storageRead").with_params((slot, 2)).returns(written.serialize()); + let _ = OracleMock::mock("storageRead").returns(written.serialize()); - let read: MockStruct = storage_read(slot); + let read: MockStruct = storage_read(address, slot, block_number); assert_eq(read.a, 13); assert_eq(read.b, 42); } diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable.nr index 32955df75934..a83224630548 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/public_immutable.nr @@ -56,6 +56,6 @@ impl PublicImmutable { impl PublicImmutable { unconstrained pub fn read(self) -> T where T: Deserialize { - storage_read(self.storage_slot) + self.context.storage_read(self.storage_slot) } } diff --git a/noir-projects/aztec-nr/aztec/src/state_vars/public_mutable.nr b/noir-projects/aztec-nr/aztec/src/state_vars/public_mutable.nr index 0d463051717b..07038e14984d 100644 --- a/noir-projects/aztec-nr/aztec/src/state_vars/public_mutable.nr +++ b/noir-projects/aztec-nr/aztec/src/state_vars/public_mutable.nr @@ -41,6 +41,6 @@ impl PublicMutable { impl PublicMutable { unconstrained pub fn read(self) -> T where T: Deserialize { - storage_read(self.storage_slot) + self.context.storage_read(self.storage_slot) } } 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 f71448809d49..a29f5e9c7371 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 @@ -48,7 +48,7 @@ impl SharedImmutable { impl SharedImmutable { unconstrained pub fn read_public(self) -> T where T: Deserialize { - storage_read(self.storage_slot) + self.context.storage_read(self.storage_slot) } } diff --git a/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr b/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr index cb5b51339d6f..5431b94bc108 100644 --- a/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr +++ b/noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr @@ -72,10 +72,11 @@ pub fn setup_and_mint(with_account_contracts: bool) -> (&mut TestEnvironment, Az pub fn check_public_balance(token_contract_address: AztecAddress, address: AztecAddress, address_amount: Field) { let current_contract_address = cheatcodes::get_contract_address(); cheatcodes::set_contract_address(token_contract_address); + let block_number = cheatcodes::get_block_number(); let balances_slot = Token::storage().public_balances.slot; let address_slot = derive_storage_slot_in_map(balances_slot, address); - let amount: U128 = storage_read(address_slot); + let amount: U128 = storage_read(token_contract_address, address_slot, block_number); assert(amount.to_field() == address_amount, "Public balance is not correct"); cheatcodes::set_contract_address(current_contract_address); } diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index 4e1eb36c75d7..b7cbfdb577a0 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -704,10 +704,11 @@ export class AztecNodeService implements AztecNode { * * @param contract - Address of the contract to query. * @param slot - Slot to query. + * @param blockNumber - The block number at which to get the data or 'latest'. * @returns Storage value at the given contract slot. */ - public async getPublicStorageAt(contract: AztecAddress, slot: Fr): Promise { - const committedDb = await this.#getWorldState('latest'); + public async getPublicStorageAt(contract: AztecAddress, slot: Fr, blockNumber: L2BlockNumber): Promise { + const committedDb = await this.#getWorldState(blockNumber); const leafSlot = computePublicDataTreeLeafSlot(contract, slot); const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt()); diff --git a/yarn-project/circuit-types/src/interfaces/aztec-node.ts b/yarn-project/circuit-types/src/interfaces/aztec-node.ts index 0890b0c6904b..a79a28ece159 100644 --- a/yarn-project/circuit-types/src/interfaces/aztec-node.ts +++ b/yarn-project/circuit-types/src/interfaces/aztec-node.ts @@ -291,9 +291,10 @@ export interface AztecNode { * * @param contract - Address of the contract to query. * @param slot - Slot to query. + * @param blockNumber - The block number at which to get the data or 'latest'. * @returns Storage value at the given contract slot. */ - getPublicStorageAt(contract: AztecAddress, slot: Fr): Promise; + getPublicStorageAt(contract: AztecAddress, slot: Fr, blockNumber: L2BlockNumber): Promise; /** * Returns the currently committed block header. diff --git a/yarn-project/end-to-end/src/benchmarks/bench_process_history.test.ts b/yarn-project/end-to-end/src/benchmarks/bench_process_history.test.ts index 0f146badc93f..1932986a768f 100644 --- a/yarn-project/end-to-end/src/benchmarks/bench_process_history.test.ts +++ b/yarn-project/end-to-end/src/benchmarks/bench_process_history.test.ts @@ -54,7 +54,7 @@ describe('benchmarks/process_history', () => { const node = await AztecNodeService.createAndSync(nodeConfig); // call getPublicStorageAt (which calls #getWorldState, which calls #syncWorldState) to force a sync with // world state to ensure the node has caught up - await node.getPublicStorageAt(AztecAddress.random(), Fr.random()); + await node.getPublicStorageAt(AztecAddress.random(), Fr.random(), 'latest'); return node; }); diff --git a/yarn-project/end-to-end/src/benchmarks/bench_publish_rollup.test.ts b/yarn-project/end-to-end/src/benchmarks/bench_publish_rollup.test.ts index b3bb55d64897..67b3a8f1c7f6 100644 --- a/yarn-project/end-to-end/src/benchmarks/bench_publish_rollup.test.ts +++ b/yarn-project/end-to-end/src/benchmarks/bench_publish_rollup.test.ts @@ -37,7 +37,7 @@ describe('benchmarks/publish_rollup', () => { // world state to ensure the node has caught up context.logger.info(`Starting new aztec node`); const node = await AztecNodeService.createAndSync({ ...context.config, disableSequencer: true }); - await node.getPublicStorageAt(AztecAddress.random(), Fr.random()); + await node.getPublicStorageAt(AztecAddress.random(), Fr.random(), 'latest'); // Spin up a new pxe and sync it, we'll use it to test sync times of new accounts for the last block context.logger.info(`Starting new pxe`); diff --git a/yarn-project/noir-contracts.js/package.json b/yarn-project/noir-contracts.js/package.json index 0a86d66706b4..6bf009796dfb 100644 --- a/yarn-project/noir-contracts.js/package.json +++ b/yarn-project/noir-contracts.js/package.json @@ -73,4 +73,4 @@ "engines": { "node": ">=18" } -} \ No newline at end of file +} diff --git a/yarn-project/pxe/src/pxe_service/pxe_service.ts b/yarn-project/pxe/src/pxe_service/pxe_service.ts index e9b76b6d07c0..52b8827e2fa8 100644 --- a/yarn-project/pxe/src/pxe_service/pxe_service.ts +++ b/yarn-project/pxe/src/pxe_service/pxe_service.ts @@ -293,7 +293,7 @@ export class PXEService implements PXE { if (!(await this.getContractInstance(contract))) { throw new Error(`Contract ${contract.toString()} is not deployed`); } - return await this.node.getPublicStorageAt(contract, slot); + return await this.node.getPublicStorageAt(contract, slot, 'latest'); } public async getIncomingNotes(filter: IncomingNotesFilter): Promise { diff --git a/yarn-project/simulator/src/acvm/oracle/oracle.ts b/yarn-project/simulator/src/acvm/oracle/oracle.ts index d2cd8ddf7d1c..b3f04b974293 100644 --- a/yarn-project/simulator/src/acvm/oracle/oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/oracle.ts @@ -287,8 +287,18 @@ export class Oracle { return message.toFields().map(toACVMField); } - async storageRead([startStorageSlot]: ACVMField[], [numberOfElements]: ACVMField[]): Promise { - const values = await this.typedOracle.storageRead(fromACVMField(startStorageSlot), +numberOfElements); + async storageRead( + [contractAddress]: ACVMField[], + [startStorageSlot]: ACVMField[], + [blockNumber]: ACVMField[], + [numberOfElements]: ACVMField[], + ): Promise { + const values = await this.typedOracle.storageRead( + fromACVMField(contractAddress), + fromACVMField(startStorageSlot), + +blockNumber, + +numberOfElements, + ); return values.map(toACVMField); } diff --git a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts index 690ccf8ac868..fdda81ac6ae1 100644 --- a/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts +++ b/yarn-project/simulator/src/acvm/oracle/typed_oracle.ts @@ -190,7 +190,12 @@ export abstract class TypedOracle { throw new OracleMethodNotAvailableError('getL1ToL2MembershipWitness'); } - storageRead(_startStorageSlot: Fr, _numberOfElements: number): Promise { + storageRead( + _contractAddress: Fr, + _startStorageSlot: Fr, + _blockNumber: number, + _numberOfElements: number, + ): Promise { throw new OracleMethodNotAvailableError('storageRead'); } diff --git a/yarn-project/simulator/src/client/client_execution_context.ts b/yarn-project/simulator/src/client/client_execution_context.ts index 7da0e48928de..f8f78336619d 100644 --- a/yarn-project/simulator/src/client/client_execution_context.ts +++ b/yarn-project/simulator/src/client/client_execution_context.ts @@ -678,16 +678,25 @@ export class ClientExecutionContext extends ViewDataOracle { /** * Read the public storage data. + * @param contractAddress - The address to read storage from. * @param startStorageSlot - The starting storage slot. + * @param blockNumber - The block number to read storage at. * @param numberOfElements - Number of elements to read from the starting storage slot. */ - public override async storageRead(startStorageSlot: Fr, numberOfElements: number): Promise { + public override async storageRead( + contractAddress: Fr, + startStorageSlot: Fr, + blockNumber: number, + numberOfElements: number, + ): Promise { const values = []; for (let i = 0n; i < numberOfElements; i++) { const storageSlot = new Fr(startStorageSlot.value + i); - const value = await this.aztecNode.getPublicStorageAt(this.callContext.storageContractAddress, storageSlot); - this.log.debug(`Oracle storage read: slot=${storageSlot.toString()} value=${value}`); + const value = await this.aztecNode.getPublicStorageAt(contractAddress, storageSlot, blockNumber); + this.log.debug( + `Oracle storage read: slot=${storageSlot.toString()} address-${contractAddress.toString()} value=${value}`, + ); values.push(value); } diff --git a/yarn-project/simulator/src/client/view_data_oracle.ts b/yarn-project/simulator/src/client/view_data_oracle.ts index fd1710205dcc..75e7f1802338 100644 --- a/yarn-project/simulator/src/client/view_data_oracle.ts +++ b/yarn-project/simulator/src/client/view_data_oracle.ts @@ -260,16 +260,25 @@ export class ViewDataOracle extends TypedOracle { /** * Read the public storage data. + * @param contractAddress - The address to read storage from. * @param startStorageSlot - The starting storage slot. + * @param blockNumber - The block number to read storage at. * @param numberOfElements - Number of elements to read from the starting storage slot. */ - public override async storageRead(startStorageSlot: Fr, numberOfElements: number) { + public override async storageRead( + contractAddress: Fr, + startStorageSlot: Fr, + blockNumber: number, + numberOfElements: number, + ) { const values = []; for (let i = 0n; i < numberOfElements; i++) { const storageSlot = new Fr(startStorageSlot.value + i); - const value = await this.aztecNode.getPublicStorageAt(this.contractAddress, storageSlot); + const value = await this.aztecNode.getPublicStorageAt(contractAddress, storageSlot, blockNumber); - this.log.debug(`Oracle storage read: slot=${storageSlot.toString()} value=${value}`); + this.log.debug( + `Oracle storage read: slot=${storageSlot.toString()} address-${contractAddress.toString()} value=${value}`, + ); values.push(value); } return values; diff --git a/yarn-project/txe/src/oracle/txe_oracle.ts b/yarn-project/txe/src/oracle/txe_oracle.ts index 567bf0a656dc..eb805c21d08b 100644 --- a/yarn-project/txe/src/oracle/txe_oracle.ts +++ b/yarn-project/txe/src/oracle/txe_oracle.ts @@ -434,13 +434,18 @@ export class TXE implements TypedOracle { throw new Error('Method not implemented.'); } - async storageRead(startStorageSlot: Fr, numberOfElements: number): Promise { + async storageRead( + contractAddress: Fr, + startStorageSlot: Fr, + blockNumber: number, // TODO(#7230): use block number + numberOfElements: number, + ): Promise { const db = this.trees.asLatest(); const values = []; for (let i = 0n; i < numberOfElements; i++) { const storageSlot = startStorageSlot.add(new Fr(i)); - const leafSlot = computePublicDataTreeLeafSlot(this.contractAddress, storageSlot).toBigInt(); + const leafSlot = computePublicDataTreeLeafSlot(contractAddress, storageSlot).toBigInt(); const lowLeafResult = await db.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot); diff --git a/yarn-project/txe/src/txe_service/txe_service.ts b/yarn-project/txe/src/txe_service/txe_service.ts index de25cc812997..bfd99bd74d1b 100644 --- a/yarn-project/txe/src/txe_service/txe_service.ts +++ b/yarn-project/txe/src/txe_service/txe_service.ts @@ -308,9 +308,16 @@ export class TXEService { return toForeignCallResult([]); } - async storageRead(startStorageSlot: ForeignCallSingle, numberOfElements: ForeignCallSingle) { + async storageRead( + contractAddress: ForeignCallSingle, + startStorageSlot: ForeignCallSingle, + blockNumber: ForeignCallSingle, + numberOfElements: ForeignCallSingle, + ) { const values = await this.typedOracle.storageRead( + fromSingle(contractAddress), fromSingle(startStorageSlot), + fromSingle(blockNumber).toNumber(), fromSingle(numberOfElements).toNumber(), ); return toForeignCallResult([toArray(values)]);