sibit 0.30.7 → 0.31.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3c1f18b76d75fe20bb741ac3c033bde8df69a6f54bb31e0b26f03e900d04f635
4
- data.tar.gz: ae570fa1bf67e683ac1c3a6f7754c225d6a6eea3e2376d2d2d96fc18a39504bf
3
+ metadata.gz: d7d8d002bcff6bcb41fc9dcd1a4a4ebd2d622460a23777e36da39406e4de8684
4
+ data.tar.gz: 91d217414aec0258cd458dbbae530738e2efc211f3d259888413a0532554d203
5
5
  SHA512:
6
- metadata.gz: 60685974e155e059ccc11607b731b32e27df7cec9b51db25701a174c3dee9ac1a22890e922d9b61727bd886b480f01819848b35869b18f2fc96e9fd045d2074e
7
- data.tar.gz: 773589a2fd128215dd2d16ddcd3ad0a89c983367dbd5c224afd90f4f1894cd37d07ee255406ce8372f0ac05327fe7d3efc44e8bf5db1c696c41781d4ea1a53df
6
+ metadata.gz: '099247f1d77cdb32d500e5798f7480b03ce48279d198c059e410f35ca9bb1fc345ee5b6f8342aa8b4ac9982079c9ec0848a26ae8201bb35af04f9bed67f9d2bc'
7
+ data.tar.gz: 16c9a7c880dc2bb401383bb5e0593fdfcafcdc7ebd77cfe19a7bc0509be14f8e7946b0a663bcd3e71826e60d93296533104a1d094bd25fb0955a1bdc181f6442
data/Gemfile.lock CHANGED
@@ -11,7 +11,7 @@ PATH
11
11
  loog (~> 0.6)
12
12
  openssl (~> 3.0)
13
13
  retriable_proxy (~> 1.0)
14
- slop (~> 4.6)
14
+ thor (~> 1.3)
15
15
 
16
16
  GEM
17
17
  remote: https://rubygems.org/
@@ -63,7 +63,7 @@ GEM
63
63
  decoor (0.1.0)
64
64
  diff-lcs (1.6.2)
65
65
  docile (1.4.1)
66
- donce (0.3.0)
66
+ donce (0.4.0)
67
67
  backtrace (~> 0.3)
68
68
  os (~> 1.1)
69
69
  qbash (~> 0.3)
@@ -75,7 +75,7 @@ GEM
75
75
  ffi (1.17.3-arm64-darwin)
76
76
  ffi (1.17.3-x86_64-linux-gnu)
77
77
  hashdiff (1.2.1)
78
- iri (0.11.4)
78
+ iri (0.11.6)
79
79
  json (2.18.0)
80
80
  language_server-protocol (3.17.0.5)
81
81
  lint_roller (1.1.0)
@@ -106,8 +106,8 @@ GEM
106
106
  psych (5.3.1)
107
107
  date
108
108
  stringio
109
- public_suffix (7.0.0)
110
- qbash (0.4.8)
109
+ public_suffix (7.0.2)
110
+ qbash (0.5.0)
111
111
  backtrace (> 0)
112
112
  elapsed (> 0)
113
113
  loog (> 0)
@@ -163,13 +163,12 @@ GEM
163
163
  simplecov (~> 0.19)
164
164
  simplecov-html (0.13.2)
165
165
  simplecov_json_formatter (0.1.4)
166
- slop (4.10.1)
167
166
  stringio (3.2.0)
168
167
  sys-uname (1.4.1)
169
168
  ffi (~> 1.1)
170
169
  memoist3 (~> 1.0.0)
171
170
  tago (0.6.0)
172
- thor (1.4.0)
171
+ thor (1.5.0)
173
172
  tsort (0.2.0)
174
173
  unicode-display_width (3.2.0)
175
174
  unicode-emoji (~> 4.1)
data/Rakefile CHANGED
@@ -33,7 +33,7 @@ end
33
33
  desc 'Run them via Ruby, one by one'
34
34
  task :picks do
35
35
  next if OS.windows?
36
- %w[test lib].each do |d|
36
+ %w[lib].each do |d|
37
37
  Dir["#{d}/**/*.rb"].each do |f|
38
38
  qbash("bundle exec ruby #{Shellwords.escape(f)}", log: $stdout, env: { 'PICKS' => 'yes' })
39
39
  end
data/bin/sibit CHANGED
@@ -10,7 +10,7 @@ require 'backtrace'
10
10
  require 'ellipsized'
