diff --git a/.gitbook.yaml b/.gitbook.yaml new file mode 100644 index 0000000000000..375d86814001f --- /dev/null +++ b/.gitbook.yaml @@ -0,0 +1,8 @@ +root: ./packages/docs/ + +​structure: + readme: README.md + summary: SUMMARY.md​ + +redirects: + previous/page: new-folder/page.md diff --git a/packages/docs/.gitbook/assets/rollup-contracts-overview.png b/packages/docs/.gitbook/assets/rollup-contracts-overview.png new file mode 100644 index 0000000000000..b5dd473d6e5a5 Binary files /dev/null and b/packages/docs/.gitbook/assets/rollup-contracts-overview.png differ diff --git a/packages/docs/.gitbook/assets/untitled-diagram-4.png b/packages/docs/.gitbook/assets/untitled-diagram-4.png new file mode 100644 index 0000000000000..ad62a0cc40499 Binary files /dev/null and b/packages/docs/.gitbook/assets/untitled-diagram-4.png differ diff --git a/packages/docs/README.md b/packages/docs/README.md new file mode 100644 index 0000000000000..5a5576c5f6d4b --- /dev/null +++ b/packages/docs/README.md @@ -0,0 +1,10 @@ +# Introduction + +Hello and welcome to the documentation for Optimism's OVM and Optimistic rollup. + +Our `@eth-optimism` module is the set of core modules that the OVM and our L2 implementations use. + +![The OVM Optipus](https://i.imgur.com/kWJ9eZ8.png) + +You can find all of our source code here in [our monorepo](https://github.com/ethereum-optimism/optimism-monorepo). + diff --git a/packages/docs/SUMMARY.md b/packages/docs/SUMMARY.md new file mode 100644 index 0000000000000..fb5595708aa15 --- /dev/null +++ b/packages/docs/SUMMARY.md @@ -0,0 +1,25 @@ +# Table of contents + +* [Introduction](README.md) +* [Synthetix Demo FAQ](synthetix-l2.md) + +## Protocol Specifications + +* [Optimistic Ethereum](protocol-specifications/optimistic-ethereum.md) +* [Optimistic Rollup](protocol-specifications/optimistic-rollup/README.md) + * [Block Production](protocol-specifications/optimistic-rollup/block-production.md) +* [Optimistic VM \(OVM\)](protocol-specifications/ovm/README.md) + * [OVM Core Components](protocol-specifications/ovm/ovm-components.md) + * [Execution Manager Overview](protocol-specifications/ovm/execution-manager.md) + * [Transpilation Overview](protocol-specifications/ovm/transpiler.md) + * [JUMP Transpilation](protocol-specifications/ovm/jump-transpilation.md) + * [CODECOPY Transpilation](protocol-specifications/ovm/codecopy.md) + +## Developer Documentation + +* [Getting Started](developer-documentation/getting-started/README.md) + * [ERC20 Token Tutorial](developer-documentation/getting-started/do-yo-thang.md) +* [Integrating the OVM](developer-documentation/erc20-tutorial/README.md) + * [Limitations](developer-documentation/erc20-tutorial/limitations.md) + * [Troubleshooting](developer-documentation/erc20-tutorial/troubleshooting.md) + diff --git a/packages/docs/developer-documentation/erc20-tutorial.md b/packages/docs/developer-documentation/erc20-tutorial.md new file mode 100644 index 0000000000000..1dfb462379354 --- /dev/null +++ b/packages/docs/developer-documentation/erc20-tutorial.md @@ -0,0 +1,2 @@ +# ERC20 Tutorial + diff --git a/packages/docs/developer-documentation/erc20-tutorial/README.md b/packages/docs/developer-documentation/erc20-tutorial/README.md new file mode 100644 index 0000000000000..e0d8f1435ef3b --- /dev/null +++ b/packages/docs/developer-documentation/erc20-tutorial/README.md @@ -0,0 +1,130 @@ +# Integrating the OVM + +If you're interested in testing your contracts running on the OVM, then you're in the right place! Please note that the OVM is in alpha and, as such, you'll probably find some bugs. If you do, please drop us a line or open a github issue! + +There are two steps you need to take to get your contracts running on the ovm: using `@eth-optimism/solc-transpiler` in place of `solc` so your contracts can be transpiled into OVM-compatible versions, and using `@eth-optimism/rollup-full-node` as your web3 provider. + +For reference, example integrations for both Truffle and Waffle can be found [in our monorepo](https://github.com/ethereum-optimism/optimism-monorepo/tree/master/packages/examples) . + +### Integrating the OVM Transpiler + +#### Installing + +Both `truffle` and `ethereum-waffle` allow you to specify a custom replacement for `solc-js`. First, you'll need to install `@eth-optimism/solc-transpiler`: + +```bash +yarn add @eth-optimism/solc-transpiler && yarn install +``` + +or + +```bash +npm install --save @eth-optimism/solc-transpiler +``` + +`@eth-optimism/solc-transpiler` Accepts the same compiler options as as `solc`, with one additional option, `executionManagerAddress`. The Execution Manager is a smart contract implementing the OVM's containerization functionality. If you are using an unmodified `rollup-full-node`, the default execution manager address is `0xA193E42526F1FEA8C99AF609dcEabf30C1c29fAA`. More info on the execution manager can be found here. \(link\) + +#### Using With Waffle + +To use the transpiler with `ethereum-waffle`, set the `solc.version` configuration to `""@eth-optimism/solc-transpiler"` and `compilerOptions.executionManagerAddress` to `"0xA193E42526F1FEA8C99AF609dcEabf30C1c29fAA"`. + +example waffle-config.json: + +```javascript +{ + "solcVersion": "@eth-optimism/solc-transpiler", + "compilerOptions": { + "executionManagerAddress": "0xA193E42526F1FEA8C99AF609dcEabf30C1c29fAA" + } +} +``` + +#### Using With Truffle + +To use the transpiler with Truffle, set truffle's `compilers.solc.version` configuration to `@eth-optimism/solc-transpiler`, and configure the `EXECUTION_MANAGER_ADDRESS` environment variable. + +Currently, Truffle does not provide a clean way to use custom chain IDs, so we have created the `@eth-optimism/ovm-truffle-provider-wrapper` package to seamlessly wrap your provider of choice to handle this. Note: you will also need to include `@eth-optimism/rollup-full-node` as a dependency if you would like to run a full node locally \(or use `ProviderWrapper.wrapProviderAndStartLocalNode(...)`\). + +example truffle-config.json: + +```javascript +const HDWalletProvider = require('truffle-hdwallet-provider'); +const ProviderWrapper = require("@eth-optimism/ovm-truffle-provider-wrapper"); +const mnemonic = 'candy maple cake sugar pudding cream honey rich smooth crumble sweet treat'; + +// Set this to the desired Execution Manager Address -- required for the transpiler +process.env.EXECUTION_MANAGER_ADDRESS = process.env.EXECUTION_MANAGER_ADDRESS || "0xA193E42526F1FEA8C99AF609dcEabf30C1c29fAA" + +module.exports = { +/** + * Note: this runs the OVM full node for the duration of the tests at `http://127.0.0.1:8545` + * + * To run tests: + * $ truffle test ./truffle-tests/test-erc20.js --config truffle-config-ovm.js + */ +networks: { + test: { + network_id: 108, + provider: function() { + return ProviderWrapper.wrapProviderAndStartLocalNode(new HDWalletProvider(mnemonic, "http://127.0.0.1:8545/", 0, 10)); + }, + gasPrice: 0, + gas: 9000000, + }, + live_example: { + provider: function () { + return ProviderWrapper.wrapProvider(new HDWalletProvider(mnemonic, "http://127.0.0.1:8545/", 0, 10)); + }, + gasPrice: 0, + gas: 9000000, + }, +}, + +// Set default mocha options here, use special reporters etc. +mocha: { + timeout: 100000 +}, + +compilers: { + solc: { + // Add path to the solc-transpiler + version: "@eth-optimism/solc-transpiler", + } +} +``` + +} + +As you can see in the above comments, you must spin up the rollup full node before running truffle tests. To do this, with `@eth-optimism/rollup-full-node` installed, you can run: + +\`\`\` sourceCode bash node rollup-full-node/build/src/exec/fullnode.js + +```text +Currently, `rollup-full-node` breaks Truffle's `gasLimit` and +`blockGasLimit`. To avoid this, you can set both to `undefined` where +they are used. + +## Integrating the OVM Full Node + +To use your transpiled contracts, you need to use +`@eth-optimism/rollup-full-node` as your web3 provider. To do this, make +sure it's installed: + +```bash +yarn add @eth-optimism/rollup-full-node && yarn install +``` + +or + +```bash +npm install --save @eth-optimism/rollup-full-node +``` + +To get your provider and some wallets: + +```typescript +const RollupFullNode = require("@eth-optimism/rollup-full-node") +const provider = RollupFullNode.getMockProvider() +const wallets = RollupFullNode.getWallets(provider) +``` + diff --git a/packages/docs/developer-documentation/erc20-tutorial/limitations.md b/packages/docs/developer-documentation/erc20-tutorial/limitations.md new file mode 100644 index 0000000000000..3b0571d2e4068 --- /dev/null +++ b/packages/docs/developer-documentation/erc20-tutorial/limitations.md @@ -0,0 +1,16 @@ +# Limitations + +Some features of the Ethereum are not yet implemented, or just don't make sense to have, in the OVM. This page documents some of those differences. + +## No Native ETH + +On L1, sending/recieving ETH is a special opcode different from ERC20s. This often leads to developers having to implement their contract functionality twice. For the OVM, we decided to eliminate this complexity and enforce that ETH only exist as an ERC20 \(WETH\). We think this will make developers' lives easier--but if you feel strongly otherwise, reach out and let us know! + +## Block Number + +Many L2 constructions, including our rollup implementation, support instant, real-time transactions. One way this manifests is that `block.number` is not easily interpretable or specified in the OVM. Instead, contracts can use `block.timestamp` \(though right now, this is not implemented so it stays at 0.\) + +## Parent/Child chain communication + +Communication between L1 and L2, also known as deposits and withdrawals, are not yet implemented in the OVM. Stay tuned for more on this! + diff --git a/packages/docs/developer-documentation/erc20-tutorial/troubleshooting.md b/packages/docs/developer-documentation/erc20-tutorial/troubleshooting.md new file mode 100644 index 0000000000000..856f7bfe1759a --- /dev/null +++ b/packages/docs/developer-documentation/erc20-tutorial/troubleshooting.md @@ -0,0 +1,22 @@ +# Troubleshooting + +The OVM is an alpha. In trying it out, you're helping us scale Ethereum and pave the way for a new generation of Layer 2 systems. It also means you'll probably run into some bugs! If you do, please reach out to us and create an issue on our [monorepo](https://github.com/ethereum-optimism/optimism-monorepo). + +While you do so, here is a collection of tips and notes that may help you figure out what's going on. + +### Limitations + +Some features of the Ethereum are not yet implemented, or just don't make sense to have, in the OVM. Check out our limitations section \(link\) to get more information to check if this is why you're running into issues. + +### Logging + +We use the npm package`debug` for logging. To get a better sense of what might be breaking, you can run .. code-block:: none + +> env DEBUG="debug:_,error:_" \[test command\] + +in your terminal. + +### Getting Wallets + +`rollup-full-node` provides an RPC-based provider, and does not always allow you to getWallets\(\). Instead, use the getWallets\(\) function exported by `rollup-full-node` instead. + diff --git a/packages/docs/developer-documentation/getting-started/README.md b/packages/docs/developer-documentation/getting-started/README.md new file mode 100644 index 0000000000000..0398750213fcc --- /dev/null +++ b/packages/docs/developer-documentation/getting-started/README.md @@ -0,0 +1,6 @@ +# Getting Started + +If you're looking to integrate with the OVM, welcome! You've come to the right place. Please note that the OVM is still in Alpha, and integrating now will inevitably uncover bugs. If you do, feel free to reach out to our [discord](https://discord.gg/cveQWV) or create a Github issue. We love talking to early adopters--don't be a stranger! + +To get you up to speed, you can follow along with our ERC20 conversion tutorial which takes a simple ERC20 EVM contract, and shows you how to transpile and run tests through the OVM. After that you will find some general notes on the current status of the OVM and helpful debugging tips. + diff --git a/packages/docs/developer-documentation/getting-started/cash.md b/packages/docs/developer-documentation/getting-started/cash.md new file mode 100644 index 0000000000000..1e674e45b58e5 --- /dev/null +++ b/packages/docs/developer-documentation/getting-started/cash.md @@ -0,0 +1,2 @@ +# Some other child page + diff --git a/packages/docs/developer-documentation/getting-started/do-yo-thang.md b/packages/docs/developer-documentation/getting-started/do-yo-thang.md new file mode 100644 index 0000000000000..421c5dc5694dc --- /dev/null +++ b/packages/docs/developer-documentation/getting-started/do-yo-thang.md @@ -0,0 +1,108 @@ +# ERC20 Token Tutorial + +## Getting Started with the OVM: Simple ERC20 Token Tutorial + +Hi there! Welcome to our OVM ERC20 tutorial. + +If you're interested in writing your first L2-compatible smart contract, you've come to the right place! This tutorial will cover how to move an existing contract and its tests into the wonderful world of L2. + +### Set up + +To start out, clone this example repo as a starting point. + +```bash +git clone https://github.com/ethereum-optimism/ERC20-Example.git +``` + +Now, enter the repository + +```bash +cd ERC20-Example +``` + +Install all dependencies + +```bash +yarn install +``` + +This project represents a fresh, non-OVM ERC20 contract example. Feel free to stop here and have a quick look at the contract and tests. + +In this tutorial, we'll cover the steps required to bring it into the world of L2. First, let's make sure all of our tests are running in our normal Ethereum environment: + +```bash +yarn test +``` + +You should see all of the tests passing. We're now ready to convert the project to build and test in an OVM environment! + +### Configuring the Transpiler + +First, we need to configure `ethereum-waffle` \(which is an alternative to truffle\) to use our new transpiler-enabled Solidity compiler. To do this, edit the `waffle-config.json` and replace it with: + +```javascript +{ + "sourcesPath": "./contracts", + "targetPath": "./build", + "npmPath": "./node_modules", + "solcVersion": "./node_modules/@eth-optimism/solc-transpiler", + "compilerOptions": { + "outputSelection": { + "*": { + "*": ["*"] + } + }, + "executionManagerAddress": "0xA193E42526F1FEA8C99AF609dcEabf30C1c29fAA" + } +} +``` + +### Using the Full Node + +To use the OVM to run our tests, open the test file at `test/erc20.spec.js`. We can import the OVM-ified versions of getWallets, createMockProvider, and deployContract near the top of the test file: + +```typescript +const { createMockProvider, getWallets, deployContract } = require('@eth-optimism/rollup-full-node') +``` + +Now remove the duplicated imports from ethereum-waffle, replacing the import on Line 2 with: + +```typescript +const {solidity} = require('ethereum-waffle'); +``` + +Our imports at the top of the file should now look like: + +```typescript +const {use, expect} = require('chai'); +const {solidity} = require('ethereum-waffle'); +const {createMockProvider, getWallets, deployContract } = require('@eth-optimism/rollup-full-node') +const ERC20 = require('../build/ERC20.json'); +``` + +We're almost there! After we've run our tests on the OVM, we need to stop our OVM server. We're going to add a single line of code after our before\(\) hook in order to close our OVM Server after our tests run: + +```typescript +before(async () => { + provider = await createMockProvider() + const wallets = getWallets(provider) + wallet = wallets[0] + walletTo = wallets[1] +}) + +//ADD TO SUPPORT OVM +after(() => {provider.closeOVM()}) +``` + +### Running the New Tests + +Great, we're ready to go! Now you can try to re-run your tests on top of the OVM with + +```bash +yarn test +``` + +### Wasn't that easy? + +The OVM provides a fresh new take on layer 2 development: it's identical to layer 1 development. No hoops, no tricks--the Ethereum you know and love, ready to scale up with L2. For more info on our progress and what's going on behind the scenes, you can follow us on [twitter](https://twitter.com/optimismPBC) and [check out our docs](https://docs.optimism.io)! + diff --git a/packages/docs/protocol-specifications/optimistic-ethereum.md b/packages/docs/protocol-specifications/optimistic-ethereum.md new file mode 100644 index 0000000000000..9705442600c1a --- /dev/null +++ b/packages/docs/protocol-specifications/optimistic-ethereum.md @@ -0,0 +1,10 @@ +# Optimistic Ethereum + +![Optimistic Ethereum Protocol Stack](../.gitbook/assets/untitled-diagram-4%20%281%29.png) + +The Optimistic Ethereum protocol consists of three key components: + +1. Ethereum mainnet: the root of security for Optimistic Ethereum. +2. Optimistic Rollup: the core scalability solution powering Optimistic Ethereum. +3. The Optimistic VM \(OVM\): EVM-compliant execution environment enabling Optimistic Ethereum to function just like L1 Ethereum. + diff --git a/packages/docs/protocol-specifications/optimistic-rollup/README.md b/packages/docs/protocol-specifications/optimistic-rollup/README.md new file mode 100644 index 0000000000000..e2d37d3ff04bc --- /dev/null +++ b/packages/docs/protocol-specifications/optimistic-rollup/README.md @@ -0,0 +1,4 @@ +# Optimistic Rollup + +Optimistic Rollup is the core scaling solution which enables the off-chain OVM to acieve cheap, instant transactions that still inherit L1 security. This section is a WIP. If you're interested in how ORU works, you can read more at a high level [here](https://medium.com/plasma-group/ethereum-smart-contracts-in-l2-optimistic-rollup-2c1cef2ec537). + diff --git a/packages/docs/protocol-specifications/optimistic-rollup/block-production.md b/packages/docs/protocol-specifications/optimistic-rollup/block-production.md new file mode 100644 index 0000000000000..76f09437760ef --- /dev/null +++ b/packages/docs/protocol-specifications/optimistic-rollup/block-production.md @@ -0,0 +1,20 @@ +# Block Production + +![Overview of Optimistic Rollup block production contracts on L1 Ethereum.](../../.gitbook/assets/rollup-contracts-overview%20%281%29.png) + +## Canonical Transaction Chain + +This is a monotonically increasing list of transactions which is maintained in an Ethereum smart contract. It can never change & is only reverted if L1 Ethereum blocks are reverted. It is the final word on what transactions are applied to the rollup chain, and in what order. Transactions from this chain come from one of two "queues": the OVM Transaction queue, and the L1->L2 Transaction Queue. + +### OVM Transaction Queue + +This is where the sequencer is allowed to post transactions which they recieved off chain to be applied to the rollup chain. Transactions can only be moved from the OVM Transaction Queue to the Canonical Transaction Chain if the transactions in the L1->L2 transaction queue are not older than some number of L1 blocks. + +### L1->L2 Transaction queue + +This is where users who are being censored, as well as L1 contracts like deposit contracts, enqueue transactions to be added to the rollup chain. After some number of L1 blocks, the L1->L2 transactions _must be included_ next in the canonical transaction chain. This enforces censorship resistance. + +## State Commitment Chain + +The state commitment chain is a rollup list of OVM “outputs” \(namely, state roots and outgoing messages\) which must correspond to the canonical chain’s inputs. When a transaction is appended to the canonical transaction chain, there is only 1 valid state which should result. These outputs are committed by the sequencer, and rolled back in the case of fraud--without touching the canonical transactions. + diff --git a/packages/docs/protocol-specifications/ovm/README.md b/packages/docs/protocol-specifications/ovm/README.md new file mode 100644 index 0000000000000..831e7d896d2cc --- /dev/null +++ b/packages/docs/protocol-specifications/ovm/README.md @@ -0,0 +1,18 @@ +# Optimistic VM \(OVM\) + +## What is the OVM? + +The Optimistic Virtual Machine \(OVM\) is a scalable form of the EVM. It lives at the core of [Optimistic Rollup](https://medium.com/plasma-group/ethereum-smart-contracts-in-l2-optimistic-rollup-2c1cef2ec537) \(ORU\) fullnodes and is able to execute Ethereum smart contracts at scale. It makes use of **optimistic execution**, allowing the blockchain to only evaluate smart contracts when there is fraud. This enables OVM computation to scale in the number of disputes as opposed to the number of transactions. + +### For Developers... + +The OVM is an EVM-based VM which supports optimistically executing EVM smart contracts on a layer 1 blockchain like Ethereum. It is structured in such a way that it is possible to verify individual steps of its computation on Ethereum mainnet. This allows the mainnet to enforce validity of state roots with fraud proofs in the layer 2 Optimistic Rollup chain. + +Each computational step is called a transition. These transitions can be evaluated off-chain as well as on-chain in an OVM [sandbox](https://en.wikipedia.org/wiki/Sandbox_%28computer_security%29) to ensure their validity. Through techniques similar to a technique called [stateless clients](https://ethresear.ch/t/the-stateless-client-concept/172) originally developed for Eth2, each transition's validity may be evaluated efficiently & in isolation. + +### Why not just use the EVM? + +Unfortunately the EVM is not structured in a way that allows you to spawn sandboxed subprocesses. Without sandboxing, we are unable to verify the validity of ORU transitions, and therefore are unable to build an ORU compatible with the EVM. + +Thankfully, the EVM is turing complete & therefore flexible enough for us to embed this sandbox functionality directly inside of it. By embedding the OVM inside of the EVM, we're able to take advantage of all of the great work on the EVM while adding this critical feature that we need for ORU. + diff --git a/packages/docs/protocol-specifications/ovm/codecopy.md b/packages/docs/protocol-specifications/ovm/codecopy.md new file mode 100644 index 0000000000000..60e5c575bf8ac --- /dev/null +++ b/packages/docs/protocol-specifications/ovm/codecopy.md @@ -0,0 +1,74 @@ +# CODECOPY Transpilation + +The opcode `CODECOPY` accepts `memOffset`, `codeOffset`, and `length` inputs from the stack, modifying the memory so that `memory[memOffset:memOffset + length] = code[codeOffset:codeOffset + length]`. Since we are by definition modifying the `code` of a contract by transpiling, there is no general way to handle pre-transpiled `CODECOPYs` since the impact on execution is dependent on how the `CODECOPY` was expected to be used. For Solidity, there are three ways in which `CODECOPY` is used: + +> 1. Constants which exceed 32 bytes in length are stored in the +> +> bytecode and `CODECOPY` ed for use in execution. \(if \<=32 bytes +> +> they are just `PUSHN` ed\) +> +> 2. All constructor logic for initcode is prefixed to the bytecode to +> +> be deployed, and this prefix runs \`CODECOPY\(suffixToDeploy\), ..., +> +> RETURN`during`CREATE\(2\)\` . +> +> 3. Constructor parameters are passed as bytecode and then are +> +> `CODECOPY` ed to memory before being treated like calldata. + +This document explains each of these cases and how they're handled transpilation-side. + +### Constants + +Constants larger than 32 bytes are stored in the bytecode and `CODECOPY` ed to access in execution. Initial investigation shows that the pattern which always occurs leading up to a `CODECOPY` for constants is: + +```text +... +PUSH2 // offset +PUSH1 // length +SWAP2 // where to shove it into memory +CODECOPY +... +``` + +With some memory allocation operations preceding the `PUSH, PUSH, SWAP...` which may also be standard \(haven't tested\). Some sample code which this example was pulled from can be found [this gist](https://gist.github.com/ben-chain/677457843793d7c6c7feced4e3b9311a) . + +To deal with constants, we still want to copy the correct constant--this will just be at a different index once we insert transpiled bytecode above it. So, we just increase the `codeOffset` input to `CODECOPY` in every case that a constant is being loaded into memory. Hopefully, all constants are appended to the end of a file so that we may simply add a fixed offset for every constant. + +### Returning deployed bytecode in `CREATE(2)` + +All constructor logic for initcode is prefixed to the bytecode to be deployed, and this prefix runs `CODECOPY(suffixToDeploy), ..., RETURN` during `CREATE(2)`. If constructor logic is empty \(i.e. no `constructor()` function specified in Solidity\) this prefix is quite simple but still exists. This `CODECOPY` simply puts the prefix into memory so that the deployed byetcode can be deployed. So, what we need to do is increase the `length` input both to `CODECOPY` and the `RETURN`. The `CODECOPY, RETURN` pattern seems to appear in the following format: + +```text +PUSH2 // codecopy's and RETURN's length +DUP1 // DUPed to use twice, for RETURN and CODECOPY both +PUSH2 // codecopy's offset +PUSH1 codecopy's destOffset +CODECOPY // copy +PUSH1 0 // RETURN offset +RETURN // uses above RETURN offset and DUP'ed length above +``` + +So by adding to the consumed bytes of the first `PUSH2` above, in accordance to the extra bytes added by transpilation, we make sure the correct length is both `CODECOPY`ed and `RETURN` ed. Note that, if we have constructor logic which gets transpiled, this will require modifying the `// codecopy's offset` line above as well. + +### Constructor Parameters + +Constructor parameters are passed as bytecode and then are `CODECOPY` ed to memory before being treated like calldata. This is because the EVM execution which is initiated by `CREATE(2)` does not have a calldata parameter, so the inputs must be passed in a different way. For more discussion, check out [this discussion](https://ethereum.stackexchange.com/questions/58866/how-does-a-contracts-constructor-work-and-load-input-values) on stack exchange. + +We handle this similarly to how we handle constants, by changing the `codeOffset` input appropriately. Both constants used in the constructor and constructor inputs are at the end of the file. + +The pattern it uses is: + +```text +... +[PC 0000000015] PUSH2: 0x01cf // should be initcode.length + deployedbytecode.length +[PC 0000000018] CODESIZE +[PC 0000000019] SUB // subtract however big the code is from the amount pushed above to get the length of constructor input +[PC 000000001a] DUP1 +[PC 000000001b] PUSH2: 0x01cf // should also be initcode.length + deployedbytecode.length +[PC 000000001e] DUP4 +[PC 000000001f] CODECOPY +``` + diff --git a/packages/docs/protocol-specifications/ovm/execution-manager.md b/packages/docs/protocol-specifications/ovm/execution-manager.md new file mode 100644 index 0000000000000..63e206877b402 --- /dev/null +++ b/packages/docs/protocol-specifications/ovm/execution-manager.md @@ -0,0 +1,103 @@ +# Execution Manager Overview + +The Execution Manager is technically just a smart contract running in a local EVM \(layer 2\) and available on Ethereum to evaluate fraud claims \(layer 1\), but in principle, it is much more. It _is_ the layer 2 EVM, and it allows our Optimistic Rollup implementation to generically support layer 1 smart contracts. + +## Motivation + +The [Unipig Demo](https://unipig.exchange/) showed that Optimistic Rollup is possible with custom contract code in both layer 1 and layer 2. Layer 1 contracts each need a custom state transition function that can be given a snapshot of the layer 2 state and a state transition to execute in order to evaluate if the layer 2 state transition was properly executed. A simple state transition function example would be transferring an ERC-20 token. The layer 1 token contract would need a function that takes in pre-state \(i.e. address balances, approvals, etc.\), evaluates a particular transition \(e.g. a transfer\), and computes the resulting state \(i.e. updated balances\). Needless to say, the logic to execute this state transition in layer 2 needed to be created as well. + +### To support generic smart contracts in layer 1... + +We need all state transitions for all possible contracts deployed to layer 2 to be generically calculable by layer 1. The EVM provides this functionality, but layer 1 runs on the EVM -- we need this to run on layer 1 \(_on the EVM_\). If we can create an EVM that can run _inside_ of the EVM, all standard EVM operations can be executed efficiently in this layer 2 EVM while also being generically verifiable in the case of fraud in layer 1 \(by calling the EVM within the layer 1 EVM\). + +### To support generic smart contracts in layer 2... + +The layer 2 EVM needs to be able to run all layer 1 smart contracts with no additional code created per contract. If layer 2 runs a layer-1-compatible EVM \(that, itself, can be run in layer 1\), this is achieved. + +We call this layer-1-compatible EVM that can run within the layer 1 EVM the OVM ExecutionManager Contract. + +## Design + +### Necessary Features + +Just like the EVM, the OVM ExecutionManager Contract must: + +* Handle all opcodes other than those deeply embedded in the layer 1 protocol (like COINBASE, DIFFICULTY, block NUMBER, BLOCKHASH\) +* Generically support smart contracts, including those that depend on and even create other smart contracts +* Serve as the entrypoint to all calls, transactions, and state modification +* Store all state created and modified by transaction and smart contract execution + +### Implementation + +As stated above, the Execution Manager is a smart contract that runs in a \[slightly modified\] local EVM\*. Below are its implementation details and reasoning behind its functionality. + +### Transaction Context & Re-implementing Opcodes + +Transactions that are run in layer 2 will necessarily have a different context than fraud proofs in layer 1. For instance, a fraud proof can only be submitted to layer 1 to dispute a layer 2 transaction some time after it has been executed. As such, opcodes like TIMESTAMP will _function_ the same \(it'll be the timestamp when the transaction was actually executed\), but the actual current time will not _be_ the same when executed in layer 1 vs challenged as fraudulent layer 2. + +![The Execution Manager](https://i.imgur.com/cOhmFRo.png) + +To handle this, the OVM ExecutionManager Contract implements these opcodes as functions. When a contract executing in layer 2 or a fraud proof executing in layer 1 needs to know the timestamp, it will call the OVM ExecutionManager Contract instead of accessing these layer-1-protocol-level opcodes directly. We have [transpilation tools](https://github.com/ethereum-optimism/optimism-monorepo/tree/37044e22125ed779c51d83d7491dc19fcd7bd1cf/packages/docs/protocol-specifications/ovm/protocol-specifications/ovm/transpiler.md) that take compiled layer 1 bytecode and swap out certain opcodes, like TIMESTAMP, for calls to our OVM ExecutionManager Contract. All contracts deployed to layer 2 must be transpiled accordingly. + +In our example, the sequencer that commits to a layer 2 transaction passes the timestamp at the time of execution to the OVM ExecutionManager Contract with the transaction to evaluate. They also specify the same timestamp in their rollup block that includes the transaction. This way, when the fraud proof is executed, the same timestamp from the rollup block will be set in the OVM ExecutionManager Contract prior to evaluating fraud so that the context that was committed to can be accessed correctly. + +### CALL + +There are many contextual differences between layer 1 and layer 2, so we won't go through all of them, but another important one to consider is that the addresses of the corresponding contracts will be different. All contracts in layer 2 are actually deployed by the OVM ExecutionManager Contract, but their addresses are created as if the caller deployed them. As such, all contract deployments go through the OVM ExecutionManager Contract, which maintains a map from OVM contract addresses \(as if the caller created them\) to EVM contract addresses \(the address of the contract that was actually deployed by the OVM ExecutionManager Contract\). This means that all CALL type opcodes must be transpiled to instead call the OVM ExecutionManager Contract so it may fill in the proper address for the call, as well as set other relevant context for the call's execution, like CALLER and ADDRESS. + +### SSTORE & SLOAD + +The last example to highlight is that SSTORE and SLOAD also need to be transpiled into calls to the OVM ExecutionManager Contract. Recall that one of the requirements is that the OVM ExecutionManager Contract needs to store all layer 2 state. This is so rollup blocks can commit to single pre-state and post-state roots and the fraud proof's pre- and post-state can be verified and executed through the OVM ExecutionManager Contract on layer 1 during fraud proofs. + +A list of transpiled opcodes and other transpilation details are available [here](./transpiler.md). + +## Example: A user trading ETH for BAT on Uniswap + +There are two main parts to this example: + +* User transaction calling the layer 2 Uniswap Contract +* The layer 2 Uniswap Contract calling the corresponding ERC-20 Contracts to update balances + +### Steps + +### Sequencer Handles Request + +1. It receives a signed transaction from the User calling the Uniswap BAT Exchange address's `ethToTokenTransferInput(...)` function. +2. It wraps this transaction's calldata in a call to the OVM ExecutionManager Contract's `executeCall(...)` function and sends the wrapped transaction. + +### OVM ExecutionManager Contract handles the transaction in executeCall\(...\) + +1. It receives the wrapped transaction, sets the transaction context (including timestamp, etc.), and calls the `ovmCALL(...)` opcode replacement function to execute the transaction. +2. Its `ovmCALL(...)` function sets the call-specific context (including the CALLER, the ADDRESS of the uniswap contract, etc.) +3. It looks up the EVM address of the Uniswap contract from the OVM address and CALLs the contract with the original transaction data. + +### Uniswap / BAT Contract interaction + +1. Uniswap determines the exchange rate based on how much BAT it has by calling the OVM ExecutionManager Contract's `ovmCALL(...)` function to call the layer 2 BAT ERC-20 contract's `balanceOf(...)` function. +2. The OVM ExecutionManager Contract temporarily updates all of the call context variables in `ovmCALL(...)` to properly reflect that the CALLER is the Uniswap contract, ADDRESS is the BAT address, etc. +3. The OVM ExecutionManager Contract calls the BAT contract and it properly returns the balance +4. The OVM ExecutionManager Contract restores the call context such that the CALLER is the original caller, the ADDRESS is the Uniswap contract, etc. +5. The OVM ExecutionManager Contract returns the result to the Uniswap contract. +6. The Uniswap contract then calls the BAT contract, through the OVM ExecutionManager Contract again, to actually execute the transfer of the calculated amount of BAT +7. The Uniswap contract makes a final call to the BAT contract, through the OVM ExecutionManager Contract, to transfer the WETH \(all ETH in the OVM is WETH\) +8. The Uniswap returns the number of tokens bought. +9. The OVM ExecutionManager Contract restores the original call context before the original call to the Uniswap contract and returns the result. + +### OVM ExecutionManager Contract handles the transaction in `executeCall(...)` \(continued\) + +1. It restores the original transaction context from before the transaction and returns the result + +### Sequencer Handles Request \(continued\) + +1. It gets the internal transaction hash as a result. +2. It stores a mapping from the original transaction hash to the internal transaction hash for future transaction lookup. +3. It returns the original transaction hash, in compliance with Web3, to the caller. + +### Not mentioned above: + +* Access of TIMESTAMP, ADDRESS, CALLER, etc. which are actually CALLs to the associated OVM ExecutionManager Contract function. +* Access of all storage, which is actually a CALL to the `ovmSLOAD(...)` OVM ExecutionManager Contract function. +* Storage modification, which is actually a CALL to the `ovmSSTORE(...)` OVM ExecutionManager Contract function. +* All other opcodes handled through the OVM ExecutionManager Contract. +* The layer 2 EVM will be run by the Sequencer that submits new layer 2 "blocks" to layer 1, validators who validate these blocks once submitted to layer 1, and any other interested party. Validation entails executing each individual state transition that is claimed to be valid by the Sequencer and ensuring that it is, in fact, valid (i.e. the resulting state from executing the state transition match the post-state claimed by the Sequencer). + diff --git a/packages/docs/protocol-specifications/ovm/jump-transpilation.md b/packages/docs/protocol-specifications/ovm/jump-transpilation.md new file mode 100644 index 0000000000000..38a7c5eee23b2 --- /dev/null +++ b/packages/docs/protocol-specifications/ovm/jump-transpilation.md @@ -0,0 +1,55 @@ +# JUMP Transpilation + +`JUMP` and `JUMPI` instructions are only allowed to jump onto destinations that are \(1\) occupied by a `JUMPDEST` opcode, and \(2\) are not inside PUSH data. We make sure we update the destinations we are `JUMP`ing to to account for the extra opcodes we are adding in by appending an assembly switch/case to all transpiled bytecode. + +## The Approach + +Here's how we deal with it, at a high level: 1. Create a map of all pre-transpilation `JUMPDEST` locations to post-transpilation `JUMPDEST` locations 2. Add some footer bytecode that acts as a `switch` statement, reading the pre-transpilation `JUMPDEST` location and `JUMP`ing to the associated post-transpilation `JUMPDEST` 3. Replace all `JUMP`s and `JUMPI`s to `JUMP` to the footer bytecode switch statement. + +### JUMP Transpilation Detail: Replacements + +Note: operations will be listed as `[operation]` -- `[resulting stack]` + +`JUMP`: + +* Expected Stack: `` +* Replacement: + * `PUSH32 ` -- ` ` + * `JUMP` -- `` +* Total Replacement: + * `JUMP` => `PUSH32 JUMP` + +`JUMPI`: + +* Expected Stack: ` ` +* Replacement: + * `SWAP1` -- ` ` + * `PUSH32 ` -- ` ` + * `JUMPI` -- `` + * `POP` -- `empty` +* Total Replacement: + * `JUMPI` => `SWAP1 PUSH32 JUMPI POP` + +`JUMPDEST`: + +* Expected Stack: `` \(footer switch statement results in 1 excess stack element\) +* Replacement: + * `JUMPDEST` -- `` + * `POP` -- `empty` +* Total Replacement: + * `JUMPDEST` => `JUMPDEST POP` + +### JUMP Transpilation Detail: Footer Switch Statement + +* Expected Stack: `` +* Single comparison: + * `DUP1` -- ` ` + * `PUSH32 ` -- ` ` + * `EQ` -- ` ` + * `PUSH32 ` -- ` ` + * `JUMPI` -- `` + +**Note on bytecode interpretation** + +Note that properly processing these conditions requires preprocessing the code; a particularly pathological use case is `PUSH2 JUMPDEST PUSH1 PUSH2 JUMPDEST PUSH1 PUSH2 JUMPDEST PUSH1 ...`, as this code has all `JUMPDEST`s invalid but an alternative piece of code equivalent to this but only with the leading `PUSH2` replaced with another op \(eg. `BALANCE`\) will have all `JUMPDESTS` valid. We appropriately deal with this, both in our transpiler and purity checker. + diff --git a/packages/docs/protocol-specifications/ovm/ovm-components.md b/packages/docs/protocol-specifications/ovm/ovm-components.md new file mode 100644 index 0000000000000..4b7003e584c29 --- /dev/null +++ b/packages/docs/protocol-specifications/ovm/ovm-components.md @@ -0,0 +1,36 @@ +# OVM Core Components + +The core functionality of the OVM is to run L2 transactions on L1 in such a way that they are "pure" or "deterministic"--that is, no matter what time in the future a dispute about them is triggered on layer 1, the output of the computation is the same--\*no matter what the state of the L1. + +To accomplish this, there are three critical components: + +1. **Execution Manager:** provides a safe, sandboxed execution environment for OVM contracts to run in. +2. **Safety Checker:** contract which can check that OVM contracts will not try to escape the Execution Manager's sandbox. +3. **Transpiler:** converts EVM contracts to safe OVM contracts which will not escape the execution sandbox. + +## Execution Manager + +The Execution Manager serves as a state and execution sandbox for executing OVM transactions and is where OVM transactions are always processed. Instead of directy accessing state variables like contract storage, the block number, and so on, OVM contracts may only access them through the execution manager. Thus, by configuring a particular state before running an OVM transaction through it, we are able to simulate how the transaction _was supposed to behave_ given the L2 state. + +For example, in an optimistic rollup fraud proof, if block `N+1` with transaction `T` was fraudulent, we are able to configure the execution manager with the previous `stateN` \(`executionManager.setContext(stateN)`\), call `executionManager.executeTransaction(T)`, and compare the output to the fraudulent `stateN+1`. + +The execution manager interfaces with "code contracts," which are EVM contracts that can only use the OVM's container interface. For instance, in L1, the EVM opcode `SSTORE(...)` is used during an ERC20 transfer to update the sender and recipients' balance. In the OVM, the ERC20 code contract instead calls `executionManager.ovmSSTORE(...)` to update the OVM's virtualized state instead. + +![The Execution Manager](https://i.imgur.com/9eMuXwc.png) + +The execution manager contract can be found [here](https://github.com/ethereum-optimism/optimism-monorepo/blob/master/packages/ovm/src/contracts/ExecutionManager.sol). + +## Safety Checker + +To ensure that the execution of an OVM transaction is deterministic for a given sandbox state, we must enforce that **only** the container interface described above is used. To accomplish this, we have a "purity checker." The purity checker analyzes the low-level assembly bytecode of an EVM contract to tell the execution manager whether the code conforms to the OVM interface. If it does not, then the execution manager does not allow such a contract to be created or used in a fraud proof. + +![The Safety Checker](https://i.imgur.com/JYKNqNC.png) + +The safety checker contract can be found [here](https://github.com/ethereum-optimism/optimism-monorepo/blob/master/packages/ovm/src/contracts/SafetyChecker.sol) + +## Transpiler + +Because smart contracts are not normally compiled to comply with any container interface, we have a transpiler which takes low-level EVM assembly, detects the usage of any stateful opcodes, and converts them into calls to the relevant Execution Manager method. There's a lot more that goes into doing that, which you can read about in the following section. + +Code for the transpiler can be found [here](https://github.com/ethereum-optimism/optimism-monorepo/tree/master/packages/rollup-dev-tools/src/tools/transpiler). + diff --git a/packages/docs/protocol-specifications/ovm/transpiler.md b/packages/docs/protocol-specifications/ovm/transpiler.md new file mode 100644 index 0000000000000..d8178ebff85fe --- /dev/null +++ b/packages/docs/protocol-specifications/ovm/transpiler.md @@ -0,0 +1,95 @@ +# Transpilation Overview + +This pages provides a quick reference which discusses how every EVM opcode is handled in the transpilation process. There are three classes of opcodes: + +1. Pure opcodes which do not need to be modified. +2. Replaced opcodes which are substituted with other bytecode by the transpiler. +3. Banned opcodes which are not replaced and simply disallowed. + +## Pure Opcodes + +The following opcodes perform stack operations which are constant in terms of L1/L2 state, and do not require modification: + +* Arithmetic/pure-math opcodes: + * `ADD, MUL, SUB, DIV, SDIV, MOD, SMOD, ADDMOD, MULMOD, EXP, SIGNEXTEND, LT, GT, SLT, SGT, EQ, ISZERO, AND, OR, XOR, NOT, BYTE, SHL, SHR, SAAR, SHA3` +* "Pure" code execution operations: + * `PUSH1....PUSH32, DUP1...DUP16, SWAP1...SWAP16, POP, LOG0...LOG4, STOP, REVERT, RETURN, PC, GAS, JUMPDEST*` + + **NOTE**: In practice, `JUMPDEST`s are modified, but not "transpiled away" like the impure opcodes. See JUMP transpilation [section](./jump-transpilation.md) for more details. +* "Pure" memory modifying operations: + * `MLOAD, MSTORE, MSTORE8, MSIZE` +* Permitted execution-context-dependent operations: + * `CALLVALUE, CALLDATALOAD, CALLDATASIZE, CALLDATACOPY, CODESIZE, RETURNDATASIZE, RETURNDATACOPY` + + **Note**: `CALLVALUE` will always be 0 because we enforce that all `CALL` s always pass 0 in our purity checking. + +## Replaced Opcodes + +The following opcodes need to be dealt with at transpilation time to work with the Execution Manager. Note: their transpiled versions may modify memory and/or the stack in ways their non-transpiled versions would not, but at the end of execution, the resulting memory and stack are guaranteed to be the same as they otherwise would be. \#\#\# Non-memory-utilizing Opcodes These opcodes do not modify memory over the course of execution but do need to be transpiled to be compatible with the Execution Manager. They all utilize a bytecode replacement function, named `callContractWithStackElementsAndReturnWordToStack(...)` in the transpiler, that calls the Execution Manager to fulfill their logic. This function passes any stack elements consumed by the opcode as calldata to the associated Execution Manager function and pushes the result to the stack. + +| Opcode | Description | Num Stack Arguments to Pass | Num Stack Elements Returned | +| :--- | :--- | :--- | :--- | +| `ADDRESS` | Returns the address of the currently execution contract. Needs to be modified since on L1 this would be the code contract's address, but on L2 it will be an OVM address. | 0 | 1 | +| `CALLER`\* | This is `msg.sender` in solidity. Needs to be modified since on L1 this would be the Execution Manager's address, but on L2 it is meant to be an OVM address. | 0 | 1 | +| `EXTCODESIZE` | This gets the size of an external contract's code. Needs to be modified on L2 since it is meant to accept an OVM address which doesn't exist on L1. | 1 \(`addr`\) | 1 | +| `EXTCODEHASH` | This gets the size of an external contract's code. Needs to be modified on L2 since it is meant to accept an OVM address which doesn't exist on L1. | 1 \(`addr`\) | 1 | +| `TIMESTAMP`\*\* | This gets the timestamp of the current block \(in Solidity: `block.timestamp`\). Needs to be transpiled to the `ovmTIMESTAMP`. | 0 | 1 | +| `SLOAD` | This gets the value of a storage slot at the first stack input \(`key`\). Needs to be transpiled to `ovmSLOAD` instead. | 1 \(`key`\) | 1 | +| `SSTORE` | This gets the value of a storage slot at the first stack input \(`key`\). Needs to be transpiled to `ovmSSTORE` instead. | 2 \(`key, value`\) | 0 | + +\* Note 1: we are currently using metatransactions, having no EOAs, and assuming all transactions are handled with account abstraction. Because of this, at the initial entry point of a rollup transaction, `CALLER` will revert the transaction--unlike the EVM's usual behavior. + +\*\* Note 2: The timestamp will correspond to the timestamp of the ORU block, and not any L1 Ethereum block. + +### Memory-reading opcodes + +These opcodes read from, but do not write to, the current execution's memory, and also need to be transpiled to work in the context of the OVM. For now, there are two such opcodes: `CREATE` and `CREATE2`, whose stack inputs specify the memory range of the initcode used to deploy the new contract. There are two functions which we use to generate replacement functions in Typescript: `getCREATEReplacement(...)` and `getCREATE2Replacement(...)`. They work by prepending `calldata` \(`methodId`, as well as `salt` if `CREATE2`\) to the existing memory of the `initcode`, calling the associated function in Execution Manager, and pushing the `returndata` onto the stack. + +### Memory-writing opcodes + +These opcodes write to, but do not require reading from, the current execution's memory, and also need to be transpiled to work in the context of the OVM. For now, there is only one such opcode: `EXTCODECOPY`. Its replacement function in typescript is called `getEXTCODECOPYReplacement(...)`. The transpiled bytecode passes the `methodId` and necessary stack inputs \(namely, the `addr` whose code is desired to copy\) as `calldata` to the associated Execution Manager function, and uses the memory modification inputs in the original stack as the `retOffset` and `retLength` for the transpiled `CALL` to the Execution Manager. Thus the Execution Manager's returned copy of the code modifies the memory as expected. + +### `CALL`-type Opcodes + +To replace Call-type opcodes, we have to pass an existing slice of `calldata` at `argOffset, argLength`, along with the `methodId` and `target` address. The typescript function `getCallTypeReplacement(...)` handles these replacements, dynamically prepending the `methodId` and `addr` stack input to the existing `calldata` memory, updating the CALL's `argOffset` and `argLength` as necessary and routing the CALL to the appropriate Execution Manager function. Because `STATICCALL` and `DELEGATECALL` do not have a `value` stack input, the function accepts as an argument `stackPositionOfCallArgsMemOffset: number` to locate the memory parameters. + +| Opcode | `stackPositionOfCallArgsMemOffset` | +| :--- | :--- | +| `CALL` | 3 | +| `STATICCALL` | 2 | +| `DELEGATECALL` | 2 | + +### Special cases: `CODECOPY` and `JUMP` s + +There are two functions which are "Pure code execution operations" just like `CODESIZE`, `REVERT`, etc., however, they are used by the Solidity compiler in ways which the transpilation process affects, and need to be dealt with in the transpiler. + +* Because we are inserting bytecode, we are changing the index of every `JUMPDEST` proceeding each insertion operation. This means our `JUMP` and `JUMPI` values need to be transpiled or they will fail/go to the wrong place. We handle this by making all `JUMP` s go to new bytecode that we append at the end that simply contains a mapping from untranspiled `JUMPDEST` bytecode location to transpiled `JUMPDEST` bytecode location. The logic finds the new location and `JUMP` s to it. See the \["JUMP Modification" page\]\([https://github.com/op-optimism/optimistic-rollup/wiki/JUMP-Transpilation](https://github.com/op-optimism/optimistic-rollup/wiki/JUMP-Transpilation)\) for more details. +* The opcode `CODECOPY` works fine, in principle, in our code contracts, as its effect on execution is independent of L1 state. However, because that code itself is modified by transpilation, we need to deal with it in the transpiler. See our `CODECOPY` [section](https://github.com/ethereum-optimism/optimism-monorepo/tree/088846ebf6e09fbd2078c69031eb4ee3e43b8248/packages/docs/protocol-specifications/ovm/codecopy.html) for how we handle these modifications. + +## Banned Opcodes + +The remaining opcodes are explicitly banned, either because we don't yet support them, or do not plan to/it's impossible. + +### Opcodes which could later be implemented + +These opcodes are banned simply because we don't want to support them currently. + +### ETH-native Value + +We have made the decision for now not to use native ETH, and instead do everything with wrapped ETH \(WETH\). Note: `CALLVALUE` is actually able to be whitelisted, because our Purity Checker enforces that all Calls are made with a value of 0. Contracts are welcome to use msg.value, it will just always return 0. This means that the following opcodes are banned, not just transpiled: - `BALANCE` -- gets `address(this).balance` While not a ban, another note here is that all `value`-related inputs to other opcodes like `CREATE` or `CALL` are overridden to `0` by their transpiled counterparts. We do have good inline documentation for how a native `value` could be added if needed. Another option is we could even transpile the native ETH opcodes to use `WETH` instead. TBD. + +### Others + +* `NUMBER` -- the relationship between `block.number` in L1 and L2 is unclear so we've banned. In the future, we could even transpile toreturn `timestamp/avg. blocktime` but unclear if this is a good idea. +* `GASPRICE` -- Before we implement proper gas metering ramifications, we shouldn't transpile anything here. Down the line, we may need to and can potentially add it depending on how we handle. +* `GASLIMIT` -- see `GASPRICE`, same arguments apply. +* `BLOCKHASH` -- in theory the previous state roots can be accessible to the OVM, but because it is EXTREMELY manipulable by the single-party sequencer, and usually used as a bad source of randomness, we'll ban for now. Down the line, we can expose historic L1 blockhashes for this purpose, but that's a lot of work \(and still a bad idea for randomness even on L1!\). +* `ORIGIN` -- see note on `CALLER` and metatransactions. In the future, could transpile to the metatransaction library's standard, once we're more confident in that approach/choice. +* `CALLCODE` -- This opcode was a failed implementation of `DELEGATECALL`. Deprecated, extremely low priority to support. +* `SELFDESTRUCT` -- This opcode is currently unsupported, and we also will not be able to handle it's default functionality to send all ETH of self destructed contract to a designated address + +### "Impossible" opcodes + +* `COINBASE` -- since we don't have inflation in L2 +* `DIFFICULTY` -- since there is no sense of difficulty in L2. An analogous value in L2 is actually the MEVA price, but it's not so analogous that transpiling would make any sense. + diff --git a/packages/docs/src/core/_static/images/optipus.png b/packages/docs/src/core/_static/images/optipus.png deleted file mode 100644 index efee2efe7d29e..0000000000000 Binary files a/packages/docs/src/core/_static/images/optipus.png and /dev/null differ diff --git a/packages/docs/src/core/conf.py b/packages/docs/src/core/conf.py deleted file mode 100644 index 09a288ecbb972..0000000000000 --- a/packages/docs/src/core/conf.py +++ /dev/null @@ -1,197 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Configuration file for the Sphinx documentation builder. -# -# This file does only contain a selection of the most common options. For a -# full list see the documentation: -# http://www.sphinx-doc.org/en/master/config - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -# -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) - - -# -- Project information ----------------------------------------------------- - -project = '@eth-optimism' -copyright = '2020, Optimism' -author = 'Optimism' - -# The short X.Y version -version = '' -# The full version, including alpha/beta/rc tags -release = '' - - -# -- General configuration --------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -# -# needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.intersphinx', - 'sphinx.ext.todo', - 'sphinx.ext.coverage', - 'sphinx.ext.mathjax', - 'sphinx.ext.ifconfig', -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# -# source_suffix = ['.rst', '.md'] -source_suffix = '.rst' - -# The master toctree document. -master_doc = 'index' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = None - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = None - - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -# -html_theme = 'alabaster' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# -# html_theme_options = {} - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Custom sidebar templates, must be a dictionary that maps document names -# to template names. -# -# The default sidebars (for documents that don't match any pattern) are -# defined by theme itself. Builtin themes are using these templates by -# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', -# 'searchbox.html']``. -# -# html_sidebars = {} - - -# -- Options for HTMLHelp output --------------------------------------------- - -# Output file base name for HTML help builder. -htmlhelp_basename = 'optimismcoredoc' - - -# -- Options for LaTeX output ------------------------------------------------ - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, 'optimismcore.tex', '@eth-optimism Documentation', - 'Optimism', 'manual'), -] - - -# -- Options for manual page output ------------------------------------------ - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'optimismcore', '@eth-optimism Documentation', - [author], 1) -] - - -# -- Options for Texinfo output ---------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - (master_doc, 'optimismcore', '@eth-optimism Documentation', - author, 'optimismcore', 'One line description of project.', - 'Miscellaneous'), -] - - -# -- Options for Epub output ------------------------------------------------- - -# Bibliographic Dublin Core info. -epub_title = project - -# The unique identifier of the text. This can be a ISBN number -# or the project homepage. -# -# epub_identifier = '' - -# A unique identification for the text. -# -# epub_uid = '' - -# A list of files that should not be packed into the epub file. -epub_exclude_files = ['search.html'] - - -# -- Extension configuration ------------------------------------------------- - -# -- Options for intersphinx extension --------------------------------------- - -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'https://docs.python.org/': None} - -# -- Options for todo extension ---------------------------------------------- - -# If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = True - -# Use RTD theme. -import sphinx_rtd_theme -html_theme = 'sphinx_rtd_theme' -html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] diff --git a/packages/docs/src/core/index.rst b/packages/docs/src/core/index.rst deleted file mode 100644 index dda8bb118bdfe..0000000000000 --- a/packages/docs/src/core/index.rst +++ /dev/null @@ -1,36 +0,0 @@ -=========================== -@eth-optimism Documentation -=========================== -Hello and welcome to the documentation for Optimism's OVM and Optimistic rollup. - -Our ``@eth-optimism`` module is the set of core modules that the OVM and our L2 implementations use. - -.. raw:: html - - The Optimism Optipus - -You can find all of our source code here on `our monorepo`_. - -.. toctree:: - :maxdepth: 2 - :caption: Developer Documentation - - src/what-is-the-ovm - src/hello-world - src/integrating-tests - src/troubleshooting - src/limitations - - -.. toctree:: - :maxdepth: 2 - :caption: Specifications and Details - - src/spec/overview - src/spec/execution-manager - src/spec/transpilation-details - src/spec/jump-transpilation - src/spec/codecopy - src/spec/design - -_`our monorepo`: https://github.com/ethereum-optimism/optimism-monorepo \ No newline at end of file diff --git a/packages/docs/src/core/src/hello-world.rst b/packages/docs/src/core/src/hello-world.rst deleted file mode 100644 index de1c740aa37d1..0000000000000 --- a/packages/docs/src/core/src/hello-world.rst +++ /dev/null @@ -1,119 +0,0 @@ -========================================================= -Getting Started with the OVM: Simple ERC20 Token Tutorial -========================================================= - -Hi there! Welcome to our OVM ERC20 tutorial. - -If you're interested in writing your first L2-compatible smart contract, you've come to the right place! This tutorial will cover how to move an existing contract and its tests into the wonderful world of L2. - -Set up -====== - -To start out, clone this example repo as a starting point. - -.. code-block:: none - - git clone https://github.com/ethereum-optimism/ERC20-Example.git - -Now, enter the repository - -.. code-block:: none - - cd ERC20-Example - -Install all dependencies - -.. code-block:: none - - yarn install - -This project represents a fresh, non-OVM ERC20 contract example. Feel free to stop here and have a quick look at the contract and tests. - -In this tutorial, we'll cover the steps required to bring it into the world of L2. First, let's make sure all of our tests are running in our normal Ethereum environment: - -.. code-block:: none - - yarn test - -You should see all of the tests passing. We're now ready to convert the project to build and test in an OVM environment! - -Configuring the Transpiler -========================== - -First, we need to configure ``ethereum-waffle`` (which is an alternative to `truffle`) to use our new transpiler-enabled Solidity compiler. To do this, edit the ``waffle-config.json`` and replace it with: - -.. code-block:: none - - { - "sourcesPath": "./contracts", - "targetPath": "./build", - "npmPath": "./node_modules", - "solcVersion": "./node_modules/@eth-optimism/solc-transpiler", - "compilerOptions": { - "outputSelection": { - "*": { - "*": ["*"] - } - }, - "executionManagerAddress": "0xA193E42526F1FEA8C99AF609dcEabf30C1c29fAA" - } - } - -Using the Full Node -=================== - -To use the OVM to run our tests, open the test file at ``test/erc20.spec.js``. We can import the OVM-ified versions of `getWallets`, `createMockProvider`, and `deployContract` near the top of the test file: - -.. code-block:: none - - const { createMockProvider, getWallets, deployContract } = require('@eth-optimism/rollup-full-node') - -Now remove the duplicated imports from `ethereum-waffle`, replacing the import on `Line 2` with: - -.. code-block:: none - - const {solidity} = require('ethereum-waffle'); - - -Our imports at the top of the file should now look like: - -.. code-block:: none - - const {use, expect} = require('chai'); - const {solidity} = require('ethereum-waffle'); - const {createMockProvider, getWallets, deployContract } = require('@eth-optimism/rollup-full-node') - const ERC20 = require('../build/ERC20.json'); - - -We're almost there! After we've run our tests on the OVM, we need to stop our OVM server. We're going to add a single line of code after our `before()` hook in order to close our OVM Server after our tests run: - -.. code-block:: none - - before(async () => { - provider = await createMockProvider() - const wallets = getWallets(provider) - wallet = wallets[0] - walletTo = wallets[1] - }) - - //ADD TO SUPPORT OVM - after(() => {provider.closeOVM()}) - -Running the New Tests -===================== - -Great, we're ready to go! Now you can try to re-run your tests on top of the OVM with - -.. code-block:: none - - yarn test - -Wasn't that easy? -================= - -The OVM provides a fresh new take on layer 2 development: it's identical to layer 1 development. No hoops, no tricks--the Ethereum you know and love, ready to scale up with L2. For more info on our progress and what's going on behind the scenes, you can follow us on `twitter`_ and `check out our docs`_! - - - -.. _`twitter`: https://twitter.com/optimismPBC -.. _`check out our docs`: https://docs.optimism.io \ No newline at end of file diff --git a/packages/docs/src/core/src/integrating-tests.rst b/packages/docs/src/core/src/integrating-tests.rst deleted file mode 100644 index 929d620449e2a..0000000000000 --- a/packages/docs/src/core/src/integrating-tests.rst +++ /dev/null @@ -1,135 +0,0 @@ -=================== -Integrating the OVM -=================== -If you're interested in testing your contracts running on the OVM, then you're in the right place! Please note that the OVM is in alpha and, as such, you'll probably find some bugs. If you do, please drop us a line or open a github issue! - -There are two steps you need to take to get your contracts running on the ovm: using ``@eth-optimism/solc-transpiler`` in place of ``solc`` so your contracts can be transpiled into OVM-compatible versions, and using ``@eth-optimism/rollup-full-node`` as your web3 provider. - -For reference, example integrations for both Truffle and Waffle can be found `in our monorepo`_ . - -Integrating the OVM Transpiler -============================== - -Installing ------------ - -Both ``truffle`` and ``ethereum-waffle`` allow you to specify a custom replacement for ``solc-js``. First, you'll need to install ``@eth-optimism/solc-transpiler``: - -.. code-block:: none - - yarn add @eth-optimism/solc-transpiler && yarn install - -or - -.. code-block:: none - - npm install --save @eth-optimism/solc-transpiler - - -``@eth-optimism/solc-transpiler`` Accepts the same compiler options as as ``solc``, with one additional option, ``executionManagerAddress``. The Execution Manager is a smart contract implementing the OVM's containerization functionality. If you are using an unmodified ``rollup-full-node``, the default execution manager address is ``0xA193E42526F1FEA8C99AF609dcEabf30C1c29fAA``. More info on the execution manager can be found here. (link) - -Using With Waffle ------------------ - -To use the transpiler with ``ethereum-waffle``, set the ``solc.version`` configuration to ``""@eth-optimism/solc-transpiler"`` and ``compilerOptions.executionManagerAddress`` to ``"0xA193E42526F1FEA8C99AF609dcEabf30C1c29fAA"``. - -example waffle-config.json: - -.. code-block:: none - - { - "solcVersion": "@eth-optimism/solc-transpiler", - "compilerOptions": { - "executionManagerAddress": "0xA193E42526F1FEA8C99AF609dcEabf30C1c29fAA" - } - } - -Using With Truffle ------------------- - -To use the transpiler with Truffle, set truffle's ``compilers.solc.version`` configuration to ``@eth-optimism/solc-transpiler``, and configure the ``EXECUTION_MANAGER_ADDRESS`` environment variable. - -Currently, Truffle does not provide a clean way to use custom chain IDs, so we have created the ``@eth-optimism/ovm-truffle-provider-wrapper`` package to seamlessly wrap your provider of choice to handle this. -Note: you will also need to include ``@eth-optimism/rollup-full-node`` as a dependency if you would like to run a full node locally (or use ``ProviderWrapper.wrapProviderAndStartLocalNode(...)``). - -example truffle-config.json: - -.. code-block:: json - - const HDWalletProvider = require('truffle-hdwallet-provider'); - const ProviderWrapper = require("@eth-optimism/ovm-truffle-provider-wrapper"); - const mnemonic = 'candy maple cake sugar pudding cream honey rich smooth crumble sweet treat'; - - // Set this to the desired Execution Manager Address -- required for the transpiler - process.env.EXECUTION_MANAGER_ADDRESS = process.env.EXECUTION_MANAGER_ADDRESS || "0xA193E42526F1FEA8C99AF609dcEabf30C1c29fAA" - - module.exports = { - /** - * Note: this runs the OVM full node for the duration of the tests at `http://127.0.0.1:8545` - * - * To run tests: - * $ truffle test ./truffle-tests/test-erc20.js --config truffle-config-ovm.js - */ - networks: { - test: { - network_id: 108, - provider: function() { - return ProviderWrapper.wrapProviderAndStartLocalNode(new HDWalletProvider(mnemonic, "http://127.0.0.1:8545/", 0, 10)); - }, - gasPrice: 0, - gas: 9000000, - }, - live_example: { - provider: function () { - return ProviderWrapper.wrapProvider(new HDWalletProvider(mnemonic, "http://127.0.0.1:8545/", 0, 10)); - }, - gasPrice: 0, - gas: 9000000, - }, - }, - - // Set default mocha options here, use special reporters etc. - mocha: { - timeout: 100000 - }, - - compilers: { - solc: { - // Add path to the solc-transpiler - version: "@eth-optimism/solc-transpiler", - } - } -} - -As you can see in the above comments, you must spin up the rollup full node before running truffle tests. To do this, with ``@eth-optimism/rollup-full-node`` installed, you can run: - -.. code-block:: bash - - node rollup-full-node/build/src/exec/fullnode.js - -Currently, ``rollup-full-node`` breaks Truffle's ``gasLimit`` and ``blockGasLimit``. To avoid this, you can set both to ``undefined`` where they are used. - -Integrating the OVM Full Node -============================== - -To use your transpiled contracts, you need to use ``@eth-optimism/rollup-full-node`` as your web3 provider. To do this, make sure it's installed: - -.. code-block:: none - - yarn add @eth-optimism/rollup-full-node && yarn install - -or - -.. code-block:: none - - npm install --save @eth-optimism/rollup-full-node - -To get your provider and some wallets: - -.. code-block:: javascript - - const RollupFullNode = require("@eth-optimism/rollup-full-node") - const provider = RollupFullNode.getMockProvider() - const wallets = RollupFullNode.getWallets(provider) - -.. _`in our monorepo`: https://github.com/ethereum-optimism/optimism-monorepo/tree/master/packages/examples \ No newline at end of file diff --git a/packages/docs/src/core/src/limitations.rst b/packages/docs/src/core/src/limitations.rst deleted file mode 100644 index ca330dcbf7bd6..0000000000000 --- a/packages/docs/src/core/src/limitations.rst +++ /dev/null @@ -1,21 +0,0 @@ -=========== -Limitations -=========== - -Some features of the Ethereum are not yet implemented, or just don't make sense to have, in the OVM. This page documents some of those differences. - -No Native ETH -------------- -On L1, sending/recieving ETH is a special opcode different from ERC20s. This often leads to developers having to implement their contract functionality twice. For the OVM, we decided to eliminate this complexity and enforce that ETH only exist as an ERC20 (WETH). We think this will make developers' lives easier--but if you feel strongly otherwise, reach out and let us know! - -Block Number -------------- -Many L2 constructions, including our rollup implementation, support instant, real-time transactions. One way this manifests is that ``block.number`` is not easily interpretable or specified in the OVM. Instead, contracts can use ``block.timestamp`` (though right now, this is not implemented so it stays at 0.) - -Solidity libraries -------------- -Our transpiler does not currently support the usage of libraries in solidity -- this is one of the next things on our chopping block, so stay tuned! In the meantime you can convert the libs into a regular smart contract. - -Parent/Child chain communication -------------- -Communication between L1 and L2, also known as deposits and withdrawals, are not yet implemented in the OVM. Stay tuned for more on this! \ No newline at end of file diff --git a/packages/docs/src/core/src/spec/codecopy.rst b/packages/docs/src/core/src/spec/codecopy.rst deleted file mode 100644 index 93c7b951bf6bf..0000000000000 --- a/packages/docs/src/core/src/spec/codecopy.rst +++ /dev/null @@ -1,69 +0,0 @@ -=================================== -``CODECOPY`` in Transpiled Bytecode -=================================== - -The opcode ``CODECOPY`` accepts ``memOffset``, ``codeOffset``, and ``length`` inputs from the stack, modifying the memory so that ``memory[memOffset:memOffset + length] = code[codeOffset:codeOffset + length]``. Since we are by definition modifying the ``code`` of a contract by transpiling, there is no general way to handle pre-transpiled ``CODECOPYs`` since the impact on execution is dependent on how the ``CODECOPY`` was expected to be used. For Solidity, there are three ways in which ``CODECOPY`` is used: - - 1. Constants which exceed 32 bytes in length are stored in the bytecode and ``CODECOPY`` ed for use in execution. (if <=32 bytes they are just ``PUSHN`` ed) - 2. All constructor logic for initcode is prefixed to the bytecode to be deployed, and this prefix runs ``CODECOPY(suffixToDeploy), ..., RETURN`` during ``CREATE(2)`` . - 3. Constructor parameters are passed as bytecode and then are ``CODECOPY`` ed to memory before being treated like calldata. - -This document explains each of these cases and how they're handled transpilation-side. - -Constants -========= - -Constants larger than 32 bytes are stored in the bytecode and ``CODECOPY`` ed to access in execution. Initial investigation shows that the pattern which always occurs leading up to a ``CODECOPY`` for constants is: - -.. code-block:: none - - ... - PUSH2 // offset - PUSH1 // length - SWAP2 // where to shove it into memory - CODECOPY - ... - -With some memory allocation operations preceding the ``PUSH, PUSH, SWAP...`` which may also be standard (haven't tested). Some sample code which this example was pulled from can be found `this gist`_ . - -To deal with constants, we still want to copy the correct constant--this will just be at a different index once we insert transpiled bytecode above it. So, we just increase the ``codeOffset`` input to ``CODECOPY`` in every case that a constant is being loaded into memory. Hopefully, all constants are appended to the end of a file so that we may simply add a fixed offset for every constant. - -Returning deployed bytecode in ``CREATE(2)`` -============================================ - -All constructor logic for initcode is prefixed to the bytecode to be deployed, and this prefix runs ``CODECOPY(suffixToDeploy), ..., RETURN`` during ``CREATE(2)``. If constructor logic is empty (i.e. no ``constructor()`` function specified in Solidity) this prefix is quite simple but still exists. This ``CODECOPY`` simply puts the prefix into memory so that the deployed byetcode can be deployed. So, what we need to do is increase the ``length`` input both to ``CODECOPY`` and the ``RETURN``. The ``CODECOPY, RETURN`` pattern seems to appear in the following format: - -.. code-block:: none - - PUSH2 // codecopy's and RETURN's length - DUP1 // DUPed to use twice, for RETURN and CODECOPY both - PUSH2 // codecopy's offset - PUSH1 codecopy's destOffset - CODECOPY // copy - PUSH1 0 // RETURN offset - RETURN // uses above RETURN offset and DUP'ed length above - -So by adding to the consumed bytes of the first ``PUSH2`` above, in accordance to the extra bytes added by transpilation, we make sure the correct length is both ``CODECOPY``ed and ``RETURN`` ed. Note that, if we have constructor logic which gets transpiled, this will require modifying the ``// codecopy's offset`` line above as well. - -Constructor Parameters -====================== - -Constructor parameters are passed as bytecode and then are ``CODECOPY`` ed to memory before being treated like calldata. This is because the EVM execution which is initiated by ``CREATE(2)`` does not have a calldata parameter, so the inputs must be passed in a different way. For more discussion, check out `this discussion`_ on stack exchange. - -We handle this similarly to how we handle constants, by changing the ``codeOffset`` input appropriately. Both constants used in the constructor and constructor inputs are at the end of the file. - -The pattern it uses is: - -.. code-block:: none - - ... - [PC 0000000015] PUSH2: 0x01cf // should be initcode.length + deployedbytecode.length - [PC 0000000018] CODESIZE - [PC 0000000019] SUB // subtract however big the code is from the amount pushed above to get the length of constructor input - [PC 000000001a] DUP1 - [PC 000000001b] PUSH2: 0x01cf // should also be initcode.length + deployedbytecode.length - [PC 000000001e] DUP4 - [PC 000000001f] CODECOPY - -.. _`this gist`: https://gist.github.com/ben-chain/677457843793d7c6c7feced4e3b9311a -.. _`this discussion`: https://ethereum.stackexchange.com/questions/58866/how-does-a-contracts-constructor-work-and-load-input-values \ No newline at end of file diff --git a/packages/docs/src/core/src/spec/design.rst b/packages/docs/src/core/src/spec/design.rst deleted file mode 100644 index 1073c9aa8f91c..0000000000000 --- a/packages/docs/src/core/src/spec/design.rst +++ /dev/null @@ -1,156 +0,0 @@ -========================== -Notes on Design Philosophy -========================== - -******** -Overview -******** - -This diagram depicts a simple developer enviornment interacting with a Rollup node using the MVOVM. - -!high-level-ovm (2) (1)](https://user-images.githubusercontent.com/706123/70545643-e78cb480-1b3b-11ea-8562-59e7d3e23b0b.png) -( Editable Version -- https://drive.google.com/open?id=1iF2gvJut3LU1NCfcJLn7Jh_Cm0PZIwTZ ) - -Components -********** - -- Local Solidity test suite - - Imports transpiler -- Rollup Fullnode - - Contains the OVM - - MVOVM uses an Ethereum node on the backend - - (Stateful) [Execution Manager](https://github.com/op-optimism/optimistic-rollup/wiki/The-Execution-Manager) -- Ethereum fraud contracts - - Stateless Execution Manager - -********************************************************** -Stateful (off-chain) vs Stateless (on-chain) State Manager -********************************************************** - -There are two settings in which we will be executing transactions against our VM: - -1. Off-chain to calculate the current state of the rollup chain; and -2. On-chain to prove a fraudulent `state root `_. - -Both cases are identical except for one key detail: _off-chain we have access to the full state, while on-chain we only have access to the state we need to compute the result of the transaction_. This difference means that state access must be handled slightly differently between the two implementations; however, we should keep the two implementations as similar as possible to reduce the risk of bugs. - -Stateless Clients -***************** - -This design comes from the work on stateless clients `introduced by Vitalik `_. - -Stateless clients evalute state transitions with only a subset of the full state. Every storage slot & contract code which is touched during the execution of the smart contract must be stored locally to evaluate the transition. If all touched state is stored, a stateless client can evaluate the validity of a transition as well as calculate the resulting state root. - -The stateless client allows us to verify a single state transition in isolation--exactly what is required for a fraud proof. Fraud proofs in ORU cannot hold all state because because then we lose the ORU scalability in the case of fraud. Instead of holding all the state, we can use a stateless client! - -Stateful (off-chain) State Manager -********************************** - -Off-chain there is no problem running our OVM with all of the ORU state. This behaves exactly like an Ethereum fullnode. - -... TODO ... - -Stateless (on-chain) State Manager -********************************** - -... TODO ... - -********************************************** -L2_CONTEXT (aka global variables to transpile) -********************************************** - -Can we add fields to "msg"? e.g. have msg.queueOrigin? - -A: Don't think so. looks like ``msg.value`` has its own assembly code of ``callvalue`` for example. - -Block and Transaction Properties -******************************** -`(Source) `_ - -- ``blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent, excluding current, blocks -- ``block.coinbase`` (``address payable``): current block miner's address -- ``block.difficulty`` (``uint``): current block difficulty -- ``block.gaslimit`` (``uint``): current block gaslimit -- ``block.number`` (``uint``): current block number -- ``block.timestamp`` (``uint``): current block timestamp as seconds since unix epoch -- ``gasleft() returns (uint256)``: remaining gas -- ``msg.data`` (``bytes calldata``): complete calldata -- ``msg.sender`` (``address payable``): sender of the message (current call) -- ``msg.sig`` (``bytes4``): first four bytes of the calldata (i.e. function identifier) -- ``msg.value`` (``uint``): number of wei sent with the message - - turns into assembly: `callvalue`([source](https://ethereum.stackexchange.com/a/47476)) -- ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``) -- ``tx.gasprice`` (``uint``): gas price of the transaction -- ``tx.origin`` (``address payable``): sender of the transaction (full call chain) - -note: - The values of all members of ``msg``, including ``msg.sender`` and - ``msg.value`` can change for every **external** function call. - This includes calls to library functions. - -note: - Do not rely on ``block.timestamp``, ``now`` and ``blockhash`` as a source of randomness, - unless you know what you are doing. - -note: - The block hashes are not available for all blocks for scalability reasons. - You can only access the hashes of the most recent 256 blocks, all other - values will be zero. - -note: - The function ``blockhash`` was previously known as ``block.blockhash``, which was deprecated in - version 0.4.22 and removed in version 0.5.0. - -note:: - The function ``gasleft`` was previously known as ``msg.gas``, which was deprecated in - version 0.4.21 and removed in version 0.5.0. - -> index: abi, encoding, packed - - - -****************************** -Other things to be transpiled: -****************************** - -Members of Address Types -************************ - -`(Source) `_ - -- ``
.balance`` (``uint256``): - balance of the :ref:`address` in Wei -- ``
.transfer(uint256 amount)``: - send given amount of Wei to :ref:`address`, reverts on failure, forwards 2300 gas stipend, not adjustable -- ``
.send(uint256 amount) returns (bool)``: - send given amount of Wei to :ref:`address`, returns ``false`` on failure, forwards 2300 gas stipend, not adjustable -- ``
.call(bytes memory) returns (bool, bytes memory)``: - issue low-level ``CALL`` with the given payload, returns success condition and return data, forwards all available gas, adjustable -- ``
.delegatecall(bytes memory) returns (bool, bytes memory)``: - issue low-level ``DELEGATECALL`` with the given payload, returns success condition and return data, forwards all available gas, adjustable -- ``
.staticcall(bytes memory) returns (bool, bytes memory)``: - issue low-level ``STATICCALL`` with the given payload, returns success condition and return data, forwards all available gas, adjustable - -warning: - There are some dangers in using ``send``: The transfer fails if the call stack depth is at 1024 - (this can always be forced by the caller) and it also fails if the recipient runs out of gas. So in order - to make safe Ether transfers, always check the return value of ``send``, use ``transfer`` or even better: - Use a pattern where the recipient withdraws the money. - -note: - Prior to version 0.5.0, Solidity allowed address members to be accessed by a contract instance, for example ``this.balance``. - This is now forbidden and an explicit conversion to address must be done: ``address(this).balance``. -NOTE: we will need address(this) to return the L2 address and not the L1 address. - -Contract Related -**************** - -- ``this`` (current contract's type): - the current contract, explicitly convertible to :ref:`address` - -- ``selfdestruct(address payable recipient)``: - Destroy the current contract, sending its funds to the given :ref:`address` - and end execution. - Note that ``selfdestruct`` has some peculiarities inherited from the EVM: - - the receiving contract's receive function is not executed. - - the contract is only really destroyed at the end of the transaction and ``revert`` s might "undo" the destruction. \ No newline at end of file diff --git a/packages/docs/src/core/src/spec/execution-manager.rst b/packages/docs/src/core/src/spec/execution-manager.rst deleted file mode 100644 index c9d03afc36dcc..0000000000000 --- a/packages/docs/src/core/src/spec/execution-manager.rst +++ /dev/null @@ -1,122 +0,0 @@ -================================ -Execution Manager Overview -================================ - -The Execution Manager is technically just a smart contract running in a local EVM (layer 2) and available on Ethereum to evaluate fraud claims (layer 1), but in principle, it is much more. It *is* the layer 2 EVM, and it allows our Optimistic Rollup implementation to generically support layer 1 smart contracts. - -Motivation -========== - -The `Unipig Demo `_ showed that Optimistic Rollup is possible with custom contract code in both layer 1 and layer 2. -Layer 1 contracts each need a custom state transition function that can be given a snapshot of the layer 2 state and a state transition to execute in order to evaluate if the layer 2 state transition was properly executed. A simple state transition function example would be transferring an ERC-20 token. The layer 1 token contract would need a function that takes in pre-state (i.e. address balances, approvals, etc.), evaluates a particular transition (e.g. a transfer), and computes the resulting state (i.e. updated balances). Needless to say, the logic to execute this state transition in layer 2 needed to be created as well. - -To support generic smart contracts in layer 1... ------------------------------------------------- - -We need all state transitions for all possible contracts deployed to layer 2 to be generically calculable by layer 1. The EVM provides this functionality, but layer 1 runs on the EVM -- we need this to run on layer 1 (*on the EVM*). If we can create an EVM that can run *inside* of the EVM, all standard EVM operations can be executed efficiently in this layer 2 EVM while also being generically verifiable in the case of fraud in layer 1 (by calling the EVM within the layer 1 EVM). - -To support generic smart contracts in layer 2... ------------------------------------------------- -The layer 2 EVM needs to be able to run all layer 1 smart contracts with no additional code created per contract. If layer 2 runs a layer-1-compatible EVM (that, itself, can be run in layer 1), this is achieved. - -We call this layer-1-compatible EVM that can run within the layer 1 EVM the OVM ExecutionManager Contract. - -Design -====== - -Necessary Features ------------------- - -Just like the EVM, the OVM ExecutionManager Contract must: -* Handle all opcodes other than those deeply embedded in the layer 1 protocol (like `COINBASE`, `DIFFICULTY`, block `NUMBER`, `BLOCKHASH`) -* Generically support smart contracts, including those that depend on and even create other smart contracts -* Serve as the entrypoint to all calls, transactions, and state modification -* Store all state created and modified by transaction and smart contract execution - -Implementation --------------- - -As stated above, the Execution Manager is a smart contract that runs in a [slightly modified] local EVM*. Below are its implementation details and reasoning behind its functionality. - -Transaction Context & Re-implementing Opcodes ---------------------------------------------- - -Transactions that are run in layer 2 will necessarily have a different context than fraud proofs in layer 1. For instance, a fraud proof can only be submitted to layer 1 to dispute a layer 2 transaction some time after it has been executed. As such, opcodes like `TIMESTAMP` will *function* the same (it'll be the timestamp when the transaction was actually executed), but the actual current time will not *be* the same when executed in layer 1 vs challenged as fraudulent layer 2. - -.. raw:: html - - The Execution Manager - - -To handle this, the OVM ExecutionManager Contract implements these opcodes as functions. When a contract executing in layer 2 or a fraud proof executing in layer 1 needs to know the timestamp, it will call the OVM ExecutionManager Contract instead of accessing these layer-1-protocol-level opcodes directly. We have `transpilation tools `_ that take compiled layer 1 bytecode and swap out certain opcodes, like `TIMESTAMP`, for calls to our OVM ExecutionManager Contract. All contracts deployed to layer 2 must be transpiled accordingly. - -In our example, the sequencer that commits to a layer 2 transaction passes the timestamp at the time of execution to the OVM ExecutionManager Contract with the transaction to evaluate. They also specify the same timestamp in their rollup block that includes the transaction. This way, when the fraud proof is executed, the same timestamp from the rollup block will be set in the OVM ExecutionManager Contract prior to evaluating fraud so that the context that was committed to can be accessed correctly. More on timestamp considerations `here `_. - -CALL ----- - -There are many contextual differences between layer 1 and layer 2, so we won't go through all of them, but another important one to consider is that the addresses of the corresponding contracts will be different. All contracts in layer 2 are actually deployed by the OVM ExecutionManager Contract, but their addresses are created as if the caller deployed them. As such, all contract deployments go through the OVM ExecutionManager Contract, which maintains a map from OVM contract addresses (as if the caller created them) to EVM contract addresses (the address of the contract that was actually deployed by the OVM ExecutionManager Contract). This means that all `CALL` type opcodes must be transpiled to instead call the OVM ExecutionManager Contract so it may fill in the proper address for the call, as well as set other relevant context for the call's execution, like `CALLER` and `ADDRESS`. - -SSTORE & SLOAD ---------------- - -The last example to highlight is that `SSTORE` and `SLOAD` also need to be transpiled into calls to the OVM ExecutionManager Contract. Recall that one of the requirements is that the OVM ExecutionManager Contract needs to store all layer 2 state. This is so rollup blocks can commit to single pre-state and post-state roots and the fraud proof's pre- and post-state can be verified and executed through the OVM ExecutionManager Contract on layer 1 during fraud proofs. - -A list of transpiled opcodes and other transpilation details are available `here `_. - -Example: A user trading ETH for BAT on Uniswap -============================================== - -There are two main parts to this example: -* User transaction calling the layer 2 Uniswap Contract -* The layer 2 Uniswap Contract calling the corresponding ERC-20 Contracts to update balances - -Steps ------ -Sequencer Handles Request ------------------------------ - -1. It receives a signed transaction from the User calling the Uniswap BAT Exchange address's `ethToTokenTransferInput(...)` function. -2. It wraps this transaction's calldata in a call to the OVM ExecutionManager Contract's `executeCall(...)` function and sends the wrapped transaction. - -OVM ExecutionManager Contract handles the transaction in `executeCall(...)` ---------------------------------------------------------------------------- - -3. It receives the wrapped transaction, sets the transaction context (including timestamp, etc.), and calls the `ovmCALL(...)` opcode replacement function to execute the transaction. -4. Its `ovmCALL(...)` function sets the call-specific context (including the `CALLER`, the `ADDRESS` of the uniswap contract, etc.) -5. It looks up the EVM address of the Uniswap contract from the OVM address and `CALL`s the contract with the original transaction data. - -Uniswap / BAT Contract interaction ----------------------------------- - -6. Uniswap determines the exchange rate based on how much BAT it has by calling the OVM ExecutionManager Contract's `ovmCALL(...)` function to call the layer 2 BAT ERC-20 contract's `balanceOf(...)` function. -7. The OVM ExecutionManager Contract temporarily updates all of the call context variables in `ovmCALL(...)` to properly reflect that the `CALLER` is the Uniswap contract, `ADDRESS` is the BAT address, etc. -8. The OVM ExecutionManager Contract calls the BAT contract and it properly returns the balance -9. The OVM ExecutionManager Contract restores the call context such that the `CALLER` is the original caller, the `ADDRESS` is the Uniswap contract, etc. -10. The OVM ExecutionManager Contract returns the result to the Uniswap contract. -11. The Uniswap contract then calls the BAT contract, through the OVM ExecutionManager Contract again, to actually execute the transfer of the calculated amount of BAT -12. The Uniswap contract makes a final call to the BAT contract, through the OVM ExecutionManager Contract, to transfer the WETH `all ETH in layer 2 is WETH `_. -13. The Uniswap returns the number of tokens bought. -14. The OVM ExecutionManager Contract restores the original call context before the original call to the Uniswap contract and returns the result. - -OVM ExecutionManager Contract handles the transaction in `executeCall(...)` (continued) ---------------------------------------------------------------------------------------- - -15. It restores the original transaction context from before the transaction and returns the result - -Sequencer Handles Request (continued) -------------------------------------- - -16. It gets the internal transaction hash as a result. -17. It stores a mapping from the original transaction hash to the internal transaction hash for future transaction lookup. -18. It returns the original transaction hash, in compliance with Web3, to the caller. - -Not mentioned above: -* Access of `TIMESTAMP`, `ADDRESS`, `CALLER`, etc. which are actually `CALL`s to the associated OVM ExecutionManager Contract function. -* Access of all storage, which is actually a `CALL` to the `ovmSLOAD(...)` OVM ExecutionManager Contract function. -* Storage modification, which is actually a `CALL` to the `ovmSSTORE(...)` OVM ExecutionManager Contract function. -* All other opcodes handled through the OVM ExecutionManager Contract. - - - -*The layer 2 EVM will be run by the Sequencer that submits new layer 2 "blocks" to layer 1, validators who validate these blocks once submitted to layer 1, and any other interested party. Validation entails executing each individual state transition that is claimed to be valid by the Sequencer and ensuring that it is, in fact, valid (i.e. the resulting state from executing the state transition match the post-state claimed by the Sequencer). \ No newline at end of file diff --git a/packages/docs/src/core/src/troubleshooting.rst b/packages/docs/src/core/src/troubleshooting.rst deleted file mode 100644 index 237a4fc5514a4..0000000000000 --- a/packages/docs/src/core/src/troubleshooting.rst +++ /dev/null @@ -1,25 +0,0 @@ -=============== -Troubleshooting -=============== - -The OVM is an alpha. In trying it out, you're helping us scale Ethereum and pave the way for a new generation of Layer 2 systems. It also means you'll probably run into some bugs! If you do, please reach out to us and create an issue on our `monorepo `_. - -While you do so, here is a collection of tips and notes that may help you figure out what's going on. - -Limitations ------------ -Some features of the Ethereum are not yet implemented, or just don't make sense to have, in the OVM. Check out our limitations section (link) to get more information to check if this is why you're running into issues. - -Logging -------- -We use the npm package``debug`` for logging. To get a better sense of what might be breaking, you can run -.. code-block:: none - - env DEBUG="debug:*,error:*" [test command] - -in your terminal. - -Getting Wallets ---------------- - -``rollup-full-node`` provides an RPC-based provider, and does not always allow you to `getWallets()`. Instead, use the `getWallets()` function exported by ``rollup-full-node`` instead. diff --git a/packages/docs/src/core/src/what-is-the-ovm.rst b/packages/docs/src/core/src/what-is-the-ovm.rst deleted file mode 100644 index c7abb27f00789..0000000000000 --- a/packages/docs/src/core/src/what-is-the-ovm.rst +++ /dev/null @@ -1,25 +0,0 @@ -=================== -The OVM 101 -=================== - -What is the OVM? -================ - -The Optimistic Virtual Machine (OVM) is a scalable form of the EVM designed for use in layer 2 (L2) systems. You can think of it as a container environment like Docker or Xen: even though a containerized program on Docker runs on your local computer, it executes *as if on the machine defined by its container.* Similarly, OVM transactions which occurred on an L2 chain can be executed on an L1 chain *as if they are running on the L2 chain*. This ability is the fundamental underpinning of `optimistic execution`_ , the `basis`_ for L2 scaling solutions: the L1 blockchain only needs to evaluate transactions in the pessimistic case of misbehavior. - -The OVM functions as a drop-in replacement for the EVM. This means that, for the first time, L2 chain can provide application developers with the same experience and features that L1 Ethereum has. No hoops, no tricks--the Ethereum you know and love, ready to scale up with L2. - -To learn more about how it works under the hood, you can check out our specifications section. - -How do I use the OVM? -===================== - -Because the OVM complies with the EVM, porting your system to use the OVM is intended to be as easy as possible. Only two things have to be done: 1. Transpile your contracts to comply with the OVM's containerization interface. To accomplish this, we provide ``@eth-optimism/solc-transpiler``: a plug-and-play replacement for the solidity compiler, ``solc``, which transpiles your smart contracts to work on the OVM. -2. Run the transpiled contracts in an OVM-enabled web3 provider. To accomplish this, we provide ``@eth-optimism/rollup-full-node``: a plug-and-play Web3 provider which converts web3 calls to work on the OVM. - -Check out our ERC20 `tutorial`_ if you'd like to try it in action! Or learn to integrate it into an existing project `here`_. - -.. _`basis`: https://twitter.com/ben_chain/status/1161425776929136641?s=20 -.. _`optimistic execution`: https://plasma.group/optimistic-game-semantics.pdf -.. _`here`: ./integrating-tests.html -.. _`tutorial`: https://github.com/ethereum-optimism/erc20-example diff --git a/packages/docs/synthetix-l2.md b/packages/docs/synthetix-l2.md new file mode 100644 index 0000000000000..23b10a32f939e --- /dev/null +++ b/packages/docs/synthetix-l2.md @@ -0,0 +1,51 @@ +--- +description: >- + Demoing the OVM, general purpose EVM-compatible L2 infrastructure: + http://l2.synthetix.exchange/ +--- + +# Synthetix Demo FAQ + +## What is the L2 Synthetix demo? + +Synthetix on L2 is a demonstration of the power of Layer 2 for Ethereum--Web2 UX, Web3 features, and a backend ready for mass adoption. With Optimistic Ethereum, this power comes to the fingertips of any Ethereum dApp with just a few lines of code. + +## How does Optimistic Ethereum work? + +Optimistic Ethereum is a layer 2 scaling solution powered by The Happy Stack: the OVM \[link to OVM explainer\] running on ORU \[link to ORU article\]. Together, the two make for web3 magic. + +## What are the UX benefits of Optimistic Ethereum? + +Optimistic Ethereum is the Ethereum you know and love--just with: + +* Instant cryptoeconomic confirmations +* Orders of magnitude better cost & throughput. +* Support for native meta-transactions/account abstraction +* No painful gas UX + +## How does Optimistic Ethereum compare to other scaling solutions? + +* Full EVM support--any Ethereum dApp works with just a few lines of code +* No moon math. \(SNARKS, STARKS, etc.\) +* Not a sidechain. \(Data availability on ETH 1.0\) +* Smart contracts \(Generalized, interoperable\) +* Simple infrastructure. \(No consensus\) + +## How many transactions per second? + +* ETH 1.0 \(today\): ~200 tps +* ETH 1.0 \(optimized\): ~2,000 tps +* ETH 2.0 \(sharded\): 2,000 \* \# of shards + +## Is Optimistic Rollup secure? + +Yes. Data availability on mainnet Ethereum guarantees anyone can challenge invalid transactions. + +## What’s missing from this demo? + +Layer 2 deposits and withdrawls. Instead, we airdrop testnet tokens to your wallet. + +## How do I build a dApp on Optimistic rollup? + +Alpha code and documentation for the OVM can be found here! It's not ready for mainnet _yet_... but stay tuned! +