Skip to content

Commit

Permalink
removed ability to specify ethereum private key as plain text over co…
Browse files Browse the repository at this point in the history
…mmand line
  • Loading branch information
Zachary Frederick authored and atodorov committed Sep 26, 2023
1 parent 2a0f1a8 commit 21df289
Show file tree
Hide file tree
Showing 7 changed files with 286 additions and 113 deletions.
38 changes: 19 additions & 19 deletions integration-tests/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -285,9 +285,9 @@
"@babel/helper-plugin-utils" "^7.22.5"

"@babel/runtime@^7.20.13", "@babel/runtime@^7.20.6":
version "7.22.15"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.15.tgz#38f46494ccf6cf020bd4eed7124b425e83e523b8"
integrity sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA==
version "7.23.1"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.1.tgz#72741dc4d413338a91dcb044a86f3c0bc402646d"
integrity sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g==
dependencies:
regenerator-runtime "^0.14.0"

Expand Down Expand Up @@ -1622,9 +1622,9 @@
"@babel/types" "^7.20.7"

"@types/bn.js@^5.1.1":
version "5.1.1"
resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.1.tgz#b51e1b55920a4ca26e9285ff79936bbdec910682"
integrity sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==
version "5.1.2"
resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.2.tgz#162f5238c46f4bcbac07a98561724eca1fcf0c5e"
integrity sha512-dkpZu0szUtn9UXTmw+e0AJFd4D2XAxDnsCLdc05SfqpqzPEBft8eQr8uaFitfo/dUUOZERaLec2hHMG87A4Dxg==
dependencies:
"@types/node" "*"

Expand Down Expand Up @@ -1668,17 +1668,17 @@
integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==

"@types/node-fetch@^2.6.2":
version "2.6.5"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.5.tgz#972756a9a0fe354b2886bf3defe667ddb4f0d30a"
integrity sha512-OZsUlr2nxvkqUFLSaY2ZbA+P1q22q+KrlxWOn/38RX+u5kTkYL2mTujEpzUhGkS+K/QCYp9oagfXG39XOzyySg==
version "2.6.6"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.6.tgz#b72f3f4bc0c0afee1c0bc9cff68e041d01e3e779"
integrity sha512-95X8guJYhfqiuVVhRFxVQcf4hW/2bCuoPwDasMf/531STFoNoWTT7YDnWdXHEZKqAGUigmpG31r2FE70LwnzJw==
dependencies:
"@types/node" "*"
form-data "^4.0.0"

"@types/node@*":
version "20.6.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.6.1.tgz#8b589bba9b2af0128796461a0979764562687e6f"
integrity sha512-4LcJvuXQlv4lTHnxwyHQZ3uR9Zw2j7m1C9DfuwoTFQQP4Pmu04O6IfLYgMmHoOCt0nosItLLZAH+sOrRE0Bo8g==
version "20.7.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.7.0.tgz#c03de4572f114a940bc2ca909a33ddb2b925e470"
integrity sha512-zI22/pJW2wUZOVyguFaUL1HABdmSVxpXrzIqkjsHmyUjNhPoWM1CKfvVuXfetHhIok4RY573cqS0mZ1SJEnoTg==

"@types/semver@^7.5.0":
version "7.5.1"
Expand All @@ -1691,9 +1691,9 @@
integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==

"@types/websocket@^1.0.5":
version "1.0.6"
resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.6.tgz#ec8dce5915741632ac3a4b1f951b6d4156e32d03"
integrity sha512-JXkliwz93B2cMWOI1ukElQBPN88vMg3CruvW4KVSKpflt3NyNCJImnhIuB/f97rG7kakqRJGFiwkA895Kn02Dg==
version "1.0.7"
resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.7.tgz#94ef83be9414db30c147d400cee08d5d767deeb0"
integrity sha512-62Omr8U0PO+hgjLCpPnMsmjh2/FRwIGOktZHyYAUzooEJotwkXHMp7vCacdYi8haxBNOiw9bc2HIHI+b/MPNjA==
dependencies:
"@types/node" "*"

Expand Down Expand Up @@ -2184,7 +2184,7 @@ create-require@^1.1.0:

