sibit 0.30.6 → 0.30.7

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: 3c1f18b76d75fe20bb741ac3c033bde8df69a6f54bb31e0b26f03e900d04f635
4
+ data.tar.gz: ae570fa1bf67e683ac1c3a6f7754c225d6a6eea3e2376d2d2d96fc18a39504bf
5
5
  SHA512:
6
- metadata.gz: 3711ea50589117bc03497b111c11f7509b7be90dd8656aa7938ae3a72e730a8f13b74a1706589ca224b883dfa2223875f9e5aa204865e58d7856dcfd28a6fd17
7
- data.tar.gz: 8ed48713e2795903a90501fed8da5a48dda43d62c9e8a152e4200003cf4ca51903fc53d745068de5502faadedf69d76d99b3024742b7d0a164924df62d3796f4
6
+ metadata.gz: 60685974e155e059ccc11607b731b32e27df7cec9b51db25701a174c3dee9ac1a22890e922d9b61727bd886b480f01819848b35869b18f2fc96e9fd045d2074e
7
+ data.tar.gz: 773589a2fd128215dd2d16ddcd3ad0a89c983367dbd5c224afd90f4f1894cd37d07ee255406ce8372f0ac05327fe7d3efc44e8bf5db1c696c41781d4ea1a53df
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,6 +5,7 @@ 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)
@@ -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.3.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.4)
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)
@@ -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)
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'
data/bin/sibit CHANGED
@@ -1,12 +1,13 @@
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
13
  require 'slop'
@@ -110,6 +111,7 @@ begin
110
111
  when 'create'
111
112
  pvt = opts.arguments[1]
112
113
  raise 'Private key argument is required' if pvt.nil?
114
+ log.debug("Private key provided: #{pvt.ellipsized(8).inspect}")
113
115
  log.info(sibit.create(pvt))
114
116
  when 'balance'
115
117
  address = opts.arguments[1]
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
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,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 'nokogiri'
@@ -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.
data/lib/sibit/cex.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'
@@ -14,7 +14,7 @@ require_relative 'json'
14
14
  # Cex.io API.
15
15
  #
16
16
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
17
- # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
17
+ # Copyright:: Copyright (c) 2019-2026 Yegor Bugayenko
18
18
  # License:: MIT
19
19
  class Sibit::Cex
20
20
  # 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
  # Cryptoapis.io 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::Cryptoapis
21
21
  # Constructor.
data/lib/sibit/dry.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 'decoor'
@@ -12,7 +12,7 @@ require 'loog'
12
12
  # All other methods are delegated to the wrapped API unchanged.
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 Sibit::Dry
18
18
  def initialize(api, log: Loog::NULL)
data/lib/sibit/error.rb CHANGED
@@ -1,12 +1,12 @@
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
  # The error.
7
7
  #
8
8
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
9
- # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
9
+ # Copyright:: Copyright (c) 2019-2026 Yegor Bugayenko
10
10
  # License:: MIT
11
11
  class Sibit
12
12
  # The error.
data/lib/sibit/fake.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_relative 'version'
@@ -8,7 +8,7 @@ require_relative 'version'
8
8
  # Fake API.
9
9
  #
10
10
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
11
- # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
11
+ # Copyright:: Copyright (c) 2019-2026 Yegor Bugayenko
12
12
  # License:: MIT
13
13
  class Sibit::Fake
14
14
  def price(_cur = 'USD')
data/lib/sibit/firstof.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 first 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::FirstOf
16
16
  # Constructor.
data/lib/sibit/http.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 'net/http'
@@ -10,7 +10,7 @@ class Sibit
10
10
  # HTTP interface.
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 Http
16
16
  def client(uri)
@@ -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 'net/http'
@@ -12,7 +12,7 @@ class Sibit
12
12
  # Accepts proxy address in format: host:port or user:password@host:port
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 HttpProxy
18
18
  def initialize(addr)
data/lib/sibit/json.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 'cgi'
@@ -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::Json
24
24
  # Constructor.
data/lib/sibit/key.rb CHANGED
@@ -1,11 +1,12 @@
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'
7
7
  require 'openssl'
8
8
  require_relative 'base58'
9
+ require_relative 'bech32'
9
10
 
10
11
  # Sibit main class.
11
12
  class Sibit
@@ -15,7 +16,7 @@ class Sibit
15
16
  # of using deprecated mutable key APIs.
16
17
  #
