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
@@ -23,7 +23,7 @@ def get_tx(hash)
23
23
  end
24
24
  json = open(url).read
25
25
  Bitcoin::Protocol::Tx.from_json(json)
26
- rescue Exception
26
+ rescue
27
27
  nil
28
28
  end
29
29
 
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift( File.expand_path("../../lib", __FILE__) )
4
+ require 'bitcoin'
5
+
6
+ Bitcoin.network = :testnet3
7
+ @store = Bitcoin::Storage.sequel(:db => "postgres://mhanne:password@localhost:5434/testnet3_full")
8
+
9
+ #@store.db.execute "DROP INDEX tx_nhash_index"
10
+
11
+ def process_block blk
12
+ print "\r#{blk.hash} - #{blk.depth}"
13
+ blk.tx.each do |tx|
14
+ @store.db[:tx].where(hash: tx.hash.htb.blob).update(nhash: tx.nhash.htb.blob)
15
+ end
16
+ end
17
+
18
+ blk = @store.get_block_by_depth(0)
19
+ process_block(blk)
20
+ while blk = blk.get_next_block
21
+ process_block(blk)
22
+ end
23
+
24
+ @store.db.execute "CREATE INDEX tx_nhash_index ON tx (nhash)"
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift( File.expand_path("../../lib", __FILE__) )
4
+ require 'bitcoin'
5
+
6
+ Bitcoin.network = :testnet3
7
+ @store = Bitcoin::Storage.sequel(:db => "postgres://mhanne:password@localhost:5434/testnet3_full")
8
+ # @store = Bitcoin::Storage.sequel(:db => "postgres:/testnet3")
9
+
10
+ puts "move namecoin types up by one to make room for op_return"
11
+ if Bitcoin.network_name == :namecoin
12
+ @store.db.run "UPDATE txout SET type = 8 WHERE type = 7"
13
+ @store.db.run "UPDATE txout SET type = 7 WHERE type = 6"
14
+ @store.db.run "UPDATE txout SET type = 6 WHERE type = 5"
15
+ end
16
+
17
+ puts "create missing p2sh output <-> address mappings"
18
+ @store.db[:txout].where(type: 4).each do |txout|
19
+ script = Bitcoin::Script.new(txout[:pk_script])
20
+ if addr = @store.db[:addr][hash160: script.get_hash160]
21
+ addr_id = addr[:id]
22
+ else
23
+ addr_id = @store.db[:addr].insert(hash160: script.get_hash160)
24
+ end
25
+
26
+ if addr_txout = @store.db[:addr_txout][addr_id: addr_id, txout_id: txout[:id]]
27
+ # mapping already exists
28
+ print "e"
29
+ else
30
+ print "C"
31
+ @store.db[:addr_txout].insert(addr_id: addr_id, txout_id: txout[:id])
32
+ end
33
+ end
34
+
35
+ puts
36
+ puts "scan all txouts of unknown type and check if they are op_returns"
37
+ @store.db[:txout].where(type: 0).each do |txout|
38
+ if Bitcoin::Script.new(txout[:pk_script]).is_op_return?
39
+ print "C"
40
+ @store.db[:txout].where(id: txout[:id]).update(type: 5)
41
+ else
42
+ print "s"
43
+ end
44
+ end
data/lib/bitcoin.rb CHANGED
@@ -4,7 +4,7 @@
4
4
  require 'digest/sha2'
5
5
  require 'digest/rmd160'
6
6
  require 'openssl'
7
-
7
+ require 'securerandom'
8
8
 
9
9
  module Bitcoin
10
10
 
@@ -21,6 +21,7 @@ module Bitcoin
21
21
  autoload :Validation, 'bitcoin/validation'
22
22
 
23
23
  autoload :Namecoin, 'bitcoin/namecoin'
24
+ autoload :Litecoin, 'bitcoin/litecoin'
24
25
 
25
26
  module Network
26
27
  autoload :ConnectionHandler, 'bitcoin/network/connection_handler'
@@ -128,6 +129,11 @@ module Bitcoin
128
129
  hash160_to_address( hash160(pubkey) )
129
130
  end
130
131
 
132
+ def pubkeys_to_p2sh_multisig_address(m, *pubkeys)
133
+ redeem_script = Bitcoin::Script.to_p2sh_multisig_script(m, *pubkeys).last
134
+ return Bitcoin.hash160_to_p2sh_address(Bitcoin.hash160(redeem_script.hth)), redeem_script
135
+ end
136
+
131
137
  def int_to_base58(int_val, leading_zero_bytes=0)
132
138
  alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
133
139
  base58_val, base = '', alpha.size
@@ -228,6 +234,23 @@ module Bitcoin
228
234
  bitcoin_hash(h)
229
235
  end
230
236
 
