bitcoin-ruby 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/.travis.yml +2 -2
  4. data/COPYING +1 -1
  5. data/Gemfile +5 -11
  6. data/README.rdoc +11 -5
  7. data/Rakefile +5 -0
  8. data/bin/bitcoin_node +11 -29
  9. data/bin/bitcoin_node_cli +81 -0
  10. data/bin/bitcoin_wallet +9 -6
  11. data/doc/NODE.rdoc +79 -26
  12. data/examples/bbe_verify_tx.rb +1 -1
  13. data/examples/index_nhash.rb +24 -0
  14. data/examples/reindex_p2sh_addrs.rb +44 -0
  15. data/lib/bitcoin.rb +135 -20
  16. data/lib/bitcoin/builder.rb +233 -63
  17. data/lib/bitcoin/key.rb +89 -16
  18. data/lib/bitcoin/litecoin.rb +13 -11
  19. data/lib/bitcoin/namecoin.rb +5 -4
  20. data/lib/bitcoin/network/command_client.rb +23 -13
  21. data/lib/bitcoin/network/command_handler.rb +336 -131
  22. data/lib/bitcoin/network/connection_handler.rb +14 -13
  23. data/lib/bitcoin/network/node.rb +61 -20
  24. data/lib/bitcoin/protocol.rb +5 -1
  25. data/lib/bitcoin/protocol/block.rb +15 -3
  26. data/lib/bitcoin/protocol/parser.rb +3 -3
  27. data/lib/bitcoin/protocol/tx.rb +82 -20
  28. data/lib/bitcoin/protocol/txin.rb +7 -0
  29. data/lib/bitcoin/protocol/txout.rb +12 -9
  30. data/lib/bitcoin/script.rb +329 -75
  31. data/lib/bitcoin/storage/dummy/dummy_store.rb +23 -4
  32. data/lib/bitcoin/storage/models.rb +6 -11
  33. data/lib/bitcoin/storage/sequel/migrations/005_change_tx_hash_to_bytea.rb +14 -0
  34. data/lib/bitcoin/storage/sequel/migrations/006_add_tx_nhash.rb +31 -0
  35. data/lib/bitcoin/storage/sequel/migrations/007_add_prev_out_index_index.rb +16 -0
  36. data/lib/bitcoin/storage/sequel/migrations/008_add_txin_p2sh_type.rb +31 -0
  37. data/lib/bitcoin/storage/sequel/migrations/009_add_addrs_type.rb +56 -0
  38. data/lib/bitcoin/storage/sequel/sequel_store.rb +168 -70
  39. data/lib/bitcoin/storage/storage.rb +161 -97
  40. data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +1 -1
  41. data/lib/bitcoin/storage/utxo/migrations/004_add_addrs_type.rb +14 -0
  42. data/lib/bitcoin/storage/utxo/utxo_store.rb +25 -12
  43. data/lib/bitcoin/validation.rb +87 -56
  44. data/lib/bitcoin/version.rb +1 -1
  45. data/spec/bitcoin/bitcoin_spec.rb +38 -0
  46. data/spec/bitcoin/builder_spec.rb +177 -0
  47. data/spec/bitcoin/fixtures/litecoin-tx-f5aa30f574e3b6f1a3d99c07a6356ba812aabb9661e1d5f71edff828cbd5c996.json +259 -0
  48. data/spec/bitcoin/fixtures/rawblock-testnet-265322.bin +0 -0
  49. data/spec/bitcoin/fixtures/tx-0295028ef826b2a188409cb905b631faebb9bb3cdf14510571c5f4bd8591338f.json +64 -0
  50. data/spec/bitcoin/fixtures/tx-03339a725007a279484fb6f5361f522dd1cf4d0923d30e6b973290dba4275f92.json +64 -0
  51. data/spec/bitcoin/fixtures/tx-0ce7e5238fbdb6c086cf1b384b21b827e91cc23f360417265874a5a0d86ce367.json +64 -0
  52. data/spec/bitcoin/fixtures/tx-0ef34c49f630aea17df0080728b0fc67bf5f87fbda936934a4b11b4a69d7821e.json +64 -0
  53. data/spec/bitcoin/fixtures/tx-1129d2a8bd5bb3a81e54dc96a90f1f6b2544575748caa17243470935c5dd91b7.json +28 -0
  54. data/spec/bitcoin/fixtures/tx-19aa42fee0fa57c45d3b16488198b27caaacc4ff5794510d0c17f173f05587ff.json +23 -0
  55. data/spec/bitcoin/fixtures/tx-1a4f3b9dc4494aeedeb39f30dd37e60541b2abe3ed4977992017cc0ad4f44956.json +64 -0
  56. data/spec/bitcoin/fixtures/tx-1f9191dcf2b1844ca28c6ef4b969e1d5fab70a5e3c56b7007949e55851cb0c4f.json +64 -0
  57. data/spec/bitcoin/fixtures/tx-22cd5fef23684d7b304e119bedffde6f54538d3d54a5bfa237e20dc2d9b4b5ad.json +64 -0
  58. data/spec/bitcoin/fixtures/tx-2958fb00b4fd6fe0353503b886eb9a193d502f4fd5fc042d5e03216ba918bbd6.json +64 -0
  59. data/spec/bitcoin/fixtures/tx-29f277145749ad6efbed3ae6ce301f8d33c585ec26b7c044ad93c2f866e9e942.json +64 -0
  60. data/spec/bitcoin/fixtures/tx-2c5e5376c20e9cc78d0fb771730e5d840cc2096eff0ef045b599fe92475ace1c.json +28 -0
  61. data/spec/bitcoin/fixtures/tx-2c63aa814701cef5dbd4bbaddab3fea9117028f2434dddcdab8339141e9b14d1.json +30 -0
  62. data/spec/bitcoin/fixtures/tx-326882a7f22b5191f1a0cc9962ca4b878cd969cf3b3a70887aece4d801a0ba5e.json +23 -0
  63. data/spec/bitcoin/fixtures/tx-345bed8785c3282a264ffb0dbee61cde54854f10e16f1b3e75b7f2d9f62946f2.json +64 -0
  64. data/spec/bitcoin/fixtures/tx-39ba7440b7103557560cc8ce258009936796485aaf8b478e66ab4cb97c66e31b.json +32 -0
  65. data/spec/bitcoin/fixtures/tx-3a04d57a833367f1655cc5ec3beb587888ef4977a86caa8c8ad4ba7cc717eae7.json +64 -0
  66. data/spec/bitcoin/fixtures/tx-4142ee4877eb116abf955a7ec6ef2dc38133b793df762b76d75e3d7d4d8badc9.json +38 -0
  67. data/spec/bitcoin/fixtures/tx-46224764c7870f95b58f155bce1e38d4da8e99d42dbb632d0dd7c07e092ee5aa.json +23 -0
  68. data/spec/bitcoin/fixtures/tx-5df1375ffe61ac35ca178ebb0cab9ea26dedbd0e96005dfcee7e379fa513232f.json +30 -0
  69. data/spec/bitcoin/fixtures/tx-62d9a565bd7b5344c5352e3e9e5f40fa4bbd467fa19c87357216ec8777ba1cce.json +64 -0
  70. data/spec/bitcoin/fixtures/tx-6327783a064d4e350c454ad5cd90201aedf65b1fc524e73709c52f0163739190.json +23 -0
  71. data/spec/bitcoin/fixtures/tx-6606c366a487bff9e412d0b6c09c14916319932db5954bf5d8719f43f828a3ba.json +27 -0
  72. data/spec/bitcoin/fixtures/tx-6aaf18b9f1283b939d8e5d40ff5f8a435229f4178372659cc3a0bce4e262bf78.json +28 -0
  73. data/spec/bitcoin/fixtures/tx-6b48bba6f6d2286d7ec0883c0fc3085955090813a4c94980466611c798b868cc.json +64 -0
  74. data/spec/bitcoin/fixtures/tx-70cfbc6690f9ab46712db44e3079ac227962b2771a9341d4233d898b521619ef.json +40 -0
  75. data/spec/bitcoin/fixtures/tx-7a1a9db42f065f75110fcdb1bc415549c8ef7670417ba1d35a67f1b8adc562c1.json +64 -0
  76. data/spec/bitcoin/fixtures/tx-9a768fc7d0c4bdc86e25154357ef7c0063ca21310e5740a2f12f90b7455184a7.json +64 -0
  77. data/spec/bitcoin/fixtures/tx-9cad8d523a0694f2509d092c39cebc8046adae62b4e4297102d568191d9478d8.json +64 -0
  78. data/spec/bitcoin/fixtures/tx-9e052eb694bd7e15906433f064dff0161a12fd325c1124537766377004023c6f.json +64 -0
  79. data/spec/bitcoin/fixtures/tx-a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944.json +23 -0
  80. data/spec/bitcoin/fixtures/tx-aab7ef280abbb9cc6fbaf524d2645c3daf4fcca2b3f53370e618d9cedf65f1f8.json +23 -0
  81. data/spec/bitcoin/fixtures/tx-ab9805c6d57d7070d9a42c5176e47bb705023e6b67249fb6760880548298e742.json +27 -0
  82. data/spec/bitcoin/fixtures/tx-ad4bcf3241e5d2ad140564e20db3567d41594cf4c2012433fe46a2b70e0d87b8.json +64 -0
  83. data/spec/bitcoin/fixtures/tx-b5b598de91787439afd5938116654e0b16b7a0d0f82742ba37564219c5afcbf9.json +27 -0
  84. data/spec/bitcoin/fixtures/tx-b8fd633e7713a43d5ac87266adc78444669b987a56b3a65fb92d58c2c4b0e84d.json +28 -0
  85. data/spec/bitcoin/fixtures/tx-bbca0628c42cb8bf7c3f4b2ad688fa56da5308dd2a10255da89fb1f46e6e413d.json +36 -0
  86. data/spec/bitcoin/fixtures/tx-bc7fd132fcf817918334822ee6d9bd95c889099c96e07ca2c1eb2cc70db63224.json +23 -0
  87. data/spec/bitcoin/fixtures/tx-c192b74844e4837a34c4a5a97b438f1c111405b01b99e2d12b7c96d07fc74c04.json +28 -0
  88. data/spec/bitcoin/fixtures/tx-e335562f7e297aadeed88e5954bc4eeb8dc00b31d829eedb232e39d672b0c009.json +406 -0
  89. data/spec/bitcoin/fixtures/tx-eb3b82c0884e3efa6d8b0be55b4915eb20be124c9766245bcc7f34fdac32bccb.json +35 -0
  90. data/spec/bitcoin/fixtures/tx-fee1b9b85531c8fb6cd7831f83490c7f2aa768b6eefe29854ef5e89ce7b9ecb1.json +64 -0
  91. data/spec/bitcoin/fixtures/txscript-invalid-too-many-sigops-followed-by-invalid-pushdata.bin +1 -0
  92. data/spec/bitcoin/helpers/fake_blockchain.rb +183 -0
  93. data/spec/bitcoin/key_spec.rb +79 -8
  94. data/spec/bitcoin/namecoin_spec.rb +1 -1
  95. data/spec/bitcoin/node/command_api_spec.rb +373 -86
  96. data/spec/bitcoin/performance/storage_spec.rb +41 -0
  97. data/spec/bitcoin/protocol/addr_spec.rb +7 -5
  98. data/spec/bitcoin/protocol/aux_pow_spec.rb +1 -0
  99. data/spec/bitcoin/protocol/block_spec.rb +6 -0
  100. data/spec/bitcoin/protocol/tx_spec.rb +184 -1
  101. data/spec/bitcoin/protocol/txin_spec.rb +27 -0
  102. data/spec/bitcoin/protocol/txout_spec.rb +27 -0
  103. data/spec/bitcoin/script/opcodes_spec.rb +74 -3
  104. data/spec/bitcoin/script/script_spec.rb +271 -0
  105. data/spec/bitcoin/spec_helper.rb +34 -6
  106. data/spec/bitcoin/storage/models_spec.rb +104 -0
  107. data/spec/bitcoin/storage/reorg_spec.rb +42 -11
  108. data/spec/bitcoin/storage/storage_spec.rb +58 -15
  109. data/spec/bitcoin/storage/validation_spec.rb +44 -14
  110. data/spec/bitcoin/wallet/keygenerator_spec.rb +6 -3
  111. data/spec/bitcoin/wallet/keystore_spec.rb +3 -3
  112. data/spec/bitcoin/wallet/wallet_spec.rb +87 -89
  113. metadata +117 -11
