bitcoin-ruby 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. data/.gitignore +4 -1
  2. data/Gemfile +21 -0
  3. data/README.rdoc +85 -25
  4. data/Rakefile +7 -3
  5. data/bin/bitcoin_node +39 -42
  6. data/bin/bitcoin_shell +1 -0
  7. data/bin/bitcoin_wallet +129 -53
  8. data/bitcoin-ruby.gemspec +4 -7
  9. data/concept-examples/blockchain-pow.rb +1 -1
  10. data/doc/CONFIG.rdoc +5 -5
  11. data/doc/EXAMPLES.rdoc +9 -5
  12. data/doc/NAMECOIN.rdoc +34 -0
  13. data/doc/NODE.rdoc +147 -10
  14. data/examples/balance.rb +10 -4
  15. data/examples/bbe_verify_tx.rb +7 -2
  16. data/examples/forwarder.rb +73 -0
  17. data/examples/generate_tx.rb +34 -0
  18. data/examples/simple_network_monitor_and_util.rb +187 -0
  19. data/examples/verify_tx.rb +1 -1
  20. data/lib/bitcoin.rb +308 -18
  21. data/lib/bitcoin/builder.rb +62 -36
  22. data/lib/bitcoin/config.rb +2 -0
  23. data/lib/bitcoin/connection.rb +11 -8
  24. data/lib/bitcoin/electrum/mnemonic.rb +162 -0
  25. data/lib/bitcoin/ffi/openssl.rb +187 -21
  26. data/lib/bitcoin/gui/addr_view.rb +2 -0
  27. data/lib/bitcoin/gui/conn_view.rb +2 -0
  28. data/lib/bitcoin/gui/connection.rb +2 -0
  29. data/lib/bitcoin/gui/em_gtk.rb +2 -0
  30. data/lib/bitcoin/gui/gui.rb +2 -0
  31. data/lib/bitcoin/gui/helpers.rb +2 -0
  32. data/lib/bitcoin/gui/tree_view.rb +2 -0
  33. data/lib/bitcoin/gui/tx_view.rb +2 -0
  34. data/lib/bitcoin/key.rb +77 -11
  35. data/lib/bitcoin/litecoin.rb +81 -0
  36. data/lib/bitcoin/logger.rb +20 -1
  37. data/lib/bitcoin/namecoin.rb +279 -0
  38. data/lib/bitcoin/network/command_client.rb +7 -6
  39. data/lib/bitcoin/network/command_handler.rb +229 -43
  40. data/lib/bitcoin/network/connection_handler.rb +182 -70
  41. data/lib/bitcoin/network/node.rb +231 -106
  42. data/lib/bitcoin/protocol.rb +44 -23
  43. data/lib/bitcoin/protocol/address.rb +5 -3
  44. data/lib/bitcoin/protocol/alert.rb +3 -4
  45. data/lib/bitcoin/protocol/aux_pow.rb +123 -0
  46. data/lib/bitcoin/protocol/block.rb +98 -18
  47. data/lib/bitcoin/protocol/handler.rb +6 -5
  48. data/lib/bitcoin/protocol/parser.rb +44 -19
  49. data/lib/bitcoin/protocol/tx.rb +105 -52
  50. data/lib/bitcoin/protocol/txin.rb +39 -19
  51. data/lib/bitcoin/protocol/txout.rb +28 -13
  52. data/lib/bitcoin/protocol/version.rb +16 -7
  53. data/lib/bitcoin/script.rb +579 -122
  54. data/lib/bitcoin/storage/{dummy.rb → dummy/dummy_store.rb} +8 -14
  55. data/lib/bitcoin/storage/models.rb +20 -7
  56. data/lib/bitcoin/storage/{sequel_store/sequel_migrations.rb → sequel/migrations.rb} +22 -7
  57. data/lib/bitcoin/storage/sequel/migrations/001_base_schema.rb +52 -0
  58. data/lib/bitcoin/storage/sequel/migrations/002_tx.rb +50 -0
  59. data/lib/bitcoin/storage/sequel/migrations/003_change_txin_script_sig_to_blob.rb +18 -0
  60. data/lib/bitcoin/storage/sequel/sequel_store.rb +436 -0
  61. data/lib/bitcoin/storage/storage.rb +233 -28
  62. data/lib/bitcoin/storage/utxo/migrations/001_base_schema.rb +52 -0
  63. data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +18 -0
  64. data/lib/bitcoin/storage/utxo/utxo_store.rb +361 -0
  65. data/lib/bitcoin/validation.rb +369 -0
  66. data/lib/bitcoin/version.rb +1 -1
  67. data/lib/bitcoin/wallet/coinselector.rb +3 -0
  68. data/lib/bitcoin/wallet/keygenerator.rb +3 -1
  69. data/lib/bitcoin/wallet/keystore.rb +6 -2
  70. data/lib/bitcoin/wallet/txdp.rb +6 -4
  71. data/lib/bitcoin/wallet/wallet.rb +54 -16
  72. data/spec/bitcoin/bitcoin_spec.rb +48 -3
  73. data/spec/bitcoin/builder_spec.rb +40 -17
  74. data/spec/bitcoin/fixtures/000000000000056b1a3d84a1e2b33cde8915a4b61c0cae14fca6d3e1490b4f98.json +3697 -0
  75. data/spec/bitcoin/fixtures/03d7e1fa4d5fefa169431f24f7798552861b255cd55d377066fedcd088fb0e99.json +23 -0
  76. data/spec/bitcoin/fixtures/0961c660358478829505e16a1f028757e54b5bbf9758341a7546573738f31429.json +24 -0
  77. data/spec/bitcoin/fixtures/0f24294a1d23efbb49c1765cf443fba7930702752aba6d765870082fe4f13cae.json +37 -0
  78. data/spec/bitcoin/fixtures/315ac7d4c26d69668129cc352851d9389b4a6868f1509c6c8b66bead11e2619f.json +31 -0
  79. data/spec/bitcoin/fixtures/35e2001b428891fefa0bfb73167c7360669d3cbd7b3aa78e7cad125ddfc51131.json +27 -0
  80. data/spec/bitcoin/fixtures/3a17dace09ffb919ed627a93f1873220f4c975c1248558b18d16bce25d38c4b7.json +72 -0
  81. data/spec/bitcoin/fixtures/3e58b7eed0fdb599019af08578effea25c8666bbe8e200845453cacce6314477.json +27 -0
  82. data/spec/bitcoin/fixtures/514c46f0b61714092f15c8dfcb576c9f79b3f959989b98de3944b19d98832b58.json +24 -0
  83. data/spec/bitcoin/fixtures/51bf528ecf3c161e7c021224197dbe84f9a8564212f6207baa014c01a1668e1e.json +30 -0
  84. data/spec/bitcoin/fixtures/69216b8aaa35b76d6613e5f527f4858640d986e1046238583bdad79b35e938dc.json +28 -0
  85. data/spec/bitcoin/fixtures/7208e5edf525f04e705fb3390194e316205b8f995c8c9fcd8c6093abe04fa27d.json +27 -0
  86. data/spec/bitcoin/fixtures/761d8c5210fdfd505f6dff38f740ae3728eb93d7d0971fb433f685d40a4c04f6.json +27 -0
  87. data/spec/bitcoin/fixtures/aea682d68a3ea5e3583e088dcbd699a5d44d4b083f02ad0aaf2598fe1fa4dfd4.json +27 -0
  88. data/spec/bitcoin/fixtures/bd1715f1abfdc62bea3f605bdb461b3ba1f2cca6ec0d73a18a548b7717ca8531.json +34 -0
  89. data/spec/bitcoin/fixtures/block-testnet-0000000000ac85bb2530a05a4214a387e6be02b22d3348abc5e7a5d9c4ce8dab.bin +0 -0
  90. data/spec/bitcoin/fixtures/cd874fa8cb0e2ec2d385735d5e1fd482c4fe648533efb4c50ee53bda58e15ae2.json +24 -0
  91. data/spec/bitcoin/fixtures/ce5fad9b4ef094d8f4937b0707edaf0a6e6ceeaf67d5edbfd51f660eac8f398b.json +41 -0
  92. data/spec/bitcoin/fixtures/f003f0c1193019db2497a675fd05d9f2edddf9b67c59e677c48d3dbd4ed5f00b.json +23 -0
  93. data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.bin +0 -0
  94. data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.json +43 -0
  95. data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.bin +0 -0
  96. data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.json +67 -0
  97. data/spec/bitcoin/fixtures/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.bin +0 -0
  98. data/spec/bitcoin/fixtures/litecoin-block-80ca095ed10b02e53d769eb6eaf92cd04e9e0759e5be4a8477b42911ba49c78f.json +39 -0
  99. data/spec/bitcoin/fixtures/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.bin +0 -0
  100. data/spec/bitcoin/fixtures/litecoin-genesis-block-12a765e31ffd4059bada1e25190f6e98c99d9714d334efa41a195a7e7e04bfe2.json +39 -0
  101. data/spec/bitcoin/fixtures/rawblock-auxpow.bin +0 -0
  102. data/spec/bitcoin/fixtures/tx-313897799b1e37e9ecae15010e56156dddde4e683c96b0e713af95272c38aee0.json +30 -0
  103. data/spec/bitcoin/fixtures/tx-3da75972766f0ad13319b0b461fd16823a731e44f6e9de4eb3c52d6a6fb6c8ae.json +23 -0
  104. data/spec/bitcoin/fixtures/tx-44b833074e671120ba33106877b49e86ece510824b9af477a3853972bcd8d06a.json +30 -0
  105. data/spec/bitcoin/fixtures/tx-d3d77d63709e47d9ef58f0b557800115a6b676c6a423012fbb96f45d8fcef830.json +28 -0
  106. data/spec/bitcoin/key_spec.rb +128 -3
  107. data/spec/bitcoin/namecoin_spec.rb +182 -0
  108. data/spec/bitcoin/network_spec.rb +5 -3
  109. data/spec/bitcoin/node/command_api_spec.rb +376 -0
  110. data/spec/bitcoin/protocol/addr_spec.rb +2 -0
  111. data/spec/bitcoin/protocol/alert_spec.rb +2 -0
  112. data/spec/bitcoin/protocol/aux_pow_spec.rb +44 -0
  113. data/spec/bitcoin/protocol/block_spec.rb +134 -39
  114. data/spec/bitcoin/protocol/getblocks_spec.rb +32 -0
  115. data/spec/bitcoin/protocol/inv_spec.rb +10 -8
  116. data/spec/bitcoin/protocol/notfound_spec.rb +31 -0
  117. data/spec/bitcoin/protocol/ping_spec.rb +2 -0
  118. data/spec/bitcoin/protocol/tx_spec.rb +83 -17
  119. data/spec/bitcoin/protocol/version_spec.rb +7 -5
  120. data/spec/bitcoin/script/opcodes_spec.rb +412 -133
  121. data/spec/bitcoin/script/script_spec.rb +112 -13
  122. data/spec/bitcoin/spec_helper.rb +68 -0
  123. data/spec/bitcoin/storage/reorg_spec.rb +199 -0
  124. data/spec/bitcoin/storage/storage_spec.rb +337 -0
  125. data/spec/bitcoin/storage/validation_spec.rb +261 -0
  126. data/spec/bitcoin/wallet/coinselector_spec.rb +10 -7
  127. data/spec/bitcoin/wallet/keygenerator_spec.rb +2 -0
  128. data/spec/bitcoin/wallet/keystore_spec.rb +2 -0
  129. data/spec/bitcoin/wallet/txdp_spec.rb +2 -0
  130. data/spec/bitcoin/wallet/wallet_spec.rb +91 -58
  131. metadata +105 -51
  132. data/lib/bitcoin/storage/sequel.rb +0 -335
  133. data/spec/bitcoin/fixtures/0d0affb5964abe804ffe85e53f1dbb9f29e406aa3046e2db04fba240e63c7fdd.json +0 -27
  134. data/spec/bitcoin/fixtures/477fff140b363ec2cc51f3a65c0c58eda38f4d41f04a295bbd62babf25e4c590.json +0 -27
  135. data/spec/bitcoin/reorg_spec.rb +0 -129
  136. data/spec/bitcoin/storage_spec.rb +0 -229
