sibit 0.30.0 → 0.30.2
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 +4 -4
- data/Gemfile.lock +9 -6
- data/README.md +2 -2
- data/bin/sibit +32 -26
- data/lib/sibit/bestof.rb +2 -2
- data/lib/sibit/bitcoinchain.rb +6 -7
- data/lib/sibit/blockchain.rb +13 -11
- data/lib/sibit/blockchair.rb +4 -5
- data/lib/sibit/btc.rb +9 -10
- data/lib/sibit/cex.rb +2 -3
- data/lib/sibit/cryptoapis.rb +6 -7
- data/lib/sibit/dry.rb +29 -0
- data/lib/sibit/firstof.rb +3 -3
- data/lib/sibit/json.rb +40 -36
- data/lib/sibit/version.rb +1 -1
- data/lib/sibit.rb +29 -24
- data/sibit.gemspec +5 -3
- metadata +42 -13
- /data/lib/sibit/{bitcoin/base58.rb → base58.rb} +0 -0
- /data/lib/sibit/{bitcoin/key.rb → key.rb} +0 -0
- /data/lib/sibit/{bitcoin/script.rb → script.rb} +0 -0
- /data/lib/sibit/{bitcoin/tx.rb → tx.rb} +0 -0
- /data/lib/sibit/{bitcoin/txbuilder.rb → txbuilder.rb} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7463ec26890a5bd8515ce96c92190a50edaf46f5b94aed0a1b54261f0d63ca65
|
|
4
|
+
data.tar.gz: 71be0a78c7bfcfffdebc6daa2c63a570d561b5e6e923f0d600e2791c2856cac5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ae0606a033a0506be0d3f397a0ac81bf51db53e7c35671256642764e2dcca358d95cbb9517c67c0a49f0621110926760154ea1c1c885d4aa5b6a2a32792078ff
|
|
7
|
+
data.tar.gz: 21a0bf5f5e27f3c22ceb82f2432d3b3fdd8a4ae1181cbdad383d34f081024c4802b6c83c2a7f51c1205728ccede22c8e45c8498f2d74ffd04282f828b29b4c54
|
data/Gemfile.lock
CHANGED
|
@@ -3,10 +3,12 @@ PATH
|
|
|
3
3
|
specs:
|
|
4
4
|
sibit (0.0.0)
|
|
5
5
|
backtrace (~> 0.3)
|
|
6
|
+
decoor (~> 0.1)
|
|
7
|
+
elapsed (~> 0.2)
|
|
6
8
|
iri (~> 0.5)
|
|
7
|
-
json (~> 2)
|
|
9
|
+
json (~> 2.18)
|
|
8
10
|
loog (~> 0.6)
|
|
9
|
-
openssl (
|
|
11
|
+
openssl (~> 3.0)
|
|
10
12
|
retriable_proxy (~> 1.0)
|
|
11
13
|
slop (~> 4.6)
|
|
12
14
|
|
|
@@ -57,6 +59,7 @@ GEM
|
|
|
57
59
|
cucumber-messages (31.1.0)
|
|
58
60
|
cucumber-tag-expressions (8.1.0)
|
|
59
61
|
date (3.5.1)
|
|
62
|
+
decoor (0.1.0)
|
|
60
63
|
diff-lcs (1.6.2)
|
|
61
64
|
docile (1.4.1)
|
|
62
65
|
elapsed (0.2.1)
|
|
@@ -75,7 +78,6 @@ GEM
|
|
|
75
78
|
logger (~> 1.0)
|
|
76
79
|
memoist3 (1.0.0)
|
|
77
80
|
mini_mime (1.1.5)
|
|
78
|
-
mini_portile2 (2.8.9)
|
|
79
81
|
minitest (6.0.1)
|
|
80
82
|
prism (~> 1.5)
|
|
81
83
|
minitest-reporters (1.7.1)
|
|
@@ -84,10 +86,11 @@ GEM
|
|
|
84
86
|
minitest (>= 5.0)
|
|
85
87
|
ruby-progressbar
|
|
86
88
|
multi_test (1.1.0)
|
|
87
|
-
nokogiri (1.18.10)
|
|
88
|
-
mini_portile2 (~> 2.8.2)
|
|
89
|
+
nokogiri (1.18.10-arm64-darwin)
|
|
89
90
|
racc (~> 1.4)
|
|
90
|
-
|
|
91
|
+
nokogiri (1.18.10-x86_64-linux-gnu)
|
|
92
|
+
racc (~> 1.4)
|
|
93
|
+
openssl (3.3.2)
|
|
91
94
|
os (1.1.4)
|
|
92
95
|
parallel (1.27.0)
|
|
93
96
|
parser (3.3.10.0)
|
data/README.md
CHANGED
|
@@ -91,13 +91,13 @@ If everything looks correct, remove the `--dry` and run again,
|
|
|
91
91
|
To use an HTTPS proxy for all requests:
|
|
92
92
|
|
|
93
93
|
```bash
|
|
94
|
-
|
|
94
|
+
sibit --proxy=host:port balance 1PfsYNygsuVL8fvBarJNQnHytkg4rGih1U
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
The proxy address may include authentication credentials:
|
|
98
98
|
|
|
99
99
|
```bash
|
|
100
|
-
|
|
100
|
+
sibit --proxy=user:password@host:port balance 1PfsYNygsuVL8fvBarJNQnHytkg4rGih1U
|
|
101
101
|
```
|
|
102
102
|
|
|
103
103
|
All operations are performed through the [Blockchain API].
|
data/bin/sibit
CHANGED
|
@@ -18,10 +18,13 @@ require_relative '../lib/sibit/blockchain'
|
|
|
18
18
|
require_relative '../lib/sibit/blockchair'
|
|
19
19
|
require_relative '../lib/sibit/btc'
|
|
20
20
|
require_relative '../lib/sibit/cex'
|
|
21
|
+
require_relative '../lib/sibit/dry'
|
|
21
22
|
require_relative '../lib/sibit/fake'
|
|
22
23
|
require_relative '../lib/sibit/firstof'
|
|
23
24
|
require_relative '../lib/sibit/version'
|
|
24
25
|
|
|
26
|
+
log = Loog::REGULAR
|
|
27
|
+
|
|
25
28
|
opts =
|
|
26
29
|
begin
|
|
27
30
|
Slop.parse(ARGV, strict: true, help: true) do |o|
|
|
@@ -43,7 +46,7 @@ Options are:"
|
|
|
43
46
|
)
|
|
44
47
|
o.bool '--dry', 'Don\'t send a real payment, run in a read-only mode'
|
|
45
48
|
o.bool '--help', 'Read this: https://github.com/yegor256/sibit' do
|
|
46
|
-
|
|
49
|
+
log.info(o)
|
|
47
50
|
exit
|
|
48
51
|
end
|
|
49
52
|
o.bool '--verbose', 'Print all possible debug messages'
|
|
@@ -62,35 +65,36 @@ Options are:"
|
|
|
62
65
|
raise e.message
|
|
63
66
|
end
|
|
64
67
|
|
|
68
|
+
raise 'Try --help' if opts.arguments.empty?
|
|
69
|
+
|
|
65
70
|
begin
|
|
66
|
-
|
|
67
|
-
log = opts[:verbose] ? Loog::VERBOSE : Loog::NULL
|
|
71
|
+
log = Loog::VERBOSE if opts[:verbose]
|
|
68
72
|
http = opts[:proxy] ? Sibit::HttpProxy.new(opts[:proxy]) : Sibit::Http.new
|
|
69
73
|
apis = opts[:api].map(&:downcase).map do |a|
|
|
70
|
-
api = nil
|
|
71
74
|
case a
|
|
72
75
|
when 'blockchain'
|
|
73
|
-
|
|
76
|
+
Sibit::Blockchain.new(http: http, log: log)
|
|
74
77
|
when 'btc'
|
|
75
|
-
|
|
78
|
+
Sibit::Btc.new(http: http, log: log)
|
|
76
79
|
when 'bitcoinchain'
|
|
77
|
-
|
|
80
|
+
Sibit::Bitcoinchain.new(http: http, log: log)
|
|
78
81
|
when 'blockchair'
|
|
79
|
-
|
|
82
|
+
Sibit::Blockchair.new(http: http, log: log)
|
|
80
83
|
when 'cex'
|
|
81
|
-
|
|
84
|
+
Sibit::Cex.new(http: http, log: log)
|
|
82
85
|
when 'fake'
|
|
83
|
-
|
|
86
|
+
Sibit::Fake.new
|
|
84
87
|
else
|
|
85
88
|
raise Sibit::Error, "Unknown API \"#{a}\""
|
|
86
89
|
end
|
|
87
|
-
api = RetriableProxy.for_object(api, on: Sibit::Error) if opts[:attempts] > 1
|
|
88
|
-
api
|
|
89
90
|
end
|
|
90
|
-
|
|
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)
|
|
91
95
|
case opts.arguments[0]
|
|
92
96
|
when 'price'
|
|
93
|
-
|
|
97
|
+
log.info(sibit.price)
|
|
94
98
|
when 'fees'
|
|
95
99
|
fees = sibit.fees
|
|
96
100
|
text = %i[S M L XL].map do |m|
|
|
@@ -98,19 +102,19 @@ begin
|
|
|
98
102
|
usd = sat * sibit.price / 100_000_000
|
|
99
103
|
"#{m}: #{sat}sat / $#{format('%<usd>.02f', usd: usd)}"
|
|
100
104
|
end.join("\n")
|
|
101
|
-
|
|
105
|
+
log.info(text)
|
|
102
106
|
when 'latest'
|
|
103
|
-
|
|
107
|
+
log.info(sibit.latest)
|
|
104
108
|
when 'generate'
|
|
105
|
-
|
|
109
|
+
log.info(sibit.generate)
|
|
106
110
|
when 'create'
|
|
107
111
|
pvt = opts.arguments[1]
|
|
108
112
|
raise 'Private key argument is required' if pvt.nil?
|
|
109
|
-
|
|
113
|
+
log.info(sibit.create(pvt))
|
|
110
114
|
when 'balance'
|
|
111
115
|
address = opts.arguments[1]
|
|
112
116
|
raise 'Address argument is required' if address.nil?
|
|
113
|
-
|
|
117
|
+
log.info(sibit.balance(address))
|
|
114
118
|
when 'pay'
|
|
115
119
|
amount = opts.arguments[1]
|
|
116
120
|
raise 'Amount argument is required' if amount.nil?
|
|
@@ -124,20 +128,22 @@ begin
|
|
|
124
128
|
raise 'Target argument is required' if target.nil?
|
|
125
129
|
change = opts.arguments[5]
|
|
126
130
|
raise 'Change argument is required' if change.nil?
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
131
|
+
log.info(
|
|
132
|
+
sibit.pay(
|
|
133
|
+
amount, fee,
|
|
134
|
+
sources.split(','),
|
|
135
|
+
target, change,
|
|
136
|
+
skip_utxo: opts['skip-utxo']
|
|
137
|
+
)
|
|
132
138
|
)
|
|
133
139
|
else
|
|
134
140
|
raise "Command #{opts.arguments[0]} is not supported"
|
|
135
141
|
end
|
|
136
142
|
rescue StandardError => e
|
|
137
143
|
if opts[:verbose]
|
|
138
|
-
|
|
144
|
+
log.error(Backtrace.new(e))
|
|
139
145
|
else
|
|
140
|
-
|
|
146
|
+
log.error(e.message)
|
|
141
147
|
end
|
|
142
148
|
exit(255)
|
|
143
149
|
end
|
data/lib/sibit/bestof.rb
CHANGED
|
@@ -92,10 +92,10 @@ class Sibit::BestOf
|
|
|
92
92
|
# Just ignore it
|
|
93
93
|
rescue Sibit::Error => e
|
|
94
94
|
errors << e
|
|
95
|
-
@log.
|
|
95
|
+
@log.debug("The API #{api.class.name} failed at #{method}(): #{e.message}") if @verbose
|
|
96
96
|
end
|
|
97
97
|
if results.empty?
|
|
98
|
-
errors.each { |e| @log.
|
|
98
|
+
errors.each { |e| @log.debug(Backtrace.new(e).to_s) }
|
|
99
99
|
raise Sibit::Error, "No APIs out of #{@list.length} managed to succeed at #{method}(): \
|
|
100
100
|
#{@list.map { |a| a.class.name }.join(', ')}"
|
|
101
101
|
end
|
data/lib/sibit/bitcoinchain.rb
CHANGED
|
@@ -19,10 +19,9 @@ require_relative 'version'
|
|
|
19
19
|
# License:: MIT
|
|
20
20
|
class Sibit::Bitcoinchain
|
|
21
21
|
# Constructor.
|
|
22
|
-
def initialize(log: Loog::NULL, http: Sibit::Http.new
|
|
22
|
+
def initialize(log: Loog::NULL, http: Sibit::Http.new)
|
|
23
23
|
@http = http
|
|
24
24
|
@log = log
|
|
25
|
-
@dry = dry
|
|
26
25
|
end
|
|
27
26
|
|
|
28
27
|
# Current price of BTC in USD (float returned).
|
|
@@ -43,8 +42,8 @@ class Sibit::Bitcoinchain
|
|
|
43
42
|
raise Sibit::Error, "Block #{hash} not found" if block.nil?
|
|
44
43
|
nxt = block['next_block']
|
|
45
44
|
nxt = nil if nxt == '0000000000000000000000000000000000000000000000000000000000000000'
|
|
46
|
-
@log.
|
|
47
|
-
@log.
|
|
45
|
+
@log.debug("The block #{hash} is the latest, there is no next block") if nxt.nil?
|
|
46
|
+
@log.debug("The next block of #{hash} is #{nxt}") unless nxt.nil?
|
|
48
47
|
nxt
|
|
49
48
|
end
|
|
50
49
|
|
|
@@ -56,12 +55,12 @@ class Sibit::Bitcoinchain
|
|
|
56
55
|
)[0]
|
|
57
56
|
b = json['balance']
|
|
58
57
|
if b.nil?
|
|
59
|
-
@log.
|
|
58
|
+
@log.debug("The balance of #{address} is not visible")
|
|
60
59
|
return 0
|
|
61
60
|
end
|
|
62
61
|
b *= 100_000_000
|
|
63
62
|
b = b.to_i
|
|
64
|
-
@log.
|
|
63
|
+
@log.debug("The balance of #{address} is #{b} satoshi (#{json['transactions']} txns)")
|
|
65
64
|
b
|
|
66
65
|
end
|
|
67
66
|
|
|
@@ -75,7 +74,7 @@ class Sibit::Bitcoinchain
|
|
|
75
74
|
hash = Sibit::Json.new(http: @http, log: @log).get(
|
|
76
75
|
Iri.new('https://api-r.bitcoinchain.com/v1/status')
|
|
77
76
|
)['hash']
|
|
78
|
-
@log.
|
|
77
|
+
@log.debug("The latest block hash is #{hash}")
|
|
79
78
|
hash
|
|
80
79
|
end
|
|
81
80
|
|
data/lib/sibit/blockchain.rb
CHANGED
|
@@ -22,10 +22,9 @@ require_relative 'version'
|
|
|
22
22
|
# License:: MIT
|
|
23
23
|
class Sibit::Blockchain
|
|
24
24
|
# Constructor.
|
|
25
|
-
def initialize(log: Loog::NULL, http: Sibit::Http.new
|
|
25
|
+
def initialize(log: Loog::NULL, http: Sibit::Http.new)
|
|
26
26
|
@http = http
|
|
27
27
|
@log = log
|
|
28
|
-
@dry = dry
|
|
29
28
|
end
|
|
30
29
|
|
|
31
30
|
# Current price of BTC in USD (float returned).
|
|
@@ -35,7 +34,7 @@ class Sibit::Blockchain
|
|
|
35
34
|
)[currency]
|
|
36
35
|
raise Error, "Unrecognized currency #{currency}" if h.nil?
|
|
37
36
|
price = h['15m']
|
|
38
|
-
@log.
|
|
37
|
+
@log.debug("The price of BTC is #{price} USD")
|
|
39
38
|
price
|
|
40
39
|
end
|
|
41
40
|
|
|
@@ -47,9 +46,9 @@ class Sibit::Blockchain
|
|
|
47
46
|
# )
|
|
48
47
|
# nxt = json['next_block'][0]
|
|
49
48
|
# if nxt.nil?
|
|
50
|
-
# @log.
|
|
49
|
+
# @log.debug("There is no block after #{hash}")
|
|
51
50
|
# else
|
|
52
|
-
# @log.
|
|
51
|
+
# @log.debug("The next block of #{hash} is #{nxt}")
|
|
53
52
|
# end
|
|
54
53
|
# nxt
|
|
55
54
|
end
|
|
@@ -60,7 +59,7 @@ class Sibit::Blockchain
|
|
|
60
59
|
Iri.new('https://blockchain.info/rawblock').append(hash)
|
|
61
60
|
)
|
|
62
61
|
h = json['height']
|
|
63
|
-
@log.
|
|
62
|
+
@log.debug("The height of #{hash} is #{h}")
|
|
64
63
|
h
|
|
65
64
|
end
|
|
66
65
|
|
|
@@ -71,7 +70,7 @@ class Sibit::Blockchain
|
|
|
71
70
|
accept: [200, 500]
|
|
72
71
|
)
|
|
73
72
|
b = json['final_balance']
|
|
74
|
-
@log.
|
|
73
|
+
@log.debug("The balance of #{address} is #{b} satoshi (#{json['n_tx']} txns)")
|
|
75
74
|
b
|
|
76
75
|
end
|
|
77
76
|
|
|
@@ -80,8 +79,12 @@ class Sibit::Blockchain
|
|
|
80
79
|
json = Sibit::Json.new(http: @http, log: @log).get(
|
|
81
80
|
Iri.new('https://api.blockchain.info/mempool/fees')
|
|
82
81
|
)
|
|
83
|
-
@log.
|
|
84
|
-
|
|
82
|
+
@log.debug(
|
|
83
|
+
[
|
|
84
|
+
'Currently recommended Bitcoin fees: ',
|
|
85
|
+
"#{json['regular']}/#{json['priority']}/#{json['limits']['max']} sat/byte"
|
|
86
|
+
].join
|
|
87
|
+
)
|
|
85
88
|
{
|
|
86
89
|
S: json['regular'] / 3,
|
|
87
90
|
M: json['regular'],
|
|
@@ -108,7 +111,6 @@ class Sibit::Blockchain
|
|
|
108
111
|
|
|
109
112
|
# Push this transaction (in hex format) to the network.
|
|
110
113
|
def push(hex)
|
|
111
|
-
return if @dry
|
|
112
114
|
Sibit::Json.new(http: @http, log: @log).post(
|
|
113
115
|
Iri.new('https://blockchain.info/pushtx'),
|
|
114
116
|
hex
|
|
@@ -120,7 +122,7 @@ class Sibit::Blockchain
|
|
|
120
122
|
hash = Sibit::Json.new(http: @http, log: @log).get(
|
|
121
123
|
Iri.new('https://blockchain.info/latestblock')
|
|
122
124
|
)['hash']
|
|
123
|
-
@log.
|
|
125
|
+
@log.debug("The latest block hash is #{hash}")
|
|
124
126
|
hash
|
|
125
127
|
end
|
|
126
128
|
|
data/lib/sibit/blockchair.rb
CHANGED
|
@@ -20,11 +20,10 @@ require_relative 'version'
|
|
|
20
20
|
# License:: MIT
|
|
21
21
|
class Sibit::Blockchair
|
|
22
22
|
# Constructor.
|
|
23
|
-
def initialize(key: nil, log: Loog::NULL, http: Sibit::Http.new
|
|
23
|
+
def initialize(key: nil, log: Loog::NULL, http: Sibit::Http.new)
|
|
24
24
|
@key = key
|
|
25
25
|
@http = http
|
|
26
26
|
@log = log
|
|
27
|
-
@dry = dry
|
|
28
27
|
end
|
|
29
28
|
|
|
30
29
|
# Current price of BTC in USD (float returned).
|
|
@@ -49,12 +48,12 @@ class Sibit::Blockchair
|
|
|
49
48
|
Iri.new('https://api.blockchair.com/bitcoin/dashboards/address').append(address).fragment(the_key)
|
|
50
49
|
)['data'][address]
|
|
51
50
|
if json.nil?
|
|
52
|
-
@log.
|
|
51
|
+
@log.debug("Address #{address} not found")
|
|
53
52
|
return 0
|
|
54
53
|
end
|
|
55
54
|
a = json['address']
|
|
56
55
|
b = a['balance']
|
|
57
|
-
@log.
|
|
56
|
+
@log.debug("The balance of #{address} is #{b} satoshi")
|
|
58
57
|
b
|
|
59
58
|
end
|
|
60
59
|
|
|
@@ -79,7 +78,7 @@ class Sibit::Blockchair
|
|
|
79
78
|
Iri.new('https://api.blockchair.com/bitcoin/push/transaction').fragment(the_key),
|
|
80
79
|
"data=#{hex}"
|
|
81
80
|
)
|
|
82
|
-
@log.
|
|
81
|
+
@log.debug("Transaction (#{hex.length} in hex) has been pushed to Blockchair")
|
|
83
82
|
end
|
|
84
83
|
|
|
85
84
|
# This method should fetch a Blockchain block and return as a hash.
|
data/lib/sibit/btc.rb
CHANGED
|
@@ -21,10 +21,9 @@ require_relative 'version'
|
|
|
21
21
|
# License:: MIT
|
|
22
22
|
class Sibit::Btc
|
|
23
23
|
# Constructor.
|
|
24
|
-
def initialize(log: Loog::NULL, http: Sibit::Http.new
|
|
24
|
+
def initialize(log: Loog::NULL, http: Sibit::Http.new)
|
|
25
25
|
@http = http
|
|
26
26
|
@log = log
|
|
27
|
-
@dry = dry
|
|
28
27
|
end
|
|
29
28
|
|
|
30
29
|
# Current price of BTC in USD (float returned).
|
|
@@ -37,21 +36,21 @@ class Sibit::Btc
|
|
|
37
36
|
uri = Iri.new('https://chain.api.btc.com/v3/address').append(address).append('unspent')
|
|
38
37
|
json = Sibit::Json.new(http: @http, log: @log).get(uri)
|
|
39
38
|
if json['err_no'] == 1
|
|
40
|
-
@log.
|
|
39
|
+
@log.debug("The balance of #{address} is zero (not found)")
|
|
41
40
|
return 0
|
|
42
41
|
end
|
|
43
42
|
data = json['data']
|
|
44
43
|
if data.nil?
|
|
45
|
-
@log.
|
|
44
|
+
@log.debug("The balance of #{address} is probably zero (not found)")
|
|
46
45
|
return 0
|
|
47
46
|
end
|
|
48
47
|
txns = data['list']
|
|
49
48
|
if txns.nil?
|
|
50
|
-
@log.
|
|
49
|
+
@log.debug("The balance of #{address} is probably zero (not found)")
|
|
51
50
|
return 0
|
|
52
51
|
end
|
|
53
52
|
balance = txns.sum { |tx| tx['value'] } || 0
|
|
54
|
-
@log.
|
|
53
|
+
@log.debug("The balance of #{address} is #{balance}, total txns: #{txns.count}")
|
|
55
54
|
balance
|
|
56
55
|
end
|
|
57
56
|
|
|
@@ -64,8 +63,8 @@ class Sibit::Btc
|
|
|
64
63
|
raise Sibit::Error, "The block #{hash} not found" if data.nil?
|
|
65
64
|
nxt = data['next_block_hash']
|
|
66
65
|
nxt = nil if nxt == '0000000000000000000000000000000000000000000000000000000000000000'
|
|
67
|
-
@log.
|
|
68
|
-
@log.
|
|
66
|
+
@log.debug("In BTC.com the block #{hash} is the latest, there is no next block") if nxt.nil?
|
|
67
|
+
@log.debug("The next block of #{hash} is #{nxt}") unless nxt.nil?
|
|
69
68
|
nxt
|
|
70
69
|
end
|
|
71
70
|
|
|
@@ -78,7 +77,7 @@ class Sibit::Btc
|
|
|
78
77
|
raise Sibit::Error, "The block #{hash} not found" if data.nil?
|
|
79
78
|
h = data['height']
|
|
80
79
|
raise Sibit::Error, "The block #{hash} found but the height is absent" if h.nil?
|
|
81
|
-
@log.
|
|
80
|
+
@log.debug("The height of #{hash} is #{h}")
|
|
82
81
|
h
|
|
83
82
|
end
|
|
84
83
|
|
|
@@ -94,7 +93,7 @@ class Sibit::Btc
|
|
|
94
93
|
data = json['data']
|
|
95
94
|
raise Sibit::Error, 'The latest block not found' if data.nil?
|
|
96
95
|
hash = data['hash']
|
|
97
|
-
@log.
|
|
96
|
+
@log.debug("The hash of the latest block is #{hash}")
|
|
98
97
|
hash
|
|
99
98
|
end
|
|
100
99
|
|
data/lib/sibit/cex.rb
CHANGED
|
@@ -18,10 +18,9 @@ require_relative 'json'
|
|
|
18
18
|
# License:: MIT
|
|
19
19
|
class Sibit::Cex
|
|
20
20
|
# Constructor.
|
|
21
|
-
def initialize(log: Loog::NULL, http: Sibit::Http.new
|
|
21
|
+
def initialize(log: Loog::NULL, http: Sibit::Http.new)
|
|
22
22
|
@http = http
|
|
23
23
|
@log = log
|
|
24
|
-
@dry = dry
|
|
25
24
|
end
|
|
26
25
|
|
|
27
26
|
# Current price of BTC in USD (float returned).
|
|
@@ -30,7 +29,7 @@ class Sibit::Cex
|
|
|
30
29
|
Iri.new('https://cex.io/api/last_price/BTC').append(currency)
|
|
31
30
|
)
|
|
32
31
|
p = json['lprice'].to_f
|
|
33
|
-
@log.
|
|
32
|
+
@log.debug("The price of BTC is #{p} #{currency}")
|
|
34
33
|
p
|
|
35
34
|
end
|
|
36
35
|
|
data/lib/sibit/cryptoapis.rb
CHANGED
|
@@ -19,11 +19,10 @@ require_relative 'version'
|
|
|
19
19
|
# License:: MIT
|
|
20
20
|
class Sibit::Cryptoapis
|
|
21
21
|
# Constructor.
|
|
22
|
-
def initialize(key, log: Loog::NULL, http: Sibit::Http.new
|
|
22
|
+
def initialize(key, log: Loog::NULL, http: Sibit::Http.new)
|
|
23
23
|
@key = key
|
|
24
24
|
@http = http
|
|
25
25
|
@log = log
|
|
26
|
-
@dry = dry
|
|
27
26
|
end
|
|
28
27
|
|
|
29
28
|
# Current price of BTC in USD (float returned).
|
|
@@ -37,8 +36,8 @@ class Sibit::Cryptoapis
|
|
|
37
36
|
Iri.new('https://api.cryptoapis.io/v1/bc/btc/mainnet/blocks').append(hash),
|
|
38
37
|
headers: headers
|
|
39
38
|
)['payload']['hash']
|
|
40
|
-
@log.
|
|
41
|
-
@log.
|
|
39
|
+
@log.debug("The block #{hash} is the latest, there is no next block") if nxt.nil?
|
|
40
|
+
@log.debug("The next block of #{hash} is #{nxt}") unless nxt.nil?
|
|
42
41
|
nxt
|
|
43
42
|
end
|
|
44
43
|
|
|
@@ -49,7 +48,7 @@ class Sibit::Cryptoapis
|
|
|
49
48
|
headers: headers
|
|
50
49
|
)['payload']
|
|
51
50
|
h = json['height']
|
|
52
|
-
@log.
|
|
51
|
+
@log.debug("The height of #{hash} is #{h}")
|
|
53
52
|
h
|
|
54
53
|
end
|
|
55
54
|
|
|
@@ -60,7 +59,7 @@ class Sibit::Cryptoapis
|
|
|
60
59
|
headers: headers
|
|
61
60
|
)['payload']
|
|
62
61
|
b = (json['balance'].to_f * 100_000_000).to_i
|
|
63
|
-
@log.
|
|
62
|
+
@log.debug("The balance of #{address} is #{b} satoshi")
|
|
64
63
|
b
|
|
65
64
|
end
|
|
66
65
|
|
|
@@ -75,7 +74,7 @@ class Sibit::Cryptoapis
|
|
|
75
74
|
Iri.new('https://api.cryptoapis.io/v1/bc/btc/mainnet/blocks/latest'),
|
|
76
75
|
headers: headers
|
|
77
76
|
)['payload']['hash']
|
|
78
|
-
@log.
|
|
77
|
+
@log.debug("The latest block hash is #{hash}")
|
|
79
78
|
hash
|
|
80
79
|
end
|
|
81
80
|
|
data/lib/sibit/dry.rb
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# SPDX-FileCopyrightText: Copyright (c) 2019-2025 Yegor Bugayenko
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
|
|
6
|
+
require 'decoor'
|
|
7
|
+
require 'loog'
|
|
8
|
+
|
|
9
|
+
# Dry mode decorator for API classes.
|
|
10
|
+
#
|
|
11
|
+
# Wraps any API object and prevents push() from sending transactions.
|
|
12
|
+
# All other methods are delegated to the wrapped API unchanged.
|
|
13
|
+
#
|
|
14
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
|
15
|
+
# Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
|
|
16
|
+
# License:: MIT
|
|
17
|
+
class Sibit::Dry
|
|
18
|
+
def initialize(api, log: Loog::NULL)
|
|
19
|
+
@api = api
|
|
20
|
+
@log = log
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
decoor(:api)
|
|
24
|
+
|
|
25
|
+
def push(_hex)
|
|
26
|
+
@log.debug('Transaction not pushed, dry mode is ON')
|
|
27
|
+
nil
|
|
28
|
+
end
|
|
29
|
+
end
|
data/lib/sibit/firstof.rb
CHANGED
|
@@ -88,7 +88,7 @@ class Sibit::FirstOf
|
|
|
88
88
|
done = false
|
|
89
89
|
result = nil
|
|
90
90
|
@list.each do |api|
|
|
91
|
-
@log.
|
|
91
|
+
@log.debug("Calling #{api.class.name}##{method}()...")
|
|
92
92
|
begin
|
|
93
93
|
result = yield api
|
|
94
94
|
done = true
|
|
@@ -97,11 +97,11 @@ class Sibit::FirstOf
|
|
|
97
97
|
# Just ignore it
|
|
98
98
|
rescue Sibit::Error => e
|
|
99
99
|
errors << e
|
|
100
|
-
@log.
|
|
100
|
+
@log.debug("The API #{api.class.name} failed at #{method}(): #{e.message}") if @verbose
|
|
101
101
|
end
|
|
102
102
|
end
|
|
103
103
|
unless done
|
|
104
|
-
errors.each { |e| @log.
|
|
104
|
+
errors.each { |e| @log.debug(Backtrace.new(e).to_s) }
|
|
105
105
|
raise Sibit::Error, "No APIs out of #{@list.length} managed to succeed at #{method}(): \
|
|
106
106
|
#{@list.map { |a| a.class.name }.join(', ')}"
|
|
107
107
|
end
|
data/lib/sibit/json.rb
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
# SPDX-License-Identifier: MIT
|
|
5
5
|
|
|
6
6
|
require 'cgi'
|
|
7
|
+
require 'elapsed'
|
|
7
8
|
require 'json'
|
|
8
9
|
require 'loog'
|
|
9
10
|
require 'uri'
|
|
@@ -30,52 +31,55 @@ class Sibit::Json
|
|
|
30
31
|
# This method will also log the process and will validate the
|
|
31
32
|
# response for correctness.
|
|
32
33
|
def get(address, headers: {}, accept: [200])
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
34
|
+
ret = nil
|
|
35
|
+
elapsed(@log) do
|
|
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
|
+
ret =
|
|
50
|
+
begin
|
|
51
|
+
JSON.parse(res.body)
|
|
52
|
+
rescue JSON::ParserError => e
|
|
53
|
+
raise Sibit::Error, "Can't parse JSON: #{e.message}"
|
|
54
|
+
end
|
|
55
|
+
throw :"GET #{uri}: #{res.code}/#{length(res.body.length)}"
|
|
46
56
|
end
|
|
47
|
-
|
|
48
|
-
JSON.parse(res.body)
|
|
49
|
-
rescue JSON::ParserError => e
|
|
50
|
-
raise Sibit::Error, "Can't parse JSON: #{e.message}"
|
|
57
|
+
ret
|
|
51
58
|
end
|
|
52
59
|
|
|
53
60
|
def post(address, body, headers: {})
|
|
54
|
-
start = Time.now
|
|
55
61
|
uri = URI(address.to_s)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
62
|
+
elapsed(@log) do
|
|
63
|
+
res = @http.client(uri).post(
|
|
64
|
+
"#{uri.path}?#{uri.query}",
|
|
65
|
+
"tx=#{CGI.escape(body)}",
|
|
66
|
+
{
|
|
67
|
+
'Accept' => 'text/plain',
|
|
68
|
+
'User-Agent' => user_agent,
|
|
69
|
+
'Accept-Charset' => 'UTF-8',
|
|
70
|
+
'Accept-Encoding' => '',
|
|
71
|
+
'Content-Type' => 'application/x-www-form-urlencoded'
|
|
72
|
+
}.merge(headers)
|
|
73
|
+
)
|
|
74
|
+
unless res.code == '200'
|
|
75
|
+
raise Sibit::Error, "Failed to post tx to #{uri}: #{res.code}\n#{res.body}"
|
|
76
|
+
end
|
|
77
|
+
throw :"POST #{uri}: #{res.code}"
|
|
69
78
|
end
|
|
70
|
-
@log.info("POST #{uri}: #{res.code} in #{age(start)}")
|
|
71
79
|
end
|
|
72
80
|
|
|
73
81
|
private
|
|
74
82
|
|
|
75
|
-
def age(start)
|
|
76
|
-
"#{((Time.now - start) * 1000).round}ms"
|
|
77
|
-
end
|
|
78
|
-
|
|
79
83
|
def length(bytes)
|
|
80
84
|
if bytes > 1024 * 1024
|
|
81
85
|
"#{bytes / (1024 * 1024)}mb"
|
data/lib/sibit/version.rb
CHANGED
data/lib/sibit.rb
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
# SPDX-License-Identifier: MIT
|
|
5
5
|
|
|
6
6
|
require 'loog'
|
|
7
|
-
require_relative 'sibit/
|
|
8
|
-
require_relative 'sibit/
|
|
9
|
-
require_relative 'sibit/
|
|
10
|
-
require_relative 'sibit/
|
|
11
|
-
require_relative 'sibit/
|
|
7
|
+
require_relative 'sibit/base58'
|
|
8
|
+
require_relative 'sibit/key'
|
|
9
|
+
require_relative 'sibit/script'
|
|
10
|
+
require_relative 'sibit/tx'
|
|
11
|
+
require_relative 'sibit/txbuilder'
|
|
12
12
|
require_relative 'sibit/blockchain'
|
|
13
13
|
require_relative 'sibit/version'
|
|
14
14
|
|
|
@@ -49,7 +49,7 @@ class Sibit
|
|
|
49
49
|
# Generates new Bitcoin private key and returns in Hash160 format.
|
|
50
50
|
def generate
|
|
51
51
|
key = Key.generate.priv
|
|
52
|
-
@log.
|
|
52
|
+
@log.debug("Bitcoin private key generated: #{key[0..8]}...")
|
|
53
53
|
key
|
|
54
54
|
end
|
|
55
55
|
|
|
@@ -100,17 +100,17 @@ class Sibit
|
|
|
100
100
|
# +change+: the address where the change has to be sent to
|
|
101
101
|
def pay(amount, fee, sources, target, change, skip_utxo: [])
|
|
102
102
|
p = price('USD')
|
|
103
|
-
sources = sources.
|
|
103
|
+
sources = sources.to_h { |k| [Key.new(k).addr, k] }
|
|
104
104
|
satoshi = satoshi(amount)
|
|
105
105
|
builder = TxBuilder.new
|
|
106
106
|
unspent = 0
|
|
107
107
|
size = 100
|
|
108
108
|
utxos = @api.utxos(sources.keys)
|
|
109
|
-
@log.
|
|
109
|
+
@log.debug("#{utxos.count} UTXOs found, these will be used \
|
|
110
110
|
(value/confirmations at tx_hash):")
|
|
111
111
|
utxos.each do |utxo|
|
|
112
112
|
if skip_utxo.include?(utxo[:hash])
|
|
113
|
-
@log.
|
|
113
|
+
@log.debug("UTXO skipped: #{utxo[:hash]}")
|
|
114
114
|
next
|
|
115
115
|
end
|
|
116
116
|
unspent += utxo[:value]
|
|
@@ -124,7 +124,7 @@ class Sibit
|
|
|
124
124
|
i.signature_key(key(k))
|
|
125
125
|
end
|
|
126
126
|
size += 180
|
|
127
|
-
@log.
|
|
127
|
+
@log.debug(
|
|
128
128
|
" #{num(utxo[:value], p)}/#{utxo[:confirmations]} at #{utxo[:hash]}"
|
|
129
129
|
)
|
|
130
130
|
break if unspent > satoshi
|
|
@@ -132,11 +132,14 @@ class Sibit
|
|
|
132
132
|
if unspent < satoshi
|
|
133
133
|
raise Error, "Not enough funds to send #{num(satoshi, p)}, only #{num(unspent, p)} left"
|
|
134
134
|
end
|
|
135
|
-
builder.output(satoshi, target)
|
|
136
135
|
f = mfee(fee, size)
|
|
137
|
-
|
|
136
|
+
if f.negative?
|
|
137
|
+
satoshi += f
|
|
138
|
+
f = -f
|
|
139
|
+
end
|
|
138
140
|
raise Error, "The fee #{f.abs} covers the entire amount" if satoshi.zero?
|
|
139
141
|
raise Error, "The fee #{f.abs} is bigger than the amount #{satoshi}" if satoshi.negative?
|
|
142
|
+
builder.output(satoshi, target)
|
|
140
143
|
tx = builder.tx(
|
|
141
144
|
input_value: unspent,
|
|
142
145
|
leave_fee: true,
|
|
@@ -144,7 +147,7 @@ class Sibit
|
|
|
144
147
|
change_address: change
|
|
145
148
|
)
|
|
146
149
|
left = unspent - tx.outputs.sum(&:value)
|
|
147
|
-
@log.
|
|
150
|
+
@log.debug("A new Bitcoin transaction #{tx.hash} prepared:
|
|
148
151
|
#{tx.in.count} input#{'s' if tx.in.count > 1}:
|
|
149
152
|
#{tx.inputs.map { |i| " in: #{i.prev_out.unpack1('H*')}:#{i.prev_out_index}" }.join("\n ")}
|
|
150
153
|
#{tx.out.count} output#{'s' if tx.out.count > 1}:
|
|
@@ -186,10 +189,10 @@ class Sibit
|
|
|
186
189
|
json = @api.block(block)
|
|
187
190
|
if json[:orphan]
|
|
188
191
|
steps = 4
|
|
189
|
-
@log.
|
|
192
|
+
@log.debug("Orphan block found at #{block}, moving #{steps} steps back...")
|
|
190
193
|
steps.times do
|
|
191
194
|
block = json[:previous]
|
|
192
|
-
@log.
|
|
195
|
+
@log.debug("Moved back to #{block}")
|
|
193
196
|
json = @api.block(block)
|
|
194
197
|
end
|
|
195
198
|
next
|
|
@@ -203,34 +206,34 @@ class Sibit
|
|
|
203
206
|
hash = "#{t[:hash]}:#{i}"
|
|
204
207
|
satoshi = o[:value]
|
|
205
208
|
if yield(address, hash, satoshi)
|
|
206
|
-
@log.
|
|
209
|
+
@log.debug("Bitcoin tx found at #{hash} for #{satoshi} sent to #{address}")
|
|
207
210
|
end
|
|
208
211
|
end
|
|
209
212
|
checked += 1
|
|
210
213
|
end
|
|
211
214
|
count += 1
|
|
212
|
-
@log.
|
|
215
|
+
@log.debug("We checked #{checked} txns and #{checked_outputs} outputs \
|
|
213
216
|
in block #{block} (by #{json[:provider]})")
|
|
214
217
|
block = json[:next]
|
|
215
218
|
begin
|
|
216
219
|
if block.nil?
|
|
217
|
-
@log.
|
|
220
|
+
@log.debug("The next_block is empty in #{json[:hash]}, this may be the end...")
|
|
218
221
|
block = @api.next_of(json[:hash])
|
|
219
222
|
end
|
|
220
223
|
rescue Sibit::Error => e
|
|
221
|
-
@log.
|
|
224
|
+
@log.debug("Failed to get the next_of(#{json[:hash]}), quitting: #{e.message}")
|
|
222
225
|
break
|
|
223
226
|
end
|
|
224
227
|
if block.nil?
|
|
225
|
-
@log.
|
|
228
|
+
@log.debug("The block #{json[:hash]} is definitely the end of Blockchain, we stop.")
|
|
226
229
|
break
|
|
227
230
|
end
|
|
228
231
|
if count >= max
|
|
229
|
-
@log.
|
|
232
|
+
@log.debug("Too many blocks (#{count}) in one go, let's get back to it next time")
|
|
230
233
|
break
|
|
231
234
|
end
|
|
232
235
|
end
|
|
233
|
-
@log.
|
|
236
|
+
@log.debug("Scanned from #{start} to #{json[:hash]} (#{count} blocks)")
|
|
234
237
|
json[:hash]
|
|
235
238
|
end
|
|
236
239
|
|
|
@@ -256,7 +259,7 @@ in block #{block} (by #{json[:provider]})")
|
|
|
256
259
|
|
|
257
260
|
# Calculates a fee in satoshi for the transaction of the specified size.
|
|
258
261
|
# The +fee+ argument could be a number in satoshi, in which case it will
|
|
259
|
-
# be returned as is, or a string like "XL" or "S", in which case the
|
|
262
|
+
# be returned as is, or a string like "XL" or "S-", in which case the
|
|
260
263
|
# fee will be calculated using the +size+ argument (which is the size
|
|
261
264
|
# of the transaction in bytes).
|
|
262
265
|
def mfee(fee, size)
|
|
@@ -269,7 +272,9 @@ in block #{block} (by #{json[:provider]})")
|
|
|
269
272
|
end
|
|
270
273
|
sat = fees[fee.to_sym]
|
|
271
274
|
raise Error, "Can't understand the fee: #{fee.inspect}" if sat.nil?
|
|
272
|
-
mul * sat * size
|
|
275
|
+
f = mul * sat * size
|
|
276
|
+
@log.debug("Fee calculated as #{mul} * #{sat} * #{size} = #{f}")
|
|
277
|
+
f
|
|
273
278
|
end
|
|
274
279
|
|
|
275
280
|
# Make key from private key string in Hash160.
|
data/sibit.gemspec
CHANGED
|
@@ -12,7 +12,7 @@ Gem::Specification.new do |s|
|
|
|
12
12
|
if s.respond_to? :required_rubygems_version=
|
|
13
13
|
s.required_rubygems_version = Gem::Requirement.new('>= 0')
|
|
14
14
|
end
|
|
15
|
-
s.required_ruby_version = '>=
|
|
15
|
+
s.required_ruby_version = '>= 3.0'
|
|
16
16
|
s.name = 'sibit'
|
|
17
17
|
s.version = Sibit::VERSION
|
|
18
18
|
s.license = 'MIT'
|
|
@@ -30,10 +30,12 @@ Gem::Specification.new do |s|
|
|
|
30
30
|
s.rdoc_options = ['--charset=UTF-8']
|
|
31
31
|
s.extra_rdoc_files = ['README.md', 'LICENSE.txt']
|
|
32
32
|
s.add_dependency 'backtrace', '~> 0.3'
|
|
33
|
+
s.add_dependency 'decoor', '~> 0.1'
|
|
34
|
+
s.add_dependency 'elapsed', '~> 0.2'
|
|
33
35
|
s.add_dependency 'iri', '~> 0.5'
|
|
34
|
-
s.add_dependency 'json', '~> 2'
|
|
36
|
+
s.add_dependency 'json', '~> 2.18'
|
|
35
37
|
s.add_dependency 'loog', '~> 0.6'
|
|
36
|
-
s.add_dependency 'openssl', '
|
|
38
|
+
s.add_dependency 'openssl', '~> 3.0'
|
|
37
39
|
s.add_dependency 'retriable_proxy', '~> 1.0'
|
|
38
40
|
s.add_dependency 'slop', '~> 4.6'
|
|
39
41
|
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.
|
|
4
|
+
version: 0.30.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yegor Bugayenko
|
|
@@ -23,6 +23,34 @@ dependencies:
|
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '0.3'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: decoor
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0.1'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0.1'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: elapsed
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0.2'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0.2'
|
|
26
54
|
- !ruby/object:Gem::Dependency
|
|
27
55
|
name: iri
|
|
28
56
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -43,14 +71,14 @@ dependencies:
|
|
|
43
71
|
requirements:
|
|
44
72
|
- - "~>"
|
|
45
73
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '2'
|
|
74
|
+
version: '2.18'
|
|
47
75
|
type: :runtime
|
|
48
76
|
prerelease: false
|
|
49
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
78
|
requirements:
|
|
51
79
|
- - "~>"
|
|
52
80
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '2'
|
|
81
|
+
version: '2.18'
|
|
54
82
|
- !ruby/object:Gem::Dependency
|
|
55
83
|
name: loog
|
|
56
84
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -69,16 +97,16 @@ dependencies:
|
|
|
69
97
|
name: openssl
|
|
70
98
|
requirement: !ruby/object:Gem::Requirement
|
|
71
99
|
requirements:
|
|
72
|
-
- - "
|
|
100
|
+
- - "~>"
|
|
73
101
|
- !ruby/object:Gem::Version
|
|
74
|
-
version: '
|
|
102
|
+
version: '3.0'
|
|
75
103
|
type: :runtime
|
|
76
104
|
prerelease: false
|
|
77
105
|
version_requirements: !ruby/object:Gem::Requirement
|
|
78
106
|
requirements:
|
|
79
|
-
- - "
|
|
107
|
+
- - "~>"
|
|
80
108
|
- !ruby/object:Gem::Version
|
|
81
|
-
version: '
|
|
109
|
+
version: '3.0'
|
|
82
110
|
- !ruby/object:Gem::Dependency
|
|
83
111
|
name: retriable_proxy
|
|
84
112
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -133,24 +161,25 @@ files:
|
|
|
133
161
|
- features/step_definitions/steps.rb
|
|
134
162
|
- features/support/env.rb
|
|
135
163
|
- lib/sibit.rb
|
|
164
|
+
- lib/sibit/base58.rb
|
|
136
165
|
- lib/sibit/bestof.rb
|
|
137
|
-
- lib/sibit/bitcoin/base58.rb
|
|
138
|
-
- lib/sibit/bitcoin/key.rb
|
|
139
|
-
- lib/sibit/bitcoin/script.rb
|
|
140
|
-
- lib/sibit/bitcoin/tx.rb
|
|
141
|
-
- lib/sibit/bitcoin/txbuilder.rb
|
|
142
166
|
- lib/sibit/bitcoinchain.rb
|
|
143
167
|
- lib/sibit/blockchain.rb
|
|
144
168
|
- lib/sibit/blockchair.rb
|
|
145
169
|
- lib/sibit/btc.rb
|
|
146
170
|
- lib/sibit/cex.rb
|
|
147
171
|
- lib/sibit/cryptoapis.rb
|
|
172
|
+
- lib/sibit/dry.rb
|
|
148
173
|
- lib/sibit/error.rb
|
|
149
174
|
- lib/sibit/fake.rb
|
|
150
175
|
- lib/sibit/firstof.rb
|
|
151
176
|
- lib/sibit/http.rb
|
|
152
177
|
- lib/sibit/httpproxy.rb
|
|
153
178
|
- lib/sibit/json.rb
|
|
179
|
+
- lib/sibit/key.rb
|
|
180
|
+
- lib/sibit/script.rb
|
|
181
|
+
- lib/sibit/tx.rb
|
|
182
|
+
- lib/sibit/txbuilder.rb
|
|
154
183
|
- lib/sibit/version.rb
|
|
155
184
|
- logo.svg
|
|
156
185
|
- sibit.gemspec
|
|
@@ -166,7 +195,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
166
195
|
requirements:
|
|
167
196
|
- - ">="
|
|
168
197
|
- !ruby/object:Gem::Version
|
|
169
|
-
version: '
|
|
198
|
+
version: '3.0'
|
|
170
199
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
171
200
|
requirements:
|
|
172
201
|
- - ">="
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|