17
18
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
18
- # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
19
+ # Copyright:: Copyright (c) 2019-2026 Yegor Bugayenko
19
20
  # License:: MIT
20
21
  class Key
21
22
  MIN_PRIV = 0x01
@@ -23,13 +24,14 @@ class Sibit
23
24
 
24
25
  attr_reader :network
25
26
 
26
- def self.generate
27
+ def self.generate(network: :mainnet)
27
28
  key = OpenSSL::PKey::EC.generate('secp256k1')
28
29
  pvt = key.private_key.to_s(16).rjust(64, '0').downcase
29
- new(pvt)
30
+ new(pvt, network: network)
30
31
  end
31
32
 
32
- def initialize(privkey)
33
+ def initialize(privkey, network: nil)
34
+ @override = network
33
35
  @network = :mainnet
34
36
  @compressed = true
35
37
  @privkey = decode(privkey)
@@ -45,7 +47,12 @@ class Sibit
45
47
  point.to_octet_string(@compressed ? :compressed : :uncompressed).unpack1('H*')
46
48
  end
47
49
 
48
- def addr
50
+ def bech32
51
+ hrp = { mainnet: 'bc', testnet: 'tb', regtest: 'bcrt' }[@network]
52
+ Bech32.encode(hrp, 0, hash160(pub))
53
+ end
54
+
55
+ def base58
49
56
  hash = hash160(pub)
50
57
  prefix = @network == :mainnet ? '00' : '6f'
51
58
  versioned = "#{prefix}#{hash}"
@@ -88,10 +95,14 @@ class Sibit
88
95
  end
89
96
 
90
97
  def decode(key)
91
- return key if key.length == 64 && key.match?(/\A[0-9a-f]+\z/i)
98
+ if key.length == 64 && key.match?(/\A[0-9a-f]+\z/i)
99
+ @network = @override || :mainnet
100
+ return key
101
+ end
92
102
  raw = Base58.new(key).decode
93
103
  version = raw[0, 2]
94
- @network = version == '80' ? :mainnet : :testnet
104
+ detected = version == '80' ? :mainnet : :testnet
105
+ @network = @override || detected
95
106
  body = raw[2..-9]
96
107
  @compressed = body.length == 66 && body.end_with?('01')
97
108
  @compressed ? body[0, 64] : body
data/lib/sibit/script.rb CHANGED
@@ -1,21 +1,23 @@
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'
7
7
  require_relative 'base58'
8
+ require_relative 'bech32'
8
9
 
9
10
  # Sibit main class.
10
11
  class Sibit
11
12
  # Bitcoin Script parser.
12
13
  #
13
- # Parses standard P2PKH scripts to extract addresses.
14
+ # Parses standard P2PKH and P2WPKH scripts to extract addresses.
14
15
  #
15
16
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
16
- # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
17
+ # Copyright:: Copyright (c) 2019-2026 Yegor Bugayenko
17
18
  # License:: MIT
18
19
  class Script
20
+ OP_0 = 0x00
19
21
  OP_DUP = 0x76
20
22
  OP_HASH160 = 0xa9
21
23
  OP_EQUALVERIFY = 0x88
@@ -26,6 +28,7 @@ class Sibit
26
28
  end
27
29
 
28
30
  def address(network = :mainnet)
31
+ return p2wpkh_address(network) if p2wpkh?
29
32
  return p2pkh_address(network) if p2pkh?
30
33
  nil
31
34
  end
@@ -39,9 +42,16 @@ class Sibit
39
42
  @bytes[24] == OP_CHECKSIG
40
43
  end
41
44
 
45
+ def p2wpkh?
46
+ @bytes.length == 22 &&
47
+ @bytes[0] == OP_0 &&
48
+ @bytes[1] == 20
49
+ end
50
+
42
51
  def hash160
43
- return nil unless p2pkh?
44
- @bytes[3, 20].pack('C*').unpack1('H*')
52
+ return @bytes[2, 20].pack('C*').unpack1('H*') if p2wpkh?
53
+ return @bytes[3, 20].pack('C*').unpack1('H*') if p2pkh?
54
+ nil
45
55
  end
46
56
 
47
57
  private
@@ -54,5 +64,12 @@ class Sibit
54
64
  checksum = Base58.new(versioned).check
55
65
  Base58.new(versioned + checksum).encode
56
66
  end
