Merge pull request #3378 from ethereum/add_vm_fork_2

Improve VM forking
pull/5370/head
yann300 2 years ago committed by GitHub
commit 48e330200f
  1. 2
      apps/remix-ide-e2e/src/tests/runAndDeploy.test.ts
  2. 29
      apps/remix-ide-e2e/src/tests/terminal.test.ts
  3. 10
      apps/remix-ide/src/app.js
  4. 2
      apps/remix-ide/src/app/providers/abstract-provider.tsx
  5. 64
      apps/remix-ide/src/app/providers/custom-vm-fork-provider.tsx
  6. 29
      apps/remix-ide/src/app/providers/goerli-vm-fork-provider.tsx
  7. 29
      apps/remix-ide/src/app/providers/sepolia-vm-fork-provider.tsx
  8. 25
      apps/remix-ide/src/app/providers/vm-provider.tsx
  9. 7
      apps/remix-ide/src/app/udapp/run-tab.js
  10. 6
      apps/remix-ide/src/blockchain/execution-context.js
  11. 7
      libs/remix-simulator/src/genesis.ts
  12. 4
      libs/remix-simulator/src/vm-context.ts
  13. 11
      libs/remix-ui/run-tab/src/lib/components/account.tsx
  14. 4
      libs/remix-ui/run-tab/src/lib/reducers/runTab.ts

@ -32,7 +32,7 @@ module.exports = {
'Should sign message using account key #group2': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="settingsRemixRunSignMsg"]')
.switchEnvironment('vm-berlin')
.switchEnvironment('vm-merge')
.pause(2000)
.click('*[data-id="settingsRemixRunSignMsg"]')
.pause(2000)

File diff suppressed because one or more lines are too long

@ -27,8 +27,10 @@ import { StoragePlugin } from './app/plugins/storage'
import { Layout } from './app/panels/layout'
import { NotificationPlugin } from './app/plugins/notification'
import { Blockchain } from './blockchain/blockchain.js'
import { BerlinVMProvider, LondonVMProvider } from './app/providers/vm-provider'
import { MergeVMProvider, LondonVMProvider, BerlinVMProvider} from './app/providers/vm-provider'
import { MainnetForkVMProvider } from './app/providers/mainnet-vm-fork-provider'
import { SepoliaForkVMProvider } from './app/providers/sepolia-vm-fork-provider'
import { GoerliForkVMProvider } from './app/providers/goerli-vm-fork-provider'
import { CustomForkVMProvider } from './app/providers/custom-vm-fork-provider'
import { HardhatProvider } from './app/providers/hardhat-provider'
import { GanacheProvider } from './app/providers/ganache-provider'
@ -207,6 +209,9 @@ class AppComponent {
const web3Provider = new Web3ProviderModule(blockchain)
const vmProviderCustomFork = new CustomForkVMProvider(blockchain)
const vmProviderMainnetFork = new MainnetForkVMProvider(blockchain)
const vmProviderSepoliaFork = new SepoliaForkVMProvider(blockchain)
const vmProviderGoerliFork = new GoerliForkVMProvider(blockchain)
const vmProviderMerge = new MergeVMProvider(blockchain)
const vmProviderBerlin = new BerlinVMProvider(blockchain)
const vmProviderLondon = new LondonVMProvider(blockchain)
const hardhatProvider = new HardhatProvider(blockchain)
@ -275,8 +280,11 @@ class AppComponent {
fetchAndCompile,
dGitProvider,
storagePlugin,
vmProviderMerge,
vmProviderBerlin,
vmProviderLondon,
vmProviderSepoliaFork,
vmProviderGoerliFork,
vmProviderMainnetFork,
vmProviderCustomFork,
hardhatProvider,

@ -113,7 +113,7 @@ export abstract class AbstractProvider extends Plugin implements IProvider {
}
this.call('notification', 'alert', modalContent)
}
await this.call('udapp', 'setEnvironmentMode', { context: 'vm-london'})
await this.call('udapp', 'setEnvironmentMode', { context: 'vm-merge'})
return
}

