bitcoinrb 1.12.0 → 1.12.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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +1 -1
- data/bitcoinrb.gemspec +1 -6
- data/lib/bitcoin/bip324/fs_chacha20.rb +3 -5
- data/lib/bitcoin/bip324/fs_chacha_poly1305.rb +19 -17
- data/lib/bitcoin/bip324.rb +1 -1
- data/lib/bitcoin/descriptor/expression.rb +2 -2
- data/lib/bitcoin/descriptor/sp.rb +219 -0
- data/lib/bitcoin/descriptor.rb +20 -0
- data/lib/bitcoin/ext_key.rb +3 -3
- data/lib/bitcoin/message/ping.rb +4 -2
- data/lib/bitcoin/message/version.rb +1 -1
- data/lib/bitcoin/message_sign.rb +98 -38
- data/lib/bitcoin/rpc.rb +0 -2
- data/lib/bitcoin/silent_payment/output.rb +46 -0
- data/lib/bitcoin/silent_payment.rb +113 -12
- data/lib/bitcoin/util.rb +2 -2
- data/lib/bitcoin/version.rb +1 -1
- data/lib/bitcoin.rb +0 -6
- metadata +18 -108
- data/exe/bitcoinrb-cli +0 -5
- data/exe/bitcoinrbd +0 -46
- data/lib/bitcoin/network/connection.rb +0 -73
- data/lib/bitcoin/network/message_handler.rb +0 -243
- data/lib/bitcoin/network/peer.rb +0 -228
- data/lib/bitcoin/network/peer_discovery.rb +0 -42
- data/lib/bitcoin/network/pool.rb +0 -135
- data/lib/bitcoin/network.rb +0 -13
- data/lib/bitcoin/node/cli.rb +0 -122
- data/lib/bitcoin/node/configuration.rb +0 -40
- data/lib/bitcoin/node/spv.rb +0 -87
- data/lib/bitcoin/node.rb +0 -7
- data/lib/bitcoin/rpc/http_server.rb +0 -84
- data/lib/bitcoin/rpc/request_handler.rb +0 -150
- data/lib/bitcoin/store/chain_entry.rb +0 -68
- data/lib/bitcoin/store/db/level_db.rb +0 -98
- data/lib/bitcoin/store/db.rb +0 -9
- data/lib/bitcoin/store/spv_chain.rb +0 -101
- data/lib/bitcoin/store/utxo_db.rb +0 -226
- data/lib/bitcoin/store.rb +0 -12
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
module Bitcoin
|
|
2
|
-
|
|
3
|
-
module Store
|
|
4
|
-
|
|
5
|
-
KEY_PREFIX = {
|
|
6
|
-
entry: 'e', # key: block hash, value: Bitcoin::Store::ChainEntry payload
|
|
7
|
-
height: 'h', # key: block height, value: block hash.
|
|
8
|
-
best: 'B', # value: best block hash.
|
|
9
|
-
next: 'n' # key: block hash, value: A hash of the next block of the specified hash
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
class SPVChain
|
|
13
|
-
|
|
14
|
-
attr_reader :db
|
|
15
|
-
attr_reader :logger
|
|
16
|
-
|
|
17
|
-
def initialize(db = Bitcoin::Store::DB::LevelDB.new)
|
|
18
|
-
@db = db # TODO multiple db switch
|
|
19
|
-
@logger = Bitcoin::Logger.create(:debug)
|
|
20
|
-
initialize_block
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# get latest block in the store.
|
|
24
|
-
# @return[Bitcoin::Store::ChainEntry]
|
|
25
|
-
def latest_block
|
|
26
|
-
hash = db.best_hash
|
|
27
|
-
return nil unless hash
|
|
28
|
-
find_entry_by_hash(hash)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
# find block entry with the specified height.
|
|
32
|
-
def find_entry_by_height(height)
|
|
33
|
-
find_entry_by_hash(db.get_hash_from_height(height))
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# find block entry with the specified hash
|
|
37
|
-
def find_entry_by_hash(hash)
|
|
38
|
-
payload = db.get_entry_payload_from_hash(hash)
|
|
39
|
-
return nil unless payload
|
|
40
|
-
ChainEntry.parse_from_payload(payload)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# append block header to chain.
|
|
44
|
-
# @param [Bitcoin::BlockHeader] header a block header.
|
|
45
|
-
# @return [Bitcoin::Store::ChainEntry] appended block header entry.
|
|
46
|
-
def append_header(header)
|
|
47
|
-
logger.info("append header #{header.block_id}")
|
|
48
|
-
raise "this header is invalid. #{header.block_hash}" unless header.valid?
|
|
49
|
-
best_block = latest_block
|
|
50
|
-
current_height = best_block.height
|
|
51
|
-
if best_block.block_hash == header.prev_hash
|
|
52
|
-
entry = Bitcoin::Store::ChainEntry.new(header, current_height + 1)
|
|
53
|
-
db.save_entry(entry)
|
|
54
|
-
entry
|
|
55
|
-
else
|
|
56
|
-
unless find_entry_by_hash(header.block_hash)
|
|
57
|
-
# TODO implements recovery process
|
|
58
|
-
raise "header's previous hash(#{header.prev_hash}) does not match current best block's(#{best_block.block_hash})."
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
# get next block hash for specified +hash+
|
|
64
|
-
# @param [String] hash the block hash(little endian)
|
|
65
|
-
# @return [String] the next block hash. If it does not exist yet, return nil.
|
|
66
|
-
def next_hash(hash)
|
|
67
|
-
db.next_hash(hash)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# get median time past for specified block +hash+
|
|
71
|
-
# @param [String] hash the block hash.
|
|
72
|
-
# @return [Integer] the median time past value.
|
|
73
|
-
def mtp(hash)
|
|
74
|
-
time = []
|
|
75
|
-
Bitcoin::MEDIAN_TIME_SPAN.times do
|
|
76
|
-
entry = find_entry_by_hash(hash)
|
|
77
|
-
break unless entry
|
|
78
|
-
|
|
79
|
-
time << entry.header.time
|
|
80
|
-
hash = entry.header.prev_hash
|
|
81
|
-
end
|
|
82
|
-
time.sort!
|
|
83
|
-
time[time.size / 2]
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
private
|
|
87
|
-
|
|
88
|
-
# if database is empty, put genesis block.
|
|
89
|
-
def initialize_block
|
|
90
|
-
unless latest_block
|
|
91
|
-
block = Bitcoin.chain_params.genesis_block
|
|
92
|
-
genesis = ChainEntry.new(block.header, 0)
|
|
93
|
-
db.save_entry(genesis)
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
end
|
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
require 'leveldb-native'
|
|
2
|
-
|
|
3
|
-
module Bitcoin
|
|
4
|
-
module Store
|
|
5
|
-
class UtxoDB
|
|
6
|
-
|
|
7
|
-
KEY_PREFIX = {
|
|
8
|
-
out_point: 'o', # key: out_point(tx_hash and index), value: Utxo
|
|
9
|
-
script: 's', # key: script_pubkey and out_point(tx_hash and index), value: Utxo
|
|
10
|
-
height: 'h', # key: block_height and out_point, value: Utxo
|
|
11
|
-
tx_hash: 't', # key: tx_hash of transaction, value: [block_height, tx_index]
|
|
12
|
-
block: 'b', # key: block_height and tx_index, value: tx_hash
|
|
13
|
-
tx_payload: 'p', # key: tx_hash, value: Tx
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
attr_reader :level_db, :logger
|
|
17
|
-
|
|
18
|
-
def initialize(path = "#{Bitcoin.base_dir}/db/utxo")
|
|
19
|
-
FileUtils.mkdir_p(path)
|
|
20
|
-
@level_db = ::LevelDBNative::DB.new(path)
|
|
21
|
-
@logger = Bitcoin::Logger.create(:debug)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def close
|
|
25
|
-
level_db.close
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Save payload of a transaction into db
|
|
29
|
-
#
|
|
30
|
-
# @param [String] tx_hash
|
|
31
|
-
# @param [String] tx_payload
|
|
32
|
-
def save_tx(tx_hash, tx_payload)
|
|
33
|
-
logger.info("UtxoDB#save_tx:#{[tx_hash, tx_payload]}")
|
|
34
|
-
level_db.batch do
|
|
35
|
-
# tx_hash -> [block_height, tx_index]
|
|
36
|
-
key = KEY_PREFIX[:tx_payload] + tx_hash
|
|
37
|
-
level_db.put(key, tx_payload)
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
# Save tx position (block height and index in the block) into db
|
|
42
|
-
# When node receives `header` message, node should call save_tx_position to store block height and its index.
|
|
43
|
-
#
|
|
44
|
-
# @param [String] tx_hash
|
|
45
|
-
# @param [Integer] block_height
|
|
46
|
-
# @param [Integer] tx_index
|
|
47
|
-
def save_tx_position(tx_hash, block_height, tx_index)
|
|
48
|
-
logger.info("UtxoDB#save_tx_position:#{[tx_hash, block_height, tx_index]}")
|
|
49
|
-
level_db.batch do
|
|
50
|
-
# tx_hash -> [block_height, tx_index]
|
|
51
|
-
key = KEY_PREFIX[:tx_hash] + tx_hash
|
|
52
|
-
level_db.put(key, [block_height, tx_index].pack('N2').bth)
|
|
53
|
-
|
|
54
|
-
# block_hash and tx_index -> tx_hash
|
|
55
|
-
key = KEY_PREFIX[:block] + [block_height, tx_index].pack('N2').bth
|
|
56
|
-
level_db.put(key, tx_hash)
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# Save utxo into db
|
|
61
|
-
#
|
|
62
|
-
# @param [Bitcoin::OutPoint] out_point
|
|
63
|
-
# @param [Double] value
|
|
64
|
-
# @param [Bitcoin::Script] script_pubkey
|
|
65
|
-
# @param [Integer] block_height
|
|
66
|
-
def save_utxo(out_point, value, script_pubkey, block_height=nil)
|
|
67
|
-
logger.info("UtxoDB#save_utxo:#{[out_point, value, script_pubkey, block_height]}")
|
|
68
|
-
level_db.batch do
|
|
69
|
-
utxo = Bitcoin::Wallet::Utxo.new(out_point.tx_hash, out_point.index, value, script_pubkey, block_height)
|
|
70
|
-
payload = utxo.to_payload
|
|
71
|
-
|
|
72
|
-
# out_point
|
|
73
|
-
key = KEY_PREFIX[:out_point] + out_point.to_hex
|
|
74
|
-
return if level_db.contains?(key)
|
|
75
|
-
level_db.put(key, payload)
|
|
76
|
-
|
|
77
|
-
# script_pubkey
|
|
78
|
-
if script_pubkey
|
|
79
|
-
key = KEY_PREFIX[:script] + script_pubkey.to_hex + out_point.to_hex
|
|
80
|
-
level_db.put(key, payload)
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
# height
|
|
84
|
-
unless block_height.nil?
|
|
85
|
-
key = KEY_PREFIX[:height] + [block_height].pack('N').bth + out_point.to_hex
|
|
86
|
-
level_db.put(key, payload)
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
utxo
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
# Get transaction stored via save_tx and save_tx_position
|
|
94
|
-
#
|
|
95
|
-
# @param [string] tx_hash
|
|
96
|
-
# @return [block_height, tx_index, tx_payload]
|
|
97
|
-
def get_tx(tx_hash)
|
|
98
|
-
key = KEY_PREFIX[:tx_hash] + tx_hash
|
|
99
|
-
return [] unless level_db.contains?(key)
|
|
100
|
-
block_height, tx_index = level_db.get(key).htb.unpack('N2')
|
|
101
|
-
key = KEY_PREFIX[:tx_payload] + tx_hash
|
|
102
|
-
tx_payload = level_db.get(key)
|
|
103
|
-
[block_height, tx_index, tx_payload]
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
# Delete utxo from db
|
|
107
|
-
#
|
|
108
|
-
# @param [Bitcoin::Outpoint] out_point
|
|
109
|
-
# @return [Bitcoin::Wallet::Utxo]
|
|
110
|
-
def delete_utxo(out_point)
|
|
111
|
-
level_db.batch do
|
|
112
|
-
# [:out_point]
|
|
113
|
-
key = KEY_PREFIX[:out_point] + out_point.to_hex
|
|
114
|
-
return unless level_db.contains?(key)
|
|
115
|
-
utxo = Bitcoin::Wallet::Utxo.parse_from_payload(level_db.get(key))
|
|
116
|
-
level_db.delete(key)
|
|
117
|
-
|
|
118
|
-
# [:script]
|
|
119
|
-
if utxo.script_pubkey
|
|
120
|
-
key = KEY_PREFIX[:script] + utxo.script_pubkey.to_hex + out_point.to_hex
|
|
121
|
-
level_db.delete(key)
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
if utxo.block_height
|
|
125
|
-
# [:height]
|
|
126
|
-
key = KEY_PREFIX[:height] + [utxo.block_height].pack('N').bth + out_point.to_hex
|
|
127
|
-
level_db.delete(key)
|
|
128
|
-
|
|
129
|
-
# [:block]
|
|
130
|
-
key = KEY_PREFIX[:block] + [utxo.block_height, utxo.index].pack('N2').bth
|
|
131
|
-
level_db.delete(key)
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
# handles both [:tx_hash] and [:tx_payload]
|
|
135
|
-
if utxo.tx_hash
|
|
136
|
-
key = KEY_PREFIX[:tx_hash] + utxo.tx_hash
|
|
137
|
-
level_db.delete(key)
|
|
138
|
-
|
|
139
|
-
key = KEY_PREFIX[:tx_payload] + utxo.tx_hash
|
|
140
|
-
level_db.delete(key)
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
utxo
|
|
144
|
-
end
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
# Get utxo of the specified out point
|
|
148
|
-
#
|
|
149
|
-
# @param [Bitcoin::Outpoint] out_point
|
|
150
|
-
# @return [Bitcoin::Wallet::Utxo]
|
|
151
|
-
def get_utxo(out_point)
|
|
152
|
-
level_db.batch do
|
|
153
|
-
key = KEY_PREFIX[:out_point] + out_point.to_hex
|
|
154
|
-
return unless level_db.contains?(key)
|
|
155
|
-
return Bitcoin::Wallet::Utxo.parse_from_payload(level_db.get(key))
|
|
156
|
-
end
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
# return [Bitcoin::Wallet::Utxo ...]
|
|
160
|
-
def list_unspent(current_block_height: 9999999, min: 0, max: 9999999, addresses: nil)
|
|
161
|
-
if addresses
|
|
162
|
-
list_unspent_by_addresses(current_block_height, min: min, max: max, addresses: addresses)
|
|
163
|
-
else
|
|
164
|
-
list_unspent_by_block_height(current_block_height, min: min, max: max)
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
# @param [Bitcoin::Wallet::Account]
|
|
169
|
-
# return [Bitcoin::Wallet::Utxo ...]
|
|
170
|
-
def list_unspent_in_account(account, current_block_height: 9999999, min: 0, max: 9999999)
|
|
171
|
-
return [] unless account
|
|
172
|
-
|
|
173
|
-
script_pubkeys = case account.purpose
|
|
174
|
-
when Bitcoin::Wallet::Account::PURPOSE_TYPE[:legacy]
|
|
175
|
-
account.watch_targets.map { |t| Bitcoin::Script.to_p2pkh(t).to_hex }
|
|
176
|
-
when Bitcoin::Wallet::Account::PURPOSE_TYPE[:nested_witness]
|
|
177
|
-
account.watch_targets.map { |t| Bitcoin::Script.to_p2wpkh(t).to_p2sh.to_hex }
|
|
178
|
-
when Bitcoin::Wallet::Account::PURPOSE_TYPE[:native_segwit]
|
|
179
|
-
account.watch_targets.map { |t| Bitcoin::Script.to_p2wpkh(t).to_hex }
|
|
180
|
-
end
|
|
181
|
-
list_unspent_by_script_pubkeys(current_block_height, min: min, max: max, script_pubkeys: script_pubkeys)
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
# @param [Bitcoin::Wallet::Account]
|
|
185
|
-
# return [Bitcoin::Wallet::Utxo ...]
|
|
186
|
-
def get_balance(account, current_block_height: 9999999, min: 0, max: 9999999)
|
|
187
|
-
list_unspent_in_account(account, current_block_height: current_block_height, min: min, max: max).sum { |u| u.value }
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
private
|
|
191
|
-
|
|
192
|
-
def utxos_between(from, to)
|
|
193
|
-
level_db.each(from: from, to: to).map { |k, v| Bitcoin::Wallet::Utxo.parse_from_payload(v) }
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
class ::Array
|
|
197
|
-
def with_height(min, max)
|
|
198
|
-
select { |u| u.block_height >= min && u.block_height <= max }
|
|
199
|
-
end
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
def list_unspent_by_block_height(current_block_height, min: 0, max: 9999999)
|
|
203
|
-
max_height = [current_block_height - min, 0].max
|
|
204
|
-
min_height = [current_block_height - max, 0].max
|
|
205
|
-
from = KEY_PREFIX[:height] + [min_height].pack('N').bth + '000000000000000000000000000000000000000000000000000000000000000000000000'
|
|
206
|
-
to = KEY_PREFIX[:height] + [max_height].pack('N').bth + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
|
|
207
|
-
utxos_between(from, to)
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
def list_unspent_by_addresses(current_block_height, min: 0, max: 9999999, addresses: [])
|
|
211
|
-
script_pubkeys = addresses.map { |a| Bitcoin::Script.parse_from_addr(a).to_hex }
|
|
212
|
-
list_unspent_by_script_pubkeys(current_block_height, min: min, max: max, script_pubkeys: script_pubkeys)
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
def list_unspent_by_script_pubkeys(current_block_height, min: 0, max: 9999999, script_pubkeys: [])
|
|
216
|
-
max_height = current_block_height - min
|
|
217
|
-
min_height = current_block_height - max
|
|
218
|
-
script_pubkeys.map do |key|
|
|
219
|
-
from = KEY_PREFIX[:script] + key + '000000000000000000000000000000000000000000000000000000000000000000000000'
|
|
220
|
-
to = KEY_PREFIX[:script] + key + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
|
|
221
|
-
utxos_between(from, to).with_height(min_height, max_height)
|
|
222
|
-
end.flatten
|
|
223
|
-
end
|
|
224
|
-
end
|
|
225
|
-
end
|
|
226
|
-
end
|
data/lib/bitcoin/store.rb
DELETED