hardhat 2.8.2 → 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.
Files changed (176) hide show
  1. package/LICENSE +3 -61
  2. package/README.md +1 -1
  3. package/builtin-tasks/compile.js +95 -54
  4. package/builtin-tasks/compile.js.map +1 -1
  5. package/builtin-tasks/node.js.map +1 -1
  6. package/builtin-tasks/task-names.d.ts +1 -0
  7. package/builtin-tasks/task-names.d.ts.map +1 -1
  8. package/builtin-tasks/task-names.js +3 -1
  9. package/builtin-tasks/task-names.js.map +1 -1
  10. package/builtin-tasks/test.js +30 -5
  11. package/builtin-tasks/test.js.map +1 -1
  12. package/builtin-tasks/utils/solidity-files-cache.d.ts.map +1 -1
  13. package/builtin-tasks/utils/solidity-files-cache.js.map +1 -1
  14. package/internal/artifacts.d.ts +10 -8
  15. package/internal/artifacts.d.ts.map +1 -1
  16. package/internal/artifacts.js +15 -10
  17. package/internal/artifacts.js.map +1 -1
  18. package/internal/cli/analytics.js +2 -2
  19. package/internal/cli/analytics.js.map +1 -1
  20. package/internal/cli/cli.js +3 -3
  21. package/internal/cli/project-creation.d.ts.map +1 -1
  22. package/internal/cli/project-creation.js +24 -1
  23. package/internal/cli/project-creation.js.map +1 -1
  24. package/internal/core/config/config-loading.d.ts.map +1 -1
  25. package/internal/core/config/config-loading.js.map +1 -1
  26. package/internal/core/config/config-validation.d.ts.map +1 -1
  27. package/internal/core/config/config-validation.js +2 -2
  28. package/internal/core/config/config-validation.js.map +1 -1
  29. package/internal/core/config/default-config.d.ts +3 -0
  30. package/internal/core/config/default-config.d.ts.map +1 -1
  31. package/internal/core/config/default-config.js +3 -1
  32. package/internal/core/config/default-config.js.map +1 -1
  33. package/internal/core/errors-list.d.ts +7 -0
  34. package/internal/core/errors-list.d.ts.map +1 -1
  35. package/internal/core/errors-list.js +16 -3
  36. package/internal/core/errors-list.js.map +1 -1
  37. package/internal/core/jsonrpc/types/input/blockTag.d.ts +3 -3
  38. package/internal/core/jsonrpc/types/input/blockTag.d.ts.map +1 -1
  39. package/internal/core/providers/accounts.d.ts +1 -1
  40. package/internal/core/providers/accounts.d.ts.map +1 -1
  41. package/internal/core/providers/accounts.js +6 -4
  42. package/internal/core/providers/accounts.js.map +1 -1
  43. package/internal/core/providers/construction.d.ts.map +1 -1
  44. package/internal/core/providers/construction.js +1 -1
  45. package/internal/core/providers/construction.js.map +1 -1
  46. package/internal/core/providers/http.d.ts +5 -1
  47. package/internal/core/providers/http.d.ts.map +1 -1
  48. package/internal/core/providers/http.js +35 -31
  49. package/internal/core/providers/http.js.map +1 -1
  50. package/internal/core/providers/util.d.ts +1 -1
  51. package/internal/core/providers/util.d.ts.map +1 -1
  52. package/internal/core/providers/util.js +3 -3
  53. package/internal/core/providers/util.js.map +1 -1
  54. package/internal/core/tasks/task-definitions.d.ts +8 -8
  55. package/internal/core/tasks/task-definitions.js +8 -8
  56. package/internal/hardhat-network/jsonrpc/server.d.ts.map +1 -1
  57. package/internal/hardhat-network/jsonrpc/server.js.map +1 -1
  58. package/internal/hardhat-network/provider/BlockchainBase.d.ts +26 -0
  59. package/internal/hardhat-network/provider/BlockchainBase.d.ts.map +1 -0
  60. package/internal/hardhat-network/provider/BlockchainBase.js +92 -0
  61. package/internal/hardhat-network/provider/BlockchainBase.js.map +1 -0
  62. package/internal/hardhat-network/provider/BlockchainData.d.ts +32 -0
  63. package/internal/hardhat-network/provider/BlockchainData.d.ts.map +1 -1
  64. package/internal/hardhat-network/provider/BlockchainData.js +78 -1
  65. package/internal/hardhat-network/provider/BlockchainData.js.map +1 -1
  66. package/internal/hardhat-network/provider/HardhatBlockchain.d.ts +9 -15
  67. package/internal/hardhat-network/provider/HardhatBlockchain.d.ts.map +1 -1
  68. package/internal/hardhat-network/provider/HardhatBlockchain.js +15 -73
  69. package/internal/hardhat-network/provider/HardhatBlockchain.js.map +1 -1
  70. package/internal/hardhat-network/provider/fork/ForkBlockchain.d.ts +6 -14
  71. package/internal/hardhat-network/provider/fork/ForkBlockchain.d.ts.map +1 -1
  72. package/internal/hardhat-network/provider/fork/ForkBlockchain.js +23 -73
  73. package/internal/hardhat-network/provider/fork/ForkBlockchain.js.map +1 -1
  74. package/internal/hardhat-network/provider/modules/eth.js +2 -2
  75. package/internal/hardhat-network/provider/modules/eth.js.map +1 -1
  76. package/internal/hardhat-network/provider/modules/hardhat.d.ts +3 -0
  77. package/internal/hardhat-network/provider/modules/hardhat.d.ts.map +1 -1
  78. package/internal/hardhat-network/provider/modules/hardhat.js +40 -5
  79. package/internal/hardhat-network/provider/modules/hardhat.js.map +1 -1
  80. package/internal/hardhat-network/provider/modules/logger.d.ts +4 -2
  81. package/internal/hardhat-network/provider/modules/logger.d.ts.map +1 -1
  82. package/internal/hardhat-network/provider/modules/logger.js +38 -12
  83. package/internal/hardhat-network/provider/modules/logger.js.map +1 -1
  84. package/internal/hardhat-network/provider/node-types.d.ts +1 -1
  85. package/internal/hardhat-network/provider/node-types.d.ts.map +1 -1
  86. package/internal/hardhat-network/provider/node.d.ts +9 -1
  87. package/internal/hardhat-network/provider/node.d.ts.map +1 -1
  88. package/internal/hardhat-network/provider/node.js +61 -10
  89. package/internal/hardhat-network/provider/node.js.map +1 -1
  90. package/internal/hardhat-network/provider/provider.d.ts +1 -1
  91. package/internal/hardhat-network/provider/provider.d.ts.map +1 -1
  92. package/internal/hardhat-network/provider/provider.js.map +1 -1
  93. package/internal/hardhat-network/provider/types/HardhatBlockchainInterface.d.ts +2 -1
  94. package/internal/hardhat-network/provider/types/HardhatBlockchainInterface.d.ts.map +1 -1
  95. package/internal/hardhat-network/provider/utils/makeForkClient.d.ts.map +1 -1
  96. package/internal/hardhat-network/provider/utils/makeForkClient.js +2 -1
  97. package/internal/hardhat-network/provider/utils/makeForkClient.js.map +1 -1
  98. package/internal/hardhat-network/provider/utils/putGenesisBlock.js +1 -1
  99. package/internal/hardhat-network/stack-traces/debug.js +1 -1
  100. package/internal/hardhat-network/stack-traces/debug.js.map +1 -1
  101. package/internal/hardhat-network/stack-traces/error-inferrer.d.ts.map +1 -1
  102. package/internal/hardhat-network/stack-traces/error-inferrer.js +21 -12
  103. package/internal/hardhat-network/stack-traces/error-inferrer.js.map +1 -1
  104. package/internal/hardhat-network/stack-traces/solidity-errors.js +2 -2
  105. package/internal/hardhat-network/stack-traces/solidity-errors.js.map +1 -1
  106. package/internal/hardhat-network/stack-traces/solidity-stack-trace.d.ts +3 -2
  107. package/internal/hardhat-network/stack-traces/solidity-stack-trace.d.ts.map +1 -1
  108. package/internal/solidity/compilation-job.d.ts.map +1 -1
  109. package/internal/solidity/compilation-job.js.map +1 -1
  110. package/internal/solidity/compiler/downloader.d.ts +2 -2
  111. package/internal/solidity/compiler/downloader.d.ts.map +1 -1
  112. package/internal/solidity/compiler/downloader.js +3 -3
  113. package/internal/solidity/compiler/downloader.js.map +1 -1
  114. package/internal/util/chunked-promise-all.d.ts +7 -0
  115. package/internal/util/chunked-promise-all.d.ts.map +1 -0
  116. package/internal/util/chunked-promise-all.js +31 -0
  117. package/internal/util/chunked-promise-all.js.map +1 -0
  118. package/internal/util/download.d.ts.map +1 -1
  119. package/internal/util/download.js +23 -25
  120. package/internal/util/download.js.map +1 -1
  121. package/internal/util/glob.d.ts.map +1 -1
  122. package/internal/util/glob.js.map +1 -1
  123. package/internal/util/global-dir.d.ts.map +1 -1
  124. package/internal/util/global-dir.js.map +1 -1
  125. package/internal/util/keys-derivation.d.ts +1 -1
  126. package/internal/util/keys-derivation.d.ts.map +1 -1
  127. package/internal/util/keys-derivation.js +2 -2
  128. package/internal/util/keys-derivation.js.map +1 -1
  129. package/package.json +11 -11
  130. package/sample-projects/advanced/.prettierrc +1 -0
  131. package/sample-projects/advanced-ts/README.md +1 -1
  132. package/src/builtin-tasks/compile.ts +106 -79
  133. package/src/builtin-tasks/node.ts +2 -1
  134. package/src/builtin-tasks/task-names.ts +2 -0
  135. package/src/builtin-tasks/test.ts +69 -11
  136. package/src/builtin-tasks/utils/solidity-files-cache.ts +3 -2
  137. package/src/internal/artifacts.ts +25 -20
  138. package/src/internal/cli/analytics.ts +2 -2
  139. package/src/internal/cli/cli.ts +3 -3
  140. package/src/internal/cli/project-creation.ts +41 -1
  141. package/src/internal/core/config/config-loading.ts +2 -1
  142. package/src/internal/core/config/config-validation.ts +2 -0
  143. package/src/internal/core/config/default-config.ts +3 -1
  144. package/src/internal/core/errors-list.ts +16 -3
  145. package/src/internal/core/providers/accounts.ts +8 -5
  146. package/src/internal/core/providers/construction.ts +3 -1
  147. package/src/internal/core/providers/http.ts +47 -16
  148. package/src/internal/core/providers/util.ts +6 -3
  149. package/src/internal/core/tasks/task-definitions.ts +8 -8
  150. package/src/internal/hardhat-network/jsonrpc/server.ts +2 -1
  151. package/src/internal/hardhat-network/provider/BlockchainBase.ts +137 -0
  152. package/src/internal/hardhat-network/provider/BlockchainData.ts +144 -0
  153. package/src/internal/hardhat-network/provider/HardhatBlockchain.ts +34 -87
  154. package/src/internal/hardhat-network/provider/fork/ForkBlockchain.ts +45 -90
  155. package/src/internal/hardhat-network/provider/modules/eth.ts +2 -2
  156. package/src/internal/hardhat-network/provider/modules/hardhat.ts +51 -5
  157. package/src/internal/hardhat-network/provider/modules/logger.ts +50 -14
  158. package/src/internal/hardhat-network/provider/node-types.ts +2 -2
  159. package/src/internal/hardhat-network/provider/node.ts +85 -11
  160. package/src/internal/hardhat-network/provider/provider.ts +9 -8
  161. package/src/internal/hardhat-network/provider/types/HardhatBlockchainInterface.ts +7 -1
  162. package/src/internal/hardhat-network/provider/utils/makeForkClient.ts +2 -1
  163. package/src/internal/hardhat-network/provider/utils/putGenesisBlock.ts +1 -1
  164. package/src/internal/hardhat-network/stack-traces/debug.ts +1 -1
  165. package/src/internal/hardhat-network/stack-traces/error-inferrer.ts +21 -13
  166. package/src/internal/hardhat-network/stack-traces/solidity-errors.ts +2 -2
  167. package/src/internal/hardhat-network/stack-traces/solidity-stack-trace.ts +3 -2
  168. package/src/internal/solidity/compilation-job.ts +2 -1
  169. package/src/internal/solidity/compiler/downloader.ts +5 -3
  170. package/src/internal/util/download.ts +28 -34
  171. package/src/internal/util/glob.ts +1 -0
  172. package/src/internal/util/global-dir.ts +2 -1
  173. package/src/internal/util/keys-derivation.ts +3 -2
  174. package/src/types/config.ts +4 -0
  175. package/types/config.d.ts +4 -0
  176. package/types/config.d.ts.map +1 -1
