sibit 0.27.1 → 0.29.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: 731731c7fc499008d0a0fd713a84f8b416d55163674e6c2e45783436a283720d
4
- data.tar.gz: f4b5d3b5119381ffdf4c764a1f4e82b4eb06f3b66135070b33ebffc43e523ec3
3
+ metadata.gz: ddde20b613a744a0618b9b91d4adfea42d945a13d778ecad2492b1a15afe3c0e
4
+ data.tar.gz: 7d83be599123ff738035fbf610d1ae6f763c5c36cec2a83a626658707cf37cda
5
5
  SHA512:
6
- metadata.gz: f4b72bfda3a91eeb48eaf97b1badaba49d0cceaf4e935fe3b3ee983217b920602ad9935f1b2f70b881b08c9ed0f946b11be93abd606642d293846657f359c473
7
- data.tar.gz: 2e4e84c22a4be9f275d53c5003ccbf5feaba4813546624c590cf853938e958b7e934c60d2924c9ed4e8f8ed301923df89edf337676fb4a231bb0f9ebe2b3c9c9
6
+ metadata.gz: 9811599df9ebb242927bcf50a8ed2b1e24adad74832b72748b1f1c113c9f30e89c0b9079f3a0898308963c55c59d0f899534d529d673f0120a718176402a4e10
7
+ data.tar.gz: 2f3821bb817a4fadbe8137ccce649d66ba388ae17f0a3b99f6293947e0d3ef084d2d0f1501fabcbec8d933b7127d655a1fae74bdb907019ae39e088d4bd6bf52
data/Gemfile.lock CHANGED
@@ -72,6 +72,7 @@ GEM
72
72
  logger (~> 1.0)
73
73
  memoist3 (1.0.0)
74
74
  mini_mime (1.1.5)
75
+ mini_portile2 (2.8.9)
75
76
  minitest (6.0.1)
76
77
  prism (~> 1.5)
77
78
  minitest-reporters (1.7.1)
@@ -80,9 +81,8 @@ GEM
80
81
  minitest (>= 5.0)
81
82
  ruby-progressbar
82
83
  multi_test (1.1.0)
83
- nokogiri (1.18.10-arm64-darwin)
84
- racc (~> 1.4)
85
- nokogiri (1.18.10-x86_64-linux-gnu)
84
+ nokogiri (1.18.10)
85
+ mini_portile2 (~> 2.8.2)
86
86
  racc (~> 1.4)
87
87
  openssl (4.0.0)
88
88
  parallel (1.27.0)
