tezos_client 1.1.4 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -3
  3. data/Gemfile.lock +29 -29
  4. data/lib/tezos_client.rb +2 -0
  5. data/lib/tezos_client/commands.rb +5 -1
  6. data/lib/tezos_client/crypto.rb +20 -0
  7. data/lib/tezos_client/exceptions.rb +3 -0
  8. data/lib/tezos_client/operation_mgr.rb +29 -29
  9. data/lib/tezos_client/rpc_interface/contracts.rb +15 -3
  10. data/lib/tezos_client/smartpy_interface.rb +10 -10
  11. data/lib/tezos_client/tools/annots_to_type.rb +56 -0
  12. data/lib/tezos_client/tools/convert_to_hash.rb +10 -91
  13. data/lib/tezos_client/tools/convert_to_hash/address.rb +29 -0
  14. data/lib/tezos_client/tools/convert_to_hash/base.rb +50 -0
  15. data/lib/tezos_client/tools/convert_to_hash/big_map.rb +20 -0
  16. data/lib/tezos_client/tools/convert_to_hash/bytes.rb +13 -0
  17. data/lib/tezos_client/tools/convert_to_hash/int.rb +13 -0
  18. data/lib/tezos_client/tools/convert_to_hash/key.rb +27 -0
  19. data/lib/tezos_client/tools/convert_to_hash/list.rb +22 -0
  20. data/lib/tezos_client/tools/convert_to_hash/map.rb +31 -0
  21. data/lib/tezos_client/tools/convert_to_hash/nat.rb +13 -0
  22. data/lib/tezos_client/tools/convert_to_hash/pair.rb +21 -0
  23. data/lib/tezos_client/tools/convert_to_hash/signature.rb +19 -0
  24. data/lib/tezos_client/tools/convert_to_hash/string.rb +13 -0
  25. data/lib/tezos_client/tools/convert_to_hash/timestamp.rb +19 -0
  26. data/lib/tezos_client/tools/find_big_maps_in_storage.rb +9 -40
  27. data/lib/tezos_client/tools/hash_to_micheline.rb +110 -0
  28. data/lib/tezos_client/tools/system_call.rb +1 -1
  29. data/lib/tezos_client/version.rb +1 -1
  30. metadata +22 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ee3c68e5d6cffc2ac7406a7c4084e3552c47cbd68c03570e4ca1df7bc390514a
4
- data.tar.gz: ec225c29031adde39b24b28a8b8f16188284da34c3afc0aafd788a95300f2b52
3
+ metadata.gz: a4b97cc7f2f7ce85618cf8e16ae7b921f7e4221b1e27a1ea52c415c309076cf1
4
+ data.tar.gz: 6ddca2df0566dc441cc0a3389f5a0f86d9015766118d775c27166b2bab2d247e
5
5
  SHA512:
6
- metadata.gz: baf64e4c19c600a270cc0fe653e5825113557f32368db57d10e449b5458a75c1c5931de4e6e1b123cdc7d183b977ae15085150de2a57458b1a477c18344d7321
7
- data.tar.gz: d41ef55a2ca3cf47615fea6860dff285bef062f86553ca558934b11717b545b21068ee33bedf8192e50596805b49e39a0ebf32644078f669275ac95dd0d572cd
6
+ metadata.gz: ba5a9ae1ab33a17abe2c263a687e03278ededd753d2baaadd999ea6350d15bc2de9ccd990aee1ca6db14acd794ec45174229b517997774d5442ad34a0526260d
7
+ data.tar.gz: 8044d64bb09b9f0c616413a1ed308ebec7e6c9af39700fcae6c134fab3ad6c785f86faf89863ee1e0cf857488ed040d8b7e56c2901cfdc9042335697798762b0
@@ -13,11 +13,11 @@ rvm:
13
13
  before_install:
14
14
  - sh travis-scripts/prepare-ubuntu.sh
15
15
  - mkdir -p $HOME/bin
16
- - curl -s https://SmartPy.io/SmartPyBasic/SmartPy.sh > SmartPy.sh
16
+ - curl -s https://smartpy.io/dev-20200912-bbb4b34cb579f3d52320c3d2aed8ebcef04429b0/cli/SmartPy.sh > SmartPy.sh
17
17
  - chmod +x ./SmartPy.sh
18
- - ./SmartPy.sh local-install $HOME/bin/smartpy
18
+ - ./SmartPy.sh local-install-auto $HOME/bin/smartpy/cli
19
19
  - rm ./SmartPy.sh
20
- - export PATH="$PATH:$HOME/bin/:$HOME/bin/smartpy/SmartPyBasic/"
20
+ - export PATH="$PATH:$HOME/bin/:$HOME/bin/smartpy/cli/"
21
21
  - npm link michelson-to-micheline
22
22
  - gem install bundler -v 1.16.3
23
23
  script:
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- tezos_client (1.1.4)
4
+ tezos_client (1.3.0)
5
5
  active_interaction (~> 3.7)
