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: batch simulate #6599

Merged
merged 7 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
3 changes: 1 addition & 2 deletions l1-contracts/src/core/libraries/ConstantsGen.sol
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,7 @@ library Constants {
APPEND_ONLY_TREE_SNAPSHOT_LENGTH + 4 + GLOBAL_VARIABLES_LENGTH;
uint256 internal constant BASE_OR_MERGE_PUBLIC_INPUTS_LENGTH = CONSTANT_ROLLUP_DATA_LENGTH
+ PARTIAL_STATE_REFERENCE_LENGTH + PARTIAL_STATE_REFERENCE_LENGTH + 5;
uint256 internal constant ENQUEUE_PUBLIC_FUNCTION_CALL_RETURN_LENGTH =
2 + FUNCTION_DATA_LENGTH + CALL_CONTEXT_LENGTH;
uint256 internal constant ENQUEUE_PUBLIC_FUNCTION_CALL_RETURN_LENGTH = 3 + CALL_CONTEXT_LENGTH;
uint256 internal constant GET_NOTES_ORACLE_RETURN_LENGTH = 674;
uint256 internal constant NOTE_HASHES_NUM_BYTES_PER_BASE_ROLLUP = 2048;
uint256 internal constant NULLIFIERS_NUM_BYTES_PER_BASE_ROLLUP = 2048;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ pub fn parse_public_call_stack_item_from_oracle(fields: [Field; ENQUEUE_PUBLIC_F
// there is no more data in fields because there is only ENQUEUE_PUBLIC_FUNCTION_CALL_RETURN_SIZE fields!
let item = PublicCallStackItem {
contract_address: AztecAddress::from_field(reader.read()),
function_data: reader.read_struct(FunctionData::deserialize),
function_data: FunctionData { selector: FunctionSelector::from_field(reader.read()), is_private: false },
public_inputs: PublicCircuitPublicInputs {
call_context: reader.read_struct(CallContext::deserialize),
args_hash: reader.read(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ global CONSTANT_ROLLUP_DATA_LENGTH = APPEND_ONLY_TREE_SNAPSHOT_LENGTH + 4 + GLOB
// + 5 for rollup_type, height_in_block_tree, txs_effects_hash, out_hash, accumulated_fees
global BASE_OR_MERGE_PUBLIC_INPUTS_LENGTH = CONSTANT_ROLLUP_DATA_LENGTH + PARTIAL_STATE_REFERENCE_LENGTH + PARTIAL_STATE_REFERENCE_LENGTH + 5;

global ENQUEUE_PUBLIC_FUNCTION_CALL_RETURN_LENGTH: u64 = 2 + FUNCTION_DATA_LENGTH + CALL_CONTEXT_LENGTH;
global ENQUEUE_PUBLIC_FUNCTION_CALL_RETURN_LENGTH: u64 = 3 + CALL_CONTEXT_LENGTH;
global GET_NOTES_ORACLE_RETURN_LENGTH: u64 = 674;
global NOTE_HASHES_NUM_BYTES_PER_BASE_ROLLUP: Field = 2048;
global NULLIFIERS_NUM_BYTES_PER_BASE_ROLLUP: Field = 2048;
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/aztec-node/src/aztec-node/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,7 @@ export class AztecNodeService implements AztecNode {
processedTx.revertReason,
processedTx.data.constants,
processedTx.data.end,
returns[0],
returns,
processedTx.gasUsed,
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { type Fr, FunctionData } from '@aztec/circuits.js';
import { type Fr } from '@aztec/circuits.js';
import {
type ContractArtifact,
type FunctionArtifact,
FunctionSelector,
encodeArguments,
getFunctionArtifact,
} from '@aztec/foundation/abi';
Expand Down Expand Up @@ -54,10 +55,16 @@ export class DeployAccountMethod extends DeployMethod {
const feePayload = await EntrypointPayload.fromFeeOptions(options?.fee);

exec.calls.push({
name: this.#feePaymentArtifact.name,
to: address,
args: encodeArguments(this.#feePaymentArtifact, [emptyAppPayload, feePayload]),
functionData: FunctionData.fromAbi(this.#feePaymentArtifact),
selector: FunctionSelector.fromNameAndParameters(
this.#feePaymentArtifact.name,
this.#feePaymentArtifact.parameters,
),
type: this.#feePaymentArtifact.functionType,
isStatic: this.#feePaymentArtifact.isStatic,
returnTypes: this.#feePaymentArtifact.returnTypes,
});

exec.authWitnesses ??= [];
Expand Down
62 changes: 62 additions & 0 deletions yarn-project/aztec.js/src/contract/batch_call.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { type FunctionCall, type TxExecutionRequest } from '@aztec/circuit-types';
import { FunctionType, decodeReturnValues } from '@aztec/foundation/abi';

import { type Wallet } from '../account/index.js';
import { BaseContractInteraction, type SendMethodOptions } from './base_contract_interaction.js';
import type { SimulateMethodOptions } from './contract_function_interaction.js';

/** A batch of function calls to be sent as a single transaction through a wallet. */
export class BatchCall extends BaseContractInteraction {
Expand All @@ -23,4 +25,64 @@ export class BatchCall extends BaseContractInteraction {
}
return this.txRequest;
}

/**
* Simulate a transaction and get its return values
* Differs from prove in a few important ways:
* 1. It returns the values of the function execution
* 2. It supports `unconstrained`, `private` and `public` functions
*
* @param options - An optional object containing additional configuration for the transaction.
* @returns The result of the transaction as returned by the contract function.
*/
public async simulate(options: SimulateMethodOptions = {}): Promise<any> {
const { calls, unconstrained } = this.calls.reduce<{
/**
* The public and private function calls in the batch
*/
calls: [FunctionCall, number][];
/**
* The unconstrained function calls in the batch.
*/
unconstrained: [FunctionCall, number][];
}>(
(acc, current, index) => {
if (current.type === FunctionType.UNCONSTRAINED) {
acc.unconstrained.push([current, index]);
} else {
acc.calls.push([current, index]);
}
return acc;
},
{ calls: [], unconstrained: [] },
);

const unconstrainedCalls = await Promise.all(
unconstrained.map(async indexedCall => {
const call = indexedCall[0];
return [await this.wallet.simulateUnconstrained(call.name, call.args, call.to, options?.from), indexedCall[1]];
}),
);

const txRequest = await this.wallet.createTxExecutionRequest({ calls: calls.map(indexedCall => indexedCall[0]) });
const simulatedTx = await this.wallet.simulateTx(txRequest, true, options?.from);

const results: any[] = [];

unconstrainedCalls.forEach(([result, index]) => {
results[index] = result;
});
calls.forEach(([call, callIndex], resultIndex) => {
// As account entrypoints are private, for private functions we retrieve the return values from the first nested call
// since we're interested in the first set of values AFTER the account entrypoint
// For public functions we retrieve the first values directly from the public output.
const rawReturnValues =
call.type == FunctionType.PRIVATE
? simulatedTx.privateReturnValues?.nested?.[resultIndex].values
: simulatedTx.publicOutput?.publicReturnValues?.[resultIndex].values;

results[callIndex] = rawReturnValues ? decodeReturnValues(call.returnTypes, rawReturnValues) : [];
});
return results;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import type { FunctionCall, TxExecutionRequest } from '@aztec/circuit-types';
import { type AztecAddress, FunctionData, type GasSettings } from '@aztec/circuits.js';
import { type FunctionAbi, FunctionType, decodeReturnValues, encodeArguments } from '@aztec/foundation/abi';
import { type AztecAddress, type GasSettings } from '@aztec/circuits.js';
import {
type FunctionAbi,
FunctionSelector,
FunctionType,
decodeReturnValues,
encodeArguments,
} from '@aztec/foundation/abi';

import { type Wallet } from '../account/wallet.js';
import { BaseContractInteraction, type SendMethodOptions } from './base_contract_interaction.js';
Expand Down Expand Up @@ -61,8 +67,15 @@ export class ContractFunctionInteraction extends BaseContractInteraction {
*/
public request(): FunctionCall {
const args = encodeArguments(this.functionDao, this.args);
const functionData = FunctionData.fromAbi(this.functionDao);
return { args, functionData, to: this.contractAddress, isStatic: this.functionDao.isStatic };
return {
name: this.functionDao.name,
args,
selector: FunctionSelector.fromNameAndParameters(this.functionDao.name, this.functionDao.parameters),
type: this.functionDao.functionType,
to: this.contractAddress,
isStatic: this.functionDao.isStatic,
returnTypes: this.functionDao.returnTypes,
};
}

/**
Expand All @@ -88,8 +101,8 @@ export class ContractFunctionInteraction extends BaseContractInteraction {
const rawReturnValues =
this.functionDao.functionType == FunctionType.PRIVATE
? simulatedTx.privateReturnValues?.nested?.[0].values
: simulatedTx.publicOutput?.publicReturnValues?.values;
: simulatedTx.publicOutput?.publicReturnValues?.[0].values;

return rawReturnValues ? decodeReturnValues(this.functionDao, rawReturnValues) : [];
return rawReturnValues ? decodeReturnValues(this.functionDao.returnTypes, rawReturnValues) : [];
}
}
8 changes: 7 additions & 1 deletion yarn-project/aztec.js/src/entrypoint/default_entrypoint.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { PackedValues, TxExecutionRequest } from '@aztec/circuit-types';
import { GasSettings, TxContext } from '@aztec/circuits.js';
import { FunctionType } from '@aztec/foundation/abi';

import { type EntrypointInterface, type ExecutionRequestInit } from './entrypoint.js';

Expand All @@ -17,13 +18,18 @@ export class DefaultEntrypoint implements EntrypointInterface {
}

const call = calls[0];

if (call.type !== FunctionType.PRIVATE) {
throw new Error('Public entrypoints are not allowed');
}

const entrypointPackedValues = PackedValues.fromValues(call.args);
const gasSettings = exec.fee?.gasSettings ?? GasSettings.default();
const txContext = new TxContext(this.chainId, this.protocolVersion, gasSettings);
return Promise.resolve(
new TxExecutionRequest(
call.to,
call.functionData,
call.selector,
entrypointPackedValues.hash,
txContext,
[...packedArguments, entrypointPackedValues],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type EntrypointInterface, EntrypointPayload, type ExecutionRequestInit } from '@aztec/aztec.js/entrypoint';
import { PackedValues, TxExecutionRequest } from '@aztec/circuit-types';
import { type AztecAddress, FunctionData, GasSettings, TxContext } from '@aztec/circuits.js';
import { type FunctionAbi, encodeArguments } from '@aztec/foundation/abi';
import { type AztecAddress, GasSettings, TxContext } from '@aztec/circuits.js';
import { type FunctionAbi, FunctionSelector, encodeArguments } from '@aztec/foundation/abi';
import { getCanonicalMultiCallEntrypointAddress } from '@aztec/protocol-contracts/multi-call-entrypoint';

/**
Expand All @@ -24,7 +24,7 @@ export class DefaultMultiCallEntrypoint implements EntrypointInterface {
const txRequest = TxExecutionRequest.from({
firstCallArgsHash: entrypointPackedArgs.hash,
origin: this.address,
functionData: FunctionData.fromAbi(abi),
functionSelector: FunctionSelector.fromNameAndParameters(abi.name, abi.parameters),
txContext: new TxContext(this.chainId, this.version, gasSettings),
argsOfCalls: [...payload.packedArguments, ...packedArguments, entrypointPackedArgs],
authWitnesses,
Expand Down
11 changes: 6 additions & 5 deletions yarn-project/aztec.js/src/entrypoint/payload.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { type FunctionCall, PackedValues, emptyFunctionCall } from '@aztec/circuit-types';
import { FunctionCall, PackedValues } from '@aztec/circuit-types';
import { Fr, type GasSettings, GeneratorIndex } from '@aztec/circuits.js';
import { FunctionType } from '@aztec/foundation/abi';
import { padArrayEnd } from '@aztec/foundation/collection';
import { pedersenHash } from '@aztec/foundation/crypto';
import { type Tuple } from '@aztec/foundation/serialize';
Expand Down Expand Up @@ -53,9 +54,9 @@ export class EntrypointPayload {
/* eslint-disable camelcase */
this.#functionCalls = functionCalls.map((call, index) => ({
args_hash: this.#packedArguments[index].hash,
function_selector: call.functionData.selector.toField(),
function_selector: call.selector.toField(),
target_address: call.to.toField(),
is_public: !call.functionData.isPrivate,
is_public: call.type == FunctionType.PUBLIC,
is_static: call.isStatic,
}));
/* eslint-enable camelcase */
Expand Down Expand Up @@ -131,7 +132,7 @@ export class EntrypointPayload {
if (functionCalls.length > APP_MAX_CALLS) {
throw new Error(`Expected at most ${APP_MAX_CALLS} function calls, got ${functionCalls.length}`);
}
const paddedCalls = padArrayEnd(functionCalls, emptyFunctionCall(), APP_MAX_CALLS);
const paddedCalls = padArrayEnd(functionCalls, FunctionCall.empty(), APP_MAX_CALLS);
return new EntrypointPayload(paddedCalls, GeneratorIndex.SIGNATURE_PAYLOAD);
}

Expand All @@ -142,7 +143,7 @@ export class EntrypointPayload {
*/
static async fromFeeOptions(feeOpts?: FeeOptions) {
const calls = feeOpts ? await feeOpts.paymentMethod.getFunctionCalls(feeOpts?.gasSettings) : [];
const paddedCalls = padArrayEnd(calls, emptyFunctionCall(), FEE_MAX_CALLS);
const paddedCalls = padArrayEnd(calls, FunctionCall.empty(), FEE_MAX_CALLS);
return new EntrypointPayload(paddedCalls, GeneratorIndex.FEE_PAYLOAD);
}
}
9 changes: 6 additions & 3 deletions yarn-project/aztec.js/src/fee/native_fee_payment_method.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { type FunctionCall } from '@aztec/circuit-types';
import { type AztecAddress, FunctionData, type GasSettings } from '@aztec/circuits.js';
import { FunctionSelector } from '@aztec/foundation/abi';
import { type AztecAddress, type GasSettings } from '@aztec/circuits.js';
import { FunctionSelector, FunctionType } from '@aztec/foundation/abi';
import { GasTokenAddress } from '@aztec/protocol-contracts/gas-token';

import { type FeePaymentMethod } from './fee_payment_method.js';
Expand Down Expand Up @@ -47,10 +47,13 @@ export class NativeFeePaymentMethod implements FeePaymentMethod {
getFunctionCalls(gasSettings: GasSettings): Promise<FunctionCall[]> {
return Promise.resolve([
{
name: 'pay_fee',
to: this.#gasTokenAddress,
functionData: new FunctionData(FunctionSelector.fromSignature('pay_fee(Field)'), /*isPrivate=*/ false),
selector: FunctionSelector.fromSignature('pay_fee(Field)'),
type: FunctionType.PUBLIC,
isStatic: false,
args: [gasSettings.getFeeLimit()],
returnTypes: [],
},
]);
}
Expand Down
20 changes: 10 additions & 10 deletions yarn-project/aztec.js/src/fee/private_fee_payment_method.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type FunctionCall } from '@aztec/circuit-types';
import { FunctionData, type GasSettings } from '@aztec/circuits.js';
import { type GasSettings } from '@aztec/circuits.js';
import { computeSecretHash } from '@aztec/circuits.js/hash';
import { FunctionSelector } from '@aztec/foundation/abi';
import { FunctionSelector, FunctionType } from '@aztec/foundation/abi';
import { type AztecAddress } from '@aztec/foundation/aztec-address';
import { Fr } from '@aztec/foundation/fields';

Expand Down Expand Up @@ -64,13 +64,13 @@ export class PrivateFeePaymentMethod implements FeePaymentMethod {
this.wallet.getChainId(),
this.wallet.getVersion(),
{
name: 'unshield',
args: [this.wallet.getCompleteAddress().address, this.paymentContract, maxFee, nonce],
functionData: new FunctionData(
FunctionSelector.fromSignature('unshield((Field),(Field),Field,Field)'),
/*isPrivate=*/ true,
),
selector: FunctionSelector.fromSignature('unshield((Field),(Field),Field,Field)'),
type: FunctionType.PRIVATE,
isStatic: false,
to: this.asset,
returnTypes: [],
},
);
await this.wallet.createAuthWit(messageHash);
Expand All @@ -79,13 +79,13 @@ export class PrivateFeePaymentMethod implements FeePaymentMethod {

return [
{
name: 'fee_entrypoint_private',
to: this.getPaymentContract(),
functionData: new FunctionData(
FunctionSelector.fromSignature('fee_entrypoint_private(Field,(Field),Field,Field)'),
/*isPrivate=*/ true,
),
selector: FunctionSelector.fromSignature('fee_entrypoint_private(Field,(Field),Field,Field)'),
type: FunctionType.PRIVATE,
isStatic: false,
args: [maxFee, this.asset, secretHashForRebate, nonce],
returnTypes: [],
},
];
}
Expand Down
20 changes: 10 additions & 10 deletions yarn-project/aztec.js/src/fee/public_fee_payment_method.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { type FunctionCall } from '@aztec/circuit-types';
import { FunctionData, type GasSettings } from '@aztec/circuits.js';
import { FunctionSelector } from '@aztec/foundation/abi';
import { type GasSettings } from '@aztec/circuits.js';
import { FunctionSelector, FunctionType } from '@aztec/foundation/abi';
import { type AztecAddress } from '@aztec/foundation/aztec-address';
import { Fr } from '@aztec/foundation/fields';

Expand Down Expand Up @@ -57,26 +57,26 @@ export class PublicFeePaymentMethod implements FeePaymentMethod {
this.wallet.getChainId(),
this.wallet.getVersion(),
{
name: 'transfer_public',
args: [this.wallet.getAddress(), this.paymentContract, maxFee, nonce],
functionData: new FunctionData(
FunctionSelector.fromSignature('transfer_public((Field),(Field),Field,Field)'),
/*isPrivate=*/ false,
),
selector: FunctionSelector.fromSignature('transfer_public((Field),(Field),Field,Field)'),
type: FunctionType.PUBLIC,
isStatic: false,
to: this.asset,
returnTypes: [],
},
);

return Promise.resolve([
this.wallet.setPublicAuthWit(messageHash, true).request(),
{
name: 'fee_entrypoint_public',
to: this.getPaymentContract(),
functionData: new FunctionData(
FunctionSelector.fromSignature('fee_entrypoint_public(Field,(Field),Field)'),
/*isPrivate=*/ true,
),
selector: FunctionSelector.fromSignature('fee_entrypoint_public(Field,(Field),Field)'),
type: FunctionType.PRIVATE,
isStatic: false,
args: [maxFee, this.asset, nonce],
returnTypes: [],
},
]);
}
Expand Down
3 changes: 1 addition & 2 deletions yarn-project/aztec.js/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export {
Body,
CompleteAddress,
ExtendedNote,
type FunctionCall,
FunctionCall,
GrumpkinPrivateKey,
L1ToL2Message,
L1Actor,
Expand All @@ -118,7 +118,6 @@ export {
TxStatus,
UnencryptedL2Log,
createAztecNodeClient,
emptyFunctionCall,
merkleTreeIds,
mockTx,
Comparator,
Expand Down
Loading
Loading