bitcoin-ruby 0.0.6 → 0.0.7
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/.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
data/lib/bitcoin/validation.rb
DELETED
|
@@ -1,400 +0,0 @@
|
|
|
1
|
-
# encoding: ascii-8bit
|
|
2
|
-
|
|
3
|
-
# Validates blocks and transactions before they are accepted into the local blockchain.
|
|
4
|
-
# There are two modes of validation, "syntax" and "context". "syntax" validates everything
|
|
5
|
-
# that can be validated without access to the rest of the blockchain, for example that the
|
|
6
|
-
# block hash matches the claimed difficulty, and the tx hashes add up to the given merkle
|
|
7
|
-
# root, etc. The "context" rules include the checks that need to cross-reference data
|
|
8
|
-
# against the local database, like comparing the difficulty target to the last blocks, or
|
|
9
|
-
# checking for doublespends. (Suggestions for better names for these modes are welcome!)
|
|
10
|
-
# Everything accepted into the local storage should at least be syntax-validated, but it
|
|
11
|
-
# should be possible to skip context-validation when the current block is already known,
|
|
12
|
-
# for example when checkpoints are used.
|
|
13
|
-
module Bitcoin::Validation
|
|
14
|
-
|
|
15
|
-
class ValidationError < StandardError
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class Block
|
|
20
|
-
attr_accessor :block, :store, :prev_block, :error
|
|
21
|
-
|
|
22
|
-
RULES = {
|
|
23
|
-
syntax: [:hash, :tx_list, :bits, :max_timestamp, :coinbase, :coinbase_scriptsig, :mrkl_root, :transactions_syntax],
|
|
24
|
-
context: [:prev_hash, :difficulty, :coinbase_value, :min_timestamp, :transactions_context]
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
# TODO merged mining validations
|
|
28
|
-
if Bitcoin.namecoin?
|
|
29
|
-
RULES[:syntax] -= [:bits, :coinbase, :coinbase_scriptsig, :mrkl_root]
|
|
30
|
-
RULES[:context] -= [:difficulty, :coinbase_value]
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
if Bitcoin.litecoin?
|
|
34
|
-
RULES[:syntax] -= [:bits]
|
|
35
|
-
RULES[:syntax] += [:scrypt_bits]
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
# validate block rules. +opts+ are:
|
|
39
|
-
# rules:: which rulesets to validate (default: [:syntax, :context])
|
|
40
|
-
# raise_errors:: whether to raise ValidationError on failure (default: false)
|
|
41
|
-
def validate(opts = {})
|
|
42
|
-
return true if KNOWN_EXCEPTIONS.include?(block.hash)
|
|
43
|
-
opts[:rules] ||= [:syntax, :context]
|
|
44
|
-
opts[:rules].each do |name|
|
|
45
|
-
store.log.debug { "validating block #{name} #{block.hash} (#{block.to_payload.bytesize} bytes)" }
|
|
46
|
-
RULES[name].each.with_index do |rule, i|
|
|
47
|
-
unless (res = send(rule)) && res == true
|
|
48
|
-
raise ValidationError, "block error: #{name} check #{i} - #{rule} failed" if opts[:raise_errors]
|
|
49
|
-
@error = [rule, res]
|
|
50
|
-
return false
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
true
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# setup new validator for given +block+, validating context with +store+,
|
|
58
|
-
# optionally passing the +prev_block+ for optimization.
|
|
59
|
-
def initialize block, store, prev_block = nil
|
|
60
|
-
@block, @store, @error = block, store, nil
|
|
61
|
-
@prev_block = prev_block || store.get_block(block.prev_block.reverse_hth)
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
# check that block hash matches header
|
|
65
|
-
def hash
|
|
66
|
-
claimed = block.hash; real = block.recalc_block_hash
|
|
67
|
-
claimed == real || [claimed, real]
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# check that block has at least one tx (the coinbase)
|
|
71
|
-
def tx_list
|
|
72
|
-
block.tx.any? || block.tx.size
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
# check that block hash matches claimed bits
|
|
76
|
-
def bits
|
|
77
|
-
actual = block.hash.to_i(16)
|
|
78
|
-
expected = Bitcoin.decode_compact_bits(block.bits).to_i(16)
|
|
79
|
-
actual <= expected || [actual, expected]
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# check that block hash matches claimed bits using Scrypt hash
|
|
83
|
-
def scrypt_bits
|
|
84
|
-
actual = block.recalc_block_scrypt_hash.to_i(16)
|
|
85
|
-
expected = Bitcoin.decode_compact_bits(block.bits).to_i(16)
|
|
86
|
-
actual <= expected || [actual, expected]
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
# check that block time is not greater than max
|
|
90
|
-
def max_timestamp
|
|
91
|
-
time, max = block.time, Time.now.to_i + 2*60*60
|
|
92
|
-
time < max || [time, max]
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
# check that coinbase is present
|
|
96
|
-
def coinbase
|
|
97
|
-
coinbase, *rest = block.tx.map{|t| t.inputs.size == 1 && t.inputs.first.coinbase? }
|
|
98
|
-
(coinbase && rest.none?) || [coinbase ? 1 : 0, rest.select{|r| r}.size]
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
# check that coinbase scriptsig is valid
|
|
102
|
-
def coinbase_scriptsig
|
|
103
|
-
size = block.tx.first.in.first.script_sig.bytesize
|
|
104
|
-
size.between?(2,100) || [size, 2, 100]
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
# check that coinbase value is valid; no more than reward + fees
|
|
108
|
-
def coinbase_value
|
|
109
|
-
reward = ((50.0 / (2 ** (store.get_depth / Bitcoin::REWARD_DROP.to_f).floor)) * 1e8).to_i
|
|
110
|
-
fees = 0
|
|
111
|
-
block.tx[1..-1].map.with_index do |t, idx|
|
|
112
|
-
val = tx_validators[idx]
|
|
113
|
-
fees += t.in.map.with_index {|i, idx|
|
|
114
|
-
val.prev_txs[idx].out[i.prev_out_index].value rescue 0
|
|
115
|
-
}.inject(:+)
|
|
116
|
-
val.clear_cache # memory optimization on large coinbases, see testnet3 block 4110
|
|
117
|
-
end
|
|
118
|
-
coinbase_output = block.tx[0].out.map(&:value).inject(:+)
|
|
119
|
-
coinbase_output <= reward + fees || [coinbase_output, reward, fees]
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
# check that merkle root matches transaction hashes
|
|
123
|
-
def mrkl_root
|
|
124
|
-
actual, expected = block.mrkl_root.reverse_hth, Bitcoin.hash_mrkl_tree(block.tx.map(&:hash))[-1]
|
|
125
|
-
actual == expected || [actual, expected]
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def prev_hash
|
|
129
|
-
@prev_block && @prev_block.hash == block.prev_block.reverse_hth
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
# check that bits satisfy required difficulty
|
|
133
|
-
def difficulty
|
|
134
|
-
return true if Bitcoin.network[:no_difficulty] == true
|
|
135
|
-
block.bits == next_bits_required || [block.bits, next_bits_required]
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
# check that timestamp is newer than the median of the last 11 blocks
|
|
139
|
-
def min_timestamp
|
|
140
|
-
return true if store.get_depth <= 11
|
|
141
|
-
d = store.get_depth
|
|
142
|
-
first = store.db[:blk][hash: block.prev_block.reverse.blob]
|
|
143
|
-
times = [first[:time]]
|
|
144
|
-
(10).times { first = store.db[:blk][hash: first[:prev_hash].blob]
|
|
145
|
-
times << first[:time] }
|
|
146
|
-
times.sort!
|
|
147
|
-
mid, rem = times.size.divmod(2)
|
|
148
|
-
min_time = (rem == 0 ? times[mid-1, 2].inject(:+) / 2.0 : times[mid])
|
|
149
|
-
|
|
150
|
-
block.time > min_time || [block.time, min_time]
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
# Run all syntax checks on transactions
|
|
154
|
-
def transactions_syntax
|
|
155
|
-
# check if there are no double spends within this block
|
|
156
|
-
return false if block.tx.map(&:in).flatten.map {|i| [i.prev_out, i.prev_out_index] }.uniq! != nil
|
|
157
|
-
|
|
158
|
-
tx_validators.all?{|v|
|
|
159
|
-
begin
|
|
160
|
-
v.validate(rules: [:syntax], raise_errors: true)
|
|
161
|
-
rescue ValidationError
|
|
162
|
-
store.log.info { $!.message }
|
|
163
|
-
return false
|
|
164
|
-
end
|
|
165
|
-
}
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
# Run all context checks on transactions
|
|
169
|
-
def transactions_context
|
|
170
|
-
tx_validators.all?{|v|
|
|
171
|
-
begin
|
|
172
|
-
v.validate(rules: [:context], raise_errors: true)
|
|
173
|
-
rescue ValidationError
|
|
174
|
-
store.log.info { $!.message }
|
|
175
|
-
return false
|
|
176
|
-
end
|
|
177
|
-
}
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
# Get validators for all tx objects in the current block
|
|
181
|
-
def tx_validators
|
|
182
|
-
@tx_validators ||= block.tx[1..-1].map {|tx| tx.validator(store, block, self)}
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
# Fetch all prev_txs that will be needed for validation
|
|
186
|
-
# Used for optimization in tx validators
|
|
187
|
-
def prev_txs_hash
|
|
188
|
-
@prev_tx_hash ||= (
|
|
189
|
-
inputs = block.tx[1..-1].map {|tx| tx.in }.flatten
|
|
190
|
-
txs = store.get_txs(inputs.map{|i| i.prev_out.reverse_hth })
|
|
191
|
-
Hash[*txs.map {|tx| [tx.hash, tx] }.flatten]
|
|
192
|
-
)
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
# Fetch all prev_outs that already have a next_in, i.e. are already spent.
|
|
196
|
-
def spent_outs_txins
|
|
197
|
-
@spent_outs_txins ||= (
|
|
198
|
-
next_ins = store.get_txins_for_txouts(block.tx[1..-1].map(&:in).flatten.map.with_index {|txin, idx| [txin.prev_out.reverse_hth, txin.prev_out_index] })
|
|
199
|
-
# Only returns next_ins that are in blocks in the main chain
|
|
200
|
-
next_ins.select {|i| store.get_block_id_for_tx_id(i.tx_id) }
|
|
201
|
-
)
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
def next_bits_required
|
|
205
|
-
retarget = (Bitcoin.network[:retarget_interval] || Bitcoin::RETARGET_INTERVAL)
|
|
206
|
-
index = (prev_block.depth + 1) / retarget
|
|
207
|
-
max_target = Bitcoin.decode_compact_bits(Bitcoin.network[:proof_of_work_limit]).to_i(16)
|
|
208
|
-
return Bitcoin.network[:proof_of_work_limit] if index == 0
|
|
209
|
-
return prev_block.bits if (prev_block.depth + 1) % retarget != 0
|
|
210
|
-
last = store.db[:blk][hash: prev_block.hash.htb.blob]
|
|
211
|
-
first = store.db[:blk][hash: last[:prev_hash].blob]
|
|
212
|
-
(retarget - 2).times { first = store.db[:blk][hash: first[:prev_hash].blob] }
|
|
213
|
-
|
|
214
|
-
nActualTimespan = last[:time] - first[:time]
|
|
215
|
-
nTargetTimespan = retarget * 600
|
|
216
|
-
|
|
217
|
-
nActualTimespan = [nActualTimespan, nTargetTimespan/4].max
|
|
218
|
-
nActualTimespan = [nActualTimespan, nTargetTimespan*4].min
|
|
219
|
-
|
|
220
|
-
target = Bitcoin.decode_compact_bits(last[:bits]).to_i(16)
|
|
221
|
-
new_target = [max_target, (target * nActualTimespan)/nTargetTimespan].min
|
|
222
|
-
Bitcoin.encode_compact_bits new_target.to_s(16)
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
KNOWN_EXCEPTIONS = [
|
|
226
|
-
Bitcoin.network[:genesis_hash], # genesis block
|
|
227
|
-
"00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec", # BIP30 exception
|
|
228
|
-
"00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721", # BIP30 exception
|
|
229
|
-
]
|
|
230
|
-
|
|
231
|
-
end
|
|
232
|
-
|
|
233
|
-
class Tx
|
|
234
|
-
attr_accessor :tx, :store, :error, :block_validator
|
|
235
|
-
|
|
236
|
-
RULES = {
|
|
237
|
-
syntax: [:hash, :lists, :max_size, :output_values, :inputs, :lock_time, :standard],
|
|
238
|
-
context: [:prev_out, :signatures, :not_spent, :input_values, :output_sum]
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
# validate tx rules. +opts+ are:
|
|
242
|
-
# rules:: which rulesets to validate (default: [:syntax, :context])
|
|
243
|
-
# raise_errors:: whether to raise ValidationError on failure (default: false)
|
|
244
|
-
def validate(opts = {})
|
|
245
|
-
return true if KNOWN_EXCEPTIONS.include?(tx.hash)
|
|
246
|
-
opts[:rules] ||= [:syntax, :context]
|
|
247
|
-
opts[:rules].each do |name|
|
|
248
|
-
store.log.debug { "validating tx #{name} #{tx.hash} (#{tx.to_payload.bytesize} bytes)" } if store
|
|
249
|
-
RULES[name].each.with_index do |rule, i|
|
|
250
|
-
unless (res = send(rule)) && res == true
|
|
251
|
-
raise ValidationError, "tx error: #{name} check #{i} - #{rule} failed" if opts[:raise_errors]
|
|
252
|
-
@error = [rule, res]
|
|
253
|
-
return false
|
|
254
|
-
end
|
|
255
|
-
end
|
|
256
|
-
end
|
|
257
|
-
clear_cache # memory optimizatons
|
|
258
|
-
true
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
KNOWN_EXCEPTIONS = [
|
|
262
|
-
# p2sh with invalid inner script, accepted by old miner before 4-2012 switchover
|
|
263
|
-
"6a26d2ecb67f27d1fa5524763b49029d7106e91e3cc05743073461a719776192",
|
|
264
|
-
# p2sh with invalid inner script, accepted by old miner before 4-2012 switchover (testnet)
|
|
265
|
-
"b3c19d78b4953b694717a47d9852f8ea1ccd4cf93a45ba2e43a0f97d7cdb2655"
|
|
266
|
-
]
|
|
267
|
-
|
|
268
|
-
# Setup new validator for given +tx+, validating context with +store+.
|
|
269
|
-
# Also needs the +block+ that includes the tx to be validated, to find
|
|
270
|
-
# prev_outs for chains of txs inside the block.
|
|
271
|
-
# Optionally accepts the validator object for the block, to optimize fetching
|
|
272
|
-
# prev_txs and checking for doublespends.
|
|
273
|
-
def initialize(tx, store, block = nil, block_validator = nil)
|
|
274
|
-
@tx, @store, @block, @errors = tx, store, block, []
|
|
275
|
-
@block_validator = block_validator
|
|
276
|
-
end
|
|
277
|
-
|
|
278
|
-
# check that tx hash matches data
|
|
279
|
-
def hash
|
|
280
|
-
generated_hash = tx.generate_hash(tx.to_payload)
|
|
281
|
-
tx.hash == generated_hash || [tx.hash, generated_hash]
|
|
282
|
-
end
|
|
283
|
-
|
|
284
|
-
# check that tx has at least one input and one output
|
|
285
|
-
def lists
|
|
286
|
-
(tx.in.any? && tx.out.any?) || [tx.in.size, tx.out.size]
|
|
287
|
-
end
|
|
288
|
-
|
|
289
|
-
# check that tx size doesn't exceed MAX_BLOCK_SIZE.
|
|
290
|
-
def max_size
|
|
291
|
-
tx.to_payload.bytesize <= Bitcoin::MAX_BLOCK_SIZE || [tx.to_payload.bytesize, Bitcoin::MAX_BLOCK_SIZE]
|
|
292
|
-
end
|
|
293
|
-
|
|
294
|
-
# check that total output value doesn't exceed MAX_MONEY.
|
|
295
|
-
def output_values
|
|
296
|
-
total = tx.out.inject(0) {|e, out| e + out.value }
|
|
297
|
-
total <= Bitcoin::network[:max_money] || [total, Bitcoin::network[:max_money]]
|
|
298
|
-
end
|
|
299
|
-
|
|
300
|
-
# check that none of the inputs is coinbase
|
|
301
|
-
# (coinbase tx do not get validated)
|
|
302
|
-
def inputs
|
|
303
|
-
tx.inputs.none?(&:coinbase?) || [tx.inputs.index(tx.inputs.find(&:coinbase?))]
|
|
304
|
-
end
|
|
305
|
-
|
|
306
|
-
# check that lock_time doesn't exceed INT_MAX
|
|
307
|
-
def lock_time
|
|
308
|
-
tx.lock_time <= Bitcoin::UINT32_MAX || [tx.lock_time, Bitcoin::UINT32_MAX]
|
|
309
|
-
end
|
|
310
|
-
|
|
311
|
-
# check that min_size is at least 86 bytes
|
|
312
|
-
# (smaller tx can't be valid / do anything useful)
|
|
313
|
-
def min_size
|
|
314
|
-
tx.to_payload.bytesize >= 86 || [tx.to_payload.bytesize, 86]
|
|
315
|
-
end
|
|
316
|
-
|
|
317
|
-
# check that tx matches "standard" rules.
|
|
318
|
-
# this is currently disabled since not all miners enforce it.
|
|
319
|
-
def standard
|
|
320
|
-
return true # not enforced by all miners
|
|
321
|
-
return false unless min_size
|
|
322
|
-
tx.out.all? {|o| Bitcoin::Script.new(o.pk_script).is_standard? }
|
|
323
|
-
end
|
|
324
|
-
|
|
325
|
-
# check that all prev_outs exist
|
|
326
|
-
# (and are in a block in the main chain, or the current block; see #prev_txs)
|
|
327
|
-
def prev_out
|
|
328
|
-
missing = tx.in.reject.with_index {|txin, idx|
|
|
329
|
-
prev_txs[idx].out[txin.prev_out_index] rescue false }
|
|
330
|
-
return true if prev_txs.size == tx.in.size && missing.empty?
|
|
331
|
-
|
|
332
|
-
missing.each {|i| store.log.warn { "prev out #{i.prev_out.reverse_hth}:#{i.prev_out_index} missing" } }
|
|
333
|
-
missing.map {|i| [i.prev_out.reverse_hth, i.prev_out_index] }
|
|
334
|
-
end
|
|
335
|
-
|
|
336
|
-
# TODO: validate coinbase maturity
|
|
337
|
-
|
|
338
|
-
# check that all input signatures are valid
|
|
339
|
-
def signatures
|
|
340
|
-
sigs = tx.in.map.with_index {|txin, idx| tx.verify_input_signature(idx, prev_txs[idx], (@block ? @block.time : 0)) }
|
|
341
|
-
sigs.all? || sigs.map.with_index {|s, i| s ? nil : i }.compact
|
|
342
|
-
end
|
|
343
|
-
|
|
344
|
-
# check that none of the prev_outs are already spent in the main chain or in the current block
|
|
345
|
-
def not_spent
|
|
346
|
-
# if we received cached spents, use it
|
|
347
|
-
return block_validator.spent_outs_txins.empty? if block_validator
|
|
348
|
-
|
|
349
|
-
# find all spent txouts
|
|
350
|
-
next_ins = store.get_txins_for_txouts(tx.in.map.with_index {|txin, idx| [txin.prev_out.reverse_hth, txin.prev_out_index] })
|
|
351
|
-
|
|
352
|
-
# no txouts found spending these txins, we can safely return true
|
|
353
|
-
return true if next_ins.empty?
|
|
354
|
-
|
|
355
|
-
# there were some txouts spending these txins, verify that they are not on the main chain
|
|
356
|
-
next_ins.select! {|i| i.get_tx.blk_id } # blk_id is only set for tx in the main chain
|
|
357
|
-
return true if next_ins.empty?
|
|
358
|
-
|
|
359
|
-
# now we know some txouts are already spent, return tx_idxs for debugging purposes
|
|
360
|
-
return next_ins.map {|i| i.get_prev_out.tx_idx }
|
|
361
|
-
end
|
|
362
|
-
|
|
363
|
-
# check that the total input value doesn't exceed MAX_MONEY
|
|
364
|
-
def input_values
|
|
365
|
-
total_in < Bitcoin::network[:max_money] || [total_in, Bitcoin::network[:max_money]]
|
|
366
|
-
end
|
|
367
|
-
|
|
368
|
-
# check that the total output value doesn't exceed the total input value
|
|
369
|
-
def output_sum
|
|
370
|
-
total_in >= total_out || [total_out, total_in]
|
|
371
|
-
end
|
|
372
|
-
|
|
373
|
-
# empty prev txs cache
|
|
374
|
-
def clear_cache
|
|
375
|
-
@prev_txs = nil
|
|
376
|
-
@total_in = nil
|
|
377
|
-
@total_out = nil
|
|
378
|
-
end
|
|
379
|
-
|
|
380
|
-
# collect prev_txs needed to verify the inputs of this tx.
|
|
381
|
-
# only returns tx that are in a block in the main chain or the current block.
|
|
382
|
-
def prev_txs
|
|
383
|
-
@prev_txs ||= tx.in.map {|i|
|
|
384
|
-
prev_tx = block_validator ? block_validator.prev_txs_hash[i.prev_out.reverse_hth] : store.get_tx(i.prev_out.reverse_hth)
|
|
385
|
-
next prev_tx if prev_tx && prev_tx.blk_id # blk_id is set only if it's in the main chain
|
|
386
|
-
@block.tx.find {|t| t.binary_hash == i.prev_out } if @block
|
|
387
|
-
}.compact
|
|
388
|
-
end
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
def total_in
|
|
392
|
-
@total_in ||= tx.in.each_with_index.inject(0){|acc,(input,idx)| acc + prev_txs[idx].out[input.prev_out_index].value }
|
|
393
|
-
end
|
|
394
|
-
|
|
395
|
-
def total_out
|
|
396
|
-
@total_out ||= tx.out.inject(0){|acc,output| acc + output.value }
|
|
397
|
-
end
|
|
398
|
-
|
|
399
|
-
end
|
|
400
|
-
end
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# encoding: ascii-8bit
|
|
2
|
-
|
|
3
|
-
module Bitcoin::Wallet
|
|
4
|
-
|
|
5
|
-
# select unspent txouts to be used by the Wallet when creating a new transaction
|
|
6
|
-
class SimpleCoinSelector
|
|
7
|
-
|
|
8
|
-
# create coinselector with given +txouts+
|
|
9
|
-
def initialize txouts
|
|
10
|
-
@txouts = txouts
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
# select txouts needed to spend +value+ btc (base units)
|
|
14
|
-
def select(value)
|
|
15
|
-
txouts = []
|
|
16
|
-
@txouts.each do |txout|
|
|
17
|
-
begin
|
|
18
|
-
next if txout.get_next_in
|
|
19
|
-
next if Bitcoin.namecoin? && txout.type.to_s =~ /^name_/
|
|
20
|
-
next unless txout.get_address
|
|
21
|
-
next unless txout.get_tx.get_block
|
|
22
|
-
txouts << txout
|
|
23
|
-
return txouts if txouts.map(&:value).inject(:+) >= value
|
|
24
|
-
rescue
|
|
25
|
-
p $!
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
nil
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
end
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
# encoding: ascii-8bit
|
|
2
|
-
|
|
3
|
-
module Bitcoin::Wallet
|
|
4
|
-
|
|
5
|
-
# Deterministic key generator as described in
|
|
6
|
-
# https://bitcointalk.org/index.php?topic=11665.0.
|
|
7
|
-
#
|
|
8
|
-
# Takes a seed and generates an arbitrary amount of keys.
|
|
9
|
-
# Protects against brute-force attacks by requiring the
|
|
10
|
-
# key hash to fit a difficulty target, much like the block chain.
|
|
11
|
-
class KeyGenerator
|
|
12
|
-
|
|
13
|
-
# difficulty target (0x0000FFFF00000000000000000000000000000000000000000000000000000000)
|
|
14
|
-
DEFAULT_TARGET = 0x0000FFFF00000000000000000000000000000000000000000000000000000000
|
|
15
|
-
|
|
16
|
-
attr_accessor :seed, :nonce, :target
|
|
17
|
-
|
|
18
|
-
# Initialize key generator with optional +seed+ and +nonce+ and +target+.
|
|
19
|
-
# [seed] the seed data for the keygenerator (default: random)
|
|
20
|
-
# [nonce] the nonce required to satisfy the target (default: computed)
|
|
21
|
-
# [target] custom difficulty target (default: DEFAULT_TARGET)
|
|
22
|
-
#
|
|
23
|
-
# Example:
|
|
24
|
-
# g = KeyGenerator.new # random seed, computed nonce, default target
|
|
25
|
-
# KeyGenerator.new(g.seed)
|
|
26
|
-
# KeyGenerator.new(g.seed, g.nonce)
|
|
27
|
-
# g.get_key(0) #=> <Bitcoin::Key>
|
|
28
|
-
#
|
|
29
|
-
# Note: When initializing without seed, you should obviously save the
|
|
30
|
-
# seed once it is generated. Saving the nonce is optional; it only saves time.
|
|
31
|
-
def initialize seed = nil, nonce = nil, target = nil
|
|
32
|
-
@seed = seed || OpenSSL::Random.random_bytes(64)
|
|
33
|
-
@target = target || DEFAULT_TARGET
|
|
34
|
-
@nonce = check_nonce(nonce)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# get key number +n+ from chain
|
|
38
|
-
def get_key(n = 0)
|
|
39
|
-
key = get_hash(@seed, @nonce)
|
|
40
|
-
(n + 1).times { key = sha256(key) }
|
|
41
|
-
key
|
|
42
|
-
Bitcoin::Key.new(key.unpack("H*")[0])
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# find a nonce that leads to the privkey satisfying the target
|
|
46
|
-
def find_nonce
|
|
47
|
-
n = 0
|
|
48
|
-
n += 1 while !check_target(get_hash(@seed, n))
|
|
49
|
-
n
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
protected
|
|
53
|
-
|
|
54
|
-
# check the nonce; compute if missing, raise if invalid.
|
|
55
|
-
def check_nonce(nonce)
|
|
56
|
-
return find_nonce unless nonce
|
|
57
|
-
# check_target(get_hash(@seed, nonce)) ? nonce : find_nonce
|
|
58
|
-
raise ArgumentError, "Nonce invalid." unless check_target(get_hash(@seed, nonce))
|
|
59
|
-
nonce
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# check if given +hash+ satisfies the difficulty target
|
|
63
|
-
def check_target(hash)
|
|
64
|
-
hash.unpack("H*")[0].to_i(16) < @target
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
# compute a single SHA256 hash for +d+.
|
|
68
|
-
def sha256(d); Digest::SHA256.digest(d); end
|
|
69
|
-
|
|
70
|
-
# get the hash corresponding to +seed+ and +n+.
|
|
71
|
-
def get_hash(seed, n)
|
|
72
|
-
sha256( sha256(seed) + sha256(n.to_s) )
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
end
|