From 80e3f98006d73fda65c6decf0d0748d15da99bb5 Mon Sep 17 00:00:00 2001 From: Rafael Belchior Date: Fri, 15 Nov 2024 18:45:22 +0200 Subject: [PATCH] feat(bungee-hermes): add BUNGEE package BUNGEE is an implementation of a view generator https://dl.acm.org/doi/10.1145/3643689 Authored-by: Eduardo Vasques Co-authored-by: Rafael Belchior Signed-off-by: Rafael Belchior --- .../cactus-plugin-bungee-hermes/README.md | 2 +- .../cactus-plugin-bungee-hermes/package.json | 1 + .../src/main/json/openapi.json | 4 +- .../src/main/json/openapi.tpl.json | 4 +- .../generated/openapi/typescript-axios/api.ts | 3 +- .../main/typescript/plugin-bungee-hermes.ts | 14 ++++--- .../src/main/typescript/public-api.ts | 3 ++ .../main/typescript/strategy/strategy-besu.ts | 6 ++- .../typescript/strategy/strategy-ethereum.ts | 6 ++- .../typescript/strategy/strategy-fabric.ts | 26 +++++++----- .../view-creation/privacy-policies.ts | 42 +++++++++++++++++++ .../main/typescript/view-creation/proof.ts | 4 +- .../main/typescript/view-creation/snapshot.ts | 19 ++++++++- .../main/typescript/view-creation/state.ts | 15 ++++++- .../view-creation/transaction-proof.ts | 3 +- .../typescript/view-creation/transaction.ts | 3 +- .../src/main/typescript/view-creation/view.ts | 7 ++-- .../view-merging/integrated-view.ts | 7 ++-- .../typescript/view-merging/merge-policies.ts | 28 +++++++++++++ yarn.lock | 8 ++++ 20 files changed, 167 insertions(+), 38 deletions(-) diff --git a/packages/cactus-plugin-bungee-hermes/README.md b/packages/cactus-plugin-bungee-hermes/README.md index 2ada98ce32..8c05a4e0cc 100644 --- a/packages/cactus-plugin-bungee-hermes/README.md +++ b/packages/cactus-plugin-bungee-hermes/README.md @@ -226,4 +226,4 @@ We welcome contributions to Hyperledger Cactus in many forms, and there’s alwa Please review [CONTRIBUTING.md](https://github.com/hyperledger/cactus/blob/main/CONTRIBUTING.md "CONTRIBUTING.md") to get started. ## License -This distribution is published under the Apache License Version 2.0 found in the [LICENSE ](https://github.com/hyperledger/cactus/blob/main/LICENSE "LICENSE ")file. \ No newline at end of file +This distribution is published under the Apache License Version 2.0 found in the [LICENSE ](https://github.com/hyperledger/cactus/blob/main/LICENSE "LICENSE ")file. diff --git a/packages/cactus-plugin-bungee-hermes/package.json b/packages/cactus-plugin-bungee-hermes/package.json index 3168a630f5..7349207913 100644 --- a/packages/cactus-plugin-bungee-hermes/package.json +++ b/packages/cactus-plugin-bungee-hermes/package.json @@ -72,6 +72,7 @@ "http-errors-enhanced-cjs": "2.0.1", "key-encoder": "2.0.3", "merkletreejs": "0.3.11", + "safe-stable-stringify": "2.5.0", "typescript-optional": "2.0.1", "uuid": "10.0.0", "web3": "1.6.1", diff --git a/packages/cactus-plugin-bungee-hermes/src/main/json/openapi.json b/packages/cactus-plugin-bungee-hermes/src/main/json/openapi.json index d445c3c768..83833ccfce 100644 --- a/packages/cactus-plugin-bungee-hermes/src/main/json/openapi.json +++ b/packages/cactus-plugin-bungee-hermes/src/main/json/openapi.json @@ -28,8 +28,8 @@ "PrivacyPolicyOpts": { "description": "identifier of the policy used to process a view", "type": "string", - "enum": ["pruneState"], - "x-enum-varnames": ["PruneState"] + "enum": ["pruneState", "singleTransaction"], + "x-enum-varnames": ["PruneState", "SingleTransaction"] }, "MergePolicyOpts": { "description": "identifier of the policy used to merge views (can be none)", diff --git a/packages/cactus-plugin-bungee-hermes/src/main/json/openapi.tpl.json b/packages/cactus-plugin-bungee-hermes/src/main/json/openapi.tpl.json index d445c3c768..83833ccfce 100644 --- a/packages/cactus-plugin-bungee-hermes/src/main/json/openapi.tpl.json +++ b/packages/cactus-plugin-bungee-hermes/src/main/json/openapi.tpl.json @@ -28,8 +28,8 @@ "PrivacyPolicyOpts": { "description": "identifier of the policy used to process a view", "type": "string", - "enum": ["pruneState"], - "x-enum-varnames": ["PruneState"] + "enum": ["pruneState", "singleTransaction"], + "x-enum-varnames": ["PruneState", "SingleTransaction"] }, "MergePolicyOpts": { "description": "identifier of the policy used to merge views (can be none)", diff --git a/packages/cactus-plugin-bungee-hermes/src/main/typescript/generated/openapi/typescript-axios/api.ts b/packages/cactus-plugin-bungee-hermes/src/main/typescript/generated/openapi/typescript-axios/api.ts index 12046f0fda..1b3ad296e4 100644 --- a/packages/cactus-plugin-bungee-hermes/src/main/typescript/generated/openapi/typescript-axios/api.ts +++ b/packages/cactus-plugin-bungee-hermes/src/main/typescript/generated/openapi/typescript-axios/api.ts @@ -185,7 +185,8 @@ export interface MergeViewsResponse { */ export const PrivacyPolicyOpts = { - PruneState: 'pruneState' + PruneState: 'pruneState', + SingleTransaction: 'singleTransaction' } as const; export type PrivacyPolicyOpts = typeof PrivacyPolicyOpts[keyof typeof PrivacyPolicyOpts]; diff --git a/packages/cactus-plugin-bungee-hermes/src/main/typescript/plugin-bungee-hermes.ts b/packages/cactus-plugin-bungee-hermes/src/main/typescript/plugin-bungee-hermes.ts index d8373dce38..15cbb1f925 100644 --- a/packages/cactus-plugin-bungee-hermes/src/main/typescript/plugin-bungee-hermes.ts +++ b/packages/cactus-plugin-bungee-hermes/src/main/typescript/plugin-bungee-hermes.ts @@ -7,6 +7,8 @@ import { LoggerProvider, Secp256k1Keys, } from "@hyperledger/cactus-common"; +import { stringify as safeStableStringify } from "safe-stable-stringify"; + import { v4 as uuidV4 } from "uuid"; import { ICactusPlugin, @@ -231,8 +233,8 @@ export class PluginBungeeHermes implements ICactusPlugin, IPluginWebService { view.setCreator(this.pubKeyBungee); view.setKey(uuidV4()); return { - view: JSON.stringify(view), - signature: this.sign(JSON.stringify(view)), + view: safeStableStringify(view), + signature: this.sign(safeStableStringify(view)), }; } onMergeViews(request: MergeViewsRequest): MergeViewsResponse { @@ -267,7 +269,7 @@ export class PluginBungeeHermes implements ICactusPlugin, IPluginWebService { request.policyArguments ? request.policyArguments : [], ); return { - integratedView: JSON.stringify(integratedView), + integratedView: safeStableStringify(integratedView), signature: integratedView.signature, }; } @@ -287,7 +289,7 @@ export class PluginBungeeHermes implements ICactusPlugin, IPluginWebService { this.logger.info("Generating view for request: ", request); const response = this.generateView(snapshot, ti, tf, request.viewID); return { - view: JSON.stringify(response.view), + view: safeStableStringify(response.view), signature: response.signature, }; } @@ -308,7 +310,7 @@ export class PluginBungeeHermes implements ICactusPlugin, IPluginWebService { const view = new View(this.pubKeyBungee, tI, tF, snapshot, id); snapshot.pruneStates(tI, tF); - const signature = this.sign(JSON.stringify(view)); + const signature = this.sign(safeStableStringify(view)); return { view: view, signature: signature }; } @@ -414,7 +416,7 @@ export class PluginBungeeHermes implements ICactusPlugin, IPluginWebService { integratedView: integratedView, //The paper specs suggest the integratedView should be jointly signed by all participants. //That process is left to be addressed in the future - signature: this.sign(JSON.stringify(integratedView)), + signature: this.sign(safeStableStringify(integratedView)), }; } diff --git a/packages/cactus-plugin-bungee-hermes/src/main/typescript/public-api.ts b/packages/cactus-plugin-bungee-hermes/src/main/typescript/public-api.ts index ff3dd0e66d..1c8403aa70 100644 --- a/packages/cactus-plugin-bungee-hermes/src/main/typescript/public-api.ts +++ b/packages/cactus-plugin-bungee-hermes/src/main/typescript/public-api.ts @@ -1,6 +1,9 @@ import { IPluginFactoryOptions } from "@hyperledger/cactus-core-api"; import { PluginFactoryBungeeHermes } from "./plugin-factory-bungee-hermes"; +export { isMergePolicyValueArray } from "./view-merging/merge-policies"; +export { isPrivacyPolicyValueArray } from "./view-creation/privacy-policies"; + export { PluginBungeeHermes, IPluginBungeeHermesOptions, diff --git a/packages/cactus-plugin-bungee-hermes/src/main/typescript/strategy/strategy-besu.ts b/packages/cactus-plugin-bungee-hermes/src/main/typescript/strategy/strategy-besu.ts index 9178e8f2fe..16e733424f 100644 --- a/packages/cactus-plugin-bungee-hermes/src/main/typescript/strategy/strategy-besu.ts +++ b/packages/cactus-plugin-bungee-hermes/src/main/typescript/strategy/strategy-besu.ts @@ -5,6 +5,8 @@ import { Logger, LoggerProvider, } from "@hyperledger/cactus-common"; +import { stringify as safeStableStringify } from "safe-stable-stringify"; + import { DefaultApi as BesuApi, EthContractInvocationType, @@ -170,7 +172,7 @@ export class StrategyBesu implements ObtainLedgerStrategy { "Transaction: " + log.transactionHash + "\nData: " + - JSON.stringify(log.data) + + safeStableStringify(log.data) + "\n =========== \n", ); const proof = new Proof({ @@ -185,7 +187,7 @@ export class StrategyBesu implements ObtainLedgerStrategy { transaction.setTarget(networkDetails.contractAddress as string); transaction.setPayload(txTx.input ? txTx.input : ""); //FIXME: payload = transaction input ? transactions.push(transaction); - values.push(JSON.stringify(log.data)); + values.push(safeStableStringify(log.data)); blocks.set(transaction.getId(), txBlock); } diff --git a/packages/cactus-plugin-bungee-hermes/src/main/typescript/strategy/strategy-ethereum.ts b/packages/cactus-plugin-bungee-hermes/src/main/typescript/strategy/strategy-ethereum.ts index 28e189c415..51b51609af 100644 --- a/packages/cactus-plugin-bungee-hermes/src/main/typescript/strategy/strategy-ethereum.ts +++ b/packages/cactus-plugin-bungee-hermes/src/main/typescript/strategy/strategy-ethereum.ts @@ -4,6 +4,8 @@ import { LoggerProvider, Checks, } from "@hyperledger/cactus-common"; +import { stringify as safeStableStringify } from "safe-stable-stringify"; + import { Web3SigningCredential, DefaultApi as EthereumApi, @@ -214,7 +216,7 @@ export class StrategyEthereum implements ObtainLedgerStrategy { "Transaction: " + log.transactionHash + "\nData: " + - JSON.stringify(log.data) + + safeStableStringify(log.data) + "\n =========== \n", ); const proof = new Proof({ @@ -229,7 +231,7 @@ export class StrategyEthereum implements ObtainLedgerStrategy { transaction.setTarget(networkDetails.contractAddress as string); transaction.setPayload(txTx.data.input ? txTx.data.input : ""); //FIXME: payload = transaction input ? transactions.push(transaction); - values.push(JSON.stringify(log.data)); + values.push(safeStableStringify(log.data)); blocks.set(transaction.getId(), txBlock.data); } diff --git a/packages/cactus-plugin-bungee-hermes/src/main/typescript/strategy/strategy-fabric.ts b/packages/cactus-plugin-bungee-hermes/src/main/typescript/strategy/strategy-fabric.ts index c7ccbf60cc..ebaae3d177 100644 --- a/packages/cactus-plugin-bungee-hermes/src/main/typescript/strategy/strategy-fabric.ts +++ b/packages/cactus-plugin-bungee-hermes/src/main/typescript/strategy/strategy-fabric.ts @@ -13,6 +13,7 @@ import { Logger, LoggerProvider, } from "@hyperledger/cactus-common"; +import { stringify as safeStableStringify } from "safe-stable-stringify"; import { Transaction } from "../view-creation/transaction"; import { State } from "../view-creation/state"; import { StateProof } from "../view-creation/state-proof"; @@ -101,7 +102,11 @@ export class StrategyFabric implements ObtainLedgerStrategy { mspid: receipt.transactionCreator.mspid, }), ); - assetValues.push(JSON.parse(receipt.rwsetWriteData).Value.toString()); + if (!receipt.rwsetWriteData) { + assetValues.push(""); + } else { + assetValues.push(JSON.parse(receipt.rwsetWriteData).Value.toString()); + } tx.setStateId(assetKey); tx.setTarget(receipt.channelID + ": " + receipt.chainCodeName); @@ -135,10 +140,11 @@ export class StrategyFabric implements ObtainLedgerStrategy { //only adding last block for each state, in the state proof stateProof.addBlock({ blockHash: block.hash, - blockCreator: JSON.stringify({ - mspid: last_receipt.blockMetaData.mspid, - id: last_receipt.blockMetaData.blockCreatorID, - }), + blockCreator: + safeStableStringify({ + mspid: last_receipt.blockMetaData.mspid, + id: last_receipt.blockMetaData.blockCreatorID, + }) ?? "", blockSigners: block.signers, }); @@ -170,7 +176,7 @@ export class StrategyFabric implements ObtainLedgerStrategy { if (!response) { throw new InternalServerError(`${fn} response is falsy`); } - const receiptLockRes = JSON.stringify(response); + const receiptLockRes = safeStableStringify(response); if (!receiptLockRes) { throw new InternalServerError(`${fn} receiptLockRes is falsy`); } @@ -199,7 +205,7 @@ export class StrategyFabric implements ObtainLedgerStrategy { throw new InternalServerError(`${fn} response.data is falsy`); } - const receiptLockRes = JSON.stringify(data); + const receiptLockRes = safeStableStringify(data); if (!receiptLockRes) { throw new InternalServerError(`${fn} receiptLockRes is falsy`); } @@ -275,7 +281,7 @@ export class StrategyFabric implements ObtainLedgerStrategy { ); } - const block = JSON.parse(JSON.stringify(block_data)).decodedBlock; + const block = JSON.parse(safeStableStringify(block_data)).decodedBlock; const blockSig = block.metadata.metadata[0].signatures; const sigs = []; @@ -289,7 +295,7 @@ export class StrategyFabric implements ObtainLedgerStrategy { }, signature: Buffer.from(sig.signature.data).toString("hex"), }; - sigs.push(JSON.stringify(decoded)); + sigs.push(safeStableStringify(decoded)); } return { hash: Buffer.from(block.header.data_hash.data).toString("hex"), @@ -440,7 +446,7 @@ export class StrategyFabric implements ObtainLedgerStrategy { ts, new TransactionProof(new Proof({ creator: "" }), txId), //transaction proof details are set in function 'generateLedgerStates' ); - transaction.setPayload(JSON.stringify(tx.value)); + transaction.setPayload(safeStableStringify(tx.value) ?? ""); transactions.push(transaction); } return transactions.reverse(); diff --git a/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/privacy-policies.ts b/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/privacy-policies.ts index a32c5314d5..7456816a3b 100644 --- a/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/privacy-policies.ts +++ b/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/privacy-policies.ts @@ -8,6 +8,35 @@ export interface IPrivacyPolicyValue { policy: PrivacyPolicyOpts; policyHash: string; } + +// Type guard for PrivacyPolicyOpts +export function isPrivacyPolicyOpts( + value: unknown, +): value is PrivacyPolicyOpts { + return ( + typeof value === "string" && + Object.values(PrivacyPolicyOpts).includes(value as PrivacyPolicyOpts) + ); +} + +// Type guard for IPrivacyPolicyValue +export function isPrivacyPolicyValue(obj: unknown): obj is IPrivacyPolicyValue { + return ( + typeof obj === "object" && + obj !== null && + "policy" in obj && // Ensure 'policy' key exists + isPrivacyPolicyOpts((obj as Record).policy) && // Check if policy is a valid PrivacyPolicyOpts value + typeof (obj as Record).policyHash === "string" // Ensure 'policyHash' is a string + ); +} + +// Type guard for an array of IPrivacyPolicyValue +export function isPrivacyPolicyValueArray( + input: unknown, +): input is Array { + return Array.isArray(input) && input.every(isPrivacyPolicyValue); +} + export class PrivacyPolicies { constructor() {} @@ -18,11 +47,24 @@ export class PrivacyPolicies { return view; } + public singleTransaction( + view: View, + stateId: string, + transactionId: string, + ): View { + const snapshot = view.getSnapshot(); + snapshot.filterTransaction(stateId, transactionId); + return view; + } + public getPrivacyPolicy(opts: PrivacyPolicyOpts): IPrivacyPolicy | undefined { switch (opts) { case PrivacyPolicyOpts.PruneState: return this.pruneState; break; + case PrivacyPolicyOpts.SingleTransaction: + return this.singleTransaction; + break; default: return undefined; break; diff --git a/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/proof.ts b/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/proof.ts index 69f18b79bc..c4baa74856 100644 --- a/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/proof.ts +++ b/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/proof.ts @@ -1,6 +1,8 @@ // Proof is a general purpose type, used to represent signatures of diverse elements. // Proof may be used, for example in Fabric, to represent an transaction endorsement // Or simply the signature of a transaction upon is creation (in Besu) +import { stringify as safeStableStringify } from "safe-stable-stringify"; + export class Proof { // The term creator refers to the ID of the entity who created the signature // For example endorserID in Fabric (when Proof represents an endorsement) @@ -36,7 +38,7 @@ export class Proof { mspid: this.mspid, signature: this.signature, }; - return JSON.stringify(proof); + return safeStableStringify(proof); } public getCreator(): string { diff --git a/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/snapshot.ts b/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/snapshot.ts index 3a22583fa8..a21166731b 100644 --- a/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/snapshot.ts +++ b/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/snapshot.ts @@ -1,4 +1,5 @@ import { State } from "./state"; +import { stringify as safeStableStringify } from "safe-stable-stringify"; export class Snapshot { private id: string; @@ -76,6 +77,22 @@ export class Snapshot { this.stateBins = stateBins; } + public selectStates(states: string[]): void { + const stateBins: State[] = []; + for (const state of this.stateBins) { + if (states.includes(state.getId())) { + stateBins.push(state); + } + } + this.stateBins = stateBins; + } + + public filterTransaction(stateId: string, transaction: string): void { + this.selectStates([stateId]); + const state = this.stateBins[0]; + state.selectTransactions([transaction]); + } + public getSnapshotJson(): string { const snapshotJson = { id: this.id, @@ -83,7 +100,7 @@ export class Snapshot { stateBins: this.stateBins, }; - return JSON.stringify(snapshotJson); + return safeStableStringify(snapshotJson); } public removeState(stateId: string) { diff --git a/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/state.ts b/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/state.ts index b390afa36c..3564da1c3b 100644 --- a/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/state.ts +++ b/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/state.ts @@ -1,5 +1,6 @@ import { StateProof } from "./state-proof"; import { Transaction } from "./transaction"; +import { stringify as safeStableStringify } from "safe-stable-stringify"; export class State { private id: string; @@ -36,7 +37,7 @@ export class State { for (const tx of this.transactions) { txs.push(tx.getTxJson()); - txEndorsements.push(JSON.stringify(tx.getProof())); + txEndorsements.push(safeStableStringify(tx.getProof())); } const jsonSnap = { @@ -47,13 +48,23 @@ export class State { proofs: txEndorsements, }; - return JSON.stringify(jsonSnap); + return safeStableStringify(jsonSnap); } public getTransactions() { return this.transactions; } + public selectTransactions(txs: string[]): void { + const transactions: Transaction[] = []; + for (const tx of this.transactions) { + if (txs.includes(tx.getId())) { + transactions.push(tx); + } + } + this.transactions = transactions; + } + public getInitialTime(): string { if (this.transactions.length >= 1) { return this.transactions[0].getTimeStamp(); diff --git a/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/transaction-proof.ts b/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/transaction-proof.ts index 1e3f51f065..7196a08223 100644 --- a/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/transaction-proof.ts +++ b/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/transaction-proof.ts @@ -1,4 +1,5 @@ import { Proof } from "./proof"; +import { stringify as safeStableStringify } from "safe-stable-stringify"; export class TransactionProof { private transactionCreator: Proof; @@ -27,7 +28,7 @@ export class TransactionProof { transactionCreator: this.transactionCreator, endorsements: this.endorsements, }; - return JSON.stringify(proof); + return safeStableStringify(proof); } public getCreator(): Proof { diff --git a/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/transaction.ts b/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/transaction.ts index 07fbe6212f..b9d4c4f49b 100644 --- a/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/transaction.ts +++ b/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/transaction.ts @@ -1,5 +1,6 @@ import { Proof } from "./proof"; import { TransactionProof } from "./transaction-proof"; +import { stringify as safeStableStringify } from "safe-stable-stringify"; export class Transaction { private id: string; @@ -52,7 +53,7 @@ export class Transaction { public getTxJson(): string { const tx = { Id: this.id, TimeStamp: this.timeStamp }; - return JSON.stringify(tx); + return safeStableStringify(tx); } public getProof(): TransactionProof { diff --git a/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/view.ts b/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/view.ts index 9e076131f5..78cce5861c 100644 --- a/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/view.ts +++ b/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-creation/view.ts @@ -5,6 +5,7 @@ import { Transaction } from "./transaction"; import { IPrivacyPolicy, IPrivacyPolicyValue } from "./privacy-policies"; import { PrivacyPolicyOpts } from "../generated/openapi/typescript-axios"; import { JsObjectSigner } from "@hyperledger/cactus-common"; +import { stringify as safeStableStringify } from "safe-stable-stringify"; export interface IViewMetadata { viewId: string; @@ -87,9 +88,9 @@ export class View { const transactions: string[] = []; for (const state of this.snapshot.getStateBins()) { - states.push(JSON.stringify(state.getStateProof())); + states.push(safeStableStringify(state.getStateProof())); for (const transaction of state.getTransactions()) { - transactions.push(JSON.stringify(transaction.getProof())); + transactions.push(safeStableStringify(transaction.getProof())); } } @@ -112,7 +113,7 @@ export class View { tF: this.tF, snapshot: this.snapshot, }; - return JSON.stringify(viewStr); + return safeStableStringify(viewStr); // return this.snapshot.getSnapshotJson(); } public getViewProof(): { diff --git a/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-merging/integrated-view.ts b/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-merging/integrated-view.ts index 36e8607b59..cf12e70ccc 100644 --- a/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-merging/integrated-view.ts +++ b/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-merging/integrated-view.ts @@ -8,6 +8,7 @@ import { Transaction } from "../view-creation/transaction"; import { IViewMetadata } from "../view-creation/view"; import { MergePolicyOpts } from "../generated/openapi/typescript-axios"; import { JsObjectSigner } from "@hyperledger/cactus-common"; +import { stringify as safeStableStringify } from "safe-stable-stringify"; export class IntegratedView { private id: string; @@ -114,11 +115,11 @@ export class IntegratedView { const states: string[] = []; const transactions: string[] = []; this.getAllTransactions().forEach((transaction) => { - transactions.push(JSON.stringify(transaction.getProof())); + transactions.push(safeStableStringify(transaction.getProof())); }); this.getAllStates().forEach((state) => { - states.push(JSON.stringify(state.getStateProof())); + states.push(safeStableStringify(state.getStateProof())); }); const statesTree = new MerkleTree(states, undefined, { sort: true, @@ -129,7 +130,7 @@ export class IntegratedView { hashLeaves: true, }); const viewsTree = new MerkleTree( - this.viewsMetadata.map((x) => JSON.stringify(x)), + this.viewsMetadata.map((x) => safeStableStringify(x)), undefined, { sort: true, diff --git a/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-merging/merge-policies.ts b/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-merging/merge-policies.ts index ec0efaa494..532cd30a95 100644 --- a/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-merging/merge-policies.ts +++ b/packages/cactus-plugin-bungee-hermes/src/main/typescript/view-merging/merge-policies.ts @@ -8,6 +8,34 @@ export interface IMergePolicyValue { policy: MergePolicyOpts; policyHash?: string; //undefined if policy is NONE } + +// Type guard for MergePolicyOpts +export function isMergePolicyOpts(value: unknown): value is MergePolicyOpts { + return ( + typeof value === "string" && + Object.values(MergePolicyOpts).includes(value as MergePolicyOpts) + ); +} + +// Type guard for IMergePolicyValue +export function isMergePolicyValue(obj: unknown): obj is IMergePolicyValue { + return ( + typeof obj === "object" && + obj !== null && + "policy" in obj && // Ensure 'policy' key exists + isMergePolicyOpts((obj as Record).policy) && // Check if policy is a valid MergePolicyOpts value + (typeof (obj as Record).policyHash === "string" || + typeof (obj as Record).policyHash === "undefined") // Ensure 'policyHash' is either a string or undefined + ); +} + +// Type guard for an array of IMergePolicyValue +export function isMergePolicyValueArray( + input: unknown, +): input is IMergePolicyValue[] { + return Array.isArray(input) && input.every(isMergePolicyValue); +} + export class MergePolicies { constructor() {} diff --git a/yarn.lock b/yarn.lock index d97677f1ed..7d5b3a589c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10106,6 +10106,7 @@ __metadata: http-errors-enhanced-cjs: "npm:2.0.1" key-encoder: "npm:2.0.3" merkletreejs: "npm:0.3.11" + safe-stable-stringify: "npm:2.5.0" socket.io: "npm:4.6.2" typescript-optional: "npm:2.0.1" uuid: "npm:10.0.0" @@ -47657,6 +47658,13 @@ __metadata: languageName: node linkType: hard +"safe-stable-stringify@npm:2.5.0": + version: 2.5.0 + resolution: "safe-stable-stringify@npm:2.5.0" + checksum: 10/2697fa186c17c38c3ca5309637b4ac6de2f1c3d282da27cd5e1e3c88eca0fb1f9aea568a6aabdf284111592c8782b94ee07176f17126031be72ab1313ed46c5c + languageName: node + linkType: hard + "safe-stable-stringify@npm:^2.3.1": version: 2.3.1 resolution: "safe-stable-stringify@npm:2.3.1"