67
+
68
+ def p2wpkh_address(network)
69
+ h = hash160
70
+ return nil unless h
71
+ hrp = { mainnet: 'bc', testnet: 'tb', regtest: 'bcrt' }[network]
72
+ Bech32.encode(hrp, 0, h)
73
+ end
57
74
  end
58
75
  end
data/lib/sibit/tx.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'
@@ -14,7 +14,7 @@ class Sibit
14
14
  # Bitcoin Transaction structure.
15
15
  #
16
16
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
17
- # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
17
+ # Copyright:: Copyright (c) 2019-2026 Yegor Bugayenko
18
18
  # License:: MIT
19
19
  class Tx
20
20
  SIGHASH_ALL = 0x01
@@ -28,8 +28,8 @@ class Sibit
28
28
  @outputs = []
29
29
  end
30
30
 
31
- def add_input(hash:, index:, script:, key:)
32
- @inputs << Input.new(hash, index, script, key)
31
+ def add_input(hash:, index:, script:, key:, value: 0)
32
+ @inputs << Input.new(hash, index, script, key, value)
33
33
  end
34
34
 
35
35
  def add_output(value, address)
@@ -60,18 +60,20 @@ class Sibit
60
60
  # Transaction input.
61
61
  #
62
62
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
63
- # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
63
+ # Copyright:: Copyright (c) 2019-2026 Yegor Bugayenko
64
64
  # License:: MIT
65
65
  class Input
66
- attr_reader :hash, :index, :prev_script, :key
67
- attr_accessor :script_sig
66
+ attr_reader :hash, :index, :prev_script, :key, :value
67
+ attr_accessor :script_sig, :witness
68
68
 
69
- def initialize(hash, index, script, key)
69
+ def initialize(hash, index, script, key, value)
70
70
  @hash = hash
71
71
  @index = index
72
72
  @prev_script = script
73
73
  @key = key
74
+ @value = value
74
75
  @script_sig = ''
76
+ @witness = []
75
77
  end
76
78
 
77
79
  def prev_out
@@ -81,12 +83,17 @@ class Sibit
81
83
  def prev_out_index
82
84
  @index
83
85
  end
86
+
87
+ def segwit?
88
+ bytes = [@prev_script].pack('H*').bytes
89
+ bytes.length == 22 && bytes[0].zero? && bytes[1] == 20
90
+ end
84
91
  end
85
92
 
86
93
  # Transaction output.
87
94
  #
88
95
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
89
- # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
96
+ # Copyright:: Copyright (c) 2019-2026 Yegor Bugayenko
90
97
  # License:: MIT
91
98
  class Output
92
99
  attr_reader :value
@@ -97,10 +104,14 @@ class Sibit
97
104
  end
98
105
 
99
106
  def script
100
- return segwit_script if @address.downcase.start_with?('bc1')
107
+ return segwit_script if segwit?
101
108
  p2pkh_script
102
109
  end
103
110
 
111
+ def segwit?
112
+ @address.downcase.start_with?('bc1', 'tb1', 'bcrt1')
113
+ end
114
+
104
115
  def script_hex
105
116
  script.unpack1('H*')
106
117
  end
@@ -123,21 +134,67 @@ class Sibit
123
134
 
124
135
  private
125
136
 
137
+ def witness?
138
+ @inputs.any?(&:segwit?)
139
+ end
140
+
126
141
  def sign_inputs
127
142
  @inputs.each_with_index do |input, idx|
128
- sighash = signature_hash(idx)
143
+ sighash = input.segwit? ? segwit_sighash(idx) : legacy_sighash(idx)
129
144
  sig = sign(input.key, sighash)
130
145
  pubkey = [input.key.pub].pack('H*')
131
- input.script_sig = der_sig(sig) + pubkey_script(pubkey)
146
+ if input.segwit?
147
+ witness_sig = sig + [SIGHASH_ALL].pack('C')
148
+ input.witness = [witness_sig.bytes, pubkey.bytes]
149
+ else
150
+ input.script_sig = der_sig(sig) + pubkey_script(pubkey)
151
+ end
132
152
  end
133
153
  end
134
154
 
135
- def signature_hash(idx)
155
+ def legacy_sighash(idx)
136
156
  tx_copy = serialize_for_signing(idx)
137
157
  hash_type = [SIGHASH_ALL].pack('V')
138
158
  Digest::SHA256.digest(Digest::SHA256.digest(tx_copy + hash_type))
139
159
  end
140
160
 