@@ -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 { BlockchainData } from "./BlockchainData";
6
+ import { BlockchainBase } from "./BlockchainBase";
6
7
  import { FilterParams } from "./node-types";
7
- import { RpcLogOutput, RpcReceiptOutput } from "./output";
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 implements HardhatBlockchainInterface {
13
- private readonly _data = new BlockchainData();
13
+ export class HardhatBlockchain
14
+ extends BlockchainBase
15
+ implements HardhatBlockchainInterface
16
+ {
14
17
  private _length = 0;
15
18
 
16
- public async getLatestBlock(): Promise<Block> {
17
- const block = this._data.getBlockByNumber(new BN(this._length - 1));
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 async getBlock(
25
- blockHashOrNumber: Buffer | BN | number
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 async putBlock(block: Block): Promise<void> {
45
- await this.addBlock(block);
46
- }
47
-
48
- public deleteBlock(blockHash: Buffer) {
49
- const block = this._data.getBlockByHash(blockHash);
50
- if (block === undefined) {
51
- throw new Error("Block not found");
52
- }
53
- this._delBlock(block);
54
- }
55
-
56
- public async delBlock(blockHash: Buffer) {
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
- const nextBlock = this._data.getBlockByNumber(
66
- new BN(actual.header.number).addn(1)
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("Invalid block number");
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
- private _computeTotalDifficulty(block: Block): BN {
145
- const difficulty = new BN(block.header.difficulty);
146
- if (block.header.parentHash.equals(zeros(32))) {
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
  }
@@ -9,7 +9,7 @@ import { RpcTransactionReceipt } from "../../../core/jsonrpc/types/output/receip
9
9
  import { RpcTransaction } from "../../../core/jsonrpc/types/output/transaction";
10
10
  import { InternalError } from "../../../core/providers/errors";
11
11
  import { JsonRpcClient } from "../../jsonrpc/client";
12
- import { BlockchainData } from "../BlockchainData";
12
+ import { BlockchainBase } from "../BlockchainBase";
13
13
  import { FilterParams } from "../node-types";
14
14
  import {
15
15
  remoteReceiptToRpcReceiptOutput,
@@ -29,28 +29,35 @@ import { rpcToTxData } from "./rpcToTxData";
29
29
 
30
30
  /* eslint-disable @nomiclabs/hardhat-internal-rules/only-hardhat-error */
31
31
 
32
- export class ForkBlockchain implements HardhatBlockchainInterface {
33
- private _data = new BlockchainData();
32
+ export class ForkBlockchain
33
+ extends BlockchainBase
34
+ implements HardhatBlockchainInterface
35
+ {
34
36
  private _latestBlockNumber = this._forkBlockNumber;
35
37
 
36
38
  constructor(
37
39
  private _jsonRpcClient: JsonRpcClient,
38
40
  private _forkBlockNumber: BN,
39
- private _common: Common
40
- ) {}
41
+ common: Common
42
+ ) {
43
+ super(common);
44
+ }
41
45
 
42
- public async getLatestBlock(): Promise<Block> {
43
- const block = await this.getBlock(this._latestBlockNumber);
44
- if (block === null) {
45
- throw new Error("Block not found");
46
- }
47
- return block;
46
+ public getLatestBlockNumber(): BN {
47
+ return this._latestBlockNumber;
48
48
  }
49
49
 
50
50
  public async getBlock(
51
51
  blockHashOrNumber: Buffer | number | BN
52
52
  ): Promise<Block | null> {
53
- let block: Block | undefined;
53
+ if (
54
+ (typeof blockHashOrNumber === "number" || BN.isBN(blockHashOrNumber)) &&
55
+ this._data.isReservedBlock(new BN(blockHashOrNumber))
56
+ ) {
57
+ this._data.fulfillBlockReservation(new BN(blockHashOrNumber));
58
+ }
59
+
60
+ let block: Block | undefined | null;
54
61
  if (Buffer.isBuffer(blockHashOrNumber)) {
55
62
  block = await this._getBlockByHash(blockHashOrNumber);
56
63
  return block ?? null;
@@ -63,7 +70,11 @@ export class ForkBlockchain implements HardhatBlockchainInterface {
63
70
  public async addBlock(block: Block): Promise<Block> {
64
71
  const blockNumber = new BN(block.header.number);
65
72
  if (!blockNumber.eq(this._latestBlockNumber.addn(1))) {
66
- throw new Error("Invalid block number");
73
+ throw new Error(
74
+ `Invalid block number ${blockNumber.toNumber()}. Expected ${this._latestBlockNumber
75
+ .addn(1)
76
+ .toNumber()}`
77
+ );
67
78
  }
68
79
 
69
80
  // When forking a network whose consensus is not the classic PoW,
@@ -82,20 +93,19 @@ export class ForkBlockchain implements HardhatBlockchainInterface {
82
93
  return block;
83
94
  }
84
95
 
85
- public async putBlock(block: Block): Promise<void> {
86
- await this.addBlock(block);
87
- }
88
-
89
- public deleteBlock(blockHash: Buffer) {
90
- const block = this._data.getBlockByHash(blockHash);
91
- if (block === undefined) {
92
- throw new Error("Block not found");
93
- }
94
- this._delBlock(block);
95
- }
96
-
97
- public async delBlock(blockHash: Buffer) {
98
- this.deleteBlock(blockHash);
96
+ public reserveBlocks(
97
+ count: BN,
98
+ interval: BN,
99
+ previousBlockStateRoot: Buffer,
100
+ previousBlockTotalDifficulty: BN
101
+ ) {
102
+ super.reserveBlocks(
103
+ count,
104
+ interval,
105
+ previousBlockStateRoot,
106
+ previousBlockTotalDifficulty
107
+ );
108
+ this._latestBlockNumber = this._latestBlockNumber.add(count);
99
109
  }
100
110
 
101
111
  public deleteLaterBlocks(block: Block): void {
@@ -109,10 +119,8 @@ export class ForkBlockchain implements HardhatBlockchainInterface {
109
119
  if (this._forkBlockNumber.gte(nextBlockNumber)) {
110
120
  throw new Error("Cannot delete remote block");
111
121
  }
112
- const nextBlock = this._data.getBlockByNumber(nextBlockNumber);
113
- if (nextBlock !== undefined) {
114
- return this._delBlock(nextBlock);
115
- }
122
+
123
+ this._delBlock(nextBlockNumber);
116
124
  }
117
125
 
118
126
  public async getTotalDifficulty(blockHash: Buffer): Promise<BN> {
@@ -144,12 +152,6 @@ export class ForkBlockchain implements HardhatBlockchainInterface {
144
152
  return tx;
145
153
  }
146
154
 
147
- public getLocalTransaction(
148
- transactionHash: Buffer
149
- ): TypedTransaction | undefined {
150
- return this._data.getTransaction(transactionHash);
151
- }
152
-
153
155
  public async getBlockByTransactionHash(
154
156
  transactionHash: Buffer
155
157
  ): Promise<Block | null> {
@@ -185,12 +187,6 @@ export class ForkBlockchain implements HardhatBlockchainInterface {
185
187
  return null;
186
188
  }
187
189
 
188
- public addTransactionReceipts(receipts: RpcReceiptOutput[]) {
189
- for (const receipt of receipts) {
190
- this._data.addTransactionReceipt(receipt);
191
- }
192
- }
193
-
194
190
  public getForkBlockNumber() {
195
191
  return this._forkBlockNumber;
196
192
  }
@@ -220,18 +216,6 @@ export class ForkBlockchain implements HardhatBlockchainInterface {
220
216
  return this._data.getLogs(filterParams);
221
217
  }
222
218
 
223
- public iterator(
224
- _name: string,
225
- _onBlock: (block: Block, reorg: boolean) => void | Promise<void>
226
- ): Promise<number | void> {
227
- throw new Error("Method not implemented.");
228
- }
229
-
230
- public async getBaseFee(): Promise<BN> {
231
- const latestBlock = await this.getLatestBlock();
232
- return latestBlock.header.calcNextBaseFee();
233
- }
234
-
235
219
  private async _getBlockByHash(blockHash: Buffer) {
236
220
  const block = this._data.getBlockByHash(blockHash);
237
221
  if (block !== undefined) {
@@ -245,8 +229,8 @@ export class ForkBlockchain implements HardhatBlockchainInterface {
245
229
  if (blockNumber.gt(this._latestBlockNumber)) {
246
230
  return undefined;
247
231
  }
248
- const block = this._data.getBlockByNumber(blockNumber);
249
- if (block !== undefined) {
232
+ const block = await super.getBlock(blockNumber);
233
+ if (block !== null) {
250
234
  return block;
251
235
  }
252
236
  const rpcBlock = await this._jsonRpcClient.getBlockByNumber(
@@ -319,41 +303,12 @@ export class ForkBlockchain implements HardhatBlockchainInterface {
319
303
  return block;
320
304
  }
321
305
 
322
- private async _computeTotalDifficulty(block: Block): Promise<BN> {
323
- const difficulty = new BN(block.header.difficulty);
324
- const blockNumber = new BN(block.header.number);
325
- if (blockNumber.eqn(0)) {
326
- return difficulty;
327
- }
328
-
329
- const parentBlock =
330
- this._data.getBlockByNumber(blockNumber.subn(1)) ??
331
- (await this.getBlock(blockNumber.subn(1)));
332
- if (parentBlock === null) {
333
- throw new Error("Block not found");
334
- }
335
- const parentHash = parentBlock.hash();
336
- const parentTD = this._data.getTotalDifficulty(parentHash);
337
- if (parentTD === undefined) {
338
- throw new Error("This should never happen");
339
- }
340
- return parentTD.add(difficulty);
341
- }
342
-
343
- private _delBlock(block: Block): void {
344
- if (new BN(block.header.number).lte(this._forkBlockNumber)) {
306
+ protected _delBlock(blockNumber: BN): void {
307
+ if (blockNumber.lte(this._forkBlockNumber)) {
345
308
  throw new Error("Cannot delete remote block");
346
309
  }
347
-
348
- const blockNumber = block.header.number.toNumber();
349
- for (let i = blockNumber; this._latestBlockNumber.gten(i); i++) {
350
- const current = this._data.getBlockByNumber(new BN(i));
351
- if (current !== undefined) {
352
- this._data.removeBlock(current);
353
- }
354
- }
355
-
356
- this._latestBlockNumber = new BN(blockNumber).subn(1);
310
+ super._delBlock(blockNumber);
311
+ this._latestBlockNumber = blockNumber.subn(1);
357
312
  }
358
313
 
359
314
  private _processRemoteTransaction(rpcTransaction: RpcTransaction | null) {
@@ -325,7 +325,7 @@ export class EthModule {
325
325
  }
326
326
 
327
327
  private async _blockNumberAction(): Promise<string> {
328
- const blockNumber = await this._node.getLatestBlockNumber();
328
+ const blockNumber = this._node.getLatestBlockNumber();
329
329
  return numberToRpcQuantity(blockNumber);
330
330
  }
331
331
 
@@ -1405,7 +1405,7 @@ export class EthModule {
1405
1405
  }
1406
1406
 
1407
1407
  if (block === undefined) {
1408
- const latestBlock = await this._node.getLatestBlockNumber();
1408
+ const latestBlock = this._node.getLatestBlockNumber();
1409
1409
 
1410
1410
  throw new InvalidInputError(
1411
1411
  `Received invalid block tag ${this._newBlockTagToString(
@@ -26,6 +26,7 @@ import {
26
26
  InvalidInputError,
27
27
  MethodNotFoundError,
28
28
  } from "../../../core/providers/errors";
29
+ import { optional } from "../../../util/io-ts";
29
30
  import { MessageTrace } from "../../stack-traces/message-trace";
30
31
  import { HardhatNode } from "../node";
31
32
  import { ForkConfig, MineBlockResult } from "../node-types";
@@ -111,6 +112,9 @@ export class HardhatModule {
111
112
 
112
113
  case "hardhat_setCoinbase":
113
114
  return this._setCoinbaseAction(...this._setCoinbaseParams(params));
115
+
116
+ case "hardhat_mine":
117
+ return this._hardhatMineAction(...this._hardhatMineParams(params));
114
118
  }
115
119
 
116
120
  throw new MethodNotFoundError(`Method ${method} not found`);
@@ -173,14 +177,14 @@ export class HardhatModule {
173
177
 
174
178
  const isEmpty = result.block.transactions.length === 0;
175
179
  if (isEmpty) {
176
- this._logger.printMinedBlockNumber(
180
+ this._logger.printIntervalMinedBlockNumber(
177
181
  blockNumber,
178
182
  isEmpty,
179
183
  result.block.header.baseFeePerGas
180
184
  );
181
185
  } else {
182
- await this._logBlock(result);
183
- this._logger.printMinedBlockNumber(blockNumber, isEmpty);
186
+ await this._logBlock(result, { isIntervalMined: true });
187
+ this._logger.printIntervalMinedBlockNumber(blockNumber, isEmpty);
184
188
  const printedSomething = this._logger.printLogs();
185
189
  if (printedSomething) {
186
190
  this._logger.printEmptyLine();
@@ -360,7 +364,31 @@ export class HardhatModule {
360
364
  return true;
361
365
  }
362
366
 
363
- private async _logBlock(result: MineBlockResult) {
367
+ // hardhat_mine
368
+ private async _hardhatMineAction(blockCount?: BN, interval?: BN) {
369
+ const mineBlockResults = await this._node.mineBlocks(blockCount, interval);
370
+
371
+ for (const [i, result] of mineBlockResults.entries()) {
372
+ await this._logHardhatMinedBlock(result);
373
+
374
+ // print an empty line after logging blocks with txs,
375
+ // unless it's the last logged block
376
+ const isEmpty = result.block.transactions.length === 0;
377
+ if (!isEmpty && i + 1 < mineBlockResults.length) {
378
+ this._logger.logEmptyLine();
379
+ }
380
+ }
381
+
382
+ return true;
383
+ }
384
+ private _hardhatMineParams(params: any[]): [BN | undefined, BN | undefined] {
385
+ return validateParams(params, optional(rpcQuantity), optional(rpcQuantity));
386
+ }
387
+
388
+ private async _logBlock(
389
+ result: MineBlockResult,
390
+ { isIntervalMined }: { isIntervalMined: boolean }
391
+ ) {
364
392
  const { block, traces } = result;
365
393
 
366
394
  const codes: Buffer[] = [];
@@ -373,7 +401,11 @@ export class HardhatModule {
373
401
  codes.push(code);
374
402
  }
375
403
 
376
- this._logger.logIntervalMinedBlock(result, codes);
404
+ if (isIntervalMined) {
405
+ this._logger.logIntervalMinedBlock(result, codes);
406
+ } else {
407
+ this._logger.logMinedBlock(result, codes);
408
+ }
377
409
 
378
410
  for (const txTrace of traces) {
379
411
  await this._runHardhatNetworkMessageTraceHooks(txTrace.trace, false);
@@ -392,4 +424,18 @@ export class HardhatModule {
392
424
  await hook(trace, isCall);
393
425
  }
394
426
  }
427
+
428
+ private async _logHardhatMinedBlock(result: MineBlockResult) {
429
+ const isEmpty = result.block.transactions.length === 0;
430
+ const blockNumber = result.block.header.number.toNumber();
431
+
432
+ if (isEmpty) {
433
+ this._logger.logEmptyHardhatMinedBlock(
434
+ blockNumber,
435
+ result.block.header.baseFeePerGas
436
+ );
437
+ } else {
438
+ await this._logBlock(result, { isIntervalMined: false });
439
+ }
440
+ }
395
441
  }