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
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use crate::protocol::{
abis::block_header::BlockHeader,
constants::{ARCHIVE_HEIGHT, NOTE_HASH_TREE_HEIGHT},
merkle_tree::MembershipWitness,
traits::Hash,
use crate::{
ephemeral::EphemeralArray,
protocol::{
abis::block_header::BlockHeader,
constants::{ARCHIVE_HEIGHT, NOTE_HASH_TREE_HEIGHT},
merkle_tree::MembershipWitness,
traits::Hash,
},
};

#[oracle(aztec_utl_getNoteHashMembershipWitness)]
Expand All @@ -17,6 +20,12 @@ unconstrained fn get_block_hash_membership_witness_oracle(
block_hash: Field,
) -> Option<MembershipWitness<ARCHIVE_HEIGHT>> {}

#[oracle(aztec_utl_areBlockHashesInArchive)]
unconstrained fn are_block_hashes_in_archive_oracle(
anchor_block_hash: Field,
block_hashes: EphemeralArray<Field>,
) -> EphemeralArray<bool> {}

// Note: get_nullifier_membership_witness function is implemented in get_nullifier_membership_witness.nr

/// Returns a membership witness for a `note_hash` in the note hash tree whose root is defined in
Expand Down Expand Up @@ -53,6 +62,15 @@ pub unconstrained fn get_maybe_block_hash_membership_witness(
get_block_hash_membership_witness_oracle(anchor_block_hash, block_hash)
}

/// Returns whether each block hash is present in the archive tree whose root is defined in `anchor_block_header`.
pub unconstrained fn are_block_hashes_in_archive(
anchor_block_header: BlockHeader,
block_hashes: EphemeralArray<Field>,
) -> EphemeralArray<bool> {
let anchor_block_hash = anchor_block_header.hash();
are_block_hashes_in_archive_oracle(anchor_block_hash, block_hashes)
}

mod test {
use crate::history::test::{create_note, NOTE_CREATED_AT};
use crate::note::note_interface::NoteHash;
Expand Down
7 changes: 6 additions & 1 deletion noir-projects/aztec-nr/aztec/src/oracle/mod.nr
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,12 @@ pub mod get_nullifier_membership_witness;
],
)]
pub mod get_public_data_witness;
#[generate_oracle_tests]
#[generate_oracle_tests_excluding(
@[
// TODO: cover once the test resolver can serve EphemeralArray contents.
quote { are_block_hashes_in_archive_oracle },
],
)]
pub mod get_membership_witness;
#[generate_oracle_tests]
pub mod keys;
Expand Down
2 changes: 1 addition & 1 deletion noir-projects/aztec-nr/aztec/src/oracle/version.nr
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
/// immediately if AZTEC_NR_MINOR > PXE_MINOR because if a contract is updated to use a newer Aztec.nr dependency
/// without actually using any of the new oracles then there is no reason to throw.
pub global ORACLE_VERSION_MAJOR: Field = 30;
pub global ORACLE_VERSION_MINOR: Field = 6;
pub global ORACLE_VERSION_MINOR: Field = 7;

/// Asserts that the version of the oracle is compatible with the version expected by the contract.
pub fn assert_compatible_oracle_version() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,14 @@ export const ORACLE_REGISTRY = {
returnType: OPTION(MEMBERSHIP_WITNESS(ARCHIVE_HEIGHT)),
}),

aztec_utl_areBlockHashesInArchive: makeEntry({
params: [
{ name: 'anchorBlockHash', type: BLOCK_HASH },
{ name: 'blockHashes', type: EPHEMERAL_ARRAY(BLOCK_HASH) },
],
returnType: EPHEMERAL_ARRAY(BOOL),
}),

