bitcoin-ruby 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/.travis.yml +2 -7
  4. data/COPYING +1 -1
  5. data/Gemfile +2 -6
  6. data/Gemfile.lock +34 -0
  7. data/README.rdoc +16 -68
  8. data/Rakefile +3 -6
  9. data/bin/bitcoin_shell +0 -1
  10. data/{concept-examples/blockchain-pow.rb → examples/concept-blockchain-pow.rb} +0 -0
  11. data/lib/bitcoin.rb +350 -296
  12. data/lib/bitcoin/builder.rb +3 -1
  13. data/lib/bitcoin/connection.rb +2 -1
  14. data/lib/bitcoin/contracthash.rb +76 -0
  15. data/lib/bitcoin/dogecoin.rb +97 -0
  16. data/lib/bitcoin/ffi/bitcoinconsensus.rb +74 -0
  17. data/lib/bitcoin/ffi/openssl.rb +98 -2
  18. data/lib/bitcoin/ffi/secp256k1.rb +144 -0
  19. data/lib/bitcoin/key.rb +12 -2
  20. data/lib/bitcoin/logger.rb +3 -12
  21. data/lib/bitcoin/protocol/block.rb +3 -9
  22. data/lib/bitcoin/protocol/parser.rb +6 -2
  23. data/lib/bitcoin/protocol/tx.rb +44 -13
  24. data/lib/bitcoin/protocol/txin.rb +4 -2
  25. data/lib/bitcoin/protocol/txout.rb +2 -2
  26. data/lib/bitcoin/script.rb +212 -37
  27. data/lib/bitcoin/trezor/mnemonic.rb +130 -0
  28. data/lib/bitcoin/version.rb +1 -1
  29. data/spec/bitcoin/bitcoin_spec.rb +32 -3
  30. data/spec/bitcoin/builder_spec.rb +18 -0
  31. data/spec/bitcoin/contracthash_spec.rb +45 -0
  32. data/spec/bitcoin/dogecoin_spec.rb +176 -0
  33. data/spec/bitcoin/ffi_openssl.rb +45 -0
  34. data/spec/bitcoin/fixtures/156e6e1b84c5c3bd3a0927b25e4119fadce6e6d5186f363317511d1d680fae9a.json +24 -0
  35. data/spec/bitcoin/fixtures/8d0b238a06b5a70be75d543902d02d7a514d68d3252a949a513865ac3538874c.json +24 -0
  36. data/spec/bitcoin/fixtures/coinbase-toshi.json +33 -0
  37. data/spec/bitcoin/fixtures/coinbase.json +24 -0
  38. data/spec/bitcoin/fixtures/dogecoin-block-60323982f9c5ff1b5a954eac9dc1269352835f47c2c5222691d80f0d50dcf053.bin +0 -0
  39. data/spec/bitcoin/fixtures/rawtx-01-toshi.json +46 -0
  40. data/spec/bitcoin/fixtures/rawtx-02-toshi.json +46 -0
  41. data/spec/bitcoin/fixtures/rawtx-03-toshi.json +73 -0
  42. data/spec/bitcoin/fixtures/rawtx-testnet-04fdc38d6722ab4b12d79113fc4b2896bdcc5169710690ee4e78541b98e467b4.bin +0 -0
  43. data/spec/bitcoin/fixtures/rawtx-testnet-0b294c7d11dd21bcccb8393e6744fed7d4d1981a08c00e3e88838cc421f33c9f.bin +0 -0
  44. data/spec/bitcoin/fixtures/rawtx-testnet-3bc52ac063291ad92d95ddda5fd776a342083b95607ad32ed8bc6f8f7d30449e.bin +0 -0
  45. data/spec/bitcoin/fixtures/rawtx-testnet-6f0bbdd4e71a8af4305018d738184df32dbb6f27284fdebd5b56d16947f7c181.bin +0 -0
  46. data/spec/bitcoin/fixtures/rawtx-testnet-a7c9b06e275e8674cc19a5f7d3e557c72c6d93576e635b33212dbe08ab7cdb60.bin +0 -0
  47. data/spec/bitcoin/fixtures/rawtx-testnet-f80acbd2f594d04ddb0e1cacba662132104909157dff526935a3c88abe9201a5.bin +0 -0
  48. data/spec/bitcoin/protocol/block_spec.rb +0 -22
  49. data/spec/bitcoin/protocol/tx_spec.rb +145 -2
  50. data/spec/bitcoin/script/script_spec.rb +282 -0
  51. data/spec/bitcoin/secp256k1_spec.rb +48 -0
  52. data/spec/bitcoin/spec_helper.rb +0 -51
  53. data/spec/bitcoin/trezor/mnemonic_spec.rb +161 -0
  54. metadata +48 -98
  55. data/bin/bitcoin_dns_seed +0 -130
  56. data/bin/bitcoin_gui +0 -80
  57. data/bin/bitcoin_node +0 -153
  58. data/bin/bitcoin_node_cli +0 -81
  59. data/bin/bitcoin_wallet +0 -402
  60. data/doc/CONFIG.rdoc +0 -66
  61. data/doc/EXAMPLES.rdoc +0 -13
  62. data/doc/NAMECOIN.rdoc +0 -34
  63. data/doc/NODE.rdoc +0 -225
  64. data/doc/STORAGE.rdoc +0 -33
  65. data/doc/WALLET.rdoc +0 -102
  66. data/examples/balance.rb +0 -66
  67. data/examples/forwarder.rb +0 -73
  68. data/examples/index_nhash.rb +0 -24
  69. data/examples/reindex_p2sh_addrs.rb +0 -44
  70. data/examples/relay_tx.rb +0 -22
  71. data/examples/verify_tx.rb +0 -57
  72. data/lib/bitcoin/config.rb +0 -58
  73. data/lib/bitcoin/gui/addr_view.rb +0 -44
  74. data/lib/bitcoin/gui/bitcoin-ruby.png +0 -0
  75. data/lib/bitcoin/gui/bitcoin-ruby.svg +0 -80
  76. data/lib/bitcoin/gui/conn_view.rb +0 -38
  77. data/lib/bitcoin/gui/connection.rb +0 -70
  78. data/lib/bitcoin/gui/em_gtk.rb +0 -30
  79. data/lib/bitcoin/gui/gui.builder +0 -1643
  80. data/lib/bitcoin/gui/gui.rb +0 -292
  81. data/lib/bitcoin/gui/helpers.rb +0 -115
  82. data/lib/bitcoin/gui/tree_view.rb +0 -84
  83. data/lib/bitcoin/gui/tx_view.rb +0 -69
  84. data/lib/bitcoin/namecoin.rb +0 -280
  85. data/lib/bitcoin/network/command_client.rb +0 -104
  86. data/lib/bitcoin/network/command_handler.rb +0 -570
  87. data/lib/bitcoin/network/connection_handler.rb +0 -387
  88. data/lib/bitcoin/network/node.rb +0 -565
  89. data/lib/bitcoin/storage/dummy/dummy_store.rb +0 -179
  90. data/lib/bitcoin/storage/models.rb +0 -171
  91. data/lib/bitcoin/storage/sequel/migrations.rb +0 -99
  92. data/lib/bitcoin/storage/sequel/migrations/001_base_schema.rb +0 -52
  93. data/lib/bitcoin/storage/sequel/migrations/002_tx.rb +0 -45
  94. data/lib/bitcoin/storage/sequel/migrations/003_change_txin_script_sig_to_blob.rb +0 -18
  95. data/lib/bitcoin/storage/sequel/migrations/004_change_txin_prev_out_to_blob.rb +0 -18
  96. data/lib/bitcoin/storage/sequel/migrations/005_change_tx_hash_to_bytea.rb +0 -14
  97. data/lib/bitcoin/storage/sequel/migrations/006_add_tx_nhash.rb +0 -31
  98. data/lib/bitcoin/storage/sequel/migrations/007_add_prev_out_index_index.rb +0 -16
  99. data/lib/bitcoin/storage/sequel/migrations/008_add_txin_p2sh_type.rb +0 -31
  100. data/lib/bitcoin/storage/sequel/migrations/009_add_addrs_type.rb +0 -56
  101. data/lib/bitcoin/storage/sequel/sequel_store.rb +0 -551
  102. data/lib/bitcoin/storage/storage.rb +0 -517
  103. data/lib/bitcoin/storage/utxo/migrations/001_base_schema.rb +0 -52
  104. data/lib/bitcoin/storage/utxo/migrations/002_utxo.rb +0 -18
  105. data/lib/bitcoin/storage/utxo/migrations/003_update_indices.rb +0 -14
  106. data/lib/bitcoin/storage/utxo/migrations/004_add_addrs_type.rb +0 -14
  107. data/lib/bitcoin/storage/utxo/utxo_store.rb +0 -374
  108. data/lib/bitcoin/validation.rb +0 -400
  109. data/lib/bitcoin/wallet/coinselector.rb +0 -33
  110. data/lib/bitcoin/wallet/keygenerator.rb +0 -77
  111. data/lib/bitcoin/wallet/keystore.rb +0 -207
  112. data/lib/bitcoin/wallet/txdp.rb +0 -118
  113. data/lib/bitcoin/wallet/wallet.rb +0 -281
  114. data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.bin +0 -0
  115. data/spec/bitcoin/fixtures/freicoin-block-000000005d231b285e63af83edae2d8f5e50e70d396468643092b9239fd3be3c.json +0 -43
  116. data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.bin +0 -0
  117. data/spec/bitcoin/fixtures/freicoin-genesis-block-000000005b1e3d23ecfd2dd4a6e1a35238aa0392c0a8528c40df52376d7efe2c.json +0 -67
  118. data/spec/bitcoin/namecoin_spec.rb +0 -182
  119. data/spec/bitcoin/node/command_api_spec.rb +0 -663
  120. data/spec/bitcoin/storage/models_spec.rb +0 -104
  121. data/spec/bitcoin/storage/reorg_spec.rb +0 -236
  122. data/spec/bitcoin/storage/storage_spec.rb +0 -387
  123. data/spec/bitcoin/storage/validation_spec.rb +0 -300
  124. data/spec/bitcoin/wallet/coinselector_spec.rb +0 -38
  125. data/spec/bitcoin/wallet/keygenerator_spec.rb +0 -69
  126. data/spec/bitcoin/wallet/keystore_spec.rb +0 -190
  127. data/spec/bitcoin/wallet/txdp_spec.rb +0 -76
  128. data/spec/bitcoin/wallet/wallet_spec.rb +0 -238
