tezos_client 1.2.2 → 1.3.3

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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +2 -5
  4. data/Gemfile.lock +19 -19
  5. data/README.md +15 -36
  6. data/lib/tezos_client.rb +19 -2
  7. data/lib/tezos_client/commands.rb +3 -1
  8. data/lib/tezos_client/crypto.rb +20 -0
  9. data/lib/tezos_client/exceptions.rb +3 -0
  10. data/lib/tezos_client/logger.rb +1 -1
  11. data/lib/tezos_client/operation_mgr.rb +28 -29
  12. data/lib/tezos_client/rpc_interface/contracts.rb +4 -0
  13. data/lib/tezos_client/smartpy_interface.rb +10 -10
  14. data/lib/tezos_client/tools/annots_to_type.rb +38 -7
  15. data/lib/tezos_client/tools/convert_to_hash/base.rb +2 -2
  16. data/lib/tezos_client/tools/convert_to_hash/option.rb +20 -0
  17. data/lib/tezos_client/tools/hash_to_micheline.rb +20 -44
  18. data/lib/tezos_client/tools/hash_to_micheline/address.rb +14 -0
  19. data/lib/tezos_client/tools/hash_to_micheline/base.rb +52 -0
  20. data/lib/tezos_client/tools/hash_to_micheline/bytes.rb +14 -0
  21. data/lib/tezos_client/tools/hash_to_micheline/contract.rb +14 -0
  22. data/lib/tezos_client/tools/hash_to_micheline/int.rb +13 -0
  23. data/lib/tezos_client/tools/hash_to_micheline/key.rb +14 -0
  24. data/lib/tezos_client/tools/hash_to_micheline/nat.rb +13 -0
  25. data/lib/tezos_client/tools/hash_to_micheline/option.rb +23 -0
  26. data/lib/tezos_client/tools/hash_to_micheline/pair.rb +45 -0
  27. data/lib/tezos_client/tools/hash_to_micheline/signature.rb +14 -0
  28. data/lib/tezos_client/tools/hash_to_micheline/string.rb +14 -0
  29. data/lib/tezos_client/tools/hash_to_micheline/timestamp.rb +14 -0
  30. data/lib/tezos_client/tools/system_call.rb +1 -1
  31. data/lib/tezos_client/version.rb +1 -1
  32. data/travis-scripts/prepare-ubuntu.sh +6 -1
  33. metadata +16 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a1459f7dd534e408bd13520b14789b9f719a6cec9b004f5ab3ca00c151da1ac4
4
- data.tar.gz: 2aa7c36fc10c82537de03593e592703274daedcff36edec84130c98cc385098f
3
+ metadata.gz: 52e7b70d781942af2a66c3db62098d68541585a00afa73c09e24993d16e3c426
4
+ data.tar.gz: 8aa477e4e46f96584904c5d6dc625545dc6a606f69225969ac4b5efc472c5573
5
5
  SHA512:
6
- metadata.gz: fa9dc95f337c4bbbda410392c03d6305c93ee8f4dfec1810f629c0143a4beb3ce88789f5c4eaa809ae572bb4871ad44b0e87d35071727f9800cc9167f290492f
7
- data.tar.gz: e625840ea65befc0adc6ece5e334d8e869daabbaa2e6a9fc51881dda0ba3971dcec2f0d3274968e07a7e9268e1af93469c1f70f1395fa3d773119e871ab55f2a
6
+ metadata.gz: 0a1f456ca8177be6127e90da3c2ca964361180e2ca6b2f1d81f6875124ce9a1e8e8f512476d4470d9d0821078be5c42d1382bd5e761fed4f1a8ce95c0eb2c535
7
+ data.tar.gz: b9ec354b6fdba9e4f1eabe5265ffa68966c2035e11900b09ca2f7d501890fe2b7abb99a693b2b8cfe16df83453d8c677f27bc657f157622db4a5e3f02986ccb2
@@ -1 +1 @@
1
- 2.5.1
1
+ 2.6.5
@@ -13,11 +13,8 @@ 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
17
- - chmod +x ./SmartPy.sh
18
- - ./SmartPy.sh local-install $HOME/bin/smartpy
19
- - rm ./SmartPy.sh
20
- - export PATH="$PATH:$HOME/bin/:$HOME/bin/smartpy/SmartPyBasic/"
16
+ - curl -s https://smartpy.io/cli/install.sh > SmartPyInstaller.sh && yes | sh SmartPyInstaller.sh
17
+ - export PATH="$PATH:$HOME/bin/:$HOME/smartpy-cli/"
21
18
  - npm link michelson-to-micheline
