Skip to content

Commit

Permalink
merged evm tool
Browse files Browse the repository at this point in the history
  • Loading branch information
joeykrug committed Oct 15, 2017
2 parents c387cfb + d262e13 commit ff838b5
Show file tree
Hide file tree
Showing 89 changed files with 4,661 additions and 1,719 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ __pycache__/
/dist/
/*.egg-info/
/.tox/
/build/
/bin/
/develop-eggs/
/eggs/
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@
path = fixtures
url = git://github.com/ethereum/tests.git
branch = develop
[submodule "casper"]
path = casper
url = https://github.com/ethereum/casper.git
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
[![Build Status](https://travis-ci.org/ethereum/pyethereum.svg?branch=develop)](https://travis-ci.org/ethereum/pyethereum)

This is the Python core library of the Ethereum project.

For the python based command line client see:
Expand Down
1 change: 1 addition & 0 deletions casper
Submodule casper added at 09bbd5
1 change: 0 additions & 1 deletion dev_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,3 @@ pytest>=2.9.0
pytest-catchlog==1.2.2
pytest-timeout==1.0.0
py-ecc
https://github.com/ethereum/serpent/tarball/develop
10 changes: 6 additions & 4 deletions ethereum/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# ############# version ##################
try:
from pkg_resources import get_distribution, DistributionNotFound
except:
except BaseException:
DistributionNotFound = Exception
import os.path
import subprocess
Expand All @@ -11,7 +11,8 @@
from . import slogging # noqa


GIT_DESCRIBE_RE = re.compile('^(?P<version>v\d+\.\d+\.\d+)-(?P<git>\d+-g[a-fA-F0-9]+(?:-dirty)?)$')
GIT_DESCRIBE_RE = re.compile(
'^(?P<version>v\d+\.\d+\.\d+)-(?P<git>\d+-g[a-fA-F0-9]+(?:-dirty)?)$')


__version__ = None
Expand All @@ -33,8 +34,9 @@
stderr=subprocess.STDOUT)
match = GIT_DESCRIBE_RE.match(rev)
if match:
__version__ = "{}+git-{}".format(match.group("version"), match.group("git"))
except: # FIXME!
__version__ = "{}+git-{}".format(
match.group("version"), match.group("git"))
except BaseException: # FIXME!
pass

if not __version__:
Expand Down
29 changes: 20 additions & 9 deletions ethereum/abi.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
import re
import warnings

import yaml # use yaml instead of json to get non unicode (works with ascii only data)
# use yaml instead of json to get non unicode (works with ascii only data)
import yaml
from rlp.utils import decode_hex
from ethereum.utils import encode_hex

Expand Down Expand Up @@ -230,7 +231,8 @@ def encode_single(typ, arg): # pylint: disable=too-many-return-statements,too-m
sub = int(sub)

if not (0 < sub <= 256 and sub % 8 == 0):
raise ValueError('invalid unsigned integer bit length {}'.format(sub))
raise ValueError(
'invalid unsigned integer bit length {}'.format(sub))

try:
i = decint(arg, signed=False)
Expand Down Expand Up @@ -478,18 +480,23 @@ def __init__(self, contract_interface):

elif entry_type == 'fallback':
if self.fallback_data is not None:
raise ValueError('Only one fallback function is supported.')
raise ValueError(
'Only one fallback function is supported.')
self.fallback_data = {'payable': description['payable']}

else:
raise ValueError('Unknown type {}'.format(description['type']))

def encode(self, function_name, args):
warnings.warn('encode is deprecated, please use encode_function_call', DeprecationWarning)
warnings.warn(
'encode is deprecated, please use encode_function_call',
DeprecationWarning)
return self.encode_function_call(function_name, args)

def decode(self, function_name, data):
warnings.warn('decode is deprecated, please use decode_function_result', DeprecationWarning)
warnings.warn(
'decode is deprecated, please use decode_function_result',
DeprecationWarning)
return self.decode_function_result(function_name, data)

def encode_function_call(self, function_name, args):
Expand Down Expand Up @@ -534,7 +541,8 @@ def decode_function_result(self, function_name, data):
def encode_constructor_arguments(self, args):
""" Return the encoded constructor call. """
if self.constructor_data is None:
raise ValueError("The contract interface didn't have a constructor")
raise ValueError(
"The contract interface didn't have a constructor")

return encode_abi(self.constructor_data['encode_types'], args)

Expand Down Expand Up @@ -575,7 +583,8 @@ def decode_event(self, log_topics, log_data):
indexed_count = 1 # skip topics[0]

result = {}
for name, type_, indexed in zip(event['names'], event['types'], event['indexed']):
for name, type_, indexed in zip(
event['names'], event['types'], event['indexed']):
if indexed:
topic_bytes = utils.zpad(
utils.encode_int(log_topics[indexed_count]),
Expand Down Expand Up @@ -617,7 +626,8 @@ def process_type(typ):
# Crazy reg expression to separate out base type component (eg. uint),
# size (eg. 256, 128x128, none), array component (eg. [], [45], none)
regexp = '([a-z]*)([0-9]*x?[0-9]*)((\[[0-9]*\])*)'
base, sub, arr, _ = re.match(regexp, utils.to_string_for_regexp(typ)).groups()
base, sub, arr, _ = re.match(
regexp, utils.to_string_for_regexp(typ)).groups()
arrlist = re.findall('\[[0-9]*\]', arr)
assert len(''.join(arrlist)) == len(arr), \
"Unknown characters found in array declaration"
Expand Down Expand Up @@ -816,7 +826,8 @@ def dec(typ, arg):
# Dynamic-sized strings are encoded as <len(str)> + <str>
if base in ('string', 'bytes') and not sub:
L = big_endian_to_int(arg[:32])
assert len(arg[32:]) == ceil32(L), "Wrong data size for string/bytes object: expected %d actual %d" % (ceil32(L), len(arg[32:]))
assert len(arg[32:]) == ceil32(
L), "Wrong data size for string/bytes object: expected %d actual %d" % (ceil32(L), len(arg[32:]))
return arg[32:][:L]
# Dynamic-sized arrays
elif sz is None:
Expand Down
19 changes: 16 additions & 3 deletions ethereum/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,13 @@ def hex_hash(self):

@property
def mining_hash(self):
return utils.sha3(rlp.encode(self, BlockHeader.exclude(['mixhash', 'nonce'])))
return utils.sha3(rlp.encode(
self, BlockHeader.exclude(['mixhash', 'nonce'])))

@property
def signing_hash(self):
return utils.sha3(rlp.encode(self, BlockHeader.exclude(['extra_data'])))
return utils.sha3(rlp.encode(
self, BlockHeader.exclude(['extra_data'])))

def to_dict(self):
"""Serialize the header to a readable dictionary."""
Expand Down Expand Up @@ -188,11 +190,22 @@ def transaction_count(self):

class FakeHeader():

def __init__(self, hash='\x00' * 32, number=0, timestamp=0, difficulty=1, gas_limit=3141592, gas_used=0, uncles_hash=BLANK_UNCLES_HASH):
def __init__(self, hash='\x00' * 32, number=0, timestamp=0, difficulty=1,
gas_limit=3141592, gas_used=0, uncles_hash=BLANK_UNCLES_HASH):
self.hash = hash
self.number = number
self.timestamp = timestamp
self.difficulty = difficulty
self.gas_limit = gas_limit
self.gas_used = gas_used
self.uncles_hash = uncles_hash

def to_block_header(self):
return BlockHeader(
difficulty=self.difficulty,
number=self.number,
timestamp=self.timestamp,
gas_used=self.gas_used,
gas_limit=self.gas_limit,
uncles_hash=self.uncles_hash
)
3 changes: 2 additions & 1 deletion ethereum/bloom.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ def bloom_insert(bloom, val):

def bloom_bits(val):
h = utils.sha3(val)
return [bits_in_number(1 << ((safe_ord(h[i + 1]) + (safe_ord(h[i]) << 8)) & 2047)) for i in range(0, BUCKETS_PER_VAL * 2, 2)]
return [bits_in_number(1 << ((safe_ord(h[i + 1]) + (safe_ord(h[i]) << 8)) & 2047))
for i in range(0, BUCKETS_PER_VAL * 2, 2)]


def bits_in_number(val):
Expand Down
6 changes: 4 additions & 2 deletions ethereum/child_dao_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
# DAO extrabalance
L.append('0x807640a13483f8ac783c557fcdf27be11ea4ac7a')
# child DAOs (created by DAO creator)
L.extend(['0x' + encode_hex(mk_contract_address(source, i)) for i in range(1, 58)])
L.extend(['0x' + encode_hex(mk_contract_address(source, i))
for i in range(1, 58)])
# child extrabalances
L.extend(['0x' + encode_hex(mk_contract_address(mk_contract_address(source, i), 0)) for i in range(1, 58)])
L.extend(['0x' + encode_hex(mk_contract_address(mk_contract_address(source, i), 0))
for i in range(1, 58)])
58 changes: 43 additions & 15 deletions ethereum/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,17 @@
from ethereum.messages import apply_transaction
log = get_logger('eth.block')


# Gas limit adjustment algo
def calc_gaslimit(parent, config=default_config):
decay = parent.gas_limit // config['GASLIMIT_EMA_FACTOR']
new_contribution = ((parent.gas_used * config['BLKLIM_FACTOR_NOM']) //
config['BLKLIM_FACTOR_DEN'] // config['GASLIMIT_EMA_FACTOR'])
gl = max(parent.gas_limit - decay + new_contribution, config['MIN_GAS_LIMIT'])
gl = max(
parent.gas_limit -
decay +
new_contribution,
config['MIN_GAS_LIMIT'])
if gl < config['GENESIS_GAS_LIMIT']:
gl2 = parent.gas_limit + decay
gl = min(config['GENESIS_GAS_LIMIT'], gl2)
Expand All @@ -30,6 +35,7 @@ def check_gaslimit(parent, gas_limit, config=default_config):
b = bool(gas_limit >= config['MIN_GAS_LIMIT'])
return a and b


# Difficulty adjustment algo
def calc_difficulty(parent, timestamp, config=default_config):
# Special case for test chains
Expand All @@ -43,23 +49,31 @@ def calc_difficulty(parent, timestamp, config=default_config):
sign = max(1 - ((timestamp - parent.timestamp) //
config['HOMESTEAD_DIFF_ADJUSTMENT_CUTOFF']), -99)
else:
sign = 1 if timestamp - parent.timestamp < config['DIFF_ADJUSTMENT_CUTOFF'] else -1
sign = 1 if timestamp - \
parent.timestamp < config['DIFF_ADJUSTMENT_CUTOFF'] else -1
# If we enter a special mode where the genesis difficulty starts off below
# the minimal difficulty, we allow low-difficulty blocks (this will never
# happen in the official protocol)
o = int(max(parent.difficulty + offset * sign, min(parent.difficulty, config['MIN_DIFF'])))
o = int(max(parent.difficulty + offset * sign,
min(parent.difficulty, config['MIN_DIFF'])))
period_count = (parent.number + 1) // config['EXPDIFF_PERIOD']
if parent.number >= (config['METROPOLIS_FORK_BLKNUM'] - 1):
period_count -= config['METROPOLIS_DELAY_PERIODS']
if period_count >= config['EXPDIFF_FREE_PERIODS']:
o = max(o + 2**(period_count - config['EXPDIFF_FREE_PERIODS']), config['MIN_DIFF'])
o = max(o + 2**(period_count -
config['EXPDIFF_FREE_PERIODS']), config['MIN_DIFF'])
return o


# Given a parent state, initialize a block with the given arguments
def mk_block_from_prevstate(chain, state=None, timestamp=None, coinbase=b'\x35'*20, extra_data='moo ha ha says the laughing cow.'):
def mk_block_from_prevstate(chain, state=None, timestamp=None,
coinbase=b'\x35' * 20, extra_data='moo ha ha says the laughing cow.'):
state = state or chain.state
blk = Block(BlockHeader())
now = timestamp or chain.time()
blk.header.number = state.block_number + 1
blk.header.difficulty = calc_difficulty(state.prev_headers[0], now, chain.config)
blk.header.number = state.prev_headers[0].number + 1
blk.header.difficulty = calc_difficulty(
state.prev_headers[0], now, chain.config)
blk.header.gas_limit = calc_gaslimit(state.prev_headers[0], chain.config)
blk.header.timestamp = max(now, state.prev_headers[0].timestamp + 1)
blk.header.prevhash = state.prev_headers[0].hash
Expand All @@ -69,6 +83,7 @@ def mk_block_from_prevstate(chain, state=None, timestamp=None, coinbase=b'\x35'*
blk.transactions = []
return blk


# Validate a block header
def validate_header(state, header):
parent = state.prev_headers[0]
Expand All @@ -77,10 +92,13 @@ def validate_header(state, header):
raise ValueError("Block's prevhash and parent's hash do not match: block prevhash %s parent hash %s" %
(encode_hex(header.prevhash), encode_hex(parent.hash)))
if header.number != parent.number + 1:
raise ValueError("Block's number is not the successor of its parent number")
raise ValueError(
"Block's number is not the successor of its parent number")
if not check_gaslimit(parent, header.gas_limit, config=state.config):
raise ValueError("Block's gaslimit is inconsistent with its parent's gaslimit")
if header.difficulty != calc_difficulty(parent, header.timestamp, config=state.config):
raise ValueError(
"Block's gaslimit is inconsistent with its parent's gaslimit")
if header.difficulty != calc_difficulty(
parent, header.timestamp, config=state.config):
raise ValueError("Block's difficulty is inconsistent with its parent's difficulty: parent %d expected %d actual %d. Time diff %d" %
(parent.difficulty, calc_difficulty(parent, header.timestamp, config=state.config), header.difficulty, header.timestamp - parent.timestamp))
if header.gas_used > header.gas_limit:
Expand All @@ -95,17 +113,20 @@ def validate_header(state, header):
raise ValueError("Timestamp waaaaaaaaaaayy too large")
if header.gas_limit >= 2**63:
raise ValueError("Header gas limit too high")
if 0 <= header.number - state.config["DAO_FORK_BLKNUM"] < 10 and header.extra_data != state.config["DAO_FORK_BLKEXTRA"]:
if 0 <= header.number - \
state.config["DAO_FORK_BLKNUM"] < 10 and header.extra_data != state.config["DAO_FORK_BLKEXTRA"]:
raise ValueError("Missing extra data for block near DAO fork")
return True


# Add transactions
def add_transactions(state, block, txqueue, min_gasprice=0):
if not txqueue:
return
pre_txs = len(block.transactions)
log.info('Adding transactions, %d in txqueue, %d dunkles' % (len(txqueue.txs), pre_txs))
while 1:
log.info('Adding transactions, %d in txqueue, %d dunkles' %
(len(txqueue.txs), pre_txs))
while True:
tx = txqueue.pop_transaction(max_gas=state.gas_limit - state.gas_used,
min_gasprice=min_gasprice)
if tx is None:
Expand All @@ -115,9 +136,10 @@ def add_transactions(state, block, txqueue, min_gasprice=0):
block.transactions.append(tx)
except (InsufficientBalance, BlockGasLimitReached, InsufficientStartGas,
InvalidNonce, UnsignedTransaction) as e:
pass
log.error(e)
log.info('Added %d transactions' % (len(block.transactions) - pre_txs))


# Validate that the transaction list root is correct
def validate_transaction_tree(state, block):
if block.header.tx_list_root != mk_transaction_sha(block.transactions):
Expand All @@ -126,6 +148,7 @@ def validate_transaction_tree(state, block):
len(block.transactions)))
return True


# Set state root, receipt root, etc
def set_execution_results(state, block):
block.header.receipts_root = mk_receipt_sha(state.receipts)
Expand All @@ -136,6 +159,7 @@ def set_execution_results(state, block):
block.header.bloom = state.bloom
log.info('Block pre-sealed, %d gas used' % state.gas_used)


# Verify state root, receipt root, etc
def verify_execution_results(state, block):
if block.header.bloom != state.bloom:
Expand All @@ -148,26 +172,30 @@ def verify_execution_results(state, block):
if block.header.receipts_root != mk_receipt_sha(state.receipts):
raise ValueError("Receipt root mismatch: header %s computed %s, gas used header %d computed %d, %d receipts" %
(encode_hex(block.header.receipts_root), encode_hex(mk_receipt_sha(state.receipts)),
block.header.gas_used, state.gas_used, len(state.receipts)))
block.header.gas_used, state.gas_used, len(state.receipts)))
if block.header.gas_used != state.gas_used:
raise ValueError("Gas used mismatch: header %d computed %d" %
(block.header.gas_used, state.gas_used))
return True


# Make the root of a receipt tree
def mk_receipt_sha(receipts):
t = trie.Trie(EphemDB())
for i, receipt in enumerate(receipts):
t.update(rlp.encode(i), rlp.encode(receipt))
return t.root_hash


# Make the root of a transaction tree
mk_transaction_sha = mk_receipt_sha


# State changes after block finalized
def post_finalize(state, block):
state.add_block_header(block.header)


# Update block variables into the state
def update_block_env_variables(state, block):
state.timestamp = block.header.timestamp
Expand Down
Loading

0 comments on commit ff838b5

Please sign in to comment.