@@ -1,25 +1,17 @@
1
1
  # encoding: ascii-8bit
2
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.
3
13
  module Bitcoin::Validation
4
-
5
- # maximum size of a block (in bytes)
6
- MAX_BLOCK_SIZE = 1_000_000
7
-
8
- # maximum number of signature operations in a block
9
- MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE / 50
10
-
11
- # maximum integer value
12
- INT_MAX = 0xffffffff
13
-
14
- # number of confirmations required before coinbase tx can be spent
15
- COINBASE_MATURITY = 100
16
-
17
- # interval (in blocks) for difficulty retarget
18
- RETARGET = 2016
19
-
20
- # interval (in blocks) for mining reward reduction
21
- REWARD_DROP = 210_000
22
-
14
+
23
15
  class ValidationError < StandardError
24
16
  end
25
17
 
@@ -38,6 +30,11 @@ module Bitcoin::Validation
38
30
  RULES[:context] -= [:difficulty, :coinbase_value]
39
31
  end
40
32
 
33
+ if Bitcoin.litecoin?
34
+ RULES[:syntax] -= [:bits]
35
+ RULES[:syntax] += [:scrypt_bits]
36
+ end
37
+
41
38
  # validate block rules. +opts+ are:
42
39
  # rules:: which rulesets to validate (default: [:syntax, :context])
