Skip to content

Commit

Permalink
feat(SimulateTx): public constrained view messy (#5433)
Browse files Browse the repository at this point in the history
  • Loading branch information
LHerskind committed Apr 2, 2024
1 parent 24be74b commit 874886b
Show file tree
Hide file tree
Showing 75 changed files with 704 additions and 678 deletions.
2 changes: 1 addition & 1 deletion boxes/boxes/react/src/hooks/useNumber.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export function useNumber({ contract }: { contract: Contract }) {

setWait(true);
const deployerWallet = await deployerEnv.getWallet();
const viewTxReceipt = await contract!.methods.getNumber(deployerWallet.getCompleteAddress()).view();
const viewTxReceipt = await contract!.methods.getNumber(deployerWallet.getCompleteAddress()).simulate();
toast(`Number is: ${viewTxReceipt.value}`);
setWait(false);
};
Expand Down
2 changes: 1 addition & 1 deletion boxes/boxes/react/tests/node.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('BoxReact Contract Tests', () => {
}, 40000);

test('Can read a number', async () => {
const viewTxReceipt = await contract.methods.getNumber(wallet.getCompleteAddress()).view();
const viewTxReceipt = await contract.methods.getNumber(wallet.getCompleteAddress()).simulate();
expect(numberToSet.toBigInt()).toEqual(viewTxReceipt.value);
}, 40000);
});
2 changes: 1 addition & 1 deletion boxes/boxes/vanilla/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@ document.querySelector('#set').addEventListener('submit', async (e: Event) => {
});

document.querySelector('#get').addEventListener('click', async () => {
const viewTxReceipt = await contract.methods.getNumber(wallet.getCompleteAddress().address).view();
const viewTxReceipt = await contract.methods.getNumber(wallet.getCompleteAddress().address).simulate();
alert(`Number is: ${viewTxReceipt.value}`);
});
2 changes: 1 addition & 1 deletion docs/docs/developers/tutorials/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ WARN Error processing tx 06dc87c4d64462916ea58426ffcfaf20017880b353c9ec3e0f0ee5f

### State

We can check private or public state directly rather than going through view-only methods, as we did in the initial example by calling `token.methods.balance().view()`. Bear in mind that directly accessing contract storage will break any kind of encapsulation.
We can check private or public state directly rather than going through view-only methods, as we did in the initial example by calling `token.methods.balance().simulate()`. Bear in mind that directly accessing contract storage will break any kind of encapsulation.

To query storage directly, you'll need to know the slot you want to access. This can be checked in the [contract's `Storage` definition](../contracts/writing_contracts/storage/main.md) directly for most data types. However, when it comes to mapping types, as in most EVM languages, we'll need to calculate the slot for a given key. To do this, we'll use the [`CheatCodes`](../sandbox/references/cheat_codes.md) utility class:

Expand Down
4 changes: 2 additions & 2 deletions docs/docs/misc/migration_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -935,13 +935,13 @@ To parse a `AztecAddress` to BigInt, use `.inner`
Before:

```js
const tokenBigInt = await bridge.methods.token().view();
const tokenBigInt = await bridge.methods.token().simulate();
```

Now:

```js
const tokenBigInt = (await bridge.methods.token().view()).inner;
const tokenBigInt = (await bridge.methods.token().simulate()).inner;
```

