Skip to content

Add Spurious Dragon Hardfork#423

Merged
SamWilsn merged 11 commits intoethereum:masterfrom
petertdavies:spurious-dragon
Jan 28, 2022
Merged

Add Spurious Dragon Hardfork#423
SamWilsn merged 11 commits intoethereum:masterfrom
petertdavies:spurious-dragon

Conversation

@petertdavies
Copy link
Contributor

@petertdavies petertdavies commented Jan 18, 2022

This patch implements the Spurious Dragon hardfork. Where appropriate refactorings have been backported to keep diffs as clean as possible.

The implementation of EIP 161 raises a number of points:

  • We delete EMPTY_ACCOUNT's immediately after touching them, rather than at the end of the transaction. This should be equivalent to EIP 161, but there are complicated edge cases that I am still investigating. If neccesary this will be dealt with in a future patch.
  • I have removed touch_account() prior to Spurious Dragon (where it was unnecessary) and have reinstated it in Spurious Dragon with a new meaning.

I have increased the rate of DB commits during the Shanghai DOS attacks and the State Clearing. During these periods ~19 million empty accounts were created and then deleted and the DB cannot cope with 1000 block transactions during this time.

Diff from Tangerine Whistle
diff -r -U3 src/ethereum/tangerine_whistle/__init__.py src/ethereum/spurious_dragon/__init__.py
--- src/ethereum/tangerine_whistle/__init__.py	2022-01-24 09:12:20.429979867 -0500
+++ src/ethereum/spurious_dragon/__init__.py	2022-01-24 09:17:21.829967506 -0500
@@ -1,9 +1,9 @@
 """
-Ethereum Tangerine Whistle Hardfork
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Ethereum Spurious Dragon Hardfork
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-The fourth Ethereum hardfork.
+The fifth Ethereum hardfork.
 """
 
-MAINNET_FORK_BLOCK = 2463000
+MAINNET_FORK_BLOCK = 2675000
 CHAIN_ID = 1
diff -r -U3 src/ethereum/tangerine_whistle/spec.py src/ethereum/spurious_dragon/spec.py
--- src/ethereum/tangerine_whistle/spec.py	2022-01-24 09:12:19.609979900 -0500
+++ src/ethereum/spurious_dragon/spec.py	2022-01-24 09:18:46.619964016 -0500
@@ -22,7 +22,7 @@
 
 from .. import crypto, rlp
 from ..base_types import U256, U256_CEIL_VALUE, Bytes, Uint
-from . import vm
+from . import CHAIN_ID, vm
 from .bloom import logs_bloom
 from .eth_types import (
     TX_BASE_COST,
@@ -41,10 +41,12 @@
 )
 from .state import (
     State,
+    account_exists,
     create_ether,
     destroy_account,
     get_account,
     increment_nonce,
+    is_account_empty,
     set_account_balance,
     state_root,
 )
@@ -529,6 +531,7 @@
         refund_counter,
         logs,
         accounts_to_delete,
+        touched_accounts,
         has_erred,
     ) = process_message_call(message, env)
 
@@ -555,6 +558,13 @@
     for address in accounts_to_delete:
         destroy_account(env.state, address)
 
+    for address in touched_accounts:
+        should_delete = account_exists(
+            env.state, address
+        ) and is_account_empty(env.state, address)
+        if should_delete:
+            destroy_account(env.state, address)
+
     return total_gas_used, logs
 
 
@@ -622,20 +632,52 @@
     """
     v, r, s = tx.v, tx.r, tx.s
 
-    #  if v > 28:
-    #      v = v - (chain_id*2+8)
-
-    ensure(v == 27 or v == 28)
     ensure(0 < r and r < SECP256K1N)
     ensure(0 < s and s <= SECP256K1N // 2)
 
-    public_key = crypto.secp256k1_recover(r, s, v - 27, signing_hash(tx))
+    if v == 27 or v == 28:
+        public_key = crypto.secp256k1_recover(
+            r, s, v - 27, signing_hash_legacy(tx)
+        )
+    else:
+        ensure(v == 35 + CHAIN_ID * 2 or v == 36 + CHAIN_ID * 2)
+        public_key = crypto.secp256k1_recover(
+            r, s, v - 35 - CHAIN_ID * 2, signing_hash_155(tx)
+        )
     return Address(crypto.keccak256(public_key)[12:32])
 
 
-def signing_hash(tx: Transaction) -> Hash32:
+def signing_hash_legacy(tx: Transaction) -> Hash32:
+    """
+    Compute the hash of a transaction used in a legacy (pre EIP 155) signature.
+
+    Parameters
+    ----------
+    tx :
+        Transaction of interest.
+
+    Returns
+    -------
+    hash : `eth1spec.eth_types.Hash32`
+        Hash of the transaction.
+    """
+    return crypto.keccak256(
+        rlp.encode(
+            (
+                tx.nonce,
+                tx.gas_price,
+                tx.gas,
+                tx.to,
+                tx.value,
+                tx.data,
+            )
+        )
+    )
+
+
+def signing_hash_155(tx: Transaction) -> Hash32:
     """
-    Compute the hash of a transaction used in the signature.
+    Compute the hash of a transaction used in a EIP 155 signature.
 
     Parameters
     ----------
