bitcoin-ruby 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -1
- data/Gemfile +21 -0
- data/README.rdoc +85 -25
- data/Rakefile +7 -3
- data/bin/bitcoin_node +39 -42
- data/bin/bitcoin_shell +1 -0
- data/bin/bitcoin_wallet +129 -53
- data/bitcoin-ruby.gemspec +4 -7
- data/concept-examples/blockchain-pow.rb +1 -1
- data/doc/CONFIG.rdoc +5 -5
- data/doc/EXAMPLES.rdoc +9 -5
- data/doc/NAMECOIN.rdoc +34 -0
- data/doc/NODE.rdoc +147 -10
- data/examples/balance.rb +10 -4
- data/examples/bbe_verify_tx.rb +7 -2
- data/examples/forwarder.rb +73 -0
- data/examples/generate_tx.rb +34 -0
- data/examples/simple_network_monitor_and_util.rb +187 -0
- data/examples/verify_tx.rb +1 -1
- data/lib/bitcoin.rb +308 -18
- data/lib/bitcoin/builder.rb +62 -36
- data/lib/bitcoin/config.rb +2 -0
- data/lib/bitcoin/connection.rb +11 -8
- data/lib/bitcoin/electrum/mnemonic.rb +162 -0
- data/lib/bitcoin/ffi/openssl.rb +187 -21
- data/lib/bitcoin/gui/addr_view.rb +2 -0
- data/lib/bitcoin/gui/conn_view.rb +2 -0
- data/lib/bitcoin/gui/connection.rb +2 -0
- data/lib/bitcoin/gui/em_gtk.rb +2 -0
- data/lib/bitcoin/gui/gui.rb +2 -0
- data/lib/bitcoin/gui/helpers.rb +2 -0
- data/lib/bitcoin/gui/tree_view.rb +2 -0
- data/lib/bitcoin/gui/tx_view.rb +2 -0
- data/lib/bitcoin/key.rb +77 -11
- data/lib/bitcoin/litecoin.rb +81 -0
- data/lib/bitcoin/logger.rb +20 -1
- data/lib/bitcoin/namecoin.rb +279 -0
- data/lib/bitcoin/network/command_client.rb +7 -6
- data/lib/bitcoin/network/command_handler.rb +229 -43
- data/lib/bitcoin/network/connection_handler.rb +182 -70
- data/lib/bitcoin/network/node.rb +231 -106
- data/lib/bitcoin/protocol.rb +44 -23
- data/lib/bitcoin/protocol/address.rb +5 -3
- data/lib/bitcoin/protocol/alert.rb +3 -4
- data/lib/bitcoin/protocol/aux_pow.rb +123 -0
- data/lib/bitcoin/protocol/block.rb +98 -18
- data/lib/bitcoin/protocol/handler.rb +6 -5
- data/lib/bitcoin/protocol/parser.rb +44 -19
- data/lib/bitcoin/protocol/tx.rb +105 -52
- data/lib/bitcoin/protocol/txin.rb +39 -19
- data/lib/bitcoin/protocol/txout.rb +28 -13
- data/lib/bitcoin/protocol/version.rb +16 -7
- data/lib/bitcoin/script.rb +579 -122
- data/lib/bitcoin/storage/{dummy.rb → dummy/dummy_store.rb} +8 -14
- data/lib/bitcoin/storage/models.rb +20 -7
- data/lib/bitcoin/storage/{sequel_store/sequel_migrations.rb → sequel/migrations.rb} +22 -7
- data/lib/bitcoin/storage/sequel/migrations/001_base_schema.rb +52 -0
- data/lib/bitcoin/storage/sequel/migrations/002_tx.rb +50 -0
- data/lib/bitcoin/storage/sequel/migrations/003_change_txin_script_sig_to_blob.rb +18 -0
- data/lib/bitcoin/storage/sequel/sequel_store.rb +436 -0
- data/lib/bitcoin/storage/storage.rb +233 -28
- data/lib/bitcoin/storage/utxo/migrations/001_base_schema.rb +52 -0
- data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +18 -0
- data/lib/bitcoin/storage/utxo/utxo_store.rb +361 -0
- data/lib/bitcoin/validation.rb +369 -0
- data/lib/bitcoin/version.rb +1 -1
- data/lib/bitcoin/wallet/coinselector.rb +3 -0
- data/lib/bitcoin/wallet/keygenerator.rb +3 -1
- data/lib/bitcoin/wallet/keystore.rb +6 -2
- data/lib/bitcoin/wallet/txdp.rb +6 -4
- data/lib/bitcoin/wallet/wallet.rb +54 -16
- data/spec/bitcoin/bitcoin_spec.rb +48 -3
- data/spec/bitcoin/builder_spec.rb +40 -17
- data/spec/bitcoin/fixtures/000000000000056b1a3d84a1e2b33cde8915a4b61c0cae14fca6d3e1490b4f98.json +3697 -0
- data/spec/bitcoin/fixtures/03d7e1fa4d5fefa169431f24f7798552861b255cd55d377066fedcd088fb0e99.json +23 -0
- data/spec/bitcoin/fixtures/0961c660358478829505e16a1f028757e54b5bbf9758341a7546573738f31429.json +24 -0
- data/spec/bitcoin/fixtures/0f24294a1d23efbb49c1765cf443fba7930702752aba6d765870082fe4f13cae.json +37 -0
- data/spec/bitcoin/fixtures/315ac7d4c26d69668129cc352851d9389b4a6868f1509c6c8b66bead11e2619f.json +31 -0
- data/spec/bitcoin/fixtures/35e2001b428891fefa0bfb73167c7360669d3cbd7b3aa78e7cad125ddfc51131.json +27 -0
- data/spec/bitcoin/fixtures/3a17dace09ffb919ed627a93f1873220f4c975c1248558b18d16bce25d38c4b7.json +72 -0
- data/spec/bitcoin/fixtures/3e58b7eed0fdb599019af08578effea25c8666bbe8e200845453cacce6314477.json +27 -0
- data/spec/bitcoin/fixtures/514c46f0b61714092f15c8dfcb576c9f79b3f959989b98de3944b19d98832b58.json +24 -0
- data/spec/bitcoin/fixtures/51bf528ecf3c161e7c021224197dbe84f9a8564212f6207baa014c01a1668e1e.json +30 -0
- data/spec/bitcoin/fixtures/69216b8aaa35b76d6613e5f527f4858640d986e1046238583bdad79b35e938dc.json +28 -0
- data/spec/bitcoin/fixtures/7208e5edf525f04e705fb3390194e316205b8f995c8c9fcd8c6093abe04fa27d.json +27 -0
- data/spec/bitcoin/fixtures/761d8c5210fdfd505f6dff38f740ae3728eb93d7d0971fb433f685d40a4c04f6.json +27 -0
- data/spec/bitcoin/fixtures/aea682d68a3ea5e3583e088dcbd699a5d44d4b083f02ad0aaf2598fe1fa4dfd4.json +27 -0
- data/spec/bitcoin/fixtures/bd1715f1abfdc62bea3f605bdb461b3ba1f2cca6ec0d73a18a548b7717ca8531.json +34 -0
- data/spec/bitcoin/fixtures/block-testnet-0000000000ac85bb2530a05a4214a387e6be02b22d3348abc5e7a5d9c4ce8dab.bin +0 -0
- data/spec/bitcoin/fixtures/cd874fa8cb0e2ec2d385735d5e1fd482c4fe648533efb4c50ee53bda58e15ae2.json +24 -0
- data/spec/bitcoin/fixtures/ce5fad9b4ef094d8f4937b0707edaf0a6e6ceeaf67d5edbfd51f660eac8f398b.json +41 -0
- data/spec/bitcoin/fixtures/f003f0c1193019db2497a675fd05d9f2edddf9b67c59e677c48d3dbd4ed5f00b.json +23 -0
- data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.bin +0 -0
- data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.json +43 -0
- data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.bin +0 -0
- data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.json +67 -0
- data/spec/bitcoin/fixtures/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.bin +0 -0
- data/spec/bitcoin/fixtures/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.json +39 -0
- data/spec/bitcoin/fixtures/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.bin +0 -0
- data/spec/bitcoin/fixtures/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.json +39 -0
- data/spec/bitcoin/fixtures/rawblock-auxpow.bin +0 -0
- data/spec/bitcoin/fixtures/tx-313897799b1e37e9ecae15010e56156dddde4e683c96b0e713af95272c38aee0.json +30 -0
- data/spec/bitcoin/fixtures/tx-3da75972766f0ad13319b0b461fd16823a731e44f6e9de4eb3c52d6a6fb6c8ae.json +23 -0
- data/spec/bitcoin/fixtures/tx-44b833074e671120ba33106877b49e86ece510824b9af477a3853972bcd8d06a.json +30 -0
- data/spec/bitcoin/fixtures/tx-d3d77d63709e47d9ef58f0b557800115a6b676c6a423012fbb96f45d8fcef830.json +28 -0
- data/spec/bitcoin/key_spec.rb +128 -3
- data/spec/bitcoin/namecoin_spec.rb +182 -0
- data/spec/bitcoin/network_spec.rb +5 -3
- data/spec/bitcoin/node/command_api_spec.rb +376 -0
- data/spec/bitcoin/protocol/addr_spec.rb +2 -0
- data/spec/bitcoin/protocol/alert_spec.rb +2 -0
- data/spec/bitcoin/protocol/aux_pow_spec.rb +44 -0
- data/spec/bitcoin/protocol/block_spec.rb +134 -39
- data/spec/bitcoin/protocol/getblocks_spec.rb +32 -0
- data/spec/bitcoin/protocol/inv_spec.rb +10 -8
- data/spec/bitcoin/protocol/notfound_spec.rb +31 -0
- data/spec/bitcoin/protocol/ping_spec.rb +2 -0
- data/spec/bitcoin/protocol/tx_spec.rb +83 -17
- data/spec/bitcoin/protocol/version_spec.rb +7 -5
- data/spec/bitcoin/script/opcodes_spec.rb +412 -133
- data/spec/bitcoin/script/script_spec.rb +112 -13
- data/spec/bitcoin/spec_helper.rb +68 -0
- data/spec/bitcoin/storage/reorg_spec.rb +199 -0
- data/spec/bitcoin/storage/storage_spec.rb +337 -0
- data/spec/bitcoin/storage/validation_spec.rb +261 -0
- data/spec/bitcoin/wallet/coinselector_spec.rb +10 -7
- data/spec/bitcoin/wallet/keygenerator_spec.rb +2 -0
- data/spec/bitcoin/wallet/keystore_spec.rb +2 -0
- data/spec/bitcoin/wallet/txdp_spec.rb +2 -0
- data/spec/bitcoin/wallet/wallet_spec.rb +91 -58
- metadata +105 -51
- data/lib/bitcoin/storage/sequel.rb +0 -335
- data/spec/bitcoin/fixtures/0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd.json +0 -27
- data/spec/bitcoin/fixtures/477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590.json +0 -27
- data/spec/bitcoin/reorg_spec.rb +0 -129
- data/spec/bitcoin/storage_spec.rb +0 -229
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
1
3
|
module Bitcoin::Storage::Backends
|
2
4
|
class DummyStore < StoreBase
|
3
5
|
|
@@ -12,29 +14,21 @@ module Bitcoin::Storage::Backends
|
|
12
14
|
@blk, @tx = [], {}
|
13
15
|
end
|
14
16
|
|
15
|
-
def
|
16
|
-
return
|
17
|
+
def persist_block(blk, chain, depth, prev_work = 0)
|
18
|
+
return [depth, chain] unless blk && chain == 0
|
17
19
|
if block = get_block(blk.hash)
|
18
20
|
log.info { "Block already stored; skipping" }
|
19
21
|
return false
|
20
22
|
end
|
21
23
|
|
22
|
-
prev_block = get_block(Bitcoin::hth(blk.prev_block.reverse))
|
23
|
-
unless prev_block
|
24
|
-
unless blk.hash == Bitcoin.network[:genesis_hash]
|
25
|
-
log.warn { "INVALID BLOCK: #{blk.hash}" }
|
26
|
-
return false
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
24
|
blk.tx.each {|tx| store_tx(tx) }
|
31
25
|
@blk << blk
|
32
26
|
|
33
27
|
log.info { "NEW HEAD: #{blk.hash} DEPTH: #{get_depth}" }
|
34
|
-
|
28
|
+
[depth, chain]
|
35
29
|
end
|
36
30
|
|
37
|
-
def store_tx(tx)
|
31
|
+
def store_tx(tx, validate = true)
|
38
32
|
if @tx.keys.include?(tx.hash)
|
39
33
|
log.info { "Tx already stored; skipping" }
|
40
34
|
return tx
|
@@ -99,7 +93,7 @@ module Bitcoin::Storage::Backends
|
|
99
93
|
txouts.map {|o| wrap_txout(o) }
|
100
94
|
end
|
101
95
|
|
102
|
-
def get_txouts_for_hash160(hash160)
|
96
|
+
def get_txouts_for_hash160(hash160, unconfirmed = false)
|
103
97
|
@tx.values.map(&:out).flatten.map {|o|
|
104
98
|
o = wrap_txout(o)
|
105
99
|
o.hash160 == hash160 ? o : nil
|
@@ -108,7 +102,7 @@ module Bitcoin::Storage::Backends
|
|
108
102
|
|
109
103
|
def wrap_block(block)
|
110
104
|
return nil unless block
|
111
|
-
data = {:id => @blk.index(block), :depth => @blk.index(block)}
|
105
|
+
data = {:id => @blk.index(block), :depth => @blk.index(block), :work => @blk.index(block), :chain => 0}
|
112
106
|
blk = Bitcoin::Storage::Models::Block.new(self, data)
|
113
107
|
[:ver, :prev_block, :mrkl_root, :time, :bits, :nonce].each do |attr|
|
114
108
|
blk.send("#{attr}=", block.send(attr))
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
1
3
|
# StorageModels defines objects that are returned from storage.
|
2
4
|
# These objects inherit from their Bitcoin::Protocol counterpart
|
3
5
|
# and add some additional data and methods.
|
@@ -12,19 +14,20 @@ module Bitcoin::Storage::Models
|
|
12
14
|
class Block < Bitcoin::Protocol::Block
|
13
15
|
|
14
16
|
attr_accessor :ver, :prev_block, :mrkl_root, :time, :bits, :nonce, :tx
|
15
|
-
attr_reader :store, :id, :depth, :chain
|
17
|
+
attr_reader :store, :id, :depth, :chain, :work
|
16
18
|
|
17
19
|
def initialize store, data
|
18
20
|
@store = store
|
19
21
|
@id = data[:id]
|
20
22
|
@depth = data[:depth]
|
21
23
|
@chain = data[:chain]
|
24
|
+
@work = data[:work]
|
22
25
|
@tx = []
|
23
26
|
end
|
24
27
|
|
25
28
|
# get the block this one builds upon
|
26
29
|
def get_prev_block
|
27
|
-
@store.get_block(
|
30
|
+
@store.get_block(@prev_block.reverse_hth)
|
28
31
|
end
|
29
32
|
|
30
33
|
# get the block that builds upon this one
|
@@ -80,7 +83,7 @@ module Bitcoin::Storage::Models
|
|
80
83
|
|
81
84
|
# get the previous output referenced by this input
|
82
85
|
def get_prev_out
|
83
|
-
prev_tx = @store.get_tx(@prev_out.
|
86
|
+
prev_tx = @store.get_tx(@prev_out.reverse_hth)
|
84
87
|
return nil unless prev_tx
|
85
88
|
prev_tx.out[@prev_out_index]
|
86
89
|
end
|
@@ -101,7 +104,7 @@ module Bitcoin::Storage::Models
|
|
101
104
|
end
|
102
105
|
|
103
106
|
def hash160
|
104
|
-
|
107
|
+
script.get_hash160
|
105
108
|
end
|
106
109
|
|
107
110
|
# get the transaction this output is in
|
@@ -112,20 +115,30 @@ module Bitcoin::Storage::Models
|
|
112
115
|
# get the next input that references this output
|
113
116
|
def get_next_in
|
114
117
|
@store.get_txin_for_txout(get_tx.hash, @tx_idx)
|
118
|
+
rescue
|
119
|
+
nil
|
115
120
|
end
|
116
121
|
|
117
122
|
# get all addresses this txout corresponds to (if possible)
|
118
123
|
def get_address
|
119
|
-
|
124
|
+
script.get_address
|
120
125
|
end
|
121
126
|
|
122
127
|
# get the single address this txout corresponds to (first for multisig tx)
|
123
128
|
def get_addresses
|
124
|
-
|
129
|
+
script.get_addresses
|
130
|
+
end
|
131
|
+
|
132
|
+
def get_namecoin_name
|
133
|
+
@store.get_name_by_txout_id(@id)
|
125
134
|
end
|
126
135
|
|
127
136
|
def type
|
128
|
-
|
137
|
+
script.type
|
138
|
+
end
|
139
|
+
|
140
|
+
def script
|
141
|
+
@_script = Bitcoin::Script.new(@pk_script)
|
129
142
|
end
|
130
143
|
|
131
144
|
end
|
@@ -1,26 +1,32 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
1
3
|
module Bitcoin::Storage::Backends::SequelMigrations
|
2
4
|
|
3
5
|
def migrate
|
6
|
+
binary = @db.database_type == :postgres ? :bytea : :blob
|
7
|
+
|
4
8
|
unless @db.tables.include?(:blk)
|
5
9
|
@db.create_table :blk do
|
6
10
|
primary_key :id
|
7
|
-
column :hash,
|
11
|
+
column :hash, binary, :null => false, :unique => true, :index => true
|
8
12
|
column :depth, :int, :null => false, :index => true
|
9
13
|
column :version, :bigint, :null => false
|
10
|
-
column :prev_hash,
|
11
|
-
column :mrkl_root,
|
14
|
+
column :prev_hash, binary, :null => false, :index => true
|
15
|
+
column :mrkl_root, binary, :null => false
|
12
16
|
column :time, :bigint, :null => false
|
13
17
|
column :bits, :bigint, :null => false
|
14
18
|
column :nonce, :bigint, :null => false
|
15
19
|
column :blk_size, :int, :null => false
|
16
20
|
column :chain, :int, :null => false
|
21
|
+
column :work, binary, :index => true
|
22
|
+
column :aux_pow, binary
|
17
23
|
end
|
18
24
|
end
|
19
25
|
|
20
26
|
unless @db.tables.include?(:tx)
|
21
27
|
@db.create_table :tx do
|
22
28
|
primary_key :id
|
23
|
-
column :hash,
|
29
|
+
column :hash, binary, :null => false, :unique => true, :index => true
|
24
30
|
column :version, :bigint, :null => false
|
25
31
|
column :lock_time, :bigint, :null => false
|
26
32
|
column :coinbase, :bool, :null => false
|
@@ -41,8 +47,8 @@ module Bitcoin::Storage::Backends::SequelMigrations
|
|
41
47
|
primary_key :id
|
42
48
|
column :tx_id, :int, :null => false, :index => true
|
43
49
|
column :tx_idx, :int, :null => false
|
44
|
-
column :script_sig,
|
45
|
-
column :prev_out,
|
50
|
+
column :script_sig, binary, :null => false
|
51
|
+
column :prev_out, binary, :null => false, :index => true
|
46
52
|
column :prev_out_index, :bigint, :null => false
|
47
53
|
column :sequence, :bigint, :null => false
|
48
54
|
end
|
@@ -53,7 +59,7 @@ module Bitcoin::Storage::Backends::SequelMigrations
|
|
53
59
|
primary_key :id
|
54
60
|
column :tx_id, :int, :null => false, :index => true
|
55
61
|
column :tx_idx, :int, :null => false
|
56
|
-
column :pk_script,
|
62
|
+
column :pk_script, binary, :null => false
|
57
63
|
column :value, :bigint
|
58
64
|
column :type, :int, :null => false, :index => true
|
59
65
|
end
|
@@ -79,6 +85,15 @@ module Bitcoin::Storage::Backends::SequelMigrations
|
|
79
85
|
"(SELECT 1 FROM blk_tx WHERE blk_tx.tx_id = tx.id)" +
|
80
86
|
"ORDER BY tx.id DESC")
|
81
87
|
end
|
88
|
+
|
89
|
+
unless @db.tables.include?(:names)
|
90
|
+
@db.create_table :names do
|
91
|
+
column :txout_id, :int, :null => false, :index => true
|
92
|
+
column :hash, binary, :index => true
|
93
|
+
column :name, binary, :index => true
|
94
|
+
column :value, binary
|
95
|
+
end
|
96
|
+
end
|
82
97
|
end
|
83
98
|
|
84
99
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
Sequel.migration do
|
2
|
+
|
3
|
+
|
4
|
+
up do
|
5
|
+
|
6
|
+
$stdout.puts "Running migration #{__FILE__}"
|
7
|
+
|
8
|
+
binary = adapter_scheme == :postgres ? :bytea : :varchar
|
9
|
+
|
10
|
+
alter_table :schema_info do
|
11
|
+
add_column :magic, :varchar # network magic-head
|
12
|
+
add_column :backend, :varchar # storage backend
|
13
|
+
end
|
14
|
+
|
15
|
+
next if tables.include?(:blk)
|
16
|
+
|
17
|
+
create_table :blk do
|
18
|
+
primary_key :id
|
19
|
+
column :hash, binary, :null => false, :unique => true, :index => true
|
20
|
+
column :depth, :int, :null => false, :index => true
|
21
|
+
column :version, :bigint, :null => false
|
22
|
+
column :prev_hash, binary, :null => false, :index => true
|
23
|
+
column :mrkl_root, binary, :null => false
|
24
|
+
column :time, :bigint, :null => false
|
25
|
+
column :bits, :bigint, :null => false
|
26
|
+
column :nonce, :bigint, :null => false
|
27
|
+
column :blk_size, :int, :null => false
|
28
|
+
column :chain, :int, :null => false
|
29
|
+
column :work, binary, :index => true
|
30
|
+
column :aux_pow, binary
|
31
|
+
end
|
32
|
+
|
33
|
+
create_table :addr do
|
34
|
+
primary_key :id
|
35
|
+
column :hash160, String, :null => false, :index => true
|
36
|
+
end
|
37
|
+
|
38
|
+
create_table :addr_txout do
|
39
|
+
column :addr_id, :int, :null => false, :index => true
|
40
|
+
column :txout_id, :int, :null => false, :index => true
|
41
|
+
end
|
42
|
+
|
43
|
+
create_table :names do
|
44
|
+
column :txout_id, :int, :null => false, :index => true
|
45
|
+
column :hash, binary, :index => true
|
46
|
+
column :name, binary, :index => true
|
47
|
+
column :value, binary
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
Sequel.migration do
|
2
|
+
|
3
|
+
up do
|
4
|
+
|
5
|
+
$stdout.puts "Running migration #{__FILE__}"
|
6
|
+
|
7
|
+
next if tables.include?(:tx)
|
8
|
+
|
9
|
+
create_table :tx do
|
10
|
+
primary_key :id
|
11
|
+
column :hash, :varchar, :null => false, :unique => true, :index => true
|
12
|
+
column :version, :bigint, :null => false
|
13
|
+
column :lock_time, :bigint, :null => false
|
14
|
+
column :coinbase, :bool, :null => false
|
15
|
+
column :tx_size, :int, :null => false
|
16
|
+
end
|
17
|
+
|
18
|
+
create_table :blk_tx do
|
19
|
+
column :blk_id, :int, :null => false, :index => true
|
20
|
+
column :tx_id, :int, :null => false, :index => true
|
21
|
+
column :idx, :int, :null => false
|
22
|
+
end
|
23
|
+
|
24
|
+
create_table :txin do
|
25
|
+
primary_key :id
|
26
|
+
column :tx_id, :int, :null => false, :index => true
|
27
|
+
column :tx_idx, :int, :null => false
|
28
|
+
column :script_sig, :varchar, :null => false
|
29
|
+
column :prev_out, :varchar, :null => false, :index => true
|
30
|
+
column :prev_out_index, :bigint, :null => false
|
31
|
+
column :sequence, :bigint, :null => false
|
32
|
+
end
|
33
|
+
|
34
|
+
create_table :txout do
|
35
|
+
primary_key :id
|
36
|
+
column :tx_id, :int, :null => false, :index => true
|
37
|
+
column :tx_idx, :int, :null => false
|
38
|
+
column :pk_script, (@db.adapter_scheme == :postgres ? :bytea : :blob), :null => false
|
39
|
+
column :value, :bigint
|
40
|
+
column :type, :int, :null => false, :index => true
|
41
|
+
end
|
42
|
+
|
43
|
+
create_view(:unconfirmed,
|
44
|
+
"SELECT * FROM tx WHERE NOT EXISTS " +
|
45
|
+
"(SELECT 1 FROM blk_tx WHERE blk_tx.tx_id = tx.id)" +
|
46
|
+
"ORDER BY tx.id DESC")
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
Sequel.migration do
|
2
|
+
|
3
|
+
up do
|
4
|
+
|
5
|
+
$stdout.puts "Running migration #{__FILE__}"
|
6
|
+
|
7
|
+
if adapter_scheme == :postgres
|
8
|
+
add_column :txin, :tmp_script_sig, :bytea
|
9
|
+
self[:txin].where.update("tmp_script_sig = script_sig::bytea")
|
10
|
+
drop_column :txin, :script_sig
|
11
|
+
add_column :txin, :script_sig, :bytea
|
12
|
+
self[:txin].where.update("script_sig = tmp_script_sig")
|
13
|
+
drop_column :txin, :tmp_script_sig
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,436 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
Bitcoin.require_dependency :sequel, message:
|
4
|
+
"Note: You will also need an adapter for your database like sqlite3, mysql2, postgresql"
|
5
|
+
|
6
|
+
module Bitcoin::Storage::Backends
|
7
|
+
|
8
|
+
# Storage backend using Sequel to connect to arbitrary SQL databases.
|
9
|
+
# Inherits from StoreBase and implements its interface.
|
10
|
+
class SequelStore < StoreBase
|
11
|
+
|
12
|
+
# sequel database connection
|
13
|
+
attr_accessor :db
|
14
|
+
|
15
|
+
DEFAULT_CONFIG = { mode: :full, cache_head: false }
|
16
|
+
|
17
|
+
# create sequel store with given +config+
|
18
|
+
def initialize config, *args
|
19
|
+
super config, *args
|
20
|
+
end
|
21
|
+
|
22
|
+
# connect to database
|
23
|
+
def connect
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
# reset database; delete all data
|
28
|
+
def reset
|
29
|
+
[:blk, :blk_tx, :tx, :txin, :txout, :addr, :addr_txout, :names].each {|table| @db[table].delete }
|
30
|
+
@head = nil
|
31
|
+
end
|
32
|
+
|
33
|
+
# persist given block +blk+ to storage.
|
34
|
+
def persist_block blk, chain, depth, prev_work = 0
|
35
|
+
@db.transaction do
|
36
|
+
attrs = {
|
37
|
+
:hash => blk.hash.htb.blob,
|
38
|
+
:depth => depth,
|
39
|
+
:chain => chain,
|
40
|
+
:version => blk.ver,
|
41
|
+
:prev_hash => blk.prev_block.reverse.blob,
|
42
|
+
:mrkl_root => blk.mrkl_root.reverse.blob,
|
43
|
+
:time => blk.time,
|
44
|
+
:bits => blk.bits,
|
45
|
+
:nonce => blk.nonce,
|
46
|
+
:blk_size => blk.to_payload.bytesize,
|
47
|
+
:work => (prev_work + blk.block_work).to_s
|
48
|
+
}
|
49
|
+
attrs[:aux_pow] = blk.aux_pow.to_payload.blob if blk.aux_pow
|
50
|
+
existing = @db[:blk].filter(:hash => blk.hash.htb.blob)
|
51
|
+
if existing.any?
|
52
|
+
existing.update attrs
|
53
|
+
block_id = existing.first[:id]
|
54
|
+
else
|
55
|
+
block_id = @db[:blk].insert(attrs)
|
56
|
+
blk_tx, new_tx, addrs, names = [], [], [], []
|
57
|
+
|
58
|
+
# store tx
|
59
|
+
blk.tx.each.with_index do |tx, idx|
|
60
|
+
existing = @db[:tx][hash: tx.hash.htb.blob]
|
61
|
+
existing ? blk_tx[idx] = existing[:id] : new_tx << [tx, idx]
|
62
|
+
end
|
63
|
+
new_tx_ids = @db[:tx].insert_multiple(new_tx.map {|tx, _| tx_data(tx) })
|
64
|
+
new_tx_ids.each.with_index {|tx_id, idx| blk_tx[new_tx[idx][1]] = tx_id }
|
65
|
+
|
66
|
+
@db[:blk_tx].insert_multiple(blk_tx.map.with_index {|id, idx|
|
67
|
+
{ blk_id: block_id, tx_id: id, idx: idx } })
|
68
|
+
|
69
|
+
# store txins
|
70
|
+
txin_ids = @db[:txin].insert_multiple(new_tx.map.with_index {|tx, tx_idx|
|
71
|
+
tx, _ = *tx
|
72
|
+
tx.in.map.with_index {|txin, txin_idx|
|
73
|
+
txin_data(new_tx_ids[tx_idx], txin, txin_idx) } }.flatten)
|
74
|
+
|
75
|
+
# store txouts
|
76
|
+
txout_i = 0
|
77
|
+
txout_ids = @db[:txout].insert_multiple(new_tx.map.with_index {|tx, tx_idx|
|
78
|
+
tx, _ = *tx
|
79
|
+
tx.out.map.with_index {|txout, txout_idx|
|
80
|
+
script_type, a, n = *parse_script(txout, txout_i)
|
81
|
+
addrs += a; names += n; txout_i += 1
|
82
|
+
txout_data(new_tx_ids[tx_idx], txout, txout_idx, script_type) } }.flatten)
|
83
|
+
|
84
|
+
# store addrs
|
85
|
+
persist_addrs addrs.map {|i, h| [txout_ids[i], h]}
|
86
|
+
names.each {|i, script| store_name(script, txout_ids[i]) }
|
87
|
+
end
|
88
|
+
@head = wrap_block(attrs.merge(id: block_id)) if chain == MAIN
|
89
|
+
@db[:blk].where(:prev_hash => blk.hash.htb.blob, :chain => ORPHAN).each do |b|
|
90
|
+
log.debug { "connecting orphan #{b[:hash].hth}" }
|
91
|
+
begin
|
92
|
+
store_block(get_block(b[:hash].hth))
|
93
|
+
rescue SystemStackError
|
94
|
+
EM.defer { store_block(get_block(b[:hash].hth)) } if EM.reactor_running?
|
95
|
+
end
|
96
|
+
end
|
97
|
+
return depth, chain
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def reorg new_side, new_main
|
102
|
+
@db.transaction do
|
103
|
+
@db[:blk].where(hash: new_side.map {|h| h.htb.blob }).update(chain: SIDE)
|
104
|
+
new_main.each {|b| get_block(b).validator(self).validate(raise_errors: true) } unless @config[:skip_validation]
|
105
|
+
@db[:blk].where(hash: new_main.map {|h| h.htb.blob }).update(chain: MAIN)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# parse script and collect address/txout mappings to index
|
110
|
+
def parse_script txout, i
|
111
|
+
addrs, names = [], []
|
112
|
+
|
113
|
+
script = Bitcoin::Script.new(txout.pk_script) rescue nil
|
114
|
+
if script
|
115
|
+
if script.is_hash160? || script.is_pubkey?
|
116
|
+
addrs << [i, script.get_hash160]
|
117
|
+
elsif script.is_multisig?
|
118
|
+
script.get_multisig_pubkeys.map do |pubkey|
|
119
|
+
addrs << [i, Bitcoin.hash160(pubkey.unpack("H*")[0])]
|
120
|
+
end
|
121
|
+
elsif Bitcoin.namecoin? && script.is_namecoin?
|
122
|
+
addrs << [i, script.get_hash160]
|
123
|
+
names << [i, script]
|
124
|
+
else
|
125
|
+
log.warn { "Unknown script type"}# #{tx.hash}:#{txout_idx}" }
|
126
|
+
end
|
127
|
+
script_type = SCRIPT_TYPES.index(script.type)
|
128
|
+
else
|
129
|
+
log.error { "Error parsing script"}# #{tx.hash}:#{txout_idx}" }
|
130
|
+
script_type = SCRIPT_TYPES.index(:unknown)
|
131
|
+
end
|
132
|
+
[script_type, addrs, names]
|
133
|
+
end
|
134
|
+
|
135
|
+
# bulk-store addresses and txout mappings
|
136
|
+
def persist_addrs addrs
|
137
|
+
addr_txouts, new_addrs = [], []
|
138
|
+
addrs.group_by {|_, a| a }.each do |hash160, txouts|
|
139
|
+
if existing = @db[:addr][:hash160 => hash160]
|
140
|
+
txouts.each {|id, _| addr_txouts << [existing[:id], id] }
|
141
|
+
else
|
142
|
+
new_addrs << [hash160, txouts.map {|id, _| id }]
|
143
|
+
end
|
144
|
+
end
|
145
|
+
new_addr_ids = @db[:addr].insert_multiple(new_addrs.map {|hash160, txout_id|
|
146
|
+
{ hash160: hash160 } })
|
147
|
+
new_addr_ids.each.with_index do |addr_id, idx|
|
148
|
+
new_addrs[idx][1].each do |txout_id|
|
149
|
+
addr_txouts << [addr_id, txout_id]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
@db[:addr_txout].insert_multiple(addr_txouts.map {|addr_id, txout_id|
|
153
|
+
{ addr_id: addr_id, txout_id: txout_id }})
|
154
|
+
end
|
155
|
+
|
156
|
+
# prepare transaction data for storage
|
157
|
+
def tx_data tx
|
158
|
+
{ hash: tx.hash.htb.blob,
|
159
|
+
version: tx.ver, lock_time: tx.lock_time,
|
160
|
+
coinbase: tx.in.size == 1 && tx.in[0].coinbase?,
|
161
|
+
tx_size: tx.payload.bytesize }
|
162
|
+
end
|
163
|
+
|
164
|
+
# store transaction +tx+
|
165
|
+
def store_tx(tx, validate = true)
|
166
|
+
@log.debug { "Storing tx #{tx.hash} (#{tx.to_payload.bytesize} bytes)" }
|
167
|
+
tx.validator(self).validate(raise_errors: true) if validate
|
168
|
+
@db.transaction do
|
169
|
+
transaction = @db[:tx][:hash => tx.hash.htb.blob]
|
170
|
+
return transaction[:id] if transaction
|
171
|
+
tx_id = @db[:tx].insert(tx_data(tx))
|
172
|
+
tx.in.each_with_index {|i, idx| store_txin(tx_id, i, idx)}
|
173
|
+
tx.out.each_with_index {|o, idx| store_txout(tx_id, o, idx)}
|
174
|
+
tx_id
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# prepare txin data for storage
|
179
|
+
def txin_data tx_id, txin, idx
|
180
|
+
{ tx_id: tx_id, tx_idx: idx,
|
181
|
+
script_sig: txin.script_sig.blob,
|
182
|
+
prev_out: txin.prev_out.blob,
|
183
|
+
prev_out_index: txin.prev_out_index,
|
184
|
+
sequence: txin.sequence.unpack("V")[0] }
|
185
|
+
end
|
186
|
+
|
187
|
+
# store input +txin+
|
188
|
+
def store_txin(tx_id, txin, idx)
|
189
|
+
@db[:txin].insert(txin_data(tx_id, txin, idx))
|
190
|
+
end
|
191
|
+
|
192
|
+
# prepare txout data for storage
|
193
|
+
def txout_data tx_id, txout, idx, script_type
|
194
|
+
{ tx_id: tx_id, tx_idx: idx,
|
195
|
+
pk_script: txout.pk_script.blob,
|
196
|
+
value: txout.value, type: script_type }
|
197
|
+
end
|
198
|
+
|
199
|
+
# store output +txout+
|
200
|
+
def store_txout(tx_id, txout, idx)
|
201
|
+
script_type, addrs, names = *parse_script(txout, idx)
|
202
|
+
txout_id = @db[:txout].insert(txout_data(tx_id, txout, idx, script_type))
|
203
|
+
persist_addrs addrs.map {|i, h| [txout_id, h] }
|
204
|
+
names.each {|i, script| store_name(script, txout_id) }
|
205
|
+
txout_id
|
206
|
+
end
|
207
|
+
|
208
|
+
# delete transaction
|
209
|
+
# TODO: also delete blk_tx mapping
|
210
|
+
def delete_tx(hash)
|
211
|
+
log.debug { "Deleting tx #{hash} since all its outputs are spent" }
|
212
|
+
@db.transaction do
|
213
|
+
tx = get_tx(hash)
|
214
|
+
tx.in.each {|i| @db[:txin].where(:id => i.id).delete }
|
215
|
+
tx.out.each {|o| @db[:txout].where(:id => o.id).delete }
|
216
|
+
@db[:tx].where(:id => tx.id).delete
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
# check if block +blk_hash+ exists
|
221
|
+
def has_block(blk_hash)
|
222
|
+
!!@db[:blk].where(:hash => blk_hash.htb.blob).get(1)
|
223
|
+
end
|
224
|
+
|
225
|
+
# check if transaction +tx_hash+ exists
|
226
|
+
def has_tx(tx_hash)
|
227
|
+
!!@db[:tx].where(:hash => tx_hash.htb.blob).get(1)
|
228
|
+
end
|
229
|
+
|
230
|
+
# get head block (highest block from the MAIN chain)
|
231
|
+
def get_head
|
232
|
+
(@config[:cache_head] && @head) ? @head :
|
233
|
+
@head = wrap_block(@db[:blk].filter(:chain => MAIN).order(:depth).last)
|
234
|
+
end
|
235
|
+
|
236
|
+
def get_head_hash
|
237
|
+
(@config[:cache_head] && @head) ? @head.hash :
|
238
|
+
@head = @db[:blk].filter(:chain => MAIN).order(:depth).last[:hash].hth
|
239
|
+
end
|
240
|
+
|
241
|
+
# get depth of MAIN chain
|
242
|
+
def get_depth
|
243
|
+
depth = (@config[:cache_head] && @head) ? @head.depth :
|
244
|
+
@depth = @db[:blk].filter(:chain => MAIN).order(:depth).last[:depth] rescue nil
|
245
|
+
|
246
|
+
return -1 unless depth
|
247
|
+
depth
|
248
|
+
end
|
249
|
+
|
250
|
+
# get block for given +blk_hash+
|
251
|
+
def get_block(blk_hash)
|
252
|
+
wrap_block(@db[:blk][:hash => blk_hash.htb.blob])
|
253
|
+
end
|
254
|
+
|
255
|
+
# get block by given +depth+
|
256
|
+
def get_block_by_depth(depth)
|
257
|
+
wrap_block(@db[:blk][:depth => depth, :chain => MAIN])
|
258
|
+
end
|
259
|
+
|
260
|
+
# get block by given +prev_hash+
|
261
|
+
def get_block_by_prev_hash(prev_hash)
|
262
|
+
wrap_block(@db[:blk][:prev_hash => prev_hash.htb.blob, :chain => MAIN])
|
263
|
+
end
|
264
|
+
|
265
|
+
# get block by given +tx_hash+
|
266
|
+
def get_block_by_tx(tx_hash)
|
267
|
+
tx = @db[:tx][:hash => tx_hash.htb.blob]
|
268
|
+
return nil unless tx
|
269
|
+
parent = @db[:blk_tx][:tx_id => tx[:id]]
|
270
|
+
return nil unless parent
|
271
|
+
wrap_block(@db[:blk][:id => parent[:blk_id]])
|
272
|
+
end
|
273
|
+
|
274
|
+
# get block by given +id+
|
275
|
+
def get_block_by_id(block_id)
|
276
|
+
wrap_block(@db[:blk][:id => block_id])
|
277
|
+
end
|
278
|
+
|
279
|
+
# get transaction for given +tx_hash+
|
280
|
+
def get_tx(tx_hash)
|
281
|
+
wrap_tx(@db[:tx][:hash => tx_hash.htb.blob])
|
282
|
+
end
|
283
|
+
|
284
|
+
# get transaction by given +tx_id+
|
285
|
+
def get_tx_by_id(tx_id)
|
286
|
+
wrap_tx(@db[:tx][:id => tx_id])
|
287
|
+
end
|
288
|
+
|
289
|
+
# get corresponding Models::TxIn for the txout in transaction
|
290
|
+
# +tx_hash+ with index +txout_idx+
|
291
|
+
def get_txin_for_txout(tx_hash, txout_idx)
|
292
|
+
tx_hash = tx_hash.htb_reverse.blob
|
293
|
+
wrap_txin(@db[:txin][:prev_out => tx_hash, :prev_out_index => txout_idx])
|
294
|
+
end
|
295
|
+
|
296
|
+
def get_txout_by_id(txout_id)
|
297
|
+
wrap_txout(@db[:txout][:id => txout_id])
|
298
|
+
end
|
299
|
+
|
300
|
+
# get corresponding Models::TxOut for +txin+
|
301
|
+
def get_txout_for_txin(txin)
|
302
|
+
tx = @db[:tx][:hash => txin.prev_out.reverse.blob]
|
303
|
+
return nil unless tx
|
304
|
+
wrap_txout(@db[:txout][:tx_idx => txin.prev_out_index, :tx_id => tx[:id]])
|
305
|
+
end
|
306
|
+
|
307
|
+
# get all Models::TxOut matching given +script+
|
308
|
+
def get_txouts_for_pk_script(script)
|
309
|
+
txouts = @db[:txout].filter(:pk_script => script.blob).order(:id)
|
310
|
+
txouts.map{|txout| wrap_txout(txout)}
|
311
|
+
end
|
312
|
+
|
313
|
+
# get all Models::TxOut matching given +hash160+
|
314
|
+
def get_txouts_for_hash160(hash160, unconfirmed = false)
|
315
|
+
addr = @db[:addr][:hash160 => hash160]
|
316
|
+
return [] unless addr
|
317
|
+
txouts = @db[:addr_txout].where(:addr_id => addr[:id])
|
318
|
+
.map{|t| @db[:txout][:id => t[:txout_id]] }
|
319
|
+
.map{|o| wrap_txout(o) }
|
320
|
+
unless unconfirmed
|
321
|
+
txouts.select!{|o| @db[:blk][:id => o.get_tx.blk_id][:chain] == MAIN rescue false }
|
322
|
+
end
|
323
|
+
txouts
|
324
|
+
end
|
325
|
+
|
326
|
+
def get_txouts_for_name_hash(hash)
|
327
|
+
@db[:names].filter(hash: hash).map {|n| get_txout_by_id(n[:txout_id]) }
|
328
|
+
end
|
329
|
+
|
330
|
+
# get all unconfirmed Models::TxOut
|
331
|
+
def get_unconfirmed_tx
|
332
|
+
@db[:unconfirmed].map{|t| wrap_tx(t)}
|
333
|
+
end
|
334
|
+
|
335
|
+
# Grab the position of a tx in a given block
|
336
|
+
def get_idx_from_tx_hash(tx_hash)
|
337
|
+
tx = @db[:tx][:hash => tx_hash.htb.blob]
|
338
|
+
return nil unless tx
|
339
|
+
parent = @db[:blk_tx][:tx_id => tx[:id]]
|
340
|
+
return nil unless parent
|
341
|
+
return parent[:idx]
|
342
|
+
end
|
343
|
+
|
344
|
+
# wrap given +block+ into Models::Block
|
345
|
+
def wrap_block(block)
|
346
|
+
return nil unless block
|
347
|
+
|
348
|
+
data = {:id => block[:id], :depth => block[:depth], :chain => block[:chain], :work => block[:work].to_i, :hash => block[:hash].hth}
|
349
|
+
blk = Bitcoin::Storage::Models::Block.new(self, data)
|
350
|
+
|
351
|
+
blk.ver = block[:version]
|
352
|
+
blk.prev_block = block[:prev_hash].reverse
|
353
|
+
blk.mrkl_root = block[:mrkl_root].reverse
|
354
|
+
blk.time = block[:time].to_i
|
355
|
+
blk.bits = block[:bits]
|
356
|
+
blk.nonce = block[:nonce]
|
357
|
+
|
358
|
+
blk.aux_pow = Bitcoin::P::AuxPow.new(block[:aux_pow]) if block[:aux_pow]
|
359
|
+
|
360
|
+
db[:blk_tx].filter(blk_id: block[:id]).join(:tx, id: :tx_id)
|
361
|
+
.order(:idx).each {|tx| blk.tx << wrap_tx(tx, block[:id]) }
|
362
|
+
|
363
|
+
blk.recalc_block_hash
|
364
|
+
blk
|
365
|
+
end
|
366
|
+
|
367
|
+
# wrap given +transaction+ into Models::Transaction
|
368
|
+
def wrap_tx(transaction, block_id = nil)
|
369
|
+
return nil unless transaction
|
370
|
+
|
371
|
+
block_id ||= @db[:blk_tx].join(:blk, id: :blk_id)
|
372
|
+
.where(tx_id: transaction[:id], chain: 0).first[:blk_id] rescue nil
|
373
|
+
|
374
|
+
data = {id: transaction[:id], blk_id: block_id}
|
375
|
+
tx = Bitcoin::Storage::Models::Tx.new(self, data)
|
376
|
+
|
377
|
+
inputs = db[:txin].filter(:tx_id => transaction[:id]).order(:tx_idx)
|
378
|
+
inputs.each { |i| tx.add_in(wrap_txin(i)) }
|
379
|
+
|
380
|
+
outputs = db[:txout].filter(:tx_id => transaction[:id]).order(:tx_idx)
|
381
|
+
outputs.each { |o| tx.add_out(wrap_txout(o)) }
|
382
|
+
tx.ver = transaction[:version]
|
383
|
+
tx.lock_time = transaction[:lock_time]
|
384
|
+
tx.hash = tx.hash_from_payload(tx.to_payload)
|
385
|
+
tx
|
386
|
+
end
|
387
|
+
|
388
|
+
# wrap given +input+ into Models::TxIn
|
389
|
+
def wrap_txin(input)
|
390
|
+
return nil unless input
|
391
|
+
data = {:id => input[:id], :tx_id => input[:tx_id], :tx_idx => input[:tx_idx]}
|
392
|
+
txin = Bitcoin::Storage::Models::TxIn.new(self, data)
|
393
|
+
txin.prev_out = input[:prev_out]
|
394
|
+
txin.prev_out_index = input[:prev_out_index]
|
395
|
+
txin.script_sig_length = input[:script_sig].bytesize
|
396
|
+
txin.script_sig = input[:script_sig]
|
397
|
+
txin.sequence = [input[:sequence]].pack("V")
|
398
|
+
txin
|
399
|
+
end
|
400
|
+
|
401
|
+
# wrap given +output+ into Models::TxOut
|
402
|
+
def wrap_txout(output)
|
403
|
+
return nil unless output
|
404
|
+
data = {:id => output[:id], :tx_id => output[:tx_id], :tx_idx => output[:tx_idx],
|
405
|
+
:hash160 => output[:hash160], :type => SCRIPT_TYPES[output[:type]]}
|
406
|
+
txout = Bitcoin::Storage::Models::TxOut.new(self, data)
|
407
|
+
txout.value = output[:value]
|
408
|
+
txout.pk_script = output[:pk_script]
|
409
|
+
txout
|
410
|
+
end
|
411
|
+
|
412
|
+
# check data consistency of the top +count+ blocks. validates that
|
413
|
+
# - the block hash computed from the stored data is the same
|
414
|
+
# - the prev_hash is the same as the previous blocks' hash
|
415
|
+
# - the merkle root computed from all transactions is correct
|
416
|
+
def check_consistency count = 1000
|
417
|
+
return if get_depth < 1 || count <= 0
|
418
|
+
depth = get_depth
|
419
|
+
count = depth - 1 if count == -1
|
420
|
+
count = depth - 1 if count >= depth
|
421
|
+
log.info { "Checking consistency of last #{count} blocks..." }
|
422
|
+
prev_blk = get_block_by_depth(depth - count - 1)
|
423
|
+
(depth - count).upto(depth).each do |depth|
|
424
|
+
blk = get_block_by_depth(depth)
|
425
|
+
raise "Block hash #{blk.depth} invalid!" unless blk.hash == blk.recalc_block_hash
|
426
|
+
raise "Prev hash #{blk.depth} invalid!" unless blk.prev_block.reverse.hth == prev_blk.hash
|
427
|
+
raise "Merkle root #{blk.depth} invalid!" unless blk.verify_mrkl_root
|
428
|
+
print "#{blk.hash} #{blk.depth} OK\r"
|
429
|
+
prev_blk = blk
|
430
|
+
end
|
431
|
+
log.info { "Last #{count} blocks are consistent." }
|
432
|
+
end
|
433
|
+
|
434
|
+
end
|
435
|
+
|
436
|
+
end
|