Skip to content

Commit

Permalink
Merge pull request #123 from pipermerriam/piper/byzantium
Browse files Browse the repository at this point in the history
Byzantium Fork Rules
  • Loading branch information
pipermerriam authored Nov 20, 2017
2 parents 70631e3 + 1a815fd commit dd00cba
Show file tree
Hide file tree
Showing 31 changed files with 1,077 additions and 55 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ env:
global:
- PYTEST_ADDOPTS="-n 2"
matrix:
# byzantium goes first because it takes the longest.
- TOX_POSARGS="-e py35-state-byzantium"
- TOX_POSARGS="-e py35-state-frontier"
- TOX_POSARGS="-e py35-state-homestead"
- TOX_POSARGS="-e py35-state-eip150"
- TOX_POSARGS="-e py35-state-eip158"
#- TOX_POSARGS="-e py35-state-byzantium"
#- TOX_POSARGS="-e py35-state-constantinople"
#- TOX_POSARGS="-e py35-state-metropolis"
- TOX_POSARGS="-e py35-blockchain"
Expand Down
2 changes: 2 additions & 0 deletions evm/chains/mainnet/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
FrontierVM,
HomesteadVM,
SpuriousDragonVM,
ByzantiumVM,
)


Expand All @@ -17,6 +18,7 @@
(constants.HOMESTEAD_MAINNET_BLOCK, HomesteadVM),
(constants.EIP150_MAINNET_BLOCK, EIP150VM),
(constants.SPURIOUS_DRAGON_MAINNET_BLOCK, SpuriousDragonVM),
(constants.BYZANTIUM_MAINNET_BLOCK, ByzantiumVM),
)


Expand Down
16 changes: 16 additions & 0 deletions evm/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@

GAS_ECRECOVER = 3000

GAS_ECADD = 500
GAS_ECMUL = 40000

GAS_ECPAIRING_BASE = 100000
GAS_ECPAIRING_PER_POINT = 80000


#
# Gas Limit
Expand All @@ -111,6 +117,7 @@

FRONTIER_DIFFICULTY_ADJUSTMENT_CUTOFF = 13
HOMESTEAD_DIFFICULTY_ADJUSTMENT_CUTOFF = 10
BYZANTIUM_DIFFICULTY_ADJUSTMENT_CUTOFF = 9

BOMB_EXPONENTIAL_PERIOD = 100000
BOMB_EXPONENTIAL_FREE_PERIODS = 2
Expand All @@ -119,6 +126,7 @@
# Mining
#
BLOCK_REWARD = 5 * denoms.ether
EIP649_BLOCK_REWARD = 3 * denoms.ether

UNCLE_DEPTH_PENALTY_FACTOR = 8
MAX_UNCLE_DEPTH = 6
Expand Down Expand Up @@ -193,3 +201,11 @@

# https://github.com/ethereum/EIPs/issues/170
EIP170_CODE_SIZE_LIMIT = 24577

#
# ByzantiumVM
#
BYZANTIUM_MAINNET_BLOCK = 4370000
BYZANTIUM_ROPSTEN_BLOCK = 1700000

GAS_MOD_EXP_QUADRATIC_DENOMINATOR = 20
27 changes: 26 additions & 1 deletion evm/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ class VMError(PyEVMError):
"""
Class of errors which can be raised during VM execution.
"""
pass
burns_gas = True
zeros_return_data = True


