Skip to content
This repository has been archived by the owner on Dec 4, 2023. It is now read-only.

[WIP] add sapphire precompiles to hardhat #4

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
18 changes: 13 additions & 5 deletions packages/hardhat-core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hardhat",
"version": "2.14.1",
"name": "@oasislabs/hardhat",
"version": "2.14.7",
"author": "Nomic Labs LLC",
"license": "MIT",
"homepage": "https://hardhat.org",
Expand Down Expand Up @@ -29,13 +29,14 @@
"lint": "yarn prettier --check && yarn eslint",
"lint:fix": "yarn prettier --write && yarn eslint --fix",
"eslint": "eslint 'src/**/*.ts' 'test/**/*.ts'",
"format": "eslint 'src/**/*.ts' 'test/**/*.ts' --fix",
"prettier": "prettier \"**/*.{js,md,json}\"",
"test": "mocha --recursive \"test/**/*.ts\"",
"test:except-tracing": "mocha --recursive \"test/**/*.ts\" --invert --grep \"Stack traces\"",
"test:tracing": "mocha --recursive \"test/internal/hardhat-network/{helpers,stack-traces}/**/*.ts\"",
"test:forking": "mocha --recursive \"test/internal/hardhat-network/{helpers,jsonrpc,provider}/**/*.ts\"",
"build": "tsc --build .",
"prepublishOnly": "yarn build",
"prepublishOnly": "npm run build",
"clean": "rimraf builtin-tasks internal types utils *.d.ts *.map *.js build-test tsconfig.tsbuildinfo test/internal/hardhat-network/provider/.hardhat_node_test_cache"
},
"files": [
Expand Down Expand Up @@ -88,7 +89,6 @@
"eslint-plugin-no-only-tests": "3.0.0",
"eslint-plugin-prettier": "3.4.0",
"ethers": "^6.1.0",
"ethers-v5": "npm:ethers@5",
"mocha": "^10.0.0",
"prettier": "2.4.1",
"rimraf": "^3.0.2",
Expand All @@ -99,6 +99,8 @@
},
"dependencies": {
"@ethersproject/abi": "^5.1.2",
"@ethersproject/bytes": "^5.7.0",
"@ethersproject/hash": "^5.7.0",
"@metamask/eth-sig-util": "^4.0.0",
"@nomicfoundation/ethereumjs-block": "5.0.1",
"@nomicfoundation/ethereumjs-blockchain": "7.0.1",
Expand All @@ -109,15 +111,18 @@
"@nomicfoundation/ethereumjs-trie": "6.0.1",
"@nomicfoundation/ethereumjs-tx": "5.0.1",
"@nomicfoundation/ethereumjs-util": "9.0.1",
"@nomicfoundation/ethereumjs-vm": "7.0.1",
"@nomicfoundation/solidity-analyzer": "^0.1.0",
"@oasislabs/ethereumjs-vm": "7.0.1",
"@oasisprotocol/deoxysii": "^0.0.5",
"@oasisprotocol/sapphire-paratime": "^1.0.13",
"@sentry/node": "^5.18.1",
"@types/bn.js": "^5.1.0",
"@types/lru-cache": "^5.1.0",
"abort-controller": "^3.0.0",
"adm-zip": "^0.4.16",
"aggregate-error": "^3.0.0",
"ansi-escapes": "^4.3.0",
"cborg": "^1.9.5",
"chalk": "^2.4.2",
"chokidar": "^3.4.0",
"ci-info": "^2.0.0",
Expand All @@ -126,12 +131,14 @@
"env-paths": "^2.2.0",
"ethereum-cryptography": "^1.0.3",
"ethereumjs-abi": "^0.6.8",
"ethers-v5": "npm:ethers@5",
"find-up": "^2.1.0",
"fp-ts": "1.19.3",
"fs-extra": "^7.0.1",
"glob": "7.2.0",
"immutable": "^4.0.0-rc.12",
"io-ts": "1.10.4",
"js-sha512": "^0.8.0",
"keccak": "^3.0.2",
"lodash": "^4.17.11",
"mnemonist": "^0.38.0",
Expand All @@ -145,6 +152,7 @@
"source-map-support": "^0.5.13",
"stacktrace-parser": "^0.1.10",
"tsort": "0.0.1",
"tweetnacl": "^1.0.3",
"undici": "^5.14.0",
"uuid": "^8.3.2",
"ws": "^7.4.6"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,17 @@ export function createProvider(
paths !== undefined ? getForkCacheDirPath(paths) : undefined,
},
new ModulesLogger(hardhatNetConfig.loggingEnabled),
artifacts
artifacts,
accounts,
hardhatNetConfig.allowUnlimitedContractSize,
hardhatNetConfig.initialDate !== undefined
? parseDateString(hardhatNetConfig.initialDate)
: undefined,
experimentalHardhatNetworkMessageTraceHooks,
forkConfig,
paths !== undefined ? getForkCacheDirPath(paths) : undefined,
hardhatNetConfig.coinbase,
hardhatNetConfig.confidential
);
} else {
const HttpProvider = importProvider<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Block } from "@nomicfoundation/ethereumjs-block";
import { Common } from "@nomicfoundation/ethereumjs-common";
import { TypedTransaction } from "@nomicfoundation/ethereumjs-tx";
import { bufferToHex } from "@nomicfoundation/ethereumjs-util";
import { Bloom } from "@nomicfoundation/ethereumjs-vm";
import { Bloom } from "@oasislabs/ethereumjs-vm";

