sibit 0.20.3 → 0.21.3

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: a10cbcbf86d4a0c612ebba219e7e5d0a09480444280a106fd47bb41cbadd0405
4
- data.tar.gz: 5c3ede973fe824a8f247099c6980b938dc83b07481553bf7925678837553d4ad
3
+ metadata.gz: c72b1319eee0a3b3dc8370b7dd1447109cfd96d86754b5a3aa3444986fb8f9e7
4
+ data.tar.gz: 7694b4e1284ac6d656b0794c7e58e5ed4d8be6eabd62e126608395f355986635
5
5
  SHA512:
6
- metadata.gz: 4bd040eb2e985bd9af773e4bcda1108939cecbcea6b4719f3779eba70a3ce3d2bd69c1deb5c8a748eaaeee85f98fcc6ac5f447189ef635befbd75d68abde6aff
7
- data.tar.gz: ee1ba5bc4c7bd03d0f975a38c0d2f5d315804eb3444dad26b93511fed19ce3092420907d0a338d4d871dc1abc7fd9307bc3d9f4f7a97a46a6a5824be52c13c62
6
+ metadata.gz: 60c20d72c1b6b6c37c785bb3c0252d5cf37ed9f55892e51ff8675c17ee8870d5f968dbeeb150efb10b959f7f3dc419a56e7f3a31531b76a4216b62654aea6323
7
+ data.tar.gz: 7da7246776833039f185b63c2829f387f4fa20f506ba5085d1746e31814d470a3064caf6928b125a2ec9d34226297a2cd6e50d2fd53cb898498cc022f4deec6c
@@ -1,12 +1,13 @@
1
1
  AllCops:
2
+ NewCops: enable
2
3
  Exclude:
3
4
  - 'assets/**/*'
4
5
  DisplayCopNames: true
5
- TargetRubyVersion: 2.3
6
+ TargetRubyVersion: 2.5
6
7
 
7
8
  Style/ClassAndModuleChildren:
8
9
  Enabled: false
9
- Metrics/LineLength:
10
+ Layout/LineLength:
10
11
  Max: 100
11
12
  Layout/EndOfLine:
12
13
  EnforcedStyle: lf
data/.simplecov CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Copyright (c) 2019-2020 Yegor Bugayenko
2
4
  #
3
5
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -18,21 +20,21 @@
18
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
21
  # SOFTWARE.
20
22
 
21
- if Gem.win_platform? then
23
+ if Gem.win_platform?
22
24
  SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
23
25
  SimpleCov::Formatter::HTMLFormatter
24
26
  ]
25
27
  SimpleCov.start do
26
- add_filter "/test/"
27
- add_filter "/features/"
28
+ add_filter '/test/'
29
+ add_filter '/features/'
28
30
  end
29
31
  else
30
32
  SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
31
33
  [SimpleCov::Formatter::HTMLFormatter]
32
34
  )
33
35
  SimpleCov.start do
34
- add_filter "/test/"
35
- add_filter "/features/"
36
+ add_filter '/test/'
37
+ add_filter '/features/'
36
38
  minimum_coverage 60
37
39
  end
38
40
  end
data/Gemfile CHANGED
@@ -21,5 +21,5 @@
21
21
  # SOFTWARE.
22
22
 
23
23
  source 'https://rubygems.org'
24
- ruby '~>2.3'
24
+ ruby '~>2.5'
25
25
  gemspec
data/bin/sibit CHANGED
@@ -21,7 +21,7 @@
21
21
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
22
  # SOFTWARE.
23
23
 
24
- STDOUT.sync = true
24
+ $stdout.sync = true
25
25
 
26
26
  require 'slop'
27
27
  require 'backtrace'
@@ -68,27 +68,28 @@ Options are:"
68
68
  default: %w[earn blockchain btc bitcoinchain blockchair cex]
69
69
  )
70
70
  end
71
- rescue Slop::Error => ex
72
- raise ex.message
71
+ rescue Slop::Error => e
72
+ raise e.message
73
73
  end
74
74
  raise 'Try --help' if opts.arguments.empty?
75
- log = Sibit::Log.new(opts[:verbose] ? STDOUT : nil)
75
+ log = Sibit::Log.new(opts[:verbose] ? $stdout : nil)
76
76
  http = opts[:proxy] ? Sibit::HttpProxy.new(opts[:proxy]) : Sibit::Http.new
77
77
  apis = opts[:api].map(&:downcase).map do |a|
78
78
  api = nil
79
- if a == 'blockchain'
79
+ case a
80
+ when 'blockchain'
80
81
  api = Sibit::Blockchain.new(http: http, log: log, dry: opts[:dry])
81
- elsif a == 'btc'
82
+ when 'btc'
82
83
  api = Sibit::Btc.new(http: http, log: log, dry: opts[:dry])
83
- elsif a == 'bitcoinchain'
84
+ when 'bitcoinchain'
84
85
  api = Sibit::Bitcoinchain.new(http: http, log: log, dry: opts[:dry])
85
- elsif a == 'blockchair'
86
+ when 'blockchair'
86
87
  api = Sibit::Blockchair.new(http: http, log: log, dry: opts[:dry])
87
- elsif a == 'cex'
88
+ when 'cex'
88
89
  api = Sibit::Cex.new(http: http, log: log, dry: opts[:dry])
89
- elsif a == 'fake'
90
+ when 'fake'
90
91
  api = Sibit::Fake.new
