sibit 0.28.0 → 0.29.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.
data/lib/sibit/json.rb CHANGED
@@ -3,13 +3,13 @@
3
3
  # SPDX-FileCopyrightText: Copyright (c) 2019-2025 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
+ require 'cgi'
6
7
  require 'json'
8
+ require 'loog'
7
9
  require 'uri'
8
- require 'cgi'
9
- require_relative 'version'
10
10
  require_relative 'error'
11
- require 'loog'
12
11
  require_relative 'http'
12
+ require_relative 'version'
13
13
 
14
14
  # Json SDK.
15
15
  #
@@ -19,77 +19,74 @@ require_relative 'http'
19
19
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
20
20
  # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
21
21
  # License:: MIT
22
- class Sibit
23
- # JSON processing.
24
- class Json
25
- # Constructor.
26
- def initialize(log: Loog::NULL, http: Sibit::Http.new)
27
- @http = http
28
- @log = log
29
- end
22
+ class Sibit::Json
23
+ # Constructor.
24
+ def initialize(log: Loog::NULL, http: Sibit::Http.new)
25
+ @http = http
26
+ @log = log
27
+ end
30
28
 
31
- # Send GET request to the HTTP and return JSON response.
32
- # This method will also log the process and will validate the
33
- # response for correctness.
34
- def get(address, headers: {}, accept: [200])
35
- start = Time.now
36
- uri = URI(address.to_s)
37
- res = @http.client(uri).get(
38
- "#{uri.path.empty? ? '/' : uri.path}#{"?#{uri.query}" if uri.query}",
39
- {
40
- 'Accept' => 'application/json',
41
- 'User-Agent' => user_agent,
42
- 'Accept-Charset' => 'UTF-8',
43
- 'Accept-Encoding' => ''
44
- }.merge(headers)
45
- )
46
- unless accept.include?(res.code.to_i)
47
- raise Sibit::Error, "Failed to retrieve #{uri} (#{res.code}): #{res.body}"
48
- end
49
- @log.info("GET #{uri}: #{res.code}/#{length(res.body.length)} in #{age(start)}")
50
- JSON.parse(res.body)
51
- rescue JSON::ParserError => e
52
- raise Sibit::Error, "Can't parse JSON: #{e.message}"
29
+ # Send GET request to the HTTP and return JSON response.
30
+ # This method will also log the process and will validate the
31
+ # response for correctness.
32
+ def get(address, headers: {}, accept: [200])
33
+ start = Time.now
34
+ uri = URI(address.to_s)
35
+ res = @http.client(uri).get(
36
+ "#{uri.path.empty? ? '/' : uri.path}#{"?#{uri.query}" if uri.query}",
37
+ {
38
+ 'Accept' => 'application/json',
39
+ 'User-Agent' => user_agent,
40
+ 'Accept-Charset' => 'UTF-8',
41
+ 'Accept-Encoding' => ''
42
+ }.merge(headers)
43
+ )
44
+ unless accept.include?(res.code.to_i)
45
+ raise Sibit::Error, "Failed to retrieve #{uri} (#{res.code}): #{res.body}"
53
46
  end
47
+ @log.info("GET #{uri}: #{res.code}/#{length(res.body.length)} in #{age(start)}")
48
+ JSON.parse(res.body)
49
+ rescue JSON::ParserError => e
50
+ raise Sibit::Error, "Can't parse JSON: #{e.message}"
51
+ end
54
52
 
55
- def post(address, body, headers: {})
56
- start = Time.now
57
- uri = URI(address.to_s)
58
- res = @http.client(uri).post(
59
- "#{uri.path}?#{uri.query}",
60
- "tx=#{CGI.escape(body)}",
61
- {
62
- 'Accept' => 'text/plain',
63
- 'User-Agent' => user_agent,
64
- 'Accept-Charset' => 'UTF-8',
65
- 'Accept-Encoding' => '',
66
- 'Content-Type' => 'application/x-www-form-urlencoded'
67
- }.merge(headers)
68
- )
69
- unless res.code == '200'
70
- raise Sibit::Error, "Failed to post tx to #{uri}: #{res.code}\n#{res.body}"
71
- end
72
- @log.info("POST #{uri}: #{res.code} in #{age(start)}")
53
+ def post(address, body, headers: {})
54
+ start = Time.now
55
+ uri = URI(address.to_s)
56
+ res = @http.client(uri).post(
57
+ "#{uri.path}?#{uri.query}",
58
+ "tx=#{CGI.escape(body)}",
59
+ {
60
+ 'Accept' => 'text/plain',
61
+ 'User-Agent' => user_agent,
62
+ 'Accept-Charset' => 'UTF-8',
63
+ 'Accept-Encoding' => '',
64
+ 'Content-Type' => 'application/x-www-form-urlencoded'
65
+ }.merge(headers)
66
+ )
67
+ unless res.code == '200'
68
+ raise Sibit::Error, "Failed to post tx to #{uri}: #{res.code}\n#{res.body}"
73
69
  end
