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

test: AztecRPC API using sandbox #1568

Merged
merged 15 commits into from
Aug 17, 2023
13 changes: 13 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -915,6 +915,17 @@ jobs:
name: "Test"
command: cond_spot_run_tests end-to-end e2e_aztec_js_browser.test.ts docker-compose-e2e-sandbox.yml

aztec-rpc-sandbox:
docker:
- image: aztecprotocol/alpine-build-image
resource_class: small
steps:
- *checkout
- *setup_env
- run:
name: "Test"
command: cond_spot_run_tests end-to-end aztec_rpc_sandbox.test.ts docker-compose-e2e-sandbox.yml

e2e-canary-test:
docker:
- image: aztecprotocol/alpine-build-image
Expand Down Expand Up @@ -1301,6 +1312,7 @@ workflows:
- e2e-p2p: *e2e_test
- e2e-canary-test: *e2e_test
- e2e-browser-sandbox: *e2e_test
- aztec-rpc-sandbox: *e2e_test

- e2e-end:
requires:
Expand All @@ -1326,6 +1338,7 @@ workflows:
- e2e-p2p
- e2e-browser-sandbox
- e2e-canary-test
- aztec-rpc-sandbox
<<: *defaults

- deploy-dockerhub:
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 @@ -71,7 +71,7 @@ export class AztecNodeService implements AztecNode {
// first create and sync the archiver
const archiver = await Archiver.createAndSync(config);

// we idenfity the P2P transaction protocol by using the rollup contract address.
// we identify the P2P transaction protocol by using the rollup contract address.
// this may well change in future
config.transactionProtocol = `/aztec/tx/${config.rollupContract.toString()}`;

Expand Down

This file was deleted.

14 changes: 6 additions & 8 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 @@ -100,12 +100,9 @@ export class AztecRPCServer implements AztecRPC {
return accounts;
}

public async getAccount(address: AztecAddress): Promise<CompleteAddress> {
public async getAccount(address: AztecAddress): Promise<CompleteAddress | undefined> {
const result = await this.getAccounts();
const account = result.find(r => r.address.equals(address));
if (!account) {
throw new Error(`Unable to get complete address for address ${address.toString()}`);
}
return Promise.resolve(account);
}

Expand All @@ -123,12 +120,9 @@ export class AztecRPCServer implements AztecRPC {
return recipients;
}

public async getRecipient(address: AztecAddress): Promise<CompleteAddress> {
public async getRecipient(address: AztecAddress): Promise<CompleteAddress | undefined> {
const result = await this.getRecipients();
const recipient = result.find(r => r.address.equals(address));
if (!recipient) {
throw new Error(`Unable to get complete address for address ${address.toString()}`);
}
return Promise.resolve(recipient);
}

Expand All @@ -142,6 +136,10 @@ export class AztecRPCServer implements AztecRPC {
}
}

public async getContracts(): Promise<AztecAddress[]> {
return (await this.db.getContracts()).map(c => c.address);
}

public async getPublicStorageAt(contract: AztecAddress, storageSlot: Fr) {
if ((await this.getContractData(contract)) === undefined) {
throw new Error(`Contract ${contract.toString()} is not deployed`);
Expand Down
1 change: 1 addition & 0 deletions yarn-project/aztec-rpc/src/aztec_rpc_server/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './aztec_rpc_server.js';
export * from './create_aztec_rpc_server.js';
export { aztecRpcTestSuite } from './test/aztec_rpc_test_suite.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Grumpkin } from '@aztec/circuits.js/barretenberg';
import { TestKeyStore } from '@aztec/key-store';
import { AztecNode, AztecRPC } from '@aztec/types';

import { mock } from 'jest-mock-extended';

import { MemoryDB } from '../../database/memory_db.js';
import { EthAddress, RpcServerConfig } from '../../index.js';
import { AztecRPCServer } from '../aztec_rpc_server.js';
import { aztecRpcTestSuite } from './aztec_rpc_test_suite.js';

async function createAztecRpcServer(): Promise<AztecRPC> {
const keyStore = new TestKeyStore(await Grumpkin.new());
const node = mock<AztecNode>();
const db = new MemoryDB();
const config: RpcServerConfig = {
l2BlockPollingIntervalMS: 100,
};

// Setup the relevant mocks
node.getBlockHeight.mockResolvedValue(2);
node.getVersion.mockResolvedValue(1);
node.getChainId.mockResolvedValue(1);
node.getRollupAddress.mockResolvedValue(EthAddress.random());

return new AztecRPCServer(keyStore, node, db, config);
}

aztecRpcTestSuite('AztecRPCServer', createAztecRpcServer);
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { AztecAddress, CompleteAddress, Fr, FunctionData, TxContext } from '@aztec/circuits.js';
import { Grumpkin } from '@aztec/circuits.js/barretenberg';
import { ConstantKeyPair } from '@aztec/key-store';
import {
AztecRPC,
DeployedContract,
INITIAL_L2_BLOCK_NUM,
TxExecutionRequest,
randomDeployedContract,
} from '@aztec/types';

export const aztecRpcTestSuite = (testName: string, aztecRpcSetup: () => Promise<AztecRPC>) => {
describe(testName, function () {
let rpc: AztecRPC;

beforeAll(async () => {
rpc = await aztecRpcSetup();
}, 120_000);

it('registers an account and returns it as an account only and not as a recipient', async () => {
const keyPair = ConstantKeyPair.random(await Grumpkin.new());
const completeAddress = await CompleteAddress.fromPrivateKey(await keyPair.getPrivateKey());

await rpc.registerAccount(await keyPair.getPrivateKey(), completeAddress);

// Check that the account is correctly registered using the getAccounts and getRecipients methods
const accounts = await rpc.getAccounts();
const recipients = await rpc.getRecipients();
expect(accounts).toContainEqual(completeAddress);
expect(recipients).not.toContainEqual(completeAddress);

// Check that the account is correctly registered using the getAccount and getRecipient methods
const account = await rpc.getAccount(completeAddress.address);
const recipient = await rpc.getRecipient(completeAddress.address);
expect(account).toEqual(completeAddress);
expect(recipient).toBeUndefined();
});

it('registers a recipient and returns it as a recipient only and not as an account', async () => {
const completeAddress = await CompleteAddress.random();

await rpc.registerRecipient(completeAddress);

// Check that the recipient is correctly registered using the getAccounts and getRecipients methods
const accounts = await rpc.getAccounts();
const recipients = await rpc.getRecipients();
expect(accounts).not.toContainEqual(completeAddress);
expect(recipients).toContainEqual(completeAddress);

// Check that the recipient is correctly registered using the getAccount and getRecipient methods
const account = await rpc.getAccount(completeAddress.address);
const recipient = await rpc.getRecipient(completeAddress.address);
expect(account).toBeUndefined();
expect(recipient).toEqual(completeAddress);
});

it('cannot register the same account twice', async () => {
const keyPair = ConstantKeyPair.random(await Grumpkin.new());
const completeAddress = await CompleteAddress.fromPrivateKey(await keyPair.getPrivateKey());

await rpc.registerAccount(await keyPair.getPrivateKey(), completeAddress);
await expect(async () => rpc.registerAccount(await keyPair.getPrivateKey(), completeAddress)).rejects.toThrow(
`Complete address corresponding to ${completeAddress.address} already exists`,
);
});

it('cannot register the same recipient twice', async () => {
const completeAddress = await CompleteAddress.random();

await rpc.registerRecipient(completeAddress);
await expect(() => rpc.registerRecipient(completeAddress)).rejects.toThrow(
`Complete address corresponding to ${completeAddress.address} already exists`,
);
});

it('successfully adds a contract', async () => {
const contracts: DeployedContract[] = [randomDeployedContract(), randomDeployedContract()];
await rpc.addContracts(contracts);

const expectedContractAddresses = contracts.map(contract => contract.address);
const contractAddresses = await rpc.getContracts();

// check if all the contracts were returned
expect(contractAddresses).toEqual(expect.arrayContaining(expectedContractAddresses));
});

it('throws when simulating a tx targeting public entrypoint', async () => {
const functionData = FunctionData.empty();
functionData.isPrivate = false;
const txExecutionRequest = new TxExecutionRequest(
AztecAddress.random(),
functionData,
new Fr(0),
TxContext.empty(),
[],
);

await expect(async () => await rpc.simulateTx(txExecutionRequest)).rejects.toThrow(
'Public entrypoints are not allowed',
);
});

// Note: Not testing a successful run of `simulateTx`, `sendTx`, `getTxReceipt` and `viewTx` here as it requires
// a larger setup and it's sufficiently tested in the e2e tests.

it('throws when getting public storage for non-existent contract', async () => {
const contract = AztecAddress.random();
await expect(async () => await rpc.getPublicStorageAt(contract, new Fr(0n))).rejects.toThrow(
`Contract ${contract.toString()} is not deployed`,
);
});

// Note: Not testing `getContractDataAndBytecode`, `getContractData` and `getUnencryptedLogs` here as these
// functions only call AztecNode and these methods are frequently used by the e2e tests.

it('successfully gets a block number', async () => {
const blockNum = await rpc.getBlockNum();
expect(blockNum).toBeGreaterThanOrEqual(INITIAL_L2_BLOCK_NUM);
});

it('successfully gets node info', async () => {
const nodeInfo = await rpc.getNodeInfo();
expect(nodeInfo.version).toBeDefined();
expect(nodeInfo.chainId).toBeDefined();
expect(nodeInfo.rollupAddress).toBeDefined();
});

// Note: Not testing `isGlobalStateSynchronised`, `isAccountStateSynchronised` and `getSyncStatus` as these methods
// only call synchroniser.
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,14 @@ export class MemoryContractDatabase implements ContractDatabase {
* @param address - The AztecAddress to search for in the stored contracts.
* @returns A Promise resolving to the ContractDao instance matching the given address or undefined.
*/
public getContract(address: AztecAddress) {
public getContract(address: AztecAddress): Promise<ContractDao | undefined> {
return Promise.resolve(this.contracts.find(c => c.address.equals(address)));
}

public getContracts(): Promise<ContractDao[]> {
return Promise.resolve(this.contracts);
}

/**
* Retrieve the bytecode associated with a given contract address and function selector.
* This function searches through the stored contracts to find a matching contract and function,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export class EntrypointCollection implements Entrypoint {

/**
* Registers an entrypoint against an aztec address
* @param addr - The aztec address agianst which to register the implementation.
* @param addr - The aztec address against which to register the implementation.
* @param impl - The entrypoint to be registered.
*/
public registerAccount(addr: AztecAddress, impl: Entrypoint) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from '@aztec/types';

export { mustSucceedFetch } from '@aztec/foundation/json-rpc/client';
export { mustSucceedFetchUnlessNoRetry } from '@aztec/foundation/json-rpc/client';

export const createAztecRpcClient = (url: string, fetch = defaultFetch): AztecRPC =>
createJsonRpcClient<AztecRPC>(
Expand Down
3 changes: 3 additions & 0 deletions yarn-project/aztec.js/src/aztec_rpc_client/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ export abstract class BaseWallet implements Wallet {
addContracts(contracts: DeployedContract[]): Promise<void> {
return this.rpc.addContracts(contracts);
}
getContracts(): Promise<AztecAddress[]> {
return this.rpc.getContracts();
}
simulateTx(txRequest: TxExecutionRequest): Promise<Tx> {
return this.rpc.simulateTx(txRequest);
}
Expand Down
Loading