From e15315487f69e65088be34eb7f18167cdee679f8 Mon Sep 17 00:00:00 2001 From: satyajeetkolhapure Date: Fri, 23 Sep 2022 15:34:20 +0100 Subject: [PATCH 1/2] added unlock all feature to allow unkown accounts send transaction --- src/chains/ethereum/ethereum/src/api.ts | 19 +++-- .../tests/api/eth/sendTransaction.test.ts | 79 +++++++++++++++++++ .../ethereum/options/src/wallet-options.ts | 20 ++++- src/packages/ganache/npm-shrinkwrap.json | 2 +- 4 files changed, 113 insertions(+), 7 deletions(-) diff --git a/src/chains/ethereum/ethereum/src/api.ts b/src/chains/ethereum/ethereum/src/api.ts index b7f0b4aa5f..721ced2325 100644 --- a/src/chains/ethereum/ethereum/src/api.ts +++ b/src/chains/ethereum/ethereum/src/api.ts @@ -1943,13 +1943,22 @@ export default class EthereumApi implements Api { const wallet = this.#wallet; const isKnownAccount = wallet.knownAccounts.has(fromString); - const privateKey = wallet.unlockedAccounts.get(fromString); + let privateKey = wallet.unlockedAccounts.get(fromString); if (privateKey === undefined) { - const msg = isKnownAccount - ? "authentication needed: passphrase or unlock" - : "sender account not recognized"; - throw new Error(msg); + // if unlock all option is set to true + // then create a fake private key for unknown account + if(this.#options.wallet.unlockAll) + { + privateKey = wallet.createFakePrivateKey(fromString) + } + else + { + const msg = isKnownAccount + ? "authentication needed: passphrase or unlock" + : "sender account not recognized"; + throw new Error(msg); + } } await autofillDefaultTransactionValues( diff --git a/src/chains/ethereum/ethereum/tests/api/eth/sendTransaction.test.ts b/src/chains/ethereum/ethereum/tests/api/eth/sendTransaction.test.ts index a12fffbeec..9b770e5219 100644 --- a/src/chains/ethereum/ethereum/tests/api/eth/sendTransaction.test.ts +++ b/src/chains/ethereum/ethereum/tests/api/eth/sendTransaction.test.ts @@ -357,6 +357,85 @@ describe("api", () => { assert(largeKey.pk < SECP256K1_N); }); }); + + describe("unlock all accounts", async () => { + const ZERO_ADDRESS = "0x" + "0".repeat(40); + const PROVIDER_OPTIONS : EthereumProviderOptions = { + miner: { + defaultGasPrice: 0 + }, + chain: { + // use berlin here because we just want to test if we can use the + // "zero" address, and we do this by transferring value while + // setting the gasPrice to `0`. This isn't possible after the + // `london` hardfork currently, as we don't provide an option to + // allow for a 0 `maxFeePerGas` value. + // TODO: remove once we have a configurable `maxFeePerGas` + hardfork: "berlin" + } + } + + it("can send transactions from an unlocked 0x0 address when unlock all is true", async () => { + PROVIDER_OPTIONS.wallet = { + unlockAll: true + } + const provider = await getProvider( + PROVIDER_OPTIONS + ); + const [from] = await provider.send("eth_accounts"); + + await provider.send("eth_subscribe", ["newHeads"]); + const initialZeroBalance = "0x1234"; + await provider.send("eth_sendTransaction", [ + { from: from, to: ZERO_ADDRESS, value: initialZeroBalance } + ]); + await provider.once("message"); + const initialBalance = await provider.send("eth_getBalance", [ + ZERO_ADDRESS + ]); + assert.strictEqual( + initialBalance, + initialZeroBalance, + "Zero address's balance isn't correct" + ); + console.log(initialBalance) + const removeValueFromZeroAmount = "0x123"; + await provider.send("eth_sendTransaction", [ + { from: ZERO_ADDRESS, to: from, value: removeValueFromZeroAmount } + ]); + await provider.once("message"); + const afterSendBalance = BigInt( + await provider.send("eth_getBalance", [ZERO_ADDRESS]) + ); + assert.strictEqual( + BigInt(initialZeroBalance) - BigInt(removeValueFromZeroAmount), + afterSendBalance, + "Zero address's balance isn't correct" + ); + }); + + it("cannot send transactions from an unlocked 0x0 address when unlock all is false", async () => { + PROVIDER_OPTIONS.wallet = { + unlockAll: false + } + const provider = await getProvider( + PROVIDER_OPTIONS + ); + const [from] = await provider.send("eth_accounts"); + await provider.send("eth_subscribe", ["newHeads"]); + const removeValueFromZeroAmount = "0x123"; + + const badSend = async () => { + return provider.send("eth_sendTransaction", [ + { from: ZERO_ADDRESS, to: from, value: removeValueFromZeroAmount } + ]); + }; + await assert.rejects( + badSend, + "Error: sender account not recognized" + ); + }); + }); }); }); }); diff --git a/src/chains/ethereum/options/src/wallet-options.ts b/src/chains/ethereum/options/src/wallet-options.ts index f4eeeb46e3..82203c899f 100644 --- a/src/chains/ethereum/options/src/wallet-options.ts +++ b/src/chains/ethereum/options/src/wallet-options.ts @@ -202,6 +202,16 @@ export type WalletConfig = { hd_path: string; }; }; + + /** + * Unlock all accounts irrespective of known or unknown status. + * + * @defaultValue false + */ + unlockAll: { + type: boolean; + hasDefault: true; + }; }; exclusiveGroups: [ ["accounts", "totalAccounts"], @@ -326,5 +336,13 @@ export const WalletOptions: Definitions = { default: () => ["m", "44'", "60'", "0'", "0"], legacyName: "hd_path", cliType: "string" - } + }, + unlockAll: { + normalize, + cliDescription: + "Unlock all accounts irrespective of known or unknown status.", + default: () => false, + cliAliases: ["unlockAll"], + cliType: "boolean" + }, }; diff --git a/src/packages/ganache/npm-shrinkwrap.json b/src/packages/ganache/npm-shrinkwrap.json index 67c6cdfcc0..c69b456573 100644 --- a/src/packages/ganache/npm-shrinkwrap.json +++ b/src/packages/ganache/npm-shrinkwrap.json @@ -1,6 +1,6 @@ { "name": "ganache", - "version": "7.3.0", + "version": "7.4.3", "lockfileVersion": 1, "requires": true, "dependencies": { From 6ab1a2a9e36055a9f3dde9b9fb6bd6c6373b9c49 Mon Sep 17 00:00:00 2001 From: satyajeetkolhapure Date: Mon, 26 Sep 2022 16:03:12 +0100 Subject: [PATCH 2/2] fixed review comments --- .../tests/api/eth/sendTransaction.test.ts | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/chains/ethereum/ethereum/tests/api/eth/sendTransaction.test.ts b/src/chains/ethereum/ethereum/tests/api/eth/sendTransaction.test.ts index 9b770e5219..29ab360d06 100644 --- a/src/chains/ethereum/ethereum/tests/api/eth/sendTransaction.test.ts +++ b/src/chains/ethereum/ethereum/tests/api/eth/sendTransaction.test.ts @@ -11,6 +11,8 @@ import Wallet from "../../../src/wallet"; import { SECP256K1_N } from "@ganache/secp256k1"; import { Data, Quantity } from "@ganache/utils"; +const ZERO_ADDRESS = "0x" + "0".repeat(40); + describe("api", () => { describe("eth", () => { describe("sendTransaction", () => { @@ -223,7 +225,6 @@ describe("api", () => { describe("unlocked accounts", () => { it("can send transactions from an unlocked 0x0 address", async () => { - const ZERO_ADDRESS = "0x" + "0".repeat(40); const provider = await getProvider({ miner: { defaultGasPrice: 0 @@ -359,8 +360,7 @@ describe("api", () => { }); describe("unlock all accounts", async () => { - const ZERO_ADDRESS = "0x" + "0".repeat(40); - const PROVIDER_OPTIONS : EthereumProviderOptions = { + const providerOptions : EthereumProviderOptions = { miner: { defaultGasPrice: 0 }, @@ -376,14 +376,14 @@ describe("api", () => { } it("can send transactions from an unlocked 0x0 address when unlock all is true", async () => { - PROVIDER_OPTIONS.wallet = { + providerOptions.wallet = { unlockAll: true } - const provider = await getProvider( - PROVIDER_OPTIONS - ); + const provider = await getProvider( + providerOptions + ); const [from] = await provider.send("eth_accounts"); - + await provider.send("eth_subscribe", ["newHeads"]); const initialZeroBalance = "0x1234"; await provider.send("eth_sendTransaction", [ @@ -398,7 +398,6 @@ describe("api", () => { initialZeroBalance, "Zero address's balance isn't correct" ); - console.log(initialBalance) const removeValueFromZeroAmount = "0x123"; await provider.send("eth_sendTransaction", [ { from: ZERO_ADDRESS, to: from, value: removeValueFromZeroAmount } @@ -415,21 +414,21 @@ describe("api", () => { }); it("cannot send transactions from an unlocked 0x0 address when unlock all is false", async () => { - PROVIDER_OPTIONS.wallet = { + providerOptions.wallet = { unlockAll: false } - const provider = await getProvider( - PROVIDER_OPTIONS - ); + const provider = await getProvider( + providerOptions + ); const [from] = await provider.send("eth_accounts"); + await provider.send("eth_subscribe", ["newHeads"]); const removeValueFromZeroAmount = "0x123"; - const badSend = async () => { return provider.send("eth_sendTransaction", [ { from: ZERO_ADDRESS, to: from, value: removeValueFromZeroAmount } ]); - }; + }; await assert.rejects( badSend, "Error: sender account not recognized"