@@ -1,21 +1,23 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  module Bitcoin
2
4
  module Protocol
3
5
 
4
6
  class Handler
5
7
  def on_inv_transaction(hash)
6
- p ['inv transaction', hth(hash)]
8
+ p ['inv transaction', hash.hth]
7
9
  end
8
10
 
9
11
  def on_inv_block(hash)
10
- p ['inv block', hth(hash)]
12
+ p ['inv block', hash.hth]
11
13
  end
12
14
 
13
15
  def on_get_transaction(hash)
14
- p ['get transaction', hth(hash)]
16
+ p ['get transaction', hash.hth]
15
17
  end
16
18
 
17
19
  def on_get_block(hash)
18
- p ['get block', hth(hash)]
20
+ p ['get block', hash.hth]
19
21
  end
20
22
 
21
23
  def on_addr(addr)
@@ -31,7 +33,6 @@ module Bitcoin
31
33
  puts block.to_json
32
34
  end
33
35
 
34
- def hth(h); h.unpack("H*")[0]; end
35
36
  end
36
37
 
37
38
  end
@@ -1,3 +1,5 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  module Bitcoin
2
4
  module Protocol
3
5
 
@@ -37,8 +39,6 @@ module Bitcoin
37
39
  }
38
40
  end
39
41
 
