Skip to content

Commit

Permalink
feat: 👀 private constrained messy
Browse files Browse the repository at this point in the history
  • Loading branch information
LHerskind committed Mar 26, 2024
1 parent 22e0f0d commit 0ee9c1c
Show file tree
Hide file tree
Showing 7 changed files with 195 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,44 @@ contract DocsExample {
assert(read.points == expected.points, "Invalid points");
}

#[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(
context.this_address(),
FunctionSelector::from_signature("get_message_sender_public()")
);
}

#[aztec(private)]
fn get_message_sender() -> pub AztecAddress {
context.msg_sender()
}

#[aztec(public)]
fn get_message_sender_public() -> pub AztecAddress {
context.msg_sender()
}

#[aztec(public)]
fn get_shared_immutable_constrained() -> pub Leader {
storage.shared_immutable.read_public()
}

#[aztec(private)]
fn get_shared_immutable_constrained_private() -> pub Leader {
storage.shared_immutable.read_private()
}

unconstrained fn get_shared_immutable() -> pub Leader {
storage.shared_immutable.read_public()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FunctionCall, TxExecutionRequest } from '@aztec/circuit-types';
import { AztecAddress, FunctionData } from '@aztec/circuits.js';
import { FunctionCall, PackedArguments, TxExecutionRequest } from '@aztec/circuit-types';
import { AztecAddress, FunctionData, TxContext } from '@aztec/circuits.js';
import { FunctionAbi, FunctionType, encodeArguments } from '@aztec/foundation/abi';

import { Wallet } from '../account/wallet.js';
Expand Down Expand Up @@ -77,4 +77,45 @@ export class ContractFunctionInteraction extends BaseContractInteraction {
const { from } = options;
return this.wallet.viewTx(this.functionDao.name, this.args, this.contractAddress, from);
}

/**
* Execute a view (read-only) transaction on an unconstrained function.
* This method is used to call functions that do not modify the contract state and only return data.
* Throws an error if called on a non-unconstrained function.
* @param options - An optional object containing additional configuration for the transaction.
* @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 txRequest = TxExecutionRequest.from({
argsHash: packedArgs.hash,
origin: this.contractAddress,
functionData: FunctionData.fromAbi(this.functionDao),
txContext: TxContext.empty(nodeInfo.chainId, nodeInfo.protocolVersion),
packedArguments: [packedArgs],
authWitnesses: [],
});

const vue = await this.pxe.simulateCall(txRequest, options.from ?? this.wallet.getAddress());
return vue.rv;
} else {
await this.create();
const vue = await this.pxe.simulateCall(this.txRequest!);
return vue.rv;
}
}
}
4 changes: 4 additions & 0 deletions yarn-project/aztec.js/src/wallet/base_wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
TxExecutionRequest,
TxHash,
TxReceipt,
Vue,
} from '@aztec/circuit-types';
import { AztecAddress, CompleteAddress, Fr, GrumpkinPrivateKey, PartialAddress } from '@aztec/circuits.js';
import { ContractArtifact } from '@aztec/foundation/abi';
Expand Down Expand Up @@ -98,6 +99,9 @@ export abstract class BaseWallet implements Wallet {
simulateTx(txRequest: TxExecutionRequest, simulatePublic: boolean): Promise<Tx> {
return this.pxe.simulateTx(txRequest, simulatePublic);
}
simulateCall(txRequest: TxExecutionRequest, msgSender: AztecAddress): Promise<Vue> {
return this.pxe.simulateCall(txRequest, msgSender);
}
sendTx(tx: Tx): Promise<TxHash> {
return this.pxe.sendTx(tx);
}
Expand Down
7 changes: 6 additions & 1 deletion 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 } from '@aztec/foundation/abi';
import { ContractArtifact, DecodedReturn } from '@aztec/foundation/abi';
import { ContractClassWithId, ContractInstanceWithAddress } from '@aztec/types/contracts';
import { NodeInfo } from '@aztec/types/interfaces';

Expand All @@ -13,6 +13,10 @@ import { TxEffect } from '../tx_effect.js';
import { TxExecutionRequest } from '../tx_execution_request.js';
import { SyncStatus } from './sync-status.js';

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

// docs:start:pxe-interface
/**
* Private eXecution Environment (PXE) runs locally for each user, providing functionality for all the operations
Expand Down Expand Up @@ -140,6 +144,7 @@ export interface PXE {
* Also throws if simulatePublic is true and public simulation reverts.
*/
simulateTx(txRequest: TxExecutionRequest, simulatePublic: boolean): Promise<Tx>;
simulateCall(txRequest: TxExecutionRequest, msgSender?: AztecAddress): Promise<Vue>;