43
40
  # raise_errors:: whether to raise ValidationError on failure (default: false)
@@ -82,6 +79,13 @@ module Bitcoin::Validation
82
79
  actual <= expected || [actual, expected]
83
80
  end
84
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
+
85
89
  # check that block time is not greater than max
86
90
  def max_timestamp
87
91
  time, max = block.time, Time.now.to_i + 2*60*60
@@ -102,7 +106,7 @@ module Bitcoin::Validation
102
106
 
103
107
  # check that coinbase value is valid; no more than reward + fees
104
108
  def coinbase_value
105
- reward = ((50.0 / (2 ** (store.get_depth / REWARD_DROP.to_f).floor)) * 1e8).to_i
109
+ reward = ((50.0 / (2 ** (store.get_depth / Bitcoin::REWARD_DROP.to_f).floor)) * 1e8).to_i
106
110
  fees = 0
107
111
  block.tx[1..-1].map.with_index do |t, idx|
108
112
  val = tx_validators[idx]
@@ -127,7 +131,7 @@ module Bitcoin::Validation
127
131
 
128
132
  # check that bits satisfy required difficulty
129
133
  def difficulty
130
- return true if Bitcoin.network_name == :testnet3
134
+ return true if Bitcoin.network[:no_difficulty] == true
131
135
  block.bits == next_bits_required || [block.bits, next_bits_required]
