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
1 change: 1 addition & 0 deletions yarn-project/pxe/src/contract_function_simulator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export {
type InputSlot,
type MaybePromise,
type OutputSlot,
type SlotShape,
type TypeMapping,
} from './oracle/oracle_type_mappings.js';
export { ExecutionNoteCache } from './execution_note_cache.js';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
export class Option<T> {
private constructor(
public readonly value: T | undefined,
public readonly template: T | undefined,
// `unknown` (not the descriptor shape) keeps this generic Option decoupled from the serialization layer's

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.

This is so that we can do things like Option.none({ maxLength: ciphertext.maxLength });

I tried passing a Size type all the way from the registry to the Option, but in most cases, a size isn't needed. So I thought the extra type added more noise than it helped

// `{ length }`/`{ maxLength }` size descriptors; only the None-serialization path inspects it.
public readonly size: unknown,
) {}

/**
Expand All @@ -26,25 +28,22 @@ export class Option<T> {
/**
* Construct an absent Option.
*
* When serialized back to ACVM, the `None` case must produce the same number of fields as `Some`.
* For types whose wire size varies per call site (`BoundedVec`, `FixedArray`), pass a `template` so the
* serializer knows how many zero fields to emit. Omit the template when the Option will not be
* re-serialized (e.g. deserialized input params).
*
* @param template - A representative empty `T` whose serialization determines the zero-filled wire format.
* When serialized back to ACVM, the `None` case must produce the same number of fields as `Some`. For an inner type whose
* wire size varies per call site (`BoundedVec`, an array), pass a `size` descriptor so the inner type's shape can
* resolve how many zero fields to emit; fixed-size inners take no argument.
*
* @example None for a fixed-size type:
* ```ts
* return Option.none(AztecAddress.ZERO);
* return Option.none();
* ```
*
* @example None for a dynamic-size type:
* ```ts
* return Option.none(BoundedVec.empty<number>({ maxLength: ciphertext.maxLength }));
* return Option.none({ maxLength: ciphertext.maxLength });
* ```
*/
static none<T>(template?: T): Option<T> {
return new Option<T>(undefined, template);
static none<T>(size?: unknown): Option<T> {
return new Option<T>(undefined, size);
}

/**
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import {
U32,
UTILITY_CONTEXT,
assertReadersConsumed,
slotsOf,
} from './oracle_type_mappings.js';

export {
Expand All @@ -59,6 +60,7 @@ export {
BOUNDED_VEC,
BUFFER,
BYTE,
CONTRACT_CLASS_LOG_INPUT,
DELIVERY_MODE,
EPHEMERAL_ARRAY,
EVENT_VALIDATION_REQUEST,
Expand All @@ -75,9 +77,11 @@ export {
PROVIDED_SECRET,
STR,
U32,
slotsOf,
type InputSlot,
type MaybePromise,
type OutputSlot,
type SlotShape,
type TypeMapping,
} from './oracle_type_mappings.js';

Expand All @@ -103,7 +107,10 @@ export const ORACLE_REGISTRY = {
aztec_utl_getUtilityContext: makeEntry({ returnType: UTILITY_CONTEXT }),

aztec_utl_getKeyValidationRequest: makeEntry({
params: [{ name: 'pkMHash', type: FIELD }],
params: [
{ name: 'pkMHash', type: FIELD },
{ name: 'keyIndex', type: FIELD },

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.

The keyIndex was not used, but it was present on the oracle. So I added it so that tests passed

],
returnType: KEY_VALIDATION_REQUEST,
}),

Expand Down Expand Up @@ -540,12 +547,12 @@ export function makeEntry<const TParams extends RegistryParam[] = [], TReturnVal
const resolvedParams: RegistryParam[] = params ?? [];
// Walk the input slots left-to-right, advancing by each param's slot count.
let offset = 0;
return resolvedParams.map(param => {
const named = resolvedParams.map(param => {
if (!param.type.deserialization) {
throw new Error(`Param '${param.name}' has no deserialization defined`);
}
// Collect the slots for this param and wrap each in a FieldReader.
const slotCount = param.type.deserialization.slots;
const slotCount = slotsOf(param.type);
const readers = inputs
.slice(offset, offset + slotCount)
.map(slot => new FieldReader(slot.map(hex => Fr.fromString(hex))));
Expand All @@ -555,7 +562,13 @@ export function makeEntry<const TParams extends RegistryParam[] = [], TReturnVal
const value = param.type.deserialization.fn(readers);
assertReadersConsumed(readers);
return { name: param.name, value };
}) as unknown as InferDeserializedParams<TParams>;
});
// Every input slot must be specified by a param: oracles whose Noir decl passes an extra field must declare it
// (the handler can ignore it). Otherwise an under-declared shape would silently drop a field into nothing.
if (offset !== inputs.length) {

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.

This new check asserts we are not messing up

throw new Error(`Oracle received ${inputs.length} input slot(s) but the registry specifies ${offset}`);
}
return named as unknown as InferDeserializedParams<TParams>;
},
serializeReturn(result: TReturnValue): OutputSlot[] {
if (returnType?.serialization === undefined) {
Expand Down
Loading
Loading