Skip to content
This repository was archived by the owner on Jul 14, 2020. It is now read-only.

Commit 4805656

Browse files
author
Jan Xie
committed
Merge branch 'develop'
2 parents 5de0611 + 0a531b7 commit 4805656

24 files changed

+679
-146
lines changed

fixtures

Submodule fixtures updated 45 files

lib/ethereum/abi.rb

Lines changed: 79 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ module ABI
1818

1919
class EncodingError < StandardError; end
2020
class DecodingError < StandardError; end
21-
class ValueOutOfBounds < StandardError; end
21+
class ValueOutOfBounds < ValueError; end
2222

2323
##
2424
# Encodes multiple arguments using the head/tail mechanism.
@@ -54,12 +54,7 @@ def encode_abi(types, args)
5454
#
5555
def encode_type(type, arg)
5656
if %w(string bytes).include?(type.base) && type.sub.empty?
57-
raise ArgumentError, "arg must be a string" unless arg.instance_of?(String)
58-
59-
size = encode_type Type.size_type, arg.size
60-
padding = BYTE_ZERO * (Utils.ceil32(arg.size) - arg.size)
61-
62-
"#{size}#{arg}#{padding}"
57+
encode_primitive_type type, arg
6358
elsif type.dynamic?
6459
raise ArgumentError, "arg must be an array" unless arg.instance_of?(Array)
6560

@@ -94,44 +89,76 @@ def encode_type(type, arg)
9489
def encode_primitive_type(type, arg)
9590
case type.base
9691
when 'uint'
97-
real_size = type.sub.to_i
98-
i = get_uint arg
99-
100-
raise ValueOutOfBounds, arg unless i >= 0 && i < 2**real_size
101-
Utils.zpad_int i
92+
begin
93+
real_size = type.sub.to_i
94+
i = get_uint arg
95+
96+
raise ValueOutOfBounds, arg unless i >= 0 && i < 2**real_size
97+
Utils.zpad_int i
98+
rescue EncodingError
99+
raise ValueOutOfBounds, arg
100+
end
102101
when 'bool'
103102
raise ArgumentError, "arg is not bool: #{arg}" unless arg.instance_of?(TrueClass) || arg.instance_of?(FalseClass)
104-
Utils.zpad_int(arg ? 1: 0)
103+
Utils.zpad_int(arg ? 1 : 0)
105104
when 'int'
106-
real_size = type.sub.to_i
107-
i = get_int arg
108-
109-
raise ValueOutOfBounds, arg unless i >= -2**(real_size-1) && i < 2**(real_size-1)
110-
Utils.zpad_int(i % 2**type.sub.to_i)
111-
when 'ureal', 'ufixed'
105+
begin
106+
real_size = type.sub.to_i
107+
i = get_int arg
108+
109+
raise ValueOutOfBounds, arg unless i >= -2**(real_size-1) && i < 2**(real_size-1)
110+
Utils.zpad_int(i % 2**type.sub.to_i)
111+
rescue EncodingError
112+
raise ValueOutOfBounds, arg
113+
end
114+
when 'ufixed'
112115
high, low = type.sub.split('x').map(&:to_i)
113116

114117
raise ValueOutOfBounds, arg unless arg >= 0 && arg < 2**high
115118
Utils.zpad_int((arg * 2**low).to_i)
116-
when 'real', 'fixed'
119+
when 'fixed'
117120
high, low = type.sub.split('x').map(&:to_i)
118121

119122
raise ValueOutOfBounds, arg unless arg >= -2**(high - 1) && arg < 2**(high - 1)
120123

121124
i = (arg * 2**low).to_i
122125
Utils.zpad_int(i % 2**(high+low))
123-
when 'string', 'bytes'
124-
raise EncodingError, "Expecting string: #{arg}" unless arg.instance_of?(String)
126+
when 'string'
127+
if arg.encoding.name == 'UTF-8'
128+
arg = arg.b
129+
else
130+
begin
131+
arg.unpack('U*')
132+
rescue ArgumentError
133+
raise ValueError, "string must be UTF-8 encoded"
134+
end
135+
end
125136

126137
if type.sub.empty? # variable length type
138+
raise ValueOutOfBounds, "Integer invalid or out of range: #{arg.size}" if arg.size >= TT256
127139
size = Utils.zpad_int arg.size
128-
padding = BYTE_ZERO * (Utils.ceil32(arg.size) - arg.size)
129-
"#{size}#{arg}#{padding}"
140+
value = Utils.rpad arg, BYTE_ZERO, Utils.ceil32(arg.size)
141+
"#{size}#{value}"
130142
else # fixed length type
131-
raise ValueOutOfBounds, arg unless arg.size <= type.sub.to_i
143+
sub = type.sub.to_i
144+
raise ValueOutOfBounds, "invalid string length #{sub}" if arg.size > sub
145+
raise ValueOutOfBounds, "invalid string length #{sub}" if sub < 0 || sub > 32
146+
Utils.rpad(arg, BYTE_ZERO, 32)
147+
end
148+
when 'bytes'
149+
raise EncodingError, "Expecting string: #{arg}" unless arg.instance_of?(String)
150+
arg = arg.b
132151