132
136
  end
133
137
 
@@ -146,8 +150,11 @@ module Bitcoin::Validation
146
150
  block.time > min_time || [block.time, min_time]
147
151
  end
148
152
 
149
- # check transactions
153
+ # Run all syntax checks on transactions
150
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
+
151
158
  tx_validators.all?{|v|
152
159
  begin
153
160
  v.validate(rules: [:syntax], raise_errors: true)
@@ -158,6 +165,7 @@ module Bitcoin::Validation
158
165
  }
159
166
  end
160
167
 
168
+ # Run all context checks on transactions
161
169
  def transactions_context
162
170
  tx_validators.all?{|v|
163
171
  begin
@@ -169,21 +177,42 @@ module Bitcoin::Validation
169
177
  }
170
178
  end
171
179
 
180
+ # Get validators for all tx objects in the current block
172
181
  def tx_validators
173
- @tx_validators ||= block.tx[1..-1].map {|tx| tx.validator(store, block) }
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
+ )
174
202
  end
175
203
 
176
204
  def next_bits_required
177
- index = (prev_block.depth + 1) / RETARGET
205
+ retarget = (Bitcoin.network[:retarget_interval] || Bitcoin::RETARGET_INTERVAL)
206
+ index = (prev_block.depth + 1) / retarget
178
207
  max_target = Bitcoin.decode_compact_bits(Bitcoin.network[:proof_of_work_limit]).to_i(16)
179
208
  return Bitcoin.network[:proof_of_work_limit] if index == 0
180
- return prev_block.bits if (prev_block.depth + 1) % RETARGET != 0
209
+ return prev_block.bits if (prev_block.depth + 1) % retarget != 0
181
210
  last = store.db[:blk][hash: prev_block.hash.htb.blob]
182
211
  first = store.db[:blk][hash: last[:prev_hash].blob]
183
- (RETARGET-2).times { first = store.db[:blk][hash: first[:prev_hash].blob] }
212
+ (retarget - 2).times { first = store.db[:blk][hash: first[:prev_hash].blob] }
184
213
 
185
214
  nActualTimespan = last[:time] - first[:time]
186
- nTargetTimespan = RETARGET * 600
215
+ nTargetTimespan = retarget * 600
187
216
 
188
217
  nActualTimespan = [nActualTimespan, nTargetTimespan/4].max
189
218
  nActualTimespan = [nActualTimespan, nTargetTimespan*4].min
@@ -202,11 +231,11 @@ module Bitcoin::Validation
202
231
  end
203
232
 
204
233
  class Tx
205
- attr_accessor :tx, :store, :error
234
+ attr_accessor :tx, :store, :error, :block_validator
206
235
 
