hardhat 2.8.4 → 2.9.0-dev.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +3 -61
- package/README.md +1 -1
- package/builtin-tasks/compile.js +86 -51
- package/builtin-tasks/compile.js.map +1 -1
- package/builtin-tasks/node.js.map +1 -1
- package/builtin-tasks/test.js +30 -5
- package/builtin-tasks/test.js.map +1 -1
- package/builtin-tasks/utils/solidity-files-cache.d.ts.map +1 -1
- package/builtin-tasks/utils/solidity-files-cache.js.map +1 -1
- package/internal/cli/analytics.js +2 -2
- package/internal/cli/analytics.js.map +1 -1
- package/internal/cli/cli.js +3 -3
- package/internal/cli/project-creation.d.ts.map +1 -1
- package/internal/cli/project-creation.js +23 -0
- package/internal/cli/project-creation.js.map +1 -1
- package/internal/core/config/config-loading.d.ts.map +1 -1
- package/internal/core/config/config-loading.js.map +1 -1
- package/internal/core/config/config-validation.d.ts.map +1 -1
- package/internal/core/config/config-validation.js +2 -2
- package/internal/core/config/config-validation.js.map +1 -1
- package/internal/core/config/default-config.d.ts +2 -0
- package/internal/core/config/default-config.d.ts.map +1 -1
- package/internal/core/config/default-config.js +1 -0
- package/internal/core/config/default-config.js.map +1 -1
- package/internal/core/errors-list.d.ts +7 -0
- package/internal/core/errors-list.d.ts.map +1 -1
- package/internal/core/errors-list.js +13 -0
- package/internal/core/errors-list.js.map +1 -1
- package/internal/core/jsonrpc/types/input/blockTag.d.ts +3 -3
- package/internal/core/jsonrpc/types/input/blockTag.d.ts.map +1 -1
- package/internal/core/providers/accounts.d.ts +1 -1
- package/internal/core/providers/accounts.d.ts.map +1 -1
- package/internal/core/providers/accounts.js +2 -2
- package/internal/core/providers/accounts.js.map +1 -1
- package/internal/core/providers/construction.d.ts.map +1 -1
- package/internal/core/providers/construction.js +1 -1
- package/internal/core/providers/construction.js.map +1 -1
- package/internal/core/providers/http.d.ts +5 -1
- package/internal/core/providers/http.d.ts.map +1 -1
- package/internal/core/providers/http.js +35 -31
- package/internal/core/providers/http.js.map +1 -1
- package/internal/core/providers/util.d.ts +1 -1
- package/internal/core/providers/util.d.ts.map +1 -1
- package/internal/core/providers/util.js +3 -3
- package/internal/core/providers/util.js.map +1 -1
- package/internal/hardhat-network/jsonrpc/server.d.ts.map +1 -1
- package/internal/hardhat-network/jsonrpc/server.js.map +1 -1
- package/internal/hardhat-network/provider/BlockchainBase.d.ts +26 -0
- package/internal/hardhat-network/provider/BlockchainBase.d.ts.map +1 -0
- package/internal/hardhat-network/provider/BlockchainBase.js +92 -0
- package/internal/hardhat-network/provider/BlockchainBase.js.map +1 -0
- package/internal/hardhat-network/provider/BlockchainData.d.ts +32 -0
- package/internal/hardhat-network/provider/BlockchainData.d.ts.map +1 -1
- package/internal/hardhat-network/provider/BlockchainData.js +78 -1
- package/internal/hardhat-network/provider/BlockchainData.js.map +1 -1
- package/internal/hardhat-network/provider/HardhatBlockchain.d.ts +9 -15
- package/internal/hardhat-network/provider/HardhatBlockchain.d.ts.map +1 -1
- package/internal/hardhat-network/provider/HardhatBlockchain.js +15 -73
- package/internal/hardhat-network/provider/HardhatBlockchain.js.map +1 -1
- package/internal/hardhat-network/provider/fork/ForkBlockchain.d.ts +6 -14
- package/internal/hardhat-network/provider/fork/ForkBlockchain.d.ts.map +1 -1
- package/internal/hardhat-network/provider/fork/ForkBlockchain.js +23 -73
- package/internal/hardhat-network/provider/fork/ForkBlockchain.js.map +1 -1
- package/internal/hardhat-network/provider/modules/eth.js +2 -2
- package/internal/hardhat-network/provider/modules/eth.js.map +1 -1
- package/internal/hardhat-network/provider/modules/hardhat.d.ts +3 -0
- package/internal/hardhat-network/provider/modules/hardhat.d.ts.map +1 -1
- package/internal/hardhat-network/provider/modules/hardhat.js +40 -5
- package/internal/hardhat-network/provider/modules/hardhat.js.map +1 -1
- package/internal/hardhat-network/provider/modules/logger.d.ts +4 -2
- package/internal/hardhat-network/provider/modules/logger.d.ts.map +1 -1
- package/internal/hardhat-network/provider/modules/logger.js +38 -12
- package/internal/hardhat-network/provider/modules/logger.js.map +1 -1
- package/internal/hardhat-network/provider/node-types.d.ts +1 -1
- package/internal/hardhat-network/provider/node-types.d.ts.map +1 -1
- package/internal/hardhat-network/provider/node.d.ts +9 -1
- package/internal/hardhat-network/provider/node.d.ts.map +1 -1
- package/internal/hardhat-network/provider/node.js +57 -8
- package/internal/hardhat-network/provider/node.js.map +1 -1
- package/internal/hardhat-network/provider/provider.d.ts +1 -1
- package/internal/hardhat-network/provider/provider.d.ts.map +1 -1
- package/internal/hardhat-network/provider/provider.js.map +1 -1
- package/internal/hardhat-network/provider/types/HardhatBlockchainInterface.d.ts +2 -1
- package/internal/hardhat-network/provider/types/HardhatBlockchainInterface.d.ts.map +1 -1
- package/internal/hardhat-network/provider/utils/makeForkClient.d.ts.map +1 -1
- package/internal/hardhat-network/provider/utils/makeForkClient.js +2 -1
- package/internal/hardhat-network/provider/utils/makeForkClient.js.map +1 -1
- package/internal/hardhat-network/provider/utils/putGenesisBlock.js +1 -1
- package/internal/hardhat-network/stack-traces/debug.js +1 -1
- package/internal/hardhat-network/stack-traces/debug.js.map +1 -1
- package/internal/hardhat-network/stack-traces/error-inferrer.d.ts.map +1 -1
- package/internal/hardhat-network/stack-traces/error-inferrer.js +21 -12
- package/internal/hardhat-network/stack-traces/error-inferrer.js.map +1 -1
- package/internal/hardhat-network/stack-traces/solidity-errors.js +2 -2
- package/internal/hardhat-network/stack-traces/solidity-errors.js.map +1 -1
- package/internal/hardhat-network/stack-traces/solidity-stack-trace.d.ts +3 -2
- package/internal/hardhat-network/stack-traces/solidity-stack-trace.d.ts.map +1 -1
- package/internal/solidity/compilation-job.d.ts.map +1 -1
- package/internal/solidity/compilation-job.js.map +1 -1
- package/internal/solidity/compiler/downloader.d.ts +2 -2
- package/internal/solidity/compiler/downloader.d.ts.map +1 -1
- package/internal/solidity/compiler/downloader.js +3 -3
- package/internal/solidity/compiler/downloader.js.map +1 -1
- package/internal/util/chunked-promise-all.d.ts +7 -0
- package/internal/util/chunked-promise-all.d.ts.map +1 -0
- package/internal/util/chunked-promise-all.js +31 -0
- package/internal/util/chunked-promise-all.js.map +1 -0
- package/internal/util/download.d.ts.map +1 -1
- package/internal/util/download.js +22 -24
- package/internal/util/download.js.map +1 -1
- package/internal/util/glob.d.ts.map +1 -1
- package/internal/util/glob.js.map +1 -1
- package/internal/util/global-dir.d.ts.map +1 -1
- package/internal/util/global-dir.js.map +1 -1
- package/internal/util/keys-derivation.d.ts +1 -1
- package/internal/util/keys-derivation.d.ts.map +1 -1
- package/internal/util/keys-derivation.js +2 -2
- package/internal/util/keys-derivation.js.map +1 -1
- package/package.json +10 -10
- package/sample-projects/advanced-ts/README.md +1 -1
- package/src/builtin-tasks/compile.ts +94 -76
- package/src/builtin-tasks/node.ts +2 -1
- package/src/builtin-tasks/test.ts +69 -11
- package/src/builtin-tasks/utils/solidity-files-cache.ts +3 -2
- package/src/internal/cli/analytics.ts +2 -2
- package/src/internal/cli/cli.ts +3 -3
- package/src/internal/cli/project-creation.ts +40 -0
- package/src/internal/core/config/config-loading.ts +2 -1
- package/src/internal/core/config/config-validation.ts +2 -0
- package/src/internal/core/config/default-config.ts +1 -0
- package/src/internal/core/errors-list.ts +13 -0
- package/src/internal/core/providers/accounts.ts +4 -2
- package/src/internal/core/providers/construction.ts +3 -1
- package/src/internal/core/providers/http.ts +47 -16
- package/src/internal/core/providers/util.ts +6 -3
- package/src/internal/hardhat-network/jsonrpc/server.ts +2 -1
- package/src/internal/hardhat-network/provider/BlockchainBase.ts +137 -0
- package/src/internal/hardhat-network/provider/BlockchainData.ts +144 -0
- package/src/internal/hardhat-network/provider/HardhatBlockchain.ts +34 -87
- package/src/internal/hardhat-network/provider/fork/ForkBlockchain.ts +45 -90
- package/src/internal/hardhat-network/provider/modules/eth.ts +2 -2
- package/src/internal/hardhat-network/provider/modules/hardhat.ts +51 -5
- package/src/internal/hardhat-network/provider/modules/logger.ts +50 -14
- package/src/internal/hardhat-network/provider/node-types.ts +2 -2
- package/src/internal/hardhat-network/provider/node.ts +81 -8
- package/src/internal/hardhat-network/provider/provider.ts +9 -8
- package/src/internal/hardhat-network/provider/types/HardhatBlockchainInterface.ts +7 -1
- package/src/internal/hardhat-network/provider/utils/makeForkClient.ts +2 -1
- package/src/internal/hardhat-network/provider/utils/putGenesisBlock.ts +1 -1
- package/src/internal/hardhat-network/stack-traces/debug.ts +1 -1
- package/src/internal/hardhat-network/stack-traces/error-inferrer.ts +21 -13
- package/src/internal/hardhat-network/stack-traces/solidity-errors.ts +2 -2
- package/src/internal/hardhat-network/stack-traces/solidity-stack-trace.ts +3 -2
- package/src/internal/solidity/compilation-job.ts +2 -1
- package/src/internal/solidity/compiler/downloader.ts +5 -3
- package/src/internal/util/download.ts +26 -31
- package/src/internal/util/glob.ts +1 -0
- package/src/internal/util/global-dir.ts +2 -1
- package/src/internal/util/keys-derivation.ts +3 -2
- package/src/types/config.ts +4 -0
- package/types/config.d.ts +4 -0
- package/types/config.d.ts.map +1 -1
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
NetworkConfig,
|
|
12
12
|
ProjectPathsConfig,
|
|
13
13
|
} from "../../../types";
|
|
14
|
+
|
|
14
15
|
import { HARDHAT_NETWORK_NAME } from "../../constants";
|
|
15
16
|
import { ModulesLogger } from "../../hardhat-network/provider/modules/logger";
|
|
16
17
|
import {
|
|
@@ -190,7 +191,8 @@ export function applyProviderWrappers(
|
|
|
190
191
|
accounts.mnemonic,
|
|
191
192
|
accounts.path,
|
|
192
193
|
accounts.initialIndex,
|
|
193
|
-
accounts.count
|
|
194
|
+
accounts.count,
|
|
195
|
+
accounts.passphrase
|
|
194
196
|
);
|
|
195
197
|
}
|
|
196
198
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EventEmitter } from "events";
|
|
2
|
-
import
|
|
2
|
+
import { Dispatcher, Pool } from "undici";
|
|
3
3
|
|
|
4
4
|
import { EIP1193Provider, RequestArguments } from "../../../types";
|
|
5
5
|
import {
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
parseJsonResponse,
|
|
14
14
|
SuccessfulJsonRpcResponse,
|
|
15
15
|
} from "../../util/jsonrpc";
|
|
16
|
+
import { getHardhatVersion } from "../../util/packageInfo";
|
|
16
17
|
import { HardhatError } from "../errors";
|
|
17
18
|
import { ERRORS } from "../errors-list";
|
|
18
19
|
|
|
@@ -27,16 +28,40 @@ const MAX_RETRY_AWAIT_SECONDS = 5;
|
|
|
27
28
|
|
|
28
29
|
const TOO_MANY_REQUEST_STATUS = 429;
|
|
29
30
|
|
|
31
|
+
const hardhatVersion = getHardhatVersion();
|
|
32
|
+
|
|
30
33
|
export class HttpProvider extends EventEmitter implements EIP1193Provider {
|
|
31
34
|
private _nextRequestId = 1;
|
|
35
|
+
private _dispatcher: Dispatcher;
|
|
36
|
+
private _path: string;
|
|
37
|
+
private _authHeader: string | undefined;
|
|
32
38
|
|
|
33
39
|
constructor(
|
|
34
40
|
private readonly _url: string,
|
|
35
41
|
private readonly _networkName: string,
|
|
36
42
|
private readonly _extraHeaders: { [name: string]: string } = {},
|
|
37
|
-
private readonly _timeout = 20000
|
|
43
|
+
private readonly _timeout = 20000,
|
|
44
|
+
client: Dispatcher | undefined = undefined
|
|
38
45
|
) {
|
|
39
46
|
super();
|
|
47
|
+
const url = new URL(this._url);
|
|
48
|
+
this._path = url.pathname;
|
|
49
|
+
this._authHeader =
|
|
50
|
+
url.username === ""
|
|
51
|
+
? undefined
|
|
52
|
+
: `Basic ${Buffer.from(
|
|
53
|
+
`${url.username}:${url.password}`,
|
|
54
|
+
"utf-8"
|
|
55
|
+
).toString("base64")}`;
|
|
56
|
+
try {
|
|
57
|
+
this._dispatcher = client ?? new Pool(url.origin);
|
|
58
|
+
} catch (e) {
|
|
59
|
+
if (e instanceof TypeError && e.message === "Invalid URL") {
|
|
60
|
+
e.message += ` ${url.origin}`;
|
|
61
|
+
}
|
|
62
|
+
// eslint-disable-next-line @nomiclabs/hardhat-internal-rules/only-hardhat-error
|
|
63
|
+
throw e;
|
|
64
|
+
}
|
|
40
65
|
}
|
|
41
66
|
|
|
42
67
|
public get url(): string {
|
|
@@ -134,28 +159,32 @@ export class HttpProvider extends EventEmitter implements EIP1193Provider {
|
|
|
134
159
|
request: JsonRpcRequest | JsonRpcRequest[],
|
|
135
160
|
retryNumber = 0
|
|
136
161
|
): Promise<JsonRpcResponse | JsonRpcResponse[]> {
|
|
137
|
-
const { default: fetch } = await import("node-fetch");
|
|
138
|
-
|
|
139
162
|
try {
|
|
140
|
-
const response = await
|
|
163
|
+
const response = await this._dispatcher.request({
|
|
141
164
|
method: "POST",
|
|
165
|
+
path: this._path,
|
|
142
166
|
body: JSON.stringify(request),
|
|
143
|
-
|
|
144
|
-
|
|
167
|
+
maxRedirections: 10,
|
|
168
|
+
headersTimeout:
|
|
145
169
|
process.env.DO_NOT_SET_THIS_ENV_VAR____IS_HARDHAT_CI !== undefined
|
|
146
170
|
? 0
|
|
147
171
|
: this._timeout,
|
|
148
172
|
headers: {
|
|
149
173
|
"Content-Type": "application/json",
|
|
174
|
+
"User-Agent": `hardhat ${hardhatVersion}`,
|
|
175
|
+
Authorization: this._authHeader,
|
|
150
176
|
...this._extraHeaders,
|
|
151
177
|
},
|
|
152
178
|
});
|
|
153
179
|
|
|
154
180
|
if (this._isRateLimitResponse(response)) {
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
|
|
158
|
-
|
|
181
|
+
// "The Fetch Standard allows users to skip consuming the response body
|
|
182
|
+
// by relying on garbage collection to release connection resources.
|
|
183
|
+
// Undici does not do the same. Therefore, it is important to always
|
|
184
|
+
// either consume or cancel the response body."
|
|
185
|
+
// https://undici.nodejs.org/#/?id=garbage-collection
|
|
186
|
+
// It's not clear how to "cancel", so we'll just consume:
|
|
187
|
+
await response.body.text();
|
|
159
188
|
const seconds = this._getRetryAfterSeconds(response);
|
|
160
189
|
if (seconds !== undefined && this._shouldRetry(retryNumber, seconds)) {
|
|
161
190
|
return await this._retry(request, seconds, retryNumber);
|
|
@@ -170,7 +199,7 @@ export class HttpProvider extends EventEmitter implements EIP1193Provider {
|
|
|
170
199
|
);
|
|
171
200
|
}
|
|
172
201
|
|
|
173
|
-
return parseJsonResponse(await response.text());
|
|
202
|
+
return parseJsonResponse(await response.body.text());
|
|
174
203
|
} catch (error: any) {
|
|
175
204
|
if (error.code === "ECONNREFUSED") {
|
|
176
205
|
throw new HardhatError(
|
|
@@ -222,12 +251,14 @@ export class HttpProvider extends EventEmitter implements EIP1193Provider {
|
|
|
222
251
|
return true;
|
|
223
252
|
}
|
|
224
253
|
|
|
225
|
-
private _isRateLimitResponse(response:
|
|
226
|
-
return response.
|
|
254
|
+
private _isRateLimitResponse(response: Dispatcher.ResponseData) {
|
|
255
|
+
return response.statusCode === TOO_MANY_REQUEST_STATUS;
|
|
227
256
|
}
|
|
228
257
|
|
|
229
|
-
private _getRetryAfterSeconds(
|
|
230
|
-
|
|
258
|
+
private _getRetryAfterSeconds(
|
|
259
|
+
response: Dispatcher.ResponseData
|
|
260
|
+
): number | undefined {
|
|
261
|
+
const header = response.headers["retry-after"];
|
|
231
262
|
|
|
232
263
|
if (header === undefined || header === null) {
|
|
233
264
|
return undefined;
|
|
@@ -13,7 +13,8 @@ export function derivePrivateKeys(
|
|
|
13
13
|
mnemonic: string,
|
|
14
14
|
hdpath: string,
|
|
15
15
|
initialIndex: number,
|
|
16
|
-
count: number
|
|
16
|
+
count: number,
|
|
17
|
+
passphrase: string
|
|
17
18
|
): Buffer[] {
|
|
18
19
|
if (hdpath.match(HD_PATH_REGEX) === null) {
|
|
19
20
|
throw new HardhatError(ERRORS.NETWORK.INVALID_HD_PATH, { path: hdpath });
|
|
@@ -28,7 +29,8 @@ export function derivePrivateKeys(
|
|
|
28
29
|
for (let i = initialIndex; i < initialIndex + count; i++) {
|
|
29
30
|
const privateKey = deriveKeyFromMnemonicAndPath(
|
|
30
31
|
mnemonic,
|
|
31
|
-
hdpath + i.toString()
|
|
32
|
+
hdpath + i.toString(),
|
|
33
|
+
passphrase
|
|
32
34
|
);
|
|
33
35
|
|
|
34
36
|
if (privateKey === undefined) {
|
|
@@ -57,7 +59,8 @@ export function normalizeHardhatNetworkAccountsConfig(
|
|
|
57
59
|
accountsConfig.mnemonic,
|
|
58
60
|
accountsConfig.path,
|
|
59
61
|
accountsConfig.initialIndex,
|
|
60
|
-
accountsConfig.count
|
|
62
|
+
accountsConfig.count,
|
|
63
|
+
accountsConfig.passphrase
|
|
61
64
|
).map((pk) => ({
|
|
62
65
|
privateKey: bufferToHex(pk),
|
|
63
66
|
balance: accountsConfig.accountsBalance ?? DEFAULT_HARDHAT_NETWORK_BALANCE,
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { Block } from "@ethereumjs/block";
|
|
2
|
+
import Common from "@ethereumjs/common";
|
|
3
|
+
import { TypedTransaction } from "@ethereumjs/tx";
|
|
4
|
+
import { BN } from "ethereumjs-util";
|
|
5
|
+
|
|
6
|
+
import { assertHardhatInvariant } from "../../core/errors";
|
|
7
|
+
import { BlockchainData } from "./BlockchainData";
|
|
8
|
+
import { RpcReceiptOutput } from "./output";
|
|
9
|
+
|
|
10
|
+
/* eslint-disable @nomiclabs/hardhat-internal-rules/only-hardhat-error */
|
|
11
|
+
|
|
12
|
+
export abstract class BlockchainBase {
|
|
13
|
+
protected readonly _data: BlockchainData;
|
|
14
|
+
|
|
15
|
+
constructor(protected _common: Common) {
|
|
16
|
+
this._data = new BlockchainData(_common);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public abstract addBlock(block: Block): Promise<Block>;
|
|
20
|
+
|
|
21
|
+
public addTransactionReceipts(receipts: RpcReceiptOutput[]) {
|
|
22
|
+
for (const receipt of receipts) {
|
|
23
|
+
this._data.addTransactionReceipt(receipt);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public async delBlock(blockHash: Buffer) {
|
|
28
|
+
this.deleteBlock(blockHash);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public deleteBlock(blockHash: Buffer) {
|
|
32
|
+
const block = this._data.getBlockByHash(blockHash);
|
|
33
|
+
if (block === undefined) {
|
|
34
|
+
throw new Error("Block not found");
|
|
35
|
+
}
|
|
36
|
+
this._delBlock(block.header.number);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public async getBlock(
|
|
40
|
+
blockHashOrNumber: Buffer | BN | number
|
|
41
|
+
): Promise<Block | null> {
|
|
42
|
+
if (
|
|
43
|
+
(typeof blockHashOrNumber === "number" || BN.isBN(blockHashOrNumber)) &&
|
|
44
|
+
this._data.isReservedBlock(new BN(blockHashOrNumber))
|
|
45
|
+
) {
|
|
46
|
+
this._data.fulfillBlockReservation(new BN(blockHashOrNumber));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (typeof blockHashOrNumber === "number") {
|
|
50
|
+
return this._data.getBlockByNumber(new BN(blockHashOrNumber)) ?? null;
|
|
51
|
+
}
|
|
52
|
+
if (BN.isBN(blockHashOrNumber)) {
|
|
53
|
+
return this._data.getBlockByNumber(blockHashOrNumber) ?? null;
|
|
54
|
+
}
|
|
55
|
+
return this._data.getBlockByHash(blockHashOrNumber) ?? null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public abstract getLatestBlockNumber(): BN;
|
|
59
|
+
|
|
60
|
+
public async getLatestBlock(): Promise<Block> {
|
|
61
|
+
const block = await this.getBlock(this.getLatestBlockNumber());
|
|
62
|
+
if (block === null) {
|
|
63
|
+
throw new Error("Block not found");
|
|
64
|
+
}
|
|
65
|
+
return block;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public getLocalTransaction(
|
|
69
|
+
transactionHash: Buffer
|
|
70
|
+
): TypedTransaction | undefined {
|
|
71
|
+
return this._data.getTransaction(transactionHash);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public iterator(
|
|
75
|
+
_name: string,
|
|
76
|
+
_onBlock: (block: Block, reorg: boolean) => void | Promise<void>
|
|
77
|
+
): Promise<number | void> {
|
|
78
|
+
throw new Error("Method not implemented.");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public async putBlock(block: Block): Promise<void> {
|
|
82
|
+
await this.addBlock(block);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public reserveBlocks(
|
|
86
|
+
count: BN,
|
|
87
|
+
interval: BN,
|
|
88
|
+
previousBlockStateRoot: Buffer,
|
|
89
|
+
previousBlockTotalDifficulty: BN
|
|
90
|
+
) {
|
|
91
|
+
this._data.reserveBlocks(
|
|
92
|
+
this.getLatestBlockNumber().addn(1),
|
|
93
|
+
count,
|
|
94
|
+
interval,
|
|
95
|
+
previousBlockStateRoot,
|
|
96
|
+
previousBlockTotalDifficulty
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
protected _delBlock(blockNumber: BN): void {
|
|
101
|
+
let i = blockNumber;
|
|
102
|
+
|
|
103
|
+
while (i.lte(this.getLatestBlockNumber())) {
|
|
104
|
+
if (this._data.isReservedBlock(i)) {
|
|
105
|
+
const reservation = this._data.cancelReservationWithBlock(i);
|
|
106
|
+
i = reservation.last.addn(1);
|
|
107
|
+
} else {
|
|
108
|
+
const current = this._data.getBlockByNumber(i);
|
|
109
|
+
if (current !== undefined) {
|
|
110
|
+
this._data.removeBlock(current);
|
|
111
|
+
}
|
|
112
|
+
i = i.addn(1);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
protected async _computeTotalDifficulty(block: Block): Promise<BN> {
|
|
118
|
+
const difficulty = block.header.difficulty;
|
|
119
|
+
const blockNumber = block.header.number;
|
|
120
|
+
|
|
121
|
+
if (blockNumber.eqn(0)) {
|
|
122
|
+
return difficulty;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const parentBlock = await this.getBlock(blockNumber.subn(1));
|
|
126
|
+
assertHardhatInvariant(parentBlock !== null, "Parent block should exist");
|
|
127
|
+
|
|
128
|
+
const parentHash = parentBlock.hash();
|
|
129
|
+
const parentTD = this._data.getTotalDifficulty(parentHash);
|
|
130
|
+
assertHardhatInvariant(
|
|
131
|
+
parentTD !== undefined,
|
|
132
|
+
"Parent block should have total difficulty"
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
return parentTD.add(difficulty);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
import { Block } from "@ethereumjs/block";
|
|
2
|
+
import Common from "@ethereumjs/common";
|
|
2
3
|
import { TypedTransaction } from "@ethereumjs/tx";
|
|
3
4
|
import Bloom from "@ethereumjs/vm/dist/bloom";
|
|
4
5
|
import { BN, bufferToHex } from "ethereumjs-util";
|
|
5
6
|
|
|
7
|
+
import { assertHardhatInvariant } from "../../core/errors";
|
|
6
8
|
import { bloomFilter, filterLogs } from "./filter";
|
|
7
9
|
import { FilterParams } from "./node-types";
|
|
8
10
|
import { RpcLogOutput, RpcReceiptOutput } from "./output";
|
|
9
11
|
|
|
12
|
+
interface Reservation {
|
|
13
|
+
first: BN;
|
|
14
|
+
last: BN;
|
|
15
|
+
interval: BN;
|
|
16
|
+
previousBlockStateRoot: Buffer;
|
|
17
|
+
previousBlockTotalDifficulty: BN;
|
|
18
|
+
}
|
|
19
|
+
|
|
10
20
|
export class BlockchainData {
|
|
11
21
|
private _blocksByNumber: Map<number, Block> = new Map();
|
|
12
22
|
private _blocksByHash: Map<string, Block> = new Map();
|
|
@@ -14,6 +24,26 @@ export class BlockchainData {
|
|
|
14
24
|
private _transactions: Map<string, TypedTransaction> = new Map();
|
|
15
25
|
private _transactionReceipts: Map<string, RpcReceiptOutput> = new Map();
|
|
16
26
|
private _totalDifficulty: Map<string, BN> = new Map();
|
|
27
|
+
private _blockReservations: Reservation[] = new Array();
|
|
28
|
+
|
|
29
|
+
constructor(private _common: Common) {}
|
|
30
|
+
|
|
31
|
+
public reserveBlocks(
|
|
32
|
+
first: BN,
|
|
33
|
+
count: BN,
|
|
34
|
+
interval: BN,
|
|
35
|
+
previousBlockStateRoot: Buffer,
|
|
36
|
+
previousBlockTotalDifficulty: BN
|
|
37
|
+
) {
|
|
38
|
+
const reservation: Reservation = {
|
|
39
|
+
first,
|
|
40
|
+
last: first.add(count.subn(1)),
|
|
41
|
+
interval,
|
|
42
|
+
previousBlockStateRoot,
|
|
43
|
+
previousBlockTotalDifficulty,
|
|
44
|
+
};
|
|
45
|
+
this._blockReservations.push(reservation);
|
|
46
|
+
}
|
|
17
47
|
|
|
18
48
|
public getBlockByNumber(blockNumber: BN) {
|
|
19
49
|
return this._blocksByNumber.get(blockNumber.toNumber());
|
|
@@ -88,6 +118,11 @@ export class BlockchainData {
|
|
|
88
118
|
}
|
|
89
119
|
}
|
|
90
120
|
|
|
121
|
+
/**
|
|
122
|
+
* WARNING: this method can leave the blockchain in an invalid state where
|
|
123
|
+
* there are gaps between blocks. Ideally we should have a method that removes
|
|
124
|
+
* the given block and all the following blocks.
|
|
125
|
+
*/
|
|
91
126
|
public removeBlock(block: Block) {
|
|
92
127
|
const blockHash = bufferToHex(block.hash());
|
|
93
128
|
const blockNumber = new BN(block.header.number).toNumber();
|
|
@@ -110,4 +145,113 @@ export class BlockchainData {
|
|
|
110
145
|
public addTransactionReceipt(receipt: RpcReceiptOutput) {
|
|
111
146
|
this._transactionReceipts.set(receipt.transactionHash, receipt);
|
|
112
147
|
}
|
|
148
|
+
|
|
149
|
+
public isReservedBlock(blockNumber: BN): boolean {
|
|
150
|
+
return this._findBlockReservation(blockNumber) !== -1;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private _findBlockReservation(blockNumber: BN): number {
|
|
154
|
+
return this._blockReservations.findIndex(
|
|
155
|
+
(reservation) =>
|
|
156
|
+
reservation.first.lte(blockNumber) && blockNumber.lte(reservation.last)
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* WARNING: this method only removes the given reservation and can result in
|
|
162
|
+
* gaps in the reservations array. Ideally we should have a method that
|
|
163
|
+
* removes the given reservation and all the following reservations.
|
|
164
|
+
*/
|
|
165
|
+
private _removeReservation(index: number): Reservation {
|
|
166
|
+
assertHardhatInvariant(
|
|
167
|
+
index in this._blockReservations,
|
|
168
|
+
`Reservation ${index} does not exist`
|
|
169
|
+
);
|
|
170
|
+
const reservation = this._blockReservations[index];
|
|
171
|
+
|
|
172
|
+
this._blockReservations.splice(index, 1);
|
|
173
|
+
|
|
174
|
+
return reservation;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Cancel and return the reservation that has block `blockNumber`
|
|
179
|
+
*/
|
|
180
|
+
public cancelReservationWithBlock(blockNumber: BN): Reservation {
|
|
181
|
+
return this._removeReservation(this._findBlockReservation(blockNumber));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
public fulfillBlockReservation(blockNumber: BN) {
|
|
185
|
+
// in addition to adding the given block, the reservation needs to be split
|
|
186
|
+
// in two in order to accomodate access to the given block.
|
|
187
|
+
|
|
188
|
+
const reservationIndex = this._findBlockReservation(blockNumber);
|
|
189
|
+
assertHardhatInvariant(
|
|
190
|
+
reservationIndex !== -1,
|
|
191
|
+
`No reservation to fill for block number ${blockNumber}`
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
// capture the timestamp before removing the reservation:
|
|
195
|
+
const timestamp = this._calculateTimestampForReservedBlock(blockNumber);
|
|
196
|
+
|
|
197
|
+
// split the block reservation:
|
|
198
|
+
const oldReservation = this._removeReservation(reservationIndex);
|
|
199
|
+
|
|
200
|
+
if (!blockNumber.eq(oldReservation.first)) {
|
|
201
|
+
this._blockReservations.push({
|
|
202
|
+
...oldReservation,
|
|
203
|
+
last: blockNumber.subn(1),
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (!blockNumber.eq(oldReservation.last)) {
|
|
208
|
+
this._blockReservations.push({
|
|
209
|
+
...oldReservation,
|
|
210
|
+
first: blockNumber.addn(1),
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
this.addBlock(
|
|
215
|
+
Block.fromBlockData(
|
|
216
|
+
{
|
|
217
|
+
header: {
|
|
218
|
+
number: blockNumber,
|
|
219
|
+
stateRoot: oldReservation.previousBlockStateRoot,
|
|
220
|
+
timestamp,
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
{ common: this._common }
|
|
224
|
+
),
|
|
225
|
+
oldReservation.previousBlockTotalDifficulty
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
private _calculateTimestampForReservedBlock(blockNumber: BN): BN {
|
|
230
|
+
const reservationIndex = this._findBlockReservation(blockNumber);
|
|
231
|
+
|
|
232
|
+
assertHardhatInvariant(
|
|
233
|
+
reservationIndex !== -1,
|
|
234
|
+
`Block ${blockNumber.toString()} does not lie within any of the reservations.`
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
const reservation = this._blockReservations[reservationIndex];
|
|
238
|
+
|
|
239
|
+
const blockNumberBeforeReservation = reservation.first.subn(1);
|
|
240
|
+
|
|
241
|
+
const blockBeforeReservation = this.getBlockByNumber(
|
|
242
|
+
blockNumberBeforeReservation
|
|
243
|
+
);
|
|
244
|
+
assertHardhatInvariant(
|
|
245
|
+
blockBeforeReservation !== undefined,
|
|
246
|
+
`Reservation after block ${blockNumberBeforeReservation.toString()} cannot be created because that block does not exist`
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
const previousTimestamp = this.isReservedBlock(blockNumberBeforeReservation)
|
|
250
|
+
? this._calculateTimestampForReservedBlock(blockNumberBeforeReservation)
|
|
251
|
+
: blockBeforeReservation.header.timestamp;
|
|
252
|
+
|
|
253
|
+
return previousTimestamp.add(
|
|
254
|
+
reservation.interval.mul(blockNumber.sub(reservation.first).addn(1))
|
|
255
|
+
);
|
|
256
|
+
}
|
|
113
257
|
}
|
|
@@ -1,60 +1,50 @@
|
|
|
1
1
|
import { Block } from "@ethereumjs/block";
|
|
2
|
+
import Common from "@ethereumjs/common";
|
|
2
3
|
import { TypedTransaction } from "@ethereumjs/tx";
|
|
3
4
|
import { BN, zeros } from "ethereumjs-util";
|
|
4
5
|
|
|
5
|
-
import {
|
|
6
|
+
import { BlockchainBase } from "./BlockchainBase";
|
|
6
7
|
import { FilterParams } from "./node-types";
|
|
7
|
-
import { RpcLogOutput
|
|
8
|
+
import { RpcLogOutput } from "./output";
|
|
8
9
|
import { HardhatBlockchainInterface } from "./types/HardhatBlockchainInterface";
|
|
9
10
|
|
|
10
11
|
/* eslint-disable @nomiclabs/hardhat-internal-rules/only-hardhat-error */
|
|
11
12
|
|
|
12
|
-
export class HardhatBlockchain
|
|
13
|
-
|
|
13
|
+
export class HardhatBlockchain
|
|
14
|
+
extends BlockchainBase
|
|
15
|
+
implements HardhatBlockchainInterface
|
|
16
|
+
{
|
|
14
17
|
private _length = 0;
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (block === undefined) {
|
|
19
|
-
throw new Error("No block available");
|
|
20
|
-
}
|
|
21
|
-
return block;
|
|
19
|
+
constructor(common: Common) {
|
|
20
|
+
super(common);
|
|
22
21
|
}
|
|
23
22
|
|
|
24
|
-
public
|
|
25
|
-
|
|
26
|
-
): Promise<Block | null> {
|
|
27
|
-
if (typeof blockHashOrNumber === "number") {
|
|
28
|
-
return this._data.getBlockByNumber(new BN(blockHashOrNumber)) ?? null;
|
|
29
|
-
}
|
|
30
|
-
if (BN.isBN(blockHashOrNumber)) {
|
|
31
|
-
return this._data.getBlockByNumber(blockHashOrNumber) ?? null;
|
|
32
|
-
}
|
|
33
|
-
return this._data.getBlockByHash(blockHashOrNumber) ?? null;
|
|
23
|
+
public getLatestBlockNumber(): BN {
|
|
24
|
+
return new BN(this._length - 1);
|
|
34
25
|
}
|
|
35
26
|
|
|
36
27
|
public async addBlock(block: Block): Promise<Block> {
|
|
37
28
|
this._validateBlock(block);
|
|
38
|
-
const totalDifficulty = this._computeTotalDifficulty(block);
|
|
29
|
+
const totalDifficulty = await this._computeTotalDifficulty(block);
|
|
39
30
|
this._data.addBlock(block, totalDifficulty);
|
|
40
31
|
this._length += 1;
|
|
41
32
|
return block;
|
|
42
33
|
}
|
|
43
34
|
|
|
44
|
-
public
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
this.deleteBlock(blockHash);
|
|
35
|
+
public reserveBlocks(
|
|
36
|
+
count: BN,
|
|
37
|
+
interval: BN,
|
|
38
|
+
previousBlockStateRoot: Buffer,
|
|
39
|
+
previousBlockTotalDifficulty: BN
|
|
40
|
+
) {
|
|
41
|
+
super.reserveBlocks(
|
|
42
|
+
count,
|
|
43
|
+
interval,
|
|
44
|
+
previousBlockStateRoot,
|
|
45
|
+
previousBlockTotalDifficulty
|
|
46
|
+
);
|
|
47
|
+
this._length = this._length + count.toNumber();
|
|
58
48
|
}
|
|
59
49
|
|
|
60
50
|
public deleteLaterBlocks(block: Block): void {
|
|
@@ -62,12 +52,8 @@ export class HardhatBlockchain implements HardhatBlockchainInterface {
|
|
|
62
52
|
if (actual === undefined) {
|
|
63
53
|
throw new Error("Invalid block");
|
|
64
54
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
);
|
|
68
|
-
if (nextBlock !== undefined) {
|
|
69
|
-
this._delBlock(nextBlock);
|
|
70
|
-
}
|
|
55
|
+
|
|
56
|
+
this._delBlock(actual.header.number.addn(1));
|
|
71
57
|
}
|
|
72
58
|
|
|
73
59
|
public async getTotalDifficulty(blockHash: Buffer): Promise<BN> {
|
|
@@ -84,12 +70,6 @@ export class HardhatBlockchain implements HardhatBlockchainInterface {
|
|
|
84
70
|
return this.getLocalTransaction(transactionHash);
|
|
85
71
|
}
|
|
86
72
|
|
|
87
|
-
public getLocalTransaction(
|
|
88
|
-
transactionHash: Buffer
|
|
89
|
-
): TypedTransaction | undefined {
|
|
90
|
-
return this._data.getTransaction(transactionHash);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
73
|
public async getBlockByTransactionHash(
|
|
94
74
|
transactionHash: Buffer
|
|
95
75
|
): Promise<Block | null> {
|
|
@@ -101,36 +81,21 @@ export class HardhatBlockchain implements HardhatBlockchainInterface {
|
|
|
101
81
|
return this._data.getTransactionReceipt(transactionHash) ?? null;
|
|
102
82
|
}
|
|
103
83
|
|
|
104
|
-
public addTransactionReceipts(receipts: RpcReceiptOutput[]) {
|
|
105
|
-
for (const receipt of receipts) {
|
|
106
|
-
this._data.addTransactionReceipt(receipt);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
84
|
public async getLogs(filterParams: FilterParams): Promise<RpcLogOutput[]> {
|
|
111
85
|
return this._data.getLogs(filterParams);
|
|
112
86
|
}
|
|
113
87
|
|
|
114
|
-
public iterator(
|
|
115
|
-
_name: string,
|
|
116
|
-
_onBlock: (block: Block, reorg: boolean) => void | Promise<void>
|
|
117
|
-
): Promise<number | void> {
|
|
118
|
-
throw new Error("Method not implemented.");
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
public async getBaseFee(): Promise<BN> {
|
|
122
|
-
const latestBlock = await this.getLatestBlock();
|
|
123
|
-
return latestBlock.header.calcNextBaseFee();
|
|
124
|
-
}
|
|
125
|
-
|
|
126
88
|
private _validateBlock(block: Block) {
|
|
127
89
|
const blockNumber = block.header.number.toNumber();
|
|
128
90
|
const parentHash = block.header.parentHash;
|
|
129
91
|
const parent = this._data.getBlockByNumber(new BN(blockNumber - 1));
|
|
130
92
|
|
|
131
93
|
if (this._length !== blockNumber) {
|
|
132
|
-
throw new Error(
|
|
94
|
+
throw new Error(
|
|
95
|
+
`Invalid block number ${blockNumber}. Expected ${this._length}.`
|
|
96
|
+
);
|
|
133
97
|
}
|
|
98
|
+
|
|
134
99
|
if (
|
|
135
100
|
(blockNumber === 0 && !parentHash.equals(zeros(32))) ||
|
|
136
101
|
(blockNumber > 0 &&
|
|
@@ -141,26 +106,8 @@ export class HardhatBlockchain implements HardhatBlockchainInterface {
|
|
|
141
106
|
}
|
|
142
107
|
}
|
|
143
108
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
return difficulty;
|
|
148
|
-
}
|
|
149
|
-
const parentTD = this._data.getTotalDifficulty(block.header.parentHash);
|
|
150
|
-
if (parentTD === undefined) {
|
|
151
|
-
throw new Error("This should never happen");
|
|
152
|
-
}
|
|
153
|
-
return parentTD.add(difficulty);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
private _delBlock(block: Block): void {
|
|
157
|
-
const blockNumber = block.header.number.toNumber();
|
|
158
|
-
for (let i = blockNumber; i < this._length; i++) {
|
|
159
|
-
const current = this._data.getBlockByNumber(new BN(i));
|
|
160
|
-
if (current !== undefined) {
|
|
161
|
-
this._data.removeBlock(current);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
this._length = blockNumber;
|
|
109
|
+
protected _delBlock(blockNumber: BN): void {
|
|
110
|
+
super._delBlock(blockNumber);
|
|
111
|
+
this._length = blockNumber.toNumber();
|
|
165
112
|
}
|
|
166
113
|
}
|