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

Commit 7d385ae

Browse files
committed
Merge pull request #323 from janx/abi-fixes
Abi fixes
2 parents 85c11d1 + 97db0e5 commit 7d385ae

File tree

2 files changed

+75
-16
lines changed

2 files changed

+75
-16
lines changed

ethereum/abi.py

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ class ValueOutOfBounds(EncodingError):
149149
def decint(n):
150150
if isinstance(n, str):
151151
n = utils.to_string(n)
152-
if is_numeric(n) and n < 2**256 and n > -2**255:
152+
if is_numeric(n) and n < 2**256 and n >= -2**255:
153153
return n
154154
elif is_numeric(n):
155155
raise EncodingError("Number out of range: %r" % n)
@@ -186,21 +186,22 @@ def encode_single(typ, arg):
186186
elif base == 'int':
187187
sub = int(sub)
188188
i = decint(arg)
189-
if not -2**(sub - 1) <= i < 2**sub:
189+
if not -2**(sub - 1) <= i < 2**(sub - 1):
190190
raise ValueOutOfBounds(repr(arg))
191191
return zpad(encode_int(i % 2**sub), 32)
192192
# Unsigned reals: ureal<high>x<low>
193193
elif base == 'ureal':
194194
high, low = [int(x) for x in sub.split('x')]
195195
if not 0 <= arg < 2**high:
196196
raise ValueOutOfBounds(repr(arg))
197-
return zpad(encode_int(arg * 2**low), 32)
197+
return zpad(encode_int(int(arg * 2**low)), 32)
198198
# Signed reals: real<high>x<low>
199199
elif base == 'real':
200200
high, low = [int(x) for x in sub.split('x')]
201201
if not -2**(high - 1) <= arg < 2**(high - 1):
202202
raise ValueOutOfBounds(repr(arg))
203-
return zpad(encode_int((arg % 2**high) * 2**low), 32)
203+
i = int(arg * 2**low)
204+
return zpad(encode_int(i % 2**(high+low)), 32)
204205
# Strings
205206
elif base == 'string' or base == 'bytes':
206207
if not is_string(arg):
@@ -221,9 +222,9 @@ def encode_single(typ, arg):
221222
raise EncodingError("too long: %r" % arg)
222223
if isnumeric(arg):
223224
return zpad(encode_int(arg), 32)
224-
elif len(arg) == len(sub):
225+
elif len(arg) == int(sub):
225226
return zpad(arg, 32)
226-
elif len(arg) == len(sub) * 2:
227+
elif len(arg) == int(sub) * 2:
227228
return zpad(decode_hex(arg), 32)
228229
else:
229230
raise EncodingError("Could not parse hash: %r" % arg)
@@ -236,7 +237,7 @@ def encode_single(typ, arg):
236237
return zpad(arg, 32)
237238
elif len(arg) == 40:
238239
return zpad(decode_hex(arg), 32)
239-
elif len(arg) == 42 and arg[2:] == '0x':
240+
elif len(arg) == 42 and arg[:2] == '0x':
240241
return zpad(decode_hex(arg[2:]), 32)
241242
else:
242243
raise EncodingError("Could not parse address: %r" % arg)
@@ -255,6 +256,8 @@ def process_type(typ):
255256
if base == 'string' or base == 'bytes':
256257
assert re.match('^[0-9]*$', sub), \
257258
"String type must have no suffix or numerical suffix"
259+
assert not sub or int(sub) <= 32, \
260+
"Maximum 32 bytes for fixed-length str or bytes"
258261
# Check validity of integer type
259262
elif base == 'uint' or base == 'int':
260263
assert re.match('^[0-9]+$', sub), \
@@ -263,12 +266,6 @@ def process_type(typ):
263266
"Integer size out of bounds"
264267
assert int(sub) % 8 == 0, \
265268
"Integer size must be multiple of 8"
266-
# Check validity of string type
267-
if base == 'string' or base == 'bytes':
268-
assert re.match('^[0-9]*$', sub), \
269-
"String type must have no suffix or numerical suffix"
270-
assert not sub or int(sub) <= 32, \
271-
"Maximum 32 bytes for fixed-length str or bytes"
272269
# Check validity of real type
273270
elif base == 'ureal' or base == 'real':
274271
assert re.match('^[0-9]+x[0-9]+$', sub), \
@@ -375,8 +372,14 @@ def decode_single(typ, data):
375372
base, sub, _ = typ
376373
if base == 'address':
377374
return encode_hex(data[12:])
378-
elif base == 'string' or base == 'bytes' or base == 'hash':
379-
return data[:int(sub)] if len(sub) else data
375+
elif base == 'hash':
376+
return data[32-int(sub):]
377+
elif base == 'string' or base == 'bytes':
378+
if len(sub):
379+
return data[:int(sub)]
380+
else:
381+
l = big_endian_to_int(data[0:32])
382+
return data[32:][:l]
380383
elif base == 'uint':
381384
return big_endian_to_int(data)
382385
elif base == 'int':
@@ -387,7 +390,9 @@ def decode_single(typ, data):
387390
return big_endian_to_int(data) * 1.0 / 2**low
388391
elif base == 'real':
389392
high, low = [int(x) for x in sub.split('x')]
390-
return (big_endian_to_int(data) * 1.0 / 2**low) % 2**high
393+
o = big_endian_to_int(data)
394+
i = (o - 2**(high+low)) if o >= 2**(high+low-1) else o
395+
return (i * 1.0 / 2**low)
391396
elif base == 'bool':
392397
return bool(int(data.encode('hex'), 16))
393398

