-
Notifications
You must be signed in to change notification settings - Fork 295
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: PXE db contract store (#10867)
- Loading branch information
Showing
17 changed files
with
450 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
use protocol_types::{address::AztecAddress, traits::{Deserialize, Serialize}}; | ||
|
||
#[oracle(store)] | ||
unconstrained fn store_oracle<let N: u32>( | ||
contract_address: AztecAddress, | ||
key: Field, | ||
values: [Field; N], | ||
) {} | ||
|
||
/// Store a value of type T that implements Serialize in local PXE database. The data is scoped to the current | ||
/// contract. If the data under the key already exists, it is overwritten. | ||
pub unconstrained fn store<T, let N: u32>(contract_address: AztecAddress, key: Field, value: T) | ||
where | ||
T: Serialize<N>, | ||
{ | ||
let serialized = value.serialize(); | ||
store_oracle(contract_address, key, serialized); | ||
} | ||
|
||
/// Load data from local PXE database. We pass in `t_size` as a parameter to have the information of how many fields | ||
/// we need to pad if the key does not exist (note that the actual response size is `t_size + 1` as the Option prefixes | ||
/// the response with a boolean indicating if the data exists). | ||
/// | ||
/// Note that we need to return an Option<[Field; N]> as we cannot return an Option<T> directly. This is because then | ||
/// the shape of T would affect the expected oracle response (e.g. if we were returning a struct of 3 u32 values | ||
/// then the expected response shape would be 3 single items. If instead we had a struct containing | ||
/// `u32, [Field;10], u32`, then the expected shape would be single, array, single.). | ||
#[oracle(load)] | ||
unconstrained fn load_oracle<let N: u32>( | ||
contract_address: AztecAddress, | ||
key: Field, | ||
t_size: u32, | ||
) -> Option<[Field; N]> {} | ||
|
||
/// Load a value of type T that implements Deserialize from local PXE database. The data is scoped to the current | ||
/// contract. If the key does not exist, Option::none() is returned. | ||
pub unconstrained fn load<T, let N: u32>(contract_address: AztecAddress, key: Field) -> Option<T> | ||
where | ||
T: Deserialize<N>, | ||
{ | ||
let serialized_option = load_oracle::<N>(contract_address, key, N); | ||
serialized_option.map(|arr| Deserialize::deserialize(arr)) | ||
} | ||
|
||
mod test { | ||
use crate::{ | ||
oracle::{pxe_db::{load, store}, random::random}, | ||
test::{helpers::test_environment::TestEnvironment, mocks::mock_struct::MockStruct}, | ||
}; | ||
|
||
#[test] | ||
unconstrained fn stores_loads_and_overwrites_data() { | ||
let env = TestEnvironment::new(); | ||
|
||
let contract_address = env.contract_address(); | ||
let key = random(); | ||
let value = MockStruct::new(5, 6); | ||
store(contract_address, key, value); | ||
|
||
let loaded_value: MockStruct = load(contract_address, key).unwrap(); | ||
|
||
assert(loaded_value == value, "Stored and loaded values should be equal"); | ||
|
||
// Now we test that the value gets overwritten correctly. | ||
let new_value = MockStruct::new(7, 8); | ||
store(contract_address, key, new_value); | ||
|
||
let loaded_value: MockStruct = load(contract_address, key).unwrap(); | ||
|
||
assert(loaded_value == new_value, "Stored and loaded values should be equal"); | ||
} | ||
|
||
#[test] | ||
unconstrained fn load_non_existent_key() { | ||
let env = TestEnvironment::new(); | ||
|
||
let contract_address = env.contract_address(); | ||
let key = random(); | ||
let loaded_value: Option<MockStruct> = load(contract_address, key); | ||
|
||
assert(loaded_value == Option::none(), "Value should not exist"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { Fr, type Wallet } from '@aztec/aztec.js'; | ||
import { TestContract } from '@aztec/noir-contracts.js/Test'; | ||
|
||
import { jest } from '@jest/globals'; | ||
|
||
import { setup } from './fixtures/utils.js'; | ||
|
||
const TIMEOUT = 120_000; | ||
|
||
// TODO(#10724): Nuke this once the linked issue is implemented (then the code will be well-tested). There is also | ||
// a TXE test in `pxe_db.nr` but I decided to keep this ugly test around as it tests the PXE oracle callback handler | ||
// (which is not tested by the TXE test). Dont't forget to remove `store_in_pxe_db` and `load_from_pxe_db` from | ||
// the test contract when removing this test. | ||
describe('PXE db', () => { | ||
jest.setTimeout(TIMEOUT); | ||
|
||
let teardown: () => Promise<void>; | ||
|
||
let testContract: TestContract; | ||
|
||
beforeAll(async () => { | ||
let wallet: Wallet; | ||
({ teardown, wallet } = await setup(1)); | ||
testContract = await TestContract.deploy(wallet).send().deployed(); | ||
}); | ||
|
||
afterAll(() => teardown()); | ||
|
||
it('stores and loads data', async () => { | ||
// In this test we feed arbitrary struct to a test contract, the test contract stores it in the PXE db and then | ||
// we load it back. | ||
const arbitraryStruct = { | ||
a: Fr.random(), | ||
b: Fr.random(), | ||
}; | ||
|
||
const key = 6n; | ||
await testContract.methods.store_in_pxe_db(key, arbitraryStruct).simulate(); | ||
|
||
// Now we try to load the data back from the PXE db. | ||
const expectedReturnValue = [arbitraryStruct.a, arbitraryStruct.b].map(v => v.toBigInt()); | ||
expect(await testContract.methods.load_from_pxe_db(key).simulate()).toEqual(expectedReturnValue); | ||
}); | ||
|
||
it('handles non-existent data', async () => { | ||
// In this test we try to load a key from the PXE db that does not exist. We should get an array of zeros. | ||
const key = 7n; | ||
expect(await testContract.methods.load_from_pxe_db(key).simulate()).toEqual([0n, 0n]); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.