11
11
  require 'loog'
12
12
  require 'retriable_proxy'
13
- require 'slop'
13
+ require 'thor'
14
14
  require_relative '../lib/sibit'
15
15
  require_relative '../lib/sibit/http'
16
16
  require_relative '../lib/sibit/httpproxy'
@@ -24,79 +24,35 @@ require_relative '../lib/sibit/fake'
24
24
  require_relative '../lib/sibit/firstof'
25
25
  require_relative '../lib/sibit/version'
26
26
 
27
- log = Loog::REGULAR
28
-
29
- opts =
30
- begin
31
- Slop.parse(ARGV, strict: true, help: true) do |o|
32
- o.banner = "Usage (#{Sibit::VERSION}): sibit [options] command [args]
33
- Commands are:
34
- price: Get current price of BTC in USD
35
- fees: Get currently recommended transaction fees
36
- latest: Get hash of the latest block
37
- generate: Generate a new private key
38
- create: Create a public Bitcoin address from the key
39
- balance: Check the balance of the Bitcoin address
40
- pay: Send a new Bitcoin transaction
41
- Options are:"
42
- o.string '--proxy', 'HTTPS proxy for all requests, e.g. "localhost:3128"'
43
- o.integer(
44
- '--attempts',
45
- 'How many times should we try before failing',
46
- default: 1
47
- )
48
- o.bool '--dry', 'Don\'t send a real payment, run in a read-only mode'
49
- o.bool '--help', 'Read this: https://github.com/yegor256/sibit' do
50
- log.info(o)
51
- exit
52
- end
53
- o.bool '--verbose', 'Print all possible debug messages'
54
- o.array(
55
- '--api',
56
- 'Ordered List of APIs to use, e.g. "eblockchain,btc,bitcoinchain"',
57
- default: %w[blockchain btc bitcoinchain blockchair cex]
58
- )
59
- o.array(
60
- '--skip-utxo',
61
- 'List of UTXTO that must be skipped while paying',
62
- default: []
63
- )
64
- end
65
- rescue Slop::Error => e
66
- raise e.message
27
+ # Command-line interface for Sibit.
28
+ # Provides commands to interact with the Bitcoin network.
29
+ class Bin < Thor
30
+ class_option :proxy, type: :string, desc: 'HTTPS proxy for all requests, e.g. "localhost:3128"'
31
+ class_option :attempts, type: :numeric, default: 1,
32
+ desc: 'How many times should we try before failing'
33
+ class_option :dry, type: :boolean, default: false,
34
+ desc: "Don't send a real payment, run in a read-only mode"
35
+ class_option :verbose, type: :boolean, default: false, desc: 'Print all possible debug messages'
36
+ class_option :api, type: :array, default: %w[blockchain btc bitcoinchain blockchair cex],
37
+ desc: 'Ordered List of APIs to use, e.g. "blockchain,btc,bitcoinchain"'
38
+
39
+ def self.exit_on_failure?
40
+ true
67
41
  end
68
42
 
69
- raise 'Try --help' if opts.arguments.empty?
43
+ def self.handle_argument_error(command, error, args, _arity)
44
+ raise error unless args.include?('--help') || args.include?('-h')
45
+ new.help(command.name)
46
+ end
70
47
 
71
- begin
72
- log = Loog::VERBOSE if opts[:verbose]
73
- http = opts[:proxy] ? Sibit::HttpProxy.new(opts[:proxy]) : Sibit::Http.new
74
- apis = opts[:api].map(&:downcase).map do |a|
75
- case a
76
- when 'blockchain'
77
- Sibit::Blockchain.new(http: http, log: log)
78
- when 'btc'
79
- Sibit::Btc.new(http: http, log: log)
80
- when 'bitcoinchain'
81
- Sibit::Bitcoinchain.new(http: http, log: log)
82
- when 'blockchair'
83
- Sibit::Blockchair.new(http: http, log: log)
84
- when 'cex'
85
- Sibit::Cex.new(http: http, log: log)
86
- when 'fake'
87
- Sibit::Fake.new
88
- else
89
- raise Sibit::Error, "Unknown API \"#{a}\""
90
- end
48
+ desc 'price', 'Get current price of BTC in USD'
49
+ def price
50
+ log.info(client.price)
91
51
  end
