Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion docs/docs-developers/docs/aztec-nr/testing_contracts.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ aztec test
2. Run `aztec test`

:::warning
Always use `aztec test` instead of `nargo test`. The `TestEnvironment` requires the TXE (Test eXecution Environment) oracle resolver.
Always use `aztec test` instead of `nargo test`. The `TestEnvironment` requires the test environment oracle resolver provided by the `aztec` CLI.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to remove the term "TXE" from all user facing docs/errors

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good chamo

:::

## Basic test structure
Expand Down Expand Up @@ -348,3 +348,25 @@ unconstrained fn test_missing_authwit() {
}

```

## Test environment oracle versioning

The test environment uses an oracle interface to communicate between your Noir test code and the `aztec test` CLI. This interface is versioned so that mismatches between the Aztec.nr dependency used to compile the test and the CLI version are detected automatically.

The version uses two components, `major.minor`, with the same compatibility rules as [PXE oracle versioning](../foundational-topics/pxe/index.md#oracle-versioning):

- **`major`** must match exactly. A major bump means oracles were removed or had their signatures changed, and a test environment on a different major cannot safely run the test.
- **`minor`** indicates additive changes (new oracles). The test environment uses a best-effort approach: a test compiled against a higher `minor` is still allowed to run, and an error is only thrown if the test actually invokes an oracle the test environment does not know about.

### Resolving a version mismatch

If you see an error like _"Incompatible test environment version: The test was compiled with a newer version of Aztec.nr than your test environment supports"_, the test uses oracles from a newer Aztec.nr than your `aztec test` CLI supports.

To fix it, make sure your `aztec` CLI version and the `aztec` dependency in the test crate's `Nargo.toml` are on the same release. Note that the test crate's Aztec.nr version can differ from the contract crate's version, depending on your project configuration. For example, if your CLI is on `v4.3.0`, the test crate's `Nargo.toml` should reference the matching tag:

```toml
[dependencies]
aztec = { git="https://github.com/AztecProtocol/aztec-nr", tag="v4.3.0", directory="aztec" }
```

If the test environment reports a version that _should_ include every oracle the test needs but an oracle is still missing, this is likely a bug rather than a version problem.
10 changes: 10 additions & 0 deletions docs/netlify.toml
Original file line number Diff line number Diff line change
Expand Up @@ -818,3 +818,13 @@
# PXE: cross-contract utility call denied by execution hook
from = "/errors/11"
to = "/developers/docs/aztec-nr/debugging#cross-contract-utility-call-denied"

[[redirects]]
# Incompatible oracle version between test and test environment
from = "/errors/12"
to = "/developers/docs/aztec-nr/testing_contracts#test-environment-oracle-versioning"

[[redirects]]
# Unexpected error: how to report issues
from = "/errors/13"
to = "/developers/docs/aztec-nr/debugging#reporting-issues"
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ impl TestEnvironment {
/// Creates a new `TestEnvironment`. This function should only be called once per test.
pub unconstrained fn new() -> Self {
assert_compatible_oracle_version();
txe_oracles::assert_compatible_txe_oracle_version();
Self {
// Use an offset to avoid secret collision with account secrets. Without this, when
// deploying multiple contracts and creating accounts, they could end up with identical
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use crate::{
oracle::version::{ORACLE_VERSION_MAJOR, ORACLE_VERSION_MINOR},
test::helpers::test_environment::TestEnvironment,
test::helpers::{
test_environment::TestEnvironment,
txe_oracles::{TXE_ORACLE_VERSION_MAJOR, TXE_ORACLE_VERSION_MINOR},
},
};
use std::test::OracleMock;

Expand Down Expand Up @@ -49,3 +52,16 @@ unconstrained fn oracle_version_is_checked_upon_env_creation() {
assert_eq(mock.times_called(), 1);
assert_eq(mock.get_last_params::<(Field, Field)>(), (ORACLE_VERSION_MAJOR, ORACLE_VERSION_MINOR));
}

#[test]
unconstrained fn txe_oracle_version_is_checked_upon_env_creation() {
let mock = OracleMock::mock("aztec_txe_assertCompatibleOracleVersion");

let _env = TestEnvironment::new();

assert_eq(mock.times_called(), 1);
assert_eq(
mock.get_last_params::<(Field, Field)>(),
(TXE_ORACLE_VERSION_MAJOR, TXE_ORACLE_VERSION_MINOR),
);
}
16 changes: 16 additions & 0 deletions noir-projects/aztec-nr/aztec/src/test/helpers/txe_oracles.nr
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,22 @@ use crate::protocol::{
traits::{Deserialize, ToField},
};

/// The TXE oracle version constants are used to check that the oracle interface used for tests is in sync between
/// TXE and Aztec.nr. This is separate from the contract oracle version in
/// [`oracle::version`](crate::oracle::version), which covers oracles used during contract execution by PXE.
///
/// The TypeScript counterparts are in `yarn-project/txe/src/txe_oracle_version.ts`.
pub global TXE_ORACLE_VERSION_MAJOR: Field = 1;
pub global TXE_ORACLE_VERSION_MINOR: Field = 0;

/// Asserts that the TXE oracle interface version is compatible.
pub unconstrained fn assert_compatible_txe_oracle_version() {
assert_compatible_txe_oracle_version_oracle_call(TXE_ORACLE_VERSION_MAJOR, TXE_ORACLE_VERSION_MINOR);
}

#[oracle(aztec_txe_assertCompatibleOracleVersion)]
unconstrained fn assert_compatible_txe_oracle_version_oracle_call(major: Field, minor: Field) {}

/// Upper bound on the number of raw offchain effects the TXE test environment will surface
/// to Noir in a single `get_last_call_offchain_effects` query.
pub(crate) global MAX_OFFCHAIN_EFFECTS_PER_TXE_QUERY: u32 = 64;
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/pxe/src/bin/check_oracle_version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { ORACLE_INTERFACE_HASH } from '../oracle_version.js';
* - If the change is backward-breaking (e.g. removing/renaming an oracle), bump ORACLE_VERSION_MAJOR.
* - If the change is an oracle addition (non-breaking), bump ORACLE_VERSION_MINOR.
*
* TODO(#16581): The following only takes into consideration changes to the oracles defined in Oracle.ts and omits TXE
* TODO(F-667): The following only takes into consideration changes to the oracles defined in Oracle.ts and omits TXE
* oracles. Ensure this checks TXE oracles as well. This hasn't been implemented yet since we don't have a clean TXE
* oracle interface like we do in PXE (i.e., there is no single Oracle class that contains only the oracles).
*/
Expand Down
21 changes: 21 additions & 0 deletions yarn-project/txe/src/rpc_translator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { AztecAddress } from '@aztec/stdlib/aztec-address';
import { BlockHash } from '@aztec/stdlib/block';

import type { IAvmExecutionOracle, ITxeExecutionOracle } from './oracle/interfaces.js';
import { TXE_ORACLE_VERSION_MAJOR } from './txe_oracle_version.js';
import type { TXESessionStateHandler } from './txe_session.js';
import {
type ForeignCallArray,
Expand Down Expand Up @@ -111,6 +112,26 @@ export class RPCTranslator {
return this.oracleHandler;
}

// eslint-disable-next-line camelcase
aztec_txe_assertCompatibleOracleVersion(foreignMajor: ForeignCallSingle, foreignMinor: ForeignCallSingle) {
const major = fromSingle(foreignMajor).toNumber();
const minor = fromSingle(foreignMinor).toNumber();

if (major !== TXE_ORACLE_VERSION_MAJOR) {
const hint =
major > TXE_ORACLE_VERSION_MAJOR
? 'The test was compiled with a newer version of Aztec.nr than your test environment supports. Upgrade your test environment to a compatible version.'
: 'The test was compiled with an older version of Aztec.nr than your test environment supports. Recompile the test with a compatible version of Aztec.nr.';
throw new Error(
`Incompatible test environment version: ${hint} See https://docs.aztec.network/errors/12 (expected test oracle major version ${TXE_ORACLE_VERSION_MAJOR}, got ${major})`,
);
}