237
+ def litecoin_hash(hex)
238
+ bytes = [hex].pack("H*").reverse
239
+ begin
240
+ require "scrypt" unless defined?(::SCrypt)
241
+ hash = SCrypt::Engine.__sc_crypt(bytes, bytes, 1024, 1, 1, 32)
242
+ rescue LoadError
243
+ hash = Litecoin::Scrypt.scrypt_1024_1_1_256_sp(bytes)
244
+ end
245
+ hash.reverse.unpack("H*")[0]
246
+ end
247
+
248
+ def block_scrypt_hash(prev_block, mrkl_root, time, bits, nonce, ver)
249
+ h = "%08x%08x%08x%064s%064s%08x" %
250
+ [nonce, bits, time, mrkl_root, prev_block, ver]
251
+ litecoin_hash(h)
252
+ end
253
+
231
254
  # get merkle tree for given +tx+ list.
232
255
  def hash_mrkl_tree(tx)
233
256
  return [nil] if tx != tx.uniq
@@ -306,13 +329,11 @@ module Bitcoin
306
329
  raise "malformed signature" unless signature.bytesize == 65
307
330
  pubkey = Bitcoin::OpenSSL_EC.recover_compact(hash, signature)
308
331
  pubkey_to_address(pubkey) == address if pubkey
309
- rescue Exception => ex
332
+ rescue => ex
310
333
  p [ex.message, ex.backtrace]; false
311
334
  end
312
335
 
313
336
 
314
- RETARGET_INTERVAL = 2016
315
-
316
337
  # block count when the next retarget will take place.
317
338
  def block_next_retarget(block_height)
318
339
  (block_height + (RETARGET_INTERVAL-block_height.divmod(RETARGET_INTERVAL).last)) - 1
@@ -409,7 +430,8 @@ module Bitcoin
409
430
  @network = :bitcoin
410
431
 
411
432
  def self.network
412
- NETWORKS[@network]
433
+ # Store the copy of network options so we can modify them in tests without breaking the defaults
434
+ @network_options ||= NETWORKS[@network].dup
413
435
  end
414
436
 
415
437
  def self.network_name
@@ -420,8 +442,9 @@ module Bitcoin
420
442
  @network_project
421
443
  end
422
444
 
423
- def self.network= name
445
+ def self.network=(name)
424
446
  raise "Network descriptor '#{name}' not found." unless NETWORKS[name.to_sym]
447
+ @network_options = nil # clear cached parameters
425
448
  @network = name.to_sym
426
449
  @network_project = network[:project] rescue nil
427
450
  Bitcoin::Namecoin.load if namecoin?
@@ -433,13 +456,39 @@ module Bitcoin
433
456
  end
434
457
 
435
458
 
436
- CENT = 1_000_000
437
- COIN = 100_000_000
459
+ # maximum size of a block (in bytes)
438
460
  MAX_BLOCK_SIZE = 1_000_000
461
+
462
+ # soft limit for new blocks
439
463
  MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2
440
- MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50
464
+
465
+ # maximum number of signature operations in a block
466
+ MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE / 50
467
+
468
+ # maximum number of orphan transactions to be kept in memory
441
469
  MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100
442
470
 
471
+ # Threshold for lock_time: below this value it is interpreted as block number, otherwise as UNIX timestamp.
472
+ LOCKTIME_THRESHOLD = 500000000 # Tue Nov 5 00:53:20 1985 UTC
473
+
474
+ # maximum integer value
475
+ UINT32_MAX = 0xffffffff
476
+ INT_MAX = 0xffffffff # deprecated name, left here for compatibility with existing users.
477
+
478
+ # number of confirmations required before coinbase tx can be spent
479
+ COINBASE_MATURITY = 100
480
+
481
+ # interval (in blocks) for difficulty retarget
482
+ RETARGET_INTERVAL = 2016
483
+ RETARGET = 2016 # deprecated constant
484
+
485
+
486
+ # interval (in blocks) for mining reward reduction
487
+ REWARD_DROP = 210_000
488
+
489
+ CENT = 1_000_000
490
+ COIN = 100_000_000
491
+
443
492
  MIN_FEE_MODE = [ :block, :relay, :send ]
444
493
 
445
494
  NETWORKS = {
@@ -454,10 +503,14 @@ module Bitcoin
454
503
  :protocol_version => 70001,
455
504
  :coinbase_maturity => 100,
456
505
  :retarget_interval => 2016,
457
- :retarget_time => 1209600, # 2 weeks
506
+ :retarget_time => 1209600, # 2 weeks
507
+ :target_spacing => 600, # block interval
458
508
  :max_money => 21_000_000 * COIN,
459
509
  :min_tx_fee => 10_000,
460
510
  :min_relay_tx_fee => 10_000,
511
+ :free_tx_bytes => 1_000,
512
+ :dust => CENT,
513
+ :per_dust_fee => false,
461
514
  :dns_seeds => [
462
515
  "seed.bitcoin.sipa.be",
463
516
  "dnsseed.bluematt.me",
@@ -484,9 +537,38 @@ module Bitcoin
484
537
  210000 => "000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e",
485
538
  216116 => "00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e",
486
539
  225430 => "00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932",
540
+ 290000 => "0000000000000000fa0b2badd05db0178623ebf8dd081fe7eb874c26e27d0b3b",
541
+ 300000 => "000000000000000082ccf8f1557c5d40b21edabb18d2d691cfbf87118bac7254",
542
+ 305000 => "0000000000000000142bb90561e1a907d500bf534a6727a63a92af5b6abc6160",
487
543
  }
488
544
  },
