Skip to content

Commit 7534d82

Browse files
authored
feat: oracle version check (#16435)
Fixes #14979 This PR implements a version compatibility system between Aztec.nr contracts and the PXE oracle interface to prevent ambiguous errors from version mismatches. The oracle version is expected to be manually updated but to ensure that this does not get forgotten the build throws an error if an oracle interface hash constant needs updating.
2 parents 86b24e3 + 78dcaf9 commit 7534d82

File tree

22 files changed

+295
-6
lines changed

22 files changed

+295
-6
lines changed

noir-projects/aztec-nr/aztec/src/macros/functions/utils.nr

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,9 @@ pub(crate) comptime fn transform_private(f: FunctionDefinition) {
151151

152152
let context_finish = quote { context.finish() };
153153

154+
// A quote to be injected at the beginning of the function body.
154155
let to_prepend = quote {
156+
dep::aztec::oracle::version::assert_compatible_oracle_version();
155157
$args_hasher
156158
$context_creation
157159
$assert_initializer
@@ -305,9 +307,9 @@ pub(crate) comptime fn transform_utility(f: FunctionDefinition) {
305307
// (`sync_private_state` function gets invoked by PXE::getPrivateEvents function).
306308
let message_discovery_call = create_message_discovery_call();
307309

308-
// Inject context creation, storage initialization, and message discovery call at the beginning of the function
309-
// body.
310+
// A quote to be injected at the beginning of the function body.
310311
let to_prepend = quote {
312+
dep::aztec::oracle::version::assert_compatible_oracle_version();
311313
$context_creation
312314
$storage_init
313315
$message_discovery_call
@@ -375,7 +377,7 @@ pub(crate) comptime fn create_message_discovery_call() -> Quoted {
375377
/// if (!from.eq(context.msg_sender())) {
376378
/// assert_current_call_valid_authwit::<N>(&mut context, from);
377379
/// } else {
378-
/// assert(authwit_nonce, "Invalid auhtwit nonce. When 'from' and 'msg_sender' are the same, authwit_nonce must be zero");
380+
/// assert(authwit_nonce, "Invalid authwit nonce. When 'from' and 'msg_sender' are the same, authwit_nonce must be zero");
379381
/// }
380382
/// ```
381383
/// where `from` and `authwit_nonce` are the names of the parameters that are expected to be present in the function definition.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ pub mod logs;
2222
pub mod message_processing;
2323
pub mod notes;
2424
pub mod offchain_effect;
25+
pub mod version;
2526
pub mod random;
2627
pub mod shared_secret;
2728
pub mod storage;
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/// The ORACLE_VERSION constant is used to check that the oracle interface is in sync between PXE and Aztec.nr. We need
2+
/// to version the oracle interface to ensure that developers get a reasonable error message if they use incompatible
3+
/// versions of Aztec.nr and PXE. The TypeScript counterpart is in `oracle_version.ts`.
4+
///
5+
/// @dev Whenever a contract function or Noir test is run, the `utilityAssertCompatibleOracleVersion` oracle is called and
6+
/// if the oracle version is incompatible an error is thrown.
7+
pub global ORACLE_VERSION: Field = 1;
8+
9+
/// Asserts that the version of the oracle is compatible with the version expected by the contract.
10+
pub fn assert_compatible_oracle_version() {
11+
// Safety: This oracle call returns nothing: we only call it to check Aztec.nr and Oracle interface versions are
12+
// compatible. It is therefore always safe to call.
13+
unsafe {
14+
assert_compatible_oracle_version_wrapper();
15+
}
16+
}
17+
18+
unconstrained fn assert_compatible_oracle_version_wrapper() {
19+
assert_compatible_oracle_version_oracle(ORACLE_VERSION);
20+
}
21+
22+
#[oracle(utilityAssertCompatibleOracleVersion)]
23+
unconstrained fn assert_compatible_oracle_version_oracle(version: Field) {}
24+
25+
mod test {
26+
use super::{assert_compatible_oracle_version_oracle, ORACLE_VERSION};
27+
28+
#[test]
29+
unconstrained fn compatible_oracle_version() {
30+
assert_compatible_oracle_version_oracle(ORACLE_VERSION);
31+
}
32+
33+
#[test(should_fail_with = "Incompatible oracle version. PXE is using version '1', but got a request for '318183437'.")]
34+
unconstrained fn incompatible_oracle_version() {
35+
let arbitrary_incorrect_version = 318183437;
36+
assert_compatible_oracle_version_oracle(arbitrary_incorrect_version);
37+
}
38+
}

noir-projects/aztec-nr/aztec/src/test/helpers/test_environment.nr

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ use crate::{
99
ReturnsHash, UtilityCallInterface, UtilityContext,
1010
},
1111
hash::hash_args,
12-
oracle::{execution::{get_block_number, get_timestamp}, execution_cache},
12+
oracle::{
13+
execution::{get_block_number, get_timestamp},
14+
execution_cache,
15+
version::assert_compatible_oracle_version,
16+
},
1317
test::helpers::{txe_oracles, utils::ContractDeployment},
1418
};
1519

@@ -102,6 +106,7 @@ impl PrivateContextOptions {
102106
impl TestEnvironment {
103107
/// Creates a new `TestEnvironment`. This function should only be called once per test.
104108
pub unconstrained fn new() -> Self {
109+
assert_compatible_oracle_version();
105110
Self {
106111
light_account_secret: Counter::new(),
107112
contract_account_secret: Counter::new(),

noir-projects/aztec-nr/aztec/src/test/helpers/test_environment/test.nr

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use crate::test::helpers::test_environment::TestEnvironment;
1+
use crate::{oracle::version::ORACLE_VERSION, test::helpers::test_environment::TestEnvironment};
2+
use std::test::OracleMock;
23

34
mod accounts;
45
mod deployment;
@@ -310,3 +311,13 @@ unconstrained fn private_public_and_utility_context_share_default_chain_id() {
310311
assert_eq(private_chain_id, public_chain_id);
311312
assert_eq(private_chain_id, utility_chain_id);
312313
}
314+
315+
#[test]
316+
unconstrained fn oracle_version_is_checked_upon_env_creation() {
317+
let mock = OracleMock::mock("utilityAssertCompatibleOracleVersion");
318+
319+
let _env = TestEnvironment::new();
320+
321+
assert_eq(mock.times_called(), 1);
322+
assert_eq(mock.get_last_params::<Field>(), ORACLE_VERSION);
323+
}

noir-projects/noir-contracts/Nargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ members = [
4646
"contracts/test/no_constructor_contract",
4747
"contracts/test/note_getter_contract",
4848
"contracts/test/offchain_effect_contract",
49+
"contracts/test/oracle_version_check_contract",
4950
"contracts/test/parent_contract",
5051
"contracts/test/pending_note_hashes_contract",
5152
"contracts/test/public_immutable_contract",
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[package]
2+
name = "oracle_version_check_contract"
3+
authors = [""]
4+
compiler_version = ">=0.25.0"
5+
type = "contract"
6+
7+
[dependencies]
8+
aztec = { path = "../../../../aztec-nr/aztec" }
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
use aztec::macros::aztec;
2+
3+
/// Used to test that the oracle version check is performed when a private or utility function is called.
4+
#[aztec]
5+
pub contract OracleVersionCheck {
6+
use aztec::macros::functions::{private, utility};
7+
8+
#[private]
9+
fn private_function() -> Field {
10+
0
11+
}
12+
13+
#[utility]
14+
unconstrained fn utility_function() -> Field {
15+
0
16+
}
17+
}

yarn-project/bootstrap.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ function compile_all {
7070

7171
get_projects | compile_project
7272

73+
# Run oracle version check for pxe after compilation
74+
cd pxe && yarn check_oracle_version
75+
cd ..
76+
7377
cmds=('format --check')
7478
if [ "${TYPECHECK:-0}" -eq 1 ] || [ "${CI:-0}" -eq 1 ]; then
7579
# Fully type check and lint.

yarn-project/pxe/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
"clean": "rm -rf ./dest .tsbuildinfo ./src/config/package_info.ts",
1818
"test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests --maxWorkers=${JEST_MAX_WORKERS:-8}",
1919
"start": "LOG_LEVEL=${LOG_LEVEL:-debug} && node ./dest/bin/index.js",
20-
"generate": "node ./scripts/generate_package_info.js"
20+
"generate": "node ./scripts/generate_package_info.js",
21+
"check_oracle_version": "node ./dest/bin/check_oracle_version.js"
2122
},
2223
"inherits": [
2324
"../package.common.json",
@@ -75,6 +76,7 @@
7576
"@aztec/protocol-contracts": "workspace:^",
7677
"@aztec/simulator": "workspace:^",
7778
"@aztec/stdlib": "workspace:^",
79+
"json-stringify-deterministic": "1.0.12",
7880
"koa": "^2.16.1",
7981
"koa-router": "^12.0.0",
8082
"lodash.omit": "^4.5.0",

0 commit comments

Comments
 (0)