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

feat: More reliable getTxReceipt api. #1793

Merged
merged 6 commits into from
Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions yarn-project/archiver/src/archiver/archiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ import {
L2BlockL2Logs,
L2BlockSource,
L2LogsSource,
L2Tx,
LogType,
TxHash,
} from '@aztec/types';

import omit from 'lodash.omit';
Expand Down Expand Up @@ -285,6 +287,10 @@ export class Archiver implements L2BlockSource, L2LogsSource, ContractDataSource
return blocks.length === 0 ? undefined : blocks[0];
}

public getL2Tx(txHash: TxHash): Promise<L2Tx | undefined> {
return this.store.getL2Tx(txHash);
}

/**
* Lookup the L2 contract data for this contract.
* Contains the contract's public function bytecode.
Expand Down
25 changes: 25 additions & 0 deletions yarn-project/archiver/src/archiver/archiver_store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import {
L1ToL2Message,
L2Block,
L2BlockL2Logs,
L2Tx,
LogType,
TxHash,
} from '@aztec/types';

import { L1ToL2MessageStore, PendingL1ToL2MessageStore } from './l1_to_l2_message_store.js';
Expand All @@ -32,6 +34,13 @@ export interface ArchiverDataStore {
*/
getL2Blocks(from: number, limit: number): Promise<L2Block[]>;

/**
* Gets an l2 tx.
* @param txHash - The txHash of the l2 tx.
* @returns The requested L2 tx.
*/
getL2Tx(txHash: TxHash): Promise<L2Tx | undefined>;

/**
* Append new logs to the store's list.
* @param data - The logs to be added to the store.
Expand Down Expand Up @@ -145,6 +154,11 @@ export class MemoryArchiverStore implements ArchiverDataStore {
*/
private l2Blocks: L2Block[] = [];

/**
* An array containing all the L2 Txs in the L2 blocks that have been fetched so far.
*/
private l2Txs: L2Tx[] = [];

/**
* An array containing all the encrypted logs that have been fetched so far.
* Note: Index in the "outer" array equals to (corresponding L2 block's number - INITIAL_L2_BLOCK_NUM).
Expand Down Expand Up @@ -187,6 +201,7 @@ export class MemoryArchiverStore implements ArchiverDataStore {
*/
public addL2Blocks(blocks: L2Block[]): Promise<boolean> {
this.l2Blocks.push(...blocks);
this.l2Txs.push(...blocks.flatMap(b => b.getTxs()));
return Promise.resolve(true);
}

Expand Down Expand Up @@ -280,6 +295,16 @@ export class MemoryArchiverStore implements ArchiverDataStore {
return Promise.resolve(this.l2Blocks.slice(startIndex, endIndex));
}

/**
* Gets an l2 tx.
* @param txHash - The txHash of the l2 tx.
* @returns The requested L2 tx.
*/
public getL2Tx(txHash: TxHash): Promise<L2Tx | undefined> {
const l2Tx = this.l2Txs.find(tx => tx.txHash.equals(txHash));
return Promise.resolve(l2Tx);
}