92
- api = Sibit::FirstOf.new(apis, log: log, verbose: true)
93
- api = Sibit::Dry.new(api, log: log) if opts[:dry]
94
- api = RetriableProxy.for_object(api, on: Sibit::Error) if opts[:attempts] > 1
95
- sibit = Sibit.new(log: log, api: api)
96
- case opts.arguments[0]
97
- when 'price'
98
- log.info(sibit.price)
99
- when 'fees'
52
+
53
+ desc 'fees', 'Get currently recommended transaction fees'
54
+ def fees
55
+ sibit = client
100
56
  fees = sibit.fees
101
57
  text = %i[S M L XL].map do |m|
102
58
  sat = fees[m] * 250
@@ -104,45 +60,91 @@ begin
104
60
  "#{m}: #{sat}sat / $#{format('%<usd>.02f', usd: usd)}"
105
61
  end.join("\n")
106
62
  log.info(text)
107
- when 'latest'
108
- log.info(sibit.latest)
109
- when 'generate'
110
- log.info(sibit.generate)
111
- when 'create'
112
- pvt = opts.arguments[1]
113
- raise 'Private key argument is required' if pvt.nil?
114
- log.debug("Private key provided: #{pvt.ellipsized(8).inspect}")
115
- log.info(sibit.create(pvt))
116
- when 'balance'
117
- address = opts.arguments[1]
118
- raise 'Address argument is required' if address.nil?
119
- log.info(sibit.balance(address))
120
- when 'pay'
121
- amount = opts.arguments[1]
122
- raise 'Amount argument is required' if amount.nil?
63
+ end
64
+
65
+ desc 'latest', 'Get hash of the latest block'
66
+ def latest
67
+ log.info(client.latest)
68
+ end
69
+
70
+ desc 'generate', 'Generate a new private key'
71
+ def generate
72
+ log.info(client.generate)
73
+ end
74
+
75
+ desc 'create KEY', 'Create a public Bitcoin address from the private key'
76
+ def create(key)
77
+ log.debug("Private key provided: #{key.ellipsized(8).inspect}")
78
+ log.info(client.create(key))
79
+ end
80
+
81
+ desc 'balance ADDRESS', 'Check the balance of the Bitcoin address'
82
+ def balance(address)
83
+ log.info(client.balance(address))
84
+ end
85
+
86
+ desc 'pay AMOUNT FEE SOURCES TARGET CHANGE', 'Send a new Bitcoin transaction'
87
+ option :skip_utxo, type: :array, default: [],
88
+ desc: 'List of UTXO that must be skipped while paying'
89
+ option :yes, type: :boolean, default: false,
90
+ desc: 'Skip confirmation prompt and send the payment immediately'
91
+ def pay(amount, fee, sources, target, change)
123
92
  amount = amount.to_i if /^[0-9]+$/.match?(amount)
124
- fee = opts.arguments[2]
125
- raise 'Miners fee argument is required' if fee.nil?
126
93
  fee = fee.to_i if /^[0-9]+$/.match?(fee)