6
6
  activesupport (~> 6.0.0)
7
7
  base58 (~> 0.2.3)
@@ -14,68 +14,68 @@ PATH
14
14
  GEM
15
15
  remote: https://rubygems.org/
16
16
  specs:
17
- actionpack (6.0.0)
18
- actionview (= 6.0.0)
19
- activesupport (= 6.0.0)
20
- rack (~> 2.0)
17
+ actionpack (6.0.3.3)
18
+ actionview (= 6.0.3.3)
19
+ activesupport (= 6.0.3.3)
20
+ rack (~> 2.0, >= 2.0.8)
21
21
  rack-test (>= 0.6.3)
22
22
  rails-dom-testing (~> 2.0)
23
23
  rails-html-sanitizer (~> 1.0, >= 1.2.0)
24
- actionview (6.0.0)
25
- activesupport (= 6.0.0)
24
+ actionview (6.0.3.3)
25
+ activesupport (= 6.0.3.3)
26
26
  builder (~> 3.1)
27
27
  erubi (~> 1.4)
28
28
  rails-dom-testing (~> 2.0)
29
29
  rails-html-sanitizer (~> 1.1, >= 1.2.0)
30
- active_interaction (3.8.0)
30
+ active_interaction (3.8.3)
31
31
  activemodel (>= 4, < 7)
32
- activemodel (6.0.0)
33
- activesupport (= 6.0.0)
34
- activesupport (6.0.0)
32
+ activemodel (6.0.3.3)
33
+ activesupport (= 6.0.3.3)
34
+ activesupport (6.0.3.3)
35
35
  concurrent-ruby (~> 1.0, >= 1.0.2)
36
36
  i18n (>= 0.7, < 2)
37
37
  minitest (~> 5.1)
38
38
  tzinfo (~> 1.1)
39
- zeitwerk (~> 2.1, >= 2.1.8)
39
+ zeitwerk (~> 2.2, >= 2.2.2)
40
40
  addressable (2.7.0)
41
41
  public_suffix (>= 2.0.2, < 5.0)
42
42
  ast (2.4.0)
43
43
  base58 (0.2.3)
44
44
  bip_mnemonic (0.0.4)
45
- builder (3.2.3)
45
+ builder (3.2.4)
46
46
  coderay (1.1.2)
47
- concurrent-ruby (1.1.5)
47
+ concurrent-ruby (1.1.7)
48
48
  crack (0.4.3)
49
49
  safe_yaml (~> 1.0.0)
50
- crass (1.0.5)
50
+ crass (1.0.6)
51
51
  diff-lcs (1.3)
52
52
  domain_name (0.5.20190701)
53
53
  unf (>= 0.0.5, < 1.0.0)
54
54
  erubi (1.9.0)
55
- ffi (1.12.2)
55
+ ffi (1.13.1)
56
56
  hashdiff (1.0.0)
57
57
  http-cookie (1.0.3)
58
58
  domain_name (~> 0.5)
59
59
  httparty (0.17.3)
60
60
  mime-types (~> 3.0)
61
61
  multi_xml (>= 0.5.2)
62
- i18n (1.7.0)
62
+ i18n (1.8.5)
63
63
  concurrent-ruby (~> 1.0)
64
64
  jaro_winkler (1.5.3)
65
- loofah (2.3.1)
65
+ loofah (2.7.0)
66
66
  crass (~> 1.0.2)
67
67
  nokogiri (>= 1.5.9)
68
68
  method_source (0.9.2)
69
69
  mime-types (3.3.1)
70
70
  mime-types-data (~> 3.2015)
71
- mime-types-data (3.2019.1009)
71
+ mime-types-data (3.2020.0512)
72
72
  mini_portile2 (2.4.0)
73
- minitest (5.13.0)
73
+ minitest (5.14.2)
74
74
  money-tree (0.10.0)
75
75
  ffi
76
76
  multi_xml (0.6.0)
77
77
  netrc (0.11.0)
78
- nokogiri (1.10.8)
78
+ nokogiri (1.10.10)
79
79
  mini_portile2 (~> 2.4.0)
80
80
  parallel (1.18.0)
81
81
  parser (2.6.5.0)
@@ -84,7 +84,7 @@ GEM
84
84
  coderay (~> 1.1.0)
85
85
  method_source (~> 0.9.0)
86
86
  public_suffix (4.0.1)
87
- rack (2.0.8)
87
+ rack (2.2.3)
88
88
  rack-test (1.1.0)
89
89
  rack (>= 1.0, < 3)
90
90
  rails-dom-testing (2.0.3)
@@ -92,9 +92,9 @@ GEM
92
92
  nokogiri (>= 1.6)
93
93
  rails-html-sanitizer (1.3.0)
94
94
  loofah (~> 2.3)
95
- railties (6.0.0)
96
- actionpack (= 6.0.0)
97
- activesupport (= 6.0.0)
95
+ railties (6.0.3.3)
96
+ actionpack (= 6.0.3.3)
97
+ activesupport (= 6.0.3.3)
98
98
  method_source