class OutOfGas(VMError):
Expand Down Expand Up @@ -109,3 +110,27 @@ class ContractCreationCollision(VMError):
Error signaling that there was an address collision during contract creation.
"""
pass


class Revert(VMError):
"""
Error used by the REVERT opcode
"""
burns_gas = False
zeros_return_data = False


class WriteProtection(VMError):
"""
Error raised if an attempt to modify the state database is made while
operating inside of a STATICCALL context.
"""
pass


class OutOfBoundsRead(VMError):
"""
Error raised to indicate an attempt was made to read data beyond the
boundaries of the buffer (such as with RETURNDATACOPY)
"""
pass
54 changes: 52 additions & 2 deletions evm/logic/call.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from evm.exceptions import (
OutOfGas,
WriteProtection,
)
from evm.opcode import (
Opcode,
Expand Down Expand Up @@ -42,6 +43,7 @@ def __call__(self, computation):
memory_output_start_position,
memory_output_size,
should_transfer_value,
is_static,
) = self.get_call_params(computation)

computation.extend_memory(memory_input_start_position, memory_input_size)
Expand All @@ -62,6 +64,7 @@ def __call__(self, computation):
stack_too_deep = computation.msg.depth + 1 > constants.STACK_DEPTH_LIMIT

if insufficient_funds or stack_too_deep:
computation.return_data = b''
if insufficient_funds:
err_message = "Insufficient Funds: have: {0} | need: {1}".format(
sender_balance,
Expand Down Expand Up @@ -94,6 +97,7 @@ def __call__(self, computation):
'code': code,
'code_address': code_address,
'should_transfer_value': should_transfer_value,
'is_static': is_static,
}
if sender is not None:
child_msg_kwargs['sender'] = sender
Expand All @@ -105,14 +109,18 @@ def __call__(self, computation):
if child_computation.error:
computation.stack.push(0)
else:
computation.stack.push(1)

if not child_computation.error or not child_computation.error.zeros_return_data:
actual_output_size = min(memory_output_size, len(child_computation.output))
computation.gas_meter.return_gas(child_computation.gas_meter.gas_remaining)
computation.memory.write(
memory_output_start_position,
actual_output_size,
child_computation.output[:actual_output_size],
)
computation.stack.push(1)

if not child_computation.error or not child_computation.error.burns_gas:
computation.gas_meter.return_gas(child_computation.gas_meter.gas_remaining)


class Call(BaseCall):
Expand Down Expand Up @@ -146,6 +154,7 @@ def get_call_params(self, computation):
memory_output_start_position,
memory_output_size,
True, # should_transfer_value,
computation.msg.is_static,
)


Expand Down Expand Up @@ -179,6 +188,7 @@ def get_call_params(self, computation):
memory_output_start_position,
memory_output_size,
True, # should_transfer_value,
computation.msg.is_static,
)


Expand Down Expand Up @@ -215,6 +225,7 @@ def get_call_params(self, computation):
memory_output_start_position,
memory_output_size,
False, # should_transfer_value,
computation.msg.is_static,
)


Expand Down Expand Up @@ -279,3 +290,42 @@ def compute_msg_extra_gas(self, computation, gas, to, value):
transfer_gas_fee = constants.GAS_CALLVALUE if value else 0
create_gas_fee = constants.GAS_NEWACCOUNT if (account_is_dead and value) else 0
return transfer_gas_fee + create_gas_fee


#
# Byzantium
#
class StaticCall(CallEIP161):
def get_call_params(self, computation):
gas = computation.stack.pop(type_hint=constants.UINT256)
to = force_bytes_to_address(computation.stack.pop(type_hint=constants.BYTES))

(
memory_input_start_position,
memory_input_size,
memory_output_start_position,
memory_output_size,
) = computation.stack.pop(num_items=4, type_hint=constants.UINT256)

return (
gas,
0, # value
to,
None, # sender
None, # code_address
memory_input_start_position,
memory_input_size,
memory_output_start_position,
memory_output_size,
False, # should_transfer_value,
True, # is_static
)


class CallByzantium(CallEIP161):
def get_call_params(self, computation):
call_params = super(CallByzantium, self).get_call_params(computation)
value = call_params[1]
if computation.msg.is_static and value != 0:
raise WriteProtection("Cannot modify state while inside of a STATICCALL context")
return call_params
38 changes: 38 additions & 0 deletions evm/logic/context.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from evm import constants
from evm.exceptions import (
OutOfBoundsRead,
)

from evm.utils.address import (
force_bytes_to_address,
Expand Down Expand Up @@ -138,3 +141,38 @@ def extcodecopy(computation):
padded_code_bytes = pad_right(code_bytes, size, b'\x00')

computation.memory.write(mem_start_position, size, padded_code_bytes)


def returndatasize(computation):
size = len(computation.return_data)
computation.stack.push(size)


def returndatacopy(computation):
(
mem_start_position,
returndata_start_position,
size,
) = computation.stack.pop(num_items=3, type_hint=constants.UINT256)

if returndata_start_position + size > len(computation.return_data):
raise OutOfBoundsRead(
"Return data length is not sufficient to satisfy request. Asked "
"for data from index {0} to {1}. Return data is {2} bytes in "
"length.".format(
returndata_start_position,
returndata_start_position + size,
len(computation.return_data),
)
)

computation.extend_memory(mem_start_position, size)

word_count = ceil32(size) // 32
copy_gas_cost = word_count * constants.GAS_COPY

computation.gas_meter.consume_gas(copy_gas_cost, reason="RETURNDATACOPY fee")

value = computation.return_data[returndata_start_position: returndata_start_position + size]

computation.memory.write(mem_start_position, size, value)
21 changes: 20 additions & 1 deletion evm/logic/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from evm import mnemonics
from evm.exceptions import (
Halt,
Revert,
WriteProtection,
)

from evm.opcode import (
Expand All @@ -28,6 +30,16 @@ def return_op(computation):
raise Halt('RETURN')


def revert(computation):
start_position, size = computation.stack.pop(num_items=2, type_hint=constants.UINT256)

computation.extend_memory(start_position, size)

output = computation.memory.read(start_position, size)
computation.output = bytes(output)
raise Revert(computation.output)


def selfdestruct(computation):
beneficiary = force_bytes_to_address(computation.stack.pop(type_hint=constants.BYTES))
_selfdestruct(computation, beneficiary)
Expand Down Expand Up @@ -147,10 +159,17 @@ def __call__(self, computation):
if child_computation.error:
computation.stack.push(0)
else:
computation.gas_meter.return_gas(child_computation.gas_meter.gas_remaining)
computation.stack.push(contract_address)
computation.gas_meter.return_gas(child_computation.gas_meter.gas_remaining)


class CreateEIP150(Create):
def max_child_gas_modifier(self, gas):
return max_child_gas_eip150(gas)


class CreateByzantium(CreateEIP150):
def __call__(self, computation):
if computation.msg.is_static:
raise WriteProtection("Cannot modify state while inside of a STATICCALL context")
return super(CreateEIP150, self).__call__(computation)
4 changes: 4 additions & 0 deletions evm/mnemonics.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
GASPRICE = 'GASPRICE'
EXTCODESIZE = 'EXTCODESIZE'
EXTCODECOPY = 'EXTCODECOPY'
RETURNDATASIZE = 'RETURNDATASIZE'
RETURNDATACOPY = 'RETURNDATACOPY'
#
# Block Information
#
Expand All @@ -71,6 +73,7 @@
MSIZE = 'MSIZE'
GAS = 'GAS'
JUMPDEST = 'JUMPDEST'
REVERT = 'REVERT'
#
# Push Operations
#
Expand Down Expand Up @@ -158,6 +161,7 @@
CREATE = 'CREATE'
CALL = 'CALL'
CALLCODE = 'CALLCODE'
STATICCALL = 'STATICCALL'
RETURN = 'RETURN'
DELEGATECALL = 'DELEGATECALL'
SELFDESTRUCT = 'SELFDESTRUCT'
4 changes: 4 additions & 0 deletions evm/opcode_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
GASPRICE = 0x3a
EXTCODESIZE = 0x3b
EXTCODECOPY = 0x3c
RETURNDATASIZE = 0x3d
RETURNDATACOPY = 0x3e


#
Expand Down Expand Up @@ -181,4 +183,6 @@
CALLCODE = 0xf2
RETURN = 0xf3
DELEGATECALL = 0xf4
STATICCALL = 0xfa
REVERT = 0xfd
SELFDESTRUCT = 0xff
4 changes: 4 additions & 0 deletions evm/precompiles/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
from .identity import identity # noqa: F401
from .ecrecover import ecrecover # noqa: F401
from .ripemd160 import ripemd160 # noqa: F401
from .modexp import modexp # noqa: F401
from .ecadd import ecadd # noqa: F401
from .ecmul import ecmul # noqa: F401
from .ecpairing import ecpairing # noqa: F401
Loading

0 comments on commit dd00cba

Please sign in to comment.