Skip to content

Commit

Permalink
Merge pull request NomicFoundation#2009 from nomiclabs/personal_sign
Browse files Browse the repository at this point in the history
Add personal_sign RPC method
  • Loading branch information
fvictorio authored Nov 1, 2021
2 parents 6aa3268 + e4017da commit 441a467
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/chilled-cooks-listen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"hardhat": patch
---

Add support for the `personal_sign RPC method to Hardhat Network.
9 changes: 9 additions & 0 deletions packages/hardhat-core/src/internal/core/errors-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,15 @@ Please double check your transactions' parameters.`,
Please double check your transactions' parameters.`,
shouldBeReported: false,
},
PERSONALSIGN_MISSING_ADDRESS_PARAM: {
number: 116,
message: 'Missing "address" param when calling personal_sign.',
title: "Missing `address` param when calling personal_sign.",
description: `You called \`personal_sign\` with incorrect parameters.
Please check that you are sending an \`address\` parameter.`,
shouldBeReported: false,
},
},
TASK_DEFINITIONS: {
PARAM_AFTER_VARIADIC: {
Expand Down
19 changes: 19 additions & 0 deletions packages/hardhat-core/src/internal/core/providers/accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,25 @@ export class LocalAccountsProvider extends ProviderWrapperWithChainId {
}
}

if (args.method === "personal_sign") {
if (params.length > 0) {
const [data, address] = validateParams(params, rpcData, rpcAddress);

if (data !== undefined) {
if (address === undefined) {
throw new HardhatError(
ERRORS.NETWORK.PERSONALSIGN_MISSING_ADDRESS_PARAM
);
}

const privateKey = this._getPrivateKeyForAddress(address);
const messageHash = hashPersonalMessage(toBuffer(data));
const signature = ecsign(messageHash, privateKey);
return toRpcSig(signature.v, signature.r, signature.s);
}
}
}

if (args.method === "eth_signTypedData_v4") {
const [address, data] = validateParams(params, rpcAddress, t.any);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Address, toRpcSig } from "ethereumjs-util";
import { rpcAddress, rpcData } from "../../../core/jsonrpc/types/base-types";
import { validateParams } from "../../../core/jsonrpc/types/input/validation";
import { MethodNotFoundError } from "../../../core/providers/errors";
import { HardhatNode } from "../node";

/* eslint-disable @nomiclabs/hardhat-internal-rules/only-hardhat-error */

export class PersonalModule {
constructor(private readonly _node: HardhatNode) {}

public async processRequest(
method: string,
params: any[] = []
): Promise<any> {
switch (method) {
case "personal_sign": {
return this._signAction(...this._signParams(params));
}
}

throw new MethodNotFoundError(`Method ${method} not found`);
}

// personal_sign

private _signParams(params: any[]): [Buffer, Buffer] {
return validateParams(params, rpcData, rpcAddress);
}

private async _signAction(data: Buffer, address: Buffer): Promise<string> {
const signature = await this._node.signPersonalMessage(
new Address(address),
data
);

return toRpcSig(signature.v, signature.r, signature.s);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { EthModule } from "./modules/eth";
import { EvmModule } from "./modules/evm";
import { HardhatModule } from "./modules/hardhat";
import { ModulesLogger } from "./modules/logger";
import { PersonalModule } from "./modules/personal";
import { NetModule } from "./modules/net";
import { Web3Module } from "./modules/web3";
import { HardhatNode } from "./node";
Expand Down Expand Up @@ -65,6 +66,7 @@ export class HardhatNetworkProvider
private _evmModule?: EvmModule;
private _hardhatModule?: HardhatModule;
private _debugModule?: DebugModule;
private _personalModule?: PersonalModule;
private readonly _mutex = new Mutex();

constructor(
Expand Down Expand Up @@ -205,6 +207,10 @@ export class HardhatNetworkProvider
return this._debugModule!.processRequest(method, params);
}

if (method.startsWith("personal_")) {
return this._personalModule!.processRequest(method, params);
}

throw new MethodNotFoundError(`Method ${method} not found`);
}

Expand Down Expand Up @@ -265,6 +271,7 @@ export class HardhatNetworkProvider
this._experimentalHardhatNetworkMessageTraceHooks
);
this._debugModule = new DebugModule(node);
this._personalModule = new PersonalModule(node);

this._forwardNodeEvents(node);
}
Expand Down
44 changes: 44 additions & 0 deletions packages/hardhat-core/test/internal/core/providers/accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,50 @@ describe("Local accounts provider", () => {
]);
});
});