@ -2,6 +2,7 @@ import React, { useRef } from 'react' // eslint-disable-line
import * as packageJson from '../../../../../package.json'
import { AppModal, ModalTypes } from '@remix-ui/app'
import { BasicVMProvider } from './vm-provider'
import { Hardfork } from '@ethereumjs/common'
export class CustomForkVMProvider extends BasicVMProvider {
nodeUrl: string
@ -11,9 +12,9 @@ export class CustomForkVMProvider extends BasicVMProvider {
constructor (blockchain) {
super({
name: 'vm-custom-fork',
displayName: 'Custom fork - Remix VM (London)',
displayName: 'Custom fork - Remix VM',
kind: 'provider',
description: 'Remix VM (London)',
description: 'Custom fork - Remix VM',
methods: ['sendAsync', 'init'],
version: packageJson.version
}, blockchain)
@ -25,33 +26,52 @@ export class CustomForkVMProvider extends BasicVMProvider {
}
async init () {
this.inputs = {nodeUrl: '', evm: '', blockNumber: '' }
const body = () => {
return <div>
<span>Please provide information about the custom fork. If the node URL is not provided, the VM will start with an empty state.</span>
<div>
<label>Node URL</label>
<input type="text" value={this.inputs.nodeUrl} ></input>
<label className="mt-3 mb-1">Node URL</label>
<input data-id="CustomForkNodeUrl" name="nodeUrl" type="text" className="border form-control border-right-0" />
</div>
<div>
<label>Block number (or "latest")</label>
<input type="text" placeholder='block number or "latest"' value={this.inputs.blockNumber} ></input>
<label className="mt-3 mb-1">Block number (or "latest")</label>
<input data-id="CustomForkBlockNumber" name="blockNumber" type="text" defaultValue="latest" placeholder='block number or "latest"' className="border form-control border-right-0" />
</div>
<div>
<label>EVM</label>
<select value={this.inputs.evm}>
<option value="berlin" key="berlin">Berlin</option>
<option value="london" key="london" >London</option>
</select>
<label className="mt-3 mb-1">EVM</label>
<select data-id="CustomForkEvmType" name="evmType" className="border form-control border-right-0">
{Object.keys(Hardfork).map((value, index) => {
return <option value={Hardfork[value]} key={index}>{value}</option>
})}
</select>
</div>
</div>
}
await ((): Promise<string> => {
const result = await ((): Promise<any> => {
return new Promise((resolve, reject) => {
const modalContent: AppModal = {
id: this.profile.name,
title: this.profile.displayName,
message: body(),
modalType: ModalTypes.default,
validationFn: (data: any) => {
if(data.nodeUrl !== '' && !data.nodeUrl.startsWith("http")) {
return {
valid: false,
message: 'node URL should be a valid URL'
}
}
if (data.blockNumber !== 'latest' && isNaN(data.blockNumber)) {
return {
valid: false,
message: 'blockNumber should be a number or "latest"'
}
}
return {
valid: true,
message: ''
}
},
modalType: ModalTypes.form,
okLabel: 'Connect',
cancelLabel: 'Cancel',
okFn: (value: string) => {
@ -64,13 +84,19 @@ export class CustomForkVMProvider extends BasicVMProvider {
setTimeout(() => reject(new Error('Hide')), 0)
}
}
this.call('notification', 'modal', modalContent)
return this.call('notification', 'modal', modalContent)
})
})()
this.fork = this.inputs.evm
this.nodeUrl = this.inputs.nodeUrl
const block = this.inputs.blockNumber
this.blockNumber = block === 'latest' ? 'latest' : parseInt(block)
this.fork = result.evmType
this.nodeUrl = result.nodeUrl
if (this.nodeUrl) {
const block = result.blockNumber
this.blockNumber = block === 'latest' ? 'latest' : parseInt(block)
} else {
this.nodeUrl = undefined
this.blockNumber = undefined
}
return {
'fork': this.fork,
'nodeUrl': this.nodeUrl,

@ -0,0 +1,29 @@
import * as packageJson from '../../../../../package.json'
import { BasicVMProvider } from './vm-provider'
export class GoerliForkVMProvider extends BasicVMProvider {
nodeUrl: string
blockNumber: number | 'latest'
constructor (blockchain) {
super({
name: 'vm-goerli-fork',
displayName: 'Goerli fork - Remix VM (London)',
kind: 'provider',
description: 'Remix VM (London)',
methods: ['sendAsync', 'init'],
version: packageJson.version
}, blockchain)
this.blockchain = blockchain
this.fork = 'merge'
this.nodeUrl = 'https://remix-sepolia.ethdevops.io'
this.blockNumber = 'latest'
}
async init () {
return {
'fork': this.fork,
'nodeUrl': this.nodeUrl,
'blockNumber': this.blockNumber
}
}
}

@ -0,0 +1,29 @@
import * as packageJson from '../../../../../package.json'
import { BasicVMProvider } from './vm-provider'
export class SepoliaForkVMProvider extends BasicVMProvider {
nodeUrl: string
blockNumber: number | 'latest'
constructor (blockchain) {
super({
name: 'vm-sepolia-fork',
displayName: 'Sepolia fork - Remix VM (London)',
kind: 'provider',
description: 'Remix VM (London)',
methods: ['sendAsync', 'init'],
version: packageJson.version
}, blockchain)
this.blockchain = blockchain
this.fork = 'merge'
this.nodeUrl = 'https://remix-sepolia.ethdevops.io'
this.blockNumber = 'latest'
}
async init () {
return {
'fork': this.fork,
'nodeUrl': this.nodeUrl,
'blockNumber': this.blockNumber
}
}
}

@ -42,18 +42,18 @@ export class BasicVMProvider extends Plugin implements IProvider {
}
}
export class BerlinVMProvider extends BasicVMProvider {
export class MergeVMProvider extends BasicVMProvider {
constructor (blockchain) {
super({
name: 'vm-berlin',
displayName: 'Remix VM (Berlin)',
name: 'vm-merge',
displayName: 'Remix VM (Merge)',
kind: 'provider',
description: 'Remix VM (Berlin)',
description: 'Remix VM (Merge)',
methods: ['sendAsync', 'init'],
version: packageJson.version
}, blockchain)
this.blockchain = blockchain
this.fork = 'berlin'
this.fork = 'merge'
}
}
@ -70,4 +70,19 @@ export class LondonVMProvider extends BasicVMProvider {
this.blockchain = blockchain
this.fork = 'london'
}
}
export class BerlinVMProvider extends BasicVMProvider {
constructor (blockchain) {
super({
name: 'vm-berlin',
displayName: 'Remix VM (Berlin)',
kind: 'provider',
description: 'Remix VM (Berlin)',
methods: ['sendAsync', 'init'],
version: packageJson.version
}, blockchain)
this.blockchain = blockchain
this.fork = 'berlin'
}
}

@ -133,10 +133,13 @@ export class RunTab extends ViewPlugin {
// VM
const titleVM = 'Execution environment is local to Remix. Data is only saved to browser memory and will vanish upon reload.'
await addProvider('vm-merge', 'Remix VM (Merge)', false, true, 'merge', 'settingsVMMergeMode', titleVM)
await addProvider('vm-london', 'Remix VM (London)', false, true, 'london', 'settingsVMLondonMode', titleVM)
await addProvider('vm-berlin', 'Remix VM (Berlin)', false, true, 'berlin', 'settingsVMBerlinMode', titleVM)
await addProvider('vm-mainnet-fork', 'Remix VM - Mainnet fork', false, true, 'london', 'settingsVMMainnetMode', titleVM)
// await addProvider('vm-custom-fork', 'Remix VM - Custom fork', false, true, '', 'settingsVMCustomMode', titleVM)
await addProvider('vm-mainnet-fork', 'Remix VM - Mainnet fork', false, true, 'merge', 'settingsVMMainnetMode', titleVM)
await addProvider('vm-sepolia-fork', 'Remix VM - Sepolia fork', false, true, 'merge', 'settingsVMSepoliaMode', titleVM)
await addProvider('vm-goerli-fork', 'Remix VM - Goerli fork', false, true, 'merge', 'settingsVMGoerliMode', titleVM)
await addProvider('vm-custom-fork', 'Remix VM - Custom fork', false, true, '', 'settingsVMCustomMode', titleVM)
// external provider
await addProvider('hardhat-provider', 'Hardhat Provider', false, false)

@ -20,7 +20,7 @@ if (typeof window !== 'undefined' && typeof window.ethereum !== 'undefined') {
export class ExecutionContext {
constructor () {
this.event = new EventManager()
this.executionContext = 'vm-london'
this.executionContext = 'vm-merge'
this.lastBlock = null
this.blockGasLimitDefault = 4300000
this.blockGasLimit = this.blockGasLimitDefault
@ -35,7 +35,7 @@ export class ExecutionContext {
init (config) {
if (config.get('settings/always-use-vm')) {
this.executionContext = 'vm-london'
this.executionContext = 'vm-merge'
}
}
@ -102,7 +102,7 @@ export class ExecutionContext {
removeProvider (name) {
if (name && this.customNetWorks[name]) {
if (this.executionContext === name) this.setContext('vm-london', null, null, null)
if (this.executionContext === name) this.setContext('vm-merge', null, null, null)
delete this.customNetWorks[name]
this.event.trigger('removeProvider', [name])
}

@ -1,13 +1,18 @@
import { Block } from '@ethereumjs/block'
import { ConsensusType } from '@ethereumjs/common'
export function generateBlock (vmContext) {
const common = vmContext.vmObject().common
const difficulty = common.consensusType() === ConsensusType.ProofOfStake ? 0 : 69762765929000
return new Promise((resolve, reject) => {
const block: Block = Block.fromBlockData({
header: {
timestamp: (new Date().getTime() / 1000 | 0),
number: 0,
coinbase: '0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a',
difficulty: 69762765929000,
difficulty,
gasLimit: 8000000
}
}, { common: vmContext.vmObject().common })

@ -168,13 +168,13 @@ export class VMContext {
async createVm (hardfork) {
let stateManager: StateManager
console.log('creating a new VM', hardfork, this.nodeUrl, this.blockNumber)
if (this.nodeUrl) {
let block = this.blockNumber
if (this.blockNumber === 'latest') {
const provider = new ethers.providers.StaticJsonRpcProvider(this.nodeUrl)
block = await provider.getBlockNumber()
}
}
stateManager = new CustomEthersStateManager({
provider: this.nodeUrl,
blockTag: BigInt(block)

@ -30,14 +30,7 @@ export function AccountUI (props: AccountProps) {
})
break
case 'vm-london':
setPlusOpt({
classList: '',
title: 'Create a new account'
})
break
case 'vm-berlin':
case 'vm-merge':
setPlusOpt({
classList: '',
title: 'Create a new account'
@ -76,7 +69,7 @@ export function AccountUI (props: AccountProps) {
return props.tooltip('Account list is empty, please make sure the current provider is properly connected to remix')
}
if (props.selectExEnv !== 'vm-london' && props.selectExEnv !== 'vm-berlin' && props.selectExEnv !== 'injected') {
if (props.selectExEnv !== 'vm-merge' && props.selectExEnv !== 'injected') {
return props.modal('Passphrase to sign a message',
<PassphrasePrompt
message='Enter your passphrase for this account to sign the message'

@ -115,7 +115,7 @@ export const runTabInitialState: RunTabState = {
sendValue: '0',
sendUnit: 'wei',
gasLimit: 3000000,
selectExEnv: 'vm-london',
selectExEnv: 'vm-merge',
personalMode: false,
networkName: 'VM',
providers: {
@ -263,7 +263,7 @@ export const runTabReducer = (state: RunTabState = runTabInitialState, action: A
return {
...state,
selectExEnv: payload,
networkName: state.selectExEnv === 'vm-london' || state.selectExEnv === 'vm-berlin' ? 'VM' : state.networkName,
networkName: state.selectExEnv === 'vm-merge' ? 'VM' : state.networkName,
accounts: {
...state.accounts,
selectedAccount: '',

Loading…
Cancel
Save