Skip to content
This repository was archived by the owner on May 23, 2023. It is now read-only.

Commit 76da6bd

Browse files
committed
Merge pull request #344 from hackaugusto/solidity-compile-from-file
Added constructor arguments and path in tester
2 parents 3aea65f + 28f1b58 commit 76da6bd

File tree

6 files changed

+514
-272
lines changed

6 files changed

+514
-272
lines changed

ethereum/abi.py

Lines changed: 185 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,215 @@
1-
import sys
1+
# -*- coding: utf8 -*-
2+
import ast
23
import re
4+
import warnings
5+
36
import yaml # use yaml instead of json to get non unicode (works with ascii only data)
4-
from ethereum import utils
57
from rlp.utils import decode_hex, encode_hex
8+
9+
from ethereum import utils
610
from ethereum.utils import encode_int, zpad, big_endian_to_int, is_numeric, is_string, ceil32
711
from ethereum.utils import isnumeric, TT256, TT255
8-
import ast
912

1013

11-
def json_decode(x):
12-
return yaml.safe_load(x)
14+
def json_decode(data):
15+
return yaml.safe_load(data)
16+
17+
18+
def split32(data):
19+
""" Split data into pieces of 32 bytes. """
20+
all_pieces = []
21+
22+
for position in range(0, len(data), 32):
23+
piece = data[position:position + 32]
24+
all_pieces.append(piece)
25+
26+
return all_pieces
27+
28+
29+
def _canonical_name(name):
30+
""" Replace aliases to the corresponding type. """
1331

32+
if name.startswith('int['):
33+
return 'uint256' + name[3:]
1434

15-
def _canonical_name(x):
16-
if x.startswith('int['):
17-
return 'uint256' + x[3:]
18-
elif x == 'int':
35+
if name == 'int':
1936
return 'uint256'
20-
elif x.startswith('real['):
21-
return 'real128x128' + x[4:]
22-
elif x == 'real':
37+
38+
if name.startswith('real['):
39+
return 'real128x128' + name[4:]
40+
41+
if name == 'real':
2342
return 'real128x128'
24-
return x
43+
44+
return name
2545

2646

2747
def method_id(name, encode_types):
28-
sig = name + '(' + ','.join(_canonical_name(x) for x in encode_types) + ')'
29-
return big_endian_to_int(utils.sha3(sig)[:4])
48+
""" Return the unique method id.
49+
50+
The signature is defined as the canonical expression of the basic
51+
prototype, i.e. the function name with the parenthesised list of parameter
52+
types. Parameter types are split by a single comma - no spaces are used.
53+
54+
The method id is defined as the first four bytes (left, high-order in
55+
big-endian) of the Keccak (SHA-3) hash of the signature of the function.
56+
"""
57+
function_types = [
58+
_canonical_name(type_)
59+
for type_ in encode_types
60+
]
61+
62+
function_signature = '{function_name}({canonical_types})'.format(
63+
function_name=name,
64+
canonical_types=','.join(function_types),
65+
)
66+
67+
function_keccak = utils.sha3(function_signature)
68+
first_bytes = function_keccak[:4]
69+
70+
return big_endian_to_int(first_bytes)
3071

3172

3273
def event_id(name, encode_types):
33-
sig = name + '(' + ','.join(_canonical_name(x) for x in encode_types) + ')'
34-
return big_endian_to_int(utils.sha3(sig))
74+
""" Return the event id.
75+
76+
Defined as:
77+
78+
`keccak(EVENT_NAME+"("+EVENT_ARGS.map(canonical_type_of).join(",")+")")`
79+
80+
Where `canonical_type_of` is a function that simply returns the canonical
81+
type of a given argument, e.g. for uint indexed foo, it would return
82+
uint256). Note the lack of spaces.
83+
"""
84+
85+
event_types = [
86+
_canonical_name(type_)
87+
for type_ in encode_types
88+
]
3589

90+
event_signature = '{event_name}({canonical_types})'.format(
91+
event_name=name,
92+
canonical_types=','.join(event_types),
93+
)
3694

