Skip to content

Commit

Permalink
Add _node property to provider
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
fvictorio committed Feb 20, 2024
1 parent b887650 commit 4b1d60f
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 155 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,16 @@ import {
} from "./node-types";
import {
edrRpcDebugTraceToHardhat,
edrTracingMessageResultToMinimalEVMResult,
edrTracingMessageToMinimalMessage,
edrTracingStepToMinimalInterpreterStep,
ethereumjsIntervalMiningConfigToEdr,
ethereumjsMempoolOrderToEdrMineOrdering,
ethereumsjsHardforkToEdrSpecId,
} from "./utils/convertToEdr";
import { makeCommon } from "./utils/makeCommon";
import { LoggerConfig, printLine, replaceLastLine } from "./modules/logger";
import { MinimalEthereumJsVm, getMinimalEthereumJsVm } from "./vm/minimal-vm";

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

Expand Down Expand Up @@ -159,6 +163,10 @@ export class EdrProviderWrapper

private constructor(
private readonly _provider: EdrProviderT,
// we add this for backwards-compatibility with plugins like solidity-coverage
private readonly _node: {
_vm: MinimalEthereumJsVm;
},
private readonly _eventAdapter: EdrProviderEventAdapter,
private readonly _vmTraceDecoder: VmTraceDecoder,
private readonly _rawTraceCallbacks: RawTraceCallbacks,
Expand Down Expand Up @@ -291,9 +299,14 @@ export class EdrProviderWrapper
}
);

const minimalEthereumJsNode = {
_vm: getMinimalEthereumJsVm(provider),
};

const common = makeCommon(getNodeConfig(config));
const wrapper = new EdrProviderWrapper(
provider,
minimalEthereumJsNode,
eventAdapter,
vmTraceDecoder,
rawTraceCallbacks,
Expand Down Expand Up @@ -344,16 +357,28 @@ export class EdrProviderWrapper
const trace = rawTrace.trace();
for (const traceItem of trace) {
if ("pc" in traceItem) {
this._node._vm.evm.events.emit(
"step",
edrTracingStepToMinimalInterpreterStep(traceItem)
);
if (this._rawTraceCallbacks.onStep !== undefined) {
await this._rawTraceCallbacks.onStep(traceItem);
}
} else if ("executionResult" in traceItem) {
this._node._vm.evm.events.emit(
"afterMessage",
edrTracingMessageResultToMinimalEVMResult(traceItem)
);
if (this._rawTraceCallbacks.onAfterMessage !== undefined) {
await this._rawTraceCallbacks.onAfterMessage(
traceItem.executionResult
);
}
} else {
this._node._vm.evm.events.emit(
"beforeMessage",
edrTracingMessageToMinimalMessage(traceItem)
);
if (this._rawTraceCallbacks.onBeforeMessage !== undefined) {
await this._rawTraceCallbacks.onBeforeMessage(traceItem);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,20 @@ import {
MineOrdering,
IntervalRange,
DebugTraceResult,
TracingMessage,
TracingMessageResult,
TracingStep,
} from "@ignored/edr";
import { Address } from "@nomicfoundation/ethereumjs-util";

import { HardforkName } from "../../../util/hardforks";
import { IntervalMiningConfig, MempoolOrder } from "../node-types";
import { RpcDebugTraceOutput, RpcStructLog } from "../output";
import {
MinimalEVMResult,
MinimalInterpreterStep,
MinimalMessage,
} from "../vm/types";

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

Expand Down Expand Up @@ -181,3 +191,38 @@ export function edrRpcDebugTraceToHardhat(
structLogs,
};
}

export function edrTracingStepToMinimalInterpreterStep(
step: TracingStep
): MinimalInterpreterStep {
return {
pc: Number(step.pc),
depth: step.depth,
opcode: {
name: step.opcode,
},
stack: step.stackTop !== undefined ? [step.stackTop] : [],
};
}

export function edrTracingMessageResultToMinimalEVMResult(
tracingMessageResult: TracingMessageResult
): MinimalEVMResult {
return {
execResult: {
executionGasUsed: tracingMessageResult.executionResult.result.gasUsed,
},
};
}

export function edrTracingMessageToMinimalMessage(
message: TracingMessage
): MinimalMessage {
return {
to: message.to !== undefined ? new Address(message.to) : undefined,
data: message.data,
value: message.value,
caller: new Address(message.caller),
gasLimit: message.gasLimit,
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import type { Provider as EdrProviderT } from "@ignored/edr";
import type { Address } from "@nomicfoundation/ethereumjs-util";
import type {
MinimalEVMResult,
MinimalInterpreterStep,
MinimalMessage,
} from "./types";

import { AsyncEventEmitter } from "@nomicfoundation/ethereumjs-util";

/**
* Used by the provider to keep the `_vm` variable used by some plugins. This
* interface only has the things used by those plugins.
*/
export interface MinimalEthereumJsVm {
evm: {
events: AsyncEventEmitter<MinimalEthereumJsVmEvents>;
};
stateManager: {
putContractCode: (address: Address, code: Buffer) => Promise<void>;
getContractStorage: (address: Address, slotHash: Buffer) => Promise<Buffer>;
putContractStorage: (
address: Address,
slotHash: Buffer,
slotValue: Buffer
) => Promise<void>;
};
}

// we need to use a type instead of an interface to satisfy the type constarint
// of the AsyncEventEmitter type param
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
type MinimalEthereumJsVmEvents = {
beforeMessage: (
data: MinimalMessage,
resolve?: (result?: any) => void
) => void;
afterMessage: (
data: MinimalEVMResult,
resolve?: (result?: any) => void
) => void;
step: (
data: MinimalInterpreterStep,
resolve?: (result?: any) => void
) => void;
};

export class MinimalEthereumJsVmEventEmitter extends AsyncEventEmitter<MinimalEthereumJsVmEvents> {}

export function getMinimalEthereumJsVm(
provider: EdrProviderT
): MinimalEthereumJsVm {
const minimalEthereumJsVm: MinimalEthereumJsVm = {
evm: {
events: new MinimalEthereumJsVmEventEmitter(),
},
stateManager: {
putContractCode: async (address: Address, code: Buffer) => {
await provider.handleRequest(
JSON.stringify({
method: "hardhat_setCode",
params: [`0x${address.toString()}`, `0x${code.toString("hex")}`],
})
);
},
getContractStorage: async (address: Address, slotHash: Buffer) => {
const responseObject = await provider.handleRequest(
JSON.stringify({
method: "eth_getStorageAt",
params: [
`0x${address.toString()}`,
`0x${slotHash.toString("hex")}`,
],
})
);

const response = JSON.parse(responseObject.json);

return Buffer.from(response.result.slice(2), "hex");
},
putContractStorage: async (
address: Address,
slotHash: Buffer,
slotValue: Buffer
) => {
await provider.handleRequest(
JSON.stringify({
method: "hardhat_setStorageAt",
params: [
`0x${address.toString()}`,
`0x${slotHash.toString("hex")}`,
`0x${slotValue.toString("hex")}`,
],
})
);
},
},
};

return minimalEthereumJsVm;
}

This file was deleted.

Loading

0 comments on commit 4b1d60f

Please sign in to comment.