this.stateHandler.setTxeOracleVersion({ major, minor });

return toForeignCallResult([]);
}

// TXE session state transition functions - these get handled by the state handler

// eslint-disable-next-line camelcase
Expand Down
9 changes: 9 additions & 0 deletions yarn-project/txe/src/txe_oracle_version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* The TXE oracle version constants are used to check that the oracle interface used for tests is in sync between
* TXE and Aztec.nr. This is separate from the contract oracle version in `pxe/src/oracle_version.ts`, which covers
* oracles used during contract execution by PXE.
*
* The Noir counterparts are in `noir-projects/aztec-nr/aztec/src/test/helpers/txe_oracles.nr`.
*/
export const TXE_ORACLE_VERSION_MAJOR = 1;
export const TXE_ORACLE_VERSION_MINOR = 0;
12 changes: 3 additions & 9 deletions yarn-project/txe/src/txe_session.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,26 +32,20 @@ describe('TXESession.processFunction', () => {
it('rejects calling a function that does not exist on RPCTranslator with the expected error message', () => {
const invalidName = 'notARealFunction' as unknown as TXEOracleFunctionName;

expect(() => session.processFunction(invalidName, [])).toThrow(
`notARealFunction does not correspond to any oracle handler available on RPCTranslator`,
);
expect(() => session.processFunction(invalidName, [])).toThrow(`Unknown oracle 'notARealFunction'.`);
});

it('rejects calling internal translator helpers (handlerAs*) with the expected error message', () => {
const illegalNames = ['handlerAsMisc', 'handlerAsUtility', 'handlerAsPrivate', 'handlerAsAvm', 'handlerAsTxe'];

for (const name of illegalNames) {
expect(() => session.processFunction(name as any, [])).toThrow(
`${name} does not correspond to any oracle handler available on RPCTranslator`,
);
expect(() => session.processFunction(name as any, [])).toThrow(`Unknown oracle '${name}'.`);
}
});

it("rejects calling the translator's constructor with the expected error message", () => {
const invalidName = 'constructor' as unknown as TXEOracleFunctionName;

expect(() => session.processFunction(invalidName, [])).toThrow(
`constructor does not correspond to any oracle handler available on RPCTranslator`,
);
expect(() => session.processFunction(invalidName, [])).toThrow(`Unknown oracle 'constructor'.`);
});
});
33 changes: 32 additions & 1 deletion yarn-project/txe/src/txe_session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import { TXEOracleTopLevelContext } from './oracle/txe_oracle_top_level_context.
import { RPCTranslator } from './rpc_translator.js';
import { TXEArchiver } from './state_machine/archiver.js';
import { TXEStateMachine } from './state_machine/index.js';
import { TXE_ORACLE_VERSION_MAJOR, TXE_ORACLE_VERSION_MINOR } from './txe_oracle_version.js';
import type { ForeignCallArgs, ForeignCallResult } from './util/encoding.js';
import { TXEAccountStore } from './util/txe_account_store.js';
import { getSingleTxBlockRequestHash, insertTxEffectIntoWorldTrees, makeTXEBlock } from './utils/block_creation.js';
Expand Down Expand Up @@ -109,6 +110,9 @@ export type TXEOracleFunctionName = Exclude<
>;