aztec_utl_getNullifierMembershipWitness: makeEntry({
params: [
{ name: 'blockHash', type: BLOCK_HASH },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,27 @@ describe('Utility Execution test suite', () => {
expect(aztecNode.getBlockHashMembershipWitness).toHaveBeenCalledTimes(1);
});

it('returns aligned archive-membership booleans for block hash batches', async () => {
const service = new EphemeralArrayService();
const oracle = makeOracle({ scopes: [scope] });
const referenceBlockHash = await anchorBlockHeader.hash();
const presentBlockHash = BlockHash.random();
const missingBlockHash = BlockHash.random();
const witness = MembershipWitness.empty(ARCHIVE_HEIGHT);

aztecNode.getBlockHashMembershipWitness.mockImplementation((_referenceBlockHash, blockHash) =>
Promise.resolve(blockHash.equals(presentBlockHash) ? witness : undefined),
);

const result = await oracle.areBlockHashesInArchive(
referenceBlockHash,
EphemeralArray.fromValues(service, [presentBlockHash, missingBlockHash, presentBlockHash]),
);

expect(result.readAll(service)).toEqual([true, false, true]);
expect(aztecNode.getBlockHashMembershipWitness).toHaveBeenCalledTimes(2);
});

it('reuses public storage reads within a utility execution', async () => {
const oracle = makeOracle({ scopes: [scope] });
const blockHash = await anchorBlockHeader.hash();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,22 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
return witness ? Option.some(witness) : Option.none();
}

/** Returns whether each block hash is present in the archive tree at the referenced block. */
public async areBlockHashesInArchive(
referenceBlockHash: BlockHash,
blockHashes: EphemeralArray<BlockHash>,
): Promise<EphemeralArray<boolean>> {
const hashes = blockHashes.readAll(this.ephemeralArrayService);
const memberships = await this.#queryWithBlockHashNotAfterAnchor(referenceBlockHash, () =>
Promise.all(
hashes.map(blockHash =>
this.aztecNodeReadCache.getBlockHashMembershipWitness(referenceBlockHash, blockHash).then(Boolean),
),
),
);
return EphemeralArray.fromValues(this.ephemeralArrayService, memberships);
}

/**
* Returns a nullifier membership witness for a given nullifier at a given block.
* @param blockHash - The block hash at which to get the index.
Expand Down
4 changes: 2 additions & 2 deletions yarn-project/pxe/src/oracle_version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
/// if AZTEC_NR_MINOR > PXE_MINOR because if a contract is updated to use a newer Aztec.nr dependency without actually
/// using any of the new oracles then there is no reason to throw.
export const ORACLE_VERSION_MAJOR = 30;
export const ORACLE_VERSION_MINOR = 6;
export const ORACLE_VERSION_MINOR = 7;

/// This hash is computed from the `ORACLE_REGISTRY` declaration (each oracle's name, ordered parameter names and
/// types, and return type) and is used to detect when the oracle interface changes. When it does, you need to either:
/// - increment `ORACLE_VERSION_MAJOR` and reset `ORACLE_VERSION_MINOR` to zero if the change is breaking, or
/// - increment only `ORACLE_VERSION_MINOR` if the change is additive (a new oracle was added).
///
/// These constants must be kept in sync between this file and `noir-projects/aztec-nr/aztec/src/oracle/version.nr`.
export const ORACLE_INTERFACE_HASH = '6094994f539407001d2adce5b6a8792c08ef645ee39813e32390f6208e78bc16';
export const ORACLE_INTERFACE_HASH = '416b0e7d3e6dca5803ebe2a65ea93f1e79759dc39ef8ec4c52eeba5e2b0f3fca';
10 changes: 10 additions & 0 deletions yarn-project/txe/src/rpc_translator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,16 @@ export class RPCTranslator {
});
}

// eslint-disable-next-line camelcase
aztec_utl_areBlockHashesInArchive(...inputs: ForeignCallArgs) {
return callTxeHandler({
oracle: 'aztec_utl_areBlockHashesInArchive',
inputs,
handler: ([anchorBlockHash, blockHashes]) =>
this.handlerAsUtility().areBlockHashesInArchive(anchorBlockHash, blockHashes),
});
}

// eslint-disable-next-line camelcase
aztec_utl_getLowNullifierMembershipWitness(...inputs: ForeignCallArgs) {
return callTxeHandler({
Expand Down
Loading