Skip to content

Commit

Permalink
Merge pull request NomicFoundation#2008 from nomiclabs/priv-key
Browse files Browse the repository at this point in the history
extra tests for privkey validation
  • Loading branch information
zoeyTM authored Nov 5, 2021
2 parents 2783718 + e4efcaf commit 9c95b1d
Show file tree
Hide file tree
Showing 3 changed files with 397 additions and 21 deletions.
5 changes: 5 additions & 0 deletions .changeset/rude-lemons-turn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"hardhat": patch
---

Improve validation of private keys in the Hardhat config
122 changes: 107 additions & 15 deletions packages/hardhat-core/src/internal/core/config/config-validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,59 @@ function getErrorMessage(path: string, value: any, expectedType: string) {
)} for ${path} - Expected a value of type ${expectedType}.`;
}

function getPrivateKeyError(index: number, network: string, message: string) {
return `Invalid account: #${index} for network: ${network} - ${message}`;
}

function validatePrivateKey(
privateKey: unknown,
index: number,
network: string,
errors: string[]
) {
if (typeof privateKey !== "string") {
errors.push(
getPrivateKeyError(
index,
network,
`Expected string, received ${typeof privateKey}`
)
);
} else {
// private key validation
const pkWithPrefix = /^0x/.test(privateKey)
? privateKey
: `0x${privateKey}`;

// 32 bytes = 64 characters + 2 char prefix = 66
if (pkWithPrefix.length < 66) {
errors.push(
getPrivateKeyError(
index,
network,
"private key too short, expected 32 bytes"
)
);
} else if (pkWithPrefix.length > 66) {
errors.push(
getPrivateKeyError(
index,
network,
"private key too long, expected 32 bytes"
)
);
} else if (hexString.decode(pkWithPrefix).isLeft()) {
errors.push(
getPrivateKeyError(
index,
network,
"invalid hex character(s) found in string"
)
);
}
}
}

export function failure(es: ValidationError[]): string[] {
return es.map(getMessage);
}
Expand Down Expand Up @@ -224,7 +277,7 @@ export function validateConfig(config: any) {
}

export function getValidationErrors(config: any): string[] {
const errors = [];
const errors: string[] = [];

// These can't be validated with io-ts
if (config !== undefined && typeof config.networks === "object") {
Expand All @@ -237,8 +290,7 @@ export function getValidationErrors(config: any): string[] {
}

// Validating the accounts with io-ts leads to very confusing errors messages
const configExceptAccounts = { ...hardhatNetwork };
delete configExceptAccounts.accounts;
const { accounts, ...configExceptAccounts } = hardhatNetwork;

const netConfigResult = HardhatNetworkConfig.decode(configExceptAccounts);
if (netConfigResult.isLeft()) {
Expand All @@ -251,31 +303,37 @@ export function getValidationErrors(config: any): string[] {
);
}

if (Array.isArray(hardhatNetwork.accounts)) {
for (const account of hardhatNetwork.accounts) {
if (typeof account.privateKey !== "string") {
// manual validation of accounts
if (Array.isArray(accounts)) {
for (const [index, account] of accounts.entries()) {
if (typeof account !== "object") {
errors.push(
getErrorMessage(
`HardhatConfig.networks.${HARDHAT_NETWORK_NAME}.accounts[].privateKey`,
account.privateKey,
"string"
getPrivateKeyError(
index,
HARDHAT_NETWORK_NAME,
`Expected object, received ${typeof account}`
)
);
continue;
}

if (typeof account.balance !== "string") {
const { privateKey, balance } = account;

validatePrivateKey(privateKey, index, HARDHAT_NETWORK_NAME, errors);

if (typeof balance !== "string") {
errors.push(
getErrorMessage(
`HardhatConfig.networks.${HARDHAT_NETWORK_NAME}.accounts[].balance`,
account.balance,
balance,
"string"
)
);
} else if (decimalString.decode(account.balance).isLeft()) {
} else if (decimalString.decode(balance).isLeft()) {
errors.push(
getErrorMessage(
`HardhatConfig.networks.${HARDHAT_NETWORK_NAME}.accounts[].balance`,
account.balance,
balance,
"decimal(wei)"
)
);
Expand Down Expand Up @@ -340,7 +398,9 @@ export function getValidationErrors(config: any): string[] {
}
}

const netConfigResult = HttpNetworkConfig.decode(netConfig);
const { accounts, ...configExceptAccounts } = netConfig;

const netConfigResult = HttpNetworkConfig.decode(configExceptAccounts);
if (netConfigResult.isLeft()) {
errors.push(
getErrorMessage(
Expand All @@ -350,6 +410,38 @@ export function getValidationErrors(config: any): string[] {
)
);
}

// manual validation of accounts
if (Array.isArray(accounts)) {
accounts.forEach((privateKey, index) =>
validatePrivateKey(privateKey, index, networkName, errors)
);
} else if (typeof accounts === "object") {
const hdConfigResult = HDAccountsConfig.decode(accounts);
if (hdConfigResult.isLeft()) {
errors.push(
getErrorMessage(
`HardhatConfig.networks.${networkName}`,
accounts,
"HttpNetworkHDAccountsConfig"
)
);
}
} else if (typeof accounts === "string") {
if (accounts !== "remote") {
errors.push(
`Invalid 'accounts' entry for network '${networkName}': expected an array of accounts or the string 'remote', but got the string '${accounts}'`
);
}
} else if (accounts !== undefined) {
errors.push(
getErrorMessage(
`HardhatConfig.networks.${networkName}.accounts`,
accounts,
'"remote" | string[] | HttpNetworkHDAccountsConfig | undefined'
)
);
}
}
}

Expand Down
Loading

0 comments on commit 9c95b1d

Please sign in to comment.