99
99
  rake (>= 0.8.7)
100
100
  thor (>= 0.20.3, < 2.0)
@@ -138,20 +138,20 @@ GEM
138
138
  rubocop-rails (~> 2.0)
139
139
  ruby-progressbar (1.10.1)
140
140
  safe_yaml (1.0.5)
141
- thor (0.20.3)
141
+ thor (1.0.1)
142
142
  thread_safe (0.3.6)
143
- tzinfo (1.2.5)
143
+ tzinfo (1.2.7)
144
144
  thread_safe (~> 0.1)
145
145
  unf (0.1.4)
146
146
  unf_ext
147
- unf_ext (0.0.7.6)
147
+ unf_ext (0.0.7.7)
148
148
  unicode-display_width (1.6.0)
149
149
  vcr (4.0.0)
150
150
  webmock (3.7.6)
151
151
  addressable (>= 2.3.6)
152
152
  crack (>= 0.3.2)
153
153
  hashdiff (>= 0.4.0, < 2.0.0)
154
- zeitwerk (2.2.1)
154
+ zeitwerk (2.4.0)
155
155
 
156
156
  PLATFORMS
157
157
  ruby
@@ -36,6 +36,8 @@ require "tezos_client/smartpy_interface"
36
36
 
37
37
  require "tezos_client/tools/convert_to_hash"
38
38
  require "tezos_client/tools/find_big_maps_in_storage"
39
+ require "tezos_client/tools/hash_to_micheline"
40
+ require "tezos_client/tools/annots_to_type"
39
41
 
40
42
  class TezosClient
41
43
  using CurrencyUtils
@@ -22,6 +22,10 @@ class TezosClient
22
22
  :pending_operations,
23
23
  :pack_data,
24
24
  :big_map_value,
25
- :list_big_map_by_contract
25
+ :contract_big_maps,
26
+ :block_operations,
27
+ :contract_storage_type,
28
+ :entrypoint,
29
+ :entrypoints
26
30
  end
27
31
  end
@@ -165,6 +165,22 @@ class TezosClient
165
165
  end
166
166
  end
167
167
 
168
+ # payload must be bytes
169
+ def check_signature(public_key:, signature:, payload:)
170
+ verify_key = RbNaCl::VerifyKey.new(decode_tz(public_key).to_bin)
171
+
172
+ bin_sig = decode_tz(signature).to_bin
173
+ payload_hash = RbNaCl::Hash::Blake2b.digest(ignore_0x(payload).to_bin, digest_size: 32)
174
+
175
+ verify_key.verify(bin_sig, payload_hash)
176
+ rescue RbNaCl::BadSignatureError
177
+ false
178
+ end
179
+
180
+ def check_signature!(public_key:, signature:, payload:)
181
+ check_signature(public_key: public_key, signature: signature, payload: payload) || raise(BadSignatureError)
182
+ end
183
+
168
184
  def operation_id(signed_operation_hex)
169
185
  hash = RbNaCl::Hash::Blake2b.digest(
170
186
  signed_operation_hex.to_bin,
@@ -220,5 +236,9 @@ class TezosClient
220
236
  RbNaCl::SigningKey.generate
221
237
  end
222
238
  end
239
+
240
+ def ignore_0x(payload)
241
+ payload.starts_with?("0x") ? payload[2..-1] : payload
242
+ end
223
243
  end
224
244
  end
@@ -23,6 +23,9 @@ class TezosClient
23
23
  class SmartPyError < SysCallError
24
24
  end
25
25
 
26
+ class BadSignatureError < StandardError
27
+ end
28
+
26
29
  class InvalidActivation < RpcRequestFailure
27
30
  attr_reader :pkh
28
31
 
@@ -184,7 +184,8 @@ class TezosClient
184
184
  operations: rpc_operation_args,
185
185
  signature: base_58_signature,
186
186
  protocol: protocol,
187
- branch: branch)
187
+ branch: branch
188
+ )
188
189
 
189
190
  ensure_applied!(rpc_responses)
190
191
 
@@ -201,40 +202,39 @@ class TezosClient
201
202
  end
202
203
 
203
204
  private
205
+ def ensure_applied!(rpc_responses)
206
+ metadatas = rpc_responses.map { |response| response[:metadata] }
207
+ operation_results = metadatas.map { |metadata| metadata[:operation_result] }
208
+ internal_operations = metadatas.map { |metadata| metadata[:internal_operation_results] }.flatten.compact
209
+ operation_results.concat(internal_operations.map { |internal_operation| internal_operation[:result] })
210
+
211
+ failed = operation_results.detect do |operation_result|
212
+ operation_result != nil && operation_result[:status] != "applied"
213
+ end
204
214
 
