ethlite 0.4.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a168744cd8aa2f76a80e7536064f8d831f3cbcef3e9148687c7f003403415d97
4
- data.tar.gz: 711fa922a92492be4ef33317a552de466a3f4b9e32428d80e6a79d55fc1b3f6d
3
+ metadata.gz: 783bf5485da84a9532d36a9285b08b7ebc6e8266b4c431ce623d4720ffefbaf4
4
+ data.tar.gz: 71e360e28ee30b99ee44accb575c56a6c6b08eb349ec257576dfa4cbb79982f8
5
5
  SHA512:
6
- metadata.gz: 9465a3704789b0c377e7dd7db33f7675bb2230b011f7792c9758bf4b72723b55516e18e594b897d015e4b5326f80359e0c7a5eecaaff1f957c5d9f354a2b827d
7
- data.tar.gz: 0fb31c94e3810430d01573dbb951787e34a8824a2d0ba2bc4305ea306c5258260bd189ab8a84b817c1260a6b1d5b978bc99b48a543390eec586c59a56726c583
6
+ metadata.gz: 77e14ee4da005f731958fc5c889d71c59aff89c9a565dec0ea58c0bd56562ba4d8cdf696182a58051a0201b95254ce28a2e4281459976dc0e8c650beeb53b0d7
7
+ data.tar.gz: b3d9aadf29db07b1afdd53c780a59947d94d0667b070ba0edf45097c3c65f76a492289a2b9f21a20f8a0dc62ea3a0b630d686cdd15f795e18355f32956e78ad9
data/Manifest.txt CHANGED
@@ -3,9 +3,6 @@ Manifest.txt
3
3
  README.md
4
4
  Rakefile
5
5
  lib/ethlite.rb
6
- lib/ethlite/abi/codec.rb
7
- lib/ethlite/abi/type.rb
8
- lib/ethlite/constant.rb
9
6
  lib/ethlite/contract.rb
10
7
  lib/ethlite/utils.rb
11
8
  lib/ethlite/version.rb
data/README.md CHANGED
@@ -64,7 +64,7 @@ def eth_call( rpc,
64
64
  ## binary encode method sig(nature)
65
65
  signature = "#{name}(#{inputs.join(',')})"
66
66
  signature_hash = Ethlite::Utils.encode_hex(
67
- Ethlite::Utils.keccak256(signature))[0..7]
67
+ Ethlite::Utils.keccak256(signature)[0,4])
68
68
 
69
69
  pp signature
70
70
  # => "tokenURI(uint256)"
@@ -73,7 +73,7 @@ def eth_call( rpc,
73
73
 
74
74
  ## binary encode method arg(ument)s
75
75
  args_encoded = Ethlite::Utils.encode_hex(
76
- Ethlite::Abi.encode_abi( inputs, args) )
76
+ ABI.encode( inputs, args) )
77
77
 
78
78
  data = '0x' + signature_hash + args_encoded
79
79
 
