diff --git a/packages/evm/DEVELOPER.md b/packages/evm/DEVELOPER.md deleted file mode 100644 index fa8872411f..0000000000 --- a/packages/evm/DEVELOPER.md +++ /dev/null @@ -1,210 +0,0 @@ -# Developer Documentation - -## TESTING - -### Running Tests - -Tests can be found in the `tests` directory. There are test runners for [State tests](http://www.ethdocs.org/en/latest/contracts-and-transactions/ethereum-tests/state_tests/index.html) and [Blockchain tests](http://www.ethdocs.org/en/latest/contracts-and-transactions/ethereum-tests/blockchain_tests/index.html). VM tests are disabled since Frontier gas costs are not supported any more. - -Tests are then executed against a snapshot of the official client-independent [Ethereum tests](https://github.com/ethereum/tests) integrated in the monorepo as a submodule in [packages/ethereum-tests](./../ethereum-tests/) pointing towards a specific commit or tag from the `ethereum/tests` `develop` branch. - -For a wider picture about how to use tests to implement EIPs you can have a look at this [Reddit post](https://www.reddit.com/r/ethereum/comments/6kc5g3/ethereumjs_team_is_seeking_contributors/) -or the associated YouTube video introduction to [Core Development with Ethereumjs-vm](https://www.youtube.com/watch?v=L0BVDl6HZzk). - -#### Running different Test Types - -Running the State tests: - -`ts-node ./tests/tester --state` - -Running the Blockchain tests: - -`ts-node ./tests/tester --blockchain` - -Tests run against source by default. They can be run with the `--dist` flag: - -`npm run build:dist && node ./tests/tester --state --dist` - -See `package.json` for all the scripts in the `test:` namespace, such as `npm run test:state` which would execute the above. - -Use `--fork` to pass in the desired hardfork: - -`ts-node ./tests/tester --state --fork='Constantinople'` - -or - -`npm run test:state -- --fork='Constantinople'` - -By default it is set to use the latest hardfork (`FORK_CONFIG` in `tests/tester.js`). - -The `--fork` parameter can also be used to activate EIPs. This is done by first entering the hardfork, and then add the EIPs seperated with the `+` sign. For instance: - -`npm run test:state -- --fork='London+3855'` - -Will run the state tests with the London hardfork and with EIP-3855 activated. To activate multiple EIPs: - -`npm run test:blockchain -- --fork='London+3855+3860'` - -This runs the blockchain tests on the London hardfork with the EIP-3855 and EIP-3860 activated. Note, that only tests which have testdata on this specific configuration will run: most combinations will run 0 tests. - -State tests run significantly faster than Blockchain tests, so it is often a good choice to start fixing State tests. - -#### Running Specific Tests - -Running all the blockchain tests in a file: - -`ts-node ./tests/tester --blockchain --file='randomStatetest303'` - -Running tests from a specific directory: - -`ts-node ./tests/tester --blockchain --dir='bcBlockGasLimitTest'` - -Running a specific state test case: - -`ts-node ./tests/tester --state --test='stackOverflow'` - -Only run test cases with selected `data`, `gas` and/or `value` values (see -[attribute description](http://ethereum-tests.readthedocs.io/en/latest/test_types/state_tests.html) in -test docs), provided by the index of the array element in the test `transaction` section: - -`ts-node ./tests/tester --state --test='CreateCollisionToEmpty' --data=0 --gas=1 --value=0` - -Recursively run all tests from a custom directory: - -`ts-node ./tests/tester --state --fork='London' --customTestsPath=../../my_custom_test_folder` - -Run a test from a specified source file not under the `tests` directory (only state tests): - -`ts-node ./tests/tester --state --customStateTest='{path_to_file}'` - -#### Running tests with a reporter/formatter - -`npm run formatTest -t [npm script name OR node command] -with [formatter]` will report test results using a formatter of your choosing. - -`npm install -g tap-mocha-reporter` -`npm run formatTest -- -t test:API -with 'tap-mocha-reporter json'` - -To pipe the results of tests run with a node command to a formatter: - -`npm run formatTest -- -t "./tests/tester --blockchain --dir='bcBlockGasLimitTest'" -with 'tap-mocha-reporter json'` - -If no reporter or formatter is provided, test results will be reported by `tape` without any additional formatting. - -#### Skipping Tests - -There are three types of skip lists (`BROKEN`, `PERMANENT` and `SLOW`) which -can be found in `tests/tester.js`. By default tests from all skip lists are omitted. - -You can change this behaviour with: - -`ts-node ./tests/tester --state --skip=BROKEN,PERMANENT` - -to skip only the `BROKEN` and `PERMANENT` tests and include the `SLOW` tests. -There are also the keywords `NONE` or `ALL` for convenience. - -It is also possible to only run the tests from the skip lists: - -`ts-node ./tests/tester --state --runSkipped=SLOW` - -### CI Test Integration - -Tests and checks are run in CI using [Github Actions](https://github.com/ethereumjs/ethereumjs-monorepo/actions). The configuration can be found in `.github/workflows`. - -#### On-demand testing for VM State and Blockchain - -On an ordinary PR, `vm-state-extended` and `vm-blockchain-extended` will be skipped -unless the special label `type: test all hardforks` is applied. -If the label is removed, the extended tests will not run anymore. - -### Debugging - -#### Local Debugging - -For state tests you can use the `--jsontrace` flag to output opcode trace information. - -Blockchain tests support `--debug` to verify the postState: - -`ts-node ./tests/tester --blockchain --debug --test='ZeroValue_SELFDESTRUCT_ToOneStorageKey_OOGRevert_d0g0v0_EIP158'` - -All/most State tests are replicated as Blockchain tests in a `GeneralStateTests` [sub directory](https://github.com/ethereum/tests/tree/develop/BlockchainTests/GeneralStateTests) in the Ethereum tests repo, so for debugging single test cases the Blockchain test version of the State test can be used. - -#### Comparing Stack Traces - -Other client implementations often also provide functionality for output trace information. - -A convenient way is to use a local `geth` installation (can be the binary installation and doesn't has to be build from source or something) and then use the included `evm` tool like: - -```shell -evm --json --nomemory statetest node_modules/ethereumjs-testing/tests/GeneralStateTests/stCreate2/create2collisionCode2.json -``` - -If you want to have only the output for a specific fork you can go into the referenced json test file and temporarily delete the `post` section for the non-desired fork outputs (or, more safe and also more convenient on triggering later: copy the test files you are interested in to your working directory and then modify without further worrying). - -#### Debugging Tools - -For comparing `EVM` traces [here](https://gist.github.com/cdetrio/41172f374ae32047a6c9e97fa9d09ad0) are some instructions for setting up `pyethereum` to generate corresponding traces for state tests. - -Compare TAP output from blockchain/state tests and produces concise diff of the differences between them (example): - -``` -curl https://gist.githubusercontent.com/jwasinger/6cef66711b5e0787667ceb3db6bea0dc/raw/0740f03b4ce90d0955d5aba1e0c30ce698c7145a/gistfile1.txt > output-wip-byzantium.txt -curl https://gist.githubusercontent.com/jwasinger/e7004e82426ff0a7137a88d273f11819/raw/66fbd58722747ebe4f7006cee59bbe22461df8eb/gistfile1.txt > output-master.txt -python utils/diffTestOutput.py output-wip-byzantium.txt output-master.txt -``` - -An extremely rich and powerful toolbox is the [evmlab](https://github.com/holiman/evmlab) from `holiman`, both for debugging and creating new test cases or example data. - -## Git Branch Performance Testing - -The [`diffTester`](./scripts/diffTester.sh) script can be used to do simple comparative performance testing of changes made targeting the VM. This script allows you to run a single State test a specified number of times on two different branches and reports the average time of the test run for each branch. While not statistically rigorous, it gives you a quick sense of how a specific change (or set of changes) may impact VM performance on a given area that is covered by one specific test. Run this script from `[monorepo-root]/packages/vm` as below: -```sh -./scripts/diffTester.sh -b git-branch-you-want-to-test -t "path/to/my/favorite/state/test.json" -r [the number of times to run the test] -``` - -and it will produce output like for the `git-branch-you-want-to-test` and then whatever git branch you are currently on: -```sh -TAP version 13 -# GeneralStateTests -# file: path/to/my/favorite/state/test.json test: test -ok 1 [ 3.785 secs ] the state roots should match (successful tx run) -ok 2 [ 1.228 secs ] the state roots should match (successful tx run) -ok 3 [ 1.212 secs ] the state roots should match (successful tx run) -ok 4 [ 1.306 secs ] the state roots should match (successful tx run) -ok 5 [ 1.472 secs ] the state roots should match (successful tx run) -# Average test run: 1.801 s -``` - -Note: this script runs by actually checking out the targeted branch, running the test, and then switching back to your current branch, running the test again, and then restoring any changes you had in the current branch. For best results, you shuld run this test while you currently have `master` checked out. -## Profiling - -[Clinic](https://github.com/nearform/node-clinic) allows profiling the VM in the node environment. It supports various profiling methods, among them is [flame](https://github.com/nearform/node-clinic-flame) which can be used for generating flamegraphs to highlight bottlenecks and hot paths. As an example, to generate a flamegraph for the VM blockchain tests, you can run: - -```sh -NODE_OPTIONS="--max-old-space-size=4096" clinic flame -- node ./tests/tester.js --blockchain --excludeDir='GeneralStateTests' -``` - -## Benchmarks - -This helps us see how the VM performs when running mainnet blocks. - -View the historical benchmark data for the master branch on the [github page](http://ethereumjs.github.io/ethereumjs-monorepo/dev/bench/vm). - -We want to use the compiled JS so `ts-node` does not show up in the profile. So run: - -`npm run build:benchmarks` - -Then: - -`npm run benchmarks -- mainnetBlocks` - -To define the number of samples to be run pass in a number like so: `npm run benchmarks -- mainnetBlocks:10` - -If you want to get a more detailed look to find bottlenecks we can use [0x](https://github.com/davidmarkclements/0x): - -``` -npm run profiling -- mainnetBlocks:10 -``` - -and open the link it generates. - -For a high-level introduction on flame graphs see e.g. [this](https://blog.codecentric.de/en/2017/09/jvm-fire-using-flame-graphs-analyse-performance/) blog article (the non-Java part). diff --git a/packages/evm/README.md b/packages/evm/README.md index e1e4dee3ea..2f7d6a2ef7 100644 --- a/packages/evm/README.md +++ b/packages/evm/README.md @@ -1,34 +1,45 @@ # @ethereumjs/evm -[![NPM Package][vm-npm-badge]][vm-npm-link] -[![GitHub Issues][vm-issues-badge]][vm-issues-link] -[![Actions Status][vm-actions-badge]][vm-actions-link] -[![Code Coverage][vm-coverage-badge]][vm-coverage-link] +[![NPM Package][evm-npm-badge]][evm-npm-link] +[![GitHub Issues][evm-issues-badge]][evm-issues-link] +[![Actions Status][evm-actions-badge]][evm-actions-link] +[![Code Coverage][evm-coverage-badge]][evm-coverage-link] [![Discord][discord-badge]][discord-link] -| TypeScript implementation of the Ethereum VM. | -| --------------------------------------------- | +| TypeScript implementation of the Ethereum EVM. | +| ---------------------------------------------- | + + # INSTALL `npm install @ethereumjs/evm` +This package provides the core Ethereum Virtual Machine (EVM) implementation which is capable of executing EVM-compatible bytecode. The package has been extracted from the [@ethereumjs/vm](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/vm) package along the VM `v6` release. + +Note that this package atm cannot be run in a standalong mode but needs to be executed via the `VM` package which provides an outer Ethereum `mainnet` compatible execution context. Standalone functionality will be added along a future non-breaking release. + # USAGE ```typescript import Common, { Chain, Hardfork } from '@ethereumjs/common' +import Blockchain from '@ethereumjs/blockchain' +import { EEI } from '@ethereumjs/vm' import EVM from '@ethereumjs/evm' +import { DefaultStateManager } from '@ethereumjs/statemanager' +// Note: in a future release there will be an EEI default implementation +// which will ease standalone initialization const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) -const blockchain = new Blockchain({ common }) +const blockchain = await Blockchain.create({ common }) const stateManager = new DefaultStateManager({ common }) -const eei = EEI(stateManager, common, blockchain) +const eei = new EEI(stateManager, common, blockchain) + const evm = new EVM({ common, blockchain, eei, - }); - + }) const STOP = '00' const ADD = '01' @@ -59,53 +70,41 @@ evm This projects contain the following examples: 1. [./examples/decode-opcodes](./examples/decode-opcodes.ts): Decodes a binary EVM program into its opcodes. +1. [./examples/run-code-browser](./examples/run-code-browser.js): Show how to use this library in a browser. All of the examples have their own `README.md` explaining how to run them. # API -## VM - -For documentation on `VM` instantiation, exposed API and emitted `events` see generated [API docs](./docs/README.md). - -## VmState - -The VmState is the wrapper class that manages the context around the underlying state while executing the VM like `EIP-2929`(Gas cost increases for state access opcodes). A Custom implementation of the `StateManager` can be plugged in the VmState +## EVM -# BROWSER +For documentation on `EVM` instantiation, exposed API and emitted `events` see generated [API docs](./docs/README.md). -To build the VM for standalone use in the browser, see: [Running the VM in a browser](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/vm/examples/run-code-browser.js). +## VM/EVM Relation -# SETUP +This package contains the inner Ethereum Virtual Machine core functionality which was included in the [@ethereumjs/vm](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/vm) package up till v5 and has been extracted along the v6 release. -## Chain Support +This will make it easier to customize the inner EVM, which can now be passed as an optional argument to the outer `VM` instance. -Starting with `v5.1.0` the VM supports running both `Ethash/PoW` and `Clique/PoA` blocks and transactions. Clique support has been added along the work on PR [#1032](https://github.com/ethereumjs/ethereumjs-monorepo/pull/1032) and follow-up PRs and (block) validation checks and the switch of the execution context now happens correctly. +At the moment the `EVM` package can not be run standalone and it is therefore recommended for most use cases to rather use the `VM` package and access `EVM` functionality through the `vm.evm` property. -### Ethash/PoW Chains +## Execution Environment (EEI) and State -`@ethereumjs/blockchain` validates the PoW algorithm with `@ethereumjs/ethash` and validates blocks' difficulty to match their canonical difficulty. +For the EVM to properly work it needs access to a respective execution environment (to e.g. request on information like block hashes) as well as the connection to an outer account and contract state. -### Clique/PoA Chains +To ensure a unified interface the `EVM` provides a TypeScript `EEI` interface providing which includes the necessary function signatures for access to environmental parameters as well as the VM state. -The following is a simple example for a block run on `Goerli`: +The [@ethereumjs/vm](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/vm) provides a concrete implementation of this interface which can be used to instantiate the `EVM` within an Ethereum `mainnet` compatible execution context. -```typescript -import VM from '@ethereumjs/vm' -import Common, { Chain } from '@ethereumjs/common' +# BROWSER -const common = new Common({ chain: Chain.Goerli }) -const hardforkByBlockNumber = true -const vm = new VM({ common, hardforkByBlockNumber }) +To build the EVM for standalone use in the browser, see: [Running the EVM in a browser](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/evm/examples/run-code-browser.js). -const serialized = Buffer.from('f901f7a06bfee7294bf4457...', 'hex') -const block = Block.fromRLPSerializedBlock(serialized, { hardforkByBlockNumber }) -const result = await vm.runBlock(block) -``` +# SETUP ## Hardfork Support -The EthereumJS VM implements all hardforks from `Frontier` (`chainstart`) up to the latest active mainnet hardfork. +The EthereumJS EVM implements all hardforks from `Frontier` (`chainstart`) up to the latest active mainnet hardfork. Currently the following hardfork rules are supported: @@ -124,41 +123,12 @@ Currently the following hardfork rules are supported: Default: `london` (taken from `Common.DEFAULT_HARDFORK`) -A specific hardfork VM ruleset can be activated by passing in the hardfork -along the `Common` instance: - -```typescript -import Common, { Chain, Hardfork } from '@ethereumjs/common' -import VM from '@ethereumjs/vm' - -const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Berlin }) -const vm = new VM({ common }) -``` - -## Custom genesis state support - -If you want to create a new instance of the VM and add your own genesis state, you can do it by passing a `Common` -instance with [custom genesis state](../common/README.md#initialize-using-customchains-array) and passing the flag `activateGenesisState` in `VMOpts`, e.g.: - -```typescript -import Common from '@ethereumjs/common' -import VM from '@ethereumjs/vm' -import myCustomChain1 from '[PATH_TO_MY_CHAINS]/myCustomChain1.json' -import chain1GenesisState from '[PATH_TO_GENESIS_STATES]/chain1GenesisState.json' - -const common = new Common({ - chain: 'myCustomChain1', - customChains: [[myCustomChain1, chain1GenesisState]], -}) -const vm = new VM({ common, activateGenesisState: true }) -``` - -Genesis state can be configured to contain both EOAs as well as (system) contracts with initial storage values set. +A specific hardfork EVM ruleset can be activated by passing in the hardfork +along the `Common` instance to the outer `@ethereumjs/vm` instance. ## EIP Support -It is possible to individually activate EIP support in the VM by instantiate the `Common` instance passed -with the respective EIPs, e.g.: +It is possible to individually activate EIP support in the EVM by instantiate the `Common` instance passed to the outer VM with the respective EIPs, e.g.: ```typescript import Common, { Chain } from '@ethereumjs/common' @@ -188,14 +158,10 @@ Currently supported EIPs: ## Tracing Events -Our `TypeScript` VM is implemented as an [AsyncEventEmitter](https://github.com/ahultgren/async-eventemitter) and events are submitted along major execution steps which you can listen to. +Our `TypeScript` EVM is implemented as an [AsyncEventEmitter](https://github.com/ahultgren/async-eventemitter) and events are submitted along major execution steps which you can listen to. You can subscribe to the following events: -- `beforeBlock`: Emits a `Block` right before running it. -- `afterBlock`: Emits `AfterBlockEvent` right after running a block. -- `beforeTx`: Emits a `Transaction` right before running it. -- `afterTx`: Emits a `AfterTxEvent` right after running a transaction. - `beforeMessage`: Emits a `Message` right after running it. - `afterMessage`: Emits an `EVMResult` right after running a message. - `step`: Emits an `InterpreterStep` right before running an EVM step. @@ -206,15 +172,15 @@ An example for the `step` event can be found in the initial usage example in thi ### Asynchronous event handlers You can perform asynchronous operations from within an event handler -and prevent the VM to keep running until they finish. +and prevent the EVM to keep running until they finish. In order to do that, your event handler has to accept two arguments. The first one will be the event object, and the second one a function. -The VM won't continue until you call this function. +The EVM won't continue until you call this function. If an exception is passed to that function, or thrown from within the handler or a function called by it, the exception will bubble into the -VM and interrupt it, possibly corrupting its state. It's strongly +EVM and interrupt it, possibly corrupting its state. It's strongly recommended not to do that. ### Synchronous event handlers @@ -226,13 +192,13 @@ Note that if your event handler receives multiple arguments, the second one will be the continuation function, and it must be called. If an exception is thrown from withing the handler or a function called -by it, the exception will bubble into the VM and interrupt it, possibly +by it, the exception will bubble into the EVM and interrupt it, possibly corrupting its state. It's strongly recommended not to throw from withing event handlers. -# Understanding the VM +# Understanding the EVM -If you want to understand your VM runs we have added a hierarchically structured list of debug loggers for your convenience which can be activated in arbitrary combinations. We also use these loggers internally for development and testing. These loggers use the [debug](https://github.com/visionmedia/debug) library and can be activated on the CL with `DEBUG=[Logger Selection] node [Your Script to Run].js` and produce output like the following: +If you want to understand your EVM runs we have added a hierarchically structured list of debug loggers for your convenience which can be activated in arbitrary combinations. We also use these loggers internally for development and testing. These loggers use the [debug](https://github.com/visionmedia/debug) library and can be activated on the CL with `DEBUG=[Logger Selection] node [Your Script to Run].js` and produce output like the following: ![EthereumJS VM Debug Logger](./debug.png?raw=true) @@ -240,13 +206,9 @@ The following loggers are currently available: | Logger | Description | | --------------------------------- | ------------------------------------------------------------------ | -| `vm:block` | Block operations (run txs, generating receipts, block rewards,...) | -| `vm:tx` |  Transaction operations (account updates, checkpointing,...)  | -| `vm:tx:gas` |  Transaction gas logger | | `vm:evm` |  EVM control flow, CALL or CREATE message execution | | `vm:evm:gas` |  EVM gas logger | | `vm:eei:gas` |  EEI gas logger | -| `vm:state` | StateManager logger | | `vm:ops` |  Opcode traces | | `vm:ops:[Lower-case opcode name]` | Traces on a specific opcode | @@ -255,7 +217,7 @@ Here are some examples for useful logger combinations. Run one specific logger: ```shell -DEBUG=vm:tx ts-node test.ts +DEBUG=vm:evm ts-node test.ts ``` Run all loggers currently available: @@ -270,32 +232,22 @@ Run only the gas loggers: DEBUG=vm:*:gas ts-node test.ts ``` -Excluding the state logger: +Excluding the ops logger: ```shell -DEBUG=vm:*,vm:*:*,-vm:state ts-node test.ts +DEBUG=vm:*,vm:*:*,-vm:ops ts-node test.ts ``` Run some specific loggers including a logger specifically logging the `SSTORE` executions from the VM (this is from the screenshot above): ```shell -DEBUG=vm:tx,vm:evm,vm:ops:sstore,vm:*:gas ts-node test.ts +DEBUG=vm:evm,vm:ops:sstore,vm:*:gas ts-node test.ts ``` # Internal Structure -The VM processes state changes at many levels. - -- **runBlockchain** - - for every block, runBlock -- **runBlock** - - for every tx, runTx - - pay miner and uncles -- **runTx** - - check sender balance - - check sender nonce - - runCall - - transfer gas charges +The EVM processes state changes at many levels. + - **runCall** - checkpoint state - transfer value @@ -315,9 +267,11 @@ The VM processes state changes at many levels. The opFns for `CREATE`, `CALL`, and `CALLCODE` call back up to `runCall`. +TODO: this section likely needs an update. + # DEVELOPMENT -Developer documentation - currently mainly with information on testing and debugging - can be found [here](./DEVELOPER.md). +See [@ethereumjs/vm](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/vm) README. # EthereumJS @@ -331,11 +285,11 @@ If you want to join for work or do improvements on the libraries have a look at [discord-badge]: https://img.shields.io/static/v1?logo=discord&label=discord&message=Join&color=blue [discord-link]: https://discord.gg/TNwARpR -[vm-npm-badge]: https://img.shields.io/npm/v/@ethereumjs/vm.svg -[vm-npm-link]: https://www.npmjs.com/package/@ethereumjs/vm -[vm-issues-badge]: https://img.shields.io/github/issues/ethereumjs/ethereumjs-monorepo/package:%20vm?label=issues -[vm-issues-link]: https://github.com/ethereumjs/ethereumjs-monorepo/issues?q=is%3Aopen+is%3Aissue+label%3A"package%3A+vm" -[vm-actions-badge]: https://github.com/ethereumjs/ethereumjs-monorepo/workflows/VM/badge.svg -[vm-actions-link]: https://github.com/ethereumjs/ethereumjs-monorepo/actions?query=workflow%3A%22VM%22 -[vm-coverage-badge]: https://codecov.io/gh/ethereumjs/ethereumjs-monorepo/branch/master/graph/badge.svg?flag=vm -[vm-coverage-link]: https://codecov.io/gh/ethereumjs/ethereumjs-monorepo/tree/master/packages/vm +[evm-npm-badge]: https://img.shields.io/npm/v/@ethereumjs/evm.svg +[evm-npm-link]: https://www.npmjs.com/package/@ethereumjs/evm +[evm-issues-badge]: https://img.shields.io/github/issues/ethereumjs/ethereumjs-monorepo/package:%20evm?label=issues +[evm-issues-link]: https://github.com/ethereumjs/ethereumjs-monorepo/issues?q=is%3Aopen+is%3Aissue+label%3A"package%3A+evm" +[evm-actions-badge]: https://github.com/ethereumjs/ethereumjs-monorepo/workflows/EVM/badge.svg +[evm-actions-link]: https://github.com/ethereumjs/ethereumjs-monorepo/actions?query=workflow%3A%22EVM%22 +[evm-coverage-badge]: https://codecov.io/gh/ethereumjs/ethereumjs-monorepo/branch/master/graph/badge.svg?flag=evm +[evm-coverage-link]: https://codecov.io/gh/ethereumjs/ethereumjs-monorepo/tree/master/packages/evm diff --git a/packages/evm/examples/decode-opcodes.ts b/packages/evm/examples/decode-opcodes.ts index 5abfb655a5..eaf5fae9f7 100644 --- a/packages/evm/examples/decode-opcodes.ts +++ b/packages/evm/examples/decode-opcodes.ts @@ -3,7 +3,7 @@ // 1. Takes binary EVM code and decodes it into opcodes import Common, { Chain, Hardfork } from '@ethereumjs/common' -import { getOpcodesForHF } from '../src/evm/opcodes' +import { getOpcodesForHF } from '../src/opcodes' const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Istanbul }) const opcodes = getOpcodesForHF(common).opcodes diff --git a/packages/vm/examples/run-code-browser.js b/packages/evm/examples/run-code-browser.js similarity index 93% rename from packages/vm/examples/run-code-browser.js rename to packages/evm/examples/run-code-browser.js index 1b8062851d..9d630e1c3a 100644 --- a/packages/vm/examples/run-code-browser.js +++ b/packages/evm/examples/run-code-browser.js @@ -6,6 +6,8 @@ * with browserify using `browserify index.js -o bundle.js` * and then load this folder onto a HTTP WebServer (e.g. * using node-static or `python -mSimpleHTTPServer`). + * + * NOTE: THIS EXAMPLE IS CURRENTLY OUTDATED AND DOESN'T WORK WITH EVM v1. */ const BN = require('bn.js') const VM = require('../dist').default diff --git a/packages/vm/README.md b/packages/vm/README.md index 61388b2da8..77a286d3c6 100644 --- a/packages/vm/README.md +++ b/packages/vm/README.md @@ -6,8 +6,14 @@ [![Code Coverage][vm-coverage-badge]][vm-coverage-link] [![Discord][discord-badge]][discord-link] -| TypeScript implementation of the Ethereum VM. | -| --------------------------------------------- | +| Execution Context for the Ethereum EVM Implementation. | +| ------------------------------------------------------ | + +This package provides an Ethereum `mainnet` compatible execution context for the +[@ethereumjs/evm](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/evm) +EVM implementation. + +Note that up till `v5` this package also was the bundled package for the EVM implementation itself. # INSTALL @@ -16,34 +22,23 @@ # USAGE ```typescript +import { Address } from '@ethereumjs/util' import Common, { Chain, Hardfork } from '@ethereumjs/common' +import { Transaction } from '@ethereumjs/tx' import VM from '@ethereumjs/vm' -const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.London }) -const vm = new VM({ common }) - -const STOP = '00' -const ADD = '01' -const PUSH1 = '60' - -// Note that numbers added are hex values, so '20' would be '32' as decimal e.g. -const code = [PUSH1, '03', PUSH1, '05', ADD, STOP] - -vm.evm.on('step', function (data) { - // Note that data.stack is not immutable, i.e. it is a reference to the vm's internal stack object - console.log(`Opcode: ${data.opcode.name}\tStack: ${data.stack}`) +const common = new Common({ chain: Chain.Mainnet, hardfork: Hardfork.Berlin }) +const vm = await VM.create({ common }) + +const tx = Transaction.fromTxData({ + gasLimit: BigInt(21000), + value: BigInt(1), + to: Address.zero(), + v: BigInt(37), + r: BigInt('62886504200765677832366398998081608852310526822767264927793100349258111544447'), + s: BigInt('21948396863567062449199529794141973192314514851405455194940751428901681436138'), }) - -vm.evm - .runCode({ - code: Buffer.from(code.join(''), 'hex'), - gasLimit: BigInt(0xffff), - }) - .then((results) => { - console.log(`Returned: ${results.returnValue.toString('hex')}`) - console.log(`gasUsed : ${results.gasUsed.toString()}`) - }) - .catch(console.error) +await vm.runTx({ tx, skipBalance: true }) ``` ## Example @@ -51,9 +46,7 @@ vm.evm This projects contain the following examples: 1. [./examples/run-blockchain](./examples/run-blockchain.ts): Loads tests data, including accounts and blocks, and runs all of them in the VM. -1. [./examples/run-code-browser](./examples/run-code-browser.js): Show how to use this library in a browser. 1. [./examples/run-solidity-contract](./examples/run-solidity-contract.ts): Compiles a Solidity contract, and calls constant and non-constant functions. -1. [./examples/decode-opcodes](./examples/decode-opcodes.ts): Decodes a binary EVM program into its opcodes. All of the examples have their own `README.md` explaining how to run them. @@ -63,13 +56,26 @@ All of the examples have their own `README.md` explaining how to run them. For documentation on `VM` instantiation, exposed API and emitted `events` see generated [API docs](./docs/README.md). -## VmState +## VM/EVM Relation + +Starting with the `VM` v6 version the inner Ethereum Virtual Machine core previously included in this library has been extracted to an own package [@ethereumjs/evm](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/evm). + +It is still possible to access all `EVM` functionality through the `evm` property of the initialized `vm` object, e.g.: + +```typescript +vm.evm.runCode() // or +vm.evm.on('step', function (data) { + console.log(`Opcode: ${data.opcode.name}\tStack: ${data.stack}`) +}) +``` + +Note that it now also get's possible to pass in an own or customized `EVM` instance by using the optional `evm` constructor option. -The VmState is the wrapper class that manages the context around the underlying state while executing the VM like `EIP-2929`(Gas cost increases for state access opcodes). A Custom implementation of the `StateManager` can be plugged in the VmState +## Execution Environment (EEI) and State -# BROWSER +This package provides a concrete implementation of the [@ethereumjs/evm](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/evm) EEI interface to instantiate a VM/EVM combination with an Ethereum `mainnet` compatible execution context. -To build the VM for standalone use in the browser, see: [Running the VM in a browser](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/vm/examples/run-code-browser.js). +With `VM` v6 the previously included `StateManager` has been extracted to its own package [@ethereumjs/statemanager](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/statemanger). The `StateManager` package provides a unified state interface and it is now also possible to provide a modified or custom `StateManager` to the VM via the optional `stateManager` constructor option. # SETUP @@ -100,27 +106,9 @@ const result = await vm.runBlock(block) ## Hardfork Support -The EthereumJS VM implements all hardforks from `Frontier` (`chainstart`) up to the latest active mainnet hardfork. - -Currently the following hardfork rules are supported: - -- `chainstart` (a.k.a. Frontier) -- `homestead` -- `tangerineWhistle` -- `spuriousDragon` -- `byzantium` -- `constantinople` -- `petersburg` -- `istanbul` -- `muirGlacier` (only `mainnet` and `ropsten`) -- `berlin` (`v5.2.0`+) -- `london` (`v5.4.0`+) -- `arrowGlacier` (only `mainnet`) (`v5.6.0`+) +For hardfork support see the [Hardfork Support](../evm#hardfork-support) section from the underlying `@ethereumjs/evm` instance. -Default: `london` (taken from `Common.DEFAULT_HARDFORK`) - -A specific hardfork VM ruleset can be activated by passing in the hardfork -along the `Common` instance: +An explicit HF in the `VM` - which is then passed on to the inner `EVM` - can be set with: ```typescript import Common, { Chain, Hardfork } from '@ethereumjs/common' @@ -132,8 +120,12 @@ const vm = new VM({ common }) ## Custom genesis state support -If you want to create a new instance of the VM and add your own genesis state, you can do it by passing a `Common` -instance with [custom genesis state](../common/README.md#initialize-using-customchains-array) and passing the flag `activateGenesisState` in `VMOpts`, e.g.: +Genesis state code logic has been reworked substantially along the v6 breaking releases and a lot of the genesis state code moved from both the `@ethereumjs/common` and `@ethereumjs/block` libraries to the `@ethereumjs/blockchain` library, see PR [#1916](https://github.com/ethereumjs/ethereumjs-monorepo/pull/1916) for an overview on the broad set of changes. + + +For initializing a custom genesis state you can now use the `genesisState` constructor option in the `Blockchain` library in a similar way this had been done in the `Common` library before. + +If you want to create a new instance of the VM and add your own genesis state, you can do it by passing a `Blockchain` instance with custom genesis state set with the `genesisState` constructor option and passing the flag `activateGenesisState` in `VMOpts`. ```typescript import Common from '@ethereumjs/common' @@ -142,10 +134,12 @@ import myCustomChain1 from '[PATH_TO_MY_CHAINS]/myCustomChain1.json' import chain1GenesisState from '[PATH_TO_GENESIS_STATES]/chain1GenesisState.json' const common = new Common({ - chain: 'myCustomChain1', - customChains: [[myCustomChain1, chain1GenesisState]], + // TODO: complete example }) -const vm = new VM({ common, activateGenesisState: true }) +const blockchain = await Blockchain.create({ + // TODO: complete example +}) +const vm = await VM.create({ common, activateGenesisState: true }) ``` Genesis state can be configured to contain both EOAs as well as (system) contracts with initial storage values set. @@ -163,23 +157,7 @@ const common = new Common({ chain: Chain.Mainnet, eips: [2537] }) const vm = new VM({ common }) ``` -Currently supported EIPs: - -- [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) - Fee Market (`london` EIP) -- [EIP-2315](https://eips.ethereum.org/EIPS/eip-2315) - Simple subroutines (`experimental`) -- [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537) - BLS precompiles (`experimental`) -- [EIP-2565](https://eips.ethereum.org/EIPS/eip-2565) - ModExp gas cost (`berlin` EIP) -- [EIP-2718](https://eips.ethereum.org/EIPS/eip-2718) - Typed transactions (`berlin` EIP) -- [EIP-2929](https://eips.ethereum.org/EIPS/eip-2929) - Gas cost increases for state access opcodes (`berlin` EIP) -- [EIP-2930](https://eips.ethereum.org/EIPS/eip-2930) - Optional Access Lists Typed Transactions (`berlin` EIP) -- [EIP-3198](https://eips.ethereum.org/EIPS/eip-3198) - BASEFEE opcode (`london` EIP) -- [EIP-3529](https://eips.ethereum.org/EIPS/eip-3529) - Reduction in refunds (`london` EIP) -- [EIP-3540](https://eips.ethereum.org/EIPS/eip-3541) - EVM Object Format (EOF) v1 (`experimental`) -- [EIP-3541](https://eips.ethereum.org/EIPS/eip-3541) - Reject new contracts starting with the 0xEF byte (`london` EIP) -- [EIP-3670](https://eips.ethereum.org/EIPS/eip-3670) - EOF - Code Validation (`experimental`) -- [EIP-3855](https://eips.ethereum.org/EIPS/eip-3855) - PUSH0 instruction (`experimental`) -- [EIP-3860](https://eips.ethereum.org/EIPS/eip-3860) - Limit and meter initcode (`experimental`) -- [EIP-4399](https://eips.ethereum.org/EIPS/eip-4399) - Supplant DIFFICULTY opcode with PREVRANDAO (Merge) (`experimental`) +For a list with supported EIPs see the [@ethereumjs/evm](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/evm) documentation. ## Tracing Events @@ -191,12 +169,8 @@ You can subscribe to the following events: - `afterBlock`: Emits `AfterBlockEvent` right after running a block. - `beforeTx`: Emits a `Transaction` right before running it. - `afterTx`: Emits a `AfterTxEvent` right after running a transaction. -- `beforeMessage`: Emits a `Message` right after running it. -- `afterMessage`: Emits an `EVMResult` right after running a message. -- `step`: Emits an `InterpreterStep` right before running an EVM step. -- `newContract`: Emits a `NewContractEvent` right before creating a contract. This event contains the deployment code, not the deployed code, as the creation message may not return such a code. -An example for the `step` event can be found in the initial usage example in this `README`. +Please note that there are additional EVM-specific events in the [@ethereumjs/evm](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/evm) package. ### Asynchronous event handlers @@ -238,12 +212,9 @@ The following loggers are currently available: | `vm:block` | Block operations (run txs, generating receipts, block rewards,...) | | `vm:tx` |  Transaction operations (account updates, checkpointing,...)  | | `vm:tx:gas` |  Transaction gas logger | -| `vm:evm` |  EVM control flow, CALL or CREATE message execution | -| `vm:evm:gas` |  EVM gas logger | -| `vm:eei:gas` |  EEI gas logger | | `vm:state` | StateManager logger | -| `vm:ops` |  Opcode traces | -| `vm:ops:[Lower-case opcode name]` | Traces on a specific opcode | + +Note that there are additional EVM-specific loggers in the [@ethereumjs/evm](https://github.com/ethereumjs/ethereumjs-monorepo/tree/master/packages/evm) package. Here are some examples for useful logger combinations. @@ -291,24 +262,8 @@ The VM processes state changes at many levels. - check sender nonce - runCall - transfer gas charges -- **runCall** - - checkpoint state - - transfer value - - load code - - runCode - - materialize created contracts - - revert or commit checkpoint -- **runCode** - - iterate over code - - run op codes - - track gas usage -- **OpFns** - - run individual op code - - modify stack - - modify memory - - calculate fee - -The opFns for `CREATE`, `CALL`, and `CALLCODE` call back up to `runCall`. + +TODO: this section likely needs an update. # DEVELOPMENT diff --git a/packages/vm/examples/run-blockchain.ts b/packages/vm/examples/run-blockchain.ts index d98c2c41b4..811d7493e7 100644 --- a/packages/vm/examples/run-blockchain.ts +++ b/packages/vm/examples/run-blockchain.ts @@ -8,11 +8,10 @@ import { Account, Address, toBuffer, setLengthLeft } from '@ethereumjs/util' import { Block } from '@ethereumjs/block' -import Blockchain, { EthashConsensus } from '@ethereumjs/blockchain' +import Blockchain from '@ethereumjs/blockchain' import Common, { ConsensusType } from '@ethereumjs/common' import VM from '../' import { testData } from './helpers/blockchain-mock-data' -import { Level } from 'level' async function main() { const common = new Common({ chain: 1, hardfork: testData.network.toLowerCase() }) diff --git a/packages/vm/examples/run-solidity-contract.ts b/packages/vm/examples/run-solidity-contract.ts index d15f249417..df3bcc4216 100644 --- a/packages/vm/examples/run-solidity-contract.ts +++ b/packages/vm/examples/run-solidity-contract.ts @@ -20,6 +20,10 @@ const block = Block.fromBlockData({ header: { extraData: Buffer.alloc(97) }},{ c * This function creates the input for the Solidity compiler. * * For more info about it, go to https://solidity.readthedocs.io/en/v0.5.10/using-the-compiler.html#compiler-input-and-output-json-description + * + * Note: this example additionally needs the Solidity compiler `solc` package (out of EthereumJS + * scope) being installed. You can do this (in this case it might make sense to install globally) + * with `npm i -g solc`. */ function getSolcInput() { return {