207
236
  RULES = {
208
237
  syntax: [:hash, :lists, :max_size, :output_values, :inputs, :lock_time, :standard],
209
- context: [:prev_out, :signatures, :spent, :input_values, :output_sum]
238
+ context: [:prev_out, :signatures, :not_spent, :input_values, :output_sum]
210
239
  }
211
240
 
212
241
  # validate tx rules. +opts+ are:
@@ -236,10 +265,14 @@ module Bitcoin::Validation
236
265
  "b3c19d78b4953b694717a47d9852f8ea1ccd4cf93a45ba2e43a0f97d7cdb2655"
237
266
  ]
238
267
 
239
- # setup new validator for given +tx+, validating context with +store+.
240
- # also needs the +block+ to find prev_outs for chains of tx inside one block.
241
- def initialize tx, store, block = nil
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)
242
274
  @tx, @store, @block, @errors = tx, store, block, []
275
+ @block_validator = block_validator
243
276
  end
244
277
 
245
278
  # check that tx hash matches data
@@ -255,7 +288,7 @@ module Bitcoin::Validation
255
288
 
256
289
  # check that tx size doesn't exceed MAX_BLOCK_SIZE.
257
290
  def max_size
258
- tx.to_payload.bytesize <= MAX_BLOCK_SIZE || [tx.to_payload.bytesize, MAX_BLOCK_SIZE]
291
+ tx.to_payload.bytesize <= Bitcoin::MAX_BLOCK_SIZE || [tx.to_payload.bytesize, Bitcoin::MAX_BLOCK_SIZE]
259
292
  end
260
293
 
261
294
  # check that total output value doesn't exceed MAX_MONEY.
@@ -272,7 +305,7 @@ module Bitcoin::Validation
272
305
 
273
306
  # check that lock_time doesn't exceed INT_MAX
274
307
  def lock_time
275
- tx.lock_time <= INT_MAX || [tx.lock_time, INT_MAX]
308
+ tx.lock_time <= Bitcoin::UINT32_MAX || [tx.lock_time, Bitcoin::UINT32_MAX]
276
309
  end
277
310
 
278
311
  # check that min_size is at least 86 bytes
@@ -308,16 +341,23 @@ module Bitcoin::Validation
308
341
  sigs.all? || sigs.map.with_index {|s, i| s ? nil : i }.compact
309
342
  end
310
343
 
311
- # check that none of the prev_outs are already spent in the main chain
312
- def spent
313
- spent = tx.in.map.with_index {|txin, idx|
314
- next false if @block && @block.tx.include?(prev_txs[idx])
315
- next false unless next_in = prev_txs[idx].out[txin.prev_out_index].get_next_in
316
- next false unless next_tx = next_in.get_tx
317
- next false unless next_block = next_tx.get_block
318
- next_block.chain == Bitcoin::Storage::Backends::StoreBase::MAIN
319
- }
320
- spent.none? || spent.map.with_index {|s, i| s ? i : nil }
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 }
321
361
  end
322
362
 
323
363
  # check that the total input value doesn't exceed MAX_MONEY
@@ -341,18 +381,9 @@ module Bitcoin::Validation
341
381
  # only returns tx that are in a block in the main chain or the current block.
342
382
  def prev_txs
343
383
  @prev_txs ||= tx.in.map {|i|
344
- prev_tx = store.get_tx(i.prev_out.reverse_hth)
345
- next prev_tx if store.class.name =~ /UtxoStore/ && prev_tx
346
- next nil if !prev_tx && !@block
347
-
348
- if store.class.name =~ /SequelStore/
349
- block = store.db[:blk][id: prev_tx.blk_id] if prev_tx
350
- next prev_tx if block && block[:chain] == 0
351
- else
352
- next prev_tx if prev_tx && prev_tx.get_block && prev_tx.get_block.chain == 0
353
- end
354
- next nil if !@block
355
- @block.tx.find {|t| t.binary_hash == i.prev_out }
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
356
387
  }.compact
357
388
  end
358
389
 
@@ -1,3 +1,3 @@
1
1
  module Bitcoin
2
- VERSION = "0.0.5"
2
+ VERSION = "0.0.6"
3
3
  end
@@ -24,6 +24,44 @@ describe 'Bitcoin Address/Hash160/PubKey' do
24
24
  .should == "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa"
25
25
  end
26
26
 
