Skip to content

Commit

Permalink
feat(aztec-js): remove sender from execution request and add batching (
Browse files Browse the repository at this point in the history
…#1415)

A few clean ups in aztec-js:
- Remove `from` in `ExecutionRequest` and rename it to `FunctionCall` to
avoid confusion with `TxExecutionRequest` and
`CallStackItem.isExecutionRequest`.
- Remove `TxContext` argument from `AccountImp.createAuthenticatedTx`
and rename it to `createTxExecutionRequest`.
- Remove unused `nonce` in method send options.
- Remove unused function to get a contract instance in deploy method
given we now have #1360.
- Extracts a base class for `contract_function_interaction` and
`deploy_method` so we can remove the `DeployWallet`.
- Adds a new `BatchCall` method for nicer batch calls. Fixes #1343.
  • Loading branch information
spalladino authored Aug 4, 2023
1 parent 801f6a5 commit 05b6e86
Show file tree
Hide file tree
Showing 27 changed files with 253 additions and 303 deletions.
12 changes: 7 additions & 5 deletions yarn-project/acir-simulator/src/client/simulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { AztecAddress } from '@aztec/foundation/aztec-address';
import { EthAddress } from '@aztec/foundation/eth-address';
import { Fr } from '@aztec/foundation/fields';
import { DebugLogger, createDebugLogger } from '@aztec/foundation/log';
import { ExecutionRequest, TxExecutionRequest } from '@aztec/types';
import { FunctionCall, TxExecutionRequest } from '@aztec/types';

import { PackedArgsCache } from '../packed_args_cache.js';
import { ClientTxExecutionContext } from './client_execution_context.js';
Expand Down Expand Up @@ -87,14 +87,16 @@ export class AcirSimulator {
/**
* Runs an unconstrained function.
* @param request - The transaction request.
* @param origin - The sender of the request.
* @param entryPointABI - The ABI of the entry point function.
* @param contractAddress - The address of the contract.
* @param portalContractAddress - The address of the portal contract.
* @param historicRoots - The historic roots.
* @returns The return values of the function.
*/
public async runUnconstrained(
request: ExecutionRequest,
request: FunctionCall,
origin: AztecAddress,
entryPointABI: FunctionAbi,
contractAddress: AztecAddress,
portalContractAddress: EthAddress,
Expand All @@ -104,7 +106,7 @@ export class AcirSimulator {
throw new Error(`Cannot run ${entryPointABI.functionType} function as constrained`);
}
const callContext = new CallContext(
request.from!,
origin,
contractAddress,
portalContractAddress,
false,
Expand Down Expand Up @@ -150,15 +152,15 @@ export class AcirSimulator {
const preimageLen = (abi.parameters[3].type as ArrayType).length;
const extendedPreimage = notePreimage.concat(Array(preimageLen - notePreimage.length).fill(Fr.ZERO));

const execRequest: ExecutionRequest = {
from: AztecAddress.ZERO,
const execRequest: FunctionCall = {
to: AztecAddress.ZERO,
functionData: FunctionData.empty(),
args: encodeArguments(abi, [contractAddress, nonce, storageSlot, extendedPreimage]),
};

const [[innerNoteHash, siloedNoteHash, uniqueSiloedNoteHash, innerNullifier]] = await this.runUnconstrained(
execRequest,
AztecAddress.ZERO,
abi,
AztecAddress.ZERO,
EthAddress.ZERO,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { AztecAddress } from '@aztec/foundation/aztec-address';
import { EthAddress } from '@aztec/foundation/eth-address';
import { Fr } from '@aztec/foundation/fields';
import { ZkTokenContractAbi } from '@aztec/noir-contracts/artifacts';
import { ExecutionRequest } from '@aztec/types';
import { FunctionCall } from '@aztec/types';

import { mock } from 'jest-mock-extended';

Expand Down Expand Up @@ -74,15 +74,15 @@ describe('Unconstrained Execution test suite', () => {
})),
);

const execRequest: ExecutionRequest = {
from: AztecAddress.random(),
const execRequest: FunctionCall = {
to: contractAddress,
functionData: new FunctionData(Buffer.alloc(4), false, true, true),
args: encodeArguments(abi, [owner]),
};

const result = await acirSimulator.runUnconstrained(
execRequest,
AztecAddress.random(),
abi,
AztecAddress.random(),
EthAddress.ZERO,
Expand Down
29 changes: 10 additions & 19 deletions yarn-project/aztec-rpc/src/aztec_rpc_server/aztec_rpc_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
ContractData,
ContractPublicData,
DeployedContract,
ExecutionRequest,
FunctionCall,
KeyStore,
L2BlockL2Logs,
LogType,
Expand Down Expand Up @@ -193,9 +193,8 @@ export class AztecRPCServer implements AztecRPC {
}

public async viewTx(functionName: string, args: any[], to: AztecAddress, from?: AztecAddress) {
const txRequest = await this.#getExecutionRequest(functionName, args, to, from ?? AztecAddress.ZERO);

const executionResult = await this.#simulateUnconstrained(txRequest);
const functionCall = await this.#getFunctionCall(functionName, args, to);
const executionResult = await this.#simulateUnconstrained(functionCall, from);

// TODO - Return typed result based on the function abi.
return executionResult;
Expand Down Expand Up @@ -254,12 +253,7 @@ export class AztecRPCServer implements AztecRPC {
return await this.node.getLogs(from, limit, LogType.UNENCRYPTED);
}

async #getExecutionRequest(
functionName: string,
args: any[],
to: AztecAddress,
from: AztecAddress,
): Promise<ExecutionRequest> {
async #getFunctionCall(functionName: string, args: any[], to: AztecAddress): Promise<FunctionCall> {
const contract = await this.db.getContract(to);
if (!contract) {
throw new Error(`Unknown contract ${to}: add it to Aztec RPC server by calling server.addContracts(...)`);
Expand All @@ -272,7 +266,6 @@ export class AztecRPCServer implements AztecRPC {

return {
args: encodeArguments(functionDao, args),
from,
functionData: FunctionData.fromAbi(functionDao),
to,
};
Expand Down Expand Up @@ -311,10 +304,10 @@ export class AztecRPCServer implements AztecRPC {
* @returns An object containing the contract address, function ABI, portal contract address, and historic tree roots.
*/
async #getSimulationParameters(
execRequest: ExecutionRequest | TxExecutionRequest,
execRequest: FunctionCall | TxExecutionRequest,
contractDataOracle: ContractDataOracle,
) {
const contractAddress = (execRequest as ExecutionRequest).to ?? (execRequest as TxExecutionRequest).origin;
const contractAddress = (execRequest as FunctionCall).to ?? (execRequest as TxExecutionRequest).origin;
const functionAbi = await contractDataOracle.getFunctionAbi(
contractAddress,
execRequest.functionData.functionSelectorBuffer,
Expand Down Expand Up @@ -369,14 +362,11 @@ export class AztecRPCServer implements AztecRPC {
* Returns the simulation result containing the outputs of the unconstrained function.
*
* @param execRequest - The transaction request object containing the target contract and function data.
* @param contractDataOracle - Optional instance of ContractDataOracle for fetching and caching contract information.
* @param from - The origin of the request.
* @returns The simulation result containing the outputs of the unconstrained function.
*/
async #simulateUnconstrained(execRequest: ExecutionRequest, contractDataOracle?: ContractDataOracle) {
if (!contractDataOracle) {
contractDataOracle = new ContractDataOracle(this.db, this.node);
}

async #simulateUnconstrained(execRequest: FunctionCall, from?: AztecAddress) {
const contractDataOracle = new ContractDataOracle(this.db, this.node);
const { contractAddress, functionAbi, portalContract, historicRoots } = await this.#getSimulationParameters(
execRequest,
contractDataOracle,
Expand All @@ -387,6 +377,7 @@ export class AztecRPCServer implements AztecRPC {
this.log('Executing unconstrained simulator...');
const result = await simulator.runUnconstrained(
execRequest,
from ?? AztecAddress.ZERO,
functionAbi,
contractAddress,
portalContract,
Expand Down
17 changes: 8 additions & 9 deletions yarn-project/aztec.js/src/account_impl/account_collection.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AztecAddress, TxContext } from '@aztec/circuits.js';
import { ExecutionRequest, TxExecutionRequest } from '@aztec/types';
import { AztecAddress } from '@aztec/circuits.js';
import { FunctionCall, TxExecutionRequest } from '@aztec/types';

import { AccountImplementation } from './index.js';
import { AccountImplementation, CreateTxRequestOpts } from './index.js';

/**
* A concrete account implementation that manages multiple accounts.
Expand All @@ -23,14 +23,13 @@ export class AccountCollection implements AccountImplementation {
return AztecAddress.fromString(this.accounts.keys().next().value as string);
}

public createAuthenticatedTxRequest(
executions: ExecutionRequest[],
txContext: TxContext,
public createTxExecutionRequest(
executions: FunctionCall[],
opts: CreateTxRequestOpts = {},
): Promise<TxExecutionRequest> {
// TODO: Check all executions have the same origin
const sender = executions[0].from;
const sender = opts.origin ?? this.getAddress();
const impl = this.accounts.get(sender.toString());
if (!impl) throw new Error(`No account implementation registered for ${sender}`);
return impl.createAuthenticatedTxRequest(executions, txContext);
return impl.createTxExecutionRequest(executions, opts);
}
}
10 changes: 5 additions & 5 deletions yarn-project/aztec.js/src/account_impl/entrypoint_payload.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { CircuitsWasm, Fr } from '@aztec/circuits.js';
import { padArrayEnd } from '@aztec/foundation/collection';
import { sha256 } from '@aztec/foundation/crypto';
import { ExecutionRequest, PackedArguments, emptyExecutionRequest } from '@aztec/types';
import { FunctionCall, PackedArguments, emptyFunctionCall } from '@aztec/types';

// These must match the values defined in yarn-project/noir-libs/noir-aztec/src/entrypoint.nr
const ACCOUNT_MAX_PRIVATE_CALLS = 2;
Expand All @@ -24,8 +24,8 @@ export type EntrypointPayload = {

/** Assembles an entrypoint payload from a set of private and public function calls */
export async function buildPayload(
privateCalls: ExecutionRequest[],
publicCalls: ExecutionRequest[],
privateCalls: FunctionCall[],
publicCalls: FunctionCall[],
): Promise<{
/** The payload for the entrypoint function */
payload: EntrypointPayload;
Expand All @@ -35,8 +35,8 @@ export async function buildPayload(
const nonce = Fr.random();

const calls = [
...padArrayEnd(privateCalls, emptyExecutionRequest(), ACCOUNT_MAX_PRIVATE_CALLS),
...padArrayEnd(publicCalls, emptyExecutionRequest(), ACCOUNT_MAX_PUBLIC_CALLS),
...padArrayEnd(privateCalls, emptyFunctionCall(), ACCOUNT_MAX_PRIVATE_CALLS),
...padArrayEnd(publicCalls, emptyFunctionCall(), ACCOUNT_MAX_PUBLIC_CALLS),
];

const packedArguments = [];
Expand Down
18 changes: 12 additions & 6 deletions yarn-project/aztec.js/src/account_impl/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { AztecAddress, TxContext } from '@aztec/circuits.js';
import { ExecutionRequest, TxExecutionRequest } from '@aztec/types';
import { AztecAddress } from '@aztec/circuits.js';
import { FunctionCall, TxExecutionRequest } from '@aztec/types';

export * from './account_collection.js';
export * from './single_key_account_contract.js';
export * from './stored_key_account_contract.js';
export * from './account_collection.js';

/** Options for creating a tx request out of a set of function calls. */
export type CreateTxRequestOpts = {
/** Origin of the tx. Needs to be an address managed by this account. */
origin?: AztecAddress;
};

/** Represents an implementation for a user account contract. Knows how to encode and sign a tx for that particular implementation. */
export interface AccountImplementation {
Expand All @@ -15,9 +21,9 @@ export interface AccountImplementation {

/**
* Generates an authenticated request out of set of intents
* @param executions - The execution intent to be authenticated.
* @param txContext - The tx context under with the execution is to be made.
* @param executions - The execution intents to be run.
* @param opts - Options.
* @returns The authenticated transaction execution request.
*/
createAuthenticatedTxRequest(executions: ExecutionRequest[], txContext: TxContext): Promise<TxExecutionRequest>;
createTxExecutionRequest(executions: FunctionCall[], opts?: CreateTxRequestOpts): Promise<TxExecutionRequest>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ import {
} from '@aztec/circuits.js';
import { Signer } from '@aztec/circuits.js/barretenberg';
import { ContractAbi, encodeArguments } from '@aztec/foundation/abi';
import { ExecutionRequest, PackedArguments, TxExecutionRequest } from '@aztec/types';
import { FunctionCall, PackedArguments, TxExecutionRequest } from '@aztec/types';

import partition from 'lodash.partition';

import SchnorrSingleKeyAccountContractAbi from '../abis/schnorr_single_key_account_contract.json' assert { type: 'json' };
import { generatePublicKey } from '../index.js';
import { DEFAULT_CHAIN_ID, DEFAULT_VERSION } from '../utils/defaults.js';
import { buildPayload, hashPayload } from './entrypoint_payload.js';
import { AccountImplementation } from './index.js';
import { AccountImplementation, CreateTxRequestOpts } from './index.js';

/**
* Account contract implementation that uses a single key for signing and encryption. This public key is not
Expand All @@ -28,18 +29,21 @@ export class SingleKeyAccountContract implements AccountImplementation {
private partialContractAddress: PartialContractAddress,
private privateKey: PrivateKey,
private signer: Signer,
private chainId: number = DEFAULT_CHAIN_ID,
private version: number = DEFAULT_VERSION,
) {}

getAddress(): AztecAddress {
return this.address;
}

async createAuthenticatedTxRequest(
executions: ExecutionRequest[],
txContext: TxContext,
async createTxExecutionRequest(
executions: FunctionCall[],
opts: CreateTxRequestOpts = {},
): Promise<TxExecutionRequest> {
this.checkSender(executions);
this.checkIsNotDeployment(txContext);
if (opts.origin && !opts.origin.equals(this.address)) {
throw new Error(`Sender ${opts.origin.toString()} does not match account address ${this.address.toString()}`);
}

const [privateCalls, publicCalls] = partition(executions, exec => exec.functionData.isPrivate);
const wasm = await CircuitsWasm.get();
Expand All @@ -55,7 +59,7 @@ export class SingleKeyAccountContract implements AccountImplementation {
argsHash: packedArgs.hash,
origin: this.address,
functionData: FunctionData.fromAbi(abi),
txContext,
txContext: TxContext.empty(this.chainId, this.version),
packedArguments: [...callsPackedArguments, packedArgs],
});

Expand All @@ -70,19 +74,4 @@ export class SingleKeyAccountContract implements AccountImplementation {
if (!abi) throw new Error(`Entrypoint abi for account contract not found`);
return abi;
}

private checkIsNotDeployment(txContext: TxContext) {
if (txContext.isContractDeploymentTx) {
throw new Error(`Cannot yet deploy contracts from an account contract`);
}
}

private checkSender(executions: ExecutionRequest[]) {
const wrongSender = executions.find(e => !e.from.equals(this.address));
if (wrongSender) {
throw new Error(
`Sender ${wrongSender.from.toString()} does not match account address ${this.address.toString()}`,
);
}
}
}
Loading

0 comments on commit 05b6e86

Please sign in to comment.