sibit 0.32.1 → 0.32.2
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/bin/sibit +13 -139
- data/lib/sibit/bin.rb +148 -0
- data/lib/sibit/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5b5729226952c9b63bbe126e8f4471de9a69b5fe5cdd1735500901a39d957c43
|
|
4
|
+
data.tar.gz: 4c9fac6187bbb379fd4874e81abb26e23073878a73f6877d92566d89d666b231
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ad897361c466fef868dc4d526dbf5fc154d0d39039be448c34dceaef22e0622a08f8e2f2f6e98f9f8192a8e4f3955e92629c9b0bf6fabc71c5e748a4255a2c88
|
|
7
|
+
data.tar.gz: 3f9138f939d908f8c65f07fb10a70e9c02c900b8cc0a6edf827187ea59b97394a1b055e3baf4a539cb798e3662ec9bf4b680550159c3272c1758e04118be0443
|
data/bin/sibit
CHANGED
|
@@ -7,11 +7,9 @@
|
|
|
7
7
|
$stdout.sync = true
|
|
8
8
|
|
|
9
9
|
require 'backtrace'
|
|
10
|
-
require 'ellipsized'
|
|
11
10
|
require 'loog'
|
|
12
|
-
require 'retriable_proxy'
|
|
13
|
-
require 'thor'
|
|
14
11
|
require_relative '../lib/sibit'
|
|
12
|
+
require_relative '../lib/sibit/bin'
|
|
15
13
|
require_relative '../lib/sibit/http'
|
|
16
14
|
require_relative '../lib/sibit/httpproxy'
|
|
17
15
|
require_relative '../lib/sibit/bitcoinchain'
|
|
@@ -24,140 +22,16 @@ require_relative '../lib/sibit/fake'
|
|
|
24
22
|
require_relative '../lib/sibit/firstof'
|
|
25
23
|
require_relative '../lib/sibit/version'
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
class_option :verbose, type: :boolean, default: false, desc: 'Print all possible debug messages'
|
|
40
|
-
class_option :quiet, type: :boolean, default: false, desc: 'Print only informative messages'
|
|
41
|
-
class_option :api, type: :array, default: %w[blockchain btc bitcoinchain blockchair cex],
|
|
42
|
-
desc: 'Ordered List of APIs to use, e.g. "blockchain,btc,bitcoinchain"'
|
|
43
|
-
|
|
44
|
-
def self.exit_on_failure?
|
|
45
|
-
true
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def self.handle_argument_error(command, error, args, _arity)
|
|
49
|
-
raise error unless args.include?('--help') || args.include?('-h')
|
|
50
|
-
new.help(command.name)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
desc 'price', 'Get current price of BTC in USD'
|
|
54
|
-
def price
|
|
55
|
-
LOG.info(client.price)
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
desc 'fees', 'Get currently recommended transaction fees'
|
|
59
|
-
def fees
|
|
60
|
-
sibit = client
|
|
61
|
-
fees = sibit.fees
|
|
62
|
-
text = %i[S M L XL].map do |m|
|
|
63
|
-
sat = fees[m] * 250
|
|
64
|
-
usd = sat * sibit.price / 100_000_000
|
|
65
|
-
"#{m}: #{sat}sat / $#{format('%<usd>.02f', usd: usd)}"
|
|
66
|
-
end.join("\n")
|
|
67
|
-
LOG.info(text)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
desc 'latest', 'Get hash of the latest block'
|
|
71
|
-
def latest
|
|
72
|
-
LOG.info(client.latest)
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
desc 'generate', 'Generate a new private key'
|
|
76
|
-
def generate
|
|
77
|
-
LOG.info(client.generate)
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
desc 'create KEY', 'Create a public Bitcoin address from the private key'
|
|
81
|
-
def create(key)
|
|
82
|
-
LOG.debug("Private key provided: #{key.ellipsized(8).inspect}")
|
|
83
|
-
LOG.info(client.create(key))
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
desc 'balance ADDRESS', 'Check the balance of the Bitcoin address'
|
|
87
|
-
def balance(address)
|
|
88
|
-
LOG.info(client.balance(address))
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
desc 'pay AMOUNT FEE SOURCES TARGET CHANGE',
|
|
92
|
-
'Send a new Bitcoin transaction (AMOUNT can be "MAX" to use full balance)'
|
|
93
|
-
option :skip_utxo, type: :array, default: [],
|
|
94
|
-
desc: 'List of UTXO that must be skipped while paying'
|
|
95
|
-
option :yes, type: :boolean, default: false,
|
|
96
|
-
desc: 'Skip confirmation prompt and send the payment immediately'
|
|
97
|
-
def pay(amount, fee, sources, target, change)
|
|
98
|
-
keys = sources.split(',')
|
|
99
|
-
if amount.upcase == 'MAX'
|
|
100
|
-
addrs = keys.map { |k| Sibit::Key.new(k).bech32 }
|
|
101
|
-
amount = addrs.sum { |a| client.balance(a) }
|
|
102
|
-
end
|
|
103
|
-
amount = amount.to_i if amount.is_a?(String) && /^[0-9]+$/.match?(amount)
|
|
104
|
-
fee = fee.to_i if /^[0-9]+$/.match?(fee)
|
|
105
|
-
args = [amount, fee, keys, target, change]
|
|
106
|
-
kwargs = { skip_utxo: options[:skip_utxo] }
|
|
107
|
-
unless options[:yes] || options[:dry]
|
|
108
|
-
client(dry: true).pay(*args, **kwargs)
|
|
109
|
-
print 'Do you confirm this payment? (yes/no): '
|
|
110
|
-
answer = $stdin.gets&.strip&.downcase
|
|
111
|
-
raise Sibit::Error, 'Payment cancelled by user' unless answer == 'yes'
|
|
112
|
-
end
|
|
113
|
-
LOG.info(client.pay(*args, **kwargs))
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
desc 'version', 'Print program version'
|
|
117
|
-
def version
|
|
118
|
-
LOG.info(Sibit::VERSION)
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
private
|
|
122
|
-
|
|
123
|
-
def client(dry: false)
|
|
124
|
-
proxy = options[:proxy] || ENV.fetch('SIBIT_PROXY', nil)
|
|
125
|
-
http = proxy ? Sibit::HttpProxy.new(proxy) : Sibit::Http.new
|
|
126
|
-
LOG.debug("Using proxy at #{http.host}") if proxy
|
|
127
|
-
apis = options[:api].flat_map { |a| a.split(',') }.map(&:downcase).map do |a|
|
|
128
|
-
case a
|
|
129
|
-
when 'blockchain'
|
|
130
|
-
Sibit::Blockchain.new(http: http, log: LOG)
|
|
131
|
-
when 'btc'
|
|
132
|
-
Sibit::Btc.new(http: http, log: LOG)
|
|
133
|
-
when 'bitcoinchain'
|
|
134
|
-
Sibit::Bitcoinchain.new(http: http, log: LOG)
|
|
135
|
-
when 'blockchair'
|
|
136
|
-
Sibit::Blockchair.new(http: http, log: LOG)
|
|
137
|
-
when 'cex'
|
|
138
|
-
Sibit::Cex.new(http: http, log: LOG)
|
|
139
|
-
when 'fake'
|
|
140
|
-
Sibit::Fake.new
|
|
141
|
-
else
|
|
142
|
-
raise Sibit::Error, "Unknown API \"#{a}\""
|
|
143
|
-
end
|
|
144
|
-
end
|
|
145
|
-
api = Sibit::FirstOf.new(apis, log: LOG, verbose: true)
|
|
146
|
-
api = Sibit::Dry.new(api, log: LOG) if options[:dry] || dry
|
|
147
|
-
api = RetriableProxy.for_object(api, on: Sibit::Error) if options[:attempts] > 1
|
|
148
|
-
Sibit.new(log: LOG, api: api)
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
if __FILE__ == $PROGRAM_NAME
|
|
153
|
-
begin
|
|
154
|
-
Bin.start(ARGV)
|
|
155
|
-
rescue StandardError => e
|
|
156
|
-
if VERBOSE
|
|
157
|
-
LOG.error(Backtrace.new(e))
|
|
158
|
-
else
|
|
159
|
-
LOG.error(e.message)
|
|
160
|
-
end
|
|
161
|
-
exit(255)
|
|
162
|
-
end
|
|
25
|
+
begin
|
|
26
|
+
Sibit::Bin.start(ARGV)
|
|
27
|
+
rescue StandardError => e
|
|
28
|
+
verbose = !ARGV.include?('--quiet') &&
|
|
29
|
+
(ARGV.include?('--verbose') || ENV.fetch('SIBIT_VERBOSE', nil))
|
|
30
|
+
log = verbose ? Loog::VERBOSE : Loog::REGULAR
|
|
31
|
+
if verbose
|
|
32
|
+
log.error(Backtrace.new(e))
|
|
33
|
+
else
|
|
34
|
+
log.error(e.message)
|
|
35
|
+
end
|
|
36
|
+
exit(255)
|
|
163
37
|
end
|
data/lib/sibit/bin.rb
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: Copyright (c) 2019-2026 Yegor Bugayenko
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
|
|
6
|
+
require 'ellipsized'
|
|
7
|
+
require 'retriable_proxy'
|
|
8
|
+
require 'thor'
|
|
9
|
+
|
|
10
|
+
# Sibit main class.
|
|
11
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
|
12
|
+
# Copyright:: Copyright (c) 2019-2026 Yegor Bugayenko
|
|
13
|
+
# License:: MIT
|
|
14
|
+
class Sibit
|
|
15
|
+
# Command-line interface for Sibit.
|
|
16
|
+
#
|
|
17
|
+
# Provides commands to interact with the Bitcoin network.
|
|
18
|
+
#
|
|
19
|
+
# Example:
|
|
20
|
+
# Sibit::Bin.start(['price'])
|
|
21
|
+
# Sibit::Bin.start(['balance', '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa'])
|
|
22
|
+
class Bin < Thor
|
|
23
|
+
class_option :proxy, type: :string, desc: 'HTTPS proxy for all requests, e.g. "localhost:3128"'
|
|
24
|
+
class_option :attempts, type: :numeric, default: 1,
|
|
25
|
+
desc: 'How many times should we try before failing'
|
|
26
|
+
class_option :dry, type: :boolean, default: false,
|
|
27
|
+
desc: "Don't send a real payment, run in a read-only mode"
|
|
28
|
+
class_option :verbose, type: :boolean, default: false, desc: 'Print all possible debug messages'
|
|
29
|
+
class_option :quiet, type: :boolean, default: false, desc: 'Print only informative messages'
|
|
30
|
+
class_option :api, type: :array, default: %w[blockchain btc bitcoinchain blockchair cex],
|
|
31
|
+
desc: 'Ordered List of APIs to use, e.g. "blockchain,btc,bitcoinchain"'
|
|
32
|
+
|
|
33
|
+
def self.exit_on_failure?
|
|
34
|
+
true
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.handle_argument_error(command, error, args, _arity)
|
|
38
|
+
raise error unless args.include?('--help') || args.include?('-h')
|
|
39
|
+
new.help(command.name)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
desc 'price', 'Get current price of BTC in USD'
|
|
43
|
+
def price
|
|
44
|
+
log.info(client.price)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
desc 'fees', 'Get currently recommended transaction fees'
|
|
48
|
+
def fees
|
|
49
|
+
sibit = client
|
|
50
|
+
fees = sibit.fees
|
|
51
|
+
text = %i[S M L XL].map do |m|
|
|
52
|
+
sat = fees[m] * 250
|
|
53
|
+
usd = sat * sibit.price / 100_000_000
|
|
54
|
+
"#{m}: #{sat}sat / $#{format('%<usd>.02f', usd: usd)}"
|
|
55
|
+
end.join("\n")
|
|
56
|
+
log.info(text)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
desc 'latest', 'Get hash of the latest block'
|
|
60
|
+
def latest
|
|
61
|
+
log.info(client.latest)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
desc 'generate', 'Generate a new private key'
|
|
65
|
+
def generate
|
|
66
|
+
log.info(client.generate)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
desc 'create KEY', 'Create a public Bitcoin address from the private key'
|
|
70
|
+
def create(key)
|
|
71
|
+
log.debug("Private key provided: #{key.ellipsized(8).inspect}")
|
|
72
|
+
log.info(client.create(key))
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
desc 'balance ADDRESS', 'Check the balance of the Bitcoin address'
|
|
76
|
+
def balance(address)
|
|
77
|
+
log.info(client.balance(address))
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
desc 'pay AMOUNT FEE SOURCES TARGET CHANGE',
|
|
81
|
+
'Send a new Bitcoin transaction (AMOUNT can be "MAX" to use full balance)'
|
|
82
|
+
option :skip_utxo, type: :array, default: [],
|
|
83
|
+
desc: 'List of UTXO that must be skipped while paying'
|
|
84
|
+
option :yes, type: :boolean, default: false,
|
|
85
|
+
desc: 'Skip confirmation prompt and send the payment immediately'
|
|
86
|
+
def pay(amount, fee, sources, target, change)
|
|
87
|
+
keys = sources.split(',')
|
|
88
|
+
if amount.upcase == 'MAX'
|
|
89
|
+
addrs = keys.map { |k| Sibit::Key.new(k).bech32 }
|
|
90
|
+
amount = addrs.sum { |a| client.balance(a) }
|
|
91
|
+
end
|
|
92
|
+
amount = amount.to_i if amount.is_a?(String) && /^[0-9]+$/.match?(amount)
|
|
93
|
+
fee = fee.to_i if /^[0-9]+$/.match?(fee)
|
|
94
|
+
args = [amount, fee, keys, target, change]
|
|
95
|
+
kwargs = { skip_utxo: options[:skip_utxo] }
|
|
96
|
+
unless options[:yes] || options[:dry]
|
|
97
|
+
client(dry: true).pay(*args, **kwargs)
|
|
98
|
+
print 'Do you confirm this payment? (yes/no): '
|
|
99
|
+
answer = $stdin.gets&.strip&.downcase
|
|
100
|
+
raise Sibit::Error, 'Payment cancelled by user' unless answer == 'yes'
|
|
101
|
+
end
|
|
102
|
+
log.info(client.pay(*args, **kwargs))
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
desc 'version', 'Print program version'
|
|
106
|
+
def version
|
|
107
|
+
log.info(Sibit::VERSION)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
private
|
|
111
|
+
|
|
112
|
+
def log
|
|
113
|
+
@log ||= begin
|
|
114
|
+
verbose = !ARGV.include?('--quiet') &&
|
|
115
|
+
(ARGV.include?('--verbose') || ENV.fetch('SIBIT_VERBOSE', nil))
|
|
116
|
+
verbose ? Loog::VERBOSE : Loog::REGULAR
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def client(dry: false)
|
|
121
|
+
proxy = options[:proxy] || ENV.fetch('SIBIT_PROXY', nil)
|
|
122
|
+
http = proxy ? Sibit::HttpProxy.new(proxy) : Sibit::Http.new
|
|
123
|
+
log.debug("Using proxy at #{http.host}") if proxy
|
|
124
|
+
apis = options[:api].flat_map { |a| a.split(',') }.map(&:downcase).map do |a|
|
|
125
|
+
case a
|
|
126
|
+
when 'blockchain'
|
|
127
|
+
Sibit::Blockchain.new(http: http, log: log)
|
|
128
|
+
when 'btc'
|
|
129
|
+
Sibit::Btc.new(http: http, log: log)
|
|
130
|
+
when 'bitcoinchain'
|
|
131
|
+
Sibit::Bitcoinchain.new(http: http, log: log)
|
|
132
|
+
when 'blockchair'
|
|
133
|
+
Sibit::Blockchair.new(http: http, log: log)
|
|
134
|
+
when 'cex'
|
|
135
|
+
Sibit::Cex.new(http: http, log: log)
|
|
136
|
+
when 'fake'
|
|
137
|
+
Sibit::Fake.new
|
|
138
|
+
else
|
|
139
|
+
raise Sibit::Error, "Unknown API \"#{a}\""
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
api = Sibit::FirstOf.new(apis, log: log, verbose: true)
|
|
143
|
+
api = Sibit::Dry.new(api, log: log) if options[:dry] || dry
|
|
144
|
+
api = RetriableProxy.for_object(api, on: Sibit::Error) if options[:attempts] > 1
|
|
145
|
+
Sibit.new(log: log, api: api)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
data/lib/sibit/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sibit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.32.
|
|
4
|
+
version: 0.32.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yegor Bugayenko
|
|
@@ -178,6 +178,7 @@ files:
|
|
|
178
178
|
- lib/sibit/base58.rb
|
|
179
179
|
- lib/sibit/bech32.rb
|
|
180
180
|
- lib/sibit/bestof.rb
|
|
181
|
+
- lib/sibit/bin.rb
|
|
181
182
|
- lib/sibit/bitcoinchain.rb
|
|
182
183
|
- lib/sibit/blockchain.rb
|
|
183
184
|
- lib/sibit/blockchair.rb
|