161
+ def segwit_sighash(idx)
162
+ input = @inputs[idx]
163
+ preimage = [VERSION].pack('V')
164
+ preimage += hash_prevouts
165
+ preimage += hash_sequence
166
+ preimage += [input.hash].pack('H*').reverse
167
+ preimage += [input.index].pack('V')
168
+ preimage += script_code(input)
169
+ preimage += [input.value].pack('Q<')
170
+ preimage += [SEQUENCE].pack('V')
171
+ preimage += hash_outputs
172
+ preimage += [0].pack('V')
173
+ preimage += [SIGHASH_ALL].pack('V')
174
+ Digest::SHA256.digest(Digest::SHA256.digest(preimage))
175
+ end
176
+
177
+ def hash_prevouts
178
+ data = @inputs.map { |i| [i.hash].pack('H*').reverse + [i.index].pack('V') }.join
179
+ Digest::SHA256.digest(Digest::SHA256.digest(data))
180
+ end
181
+
182
+ def hash_sequence
183
+ data = @inputs.map { [SEQUENCE].pack('V') }.join
184
+ Digest::SHA256.digest(Digest::SHA256.digest(data))
185
+ end
186
+
187
+ def hash_outputs
188
+ data = @outputs.map { |o| [o.value].pack('Q<') + varint(o.script.length) + o.script }.join
189
+ Digest::SHA256.digest(Digest::SHA256.digest(data))
190
+ end
191
+
192
+ def script_code(input)
193
+ hash160 = input.prev_script[4..]
194
+ code = [0x76, 0xa9, 0x14].pack('C*') + [hash160].pack('H*') + [0x88, 0xac].pack('C*')
195
+ varint(code.length) + code
196
+ end
197
+
141
198
  def sign(key, hash)
142
199
  der = key.sign(hash)
143
200
  repack(der)
@@ -173,6 +230,7 @@ class Sibit
173
230
 
174
231
  def serialize
175
232
  result = [VERSION].pack('V')
233
+ result += [0x00, 0x01].pack('CC') if witness?
176
234
  result += varint(@inputs.length)
177
235
  @inputs.each do |input|
178
236
  result += [input.hash].pack('H*').reverse
@@ -188,10 +246,27 @@ class Sibit
188
246
  result += varint(script.length)
189
247
  result += script
190
248
  end
249
+ result += serialize_witness if witness?
191
250
  result += [0].pack('V')
192
251
  result
193
252
  end
194
253
 
254
+ def serialize_witness
255
+ result = ''.b
256
+ @inputs.each do |input|
257
+ if input.segwit?
258
+ result += varint(input.witness.length)
259
+ input.witness.each do |item|
260
+ result += varint(item.length)
261
+ result += item.pack('C*')
262
+ end
263
+ else
264
+ result += varint(0)
265
+ end
266
+ end
267
+ result
268
+ end
269
+
195
270
  def serialize_for_signing(idx)
196
271
  result = [VERSION].pack('V')
197
272
  result += varint(@inputs.length)
@@ -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_relative 'key'
@@ -14,7 +14,7 @@ class Sibit
14
14
  # building and signing Bitcoin transactions.
15
15
  #
16
16
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
17
- # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
17
+ # Copyright:: Copyright (c) 2019-2026 Yegor Bugayenko
18
18
  # License:: MIT
19
19
  class TxBuilder
20
20
  def initialize
@@ -39,7 +39,8 @@ class Sibit
39
39
  hash: inp.prev_out_hash,
40
40
  index: inp.prev_out_idx,
41
41
  script: inp.script,
42
- key: inp.key
42
+ key: inp.key,
43
+ value: inp.amount
43
44
  )
44
45
  end
45
46
  total_out = @outputs.sum { |o| o[:value] }
@@ -54,10 +55,10 @@ class Sibit
54
55
  # Input builder for collecting input parameters.
55
56
  #
56
57
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
57
- # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
58
+ # Copyright:: Copyright (c) 2019-2026 Yegor Bugayenko
58
59
  # License:: MIT
59
60
  class Input
60
- attr_reader :prev_out_hash, :prev_out_idx, :script, :key
61
+ attr_reader :prev_out_hash, :prev_out_idx, :script, :key, :amount
61
62
 
62
63
  def prev_out(hash)
63
64
  @prev_out_hash = hash
@@ -71,6 +72,10 @@ class Sibit
71
72
  @script = scr
72
73
  end
73
74
 