489
545
 
546
+ :regtest => {
547
+ :project => :bitcoin,
548
+ :magic_head => "\xFA\xBF\xB5\xDA",
549
+ :address_version => "6f",
550
+ :p2sh_version => "c4",
551
+ :privkey_version => "ef",
552
+ :default_port => 18444,
553
+ :max_money => 21_000_000 * COIN,
554
+ :dns_seeds => [ ],
555
+ :genesis_hash => "0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206",
556
+ :proof_of_work_limit => (1<<255) - 1,
557
+ :alert_pubkeys => ["04302390343f91cc401d56d68b123028bf52e5fca1939df127f63c6467cdf9c8e2c14b61104cf817d0b780da337893ecc4aaff1309e536162dabbdb45200ca2b0a"],
558
+ :known_nodes => [],
559
+ :checkpoints => {},
560
+ :coinbase_maturity => 100,
561
+ :retarget_interval => 2016,
562
+ :retarget_time => 1209600, # 2 weeks
563
+ :target_spacing => 600, # block interval
564
+ :max_money => 21_000_000 * COIN,
565
+ :min_tx_fee => 10_000,
566
+ :min_relay_tx_fee => 10_000,
567
+ :free_tx_bytes => 1_000,
568
+ :dust => CENT,
569
+ :per_dust_fee => false,
570
+ },
571
+
490
572
  :testnet => {
491
573
  :project => :bitcoin,
492
574
  :magic_head => "\xFA\xBF\xB5\xDA",
@@ -504,9 +586,13 @@ module Bitcoin
504
586
  :coinbase_maturity => 100,
505
587
  :retarget_interval => 2016,
506
588
  :retarget_time => 1209600, # 2 weeks
589
+ :target_spacing => 600, # block interval
507
590
  :max_money => 21_000_000 * COIN,
508
591
  :min_tx_fee => 10_000,
509
592
  :min_relay_tx_fee => 10_000,
593
+ :free_tx_bytes => 1_000,
594
+ :dust => CENT,
595
+ :per_dust_fee => false,
510
596
  },
511
597
 
512
598
  :testnet3 => {
@@ -520,21 +606,28 @@ module Bitcoin
520
606
  :coinbase_maturity => 100,
521
607
  :retarget_interval => 2016,
522
608
  :retarget_time => 1209600, # 2 weeks
609
+ :target_spacing => 600, # block interval
523
610
  :max_money => 21_000_000 * COIN,
524
611
  :min_tx_fee => 10_000,
612
+ :no_difficulty => true, # no good. add right testnet3 difficulty calculation instead
525
613
  :min_relay_tx_fee => 10_000,
614
+ :free_tx_bytes => 1_000,
615
+ :dust => CENT,
616
+ :per_dust_fee => false,
526
617
  :dns_seeds => [
527
618
  "testnet-seed.bitcoin.petertodd.org",
528
619
  "testnet-seed.bluematt.me",
529
620
  ],
530
621
  :genesis_hash => "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943",
531
- :proof_of_work_limit => 0x1d07fff8,
622
+ :proof_of_work_limit => 0x1d00ffff,
532
623
  :alert_pubkeys => ["04302390343f91cc401d56d68b123028bf52e5fca1939df127f63c6467cdf9c8e2c14b61104cf817d0b780da337893ecc4aaff1309e536162dabbdb45200ca2b0a"],
533
624
  :known_nodes => [],
534
625
  :checkpoints => {
535
626
  # 542 contains invalid transaction
536
627
  542 => "0000000083c1f82cf72c6724f7a317325806384b06408bce7a4327f418dfd5ad",
537
628
  71018 => "000000000010dd93dc55541116b2744eb8f4c3b706df6e8512d231a03fb9e435",
629
+ 200000 => "0000000000287bffd321963ef05feab753ebe274e1d78b2fd4e2bfe9ad3aa6f2",
630
+ 250000 => "0000000005910c146e4e8d71e8aa6617393738a9794b43cf113076dbaf08460b",
538
631
  }
539
632
  },
540
633
 
@@ -545,13 +638,16 @@ module Bitcoin
545
638
  :p2sh_version => "05",
546
639
  :privkey_version => "b0",
547
640
  :default_port => 9333,
548
- :protocol_version => 60002,
641
+ :protocol_version => 70002,
549
642
  :max_money => 84_000_000 * COIN,
