block_io 3.0.5 → 3.1.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: eb185ac520ba882b4acc7a0b9456c9005724279c3b48870ebd2c14ae0f4327e9
4
- data.tar.gz: fa7327a6ed20968ed94395dde29a4c693bdcbf0cd800df7a038b98236aca53c9
3
+ metadata.gz: 890b54dc8511344d1a39cf22e4379dfadc78d60395464a5bd3b5566fe19e775a
4
+ data.tar.gz: 0c286e1f3ac4d617034b18d621401eb06d243a18a9cef1a4fe19e39b388c72b9
5
5
  SHA512:
6
- metadata.gz: 943a8bd870a90b3e317a10880d5798cd586c9dbed7d9fdf83f2833a64bf134acf44d3a80be71b6c695de92f50a02584923792efe57f8a2fea76169eb6d638d72
7
- data.tar.gz: 6f4e19484ab274871068321022d3d4ea56e8be476930b4efd37761c575f9e7c377ffe36e071a294f061cbdb95e1f14f7d31ae4c8e9986ed92d02ce68bc40940c
6
+ metadata.gz: 358f8605db3bd1c6a1931384848d6533a3ecdc0dc41dce7a1b4e9799f6ae67d0819bb34a1d0bbbf8a83cc0d655bd8e5bb8bd573666480dbddf38540d7ba1e14a
7
+ data.tar.gz: 164c8c5e6689657b7535e689a83395edcd917eeade0aa03dbc3dffd03f2a0e732d0d00498a00d3fa879bc058c50ec4345007abe6c2eebe65eefce431f9c3bc61
data/README.md CHANGED
@@ -6,7 +6,7 @@ This Ruby Gem is the official reference client for the Block.io's infrastructure
6
6
 
7
7
  Add this line to your application's Gemfile:
8
8
 
9
- gem 'block_io'
9
+ gem "block_io"
10
10
 
11
11
  And then execute:
12
12
 
@@ -17,6 +17,7 @@ Or install it yourself as:
17
17
  $ gem install block_io
18
18
 
19
19
  ## Changelog
20
+ *10/28/22*: Version 3.1.0 uses Typhoeus instead of httprb. If using proxy, :url must be provided instead of previous :hostname and :port.
20
21
  *05/30/22*: Version 3.0.5 fixes Litecoin P2SH address version (properly now).
21
22
  *12/26/21*: Version 3.0.4 drops support for EOL Ruby 2.4, 2.5. Supports Ruby 3.1.
22
23
  *09/28/21*: Version 3.0.3 supports witness_v1 outputs (Bech32m).
data/block_io.gemspec CHANGED
@@ -18,12 +18,11 @@ Gem::Specification.new do |spec|
18
18
  spec.required_ruby_version = '>= 2.6.0'
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_development_dependency "bundler", ">= 1.16", "< 3.0"
22
- spec.add_development_dependency "rake", "~> 13.0", ">= 13.0"
23
- spec.add_development_dependency "rspec", "~> 3.6", ">= 3.6"
24
- spec.add_development_dependency "webmock", "~> 3.12", "< 4.0"
25
- spec.add_runtime_dependency "bitcoinrb", "~> 1.0", "< 1.2.0"
26
- spec.add_runtime_dependency "http", ">= 4.4.1", "< 6.0"
27
- spec.add_runtime_dependency "oj", "~> 3.0", "< 4.0"
28
- spec.add_runtime_dependency "connection_pool", ">= 2.2", "< 3.0"
21
+ spec.add_development_dependency "bundler", ">= 1.16", "< 3"
22
+ spec.add_development_dependency "rake", "~> 13.0", "< 14"
23
+ spec.add_development_dependency "rspec", "~> 3.0", "< 4"
24
+ spec.add_development_dependency "webmock", "~> 3.0", "< 4"
25
+ spec.add_runtime_dependency "bitcoinrb", ">= 1.2.1", "< 2"
26
+ spec.add_runtime_dependency "typhoeus", "~> 1.0", "< 2"
27
+ spec.add_runtime_dependency "oj", "~> 3.0", "< 4"
29
28
  end
@@ -2,8 +2,14 @@ module BlockIo
2
2
 
3
3
  class Client
4
4
 
5
- attr_reader :api_key, :version, :network
5
+ attr_reader :api_key, :version, :network, :api_request_headers
6
6
 
7
+ USER_AGENT = 'gem:block_io:' << VERSION.to_s
8
+ CONTENT_TYPE = 'application/json; charset=UTF-8'
9
+ ACCEPT_TYPE = 'application/json'
10
+ KEEP_ALIVE = 'Keep-Alive'
11
+ TIMEOUT = 60
12
+
7
13
  def initialize(args = {})
8
14
  # api_key
9
15
  # pin
@@ -13,21 +19,25 @@ module BlockIo
13
19
  # pool_size
14
20
  # keys
15
21
 
16
- raise "Must provide an API Key." unless args.key?(:api_key) and args[:api_key].to_s.size > 0
22
+ raise 'Must provide an API Key.' unless args.key?(:api_key) and args[:api_key].to_s.size > 0
17
23
 
18
24
  @api_key = args[:api_key]
19
25
  @pin = args[:pin]
20
26
  @version = args[:version] || 2
21
- @hostname = args[:hostname] || "block.io"
22
- @proxy = args[:proxy] || {}
27
+ @hostname = args[:hostname] || 'block.io'
23
28
  @keys = {}
24
29
 
25
- raise Exception.new("Must specify hostname, port, username, password if using a proxy.") if @proxy.keys.size > 0 and [:hostname, :port, :username, :password].any?{|x| !@proxy.key?(x)}
30
+ # prepare proxy settings if provided
31
+ proxy = args[:proxy] || {}
32
+
33
+ if proxy.keys.size > 0 then
34
+ raise Exception.new('Must specify hostname, port, username, password if using a proxy.') if [:url, :username, :password].any?{|x| !proxy.key?(x)}
35
+ @proxy = {:proxy => proxy[:url], :proxyuserpwd => "#{proxy[:username]}:#{proxy[:password]}"}.freeze
36
+ else
37
+ @proxy = {}
38
+ end
26
39
 
27
- @conn = ConnectionPool.new(:size => args[:pool_size] || 5) { http = HTTP.headers(:accept => "application/json", :user_agent => "gem:block_io:#{VERSION}");
28
- http = http.via(args.dig(:proxy, :hostname), args.dig(:proxy, :port), args.dig(:proxy, :username), args.dig(:proxy, :password)) if @proxy.key?(:hostname);
29
- http = http.persistent("https://#{@hostname}");
30
- http }
40
+ @api_request_headers = {'User-Agent' => USER_AGENT, 'Content-Type' => CONTENT_TYPE, 'Accept' => ACCEPT_TYPE, 'Expect' => '', 'Connection' => KEEP_ALIVE, 'Host' => @hostname}.freeze
31
41
 
32
42
  # this will get populated after a successful API call
33
43
  @network = nil
@@ -38,12 +48,12 @@ module BlockIo
38
48
 
39
49
  method_name = m.to_s
40
50
 
