Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

fix: regression in eth_sign signature v values #4527

Merged
merged 2 commits into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"start": "lerna exec --loglevel=silent --scope ganache -- npm run start --silent -- ",
"test": "lerna exec --concurrency 1 -- npm run test",
"tsc": "tsc --build",
"tsc.clean": "npx lerna exec -- npx shx rm -rf lib dist typings"
"tsc.clean": "npx lerna exec -- npx shx rm -rf lib dist typings",
"update-ethereumjs": "cd scripts && ts-node update-ethereumjs"
},
"devDependencies": {
"@istanbuljs/nyc-config-typescript": "1.0.2",
Expand Down
2 changes: 1 addition & 1 deletion packages/ethereum/ethereum/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2079,7 +2079,7 @@ export default class EthereumApi implements Api {

const messageHash = hashPersonalMessage(Data.toBuffer(message));
const { v, r, s } = ecsign(messageHash, privateKey.toBuffer());
return toRpcSig(v, r, s);
return toRpcSig(v + 27n, r, s);
}

/**
Expand Down
17 changes: 16 additions & 1 deletion packages/ethereum/ethereum/tests/api/eth/sign.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from "@ethereumjs/util";
import getProvider from "../../helpers/getProvider";
import { Data, Quantity } from "@ganache/utils";
import { sign } from "crypto";

describe("api", () => {
describe("eth", () => {
Expand Down Expand Up @@ -37,10 +38,17 @@ describe("api", () => {
const msgHash = hashPersonalMessage(msg);

const address = accounts[0];
let sgn = await provider.send("eth_sign", [
let sgn: string = await provider.send("eth_sign", [
address,
Data.toString(msg)
]);

assert.strictEqual(
sgn.substring(sgn.length - 2),
"1c", // 28 in hex
"eth_sign should produce a v value of 27 or 28"
);

const { v, r, s } = fromRpcSig(sgn);

const pub = ecrecover(msgHash, v, r, s);
Expand All @@ -59,7 +67,14 @@ describe("api", () => {

let sgn = await provider.send("eth_sign", [accounts[0], msgHex]);

assert.strictEqual(
sgn.substring(sgn.length - 2),
"1c", // 28 in hex
"eth_sign should produce a v value of 27 or 28"
);

const { v, r, s } = fromRpcSig(sgn);

const pub = ecrecover(msgHash, v, r, s);
const addr = fromSigned(pubToAddress(pub));
const strAddr = Data.toString(Quantity.toBuffer(addr), 20);
Expand Down
95 changes: 95 additions & 0 deletions scripts/update-ethereum-js.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// search through all folders in the parent directory to find all package.json
// files. Then read each file looking for ethereumjs dependencies,
// devDependencies, or optionalDependencies. If found, update the version
// number to the latest version on npm (by querying the npm registry).

import * as fs from "fs";
import * as path from "path";
import * as util from "util";
import * as child_process from "child_process";
import * as https from "https";

const readdir = util.promisify(fs.readdir);
const readFile = util.promisify(fs.readFile);
const writeFile = util.promisify(fs.writeFile);
const exec = util.promisify(child_process.exec);

const parentDir = path.resolve(__dirname, "../");

async function findPackageFiles(dir: string): Promise<string[]> {
const files: string[] = [];
const dirents = await readdir(dir, { withFileTypes: true });

for (const dirent of dirents) {
const res = path.resolve(dir, dirent.name);
if (dirent.isDirectory() && dirent.name !== "node_modules") {
const subFiles = await findPackageFiles(res);
files.push(...subFiles);
} else if (dirent.isFile() && dirent.name === "package.json") {
files.push(res);
}
}

return files;
}

const cache = new Map<string, Buffer>();

async function updateDependencies(packagePath: string) {
const packageData = await readFile(packagePath, { encoding: "utf-8" });
const packageJson = JSON.parse(packageData);

const dependencies = [
[[...Object.entries(packageJson.dependencies ?? {})], "dependencies"],
[[...Object.entries(packageJson.devDependencies ?? {})], "devDependencies"],
[
[...Object.entries(packageJson.optionalDependencies ?? {})],
"optionalDependencies"
]
] as [[string, string][], string][];

let changed = false;

for (const [matches, group] of dependencies) {
for (const [name, version] of matches) {
if (name.startsWith("@ethereumjs/")) {
const response = cache.has(name)
? cache.get(name)!
: await new Promise<Buffer>((resolve, reject) => {
https
.get(`https://registry.npmjs.org/${name}`, res => {
const chunks: Uint8Array[] = [];
res.on("data", chunk => chunks.push(chunk));
res.on("end", () => resolve(Buffer.concat(chunks)));
})
.on("error", reject);
});
if (cache.has(name)) {
cache.set(name, response);
}
const registryData = JSON.parse(response.toString());
const latestVersion = registryData["dist-tags"].latest;
if (version !== latestVersion) {
packageJson[group][name] = latestVersion;
changed = true;
}
}
}
}

if (changed) {
await writeFile(packagePath, JSON.stringify(packageJson, null, 2));
}
}

async function main() {
const packagePaths = await findPackageFiles(parentDir);

for (const packagePath of packagePaths) {
await updateDependencies(packagePath);
}

await exec("npm run reinstall", { cwd: parentDir });
}

main().catch(console.error);