75
+ def prev_out_value(val)
76
+ @amount = val
77
+ end
78
+
74
79
  def signature_key(key)
75
80
  @key = key
76
81
  end
@@ -79,7 +84,7 @@ class Sibit
79
84
  # Wrapper for built transaction with convenience methods.
80
85
  #
81
86
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
82
- # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
87
+ # Copyright:: Copyright (c) 2019-2026 Yegor Bugayenko
83
88
  # License:: MIT
84
89
  class Built
85
90
  def initialize(txn, inputs, outputs)
@@ -115,7 +120,7 @@ class Sibit
115
120
  # Wrapper for payload with hex conversion.
116
121
  #
117
122
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
118
- # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
123
+ # Copyright:: Copyright (c) 2019-2026 Yegor Bugayenko
119
124
  # License:: MIT
120
125
  class Payload
121
126
  def initialize(bytes)
data/lib/sibit/version.rb CHANGED
@@ -1,13 +1,13 @@
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
  # Sibit main class.
7
7
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
8
- # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
8
+ # Copyright:: Copyright (c) 2019-2026 Yegor Bugayenko
9
9
  # License:: MIT
10
10
  class Sibit
11
11
  # Current version of the library.
12
- VERSION = '0.30.6' unless defined?(VERSION)
12
+ VERSION = '0.30.7' unless defined?(VERSION)
13
13
  end
data/lib/sibit.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 'loog'
@@ -15,7 +15,7 @@ require_relative 'sibit/version'
15
15
  # Sibit main class.
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
21
21
  # Minimum fee we must pay for transaction processing:
@@ -58,7 +58,8 @@ class Sibit
58
58
 
59
59
  # Creates Bitcoin address using the private key in Hash160 format.
60
60
  def create(pvt)
61
- Key.new(pvt).addr
61
+ raise Error, 'Invalid private key (must be 64 chars)' unless /^[0-9a-f]{64}$/.match?(pvt)
62
+ Key.new(pvt).bech32
62
63
  end
63
64
 
64
65
  # Gets the balance of the address, in satoshi.
@@ -101,11 +102,12 @@ class Sibit
101
102
  # +sources+: the list of private bitcoin keys where the coins are now
102
103
  # +target+: the target address to send to
103
104
  # +change+: the address where the change has to be sent to
104
- def pay(amount, fee, sources, target, change, skip_utxo: [])
105
+ # +network+: optional network override (:mainnet, :testnet, :regtest)
106
+ def pay(amount, fee, sources, target, change, skip_utxo: [], network: nil)
105
107
  p = price('USD')
106
- keys = sources.map { |k| Key.new(k) }
108
+ keys = sources.map { |k| Key.new(k, network: network) }
107
109
  network = keys.first&.network || :mainnet
108
- sources = keys.to_h { |k| [k.addr, k.priv] }
110
+ sources = keys.to_h { |k| [k.bech32, k.priv] }
109
111
  satoshi = satoshi(amount)
110
112
  builder = TxBuilder.new
111
113
  unspent = 0
@@ -127,6 +129,7 @@ class Sibit
127
129
  i.prev_out(utxo[:hash])
128
130
  i.prev_out_index(utxo[:index])
129
131
  i.prev_out_script = script_hex(utxo[:script])
132
+ i.prev_out_value(utxo[:value])
130
133
  address = Script.new(script_hex(utxo[:script])).address(network)
131
134
  k = sources[address]
132
135
  raise Error, "UTXO arrived to #{address} is incorrect" unless k
data/sibit.gemspec 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 'English'
@@ -32,6 +32,7 @@ Gem::Specification.new do |s|
32
32
  s.add_dependency 'backtrace', '~> 0.3'
33
33
  s.add_dependency 'decoor', '~> 0.1'
34
34
  s.add_dependency 'elapsed', '~> 0.2'
35
+ s.add_dependency 'ellipsized', '~> 0.3'
35
36
  s.add_dependency 'iri', '~> 0.5'
36
37
  s.add_dependency 'json', '~> 2.18'
37
38
  s.add_dependency 'loog', '~> 0.6'
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.6
4
+ version: 0.30.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko
@@ -51,6 +51,20 @@ dependencies:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
53
  version: '0.2'
54
+ - !ruby/object:Gem::Dependency
55
+ name: ellipsized
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.3'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.3'
54
68
  - !ruby/object:Gem::Dependency
55
69
  name: iri
56
70
  requirement: !ruby/object:Gem::Requirement