Skip to content

Commit

Permalink
refactor: replacing use of capsules 1.0 with pxe_db + nuking capsules…
Browse files Browse the repository at this point in the history
… 1.0 (#11885)
  • Loading branch information
benesjan authored Feb 11, 2025
1 parent 4146496 commit 72be678
Show file tree
Hide file tree
Showing 22 changed files with 76 additions and 134 deletions.
1 change: 1 addition & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ yarn-error.log*

/docs/developers/reference/aztecjs
/docs/developers/reference/smart_contract_reference/aztec-nr
/docs/docs/protocol-specs/public-vm/gen
test-results
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,15 @@ sidebar_position: 5
tags: [functions, oracles]
---

`popCapsule` is used for passing artbitrary data. We have not yet included this in Aztec.nr, so it is a bit more complex than the other oracles. You can follow this how-to:
`popCapsule` is used for passing arbitrary data. We have not yet included this in Aztec.nr, so it is a bit more complex than the other oracles. You can follow this how-to:

### 1. Define the pop_capsule function

In a new file on the same level as your `main.nr`, implement an unconstrained function that calls the pop_capsule oracle:

#include_code pop_capsule noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/capsule.nr rust

### 2. Import this into your smart contract
### 1. Import capsules into your smart contract

If it lies in the same directory as your smart contract, you can import it like this:

#include_code import_pop_capsule noir-projects/noir-contracts/contracts/contract_class_registerer_contract/src/main.nr rust

### 3. Use it as any other oracle
### 2. Use it as any other oracle

Now it becomes a regular oracle you can call like this:

Expand Down
1 change: 1 addition & 0 deletions l1-contracts/src/core/libraries/ConstantsGen.sol
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ library Constants {
uint256 internal constant MULTI_CALL_ENTRYPOINT_ADDRESS = 4;
uint256 internal constant FEE_JUICE_ADDRESS = 5;
uint256 internal constant ROUTER_ADDRESS = 6;
uint256 internal constant REGISTERER_CONTRACT_BYTECODE_CAPSULE_SLOT = 79025834455612;
uint256 internal constant FEE_JUICE_BALANCES_SLOT = 1;
uint256 internal constant DEFAULT_NPK_M_X =
582240093077765400562621227108555700500271598878376310175765873770292988861;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
mod events;
mod capsule;

use dep::aztec::macros::aztec;

Expand All @@ -11,7 +10,7 @@ pub contract ContractClassRegisterer {
ARTIFACT_FUNCTION_TREE_MAX_HEIGHT, FUNCTION_TREE_HEIGHT,
MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS,
MAX_PACKED_BYTECODE_SIZE_PER_UNCONSTRAINED_FUNCTION_IN_FIELDS,
MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS,
MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS, REGISTERER_CONTRACT_BYTECODE_CAPSULE_SLOT,
},
contract_class_id::ContractClassId,
};
Expand All @@ -33,8 +32,7 @@ pub contract ContractClassRegisterer {
};

// docs:start:import_pop_capsule
use crate::capsule::pop_capsule;

use dep::aztec::oracle::pxe_db;
// docs:end:import_pop_capsule

#[private]
Expand All @@ -47,8 +45,13 @@ pub contract ContractClassRegisterer {
// TODO: Validate public_bytecode_commitment is the correct commitment of packed_public_bytecode
// TODO: We should be able to remove public_bytecode_commitment from the input if it's calculated in this function
// docs:start:pop_capsule
let packed_public_bytecode: [Field; MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS] =
unsafe { pop_capsule() };
let packed_public_bytecode: [Field; MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS] = unsafe {
pxe_db::load(
context.this_address(),
REGISTERER_CONTRACT_BYTECODE_CAPSULE_SLOT,
)
.unwrap()
};
// docs:end:pop_capsule
// First field element contains the length of the bytecode
let bytecode_length_in_bytes: u32 = packed_public_bytecode[0] as u32;
Expand Down Expand Up @@ -121,8 +124,13 @@ pub contract ContractClassRegisterer {
artifact_function_tree_leaf_index: Field,
function_data: InnerPrivateFunction,
) {
let private_bytecode: [Field; MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS] =
unsafe { pop_capsule() };
let private_bytecode: [Field; MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS] = unsafe {
pxe_db::load(
context.this_address(),
REGISTERER_CONTRACT_BYTECODE_CAPSULE_SLOT,
)
.unwrap()
};

let event = ClassPrivateFunctionBroadcasted {
contract_class_id,
Expand Down Expand Up @@ -162,8 +170,13 @@ pub contract ContractClassRegisterer {
artifact_function_tree_leaf_index: Field,
function_data: InnerUnconstrainedFunction,
) {
let unconstrained_bytecode: [Field; MAX_PACKED_BYTECODE_SIZE_PER_UNCONSTRAINED_FUNCTION_IN_FIELDS] =
unsafe { pop_capsule() };
let unconstrained_bytecode: [Field; MAX_PACKED_BYTECODE_SIZE_PER_UNCONSTRAINED_FUNCTION_IN_FIELDS] = unsafe {
pxe_db::load(
context.this_address(),
REGISTERER_CONTRACT_BYTECODE_CAPSULE_SLOT,
)
.unwrap()
};
let event = ClassUnconstrainedFunctionBroadcasted {
contract_class_id,
artifact_metadata_hash,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@ pub global MULTI_CALL_ENTRYPOINT_ADDRESS: AztecAddress = AztecAddress::from_fiel
pub global FEE_JUICE_ADDRESS: AztecAddress = AztecAddress::from_field(5);
pub global ROUTER_ADDRESS: AztecAddress = AztecAddress::from_field(6);

// Randomly chosen slot for the bytecode capsule.
pub global REGISTERER_CONTRACT_BYTECODE_CAPSULE_SLOT: Field = 79025834455612;

// Slot of the balances map to be hashed with an AztecAddress (map key) to get an actual storage slot.
pub global FEE_JUICE_BALANCES_SLOT: u32 = 1;

Expand Down
15 changes: 13 additions & 2 deletions yarn-project/aztec.js/src/deployment/broadcast_function.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import {
ARTIFACT_FUNCTION_TREE_MAX_HEIGHT,
AztecAddress,
MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS,
REGISTERER_CONTRACT_ADDRESS,
REGISTERER_CONTRACT_BYTECODE_CAPSULE_SLOT,
computeVerificationKeyHash,
createPrivateFunctionMembershipProof,
createUnconstrainedFunctionMembershipProof,
Expand Down Expand Up @@ -57,7 +60,11 @@ export async function broadcastPrivateFunction(
MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS,
);

await wallet.addCapsule(bytecode);
await wallet.addCapsule(
AztecAddress.fromNumber(REGISTERER_CONTRACT_ADDRESS),
new Fr(REGISTERER_CONTRACT_BYTECODE_CAPSULE_SLOT),
bytecode,
);

const registerer = await getRegistererContract(wallet);
return Promise.resolve(
Expand Down Expand Up @@ -115,7 +122,11 @@ export async function broadcastUnconstrainedFunction(
MAX_PACKED_BYTECODE_SIZE_PER_PRIVATE_FUNCTION_IN_FIELDS,
);

await wallet.addCapsule(bytecode);
await wallet.addCapsule(
AztecAddress.fromNumber(REGISTERER_CONTRACT_ADDRESS),
new Fr(REGISTERER_CONTRACT_BYTECODE_CAPSULE_SLOT),
bytecode,
);

const registerer = await getRegistererContract(wallet);
return registerer.methods.broadcast_unconstrained_function(
Expand Down
15 changes: 13 additions & 2 deletions yarn-project/aztec.js/src/deployment/register_class.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS, getContractClassFromArtifact } from '@aztec/circuits.js';
import {
AztecAddress,
Fr,
MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS,
REGISTERER_CONTRACT_ADDRESS,
REGISTERER_CONTRACT_BYTECODE_CAPSULE_SLOT,
getContractClassFromArtifact,
} from '@aztec/circuits.js';
import { type ContractArtifact, bufferAsFields } from '@aztec/foundation/abi';

import { type ContractFunctionInteraction } from '../contract/contract_function_interaction.js';
Expand All @@ -21,6 +28,10 @@ export async function registerContractClass(
await getContractClassFromArtifact(artifact);
const encodedBytecode = bufferAsFields(packedBytecode, MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS);
const registerer = await getRegistererContract(wallet);
await wallet.addCapsule(encodedBytecode);
await wallet.addCapsule(
AztecAddress.fromNumber(REGISTERER_CONTRACT_ADDRESS),
new Fr(REGISTERER_CONTRACT_BYTECODE_CAPSULE_SLOT),
encodedBytecode,
);
return registerer.methods.register(artifactHash, privateFunctionsRoot, publicBytecodeCommitment, emitPublicBytecode);
}
4 changes: 2 additions & 2 deletions yarn-project/aztec.js/src/wallet/base_wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ export abstract class BaseWallet implements Wallet {
getAddress() {
return this.getCompleteAddress().address;
}
addCapsule(capsule: Fr[]): Promise<void> {
return this.pxe.addCapsule(capsule);
addCapsule(contract: AztecAddress, storageSlot: Fr, capsule: Fr[]): Promise<void> {
return this.pxe.addCapsule(contract, storageSlot, capsule);
}
registerAccount(secretKey: Fr, partialAddress: PartialAddress): Promise<CompleteAddress> {
return this.pxe.registerAccount(secretKey, partialAddress);
Expand Down
6 changes: 4 additions & 2 deletions yarn-project/circuit-types/src/interfaces/pxe.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ describe('PXESchema', () => {
});

it('addCapsule', async () => {
await context.client.addCapsule(times(3, Fr.random));
await context.client.addCapsule(address, Fr.random(), times(3, Fr.random));
});

it('registerAccount', async () => {
Expand Down Expand Up @@ -344,7 +344,9 @@ class MockPXE implements PXE {
expect(messageHash).toBeInstanceOf(Fr);
return Promise.resolve([Fr.random()]);
}
addCapsule(capsule: Fr[]): Promise<void> {
addCapsule(contract: AztecAddress, storageSlot: Fr, capsule: Fr[]): Promise<void> {
expect(contract).toBeInstanceOf(AztecAddress);
expect(storageSlot).toBeInstanceOf(Fr);
expect(capsule.every(c => c instanceof Fr)).toBeTruthy();
return Promise.resolve();
}
Expand Down
12 changes: 8 additions & 4 deletions yarn-project/circuit-types/src/interfaces/pxe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,15 @@ export interface PXE {
getAuthWitness(messageHash: Fr): Promise<Fr[] | undefined>;

/**
* Adding a capsule to the capsule dispenser.
* Adds a capsule.
* @param contract - The address of the contract to add the capsule to.
* @param storageSlot - The storage slot to add the capsule to.
* @param capsule - An array of field elements representing the capsule.
* @remarks A capsule is a "blob" of data that is passed to the contract through an oracle.
* @remarks A capsule is a "blob" of data that is passed to the contract through an oracle. It works similarly
* to public contract storage in that it's indexed by the contract address and storage slot but instead of the global
* network state it's backed by local PXE db.
*/
addCapsule(capsule: Fr[]): Promise<void>;
addCapsule(contract: AztecAddress, storageSlot: Fr, capsule: Fr[]): Promise<void>;

/**
* Registers a user account in PXE given its master encryption private key.
Expand Down Expand Up @@ -464,7 +468,7 @@ export const PXESchema: ApiSchemaFor<PXE> = {
.function()
.args(schemas.Fr)
.returns(z.union([z.undefined(), z.array(schemas.Fr)])),
addCapsule: z.function().args(z.array(schemas.Fr)).returns(z.void()),
addCapsule: z.function().args(schemas.AztecAddress, schemas.Fr, z.array(schemas.Fr)).returns(z.void()),
registerAccount: z.function().args(schemas.Fr, schemas.Fr).returns(CompleteAddress.schema),
getRegisteredAccounts: z.function().returns(z.array(CompleteAddress.schema)),
registerSender: z.function().args(schemas.AztecAddress).returns(schemas.AztecAddress),
Expand Down
1 change: 1 addition & 0 deletions yarn-project/circuits.js/src/constants.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ export const REGISTERER_CONTRACT_ADDRESS = 3;
export const MULTI_CALL_ENTRYPOINT_ADDRESS = 4;
export const FEE_JUICE_ADDRESS = 5;
export const ROUTER_ADDRESS = 6;
export const REGISTERER_CONTRACT_BYTECODE_CAPSULE_SLOT = 79025834455612;
export const FEE_JUICE_BALANCES_SLOT = 1;
export const DEFAULT_NPK_M_X = 582240093077765400562621227108555700500271598878376310175765873770292988861n;
export const DEFAULT_NPK_M_Y = 10422444662424639723529825114205836958711284159673861467999592572974769103684n;
Expand Down
13 changes: 1 addition & 12 deletions yarn-project/pxe/src/database/kv_pxe_database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ export class KVPxeDatabase implements PxeDatabase {
#completeAddressIndex: AztecAsyncMap<string, number>;
#addressBook: AztecAsyncSet<string>;
#authWitnesses: AztecAsyncMap<string, Buffer[]>;
#capsules: AztecAsyncArray<Buffer[]>;
#notes: AztecAsyncMap<string, Buffer>;
#nullifiedNotes: AztecAsyncMap<string, Buffer>;
#nullifierToNoteId: AztecAsyncMap<string, string>;
Expand Down Expand Up @@ -78,7 +77,6 @@ export class KVPxeDatabase implements PxeDatabase {
this.#addressBook = db.openSet('address_book');

this.#authWitnesses = db.openMap('auth_witnesses');
this.#capsules = db.openArray('capsules');

this.#contractArtifacts = db.openMap('contract_artifacts');
this.#contractInstances = db.openMap('contracts_instances');
Expand Down Expand Up @@ -189,15 +187,6 @@ export class KVPxeDatabase implements PxeDatabase {
return Promise.resolve(witness?.map(w => Fr.fromBuffer(w)));
}

async addCapsule(capsule: Fr[]): Promise<void> {
await this.#capsules.push(capsule.map(c => c.toBuffer()));
}

async popCapsule(): Promise<Fr[] | undefined> {
const val = await this.#capsules.pop();
return val?.map(b => Fr.fromBuffer(b));
}

async addNote(note: NoteDao, scope?: AztecAddress): Promise<void> {
await this.addNotes([note], scope);
}
Expand Down Expand Up @@ -652,7 +641,7 @@ export class KVPxeDatabase implements PxeDatabase {
}

async dbCopy(contractAddress: AztecAddress, srcSlot: Fr, dstSlot: Fr, numEntries: number): Promise<void> {
// In order to support overlaping source and destination regions we need to check the relative positions of source
// In order to support overlapping source and destination regions, we need to check the relative positions of source
// and destination. If destination is ahead of source, then by the time we overwrite source elements using forward
// indexes we'll have already read those. On the contrary, if source is ahead of destination we need to use backward
// indexes to avoid reading elements that've been overwritten.
Expand Down
16 changes: 1 addition & 15 deletions yarn-project/pxe/src/database/pxe_database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,6 @@ export interface PxeDatabase extends ContractArtifactDatabase, ContractInstanceD
*/
getAuthWitness(messageHash: Fr): Promise<Fr[] | undefined>;

/**
* Adding a capsule to the capsule dispenser.
* @remarks A capsule is a "blob" of data that is passed to the contract through an oracle.
* @param capsule - An array of field elements representing the capsule.
*/
addCapsule(capsule: Fr[]): Promise<void>;

/**
* Get the next capsule from the capsule dispenser.
* @remarks A capsule is a "blob" of data that is passed to the contract through an oracle.
* @returns A promise that resolves to an array of field elements representing the capsule.
*/
popCapsule(): Promise<Fr[] | undefined>;

/**
* Gets notes based on the provided filter.
* @param filter - The filter to apply to the notes.
Expand Down Expand Up @@ -216,7 +202,7 @@ export interface PxeDatabase extends ContractArtifactDatabase, ContractInstanceD

/**
* Stores arbitrary information in a per-contract non-volatile database, which can later be retrieved with `dbLoad`.
* If data was already stored at this slot, it is overwrriten.
* If data was already stored at this slot, it is overwritten.
* @param contractAddress - The contract address to scope the data under.
* @param slot - The slot in the database in which to store the value. Slots need not be contiguous.
* @param values - The data to store.
Expand Down
23 changes: 0 additions & 23 deletions yarn-project/pxe/src/database/pxe_database_test_suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,29 +54,6 @@ export function describePxeDatabase(getDatabase: () => PxeDatabase) {
});
});

describe('capsules', () => {
it('stores and retrieves capsules', async () => {
const capsule = [Fr.random(), Fr.random()];

await database.addCapsule(capsule);
await expect(database.popCapsule()).resolves.toEqual(capsule);
});

it("returns undefined if it doesn't have capsules", async () => {
await expect(database.popCapsule()).resolves.toBeUndefined();
});

it('behaves like a stack when storing capsules', async () => {
const capsule1 = [Fr.random(), Fr.random()];
const capsule2 = [Fr.random(), Fr.random()];

await database.addCapsule(capsule1);
await database.addCapsule(capsule2);
await expect(database.popCapsule()).resolves.toEqual(capsule2);
await expect(database.popCapsule()).resolves.toEqual(capsule1);
});
});

describe('incoming notes', () => {
let owners: CompleteAddress[];
let contractAddresses: AztecAddress[];
Expand Down
4 changes: 2 additions & 2 deletions yarn-project/pxe/src/pxe_service/pxe_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ export class PXEService implements PXE {
return this.db.getAuthWitness(messageHash);
}

public addCapsule(capsule: Fr[]) {
return this.db.addCapsule(capsule);
public addCapsule(contract: AztecAddress, storageSlot: Fr, capsule: Fr[]) {
return this.db.dbStore(contract, storageSlot, capsule);
}

public getContractInstance(address: AztecAddress): Promise<ContractInstanceWithAddress | undefined> {
Expand Down
8 changes: 0 additions & 8 deletions yarn-project/pxe/src/simulator_oracle/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,6 @@ export class SimulatorOracle implements DBOracle {
return witness;
}

async popCapsule(): Promise<Fr[]> {
const capsule = await this.db.popCapsule();
if (!capsule) {
throw new Error(`No capsules available`);
}
return capsule;
}

async getNotes(contractAddress: AztecAddress, storageSlot: Fr, status: NoteStatus, scopes?: AztecAddress[]) {
const noteDaos = await this.db.getNotes({
contractAddress,
Expand Down
Loading

0 comments on commit 72be678

Please sign in to comment.