127
- sources = opts.arguments[3]
128
- raise 'Addresses argument is required' if sources.nil?
129
- target = opts.arguments[4]
130
- raise 'Target argument is required' if target.nil?
131
- change = opts.arguments[5]
132
- raise 'Change argument is required' if change.nil?
133
- log.info(
134
- sibit.pay(
135
- amount, fee,
136
- sources.split(','),
137
- target, change,
138
- skip_utxo: opts['skip-utxo']
139
- )
140
- )
141
- else
142
- raise "Command #{opts.arguments[0]} is not supported"
94
+ args = [amount, fee, sources.split(','), 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 ||= options[:verbose] ? Loog::VERBOSE : Loog::REGULAR
143
114
  end
115
+
116
+ def client(dry: false)
117
+ http = options[:proxy] ? Sibit::HttpProxy.new(options[:proxy]) : Sibit::Http.new
118
+ apis = options[:api].flat_map { |a| a.split(',') }.map(&:downcase).map do |a|
119
+ case a
120
+ when 'blockchain'
121
+ Sibit::Blockchain.new(http: http, log: log)
122
+ when 'btc'
123
+ Sibit::Btc.new(http: http, log: log)
124
+ when 'bitcoinchain'
125
+ Sibit::Bitcoinchain.new(http: http, log: log)
126
+ when 'blockchair'
127
+ Sibit::Blockchair.new(http: http, log: log)
128
+ when 'cex'
129
+ Sibit::Cex.new(http: http, log: log)
130
+ when 'fake'
131
+ Sibit::Fake.new
132
+ else
133
+ raise Sibit::Error, "Unknown API \"#{a}\""
134
+ end
135
+ end
136
+ api = Sibit::FirstOf.new(apis, log: log, verbose: true)
137
+ api = Sibit::Dry.new(api, log: log) if options[:dry] || dry
138
+ api = RetriableProxy.for_object(api, on: Sibit::Error) if options[:attempts] > 1
139
+ Sibit.new(log: log, api: api)
140
+ end
141
+ end
142
+
143
+ begin
144
+ Bin.start(ARGV)
144
145
  rescue StandardError => e
145
- if opts[:verbose]
146
+ log = ARGV.include?('--verbose') ? Loog::VERBOSE : Loog::REGULAR
147
+ if ARGV.include?('--verbose')
146
148
  log.error(Backtrace.new(e))
147
149
  else
148
150
  log.error(e.message)
data/features/cli.feature CHANGED
@@ -6,7 +6,7 @@ Feature: Command Line Processing
6
6
  Scenario: Help can be printed
7
7
  When I run bin/sibit with "--help"
8
8
  Then Exit code is zero
9
- And Stdout contains "--help"
9
+ And Stdout contains "Commands:"
10
10
 
11
11
  Scenario: Bitcoin price can be retrieved
12
12
  When I run bin/sibit with "price --attempts=4"
@@ -31,3 +31,8 @@ Feature: Command Line Processing
31
31
  Scenario: Bitcoin fees can be printed
32
32
  When I run bin/sibit with "fees --verbose --api=fake"
33
33
  Then Exit code is zero
34
+
35
+ Scenario: Pay help shows all options
36
+ When I run bin/sibit with "pay --help"
37
+ Then Exit code is zero
38
+ And Stdout contains "--skip-utxo"
@@ -5,7 +5,6 @@
5
5
 
6
6
  require 'nokogiri'
7
7
  require 'tmpdir'
8
- require 'slop'
9
8
  require 'English'
10
9
  require_relative '../../lib/sibit'
11
10
 
@@ -14,10 +13,6 @@ Before do
14
13
  @dir = Dir.mktmpdir('test')
15
14
  FileUtils.mkdir_p(@dir)
16
15
  Dir.chdir(@dir)
17
- @opts = Slop.parse ['-v', '-s', @dir] do |o|
18
- o.bool '-v', '--verbose'
19
- o.string '-s', '--source'
20
- end
21
16
  end
22
17
 
23
18
  After do
data/lib/sibit/version.rb CHANGED
@@ -9,5 +9,5 @@
9
9
  # License:: MIT
10
10
  class Sibit
11
11
  # Current version of the library.
12
- VERSION = '0.30.7' unless defined?(VERSION)
12
+ VERSION = '0.31.0' unless defined?(VERSION)
13
13
  end
data/sibit.gemspec CHANGED
@@ -38,5 +38,5 @@ Gem::Specification.new do |s|
38
38
  s.add_dependency 'loog', '~> 0.6'
39
39
  s.add_dependency 'openssl', '~> 3.0'
40
40
  s.add_dependency 'retriable_proxy', '~> 1.0'
41
- s.add_dependency 'slop', '~> 4.6'
41
+ s.add_dependency 'thor', '~> 1.3'
42
42
  end
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.30.7
4
+ version: 0.31.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko
@@ -136,19 +136,19 @@ dependencies:
136
136
  - !ruby/object:Gem::Version
137
137
  version: '1.0'
138
138
  - !ruby/object:Gem::Dependency
139
- name: slop
139
+ name: thor
140
140
  requirement: !ruby/object:Gem::Requirement
141
141
  requirements:
142
142
  - - "~>"
143
143
  - !ruby/object:Gem::Version
144
- version: '4.6'
144
+ version: '1.3'
145
145
  type: :runtime
146
146
  prerelease: false
147
147
  version_requirements: !ruby/object:Gem::Requirement
148
148
  requirements:
149
149
  - - "~>"
150
150
  - !ruby/object:Gem::Version
151
- version: '4.6'
151
+ version: '1.3'
152
152
  description: This is a simple Bitcoin client, to use from command line or from your
153
153
  Ruby app. You don't need to run any Bitcoin software, no need to install anything,
154
154
  etc. All you need is just a command line and Ruby 2.5+.