@@ -98,7 +98,12 @@ module Bitcoin
98
98
  # key1 = Bitcoin::Key.generate
99
99
  # sig = key1.sign("some data")
100
100
  def sign(data)
101
- @key.dsa_sign_asn1(data)
101
+ sig = @key.dsa_sign_asn1(data)
102
+ if Script::is_low_der_signature?(sig)
103
+ sig
104
+ else
105
+ Bitcoin::OpenSSL_EC.signature_to_low_s(sig)
106
+ end
102
107
  end
103
108
 
104
109
  # Verify signature +sig+ for +data+.
@@ -106,7 +111,12 @@ module Bitcoin
106
111
  # key2.verify("some data", sig)
107
112
  def verify(data, sig)
108
113
  regenerate_pubkey unless @key.public_key
109
- @key.dsa_verify_asn1(data, sig)
114
+ sig = Bitcoin::OpenSSL_EC.repack_der_signature(sig)
115
+ if sig
116
+ @key.dsa_verify_asn1(data, sig)
117
+ else
118
+ false
119
+ end
110
120
  end
111
121
 
112
122
 
@@ -1,17 +1,8 @@
1
1
  # encoding: ascii-8bit
2
2
 
3
- if Bitcoin.require_dependency :log4r, exit: false
4
- # monkey-patch Log4r to accept level names as symbols
5
- class Log4r::Logger
6
- def level= l = 0
7
- _level = l.is_a?(Fixnum) ? l : Log4r::LNAMES.index(l.to_s.upcase)
8
- Log4r::Log4rTools.validate_level(_level)
9
- @level = _level
10
- LoggerFactory.define_methods(self)
11
- Log4r::Logger.log_internal {"Logger '#{@fullname}' set to #{LNAMES[@level]}"}
12
- @level
13
- end
14
- end
3
+ begin
4
+ require 'log4r'
5
+ rescue LoadError
15
6
  end
