tezos_client 0.4.17 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.travis.yml +14 -7
  4. data/Gemfile.lock +4 -4
  5. data/README.md +64 -1
  6. data/lib/tezos_client/client_interface/block_contextual.rb +0 -16
  7. data/lib/tezos_client/client_interface/client_wrapper.rb +0 -37
  8. data/lib/tezos_client/client_interface/contract.rb +0 -17
  9. data/lib/tezos_client/client_interface/key.rb +0 -35
  10. data/lib/tezos_client/client_interface/misc.rb +0 -26
  11. data/lib/tezos_client/commands.rb +4 -10
  12. data/lib/tezos_client/crypto.rb +8 -4
  13. data/lib/tezos_client/exceptions.rb +9 -2
  14. data/lib/tezos_client/liquidity_inteface/liquidity_wrapper.rb +3 -17
  15. data/lib/tezos_client/liquidity_interface.rb +8 -33
  16. data/lib/tezos_client/operation_mgr.rb +63 -50
  17. data/lib/tezos_client/operations/transaction_operation.rb +9 -1
  18. data/lib/tezos_client/rpc_interface/blocks.rb +1 -1
  19. data/lib/tezos_client/rpc_interface/contracts.rb +7 -0
  20. data/lib/tezos_client/rpc_interface/helper.rb +4 -6
  21. data/lib/tezos_client/rpc_interface/operations.rb +14 -3
  22. data/lib/tezos_client/rpc_interface.rb +0 -1
  23. data/lib/tezos_client/smartpy_inteface/micheline_serializer_wrapper.rb +17 -0
  24. data/lib/tezos_client/smartpy_inteface/smartpy_wrapper.rb +20 -0
  25. data/lib/tezos_client/smartpy_interface.rb +46 -0
  26. data/lib/tezos_client/tools/system_call.rb +24 -0
  27. data/lib/tezos_client/tools/temporary_file.rb +30 -0
  28. data/lib/tezos_client/version.rb +1 -1
  29. data/lib/tezos_client.rb +75 -20
  30. data/travis-scripts/prepare-ubuntu.sh +1 -1
  31. metadata +7 -5
  32. data/lib/tezos_client/client_interface.rb +0 -25
  33. data/travis-scripts/install-liquidity.sh +0 -20
  34. data/travis-scripts/install-opam.sh +0 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 77e64070f9d16862fe3ea855743decfd8177b364643e52166efd959cac713997
4
- data.tar.gz: f6393e2ee64d200169e1ffa47c2b9bb65cf5cd1b5c3507d7bf9d812f6aa1daac
3
+ metadata.gz: 579469179cc4a5bb283d1bcf72b47ef45526cfa043b8e726c0f0fd28ce898660
4
+ data.tar.gz: 96812ca579b2385498b1d6d77ec0402b971e0cd5404a14358d82afef38d9a155
5
5
  SHA512:
6
- metadata.gz: 37f86d63ec3aab4310e6328ef7e5f4a9e935e693e98eef92ccdd640a562667692ed7850230a7014386f5030ee962cc8e74f1d3c6171c0b595f9b26000ddd41d7
7
- data.tar.gz: ca437a9e9a68bef967f10b5621582f10b28a3ee6ae5e23033509b950bd686a1b1a617188088a2eb0463fdfacd2fb9ad82bb4396b1726eb042f255b6854d75f61
6
+ metadata.gz: 2430302849c553bb54824fdb7f9f42f2370e382996217560895a4423fb7639d4ce3ed35bbeab04726385b156759ffff938912cb1d9d9c10f0a59fbc682e9e573
7
+ data.tar.gz: 9e998064917d3163aa589ba96bf7dbbb06cee177192d9487d17d5d181141634b951fb2c3c0c39358abe911efc21b7c44e9bbefc670171f0b613c8417a71abeed
data/.gitignore CHANGED
@@ -6,6 +6,7 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ node_modules/
9
10
 
10
11
  # rspec failure tracking
11
12
  .rspec_status
@@ -15,3 +16,4 @@
15
16
 
16
17
  # vim ctags
17
18
  .tags
19
+
data/.travis.yml CHANGED
@@ -1,4 +1,6 @@
1
1
  ---
2
+ service:
3
+ - docker
2
4
  dist: xenial
3
5
  language: ruby
4
6
  sudo: required
@@ -6,17 +8,22 @@ cache:
6
8
  bundler: true
7
9
  directories:
8
10
  - $HOME/.opam
9
- - liquidity
10
11
  rvm:
11
12
  - 2.5.1
12
- env:
13
- - OPAMYES=1
14
13
  before_install:
15
14
  - sh travis-scripts/prepare-ubuntu.sh
16
- - sh travis-scripts/install-opam.sh
17
- - sh travis-scripts/install-liquidity.sh
15
+ - mkdir $HOME/bin
16
+ - docker pull moneytrack/liquidity:babylonnet
17
+ - id=$(docker create moneytrack/liquidity:babylonnet)
18
+ - docker cp $id:/home/opam/.opam/4.07/bin/liquidity - > $HOME/bin/liquidity
19
+ - chmod +x liquidity
20
+ - curl -s https://SmartPy.io/SmartPyBasicTest2/SmartPy.sh > SmartPy.sh
21
+ - chmod +x ./SmartPy.sh
22
+ - ./SmartPy.sh local-install-custom SmartPyBasicTest2 $HOME/bin/smartpy
23
+ - rm ./SmartPy.sh
24
+ - export PATH="$PATH:$HOME/bin/:$HOME/bin/smartpy/SmartPyBasic/"
25
+ - npm link michelson-to-micheline
18
26
  - gem install bundler -v 1.16.3
19
27
  script:
20
- - eval `opam config env`
21
28
  - bundle install
