abicoder 0.1.0 → 0.1.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 +4 -4
- data/README.md +87 -18
- data/lib/abicoder/decoder.rb +208 -0
- data/lib/abicoder/encoder.rb +279 -0
- data/lib/abicoder/parser.rb +158 -0
- data/lib/abicoder/types.rb +155 -0
- data/lib/abicoder/version.rb +1 -1
- data/lib/abicoder.rb +36 -14
- metadata +6 -6
- data/lib/abicoder/codec.rb +0 -413
- data/lib/abicoder/type.rb +0 -141
- data/lib/abicoder/type_tuple.rb +0 -106
- data/lib/abicoder/utils.rb +0 -81
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3bc1e75e6080a55ae3447f7cfd3630ed587edf021658dd9159584fbb3ef28743
|
4
|
+
data.tar.gz: 84d27e90d278d0c2fc84db605005735164b4a63c084c087777218eadadb0044b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 597f54c3c8e21027bb10d326c7c6763653d752ad3285abc1c2086023d17c6dc9207d8e99f16a008265bff3a2ad61e1f261db501911c46df56a509660102dc019
|
7
|
+
data.tar.gz: b776e32b5afd57e233aef6ffb87dba66b65272533f60f635c371e0a199fad593a1bd55cd6248f39a3002849b66e77ecf1bb7bd06f3ccc453cc87854d0c77d059
|
data/Manifest.txt
CHANGED
@@ -3,8 +3,8 @@ Manifest.txt
|
|
3
3
|
README.md
|
4
4
|
Rakefile
|
5
5
|
lib/abicoder.rb
|
6
|
-
lib/abicoder/
|
7
|
-
lib/abicoder/
|
8
|
-
lib/abicoder/
|
9
|
-
lib/abicoder/
|
6
|
+
lib/abicoder/decoder.rb
|
7
|
+
lib/abicoder/encoder.rb
|
8
|
+
lib/abicoder/parser.rb
|
9
|
+
lib/abicoder/types.rb
|
10
10
|
lib/abicoder/version.rb
|
data/README.md
CHANGED
@@ -13,6 +13,9 @@ abicoder - "lite" application binary interface (abi) encoding / decoding machine
|
|
13
13
|
## Usage
|
14
14
|
|
15
15
|
|
16
|
+
### Encode & Decode Types (Contract Function Call Data)
|
17
|
+
|
18
|
+
|
16
19
|
``` ruby
|
17
20
|
require 'abicode'
|
18
21
|
|
@@ -20,38 +23,53 @@ require 'abicode'
|
|
20
23
|
# try ABI.encode
|
21
24
|
|
22
25
|
## Encoding simple types
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
#
|
28
|
-
#
|
26
|
+
types = [ 'uint256', 'string' ]
|
27
|
+
args = [ 1234, 'Hello World' ]
|
28
|
+
ABI.encode( types, args ) # returns binary blob / string
|
29
|
+
#=> hex"00000000000000000000000000000000000000000000000000000000000004d2"+
|
30
|
+
# "0000000000000000000000000000000000000000000000000000000000000040"+
|
31
|
+
# "000000000000000000000000000000000000000000000000000000000000000b"+
|
32
|
+
# "48656c6c6f20576f726c64000000000000000000000000000000000000000000"
|
29
33
|
|
30
34
|
## Encoding with arrays types
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
35
|
+
types = [ 'uint256[]', 'string' ]
|
36
|
+
args = [ [1234, 5678] , 'Hello World' ]
|
37
|
+
ABI.encode( types, args ) # returns binary blob / string
|
38
|
+
#=> hex"0000000000000000000000000000000000000000000000000000000000000040"+
|
39
|
+
# "00000000000000000000000000000000000000000000000000000000000000a0"+
|
40
|
+
# "0000000000000000000000000000000000000000000000000000000000000002"+
|
41
|
+
# "00000000000000000000000000000000000000000000000000000000000004d2"+
|
42
|
+
# "000000000000000000000000000000000000000000000000000000000000162e"+
|
43
|
+
# "000000000000000000000000000000000000000000000000000000000000000b"+
|
44
|
+
# "48656c6c6f20576f726c64000000000000000000000000000000000000000000"
|
45
|
+
|
46
|
+
## Encoding complex structs (also known as tuples)
|
47
|
+
types = [ 'uint256', '(uint256,string)']
|
48
|
+
args = [1234, [5678, 'Hello World']]
|
49
|
+
ABI.encode( types, args ) # returns binary blob / string
|
50
|
+
#=> hex'00000000000000000000000000000000000000000000000000000000000004d2'+
|
51
|
+
# '0000000000000000000000000000000000000000000000000000000000000040'+
|
52
|
+
# '000000000000000000000000000000000000000000000000000000000000162e'+
|
53
|
+
# '0000000000000000000000000000000000000000000000000000000000000040'+
|
54
|
+
# '000000000000000000000000000000000000000000000000000000000000000b'+
|
55
|
+
# '48656c6c6f20576f726c64000000000000000000000000000000000000000000'
|
40
56
|
|
41
57
|
|
42
58
|
#####
|
43
59
|
# try ABI.decode
|
44
60
|
|
45
61
|
## Decoding simple types
|
62
|
+
types = [ 'uint256', 'string' ]
|
46
63
|
data = hex'00000000000000000000000000000000000000000000000000000000000004d2'+
|
47
64
|
'0000000000000000000000000000000000000000000000000000000000000040'+
|
48
65
|
'000000000000000000000000000000000000000000000000000000000000000b'+
|
49
66
|
'48656c6c6f20576f726c64000000000000000000000000000000000000000000'
|
50
|
-
ABI.decode(
|
67
|
+
ABI.decode( types, data) # returns args
|
51
68
|
#=> [1234, "Hello World"]
|
52
69
|
|
53
70
|
|
54
71
|
## Decoding with arrays types
|
72
|
+
types = [ 'uint256[]', 'string' ]
|
55
73
|
data = hex'0000000000000000000000000000000000000000000000000000000000000040'+
|
56
74
|
'00000000000000000000000000000000000000000000000000000000000000a0'+
|
57
75
|
'0000000000000000000000000000000000000000000000000000000000000002'+
|
@@ -59,21 +77,72 @@ data = hex'0000000000000000000000000000000000000000000000000000000000000040'+
|
|
59
77
|
'000000000000000000000000000000000000000000000000000000000000162e'+
|
60
78
|
'000000000000000000000000000000000000000000000000000000000000000b'+
|
61
79
|
'48656c6c6f20576f726c64000000000000000000000000000000000000000000'
|
62
|
-
ABI.decode(
|
80
|
+
ABI.decode( types, data ) # returns args
|
63
81
|
#=> [[1234, 5678], "Hello World"]
|
64
82
|
|
65
83
|
|
66
84
|
## Decoding complex structs (also known as tuples)
|
85
|
+
types = [ 'uint256', '(uint256,string)']
|
67
86
|
data = hex'00000000000000000000000000000000000000000000000000000000000004d2'+
|
68
87
|
'0000000000000000000000000000000000000000000000000000000000000040'+
|
69
88
|
'000000000000000000000000000000000000000000000000000000000000162e'+
|
70
89
|
'0000000000000000000000000000000000000000000000000000000000000040'+
|
71
90
|
'000000000000000000000000000000000000000000000000000000000000000b'+
|
72
91
|
'48656c6c6f20576f726c64000000000000000000000000000000000000000000'
|
73
|
-
ABI.decode(
|
92
|
+
ABI.decode( types, data ) # returns args
|
74
93
|
#=> [1234, [5678, "Hello World"]]
|
75
94
|
```
|
76
95
|
|
96
|
+
and so on.
|
97
|
+
|
98
|
+
|
99
|
+
|
100
|
+
|
101
|
+
|
102
|
+
## Reference
|
103
|
+
|
104
|
+
For the full (and latest) official application
|
105
|
+
binary inteface (abi) specification
|
106
|
+
see the [**Contract ABI Specification**](https://docs.soliditylang.org/en/develop/abi-spec.html).
|
107
|
+
|
108
|
+
|
109
|
+
### Types
|
110
|
+
|
111
|
+
The following elementary types are supported:
|
112
|
+
|
113
|
+
- `uint<M>`: unsigned integer type of `M` bits, `0 < M <= 256`, `M % 8 == 0`. e.g. `uint32`, `uint8`, `uint256`.
|
114
|
+
- `int<M>`: two's complement signed integer type of `M` bits, `0 < M <= 256`, `M % 8 == 0`.
|
115
|
+
- `address`: equivalent to `uint160`, except for the assumed interpretation and language typing.
|
116
|
+
For computing the function selector, `address` is used.
|
117
|
+
- `bool`: equivalent to `uint8` restricted to the values 0 and 1. For computing the function selector, `bool` is used.
|
118
|
+
- `bytes<M>`: binary type of `M` bytes, `0 < M <= 32`.
|
119
|
+
|
120
|
+
|
121
|
+
The following (fixed-size) array types are supported:
|
122
|
+
|
123
|
+
- `<type>[M]`: a fixed-length array of `M` elements, `M >= 0`, of the given type.
|
124
|
+
|
125
|
+
<!--
|
126
|
+
Note: While this ABI specification can express fixed-length arrays with zero elements,
|
127
|
+
they're not supported by the compiler.
|
128
|
+
-->
|
129
|
+
|
130
|
+
The following non-fixed-size types are supported:
|
131
|
+
|
132
|
+
- `bytes`: dynamic sized byte sequence.
|
133
|
+
- `string`: dynamic sized unicode string assumed to be UTF-8 encoded.
|
134
|
+
- `<type>[]`: a variable-length array of elements of the given type.
|
135
|
+
|
136
|
+
Types can be combined to a tuple by enclosing them inside parentheses, separated by commas:
|
137
|
+
|
138
|
+
- `(T1,T2,...,Tn)`: tuple consisting of the types `T1`, ..., `Tn`, `n >= 0`
|
139
|
+
|
140
|
+
It is possible to form tuples of tuples, arrays of tuples and so on.
|
141
|
+
|
142
|
+
<!--
|
143
|
+
It is also possible to form zero-tuples (where `n == 0`).
|
144
|
+
-->
|
145
|
+
|
77
146
|
|
78
147
|
|
79
148
|
|
@@ -0,0 +1,208 @@
|
|
1
|
+
module ABI
|
2
|
+
|
3
|
+
|
4
|
+
class Decoder
|
5
|
+
|
6
|
+
##
|
7
|
+
# Decodes multiple arguments using the head/tail mechanism.
|
8
|
+
#
|
9
|
+
def decode( types, data, raise_errors = false )
|
10
|
+
## for convenience check if types is a String
|
11
|
+
## otherwise assume ABI::Type already
|
12
|
+
types = types.map { |type| type.is_a?( Type ) ? type : Type.parse( type ) }
|
13
|
+
|
14
|
+
outputs = [nil] * types.size
|
15
|
+
start_positions = [nil] * types.size + [data.size]
|
16
|
+
|
17
|
+
# TODO: refactor, a reverse iteration will be better
|
18
|
+
pos = 0
|
19
|
+
types.each_with_index do |t, i|
|
20
|
+
# If a type is static, grab the data directly, otherwise record its
|
21
|
+
# start position
|
22
|
+
if t.dynamic?
|
23
|
+
|
24
|
+
if pos>data.size-1
|
25
|
+
if raise_errors
|
26
|
+
raise DecodingError, "Position out of bounds #{pos}>#{data.size-1}"
|
27
|
+
else
|
28
|
+
puts "!! WARN - DecodingError: Position out of bounds #{pos}>#{data.size-1}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
start_positions[i] = big_endian_to_int(data[pos, 32])
|
33
|
+
|
34
|
+
if start_positions[i]>data.size-1
|
35
|
+
if raise_errors
|
36
|
+
raise DecodingError, "Start position out of bounds #{start_positions[i]}>#{data.size-1}"
|
37
|
+
else
|
38
|
+
puts "!! WARN - DecodingError: Start position out of bounds #{start_positions[i]}>#{data.size-1}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
j = i - 1
|
44
|
+
while j >= 0 && start_positions[j].nil?
|
45
|
+
start_positions[j] = start_positions[i]
|
46
|
+
j -= 1
|
47
|
+
end
|
48
|
+
|
49
|
+
pos += 32
|
50
|
+
else
|
51
|
+
## puts "step 1 - decode item [#{i}] - #{t.format} size: #{t.size} dynamic? #{t.dynamic?}"
|
52
|
+
|
53
|
+
outputs[i] = zero_padding( data, pos, t.size, start_positions )
|
54
|
+
pos += t.size
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
# We add a start position equal to the length of the entire data for
|
61
|
+
# convenience.
|
62
|
+
j = types.size - 1
|
63
|
+
while j >= 0 && start_positions[j].nil?
|
64
|
+
start_positions[j] = start_positions[types.size]
|
65
|
+
j -= 1
|
66
|
+
end
|
67
|
+
|
68
|
+
if pos > data.size
|
69
|
+
if raise_errors
|
70
|
+
raise DecodingError, "Not enough data for head"
|
71
|
+
else
|
72
|
+
puts "!! WARN - DecodingError: Not enough data for head"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
types.each_with_index do |t, i|
|
78
|
+
if t.dynamic?
|
79
|
+
offset, next_offset = start_positions[i, 2]
|
80
|
+
if offset<=data.size && next_offset<=data.size
|
81
|
+
outputs[i] = data[offset...next_offset]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
if outputs.include?(nil)
|
87
|
+
if raise_errors
|
88
|
+
raise DecodingError, "Not all data can be parsed"
|
89
|
+
else
|
90
|
+
puts "!! WARN: DecodingError - Not all data can be parsed"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
types.zip(outputs).map do |(t, out)|
|
96
|
+
## puts "step 2 - decode item - #{t.format} got: #{out.size} byte(s) - size: #{t.size} dynamic? #{t.dynamic?}"
|
97
|
+
|
98
|
+
decode_type(t, out)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
|
104
|
+
def decode_type( type, arg )
|
105
|
+
return nil if arg.nil? || arg.empty?
|
106
|
+
|
107
|
+
|
108
|
+
if type.is_a?( String ) || type.is_a?( Bytes )
|
109
|
+
l = big_endian_to_int( arg[0,32] )
|
110
|
+
data = arg[32..-1]
|
111
|
+
data[0, l]
|
112
|
+
elsif type.is_a?( Tuple )
|
113
|
+
arg ? decode(type.types, arg) : []
|
114
|
+
elsif type.is_a?( FixedArray ) # static-sized arrays
|
115
|
+
l = type.dim
|
116
|
+
subtype = type.subtype
|
117
|
+
if subtype.dynamic?
|
118
|
+
start_positions = (0...l).map {|i| big_endian_to_int(arg[32*i, 32]) }
|
119
|
+
start_positions.push arg.size
|
120
|
+
|
121
|
+
outputs = (0...l).map {|i| arg[start_positions[i]...start_positions[i+1]] }
|
122
|
+
|
123
|
+
outputs.map {|out| decode_type(subtype, out) }
|
124
|
+
else
|
125
|
+
(0...l).map {|i| decode_type(subtype, arg[subtype.size*i, subtype.size]) }
|
126
|
+
end
|
127
|
+
elsif type.is_a?( Array )
|
128
|
+
l = big_endian_to_int( arg[0,32] )
|
129
|
+
raise DecodingError, "Too long length: #{l}" if l > 100000
|
130
|
+
subtype = type.subtype
|
131
|
+
|
132
|
+
if subtype.dynamic?
|
133
|
+
raise DecodingError, "Not enough data for head" unless arg.size >= 32 + 32*l
|
134
|
+
|
135
|
+
start_positions = (1..l).map {|i| 32+big_endian_to_int(arg[32*i, 32]) }
|
136
|
+
start_positions.push arg.size
|
137
|
+
|
138
|
+
outputs = (0...l).map {|i| arg[start_positions[i]...start_positions[i+1]] }
|
139
|
+
|
140
|
+
outputs.map {|out| decode_type(subtype, out) }
|
141
|
+
else
|
142
|
+
(0...l).map {|i| decode_type(subtype, arg[32 + subtype.size*i, subtype.size]) }
|
143
|
+
end
|
144
|
+
else
|
145
|
+
decode_primitive_type( type, arg )
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
def decode_primitive_type(type, data)
|
151
|
+
case type
|
152
|
+
when Address
|
153
|
+
encode_hex( data[12..-1] )
|
154
|
+
when String, Bytes
|
155
|
+
if data.length == 32
|
156
|
+
data[0..32]
|
157
|
+
else
|
158
|
+
size = big_endian_to_int( data[0,32] )
|
159
|
+
data[32..-1][0,size]
|
160
|
+
end
|
161
|
+
when FixedBytes
|
162
|
+
data[0, type.length]
|
163
|
+
when Uint
|
164
|
+
big_endian_to_int( data )
|
165
|
+
when Int
|
166
|
+
u = big_endian_to_int( data )
|
167
|
+
u >= 2**(type.bits-1) ? (u - 2**type.bits) : u
|
168
|
+
when Bool
|
169
|
+
data[-1] == BYTE_ONE
|
170
|
+
else
|
171
|
+
raise DecodingError, "Unknown primitive type: #{type.class.name} #{type.format}"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
def zero_padding( data, pos, count, start_positions )
|
177
|
+
if pos >= data.size
|
178
|
+
start_positions[start_positions.size-1] += count
|
179
|
+
"\x00"*count
|
180
|
+
elsif pos + count > data.size
|
181
|
+
start_positions[start_positions.size-1] += ( count - (data.size-pos))
|
182
|
+
data[pos,data.size-pos] + "\x00"*( count - (data.size-pos))
|
183
|
+
else
|
184
|
+
data[pos, count]
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
###########
|
190
|
+
# decoding helpers / utils
|
191
|
+
|
192
|
+
def big_endian_to_int( bin )
|
193
|
+
bin = bin.sub( /\A(\x00)+/, '' ) ## keep "performance" shortcut - why? why not?
|
194
|
+
### todo/check - allow nil - why? why not?
|
195
|
+
## raise DeserializationError, "Invalid serialization (not minimal length)" if !@size && serial.size > 0 && serial[0] == BYTE_ZERO
|
196
|
+
bin = bin || BYTE_ZERO
|
197
|
+
bin.unpack("H*").first.to_i(16)
|
198
|
+
end
|
199
|
+
|
200
|
+
def encode_hex( bin ) ## bin_to_hex
|
201
|
+
raise TypeError, "Value must be a String" unless bin.is_a?(::String)
|
202
|
+
bin.unpack("H*").first
|
203
|
+
end
|
204
|
+
|
205
|
+
|
206
|
+
end # class Decoder
|
207
|
+
|
208
|
+
end ## module ABI
|
@@ -0,0 +1,279 @@
|
|
1
|
+
|
2
|
+
module ABI
|
3
|
+
##
|
4
|
+
# Contract ABI encoding and decoding.
|
5
|
+
#
|
6
|
+
# @see https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI
|
7
|
+
#
|
8
|
+
|
9
|
+
class EncodingError < StandardError; end
|
10
|
+
class DecodingError < StandardError; end
|
11
|
+
class ValueError < StandardError; end
|
12
|
+
|
13
|
+
class ValueOutOfBounds < ValueError; end
|
14
|
+
|
15
|
+
|
16
|
+
|
17
|
+
class Encoder
|
18
|
+
|
19
|
+
##
|
20
|
+
# Encodes multiple arguments using the head/tail mechanism.
|
21
|
+
#
|
22
|
+
def encode( types, args )
|
23
|
+
|
24
|
+
## todo/fix: enforce args.size and types.size must match!!
|
25
|
+
## raise ArgumentError - expected x arguments got y!!!
|
26
|
+
|
27
|
+
|
28
|
+
## for convenience check if types is a String
|
29
|
+
## otherwise assume ABI::Type already
|
30
|
+
types = types.map { |type| type.is_a?( Type ) ? type : Type.parse( type ) }
|
31
|
+
|
32
|
+
## todo/check: use args.map (instead of types)
|
33
|
+
## might allow encoding less args than types? - why? why not?
|
34
|
+
head_size = types
|
35
|
+
.map {|type| type.size || 32 }
|
36
|
+
.sum
|
37
|
+
|
38
|
+
head, tail = '', ''
|
39
|
+
types.each_with_index do |type, i|
|
40
|
+
if type.dynamic?
|
41
|
+
head += encode_uint256( head_size + tail.size )
|
42
|
+
tail += encode_type( type, args[i] )
|
43
|
+
else
|
44
|
+
head += encode_type( type, args[i] )
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
head + tail
|
49
|
+
end
|
50
|
+
|
51
|
+
##
|
52
|
+
# Encodes a single value (static or dynamic).
|
53
|
+
#
|
54
|
+
# @param type [ABI::Type] value type
|
55
|
+
# @param arg [Object] value
|
56
|
+
#
|
57
|
+
# @return [String] encoded bytes
|
58
|
+
#
|
59
|
+
def encode_type( type, arg )
|
60
|
+
## case 1) string or bytes (note:are dynamic too!!! most go first)
|
61
|
+
## use type == Type.new( 'string', nil, [] ) - same as Type.new('string'))
|
62
|
+
## or type == Type.new( 'bytes', nil, [] ) - same as Type.new('bytes')
|
63
|
+
## - why? why not?
|
64
|
+
if type.is_a?( String )
|
65
|
+
encode_string( arg )
|
66
|
+
elsif type.is_a?( Bytes )
|
67
|
+
encode_bytes( arg )
|
68
|
+
elsif type.is_a?( Tuple )
|
69
|
+
encode_tuple( type, arg )
|
70
|
+
elsif type.is_a?( Array ) || type.is_a?( FixedArray )
|
71
|
+
if type.dynamic?
|
72
|
+
encode_dynamic_array( type, arg )
|
73
|
+
else
|
74
|
+
encode_static_array( type, arg )
|
75
|
+
end
|
76
|
+
else # assume static (primitive) type
|
77
|
+
encode_primitive_type( type, arg )
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
def encode_dynamic_array( type, args )
|
83
|
+
raise ArgumentError, "arg must be an array" unless args.is_a?(::Array)
|
84
|
+
|
85
|
+
head, tail = '', ''
|
86
|
+
|
87
|
+
if type.is_a?( Array ) ## dynamic array
|
88
|
+
head += encode_uint256( args.size )
|
89
|
+
else ## fixed array
|
90
|
+
raise ArgumentError, "Wrong array size: found #{args.size}, expecting #{type.dim}" unless args.size == type.dim
|
91
|
+
end
|
92
|
+
|
93
|
+
subtype = type.subtype
|
94
|
+
args.each do |arg|
|
95
|
+
if subtype.dynamic?
|
96
|
+
head += encode_uint256( 32*args.size + tail.size )
|
97
|
+
tail += encode_type( subtype, arg )
|
98
|
+
else
|
99
|
+
head += encode_type( subtype, arg )
|
100
|
+
end
|
101
|
+
end
|
102
|
+
head + tail
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
def encode_static_array( type, args )
|
107
|
+
raise ArgumentError, "arg must be an array" unless args.is_a?(::Array)
|
108
|
+
raise ArgumentError, "Wrong array size: found #{args.size}, expecting #{type.dim}" unless args.size == type.dim
|
109
|
+
|
110
|
+
args.map {|arg| encode_type( type.subtype, arg ) }.join
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
##
|
115
|
+
## todo/check: if static tuple gets encoded different
|
116
|
+
## without offset (head/tail)
|
117
|
+
|
118
|
+
def encode_tuple( tuple, args )
|
119
|
+
raise ArgumentError, "arg must be an array" unless args.is_a?(::Array)
|
120
|
+
raise ArgumentError, "Wrong array size (for tuple): found #{args.size}, expecting #{tuple.type.size} tuple elements" unless args.size == tuple.types.size
|
121
|
+
|
122
|
+
head_size = tuple.types
|
123
|
+
.map {|type| type.size || 32 }
|
124
|
+
.sum
|
125
|
+
|
126
|
+
head, tail = '', ''
|
127
|
+
tuple.types.each_with_index do |type, i|
|
128
|
+
if type.dynamic?
|
129
|
+
head += encode_uint256( head_size + tail.size )
|
130
|
+
tail += encode_type( type, args[i] )
|
131
|
+
else
|
132
|
+
head += encode_type( type, args[i] )
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
head + tail
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
|
141
|
+
def encode_primitive_type( type, arg )
|
142
|
+
case type
|
143
|
+
when Uint
|
144
|
+
## note: for now size in bits always required
|
145
|
+
encode_uint( arg, type.bits )
|
146
|
+
when Int
|
147
|
+
## note: for now size in bits always required
|
148
|
+
encode_int( arg, type.bits )
|
149
|
+
when Bool
|
150
|
+
encode_bool( arg )
|
151
|
+
when String
|
152
|
+
encode_string( arg )
|
153
|
+
when FixedBytes
|
154
|
+
encode_bytes( arg, type.length )
|
155
|
+
when Bytes
|
156
|
+
encode_bytes( arg )
|
157
|
+
when Address
|
158
|
+
encode_address( arg )
|
159
|
+
else
|
160
|
+
raise EncodingError, "Unhandled type: #{type.class.name} #{type.format}"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
|
166
|
+
def encode_bool( arg )
|
167
|
+
raise ArgumentError, "arg is not bool: #{arg}" unless arg.is_a?(TrueClass) || arg.is_a?(FalseClass)
|
168
|
+
lpad_int( arg ? 1 : 0 )
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
def encode_uint256( arg ) encode_uint( arg, 256 ); end
|
173
|
+
def encode_uint( arg, bits )
|
174
|
+
raise ArgumentError, "arg is not integer: #{arg}" unless arg.is_a?(Integer)
|
175
|
+
raise ValueOutOfBounds, arg unless arg >= 0 && arg < 2**bits
|
176
|
+
lpad_int( arg )
|
177
|
+
end
|
178
|
+
|
179
|
+
def encode_int( arg, bits )
|
180
|
+
raise ArgumentError, "arg is not integer: #{arg}" unless arg.is_a?(Integer)
|
181
|
+
raise ValueOutOfBounds, arg unless arg >= -2**(bits-1) && arg < 2**(bits-1)
|
182
|
+
lpad_int( arg % 2**bits )
|
183
|
+
end
|
184
|
+
|
185
|
+
|
186
|
+
def encode_string( arg )
|
187
|
+
|
188
|
+
## todo/fix: do NOT auto-unpack hexstring EVER!!!
|
189
|
+
## remove arg.unpack('U*')
|
190
|
+
|
191
|
+
if arg.encoding == Encoding::UTF_8 ## was: name == 'UTF-8'
|
192
|
+
arg = arg.b
|
193
|
+
else
|
194
|
+
begin
|
195
|
+
arg.unpack('U*')
|
196
|
+
rescue ArgumentError
|
197
|
+
raise ValueError, "string must be UTF-8 encoded"
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
raise ValueOutOfBounds, "Integer invalid or out of range: #{arg.size}" if arg.size > UINT_MAX
|
202
|
+
size = lpad_int( arg.size )
|
203
|
+
value = rpad( arg, ceil32(arg.size) )
|
204
|
+
|
205
|
+
size + value
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
def encode_bytes( arg, length=nil )
|
210
|
+
raise EncodingError, "Expecting string: #{arg}" unless arg.is_a?(::String)
|
211
|
+
arg = arg.b
|
212
|
+
|
213
|
+
if length # fixed length type
|
214
|
+
raise ValueOutOfBounds, "invalid bytes length #{length}" if arg.size > length
|
215
|
+
raise ValueOutOfBounds, "invalid bytes length #{length}" if length < 0 || length > 32
|
216
|
+
rpad( arg )
|
217
|
+
else # variable length type (if length is nil)
|
218
|
+
raise ValueOutOfBounds, "Integer invalid or out of range: #{arg.size}" if arg.size > UINT_MAX
|
219
|
+
size = lpad_int( arg.size )
|
220
|
+
value = rpad( arg, ceil32(arg.size) )
|
221
|
+
size + value
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
|
226
|
+
def encode_address( arg )
|
227
|
+
if arg.is_a?( Integer )
|
228
|
+
lpad_int( arg )
|
229
|
+
elsif arg.size == 20
|
230
|
+
lpad( arg )
|
231
|
+
elsif arg.size == 40
|
232
|
+
lpad_hex( arg )
|
233
|
+
elsif arg.size == 42 && arg[0,2] == '0x' ## todo/fix: allow 0X too - why? why not?
|
234
|
+
lpad_hex( arg[2..-1] )
|
235
|
+
else
|
236
|
+
raise EncodingError, "Could not parse address: #{arg}"
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
|
241
|
+
###########
|
242
|
+
# encoding helpers / utils
|
243
|
+
|
244
|
+
def rpad( bin, l=32, symbol=BYTE_ZERO ) ## note: same as builtin String#ljust !!!
|
245
|
+
return bin if bin.size >= l
|
246
|
+
bin + symbol * (l - bin.size)
|
247
|
+
end
|
248
|
+
|
249
|
+
def lpad( bin, l=32, symbol=BYTE_ZERO ) ## note: same as builtin String#rjust !!!
|
250
|
+
return bin if bin.size >= l
|
251
|
+
symbol * (l - bin.size) + bin
|
252
|
+
end
|
253
|
+
|
254
|
+
def lpad_int( n, l=32, symbol=BYTE_ZERO )
|
255
|
+
raise ArgumentError, "Integer invalid or out of range: #{n}" unless n.is_a?(Integer) && n >= 0 && n <= UINT_MAX
|
256
|
+
hex = n.to_s(16)
|
257
|
+
hex = "0#{hex}" if hex.size.odd?
|
258
|
+
bin = [hex].pack("H*")
|
259
|
+
|
260
|
+
lpad( bin, l, symbol )
|
261
|
+
end
|
262
|
+
|
263
|
+
def lpad_hex( hex, l=32, symbol=BYTE_ZERO )
|
264
|
+
raise TypeError, "Value must be a string" unless hex.is_a?( ::String )
|
265
|
+
raise TypeError, 'Non-hexadecimal digit found' unless hex =~ /\A[0-9a-fA-F]*\z/
|
266
|
+
bin = [hex].pack("H*")
|
267
|
+
|
268
|
+
lpad( bin, l, symbol )
|
269
|
+
end
|
270
|
+
|
271
|
+
|
272
|
+
|
273
|
+
def ceil32(x)
|
274
|
+
x % 32 == 0 ? x : (x + 32 - x%32)
|
275
|
+
end
|
276
|
+
|
277
|
+
end # class Encoder
|
278
|
+
end # module ABI
|
279
|
+
|