37-
class ContractTranslator():
95+
return big_endian_to_int(utils.sha3(event_signature))
3896

39-
def __init__(self, full_signature):
97+
98+
def _normalize_name(name):
99+
""" Return normalized event/function name. """
100+
if '(' in name:
101+
return name[:name.find('(')]
102+
103+
return name
104+
105+
106+
class ContractTranslator(object):
107+
108+
def __init__(self, contract_interface):
109+
if is_string(contract_interface):
110+
contract_interface = json_decode(contract_interface)
111+
112+
self.constructor_data = None
40113
self.function_data = {}
41114
self.event_data = {}
42-
v = vars(self)
43-
if is_string(full_signature):
44-
full_signature = json_decode(full_signature)
45-
for sig_item in full_signature:
46-
if sig_item['type'] == 'constructor':
47-
continue
48-
encode_types = [f['type'] for f in sig_item['inputs']]
49-
signature = [(f['type'], f['name']) for f in sig_item['inputs']]
50-
name = sig_item['name']
51-
if '(' in name:
52-
name = name[:name.find('(')]
53-
if name in v:
54-
i = 2
55-
while name + utils.to_string(i) in v:
56-
i += 1
57-
name += utils.to_string(i)
58-
sys.stderr.write("Warning: multiple methods with the same "
59-
" name. Use %s to call %s with types %r"
60-
% (name, sig_item['name'], encode_types))
61-
if sig_item['type'] == 'function':
62-
decode_types = [f['type'] for f in sig_item['outputs']]
63-
is_unknown_type = len(sig_item['outputs']) and \
64-
sig_item['outputs'][0]['name'] == 'unknown_out'
65-
self.function_data[name] = {
66-
"prefix": method_id(name, encode_types),
67-
"encode_types": encode_types,
68-
"decode_types": decode_types,
69-
"is_unknown_type": is_unknown_type,
70-
"is_constant": sig_item.get('constant', False),
71-
"signature": signature
115+
116+
for description in contract_interface:
117+
encode_types = [
118+
element['type']
119+
for element in description['inputs']
120+
]
121+
122+
signature = [
123+
(element['type'], element['name'])
124+
for element in description['inputs']
125+
]
126+
127+
# type can be omitted, defaulting to function
128+
if description.get('type', 'function') == 'function':
129+
normalized_name = _normalize_name(description['name'])
130+
131+
decode_types = [
132+
element['type']
133+
for element in description['outputs']
134+
]
135+
136+
self.function_data[normalized_name] = {
137+
'prefix': method_id(normalized_name, encode_types),
138+
'encode_types': encode_types,
139+
'decode_types': decode_types,
140+
'is_constant': description.get('constant', False),
141+
'signature': signature,
72142
}
73-
elif sig_item['type'] == 'event':
74-
indexed = [f['indexed'] for f in sig_item['inputs']]
75-
names = [f['name'] for f in sig_item['inputs']]
76-
self.event_data[event_id(name, encode_types)] = {
77-
"types": encode_types,
78-
"name": name,
79-
"names": names,
80-
"indexed": indexed,
81-
"anonymous": sig_item.get('anonymous', False)
143+
144+
elif description['type'] == 'event':
145+
normalized_name = _normalize_name(description['name'])
146+
147+
indexed = [
148+
element['indexed']
149+
for element in description['inputs']
150+
]
151+
names = [
152+
element['name']
153+
for element in description['inputs']
154+
]
155+
self.event_data[event_id(normalized_name, encode_types)] = {
156+
'types': encode_types,
157+
'name': normalized_name,
158+
'names': names,
159+
'indexed': indexed,
160+
'anonymous': description.get('anonymous', False),
82161
}
83162

84-
def encode(self, name, args):
85-
fdata = self.function_data[name]
86-
o = zpad(encode_int(fdata['prefix']), 4) + \
87-
encode_abi(fdata['encode_types'], args)
88-
return o
163+
elif description['type'] == 'constructor':
164+
if self.constructor_data is not None:
165+
raise ValueError('Only one constructor is supported.')
89166

90-
def decode(self, name, data):
91-
# print 'out', utils.encode_hex(data)
92-
fdata = self.function_data[name]
93-
if fdata['is_unknown_type']:
94-
o = [utils.to_signed(utils.big_endian_to_int(data[i:i + 32]))
95-
for i in range(0, len(data), 32)]
96-
return [0 if not o else o[0] if len(o) == 1 else o]
97-
o = decode_abi(fdata['decode_types'], data)
98-
return o
167+
self.constructor_data = {
168+
'encode_types': encode_types,
169+
'signature': signature,
170+
}
171+
172+
else:
173+
raise ValueError('Unknown type {}'.format(description['type']))
174+
175+
def encode_function_call(self, function_name, args):
176+
""" Return the encoded function call.
177+
178+
Args:
179+
function_name (str): One of the existing functions described in the
180+
contract interface.
181+
args (List[object]): The function arguments that wll be encoded and
182+
used in the contract execution in the vm.
183+
184+
Return:
185+
bin: The encoded function name and arguments so that it can be used
186+
with the evm to execute a funcion call, the binary string follows
187+
the Ethereum Contract ABI.
188+
"""
189+
if function_name not in self.function_data:
190+
raise ValueError('Unkown function {}'.format(function_name))
191+
192+
description = self.function_data[function_name]
193+
194+
function_selector = zpad(encode_int(description['prefix']), 4)
195+
arguments = encode_abi(description['encode_types'], args)
99196

100-
def is_unknown_type(self, name):
101-
return self.function_data[name]["is_unknown_type"]
197+
return function_selector + arguments
198+
199+
def encode(self, function_name, args):
200+
warnings.warn('encode is deprecated, please use encode_function_call', DeprecationWarning)
201+
return self.encode_function_call(function_name, args)
202+
203+
def encode_constructor_arguments(self, args):
204+
""" Return the encoded constructor call. """
205+
if self.constructor_data is None:
206+
raise ValueError("The contract interface didn't have a constructor")
207+
208+
return encode_abi(self.constructor_data['encode_types'], args)
209+
210+
def decode(self, function_name, data):
211+
description = self.function_data[function_name]
212+
return decode_abi(description['decode_types'], data)
102213

103214
def listen(self, log, noprint=True):
104215
if not len(log.topics) or log.topics[0] not in self.event_data:
@@ -130,13 +241,6 @@ def listen(self, log, noprint=True):
130241
return o
131242

132243

133-
def split32(s):
134-
o = []
135-
for i in range(0, len(s), 32):
136-
o.append(s[i:i + 32])
137-
return o
138-
139-
140244
class EncodingError(Exception):
141245
pass
142246

ethereum/blocks.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1353,15 +1353,22 @@ def get_block(env, blockhash):
13531353
return CachedBlock.create_cached(blk)
13541354

13551355

1356-
# def has_block(blockhash):
1357-
# return blockhash in db.DB(utils.get_db_path())
1358-
1359-
13601356
def genesis(env, **kwargs):
1357+
""" Build the genesis block. """
13611358
assert isinstance(env, Env)
1362-
"""Build the genesis block."""
1363-
allowed_args = set(['start_alloc', 'prevhash', 'coinbase', 'difficulty', 'gas_limit',
1364-
'timestamp', 'extra_data', 'mixhash', 'nonce'])
1359+
1360+
allowed_args = set([
1361+
'start_alloc',
1362+
'prevhash',
1363+
'coinbase',
1364+
'difficulty',
1365+
'gas_limit',
1366+
'timestamp',
1367+
'extra_data',
1368+
'mixhash',
1369+
'nonce',
1370+
])
1371+
13651372
assert set(kwargs.keys()).issubset(allowed_args)
13661373

13671374
# https://ethereum.etherpad.mozilla.org/11

0 commit comments

Comments
 (0)