22
- - bundle exec rake
29
+ - bundle exec rake
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- tezos_client (0.4.17)
4
+ tezos_client (1.0.0)
5
5
  activesupport (~> 6.0.0)
6
6
  base58 (~> 0.2.3)
7
7
  bip_mnemonic (~> 0.0.2)
@@ -47,7 +47,7 @@ GEM
47
47
  domain_name (0.5.20190701)
48
48
  unf (>= 0.0.5, < 1.0.0)
49
49
  erubi (1.9.0)
50
- ffi (1.12.2)
50
+ ffi (1.12.1)
51
51
  hashdiff (1.0.0)
52
52
  http-cookie (1.0.3)
53
53
  domain_name (~> 0.5)
@@ -65,7 +65,7 @@ GEM
65
65
  mime-types-data (~> 3.2015)
66
66
  mime-types-data (3.2019.1009)
67
67
  mini_portile2 (2.4.0)
68
- minitest (5.12.2)
68
+ minitest (5.13.0)
69
69
  money-tree (0.10.0)
70
70
  ffi
71
71
  multi_xml (0.6.0)
@@ -146,7 +146,7 @@ GEM
146
146
  addressable (>= 2.3.6)
147
147
  crack (>= 0.3.2)
148
148
  hashdiff (>= 0.4.0, < 2.0.0)
149
- zeitwerk (2.2.0)
149
+ zeitwerk (2.2.1)
150
150
 
151
151
  PLATFORMS
152
152
  ruby
data/README.md CHANGED
@@ -10,6 +10,31 @@ Tezos Client interracts with tezos nodes using RPC commands.
10
10
  Tezos client requires liquidity to be installed in order to work properly.
11
11
  For installing on linux, you can basically follow the steps coded in travis-script folder.
12
12
 