41
- raise Exception.new("Must provide arguments as a Hash.") unless args.size <= 1 and args.all?{|x| x.is_a?(Hash)}
42
- raise Exception.new("Parameter keys must be symbols. For instance: :label => 'default' instead of 'label' => 'default'") unless args[0].nil? or args[0].keys.all?{|x| x.is_a?(Symbol)}
43
- raise Exception.new("Cannot pass PINs to any calls. PINs can only be set when initiating this library.") if !args[0].nil? and args[0].key?(:pin)
44
- raise Exception.new("Do not specify API Keys here. Initiate a new BlockIo object instead if you need to use another API Key.") if !args[0].nil? and args[0].key?(:api_key)
51
+ raise Exception.new('Must provide arguments as a Hash.') unless args.size <= 1 and args.all?{|x| x.is_a?(Hash)}
52
+ raise Exception.new('Parameter keys must be symbols. For instance: :label => "default" instead of "label" => "default"') unless args[0].nil? or args[0].keys.all?{|x| x.is_a?(Symbol)}
53
+ raise Exception.new('Cannot pass PINs to any calls. PINs can only be set when initiating this library.') if !args[0].nil? and args[0].key?(:pin)
54
+ raise Exception.new('Do not specify API Keys here. Initiate a new BlockIo object instead if you need to use another API Key.') if !args[0].nil? and args[0].key?(:api_key)
45
55
 
46
- if method_name.eql?("prepare_sweep_transaction") then
56
+ if method_name.eql?('prepare_sweep_transaction') then
47
57
  # we need to ensure @network is set before we allow this
48
58
  # we need to send only the public key, not the given private key
49
59
  # we're sweeping from an address
@@ -54,6 +64,13 @@ module BlockIo
54
64
 
55
65
  end
56
66
 
67
+ def padded_f(d)
68
+ # returns a padded decimal to 8 decimal places
69
+ b = BigDecimal(d).to_s('F')
70
+ b << '0' * (8 - (b.size - b.index('.') - 1))
71
+ b
72
+ end
73
+
57
74
  def summarize_prepared_transaction(data)
58
75
  # takes the response from prepare_transaction/prepare_dtrust_transaction/prepare_sweep_transaction
59
76
  # returns the network fee being paid, the blockio fee being paid, amounts being sent
@@ -64,10 +81,15 @@ module BlockIo
64
81
  blockio_fees = [BigDecimal(0)]
65
82
  change_amounts = [BigDecimal(0)]
66
83
 
67
- data['data']['outputs'].each do |output|
68
- if output['output_category'] == 'blockio-fee' then
84
+ i = 0
85
+ loop do
86
+ output = data['data']['outputs'][i]
87
+ break if output.nil?
88
+ i += 1
89
+
90
+ if output['output_category'].eql?('blockio-fee') then
69
91
  blockio_fees << BigDecimal(output['output_value'])
70
- elsif output['output_category'] == 'change' then
92
+ elsif output['output_category'].eql?('change') then
71
93
  change_amounts << BigDecimal(output['output_value'])
72
94
  else
73
95
  # user-specified
@@ -83,9 +105,9 @@ module BlockIo
83
105
 
84
106
  {
85
107
  'network' => data['data']['network'],
86
- 'network_fee' => '%0.8f' % network_fee,
87
- "blockio_fee" => '%0.8f' % blockio_fee,
88
- "total_amount_to_send" => '%0.8f' % output_sum
108
+ 'network_fee' => padded_f(network_fee),
109
+ 'blockio_fee' => padded_f(blockio_fee),
110
+ 'total_amount_to_send' => padded_f(output_sum)
89
111
  }
90
112
 
91
113
  end
@@ -97,33 +119,32 @@ module BlockIo
97
119
 
98
120
  set_network(data['data']['network']) if data['data'].key?('network')
99
121
 
100
- raise "Data must be contain one or more inputs" unless data['data']['inputs'].size > 0
101
- raise "Data must contain one or more outputs" unless data['data']['outputs'].size > 0
102
- raise "Data must contain information about addresses" unless data['data']['input_address_data'].size > 0 # TODO make stricter
122
+ raise 'Data must be contain one or more inputs' unless data['data']['inputs'].size > 0
123
+ raise 'Data must contain one or more outputs' unless data['data']['outputs'].size > 0
124
+ raise 'Data must contain information about addresses' unless data['data']['input_address_data'].size > 0 # TODO make stricter
103
125
 
104
126
  private_keys = keys.map{|x| Key.from_private_key_hex(x)}
105
127
 
106
- # TODO debug all of this
107
-
108
128
  inputs = data['data']['inputs']
109
129
  outputs = data['data']['outputs']
110
130
 
111
131
  tx = Bitcoin::Tx.new
112
132
 
113
133
  # populate the inputs
114
- inputs.each do |input|
115
- tx.in << Bitcoin::TxIn.new(:out_point => Bitcoin::OutPoint.from_txid(input['previous_txid'], input['previous_output_index']))
134
+ tx.in << inputs.map do |input|
135
+ Bitcoin::TxIn.new(:out_point => Bitcoin::OutPoint.from_txid(input['previous_txid'], input['previous_output_index']))
116
136
  end
117
-
137
+ tx.in.flatten!
138
+
118
139
  # populate the outputs
119
- outputs.each do |output|
120
- tx.out << Bitcoin::TxOut.new(:value => (BigDecimal(output['output_value']) * BigDecimal(100000000)).to_i, :script_pubkey => Bitcoin::Script.parse_from_addr(output['receiving_address']))
140
+ tx.out << outputs.map do |output|
141
+ Bitcoin::TxOut.new(:value => (BigDecimal(output['output_value']) * BigDecimal(100000000)).to_i, :script_pubkey => Bitcoin::Script.parse_from_addr(output['receiving_address']))
121
142
  end
122
-
123
-
143
+ tx.out.flatten!
144
+
124
145
  # some protection against misbehaving machines and/or code
125
- raise Exception.new("Expected unsigned transaction ID mismatch. Please report this error to support@block.io.") unless (data['data']['expected_unsigned_txid'].nil? or
126
- data['data']['expected_unsigned_txid'] == tx.txid)
146
+ raise Exception.new('Expected unsigned transaction ID mismatch. Please report this error to support@block.io.') unless (data['data']['expected_unsigned_txid'].nil? or
147
+ data['data']['expected_unsigned_txid'].eql?(tx.txid))
127
148
 
128
149
  # extract key
129
150
  encrypted_key = data['data']['user_key']
@@ -131,11 +152,11 @@ module BlockIo
131
152
  if !encrypted_key.nil? and !@keys.key?(encrypted_key['public_key']) then
132
153
  # decrypt the key with PIN
133
154
 
134
- raise Exception.new("PIN not set and no keys provided. Cannot sign transaction.") unless !@pin.nil? or @keys.size > 0
155
+ raise Exception.new('PIN not set and no keys provided. Cannot sign transaction.') unless !@pin.nil? or @keys.size > 0
135
156
 
136
157
  key = Helper.dynamicExtractKey(encrypted_key, @pin)
137
158
 
138
- raise Exception.new("Public key mismatch for requested signer and ourselves. Invalid Secret PIN detected.") unless key.public_key_hex.eql?(encrypted_key["public_key"])
159
+ raise Exception.new('Public key mismatch for requested signer and ourselves. Invalid Secret PIN detected.') unless key.public_key_hex.eql?(encrypted_key['public_key'])
139
160
 
140
161
  # store this key for later use
141
162
  @keys[key.public_key_hex] = key
@@ -143,7 +164,7 @@ module BlockIo
143
164
  end
144
165
 
145
166
  # store the provided keys, if any, for later use