data/README.md CHANGED
@@ -7,15 +7,13 @@
7
7
  [![rake](https://github.com/yegor256/sibit/actions/workflows/rake.yml/badge.svg)](https://github.com/yegor256/sibit/actions/workflows/rake.yml)
8
8
  [![PDD status](https://www.0pdd.com/svg?name=yegor256/sibit)](https://www.0pdd.com/p?name=yegor256/sibit)
9
9
  [![Gem Version](https://badge.fury.io/rb/sibit.svg)](https://badge.fury.io/rb/sibit)
10
- [![Maintainability](https://api.codeclimate.com/v1/badges/74c909f06d4afa0d8001/maintainability)](https://codeclimate.com/github/yegor256/sibit/maintainability)
11
10
  [![License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/yegor256/takes/sibit/master/LICENSE.txt)
12
11
  [![Test Coverage](https://img.shields.io/codecov/c/github/yegor256/sibit.svg)](https://codecov.io/github/yegor256/sibit?branch=master)
13
12
  [![Hits-of-Code](https://hitsofcode.com/github/yegor256/sibit)](https://hitsofcode.com/view/github/yegor256/sibit)
14
13
 
15
- To understand how the Bitcoin protocol works,
16
- I recommend you watching this [short video] and
17
- then reading this blog post of mine:
18
- [_Sibit Demonstrates How Bitcoin Works_][blog].
14
+ To understand how the Bitcoin protocol works, I recommend you watching
15
+ this [short video] and then reading this blog post of mine:
16
+ [_Sibit Demonstrates How Bitcoin Works_][blog].
19
17
 
20
18
  This is a simple Bitcoin client for use from the command line
21
19
  or from your Ruby app.
@@ -26,10 +24,10 @@ If you need something more complex, I would recommend using
26
24
  [bitcoin-ruby] for Ruby and [Electrum] as a GUI client.
27
25
 
28
26
  You may want to discuss this tool at [Bitcointalk]
29
- and give the thread a few merits.
27
+ and give the thread a few merits.
30
28
 
31
29
  This is a Ruby gem, install it first (if it doesn't work, there are
32
- some hints at the bottom of this page):
30
+ some hints at the bottom of this page):
33
31
 
34
32
  ```bash
35
33
  gem install sibit
@@ -61,38 +59,37 @@ $ sibit balance 1PfsYNygsuVL8fvBarJNQnHytkg4rGih1U
61
59
  To send a payment from a few addresses to a new address:
62
60
 
63
61
  ```bash
64
- $ sibit pay AMOUNT FEE A1:P1,A2:P2,... TARGET CHANGE
62
+ $ sibit pay AMOUNT FEE P1,P2,... TARGET CHANGE
65
63
  e87f138c9ebf5986151667719825c28458a28cc66f69fed4f1032a93b399fdf8
66
64
  ```
67
65
 
68
66
  Here,
69
- `AMOUNT` is the amount of [satoshi] you are sending,
70
- `FEE` is the [miner fee] you are ready to spend to get
71
- this transaction delivered
72
- (you can say `S`, `M`, `L`, or `XL` if you want it
73
- to be calculated automatically),
74
- `A1:P1,A2:P2,...` is a comma-separated list
75
- of addresses `A` and private keys `P` you are sending your coins from,
76
- `TARGET` is the address you are sending to,
77
- `CHANGE` is the address where the change will be sent to.
78
- The transaction hash will be returned.
79
- Not all [UTXOs] will be used, but only the necessary amount of them.
80
-
81
- By default, the fee will be paid on top of the payment amount you are sending.
82
- Say, you are sending 0.5 BTC and the fee is 0.0001 BTC. Totally, you will
83
- spend 0.5001. However, you can make Sibit deduct the fee from the payment
84
- amount. In this case you should provide a negative amount of the fee
85
- or one of `-S`, `-M`, `-L`, `-XL`. You can also say `+S`, if you want the
86
- opposite, which is the default.
87
-
88
- It is recommended to run it with `--dry --verbose` options first, to see
89
- what's going to be sent to the network. If everything looks correct, remove
90
- the `--dry` and run again, the transaction will be pushed to the network.
91
-
92
- All operations are performed through the
93
- [Blockchain API].
94
- Transactions are pushed to the Bitcoin network via
95
- [this relay].
67
+ `AMOUNT` is the amount of [satoshi] you are sending,
68
+ `FEE` is the [miner fee] you are ready to spend to get
69
+ this transaction delivered (you can say `S`, `M`, `L`, or `XL` if you want it
70
+ to be calculated automatically),
71
+ `P1,P2,...` is a comma-separated list
72
+ of private keys `P` you are sending your coins from,
73
+ `TARGET` is the address you are sending to,
74
+ `CHANGE` is the address where the change goes.
75
+ The transaction hash is returned.
76
+ Not all [UTXOs] may be used, but only the necessary amount of them.
77
+
78
+ By default, the fee is paid on top of the payment amount you are sending.
79
+ Say, you are sending 0.5 BTC and the fee is 0.0001 BTC.
80
+ Totally, you spend 0.5001.
81
+ However, you can make Sibit deduct the fee from the payment amount.
82
+ In this case you should provide a negative amount
83
+ of the fee or one of `S-`, `M-`, `L-`, `XL-`.
84
+ You can also say `S+`, if you want the opposite, which is the default.
85
+
86
+ It is recommended to run it with `--dry --verbose` options first,
87
+ to see what's going to be sent to the network.
88
+ If everything looks correct, remove the `--dry` and run again,
89
+ the transaction is pushed to the network.
90
+
91
+ All operations are performed through the [Blockchain API].
92
+ Transactions are pushed to the Bitcoin network via [this relay].
96
93
 
97
94
  ## Ruby SDK
98
95
 
@@ -105,7 +102,7 @@ pkey = sibit.generate
105
102
  address = sibit.create(pkey)
106
103
  balance = sibit.balance(address)
107
104
  target = sibit.create(pkey) # where to send coins to
108
- change = sibit.create(pkey) # where the change will be sent to
105
+ change = sibit.create(pkey) # where the change goes
109
106
  tx = sibit.pay(10_000_000, 'XL', { address => pkey }, target, change)
110
107
  ```
111
108
 
@@ -114,8 +111,8 @@ It should work.
114
111
  ## APIs
115
112
 
116
113
  The library works through one (or a few) public APIs for fetching
117
- Bitcoin data and pushing transactions to the network. At the moment we
118
- work with the following APIs:
114
+ Bitcoin data and pushing transactions to the network.
115
+ At the moment we work with the following APIs:
119
116
 
120
117
  * [Blockchain.com] - `Sibit::Blockchain`
121
118
  * [BTC.com] - `Sibit::Btc`
@@ -124,8 +121,9 @@ work with the following APIs:
124
121
  * [Blockchair.com] - `Sibit::Blockchair`
125
122
  * [Cex.io] - `Sibit::Cex`
126
123
 
127
- The first one in this list is used by default. If you want to use a different
128
- one, you just specify it in the constructor of `Sibit` object:
124
+ The first one in this list is used by default.
125
+ If you want to use a different one,
126
+ you just specify it in the constructor of `Sibit` object:
129
127
 
130
128
  ```ruby
131
129
  require 'sibit'
@@ -133,10 +131,11 @@ require 'sibit/btc'
133
131
  sibit = Sibit.new(api: Sibit::Btc.new)
134
132
  ```
135
133
 
136
- You may also use a combination of APIs. This may be very useful since
137
- some APIs are not reliable and others don't have all the features required.
138
- You can provide an array of objects and they
139
- will be used one by one, until a successful response is obtained:
134
+ You may also use a combination of APIs.
135
+ This may be very useful since some APIs are not reliable
136
+ and others don't have all the features required.
137
+ You can provide an array of objects and they are used one by one,
138
+ until a successful response is obtained:
140
139
 
141
140
  ```ruby
142
141
  require 'sibit'
@@ -168,13 +167,13 @@ gem install sibit
168
167
  ```
169
168
 
170
169
  It should work.
171
- If it doesn't, submit an issue and I will try to help.
170
+ If it doesn't, submit an issue and I can try to help.
172
171
 
173
172
  ## How to contribute
174
173
 
175
174
  Read [these guidelines].
176
- Make sure your build is green before you contribute
177
- your pull request. You will need to have [Ruby] 2.3+ and [Bundler] installed.
175
+ Make sure your build is green before you contribute your pull request.
176
+ You need to have [Ruby] 2.3+ and [Bundler] installed.
178
177
  Then:
179
178
 
180
179
  ```bash
data/bin/sibit CHANGED
@@ -6,10 +6,6 @@
6
6
 
7
7
  $stdout.sync = true
8
8
 
9
- # see https://stackoverflow.com/a/6048451/187141
10
- require 'openssl'
11
- OpenSSL::SSL::VERIFY_PEER ||= OpenSSL::SSL::VERIFY_NONE
12
-
13
9
  require 'backtrace'
14
10
  require 'loog'
15
11
  require 'retriable_proxy'
@@ -24,18 +20,18 @@ require_relative '../lib/sibit/fake'
24
20
  require_relative '../lib/sibit/firstof'
25
21
  require_relative '../lib/sibit/version'
26
22
 
27
- begin
23
+ opts =
28
24
  begin
29
- opts = Slop.parse(ARGV, strict: true, help: true) do |o|
25
+ Slop.parse(ARGV, strict: true, help: true) do |o|
30
26
  o.banner = "Usage (#{Sibit::VERSION}): sibit [options] command [args]
31
27
  Commands are:
32
- price: Get current price of BTC in USD
33
- fees: Get currently recommended transaction fees
34
- latest: Get hash of the latest block
35
- generate: Generate a new private key
36
- create: Create a public Bitcoin address from the key
37
- balance: Check the balance of the Bitcoin address
38
- pay: Send a new Bitcoin transaction
28
+ price: Get current price of BTC in USD
29
+ fees: Get currently recommended transaction fees
30
+ latest: Get hash of the latest block
31
+ generate: Generate a new private key
32
+ create: Create a public Bitcoin address from the key
33
+ balance: Check the balance of the Bitcoin address
34
+ pay: Send a new Bitcoin transaction
39
35
  Options are:"
40
36
  o.string '--proxy', 'HTTPS proxy for all requests, e.g. "localhost:3128"'
41
37
  o.integer(
@@ -63,6 +59,8 @@ Options are:"
63
59
  rescue Slop::Error => e
64
60
  raise e.message
65
61
  end
62
+
63
+ begin
66
64
  raise 'Try --help' if opts.arguments.empty?
67
65
  log = opts[:verbose] ? Loog::VERBOSE : Loog::NULL
68
66
  http = opts[:proxy] ? Sibit::HttpProxy.new(opts[:proxy]) : Sibit::Http.new
@@ -126,7 +124,7 @@ Options are:"
126
124
  raise 'Change argument is required' if change.nil?
127
125
  puts sibit.pay(
128
126
  amount, fee,
129
- sources.split(',').map { |p| p.split(':') }.to_h,
127
+ sources.split(','),
130
128
  target, change,
131
129
  skip_utxo: opts['skip-utxo']
132
130
  )
data/lib/sibit/bestof.rb CHANGED
@@ -12,96 +12,93 @@ require_relative 'error'
12
12
  # Author:: Yegor Bugayenko (yegor256@gmail.com)
13
13
  # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
14
14
  # License:: MIT
15
- class Sibit
16
- # Best of API.
17
- class BestOf
18
- # Constructor.
19
- def initialize(list, log: Loog::NULL, verbose: false)
20
- @list = list
21
- @log = log
22
- @verbose = verbose
23
- end
15
+ class Sibit::BestOf
16
+ # Constructor.
17
+ def initialize(list, log: Loog::NULL, verbose: false)
18
+ @list = list
19
+ @log = log
20
+ @verbose = verbose
21
+ end
24
22
 
25
- # Current price of BTC in USD (float returned).
26
- def price(currency = 'USD')
27
- best_of('price') do |api|
28
- api.price(currency)
29
- end
23
+ # Current price of BTC in USD (float returned).
24
+ def price(currency = 'USD')
25
+ best_of('price') do |api|
26
+ api.price(currency)
30
27
  end
28
+ end
31
29
 
32
- # Gets the balance of the address, in satoshi.
33
- def balance(address)
34
- best_of('balance') do |api|
35
- api.balance(address)
36
- end
30
+ # Gets the balance of the address, in satoshi.
31
+ def balance(address)
32
+ best_of('balance') do |api|
33
+ api.balance(address)
37
34
  end
35
+ end
38
36
 
39
- # Get the height of the block.
40
- def height(hash)
41
- best_of('height') do |api|
42
- api.height(hash)
43
- end
37
+ # Get the height of the block.
38
+ def height(hash)
39
+ best_of('height') do |api|
40
+ api.height(hash)
44
41
  end
42
+ end
45
43
 
46
- # Get the hash of the next block.
47
- def next_of(hash)
48
- best_of('next_of') do |api|
49
- api.next_of(hash)
50
- end
44
+ # Get the hash of the next block.
45
+ def next_of(hash)
46
+ best_of('next_of') do |api|
47
+ api.next_of(hash)
51
48
  end
49
+ end
52
50
 
53
- # Get recommended fees, in satoshi per byte. The method returns
54
- # a hash: { S: 12, M: 45, L: 100, XL: 200 }
55
- def fees
56
- best_of('fees', &:fees)
57
- end
51
+ # Get recommended fees, in satoshi per byte. The method returns
52
+ # a hash: { S: 12, M: 45, L: 100, XL: 200 }
53
+ def fees
54
+ best_of('fees', &:fees)
55
+ end
58
56
 
59
- # Fetch all unspent outputs per address.
60
- def utxos(keys)
61
- best_of('utxos') do |api|
62
- api.utxos(keys)
63
- end
57
+ # Fetch all unspent outputs per address.
58
+ def utxos(keys)
59
+ best_of('utxos') do |api|
60
+ api.utxos(keys)
64
61
  end
62
+ end
65
63
 
66
- # Latest block hash.
67
- def latest
68
- best_of('latest', &:latest)
69
- end
64
+ # Latest block hash.
65
+ def latest
66
+ best_of('latest', &:latest)
67
+ end
70
68
 
71
- # Push this transaction (in hex format) to the network.
72
- def push(hex)
73
- best_of('push') do |api|
74
- api.push(hex)
75
- end
69
+ # Push this transaction (in hex format) to the network.
70
+ def push(hex)
71
+ best_of('push') do |api|
72
+ api.push(hex)
76
73
  end
74
+ end
77
75
 
78
- # This method should fetch a block and return as a hash.
79
- def block(hash)
80
- best_of('block') do |api|
81
- api.block(hash)
82
- end
76
+ # This method should fetch a block and return as a hash.
77
+ def block(hash)
78
+ best_of('block') do |api|
79
+ api.block(hash)
83
80
  end
81
+ end
84
82
 
85
- private
83
+ private
86
84
 
87
- def best_of(method)
88
- return yield @list unless @list.is_a?(Array)
89
- results = []
90
- errors = []
91
- @list.each do |api|
92
- results << yield(api)
93
- rescue Sibit::NotSupportedError
94
- # Just ignore it
95
- rescue Sibit::Error => e
96
- errors << e
97
- @log.info("The API #{api.class.name} failed at #{method}(): #{e.message}") if @verbose
98
- end
99
- if results.empty?
100
- errors.each { |e| @log.info(Backtrace.new(e).to_s) }
101
- raise Sibit::Error, "No APIs out of #{@list.length} managed to succeed at #{method}(): \
85
+ def best_of(method)
86
+ return yield @list unless @list.is_a?(Array)
87
+ results = []
88
+ errors = []
89
+ @list.each do |api|
90
+ results << yield(api)
91
+ rescue Sibit::NotSupportedError
92
+ # Just ignore it
93
+ rescue Sibit::Error => e
94
+ errors << e
95
+ @log.info("The API #{api.class.name} failed at #{method}(): #{e.message}") if @verbose
96
+ end
97
+ if results.empty?
98
+ errors.each { |e| @log.info(Backtrace.new(e).to_s) }
99
+ raise Sibit::Error, "No APIs out of #{@list.length} managed to succeed at #{method}(): \
102
100
  #{@list.map { |a| a.class.name }.join(', ')}"
103
- end
104
- results.group_by(&:to_s).values.max_by(&:size)[0]
105
101
  end
102
+ results.group_by(&:to_s).values.max_by(&:size)[0]
106
103
  end
107
104
  end
@@ -5,46 +5,44 @@
5
5
 
6
6
  require 'digest'
7
7
 
8
- class Sibit
9
- # Bitcoin primitives module.
10
- #
11
- # Pure Ruby implementation of Bitcoin functionality using OpenSSL 3.0+.
12
- # Replaces the bitcoin-ruby dependency which is incompatible with OpenSSL 3.0.
13
- module Bitcoin
14
- MIN_TX_FEE = 10_000
8
+ # Bitcoin primitives module.
9
+ #
10
+ # Pure Ruby implementation of Bitcoin functionality using OpenSSL 3.0+.
11
+ # Replaces the bitcoin-ruby dependency which is incompatible with OpenSSL 3.0.
12
+ module Sibit::Bitcoin
13
+ MIN_TX_FEE = 10_000
15
14
 
16
- # Base58 encoding for Bitcoin addresses.
17
- #
18
- # Author:: Yegor Bugayenko (yegor256@gmail.com)
19
- # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
20
- # License:: MIT
21
- module Base58
22
- ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
15
+ # Base58 encoding for Bitcoin addresses.
16
+ #
17
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
18
+ # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
19
+ # License:: MIT
20
+ module Base58
21
+ ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
23
22
 
24
- def self.encode(hex)
25
- bytes = [hex].pack('H*')
26
- leading = bytes.match(/^\x00*/)[0].length
27
- num = hex.to_i(16)
28
- result = ''
29
- while num.positive?
30
- num, remainder = num.divmod(58)
31
- result = ALPHABET[remainder] + result
32
- end
33
- ('1' * leading) + result
23
+ def self.encode(hex)
24
+ bytes = [hex].pack('H*')
25
+ leading = bytes.match(/^\x00*/)[0].length
26
+ num = hex.to_i(16)
27
+ result = ''
28
+ while num.positive?
29
+ num, remainder = num.divmod(58)
30
+ result = ALPHABET[remainder] + result
34
31
  end
32
+ ('1' * leading) + result
33
+ end
35
34
 
36
- def self.decode(str)
37
- leading = str.match(/^1*/)[0].length
38
- num = 0
39
- str.each_char { |c| num = (num * 58) + ALPHABET.index(c) }
40
- hex = num.zero? ? '' : num.to_s(16)
41
- hex = "0#{hex}" if hex.length.odd?
42
- ('00' * leading) + hex
43
- end
35
+ def self.decode(str)
36
+ leading = str.match(/^1*/)[0].length
37
+ num = 0
38
+ str.each_char { |c| num = (num * 58) + ALPHABET.index(c) }
39
+ hex = num.zero? ? '' : num.to_s(16)
40
+ hex = "0#{hex}" if hex.length.odd?
41
+ ('00' * leading) + hex
42
+ end
44
43
 
45
- def self.check(hex)
46
- Digest::SHA256.hexdigest(Digest::SHA256.digest([hex].pack('H*')))[0...8]
47
- end
44
+ def self.check(hex)
45
+ Digest::SHA256.hexdigest(Digest::SHA256.digest([hex].pack('H*')))[0...8]
48
46
  end
49
47
  end
50
48
  end
@@ -3,85 +3,83 @@
3
3
  # SPDX-FileCopyrightText: Copyright (c) 2019-2025 Yegor Bugayenko
4
4
  # SPDX-License-Identifier: MIT
5
5
 
6
- require 'openssl'
7
6
  require 'digest'
7
+ require 'openssl'
8
8
  require_relative 'base58'
9
9
 
10
- class Sibit
11
- module Bitcoin
12
- # Bitcoin ECDSA key using secp256k1 curve.
13
- #
14
- # Supports OpenSSL 3.0+ by constructing keys via DER encoding instead
15
- # of using deprecated mutable key APIs.
16
- #
17
- # Author:: Yegor Bugayenko (yegor256@gmail.com)
18
- # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
19
- # License:: MIT
20
- class Key
21
- MIN_PRIV = 0x01
22
- MAX_PRIV = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140
10
+ module Sibit::Bitcoin
11
+ # Bitcoin ECDSA key using secp256k1 curve.
12
+ #
13
+ # Supports OpenSSL 3.0+ by constructing keys via DER encoding instead
14
+ # of using deprecated mutable key APIs.
15
+ #
16
+ # Author:: Yegor Bugayenko (yegor256@gmail.com)
17
+ # Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
18
+ # License:: MIT
19
+ class Key
20
+ MIN_PRIV = 0x01
21
+ MAX_PRIV = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140
23
22
 
24
- def self.generate
25
- key = OpenSSL::PKey::EC.generate('secp256k1')
26
- pvt = key.private_key.to_s(16).rjust(64, '0').downcase
27
- new(pvt)
28
- end
23
+ def self.generate
24
+ key = OpenSSL::PKey::EC.generate('secp256k1')
25
+ pvt = key.private_key.to_s(16).rjust(64, '0').downcase
26
+ new(pvt)
27
+ end
29
28
 
30
- def initialize(privkey)
31
- @privkey = privkey
32
- @compressed = true
33
- @key = build(privkey)
34
- end
29
+ def initialize(privkey)
30
+ @privkey = privkey
31
+ @compressed = true
32
+ @key = build(privkey)
33
+ end
35
34
 
36
- def priv
37
- @privkey
38
- end
35
+ def priv
36
+ @privkey
37
+ end
39
38
 
40
- def pub
41
- point = @key.public_key
42
- point.to_octet_string(@compressed ? :compressed : :uncompressed).unpack1('H*')
43
- end
39
+ def pub
40
+ point = @key.public_key
41
+ point.to_octet_string(@compressed ? :compressed : :uncompressed).unpack1('H*')
42
+ end
44
43
 
45
- def addr
46
- hash = hash160(pub)
47
- versioned = "00#{hash}"
48
- checksum = Base58.check(versioned)
49
- Base58.encode(versioned + checksum)
50
- end
44
+ def addr
45
+ hash = hash160(pub)
46
+ versioned = "00#{hash}"
47
+ checksum = Base58.check(versioned)
48
+ Base58.encode(versioned + checksum)
49
+ end
51
50
 
52
- def sign(data)
53
- @key.sign('SHA256', data)
54
- end
51
+ def sign(data)
52
+ @key.sign('SHA256', data)
53
+ end
55
54
 
56
- def verify(data, sig)
57
- @key.verify('SHA256', sig, data)
58
- rescue OpenSSL::PKey::PKeyError
59
- false
60
- end
55
+ def verify(data, sig)
56
+ @key.verify('SHA256', sig, data)
57
+ rescue OpenSSL::PKey::PKeyError
58
+ false
59
+ end
61
60
 
62
- private
61
+ private
63
62
 
64
- def build(privkey)
65
- value = privkey.to_i(16)
66
- raise 'private key is not on curve' unless value.between?(MIN_PRIV, MAX_PRIV)
67
- group = OpenSSL::PKey::EC::Group.new('secp256k1')
68
- bn = OpenSSL::BN.new(privkey, 16)
69
- pubkey = group.generator.mul(bn)
70
- asn1 = OpenSSL::ASN1::Sequence(
71
- [
72
- OpenSSL::ASN1::Integer.new(1),
73
- OpenSSL::ASN1::OctetString(bn.to_s(2)),
74
- OpenSSL::ASN1::ObjectId('secp256k1', 0, :EXPLICIT),
75
- OpenSSL::ASN1::BitString(pubkey.to_octet_string(:uncompressed), 1, :EXPLICIT)
76
- ]
77
- )
78
- OpenSSL::PKey::EC.new(asn1.to_der)
79
- end
63
+ def build(privkey)
64
+ value = privkey.to_i(16)
65
+ raise 'private key is not on curve' unless value.between?(MIN_PRIV, MAX_PRIV)
66
+ group = OpenSSL::PKey::EC::Group.new('secp256k1')
67
+ bn = OpenSSL::BN.new(privkey, 16)
68
+ pubkey = group.generator.mul(bn)
69
+ asn1 = OpenSSL::ASN1::Sequence(
70
+ [
71
+ OpenSSL::ASN1::Integer.new(1),
72
+ OpenSSL::ASN1::OctetString(bn.to_s(2)),
73
+ OpenSSL::ASN1::ObjectId('secp256k1', 0, :EXPLICIT),
74
+ OpenSSL::ASN1::BitString(pubkey.to_octet_string(:uncompressed), 1, :EXPLICIT)
75
+ ]
76
+ )
77
+ OpenSSL::PKey::EC.new(asn1.to_der)
78
+ end
80
79
 
81
- def hash160(hex)
82
- bytes = [hex].pack('H*')
83
- Digest::RMD160.hexdigest(Digest::SHA256.digest(bytes))
84
- end
80
+ def hash160(hex)
81
+ bytes = [hex].pack('H*')
82
+ Digest::RMD160.hexdigest(Digest::SHA256.digest(bytes))
85
83
  end
86
84
  end
87
85
  end