diff --git a/examples/cactus-example-tcs-huawei/.gitignore b/examples/cactus-example-tcs-huawei/.gitignore new file mode 100644 index 0000000000..bc3fec4ce7 --- /dev/null +++ b/examples/cactus-example-tcs-huawei/.gitignore @@ -0,0 +1,7 @@ +MeterInfo.json + +# BLP artifacts +etc/ + +# don't commit package-lock +package-lock.json diff --git a/examples/cactus-example-tcs-huawei/BalanceManagement.ts b/examples/cactus-example-tcs-huawei/BalanceManagement.ts new file mode 100644 index 0000000000..2eaf10e66b --- /dev/null +++ b/examples/cactus-example-tcs-huawei/BalanceManagement.ts @@ -0,0 +1,65 @@ +/* + * Copyright 2020-2021 Hyperledger Cactus Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * BalanceManagement.ts + */ + +import { + LPInfoHolder, + ConfigUtil, +} from "@hyperledger/cactus-cmd-socketio-server"; +import { + VerifierFactory, + VerifierFactoryConfig, +} from "@hyperledger/cactus-verifier-client"; + +const config: any = ConfigUtil.getConfig(); +import { getLogger } from "log4js"; +const moduleName = "BalanceManagement"; +const logger = getLogger(`${moduleName}`); +logger.level = config.logLevel; + +export class BalanceManagement { + private connectInfo: LPInfoHolder = null; // connection information + private readonly verifierFactory: VerifierFactory; + + constructor() { + this.connectInfo = new LPInfoHolder(); + this.verifierFactory = new VerifierFactory( + this.connectInfo.ledgerPluginInfo as VerifierFactoryConfig, + config.logLevel, + ); + } + + getBalance(account: string): Promise { + return new Promise((resolve, reject) => { + // for LedgerOperation + // const execData = {"referedAddress": account}; + // const ledgerOperation: LedgerOperation = new LedgerOperation("getNumericBalance", "", execData); + + // for Neo + const contract = {}; // NOTE: Since contract does not need to be specified, specify an empty object. + const method = { type: "web3Eth", command: "getBalance" }; + const template = "default"; + const args = { args: [account] }; + // const method = "default"; + // const args = {"method": {type: "web3Eth", command: "getBalance"}, "args": {"args": [account]}}; + + this.verifierFactory + .getVerifier("84jUisrs") + .sendSyncRequest(contract, method, args) + .then((result) => { + const response = { + status: result.status, + amount: parseFloat(result.data), + }; + resolve(response); + }) + .catch((err) => { + logger.error(err); + reject(err); + }); + }); + } +} diff --git a/examples/cactus-example-tcs-huawei/BusinessLogicTcsElectricityTrade.ts b/examples/cactus-example-tcs-huawei/BusinessLogicTcsElectricityTrade.ts new file mode 100644 index 0000000000..c9cb6ee16d --- /dev/null +++ b/examples/cactus-example-tcs-huawei/BusinessLogicTcsElectricityTrade.ts @@ -0,0 +1,429 @@ +/* + * Copyright 2021 Hyperledger Cactus Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * BusinessLogicElectricityTrade.ts + */ + +import { Request } from "express"; +import { RequestInfo } from "./RequestInfo"; +import { MeterManagement } from "./MeterManagement"; +import { MeterInfo } from "./MeterInfo"; +import { + TradeInfo, + routesTransactionManagement, + BusinessLogicBase, + LedgerEvent, + json2str, + ConfigUtil, + LPInfoHolder, +} from "@hyperledger/cactus-cmd-socketio-server"; +import { makeRawTransaction } from "./TransactionEthereum"; + +import fs from "fs"; +import yaml from "js-yaml"; +//const config: any = JSON.parse(fs.readFileSync("/etc/cactus/default.json", 'utf8')); +const config: any = ConfigUtil.getConfig(); +import { getLogger } from "log4js"; +import { + VerifierFactory, + VerifierFactoryConfig, +} from "@hyperledger/cactus-verifier-client"; +import { NIL } from "uuid"; + +const moduleName = "BusinessLogicElectricityTrade"; +const logger = getLogger(`${moduleName}`); +logger.level = config.logLevel; +const connectInfo = new LPInfoHolder(); +const routesVerifierFactory = new VerifierFactory( + connectInfo.ledgerPluginInfo as VerifierFactoryConfig, + config.logLevel, +); + +export class BusinessLogicElectricityTrade extends BusinessLogicBase { + businessLogicID: string; + meterManagement: MeterManagement; + + constructor(businessLogicID: string) { + super(); + this.businessLogicID = businessLogicID; + this.meterManagement = new MeterManagement(); + } + + startTransaction(req: Request, businessLogicID: string, tradeID: string) { + logger.debug("called startTransaction()"); + + // set RequestInfo + const requestInfo: RequestInfo = new RequestInfo(); + requestInfo.businessLogicID = businessLogicID; + + // set TradeID + requestInfo.setTradeID(tradeID); + + // Create trade information + const tradeInfo: TradeInfo = new TradeInfo( + requestInfo.businessLogicID, + requestInfo.tradeID, + ); + + this.startMonitor(tradeInfo); + } + + startMonitor(tradeInfo: TradeInfo) { + // Get Verifier Instance + logger.debug( + `##startMonitor(): businessLogicID: ${tradeInfo.businessLogicID}`, + ); + const useValidator = JSON.parse( + routesTransactionManagement.getValidatorToUse(tradeInfo.businessLogicID), + ); + logger.debug( + `filterKey: ${config.electricityTradeInfo.tcsHuawei.filterKey}`, + ); + const options = { + filterKey: config.electricityTradeInfo.tcsHuawei.filterKey, + }; + const verifierTcs = routesVerifierFactory.getVerifier( + useValidator["validatorID"][0], + ); + verifierTcs.startMonitor( + "BusinessLogicTcsElectricityTrade", + options, + routesTransactionManagement, + ); + logger.debug("getVerifierTcs"); + } + + remittanceTransaction(transactionSubset: object) { + logger.debug( + `called remittanceTransaction(), accountInfo = ${json2str( + transactionSubset, + )}`, + ); + + const accountInfo = this.getAccountInfo(transactionSubset); + if (Object.keys(accountInfo).length === 0) { + logger.debug(`remittanceTransaction(): skip (No target meter)`); + return; + } + + const txParam: { + fromAddress: string; + fromAddressPkey: string; + toAddress: string; + amount: number; + gas: number; + } = { + fromAddress: accountInfo["fromAddress"], + fromAddressPkey: accountInfo["fromAddressPkey"], + toAddress: accountInfo["toAddress"], + amount: Number(transactionSubset["Value"]), + gas: config.electricityTradeInfo.ethereum.gas, + }; + logger.debug(`####txParam = ${json2str(txParam)}`); + + // Get Verifier Instance + logger.debug( + `##remittanceTransaction(): businessLogicID: ${this.businessLogicID}`, + ); + const useValidator = JSON.parse( + routesTransactionManagement.getValidatorToUse(this.businessLogicID), + ); + // const verifierEthereum = routesTransactionManagement.getVerifier(useValidator['validatorID'][1]); + const verifierEthereum = routesVerifierFactory.getVerifier( + useValidator["validatorID"][1], + ); + verifierEthereum.startMonitor( + "BusinessLogicElectricityTrade", + {}, + routesTransactionManagement, + ); + logger.debug("getVerifierEthereum"); + + // Generate parameters for// sendRawTransaction + logger.debug(`####exec makeRawTransaction!!`); + makeRawTransaction(txParam) + .then((result) => { + logger.info("remittanceTransaction txId : " + result.txId); + + // Set Parameter + logger.debug("remittanceTransaction data : " + json2str(result.data)); + const contract = {}; // NOTE: Since contract does not need to be specified, specify an empty object. + const method = { type: "web3Eth", command: "sendRawTransaction" }; + const args = { args: [result.data["serializedTx"]] }; + + // Run Verifier (Ethereum) + verifierEthereum + .sendAsyncRequest(contract, method, args) + .then(() => { + logger.debug(`##remittanceTransaction sendAsyncRequest finish`); + }) + .catch((err) => { + logger.error(err); + }); + }) + .catch((err) => { + logger.error(err); + }); + } + + onEvent(ledgerEvent: LedgerEvent, targetIndex: number): void { + logger.debug(`##in BLP:onEvent()`); + logger.debug( + `##onEvent(): ${json2str(ledgerEvent["data"]["blockData"][targetIndex])}`, + ); + switch (ledgerEvent.verifierId) { + case config.electricityTradeInfo.tcsHuawei.validatorID: + this.onEventTCS(ledgerEvent.data, targetIndex); + break; + case config.electricityTradeInfo.ethereum.validatorID: + this.onEventEthereum(ledgerEvent.data, targetIndex); + break; + default: + logger.error( + `##onEvent(), invalid verifierId: ${ledgerEvent.verifierId}`, + ); + return; + } + } + + onEventTCS(event: any, targetIndex: number): void { + logger.debug(`##in onEventTCS()`); + logger.debug("event.blockData", event["blockData"]) + logger.debug("event.blockData[0]", event["blockData"][targetIndex]) + logger.debug("event.blockData[0].Payload", event["blockData"][targetIndex]["Payload"]) + logger.debug("event.blockData.Payload.Name", event["blockData"][targetIndex]["Payload"]["Name"]) + + var trxPayload = event["blockData"][targetIndex]["Payload"] + if (trxPayload == undefined || trxPayload == NIL) { + logger.error("transaction payload is empty") + return + } + try { + if (trxPayload["Verb"].Verb !== "set") { + const transactionSubset = { + Name: trxPayload["Name"], + Value: trxPayload["Value"], + Verb: trxPayload["Verb"], + }; + this.remittanceTransaction(transactionSubset); + } + } catch (err) { + logger.error( + `##onEventTCS(): err: ${err}, event: ${json2str(event)}`, + ); + } + } + + getTransactionFromTCSEvent( + event: object, + targetIndex: number, + ): object | null { + try { + const retTransaction = event["blockData"][targetIndex]; + logger.debug( + `##getTransactionFromTCSEvent(), retTransaction: ${retTransaction}`, + ); + return retTransaction; + } catch (err) { + logger.error( + `##getTransactionFromTCSEvent(): invalid even, err:${err}, event:${event}`, + ); + } + } + + onEventEthereum(event: object, targetIndex: number): void { + logger.debug(`##in onEventEthereum()`); + const tx = this.getTransactionFromEthereumEvent(event, targetIndex); + if (tx == null) { + logger.error(`##onEventEthereum(): invalid event: ${json2str(event)}`); + return; + } + + try { + const txId = tx["hash"]; + const status = event["status"]; + logger.debug(`##txId = ${txId}`); + logger.debug(`##status =${status}`); + + if (status !== 200) { + logger.error( + `##onEventEthereum(): error event, status: ${status}, txId: ${txId}`, + ); + return; + } + } catch (err) { + logger.error( + `##onEventEthereum(): err: ${err}, event: ${json2str(event)}`, + ); + } + } + + getTransactionFromEthereumEvent( + event: object, + targetIndex: number, + ): object | null { + try { + const retTransaction = event["blockData"]["transactions"][targetIndex]; + logger.debug( + `##getTransactionFromEthereumEvent(), retTransaction: ${retTransaction}`, + ); + return retTransaction; + } catch (err) { + logger.error( + `##getTransactionFromEthereumEvent(): invalid even, err:${err}, event:${event}`, + ); + } + } + + getOperationStatus(tradeID: string): object { + logger.debug(`##in getOperationStatus()`); + return {}; + } + + getTxIDFromEvent( + ledgerEvent: LedgerEvent, + targetIndex: number, + ): string | null { + logger.debug(`##in getTxIDFromEvent`); + // logger.debug(`##event: ${json2str(ledgerEvent)}`); + + switch (ledgerEvent.verifierId) { + case config.electricityTradeInfo.tcsHuawei.validatorID: + return this.getTxIDFromEventTCS(ledgerEvent.data, targetIndex); + case config.electricityTradeInfo.ethereum.validatorID: + return this.getTxIDFromEventEtherem(ledgerEvent.data, targetIndex); + default: + logger.error( + `##getTxIDFromEvent(): invalid verifierId: ${ledgerEvent.verifierId}`, + ); + } + return null; + } + + getTxIDFromEventTCS(event: object, targetIndex: number): string | null { + logger.debug(`##in getTxIDFromEventTCS()`); + const tx = this.getTransactionFromTCSEvent(event, targetIndex); + if (tx == null) { + logger.warn(`#getTxIDFromEventTCS(): skip(not found tx)`); + return null; + } + + try { + return "txId-test-1" + const txId = tx["header_signature"]; + + if (typeof txId !== "string") { + logger.warn( + `#getTxIDFromEventTCS(): skip(invalid block, not found txId.), event: ${json2str( + event, + )}`, + ); + return null; + } + + logger.debug(`###getTxIDFromEventTCS(): txId: ${txId}`); + return txId; + } catch (err) { + logger.error( + `##getTxIDFromEventTCS(): err: ${err}, event: ${json2str(event)}`, + ); + return null; + } + } + + getTxIDFromEventEtherem(event: object, targetIndex: number): string | null { + logger.debug(`##in getTxIDFromEventEtherem()`); + const tx = this.getTransactionFromEthereumEvent(event, targetIndex); + if (tx == null) { + logger.warn(`#getTxIDFromEventEtherem(): skip(not found tx)`); + return null; + } + + try { + const txId = tx["hash"]; + + if (typeof txId !== "string") { + logger.warn( + `#getTxIDFromEventEtherem(): skip(invalid block, not found txId.), event: ${json2str( + event, + )}`, + ); + return null; + } + + logger.debug(`###getTxIDFromEventEtherem(): txId: ${txId}`); + return txId; + } catch (err) { + logger.error( + `##getTxIDFromEventEtherem(): err: ${err}, event: ${json2str(event)}`, + ); + return null; + } + } + + getEventDataNum(ledgerEvent: LedgerEvent): number { + logger.debug( + `##in BLP:getEventDataNum(), ledgerEvent.verifierId: ${ledgerEvent.verifierId}`, + ); + const event = ledgerEvent.data; + let retEventNum = 0; + + try { + switch (ledgerEvent.verifierId) { + case config.electricityTradeInfo.tcsHuawei.validatorID: + retEventNum = event["blockData"].length; + break; + case config.electricityTradeInfo.ethereum.validatorID: + retEventNum = event["blockData"]["transactions"].length; + break; + default: + logger.error( + `##getEventDataNum(): invalid verifierId: ${ledgerEvent.verifierId}`, + ); + break; + } + logger.debug( + `##getEventDataNum(): retEventNum: ${retEventNum}, verifierId: ${ledgerEvent.verifierId}`, + ); + return retEventNum; + } catch (err) { + logger.error( + `##getEventDataNum(): invalid even, err: ${err}, event: ${event}`, + ); + return 0; + } + } + + getAccountInfo(transactionSubset: object): object { + const transactionInfo = {}; + + // Get Meter Information. + const meterInfo: MeterInfo | null = this.meterManagement.getMeterInfo( + transactionSubset["Name"], + ); + if (meterInfo === null) { + logger.debug(`Not registered. meterID = ${transactionSubset["Name"]}`); + return transactionInfo; + } + + logger.debug(`getAccountInfo(): Verb = ${transactionSubset["Verb"]}`); + if (transactionSubset["Verb"] === "inc") { + logger.debug("getAccountInfo(): Verb = inc"); + transactionInfo["fromAddress"] = "0x" + meterInfo.bankAccount; + transactionInfo["fromAddressPkey"] = meterInfo.bankAccountPKey; + transactionInfo["toAddress"] = "0x" + meterInfo.powerCompanyAccount; + } + + return transactionInfo; + } + + setConfig(meterParams: string[]): object { + logger.debug("called setConfig()"); + + // add MeterInfo + const meterInfo = new MeterInfo(meterParams); + const result: {} = this.meterManagement.addMeterInfo(meterInfo); + return result; + } +} diff --git a/examples/cactus-example-tcs-huawei/Dockerfile b/examples/cactus-example-tcs-huawei/Dockerfile new file mode 100644 index 0000000000..6795b68371 --- /dev/null +++ b/examples/cactus-example-tcs-huawei/Dockerfile @@ -0,0 +1,16 @@ +FROM cactus-cmd-socketio-server:latest + +ARG NPM_PKG_VERSION=latest + +ENV APP_HOME=/root/cactus + +WORKDIR ${APP_HOME} + +COPY ./dist/yarn.lock ./package.json ./ +RUN yarn add "${CACTUS_CMD_SOCKETIO_PATH}" "@hyperledger/cactus-verifier-client@${NPM_PKG_VERSION}" \ + --production --ignore-engines --non-interactive --cache-folder ./.yarnCache && \ + rm -rf ./.yarnCache + +COPY ./dist ./dist/ + +CMD ["node", "./dist/www.js"] diff --git a/examples/cactus-example-tcs-huawei/MeterInfo.ts b/examples/cactus-example-tcs-huawei/MeterInfo.ts new file mode 100644 index 0000000000..e598b81d16 --- /dev/null +++ b/examples/cactus-example-tcs-huawei/MeterInfo.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2021 Hyperledger Cactus Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * MeterInfo.ts + */ + +export class MeterInfo { + meterID: string; + bankAccount: string; + bankAccountPKey: string; + powerCompanyAccount: string; + + constructor(meterParams: string[]) { + this.meterID = meterParams[0]; + this.bankAccount = meterParams[1]; + this.bankAccountPKey = meterParams[2]; + this.powerCompanyAccount = meterParams[3]; + } +} diff --git a/examples/cactus-example-tcs-huawei/MeterManagement.ts b/examples/cactus-example-tcs-huawei/MeterManagement.ts new file mode 100644 index 0000000000..89b4c9bc2c --- /dev/null +++ b/examples/cactus-example-tcs-huawei/MeterManagement.ts @@ -0,0 +1,304 @@ +/* + * Copyright 2021 Hyperledger Cactus Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * MeterManagement.ts + */ + +import { MeterInfo } from "./MeterInfo"; +import { ConfigUtil } from "@hyperledger/cactus-cmd-socketio-server"; +import fs from "fs" +import yaml from "js-yaml" +const config: any = ConfigUtil.getConfig(); +import { getLogger } from "log4js"; +const moduleName = "MeterManagement"; +const logger = getLogger(`${moduleName}`); +logger.level = config.logLevel; + +// Meter Management Class +export class MeterManagement { + fileName = "MeterInfo.json"; + + constructor() {} + + // For debugging + fileDump() { + const confirmData: string = fs.readFileSync(this.fileName, "utf8"); + const arrayData: MeterInfo[] = JSON.parse(confirmData).table as MeterInfo[]; + logger.debug(arrayData); + } + + addMeterInfo(addMeterInfo: MeterInfo): object { + // Existence check of table file + try { + fs.statSync(this.fileName); + } catch (err) { + // Creating an empty table file + const emptyTable = { + table: [], + }; + const emptyTableJson: string = JSON.stringify(emptyTable); + fs.writeFileSync(this.fileName, emptyTableJson, "utf8"); + } + + // Read table file + const meterInfoFileData: string = fs.readFileSync(this.fileName, "utf8"); + const meterInfoTable: string[] = JSON.parse(meterInfoFileData) + .table as string[]; + + // Search target records / replace data + const meterInfoTableNew = { + table: [], + }; + let existFlag = false; + let action = ""; + meterInfoTable.forEach((meterInfoJSON, index) => { + const meterInfo: MeterInfo = JSON.parse(meterInfoJSON) as MeterInfo; + + // Determine if it is a target record + if (meterInfo.meterID === addMeterInfo.meterID) { + // Change Status + meterInfo.bankAccount = addMeterInfo.bankAccount; + meterInfo.bankAccountPKey = addMeterInfo.bankAccountPKey; + meterInfo.powerCompanyAccount = addMeterInfo.powerCompanyAccount; + existFlag = true; + action = "update"; + } + + // Register Record + const meterInfoNewJson: string = JSON.stringify(meterInfo); + // logger.debug(`meter info = ${meterInfoNewJson}`); + meterInfoTableNew.table.push(meterInfoNewJson); + }); + if (existFlag === false) { + const addMeterInfoJson: string = JSON.stringify(addMeterInfo); + logger.debug(`add meter info = ${addMeterInfoJson}`); + meterInfoTableNew.table.push(addMeterInfoJson); + action = "add"; + } + + // Table File Write + const meterInfoTableNewJson: string = JSON.stringify(meterInfoTableNew); + fs.writeFileSync(this.fileName, meterInfoTableNewJson, "utf8"); + + // this.fileDump(); + + const result = { + action: action, + meterID: addMeterInfo.meterID, + }; + return result; + } + + getMeterInfo(meterID: string): MeterInfo { + // Existence check of table file + try { + fs.statSync(this.fileName); + } catch (err) { + throw err; + } + + // Read table file + const meterInfoFileData: string = fs.readFileSync(this.fileName, "utf8"); + const meterInfoTable: string[] = JSON.parse(meterInfoFileData) + .table as string[]; + + // Search target records + let retMeterInfo: MeterInfo | null = null; + meterInfoTable.forEach((meterInfoJSON, index) => { + const meterInfo: MeterInfo = JSON.parse(meterInfoJSON) as MeterInfo; + + // Determine if it is a target record + if (meterInfo.meterID === meterID) { + retMeterInfo = meterInfo; + return; + } + }); + + return retMeterInfo; + } + + /* + setStatus(tradeInfo: TradeInfo, status: CartradeStatus) { + + // Existence check of table file + try { + fs.statSync(this.fileName); + } catch (err) { + throw err; + } + + // Read table file + const fileData: string = fs.readFileSync(this.fileName, 'utf8'); + const transactionInfoTable: string[] = JSON.parse(fileData).table as string[]; + + // Search target records / replace data + const transactionInfoTableNew = { + table: [] + }; + transactionInfoTable.forEach((transactionInfoJSON, index) => { + const transactionInfo: TransactionInfo = JSON.parse(transactionInfoJSON) as TransactionInfo; + + // Determine if it is a target record + if (transactionInfo.businessLogicID === tradeInfo.businessLogicID && transactionInfo.tradeID === tradeInfo.tradeID) { + // Change Status + transactionInfo.status = status; + } + + // Register Record + const transactionInfoJson: string = JSON.stringify(transactionInfo); + transactionInfoTableNew.table.push(transactionInfoJson); + }); + + // Table File Write + const transactionInfoTableNewJson: string = JSON.stringify(transactionInfoTableNew); + fs.writeFileSync(this.fileName, transactionInfoTableNewJson, 'utf8'); + + this.fileDump(); + } + + setTransactionData(tradeInfo: TradeInfo, transactionData: TransactionData) { + + // Existence check of table file + try { + fs.statSync(this.fileName); + } catch (err) { + throw err; + } + + // Read table file + const fileData: string = fs.readFileSync(this.fileName, 'utf8'); + const transactionInfoTable: string[] = JSON.parse(fileData).table as string[]; + + // Search target records / replace data + const transactionInfoTableNew = { + table: [] + }; + transactionInfoTable.forEach((transactionInfoJSON, index) => { + const transactionInfo: TransactionInfo = JSON.parse(transactionInfoJSON) as TransactionInfo; + + // Determine if it is a target record + if (transactionInfo.businessLogicID === tradeInfo.businessLogicID && transactionInfo.tradeID === tradeInfo.tradeID) { + + // Determine if it is the target transaction + if (transactionData.target === "escrow") { + // escrow: dataset + transactionInfo.escrowLedger = transactionData.ledger; + transactionInfo.escrowTxID = transactionData.txID; + } + else if (transactionData.target === "transfer") { + // transfer: dataset + transactionInfo.transferLedger = transactionData.ledger; + transactionInfo.transferTxID = transactionData.txID; + } + else if (transactionData.target === "settlement") { + // settlement: dataset + transactionInfo.settlementLedger = transactionData.ledger; + transactionInfo.settlementTxID = transactionData.txID; + } + + } + + // Register Record + const transactionInfoJson: string = JSON.stringify(transactionInfo); + transactionInfoTableNew.table.push(transactionInfoJson); + }); + + // Table File Write + const transactionInfoTableNewJson: string = JSON.stringify(transactionInfoTableNew); + fs.writeFileSync(this.fileName, transactionInfoTableNewJson, 'utf8'); + + this.fileDump(); + } + + setTxInfo(tradeInfo: TradeInfo, txInfoData: TxInfoData) { + + // Existence check of table file + try { + fs.statSync(this.fileName); + } catch (err) { + throw err; + } + + // Read table file + const fileData: string = fs.readFileSync(this.fileName, 'utf8'); + const transactionInfoTable: string[] = JSON.parse(fileData).table as string[]; + + // Search target records / replace data + const transactionInfoTableNew = { + table: [] + }; + transactionInfoTable.forEach((transactionInfoJSON, index) => { + const transactionInfo: TransactionInfo = JSON.parse(transactionInfoJSON) as TransactionInfo; + + // Determine if it is a target record + if (transactionInfo.businessLogicID === tradeInfo.businessLogicID && transactionInfo.tradeID === tradeInfo.tradeID) { + + // Determine if it is the target transaction + if (txInfoData.target === "escrow") { + // escrow: dataset + transactionInfo.escrowTxInfo = txInfoData.txInfo; + } + else if (txInfoData.target === "transfer") { + // transfer: dataset + transactionInfo.transferTxInfo = txInfoData.txInfo; + } + else if (txInfoData.target === "settlement") { + // settlement: dataset + transactionInfo.settlementTxInfo = txInfoData.txInfo; + } + + } + + // Register Record + const transactionInfoJson: string = JSON.stringify(transactionInfo); + transactionInfoTableNew.table.push(transactionInfoJson); + }); + + // Table File Write + const transactionInfoTableNewJson: string = JSON.stringify(transactionInfoTableNew); + fs.writeFileSync(this.fileName, transactionInfoTableNewJson, 'utf8'); + + this.fileDump(); + } +*/ + /** + * Get transaction data corresponding to the specified txId. + * (*Return if any of escrowTxID, transferTxID, settlementTxID matches txId) + * + * @return Transaction data corresponding to txId. Returns null if it does not exist. + * + */ + /* + getTransactionInfoByTxId(txId: string): TransactionInfo { + + // Existence check of table file + try { + fs.statSync(this.fileName); + } catch (err) { + throw err; + } + + // Read table file + const fileData: string = fs.readFileSync(this.fileName, 'utf8'); + const transactionInfoTable: string[] = JSON.parse(fileData).table as string[]; + + // Search target records + const transactionInfoTableNew = { + table: [] + }; + let retTransactionInfo: TransactionInfo | null = null; + transactionInfoTable.forEach((transactionInfoJSON, index) => { + const transactionInfo: TransactionInfo = JSON.parse(transactionInfoJSON) as TransactionInfo; + + // Determine if it is a target record + if (transactionInfo.escrowTxID === txId || transactionInfo.transferTxID === txId || transactionInfo.settlementTxID === txId) { + retTransactionInfo = transactionInfo; + return; + } + }); + + return retTransactionInfo; + } +*/ +} diff --git a/examples/cactus-example-tcs-huawei/README.md b/examples/cactus-example-tcs-huawei/README.md new file mode 100644 index 0000000000..40b52f8671 --- /dev/null +++ b/examples/cactus-example-tcs-huawei/README.md @@ -0,0 +1,13 @@ +# Cactus example-tcs-huawei + +## Abstract + +Cactus **example-tcs-huawei** is a sample application for exchanging electricity .It starts in a similar way to cactus-example-electricity-trade. + +![electricity-trade image](./images/electricity-trade-image.png) + +## Required software components +- OS: Linux (recommend: Ubuntu18.04 or CentOS7) +- Docker (recommend: v17.06.2-ce or greater) +- Docker-compose (recommend: v1.14.0 or greater) +- node.js v16 (recommend: v16 or greater) diff --git a/examples/cactus-example-tcs-huawei/RequestInfo.ts b/examples/cactus-example-tcs-huawei/RequestInfo.ts new file mode 100644 index 0000000000..8753de81a9 --- /dev/null +++ b/examples/cactus-example-tcs-huawei/RequestInfo.ts @@ -0,0 +1,33 @@ +/* + * Copyright 2021 Hyperledger Cactus Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * RequestInfo.ts + */ + +// transaction information +class TradeInfo { + ethereumAccountA: string; + ethereumAccountB: string; +} + +// authorization information +class AuthInfo { + company: string; +} + +// request information +export class RequestInfo { + businessLogicID: string; + tradeID: string; + tradeInfo: TradeInfo; + authInfo: AuthInfo; + constructor() { + this.tradeInfo = new TradeInfo(); + this.authInfo = new AuthInfo(); + } + + setTradeID(tradeID: string) { + this.tradeID = tradeID; + } +} diff --git a/examples/cactus-example-tcs-huawei/TransactionEthereum.ts b/examples/cactus-example-tcs-huawei/TransactionEthereum.ts new file mode 100644 index 0000000000..8e63a04c5c --- /dev/null +++ b/examples/cactus-example-tcs-huawei/TransactionEthereum.ts @@ -0,0 +1,163 @@ +/* + * Copyright 2021 Hyperledger Cactus Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * TransactionEthereum.ts + */ + +import { + LPInfoHolder, + TransactionSigner, + ConfigUtil, +} from "@hyperledger/cactus-cmd-socketio-server"; + +import { + VerifierFactory, + VerifierFactoryConfig, +} from "@hyperledger/cactus-verifier-client"; + + +import fs from "fs"; +import yaml from "js-yaml"; +//const config: any = JSON.parse(fs.readFileSync("/etc/cactus/default.json", 'utf8')); +const config: any = ConfigUtil.getConfig(); +import { getLogger } from "log4js"; +const moduleName = "TransactionEthereum"; +const logger = getLogger(`${moduleName}`); +logger.level = config.logLevel; + +const mapFromAddressNonce: Map = new Map(); +let xConnectInfo: LPInfoHolder = null; // connection information +let xVerifierFactory: VerifierFactory = null; + +export function makeRawTransaction(txParam: { + fromAddress: string; + fromAddressPkey: string; + toAddress: string; + amount: number; + gas: number; +}): Promise<{ data: {}; txId: string }> { + return new Promise(async (resolve, reject) => { + try { + logger.debug(`makeRawTransaction: txParam: ${JSON.stringify(txParam)}`); + + getNewNonce(txParam.fromAddress).then((result) => { + logger.debug( + `##makeRawTransaction(A): result: ${JSON.stringify(result)}`, + ); + + const txnCountHex: string = result.txnCountHex; + + const rawTx: { nonce: string; to: string; value: number; gas: number } = + { + nonce: txnCountHex, + to: txParam.toAddress, + value: txParam.amount, + gas: txParam.gas, + }; + logger.debug( + `##makeRawTransaction(B), rawTx: ${JSON.stringify(rawTx)}`, + ); + + const signedTx = TransactionSigner.signTxEthereum( + rawTx, + txParam.fromAddressPkey, + ); + const resp: { data: {}; txId: string } = { + data: { serializedTx: signedTx["serializedTx"] }, + txId: signedTx["txId"], + }; + + return resolve(resp); + }); + } catch (err) { + logger.error(err); + return reject(err); + } + }); +} + +function getNewNonce(fromAddress: string): Promise<{ txnCountHex: string }> { + return new Promise(async (resolve, reject) => { + try { + logger.debug(`getNewNonce start: fromAddress: ${fromAddress}`); + + if (xConnectInfo === null) { + xConnectInfo = new LPInfoHolder(); + } + + if (xVerifierFactory === null) { + logger.debug("create verifier factory"); + xVerifierFactory = new VerifierFactory( + xConnectInfo.ledgerPluginInfo as VerifierFactoryConfig, + config.logLevel, + ); + } + + // Get the number of transactions in account + const contract = {}; // NOTE: Since contract does not need to be specified, specify an empty object. + const method = { type: "function", command: "getNonce" }; + const template = "default"; + const args = { args: { args: [fromAddress] } }; + + logger.debug(`##getNewNonce(A): call validator#getNonce()`); + xVerifierFactory + .getVerifier("84jUisrs") + .sendSyncRequest(contract, method, args) + .then((result) => { + // logger.debug(`##getNewNonce(A): result: ${JSON.stringify(result)}`); + + let txnCount: number = result.data.nonce; + let txnCountHex: string = result.data.nonceHex; + + const latestNonce = getLatestNonce(fromAddress); + // logger.debug(`##getNewNonce(B): fromAddress: ${fromAddress}, txnCount: ${txnCount}, latestNonce: ${latestNonce}`); + if (txnCount <= latestNonce) { + // nonce correction + txnCount = latestNonce + 1; + logger.debug( + `##getNewNonce(C): Adjust txnCount, fromAddress: ${fromAddress}, txnCount: ${txnCount}, latestNonce: ${latestNonce}`, + ); + + const method = { type: "function", command: "toHex" }; + const args = { args: { args: [txnCount] } }; + + logger.debug(`##getNewNonce(D): call validator#toHex()`); + xVerifierFactory + .getVerifier("84jUisrs") + .sendSyncRequest(contract, method, args) + .then((result) => { + txnCountHex = result.data.hexStr; + logger.debug(`##getNewNonce(E): txnCountHex: ${txnCountHex}`); + + // logger.debug(`##getNewNonce(F) _nonce: ${txnCount}, latestNonce: ${latestNonce}`); + setLatestNonce(fromAddress, txnCount); + + return resolve({ txnCountHex: txnCountHex }); + }); + } else { + // logger.debug(`##getNewNonce(F) _nonce: ${txnCount}, latestNonce: ${latestNonce}`); + setLatestNonce(fromAddress, txnCount); + + logger.debug(`##getNewNonce(G): txnCountHex: ${txnCountHex}`); + return resolve({ txnCountHex: txnCountHex }); + } + }); + } catch (err) { + logger.error(err); + return reject(err); + } + }); +} + +function getLatestNonce(fromAddress: string): number { + if (mapFromAddressNonce.has(fromAddress)) { + return mapFromAddressNonce.get(fromAddress); + } + //return 0; + return -1; +} + +function setLatestNonce(fromAddress: string, nonce: number): void { + mapFromAddressNonce.set(fromAddress, nonce); +} diff --git a/examples/cactus-example-tcs-huawei/balance.ts b/examples/cactus-example-tcs-huawei/balance.ts new file mode 100644 index 0000000000..79d87dfe66 --- /dev/null +++ b/examples/cactus-example-tcs-huawei/balance.ts @@ -0,0 +1,49 @@ +/* + * Copyright 2020-2021 Hyperledger Cactus Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * balance.ts + */ + +import { Router, NextFunction, Request, Response } from "express"; +import { ConfigUtil, RIFError } from "@hyperledger/cactus-cmd-socketio-server"; +import { BalanceManagement } from "./BalanceManagement"; + +const config: any = ConfigUtil.getConfig(); +import { getLogger } from "log4js"; +const moduleName = "balance"; +const logger = getLogger(`${moduleName}`); +logger.level = config.logLevel; + +const router: Router = Router(); +const balanceManagement: BalanceManagement = new BalanceManagement(); + +/* GET balance. */ +router.get("/:account", (req: Request, res: Response, next: NextFunction) => { + try { + balanceManagement + .getBalance(req.params.account) + .then((result) => { + logger.debug(`#####[sample/balance.ts]`); + logger.debug("result(getBalance) = " + JSON.stringify(result)); + res.status(200).json(result); + }) + .catch((err) => { + logger.error(err); + }); + } catch (err) { + logger.error(`##err name: ${err.constructor.name}`); + + if (err instanceof RIFError) { + logger.debug(`##catch RIFError, ${err.statusCode}, ${err.message}`); + res.status(err.statusCode); + res.send(err.message); + return; + } + + logger.error(`##err in balance: ${err}`); + next(err); + } +}); + +export default router; diff --git a/examples/cactus-example-tcs-huawei/config/contractInfo.yaml b/examples/cactus-example-tcs-huawei/config/contractInfo.yaml new file mode 100644 index 0000000000..5bcb340a4f --- /dev/null +++ b/examples/cactus-example-tcs-huawei/config/contractInfo.yaml @@ -0,0 +1 @@ +contractInfo: [] \ No newline at end of file diff --git a/examples/cactus-example-tcs-huawei/config/default.yaml b/examples/cactus-example-tcs-huawei/config/default.yaml new file mode 100644 index 0000000000..8b79c03caf --- /dev/null +++ b/examples/cactus-example-tcs-huawei/config/default.yaml @@ -0,0 +1,28 @@ +# Default cactus-cmd-socketio-server configuration +# Do not change this, update the settings in usersetting.yaml +# Check examples/cactus-example-discounted-asset-trade/config for working example + +# List of Bussiness Logic Plugin and it's validators +blpRegistry: [] + +# log4js log levels used by cactus-cmd-socketio-server and smaple BLPs +logLevel: info + +# Connection info +applicationHostInfo: + hostName: 0.0.0.0 + hostPort: 5034 + +# Socket.IO Manager options +socketOptions: + rejectUnauthorized: false + reconnection: false + timeout: 20000 + +# Verifier component configuration +verifier: + maxCounterRequestID: 100 + syncFunctionTimeoutMillisecond: 5000 + +# API route path and JS file to handle that route logic +appRouters: [] diff --git a/examples/cactus-example-tcs-huawei/config/usersetting.yaml b/examples/cactus-example-tcs-huawei/config/usersetting.yaml new file mode 100644 index 0000000000..730b4fd332 --- /dev/null +++ b/examples/cactus-example-tcs-huawei/config/usersetting.yaml @@ -0,0 +1,30 @@ +# Overwrite defaults +blpRegistry: + - + businessLogicID: h40Q9eMD + validatorID: [sUr7dZly, 84jUisrs] + +logLevel: debug + +appRouters: + - + path: /api/v1/bl/electricity-trade/ + routerJs: /root/cactus/dist/electricity-trade.js + - + path: /api/v1/bl/balance/ + routerJs: /root/cactus/dist/balance.js + +# BLP Config +electricityTradeInfo: + tcsHuawei: + validatorID: sUr7dZly + filterKey: intkey + ethereum: + validatorID: 84jUisrs + gethURL: http://localhost:8545 + chainName: geth1 + networkID: 10 + chainID: 10 + network: mainnet + hardfork: petersburg + gas: 21000 diff --git a/examples/cactus-example-tcs-huawei/config/validator-registry-config.yaml b/examples/cactus-example-tcs-huawei/config/validator-registry-config.yaml new file mode 100644 index 0000000000..c939a4b6a0 --- /dev/null +++ b/examples/cactus-example-tcs-huawei/config/validator-registry-config.yaml @@ -0,0 +1,62 @@ +ledgerPluginInfo: + - + validatorID: 84jUisrs + validatorType: legacy-socketio + validatorURL: https://ethereum-validator:5050 + validatorKeyPath: /etc/cactus/connector-go-ethereum-socketio/CA/connector.crt + maxCounterRequestID: 100 + syncFunctionTimeoutMillisecond: 5000 + socketOptions: + rejectUnauthorized: false + reconnection: false + timeout: 20000 + ledgerInfo: + ledgerAbstract: Go-Ethereum Ledger + apiInfo: + - + apiType: getNumericBalance + requestedData: + - + dataName: referedAddress + dataType: string + - + apiType: transferNumericAsset + requestedData: + - + dataName: fromAddress + dataType: string + - + dataName: toAddress + dataType: string + - + dataName: amount + dataType: number + - + apiType: sendRawTransaction + requestedData: + - + dataName: serializedTx + dataType: string + + - + validatorID: sUr7dZly + validatorType: legacy-socketio + validatorURL: https://tcs-validator:5140 + validatorKeyPath: /etc/cactus/connector-tcs-huawei/CA/connector.crt + maxCounterRequestID: 100 + syncFunctionTimeoutMillisecond: 5000 + socketOptions: + rejectUnauthorized: false + reconnection: false + timeout: 20000 + ledgerInfo: + ledgerAbstract: Sawtooth Ledger + apiInfo: [] + +signTxInfo: + ethereum: + chainName: geth1 + networkID: 10 + chainID: 10 + network: mainnet + hardfork: petersburg diff --git a/examples/cactus-example-tcs-huawei/docker-compose.debug.yml b/examples/cactus-example-tcs-huawei/docker-compose.debug.yml new file mode 100644 index 0000000000..bd8f1b63a9 --- /dev/null +++ b/examples/cactus-example-tcs-huawei/docker-compose.debug.yml @@ -0,0 +1,9 @@ +version: "3.4" + +# Use: docker-compose -f docker-compose.yml -f docker-compose.debug.yml up + +services: + cactus-example-electricity-trade-blp: + ports: + - "9034:9034" + command: node --inspect=0.0.0.0:9034 ./dist/www.js diff --git a/examples/cactus-example-tcs-huawei/docker-compose.yml b/examples/cactus-example-tcs-huawei/docker-compose.yml new file mode 100644 index 0000000000..8414e7ff26 --- /dev/null +++ b/examples/cactus-example-tcs-huawei/docker-compose.yml @@ -0,0 +1,69 @@ +version: "3.4" + +# Start the ledgers first +# ./etc/cactus should already exist and contain cactus node configs + +services: + ethereum-validator: + container_name: cactus-example-electricity-trade-ethereum-validator + image: cactus-plugin-ledger-connector-go-ethereum-socketio + build: + context: ../../packages/cactus-plugin-ledger-connector-go-ethereum-socketio/ + ports: + - "5050:5050" + networks: + - geth1net + - cactus-example-electricity-trade-net + volumes: + - type: bind + source: ./etc/cactus + target: /etc/cactus + + tcs-validator: + container_name: cactus-example-electricity-trade-tcs-validator + image: cactus-plugin-ledger-connector-tcs + build: + context: ../../packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/ + ports: + - "5140:5140" + networks: + - example_tcs_huawei_testnet_1x + - cactus-example-electricity-trade-net + volumes: + - type: bind + source: ./etc/cactus + target: /etc/cactus + + cmd-socketio-base-image: + # Build base image and immediately exit + container_name: cmd-socketio-base-dummy + image: cactus-cmd-socketio-server + build: + context: ../../packages/cactus-cmd-socketio-server/ + command: ["echo", "OK - Exit"] + + cactus-example-electricity-trade-blp: + container_name: cactus-example-electricity-trade-blp + image: cactus-example-electricity-trade-blp + build: + context: . + ports: + - "5034:5034" + networks: + - cactus-example-electricity-trade-net + depends_on: + - ethereum-validator + - tcs-validator + - cmd-socketio-base-image + volumes: + - type: bind + source: ./etc/cactus + target: /etc/cactus + +networks: + example_tcs_huawei_testnet_1x: + external: true + geth1net: + external: true + cactus-example-electricity-trade-net: + driver: bridge diff --git a/examples/cactus-example-tcs-huawei/electricity-trade.ts b/examples/cactus-example-tcs-huawei/electricity-trade.ts new file mode 100644 index 0000000000..5fc73c4703 --- /dev/null +++ b/examples/cactus-example-tcs-huawei/electricity-trade.ts @@ -0,0 +1,75 @@ +/* + * Copyright 2021 Hyperledger Cactus Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * electricity-trade.ts + */ + +import { Router, NextFunction, Request, Response } from "express"; +import { + TransactionManagement, + RIFError, + ConfigUtil, +} from "@hyperledger/cactus-cmd-socketio-server"; +import escapeHtml from "escape-html"; + +const config: any = ConfigUtil.getConfig(); +import { getLogger } from "log4js"; +const moduleName = "trades"; +const logger = getLogger(`${moduleName}`); +logger.level = config.logLevel; + +const router: Router = Router(); +export const transactionManagement: TransactionManagement = + new TransactionManagement(); + +// Request Execution of Trade +router.post("/", (req: Request, res: Response, next: NextFunction) => { + try { + logger.warn("pre startBusinessLogic ") + const tradeID = transactionManagement.startBusinessLogic(req); + if (!tradeID) { + throw new RIFError(`Could not run BLP, tradeId = ${tradeID}`); + } + const result = { tradeID: tradeID }; + res.status(200).json(result); + } catch (err) { + if (err instanceof RIFError) { + res.status(err.statusCode); + res.send(escapeHtml(err.message)); + return; + } + + next(err); + } +}); + +// Request Execution of Trade +router.post( + "/meter/register/", + (req: Request, res: Response, next: NextFunction) => { + try { + const result = transactionManagement.setBusinessLogicConfig(req); + + if (!result) { + throw new RIFError("Error when running setBusinessLogicConfig"); + } + + let status = 200; + if (result["action"] === "add") { + status = 201; + } + res.status(status).json(result); + } catch (err) { + if (err instanceof RIFError) { + res.status(err.statusCode); + res.send(escapeHtml(err.message)); + return; + } + + next(err); + } + } +); + +export default router; diff --git a/examples/cactus-example-tcs-huawei/images/electricity-trade-image.png b/examples/cactus-example-tcs-huawei/images/electricity-trade-image.png new file mode 100644 index 0000000000..4fa9881524 Binary files /dev/null and b/examples/cactus-example-tcs-huawei/images/electricity-trade-image.png differ diff --git a/examples/cactus-example-tcs-huawei/images/electricity-trade-image.pptx b/examples/cactus-example-tcs-huawei/images/electricity-trade-image.pptx new file mode 100644 index 0000000000..c7cc25a4c4 Binary files /dev/null and b/examples/cactus-example-tcs-huawei/images/electricity-trade-image.pptx differ diff --git a/examples/cactus-example-tcs-huawei/package.json b/examples/cactus-example-tcs-huawei/package.json new file mode 100644 index 0000000000..1f119e97e1 --- /dev/null +++ b/examples/cactus-example-tcs-huawei/package.json @@ -0,0 +1,46 @@ +{ + "name": "@hyperledger/cactus-example-tcs-huawei", + "version": "2.0.0-alpha.1", + "license": "Apache-2.0", + "main": "dist/www.js", + "module": "dist/www.js", + "types": "dist/www.d.ts", + "private": false, + "scripts": { + "start": "docker-compose build && docker-compose up", + "build": "npm run build-ts && npm run build:dev:backend:postbuild", + "build-ts": "tsc", + "build:dev:backend:postbuild": "cp -f ../../yarn.lock ./dist/" + }, + "dependencies": { + "@types/node": "14.18.12", + "body-parser": "1.19.2", + "cookie-parser": "1.4.6", + "debug": "2.6.9", + "escape-html": "1.0.3", + "ethereumjs-common": "1.5.2", + "ethereumjs-tx": "2.1.2", + "express": "4.16.4", + "fabric-ca-client": "2.2.10", + "fabric-network": "2.2.10", + "http-errors": "1.6.3", + "js-yaml": "3.14.1", + "jsonwebtoken": "8.5.1", + "log4js": "6.4.0", + "morgan": "1.9.1", + "shelljs": "0.8.5", + "socket.io": "4.4.1", + "ts-node": "8.9.1", + "web3": "1.7.0", + "xmlhttprequest": "1.8.0" + }, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "4.33.0", + "@typescript-eslint/parser": "4.33.0", + "@types/escape-html": "1.0.1", + "eslint": "7.32.0", + "eslint-config-prettier": "8.4.0", + "eslint-plugin-prettier": "4.0.0", + "prettier": "2.5.1" + } +} diff --git a/examples/cactus-example-tcs-huawei/script-cleanup.sh b/examples/cactus-example-tcs-huawei/script-cleanup.sh new file mode 100644 index 0000000000..e8f02d166f --- /dev/null +++ b/examples/cactus-example-tcs-huawei/script-cleanup.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# Copyright 2020-2022 Hyperledger Cactus Contributors +# SPDX-License-Identifier: Apache-2.0 + +ROOT_DIR="../.." # Path to cactus root dir + +echo ">> Remove the config files on your machine" +rm -rf ./etc/cactus/ + +# pushd ${ROOT_DIR}/tools/docker/tcs-huawei-testnet/example +# docker-compose down & +# popd + +echo ">> Stop and remove the docker containers" +docker rm -f geth1 \ + cactus-example-electricity-trade-blp \ + cactus-example-electricity-trade-ethereum-validator \ + cactus-example-electricity-trade-tcs-validator \ + cmd-socketio-base-dummy \ + agent + +echo ">> Remove docker networks" +docker network rm tcs_huawei_testnet_1x \ + cactus-example-tcs_default \ + cactus-example-tcs_cactus-example-electricity-trade-net \ + geth1net \ + geth-testnet_default + + +echo "Cleanup done." diff --git a/examples/cactus-example-tcs-huawei/script-gen-electricity-usage.sh b/examples/cactus-example-tcs-huawei/script-gen-electricity-usage.sh new file mode 100644 index 0000000000..c6427592a1 --- /dev/null +++ b/examples/cactus-example-tcs-huawei/script-gen-electricity-usage.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# Copyright 2020-2022 Hyperledger Cactus Contributors +# SPDX-License-Identifier: Apache-2.0 + +# echo "# Create intkey representing electricity usage" +# docker exec -t sawtooth_all_in_one_ledger_1x shell intkey set MI000001 50 --url http://rest-api:8008 + +# echo "# Increase usage" +# docker exec -t sawtooth_all_in_one_ledger_1x shell intkey inc MI000001 24 --url http://rest-api:8008 + +curl --location -k --request POST 'https://127.0.0.1:8001/v1/cross/transaction/query' \ +--header 'Content-Type: text/plain' --data '{ + "to_chain":"B01234567890123456789012345678901234567890123456789", + "from_chaincode_id":"ExcuteChainCode", + "to_chaincode_id":"ExcuteChainCode", + "to_query_func_name":"set", + "args":["MI000001", "50"] + }' --cert ../../packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/sample-config/cert.pem --key ../../packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/sample-config/key.pem + +curl --location -k --request POST 'https://127.0.0.1:8001/v1/cross/transaction/query' \ +--header 'Content-Type: text/plain' --data '{ + "to_chain":"B01234567890123456789012345678901234567890123456789", + "from_chaincode_id":"ExcuteChainCode", + "to_chaincode_id":"ExcuteChainCode", + "to_query_func_name":"inc", + "args":["MI000001", "24"] + }' --cert ../../packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/sample-config/cert.pem --key ../../packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/sample-config/key.pem \ No newline at end of file diff --git a/examples/cactus-example-tcs-huawei/script-get-app.sh b/examples/cactus-example-tcs-huawei/script-get-app.sh new file mode 100644 index 0000000000..f1873bed1d --- /dev/null +++ b/examples/cactus-example-tcs-huawei/script-get-app.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +# Copyright 2020-2022 Hyperledger Cactus Contributors +# SPDX-License-Identifier: Apache-2.0 + +echo "# Source Eth balance:" +curl localhost:5034/api/v1/bl/balance/06fc56347d91c6ad2dae0c3ba38eb12ab0d72e97 + +echo -e "\n\n# Destination Eth balance:" +curl localhost:5034/api/v1/bl/balance/9d624f7995e8bd70251f8265f2f9f2b49f169c55 + +echo -e "\n\n# Electricity usage" +# docker exec -t sawtooth_all_in_one_ledger_1x shell intkey list --url http://rest-api:8008 diff --git a/examples/cactus-example-tcs-huawei/script-post-setup-request.sh b/examples/cactus-example-tcs-huawei/script-post-setup-request.sh new file mode 100644 index 0000000000..7851c4be4b --- /dev/null +++ b/examples/cactus-example-tcs-huawei/script-post-setup-request.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +# Copyright 2020-2022 Hyperledger Cactus Contributors +# SPDX-License-Identifier: Apache-2.0 + +echo "Register electricity-trade account information" +curl localhost:5034/api/v1/bl/electricity-trade/meter/register/ -XPOST \ + -H "Content-Type: application/json" \ + -d '{"businessLogicID":"h40Q9eMD", + "meterParams":[ + "MI000001", + "06fc56347d91c6ad2dae0c3ba38eb12ab0d72e97", + "cb5d48d371916a4ea1627189d8af4f642a5d72746a06b559780c3f5932658207", + "9d624f7995e8bd70251f8265f2f9f2b49f169c55" + ] + }' diff --git a/examples/cactus-example-tcs-huawei/script-post-start-request.sh b/examples/cactus-example-tcs-huawei/script-post-start-request.sh new file mode 100644 index 0000000000..99c084df76 --- /dev/null +++ b/examples/cactus-example-tcs-huawei/script-post-start-request.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +# Copyright 2020-2022 Hyperledger Cactus Contributors +# SPDX-License-Identifier: Apache-2.0 + +echo "Start electricity-trade" +curl localhost:5034/api/v1/bl/electricity-trade/ -XPOST -H "Content-Type: application/json" -d '{"businessLogicID":"h40Q9eMD"}' diff --git a/examples/cactus-example-tcs-huawei/script-start-ledgers.sh b/examples/cactus-example-tcs-huawei/script-start-ledgers.sh new file mode 100644 index 0000000000..e9fb702ae2 --- /dev/null +++ b/examples/cactus-example-tcs-huawei/script-start-ledgers.sh @@ -0,0 +1,130 @@ +#!/usr/bin/env bash +# Copyright 2020-2022 Hyperledger Cactus Contributors +# SPDX-License-Identifier: Apache-2.0 + +set -e + +ROOT_DIR="../.." # Path to cactus root dir +CONFIG_VOLUME_PATH="./etc/cactus" # Docker volume with shared configuration +WAIT_TIME=10 # How often to check container status + +# Cert options +CERT_CURVE_NAME="prime256v1" +CERT_COUNTRY="JP" +CERT_STATE="Tokyo" +CERT_LOCALITY="Minato-Ku" +CERT_ORG="CactusSamples" + +# generate_certificate +function generate_certificate() { + # Check OpenSSL command existance + if ! openssl version > /dev/null; then + echo "Could not execute [openssl version], check if OpenSSL tool is available on the system." + exit 1; + fi + + # Check input parameters + ARGS_NUMBER=2 + if [ "$#" -lt "$ARGS_NUMBER" ]; then + echo "generate_certificate called with wrong number of arguments (expected - $ARGS_NUMBER, actual - $#)"; + exit 2 + fi + + common_name=$1 + destination=$2 + subject="/C=$CERT_COUNTRY/ST=$CERT_STATE/L=$CERT_LOCALITY/O=$CERT_ORG/CN=$common_name" + echo "Create new cert in '${destination}' with subject '${subject}'" + + # Crete destination path + if [ ! -d "$destination" ]; then + echo "Re-create destination dir..." + rm -rf "$destination" + mkdir -p "$destination" + fi + + keyPath="${destination}/connector.priv" + csrPath="${destination}/connector.csr" + certPath="${destination}/connector.crt" + + # Generate keys + openssl ecparam -genkey -name "$CERT_CURVE_NAME" -out "$keyPath" + openssl req -new -sha256 -key "$keyPath" -out "$csrPath" -subj "$subject" + openssl req -x509 -sha256 -days 365 -key "$keyPath" -in "$csrPath" -out "$certPath" +} + +function start_ethereum_testnet() { + pushd "${ROOT_DIR}/tools/docker/geth-testnet" + ./script-start-docker.sh + popd +} + +function copy_ethereum_validator_config() { + echo ">> copy_ethereum_validator_config()" + cp -fr ${ROOT_DIR}/packages/cactus-plugin-ledger-connector-go-ethereum-socketio/sample-config/* \ + "${CONFIG_VOLUME_PATH}/connector-go-ethereum-socketio/" + generate_certificate "GoEthereumCactusValidator" "${CONFIG_VOLUME_PATH}/connector-go-ethereum-socketio/CA/" + echo ">> copy_ethereum_validator_config() done." +} + +function copy_tcs_config() { + cp -fr ${ROOT_DIR}/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/sample-config/* \ + "${CONFIG_VOLUME_PATH}/connector-tcs-huawei/" + generate_certificate "TcsCactusValidator" "${CONFIG_VOLUME_PATH}/connector-tcs-huawei/CA/" +} + +function start_tcs_agent(){ + echo ">> start_tcs_agent()" + docker network create tcs_huawei_testnet_1x + pushd ${ROOT_DIR}/tools/docker/tcs-huawei-testnet/agent/example + docker-compose up > tcs-agent.log & + popd + echo ">> start_tcs_agent() done" +} + +function start_ledgers() { + prepare_certificate + + # Clear ./etc/cactus + mkdir -p "${CONFIG_VOLUME_PATH}/" + rm -fr ${CONFIG_VOLUME_PATH}/* + + # Copy cmd-socketio-config + cp -f ./config/*.yaml "${CONFIG_VOLUME_PATH}/" + + # Start Ethereum + mkdir -p "${CONFIG_VOLUME_PATH}/connector-go-ethereum-socketio" + start_ethereum_testnet + copy_ethereum_validator_config + + # Start tcs-agent + mkdir -p "${CONFIG_VOLUME_PATH}/connector-tcs-huawei" + start_tcs_agent + copy_tcs_config + +} + +function prepare_certificate(){ + mkdir Server_Cert + pushd ./Server_Cert + openssl genrsa -out cakey.pem 2048 + openssl req -new -key cakey.pem -out ca.csr -subj "/C=CN/ST=myprovince/L=mycity/O=myorganization/OU=mygroup/CN=myCA" + openssl x509 -req -days 365 -sha1 -extensions v3_ca -signkey cakey.pem -in ca.csr -out cacert.pem + + openssl genrsa -out key.pem 2048 + openssl req -new -key key.pem -out server.csr -subj "/C=CN/ST=myprovince/L=mycity/O=myorganization/OU=mygroup/CN=myServer" + openssl x509 -req -days 365 -sha1 -extensions v3_req -CA ./cacert.pem -CAkey ./cakey.pem -CAserial ca.srl -CAcreateserial -in server.csr -out cert.pem + + openssl genrsa -out clikey.pem 2048 + openssl req -new -key clikey.pem -out client.csr -subj "/C=CN/ST=myprovince/L=mycity/O=myorganization/OU=mygroup/CN=myClient" + openssl x509 -req -days 365 -sha1 -extensions v3_req -CA ./cacert.pem -CAkey ./cakey.pem -CAserial ./ca.srl -in client.csr -out clicert.pem + + openssl x509 -in clicert.pem -out client.crt + openssl rsa -in clikey.pem -out client.key + popd + cp ./Server_Cert/cert.pem ./Server_Cert/key.pem ${ROOT_DIR}/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/sample-config + mv ./Server_Cert ${ROOT_DIR}/tools/docker/tcs-huawei-testnet/agent/example +} + +start_ledgers + +echo "All Done." diff --git a/examples/cactus-example-tcs-huawei/tools/periodicExecuter/package.json b/examples/cactus-example-tcs-huawei/tools/periodicExecuter/package.json new file mode 100644 index 0000000000..12f02997ee --- /dev/null +++ b/examples/cactus-example-tcs-huawei/tools/periodicExecuter/package.json @@ -0,0 +1,17 @@ +{ + "name": "periodicExecuter", + "version": "0.0.1", + "private": true, + "scripts": { + "build": "npm run build-ts", + "build-ts": "tsc", + "tslint": "tslint -c tslint.json -p tsconfig.json" + }, + "dependencies": { + "@types/node": "14.18.12", + "ts-node": "9.1.1" + }, + "devDependencies": { + "typescript": "3.9.10" + } +} diff --git a/examples/cactus-example-tcs-huawei/tools/periodicExecuter/periodicExecuter.ts b/examples/cactus-example-tcs-huawei/tools/periodicExecuter/periodicExecuter.ts new file mode 100644 index 0000000000..36b33374af --- /dev/null +++ b/examples/cactus-example-tcs-huawei/tools/periodicExecuter/periodicExecuter.ts @@ -0,0 +1,75 @@ +/* + * Copyright 2021 Hyperledger Cactus Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * periodicExecuter.ts + */ + +const { execSync } = require("child_process"); + +console.log("## start periodicExecuter"); + +if (process.argv.length !== 5 && process.argv.length !== 6) { + console.log("Number of parameters is abnormal."); + process.exit(-1); +} + +const interval = parseInt(process.argv[2]); +const keyString = process.argv[3]; +const valueAdd = process.argv[4]; +const dockerExecString = "docker exec sawtooth-shell-default "; +const submitCommand = + "sawtooth batch submit -f batches.intkey --url http://rest-api:8008"; +const incCommand = + "intkey create_batch --key-name " + + keyString + + " --value-inc-rand " + + valueAdd; + +if (process.argv.length === 6) { + const valueInitial = process.argv[5]; + + const setCommand = + "intkey create_batch --key-name " + + keyString + + " --value-set " + + valueInitial; + console.log(`setCommand : ${setCommand}`); + const stdoutSet = execSync(dockerExecString + setCommand); + console.log(`setCommand stdout: ${stdoutSet.toString()}`); + + console.log(`submitCommand(set) : ${submitCommand}`); + const stdoutSetSubmit = execSync(dockerExecString + submitCommand); + console.log(`submitCommand(set) stdout: ${stdoutSetSubmit.toString()}`); +} + +const timerIdArowDown = setInterval(function () { + console.log(`incCommand : ${incCommand}`); + const stdoutInc = execSync(dockerExecString + incCommand); + console.log(`incCommand stdout: ${stdoutInc.toString()}`); + + console.log(`submitCommand(inc) : ${submitCommand}`); + const stdoutIncSubmit = execSync(dockerExecString + submitCommand); + console.log(`submitCommand(inc) stdout: ${stdoutIncSubmit.toString()}`); + + console.log(`##${getCurrentTime()} execute intkey`); +}, interval * 1000); + +function getCurrentTime(): string { + const now = new Date(); + return ( + "[" + + now.getFullYear() + + "/" + + ("0" + (now.getMonth() + 1)).slice(-2) + + "/" + + ("0" + now.getDate()).slice(-2) + + " " + + ("0" + now.getHours()).slice(-2) + + ":" + + ("0" + now.getMinutes()).slice(-2) + + ":" + + ("0" + now.getSeconds()).slice(-2) + + "]" + ); +} diff --git a/examples/cactus-example-tcs-huawei/tools/periodicExecuter/tsconfig.json b/examples/cactus-example-tcs-huawei/tools/periodicExecuter/tsconfig.json new file mode 100644 index 0000000000..a19738369b --- /dev/null +++ b/examples/cactus-example-tcs-huawei/tools/periodicExecuter/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "outDir": "./dist", + "sourceMap": false, + "declaration": false, + "moduleResolution": "node", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "target": "ES2017", + "module": "CommonJS", + "typeRoots": [ + "node_modules/@types", + "./typings" + ], + "lib": [ + "es2017", + "dom" + ] + }, + "include": [ + "./*.ts" + ] +} diff --git a/examples/cactus-example-tcs-huawei/tools/transferNumericAsset/config/default.js b/examples/cactus-example-tcs-huawei/tools/transferNumericAsset/config/default.js new file mode 100644 index 0000000000..2827d3391c --- /dev/null +++ b/examples/cactus-example-tcs-huawei/tools/transferNumericAsset/config/default.js @@ -0,0 +1,17 @@ +/* + * Copyright 2021 Hyperledger Cactus Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * default.js + */ + +module.exports = { + // URL to validator + validatorUrl: "https://localhost:5050", + gethUrl: "http://localhost:8545", + + // forCustomChain + chainName: "geth1", + networkId: 10, + chainId: 10, +}; diff --git a/examples/cactus-example-tcs-huawei/tools/transferNumericAsset/copyStaticAssets.ts b/examples/cactus-example-tcs-huawei/tools/transferNumericAsset/copyStaticAssets.ts new file mode 100644 index 0000000000..8aac76aa55 --- /dev/null +++ b/examples/cactus-example-tcs-huawei/tools/transferNumericAsset/copyStaticAssets.ts @@ -0,0 +1,8 @@ +/* + * Copyright 2021 Hyperledger Cactus Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * copyStaticAssets.ts + */ +import * as shell from "shelljs"; +shell.cp("config/default.js", "./dist/config"); diff --git a/examples/cactus-example-tcs-huawei/tools/transferNumericAsset/package.json b/examples/cactus-example-tcs-huawei/tools/transferNumericAsset/package.json new file mode 100644 index 0000000000..e9b483161c --- /dev/null +++ b/examples/cactus-example-tcs-huawei/tools/transferNumericAsset/package.json @@ -0,0 +1,23 @@ +{ + "name": "transferNumericAsset", + "version": "0.0.0", + "private": true, + "scripts": { + "build": "npm run build-ts && npm run copy-static-assets", + "build-ts": "tsc", + "tslint": "tslint -c tslint.json -p tsconfig.json", + "copy-static-assets": "ts-node copyStaticAssets.ts" + }, + "dependencies": { + "@types/node": "14.18.12", + "config": "1.31.0", + "ethereumjs-common": "1.5.2", + "ethereumjs-tx": "2.1.2", + "ts-node": "9.1.1", + "web3": "1.7.0", + "socket.io": "4.4.1" + }, + "devDependencies": { + "typescript": "3.9.10" + } +} \ No newline at end of file diff --git a/examples/cactus-example-tcs-huawei/tools/transferNumericAsset/transferNumericAsset.ts b/examples/cactus-example-tcs-huawei/tools/transferNumericAsset/transferNumericAsset.ts new file mode 100644 index 0000000000..9af42138f2 --- /dev/null +++ b/examples/cactus-example-tcs-huawei/tools/transferNumericAsset/transferNumericAsset.ts @@ -0,0 +1,121 @@ +/* + * Copyright 2021 Hyperledger Cactus Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * transferNumericAsset.ts + */ + +import { io } from "socket.io-client"; + +{ + // Validator test program.(socket.io client) + const config = require("config"); + + // Specify the server (Validator) of the communication destination + const validatorUrl = config.validatorUrl; + console.log("validatorUrl: " + validatorUrl); + const options = { + rejectUnauthorized: false, // temporary avoidance since self-signed certificates are used + reconnection: false, + timeout: 20000, + }; + const socket = io(validatorUrl, options); + + // ## Request for "transferNumericAsset" + if (process.argv.length !== 5) { + console.log("Number of parameters is abnormal."); + process.exit(-1); + } + const paramAmount: number = parseInt(process.argv[4]); + if (Number.isNaN(paramAmount)) { + console.log(`Third parameter is not numeric. ${process.argv[4]}`); + process.exit(-1); + } + + console.log( + `execution parameter : fromAddress = ${process.argv[2]}, toAddress = ${process.argv[3]}, amount = ${process.argv[4]}`, + ); + + const fromAddress = process.argv[2]; + const toAddress = process.argv[3]; + const amount = paramAmount; + const reqID = "reqID_TNA_001"; + + // function param + const requestData = { + contract: {}, + method: { + type: "web3Eth", + command: "sendTransaction", + }, + args: { + args: [ + { + from: fromAddress, + to: toAddress, + value: amount, + }, + ], + }, + reqID: reqID, + }; + + const json2str = (jsonObj) => { + try { + return JSON.stringify(jsonObj); + } catch (error) { + return null; + } + }; + + socket.on("connect_error", (err) => { + console.log("####connect_error:", err); + // end communication + socket.disconnect(); + process.exit(0); + }); + + socket.on("connect_timeout", (err) => { + console.log("####Error:", err); + // end communication + socket.disconnect(); + process.exit(0); + }); + + socket.on("error", (err) => { + console.log("####Error:", err); + }); + + socket.on("eventReceived", function (res) { + // output the data received from the client + console.log("#[recv]eventReceived, res: " + json2str(res)); + }); + + const requestStopMonitor = () => { + console.log("##exec requestStopMonitor()"); + socket.emit("stopMonitor"); + + setTimeout(function () { + // end communication + socket.disconnect(); + process.exit(0); + }, 5000); + }; + + // request StartMonitor + const requestStartMonitor = () => { + console.log("##exec requestStartMonitor()"); + socket.emit("startMonitor"); + + setTimeout(requestStopMonitor, 15000); + }; + + const sendRequest = () => { + console.log("exec sendRequest()"); + console.log("#[send]requestData: " + json2str(requestData)); + socket.emit("request2", requestData); + }; + + setTimeout(requestStartMonitor, 2000); + setTimeout(sendRequest, 4000); +} diff --git a/examples/cactus-example-tcs-huawei/tools/transferNumericAsset/tsconfig.json b/examples/cactus-example-tcs-huawei/tools/transferNumericAsset/tsconfig.json new file mode 100644 index 0000000000..b0f7b49357 --- /dev/null +++ b/examples/cactus-example-tcs-huawei/tools/transferNumericAsset/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "outDir": "./dist", + "sourceMap": false, + "declaration": false, + "moduleResolution": "node", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "target": "ES2017", + "module": "CommonJS", + "typeRoots": [ + "node_modules/@types", + "./typings" + ], + "lib": [ + "es2017", + "dom" + ] + }, + "include": [ + "./*.ts" + ], + "exclude": [ + "copyStaticAssets.ts" + ] +} diff --git a/examples/cactus-example-tcs-huawei/tsconfig.json b/examples/cactus-example-tcs-huawei/tsconfig.json new file mode 100644 index 0000000000..bc78419774 --- /dev/null +++ b/examples/cactus-example-tcs-huawei/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./dist/", + "tsBuildInfoFile": "../../.build-cache/cactus-example-electricity-trade.tsbuildinfo", + "strict": false // TODO - true, fix warnings + }, + "include": [ + "./*.ts", + ], + "references": [ + { + "path": "../../packages/cactus-cmd-socketio-server/tsconfig.json" + }, + { + "path": "../../packages/cactus-verifier-client/tsconfig.json" + } + ] +} \ No newline at end of file diff --git a/examples/cactus-example-tcs-huawei/tslint.json b/examples/cactus-example-tcs-huawei/tslint.json new file mode 100644 index 0000000000..9a00b4aa19 --- /dev/null +++ b/examples/cactus-example-tcs-huawei/tslint.json @@ -0,0 +1,28 @@ +{ + "defaultSeverity": "error", + "extends": [ + "tslint:recommended" + ], + "jsRules": {}, + "rules": { + "variable-name": { + "options": [ + "allow-leading-underscore", + "ban-keywords", + "check-format" + ] + }, + "no-string-literal": false, + "no-var-requires": false, + "object-literal-shorthand": false, + "max-classes-per-file": false, + "no-console": false + }, + "rulesDirectory": [], + "linterOptions": { + "exclude": [ + "packages/cactus-sdk/src/main/typescript/generated/**/*.ts", + "packages/cactus-sdk/src/main/typescript/public-api.ts" + ] + } +} diff --git a/examples/cactus-example-tcs-huawei/www.ts b/examples/cactus-example-tcs-huawei/www.ts new file mode 100644 index 0000000000..3c3295767b --- /dev/null +++ b/examples/cactus-example-tcs-huawei/www.ts @@ -0,0 +1,7 @@ +import { BusinessLogicElectricityTrade } from "./BusinessLogicTcsElectricityTrade"; +import { startCactusSocketIOServer } from "@hyperledger/cactus-cmd-socketio-server"; + +startCactusSocketIOServer({ + id: "h40Q9eMD", + plugin: new BusinessLogicElectricityTrade("h40Q9eMD"), +}); diff --git a/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/.gitignore b/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/.gitignore new file mode 100644 index 0000000000..849ddff3b7 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/.gitignore @@ -0,0 +1 @@ +dist/ diff --git a/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/Dockerfile b/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/Dockerfile new file mode 100644 index 0000000000..97a0951d2e --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/Dockerfile @@ -0,0 +1,13 @@ +# TODO +# Install connector as yarn package like in @hyperledger/cactus-plugin-ledger-connector-besu + +FROM node:16 + +WORKDIR /root/cactus/ + +COPY ./dist ./dist/ +COPY ./dist/yarn.lock ./package.json ./ +RUN yarn install --production --frozen-lockfile --non-interactive --cache-folder ./.yarnCache; rm -rf ./.yarnCache + +EXPOSE 5140 +CMD [ "npm", "run", "start" ] diff --git a/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/README.md b/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/README.md new file mode 100644 index 0000000000..d9cb61167b --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/README.md @@ -0,0 +1,77 @@ + +# `@hyperledger/cactus-plugin-ledger-connector-tcs-huawei-socketio` + +This plugin provides `Cactus` a way to interact with tcs-huawei networks.tcs-huawei (Trusted Cross-chain Service-Huawei) is a blockchain integration service on Huawei Cloud. Using this we can perform: +- `startMonitor`: Start monitoring blocks on the ledgers +- `stopMonitor`: Stop the block monitoring + +## Summary +- [Getting started](#getting-started) +- [Usage samples](#usage-samples) +- [Contributing](#contributing) +- [License](#license) +- [Acknowledgments](#acknowledgments) + +## Getting started + +### Required software components +- OS: Linux (recommended Ubuntu20.04,18.04 or CentOS7) +- Docker (recommend: v17.06.2-ce or greater) +- node.js v16 (recommend: v16 or greater) + +### Prerequisites +- Please ensure that the destination ledger (default: [tcs-huawei](../../tools/docker/tcs-huawei-testnet)) is already launched. + +## Boot methods + +### Common setup +1. Always run configure command first, from the project root directory: + ``` + pushd ../.. + npm run configure + popd + ``` + +1. Copy default configuration +- **Remember to replace default CA and to adjust the `default.yaml` configuration on production deployments!** + ``` + mkdir -p /etc/cactus/connector-tcs-huawei-socketio/ + rm -r /etc/cactus/connector-tcs-huawei-socketio/* + cp -rf ./sample-config/* /etc/cactus/connector-tcs-huawei-socketio/ + ``` + +### Docker +``` +# Build +docker build . -t cactus-plugin-ledger-connector-tcs-huawei-socketio + + +``` + +### Manual +- Ensure ledger ports are exposed to the host first. + +``` +npm run start +``` + +## Usage samples +- To confirm the operation of this package, please refer to the following business-logic sample application: + - [cactus-example-electricity-trade](../../examples/cactus-example-electricity-trade) + +## Contributing + +We welcome contributions to Hyperledger Cactus in many forms, and there's always plenty to do! + +Please review [CONTIRBUTING.md](../../CONTRIBUTING.md) to get started. + +## License + +This distribution is published under the Apache License Version 2.0 found in the [LICENSE](../../LICENSE) file. + +## Acknowledgments diff --git a/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/package.json b/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/package.json new file mode 100644 index 0000000000..981c375208 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/package.json @@ -0,0 +1,33 @@ +{ + "name": "@hyperledger/cactus-plugin-ledger-connector-tcs-huawei-socketio", + "version": "2.0.0-alpha.1", + "license": "Apache-2.0", + "scripts": { + "start": "cd ./dist && node common/core/bin/www.js", + "debug": "nodemon --inspect ./dist/common/core/bin/www.js", + "build": "npm run build-ts && npm run build:dev:backend:postbuild", + "build-ts": "tsc", + "build:dev:backend:postbuild": "npm run init", + "init": "cp -af ../../yarn.lock ./dist/yarn.lock" + }, + "dependencies": { + "@types/node": "14.17.32", + "body-parser": "1.17.2", + "cbor": "6.0.1", + "config": "3.3.7", + "cookie-parser": "1.4.6", + "debug": "4.1.1", + "express": "4.17.3", + "js-yaml": "3.14.1", + "jsonwebtoken": "8.5.1", + "log4js": "6.4.1", + "morgan": "1.10.0", + "serve-favicon": "2.4.5", + "shelljs": "0.8.5", + "socket.io": "4.4.1", + "xmlhttprequest": "1.8.0" + }, + "devDependencies": { + "@types/config": "0.0.41" + } +} diff --git a/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/sample-config/default.yaml b/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/sample-config/default.yaml new file mode 100644 index 0000000000..6d63362cde --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/sample-config/default.yaml @@ -0,0 +1,21 @@ +sslParam: + port: 5140 + key: "/etc/cactus/connector-tcs-huawei/CA/connector.priv" + cert: "/etc/cactus/connector-tcs-huawei/CA/connector.crt" + clientCert: "/etc/cactus/connector-tcs-huawei/cert.pem" + clientKey: "/etc/cactus/connector-tcs-huawei/key.pem" +blockMonitor: + request: + method: "GET" + host: "https://127.0.0.1:30081/" + getChannelListCommand: "v1/blockcross/channel/list" + getLatestBlockNumberCommand: "blocks?limit=1" + periodicMonitoringCommand1: "blocks?start=" + periodicMonitoringCommand2: "&reverse" + pollingInterval: 5000 +testURL: + request: + method: "GET" + host: "https://relay1/v1/blockcross/channel/list" +logLevel: "debug" +CertCheck: diff --git a/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/src/main/typescript/common/core/app.ts b/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/src/main/typescript/common/core/app.ts new file mode 100644 index 0000000000..da68334782 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/src/main/typescript/common/core/app.ts @@ -0,0 +1,53 @@ +/* + * Copyright 2021 Hyperledger Cactus Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * app.js + */ + +/* Summary: + * + */ + +import { NextFunction, Request, Response } from "express"; +import createError = require("http-errors"); +import express = require("express"); +import cookieParser = require("cookie-parser"); +import bodyParser = require("body-parser"); + +const app: express.Express = express(); + +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: false })); +app.use(cookieParser()); + +// catch 404 and forward to error handler +app.use((req: Request, res: Response, next: NextFunction) => { + next(createError(404)); +}); + +// error handler +app.use( + ( + err: { message: string; status?: number }, + req: Request, + res: Response, + next: NextFunction, + ) => { + // set locals, only providing error in development + res.locals.message = err.message; + res.locals.error = req.app.get("env") === "development" ? err : {}; + + // set erreor response + const errorResponse: {} = { + statusCode: err.status || 500, + message: err.message, + }; + + // render the error page + res.status(err.status || 500); + res.send(errorResponse); + }, +); + +export default app; diff --git a/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/src/main/typescript/common/core/bin/www.ts b/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/src/main/typescript/common/core/bin/www.ts new file mode 100644 index 0000000000..c4ad1b794e --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/src/main/typescript/common/core/bin/www.ts @@ -0,0 +1,144 @@ +#!/usr/bin/env node +/* + * Copyright 2021 Hyperledger Cactus Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * www.js + */ + +/* Summary: + * Connector: a part independent of end-chains + */ + +/** + * Module dependencies. + */ + + import app from "../app"; + const debug = require("debug")("connector:server"); + import https = require("https"); + import * as config from "../config"; + import fs = require("fs"); + import { Server } from "socket.io" + + // Log settings + import { getLogger } from "log4js"; + const logger = getLogger("connector_main[" + process.pid + "]"); + logger.level = config.read('logLevel', 'info'); + + // destination dependency (MONITOR) implementation class + import { ServerMonitorPlugin } from "../../../connector/ServerMonitorPlugin"; + const Smonitor = new ServerMonitorPlugin(); + + /** + * Get port from environment and store in Express. + */ + + const sslport = normalizePort(process.env.PORT || config.read('sslParam.port')); + app.set("port", sslport); + + // Specify private key and certificate + const sslParam = { + key: fs.readFileSync(config.read('sslParam.key')), + cert: fs.readFileSync(config.read('sslParam.cert')), + }; + + /** + * Create HTTPS server. + */ + + const server = https.createServer(sslParam, app); // Start as an https server. + const io = new Server(server); + + /** + * Listen on provided port, on all network interfaces. + */ + + server.listen(sslport, function () { + console.log("listening on *:" + sslport); + }); + server.on("error", onError); + server.on("listening", onListening); + + /** + * Normalize a port into a number, string, or false. + */ + + function normalizePort(val: string) { + const port = parseInt(val, 10); + + if (isNaN(port)) { + // named pipe + return val; + } + + if (port >= 0) { + // port number + return port; + } + + return false; + } + + /** + * Event listener for HTTPS server "error" event. + */ + + function onError(error: any) { + if (error.syscall !== "listen") { + throw error; + } + + const bind = + typeof sslport === "string" ? "Pipe " + sslport : "Port " + sslport; + + // handle specific listen errors with friendly messages + switch (error.code) { + case "EACCES": + console.error(bind + " requires elevated privileges"); + process.exit(1); + break; + case "EADDRINUSE": + console.error(bind + " is already in use"); + process.exit(1); + break; + default: + throw error; + } + } + + /** + * Event listener for HTTPS server "listening" event. + */ + + function onListening() { + const addr = server.address(); + + if (!addr) { + logger.error("Could not get running server address - exit."); + process.exit(1); + } + + const bind = typeof addr === "string" ? "pipe " + addr : "port " + addr.port; + debug("Listening on " + bind); + } + + io.on("connection", function (client) { + logger.info("Client " + client.id + " connected."); + + /** + * startMonitor: starting block generation event monitoring + **/ + client.on("startMonitor", function (data) { + Smonitor.startMonitor(client.id, data.filterKey, (callbackData) => { + let emitType = ""; + if (callbackData.status == 200) { + emitType = "eventReceived"; + logger.info("event data callbacked."); + } else { + emitType = "monitor_error"; + } + client.emit(emitType, callbackData); + }); + }); + }); \ No newline at end of file diff --git a/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/src/main/typescript/common/core/config.ts b/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/src/main/typescript/common/core/config.ts new file mode 100644 index 0000000000..327e7efb4f --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/src/main/typescript/common/core/config.ts @@ -0,0 +1,37 @@ +/* + * Copyright 2021 Hyperledger Cactus Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * config.js + */ + +// TODO - Common + +export const DEFAULT_NODE_CONFIG_DIR = "/etc/cactus/connector-tcs-huawei/"; +if (!process.env["NODE_CONFIG_DIR"]) { + // Must be set before import config + process.env["NODE_CONFIG_DIR"] = DEFAULT_NODE_CONFIG_DIR; +} + +import config from "config"; + +/** + * Get configuration entry (uses node-config setup) + * + * @param key : Key to retrieve + * @param defaultValue : Value to return if key is not present in the config. + * @returns : Configuration value + */ +export function read(key: string, defaultValue?: T): T { + if (config.has(key)) { + return config.get(key); + } + + if (defaultValue) { + return defaultValue; + } + + throw Error( + `Missing configuration entry '${key}', config dir = ${process.env["NODE_CONFIG_DIR"]}`, + ); +} diff --git a/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/src/main/typescript/connector/PluginUtil.ts b/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/src/main/typescript/connector/PluginUtil.ts new file mode 100644 index 0000000000..d3aaddd292 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/src/main/typescript/connector/PluginUtil.ts @@ -0,0 +1,87 @@ +/* + * Copyright 2021 Hyperledger Cactus Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * PluginUtil.js + */ + +const cbor = require("cbor"); + +/* + * Summary: + * Cooperation server: utility library dependent on endchains + * For example, implementing internal functions that should not be exposed as functions of server plugins. + */ + +/* + * convNum + * + * @param {String/Number} value: The scientific string or numeric value to be converted. For numbers, it is returned as is. + * @param {String/Number} defaultValue: The value to use if conversion was not possible. Text or number in scientific notation. Optional. + * + * @return {Number} The value converted to a number, or the defaultValue if the conversion failed. + * + * @desc exponentiation is also supported. The following formats are supported:. (value, defaultValue) + * 3.78*10^14 + * 3.78e14 + */ +exports.convNum = function convNum( + value: number | string, + defaultValue: number | string, +) { + let retValue = 0; + let defValue = 0; + + switch (typeof defaultValue) { + case "number": + defValue = defaultValue; + break; + case "string": + const defValueStr = defaultValue.replace(/\*10\^/g, "e"); + defValue = parseFloat(defValueStr); + break; + default: + // undefined should also be included here. + // Fixed value because of cumbersome handling. + defValue = 0; + break; + } // switch(typeof(defaultValue)) + + if (defValue == NaN) { + // number is guaranteed. + defValue = 0; + } + + switch (typeof value) { + case "number": + retValue = value; + break; + case "string": + const valueStr = value.replace(/\*10\^/g, "e"); + retValue = parseFloat(valueStr); + break; + default: + // Set default value. + retValue = defValue; + break; + } // switch(typeof(value)) + + if (retValue == NaN) { + // number is guaranteed. + retValue = defValue; + } + + return retValue; +}; + +exports.convertBlockNumber = function (value: number) { + return "0x" + ("0000000000000000" + value.toString(16)).substr(-16); +}; + +exports.decodeBase64 = function (encoded: string): Buffer { + return Buffer.from(encoded, "base64"); +}; + +exports.deccodeCbor = function (encoded: any): Array { + return cbor.decodeAllSync(encoded); +}; diff --git a/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/src/main/typescript/connector/ServerMonitorPlugin.ts b/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/src/main/typescript/connector/ServerMonitorPlugin.ts new file mode 100644 index 0000000000..5622b93bf8 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/src/main/typescript/connector/ServerMonitorPlugin.ts @@ -0,0 +1,198 @@ +/* + * Copyright 2021 Hyperledger Cactus Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * ServerMonitorPlugin.js + */ + +/* + * Summary: + * Connector: monitoring class of a part dependent on end-chains + * Used in the case of monitoring continuously. + * Processing that ends with the acceptance of a single event is handled by a custom function implementation in server plugins. + * Unlike server plugins, it basically does not handle its own functions. + */ + +// configuration file +const SplugUtil = require("./PluginUtil"); +import * as config from "../common/core/config"; +// Log settings +import { getLogger } from "log4js"; +const logger = getLogger("ServerMonitorPlugin[" + process.pid + "]"); +logger.level = config.read("logLevel", "info"); +// utility +import { ValidatorAuthentication } from "./ValidatorAuthentication"; +const XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest; + +import https from "https"; +import fs = require("fs"); + +export type MonitorCallback = (callback: { + status: number; + blockData: string; +}) => void; + +/* + * ServerMonitorPlugin + * Class definitions of server monitoring + */ +export class ServerMonitorPlugin { + currentBlockNumber: number; + + /* + * constructors + */ + constructor() { + // Define dependent specific settings + this.currentBlockNumber = -1; + } + + /* + * startMonitor + * Start Monitoring + * @param {string} clientId: Client ID from which monitoring start request was made + * @param {string} filterKey: Key to filter blocks + * @param {function} cb: A callback function that receives monitoring results at any time. + */ + startMonitor(clientId: string, filterKey: string, cb: MonitorCallback) { + const request_blocknum_body = JSON.stringify({ + to_chain: "B0123456789012345678901234567890123456789", + from_chaincode_id: "QueryChainCode", + to_chaincode_id: "QueryChainCode", + to_query_func_name: "QueryBlockNum", + args: ["a"], + }); + //- (not recommended - only for development): Temporarily set CertCheck to false when using a self-signed certificate + var CertCheck = config.read("CertCheck") + if (CertCheck == undefined){ + CertCheck = true + } + const options: https.RequestOptions = { + hostname: "agent", + port: 8080, + path: "/v1/cross/transaction/query", + method: "POST", + headers: { + "Content-Type": "text/plain", + "Content-Length": Buffer.byteLength(request_blocknum_body), + }, + cert: fs.readFileSync(config.read("sslParam.clientCert")), + key: fs.readFileSync(config.read("sslParam.clientKey")), + rejectUnauthorized: CertCheck, + }; + + let resData; + let blockNum; + const that = this; + const req = https.request(options, (res) => { + res.on("data", (d) => { + resData = JSON.parse(d); + that.currentBlockNumber = resData.data.payload; + setInterval(function () { + that.periodicMonitoring(clientId, filterKey, cb); + }, 2000); + }); + }); + + req.write(request_blocknum_body); + + req.on("error", (error) => { + logger.error(error); + }); + req.end(); + + logger.info("*** START MONITOR ***"); + logger.info("Client ID :" + clientId); + logger.debug(`filterKey = ${filterKey}`); + } + + /* + * periodicMonitoring + * Periodic monitoring + * @param {string} clientId: Client ID from which monitoring start request was made + * @param {string} filterKey: Key to filter blocks + * @param {function} cb: A callback function that receives monitoring results at any time. + */ + periodicMonitoring(clientId: string, filterKey: string, cb: MonitorCallback) { + let targetBlockNum: number = this.currentBlockNumber; + targetBlockNum++; + const request_block_body = JSON.stringify({ + to_chain: "B0123456789012345678901234567890123456789", + from_chaincode_id: "QueryChainCode", + to_chaincode_id: "QueryChainCode", + to_query_func_name: "GetBlockByNumber", + args: [targetBlockNum.toString()], + }); + var CertCheck = config.read("CertCheck") + if (CertCheck == undefined){ + CertCheck = true + } + const options: https.RequestOptions = { + hostname: "agent", + port: 8080, + path: "/v1/cross/transaction/query", + method: "POST", + headers: { + "Content-Type": "text/plain", + "Content-Length": Buffer.byteLength(request_block_body), + }, + cert: fs.readFileSync(config.read("sslParam.clientCert")), + key: fs.readFileSync(config.read("sslParam.clientKey")), + rejectUnauthorized: CertCheck, + }; + + let resData; + const transactionDataArray: any[] = []; + const that = this; + const req = https.request(options, (res) => { + res.on("data", (d) => { + resData = JSON.parse(d); + if (resData.error_message == "block not found") { + logger.debug("block not found, continue to poll"); + return; + } + const payload = JSON.parse(resData.data.payload); + if (payload.Header.BlockNum != targetBlockNum) { + logger.error( + "expected ", + targetBlockNum, + " get ", + payload.Header.BlockNum, + ); + return; + } + for (const transaction of payload.Data) { + transactionDataArray.push(transaction); + } + if (this.currentBlockNumber < targetBlockNum) { + this.currentBlockNumber = targetBlockNum; + } + if (transactionDataArray.length > 0) { + logger.info("*** SEND TRANSACTION DATA ***"); + logger.debug( + `transactionDataArray = ${JSON.stringify(transactionDataArray)}`, + ); + const signedTransactionDataArray = ValidatorAuthentication.sign({ + blockData: transactionDataArray, + }); + logger.debug( + `signedTransactionDataArray = ${signedTransactionDataArray}`, + ); + const retObj = { + status: 200, + blockData: signedTransactionDataArray, + }; + cb(retObj); + } + this.currentBlockNumber + 1; + }); + }); + + req.write(request_block_body); + + req.on("error", (error) => { + logger.error(error); + }); + req.end(); + } +} diff --git a/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/src/main/typescript/connector/ValidatorAuthentication.ts b/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/src/main/typescript/connector/ValidatorAuthentication.ts new file mode 100644 index 0000000000..4ab02e4521 --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/src/main/typescript/connector/ValidatorAuthentication.ts @@ -0,0 +1,33 @@ +/* + * Copyright 2020-2021 Hyperledger Cactus Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * ValidatorAuthentication.ts + */ + +const fs = require("fs"); +const path = require("path"); +const jwt = require("jsonwebtoken"); +import * as config from "../common/core/config"; +import { getLogger } from "log4js"; +const logger = getLogger("ValidatorAuthentication[" + process.pid + "]"); +logger.level = config.read("logLevel", "info"); + +const privateKey = fs.readFileSync( + path.resolve(__dirname, config.read("sslParam.key")), +); + +export class ValidatorAuthentication { + static sign(payload: object): string { + const option = { + algorithm: "ES256", + expiresIn: "1000", + }; + + // logger.debug(`payload = ${JSON.stringify(payload)}`); + const signature: string = jwt.sign(payload, privateKey, option); + // logger.debug(`signature = ${signature}`); + logger.debug(`signature: OK`); + return signature; + } +} diff --git a/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/tsconfig.json b/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/tsconfig.json new file mode 100644 index 0000000000..952920156b --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "./src/main/typescript", + "composite": true, + "outDir": "./dist", + "sourceMap": false, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "tsBuildInfoFile": "../../.build-cache/cactus-plugin-ledger-connector-tcs-huawei-socketio.tsbuildinfo", + }, + "include": [ + "./src/main/typescript/common/core/*.ts", + "./src/main/typescript/common/core/bin/*.ts", + "./src/main/typescript/common/core/config/*.ts", + "./src/main/typescript/connector/*.ts" + ] +} diff --git a/tools/docker/tcs-huawei-testnet/agent/example/cross_config/config.yaml b/tools/docker/tcs-huawei-testnet/agent/example/cross_config/config.yaml new file mode 100644 index 0000000000..9960c4ff77 --- /dev/null +++ b/tools/docker/tcs-huawei-testnet/agent/example/cross_config/config.yaml @@ -0,0 +1,35 @@ +General: + AgentServer: + Address: 0.0.0.0 + GRPCPort: 8098 + RelayInfo: + Address: relay1 + Port: 8082 +FabricConfiguration: + SupportContractEntry: + BlockCrossContract: blockcross + BlockCrossInvoke: invoke + FabricCfgFilePath: + CfgFile: /root/Certs/blockcrossA-NoDelete-channel-sdk-config.yaml + AppchainInfo: + UserName: Admin + ChaincodeID: blockcross + Name: A +CrossChannel: + CrossChannelID: channelidA + CrossChannelName: mychannel +CryptoPath: + SelfTLSPath: + PrivateKey: /root/Certs/A/channelidA/tls/A.key + Certificate: /root/Certs/A/channelidA/tls/A.crt + RootCA: /root/Certs/A/tlsca.crt + SelfMemPath: + PrivateKey: /root/Certs/A/membership/A.key + Certificate: /root/Certs/A/membership/A.crt + RootCA: /root/Certs/A/memca.crt + ServerTLSPath: + PrivateKey: /root/Certs/Server/key.pem + Certificate: /root/Certs/Server/cert.pem + RootCA: /root/Certs/Server/cacert.pem +PersistentData: + DBPath: /root/tcdas/production/agent/DB \ No newline at end of file diff --git a/tools/docker/tcs-huawei-testnet/agent/example/docker-compose.yml b/tools/docker/tcs-huawei-testnet/agent/example/docker-compose.yml new file mode 100644 index 0000000000..233d0f8fb0 --- /dev/null +++ b/tools/docker/tcs-huawei-testnet/agent/example/docker-compose.yml @@ -0,0 +1,37 @@ +version: '3' +networks: + tcs_huawei_testnet_1x: +services: + agent1: + image: "registry-cbu.huawei.com/tcdas/tcdas-agent:0.0.1.test-x86_64" + container_name: agent + volumes: + # 以下为需要外部生成并导入容器的物料 + - $PWD/cross_config:/home/service/config + - $PWD/Server_Cert:/home/service/Certs/Server + # 以下为需要挂载到容器外部的文件夹 + - /var/paas/sys/log/agent1/tcdas:/var/log/tcdas + - /home/paas/tcdas/agent1:/home/service/tcdas/production/agent/DB + environment: + - AGENT_CONFIG_PATH=/home/service/config + - AGENT_GENERAL_RELAYINFO_ADDRESS=relay1 + - AGENT_FABRICCONFIGURATION_FABRICCFGFILEPATH_CFGFILE=/home/service/Certs/blockcrossA-NoDelete-channel-sdk-config.yaml + - AGENT_FABRICCONFIGURATION_APPCHAININFO_NAME=A0123456789012345678901234567890123456789 + - AGENT_GENERAL_AGENTID=channelidA + - AGENT_CROSSCHANNEL_CROSSCHANNELID=channelidA + - AGENT_CROSSCHANNEL_CROSSCHANNELNAME=mychannel + - AGENT_CRYPTOPATH_SERVERTLSPATH_PRIVATEKEY=/home/service/Certs/Server/key.pem + - AGENT_CRYPTOPATH_SERVERTLSPATH_CERTIFICATE=/home/service/Certs/Server/cert.pem + - AGENT_CRYPTOPATH_SERVERTLSPATH_ROOTCA=/home/service/Certs/Server/cacert.pem + - AGENT_PERSISTENTDATA_DBPATH=/home/service/tcdas/production/agent/DB + - AGENT_LOGGER_ROOT=/var/log/tcdas/agent + - AGENT_LOGGER_LEVEL=LevelInformational + ports: + - "8001:8080" + networks: + - tcs_huawei_testnet_1x + command: + - /bin/bash + - -c + - | + bash -x /usr/local/bin/start.sh \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index aebbe988f3..c329c56c0b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -67,6 +67,9 @@ { "path": "./packages/cactus-plugin-ledger-connector-iroha/tsconfig.json" }, + { + "path": "./packages/cactus-plugin-ledger-connector-tcs-huawei-socketio/tsconfig.json" + }, { "path": "./packages/cactus-plugin-ledger-connector-xdai/tsconfig.json" }, @@ -124,6 +127,9 @@ { "path": "./examples/cactus-example-supply-chain-frontend/tsconfig.json" }, + { + "path": "./examples/cactus-example-tcs-huawei/tsconfig.json" + }, { "path": "./packages/cactus-plugin-odap-hermes/tsconfig.json" },