sibit 0.30.6 → 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: aa32d663437f956373254dfd5ee62d072a984a7848ef455d26d22727293199ef
4
- data.tar.gz: 6a0464cb926cce844ec89e380352c9d0e94c97ffbd26a9609ecbcefd82ee9413
3
+ metadata.gz: d7d8d002bcff6bcb41fc9dcd1a4a4ebd2d622460a23777e36da39406e4de8684
4
+ data.tar.gz: 91d217414aec0258cd458dbbae530738e2efc211f3d259888413a0532554d203
5
5
  SHA512:
6
- metadata.gz: 3711ea50589117bc03497b111c11f7509b7be90dd8656aa7938ae3a72e730a8f13b74a1706589ca224b883dfa2223875f9e5aa204865e58d7856dcfd28a6fd17
7
- data.tar.gz: 8ed48713e2795903a90501fed8da5a48dda43d62c9e8a152e4200003cf4ca51903fc53d745068de5502faadedf69d76d99b3024742b7d0a164924df62d3796f4
6
+ metadata.gz: '099247f1d77cdb32d500e5798f7480b03ce48279d198c059e410f35ca9bb1fc345ee5b6f8342aa8b4ac9982079c9ec0848a26ae8201bb35af04f9bed67f9d2bc'
7
+ data.tar.gz: 16c9a7c880dc2bb401383bb5e0593fdfcafcdc7ebd77cfe19a7bc0509be14f8e7946b0a663bcd3e71826e60d93296533104a1d094bd25fb0955a1bdc181f6442
data/Gemfile CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # SPDX-FileCopyrightText: Copyright (c) 2019-2025 Yegor Bugayenko
3
+ # SPDX-FileCopyrightText: Copyright (c) 2019-2026 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
6
  source 'https://rubygems.org'
data/Gemfile.lock CHANGED
@@ -5,12 +5,13 @@ PATH
5
5
  backtrace (~> 0.3)
6
6
  decoor (~> 0.1)
7
7
  elapsed (~> 0.2)
8
+ ellipsized (~> 0.3)
8
9
  iri (~> 0.5)
9
10
  json (~> 2.18)
10
11
  loog (~> 0.6)
11
12
  openssl (~> 3.0)
12
13
  retriable_proxy (~> 1.0)
13
- slop (~> 4.6)
14
+ thor (~> 1.3)
14
15
 
15
16
  GEM
16
17
  remote: https://rubygems.org/
@@ -62,18 +63,19 @@ GEM
62
63
  decoor (0.1.0)
63
64
  diff-lcs (1.6.2)
64
65
  docile (1.4.1)
65
- donce (0.2.4)
66
+ donce (0.4.0)
66
67
  backtrace (~> 0.3)
67
68
  os (~> 1.1)
68
69
  qbash (~> 0.3)
69
70
  elapsed (0.2.1)
70
71
  loog (~> 0.6)
71
72
  tago (~> 0.1)
73
+ ellipsized (0.3.0)
72
74
  erb (6.0.1)
73
- ffi (1.17.2-arm64-darwin)
74
- ffi (1.17.2-x86_64-linux-gnu)
75
+ ffi (1.17.3-arm64-darwin)
76
+ ffi (1.17.3-x86_64-linux-gnu)
75
77
  hashdiff (1.2.1)
76
- iri (0.11.3)
78
+ iri (0.11.6)
77
79
  json (2.18.0)
78
80
  language_server-protocol (3.17.0.5)
79
81
  lint_roller (1.1.0)
@@ -82,7 +84,6 @@ GEM
82
84
  logger (~> 1.0)
83
85
  memoist3 (1.0.0)
84
86
  mini_mime (1.1.5)
85
- mini_portile2 (2.8.9)
86
87
  minitest (6.0.1)
87
88
  prism (~> 1.5)
88
89
  minitest-reporters (1.7.1)
@@ -91,8 +92,9 @@ GEM
91
92
  minitest (>= 5.0)
92
93
  ruby-progressbar
93
94
  multi_test (1.1.0)
94
- nokogiri (1.18.10)
95
- mini_portile2 (~> 2.8.2)
95
+ nokogiri (1.19.0-arm64-darwin)
96
+ racc (~> 1.4)
97
+ nokogiri (1.19.0-x86_64-linux-gnu)
96
98
  racc (~> 1.4)
