Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Lazy wasm pt3 #11435

Merged
merged 4 commits into from
Jan 23, 2025
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
Expand Up @@ -15,17 +15,21 @@ export class HeapAllocator {

constructor(private wasm: BarretenbergWasmMain) {}

copyToMemory(buffers: Uint8Array[]) {
return buffers.map(buf => {
if (buf.length <= this.inScratchRemaining) {
const ptr = (this.inScratchRemaining -= buf.length);
this.wasm.writeMemory(ptr, buf);
return ptr;
getInputs(buffers: (Uint8Array | number)[]) {
return buffers.map(bufOrNum => {
if (typeof bufOrNum === 'object') {
if (bufOrNum.length <= this.inScratchRemaining) {
const ptr = (this.inScratchRemaining -= bufOrNum.length);
this.wasm.writeMemory(ptr, bufOrNum);
return ptr;
} else {
const ptr = this.wasm.call('bbmalloc', bufOrNum.length);
this.wasm.writeMemory(ptr, bufOrNum);
this.allocs.push(ptr);
return ptr;
}
} else {
const ptr = this.wasm.call('bbmalloc', buf.length);
this.wasm.writeMemory(ptr, buf);
this.allocs.push(ptr);
return ptr;
return bufOrNum;
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,9 @@ export class BarretenbergWasmMain extends BarretenbergWasmBase {
/* eslint-enable camelcase */
}

callWasmExport(funcName: string, inArgs: Uint8Array[], outLens: (number | undefined)[]) {
callWasmExport(funcName: string, inArgs: (Uint8Array | number)[], outLens: (number | undefined)[]) {
const alloc = new HeapAllocator(this);
const inPtrs = alloc.copyToMemory(inArgs);
const inPtrs = alloc.getInputs(inArgs);
Copy link
Contributor

Choose a reason for hiding this comment

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

So inPtrs can now just be numbers that are passed in from above ? If inArgs are numbers it seems like they are simply returned

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes! basically before this method only allowed passing pointers, but in some cases we can just send a value. It's updated to reflect that, since on the CPP side we can read values directly.

const outPtrs = alloc.getOutputPtrs(outLens);
this.call(funcName, ...inPtrs, ...outPtrs);
const outArgs = this.getOutputArgs(outLens, outPtrs, alloc);
Expand Down
1 change: 1 addition & 0 deletions yarn-project/accounts/src/ecdsa/ecdsa_k/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export { EcdsaKAccountContract };
* @param secretKey - Secret key used to derive all the keystore keys.
* @param signingPrivateKey - Secp256k1 key used for signing transactions.
* @param salt - Deployment salt.
* @returns An account manager initialized with the account contract and its deployment params
*/
export function getEcdsaKAccount(
pxe: PXE,
Expand Down
1 change: 1 addition & 0 deletions yarn-project/accounts/src/ecdsa/ssh_ecdsa_r/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export { EcdsaRSSHAccountContract };
* @param secretKey - Secret key used to derive all the keystore keys.
* @param signingPublicKey - Secp2561 key used to identify its corresponding private key in the SSH Agent.
* @param salt - Deployment salt.
* @returns An account manager initialized with the account contract and its deployment params
*/
export function getEcdsaRSSHAccount(
pxe: PXE,
Expand Down
1 change: 1 addition & 0 deletions yarn-project/accounts/src/schnorr/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export { SchnorrAccountContractArtifact } from './artifact.js';
* @param secretKey - Secret key used to derive all the keystore keys.
* @param signingPrivateKey - Grumpkin key used for signing transactions.
* @param salt - Deployment salt.
* @returns An account manager initialized with the account contract and its deployment params
*/
export function getSchnorrAccount(
pxe: PXE,
Expand Down
1 change: 1 addition & 0 deletions yarn-project/accounts/src/single_key/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export { SchnorrSingleKeyAccountContractArtifact as SingleKeyAccountContractArti
* @param pxe - An PXE server instance.
* @param secretKey - Secret key used to derive all the keystore keys (in this case also used to get signing key).
* @param salt - Deployment salt.
* @returns An account manager initialized with the account contract and its deployment params
Thunkar marked this conversation as resolved.
Show resolved Hide resolved
*/
export function getSingleKeyAccount(pxe: PXE, secretKey: Fr, salt?: Salt) {
const encryptionPrivateKey = deriveMasterIncomingViewingSecretKey(secretKey);
Expand Down
32 changes: 20 additions & 12 deletions yarn-project/accounts/src/testing/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,21 +48,29 @@ export function getInitialTestAccountsWallets(pxe: PXE): Promise<AccountWalletWi
*/
export async function getDeployedTestAccountsWallets(pxe: PXE): Promise<AccountWalletWithSecretKey[]> {
const registeredAccounts = await pxe.getRegisteredAccounts();
return Promise.all(
INITIAL_TEST_SECRET_KEYS.filter(initialSecretKey => {
const publicKeys = await Promise.all(
INITIAL_TEST_SECRET_KEYS.map(async initialSecretKey => {
const initialEncryptionKey = deriveMasterIncomingViewingSecretKey(initialSecretKey);
const publicKey = generatePublicKey(initialEncryptionKey);
return (
registeredAccounts.find(registered => registered.publicKeys.masterIncomingViewingPublicKey.equals(publicKey)) !=
undefined
);
}).map(async secretKey => {
const signingKey = deriveSigningKey(secretKey);
// TODO(#5726): use actual salt here instead of hardcoding Fr.ZERO
const account = await getSchnorrAccount(pxe, secretKey, signingKey, Fr.ZERO);
return account.getWallet();
const publicKey = await generatePublicKey(initialEncryptionKey);
return { sk: initialSecretKey, pk: publicKey };
}),
);
return Promise.all(
publicKeys
.filter(keyPairs => {
return (
registeredAccounts.find(registered =>
registered.publicKeys.masterIncomingViewingPublicKey.equals(keyPairs.pk),
) != undefined
);
})
.map(async keyPairs => {
const signingKey = deriveSigningKey(keyPairs.sk);
// TODO(#5726): use actual salt here instead of hardcoding Fr.ZERO
const account = await getSchnorrAccount(pxe, keyPairs.sk, signingKey, Fr.ZERO);
return account.getWallet();
}),
);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/archiver/src/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export async function createArchiver(
async function registerProtocolContracts(store: KVArchiverDataStore) {
const blockNumber = 0;
for (const name of protocolContractNames) {
const contract = getCanonicalProtocolContract(name);
const contract = await getCanonicalProtocolContract(name);
const contractClassPublic: ContractClassPublic = {
...contract.contractClass,
privateFunctions: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export class DeployAccountMethod extends DeployMethod {
const exec = await super.getInitializeFunctionCalls(options);

if (options.fee && this.#feePaymentArtifact) {
const { address } = this.getInstance();
const { address } = await this.getInstance();
const emptyAppPayload = EntrypointPayload.fromAppExecution([]);
const fee = await this.getDefaultFeeOptions(options.fee);
const feePayload = await EntrypointPayload.fromFeeOptions(address, fee);
Expand Down
18 changes: 10 additions & 8 deletions yarn-project/aztec.js/src/account_manager/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ export class AccountManager {
) {}

static async create(pxe: PXE, secretKey: Fr, accountContract: AccountContract, salt?: Salt) {
const { publicKeys } = deriveKeys(secretKey);
const { publicKeys } = await deriveKeys(secretKey);
salt = salt !== undefined ? new Fr(salt) : Fr.random();

const instance = getContractInstanceFromDeployParams(accountContract.getContractArtifact(), {
const instance = await getContractInstanceFromDeployParams(accountContract.getContractArtifact(), {
constructorArgs: await accountContract.getDeploymentArgs(),
salt: salt,
publicKeys,
Expand All @@ -63,7 +63,7 @@ export class AccountManager {
*/
public async getAccount(): Promise<AccountInterface> {
const nodeInfo = await this.pxe.getNodeInfo();
const completeAddress = this.getCompleteAddress();
const completeAddress = await this.getCompleteAddress();
return this.accountContract.getInterface(completeAddress, nodeInfo);
}

Expand All @@ -72,7 +72,7 @@ export class AccountManager {
* Does not require the account to be deployed or registered.
* @returns The address, partial address, and encryption public key.
*/
public getCompleteAddress(): CompleteAddress {
public getCompleteAddress(): Promise<CompleteAddress> {
return CompleteAddress.fromSecretKeyAndInstance(this.secretKey, this.instance);
}

Expand All @@ -82,7 +82,7 @@ export class AccountManager {
* @returns The address.
*/
public getAddress() {
return this.getCompleteAddress().address;
return this.instance.address;
}

/**
Expand Down Expand Up @@ -117,7 +117,7 @@ export class AccountManager {
instance: this.getInstance(),
});

await this.pxe.registerAccount(this.secretKey, this.getCompleteAddress().partialAddress);
await this.pxe.registerAccount(this.secretKey, (await this.getCompleteAddress()).partialAddress);

return this.getWallet();
}
Expand All @@ -135,7 +135,9 @@ export class AccountManager {
);
}

await this.pxe.registerAccount(this.secretKey, this.getCompleteAddress().partialAddress);
const completeAddress = await this.getCompleteAddress();

await this.pxe.registerAccount(this.secretKey, completeAddress.partialAddress);

const { l1ChainId: chainId, protocolVersion } = await this.pxe.getNodeInfo();
const deployWallet = new SignerlessWallet(this.pxe, new DefaultMultiCallEntrypoint(chainId, protocolVersion));
Expand All @@ -145,7 +147,7 @@ export class AccountManager {
// and it can't be used unless the contract is initialized
const args = (await this.accountContract.getDeploymentArgs()) ?? [];
return new DeployAccountMethod(
this.accountContract.getAuthWitnessProvider(this.getCompleteAddress()),
this.accountContract.getAuthWitnessProvider(completeAddress),
this.getPublicKeys(),
deployWallet,
this.accountContract.getContractArtifact(),
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/aztec.js/src/contract/contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ describe('Contract Class', () => {

beforeEach(async () => {
contractAddress = await AztecAddress.random();
account = CompleteAddress.random();
account = await CompleteAddress.random();
contractInstance = { address: contractAddress } as ContractInstanceWithAddress;

const mockNodeInfo: NodeInfo = {
Expand Down
31 changes: 15 additions & 16 deletions yarn-project/aztec.js/src/contract/deploy_method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export class DeployMethod<TContract extends ContractBase = Contract> extends Bas
// in case the initializer is public. This hints at the need of having "transient" contracts scoped to a
// simulation, so we can run the simulation with a set of contracts, but only "commit" them to the wallet
// once this tx has gone through.
await this.wallet.registerContract({ artifact: this.artifact, instance: this.getInstance(options) });
await this.wallet.registerContract({ artifact: this.artifact, instance: await this.getInstance(options) });

const bootstrap = await this.getInitializeFunctionCalls(options);

Expand All @@ -124,7 +124,7 @@ export class DeployMethod<TContract extends ContractBase = Contract> extends Bas
* @param options - Deployment options.
*/
public async register(options: DeployOptions = {}): Promise<TContract> {
const instance = this.getInstance(options);
const instance = await this.getInstance(options);
await this.wallet.registerContract({ artifact: this.artifact, instance });
return this.postDeployCtor(instance.address, this.wallet);
}
Expand All @@ -140,7 +140,7 @@ export class DeployMethod<TContract extends ContractBase = Contract> extends Bas
const calls: FunctionCall[] = [];

// Set contract instance object so it's available for populating the DeploySendTx object
const instance = this.getInstance(options);
const instance = await this.getInstance(options);

// Obtain contract class from artifact and check it matches the reported one by the instance.
// TODO(@spalladino): We're unnecessarily calculating the contract class multiple times here.
Expand All @@ -167,7 +167,8 @@ export class DeployMethod<TContract extends ContractBase = Contract> extends Bas

// Deploy the contract via the instance deployer.
if (!options.skipPublicDeployment) {
calls.push(deployInstance(this.wallet, instance).request());
const deploymentInteraction = await deployInstance(this.wallet, instance);
calls.push(deploymentInteraction.request());
}

return { calls };
Expand All @@ -178,10 +179,10 @@ export class DeployMethod<TContract extends ContractBase = Contract> extends Bas
* @param options - Deployment options.
* @returns - An array of function calls.
*/
protected getInitializeFunctionCalls(
protected async getInitializeFunctionCalls(
options: DeployOptions,
): Promise<Pick<ExecutionRequestInit, 'calls' | 'authWitnesses' | 'hashedArguments'>> {
const { address } = this.getInstance(options);
const { address } = await this.getInstance(options);
const calls: FunctionCall[] = [];
if (this.constructorArtifact && !options.skipInitialization) {
const constructorCall = new ContractFunctionInteraction(
Expand All @@ -192,7 +193,7 @@ export class DeployMethod<TContract extends ContractBase = Contract> extends Bas
);
calls.push(constructorCall.request());
}
return Promise.resolve({ calls });
return { calls };
}

/**
Expand All @@ -205,11 +206,8 @@ export class DeployMethod<TContract extends ContractBase = Contract> extends Bas
*/
public override send(options: DeployOptions = {}): DeploySentTx<TContract> {
const txHashPromise = super.send(options).getTxHash();
const instance = this.getInstance(options);
this.log.debug(
`Sent deployment tx of ${this.artifact.name} contract with deployment address ${instance.address.toString()}`,
);
return new DeploySentTx(this.wallet, txHashPromise, this.postDeployCtor, instance);
this.log.debug(`Sent deployment tx of ${this.artifact.name} contract`);
return new DeploySentTx(this.wallet, txHashPromise, this.postDeployCtor, () => this.getInstance(options));
}

/**
Expand All @@ -218,9 +216,9 @@ export class DeployMethod<TContract extends ContractBase = Contract> extends Bas
* @param options - An object containing various deployment options.
* @returns An instance object.
*/
public getInstance(options: DeployOptions = {}): ContractInstanceWithAddress {
public async getInstance(options: DeployOptions = {}): Promise<ContractInstanceWithAddress> {
if (!this.instance) {
this.instance = getContractInstanceFromDeployParams(this.artifact, {
this.instance = await getContractInstanceFromDeployParams(this.artifact, {
constructorArgs: this.args,
salt: options.contractAddressSalt,
publicKeys: this.publicKeys,
Expand All @@ -238,8 +236,9 @@ export class DeployMethod<TContract extends ContractBase = Contract> extends Bas
*/
public override async prove(options: DeployOptions): Promise<DeployProvenTx<TContract>> {
const txProvingResult = await this.proveInternal(options);
const instance = this.getInstance(options);
return new DeployProvenTx(this.wallet, txProvingResult.toTx(), this.postDeployCtor, instance);
return new DeployProvenTx(this.wallet, txProvingResult.toTx(), this.postDeployCtor, () =>
this.getInstance(options),
);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions yarn-project/aztec.js/src/contract/deploy_proven_tx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export class DeployProvenTx<TContract extends Contract = Contract> extends Prove
wallet: PXE | Wallet,
tx: Tx,
private postDeployCtor: (address: AztecAddress, wallet: Wallet) => Promise<TContract>,
private instance: ContractInstanceWithAddress,
private instanceGetter: () => Promise<ContractInstanceWithAddress>,
) {
super(wallet, tx);
}
Expand All @@ -27,6 +27,6 @@ export class DeployProvenTx<TContract extends Contract = Contract> extends Prove
return this.wallet.sendTx(this.getPlainDataTx());
})();

return new DeploySentTx(this.wallet, promise, this.postDeployCtor, this.instance);
return new DeploySentTx(this.wallet, promise, this.postDeployCtor, this.instanceGetter);
}
}
12 changes: 7 additions & 5 deletions yarn-project/aztec.js/src/contract/deploy_sent_tx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ export class DeploySentTx<TContract extends Contract = Contract> extends SentTx
wallet: PXE | Wallet,
txHashPromise: Promise<TxHash>,
private postDeployCtor: (address: AztecAddress, wallet: Wallet) => Promise<TContract>,
/** The deployed contract instance */
public instance: ContractInstanceWithAddress,
/** A getter for the deployed contract instance */
public instanceGetter: () => Promise<ContractInstanceWithAddress>,
) {
super(wallet, txHashPromise);
}
Expand All @@ -43,7 +43,8 @@ export class DeploySentTx<TContract extends Contract = Contract> extends SentTx
*/
public async deployed(opts?: DeployedWaitOpts): Promise<TContract> {
const receipt = await this.wait(opts);
this.log.info(`Contract ${this.instance.address.toString()} successfully deployed.`);
const instance = await this.instanceGetter();
this.log.info(`Contract ${instance.address.toString()} successfully deployed.`);
return receipt.contract;
}

Expand All @@ -58,12 +59,13 @@ export class DeploySentTx<TContract extends Contract = Contract> extends SentTx
return { ...receipt, contract };
}

protected getContractObject(wallet?: Wallet): Promise<TContract> {
protected async getContractObject(wallet?: Wallet): Promise<TContract> {
const isWallet = (pxe: PXE | Wallet): pxe is Wallet => !!(pxe as Wallet).createTxExecutionRequest;
const contractWallet = wallet ?? (isWallet(this.pxe) && this.pxe);
if (!contractWallet) {
throw new Error(`A wallet is required for creating a contract instance`);
}
return this.postDeployCtor(this.instance.address, contractWallet) as Promise<TContract>;
const instance = await this.instanceGetter();
return this.postDeployCtor(instance.address, contractWallet) as Promise<TContract>;
}
}
4 changes: 2 additions & 2 deletions yarn-project/aztec.js/src/deployment/broadcast_function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export async function broadcastPrivateFunction(

await wallet.addCapsule(bytecode);

const registerer = getRegistererContract(wallet);
const registerer = await getRegistererContract(wallet);
return Promise.resolve(
registerer.methods.broadcast_private_function(
contractClass.id,
Expand Down Expand Up @@ -103,7 +103,7 @@ export async function broadcastUnconstrainedFunction(

await wallet.addCapsule(bytecode);

const registerer = getRegistererContract(wallet);
const registerer = await getRegistererContract(wallet);
return registerer.methods.broadcast_unconstrained_function(
contractClass.id,
artifactMetadataHash,
Expand Down
7 changes: 5 additions & 2 deletions yarn-project/aztec.js/src/deployment/deploy_instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ import { getDeployerContract } from './protocol_contracts.js';
* @param wallet - The wallet to use for the deployment.
* @param instance - The instance to deploy.
*/
export function deployInstance(wallet: Wallet, instance: ContractInstanceWithAddress): ContractFunctionInteraction {
const deployerContract = getDeployerContract(wallet);
export async function deployInstance(
wallet: Wallet,
instance: ContractInstanceWithAddress,
): Promise<ContractFunctionInteraction> {
const deployerContract = await getDeployerContract(wallet);
const { salt, contractClassId, publicKeys, deployer } = instance;
const isUniversalDeploy = deployer.isZero();
if (!isUniversalDeploy && !wallet.getAddress().equals(deployer)) {
Expand Down
Loading
Loading