From 84c5b34afa73b4f906e413c4d5dd3ff46a1dd7a8 Mon Sep 17 00:00:00 2001 From: Michal Bajer Date: Fri, 6 Oct 2023 12:21:07 +0000 Subject: [PATCH] feat(cactus-plugin-ledger-connector-ethereum): add signing utils - Add methods for signing transactions to ethereum connector. - Modify offline signing test to use this new method. - Modify electricity trade sample to use new signing method. - Add offline signature section to ethereum connector readme. Co-authored-by: Peter Somogyvari Signed-off-by: Michal Bajer Signed-off-by: Peter Somogyvari --- .../BusinessLogicElectricityTrade.ts | 2 +- .../TransactionEthereum.ts | 18 +++-- .../config/usersetting.yaml | 7 ++ .../config/validator-registry-config.yaml | 8 --- .../README.md | 68 +++++++++++++++++++ .../package.json | 2 + .../src/main/typescript/public-api.ts | 2 + .../src/main/typescript/sign-utils.ts | 59 ++++++++++++++++ ...eploy-and-invoke-using-keychain-v1.test.ts | 13 ++-- yarn.lock | 65 ++++++++++++++++++ 10 files changed, 225 insertions(+), 19 deletions(-) create mode 100644 packages/cactus-plugin-ledger-connector-ethereum/src/main/typescript/sign-utils.ts diff --git a/examples/cactus-example-electricity-trade/BusinessLogicElectricityTrade.ts b/examples/cactus-example-electricity-trade/BusinessLogicElectricityTrade.ts index eae622ec5e..84f693aca2 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 227ddac4ac..deaec3799d 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 99ac3004e9..13604fb7b9 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 2dae4c5120..6f2f49a879 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 9e23ad4ac6..065367c152 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/README.md +++ b/packages/cactus-plugin-ledger-connector-ethereum/README.md @@ -147,6 +147,74 @@ 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, + }, +}); +``` + +### 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, + }, +}); +``` + ## JSON-RPC Proxy - Connector can be used with web3js to send any JSON-RPC request to the ethereum node. diff --git a/packages/cactus-plugin-ledger-connector-ethereum/package.json b/packages/cactus-plugin-ledger-connector-ethereum/package.json index ea3cddb072..756fe8f409 100644 --- a/packages/cactus-plugin-ledger-connector-ethereum/package.json +++ b/packages/cactus-plugin-ledger-connector-ethereum/package.json @@ -65,6 +65,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 22a8089236..56d5b0cfbb 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 0000000000..66827c879d --- /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 227f9c62cc..2c0e470017 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 @@ -46,6 +46,7 @@ import { InvokeContractV1Request, DeployContractV1Request, ContractKeychainDefinition, + signTransaction, } from "../../../main/typescript/public-api"; import { K_CACTI_ETHEREUM_TOTAL_TX_COUNT } from "../../../main/typescript/prometheus-exporter/metrics"; @@ -348,13 +349,12 @@ 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, @@ -363,6 +363,11 @@ describe("Ethereum contract deploy and invoke using keychain tests", () => { type: 2, }, testEthAccount.privateKey, + { + networkId: 10, + chainId: 10, + defaultHardfork: "london", + }, ); await apiClient.runTransactionV1({ @@ -370,7 +375,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 25e5b39dcb..44349547d7 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" @@ -6475,6 +6526,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 @@ -17341,6 +17394,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"