16
7
 
17
8
  module Bitcoin
@@ -56,7 +56,7 @@ module Bitcoin
56
56
  end
57
57
 
58
58
  # create block from raw binary +data+
59
- def initialize(data)
59
+ def initialize(data=nil)
60
60
  @tx = []
61
61
  parse_data_from_io(data) if data
62
62
  end
@@ -73,7 +73,7 @@ module Bitcoin
73
73
  @ver, @prev_block, @mrkl_root, @time, @bits, @nonce = buf.read(80).unpack("Va32a32VVV")
74
74
  recalc_block_hash
75
75
 
76
- if Bitcoin.network[:project] == :namecoin && (@ver & BLOCK_VERSION_AUXPOW) > 0
76
+ if Bitcoin.network[:auxpow_chain_id] != nil && (@ver & BLOCK_VERSION_AUXPOW) > 0
77
77
  @aux_pow = AuxPow.new(nil)
78
78
  @aux_pow.parse_data_from_io(buf)
79
79
  end
@@ -197,7 +197,7 @@ module Bitcoin
197
197
  end
198
198
  @aux_pow = AuxPow.from_hash(h['aux_pow']) if h['aux_pow']
199
199
  h['tx'].each{|tx| @tx << Tx.from_hash(tx) }
200
- if h['tx'].any? && !Bitcoin.freicoin?
200
+ if h['tx'].any?
201
201
  (raise "Block merkle root mismatch! Block: #{h['hash']}" unless verify_mrkl_root) if do_raise
202
202
  end
203
203
  }
@@ -231,12 +231,6 @@ module Bitcoin
231
231
  # read json block from a file
232
232
  def self.from_json_file(path); from_json( Bitcoin::Protocol.read_binary_file(path) ); end
233
233
 
234
- # Get a Bitcoin::Validation object to validate this block. It needs a +store+
235
- # to validate against, and optionally takes the +prev_block+ for optimization.
236
- def validator(store, prev_block = nil)
237
- @validator ||= Bitcoin::Validation::Block.new(self, store, prev_block)
238
- end
239
-
240
234
  # get the (statistical) amount of work that was needed to generate this block.
241
235
  def block_work
242
236
  target = Bitcoin.decode_compact_bits(@bits).to_i(16)
@@ -23,7 +23,7 @@ module Bitcoin
23
23
  #
24
24
  def parse_inv(payload, type=:put)
25
25
  count, payload = Protocol.unpack_var_int(payload)