133-
padding = BYTE_ZERO * (32 - arg.size)
134-
"#{arg}#{padding}"
152+
if type.sub.empty? # variable length type
153+
raise ValueOutOfBounds, "Integer invalid or out of range: #{arg.size}" if arg.size >= TT256
154+
size = Utils.zpad_int arg.size
155+
value = Utils.rpad arg, BYTE_ZERO, Utils.ceil32(arg.size)
156+
"#{size}#{value}"
157+
else # fixed length type
158+
sub = type.sub.to_i
159+
raise ValueOutOfBounds, "invalid bytes length #{sub}" if arg.size > sub
160+
raise ValueOutOfBounds, "invalid bytes length #{sub}" if sub < 0 || sub > 32
161+
Utils.rpad(arg, BYTE_ZERO, 32)
135162
end
136163
when 'hash'
137164
size = type.sub.to_i
@@ -266,10 +293,10 @@ def decode_primitive_type(type, data)
266293
when 'int'
267294
u = Utils.big_endian_to_int data
268295
u >= 2**(type.sub.to_i-1) ? (u - 2**type.sub.to_i) : u
269-
when 'ureal', 'ufixed'
296+
when 'ufixed'
270297
high, low = type.sub.split('x').map(&:to_i)
271298
Utils.big_endian_to_int(data) * 1.0 / 2**low
272-
when 'real', 'fixed'
299+
when 'fixed'
273300
high, low = type.sub.split('x').map(&:to_i)
274301
u = Utils.big_endian_to_int data
275302
i = u >= 2**(high+low-1) ? (u - 2**(high+low)) : u
@@ -289,13 +316,17 @@ def get_uint(n)
289316
raise EncodingError, "Number out of range: #{n}" if n > UINT_MAX || n < UINT_MIN
290317
n
291318
when String
292-
if n.size == 40
293-
Utils.big_endian_to_int Utils.decode_hex(n)
294-
elsif n.size <= 32
295-
Utils.big_endian_to_int n
296-
else
297-
raise EncodingError, "String too long: #{n}"
298-
end
319+
i = if n.size == 40
320+
Utils.decode_hex(n)
321+
elsif n.size <= 32
322+
n
323+
else
324+
raise EncodingError, "String too long: #{n}"
325+
end
326+
i = Utils.big_endian_to_int i
327+
328+
raise EncodingError, "Number out of range: #{i}" if i > UINT_MAX || i < UINT_MIN
329+
i
299330
when true
300331
1
301332
when false, nil
@@ -311,14 +342,18 @@ def get_int(n)
311342
raise EncodingError, "Number out of range: #{n}" if n > INT_MAX || n < INT_MIN
312343
n
313344
when String
314-
if n.size == 40
315-
i = Utils.big_endian_to_int Utils.decode_hex(n)
316-
elsif n.size <= 32
317-
i = Utils.big_endian_to_int n
318-
else
319-
raise EncodingError, "String too long: #{n}"
320-
end
321-
i > INT_MAX ? (i-TT256) : i
345+
i = if n.size == 40
346+
Utils.decode_hex(n)
347+
elsif n.size <= 32
348+
n
349+
else
350+
raise EncodingError, "String too long: #{n}"
351+
end
352+
i = Utils.big_endian_to_int i
353+
354+
i = i > INT_MAX ? (i-TT256) : i
355+
raise EncodingError, "Number out of range: #{i}" if i > INT_MAX || i < INT_MIN
356+
i
322357
when true
323358
1
324359
when false, nil

lib/ethereum/abi/contract_translator.rb

Lines changed: 71 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,77 @@ def encode_constructor_arguments(args)
8888
ABI.encode_abi constructor_data[:encode_types], args
8989
end
9090

91-
def decode(name, data)
91+
##
92+
# Return the function call result decoded.
93+
#
94+
# @param name [String] One of the existing functions described in the
95+
# contract interface.
96+
# @param data [String] The encoded result from calling function `name`.
97+
#
98+
# @return [Array[Object]] The values returned by the call to function
99+
#
100+
def decode_function_result(name, data)
92101
desc = function_data[name]
93102
ABI.decode_abi desc[:decode_types], data
94103
end
104+
alias :decode :decode_function_result
105+
106+
##
107+
# Return a dictionary represent the log.
108+
#
109+
# Notes: this function won't work with anonymous events.
110+
#
111+
# @param log_topics [Array[String]] The log's indexed arguments.
112+
# @param log_data [String] The encoded non-indexed arguments.
113+
#
114+
def decode_event(log_topics, log_data)
115+
# topics[0]: keccak256(normalized_event_name)
116+
raise ValueError, "Unknown log type" unless log_topics.size > 0 && event_data.has_key?(log_topics[0])
117+
118+
event_id = log_topics[0]
119+
event = event_data[event_id]
120+
121+
names = event[:names]
122+
types = event[:types]
123+
indexed = event[:indexed]
124+
125+
unindexed_types = types.zip(indexed).select {|(t, i)| i.false? }.map(&:first)
126+
unindexed_args = ABI.decode_abi unindexed_types, log_data
127+
128+
result = {}
129+
indexed_count = 1 # skip topics[0]
130+
names.each_with_index do |name, i|
131+
v = if indexed[i].true?
132+
topic_bytes = Utils.zpad_int log_topics[indexed_count]
133+
indexed_count += 1
134+
ABI.decode_primitive_type ABI::Type.parse(types[i]), topic_bytes
135+
else
136+
unindexed_args.shift
137+
end
138+
139+
result[name] = v
140+
end
141+
142+
result['_event_type'] = event[:name]
143+
result
144+
end
145+
146+
##.
147+
# Return a dictionary representation of the Log instance.
148+
#
149+
# Note: this function won't work with anonymous events.
150+
#
151+
# @param log [Log] The Log instance that needs to be parsed.
152+
# @param noprint [Bool] Flag to turn off printing of the decoded log
153+
# instance.
154+
#
155+
def listen(log, noprint: true)
156+
result = decode_event log.topics, log.data
157+
p result if noprint
158+
result
159+
rescue ValueError
160+
nil # api compatibility
161+
end
95162