/**
* Sends a transaction to an Aztec node to be broadcasted to the network and mined.
Expand Down
30 changes: 28 additions & 2 deletions yarn-project/end-to-end/src/e2e_state_vars.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Wallet } from '@aztec/aztec.js';
import { DebugLogger, FunctionSelector, Wallet } from '@aztec/aztec.js';
import { decodeFunctionSignature } from '@aztec/foundation/abi';
import { DocsExampleContract } from '@aztec/noir-contracts.js';

import { setup } from './fixtures/utils.js';
Expand All @@ -8,18 +9,43 @@ describe('e2e_state_vars', () => {

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

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

beforeAll(async () => {
({ teardown, wallet } = await setup());
({ teardown, wallet, logger } = await setup());
contract = await DocsExampleContract.deploy(wallet).send().deployed();
}, 25_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();

Expand Down
68 changes: 68 additions & 0 deletions yarn-project/pxe/src/pxe_service/pxe_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ import {
TxHash,
TxL2Logs,
TxReceipt,
Vue,
isNoirCallStackUnresolved,
} from '@aztec/circuit-types';
import { TxPXEProcessingStats } from '@aztec/circuit-types/stats';
import {
AztecAddress,
CallContext,
CallRequest,
CompleteAddress,
FunctionData,
Expand All @@ -30,6 +32,7 @@ import {
MAX_REVERTIBLE_PUBLIC_CALL_STACK_LENGTH_PER_TX,
PartialAddress,
PrivateKernelTailCircuitPublicInputs,
Proof,
PublicCallRequest,
computeContractClassId,
getContractClassFromArtifact,
Expand Down Expand Up @@ -410,6 +413,71 @@ export class PXEService implements PXE {
});
}

public async simulateCall(txRequest: TxExecutionRequest, msgSender: AztecAddress | undefined = undefined) {
if (!txRequest.functionData.isPrivate) {
throw new Error(`Public entrypoints are not allowed`);
}
return await this.jobQueue.put(async () => {
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];*/
}
return vue;
});
}

async #simulateCall(txExecutionRequest: TxExecutionRequest, msgSender?: AztecAddress) {
// TODO - Pause syncing while simulating.
// Get values that allow us to reconstruct the block hash

// todo: We should likely do something here for the public calls when we are starting with those as well :thinking:

const sim = async (txRequest: TxExecutionRequest): Promise<ExecutionResult> => {
const { contractAddress, functionArtifact, portalContract } = await this.#getSimulationParameters(txRequest);
try {
const result = await this.simulator.run(
txRequest,
functionArtifact,
contractAddress,
portalContract,
msgSender,
);
return result;
} catch (err) {
if (err instanceof SimulationError) {
await this.#enrichSimulationError(err);
}
throw err;
}
};

const executionResult = await sim(txExecutionRequest);

const kernelOracle = new KernelOracle(this.contractDataOracle, this.keyStore, this.node);
const kernelProver = new KernelProver(kernelOracle);
this.log(`Executing kernel prover...`);
const { proof, publicInputs } = await kernelProver.prove(txExecutionRequest.toTxRequest(), executionResult);
this.log(
`Needs setup: ${publicInputs.needsSetup}, needs app logic: ${publicInputs.needsAppLogic}, needs teardown: ${publicInputs.needsTeardown}`,
);

const encryptedLogs = new TxL2Logs(collectEncryptedLogs(executionResult));
const unencryptedLogs = new TxL2Logs(collectUnencryptedLogs(executionResult));
const enqueuedPublicFunctions = collectEnqueuedPublicFunctionCalls(executionResult);

// HACK(#1639): Manually patches the ordering of the public call stack
// TODO(#757): Enforce proper ordering of enqueued public calls
await this.patchPublicCallStackOrdering(publicInputs, enqueuedPublicFunctions);

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

return new Vue(tx, executionResult.returnValues);
}

public async sendTx(tx: Tx): Promise<TxHash> {
const txHash = tx.getTxHash();
if (await this.node.getTxEffect(txHash)) {
Expand Down
10 changes: 8 additions & 2 deletions yarn-project/simulator/src/client/private_execution.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FunctionData, PrivateCallStackItem, PrivateCircuitPublicInputs } from '@aztec/circuits.js';
import { FunctionArtifactWithDebugMetadata, decodeReturnValues } from '@aztec/foundation/abi';
import { ABIType, FunctionArtifactWithDebugMetadata, decodeReturnValues } from '@aztec/foundation/abi';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { Fr } from '@aztec/foundation/fields';
import { createDebugLogger } from '@aztec/foundation/log';
Expand Down Expand Up @@ -52,7 +52,13 @@ export async function executePrivateFunction(
publicInputs.unencryptedLogPreimagesLength = new Fr(unencryptedLogs.getSerializedLength());

const callStackItem = new PrivateCallStackItem(contractAddress, functionData, publicInputs);
const returnValues = decodeReturnValues(artifact, publicInputs.returnValues);

// Mocking the return type to be an array of 4 fields
// TODO: @LHerskind must be updated as we are progressing with the macros to get the information
const returnTypes: ABIType[] = [{ kind: 'array', length: 4, type: { kind: 'field' } }];
const mockArtifact = { ...artifact, returnTypes };
const returnValues = decodeReturnValues(mockArtifact, publicInputs.returnValues);

const noteHashReadRequestPartialWitnesses = context.getNoteHashReadRequestPartialWitnesses(
publicInputs.noteHashReadRequests,
);
Expand Down

0 comments on commit 0ee9c1c

Please sign in to comment.