70
+ @log.info("POST #{uri}: #{res.code} in #{age(start)}")
71
+ end
74
72
 
75
- private
73
+ private
76
74
 
77
- def age(start)
78
- "#{((Time.now - start) * 1000).round}ms"
79
- end
75
+ def age(start)
76
+ "#{((Time.now - start) * 1000).round}ms"
77
+ end
80
78
 
81
- def length(bytes)
82
- if bytes > 1024 * 1024
83
- "#{bytes / (1024 * 1024)}mb"
84
- elsif bytes > 1024
85
- "#{bytes / 1024}kb"
86
- else
87
- "#{bytes}b"
88
- end
79
+ def length(bytes)
80
+ if bytes > 1024 * 1024
81
+ "#{bytes / (1024 * 1024)}mb"
82
+ elsif bytes > 1024
83
+ "#{bytes / 1024}kb"
84
+ else
85
+ "#{bytes}b"
89
86
  end
87
+ end
90
88
 
91
- def user_agent
92
- "Anonymous/#{Sibit::VERSION}"
93
- end
89
+ def user_agent
90
+ "Anonymous/#{Sibit::VERSION}"
94
91
  end
95
92
  end
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.28.0'
12
+ VERSION = '0.29.1'
13
13
  end
data/lib/sibit.rb CHANGED
@@ -4,13 +4,14 @@
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
6
  require 'loog'
7
- require_relative 'sibit/version'
8
- require_relative 'sibit/blockchain'
9
7
  require_relative 'sibit/bitcoin/base58'
10
8
  require_relative 'sibit/bitcoin/key'
11
9
  require_relative 'sibit/bitcoin/script'
12
10
  require_relative 'sibit/bitcoin/tx'
13
11
  require_relative 'sibit/bitcoin/txbuilder'
12
+ require_relative 'sibit/blockchain'
13
+ require_relative 'sibit/version'
14
+
14
15
  # Sibit main class.
15
16
  #
16
17
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
@@ -47,14 +48,14 @@ class Sibit
47
48
 
48
49
  # Generates new Bitcoin private key and returns in Hash160 format.
49
50
  def generate
50
- key = Bitcoin::Key.generate.priv
51
+ key = Key.generate.priv
51
52
  @log.info("Bitcoin private key generated: #{key[0..8]}...")
52
53
  key
53
54
  end
54
55
 
55
56
  # Creates Bitcoin address using the private key in Hash160 format.
56
57
  def create(pvt)
57
- Bitcoin::Key.new(pvt).addr
58
+ Key.new(pvt).addr
58
59
  end
59
60
 
60
61
  # Gets the balance of the address, in satoshi.
@@ -99,9 +100,9 @@ class Sibit
99
100
  # +change+: the address where the change has to be sent to
100
101
  def pay(amount, fee, sources, target, change, skip_utxo: [])
101
102
  p = price('USD')
102
- sources = sources.map { |k| [Bitcoin::Key.new(k).addr, k] }.to_h
103
+ sources = sources.map { |k| [Key.new(k).addr, k] }.to_h
103
104
  satoshi = satoshi(amount)
104
- builder = Bitcoin::TxBuilder.new
105
+ builder = TxBuilder.new
105
106
  unspent = 0
106
107
  size = 100
107
108
  utxos = @api.utxos(sources.keys)
@@ -117,8 +118,10 @@ class Sibit
117
118
  i.prev_out(utxo[:hash])
