Skip to content

Commit

Permalink
implement EIP-1153
Browse files Browse the repository at this point in the history
  • Loading branch information
gurukamath committed Nov 21, 2023
1 parent fcf2cd6 commit 8041452
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 7 deletions.
94 changes: 92 additions & 2 deletions src/ethereum/cancun/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,19 @@ class State:
_created_accounts: Set[Address] = field(default_factory=set)


@dataclass
class TransientStorage:
"""
Contains all information that is preserved between message calls
within a transaction.
"""

_tries: Dict[Address, Trie[Bytes, U256]] = field(default_factory=dict)
_snapshots: List[Dict[Address, Trie[Bytes, U256]]] = field(
default_factory=list
)


def close_state(state: State) -> None:
"""
Free resources held by the state. Used by optimized implementations to
Expand All @@ -57,7 +70,9 @@ def close_state(state: State) -> None:
del state._created_accounts


def begin_transaction(state: State) -> None:
def begin_transaction(
state: State, transient_storage: Optional[TransientStorage] = None
) -> None:
"""
Start a state transaction.
Expand All @@ -68,6 +83,8 @@ def begin_transaction(state: State) -> None:
----------
state : State
The state.
transient_storage : TransientStorage
The transient storage of the transaction.
"""
state._snapshots.append(
(
Expand All @@ -76,6 +93,11 @@ def begin_transaction(state: State) -> None:
)
)

if transient_storage is not None:
transient_storage._snapshots.append(
{k: copy_trie(t) for (k, t) in transient_storage._tries.items()}
)


def commit_transaction(state: State) -> None:
"""
Expand All @@ -91,7 +113,9 @@ def commit_transaction(state: State) -> None:
state._created_accounts.clear()


def rollback_transaction(state: State) -> None:
def rollback_transaction(
state: State, transient_storage: Optional[TransientStorage] = None
) -> None:
"""
Rollback a state transaction, resetting the state to the point when the
corresponding `start_transaction()` call was made.
Expand All @@ -100,11 +124,16 @@ def rollback_transaction(state: State) -> None:
----------
state : State
The state.
transient_storage : TransientStorage
The transient storage of the transaction.
"""
state._main_trie, state._storage_tries = state._snapshots.pop()
if not state._snapshots:
state._created_accounts.clear()

if transient_storage:
transient_storage._tries = transient_storage._snapshots.pop()


