tezos_client 0.3.9 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6bf4544c54f90bdbafd3fed0e9ba139470ffc59c0925de1d388a290a40e15a90
4
- data.tar.gz: 42ad8f061e34b90ef5c646b235794945456dc17b81b2e4914513f1a53fad250d
3
+ metadata.gz: 725a3f4be32e4f23f291c0abb15ad9671bff432a3b00f4f149fbad9c8cadf007
4
+ data.tar.gz: 21864905eeb5b701357551ee410c88f2e24832b31f53a1d811b0025f6410248d
5
5
  SHA512:
6
- metadata.gz: dcd4257f68999a0ef7a88fdebdcb9b65d19704f963dba7723799e89e90567d513f479f10f5f3e2df4397231c3606e176c3ea07b6feec14dec3affda0c8cea343
7
- data.tar.gz: e4afe38e6103582396862a447458fea502063fe8ef111ba5fd7b34fca9b2900b3764707a4be1943d204911224b0b5cab231de42399f10d67a9c193ea271a52b4
6
+ metadata.gz: 810cdd1ea762452c662bac91752c53b60756ad780989c58fa488fcb056d9907b4ac76e37907bbdb0fe7e40398badf686aa8f229033b5c53ec7a27bf07c389b9e
7
+ data.tar.gz: 39b1a9bfefda07893cab407b5abdd9835603473098e6db4828847c9265358b11833e3ed160c91db6f50d0f6d52a659f38242d243a1cbc2ea112f0fccab71683e
@@ -1,8 +1,7 @@
1
1
  ---
2
- dist: trusty
2
+ dist: xenial
3
+ language: ruby
3
4
  sudo: required
4
- #language: ruby
5
- language: ocaml
6
5
  cache:
7
6
  bundler: true
8
7
  directories:
@@ -13,9 +12,11 @@ rvm:
13
12
  env:
14
13
  - OPAMYES=1
15
14
  before_install:
16
- - sh travis-scripts/prepare-trusty.sh
15
+ - sh travis-scripts/prepare-ubuntu.sh
17
16
  - sh travis-scripts/install-opam.sh
18
17
  - sh travis-scripts/install-liquidity.sh
19
18
  - gem install bundler -v 1.16.3
20
19
  script:
20
+ - eval `opam config env`
21
+ - bundle install
21
22
  - bundle exec rake
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- tezos_client (0.3.9)
4
+ tezos_client (0.4.0)
5
5
  activesupport (~> 5.2.0)
6
6
  base58 (~> 0.2.3)
7
7
  bip_mnemonic (~> 0.0.2)
@@ -3,6 +3,7 @@
3
3
  require "pp"
4
4
  require "active_support/core_ext/hash/indifferent_access"
5
5
  require "active_support/core_ext/string/inflections"
6
+ require "active_support/core_ext/module/delegation"
6
7
  require "timeout"
7
8
 
8
9
  require "tezos_client/version"
@@ -11,13 +12,16 @@ require "tezos_client/currency_utils"
11
12
  require "tezos_client/crypto"
12
13
  require "tezos_client/commands"
13
14
  require "tezos_client/logger"
15
+ require "tezos_client/exceptions"
14
16
  require "tezos_client/encode_utils"
15
- require "tezos_client/operation"
17
+ require "tezos_client/operation_mgr"
18
+ require "tezos_client/operations/operation"
16
19
  require "tezos_client/operations/origination_operation"
17
20
  require "tezos_client/operations/transaction_operation"
18
21
  require "tezos_client/operations/transactions_operation"
19
22
  require "tezos_client/operations/activate_account_operation"
20
23
  require "tezos_client/operations/reveal_operation"
24
+ require "tezos_client/operations/operation_array"
21
25
 
22
26
  require "tezos_client/client_interface"
23
27
  require "tezos_client/rpc_interface"
@@ -72,17 +76,26 @@ class TezosClient
72
76
  #
73
77
  # @return [Hash] result of the origination containing :operation_id, :operation_result and :originated_contract
74
78
  #