@@ -656,6 +698,9 @@
                 tx.to,
                 tx.value,
                 tx.data,
+                Uint(1),
+                Uint(0),
+                Uint(0),
             )
         )
     )
diff -r -U3 src/ethereum/tangerine_whistle/state.py src/ethereum/spurious_dragon/state.py
--- src/ethereum/tangerine_whistle/state.py	2022-01-24 09:08:51.153321801 -0500
+++ src/ethereum/spurious_dragon/state.py	2022-01-24 09:12:20.689979856 -0500
@@ -330,6 +330,30 @@
     return account.nonce != Uint(0) or account.code != b""
 
 
+def is_account_empty(state: State, address: Address) -> bool:
+    """
+    Checks if an account has non zero nonce, non empty code and non zero
+    balance.
+
+    Parameters
+    ----------
+    state:
+        The state
+    address:
+        Address of the account that needs to be checked.
+
+    Returns
+    -------
+    is_empty : `bool`
+        True if if an account has non zero nonce, non empty code and
+        non zero balance, False otherwise.
+    """
+    return (
+        not account_has_code_or_nonce(state, address)
+        and get_account(state, address).balance == 0
+    )
+
+
 def modify_state(
     state: State, address: Address, f: Callable[[Account], None]
 ) -> None:
diff -r -U3 src/ethereum/tangerine_whistle/trie.py src/ethereum/spurious_dragon/trie.py
--- src/ethereum/tangerine_whistle/trie.py	2022-01-11 23:05:53.145887982 -0500
+++ src/ethereum/spurious_dragon/trie.py	2022-01-24 09:12:19.626646566 -0500
@@ -29,7 +29,7 @@
     cast,
 )
 
-import ethereum.dao_fork.trie
+import ethereum.tangerine_whistle.trie
 from ethereum.utils.ensure import ensure
 from ethereum.utils.hexadecimal import hex_to_bytes
 
@@ -165,7 +165,7 @@
     elif isinstance(node, Bytes):
         return node
     else:
-        return ethereum.dao_fork.trie.encode_node(node, storage_root)
+        return ethereum.tangerine_whistle.trie.encode_node(node, storage_root)
 
 
 @dataclass
diff -r -U3 src/ethereum/tangerine_whistle/utils/address.py src/ethereum/spurious_dragon/utils/address.py
--- src/ethereum/tangerine_whistle/utils/address.py	2022-01-11 23:05:53.142554649 -0500
+++ src/ethereum/spurious_dragon/utils/address.py	2022-01-24 09:12:19.626646566 -0500
@@ -1,6 +1,6 @@
 """
-Tangerine Whistle Utility Functions For Addresses
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Spurious Dragon Utility Functions For Addresses
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 .. contents:: Table of Contents
     :backlinks: none
@@ -9,7 +9,7 @@
 Introduction
 ------------
 
-Address specific functions used in this tangerine whistle version of
+Address specific functions used in this spurious dragon version of
 specification.
 """
 from typing import Union
@@ -52,7 +52,7 @@
 
     Returns
     -------
-    address: `ethereum.tangerine_whistle.eth_types.Address`
+    address: `ethereum.spurious_dragon.eth_types.Address`
         The computed address of the new account.
     """
     computed_address = keccak256(rlp.encode([address, nonce]))
diff -r -U3 src/ethereum/tangerine_whistle/utils/hexadecimal.py src/ethereum/spurious_dragon/utils/hexadecimal.py
--- src/ethereum/tangerine_whistle/utils/hexadecimal.py	2022-01-11 23:05:53.142554649 -0500
+++ src/ethereum/spurious_dragon/utils/hexadecimal.py	2022-01-24 09:12:19.626646566 -0500
@@ -10,7 +10,7 @@
 ------------
 
 Hexadecimal utility functions used in this specification, specific to
-Tangerine Whistle types.
+Spurious Dragon types.
 """
 from ethereum.utils.hexadecimal import remove_hex_prefix
 
diff -r -U3 src/ethereum/tangerine_whistle/utils/__init__.py src/ethereum/spurious_dragon/utils/__init__.py
--- src/ethereum/tangerine_whistle/utils/__init__.py	2022-01-11 23:05:53.142554649 -0500
+++ src/ethereum/spurious_dragon/utils/__init__.py	2022-01-24 09:12:19.626646566 -0500
@@ -1,6 +1,6 @@
 """
-Tangerine Whistle Utility Functions
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Spurious Dragon Utility Functions
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 .. contents:: Table of Contents
     :backlinks: none
@@ -9,5 +9,5 @@
 Introduction
 ------------
 
-Utility functions used in this tangerine whistle version of specification.
+Utility functions used in this spurious dragon version of specification.
 """
diff -r -U3 src/ethereum/tangerine_whistle/utils/json.py src/ethereum/spurious_dragon/utils/json.py
--- src/ethereum/tangerine_whistle/utils/json.py	2022-01-11 23:05:53.142554649 -0500
+++ src/ethereum/spurious_dragon/utils/json.py	2022-01-24 09:12:19.626646566 -0500
@@ -1,6 +1,6 @@
 """
-Tangerine Whistle Utilities Json
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Spurious Dragon Utilities Json
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 .. contents:: Table of Contents
     :backlinks: none
@@ -9,7 +9,7 @@
 Introduction
 ------------
 
-Json specific utilities used in this tangerine whistle version of
+Json specific utilities used in this spurious dragon version of
 specification.
 """
 from typing import Any, Dict, Tuple