ethereum/tests/test_abi.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import os
2+
import pytest
23
import ethereum.testutils as testutils
34
from ethereum.slogging import get_logger
45
import ethereum.abi as abi
6+
from ethereum.utils import zpad
7+
58
logger = get_logger()
69

710

@@ -17,6 +20,57 @@ def test_abi_encode_signed_int():
1720
assert abi.decode_abi(['int8'], abi.encode_abi(['int8'], [1]))[0] == 1
1821
assert abi.decode_abi(['int8'], abi.encode_abi(['int8'], [-1]))[0] == -1
1922

23+
def test_abi_encode_single_int():
24+
assert abi.encode_single(['int', '256', []], -2**255) == (b'\x80'+b'\x00'*31)
25+
26+
assert abi.encode_single(['int', '8', []], -128) == zpad(b'\x80', 32)
27+
with pytest.raises(abi.ValueOutOfBounds):
28+
assert abi.encode_single(['int', '8', []], -129)
29+
30+
assert abi.encode_single(['int', '8', []], 127) == zpad(b'\x7f', 32)
31+
with pytest.raises(abi.ValueOutOfBounds):
32+
assert abi.encode_single(['int', '8', []], 128)
33+
34+
def test_abi_encode_single_ureal():
35+
assert abi.encode_single(['ureal', '128x128', []], 0) == (b'\x00'*32)
36+
assert abi.encode_single(['ureal', '128x128', []], 1.125) == (b'\x00'*15 + b'\x01\x20' + '\x00'*15)
37+
assert abi.encode_single(['ureal', '128x128', []], 2**127-1) == (b'\x7f' + b'\xff'*15 + b'\x00'*16)
38+
39+
def test_abi_encode_single_real():
40+
assert abi.encode_single(['real', '128x128', []], 1.125) == (b'\x00'*15 + b'\x01\x20' + b'\x00'*15)
41+
assert abi.encode_single(['real', '128x128', []], -1.125) == (b'\xff'*15 + b'\xfe' + b'\xe0' + b'\x00'*15)
42+
43+
def test_abi_encode_single_hash():
44+
assert abi.encode_single(['hash', '8', []], b'\x00'*8) == b'\x00'*32
45+
assert abi.encode_single(['hash', '8', []], '00'*8) == b'\x00'*32
46+
47+
def test_abi_decode_single_hash():
48+
typ = ['hash', '8', []]
49+
assert b'\x01'*8 == abi.decode_single(typ, abi.encode_single(typ, b'\x01'*8))
50+
51+
def test_abi_decode_single_bytes():
52+
typ = ['bytes', '8', []]
53+
assert (b'\x01\x02' + b'\x00'*6) == abi.decode_single(typ, abi.encode_single(typ, '\x01\x02'))
54+
55+
typ = ['bytes', '', []]
56+
assert b'\x01\x02' == abi.decode_single(typ, abi.encode_single(typ, '\x01\x02'))
57+
58+
def test_abi_encode_single_prefixed_address():
59+
prefixed_address = '0x' + '0'*40
60+
assert abi.encode_single(['address', '', []], prefixed_address) == b'\x00' * 32
61+
62+
def test_abi_decode_single_real():
63+
real_data = abi.encode_single(['real', '128x128', []], 1)
64+
assert abi.decode_single(['real', '128x128', []], real_data) == 1
65+
66+
real_data = abi.encode_single(['real', '128x128', []], 2**127-1)
67+
assert abi.decode_single(['real', '128x128', []], real_data) == (2**127-1)*1.0
68+
69+
real_data = abi.encode_single(['real', '128x128', []], -1)
70+
assert abi.decode_single(['real', '128x128', []], real_data) == -1
71+
72+
real_data = abi.encode_single(['real', '128x128', []], -2**127)
73+
assert abi.decode_single(['real', '128x128', []], real_data) == -2**127
2074

2175
# Will be parametrized fron json fixtures
2276
def test_state(filename, testname, testdata):

0 commit comments

Comments
 (0)