27
+ it 'bitcoin-address from pubkey' do
28
+ Bitcoin::network = :testnet
29
+ Bitcoin.pubkey_to_address("029e31ccb7308c2525d542024b8119a3ab3767933e82aedd1471f9c714d998d1b4")
30
+ .should == "mu6vSuyvpVxvDAJyZczjxaU56pXLNBSf9C"
31
+ Bitcoin.pubkey_to_address("049e31ccb7308c2525d542024b8119a3ab3767933e82aedd1471f9c714d998d1b4b82314814017e4e9b06a0fd8e01772bb410cb1c36cfc2d03079c315bc7494b86")
32
+ .should == "n4bZ82i9SdLj6YauPn3PPKFRhQHMZrdaPq"
33
+
34
+ Bitcoin::network = :bitcoin
35
+ Bitcoin.pubkey_to_address("029e31ccb7308c2525d542024b8119a3ab3767933e82aedd1471f9c714d998d1b4")
36
+ .should == "1Eay9rtx1UXfS3qMr42N8fFkEpvdR2euvg"
37
+ Bitcoin.pubkey_to_address("049e31ccb7308c2525d542024b8119a3ab3767933e82aedd1471f9c714d998d1b4b82314814017e4e9b06a0fd8e01772bb410cb1c36cfc2d03079c315bc7494b86")
38
+ .should == "1Q5bpydAdbuUKS7HgD51ZQ36qQgeiN8cBE"
39
+ end
40
+
41
+ it 'bitcoin p2sh multisig address from pubkeys' do
42
+ Bitcoin::network = :testnet
43
+ address, redeem_script = Bitcoin.pubkeys_to_p2sh_multisig_address(2, "029e31ccb7308c2525d542024b8119a3ab3767933e82aedd1471f9c714d998d1b4",
44
+ "0299acf23a65c31fe02052d7474769529c21612b1afa56cc149747fe63867592ec")
45
+ address.should == "2NGaiH7MNYWhsWPQKudZEvy8KnoWPfuGPg1"
46
+ redeem_script.hth.should == "52" + # OP_2
47
+ "21" + "029e31ccb7308c2525d542024b8119a3ab3767933e82aedd1471f9c714d998d1b4" + # pubkey.bytesize + pubkey
48
+ "21" + "0299acf23a65c31fe02052d7474769529c21612b1afa56cc149747fe63867592ec" + # pubkey.bytesize + pubkey
49
+ "52" + # OP_2
50
+ "ae" # OP_CHECKMULTISIG
51
+
52
+ Bitcoin::network = :bitcoin
53
+ address, redeem_script = Bitcoin.pubkeys_to_p2sh_multisig_address(2, "029e31ccb7308c2525d542024b8119a3ab3767933e82aedd1471f9c714d998d1b4",
54
+ "0299acf23a65c31fe02052d7474769529c21612b1afa56cc149747fe63867592ec",
55
+ "020b16a7227f873ac68cf3140f1101d2eda5acb28bf3e7d546409139caf25142e4")
56
+ address.should == "38eiL6Jac27TVAu83wj81Miso9rXiVqgcP"
57
+ redeem_script.hth.should == "52" + # OP_2
58
+ "21" + "029e31ccb7308c2525d542024b8119a3ab3767933e82aedd1471f9c714d998d1b4" + # pubkey.bytesize + pubkey
59
+ "21" + "0299acf23a65c31fe02052d7474769529c21612b1afa56cc149747fe63867592ec" + # pubkey.bytesize + pubkey
60
+ "21" + "020b16a7227f873ac68cf3140f1101d2eda5acb28bf3e7d546409139caf25142e4" + # pubkey.bytesize + pubkey
61
+ "53" + # OP_3
62
+ "ae" # OP_CHECKMULTISIG
63
+ end
64
+
27
65
  it 'bitcoin p2sh address from bitcoin-hash160' do
28
66
  Bitcoin::network = :testnet
29
67
  Bitcoin.hash160_to_p2sh_address("d11e2f2f385efeecd30f867f1d55c0bc8a27f29e")
@@ -85,6 +85,29 @@ describe "Bitcoin::Builder" do
85
85
  tx2.out[0].pk_script.should == tx.out[0].pk_script
86
86
  end
87
87
 