97
99
  openssl (3.3.2)
98
100
  os (1.1.4)
@@ -104,8 +106,8 @@ GEM
104
106
  psych (5.3.1)
105
107
  date
106
108
  stringio
107
- public_suffix (7.0.0)
108
- qbash (0.4.8)
109
+ public_suffix (7.0.2)
110
+ qbash (0.5.0)
109
111
  backtrace (> 0)
110
112
  elapsed (> 0)
111
113
  loog (> 0)
@@ -137,9 +139,9 @@ GEM
137
139
  rubocop-ast (>= 1.48.0, < 2.0)
138
140
  ruby-progressbar (~> 1.7)
139
141
  unicode-display_width (>= 2.4.0, < 4.0)
140
- rubocop-ast (1.48.0)
142
+ rubocop-ast (1.49.0)
141
143
  parser (>= 3.3.7.2)
142
- prism (~> 1.4)
144
+ prism (~> 1.7)
143
145
  rubocop-minitest (0.38.2)
144
146
  lint_roller (~> 1.1)
145
147
  rubocop (>= 1.75.0, < 2.0)
@@ -161,13 +163,12 @@ GEM
161
163
  simplecov (~> 0.19)
162
164
  simplecov-html (0.13.2)
163
165
  simplecov_json_formatter (0.1.4)
164
- slop (4.10.1)
165
166
  stringio (3.2.0)
166
167
  sys-uname (1.4.1)
167
168
  ffi (~> 1.1)
168
169
  memoist3 (~> 1.0.0)
169
170
  tago (0.6.0)
170
- thor (1.4.0)
171
+ thor (1.5.0)
171
172
  tsort (0.2.0)
172
173
  unicode-display_width (3.2.0)
173
174
  unicode-emoji (~> 4.1)
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  (The MIT License)
2
2
 
3
- Copyright (c) 2019-2025 Yegor Bugayenko
3
+ Copyright (c) 2019-2026 Yegor Bugayenko
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the 'Software'), to deal
data/LICENSES/MIT.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  (The MIT License)
2
2
 
3
- Copyright (c) 2019-2025 Yegor Bugayenko
3
+ Copyright (c) 2019-2026 Yegor Bugayenko
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the 'Software'), to deal
data/Rakefile CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # SPDX-FileCopyrightText: Copyright (c) 2019-2025 Yegor Bugayenko
3
+ # SPDX-FileCopyrightText: Copyright (c) 2019-2026 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
6
  require 'os'
@@ -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
@@ -1,15 +1,16 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- # SPDX-FileCopyrightText: Copyright (c) 2019-2025 Yegor Bugayenko
4
+ # SPDX-FileCopyrightText: Copyright (c) 2019-2026 Yegor Bugayenko
5
5
  # SPDX-License-Identifier: MIT
6
6
 
7
7
  $stdout.sync = true
8
8
 
9
9
  require 'backtrace'
10
+ require 'ellipsized'
10
11
  require 'loog'
11
12
  require 'retriable_proxy'
12
- require 'slop'
13
+ require 'thor'
13
14
  require_relative '../lib/sibit'
14
15
  require_relative '../lib/sibit/http'
15
16
  require_relative '../lib/sibit/httpproxy'
@@ -23,79 +24,35 @@ require_relative '../lib/sibit/fake'
23
24
  require_relative '../lib/sibit/firstof'
24
25
  require_relative '../lib/sibit/version'
25
26
 
26
- log = Loog::REGULAR
27
-
28
- opts =
29
- begin
30
- Slop.parse(ARGV, strict: true, help: true) do |o|
31
- o.banner = "Usage (#{Sibit::VERSION}): sibit [options] command [args]
32
- Commands are:
33
- price: Get current price of BTC in USD
34
- fees: Get currently recommended transaction fees
35
- latest: Get hash of the latest block
36
- generate: Generate a new private key
37
- create: Create a public Bitcoin address from the key
38
- balance: Check the balance of the Bitcoin address
39
- pay: Send a new Bitcoin transaction
40
- Options are:"
41
- o.string '--proxy', 'HTTPS proxy for all requests, e.g. "localhost:3128"'
42
- o.integer(
43
- '--attempts',
44
- 'How many times should we try before failing',
45
- default: 1
46
- )
47
- o.bool '--dry', 'Don\'t send a real payment, run in a read-only mode'
48
- o.bool '--help', 'Read this: https://github.com/yegor256/sibit' do
49
- log.info(o)
50
- exit
51
- end
52
- o.bool '--verbose', 'Print all possible debug messages'
53
- o.array(
54
- '--api',
55
- 'Ordered List of APIs to use, e.g. "eblockchain,btc,bitcoinchain"',
56
- default: %w[blockchain btc bitcoinchain blockchair cex]
57
- )
58
- o.array(
59
- '--skip-utxo',
60
- 'List of UTXTO that must be skipped while paying',
61
- default: []
62
- )
63
- end
64
- rescue Slop::Error => e
65
- 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
66
41
  end
