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(aztec-js): remove sender from execution request and add batching #1415

Merged
merged 8 commits into from
Aug 4, 2023
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