def get_account(state: State, address: Address) -> Account:
"""
Expand Down Expand Up @@ -603,3 +632,64 @@ def get_storage_original(state: State, address: Address, key: Bytes) -> U256:
assert isinstance(original_value, U256)

return original_value


def get_transient_storage(
transient_storage: TransientStorage, address: Address, key: Bytes
) -> U256:
"""
Get a value at a storage key on an account from transient storage.
Returns `U256(0)` if the storage key has not been set previously.
Parameters
----------
transient_storage: `TransientStorage`
The transient storage
address : `Address`
Address of the account.
key : `Bytes`
Key to lookup.
Returns
-------
value : `U256`
Value at the key.
"""
trie = transient_storage._tries.get(address)
if trie is None:
return U256(0)

value = trie_get(trie, key)

assert isinstance(value, U256)
return value


def set_transient_storage(
transient_storage: TransientStorage,
address: Address,
key: Bytes,
value: U256,
) -> None:
"""
Set a value at a storage key on an account. Setting to `U256(0)` deletes
the key.
Parameters
----------
transient_storage: `TransientStorage`
The transient storage
address : `Address`
Address of the account.
key : `Bytes`
Key to set.
value : `U256`
Value to set at the key.
"""
trie = transient_storage._tries.get(address)
if trie is None:
trie = Trie(secured=True, default=U256(0))
transient_storage._tries[address] = trie
trie_set(trie, key, value)
if trie._data == {}:
del transient_storage._tries[address]
3 changes: 2 additions & 1 deletion src/ethereum/cancun/vm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from ethereum.crypto.hash import Hash32

from ..fork_types import Address, Log
from ..state import State, account_exists_and_is_empty
from ..state import State, TransientStorage, account_exists_and_is_empty
from .precompiled_contracts import RIPEMD160_ADDRESS

__all__ = ("Environment", "Evm", "Message")
Expand Down Expand Up @@ -91,6 +91,7 @@ class Evm:
error: Optional[Exception]
accessed_addresses: Set[Address]
accessed_storage_keys: Set[Tuple[Address, Bytes32]]
transient_storage: TransientStorage


def incorporate_child_on_success(evm: Evm, child_evm: Evm) -> None:
Expand Down
4 changes: 4 additions & 0 deletions src/ethereum/cancun/vm/instructions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ class Ops(enum.Enum):
# Storage Ops
SLOAD = 0x54
SSTORE = 0x55
TLOAD = 0x5C
TSTORE = 0x5D

# Pop Operation
POP = 0x50
Expand Down Expand Up @@ -268,6 +270,8 @@ class Ops(enum.Enum):
Ops.SELFBALANCE: environment_instructions.self_balance,
Ops.BASEFEE: environment_instructions.base_fee,
Ops.SSTORE: storage_instructions.sstore,
Ops.TLOAD: storage_instructions.tload,
Ops.TSTORE: storage_instructions.tstore,
Ops.JUMP: control_flow_instructions.jump,
Ops.JUMPI: control_flow_instructions.jumpi,
Ops.PC: control_flow_instructions.pc,
Expand Down
62 changes: 61 additions & 1 deletion src/ethereum/cancun/vm/instructions/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@
from ethereum.base_types import Uint
from ethereum.utils.ensure import ensure

from ...state import get_storage, get_storage_original, set_storage
from ...state import (
get_storage,
get_storage_original,
get_transient_storage,
set_storage,
set_transient_storage,
)
from .. import Evm
from ..exceptions import OutOfGasError, WriteInStaticContext
from ..gas import (
Expand Down Expand Up @@ -124,3 +130,57 @@ def sstore(evm: Evm) -> None:

# PROGRAM COUNTER
evm.pc += 1


def tload(evm: Evm) -> None:
"""
Loads to the stack, the value corresponding to a certain key from the
transient storage of the current account.
Parameters
----------
evm :
The current EVM frame.
"""
# STACK
key = pop(evm.stack).to_be_bytes32()

# GAS
charge_gas(evm, GAS_WARM_ACCESS)

# OPERATION
value = get_transient_storage(
evm.transient_storage, evm.message.current_target, key
)
push(evm.stack, value)

# PROGRAM COUNTER
evm.pc += 1


def tstore(evm: Evm) -> None:
"""
Stores a value at a certain key in the current context's transient storage.
Parameters
----------
evm :
The current EVM frame.
"""
# STACK
key = pop(evm.stack).to_be_bytes32()
new_value = pop(evm.stack)

# GAS
charge_gas(evm, GAS_WARM_ACCESS)

# OPERATION
ensure(not evm.message.is_static, WriteInStaticContext)
set_transient_storage(
evm.transient_storage, evm.message.current_target, key, new_value
)

# PROGRAM COUNTER
evm.pc += 1
14 changes: 12 additions & 2 deletions src/ethereum/cancun/vm/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

from ..fork_types import Address, Log
from ..state import (
TransientStorage,
account_exists_and_is_empty,
account_has_code_or_nonce,
begin_transaction,
Expand Down Expand Up @@ -219,7 +220,10 @@ def process_message(message: Message, env: Environment) -> Evm:
raise StackDepthLimitError("Stack depth limit reached")

# take snapshot of state before processing the message
begin_transaction(env.state)
transient_storage = (
message.parent_evm.transient_storage if message.parent_evm else None
)
begin_transaction(env.state, transient_storage)

touch_account(env.state, message.current_target)

Expand All @@ -232,7 +236,7 @@ def process_message(message: Message, env: Environment) -> Evm:
if evm.error:
# revert state to the last saved checkpoint
# since the message call resulted in an error
rollback_transaction(env.state)
rollback_transaction(env.state, evm.transient_storage)
else:
commit_transaction(env.state)
return evm
Expand All @@ -257,6 +261,11 @@ def execute_code(message: Message, env: Environment) -> Evm:
code = message.code
valid_jump_destinations = get_valid_jump_destinations(code)

if message.parent_evm and message.parent_evm.transient_storage:
transient_storage = message.parent_evm.transient_storage
else:
transient_storage = TransientStorage()

evm = Evm(
pc=Uint(0),
stack=[],
Expand All @@ -276,6 +285,7 @@ def execute_code(message: Message, env: Environment) -> Evm:
error=None,
accessed_addresses=message.accessed_addresses,
accessed_storage_keys=message.accessed_storage_keys,
transient_storage=transient_storage,
)
try:

Expand Down
5 changes: 4 additions & 1 deletion whitelist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -389,4 +389,7 @@ rlps
jsons
mem

checkable
checkable

tload
tstore

0 comments on commit 8041452

Please sign in to comment.