From 8d305beef92763c1ad1ba1736996c3844938df0d Mon Sep 17 00:00:00 2001 From: raulk Date: Sat, 21 Jan 2023 07:06:37 +0000 Subject: [PATCH] FIP-0054: Filecoin EVM runtime (FEVM) (#569) * initial incomplete draft for EVM compatibility FIP. * outdent specification sections. * split into two FIPs; WIP. * continue fleshing out specifications; use semantic line breaks. * continue fleshing out the Ethereum Accounts and EAM FIP. * rename FIP file. * stamp FIP number. * add LOG opcodes specification. * remove FIP-0055 from here. * wip: major updates for completeness. * complete draft. * document Filecoin precompiles. * minor edit. * adjust event definition for LOG opcodes. * apply method number reorganization, adopting FRC42 call convention. * prose adjustments. * add token_amount to InvokeContractDelegate params. * align HandleFilecoinMethod specification with latest implementation. * various refinements. * further refine opcode remarks. * re-align precompiles specification with implementation. * update discussion link. * document more differences between Ethereum and Filecoin EVMs. * cover addressing stability caveat. * regenerate ToC. * adjust opcode definitions. * address nits. * address nit. * fix actor_type definition * remove vscode file. * address my own feedback (and document PUSH0) * params are CBOR. * elaborate on memory semantics. * adjust GASPRICE opcode behavior. * clearly specify that REVERT aborts. * send::send now takes a gas_limit. * refinements to differences with Ethereum. Co-authored-by: Steven Allen --- FIPS/fip-0054.md | 1325 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1325 insertions(+) create mode 100644 FIPS/fip-0054.md diff --git a/FIPS/fip-0054.md b/FIPS/fip-0054.md new file mode 100644 index 00000000..8b532913 --- /dev/null +++ b/FIPS/fip-0054.md @@ -0,0 +1,1325 @@ +--- +fip: "0054" +title: Filecoin EVM runtime (FEVM) +author: Raúl Kripalani (@raulk), Steven Allen (@stebalien) +discussions-to: https://github.com/filecoin-project/FIPs/discussions/592 +status: Draft +type: Technical Core +category: Core +created: 2022-12-02 +spec-sections: +requires: N/A +replaces: N/A +--- + +# Filecoin EVM compatibility (FEVM) + + + +**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* + +- [Simple Summary](#simple-summary) +- [Abstract](#abstract) +- [Change Motivation](#change-motivation) +- [Specification: Filecoin EVM (FEVM) Runtime Actor](#specification-filecoin-evm-fevm-runtime-actor) + - [Installation and wiring](#installation-and-wiring) + - [Instantiatable actor](#instantiatable-actor) + - [State](#state) + - [Contract storage (KAMT)](#contract-storage-kamt) + - [Actor interface (methods)](#actor-interface-methods) + - [`Constructor` (method number 1)](#constructor-method-number-1) + - [`Resurrect` (method number 2)](#resurrect-method-number-2) + - [`GetBytecode` (method number 3)](#getbytecode-method-number-3) + - [`GetBytecodeHash` (method number 4)](#getbytecodehash-method-number-4) + - [`GetStorageAt` (method number 5)](#getstorageat-method-number-5) + - [`InvokeContractDelegate` (method number 6)](#invokecontractdelegate-method-number-6) + - [`InvokeContract` (method number 3844450837[^1])](#invokecontract-method-number-3844450837%5E1) + - [`HandleFilecoinMethod` (general handler for method numbers >= 1024)](#handlefilecoinmethod-general-handler-for-method-numbers--1024) + - [Addressing](#addressing) + - [Opcode support](#opcode-support) + - [Opcodes without remarks](#opcodes-without-remarks) + - [Opcodes with remarks](#opcodes-with-remarks) + - [Precompiles](#precompiles) + - [Ethereum precompiles](#ethereum-precompiles) + - [Filecoin precompiles](#filecoin-precompiles) + - [Other considerations](#other-considerations) + - [Historical support](#historical-support) + - [Transaction types](#transaction-types) + - [Native currency](#native-currency) +- [Specification: Filecoin Virtual Machine changes](#specification-filecoin-virtual-machine-changes) + - [Environmental data during FVM Machine construction](#environmental-data-during-fvm-machine-construction) + - [EIP-155 Chain IDs of Filecoin networks](#eip-155-chain-ids-of-filecoin-networks) + - [Added syscalls](#added-syscalls) + - [Changed syscalls](#changed-syscalls) + - [`send::send` syscall](#sendsend-syscall) + - [`crypto::hash` syscall](#cryptohash-syscall) + - [Other syscall changes](#other-syscall-changes) + - [New externs](#new-externs) + - [New general exit codes](#new-general-exit-codes) +- [Specification: Client changes](#specification-client-changes) + - [Tipset CID](#tipset-cid) +- [Design Rationale](#design-rationale) + - [Flat vs. nested contract deployment model](#flat-vs-nested-contract-deployment-model) + - [Optimizing EVM storage](#optimizing-evm-storage) + - [Value sends](#value-sends) + - [FRC42 method number of `InvokeContract``](#frc42-method-number-of-invokecontract) +- [Backwards Compatibility](#backwards-compatibility) +- [Test Cases](#test-cases) +- [Security Considerations](#security-considerations) +- [Incentive Considerations](#incentive-considerations) +- [Product Considerations](#product-considerations) + - [Gas](#gas) + - [Gas changes through upgrades](#gas-changes-through-upgrades) + - [Main differences between Filecoin's EVM and Ethereum's EVM](#main-differences-between-filecoins-evm-and-ethereums-evm) + - [Inability to stably address some actors from EVM smart contracts](#inability-to-stably-address-some-actors-from-evm-smart-contracts) +- [Implementation](#implementation) +- [Copyright](#copyright) + + + +## Simple Summary + +We introduce a new built-in actor: the Filecoin EVM (FEVM) runtime actor, capable of running EVM smart contracts on top of the Filecoin Virtual Machine. +We also introduce various changes to the Filecoin Virtual Machine and to client implementations, necessary to support the operation of this new actor. + +## Abstract + +The Filecoin EVM (FEVM) runtime built-in actor runs EVM smart contracts compatible with the [Ethereum Paris fork], plus [EIP-3855] (PUSH0). + +It achieves this by embedding a special-purpose EVM interpreter, implementing the integration logic with the Filecoin environment, translating environment-dependent opcodes into their corresponding Filecoin primitives, and mapping all state I/O to the underlying IPLD model. + +The EVM interpreter strives for maximal as-is portability of existing EVM bytecode. +For this reason, the EVM interpreter supports all required opcodes and Ethereum precompiles to strive for maximal portability. +Functional and technical departures from Ethereum's standard expectations are documented herein. + +The FEVM runtime actor motivates some changes in the FVM. +Syscalls are modified, syscalls are added, new environmental data is required, and new exit codes are created. +The concept of a TipsetCID is also introduced formally, and is of required implementation by clients. + +This FIP is dependent on [FIP-0048] (f4 address class), [FIP-0049] (Actor events), and [FIP-0055] (Supporting Ethereum Accounts, Addresses, and Transactions). + +## Change Motivation + +A basic requirement to achieve EVM compatibility is to be able to run EVM smart contracts. +Given the inability to deploy user-defined Wasm actors (arriving at a later stage of the FVM roadmap), we introduce this capability by adding a new built-in actor to the network. +This actor is accompanied by FVM and client changes necessary for functional completeness. + +## Specification: Filecoin EVM (FEVM) Runtime Actor + +The FEVM runtime actor is the Filecoin built-in actor that _hosts_ and _executes_ EVM bytecode. It contains: + +1. An embedded EVM interpreter. + It processes EVM bytecode handling every instruction according to EVM expectations, with functional departures described herein. +2. The integration logic connecting the EVM interpreter with the Filecoin environment, chain, and virtual machine (FVM). +3. A collection of Ethereum and Filecoin-specific precompiles, as specified below. + +### Installation and wiring + +On the migration for the network upgrade where this FIP goes live: + +1. The Wasm bytecode for the EVM runtime actor is loaded onto the node's blockstore. +2. Its CodeCID is linked to the System actor's state under the key `"evm"`. +3. An Ethereum Account actor (as per [FIP-0055]) is created in the state tree with balance zero, nonce zero, and delegated address `f410faaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaonc6iji`, corresponding to the [Ethereum zero address](#ethereum-zero-address). + +### Instantiatable actor + +The Filecoin EVM runtime is an actor that can be instantiated by users (and therefore, not a singleton actor). +A new instance is to be created for each EVM smart contract deployed on the network. +Instantiation can only originate in the Ethereum Address Manager (EAM), as defined in [FIP-0055], who assigns an f410 subaddress prior to creating the actor via the Init actor, as per standard Filecoin mechanics. + +At the time of writing, this makes the EVM runtime actor comparable to the Miner, Payment Channel, and Multisig built-in actors, and unlike the Storage Power, Storage Market, Cron, and other singleton built-in actors. + +### State + +The state of the EVM runtime actor is as follows: + +```rust +/// DAG-CBOR tuple-encoded. +pub struct State { + /// The EVM contract bytecode resulting from calling the + /// initialization code (init code) by the constructor. + pub bytecode: Cid, + + /// The EVM contract bytecode hash keccak256(bytecode) + pub bytecode_hash: [u8; 20], + + /// The EVM contract state dictionary. + /// All EVM contract state is a map of U256 -> U256 values. + /// + /// Root of KAMT + pub contract_state: Cid, + + /// The EVM nonce used to track how many times CREATE or + /// CREATE2 have been called. + pub nonce: u64, + + /// Possibly a tombstone if this actor has been self-destructed. + /// In the EVM, self-destructed contracts are "alive" until the current top-level transaction + /// ends. We track this by recording the origin and nonce. + /// + /// Specifically: + /// + /// 1. On SELFDESTRUCT, they mark themselves as "deleted" (by setting a tombstone with the + /// current origin/nonce), send away all funds, and return immediately. + /// 2. For the rest of the current transaction (as long as the tombstone's origin/nonce matches + /// the currently executing top-level transaction), the contract continues to behave + /// normally. + /// 3. After the current transaction ends, the contract behaves as if it were an "empty" + /// contract, kind of like a placeholder. At this point, the contract can be "resurrected" + /// (recreated) by via CREATE/CREATE2. + /// + /// See https://github.com/filecoin-project/ref-fvm/issues/1174 for some context. + pub tombstone: Option +} + +/// A tombstone indicating that the contract has been self-destructed. +/// DAG-CBOR tuple-encoded. +pub struct Tombstone { + /// The message origin when this actor was self-destructed. + pub origin: ActorID, + /// The message nonce when this actor was self-destructed. + pub nonce: u64, +} +``` + +### Contract storage (KAMT) + +EVM storage (u256 => u256 map) is backed by a specialized data structure based on the [Filecoin HAMT]: the fixed size Keyed AMT (KAMT). +This data structure is more mechanically sympathetic to typical EVM storage read and write patterns (refer to [Design Rationale](#design-rationale) for more detail). +`SLOAD` and `SSTORE` EVM opcodes map to reads and writes onto this KAMT, respectively, and ultimately to `ipld` syscalls specified in [FIP-0030]. + +This is how the KAMT compares to its Filecoin HAMT: + +1. **Key length.** + The HAMT uses 256-bit hashed keys. With the KAMT it is possible to specify an arbitrary key length. +3. **Key content.** + The HAMT assumes arbitrary keys, and hashes them on read and writes. + The KAMT obviates the need for key hashing and assumes the caller has hashed keys to a fixed-width binary format. +4. **Overlapping prefixes and extensions**. + The HAMT assumes hashing function that results in unpredictable keys which are uniformly distributed across the keyspace. + The KAMT optimizes for long overlaps in keys. + Whenever a node in the data structure is full, it looks for the longest common prefix of the keys and uses extensions to skip multiple levels in the tree that would otherwise only contain a single pointer pointing at the next level. + +Contrary to traditional EVM storage, the KAMT is an enumerable data structure. +However, no operations are provided for smart contracts to enumerate keys, at least not at this stage. +This useful property merely facilitates external observability and debuggability. + +### Actor interface (methods) + +The Filecoin EVM runtime actor offers these entrypoints, each of which handles messages sent with the specified Filecoin method numbers. +Method numbers respect the [FRC42 calling convention]. + +#### `Constructor` (method number 1) + +Initializes a new EVM smart contract with some init bytecode supplied as a constructor parameter. + +It is only be invocable by the Ethereum Address Manager (EAM), specified in [FIP-0055], indirectly via the Init actor. +This precondition is validated by checking that an f4 address with namespace `10` (corresponding to the EAM) was assigned to the current actor, prior to its `Constructor` method being called. + +The Constructor runs the init bytecode with the EVM interpreter, and populates the actor's state accordingly: + +- `bytecode` field: the execution bytecode is stored as a raw IPLD block and linked by CID from this field. +- `contract_state` field: contains the root CID of the storage [KAMT](#kamt-specification) resulting from `SSTORE` operations during construction. +- `nonce` field: set to 0, unless any calls to CREATE or CREATE2 happen during initialization. +- `bytecode_hash`: the last 20 bytes of the Keccak-256 hash of the bytecode. +- `tombstone`: may be non-empty if the contract selfdestructed itself during construction. + +_Input parameters_ + +```rust +// CBOR tuple encoded. +pub struct ConstructorParams { + /// The actor's "creator" (specified by the EAM). + pub creator: EthAddress, + /// The EVM initcode that will construct the new EVM actor. + pub initcode: RawBytes, +} +``` + +_Return value_ + +None. + +_Errors_ + +- `USR_ASSERTION_FAILED` (exit code 24) if the caller does not have an f4 delegated address under namespace `10` (that of the Ethereum Address Manager), or if it's lacking a subaddress. +- `USR_ILLEGAL_ARGUMENT` (exit code 16) if the EVM execution bytecode is longer than 24KiB (same limit as Ethereum), or if the bytecode starts with 0xef ([EIP-3541]). +- `USR_ILLEGAL_STATE` (exit code 20) if we failed to hash, or write the bytecode to the blockstore. +- `EVM_CONTRACT_REVERTED` (exit code 33) if the init bytecode reverted. + +#### `Resurrect` (method number 2) + +Reinstates a contract at a previously self-destructed actor. +This is a special version of the `Constructor` that the EAM invokes directly (not through the Init actor) when an EVM smart contract already existed at the address. + +_Input parameters_ + +Equal to `Constructor`. + +_Return value_ + +Equal to `Constructor`. + +_Errors_ + +Same as `Constructor`. + +- `USR_ASSERTION_FAILED` (exit code 24) if the caller does not have an f4 delegated address under namespace `10` (that of the Ethereum Address Manager), or if it's lacking a subaddress. +- `USR_ILLEGAL_ARGUMENT` (exit code 16) if the EVM execution bytecode is longer than 24KiB (same limit as Ethereum), or if the bytecode starts with 0xef ([EIP-3541]). +- `USR_ILLEGAL_STATE` (exit code 20) if we failed to hash, or write the bytecode to the blockstore. +- `EVM_CONTRACT_REVERTED` (exit code 33) if the init bytecode reverted. + +#### `GetBytecode` (method number 3) + +Returns the CID of the contract's EVM bytecode block, adding it to the caller's reachable set. +This method is used internally to resolve `EXTCODE*` opcodes. + +_Input parameters_ + +None. + +_Return value_ + +Nillable CID of the contract's bytecode as stored in state. +Returns nil if the contract is dead. + +_Errors_ + +- `USR_ILLEGAL_ARGUMENT` (exit code 16) when we fail to load the state. + +#### `GetBytecodeHash` (method number 4) + +Returns the keccak-256 hash of the execution bytecode, which was computed at construction time and stored in state. + +_Input parameters_ + +None. + +_Return value_ + +Keccak-256 hash of the contract's bytecode as stored in state. + +_Errors_ + +- `USR_ILLEGAL_ARGUMENT` (exit code 16) when we fail to load the state. + +#### `GetStorageAt` (method number 5) + +Returns the value stored at the specified EVM storage slot. +This method exists purely for encapsulation purposes; concretely, to enable tools to inspect EVM storage without having to parse the state object, or understand the KAMT. +Calling is restricted to `f00` (system actor), and therefore cannot be invoked via messages or internal sends. +It can be used by the Ethereum JSON-RPC `eth_getStorageAt` operation to resolve requests by constructing a local call and processing it with the FVM. + +_Input parameters_ + +```rust +// CBOR tuple encoded. +pub struct GetStorageAtParams { + pub storage_key: U256, // encoded as a CBOR byte string +} +``` + +_Return value_ + +Storage value (U256), encoded as a CBOR byte string. +Returns a 0-filled 32-byte array, if the key doesn't exist, or if the contract is dead. + +_Errors_ + +- `USR_FORBIDDEN` (exit code 18) when not called by address f00. +- `USR_ASSERTION_FAILED` (exit code 24) on internal errors. +- `USR_UNSPECIFIED` (exit code 23) when we fail to get the storage slot due to an internal error. + +#### `InvokeContractDelegate` (method number 6) + +Recursive invocation entrypoint backing calls made through the `DELEGATECALL` opcode. +Only callable by self. +See remarks on the `DELEGATECALL` opcode below for more information. + +_Input parameters_ + +```rust +// CBOR tuple encoded. +pub struct DelegateCallParams { + pub code: Cid, + /// The contract invocation parameters + /// Encoded as a CBOR byte string + pub input: Vec, + /// The original caller's Eth address. + pub caller: EthAddress, + /// The value passed in the original call. + pub value: TokenAmount, +} +``` + +_Return value_ + +Same as `InvokeContract`. + +_Errors_ + +- Exit code `USR_FORBIDDEN` (18) when caller is not self. + +#### `InvokeContract` (method number 3844450837[^1]) + +Invokes an EVM smart contract by loading the execution bytecode from state, and dispatching input data to it. + +The input data is expected to be framed as a CBOR byte string. +This method unframes it before handing it over to the contract (subsequently retrievable through CALLDATA* opcodes). +This method is universally callable. + +_Input parameters_ + +Raw bytes, encoded as a CBOR byte string. + +_Return value_ + +Raw bytes, encoded as a CBOR byte string. + +_Errors_ + +- `USR_UNSPECIFIED` (exit code 23) on internal errors instantiating the EVM runtime. +- `USR_NOT_FOUND` (exit code 17) when failing to load the bytecode. +- `EVM_CONTRACT_REVERTED` (exit code 33) when the contract reverts. +- `EVM_CONTRACT_INVALID_INSTRUCTION` (exit code 34) the INVALID instruction was executed. +- `EVM_CONTRACT_UNDEFINED_INSTRUCTION` (exit code 35) an undefined instruction was executed. +- `EVM_CONTRACT_STACK_UNDERFLOW` (exit code 36) there was a stack underflow when executing an instruction. +- `EVM_CONTRACT_STACK_OVERFLOW` (exit code 37) there was a stack overflow when executing an instruction. +- `EVM_CONTRACT_ILLEGAL_MEMORY_ACCESS` (exit code 38) there was an illegal memory access. +- `EVM_CONTRACT_BAD_JUMPDEST` (exit code 39) the contract jumped to an illegal offset. +- Any other error that is raised during bytecode execution. + +#### `HandleFilecoinMethod` (general handler for method numbers >= 1024) + +Filecoin native messages carrying a method number above or equal to 1024 (a superset of the public range of the [FRC42 calling convention]) are processed by this entrypoint. +This path enables processing transactions sent from non-Ethereum sending sites, e.g. built-in actors, Filecoin wallets, and future Wasm actors, with arbitrary Filecoin method numbers. + +Calling this entrypoint performs the following logic: +1. Map the Filecoin native message to Solidity input data satisfying the signature below. +2. Hand off execution to the EVM bytecode. +3. If execution succeeds, interpret the return data according to the signature below, and call the `fvm::exit` syscall with the extracted exit code and return data. + +When called with no parameters, the input `codec` will be 0 and the input `params` will be empty. To return no result, return the `codec` 0 and empty `return_data`. + +```solidity +// Function selector: 0x868e10c4 +// Note: return parameters have been named for specification purposes only. +// Naming return parameters is not valid Solidity. +function handle_filecoin_method(uint64 method, uint64 codec, bytes calldata params) public + returns (exit_code uint32, codec uint64, return_data bytes memory) {} +``` + +For more information, see the [Solidity Contract ABI Specification](https://docs.soliditylang.org/en/v0.8.17/abi-spec.html#contract-abi-specification) for more details about the call convention, parameter layouts, and packing. + +_Input parameters_ + +Limited to nothing or a valid CBOR object (for now). In the future, other codecs will be supported. + +_Return value_ + +Limited to nothing or a valid CBOR object (for now). In the future, other codecs will be supported. + +### Addressing + +EVM smart contracts deployed on Filecoin calling opcodes that take addresses as parameters can use two types of addresses. +Both these addresses conform to EVM addressing expectations (160-bit width): + +1. Masked Filecoin ID addresses. +2. Actual Ethereum addresses. + +**Masked Filecoin ID addresses** are addresses conforming to Ethereum's type (160-bit) that encode Filecoin ID addresses. +They are distinguished through a byte mask. +The high byte is `0xff` (discriminator), followed by thirteen 0 bytes of padding, then the uint64 ID of the actor. + +``` +|- disc -| |----- padding ------| |----- actor id -----| +0xff || 0000000000000000000000 || +``` + +**Ethereum addresses** are all addresses that do not satisfy the above mask. +They are converted to `f410` addresses as per the conversion specified in [FIP-0055]. +This includes the Ethereum Zero Address and precompile addresses. + +### Opcode support + +All opcodes from the [Ethereum Paris hard fork] are supported, plus PUSH0 from [EIP-3855]. +This section enumerates all supported opcodes, noting functional departures from their Ethereum counterparts, as well as relevant remarks about how they operate. +Opcodes are referred to by their mnemonic name. + +#### Opcodes without remarks + +These opcodes are handled locally within the EVM interpreter and have no departures from their original behaviour in Ethereum. + +- Arithmetic family: `ADD`, `MUL`, `SUB`, `DIV`, `SDIV`, `MOD`, `SMOD`, `ADDMOD`, `MULMOD`, `EXP`, `SIGNEXTEND`. +- Boolean operators: `LT`, `GT`, `SLT`, `SGT`, `EQ`, `ISZERO`, `AND`, `OR`, `XOR`, `NOT`. +- Bitwise operations: `BYTE`, `SHL`, `SHR`, `SAR`. +- Control flow: `STOP`, `JUMPDEST`, `JUMP`, `JUMPI`, `PC`, `STOP`, `RETURN`, `INVALID`. +- Parameters: `CALLDATALOAD`, `CALLDATASIZE`, `CALLDATACOPY`, `RETURNDATASIZE`, `RETURNDATACOPY`. +- Stack manipulation: `POP`, `PUSH{0..32}`, `DUP{1..32}`, `SWAP{1..16}`. + +#### Opcodes with remarks + +**Memory: `MLOAD`, `MSTORE`, `MSTORE8`, `MSIZE`.** +EVM memory is modelled as an object inside the interpreter, ultimately backed by 32-bit Wasm memory. +Usage of of these instructions incurs in Wasm memory expansion costs and is bounded by the memory limits specified in [FIP-0057]. +Contrary to Ethereum, Filecoin does enforce a maximum memory limit. +In Ethereum, memory is bounded implicitly by gas. +A failure to allocate will cause the system to abort with the `SYS_ILLEGAL_INSTRUCTION` exit code. + +**Accessors: `ADDRESS`.** +Returns the Ethereum address of the executing contract. + +**Accessors: `BALANCE`.** +Returns the filecoin balance of the contract, in atto precision (same precision as Ethereum). + +**Accessors: `ORIGIN`.** +Returns the Ethereum address of the account where the chain message originated. + +**Accessors: `CALLER`.** +Returns the Ethereum address of the immediate caller of the contract. + +**Accessors: `CALLVALUE`.** +Returns the filecoin value sent in the message, in atto precision (same precision as Ethereum). + +**Accessors: `GASPRICE`.** +Returns the base fee plus the gas premium, in atto precision (same precision as Ethereum). +This is functionally equivalent to Ethereum's `GASPRICE` definition following [EIP-1559], which is to return the `effective_gas_price`, defined as the following: + +``` +effective_gas_price = effective_gas_premium + BaseFee +``` + +In Filecoin, the `effective_gas_premium` is bounded by the `Message.GasFeeCap` and the `BaseFee` and clamped at 0: + +``` +effective_gas_premium = max(min(Message.GasPremium, Message.GasFeeCap - BaseFee), 0) +``` + +**Accessors: `BLOCKHASH`.** +Returns the hash within the tipset CID of the requested epoch, with a maximum lookback of 256 epochs, truncated to preserve the left-most 32 bytes. +If the requested epoch is a null round, it seeks to the last non-null tipset. +This means that the lookup can range beyond 256 epochs if null rounds are involved. + +**Accessors: `COINBASE`.** +Nil implementation. +Returns a fixed 0x0 value. + +**Accessors: `TIMESTAMP`.** +Returns the timestamp of the epoch being executed, as a Unix timestamp, as supplied by the client during Machine construction. + +**Accessors: `NUMBER`.** +Returns the epoch being executed. + +**Accessors: `PREVRANDAO` (former `DIFFICULTY`, see [EIP-4399]).** +Returns beacon randomness for the current epoch, by calling the `rand::get_beacon_randomness` syscall as defined in [FIP-0030], supplying: + +- domain separation tag `10` +- entropy equivalent to the UTF-8 bytes of string "prevrandao" + +**Accessors: `GASLIMIT`.** +Returns the fixed number 10e9 (10B gas). + +**Accessors: `CHAINID`.** +Returns the [EIP-155 Chain ID](#eip-155-chain-ids-of-filecoin-networks) of the current network. + +**Accessors: `BASEFEE`.** +Returns the base fee, in atto precision (same precision as Ethereum). + +**Accessors: `SELFBALANCE`.** +Returns the contract's filecoin balance, in atto precision (same precision as Ethereum). + +**Accessors: `GAS`.** +Returns the Filecoin gas available. + +**Control flow: `REVERT`.** +Aborts by calling the `fvm::exit` syscall with user-space exit code 33 (`EVM_CONTRACT_REVERTED`), passing in the supplied value to be returned as return data, and reverting all changes. + +**Hashing: `KECCAK256`.** +Makes a syscall to `crypto::hash` with the supplied preimage and the Keccak-256 multihash. + +**Logging: `LOG{0..4}`.** `LOG{0..4}` opcodes emit [FIP-0049] compliant actor events conforming to the following template: + +```rust +// Values are encoded as DAG-CBOR byte strings. +ActorEvent { + entries: [ + (0x03, "t1", ), // when LOG1, LOG2, LOG3, LOG4 + (0x03, "t2", ), // when LOG2, LOG3, LOG4 + (0x03, "t3", ), // when LOG3, LOG4 + (0x03, "t4", ), // when LOG4 + (0x03, "d", ), // event data, omitted with LOG0 + ], +} +``` + +All fields are indexed by key and value because standard Ethereum queries against log data need to take the order of topics into account, as well as identify the data. + +**Storage: `SLOAD`, `SSTORE`.** +Maps storage operations to underlying reads and writes on the contract storage KAMT, which eventually result in `ipld` syscalls. + +**Lifecycle: `CREATE`.** +Calls the `Create` method of the Ethereum Address Manager ([FIP-0055]) to deploy the smart contract. + +**Lifecycle: `CREATE2`.** +Calls the `Create2` method of the Ethereum Address Manager ([FIP-0055]) to deploy the smart contract. + +**Lifecycle: `SELFDESTRUCT`.** + +1. Marks the current contract as deleted by setting the tombstone in state, recording the current message origin and nonce. +2. Transfers the balance out to the designated beneficiary. +3. Returns immediately. + +The contract isn't considered to be "dead" until it has a tombstone where the recorded origin and nonce differ from the origin and nonce of the currently executing message. This respects Ethereum semantics in that the contract continues to be "alive" until the end of the transaction. + +- Subsequent calls to the self-destructed contract within the same transaction will continue to run the existing bytecode. +- Calls to this contract in subsequent transactions will return successfully with no code being executed. + +**Calls: `CALL`.** +Performs a call to another actor, behaving differently depending on the target's actor type. +If the target address is a precompile address, it is handled internally by first calling the precompile logic, and then transferring any value to the actual precompile address on-chain. +Otherwise, the target actor type is determined by calling the `actor::get_actor_code_cid` and `actor::get_builtin_actor_type` syscalls. +If the target is another EVM smart contract or a non-account Wasm actor, it invokes its `InvokeContract` method. +If the target is a placeholder, an account, or an EthAccount actor ([FIP-0055]), it lowers the call to a bare value send. +See [Value sends](#value-sends) for considerations. + +If the gas limit is 2300, no matter the target's type, the call is lowered to a bare value send. +This is to honor the well-known Ethereum convention of setting a gas limit equal to 2300 to prevent any possible contract logic from running. + +The 1/64th gas reservation rule from Ethereum is honored. +CALL opcodes specifying no gas limit will implicitly set a 63/64th gas limit for the callee, except when the callee is a precompile (since precompiles are resolved internally). +Note that the reservation is applied on Filecoin gas, not Ethereum gas. +See [Product considerations: Gas](#gas) for more information. + +**Calls: `CALLCODE`.** +Not supported. +Aborts with `EVM_CONTRACT_EXECUTION_ERROR` (exit code 34) when used. +This is because [EIP-7](https://eips.ethereum.org/EIPS/eip-7) introduced `DELEGATECALL` as a hardened replacement for `CALLCODE`. +Solidity no longer supports `CALLCODE`. + +**Calls: `DELEGATECALL`.** +Fetches the bytecode of the target, as long as it's an EVM runtime actor, and calls `InvokeContractDelegate` on itself to create a new call stack "frame" whilst still operating on our own state. +If the target is an account, placeholder, or an EthAccount actor ([FIP-0055]), it returns nothing. +If the target is another Wasm actor, we revert (aborting with exit code 33, i.e. `EVM_CONTRACT_REVERTED`). +If the target is a precompile address, it is handled internally by first calling the precompile logic, and then transferring any value to the actual precompile address on-chain. + +A delegate call against a precompile has the same effect as `CALL`. + +**Calls: `STATICCALL`.** +Performs the `CALL` in read-only mode, disallowing state mutations, event emission, and actor deletions. +See [`send::send`](#sendsend-syscall) for more information. + +**External code: `EXTCODESIZE`.** + +- If the supplied address belongs to an EVM smart contract, it retrieves the size of the bytecode by calling the [`GetBytecode`](#getbytecode-method-number-3) method, fetching the block via an `ipld::block_read` operation, and returning the length of the block. +- If the supplied address is deemed an account, whether a native account, a placeholder, or an Eth account, it returns 0 (no code). +- If the supplied address does not exist, it returns 0 (no code). +- Otherwise, it returns the non-zero sentinel value 1, to indicate that there is code. However, it's not EVM bytecode so we can't return the real size. + +NOTE: this operation is used by popular utilities to check if an address is a contract, e.g. [OpenZeppelin's toolbox](https://docs.openzeppelin.com/contracts/2.x/api/utils#Address-isContract-address-). + +**External code: `EXTCODECOPY`.** + +- If the supplied address belongs to an EVM smart contract, it copies its bytecode into the designated memory region by calling the [`GetBytecode`](#getbytecode-method-number-3) method, fetching the block via an `ipld::block_read` operation, and copying it into the memory segment. +- If the supplied address is deemed an account, whether a native account, a placeholder, or an Eth account, it copies nothing. +- If the supplied address does not exist, it copies nothing. +- Otherwise, it copies the sentinel value `0xfe`. + +**External code: `EXTCODEHASH`.** + +- If the supplied address belongs to an EVM smart contract, it copies its bytecode into the designated memory region by calling the [`GetBytecode`](#getbytecode-method-number-3) method, fetching the block via an `ipld::block_read` operation, and copying it into the memory segment. +- If the supplied address is deemed an account, whether a native account, a placeholder, or an Eth account, it copies nothing. +- If the supplied address does not exist, it copies nothing. +- Otherwise, it copies the sentinel value `0xfe`. + +### Precompiles + +There are two kinds of precompiles available to EVM smart contracts running within the FEVM runtime actor: + +- Ethereum precompiles +- Filecoin precompiles + +#### Ethereum precompiles + +The FEVM runtime actor supports all [Ethereum precompiles] available in the Ethereum Paris fork. +These precompiles sit at their original Ethereum addresses. +There are no functional departures with respect to their original behaviors. +However, gas accounting follows Filecoin mechanics instead of Ethereum's. +See the [Product considerations: gas](#gas) section for more information. +Refer to Ethereum documentation for more information. + +| Ethereum Address | Precompile | +| ---------------- | ------------ | +| `0x01` | `ecRecover` | +| `0x02` | `SHA2-256` | +| `0x03` | `RIPEMD-160` | +| `0x04` | `identity` | +| `0x05` | `modexp` | +| `0x06` | `ecAdd` | +| `0x07` | `ecMul` | +| `0x08` | `ecPairing` | +| `0x09` | `blake2f` | + +#### Filecoin precompiles + +The following Filecoin-specific precompiles are exposed to EVM smart contracts at the designated addresses: + +| Ethereum Address | Precompile | +| ---------------- | ------------------------------ | +| `0xfe00..01` | `resolve_address` | +| `0xfe00..02` | `lookup_delegated_address` | +| `0xfe00..03` | `call_actor` | +| `0xfe00..05` | `call_actor_id` | + +**`resolve_address` precompile** + +Reads a Filecoin address and resolves it into a BE-encoded u64 actor ID, or an empty array if nothing found. +A failure to parse the address will result in a `1` being placed on the stack, which leads high-level languages like Solidity to REVERT. + +_Input data layout_ + +``` +
as bytes +``` + +_Return data layout_ + +``` + as u64 BE bytes (left padded to u256) +``` + +**`lookup_delegated_address` precompile** + +Reads a BE-encoded low u64 ID address from an u256 word, interprets it as an actor ID, and looks up the f4 address, returning an empty array if not found. + +_Input data layout_ + +``` + u64 (right padded to fit u256) +``` + +_Return data layout_ + +``` +
as bytes +``` + +Or empty if error, or inexistent. + +**`call_actor` precompile** + +Calls an actor with the ability to supply a method number, using any Filecoin address. + +_Input data layout_ + +``` + as u64 (left padded to u256) + || as u256 + || as u64 (left padded to u256) + || as u64 (left padded to u256) + || as u32 (left padded to u256) + || as u64 (left padded to u256) + +at : + as u32 (left padded to u256) + || as bytes + +at : + as u32 (left padded to u256) + || as bytes +``` + +```solidity +uint64 method; +uint256 value; +uint64 flags; +uint64 codec; +bytes memory params; +bytes memory target_address; +abi.encode(method, value, flags, codec, params, target_address) +``` + +_Return data layout_ + +``` + as u32 (left padded to u256) + || as u64 (left padded to u256) + || as u32 (left padded to u256) + +at : + as u32 (left padded to u256) + || as bytes + || optional 0 padding to round out an Ethereum word +``` + +```solidity +abi.decode(data, (int256, uint64, bytes)); +``` + +**`call_actor_id` precompile** + +Calls an actor with the ability to supply a method number, using an actor ID. + +_Input data layout_ + +``` + as u64 (left padded to u256) + || as u256 + || as u64 (left padded to u256) + || as u64 (left padded to u256) + || as u32 (left padded to u256) + || as u64 (left padded to u256) + +at : + as u32 (left padded to u256) + || as bytes +``` + +```solidity +uint64 method; +uint256 value; +uint64 flags; +uint64 codec; +bytes memory params; +uint64 actor_id +abi.encode(method, value, flags, codec, params, actor_id) +``` + +_Return data layout_ + +``` + as u32 (left padded to u256) + || as u64 (left padded to u256) + || as u32 (left padded to u256) + +at : + as u32 (left padded to u256) + || as bytes + || optional 0 padding to round out an Ethereum word +``` + +```solidity +abi.decode(data, (int256, uint64, bytes)); +``` + +### Other considerations + +#### Historical support + +Historical Ethereum behaviors are not supported. +EVM opcode and precompile support is restricted to the Ethereum Paris fork, plus PUSH0 from [EIP-3855]. + +#### Transaction types + +The only supported Ethereum transaction type is the EIP-1559 transaction (type 2 in the RLP-encoded transaction format). +Such transactions carry a gas fee cap and a gas premium, both of which map cleanly to Filecoin's message model. + +#### Native currency + +The native currency of the Filecoin EVM runtime is filecoin. +This environment has no dependence to, or understanding of, Ether as a currency. + +## Specification: Filecoin Virtual Machine changes + +### Environmental data during FVM Machine construction + +New environmental data is required by the FVM to satisfy new data returned in syscalls: + +- [EIP-155] [Chain ID](#eip-155-chain-ids-of-filecoin-networks) of the network. +- Timestamp of the execution epoch. + +### EIP-155 Chain IDs of Filecoin networks + +We define the following [EIP-155] Chain IDs for Filecoin networks: + +- Mainnet: 314 +- Hyperspace testnet: 3141 +- Wallaby testnet: 31415 +- Calibration testnet: 314159 +- Butterfly testnet: 3141592 +- Local/private testnets: 31415926 + +These Chain IDs have been registered in the [`ethereum-lists/chains`] registry, which in turn is prepared for [CAIP-2] compliance. + +### Added syscalls + +**`network::context`** + +```rust +#[repr(packed, C)] +pub struct NetworkContext { + /// The current epoch. + pub epoch: ChainEpoch, + /// The current time (seconds since the unix epoch). + pub timestamp: u64, + /// The current base-fee. + pub base_fee: TokenAmount, + /// The Chain ID of the network. + pub chain_id: u64, + /// The network version. + pub network_version: u32, +} + +/// Returns the details about the network. +/// +/// # Errors +/// +/// None +pub fn context() -> Result; +``` + +**`network::tipset_cid`** + +See [Tipset CID](#tipset-cid) for more context. + +```rust +/// Retrieves a tipset's CID within the last finality, if available. +/// +/// # Arguments +/// +/// - `epoch` the epoch being queried. +/// - `ret_off` and `ret_len` specify the location and length of the buffer into which the +/// tipset CID will be written. +/// +/// # Returns +/// +/// Returns the length of the CID written to the output buffer. +/// +/// # Errors +/// +/// | Error | Reason | +/// |---------------------|----------------------------------------------| +/// | [`IllegalArgument`] | specified epoch is negative or in the future | +/// | [`LimitExceeded`] | specified epoch exceeds finality | +pub fn tipset_cid( + epoch: i64, + ret_off: *mut u8, + ret_len: u32, +) -> Result; +``` + +**`actor::lookup_address`** + +```rust +/// Looks up the "delegated" (f4) address of the target actor (if any). +/// +/// # Arguments +/// +/// `addr_buf_off` and `addr_buf_len` specify the location and length of the output buffer in +/// which to store the address. +/// +/// # Returns +/// +/// The length of the address written to the output buffer, or 0 if the target actor has no +/// delegated (f4) address. +/// +/// # Errors +/// +/// | Error | Reason | +/// |---------------------|------------------------------------------------------------------| +/// | [`NotFound`] | if the target actor does not exist | +/// | [`BufferTooSmall`] | if the output buffer isn't large enough to fit the address | +/// | [`IllegalArgument`] | if the output buffer isn't valid, in memory, etc. | +pub fn lookup_delegated_address( + actor_id: u64, + addr_buf_off: *mut u8, + addr_buf_len: u32, +) -> Result; +``` + +**`actor::balance_of`** + +```rust +/// Gets the balance of the specified actor. +/// +/// # Arguments +/// +/// - `actor_id` is the ID of the target actor. +/// +/// # Errors +/// +/// | Error | Reason | +/// |----------------------|------------------------------------------------| +/// | [`NotFound`] | the target actor does not exist | +pub fn balance_of( + actor_id: u64 +) -> Result; +``` + +**`actor::next_actor_address`** + +```rust +/// Generates a new actor address for an actor deployed by the calling actor. +/// +/// **Privileged:** May only be called by the init actor. +pub fn next_actor_address(obuf_off: *mut u8, obuf_len: u32) -> Result; +``` + +**`crypto::recover_secp_public_key`** + +```rust +/// Recovers the signer public key from a signed message hash and its signature. +/// +/// Returns the public key in uncompressed 65 bytes form. +/// +/// # Arguments +/// +/// - `hash_off` specify location of a 32-byte message hash. +/// - `sig_off` specify location of a 65-byte signature. +/// +/// # Errors +/// +/// | Error | Reason | +/// |---------------------|------------------------------------------------------| +/// | [`IllegalArgument`] | signature or hash buffers are invalid | +pub fn recover_secp_public_key( + hash_off: *const u8, + sig_off: *const u8, +) -> Result<[u8; SECP_PUB_LEN]>; +``` + +**`event::emit_event`** + +Originally defined in [FIP-0049] (Actor events). Redefined and bundled here for convenience. + +```rust +/// Emits an actor event to be recorded in the receipt. +/// +/// Expects a DAG-CBOR representation of the ActorEvent struct. +/// +/// # Errors +/// +/// | Error | Reason | +/// |---------------------|---------------------------------------------------------------------| +/// | [`IllegalArgument`] | entries failed to validate due to improper encoding or invalid data | +pub fn emit_event( + evt_off: *const u8, + evt_len: u32, +) -> Result<()>; +``` + +**`gas::available`** + +```rust +/// Returns the amount of gas remaining. +pub fn available() -> Result; +``` + +**`vm::exit`** + +```rust +/// Abort execution with the given code and optional message and data for the return value. +/// The code and return value are recorded in the receipt, the message is for debugging only. +/// +/// # Arguments +/// +/// - `code` is the `ExitCode` to abort with. +/// If this code is zero, then the exit indicates a successful non-local return from +/// the current execution context. +/// If this code is not zero and less than the minimum "user" exit code, it will be replaced with +/// `SYS_ILLEGAL_EXIT_CODE`. +/// - `blk_id` is the optional data block id; it should be 0 if there are no data attached to +/// this exit. +/// - `message_off` and `message_len` specify the offset and length (in wasm memory) of an +/// optional debug message associated with this abort. These parameters may be null/0 and will +/// be ignored if invalid. +/// +/// # Errors +/// +/// None. This function doesn't return. +pub fn exit(code: u32, blk_id: u32, message_off: *const u8, message_len: u32) -> !; +``` + +**`vm::message_context`** + +```rust +#[repr(packed, C)] +pub struct MessageContext { + /// The current call's origin actor ID. + pub origin: ActorID, + /// The caller's actor ID. + pub caller: ActorID, + /// The receiver's actor ID (i.e. ourselves). + pub receiver: ActorID, + /// The method number from the message. + pub method_number: MethodNum, + /// The value that was received. + pub value_received: TokenAmount, + /// The current gas premium + pub gas_premium: TokenAmount, + /// The current gas limit + pub gas_limit: u64, + /// Flags pertaining to the currently executing actor's invocation context. + /// Where ContextFlags is an u64-encoded bitmap, accepting values: + /// - 0x01: read only + pub flags: ContextFlags, +} + +/// Returns the details about the message causing this invocation. +/// +/// # Errors +/// +/// None +pub fn message_context() -> Result; +``` + +### Changed syscalls + +#### `send::send` syscall + +This syscall now takes two extra fields: +- `gas_limit` (u64), the gas limit applicable to the send (where `u64::MAX` can be safely used to mean none). +- `send_flags` (u64), encoding a bitmap of send flags. + +The possible send flags are: + +- `0x01`: Read-only. Nor the callee nor any of its transitive callees can cause any side effects. + That is, no IPLD writes are allowed, no events can be emitted, no value can be sent, and no actor deletions are possible. + +This syscall now returns a new error number: + +- `ReadOnly` (number 13): returned when the called actor tries to perform one of the restricted mutations. + +#### `crypto::hash` syscall + +This syscall now supports the following hashes, specified by their [multicodec] value: + +- Sha2_256 (`0x12`) +- Blake2b256 (`0xb220`) +- Blake2b512 (`0xb240`) +- Keccak256 (`0x1b`) +- Ripemd160 (`0x1053`) + +### Other syscall changes + +- `vm::abort` is replaced by the more general syscall `vm::exit`, which can accept a zero exit code, as well as return data on error. +- `vm::context` is renamed to `vm::mesage_context`. +- `network::base_fee` is superseded by `network::context`, which returns the base fee, in addition to other fields. +- `actor::new_actor_address` is replaced by `actor::next_actor_address`. + +### New externs + +We define a new extern, a function provided by the host client, to retrieve the Tipset CID of a given epoch. + +```rust +/// Gets the CID for a given tipset. +/// +/// If the supplied epoch happens to be a null round, the client must return the CID +/// of the previous first non-null round. +fn get_tipset_cid(&self, epoch: ChainEpoch) -> anyhow::Result; +``` + +### New general exit codes + +- USR_READ_ONLY (exit code 25). To be returned by actors when the requested operation cannot be performed in "read-only" mode. Specifically: + - Value transfers in `send::send`. + - Actor state-root changes (`self::set_root`). + - `self::selfdestruct` + - Actor creation. + +## Specification: Client changes + +### Tipset CID + +We introduce the concept of the tipset CID, which uniquely identifies the set of blocks that were incorporated to the blockchain at a given epoch. + +In the past, tipsets were identified by a composite key enumerating the block CIDs of every block in canonical order, also known as "tipset key". + +The tipset key is the concatenation of the bytes of the CIDs of the blocks that make up the tipset, in lexicographic order of their `Ticket` field ([block canonical order](https://spec.filecoin.io/#section-systems.filecoin_blockchain.struct.block.block-semantic-validation)). +It is CBOR-serialized as a byte string (major type 2). + +Starting from now, clients must compute and remember the tipset CID of every tipset committed to the chain. +This is achieved by inserting the CBOR-serialized tipset key into the chain blockstore, and computing its CID using the Blake2b-256 multihash function. + +## Design Rationale + +### Flat vs. nested contract deployment model + +Early on, we had to choose the broad architecture by which EVM smart contracts would become an entity on-chain. +We considered two large architectural options: + +1. A flat deployment model, where each EVM smart contract is an independent actor on-chain, with its own entry in the state tree, and its own address set. +2. A nested deployment model, where a singleton built-in actor would contain the entirety of the EVM domain within it, with some form of internal fragmentation to model every smart contract as an independent entity within a monolithic state. + +The choice went hand-in-hand with the addressing model, since we needed to assign and recognize Ethereum addresses either way. +With (1), we'd need to find a way to delegate addressing to another actor that controlled a discretionary address space. +With (2), addressing could be _recursive_, where the address first referred to the singleton actor, followed by an opaque component to be interpreted by such actor for internal routing. + +This choice is a foundational one, as it defines the architectural trajectory of the Filecoin network for years to come, insofar programmability is concerned. +These are the main reasons we chose to adopt (1): + +1. It places EVM smart contracts, as well as any other future foreign program, at equal footing and hierarchical level as any other actor, such as built-in actors, eventual user-deployed Wasm actors, and other future entities. +2. It physically isolates state between actors through a shared-nothing model. + This simplifies state management, makes state more granular, prevents undesired churn on update, eliminates the possibility of side effects, and contagion of IO overheads across smart contracts. +3. It enables future versioning of the EVM runtime with explicit opt-in from developers at the contract level (i.e. optionality). +4. It forces us to introduce general protocol advancements to facilitate seamless interoperability across supported runtimes, versus hyperspecialization within runtimes. + +See the discussion under [filecoin-project/ref-fvm#742](https://github.com/filecoin-project/ref-fvm/issues/742) for further context. + +### Optimizing EVM storage + +The EVM contract storage model makes no assumptions about concrete key patterns, slot placement of state variables, nor slot addressing in the case of complex and/or dynamic types such as structs, maps, or arrays. +Languages such as Solidity and Vyper are responsible mapping state to EVM storage by adopting a concrete storage layout for its types. +When designing the KAMT, we optimized for the [Solidity storage layout], such that: + +1. Contiguous slots are adjacently placed within the same node as adjacent pointers, or in adjacent nodes if overflowing. +2. Access to slots sharing common prefixes is shortcut through extensions that skip otherwise-sparse tree levels. + +Optimizing for Solidity makes sense because it is, by far, the most popular Ethereum smart contract programming language. +Furthermore, [Vyper has adopted Solidity's storage layout](https://github.com/vyperlang/vyper/issues/769) for complex and dynamic state types. + +### Value sends + +Ethereum does not distinguish between bare value sends and contract invocations. +Bare value sends can trigger smart contract logic in Ethereum. + +Filecoin distinguishes one from the other through the method number. +Method number 0 designates a bare value send and does not dispatch the call to actor logic on the recipient. +Method numbers other than 0 result in a Wasm actor instantiation and the consequent call dispatch. + +We have not altered these mechanisms in this FIP. +As a result, bare value sends to EVM smart contracts will not trigger EVM smart contract logic. +This is a significant functional difference between Filecoin and Ethereum. + +Refer to the community discussion at [`filecoin-project/ref-fvm#835`](https://github.com/filecoin-project/ref-fvm/issues/835) for more context. + +### FRC42 method number of `InvokeContract`` + +This method has been assigned a number in the non-reserved range compliant with the [FRC42 call convention] to pave the way for future Wasm actors to be able to act as Filecoin EVM smart contracts by, for example, processing native ERC-20, ERC-721, etc. transactions submitted from Ethereum wallets. +Having chosen a reserved number, e.g. `2`, would've raised the risk of method number conflicts down the line. + +## Backwards Compatibility + +Built-in actors must be updated to absorb the syscall changes specified herein to retain backwards compatibility. +This will lead to a new version of actors being produced. +A migration is required to add their Wasm bytecode to the blockstore, to link in their CodeCIDs in the System actor, and to update all existing actors to the new CodeCIDs. + +Furthermore, changes in syscalls must be absorbed by other built-in actors to preserve backwards compatibility. + +## Test Cases + +See integration tests in [`filecoin-project/ref-fvm`], as well as unit and integration tests of the [EVM runtime actor](https://github.com/filecoin-project/builtin-actors/tree/next/actors/evm/tests). + +## Security Considerations + +This FIP enables developers to deploy custom, untrusted code to the Filecoin network for the first time. +The security considerations are multi-faceted: + +1. Value will no longer flow exclusively through trusted smart contract logic. + Users must exercise caution and carefully analyze and decide what and who to trust. +2. EVM smart contract execution is doubly sandboxed. + First, within the Filecoin EVM runtime actor; and second within the Wasm invocation container. + Thus, the risk of malicious systemic outcomes is contained. +3. Inside the current gas architecture, the popularity of user contracts may conflict with the need for storage + providers to send system messages to continue operating and managing the network. + It is hard or impossible to forecast how the gas market will be impacted by the arrival of user-defined contracts to Filecoin. + The FVM team has requested the Cryptoecon Lab to produce a community report to evaluate possible scenarios for preparedness. + +## Incentive Considerations + +This is a technical FIP that independently raises no incentive considerations. +However, smart contracts may introduce new incentive structures to the Filecoin network that users could engage with. + +## Product Considerations + +Product considerations are broken down into subsections covering various topics. + +### Gas + +Gas metering and execution halt are performed according to the Filecoin gas model. +EVM smart contracts accrue: + +- Execution costs: resulting from the handling of Wasm instructions executed during the interpretation of the EVM bytecode, _as well as_ the EVM runtime actor logic (e.g. method dispatch, payload handling, and more). +- Syscall and extern costs: resulting from the syscalls made by the EVM opcode handlers, _and_ the EVM runtime actor logic itself. +- Memory expansion costs: resulting from the allocation of Wasm memory pages. +- Storage costs: resulting from IPLD state reads and accesses, directly by the smart contract as a result of `SSTORE` and `SLOAD` opcodes, or indirectly by the EVM runtime actor during dispatch or opcode handling. + +As specified above, gas-related opcodes such as `GAS` and `GASLIMIT` return Filecoin gas, coercing their natural u64 type to u256 (EVM type). +The gas limit supplied to the `CALL`, `DELEGATECALL` and `STATICCALL` opcodes is also Filecoin gas. + +Furthermore, gas limits specified when calling precompiles are disregarded. + +Consequently, contracts ported from Ethereum that use literal gas values (e.g. the well known 2300 gas price for bare value transfers) may require adaptation, as these gas values won't directly translate into the Filecoin gas model. + +### Gas changes through upgrades + +The EVM runtime actor backing EVM smart contracts may be upgraded through future migrations. +Upgrades will impact gas costs in hard-to-predict ways, with compute gas being the most sensitive component. +We discourage smart contract developers to rely on specific gas values in their contract logic. +This includes gas limits passed in `*CALL*` operations. +While the system honors those limits, costs will change across upgrades, so it's unsafe to hardcode assumptions. + +### Main differences between Filecoin's EVM and Ethereum's EVM + +1. Gas metering and execution halt are performed according to the Filecoin gas model. + Ethereum gas accounting does not apply in the Filecoin EVM. +2. Gas limits set when CALLing precompiles do not apply, except for the `call_*` precompiles where the gas limit supplied to the CALL constrains the resulting send. +3. Filecoin gas costs are not stable over network updates. + Developers must not make gas usage assumptions, as Filecoin gas is highly sensitive to implementation details, and they should refrain from using hardcoded gas limits in calls. +4. Bare value sends (messages with method number 0) to Filecoin EVM smart contracts do not trigger smart contract logic, even if the message carries parameters. +5. The `CALLCODE` opcode is not supported, since it was superseded in Ethereum by the more robust [`DELEGATECALL`](https://eips.ethereum.org/EIPS/eip-7) opcode, there are [requests to deprecate](https://github.com/ethereum/EIPs/pull/2488), and [Solidity no longer supports it](https://docs.soliditylang.org/en/v0.8.17/050-breaking-changes.html?highlight=callcode#functions) as of v0.5.0. +6. `SELFDESTRUCT` behavior: + - Contrary to Ethereum, if a self-destructed contract gets sent funds after it calls `SELFDESTRUCT` but before the transaction ends, those funds don't vanish. + Instead, they remain in the tombstoned smart contract. + - Contrary to Ethereum, `SELFDESTRUCT` does not trigger a physical deletion of the contract, but rather a logical deletion by way of tombstoning. + Thus, there is no gas refund for deleting an EVM smart contract. + +### Inability to stably address some actors from EVM smart contracts + +Only Ethereum Accounts and EVM smart contracts are assigned an `f410` address. +See [FIP-0055] for more context. +All other actors need to be addressed through Masked ID addresses. + +Unfortunately, contrary to `f410` addresses, ID addresses are not reorg stable. +In other words, **the ID may be reassigned within the chain's finality window**. + +This means that only these interactions benefit from addressing stability within the 900 epochs of the creation of the callee actor, when being addressed through their Ethereum address from within an EVM smart contract: + +1. EVM <> EVM interactions. +2. EVM <> EthAccount interactions. + +Calls from EVM to non-EVM actors must used Masked ID addresses, which do not benefit from stability until after 900 epochs from creation. +This means that the ID address may be reassigned within 900 epochs from creation. + +EVM smart contracts can only address the following actors through Masked ID addresses, thus making these interactions subject to recency instability: + +1. Non-Ethereum accounts (using f1/f3 addresses is not possible from within EVM smart contracts). +2. Other built-in actors like the miner actor, multisigs, etc. + +The stability issue is not a problem for singleton actors (e.g. power actor, storage market actor, etc.), as these actors sit at fixed ID addresses. + +Stemming from this, transferring value to inexistent f1 and f3 addresses from EVM smart contracts is not possible. +These are counterfactual interactions where the target still doesn't have an ID address, and thus cannot be currently addressed from an EVM smart contract. + +Note that we discarded assigning `f410` addresses to non-Ethereum related actors at this stage in the name of simplicity, but we may revisit this decision in the future, depending on user feedback. + +## Implementation + +At the time of writing, the EVM runtime actor implementation resides in the [`next` branch of `ref-fvm`](https://github.com/filecoin-project/builtin-actors/tree/next/actors/evm). + +## Copyright + +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). + + +[`ethereum-lists/chains`]: https://github.com/ethereum-lists/chains +[`filecoin-project/builtin-actors`]: https://github.com/filecoin-project/builtin-actors +[`filecoin-project/ref-fvm`]: https://github.com/filecoin-project/ref-fvm +[CAIP-2]: https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-2.md +[Contract ABI spec]: https://docs.soliditylang.org/en/v0.5.3/abi-spec.html +[EIP-155]: https://eips.ethereum.org/EIPS/eip-155 +[EIP-1559]: https://eips.ethereum.org/EIPS/eip-1559 +[EIP-3541]: https://eips.ethereum.org/EIPS/eip-3541 +[EIP-4399]: https://eips.ethereum.org/EIPS/eip-4399 +[EIP-3855]: https://eips.ethereum.org/EIPS/eip-3855 +[Ethereum Paris fork]: https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/ +[Ethereum precompiles]: https://www.evm.codes/precompiled +[Filecoin HAMT]: https://ipld.io/specs/advanced-data-layouts/hamt/spec/#appendix-filecoin-hamt-variant +[FIP-0048]: https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0048.md +[FIP-0049]: https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0049.md +[FIP-0055]: https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0055.md +[FIP-0057]: https://github.com/filecoin-project/FIPs/pull/573 +[FRC42 calling convention]: https://github.com/filecoin-project/FIPs/blob/master/FRCs/frc-0042.md +[multicodec]: https://github.com/multiformats/multicodec +[Solidity storage layout]: https://docs.soliditylang.org/en/v0.8.17/internals/layout_in_storage.html + +[^1]: [FRC42](https://github.com/filecoin-project/FIPs/blob/master/FRCs/frc-0042.md) hash of `InvokeEVM`.