146
- private_keys.each{|key| @keys[key.public_key_hex] = key}
167
+ @keys.merge!(private_keys.map{|key| [key.public_key_hex, key]}.to_h)
147
168
 
148
169
  signatures = []
149
170
 
@@ -152,20 +173,22 @@ module BlockIo
152
173
  # Block.io will check to see if all signatures are present, or return an error otherwise saying insufficient signatures provided
153
174
 
154
175
  i = 0
155
- while i < inputs.size do
176
+ loop do
156
177
  input = inputs[i]
178
+ break if input.nil?
157
179
 
158
- input_address_data = data['data']['input_address_data'].detect{|d| d['address'] == input['spending_address']}
180
+ input_address_data = data['data']['input_address_data'].detect{|d| d['address'].eql?(input['spending_address'])}
159
181
  sighash_for_input = Helper.getSigHashForInput(tx, i, input, input_address_data) # in bytes
160
182
 
161
- input_address_data['public_keys'].each do |signer_public_key|
183
+ signatures << input_address_data['public_keys'].map do |signer_public_key|
162
184
  # sign what we can and append signatures to the signatures object
163
-
164
185
  next unless @keys.key?(signer_public_key)
165
186
 
166
- signature = @keys[signer_public_key].sign(sighash_for_input).unpack("H*")[0] # in hex
167
- signatures << {"input_index" => i, "public_key" => signer_public_key, "signature" => signature}
168
-
187
+ {
188
+ 'input_index' => i,
189
+ 'public_key' => signer_public_key,
190
+ 'signature' => @keys[signer_public_key].sign(sighash_for_input).unpack1('H*') # in hex
191
+ }
169
192
  end
170
193
 
171
194
  i += 1 # go to next input
@@ -173,6 +196,9 @@ module BlockIo
173
196
 
174
197
  end
175
198
 
199
+ signatures.flatten!
200
+ signatures.compact!
201
+
176
202
  # if we have everything we need for this transaction, just finalize the transaction
177
203
  if Helper.allSignaturesPresent?(tx, inputs, signatures, data['data']['input_address_data']) then
178
204
  Helper.finalizeTransaction(tx, inputs, signatures, data['data']['input_address_data'])
@@ -183,18 +209,18 @@ module BlockIo
183
209
  @keys = {}
184
210
 
185
211
  # the response for submitting the transaction
186
- {"tx_type" => data['data']['tx_type'], "tx_hex" => tx.to_hex, "signatures" => (signatures.size == 0 ? nil : signatures)}
212
+ {'tx_type' => data['data']['tx_type'], 'tx_hex' => tx.to_hex, 'signatures' => (signatures.size.eql?(0) ? nil : signatures)}
187
213
 
188
214
  end
189
215
 
190
216
  private
191
217
 
192
- def internal_prepare_sweep_transaction(args = {}, method_name = "prepare_sweep_transaction")
218
+ def internal_prepare_sweep_transaction(args = {}, method_name = 'prepare_sweep_transaction')
193
219
 
194
220
  # set the network first if not already known
195
- api_call({:method_name => "get_balance", :params => {}}) if @network.nil?
221
+ api_call({:method_name => 'get_balance', :params => {}}) if @network.nil?
196
222
 
197
- raise Exception.new("No private_key provided.") unless args.key?(:private_key) and (args[:private_key] || "").size > 0
223
+ raise Exception.new('No private_key provided.') unless args.key?(:private_key) and (args[:private_key] || '').size > 0
198
224
 
199
225
  # ensure the private key never goes to Block.io
200
226
  key = Key.from_wif(args[:private_key])
@@ -215,26 +241,36 @@ module BlockIo
215
241
 
216
242
  def api_call(args)
217
243
 
218
- raise Exception.new("No connections left to perform API call. Please re-initialize BlockIo::Client with :pool_size greater than #{@conn.size}.") unless @conn.available > 0
219
-
220
- response = @conn.with {|http| http.post("/api/v#{@version}/#{args[:method_name]}", :json => args[:params].merge({:api_key => @api_key}))}
244
+ response = Typhoeus.post("https://#{@hostname}/api/v#{@version}/#{args[:method_name]}",
245
+ :body => Oj.dump(args[:params].merge({:api_key => @api_key}), :mode => :compat),
246
+ :headers => @api_request_headers,
247
+ :timeout => TIMEOUT,
248
+ **@proxy)
221
249
 
222
- begin
223
- body = Oj.safe_load(response.to_s)
224
- rescue
225
- body = {"status" => "fail", "data" => {"error_message" => "Unknown error occurred. Please report this to support@block.io. Status #{response.code}."}}
250
+ body = nil
251
+
252
+ if response.timed_out? then
253
+ body = {'status' => 'fail', 'data' => {'error_message' => 'Request timed out.'}}
254
+ elsif response.code.eql?(0) then
255
+ body = {'status' => 'fail', 'data' => {'error_message' => 'No response received.'}}
256
+ else
257
+ begin
258
+ body = Oj.safe_load(response.body.to_s)
259
+ rescue Exception => e
260
+ body = {'status' => 'fail', 'data' => {'error_message' => "Unknown error occurred. Please report this to support@block.io. Status #{response.code}."}}
261
+ end
226
262
  end
227
-
228
- if !body["status"].eql?("success") then
263
+
264
+ if !body['status'].eql?('success') then
229
265
  # raise an exception on error for easy handling
230
266
  # user can extract raw response using e.raw_data
231
- e = APIException.new("#{body["data"]["error_message"]}")
267
+ e = APIException.new(body['data']['error_message'].to_s)
232
268
  e.set_raw_data(body)
233
269
  raise e
234
270
  end
235
271
 
272
+ # success
236
273
  set_network(body['data']['network']) if body['data'].key?('network')
237
-
238
274
  body
239
275
 
240
276
  end
@@ -61,7 +61,7 @@ module Bitcoin
61
61
 
62
62
  signature = ECDSA::Signature.new(r, s).to_der
63
63
 
64
- # comment lines below lead to performance issues
64
+ # these lines lead to performance issues
65
65
  # public_key = Bitcoin::Key.new(priv_key: privkey.bth, :key_type => Bitcoin::Key::TYPES[:compressed]).pubkey # get rid of the key_type warning
66
66
  # raise 'Creation of signature failed.' unless Bitcoin::Secp256k1::Ruby.verify_sig(data, signature, public_key)
67
67
 
@@ -77,7 +77,7 @@ module Bitcoin
77
77
  # override so enforce compressed keys
78
78
 
79
79
  raise "key_type must always be Bitcoin::KEY::TYPES[:compressed]" unless key_type == TYPES[:compressed]
80
- puts "[Warning] Use key_type parameter instead of compressed. compressed parameter removed in the future." if key_type.nil? && !compressed.nil? && pubkey.nil?
80
+ puts '[Warning] Use key_type parameter instead of compressed. compressed parameter removed in the future.' if key_type.nil? && !compressed.nil? && pubkey.nil?
81
81
  if key_type
82
82
  @key_type = key_type
83
83
  compressed = @key_type != TYPES[:uncompressed]
@@ -113,14 +113,16 @@ module Bech32
113
113
  # override so we can parse non-Bitcoin Bech32 addresses
114
114
 
115
115
  class SegwitAddr
116
+
117
+ VALID_HRPS = ['bc', 'ltc', 'tb', 'tltc', 'doge', 'tdge'].inject({}){|h,v| h[v] = true; h}
116
118
 
117
119
  private