40
- def hth(h); h.unpack("H*")[0]; end
41
-
42
42
  def parse_addr(payload)
43
43
  count, payload = Protocol.unpack_var_int(payload)
44
44
  payload.each_byte.each_slice(30){|i|
@@ -52,18 +52,19 @@ module Bitcoin
52
52
  end
53
53
 
54
54
  def parse_headers(payload)
55
- count, payload = Protocol.unpack_var_int(payload)
56
- idx = 0
57
- headers = count.times.map{ Block.new(payload[idx..idx+=81]) }
55
+ return unless @h.respond_to?(:on_headers)
56
+ buf = StringIO.new(payload)
57
+ count = Protocol.unpack_var_int_from_io(buf)
58
+ headers = count.times.map{ b = Block.new; b.parse_data_from_io(buf, header_only=true); b }
58
59
  @h.on_headers(headers)
59
60
  end
60
61
 
61
62
  def parse_getblocks(payload)
62
- version, payload = payload.unpack('a4a*')
63
+ version, payload = payload.unpack('Va*')
63
64
  count, payload = Protocol.unpack_var_int(payload)
64
65
  buf, payload = payload.unpack("a#{count*32}a*")
65
- hashes = buf.each_byte.each_slice(32).map{|i| hash = Protocol.hth(i.reverse.pack("C32")) }
66
- stop_hash = Protocol.hth(payload[0..32].reverse)
66
+ hashes = buf.each_byte.each_slice(32).map{|i| hash = i.reverse.pack("C32").hth }
67
+ stop_hash = payload[0..32].reverse_hth
67
68
  [version, hashes, stop_hash]
68
69
  end
69
70
 
@@ -75,21 +76,24 @@ module Bitcoin
75
76
  when 'inv'; parse_inv(payload, :put)
76
77
  when 'getdata'; parse_inv(payload, :get)
77
78
  when 'addr'; parse_addr(payload)
78
- when 'verack'; @h.on_handshake_complete # nop
79
+ when 'getaddr'; @h.on_getaddr if @h.respond_to?(:on_getaddr)
80
+ when 'verack'; @h.respond_to?(:on_verack) ? @h.on_verack : (@h.respond_to?(:on_handshake_complete) ? @h.on_handshake_complete : nil)
79
81
  when 'version'; parse_version(payload)
80
82
  when 'alert'; parse_alert(payload)
81
83
  when 'ping'; @h.on_ping(payload.unpack("Q")[0])
82
84
  when 'pong'; @h.on_pong(payload.unpack("Q")[0])
83
- when 'getblocks'; @h.on_getblocks(*parse_getblocks(payload))
84
- when 'getheaders'; @h.on_getheaders(*parse_getblocks(payload))
85
+ when 'getblocks'; @h.on_getblocks(*parse_getblocks(payload)) if @h.respond_to?(:on_getblocks)
86
+ when 'getheaders'; @h.on_getheaders(*parse_getblocks(payload)) if @h.respond_to?(:on_getheaders)
87
+ when 'mempool'; handle_mempool_request(payload)
88
+ when 'notfound'; handle_notfound_reply(payload)
85
89
  else
86
- p ['unkown-packet', command, payload]
90
+ p ['unknown-packet', command, payload]
87
91
  end
88
92
  end
89
93
 
90
94
  def parse_version(payload)
91
- version = Bitcoin::Protocol::Version.parse(payload)
92
- @h.on_version(version)
95
+ @version = Bitcoin::Protocol::Version.parse(payload)
96
+ @h.on_version(@version)
93
97
  end
94
98
 
95
99
  def parse_alert(payload)
@@ -97,6 +101,27 @@ module Bitcoin
97
101
  @h.on_alert Bitcoin::Protocol::Alert.parse(payload)
98
102
  end
99
103
 
104
+ # https://en.bitcoin.it/wiki/BIP_0035
105
+ def handle_mempool_request(payload)
106
+ return unless @version[:version] >= 60002 # Protocol version >= 60002
107
+ return unless (@version[:services] & Bitcoin::Protocol::Version::NODE_NETWORK) == 1 # NODE_NETWORK bit set in Services
108
+ @h.on_mempool if @h.respond_to?(:on_mempool)
109
+ end
110
+
111
+ def handle_notfound_reply(payload)
112
+ return unless @h.respond_to?(:on_notfound)
113
+ count, payload = Protocol.unpack_var_int(payload)
114
+ payload.each_byte.each_slice(36){|i|
115
+ hash = i[4..-1].reverse.pack("C32")
116
+ case i[0]
117
+ when 1; @h.on_notfound(:tx, hash)
118
+ when 2; @h.on_notfound(:block, hash)
119
+ else
120
+ p ['handle_notfound_reply error', i, hash]
121
+ end
122
+ }
123
+ end
124
+
100
125
  def parse(buf)
101
126
  @buf += buf
102
127
  while parse_buffer; end
@@ -108,20 +133,20 @@ module Bitcoin
108
133
  head_size = 24
109
134
  return false if @buf.size <= head_size
110
135
 
111
- magic, cmd, length, checksum = @buf.unpack("a4A12Ia4")
136
+ magic, cmd, length, checksum = @buf.unpack("a4A12Va4")
112
137
  payload = @buf[head_size...head_size+length]
113
138
 
114
139
  unless magic == head_magic
115
- handle_error(:close, "head_magic not found")
140
+ handle_stream_error(:close, "head_magic not found")
116
141
  @buf = ''
117
142
  else
118
143
 
119
144
  if Digest::SHA256.digest(Digest::SHA256.digest( payload ))[0...4] != checksum
120
145
  if (length < 50000) && (payload.size < length)
121
146
  size_info = [payload.size, length].join('/')
122
- handle_error(:debug, "chunked packet stream (#{size_info})")
147
+ handle_stream_error(:debug, "chunked packet stream (#{size_info})")
123
148
  else
124
- handle_error(:close, "checksum mismatch")
149
+ handle_stream_error(:close, "checksum mismatch")
125
150
  end
126
151
  return
127
152
  end
@@ -134,7 +159,7 @@ module Bitcoin
134
159
  @buf[0] != nil
135
160
  end
136
161
 
137
- def handle_error(type, msg)
162
+ def handle_stream_error(type, msg)
138
163
  case type
139
164
  when :close
140
165
  log.debug {"closing packet stream (#{msg})"}
@@ -1,3 +1,5 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  require 'bitcoin/script'
2
4
 
3
5
  module Bitcoin
@@ -39,12 +41,12 @@ module Bitcoin
39
41
  # create tx from raw binary +data+
40
42
  def initialize(data=nil)
41
43
  @ver, @lock_time, @in, @out = 1, 0, [], []
42
- parse_data(data) if data
44
+ parse_data_from_io(data) if data
43
45
  end
44
46
 
45
47
  # generate the tx hash for given +payload+ in hex format
46
48
  def hash_from_payload(payload)
47
- Digest::SHA256.digest(Digest::SHA256.digest( payload )).reverse.unpack("H*")[0]
49
+ Digest::SHA256.digest(Digest::SHA256.digest( payload )).reverse_hth
48
50
  end
49
51
  alias generate_hash hash_from_payload
50
52
 
@@ -55,49 +57,49 @@ module Bitcoin
55
57
  def add_out(output); (@out ||= []) << output; end
56
58
 
57
59
  # parse raw binary data
58
- def parse_data(data)
59
- @ver = data.unpack("I")[0]
60
- idx = 4
61
- in_size, tmp = Protocol.unpack_var_int(data[idx..-1])
62
- idx += data[idx..-1].bytesize-tmp.bytesize
63
- # raise "unkown transaction version: #{@ver}" unless @ver == 1
64
-
65
- @in = (0...in_size).map{
66
- txin = TxIn.new
67
- idx += txin.parse_data(data[idx..-1])
68
- txin
69
- }
60
+ def parse_data_from_io(data)
61
+ buf = data.is_a?(String) ? StringIO.new(data) : data
62
+ payload_start = buf.pos
70
63
 
71
- out_size, tmp = Protocol.unpack_var_int(data[idx..-1])
72
- idx += data[idx..-1].bytesize-tmp.bytesize
64
+ @ver = buf.read(4).unpack("V")[0]
73
65
 
74
- @out = (0...out_size).map{
75
- txout = TxOut.new
76
- idx += txout.parse_data(data[idx..-1])
77
- txout
78
- }
66
+ in_size = Protocol.unpack_var_int_from_io(buf)
67
+ @in = []
68
+ in_size.times{ @in << TxIn.from_io(buf) }
69
+
70
+ out_size = Protocol.unpack_var_int_from_io(buf)
71
+ @out = []
72
+ out_size.times{ @out << TxOut.from_io(buf) }
79
73
 
80
- @lock_time = data[idx...idx+=4].unpack("I")[0]
74
+ @lock_time = buf.read(4).unpack("V")[0]
81
75
 
82
- @payload = data[0...idx]
76
+ payload_end = buf.pos;
77
+ buf.seek(payload_start)
78
+ @payload = buf.read( payload_end-payload_start )
83
79
  @hash = hash_from_payload(@payload)
84
80
 
85
- if data[idx] == nil
86
- true # reached the end.
81
+ if buf.eof?
82
+ true
87
83
  else
88
- data[idx..-1] # rest of buffer.
84
+ data.is_a?(StringIO) ? buf : buf.read
89
85
  end
90
86
  end
91
87
 
88
+ alias :parse_data :parse_data_from_io
89
+
92
90
  # output transaction in raw binary format
93
91
  def to_payload
94
- pin = @in.map(&:to_payload).join
95
- pout = @out.map(&:to_payload).join
92
+ pin = ""
93
+ @in.each{|input| pin << input.to_payload }
94
+ pout = ""
95
+ @out.each{|output| pout << output.to_payload }
96
96
 
97
- in_size, out_size = Protocol.pack_var_int(@in.size), Protocol.pack_var_int(@out.size)
98
- [[@ver].pack("I"), in_size, pin, out_size, pout, [@lock_time].pack("I")].join
97
+ [@ver].pack("V") << Protocol.pack_var_int(@in.size) << pin << Protocol.pack_var_int(@out.size) << pout << [@lock_time].pack("V")
99
98
  end
100
99
 
100
+
101
+ SIGHASH_TYPE = { all: 1, none: 2, single: 3, anyonecanpay: 128 }
102
+
101
103
  # generate a signature hash for input +input_idx+.
102
104
  # either pass the +outpoint_tx+ or the +script_pubkey+ directly.
103
105
  def signature_hash_for_input(input_idx, outpoint_tx, script_pubkey=nil, hash_type=nil, drop_sigs=nil, script=nil)
@@ -105,46 +107,58 @@ module Bitcoin
105
107
  # http://code.google.com/p/bitcoinj/source/browse/trunk/src/com/google/bitcoin/core/Script.java#318
106
108
  # https://en.bitcoin.it/wiki/OP_CHECKSIG#How_it_works
107
109
  # https://github.com/bitcoin/bitcoin/blob/c2e8c8acd8ae0c94c70b59f55169841ad195bb99/src/script.cpp#L1058
110
+ # https://en.bitcoin.it/wiki/OP_CHECKSIG
108
111
 
109
- hash_type ||= 1 # 1: ALL, 2: NONE, 3: SINGLE
112
+ return "\x01".ljust(32, "\x00") if input_idx >= @in.size # ERROR: SignatureHash() : input_idx=%d out of range
113
+
114
+ hash_type ||= SIGHASH_TYPE[:all]
110
115
 
111
116
  pin = @in.map.with_index{|input,idx|
112
117
  if idx == input_idx
113
118
  script_pubkey ||= outpoint_tx.out[ input.prev_out_index ].pk_script
114
- script_pubkey = Bitcoin::Script.binary_from_string(script) if script # force this string a script
115
- script_pubkey = Bitcoin::Script.drop_signatures(script_pubkey, drop_sigs) if drop_sigs # array of signature to drop
119
+ script_pubkey = script if script # force binary aa script
120
+ script_pubkey = Bitcoin::Script.drop_signatures(script_pubkey, drop_sigs) if drop_sigs # array of signature to drop (slow)
121
+ #p Bitcoin::Script.new(script_pubkey).to_string
116
122
  input.to_payload(script_pubkey)
117
123
  else
118
- case hash_type
119
- when 2; input.to_payload("", "\x00\x00\x00\x00")
120
- else; input.to_payload("")
124
+ case (hash_type & 0x1f)
125
+ when SIGHASH_TYPE[:none]; input.to_payload("", "\x00\x00\x00\x00")
126
+ when SIGHASH_TYPE[:single]; input.to_payload("", "\x00\x00\x00\x00")
127
+ else; input.to_payload("")
121
128
  end
122
129
  end
123
- }.join
130
+ }
124
131
 
