Skip to content

Commit a5d58a1

Browse files
authored
feat: add compatibility for community modules using EthereumJS' VM (#4894)
Add a _node property to the provider, which in turn has a _vm property with an object that works as a partial implementation of ethereumjs vm we used to have. More specifically: it supports evm events and some statemanager methods. We are doing this to prevent plugins like solidity-coverage from breaking with the EDR refactor.
1 parent 8a1a2b6 commit a5d58a1

File tree

5 files changed

+190
-155
lines changed

5 files changed

+190
-155
lines changed

packages/hardhat-core/src/internal/hardhat-network/provider/provider.ts

+25
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,16 @@ import {
6767
} from "./node-types";
6868
import {
6969
edrRpcDebugTraceToHardhat,
70+
edrTracingMessageResultToMinimalEVMResult,
71+
edrTracingMessageToMinimalMessage,
72+
edrTracingStepToMinimalInterpreterStep,
7073
ethereumjsIntervalMiningConfigToEdr,
7174
ethereumjsMempoolOrderToEdrMineOrdering,
7275
ethereumsjsHardforkToEdrSpecId,
7376
} from "./utils/convertToEdr";
7477
import { makeCommon } from "./utils/makeCommon";
7578
import { LoggerConfig, printLine, replaceLastLine } from "./modules/logger";
79+
import { MinimalEthereumJsVm, getMinimalEthereumJsVm } from "./vm/minimal-vm";
7680

7781
const log = debug("hardhat:core:hardhat-network:provider");
7882

@@ -159,6 +163,10 @@ export class EdrProviderWrapper
159163

160164
private constructor(
161165
private readonly _provider: EdrProviderT,
166+
// we add this for backwards-compatibility with plugins like solidity-coverage
167+
private readonly _node: {
168+
_vm: MinimalEthereumJsVm;
169+
},
162170
private readonly _eventAdapter: EdrProviderEventAdapter,
163171
private readonly _vmTraceDecoder: VmTraceDecoder,
164172
private readonly _rawTraceCallbacks: RawTraceCallbacks,
@@ -291,9 +299,14 @@ export class EdrProviderWrapper
291299
}
292300
);
293301