550
- :min_tx_fee => 2_000_000,
643
+ :min_tx_fee => 100_000, # 0.001 LTC
644
+ :min_relay_tx_fee => 100_000, # 0.001 LTC
645
+ :free_tx_bytes => 5_000,
646
+ :dust => CENT / 10,
647
+ :per_dust_fee => true,
551
648
  :coinbase_maturity => 100,
552
649
  :retarget_interval => 2016,
553
650
  :retarget_time => 302400, # 3.5 days
554
- :min_relay_tx_fee => 1_000_000,
555
651
  :dns_seeds => [
556
652
  "dnsseed.litecointools.com",
557
653
  "dnsseed.litecoinpool.org",
@@ -561,7 +657,7 @@ module Bitcoin
561
657
  ],
562
658
  :genesis_hash => "12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2",
563
659
  :proof_of_work_limit => 0,
564
- :alert_pubkeys => [],
660
+ :alert_pubkeys => ["040184710fa689ad5023690c80f3a49c8f13f8d45b8c857fbcbc8bc4a8e4d3eb4b10f4d4604fa08dce601aaf0f470216fe1b51850b4acf21b179c45070ac7b03a9"],
565
661
  :known_nodes => [],
566
662
  :checkpoints => {
567
663
  1 => "80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f",
@@ -578,6 +674,9 @@ module Bitcoin
578
674
  179620 => "2ad9c65c990ac00426d18e446e0fd7be2ffa69e9a7dcb28358a50b2b78b9f709",
579
675
  240000 => "7140d1c4b4c2157ca217ee7636f24c9c73db39c4590c4e6eab2e3ea1555088aa",
580
676
  383640 => "2b6809f094a9215bafc65eb3f110a35127a34be94b7d0590a096c3f126c6f364",
677
+ 409004 => "487518d663d9f1fa08611d9395ad74d982b667fbdc0e77e9cf39b4f1355908a3",
678
+ 456000 => "bf34f71cc6366cd487930d06be22f897e34ca6a40501ac7d401be32456372004",
679
+ 541794 => "1cbccbe6920e7c258bbce1f26211084efb19764aa3224bec3f4320d77d6a2fd2",
581
680
  }
582
681
  },
583
682
 
@@ -588,22 +687,28 @@ module Bitcoin
588
687
  :p2sh_version => "c4",
589
688
  :privkey_version => "ef",
590
689
  :default_port => 19333,
591
- :protocol_version => 60002,
592
- :min_tx_fee => 2_000_000,
593
- :min_relay_tx_fee => 1_000_000,
690
+ :protocol_version => 70002,
691
+ :min_tx_fee => 100_000, # 0.001 LTC
692
+ :min_relay_tx_fee => 100_000, # 0.001 LTC
693
+ :dust => CENT / 10,
694
+ :per_dust_fee => true,
695
+ :free_tx_bytes => 5_000,
594
696
  :coinbase_maturity => 100,
595
697
  :retarget_interval => 2016,
596
698
  :retarget_time => 302400, # 3.5 days
597
699
  :max_money => 84_000_000 * COIN,
598
700
  :dns_seeds => [
599
701
  "testnet-seed.litecointools.com",
702
+ "testnet-seed.ltc.xurious.com",
600
703
  "testnet-seed.weminemnc.com",
601
704
  ],
602
705
  :genesis_hash => "f5ae71e26c74beacc88382716aced69cddf3dffff24f384e1808905e0188f68f",
603
706
  :proof_of_work_limit => 0,
604
- :alert_pubkeys => [],
707
+ :alert_pubkeys => ["04302390343f91cc401d56d68b123028bf52e5fca1939df127f63c6467cdf9c8e2c14b61104cf817d0b780da337893ecc4aaff1309e536162dabbdb45200ca2b0a"],
605
708
  :known_nodes => [],
606
- :checkpoints => {}
709
+ :checkpoints => {
710
+ 546 => "a0fea99a6897f531600c8ae53367b126824fd6a847b2b2b73817a95b8e27e602",
711
+ }
607
712
  },
608
713
 
609
714
 
@@ -618,6 +723,9 @@ module Bitcoin
618
723
  :max_money => 21_000_000 * COIN,
619
724
  :min_tx_fee => 50_000,
620
725
  :min_relay_tx_fee => 10_000,
726
+ :free_tx_bytes => 1_000,
727
+ :dust => CENT,
728
+ :per_dust_fee => false,
621
729
  :dns_seeds => [ "seed.freico.in", "fledge.freico.in" ],
622
730
  :genesis_hash => "000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c",
623
731
  :proof_of_work_limit => 0,
@@ -638,6 +746,9 @@ module Bitcoin
638
746
  :max_money => 21_000_000 * COIN,
639
747
  :min_tx_fee => 50_000,
640
748
  :min_relay_tx_fee => 10_000,
