sibit 0.12.5 → 0.12.6

Sign up to get free protection for your applications and to get access to all the features.
data/lib/sibit/version.rb CHANGED
@@ -26,5 +26,5 @@
26
26
  # License:: MIT
27
27
  class Sibit
28
28
  # Current version of the library.
29
- VERSION = '0.12.5'
29
+ VERSION = '0.12.6'
30
30
  end
data/lib/sibit.rb CHANGED
@@ -20,131 +20,71 @@
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  # SOFTWARE.
22
22
 
23
- require 'net/http'
24
- require 'uri'
25
23
  require 'bitcoin'
26
- require 'json'
27
- require 'cgi'
28
24
  require_relative 'sibit/version'
25
+ require_relative 'sibit/log'
26
+ require_relative 'sibit/blockchain'
29
27
 
30
28
  # Sibit main class.
31
29
  #
32
- # It works through the Blockchain API at the moment:
33
- # https://www.blockchain.com/api/blockchain_api
34
- #
35
30
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
36
31
  # Copyright:: Copyright (c) 2019 Yegor Bugayenko
37
32
  # License:: MIT
38
33
  class Sibit
39
- # If something goes wrong.
40
- class Error < StandardError; end
41
-
42
- # Fake one, which is useful for testing.
43
- class Fake
44
- def price(_cur = 'USD')
45
- 4_000
46
- end
47
-
48
- def fees
49
- { S: 12, M: 45, L: 100, XL: 200 }
50
- end
51
-
52
- def generate
53
- 'fd2333686f49d8647e1ce8d5ef39c304520b08f3c756b67068b30a3db217dcb2'
54
- end
55
-
56
- def create(_pvt)
57
- '1JvCsJtLmCxEk7ddZFnVkGXpr9uhxZPmJi'
58
- end
59
-
60
- def balance(_address)
61
- 100_000_000
62
- end
63
-
64
- def pay(_amount, _fee, _sources, _target, _change)
65
- '9dfe55a30b5ee732005158c589179a398117117a68d21531fb6c78b85b544c54'
66
- end
67
-
68
- def latest
69
- '00000000000000000008df8a6e1b61d1136803ac9791b8725235c9f780b4ed71'
70
- end
71
-
72
- def get_json(_uri)
73
- {}
74
- end
75
- end
76
-
77
- # This HTTP client will be used by default.
78
- def self.default_http
79
- http = Net::HTTP.new('blockchain.info', 443)
80
- http.use_ssl = true
81
- http
82
- end
83
-
84
- # This HTTP client with proxy.
85
- def self.proxy_http(addr)
86
- host, port = addr.split(':')
87
- http = Net::HTTP.new('blockchain.info', 443, host, port.to_i)
88
- http.use_ssl = true
89
- http
90
- end
91
-
92
34
  # Constructor.
93
35
  #
94
36
  # You may provide the log you want to see the messages in. If you don't
95
37
  # provide anything, the console will be used. The object you provide
96
38
  # has to respond to the method +info+ or +puts+ in order to receive logging
97
39
  # messages.
98
- def initialize(log: STDOUT, http: Sibit.default_http, dry: false, attempts: 1)
99
- @log = log
100
- @http = http
101
- @dry = dry
102
- @attempts = attempts
103
- end
104
-
105
- # Current price of 1 BTC.
106
- def price(cur = 'USD')
107
- h = get_json('/ticker')[cur.upcase]
108
- raise Error, "Unrecognized currency #{cur}" if h.nil?
109
- h['15m']
40
+ #
41
+ # It is recommended to wrap the API in a RetriableProxy from
42
+ # retriable_proxy gem and to configure it to retry on Sibit::Error:
43
+ #
44
+ # RetriableProxy.for_object(api, on: Sibit::Error)
45
+ #
46
+ # This will help you avoid some temporary network issues.
47
+ #
48
+ # The +api+ argument can be an object or an array of objects. If an array
49
+ # is provided, we will make an attempt to try them one by one, until
50
+ # one of them succeedes.
51
+ def initialize(log: STDOUT, api: Sibit::Blockchain.new(log: Sibit::Log.new(log)))
52
+ @log = Sibit::Log.new(log)
53
+ @api = api
54
+ end
55
+
56
+ # Current price of 1 BTC in USD (or another currency), float returned.
57
+ def price(currency = 'USD')
58
+ first_one do |api|
59
+ api.price(currency)
60
+ end
110
61
  end
