From cb1a9f32779626cb4992280382c0d800914b41ef Mon Sep 17 00:00:00 2001 From: Jakub Dzikowski Date: Wed, 7 May 2025 18:51:02 +0200 Subject: [PATCH 1/6] Support multiple namespaces for rwset Signed-off-by: Jakub Dzikowski --- package-lock.json | 4 ++-- package.json | 2 +- src/parseBlock.ts | 15 +++++++++++---- src/types.ts | 3 +++ 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7349530..29ee2ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@gala-chain/stream", - "version": "0.0.2", + "version": "0.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@gala-chain/stream", - "version": "0.0.2", + "version": "0.0.3", "license": "Apache-2.0", "dependencies": { "@hyperledger/fabric-gateway": "^1.6.0", diff --git a/package.json b/package.json index 87dcda1..ecb6bd4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@gala-chain/stream", - "version": "0.0.2", + "version": "0.0.3", "description": "Streams blocks from GalaChain or Hyperledger Fabric network as RxJS Observables", "author": "", "private": false, diff --git a/src/parseBlock.ts b/src/parseBlock.ts index ea7ea0b..bb88ffe 100644 --- a/src/parseBlock.ts +++ b/src/parseBlock.ts @@ -17,6 +17,7 @@ import { X509Certificate } from "crypto"; import { sha256 } from "js-sha256"; import { Block, RangeRead, Read, Transaction, TransactionValidationCode, Write } from "./types"; +import { inspect } from "util"; export interface RWSet { namespace: string; @@ -48,6 +49,7 @@ export function parseOrString(s: string): Record | string { /* eslint-disable @typescript-eslint/no-explicit-any */ export function parseBlock(block: any): Block { + console.log("block", inspect(block, { depth: null, colors: true })); const rawTransactions = block.data.data; const firstTransactionHeader = rawTransactions[0].payload.header; @@ -57,6 +59,8 @@ export function parseBlock(block: any): Block { const transactions: Array = []; + console.log("rawTransactions", rawTransactions.length); + for (const rawTransaction of rawTransactions) { const transactionHeader = rawTransaction.payload.header; @@ -72,7 +76,7 @@ export function parseBlock(block: any): Block { const subjectMatch = cert.subject.match(/OU=(\w+).*CN=(\w+)/s); const creatorName = subjectMatch ? (subjectMatch[1] ?? "") + "|" + (subjectMatch[2] ?? "") : "|"; - let chaincodeRWSets: Array; + let rwSets: Array; if (transactionType === "ENDORSER_TRANSACTION") { if (!rawTransaction.payload.data.actions) continue; @@ -90,8 +94,8 @@ export function parseBlock(block: any): Block { version: action.payload.action.proposal_response_payload.extension.chaincode_id.version }; - const allRWSets = action.payload.action.proposal_response_payload.extension.results.ns_rwset; - chaincodeRWSets = allRWSets.filter(({ namespace }) => namespace === chaincode.name) as Array; + rwSets = action.payload.action.proposal_response_payload.extension.results.ns_rwset as Array; + console.log("rwSets", inspect(rwSets, { depth: null, colors: true })); if (transactionType === "CONFIG") { txId = sha256(JSON.stringify(rawTransaction)); @@ -103,15 +107,17 @@ export function parseBlock(block: any): Block { rangeReads: [] }; - const sets = chaincodeRWSets.reduce((acc, set) => { + const sets = rwSets.reduce((acc, set) => { const { reads, writes, range_queries_info } = set.rwset; const parsedReads = reads.map((read) => ({ + namespace: set.namespace, key: read.key.replace("\0", "/") })); acc.reads.push(...parsedReads); const rangeReads = range_queries_info.map((range) => ({ + namespace: set.namespace, startKey: range.start_key.replace("\0", "/"), endKey: range.end_key.replace("\0", "/") })); @@ -119,6 +125,7 @@ export function parseBlock(block: any): Block { const parsedWrites = writes.map((write) => { return { + namespace: set.namespace, isDelete: write.is_delete, key: write.key.replace("\0", "/"), value: parseOrString(write.value.toString()) // chain objects diff --git a/src/types.ts b/src/types.ts index 88fc110..2efc598 100644 --- a/src/types.ts +++ b/src/types.ts @@ -87,15 +87,18 @@ export interface Transaction } export interface Read { + namespace: string; key: string; } export interface RangeRead { + namespace: string; startKey: string; endKey: string; } export interface Write { + namespace: string; isDelete: boolean; key: string; value: ChainObject; From 598d4acc53e6ebc1a94348d798f7dad85a32fb7e Mon Sep 17 00:00:00 2001 From: Jakub Dzikowski Date: Wed, 7 May 2025 18:52:27 +0200 Subject: [PATCH 2/6] Remove redundant logging Signed-off-by: Jakub Dzikowski --- src/parseBlock.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/parseBlock.ts b/src/parseBlock.ts index bb88ffe..9be2f17 100644 --- a/src/parseBlock.ts +++ b/src/parseBlock.ts @@ -17,7 +17,6 @@ import { X509Certificate } from "crypto"; import { sha256 } from "js-sha256"; import { Block, RangeRead, Read, Transaction, TransactionValidationCode, Write } from "./types"; -import { inspect } from "util"; export interface RWSet { namespace: string; @@ -49,7 +48,6 @@ export function parseOrString(s: string): Record | string { /* eslint-disable @typescript-eslint/no-explicit-any */ export function parseBlock(block: any): Block { - console.log("block", inspect(block, { depth: null, colors: true })); const rawTransactions = block.data.data; const firstTransactionHeader = rawTransactions[0].payload.header; @@ -59,8 +57,6 @@ export function parseBlock(block: any): Block { const transactions: Array = []; - console.log("rawTransactions", rawTransactions.length); - for (const rawTransaction of rawTransactions) { const transactionHeader = rawTransaction.payload.header; @@ -95,7 +91,6 @@ export function parseBlock(block: any): Block { }; rwSets = action.payload.action.proposal_response_payload.extension.results.ns_rwset as Array; - console.log("rwSets", inspect(rwSets, { depth: null, colors: true })); if (transactionType === "CONFIG") { txId = sha256(JSON.stringify(rawTransaction)); From 66006a31274355198f745ec575a87299d3892131 Mon Sep 17 00:00:00 2001 From: Jakub Dzikowski Date: Wed, 7 May 2025 18:56:12 +0200 Subject: [PATCH 3/6] Build test chaincode in CI Signed-off-by: Jakub Dzikowski --- .github/workflows/test-on-push.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test-on-push.yml b/.github/workflows/test-on-push.yml index 2893b19..05cbde0 100644 --- a/.github/workflows/test-on-push.yml +++ b/.github/workflows/test-on-push.yml @@ -53,6 +53,7 @@ jobs: nvm install 18.20.5 npm i npm i --prefix ./test-chaincode + npm run build --prefix ./test-chaincode (cd test-chaincode && npm run network:up && cd ..) - name: Run e2e tests run: npm run test:e2e --prefix ./test-chaincode From 77ec743ee3d5a7089537f6eb0d6243eabe228769 Mon Sep 17 00:00:00 2001 From: Jakub Dzikowski Date: Wed, 7 May 2025 20:54:43 +0200 Subject: [PATCH 4/6] Fix disconnecting Signed-off-by: Jakub Dzikowski --- .github/workflows/test-on-push.yml | 4 +--- src/ChainStream.ts | 8 +++++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-on-push.yml b/.github/workflows/test-on-push.yml index 05cbde0..c8ba345 100644 --- a/.github/workflows/test-on-push.yml +++ b/.github/workflows/test-on-push.yml @@ -52,9 +52,7 @@ jobs: [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" nvm install 18.20.5 npm i - npm i --prefix ./test-chaincode - npm run build --prefix ./test-chaincode - (cd test-chaincode && npm run network:up && cd ..) + (cd test-chaincode && npm i && npm run build && npm run network:up) - name: Run e2e tests run: npm run test:e2e --prefix ./test-chaincode - name: Stream blocks from the network diff --git a/src/ChainStream.ts b/src/ChainStream.ts index bf36a5f..218e200 100644 --- a/src/ChainStream.ts +++ b/src/ChainStream.ts @@ -41,7 +41,7 @@ export interface LoggerInterface { export class ChainStream { private readonly chainInfo: BehaviorSubject; private identityPromise: Promise | undefined; - + private isDisconnected = false; constructor( private readonly caService: CAService, private readonly chainService: ChainService, @@ -109,6 +109,10 @@ export class ChainStream { return of([]).pipe( expand(() => { + if (this.isDisconnected) { + return of([]); + } + if (currentBlock >= this.chainInfo.value.height) { return timer(config.intervalMs).pipe( tap(() => this.logger.log(`No new blocks, retrying after ${config.intervalMs} ms...`)), @@ -154,6 +158,8 @@ export class ChainStream { } public disconnect() { + this.logger.log("Disconnected, closing the stream"); + this.isDisconnected = true; this.chainService.disconnect(); } } From a6f1b56fc759340dd719de1c5da1f765a182a3c9 Mon Sep 17 00:00:00 2001 From: Jakub Dzikowski Date: Wed, 7 May 2025 20:55:17 +0200 Subject: [PATCH 5/6] Remove redundant test Signed-off-by: Jakub Dzikowski --- src/stream.spec.ts | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/src/stream.spec.ts b/src/stream.spec.ts index 0e9fa9a..8f068b2 100644 --- a/src/stream.spec.ts +++ b/src/stream.spec.ts @@ -115,32 +115,6 @@ it("should stream transactions", async () => { expect(methodNames).toEqual([methodWanted]); }); -it("should stream transactions", async () => { - // Given - const fetchedTransactions: StreamedTransaction[] = []; - - const methodWanted = "GalaChainToken:TransferToken"; - - // When - connectedStream - .transactions((t) => t.method === methodWanted) - .fromBlock(0) - .subscribe({ - next: (transaction) => { - console.log("Transaction:", transaction.id); - fetchedTransactions.push(transaction); - }, - error: (err) => console.error("Error:", err), - complete: () => console.log("Stream completed") - }); - - await new Promise((resolve) => setTimeout(resolve, 10000)); - - // Then - const methodNames = Array.from(new Set(fetchedTransactions.map((t) => t.method))); - expect(methodNames).toEqual([methodWanted]); -}); - class ChainServiceWithEntropy { private readonly errorRate = 0.4; private readonly maxDelayMs = 500; From 6eb1508834c92c1e3164babc34121df31f811972 Mon Sep 17 00:00:00 2001 From: Jakub Dzikowski Date: Wed, 7 May 2025 21:06:27 +0200 Subject: [PATCH 6/6] Fix jest config Signed-off-by: Jakub Dzikowski --- jest.config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jest.config.ts b/jest.config.ts index 950311b..5abab25 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -14,11 +14,11 @@ */ export default { - displayName: "chaincode-template", + displayName: "stream", testEnvironment: "node", transform: { "^.+\\.[tj]s$": ["ts-jest", { tsconfig: "/tsconfig.spec.json" }] }, moduleFileExtensions: ["ts", "js"], - modulePathIgnorePatterns: ["lib", "e2e"] + modulePathIgnorePatterns: ["lib", "e2e", "test-chaincode"] };