118
120
  def parse_addr(addr)
119
121
  @hrp, data, spec = Bech32.decode(addr)
120
- raise 'Invalid address.' if hrp.nil? || data[0].nil? || !['bc', 'ltc', 'tb', 'tltc', 'doge', 'tdge'].include?(hrp) # HRP_MAINNET, HRP_TESTNET, HRP_REGTEST].include?(hrp)
122
+ raise 'Invalid address.' if hrp.nil? || data[0].nil? || !VALID_HRPS.key?(hrp) # HRP_MAINNET, HRP_TESTNET, HRP_REGTEST].include?(hrp)
121
123
  @ver = data[0]
122
124
  raise 'Invalid witness version' if @ver > 16
123
- @prog = convert_bits(data[1..-1], 5, 8, false)
125
+ @prog = Bech32.convert_bits(data[1..-1], 5, 8, false)
124
126
  raise 'Invalid witness program' if @prog.nil? || @prog.length < 2 || @prog.length > 40
125
127
  raise 'Invalid witness program with version 0' if @ver == 0 && (@prog.length != 20 && @prog.length != 32)
126
128
  raise 'Witness version and encoding spec do not match' if (@ver == 0 && spec != Bech32::Encoding::BECH32) || (@ver != 0 && spec != Bech32::Encoding::BECH32M)
@@ -3,13 +3,13 @@ module BlockIo
3
3
  class Helper
4
4
 
5
5
  LEGACY_DECRYPTION_ALGORITHM = {
6
- :pbkdf2_salt => "",
6
+ :pbkdf2_salt => '',
7
7
  :pbkdf2_iterations => 2048,
8
- :pbkdf2_hash_function => "SHA256",
8
+ :pbkdf2_hash_function => 'SHA256',
9
9
  :pbkdf2_phase1_key_length => 16,
10
10
  :pbkdf2_phase2_key_length => 32,
11
11
  :aes_iv => nil,
12
- :aes_cipher => "AES-256-ECB",
12
+ :aes_cipher => 'AES-256-ECB',
13
13
  :aes_auth_tag => nil,
14
14
  :aes_auth_data => nil
15
15
  }
@@ -19,20 +19,23 @@ module BlockIo
19
19
 
20
20
  all_signatures_present = false
21
21
 
22
- inputs.each do |input|
22
+ i = 0
23
+ loop do
23
24
  # check if each input has its required signatures
25
+ input = inputs[i]
26
+ break if input.nil?
27
+ i += 1
24
28
 
25
29
  spending_address = input['spending_address']
26
- current_input_address_data = input_address_data.detect{|x| x['address'] == spending_address}
30
+ current_input_address_data = input_address_data.detect{|x| x['address'].eql?(spending_address)}
27
31
  required_signatures = current_input_address_data['required_signatures']
28
32
  public_keys = current_input_address_data['public_keys']
29
33
 
30
- signatures_present = signatures.map{|x| x if x['input_index'] == input['input_index']}.compact.inject({}){|h,v| h[v['public_key']] = v['signature']; h}
34
+ signatures_present = signatures.map{|x| x if x['input_index'].eql?(input['input_index'])}.compact.inject({}){|h,v| h[v['public_key']] = v['signature']; h}
31
35
 
32
36
  # break the loop if all signatures are not present for this input
33
- all_signatures_present = (signatures_present.keys.size >= required_signatures)
34
- break unless all_signatures_present
35
-
37
+ all_signatures_present = (signatures_present.size >= required_signatures)
38
+ break unless all_signatures_present
36
39
  end
37
40
 
38
41
  all_signatures_present
@@ -56,12 +59,16 @@ module BlockIo
56
59
 
57
60
  def self.finalizeTransaction(tx, inputs, signatures, input_address_data)
58
61
  # append signatures to the transaction and return its hexadecimal representation
59
-
60
- inputs.each do |input|
62
+
63
+ i = 0
64
+ loop do
61
65
  # for each input
66
+ input = inputs[i]
67
+ break if input.nil?
68
+ i += 1
62
69
 
63
- signatures_present = signatures.map{|x| x if x['input_index'] == input['input_index']}.compact.inject({}){|h,v| h[v['public_key']] = v['signature']; h}
64
- address_data = input_address_data.detect{|x| x['address'] == input['spending_address']} # contains public keys (ordered) and the address type
70
+ signatures_present = signatures.map{|x| x if x['input_index'].eql?(input['input_index'])}.compact.inject({}){|h,v| h[v['public_key']] = v['signature']; h}
71
+ address_data = input_address_data.detect{|x| x['address'].eql?(input['spending_address'])} # contains public keys (ordered) and the address type
65
72
  input_index = input['input_index']
66
73
  is_segwit = isSegwitAddressType?(address_data['address_type'])
67
74
  script_stack = (is_segwit ? tx.in[input_index].script_witness.stack : tx.in[input_index].script_sig)
@@ -74,16 +81,16 @@ module BlockIo
74
81
  current_signature = signatures_present[current_public_key]
75
82
 
76
83
  # no blank push necessary for P2PKH, P2WPKH, P2WPKH-over-P2SH
77
- script_stack << ([current_signature].pack("H*") + [Bitcoin::SIGHASH_TYPE[:all]].pack('C'))
78
- script_stack << [current_public_key].pack("H*")
84
+ script_stack << ([current_signature].pack('H*') + [Bitcoin::SIGHASH_TYPE[:all]].pack('C'))
85
+ script_stack << [current_public_key].pack('H*')
79
86
 
80
87
  # P2WPKH-over-P2SH required script_sig still
81
88
  tx.in[input_index].script_sig << (
82
89
  Bitcoin::Script.to_p2wpkh(
83
90
  Bitcoin::Key.new(:pubkey => current_public_key, :key_type => Bitcoin::Key::TYPES[:compressed]).hash160 # hash160 of the compressed pubkey
84
91
  ).to_payload
85
- ) if address_data['address_type'] == "P2WPKH-over-P2SH"
86
-
92
+ ) if address_data['address_type'].eql?('P2WPKH-over-P2SH')
93
+
87
94
  elsif ['P2SH', 'WITNESS_V0', 'P2WSH-over-P2SH'].include?(address_data['address_type']) then
88
95
  # P2SH will use script_sig as script_stack
89
96
  # P2WSH or P2WSH-over-P2SH input will use script_witness.stack as script_stack
@@ -94,23 +101,27 @@ module BlockIo
94
101
 
95
102
  signatures_added = 0
96
103
 
97
- address_data['public_keys'].each do |public_key|
104
+ j = 0
105
+ loop do
106
+ public_key = address_data['public_keys'][j]
107
+ break if public_key.nil?
108
+ j += 1
98
109
  next unless signatures_present.key?(public_key)
99
110
 
100
111
  # append signatures, no sighash needed, in correct order of public keys
101
112
  current_signature = signatures_present[public_key]
102
- script_stack << ([current_signature].pack("H*") + [Bitcoin::SIGHASH_TYPE[:all]].pack('C'))
113
+ script_stack << ([current_signature].pack('H*') + [Bitcoin::SIGHASH_TYPE[:all]].pack('C'))
103
114
 
104
115
  signatures_added += 1
105
116
 
106
117
  # required signatures added? break loop and move on
107
- break if signatures_added == address_data['required_signatures']
118
+ break if signatures_added.eql?(address_data['required_signatures'])
108
119
  end
109
120
 
110
121
  script_stack << script.last.to_payload