111
62
 
112
63
  # Generates new Bitcon private key and returns in Hash160 format.
113
64
  def generate
114
65
  key = Bitcoin::Key.generate.priv
115
- info("Bitcoin private key generated: #{key[0..8]}...")
66
+ @log.info("Bitcoin private key generated: #{key[0..8]}...")
116
67
  key
117
68
  end
118
69
 
119
70
  # Creates Bitcon address using the private key in Hash160 format.
120
71
  def create(pvt)
121
- key(pvt).addr
72
+ key = Bitcoin::Key.new
73
+ key.priv = pvt
74
+ key.addr
122
75
  end
123
76
 
124
77
  # Gets the balance of the address, in satoshi.
125
78
  def balance(address)
126
- json = get_json("/rawaddr/#{address}")
127
- info("Total transactions: #{json['n_tx']}")
128
- info("Received/sent: #{json['total_received']}/#{json['total_sent']}")
129
- json['final_balance']
79
+ first_one do |api|
80
+ api.balance(address)
81
+ end
130
82
  end
131
83
 
132
84
  # Get recommended fees, in satoshi per byte. The method returns
133
85
  # a hash: { S: 12, M: 45, L: 100, XL: 200 }
134
86
  def fees
135
- json = JSON.parse(
136
- Net::HTTP.get(
137
- URI('https://bitcoinfees.earn.com/api/v1/fees/recommended')
138
- )
139
- )
140
- info("Current recommended Bitcoin fees: \
141
- #{json['hourFee']}/#{json['halfHourFee']}/#{json['fastestFee']} sat/byte")
142
- {
143
- S: json['hourFee'] / 3,
144
- M: json['hourFee'],
145
- L: json['halfHourFee'],
146
- XL: json['fastestFee']
147
- }
87
+ first_one(&:fees)
148
88
  end
149
89
 
150
90
  # Sends a payment and returns the transaction hash.
@@ -165,192 +105,32 @@ class Sibit
165
105
  # +target+: the target address to send to
166
106
  # +change+: the address where the change has to be sent to
167
107
  def pay(amount, fee, sources, target, change)
168
- p = price
169
- satoshi = satoshi(amount)
170
- f = mfee(fee, size_of(amount, sources))
171
- satoshi += f if f.negative?
172
- raise Error, "The fee #{f.abs} covers the entire amount" if satoshi.zero?
173
- raise Error, "The fee #{f.abs} is bigger than the amount #{satoshi}" if satoshi.negative?
174
- builder = Bitcoin::Builder::TxBuilder.new
175
- unspent = 0
176
- size = 100
177
- utxos = get_json(
178
- "/unspent?active=#{sources.keys.join('|')}&limit=1000"
179
- )['unspent_outputs']
180
- info("#{utxos.count} UTXOs found, these will be used \
181
- (value/confirmations at tx_hash):")
182
- utxos.each do |utxo|
183
- unspent += utxo['value']
184
- builder.input do |i|
185
- i.prev_out(utxo['tx_hash_big_endian'])
186
- i.prev_out_index(utxo['tx_output_n'])
187
- i.prev_out_script = [utxo['script']].pack('H*')
188
- address = Bitcoin::Script.new([utxo['script']].pack('H*')).get_address
189
- i.signature_key(key(sources[address]))
190
- end
191
- size += 180
192
- info(" #{num(utxo['value'], p)}/#{utxo['confirmations']} at #{utxo['tx_hash_big_endian']}")
193
- break if unspent > satoshi
108
+ first_one do |api|
109
+ api.pay(amount, fee, sources, target, change)
194
110
  end
195
- if unspent < satoshi
196
- raise Error, "Not enough funds to send #{num(satoshi, p)}, only #{num(unspent, p)} left"
197
- end
198
- builder.output(satoshi, target)
199
- f = mfee(fee, size)
200
- tx = builder.tx(
201
- input_value: unspent,
202
- leave_fee: true,
203
- extra_fee: [f, Bitcoin.network[:min_tx_fee]].max,
204
- change_address: change
205
- )
206
- left = unspent - tx.outputs.map(&:value).inject(&:+)
207
- info("A new Bitcoin transaction #{tx.hash} prepared:
208
- #{tx.in.count} input#{tx.in.count > 1 ? 's' : ''}:
209
- #{tx.inputs.map { |i| " in: #{i.prev_out.bth}:#{i.prev_out_index}" }.join("\n ")}
210
- #{tx.out.count} output#{tx.out.count > 1 ? 's' : ''}:
211
- #{tx.outputs.map { |o| "out: #{o.script.bth} / #{num(o.value, p)}" }.join("\n ")}
212
- Min tx fee: #{num(Bitcoin.network[:min_tx_fee], p)}
213
- Fee requested: #{num(f, p)} as \"#{fee}\"
214
- Fee left: #{num(left, p)}
215
- Tx size: #{size} bytes
216
- Unspent: #{num(unspent, p)}
217
- Amount: #{num(satoshi, p)}
218
- Target address: #{target}
219
- Change address is #{change}")
220
- post_tx(tx.to_payload.bth) unless @dry
221
- tx.hash
222
111
  end
223
112
 
224
113
  # Gets the hash of the latest block.
225
114
  def latest
226
- get_json('/latestblock')['hash']
227
- end
228
-
229
- # Send GET request to the Blockchain API and return JSON response.
230
- # This method will also log the process and will validate the
231
- # response for correctness.
232
- def get_json(uri)
233
- start = Time.now
234
- attempt = 0
235
- begin
236
- res = @http.get(
237
- uri,
238
- 'Accept' => 'text/plain',
239
- 'User-Agent' => user_agent,
240
- 'Accept-Encoding' => ''
241
- )
242
- raise Error, "Failed to retrieve #{uri} (#{res.code}): #{res.body}" unless res.code == '200'
243
- info("GET #{uri}: #{res.code}/#{res.body.length}b in #{age(start)}")
244
- JSON.parse(res.body)
245
- rescue StandardError => e
246
- attempt += 1
247
- raise e if attempt >= @attempts
248
- retry
249
- end
115
+ first_one(&:latest)
250
116
  end
251
117
 
252
118
  private
253
119
 
254
- def num(satoshi, usd)
255
- format(
256
- '%<satoshi>ss/$%<dollars>0.2f',
257
- satoshi: satoshi.to_s.gsub(/\d(?=(...)+$)/, '\0,'),
258
- dollars: satoshi * usd / 100_000_000
259
- )
260
- end
261
-
262
- # Convert text to amount.
263
- def satoshi(amount)
264
- return amount if amount.is_a?(Integer)
265
- raise Error, 'Amount should either be a String or Integer' unless amount.is_a?(String)
266
- return (amount.gsub(/BTC$/, '').to_f * 100_000_000).to_i if amount.end_with?('BTC')
267
- raise Error, "Can't understand the amount #{amount.inspect}"
268
- end
269
-
270
- # Calculates a fee in satoshi for the transaction of the specified size.
271
- # The +fee+ argument could be a number in satoshi, in which case it will
272
- # be returned as is, or a string like "XL" or "S", in which case the
273
- # fee will be calculated using the +size+ argument (which is the size
274
- # of the transaction in bytes).
275
- def mfee(fee, size)
276
- return fee.to_i if fee.is_a?(Integer)
277
- raise Error, 'Fee should either be a String or Integer' unless fee.is_a?(String)
278
- mul = 1
279
- if fee.start_with?('+', '-')
280
- mul = -1 if fee.start_with?('-')
281
- fee = fee[1..-1]
282
- end
283
- sat = fees[fee.to_sym]
284
- raise Error, "Can't understand the fee: #{fee.inspect}" if sat.nil?
285
- mul * sat * size
286
- end
287
-
288
- # Make key from private key string in Hash160.
289
- def key(hash160)
290
- key = Bitcoin::Key.new
291
- key.priv = hash160
292
- key
293
- end
294
-
295
- def age(start)
296
- "#{((Time.now - start) * 1000).round}ms"
297
- end
298
-
299
- def post_tx(body)
300
- start = Time.now
301
- attempt = 0
302
- begin
303
- uri = '/pushtx'
304
- res = @http.post(
305
- '/pushtx',
306
- "tx=#{CGI.escape(body)}",
307
- 'Accept' => 'text/plain',
308
- 'User-Agent' => user_agent,
309
- 'Accept-Encoding' => '',
310
- 'Content-Type' => 'application/x-www-form-urlencoded'
311
- )
312
- raise Error, "Failed to post tx to #{uri}: #{res.code}\n#{res.body}" unless res.code == '200'
313
- info("POST #{uri}: #{res.code} in #{age(start)}")
314
- rescue StandardError => e
315
- attempt += 1
316
- raise e if attempt >= @attempts
317
- retry
318
- end
319
- end
320
-
321
- # Calculate an approximate size of the transaction.
322
- def size_of(amount, sources)
323
- satoshi = satoshi(amount)
324
- builder = Bitcoin::Builder::TxBuilder.new
325
- unspent = 0
326
- size = 100
327
- utxos = get_json(
328
- "/unspent?active=#{sources.keys.join('|')}&limit=1000"
329
- )['unspent_outputs']
330
- utxos.each do |utxo|
331
- unspent += utxo['value']
332
- builder.input do |i|
333
- i.prev_out(utxo['tx_hash_big_endian'])
334
- i.prev_out_index(utxo['tx_output_n'])
335
- i.prev_out_script = [utxo['script']].pack('H*')
336
- address = Bitcoin::Script.new([utxo['script']].pack('H*')).get_address
337
- i.signature_key(key(sources[address]))
120
+ def first_one
121
+ return yield @api unless @api.is_a?(Array)
122
+ done = false
123
+ result = nil
124
+ @api.each do |api|
125
+ begin
126
+ result = yield api
127
+ done = true
128
+ break
129
+ rescue Sibit::Error => e
130
+ @log.info("The API #{api.class.name} failed: #{e.message}")
338
131
  end
339
- size += 180
340
- break if unspent > satoshi
341
132
  end
342
- size
343
- end
344
-
345
- def info(msg)
346
- if @log.respond_to?(:info)
347
- @log.info(msg)
348
- elsif @log.respond_to?(:puts)
349
- @log.puts(msg)
350
- end
351
- end
352
-
353
- def user_agent
354
- "Anonymous/#{Sibit::VERSION}"
133
+ raise Sibit::Error, 'No APIs managed to succeed' unless done
134
+ result
355
135
  end
356
136
  end
data/sibit.gemspec CHANGED
@@ -51,9 +51,11 @@ and Ruby 2.3+.'
51
51
  s.add_runtime_dependency 'backtrace', '~> 0.3'
52
52
  s.add_runtime_dependency 'bitcoin', '0.2.0'
53
53
  s.add_runtime_dependency 'json', '~> 2'
54
+ s.add_runtime_dependency 'retriable_proxy', '1.0.2'
54
55
  s.add_runtime_dependency 'slop', '~> 4.6'
56
+ s.add_development_dependency 'aruba', '~> 0.14.1'
55
57
  s.add_development_dependency 'codecov', '0.1.10'
56
- s.add_development_dependency 'cucumber', '1.3.17'
58
+ s.add_development_dependency 'cucumber', '~> 1.3.17'
57
59
  s.add_development_dependency 'debase', '0.2.2'
58
60
  s.add_development_dependency 'minitest', '5.5.0'
59
61
  s.add_development_dependency 'rake', '12.0.0'
@@ -61,5 +63,5 @@ and Ruby 2.3+.'
61
63
  s.add_development_dependency 'rubocop', '0.61.0'
62
64
  s.add_development_dependency 'rubocop-rspec', '1.31.0'
63
65
  s.add_development_dependency 'ruby-debug-ide', '0.6.1'
64
- s.add_development_dependency 'webmock', '3.4.2'
66
+ s.add_development_dependency 'webmock', '3.7.6'
65
67
  end
data/test/test_btc.rb ADDED
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2019 Yegor Bugayenko
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the 'Software'), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require 'minitest/autorun'
24
+ require 'webmock/minitest'
25
+ require 'json'
26
+ require_relative '../lib/sibit'
27
+ require_relative '../lib/sibit/btc'
28
+
29
+ # Sibit::Btc test.
30
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
31
+ # Copyright:: Copyright (c) 2019 Yegor Bugayenko
32
+ # License:: MIT
33
+ class TestBtc < Minitest::Test
34
+ def test_get_zero_balance
35
+ stub_request(
36
+ :get,
37
+ 'https://chain.api.btc.com/v3/address/1MZT1fa6y8H9UmbZV6HqKF4UY41o9MGT5f/unspent'
38
+ ).to_return(body: '{"data":{"list":[]}}')
39
+ sibit = Sibit::Btc.new
40
+ balance = sibit.balance('1MZT1fa6y8H9UmbZV6HqKF4UY41o9MGT5f')
41
+ assert(balance.is_a?(Integer))
42
+ assert_equal(0, balance)
43
+ end
44
+
45
+ def test_get_balance
46
+ stub_request(
47
+ :get,
48
+ 'https://chain.api.btc.com/v3/address/1MZT1fa6y8H9UmbZV6HqKF4UY41o9MGT5f/unspent'
49
+ ).to_return(body: '{"data":{"list":[{"value":123}]}}')
50
+ sibit = Sibit::Btc.new
51
+ balance = sibit.balance('1MZT1fa6y8H9UmbZV6HqKF4UY41o9MGT5f')
52
+ assert(balance.is_a?(Integer))
53
+ assert_equal(123, balance)
54
+ end
55
+ end
data/test/test_fake.rb ADDED
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2019 Yegor Bugayenko
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the 'Software'), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require 'minitest/autorun'
24
+ require 'webmock/minitest'
25
+ require 'json'
26
+ require_relative '../lib/sibit/fake'
27
+
28
+ # Sibit::Fake test.
29
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
30
+ # Copyright:: Copyright (c) 2019 Yegor Bugayenko
31
+ # License:: MIT
32
+ class TestFake < Minitest::Test
33
+ def test_fake_object_works
34
+ sibit = Sibit::Fake.new
35
+ assert_equal(4_000, sibit.price)
36
+ assert_equal(12, sibit.fees[:S])
37
+ assert_equal(100_000_000, sibit.balance(''))
38
+ assert_equal(
39
+ '9dfe55a30b5ee732005158c589179a398117117a68d21531fb6c78b85b544c54',
40
+ sibit.pay(0, 'M', {}, '', '')
41
+ )
42
+ assert_equal('00000000000000000008df8a6e1b61d1136803ac9791b8725235c9f780b4ed71', sibit.latest)
43
+ assert_equal({}, sibit.get_json('/'))
44
+ end
45
+ end
data/test/test_sibit.rb CHANGED
@@ -53,7 +53,7 @@ class TestSibit < Minitest::Test
53
53
  end
54
54
 
55
55
  def test_generate_key
56
- sibit = Sibit.new
56
+ sibit = Sibit.new(api: Sibit::Fake.new)
57
57
  pkey = sibit.generate
58
58
  assert(!pkey.nil?)
59
59
  assert(/^[0-9a-f]{64}$/.match?(pkey))
@@ -63,7 +63,7 @@ class TestSibit < Minitest::Test
63
63
  require 'stringio'
64
64
  require 'logger'
65
65
  strio = StringIO.new
66
- sibit = Sibit.new(log: Logger.new(strio))
66
+ sibit = Sibit.new(log: Logger.new(strio), api: Sibit::Fake.new)
67
67
  key = sibit.generate
68
68
  assert(strio.string.include?('private key generated'))
69
69
  assert(strio.string.include?(key[0..4]))
@@ -71,7 +71,7 @@ class TestSibit < Minitest::Test
71
71
  end
72
72
 
73
73
  def test_create_address
74
- sibit = Sibit.new
74
+ sibit = Sibit.new(api: Sibit::Fake.new)
75
75
  pkey = sibit.generate
76
76
  puts "key: #{pkey}"
77
77
  address = sibit.create(pkey)
@@ -123,10 +123,8 @@ class TestSibit < Minitest::Test
123
123
  :get,
124
124
  'https://blockchain.info/unspent?active=1JvCsJtLmCxEk7ddZFnVkGXpr9uhxZPmJi&limit=1000'
125
125
  ).to_return(body: JSON.pretty_generate(json))