67
42
 
68
- 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
69
47
 
70
- begin
71
- log = Loog::VERBOSE if opts[:verbose]
72
- http = opts[:proxy] ? Sibit::HttpProxy.new(opts[:proxy]) : Sibit::Http.new
73
- apis = opts[:api].map(&:downcase).map do |a|
74
- case a
75
- when 'blockchain'
76
- Sibit::Blockchain.new(http: http, log: log)
77
- when 'btc'
78
- Sibit::Btc.new(http: http, log: log)
79
- when 'bitcoinchain'
80
- Sibit::Bitcoinchain.new(http: http, log: log)
81
- when 'blockchair'
82
- Sibit::Blockchair.new(http: http, log: log)
83
- when 'cex'
84
- Sibit::Cex.new(http: http, log: log)
85
- when 'fake'
86
- Sibit::Fake.new
87
- else
88
- raise Sibit::Error, "Unknown API \"#{a}\""
89
- end
48
+ desc 'price', 'Get current price of BTC in USD'
49
+ def price
50
+ log.info(client.price)
90
51
  end
91
- api = Sibit::FirstOf.new(apis, log: log, verbose: true)
92
- api = Sibit::Dry.new(api, log: log) if opts[:dry]
93
- api = RetriableProxy.for_object(api, on: Sibit::Error) if opts[:attempts] > 1
94
- sibit = Sibit.new(log: log, api: api)
95
- case opts.arguments[0]
96
- when 'price'
97
- log.info(sibit.price)
98
- when 'fees'
52
+
53
+ desc 'fees', 'Get currently recommended transaction fees'
54
+ def fees
55
+ sibit = client
99
56
  fees = sibit.fees
100
57
  text = %i[S M L XL].map do |m|
101
58
  sat = fees[m] * 250
@@ -103,44 +60,91 @@ begin
103
60
  "#{m}: #{sat}sat / $#{format('%<usd>.02f', usd: usd)}"
104
61
  end.join("\n")
105
62
  log.info(text)
106
- when 'latest'
107
- log.info(sibit.latest)
108
- when 'generate'
109
- log.info(sibit.generate)
110
- when 'create'
111
- pvt = opts.arguments[1]
112
- raise 'Private key argument is required' if pvt.nil?
113
- log.info(sibit.create(pvt))
114
- when 'balance'
115
- address = opts.arguments[1]
116
- raise 'Address argument is required' if address.nil?
117
- log.info(sibit.balance(address))
118
- when 'pay'
119
- amount = opts.arguments[1]
120
- 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)
121
92
  amount = amount.to_i if /^[0-9]+$/.match?(amount)
122
- fee = opts.arguments[2]
123
- raise 'Miners fee argument is required' if fee.nil?
124
93
  fee = fee.to_i if /^[0-9]+$/.match?(fee)
125
- sources = opts.arguments[3]
126
- raise 'Addresses argument is required' if sources.nil?
127
- target = opts.arguments[4]
128
- raise 'Target argument is required' if target.nil?
129
- change = opts.arguments[5]
130
- raise 'Change argument is required' if change.nil?
131
- log.info(
132
- sibit.pay(
133
- amount, fee,
134
- sources.split(','),
135
- target, change,
136
- skip_utxo: opts['skip-utxo']
137
- )
138
- )
139
- else
140
- 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
141
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)
142
145
  rescue StandardError => e
143
- if opts[:verbose]
146
+ log = ARGV.include?('--verbose') ? Loog::VERBOSE : Loog::REGULAR
147
+ if ARGV.include?('--verbose')
144
148
  log.error(Backtrace.new(e))