111
122
 
112
123
  # P2WSH-over-P2SH needs script_sig populated still
113
- tx.in[input_index].script_sig << Bitcoin::Script.to_p2wsh(script.last).to_payload if address_data['address_type'] == "P2WSH-over-P2SH"
124
+ tx.in[input_index].script_sig << Bitcoin::Script.to_p2wsh(script.last).to_payload if address_data['address_type'].eql?('P2WSH-over-P2SH')
114
125
 
115
126
  else
116
127
  raise "Unrecognized input address: #{address_data['address_type']}"
@@ -125,35 +136,35 @@ module BlockIo
125
136
  def self.getSigHashForInput(tx, input_index, input_data, input_address_data)
126
137
  # returns the sighash for the given input in bytes
127
138
 
128
- address_type = input_address_data["address_type"]
129
- input_value = (BigDecimal(input_data['input_value']) * BigDecimal(100000000)).to_i # in sats
139
+ address_type = input_address_data['address_type']
140
+ input_value = (BigDecimal(input_data['input_value']) * 100_000_000).to_i # in sats
130
141
  sighash = nil
131
142
 
132
- if address_type == "P2SH" then
143
+ if address_type.eql?('P2SH') then
133
144
  # P2SH addresses
134
145
 
135
- script = Bitcoin::Script.to_p2sh_multisig_script(input_address_data["required_signatures"], input_address_data["public_keys"])
146
+ script = Bitcoin::Script.to_p2sh_multisig_script(input_address_data['required_signatures'], input_address_data['public_keys'])
136
147
  sighash = tx.sighash_for_input(input_index, script.last)
137
148
 
138
- elsif address_type == "P2WSH-over-P2SH" or address_type == "WITNESS_V0" then
149
+ elsif address_type.eql?('P2WSH-over-P2SH') or address_type.eql?('WITNESS_V0') then
139
150
  # P2WSH-over-P2SH addresses
140
151
  # WITNESS_V0 addresses
141
152
 
142
- script = Bitcoin::Script.to_p2sh_multisig_script(input_address_data["required_signatures"], input_address_data["public_keys"])
153
+ script = Bitcoin::Script.to_p2sh_multisig_script(input_address_data['required_signatures'], input_address_data['public_keys'])
143
154
  sighash = tx.sighash_for_input(input_index, script.last, amount: input_value, sig_version: :witness_v0)
144
155
 
145
- elsif address_type == "P2WPKH-over-P2SH" or address_type == "P2WPKH" then
156
+ elsif address_type.eql?('P2WPKH-over-P2SH') or address_type.eql?('P2WPKH') then
146
157
  # P2WPKH-over-P2SH addresses
147
158
  # P2WPKH addresses
148
159
 
149
- pub_key = Bitcoin::Key.new(:pubkey => input_address_data['public_keys'].first, :key_type => Bitcoin::Key::TYPES[:compressed]) # compressed
160
+ pub_key = Bitcoin::Key.new(:pubkey => input_address_data['public_keys'][0], :key_type => Bitcoin::Key::TYPES[:compressed]) # compressed
150
161
  script = Bitcoin::Script.to_p2wpkh(pub_key.hash160)
151
162
  sighash = tx.sighash_for_input(input_index, script, amount: input_value, sig_version: :witness_v0)
152
163
 
153
- elsif address_type == "P2PKH" then
164
+ elsif address_type.eql?('P2PKH') then
154
165
  # P2PKH addresses
155
166
 
156
- pub_key = Bitcoin::Key.new(:pubkey => input_address_data['public_keys'].first, :key_type => Bitcoin::Key::TYPES[:compressed]) # compressed
167
+ pub_key = Bitcoin::Key.new(:pubkey => input_address_data['public_keys'][0], :key_type => Bitcoin::Key::TYPES[:compressed]) # compressed
157
168
  script = Bitcoin::Script.to_p2pkh(pub_key.hash160)
158
169
  sighash = tx.sighash_for_input(input_index, script)
159
170
 
@@ -168,7 +179,7 @@ module BlockIo
168
179
  def self.getDecryptionAlgorithm(user_key_algorithm = nil)
169
180
  # mainly used so existing unit tests do not break
170
181
 
171
- algorithm = ({}).merge(LEGACY_DECRYPTION_ALGORITHM)
182
+ algorithm = ({}).merge!(LEGACY_DECRYPTION_ALGORITHM)
172
183
 
173
184
  if !user_key_algorithm.nil? then
174
185
  algorithm[:pbkdf2_salt] = user_key_algorithm['pbkdf2_salt']
@@ -216,14 +227,14 @@ module BlockIo
216
227
 
217
228
  def self.sha256(value)
218
229
  # returns the hex of the hash of the given value
219
- OpenSSL::Digest::SHA256.digest(value).unpack("H*")[0]
230
+ OpenSSL::Digest::SHA256.digest(value).unpack1('H*')
220
231
  end
221
232
 
222
- def self.pinToAesKey(secret_pin, iterations = 2048, salt = "", hash_function = "SHA256", pbkdf2_phase1_key_length = 16, pbkdf2_phase2_key_length = 32)
233
+ def self.pinToAesKey(secret_pin, iterations = 2048, salt = '', hash_function = 'SHA256', pbkdf2_phase1_key_length = 16, pbkdf2_phase2_key_length = 32)
223
234
  # converts the pincode string to PBKDF2
224
235
  # returns a base64 version of PBKDF2 pincode
225
236
 
226
- raise Exception.new("Unknown hash function specified. Are you using current version of this library?") unless hash_function == "SHA256"
237
+ raise Exception.new('Unknown hash function specified. Are you using current version of this library?') unless hash_function.eql?('SHA256')
227
238
 
228
239
  part1 = OpenSSL::PKCS5.pbkdf2_hmac(
229
240
  secret_pin,
@@ -231,7 +242,7 @@ module BlockIo
231
242
  iterations/2,
232
243
  pbkdf2_phase1_key_length,
233
244
  OpenSSL::Digest::SHA256.new
234
- ).unpack("H*")[0]
245
+ ).unpack1('H*')
235
246
 
236
247
  part2 = OpenSSL::PKCS5.pbkdf2_hmac(
237
248
  part1,
@@ -241,80 +252,78 @@ module BlockIo
241
252
  OpenSSL::Digest::SHA256.new
242
253
  ) # binary
243
254
 
244
- [part2].pack("m0") # the base64 encryption key
255
+ [part2].pack('m0') # the base64 encryption key
245
256
 
246
257
  end
247
258
 
248
259
  # Decrypts a block of data (encrypted_data) given an encryption key
249
- def self.decrypt(encrypted_data, b64_enc_key, iv = nil, cipher_type = "AES-256-ECB", auth_tag = nil, auth_data = nil)
260
+ def self.decrypt(encrypted_data, b64_enc_key, iv = nil, cipher_type = 'AES-256-ECB', auth_tag = nil, auth_data = nil)
250
261
 
251
- raise Exception.new("Auth tag must be 16 bytes exactly.") unless auth_tag.nil? or auth_tag.size == 32
262
+ raise Exception.new('Auth tag must be 16 bytes exactly.') unless auth_tag.nil? or auth_tag.size.eql?(32)
252
263
 
253
264
  response = nil
254
265
 
255
266
  begin
256
267
  aes = OpenSSL::Cipher.new(cipher_type.downcase)
257
268
  aes.decrypt
