sibit 0.18.4 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -0
- data/.rultor.yml +2 -7
- data/README.md +9 -2
- data/bin/sibit +2 -1
- data/lib/sibit.rb +11 -40
- data/lib/sibit/bestof.rb +119 -0
- data/lib/sibit/bitcoinchain.rb +12 -9
- data/lib/sibit/blockchain.rb +5 -4
- data/lib/sibit/blockchair.rb +9 -8
- data/lib/sibit/btc.rb +23 -12
- data/lib/sibit/cex.rb +8 -8
- data/lib/sibit/cryptoapis.rb +7 -4
- data/lib/sibit/earn.rb +8 -8
- data/lib/sibit/error.rb +3 -0
- data/lib/sibit/firstof.rb +122 -0
- data/lib/sibit/version.rb +1 -1
- data/sibit.gemspec +3 -3
- data/test/test_bestof.rb +50 -0
- data/test/test_blockchair.rb +2 -1
- data/test/test_firstof.rb +50 -0
- data/test/test_sibit.rb +4 -2
- metadata +11 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c16c2d9c8fd8acd2635dc38d1b97599dc2138851ffc162db62aa3dc35feec993
|
|
4
|
+
data.tar.gz: b17f0c383453182402041ac8793eb2e308480b660e4bd79577a846a6517cf66e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3a7234573110a1fd17ed0e67257474f5c07801e0fd74a0212ca1e08453e9088aeae00b803382814119b9e16fe1b22a9f2fab1974724fdc5c1f5f0d11649e5e29
|
|
7
|
+
data.tar.gz: '089e4b96cf9c5fb0b3e17bd1692e9ee66f553c2752fd8669a520008a533dacc60063c473264f0469d394a5c272209419026f4920dfa3f3f97bbfc422fe359751'
|
data/.rubocop.yml
CHANGED
data/.rultor.yml
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
assets:
|
|
2
2
|
rubygems.yml: yegor256/home#assets/rubygems.yml
|
|
3
3
|
install: |
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
sudo apt-get -y update
|
|
7
|
-
sudo gem install pdd -v 0.20.5
|
|
8
|
-
bundle install
|
|
4
|
+
pdd -f /dev/null
|
|
5
|
+
sudo bundle install --no-color "--gemfile=$(pwd)/Gemfile"
|
|
9
6
|
release:
|
|
10
7
|
script: |-
|
|
11
8
|
bundle exec rake
|
|
12
|
-
rm -rf *.gem
|
|
13
9
|
sed -i "s/1\.0\.snapshot/${tag}/g" lib/sibit/version.rb
|
|
14
10
|
git add lib/sibit/version.rb
|
|
15
11
|
git commit -m "version set to ${tag}"
|
|
@@ -19,7 +15,6 @@ release:
|
|
|
19
15
|
merge:
|
|
20
16
|
script: |-
|
|
21
17
|
bundle exec rake
|
|
22
|
-
pdd -f /dev/null
|
|
23
18
|
deploy:
|
|
24
19
|
script: |-
|
|
25
20
|
echo "There is nothing to deploy"
|
data/README.md
CHANGED
|
@@ -107,7 +107,7 @@ pkey = sibit.generate
|
|
|
107
107
|
address = sibit.create(pkey)
|
|
108
108
|
balance = sibit.balance(address)
|
|
109
109
|
target = sibit.create(pkey) # where to send coins to
|
|
110
|
-
change = sibit.create(pkey) # where the change will sent to
|
|
110
|
+
change = sibit.create(pkey) # where the change will be sent to
|
|
111
111
|
tx = sibit.pay(10_000_000, 'XL', { address => pkey }, target, change)
|
|
112
112
|
```
|
|
113
113
|
|
|
@@ -145,7 +145,14 @@ will be used one by one, until a successful response is obtained:
|
|
|
145
145
|
require 'sibit'
|
|
146
146
|
require 'sibit/btc'
|
|
147
147
|
require 'sibit/cryptoapis'
|
|
148
|
-
sibit = Sibit.new(
|
|
148
|
+
sibit = Sibit.new(
|
|
149
|
+
api: Sibit::FirstOf.new(
|
|
150
|
+
[
|
|
151
|
+
Sibit::Btc.new,
|
|
152
|
+
Sibit::Cryptoapis.new('key')
|
|
153
|
+
]
|
|
154
|
+
)
|
|
155
|
+
)
|
|
149
156
|
```
|
|
150
157
|
|
|
151
158
|
If you think we may need to use some other API, you can submit a ticket,
|
data/bin/sibit
CHANGED
|
@@ -32,6 +32,7 @@ require_relative '../lib/sibit/blockchain'
|
|
|
32
32
|
require_relative '../lib/sibit/btc'
|
|
33
33
|
require_relative '../lib/sibit/bitcoinchain'
|
|
34
34
|
require_relative '../lib/sibit/earn'
|
|
35
|
+
require_relative '../lib/sibit/firstof'
|
|
35
36
|
|
|
36
37
|
begin
|
|
37
38
|
begin
|
|
@@ -86,7 +87,7 @@ Options are:"
|
|
|
86
87
|
api = RetriableProxy.for_object(api, on: Sibit::Error) if opts[:attempts] > 1
|
|
87
88
|
api
|
|
88
89
|
end
|
|
89
|
-
sibit = Sibit.new(log: log, api: apis)
|
|
90
|
+
sibit = Sibit.new(log: log, api: Sibit::FirstOf.new(apis, log: log))
|
|
90
91
|
case opts.arguments[0]
|
|
91
92
|
when 'price'
|
|
92
93
|
puts sibit.price
|
data/lib/sibit.rb
CHANGED
|
@@ -55,9 +55,7 @@ class Sibit
|
|
|
55
55
|
|
|
56
56
|
# Current price of 1 BTC in USD (or another currency), float returned.
|
|
57
57
|
def price(currency = 'USD')
|
|
58
|
-
|
|
59
|
-
api.price(currency)
|
|
60
|
-
end
|
|
58
|
+
@api.price(currency)
|
|
61
59
|
end
|
|
62
60
|
|
|
63
61
|
# Generates new Bitcon private key and returns in Hash160 format.
|
|
@@ -76,29 +74,23 @@ class Sibit
|
|
|
76
74
|
|
|
77
75
|
# Gets the balance of the address, in satoshi.
|
|
78
76
|
def balance(address)
|
|
79
|
-
|
|
80
|
-
api.balance(address)
|
|
81
|
-
end
|
|
77
|
+
@api.balance(address)
|
|
82
78
|
end
|
|
83
79
|
|
|
84
80
|
# Get the height of the block.
|
|
85
81
|
def height(hash)
|
|
86
|
-
|
|
87
|
-
api.height(hash)
|
|
88
|
-
end
|
|
82
|
+
@api.height(hash)
|
|
89
83
|
end
|
|
90
84
|
|
|
91
85
|
# Get the hash of the next block.
|
|
92
86
|
def next_of(hash)
|
|
93
|
-
|
|
94
|
-
api.next_of(hash)
|
|
95
|
-
end
|
|
87
|
+
@api.next_of(hash)
|
|
96
88
|
end
|
|
97
89
|
|
|
98
90
|
# Get recommended fees, in satoshi per byte. The method returns
|
|
99
91
|
# a hash: { S: 12, M: 45, L: 100, XL: 200 }
|
|
100
92
|
def fees
|
|
101
|
-
|
|
93
|
+
@api.fees
|
|
102
94
|
end
|
|
103
95
|
|
|
104
96
|
# Sends a payment and returns the transaction hash.
|
|
@@ -124,7 +116,7 @@ class Sibit
|
|
|
124
116
|
builder = Bitcoin::Builder::TxBuilder.new
|
|
125
117
|
unspent = 0
|
|
126
118
|
size = 100
|
|
127
|
-
utxos =
|
|
119
|
+
utxos = @api.utxos(sources.keys)
|
|
128
120
|
@log.info("#{utxos.count} UTXOs found, these will be used \
|
|
129
121
|
(value/confirmations at tx_hash):")
|
|
130
122
|
utxos.each do |utxo|
|
|
@@ -170,15 +162,13 @@ class Sibit
|
|
|
170
162
|
Amount: #{num(satoshi, p)}
|
|
171
163
|
Target address: #{target}
|
|
172
164
|
Change address is #{change}")
|
|
173
|
-
|
|
174
|
-
api.push(tx.to_payload.bth)
|
|
175
|
-
end
|
|
165
|
+
@api.push(tx.to_payload.bth)
|
|
176
166
|
tx.hash
|
|
177
167
|
end
|
|
178
168
|
|
|
179
169
|
# Gets the hash of the latest block.
|
|
180
170
|
def latest
|
|
181
|
-
|
|
171
|
+
@api.latest
|
|
182
172
|
end
|
|
183
173
|
|
|
184
174
|
# You call this method and provide a callback. You provide the hash
|
|
@@ -197,7 +187,7 @@ class Sibit
|
|
|
197
187
|
wrong = []
|
|
198
188
|
json = {}
|
|
199
189
|
loop do
|
|
200
|
-
json =
|
|
190
|
+
json = @api.block(block)
|
|
201
191
|
if json[:orphan]
|
|
202
192
|
steps = 4
|
|
203
193
|
@log.info("Orphan block found at #{block}, moving #{steps} steps back...")
|
|
@@ -206,7 +196,7 @@ class Sibit
|
|
|
206
196
|
block = json[:previous]
|
|
207
197
|
wrong << block
|
|
208
198
|
@log.info("Moved back to #{block}")
|
|
209
|
-
json =
|
|
199
|
+
json = @api.block(block)
|
|
210
200
|
end
|
|
211
201
|
next
|
|
212
202
|
end
|
|
@@ -236,31 +226,12 @@ class Sibit
|
|
|
236
226
|
break
|
|
237
227
|
end
|
|
238
228
|
end
|
|
229
|
+
@log.info("Scanned from #{start} to #{json[:hash]} (#{count} blocks)")
|
|
239
230
|
json[:hash]
|
|
240
231
|
end
|
|
241
232
|
|
|
242
233
|
private
|
|
243
234
|
|
|
244
|
-
def first_one
|
|
245
|
-
return yield @api unless @api.is_a?(Array)
|
|
246
|
-
done = false
|
|
247
|
-
result = nil
|
|
248
|
-
@api.each do |api|
|
|
249
|
-
begin
|
|
250
|
-
result = yield api
|
|
251
|
-
done = true
|
|
252
|
-
break
|
|
253
|
-
rescue Sibit::Error => e
|
|
254
|
-
@log.info("The API #{api.class.name} failed: #{e.message}")
|
|
255
|
-
end
|
|
256
|
-
end
|
|
257
|
-
unless done
|
|
258
|
-
raise Sibit::Error, "No APIs out of #{@api.length} managed to succeed: \
|
|
259
|
-
#{@api.map { |a| a.class.name }.join(', ')}"
|
|
260
|
-
end
|
|
261
|
-
result
|
|
262
|
-
end
|
|
263
|
-
|
|
264
235
|
def num(satoshi, usd)
|
|
265
236
|
format(
|
|
266
237
|
'%<satoshi>ss/$%<dollars>0.2f',
|
data/lib/sibit/bestof.rb
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2019-2020 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_relative 'error'
|
|
24
|
+
require_relative 'log'
|
|
25
|
+
|
|
26
|
+
# API best of.
|
|
27
|
+
#
|
|
28
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
|
29
|
+
# Copyright:: Copyright (c) 2019-2020 Yegor Bugayenko
|
|
30
|
+
# License:: MIT
|
|
31
|
+
class Sibit
|
|
32
|
+
# Best of API.
|
|
33
|
+
class BestOf
|
|
34
|
+
# Constructor.
|
|
35
|
+
def initialize(list, log: Sibit::Log.new)
|
|
36
|
+
@list = list
|
|
37
|
+
@log = log
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Current price of BTC in USD (float returned).
|
|
41
|
+
def price(currency = 'USD')
|
|
42
|
+
best_of('price') do |api|
|
|
43
|
+
api.price(currency)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Gets the balance of the address, in satoshi.
|
|
48
|
+
def balance(address)
|
|
49
|
+
best_of('balance') do |api|
|
|
50
|
+
api.balance(address)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Get the height of the block.
|
|
55
|
+
def height(hash)
|
|
56
|
+
best_of('height') do |api|
|
|
57
|
+
api.height(hash)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Get the hash of the next block.
|
|
62
|
+
def next_of(hash)
|
|
63
|
+
best_of('next_of') do |api|
|
|
64
|
+
api.next_of(hash)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Get recommended fees, in satoshi per byte. The method returns
|
|
69
|
+
# a hash: { S: 12, M: 45, L: 100, XL: 200 }
|
|
70
|
+
def fees
|
|
71
|
+
best_of('fees', &:fees)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Fetch all unspent outputs per address.
|
|
75
|
+
def utxos(keys)
|
|
76
|
+
best_of('utxos') do |api|
|
|
77
|
+
api.utxos(keys)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Latest block hash.
|
|
82
|
+
def latest
|
|
83
|
+
best_of('latest', &:latest)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Push this transaction (in hex format) to the network.
|
|
87
|
+
def push(hex)
|
|
88
|
+
best_of('push') do |api|
|
|
89
|
+
api.push(hex)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# This method should fetch a block and return as a hash.
|
|
94
|
+
def block(hash)
|
|
95
|
+
best_of('block') do |api|
|
|
96
|
+
api.block(hash)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
|
|
102
|
+
def best_of(method)
|
|
103
|
+
return yield @list unless @list.is_a?(Array)
|
|
104
|
+
results = []
|
|
105
|
+
@list.each do |api|
|
|
106
|
+
begin
|
|
107
|
+
results << yield(api)
|
|
108
|
+
rescue Sibit::Error => e
|
|
109
|
+
@log.info("The API #{api.class.name} failed at #{method}(): #{e.message}")
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
if results.empty?
|
|
113
|
+
raise Sibit::Error, "No APIs out of #{@api.length} managed to succeed at #{method}(): \
|
|
114
|
+
#{@api.map { |a| a.class.name }.join(', ')}"
|
|
115
|
+
end
|
|
116
|
+
results.group_by(&:to_s).values.max_by(&:size)[0]
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
data/lib/sibit/bitcoinchain.rb
CHANGED
|
@@ -45,21 +45,24 @@ class Sibit
|
|
|
45
45
|
|
|
46
46
|
# Current price of BTC in USD (float returned).
|
|
47
47
|
def price(_currency = 'USD')
|
|
48
|
-
raise Sibit::
|
|
48
|
+
raise Sibit::NotSupportedError, 'Bitcoinchain API doesn\'t provide BTC price'
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
# The height of the block.
|
|
52
52
|
def height(_hash)
|
|
53
|
-
raise Sibit::
|
|
53
|
+
raise Sibit::NotSupportedError, 'Bitcoinchain API doesn\'t provide height()'
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
# Get hash of the block after this one.
|
|
57
57
|
def next_of(hash)
|
|
58
|
-
|
|
58
|
+
block = Sibit::Json.new(http: @http, log: @log).get(
|
|
59
59
|
URI("https://api-r.bitcoinchain.com/v1/block/#{hash}")
|
|
60
|
-
)[0]
|
|
60
|
+
)[0]
|
|
61
|
+
raise Sibit::Error, "Block #{hash} not found" if block.nil?
|
|
62
|
+
nxt = block['next_block']
|
|
61
63
|
nxt = nil if nxt == '0000000000000000000000000000000000000000000000000000000000000000'
|
|
62
|
-
@log.info("The
|
|
64
|
+
@log.info("The block #{hash} is the latest, there is no next block") if nxt.nil?
|
|
65
|
+
@log.info("The next block of #{hash} is #{nxt}") unless nxt.nil?
|
|
63
66
|
nxt
|
|
64
67
|
end
|
|
65
68
|
|
|
@@ -76,13 +79,13 @@ class Sibit
|
|
|
76
79
|
end
|
|
77
80
|
b *= 100_000_000
|
|
78
81
|
b = b.to_i
|
|
79
|
-
@log.info("The balance of #{address} is #{b} satoshi")
|
|
82
|
+
@log.info("The balance of #{address} is #{b} satoshi (#{json['transactions']} txns)")
|
|
80
83
|
b
|
|
81
84
|
end
|
|
82
85
|
|
|
83
86
|
# Get recommended fees, in satoshi per byte.
|
|
84
87
|
def fees
|
|
85
|
-
raise Sibit::
|
|
88
|
+
raise Sibit::NotSupportedError, 'Not implemented yet'
|
|
86
89
|
end
|
|
87
90
|
|
|
88
91
|
# Gets the hash of the latest block.
|
|
@@ -96,12 +99,12 @@ class Sibit
|
|
|
96
99
|
|
|
97
100
|
# Fetch all unspent outputs per address.
|
|
98
101
|
def utxos(_sources)
|
|
99
|
-
raise Sibit::
|
|
102
|
+
raise Sibit::NotSupportedError, 'Not implemented yet'
|
|
100
103
|
end
|
|
101
104
|
|
|
102
105
|
# Push this transaction (in hex format) to the network.
|
|
103
106
|
def push(_hex)
|
|
104
|
-
raise Sibit::
|
|
107
|
+
raise Sibit::NotSupportedError, 'Not implemented yet'
|
|
105
108
|
end
|
|
106
109
|
|
|
107
110
|
# This method should fetch a Blockchain block and return as a hash. Raises
|
data/lib/sibit/blockchain.rb
CHANGED
|
@@ -59,7 +59,7 @@ class Sibit
|
|
|
59
59
|
|
|
60
60
|
# Get hash of the block after this one.
|
|
61
61
|
def next_of(_hash)
|
|
62
|
-
raise Sibit::
|
|
62
|
+
raise Sibit::NotSupportedError, 'Blockchain API doesn\'t provide next_of()'
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
# The height of the block.
|
|
@@ -78,13 +78,14 @@ class Sibit
|
|
|
78
78
|
URI("https://blockchain.info/rawaddr/#{address}?limit=0"),
|
|
79
79
|
accept: [200, 500]
|
|
80
80
|
)
|
|
81
|
-
|
|
82
|
-
json['
|
|
81
|
+
b = json['final_balance']
|
|
82
|
+
@log.info("The balance of #{address} is #{b} satoshi (#{json['n_tx']} txns)")
|
|
83
|
+
b
|
|
83
84
|
end
|
|
84
85
|
|
|
85
86
|
# Get recommended fees.
|
|
86
87
|
def fees
|
|
87
|
-
raise Sibit::
|
|
88
|
+
raise Sibit::NotSupportedError, 'fees() is not provided by Blockchain API'
|
|
88
89
|
end
|
|
89
90
|
|
|
90
91
|
# Fetch all unspent outputs per address. The argument is an array
|
data/lib/sibit/blockchair.rb
CHANGED
|
@@ -47,18 +47,18 @@ class Sibit
|
|
|
47
47
|
|
|
48
48
|
# Current price of BTC in USD (float returned).
|
|
49
49
|
def price(_currency = 'USD')
|
|
50
|
-
raise Sibit::
|
|
50
|
+
raise Sibit::NotSupportedError, 'Blockchair doesn\'t provide BTC price'
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
# The height of the block.
|
|
54
54
|
def height(_hash)
|
|
55
|
-
raise Sibit::
|
|
55
|
+
raise Sibit::NotSupportedError, 'Blockchair API doesn\'t provide height()'
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
# Get hash of the block after this one.
|
|
59
59
|
def next_of(_hash)
|
|
60
60
|
# They don't provide next block hash
|
|
61
|
-
raise Sibit::
|
|
61
|
+
raise Sibit::NotSupportedError, 'Blockchair API doesn\'t provide next_of()'
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
# Gets the balance of the address, in satoshi.
|
|
@@ -70,24 +70,25 @@ class Sibit
|
|
|
70
70
|
@log.info("Address #{address} not found")
|
|
71
71
|
return 0
|
|
72
72
|
end
|
|
73
|
-
|
|
73
|
+
a = json['address']
|
|
74
|
+
b = a['balance']
|
|
74
75
|
@log.info("The balance of #{address} is #{b} satoshi")
|
|
75
76
|
b
|
|
76
77
|
end
|
|
77
78
|
|
|
78
79
|
# Get recommended fees, in satoshi per byte.
|
|
79
80
|
def fees
|
|
80
|
-
raise Sibit::
|
|
81
|
+
raise Sibit::NotSupportedError, 'Blockchair doesn\'t implement fees()'
|
|
81
82
|
end
|
|
82
83
|
|
|
83
84
|
# Gets the hash of the latest block.
|
|
84
85
|
def latest
|
|
85
|
-
raise Sibit::
|
|
86
|
+
raise Sibit::NotSupportedError, 'Blockchair doesn\'t implement latest()'
|
|
86
87
|
end
|
|
87
88
|
|
|
88
89
|
# Fetch all unspent outputs per address.
|
|
89
90
|
def utxos(_sources)
|
|
90
|
-
raise Sibit::
|
|
91
|
+
raise Sibit::NotSupportedError, 'Blockchair doesn\'t implement utxos()'
|
|
91
92
|
end
|
|
92
93
|
|
|
93
94
|
# Push this transaction (in hex format) to the network.
|
|
@@ -101,7 +102,7 @@ class Sibit
|
|
|
101
102
|
|
|
102
103
|
# This method should fetch a Blockchain block and return as a hash.
|
|
103
104
|
def block(_hash)
|
|
104
|
-
raise Sibit::
|
|
105
|
+
raise Sibit::NotSupportedError, 'Blockchair doesn\'t implement block()'
|
|
105
106
|
end
|
|
106
107
|
|
|
107
108
|
private
|
data/lib/sibit/btc.rb
CHANGED
|
@@ -47,7 +47,7 @@ class Sibit
|
|
|
47
47
|
|
|
48
48
|
# Current price of BTC in USD (float returned).
|
|
49
49
|
def price(_currency = 'USD')
|
|
50
|
-
raise Sibit::
|
|
50
|
+
raise Sibit::NotSupportedError, 'Btc.com API doesn\'t provide prices'
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
# Gets the balance of the address, in satoshi.
|
|
@@ -69,9 +69,12 @@ class Sibit
|
|
|
69
69
|
head = Sibit::Json.new(http: @http, log: @log).get(
|
|
70
70
|
URI("https://chain.api.btc.com/v3/block/#{hash}")
|
|
71
71
|
)
|
|
72
|
-
|
|
72
|
+
data = head['data']
|
|
73
|
+
raise Sibit::Error, "The block #{hash} not found" if data.nil?
|
|
74
|
+
nxt = data['next_block_hash']
|
|
73
75
|
nxt = nil if nxt == '0000000000000000000000000000000000000000000000000000000000000000'
|
|
74
|
-
@log.info("The
|
|
76
|
+
@log.info("The block #{hash} is the latest, there is no next block") if nxt.nil?
|
|
77
|
+
@log.info("The next block of #{hash} is #{nxt}") unless nxt.nil?
|
|
75
78
|
nxt
|
|
76
79
|
end
|
|
77
80
|
|
|
@@ -80,21 +83,25 @@ class Sibit
|
|
|
80
83
|
json = Sibit::Json.new(http: @http, log: @log).get(
|
|
81
84
|
URI("https://chain.api.btc.com/v3/block/#{hash}")
|
|
82
85
|
)
|
|
83
|
-
|
|
86
|
+
data = json['data']
|
|
87
|
+
raise Sibit::Error, "The block #{hash} not found" if data.nil?
|
|
88
|
+
h = data['height']
|
|
84
89
|
@log.info("The height of #{hash} is #{h}")
|
|
85
90
|
h
|
|
86
91
|
end
|
|
87
92
|
|
|
88
93
|
# Get recommended fees, in satoshi per byte.
|
|
89
94
|
def fees
|
|
90
|
-
raise Sibit::
|
|
95
|
+
raise Sibit::NotSupportedError, 'Btc.com doesn\'t provide recommended fees'
|
|
91
96
|
end
|
|
92
97
|
|
|
93
98
|
# Gets the hash of the latest block.
|
|
94
99
|
def latest
|
|
95
100
|
uri = URI('https://chain.api.btc.com/v3/block/latest')
|
|
96
101
|
json = Sibit::Json.new(http: @http, log: @log).get(uri)
|
|
97
|
-
|
|
102
|
+
data = json['data']
|
|
103
|
+
raise Sibit::Error, 'The latest block not found' if data.nil?
|
|
104
|
+
hash = data['hash']
|
|
98
105
|
@log.info("The hash of the latest block is #{hash}")
|
|
99
106
|
hash
|
|
100
107
|
end
|
|
@@ -106,7 +113,9 @@ class Sibit
|
|
|
106
113
|
json = Sibit::Json.new(http: @http, log: @log).get(
|
|
107
114
|
URI("https://chain.api.btc.com/v3/address/#{hash}/unspent")
|
|
108
115
|
)
|
|
109
|
-
json['data']
|
|
116
|
+
data = json['data']
|
|
117
|
+
raise Sibit::Error, "The address #{hash} not found" if data.nil?
|
|
118
|
+
data['list'].each do |u|
|
|
110
119
|
outs = Sibit::Json.new(http: @http, log: @log).get(
|
|
111
120
|
URI("https://chain.api.btc.com/v3/tx/#{u['tx_hash']}?verbose=3")
|
|
112
121
|
)['data']['outputs']
|
|
@@ -127,7 +136,7 @@ class Sibit
|
|
|
127
136
|
|
|
128
137
|
# Push this transaction (in hex format) to the network.
|
|
129
138
|
def push(_hex)
|
|
130
|
-
raise Sibit::
|
|
139
|
+
raise Sibit::NotSupportedError, 'Btc.com doesn\'t provide payment gateway'
|
|
131
140
|
end
|
|
132
141
|
|
|
133
142
|
# This method should fetch a Blockchain block and return as a hash.
|
|
@@ -135,13 +144,15 @@ class Sibit
|
|
|
135
144
|
head = Sibit::Json.new(http: @http, log: @log).get(
|
|
136
145
|
URI("https://chain.api.btc.com/v3/block/#{hash}")
|
|
137
146
|
)
|
|
138
|
-
|
|
147
|
+
data = head['data']
|
|
148
|
+
raise Sibit::Error, "The block #{hash} not found" if data.nil?
|
|
149
|
+
nxt = data['next_block_hash']
|
|
139
150
|
nxt = nil if nxt == '0000000000000000000000000000000000000000000000000000000000000000'
|
|
140
151
|
{
|
|
141
|
-
hash:
|
|
142
|
-
orphan:
|
|
152
|
+
hash: data['hash'],
|
|
153
|
+
orphan: data['is_orphan'],
|
|
143
154
|
next: nxt,
|
|
144
|
-
previous:
|
|
155
|
+
previous: data['prev_block_hash'],
|
|
145
156
|
txns: txns(hash)
|
|
146
157
|
}
|
|
147
158
|
end
|
data/lib/sibit/cex.rb
CHANGED
|
@@ -54,41 +54,41 @@ class Sibit
|
|
|
54
54
|
|
|
55
55
|
# Get hash of the block after this one.
|
|
56
56
|
def next_of(_hash)
|
|
57
|
-
raise Sibit::
|
|
57
|
+
raise Sibit::NotSupportedError, 'Cex.io API doesn\'t provide next_of()'
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
# Gets the balance of the address, in satoshi.
|
|
61
61
|
def balance(_address)
|
|
62
|
-
raise Sibit::
|
|
62
|
+
raise Sibit::NotSupportedError, 'Cex.io doesn\'t implement balance()'
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
# The height of the block.
|
|
66
66
|
def height(_hash)
|
|
67
|
-
raise Sibit::
|
|
67
|
+
raise Sibit::NotSupportedError, 'Cex.io doesn\'t implement height()'
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
# Get recommended fees, in satoshi per byte.
|
|
71
71
|
def fees
|
|
72
|
-
raise Sibit::
|
|
72
|
+
raise Sibit::NotSupportedError, 'Cex.io doesn\'t implement fees()'
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
# Gets the hash of the latest block.
|
|
76
76
|
def latest
|
|
77
|
-
raise Sibit::
|
|
77
|
+
raise Sibit::NotSupportedError, 'Cex.io doesn\'t implement latest()'
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
# Fetch all unspent outputs per address.
|
|
81
81
|
def utxos(_sources)
|
|
82
|
-
raise Sibit::
|
|
82
|
+
raise Sibit::NotSupportedError, 'Cex.io doesn\'t implement utxos()'
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
# Push this transaction (in hex format) to the network.
|
|
86
86
|
def push(_hex)
|
|
87
|
-
raise Sibit::
|
|
87
|
+
raise Sibit::NotSupportedError, 'Cex.io doesn\'t implement push()'
|
|
88
88
|
end
|
|
89
89
|
|
|
90
90
|
def block(_hash)
|
|
91
|
-
raise Sibit::
|
|
91
|
+
raise Sibit::NotSupportedError, 'Cex.io doesn\'t implement block()'
|
|
92
92
|
end
|
|
93
93
|
end
|
|
94
94
|
end
|
data/lib/sibit/cryptoapis.rb
CHANGED
|
@@ -46,15 +46,18 @@ class Sibit
|
|
|
46
46
|
|
|
47
47
|
# Current price of BTC in USD (float returned).
|
|
48
48
|
def price(_currency = 'USD')
|
|
49
|
-
raise Sibit::
|
|
49
|
+
raise Sibit::NotSupportedError, 'Cryptoapis doesn\'t provide BTC price'
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
# Get hash of the block after this one.
|
|
53
53
|
def next_of(hash)
|
|
54
|
-
Sibit::Json.new(http: @http, log: @log).get(
|
|
54
|
+
nxt = Sibit::Json.new(http: @http, log: @log).get(
|
|
55
55
|
URI("https://api.cryptoapis.io/v1/bc/btc/mainnet/blocks/#{hash}"),
|
|
56
56
|
headers: headers
|
|
57
57
|
)['payload']['hash']
|
|
58
|
+
@log.info("The block #{hash} is the latest, there is no next block") if nxt.nil?
|
|
59
|
+
@log.info("The next block of #{hash} is #{nxt}") unless nxt.nil?
|
|
60
|
+
nxt
|
|
58
61
|
end
|
|
59
62
|
|
|
60
63
|
# The height of the block.
|
|
@@ -81,7 +84,7 @@ class Sibit
|
|
|
81
84
|
|
|
82
85
|
# Get recommended fees, in satoshi per byte.
|
|
83
86
|
def fees
|
|
84
|
-
raise Sibit::
|
|
87
|
+
raise Sibit::NotSupportedError, 'Cryptoapis doesn\'t provide recommended fees'
|
|
85
88
|
end
|
|
86
89
|
|
|
87
90
|
# Gets the hash of the latest block.
|
|
@@ -96,7 +99,7 @@ class Sibit
|
|
|
96
99
|
|
|
97
100
|
# Fetch all unspent outputs per address.
|
|
98
101
|
def utxos(_sources)
|
|
99
|
-
raise Sibit::
|
|
102
|
+
raise Sibit::NotSupportedError, 'Not implemented yet'
|
|
100
103
|
end
|
|
101
104
|
|
|
102
105
|
# Push this transaction (in hex format) to the network.
|
data/lib/sibit/earn.rb
CHANGED
|
@@ -44,22 +44,22 @@ class Sibit
|
|
|
44
44
|
|
|
45
45
|
# Current price of BTC in USD (float returned).
|
|
46
46
|
def price(_currency)
|
|
47
|
-
raise Sibit::
|
|
47
|
+
raise Sibit::NotSupportedError, 'price() doesn\'t work here'
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
# Gets the balance of the address, in satoshi.
|
|
51
51
|
def balance(_address)
|
|
52
|
-
raise Sibit::
|
|
52
|
+
raise Sibit::NotSupportedError, 'balance() doesn\'t work here'
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
# Get hash of the block after this one.
|
|
56
56
|
def next_of(_hash)
|
|
57
|
-
raise Sibit::
|
|
57
|
+
raise Sibit::NotSupportedError, 'Earn.com API doesn\'t provide next_of()'
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
# The height of the block.
|
|
61
61
|
def height(_hash)
|
|
62
|
-
raise Sibit::
|
|
62
|
+
raise Sibit::NotSupportedError, 'Earn API doesn\'t provide height()'
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
# Get recommended fees, in satoshi per byte. The method returns
|
|
@@ -80,22 +80,22 @@ class Sibit
|
|
|
80
80
|
|
|
81
81
|
# Fetch all unspent outputs per address.
|
|
82
82
|
def utxos(_sources)
|
|
83
|
-
raise Sibit::
|
|
83
|
+
raise Sibit::NotSupportedError, 'Not implemented yet'
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
# Push this transaction (in hex format) to the network.
|
|
87
87
|
def push(_hex)
|
|
88
|
-
raise Sibit::
|
|
88
|
+
raise Sibit::NotSupportedError, 'Not implemented yet'
|
|
89
89
|
end
|
|
90
90
|
|
|
91
91
|
# Gets the hash of the latest block.
|
|
92
92
|
def latest
|
|
93
|
-
raise Sibit::
|
|
93
|
+
raise Sibit::NotSupportedError, 'latest() doesn\'t work here'
|
|
94
94
|
end
|
|
95
95
|
|
|
96
96
|
# This method should fetch a Blockchain block and return as a hash.
|
|
97
97
|
def block(_hash)
|
|
98
|
-
raise Sibit::
|
|
98
|
+
raise Sibit::NotSupportedError, 'block() doesn\'t work here'
|
|
99
99
|
end
|
|
100
100
|
end
|
|
101
101
|
end
|
data/lib/sibit/error.rb
CHANGED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2019-2020 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_relative 'error'
|
|
24
|
+
require_relative 'log'
|
|
25
|
+
|
|
26
|
+
# API first of.
|
|
27
|
+
#
|
|
28
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
|
29
|
+
# Copyright:: Copyright (c) 2019-2020 Yegor Bugayenko
|
|
30
|
+
# License:: MIT
|
|
31
|
+
class Sibit
|
|
32
|
+
# First of API.
|
|
33
|
+
class FirstOf
|
|
34
|
+
# Constructor.
|
|
35
|
+
def initialize(list, log: Sibit::Log.new)
|
|
36
|
+
@list = list
|
|
37
|
+
@log = log
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Current price of BTC in USD (float returned).
|
|
41
|
+
def price(currency = 'USD')
|
|
42
|
+
first_of('price') do |api|
|
|
43
|
+
api.price(currency)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Gets the balance of the address, in satoshi.
|
|
48
|
+
def balance(address)
|
|
49
|
+
first_of('balance') do |api|
|
|
50
|
+
api.balance(address)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Get the height of the block.
|
|
55
|
+
def height(hash)
|
|
56
|
+
first_of('height') do |api|
|
|
57
|
+
api.height(hash)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Get the hash of the next block.
|
|
62
|
+
def next_of(hash)
|
|
63
|
+
first_of('next_of') do |api|
|
|
64
|
+
api.next_of(hash)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Get recommended fees, in satoshi per byte. The method returns
|
|
69
|
+
# a hash: { S: 12, M: 45, L: 100, XL: 200 }
|
|
70
|
+
def fees
|
|
71
|
+
first_of('fees', &:fees)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Fetch all unspent outputs per address.
|
|
75
|
+
def utxos(keys)
|
|
76
|
+
first_of('utxos') do |api|
|
|
77
|
+
api.utxos(keys)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Latest block hash.
|
|
82
|
+
def latest
|
|
83
|
+
first_of('latest', &:latest)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Push this transaction (in hex format) to the network.
|
|
87
|
+
def push(hex)
|
|
88
|
+
first_of('push') do |api|
|
|
89
|
+
api.push(hex)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# This method should fetch a block and return as a hash.
|
|
94
|
+
def block(hash)
|
|
95
|
+
first_of('block') do |api|
|
|
96
|
+
api.block(hash)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
|
|
102
|
+
def first_of(method)
|
|
103
|
+
return yield @list unless @list.is_a?(Array)
|
|
104
|
+
done = false
|
|
105
|
+
result = nil
|
|
106
|
+
@list.each do |api|
|
|
107
|
+
begin
|
|
108
|
+
result = yield api
|
|
109
|
+
done = true
|
|
110
|
+
break
|
|
111
|
+
rescue Sibit::Error => e
|
|
112
|
+
@log.info("The API #{api.class.name} failed at #{method}(): #{e.message}")
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
unless done
|
|
116
|
+
raise Sibit::Error, "No APIs out of #{@api.length} managed to succeed at #{method}(): \
|
|
117
|
+
#{@api.map { |a| a.class.name }.join(', ')}"
|
|
118
|
+
end
|
|
119
|
+
result
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
data/lib/sibit/version.rb
CHANGED
data/sibit.gemspec
CHANGED
|
@@ -36,9 +36,9 @@ Gem::Specification.new do |s|
|
|
|
36
36
|
s.version = Sibit::VERSION
|
|
37
37
|
s.license = 'MIT'
|
|
38
38
|
s.summary = 'Simple Bitcoin Client'
|
|
39
|
-
s.description = 'This is a simple Bitcoin client, to use from command line
|
|
40
|
-
or from your Ruby app. You don\'t need to run any Bitcoin software,
|
|
41
|
-
no need to install anything, etc. All you need is just a command line
|
|
39
|
+
s.description = 'This is a simple Bitcoin client, to use from command line
|
|
40
|
+
or from your Ruby app. You don\'t need to run any Bitcoin software,
|
|
41
|
+
no need to install anything, etc. All you need is just a command line
|
|
42
42
|
and Ruby 2.3+.'
|
|
43
43
|
s.authors = ['Yegor Bugayenko']
|
|
44
44
|
s.email = 'yegor256@gmail.com'
|
data/test/test_bestof.rb
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2019-2020 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_relative '../lib/sibit'
|
|
25
|
+
require_relative '../lib/sibit/fake'
|
|
26
|
+
require_relative '../lib/sibit/bestof'
|
|
27
|
+
|
|
28
|
+
# Sibit::BestOf test.
|
|
29
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
|
30
|
+
# Copyright:: Copyright (c) 2019-2020 Yegor Bugayenko
|
|
31
|
+
# License:: MIT
|
|
32
|
+
class TestBestOf < Minitest::Test
|
|
33
|
+
def test_not_array
|
|
34
|
+
sibit = Sibit::BestOf.new(Sibit::Fake.new)
|
|
35
|
+
assert_equal(64, sibit.latest.length)
|
|
36
|
+
assert_equal(12, sibit.fees[:S])
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def test_one_apis
|
|
40
|
+
sibit = Sibit::BestOf.new([Sibit::Fake.new])
|
|
41
|
+
assert_equal(64, sibit.latest.length)
|
|
42
|
+
assert_equal(12, sibit.fees[:S])
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_two_apis
|
|
46
|
+
sibit = Sibit::BestOf.new([Sibit::Fake.new, Sibit::Fake.new])
|
|
47
|
+
assert_equal(64, sibit.latest.length)
|
|
48
|
+
assert_equal(12, sibit.fees[:S])
|
|
49
|
+
end
|
|
50
|
+
end
|
data/test/test_blockchair.rb
CHANGED
|
@@ -34,7 +34,8 @@ class TestBlockchair < Minitest::Test
|
|
|
34
34
|
def test_fetch_balance
|
|
35
35
|
hash = '1GkQmKAmHtNfnD3LHhTkewJxKHVSta4m2a'
|
|
36
36
|
stub_request(:get, "https://api.blockchair.com/bitcoin/dashboards/address/#{hash}")
|
|
37
|
-
.to_return(body: "{\"data\": {\"#{hash}\": {\"address\":
|
|
37
|
+
.to_return(body: "{\"data\": {\"#{hash}\": {\"address\":
|
|
38
|
+
{\"balance\": 1, \"transactions\": []}}}}")
|
|
38
39
|
sibit = Sibit::Blockchair.new
|
|
39
40
|
satoshi = sibit.balance(hash)
|
|
40
41
|
assert_equal(1, satoshi)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright (c) 2019-2020 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_relative '../lib/sibit'
|
|
25
|
+
require_relative '../lib/sibit/fake'
|
|
26
|
+
require_relative '../lib/sibit/firstof'
|
|
27
|
+
|
|
28
|
+
# Sibit::FirstOf test.
|
|
29
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
|
30
|
+
# Copyright:: Copyright (c) 2019-2020 Yegor Bugayenko
|
|
31
|
+
# License:: MIT
|
|
32
|
+
class TestFirstOf < Minitest::Test
|
|
33
|
+
def test_not_array
|
|
34
|
+
sibit = Sibit::FirstOf.new(Sibit::Fake.new)
|
|
35
|
+
assert_equal(64, sibit.latest.length)
|
|
36
|
+
assert_equal(12, sibit.fees[:S])
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def test_one_apis
|
|
40
|
+
sibit = Sibit::FirstOf.new([Sibit::Fake.new])
|
|
41
|
+
assert_equal(64, sibit.latest.length)
|
|
42
|
+
assert_equal(12, sibit.fees[:S])
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_two_apis
|
|
46
|
+
sibit = Sibit::FirstOf.new([Sibit::Fake.new, Sibit::Fake.new])
|
|
47
|
+
assert_equal(64, sibit.latest.length)
|
|
48
|
+
assert_equal(12, sibit.fees[:S])
|
|
49
|
+
end
|
|
50
|
+
end
|
data/test/test_sibit.rb
CHANGED
|
@@ -27,6 +27,8 @@ require_relative '../lib/sibit'
|
|
|
27
27
|
require_relative '../lib/sibit/earn'
|
|
28
28
|
require_relative '../lib/sibit/fake'
|
|
29
29
|
require_relative '../lib/sibit/blockchain'
|
|
30
|
+
require_relative '../lib/sibit/firstof'
|
|
31
|
+
require_relative '../lib/sibit/bestof'
|
|
30
32
|
|
|
31
33
|
# Sibit.
|
|
32
34
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
|
@@ -127,7 +129,7 @@ class TestSibit < Minitest::Test
|
|
|
127
129
|
'https://blockchain.info/unspent?active=1JvCsJtLmCxEk7ddZFnVkGXpr9uhxZPmJi&limit=1000'
|
|
128
130
|
).to_return(body: JSON.pretty_generate(json))
|
|
129
131
|
stub_request(:post, 'https://blockchain.info/pushtx').to_return(status: 200)
|
|
130
|
-
sibit = Sibit.new(api: [Sibit::Earn.new, Sibit::Blockchain.new])
|
|
132
|
+
sibit = Sibit.new(api: Sibit::FirstOf.new([Sibit::Earn.new, Sibit::Blockchain.new]))
|
|
131
133
|
target = sibit.create(sibit.generate)
|
|
132
134
|
change = sibit.create(sibit.generate)
|
|
133
135
|
tx = sibit.pay(
|
|
@@ -156,7 +158,7 @@ class TestSibit < Minitest::Test
|
|
|
156
158
|
:get,
|
|
157
159
|
'https://blockchain.info/unspent?active=1JvCsJtLmCxEk7ddZFnVkGXpr9uhxZPmJi&limit=1000'
|
|
158
160
|
).to_return(body: JSON.pretty_generate(json))
|
|
159
|
-
sibit = Sibit.new
|
|
161
|
+
sibit = Sibit.new(api: Sibit::BestOf.new([Sibit::Fake.new, Sibit::Fake.new]))
|
|
160
162
|
target = sibit.create(sibit.generate)
|
|
161
163
|
change = sibit.create(sibit.generate)
|
|
162
164
|
assert_raises Sibit::Error do
|
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.
|
|
4
|
+
version: 0.19.0
|
|
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-
|
|
11
|
+
date: 2020-06-17 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: backtrace
|
|
@@ -249,9 +249,9 @@ dependencies:
|
|
|
249
249
|
- !ruby/object:Gem::Version
|
|
250
250
|
version: 3.7.6
|
|
251
251
|
description: |-
|
|
252
|
-
This is a simple Bitcoin client, to use from command line
|
|
253
|
-
or from your Ruby app. You don't need to run any Bitcoin software,
|
|
254
|
-
no need to install anything, etc. All you need is just a command line
|
|
252
|
+
This is a simple Bitcoin client, to use from command line
|
|
253
|
+
or from your Ruby app. You don't need to run any Bitcoin software,
|
|
254
|
+
no need to install anything, etc. All you need is just a command line
|
|
255
255
|
and Ruby 2.3+.
|
|
256
256
|
email: yegor256@gmail.com
|
|
257
257
|
executables:
|
|
@@ -281,6 +281,7 @@ files:
|
|
|
281
281
|
- features/step_definitions/steps.rb
|
|
282
282
|
- features/support/env.rb
|
|
283
283
|
- lib/sibit.rb
|
|
284
|
+
- lib/sibit/bestof.rb
|
|
284
285
|
- lib/sibit/bitcoinchain.rb
|
|
285
286
|
- lib/sibit/blockchain.rb
|
|
286
287
|
- lib/sibit/blockchair.rb
|
|
@@ -290,6 +291,7 @@ files:
|
|
|
290
291
|
- lib/sibit/earn.rb
|
|
291
292
|
- lib/sibit/error.rb
|
|
292
293
|
- lib/sibit/fake.rb
|
|
294
|
+
- lib/sibit/firstof.rb
|
|
293
295
|
- lib/sibit/http.rb
|
|
294
296
|
- lib/sibit/json.rb
|
|
295
297
|
- lib/sibit/log.rb
|
|
@@ -297,6 +299,7 @@ files:
|
|
|
297
299
|
- logo.svg
|
|
298
300
|
- sibit.gemspec
|
|
299
301
|
- test/test__helper.rb
|
|
302
|
+
- test/test_bestof.rb
|
|
300
303
|
- test/test_bitcoinchain.rb
|
|
301
304
|
- test/test_blockchain.rb
|
|
302
305
|
- test/test_blockchair.rb
|
|
@@ -304,6 +307,7 @@ files:
|
|
|
304
307
|
- test/test_cex.rb
|
|
305
308
|
- test/test_cryptoapis.rb
|
|
306
309
|
- test/test_fake.rb
|
|
310
|
+
- test/test_firstof.rb
|
|
307
311
|
- test/test_json.rb
|
|
308
312
|
- test/test_live.rb
|
|
309
313
|
- test/test_sibit.rb
|
|
@@ -337,6 +341,7 @@ test_files:
|
|
|
337
341
|
- features/step_definitions/steps.rb
|
|
338
342
|
- features/support/env.rb
|
|
339
343
|
- test/test__helper.rb
|
|
344
|
+
- test/test_bestof.rb
|
|
340
345
|
- test/test_bitcoinchain.rb
|
|
341
346
|
- test/test_blockchain.rb
|
|
342
347
|
- test/test_blockchair.rb
|
|
@@ -344,6 +349,7 @@ test_files:
|
|
|
344
349
|
- test/test_cex.rb
|
|
345
350
|
- test/test_cryptoapis.rb
|
|
346
351
|
- test/test_fake.rb
|
|
352
|
+
- test/test_firstof.rb
|
|
347
353
|
- test/test_json.rb
|
|
348
354
|
- test/test_live.rb
|
|
349
355
|
- test/test_sibit.rb
|