22
19
  - gem install bundler -v 1.16.3
23
20
  script:
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- tezos_client (1.2.2)
4
+ tezos_client (1.3.3)
5
5
  active_interaction (~> 3.7)
6
6
  activesupport (~> 6.0.0)
7
7
  base58 (~> 0.2.3)
@@ -14,24 +14,24 @@ PATH
14
14
  GEM
15
15
  remote: https://rubygems.org/
16
16
  specs:
17
- actionpack (6.0.3.2)
18
- actionview (= 6.0.3.2)
19
- activesupport (= 6.0.3.2)
17
+ actionpack (6.0.3.3)
18
+ actionview (= 6.0.3.3)
19
+ activesupport (= 6.0.3.3)
20
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.3.2)
25
- activesupport (= 6.0.3.2)
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.2)
30
+ active_interaction (3.8.3)
31
31
  activemodel (>= 4, < 7)
32
- activemodel (6.0.3.2)
33
- activesupport (= 6.0.3.2)
34
- activesupport (6.0.3.2)
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)
@@ -44,7 +44,7 @@ GEM
44
44
  bip_mnemonic (0.0.4)
45
45
  builder (3.2.4)
46
46
  coderay (1.1.2)
47
- concurrent-ruby (1.1.6)
47
+ concurrent-ruby (1.1.7)
48
48
  crack (0.4.3)
49
49
  safe_yaml (~> 1.0.0)
50
50
  crass (1.0.6)
@@ -59,10 +59,10 @@ GEM
59
59
  httparty (0.17.3)
60
60
  mime-types (~> 3.0)
61
61
  multi_xml (>= 0.5.2)
62
- i18n (1.8.3)
62
+ i18n (1.8.5)
63
63
  concurrent-ruby (~> 1.0)
64
64
  jaro_winkler (1.5.3)
65
- loofah (2.6.0)
65
+ loofah (2.7.0)
66
66
  crass (~> 1.0.2)
67
67
  nokogiri (>= 1.5.9)
68
68
  method_source (0.9.2)
@@ -70,12 +70,12 @@ GEM
70
70
  mime-types-data (~> 3.2015)
71
71
  mime-types-data (3.2020.0512)
72
72
  mini_portile2 (2.4.0)
73
- minitest (5.14.1)
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.9)
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)
@@ -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.3.2)
96
- actionpack (= 6.0.3.2)
97
- activesupport (= 6.0.3.2)
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)
@@ -151,7 +151,7 @@ GEM
151
151
  addressable (>= 2.3.6)
152
152
  crack (>= 0.3.2)
153
153
  hashdiff (>= 0.4.0, < 2.0.0)
154
- zeitwerk (2.3.0)
154
+ zeitwerk (2.4.0)
155
155
 
156
156
  PLATFORMS
157
157
  ruby
data/README.md CHANGED
@@ -4,11 +4,12 @@
4
4
 
