Skip to content

Commit

Permalink
feat(aztec-js): tx.wait waits for rpc to be synced
Browse files Browse the repository at this point in the history
  • Loading branch information
spalladino committed Aug 2, 2023
1 parent 2f66de1 commit 5cd1b12
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -445,4 +445,8 @@ export class AztecRPCServer implements AztecRPC {
public async isAccountSynchronised(account: AztecAddress) {
return await this.synchroniser.isAccountSynchronised(account);
}

public getSyncStatus() {
return Promise.resolve(this.synchroniser.getSyncStatus());
}
}
7 changes: 7 additions & 0 deletions yarn-project/aztec-rpc/src/note_processor/note_processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ export class NoteProcessor {
return this.syncedToBlock === remoteBlockHeight;
}

/**
* Returns synchronisation status (ie up to which block has been synced ) for this note processor.
*/
public get status() {
return { syncedToBlock: this.syncedToBlock };
}

/**
* Process the given L2 block contexts and encrypted logs to update the note processor.
* It synchronizes the user's account by decrypting the encrypted logs and processing
Expand Down
11 changes: 11 additions & 0 deletions yarn-project/aztec-rpc/src/synchroniser/synchroniser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,17 @@ export class Synchroniser {
return latest <= this.synchedToBlock;
}

/**
* Returns the latest block that has been synchronised by the synchronizer and each account.
* @returns The latest block synchronised for blocks, and the latest block synched for notes for each public key being tracked.
*/
public getSyncStatus() {
return {
blocks: this.synchedToBlock,
notes: Object.fromEntries(this.noteProcessors.map(n => [n.publicKey.toString(), n.status.syncedToBlock])),
};
}

/**
* Updates the block information for all transactions in a given block context.
* The function retrieves transaction data objects from the database using their hashes,
Expand Down
2 changes: 2 additions & 0 deletions yarn-project/aztec.js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,15 @@
"@aztec/circuits.js": "workspace:^",
"@aztec/foundation": "workspace:^",
"@aztec/types": "workspace:^",
"lodash.every": "^4.6.0",
"lodash.partition": "^4.6.0",
"tslib": "^2.4.0"
},
"devDependencies": {
"@jest/globals": "^29.5.0",
"@rushstack/eslint-patch": "^1.1.4",
"@types/jest": "^29.5.0",
"@types/lodash.every": "^4.6.7",
"@types/lodash.partition": "^4.6.0",
"@types/node": "^18.7.23",
"buffer": "^6.0.3",
Expand Down
4 changes: 4 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 @@ -7,6 +7,7 @@ import {
ExecutionRequest,
L2BlockL2Logs,
NodeInfo,
SyncStatus,
Tx,
TxExecutionRequest,
TxHash,
Expand Down Expand Up @@ -94,6 +95,9 @@ export abstract class BaseWallet implements Wallet {
isAccountSynchronised(account: AztecAddress) {
return this.rpc.isAccountSynchronised(account);
}
getSyncStatus(): Promise<SyncStatus> {
return this.rpc.getSyncStatus();
}
}

/**
Expand Down
46 changes: 46 additions & 0 deletions yarn-project/aztec.js/src/contract/sent_tx.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { AztecRPC, TxHash, TxReceipt, TxStatus } from '@aztec/types';

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

import { SentTx } from './sent_tx.js';

describe('SentTx', () => {
let rpc: MockProxy<AztecRPC>;
let txHashPromise: Promise<TxHash>;

let sentTx: SentTx;

beforeEach(() => {
rpc = mock();
txHashPromise = Promise.resolve(TxHash.fromBigInt(1n));
sentTx = new SentTx(rpc, txHashPromise);
});

describe('wait', () => {
let txReceipt: TxReceipt;
beforeEach(() => {
txReceipt = { status: TxStatus.MINED, blockNumber: 20 } as TxReceipt;
rpc.getTxReceipt.mockResolvedValue(txReceipt);
});

it('waits for all notes accounts to be synced', async () => {
rpc.getSyncStatus
.mockResolvedValueOnce({ blocks: 25, notes: { '0x1': 19, '0x2': 20 } })
.mockResolvedValueOnce({ blocks: 25, notes: { '0x1': 20, '0x2': 20 } });

const actual = await sentTx.wait({ timeout: 1, interval: 0.4 });
expect(actual).toEqual(txReceipt);
});

it('fails if an account is not synced', async () => {
rpc.getSyncStatus.mockResolvedValue({ blocks: 25, notes: { '0x1': 19, '0x2': 20 } });
await expect(sentTx.wait({ timeout: 1, interval: 0.4 })).rejects.toThrowError(/timeout/i);
});

it('does not wait for notes sync', async () => {
rpc.getSyncStatus.mockResolvedValue({ blocks: 19, notes: { '0x1': 19, '0x2': 19 } });
const actual = await sentTx.wait({ timeout: 1, interval: 0.4, waitForNotesSync: false });
expect(actual).toEqual(txReceipt);
});
});
});
19 changes: 18 additions & 1 deletion yarn-project/aztec.js/src/contract/sent_tx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,25 @@ import { FieldsOf } from '@aztec/circuits.js';
import { retryUntil } from '@aztec/foundation/retry';
import { AztecRPC, TxHash, TxReceipt, TxStatus } from '@aztec/types';

import every from 'lodash.every';

/** Options related to waiting for a tx. */
export type WaitOpts = {
/** The maximum time (in seconds) to wait for the transaction to be mined. */
timeout?: number;
/** The time interval (in seconds) between retries to fetch the transaction receipt. */
interval?: number;
/**
* Whether to wait for the RPC server to sync all notes up to the block in which this tx was mined.
* If false, then any queries that depend on state set by this transaction may return stale data. Defaults to true.
**/
waitForNotesSync?: boolean;
};