125
- pout = @out.map(&:to_payload).join
132
+ pout = @out.map(&:to_payload)
133
+ in_size, out_size = Protocol.pack_var_int(@in.size), Protocol.pack_var_int(@out.size)
126
134
 
127
- case hash_type
128
- when 2
135
+ case (hash_type & 0x1f)
136
+ when SIGHASH_TYPE[:none]
129
137
  pout = ""
130
- in_size, out_size = Protocol.pack_var_int(@in.size), Protocol.pack_var_int(0)
131
- else
132
- in_size, out_size = Protocol.pack_var_int(@in.size), Protocol.pack_var_int(@out.size)
138
+ out_size = Protocol.pack_var_int(0)
139
+ when SIGHASH_TYPE[:single]
140
+ return "\x01".ljust(32, "\x00") if input_idx >= @out.size # ERROR: SignatureHash() : input_idx=%d out of range (SIGHASH_SINGLE)
141
+ pout = @out[0...(input_idx+1)].map.with_index{|out,idx| (idx==input_idx) ? out.to_payload : out.to_null_payload }.join
142
+ out_size = Protocol.pack_var_int(input_idx+1)
133
143
  end
134
144
 
135
- buf = [ [@ver].pack("I"), in_size, pin, out_size, pout, [@lock_time, hash_type].pack("II") ].join
145
+ if (hash_type & SIGHASH_TYPE[:anyonecanpay]) != 0
146
+ in_size, pin = Protocol.pack_var_int(1), [ pin[input_idx] ]
147
+ end
148
+
149
+ buf = [ [@ver].pack("V"), in_size, pin, out_size, pout, [@lock_time, hash_type].pack("VV") ].join
136
150
  Digest::SHA256.digest( Digest::SHA256.digest( buf ) )
