bitcoin-ruby 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/.travis.yml +2 -7
- data/COPYING +1 -1
- data/Gemfile +2 -6
- data/Gemfile.lock +34 -0
- data/README.rdoc +16 -68
- data/Rakefile +3 -6
- data/bin/bitcoin_shell +0 -1
- data/{concept-examples/blockchain-pow.rb → examples/concept-blockchain-pow.rb} +0 -0
- data/lib/bitcoin.rb +350 -296
- data/lib/bitcoin/builder.rb +3 -1
- data/lib/bitcoin/connection.rb +2 -1
- data/lib/bitcoin/contracthash.rb +76 -0
- data/lib/bitcoin/dogecoin.rb +97 -0
- data/lib/bitcoin/ffi/bitcoinconsensus.rb +74 -0
- data/lib/bitcoin/ffi/openssl.rb +98 -2
- data/lib/bitcoin/ffi/secp256k1.rb +144 -0
- data/lib/bitcoin/key.rb +12 -2
- data/lib/bitcoin/logger.rb +3 -12
- data/lib/bitcoin/protocol/block.rb +3 -9
- data/lib/bitcoin/protocol/parser.rb +6 -2
- data/lib/bitcoin/protocol/tx.rb +44 -13
- data/lib/bitcoin/protocol/txin.rb +4 -2
- data/lib/bitcoin/protocol/txout.rb +2 -2
- data/lib/bitcoin/script.rb +212 -37
- data/lib/bitcoin/trezor/mnemonic.rb +130 -0
- data/lib/bitcoin/version.rb +1 -1
- data/spec/bitcoin/bitcoin_spec.rb +32 -3
- data/spec/bitcoin/builder_spec.rb +18 -0
- data/spec/bitcoin/contracthash_spec.rb +45 -0
- data/spec/bitcoin/dogecoin_spec.rb +176 -0
- data/spec/bitcoin/ffi_openssl.rb +45 -0
- data/spec/bitcoin/fixtures/156e6e1b84c5c3bd3a0927b25e4119fadce6e6d5186f363317511d1d680fae9a.json +24 -0
- data/spec/bitcoin/fixtures/8d0b238a06b5a70be75d543902d02d7a514d68d3252a949a513865ac3538874c.json +24 -0
- data/spec/bitcoin/fixtures/coinbase-toshi.json +33 -0
- data/spec/bitcoin/fixtures/coinbase.json +24 -0
- data/spec/bitcoin/fixtures/dogecoin-block-60323982f9c5ff1b5a954eac9dc1269352835f47c2c5222691d80f0d50dcf053.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-01-toshi.json +46 -0
- data/spec/bitcoin/fixtures/rawtx-02-toshi.json +46 -0
- data/spec/bitcoin/fixtures/rawtx-03-toshi.json +73 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-04fdc38d6722ab4b12d79113fc4b2896bdcc5169710690ee4e78541b98e467b4.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-0b294c7d11dd21bcccb8393e6744fed7d4d1981a08c00e3e88838cc421f33c9f.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-3bc52ac063291ad92d95ddda5fd776a342083b95607ad32ed8bc6f8f7d30449e.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-6f0bbdd4e71a8af4305018d738184df32dbb6f27284fdebd5b56d16947f7c181.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-a7c9b06e275e8674cc19a5f7d3e557c72c6d93576e635b33212dbe08ab7cdb60.bin +0 -0
- data/spec/bitcoin/fixtures/rawtx-testnet-f80acbd2f594d04ddb0e1cacba662132104909157dff526935a3c88abe9201a5.bin +0 -0
- data/spec/bitcoin/protocol/block_spec.rb +0 -22
- data/spec/bitcoin/protocol/tx_spec.rb +145 -2
- data/spec/bitcoin/script/script_spec.rb +282 -0
- data/spec/bitcoin/secp256k1_spec.rb +48 -0
- data/spec/bitcoin/spec_helper.rb +0 -51
- data/spec/bitcoin/trezor/mnemonic_spec.rb +161 -0
- metadata +48 -98
- data/bin/bitcoin_dns_seed +0 -130
- data/bin/bitcoin_gui +0 -80
- data/bin/bitcoin_node +0 -153
- data/bin/bitcoin_node_cli +0 -81
- data/bin/bitcoin_wallet +0 -402
- data/doc/CONFIG.rdoc +0 -66
- data/doc/EXAMPLES.rdoc +0 -13
- data/doc/NAMECOIN.rdoc +0 -34
- data/doc/NODE.rdoc +0 -225
- data/doc/STORAGE.rdoc +0 -33
- data/doc/WALLET.rdoc +0 -102
- data/examples/balance.rb +0 -66
- data/examples/forwarder.rb +0 -73
- data/examples/index_nhash.rb +0 -24
- data/examples/reindex_p2sh_addrs.rb +0 -44
- data/examples/relay_tx.rb +0 -22
- data/examples/verify_tx.rb +0 -57
- data/lib/bitcoin/config.rb +0 -58
- data/lib/bitcoin/gui/addr_view.rb +0 -44
- data/lib/bitcoin/gui/bitcoin-ruby.png +0 -0
- data/lib/bitcoin/gui/bitcoin-ruby.svg +0 -80
- data/lib/bitcoin/gui/conn_view.rb +0 -38
- data/lib/bitcoin/gui/connection.rb +0 -70
- data/lib/bitcoin/gui/em_gtk.rb +0 -30
- data/lib/bitcoin/gui/gui.builder +0 -1643
- data/lib/bitcoin/gui/gui.rb +0 -292
- data/lib/bitcoin/gui/helpers.rb +0 -115
- data/lib/bitcoin/gui/tree_view.rb +0 -84
- data/lib/bitcoin/gui/tx_view.rb +0 -69
- data/lib/bitcoin/namecoin.rb +0 -280
- data/lib/bitcoin/network/command_client.rb +0 -104
- data/lib/bitcoin/network/command_handler.rb +0 -570
- data/lib/bitcoin/network/connection_handler.rb +0 -387
- data/lib/bitcoin/network/node.rb +0 -565
- data/lib/bitcoin/storage/dummy/dummy_store.rb +0 -179
- data/lib/bitcoin/storage/models.rb +0 -171
- data/lib/bitcoin/storage/sequel/migrations.rb +0 -99
- data/lib/bitcoin/storage/sequel/migrations/001_base_schema.rb +0 -52
- data/lib/bitcoin/storage/sequel/migrations/002_tx.rb +0 -45
- data/lib/bitcoin/storage/sequel/migrations/003_change_txin_script_sig_to_blob.rb +0 -18
- data/lib/bitcoin/storage/sequel/migrations/004_change_txin_prev_out_to_blob.rb +0 -18
- data/lib/bitcoin/storage/sequel/migrations/005_change_tx_hash_to_bytea.rb +0 -14
- data/lib/bitcoin/storage/sequel/migrations/006_add_tx_nhash.rb +0 -31
- data/lib/bitcoin/storage/sequel/migrations/007_add_prev_out_index_index.rb +0 -16
- data/lib/bitcoin/storage/sequel/migrations/008_add_txin_p2sh_type.rb +0 -31
- data/lib/bitcoin/storage/sequel/migrations/009_add_addrs_type.rb +0 -56
- data/lib/bitcoin/storage/sequel/sequel_store.rb +0 -551
- data/lib/bitcoin/storage/storage.rb +0 -517
- data/lib/bitcoin/storage/utxo/migrations/001_base_schema.rb +0 -52
- data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +0 -18
- data/lib/bitcoin/storage/utxo/migrations/003_update_indices.rb +0 -14
- data/lib/bitcoin/storage/utxo/migrations/004_add_addrs_type.rb +0 -14
- data/lib/bitcoin/storage/utxo/utxo_store.rb +0 -374
- data/lib/bitcoin/validation.rb +0 -400
- data/lib/bitcoin/wallet/coinselector.rb +0 -33
- data/lib/bitcoin/wallet/keygenerator.rb +0 -77
- data/lib/bitcoin/wallet/keystore.rb +0 -207
- data/lib/bitcoin/wallet/txdp.rb +0 -118
- data/lib/bitcoin/wallet/wallet.rb +0 -281
- data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.bin +0 -0
- data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.json +0 -43
- data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.bin +0 -0
- data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.json +0 -67
- data/spec/bitcoin/namecoin_spec.rb +0 -182
- data/spec/bitcoin/node/command_api_spec.rb +0 -663
- data/spec/bitcoin/storage/models_spec.rb +0 -104
- data/spec/bitcoin/storage/reorg_spec.rb +0 -236
- data/spec/bitcoin/storage/storage_spec.rb +0 -387
- data/spec/bitcoin/storage/validation_spec.rb +0 -300
- data/spec/bitcoin/wallet/coinselector_spec.rb +0 -38
- data/spec/bitcoin/wallet/keygenerator_spec.rb +0 -69
- data/spec/bitcoin/wallet/keystore_spec.rb +0 -190
- data/spec/bitcoin/wallet/txdp_spec.rb +0 -76
- data/spec/bitcoin/wallet/wallet_spec.rb +0 -238
@@ -1,517 +0,0 @@
|
|
1
|
-
# encoding: ascii-8bit
|
2
|
-
|
3
|
-
# The storage implementation supports different backends, which inherit from
|
4
|
-
# Storage::StoreBase and implement the same interface.
|
5
|
-
# Each backend returns Storage::Models objects to easily access helper methods and metadata.
|
6
|
-
#
|
7
|
-
# The most stable backend is Backends::SequelStore, which uses sequel and can use all
|
8
|
-
# kinds of SQL database backends.
|
9
|
-
module Bitcoin::Storage
|
10
|
-
|
11
|
-
autoload :Models, 'bitcoin/storage/models'
|
12
|
-
|
13
|
-
@log = Bitcoin::Logger.create(:storage)
|
14
|
-
def self.log; @log; end
|
15
|
-
|
16
|
-
BACKENDS = [:dummy, :sequel, :utxo]
|
17
|
-
BACKENDS.each do |name|
|
18
|
-
module_eval <<-EOS
|
19
|
-
def self.#{name} config, *args
|
20
|
-
Backends.const_get("#{name.capitalize}Store").new(config, *args)
|
21
|
-
end
|
22
|
-
EOS
|
23
|
-
end
|
24
|
-
|
25
|
-
module Backends
|
26
|
-
|
27
|
-
BACKENDS.each {|b| autoload("#{b.to_s.capitalize}Store", "bitcoin/storage/#{b}/#{b}_store.rb") }
|
28
|
-
|
29
|
-
# Base class for storage backends.
|
30
|
-
# Every backend must overwrite the "Not implemented" methods
|
31
|
-
# and provide an implementation specific to the storage.
|
32
|
-
# Also, before returning the objects, they should be wrapped
|
33
|
-
# inside the appropriate Bitcoin::Storage::Models class.
|
34
|
-
class StoreBase
|
35
|
-
|
36
|
-
# main branch (longest valid chain)
|
37
|
-
MAIN = 0
|
38
|
-
|
39
|
-
# side branch (connected, valid, but too short)
|
40
|
-
SIDE = 1
|
41
|
-
|
42
|
-
# orphan branch (not connected to main branch / genesis block)
|
43
|
-
ORPHAN = 2
|
44
|
-
|
45
|
-
# possible script types
|
46
|
-
SCRIPT_TYPES = [:unknown, :pubkey, :hash160, :multisig, :p2sh, :op_return]
|
47
|
-
if Bitcoin.namecoin?
|
48
|
-
[:name_new, :name_firstupdate, :name_update].each {|n| SCRIPT_TYPES << n }
|
49
|
-
end
|
50
|
-
|
51
|
-
# possible address types
|
52
|
-
ADDRESS_TYPES = [:hash160, :p2sh]
|
53
|
-
|
54
|
-
DEFAULT_CONFIG = {}
|
55
|
-
|
56
|
-
attr_reader :log
|
57
|
-
|
58
|
-
attr_accessor :config
|
59
|
-
|
60
|
-
def initialize(config = {}, getblocks_callback = nil)
|
61
|
-
# merge all the configuration defaults, keeping the most specific ones.
|
62
|
-
store_ancestors = self.class.ancestors.select {|a| a.name =~ /StoreBase$/ }.reverse
|
63
|
-
base = store_ancestors.reduce(store_ancestors[0]::DEFAULT_CONFIG) do |config, ancestor|
|
64
|
-
config.merge(ancestor::DEFAULT_CONFIG)
|
65
|
-
end
|
66
|
-
@config = base.merge(self.class::DEFAULT_CONFIG).merge(config)
|
67
|
-
@log = config[:log] || Bitcoin::Storage.log
|
68
|
-
@log.level = @config[:log_level] if @config[:log_level]
|
69
|
-
init_store_connection
|
70
|
-
@getblocks_callback = getblocks_callback
|
71
|
-
@checkpoints = Bitcoin.network[:checkpoints] || {}
|
72
|
-
@watched_addrs = []
|
73
|
-
@notifiers = {}
|
74
|
-
end
|
75
|
-
|
76
|
-
def init_store_connection
|
77
|
-
end
|
78
|
-
|
79
|
-
# name of the storage backend currently in use ("sequel" or "utxo")
|
80
|
-
def backend_name
|
81
|
-
self.class.name.split("::")[-1].split("Store")[0].downcase
|
82
|
-
end
|
83
|
-
|
84
|
-
# reset the store; delete all data
|
85
|
-
def reset
|
86
|
-
raise "Not implemented"
|
87
|
-
end
|
88
|
-
|
89
|
-
# check data consistency of the top +count+ blocks.
|
90
|
-
def check_consistency count
|
91
|
-
raise "Not implemented"
|
92
|
-
end
|
93
|
-
|
94
|
-
|
95
|
-
# handle a new block incoming from the network
|
96
|
-
def new_block blk
|
97
|
-
time = Time.now
|
98
|
-
res = store_block(blk)
|
99
|
-
log.info { "block #{blk.hash} " +
|
100
|
-
"[#{res[0]}, #{['main', 'side', 'orphan'][res[1]]}] " +
|
101
|
-
"(#{"%.4fs, %3dtx, %.3fkb" % [(Time.now - time), blk.tx.size, blk.payload.bytesize.to_f/1000]})" } if res && res[1]
|
102
|
-
res
|
103
|
-
end
|
104
|
-
|
105
|
-
# store given block +blk+.
|
106
|
-
# determine branch/chain and dept of block. trigger reorg if side branch becomes longer
|
107
|
-
# than current main chain and connect orpans.
|
108
|
-
def store_block blk
|
109
|
-
log.debug { "new block #{blk.hash}" }
|
110
|
-
|
111
|
-
existing = get_block(blk.hash)
|
112
|
-
if existing && existing.chain == MAIN
|
113
|
-
log.debug { "=> exists (#{existing.depth}, #{existing.chain})" }
|
114
|
-
return [existing.depth]
|
115
|
-
end
|
116
|
-
|
117
|
-
prev_block = get_block(blk.prev_block.reverse_hth)
|
118
|
-
unless @config[:skip_validation]
|
119
|
-
validator = blk.validator(self, prev_block)
|
120
|
-
validator.validate(rules: [:syntax], raise_errors: true)
|
121
|
-
end
|
122
|
-
|
123
|
-
if !prev_block || prev_block.chain == ORPHAN
|
124
|
-
if blk.hash == Bitcoin.network[:genesis_hash]
|
125
|
-
log.debug { "=> genesis (0)" }
|
126
|
-
return persist_block(blk, MAIN, 0)
|
127
|
-
else
|
128
|
-
depth = prev_block ? prev_block.depth + 1 : 0
|
129
|
-
log.debug { "=> orphan (#{depth})" }
|
130
|
-
return [0, 2] unless (in_sync? || Bitcoin.network_name =~ /testnet/)
|
131
|
-
return persist_block(blk, ORPHAN, depth)
|
132
|
-
end
|
133
|
-
end
|
134
|
-
depth = prev_block.depth + 1
|
135
|
-
|
136
|
-
checkpoint = @checkpoints[depth]
|
137
|
-
if checkpoint && blk.hash != checkpoint
|
138
|
-
log.warn "Block #{depth} doesn't match checkpoint #{checkpoint}"
|
139
|
-
exit if depth > get_depth # TODO: handle checkpoint mismatch properly
|
140
|
-
end
|
141
|
-
if prev_block.chain == MAIN
|
142
|
-
if prev_block == get_head
|
143
|
-
log.debug { "=> main (#{depth})" }
|
144
|
-
if !@config[:skip_validation] && ( !@checkpoints.any? || depth > @checkpoints.keys.last )
|
145
|
-
if self.class.name =~ /UtxoStore/
|
146
|
-
@config[:utxo_cache] = 0
|
147
|
-
@config[:block_cache] = 120
|
148
|
-
end
|
149
|
-
validator.validate(rules: [:context], raise_errors: true)
|
150
|
-
end
|
151
|
-
res = persist_block(blk, MAIN, depth, prev_block.work)
|
152
|
-
push_notification(:block, [blk, *res])
|
153
|
-
return res
|
154
|
-
else
|
155
|
-
log.debug { "=> side (#{depth})" }
|
156
|
-
return persist_block(blk, SIDE, depth, prev_block.work)
|
157
|
-
end
|
158
|
-
else
|
159
|
-
head = get_head
|
160
|
-
if prev_block.work + blk.block_work <= head.work
|
161
|
-
log.debug { "=> side (#{depth})" }
|
162
|
-
return persist_block(blk, SIDE, depth, prev_block.work)
|
163
|
-
else
|
164
|
-
log.debug { "=> reorg" }
|
165
|
-
new_main, new_side = [], []
|
166
|
-
fork_block = prev_block
|
167
|
-
while fork_block.chain != MAIN
|
168
|
-
new_main << fork_block.hash
|
169
|
-
fork_block = fork_block.get_prev_block
|
170
|
-
end
|
171
|
-
b = fork_block
|
172
|
-
while b = b.get_next_block
|
173
|
-
new_side << b.hash
|
174
|
-
end
|
175
|
-
log.debug { "new main: #{new_main.inspect}" }
|
176
|
-
log.debug { "new side: #{new_side.inspect}" }
|
177
|
-
|
178
|
-
push_notification(:reorg, [ new_main, new_side ])
|
179
|
-
|
180
|
-
reorg(new_side.reverse, new_main.reverse)
|
181
|
-
return persist_block(blk, MAIN, depth, prev_block.work)
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
# persist given block +blk+ to storage.
|
187
|
-
def persist_block(blk)
|
188
|
-
raise "Not implemented"
|
189
|
-
end
|
190
|
-
|
191
|
-
# update +attrs+ for block with given +hash+.
|
192
|
-
# typically used to update the chain value during reorg.
|
193
|
-
def update_block(hash, attrs)
|
194
|
-
raise "Not implemented"
|
195
|
-
end
|
196
|
-
|
197
|
-
def new_tx(tx)
|
198
|
-
store_tx(tx)
|
199
|
-
end
|
200
|
-
|
201
|
-
# store given +tx+
|
202
|
-
def store_tx(tx, validate = true)
|
203
|
-
raise "Not implemented"
|
204
|
-
end
|
205
|
-
|
206
|
-
# check if block with given +blk_hash+ is already stored
|
207
|
-
def has_block(blk_hash)
|
208
|
-
raise "Not implemented"
|
209
|
-
end
|
210
|
-
|
211
|
-
# check if tx with given +tx_hash+ is already stored
|
212
|
-
def has_tx(tx_hash)
|
213
|
-
raise "Not implemented"
|
214
|
-
end
|
215
|
-
|
216
|
-
# get the hash of the leading block
|
217
|
-
def get_head
|
218
|
-
raise "Not implemented"
|
219
|
-
end
|
220
|
-
|
221
|
-
# return depth of the head block
|
222
|
-
def get_depth
|
223
|
-
raise "Not implemented"
|
224
|
-
end
|
225
|
-
|
226
|
-
# compute blockchain locator
|
227
|
-
def get_locator pointer = get_head
|
228
|
-
if @locator
|
229
|
-
locator, head = @locator
|
230
|
-
if head == pointer
|
231
|
-
return locator
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
|
-
return [("\x00"*32).hth] if get_depth == -1
|
236
|
-
locator, step, orig_pointer = [], 1, pointer
|
237
|
-
while pointer && pointer.hash != Bitcoin::network[:genesis_hash]
|
238
|
-
locator << pointer.hash
|
239
|
-
depth = pointer.depth - step
|
240
|
-
break unless depth > 0
|
241
|
-
prev_block = get_block_by_depth(depth) # TODO
|
242
|
-
break unless prev_block
|
243
|
-
pointer = prev_block
|
244
|
-
step *= 2 if locator.size > 10
|
245
|
-
end
|
246
|
-
locator << Bitcoin::network[:genesis_hash]
|
247
|
-
@locator = [locator, orig_pointer]
|
248
|
-
locator
|
249
|
-
end
|
250
|
-
|
251
|
-
# get block with given +blk_hash+
|
252
|
-
def get_block(blk_hash)
|
253
|
-
raise "Not implemented"
|
254
|
-
end
|
255
|
-
|
256
|
-
# get block with given +depth+ from main chain
|
257
|
-
def get_block_by_depth(depth)
|
258
|
-
raise "Not implemented"
|
259
|
-
end
|
260
|
-
|
261
|
-
# get block with given +prev_hash+
|
262
|
-
def get_block_by_prev_hash(prev_hash)
|
263
|
-
raise "Not implemented"
|
264
|
-
end
|
265
|
-
|
266
|
-
# get block that includes tx with given +tx_hash+
|
267
|
-
def get_block_by_tx(tx_hash)
|
268
|
-
raise "Not implemented"
|
269
|
-
end
|
270
|
-
|
271
|
-
# get block by given +block_id+
|
272
|
-
def get_block_by_id(block_id)
|
273
|
-
raise "Not implemented"
|
274
|
-
end
|
275
|
-
|
276
|
-
# get block id in main chain by given +tx_id+
|
277
|
-
def get_block_id_for_tx_id(tx_id)
|
278
|
-
get_tx_by_id(tx_id).blk_id rescue nil # tx.blk_id is always in main chain
|
279
|
-
end
|
280
|
-
|
281
|
-
# get corresponding txin for the txout in
|
282
|
-
# transaction +tx_hash+ with index +txout_idx+
|
283
|
-
def get_txin_for_txout(tx_hash, txout_idx)
|
284
|
-
raise "Not implemented"
|
285
|
-
end
|
286
|
-
|
287
|
-
# get an array of corresponding txins for provided +txouts+
|
288
|
-
# txouts = [tx_hash, tx_idx]
|
289
|
-
# can be overwritten by specific storage for opimization
|
290
|
-
def get_txins_for_txouts(txouts)
|
291
|
-
txouts.map{|tx_hash, tx_idx| get_txin_for_txout(tx_hash, tx_idx) }.compact
|
292
|
-
end
|
293
|
-
|
294
|
-
# get tx with given +tx_hash+
|
295
|
-
def get_tx(tx_hash)
|
296
|
-
raise "Not implemented"
|
297
|
-
end
|
298
|
-
|
299
|
-
# get more than one tx by +tx_hashes+, returns an array
|
300
|
-
# can be reimplemented by specific storage for optimization
|
301
|
-
def get_txs(tx_hashes)
|
302
|
-
tx_hashes.map {|h| get_tx(h)}.compact
|
303
|
-
end
|
304
|
-
|
305
|
-
# get tx with given +tx_id+
|
306
|
-
def get_tx_by_id(tx_id)
|
307
|
-
raise "Not implemented"
|
308
|
-
end
|
309
|
-
|
310
|
-
# Grab the position of a tx in a given block
|
311
|
-
def get_idx_from_tx_hash(tx_hash)
|
312
|
-
raise "Not implemented"
|
313
|
-
end
|
314
|
-
|
315
|
-
# collect all txouts containing the
|
316
|
-
# given +script+
|
317
|
-
def get_txouts_for_pk_script(script)
|
318
|
-
raise "Not implemented"
|
319
|
-
end
|
320
|
-
|
321
|
-
# collect all txouts containing a
|
322
|
-
# standard tx to given +address+
|
323
|
-
def get_txouts_for_address(address, unconfirmed = false)
|
324
|
-
hash160 = Bitcoin.hash160_from_address(address)
|
325
|
-
type = Bitcoin.address_type(address)
|
326
|
-
get_txouts_for_hash160(hash160, type, unconfirmed)
|
327
|
-
end
|
328
|
-
|
329
|
-
# collect all unspent txouts containing a
|
330
|
-
# standard tx to given +address+
|
331
|
-
def get_unspent_txouts_for_address(address, unconfirmed = false)
|
332
|
-
txouts = self.get_txouts_for_address(address, unconfirmed)
|
333
|
-
txouts.select! do |t|
|
334
|
-
not t.get_next_in
|
335
|
-
end
|
336
|
-
txouts
|
337
|
-
end
|
338
|
-
|
339
|
-
# get balance for given +hash160+
|
340
|
-
def get_balance(hash160_or_addr, unconfirmed = false)
|
341
|
-
if Bitcoin.valid_address?(hash160_or_addr)
|
342
|
-
txouts = get_txouts_for_address(hash160_or_addr)
|
343
|
-
else
|
344
|
-
txouts = get_txouts_for_hash160(hash160_or_addr, :hash160, unconfirmed)
|
345
|
-
end
|
346
|
-
unspent = txouts.select {|o| o.get_next_in.nil?}
|
347
|
-
unspent.map(&:value).inject {|a,b| a+=b; a} || 0
|
348
|
-
rescue
|
349
|
-
nil
|
350
|
-
end
|
351
|
-
|
352
|
-
# parse script and collect address/txout mappings to index
|
353
|
-
def parse_script txout, i, tx_hash = "", tx_idx
|
354
|
-
addrs, names = [], []
|
355
|
-
|
356
|
-
script = Bitcoin::Script.new(txout.pk_script) rescue nil
|
357
|
-
if script
|
358
|
-
if script.is_hash160? || script.is_pubkey? || script.is_p2sh?
|
359
|
-
addrs << [i, script.get_address]
|
360
|
-
elsif script.is_multisig?
|
361
|
-
script.get_multisig_addresses.map do |address|
|
362
|
-
addrs << [i, address]
|
363
|
-
end
|
364
|
-
elsif Bitcoin.namecoin? && script.is_namecoin?
|
365
|
-
addrs << [i, script.get_address]
|
366
|
-
names << [i, script]
|
367
|
-
elsif script.is_op_return?
|
368
|
-
log.info { "Ignoring OP_RETURN script: #{script.get_op_return_data}" }
|
369
|
-
else
|
370
|
-
log.info { "Unknown script type in txout #{tx_hash}:#{tx_idx}" }
|
371
|
-
log.debug { script.to_string }
|
372
|
-
end
|
373
|
-
script_type = SCRIPT_TYPES.index(script.type)
|
374
|
-
else
|
375
|
-
log.error { "Error parsing script #{tx_hash}:#{tx_idx}" }
|
376
|
-
script_type = SCRIPT_TYPES.index(:unknown)
|
377
|
-
end
|
378
|
-
[script_type, addrs, names]
|
379
|
-
end
|
380
|
-
|
381
|
-
def add_watched_address address
|
382
|
-
hash160 = Bitcoin.hash160_from_address(address)
|
383
|
-
@db[:addr].insert(hash160: hash160) unless @db[:addr][hash160: hash160]
|
384
|
-
@watched_addrs << hash160 unless @watched_addrs.include?(hash160)
|
385
|
-
end
|
386
|
-
|
387
|
-
def rescan
|
388
|
-
raise "Not implemented"
|
389
|
-
end
|
390
|
-
|
391
|
-
# import satoshi bitcoind blk0001.dat blockchain file
|
392
|
-
def import filename, max_depth = nil
|
393
|
-
if File.file?(filename)
|
394
|
-
log.info { "Importing #{filename}" }
|
395
|
-
File.open(filename) do |file|
|
396
|
-
until file.eof?
|
397
|
-
magic = file.read(4)
|
398
|
-
|
399
|
-
# bitcoind pads the ends of the block files so that it doesn't
|
400
|
-
# have to reallocate space on every new block.
|
401
|
-
break if magic == "\0\0\0\0"
|
402
|
-
raise "invalid network magic" unless Bitcoin.network[:magic_head] == magic
|
403
|
-
|
404
|
-
size = file.read(4).unpack("L")[0]
|
405
|
-
blk = Bitcoin::P::Block.new(file.read(size))
|
406
|
-
depth, chain = new_block(blk)
|
407
|
-
break if max_depth && depth >= max_depth
|
408
|
-
end
|
409
|
-
end
|
410
|
-
elsif File.directory?(filename)
|
411
|
-
Dir.entries(filename).sort.each do |file|
|
412
|
-
next unless file =~ /^blk.*?\.dat$/
|
413
|
-
import(File.join(filename, file), max_depth)
|
414
|
-
end
|
415
|
-
else
|
416
|
-
raise "Import dir/file #{filename} not found"
|
417
|
-
end
|
418
|
-
end
|
419
|
-
|
420
|
-
def in_sync?
|
421
|
-
(get_head && (Time.now - get_head.time).to_i < 3600) ? true : false
|
422
|
-
end
|
423
|
-
|
424
|
-
def push_notification channel, message
|
425
|
-
@notifiers[channel.to_sym].push(message) if @notifiers[channel.to_sym]
|
426
|
-
end
|
427
|
-
|
428
|
-
def subscribe channel
|
429
|
-
@notifiers[channel.to_sym] ||= EM::Channel.new
|
430
|
-
@notifiers[channel.to_sym].subscribe {|*data| yield(*data) }
|
431
|
-
end
|
432
|
-
|
433
|
-
end
|
434
|
-
|
435
|
-
class SequelStoreBase < StoreBase
|
436
|
-
|
437
|
-
DEFAULT_CONFIG = {
|
438
|
-
sqlite_pragmas: {
|
439
|
-
# journal_mode pragma
|
440
|
-
journal_mode: false,
|
441
|
-
# synchronous pragma
|
442
|
-
synchronous: false,
|
443
|
-
# cache_size pragma
|
444
|
-
# positive specifies number of cache pages to use,
|
445
|
-
# negative specifies cache size in kilobytes.
|
446
|
-
cache_size: -200_000,
|
447
|
-
}
|
448
|
-
}
|
449
|
-
|
450
|
-
SEQUEL_ADAPTERS = { :sqlite => "sqlite3", :postgres => "pg", :mysql => "mysql" }
|
451
|
-
|
452
|
-
#set the connection
|
453
|
-
def init_store_connection
|
454
|
-
return unless (self.is_a?(SequelStore) || self.is_a?(UtxoStore)) && @config[:db]
|
455
|
-
@config[:db].sub!("~", ENV["HOME"])
|
456
|
-
@config[:db].sub!("<network>", Bitcoin.network_name.to_s)
|
457
|
-
adapter = SEQUEL_ADAPTERS[@config[:db].split(":").first] rescue nil
|
458
|
-
Bitcoin.require_dependency(adapter, gem: adapter) if adapter
|
459
|
-
connect
|
460
|
-
end
|
461
|
-
|
462
|
-
# connect to database
|
463
|
-
def connect
|
464
|
-
Sequel.extension(:core_extensions, :sequel_3_dataset_methods)
|
465
|
-
@db = Sequel.connect(@config[:db].sub("~", ENV["HOME"]))
|
466
|
-
@db.extend_datasets(Sequel::Sequel3DatasetMethods)
|
467
|
-
sqlite_pragmas; migrate; check_metadata
|
468
|
-
log.info { "opened #{backend_name} store #{@db.uri}" }
|
469
|
-
end
|
470
|
-
|
471
|
-
# check if schema is up to date and migrate to current version if necessary
|
472
|
-
def migrate
|
473
|
-
migrations_path = File.join(File.dirname(__FILE__), "#{backend_name}/migrations")
|
474
|
-
Sequel.extension :migration
|
475
|
-
unless Sequel::Migrator.is_current?(@db, migrations_path)
|
476
|
-
store = self; log = @log; @db.instance_eval { @log = log; @store = store }
|
477
|
-
Sequel::Migrator.run(@db, migrations_path)
|
478
|
-
unless (v = @db[:schema_info].first) && v[:magic] && v[:backend]
|
479
|
-
@db[:schema_info].update(
|
480
|
-
magic: Bitcoin.network[:magic_head].hth, backend: backend_name)
|
481
|
-
end
|
482
|
-
end
|
483
|
-
end
|
484
|
-
|
485
|
-
# check that database network magic and backend match the ones we are using
|
486
|
-
def check_metadata
|
487
|
-
version = @db[:schema_info].first
|
488
|
-
unless version[:magic] == Bitcoin.network[:magic_head].hth
|
489
|
-
name = Bitcoin::NETWORKS.find{|n,d| d[:magic_head].hth == version[:magic]}[0]
|
490
|
-
raise "Error: DB #{@db.url} was created for '#{name}' network!"
|
491
|
-
end
|
492
|
-
unless version[:backend] == backend_name
|
493
|
-
if version[:backend] == "sequel" && backend_name == "utxo"
|
494
|
-
log.warn { "Note: The 'utxo' store is now the default backend.
|
495
|
-
To keep using the full storage, change the configuration to use storage: 'sequel::#{@db.url}'.
|
496
|
-
To use the new storage backend, delete or move #{@db.url}, or specify a different database path in the config." }
|
497
|
-
end
|
498
|
-
raise "Error: DB #{@db.url} was created for '#{version[:backend]}' backend!"
|
499
|
-
end
|
500
|
-
end
|
501
|
-
|
502
|
-
# set pragma options for sqlite (if it is sqlite)
|
503
|
-
def sqlite_pragmas
|
504
|
-
return unless (@db.is_a?(Sequel::SQLite::Database) rescue false)
|
505
|
-
@config[:sqlite_pragmas].each do |name, value|
|
506
|
-
@db.pragma_set name, value
|
507
|
-
log.debug { "set sqlite pragma #{name} to #{value}" }
|
508
|
-
end
|
509
|
-
end
|
510
|
-
end
|
511
|
-
|
512
|
-
end
|
513
|
-
end
|
514
|
-
|
515
|
-
|
516
|
-
# TODO: someday sequel will support #blob directly and #to_sequel_blob will be gone
|
517
|
-
class String; def blob; to_sequel_blob; end; end
|