205
- def ensure_applied!(rpc_responses)
206
- metadatas = rpc_responses.map { |response| response[:metadata] }
207
- operation_results = metadatas.map { |metadata| metadata[:operation_result] }
208
- internal_operations = metadatas.map { |metadata| metadata[:internal_operation_results] }.flatten.compact
209
- operation_results.concat(internal_operations.map { |internal_operation| internal_operation[:result] })
210
-
211
- failed = operation_results.detect do |operation_result|
212
- operation_result != nil && operation_result[:status] != "applied"
213
- end
215
+ return metadatas if failed.nil?
214
216
 
215
- return metadatas if failed.nil?
217
+ failed_operation_result = operation_results.detect do |operation_result|
218
+ operation_result[:status] == "failed"
219
+ end
216
220
 
217
- failed_operation_result = operation_results.detect do |operation_result|
218
- operation_result[:status] == "failed"
221
+ failed!("failed", failed_operation_result[:errors], operation_results)
219
222
  end
220
223
 
221
- failed!("failed", failed_operation_result[:errors], operation_results)
222
- end
223
-
224
- def exception_klass(errors)
225
- error = errors[0]
226
- case error[:id]
227
- when TezBalanceTooLow::FIRST_ERROR_REGEXP
228
- TezBalanceTooLow
229
- when ScriptRuntimeError::FIRST_ERROR_REGEXP
230
- ScriptRuntimeError
231
- else
232
- OperationFailure
224
+ def exception_klass(errors)
225
+ error = errors[0]
226
+ case error[:id]
227
+ when TezBalanceTooLow::FIRST_ERROR_REGEXP
228
+ TezBalanceTooLow
229
+ when ScriptRuntimeError::FIRST_ERROR_REGEXP
230
+ ScriptRuntimeError
231
+ else
232
+ OperationFailure
233
+ end
233
234
  end
234
- end
235
235
 
236
- def failed!(status, errors, metadata)
237
- raise exception_klass(errors).new(metadata: metadata, errors: errors, status: status)
238
- end
236
+ def failed!(status, errors, metadata)
237
+ raise exception_klass(errors).new(metadata: metadata, errors: errors, status: status)
238
+ end
239
239
  end
240
240
  end
@@ -28,20 +28,32 @@ class TezosClient
28
28
  get "#{contract_link(contract_id)}/manager_key"
29
29
  end
30
30
 
31
+ def contract_storage_type(contract_id)
32
+ contract = contract_detail(contract_id)
33
+ contract[:script][:code].find { |elem| elem[:prim] == "storage" }[:args].first
34
+ end
35
+
31
36
  def contract_storage(contract_id)
32
37
  get "#{contract_link(contract_id)}/storage"
33
38
  end
34
39
 
40
+ def entrypoints(contract_id)
41
+ get("#{contract_link(contract_id)}/entrypoints")
42
+ end
43
+
44
+ def entrypoint(contract_id, entrypoint)
45
+ get("#{contract_link(contract_id)}/entrypoints/#{entrypoint}")
46
+ end
47
+
35
48
  def big_map_value(big_map_id:, key:, key_type:)
36
49
  expr_key = encode_script_expr(data: key, type: key_type)
37
50
 
38
51
  get "/chains/main/blocks/head/context/big_maps/#{big_map_id}/#{expr_key}"
39
52
  end
40
53
 
41
- def list_big_map_by_contract(contract_address:)
54
+ def contract_big_maps(contract_address)
42
55
  contract_storage = contract_storage(contract_address)
43
- contract = contract_detail(contract_address)
44
- storage_type = contract[:script][:code].find { |elem| elem[:prim] == "storage" }[:args].first
56
+ storage_type = contract_storage_type(contract_address)
45
57
 