diff -r -U3 src/ethereum/tangerine_whistle/utils/message.py src/ethereum/spurious_dragon/utils/message.py
--- src/ethereum/tangerine_whistle/utils/message.py	2022-01-11 23:05:53.142554649 -0500
+++ src/ethereum/spurious_dragon/utils/message.py	2022-01-24 09:12:19.626646566 -0500
@@ -1,6 +1,6 @@
 """
-Tangerine Whistle Utility Functions For The Message Data-structure
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Spurious Dragon Utility Functions For The Message Data-structure
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 .. contents:: Table of Contents
     :backlinks: none
@@ -9,7 +9,7 @@
 Introduction
 ------------
 
-Message specific functions used in this tangerine whistle version of
+Message specific functions used in this spurious dragon version of
 specification.
 """
 from typing import Optional, Union
@@ -58,7 +58,7 @@
 
     Returns
     -------
-    message: `ethereum.tangerine_whistle.vm.Message`
+    message: `ethereum.spurious_dragon.vm.Message`
         Items containing contract creation or message call specific data.
     """
     if isinstance(target, Bytes0):
diff -r -U3 src/ethereum/tangerine_whistle/vm/gas.py src/ethereum/spurious_dragon/vm/gas.py
--- src/ethereum/tangerine_whistle/vm/gas.py	2022-01-24 09:12:20.303313205 -0500
+++ src/ethereum/spurious_dragon/vm/gas.py	2022-01-24 09:20:41.279959293 -0500
@@ -28,7 +28,7 @@
 GAS_MID = U256(8)
 GAS_HIGH = U256(10)
 GAS_EXPONENTIATION = U256(10)
-GAS_EXPONENTIATION_PER_BYTE = U256(10)
+GAS_EXPONENTIATION_PER_BYTE = U256(50)
 GAS_MEMORY = U256(3)
 GAS_KECCAK256 = U256(30)
 GAS_KECCAK256_WORD = U256(6)
@@ -71,7 +71,7 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `gas_left` is less than `amount`.
     """
     if gas_left < amount:
diff -r -U3 src/ethereum/tangerine_whistle/vm/__init__.py src/ethereum/spurious_dragon/vm/__init__.py
--- src/ethereum/tangerine_whistle/vm/__init__.py	2022-01-11 23:05:53.155887982 -0500
+++ src/ethereum/spurious_dragon/vm/__init__.py	2022-01-24 09:12:20.689979856 -0500
@@ -14,7 +14,7 @@
 """
 
 from dataclasses import dataclass
-from typing import List, Optional, Set, Tuple, Union
+from typing import Dict, List, Optional, Set, Tuple, Union
 
 from ethereum.base_types import U256, Bytes, Bytes0, Uint
 from ethereum.crypto import Hash32
@@ -77,5 +77,6 @@
     running: bool
     message: Message
     output: Bytes
-    accounts_to_delete: Set[Address]
+    accounts_to_delete: Dict[Address, Address]
     has_erred: bool
+    children: List["Evm"]
diff -r -U3 src/ethereum/tangerine_whistle/vm/instructions/arithmetic.py src/ethereum/spurious_dragon/vm/instructions/arithmetic.py
--- src/ethereum/tangerine_whistle/vm/instructions/arithmetic.py	2022-01-24 09:12:20.303313205 -0500
+++ src/ethereum/spurious_dragon/vm/instructions/arithmetic.py	2022-01-24 09:25:17.356614607 -0500
@@ -39,9 +39,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `2`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `3`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_VERY_LOW)
@@ -67,9 +67,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `2`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `3`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_VERY_LOW)
@@ -95,9 +95,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `2`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `5`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_LOW)
@@ -123,9 +123,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `2`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `5`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_LOW)
@@ -154,9 +154,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `2`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `5`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_LOW)
@@ -189,9 +189,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `2`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `5`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_LOW)
@@ -220,9 +220,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `2`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `5`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_LOW)
@@ -252,9 +252,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `3`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `8`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_MID)
@@ -285,9 +285,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `3`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `8`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_MID)
@@ -318,7 +318,7 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `2`.
     """
     base = Uint(pop(evm.stack))
@@ -352,9 +352,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `2`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `5`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_LOW)
diff -r -U3 src/ethereum/tangerine_whistle/vm/instructions/bitwise.py src/ethereum/spurious_dragon/vm/instructions/bitwise.py
--- src/ethereum/tangerine_whistle/vm/instructions/bitwise.py	2022-01-24 09:12:19.613313233 -0500
+++ src/ethereum/spurious_dragon/vm/instructions/bitwise.py	2022-01-24 09:25:39.203280375 -0500
@@ -31,9 +31,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `2`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `GAS_VERY_LOW`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_VERY_LOW)
@@ -56,9 +56,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `2`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `GAS_VERY_LOW`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_VERY_LOW)
@@ -81,9 +81,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `2`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `GAS_VERY_LOW`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_VERY_LOW)
@@ -106,9 +106,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `1`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `GAS_VERY_LOW`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_VERY_LOW)
@@ -131,9 +131,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `2`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `GAS_VERY_LOW`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_VERY_LOW)
diff -r -U3 src/ethereum/tangerine_whistle/vm/instructions/block.py src/ethereum/spurious_dragon/vm/instructions/block.py
--- src/ethereum/tangerine_whistle/vm/instructions/block.py	2022-01-24 09:12:19.613313233 -0500
+++ src/ethereum/spurious_dragon/vm/instructions/block.py	2022-01-24 09:25:58.956612896 -0500
@@ -31,9 +31,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `1`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `20`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_BLOCK_HASH)
@@ -68,9 +68,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackOverflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackOverflowError`
         If `len(stack)` is equal to `1024`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `2`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_BASE)
@@ -94,9 +94,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackOverflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackOverflowError`
         If `len(stack)` is equal to `1024`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `2`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_BASE)
