sibit 0.33.0 → 0.34.1
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/Gemfile +4 -5
- data/Gemfile.lock +78 -58
- data/README.md +21 -2
- data/Rakefile +1 -1
- data/bin/sibit +6 -4
- data/cross/alpine.df +6 -0
- data/cross/centos.df +8 -0
- data/cross/debian.df +9 -0
- data/cross/fedora.df +7 -0
- data/cross/opensuse.df +17 -0
- data/cross/rocky.df +8 -0
- data/cross/ubuntu.df +9 -0
- data/features/step_definitions/steps.rb +5 -5
- data/features/support/env.rb +1 -1
- data/lib/sibit/base58.rb +3 -6
- data/lib/sibit/bech32.rb +23 -15
- data/lib/sibit/bestof.rb +41 -11
- data/lib/sibit/bin.rb +68 -54
- data/lib/sibit/bitcoinchain.rb +10 -10
- data/lib/sibit/blockchain.rb +10 -20
- data/lib/sibit/blockchair.rb +14 -24
- data/lib/sibit/btc.rb +51 -60
- data/lib/sibit/cex.rb +12 -11
- data/lib/sibit/coins.rb +35 -0
- data/lib/sibit/cryptoapis.rb +35 -30
- data/lib/sibit/firstof.rb +8 -5
- data/lib/sibit/httpproxy.rb +1 -1
- data/lib/sibit/json.rb +8 -9
- data/lib/sibit/key.rb +39 -41
- data/lib/sibit/script.rb +3 -6
- data/lib/sibit/sochain.rb +180 -0
- data/lib/sibit/tx.rb +75 -36
- data/lib/sibit/txbuilder.rb +15 -3
- data/lib/sibit/version.rb +1 -2
- data/lib/sibit.rb +80 -62
- data/sibit.gemspec +11 -11
- metadata +10 -1
data/lib/sibit/bestof.rb
CHANGED
|
@@ -66,11 +66,26 @@ class Sibit::BestOf
|
|
|
66
66
|
best_of('latest', &:latest)
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
-
# Push this transaction (in hex format) to the network.
|
|
69
|
+
# Push this transaction (in hex format) to the network. This one does
|
|
70
|
+
# not vote: pushing is a side effect, so it hands the transaction to
|
|
71
|
+
# each API in turn and stops at the first that accepts it.
|
|
70
72
|
def push(hex)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
+
return @list.push(hex) unless @list.is_a?(Array)
|
|
74
|
+
errors = []
|
|
75
|
+
@list.each do |api|
|
|
76
|
+
return api.push(hex)
|
|
77
|
+
rescue Sibit::NotSupportedError
|
|
78
|
+
nil
|
|
79
|
+
rescue Sibit::Error => e
|
|
80
|
+
errors << e
|
|
81
|
+
@log.debug("The API #{api.class.name} failed at push(): #{e.message}") if @verbose
|
|
73
82
|
end
|
|
83
|
+
errors.each { |e| @log.debug(Backtrace.new(e).to_s) }
|
|
84
|
+
raise(
|
|
85
|
+
Sibit::Error,
|
|
86
|
+
"No APIs out of #{@list.length} managed to succeed at push(): " \
|
|
87
|
+
"#{@list.map { |a| a.class.name }.join(', ')}"
|
|
88
|
+
)
|
|
74
89
|
end
|
|
75
90
|
|
|
76
91
|
# This method should fetch a block and return as a hash.
|
|
@@ -83,22 +98,37 @@ class Sibit::BestOf
|
|
|
83
98
|
private
|
|
84
99
|
|
|
85
100
|
def best_of(method)
|
|
86
|
-
return yield
|
|
87
|
-
|
|
101
|
+
return yield(@list) unless @list.is_a?(Array)
|
|
102
|
+
votes = []
|
|
88
103
|
errors = []
|
|
89
104
|
@list.each do |api|
|
|
90
|
-
|
|
105
|
+
votes << [api.class.name, yield(api)]
|
|
91
106
|
rescue Sibit::NotSupportedError
|
|
92
|
-
|
|
107
|
+
nil
|
|
93
108
|
rescue Sibit::Error => e
|
|
94
109
|
errors << e
|
|
95
110
|
@log.debug("The API #{api.class.name} failed at #{method}(): #{e.message}") if @verbose
|
|
96
111
|
end
|
|
97
|
-
if
|
|
112
|
+
if votes.empty?
|
|
98
113
|
errors.each { |e| @log.debug(Backtrace.new(e).to_s) }
|
|
99
|
-
raise
|
|
100
|
-
|
|
114
|
+
raise(
|
|
115
|
+
Sibit::Error,
|
|
116
|
+
"No APIs out of #{@list.length} managed to succeed at #{method}(): " \
|
|
117
|
+
"#{@list.map { |a| a.class.name }.join(', ')}"
|
|
118
|
+
)
|
|
119
|
+
end
|
|
120
|
+
majority(method, votes)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def majority(method, votes)
|
|
124
|
+
winner = votes.group_by { |v| v[1].to_s }.values.max_by(&:size)
|
|
125
|
+
if winner.size <= votes.size / 2
|
|
126
|
+
raise(
|
|
127
|
+
Sibit::Error,
|
|
128
|
+
"No strict majority among #{votes.size} answers at #{method}(): " \
|
|
129
|
+
"#{votes.map { |name, answer| "#{name}=#{answer}" }.join(', ')}"
|
|
130
|
+
)
|
|
101
131
|
end
|
|
102
|
-
|
|
132
|
+
winner[0][1]
|
|
103
133
|
end
|
|
104
134
|
end
|
data/lib/sibit/bin.rb
CHANGED
|
@@ -22,16 +22,13 @@ class Sibit
|
|
|
22
22
|
class Bin < Thor
|
|
23
23
|
stop_on_unknown_option!
|
|
24
24
|
|
|
25
|
-
class_option :proxy, type: :string,
|
|
26
|
-
desc: 'HTTPS proxy for all requests, e.g. "localhost:3128"'
|
|
25
|
+
class_option :proxy, type: :string, desc: 'HTTPS proxy for all requests, e.g. "localhost:3128"'
|
|
27
26
|
class_option :attempts, type: :numeric, default: 1,
|
|
28
27
|
desc: 'How many times should we try before failing'
|
|
29
28
|
class_option :dry, type: :boolean, default: false,
|
|
30
29
|
desc: "Don't send a real payment, run in a read-only mode"
|
|
31
|
-
class_option :verbose, type: :boolean, default: false,
|
|
32
|
-
|
|
33
|
-
class_option :quiet, type: :boolean, default: false,
|
|
34
|
-
desc: 'Print only informative messages'
|
|
30
|
+
class_option :verbose, type: :boolean, default: false, desc: 'Print all possible debug messages'
|
|
31
|
+
class_option :quiet, type: :boolean, default: false, desc: 'Print only informative messages'
|
|
35
32
|
class_option :api, type: :array, default: %w[blockchain btc bitcoinchain blockchair cex],
|
|
36
33
|
desc: 'Ordered List of APIs to use, e.g. "blockchain,btc,bitcoinchain"'
|
|
37
34
|
class_option :base58, type: :boolean, default: false,
|
|
@@ -45,10 +42,10 @@ class Sibit
|
|
|
45
42
|
return new.help(command.name) if args.include?('--help') || args.include?('-h')
|
|
46
43
|
unknown = args.find { |a| a.start_with?('-') }
|
|
47
44
|
if unknown
|
|
48
|
-
warn
|
|
49
|
-
exit
|
|
45
|
+
warn("Unknown option: #{unknown}")
|
|
46
|
+
exit(1)
|
|
50
47
|
end
|
|
51
|
-
raise
|
|
48
|
+
raise(error)
|
|
52
49
|
end
|
|
53
50
|
|
|
54
51
|
desc 'price', 'Get current price of BTC in USD'
|
|
@@ -60,12 +57,12 @@ class Sibit
|
|
|
60
57
|
def fees
|
|
61
58
|
sibit = client
|
|
62
59
|
fees = sibit.fees
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
60
|
+
log.info(
|
|
61
|
+
%i[S M L XL].map do |m|
|
|
62
|
+
sat = fees[m] * 250
|
|
63
|
+
"#{m}: #{sat}sat / $#{format('%<usd>.02f', usd: (sat * sibit.price / 100_000_000))}"
|
|
64
|
+
end.join("\n")
|
|
65
|
+
)
|
|
69
66
|
end
|
|
70
67
|
|
|
71
68
|
desc 'latest', 'Get hash of the latest block'
|
|
@@ -86,8 +83,10 @@ class Sibit
|
|
|
86
83
|
end
|
|
87
84
|
|
|
88
85
|
desc 'balance ADDRESS', 'Check the balance of the Bitcoin address'
|
|
86
|
+
option :trust, type: :numeric, default: 0,
|
|
87
|
+
desc: 'Minimum number of confirmations required for a UTXO to count toward the balance'
|
|
89
88
|
def balance(address)
|
|
90
|
-
log.info(client.balance(address))
|
|
89
|
+
log.info(client.balance(address, trust: options[:trust]))
|
|
91
90
|
end
|
|
92
91
|
|
|
93
92
|
desc \
|
|
@@ -97,26 +96,34 @@ class Sibit
|
|
|
97
96
|
desc: 'List of UTXO that must be skipped while paying'
|
|
98
97
|
option :yes, type: :boolean, default: false,
|
|
99
98
|
desc: 'Skip confirmation prompt and send the payment immediately'
|
|
100
|
-
option :price, type: :numeric,
|
|
101
|
-
|
|
99
|
+
option :price, type: :numeric, desc: 'BTC price in USD (skips API price fetch if provided)'
|
|
100
|
+
option :trust, type: :numeric, default: 0,
|
|
101
|
+
desc: 'Minimum number of confirmations required on a UTXO before it can be spent'
|
|
102
102
|
def pay(amount, fee, sources, target, change)
|
|
103
103
|
keys = sources.split(',')
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
104
|
+
sweep = amount.upcase == 'MAX'
|
|
105
|
+
if sweep
|
|
106
|
+
addrs =
|
|
107
|
+
keys.map do |k|
|
|
108
|
+
kk = Sibit::Key.new(k)
|
|
109
|
+
options[:base58] ? kk.base58 : kk.bech32
|
|
110
|
+
end
|
|
111
|
+
amount = addrs.sum { |a| client.balance(a, trust: options[:trust]) }
|
|
110
112
|
end
|
|
111
|
-
amount = amount
|
|
112
|
-
fee = fee
|
|
113
|
+
amount = Integer(amount, 10) if amount.is_a?(String) && /^[0-9]+$/.match?(amount)
|
|
114
|
+
fee = Integer(fee, 10) if /^[0-9]+$/.match?(fee)
|
|
115
|
+
amount -= fee if sweep && fee.is_a?(Integer)
|
|
113
116
|
args = [amount, fee, keys, target, change]
|
|
114
|
-
kwargs = {
|
|
117
|
+
kwargs = {
|
|
118
|
+
skip_utxo: options[:skip_utxo], base58: options[:base58],
|
|
119
|
+
price: options[:price], trust: options[:trust]
|
|
120
|
+
}
|
|
115
121
|
unless options[:yes] || options[:dry]
|
|
116
122
|
client(dry: true).pay(*args, **kwargs)
|
|
117
|
-
print
|
|
118
|
-
|
|
119
|
-
|
|
123
|
+
print('Do you confirm this payment? (yes/no): ')
|
|
124
|
+
unless $stdin.gets&.strip&.downcase == 'yes'
|
|
125
|
+
raise(Sibit::Error, 'Payment cancelled by user')
|
|
126
|
+
end
|
|
120
127
|
end
|
|
121
128
|
log.info(client.pay(*args, **kwargs))
|
|
122
129
|
end
|
|
@@ -129,36 +136,43 @@ class Sibit
|
|
|
129
136
|
private
|
|
130
137
|
|
|
131
138
|
def log
|
|
132
|
-
@log
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
139
|
+
return @log if @log
|
|
140
|
+
@log =
|
|
141
|
+
if !options[:quiet] && (options[:verbose] || ENV.fetch('SIBIT_VERBOSE', nil))
|
|
142
|
+
Loog::VERBOSE
|
|
143
|
+
else
|
|
144
|
+
Loog::REGULAR
|
|
145
|
+
end
|
|
137
146
|
end
|
|
138
147
|
|
|
139
148
|
def client(dry: false)
|
|
140
149
|
proxy = options[:proxy] || ENV.fetch('SIBIT_PROXY', nil)
|
|
141
150
|
http = proxy ? Sibit::HttpProxy.new(proxy) : Sibit::Http.new
|
|
142
151
|
log.debug("Using proxy at #{http.host}") if proxy
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
152
|
+
api = Sibit::FirstOf.new(
|
|
153
|
+
options[:api].flat_map { |a| a.split(',') }.map(&:downcase).map do |a|
|
|
154
|
+
case a
|
|
155
|
+
when 'blockchain'
|
|
156
|
+
Sibit::Blockchain.new(http: http, log: log)
|
|
157
|
+
when 'btc'
|
|
158
|
+
Sibit::Btc.new(http: http, log: log)
|
|
159
|
+
when 'bitcoinchain'
|
|
160
|
+
Sibit::Bitcoinchain.new(http: http, log: log)
|
|
161
|
+
when 'blockchair'
|
|
162
|
+
Sibit::Blockchair.new(key: ENV.fetch('SIBIT_BLOCKCHAIR_KEY', nil), http: http, log: log)
|
|
163
|
+
when 'cex'
|
|
164
|
+
Sibit::Cex.new(http: http, log: log)
|
|
165
|
+
when 'cryptoapis'
|
|
166
|
+
Sibit::Cryptoapis.new(ENV.fetch('SIBIT_CRYPTOAPIS_KEY', nil), http: http, log: log)
|
|
167
|
+
when 'sochain'
|
|
168
|
+
Sibit::Sochain.new(http: http, log: log)
|
|
169
|
+
when 'fake'
|
|
170
|
+
Sibit::Fake.new
|
|
171
|
+
else
|
|
172
|
+
raise(Sibit::Error, "Unknown API \"#{a}\"")
|
|
173
|
+
end
|
|
174
|
+
end, log: log, verbose: true
|
|
175
|
+
)
|
|
162
176
|
api = Sibit::Dry.new(api, log: log) if options[:dry] || dry
|
|
163
177
|
api = RetriableProxy.for_object(api, on: Sibit::Error) if options[:attempts] > 1
|
|
164
178
|
Sibit.new(log: log, api: api)
|
data/lib/sibit/bitcoinchain.rb
CHANGED
|
@@ -7,6 +7,7 @@ require 'iri'
|
|
|
7
7
|
require 'json'
|
|
8
8
|
require 'loog'
|
|
9
9
|
require 'uri'
|
|
10
|
+
require_relative 'coins'
|
|
10
11
|
require_relative 'error'
|
|
11
12
|
require_relative 'http'
|
|
12
13
|
require_relative 'json'
|
|
@@ -26,12 +27,12 @@ class Sibit::Bitcoinchain
|
|
|
26
27
|
|
|
27
28
|
# Current price of BTC in USD (float returned).
|
|
28
29
|
def price(_currency = 'USD')
|
|
29
|
-
raise
|
|
30
|
+
raise(Sibit::NotSupportedError, 'Bitcoinchain API doesn\'t provide BTC price')
|
|
30
31
|
end
|
|
31
32
|
|
|
32
33
|
# The height of the block.
|
|
33
34
|
def height(_hash)
|
|
34
|
-
raise
|
|
35
|
+
raise(Sibit::NotSupportedError, 'Bitcoinchain API doesn\'t provide height()')
|
|
35
36
|
end
|
|
36
37
|
|
|
37
38
|
# Get hash of the block after this one.
|
|
@@ -39,7 +40,7 @@ class Sibit::Bitcoinchain
|
|
|
39
40
|
block = Sibit::Json.new(http: @http, log: @log).get(
|
|
40
41
|
Iri.new('https://api-r.bitcoinchain.com/v1/block').append(hash)
|
|
41
42
|
)[0]
|
|
42
|
-
raise
|
|
43
|
+
raise(Sibit::Error, "Block #{hash} not found") if block.nil?
|
|
43
44
|
nxt = block['next_block']
|
|
44
45
|
nxt = nil if nxt == '0000000000000000000000000000000000000000000000000000000000000000'
|
|
45
46
|
@log.debug("The block #{hash} is the latest, there is no next block") if nxt.nil?
|
|
@@ -58,15 +59,14 @@ class Sibit::Bitcoinchain
|
|
|
58
59
|
@log.debug("The balance of #{address} is not visible")
|
|
59
60
|
return 0
|
|
60
61
|
end
|
|
61
|
-
b
|
|
62
|
-
b = b.to_i
|
|
62
|
+
b = Sibit::Coins.new(b).satoshi
|
|
63
63
|
@log.debug("The balance of #{address} is #{b} satoshi (#{json['transactions']} txns)")
|
|
64
64
|
b
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
# Get recommended fees, in satoshi per byte.
|
|
68
68
|
def fees
|
|
69
|
-
raise
|
|
69
|
+
raise(Sibit::NotSupportedError, 'Not implemented yet')
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
# Gets the hash of the latest block.
|
|
@@ -80,12 +80,12 @@ class Sibit::Bitcoinchain
|
|
|
80
80
|
|
|
81
81
|
# Fetch all unspent outputs per address.
|
|
82
82
|
def utxos(_sources)
|
|
83
|
-
raise
|
|
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
|
|
88
|
+
raise(Sibit::NotSupportedError, 'Not implemented yet')
|
|
89
89
|
end
|
|
90
90
|
|
|
91
91
|
# This method should fetch a Blockchain block and return as a hash. Raises
|
|
@@ -94,7 +94,7 @@ class Sibit::Bitcoinchain
|
|
|
94
94
|
head = Sibit::Json.new(http: @http, log: @log).get(
|
|
95
95
|
Iri.new('https://api-r.bitcoinchain.com/v1/block').append(hash)
|
|
96
96
|
)[0]
|
|
97
|
-
raise
|
|
97
|
+
raise(Sibit::Error, "The block #{hash} is not found") if head.nil?
|
|
98
98
|
txs = Sibit::Json.new(http: @http, log: @log).get(
|
|
99
99
|
Iri.new('https://api-r.bitcoinchain.com/v1/block/txs').append(hash)
|
|
100
100
|
)
|
|
@@ -112,7 +112,7 @@ class Sibit::Bitcoinchain
|
|
|
112
112
|
outputs: t['outputs'].map do |o|
|
|
113
113
|
{
|
|
114
114
|
address: o['receiver'],
|
|
115
|
-
value: o['value']
|
|
115
|
+
value: Sibit::Coins.new(o['value']).satoshi
|
|
116
116
|
}
|
|
117
117
|
end
|
|
118
118
|
}
|
data/lib/sibit/blockchain.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
# SPDX-FileCopyrightText: Copyright (c) 2019-2026 Yegor Bugayenko
|
|
4
4
|
# SPDX-License-Identifier: MIT
|
|
5
5
|
|
|
6
|
+
require 'cgi'
|
|
6
7
|
require 'iri'
|
|
7
8
|
require 'json'
|
|
8
9
|
require 'loog'
|
|
@@ -32,7 +33,7 @@ class Sibit::Blockchain
|
|
|
32
33
|
h = Sibit::Json.new(http: @http, log: @log).get(
|
|
33
34
|
Iri.new('https://blockchain.info/ticker')
|
|
34
35
|
)[currency]
|
|
35
|
-
raise
|
|
36
|
+
raise(Sibit::Error, "Unrecognized currency #{currency}") if h.nil?
|
|
36
37
|
price = h['15m']
|
|
37
38
|
@log.debug("The price of BTC is #{price} USD")
|
|
38
39
|
price
|
|
@@ -40,25 +41,15 @@ class Sibit::Blockchain
|
|
|
40
41
|
|
|
41
42
|
# Get hash of the block after this one.
|
|
42
43
|
def next_of(_hash)
|
|
43
|
-
raise
|
|
44
|
-
# json = Sibit::Json.new(http: @http, log: @log).get(
|
|
45
|
-
# Iri.new('https://blockchain.info/rawblock').append(hash)
|
|
46
|
-
# )
|
|
47
|
-
# nxt = json['next_block'][0]
|
|
48
|
-
# if nxt.nil?
|
|
49
|
-
# @log.debug("There is no block after #{hash}")
|
|
50
|
-
# else
|
|
51
|
-
# @log.debug("The next block of #{hash} is #{nxt}")
|
|
52
|
-
# end
|
|
53
|
-
# nxt
|
|
44
|
+
raise(Sibit::NotSupportedError, 'next_of() in Blockchain API is broken, always returns NULL')
|
|
54
45
|
end
|
|
55
46
|
|
|
56
47
|
# The height of the block.
|
|
57
48
|
def height(hash)
|
|
58
|
-
|
|
49
|
+
h = Sibit::Json.new(http: @http, log: @log).get(
|
|
59
50
|
Iri.new('https://blockchain.info/rawblock').append(hash)
|
|
60
|
-
)
|
|
61
|
-
|
|
51
|
+
)['height']
|
|
52
|
+
raise(Sibit::Error, "The block #{hash} found but the height is absent") if h.nil?
|
|
62
53
|
@log.debug("The height of #{hash} is #{h}")
|
|
63
54
|
h
|
|
64
55
|
end
|
|
@@ -66,10 +57,10 @@ class Sibit::Blockchain
|
|
|
66
57
|
# Gets the balance of the address, in satoshi.
|
|
67
58
|
def balance(address)
|
|
68
59
|
json = Sibit::Json.new(http: @http, log: @log).get(
|
|
69
|
-
Iri.new('https://blockchain.info/rawaddr').append(address).add(limit: 0)
|
|
70
|
-
accept: [200, 500]
|
|
60
|
+
Iri.new('https://blockchain.info/rawaddr').append(address).add(limit: 0)
|
|
71
61
|
)
|
|
72
62
|
b = json['final_balance']
|
|
63
|
+
raise(Sibit::Error, "The balance of #{address} is absent") unless b.is_a?(Integer)
|
|
73
64
|
@log.debug("The balance of #{address} is #{b} satoshi (#{json['n_tx']} txns)")
|
|
74
65
|
b
|
|
75
66
|
end
|
|
@@ -86,7 +77,7 @@ class Sibit::Blockchain
|
|
|
86
77
|
].join
|
|
87
78
|
)
|
|
88
79
|
{
|
|
89
|
-
S: json['regular'] / 3,
|
|
80
|
+
S: (json['regular'] / 3.0).ceil,
|
|
90
81
|
M: json['regular'],
|
|
91
82
|
L: json['priority'],
|
|
92
83
|
XL: json['limits']['max']
|
|
@@ -112,8 +103,7 @@ class Sibit::Blockchain
|
|
|
112
103
|
# Push this transaction (in hex format) to the network.
|
|
113
104
|
def push(hex)
|
|
114
105
|
Sibit::Json.new(http: @http, log: @log).post(
|
|
115
|
-
Iri.new('https://blockchain.info/pushtx'),
|
|
116
|
-
hex
|
|
106
|
+
Iri.new('https://blockchain.info/pushtx'), "tx=#{CGI.escape(hex)}"
|
|
117
107
|
)
|
|
118
108
|
end
|
|
119
109
|
|
data/lib/sibit/blockchair.rb
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
# SPDX-FileCopyrightText: Copyright (c) 2019-2026 Yegor Bugayenko
|
|
4
4
|
# SPDX-License-Identifier: MIT
|
|
5
5
|
|
|
6
|
-
require 'cgi'
|
|
7
6
|
require 'iri'
|
|
8
7
|
require 'json'
|
|
9
8
|
require 'loog'
|
|
@@ -28,67 +27,58 @@ class Sibit::Blockchair
|
|
|
28
27
|
|
|
29
28
|
# Current price of BTC in USD (float returned).
|
|
30
29
|
def price(_currency = 'USD')
|
|
31
|
-
raise
|
|
30
|
+
raise(Sibit::NotSupportedError, 'Blockchair doesn\'t provide BTC price')
|
|
32
31
|
end
|
|
33
32
|
|
|
34
33
|
# The height of the block.
|
|
35
34
|
def height(_hash)
|
|
36
|
-
raise
|
|
35
|
+
raise(Sibit::NotSupportedError, 'Blockchair API doesn\'t provide height()')
|
|
37
36
|
end
|
|
38
37
|
|
|
39
38
|
# Get hash of the block after this one.
|
|
40
39
|
def next_of(_hash)
|
|
41
|
-
|
|
42
|
-
raise Sibit::NotSupportedError, 'Blockchair API doesn\'t provide next_of()'
|
|
40
|
+
raise(Sibit::NotSupportedError, 'Blockchair API doesn\'t provide next_of()')
|
|
43
41
|
end
|
|
44
42
|
|
|
45
43
|
# Gets the balance of the address, in satoshi.
|
|
46
44
|
def balance(address)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
)['data'][address]
|
|
45
|
+
uri = Iri.new('https://api.blockchair.com/bitcoin/dashboards/address').append(address)
|
|
46
|
+
uri = uri.add(key: @key) unless @key.nil?
|
|
47
|
+
json = Sibit::Json.new(http: @http, log: @log).get(uri)['data'][address]
|
|
50
48
|
if json.nil?
|
|
51
49
|
@log.debug("Address #{address} not found")
|
|
52
50
|
return 0
|
|
53
51
|
end
|
|
54
|
-
|
|
55
|
-
b = a['balance']
|
|
52
|
+
b = json['address']['balance']
|
|
56
53
|
@log.debug("The balance of #{address} is #{b} satoshi")
|
|
57
54
|
b
|
|
58
55
|
end
|
|
59
56
|
|
|
60
57
|
# Get recommended fees, in satoshi per byte.
|
|
61
58
|
def fees
|
|
62
|
-
raise
|
|
59
|
+
raise(Sibit::NotSupportedError, 'Blockchair doesn\'t implement fees()')
|
|
63
60
|
end
|
|
64
61
|
|
|
65
62
|
# Gets the hash of the latest block.
|
|
66
63
|
def latest
|
|
67
|
-
raise
|
|
64
|
+
raise(Sibit::NotSupportedError, 'Blockchair doesn\'t implement latest()')
|
|
68
65
|
end
|
|
69
66
|
|
|
70
67
|
# Fetch all unspent outputs per address.
|
|
71
68
|
def utxos(_sources)
|
|
72
|
-
raise
|
|
69
|
+
raise(Sibit::NotSupportedError, 'Blockchair doesn\'t implement utxos()')
|
|
73
70
|
end
|
|
74
71
|
|
|
75
72
|
# Push this transaction (in hex format) to the network.
|
|
76
73
|
def push(hex)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
)
|
|
74
|
+
uri = Iri.new('https://api.blockchair.com/bitcoin/push/transaction')
|
|
75
|
+
uri = uri.add(key: @key) unless @key.nil?
|
|
76
|
+
Sibit::Json.new(http: @http, log: @log).post(uri, "data=#{hex}")
|
|
81
77
|
@log.debug("Transaction (#{hex.length} in hex) has been pushed to Blockchair")
|
|
82
78
|
end
|
|
83
79
|
|
|
84
80
|
# This method should fetch a Blockchain block and return as a hash.
|
|
85
81
|
def block(_hash)
|
|
86
|
-
raise
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
private
|
|
90
|
-
|
|
91
|
-
def the_key
|
|
92
|
-
@key.nil? ? '' : "key=#{CGI.escape(@key)}"
|
|
82
|
+
raise(Sibit::NotSupportedError, 'Blockchair doesn\'t implement block()')
|
|
93
83
|
end
|
|
94
84
|
end
|