749
+ :free_tx_bytes => 1_000,
750
+ :dust => CENT,
751
+ :per_dust_fee => true,
641
752
  :dns_seeds => [],
642
753
  :genesis_hash => "000000000062b72c5e2ceb45fbc8587e807c155b0da735e6483dfba2f0a9c770",
643
754
  :proof_of_work_limit => 0x1d00ffff,
@@ -648,6 +759,7 @@ module Bitcoin
648
759
  19200 => "d8a7c3e01e1e95bcee015e6fcc7583a2ca60b79e5a3aa0a171eddd344ada903d",
649
760
  24000 => "425ab0983cf04f43f346a4ca53049d0dc2db952c0a68eb0b55c3bb64108d5371",
650
761
  97778 => "7553b1e43da01cfcda4335de1caf623e941d43894bd81c2af27b6582f9d83c6f",
762
+ 165000 => "823d7a54ebab04d14c4ba3508f6b5f25977406f4d389539eac0174d52c6b4b62",
651
763
  }
652
764
  },
653
765
 
@@ -659,6 +771,9 @@ module Bitcoin
659
771
  :protocol_version => 35000,
660
772
  :min_tx_fee => 50_000,
661
773
  :min_relay_tx_fee => 10_000,
774
+ :free_tx_bytes => 1_000,
775
+ :dust => CENT,
776
+ :per_dust_fee => true,
662
777
  :max_money => 21_000_000 * COIN,
663
778
  :dns_seeds => [],
664
779
  :genesis_hash => "00000001f8ab0d14bceaeb50d163b0bef15aecf62b87bd5f5c864d37f201db97",
@@ -18,10 +18,10 @@ module Bitcoin
18
18
 
19
19
  # build a Bitcoin::Protocol::Tx.
20
20
  # see TxBuilder for details.
21
- def build_tx
21
+ def build_tx opts = {}
22
22
  c = TxBuilder.new
23
23
  yield c
24
- c.tx
24
+ c.tx opts
25
25
  end
26
26
  alias :tx :build_tx
27
27
 
@@ -40,13 +40,12 @@ module Bitcoin
40
40
  # t.input {|i| i.coinbase }
41
41
  # t.output do |o|
42
42
  # o.value 5000000000;
43
- # o.script do |s|
44
- # s.type :address
45
- # s.recipient Bitcoin::Key.generate.addr
46
- # end
43
+ # o.to Bitcoin::Key.generate.addr
47
44
  # end
48
45
  # end
49
46
  # end
47
+ #
48
+ # See Bitcoin::Builder::TxBuilder for details on building transactions.
50
49
  class BlockBuilder
51
50
 
52
51
  def initialize
@@ -69,10 +68,10 @@ module Bitcoin
69
68
  end
70
69
 
71
70
  # add transactions to the block (see TxBuilder).
72
- def tx
73
- c = TxBuilder.new
74
- yield c
75
- @block.tx << c.tx
71
+ def tx tx = nil
72
+ tx ||= ( c = TxBuilder.new; yield c; c.tx )
73
+ @block.tx << tx
74
+ tx
76
75
  end
77
76
 
78
77
  # create the block according to values specified via DSL.
@@ -117,18 +116,20 @@ module Bitcoin
117
116
  # DSL to create Bitcoin::Protocol::Tx used by Builder#build_tx.
118
117
  # tx = tx do |t|
119
118
  # t.input do |i|
120
- # i.prev_out prev_tx # previous transaction
121
- # i.prev_out_index 0 # index of previous output
122
- # i.signature_key key # Bitcoin::Key used to sign the input
119
+ # i.prev_out prev_tx, 0
120
+ # i.signature_key key
123
121
  # end
124
122
  # t.output do |o|
125
123
  # o.value 12345 # 0.00012345 BTC
126
- # o.script {|s| s.type :address; s.recipient key.addr }
124
+ # o.to key.addr
127
125
  # end
128
126
  # end
129
127
  #
130
- # signs every input that has a signature key. if the signature key is
131
- # not specified, the input will include the #sig_hash that needs to be signed.
128
+ # Signs every input that has a signature key and where the previous outputs
129
+ # pk_script is known. If unable to sign, the resulting txin will include
130
+ # the #sig_hash that needs to be signed.
131
+ #
132
+ # See TxInBuilder and TxOutBuilder for details on how to build in/outputs.
132
133
  class TxBuilder
133
134
 
134
135
  def initialize
@@ -161,64 +162,202 @@ module Bitcoin
161
162
  @outs << c
162
163
  end
163
164
 
164
- # create the transaction according to values specified via DSL.
165
- # sign each input that has a signature key specified. if there is
165
+ # Create the transaction according to values specified via DSL.
166
+ # Sign each input that has a signature key specified. If there is
166
167
  # no key, store the sig_hash in the input, so it can easily be
167
168
  # signed later.
