bitcoin-ruby 0.0.5 → 0.0.6

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.
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