258
- aes.key = b64_enc_key.unpack("m0")[0]
259
- aes.iv = [iv].pack("H*") unless iv.nil?
260
- aes.auth_tag = [auth_tag].pack("H*") unless auth_tag.nil?
261
- aes.auth_data = [auth_data].pack("H*") unless auth_data.nil?
262
- response = aes.update(encrypted_data.unpack("m0")[0]) << aes.final
269
+ aes.key = b64_enc_key.unpack1('m0')
270
+ aes.iv = [iv].pack('H*') unless iv.nil?
271
+ aes.auth_tag = [auth_tag].pack('H*') unless auth_tag.nil?
272
+ aes.auth_data = [auth_data].pack('H*') unless auth_data.nil?
273
+ response = aes.update(encrypted_data.unpack1('m0')) << aes.final
263
274
  rescue Exception => e
264
275
  # decryption failed, must be an invalid Secret PIN
265
- raise Exception.new("Invalid Secret PIN provided.")
276
+ raise Exception.new('Invalid Secret PIN provided.')
266
277
  end
267
278
 
268
279
  response
269
280
  end
270
281
 
271
282
  # Encrypts a block of data given an encryption key
272
- def self.encrypt(data, b64_enc_key, iv = nil, cipher_type = "AES-256-ECB", auth_data = nil)
283
+ def self.encrypt(data, b64_enc_key, iv = nil, cipher_type = 'AES-256-ECB', auth_data = nil)
273
284
  aes = OpenSSL::Cipher.new(cipher_type.downcase)
274
285
  aes.encrypt
275
- aes.key = b64_enc_key.unpack("m0")[0]
276
- aes.iv = [iv].pack("H*") unless iv.nil?
277
- aes.auth_data = [auth_data].pack("H*") unless auth_data.nil?
278
- result = [aes.update(data) << aes.final].pack("m0")
279
- auth_tag = (cipher_type.end_with?("-GCM") ? aes.auth_tag.unpack("H*")[0] : nil)
286
+ aes.key = b64_enc_key.unpack1('m0')
287
+ aes.iv = [iv].pack('H*') unless iv.nil?
288
+ aes.auth_data = [auth_data].pack('H*') unless auth_data.nil?
289
+ result = [aes.update(data) << aes.final].pack('m0')
290
+ auth_tag = (cipher_type.end_with?('-GCM') ? aes.auth_tag.unpack1('H*') : nil)
280
291
 
281
292
  {:aes_auth_tag => auth_tag, :aes_cipher_text => result, :aes_iv => iv, :aes_cipher => cipher_type, :aes_auth_data => auth_data}
282
293
 
283
294
  end
284
295
 
285
296
  # courtesy bitcoin-ruby
286
-
297
+ BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
287
298
  def self.int_to_base58(int_val, leading_zero_bytes=0)
288
- alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
289
- base58_val, base = "", alpha.size
299
+ base58_val, base = '', BASE58_ALPHABET.size
290
300
  while int_val > 0
291
301
  int_val, remainder = int_val.divmod(base)
292
- base58_val = alpha[remainder] << base58_val
302
+ base58_val = '' << BASE58_ALPHABET[remainder] << base58_val
293
303
  end
294
304
  base58_val
295
305
  end
296
306
 
297
307
  def self.base58_to_int(base58_val)
298
- alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
299
- int_val, base = 0, alpha.size
308
+ int_val, base = 0, BASE58_ALPHABET.size
300
309
  base58_val.reverse.each_char.with_index do |char,index|
301
- raise ArgumentError, "Value not a valid Base58 String." unless char_index = alpha.index(char)
310
+ raise ArgumentError, 'Value not a valid Base58 String.' unless char_index = BASE58_ALPHABET.index(char)
302
311
  int_val += char_index*(base**index)
303
312
  end
304
313
  int_val
305
314
  end
306
315
 
307
316
  def self.encode_base58(hex)
308
- leading_zero_bytes = (hex.match(/^([0]+)/) ? $1 : "").size / 2
309
- ("1"*leading_zero_bytes) << Helper.int_to_base58( hex.to_i(16) )
317
+ leading_zero_bytes = (hex.match(/^([0]+)/) ? $1 : '').size / 2
318
+ ('1'*leading_zero_bytes) << Helper.int_to_base58(hex.to_i(16))
310
319
  end
311
320
 
312
321
  def self.decode_base58(base58_val)
313
322
  s = Helper.base58_to_int(base58_val).to_s(16)
314
- s = (s.bytesize.odd? ? ("0" << s) : s)
315
- s = "" if s == "00"
316
- leading_zero_bytes = (base58_val.match(/^([1]+)/) ? $1 : "").size
317
- s = ("00"*leading_zero_bytes) << s if leading_zero_bytes > 0
323
+ s = (s.bytesize.odd? ? ('0' << s) : s)
324
+ s = '' if s.eql?('00')
325
+ leading_zero_bytes = (base58_val.match(/^([1]+)/) ? $1 : '').size
326
+ s = ('00'*leading_zero_bytes) << s if leading_zero_bytes > 0
318
327
  s
319
328
  end
320
329
  end
data/lib/block_io/key.rb CHANGED
@@ -18,9 +18,9 @@ module BlockIo
18
18
  # create a private/public key pair from a given passphrase
19
19
  # use a long, random passphrase. your security depends on the passphrase's entropy.
20
20
 
21
- raise Exception.new("Must provide passphrase at least 8 characters long.") if passphrase.nil? or passphrase.length < 8
21
+ raise Exception.new('Must provide passphrase at least 8 characters long.') if passphrase.nil? or passphrase.length < 8
22
22
 
23
- hashed_key = Helper.sha256([passphrase].pack("H*")) # must pass bytes to sha256
23
+ hashed_key = Helper.sha256([passphrase].pack('H*')) # must pass bytes to sha256
24
24
 
25
25
  # modding is for backward compatibility with legacy bitcoinjs
26
26
  BlockIo::Key.from_private_key_hex((hashed_key.to_i(16) % ECDSA::Group::Secp256k1.order).to_s(16))
@@ -1,3 +1,3 @@
1
1
  module BlockIo
2
- VERSION = "3.0.5"
2
+ VERSION = '3.1.0'
3
3
  end
data/lib/block_io.rb CHANGED
@@ -1,9 +1,8 @@
1
- require "http"
2
1
  require "oj"
3
2
  require "bitcoin"
4
3
  require "openssl"
5
4
  require "securerandom"
6
- require "connection_pool"
5
+ require "typhoeus"
7
6
 
8
7
  require_relative "block_io/version"
9
8
  require_relative "block_io/helper"
@@ -5,19 +5,12 @@ describe "Client" do
5
5
  before(:each) do
6
6
  @api_key = "0000-0000-0000-0000"
7
7
  @req_params = {:to_address => "QTLcyTFrH7T6kqUsi1VV2mJVXmX3AmwUNH", :amounts => "0.248"}
8
- @headers = {
9
- 'Accept' => 'application/json',
10
- 'Connection' => 'Keep-Alive',
11
- 'Content-Type' => 'application/json; charset=UTF-8',
12
- 'Host' => 'block.io',
13
- 'User-Agent' => "gem:block_io:#{BlockIo::VERSION}"
14
- }
15
8
 
16
9
  @prepare_transaction_response = File.new("spec/test-cases/json/prepare_transaction_response_with_blockio_fee_and_expected_unsigned_txid.json").read