126
- stub_request(:post, 'https://blockchain.info/pushtx').to_return(
127
- [{ status: 500 }, { status: 200 }]
128
- )
129
- sibit = Sibit.new(attempts: 4)
126
+ stub_request(:post, 'https://blockchain.info/pushtx').to_return(status: 200)
127
+ sibit = Sibit.new
130
128
  target = sibit.create(sibit.generate)
131
129
  change = sibit.create(sibit.generate)
132
130
  tx = sibit.pay(
@@ -169,19 +167,4 @@ class TestSibit < Minitest::Test
169
167
  )
170
168
  end
171
169
  end
172
-
173
- def test_fake_object_works
174
- sibit = Sibit::Fake.new
175
- assert_equal(4_000, sibit.price)
176
- assert_equal(12, sibit.fees[:S])
177
- assert_equal('fd2333686f49d8647e1ce8d5ef39c304520b08f3c756b67068b30a3db217dcb2', sibit.generate)
178
- assert_equal('1JvCsJtLmCxEk7ddZFnVkGXpr9uhxZPmJi', sibit.create(''))
179
- assert_equal(100_000_000, sibit.balance(''))
180
- assert_equal(
181
- '9dfe55a30b5ee732005158c589179a398117117a68d21531fb6c78b85b544c54',
182
- sibit.pay(0, 'M', {}, '', '')
183
- )
184
- assert_equal('00000000000000000008df8a6e1b61d1136803ac9791b8725235c9f780b4ed71', sibit.latest)
185
- assert_equal({}, sibit.get_json('/'))
186
- end
187
170
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sibit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.5
4
+ version: 0.12.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-07 00:00:00.000000000 Z
11
+ date: 2019-12-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: backtrace
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: retriable_proxy
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 1.0.2
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 1.0.2
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: slop
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +80,20 @@ dependencies:
66
80
  - - "~>"