168
- def tx
169
+ #
170
+ # When :change_address and :input_value options are given, it will
171
+ # automatically create a change output sending the remaining funds
172
+ # to the given address. The :leave_fee option can be used in this
173
+ # case to specify a tx fee that should be left unclaimed by the
174
+ # change output.
175
+ def tx opts = {}
176
+ if opts[:change_address] && !opts[:input_value]
177
+ raise "Must give 'input_value' when auto-generating change output!"
178
+ end
169
179
  @ins.each {|i| @tx.add_in(i.txin) }
170
180
  @outs.each {|o| @tx.add_out(o.txout) }
181
+
182
+ if opts[:change_address]
183
+ output_value = @tx.out.map(&:value).inject(:+)
184
+ change_value = opts[:input_value] - output_value
185
+ if opts[:leave_fee]
186
+ if change_value >= @tx.minimum_block_fee
187
+ change_value -= @tx.minimum_block_fee
188
+ else
189
+ change_value = 0
190
+ end
191
+ end
192
+ if change_value > 0
193
+ script = Script.to_address_script(opts[:change_address])
194
+ @tx.add_out(P::TxOut.new(change_value, script))
195
+ end
196
+ end
197
+
171
198
  @ins.each_with_index do |inc, i|
172
- if @tx.in[i].coinbase?
173
- script_sig = [inc.coinbase_data].pack("H*")
174
- @tx.in[i].script_sig_length = script_sig.bytesize
175
- @tx.in[i].script_sig = script_sig
176
- next
199
+ sign_input(i, inc)
200
+ end
201
+
202
+ # run our tx through an encode/decode cycle to make sure that the binary format is sane
203
+ raise "Payload Error" unless P::Tx.new(@tx.to_payload).to_payload == @tx.to_payload
204
+ @tx.instance_eval do
205
+ @payload = to_payload
206
+ @hash = hash_from_payload(@payload)
207
+ end
208
+
209
+ @tx
210
+ end
211
+
212
+ # coinbase inputs don't need to be signed, they only include the given +coinbase_data+
213
+ def include_coinbase_data i, inc
214
+ script_sig = [inc.coinbase_data].pack("H*")
215
+ @tx.in[i].script_sig_length = script_sig.bytesize
216
+ @tx.in[i].script_sig = script_sig
217
+ end
218
+
219
+ def sig_hash_and_all_keys_exist?(inc, sig_script)
220
+ return false unless @sig_hash && inc.has_keys?
221
+ script = Bitcoin::Script.new(sig_script)
222
+ return true if script.is_hash160? || script.is_pubkey?
223
+ if script.is_multisig?
224
+ return inc.has_multiple_keys? && inc.key.size >= script.get_signatures_required
225
+ end
226
+ raise "Script type must be hash160, pubkey or multisig"
227
+ end
228
+
229
+ def add_empty_script_sig_to_input(i)
230
+ @tx.in[i].script_sig_length = 0
231
+ @tx.in[i].script_sig = ""
232
+ # add the sig_hash that needs to be signed, so it can be passed on to a signing device
233
+ @tx.in[i].sig_hash = @sig_hash
234
+ # add the address the sig_hash needs to be signed with as a convenience for the signing device
235
+ @tx.in[i].sig_address = Script.new(@prev_script).get_address if @prev_script
236
+ end
237
+
238
+ def get_script_sig(inc)
239
+ if inc.has_multiple_keys?
240
+ # multiple keys given, generate signature for each one
241
+ sigs = inc.sign(@sig_hash)
242
+ if redeem_script = inc.instance_eval { @redeem_script }
243
+ # when a redeem_script was specified, assume we spend a p2sh multisig script
244
+ script_sig = Script.to_p2sh_multisig_script_sig(redeem_script, sigs)
245
+ else
246
+ # when no redeem_script is given, do a regular multisig spend
247
+ script_sig = Script.to_multisig_script_sig(*sigs)
177
248
  end