145
149
  else
146
150
  log.error(e.message)
data/cucumber.yml CHANGED
@@ -1,4 +1,4 @@
1
- # SPDX-FileCopyrightText: Copyright (c) 2019-2025 Yegor Bugayenko
1
+ # SPDX-FileCopyrightText: Copyright (c) 2019-2026 Yegor Bugayenko
2
2
  # SPDX-License-Identifier: MIT
3
3
  ---
4
4
  default: --format pretty
data/features/cli.feature CHANGED
@@ -1,4 +1,4 @@
1
- # SPDX-FileCopyrightText: Copyright (c) 2019-2025 Yegor Bugayenko
1
+ # SPDX-FileCopyrightText: Copyright (c) 2019-2026 Yegor Bugayenko
2
2
  # SPDX-License-Identifier: MIT
3
3
  Feature: Command Line Processing
4
4
  As a holder of BTC I want to be able to use sibit
@@ -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"
data/features/dry.feature CHANGED
@@ -1,4 +1,4 @@
1
- # SPDX-FileCopyrightText: Copyright (c) 2019-2025 Yegor Bugayenko
1
+ # SPDX-FileCopyrightText: Copyright (c) 2019-2026 Yegor Bugayenko
2
2
  # SPDX-License-Identifier: MIT
3
3
  Feature: Command Line Processing
4
4
  As a holder of BTC I want to use sibit in dry mode
@@ -1,4 +1,4 @@
1
- # SPDX-FileCopyrightText: Copyright (c) 2019-2025 Yegor Bugayenko
1
+ # SPDX-FileCopyrightText: Copyright (c) 2019-2026 Yegor Bugayenko
2
2
  # SPDX-License-Identifier: MIT
3
3
  Feature: Gem Package
4
4
  As a source code writer I want to be able to
@@ -1,11 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # SPDX-FileCopyrightText: Copyright (c) 2019-2025 Yegor Bugayenko
3
+ # SPDX-FileCopyrightText: Copyright (c) 2019-2026 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
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
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # SPDX-FileCopyrightText: Copyright (c) 2019-2025 Yegor Bugayenko
3
+ # SPDX-FileCopyrightText: Copyright (c) 2019-2026 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
6
  require 'simplecov'
data/lib/sibit/base58.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # SPDX-FileCopyrightText: Copyright (c) 2019-2025 Yegor Bugayenko
3
+ # SPDX-FileCopyrightText: Copyright (c) 2019-2026 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
6
  require 'digest'
@@ -13,7 +13,7 @@ class Sibit
13
13
  # Encapsulates hex data and provides encoding/decoding functionality.
14
14
  #
15
15
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
16
- # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
16
+ # Copyright:: Copyright (c) 2019-2026 Yegor Bugayenko
17
17
  # License:: MIT
18
18
  class Base58
19
19
  ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
data/lib/sibit/bech32.rb CHANGED
@@ -1,23 +1,67 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # SPDX-FileCopyrightText: Copyright (c) 2019-2025 Yegor Bugayenko
3
+ # SPDX-FileCopyrightText: Copyright (c) 2019-2026 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
6
  require_relative 'error'
7
7
 
8
8
  # Sibit main class.
9
9
  class Sibit
10
- # Bech32 decoding for SegWit addresses.
10
+ # Bech32 encoding and decoding for SegWit addresses.
11
11
  #
12
- # Decodes Bech32/Bech32m addresses (bc1...) to witness programs.
12
+ # Encodes witness programs to Bech32 addresses (bc1...) and decodes them back.
13
13
  #
14
14
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
15
- # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
15
+ # Copyright:: Copyright (c) 2019-2026 Yegor Bugayenko
16
16
  # License:: MIT
17
17
  class Bech32
18
18
  CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'
19
19
  GENERATOR = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3].freeze
20
20
 
