bitcoin-ruby 0.0.6 → 0.0.7

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