import { assertHardhatInvariant } from "../../core/errors";
import { bloomFilter, filterLogs } from "./filter";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { bufferToHex, toBuffer } from "@nomicfoundation/ethereumjs-util";
import { Bloom } from "@nomicfoundation/ethereumjs-vm";
import { Bloom } from "@oasislabs/ethereumjs-vm";

import { RpcLogOutput } from "./output";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ import {
} from "@nomicfoundation/ethereumjs-util";
import * as t from "io-ts";
import cloneDeep from "lodash/cloneDeep";
import * as cbor from "cborg";
import * as ethers from "ethers-v5";
import { _TypedDataEncoder } from "@ethersproject/hash";
import { cipher, signedCalls } from "@oasisprotocol/sapphire-paratime";
import { hexlify } from "@ethersproject/bytes";

import { BoundExperimentalHardhatNetworkMessageTraceHook } from "../../../../types";
import { RpcAccessList } from "../../../core/jsonrpc/types/access-list";
Expand Down Expand Up @@ -346,8 +351,28 @@ export class EthModule {

const blockNumberOrPending = await this._resolveNewBlockTag(blockTag);

const callParams = await this._rpcCallRequestToNodeCallParams(rpcCall);
const originCallParams = await this._rpcCallRequestToNodeCallParams(
rpcCall
);

let callParams: CallParams;

if (this._node.confidential && originCallParams.to !== null) {
const {
data: encryptData,
leash,
signature,
} = cbor.decode(originCallParams.data);
await this._verifySignedCall(rpcCall, encryptData, leash, signature);

const data = new Buffer(cbor.encode(encryptData));
callParams = {
...originCallParams,
data,
};
} else {
callParams = originCallParams;
}
const {
result: returnData,
trace,
Expand All @@ -356,15 +381,13 @@ export class EthModule {
} = await this._node.runCall(callParams, blockNumberOrPending);

const code = await this._node.getCodeFromTrace(trace, blockNumberOrPending);

this._logger.logCallTrace(
callParams,
code,
trace,
consoleLogMessages,
error
);

await this._runHardhatNetworkMessageTraceHooks(trace, true);

if (error !== undefined && this._throwOnCallFailures) {
Expand All @@ -377,6 +400,69 @@ export class EthModule {
return bufferToRpcData(returnData.value);
}

private async _verifySignedCall(
rpcCall: RpcCallRequest,
encryptData: cipher.Envelope,
leash: signedCalls.Leash,
signature: Uint8Array
): Promise<void> {
// construct the EthCall with the plain callData
const { format, body: envelopeBody } = encryptData;
let plainData: Uint8Array;
if (format === cipher.Kind.X25519DeoxysII && "nonce" in envelopeBody) {
const { nonce: deoxysiiNonce, data: envelopeData, pk } = envelopeBody;
const aead = cipher.X25519DeoxysII.fromSecretKey(
this._node.getSecretKey(),
pk
);
plainData = await aead.decryptCallData(deoxysiiNonce, envelopeData);
} else {
plainData = envelopeBody as Uint8Array;
}
const ethCall = await this._rpcCallRequestToEthCallParams(
rpcCall,
plainData
);

// first verify signature
const chainId = this._common.chainId();
const { domain, types } = signedCalls.signedCallEIP712Params(
Number(chainId)
);
const digest = _TypedDataEncoder.hash(
domain,
types,
signedCalls.makeSignableCall(ethCall, leash)
);
const signerAddress = ethers.utils.recoverAddress(
digest,
hexlify(signature)
);
if (signerAddress.toLowerCase() !== ethCall.from.toLowerCase())
throw new Error("signer != caller");

// next, verify the leash nonce

const nonce = await this._node.getNextConfirmedNonce(
new Address(toBuffer(ethCall.from)),
"pending"
);
if (nonce > leash.nonce) throw new Error("stale nonce");

// then, verify the leash block hash

const leashBlockNumber = BigInt(leash.block_number);
const block = await this._node.getBlockByNumber(leashBlockNumber);
if (block === undefined) throw new Error("base block not found");
if (!block.hash().equals(Buffer.from(leash.block_hash)))
throw new Error("unexpected base block");

// last, verify the block range
const blockDelta = this._node.getLatestBlockNumber() - leashBlockNumber;
if (blockDelta > leash.block_range)
throw new Error("current block out of range");
}

// eth_chainId

private _chainIdParams(params: any[]): [] {
Expand Down Expand Up @@ -1272,6 +1358,23 @@ export class EthModule {
};
}

private async _rpcCallRequestToEthCallParams(
rpcCall: RpcCallRequest,
plainData: Uint8Array
): Promise<signedCalls.EthCall> {
return {
from:
rpcCall.from !== undefined
? bufferToHex(rpcCall.from)
: bufferToHex(await this._getDefaultCallFrom()),
to: rpcCall.to !== undefined ? bufferToHex(rpcCall.to) : zeroAddress(),
data: hexlify(plainData),
value: rpcCall.value,
gasLimit: rpcCall.gas,
gasPrice: rpcCall.gasPrice,
};
}

private async _rpcTransactionRequestToNodeTransactionParams(
rpcTx: RpcTransactionRequest
): Promise<TransactionParams> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { hexlify } from "@ethersproject/bytes";

import { MethodNotFoundError } from "../../../core/providers/errors";
import { HardhatNode } from "../node";

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

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

public async processRequest(
method: string,
_params: any[] = []
): Promise<any> {
switch (method) {
case "oasis_callDataPublicKey": {
const publicKey = this._node.getPublicKey();
return { key: hexlify(publicKey) };
}
}

throw new MethodNotFoundError(`Method ${method} not found`);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ReturnData } from "./return-data";

import { Block } from "@nomicfoundation/ethereumjs-block";
import { RunBlockResult } from "@nomicfoundation/ethereumjs-vm";
import { RunBlockResult } from "@oasislabs/ethereumjs-vm";

import { HARDHAT_MEMPOOL_SUPPORTED_ORDERS } from "../../constants";
import { BuildInfo, HardhatNetworkChainsConfig } from "../../../types";
Expand Down Expand Up @@ -32,6 +32,7 @@ interface CommonConfig {
coinbase: string;
chains: HardhatNetworkChainsConfig;
allowBlocksWithSameTimestamp: boolean;
confidential?: boolean;
}

export type LocalNodeConfig = CommonConfig;
Expand Down
Loading