diff --git a/examples/cactus-example-discounted-asset-trade/README.md b/examples/cactus-example-discounted-asset-trade/README.md index d98de38144a..e921f4b2851 100644 --- a/examples/cactus-example-discounted-asset-trade/README.md +++ b/examples/cactus-example-discounted-asset-trade/README.md @@ -26,7 +26,7 @@ Alice will use credentials and other Indy formats such as schema and definition ## Setup Overview -### fabric-socketio +### fabric-validator - Validator for fabric ledger. - Started as part of discounted asset trade BLP. @@ -34,7 +34,7 @@ Alice will use credentials and other Indy formats such as schema and definition ### ethereum-validator - Validator for ethereum ledger. -- Docker network: `geth1net`, `cactus-example-discounted-asset-trade-net` +- Started as part of discounted asset trade BLP. ### indy-sdk-cli-base-image @@ -112,7 +112,6 @@ Alice will use credentials and other Indy formats such as schema and definition This will build and launch all needed containers, the final output should look like this: ``` - cactus-example-discounted-asset-trade-ethereum-validator | listening on *:5050 ... cactus-example-discounted-asset-trade-indy-validator | 2022-01-31 16:00:49,552 INFO success: validator entered RUNNING state, process has stayed up for > than 1 seconds (startsecs) ... @@ -151,10 +150,7 @@ For development purposes, it might be useful to run the sample application outsi 1. Add indy-sdk node package: `yarn add indy-sdk@1.16.0-dev-1649` (or edit `package.json` directly) 1. Configure cactus and start the ledgers as described above. 1. Run `./script-dockerless-config-patch.sh` from `cactus-example-discounted-asset-trade/` directory. This will patch the configs and copy it to global location. -1. Start validators (each in separate cmd window). - 1. ```bash - cd packages/cactus-plugin-ledger-connector-go-ethereum-socketio/ && npm run start - ``` +1. Start Indy validator (in separate cmd window). 1. ```bash docker build tools/docker/indy-sdk-cli -t indy-sdk-cli && pushd packages-python/cactus_validator_socketio_indy/ && sh ./setup_indy.sh && popd && @@ -175,13 +171,13 @@ For development purposes, it might be useful to run the sample application outsi ``` # Ethereum fromAccount: - { status: 200, amount: 1e+26 } + 100000105000000000000000000 # Ethereum escrowAccount: - { status: 200, amount: 0 } + 0 # Ethereum toAccount: - { status: 200, amount: 0 } + 0 # Fabric: @@ -240,13 +236,13 @@ For development purposes, it might be useful to run the sample application outsi ``` # Ethereum fromAccount: - { status: 200, amount: 1.00000045e+26 } + 100000184999999999999999975 # Ethereum escrowAccount: - { status: 200, amount: 0 } + 0 # Ethereum toAccount: - { status: 200, amount: 25 } + 25 # Fabric: diff --git a/examples/cactus-example-discounted-asset-trade/balance-management.ts b/examples/cactus-example-discounted-asset-trade/balance-management.ts deleted file mode 100644 index ac973c4ae2b..00000000000 --- a/examples/cactus-example-discounted-asset-trade/balance-management.ts +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2020-2022 Hyperledger Cactus Contributors - * SPDX-License-Identifier: Apache-2.0 - * - * balance-management.ts - */ - -import { LPInfoHolder } from "@hyperledger/cactus-cmd-socketio-server"; -import { ConfigUtil } from "@hyperledger/cactus-cmd-socketio-server"; -import { - VerifierFactory, - VerifierFactoryConfig, -} from "@hyperledger/cactus-verifier-client"; - -const config: any = ConfigUtil.getConfig() as any; -import { getLogger } from "log4js"; -const moduleName = "BalanceManagement"; -const logger = getLogger(`${moduleName}`); -logger.level = config.logLevel; - -export class BalanceManagement { - private connectInfo: LPInfoHolder | null = 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<{ - status: number; - amount: number; - }> { - return new Promise((resolve, reject) => { - // for LedgerOperation - - const contract = {}; // NOTE: Since contract does not need to be specified, specify an empty object. - const method = { type: "web3Eth", command: "getBalance" }; - const 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-discounted-asset-trade/balance.ts b/examples/cactus-example-discounted-asset-trade/balance.ts index 403c1b9bcd1..5ba9da3dbbc 100644 --- a/examples/cactus-example-discounted-asset-trade/balance.ts +++ b/examples/cactus-example-discounted-asset-trade/balance.ts @@ -8,22 +8,20 @@ import { Router, NextFunction, Request, Response } from "express"; import { ConfigUtil } from "@hyperledger/cactus-cmd-socketio-server"; import { RIFError } from "@hyperledger/cactus-cmd-socketio-server"; -import { BalanceManagement } from "./balance-management"; const config: any = ConfigUtil.getConfig(); import { getLogger } from "log4js"; +import { getAccountBalance } from "./transaction-ethereum"; 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) + getAccountBalance(req.params.account) .then((result) => { logger.debug(`#####[sample/balance.ts]`); logger.debug("result(getBalance) = " + JSON.stringify(result)); diff --git a/examples/cactus-example-discounted-asset-trade/business-logic-asset-trade.ts b/examples/cactus-example-discounted-asset-trade/business-logic-asset-trade.ts index 24cd7b3bc20..1d53fc0be52 100644 --- a/examples/cactus-example-discounted-asset-trade/business-logic-asset-trade.ts +++ b/examples/cactus-example-discounted-asset-trade/business-logic-asset-trade.ts @@ -13,10 +13,7 @@ import { TransactionInfo } from "./transaction-info"; import { TransactionData } from "./transaction-data"; import { BusinessLogicInquireAssetTradeStatus } from "./business-logic-inquire-asset-trade-status"; import { TxInfoData } from "./tx-info-data"; -import { routesTransactionManagement } from "@hyperledger/cactus-cmd-socketio-server"; import { BusinessLogicBase } from "@hyperledger/cactus-cmd-socketio-server"; -import { LPInfoHolder } from "@hyperledger/cactus-cmd-socketio-server"; -import { makeRawTransaction } from "./transaction-ethereum"; import { transferOwnership } from "./transaction-fabric"; import { getDataFromIndy } from "./transaction-indy"; import { @@ -26,14 +23,15 @@ import { import { json2str } from "@hyperledger/cactus-cmd-socketio-server"; import { AssetTradeStatus } from "./define"; import { - VerifierFactory, - VerifierFactoryConfig, -} from "@hyperledger/cactus-verifier-client"; -import { - WatchBlocksCactusTransactionsEventV1, - WatchBlocksListenerTypeV1, - WatchBlocksResponseV1, + WatchBlocksCactusTransactionsEventV1 as FabricWatchBlocksCactusTransactionsEventV1, + WatchBlocksListenerTypeV1 as FabricWatchBlocksListenerTypeV1, + WatchBlocksResponseV1 as FabricWatchBlocksResponseV1, } from "@hyperledger/cactus-plugin-ledger-connector-fabric"; +import { + WatchBlocksV1Progress as EthWatchBlocksV1Progress, + Web3Transaction, +} from "@hyperledger/cactus-plugin-ledger-connector-ethereum"; +import type { Subscription } from "rxjs"; const config: any = ConfigUtil.getConfig(); @@ -43,37 +41,18 @@ import { getFabricApiClient, getSignerIdentity, } from "./fabric-connector"; +import { getEthereumApiClient } from "./ethereum-connector"; +import { sendEthereumTransaction } from "./transaction-ethereum"; + const moduleName = "BusinessLogicAssetTrade"; const logger = getLogger(`${moduleName}`); logger.level = config.logLevel; -const connectInfo = new LPInfoHolder(); -const routesVerifierFactory = new VerifierFactory( - connectInfo.ledgerPluginInfo as VerifierFactoryConfig, - config.logLevel, -); - const indy = require("indy-sdk"); const assert = require("assert"); const identifierSchema = "schema"; const identifierCredDef = "credDef"; -interface EthEvent { - blockData: { transactions: { [key: string]: EthData } }; - hash: string; - status: number; -} -interface EthData { - hash: string; -} - -interface FabricEvent { - txId: string; - blockData: []; - hash: string; - status: number; -} - interface TransactionStatusData { stateInfo: number | undefined; transactionStatus: { @@ -86,7 +65,7 @@ interface TransactionStatusData { export class BusinessLogicAssetTrade extends BusinessLogicBase { transactionInfoManagement: TransactionInfoManagement; - // useValidator: {}; + ethWatchBlockSub: Subscription | undefined; constructor() { super(); @@ -323,29 +302,52 @@ export class BusinessLogicAssetTrade extends BusinessLogicBase { return [schemas, credDefs, revRegDefs, revRegs]; } + /** + * Eth Escrow + */ firstTransaction(requestInfo: RequestInfo, tradeInfo: TradeInfo): void { - logger.debug("called firstTransaction"); - - ///// Eth Escrow - - // Get Verifier Instance logger.debug( `##firstTransaction(): businessLogicID: ${tradeInfo.businessLogicID}`, ); - const useValidator = JSON.parse( - routesTransactionManagement.getValidatorToUse(tradeInfo.businessLogicID), - ); - const verifierEthereum = routesVerifierFactory.getVerifier( - useValidator["validatorID"][0], - ); - verifierEthereum.startMonitor( - "BusinessLogicAssetTrade", - {}, - routesTransactionManagement, - ); - logger.debug("getVerifierEthereum"); - // TODO: get private key from + // Start monitoring + const ethereumApiClient = getEthereumApiClient(); + const watchObservable = ethereumApiClient.watchBlocksV1({ + getBlockData: true, + }); + this.ethWatchBlockSub = watchObservable.subscribe({ + next: (event: EthWatchBlocksV1Progress) => { + if (!event.blockData) { + logger.error("Wrong input block format!", event); + return; + } + + for (const tx of event.blockData.transactions) { + logger.debug(`##in onEvenEtherem()`); + + try { + const txId = tx.hash; + logger.debug(`##txId = ${txId}`); + + if (this.hasTxIDInTransactions(txId)) { + this.executeNextTransaction(tx, txId); + break; + } + } catch (err) { + logger.error( + `##onEvenEtherem(): onEvent, err: ${err}, event: ${JSON.stringify( + tx, + )}`, + ); + } + } + }, + error(err: unknown) { + logger.error("Ethereum watchBlocksV1() error:", err); + }, + }); + + // Send funds to escrow logger.debug( `####fromAddress: ${requestInfo.tradeInfo.ethereumAccountFrom}`, ); @@ -354,57 +356,35 @@ export class BusinessLogicAssetTrade extends BusinessLogicBase { "fromAddressPkey_" + requestInfo.tradeInfo.ethereumAccountFrom ]; logger.debug(`####fromAddressPkey: ${fromAddressPkey}`); - // TODO: Get address of escrow and set parameter const escrowAddress = config.assetTradeInfo.ethereum.escrowAddress; - // Generate parameters for// sendSignedTransaction - const txParam: { - fromAddress: string; - fromAddressPkey: string; - toAddress: string; - amount: number; - gas: number; - } = { - fromAddress: requestInfo.tradeInfo.ethereumAccountFrom, - fromAddressPkey: fromAddressPkey, - toAddress: escrowAddress, - amount: Number(requestInfo.tradeInfo.tradingValue), - gas: config.assetTradeInfo.ethereum.gas, - }; - logger.debug(`####exec makeRawTransaction!!`); - makeRawTransaction(txParam) + sendEthereumTransaction( + { + to: escrowAddress, + value: Number(requestInfo.tradeInfo.tradingValue), + gas: config.assetTradeInfo.ethereum.gas, + }, + requestInfo.tradeInfo.ethereumAccountFrom, + fromAddressPkey, + ) .then((result) => { - logger.info("firstTransaction txId : " + result.txId); + logger.debug("sendEthereumTransaction escrow tx:", result); + const txId = result.transactionReceipt.transactionHash; + logger.info("firstTransaction txId : ", txId); // Register transaction data in DB const transactionData: TransactionData = new TransactionData( "escrow", "ledger001", - result.txId, + txId, ); this.transactionInfoManagement.setTransactionData( tradeInfo, transactionData, ); - - // Set Parameter - logger.debug("firstTransaction data : " + JSON.stringify(result.data)); - const contract = {}; // NOTE: Since contract does not need to be specified, specify an empty object. - const method = { type: "web3Eth", command: "sendSignedTransaction" }; - - const args = { args: [result.data["serializedTx"]] }; - // Run Verifier (Ethereum) - verifierEthereum - .sendAsyncRequest(contract, method, args) - .then(() => { - logger.debug(`##firstTransaction sendAsyncRequest finish`); - }) - .catch((err) => { - logger.error(err); - }); }) .catch((err) => { - logger.error(err); + logger.error("sendEthereumTransaction Escrow ERROR:", err); }); } @@ -419,13 +399,13 @@ export class BusinessLogicAssetTrade extends BusinessLogicBase { const fabricApiClient = getFabricApiClient(); const watchObservable = fabricApiClient.watchBlocksDelegatedSignV1({ channelName: config.assetTradeInfo.fabric.channelName, - type: WatchBlocksListenerTypeV1.CactusTransactions, + type: FabricWatchBlocksListenerTypeV1.CactusTransactions, signerCertificate: getSignerIdentity().credentials.certificate, signerMspID: getSignerIdentity().mspId, uniqueTransactionData: createSigningToken("watchBlock"), }); const watchSub = watchObservable.subscribe({ - next: (event: WatchBlocksResponseV1) => { + next: (event: FabricWatchBlocksResponseV1) => { if (!("cactusTransactionsEvents" in event)) { logger.error("Wrong input block format!", event); return; @@ -478,78 +458,39 @@ export class BusinessLogicAssetTrade extends BusinessLogicBase { tradingValue: string, tradeInfo: TradeInfo, ): void { - logger.debug("called thirdTransaction"); - - // Get Verifier Instance logger.debug( `##thirdTransaction(): businessLogicID: ${tradeInfo.businessLogicID}`, ); - const useValidator = JSON.parse( - routesTransactionManagement.getValidatorToUse(tradeInfo.businessLogicID), - ); - const verifierEthereum = routesVerifierFactory.getVerifier( - useValidator["validatorID"][0], - ); - logger.debug("getVerifierEthereum"); - - // TODO: Get address of escrow and set parameter const escrowAddress = config.assetTradeInfo.ethereum.escrowAddress; - // TODO: get escrow secret key const escrowAddressPkey = config.assetTradeInfo.ethereum.escrowAddressPkey; - // Generate parameters for sendSignedTransaction - const txParam: { - fromAddress: string; - fromAddressPkey: string; - toAddress: string; - amount: number; - gas: number; - } = { - fromAddress: escrowAddress, - fromAddressPkey: escrowAddressPkey, - toAddress: ethereumAccountTo, - amount: Number(tradingValue), - gas: config.assetTradeInfo.ethereum.gas, - }; - makeRawTransaction(txParam) + sendEthereumTransaction( + { + to: ethereumAccountTo, + value: Number(tradingValue), + gas: config.assetTradeInfo.ethereum.gas, + }, + escrowAddress, + escrowAddressPkey, + ) .then((result) => { - logger.info("thirdTransaction txId : " + result.txId); + logger.debug("sendEthereumTransaction() final results:", result); + const txId = result.transactionReceipt.transactionHash; + logger.info("thirdTransaction txId : ", txId); // Register transaction data in DB const transactionData: TransactionData = new TransactionData( "settlement", "ledger003", - result.txId, + txId, ); this.transactionInfoManagement.setTransactionData( tradeInfo, transactionData, ); - - // Set LedgerOperation - logger.debug("thirdTransaction data : " + JSON.stringify(result.data)); - - // Run Verifier (Ethereum) - // verifierEthereum.requestLedgerOperation(ledgerOperation); - - // TODO: Neo!! - // Set Parameter - const contract = {}; // NOTE: Since contract does not need to be specified, specify an empty object. - const method = { type: "web3Eth", command: "sendSignedTransaction" }; - const args = { args: [result.data["serializedTx"]] }; - - // Run Verifier (Ethereum) - verifierEthereum - .sendAsyncRequest(contract, method, args) - .then(() => { - logger.debug(`##thirdTransaction sendAsyncRequest finish`); - }) - .catch((err) => { - logger.error(err); - }); }) .catch((err) => { - logger.error(err); + logger.error("sendEthereumTransaction Final ERROR:", err); }); } @@ -565,69 +506,12 @@ export class BusinessLogicAssetTrade extends BusinessLogicBase { logger.debug("called finish"); } - onEvent(ledgerEvent: LedgerEvent, targetIndex: number): void { - logger.debug(`##in BLP:onEvent()`); - logger.debug(`##onEvent(): ${json2str(ledgerEvent)}`); - - switch (ledgerEvent.verifierId) { - case config.assetTradeInfo.ethereum.validatorID: - this.onEvenEtherem(ledgerEvent.data, targetIndex); - break; - default: - logger.error( - `##onEvent(), invalid verifierId: ${ledgerEvent.verifierId}`, - ); - return; - } - } - - onEvenEtherem(event: EthEvent, targetIndex: number): void { - logger.debug(`##in onEvenEtherem()`); - const tx = this.getTransactionFromEthereumEvent(event, targetIndex); - if (tx == null) { - logger.error(`##onEvenEtherem(): 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( - `##onEvenEtherem(): error event, status: ${status}, txId: ${txId}`, - ); - return; - } - - // Perform the following transaction actions - this.executeNextTransaction(tx, txId); - } catch (err) { - logger.error(`##onEvenEtherem(): err: ${err}, event: ${json2str(event)}`); - } - } - - getTransactionFromEthereumEvent( - event: EthEvent, - targetIndex: number, - ): EthData | undefined { - 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}`, - ); - } + onEvent(): void { + logger.warn(`onEvent() should not be called`); } executeNextTransaction( - txInfo: WatchBlocksCactusTransactionsEventV1 | EthData, + txInfo: FabricWatchBlocksCactusTransactionsEventV1 | Web3Transaction, txId: string, ): void { let transactionInfo: TransactionInfo | null = null; @@ -701,6 +585,10 @@ export class BusinessLogicAssetTrade extends BusinessLogicBase { `##INFO: completed asset-trade, businessLogicID: ${transactionInfo.businessLogicID}, tradeID: ${transactionInfo.tradeID}`, ); this.completedTransaction(tradeInfo); + if (this.ethWatchBlockSub) { + this.ethWatchBlockSub.unsubscribe(); + this.ethWatchBlockSub = undefined; + } break; case AssetTradeStatus.Completed: logger.warn( @@ -741,53 +629,6 @@ export class BusinessLogicAssetTrade extends BusinessLogicBase { return transactionStatusData; } - getTxIDFromEvent( - ledgerEvent: LedgerEvent, - targetIndex: number, - ): string | null { - logger.debug(`##in getTxIDFromEvent`); - logger.debug(`##event: ${json2str(ledgerEvent)}`); - - if (ledgerEvent.verifierId !== config.assetTradeInfo.ethereum.validatorID) { - logger.error( - `##getTxIDFromEvent(): invalid verifierId: ${ledgerEvent.verifierId}`, - ); - } - - logger.debug(`##in getTxIDFromEventEtherem()`); - const tx = this.getTransactionFromEthereumEvent( - ledgerEvent.data, - 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( - ledgerEvent.data, - )}`, - ); - return null; - } - - logger.debug(`###getTxIDFromEventEtherem(): txId: ${txId}`); - return txId; - } catch (err) { - logger.error( - `##getTxIDFromEventEtherem(): err: ${err}, event: ${json2str( - ledgerEvent.data, - )}`, - ); - return null; - } - } - hasTxIDInTransactions(txID: string): boolean { logger.debug(`##in hasTxIDInTransactions(), txID: ${txID}`); const transactionInfo = diff --git a/examples/cactus-example-discounted-asset-trade/business-logic-inquire-asset-trade-status.ts b/examples/cactus-example-discounted-asset-trade/business-logic-inquire-asset-trade-status.ts index bac25435dae..8a07478e15c 100644 --- a/examples/cactus-example-discounted-asset-trade/business-logic-inquire-asset-trade-status.ts +++ b/examples/cactus-example-discounted-asset-trade/business-logic-inquire-asset-trade-status.ts @@ -18,9 +18,10 @@ export class BusinessLogicInquireAssetTradeStatus extends BusinessLogicBase { super(); } - getAssetTradeOperationStatus( - tradeID: string, - ): { stateInfo: number | undefined; transactionStatus: TransactionStatus[] } { + getAssetTradeOperationStatus(tradeID: string): { + stateInfo: number | undefined; + transactionStatus: TransactionStatus[]; + } { // Existence check of table file try { fs.statSync(this.fileName); @@ -34,7 +35,8 @@ export class BusinessLogicInquireAssetTradeStatus extends BusinessLogicBase { .table as string[]; // Create Response Information - const resultTransactionStatusData: ResultTransactionStatusData = new ResultTransactionStatusData(); + const resultTransactionStatusData: ResultTransactionStatusData = + new ResultTransactionStatusData(); for (const transactionInfoJson of transactionInfoTable) { const transactionInfo: TransactionInfo = JSON.parse( transactionInfoJson, @@ -45,7 +47,8 @@ export class BusinessLogicInquireAssetTradeStatus extends BusinessLogicBase { // Set information resultTransactionStatusData.stateInfo = transactionInfo.status; - const escrowTransactionStatus: TransactionStatus = new TransactionStatus(); + const escrowTransactionStatus: TransactionStatus = + new TransactionStatus(); escrowTransactionStatus.state = "escrow"; escrowTransactionStatus.ledger = transactionInfo.escrowLedger; escrowTransactionStatus.txID = transactionInfo.escrowTxID; diff --git a/examples/cactus-example-discounted-asset-trade/config/usersetting.yaml b/examples/cactus-example-discounted-asset-trade/config/usersetting.yaml index d5c4d2983bc..91a8e8d449f 100644 --- a/examples/cactus-example-discounted-asset-trade/config/usersetting.yaml +++ b/examples/cactus-example-discounted-asset-trade/config/usersetting.yaml @@ -1,7 +1,7 @@ # Overwrite defaults blpRegistry: - businessLogicID: guks32pf - validatorID: [84jUisrs] + validatorID: [] logLevel: debug @@ -41,7 +41,7 @@ assetTradeInfo: ethereum: validatorID: 84jUisrs - gethURL: http://localhost:8545 + gethURL: ws://localhost:8546 chainName: geth1 networkID: 10 chainID: 10 diff --git a/examples/cactus-example-discounted-asset-trade/config/validator-registry-config.yaml b/examples/cactus-example-discounted-asset-trade/config/validator-registry-config.yaml index 62685978701..bec47f1df36 100644 --- a/examples/cactus-example-discounted-asset-trade/config/validator-registry-config.yaml +++ b/examples/cactus-example-discounted-asset-trade/config/validator-registry-config.yaml @@ -1,34 +1,4 @@ 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: 3PfTJw8g validatorType: legacy-socketio validatorURL: http://indy-validator-nginx:10080 diff --git a/examples/cactus-example-discounted-asset-trade/docker-compose.yml b/examples/cactus-example-discounted-asset-trade/docker-compose.yml index b813f0c6aa9..626f2804396 100644 --- a/examples/cactus-example-discounted-asset-trade/docker-compose.yml +++ b/examples/cactus-example-discounted-asset-trade/docker-compose.yml @@ -12,23 +12,6 @@ services: context: ../../packages/cactus-cmd-socketio-server/ command: ["echo", "OK - Exit"] - ethereum-validator: - container_name: cactus-example-discounted-asset-trade-ethereum-validator - image: cactus-plugin-ledger-connector-go-ethereum-socketio - build: - context: ../../packages/cactus-plugin-ledger-connector-go-ethereum-socketio/ - ports: - - "5050:5050" - depends_on: - - cmd-socketio-base-image - volumes: - - type: bind - source: ./etc/cactus - target: /etc/cactus - networks: - - geth1net - - cactus-example-discounted-asset-trade-net - indy-sdk-cli-base-image: # Build base image and immediately exit container_name: indy-sdk-cli-base-dummy @@ -81,7 +64,6 @@ services: networks: - cactus-example-discounted-asset-trade-net depends_on: - - ethereum-validator - indy-validator-nginx - cmd-socketio-base-image - indy-sdk-cli-base-image @@ -111,8 +93,6 @@ services: networks: fabric-all-in-one_testnet-2x: external: true - geth1net: - external: true indy-testnet_indy_net: external: true cactus-example-discounted-asset-trade-net: diff --git a/examples/cactus-example-discounted-asset-trade/ethereum-connector.ts b/examples/cactus-example-discounted-asset-trade/ethereum-connector.ts new file mode 100644 index 00000000000..a3d6d6c7c7a --- /dev/null +++ b/examples/cactus-example-discounted-asset-trade/ethereum-connector.ts @@ -0,0 +1,141 @@ +import { PluginRegistry } from "@hyperledger/cactus-core"; +import { IListenOptions, Servers } from "@hyperledger/cactus-common"; +import { Constants, Configuration } from "@hyperledger/cactus-core-api"; +import { ConfigUtil } from "@hyperledger/cactus-cmd-socketio-server"; +import { + PluginLedgerConnectorEthereum, + EthereumApiClient, +} from "@hyperledger/cactus-plugin-ledger-connector-ethereum"; +import { PluginKeychainMemory } from "@hyperledger/cactus-plugin-keychain-memory"; + +import http from "http"; +import express from "express"; +import bodyParser from "body-parser"; +import { AddressInfo } from "net"; +import { v4 as uuidv4 } from "uuid"; +import { getLogger } from "log4js"; +import { Server as SocketIoServer } from "socket.io"; + +const config: any = ConfigUtil.getConfig(); +const moduleName = "ethereum-connector"; +const logger = getLogger(`${moduleName}`); +logger.level = config.logLevel; + +const keychainId = uuidv4(); + +// Single Ethereum connector instance +let ethereumConnectorPlugin: PluginLedgerConnectorEthereum | undefined = + undefined; +let ethereumApiClient: EthereumApiClient | undefined = undefined; + +async function createEthereumConnector() { + if (ethereumConnectorPlugin) { + ethereumConnectorPlugin.shutdown(); + ethereumConnectorPlugin = undefined; + } + + // Create empty Keychain Plugin + const keychainPlugin = new PluginKeychainMemory({ + instanceId: uuidv4(), + keychainId, + logLevel: config.logLevel, + backend: new Map(), + }); + + ethereumConnectorPlugin = new PluginLedgerConnectorEthereum({ + instanceId: `ethAssetTrade-${uuidv4()}`, + rpcApiHttpHost: config.assetTradeInfo.ethereum.gethURL, // TODO - remove + rpcApiWsHost: config.assetTradeInfo.ethereum.gethURL, + logLevel: config.logLevel, + pluginRegistry: new PluginRegistry({ plugins: [keychainPlugin] }), + }); + + await ethereumConnectorPlugin.onPluginInit(); + + // Run http server + const expressApp = express(); + expressApp.use(bodyParser.json({ limit: "250mb" })); + const connectorServer = http.createServer(expressApp); + const listenOptions: IListenOptions = { + hostname: "127.0.0.1", + port: 0, + server: connectorServer, + }; + const addressInfo = (await Servers.listen(listenOptions)) as AddressInfo; + const apiHost = `http://${addressInfo.address}:${addressInfo.port}`; + + // Run socketio server + const socketioServer = new SocketIoServer(connectorServer, { + path: Constants.SocketIoConnectionPathV1, + }); + + // Register services + await ethereumConnectorPlugin.getOrCreateWebServices(); + await ethereumConnectorPlugin.registerWebServices(expressApp, socketioServer); + + // Create ApiClient + const apiConfig = new Configuration({ basePath: apiHost }); + ethereumApiClient = new EthereumApiClient(apiConfig); +} + +/** + * Get latest block data. Can be used to test ethereum connection. + */ +async function getLatestBlock(): Promise { + if (!ethereumConnectorPlugin) { + throw new Error("getLatestBlock() called before initEthereumConnector()!"); + } + + return ethereumConnectorPlugin.invokeRawWeb3EthMethod({ + methodName: "getBlock", + params: ["latest"], + }); +} + +/** + * Create ethereum connector and check if connection can be established + */ +export async function initEthereumConnector(): Promise { + if (!ethereumConnectorPlugin) { + await createEthereumConnector(); + + const latestBlockResponse = await getLatestBlock(); + if (!latestBlockResponse.hash) { + throw new Error( + `Invalid getLatestBlock response: ${latestBlockResponse}`, + ); + } + + logger.info("initEthereumConnector() done."); + } else { + logger.info( + "initEthereumConnector() Ethereum connector already initialized", + ); + } +} + +/** + * Get instance of ethereum connector, initialize it if not done yet. + */ +export async function getEthereumConnector(): Promise { + if (!ethereumConnectorPlugin) { + await initEthereumConnector(); + } + + if (ethereumConnectorPlugin) { + return ethereumConnectorPlugin; + } else { + throw new Error("Could not initialize new ethereum connector!"); + } +} + +/** + * Get instance of ethereum api client. + */ +export function getEthereumApiClient(): EthereumApiClient { + if (ethereumApiClient) { + return ethereumApiClient; + } else { + throw new Error("Ethereum connector not initialized yet!"); + } +} diff --git a/examples/cactus-example-discounted-asset-trade/fabric-connector.ts b/examples/cactus-example-discounted-asset-trade/fabric-connector.ts index 53d0c9590b0..841d12999c1 100644 --- a/examples/cactus-example-discounted-asset-trade/fabric-connector.ts +++ b/examples/cactus-example-discounted-asset-trade/fabric-connector.ts @@ -184,7 +184,7 @@ async function createFabricConnector(signerIdentity: FabricIdentity) { }); fabricConnectorPlugin = new PluginLedgerConnectorFabric({ - instanceId: "cactus-example-discounted-asset-trade", + instanceId: `fabricAssetTrade-${uuidv4()}`, pluginRegistry: new PluginRegistry({ plugins: [keychainPlugin] }), sshConfig: {}, // Provide SSH config to deploy contracts through connector cliContainerEnv: {}, diff --git a/examples/cactus-example-discounted-asset-trade/package.json b/examples/cactus-example-discounted-asset-trade/package.json index 166da2b7bb9..cc1b44f09c6 100644 --- a/examples/cactus-example-discounted-asset-trade/package.json +++ b/examples/cactus-example-discounted-asset-trade/package.json @@ -20,6 +20,7 @@ "@hyperledger/cactus-core": "2.0.0-alpha.1", "@hyperledger/cactus-core-api": "2.0.0-alpha.1", "@hyperledger/cactus-plugin-keychain-memory": "2.0.0-alpha.1", + "@hyperledger/cactus-plugin-ledger-connector-ethereum": "2.0.0-alpha.1", "@hyperledger/cactus-plugin-ledger-connector-fabric": "2.0.0-alpha.1", "@hyperledger/cactus-verifier-client": "2.0.0-alpha.1", "@types/node": "14.18.54", @@ -42,7 +43,6 @@ "shelljs": "0.8.5", "socket.io": "4.5.4", "ts-node": "8.9.1", - "web3": "1.8.1", "xmlhttprequest": "1.8.0" }, "devDependencies": { @@ -50,6 +50,7 @@ "@types/escape-html": "1.0.1", "@types/express": "4.17.13", "@types/jsonwebtoken": "9.0.2", - "@types/jsrsasign": "10.5.8" + "@types/jsrsasign": "10.5.8", + "rxjs": "7.8.1" } } diff --git a/examples/cactus-example-discounted-asset-trade/transaction-ethereum.ts b/examples/cactus-example-discounted-asset-trade/transaction-ethereum.ts index 060d5938558..e2cd3833de4 100644 --- a/examples/cactus-example-discounted-asset-trade/transaction-ethereum.ts +++ b/examples/cactus-example-discounted-asset-trade/transaction-ethereum.ts @@ -7,14 +7,9 @@ import { ConfigUtil, - LPInfoHolder, TransactionSigner, } from "@hyperledger/cactus-cmd-socketio-server"; - -import { - VerifierFactory, - VerifierFactoryConfig, -} from "@hyperledger/cactus-verifier-client"; +import { Web3SigningCredentialType } from "@hyperledger/cactus-plugin-ledger-connector-ethereum"; const config: any = ConfigUtil.getConfig(); import { getLogger } from "log4js"; @@ -23,127 +18,71 @@ const logger = getLogger(`${moduleName}`); logger.level = config.logLevel; const mapFromAddressNonce: Map = new Map(); -const xConnectInfo = new LPInfoHolder(); -const verifierFactory = new VerifierFactory( - xConnectInfo.ledgerPluginInfo as VerifierFactoryConfig, - config.logLevel, -); - -export function makeRawTransaction(txParam: { - fromAddress: string; - fromAddressPkey: string; - toAddress: string; - amount: number; - gas: number; -}): Promise<{ - data: { serializedTx: string }; - 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: 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: { 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}`); - - // 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 args = { args: { args: [fromAddress] } }; - - logger.debug(`##getNewNonce(A): call validator#getNonce()`); - - verifierFactory - .getVerifier("84jUisrs") - .sendSyncRequest(contract, method, args) - .then((result) => { - let txnCount: number = result.data.nonce; - let txnCountHex: string = result.data.nonceHex; - - const latestNonce = getLatestNonce(fromAddress); - if (latestNonce) { - if (txnCount <= latestNonce && 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()`); - verifierFactory - .getVerifier("84jUisrs") - .sendSyncRequest(contract, method, args) - .then((result) => { - txnCountHex = result.data.hexStr; - logger.debug(`##getNewNonce(E): txnCountHex: ${txnCountHex}`); - setLatestNonce(fromAddress, txnCount); - - return resolve({ txnCountHex: txnCountHex }); - }); - } else { - setLatestNonce(fromAddress, txnCount); - - logger.debug(`##getNewNonce(G): txnCountHex: ${txnCountHex}`); - return resolve({ txnCountHex: txnCountHex }); - } - } - }); - } catch (err) { - logger.error(err); - return reject(err); - } +import { getEthereumConnector } from "./ethereum-connector"; + +export async function sendEthereumTransaction( + transaction: any, + from: string, + privateKey: string, +) { + const connector = await getEthereumConnector(); + + if (!("nonce" in transaction)) { + const nonce = await getNewNonce(from); + transaction = { + ...transaction, + nonce, + }; + } + logger.debug("sendEthereumTransaction", transaction); + + const { serializedTx, txId } = TransactionSigner.signTxEthereum( + transaction, + privateKey, + ); + logger.info("Sending ethereum transaction with ID", txId); + + return connector.transact({ + web3SigningCredential: { + type: Web3SigningCredentialType.None, + }, + transactionConfig: { + rawTransaction: serializedTx, + }, }); } -function getLatestNonce(fromAddress: string): number | undefined { - if (mapFromAddressNonce.has(fromAddress)) { - return mapFromAddressNonce.get(fromAddress); +export async function getNewNonce(fromAddress: string): Promise { + logger.info("Get current nonce for account", fromAddress); + const connector = await getEthereumConnector(); + + let nonce = (await connector.invokeRawWeb3EthMethod({ + methodName: "getTransactionCount", + params: [fromAddress], + })) as number; + const latestNonce = mapFromAddressNonce.get(fromAddress); + + if (latestNonce && nonce <= latestNonce) { + // nonce correction + nonce = latestNonce + 1; + logger.debug( + `##getNewNonce(C): Adjust txnCount, fromAddress: ${fromAddress}, nonce: ${nonce}`, + ); } - return -1; + logger.debug(`##getNewNonce(G): txnCountHex: ${nonce}`); + mapFromAddressNonce.set(fromAddress, nonce); + return nonce; } -function setLatestNonce(fromAddress: string, nonce: number): void { - mapFromAddressNonce.set(fromAddress, nonce); +export async function getAccountBalance(address: string): Promise { + logger.info("Get account balance", address); + const connector = await getEthereumConnector(); + + const balance = (await connector.invokeRawWeb3EthMethod({ + methodName: "getBalance", + params: [address], + })) as number; + return balance; } diff --git a/examples/cactus-example-discounted-asset-trade/transaction-info-management.ts b/examples/cactus-example-discounted-asset-trade/transaction-info-management.ts index 16e42d1cee2..44f314f74f8 100644 --- a/examples/cactus-example-discounted-asset-trade/transaction-info-management.ts +++ b/examples/cactus-example-discounted-asset-trade/transaction-info-management.ts @@ -62,9 +62,8 @@ export class TransactionInfoManagement { ); transactionInfoTable = JSON.parse(transactionInfoFileData); transactionInfoTable.table.push(transactionInfoJson); - const transactionInfoTableJson: string = JSON.stringify( - transactionInfoTable, - ); + const transactionInfoTableJson: string = + JSON.stringify(transactionInfoTable); fs.writeFileSync(this.fileName, transactionInfoTableJson, "utf8"); this.fileDump(); diff --git a/examples/cactus-example-discounted-asset-trade/tsconfig.json b/examples/cactus-example-discounted-asset-trade/tsconfig.json index a036fd9592d..8fc752a3bc9 100644 --- a/examples/cactus-example-discounted-asset-trade/tsconfig.json +++ b/examples/cactus-example-discounted-asset-trade/tsconfig.json @@ -26,6 +26,9 @@ { "path": "../../packages/cactus-plugin-ledger-connector-fabric/tsconfig.json" }, + { + "path": "../../packages/cactus-plugin-ledger-connector-ethereum/tsconfig.json" + }, { "path": "../../packages/cactus-verifier-client/tsconfig.json" } diff --git a/examples/cactus-example-discounted-asset-trade/www.ts b/examples/cactus-example-discounted-asset-trade/www.ts index 4d204ed4a28..9067e44f70e 100644 --- a/examples/cactus-example-discounted-asset-trade/www.ts +++ b/examples/cactus-example-discounted-asset-trade/www.ts @@ -1,10 +1,12 @@ import { BusinessLogicAssetTrade } from "./business-logic-asset-trade"; import { startCactusSocketIOServer } from "@hyperledger/cactus-cmd-socketio-server"; import { initFabricConnector } from "./fabric-connector"; +import { initEthereumConnector } from "./ethereum-connector"; async function startBLP() { try { await initFabricConnector(); + await initEthereumConnector(); startCactusSocketIOServer({ id: "guks32pf", diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/run-transaction-with-identities.test.ts b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/run-transaction-with-identities.test.ts index cbeee10dd51..c1dd83b7777 100644 --- a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/run-transaction-with-identities.test.ts +++ b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/run-transaction-with-identities.test.ts @@ -313,7 +313,7 @@ test("run-transaction-with-identities", async (t: Test) => { t.comment(out); { // make invoke InitLedger using a client1 client - const resp = await plugin.transact({ + await plugin.transact({ signingCredential: { keychainId: keychainId, keychainRef: client1Key, @@ -327,7 +327,7 @@ test("run-transaction-with-identities", async (t: Test) => { } { // make invoke TransferAsset using a client2 client - const resp = await plugin.transact({ + await plugin.transact({ signingCredential: { keychainId: keychainId, keychainRef: client2Key, diff --git a/yarn.lock b/yarn.lock index 8bcad3ecf84..5204eddecff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5951,6 +5951,7 @@ __metadata: "@hyperledger/cactus-core": 2.0.0-alpha.1 "@hyperledger/cactus-core-api": 2.0.0-alpha.1 "@hyperledger/cactus-plugin-keychain-memory": 2.0.0-alpha.1 + "@hyperledger/cactus-plugin-ledger-connector-ethereum": 2.0.0-alpha.1 "@hyperledger/cactus-plugin-ledger-connector-fabric": 2.0.0-alpha.1 "@hyperledger/cactus-verifier-client": 2.0.0-alpha.1 "@types/elliptic": 6.4.14 @@ -5975,10 +5976,10 @@ __metadata: jsrsasign: 10.5.25 log4js: 6.4.0 morgan: 1.9.1 + rxjs: 7.8.1 shelljs: 0.8.5 socket.io: 4.5.4 ts-node: 8.9.1 - web3: 1.8.1 xmlhttprequest: 1.8.0 languageName: unknown linkType: soft