sibit 0.28.0 → 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 +4 -4
- data/Gemfile.lock +3 -3
- data/README.md +14 -17
- data/bin/sibit +0 -4
- data/lib/sibit/bestof.rb +68 -71
- data/lib/sibit/bitcoin/base58.rb +33 -35
- data/lib/sibit/bitcoin/key.rb +64 -66
- data/lib/sibit/bitcoin/script.rb +45 -47
- data/lib/sibit/bitcoin/tx.rb +162 -164
- data/lib/sibit/bitcoin/txbuilder.rb +1 -1
- data/lib/sibit/bitcoinchain.rb +93 -96
- data/lib/sibit/blockchain.rb +115 -118
- data/lib/sibit/blockchair.rb +62 -65
- data/lib/sibit/btc.rb +147 -150
- data/lib/sibit/cex.rb +49 -50
- data/lib/sibit/cryptoapis.rb +113 -116
- data/lib/sibit/fake.rb +42 -47
- data/lib/sibit/firstof.rb +73 -76
- data/lib/sibit/http.rb +17 -20
- data/lib/sibit/json.rb +63 -66
- data/lib/sibit/version.rb +1 -1
- data/lib/sibit.rb +7 -9
- metadata +1 -1
data/lib/sibit/blockchain.rb
CHANGED
|
@@ -20,135 +20,132 @@ require_relative 'version'
|
|
|
20
20
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
|
21
21
|
# Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
|
|
22
22
|
# License:: MIT
|
|
23
|
-
class Sibit
|
|
24
|
-
#
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
@dry = dry
|
|
31
|
-
end
|
|
23
|
+
class Sibit::Blockchain
|
|
24
|
+
# Constructor.
|
|
25
|
+
def initialize(log: Loog::NULL, http: Sibit::Http.new, dry: false)
|
|
26
|
+
@http = http
|
|
27
|
+
@log = log
|
|
28
|
+
@dry = dry
|
|
29
|
+
end
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
31
|
+
# Current price of BTC in USD (float returned).
|
|
32
|
+
def price(currency = 'USD')
|
|
33
|
+
h = Sibit::Json.new(http: @http, log: @log).get(
|
|
34
|
+
Iri.new('https://blockchain.info/ticker')
|
|
35
|
+
)[currency]
|
|
36
|
+
raise Error, "Unrecognized currency #{currency}" if h.nil?
|
|
37
|
+
price = h['15m']
|
|
38
|
+
@log.info("The price of BTC is #{price} USD")
|
|
39
|
+
price
|
|
40
|
+
end
|
|
43
41
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
42
|
+
# Get hash of the block after this one.
|
|
43
|
+
def next_of(_hash)
|
|
44
|
+
raise Sibit::NotSupportedError, 'next_of() in Blockchain API is broken, always returns NULL'
|
|
45
|
+
# json = Sibit::Json.new(http: @http, log: @log).get(
|
|
46
|
+
# Iri.new('https://blockchain.info/rawblock').append(hash)
|
|
47
|
+
# )
|
|
48
|
+
# nxt = json['next_block'][0]
|
|
49
|
+
# if nxt.nil?
|
|
50
|
+
# @log.info("There is no block after #{hash}")
|
|
51
|
+
# else
|
|
52
|
+
# @log.info("The next block of #{hash} is #{nxt}")
|
|
53
|
+
# end
|
|
54
|
+
# nxt
|
|
55
|
+
end
|
|
58
56
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
57
|
+
# The height of the block.
|
|
58
|
+
def height(hash)
|
|
59
|
+
json = Sibit::Json.new(http: @http, log: @log).get(
|
|
60
|
+
Iri.new('https://blockchain.info/rawblock').append(hash)
|
|
61
|
+
)
|
|
62
|
+
h = json['height']
|
|
63
|
+
@log.info("The height of #{hash} is #{h}")
|
|
64
|
+
h
|
|
65
|
+
end
|
|
68
66
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
67
|
+
# Gets the balance of the address, in satoshi.
|
|
68
|
+
def balance(address)
|
|
69
|
+
json = Sibit::Json.new(http: @http, log: @log).get(
|
|
70
|
+
Iri.new('https://blockchain.info/rawaddr').append(address).add(limit: 0),
|
|
71
|
+
accept: [200, 500]
|
|
72
|
+
)
|
|
73
|
+
b = json['final_balance']
|
|
74
|
+
@log.info("The balance of #{address} is #{b} satoshi (#{json['n_tx']} txns)")
|
|
75
|
+
b
|
|
76
|
+
end
|
|
79
77
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
78
|
+
# Get recommended fees.
|
|
79
|
+
def fees
|
|
80
|
+
json = Sibit::Json.new(http: @http, log: @log).get(
|
|
81
|
+
Iri.new('https://api.blockchain.info/mempool/fees')
|
|
82
|
+
)
|
|
83
|
+
@log.info("Current recommended Bitcoin fees: \
|
|
84
|
+
#{json['regular']}/#{json['priority']}/#{json['limits']['max']} sat/byte")
|
|
85
|
+
{
|
|
86
|
+
S: json['regular'] / 3,
|
|
87
|
+
M: json['regular'],
|
|
88
|
+
L: json['priority'],
|
|
89
|
+
XL: json['limits']['max']
|
|
90
|
+
}
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Fetch all unspent outputs per address. The argument is an array
|
|
94
|
+
# of Bitcoin addresses.
|
|
95
|
+
def utxos(sources)
|
|
96
|
+
Sibit::Json.new(http: @http, log: @log).get(
|
|
97
|
+
Iri.new('https://blockchain.info/unspent').add(active: sources.join('|'), limit: 1000)
|
|
98
|
+
)['unspent_outputs'].map do |u|
|
|
87
99
|
{
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
100
|
+
value: u['value'],
|
|
101
|
+
hash: u['tx_hash_big_endian'],
|
|
102
|
+
index: u['tx_output_n'],
|
|
103
|
+
confirmations: u['confirmations'],
|
|
104
|
+
script: [u['script']].pack('H*')
|
|
92
105
|
}
|
|
93
106
|
end
|
|
107
|
+
end
|
|
94
108
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
109
|
+
# Push this transaction (in hex format) to the network.
|
|
110
|
+
def push(hex)
|
|
111
|
+
return if @dry
|
|
112
|
+
Sibit::Json.new(http: @http, log: @log).post(
|
|
113
|
+
Iri.new('https://blockchain.info/pushtx'),
|
|
114
|
+
hex
|
|
115
|
+
)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Gets the hash of the latest block.
|
|
119
|
+
def latest
|
|
120
|
+
hash = Sibit::Json.new(http: @http, log: @log).get(
|
|
121
|
+
Iri.new('https://blockchain.info/latestblock')
|
|
122
|
+
)['hash']
|
|
123
|
+
@log.info("The latest block hash is #{hash}")
|
|
124
|
+
hash
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# This method should fetch a Blockchain block and return as a hash.
|
|
128
|
+
def block(hash)
|
|
129
|
+
json = Sibit::Json.new(http: @http, log: @log).get(
|
|
130
|
+
Iri.new('https://blockchain.info/rawblock').append(hash)
|
|
131
|
+
)
|
|
132
|
+
{
|
|
133
|
+
provider: self.class.name,
|
|
134
|
+
hash: json['hash'],
|
|
135
|
+
orphan: !json['main_chain'],
|
|
136
|
+
next: json['next_block'][0],
|
|
137
|
+
previous: json['prev_block'],
|
|
138
|
+
txns: json['tx'].map do |t|
|
|
101
139
|
{
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
140
|
+
hash: t['hash'],
|
|
141
|
+
outputs: t['out'].map do |o|
|
|
142
|
+
{
|
|
143
|
+
address: o['addr'],
|
|
144
|
+
value: o['value']
|
|
145
|
+
}
|
|
146
|
+
end
|
|
107
147
|
}
|
|
108
148
|
end
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
# Push this transaction (in hex format) to the network.
|
|
112
|
-
def push(hex)
|
|
113
|
-
return if @dry
|
|
114
|
-
Sibit::Json.new(http: @http, log: @log).post(
|
|
115
|
-
Iri.new('https://blockchain.info/pushtx'),
|
|
116
|
-
hex
|
|
117
|
-
)
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
# Gets the hash of the latest block.
|
|
121
|
-
def latest
|
|
122
|
-
hash = Sibit::Json.new(http: @http, log: @log).get(
|
|
123
|
-
Iri.new('https://blockchain.info/latestblock')
|
|
124
|
-
)['hash']
|
|
125
|
-
@log.info("The latest block hash is #{hash}")
|
|
126
|
-
hash
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
# This method should fetch a Blockchain block and return as a hash.
|
|
130
|
-
def block(hash)
|
|
131
|
-
json = Sibit::Json.new(http: @http, log: @log).get(
|
|
132
|
-
Iri.new('https://blockchain.info/rawblock').append(hash)
|
|
133
|
-
)
|
|
134
|
-
{
|
|
135
|
-
provider: self.class.name,
|
|
136
|
-
hash: json['hash'],
|
|
137
|
-
orphan: !json['main_chain'],
|
|
138
|
-
next: json['next_block'][0],
|
|
139
|
-
previous: json['prev_block'],
|
|
140
|
-
txns: json['tx'].map do |t|
|
|
141
|
-
{
|
|
142
|
-
hash: t['hash'],
|
|
143
|
-
outputs: t['out'].map do |o|
|
|
144
|
-
{
|
|
145
|
-
address: o['addr'],
|
|
146
|
-
value: o['value']
|
|
147
|
-
}
|
|
148
|
-
end
|
|
149
|
-
}
|
|
150
|
-
end
|
|
151
|
-
}
|
|
152
|
-
end
|
|
149
|
+
}
|
|
153
150
|
end
|
|
154
151
|
end
|
data/lib/sibit/blockchair.rb
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
require 'cgi'
|
|
7
7
|
require 'iri'
|
|
8
8
|
require 'json'
|
|
9
|
+
require 'loog'
|
|
9
10
|
require 'uri'
|
|
10
11
|
require_relative 'error'
|
|
11
12
|
require_relative 'http'
|
|
12
|
-
require 'loog'
|
|
13
13
|
require_relative 'json'
|
|
14
14
|
require_relative 'version'
|
|
15
15
|
|
|
@@ -18,81 +18,78 @@ require_relative 'version'
|
|
|
18
18
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
|
19
19
|
# Copyright:: Copyright (c) 2019-2025 Yegor Bugayenko
|
|
20
20
|
# License:: MIT
|
|
21
|
-
class Sibit
|
|
22
|
-
#
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@dry = dry
|
|
30
|
-
end
|
|
21
|
+
class Sibit::Blockchair
|
|
22
|
+
# Constructor.
|
|
23
|
+
def initialize(key: nil, log: Loog::NULL, http: Sibit::Http.new, dry: false)
|
|
24
|
+
@key = key
|
|
25
|
+
@http = http
|
|
26
|
+
@log = log
|
|
27
|
+
@dry = dry
|
|
28
|
+
end
|
|
31
29
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
# Current price of BTC in USD (float returned).
|
|
31
|
+
def price(_currency = 'USD')
|
|
32
|
+
raise Sibit::NotSupportedError, 'Blockchair doesn\'t provide BTC price'
|
|
33
|
+
end
|
|
36
34
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
# The height of the block.
|
|
36
|
+
def height(_hash)
|
|
37
|
+
raise Sibit::NotSupportedError, 'Blockchair API doesn\'t provide height()'
|
|
38
|
+
end
|
|
41
39
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
# Get hash of the block after this one.
|
|
41
|
+
def next_of(_hash)
|
|
42
|
+
# They don't provide next block hash
|
|
43
|
+
raise Sibit::NotSupportedError, 'Blockchair API doesn\'t provide next_of()'
|
|
44
|
+
end
|
|
47
45
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
end
|
|
57
|
-
a = json['address']
|
|
58
|
-
b = a['balance']
|
|
59
|
-
@log.info("The balance of #{address} is #{b} satoshi")
|
|
60
|
-
b
|
|
46
|
+
# Gets the balance of the address, in satoshi.
|
|
47
|
+
def balance(address)
|
|
48
|
+
json = Sibit::Json.new(http: @http, log: @log).get(
|
|
49
|
+
Iri.new('https://api.blockchair.com/bitcoin/dashboards/address').append(address).fragment(the_key)
|
|
50
|
+
)['data'][address]
|
|
51
|
+
if json.nil?
|
|
52
|
+
@log.info("Address #{address} not found")
|
|
53
|
+
return 0
|
|
61
54
|
end
|
|
55
|
+
a = json['address']
|
|
56
|
+
b = a['balance']
|
|
57
|
+
@log.info("The balance of #{address} is #{b} satoshi")
|
|
58
|
+
b
|
|
59
|
+
end
|
|
62
60
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
61
|
+
# Get recommended fees, in satoshi per byte.
|
|
62
|
+
def fees
|
|
63
|
+
raise Sibit::NotSupportedError, 'Blockchair doesn\'t implement fees()'
|
|
64
|
+
end
|
|
67
65
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
# Gets the hash of the latest block.
|
|
67
|
+
def latest
|
|
68
|
+
raise Sibit::NotSupportedError, 'Blockchair doesn\'t implement latest()'
|
|
69
|
+
end
|
|
72
70
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
71
|
+
# Fetch all unspent outputs per address.
|
|
72
|
+
def utxos(_sources)
|
|
73
|
+
raise Sibit::NotSupportedError, 'Blockchair doesn\'t implement utxos()'
|
|
74
|
+
end
|
|
77
75
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
76
|
+
# Push this transaction (in hex format) to the network.
|
|
77
|
+
def push(hex)
|
|
78
|
+
Sibit::Json.new(http: @http, log: @log).post(
|
|
79
|
+
Iri.new('https://api.blockchair.com/bitcoin/push/transaction').fragment(the_key),
|
|
80
|
+
"data=#{hex}"
|
|
81
|
+
)
|
|
82
|
+
@log.info("Transaction (#{hex.length} in hex) has been pushed to Blockchair")
|
|
83
|
+
end
|
|
86
84
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
85
|
+
# This method should fetch a Blockchain block and return as a hash.
|
|
86
|
+
def block(_hash)
|
|
87
|
+
raise Sibit::NotSupportedError, 'Blockchair doesn\'t implement block()'
|
|
88
|
+
end
|
|
91
89
|
|
|
92
|
-
|
|
90
|
+
private
|
|
93
91
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
end
|
|
92
|
+
def the_key
|
|
93
|
+
@key.nil? ? '' : "key=#{CGI.escape(@key)}"
|
|
97
94
|
end
|
|
98
95
|
end
|