"creditcoin-js@file:../creditcoin-js/creditcoin-js-v0.10.4.tgz":
version "0.10.4"
resolved "file:../creditcoin-js/creditcoin-js-v0.10.4.tgz#78d4c8e4b0b6562537bfd110f0017ea7c65d4db1"
resolved "file:../creditcoin-js/creditcoin-js-v0.10.4.tgz#cceccd3ad3dad8511d7cd244fe74873eba7e1bdf"
dependencies:
"@polkadot/api" "9.14.2"
ethers "^5.7.1"
Expand Down Expand Up @@ -4265,9 +4265,9 @@ ws@^8.5.0:
integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==

ws@^8.8.1:
version "8.14.1"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.1.tgz#4b9586b4f70f9e6534c7bb1d3dc0baa8b8cf01e0"
integrity sha512-4OOseMUq8AzRBI/7SLMUwO+FEDnguetSk7KMb1sHwvF2w2Wv5Hoj0nlifx8vtGsftE/jWHojPy8sMMzYLJ2G/A==
version "8.14.2"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f"
integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==

y18n@^5.0.5:
version "5.0.8"
Expand Down
112 changes: 97 additions & 15 deletions scripts/cc-cli/src/commands/registerAddress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { Wallet } from "ethers";
import { signAccountId } from "creditcoin-js/lib/utils";
import { AddressRegistered } from "creditcoin-js/lib/extrinsics/register-address";
import { utils } from "ethers";
import { getErrorMessage } from "../utils/error";
import prompts from "prompts";

const blockchains = ["Ethereum", "Rinkeby", "Luniverse", "Bitcoin", "Other"];

Expand All @@ -14,16 +16,15 @@ export function makeRegisterAddressCmd() {
"-b, --blockchain <chain> The blockchain that this external address belongs to",
).choices(blockchains);

const privateKeyOpt = new Option(
"-p, --private-key <key> The private key for the Ethereum address that you want to register.",
).env("ETH_PRIVATE_KEY");

return new Command("register-address")
.description(
"Link a CreditCoin address to an address from another blockchain",
)
.addOption(privateKeyOpt)
.addOption(blockchainOpt)
.option(
"--eth-mnemonic",
"Specify the ethereum address using a mnemonic rather than a private key",
)
.action(registerAddressAction);
}

Expand All @@ -35,9 +36,11 @@ async function registerAddressAction(options: OptionValues) {
extrinsics: { registerAddress },
} = await newApi(options.url);

// Reads CC_SECRET env variable if it exists or propmpts the user to enter a mneumonic
// Reads CC_SECRET env variable if it exists or propmpts the user to enter a mnemonic
const signer = await initCallerKeyring(options);
const wallet = new Wallet(options.privateKey);

// Reads ETH_PRIVATE_KEY env variable if found or prompts the user to enter an ethereum mnemonic
const wallet = await initCallerEthWallet(options);

// create the cryptographic proof of ownership
const proof = signAccountId(api, wallet, signer.address);
Expand Down Expand Up @@ -68,14 +71,6 @@ function validateOptsOrExit(options: OptionValues) {
`ERROR: A blockchain must be specified (possible values: ${blockchains.toString()})`,
);
}

if (options.privateKey === undefined) {
fatalErr("ERROR: No external address specified");
}

if (!isValidPrivateKey(options.privateKey)) {
fatalErr(`ERROR: Invalid private key: ${options.privateKey as string}`);
}
}

