Skip to content

Commit

Permalink
Sync block clients (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
grod220 authored Sep 8, 2023
0 parents commit 675b574
Show file tree
Hide file tree
Showing 7 changed files with 150 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
root: true,
extends: ['custom'],
parserOptions: {
project: true,
tsconfigRootDir: __dirname,
},
};
1 change: 1 addition & 0 deletions index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './src/block-processor';
19 changes: 19 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "penumbra-query",
"version": "1.0.0",
"license": "MIT",
"scripts": {
"lint": "eslint \"**/*.ts*\""
},
"dependencies": {
"@buf/penumbra-zone_penumbra.bufbuild_es": "1.3.1-20230907002021-abc48eee55e7.1",
"@buf/penumbra-zone_penumbra.connectrpc_es": "0.13.2-20230907002021-abc48eee55e7.1",
"@connectrpc/connect": "^0.13.2",
"@connectrpc/connect-web": "^0.13.2",
"eslint": "^8.48.0",
"eslint-config-custom": "workspace:*",
"penumbra-types": "workspace:*",
"tsconfig": "workspace:*",
"typescript": "^5.2.2"
}
}
45 changes: 45 additions & 0 deletions src/block-processor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { ObliviousQuerier } from './oblivious';
import { SpecificQuerier } from './specific';
import { IndexedDbInterface, SpendableNoteRecord, ViewServerInterface } from 'penumbra-types';

interface QueryClientProps {
oblQuerier: ObliviousQuerier;
specQuerier: SpecificQuerier;
indexedDb: IndexedDbInterface;
viewServer: ViewServerInterface;
}

export class BlockProcessor {
private readonly oblQuerier: ObliviousQuerier;
private readonly specQuerier: SpecificQuerier;
private readonly indexedDb: IndexedDbInterface;
private readonly viewServer: ViewServerInterface;

constructor({ indexedDb, viewServer, oblQuerier, specQuerier }: QueryClientProps) {
this.indexedDb = indexedDb;
this.viewServer = viewServer;
this.oblQuerier = oblQuerier;
this.specQuerier = specQuerier;
}

async syncBlocks() {
const lastBlock = await this.indexedDb.getLastBlockSynced();
const startHeight = lastBlock ? lastBlock + 1n : 0n;

for await (const res of this.oblQuerier.compactBlockRange(startHeight, true)) {
const scanResult = await this.viewServer.scanBlock(res.compactBlock!);
await this.handleNewNotes(scanResult.new_notes);
await this.indexedDb.saveLastBlockSynced(res.compactBlock!.height);
}
}

async handleNewNotes(notes: SpendableNoteRecord[]) {
for (const n of notes) {
await this.indexedDb.saveSpendableNote(n);
const metadata = await this.specQuerier.denomMetadata(n.note.value.assetId);
if (metadata) {
await this.indexedDb.saveAssetsMetadata(metadata);
}
}
}
}
40 changes: 40 additions & 0 deletions src/oblivious.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { ObliviousQueryService } from '@buf/penumbra-zone_penumbra.connectrpc_es/penumbra/client/v1alpha1/client_connect';
import { createPromiseClient, PromiseClient } from '@connectrpc/connect';
import { createGrpcWebTransport } from '@connectrpc/connect-web';
import {
ChainParametersRequest,
CompactBlockRangeRequest,
} from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/client/v1alpha1/client_pb';

interface ObliviousQuerierProps {
grpcEndpoint: string;
}

export class ObliviousQuerier {
private readonly client: PromiseClient<typeof ObliviousQueryService>;

constructor({ grpcEndpoint }: ObliviousQuerierProps) {
this.client = this.createClient(grpcEndpoint);
}

compactBlockRange(
startHeight: bigint,
keepAlive: boolean, // Will continuously receive blocks as long as service worker is running
) {
const req = new CompactBlockRangeRequest({ keepAlive, startHeight });
return this.client.compactBlockRange(req);
}

async chainParameters() {
const req = new ChainParametersRequest({});
const res = await this.client.chainParameters(req);
return res.chainParameters!;
}

private createClient(grpcEndpoint: string): PromiseClient<typeof ObliviousQueryService> {
const transport = createGrpcWebTransport({
baseUrl: grpcEndpoint,
});
return createPromiseClient(ObliviousQueryService, transport);
}
}
32 changes: 32 additions & 0 deletions src/specific.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { SpecificQueryService } from '@buf/penumbra-zone_penumbra.connectrpc_es/penumbra/client/v1alpha1/client_connect';
import { createPromiseClient, PromiseClient } from '@connectrpc/connect';
import { createGrpcWebTransport } from '@connectrpc/connect-web';
import { DenomMetadataByIdRequest } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/client/v1alpha1/client_pb';
import { AssetId } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/crypto/v1alpha1/crypto_pb';

interface SpecificQuerierProps {
grpcEndpoint: string;
}

export class SpecificQuerier {
private readonly client: PromiseClient<typeof SpecificQueryService>;

constructor({ grpcEndpoint }: SpecificQuerierProps) {
this.client = this.createClient(grpcEndpoint);
}

async denomMetadata(assetId: AssetId) {
const request = new DenomMetadataByIdRequest({
assetId: { inner: assetId.inner },
});
const res = await this.client.denomMetadataById(request);
return res.denomMetadata;
}

private createClient(grpcEndpoint: string): PromiseClient<typeof SpecificQueryService> {
const transport = createGrpcWebTransport({
baseUrl: grpcEndpoint,
});
return createPromiseClient(SpecificQueryService, transport);
}
}
5 changes: 5 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "tsconfig/base.json",
"include": ["."],
"exclude": ["node_modules"]
}

0 comments on commit 675b574

Please sign in to comment.