hardhat 2.8.4 → 2.9.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/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 +7 -2
- 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/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 +16 -3
- 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
|
@@ -216,6 +216,19 @@ If you were trying to create a new project, please try again using Windows Subsy
|
|
|
216
216
|
You can learn how to use Hardhat by reading the [Getting Started guide](../getting-started).`,
|
|
217
217
|
shouldBeReported: false,
|
|
218
218
|
},
|
|
219
|
+
CONFLICTING_FILES: {
|
|
220
|
+
number: 16,
|
|
221
|
+
message: `The directory %dest% contains files that could conflict:
|
|
222
|
+
|
|
223
|
+
%conflicts%
|
|
224
|
+
|
|
225
|
+
Either try using a new directory, or remove the files listed above.`,
|
|
226
|
+
title: "conflicting files during project creation",
|
|
227
|
+
description: `You are trying to create a new hardhat project, but there are existing files that would be overwritten by the creation process.
|
|
228
|
+
|
|
229
|
+
Either try using a new directory name, or remove the conflicting files.`,
|
|
230
|
+
shouldBeReported: false,
|
|
231
|
+
},
|
|
219
232
|
},
|
|
220
233
|
NETWORK: {
|
|
221
234
|
CONFIG_NOT_FOUND: {
|
|
@@ -307,13 +307,15 @@ export class HDWalletProvider extends LocalAccountsProvider {
|
|
|
307
307
|
mnemonic: string,
|
|
308
308
|
hdpath: string = "m/44'/60'/0'/0/",
|
|
309
309
|
initialIndex: number = 0,
|
|
310
|
-
count: number = 10
|
|
310
|
+
count: number = 10,
|
|
311
|
+
passphrase: string = ""
|
|
311
312
|
) {
|
|
312
313
|
const privateKeys = derivePrivateKeys(
|
|
313
314
|
mnemonic,
|
|
314
315
|
hdpath,
|
|
315
316
|
initialIndex,
|
|
316
|
-
count
|
|
317
|
+
count,
|
|
318
|
+
passphrase
|
|
317
319
|
);
|
|
318
320
|
|
|
319
321
|
const { bufferToHex } = require("ethereumjs-util");
|
|
@@ -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,
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import type WsT from "ws";
|
|
2
|
+
|
|
1
3
|
import debug from "debug";
|
|
2
4
|
import http, { Server } from "http";
|
|
3
5
|
import { AddressInfo } from "net";
|
|
4
|
-
import
|
|
6
|
+
import { Client } from "undici";
|
|
5
7
|
|
|
6
8
|
import {
|
|
7
9
|
EIP1193Provider,
|
|
@@ -42,9 +44,20 @@ export class JsonRpcServer implements IJsonRpcServer {
|
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
public getProvider = (name = "json-rpc"): EIP1193Provider => {
|
|
45
|
-
const { address, port } = this._httpServer.address() as AddressInfo;
|
|
47
|
+
const { address, port } = this._httpServer.address() as AddressInfo;
|
|
48
|
+
|
|
49
|
+
const dispatcher = new Client(`http://${address}:${port}/`, {
|
|
50
|
+
keepAliveTimeout: 10,
|
|
51
|
+
keepAliveMaxTimeout: 10,
|
|
52
|
+
});
|
|
46
53
|
|
|
47
|
-
return new HttpProvider(
|
|
54
|
+
return new HttpProvider(
|
|
55
|
+
`http://${address}:${port}/`,
|
|
56
|
+
name,
|
|
57
|
+
{},
|
|
58
|
+
20000,
|
|
59
|
+
dispatcher
|
|
60
|
+
);
|
|
48
61
|
};
|
|
49
62
|
|
|
50
63
|
public listen = (): Promise<{ address: string; port: number }> => {
|
|
@@ -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
|
}
|