302+
const minimalEthereumJsNode = {
303+
_vm: getMinimalEthereumJsVm(provider),
304+
};
305+
294306
const common = makeCommon(getNodeConfig(config));
295307
const wrapper = new EdrProviderWrapper(
296308
provider,
309+
minimalEthereumJsNode,
297310
eventAdapter,
298311
vmTraceDecoder,
299312
rawTraceCallbacks,
@@ -344,16 +357,28 @@ export class EdrProviderWrapper
344357
const trace = rawTrace.trace();
345358
for (const traceItem of trace) {
346359
if ("pc" in traceItem) {
360+
this._node._vm.evm.events.emit(
361+
"step",
362+
edrTracingStepToMinimalInterpreterStep(traceItem)
363+
);
347364
if (this._rawTraceCallbacks.onStep !== undefined) {
348365
await this._rawTraceCallbacks.onStep(traceItem);
349366
}
350367
} else if ("executionResult" in traceItem) {
368+
this._node._vm.evm.events.emit(
369+
"afterMessage",
370+
edrTracingMessageResultToMinimalEVMResult(traceItem)
371+
);
351372
if (this._rawTraceCallbacks.onAfterMessage !== undefined) {
352373
await this._rawTraceCallbacks.onAfterMessage(
353374
traceItem.executionResult
354375
);
355376
}
356377
} else {
378+
this._node._vm.evm.events.emit(
379+
"beforeMessage",
380+
edrTracingMessageToMinimalMessage(traceItem)
381+
);
357382
if (this._rawTraceCallbacks.onBeforeMessage !== undefined) {
358383
await this._rawTraceCallbacks.onBeforeMessage(traceItem);
359384
}

packages/hardhat-core/src/internal/hardhat-network/provider/utils/convertToEdr.ts

+45
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,20 @@ import {
33
MineOrdering,
44
IntervalRange,
55
DebugTraceResult,
6+
TracingMessage,
7+
TracingMessageResult,
8+
TracingStep,
69
} from "@ignored/edr";
10+
import { Address } from "@nomicfoundation/ethereumjs-util";
11+
712
import { HardforkName } from "../../../util/hardforks";
813
import { IntervalMiningConfig, MempoolOrder } from "../node-types";
914
import { RpcDebugTraceOutput, RpcStructLog } from "../output";
15+
import {
16+
MinimalEVMResult,
17+
MinimalInterpreterStep,
18+
MinimalMessage,
19+
} from "../vm/types";
1020

1121
/* eslint-disable @nomicfoundation/hardhat-internal-rules/only-hardhat-error */
1222

@@ -181,3 +191,38 @@ export function edrRpcDebugTraceToHardhat(
181191
structLogs,
182192
};
183193
}
194+
195+
export function edrTracingStepToMinimalInterpreterStep(
196+
step: TracingStep
197+
): MinimalInterpreterStep {
198+
return {
199+
pc: Number(step.pc),
200+
depth: step.depth,
201+
opcode: {
202+
name: step.opcode,
203+
},
204+
stack: step.stackTop !== undefined ? [step.stackTop] : [],
205+
};
206+
}
207+
208+
export function edrTracingMessageResultToMinimalEVMResult(
209+
tracingMessageResult: TracingMessageResult
210+
): MinimalEVMResult {
211+
return {
212+
execResult: {
213+
executionGasUsed: tracingMessageResult.executionResult.result.gasUsed,
214+
},
215+
};
216+
}
217+
218+
export function edrTracingMessageToMinimalMessage(
219+
message: TracingMessage
220+
): MinimalMessage {
221+
return {
222+
to: message.to !== undefined ? new Address(message.to) : undefined,
223+
data: message.data,
224+
value: message.value,
225+
caller: new Address(message.caller),
226+
gasLimit: message.gasLimit,
227+
};
228+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import type { Provider as EdrProviderT } from "@ignored/edr";
2+
import type { Address } from "@nomicfoundation/ethereumjs-util";
3+
import type {
4+
MinimalEVMResult,
5+
MinimalInterpreterStep,
6+
MinimalMessage,
7+
} from "./types";
8+
9+
import { AsyncEventEmitter } from "@nomicfoundation/ethereumjs-util";
10+
11+
/**
12+
* Used by the provider to keep the `_vm` variable used by some plugins. This
13+
* interface only has the things used by those plugins.
14+
*/
15+
export interface MinimalEthereumJsVm {
16+
evm: {
17+
events: AsyncEventEmitter<MinimalEthereumJsVmEvents>;
18+
};
19+
stateManager: {
20+
putContractCode: (address: Address, code: Buffer) => Promise<void>;
21+
getContractStorage: (address: Address, slotHash: Buffer) => Promise<Buffer>;
22+
putContractStorage: (
23+
address: Address,
24+
slotHash: Buffer,
25+
slotValue: Buffer
26+
) => Promise<void>;
27+
};
28+
}
29+
30+
// we need to use a type instead of an interface to satisfy the type constarint
31+
// of the AsyncEventEmitter type param
32+
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
33+
type MinimalEthereumJsVmEvents = {
34+
beforeMessage: (
35+
data: MinimalMessage,
36+
resolve?: (result?: any) => void
37+
) => void;
38+
afterMessage: (
39+
data: MinimalEVMResult,
40+
resolve?: (result?: any) => void
41+
) => void;
42+
step: (
43+
data: MinimalInterpreterStep,
44+
resolve?: (result?: any) => void
45+
) => void;
46+
};
47+
48+
export class MinimalEthereumJsVmEventEmitter extends AsyncEventEmitter<MinimalEthereumJsVmEvents> {}
49+
50+
export function getMinimalEthereumJsVm(
51+
provider: EdrProviderT
52+
): MinimalEthereumJsVm {
53+
const minimalEthereumJsVm: MinimalEthereumJsVm = {
54+
evm: {
55+
events: new MinimalEthereumJsVmEventEmitter(),
56+
},
57+
stateManager: {
58+
putContractCode: async (address: Address, code: Buffer) => {
59+
await provider.handleRequest(
60+
JSON.stringify({
61+
method: "hardhat_setCode",
62+
params: [`0x${address.toString()}`, `0x${code.toString("hex")}`],
63+
})
64+
);
65+
},
66+
getContractStorage: async (address: Address, slotHash: Buffer) => {
67+
const responseObject = await provider.handleRequest(
68+
JSON.stringify({
69+
method: "eth_getStorageAt",
70+
params: [
71+
`0x${address.toString()}`,
72+
`0x${slotHash.toString("hex")}`,
73+
],
74+
})
75+
);
76+
77+
const response = JSON.parse(responseObject.json);
78+
79+
return Buffer.from(response.result.slice(2), "hex");
80+
},
81+
putContractStorage: async (
82+
address: Address,
83+
slotHash: Buffer,
84+
slotValue: Buffer
85+
) => {
86+
await provider.handleRequest(
87+
JSON.stringify({
88+
method: "hardhat_setStorageAt",
89+
params: [
90+
`0x${address.toString()}`,
91+
`0x${slotHash.toString("hex")}`,
92+
`0x${slotValue.toString("hex")}`,
93+
],
94+
})
95+
);
96+
},
97+
},
98+
};
99+
100+
return minimalEthereumJsVm;
101+
}

packages/hardhat-core/src/internal/hardhat-network/provider/vm/proxy-vm.ts

-123
This file was deleted.

0 commit comments

Comments
 (0)