67
81
  - !ruby/object:Gem::Version
68
82
  version: '4.6'
83
+ - !ruby/object:Gem::Dependency
84
+ name: aruba
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.14.1
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.14.1
69
97
  - !ruby/object:Gem::Dependency
70
98
  name: codecov
71
99
  requirement: !ruby/object:Gem::Requirement
@@ -84,14 +112,14 @@ dependencies:
84
112
  name: cucumber
85
113
  requirement: !ruby/object:Gem::Requirement
86
114
  requirements:
87
- - - '='
115
+ - - "~>"
88
116
  - !ruby/object:Gem::Version
89
117
  version: 1.3.17
90
118
  type: :development
91
119
  prerelease: false
92
120
  version_requirements: !ruby/object:Gem::Requirement
93
121
  requirements:
94
- - - '='
122
+ - - "~>"
95
123
  - !ruby/object:Gem::Version
96
124
  version: 1.3.17
97
125
  - !ruby/object:Gem::Dependency
@@ -198,14 +226,14 @@ dependencies:
198
226
  requirements:
199
227
  - - '='
200
228
  - !ruby/object:Gem::Version
201
- version: 3.4.2
229
+ version: 3.7.6
202
230
  type: :development
203
231
  prerelease: false
204
232
  version_requirements: !ruby/object:Gem::Requirement