88
+ it "should allow txin.prev_out as tx or hash" do
89
+ prev_tx = @block.tx[0]
90
+ tx1 = build_tx do |t|
91
+ t.input {|i| i.prev_out prev_tx, 0 }
92
+ end
93
+ tx2 = build_tx do |t|
94
+ t.input {|i| i.prev_out prev_tx.hash, 0, prev_tx.out[0].pk_script }
95
+ end
96
+ tx1.in[0].should == tx2.in[0]
97
+ end
98
+
99
+
100
+ it "should provide txout#to shortcut" do
101
+ tx1 = build_tx do |t|
102
+ t.output {|o| o.value 123; o.to @keys[1].addr }
103
+ end
104
+ tx2 = build_tx do |t|
105
+ t.output {|o| o.value 123
106
+ o.script {|s| s.recipient @keys[1].addr } }
107
+ end
108
+ tx1.out[0].should == tx2.out[0]
109
+ end
110
+
88
111
  it "should build unsigned transactions and add the signature hash" do
89
112
  tx = build_tx do |t|
90
113
  t.input do |i|
@@ -102,6 +125,106 @@ describe "Bitcoin::Builder" do
102
125
  tx.in[0].sig_hash.should != nil
103
126
  end
104
127
 
128
+ it "should build unsigned multisig transactions and add the signature hash" do
129
+ tx1 = build_tx do |t|
130
+ t.input {|i| i.prev_out(@block.tx[0], 0); i.signature_key(@keys[0]) }
131
+ t.output do |o|
132
+ o.value 123
133
+ o.to [2, *@keys[0..2].map(&:pub)], :multisig
134
+ end
135
+ end
136
+
137
+ tx2 = build_tx do |t|
138
+ t.input do |i|
139
+ i.prev_out tx1, 0
140
+ i.signature_key @keys[0]
141
+ end
142
+ t.output {|o| o.value 123; o.to @keys[0].addr }
143
+ end
144
+
145
+ tx2.is_a?(Bitcoin::P::Tx).should == true
146
+ tx2.in[0].sig_hash.should != nil
147
+ end
148
+
149
+ it "should build unsigned p2sh multisig transactions and add the signature hash" do
150
+ tx1 = build_tx do |t|
151
+ t.input {|i| i.prev_out(@block.tx[0], 0); i.signature_key(@keys[0]) }
152
+ t.output do |o|
153
+ o.value 123
154
+ o.to [2, *@keys[0..2].map(&:pub)], :p2sh_multisig
155
+ end
156
+ end
157
+
158
+ tx2 = build_tx do |t|
159
+ t.input do |i|
160
+ i.prev_out tx1, 0
161
+ i.signature_key @keys[0]
162
+ i.redeem_script tx1.out[0].redeem_script
163
+ end
164
+ t.output {|o| o.value 123; o.to @keys[0].addr }
165
+ end
166
+
167
+ tx2.is_a?(Bitcoin::P::Tx).should == true
168
+ tx2.in[0].sig_hash.should != nil
169
+ end
170
+
171
+ it "should add change output" do
172
+ change_address = Bitcoin::Key.generate.addr
173
+ tx = build_tx(input_value: @block.tx[0].out.map(&:value).inject(:+),
174
+ change_address: change_address) do |t|
175
+ t.input {|i| i.prev_out @block.tx[0]; i.prev_out_index 0; i.signature_key @keys[0] }
176
+ t.output {|o| o.value 12345; o.script {|s| s.recipient @keys[1].addr } }
177
+ end
178
+ tx.out.count.should == 2
179
+ tx.out.last.value.should == 50e8 - 12345
180
+ Bitcoin::Script.new(tx.out.last.pk_script).get_address.should == change_address
181
+ end
182
+
183
+ it "should add change output and leave fee" do
184
+ change_address = Bitcoin::Key.generate.addr
185
+ tx = build_tx(input_value: @block.tx[0].out.map(&:value).inject(:+),
186
+ change_address: change_address, leave_fee: true) do |t|
187
+ t.input {|i| i.prev_out @block.tx[0]; i.prev_out_index 0; i.signature_key @keys[0] }
188
+ t.output {|o| o.value 12345; o.script {|s| s.recipient @keys[1].addr } }
189
+ end
190
+ tx.out.count.should == 2
191
+ tx.out.last.value.should == 50e8 - 12345 - Bitcoin.network[:min_tx_fee]
192
+ Bitcoin::Script.new(tx.out.last.pk_script).get_address.should == change_address
193
+
194
+ tx = build_tx(input_value: @block.tx[0].out.map(&:value).inject(:+),
195
+ change_address: change_address, leave_fee: true) do |t|
196
+ t.input {|i| i.prev_out @block.tx[0]; i.prev_out_index 0; i.signature_key @keys[0] }
197
+ 49.times { t.output {|o| o.value 1e8; o.script {|s| s.recipient @keys[1].addr } } }
198
+ t.output {|o| o.value(1e8 - 10000); o.script {|s| s.recipient @keys[1].addr } }
199
+ end
200
+ tx.out.size.should == 50
201
+ tx.out.map(&:value).inject(:+).should == 50e8 - 10000
202
+ end
203
+
204
+ it "randomize_outputs should not modify output values or fees" do
205
+ change_address = Bitcoin::Key.generate.addr
206
+ tx = build_tx(input_value: @block.tx[0].out.map(&:value).inject(:+),
207
+ change_address: change_address, leave_fee: true) do |t|
208
+ t.input {|i| i.prev_out @block.tx[0]; i.prev_out_index 0; i.signature_key @keys[0] }
209
+ t.output {|o| o.value 12345; o.script {|s| s.recipient @keys[1].addr } }
210
+ t.randomize_outputs
211
+ end
212
+
213
+ tx.out.count.should == 2
214
+ tx.out.last.value.should == 50e8 - 12345 - Bitcoin.network[:min_tx_fee]
215
+ Bitcoin::Script.new(tx.out.last.pk_script).get_address.should == change_address
216
+
217
+ tx = build_tx(input_value: @block.tx[0].out.map(&:value).inject(:+),
218
+ change_address: change_address, leave_fee: true) do |t|
219
+ t.input {|i| i.prev_out @block.tx[0]; i.prev_out_index 0; i.signature_key @keys[0] }
220
+ 49.times { t.output {|o| o.value 1e8; o.script {|s| s.recipient @keys[1].addr } } }
221
+ t.output {|o| o.value(1e8 - 10000); o.script {|s| s.recipient @keys[1].addr } }
222
+ t.randomize_outputs
223
+ end
224
+ tx.out.size.should == 50
225
+ tx.out.map(&:value).inject(:+).should == 50e8 - 10000
226
+ end
227
+
105
228
  it "should build address script" do