91
- elsif a == 'earn'
92
+ when 'earn'
92
93
  api = Sibit::Earn.new(http: http, log: log, dry: opts[:dry])
93
94
  else
94
95
  raise Sibit::Error, "Unknown API \"#{a}\""
@@ -105,7 +106,7 @@ Options are:"
105
106
  text = %i[S M L XL].map do |m|
106
107
  sat = fees[m] * 250
107
108
  usd = sat * sibit.price / 100_000_000
108
- "#{m}: #{sat}sat / $#{format('%.02f', usd)}"
109
+ "#{m}: #{sat}sat / $#{format('%<usd>.02f', usd: usd)}"
109
110
  end.join("\n")
110
111
  puts text
111
112
  when 'latest'
@@ -141,11 +142,11 @@ Options are:"
141
142
  else
142
143
  raise "Command #{opts.arguments[0]} is not supported"
143
144
  end
144
- rescue StandardError => ex
145
+ rescue StandardError => e
145
146
  if opts[:verbose]
146
- puts Backtrace.new(ex).to_s
147
+ puts Backtrace.new(e).to_s
147
148
  else
148
- puts "ERROR: #{ex.message}"
149
+ puts "ERROR: #{e.message}"
149
150
  end
150
151
  exit(255)
151
152
  end
@@ -48,7 +48,7 @@ class Sibit
48
48
  # The +api+ argument can be an object or an array of objects. If an array
49
49
  # is provided, we will make an attempt to try them one by one, until
50
50
  # one of them succeedes.
51
- def initialize(log: STDOUT, api: Sibit::Blockchain.new(log: Sibit::Log.new(log)))
51
+ def initialize(log: $stdout, api: Sibit::Blockchain.new(log: Sibit::Log.new(log)))
52
52
  @log = Sibit::Log.new(log)
53
53
  @api = api
54
54
  end
@@ -20,6 +20,7 @@
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 'backtrace'
23
24
  require_relative 'error'
24
25
  require_relative 'log'
25
26
 
@@ -103,14 +104,15 @@ class Sibit
103
104
  def best_of(method)
104
105
  return yield @list unless @list.is_a?(Array)
105
106
  results = []
107
+ errors = []
106
108
  @list.each do |api|
107
- begin
108
- results << yield(api)
109
- rescue Sibit::Error => e
110
- @log.info("The API #{api.class.name} failed at #{method}(): #{e.message}") if @verbose
111
- end
109
+ results << yield(api)
110
+ rescue Sibit::Error => e
111
+ errors << e
112
+ @log.info("The API #{api.class.name} failed at #{method}(): #{e.message}") if @verbose
112
113
  end
113
114
  if results.empty?
115
+ errors.each { |e| @log.info(Backtrace.new(e).to_s) }
114
116
  raise Sibit::Error, "No APIs out of #{@list.length} managed to succeed at #{method}(): \
115
117
  #{@list.map { |a| a.class.name }.join(', ')}"
116
118
  end
@@ -20,13 +20,14 @@
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 'uri'
23
+ require 'iri'
24
24
  require 'json'
25
- require_relative 'version'
25
+ require 'uri'
26
26
  require_relative 'error'
27
- require_relative 'log'
28
27
  require_relative 'http'
29
28
  require_relative 'json'
29
+ require_relative 'log'
30
+ require_relative 'version'
30
31
 
31
32
  # Bitcoinchain.com API.
32
33
  #
@@ -56,7 +57,7 @@ class Sibit
56
57
  # Get hash of the block after this one.
57
58
  def next_of(hash)
58
59
  block = Sibit::Json.new(http: @http, log: @log).get(
59
- URI("https://api-r.bitcoinchain.com/v1/block/#{hash}")
60
+ Iri.new('https://api-r.bitcoinchain.com/v1/block').append(hash)
60
61
  )[0]
61
62
  raise Sibit::Error, "Block #{hash} not found" if block.nil?
62
63
  nxt = block['next_block']
@@ -69,7 +70,7 @@ class Sibit
69
70
  # Gets the balance of the address, in satoshi.
70
71
  def balance(address)
71
72
  json = Sibit::Json.new(http: @http, log: @log).get(
72
- URI("https://api-r.bitcoinchain.com/v1/address/#{address}"),
73
+ Iri.new('https://api-r.bitcoinchain.com/v1/address').append(address),
73
74
  accept: [200, 409]
74
75
  )[0]
75
76
  b = json['balance']
@@ -91,7 +92,7 @@ class Sibit
91
92
  # Gets the hash of the latest block.
92
93
  def latest
93
94
  hash = Sibit::Json.new(http: @http, log: @log).get(
94
- URI('https://api-r.bitcoinchain.com/v1/status')
95
+ Iri.new('https://api-r.bitcoinchain.com/v1/status')
95
96
  )['hash']
96
97
  @log.info("The latest block hash is #{hash}")
97
98
  hash
@@ -111,11 +112,11 @@ class Sibit
111
112
  # an exception if the block is not found.
112
113
  def block(hash)
113
114
  head = Sibit::Json.new(http: @http, log: @log).get(
114
- URI("https://api-r.bitcoinchain.com/v1/block/#{hash}")
115
+ Iri.new('https://api-r.bitcoinchain.com/v1/block').append(hash)
115
116
  )[0]