205
233
  requirements:
206
234
  - - '='
207
235
  - !ruby/object:Gem::Version
208
- version: 3.4.2
236
+ version: 3.7.6
209
237
  description: |-
210
238
  This is a simple Bitcoin client, to use from command line \
211
239
  or from your Ruby app. You don't need to run any Bitcoin software, \
@@ -239,10 +267,19 @@ files:
239
267
  - features/step_definitions/steps.rb
240
268
  - features/support/env.rb
241
269
  - lib/sibit.rb
270
+ - lib/sibit/blockchain.rb
271
+ - lib/sibit/btc.rb
272
+ - lib/sibit/error.rb
273
+ - lib/sibit/fake.rb
274
+ - lib/sibit/http.rb
275
+ - lib/sibit/json.rb
276
+ - lib/sibit/log.rb
242
277
  - lib/sibit/version.rb
243
278
  - logo.svg
244
279
  - sibit.gemspec
245
280
  - test/test__helper.rb
281
+ - test/test_btc.rb
282
+ - test/test_fake.rb
246
283
  - test/test_sibit.rb
247
284
  homepage: http://github.com/yegor256/sibit
248
285
  licenses:
@@ -274,4 +311,6 @@ test_files:
274
311
  - features/step_definitions/steps.rb
275
312
  - features/support/env.rb
276
313
  - test/test__helper.rb
314
+ - test/test_btc.rb
315
+ - test/test_fake.rb
277
316
  - test/test_sibit.rb