Skip to content

Commit

Permalink
Implement EIP-1283
Browse files Browse the repository at this point in the history
  • Loading branch information
cburgdorf committed Oct 20, 2018
1 parent e68e7c3 commit e555f2a
Show file tree
Hide file tree
Showing 8 changed files with 298 additions and 22 deletions.
8 changes: 4 additions & 4 deletions eth/db/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,11 +239,11 @@ def has_root(self, state_root: bytes) -> bool:
#
# Storage
#
def get_storage(self, address, slot):
def get_storage(self, address, slot, from_journal=True):
validate_canonical_address(address, title="Storage Address")
validate_uint256(slot, title="Storage Slot")

account = self._get_account(address)
account = self._get_account(address, from_journal)
storage = HashTrie(HexaryTrie(self._journaldb, account.storage_root))

slot_as_key = pad32(int_to_big_endian(slot))
Expand Down Expand Up @@ -375,8 +375,8 @@ def account_is_empty(self, address):
#
# Internal
#
def _get_account(self, address):
rlp_account = self._journaltrie.get(address, b'')
def _get_account(self, address, from_journal=True):
rlp_account = (self._journaltrie if from_journal else self._trie).get(address, b'')
if rlp_account:
account = rlp.decode(rlp_account, sedes=Account)
else:
Expand Down
5 changes: 4 additions & 1 deletion eth/vm/computation.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def __init__(self,

self._memory = Memory()
self._stack = Stack()
self._gas_meter = GasMeter(message.gas)
self._gas_meter = self.get_gas_meter()

self.children = []
self.accounts_to_delete = {}
Expand Down Expand Up @@ -406,6 +406,9 @@ def add_log_entry(self, account: Address, topics: List[int], data: bytes) -> Non
#
# Getters
#
def get_gas_meter(self) -> GasMeter:
return GasMeter(self.msg.gas)

def get_accounts_for_deletion(self) -> Tuple[Tuple[bytes, bytes], ...]:
if self.is_error:
return tuple()
Expand Down
10 changes: 10 additions & 0 deletions eth/vm/forks/constantinople/computation.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
from eth.vm.forks.byzantium.computation import (
ByzantiumComputation
)
from eth.vm.gas_meter import (
allow_negative_refund_strategy,
GasMeter,
)

from .opcodes import CONSTANTINOPLE_OPCODES

Expand All @@ -27,3 +31,9 @@ class ConstantinopleComputation(ByzantiumComputation):
# Override
opcodes = CONSTANTINOPLE_OPCODES
_precompiles = CONSTANTINOPLE_PRECOMPILES

def get_gas_meter(self) -> GasMeter:
return GasMeter(
self.msg.gas,
allow_negative_refund_strategy
)
6 changes: 6 additions & 0 deletions eth/vm/forks/constantinople/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,11 @@


GAS_EXTCODEHASH_EIP1052 = 400
GAS_SSTORE_NOOP_EIP1283 = 200
GAS_SSTORE_INIT_EIP1283 = 20000
GAS_SSTORE_CLEAN_EIP1283 = 5000
GAS_SSTORE_CLEAR_REFUND_EIP1283 = 15000
GAS_SSTORE_RESET_CLEAR_REFUND_EIP1283 = 19800
GAS_SSTORE_RESET_REFUND = 4800

EIP1234_BLOCK_REWARD = 2 * denoms.ether
8 changes: 8 additions & 0 deletions eth/vm/forks/constantinople/opcodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
from eth.vm.forks.constantinople.constants import (
GAS_EXTCODEHASH_EIP1052
)
from eth.vm.forks.constantinople.storage import (
sstore_eip1283,
)
from eth.vm.logic import (
arithmetic,
context,
Expand Down Expand Up @@ -52,6 +55,11 @@
mnemonic=mnemonics.CREATE2,
gas_cost=constants.GAS_CREATE,
)(),
opcode_values.SSTORE: as_opcode(
logic_fn=sstore_eip1283,
mnemonic=mnemonics.SSTORE,
gas_cost=constants.GAS_NULL,
),
}

CONSTANTINOPLE_OPCODES = merge(
Expand Down
73 changes: 73 additions & 0 deletions eth/vm/forks/constantinople/storage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from eth.constants import (
UINT256
)
from eth.vm.forks.constantinople import (
constants
)

from eth.utils.hexadecimal import (
encode_hex,
)


def sstore_eip1283(computation):
slot, value = computation.stack_pop(num_items=2, type_hint=UINT256)

current_value = computation.state.account_db.get_storage(
address=computation.msg.storage_address,
slot=slot,
)

original_value = computation.state.account_db.get_storage(
address=computation.msg.storage_address,
slot=slot,
from_journal=False
)

gas_refund = 0

if current_value == value:
gas_cost = constants.GAS_SSTORE_NOOP_EIP1283
else:
if original_value == current_value:
if original_value == 0:
gas_cost = constants.GAS_SSTORE_INIT_EIP1283
else:
gas_cost = constants.GAS_SSTORE_CLEAN_EIP1283

if value == 0:
gas_refund += constants.GAS_SSTORE_CLEAR_REFUND_EIP1283
else:
gas_cost = constants.GAS_SSTORE_NOOP_EIP1283

if original_value != 0:
if current_value == 0:
gas_refund -= constants.GAS_SSTORE_CLEAR_REFUND_EIP1283
if value == 0:
gas_refund += constants.GAS_SSTORE_CLEAR_REFUND_EIP1283

if original_value == value:
if original_value == 0:
gas_refund += constants.GAS_SSTORE_RESET_CLEAR_REFUND_EIP1283
else:
gas_refund += constants.GAS_SSTORE_RESET_REFUND

computation.consume_gas(
gas_cost,
reason="SSTORE: {0}[{1}] -> {2} (current: {3} / original: {4})".format(
encode_hex(computation.msg.storage_address),
slot,
value,
current_value,
original_value,
)
)

if gas_refund:
computation.refund_gas(gas_refund)

computation.state.account_db.set_storage(
address=computation.msg.storage_address,
slot=slot,
value=value,
)
28 changes: 22 additions & 6 deletions eth/vm/gas_meter.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging
from typing import (
cast
Callable,
cast,
)
from eth_utils import (
ValidationError,
Expand All @@ -16,17 +17,35 @@
)


def default_refund_strategy(gas_refunded_total: int, amount: int) -> int:
if amount < 0:
raise ValidationError("Gas refund amount must be positive")

return gas_refunded_total + amount


def allow_negative_refund_strategy(gas_refunded_total: int, amount: int) -> int:
return gas_refunded_total + amount


RefundStrategy = Callable[[int, int], int]


class GasMeter(object):

start_gas = None # type: int

gas_refunded = None # type: int
gas_remaining = None # type: int

logger = cast(TraceLogger, logging.getLogger('eth.gas.GasMeter'))

def __init__(self, start_gas: int) -> None:
def __init__(self,
start_gas: int,
refund_strategy: RefundStrategy = default_refund_strategy) -> None:
validate_uint256(start_gas, title="Start Gas")

self.refund_strategy = refund_strategy
self.start_gas = start_gas

self.gas_remaining = self.start_gas
Expand Down Expand Up @@ -70,10 +89,7 @@ def return_gas(self, amount: int) -> None:
)

def refund_gas(self, amount: int) -> None:
if amount < 0:
raise ValidationError("Gas refund amount must be positive")

self.gas_refunded += amount
self.gas_refunded = self.refund_strategy(self.gas_refunded, amount)

self.logger.trace(
'GAS REFUND: %s + %s -> %s',
Expand Down
Loading

0 comments on commit e555f2a

Please sign in to comment.