116
117
  raise Sibit::Error, "The block #{hash} is not found" if head.nil?
117
118
  txs = Sibit::Json.new(http: @http, log: @log).get(
118
- URI("https://api-r.bitcoinchain.com/v1/block/txs/#{hash}")
119
+ Iri.new('https://api-r.bitcoinchain.com/v1/block/txs').append(hash)
119
120
  )
120
121
  nxt = head['next_block']
121
122
  nxt = nil if nxt == '0000000000000000000000000000000000000000000000000000000000000000'
@@ -21,12 +21,13 @@
21
21
  # SOFTWARE.
22
22
 
23
23
  require 'bitcoin'
24
+ require 'iri'
24
25
  require 'json'
25
26
  require 'uri'
26
- require_relative 'version'
27
27
  require_relative 'error'
28
28
  require_relative 'http'
29
29
  require_relative 'json'
30
+ require_relative 'version'
30
31
 
31
32
  # Blockchain.info API.
32
33
  #
@@ -49,7 +50,7 @@ class Sibit
49
50
  # Current price of BTC in USD (float returned).
50
51
  def price(currency = 'USD')
51
52
  h = Sibit::Json.new(http: @http, log: @log).get(
52
- URI('https://blockchain.info/ticker')
53
+ Iri.new('https://blockchain.info/ticker')
53
54
  )[currency]
54
55
  raise Error, "Unrecognized currency #{currency}" if h.nil?
55
56
  price = h['15m']
@@ -59,13 +60,23 @@ class Sibit
59
60
 
60
61
  # Get hash of the block after this one.
61
62
  def next_of(_hash)
62
- raise Sibit::NotSupportedError, 'Blockchain API doesn\'t provide next_of()'
63
+ raise Sibit::NotSupportedError, 'next_of() in Blockchain API is broken, always returns NULL'
64
+ # json = Sibit::Json.new(http: @http, log: @log).get(
65
+ # Iri.new('https://blockchain.info/rawblock').append(hash)
66
+ # )
67
+ # nxt = json['next_block'][0]
68
+ # if nxt.nil?
69
+ # @log.info("There is no block after #{hash}")
70
+ # else
71
+ # @log.info("The next block of #{hash} is #{nxt}")
72
+ # end
73
+ # nxt
63
74
  end
64
75
 
65
76
  # The height of the block.
66
77
  def height(hash)
67
78
  json = Sibit::Json.new(http: @http, log: @log).get(
68
- URI("https://blockchain.info/rawblock/#{hash}")
79
+ Iri.new('https://blockchain.info/rawblock').append(hash)
69
80
  )
70
81
  h = json['height']
71
82
  @log.info("The height of #{hash} is #{h}")
@@ -75,7 +86,7 @@ class Sibit
75
86
  # Gets the balance of the address, in satoshi.
76
87
  def balance(address)
77
88
  json = Sibit::Json.new(http: @http, log: @log).get(
78
- URI("https://blockchain.info/rawaddr/#{address}?limit=0"),
89
+ Iri.new('https://blockchain.info/rawaddr').append(address).add(limit: 0),
79
90
  accept: [200, 500]
80
91
  )
81
92
  b = json['final_balance']
@@ -92,7 +103,7 @@ class Sibit
92
103
  # of Bitcoin addresses.
93
104
  def utxos(sources)
94
105
  Sibit::Json.new(http: @http, log: @log).get(
95
- URI("https://blockchain.info/unspent?active=#{sources.join('|')}&limit=1000")
106
+ Iri.new('https://blockchain.info/unspent').add(active: sources.join('|'), limit: 1000)
96
107
  )['unspent_outputs'].map do |u|
