Skip to content

Commit

Permalink
test: no private key account state (#2491)
Browse files Browse the repository at this point in the history
Fixes #2458 

> Note: Now the Synchronizer throws if we request a sync status for
account which is not registered. I think this is the correct behavior
because when a dev requests this info for unregistered account it would
probably be a mistake even though returning false there is correct
(unregistered account is technically not synchronized).
  • Loading branch information
benesjan authored Sep 25, 2023
1 parent bf73bc3 commit 5813fb3
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 16 deletions.
7 changes: 5 additions & 2 deletions yarn-project/aztec-rpc/src/synchronizer/synchronizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,15 +247,18 @@ export class Synchronizer {
* @returns True if the account is fully synched, false otherwise.
* @remarks Checks whether all the notes from all the blocks have been processed. If it is not the case, the
* retrieved information from contracts might be old/stale (e.g. old token balance).
* @throws If checking a sync status of account which is not registered.
*/
public async isAccountStateSynchronized(account: AztecAddress) {
const completeAddress = await this.db.getCompleteAddress(account);
if (!completeAddress) {
return false;
throw new Error(`Checking if account is synched is not possible for ${account} because it is not registered.`);
}
const processor = this.noteProcessors.find(x => x.publicKey.equals(completeAddress.publicKey));
if (!processor) {
return false;
throw new Error(
`Checking if account is synched is not possible for ${account} because it is only registered as a recipient.`,
);
}
return await processor.isSynchronized();
}
Expand Down
79 changes: 65 additions & 14 deletions yarn-project/end-to-end/src/e2e_2_rpc_servers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@ import { toBigInt } from '@aztec/foundation/serialize';
import { ChildContract, TokenContract } from '@aztec/noir-contracts/types';
import { AztecRPC, CompleteAddress, TxStatus } from '@aztec/types';

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

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

const { SANDBOX_URL = '' } = process.env;

const TIMEOUT = 60_000;

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

let aztecNode: AztecNodeService | undefined;
let aztecRpcServerA: AztecRPC;
let aztecRpcServerB: AztecRPC;
Expand Down Expand Up @@ -66,9 +72,12 @@ describe('e2e_2_rpc_servers', () => {
tokenAddress: AztecAddress,
owner: AztecAddress,
expectedBalance: bigint,
checkIfSynchronized = true,
) => {
// First wait until the corresponding RPC server has synchronized the account
await awaitUserSynchronized(wallet, owner);
if (checkIfSynchronized) {
// First wait until the corresponding RPC server has synchronized the account
await awaitUserSynchronized(wallet, owner);
}

// Then check the balance
const contractWithWallet = await TokenContract.at(tokenAddress, wallet);
Expand All @@ -77,24 +86,28 @@ describe('e2e_2_rpc_servers', () => {
expect(balance).toBe(expectedBalance);
};

const deployTokenContract = async (initialBalance: bigint, owner: AztecAddress) => {
const deployTokenContract = async (initialAdminBalance: bigint, admin: AztecAddress) => {
logger(`Deploying Token contract...`);
const contract = await TokenContract.deploy(walletA).send().deployed();
expect((await contract.methods._initialize(owner).send().wait()).status).toBe(TxStatus.MINED);
expect((await contract.methods._initialize(admin).send().wait()).status).toBe(TxStatus.MINED);

if (initialAdminBalance > 0n) {
await mintTokens(contract, admin, initialAdminBalance);
}

logger('L2 contract deployed');

return contract.completeAddress;
};

const mintTokens = async (contract: TokenContract, recipient: AztecAddress, balance: bigint) => {
const secret = Fr.random();
const secretHash = await computeMessageSecretHash(secret);

expect((await contract.methods.mint_private(initialBalance, secretHash).send().wait()).status).toEqual(
expect((await contract.methods.mint_private(balance, secretHash).send().wait()).status).toEqual(TxStatus.MINED);
expect((await contract.methods.redeem_shield(recipient, balance, secret).send().wait()).status).toEqual(
TxStatus.MINED,
);
expect((await contract.methods.redeem_shield(owner, initialBalance, secret).send().wait()).status).toEqual(
TxStatus.MINED,
);

logger('L2 contract deployed');

return contract.completeAddress;
};

it('transfers fund from user A to B via RPC server A followed by transfer from B to A via RPC server B', async () => {
Expand All @@ -110,7 +123,7 @@ describe('e2e_2_rpc_servers', () => {
// Add account A to wallet B
await aztecRpcServerB.registerRecipient(userA);

// Add token to RPC server B
// Add token to RPC server B (RPC server A already has it because it was deployed through it)
await aztecRpcServerB.addContracts([
{
abi: TokenContract.abi,
Expand Down Expand Up @@ -191,5 +204,43 @@ describe('e2e_2_rpc_servers', () => {

const storedValue = await getChildStoredValue(childCompleteAddress, aztecRpcServerB);
expect(storedValue).toBe(newValueToSet);
}, 60_000);
});

it('private state is "zero" when Aztec RPC Server does not have the account private key', async () => {
const userABalance = 100n;
const userBBalance = 150n;

const completeTokenAddress = await deployTokenContract(userABalance, userA.address);
const contractWithWalletA = await TokenContract.at(completeTokenAddress.address, walletA);

// Add account B to wallet A
await aztecRpcServerA.registerRecipient(userB);
// Add account A to wallet B
await aztecRpcServerB.registerRecipient(userA);

// Add token to RPC server B (RPC server A already has it because it was deployed through it)
await aztecRpcServerB.addContracts([
{
abi: TokenContract.abi,
completeAddress: completeTokenAddress,
portalContract: EthAddress.ZERO,
},
]);

// Mint tokens to user B
await mintTokens(contractWithWalletA, userB.address, userBBalance);

// Check that user A balance is 100 on server A
await expectTokenBalance(walletA, completeTokenAddress.address, userA.address, userABalance);
// Check that user B balance is 150 on server B
await expectTokenBalance(walletB, completeTokenAddress.address, userB.address, userBBalance);

// CHECK THAT PRIVATE BALANCES ARE 0 WHEN ACCOUNT'S PRIVATE KEYS ARE NOT REGISTERED
// Note: Not checking if the account is synchronized because it is not registered as an account (it would throw).
const checkIfSynchronized = false;
// Check that user A balance is 0 on server B
await expectTokenBalance(walletB, completeTokenAddress.address, userA.address, 0n, checkIfSynchronized);
// Check that user B balance is 0 on server A
await expectTokenBalance(walletA, completeTokenAddress.address, userB.address, 0n, checkIfSynchronized);
});
});
1 change: 1 addition & 0 deletions yarn-project/types/src/interfaces/aztec_rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ export interface AztecRPC {
* @deprecated Use `getSyncStatus` instead.
* @remarks Checks whether all the notes from all the blocks have been processed. If it is not the case, the
* retrieved information from contracts might be old/stale (e.g. old token balance).
* @throws If checking a sync status of account which is not registered.
*/
isAccountStateSynchronized(account: AztecAddress): Promise<boolean>;

Expand Down

0 comments on commit 5813fb3

Please sign in to comment.