@@ -119,9 +119,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackOverflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackOverflowError`
         If `len(stack)` is equal to `1024`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `2`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_BASE)
@@ -144,9 +144,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackOverflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackOverflowError`
         If `len(stack)` is equal to `1024`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `2`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_BASE)
@@ -169,9 +169,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackOverflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackOverflowError`
         If `len(stack)` is equal to `1024`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `2`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_BASE)
diff -r -U3 src/ethereum/tangerine_whistle/vm/instructions/comparison.py src/ethereum/spurious_dragon/vm/instructions/comparison.py
--- src/ethereum/tangerine_whistle/vm/instructions/comparison.py	2022-01-24 09:12:19.613313233 -0500
+++ src/ethereum/spurious_dragon/vm/instructions/comparison.py	2022-01-24 09:26:18.283278772 -0500
@@ -31,9 +31,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `2`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `GAS_VERY_LOW`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_VERY_LOW)
@@ -58,9 +58,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `2`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `GAS_VERY_LOW`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_VERY_LOW)
@@ -86,9 +86,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `2`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `GAS_VERY_LOW`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_VERY_LOW)
@@ -113,9 +113,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `2`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `GAS_VERY_LOW`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_VERY_LOW)
@@ -141,9 +141,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `2`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `GAS_VERY_LOW`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_VERY_LOW)
@@ -169,9 +169,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `1`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `GAS_VERY_LOW`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_VERY_LOW)
diff -r -U3 src/ethereum/tangerine_whistle/vm/instructions/control_flow.py src/ethereum/spurious_dragon/vm/instructions/control_flow.py
--- src/ethereum/tangerine_whistle/vm/instructions/control_flow.py	2022-01-24 09:12:19.613313233 -0500
+++ src/ethereum/spurious_dragon/vm/instructions/control_flow.py	2022-01-24 09:26:37.256611324 -0500
@@ -44,15 +44,15 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.InvalidJumpDestError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.InvalidJumpDestError`
         If the jump destination doesn't meet any of the following criteria:
             * The jump destination is less than the length of the code.
             * The jump destination should have the `JUMPDEST` opcode (0x5B).
             * The jump destination shouldn't be part of the data corresponding
             to `PUSH-N` opcodes.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `1`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `8`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_MID)
@@ -77,15 +77,15 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.InvalidJumpDestError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.InvalidJumpDestError`
         If the jump destination doesn't meet any of the following criteria:
             * The jump destination is less than the length of the code.
             * The jump destination should have the `JUMPDEST` opcode (0x5B).
             * The jump destination shouldn't be part of the data corresponding
             to `PUSH-N` opcodes.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `2`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `10`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_HIGH)
@@ -115,9 +115,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackOverflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackOverflowError`
         If `len(stack)` is more than `1023`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `2`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_BASE)