/**
* Gets up to `limit` amount of pending L1 to L2 messages, sorted by fee
* @param limit - The number of messages to return (by default NUMBER_OF_L1_L2_MESSAGES_PER_ROLLUP).
Expand Down
5 changes: 5 additions & 0 deletions yarn-project/aztec-node/src/aztec-node/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
L2BlockL2Logs,
L2BlockSource,
L2LogsSource,
L2Tx,
LogType,
MerkleTreeId,
SiblingPath,
Expand Down Expand Up @@ -210,6 +211,10 @@ export class AztecNodeService implements AztecNode {
await this.p2pClient!.sendTx(tx);
}

public getTx(txHash: TxHash): Promise<L2Tx | undefined> {
return this.blockSource.getL2Tx(txHash);
}

/**
* Method to stop the aztec node.
*/
Expand Down
75 changes: 24 additions & 51 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 @@ -44,7 +44,7 @@ import {

import { RpcServerConfig } from '../config/index.js';
import { ContractDataOracle } from '../contract_data_oracle/index.js';
import { Database, TxDao } from '../database/index.js';
import { Database } from '../database/index.js';
import { KernelOracle } from '../kernel_oracle/index.js';
import { KernelProver } from '../kernel_prover/kernel_prover.js';
import { getAcirSimulator } from '../simulator/index.js';
Expand Down Expand Up @@ -185,16 +185,8 @@ export class AztecRPCServer implements AztecRPC {
const newContract = deployedContractAddress ? await this.db.getContract(deployedContractAddress) : undefined;

const tx = await this.#simulateAndProve(txRequest, newContract);

await this.db.addTx(
TxDao.from({
txHash: await tx.getTxHash(),
origin: txRequest.origin,
contractAddress: deployedContractAddress,
}),
);

this.log.info(`Executed local simulation for ${await tx.getTxHash()}`);

return tx;
}

Expand All @@ -214,40 +206,35 @@ export class AztecRPCServer implements AztecRPC {
}

public async getTxReceipt(txHash: TxHash): Promise<TxReceipt> {
const localTx = await this.#getTxByHash(txHash);
const partialReceipt = new TxReceipt(
txHash,
TxStatus.PENDING,
'',
localTx?.blockHash,
localTx?.blockNumber,
localTx?.origin,
localTx?.contractAddress,
);

if (localTx?.blockHash) {
partialReceipt.status = TxStatus.MINED;
return partialReceipt;
const settledTx = await this.node.getTx(txHash);
if (settledTx) {
const deployedContractAddress = settledTx.newContractData.find(
c => !c.contractAddress.equals(AztecAddress.ZERO),
)?.contractAddress;

return new TxReceipt(
txHash,
TxStatus.MINED,
'',
settledTx.blockHash,
settledTx.blockNumber,
deployedContractAddress,
);
}

const pendingTx = await this.node.getPendingTxByHash(txHash);
if (pendingTx) {
return partialReceipt;
return new TxReceipt(txHash, TxStatus.PENDING, '');
}

// if the transaction mined it will be removed from the pending pool and there is a race condition here as the synchroniser will not have the tx as mined yet, so it will appear dropped
// until the synchroniser picks this up

const isSynchronised = await this.synchroniser.isGlobalStateSynchronised();
if (!isSynchronised) {
// there is a pending L2 block, which means the transaction will not be in the tx pool but may be awaiting mine on L1
return partialReceipt;
}
// https://github.com/AztecProtocol/aztec-packages/issues/1547
// Pending txs are managed by p2pClient, and settled txs are managed by archiver.
LeilaWang marked this conversation as resolved.
Show resolved Hide resolved
// Becasue they operate independently,
// there's a slight chance that a tx that has been removed from the pending txs pool by p2pClient
// hasn't been added as a settled tx by archiver.
// In that situation, the tx will be incorrectly marked as DROPPED.

// TODO we should refactor this once the node can store transactions. At that point we should query the node and not deal with block heights.
partialReceipt.status = TxStatus.DROPPED;
partialReceipt.error = 'Tx dropped by P2P node.';
return partialReceipt;
return new TxReceipt(txHash, TxStatus.DROPPED, 'Tx dropped by P2P node.');
}

async getBlockNumber(): Promise<number> {
Expand Down Expand Up @@ -298,20 +285,6 @@ export class AztecRPCServer implements AztecRPC {
};
}

/**
* Retrieve a transaction by its hash from the database.
*
* @param txHash - The hash of the transaction to be fetched.
* @returns A TxDao instance representing the retrieved transaction.
*/
async #getTxByHash(txHash: TxHash): Promise<TxDao> {
const tx = await this.db.getTx(txHash);
if (!tx) {
throw new Error(`Transaction ${txHash} not found in RPC database`);
}
return tx;
}

/**
* Retrieves the simulation parameters required to run an ACIR simulation.
* This includes the contract address, function ABI, portal contract address, and historic tree roots.
Expand Down
33 changes: 1 addition & 32 deletions yarn-project/aztec-rpc/src/database/database.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,15 @@
import { CompleteAddress, HistoricBlockData } from '@aztec/circuits.js';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { Fr } from '@aztec/foundation/fields';
import { ContractDatabase, MerkleTreeId, PublicKey, TxHash } from '@aztec/types';
import { ContractDatabase, MerkleTreeId, PublicKey } from '@aztec/types';

import { NoteSpendingInfoDao } from './note_spending_info_dao.js';
import { TxDao } from './tx_dao.js';

/**
* A database interface that provides methods for retrieving, adding, and removing transactional data related to Aztec
* addresses, storage slots, and nullifiers.
*/
export interface Database extends ContractDatabase {
/**
* Retrieve a transaction from the MemoryDB using its transaction hash.
* The function searches for the transaction with the given hash in the txTable and returns it as a Promise.
* Returns 'undefined' if the transaction is not found in the database.
*
* @param txHash - The TxHash of the transaction to be retrieved.
* @returns A Promise that resolves to the found TxDao instance, or undefined if not found.
*/
getTx(txHash: TxHash): Promise<TxDao | undefined>;

/**
* Adds a TxDao instance to the transaction table.
* If a transaction with the same hash already exists in the table, it replaces the existing one.
* Otherwise, it pushes the new transaction to the table.
*
* @param tx - The TxDao instance representing the transaction to be added.
* @returns A Promise that resolves when the transaction is successfully added/updated in the table.
*/
addTx(tx: TxDao): Promise<void>;

/**
* Add an array of transaction data objects.
* If a transaction with the same hash already exists in the database, it will be updated
* with the new transaction data. Otherwise, the new transaction will be added to the database.
*
* @param txs - An array of TxDao instances representing the transactions to be added to the database.
* @returns A Promise that resolves when all the transactions have been added or updated.
*/
addTxs(txs: TxDao[]): Promise<void>;

/**
* Get auxiliary transaction data based on contract address and storage slot.
* It searches for matching NoteSpendingInfoDao objects in the MemoryDB's noteSpendingInfoTable
Expand Down
1 change: 0 additions & 1 deletion yarn-project/aztec-rpc/src/database/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export * from './database.js';
export * from './memory_db.js';
export * from './note_spending_info_dao.js';
export * from './tx_dao.js';
22 changes: 1 addition & 21 deletions yarn-project/aztec-rpc/src/database/memory_db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ import { CompleteAddress, HistoricBlockData } from '@aztec/circuits.js';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { Fr } from '@aztec/foundation/fields';
import { createDebugLogger } from '@aztec/foundation/log';
import { MerkleTreeId, PublicKey, TxHash } from '@aztec/types';
import { MerkleTreeId, PublicKey } from '@aztec/types';

import { MemoryContractDatabase } from '../contract_database/index.js';
import { Database } from './database.js';
import { NoteSpendingInfoDao } from './note_spending_info_dao.js';
import { TxDao } from './tx_dao.js';

/**
* The MemoryDB class provides an in-memory implementation of a database to manage transactions and auxiliary data.
Expand All @@ -16,7 +15,6 @@ import { TxDao } from './tx_dao.js';
* As an in-memory database, the stored data will not persist beyond the life of the application instance.
*/
export class MemoryDB extends MemoryContractDatabase implements Database {
private txTable: TxDao[] = [];
private noteSpendingInfoTable: NoteSpendingInfoDao[] = [];
private treeRoots: Record<MerkleTreeId, Fr> | undefined;
private globalVariablesHash: Fr | undefined;
Expand All @@ -26,24 +24,6 @@ export class MemoryDB extends MemoryContractDatabase implements Database {
super(createDebugLogger(logSuffix ? 'aztec:memory_db_' + logSuffix : 'aztec:memory_db'));
}

public getTx(txHash: TxHash) {
return Promise.resolve(this.txTable.find(tx => tx.txHash.equals(txHash)));
}

public addTx(tx: TxDao) {
const index = this.txTable.findIndex(t => t.txHash.equals(tx.txHash));
if (index === -1) {
this.txTable.push(tx);
} else {
this.txTable[index] = tx;
}
return Promise.resolve();
}

public async addTxs(txs: TxDao[]) {
await Promise.all(txs.map(tx => this.addTx(tx)));
}

public addNoteSpendingInfo(noteSpendingInfoDao: NoteSpendingInfoDao) {
this.noteSpendingInfoTable.push(noteSpendingInfoDao);
return Promise.resolve();
Expand Down
70 changes: 0 additions & 70 deletions yarn-project/aztec-rpc/src/database/tx_dao.ts

This file was deleted.

Loading
Loading