From dcaf9fe5de5b830975f3d308f140eff32d3cb79d Mon Sep 17 00:00:00 2001 From: Michal Bajer Date: Tue, 17 Oct 2023 11:21:08 +0000 Subject: [PATCH] feat(cactus-example-discounted-asset-trade): use openapi ethereum connector - Refactor discounted asset trade sample to use openapi ethereum connector instead of ethereum-socketio. - Use eth signing utils from ethereum connector package. - Fix nodejs setup in indy-sdk-cli docker image. - Small fixes after recent ethereum connector refactors. Depends on: #2645 Signed-off-by: Michal Bajer --- .../README.md | 20 +- .../balance-management.ts | 62 ---- .../balance.ts | 8 +- .../business-logic-asset-trade.ts | 294 +++--------------- ...siness-logic-inquire-asset-trade-status.ts | 13 +- .../config/usersetting.yaml | 11 +- .../config/validator-registry-config.yaml | 37 --- .../docker-compose.yml | 20 -- .../ethereum-connector.ts | 140 +++++++++ .../fabric-connector.ts | 2 +- .../package.json | 2 +- .../transaction-ethereum.ts | 206 +++++------- .../transaction-info-management.ts | 5 +- .../tsconfig.json | 3 + .../www.ts | 2 + .../ethereum-connector.ts | 1 - tools/docker/indy-sdk-cli/Dockerfile | 3 +- yarn.lock | 2 +- 18 files changed, 299 insertions(+), 532 deletions(-) delete mode 100644 examples/cactus-example-discounted-asset-trade/balance-management.ts create mode 100644 examples/cactus-example-discounted-asset-trade/ethereum-connector.ts diff --git a/examples/cactus-example-discounted-asset-trade/README.md b/examples/cactus-example-discounted-asset-trade/README.md index afcd48ecb3..b0aa4c0630 100644 --- a/examples/cactus-example-discounted-asset-trade/README.md +++ b/examples/cactus-example-discounted-asset-trade/README.md @@ -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 } + 1e+26 # Ethereum escrowAccount: - { status: 200, amount: 0 } + 0 # Ethereum toAccount: - { status: 200, amount: 0 } + 0 # Fabric: @@ -231,13 +227,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 9f58d471f4..0000000000 --- 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 { ISendRequestResultV1 } from "@hyperledger/cactus-core-api"; -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 = 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 res1 = result as ISendRequestResultV1; - const response = { - status: res1.status, - amount: parseFloat(res1.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 403c1b9bcd..cba1d4bb09 100644 --- a/examples/cactus-example-discounted-asset-trade/balance.ts +++ b/examples/cactus-example-discounted-asset-trade/balance.ts @@ -8,25 +8,23 @@ 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)); + logger.debug("result(getBalance) = " + result); res.status(200).json(result); }) .catch((err) => { 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 24cd7b3bc2..0867547f17 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,15 +23,11 @@ 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 { Web3TransactionReceipt } from "@hyperledger/cactus-plugin-ledger-connector-ethereum"; const config: any = ConfigUtil.getConfig(); import { getLogger } from "log4js"; @@ -43,37 +36,17 @@ import { getFabricApiClient, getSignerIdentity, } from "./fabric-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 +59,6 @@ interface TransactionStatusData { export class BusinessLogicAssetTrade extends BusinessLogicBase { transactionInfoManagement: TransactionInfoManagement; - // useValidator: {}; constructor() { super(); @@ -323,29 +295,15 @@ 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 + // Send funds to escrow logger.debug( `####fromAddress: ${requestInfo.tradeInfo.ethereumAccountFrom}`, ); @@ -354,57 +312,37 @@ 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), + gasLimit: 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); - }); + this.executeNextTransaction(result.transactionReceipt, txId); }) .catch((err) => { - logger.error(err); + logger.error("sendEthereumTransaction Escrow ERROR:", err); }); } @@ -419,13 +357,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 +416,40 @@ 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), + gasLimit: 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); - }); + this.executeNextTransaction(result.transactionReceipt, txId); }) .catch((err) => { - logger.error(err); + logger.error("sendEthereumTransaction Final ERROR:", err); }); } @@ -565,69 +465,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 | Web3TransactionReceipt, txId: string, ): void { let transactionInfo: TransactionInfo | null = null; @@ -741,53 +584,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 bac25435da..8a07478e15 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 d5c4d2983b..dafa0f2206 100644 --- a/examples/cactus-example-discounted-asset-trade/config/usersetting.yaml +++ b/examples/cactus-example-discounted-asset-trade/config/usersetting.yaml @@ -1,10 +1,17 @@ # Overwrite defaults blpRegistry: - businessLogicID: guks32pf - validatorID: [84jUisrs] + validatorID: [] logLevel: debug +signTxInfo: + ethereum: + chainName: geth1 + networkID: 10 + chainID: 10 + defaultHardfork: petersburg + appRouters: - path: /api/v1/bl/trades/ routerJs: /root/cactus/dist/trades.js @@ -41,7 +48,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 6268597870..6dfa188b3b 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 @@ -44,13 +14,6 @@ ledgerPluginInfo: apiInfo: [] signTxInfo: - ethereum: - chainName: geth1 - networkID: 10 - chainID: 10 - network: mainnet - hardfork: petersburg - fabric: mspID: Org1MSP peers: diff --git a/examples/cactus-example-discounted-asset-trade/docker-compose.yml b/examples/cactus-example-discounted-asset-trade/docker-compose.yml index b813f0c6aa..626f280439 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 0000000000..10cd21d637 --- /dev/null +++ b/examples/cactus-example-discounted-asset-trade/ethereum-connector.ts @@ -0,0 +1,140 @@ +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()}`, + 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 53d0c9590b..841d12999c 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 dc678379ca..4f0337a403 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.2", "@hyperledger/cactus-core-api": "2.0.0-alpha.2", "@hyperledger/cactus-plugin-keychain-memory": "2.0.0-alpha.2", + "@hyperledger/cactus-plugin-ledger-connector-ethereum": "2.0.0-alpha.2", "@hyperledger/cactus-plugin-ledger-connector-fabric": "2.0.0-alpha.2", "@hyperledger/cactus-verifier-client": "2.0.0-alpha.2", "@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": { diff --git a/examples/cactus-example-discounted-asset-trade/transaction-ethereum.ts b/examples/cactus-example-discounted-asset-trade/transaction-ethereum.ts index 793b46ff48..deaec3799d 100644 --- a/examples/cactus-example-discounted-asset-trade/transaction-ethereum.ts +++ b/examples/cactus-example-discounted-asset-trade/transaction-ethereum.ts @@ -5,17 +5,11 @@ * transaction-ethereum.ts */ +import { ConfigUtil } from "@hyperledger/cactus-cmd-socketio-server"; import { - ConfigUtil, - LPInfoHolder, - TransactionSigner, -} from "@hyperledger/cactus-cmd-socketio-server"; -import { ISendRequestResultV1 } from "@hyperledger/cactus-core-api"; - -import { - VerifierFactory, - VerifierFactoryConfig, -} from "@hyperledger/cactus-verifier-client"; + Web3SigningCredentialType, + signTransaction, +} from "@hyperledger/cactus-plugin-ledger-connector-ethereum"; const config: any = ConfigUtil.getConfig(); import { getLogger } from "log4js"; @@ -24,135 +18,83 @@ 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); - } +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 { serializedTransactionHex, txId } = signTransaction( + transaction, + privateKey, + { + name: config.signTxInfo.ethereum.chainName, + chainId: config.signTxInfo.ethereum.chainID, + networkId: config.signTxInfo.ethereum.networkID, + defaultHardfork: config.signTxInfo.ethereum.defaultHardfork, + }, + ); + logger.info("Sending ethereum transaction with ID", txId); + + return connector.transact({ + web3SigningCredential: { + type: Web3SigningCredentialType.None, + }, + transactionConfig: { + rawTransaction: serializedTransactionHex, + }, }); } -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) => { - const res1 = result as ISendRequestResultV1<{ - readonly nonce: number; - readonly nonceHex: string; - }>; - - let txnCount = res1.data.nonce; - let txnCountHex: string = res1.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) => { - const res2 = result as ISendRequestResultV1<{ - readonly hexStr: string; - }>; - txnCountHex = res2.data.hexStr; - logger.debug(`##getNewNonce(E): txnCountHex: ${txnCountHex}`); - setLatestNonce(fromAddress, txnCount); - - return resolve({ txnCountHex }); - }); - } else { - setLatestNonce(fromAddress, txnCount); +export async function getNewNonce(fromAddress: string): Promise { + logger.info("Get current nonce for account", fromAddress); + const connector = await getEthereumConnector(); + + const nonceBigInt = (await connector.invokeRawWeb3EthMethod({ + methodName: "getTransactionCount", + params: [fromAddress], + })) as bigint; + let nonce = Number(nonceBigInt); + const latestNonce = mapFromAddressNonce.get(fromAddress); + + if (latestNonce && nonce <= latestNonce) { + // nonce correction + nonce = latestNonce + 1; + logger.debug( + `##getNewNonce(C): Adjust txnCount, fromAddress: ${fromAddress}, nonce: ${nonce}`, + ); + } - logger.debug(`##getNewNonce(G): txnCountHex: ${txnCountHex}`); - return resolve({ txnCountHex: txnCountHex }); - } - } - }); - } catch (err) { - logger.error(err); - return reject(err); - } - }); + logger.debug(`##getNewNonce(G): txnCountHex: ${nonce}`); + mapFromAddressNonce.set(fromAddress, nonce); + return nonce; } -function getLatestNonce(fromAddress: string): number | undefined { - if (mapFromAddressNonce.has(fromAddress)) { - return mapFromAddressNonce.get(fromAddress); - } +export async function getAccountBalance(address: string): Promise { + logger.info("Get account balance", address); + const connector = await getEthereumConnector(); - return -1; -} + if (!address.toLowerCase().startsWith("0x")) { + address = "0x" + address; + logger.debug("Added hex prefix to address:", address); + } -function setLatestNonce(fromAddress: string, nonce: number): void { - mapFromAddressNonce.set(fromAddress, nonce); + const balance = (await connector.invokeRawWeb3EthMethod({ + methodName: "getBalance", + params: [address], + })) as bigint; + return Number(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 16e42d1cee..44f314f74f 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 a036fd9592..8fc752a3bc 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 4d204ed4a2..9067e44f70 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/examples/cactus-example-electricity-trade/ethereum-connector.ts b/examples/cactus-example-electricity-trade/ethereum-connector.ts index 6ed8043139..47eaba0d16 100644 --- a/examples/cactus-example-electricity-trade/ethereum-connector.ts +++ b/examples/cactus-example-electricity-trade/ethereum-connector.ts @@ -44,7 +44,6 @@ async function createEthereumConnector() { ethereumConnectorPlugin = new PluginLedgerConnectorEthereum({ instanceId: `ethElectricityTrade-${uuidv4()}`, - rpcApiHttpHost: config.electricityTradeInfo.ethereum.gethURL, // TODO - remove rpcApiWsHost: config.electricityTradeInfo.ethereum.gethURL, logLevel: config.logLevel, pluginRegistry: new PluginRegistry({ plugins: [keychainPlugin] }), diff --git a/tools/docker/indy-sdk-cli/Dockerfile b/tools/docker/indy-sdk-cli/Dockerfile index 4c81c38a21..d7cf5c01e2 100644 --- a/tools/docker/indy-sdk-cli/Dockerfile +++ b/tools/docker/indy-sdk-cli/Dockerfile @@ -23,8 +23,9 @@ RUN apt-get update \ && rm -rf /var/lib/apt/lists/* # NodeJS - Updated to use the new NodeSource installation method +ENV NODE_MAJOR=16 RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /usr/share/keyrings/nodesource-archive-keyring.gpg \ - && echo "deb [arch=amd64 signed-by=/usr/share/keyrings/nodesource-archive-keyring.gpg] https://deb.nodesource.com/node bionic main" | tee /etc/apt/sources.list.d/nodesource.list + && echo "deb [arch=amd64 signed-by=/usr/share/keyrings/nodesource-archive-keyring.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list # Indy-sdk RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CE7709D068DB5E88 \ diff --git a/yarn.lock b/yarn.lock index 11f4b800fe..69ba841277 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7135,6 +7135,7 @@ __metadata: "@hyperledger/cactus-core": 2.0.0-alpha.2 "@hyperledger/cactus-core-api": 2.0.0-alpha.2 "@hyperledger/cactus-plugin-keychain-memory": 2.0.0-alpha.2 + "@hyperledger/cactus-plugin-ledger-connector-ethereum": 2.0.0-alpha.2 "@hyperledger/cactus-plugin-ledger-connector-fabric": 2.0.0-alpha.2 "@hyperledger/cactus-verifier-client": 2.0.0-alpha.2 "@types/elliptic": 6.4.14 @@ -7162,7 +7163,6 @@ __metadata: 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