Skip to content

Commit 2967179

Browse files
authored
feat: add batch is block in archive oracle (#24634)
Adds an oracle to check whether a collection of block hashes is present in the archiver tree as of a given block. An upstream port of: aztec-labs-eng/oxide#257 Note: I considered making this batch call return something more "interesting" than existence booleans (such as MembershipWitnesses), but that would potentially be heavy over the wire and there's no current evidence of it being needed. We can add a new oracle for that if turns out to be useful in the future. Closes F-810
1 parent 736f391 commit 2967179

8 files changed

Lines changed: 87 additions & 9 deletions

File tree

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

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
use crate::protocol::{
2-
abis::block_header::BlockHeader,
3-
constants::{ARCHIVE_HEIGHT, NOTE_HASH_TREE_HEIGHT},
4-
merkle_tree::MembershipWitness,
5-
traits::Hash,
1+
use crate::{
2+
ephemeral::EphemeralArray,
3+
protocol::{
4+
abis::block_header::BlockHeader,
5+
constants::{ARCHIVE_HEIGHT, NOTE_HASH_TREE_HEIGHT},
6+
merkle_tree::MembershipWitness,
7+
traits::Hash,
8+
},
69
};
710

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

23+
#[oracle(aztec_utl_areBlockHashesInArchive)]
24+
unconstrained fn are_block_hashes_in_archive_oracle(
25+
anchor_block_hash: Field,
26+
block_hashes: EphemeralArray<Field>,
27+
) -> EphemeralArray<bool> {}
28+
2029
// Note: get_nullifier_membership_witness function is implemented in get_nullifier_membership_witness.nr
2130

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

65+
/// Returns whether each block hash is present in the archive tree whose root is defined in `anchor_block_header`.
66+
pub unconstrained fn are_block_hashes_in_archive(
67+
anchor_block_header: BlockHeader,
68+
block_hashes: EphemeralArray<Field>,
69+
) -> EphemeralArray<bool> {
70+
let anchor_block_hash = anchor_block_header.hash();
71+
are_block_hashes_in_archive_oracle(anchor_block_hash, block_hashes)
72+
}
73+
5674
mod test {
5775
use crate::history::test::{create_note, NOTE_CREATED_AT};
5876
use crate::note::note_interface::NoteHash;

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,12 @@ pub mod get_nullifier_membership_witness;
9999
],
100100
)]
101101
pub mod get_public_data_witness;
102-
#[generate_oracle_tests]
102+
#[generate_oracle_tests_excluding(
103+
@[
104+
// TODO: cover once the test resolver can serve EphemeralArray contents.
105+
quote { are_block_hashes_in_archive_oracle },
106+
],
107+
)]
103108
pub mod get_membership_witness;
104109
#[generate_oracle_tests]
105110
pub mod keys;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
/// immediately if AZTEC_NR_MINOR > PXE_MINOR because if a contract is updated to use a newer Aztec.nr dependency
1212
/// without actually using any of the new oracles then there is no reason to throw.
1313
pub global ORACLE_VERSION_MAJOR: Field = 30;
14-
pub global ORACLE_VERSION_MINOR: Field = 6;
14+
pub global ORACLE_VERSION_MINOR: Field = 7;
1515

1616
/// Asserts that the version of the oracle is compatible with the version expected by the contract.
1717
pub fn assert_compatible_oracle_version() {

yarn-project/pxe/src/contract_function_simulator/oracle/oracle_registry.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,14 @@ export const ORACLE_REGISTRY = {
141141
returnType: OPTION(MEMBERSHIP_WITNESS(ARCHIVE_HEIGHT)),
142142
}),
143143