17
10
  @stub1 = stub_request(:post, "https://block.io/api/v2/prepare_transaction").
18
11
  with(
19
12
  body: @req_params.merge({:api_key => @api_key}).to_json,
20
- headers: @headers).
13
+ headers: SPEC_REQUEST_HEADERS).
21
14
  to_return(status: 200, body: @prepare_transaction_response, headers: {})
22
15
 
23
16
  @create_and_sign_transaction_response = File.new("spec/test-cases/json/create_and_sign_transaction_response_with_blockio_fee_and_expected_unsigned_txid.json").read
data/spec/client_spec.rb CHANGED
@@ -3,19 +3,12 @@ describe "Client.prepare_transaction" do
3
3
  before(:each) do
4
4
  @api_key = "0000-0000-0000-0000"
5
5
  @req_params = {:to_address => "QTLcyTFrH7T6kqUsi1VV2mJVXmX3AmwUNH", :amounts => "0.248"}
6
- @headers = {
7
- 'Accept' => 'application/json',
8
- 'Connection' => 'Keep-Alive',
9
- 'Content-Type' => 'application/json; charset=UTF-8',
10
- 'Host' => 'block.io',
11
- 'User-Agent' => "gem:block_io:#{BlockIo::VERSION}"
12
- }
13
6
 
14
7
  @prepare_transaction_response = File.new("spec/test-cases/json/prepare_transaction_response.json").read
15
8
  @stub1 = stub_request(:post, "https://block.io/api/v2/prepare_transaction").
16
9
  with(
17
10
  body: @req_params.merge({:api_key => @api_key}).to_json,
18
- headers: @headers).
11
+ headers: SPEC_REQUEST_HEADERS).
19
12
  to_return(status: 200, body: @prepare_transaction_response, headers: {})
20
13
 
21
14
  @create_and_sign_transaction_response = File.new("spec/test-cases/json/create_and_sign_transaction_response.json").read
data/spec/dtrust_spec.rb CHANGED
@@ -8,14 +8,6 @@ describe "Client.prepare_dtrust_transaction" do
8
8
  "6c1cefdfd9187b36b36c3698c1362642083dcc1941dc76d751481d3aa29ca65"]
9
9
  @to_address = "QcnYiN3t3foHxHv7CnqXrmRoiMkADhapZw"
10
10
  @amount = "0.00020000"
11
- @headers = {
12
- 'Accept' => 'application/json',
13
- 'Connection' => 'Keep-Alive',
14
- 'Content-Type' => 'application/json; charset=UTF-8',
15
- 'Host' => 'block.io',
16
- 'User-Agent' => "gem:block_io:#{BlockIo::VERSION}"
17
- }
18
-
19
11
  end
20
12
 
21
13
  context "p2sh" do
@@ -29,7 +21,7 @@ describe "Client.prepare_dtrust_transaction" do
29
21
  @stub1 = stub_request(:post, "https://block.io/api/v2/prepare_dtrust_transaction").
30
22
  with(
31
23
  body: @req_params.merge({:api_key => @api_key}).to_json,
32
- headers: @headers).
24
+ headers: SPEC_REQUEST_HEADERS).
33
25
  to_return(status: 200, body: @prepare_dtrust_transaction_response_p2sh, headers: {})
34
26
 
35
27
  @create_and_sign_transaction_response_dtrust_p2sh_3_of_5_keys = File.new("spec/test-cases/json/create_and_sign_transaction_response_dtrust_p2sh_3_of_5_keys.json").read
@@ -76,7 +68,7 @@ describe "Client.prepare_dtrust_transaction" do
76
68
  @stub1 = stub_request(:post, "https://block.io/api/v2/prepare_dtrust_transaction").
77
69
  with(
78
70
  body: @req_params.merge({:api_key => @api_key}).to_json,
79
- headers: @headers).
71
+ headers: SPEC_REQUEST_HEADERS).
80
72
  to_return(status: 200, body: @prepare_dtrust_transaction_response_p2wsh_over_p2sh, headers: {})
81
73
 
82
74
  @create_and_sign_transaction_response_dtrust_p2wsh_over_p2sh_3_of_5_keys = File.new("spec/test-cases/json/create_and_sign_transaction_response_dtrust_p2wsh_over_p2sh_3_of_5_keys.json").read
@@ -125,7 +117,7 @@ describe "Client.prepare_dtrust_transaction" do
125
117
  @stub1 = stub_request(:post, "https://block.io/api/v2/prepare_dtrust_transaction").
126
118
  with(
127
119
  body: @req_params.merge({:api_key => @api_key}).to_json,
128
- headers: @headers).
120
+ headers: SPEC_REQUEST_HEADERS).
129
121
  to_return(status: 200, body: @prepare_dtrust_transaction_response_witness_v0, headers: {})
130
122
 
131
123
  @create_and_sign_transaction_response_dtrust_witness_v0_3_of_5_keys = File.new("spec/test-cases/json/create_and_sign_transaction_response_dtrust_witness_v0_3_of_5_keys.json").read
data/spec/spec_helper.rb CHANGED
@@ -3,3 +3,5 @@ require_relative '../lib/block_io'
3
3
  require 'webmock/rspec'
4
4
 
5
5
  WebMock.disable_net_connect!
6
+
7
+ SPEC_REQUEST_HEADERS = BlockIo::Client.new(:api_key => "0000-0000-0000-0000").api_request_headers.freeze
data/spec/sweep_spec.rb CHANGED
@@ -6,20 +6,12 @@ describe "Client.prepare_sweep_transaction" do
6
6
  @req_params = {:to_address => "QTLcyTFrH7T6kqUsi1VV2mJVXmX3AmwUNH", :public_key => "021499295873879c280c31fff94845a6153142e76b84a29afc92128d482857ed93"}
7
7
  @req_params_with_wif = {:to_address => "QTLcyTFrH7T6kqUsi1VV2mJVXmX3AmwUNH", :private_key => @wif}
8
8
 
9
- @headers = {
10
- 'Accept' => 'application/json',
11
- 'Connection' => 'Keep-Alive',
12
- 'Content-Type' => 'application/json; charset=UTF-8',
13
- 'Host' => 'block.io',
14
- 'User-Agent' => "gem:block_io:#{BlockIo::VERSION}"
15
- }
16
-
17
9
  # since @network won't be set, the library will call get_balance first on prepare_sweep_transaction
18
10
  @get_balance_response = File.new("spec/test-cases/json/get_balance_response.json").read
19
11
  @stub_network = stub_request(:post, "https://block.io/api/v2/get_balance").
20
12
  with(
21
13
  body: {:api_key => @api_key}.to_json,
22
- headers: @headers).
14
+ headers: SPEC_REQUEST_HEADERS).
23
15
  to_return(status: 200, body: @get_balance_response, headers: {})
24
16
 
25
17
  end
@@ -34,7 +26,7 @@ describe "Client.prepare_sweep_transaction" do
34
26
  @stub1 = stub_request(:post, "https://block.io/api/v2/prepare_sweep_transaction").
35
27
  with(
36
28
  body: @req_params.merge({:api_key => @api_key}).to_json,
37
- headers: @headers).
29
+ headers: SPEC_REQUEST_HEADERS).
38
30
  to_return(status: 200, body: @prepare_sweep_transaction_response_p2pkh, headers: {})
39
31
 
40
32
  @create_and_sign_transaction_response_sweep_p2pkh = File.new("spec/test-cases/json/create_and_sign_transaction_response_sweep_p2pkh.json").read