46
58
  TezosClient::Tools::FindBigMapsInStorage.run!(
47
59
  storage: contract_storage,
@@ -30,16 +30,16 @@ class TezosClient
30
30
  end
31
31
 
32
32
  private
33
- def compile_to_michelson(args)
34
- Tools::TemporaryFile.with_file_copy(args[:script]) do |script_copy_path|
35
- script_basename = script_copy_path.split("/").last.sub(/.py$/, "")
36
- script_path = "/tmp/#{script_basename}/"
37
- init_script_filename = "contractStorage.tz"
38
- contract_script_filename = "contractCode.tz.json"
39
- call_smartpy ["local-compile", script_copy_path, args[:init_params], script_path]
40
-
41
- yield(script_path + contract_script_filename, script_path + init_script_filename)
42
- end
33
+ def compile_to_michelson(args)
34
+ Tools::TemporaryFile.with_file_copy(args[:script]) do |script_copy_path|
35
+ script_basename = script_copy_path.split("/").last.sub(/.py$/, "")
36
+ script_path = "/tmp/#{script_basename}/"
37
+ init_script_filename = "#{script_basename}_storage_init.tz"
38
+ contract_script_filename = "#{script_basename}_compiled.json"
39
+ call_smartpy ["compile", script_copy_path, args[:init_params], script_path]
40
+
41
+ yield(script_path + contract_script_filename, script_path + init_script_filename)
43
42
  end
43
+ end
44
44
  end
45
45
  end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient::Tools::AnnotsToType < ActiveInteraction::Base
4
+ # example of typed_annots :
5
+ # {
6
+ # spending_ref: "string",
7
+ # remainder_amount: "nat",
8
+ # expires_at: "timestamp"
9
+ # }
10
+ hash :typed_annots, strip: false
11
+
12
+ validate :validate_types
13
+
14
+ def execute
15
+ return { "prim" => typed_annots.values.first } if typed_annots.size == 1
16
+
17
+ { "prim" => "pair", "args" => generate_type_args(ordered_annots) }
18
+ end
19
+
20
+ private
21
+ def generate_type_args(annots)
22
+ annot = annots.pop
23
+ annot_type = typed_annots[annot]
24
+
25
+ unless annots.size == 1
26
+ return [
27
+ {
28
+ "prim" => annot_type,
29
+ "annots" => ["%#{annot}"]
30
+ },
31
+ {
32
+ "prim" => "pair",
33
+ "args" => generate_type_args(annots)
34
+ }
35
+ ]
36
+ end
37
+
38
+ generated_args = [{ "prim" => annot_type, "annots" => ["%#{annot}"] }]
39
+ annot = annots.pop
40
+ annot_type = typed_annots[annot]
41
+ generated_args.append({ "prim" => annot_type, "annots" => ["%#{annot}"] })
42
+
43
+ generated_args
44
+ end
45
+
46
+ def ordered_annots
47
+ @ordered_annots ||= typed_annots.keys.sort.reverse
48
+ end
49
+
50
+ def validate_types
51
+ allowed_types = TezosClient::Tools::HashToMicheline::TYPES_MAPPING.keys
52
+ return if typed_annots.values.map(&:to_sym).all? { |type| type.in? allowed_types }
53
+
54
+ errors.add(:base, "The allowed types are: #{allowed_types.join(', ')}")
55
+ end
56
+ end
@@ -1,99 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class TezosClient::Tools::ConvertToHash < ActiveInteraction::Base
4
- interface :data
5
- interface :type
3
+ require_relative "convert_to_hash/base"
6
4
 
7
- def execute
8
- decorated_value
9
- end
10
-
11
- private
12
- def value
13
- case type[:prim]
14
- when "pair"
15
- pair_type
16
- when "list"
17
- list_type
18
- when "int"
19
- int_type
20
- when "nat"
21
- int_type
22
- when "key"
23
- key_type
24
- when "timestamp"
25
- timestamp_type
26
- when "string"
27
- string_type
28
- when "address"
29
- address_type
30
- else
31
- raise "type '#{type[:prim]}' not implemented"
32
- end
33
- end
34
-
35
- def pair_type
36
- raise "Not a 'Pair' type" unless data[:prim] == "Pair"
37
- raise "Difference detected between data and type \nDATA: #{data} \nTYPE:#{type} " unless data[:args].size == type[:args].size
38
-
39
- (data[:args]).zip(type[:args]).map do |data_n, type_n|
40
- compose(
41
- TezosClient::Tools::ConvertToHash,
42
- data: data_n,
43
- type: type_n
44
- )
45
- end.reduce({}, &:merge)
46
- end
5
+ Dir[File.join(__dir__, "convert_to_hash", "*.rb")].each { |file| require file }
47
6
 
48
- def list_type
49
- convert_list_element(data: data, element_type: type[:args].first)
50
- end
7
+ class TezosClient
8
+ module Tools
9
+ class ConvertToHash < ActiveInteraction::Base
10
+ interface :data
11
+ interface :type
51
12
 
52
- def convert_list_element(data:, element_type:)
53
- data.map do |elem|
54
- compose(
55
- TezosClient::Tools::ConvertToHash,
56
- data: elem,
57
- type: element_type
58
- )
13
+ def execute
14
+ TezosClient::Tools::ConvertToHash::Base.new(data: data, type: type).value
15
+ end
59
16
  end
60
17
  end
61
-
62
- def int_type
63
- data[:int].to_i
64
- end
65
-
66
- def key_type
67
- data[:bytes] || data[:string]
68
- end
69
-
70
- def timestamp_type
71
- Time.zone.at(data[:int].to_i)
72
- end
73
-
74
- def string_type
75
- data[:string]
76
- end
77
-
78
- def address_type
79
- data[:bytes] || data[:string]
80
- end
81
-
82
- def decorated_value
83
- anonymous? ? value : { var_name => value }
84
- end
85
-
86
- def anonymous?
87
- !(type.key?(:annots) && type[:annots].any?)
88
- end
89
-
90
- def var_name_annot
91
- type[:annots].first
92
- end
93
-
94
- def var_name
95
- return nil if anonymous?
96
-
97
- "#{var_name_annot[1..-1]}".to_sym
98
- end
99
18
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ module Tools
5
+ class ConvertToHash < ActiveInteraction::Base
6
+ class Address < Base
7
+ include TezosClient::Crypto
8
+
9
+ def decode
10
+ if data.key?(:bytes)
11
+ if data[:bytes].start_with?("0000")
12
+ encode_tz(:tz1, data[:bytes][4..-1])
13
+ elsif data[:bytes].start_with?("0001")
14
+ encode_tz(:tz2, data[:bytes][4..-1])
15
+ elsif data[:bytes].start_with?("0002")
16
+ encode_tz(:tz3, data[:bytes][4..-1])
17
+ elsif data[:bytes].start_with?("01")
18
+ encode_tz(:KT, data[:bytes][2..-3])
19
+ else
20
+ data[:bytes]
21
+ end
22
+ else
23
+ data[:string]
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ module Tools
5
+ class ConvertToHash < ActiveInteraction::Base
6
+ class Base
7
+ def initialize(data:, type:)
8
+ @data = data
9
+ @type = type
10
+ end
11
+
12
+ attr_accessor :data, :type
13
+
14
+ def value
15
+ anonymous? ? decode : { var_name => decode }
16
+ end
17
+
18
+ protected
19
+ def decode
20
+ klass.new(
21
+ data: data,
22
+ type: type
23
+ ).decode
24
+
25
+ rescue NameError
26
+ raise NotImplementedError, "type '#{type[:prim]}' not implemented"
27
+ end
28
+
29
+ def anonymous?
30
+ !(type.key?(:annots) && type[:annots].any?)
31
+ end
32
+
33
+ def var_name_annot
34
+ type[:annots].first
35
+ end
36
+
37
+ def var_name
38
+ return nil if anonymous?
39
+
40
+ "#{var_name_annot[1..-1]}".to_sym
41
+ end
42
+
43
+ private
44
+ def klass
45
+ "TezosClient::Tools::ConvertToHash::#{type[:prim].camelize}".constantize
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ class BigMap < Struct.new(:name, :id, :value_type, :key_type); end
5
+
6
+ module Tools
7
+ class ConvertToHash < ActiveInteraction::Base
8
+ class BigMap < Base
9
+ def decode
10
+ ::TezosClient::BigMap.new(
11
+ var_name,
12
+ data[:int],
13
+ type[:args].second,
14
+ type[:args].first
15
+ )
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ module Tools
5
+ class ConvertToHash < ActiveInteraction::Base
6
+ class Bytes < Base
7
+ def decode
8
+ data[:bytes] || data[:string]
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ module Tools
5
+ class ConvertToHash < ActiveInteraction::Base
6
+ class Int < Base
7
+ def decode
8
+ data[:int].to_i
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ module Tools
5
+ class ConvertToHash < ActiveInteraction::Base
6
+ class Key < Base
7
+ include TezosClient::Crypto
8
+
9
+ def decode
10
+ if data.key?(:bytes)
11
+ if data[:bytes].start_with?("00")
12
+ encode_tz(:edpk, data[:bytes][2..-1])
13
+ elsif data[:bytes].start_with?("01")
14
+ encode_tz(:sppk, data[:bytes][2..-1])
15
+ elsif data[:bytes].start_with?("02")
16
+ encode_tz(:p2pk, data[:bytes][2..-1])
17
+ else
18
+ data[:bytes]
19
+ end
20
+ else
21
+ data[:string]
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ module Tools
5
+ class ConvertToHash < ActiveInteraction::Base
6
+ class List < Base
7
+ def decode
8
+ data.map do |elem|
9
+ TezosClient::Tools::ConvertToHash::Base.new(
10
+ data: elem,
11
+ type: elem_type
12
+ ).value
13
+ end
14
+ end
15
+
16
+ def elem_type
17
+ type[:args].first
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ module Tools
5
+ class ConvertToHash < ActiveInteraction::Base
6
+ class Map < Base
7
+ def decode
8
+ new_map = {}
9
+
10
+ data.each do |elem|
11
+ raise "Not a 'Map' type" unless elem[:prim] == "Elt"
12
+
13
+ key = TezosClient::Tools::ConvertToHash::Base.new(
14
+ data: elem[:args].first,
15
+ type: type[:args].first
16
+ ).value
17
+
18
+ value = TezosClient::Tools::ConvertToHash::Base.new(
19
+ data: elem[:args].second,
20
+ type: type[:args].second
21
+ ).value
22
+
23
+ new_map[key] = value
24
+ end
25
+
26
+ new_map
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ module Tools
5
+ class ConvertToHash < ActiveInteraction::Base
6
+ class Nat < Base
7
+ def decode
8
+ data[:int].to_i
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ module Tools
5
+ class ConvertToHash < ActiveInteraction::Base
6
+ class Pair < Base
7
+ def decode
8
+ raise "Not a 'Pair' type" unless data[:prim] == "Pair"
9
+ raise "Difference detected between data and type \nDATA: #{data} \nTYPE:#{type} " unless data[:args].size == type[:args].size
10
+
11
+ (data[:args]).zip(type[:args]).map do |data_n, type_n|
12
+ TezosClient::Tools::ConvertToHash::Base.new(
13
+ data: data_n,
14
+ type: type_n
15
+ ).value
16
+ end.reduce({}, &:merge)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ module Tools
5
+ class ConvertToHash < ActiveInteraction::Base
6
+ class Signature < Base
7
+ include TezosClient::Crypto
8
+
9
+ def decode
10
+ if data.key?(:bytes)
11
+ encode_tz(:edsig, data[:bytes])
12
+ else
13
+ data[:string]
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ module Tools
5
+ class ConvertToHash < ActiveInteraction::Base
6
+ class String < Base
7
+ def decode
8
+ data[:string]
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ module Tools
5
+ class ConvertToHash < ActiveInteraction::Base
6
+ class Timestamp < Base
7
+ def decode
8
+ if data.key? :int
9
+ Time.zone.at(data[:int].to_i)
10
+ elsif data.key? :string
11
+ Time.zone.parse(data[:string])
12
+ else
13
+ raise "Can not convert timestamp: #{data}"
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -7,50 +7,19 @@ class TezosClient::Tools::FindBigMapsInStorage < ActiveInteraction::Base
7
7
  strip: false
8
8
 
9
9
  def execute
10
- case storage_type[:prim]
11
- when "pair"
12
- pair_type(data: storage, type: storage_type)
13
- when "list"
14
- list_type(data: storage, type: storage_type)
15
- when "big_map"
16
- big_map_type(data: storage, type: storage_type)
17
- end
18
- end
19
-
20
- def pair_type(data:, type:)
21
- raise "Not a 'Pair' type" unless data[:prim] == "Pair"
22
- raise "Difference detected between data and type \nDATA: #{data} \nTYPE:#{type} " unless data[:args].size == type[:args].size
23
-
24
- (0 .. data[:args].size - 1).map do |iter|
25
- compose(
26
- TezosClient::Tools::FindBigMapsInStorage,
27
- storage: data[:args][iter],
28
- storage_type: type[:args][iter]
29
- )
30
- end.compact.flatten
10
+ hash_storage
11
+ .map(&:last)
12
+ .select { |value| value.is_a? TezosClient::BigMap }
13
+ .map(&:to_h)
14
+ .map(&:with_indifferent_access)
31
15
  end
32
16
 
33
- def list_type(data:, type:)
34
- element_type = type[:args].first
35
- data.map do |elem|
17
+ private
18
+ def hash_storage
36
19
  compose(
37
20
  TezosClient::Tools::ConvertToHash,
38
- data: elem,
39
- type: element_type
21
+ data: storage,
22
+ type: storage_type
40
23
  )
41
24
  end
42
- end
43
-
44
- def big_map_type(data:, type:)
45
- {
46
- name: var_name(type),
47
- id: data[:int],
48
- value_type: type[:args].second,
49
- key_type: type[:args].first
50
- }.with_indifferent_access
51
- end
52
-
53
- def var_name(type)
54
- "#{type[:annots].first[1..-1]}".to_sym
55
- end
56
25
  end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient::Tools::HashToMicheline < ActiveInteraction::Base
4
+ # TODO: handle Arrays and Maps
5
+ TYPES_MAPPING = {
6
+ int: :int,
7
+ nat: :int,
8
+ string: :string,
9
+ signature: :string,
10
+ bytes: :bytes,
11
+ timestamp: :int,
12
+ key: :string,
13
+ address: :string
14
+ }.freeze
15
+
16
+ string :contract_address, default: nil
17
+ string :entrypoint, default: nil
18
+ # example of params:
19
+ # {
20
+ # spending_ref: "toto",
21
+ # expires_at: Time.now
22
+ # }
23
+ hash :params, strip: false
24
+ hash :storage_type, strip: false, default: {}
25
+ interface :blockchain_client, methods: %i[entrypoint entrypoints], default: -> { TezosClient.new }
26
+
27
+ # if storage_type is not received, it is fetched from the blockchain using
28
+ # contract_address and entrypoint (that are mandatory in this case)
29
+ validate :storage_type_or_contract_address_presence
30
+
31
+ def execute
32
+ return hash_type_to_hash_data(_storage_type.fetch(:prim), params.values.first) if params.size == 1
33
+
34
+ { prim: "Pair", args: generate_micheline(_storage_type[:args]) }
35
+ end
36
+
37
+ private
38
+ def generate_micheline(remaining_storage_type)
39
+ remaining_storage_type.each_with_object([]) do |h, acc|
40
+ next acc << { prim: "Pair", args: generate_micheline(h[:args]) } if h[:prim] == "pair"
41
+
42
+ annot = h[:annots].first.slice(1..-1).to_sym # remove '%'
43
+
44
+ if h[:prim] == "option"
45
+ value = params.fetch(annot)
46
+ if value
47
+ acc << {
48
+ "prim": "Some",
49
+ "args": [
50
+ hash_type_to_hash_data(h[:args][0][:prim], params.fetch(annot))
51
+ ]
52
+ }
53
+ else
54
+ acc << {
55
+ "prim": "None"
56
+ }
57
+ end
58
+ else
59
+ acc << hash_type_to_hash_data(h[:prim], params.fetch(annot))
60
+ end
61
+ end
62
+ end
63
+
64
+ def convert_type(michelson_type)
65
+ TYPES_MAPPING.fetch(michelson_type.to_sym)
66
+ end
67
+
68
+ def hash_type_to_hash_data(michelson_type, value)
69
+ type = convert_type(michelson_type)
70
+
71
+ converted_value = case michelson_type.to_sym
72
+ when :nat, :int
73
+ value.to_s
74
+ when :timestamp
75
+ errors.add(:base, "timestamp input must be an instance of Time") unless value.is_a? Time
76
+
77
+ value.to_i.to_s
78
+ else
79
+ value
80
+ end
81
+
82
+ { type => converted_value }
83
+ end
84
+
85
+ def _entrypoint
86
+ @_entrypoint ||= select_entrypoint
87
+ end
88
+
89
+ def select_entrypoint
90
+ entrypoints = blockchain_client.entrypoints(contract_address)["entrypoints"].keys
91
+ if entrypoints.count == 0
92
+ "default"
93
+ elsif entrypoints.include?(entrypoint)
94
+ entrypoint
95
+ else
96
+ errors.add(:entrypoint, :not_found)
97
+ end
98
+ end
99
+
100
+ def _storage_type
101
+ (storage_type.presence || blockchain_client.entrypoint(contract_address, _entrypoint)).deep_symbolize_keys
102
+ end
103
+
104
+ def storage_type_or_contract_address_presence
105
+ return if storage_type.present? ^ (contract_address.present?)
106
+
107
+ errors.add(:base,
108
+ "You should provide the contract_address and the entrypoint only if storage_type is not provided")
109
+ end
110
+ end
@@ -5,10 +5,10 @@ class TezosClient
5
5
  module SystemCall
6
6
  def self.execute(cmd)
7
7
  Open3.popen3(*cmd) do |_stdin, stdout, stderr, wait_thr|
8
- err = stderr.read
9
8
  status = wait_thr.value.exitstatus
10
9
 
11
10
  if status != 0
11
+ err = stdout.read + stderr.read
12
12
  raise ::TezosClient::SysCallError, "command '#{cmd}' existed with status #{status}: #{err}"
13
13
  end
14
14
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class TezosClient
4
- VERSION = "1.1.4"
4
+ VERSION = "1.3.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tezos_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.4
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pierre Michard
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-03-04 00:00:00.000000000 Z
11
+ date: 2020-09-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -274,8 +274,23 @@ files:
274
274
  - lib/tezos_client/smartpy_inteface/smartpy_wrapper.rb
275
275
  - lib/tezos_client/smartpy_interface.rb
276
276
  - lib/tezos_client/string_utils.rb
277
+ - lib/tezos_client/tools/annots_to_type.rb
277
278
  - lib/tezos_client/tools/convert_to_hash.rb
279
+ - lib/tezos_client/tools/convert_to_hash/address.rb
280
+ - lib/tezos_client/tools/convert_to_hash/base.rb
281
+ - lib/tezos_client/tools/convert_to_hash/big_map.rb
282
+ - lib/tezos_client/tools/convert_to_hash/bytes.rb
283
+ - lib/tezos_client/tools/convert_to_hash/int.rb
284
+ - lib/tezos_client/tools/convert_to_hash/key.rb
285
+ - lib/tezos_client/tools/convert_to_hash/list.rb
286
+ - lib/tezos_client/tools/convert_to_hash/map.rb
287
+ - lib/tezos_client/tools/convert_to_hash/nat.rb
288
+ - lib/tezos_client/tools/convert_to_hash/pair.rb
289
+ - lib/tezos_client/tools/convert_to_hash/signature.rb
290
+ - lib/tezos_client/tools/convert_to_hash/string.rb
291
+ - lib/tezos_client/tools/convert_to_hash/timestamp.rb
278
292
  - lib/tezos_client/tools/find_big_maps_in_storage.rb
293
+ - lib/tezos_client/tools/hash_to_micheline.rb
279
294
  - lib/tezos_client/tools/system_call.rb
280
295
  - lib/tezos_client/tools/temporary_file.rb
281
296
  - lib/tezos_client/version.rb
@@ -286,7 +301,7 @@ licenses:
286
301
  - MIT
287
302
  metadata:
288
303
  allowed_push_host: https://rubygems.org
289
- post_install_message:
304
+ post_install_message:
290
305
  rdoc_options: []
291
306
  require_paths:
292
307
  - lib
@@ -301,8 +316,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
301
316
  - !ruby/object:Gem::Version
302
317
  version: '0'
303
318
  requirements: []
304
- rubygems_version: 3.0.6
305
- signing_key:
319
+ rubyforge_project:
320
+ rubygems_version: 2.7.6
321
+ signing_key:
306
322
  specification_version: 4
307
323
  summary: Wrapper to the tezos client.
308
324
  test_files: []