From 5813fb365f63d4921dcfd53b205a15f14065e213 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Bene=C5=A1?= Date: Mon, 25 Sep 2023 16:30:14 +0200 Subject: [PATCH] test: no private key account state (#2491) 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). --- .../src/synchronizer/synchronizer.ts | 7 +- .../end-to-end/src/e2e_2_rpc_servers.test.ts | 79 +++++++++++++++---- .../types/src/interfaces/aztec_rpc.ts | 1 + 3 files changed, 71 insertions(+), 16 deletions(-) diff --git a/yarn-project/aztec-rpc/src/synchronizer/synchronizer.ts b/yarn-project/aztec-rpc/src/synchronizer/synchronizer.ts index b3cc55363a3..e8e69c7b004 100644 --- a/yarn-project/aztec-rpc/src/synchronizer/synchronizer.ts +++ b/yarn-project/aztec-rpc/src/synchronizer/synchronizer.ts @@ -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(); } diff --git a/yarn-project/end-to-end/src/e2e_2_rpc_servers.test.ts b/yarn-project/end-to-end/src/e2e_2_rpc_servers.test.ts index 856b6c354da..91c4e736dbc 100644 --- a/yarn-project/end-to-end/src/e2e_2_rpc_servers.test.ts +++ b/yarn-project/end-to-end/src/e2e_2_rpc_servers.test.ts @@ -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; @@ -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); @@ -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 () => { @@ -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, @@ -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); + }); }); diff --git a/yarn-project/types/src/interfaces/aztec_rpc.ts b/yarn-project/types/src/interfaces/aztec_rpc.ts index d362e4fc728..2c487329fac 100644 --- a/yarn-project/types/src/interfaces/aztec_rpc.ts +++ b/yarn-project/types/src/interfaces/aztec_rpc.ts @@ -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;