96163
def constructor_data
97164
@contract[:constructor_data]
@@ -113,38 +180,6 @@ def event(name, encode_types)
113180
event_data[event_id(name, encode_types)]
114181
end
115182

116-
def listen(log, noprint: false)
117-
return if log.topics.size == 0 || !event_data.has_key?(log.topics[0])
118-
119-
data = event_data[log.topics[0]]
120-
types = data[:types]
121-
name = data[:name]
122-
names = data[:names]
123-
indexed = data[:indexed]
124-
indexed_types = types.zip(indexed).select {|(t, i)| i.true? }.map(&:first)
125-
unindexed_types = types.zip(indexed).select {|(t, i)| i.false? }.map(&:first)
126-
127-
deserialized_args = ABI.decode_abi unindexed_types, log.data
128-
129-
o = {}
130-
c1, c2 = 0, 0
131-
names.each_with_index do |n, i|
132-
if indexed[i].true?
133-
topic_bytes = Utils.zpad_int log.topics[c1+1]
134-
o[n] = ABI.decode_primitive_type ABI::Type.parse(indexed_types[c1]), topic_bytes
135-
c1 += 1
136-
else
137-
o[n] = deserialized_args[c2]
138-
c2 += 1
139-
end
140-
end
141-
142-
o['_event_type'] = name
143-
p o unless noprint
144-
145-
o
146-
end
147-
148183
def method_id(name, encode_types)
149184
Utils.big_endian_to_int Utils.keccak256(get_sig(name, encode_types))[0,4]
150185
end
@@ -160,14 +195,14 @@ def logger
160195
end
161196

162197
def get_sig(name, encode_types)
163-
"#{name}(#{encode_types.map {|x| canonical_name(x) }.join(',')})"
198+
"#{name}(#{encode_types.map {|x| canonical_type(x) }.join(',')})"
164199
end
165200

166-
def canonical_name(x)
201+
def canonical_type(x)
167202
case x
168203
when /\A(uint|int)(\[.*\])?\z/
169204
"#{$1}256#{$2}"
170-
when /\A(real|ureal|fixed|ufixed)(\[.*\])?\z/
205+
when /\A(fixed|ufixed)(\[.*\])?\z/
171206
"#{$1}128x128#{$2}"
172207
else
173208
x

lib/ethereum/abi/type.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,14 @@ def parse(type)
2828
size = sub.to_i
2929
raise ParseError, "Integer size out of bounds" unless size >= 8 && size <= 256
3030
raise ParseError, "Integer size must be multiple of 8" unless size % 8 == 0
31-
when 'ureal', 'real', 'fixed', 'ufixed'
32-
raise ParseError, "Real type must have suffix of form <high>x<low>, e.g. 128x128" unless sub =~ /\A[0-9]+x[0-9]+\z/
31+
when 'fixed', 'ufixed'
32+
raise ParseError, "Fixed type must have suffix of form <high>x<low>, e.g. 128x128" unless sub =~ /\A[0-9]+x[0-9]+\z/
3333

3434
high, low = sub.split('x').map(&:to_i)
3535
total = high + low
3636

37-
raise ParseError, "Real size out of bounds (max 32 bytes)" unless total >= 8 && total <= 256
38-
raise ParseError, "Real high/low sizes must be multiples of 8" unless high % 8 == 0 && low % 8 == 0
37+
raise ParseError, "Fixed size out of bounds (max 32 bytes)" unless total >= 8 && total <= 256
38+
raise ParseError, "Fixed high/low sizes must be multiples of 8" unless high % 8 == 0 && low % 8 == 0
3939
when 'hash'
4040
raise ParseError, "Hash type must have numerical suffix" unless sub =~ /\A[0-9]+\z/
4141
when 'address'

lib/ethereum/address.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ def blank?
3131
private
3232

3333
def parse(s)
34+
return Utils.int_to_addr(s) if s.is_a?(Integer)
35+
3436
case s.size
3537
when 0
3638
s

0 commit comments

Comments
 (0)