5
5
  [![Build Status](https://travis-ci.org/moneytrackio/tezos_client.svg?branch=master)](https://travis-ci.org/moneytrackio/tezos_client)
6
6
 
7
- Tezos Client interracts with tezos nodes using RPC commands.
7
+ Tezos Client interacts with Tezos nodes using RPC commands.
8
8
 
9
- ## Requirements:
10
- Tezos client requires liquidity to be installed in order to work properly.
11
- For installing on linux, you can basically follow the steps coded in travis-script folder.
9
+ ## Requirements
10
+
11
+ Tezos client requires SmartPy to be installed in order to work properly.
12
+ To install it on Linux, you can basically follow the steps coded in travis-script folder.
12
13
 
13
14
  ## Dependency
14
15
 
@@ -18,16 +19,12 @@ sudo apt-get install nodejs
18
19
  npm i -g michelson-to-micheline
19
20
  ```
20
21
 
21
- ### liquidity
22
- [liquidity installation](http://www.liquidity-lang.org/doc/installation/index.html)
23
-
24
- need the tezos version (not Dune version)
25
-
26
22
  ### SmartPy
27
23
  [SmartPy](https://smartpy.io/)
28
24
 
29
25
  ```bash
30
- sh <(curl -s https://SmartPy.io/SmartPyBasic/SmartPy.sh) local-install /
26
+ sh <(curl -s https://smartpy.io/dev-20200924-23b26494361d96abf034bdbb1ad1af396f95fd61/cli/SmartPy.sh) local-install-auto
27
+ export PATH=$PATH:$HOME/smartpy-cli/
31
28
  ```
32
29
 
33
30
  ### TypeScript (for dev)
@@ -53,9 +50,10 @@ Or install it yourself as:
53
50
 
54
51
  ## Usage
55
52
 
56
- ### Generate Tezos key paris
53
+ ### Generate Tezos key pairs
54
+
55
+ Generate a perfectly random key pair:
57
56
 
58
- Generate a prefectly random key pair:
59
57
  ```ruby
60
58
  client = TezosClient.new
61
59
  key = subject.generate_key
@@ -68,6 +66,7 @@ key = subject.generate_key
68
66
  ```
69
67
 
70
68
  Generate a key pair from a seed and a BIP 44 Path:
69
+
71
70
  ```ruby
72
71
  key = subject.generate_key(wallet_seed:"000102030405060708090a0b0c0d0e0f", path: "m/44'/1729'/0'/0'/0'")
73
72
  expect(key[:address]).to eq "tz1RfnzRopJXH32SSDap2wMYGULBAnmHxdP1"
@@ -77,7 +76,8 @@ key = subject.generate_key(wallet_seed:"000102030405060708090a0b0c0d0e0f", path:
77
76
  # :address=>"tz1a97x7GAvMDyrwwKTLQo131CoidXyUef48"
78
77
  # }
79
78
  ```
80
- Generate a key pair from a BIP-39 mnemonic sentence and a BIP 44 Path:
79
+ Generate a key pair from a BIP-39 mnemonic sentence and a BIP 44 Path:
80
+
81
81
  ```ruby
82
82
  key = subject.generate_key(
83
83
  mnemonic: "below dove cushion divide future artefact orange congress maple fiscal flower enable",
@@ -117,28 +117,6 @@ client.transfer(
117
117
  )
118
118
  ```
119
119
 
120
- ### Originate a contract written in liquidity
121
-
122
- ```ruby
123
- script = File.expand_path("./spec/fixtures/demo.py")
124
- source = "tz1ZWiiPXowuhN1UqNGVTrgNyf5tdxp4XUUq"
125
- secret_key = "edsk4EcqupPmaebat5mP57ZQ3zo8NDkwv8vQmafdYZyeXxrSc72pjN"
126
- amount = 0
127
- init_params= "MyContract()"
128
- client = TezosClient.new
129
-
130
- res = client.originate_contract(
131
- from: source,
132
- amount: amount,
133
- script: script,
134
- secret_key: secret_key,
135
- init_params: init_params
136
- )
137
-
138
- puts "Origination operation: #{res[:operation_id]}"
139
- puts "Contract address: #{res[:originated_contract]}"
140
- ```
141
-
142
120
  ### Originate a contract written in SmartPy
143
121
 
144
122
  ```ruby
@@ -146,7 +124,7 @@ script = File.expand_path("./spec/fixtures/demo.py")
146
124
  source = "tz1ZWiiPXowuhN1UqNGVTrgNyf5tdxp4XUUq"
147
125
  secret_key = "edsk4EcqupPmaebat5mP57ZQ3zo8NDkwv8vQmafdYZyeXxrSc72pjN"
148
126
  amount = 0
149
- init_params = "MyContract(1, 1)"
127
+ init_params= "MyContract()"
150
128
  client = TezosClient.new
151
129
 
152
130
  res = client.originate_contract(
@@ -162,6 +140,7 @@ puts "Contract address: #{res[:originated_contract]}"
162
140
  ```
163
141
 
164
142
  ### Call a contract written in SmartPy
143
+
165
144
  ```ruby
166
145
  TezosClient.new.call_contract(
167
146
  from: "tz1ZWiiPXowuhN1UqNGVTrgNyf5tdxp4XUUq",
@@ -165,14 +165,19 @@ class TezosClient
165
165
  end
166
166
 
167
167
  def call_contract(dry_run: false, entrypoint:, params:, params_type:, **args)
168
+ _entrypoint = select_entrypoint(
169
+ contract_address: args[:to],
170
+ entrypoint: entrypoint
171
+ )
172
+
168
173
  json_params = micheline_params(
169
174
  params: params,
170
- entrypoint: entrypoint,
175
+ entrypoint: _entrypoint,
171
176
  params_type: params_type
172
177
  )
173
178
 
174
179
  transfer_args = args.merge(
175
- entrypoint: entrypoint,
180
+ entrypoint: _entrypoint,
176
181
  parameters: json_params,
177
182
  dry_run: dry_run
178
183
  )
@@ -180,6 +185,18 @@ class TezosClient
180
185
  transfer(transfer_args)
181
186
  end
182
187
 
188
+ def select_entrypoint(contract_address:, entrypoint:)
189
+ entrypoints = entrypoints(contract_address)["entrypoints"].keys
190
+
191
+ if entrypoints.count == 0
192
+ "default"
193
+ elsif entrypoints.include?(entrypoint)
194
+ entrypoint
195
+ else
196
+ raise ::ArgumentError, "entrypoint #{entrypoint} not found in #{entrypoints}"
197
+ end
198
+ end
199
+
183
200
  def inject_raw_operations(secret_key:, raw_operations:, dry_run: false, **args)
184
201
  public_key = secret_key_to_public_key(secret_key)
185
202
  from = public_key_to_address(public_key)
@@ -25,6 +25,8 @@ class TezosClient
25
25
  :contract_big_maps,
26
26
  :block_operations,
27
27
  :contract_storage_type,
28
- :entrypoint
28
+ :entrypoint,
29
+ :entrypoints,
30
+ :select_entrypoint
29
31
  end
30
32
  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
 
@@ -13,7 +13,7 @@ class TezosClient
13
13
  self.class.logger << out + "\n"
14
14
  end
15
15
 
16
- FILTERED_KEYS = [:code, :contractCode]
16
+ FILTERED_KEYS = [:code, :contractCode, :contract_code]
17
17
  def tezos_contents_log_filter(content)
18
18
  if content.is_a? Array
19
19
  content.map { |el| tezos_contents_log_filter(el) }
@@ -202,40 +202,39 @@ class TezosClient
202
202
  end
203
203
 
204
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
205
214
 
206
- def ensure_applied!(rpc_responses)
207
- metadatas = rpc_responses.map { |response| response[:metadata] }
208
- operation_results = metadatas.map { |metadata| metadata[:operation_result] }
209
- internal_operations = metadatas.map { |metadata| metadata[:internal_operation_results] }.flatten.compact
210
- operation_results.concat(internal_operations.map { |internal_operation| internal_operation[:result] })
211
-
212
- failed = operation_results.detect do |operation_result|
213
- operation_result != nil && operation_result[:status] != "applied"
214
- end
215
+ return metadatas if failed.nil?
215
216
 
216
- return metadatas if failed.nil?
217
+ failed_operation_result = operation_results.detect do |operation_result|
218
+ operation_result[:status] == "failed"
219
+ end
217
220
 
218
- failed_operation_result = operation_results.detect do |operation_result|
219
- operation_result[:status] == "failed"
221
+ failed!("failed", failed_operation_result[:errors], operation_results)
220
222
  end
221
223
 
222
- failed!("failed", failed_operation_result[:errors], operation_results)
223
- end
224
-
225
- def exception_klass(errors)
226
- error = errors[0]
227
- case error[:id]
228
- when TezBalanceTooLow::FIRST_ERROR_REGEXP
229
- TezBalanceTooLow
230
- when ScriptRuntimeError::FIRST_ERROR_REGEXP
231
- ScriptRuntimeError
232
- else
233
- 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
234
234
  end
235
- end
236
235
 
237
- def failed!(status, errors, metadata)
238
- raise exception_klass(errors).new(metadata: metadata, errors: errors, status: status)
239
- end
236
+ def failed!(status, errors, metadata)
237
+ raise exception_klass(errors).new(metadata: metadata, errors: errors, status: status)
238
+ end
240
239
  end
241
- end
240
+ end
@@ -37,6 +37,10 @@ class TezosClient
37
37
  get "#{contract_link(contract_id)}/storage"
38
38
  end
39
39
 
40
+ def entrypoints(contract_id)
41
+ get("#{contract_link(contract_id)}/entrypoints")
42
+ end
43
+
40
44
  def entrypoint(contract_id, entrypoint)
41
45
  get("#{contract_link(contract_id)}/entrypoints/#{entrypoint}")
42
46
  end
@@ -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
@@ -11,6 +11,17 @@ class TezosClient::Tools::AnnotsToType < ActiveInteraction::Base
11
11
 
12
12
  validate :validate_types
13
13
 
14
+ TYPES_MAPPING = {
15
+ int: :int,
16
+ nat: :int,
17
+ string: :string,
18
+ signature: :string,
19
+ bytes: :bytes,
20
+ timestamp: :int,
21
+ key: :string,
22
+ address: :string
23
+ }.freeze
24
+
14
25
  def execute
15
26
  return { "prim" => typed_annots.values.first } if typed_annots.size == 1
16
27
 
@@ -18,30 +29,50 @@ class TezosClient::Tools::AnnotsToType < ActiveInteraction::Base
18
29
  end
19
30
 
20
31
  private
32
+ def micheline_type(annot_type, annot)
33
+ if annot_type.to_s.start_with?("optional_")
34
+ {
35
+ "prim" => "option",
36
+ "args" => [{ "prim" => annot_type.to_s.delete_prefix("optional_") }],
37
+ "annots" => ["%#{annot}"]
38
+ }
39
+ else
40
+ {
41
+ "prim" => annot_type,
42
+ "annots" => ["%#{annot}"]
43
+ }
44
+ end
45
+ end
46
+
21
47
  def generate_type_args(annots)
22
48
  annot = annots.pop
23
49
  annot_type = typed_annots[annot]
24
50
 
25
51
  unless annots.size == 1
26
- return [{ "prim" => "pair", "args" => generate_type_args(annots) },
27
- { "prim" => annot_type, "annots" => ["%#{annot}"] }]
52
+ return [
53
+ micheline_type(annot_type, annot),
54
+ {
55
+ "prim" => "pair",
56
+ "args" => generate_type_args(annots)
57
+ }
58
+ ]
28
59
  end
29
60
 
30
- generated_args = [{ "prim" => annot_type, "annots" => ["%#{annot}"] }]
61
+ generated_args = [micheline_type(annot_type, annot)]
31
62
  annot = annots.pop
32
63
  annot_type = typed_annots[annot]
33
- generated_args.unshift({ "prim" => annot_type, "annots" => ["%#{annot}"] })
64
+ generated_args.append(micheline_type(annot_type, annot))
34
65
 
35
66
  generated_args
36
67
  end
37
68
 
38
69
  def ordered_annots
39
- @ordered_annots ||= typed_annots.keys.sort
70
+ @ordered_annots ||= typed_annots.keys.sort.reverse
40
71
  end
41
72
 
42
73
  def validate_types
43
- allowed_types = TezosClient::Tools::HashToMicheline::TYPES_MAPPING.keys
44
- return if typed_annots.values.map(&:to_sym).all? { |type| type.in? allowed_types }
74
+ allowed_types = TYPES_MAPPING.keys
75
+ return if typed_annots.values.map{|type| type.to_s.delete_prefix("optional_").to_sym}.all? { |type| type.in? allowed_types }
45
76
 
46
77
  errors.add(:base, "The allowed types are: #{allowed_types.join(', ')}")
47
78
  end
@@ -22,7 +22,7 @@ class TezosClient
22
22
  type: type
23
23
  ).decode
24
24
 
25
- rescue NameError => e
25
+ rescue NameError
26
26
  raise NotImplementedError, "type '#{type[:prim]}' not implemented"
27
27
  end
28
28
 
@@ -42,7 +42,7 @@ class TezosClient
42
42
 
43
43
  private
44
44
  def klass
45
- "TezosClient::Tools::ConvertToHash::#{type[:prim].camelize}".constantize
45
+ "#{self.class.name.deconstantize}::#{type[:prim].camelize}".constantize
46
46
  end
47
47
  end
48
48
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ module Tools
5
+ class ConvertToHash < ActiveInteraction::Base
6
+ class Option < Base
7
+ def decode
8
+ if data[:prim] == "None"
9
+ return nil
10
+ elsif data[:prim] == "Some"
11
+ TezosClient::Tools::ConvertToHash::Base.new(
12
+ data: data[:args][0],
13
+ type: type[:args][0]
14
+ ).value
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,18 +1,11 @@
1
1
  # frozen_string_literal: true
2
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
3
+ require_relative "hash_to_micheline/base"
4
+
5
+ Dir[File.join(__dir__, "hash_to_micheline", "*.rb")].each { |file| require file }
6
+
15
7
 
8
+ class TezosClient::Tools::HashToMicheline < ActiveInteraction::Base
16
9
  string :contract_address, default: nil
17
10
  string :entrypoint, default: nil
18
11
  # example of params:
@@ -20,57 +13,40 @@ class TezosClient::Tools::HashToMicheline < ActiveInteraction::Base
20
13
  # spending_ref: "toto",
21
14
  # expires_at: Time.now
22
15
  # }
23
- hash :params, strip: false
16
+ interface :params
24
17
  hash :storage_type, strip: false, default: {}
25
- interface :blockchain_client, methods: [:entrypoint], default: -> { TezosClient.new }
18
+ interface :blockchain_client, methods: %i[entrypoint entrypoints select_entrypoint], default: -> { TezosClient.new }
26
19
 
27
20
  # if storage_type is not received, it is fetched from the blockchain using
28
21
  # contract_address and entrypoint (that are mandatory in this case)
29
22
  validate :storage_type_or_contract_address_presence
30
23
 
31
24
  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]) }
25
+ TezosClient::Tools::HashToMicheline::Base.new(data: _params, type: _storage_type).value
35
26
  end
36
27
 
37
28
  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
- acc << hash_type_to_hash_data(h[:prim], params.fetch(annot))
29
+ def _params
30
+ if params.respond_to?(:keys) && params.keys.size == 1 && !_storage_type.key?(:annots)
31
+ params.values.first
32
+ else
33
+ params
44
34
  end
45
35
  end
46
36
 
47
- def convert_type(michelson_type)
48
- TYPES_MAPPING.fetch(michelson_type.to_sym)
49
- end
50
-
51
- def hash_type_to_hash_data(michelson_type, value)
52
- type = convert_type(michelson_type)
53
-
54
- converted_value = case michelson_type.to_sym
55
- when :nat, :int
56
- value.to_s
57
- when :timestamp
58
- errors.add(:base, "timestamp input must be an instance of Time") unless value.is_a? Time
59
-
60
- value.to_i.to_s
61
- else
62
- value
63
- end
64
-
65
- { type => converted_value }
37
+ def _entrypoint
38
+ @_entrypoint ||= blockchain_client.select_entrypoint(
39
+ contract_address: contract_address,
40
+ entrypoint: entrypoint
41
+ )
66
42
  end
67
43
 
68
44
  def _storage_type
69
- (storage_type.presence || blockchain_client.entrypoint(contract_address, entrypoint)).deep_symbolize_keys
45
+ (storage_type.presence || blockchain_client.entrypoint(contract_address, _entrypoint)).deep_symbolize_keys
70
46
  end
71
47
 
72
48
  def storage_type_or_contract_address_presence
73
- return if storage_type.present? ^ (contract_address.present? && entrypoint.present?)
49
+ return if storage_type.present? ^ (contract_address.present?)
74
50
 
75
51
  errors.add(:base,
76
52
  "You should provide the contract_address and the entrypoint only if storage_type is not provided")
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ module Tools
5
+ class HashToMicheline < ActiveInteraction::Base
6
+ class Address < Base
7
+ def encode
8
+ raise "#{data} #{data.class} Not a 'String' type" unless data.is_a? ::String
9
+ { string: data }
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ module Tools
5
+ class HashToMicheline < 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
+ @data = anonymous? ? @data : @data.fetch(var_name)
16
+ encode
17
+ end
18
+
19
+ protected
20
+ def encode
21
+ klass.new(
22
+ data: data,
23
+ type: type
24
+ ).encode
25
+
26
+ rescue NameError
27
+ raise
28
+ raise NotImplementedError, "type '#{type[:prim]}' not implemented"
29
+ end
30
+
31
+ def anonymous?
32
+ !(type.key?(:annots) && type[:annots].any?)
33
+ end
34
+
35
+ def var_name_annot
36
+ type[:annots].first
37
+ end
38
+
39
+ def var_name
40
+ return nil if anonymous?
41
+
42
+ "#{var_name_annot[1..-1]}".to_sym
43
+ end
44
+
45
+ private
46
+ def klass
47
+ "#{self.class.name.deconstantize}::#{type[:prim].to_s.camelize}".constantize
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ module Tools
5
+ class HashToMicheline < ActiveInteraction::Base
6
+ class Bytes < Base
7
+ def encode
8
+ raise "#{data} #{data.class} Not a 'String' type" unless data.is_a? ::String
9
+ { bytes: data }
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ module Tools
5
+ class HashToMicheline < ActiveInteraction::Base
6
+ class Contract < Base
7
+ def encode
8
+ raise "#{data} #{data.class} Not a 'String' type" unless data.is_a? ::String
9
+ { string: data }
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ module Tools
5
+ class HashToMicheline < ActiveInteraction::Base
6
+ class Int < Base
7
+ def encode
8
+ { int: data.to_s }
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ module Tools
5
+ class HashToMicheline < ActiveInteraction::Base
6
+ class Key < Base
7
+ def encode
8
+ raise "#{data} #{data.class} Not a 'String' type" unless data.is_a? ::String
9
+ { string: data }
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ module Tools
5
+ class HashToMicheline < ActiveInteraction::Base
6
+ class Nat < Base
7
+ def encode
8
+ { int: data.to_s }
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ module Tools
5
+ class HashToMicheline < ActiveInteraction::Base
6
+ class Option < Base
7
+ def encode
8
+ return { prim: "None" } if data.nil?
9
+
10
+ {
11
+ prim: "Some",
12
+ args: [
13
+ TezosClient::Tools::HashToMicheline::Base.new(
14
+ data: data,
15
+ type: type[:args][0]
16
+ ).value
17
+ ]
18
+ }
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ module Tools
5
+ class HashToMicheline < ActiveInteraction::Base
6
+ class Pair < Base
7
+ def encode
8
+ {
9
+ prim: "Pair",
10
+ args: [
11
+ TezosClient::Tools::HashToMicheline::Base.new(
12
+ data: data_0,
13
+ type: type[:args][0]
14
+ ).value,
15
+ TezosClient::Tools::HashToMicheline::Base.new(
16
+ data: data_1,
17
+ type: type[:args][1]
18
+ ).value
19
+ ]
20
+ }
21
+ end
22
+
23
+ def data_0
24
+ if data.is_a? ::Array
25
+ data[0]
26
+ else
27
+ data
28
+ end
29
+ end
30
+
31
+ def data_1
32
+ if data.is_a? ::Array
33
+ if data.size > 2
34
+ data.drop(1)
35
+ else
36
+ data[1]
37
+ end
38
+ else
39
+ data
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ module Tools
5
+ class HashToMicheline < ActiveInteraction::Base
6
+ class Signature < Base
7
+ def encode
8
+ raise "#{data} does not seem to be a signature" unless data.starts_with?("edsig")
9
+ { string: data }
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ module Tools
5
+ class HashToMicheline < ActiveInteraction::Base
6
+ class String < Base
7
+ def encode
8
+ raise "#{data} #{data.class} Not a 'String' type" unless data.is_a? ::String
9
+ { string: data }
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ module Tools
5
+ class HashToMicheline < ActiveInteraction::Base
6
+ class Timestamp < Base
7
+ def encode
8
+ raise "timestamp input (#{data}) must be an instance of Time" unless data.is_a? Time
9
+ { int: data.to_i.to_s }
10
+ end
11
+ end
12
+ end
13
+ end
14
+ 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.2.2"
4
+ VERSION = "1.3.3"
5
5
  end
@@ -11,4 +11,9 @@ sudo add-apt-repository "deb http://fr.archive.ubuntu.com/ubuntu bionic main uni
11
11
  sudo apt-get update -qq
12
12
  sudo apt-get install -y -qq \
13
13
  libsecp256k1-dev libsecp256k1-0 libsodium-dev libssl-dev \
14
- bubblewrap libev-dev libhidapi-dev npm
14
+ bubblewrap libev-dev libhidapi-dev
15
+
16
+ wget -qO- https://deb.nodesource.com/setup_14.x | sudo -E bash -
17
+ sudo apt install -y nodejs
18
+ node --version
19
+ npm --version
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.2.2
4
+ version: 1.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pierre Michard
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-07-15 00:00:00.000000000 Z
11
+ date: 2021-01-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -285,12 +285,25 @@ files:
285
285
  - lib/tezos_client/tools/convert_to_hash/list.rb
286
286
  - lib/tezos_client/tools/convert_to_hash/map.rb
287
287
  - lib/tezos_client/tools/convert_to_hash/nat.rb
288
+ - lib/tezos_client/tools/convert_to_hash/option.rb
288
289
  - lib/tezos_client/tools/convert_to_hash/pair.rb
289
290
  - lib/tezos_client/tools/convert_to_hash/signature.rb
290
291
  - lib/tezos_client/tools/convert_to_hash/string.rb
291
292
  - lib/tezos_client/tools/convert_to_hash/timestamp.rb
292
293
  - lib/tezos_client/tools/find_big_maps_in_storage.rb
293
294
  - lib/tezos_client/tools/hash_to_micheline.rb
295
+ - lib/tezos_client/tools/hash_to_micheline/address.rb
296
+ - lib/tezos_client/tools/hash_to_micheline/base.rb
297
+ - lib/tezos_client/tools/hash_to_micheline/bytes.rb
298
+ - lib/tezos_client/tools/hash_to_micheline/contract.rb
299
+ - lib/tezos_client/tools/hash_to_micheline/int.rb
300
+ - lib/tezos_client/tools/hash_to_micheline/key.rb
301
+ - lib/tezos_client/tools/hash_to_micheline/nat.rb
302
+ - lib/tezos_client/tools/hash_to_micheline/option.rb
303
+ - lib/tezos_client/tools/hash_to_micheline/pair.rb
304
+ - lib/tezos_client/tools/hash_to_micheline/signature.rb
305
+ - lib/tezos_client/tools/hash_to_micheline/string.rb
306
+ - lib/tezos_client/tools/hash_to_micheline/timestamp.rb
294
307
  - lib/tezos_client/tools/system_call.rb
295
308
  - lib/tezos_client/tools/temporary_file.rb
296
309
  - lib/tezos_client/version.rb
@@ -316,7 +329,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
316
329
  - !ruby/object:Gem::Version
317
330
  version: '0'
318
331
  requirements: []
319
- rubygems_version: 3.0.4
332
+ rubygems_version: 3.0.3
320
333
  signing_key:
321
334
  specification_version: 4
322
335
  summary: Wrapper to the tezos client.