@@ -63,7 +55,7 @@ describe "Client.prepare_sweep_transaction" do
63
55
  @stub1 = stub_request(:post, "https://block.io/api/v2/prepare_sweep_transaction").
64
56
  with(
65
57
  body: @req_params.merge({:api_key => @api_key}).to_json,
66
- headers: @headers).
58
+ headers: SPEC_REQUEST_HEADERS).
67
59
  to_return(status: 200, body: @prepare_sweep_transaction_response_p2wpkh_over_p2sh, headers: {})
68
60
 
69
61
  @create_and_sign_transaction_response_sweep_p2wpkh_over_p2sh = File.new("spec/test-cases/json/create_and_sign_transaction_response_sweep_p2wpkh_over_p2sh.json").read
@@ -92,7 +84,7 @@ describe "Client.prepare_sweep_transaction" do
92
84
  @stub1 = stub_request(:post, "https://block.io/api/v2/prepare_sweep_transaction").
93
85
  with(
94
86
  body: @req_params.merge({:api_key => @api_key}).to_json,
95
- headers: @headers).
87
+ headers: SPEC_REQUEST_HEADERS).
96
88
  to_return(status: 200, body: @prepare_sweep_transaction_response_p2wpkh, headers: {})
97
89
 
98
90
  @create_and_sign_transaction_response_sweep_p2wpkh = File.new("spec/test-cases/json/create_and_sign_transaction_response_sweep_p2wpkh.json").read
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: block_io
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.5
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Atif Nazir
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-05-30 00:00:00.000000000 Z
11
+ date: 2022-10-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: '1.16'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: '3.0'
22
+ version: '3'
23
23
  type: :development
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,7 +29,7 @@ dependencies:
29
29
  version: '1.16'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '3.0'
32
+ version: '3'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: rake
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -37,9 +37,9 @@ dependencies:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
39
  version: '13.0'
40
- - - ">="
40
+ - - "<"
41
41
  - !ruby/object:Gem::Version
42
- version: '13.0'
42
+ version: '14'
43
43
  type: :development
44
44
  prerelease: false
45
45
  version_requirements: !ruby/object:Gem::Requirement
@@ -47,89 +47,89 @@ dependencies:
47
47
  - - "~>"
48
48
  - !ruby/object:Gem::Version
49
49
  version: '13.0'
50
- - - ">="
50
+ - - "<"
51
51
  - !ruby/object:Gem::Version
52
- version: '13.0'
52
+ version: '14'
53
53
  - !ruby/object:Gem::Dependency
54
54
  name: rspec
55
55
  requirement: !ruby/object:Gem::Requirement
56
56
  requirements:
57
57
  - - "~>"
58
58
  - !ruby/object:Gem::Version
59
- version: '3.6'
60
- - - ">="
59
+ version: '3.0'
60
+ - - "<"
61
61
  - !ruby/object:Gem::Version
62
- version: '3.6'
62
+ version: '4'
63
63
  type: :development
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
67
  - - "~>"
68
68
  - !ruby/object:Gem::Version
69
- version: '3.6'
70
- - - ">="
69
+ version: '3.0'
70
+ - - "<"
71
71
  - !ruby/object:Gem::Version
72
- version: '3.6'
72
+ version: '4'
73
73
  - !ruby/object:Gem::Dependency
74
74
  name: webmock
75
75
  requirement: !ruby/object:Gem::Requirement
76
76
  requirements:
77
77
  - - "~>"
78
78
  - !ruby/object:Gem::Version
79
- version: '3.12'
79
+ version: '3.0'
80
80
  - - "<"
81
81
  - !ruby/object:Gem::Version
82
- version: '4.0'
82
+ version: '4'
83
83
  type: :development
84
84
  prerelease: false
85
85
  version_requirements: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '3.12'
89
+ version: '3.0'
90
90
  - - "<"
91
91
  - !ruby/object:Gem::Version
92
- version: '4.0'
92
+ version: '4'
93
93
  - !ruby/object:Gem::Dependency
94
94
  name: bitcoinrb
95
95
  requirement: !ruby/object:Gem::Requirement
96
96
  requirements:
97
- - - "~>"
97
+ - - ">="
98
98
  - !ruby/object:Gem::Version
99
- version: '1.0'
99
+ version: 1.2.1
100
100
  - - "<"
101
101
  - !ruby/object:Gem::Version
102
- version: 1.2.0
102
+ version: '2'
103
103
  type: :runtime
104
104
  prerelease: false
105
105
  version_requirements: !ruby/object:Gem::Requirement
106
106
  requirements:
107
- - - "~>"
107
+ - - ">="
108
108
  - !ruby/object:Gem::Version
109
- version: '1.0'
109
+ version: 1.2.1
110
110
  - - "<"
111
111
  - !ruby/object:Gem::Version
112
- version: 1.2.0
112
+ version: '2'
113
113
  - !ruby/object:Gem::Dependency
114
- name: http
114
+ name: typhoeus
115
115
  requirement: !ruby/object:Gem::Requirement
116
116
  requirements:
117
- - - ">="
117
+ - - "~>"
118
118
  - !ruby/object:Gem::Version
119
- version: 4.4.1
119
+ version: '1.0'
120
120
  - - "<"
121
121
  - !ruby/object:Gem::Version
122
- version: '6.0'
122
+ version: '2'
123
123
  type: :runtime
124
124
  prerelease: false
125
125
  version_requirements: !ruby/object:Gem::Requirement
126
126
  requirements:
127
- - - ">="
127
+ - - "~>"
128
128
  - !ruby/object:Gem::Version
129
- version: 4.4.1
129
+ version: '1.0'
130
130
  - - "<"
131
131
  - !ruby/object:Gem::Version
132
- version: '6.0'
132
+ version: '2'
133
133
  - !ruby/object:Gem::Dependency
134
134
  name: oj
135
135
  requirement: !ruby/object:Gem::Requirement
@@ -139,7 +139,7 @@ dependencies:
139
139
  version: '3.0'
140
140
  - - "<"
141
141
  - !ruby/object:Gem::Version
142
- version: '4.0'
142
+ version: '4'
143
143
  type: :runtime
144
144
  prerelease: false
145
145
  version_requirements: !ruby/object:Gem::Requirement
@@ -149,27 +149,7 @@ dependencies:
149
149
  version: '3.0'
150
150
  - - "<"
151
151
  - !ruby/object:Gem::Version
152
- version: '4.0'
153
- - !ruby/object:Gem::Dependency
154
- name: connection_pool
155
- requirement: !ruby/object:Gem::Requirement
156
- requirements:
157
- - - ">="
158
- - !ruby/object:Gem::Version
159
- version: '2.2'
160
- - - "<"
161
- - !ruby/object:Gem::Version
162
- version: '3.0'
163
- type: :runtime
164
- prerelease: false
165
- version_requirements: !ruby/object:Gem::Requirement
166
- requirements:
167
- - - ">="
168
- - !ruby/object:Gem::Version
169
- version: '2.2'
170
- - - "<"
171
- - !ruby/object:Gem::Version
172
- version: '3.0'
152
+ version: '4'
173
153
  description: This Ruby Gem is the official reference client for the Block.io payments
174
154
  API. To use this, you will need the Dogecoin, Bitcoin, or Litecoin API key(s) from
175
155
  Block.io. Go ahead, sign up :)