diff --git a/packages/cactus-api-client/src/main/typescript/socketio-api-client.ts b/packages/cactus-api-client/src/main/typescript/socketio-api-client.ts index 2f68277799..32b9c3ba91 100644 --- a/packages/cactus-api-client/src/main/typescript/socketio-api-client.ts +++ b/packages/cactus-api-client/src/main/typescript/socketio-api-client.ts @@ -195,6 +195,11 @@ export class SocketIOApiClient implements ISocketApiClient { method: Record, args: any, ): Promise { + let timeout: ReturnType | undefined; + // `Function` is used by socketio `socket.off()` method + // eslint-disable-next-line @typescript-eslint/ban-types + const freeableListeners = new Map(); + return new Promise((resolve, reject) => { this.log.debug("call : sendSyncRequest"); @@ -213,22 +218,31 @@ export class SocketIOApiClient implements ISocketApiClient { const reqID = this.genarateReqID(); this.log.debug(`##sendSyncRequest, reqID = ${reqID}`); - this.socket.on("connect_error", (err: Error) => { + const connectErrorHandler = (err: Error) => { this.log.error("##connect_error:", err); this.socket.disconnect(); reject(err); - }); - this.socket.on("connect_timeout", (err: Record) => { + }; + this.socket.on("connect_error", connectErrorHandler); + freeableListeners.set("connect_error", connectErrorHandler); + + const connectTimeoutHandler = (err: Record) => { this.log.error("####Error:", err); this.socket.disconnect(); reject(err); - }); - this.socket.on("error", (err: Record) => { + }; + this.socket.on("connect_timeout", connectTimeoutHandler); + freeableListeners.set("connect_timeout", connectTimeoutHandler); + + const errorHandler = (err: Record) => { this.log.error("####Error:", err); this.socket.disconnect(); reject(err); - }); - this.socket.on("response", (result: any) => { + }; + this.socket.on("error", errorHandler); + freeableListeners.set("error", errorHandler); + + const responseHandler = (result: any) => { this.log.debug("#[recv]response, res:", result); if (reqID === result.id) { responseFlag = true; @@ -253,12 +267,17 @@ export class SocketIOApiClient implements ISocketApiClient { }) .catch((err) => { responseFlag = false; - this.log.debug("checkValidator error:", err); - this.log.error(err); + this.log.error("checkValidator error:", err); + reject({ + status: 504, + error: err, + }); }); } } - }); + }; + this.socket.on("response", responseHandler); + freeableListeners.set("response", responseHandler); // Call Validator const requestData = { @@ -275,7 +294,7 @@ export class SocketIOApiClient implements ISocketApiClient { const timeoutMilliseconds = this.options.syncFunctionTimeoutMillisecond || defaultSyncFunctionTimeoutMillisecond; - setTimeout(() => { + timeout = setTimeout(() => { if (responseFlag === false) { this.log.debug("requestTimeout reqID:", reqID); resolve({ status: 504 }); @@ -285,6 +304,13 @@ export class SocketIOApiClient implements ISocketApiClient { this.log.error("##Error: sendSyncRequest:", err); reject(err); } + }).finally(() => { + freeableListeners.forEach((listener, eventName) => + this.socket.off(eventName, listener), + ); + if (timeout) { + clearTimeout(timeout); + } }); } @@ -307,6 +333,14 @@ export class SocketIOApiClient implements ISocketApiClient { } else { this.log.debug("Create new observable subject..."); + // `Function` is used by socketio `socket.off()` method + // eslint-disable-next-line @typescript-eslint/ban-types + const freeableListeners = new Map(); + const freeListeners = () => + freeableListeners.forEach((listener, eventName) => + this.socket.off(eventName, listener), + ); + this.monitorSubject = new ReplaySubject(0); this.log.debug("call : startMonitor"); @@ -315,38 +349,46 @@ export class SocketIOApiClient implements ISocketApiClient { `##in startMonitor, validatorUrl = ${this.options.validatorURL}`, ); - this.socket.on("connect_error", (err: Error) => { + const connectErrorHandler = (err: Error) => { this.log.error("##connect_error:", err); this.socket.disconnect(); if (this.monitorSubject) { this.monitorSubject.error(err); } - }); + }; + this.socket.on("connect_error", connectErrorHandler); + freeableListeners.set("connect_error", connectErrorHandler); - this.socket.on("connect_timeout", (err: Record) => { + const connectTimeoutHandler = (err: Record) => { this.log.error("####Error:", err); this.socket.disconnect(); if (this.monitorSubject) { this.monitorSubject.error(err); } - }); + }; + this.socket.on("connect_timeout", connectTimeoutHandler); + freeableListeners.set("connect_timeout", connectTimeoutHandler); - this.socket.on("error", (err: Record) => { + const errorHandler = (err: Record) => { this.log.error("####Error:", err); this.socket.disconnect(); if (this.monitorSubject) { this.monitorSubject.error(err); } - }); + }; + this.socket.on("error", errorHandler); + freeableListeners.set("error", errorHandler); - this.socket.on("monitor_error", (err: Record) => { + const monitorErrorHandler = (err: Record) => { this.log.error("#### Monitor Error:", err); if (this.monitorSubject) { this.monitorSubject.error(err); } - }); + }; + this.socket.on("monitor_error", monitorErrorHandler); + freeableListeners.set("monitor_error", monitorErrorHandler); - this.socket.on("eventReceived", (res: any) => { + const eventReceivedHandler = (res: any) => { // output the data received from the client this.log.debug("#[recv]eventReceived, res:", res); @@ -364,7 +406,9 @@ export class SocketIOApiClient implements ISocketApiClient { .catch((err) => { this.log.error(err); }); - }); + }; + this.socket.on("eventReceived", eventReceivedHandler); + freeableListeners.set("eventReceived", eventReceivedHandler); const emitStartMonitor = () => { this.log.debug("##emit: startMonitor"); @@ -378,27 +422,33 @@ export class SocketIOApiClient implements ISocketApiClient { if (this.socket.connected) { emitStartMonitor(); } else { - this.socket.on("connect", () => { + const connectHandler = () => { this.log.debug("#connect"); emitStartMonitor(); - }); + }; + this.socket.on("connect", connectHandler); + freeableListeners.set("connect", connectHandler); } + + return this.monitorSubject.pipe( + finalize(() => { + if (this.monitorSubject && !this.monitorSubject.observed) { + // Last observer finished + this.log.debug("##emit: stopMonitor"); + this.socket.emit("stopMonitor"); + freeListeners(); + this.monitorSubject = undefined; + } + }), + ); } catch (err) { this.log.error(`##Error: startMonitor, ${err}`); + freeListeners(); this.monitorSubject.error(err); } - - return this.monitorSubject.pipe( - finalize(() => { - if (this.monitorSubject && !this.monitorSubject.observed) { - // Last observer finished - this.log.debug("##emit: stopMonitor"); - this.socket.emit("stopMonitor"); - this.monitorSubject = undefined; - } - }), - ); } + + return this.monitorSubject; } /** diff --git a/packages/cactus-api-client/src/test/typescript/unit/socketio-api-client.test.ts b/packages/cactus-api-client/src/test/typescript/unit/socketio-api-client.test.ts index a6da7bafa1..b8d6a76f8b 100644 --- a/packages/cactus-api-client/src/test/typescript/unit/socketio-api-client.test.ts +++ b/packages/cactus-api-client/src/test/typescript/unit/socketio-api-client.test.ts @@ -379,12 +379,13 @@ describe("SocketIOApiClient Tests", function () { }); const verifyMock = jest.fn(); - verifyMock.mockRejectedValue({ message: "mock verify error" }); + const verifyError = { message: "mock verify error" }; + verifyMock.mockRejectedValue(verifyError); sut.checkValidator = verifyMock; return expect( sut.sendSyncRequest(reqContract, reqMethod, reqArgs), - ).resolves.toEqual({ status: 504 }); // timeout + ).rejects.toEqual({ status: 504, error: verifyError }); }); test("Process only requests with matching ID", () => { diff --git a/packages/cactus-plugin-ledger-connector-go-ethereum-socketio/src/main/typescript/connector/ServerPlugin.ts b/packages/cactus-plugin-ledger-connector-go-ethereum-socketio/src/main/typescript/connector/ServerPlugin.ts index af960db19e..74085bfeb6 100644 --- a/packages/cactus-plugin-ledger-connector-go-ethereum-socketio/src/main/typescript/connector/ServerPlugin.ts +++ b/packages/cactus-plugin-ledger-connector-go-ethereum-socketio/src/main/typescript/connector/ServerPlugin.ts @@ -27,6 +27,41 @@ import Web3 from "web3"; import { AbiItem } from "web3-utils"; import { safeStringifyException } from "@hyperledger/cactus-common"; +var WEB3_HTTP_PROVIDER_OPTIONS = { + keepAlive: true, +}; + +const WEB3_WS_PROVIDER_OPTIONS = { + // Enable auto reconnection + reconnect: { + auto: true, + delay: 3000, // ms + maxAttempts: 30, + onTimeout: false, + }, +}; + +function getWeb3Provider(host: string) { + const hostUrl = new URL(host); + + switch (hostUrl.protocol) { + case "http:": + case "https:": + return new Web3.providers.HttpProvider(host, WEB3_HTTP_PROVIDER_OPTIONS); + case "ws:": + return new Web3.providers.WebsocketProvider( + host, + WEB3_WS_PROVIDER_OPTIONS, + ); + default: + throw new Error( + `Unknown host protocol ${hostUrl.protocol} in URL ${host}`, + ); + } +} + +const web3 = new Web3(getWeb3Provider(configRead("ledgerUrl"))); + /* * ServerPlugin * Class definition for server plugins @@ -95,7 +130,6 @@ export class ServerPlugin { // Handling exceptions to absorb the difference of interest. try { - const web3 = new Web3(configRead("ledgerUrl")); const balance = await web3.eth.getBalance(ethargs); const amountVal = parseInt(balance, 10); const retObj = { @@ -178,7 +212,6 @@ export class ServerPlugin { // Handle the exception once to absorb the difference of interest. try { - const web3 = new Web3(configRead("ledgerUrl")); const res = web3.eth[sendFunction](sendArgs); retObj = { @@ -252,7 +285,6 @@ export class ServerPlugin { // Handling exceptions to absorb the difference of interest. try { - const web3 = new Web3(configRead("ledgerUrl")); const txnCount = await web3.eth.getTransactionCount(ethargs); logger.info(`getNonce(): txnCount: ${txnCount}`); const hexStr = web3.utils.toHex(txnCount); @@ -324,7 +356,6 @@ export class ServerPlugin { // Handling exceptions to absorb the difference of interest. try { - const web3 = new Web3(configRead("ledgerUrl")); const hexStr = web3.utils.toHex(targetValue); logger.info(`toHex(): hexStr: ${hexStr}`); const result = { @@ -396,7 +427,6 @@ export class ServerPlugin { const serializedTx = funcParam["serializedTx"]; logger.info("serializedTx :" + serializedTx); - const web3 = new Web3(configRead("ledgerUrl")); const res = await web3.eth.sendSignedTransaction(serializedTx); const result = { txid: res.transactionHash, @@ -453,7 +483,6 @@ export class ServerPlugin { // Handle the exception once to absorb the difference of interest. try { - const web3 = new Web3(configRead("ledgerUrl")); const looseWeb3Eth = web3.eth as any; const isSafeToCall = @@ -521,12 +550,11 @@ export class ServerPlugin { // Handle the exception once to absorb the difference of interest. try { - const web3 = new Web3(configRead("ledgerUrl")); - const contract = new web3.eth.Contract( args.contract.abi as AbiItem[], args.contract.address, ); + (contract as any).setProvider(web3.currentProvider); const isSafeToCall = Object.prototype.hasOwnProperty.call(contract.methods, sendCommand) && diff --git a/packages/cactus-test-verifier-client/README.md b/packages/cactus-test-verifier-client/README.md new file mode 100644 index 0000000000..ff02083ef2 --- /dev/null +++ b/packages/cactus-test-verifier-client/README.md @@ -0,0 +1,35 @@ +# `@hyperledger/cactus-test-verifier-client` + +## Usage + +### Stress test +- Used for manual leak analysis. +- The test will execute with exposed GC and disabled optimizations. +- Results will be written to `./integration-with-verifier-client-stress.log` for further analysis (in the `cwd` where you execute the command). Columns (in order): + - `rss` + - `heapTotal` + - `heapUsed` + - `external` + - `arrayBuffers` +- Report lines: + - Initial memory usage (before running the tests). + - Usage after running the stress test. + - Usage after freeing the verifier client. +- You can uncomment the `checkMemory` call in test file for more step-by-step report, but it will make the test run longer. +- To investigate with the node debugger use `stress-test-inspect` script. + +``` bash +# Make sure the build was successful before executing these commands. + +# Execute stress test +yarn run stress-test + +# Execute stress test with debugger inspect break +yarn run stress-test-inspect +``` + +## FAQ + +### **What is a dedicated test package for?** + +This is a dedicated test package meaning that it verifies the integration between two packages that are somehow dependent on each other and therefore these tests cannot be added properly in the child package due to circular dependency issues and it would not be fitting to add it in the parent because the child package's tests should not be held by the parent as a matter of principle. diff --git a/packages/cactus-test-verifier-client/package.json b/packages/cactus-test-verifier-client/package.json new file mode 100644 index 0000000000..cd6fe5ec19 --- /dev/null +++ b/packages/cactus-test-verifier-client/package.json @@ -0,0 +1,69 @@ +{ + "name": "@hyperledger/cactus-test-verifier-client", + "version": "1.1.3", + "description": "Integration and stress tests for the verifier client.", + "keywords": [ + "Hyperledger", + "Cactus", + "Integration", + "Blockchain", + "Distributed Ledger Technology" + ], + "homepage": "https://github.com/hyperledger/cactus#readme", + "bugs": { + "url": "https://github.com/hyperledger/cactus/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/hyperledger/cactus.git" + }, + "license": "Apache-2.0", + "author": { + "name": "Hyperledger Cactus Contributors", + "email": "cactus@lists.hyperledger.org", + "url": "https://www.hyperledger.org/use/cactus" + }, + "contributors": [ + { + "name": "Please add yourself to the list of contributors", + "email": "your.name@example.com", + "url": "https://example.com" + }, + { + "name": "Michal Bajer", + "email": "michal.bajer@fujitsu.com", + "url": "https://www.fujitsu.com/global/" + } + ], + "main": "dist/lib/main/typescript/index.js", + "module": "dist/lib/main/typescript/index.js", + "types": "dist/lib/main/typescript/index.d.ts", + "files": [ + "dist/*" + ], + "scripts": { + "stress-test": "node --expose-gc --no-opt dist/main/typescript/verifier-with-go-eth-stress-check.js", + "stress-test-inspect": "node --expose-gc --inspect-brk --no-opt dist/main/typescript/verifier-with-go-eth-stress-check.js" + }, + "dependencies": { + "@hyperledger/cactus-common": "1.1.3", + "@hyperledger/cactus-test-tooling": "1.1.3", + "@hyperledger/cactus-cmd-api-server": "1.1.3", + "@hyperledger/cactus-api-client": "1.1.3", + "@hyperledger/cactus-verifier-client": "1.1.3", + "@hyperledger/cactus-plugin-ledger-connector-go-ethereum-socketio": "1.1.3", + "log4js": "6.4.1", + "web3": "1.7.3" + }, + "devDependencies": { + "@types/node": "14.17.32" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + }, + "publishConfig": { + "access": "public" + }, + "watch": {} +} \ No newline at end of file diff --git a/packages/cactus-test-verifier-client/src/main/typescript/index.ts b/packages/cactus-test-verifier-client/src/main/typescript/index.ts new file mode 100755 index 0000000000..87cb558397 --- /dev/null +++ b/packages/cactus-test-verifier-client/src/main/typescript/index.ts @@ -0,0 +1 @@ +export * from "./public-api"; diff --git a/packages/cactus-test-verifier-client/src/main/typescript/index.web.ts b/packages/cactus-test-verifier-client/src/main/typescript/index.web.ts new file mode 100755 index 0000000000..cb0ff5c3b5 --- /dev/null +++ b/packages/cactus-test-verifier-client/src/main/typescript/index.web.ts @@ -0,0 +1 @@ +export {}; diff --git a/packages/cactus-test-verifier-client/src/main/typescript/public-api.ts b/packages/cactus-test-verifier-client/src/main/typescript/public-api.ts new file mode 100755 index 0000000000..ea1bc5d462 --- /dev/null +++ b/packages/cactus-test-verifier-client/src/main/typescript/public-api.ts @@ -0,0 +1 @@ +export { runStressTest } from "./verifier-with-go-eth-stress-check"; diff --git a/packages/cactus-test-verifier-client/src/main/typescript/verifier-with-go-eth-stress-check.ts b/packages/cactus-test-verifier-client/src/main/typescript/verifier-with-go-eth-stress-check.ts new file mode 100644 index 0000000000..78019f39be --- /dev/null +++ b/packages/cactus-test-verifier-client/src/main/typescript/verifier-with-go-eth-stress-check.ts @@ -0,0 +1,250 @@ +/** + * Stress test to detect possible memory leaks in cactus-verifier-client. + * Repeated requests are sent to go-ethereum validator (packages/cactus-plugin-ledger-connector-go-ethereum-socketio). + * Run command: + * node --expose-gc --no-opt dist/main/typescript/verifier-with-go-eth-stress-check.js + */ + +const STRESS_LOG_FILENAME = "integration-with-verifier-client-stress.log"; +const testLogLevel: LogLevelDesc = "info"; +const sutLogLevel: LogLevelDesc = "info"; + +// Ledger settings +const imageName = "openethereum/openethereum"; +const imageVersion = "v3.3.5"; + +// ApiClient settings +const syncReqTimeout = 1000 * 10; // 10 seconds + +import { + LogLevelDesc, + LoggerProvider, + Logger, +} from "@hyperledger/cactus-common"; + +import { + OpenEthereumTestLedger, + pruneDockerAllIfGithubAction, +} from "@hyperledger/cactus-test-tooling"; + +import { SelfSignedPkiGenerator } from "@hyperledger/cactus-cmd-api-server"; +import { SocketIOApiClient } from "@hyperledger/cactus-api-client"; +import { + Verifier, + VerifierFactory, + VerifierFactoryConfig, +} from "@hyperledger/cactus-verifier-client"; + +import { Server as HttpsServer } from "https"; +import { Account } from "web3-core"; +import { appendFileSync, writeFileSync } from "fs"; + +// Logger setup +const log: Logger = LoggerProvider.getOrCreate({ + label: "integration-with-verifier-client-stress.test", + level: testLogLevel, +}); + +let ledger: OpenEthereumTestLedger; +let connectorCertValue: string; +let connectorPrivKeyValue: string; +let connectorServer: HttpsServer; +let verifier: Verifier; +let constTestAcc: Account; +const constTestAccBalance = 5 * 1000000; + +/** + * Check current memory usage, log it to the screen and write it to a file for future analysis. + */ +function checkMemory(): void { + if (global.gc) { + log.info("Run GC"); + global.gc(); + } else { + throw new Error("Run with --expose-gc"); + } + + const memoryUsage = process.memoryUsage(); + const entry = [ + memoryUsage.rss, + memoryUsage.heapTotal, + memoryUsage.heapUsed, + memoryUsage.external, + memoryUsage.arrayBuffers, + ].join(", "); + + log.warn(entry); + appendFileSync(STRESS_LOG_FILENAME, entry + "\n"); +} + +/** + * Startup the ledger and cactus connector + */ +async function setupEnvironment(): Promise { + log.info("Prune Docker..."); + await pruneDockerAllIfGithubAction({ logLevel: testLogLevel }); + + log.info(`Start Ledger ${imageName}:${imageVersion}...`); + ledger = new OpenEthereumTestLedger({ + imageName, + imageVersion, + emitContainerLogs: true, + logLevel: sutLogLevel, + }); + await ledger.start(); + const ledgerRpcUrl = await ledger.getRpcApiWebSocketHost(); + log.info(`Ledger started, RPC: ${ledgerRpcUrl}`); + + // Create Test Account + constTestAcc = await ledger.createEthTestAccount(constTestAccBalance); + + // Generate connector private key and certificate + const pkiGenerator = new SelfSignedPkiGenerator(); + const pki = pkiGenerator.create("localhost"); + connectorPrivKeyValue = pki.privateKeyPem; + connectorCertValue = pki.certificatePem; + const jwtAlgo = "RS512"; + + const connectorConfig: any = { + sslParam: { + port: 0, // random port + keyValue: connectorPrivKeyValue, + certValue: connectorCertValue, + jwtAlgo: jwtAlgo, + }, + logLevel: sutLogLevel, + ledgerUrl: ledgerRpcUrl, + }; + const configJson = JSON.stringify(connectorConfig); + log.debug("Connector Config:", configJson); + + log.info("Export connector config before loading the module..."); + process.env["NODE_CONFIG"] = configJson; + + const conenctorModule = await import( + "@hyperledger/cactus-plugin-ledger-connector-go-ethereum-socketio" + ); + // Run the connector + connectorServer = await conenctorModule.startGoEthereumSocketIOConnector(); + const connectorAddress = connectorServer.address(); + if (!connectorAddress || typeof connectorAddress === "string") { + throw new Error("Unexpected go-ethereum connector AddressInfo type"); + } + log.info( + "Go-Ethereum-SocketIO Connector started on:", + `${connectorAddress.address}:${connectorAddress.port}`, + ); + + // Create Verifier + const ledgerPluginInfo: VerifierFactoryConfig = [ + { + validatorID: "go-eth-socketio-test", + validatorType: "legacy-socketio", + validatorURL: `https://localhost:${connectorAddress.port}`, + validatorKeyValue: connectorCertValue, + logLevel: sutLogLevel, + maxCounterRequestID: 1000, + syncFunctionTimeoutMillisecond: syncReqTimeout, + socketOptions: { + rejectUnauthorized: false, + reconnection: false, + timeout: syncReqTimeout * 2, + }, + ledgerInfo: { + ledgerAbstract: "Go-Ethereum Ledger", + }, + apiInfo: [], + }, + ]; + const verifierFactory = new VerifierFactory(ledgerPluginInfo); + verifier = verifierFactory.getVerifier( + "go-eth-socketio-test", + "legacy-socketio", + ); + + // Clear the stress log file + writeFileSync(STRESS_LOG_FILENAME, ""); +} + +/** + * Cleanup the ledger and cactus connector + */ +async function cleanupEnvironment(): Promise { + log.info("FINISHING THE TESTS"); + + if (verifier) { + log.info("Close Verifier ApiClient connection..."); + verifier.ledgerApi.close(); + } + + // Report after verifier close + await new Promise((resolve) => setTimeout(resolve, 2000)); + checkMemory(); + + if (connectorServer) { + log.info("Stop the ethereum connector..."); + await new Promise((resolve) => + connectorServer.close(() => resolve()), + ); + } + + if (ledger) { + log.info("Stop the ethereum ledger..."); + await ledger.stop(); + await ledger.destroy(); + } + + log.info("Prune Docker..."); + await pruneDockerAllIfGithubAction({ logLevel: testLogLevel }); +} + +/** + * Stress test body + */ +async function executeTest(): Promise { + // getNumericBalance input + const getBalanceMethod = { type: "function", command: "getNumericBalance" }; + const getBalanceArgs = { + args: [constTestAcc.address], + }; + + // Initial memory report + await new Promise((resolve) => setTimeout(resolve, 5000)); + checkMemory(); + + // Test loop + const startTime = Date.now(); + let count = 10000; + while (count-- > 0) { + try { + await verifier.sendSyncRequest({}, getBalanceMethod, getBalanceArgs); + //checkMemory(); + } catch (error) { + log.error(error); + } + } + const endTime = Date.now(); + log.info(`Execution time: ${(endTime - startTime) / 1000} seconds.`); + + // Final report + await new Promise((resolve) => setTimeout(resolve, 5000)); + checkMemory(); +} + +/** + * Main logic of the test + */ +export async function runStressTest(): Promise { + await setupEnvironment(); + try { + await executeTest(); + } catch (error) { + log.error("Stress test failed -", error); + } finally { + log.info("Cleanup the environment"); + await cleanupEnvironment(); + } +} + +// Entry point +runStressTest(); diff --git a/packages/cactus-test-verifier-client/tsconfig.json b/packages/cactus-test-verifier-client/tsconfig.json new file mode 100644 index 0000000000..443b21d086 --- /dev/null +++ b/packages/cactus-test-verifier-client/tsconfig.json @@ -0,0 +1,34 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./dist/", + "declarationDir": "dist/", + "resolveJsonModule": true, + "rootDir": "./src", + "tsBuildInfoFile": "../../.build-cache/cactus-test-verifier-client.tsbuildinfo" + }, + "include": [ + "./src" + ], + "references": [ + { + "path": "../cactus-common/tsconfig.json" + }, + { + "path": "../cactus-test-tooling/tsconfig.json" + }, + { + "path": "../cactus-cmd-api-server/tsconfig.json" + }, + { + "path": "../cactus-api-client/tsconfig.json" + }, + { + "path": "../cactus-verifier-client/tsconfig.json" + }, + { + "path": "../cactus-plugin-ledger-connector-go-ethereum-socketio/tsconfig.json" + } + ] +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 3eb5852e7c..ef309cad09 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -100,6 +100,9 @@ { "path": "./packages/cactus-test-tooling/tsconfig.json" }, + { + "path": "./packages/cactus-test-verifier-client/tsconfig.json" + }, { "path": "./examples/cactus-example-carbon-accounting-backend/tsconfig.json" },