75
- def originate_contract(from:, amount:, secret_key:, **args)
76
- res = OriginationOperation.new(
77
- liquidity_interface: liquidity_interface,
79
+ def originate_contract(from:, amount:, secret_key:, script: nil, init_params: nil, **args)
80
+ origination_args = {
78
81
  rpc_interface: rpc_interface,
79
82
  from: from,
80
83
  secret_key: secret_key,
81
84
  amount: amount,
82
85
  **args
83
- ).test_and_broadcast
86
+ }
87
+
88
+ if script != nil
89
+ origination_args[:script] = liquidity_interface.origination_script(
90
+ from: from,
91
+ script: script,
92
+ init_params: init_params
93
+ )
94
+ end
95
+
96
+ res = OriginationOperation.new(origination_args).test_and_broadcast
84
97
 
85
- res.merge(originated_contract: res[:operation_result][:originated_contracts][0])
98
+ res.merge(originated_contract: res[:operations_result][0][:originated_contracts][0])
86
99
  end
87
100
 
88
101
  # Transfer funds to an account
@@ -97,7 +110,6 @@ class TezosClient
97
110
  #
98
111
  def transfer(from:, amount:, to:, secret_key:, **args)
99
112
  TransactionOperation.new(
100
- liquidity_interface: liquidity_interface,
101
113
  rpc_interface: rpc_interface,
102
114
  from: from,
103
115
  to: to,
@@ -109,7 +121,6 @@ class TezosClient
109
121
 
110
122
  def activate_account(pkh:, secret:, **args)
111
123
  ActivateAccountOperation.new(
112
- liquidity_interface: liquidity_interface,
113
124
  rpc_interface: rpc_interface,
114
125
  pkh: pkh,
115
126
  secret: secret,
@@ -119,7 +130,6 @@ class TezosClient
119
130
 
120
131
  def transfer_to_many(from:, amounts:, secret_key:, **args)
121
132
  TransactionsOperation.new(
122
- liquidity_interface: liquidity_interface,
123
133
  rpc_interface: rpc_interface,
124
134
  from: from,
125
135
  amounts: amounts,
@@ -129,7 +139,6 @@ class TezosClient
129
139
  end
130
140
 
131
141
  def reveal_pubkey(secret_key:, **args)
132
-
133
142
  public_key = secret_key_to_public_key(secret_key)
134
143
  from = public_key_to_address(public_key)
135
144
 
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TezosClient
4
+ class RpcRequestFailure < Exception
5
+ include Logger
6
+
7
+ attr_reader :status_code
8
+ attr_reader :error
9
+ attr_reader :formatted_response
10
+
11
+ def initialize(error:, url:, status_code:)
12
+ @status_code = status_code
13
+ @error = error
14
+ @formatted_response = formatted_response
15
+
16
+ if @message.nil?
17
+ @message = "#{url} failed with status #{status_code}:\n #{tezos_contents_log(formatted_response)}"
18
+ end
19
+
20
+ super @message
21
+ end
22
+ end
23
+
24
+ class InvalidActivation < RpcRequestFailure
25
+ attr_reader :pkh
26
+
27
+ def initialize(error:, **_args)
28
+ @pkh = error[:pkh]
29
+ @message = "Invalid activation (pkh: #{pkh})"
30
+ super
31
+ end
32
+ end
33
+
34
+ class OperationFailure < Exception
35
+ include Logger
36
+
37
+ attr_reader :metadata
38
+ attr_reader :errors
39
+ attr_reader :status
40
+ attr_reader :message
41
+
42
+ def initialize(metadata:, errors:, status:)
43
+ @metadata = metadata
44
+ @errors = errors
45
+ @status = status
46
+
47
+ error = errors[0]
48
+
49
+ if @message.nil?
50
+ @message = "failure #{status}: #{tezos_contents_log(error).pretty_inspect}"
51
+ end
52
+
53
+ super(message)
54
+ end
55
+ end
56
+
57
+ class TezBalanceTooLow < OperationFailure
58
+ attr_reader :contract
59
+ attr_reader :balance
60
+ attr_reader :amount
61
+
62
+ def initialize(metadata:, errors:, status:)
63
+ error = errors[0]
64
+ @contract = error[:contract]
65
+ @balance = error[:balance]
66
+ @amount = error[:amount]
67
+
68
+ @message = "Tezos balance too low for address #{contract} (balance: #{balance}, amount #{amount})"
69
+
70
+ super
71
+ end
72
+ end
73
+
74
+ class ScriptRuntimeError < OperationFailure
75
+ attr_reader :location
76
+ attr_reader :with
77
+ attr_reader :contract
78
+
79
+ def initialize(metadata:, errors:, status:)
80
+ error = errors[0]
81
+ rejection_error = errors.detect { |error| error[:id] == "proto.003-PsddFKi3.scriptRejectedRuntimeError" }
82
+
83
+ @location = rejection_error[:location]
84
+ @contract = error[:contractHandle]
85
+ @with = rejection_error[:with]
86
+ @message = "Script runtime Error when executing #{contract}: #{with} (location: #{location})"
87
+ super
88
+ end
89
+ end
90
+
91
+
92
+ end
@@ -57,8 +57,10 @@ class TezosClient
57
57
 
58
58
  def json_scripts(args)
59
59
  with_file_copy(args[:script]) do |script_copy_path|
60
- json_init_script_path = "#{script_copy_path}.initializer.tz.json"
61
- json_contract_script_path = "#{script_copy_path}.tz.json"
60
+ script_basename = script_copy_path.sub(/.liq$/, "")
61
+
62
+ json_init_script_path = "#{script_basename}.initializer.tz.json"
63
+ json_contract_script_path = "#{script_basename}.tz.json"
62
64
 
63
65
  call_liquidity "--json #{script_copy_path}"
64
66
 
@@ -12,6 +12,28 @@ class TezosClient
12
12
  self.class.logger << out + "\n"
13
13
  end
14
14
 
15
+ FILTERED_KEYS = [:code, :contractCode]
16
+ def tezos_contents_log_filter(content)
17
+ if content.is_a? Array
18
+ content.map { |el| tezos_contents_log_filter(el) }
19
+ elsif content.is_a? Hash
20
+ content.reduce({}) do |h, (k, v)|
21
+ value = if FILTERED_KEYS.include? k.to_sym
22
+ "#{v.to_s[0..30]}..."
23
+ else
24
+ tezos_contents_log_filter(v)
25
+ end
26
+ h.merge(k => value)
27
+ end
28
+ else
29
+ content
30
+ end
31
+ end
32
+
33
+ def tezos_contents_log(content)
34
+ tezos_contents_log_filter(content).pretty_inspect
35
+ end
36
+
15
37
  class_methods do
16
38
  # Setup the log for TezosClient calls.
17
39
  # Value should be a logger but can can be stdout, stderr, or a filename.
@@ -0,0 +1,178 @@
1
+
2
+ class TezosClient
3
+
4
+ class OperationMgr
5
+ include Crypto
6
+ using CurrencyUtils
7
+
8
+ attr_accessor :rpc_interface,
9
+ :rpc_operation_args
10
+
11
+ def initialize(rpc_interface:, rpc_operation_args:, **args)
12
+ @rpc_interface = rpc_interface
13
+ @secret_key = args.fetch(:secret_key)
14
+ @multiple_operations = rpc_operation_args.is_a?(Array)
15
+ @rpc_operation_args = @multiple_operations ? rpc_operation_args : [rpc_operation_args]
16
+ @signed_operation_args_h = nil
17
+ @branch = args[:branch]
18
+ @protocol = args[:protocol]
19
+ end
20
+
21
+ def multiple_operations?
22
+ @multiple_operations
23
+ end
24
+
25
+ def single_operation?
26
+ !multiple_operations?
27
+ end
28
+
29
+ def branch
30
+ @branch ||= rpc_interface.head_hash
31
+ end
32
+
33
+ def protocol
34
+ @protocol ||= rpc_interface.protocol
35
+ end
36
+
37
+ def simulate_and_update_limits
38
+ run_result = run
39
+
40
+ run_result[:operations_result].zip(rpc_operation_args) do |operation_result, rpc_operation_args|
41
+ if rpc_operation_args.key?(:gas_limit)
42
+ rpc_operation_args[:gas_limit] = (operation_result[:consumed_gas].to_i + 0.001.to_satoshi).to_s
43
+ end
44
+ end
45
+
46
+ run_result
47
+ end
48
+
49
+ def to_hex
50
+ rpc_interface.forge_operations(operations: rpc_operation_args, branch: branch)
51
+ end
52
+
53
+ def sign
54
+ sign_operation(
55
+ secret_key: @secret_key,
56
+ operation_hex: to_hex
57
+ ) do |base_58_signature, signed_hex, _op_id|
58
+ @signed_operation_args_h = rpc_operation_args.hash
59
+ @base_58_signature = base_58_signature
60
+ @signed_hex = signed_hex
61
+ end
62
+ end
63
+
64
+ def signed?
65
+ @signed_operation_args_h == rpc_operation_args.hash
66
+ end
67
+
68
+ def base_58_signature
69
+ sign unless signed?
70
+ @base_58_signature
71
+ end
72
+
73
+ def signed_hex
74
+ sign unless signed?
75
+ @signed_hex
76
+ end
77
+
78
+ def test_and_broadcast
79
+ # simulate operations and adjust gas limits
80
+ simulate_and_update_limits
81
+ operations_result = preapply
82
+
83
+ op_id = broadcast
84
+ {
85
+ operation_id: op_id,
86
+ operations_result: operations_result,
87
+ }
88
+ end
89
+
90
+ def run
91
+ rpc_responses = rpc_interface.run_operations(
92
+ operations: rpc_operation_args,
93
+ signature: base_58_signature,
94
+ branch: branch)
95
+
96
+ consumed_storage = 0
97
+ consumed_gas = 0
98
+
99
+ operations_result = rpc_responses.map do |rpc_response|
100
+ metadata = rpc_response[:metadata]
101
+ ensure_applied!(metadata)
102
+ consumed_storage += compute_consumed_storage(metadata)
103
+ consumed_gas += compute_consumed_gas(metadata)
104
+ metadata[:operation_result]
105
+ end
106
+
107
+ {
108
+ status: :applied,
109
+ consumed_gas: consumed_gas,
110
+ consumed_storage: consumed_storage,
111
+ operations_result: operations_result
112
+ }
113
+ end
114
+
115
+ def compute_consumed_gas(metadata)
116
+ consumed_gas = (metadata.dig(:operation_result, :consumed_gas) || "0").to_i.from_satoshi
117
+
118
+ if metadata.key?(:internal_operation_results)
119
+ metadata[:internal_operation_results].each do |internal_operation_result|
120
+ consumed_gas += (internal_operation_result[:result][:consumed_gas] || "0").to_i.from_satoshi
121
+ end
122
+ end
123
+ consumed_gas
124
+ end
125
+
126
+ def compute_consumed_storage(metadata)
127
+ (metadata.dig(:operation_result, :paid_storage_size_diff) || "0").to_i.from_satoshi
128
+ end
129
+
130
+
131
+ def preapply
132
+ rpc_responses = rpc_interface.preapply_operations(
133
+ operations: rpc_operation_args,
134
+ signature: base_58_signature,
135
+ protocol: protocol,
136
+ branch: branch)
137
+
138
+ rpc_responses.map do |rpc_response|
139
+ ensure_applied!(rpc_response[:metadata])
140
+ end
141
+ end
142
+
143
+ def broadcast
144
+ rpc_interface.broadcast_operation(signed_hex)
145
+ end
146
+
147
+ private
148
+
149
+ def ensure_applied!(metadata)
150
+ operation_result = metadata[:operation_result]
151
+
152
+ unless operation_result.nil?
153
+ status = operation_result[:status]
154
+ if status != "applied"
155
+ failed!(status, operation_result[:errors], metadata)
156
+ end
157
+ end
158
+
159
+ operation_result
160
+ end
161
+
162
+ def exception_klass(errors)
163
+ error = errors[0]
164
+ case error[:id]
165
+ when "proto.003-PsddFKi3.contract.balance_too_low"
166
+ TezBalanceTooLow
167
+ when "proto.003-PsddFKi3.scriptRuntimeError"
168
+ ScriptRuntimeError
169
+ else
170
+ OperationFailure
171
+ end
172
+ end
173
+
174
+ def failed!(status, errors, metadata)
175
+ raise exception_klass(errors).new(metadata: metadata, errors: errors, status: status)
176
+ end
177
+ end
178
+ end