26
- payload.each_byte.each_slice(36){|i|
26
+ payload.each_byte.each_slice(36).with_index{|i, idx|
27
27
  hash = i[4..-1].reverse.pack("C32")
28
28
  case i[0]
29
29
  when 1
@@ -34,7 +34,11 @@ module Bitcoin
34
34
  end
35
35
  when 2
36
36
  if type == :put
37
- @h.on_inv_block(hash)
37
+ if @h.respond_to?(:on_inv_block_v2)
38
+ @h.on_inv_block_v2(hash, idx, count)
39
+ else
40
+ @h.on_inv_block(hash)
41
+ end
38
42
  else
39
43
  @h.on_get_block(hash)
40
44
  end
@@ -44,6 +44,7 @@ module Bitcoin
44
44
  # create tx from raw binary +data+
45
45
  def initialize(data=nil)
46
46
  @ver, @lock_time, @in, @out, @scripts = 1, 0, [], [], []
47
+ @enable_bitcoinconsensus = !!ENV['USE_BITCOINCONSENSUS']
47
48
  parse_data_from_io(data) if data
48
49
  end
49
50
 
@@ -160,7 +161,13 @@ module Bitcoin
160
161
  # verify input signature +in_idx+ against the corresponding
161
162
  # output in +outpoint_tx+
162
163
  # outpoint
163
- def verify_input_signature(in_idx, outpoint_tx_or_script, block_timestamp=Time.now.to_i)
164
+ #
165
+ # options are: verify_sigpushonly, verify_minimaldata, verify_cleanstack, verify_dersig, verify_low_s, verify_strictenc
166
+ def verify_input_signature(in_idx, outpoint_tx_or_script, block_timestamp=Time.now.to_i, opts={})
167
+ if @enable_bitcoinconsensus
168
+ return bitcoinconsensus_verify_script(in_idx, outpoint_tx_or_script, block_timestamp, opts)
169
+ end
170
+
164
171
  outpoint_idx = @in[in_idx].prev_out_index
165
172
  script_sig = @in[in_idx].script_sig
166
173
 
@@ -173,10 +180,39 @@ module Bitcoin
173
180
  end
174
181
 
175
182
  @scripts[in_idx] = Bitcoin::Script.new(script_sig, script_pubkey)
176
- @scripts[in_idx].run(block_timestamp) do |pubkey,sig,hash_type,subscript|
183
+ return false if opts[:verify_sigpushonly] && !@scripts[in_idx].is_push_only?(script_sig)
184
+ return false if opts[:verify_minimaldata] && !@scripts[in_idx].pushes_are_canonical?
185
+ sig_valid = @scripts[in_idx].run(block_timestamp, opts) do |pubkey,sig,hash_type,subscript|
177
186
  hash = signature_hash_for_input(in_idx, subscript, hash_type)
178
187
  Bitcoin.verify_signature( hash, sig, pubkey.unpack("H*")[0] )
179
188
  end
189
+ # BIP62 rule #6
190
+ return false if opts[:verify_cleanstack] && !@scripts[in_idx].stack.empty?
191
+
192
+ return sig_valid
193
+ end
194
+
195
+ def bitcoinconsensus_verify_script(in_idx, outpoint_tx_or_script, block_timestamp=Time.now.to_i, opts={})
196
+ raise "Bitcoin::BitcoinConsensus shared library not found" unless Bitcoin::BitcoinConsensus.lib_available?
197
+
198
+ # If given an entire previous transaction, take the script from it
199
+ script_pubkey = if outpoint_tx_or_script.respond_to?(:out)
200
+ outpoint_idx = @in[in_idx].prev_out_index
201
+ outpoint_tx_or_script.out[outpoint_idx].pk_script
202
+ else
203
+ # Otherwise, it's already a script.
204
+ outpoint_tx_or_script
205
+ end
206
+
207
+ flags = Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_NONE
208
+ flags |= Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_P2SH if block_timestamp >= 1333238400
209
+ flags |= Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_SIGPUSHONLY if opts[:verify_sigpushonly]
210
+ flags |= Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_MINIMALDATA if opts[:verify_minimaldata]
211
+ flags |= Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_CLEANSTACK if opts[:verify_cleanstack]
212
+ flags |= Bitcoin::BitcoinConsensus::SCRIPT_VERIFY_LOW_S if opts[:verify_low_s]
213
+
214
+ payload ||= to_payload
215
+ Bitcoin::BitcoinConsensus.verify_script(in_idx, script_pubkey, payload, flags)
180
216
  end
181
217
 
182
218
  # convert to ruby hash (see also #from_hash)
@@ -206,9 +242,11 @@ module Bitcoin
206
242
  # parse ruby hash (see also #to_hash)
207
243
  def self.from_hash(h)
208
244
  tx = new(nil)
209
- tx.ver, tx.lock_time = *h.values_at('ver', 'lock_time')
210
- h['in'] .each{|input| tx.add_in TxIn.from_hash(input) }
211
- h['out'].each{|output| tx.add_out TxOut.from_hash(output) }
245
+ tx.ver, tx.lock_time = (h['ver'] || h['version']), h['lock_time']
246
+ ins = h['in'] || h['inputs']
247
+ outs = h['out'] || h['outputs']
248
+ ins .each{|input| tx.add_in TxIn.from_hash(input) }
249
+ outs.each{|output| tx.add_out TxOut.from_hash(output) }
212
250
  tx.instance_eval{ @hash = hash_from_payload(@payload = to_payload) }
213
251
  tx
214
252
  end
@@ -228,13 +266,6 @@ module Bitcoin
228
266
  # read json block from a file
229
267
  def self.from_json_file(path); from_json( Bitcoin::Protocol.read_binary_file(path) ); end
230
268
 
231
- # Get a Bitcoin::Validation object to validate this block. It needs a +store+
232
- # to validate against, a block to validate tx chains inside one block, and
233
- # optionally takes the +block_validator+ as an optimization.
234
- def validator(store, block = nil, block_validator = nil)
235
- @validator ||= Bitcoin::Validation::Tx.new(self, store, block, block_validator)
236
- end
237
-
238
269
  def size
239
270
  payload.bytesize
240
271
  end
@@ -316,7 +347,7 @@ module Bitcoin
316
347
  end
317
348
 
318
349
  def normalized_hash
319
- signature_hash_for_input(-1, nil, SIGHASH_TYPE[:all]).unpack("H*")[0]
350
+ signature_hash_for_input(-1, nil, SIGHASH_TYPE[:all]).reverse.hth
320
351
  end
321
352
  alias :nhash :normalized_hash
322
353
 
@@ -85,11 +85,13 @@ module Bitcoin
85
85
  end
86
86
 
87
87
  def self.from_hash(input)
88
- txin = TxIn.new([ input['prev_out']['hash'] ].pack('H*').reverse, input['prev_out']['n'])
88
+ previous_hash = input['previous_transaction_hash'] || input['prev_out']['hash']
89
+ previous_output_index = input['output_index'] || input['prev_out']['n']
90
+ txin = TxIn.new([ previous_hash ].pack('H*').reverse, previous_output_index)
89
91
  if input['coinbase']
90
92
  txin.script_sig = [ input['coinbase'] ].pack("H*")
91
93
  else
92
- txin.script_sig = Script.binary_from_string(input['scriptSig'])
94
+ txin.script_sig = Script.binary_from_string(input['scriptSig'] || input['script'])
93
95
  end
94
96
  txin.sequence = [ input['sequence'] || 0xffffffff ].pack("V")
95
97
  txin
@@ -66,8 +66,8 @@ module Bitcoin
66
66
  end
67
67
 
68
68
  def self.from_hash(output)
69
- amount = output['value'].gsub('.','').to_i
70
- script = Script.binary_from_string(output['scriptPubKey'])
69
+ amount = output['value'] ? output['value'].gsub('.','').to_i : output['amount']
70
+ script = Script.binary_from_string(output['scriptPubKey'] || output['script'])
71
71
  new(amount, script)
72
72
  end
73
73
 
@@ -152,24 +152,25 @@ class Bitcoin::Script
152
152
 
153
153
  SIGHASH_TYPE = { all: 1, none: 2, single: 3, anyonecanpay: 128 }
154
154
 
155
- attr_reader :raw, :chunks, :debug
155
+ attr_reader :raw, :chunks, :debug, :stack
156
156
 
157
157
  # create a new script. +bytes+ is typically input_script + output_script
158
158
  def initialize(input_script, previous_output_script=nil)
159
159
  @raw_byte_sizes = [input_script.bytesize, previous_output_script ? previous_output_script.bytesize : 0]
160
+ @input_script, @previous_output_script = input_script, previous_output_script
160
161
 
161
- @raw = if previous_output_script
162
- input_script + [ Bitcoin::Script::OP_CODESEPARATOR ].pack("C") + previous_output_script
162
+ @raw = if @previous_output_script
163
+ @input_script + [ Bitcoin::Script::OP_CODESEPARATOR ].pack("C") + @previous_output_script
163
164
  else
164
- input_script
165
+ @input_script
165
166
  end
166
167
 
167
- @chunks = parse(input_script)
168
+ @chunks = parse(@input_script)
168
169
 
169
170
  if previous_output_script
170
171
  @script_codeseparator_index = @chunks.size
171
172
  @chunks << Bitcoin::Script::OP_CODESEPARATOR
172
- @chunks += parse(previous_output_script)
173
+ @chunks += parse(@previous_output_script)
173
174
  end
174
175
 
175
176
  @stack, @stack_alt, @exec_stack = [], [], []
@@ -354,8 +355,12 @@ class Bitcoin::Script
354
355
  end
355
356
 
356
357
  # script object of a string representation
357
- def self.from_string(script_string)
358
- new(binary_from_string(script_string))
358
+ def self.from_string(input_script, previous_output_script=nil)
359
+ if previous_output_script
360
+ new(binary_from_string(input_script), binary_from_string(previous_output_script))
361
+ else
362
+ new(binary_from_string(input_script))
363
+ end
359
364
  end
360
365
 
361
366
  class ScriptOpcodeError < StandardError; end
@@ -396,7 +401,7 @@ class Bitcoin::Script
396
401
  end
397
402
 
398
403
  # run the script. +check_callback+ is called for OP_CHECKSIG operations
399
- def run(block_timestamp=Time.now.to_i, &check_callback)
404
+ def run(block_timestamp=Time.now.to_i, opts={}, &check_callback)
400
405
  return false if @parse_invalid
401
406
 
402
407
  #p [to_string, block_timestamp, is_p2sh?]
@@ -404,7 +409,7 @@ class Bitcoin::Script
404
409
  @last_codeseparator_index = 0
405
410
 
406
411
  if block_timestamp >= 1333238400 # Pay to Script Hash (BIP 0016)
407
- return pay_to_script_hash(check_callback) if is_p2sh?
412
+ return pay_to_script_hash(block_timestamp, opts, check_callback) if is_p2sh?
408
413
  end
409
414
 
410
415
  @debug = []
@@ -430,7 +435,17 @@ class Bitcoin::Script
430
435
  when *OPCODES_METHOD.keys
431
436
  m = method( n=OPCODES_METHOD[chunk] )
432
437
  @debug << n.to_s.upcase
433
- (m.arity == 1) ? m.call(check_callback) : m.call # invoke opcode method
438
+ # invoke opcode method
439
+ case m.arity
440
+ when 0
441
+ m.call
442
+ when 1
443
+ m.call(check_callback)
444
+ when -2 # One fixed parameter, one optional
445
+ m.call(check_callback, opts)
446
+ else
447
+ puts "Bitcoin::Script: opcode #{name} method parameters invalid"
448
+ end
434
449
  when *OP_2_16
435
450
  @stack << OP_2_16.index(chunk) + 2
436
451
  @debug << "OP_#{chunk-80}"
@@ -473,17 +488,19 @@ class Bitcoin::Script
473
488
  # pay_to_script_hash: https://en.bitcoin.it/wiki/BIP_0016
474
489
  #
475
490
  # <sig> {<pub> OP_CHECKSIG} | OP_HASH160 <script_hash> OP_EQUAL
476
- def pay_to_script_hash(check_callback)
491
+ def pay_to_script_hash(block_timestamp, opts, check_callback)
477
492
  return false if @chunks.size < 4
478
493
  *rest, script, _, script_hash, _ = @chunks
479
494
  script = rest.pop if script == OP_CODESEPARATOR
480
495
  script, script_hash = cast_to_string(script), cast_to_string(script_hash)
481
496
 
482
497
  return false unless Bitcoin.hash160(script.unpack("H*")[0]) == script_hash.unpack("H*")[0]
498
+ return true if check_callback == :check
483
499
 
484
500
  script = self.class.new(to_binary(rest) + script).inner_p2sh!(script)
485
- result = script.run(&check_callback)
501
+ result = script.run(block_timestamp, opts, &check_callback)
486
502
  @debug = script.debug
503
+ @stack = script.stack # Set the execution stack to match the redeem script, so checks on stack contents at end of script execution validate correctly
487
504
  result
488
505
  end
489
506
 
@@ -501,11 +518,23 @@ class Bitcoin::Script
501
518
  script
502
519
  end
503
520
 
521
+ # is this a :script_hash (pay-to-script-hash/p2sh) script?
504
522
  def is_pay_to_script_hash?
505
523
  return false if @inner_p2sh
506
- return false unless @chunks[-2].is_a?(String)
507
- @chunks.size >= 3 && @chunks[-3] == OP_HASH160 &&
508
- @chunks[-2].bytesize == 20 && @chunks[-1] == OP_EQUAL
524
+ if @previous_output_script
525
+ chunks = Bitcoin::Script.new(@previous_output_script).chunks
526
+ chunks.size == 3 &&
527
+ chunks[-3] == OP_HASH160 &&
528
+ chunks[-2].is_a?(String) && chunks[-2].bytesize == 20 &&
529
+ chunks[-1] == OP_EQUAL
530
+ else
531
+ @chunks.size >= 3 &&
532
+ @chunks[-3] == OP_HASH160 &&
533
+ @chunks[-2].is_a?(String) && @chunks[-2].bytesize == 20 &&
534
+ @chunks[-1] == OP_EQUAL &&
535
+ # make sure the script_sig matches the p2sh hash from the pk_script (if there is one)
536
+ (@chunks.size > 3 ? pay_to_script_hash(nil, nil, :check) : true)
537
+ end
509
538
  end
510
539
  alias :is_p2sh? :is_pay_to_script_hash?
511
540
 
@@ -540,6 +569,54 @@ class Bitcoin::Script
540
569
  @chunks[0] == OP_RETURN && @chunks.size <= 2
541
570
  end
542
571
 
572
+ # Verify the script is only pushing data onto the stack
573
+ def is_push_only?(script_data=nil)
574
+ check_pushes(push_only=true, canonical_only=false, (script_data||@input_script))
575
+ end
576
+
577
+ # Make sure opcodes used to push data match their intended length ranges
578
+ def pushes_are_canonical?(script_data=nil)
579
+ check_pushes(push_only=false, canonical_only=true, (script_data||@raw))
580
+ end
581
+
582
+ def check_pushes(push_only=true, canonical_only=false, buf)
583
+ program = buf.unpack("C*")
584
+ until program.empty?
585
+ opcode = program.shift
586
+ if opcode > OP_16
587
+ return false if push_only
588
+ next
589
+ end
590
+ if opcode < OP_PUSHDATA1 && opcode > OP_0
591
+ # Could have used an OP_n code, rather than a 1-byte push.
592
+ return false if canonical_only && opcode == 1 && program[0] <= 16
593
+ program.shift(opcode)
594
+ end
595
+ if opcode == OP_PUSHDATA1
596
+ len = program.shift(1)[0]
597
+ # Could have used a normal n-byte push, rather than OP_PUSHDATA1.
598
+ return false if canonical_only && len < OP_PUSHDATA1
599
+ program.shift(len)
600
+ end
601
+ if opcode == OP_PUSHDATA2
602
+ len = program.shift(2).pack("C*").unpack("v")[0]
603
+ # Could have used an OP_PUSHDATA1.
604
+ return false if canonical_only && len <= 0xff
605
+ program.shift(len)
606
+ end
607
+ if opcode == OP_PUSHDATA4
608
+ len = program.shift(4).pack("C*").unpack("V")[0]
609
+ # Could have used an OP_PUSHDATA2.
610
+ return false if canonical_only && len <= 0xffff
611
+ program.shift(len)
612
+ end
613
+ end
614
+ true
615
+ rescue => ex
616
+ # catch parsing errors
617
+ false
618
+ end
619
+
543
620
  # get type of this tx
544
621
  def type
545
622
  if is_hash160?; :hash160
@@ -671,9 +748,8 @@ class Bitcoin::Script
671
748
  # generate input script sig spending a pubkey output with given +signature+ and +pubkey+.
672
749
  # returns a raw binary script sig of the form:
673
750
  # <signature> [<pubkey>]
674
- def self.to_pubkey_script_sig(signature, pubkey)
675
- hash_type = [ SIGHASH_TYPE[:all] ].pack("C")
676
- buf = pack_pushdata(signature + hash_type)
751
+ def self.to_pubkey_script_sig(signature, pubkey, hash_type = SIGHASH_TYPE[:all])
752
+ buf = pack_pushdata(signature + [hash_type].pack("C"))
677
753
  return buf unless pubkey
678
754
 
679
755
  expected_size = case pubkey[0]
@@ -704,8 +780,9 @@ class Bitcoin::Script
704
780
  # returns a raw binary script sig of the form:
705
781
  # OP_0 <sig> [<sig> ...]
706
782
  def self.to_multisig_script_sig(*sigs)
783
+ hash_type = sigs.last.is_a?(Numeric) ? sigs.pop : SIGHASH_TYPE[:all]
707
784
  partial_script = [OP_0].pack("C*")
708
- sigs.reverse_each{ |sig| partial_script = add_sig_to_multisig_script_sig(sig, partial_script) }
785
+ sigs.reverse_each{ |sig| partial_script = add_sig_to_multisig_script_sig(sig, partial_script, hash_type) }
709
786
  partial_script
710
787
  end
711
788
 
@@ -713,8 +790,8 @@ class Bitcoin::Script
713
790
  # another signature to it after the OP_0. Used to sign a tx by
714
791
  # multiple parties. Signatures must be in the same order as the
715
792
  # pubkeys in the output script being redeemed.
716
- def self.add_sig_to_multisig_script_sig(sig, script_sig)
717
- signature = sig + [SIGHASH_TYPE[:all]].pack("C*")
793
+ def self.add_sig_to_multisig_script_sig(sig, script_sig, hash_type = SIGHASH_TYPE[:all])
794
+ signature = sig + [hash_type].pack("C*")
718
795
  offset = script_sig.empty? ? 0 : 1
719
796
  script_sig.insert(offset, pack_pushdata(signature))
720
797
  end
@@ -726,6 +803,25 @@ class Bitcoin::Script
726
803
  to_multisig_script_sig(*sigs.flatten) + pack_pushdata(redeem_script)
727
804
  end
728
805
 
806
+ # Sort signatures in the given +script_sig+ according to the order of pubkeys in
807
+ # the redeem script. Also needs the +sig_hash+ to match signatures to pubkeys.
808
+ def self.sort_p2sh_multisig_signatures script_sig, sig_hash
809
+ script = new(script_sig)
810
+ redeem_script = new(script.chunks[-1])
811
+ pubkeys = redeem_script.get_multisig_pubkeys
812
+
813
+ # find the pubkey for each signature by trying to verify it
814
+ sigs = Hash[script.chunks[1...-1].map.with_index do |sig, idx|
815
+ pubkey = pubkeys.map {|key|
816
+ Bitcoin::Key.new(nil, key.hth).verify(sig_hash, sig) ? key : nil }.compact.first
817
+ raise "Key for signature ##{idx} not found in redeem script!" unless pubkey
818
+ [pubkey, sig]
819
+ end]
820
+
821
+ [OP_0].pack("C*") + pubkeys.map {|k| sigs[k] ? pack_pushdata(sigs[k]) : nil }.join +
822
+ pack_pushdata(redeem_script.raw)
823
+ end
824
+
729
825
  def get_signatures_required
730
826
  return false unless is_multisig?
731
827
  @chunks[0] - 80
@@ -1240,14 +1336,14 @@ class Bitcoin::Script
1240
1336
  # do a CHECKSIG operation on the current stack,
1241
1337
  # asking +check_callback+ to do the actual signature verification.
1242
1338
  # This is used by Protocol::Tx#verify_input_signature
1243
- def op_checksig(check_callback)
1339
+ def op_checksig(check_callback, opts={})
1244
1340
  return invalid if @stack.size < 2
1245
1341
  pubkey = cast_to_string(@stack.pop)
1246
- #return (@stack << 0) unless Bitcoin::Script.is_canonical_pubkey?(pubkey) # only for isStandard
1342
+ return (@stack << 0) unless Bitcoin::Script.check_pubkey_encoding?(pubkey, opts)
1247
1343
  drop_sigs = [ cast_to_string(@stack[-1]) ]
1248
1344
 
1249
1345
  signature = cast_to_string(@stack.pop)
1250
- #return (@stack << 0) unless Bitcoin::Script.is_canonical_signature?(signature) # only for isStandard
1346
+ return invalid unless Bitcoin::Script.check_signature_encoding?(signature, opts)
1251
1347
  return (@stack << 0) if signature == ""
1252
1348
 
1253
1349
  sig, hash_type = parse_sig(signature)
@@ -1271,8 +1367,8 @@ class Bitcoin::Script
1271
1367
  end
1272
1368
 
1273
1369
  # Same as OP_CHECKSIG, but OP_VERIFY is executed afterward.
1274
- def op_checksigverify(check_callback)
1275
- op_checksig(check_callback)
1370
+ def op_checksigverify(check_callback, opts={})
1371
+ op_checksig(check_callback, opts)
1276
1372
  op_verify
1277
1373
  end
1278
1374
 
@@ -1289,7 +1385,7 @@ class Bitcoin::Script
1289
1385
  #
1290
1386
  # TODO: validate signature order
1291
1387
  # TODO: take global opcode count
1292
- def op_checkmultisig(check_callback)
1388
+ def op_checkmultisig(check_callback, opts={})
1293
1389
  return invalid if @stack.size < 1
1294
1390
  n_pubkeys = pop_int
1295
1391
  return invalid unless (0..20).include?(n_pubkeys)
@@ -1312,6 +1408,8 @@ class Bitcoin::Script
1312
1408
  success = true
1313
1409
  while success && n_sigs > 0
1314
1410
  sig, pub = sigs.pop, pubkeys.pop
1411
+ return (@stack << 0) unless Bitcoin::Script.check_pubkey_encoding?(pub, opts)
1412
+ return invalid unless Bitcoin::Script.check_signature_encoding?(sig, opts)
1315
1413
  unless sig && sig.size > 0
1316
1414
  success = false
1317
1415
  break
@@ -1330,8 +1428,8 @@ class Bitcoin::Script
1330
1428
  end
1331
1429
 
1332
1430
  # Same as OP_CHECKMULTISIG, but OP_VERIFY is executed afterward.
1333
- def op_checkmultisigverify(check_callback)
1334
- op_checkmultisig(check_callback)
1431
+ def op_checkmultisigverify(check_callback, opts={})
1432
+ op_checkmultisig(check_callback, opts)
1335
1433
  op_verify
1336
1434
  end
1337
1435
 
@@ -1346,7 +1444,12 @@ class Bitcoin::Script
1346
1444
  OPCODES_METHOD[0] = :op_0
1347
1445
  OPCODES_METHOD[81] = :op_1
1348
1446
 
1349
- def self.is_canonical_pubkey?(pubkey)
1447
+ def self.check_pubkey_encoding?(pubkey, opts={})
1448
+ return false if opts[:verify_strictenc] && !is_compressed_or_uncompressed_pub_key?(pubkey)
1449
+ true
1450
+ end
1451
+
1452
+ def self.is_compressed_or_uncompressed_pub_key?(pubkey)
1350
1453
  return false if pubkey.bytesize < 33 # "Non-canonical public key: too short"
1351
1454
  case pubkey[0]
1352
1455
  when "\x04"
@@ -1359,21 +1462,93 @@ class Bitcoin::Script
1359
1462
  true
1360
1463
  end
1361
1464
 
1465
+ # Loosely matches CheckSignatureEncoding()
1466
+ def self.check_signature_encoding?(sig, opts={})
1467
+ return true if sig.bytesize == 0
1468
+ return false if (opts[:verify_dersig] || opts[:verify_low_s] || opts[:verify_strictenc]) and !is_der_signature?(sig)
1469
+ return false if opts[:verify_low_s] && !is_low_der_signature?(sig)
1470
+ return false if opts[:verify_strictenc] && !is_defined_hashtype_signature?(sig)
1471
+ true
1472
+ end
1362
1473
 
1363
-
1364
- def self.is_canonical_signature?(sig)
1474
+ # Loosely correlates with IsDERSignature() from interpreter.cpp
1475
+ def self.is_der_signature?(sig)
1365
1476
  return false if sig.bytesize < 9 # Non-canonical signature: too short
1366
1477
  return false if sig.bytesize > 73 # Non-canonical signature: too long
1367
1478
 
1368
1479
  s = sig.unpack("C*")
1369
1480
 
1370
- hash_type = s[-1] & (~(SIGHASH_TYPE[:anyonecanpay]))
1371
- return false if hash_type < SIGHASH_TYPE[:all] || hash_type > SIGHASH_TYPE[:single] # Non-canonical signature: unknown hashtype byte
1372
-
1373
1481
  return false if s[0] != 0x30 # Non-canonical signature: wrong type
1374
1482
  return false if s[1] != s.size-3 # Non-canonical signature: wrong length marker
1375
1483
 
1376
- # TODO: add/port rest from bitcoind
1484
+ length_r = s[3]
1485
+ return false if (5 + length_r) >= s.size # Non-canonical signature: S length misplaced
1486
+ length_s = s[5+length_r]
1487
+ return false if (length_r + length_s + 7) != s.size # Non-canonical signature: R+S length mismatch
1488
+
1489
+ return false if s[2] != 0x02 # Non-canonical signature: R value type mismatch
1490
+
1491
+ return false if length_r == 0 # Non-canonical signature: R length is zero
1492
+
1493
+ r_val = s.slice(4, length_r)
1494
+ return false if r_val[0] & 0x80 != 0 # Non-canonical signature: R value negative
1495
+
1496
+ return false if length_r > 1 && (r_val[0] == 0x00) && !(r_val[1] & 0x80 != 0) # Non-canonical signature: R value excessively padded
1497
+
1498
+ s_val = s.slice(6 + length_r, length_s)
1499
+ return false if s[6 + length_r - 2] != 0x02 # Non-canonical signature: S value type mismatch
1500
+
1501
+ return false if length_s == 0 # Non-canonical signature: S length is zero
1502
+ return false if (s_val[0] & 0x80) != 0 # Non-canonical signature: S value negative
1503
+
1504
+ return false if length_s > 1 && (s_val[0] == 0x00) && !(s_val[1] & 0x80) # Non-canonical signature: S value excessively padded
1505
+
1506
+ true
1507
+ end
1508
+
1509
+ # Compares two arrays of bytes
1510
+ def self.compare_big_endian(c1, c2)
1511
+ c1, c2 = c1.dup, c2.dup # Clone the arrays
1512
+
1513
+ while c1.size > c2.size
1514
+ return 1 if c1.shift > 0
1515
+ end
1516
+
1517
+ while c2.size > c1.size
1518
+ return -1 if c2.shift > 0
1519
+ end
1520
+
1521
+ c1.size.times{|idx| return c1[idx] - c2[idx] if c1[idx] != c2[idx] }
1522
+ 0
1523
+ end
1524
+
1525
+ # Loosely correlates with IsLowDERSignature() from interpreter.cpp
1526
+ def self.is_low_der_signature?(sig)
1527
+ s = sig.unpack("C*")
1528
+
1529
+ length_r = s[3]
1530
+ length_s = s[5+length_r]
1531
+ s_val = s.slice(6 + length_r, length_s)
1532
+
1533
+ # If the S value is above the order of the curve divided by two, its
1534
+ # complement modulo the order could have been used instead, which is
1535
+ # one byte shorter when encoded correctly.
1536
+ max_mod_half_order = [
1537
+ 0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
1538
+ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
1539
+ 0x5d,0x57,0x6e,0x73,0x57,0xa4,0x50,0x1d,
1540
+ 0xdf,0xe9,0x2f,0x46,0x68,0x1b,0x20,0xa0]
1541
+
1542
+ compare_big_endian(s_val, [0]) > 0 &&
1543
+ compare_big_endian(s_val, max_mod_half_order) <= 0
1544
+ end
1545
+
1546
+ def self.is_defined_hashtype_signature?(sig)
1547
+ return false if sig.empty?
1548
+
1549
+ s = sig.unpack("C*")
1550
+ hash_type = s[-1] & (~(SIGHASH_TYPE[:anyonecanpay]))
1551
+ return false if hash_type < SIGHASH_TYPE[:all] || hash_type > SIGHASH_TYPE[:single] # Non-canonical signature: unknown hashtype byte
1377
1552
 
1378
1553
  true
1379
1554
  end