Skip to content

Commit

Permalink
feat: public constrained view messy
Browse files Browse the repository at this point in the history
  • Loading branch information
LHerskind committed Mar 26, 2024
1 parent 0ee9c1c commit 8f45f03
Show file tree
Hide file tree
Showing 15 changed files with 124 additions and 91 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -109,31 +109,21 @@ contract DocsExample {
}

#[aztec(private)]
fn multicall() -> pub AztecAddress {
AztecAddress::from_field(
context.call_private_function_no_args(
context.this_address(),
FunctionSelector::from_signature("get_message_sender()")
)[0]
)
}

#[aztec(private)]
fn multicall_public() {
context.call_public_function_no_args(
fn get_shared_immutable_constrained_private_indirect() -> pub Leader {
let ret = context.call_private_function_no_args(
context.this_address(),
FunctionSelector::from_signature("get_message_sender_public()")
FunctionSelector::from_signature("get_shared_immutable_constrained_private()")
);
}

#[aztec(private)]
fn get_message_sender() -> pub AztecAddress {
context.msg_sender()
Leader::deserialize([ret[0], ret[1]])
}

#[aztec(public)]
fn get_message_sender_public() -> pub AztecAddress {
context.msg_sender()
fn get_shared_immutable_constrained_indirect() -> pub Leader {
let ret = context.call_public_function_no_args(
context.this_address(),
FunctionSelector::from_signature("get_shared_immutable_constrained()")
);
Leader::deserialize([ret[0], ret[1]])
}

#[aztec(public)]
Expand Down
3 changes: 2 additions & 1 deletion yarn-project/aztec-node/src/aztec-node/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -648,7 +648,7 @@ export class AztecNodeService implements AztecNode {
new WASMSimulator(),
);
const processor = await publicProcessorFactory.create(prevHeader, newGlobalVariables);
const [processedTxs, failedTxs] = await processor.process([tx]);
const [processedTxs, failedTxs, returns] = await processor.process([tx]);
if (failedTxs.length) {
this.log.warn(`Simulated tx ${tx.getTxHash()} fails: ${failedTxs[0].error}`);
throw failedTxs[0].error;
Expand All @@ -659,6 +659,7 @@ export class AztecNodeService implements AztecNode {
throw reverted[0].revertReason;
}
this.log.info(`Simulated tx ${tx.getTxHash()} succeeds`);
return returns;
}

public setConfig(config: Partial<SequencerConfig>): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,21 +86,13 @@ export class ContractFunctionInteraction extends BaseContractInteraction {
* @returns The result of the view transaction as returned by the contract function.
*/
public async viewConstrained(options: ViewMethodOptions = {}) {
// We need to create the request, but we need to change the entry-point slightly I think :thinking:

const packedArgs = PackedArguments.fromArgs(encodeArguments(this.functionDao, this.args));

// I have forgotten what the hell origin is.

const nodeInfo = await this.wallet.getNodeInfo();

// We need to figure something better around for doing the simulation to have a origin and a to that is different
// such that we can actually replace the "msg_sender" etc

// Depending on public or not we need to do some changes.
const a = FunctionData.fromAbi(this.functionDao);

if (a.isPrivate) {
const nodeInfo = await this.wallet.getNodeInfo();

const txRequest = TxExecutionRequest.from({
argsHash: packedArgs.hash,
origin: this.contractAddress,
Expand All @@ -111,11 +103,11 @@ export class ContractFunctionInteraction extends BaseContractInteraction {
});

const vue = await this.pxe.simulateCall(txRequest, options.from ?? this.wallet.getAddress());
return vue.rv;
return vue.privateReturnValues && vue.privateReturnValues[0];
} else {
await this.create();
const vue = await this.pxe.simulateCall(this.txRequest!);
return vue.rv;
return vue.publicReturnValues && vue.publicReturnValues[0];
}
}
}
3 changes: 2 additions & 1 deletion yarn-project/circuit-types/src/interfaces/aztec-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
PUBLIC_DATA_TREE_HEIGHT,
} from '@aztec/circuits.js';
import { L1ContractAddresses } from '@aztec/ethereum';
import { DecodedReturn, ProcessReturnValues } from '@aztec/foundation/abi';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { Fr } from '@aztec/foundation/fields';
import { ContractClassPublic, ContractInstanceWithAddress } from '@aztec/types/contracts';
Expand Down Expand Up @@ -272,7 +273,7 @@ export interface AztecNode {
* This currently just checks that the transaction execution succeeds.
* @param tx - The transaction to simulate.
**/
simulatePublicCalls(tx: Tx): Promise<void>;
simulatePublicCalls(tx: Tx): Promise<ProcessReturnValues[]>;

/**
* Updates the configuration of this node.
Expand Down
8 changes: 6 additions & 2 deletions yarn-project/circuit-types/src/interfaces/pxe.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AztecAddress, CompleteAddress, Fr, GrumpkinPrivateKey, PartialAddress } from '@aztec/circuits.js';
import { ContractArtifact, DecodedReturn } from '@aztec/foundation/abi';
import { ContractArtifact, ProcessReturnValues } from '@aztec/foundation/abi';
import { ContractClassWithId, ContractInstanceWithAddress } from '@aztec/types/contracts';
import { NodeInfo } from '@aztec/types/interfaces';

Expand All @@ -14,7 +14,11 @@ import { TxExecutionRequest } from '../tx_execution_request.js';
import { SyncStatus } from './sync-status.js';

export class Vue {
constructor(public tx: Tx, public rv?: DecodedReturn) {}
constructor(
public tx: Tx,
public privateReturnValues?: ProcessReturnValues,
public publicReturnValues?: ProcessReturnValues,
) {}
}

// docs:start:pxe-interface
Expand Down
69 changes: 36 additions & 33 deletions yarn-project/end-to-end/src/e2e_state_vars.test.ts
Original file line number Diff line number Diff line change
@@ -1,64 +1,67 @@
import { DebugLogger, FunctionSelector, Wallet } from '@aztec/aztec.js';
import { decodeFunctionSignature } from '@aztec/foundation/abi';
import { Wallet } from '@aztec/aztec.js';
import { DocsExampleContract } from '@aztec/noir-contracts.js';

import { jest } from '@jest/globals';

import { setup } from './fixtures/utils.js';

const TIMEOUT = 100_000;

describe('e2e_state_vars', () => {
jest.setTimeout(TIMEOUT);

let wallet: Wallet;

let teardown: () => Promise<void>;
let contract: DocsExampleContract;
let logger: DebugLogger;

const POINTS = 1n;
const RANDOMNESS = 2n;

beforeAll(async () => {
({ teardown, wallet, logger } = await setup());
({ teardown, wallet } = await setup());
contract = await DocsExampleContract.deploy(wallet).send().deployed();
}, 25_000);
}, 30_000);

afterAll(() => teardown());

describe('SharedImmutable', () => {
it.only('private read of uninitialized SharedImmutable', async () => {
await contract.methods.initialize_shared_immutable(1).send().wait();
const returnsConstrained = await contract.methods.get_shared_immutable_constrained_private().viewConstrained();
const returnsUnconstrained = await contract.methods.get_shared_immutable().view();

console.log(`Return values from private constrained:`, returnsConstrained);
console.log(`Return values from unconstrained:`, returnsUnconstrained);

console.log(await contract.methods.get_message_sender().viewConstrained());

console.log(await contract.methods.multicall().viewConstrained());
});

it('public read of uninitialized SharedImmutable', async () => {
DocsExampleContract.artifact.functions.forEach(fn => {
const sig = decodeFunctionSignature(fn.name, fn.parameters);
logger(`Function ${sig} and the selector: ${FunctionSelector.fromNameAndParameters(fn.name, fn.parameters)}`);
});

// await contract.methods.initialize_shared_immutable(1).send().wait();
// console.log(await contract.methods.get_shared_immutable_constrained().viewConstrained());
console.log(await contract.methods.get_message_sender_public().viewConstrained());
});

it('private read of uninitialized SharedImmutable', async () => {
const s = await contract.methods.get_shared_immutable().view();

// Send the transaction and wait for it to be mined (wait function throws if the tx is not mined)
await contract.methods.match_shared_immutable(s.account, s.points).send().wait();
});

it('private read of initialized SharedImmutable', async () => {
it('private read of SharedImmutable', async () => {
await contract.methods.initialize_shared_immutable(1).send().wait();
const s = await contract.methods.get_shared_immutable().view();

await contract.methods.match_shared_immutable(s.account, s.points).send().wait();
}, 200_000);
const a = await contract.methods.get_shared_immutable_constrained_private().viewConstrained();
const b = await contract.methods.get_shared_immutable_constrained_private_indirect().viewConstrained();
const c = await contract.methods.get_shared_immutable().view();

expect((a as any)[0]).toEqual((c as any)['account'].toBigInt());
expect((a as any)[1]).toEqual((c as any)['points']);
expect((b as any)[0]).toEqual((c as any)['account'].toBigInt());
expect((b as any)[1]).toEqual((c as any)['points']);

expect(a).toEqual(b);
await contract.methods.match_shared_immutable(c.account, c.points).send().wait();
});

it('public read of SharedImmutable', async () => {
const a = await contract.methods.get_shared_immutable_constrained().viewConstrained();
const b = await contract.methods.get_shared_immutable_constrained_indirect().viewConstrained();
const c = await contract.methods.get_shared_immutable().view();

expect((a as any)[0]).toEqual((c as any)['account'].toBigInt());
expect((a as any)[1]).toEqual((c as any)['points']);
expect((b as any)[0]).toEqual((c as any)['account'].toBigInt());
expect((b as any)[1]).toEqual((c as any)['points']);

expect(a).toEqual(b);
await contract.methods.match_shared_immutable(c.account, c.points).send().wait();
});

it('initializing SharedImmutable the second time should fail', async () => {
// Jest executes the tests sequentially and the first call to initialize_shared_immutable was executed
Expand Down
1 change: 1 addition & 0 deletions yarn-project/foundation/src/abi/decoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { isAztecAddressStruct } from './utils.js';
* The type of our decoded ABI.
*/
export type DecodedReturn = bigint | boolean | AztecAddress | DecodedReturn[] | { [key: string]: DecodedReturn };
export type ProcessReturnValues = (DecodedReturn | undefined)[] | undefined;

/**
* Decodes return values from a function call.
Expand Down
23 changes: 14 additions & 9 deletions yarn-project/pxe/src/pxe_service/pxe_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import {
import { TxPXEProcessingStats } from '@aztec/circuit-types/stats';
import {
AztecAddress,
CallContext,
CallRequest,
CompleteAddress,
FunctionData,
Expand All @@ -32,13 +31,18 @@ import {
MAX_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX,
PartialAddress,
PrivateKernelTailCircuitPublicInputs,
Proof,
PublicCallRequest,
computeContractClassId,
getContractClassFromArtifact,
} from '@aztec/circuits.js';
import { computeCommitmentNonce, siloNullifier } from '@aztec/circuits.js/hash';
import { ContractArtifact, DecodedReturn, FunctionSelector, encodeArguments } from '@aztec/foundation/abi';
import {
ContractArtifact,
DecodedReturn,
FunctionSelector,
ProcessReturnValues,
encodeArguments,
} from '@aztec/foundation/abi';
import { arrayNonEmptyLength, padArrayEnd } from '@aztec/foundation/collection';
import { Fr } from '@aztec/foundation/fields';
import { SerialQueue } from '@aztec/foundation/fifo';
Expand Down Expand Up @@ -421,10 +425,8 @@ export class PXEService implements PXE {
const simulatePublic = msgSender === undefined;
const vue = await this.#simulateCall(txRequest, msgSender);
if (simulatePublic) {
const returns = await this.#simulatePublicCalls(vue.tx);
console.log(returns);
/*const t = returns[0];
vue.rv = t && t[0];*/
// Only one transaction, so we can take index 0.
vue.publicReturnValues = (await this.#simulatePublicCalls(vue.tx))[0];
}
return vue;
});
Expand Down Expand Up @@ -475,7 +477,10 @@ export class PXEService implements PXE {

const tx = new Tx(publicInputs, proof, encryptedLogs, unencryptedLogs, enqueuedPublicFunctions);

return new Vue(tx, executionResult.returnValues);
// TODO - Consider whether it would be possible to abuse this to make a multicall
const privateReturn: ProcessReturnValues = [executionResult.returnValues];

return new Vue(tx, privateReturn);
}

public async sendTx(tx: Tx): Promise<TxHash> {
Expand Down Expand Up @@ -642,7 +647,7 @@ export class PXEService implements PXE {
*/
async #simulatePublicCalls(tx: Tx) {
try {
await this.node.simulatePublicCalls(tx);
return await this.node.simulatePublicCalls(tx);
} catch (err) {
// Try to fill in the noir call stack since the PXE may have access to the debug metadata
if (err instanceof SimulationError) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ import {
makeEmptyProof,
} from '@aztec/circuits.js';
import { computeVarArgsHash } from '@aztec/circuits.js/hash';
import {
ABIType,
DecodedReturn,
FunctionArtifact,
ProcessReturnValues,
decodeReturnValues,
} from '@aztec/foundation/abi';
import { arrayNonEmptyLength, padArrayEnd } from '@aztec/foundation/collection';
import { DebugLogger, createDebugLogger } from '@aztec/foundation/log';
import { Tuple } from '@aztec/foundation/serialize';
Expand Down Expand Up @@ -112,6 +119,7 @@ export abstract class AbstractPhaseManager {
* revert reason, if any
*/
revertReason: SimulationError | undefined;
returnValues: ProcessReturnValues;
}>;

public static extractEnqueuedPublicCallsByPhase(
Expand Down Expand Up @@ -182,14 +190,22 @@ export abstract class AbstractPhaseManager {
tx: Tx,
previousPublicKernelOutput: PublicKernelCircuitPublicInputs,
previousPublicKernelProof: Proof,
): Promise<[PublicKernelCircuitPublicInputs, Proof, FunctionL2Logs[], SimulationError | undefined]> {
): Promise<
[
PublicKernelCircuitPublicInputs,
Proof,
FunctionL2Logs[],
SimulationError | undefined,
(DecodedReturn | undefined)[] | undefined,
]
> {
let kernelOutput = previousPublicKernelOutput;
let kernelProof = previousPublicKernelProof;

const enqueuedCalls = this.extractEnqueuedPublicCalls(tx);

if (!enqueuedCalls || !enqueuedCalls.length) {
return [kernelOutput, kernelProof, [], undefined];
return [kernelOutput, kernelProof, [], undefined, undefined];
}

const newUnencryptedFunctionLogs: FunctionL2Logs[] = [];
Expand All @@ -198,9 +214,13 @@ export abstract class AbstractPhaseManager {
// separate public callstacks to be proven by separate public kernel sequences
// and submitted separately to the base rollup?

const returns = [];

for (const enqueuedCall of enqueuedCalls) {
const executionStack: (PublicExecution | PublicExecutionResult)[] = [enqueuedCall];

let currentReturn: DecodedReturn | undefined = undefined;

// Keep track of which result is for the top/enqueued call
let enqueuedExecutionResult: PublicExecutionResult | undefined;

Expand Down Expand Up @@ -254,22 +274,30 @@ export abstract class AbstractPhaseManager {
result.revertReason
}`,
);
return [kernelOutput, kernelProof, [], result.revertReason];
return [kernelOutput, kernelProof, [], result.revertReason, undefined];
}

if (!enqueuedExecutionResult) {
enqueuedExecutionResult = result;

// @todo @lherskind We need to get the proper artifact here
const returnTypes: ABIType[] = [{ kind: 'array', length: 4, type: { kind: 'field' } }];
const mockArtifact = { returnTypes } as any as FunctionArtifact;

currentReturn = decodeReturnValues(mockArtifact, result.returnValues);
}
}
// HACK(#1622): Manually patches the ordering of public state actions
// TODO(#757): Enforce proper ordering of public state actions
patchPublicStorageActionOrdering(kernelOutput, enqueuedExecutionResult!, this.phase);

returns.push(currentReturn);
}

// TODO(#3675): This should be done in a public kernel circuit
removeRedundantPublicDataWrites(kernelOutput);

return [kernelOutput, kernelProof, newUnencryptedFunctionLogs, undefined];
return [kernelOutput, kernelProof, newUnencryptedFunctionLogs, undefined, returns];
}

protected async runKernelCircuit(
Expand Down
Loading

0 comments on commit 8f45f03

Please sign in to comment.