@@ -137,9 +137,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackOverflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackOverflowError`
         If `len(stack)` is more than `1023`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `2`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_BASE)
@@ -160,7 +160,7 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `1`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_JUMPDEST)
diff -r -U3 src/ethereum/tangerine_whistle/vm/instructions/environment.py src/ethereum/spurious_dragon/vm/instructions/environment.py
--- src/ethereum/tangerine_whistle/vm/instructions/environment.py	2022-01-24 09:12:19.613313233 -0500
+++ src/ethereum/spurious_dragon/vm/instructions/environment.py	2022-01-24 09:26:59.416610411 -0500
@@ -44,7 +44,7 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `2`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_BASE)
@@ -64,9 +64,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `1`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `20`.
     """
     # TODO: There are no test cases against this function. Need to write
@@ -95,7 +95,7 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `2`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_BASE)
@@ -115,7 +115,7 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `2`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_BASE)
@@ -135,7 +135,7 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `2`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_BASE)
@@ -156,9 +156,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `1`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `3`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_VERY_LOW)
@@ -186,7 +186,7 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `2`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_BASE)
@@ -209,7 +209,7 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `3`.
     """
     # Converting below to Uint as though the start indices may belong to U256,
@@ -261,7 +261,7 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `2`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_BASE)
@@ -284,7 +284,7 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `3`.
     """
     # Converting below to Uint as though the start indices may belong to U256,
@@ -337,7 +337,7 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `2`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_BASE)
@@ -357,9 +357,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `1`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `20`.
     """
     # TODO: There are no test cases against this function. Need to write
@@ -387,7 +387,7 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `4`.
     """
     # TODO: There are no test cases against this function. Need to write
diff -r -U3 src/ethereum/tangerine_whistle/vm/instructions/keccak.py src/ethereum/spurious_dragon/vm/instructions/keccak.py
--- src/ethereum/tangerine_whistle/vm/instructions/keccak.py	2022-01-24 09:12:19.613313233 -0500
+++ src/ethereum/spurious_dragon/vm/instructions/keccak.py	2022-01-24 09:27:10.653276615 -0500
@@ -43,7 +43,7 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `2`.
     """
     # Converting memory_start_index to Uint as memory_end_index can
diff -r -U3 src/ethereum/tangerine_whistle/vm/instructions/log.py src/ethereum/spurious_dragon/vm/instructions/log.py
--- src/ethereum/tangerine_whistle/vm/instructions/log.py	2022-01-24 09:12:19.613313233 -0500
+++ src/ethereum/spurious_dragon/vm/instructions/log.py	2022-01-24 09:37:49.276583756 -0500
@@ -46,7 +46,7 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `2 + num_topics`.
     """
     # Converting memory_start_index to Uint as memory_start_index + size - 1
diff -r -U3 src/ethereum/tangerine_whistle/vm/instructions/memory.py src/ethereum/spurious_dragon/vm/instructions/memory.py
--- src/ethereum/tangerine_whistle/vm/instructions/memory.py	2022-01-24 09:12:19.613313233 -0500
+++ src/ethereum/spurious_dragon/vm/instructions/memory.py	2022-01-24 09:38:05.536583090 -0500
@@ -39,9 +39,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `2`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than
         `3` + gas needed to extend memeory.
     """
@@ -79,9 +79,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `2`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than
         `3` + gas needed to extend memory.
     """
@@ -119,9 +119,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `1`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than
         `3` + gas needed to extend memory.
     """
@@ -159,7 +159,7 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `2`
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_BASE)
diff -r -U3 src/ethereum/tangerine_whistle/vm/instructions/stack.py src/ethereum/spurious_dragon/vm/instructions/stack.py
--- src/ethereum/tangerine_whistle/vm/instructions/stack.py	2022-01-24 09:12:19.613313233 -0500
+++ src/ethereum/spurious_dragon/vm/instructions/stack.py	2022-01-24 10:01:56.109857758 -0500
@@ -33,9 +33,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `1`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `2`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_BASE)
@@ -59,9 +59,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackOverflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackOverflowError`
         If `len(stack)` is equals `1024`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `3`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_VERY_LOW)
@@ -89,7 +89,7 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `3`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_VERY_LOW)
@@ -120,7 +120,7 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `3`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_VERY_LOW)
diff -r -U3 src/ethereum/tangerine_whistle/vm/instructions/storage.py src/ethereum/spurious_dragon/vm/instructions/storage.py
--- src/ethereum/tangerine_whistle/vm/instructions/storage.py	2022-01-24 09:12:19.616646567 -0500
+++ src/ethereum/spurious_dragon/vm/instructions/storage.py	2022-01-24 09:49:45.119887746 -0500
@@ -36,9 +36,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `1`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `50`.
     """
     evm.gas_left = subtract_gas(evm.gas_left, GAS_SLOAD)
@@ -62,9 +62,9 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `len(stack)` is less than `2`.
-    :py:class:`~ethereum.tangerine_whistle.vm.error.OutOfGasError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.OutOfGasError`
         If `evm.gas_left` is less than `20000`.
     """
     key = pop(evm.stack).to_be_bytes32()
diff -r -U3 src/ethereum/tangerine_whistle/vm/instructions/system.py src/ethereum/spurious_dragon/vm/instructions/system.py
--- src/ethereum/tangerine_whistle/vm/instructions/system.py	2022-01-24 09:12:19.616646567 -0500
+++ src/ethereum/spurious_dragon/vm/instructions/system.py	2022-01-24 10:02:18.036523526 -0500
@@ -19,6 +19,7 @@
     account_has_code_or_nonce,
     get_account,
     increment_nonce,
+    is_account_empty,
     set_account_balance,
 )
 from ...utils.address import compute_contract_address, to_address
@@ -117,13 +118,13 @@
         should_transfer_value=True,
     )
     child_evm = process_create_message(child_message, evm.env)
+    evm.children.append(child_evm)
     if child_evm.has_erred:
         push(evm.stack, U256(0))
     else:
         push(evm.stack, U256.from_be_bytes(child_evm.message.current_target))
     evm.gas_left += child_evm.gas_left
     evm.refund_counter += child_evm.refund_counter
-    evm.accounts_to_delete.update(child_evm.accounts_to_delete)
     evm.logs += child_evm.logs
 
 
@@ -185,8 +186,12 @@
         evm.memory, memory_input_start_position, memory_input_size
     )
 
-    _account_exists = account_exists(evm.env.state, to)
-    create_gas_cost = U256(0) if _account_exists else GAS_NEW_ACCOUNT
+    is_account_alive = account_exists(
+        evm.env.state, to
+    ) and not is_account_empty(evm.env.state, to)
+    create_gas_cost = (
+        U256(0) if is_account_alive or value == 0 else GAS_NEW_ACCOUNT
+    )
     transfer_gas_cost = U256(0) if value == 0 else GAS_CALL_VALUE
     extra_gas = u256_safe_add(
         create_gas_cost,
@@ -228,6 +233,7 @@
         should_transfer_value=True,
     )
     child_evm = process_message(child_message, evm.env)
