Skip to content

Commit 153b201

Browse files
authored
feat!: extend storage read oracle to receive address and block number (#7243)
Follow up to #7237, closes #7230. I only changed the oracles and not the PXE interface to keep this change as small as possible. I did change the node interface, but made it so you can still do it the old way by passing `'latest'`, which I had to do in a couple places. Finally, I added getters for `UnconstrainedContext`, mirroring the work in #7320, which I imagine are the ones we'll use in the vast majority of cases.
1 parent 99ce26f commit 153b201

18 files changed

Lines changed: 110 additions & 37 deletions

File tree

noir-projects/aztec-nr/aztec/src/context/unconstrained_context.nr

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use dep::protocol_types::address::AztecAddress;
1+
use dep::protocol_types::{address::AztecAddress, traits::Deserialize};
2+
use crate::oracle::storage::{raw_storage_read, storage_read};
23

34
struct UnconstrainedContext {
45
block_number: u32,
@@ -35,6 +36,14 @@ impl UnconstrainedContext {
3536
fn chain_id(self) -> Field {
3637
self.chain_id
3738
}
39+
40+
unconstrained fn raw_storage_read<N>(self: Self, storage_slot: Field) -> [Field; N] {
41+
storage_read(self.this_address(), storage_slot, self.block_number())
42+
}
43+
44+
unconstrained fn storage_read<T, N>(self, storage_slot: Field) -> T where T: Deserialize<N> {
45+
T::deserialize(self.raw_storage_read(storage_slot))
46+
}
3847
}
3948

4049
#[oracle(getContractAddress)]

noir-projects/aztec-nr/aztec/src/oracle/storage.nr

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,58 @@
1-
use dep::protocol_types::traits::Deserialize;
1+
use dep::protocol_types::{address::AztecAddress, traits::Deserialize};
22

33
#[oracle(storageRead)]
4-
unconstrained fn storage_read_oracle<N>(storage_slot: Field, length: Field) -> [Field; N] {}
5-
6-
unconstrained pub fn raw_storage_read<N>(storage_slot: Field) -> [Field; N] {
7-
storage_read_oracle(storage_slot, N)
4+
unconstrained fn storage_read_oracle<N>(
5+
address: Field,
6+
storage_slot: Field,
7+
block_number: Field,
8+
length: Field
9+
) -> [Field; N] {}
10+
11+
unconstrained pub fn raw_storage_read<N>(
12+
address: AztecAddress,
13+
storage_slot: Field,
14+
block_number: u32
15+
) -> [Field; N] {
16+
storage_read_oracle(address.to_field(), storage_slot, block_number as Field, N)
817
}
918

10-
unconstrained pub fn storage_read<T, N>(storage_slot: Field) -> T where T: Deserialize<N> {
11-
T::deserialize(raw_storage_read(storage_slot))
19+
unconstrained pub fn storage_read<T, N>(
20+
address: AztecAddress,
21+
storage_slot: Field,
22+
block_number: u32
23+
) -> T where T: Deserialize<N> {
24+
T::deserialize(raw_storage_read(address, storage_slot, block_number))
1225
}
1326

1427
mod tests {
1528
use crate::oracle::storage::{raw_storage_read, storage_read};
29+
use dep::protocol_types::address::AztecAddress;
1630

1731
use std::test::OracleMock;
1832
use crate::test::mocks::mock_struct::MockStruct;
1933

34+
global address = AztecAddress::from_field(29);
35+
global slot = 7;
36+
global block_number = 17;
37+
2038
#[test]
2139
fn test_raw_storage_read() {
22-
let slot = 7;
2340
let written = MockStruct { a: 13, b: 42 };
2441

25-
let _ = OracleMock::mock("storageRead").with_params((slot, 2)).returns(written.serialize());
42+
let _ = OracleMock::mock("storageRead").returns(written.serialize());
2643

27-
let read: [Field; 2] = raw_storage_read(slot);
44+
let read: [Field; 2] = raw_storage_read(address, slot, block_number);
2845
assert_eq(read[0], 13);
2946
assert_eq(read[1], 42);
3047
}
3148

3249
#[test]
3350
fn test_storage_read() {
34-
let slot = 7;
3551
let written = MockStruct { a: 13, b: 42 };
3652

37-
let _ = OracleMock::mock("storageRead").with_params((slot, 2)).returns(written.serialize());
53+
let _ = OracleMock::mock("storageRead").returns(written.serialize());
3854

39-
let read: MockStruct = storage_read(slot);
55+
let read: MockStruct = storage_read(address, slot, block_number);
4056
assert_eq(read.a, 13);
4157
assert_eq(read.b, 42);
4258
}

noir-projects/aztec-nr/aztec/src/state_vars/public_immutable.nr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,6 @@ impl <T> PublicImmutable<T, &mut PublicContext> {
5656

5757
impl<T> PublicImmutable<T, UnconstrainedContext> {
5858
unconstrained pub fn read<T_SERIALIZED_LEN>(self) -> T where T: Deserialize<T_SERIALIZED_LEN> {
59-
storage_read(self.storage_slot)
59+
self.context.storage_read(self.storage_slot)
6060
}
6161
}

noir-projects/aztec-nr/aztec/src/state_vars/public_mutable.nr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,6 @@ impl<T> PublicMutable<T, &mut PublicContext> {
4141

4242
impl<T> PublicMutable<T, UnconstrainedContext> {
4343
unconstrained pub fn read<T_SERIALIZED_LEN>(self) -> T where T: Deserialize<T_SERIALIZED_LEN> {
44-
storage_read(self.storage_slot)
44+
self.context.storage_read(self.storage_slot)
4545
}
4646
}

noir-projects/aztec-nr/aztec/src/state_vars/shared_immutable.nr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ impl<T> SharedImmutable<T, &mut PublicContext> {
4848

4949
impl<T> SharedImmutable<T, UnconstrainedContext> {
5050
unconstrained pub fn read_public<T_SERIALIZED_LEN>(self) -> T where T: Deserialize<T_SERIALIZED_LEN> {
51-
storage_read(self.storage_slot)
51+
self.context.storage_read(self.storage_slot)
5252
}
5353
}
5454

noir-projects/noir-contracts/contracts/token_contract/src/test/utils.nr

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,11 @@ pub fn setup_and_mint(with_account_contracts: bool) -> (&mut TestEnvironment, Az
7272
pub fn check_public_balance(token_contract_address: AztecAddress, address: AztecAddress, address_amount: Field) {
7373
let current_contract_address = cheatcodes::get_contract_address();
7474
cheatcodes::set_contract_address(token_contract_address);
75+
let block_number = cheatcodes::get_block_number();
7576

7677
let balances_slot = Token::storage().public_balances.slot;
7778
let address_slot = derive_storage_slot_in_map(balances_slot, address);
78-
let amount: U128 = storage_read(address_slot);
79+
let amount: U128 = storage_read(token_contract_address, address_slot, block_number);
7980
assert(amount.to_field() == address_amount, "Public balance is not correct");
8081
cheatcodes::set_contract_address(current_contract_address);
8182
}

yarn-project/aztec-node/src/aztec-node/server.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -704,10 +704,11 @@ export class AztecNodeService implements AztecNode {
704704
*
705705
* @param contract - Address of the contract to query.
706706
* @param slot - Slot to query.
707+
* @param blockNumber - The block number at which to get the data or 'latest'.
707708
* @returns Storage value at the given contract slot.
708709
*/
709-
public async getPublicStorageAt(contract: AztecAddress, slot: Fr): Promise<Fr> {
710-
const committedDb = await this.#getWorldState('latest');
710+
public async getPublicStorageAt(contract: AztecAddress, slot: Fr, blockNumber: L2BlockNumber): Promise<Fr> {
711+
const committedDb = await this.#getWorldState(blockNumber);
711712
const leafSlot = computePublicDataTreeLeafSlot(contract, slot);
712713

713714
const lowLeafResult = await committedDb.getPreviousValueIndex(MerkleTreeId.PUBLIC_DATA_TREE, leafSlot.toBigInt());

yarn-project/circuit-types/src/interfaces/aztec-node.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,9 +291,10 @@ export interface AztecNode {
291291
*
292292
* @param contract - Address of the contract to query.
293293
* @param slot - Slot to query.
294+
* @param blockNumber - The block number at which to get the data or 'latest'.
294295
* @returns Storage value at the given contract slot.
295296
*/
296-
getPublicStorageAt(contract: AztecAddress, slot: Fr): Promise<Fr>;
297+
getPublicStorageAt(contract: AztecAddress, slot: Fr, blockNumber: L2BlockNumber): Promise<Fr>;
297298

298299
/**
299300
* Returns the currently committed block header.

yarn-project/end-to-end/src/benchmarks/bench_process_history.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ describe('benchmarks/process_history', () => {
5454
const node = await AztecNodeService.createAndSync(nodeConfig);
5555
// call getPublicStorageAt (which calls #getWorldState, which calls #syncWorldState) to force a sync with
5656
// world state to ensure the node has caught up
57-
await node.getPublicStorageAt(AztecAddress.random(), Fr.random());
57+
await node.getPublicStorageAt(AztecAddress.random(), Fr.random(), 'latest');
5858
return node;
5959
});
6060

yarn-project/end-to-end/src/benchmarks/bench_publish_rollup.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ describe('benchmarks/publish_rollup', () => {
3737
// world state to ensure the node has caught up
3838
context.logger.info(`Starting new aztec node`);
3939
const node = await AztecNodeService.createAndSync({ ...context.config, disableSequencer: true });
40-
await node.getPublicStorageAt(AztecAddress.random(), Fr.random());
40+
await node.getPublicStorageAt(AztecAddress.random(), Fr.random(), 'latest');
4141

4242
// Spin up a new pxe and sync it, we'll use it to test sync times of new accounts for the last block
4343
context.logger.info(`Starting new pxe`);

0 commit comments

Comments
 (0)