const DefaultWaitOpts: WaitOpts = {
timeout: 0,
interval: 1,
waitForNotesSync: true,
};

/**
Expand Down Expand Up @@ -74,7 +82,16 @@ export class SentTx {
return await retryUntil(
async () => {
const txReceipt = await this.arc.getTxReceipt(txHash);
return txReceipt.status != TxStatus.PENDING ? txReceipt : undefined;
// If receipt is not yet available, try again
if (txReceipt.status === TxStatus.PENDING) return undefined;
// If we don't care about waiting for notes to be synced, return the receipt
const waitForNotesSync = opts?.waitForNotesSync ?? DefaultWaitOpts.waitForNotesSync;
if (!waitForNotesSync) return txReceipt;
// Check if all sync blocks on the rpc server are greater or equal than the block in which the tx was mined
const { blocks, notes } = await this.arc.getSyncStatus();
const targetBlock = txReceipt.blockNumber!;
const areNotesSynced = blocks >= targetBlock && every(notes, block => block >= targetBlock);
return areNotesSynced ? txReceipt : undefined;
},
'isMined',
opts?.timeout ?? DefaultWaitOpts.timeout,
Expand Down
14 changes: 14 additions & 0 deletions yarn-project/types/src/interfaces/aztec_rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ export type NodeInfo = {
chainId: number;
};

/** Provides up to which block has been synced by different components. */
export type SyncStatus = {
/** Up to which block has been synched for blocks and txs. */
blocks: number;
/** Up to which block has been synched for notes, indexed by each public key being monitored. */
notes: Record<string, number>;
};

/**
* Represents an Aztec RPC implementation.
* Provides functionality for all the operations needed to interact with the Aztec network,
Expand Down Expand Up @@ -226,4 +234,10 @@ export interface AztecRPC {
* @returns True if the account is fully synched, false otherwise
*/
isAccountSynchronised(account: AztecAddress): Promise<boolean>;

/**
* Returns the latest block that has been synchronised by the synchronizer and each account.
* @returns The latest block synchronised for blocks, and the latest block synched for notes for each public key being tracked.
*/
getSyncStatus(): Promise<SyncStatus>;
}
2 changes: 2 additions & 0 deletions yarn-project/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -218,12 +218,14 @@ __metadata:
"@jest/globals": ^29.5.0
"@rushstack/eslint-patch": ^1.1.4
"@types/jest": ^29.5.0
"@types/lodash.every": ^4.6.7
"@types/lodash.partition": ^4.6.0
"@types/node": ^18.7.23
buffer: ^6.0.3
crypto-browserify: ^3.12.0
jest: ^29.5.0
jest-mock-extended: ^3.0.3
lodash.every: ^4.6.0
lodash.partition: ^4.6.0
process: ^0.11.10
resolve-typescript-plugin: ^2.0.1
Expand Down

0 comments on commit 5cd1b12

Please sign in to comment.