178
- prev_tx = inc.instance_variable_get(:@prev_out)
179
- @sig_hash = @tx.signature_hash_for_input(i, prev_tx)
180
- if inc.key && inc.key.priv
181
- sig = inc.key.sign(@sig_hash)
182
- script_sig = Script.to_signature_pubkey_script(sig, [inc.key.pub].pack("H*"))
183
- @tx.in[i].script_sig_length = script_sig.bytesize
184
- @tx.in[i].script_sig = script_sig
185
- raise "Signature error" unless @tx.verify_input_signature(i, prev_tx)
249
+ else
250
+ # only one key given, generate signature and script_sig
251
+ sig = inc.sign(@sig_hash)
252
+ script_sig = Script.to_signature_pubkey_script(sig, [inc.key.pub].pack("H*"))
253
+ end
254
+ return script_sig
255
+ end
256
+
257
+ # Sign input number +i+ with data from given +inc+ object (a TxInBuilder).
258
+ def sign_input i, inc
259
+ if @tx.in[i].coinbase?
260
+ include_coinbase_data(i, inc)
261
+ else
262
+ @prev_script = inc.instance_variable_get(:@prev_out_script)
263
+
264
+ # get the signature script; use +redeem_script+ if given
265
+ # (indicates spending a p2sh output), otherwise use the prev_script
266
+ sig_script = inc.instance_eval { @redeem_script }
267
+ sig_script ||= @prev_script
268
+
269
+ # when a sig_script was found, generate the sig_hash to be signed
270
+ @sig_hash = @tx.signature_hash_for_input(i, sig_script) if sig_script
271
+
272
+ # when there is a sig_hash and one or more signature_keys were specified
273
+ if sig_hash_and_all_keys_exist?(inc, sig_script)
274
+ # add the script_sig to the txin
275
+ @tx.in[i].script_sig = get_script_sig(inc)
276
+
277
+ # double-check that the script_sig is valid to spend the given prev_script
278
+ raise "Signature error" if @prev_script && !@tx.verify_input_signature(i, @prev_script)
279
+ elsif inc.has_multiple_keys?
280
+ raise "Keys missing for multisig signing"
186
281
  else
187
- @tx.in[i].script_sig_length = 0
188
- @tx.in[i].script_sig = ""
189
- @tx.in[i].sig_hash = @sig_hash
190
- @tx.in[i].sig_address = Script.new(prev_tx.out[@tx.in[i].prev_out_index].pk_script).get_address
282
+ # no sig_hash, add an empty script_sig.
283
+ add_empty_script_sig_to_input(i)
191
284
  end
192
285
  end
193
- data = @tx.in.map {|i| [i.sig_hash, i.sig_address] }
194
- tx = P::Tx.new(@tx.to_payload)
195
- data.each.with_index {|d, i| i = tx.in[i]; i.sig_hash = d[0]; i.sig_address = d[1] }
196
- raise "Payload Error" unless tx.to_payload == @tx.to_payload
197
- tx
286
+ end
287
+
288
+ # Randomize the outputs using SecureRandom
289
+ def randomize_outputs
290
+ @outs.sort_by!{ SecureRandom.random_bytes(4).unpack("I")[0] }
198
291
  end
199
292
  end
200
293
 
201
- # create a Bitcoin::Protocol::TxIn used by TxBuilder#input.
294
+ # Create a Bitcoin::Protocol::TxIn used by TxBuilder#input.
295
+ #
296
+ # Inputs need the transaction hash and the index of the output they spend.
297
+ # You can pass either the transaction, or just its hash (in hex form).
298
+ # To sign the input, builder also needs the pk_script of the previous output.
299
+ # If you specify a tx hash instead of the whole tx, you need to specify the
300
+ # output script separately.
301
+ #
302
+ # t.input do |i|
303
+ # i.prev_out prev_tx # previous transaction
304
+ # i.prev_out_index 0 # index of previous output
305
+ # i.signature_key key # Bitcoin::Key used to sign the input
306
+ # end
307
+ #
308
+ # t.input {|i| i.prev_out prev_tx, 0 }
202
309
  #
203
- # inputs need a #prev_out tx and #prev_out_index of the output they spend.
310
+ # If you want to spend a p2sh output, you also need to specify the +redeem_script+.
311
+ #
312
+ # t.input do |i|
313
+ # i.prev_out prev_tx, 0
314
+ # i.redeem_script prev_out.redeem_script
315
+ # end
316
+ #
317
+ # If you want to spend a multisig output, just provide an array of keys to #signature_key.
204
318
  class TxInBuilder
205
- attr_reader :key, :coinbase_data
319
+ attr_reader :prev_tx, :prev_script, :redeem_script, :key, :coinbase_data
206
320
 
207
321
  def initialize
208
322
  @txin = P::TxIn.new
323
+ @prev_out_hash = "\x00" * 32
324
+ @prev_out_index = 0
209
325
  end
210
326
 
211
- # previous transaction that contains the output we want to use.
212
- def prev_out tx, idx = nil
213
- @prev_out, @prev_out_index = tx, idx
327
+ # Previous transaction that contains the output we want to use.
328
+ # You can either pass the transaction, or just the tx hash.
329
+ # If you pass only the hash, you need to pass the previous outputs
330
+ # +script+ separately if you want the txin to be signed.
331
+ def prev_out tx, idx = nil, script = nil
332
+ if tx.is_a?(Bitcoin::P::Tx)
333
+ @prev_tx = tx
334
+ @prev_out_hash = tx.binary_hash
335
+ @prev_out_script = tx.out[idx].pk_script if idx
336
+ else
337
+ @prev_out_hash = tx.htb.reverse
338
+ end
339
+ @prev_out_script = script if script
340
+ @prev_out_index = idx if idx
214
341
  end
215
342
 
216
- # index of the output in the #prev_out transaction.
343
+ # Index of the output in the #prev_out transaction.
217
344
  def prev_out_index i
