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

chore: add FunctionData.fromAbi for QoL #1333

Merged
merged 5 commits into from
Aug 1, 2023
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
24 changes: 14 additions & 10 deletions yarn-project/acir-simulator/src/client/private_execution.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,21 +86,20 @@ describe('Private Execution test suite', () => {
args = [],
origin = AztecAddress.random(),
contractAddress = defaultContractAddress,
isConstructor = false,
txContext = {},
}: {
abi: FunctionAbi;
origin?: AztecAddress;
contractAddress?: AztecAddress;
isConstructor?: boolean;
args?: any[];
txContext?: Partial<FieldsOf<TxContext>>;
}) => {
const packedArguments = await PackedArguments.fromArgs(encodeArguments(abi, args), circuitsWasm);
const functionData = FunctionData.fromAbi(abi);
const txRequest = TxExecutionRequest.from({
origin,
argsHash: packedArguments.hash,
functionData: new FunctionData(Buffer.alloc(4), false, true, isConstructor),
functionData,
txContext: TxContext.from({ ...txContextFields, ...txContext }),
packedArguments: [packedArguments],
});
Expand All @@ -110,7 +109,7 @@ describe('Private Execution test suite', () => {
return acirSimulator.run(
txRequest,
abi,
isConstructor ? AztecAddress.ZERO : contractAddress,
functionData.isConstructor ? AztecAddress.ZERO : contractAddress,
EthAddress.ZERO,
historicRoots,
);
Expand Down Expand Up @@ -156,7 +155,7 @@ describe('Private Execution test suite', () => {
const abi = TestContractAbi.functions[0];
const contractDeploymentData = makeContractDeploymentData(100);
const txContext = { isContractDeploymentTx: true, contractDeploymentData };
const result = await runSimulator({ abi, isConstructor: true, txContext });
const result = await runSimulator({ abi, txContext });

const emptyCommitments = new Array(MAX_NEW_COMMITMENTS_PER_CALL).fill(Fr.ZERO);
expect(result.callStackItem.publicInputs.newCommitments).toEqual(emptyCommitments);
Expand Down Expand Up @@ -242,7 +241,7 @@ describe('Private Execution test suite', () => {
it('should a constructor with arguments that inserts notes', async () => {
const abi = ZkTokenContractAbi.functions.find(f => f.name === 'constructor')!;

const result = await runSimulator({ args: [140, owner], abi, isConstructor: true });
const result = await runSimulator({ args: [140, owner], abi });

expect(result.preimages.newNotes).toHaveLength(1);
const newNote = result.preimages.newNotes[0];
Expand Down Expand Up @@ -398,7 +397,7 @@ describe('Private Execution test suite', () => {
const parentAbi = ParentContractAbi.functions.find(f => f.name === 'entryPoint')!;
const parentAddress = AztecAddress.random();
const childAddress = AztecAddress.random();
const childSelector = Buffer.alloc(4, 1); // should match the call
const childSelector = generateFunctionSelector(childAbi.name, childAbi.parameters);
Copy link
Contributor

Choose a reason for hiding this comment

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

love this!


oracle.getFunctionABI.mockImplementation(() => Promise.resolve(childAbi));
oracle.getPortalContractAddress.mockImplementation(() => Promise.resolve(EthAddress.ZERO));
Expand Down Expand Up @@ -512,13 +511,14 @@ describe('Private Execution test suite', () => {
describe('enqueued calls', () => {
it.each([false, true])('parent should enqueue call to child', async isInternal => {
const parentAbi = ParentContractAbi.functions.find(f => f.name === 'enqueueCallToChild')!;
const childContractAbi = ParentContractAbi.functions[0];
const childAddress = AztecAddress.random();
const childPortalContractAddress = EthAddress.random();
const childSelector = Buffer.alloc(4, 1); // should match the call
const childSelector = generateFunctionSelector(childContractAbi.name, childContractAbi.parameters);
const parentAddress = AztecAddress.random();

oracle.getPortalContractAddress.mockImplementation(() => Promise.resolve(childPortalContractAddress));
oracle.getFunctionABI.mockImplementation(() => Promise.resolve({ ...ChildContractAbi.functions[0], isInternal }));
oracle.getFunctionABI.mockImplementation(() => Promise.resolve({ ...childContractAbi, isInternal }));

const args = [Fr.fromBuffer(childAddress.toBuffer()), Fr.fromBuffer(childSelector), 42n];
const result = await runSimulator({
Expand All @@ -528,9 +528,13 @@ describe('Private Execution test suite', () => {
args,
});

// Alter function data (abi) to match the manipulated oracle
const functionData = FunctionData.fromAbi(childContractAbi);
functionData.isInternal = isInternal;

const publicCallRequest = PublicCallRequest.from({
contractAddress: childAddress,
functionData: new FunctionData(childSelector, isInternal, false, false),
functionData: functionData,
args: [new Fr(42n)],
callContext: CallContext.from({
msgSender: parentAddress,
Expand Down
5 changes: 3 additions & 2 deletions yarn-project/acir-simulator/src/client/private_execution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ export class PrivateFunctionExecution {
curve: Grumpkin,
) {
const targetAbi = await this.context.db.getFunctionABI(targetContractAddress, targetFunctionSelector);
const targetFunctionData = new FunctionData(targetFunctionSelector, targetAbi.isInternal, true, false);
const targetFunctionData = FunctionData.fromAbi(targetAbi);
const derivedCallContext = await this.deriveCallContext(callerContext, targetContractAddress, false, false);
const context = this.context.extend();

Expand Down Expand Up @@ -301,10 +301,11 @@ export class PrivateFunctionExecution {
): Promise<PublicCallRequest> {
const targetAbi = await this.context.db.getFunctionABI(targetContractAddress, targetFunctionSelector);
const derivedCallContext = await this.deriveCallContext(callerContext, targetContractAddress, false, false);

return PublicCallRequest.from({
args: targetArgs,
callContext: derivedCallContext,
functionData: new FunctionData(targetFunctionSelector, targetAbi.isInternal, false, false),
functionData: FunctionData.fromAbi(targetAbi),
contractAddress: targetContractAddress,
});
}
Expand Down
12 changes: 7 additions & 5 deletions yarn-project/acir-simulator/src/public/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ import {
PrivateHistoricTreeRoots,
} from '@aztec/circuits.js';
import { pedersenPlookupCommitInputs } from '@aztec/circuits.js/barretenberg';
import { FunctionAbi, encodeArguments } from '@aztec/foundation/abi';
import { FunctionAbi, encodeArguments, generateFunctionSelector } from '@aztec/foundation/abi';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { keccak } from '@aztec/foundation/crypto';
import { EthAddress } from '@aztec/foundation/eth-address';
import { Fr } from '@aztec/foundation/fields';
import { toBigInt } from '@aztec/foundation/serialize';
Expand Down Expand Up @@ -63,7 +62,7 @@ describe('ACIR public execution simulator', () => {
it('should run the mint function', async () => {
const contractAddress = AztecAddress.random();
const mintAbi = PublicTokenContractAbi.functions.find(f => f.name === 'mint')!;
const functionData = new FunctionData(Buffer.alloc(4), false, false, false);
const functionData = FunctionData.fromAbi(mintAbi);
const args = encodeArguments(mintAbi, [140, recipient]);

const callContext = CallContext.from({
Expand Down Expand Up @@ -192,11 +191,14 @@ describe('ACIR public execution simulator', () => {
async isInternal => {
const parentContractAddress = AztecAddress.random();
const parentEntryPointFn = ParentContractAbi.functions.find(f => f.name === 'pubEntryPoint')!;
const parentEntryPointFnSelector = keccak(Buffer.from(parentEntryPointFn.name)).subarray(0, 4);
const parentEntryPointFnSelector = generateFunctionSelector(
parentEntryPointFn.name,
parentEntryPointFn.parameters,
);

const childContractAddress = AztecAddress.random();
const childValueFn = ChildContractAbi.functions.find(f => f.name === 'pubValue')!;
const childValueFnSelector = keccak(Buffer.from(childValueFn.name)).subarray(0, 4);
const childValueFnSelector = generateFunctionSelector(childValueFn.name, childValueFn.parameters);

const initialValue = 3n;

Expand Down
15 changes: 3 additions & 12 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 @@ -11,7 +11,7 @@ import {
PrivateKey,
PublicKey,
} from '@aztec/circuits.js';
import { FunctionType, encodeArguments } from '@aztec/foundation/abi';
import { encodeArguments } from '@aztec/foundation/abi';
import { Fr } from '@aztec/foundation/fields';
import { DebugLogger, createDebugLogger } from '@aztec/foundation/log';
import {
Expand Down Expand Up @@ -385,19 +385,10 @@ export class AztecRPCServer implements AztecRPC {
throw new Error(`Unknown function ${functionName} in contract ${contract.name}.`);
}

const flatArgs = encodeArguments(functionDao, args);

const functionData = new FunctionData(
functionDao.selector,
functionDao.isInternal,
functionDao.functionType === FunctionType.SECRET,
false,
);

return {
args: flatArgs,
args: encodeArguments(functionDao, args),
from,
functionData,
functionData: FunctionData.fromAbi(functionDao),
to,
};
}
Expand Down
3 changes: 1 addition & 2 deletions yarn-project/aztec-rpc/src/contract_tree/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,7 @@ export class ContractTree {
}));
const leaves = generateFunctionLeaves(functions, wasm);
const root = computeFunctionTreeRoot(wasm, leaves);
const constructorSelector = generateFunctionSelector(constructorAbi.name, constructorAbi.parameters);
const functionData = new FunctionData(constructorSelector, false, true, true);
const functionData = FunctionData.fromAbi(constructorAbi);
const vkHash = hashVKStr(constructorAbi.verificationKey, wasm);
const argsHash = await computeVarArgsHash(wasm, args);
const constructorHash = hashConstructor(wasm, functionData, argsHash, vkHash);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
TxContext,
} from '@aztec/circuits.js';
import { Signer } from '@aztec/circuits.js/barretenberg';
import { ContractAbi, encodeArguments, generateFunctionSelector } from '@aztec/foundation/abi';
import { ContractAbi, encodeArguments } from '@aztec/foundation/abi';
import { ExecutionRequest, PackedArguments, TxExecutionRequest } from '@aztec/types';

import partition from 'lodash.partition';
Expand Down Expand Up @@ -50,12 +50,11 @@ export class SingleKeyAccountContract implements AccountImplementation {
const publicKey = await generatePublicKey(this.privateKey);
const args = [payload, publicKey.toBuffer(), signature, this.partialContractAddress];
const abi = this.getEntrypointAbi();
const selector = generateFunctionSelector(abi.name, abi.parameters);
const packedArgs = await PackedArguments.fromArgs(encodeArguments(abi, args), wasm);
const txRequest = TxExecutionRequest.from({
argsHash: packedArgs.hash,
origin: this.address,
functionData: new FunctionData(selector, abi.isInternal, true, false),
functionData: FunctionData.fromAbi(abi),
txContext,
packedArguments: [...callsPackedArguments, packedArgs],
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AztecAddress, CircuitsWasm, FunctionData, PrivateKey, TxContext } from '@aztec/circuits.js';
import { Signer } from '@aztec/circuits.js/barretenberg';
import { ContractAbi, encodeArguments, generateFunctionSelector } from '@aztec/foundation/abi';
import { ContractAbi, encodeArguments } from '@aztec/foundation/abi';
import { DebugLogger, createDebugLogger } from '@aztec/foundation/log';
import { ExecutionRequest, PackedArguments, TxExecutionRequest } from '@aztec/types';

Expand Down Expand Up @@ -41,12 +41,11 @@ export class StoredKeyAccountContract implements AccountImplementation {

const args = [payload, signature];
const abi = this.getEntrypointAbi();
const selector = generateFunctionSelector(abi.name, abi.parameters);
const packedArgs = await PackedArguments.fromArgs(encodeArguments(abi, args), wasm);
const txRequest = TxExecutionRequest.from({
argsHash: packedArgs.hash,
origin: this.address,
functionData: new FunctionData(selector, abi.isInternal, true, false),
functionData: FunctionData.fromAbi(abi),
txContext,
packedArguments: [...callsPackedArguments, packedArgs],
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AztecAddress, Fr, FunctionData, TxContext } from '@aztec/circuits.js';
import { FunctionAbi, FunctionType, encodeArguments, generateFunctionSelector } from '@aztec/foundation/abi';
import { FunctionAbi, FunctionType, encodeArguments } from '@aztec/foundation/abi';
import { ExecutionRequest, Tx, TxExecutionRequest } from '@aztec/types';

import { Wallet } from '../aztec_rpc_client/wallet.js';
Expand Down Expand Up @@ -98,16 +98,9 @@ export class ContractFunctionInteraction {
const flatArgs = encodeArguments(this.functionDao, this.args);
from = from ?? this.wallet.getAddress();

const functionData = new FunctionData(
generateFunctionSelector(this.functionDao.name, this.functionDao.parameters),
this.functionDao.isInternal,
this.functionDao.functionType === FunctionType.SECRET,
this.functionDao.name === 'constructor',
);

return {
args: flatArgs,
functionData,
functionData: FunctionData.fromAbi(this.functionDao),
to,
from,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ export async function getContractDeploymentInfo(
}));
const leaves = generateFunctionLeaves(functions, wasm);
const functionTreeRoot = computeFunctionTreeRoot(wasm, leaves);
const constructorSelector = generateFunctionSelector(constructorAbi.name, constructorAbi.parameters);
const functionData = new FunctionData(constructorSelector, false, true, true);
const functionData = FunctionData.fromAbi(constructorAbi);
const flatArgs = encodeArguments(constructorAbi, args);
const argsHash = await computeVarArgsHash(wasm, flatArgs);
const constructorHash = hashConstructor(wasm, functionData, argsHash, constructorVkHash.toBuffer());
Expand Down
12 changes: 12 additions & 0 deletions yarn-project/circuits.js/src/structs/function_data.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { FunctionAbi, FunctionType, generateFunctionSelector } from '@aztec/foundation/abi';
import { BufferReader, deserializeUInt32, numToUInt32BE } from '@aztec/foundation/serialize';

import { ContractFunctionDao } from '../index.js';
import { serializeToBuffer } from '../utils/serialize.js';

const FUNCTION_SELECTOR_LENGTH = 4;
Expand Down Expand Up @@ -40,6 +42,16 @@ export class FunctionData {
this.functionSelectorBuffer = numToUInt32BE(functionSelector);
}
}

static fromAbi(abi: FunctionAbi | ContractFunctionDao): FunctionData {
return new FunctionData(
generateFunctionSelector(abi.name, abi.parameters),
abi.isInternal,
abi.functionType === FunctionType.SECRET,
abi.name === 'constructor',
);
}

// For serialization, must match function_selector name in C++ and return as number
// TODO(AD) somehow remove this cruft, probably by using a buffer selector in C++
get functionSelector(): number {
Expand Down