+    evm.children.append(child_evm)
 
     if child_evm.has_erred:
         push(evm.stack, U256(0))
@@ -242,7 +248,6 @@
     )
     evm.gas_left += child_evm.gas_left
     evm.refund_counter += child_evm.refund_counter
-    evm.accounts_to_delete.update(child_evm.accounts_to_delete)
     evm.logs += child_evm.logs
 
 
@@ -321,6 +326,7 @@
     )
 
     child_evm = process_message(child_message, evm.env)
+    evm.children.append(child_evm)
     if child_evm.has_erred:
         push(evm.stack, U256(0))
     else:
@@ -333,7 +339,6 @@
     )
     evm.gas_left += child_evm.gas_left
     evm.refund_counter += child_evm.refund_counter
-    evm.accounts_to_delete.update(child_evm.accounts_to_delete)
     evm.logs += child_evm.logs
 
 
@@ -349,7 +354,15 @@
     evm.gas_left = subtract_gas(evm.gas_left, GAS_SELF_DESTRUCT)
     beneficiary = to_address(pop(evm.stack))
 
-    if not account_exists(evm.env.state, beneficiary):
+    originator = evm.message.current_target
+    beneficiary_balance = get_account(evm.env.state, beneficiary).balance
+    originator_balance = get_account(evm.env.state, originator).balance
+
+    is_dead_account = not account_exists(
+        evm.env.state, beneficiary
+    ) or is_account_empty(evm.env.state, beneficiary)
+
+    if is_dead_account and originator_balance != 0:
         evm.gas_left = subtract_gas(
             evm.gas_left, GAS_SELF_DESTRUCT_NEW_ACCOUNT
         )
@@ -368,7 +381,7 @@
     set_account_balance(evm.env.state, originator, U256(0))
 
     # register account for deletion
-    evm.accounts_to_delete.add(originator)
+    evm.accounts_to_delete[originator] = beneficiary
 
     # HALT the execution
     evm.running = False
@@ -441,6 +454,7 @@
     )
 
     child_evm = process_message(child_message, evm.env)
+    evm.children.append(child_evm)
     if child_evm.has_erred:
         push(evm.stack, U256(0))
     else:
@@ -453,5 +467,4 @@
     )
     evm.gas_left += child_evm.gas_left
     evm.refund_counter += child_evm.refund_counter
-    evm.accounts_to_delete.update(child_evm.accounts_to_delete)
     evm.logs += child_evm.logs
diff -r -U3 src/ethereum/tangerine_whistle/vm/interpreter.py src/ethereum/spurious_dragon/vm/interpreter.py
--- src/ethereum/tangerine_whistle/vm/interpreter.py	2022-01-24 09:12:19.616646567 -0500
+++ src/ethereum/spurious_dragon/vm/interpreter.py	2022-01-24 10:58:35.859718634 -0500
@@ -11,10 +11,10 @@
 
 A straightforward interpreter that executes EVM code.
 """
-from typing import Set, Tuple, Union
+from typing import Iterable, Set, Tuple, Union
 
 from ethereum.base_types import U256, Bytes0, Uint
-from ethereum.utils.ensure import EnsureError
+from ethereum.utils.ensure import EnsureError, ensure
 
 from ..eth_types import Address, Log
 from ..state import (
@@ -22,11 +22,13 @@
     begin_transaction,
     commit_transaction,
     get_account,
+    increment_nonce,
     move_ether,
     rollback_transaction,
     set_code,
     touch_account,
 )
+from ..utils.address import to_address
 from ..vm import Message
 from ..vm.error import (
     InsufficientFunds,
@@ -44,11 +46,20 @@
 from .runtime import get_valid_jump_destinations
 
 STACK_DEPTH_LIMIT = U256(1024)
+MAX_CODE_SIZE = 0x6000
+THREE = to_address(Uint(3))
 
 
 def process_message_call(
     message: Message, env: Environment
-) -> Tuple[U256, U256, Union[Tuple[()], Tuple[Log, ...]], Set[Address], bool]:
+) -> Tuple[
+    U256,
+    U256,
+    Union[Tuple[()], Tuple[Log, ...]],
+    Set[Address],
+    Iterable[Address],
+    bool,
+]:
     """
     If `message.current` is empty then it creates a smart contract
     else it executes a call from the `message.caller` to the `message.target`.
@@ -77,19 +88,21 @@
             env.state, message.current_target
         )
         if is_collision:
-            return U256(0), U256(0), tuple(), set(), True
+            return U256(0), U256(0), tuple(), set(), set(), True
         else:
             evm = process_create_message(message, env)
     else:
         evm = process_message(message, env)
 
-    evm.refund_counter += len(evm.accounts_to_delete) * REFUND_SELF_DESTRUCT
+    accounts_to_delete = collect_accounts_to_delete(evm, set())
+    evm.refund_counter += len(accounts_to_delete) * REFUND_SELF_DESTRUCT
 
     return (
         evm.gas_left,
         evm.refund_counter,
         evm.logs,
-        evm.accounts_to_delete,
+        accounts_to_delete,
+        collect_touched_accounts(evm),
         evm.has_erred,
     )
 