137
151
  end
138
152
 
139
153
  # verify input signature +in_idx+ against the corresponding
140
154
  # output in +outpoint_tx+
141
- def verify_input_signature(in_idx, outpoint_tx)
155
+ def verify_input_signature(in_idx, outpoint_tx, block_timestamp=Time.now.to_i)
142
156
  outpoint_idx = @in[in_idx].prev_out_index
143
157
  script_sig = @in[in_idx].script_sig
144
158
  script_pubkey = outpoint_tx.out[outpoint_idx].pk_script
145
159
  script = script_sig + script_pubkey
146
160
 
147
- Bitcoin::Script.new(script).run do |pubkey,sig,hash_type,drop_sigs,script|
161
+ Bitcoin::Script.new(script).run(block_timestamp) do |pubkey,sig,hash_type,drop_sigs,script|
148
162
  # this IS the checksig callback, must return true/false
149
163
  hash = signature_hash_for_input(in_idx, outpoint_tx, nil, hash_type, drop_sigs, script)
150
164
  #hash = signature_hash_for_input(in_idx, nil, script_pubkey, hash_type, drop_sigs, script)
@@ -153,20 +167,21 @@ module Bitcoin
153
167
  end
154
168
 
155
169
  # convert to ruby hash (see also #from_hash)