describe("personal_sign", () => {
it("Should be compatible with geth's implementation", async () => {
// This test was created by using Geth 1.10.12-unstable and calling personal_sign

const provider = new LocalAccountsProvider(mock, [
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
]);

const result = await provider.request({
method: "personal_sign",
params: [
"0x5417aa2a18a44da0675524453ff108c545382f0d7e26605c56bba47c21b5e979",
"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
],
});

assert.equal(
result,
"0x9c73dd4937a37eecab3abb54b74b6ec8e500080431d36afedb1726624587ee6710296e10c1194dded7376f13ff03ef6c9e797eb86bae16c20c57776fc69344271c"
);
});

it("Should be compatible with metamask's implementation", async () => {
// This test was created by using Metamask 10.3.0

const provider = new LocalAccountsProvider(mock, [
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
]);

const result = (await provider.request({
method: "personal_sign",
params: [
"0x7699f568ecd7753e6ddf75a42fa4c2cc86cbbdc704c9eb1a6b6d4b9d8b8d1519",
"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
],
})) as string;

assert.equal(
result,
"0x2875e4206c9fe3b229291c81f95cc4f421e2f4d3e023f5b4041daa56ab4000977010b47a3c01036ec8a6a0872aec2ab285150f003d01b0d8da60c1cceb9154181c"
);
});
});
});

describe("hdwallet provider", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ export const DEFAULT_ACCOUNTS = [
"0xe331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd10b",
balance: new BN(10).pow(new BN(21)),
},
{
privateKey:
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
balance: new BN(10).pow(new BN(21)),
},
];
export const DEFAULT_ACCOUNTS_ADDRESSES = DEFAULT_ACCOUNTS.map((account) =>
bufferToHex(privateToAddress(toBuffer(account.privateKey))).toLowerCase()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { assert } from "chai";

import { workaroundWindowsCiFailures } from "../../../../utils/workaround-windows-ci-failures";
import { setCWD } from "../../helpers/cwd";
import { PROVIDERS } from "../../helpers/providers";

describe("Personal module", function () {
PROVIDERS.forEach(({ name, useProvider, isFork }) => {
if (isFork) {
this.timeout(50000);
}

workaroundWindowsCiFailures.call(this, { isFork });

describe(`${name} provider`, function () {
setCWD();
useProvider();

describe("personal_sign", async function () {
it("Should be compatible with geth's implementation", async function () {
// This test was created by using Geth 1.10.12-unstable and calling personal_sign
const result = await this.provider.request({
method: "personal_sign",
params: [
"0x5417aa2a18a44da0675524453ff108c545382f0d7e26605c56bba47c21b5e979",
"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
],
});

assert.equal(
result,
"0x9c73dd4937a37eecab3abb54b74b6ec8e500080431d36afedb1726624587ee6710296e10c1194dded7376f13ff03ef6c9e797eb86bae16c20c57776fc69344271c"
);
});

it("Should be compatible with metamask's implementation", async function () {
// This test was created by using Metamask 10.3.0

const result = (await this.provider.request({
method: "personal_sign",
params: [
"0x7699f568ecd7753e6ddf75a42fa4c2cc86cbbdc704c9eb1a6b6d4b9d8b8d1519",
"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
],
})) as string;

assert.equal(
result,
"0x2875e4206c9fe3b229291c81f95cc4f421e2f4d3e023f5b4041daa56ab4000977010b47a3c01036ec8a6a0872aec2ab285150f003d01b0d8da60c1cceb9154181c"
);
});
});
});
});
});

0 comments on commit 441a467

Please sign in to comment.