From d0b527c446930ce785de15d3a39b77e041e0bc83 Mon Sep 17 00:00:00 2001 From: valentine Date: Mon, 11 Mar 2024 23:38:27 +0200 Subject: [PATCH] change to dynamically computed transactionviews --- .../broadcast-transaction.test.ts | 9 +++- .../broadcast-transaction.ts | 6 ++- .../transaction-info-by-hash.ts | 5 +- .../transaction-info.test.ts | 54 +++++++++---------- .../view-protocol-server/transaction-info.ts | 6 ++- packages/storage/src/indexed-db/index.ts | 30 ++++++----- .../src/indexed-db/indexed-db.test-data.ts | 5 +- .../storage/src/indexed-db/indexed-db.test.ts | 20 +++---- packages/types/src/indexed-db.ts | 15 ++---- 9 files changed, 80 insertions(+), 70 deletions(-) diff --git a/packages/router/src/grpc/view-protocol-server/broadcast-transaction.test.ts b/packages/router/src/grpc/view-protocol-server/broadcast-transaction.test.ts index a11b8afee3..466e12d727 100644 --- a/packages/router/src/grpc/view-protocol-server/broadcast-transaction.test.ts +++ b/packages/router/src/grpc/view-protocol-server/broadcast-transaction.test.ts @@ -84,7 +84,14 @@ describe('BroadcastTransaction request handler', () => { mockTendermint.broadcastTx?.mockResolvedValue(transactionIdData); txSubNext.mockResolvedValueOnce({ - value: { value: {id: transactionIdData, height: detectionHeight, transaction: transactionData}, table: 'TRANSACTIONS' }, + value: { + value: new TransactionInfo({ + id: transactionIdData, + height: detectionHeight, + transaction: transactionData, + }).toJson(), + table: 'TRANSACTIONS', + }, }); broadcastTransactionRequest.awaitDetection = true; diff --git a/packages/router/src/grpc/view-protocol-server/broadcast-transaction.ts b/packages/router/src/grpc/view-protocol-server/broadcast-transaction.ts index dfa391d38e..c42effb580 100644 --- a/packages/router/src/grpc/view-protocol-server/broadcast-transaction.ts +++ b/packages/router/src/grpc/view-protocol-server/broadcast-transaction.ts @@ -7,6 +7,7 @@ import { ConnectError, Code } from '@connectrpc/connect'; import { sha256Hash } from '@penumbra-zone/crypto-web'; import { uint8ArrayToHex } from '@penumbra-zone/types'; +import { TransactionInfo } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/view/v1/view_pb'; export const broadcastTransaction: Impl['broadcastTransaction'] = async function* (req, ctx) { const services = ctx.values.get(servicesCtx); @@ -39,8 +40,9 @@ export const broadcastTransaction: Impl['broadcastTransaction'] = async function // Wait until DB records a new transaction with this id for await (const { value } of subscription) { - const detectionId = TransactionId.fromJson(value.id); - const detectionHeight = value.height; + const transactionRecord = TransactionInfo.fromJson(value); + const detectionId = transactionRecord.id; + const detectionHeight = transactionRecord.height; if (id.equals(detectionId)) { yield { status: { diff --git a/packages/router/src/grpc/view-protocol-server/transaction-info-by-hash.ts b/packages/router/src/grpc/view-protocol-server/transaction-info-by-hash.ts index c41cb9cd9a..5e9337393b 100644 --- a/packages/router/src/grpc/view-protocol-server/transaction-info-by-hash.ts +++ b/packages/router/src/grpc/view-protocol-server/transaction-info-by-hash.ts @@ -14,7 +14,10 @@ export const transactionInfoByHash: Impl['transactionInfoByHash'] = async (req, if (!req.id) throw new ConnectError('Missing transaction ID in request', Code.InvalidArgument); const { transaction, height } = - (await indexedDb.getTransaction(req.id)) || (await querier.tendermint.getTransaction(req.id)); + (await indexedDb.getTransaction(req.id)) ?? (await querier.tendermint.getTransaction(req.id)); + + if (!transaction) throw new ConnectError('Transaction not available', Code.NotFound); + const { txp: perspective, txv: view } = await generateTransactionInfo( fullViewingKey, transaction, diff --git a/packages/router/src/grpc/view-protocol-server/transaction-info.test.ts b/packages/router/src/grpc/view-protocol-server/transaction-info.test.ts index eacad0846d..8c86ffc74f 100644 --- a/packages/router/src/grpc/view-protocol-server/transaction-info.test.ts +++ b/packages/router/src/grpc/view-protocol-server/transaction-info.test.ts @@ -6,15 +6,13 @@ import { createContextValues, createHandlerContext, HandlerContext } from '@conn import { beforeEach, describe, expect, test, vi } from 'vitest'; import { + TransactionInfo, TransactionInfoRequest, TransactionInfoResponse, } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/view/v1/view_pb'; import { IndexedDbMock, MockServices, ViewServerMock } from '../test-utils'; import { Services } from '@penumbra-zone/services'; import { transactionInfo } from './transaction-info'; -import { TransactionRecord } from '@penumbra-zone/types'; -import { Transaction } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/transaction/v1/transaction_pb'; -import { TransactionId } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/txhash/v1/txhash_pb'; const mockTransactionInfo = vi.hoisted(() => vi.fn()); vi.mock('@penumbra-zone/wasm', () => ({ @@ -117,33 +115,33 @@ describe('TransactionInfo request handler', () => { }); }); -const testData: TransactionRecord[] = [ - { - height: 222n, - id: TransactionId.fromJson({ +const testData: TransactionInfo[] = [ + TransactionInfo.fromJson({ + height: '222', + id: { inner: '1MI8IG5D3MQj3s1j0MXTwCQtAaVbwTlPkW8Qdz1EVIo=', - }), - transaction: Transaction.fromJson({}), - }, - { - height: 1000n, - id: TransactionId.fromJson({ + }, + transaction: {}, + }), + TransactionInfo.fromJson({ + height: '1000', + id: { inner: '2MI8IG5D3MQj3s1j0MXTwCQtAaVbwTlPkW8Qdz1EVIo=', - }), - transaction: Transaction.fromJson({}), - }, - { - height: 2525n, - id: TransactionId.fromJson({ + }, + transaction: {}, + }), + TransactionInfo.fromJson({ + height: '2525', + id: { inner: '3MI8IG5D3MQj3s1j0MXTwCQtAaVbwTlPkW8Qdz1EVIo=', - }), - transaction: Transaction.fromJson({}), - }, - { - height: 12255n, - id: TransactionId.fromJson({ + }, + transaction: {}, + }), + TransactionInfo.fromJson({ + height: '12255', + id: { inner: '4MI8IG5D3MQj3s1j0MXTwCQtAaVbwTlPkW8Qdz1EVIo=', - }), - transaction: Transaction.fromJson({}), - }, + }, + transaction: {}, + }), ]; diff --git a/packages/router/src/grpc/view-protocol-server/transaction-info.ts b/packages/router/src/grpc/view-protocol-server/transaction-info.ts index a91d71e670..53a96ffcbf 100644 --- a/packages/router/src/grpc/view-protocol-server/transaction-info.ts +++ b/packages/router/src/grpc/view-protocol-server/transaction-info.ts @@ -12,7 +12,11 @@ export const transactionInfo: Impl['transactionInfo'] = async function* (req, ct for await (const txRecord of indexedDb.iterateTransactions()) { // filter transactions between startHeight and endHeight, inclusive - if (txRecord.height < req.startHeight || (req.endHeight && txRecord.height > req.endHeight)) + if ( + !txRecord.transaction || + txRecord.height < req.startHeight || + (req.endHeight && txRecord.height > req.endHeight) + ) continue; const { txp: perspective, txv: view } = await generateTransactionInfo( diff --git a/packages/storage/src/indexed-db/index.ts b/packages/storage/src/indexed-db/index.ts index 54809d780e..b52e77b66c 100644 --- a/packages/storage/src/indexed-db/index.ts +++ b/packages/storage/src/indexed-db/index.ts @@ -10,7 +10,6 @@ import { PenumbraDb, ScanBlockResult, StateCommitmentTree, - TransactionRecord, uint8ArrayToBase64, uint8ArrayToHex, } from '@penumbra-zone/types'; @@ -25,6 +24,7 @@ import { NotesForVotingResponse, SpendableNoteRecord, SwapRecord, + TransactionInfo, } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/view/v1/view_pb'; import { AssetId, @@ -214,7 +214,7 @@ export class IndexedDb implements IndexedDbInterface { start: async cont => { let cursor = await this.db.transaction('TRANSACTIONS').store.openCursor(); while (cursor) { - cont.enqueue(cursor.value); + cont.enqueue(TransactionInfo.fromJson(cursor.value)); cursor = await cursor.continue(); } cont.close(); @@ -222,23 +222,27 @@ export class IndexedDb implements IndexedDbInterface { }); } - async saveTransaction(id: TransactionId, height: bigint, tx: Transaction): Promise { - const txRecord: TransactionRecord = { - id: id.toJson() as Jsonified, - height: height, - tx: tx.toJson() as Jsonified, - }; + async saveTransaction( + id: TransactionId, + height: bigint, + transaction: Transaction, + ): Promise { + const tx = new TransactionInfo({ + id, + height, + transaction, + }); await this.u.update({ table: 'TRANSACTIONS', - value: txRecord, + value: tx.toJson() as Jsonified, }); } - async getTransaction(txId: TransactionId): Promise { + async getTransaction(txId: TransactionId): Promise { const key = uint8ArrayToBase64(txId.inner); - const transactionRecord = await this.db.get('TRANSACTIONS', key); - if (!transactionRecord) return undefined; - return transactionRecord; + const jsonRecord = await this.db.get('TRANSACTIONS', key); + if (!jsonRecord) return undefined; + return TransactionInfo.fromJson(jsonRecord); } async getFmdParams(): Promise { diff --git a/packages/storage/src/indexed-db/indexed-db.test-data.ts b/packages/storage/src/indexed-db/indexed-db.test-data.ts index cb5f82c92d..a1d91844fd 100644 --- a/packages/storage/src/indexed-db/indexed-db.test-data.ts +++ b/packages/storage/src/indexed-db/indexed-db.test-data.ts @@ -3,7 +3,6 @@ import { Metadata } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/ import { SpendableNoteRecord, SwapRecord, - TransactionInfo, } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/view/v1/view_pb'; import { Position, @@ -716,6 +715,4 @@ export const epoch2 = new Epoch({ export const epoch3 = new Epoch({ index: 2n, startHeight: 200n, -}); - -export class transactionInfo {} +}); \ No newline at end of file diff --git a/packages/storage/src/indexed-db/indexed-db.test.ts b/packages/storage/src/indexed-db/indexed-db.test.ts index 528d845611..e6f5b863f6 100644 --- a/packages/storage/src/indexed-db/indexed-db.test.ts +++ b/packages/storage/src/indexed-db/indexed-db.test.ts @@ -2,8 +2,9 @@ import { FmdParameters } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/ import { SpendableNoteRecord, SwapRecord, + TransactionInfo, } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/view/v1/view_pb'; -import { IdbUpdate, PenumbraDb, TransactionRecord } from '@penumbra-zone/types'; +import { IdbUpdate, PenumbraDb } from '@penumbra-zone/types'; import { beforeEach, describe, expect, it } from 'vitest'; import { IndexedDb } from '.'; import { @@ -41,7 +42,6 @@ import { } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/component/dex/v1/dex_pb'; import { Metadata } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/asset/v1/asset_pb'; import { localAssets } from '@penumbra-zone/constants'; -import { Transaction } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/transaction/v1/transaction_pb'; describe('IndexedDb', () => { // uses different wallet ids so no collisions take place @@ -137,9 +137,9 @@ describe('IndexedDb', () => { expect(assets.length).toBe(1 + localAssets.length); await db.saveTransaction(transactionId, 1000n, transaction); - const txs: TransactionRecord[] = []; + const txs: TransactionInfo[] = []; for await (const tx of db.iterateTransactions()) { - txs.push(tx); + txs.push(tx as TransactionInfo); } expect(txs.length).toBe(1); @@ -179,9 +179,9 @@ describe('IndexedDb', () => { } expect(assetsAfterClear.length).toBe(0); - const txsAfterClean: TransactionRecord[] = []; + const txsAfterClean: TransactionInfo[] = []; for await (const tx of db.iterateTransactions()) { - txsAfterClean.push(tx); + txsAfterClean.push(tx as TransactionInfo); } expect(txsAfterClean.length).toBe(0); expect(await db.getFullSyncHeight()).toBeUndefined(); @@ -314,19 +314,19 @@ describe('IndexedDb', () => { await db.saveTransaction(transactionId, 1000n, transaction); const savedTransaction = await db.getTransaction(transactionId); - expect(transaction.equals(Transaction.fromJson(savedTransaction?.tx!))).toBeTruthy(); + expect(transaction.equals(savedTransaction?.transaction)).toBeTruthy(); }); it('should be able to set/get all', async () => { const db = await IndexedDb.initialize({ ...generateInitialProps() }); await db.saveTransaction(transactionId, 1000n, transaction); - const savedTransactions: TransactionRecord[] = []; + const savedTransactions: TransactionInfo[] = []; for await (const tx of db.iterateTransactions()) { - savedTransactions.push(tx); + savedTransactions.push(tx as TransactionInfo); } expect(savedTransactions.length === 1).toBeTruthy(); - expect(transaction.equals(Transaction.fromJson(savedTransactions[0]?.tx!))).toBeTruthy(); + expect(transaction.equals(savedTransactions[0]?.transaction)).toBeTruthy(); }); }); diff --git a/packages/types/src/indexed-db.ts b/packages/types/src/indexed-db.ts index d9dde2bdc1..57c6c57b5b 100644 --- a/packages/types/src/indexed-db.ts +++ b/packages/types/src/indexed-db.ts @@ -12,6 +12,7 @@ import { NotesForVotingResponse, SpendableNoteRecord, SwapRecord, + TransactionInfo, } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/view/v1/view_pb'; import { AssetId, @@ -60,8 +61,8 @@ export interface IndexedDbInterface { saveSpendableNote(note: SpendableNoteRecord): Promise; iterateSpendableNotes(): AsyncGenerator; saveTransaction(id: TransactionId, height: bigint, tx: Transaction): Promise; - getTransaction(txId: TransactionId): Promise; - iterateTransactions(): AsyncGenerator; + getTransaction(txId: TransactionId): Promise; + iterateTransactions(): AsyncGenerator; getAssetsMetadata(assetId: AssetId): Promise; saveAssetsMetadata(metadata: Metadata): Promise; iterateAssetsMetadata(): AsyncGenerator; @@ -123,8 +124,8 @@ export interface PenumbraDb extends DBSchema { value: Jsonified; }; TRANSACTIONS: { - key: string; // base64 TransactionRecord['id']['inner']; - value: Jsonified; + key: string; // base64 TransactionInfo['id']['inner']; + value: Jsonified; // TransactionInfo with undefined view and perspective }; // ======= Json serialized values ======= // Allows wasm crate to directly deserialize @@ -178,12 +179,6 @@ export interface PositionRecord { position: Jsonified; // Position } -export interface TransactionRecord { - id: TransactionId; - height: bigint; - transaction: Transaction; -} - export type Tables = Record>; export type PenumbraStoreNames = StoreNames;