export interface TXESessionStateHandler {
/** Records the TXE oracle version reported by the Noir test code for diagnostics. */
setTxeOracleVersion(version: { major: number; minor: number }): void;

enterTopLevelState(): Promise<void>;
enterPublicState(contractAddress?: AztecAddress): Promise<void>;
enterPrivateState(contractAddress?: AztecAddress, anchorBlockNumber?: BlockNumber): Promise<PrivateContextInputs>;
Expand Down Expand Up @@ -186,6 +190,7 @@ export class TXESession implements TXESessionStateHandler {
private state: SessionState = { name: 'TOP_LEVEL' };
private authwits: Map<string, AuthWitness> = new Map();
private lastCallInfo: LastCallState = emptyLastCallState();
private txeOracleVersion: { major: number; minor: number } | undefined;

constructor(
private logger: Logger,
Expand Down Expand Up @@ -306,7 +311,28 @@ export class TXESession implements TXESessionStateHandler {
return translator[validatedFunctionName](...inputs);
} catch (error) {
if (error instanceof z.ZodError) {
throw new Error(`${functionName} does not correspond to any oracle handler available on RPCTranslator`);
let versionHint: string;
if (!this.txeOracleVersion) {
versionHint =
' The test appears to use an older version of Aztec.nr that does not' +
' support test environment oracle versioning. Update Aztec.nr to a compatible version.' +
' See https://docs.aztec.network/errors/12';
} else if (this.txeOracleVersion.minor > TXE_ORACLE_VERSION_MINOR) {
versionHint =
` The test uses Aztec.nr test oracle version` +
` ${this.txeOracleVersion.major}.${this.txeOracleVersion.minor}, but this test environment` +
` only supports up to ${TXE_ORACLE_VERSION_MAJOR}.${TXE_ORACLE_VERSION_MINOR}.` +
` Upgrade the Aztec CLI to a compatible version.` +
` See https://docs.aztec.network/errors/12`;
} else {
versionHint =
` The test's oracle version (${this.txeOracleVersion.major}.${this.txeOracleVersion.minor})` +
` is compatible with this test environment` +
` (${TXE_ORACLE_VERSION_MAJOR}.${TXE_ORACLE_VERSION_MINOR}), so this oracle should be` +
` available. This is an unexpected error, please report it.` +
` See https://docs.aztec.network/errors/13`;
}
throw new Error(`Unknown oracle '${functionName}'.${versionHint}`);
} else if (error instanceof Error) {
throw new Error(
`Execution error while processing function ${functionName} in state ${this.state.name}: ${error.message}`,
Expand Down Expand Up @@ -375,6 +401,11 @@ export class TXESession implements TXESessionStateHandler {
return { txHash, anchorBlockTimestamp };
}

setTxeOracleVersion(version: { major: number; minor: number }): void {
this.txeOracleVersion = version;
this.logger.debug(`Test compiled with test oracle version ${version.major}.${version.minor}`);
}

async enterTopLevelState() {
switch (this.state.name) {
case 'PRIVATE': {
Expand Down
Loading