@@ -90,10 +90,9 @@ def eth_call( rpc,
90
90
  puts "response:"
91
91
  pp response
92
92
 
93
- ## decode binary result
94
- string_data = Ethlite::Utils.decode_hex(
95
- Ethlite::Utils.remove_0x_head(response))
96
- result = Ethlite::Abi.decode_abi( outputs, string_data )
93
+ ## decode binary result (returned as a hex string starting with 0x)
94
+ bin = Ethlite::Utils.decode_hex( response )
95
+ result = ABI.decode( outputs, bin )
97
96
  result.length == 1 ? result[0] : result
98
97
  end
99
98
  ```
data/Rakefile CHANGED
@@ -20,8 +20,8 @@ Hoe.spec 'ethlite' do
20
20
 
21
21
  self.extra_deps = [
22
22
  ['cocos'],
23
- ['rlp-lite'],
24
23
  ['digest-lite'],
24
+ ['abicoder'],
25
25
  ]
26
26
 
27
27
  self.licenses = ['Public Domain']
@@ -1,31 +1,6 @@
1
1
  module Ethlite
2
2
  class ContractMethod
3
3
 
4
- def self.parse_abi( abi )
5
- ## convenience helper - auto-convert to json if string passed in
6
- abi = JSON.parse( abi ) if abi.is_a?( String )
7
-
8
- name = abi['name']
9
- constant = !!abi['constant'] || abi['stateMutability']=='view'
10
- input_types = abi['inputs'] ? abi['inputs'].map{|a| _parse_component_type( a ) } : []
11
- output_types = abi['outputs'] ? abi['outputs'].map{|a| _parse_component_type( a ) } : []
12
-
13
- new( name, inputs: input_types,
14
- outputs: output_types,
15
- constant: constant )
16
- end
17
-
18
- def self._parse_component_type( argument )
19
- if argument['type'] =~ /^tuple((\[[0-9]*\])*)/
20
- argument['components'] ? "(#{argument['components'].collect{ |c| _parse_component_type( c ) }.join(',')})#{$1}"
21
- : "()#{$1}"
22
- else
23
- argument['type']
24
- end
25
- end
26
-
27
-
28
-
29
4
 
30
5
  attr_reader :signature,
31
6
  :name,
@@ -34,21 +9,25 @@
34
9
  :output_types,
35
10
  :constant
36
11
 
37
- def initialize( name, inputs:,
12
+ def initialize( name, inputs: [],
38
13
  outputs: [],
39
14
  constant: true )
40
15
  @name = name
41
16
  @constant = constant
42
- @input_types = inputs
43
- @output_types = outputs
44
- @signature = "#{@name}(#{@input_types.join(',')})"
45
- @signature_hash = Utils.signature_hash( @signature, 8 )
17
+
18
+ ## parse inputs & outputs into types
19
+ @input_types = inputs.map { |str| ABI::Type.parse( str ) }
20
+ @output_types = outputs.map { |str| ABI::Type.parse( str ) }
21
+
22
+ types = @input_types.map {|type| type.format }.join(',')
23
+ @signature = "#{@name}(#{types})"
24
+ @signature_hash = Utils.encode_hex( Utils.keccak256( @signature)[0,4] )
46
25
  end
47
26
 
48
27
 
49
28
  def do_call( rpc, contract_address, args )
50
29
  data = '0x' + @signature_hash + Utils.encode_hex(
51
- Abi.encode_abi(@input_types, args) )
30
+ ABI.encode(@input_types, args) )
52
31
 
53
32
  method = 'eth_call'
54
33
  params = [{ to: contract_address,
@@ -56,23 +35,30 @@
56
35
  'latest']
57
36
  response = rpc.request( method, params )
58
37
 
38
+ if debug?
39
+ puts "response:"
40
+ pp response
41
+ end
59
42
 
60
- puts "response:"
61
- pp response
43
+ bin = Utils.decode_hex( response )
44
+ return nil if bin.empty?
62
45
 
63
- string_data = Utils.decode_hex(
64
- Utils.remove_0x_head(response))
65
- return nil if string_data.empty?
46
+ result = ABI.decode( @output_types, bin )
66
47
 
67
- result = Abi.decode_abi( @output_types, string_data )
68
- puts
69
- puts "result decode_abi:"
70
- pp result
71
48
 
49
+ if debug?
50
+ puts
51
+ puts "result decode_abi:"
52
+ pp result
53
+ end
72
54
 
73
55
  result.length==1 ? result.first : result
74
56
  end
75
57
 
58
+ ####
59
+ # private helpers
60
+ def debug?() Ethlite.debug?; end ## forward to global Ethlite config
76
61
  end # class ContractMethod
62
+
77
63
  end # module Ethlite
78
64
 
data/lib/ethlite/utils.rb CHANGED
@@ -1,225 +1,36 @@
1
1
  module Ethlite
2
2
 
3
3
 
4
- module UtilHelper
5
- ##
6
- # Not the keccak in sha3, although it's underlying lib named SHA3
7
- #
8
- def keccak256(x)
4
+ module Helpers
5
+ def keccak256( bin )
9
6
  # Digest::SHA3.new(256).digest(x)
10
7
  ## Digest::Keccak.digest(x, 256)
11
- Digest::KeccakLite.new(256).digest( x )
8
+ Digest::KeccakLite.new(256).digest( bin )
12
9
  end
13
10
 
14
- ## todo/check where required / in use - what for?
15
- def keccak512(x)
16
- # Digest::SHA3.new(512).digest(x)
17
- # Digest::Keccak.digest(x, 512)
18
- Digest::KeccakLite.new(512).digest( x )
11
+ def encode_hex( bin ) ## bin_to_hex
12
+ raise TypeError, "Value must be a string" unless bin.is_a?( String )
13
+ ## note: always return a hex string with default encoding e.g. utf-8 - why? why not?
14
+ bin.unpack("H*").first.force_encoding( Encoding::UTF_8 )
19
15
  end
16
+ alias_method :bin_to_hex, :encode_hex
20
17
 
18
+ def decode_hex( hex ) ## hex_to_bin
19
+ raise TypeError, "Value must be a string" unless hex.is_a?( String )
20
+ raise TypeError, 'Non-hexadecimal char found' unless hex.empty? || hex =~ /\A(0x)?[0-9a-fA-F]{2,}\z/
21
21
 
22
- def keccak256_rlp(x)
23
- keccak256 RLP.encode(x)
24
- end
25
-
26
- def sha256(x)
27
- Digest::SHA256.digest x
28
- end
29
-
30
- def double_sha256(x)
31
- sha256 sha256(x)
32
- end
33
-
34
- def ripemd160(x)
35
- Digest::RMD160.digest x
36
- end
37
-
38
- def hash160(x)
39
- ripemd160 sha256(x)
40
- end
41
-
42
- def hash160_hex(x)
43
- encode_hex hash160(x)
44
- end
45
-
46
- def mod_exp(x, y, n)
47
- x.to_bn.mod_exp(y, n).to_i
48
- end
49
-
50
- def mod_mul(x, y, n)
51
- x.to_bn.mod_mul(y, n).to_i
52
- end
53
-
54
- def to_signed(i)
55
- i > INT_MAX ? (i-TT256) : i
56
- end
57
-
58
-
59
- def ceil32(x)
60
- x % 32 == 0 ? x : (x + 32 - x%32)
61
- end
62
-
63
- def encode_hex(b)
64
- raise TypeError, "Value must be an instance of String" unless b.instance_of?(String)
65
- b.unpack("H*").first
66
- end
67
-
68
- def decode_hex(str)
69
- raise TypeError, "Value must be an instance of string" unless str.instance_of?(String)
70
- raise TypeError, 'Non-hexadecimal digit found' unless str =~ /\A[0-9a-fA-F]*\z/
71
- [str].pack("H*")
72
- end
73
-
74
-
75
-
76
-
77
- def int_to_big_endian(n)
78
- RLP::Sedes.big_endian_int.serialize n
79
- end
80
-
81
- def big_endian_to_int(s)
82
- RLP::Sedes.big_endian_int.deserialize s.sub(/\A(\x00)+/, '')
83
- end
84
-
85
-
86
- def encode_int(n)
87
- raise ArgumentError, "Integer invalid or out of range: #{n}" unless n.is_a?(Integer) && n >= 0 && n <= UINT_MAX
88
- int_to_big_endian n
89
- end
90
-
91
- def decode_int(v)
92
- raise ArgumentError, "No leading zero bytes allowed for integers" if v.size > 0 && (v[0] == BYTE_ZERO || v[0] == 0)
93
- big_endian_to_int v
94
- end
95
-
96
-
97
-
98
-
99
- def lpad(x, symbol, l)
100
- return x if x.size >= l
101
- symbol * (l - x.size) + x
102
- end
103
-
104
- def rpad(x, symbol, l)
105
- return x if x.size >= l
106
- x + symbol * (l - x.size)
107
- end
108
-
109
- def zpad(x, l)
110
- lpad x, BYTE_ZERO, l
111
- end
112
-
113
- def zunpad(x)
114
- x.sub /\A\x00+/, ''
115
- end
116
-
117
- def zpad_int(n, l=32)
118
- zpad encode_int(n), l
119
- end
120
-
121
- def zpad_hex(s, l=32)
122
- zpad decode_hex(s), l
123
- end
124
-
125
- def int_to_addr(x)
126
- zpad_int x, 20
127
- end
128
-
129
-
130
- def bytearray_to_int(arr)
131
- o = 0
132
- arr.each {|x| o = (o << 8) + x }
133
- o
134
- end
135
-
136
- def int_array_to_bytes(arr)
137
- arr.pack('C*')
138
- end
139
-
140
- def bytes_to_int_array(bytes)
141
- bytes.unpack('C*')
142
- end
22
+ ## allow optional starting 0x - why? why not?
23
+ hex = hex[2..-1] if hex[0,2] == '0x'
143
24
 
144
-
145
- def coerce_to_int(x)
146
- if x.is_a?(Numeric)
147
- x
148
- elsif x.size == 40
149
- big_endian_to_int decode_hex(x)
150
- else
151
- big_endian_to_int x
152
- end
153
- end
154
-
155
- def coerce_to_bytes(x)
156
- if x.is_a?(Numeric)
157
- int_to_big_endian x
158
- elsif x.size == 40
159
- decode_hex(x)
160
- else
161
- x
162
- end
163
- end
164
-
165
- def coerce_addr_to_hex(x)
166
- if x.is_a?(Numeric)
167
- encode_hex zpad(int_to_big_endian(x), 20)
168
- elsif x.size == 40 || x.size == 0
169
- x
170
- else
171
- encode_hex zpad(x, 20)[-20..-1]
172
- end
173
- end
174
-
175
-
176
-
177
- def parse_int_or_hex(s)
178
- if s.is_a?(Numeric)
179
- s
180
- elsif s[0,2] == '0x'
181
- big_endian_to_int decode_hex(normalize_hex_without_prefix(s))
182
- else
183
- s.to_i
184
- end
185
- end
186
-
187
-
188
- =begin
189
- ## add? moved over from the old module Utility
190
- def hex( num )
191
- '0x' + num.to_s(16)
192
- end
193
-
194
- def from_hex( h )
195
- h.nil? ? 0 : (h.kind_of?(String) ? h.to_i(16) : h)
196
- end
197
- =end
198
-
199
-
200
- def remove_0x_head( s )
201
- return s if !s || s.length<2
202
- s[0,2] == '0x' ? s[2..-1] : s
25
+ [hex].pack("H*")
203
26
  end
204
-
205
- def normalize_hex_without_prefix(s)
206
- if s[0,2] == '0x'
207
- (s.size % 2 == 1 ? '0' : '') + s[2..-1]
208
- else
209
- s
210
- end
211
- end
212
-
213
- def signature_hash( signature, length=8 )
214
- encode_hex( keccak256(signature) )[0...length]
215
- end
216
-
217
- end # module UtilHelper
27
+ alias_method :hex_to_bin, :decode_hex
28
+ end # module Helpers
218
29
 
219
30
 
220
31
 
221
32
  module Utils
222
- extend UtilHelper
33
+ extend Helpers
223
34
  end
224
35
 
225
36
  end # module Ethlite
@@ -1,9 +1,9 @@
1
1
 
2
2
 
3
3
  module Ethlite
4
- MAJOR = 0
5
- MINOR = 4
6
- PATCH = 1
4
+ MAJOR = 1
5
+ MINOR = 0
6
+ PATCH = 0
7
7
  VERSION = [MAJOR,MINOR,PATCH].join('.')
8
8
 
9
9
  def self.version
data/lib/ethlite.rb CHANGED
@@ -19,18 +19,14 @@ end
19
19
  load_env
20
20
 
21
21
 
22
- require 'uri'
23
- require 'net/http'
24
- require 'net/https'
25
- require 'json'
26
-
27
22
 
28
23
  require 'openssl'
29
24
  require 'digest'
30
25
 
26
+
31
27
  ## 3rd party gems
32
- require 'rlp-lite'
33
28
  require 'digest-lite'
29
+ require 'abicoder'
34
30
 
35
31
 
36
32
  require_relative 'jsonrpc/jsonrpc'
@@ -38,15 +34,7 @@ require_relative 'jsonrpc/jsonrpc'
38
34
 
39
35
  ## our own code
40
36
  require_relative 'ethlite/version' # note: let version always go first
41
-
42
- require_relative 'ethlite/constant'
43
37
  require_relative 'ethlite/utils'
44
-
45
-
46
- require_relative 'ethlite/abi/type'
47
- require_relative 'ethlite/abi/codec'
48
-
49
-
50
38
  require_relative 'ethlite/contract'
51
39
 
52
40
 
@@ -61,6 +49,10 @@ class Configuration
61
49
  value
62
50
  end
63
51
  end
52
+
53
+ ## add "global" debug switch - why? why not?
54
+ def debug?() (@debug || false) == true; end
55
+ def debug=(value) @debug = value; end
64
56
  end # class Configuration
65
57
 
66
58
 
@@ -70,6 +62,9 @@ end # class Configuration
70
62
  ## end
71
63
  def self.configure() yield( config ); end
72
64
  def self.config() @config ||= Configuration.new; end
65
+
66
+ ## add debug convenience config shortcut - forwarding to config.debug? - why? why not?
67
+ def self.debug?() config.debug?; end
73
68
  end # module Ethlite
74
69
 
75
70
 
@@ -8,6 +8,16 @@
8
8
 
9
9
 
10
10
  class JsonRpc
11
+
12
+ ### add a global debug switch - why? why not?
13
+ def self.debug?() (@debug || false) == true; end
14
+ def self.debug( value ) @debug = value; end
15
+ ####
16
+ # private helpers
17
+ def debug?() self.class.debug?; end
18
+
19
+
20
+
11
21
  def initialize( uri )
12
22
  @uri = uri ## assume uri always as string for now
13
23
  @request_id = 1
@@ -23,8 +33,10 @@ class JsonRpc
23
33
 
24
34
  @request_id += 1
25
35
 
26
- puts "json_rpc POST payload:"
27
- puts data.to_json
36
+ if debug?
37
+ puts "json_rpc POST payload:"
38
+ puts data.to_json
39
+ end
28
40
 
29
41
  response = Webclient.post( @uri, json: data )
30
42
 
@@ -33,8 +45,10 @@ class JsonRpc
33
45
  raise "Error code #{response.status.code} on request #{@uri} #{data}"
34
46
  end
35
47
 
36
- puts "json_rpc response.body:"
37
- puts response.body
48
+ if debug?
49
+ puts "json_rpc response.body:"
50
+ puts response.body
51
+ end
38
52
 
39
53
 
40
54
  body = JSON.parse( response.body, max_nesting: 1500 )
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ethlite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gerald Bauer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-12-12 00:00:00.000000000 Z
11
+ date: 2023-01-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cocos
@@ -25,7 +25,7 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rlp-lite
28
+ name: digest-lite
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
@@ -39,7 +39,7 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: digest-lite
42
+ name: abicoder
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
@@ -101,9 +101,6 @@ files:
101
101
  - README.md
102
102
  - Rakefile
103
103
  - lib/ethlite.rb
104
- - lib/ethlite/abi/codec.rb
105
- - lib/ethlite/abi/type.rb
106
- - lib/ethlite/constant.rb
107
104
  - lib/ethlite/contract.rb
108
105
  - lib/ethlite/utils.rb
109
106
  - lib/ethlite/version.rb
@@ -1,436 +0,0 @@
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
- class Codec
12
- class EncodingError < StandardError; end
13
- class DecodingError < StandardError; end
14
- class ValueError < StandardError; end
15
-
16
- class ValueOutOfBounds < ValueError; end
17
-
18
- ##
19
- # Encodes multiple arguments using the head/tail mechanism.
20
- #
21
- def encode_abi(types, args)
22
- parsed_types = types.map {|t| Type.parse(t) }
23
-
24
- head_size = (0...args.size)
25
- .map {|i| parsed_types[i].size || 32 }
26
- .reduce(0, &:+)
27
-
28
- head, tail = '', ''
29
- args.each_with_index do |arg, i|
30
- if parsed_types[i].dynamic?
31
- head += encode_type(Type.size_type, head_size + tail.size)
32
- tail += encode_type(parsed_types[i], arg)
33
- else
34
- head += encode_type(parsed_types[i], arg)
35
- end
36
- end
37
-
38
- "#{head}#{tail}"
39
- end
40
-
41
- ##
42
- # Encodes a single value (static or dynamic).
43
- #
44
- # @param type [Ethereum::ABI::Type] value type
45
- # @param arg [Object] value
46
- #
47
- # @return [String] encoded bytes
48
- #
49
- def encode_type(type, arg)
50
- if %w(string bytes).include?(type.base) && type.sub.empty?
51
- encode_primitive_type type, arg
52
- elsif type.dynamic?
53
- raise ArgumentError, "arg must be an array" unless arg.instance_of?(Array)
54
-
55
- head, tail = '', ''
56
- if type.dims.last == 0
57
- head += encode_type(Type.size_type, arg.size)
58
- else
59
- raise ArgumentError, "Wrong array size: found #{arg.size}, expecting #{type.dims.last}" unless arg.size == type.dims.last
60
- end
61
-
62
- sub_type = type.subtype
63
- sub_size = type.subtype.size
64
- arg.size.times do |i|
65
- if sub_size.nil?
66
- head += encode_type(Type.size_type, 32*arg.size + tail.size)
67
- tail += encode_type(sub_type, arg[i])
68
- else
69
- head += encode_type(sub_type, arg[i])
70
- end
71
- end
72
-
73
- "#{head}#{tail}"
74
- else # static type
75
- if type.dims.empty?
76
- encode_primitive_type type, arg
77
- else
78
- arg.map {|x| encode_type(type.subtype, x) }.join
79
- end
80
- end
81
- end
82
-
83
- def encode_primitive_type(type, arg)
84
- case type.base
85
- when 'uint'
86
- begin
87
- real_size = type.sub.to_i
88
- i = get_uint arg
89
-
90
- raise ValueOutOfBounds, arg unless i >= 0 && i < 2**real_size
91
- Utils.zpad_int i
92
- rescue EncodingError
93
- raise ValueOutOfBounds, arg
94
- end
95
- when 'bool'
96
- raise ArgumentError, "arg is not bool: #{arg}" unless arg.instance_of?(TrueClass) || arg.instance_of?(FalseClass)
97
- Utils.zpad_int(arg ? 1 : 0)
98
- when 'int'
99
- begin
100
- real_size = type.sub.to_i
101
- i = get_int arg
102
-
103
- raise ValueOutOfBounds, arg unless i >= -2**(real_size-1) && i < 2**(real_size-1)
104
- Utils.zpad_int(i % 2**type.sub.to_i)
105
- rescue EncodingError
106
- raise ValueOutOfBounds, arg
107
- end
108
- when 'ufixed'
109
- high, low = type.sub.split('x').map(&:to_i)
110
-
111
- raise ValueOutOfBounds, arg unless arg >= 0 && arg < 2**high
112
- Utils.zpad_int((arg * 2**low).to_i)
113
- when 'fixed'
114
- high, low = type.sub.split('x').map(&:to_i)
115
-
116
- raise ValueOutOfBounds, arg unless arg >= -2**(high - 1) && arg < 2**(high - 1)
117
-
118
- i = (arg * 2**low).to_i
119
- Utils.zpad_int(i % 2**(high+low))
120
- when 'string'
121
- if arg.encoding.name == 'UTF-8'
122
- arg = arg.b
123
- else
124
- begin
125
- arg.unpack('U*')
126
- rescue ArgumentError
127
- raise ValueError, "string must be UTF-8 encoded"
128
- end
129
- end
130
-
131
- if type.sub.empty? # variable length type
132
- raise ValueOutOfBounds, "Integer invalid or out of range: #{arg.size}" if arg.size >= TT256
133
- size = Utils.zpad_int arg.size
134
- value = Utils.rpad arg, BYTE_ZERO, Utils.ceil32(arg.size)
135
- "#{size}#{value}"
136
- else # fixed length type
137
- sub = type.sub.to_i
138
- raise ValueOutOfBounds, "invalid string length #{sub}" if arg.size > sub
139
- raise ValueOutOfBounds, "invalid string length #{sub}" if sub < 0 || sub > 32
140
- Utils.rpad(arg, BYTE_ZERO, 32)
141
- end
142
- when 'bytes'
143
- raise EncodingError, "Expecting string: #{arg}" unless arg.instance_of?(String)
144
- arg = arg.b
145
-
146
- if type.sub.empty? # variable length type
147
- raise ValueOutOfBounds, "Integer invalid or out of range: #{arg.size}" if arg.size >= TT256
148
- size = Utils.zpad_int arg.size
149
- value = Utils.rpad arg, BYTE_ZERO, Utils.ceil32(arg.size)
150
- "#{size}#{value}"
151
- else # fixed length type
152
- sub = type.sub.to_i
153
- raise ValueOutOfBounds, "invalid bytes length #{sub}" if arg.size > sub
154
- raise ValueOutOfBounds, "invalid bytes length #{sub}" if sub < 0 || sub > 32
155
- Utils.rpad(arg, BYTE_ZERO, 32)
156
- end
157
- when 'hash'
158
- size = type.sub.to_i
159
- raise EncodingError, "too long: #{arg}" unless size > 0 && size <= 32
160
-
161
- if arg.is_a?(Integer)
162
- Utils.zpad_int(arg)
163
- elsif arg.size == size
164
- Utils.zpad arg, 32
165
- elsif arg.size == size * 2
166
- Utils.zpad_hex arg
167
- else
168
- raise EncodingError, "Could not parse hash: #{arg}"
169
- end
170
- when 'address'
171
- if arg.is_a?(Integer)
172
- Utils.zpad_int arg
173
- elsif arg.size == 20
174
- Utils.zpad arg, 32
175
- elsif arg.size == 40
176
- Utils.zpad_hex arg
177
- elsif arg.size == 42 && arg[0,2] == '0x'
178
- Utils.zpad_hex arg[2..-1]
179
- else
180
- raise EncodingError, "Could not parse address: #{arg}"
181
- end
182
- else
183
- raise EncodingError, "Unhandled type: #{type.base} #{type.sub}"
184
- end
185
- end
186
-
187
-
188
- def min_data_size types
189
- types.size*32
190
- end
191
-
192
- ##
193
- # Decodes multiple arguments using the head/tail mechanism.
194
- #
195
- def decode_abi types, data, raise_errors = false
196
- parsed_types = types.map {|t| Type.parse(t) }
197
-
198
- outputs = [nil] * types.size
199
- start_positions = [nil] * types.size + [data.size]
200
-
201
- # TODO: refactor, a reverse iteration will be better
202
- pos = 0
203
- parsed_types.each_with_index do |t, i|
204
- # If a type is static, grab the data directly, otherwise record its
205
- # start position
206
- if t.dynamic?
207
-
208
- if raise_errors && pos>data.size-1
209
- raise DecodingError, "Position out of bounds #{pos}>#{data.size-1}"
210
- end
211
-
212
- start_positions[i] = Utils.big_endian_to_int(data[pos, 32])
213
-
214
- if raise_errors && start_positions[i]>data.size-1
215
- raise DecodingError, "Start position out of bounds #{start_positions[i]}>#{data.size-1}"
216
- end
217
-
218
- j = i - 1
219
- while j >= 0 && start_positions[j].nil?
220
- start_positions[j] = start_positions[i]
221
- j -= 1
222
- end
223
-
224
- pos += 32
225
- else
226
- outputs[i] = zero_padding data, pos, t.size, start_positions
227
- pos += t.size
228
- end
229
- end
230
-
231
- # We add a start position equal to the length of the entire data for
232
- # convenience.
233
- j = types.size - 1
234
- while j >= 0 && start_positions[j].nil?
235
- start_positions[j] = start_positions[types.size]
236
- j -= 1
237
- end
238
-
239
- if raise_errors && pos > data.size
240
- raise DecodingError, "Not enough data for head"
241
- end
242
-
243
-
244
- parsed_types.each_with_index do |t, i|
245
- if t.dynamic?
246
- offset, next_offset = start_positions[i, 2]
247
- if offset<=data.size && next_offset<=data.size
248
- outputs[i] = data[offset...next_offset]
249
- end
250
- end
251
- end
252
-
253
- if raise_errors && outputs.include?(nil)
254
- raise DecodingError, "Not all data can be parsed"
255
- end
256
-
257
- parsed_types.zip(outputs).map {|(type, out)| decode_type(type, out) }
258
- end
259
-
260
-
261
- def zero_padding data, pos, count, start_positions
262
- if pos >= data.size
263
- start_positions[start_positions.size-1] += count
264
- "\x00"*count
265
- elsif pos + count > data.size
266
- start_positions[start_positions.size-1] += ( count - (data.size-pos))
267
- data[pos,data.size-pos] + "\x00"*( count - (data.size-pos))
268
- else
269
- data[pos, count]
270
- end
271
- end
272
-
273
- def decode_typed_data type_name, data
274
- decode_primitive_type Type.parse(type_name), data
275
- end
276
-
277
- def decode_type(type, arg)
278
- return nil if arg.nil? || arg.empty?
279
- if type.kind_of?(Tuple) && type.dims.empty?
280
- arg ? decode_abi(type.types, arg) : []
281
- elsif %w(string bytes).include?(type.base) && type.sub.empty?
282
- l = Utils.big_endian_to_int arg[0,32]
283
- data = arg[32..-1]
284
- data[0, l]
285
- elsif !type.dims.empty? && (l = type.dims.last)>0 # static-sized arrays
286
- subtype = type.subtype
287
- if subtype.dynamic?
288
- start_positions = (0...l).map {|i| Utils.big_endian_to_int(arg[32*i, 32]) }
289
- start_positions.push arg.size
290
-
291
- outputs = (0...l).map {|i| arg[start_positions[i]...start_positions[i+1]] }
292
-
293
- outputs.map {|out| decode_type(subtype, out) }
294
- else
295
- (0...l).map {|i| decode_type(subtype, arg[subtype.size*i, subtype.size]) }
296
- end
297
-
298
- elsif type.dynamic?
299
- l = Utils.big_endian_to_int arg[0,32]
300
- raise DecodingError, "Too long length: #{l}" if l>100000
301
- subtype = type.subtype
302
-
303
- if subtype.dynamic?
304
- raise DecodingError, "Not enough data for head" unless arg.size >= 32 + 32*l
305
-
306
- start_positions = (1..l).map {|i| 32+Utils.big_endian_to_int(arg[32*i, 32]) }
307
- start_positions.push arg.size
308
-
309
- outputs = (0...l).map {|i| arg[start_positions[i]...start_positions[i+1]] }
310
-
311
- outputs.map {|out| decode_type(subtype, out) }
312
- else
313
- (0...l).map {|i| decode_type(subtype, arg[32 + subtype.size*i, subtype.size]) }
314
- end
315
-
316
- else
317
- decode_primitive_type type, arg
318
- end
319
- end
320
-
321
- def decode_primitive_type(type, data)
322
- case type.base
323
- when 'address'
324
- Utils.encode_hex data[12..-1]
325
- when 'string', 'bytes'
326
- if type.sub.empty? # dynamic
327
- if data.length==32
328
- data[0..32]
329
- else
330
- size = Utils.big_endian_to_int data[0,32]
331
- data[32..-1][0,size]
332
- end
333
- else # fixed
334
- data[0, type.sub.to_i]
335
- end
336
- when 'hash'
337
- data[(32 - type.sub.to_i), type.sub.to_i]
338
- when 'uint'
339
- Utils.big_endian_to_int data
340
- when 'int'
341
- u = Utils.big_endian_to_int data
342
- u >= 2**(type.sub.to_i-1) ? (u - 2**type.sub.to_i) : u
343
- when 'ufixed'
344
- high, low = type.sub.split('x').map(&:to_i)
345
- Utils.big_endian_to_int(data) * 1.0 / 2**low
346
- when 'fixed'
347
- high, low = type.sub.split('x').map(&:to_i)
348
- u = Utils.big_endian_to_int data
349
- i = u >= 2**(high+low-1) ? (u - 2**(high+low)) : u
350
- i * 1.0 / 2**low
351
- when 'bool'
352
- data[-1] == BYTE_ONE
353
- else
354
- raise DecodingError, "Unknown primitive type: #{type.base}"
355
- end
356
- end
357
-
358
- private
359
-
360
- def get_uint(n)
361
- case n
362
- when Integer
363
- raise EncodingError, "Number out of range: #{n}" if n > UINT_MAX || n < UINT_MIN
364
- n
365
- when String
366
- i = if n.size == 40
367
- Utils.decode_hex(n)
368
- elsif n.size <= 32
369
- n
370
- else
371
- raise EncodingError, "String too long: #{n}"
372
- end
373
- i = Utils.big_endian_to_int i
374
-
375
- raise EncodingError, "Number out of range: #{i}" if i > UINT_MAX || i < UINT_MIN
376
- i
377
- when true
378
- 1
379
- when false, nil
380
- 0
381
- else
382
- raise EncodingError, "Cannot decode uint: #{n}"
383
- end
384
- end
385
-
386
- def get_int(n)
387
- case n
388
- when Integer
389
- raise EncodingError, "Number out of range: #{n}" if n > INT_MAX || n < INT_MIN
390
- n
391
- when String
392
- i = if n.size == 40
393
- Utils.decode_hex(n)
394
- elsif n.size <= 32
395
- n
396
- else
397
- raise EncodingError, "String too long: #{n}"
398
- end
399
- i = Utils.big_endian_to_int i
400
-
401
- i = i > INT_MAX ? (i-TT256) : i
402
- raise EncodingError, "Number out of range: #{i}" if i > INT_MAX || i < INT_MIN
403
- i
404
- when true
405
- 1
406
- when false, nil
407
- 0
408
- else
409
- raise EncodingError, "Cannot decode int: #{n}"
410
- end
411
- end
412
- end # class Codec
413
-
414
-
415
-
416
-
417
-
418
-
419
- def self.codec
420
- @codec ||= Codec.new
421
- end
422
-
423
- def self.encode_abi(types, args)
424
- codec.encode_abi( types, args )
425
- end
426
-
427
- def self.decode_abi(types, data, raise_errors = false)
428
- codec.decode_abi( types, data, raise_errors )
429
- end
430
-
431
-
432
- end # module Abi
433
- end # module Ethlite
434
-
435
-
436
-
@@ -1,200 +0,0 @@
1
- module Ethlite
2
- module Abi
3
- class Type
4
-
5
- class ParseError < StandardError; end
6
-
7
-
8
- ##
9
- # Crazy regexp to seperate out base type component (eg. uint), size (eg.
10
- # 256, 128x128, nil), array component (eg. [], [45], nil)
11
- #
12
- def self.parse( type )
13
-
14
- return parse('uint256') if type=='trcToken'
15
-
16
- if type =~ /^\((.*)\)((\[[0-9]*\])*)/
17
- return Tuple.parse $1, $2.scan(/\[[0-9]*\]/)
18
- end
19
-
20
- _, base, sub, dimension = /([a-z]*)([0-9]*x?[0-9]*)((\[[0-9]*\])*)/.match(type).to_a
21
-
22
- dims = dimension.scan(/\[[0-9]*\]/)
23
- raise ParseError, "Unknown characters found in array declaration" if dims.join != dimension
24
-
25
- case base
26
- when ''
27
- return parse 'address'
28
- when 'bytes', 'string'
29
- raise ParseError, "Maximum 32 bytes for fixed-length string or bytes" unless sub.empty? || sub.to_i <= 32
30
- when 'uint', 'int'
31
- raise ParseError, "Integer type must have numerical suffix" unless sub =~ /\A[0-9]+\z/
32
-
33
- size = sub.to_i
34
- raise ParseError, "Integer size out of bounds" unless size >= 8 && size <= 256
35
- raise ParseError, "Integer size must be multiple of 8" unless size % 8 == 0
36
- when 'fixed', 'ufixed'
37
- raise ParseError, "Fixed type must have suffix of form <high>x<low>, e.g. 128x128" unless sub =~ /\A[0-9]+x[0-9]+\z/
38
-
39
- high, low = sub.split('x').map(&:to_i)
40
- total = high + low
41
-
42
- raise ParseError, "Fixed size out of bounds (max 32 bytes)" unless total >= 8 && total <= 256
43
- raise ParseError, "Fixed high size must be multiple of 8" unless high % 8 == 0
44
- raise ParseError, "Low sizes must be 0 to 80" unless low>0 && low<=80
45
- when 'hash'
46
- raise ParseError, "Hash type must have numerical suffix" unless sub =~ /\A[0-9]+\z/
47
- when 'address'
48
- raise ParseError, "Address cannot have suffix" unless sub.empty?
49
- when 'bool'
50
- raise ParseError, "Bool cannot have suffix" unless sub.empty?
51
- else
52
- raise ParseError, "Unrecognized type base: #{base}"
53
- end
54
-
55
- new(base, sub, dims.map {|x| x[1...-1].to_i})
56
- end
57
-
58
- def self.size_type
59
- @size_type ||= new('uint', 256, [])
60
- end
61
-
62
-
63
- attr :base, :sub, :dims
64
-
65
- ##
66
- # @param base [String] base name of type, e.g. uint for uint256[4]
67
- # @param sub [String] subscript of type, e.g. 256 for uint256[4]
68
- # @param dims [Array[Integer]] dimensions of array type, e.g. [1,2,0]
69
- # for uint256[1][2][], [] for non-array type
70
- #
71
- def initialize( base, sub, dims )
72
- @base = base
73
- @sub = sub
74
- @dims = dims
75
- end
76
-
77
- def ==(another_type)
78
- base == another_type.base &&
79
- sub == another_type.sub &&
80
- dims == another_type.dims
81
- end
82
-
83
- ##
84
- # Get the static size of a type, or nil if dynamic.
85
- #
86
- # @return [Integer, NilClass] size of static type, or nil for dynamic
87
- # type
88
- #
89
- def size
90
- @size ||= if dims.empty?
91
- if %w(string bytes).include?(base) && sub.empty?
92
- nil
93
- else
94
- 32
95
- end
96
- else
97
- if dims.last == 0 # 0 for dynamic array []
98
- nil
99
- else
100
- subtype.dynamic? ? nil : dims.last * subtype.size
101
- end
102
- end
103
- end
104
-
105
- def dynamic?
106
- size.nil?
107
- end
108
-
109
- ##
110
- # Type with one dimension lesser.
111
- #
112
- # @example
113
- # Type.parse("uint256[2][]").subtype # => Type.new('uint', 256, [2])
114
- #
115
- # @return [Ethereum::ABI::Type]
116
- #
117
- def subtype
118
- @subtype ||= self.class.new(base, sub, dims[0...-1])
119
- end
120
- end # class Type
121
-
122
-
123
- class Tuple < Type
124
-
125
- def self.parse( types, dims )
126
-
127
- depth = 0
128
- collected = []
129
- current = ''
130
-
131
- types.split('').each do |c|
132
- case c
133
- when ',' then
134
- if depth==0
135
- collected << current
136
- current = ''
137
- else
138
- current += c
139
- end
140
- when '(' then
141
- depth += 1
142
- current += c
143
- when ')' then
144
- depth -= 1
145
- current += c
146
- else
147
- current += c
148
- end
149
-
150
- end
151
- collected << current unless current.empty?
152
-
153
- Tuple.new collected, dims.map {|x| x[1...-1].to_i}
154
-
155
- end
156
-
157
- attr_reader :types, :parsed_types
158
-
159
- def initialize( types, dims )
160
- super('tuple', '', dims)
161
- @types = types
162
- @parsed_types = types.map{|t| Type.parse t}
163
- end
164
-
165
- def ==(another_type)
166
- another_type.kind_of?(Tuple) &&
167
- another_type.types == types &&
168
- another_type.dims == dims
169
- end
170
-
171
- def size
172
- @size ||= calculate_size
173
- end
174
-
175
- def calculate_size
176
- if dims.empty?
177
- s = 0
178
- parsed_types.each do |type|
179
- ts = type.size
180
- return nil if ts.nil?
181
- s += ts
182
- end
183
- s
184
- else
185
- if dims.last == 0 # 0 for dynamic array []
186
- nil
187
- else
188
- subtype.dynamic? ? nil : dims.last * subtype.size
189
- end
190
- end
191
- end
192
-
193
- def subtype
194
- @subtype ||= Tuple.new(types, dims[0...-1])
195
- end
196
- end # class Tuple
197
-
198
-
199
- end # module Abi
200
- end # module Ethlite
@@ -1,35 +0,0 @@
1
-
2
- module Ethlite
3
-
4
-
5
- ## todo/check - use encoding -ascii-8bit for source file or ? - why? why not?
6
- ## use #b/.b to ensure binary encoding? - why? why not?
7
-
8
- ## todo/check: use auto-freeze string literals magic comment - why? why not?
9
- BYTE_EMPTY = "".b.freeze
10
- BYTE_ZERO = "\x00".b.freeze
11
- BYTE_ONE = "\x01".b.freeze
12
-
13
-
14
- TT32 = 2**32
15
- TT40 = 2**40
16
- TT160 = 2**160
17
- TT256 = 2**256
18
- TT64M1 = 2**64 - 1
19
-
20
- UINT_MAX = 2**256 - 1
21
- UINT_MIN = 0
22
- INT_MAX = 2**255 - 1
23
- INT_MIN = -2**255
24
-
25
- HASH_ZERO = ("\x00"*32).b.freeze
26
-
27
-
28
- PUBKEY_ZERO = ("\x00"*32).b.freeze
29
- PRIVKEY_ZERO = ("\x00"*32).b.freeze
30
-
31
- PRIVKEY_ZERO_HEX = ('0'*64).freeze
32
-
33
- CONTRACT_CODE_SIZE_LIMIT = 0x6000
34
-
35
- end # module Ethlite