diff --git a/package.json b/package.json index b957ee65fc..c5cd9451f0 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/packages/core/tests/server.test.ts b/packages/core/tests/server.test.ts index bf83ed3de2..18d339d5c7 100644 --- a/packages/core/tests/server.test.ts +++ b/packages/core/tests/server.test.ts @@ -461,7 +461,9 @@ describe("server", () => { } finally { await teardown(); } - }); + // TODO: this test can take a while on mac in CI, figure out why + // https://github.com/trufflesuite/ganache/issues/4505 + }).timeout(0); it("fails to `.listen()` twice, Callback", async () => { await setup(); @@ -514,9 +516,19 @@ describe("server", () => { await s.close(); - const error = await post("localhost", port, jsonRpcJson, agent).catch(e => e); - assert(error instanceof Error, `Expected error to be an instance of Error, but got ${error} instead`); - assert("code" in error && (error["code"] === "ECONNREFUSED" || error["code"] === "ECONNRESET"), `Expected error.code to be ECONNREFUSED or ECONNRESET, got ${error} instead`); + const error = await post("localhost", port, jsonRpcJson, agent).catch( + e => e + ); + assert( + error instanceof Error, + `Expected error to be an instance of Error, but got ${error} instead` + ); + assert( + "code" in error && + (error["code"] === "ECONNREFUSED" || + error["code"] === "ECONNRESET"), + `Expected error.code to be ECONNREFUSED or ECONNRESET, got ${error} instead` + ); } finally { teardown(); } diff --git a/packages/ethereum/ethereum/src/api.ts b/packages/ethereum/ethereum/src/api.ts index 93d5674e8a..c4289b6825 100644 --- a/packages/ethereum/ethereum/src/api.ts +++ b/packages/ethereum/ethereum/src/api.ts @@ -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); } /** diff --git a/packages/ethereum/ethereum/src/miner/miner.ts b/packages/ethereum/ethereum/src/miner/miner.ts index 3cb1684d9c..6c1621c273 100644 --- a/packages/ethereum/ethereum/src/miner/miner.ts +++ b/packages/ethereum/ethereum/src/miner/miner.ts @@ -64,7 +64,7 @@ export type BlockData = { const updateBloom = (blockBloom: Buffer, bloom: Buffer) => { let i = 256; - while (--i) blockBloom[i] |= bloom[i]; + while (i--) blockBloom[i] |= bloom[i]; }; const sortByPrice = (values: TypedTransaction[], a: number, b: number) => diff --git a/packages/ethereum/ethereum/tests/api/eth/sign.test.ts b/packages/ethereum/ethereum/tests/api/eth/sign.test.ts index ecfab32d06..f57726ba4e 100644 --- a/packages/ethereum/ethereum/tests/api/eth/sign.test.ts +++ b/packages/ethereum/ethereum/tests/api/eth/sign.test.ts @@ -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", () => { @@ -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); @@ -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); diff --git a/packages/ganache/README.md b/packages/ganache/README.md index 0075c03c3b..584608a4ce 100644 --- a/packages/ganache/README.md +++ b/packages/ganache/README.md @@ -1,6 +1,6 @@

- Ganache + Ganache

@@ -40,6 +40,7 @@ Ganache is an Ethereum simulator that makes developing Ethereum applications fas - Listens for JSON-RPC 2.0 requests over HTTP/WebSockets - Programmatic use in Node.js - Pending Transactions +- Flavors (aka Plugins), like Filecoin ## Getting Started @@ -146,7 +147,7 @@ You can use Ganache programmatically from Node.js. Install Ganache into your npm $ npm install ganache ``` -Then you can use ganache as an [EIP-1193 provider only](#as-an-eip-1193-provider-only), an [EIP-1193 provider and JSON-RPC web server](#as-an-eip-1193-provider-and-json-rpc-web-server), as a [Web3 provider](#as-a-web3js-provider), or an [ethers provider](#as-an-ethersjs-provider). +Then you can use ganache as an [EIP-1193 provider only](#as-an-eip-1193-provider-only), an [EIP-1193 provider and JSON-RPC web server](#as-an-eip-1193-provider-and-json-rpc-web-server), as a [Web3 provider](#as-a-web3js-provider), an [ethers provider](#as-an-ethersjs-provider), or a [viem transport](#as-a-viem-transport). #### As an EIP-1193 provider only: @@ -203,6 +204,20 @@ const ganache = require("ganache"); const provider = new ethers.providers.Web3Provider(ganache.provider()); ``` +#### As a [viem](https://www.npmjs.com/package/viem) transport: + +To use a ganache provider as a viem transport: + +```javascript +import { createWalletClient, custom } from "viem"; +import { localhost } from "viem/chains"; +import ganache from "ganache"; +const client = createWalletClient({ + chain: localhost, + transport: custom(ganache.provider()) +}); +``` + ### Browser Use You can also use Ganache in the browser by adding the following script to your HTML: diff --git a/scripts/update-ethereum-js.ts b/scripts/update-ethereum-js.ts new file mode 100644 index 0000000000..8cc014d9a9 --- /dev/null +++ b/scripts/update-ethereum-js.ts @@ -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 { + 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(); + +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((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);