106
229
  key = Bitcoin::Key.generate
107
230
  s = script {|s| s.type :address; s.recipient key.addr }
@@ -121,4 +244,58 @@ describe "Bitcoin::Builder" do
121
244
  Bitcoin::Script.new(s).to_string.should == "1 #{keys[0].pub} #{keys[1].pub} 2 OP_CHECKMULTISIG"
122
245
  end
123
246
 
247
+ it "should build and spend multisig output" do
248
+ tx1 = build_tx do |t|
249
+ t.input {|i| i.prev_out(@block.tx[0], 0); i.signature_key(@keys[0]) }
250
+ t.output do |o|
251
+ o.value 123
252
+ o.to [2, *@keys[0..2].map(&:pub)], :multisig
253
+ end
254
+ end
255
+
256
+ Bitcoin::Script.new(tx1.out[0].pk_script).to_string.should ==
257
+ "2 #{@keys[0..2].map(&:pub).join(' ')} 3 OP_CHECKMULTISIG"
258
+
259
+ tx2 = build_tx do |t|
260
+ t.input do |i|
261
+ i.prev_out tx1, 0
262
+ i.signature_key @keys[0..1]
263
+ end
264
+ t.output {|o| o.value 123; o.to @keys[0].addr }
265
+ end
266
+
267
+ tx2.verify_input_signature(0, tx1).should == true
268
+ end
269
+
270
+ it "should build and spend p2sh multisig output" do
271
+ tx1 = build_tx do |t|
272
+ t.input {|i| i.prev_out(@block.tx[0], 0); i.signature_key(@keys[0]) }
273
+ t.output do |o|
274
+ o.value 123
275
+ o.to [2, *@keys[0..2].map(&:pub)], :p2sh_multisig
276
+ end
277
+ end
278
+
279
+ Bitcoin::Script.new(tx1.out[0].pk_script).to_string.should ==
280
+ "OP_HASH160 #{Bitcoin.hash160(tx1.out[0].redeem_script.hth)} OP_EQUAL"
281
+
282
+ tx2 = build_tx do |t|
283
+ t.input do |i|
284
+ i.prev_out tx1, 0
285
+ # provide 2 required keys for signing
286
+ i.signature_key @keys[0..1]
287
+ # provide the redeem script from the previous output
288
+ i.redeem_script tx1.out[0].redeem_script
289
+ end
290
+
291
+ t.output {|o| o.value 123; o.to @keys[0].addr }
292
+ end
293
+
294
+ script = Bitcoin::Script.new(tx2.in[0].script_sig, tx1.out[0].pk_script)
295
+ # check script execution is valid
296
+ script.run { true }.should == true
297
+ # check signatures are valid
298
+ tx2.verify_input_signature(0, tx1).should == true
299
+ end
300
+
124
301
  end