97
108
  {
98
109
  value: u['value'],
@@ -108,7 +119,7 @@ class Sibit
108
119
  def push(hex)
109
120
  return if @dry
110
121
  Sibit::Json.new(http: @http, log: @log).post(
111
- URI('https://blockchain.info/pushtx'),
122
+ Iri.new('https://blockchain.info/pushtx'),
112
123
  hex
113
124
  )
114
125
  end
@@ -116,7 +127,7 @@ class Sibit
116
127
  # Gets the hash of the latest block.
117
128
  def latest
118
129
  hash = Sibit::Json.new(http: @http, log: @log).get(
119
- URI('https://blockchain.info/latestblock')
130
+ Iri.new('https://blockchain.info/latestblock')
120
131
  )['hash']
121
132
  @log.info("The latest block hash is #{hash}")
122
133
  hash
@@ -125,7 +136,7 @@ class Sibit
125
136
  # This method should fetch a Blockchain block and return as a hash.
126
137
  def block(hash)
127
138
  json = Sibit::Json.new(http: @http, log: @log).get(
128
- URI("https://blockchain.info/rawblock/#{hash}")
139
+ Iri.new('https://blockchain.info/rawblock').append(hash)
129
140
  )
130
141
  {
131
142
  hash: json['hash'],
@@ -20,14 +20,15 @@
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 'uri'
24
- require 'json'
25
23
  require 'cgi'
26
- require_relative 'version'
24
+ require 'iri'
25
+ require 'json'
26
+ require 'uri'
27
27
  require_relative 'error'
28
- require_relative 'log'
29
28
  require_relative 'http'
30
29
  require_relative 'json'
30
+ require_relative 'log'
31
+ require_relative 'version'
31
32
 
32
33
  # Blockchair.com API.
33
34
  #
@@ -64,7 +65,7 @@ class Sibit
64
65
  # Gets the balance of the address, in satoshi.
65
66
  def balance(address)
66
67
  json = Sibit::Json.new(http: @http, log: @log).get(
67
- URI("https://api.blockchair.com/bitcoin/dashboards/address/#{address}?#{the_key}")
68
+ Iri.new('https://api.blockchair.com/bitcoin/dashboards/address').append(address).fragment(the_key)
68
69
  )['data'][address]
69
70
  if json.nil?
70
71
  @log.info("Address #{address} not found")
@@ -94,7 +95,7 @@ class Sibit
94
95
  # Push this transaction (in hex format) to the network.
95
96
  def push(hex)
96
97
  Sibit::Json.new(http: @http, log: @log).post(
97
- URI("https://api.blockchair.com/bitcoin/push/transaction?#{the_key}"),
98
+ Iri.new('https://api.blockchair.com/bitcoin/push/transaction').fragment(the_key),
98
99
  "data=#{hex}"
99
100
  )
100
101
  @log.info("Transaction (#{hex.length} in hex) has been pushed to Blockchair")
@@ -20,13 +20,14 @@
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 'uri'
23
+ require 'iri'
24
24
  require 'json'
25
- require_relative 'version'
25
+ require 'uri'
26
26
  require_relative 'error'
27
- require_relative 'log'
28
27
  require_relative 'http'
29
28
  require_relative 'json'
29
+ require_relative 'log'
30
+ require_relative 'version'
30
31
 
31
32
  # Btc.com API.
32
33
  #
@@ -52,7 +53,7 @@ class Sibit
52
53
 
53
54
  # Gets the balance of the address, in satoshi.
54
55
  def balance(address)
55
- uri = URI("https://chain.api.btc.com/v3/address/#{address}/unspent")
56
+ uri = Iri.new('https://chain.api.btc.com/v3/address').append(address).append('unspent')
56
57
  json = Sibit::Json.new(http: @http, log: @log).get(uri)
57
58
  if json['err_no'] == 1
58
59
  @log.info("The balance of #{address} is zero (not found)")
@@ -76,13 +77,13 @@ class Sibit
76
77
  # Get hash of the block after this one.
77
78
  def next_of(hash)
78
79
  head = Sibit::Json.new(http: @http, log: @log).get(
79
- URI("https://chain.api.btc.com/v3/block/#{hash}")
80
+ Iri.new('https://chain.api.btc.com/v3/block').append(hash)
80
81
  )
81
82
  data = head['data']
82
83
  raise Sibit::Error, "The block #{hash} not found" if data.nil?
83
84
  nxt = data['next_block_hash']
84
85
  nxt = nil if nxt == '0000000000000000000000000000000000000000000000000000000000000000'
85
- @log.info("The block #{hash} is the latest, there is no next block") if nxt.nil?
86
+ @log.info("In BTC.com the block #{hash} is the latest, there is no next block") if nxt.nil?
86
87
  @log.info("The next block of #{hash} is #{nxt}") unless nxt.nil?
87
88
  nxt
88
89
  end
@@ -90,7 +91,7 @@ class Sibit
90
91
  # The height of the block.
91
92
  def height(hash)
92
93
  json = Sibit::Json.new(http: @http, log: @log).get(
93
- URI("https://chain.api.btc.com/v3/block/#{hash}")
94
+ Iri.new('https://chain.api.btc.com/v3/block').append(hash)
94
95
  )
95
96
  data = json['data']
96
97
  raise Sibit::Error, "The block #{hash} not found" if data.nil?
@@ -107,7 +108,7 @@ class Sibit
107
108
 
108
109
  # Gets the hash of the latest block.
109
110
  def latest
110
- uri = URI('https://chain.api.btc.com/v3/block/latest')
111
+ uri = Iri.new('https://chain.api.btc.com/v3/block/latest')
111
112
  json = Sibit::Json.new(http: @http, log: @log).get(uri)
112
113
  data = json['data']
113
114
  raise Sibit::Error, 'The latest block not found' if data.nil?
@@ -121,7 +122,7 @@ class Sibit
121
122
  txns = []
122
123
  sources.each do |hash|
123
124
  json = Sibit::Json.new(http: @http, log: @log).get(
124
- URI("https://chain.api.btc.com/v3/address/#{hash}/unspent")
125
+ Iri.new('https://chain.api.btc.com/v3/address').append(hash).append('unspent')
125
126
  )
126
127
  data = json['data']
127
128
  raise Sibit::Error, "The address #{hash} not found" if data.nil?
@@ -129,7 +130,7 @@ class Sibit
129
130
  next if txns.nil?
130
131
  txns.each do |u|
131
132
  outs = Sibit::Json.new(http: @http, log: @log).get(
132
- URI("https://chain.api.btc.com/v3/tx/#{u['tx_hash']}?verbose=3")
133
+ Iri.new('https://chain.api.btc.com/v3/tx').append(u['tx_hash']).add(verbose: 3)
133
134
  )['data']['outputs']
134
135
  outs.each_with_index do |o, i|
135
136
  next unless o['addresses'].include?(hash)
@@ -154,7 +155,7 @@ class Sibit
154
155
  # This method should fetch a Blockchain block and return as a hash.
155
156
  def block(hash)
156
157
  head = Sibit::Json.new(http: @http, log: @log).get(
157
- URI("https://chain.api.btc.com/v3/block/#{hash}")
158
+ Iri.new('https://chain.api.btc.com/v3/block').append(hash)
158
159
  )
159
160
  data = head['data']
160
161
  raise Sibit::Error, "The block #{hash} not found" if data.nil?
@@ -177,7 +178,8 @@ class Sibit
177
178
  all = []
178
179
  loop do
179
180
  data = Sibit::Json.new(http: @http, log: @log).get(
180
- URI("https://chain.api.btc.com/v3/block/#{hash}/tx?page=#{page}&pagesize=#{psize}")
181
+ Iri.new('https://chain.api.btc.com/v3/block')
182
+ .append(hash).append('tx').add(page: page, pagesize: psize)
181
183
  )['data']
182
184
  raise Sibit::Error, "The block #{hash} has no data at page #{page}" if data.nil?
183
185
  list = data['list']
@@ -45,7 +45,7 @@ class Sibit
45
45
  # Current price of BTC in USD (float returned).
46
46
  def price(currency = 'USD')
47
47
  json = Sibit::Json.new(http: @http, log: @log).get(
48
- URI("https://cex.io/api/last_price/BTC/#{currency}")
48
+ Iri.new('https://cex.io/api/last_price/BTC').append(currency)
49
49
  )
50
50
  p = json['lprice'].to_f
51
51
  @log.info("The price of BTC is #{p} #{currency}")
@@ -20,13 +20,14 @@
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 'uri'
23
+ require 'iri'
24
24
  require 'json'
25
- require_relative 'version'
25
+ require 'uri'
26
26
  require_relative 'error'
27
- require_relative 'log'
28
27
  require_relative 'http'
29
28
  require_relative 'json'
29
+ require_relative 'log'
30
+ require_relative 'version'
30
31
 
31
32
  # Cryptoapis.io API.
32
33
  #
@@ -52,7 +53,7 @@ class Sibit
52
53
  # Get hash of the block after this one.
53
54
  def next_of(hash)
54
55
  nxt = Sibit::Json.new(http: @http, log: @log).get(
55
- URI("https://api.cryptoapis.io/v1/bc/btc/mainnet/blocks/#{hash}"),
56
+ Iri.new('https://api.cryptoapis.io/v1/bc/btc/mainnet/blocks').append(hash),
56
57
  headers: headers
57
58
  )['payload']['hash']
58
59
  @log.info("The block #{hash} is the latest, there is no next block") if nxt.nil?
@@ -63,7 +64,7 @@ class Sibit
63
64
  # The height of the block.
64
65
  def height(hash)
65
66
  json = Sibit::Json.new(http: @http, log: @log).get(
66
- URI("https://api.cryptoapis.io/v1/bc/btc/mainnet/blocks/#{hash}"),
67
+ Iri.new('https://api.cryptoapis.io/v1/bc/btc/mainnet/blocks').append(hash),
67
68
  headers: headers
68
69
  )['payload']
69
70
  h = json['height']
@@ -74,7 +75,7 @@ class Sibit
74
75
  # Gets the balance of the address, in satoshi.
75
76
  def balance(address)
76
77
  json = Sibit::Json.new(http: @http, log: @log).get(
77
- URI("https://api.cryptoapis.io/v1/bc/btc/mainnet/address/#{address}"),
78
+ Iri.new('https://api.cryptoapis.io/v1/bc/btc/mainnet/address').append(address),
78
79
  headers: headers
79
80
  )['payload']
80
81
  b = (json['balance'].to_f * 100_000_000).to_i
@@ -90,7 +91,7 @@ class Sibit
90
91
  # Gets the hash of the latest block.
91
92
  def latest
92
93
  hash = Sibit::Json.new(http: @http, log: @log).get(
93
- URI('https://api.cryptoapis.io/v1/bc/btc/mainnet/blocks/latest'),
94
+ Iri.new('https://api.cryptoapis.io/v1/bc/btc/mainnet/blocks/latest'),
94
95
  headers: headers
95
96
  )['payload']['hash']
96
97
  @log.info("The latest block hash is #{hash}")
@@ -105,7 +106,7 @@ class Sibit
105
106
  # Push this transaction (in hex format) to the network.
106
107
  def push(hex)
107
108
  Sibit::Json.new(http: @http, log: @log).post(
108
- URI('https://api.cryptoapis.io/v1/bc/btc/testnet/txs/send'),
109
+ Iri.new('https://api.cryptoapis.io/v1/bc/btc/testnet/txs/send'),
109
110
  JSON.pretty_generate(hex: hex),
110
111
  headers: headers
111
112
  )
@@ -114,7 +115,7 @@ class Sibit
114
115
  # This method should fetch a Blockchain block and return as a hash.
115
116
  def block(hash)
116
117
  head = Sibit::Json.new(http: @http, log: @log).get(
117
- URI("https://api.cryptoapis.io/v1/bc/btc/mainnet/blocks/#{hash}"),
118
+ Iri.new('https://api.cryptoapis.io/v1/bc/btc/mainnet/blocks').append(hash),
118
119
  headers: headers
119
120
  )['payload']
120
121
  {
@@ -141,12 +142,8 @@ class Sibit
141
142
  all = []
142
143
  loop do
143
144
  txns = Sibit::Json.new(http: @http, log: @log).get(
144
- URI(
145
- [
146
- 'https://api.cryptoapis.io/v1/bc/btc/mainnet/txs/block/',
147
- "#{hash}?index=#{index}&limit=#{limit}"
148
- ].join
149
- ),
145
+ Iri.new('https://api.cryptoapis.io/v1/bc/btc/mainnet/txs/block/')
146
+ .append(hash).add(index: index, limit: limit),
150
147
  headers: headers
151
148
  )['payload'].map do |t|
152
149
  {
@@ -20,12 +20,13 @@
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 'iri'
23
24
  require 'json'
24
25
  require 'uri'
25
- require_relative 'version'
26
26
  require_relative 'error'
27
27
  require_relative 'http'
28
28
  require_relative 'json'
29
+ require_relative 'version'
29
30
 
30
31
  # Earn.com API.
31
32
  #
@@ -66,7 +67,7 @@ class Sibit
66
67
  # a hash: { S: 12, M: 45, L: 100, XL: 200 }
67
68
  def fees
68
69
  json = Sibit::Json.new(http: @http, log: @log).get(
69
- URI('https://bitcoinfees.earn.com/api/v1/fees/recommended')
70
+ Iri.new('https://bitcoinfees.earn.com/api/v1/fees/recommended')
70
71
  )
71
72
  @log.info("Current recommended Bitcoin fees: \
72
73
  #{json['hourFee']}/#{json['halfHourFee']}/#{json['fastestFee']} sat/byte")
@@ -20,6 +20,7 @@
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 'backtrace'
23
24
  require_relative 'error'
24
25
  require_relative 'log'
25
26
 
@@ -102,6 +103,7 @@ class Sibit
102
103
 
103
104
  def first_of(method)
104
105
  return yield @list unless @list.is_a?(Array)
106
+ errors = []
105
107
  done = false
106
108
  result = nil
107
109
  @list.each do |api|
@@ -111,10 +113,12 @@ class Sibit
111
113
  done = true
112
114
  break
113
115
  rescue Sibit::Error => e
116
+ errors << e
114
117
  @log.info("The API #{api.class.name} failed at #{method}(): #{e.message}") if @verbose
115
118
  end
116
119
  end
117
120
  unless done
121
+ errors.each { |e| @log.info(Backtrace.new(e).to_s) }
118
122
  raise Sibit::Error, "No APIs out of #{@list.length} managed to succeed at #{method}(): \
119
123
  #{@list.map { |a| a.class.name }.join(', ')}"
120
124
  end
@@ -48,8 +48,9 @@ class Sibit
48
48
  # Send GET request to the HTTP and return JSON response.
49
49
  # This method will also log the process and will validate the
50
50
  # response for correctness.
51
- def get(uri, headers: {}, accept: [200])
51
+ def get(address, headers: {}, accept: [200])
52
52
  start = Time.now
53
+ uri = URI(address.to_s)
53
54
  res = @http.client(uri).get(
54
55
  "#{uri.path.empty? ? '/' : uri.path}#{uri.query ? "?#{uri.query}" : ''}",
55
56
  {
@@ -66,8 +67,9 @@ class Sibit
66
67
  JSON.parse(res.body)
67
68
  end
68
69
 
69
- def post(uri, body, headers: {})
70
+ def post(address, body, headers: {})
70
71
  start = Time.now
72
+ uri = URI(address.to_s)
71
73
  res = @http.client(uri).post(
72
74
  "#{uri.path}?#{uri.query}",
73
75
  "tx=#{CGI.escape(body)}",
@@ -34,7 +34,7 @@ class Sibit
34
34
  # provide anything, the console will be used. The object you provide
35
35
  # has to respond to the method +info+ or +puts+ in order to receive logging
36
36
  # messages.
37
- def initialize(log = STDOUT)
37
+ def initialize(log = $stdout)
38
38
  @log = log
39
39
  end
40
40
 
@@ -26,5 +26,5 @@
26
26
  # License:: MIT
27
27
  class Sibit
28
28
  # Current version of the library.
29
- VERSION = '0.20.3'
29
+ VERSION = '0.21.3'
30
30
  end
@@ -31,7 +31,7 @@ Gem::Specification.new do |s|
31
31
  s.required_rubygems_version = Gem::Requirement.new('>= 0')
32
32
  end
33
33
  s.rubygems_version = '2.2'
34
- s.required_ruby_version = '>= 2.3'
34
+ s.required_ruby_version = '>= 2.5'
35
35
  s.name = 'sibit'
36
36
  s.version = Sibit::VERSION
37
37
  s.license = 'MIT'
@@ -49,20 +49,19 @@ and Ruby 2.3+.'
49
49
  s.rdoc_options = ['--charset=UTF-8']
50
50
  s.extra_rdoc_files = ['README.md', 'LICENSE.txt']
51
51
  s.add_runtime_dependency 'backtrace', '~> 0.3'
52
- s.add_runtime_dependency 'bitcoin', '0.2.0'
53
52
  s.add_runtime_dependency 'bitcoin-ruby', '0.0.19'
53
+ s.add_runtime_dependency 'iri', '~> 0.5'
54
54
  s.add_runtime_dependency 'json', '~> 2'
55
55
  s.add_runtime_dependency 'retriable_proxy', '1.0.2'
56
56
  s.add_runtime_dependency 'slop', '~> 4.6'
57
- s.add_development_dependency 'aruba', '~> 0.14.1'
58
- s.add_development_dependency 'codecov', '0.2.8'
59
- s.add_development_dependency 'cucumber', '~> 1.3.17'
60
- s.add_development_dependency 'debase', '0.2.2'
61
- s.add_development_dependency 'minitest', '5.5.0'
62
- s.add_development_dependency 'rake', '12.0.0'
63
- s.add_development_dependency 'rspec-rails', '3.1.0'
64
- s.add_development_dependency 'rubocop', '0.61.0'
65
- s.add_development_dependency 'rubocop-rspec', '1.31.0'
66
- s.add_development_dependency 'ruby-debug-ide', '0.6.1'
67
- s.add_development_dependency 'webmock', '3.7.6'
57
+ s.add_development_dependency 'aruba', '~> 1.0'
58
+ s.add_development_dependency 'codecov', '0.2.11'
59
+ s.add_development_dependency 'cucumber', '~> 5.1'
60
+ s.add_development_dependency 'debase', '~> 0.2'
61
+ s.add_development_dependency 'minitest', '5.14.2'
62
+ s.add_development_dependency 'rake', '13.0.1'
63
+ s.add_development_dependency 'rspec-rails', '4.0.1'
64
+ s.add_development_dependency 'rubocop', '0.91.0'
65
+ s.add_development_dependency 'rubocop-rspec', '1.43.2'
66
+ s.add_development_dependency 'webmock', '3.9.1'
68
67
  end
@@ -20,7 +20,7 @@
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
21
  # SOFTWARE.
22
22
 
23
- STDOUT.sync = true
23
+ $stdout.sync = true
24
24
 
25
25
  require 'simplecov'
26
26
  SimpleCov.start
@@ -34,7 +34,7 @@ class TestBlockchain < Minitest::Test
34
34
  def test_fetch_block
35
35
  hash = '0000000000000000000f676241aabc9b62b748d26192a44bc25720c34de27d19'
36
36
  stub_request(:get, "https://blockchain.info/rawblock/#{hash}")
37
- .to_return(body: '{"next_block": "n", "prev_block": "p", "hash": "h",
37
+ .to_return(body: '{"next_block": ["n"], "prev_block": "p", "hash": "h",
38
38
  "tx": [{"hash": "h1", "out": [{"hash": "oh", "value": 123}]}]}')
39
39
  sibit = Sibit::Blockchain.new
40
40
  json = sibit.block(hash)
@@ -45,4 +45,14 @@ class TestBlockchain < Minitest::Test
45
45
  assert_equal('h1', json[:txns][0][:hash])
46
46
  assert(json[:txns][0][:outputs].is_a?(Array))
47
47
  end
48
+
49
+ def test_next_of
50
+ skip
51
+ hash = '0000000000000000000f676241aabc9b62b748d26192a44bc25720c34de27d19'
52
+ stub_request(:get, "https://blockchain.info/rawblock/#{hash}")
53
+ .to_return(body: '{"next_block": ["nxt"]}')
54
+ sibit = Sibit::Blockchain.new
55
+ nxt = sibit.next_of(hash)
56
+ assert_equal('nxt', nxt)
57
+ end
48
58
  end
@@ -107,7 +107,7 @@ class TestLive < Minitest::Test
107
107
  json = api.utxos(['12fCwqBN4XsHq4iu2Wbfgq5e8YhqEGP3ee'])
108
108
  assert_equal(3, json.length)
109
109
  assert(json.find { |t| t[:value] == 16_200_000 }, 'UTXO not found')
110
- assert(json.find { |t| t[:script].unpack('H*').first.start_with?('76a9141231e760') })
110
+ assert(json.find { |t| t[:script].unpack1('H*').start_with?('76a9141231e760') })
111
111
  end
112
112
  end
113
113
 
@@ -130,11 +130,9 @@ class TestLive < Minitest::Test
130
130
  require_relative '../lib/sibit/bitcoinchain'
131
131
  apis << Sibit::Bitcoinchain.new
132
132
  apis.each do |api|
133
- begin
134
- yield api
135
- rescue Sibit::Error => e
136
- puts Backtrace.new(e).to_s
137
- end
133
+ yield api
134
+ rescue Sibit::Error => e
135
+ puts Backtrace.new(e).to_s
138
136
  end
139
137
  end
140
138
  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.20.3
4
+ version: 0.21.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-08 00:00:00.000000000 Z
11
+ date: 2020-11-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: backtrace
@@ -25,33 +25,33 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0.3'
27
27
  - !ruby/object:Gem::Dependency
28
- name: bitcoin
28
+ name: bitcoin-ruby
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - '='
32
32
  - !ruby/object:Gem::Version
33
- version: 0.2.0
33
+ version: 0.0.19
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - '='
39
39
  - !ruby/object:Gem::Version
40
- version: 0.2.0
40
+ version: 0.0.19
41
41
  - !ruby/object:Gem::Dependency
42
- name: bitcoin-ruby
42
+ name: iri
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '='
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 0.0.19
47
+ version: '0.5'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '='
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 0.0.19
54
+ version: '0.5'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: json
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -100,154 +100,140 @@ dependencies:
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: 0.14.1
103
+ version: '1.0'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: 0.14.1
110
+ version: '1.0'
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: codecov
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - '='
116
116
  - !ruby/object:Gem::Version
117
- version: 0.2.8
117
+ version: 0.2.11
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - '='
123
123
  - !ruby/object:Gem::Version
124
- version: 0.2.8
124
+ version: 0.2.11
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: cucumber
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
129
  - - "~>"
130
130
  - !ruby/object:Gem::Version
131
- version: 1.3.17
131
+ version: '5.1'
132
132
  type: :development
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: 1.3.17
138
+ version: '5.1'
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: debase
141
141
  requirement: !ruby/object:Gem::Requirement
142
142
  requirements:
143
- - - '='
143
+ - - "~>"
144
144
  - !ruby/object:Gem::Version
145
- version: 0.2.2
145
+ version: '0.2'
146
146
  type: :development
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
- - - '='
150
+ - - "~>"
151
151
  - !ruby/object:Gem::Version
152
- version: 0.2.2
152
+ version: '0.2'
153
153
  - !ruby/object:Gem::Dependency
154
154
  name: minitest
155
155
  requirement: !ruby/object:Gem::Requirement
156
156
  requirements:
157
157
  - - '='
158
158
  - !ruby/object:Gem::Version
159
- version: 5.5.0
159
+ version: 5.14.2
160
160
  type: :development
161
161
  prerelease: false
162
162
  version_requirements: !ruby/object:Gem::Requirement
163
163
  requirements:
164
164
  - - '='
165
165
  - !ruby/object:Gem::Version
166
- version: 5.5.0
166
+ version: 5.14.2
167
167
  - !ruby/object:Gem::Dependency
168
168
  name: rake
169
169
  requirement: !ruby/object:Gem::Requirement
170
170
  requirements:
171
171
  - - '='
172
172
  - !ruby/object:Gem::Version
173
- version: 12.0.0
173
+ version: 13.0.1
174
174
  type: :development
175
175
  prerelease: false
176
176
  version_requirements: !ruby/object:Gem::Requirement
177
177
  requirements:
178
178
  - - '='
179
179
  - !ruby/object:Gem::Version
180
- version: 12.0.0
180
+ version: 13.0.1
181
181
  - !ruby/object:Gem::Dependency
182
182
  name: rspec-rails
183
183
  requirement: !ruby/object:Gem::Requirement
184
184
  requirements:
185
185
  - - '='
186
186
  - !ruby/object:Gem::Version
187
- version: 3.1.0
187
+ version: 4.0.1
188
188
  type: :development
189
189
  prerelease: false
190
190
  version_requirements: !ruby/object:Gem::Requirement
191
191
  requirements:
192
192
  - - '='
193
193
  - !ruby/object:Gem::Version
194
- version: 3.1.0
194
+ version: 4.0.1
195
195
  - !ruby/object:Gem::Dependency
196
196
  name: rubocop
197
197
  requirement: !ruby/object:Gem::Requirement
198
198
  requirements:
199
199
  - - '='
200
200
  - !ruby/object:Gem::Version
201
- version: 0.61.0
201
+ version: 0.91.0
202
202
  type: :development
203
203
  prerelease: false
204
204
  version_requirements: !ruby/object:Gem::Requirement
205
205
  requirements:
206
206
  - - '='
207
207
  - !ruby/object:Gem::Version
208
- version: 0.61.0
208
+ version: 0.91.0
209
209
  - !ruby/object:Gem::Dependency
210
210
  name: rubocop-rspec
211
211
  requirement: !ruby/object:Gem::Requirement
212
212
  requirements:
213
213
  - - '='
214
214
  - !ruby/object:Gem::Version
215
- version: 1.31.0
216
- type: :development
217
- prerelease: false
218
- version_requirements: !ruby/object:Gem::Requirement
219
- requirements:
220
- - - '='
221
- - !ruby/object:Gem::Version
222
- version: 1.31.0
223
- - !ruby/object:Gem::Dependency
224
- name: ruby-debug-ide
225
- requirement: !ruby/object:Gem::Requirement
226
- requirements:
227
- - - '='
228
- - !ruby/object:Gem::Version
229
- version: 0.6.1
215
+ version: 1.43.2
230
216
  type: :development
231
217
  prerelease: false
232
218
  version_requirements: !ruby/object:Gem::Requirement
233
219
  requirements:
234
220
  - - '='
235
221
  - !ruby/object:Gem::Version
236
- version: 0.6.1
222
+ version: 1.43.2
237
223
  - !ruby/object:Gem::Dependency
238
224
  name: webmock
239
225
  requirement: !ruby/object:Gem::Requirement
240
226
  requirements:
241
227
  - - '='
242
228
  - !ruby/object:Gem::Version
243
- version: 3.7.6
229
+ version: 3.9.1
244
230
  type: :development
245
231
  prerelease: false
246
232
  version_requirements: !ruby/object:Gem::Requirement
247
233
  requirements:
248
234
  - - '='
249
235
  - !ruby/object:Gem::Version
250
- version: 3.7.6
236
+ version: 3.9.1
251
237
  description: |-
252
238
  This is a simple Bitcoin client, to use from command line
253
239
  or from your Ruby app. You don't need to run any Bitcoin software,
@@ -324,7 +310,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
324
310
  requirements:
325
311
  - - ">="
326
312
  - !ruby/object:Gem::Version
327
- version: '2.3'
313
+ version: '2.5'
328
314
  required_rubygems_version: !ruby/object:Gem::Requirement
329
315
  requirements:
330
316
  - - ">="