118
119
  i.prev_out_index(utxo[:index])
119
120
  i.prev_out_script = script_hex(utxo[:script])
120
- address = Bitcoin::Script.new(script_hex(utxo[:script])).address
121
- i.signature_key(key(sources[address]))
121
+ address = Script.new(script_hex(utxo[:script])).address
122
+ k = sources[address]
123
+ raise Error, "UTXO arrived to #{address} is incorrect" unless k
124
+ i.signature_key(key(k))
122
125
  end
123
126
  size += 180
124
127
  @log.info(
@@ -137,7 +140,7 @@ class Sibit
137
140
  tx = builder.tx(
138
141
  input_value: unspent,
139
142
  leave_fee: true,
140
- extra_fee: [f, Bitcoin::MIN_TX_FEE].max,
143
+ extra_fee: [f, MIN_TX_FEE].max,
141
144
  change_address: change
142
145
  )
143
146
  left = unspent - tx.outputs.sum(&:value)
@@ -146,7 +149,7 @@ class Sibit
146
149
  #{tx.inputs.map { |i| " in: #{i.prev_out.unpack1('H*')}:#{i.prev_out_index}" }.join("\n ")}
147
150
  #{tx.out.count} output#{'s' if tx.out.count > 1}:
148
151
  #{tx.outputs.map { |o| "out: #{o.script_hex} / #{num(o.value, p)}" }.join("\n ")}
149
- Min tx fee: #{num(Bitcoin::MIN_TX_FEE, p)}
152
+ Min tx fee: #{num(MIN_TX_FEE, p)}
150
153
  Fee requested: #{num(f, p)} as \"#{fee}\"
151
154
  Fee actually paid: #{num(left, p)}
152
155
  Tx size: #{size} bytes
@@ -178,17 +181,14 @@ class Sibit
178
181
  raise Error, "The max number must be above zero: #{max}" if max < 1
179
182
  block = start
180
183
  count = 0
181
- wrong = []
182
184
  json = {}
183
185
  loop do
184
186
  json = @api.block(block)
185
187
  if json[:orphan]
186
188
  steps = 4
187
189
  @log.info("Orphan block found at #{block}, moving #{steps} steps back...")
188
- wrong << block
189
190
  steps.times do
190
191
  block = json[:previous]
191
- wrong << block
192
192
  @log.info("Moved back to #{block}")
193
193
  json = @api.block(block)
194
194
  end
@@ -225,7 +225,7 @@ in block #{block} (by #{json[:provider]})")
225
225
  @log.info("The block #{json[:hash]} is definitely the end of Blockchain, we stop.")
226
226
  break
227
227
  end
228
- if count > max
228
+ if count >= max
229
229
  @log.info("Too many blocks (#{count}) in one go, let's get back to it next time")
230
230
  break
231
231
  end
@@ -263,9 +263,9 @@ in block #{block} (by #{json[:provider]})")
263
263
  return fee.to_i if fee.is_a?(Integer)
264
264
  raise Error, 'Fee should either be a String or Integer' unless fee.is_a?(String)
265
265
  mul = 1
266
- if fee.start_with?('+', '-')
267
- mul = -1 if fee.start_with?('-')
268
- fee = fee[1..-1]
266
+ if fee.end_with?('+', '-')
267
+ mul = -1 if fee.end_with?('-')
268
+ fee = fee[0..-2]
269
269
  end
270
270
  sat = fees[fee.to_sym]
271
271
  raise Error, "Can't understand the fee: #{fee.inspect}" if sat.nil?
@@ -274,7 +274,7 @@ in block #{block} (by #{json[:provider]})")
274
274
 
275
275
  # Make key from private key string in Hash160.
276
276
  def key(hash160)
277
- Bitcoin::Key.new(hash160)
277
+ Key.new(hash160)
278
278
  end
279
279
 
280
280
  # Convert script to hex string if needed.
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.28.0
4
+ version: 0.29.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko
@@ -128,6 +128,7 @@ files:
128
128
  - bin/sibit
129
129
  - cucumber.yml
130
130
  - features/cli.feature
131
+ - features/dry.feature
131
132
  - features/gem_package.feature
132
133
  - features/step_definitions/steps.rb
133
134
  - features/support/env.rb