13
+ ## Dependency
14
+
15
+ ### michelson-to-micheline
16
+ ```bash
17
+ sudo apt-get install nodejs
18
+ npm i -g michelson-to-micheline
19
+ ```
20
+
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
+ ### SmartPy
27
+ [SmartPy](https://smartpy.io/)
28
+
29
+ ```bash
30
+ sh <(curl -s https://SmartPy.io/SmartPyBasic/SmartPy.sh) local-install /
31
+ ```
32
+
33
+ ### TypeScript (for dev)
34
+ ```bash
35
+ npm install -g typescript
36
+ ```
37
+
13
38
  ## Installation
14
39
 
15
40
  Add this line to your application's Gemfile:
@@ -122,10 +147,48 @@ TezosClient.new.call_contract(
122
147
  script: File.expand_path("./spec/fixtures/demo.liq"),
123
148
  secret_key: "edsk4EcqupPmaebat5mP57ZQ3zo8NDkwv8vQmafdYZyeXxrSc72pjN",
124
149
  to: "KT1STzq9p2tfW3K4RdoM9iYd1htJ4QcJ8Njs",
125
- parameters: [ "manage", "(Some { destination = tz1YLtLqD1fWHthSVHPD116oYvsd4PTAHUoc; amount = 1tz })" ]
150
+ entrypoint: "manage",
151
+ params: "(Some { destination = tz1YLtLqD1fWHthSVHPD116oYvsd4PTAHUoc; amount = 1tz })" ,
152
+ params_type: :caml
126
153
  )
127
154
  ```
128
155
 
156
+ ### Originate a contract written in SmartPy
157
+
158
+ ```ruby
159
+ script = File.expand_path("./spec/fixtures/demo.py")
160
+ source = "tz1ZWiiPXowuhN1UqNGVTrgNyf5tdxp4XUUq"
161
+ secret_key = "edsk4EcqupPmaebat5mP57ZQ3zo8NDkwv8vQmafdYZyeXxrSc72pjN"
162
+ amount = 0
163
+ init_params = "MyContract(1, 1)"
164
+ client = TezosClient.new
165
+
166
+ res = client.originate_contract(
167
+ from: source,
168
+ amount: amount,
169
+ script: script,
170
+ secret_key: secret_key,
171
+ init_params: init_params
172
+ )
173
+
174
+ puts "Origination operation: #{res[:operation_id]}"
175
+ puts "Contract address: #{res[:originated_contract]}"
176
+ ```
177
+
178
+ ### Call a contract written in SmartPy
179
+ ```ruby
180
+ TezosClient.new.call_contract(
181
+ from: "tz1ZWiiPXowuhN1UqNGVTrgNyf5tdxp4XUUq",
182
+ secret_key: "edsk4EcqupPmaebat5mP57ZQ3zo8NDkwv8vQmafdYZyeXxrSc72pjN",
183
+ to: "KT1STzq9p2tfW3K4RdoM9iYd1htJ4QcJ8Njs",
184
+ amount: 0,
185
+ entrypoint: "myEntryPoint",
186
+ params: { int: 1 },
187
+ params_type: :micheline
188
+ )
189
+ ```
190
+
191
+
129
192
  ## Options
130
193
 
131
194
  ### Liquidity options
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class TezosClient
4
- class ClientInterface
5
- # Commands managing keys and accounts
6
- module BlockContextual
7
- def transfer(quantity:, from:, to:, dry_run: false, arg: nil)
8
- cmd = ["transfer", quantity, "from", from, "to", to]
9
- cmd << "--dry-run" if dry_run
10
- cmd += ["--arg", "#{arg}"] unless arg.nil?
11
-
12
- res = call_client(cmd)
13
- end
14
- end
15
- end
16
- end
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class TezosClient
4
- class ClientInterface
5
- # Wrapper used to call the tezos-client binary
6
- module ClientWrapper
7
- def call_client(command)
8
- cmd = client_cmd + command
9
- Open3.popen3(*cmd) do |_stdin, stdout, stderr, wait_thr|
10
- err = stderr.read
11
- status = wait_thr.value.exitstatus
12
-
13
- if status != 0
14
- raise "command '#{cmd}' existed with status #{status}: #{err}"
15
- end
16
-
17
- log err
18
- output = stdout.read
19
-
20
- if block_given?
21
- yield(output)
22
- else
23
- output
24
- end
25
- end
26
- end
27
-
28
- def client_cmd
29
- res = ["tezos-client", "-l"]
30
- if config_file
31
- res += ["-c", config_file]
32
- end
33
- res
34
- end
35
- end
36
- end
37
- end
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class TezosClient
4
- class ClientInterface
5
- # Commands managing keys and accounts
6
- module Contract
7
- def known_contracts
8
- res = call_client(["list", "known", "contracts"])
9
- res.lines.reduce({}) do |acc, contract_output|
10
- address_format = /([^:]+): (\w+)/
11
- res = address_format.match(contract_output)
12
- acc.merge(res[1] => res[2])
13
- end
14
- end
15
- end
16
- end
17
- end
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class TezosClient
4
- class ClientInterface
5
- # Commands managing keys and accounts
6
- module Key
7
- def gen_keys(name)
8
- call_client(["gen", "keys", name])
9
- end
10
-
11
- def addresses
12
- output = call_client(["list", "known", "addresses"])
13
- output.lines.reduce({}) do |acc, address_output|
14
- address_format = /([^:]+): (\w+) /
15
- res = address_format.match(address_output)
16
- acc.merge(res[1] => res[2])
17
- end
18
- end
19
-
20
- def import_public_key(name, public_key, force: false)
21
- cmd = ["import", "public", "key", name, public_key]
22
- cmd << "--force" if force
23
-
24
- call_client(cmd)
25
- end
26
-
27
- def import_secret_key(name, secret_key, force: false)
28
- cmd = ["import", "secret", "key", name, secret_key]
29
- cmd << "--force" if force
30
-
31
- call_client(cmd)
32
- end
33
- end
34
- end
35
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "open3"
4
- require "date"
5
-
6
- class TezosClient
7
- class ClientInterface
8
- module Misc
9
- def bootstrapped
10
- call_client(["bootstrapped"]) do |output|
11
- output_format = /Current head: ([^ ]+) \(timestamp: ([^,]+), validation: (.+)\)/
12
- res = output_format.match(output)
13
- head = res[1]
14
- timestamp = DateTime.parse(res[2])
15
- validation = DateTime.parse(res[3])
16
-
17
- {
18
- "block" => head,
19
- "timestamp" => timestamp,
20
- "validated_at" => validation
21
- }
22
- end
23
- end
24
- end
25
- end
26
- end
@@ -18,18 +18,12 @@ class TezosClient
18
18
  :balance,
19
19
  :contract_manager_key,
20
20
  :contract_storage,
21
- :pending_operations
22
-
23
- def_delegators :client_interface,
24
- :gen_keys,
25
- :addresses,
26
- :import_public_key,
27
- :import_secret_key,
28
- :known_contracts,
29
- :transfer
21
+ :pending_operations,
22
+ :pack_data,
23
+ :big_map_value
30
24
 
31
25
  def_delegators :liquidity_interface,
32
26
  :get_storage,
33
- :pack_data
27
+ :liquidity_pack_data
34
28
  end
35
29
  end
@@ -15,6 +15,7 @@ class TezosClient
15
15
  tz2: [6, 161, 161],
16
16
  tz3: [6, 161, 164],
17
17
  KT: [2, 90, 121],
18
+ expr: [13, 44, 64, 27],
18
19
  edpk: [13, 15, 37, 217],
19
20
  edsk2: [13, 15, 58, 7],
20
21
  spsk: [17, 162, 224, 201],
@@ -112,10 +113,7 @@ class TezosClient
112
113
  def generate_key(mnemonic: nil, password: nil, wallet_seed: nil, path: nil)
113
114
  signing_key = generate_signing_key(mnemonic: mnemonic, password: password, wallet_seed: wallet_seed, path: path).to_bytes.to_hex
114
115
 
115
- verify_key = RbNaCl::SigningKey.new(signing_key.to_bin)
116
- hex_pubkey = verify_key.to_s.to_hex
117
-
118
- secret_key = encode_tz(:edsk, signing_key + hex_pubkey)
116
+ secret_key = encode_tz(:edsk2, signing_key)
119
117
  public_key = secret_key_to_public_key(secret_key)
120
118
  address = public_key_to_address(public_key)
121
119
 
@@ -199,6 +197,12 @@ class TezosClient
199
197
  key.merge(activation_secret: wallet_config[:secret])
200
198
  end
201
199
 
200
+ def encode_script_expr(data:, type:)
201
+ packed_key = pack_data(data: data, type: type)
202
+ raw_expr_key = RbNaCl::Hash::Blake2b.digest(packed_key["packed"].to_bin, digest_size: 32).to_hex
203
+ encode_tz(:expr, raw_expr_key)
204
+ end
205
+
202
206
  private
203
207
  def generate_signing_key(mnemonic: nil, password: nil, wallet_seed: nil, path: nil)
204
208
  if mnemonic
@@ -18,6 +18,15 @@ class TezosClient
18
18
  end
19
19
  end
20
20
 
21
+ class SysCallError < RuntimeError
22
+ end
23
+
24
+ class LiquidityError < SysCallError
25
+ end
26
+
27
+ class SmartPyError < SysCallError
28
+ end
29
+
21
30
  class InvalidActivation < RpcRequestFailure
22
31
  attr_reader :pkh
23
32
 
@@ -101,6 +110,4 @@ class TezosClient
101
110
  super
102
111
  end
103
112
  end
104
-
105
- class UnknownTransactionError < Exception; end
106
113
  end
@@ -6,24 +6,10 @@ class TezosClient
6
6
  module LiquidityWrapper
7
7
  def call_liquidity(command, verbose: false)
8
8
  cmd = liquidity_cmd(verbose: verbose).concat command
9
- log cmd.to_s
10
- Open3.popen3(*cmd) do |_stdin, stdout, stderr, wait_thr|
11
- err = stderr.read
12
- status = wait_thr.value.exitstatus
13
- log err
14
9
 
15
- if status != 0
16
- raise "command '#{cmd}' existed with status #{status}: #{err}"
17
- end
18
-
19
- output = stdout.read
20
-
21
- if block_given?
22
- yield(output)
23
- else
24
- output
25
- end
26
- end
10
+ Tools::SystemCall.execute(cmd)
11
+ rescue SysCallError => e
12
+ raise LiquidityError, e.message
27
13
  end
28
14
 
29
15
  def liquidity_cmd(verbose:)
@@ -28,7 +28,7 @@ class TezosClient
28
28
  init_params = args.fetch :init_params
29
29
  init_params = format_params(init_params)
30
30
 
31
- with_tempfile(".json") do |json_file|
31
+ Tools::TemporaryFile.with_tempfile(".json") do |json_file|
32
32
  cmd_opt = ["--source", from.to_s,"--json", script.to_s, "-o", json_file.path.to_s, "--init-storage"] + init_params
33
33
 
34
34
  call_liquidity cmd_opt, verbose: options[:verbose]
@@ -36,34 +36,8 @@ class TezosClient
36
36
  end
37
37
  end
38
38
 
39
- def with_tempfile(extension)
40
- file = Tempfile.new(["script", extension])
41
- yield(file)
42
-
43
- ensure
44
- file.unlink
45
- end
46
-
47
- def with_file_copy(source_file_path)
48
- source_file = File.open(source_file_path, "r")
49
- source_extention = File.extname(source_file_path)
50
-
51
- file_copy_path = nil
52
-
53
- res = with_tempfile(source_extention) do |file_copy|
54
- file_copy.write(source_file.read)
55
- file_copy_path = file_copy.path
56
- file_copy.close
57
- yield(file_copy_path)
58
- end
59
-
60
- res
61
- ensure
62
- File.delete(file_copy_path) if File.exists? file_copy_path
63
- end
64
-
65
- def json_scripts(args)
66
- with_file_copy(args[:script]) do |script_copy_path|
39
+ def json_scripts(script:)
40
+ Tools::TemporaryFile.with_file_copy(script) do |script_copy_path|
67
41
  script_basename = script_copy_path.sub(/.liq$/, "")
68
42
 
69
43
  json_init_script_path = "#{script_basename}.initializer.tz.json"
@@ -96,7 +70,7 @@ class TezosClient
96
70
 
97
71
  def origination_script(args)
98
72
  storage = initial_storage(args)
99
- _json_init_script, json_contract_script = json_scripts(args)
73
+ _json_init_script, json_contract_script = json_scripts(script: args[:script])
100
74
 
101
75
  {
102
76
  code: json_contract_script,
@@ -129,15 +103,16 @@ class TezosClient
129
103
  res.strip
130
104
  end
131
105
 
132
- def call_parameters(script:, parameters:)
106
+ def call_parameters(script:, entrypoint:, parameters:)
133
107
  params = format_params parameters
134
- with_tempfile(".json") do |json_file|
108
+ Tools::TemporaryFile.with_tempfile(".json") do |json_file|
109
+ params = [ entrypoint ] + params
135
110
  res = call_liquidity ["--json", "-o", "#{json_file.path}", "#{script}", "--data"] + params
136
111
  JSON.parse res
137
112
  end
138
113
  end
139
114
 
140
- def pack_data(data:, type:)
115
+ def liquidity_pack_data(data:, type:)
141
116
  res = call_liquidity ["--pack", "#{data}", "#{type}"]
142
117
  res.strip
143
118
  end
@@ -39,6 +39,10 @@ class TezosClient
39
39
  @protocol ||= rpc_interface.protocol
40
40
  end
41
41
 
42
+ def chain_id
43
+ @chain_id ||= rpc_interface.chain_id
44
+ end
45
+
42
46
  def simulate_and_update_limits
43
47
  run_result = run
44
48
 
@@ -47,13 +51,21 @@ class TezosClient
47
51
  rpc_operation_args[:gas_limit] = (operation_result[:consumed_gas].to_i.from_satoshi + 0.001).to_satoshi.to_s
48
52
  end
49
53
  end
54
+
55
+ run_result
50
56
  end
51
57
 
52
58
  def to_hex
53
59
  rpc_interface.forge_operations(operations: rpc_operation_args, branch: branch)
54
60
  end
55
61
 
62
+ def valid_secret_key?
63
+ @secret_key&.match?(/^edsk/)
64
+ end
65
+
56
66
  def sign
67
+ raise ArgumentError, "Invalid secret key" unless valid_secret_key?
68
+
57
69
  sign_operation(
58
70
  secret_key: @secret_key,
59
71
  operation_hex: to_hex
@@ -80,52 +92,73 @@ class TezosClient
80
92
 
81
93
  def simulate
82
94
  # simulate operations and adjust gas limits
83
- simulate_and_update_limits
84
- res = preapply
85
- operation_results = res.map { |metadata| metadata[:operation_result] }
86
-
87
- {
88
- operation_results: operation_results,
89
- }
95
+ run_result = simulate_and_update_limits
96
+ if valid_secret_key?
97
+ preapply
98
+ else
99
+ run_result
100
+ end
90
101
  end
91
102
 
92
103
  def test_and_broadcast
93
104
  simulate_res = simulate
94
105
  op_id = broadcast
95
106
 
96
- {
107
+ simulate_res.merge(
97
108
  operation_id: op_id,
98
- operation_results: simulate_res[:operation_results],
99
- }
109
+ )
100
110
  end
101
111
 
102
112
  def run
103
113
  rpc_responses = rpc_interface.run_operations(
104
114
  operations: rpc_operation_args,
105
- signature: base_58_signature,
106
- branch: branch)
107
-
108
- total_consumed_storage = 0
109
- total_consumed_gas = 0
115
+ signature: RANDOM_SIGNATURE,
116
+ branch: branch,
117
+ chain_id: chain_id
118
+ )
110
119
 
111
120
  ensure_applied!(rpc_responses)
112
121
 
113
- operation_results = rpc_responses.map do |rpc_response|
114
- metadata = rpc_response[:metadata]
122
+ convert_rpc_response(rpc_responses)
123
+ end
115
124
 
116
- total_consumed_storage += compute_consumed_storage(metadata)
117
- consumed_gas = compute_consumed_gas(metadata)
118
- total_consumed_gas += consumed_gas
125
+ def convert_rpc_response(rpc_responses)
126
+ converted_rpc_responce = {
127
+ status: :applied,
128
+ operation_results: operation_results(rpc_responses),
129
+ internal_operation_results: internal_operation_result(rpc_responses)
130
+ }
131
+
132
+ converted_rpc_responce.merge(consumed_tez(rpc_responses))
133
+ end
119
134
 
120
- metadata[:operation_result][:consumed_gas] = consumed_gas if metadata.key? :operation_result
135
+ def operation_results(rpc_responses)
136
+ rpc_responses.map do |rpc_response|
137
+ metadata = rpc_response[:metadata]
138
+ metadata[:operation_result][:consumed_gas] = compute_consumed_gas(metadata) if metadata.key? :operation_result
121
139
  metadata[:operation_result]
122
140
  end
141
+ end
142
+
143
+ def internal_operation_result(rpc_responses)
144
+ rpc_responses.map do |rpc_response|
145
+ rpc_response[:metadata][:internal_operation_results]
146
+ end.compact.flatten
147
+ end
148
+
149
+ def consumed_tez(rpc_responses)
150
+ total_consumed_storage = 0
151
+ total_consumed_gas = 0
152
+
153
+ rpc_responses.each do |rpc_response|
154
+ metadata = rpc_response[:metadata]
155
+ total_consumed_storage += compute_consumed_storage(metadata)
156
+ total_consumed_gas += compute_consumed_gas(metadata)
157
+ end
123
158
 
124
159
  {
125
- status: :applied,
126
- consumed_gas: total_consumed_gas,
127
160
  consumed_storage: total_consumed_storage,
128
- operation_results: operation_results
161
+ consumed_gas: total_consumed_gas
129
162
  }
130
163
  end
131
164
 
@@ -144,7 +177,6 @@ class TezosClient
144
177
  (metadata.dig(:operation_result, :paid_storage_size_diff) || "0").to_i.from_satoshi
145
178
  end
146
179
 
147
-
148
180
  def preapply
149
181
  rpc_responses = rpc_interface.preapply_operations(
150
182
  operations: rpc_operation_args,
@@ -153,6 +185,8 @@ class TezosClient
153
185
  branch: branch)
154
186
 
155
187
  ensure_applied!(rpc_responses)
188
+
189
+ convert_rpc_response(rpc_responses)
156
190
  end
157
191
 
158
192
  def broadcast
@@ -168,39 +202,18 @@ class TezosClient
168
202
 
169
203
  def ensure_applied!(rpc_responses)
170
204
  metadatas = rpc_responses.map { |response| response[:metadata] }
171
- operation_full_results = metadatas.map do |metadata|
172
- { op_result: metadata[:operation_result], internal_operation_results: metadata[:internal_operation_results]}
173
- end
205
+ operation_results = metadatas.map { |metadata| metadata[:operation_result] }
174
206
 
175
- failed = operation_full_results.map{|op_res| op_res[:op_result]}.detect do |operation_result|
207
+ failed = operation_results.detect do |operation_result|
176
208
  operation_result != nil && operation_result[:status] != "applied"
177
209
  end
178
210
 
179
211
  return metadatas if failed.nil?
180
212
 
181
- process_transaction_errors!(operation_full_results)
182
- end
183
-
184
- def process_transaction_errors!(operations_full_results)
185
- detect_operation = ->(op_full_results, status: "failed") do
186
- op_full_results.detect do |operation_result|
187
- operation_result[:op_result][:status] == status
188
- end
189
- end
190
-
191
- failed_operation_result = detect_operation.call(operations_full_results)
192
-
193
- if failed_operation_result.nil?
194
- backtracked_operation_result = detect_operation.call(operations_full_results, status: "backtracked")
195
- raise UnknownTransactionError if backtracked_operation_result.nil?
196
-
197
- internal_operations_results = backtracked_operation_result[:internal_operation_results].map{|op| op[:result]}
198
- failed_operation_result = internal_operations_results.detect { |op_result| op_result[:status] == "failed" }
199
- else
200
- failed_operation_result = failed_operation_result[:op_result]
213
+ failed_operation_result = operation_results.detect do |operation_result|
214
+ operation_result[:status] == "failed"
201
215
  end
202
216
 
203
- operation_results = operations_full_results.map { |full_result| full_result[:op_result] }
204
217
  failed!("failed", failed_operation_result[:errors], operation_results)
205
218
  end
206
219
 
@@ -32,7 +32,15 @@ class TezosClient
32
32
  end
33
33
 
34
34
  def parameters
35
- (@args[:parameters].is_a? String) ? encode_args(@args[:parameters]) : @args[:parameters]
35
+ parameters = @args[:parameters].clone
36
+ if parameters.is_a? String
37
+ {
38
+ entrypoint: "default",
39
+ value: encode_args(@args[:parameters])
40
+ }
41
+ else
42
+ parameters
43
+ end
36
44
  end
37
45
  end
38
46
  end
@@ -11,7 +11,7 @@ class TezosClient
11
11
  query = {
12
12
  length: length,
13
13
  head: head,
14
- min_date: min_date&.utc&.iso8601
14
+ min_date: min_date&.to_i
15
15
  }.compact
16
16
 
17
17
  res = get "chains/main/blocks/", query: query
@@ -3,6 +3,7 @@
3
3
  class TezosClient
4
4
  class RpcInterface
5
5
  using CurrencyUtils
6
+ include Crypto
6
7
 
7
8
  module Contracts
8
9
  def contract_link(contract_id)
@@ -26,6 +27,12 @@ class TezosClient
26
27
  def contract_storage(contract_id)
27
28
  get "#{contract_link(contract_id)}/storage"
28
29
  end
30
+
31
+ def big_map_value(big_map_id:, key:, type_key:)
32
+ expr_key = encode_script_expr(data: key, type: type_key)
33
+
34
+ get "/chains/main/blocks/head/context/big_maps/#{big_map_id}/#{expr_key}"
35
+ end
29
36
  end
30
37
  end
31
38
  end
@@ -16,24 +16,22 @@ class TezosClient
16
16
  counter: counter(args).to_s,
17
17
  fee: args.fetch(:fee, 0.05).to_satoshi.to_s
18
18
  }
19
- operation[:parameters] = args[:parameters] if args[:parameters]
19
+
20
+ if args[:parameters]
21
+ operation[:parameters] = args[:parameters]
22
+ end
20
23
  operation
21
24
  end
22
25
 
23
26
  def origination_operation(args)
24
- manager = (args.fetch(:manager) { args.fetch(:from) })
25
-
26
27
  operation = {
27
28
  kind: "origination",
28
- delegatable: args.fetch(:delegatable, false),
29
- spendable: args.fetch(:spendable, false),
30
29
  balance: args.fetch(:amount, 0).to_satoshi.to_s,
31
30
  source: args.fetch(:from),
32
31
  gas_limit: args.fetch(:gas_limit, 0.1).to_satoshi.to_s,
33
32
  storage_limit: args.fetch(:storage_limit, 0.06).to_satoshi.to_s,
34
33
  counter: counter(args).to_s,
35
34
  fee: args.fetch(:fee, 0.05).to_satoshi.to_s,
36
- manager_pubkey: manager
37
35
  }
38
36
 
39
37
  operation[:script] = args[:script] if args[:script]
@@ -20,9 +20,12 @@ class TezosClient
20
20
 
21
21
  def run_operations(operations:, **options)
22
22
  content = {
23
- branch: options.fetch(:branch),
24
- contents: operations,
25
- signature: options.fetch(:signature)
23
+ operation: {
24
+ branch: options.fetch(:branch),
25
+ contents: operations,
26
+ signature: options.fetch(:signature)
27
+ },
28
+ chain_id: options.fetch(:chain_id)
26
29
  }
27
30
  res = post("chains/main/blocks/head/helpers/scripts/run_operation", content)
28
31
  res["contents"]
@@ -43,6 +46,14 @@ class TezosClient
43
46
  def pending_operations
44
47
  get("chains/main/mempool/pending_operations")
45
48
  end
49
+
50
+ def pack_data(data:, type:)
51
+ content = {
52
+ data: data,
53
+ type: type
54
+ }
55
+ post("/chains/main/blocks/head/helpers/scripts/pack_data", content)
56
+ end
46
57
  end
47
58
  end
48
59
  end
@@ -26,6 +26,5 @@ class TezosClient
26
26
  @host = host
27
27
  @port = port
28
28
  end
29
-
30
29
  end
31
30
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ class SmartpyInterface
5
+ module MichelineSerializerWrapper
6
+ def convert_michelson_to_micheline(script)
7
+ cmd = ["michelson-to-micheline", script]
8
+
9
+ Tools::SystemCall.execute(cmd)
10
+ end
11
+
12
+ def actual_project_path
13
+ TezosClient.root_path
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ class SmartpyInterface
5
+ # Wrapper used to call the tezos-client binary
6
+ module SmartpyWrapper
7
+ def call_smartpy(command)
8
+ cmd = smartpy_cmd + command
9
+
10
+ Tools::SystemCall.execute(cmd)
11
+ rescue SysCallError => e
12
+ raise SmartPyError, e.to_s
13
+ end
14
+
15
+ def smartpy_cmd
16
+ [ "SmartPy.sh" ]
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "smartpy_inteface/smartpy_wrapper"
4
+ require_relative "smartpy_inteface/micheline_serializer_wrapper"
5
+
6
+ class TezosClient
7
+ class SmartpyInterface
8
+ include Logger
9
+ include SmartpyWrapper
10
+ include MichelineSerializerWrapper
11
+
12
+ attr_reader :options
13
+
14
+ def json_scripts(args)
15
+ compile_to_michelson(args) do |contract_script_filename, init_script_filename|
16
+ micheline_contract = File.read(contract_script_filename)
17
+ micheline_storage = convert_michelson_to_micheline(File.read(init_script_filename))
18
+
19
+ [JSON.parse(micheline_storage), JSON.parse(micheline_contract)]
20
+ end
21
+ end
22
+
23
+ def origination_script(args)
24
+ json_init_script, json_contract_script = json_scripts(args)
25
+
26
+ {
27
+ code: json_contract_script,
28
+ storage: json_init_script
29
+ }
30
+ end
31
+
32
+ private
33
+
34
+ def compile_to_michelson(args)
35
+ Tools::TemporaryFile.with_file_copy(args[:script]) do |script_copy_path|
36
+ script_basename = script_copy_path.split("/").last.sub(/.py$/, "")
37
+ script_path = "/tmp/#{script_basename}/"
38
+ init_script_filename = "contractStorage.tz"
39
+ contract_script_filename = "contractCode.tz.json"
40
+ call_smartpy ["local-compile", script_copy_path, args[:init_params], script_path]
41
+
42
+ yield(script_path + contract_script_filename, script_path + init_script_filename)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,24 @@
1
+ class TezosClient
2
+ module Tools
3
+ module SystemCall
4
+ def self.execute(cmd)
5
+ Open3.popen3(*cmd) do |_stdin, stdout, stderr, wait_thr|
6
+ err = stderr.read
7
+ status = wait_thr.value.exitstatus
8
+
9
+ if status != 0
10
+ raise ::TezosClient::SysCallError, "command '#{cmd}' existed with status #{status}: #{err}"
11
+ end
12
+
13
+ output = stdout.read
14
+
15
+ if block_given?
16
+ yield(output)
17
+ else
18
+ output
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,30 @@
1
+ class TezosClient
2
+ module Tools
3
+ module TemporaryFile
4
+ def self.with_file_copy(source_file_path)
5
+ source_file = File.open(source_file_path, "r")
6
+ source_extention = File.extname(source_file_path)
7
+
8
+ file_copy_path = nil
9
+
10
+ res = Tools::TemporaryFile.with_tempfile(source_extention) do |file_copy|
11
+ file_copy.write(source_file.read)
12
+ file_copy_path = file_copy.path
13
+ file_copy.close
14
+ yield(file_copy_path)
15
+ end
16
+
17
+ res
18
+ ensure
19
+ File.delete(file_copy_path) if File.exists? file_copy_path
20
+ end
21
+
22
+ def self.with_tempfile(extension)
23
+ file = Tempfile.new(["script", extension])
24
+ yield(file)
25
+ ensure
26
+ file.unlink
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class TezosClient
4
- VERSION = "0.4.17"
4
+ VERSION = "1.0.0"
5
5
  end
data/lib/tezos_client.rb CHANGED
@@ -6,6 +6,7 @@ require "active_support/core_ext/string/inflections"
6
6
  require "active_support/core_ext/module/delegation"
7
7
  require "timeout"
8
8
  require "benchmark"
9
+ require "open3"
9
10
 
10
11
  require "tezos_client/version"
11
12
  require "tezos_client/string_utils"
@@ -25,9 +26,12 @@ require "tezos_client/operations/reveal_operation"
25
26
  require "tezos_client/operations/raw_operation_array"
26
27
  require "tezos_client/operations/operation_array"
27
28
 
28
- require "tezos_client/client_interface"
29
+ require "tezos_client/tools/system_call"
30
+ require "tezos_client/tools/temporary_file"
31
+
29
32
  require "tezos_client/rpc_interface"
30
33
  require "tezos_client/liquidity_interface"
34
+ require "tezos_client/smartpy_interface"
31
35
 
32
36
  class TezosClient
33
37
  using CurrencyUtils
@@ -38,9 +42,9 @@ class TezosClient
38
42
  include Commands
39
43
  include Crypto
40
44
 
41
- attr_accessor :client_interface
42
45
  attr_accessor :rpc_interface
43
46
  attr_accessor :liquidity_interface
47
+ attr_accessor :smartpy_interface
44
48
 
45
49
  RANDOM_SIGNATURE = "edsigu165B7VFf3Dpw2QABVzEtCxJY2gsNBNcE3Ti7rRxtDUjqTFRpg67EdAQmY6YWPE5tKJDMnSTJDFu65gic8uLjbW2YwGvAZ"
46
50
 
@@ -50,15 +54,14 @@ class TezosClient
50
54
  @liquidity_options = liquidity_options
51
55
 
52
56
  @client_config_file = ENV["TEZOS_CLIENT_CONFIG_FILE"]
53
- @client_interface = ClientInterface.new(
54
- config_file: @client_config_file
55
- )
56
57
 
57
58
  @rpc_interface = RpcInterface.new(
58
59
  host: @rpc_node_address,
59
60
  port: @rpc_node_port
60
61
  )
61
62
 
63
+ @smartpy_interface = SmartpyInterface.new
64
+
62
65
  @liquidity_interface = LiquidityInterface.new(
63
66
  rpc_node_address: @rpc_node_address,
64
67
  rpc_node_port: @rpc_node_port,
@@ -79,7 +82,7 @@ class TezosClient
79
82
  #
80
83
  # @return [Hash] result of the origination containing :operation_id, :operation_result and :originated_contract
81
84
  #
82
- def originate_contract(from:, amount:, secret_key:, script: nil, init_params: nil, dry_run: false, **args)
85
+ def originate_contract(from:, amount:, secret_key: nil, script: nil, init_params: nil, dry_run: false, **args)
83
86
  origination_args = {
84
87
  rpc_interface: rpc_interface,
85
88
  from: from,
@@ -88,13 +91,11 @@ class TezosClient
88
91
  **args
89
92
  }
90
93
 
91
- if script != nil
92
- origination_args[:script] = liquidity_interface.origination_script(
93
- from: from,
94
- script: script,
95
- init_params: init_params
96
- )
97
- end
94
+ origination_args[:script] = contract_interface(script).origination_script(
95
+ from: from,
96
+ script: script,
97
+ init_params: init_params
98
+ )
98
99
 
99
100
  operation = OriginationOperation.new(origination_args)
100
101
  res = broadcast_operation(operation: operation, dry_run: dry_run)
@@ -166,15 +167,19 @@ class TezosClient
166
167
  broadcast_operation(operation: operation, dry_run: dry_run)
167
168
  end
168
169
 
169
- def call_contract(dry_run: false, **args)
170
- parameters = args.fetch(:parameters)
171
-
172
- json_params = liquidity_interface.call_parameters(
173
- script: args.fetch(:script),
174
- parameters: parameters
170
+ def call_contract(dry_run: false, entrypoint:, params:, script: nil, params_type:, **args)
171
+ json_params = micheline_params(
172
+ params: params,
173
+ entrypoint: entrypoint,
174
+ script: script,
175
+ params_type: params_type
175
176
  )
176
177
 
177
- transfer_args = args.merge(parameters: json_params, dry_run: dry_run)
178
+ transfer_args = args.merge(
179
+ entrypoint: entrypoint,
180
+ parameters: json_params,
181
+ dry_run: dry_run
182
+ )
178
183
 
179
184
  transfer(transfer_args)
180
185
  end
@@ -242,6 +247,10 @@ class TezosClient
242
247
  end
243
248
  end
244
249
 
250
+ def self.root_path
251
+ File.expand_path(File.dirname(__FILE__))
252
+ end
253
+
245
254
  private
246
255
 
247
256
  def broadcast_operation(operation:, dry_run:)
@@ -256,4 +265,50 @@ class TezosClient
256
265
  )
257
266
  end
258
267
 
268
+ def liquidity_contract? filename
269
+ filename&.to_s&.end_with?(".liq")
270
+ end
271
+
272
+ def micheline_params(params:, entrypoint:, script: nil, params_type:)
273
+ {
274
+ entrypoint: entrypoint,
275
+ value: convert_params(
276
+ params: params,
277
+ entrypoint: entrypoint,
278
+ script: script,
279
+ params_type: params_type
280
+ )
281
+ }
282
+ end
283
+
284
+ def convert_params(params:, entrypoint:, script: nil, params_type:)
285
+ case params_type.to_sym
286
+ when :micheline
287
+ params
288
+ when :caml
289
+ raise ::ArgumentError, "need liquidity script path with camel type" if script.nil?
290
+
291
+ liquidity_interface.call_parameters(
292
+ script: script,
293
+ entrypoint: entrypoint,
294
+ parameters: params
295
+ )
296
+ else
297
+ raise ::ArgumentError, "params type must be equal to [ :micheline, :caml ]"
298
+ end
299
+ end
300
+
301
+
302
+ def contract_interface(script)
303
+ case script.to_s
304
+ when /[A-Za-z_\/\-]*.liq/
305
+ liquidity_interface
306
+ when /[A-Za-z_\/\-]*.py/
307
+ smartpy_interface
308
+ when nil
309
+ raise "script var unset"
310
+ else
311
+ raise "unknown contract type"
312
+ end
313
+ end
259
314
  end
@@ -11,4 +11,4 @@ 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
14
+ bubblewrap libev-dev libhidapi-dev npm
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: 0.4.17
4
+ version: 1.0.0
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-02-24 00:00:00.000000000 Z
11
+ date: 2020-02-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -227,7 +227,6 @@ files:
227
227
  - bin/console
228
228
  - bin/setup
229
229
  - lib/tezos_client.rb
230
- - lib/tezos_client/client_interface.rb
231
230
  - lib/tezos_client/client_interface/block_contextual.rb
232
231
  - lib/tezos_client/client_interface/client_wrapper.rb
233
232
  - lib/tezos_client/client_interface/contract.rb
@@ -259,11 +258,14 @@ files:
259
258
  - lib/tezos_client/rpc_interface/monitor.rb
260
259
  - lib/tezos_client/rpc_interface/operations.rb
261
260
  - lib/tezos_client/rpc_interface/request_manager.rb
261
+ - lib/tezos_client/smartpy_inteface/micheline_serializer_wrapper.rb
262
+ - lib/tezos_client/smartpy_inteface/smartpy_wrapper.rb
263
+ - lib/tezos_client/smartpy_interface.rb
262
264
  - lib/tezos_client/string_utils.rb
265
+ - lib/tezos_client/tools/system_call.rb
266
+ - lib/tezos_client/tools/temporary_file.rb
263
267
  - lib/tezos_client/version.rb
264
268
  - tezos_client.gemspec
265
- - travis-scripts/install-liquidity.sh
266
- - travis-scripts/install-opam.sh
267
269
  - travis-scripts/prepare-ubuntu.sh
268
270
  homepage: http://moneytrack.io
269
271
  licenses:
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "client_interface/client_wrapper"
4
- require_relative "client_interface/misc"
5
- require_relative "client_interface/key"
6
- require_relative "client_interface/contract"
7
- require_relative "client_interface/block_contextual"
8
-
9
- class TezosClient
10
- class ClientInterface
11
- attr_reader :config_file
12
-
13
- include Logger
14
-
15
- include ClientWrapper
16
- include Misc
17
- include Key
18
- include Contract
19
- include BlockContextual
20
-
21
- def initialize(config_file: nil)
22
- @config_file = config_file
23
- end
24
- end
25
- end
@@ -1,20 +0,0 @@
1
- #!/bin/sh
2
- #set -ex
3
- # This script is used in .travis.yml for continuous integration on travis.
4
- # BTW, it also show some needed system packages to build liquidity
5
- # Travis CI is done on Ubuntu trusty
6
- export OPAMYES=1
7
-
8
- [ -d "liquidity/.git" ] || git clone --depth=50 https://github.com/OCamlPro/liquidity.git liquidity
9
- cd liquidity
10
- git pull
11
- # git checkout next
12
- git checkout 5ed5b09674cb96f8cd4ac83a55621f77d9a9110c
13
-
14
- eval `opam config env`
15
-
16
- opam install camlp4 ctypes-foreign ocaml-migrate-parsetree
17
- make build-deps
18
- make clone-tezos
19
- make
20
- make install
@@ -1,12 +0,0 @@
1
- # This script is used in .travis.yml for continuous integration on travis.
2
- # Travis CI is done on Ubuntu trusty
3
-
4
- export OPAMYES=1
5
- wget https://raw.githubusercontent.com/ocaml/opam/master/shell/install.sh
6
- yes "" | sh install.sh
7
-
8
- opam switch create liquidity 4.06.1 || opam switch set liquidity
9
-
10
- eval $(opam config env)
11
-
12
- opam update