diff --git a/examples/cactus-example-electricity-trade/BusinessLogicElectricityTrade.ts b/examples/cactus-example-electricity-trade/BusinessLogicElectricityTrade.ts index eae622ec5e9..84f693aca28 100644 --- a/examples/cactus-example-electricity-trade/BusinessLogicElectricityTrade.ts +++ b/examples/cactus-example-electricity-trade/BusinessLogicElectricityTrade.ts @@ -126,7 +126,7 @@ export class BusinessLogicElectricityTrade extends BusinessLogicBase { { to: accountInfo["toAddress"], value: Number(transactionSubset["Value"]), - gas: config.electricityTradeInfo.ethereum.gas, + gasLimit: config.electricityTradeInfo.ethereum.gas, }, accountInfo["fromAddress"], accountInfo["fromAddressPkey"], diff --git a/examples/cactus-example-electricity-trade/TransactionEthereum.ts b/examples/cactus-example-electricity-trade/TransactionEthereum.ts index 227ddac4ac1..deaec3799d7 100644 --- a/examples/cactus-example-electricity-trade/TransactionEthereum.ts +++ b/examples/cactus-example-electricity-trade/TransactionEthereum.ts @@ -5,11 +5,11 @@ * transaction-ethereum.ts */ +import { ConfigUtil } from "@hyperledger/cactus-cmd-socketio-server"; import { - ConfigUtil, - TransactionSigner, -} from "@hyperledger/cactus-cmd-socketio-server"; -import { Web3SigningCredentialType } from "@hyperledger/cactus-plugin-ledger-connector-ethereum"; + Web3SigningCredentialType, + signTransaction, +} from "@hyperledger/cactus-plugin-ledger-connector-ethereum"; const config: any = ConfigUtil.getConfig(); import { getLogger } from "log4js"; @@ -37,9 +37,15 @@ export async function sendEthereumTransaction( } logger.debug("sendEthereumTransaction", transaction); - const { serializedTx, txId } = TransactionSigner.signTxEthereum( + 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); @@ -48,7 +54,7 @@ export async function sendEthereumTransaction( type: Web3SigningCredentialType.None, }, transactionConfig: { - rawTransaction: serializedTx, + rawTransaction: serializedTransactionHex, }, }); } diff --git a/examples/cactus-example-electricity-trade/config/usersetting.yaml b/examples/cactus-example-electricity-trade/config/usersetting.yaml index 99ac3004e9c..13604fb7b9d 100644 --- a/examples/cactus-example-electricity-trade/config/usersetting.yaml +++ b/examples/cactus-example-electricity-trade/config/usersetting.yaml @@ -6,6 +6,13 @@ blpRegistry: logLevel: debug +signTxInfo: + ethereum: + chainName: geth1 + networkID: 10 + chainID: 10 + defaultHardfork: petersburg + appRouters: - path: /api/v1/bl/electricity-trade/ diff --git a/examples/cactus-example-electricity-trade/config/validator-registry-config.yaml b/examples/cactus-example-electricity-trade/config/validator-registry-config.yaml index 2dae4c51204..6f2f49a8797 100644 --- a/examples/cactus-example-electricity-trade/config/validator-registry-config.yaml +++ b/examples/cactus-example-electricity-trade/config/validator-registry-config.yaml @@ -13,11 +13,3 @@ ledgerPluginInfo: ledgerInfo: ledgerAbstract: Sawtooth Ledger apiInfo: [] - -signTxInfo: - ethereum: - chainName: geth1 - networkID: 10 - chainID: 10 - network: mainnet - hardfork: petersburg diff --git a/packages/cactus-plugin-ledger-connector-ethereum/README.md b/packages/cactus-plugin-ledger-connector-ethereum/README.md index 52533de45dd..1d6b60acd01 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/README.md +++ b/packages/cactus-plugin-ledger-connector-ethereum/README.md @@ -147,6 +147,40 @@ args: { }, ``` +### Offline signing utils +- Use `signTransaction` from this package to sign transactions payload locally (outside of connector process). +- Offline signed transaction can be send with `Web3SigningCredentialType.None` signing credetnial type in runTransactionV1 endpoint. + +``` typescript +// Offline sign transaction +const { serializedTransactionHex } = signTransaction( + { + to: anotherAccount, + value: 10e6, + maxPriorityFeePerGas: 0, + maxFeePerGas: 0x40000000, + gasLimit: 21000, + type: 2 + }, + myPrivateKey, + { + networkId: 10, + chainId: 10, + defaultHardfork: "london", + }, +); + +// Send transaction payload to connector +await apiClient.runTransactionV1({ + web3SigningCredential: { + type: Web3SigningCredentialType.None, + }, + transactionConfig: { + rawTransaction: serializedTransactionHex, + }, +}); +``` + ## Running the tests To check that all has been installed correctly and that the pugin has no errors run jest test suites. diff --git a/packages/cactus-plugin-ledger-connector-ethereum/package.json b/packages/cactus-plugin-ledger-connector-ethereum/package.json index f053715ded1..1f0354858d9 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/package.json +++ b/packages/cactus-plugin-ledger-connector-ethereum/package.json @@ -62,6 +62,8 @@ "webpack:dev:web": "webpack --env=dev --target=web --config ../../webpack.config.js" }, "dependencies": { + "@ethereumjs/common": "4.0.0", + "@ethereumjs/tx": "5.0.0", "@hyperledger/cactus-common": "2.0.0-alpha.2", "@hyperledger/cactus-core": "2.0.0-alpha.2", "@hyperledger/cactus-core-api": "2.0.0-alpha.2", diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/public-api.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/public-api.ts index 22a80892364..56d5b0cfbb6 100755 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/public-api.ts +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/public-api.ts @@ -5,6 +5,8 @@ export { IPluginLedgerConnectorEthereumOptions, } from "./plugin-ledger-connector-ethereum"; +export * from "./sign-utils"; + export * from "./types/model-type-guards"; export { PluginFactoryLedgerConnector } from "./plugin-factory-ledger-connector"; diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/sign-utils.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/sign-utils.ts new file mode 100644 index 00000000000..66827c879dd --- /dev/null +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/sign-utils.ts @@ -0,0 +1,59 @@ +import { Chain, Common } from "@ethereumjs/common"; +import { + AccessListEIP2930Transaction, + BlobEIP4844Transaction, + FeeMarketEIP1559Transaction, + LegacyTransaction, + TransactionFactory, + TypedTxData, +} from "@ethereumjs/tx"; + +type CustomChainArg = Parameters[0]; + +/** + * Sign ethereum transaction data offline. Can be used with both mainnet and custom chains. + * + * @param txData transaction data (format must be compatible with @ethereumjs/tx) + * @param privateKey HEX private signing key + * @param customChainInfo optional custom chain information (default: mainnet) + * + * @returns serialized transaction, txId (hash) and signedTransaction object + */ +export function signTransaction( + txData: TypedTxData, + privateKey: string, + customChainInfo?: CustomChainArg, +): { + serializedTransactionHex: string; + txId: string; + signedTransactionObject: + | LegacyTransaction + | AccessListEIP2930Transaction + | FeeMarketEIP1559Transaction + | BlobEIP4844Transaction; +} { + let chainConfiguration = new Common({ chain: Chain.Mainnet }); + if (customChainInfo) { + chainConfiguration = Common.custom(customChainInfo); + } + + const transaction = TransactionFactory.fromTxData(txData, { + common: chainConfiguration, + }); + if (privateKey.toLowerCase().startsWith("0x")) { + privateKey = privateKey.slice(2); + } + const signedTransaction = transaction.sign(Buffer.from(privateKey, "hex")); + + const hash = signedTransaction.hash(); + const txId = "0x" + Buffer.from(hash).toString("hex"); + const serializedTransaction = signedTransaction.serialize(); + const serializedTransactionHex = + "0x" + Buffer.from(serializedTransaction).toString("hex"); + + return { + serializedTransactionHex, + txId, + signedTransactionObject: signedTransaction, + }; +} \ No newline at end of file diff --git a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/geth-contract-deploy-and-invoke-using-keychain-v1.test.ts b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/geth-contract-deploy-and-invoke-using-keychain-v1.test.ts index 6fba7f0bdc2..45800e1739b 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/geth-contract-deploy-and-invoke-using-keychain-v1.test.ts +++ b/packages/cactus-plugin-ledger-connector-ethereum/src/test/typescript/integration/geth-contract-deploy-and-invoke-using-keychain-v1.test.ts @@ -45,6 +45,7 @@ import { DeployContractSolidityBytecodeV1Request, RunTransactionRequest, InvokeContractV1Request, + signTransaction, } from "../../../main/typescript/public-api"; import { K_CACTUS_ETHEREUM_TOTAL_TX_COUNT } from "../../../main/typescript/prometheus-exporter/metrics"; @@ -336,20 +337,25 @@ describe("Ethereum contract deploy and invoke using keychain tests", () => { expect(invokeGetNameOut.data.callOutput).toBe(newName); }); - test("invoke Web3SigningCredentialType.NONE", async () => { + test("invoke Web3SigningCredentialType.None", async () => { const testEthAccount2 = web3.eth.accounts.create(); const value = 10e6; - const { rawTransaction } = await web3.eth.accounts.signTransaction( + const { serializedTransactionHex } = signTransaction( { - from: testEthAccount.address, to: testEthAccount2.address, value, maxPriorityFeePerGas: 0, maxFeePerGas: 0x40000000, gasLimit: 21000, + type: 2 }, testEthAccount.privateKey, + { + networkId: 10, + chainId: 10, + defaultHardfork: "london", + }, ); await apiClient.runTransactionV1({ @@ -357,7 +363,7 @@ describe("Ethereum contract deploy and invoke using keychain tests", () => { type: Web3SigningCredentialType.None, }, transactionConfig: { - rawTransaction, + rawTransaction: serializedTransactionHex, }, }); diff --git a/yarn.lock b/yarn.lock index 24ee6cb6f45..804411e2770 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4335,6 +4335,16 @@ __metadata: languageName: node linkType: hard +"@ethereumjs/common@npm:4.0.0, @ethereumjs/common@npm:^4.0.0": + version: 4.0.0 + resolution: "@ethereumjs/common@npm:4.0.0" + dependencies: + "@ethereumjs/util": ^9.0.0 + crc: ^4.3.2 + checksum: 3fa2f6a55ff5a2406241fc1ffbdc6f60cfa139edaf9afac2164d25ca35e31a0d2901de40a517f42e5a466aab316f9187c5b787f33f3260e746b13209c910c67f + languageName: node + linkType: hard + "@ethereumjs/common@npm:^2.3.0, @ethereumjs/common@npm:^2.4.0, @ethereumjs/common@npm:^2.6.1": version: 2.6.2 resolution: "@ethereumjs/common@npm:2.6.2" @@ -4374,6 +4384,15 @@ __metadata: languageName: node linkType: hard +"@ethereumjs/rlp@npm:^5.0.0": + version: 5.0.0 + resolution: "@ethereumjs/rlp@npm:5.0.0" + bin: + rlp: bin/rlp + checksum: 86d42f88945f25fc2874f80efc4d4c37cc85fc375920134392b658c0140346fb22b815e73680b87d1fea3ae55ad6ca42ea9fec7150583be496577b01bffb9e58 + languageName: node + linkType: hard + "@ethereumjs/tx@npm:3.3.2": version: 3.3.2 resolution: "@ethereumjs/tx@npm:3.3.2" @@ -4384,6 +4403,23 @@ __metadata: languageName: node linkType: hard +"@ethereumjs/tx@npm:5.0.0": + version: 5.0.0 + resolution: "@ethereumjs/tx@npm:5.0.0" + dependencies: + "@ethereumjs/common": ^4.0.0 + "@ethereumjs/rlp": ^5.0.0 + "@ethereumjs/util": ^9.0.0 + ethereum-cryptography: ^2.1.2 + peerDependencies: + c-kzg: ^2.1.0 + peerDependenciesMeta: + c-kzg: + optional: true + checksum: 1f642781cca8251fb036ef9b77e95c8e3e8e7b1996d295e01f9875a581ccf25fa38eea270d032a8ab4cb1b6d716b50496b7400ccc37875b896912e6fd0300de8 + languageName: node + linkType: hard + "@ethereumjs/tx@npm:^3.2.1": version: 3.5.0 resolution: "@ethereumjs/tx@npm:3.5.0" @@ -4415,6 +4451,21 @@ __metadata: languageName: node linkType: hard +"@ethereumjs/util@npm:^9.0.0": + version: 9.0.0 + resolution: "@ethereumjs/util@npm:9.0.0" + dependencies: + "@ethereumjs/rlp": ^5.0.0 + ethereum-cryptography: ^2.1.2 + peerDependencies: + c-kzg: ^2.1.0 + peerDependenciesMeta: + c-kzg: + optional: true + checksum: c0e2a50aac614a8167df060f0fe6d7b9805adebd83df81a26b596c92eb76454e2e6f25d8f5b9e49cb629355020a8a07feb4d6c1b8238990dcf82e256153bf328 + languageName: node + linkType: hard + "@ethersproject/abi@npm:5.0.7": version: 5.0.7 resolution: "@ethersproject/abi@npm:5.0.7" @@ -5343,7 +5394,7 @@ __metadata: autoprefixer: 10.4.8 chart.js: 3.9.1 moment: 2.29.4 - postcss: 8.4.16 + postcss: 8.4.31 solid-apexcharts: 0.1.6 solid-icons: 1.0.4 solid-js: 1.5.7 @@ -6473,6 +6524,8 @@ __metadata: version: 0.0.0-use.local resolution: "@hyperledger/cactus-plugin-ledger-connector-ethereum@workspace:packages/cactus-plugin-ledger-connector-ethereum" dependencies: + "@ethereumjs/common": 4.0.0 + "@ethereumjs/tx": 5.0.0 "@hyperledger/cactus-common": 2.0.0-alpha.2 "@hyperledger/cactus-core": 2.0.0-alpha.2 "@hyperledger/cactus-core-api": 2.0.0-alpha.2 @@ -6761,7 +6814,7 @@ __metadata: cbor: 6.0.1 config: 3.3.7 cookie-parser: 1.4.6 - debug: 4.1.1 + debug: 4.3.1 express: 4.17.3 js-yaml: 3.14.1 jsonwebtoken: 8.5.1 @@ -17316,6 +17369,18 @@ __metadata: languageName: node linkType: hard +"crc@npm:^4.3.2": + version: 4.3.2 + resolution: "crc@npm:4.3.2" + peerDependencies: + buffer: ">=6.0.3" + peerDependenciesMeta: + buffer: + optional: true + checksum: 8231cc25331727083ffd22da3575110fc49b4dc8725de973bd43261d4426aba134ed3a75cc247f7c5e97a6e171f87dffc3325b82890e86d032de2e6bcef09c32 + languageName: node + linkType: hard + "create-ecdh@npm:^4.0.0": version: 4.0.4 resolution: "create-ecdh@npm:4.0.4" @@ -17900,12 +17965,15 @@ __metadata: languageName: node linkType: hard -"debug@npm:4.1.1": - version: 4.1.1 - resolution: "debug@npm:4.1.1" +"debug@npm:4.3.1": + version: 4.3.1 + resolution: "debug@npm:4.3.1" dependencies: - ms: ^2.1.1 - checksum: 1e681f5cce94ba10f8dde74b20b42e4d8cf0d2a6700f4c165bb3bb6885565ef5ca5885bf07e704974a835f2415ff095a63164f539988a1f07e8a69fe8b1d65ad + ms: 2.1.2 + peerDependenciesMeta: + supports-color: + optional: true + checksum: 2c3352e37d5c46b0d203317cd45ea0e26b2c99f2d9dfec8b128e6ceba90dfb65425f5331bf3020fe9929d7da8c16758e737f4f3bfc0fce6b8b3d503bae03298b languageName: node linkType: hard @@ -34257,25 +34325,25 @@ __metadata: languageName: node linkType: hard -"postcss@npm:8.4.16": - version: 8.4.16 - resolution: "postcss@npm:8.4.16" +"postcss@npm:8.4.27": + version: 8.4.27 + resolution: "postcss@npm:8.4.27" dependencies: - nanoid: ^3.3.4 + nanoid: ^3.3.6 picocolors: ^1.0.0 source-map-js: ^1.0.2 - checksum: 10eee25efd77868036403858577da0cefaf2e0905feeaba5770d5438ccdddba3d01cba8063e96b8aac4c6daa0ed413dd5ae0554a433a3c4db38df1d134cffc1f + checksum: 1cdd0c298849df6cd65f7e646a3ba36870a37b65f55fd59d1a165539c263e9b4872a402bf4ed1ca1bc31f58b68b2835545e33ea1a23b161a1f8aa6d5ded81e78 languageName: node linkType: hard -"postcss@npm:8.4.27": - version: 8.4.27 - resolution: "postcss@npm:8.4.27" +"postcss@npm:8.4.31": + version: 8.4.31 + resolution: "postcss@npm:8.4.31" dependencies: nanoid: ^3.3.6 picocolors: ^1.0.0 source-map-js: ^1.0.2 - checksum: 1cdd0c298849df6cd65f7e646a3ba36870a37b65f55fd59d1a165539c263e9b4872a402bf4ed1ca1bc31f58b68b2835545e33ea1a23b161a1f8aa6d5ded81e78 + checksum: 1d8611341b073143ad90486fcdfeab49edd243377b1f51834dc4f6d028e82ce5190e4f11bb2633276864503654fb7cab28e67abdc0fbf9d1f88cad4a0ff0beea languageName: node linkType: hard