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

Commit 06b76e5

Browse files
committed
Added constructor arguments and path in tester
abi.py: - reodering of the functions - added some documentation - refactored the ContractTranslator - Simplified it __init__ - Do not ignore the constructor description - Added encoding for the constructor parameters tester.py: - fixed pylint complaints - pep8 - added path and the contract costructor parametes into `contract` and `abi_contract` blocks.py, transactions.py, test_contracts.py: - comestic changes and small fixes
1 parent 96e9b57 commit 06b76e5

File tree

5 files changed

+483
-272
lines changed

5 files changed

+483
-272
lines changed

ethereum/abi.py

Lines changed: 180 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,210 @@
1-
import sys
1+
# -*- coding: utf8 -*-
2+
import ast
23
import re
4+
35
import yaml # use yaml instead of json to get non unicode (works with ascii only data)
4-
from ethereum import utils
56
from rlp.utils import decode_hex, encode_hex
7+
8+
from ethereum import utils
69
from ethereum.utils import encode_int, zpad, big_endian_to_int, is_numeric, is_string, ceil32
710
from ethereum.utils import isnumeric, TT256, TT255
8-
import ast
911

1012

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

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

15-
def _canonical_name(x):
16-
if x.startswith('int['):
17-
return 'uint256' + x[3:]
18-
elif x == 'int':
34+
if name == 'int':
1935
return 'uint256'
20-
elif x.startswith('real['):
21-
return 'real128x128' + x[4:]
22-
elif x == 'real':
36+
37+
if name.startswith('real['):
38+
return 'real128x128' + name[4:]
39+
40+
if name == 'real':
2341
return 'real128x128'
24-
return x
42+
43+
return name
2544

2645

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

3171

3272
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))
73+
""" Return the event id.
74+
75+
Defined as:
76+
77+
`keccak(EVENT_NAME+"("+EVENT_ARGS.map(canonical_type_of).join(",")+")")`
78+
79+
Where `canonical_type_of` is a function that simply returns the canonical
80+
type of a given argument, e.g. for uint indexed foo, it would return
81+
uint256). Note the lack of spaces.
82+
"""
83+
84+
event_types = [
85+
_canonical_name(type_)
86+
for type_ in encode_types
87+
]
3588

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

37-
class ContractTranslator():
94+
return big_endian_to_int(utils.sha3(event_signature))
3895

39-
def __init__(self, full_signature):
96+
97+
def _normalize_name(name):
98+
""" Return normalized event/function name. """
99+
if '(' in name:
100+
return name[:name.find('(')]
101+
102+
return name
103+
104+
105+
class ContractTranslator(object):
106+
107+
def __init__(self, contract_interface):
108+
if is_string(contract_interface):
109+
contract_interface = json_decode(contract_interface)
110+
111+
self.constructor_data = None
40112
self.function_data = {}
41113
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
114+
115+
for description in contract_interface:
116+
encode_types = [
117+
element['type']
118+
for element in description['inputs']
119+
]
120+
121+
signature = [
122+
(element['type'], element['name'])
123+
for element in description['inputs']
124+
]
125+
126+
# type can be omitted, defaulting to function
127+
if description.get('type', 'function') == 'function':
128+
normalized_name = _normalize_name(description['name'])
129+
130+
decode_types = [
131+
element['type']
132+
for element in description['outputs']
133+
]
134+
135+
self.function_data[normalized_name] = {
136+
'prefix': method_id(normalized_name, encode_types),
137+
'encode_types': encode_types,
138+
'decode_types': decode_types,
139+
'is_constant': description.get('constant', False),
140+
'signature': signature,
72141
}
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)
142+
143+
elif description['type'] == 'event':
144+
normalized_name = _normalize_name(description['name'])
145+
146+
indexed = [
147+
element['indexed']
148+
for element in description['inputs']
149+
]
150+
names = [
151+
element['name']
152+
for element in description['inputs']
153+
]
154+
self.event_data[event_id(normalized_name, encode_types)] = {
155+
'types': encode_types,
156+
'name': normalized_name,
157+
'names': names,
158+
'indexed': indexed,
159+
'anonymous': description.get('anonymous', False),
82160
}
83161

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
162+
elif description['type'] == 'constructor':
163+
if self.constructor_data is not None:
164+
raise ValueError('Only one constructor is supported.')
89165

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
166+
self.constructor_data = {
167+
'encode_types': encode_types,
168+
'signature': signature,
169+
}
99170

100-
def is_unknown_type(self, name):
101-
return self.function_data[name]["is_unknown_type"]
171+
else:
172+
raise ValueError('Unknown type {}'.format(description['type']))
173+
174+
def encode(self, function_name, args):
175+
""" Return the encoded function call.
176+
177+
Args:
178+
function_name (str): One of the existing functions described in the
179+
contract interface.
180+
args (List[object]): The function arguments that wll be encoded and
181+
used in the contract execution in the vm.
182+
183+
Return:
184+
bin: The encoded function name and arguments so that it can be used
185+
with the evm to execute a funcion call, the binary string follows
186+
the Ethereum Contract ABI.
187+
"""
188+
if function_name not in self.function_data:
189+
raise ValueError('Unkown function {}'.format(function_name))
190+
191+
description = self.function_data[function_name]
192+
193+
function_selector = zpad(encode_int(description['prefix']), 4)
194+
arguments = encode_abi(description['encode_types'], args)
195+
196+
return function_selector + arguments
197+
198+
def encode_constructor_arguments(self, args):
199+
""" Return the encoded constructor call. """
200+
if self.constructor_data is None:
201+
raise ValueError("The contract interface didn't have a constructor")
202+
203+
return encode_abi(self.constructor_data['encode_types'], args)
204+
205+
def decode(self, function_name, data):
206+
description = self.function_data[function_name]
207+
return decode_abi(description['decode_types'], data)
102208

103209
def listen(self, log, noprint=True):
104210
if not len(log.topics) or log.topics[0] not in self.event_data:
@@ -130,13 +236,6 @@ def listen(self, log, noprint=True):
130236
return o
131237

132238

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-
140239
class EncodingError(Exception):
141240
pass
142241

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)