218
345
  @prev_out_index = i
346
+ @prev_out_script = @prev_tx.out[i].pk_script if @prev_tx
219
347
  end
220
348
 
221
- # specify sequence. this is usually not needed.
349
+ # Previous output's +pk_script+. Needed when only the tx hash is specified as #prev_out.
350
+ def prev_out_script script
351
+ @prev_out_script = script
352
+ end
353
+
354
+ # Redeem script for P2SH output. To spend from a P2SH output, you need to provide
355
+ # the script with a hash matching the P2SH address.
356
+ def redeem_script script
357
+ @redeem_script = script
358
+ end
359
+
360
+ # Specify sequence. This is usually not needed.
222
361
  def sequence s
223
362
  @sequence = s
224
363
  end
@@ -229,44 +368,70 @@ module Bitcoin
229
368
  @key = key
230
369
  end
231
370
 
232
- # specify that this is a coinbase input. optionally set +data+.
371
+ # Specify that this is a coinbase input. Optionally set +data+.
372
+ # If this is set, no other options need to be given.
233
373
  def coinbase data = nil
234
374
  @coinbase_data = data || OpenSSL::Random.random_bytes(32)
235
- @prev_out = nil
375
+ @prev_out_hash = "\x00" * 32
236
376
  @prev_out_index = 4294967295
237
377
  end
238
378
 
239
- # create the txin according to values specified via DSL
379
+ # Create the txin according to specified values
240
380
  def txin
241
- @txin.prev_out = (@prev_out ? @prev_out.binary_hash : "\x00"*32)
381
+ @txin.prev_out = @prev_out_hash
242
382
  @txin.prev_out_index = @prev_out_index
243
383
  @txin.sequence = @sequence || "\xff\xff\xff\xff"
244
384
  @txin
245
385
  end
386
+
387
+ def has_multiple_keys?
388
+ @key.is_a?(Array)
389
+ end
390
+
391
+ def has_keys?
392
+ @key && (has_multiple_keys? ? @key.all?(&:priv) : @key.priv)
393
+ end
394
+
395
+ def sign(sig_hash)
396
+ if has_multiple_keys?
397
+ @key.map {|k| k.sign(sig_hash) }
398
+ else
399
+ @key.sign(sig_hash)
400
+ end
401
+ end
246
402
  end
247
403
 
248
- # create a Bitcoin::Script used by TxOutBuilder#script.
404
+ # Create a Bitcoin::Script used by TxOutBuilder#script.
249
405
  class ScriptBuilder
250
- attr_reader :script
406
+ attr_reader :script, :redeem_script
251
407
 
252
408
  def initialize
253
409
  @type = :address
254
410
  @script = nil
255
411
  end
256
412
 
257
- # script type (:pubkey, :address/hash160, :multisig).
413
+ # Script type (:pubkey, :address/hash160, :multisig).
414
+ # Defaults to :address.
258
415
  def type type
259
416
  @type = type.to_sym
260
417
  end
261
418
 
262
- # recipient(s) of the script.
263
- # depending on the #type, either an address, hash160 pubkey, etc.
419
+ # Recipient(s) of the script.
420
+ # Depending on the #type, this should be an address, a hash160 pubkey,
421
+ # or an array of multisig pubkeys.
264
422
  def recipient *data
265
- @script = Script.send("to_#{@type}_script", *data)
423
+ @script, @redeem_script = *Script.send("to_#{@type}_script", *data)
266
424
  end
267
425
  end
268
426
 
269
- # create a Bitcoin::Protocol::TxOut used by TxBuilder#output.
427
+ # Create a Bitcoin::Protocol::TxOut used by TxBuilder#output.
428
+ #
429
+ # t.output {|o| o.value 12345; o.to address }
430
+ #
431
+ # t.output do |o|
432
+ # o.value 12345
433
+ # o.script {|s| s.recipient address }
434
+ # end
270
435
  class TxOutBuilder
271
436
  attr_reader :txout
272
437
 
@@ -274,16 +439,21 @@ module Bitcoin
274
439
  @txout = P::TxOut.new
275
440
  end
276
441
 
277
- # set output value (in base units / "satoshis")
442
+ # Set output value (in base units / "satoshis")
278
443
  def value value
279
444
  @txout.value = value
280
445
  end
281
446
 
282
- # add a script to the output (see ScriptBuilder).
447
+ # Set recipient address and script type (defaults to :address).
448
+ def to recipient, type = :address
449
+ @txout.pk_script, @txout.redeem_script = *Bitcoin::Script.send("to_#{type}_script", *recipient)
450
+ end
451
+
452
+ # Add a script to the output (see ScriptBuilder).
283
453
  def script &block
284
454
  c = ScriptBuilder.new
285
455
  yield c
286
- @txout.pk_script = c.script
456
+ @txout.pk_script, @txout.redeem_script = c.script, c.redeem_script
287
457
  end
288
458
 
289
459
  end