tezos_client 0.3.9 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 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