156
- def to_hash
170
+ def to_hash(options = {})
157
171
  @hash ||= hash_from_payload(to_payload)
158
- {
172
+ h = {
159
173
  'hash' => @hash, 'ver' => @ver,
160
174
  'vin_sz' => @in.size, 'vout_sz' => @out.size,
161
175
  'lock_time' => @lock_time, 'size' => (@payload ||= to_payload).bytesize,
162
- 'in' => @in.map(&:to_hash),
163
- 'out' => @out.map(&:to_hash)
176
+ 'in' => @in.map{|i| i.to_hash(options) },
177
+ 'out' => @out.map{|o| o.to_hash(options) }
164
178
  }
179
+ h
165
180
  end
166
181
 
167
182
  # generates rawblock json as seen in the block explorer.
168
183
  def to_json(options = {:space => ''}, *a)
169
- JSON.pretty_generate( to_hash, options )
184
+ JSON.pretty_generate( to_hash(options), options )
170
185
  end
171
186
 
172
187
  # write json representation to a file
@@ -199,7 +214,45 @@ module Bitcoin
199
214
 
200
215
  # read json block from a file
201
216
  def self.from_json_file(path); from_json( Bitcoin::Protocol.read_binary_file(path) ); end
202
- end
203
217
 
218
+ def validator(store, block = nil)
219
+ @validator ||= Bitcoin::Validation::Tx.new(self, store, block)
220
+ end
221
+
222
+ def minimum_relay_fee; calculate_minimum_fee(1_000, true, :relay); end
223
+ def minimum_block_fee; calculate_minimum_fee(1_000, true, :block); end
224
+
225
+ def calculate_minimum_fee(block_size=1, allow_free=true, mode=:block)
226
+ base_fee = (mode == :relay) ? Bitcoin.network[:min_relay_tx_fee] : Bitcoin.network[:min_tx_fee]
227
+ tx_size = to_payload.bytesize
228
+ new_block_size = block_size + tx_size
229
+ min_fee = (1 + tx_size / 1_000) * base_fee
230
+
231
+ if allow_free
232
+ if block_size == 1
233
+ min_fee = 0 if tx_size < 10_000
234
+ else
235
+ min_fee = 0 if new_block_size < 27_000
236
+ end
237
+ end
238
+
239
+ if min_fee < base_fee
240
+ outputs.each{|output| (min_fee = base_fee; break) if output.value < Bitcoin::CENT }
241
+ end
242
+
243
+ if block_size != 1 && new_block_size >= (Bitcoin::MAX_BLOCK_SIZE_GEN/2)
244
+ #return Bitcoin::network[:max_money] if new_block_size >= Bitcoin::MAX_BLOCK_SIZE_GEN
245
+ min_fee *= Bitcoin::MAX_BLOCK_SIZE_GEN / (Bitcoin::MAX_BLOCK_SIZE_GEN - new_block_size)
246
+ end
247
+
248
+ min_fee = Bitcoin::network[:max_money] unless min_fee.between?(0, Bitcoin::network[:max_money])
249
+ min_fee
250
+ end
251
+
252
+ def is_coinbase?
253
+ inputs.size == 1 and inputs.first.coinbase?
254
+ end
255
+
256
+ end
204
257
  end
205
258
  end
@@ -1,3 +1,5 @@
1
+ # encoding: ascii-8bit
2
+
1
3
  module Bitcoin
2
4
  module Protocol
3
5
 
@@ -12,10 +14,19 @@ module Bitcoin
12
14
  # script_sig input Script (signature)
13
15
  attr_accessor :script_sig, :script_sig_length
14
16
 
17
+ # signature hash and the address of the key that needs to sign it
18
+ # (used when dealing with unsigned or partly signed tx)
19
+ attr_accessor :sig_hash, :sig_address
20
+
21
+ alias :script :script_sig
22
+ alias :script_length :script_sig_length
23
+
15
24
  # sequence
16
25
  attr_accessor :sequence
17
26
 
18
27
  DEFAULT_SEQUENCE = "\xff\xff\xff\xff"
28
+ NULL_HASH = "\x00"*32
29
+ COINBASE_INDEX = 0xffffffff
19
30
 
20
31
  def initialize *args
21
32
  @prev_out, @prev_out_index, @script_sig_length,
@@ -33,32 +44,34 @@ module Bitcoin
33
44
 
34
45
  # parse raw binary data for transaction input
35
46
  def parse_data(data)
36
- idx = 0
37
- @prev_out, @prev_out_index = data[idx...idx+=36].unpack("a32I")
38
- @script_sig_length, tmp = Protocol.unpack_var_int(data[idx..-1])
39
- idx += data[idx..-1].bytesize - tmp.bytesize
40
- @script_sig = data[idx...idx+=@script_sig_length]
41
- @sequence = data[idx...idx+=4]
42
- idx
47
+ buf = data.is_a?(String) ? StringIO.new(data) : data
48
+ parse_data_from_io(buf)
49
+ buf.pos
50
+ end
51
+
52
+ def self.from_io(buf)
53
+ txin = new; txin.parse_data_from_io(buf); txin
43
54
  end
44
55
 
45
- alias :parse_payload :parse_data
56
+ def parse_data_from_io(buf)
57
+ @prev_out, @prev_out_index = buf.read(36).unpack("a32V")
58
+ @script_sig_length = Protocol.unpack_var_int_from_io(buf)
59
+ @script_sig = buf.read(@script_sig_length)
60
+ @sequence = buf.read(4)
61
+ end
46
62
 
47
63
  def to_payload(script=@script_sig, sequence=@sequence)
48
- buf = [ @prev_out, @prev_out_index ].pack("a32I")
49
- buf << Protocol.pack_var_int(script.bytesize)
50
- buf << script if script.bytesize > 0
51
- buf << (sequence || DEFAULT_SEQUENCE)
64
+ [@prev_out, @prev_out_index].pack("a32V") << Protocol.pack_var_int(script.bytesize) << script << (sequence || DEFAULT_SEQUENCE)
52
65
  end
53
66
 
54
- def to_hash
55
- t = { 'prev_out' => { 'hash' => @prev_out.reverse.unpack("H*")[0], 'n' => @prev_out_index } }
67
+ def to_hash(options = {})
68
+ t = { 'prev_out' => { 'hash' => @prev_out.reverse_hth, 'n' => @prev_out_index } }
56
69
  if coinbase?
57
70
  t['coinbase'] = @script_sig.unpack("H*")[0]
58
71
  else # coinbase tx
59
72
  t['scriptSig'] = Bitcoin::Script.new(@script_sig).to_string
60
- t['sequence'] = @sequence.unpack("I")[0] unless @sequence == "\xff\xff\xff\xff"
61
73
  end
74
+ t['sequence'] = @sequence.unpack("V")[0] unless @sequence == "\xff\xff\xff\xff"
62
75
  t
63
76
  end
64
77
 
@@ -66,22 +79,25 @@ module Bitcoin
66
79
  txin = TxIn.new([ input['prev_out']['hash'] ].pack('H*').reverse, input['prev_out']['n'])
67
80
  if input['coinbase']
68
81
  txin.script_sig = [ input['coinbase'] ].pack("H*")
69
- txin.sequence = "\xff\xff\xff\xff"
70
82
  else
71
83
  txin.script_sig = Script.binary_from_string(input['scriptSig'])
72
- txin.sequence = [ input['sequence'] || 0xffffffff ].pack("I")
73
84
  end
85
+ txin.sequence = [ input['sequence'] || 0xffffffff ].pack("V")
74
86
  txin
75
87
  end
76
88
 
89
+ def self.from_hex_hash(hash, index)
90
+ TxIn.new([hash].pack("H*").reverse, index, 0)
91
+ end
92
+
77
93
  # previous output in hex
78
94
  def previous_output
79
- @prev_out.reverse.unpack("H*")[0]
95
+ @prev_out.reverse_hth
80
96
  end
81
97
 
82
98
  # check if input is coinbase
83
99
  def coinbase?
84
- (@prev_out_index == 4294967295) && (@prev_out == "\x00"*32)
100
+ (@prev_out_index == COINBASE_INDEX) && (@prev_out == NULL_HASH)
85
101
  end
86
102
 
87
103
  # set script_sig and script_sig_length
@@ -91,6 +107,10 @@ module Bitcoin
91
107
  end
92
108
  alias :script= :script_sig=
93
109
 
110
+ def add_signature_pubkey_script(sig, pubkey_hex)
111
+ self.script = Bitcoin::Script.to_signature_pubkey_script(sig, [pubkey_hex].pack("H*"))
112
+ end
113
+
94
114
  end
95
115
 
96
116
  end