144+
aztec_utl_areBlockHashesInArchive: makeEntry({
145+
params: [
146+
{ name: 'anchorBlockHash', type: BLOCK_HASH },
147+
{ name: 'blockHashes', type: EPHEMERAL_ARRAY(BLOCK_HASH) },
148+
],
149+
returnType: EPHEMERAL_ARRAY(BOOL),
150+
}),
151+
144152
aztec_utl_getNullifierMembershipWitness: makeEntry({
145153
params: [
146154
{ name: 'blockHash', type: BLOCK_HASH },

yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,27 @@ describe('Utility Execution test suite', () => {
741741
expect(aztecNode.getBlockHashMembershipWitness).toHaveBeenCalledTimes(1);
742742
});
743743

744+
it('returns aligned archive-membership booleans for block hash batches', async () => {
745+
const service = new EphemeralArrayService();
746+
const oracle = makeOracle({ scopes: [scope] });
747+
const referenceBlockHash = await anchorBlockHeader.hash();
748+
const presentBlockHash = BlockHash.random();
749+
const missingBlockHash = BlockHash.random();
750+
const witness = MembershipWitness.empty(ARCHIVE_HEIGHT);
751+
752+
aztecNode.getBlockHashMembershipWitness.mockImplementation((_referenceBlockHash, blockHash) =>
753+
Promise.resolve(blockHash.equals(presentBlockHash) ? witness : undefined),
754+
);
755+
756+
const result = await oracle.areBlockHashesInArchive(
757+
referenceBlockHash,
758+
EphemeralArray.fromValues(service, [presentBlockHash, missingBlockHash, presentBlockHash]),
759+
);
760+
761+
expect(result.readAll(service)).toEqual([true, false, true]);
762+
expect(aztecNode.getBlockHashMembershipWitness).toHaveBeenCalledTimes(2);
763+
});
764+
744765
it('reuses public storage reads within a utility execution', async () => {
745766
const oracle = makeOracle({ scopes: [scope] });
746767
const blockHash = await anchorBlockHeader.hash();

yarn-project/pxe/src/contract_function_simulator/oracle/utility_execution_oracle.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,22 @@ export class UtilityExecutionOracle implements IMiscOracle, IUtilityExecutionOra
273273
return witness ? Option.some(witness) : Option.none();
274274
}
275275

276+
/** Returns whether each block hash is present in the archive tree at the referenced block. */
277+
public async areBlockHashesInArchive(
278+
referenceBlockHash: BlockHash,
279+
blockHashes: EphemeralArray<BlockHash>,
280+
): Promise<EphemeralArray<boolean>> {
281+
const hashes = blockHashes.readAll(this.ephemeralArrayService);
282+
const memberships = await this.#queryWithBlockHashNotAfterAnchor(referenceBlockHash, () =>
283+
Promise.all(
284+
hashes.map(blockHash =>
285+
this.aztecNodeReadCache.getBlockHashMembershipWitness(referenceBlockHash, blockHash).then(Boolean),
286+
),
287+
),
288+
);
289+
return EphemeralArray.fromValues(this.ephemeralArrayService, memberships);
290+
}
291+
276292
/**
277293
* Returns a nullifier membership witness for a given nullifier at a given block.
278294
* @param blockHash - The block hash at which to get the index.

yarn-project/pxe/src/oracle_version.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@
1111
/// if AZTEC_NR_MINOR > PXE_MINOR because if a contract is updated to use a newer Aztec.nr dependency without actually
1212
/// using any of the new oracles then there is no reason to throw.
1313
export const ORACLE_VERSION_MAJOR = 30;
14-
export const ORACLE_VERSION_MINOR = 6;
14+
export const ORACLE_VERSION_MINOR = 7;
1515

1616
/// This hash is computed from the `ORACLE_REGISTRY` declaration (each oracle's name, ordered parameter names and
1717
/// types, and return type) and is used to detect when the oracle interface changes. When it does, you need to either:
1818
/// - increment `ORACLE_VERSION_MAJOR` and reset `ORACLE_VERSION_MINOR` to zero if the change is breaking, or
1919
/// - increment only `ORACLE_VERSION_MINOR` if the change is additive (a new oracle was added).
2020
///
2121
/// These constants must be kept in sync between this file and `noir-projects/aztec-nr/aztec/src/oracle/version.nr`.
22-
export const ORACLE_INTERFACE_HASH = '6094994f539407001d2adce5b6a8792c08ef645ee39813e32390f6208e78bc16';
22+
export const ORACLE_INTERFACE_HASH = '416b0e7d3e6dca5803ebe2a65ea93f1e79759dc39ef8ec4c52eeba5e2b0f3fca';

yarn-project/txe/src/rpc_translator.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,16 @@ export class RPCTranslator {
572572
});
573573
}
574574

575+
// eslint-disable-next-line camelcase
576+
aztec_utl_areBlockHashesInArchive(...inputs: ForeignCallArgs) {
577+
return callTxeHandler({
578+
oracle: 'aztec_utl_areBlockHashesInArchive',
579+
inputs,
580+
handler: ([anchorBlockHash, blockHashes]) =>
581+
this.handlerAsUtility().areBlockHashesInArchive(anchorBlockHash, blockHashes),
582+
});
583+
}
584+
575585
// eslint-disable-next-line camelcase
576586
aztec_utl_getLowNullifierMembershipWitness(...inputs: ForeignCallArgs) {
577587
return callTxeHandler({

0 commit comments

Comments
 (0)