21
+ def self.encode(hrp, ver, prog)
22
+ bytes = [prog].pack('H*').bytes
23
+ data = [ver] + bits(bytes, 8, 5, true)
24
+ chk = checksum(hrp, data)
25
+ "#{hrp}1#{(data + chk).map { |d| CHARSET[d] }.join}"
26
+ end
27
+
28
+ def self.checksum(hrp, data)
29
+ values = expanded(hrp) + data + [0, 0, 0, 0, 0, 0]
30
+ poly = pm(values) ^ 1
31
+ (0..5).map { |i| (poly >> (5 * (5 - i))) & 31 }
32
+ end
33
+
34
+ def self.expanded(hrp)
35
+ hrp.chars.map { |c| c.ord >> 5 } + [0] + hrp.chars.map { |c| c.ord & 31 }
36
+ end
37
+
38
+ def self.pm(values)
39
+ chk = 1
40
+ values.each do |v|
41
+ top = chk >> 25
42
+ chk = ((chk & 0x1ffffff) << 5) ^ v
43
+ 5.times { |i| chk ^= GENERATOR[i] if (top >> i).allbits?(1) }
44
+ end
45
+ chk
46
+ end
47
+
48
+ def self.bits(data, frombits, tobits, pad)
49
+ acc = 0
50
+ num = 0
51
+ result = []
52
+ maxv = (1 << tobits) - 1
53
+ data.each do |v|
54
+ acc = (acc << frombits) | v
55
+ num += frombits
56
+ while num >= tobits
57
+ num -= tobits
58
+ result << ((acc >> num) & maxv)
59
+ end
60
+ end
61
+ result << ((acc << (tobits - num)) & maxv) if pad && num.positive?
62
+ result
63
+ end
64
+
21
65
  def initialize(addr)
22
66
  @addr = addr.downcase
23
67
  end
data/lib/sibit/bestof.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # SPDX-FileCopyrightText: Copyright (c) 2019-2025 Yegor Bugayenko
3
+ # SPDX-FileCopyrightText: Copyright (c) 2019-2026 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
6
  require 'backtrace'
@@ -10,7 +10,7 @@ require_relative 'error'
10
10
  # API best of.
11
11
  #
12
12
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
13
- # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
13
+ # Copyright:: Copyright (c) 2019-2026 Yegor Bugayenko
14
14
  # License:: MIT
15
15
  class Sibit::BestOf
16
16
  # Constructor.
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # SPDX-FileCopyrightText: Copyright (c) 2019-2025 Yegor Bugayenko
3
+ # SPDX-FileCopyrightText: Copyright (c) 2019-2026 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
6
  require 'iri'
@@ -15,7 +15,7 @@ require_relative 'version'
15
15
  # Bitcoinchain.com API.
16
16
  #
17
17
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
18
- # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
18
+ # Copyright:: Copyright (c) 2019-2026 Yegor Bugayenko
19
19
  # License:: MIT
20
20
  class Sibit::Bitcoinchain
21
21
  # Constructor.
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # SPDX-FileCopyrightText: Copyright (c) 2019-2025 Yegor Bugayenko
3
+ # SPDX-FileCopyrightText: Copyright (c) 2019-2026 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
6
  require 'iri'
@@ -18,7 +18,7 @@ require_relative 'version'
18
18
  # https://www.blockchain.com/api/blockchain_api
19
19
  #
20
20
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
21
- # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
21
+ # Copyright:: Copyright (c) 2019-2026 Yegor Bugayenko
22
22
  # License:: MIT
23
23
  class Sibit::Blockchain
24
24
  # Constructor.
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # SPDX-FileCopyrightText: Copyright (c) 2019-2025 Yegor Bugayenko
3
+ # SPDX-FileCopyrightText: Copyright (c) 2019-2026 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
6
  require 'cgi'
@@ -16,7 +16,7 @@ require_relative 'version'
16
16
  # Blockchair.com API.
17
17
  #
18
18
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
19
- # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
19
+ # Copyright:: Copyright (c) 2019-2026 Yegor Bugayenko
20
20
  # License:: MIT
21
21
  class Sibit::Blockchair
22
22
  # Constructor.
data/lib/sibit/btc.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # SPDX-FileCopyrightText: Copyright (c) 2019-2025 Yegor Bugayenko
3
+ # SPDX-FileCopyrightText: Copyright (c) 2019-2026 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
6
  require 'iri'
@@ -17,7 +17,7 @@ require_relative 'version'
17
17
  # Here: https://btc.com/api-doc
18
18
  #
19
19
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
20
- # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
20
+ # Copyright:: Copyright (c) 2019-2026 Yegor Bugayenko
21
21
  # License:: MIT
22
22
  class Sibit::Btc
23
23
  # Constructor.