### [Aztec.nr] Add `protocol_types` to Nargo.toml
Expand Down
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
2 changes: 1 addition & 1 deletion yarn-project/accounts/src/testing/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export async function deployInitialTestAccounts(pxe: PXE) {
skipPublicDeployment: true,
universalDeploy: true,
});
await deployMethod.simulate({});
await deployMethod.prove({});
return deployMethod;
}),
);
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/accounts/src/testing/create_account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export async function createAccounts(pxe: PXE, numberOfAccounts = 1): Promise<Ac
// the results get stored within the account object. By calling it here we increase the probability of all the
// accounts being deployed in the same block because it makes the deploy() method basically instant.
await account.getDeployMethod().then(d =>
d.simulate({
d.prove({
contractAddressSalt: account.salt,
skipClassRegistration: true,
skipPublicDeployment: true,
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 @@ -663,7 +663,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 @@ -674,6 +674,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
4 changes: 2 additions & 2 deletions yarn-project/aztec.js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ const tx = await contract.methods.transfer(amount, recipientAddress).send().wait
console.log(`Transferred ${amount} to ${recipientAddress} on block ${tx.blockNumber}`);
```

### Call a view function
### Simulate a function

```typescript
import { Contract } from '@aztec/aztec.js';

const contract = await Contract.at(contractAddress, MyContractArtifact, wallet);
const balance = await contract.methods.get_balance(wallet.getAddress()).view();
const balance = await contract.methods.get_balance(wallet.getAddress()).simulate();
console.log(`Account balance is ${balance}`);
```
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ export abstract class BaseContractInteraction {
public abstract create(options?: SendMethodOptions): Promise<TxExecutionRequest>;

/**
* Simulates a transaction execution request and returns a tx object ready to be sent.
* Proves a transaction execution request and returns a tx object ready to be sent.
* @param options - optional arguments to be used in the creation of the transaction
* @returns The resulting transaction
*/
public async simulate(options: SendMethodOptions = {}): Promise<Tx> {
public async prove(options: SendMethodOptions = {}): Promise<Tx> {
const txRequest = this.txRequest ?? (await this.create(options));
this.tx = await this.pxe.simulateTx(txRequest, !options.skipPublicSimulation);
this.tx = await this.pxe.proveTx(txRequest, !options.skipPublicSimulation);
return this.tx;
}

Expand All @@ -58,7 +58,7 @@ export abstract class BaseContractInteraction {
*/
public send(options: SendMethodOptions = {}) {
const promise = (async () => {
const tx = this.tx ?? (await this.simulate(options));
const tx = this.tx ?? (await this.prove(options));
return this.pxe.sendTx(tx);
})();

Expand Down
14 changes: 4 additions & 10 deletions yarn-project/aztec.js/src/contract/contract.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type Tx, type TxExecutionRequest, type TxHash, type TxReceipt } from '@aztec/circuit-types';
import { AztecAddress, CompleteAddress, EthAddress } from '@aztec/circuits.js';
import { type L1ContractAddresses } from '@aztec/ethereum';
import { ABIParameterVisibility, type ContractArtifact, FunctionType } from '@aztec/foundation/abi';
import { ABIParameterVisibility, type ContractArtifact, type DecodedReturn, FunctionType } from '@aztec/foundation/abi';
import { type NodeInfo } from '@aztec/types/interfaces';

import { type MockProxy, mock } from 'jest-mock-extended';
Expand Down Expand Up @@ -113,10 +113,10 @@ describe('Contract Class', () => {
wallet.createTxExecutionRequest.mockResolvedValue(mockTxRequest);
wallet.getContractInstance.mockResolvedValue(contractInstance);
wallet.sendTx.mockResolvedValue(mockTxHash);
wallet.viewTx.mockResolvedValue(mockViewResultValue);
wallet.viewTx.mockResolvedValue(mockViewResultValue as any as DecodedReturn);
wallet.getTxReceipt.mockResolvedValue(mockTxReceipt);
wallet.getNodeInfo.mockResolvedValue(mockNodeInfo);
wallet.simulateTx.mockResolvedValue(mockTx);
wallet.proveTx.mockResolvedValue(mockTx);
wallet.getRegisteredAccounts.mockResolvedValue([account]);
});

Expand All @@ -137,7 +137,7 @@ describe('Contract Class', () => {

it('should call view on an unconstrained function', async () => {
const fooContract = await Contract.at(contractAddress, defaultArtifact, wallet);
const result = await fooContract.methods.qux(123n).view({
const result = await fooContract.methods.qux(123n).simulate({
from: account.address,
});
expect(wallet.viewTx).toHaveBeenCalledTimes(1);
Expand All @@ -149,10 +149,4 @@ describe('Contract Class', () => {
const fooContract = await Contract.at(contractAddress, defaultArtifact, wallet);
await expect(fooContract.methods.qux().create()).rejects.toThrow();
});

it('should not call view on a secret or open function', async () => {
const fooContract = await Contract.at(contractAddress, defaultArtifact, wallet);
expect(() => fooContract.methods.bar().view()).toThrow();
expect(() => fooContract.methods.baz().view()).toThrow();
});
});
52 changes: 15 additions & 37 deletions yarn-project/aztec.js/src/contract/contract_function_interaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,44 +63,23 @@ export class ContractFunctionInteraction extends BaseContractInteraction {
}

/**
* 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.
* Simulate a transaction and get its return values
* @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 view(options: ViewMethodOptions = {}) {
if (this.functionDao.functionType !== FunctionType.UNCONSTRAINED) {
throw new Error('Can only call `view` on an unconstrained function.');
public async simulate(options: ViewMethodOptions = {}): Promise<any> {
if (this.functionDao.functionType == FunctionType.UNCONSTRAINED) {
return this.wallet.viewTx(this.functionDao.name, this.args, this.contractAddress, options.from);
}

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:
// TODO: If not unconstrained, we return a size 4 array of fields.
// TODO: It should instead return the correctly decoded value
// TODO: The return type here needs to be fixed! @LHerskind

const packedArgs = PackedArguments.fromArgs(encodeArguments(this.functionDao, this.args));
if (this.functionDao.functionType == FunctionType.SECRET) {
const nodeInfo = await this.wallet.getNodeInfo();
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,
Expand All @@ -109,13 +88,12 @@ export class ContractFunctionInteraction extends BaseContractInteraction {
packedArguments: [packedArgs],
authWitnesses: [],
});

const vue = await this.pxe.simulateCall(txRequest, options.from ?? this.wallet.getAddress());
return vue.rv;
const vue = await this.pxe.simulateTx(txRequest, false, options.from ?? this.wallet.getAddress());
return vue.privateReturnValues && vue.privateReturnValues[0];
} else {
await this.create();
const vue = await this.pxe.simulateCall(this.txRequest!);
return vue.rv;
const txRequest = await this.create();
const vue = await this.pxe.simulateTx(txRequest, true);
return vue.publicReturnValues && vue.publicReturnValues[0];
}
}
}
8 changes: 4 additions & 4 deletions yarn-project/aztec.js/src/contract/deploy_method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,12 +193,12 @@ export class DeployMethod<TContract extends ContractBase = Contract> extends Bas
}

/**
* Simulate the request.
* Prove the request.
* @param options - Deployment options.
* @returns The simulated tx.
* @returns The proven tx.
*/
public simulate(options: DeployOptions): Promise<Tx> {
return super.simulate(options);
public prove(options: DeployOptions): Promise<Tx> {
return super.prove(options);
}

/** Return this deployment address. */
Expand Down
6 changes: 3 additions & 3 deletions yarn-project/aztec.js/src/contract/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
*
* The {@link Contract} class is the main class in this module, and provides static methods for deploying
* a contract or interacting with an already deployed one. The `methods` property of the contract instance
* provides access to private, public, and view methods, that can be invoked in a transaction via `send()`,
* or can be queried via `view()`.
* provides access to private, public, and simulate methods, that can be invoked in a transaction via `send()`,
* or can be queried via `simulate()`.
*
* ```ts
* const contract = await Contract.deploy(wallet, MyContractArtifact, [...constructorArgs]).send().deployed();
Expand All @@ -17,7 +17,7 @@
* ```ts
* const contract = await Contract.at(address, MyContractArtifact, wallet);
* await contract.methods.mint(1000, owner).send().wait();
* console.log(`Total supply is now ${await contract.methods.totalSupply().view()}`);
* console.log(`Total supply is now ${await contract.methods.totalSupply().simulate()}`);
* ```
*
* The result of calling a method in a contract instance, such as `contract.methods.mint(1000, owner)`
Expand Down
3 changes: 2 additions & 1 deletion yarn-project/aztec.js/src/rpc_clients/pxe_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
TxHash,
TxReceipt,
UnencryptedL2BlockL2Logs,
Vue,
} from '@aztec/circuit-types';
import {
AztecAddress,
Expand Down Expand Up @@ -53,7 +54,7 @@ export const createPXEClient = (url: string, fetch = makeFetch([1, 2, 3], false)
TxExecutionRequest,
TxHash,
},
{ Tx, TxReceipt, EncryptedL2BlockL2Logs, UnencryptedL2BlockL2Logs, NullifierMembershipWitness },
{ Tx, Vue, TxReceipt, EncryptedL2BlockL2Logs, UnencryptedL2BlockL2Logs, NullifierMembershipWitness },
false,
'pxe',
fetch,
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/aztec.js/src/wallet/account_wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ export class AccountWallet extends BaseWallet {
messageHash,
]);

const [isValidInPrivate, isValidInPublic] = await interaction.view();
const [isValidInPrivate, isValidInPublic] = (await interaction.simulate()) as [boolean, boolean];
return { isValidInPrivate, isValidInPublic };
}

Expand Down
8 changes: 4 additions & 4 deletions yarn-project/aztec.js/src/wallet/base_wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,11 @@ export abstract class BaseWallet implements Wallet {
getContracts(): Promise<AztecAddress[]> {
return this.pxe.getContracts();
}
simulateTx(txRequest: TxExecutionRequest, simulatePublic: boolean): Promise<Tx> {
return this.pxe.simulateTx(txRequest, simulatePublic);
proveTx(txRequest: TxExecutionRequest, simulatePublic: boolean): Promise<Tx> {
return this.pxe.proveTx(txRequest, simulatePublic);
}
simulateCall(txRequest: TxExecutionRequest, msgSender: AztecAddress): Promise<Vue> {
return this.pxe.simulateCall(txRequest, msgSender);
simulateTx(txRequest: TxExecutionRequest, simulatePublic: boolean, msgSender: AztecAddress): Promise<Vue> {
return this.pxe.simulateTx(txRequest, simulatePublic, msgSender);
}
sendTx(tx: Tx): Promise<TxHash> {
return this.pxe.sendTx(tx);
Expand Down
6 changes: 3 additions & 3 deletions yarn-project/aztec/src/examples/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,18 +75,18 @@ async function main() {
await tokenAlice.methods.redeem_shield(alice, ALICE_MINT_BALANCE, aliceSecret).send().wait();
logger(`${ALICE_MINT_BALANCE} tokens were successfully minted and redeemed by Alice`);

const balanceAfterMint = await tokenAlice.methods.balance_of_private(alice).view();
const balanceAfterMint = await tokenAlice.methods.balance_of_private(alice).simulate();
logger(`Tokens successfully minted. New Alice's balance: ${balanceAfterMint}`);

// We will now transfer tokens from Alice to Bob
logger(`Transferring ${TRANSFER_AMOUNT} tokens from Alice to Bob...`);
await tokenAlice.methods.transfer(alice, bob, TRANSFER_AMOUNT, 0).send().wait();

// Check the new balances
const aliceBalance = await tokenAlice.methods.balance_of_private(alice).view();
const aliceBalance = await tokenAlice.methods.balance_of_private(alice).simulate();
logger(`Alice's balance ${aliceBalance}`);

const bobBalance = await tokenBob.methods.balance_of_private(bob).view();
const bobBalance = await tokenBob.methods.balance_of_private(bob).simulate();
logger(`Bob's balance ${bobBalance}`);
}

Expand Down
Loading

0 comments on commit 874886b

Please sign in to comment.