export function fatalErr(s: string) {
Expand All @@ -91,3 +86,90 @@ function errorMsg(s: string) {
export function isValidPrivateKey(pk: string): boolean {
return utils.isHexString(pk, 32);
}

export async function initCallerEthWallet(
options: OptionValues,
): Promise<Wallet> {
try {
return await initWalletFromEnvOrPrompt("ETH_PRIVATE_KEY", options);
} catch (e) {
console.error(getErrorMessage(e));
process.exit(1);
}
}

async function initWalletFromEnvOrPrompt(
envVar: string,
options: OptionValues,
): Promise<Wallet> {
const interactive = options.input;
const useMnemonic = options.ethMnemonic;
const inputName = useMnemonic ? "mnemonic" : "private key";
const generateWallet = useMnemonic
? newWalletFromMnemonic
: newWalletFromPrivateKey;
const validateInput = useMnemonic ? isMnemonicValid : isValidPrivateKey;

if (!interactive && process.env[envVar] === undefined) {
throw new Error(
"Error: Must specify a private key using the environment variable ETH_PRIVATE_KEY or an interactive prompt",
);
}

if (process.env[envVar] !== undefined) {
const seed = process.env[envVar] as string;

if (!validateInput(seed)) {
throw new Error(`Error: ${inputName} is invalid`);
}

return generateWallet(seed);
} else if (interactive) {
const promptResult = await prompts([
{
type: "password",
name: "seed",
message: `Specify the ${inputName} for the ethereum address`,
validate: validateInput,
},
]);

const seed = promptResult.seed;

if (!seed) {
throw new Error(
`The ${inputName} could not be retrieved from the prompt`,
);
}

return generateWallet(seed);
}

throw new Error("The ethereum wallet could not be generated");
}

export function newWalletFromMnemonic(mnemonic: string): Wallet {
try {
return Wallet.fromMnemonic(mnemonic);
} catch (e) {
throw new Error(
`Error: Could not create wallet from mnemonic: ${getErrorMessage(e)}`,
);
}
}

export function newWalletFromPrivateKey(pk: string): Wallet {
try {
return new Wallet(pk);
} catch (e) {
throw new Error(
`Error: Could not create wallet from private key: ${getErrorMessage(e)}`,
);
}
}

// This wrapper function is needed to ensure comaptibility with the validate function in the prompt call
// see initWalletFromEnvOrPrompt for details
export function isMnemonicValid(mnemonic: string): boolean {
return utils.isValidMnemonic(mnemonic);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ import {
import { cryptoWaitReady } from "@polkadot/util-crypto";
import { deployCtcContract, CREDO_PER_CTC } from "creditcoin-js/lib/ctc-deploy";
import {
forElapsedBlocks,
testData,
tryRegisterAddress,
forElapsedBlocks,
} from "creditcoin-js/lib/testUtils";
import { describeIf } from "../../utils/tests";
import { getBalance } from "../../utils/balance";
Expand Down
67 changes: 43 additions & 24 deletions scripts/cc-cli/src/test/integration-tests/registerAddress.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from "creditcoin-js";
import { cryptoWaitReady } from "@polkadot/util-crypto";
import { testData } from "creditcoin-js/lib/testUtils";
import { utils } from "ethers";

describe("register-address", () => {
let ccApi: CreditcoinApi;
Expand All @@ -32,14 +33,9 @@ describe("register-address", () => {
arg("CREDITCOIN_CREATE_WALLET"),
);

// this arg call returns a function
sudo = arg("CREDITCOIN_CREATE_SIGNER")(keyring, "sudo");
});

afterAll(async () => {
await ccApi.api.disconnect();
});

test("e2e", async () => {
const { api } = ccApi;

const fundTx = await fundFromSudo(
Expand All @@ -48,26 +44,49 @@ describe("register-address", () => {
arg("CREDITCOIN_API_URL"),
);
await signSendAndWatch(fundTx, api, sudo);
}, 100_000);

afterAll(async () => {
await ccApi.api.disconnect();
});

const url = arg("CREDITCOIN_API_URL") as string;
const result = execa.commandSync(
`node dist/index.js register-address --url ${url} --blockchain Ethereum`,
{
env: {
// eslint-disable-next-line @typescript-eslint/naming-convention
ETH_PRIVATE_KEY: ethWallet.privateKey,
CC_SECRET: caller.secret,
it.each([
["using ethereum private key", false],
["Using an ethereum mnemonic", true],
])(
"should be able to register address: %s",
(text, useMnemonic) => {
let ethPrivateKey: string;

if (useMnemonic) {
ethPrivateKey = utils.entropyToMnemonic(utils.randomBytes(32));
} else {
ethPrivateKey = ethWallet.privateKey;
}

const url = arg("CREDITCOIN_API_URL") as string;
const result = execa.commandSync(
`node dist/index.js register-address --url ${url} --blockchain Ethereum ${
useMnemonic ? "--eth-mnemonic" : ""
}`,
{
env: {
// eslint-disable-next-line @typescript-eslint/naming-convention
ETH_PRIVATE_KEY: ethPrivateKey,
CC_SECRET: caller.secret,
},
},
},
);
);

const stdout = result.stdout.split("\n");
const stdout = result.stdout.split("\n");

expect(result.failed).toBe(false);
expect(result.exitCode).toBe(0);
expect(result.stderr).toBe("");
expect(
stdout[stdout.length - 1].includes("Address Registered Successfully"),
).toBe(true);
}, 50_000);
expect(result.failed).toBe(false);
expect(result.exitCode).toBe(0);
expect(result.stderr).toBe("");
expect(
stdout[stdout.length - 1].includes("Address Registered Successfully"),
).toBe(true);
},
50_000,
);
});
Loading

0 comments on commit 21df289

Please sign in to comment.