ethlite 0.1.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Manifest.txt +10 -0
- data/Rakefile +2 -0
- data/lib/digest/keccak256.rb +111 -0
- data/lib/digest/sha3.rb +108 -0
- data/lib/ethlite/abi/abi_coder.rb +423 -0
- data/lib/ethlite/abi/constant.rb +32 -0
- data/lib/ethlite/abi/exceptions.rb +29 -0
- data/lib/ethlite/abi/type.rb +203 -0
- data/lib/ethlite/abi/utils.rb +227 -0
- data/lib/ethlite/contract.rb +64 -0
- data/lib/ethlite/rpc.rb +48 -0
- data/lib/ethlite/utility.rb +23 -0
- data/lib/ethlite/version.rb +2 -2
- data/lib/ethlite.rb +31 -0
- metadata +25 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5dea4d9654501b9b1fa7a86477a0b509e7722975fdf29ce3c95e060750d917f7
|
4
|
+
data.tar.gz: 25b082b2803cb0404b39cab23b62740c716680f22d3f61825e364f9dfd387640
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6c28f0334a44bdbb3c2b4d2f375b94ff9ee643a49aa66953eac3bcd471a78398f2c89f8f8b150f64a1d64d19c2a47590f793b4c88ab7c41c045330a44a85a06b
|
7
|
+
data.tar.gz: 45ff4d1b251eb7273f8fa1034b92bb2a95164796268e2d0a69d26f3567e7b806163ab7d851ef8d314d75bb61845d0117ae56fd7af089098ff43e3940dc1c7b2d
|
data/Manifest.txt
CHANGED
@@ -2,5 +2,15 @@ CHANGELOG.md
|
|
2
2
|
Manifest.txt
|
3
3
|
README.md
|
4
4
|
Rakefile
|
5
|
+
lib/digest/keccak256.rb
|
6
|
+
lib/digest/sha3.rb
|
5
7
|
lib/ethlite.rb
|
8
|
+
lib/ethlite/abi/abi_coder.rb
|
9
|
+
lib/ethlite/abi/constant.rb
|
10
|
+
lib/ethlite/abi/exceptions.rb
|
11
|
+
lib/ethlite/abi/type.rb
|
12
|
+
lib/ethlite/abi/utils.rb
|
13
|
+
lib/ethlite/contract.rb
|
14
|
+
lib/ethlite/rpc.rb
|
15
|
+
lib/ethlite/utility.rb
|
6
16
|
lib/ethlite/version.rb
|
data/Rakefile
CHANGED
@@ -0,0 +1,111 @@
|
|
1
|
+
###
|
2
|
+
# use a "vendor" bundled keccak 256 in all ruby (no c-extensions required)
|
3
|
+
#
|
4
|
+
# see https://github.com/evtaylor/keccak256
|
5
|
+
|
6
|
+
|
7
|
+
|
8
|
+
## require 'digest'
|
9
|
+
|
10
|
+
|
11
|
+
module Digest
|
12
|
+
class Keccak256 < Digest::Class
|
13
|
+
PILN = [10, 7, 11, 17, 18, 3, 5, 16,
|
14
|
+
8, 21, 24, 4, 15, 23, 19, 13,
|
15
|
+
12, 2, 20, 14, 22, 9, 6, 1]
|
16
|
+
|
17
|
+
ROTC = [ 1, 3, 6, 10, 15, 21, 28, 36,
|
18
|
+
45, 55, 2, 14, 27, 41, 56, 8,
|
19
|
+
25, 43, 62, 18, 39, 61, 20, 44]
|
20
|
+
|
21
|
+
RNDC = [0x0000000000000001, 0x0000000000008082, 0x800000000000808a,
|
22
|
+
0x8000000080008000, 0x000000000000808b, 0x0000000080000001,
|
23
|
+
0x8000000080008081, 0x8000000000008009, 0x000000000000008a,
|
24
|
+
0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
|
25
|
+
0x000000008000808b, 0x800000000000008b, 0x8000000000008089,
|
26
|
+
0x8000000000008003, 0x8000000000008002, 0x8000000000000080,
|
27
|
+
0x000000000000800a, 0x800000008000000a, 0x8000000080008081,
|
28
|
+
0x8000000000008080, 0x0000000080000001, 0x8000000080008008]
|
29
|
+
|
30
|
+
def initialize
|
31
|
+
@size = 256 / 8
|
32
|
+
@buffer = ''
|
33
|
+
end
|
34
|
+
|
35
|
+
def << s
|
36
|
+
@buffer << s
|
37
|
+
self
|
38
|
+
end
|
39
|
+
alias update <<
|
40
|
+
|
41
|
+
def reset
|
42
|
+
@buffer.clear
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
def finish
|
47
|
+
s = Array.new 25, 0
|
48
|
+
width = 200 - @size * 2
|
49
|
+
padding = "\x01"
|
50
|
+
|
51
|
+
buffer = @buffer
|
52
|
+
buffer << padding << "\0" * (width - buffer.size % width)
|
53
|
+
buffer[-1] = (buffer[-1].ord | 0x80).chr
|
54
|
+
|
55
|
+
0.step buffer.size - 1, width do |j|
|
56
|
+
quads = buffer[j, width].unpack 'Q*'
|
57
|
+
(width / 8).times do |i|
|
58
|
+
s[i] ^= quads[i]
|
59
|
+
end
|
60
|
+
|
61
|
+
keccak s
|
62
|
+
end
|
63
|
+
|
64
|
+
s.pack('Q*')[0, @size]
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
def keccak s
|
69
|
+
24.times.each_with_object [] do |round, a|
|
70
|
+
# Theta
|
71
|
+
5.times do |i|
|
72
|
+
a[i] = s[i] ^ s[i + 5] ^ s[i + 10] ^ s[i + 15] ^ s[i + 20]
|
73
|
+
end
|
74
|
+
|
75
|
+
5.times do |i|
|
76
|
+
t = a[(i + 4) % 5] ^ rotate(a[(i + 1) % 5], 1)
|
77
|
+
0.step 24, 5 do |j|
|
78
|
+
s[j + i] ^= t
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Rho Pi
|
83
|
+
t = s[1]
|
84
|
+
24.times do |i|
|
85
|
+
j = PILN[i]
|
86
|
+
a[0] = s[j]
|
87
|
+
s[j] = rotate t, ROTC[i]
|
88
|
+
t = a[0]
|
89
|
+
end
|
90
|
+
|
91
|
+
# Chi
|
92
|
+
0.step 24, 5 do |j|
|
93
|
+
5.times do |i|
|
94
|
+
a[i] = s[j + i]
|
95
|
+
end
|
96
|
+
|
97
|
+
5.times do |i|
|
98
|
+
s[j + i] ^= ~a[(i + 1) % 5] & a[(i + 2) % 5]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Iota
|
103
|
+
s[0] ^= RNDC[round]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def rotate x, y
|
108
|
+
(x << y | x >> 64 - y) & (1 << 64) - 1
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
data/lib/digest/sha3.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
###
|
2
|
+
# use a "vendor" bundled sha3 in all ruby (no c-extensions required)
|
3
|
+
#
|
4
|
+
# see https://github.com/havenwood/sha3-pure-ruby/blob/master/lib/sha3-pure-ruby.rb
|
5
|
+
|
6
|
+
|
7
|
+
## require 'digest'
|
8
|
+
|
9
|
+
module Digest
|
10
|
+
class SHA3 < Digest::Class
|
11
|
+
PILN = [10, 7, 11, 17, 18, 3, 5, 16,
|
12
|
+
8, 21, 24, 4, 15, 23, 19, 13,
|
13
|
+
12, 2, 20, 14, 22, 9, 6, 1]
|
14
|
+
|
15
|
+
ROTC = [ 1, 3, 6, 10, 15, 21, 28, 36,
|
16
|
+
45, 55, 2, 14, 27, 41, 56, 8,
|
17
|
+
25, 43, 62, 18, 39, 61, 20, 44]
|
18
|
+
|
19
|
+
RNDC = [0x0000000000000001, 0x0000000000008082, 0x800000000000808a,
|
20
|
+
0x8000000080008000, 0x000000000000808b, 0x0000000080000001,
|
21
|
+
0x8000000080008081, 0x8000000000008009, 0x000000000000008a,
|
22
|
+
0x0000000000000088, 0x0000000080008009, 0x000000008000000a,
|
23
|
+
0x000000008000808b, 0x800000000000008b, 0x8000000000008089,
|
24
|
+
0x8000000000008003, 0x8000000000008002, 0x8000000000000080,
|
25
|
+
0x000000000000800a, 0x800000008000000a, 0x8000000080008081,
|
26
|
+
0x8000000000008080, 0x0000000080000001, 0x8000000080008008]
|
27
|
+
|
28
|
+
def initialize hash_size = 512
|
29
|
+
@size = hash_size / 8
|
30
|
+
@buffer = ''
|
31
|
+
end
|
32
|
+
|
33
|
+
def << s
|
34
|
+
@buffer << s
|
35
|
+
self
|
36
|
+
end
|
37
|
+
alias update <<
|
38
|
+
|
39
|
+
def reset
|
40
|
+
@buffer.clear
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def finish
|
45
|
+
s = Array.new 25, 0
|
46
|
+
width = 200 - @size * 2
|
47
|
+
|
48
|
+
buffer = @buffer
|
49
|
+
buffer << "\x06" << "\0" * (width - buffer.size % width)
|
50
|
+
buffer[-1] = (buffer[-1].ord | 0x80).chr
|
51
|
+
|
52
|
+
0.step buffer.size - 1, width do |j|
|
53
|
+
quads = buffer[j, width].unpack 'Q*'
|
54
|
+
(width / 8).times do |i|
|
55
|
+
s[i] ^= quads[i]
|
56
|
+
end
|
57
|
+
|
58
|
+
keccak s
|
59
|
+
end
|
60
|
+
|
61
|
+
s.pack('Q*')[0, @size]
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
def keccak s
|
66
|
+
24.times.each_with_object [] do |round, a|
|
67
|
+
# Theta
|
68
|
+
5.times do |i|
|
69
|
+
a[i] = s[i] ^ s[i + 5] ^ s[i + 10] ^ s[i + 15] ^ s[i + 20]
|
70
|
+
end
|
71
|
+
|
72
|
+
5.times do |i|
|
73
|
+
t = a[(i + 4) % 5] ^ rotate(a[(i + 1) % 5], 1)
|
74
|
+
0.step 24, 5 do |j|
|
75
|
+
s[j + i] ^= t
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Rho Pi
|
80
|
+
t = s[1]
|
81
|
+
24.times do |i|
|
82
|
+
j = PILN[i]
|
83
|
+
a[0] = s[j]
|
84
|
+
s[j] = rotate t, ROTC[i]
|
85
|
+
t = a[0]
|
86
|
+
end
|
87
|
+
|
88
|
+
# Chi
|
89
|
+
0.step 24, 5 do |j|
|
90
|
+
5.times do |i|
|
91
|
+
a[i] = s[j + i]
|
92
|
+
end
|
93
|
+
|
94
|
+
5.times do |i|
|
95
|
+
s[j + i] ^= ~a[(i + 1) % 5] & a[(i + 2) % 5]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Iota
|
100
|
+
s[0] ^= RNDC[round]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def rotate x, y
|
105
|
+
(x << y | x >> 64 - y) & (1 << 64) - 1
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,423 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Ethlite
|
4
|
+
module Abi
|
5
|
+
|
6
|
+
##
|
7
|
+
# Contract ABI encoding and decoding.
|
8
|
+
#
|
9
|
+
# @see https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI
|
10
|
+
#
|
11
|
+
module AbiCoder
|
12
|
+
|
13
|
+
extend self
|
14
|
+
|
15
|
+
include Constant
|
16
|
+
|
17
|
+
class EncodingError < StandardError; end
|
18
|
+
class DecodingError < StandardError; end
|
19
|
+
class ValueOutOfBounds < ValueError; end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Encodes multiple arguments using the head/tail mechanism.
|
23
|
+
#
|
24
|
+
def encode_abi(types, args)
|
25
|
+
parsed_types = types.map {|t| Type.parse(t) }
|
26
|
+
|
27
|
+
head_size = (0...args.size)
|
28
|
+
.map {|i| parsed_types[i].size || 32 }
|
29
|
+
.reduce(0, &:+)
|
30
|
+
|
31
|
+
head, tail = '', ''
|
32
|
+
args.each_with_index do |arg, i|
|
33
|
+
if parsed_types[i].dynamic?
|
34
|
+
head += encode_type(Type.size_type, head_size + tail.size)
|
35
|
+
tail += encode_type(parsed_types[i], arg)
|
36
|
+
else
|
37
|
+
head += encode_type(parsed_types[i], arg)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
"#{head}#{tail}"
|
42
|
+
end
|
43
|
+
alias :encode :encode_abi
|
44
|
+
|
45
|
+
##
|
46
|
+
# Encodes a single value (static or dynamic).
|
47
|
+
#
|
48
|
+
# @param type [Ethereum::ABI::Type] value type
|
49
|
+
# @param arg [Object] value
|
50
|
+
#
|
51
|
+
# @return [String] encoded bytes
|
52
|
+
#
|
53
|
+
def encode_type(type, arg)
|
54
|
+
if %w(string bytes).include?(type.base) && type.sub.empty?
|
55
|
+
encode_primitive_type type, arg
|
56
|
+
elsif type.dynamic?
|
57
|
+
raise ArgumentError, "arg must be an array" unless arg.instance_of?(Array)
|
58
|
+
|
59
|
+
head, tail = '', ''
|
60
|
+
if type.dims.last == 0
|
61
|
+
head += encode_type(Type.size_type, arg.size)
|
62
|
+
else
|
63
|
+
raise ArgumentError, "Wrong array size: found #{arg.size}, expecting #{type.dims.last}" unless arg.size == type.dims.last
|
64
|
+
end
|
65
|
+
|
66
|
+
sub_type = type.subtype
|
67
|
+
sub_size = type.subtype.size
|
68
|
+
arg.size.times do |i|
|
69
|
+
if sub_size.nil?
|
70
|
+
head += encode_type(Type.size_type, 32*arg.size + tail.size)
|
71
|
+
tail += encode_type(sub_type, arg[i])
|
72
|
+
else
|
73
|
+
head += encode_type(sub_type, arg[i])
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
"#{head}#{tail}"
|
78
|
+
else # static type
|
79
|
+
if type.dims.empty?
|
80
|
+
encode_primitive_type type, arg
|
81
|
+
else
|
82
|
+
arg.map {|x| encode_type(type.subtype, x) }.join
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def encode_primitive_type(type, arg)
|
88
|
+
case type.base
|
89
|
+
when 'uint'
|
90
|
+
begin
|
91
|
+
real_size = type.sub.to_i
|
92
|
+
i = get_uint arg
|
93
|
+
|
94
|
+
raise ValueOutOfBounds, arg unless i >= 0 && i < 2**real_size
|
95
|
+
Utils.zpad_int i
|
96
|
+
rescue EncodingError
|
97
|
+
raise ValueOutOfBounds, arg
|
98
|
+
end
|
99
|
+
when 'bool'
|
100
|
+
raise ArgumentError, "arg is not bool: #{arg}" unless arg.instance_of?(TrueClass) || arg.instance_of?(FalseClass)
|
101
|
+
Utils.zpad_int(arg ? 1 : 0)
|
102
|
+
when 'int'
|
103
|
+
begin
|
104
|
+
real_size = type.sub.to_i
|
105
|
+
i = get_int arg
|
106
|
+
|
107
|
+
raise ValueOutOfBounds, arg unless i >= -2**(real_size-1) && i < 2**(real_size-1)
|
108
|
+
Utils.zpad_int(i % 2**type.sub.to_i)
|
109
|
+
rescue EncodingError
|
110
|
+
raise ValueOutOfBounds, arg
|
111
|
+
end
|
112
|
+
when 'ufixed'
|
113
|
+
high, low = type.sub.split('x').map(&:to_i)
|
114
|
+
|
115
|
+
raise ValueOutOfBounds, arg unless arg >= 0 && arg < 2**high
|
116
|
+
Utils.zpad_int((arg * 2**low).to_i)
|
117
|
+
when 'fixed'
|
118
|
+
high, low = type.sub.split('x').map(&:to_i)
|
119
|
+
|
120
|
+
raise ValueOutOfBounds, arg unless arg >= -2**(high - 1) && arg < 2**(high - 1)
|
121
|
+
|
122
|
+
i = (arg * 2**low).to_i
|
123
|
+
Utils.zpad_int(i % 2**(high+low))
|
124
|
+
when 'string'
|
125
|
+
if arg.encoding.name == 'UTF-8'
|
126
|
+
arg = arg.b
|
127
|
+
else
|
128
|
+
begin
|
129
|
+
arg.unpack('U*')
|
130
|
+
rescue ArgumentError
|
131
|
+
raise ValueError, "string must be UTF-8 encoded"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
if type.sub.empty? # variable length type
|
136
|
+
raise ValueOutOfBounds, "Integer invalid or out of range: #{arg.size}" if arg.size >= TT256
|
137
|
+
size = Utils.zpad_int arg.size
|
138
|
+
value = Utils.rpad arg, BYTE_ZERO, Utils.ceil32(arg.size)
|
139
|
+
"#{size}#{value}"
|
140
|
+
else # fixed length type
|
141
|
+
sub = type.sub.to_i
|
142
|
+
raise ValueOutOfBounds, "invalid string length #{sub}" if arg.size > sub
|
143
|
+
raise ValueOutOfBounds, "invalid string length #{sub}" if sub < 0 || sub > 32
|
144
|
+
Utils.rpad(arg, BYTE_ZERO, 32)
|
145
|
+
end
|
146
|
+
when 'bytes'
|
147
|
+
raise EncodingError, "Expecting string: #{arg}" unless arg.instance_of?(String)
|
148
|
+
arg = arg.b
|
149
|
+
|
150
|
+
if type.sub.empty? # variable length type
|
151
|
+
raise ValueOutOfBounds, "Integer invalid or out of range: #{arg.size}" if arg.size >= TT256
|
152
|
+
size = Utils.zpad_int arg.size
|
153
|
+
value = Utils.rpad arg, BYTE_ZERO, Utils.ceil32(arg.size)
|
154
|
+
"#{size}#{value}"
|
155
|
+
else # fixed length type
|
156
|
+
sub = type.sub.to_i
|
157
|
+
raise ValueOutOfBounds, "invalid bytes length #{sub}" if arg.size > sub
|
158
|
+
raise ValueOutOfBounds, "invalid bytes length #{sub}" if sub < 0 || sub > 32
|
159
|
+
Utils.rpad(arg, BYTE_ZERO, 32)
|
160
|
+
end
|
161
|
+
when 'hash'
|
162
|
+
size = type.sub.to_i
|
163
|
+
raise EncodingError, "too long: #{arg}" unless size > 0 && size <= 32
|
164
|
+
|
165
|
+
if arg.is_a?(Integer)
|
166
|
+
Utils.zpad_int(arg)
|
167
|
+
elsif arg.size == size
|
168
|
+
Utils.zpad arg, 32
|
169
|
+
elsif arg.size == size * 2
|
170
|
+
Utils.zpad_hex arg
|
171
|
+
else
|
172
|
+
raise EncodingError, "Could not parse hash: #{arg}"
|
173
|
+
end
|
174
|
+
when 'address'
|
175
|
+
if arg.is_a?(Integer)
|
176
|
+
Utils.zpad_int arg
|
177
|
+
elsif arg.size == 20
|
178
|
+
Utils.zpad arg, 32
|
179
|
+
elsif arg.size == 40
|
180
|
+
Utils.zpad_hex arg
|
181
|
+
elsif arg.size == 42 && arg[0,2] == '0x'
|
182
|
+
Utils.zpad_hex arg[2..-1]
|
183
|
+
else
|
184
|
+
raise EncodingError, "Could not parse address: #{arg}"
|
185
|
+
end
|
186
|
+
else
|
187
|
+
raise EncodingError, "Unhandled type: #{type.base} #{type.sub}"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
def min_data_size types
|
193
|
+
types.size*32
|
194
|
+
end
|
195
|
+
|
196
|
+
##
|
197
|
+
# Decodes multiple arguments using the head/tail mechanism.
|
198
|
+
#
|
199
|
+
def decode_abi types, data, raise_errors = false
|
200
|
+
parsed_types = types.map {|t| Type.parse(t) }
|
201
|
+
|
202
|
+
outputs = [nil] * types.size
|
203
|
+
start_positions = [nil] * types.size + [data.size]
|
204
|
+
|
205
|
+
# TODO: refactor, a reverse iteration will be better
|
206
|
+
pos = 0
|
207
|
+
parsed_types.each_with_index do |t, i|
|
208
|
+
# If a type is static, grab the data directly, otherwise record its
|
209
|
+
# start position
|
210
|
+
if t.dynamic?
|
211
|
+
|
212
|
+
if raise_errors && pos>data.size-1
|
213
|
+
raise DecodingError, "Position out of bounds #{pos}>#{data.size-1}"
|
214
|
+
end
|
215
|
+
|
216
|
+
start_positions[i] = Utils.big_endian_to_int(data[pos, 32])
|
217
|
+
|
218
|
+
if raise_errors && start_positions[i]>data.size-1
|
219
|
+
raise DecodingError, "Start position out of bounds #{start_positions[i]}>#{data.size-1}"
|
220
|
+
end
|
221
|
+
|
222
|
+
j = i - 1
|
223
|
+
while j >= 0 && start_positions[j].nil?
|
224
|
+
start_positions[j] = start_positions[i]
|
225
|
+
j -= 1
|
226
|
+
end
|
227
|
+
|
228
|
+
pos += 32
|
229
|
+
else
|
230
|
+
outputs[i] = zero_padding data, pos, t.size, start_positions
|
231
|
+
pos += t.size
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
# We add a start position equal to the length of the entire data for
|
236
|
+
# convenience.
|
237
|
+
j = types.size - 1
|
238
|
+
while j >= 0 && start_positions[j].nil?
|
239
|
+
start_positions[j] = start_positions[types.size]
|
240
|
+
j -= 1
|
241
|
+
end
|
242
|
+
|
243
|
+
if raise_errors && pos > data.size
|
244
|
+
raise DecodingError, "Not enough data for head"
|
245
|
+
end
|
246
|
+
|
247
|
+
|
248
|
+
parsed_types.each_with_index do |t, i|
|
249
|
+
if t.dynamic?
|
250
|
+
offset, next_offset = start_positions[i, 2]
|
251
|
+
if offset<=data.size && next_offset<=data.size
|
252
|
+
outputs[i] = data[offset...next_offset]
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
if raise_errors && outputs.include?(nil)
|
258
|
+
raise DecodingError, "Not all data can be parsed"
|
259
|
+
end
|
260
|
+
|
261
|
+
parsed_types.zip(outputs).map {|(type, out)| decode_type(type, out) }
|
262
|
+
end
|
263
|
+
alias :decode :decode_abi
|
264
|
+
|
265
|
+
def zero_padding data, pos, count, start_positions
|
266
|
+
if pos >= data.size
|
267
|
+
start_positions[start_positions.size-1] += count
|
268
|
+
"\x00"*count
|
269
|
+
elsif pos + count > data.size
|
270
|
+
start_positions[start_positions.size-1] += ( count - (data.size-pos))
|
271
|
+
data[pos,data.size-pos] + "\x00"*( count - (data.size-pos))
|
272
|
+
else
|
273
|
+
data[pos, count]
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
def decode_typed_data type_name, data
|
278
|
+
decode_primitive_type Type.parse(type_name), data
|
279
|
+
end
|
280
|
+
|
281
|
+
def decode_type(type, arg)
|
282
|
+
return nil if arg.nil? || arg.empty?
|
283
|
+
if type.kind_of?(Tuple) && type.dims.empty?
|
284
|
+
arg ? decode_abi(type.types, arg) : []
|
285
|
+
elsif %w(string bytes).include?(type.base) && type.sub.empty?
|
286
|
+
l = Utils.big_endian_to_int arg[0,32]
|
287
|
+
data = arg[32..-1]
|
288
|
+
data[0, l]
|
289
|
+
elsif !type.dims.empty? && (l = type.dims.last)>0 # static-sized arrays
|
290
|
+
subtype = type.subtype
|
291
|
+
if subtype.dynamic?
|
292
|
+
start_positions = (0...l).map {|i| Utils.big_endian_to_int(arg[32*i, 32]) }
|
293
|
+
start_positions.push arg.size
|
294
|
+
|
295
|
+
outputs = (0...l).map {|i| arg[start_positions[i]...start_positions[i+1]] }
|
296
|
+
|
297
|
+
outputs.map {|out| decode_type(subtype, out) }
|
298
|
+
else
|
299
|
+
(0...l).map {|i| decode_type(subtype, arg[subtype.size*i, subtype.size]) }
|
300
|
+
end
|
301
|
+
|
302
|
+
elsif type.dynamic?
|
303
|
+
l = Utils.big_endian_to_int arg[0,32]
|
304
|
+
raise DecodingError, "Too long length: #{l}" if l>100000
|
305
|
+
subtype = type.subtype
|
306
|
+
|
307
|
+
if subtype.dynamic?
|
308
|
+
raise DecodingError, "Not enough data for head" unless arg.size >= 32 + 32*l
|
309
|
+
|
310
|
+
start_positions = (1..l).map {|i| 32+Utils.big_endian_to_int(arg[32*i, 32]) }
|
311
|
+
start_positions.push arg.size
|
312
|
+
|
313
|
+
outputs = (0...l).map {|i| arg[start_positions[i]...start_positions[i+1]] }
|
314
|
+
|
315
|
+
outputs.map {|out| decode_type(subtype, out) }
|
316
|
+
else
|
317
|
+
(0...l).map {|i| decode_type(subtype, arg[32 + subtype.size*i, subtype.size]) }
|
318
|
+
end
|
319
|
+
|
320
|
+
else
|
321
|
+
decode_primitive_type type, arg
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
def decode_primitive_type(type, data)
|
326
|
+
case type.base
|
327
|
+
when 'address'
|
328
|
+
Utils.encode_hex data[12..-1]
|
329
|
+
when 'string', 'bytes'
|
330
|
+
if type.sub.empty? # dynamic
|
331
|
+
if data.length==32
|
332
|
+
data[0..32]
|
333
|
+
else
|
334
|
+
size = Utils.big_endian_to_int data[0,32]
|
335
|
+
data[32..-1][0,size]
|
336
|
+
end
|
337
|
+
else # fixed
|
338
|
+
data[0, type.sub.to_i]
|
339
|
+
end
|
340
|
+
when 'hash'
|
341
|
+
data[(32 - type.sub.to_i), type.sub.to_i]
|
342
|
+
when 'uint'
|
343
|
+
Utils.big_endian_to_int data
|
344
|
+
when 'int'
|
345
|
+
u = Utils.big_endian_to_int data
|
346
|
+
u >= 2**(type.sub.to_i-1) ? (u - 2**type.sub.to_i) : u
|
347
|
+
when 'ufixed'
|
348
|
+
high, low = type.sub.split('x').map(&:to_i)
|
349
|
+
Utils.big_endian_to_int(data) * 1.0 / 2**low
|
350
|
+
when 'fixed'
|
351
|
+
high, low = type.sub.split('x').map(&:to_i)
|
352
|
+
u = Utils.big_endian_to_int data
|
353
|
+
i = u >= 2**(high+low-1) ? (u - 2**(high+low)) : u
|
354
|
+
i * 1.0 / 2**low
|
355
|
+
when 'bool'
|
356
|
+
data[-1] == BYTE_ONE
|
357
|
+
else
|
358
|
+
raise DecodingError, "Unknown primitive type: #{type.base}"
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
private
|
363
|
+
|
364
|
+
def get_uint(n)
|
365
|
+
case n
|
366
|
+
when Integer
|
367
|
+
raise EncodingError, "Number out of range: #{n}" if n > UINT_MAX || n < UINT_MIN
|
368
|
+
n
|
369
|
+
when String
|
370
|
+
i = if n.size == 40
|
371
|
+
Utils.decode_hex(n)
|
372
|
+
elsif n.size <= 32
|
373
|
+
n
|
374
|
+
else
|
375
|
+
raise EncodingError, "String too long: #{n}"
|
376
|
+
end
|
377
|
+
i = Utils.big_endian_to_int i
|
378
|
+
|
379
|
+
raise EncodingError, "Number out of range: #{i}" if i > UINT_MAX || i < UINT_MIN
|
380
|
+
i
|
381
|
+
when true
|
382
|
+
1
|
383
|
+
when false, nil
|
384
|
+
0
|
385
|
+
else
|
386
|
+
raise EncodingError, "Cannot decode uint: #{n}"
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
def get_int(n)
|
391
|
+
case n
|
392
|
+
when Integer
|
393
|
+
raise EncodingError, "Number out of range: #{n}" if n > INT_MAX || n < INT_MIN
|
394
|
+
n
|
395
|
+
when String
|
396
|
+
i = if n.size == 40
|
397
|
+
Utils.decode_hex(n)
|
398
|
+
elsif n.size <= 32
|
399
|
+
n
|
400
|
+
else
|
401
|
+
raise EncodingError, "String too long: #{n}"
|
402
|
+
end
|
403
|
+
i = Utils.big_endian_to_int i
|
404
|
+
|
405
|
+
i = i > INT_MAX ? (i-TT256) : i
|
406
|
+
raise EncodingError, "Number out of range: #{i}" if i > INT_MAX || i < INT_MIN
|
407
|
+
i
|
408
|
+
when true
|
409
|
+
1
|
410
|
+
when false, nil
|
411
|
+
0
|
412
|
+
else
|
413
|
+
raise EncodingError, "Cannot decode int: #{n}"
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
end
|
418
|
+
|
419
|
+
end # module Abi
|
420
|
+
end # module Ethlite
|
421
|
+
|
422
|
+
|
423
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
module Ethlite
|
3
|
+
module Abi
|
4
|
+
module Constant
|
5
|
+
|
6
|
+
BYTE_EMPTY = "".freeze
|
7
|
+
BYTE_ZERO = "\x00".freeze
|
8
|
+
BYTE_ONE = "\x01".freeze
|
9
|
+
|
10
|
+
TT32 = 2**32
|
11
|
+
TT40 = 2**40
|
12
|
+
TT160 = 2**160
|
13
|
+
TT256 = 2**256
|
14
|
+
TT64M1 = 2**64 - 1
|
15
|
+
|
16
|
+
UINT_MAX = 2**256 - 1
|
17
|
+
UINT_MIN = 0
|
18
|
+
INT_MAX = 2**255 - 1
|
19
|
+
INT_MIN = -2**255
|
20
|
+
|
21
|
+
HASH_ZERO = ("\x00"*32).freeze
|
22
|
+
|
23
|
+
PUBKEY_ZERO = ("\x00"*32).freeze
|
24
|
+
PRIVKEY_ZERO = ("\x00"*32).freeze
|
25
|
+
PRIVKEY_ZERO_HEX = ('0'*64).freeze
|
26
|
+
|
27
|
+
CONTRACT_CODE_SIZE_LIMIT = 0x6000
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end # module Abi
|
32
|
+
end # module Ethlite
|