@@ -107,23 +120,25 @@
 
     Returns
     -------
-    evm: :py:class:`~ethereum.tangerine_whistle.vm.Evm`
+    evm: :py:class:`~ethereum.spurious_dragon.vm.Evm`
         Items containing execution specific objects.
     """
     # take snapshot of state before processing the message
     begin_transaction(env.state)
 
+    increment_nonce(env.state, message.current_target)
     evm = process_message(message, env)
     if not evm.has_erred:
         contract_code = evm.output
         contract_code_gas = len(contract_code) * GAS_CODE_DEPOSIT
         try:
             evm.gas_left = subtract_gas(evm.gas_left, contract_code_gas)
+            ensure(len(contract_code) <= MAX_CODE_SIZE, OutOfGasError)
         except OutOfGasError:
             rollback_transaction(env.state)
             evm.gas_left = U256(0)
             evm.logs = ()
-            evm.accounts_to_delete = set()
+            evm.accounts_to_delete = dict()
             evm.refund_counter = U256(0)
             evm.has_erred = True
         else:
@@ -147,7 +162,7 @@
 
     Returns
     -------
-    evm: :py:class:`~ethereum.tangerine_whistle.vm.Evm`
+    evm: :py:class:`~ethereum.spurious_dragon.vm.Evm`
         Items containing execution specific objects
     """
     if message.depth > STACK_DEPTH_LIMIT:
@@ -211,8 +226,9 @@
         running=True,
         message=message,
         output=b"",
-        accounts_to_delete=set(),
+        accounts_to_delete=dict(),
         has_erred=False,
+        children=[],
     )
     try:
 
@@ -239,7 +255,7 @@
     ):
         evm.gas_left = U256(0)
         evm.logs = ()
-        evm.accounts_to_delete = set()
+        evm.accounts_to_delete = dict()
         evm.refund_counter = U256(0)
         evm.has_erred = True
     except (
@@ -249,3 +265,94 @@
         evm.has_erred = True
     finally:
         return evm
+
+
+def collect_touched_accounts(
+    evm: Evm, ancestor_had_error: bool = False
+) -> Iterable[Address]:
+    """
+    Collect all of the accounts that *may* need to be deleted based on
+    `EIP-161 <https://eips.ethereum.org/EIPS/eip-161>`_.
+    Checking whether they *do* need to be deleted happens in the caller.
+    See also: https://github.com/ethereum/EIPs/issues/716
+
+    Parameters
+    ----------
+    evm :
+        The current EVM frame.
+    ancestor_had_error :
+        True if the ancestors of the evm object erred else False
+
+    Returns
+    -------
+    touched_accounts: `typing.Iterable`
+        returns all the accounts that were touched and may need to be deleted.
+    """
+    # collect the coinbase account if it was touched via zero-fee transfer
+    if (evm.message.caller == evm.env.origin) and evm.env.gas_price == 0:
+        yield evm.env.coinbase
+
+    # collect those explicitly marked for deletion
+    # ("beneficiary" is of SELFDESTRUCT)
+    for beneficiary in sorted(set(evm.accounts_to_delete.values())):
+        if evm.has_erred or ancestor_had_error:
+            # Special case to account for geth+parity bug
+            # https://github.com/ethereum/EIPs/issues/716
+            if beneficiary == THREE:
+                yield beneficiary
+            continue
+        else:
+            yield beneficiary
+
+    # collect account directly addressed
+    if evm.message.target != Bytes0(b""):
+        if evm.has_erred or ancestor_had_error:
+            # collect RIPEMD160 precompile even if ancestor evm had error.
+            # otherwise, skip collection from children of erred-out evm objects
+            if evm.message.target == THREE:
+                # mypy is a little dumb;
+                # Although we have evm.message.target != Bytes0(b""),
+                # my expects target to be Union[Bytes0, Address]
+                yield evm.message.target  # type: ignore
+        else:
+            # mypy is a little dumb;
+            # Although we have evm.message.target != Bytes0(b""),
+            # my expects target to be Union[Bytes0, Address]
+            yield evm.message.target  # type: ignore
+
+    # recurse into nested computations
+    # (even erred ones, since looking for RIPEMD160)
+    for child in evm.children:
+        yield from collect_touched_accounts(
+            child, ancestor_had_error=(evm.has_erred or ancestor_had_error)
+        )
+
+
+def collect_accounts_to_delete(
+    evm: Evm, accounts_to_delete: Set[Address]
+) -> Set[Address]:
+    """
+    Collects all the accounts that need to deleted from the `evm` object and
+    its children
+
+    Parameters
+    ----------
+    evm :
+        The current EVM frame.
+    accounts_to_delete :
+        list of accounts that need to be deleted.
+        Note: An empty set should be passed to this parameter. This set
+        is used to store the results obtained by recursively iterating over the
+        child evm objects
+
+    Returns
+    -------
+    touched_accounts: `set`
+        returns all the accounts that were touched and may need to be deleted.
+    """
+    if not evm.has_erred:
+        for address in evm.accounts_to_delete.keys():
+            accounts_to_delete.add(address)
+        for child in evm.children:
+            collect_accounts_to_delete(child, accounts_to_delete)
+    return accounts_to_delete
diff -r -U3 src/ethereum/tangerine_whistle/vm/stack.py src/ethereum/spurious_dragon/vm/stack.py
--- src/ethereum/tangerine_whistle/vm/stack.py	2022-01-24 09:12:19.616646567 -0500
+++ src/ethereum/spurious_dragon/vm/stack.py	2022-01-24 10:04:05.593185777 -0500
@@ -35,7 +35,7 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackUnderflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackUnderflowError`
         If `stack` is empty.
     """
     if len(stack) == 0:
@@ -58,7 +58,7 @@
 
     Raises
     ------
-    :py:class:`~ethereum.tangerine_whistle.vm.error.StackOverflowError`
+    :py:class:`~ethereum.spurious_dragon.vm.error.StackOverflowError`
         If `len(stack)` is `1024`.
     """
     if len(stack) == 1024:

Non-spurious Komodo Dragons

Put a link to a cute animal picture inside the parenthesis-->

@ethereum ethereum locked and limited conversation to collaborators Jan 18, 2022
@ethereum ethereum unlocked this conversation Jan 19, 2022
@petertdavies
Copy link
Contributor Author

There are two somewhat subtle points of the EIP 161.

  1. Empty accounts are allowed to have storage, this means deleting an empty account must use destroy_account(). If you don't do this you violate the invariants of the State interface.
  2. The condition d) of EIP 161 (empty accounts are deleted at the end of the transaction) is important. This is untested and not entirely obvious.

Condition d) matters because of the following edge case:

Suppose X is an empty account with storage, then in the same transaction:

  1. Make a zero value call to X
  2. Make a non-zero value call to X

Without d) X would deleted in 1 and reinstated in 2 without storage. With d) X is never deleted and retains its storage.

Empty accounts with storage exist on mainnet and so getting this wrong would be a consensus issue. All clients seem to have this right though.

@voith is doing some work on this PR so I'm not going to fix anything until he has finished.

@voith
Copy link
Contributor

voith commented Jan 20, 2022

@voith is doing some work on this PR so I'm not going to fix anything until he has finished.

I have submitted a PR to Peters repo: petertdavies#1

@petertdavies
Copy link
Contributor Author

I have added @voith's implementation of EIP 161 to the PR. I have rebase'd out my previous version.

@petertdavies
Copy link
Contributor Author

touch_account() could benefit from a rename, but that doesn't need to be done in this PR.

@codecov-commenter
Copy link

Codecov Report

Merging #423 (c232e43) into master (f04c856) will increase coverage by 3.91%.
The diff coverage is 96.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #423      +/-   ##
==========================================
+ Coverage   75.53%   79.44%   +3.91%     
==========================================
  Files         152      187      +35     
  Lines        7943     9815    +1872     
==========================================
+ Hits         6000     7798    +1798     
- Misses       1943     2017      +74     
Flag Coverage Δ
unittests 79.44% <96.00%> (+3.91%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
src/ethereum/dao_fork/vm/gas.py 0.00% <0.00%> (ø)
...rc/ethereum/dao_fork/vm/instructions/arithmetic.py 0.00% <0.00%> (ø)
src/ethereum/spurious_dragon/utils/json.py 52.94% <52.94%> (ø)
src/ethereum/spurious_dragon/spec.py 80.71% <80.71%> (ø)
...rious_dragon/vm/precompiled_contracts/ecrecover.py 93.33% <93.33%> (ø)
src/ethereum/spurious_dragon/vm/interpreter.py 93.57% <93.57%> (ø)
src/ethereum/spurious_dragon/utils/message.py 94.73% <94.73%> (ø)
...ethereum/spurious_dragon/vm/instructions/system.py 95.52% <95.52%> (ø)
.../ethereum/spurious_dragon/vm/instructions/block.py 96.87% <96.87%> (ø)
src/ethereum/spurious_dragon/trie.py 97.72% <97.72%> (ø)
... and 54 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update f04c856...c232e43. Read the comment docs.


Returns
-------
touched_accounts: `set`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
touched_accounts: `set`
accounts_to_delete: `set`

Returns
-------
touched_accounts: `set`
returns all the accounts that were touched and may need to be deleted.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
returns all the accounts that were touched and may need to be deleted.
returns all the accounts that have been `SELFDESTRUCT`'ed

# collect RIPEMD160 precompile even if ancestor evm had error.
# otherwise, skip collection from children of erred-out evm objects
if evm.message.target == THREE:
# mypy is a little dumb;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can get rid of this mypy error if change line 307 with:
if evm.message.target != Bytes0(b"") --> isinstance(evm.message.target, Bytes0)
(Haven't tested though)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would probably want isinstance(evm.message.target, Address), right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad, You're right.

Copy link
Contributor

@SamWilsn SamWilsn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the only major issue is the mypy ignore comment!

# collect RIPEMD160 precompile even if ancestor evm had error.
# otherwise, skip collection from children of erred-out evm objects
if evm.message.target == THREE:
# mypy is a little dumb;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would probably want isinstance(evm.message.target, Address), right?

@voith
Copy link
Contributor

voith commented Jan 25, 2022

@ultratwo will you be able to make the changes that sam has requested? I don't have permission to directly push.
If not, then I can submit a PR to your branch.

@SamWilsn SamWilsn merged commit 3cd671e into ethereum:master Jan 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

4 participants