Commit 7905bc192c0bbc32a12962be75b50dd51036c618

Authored by Marius Hanne
1 parent f9d6d3dbd2
Exists in script_definitions

script type definitions

Showing 4 changed files with 415 additions and 323 deletions Side-by-side Diff

... ... @@ -10,6 +10,7 @@
10 10 autoload :Connection, 'bitcoin/connection'
11 11 autoload :Protocol, 'bitcoin/protocol'
12 12 autoload :P, 'bitcoin/protocol'
  13 + autoload :Opcodes, 'bitcoin/opcodes'
13 14 autoload :Script, 'bitcoin/script'
14 15 autoload :VERSION, 'bitcoin/version'
15 16 autoload :Storage, 'bitcoin/storage/storage'
lib/bitcoin/opcodes.rb
  1 +module Bitcoin::Opcodes
  2 + OP_1 = 81
  3 + OP_TRUE = 81
  4 + OP_0 = 0
  5 + OP_FALSE = 0
  6 + OP_PUSHDATA1 = 76
  7 + OP_PUSHDATA2 = 77
  8 + OP_PUSHDATA4 = 78
  9 + OP_NOP = 97
  10 + OP_DUP = 118
  11 + OP_HASH160 = 169
  12 + OP_EQUAL = 135
  13 + OP_VERIFY = 105
  14 + OP_EQUALVERIFY = 136
  15 + OP_CHECKSIG = 172
  16 + OP_CHECKSIGVERIFY = 173
  17 + OP_CHECKMULTISIG = 174
  18 + OP_CHECKMULTISIGVERIFY = 175
  19 + OP_TOALTSTACK = 107
  20 + OP_FROMALTSTACK = 108
  21 + OP_TUCK = 125
  22 + OP_SWAP = 124
  23 + OP_BOOLAND = 154
  24 + OP_ADD = 147
  25 + OP_SUB = 148
  26 + OP_GREATERTHANOREQUAL = 162
  27 + OP_DROP = 117
  28 + OP_HASH256 = 170
  29 + OP_SHA256 = 168
  30 + OP_SHA1 = 167
  31 + OP_RIPEMD160 = 166
  32 + OP_EVAL = 176
  33 + OP_NOP2 = 177
  34 + OP_CHECKHASHVERIFY = 177
  35 + OP_CODESEPARATOR = 171
  36 +
  37 + OPCODES = Hash[*constants.grep(/^OP_/).map{|i| [const_get(i), i.to_s] }.flatten]
  38 + OPCODES[0] = "0"
  39 + OPCODES[81] = "1"
  40 +
  41 + OPCODES_ALIAS = {
  42 + "OP_TRUE" => OP_1,
  43 + "OP_FALSE" => OP_0,
  44 + "OP_NOP1" => OP_EVAL,
  45 + "OP_NOP2" => OP_CHECKHASHVERIFY
  46 + }
  47 +
  48 + OP_2_16 = (82..96).to_a
  49 +
  50 + # Does nothing
  51 + def op_nop
  52 + end
  53 +
  54 + # Duplicates the top stack item.
  55 + def op_dup
  56 + @stack << (@stack[-1].dup rescue @stack[-1])
  57 + end
  58 +
  59 + # The input is hashed using SHA-256.
  60 + def op_sha256
  61 + buf = @stack.pop
  62 + @stack << Digest::SHA256.digest(buf)
  63 + end
  64 +
  65 + # The input is hashed using SHA-1.
  66 + def op_sha1
  67 + buf = @stack.pop
  68 + @stack << Digest::SHA1.digest(buf)
  69 + end
  70 +
  71 + # The input is hashed twice: first with SHA-256 and then with RIPEMD-160.
  72 + def op_hash160
  73 + buf = @stack.pop
  74 + @stack << Digest::RMD160.digest(Digest::SHA256.digest(buf))
  75 + end
  76 +
  77 + # The input is hashed using RIPEMD-160.
  78 + def op_ripemd160
  79 + buf = @stack.pop
  80 + @stack << Digest::RMD160.digest(buf)
  81 + end
  82 +
  83 + # The input is hashed two times with SHA-256.
  84 + def op_hash256
  85 + buf = @stack.pop
  86 + @stack << Digest::SHA256.digest(Digest::SHA256.digest(buf))
  87 + end
  88 +
  89 + # Puts the input onto the top of the alt stack. Removes it from the main stack.
  90 + def op_toaltstack
  91 + @stack_alt << @stack.pop
  92 + end
  93 +
  94 + # Puts the input onto the top of the main stack. Removes it from the alt stack.
  95 + def op_fromaltstack
  96 + @stack << @stack_alt.pop
  97 + end
  98 +
  99 + # The item at the top of the stack is copied and inserted before the second-to-top item.
  100 + def op_tuck
  101 + @stack[-2..-1] = [ @stack[-1], *@stack[-2..-1] ]
  102 + end
  103 +
  104 + # The top two items on the stack are swapped.
  105 + def op_swap
  106 + @stack[-2..-1] = @stack[-2..-1].reverse
  107 + end
  108 +
  109 + # If both a and b are not 0, the output is 1. Otherwise 0.
  110 + def op_booland
  111 + a, b = @stack.pop(2)
  112 + @stack << (![a,b].any?{|n| n == 0 } ? 1 : 0)
  113 + end
  114 +
  115 + # a is added to b.
  116 + def op_add
  117 + a, b = @stack.pop(2).reverse
  118 + @stack << a + b
  119 + end
  120 +
  121 + # b is subtracted from a.
  122 + def op_sub
  123 + a, b = @stack.pop(2).reverse
  124 + @stack << a - b
  125 + end
  126 +
  127 + # Returns 1 if a is greater than or equal to b, 0 otherwise.
  128 + def op_greaterthanorequal
  129 + a, b = @stack.pop(2).reverse
  130 + @stack << (a >= b ? 1 : 0)
  131 + end
  132 +
  133 + # Removes the top stack item.
  134 + def op_drop
  135 + @stack.pop
  136 + end
  137 +
  138 + # Returns 1 if the inputs are exactly equal, 0 otherwise.
  139 + def op_equal
  140 + a, b = @stack.pop(2).reverse
  141 + @stack << (a == b ? 1 : 0)
  142 + end
  143 +
  144 + # Marks transaction as invalid if top stack value is not true. True is removed, but false is not.
  145 + def op_verify
  146 + res = @stack.pop
  147 + if res != 1
  148 + @stack << res
  149 + @script_invalid = true # raise 'transaction invalid' ?
  150 + else
  151 + @script_invalid = false
  152 + end
  153 + end
  154 +
  155 + # Same as OP_EQUAL, but runs OP_VERIFY afterward.
  156 + def op_equalverify
  157 + op_equal; op_verify
  158 + end
  159 +
  160 + # An empty array of bytes is pushed onto the stack.
  161 + def op_0
  162 + @stack << "" # []
  163 + end
  164 +
  165 + # The number 1 is pushed onto the stack. Same as OP_TRUE
  166 + def op_1
  167 + @stack << 1
  168 + end
  169 +
  170 + # https://en.bitcoin.it/wiki/BIP_0017 (old OP_NOP2)
  171 + # TODO: don't rely on it yet. add guards from wikipage too.
  172 + def op_checkhashverify
  173 + unless @checkhash && (@checkhash == @stack[-1].unpack("H*")[0])
  174 + @script_invalid = true
  175 + end
  176 + end
  177 +
  178 + # All of the signature checking words will only match signatures to the data after the most recently-executed OP_CODESEPARATOR.
  179 + def op_codeseparator
  180 + @codehash_start = @chunks.size - @chunks.reverse.index(OP_CODESEPARATOR)
  181 + end
  182 +
  183 + # do a CHECKSIG operation on the current stack,
  184 + # asking +check_callback+ to do the actual signature verification.
  185 + # This is used by Protocol::Tx#verify_input_signature
  186 + def op_checksig(check_callback)
  187 + return invalid if @stack.size < 2
  188 + pubkey = @stack.pop
  189 + drop_sigs = [@stack[-1].unpack("H*")[0]]
  190 + sig, hash_type = parse_sig(@stack.pop)
  191 +
  192 + if @chunks.include?(OP_CHECKHASHVERIFY)
  193 + # Subset of script starting at the most recent codeseparator to OP_CHECKSIG
  194 + script_code, @checkhash = codehash_script(OP_CHECKSIG)
  195 + else
  196 + script_code, drop_sigs = nil, nil
  197 + end
  198 +
  199 + if check_callback == nil # for tests
  200 + @stack << 1
  201 + else # real signature check callback
  202 + @stack <<
  203 + ((check_callback.call(pubkey, sig, hash_type, drop_sigs, script_code) == true) ? 1 : 0)
  204 + end
  205 + end
  206 +
  207 + def op_checksigverify(check_callback)
  208 + op_checksig(check_callback)
  209 + op_verify
  210 + end
  211 +
  212 + # do a CHECKMULTISIG operation on the current stack,
  213 + # asking +check_callback+ to do the actual signature verification.
  214 + #
  215 + # CHECKMULTISIG does a m-of-n signatures verification on scripts of the form:
  216 + # 0 <sig1> <sig2> | 2 <pub1> <pub2> 2 OP_CHECKMULTISIG
  217 + # 0 <sig1> <sig2> | 2 <pub1> <pub2> <pub3> 3 OP_CHECKMULTISIG
  218 + # 0 <sig1> <sig2> <sig3> | 3 <pub1> <pub2> <pub3> 3 OP_CHECKMULTISIG
  219 + #
  220 + # see https://en.bitcoin.it/wiki/BIP_0011 for details.
  221 + # see https://github.com/bitcoin/bitcoin/blob/master/src/script.cpp#L931
  222 + #
  223 + # TODO: validate signature order
  224 + # TODO: take global opcode count
  225 + def op_checkmultisig(check_callback)
  226 + n_pubkeys = @stack.pop
  227 + return invalid unless (0..20).include?(n_pubkeys)
  228 + return invalid unless @stack.last(n_pubkeys).all?{|e| e.is_a?(String) && e != '' }
  229 + #return invalid if ((@op_count ||= 0) += n_pubkeys) > 201
  230 + pubkeys = @stack.pop(n_pubkeys)
  231 +
  232 + n_sigs = @stack.pop
  233 + return invalid unless (0..n_pubkeys).include?(n_sigs)
  234 + return invalid unless @stack.last(n_sigs).all?{|e| e.is_a?(String) && e != '' }
  235 + sigs = (drop_sigs = @stack.pop(n_sigs)).map{|s| parse_sig(s) }
  236 +
  237 + @stack.pop if @stack[-1] == '' # remove OP_NOP from stack
  238 +
  239 + if @chunks.include?(OP_CHECKHASHVERIFY)
  240 + # Subset of script starting at the most recent codeseparator to OP_CHECKMULTISIG
  241 + script_code, @checkhash = codehash_script(OP_CHECKMULTISIG)
  242 + drop_sigs.map!{|i| i.unpack("H*")[0] }
  243 + else
  244 + script_code, drop_sigs = nil, nil
  245 + end
  246 +
  247 + valid_sigs = 0
  248 + sigs.each{|sig, hash_type| pubkeys.each{|pubkey|
  249 + valid_sigs += 1 if check_callback.call(pubkey, sig, hash_type, drop_sigs, script_code)
  250 + }}
  251 +
  252 + @stack << ((valid_sigs == n_sigs) ? 1 : (invalid; 0))
  253 + end
  254 +
  255 + OPCODES_METHOD = Hash[*instance_methods.grep(/^op_/).map{|m|
  256 + [ (OPCODES.find{|k,v| v == m.to_s.upcase }.first rescue nil), m ]
  257 + }.flatten]
  258 + OPCODES_METHOD[0] = :op_0
  259 + OPCODES_METHOD[81] = :op_1
  260 +
  261 + private
  262 +
  263 + def parse_sig(sig)
  264 + hash_type = sig[-1].unpack("C")[0]
  265 + sig = sig[0...-1]
  266 + return sig, hash_type
  267 + end
  268 +
  269 +end
lib/bitcoin/script.rb
1 1 require 'bitcoin'
2 2  
3 3 module Bitcoin
  4 +
4 5 class Script
5   - OP_1 = 81
6   - OP_TRUE = 81
7   - OP_0 = 0
8   - OP_FALSE = 0
9   - OP_PUSHDATA1 = 76
10   - OP_PUSHDATA2 = 77
11   - OP_PUSHDATA4 = 78
12   - OP_NOP = 97
13   - OP_DUP = 118
14   - OP_HASH160 = 169
15   - OP_EQUAL = 135
16   - OP_VERIFY = 105
17   - OP_EQUALVERIFY = 136
18   - OP_CHECKSIG = 172
19   - OP_CHECKSIGVERIFY = 173
20   - OP_CHECKMULTISIG = 174
21   - OP_CHECKMULTISIGVERIFY = 175
22   - OP_TOALTSTACK = 107
23   - OP_FROMALTSTACK = 108
24   - OP_TUCK = 125
25   - OP_SWAP = 124
26   - OP_BOOLAND = 154
27   - OP_ADD = 147
28   - OP_SUB = 148
29   - OP_GREATERTHANOREQUAL = 162
30   - OP_DROP = 117
31   - OP_HASH256 = 170
32   - OP_SHA256 = 168
33   - OP_SHA1 = 167
34   - OP_RIPEMD160 = 166
35   - OP_EVAL = 176
36   - OP_NOP2 = 177
37   - OP_CHECKHASHVERIFY = 177
38   - OP_CODESEPARATOR = 171
39 6  
40   - OPCODES = Hash[*constants.grep(/^OP_/).map{|i| [const_get(i), i.to_s] }.flatten]
41   - OPCODES[0] = "0"
42   - OPCODES[81] = "1"
  7 + include Bitcoin::Opcodes
43 8  
44   - OPCODES_ALIAS = {
45   - "OP_TRUE" => OP_1,
46   - "OP_FALSE" => OP_0,
47   - "OP_NOP1" => OP_EVAL,
48   - "OP_NOP2" => OP_CHECKHASHVERIFY
49   - }
50   -
51   -
52   - OP_2_16 = (82..96).to_a
53   -
54 9 attr_reader :raw, :chunks, :debug
55 10  
56 11 # create a new script. +bytes+ is typically input_script + output_script
57 12  
58 13  
... ... @@ -114,25 +69,25 @@
114 69 def self.binary_from_string(script_string)
115 70 script_string.split(" ").map{|i|
116 71 case i
117   - when *OPCODES.values; OPCODES.find{|k,v| v == i }.first
118   - when *OPCODES_ALIAS.keys; OPCODES_ALIAS.find{|k,v| k == i }.last
119   - when /^([2-9]$|1[0-7])$/; OP_2_16[$1.to_i-2]
120   - when /\(opcode (\d+)\)/; $1.to_i
121   - else
122   - data = [i].pack("H*")
123   - size = data.bytesize
  72 + when *OPCODES.values; OPCODES.find{|k,v| v == i }.first
  73 + when *OPCODES_ALIAS.keys; OPCODES_ALIAS.find{|k,v| k == i }.last
  74 + when /^([2-9]$|1[0-7])$/; OP_2_16[$1.to_i-2]
  75 + when /\(opcode (\d+)\)/; $1.to_i
  76 + else
  77 + data = [i].pack("H*")
  78 + size = data.bytesize
124 79  
125   - head = if size < OP_PUSHDATA1
126   - [size].pack("C")
127   - elsif size > OP_PUSHDATA1 && size <= 0xff
128   - [OP_PUSHDATA1, size].pack("CC")
129   - elsif size > 0xff && size <= 0xffff
130   - [OP_PUSHDATA2, size].pack("Cn")
131   - elsif size > 0xffff && size <= 0xffffffff
132   - [OP_PUSHDATA4, size].pack("CN")
133   - end
  80 + head = if size < OP_PUSHDATA1
  81 + [size].pack("C")
  82 + elsif size > OP_PUSHDATA1 && size <= 0xff
  83 + [OP_PUSHDATA1, size].pack("CC")
  84 + elsif size > 0xff && size <= 0xffff
  85 + [OP_PUSHDATA2, size].pack("Cn")
  86 + elsif size > 0xffff && size <= 0xffffffff
  87 + [OP_PUSHDATA4, size].pack("CN")
  88 + end
134 89  
135   - head + data
  90 + head + data
136 91 end
137 92 }.map{|i|
138 93 i.is_a?(Fixnum) ? [i].pack("C*") : i # TODO yikes, implement/pack 2 byte opcodes.
139 94  
140 95  
... ... @@ -143,222 +98,8 @@
143 98 @script_invalid ||= false
144 99 end
145 100  
146   - # Does nothing
147   - def op_nop
148   - end
149 101  
150   - # Duplicates the top stack item.
151   - def op_dup
152   - @stack << (@stack[-1].dup rescue @stack[-1])
153   - end
154 102  
155   - # The input is hashed using SHA-256.
156   - def op_sha256
157   - buf = @stack.pop
158   - @stack << Digest::SHA256.digest(buf)
159   - end
160   -
161   - # The input is hashed using SHA-1.
162   - def op_sha1
163   - buf = @stack.pop
164   - @stack << Digest::SHA1.digest(buf)
165   - end
166   -
167   - # The input is hashed twice: first with SHA-256 and then with RIPEMD-160.
168   - def op_hash160
169   - buf = @stack.pop
170   - @stack << Digest::RMD160.digest(Digest::SHA256.digest(buf))
171   - end
172   -
173   - # The input is hashed using RIPEMD-160.
174   - def op_ripemd160
175   - buf = @stack.pop
176   - @stack << Digest::RMD160.digest(buf)
177   - end
178   -
179   - # The input is hashed two times with SHA-256.
180   - def op_hash256
181   - buf = @stack.pop
182   - @stack << Digest::SHA256.digest(Digest::SHA256.digest(buf))
183   - end
184   -
185   - # Puts the input onto the top of the alt stack. Removes it from the main stack.
186   - def op_toaltstack
187   - @stack_alt << @stack.pop
188   - end
189   -
190   - # Puts the input onto the top of the main stack. Removes it from the alt stack.
191   - def op_fromaltstack
192   - @stack << @stack_alt.pop
193   - end
194   -
195   - # The item at the top of the stack is copied and inserted before the second-to-top item.
196   - def op_tuck
197   - @stack[-2..-1] = [ @stack[-1], *@stack[-2..-1] ]
198   - end
199   -
200   - # The top two items on the stack are swapped.
201   - def op_swap
202   - @stack[-2..-1] = @stack[-2..-1].reverse
203   - end
204   -
205   - # If both a and b are not 0, the output is 1. Otherwise 0.
206   - def op_booland
207   - a, b = @stack.pop(2)
208   - @stack << (![a,b].any?{|n| n == 0 } ? 1 : 0)
209   - end
210   -
211   - # a is added to b.
212   - def op_add
213   - a, b = @stack.pop(2).reverse
214   - @stack << a + b
215   - end
216   -
217   - # b is subtracted from a.
218   - def op_sub
219   - a, b = @stack.pop(2).reverse
220   - @stack << a - b
221   - end
222   -
223   - # Returns 1 if a is greater than or equal to b, 0 otherwise.
224   - def op_greaterthanorequal
225   - a, b = @stack.pop(2).reverse
226   - @stack << (a >= b ? 1 : 0)
227   - end
228   -
229   - # Removes the top stack item.
230   - def op_drop
231   - @stack.pop
232   - end
233   -
234   - # Returns 1 if the inputs are exactly equal, 0 otherwise.
235   - def op_equal
236   - a, b = @stack.pop(2).reverse
237   - @stack << (a == b ? 1 : 0)
238   - end
239   -
240   - # Marks transaction as invalid if top stack value is not true. True is removed, but false is not.
241   - def op_verify
242   - res = @stack.pop
243   - if res != 1
244   - @stack << res
245   - @script_invalid = true # raise 'transaction invalid' ?
246   - else
247   - @script_invalid = false
248   - end
249   - end
250   -
251   - # Same as OP_EQUAL, but runs OP_VERIFY afterward.
252   - def op_equalverify
253   - op_equal; op_verify
254   - end
255   -
256   - # An empty array of bytes is pushed onto the stack.
257   - def op_0
258   - @stack << "" # []
259   - end
260   -
261   - # The number 1 is pushed onto the stack. Same as OP_TRUE
262   - def op_1
263   - @stack << 1
264   - end
265   -
266   - # https://en.bitcoin.it/wiki/BIP_0017 (old OP_NOP2)
267   - # TODO: don't rely on it yet. add guards from wikipage too.
268   - def op_checkhashverify
269   - unless @checkhash && (@checkhash == @stack[-1].unpack("H*")[0])
270   - @script_invalid = true
271   - end
272   - end
273   -
274   - # All of the signature checking words will only match signatures to the data after the most recently-executed OP_CODESEPARATOR.
275   - def op_codeseparator
276   - @codehash_start = @chunks.size - @chunks.reverse.index(OP_CODESEPARATOR)
277   - end
278   -
279   - # do a CHECKSIG operation on the current stack,
280   - # asking +check_callback+ to do the actual signature verification.
281   - # This is used by Protocol::Tx#verify_input_signature
282   - def op_checksig(check_callback)
283   - return invalid if @stack.size < 2
284   - pubkey = @stack.pop
285   - drop_sigs = [@stack[-1].unpack("H*")[0]]
286   - sig, hash_type = parse_sig(@stack.pop)
287   -
288   - if @chunks.include?(OP_CHECKHASHVERIFY)
289   - # Subset of script starting at the most recent codeseparator to OP_CHECKSIG
290   - script_code, @checkhash = codehash_script(OP_CHECKSIG)
291   - else
292   - script_code, drop_sigs = nil, nil
293   - end
294   -
295   - if check_callback == nil # for tests
296   - @stack << 1
297   - else # real signature check callback
298   - @stack <<
299   - ((check_callback.call(pubkey, sig, hash_type, drop_sigs, script_code) == true) ? 1 : 0)
300   - end
301   - end
302   -
303   - def op_checksigverify(check_callback)
304   - op_checksig(check_callback)
305   - p @stack
306   - op_verify
307   - end
308   -
309   - # do a CHECKMULTISIG operation on the current stack,
310   - # asking +check_callback+ to do the actual signature verification.
311   - #
312   - # CHECKMULTISIG does a m-of-n signatures verification on scripts of the form:
313   - # 0 <sig1> <sig2> | 2 <pub1> <pub2> 2 OP_CHECKMULTISIG
314   - # 0 <sig1> <sig2> | 2 <pub1> <pub2> <pub3> 3 OP_CHECKMULTISIG
315   - # 0 <sig1> <sig2> <sig3> | 3 <pub1> <pub2> <pub3> 3 OP_CHECKMULTISIG
316   - #
317   - # see https://en.bitcoin.it/wiki/BIP_0011 for details.
318   - # see https://github.com/bitcoin/bitcoin/blob/master/src/script.cpp#L931
319   - #
320   - # TODO: validate signature order
321   - # TODO: take global opcode count
322   - def op_checkmultisig(check_callback)
323   - n_pubkeys = @stack.pop
324   - return invalid unless (0..20).include?(n_pubkeys)
325   - return invalid unless @stack.last(n_pubkeys).all?{|e| e.is_a?(String) && e != '' }
326   - #return invalid if ((@op_count ||= 0) += n_pubkeys) > 201
327   - pubkeys = @stack.pop(n_pubkeys)
328   -
329   - n_sigs = @stack.pop
330   - return invalid unless (0..n_pubkeys).include?(n_sigs)
331   - return invalid unless @stack.last(n_sigs).all?{|e| e.is_a?(String) && e != '' }
332   - sigs = (drop_sigs = @stack.pop(n_sigs)).map{|s| parse_sig(s) }
333   -
334   - @stack.pop if @stack[-1] == '' # remove OP_NOP from stack
335   -
336   - if @chunks.include?(OP_CHECKHASHVERIFY)
337   - # Subset of script starting at the most recent codeseparator to OP_CHECKMULTISIG
338   - script_code, @checkhash = codehash_script(OP_CHECKMULTISIG)
339   - drop_sigs.map!{|i| i.unpack("H*")[0] }
340   - else
341   - script_code, drop_sigs = nil, nil
342   - end
343   -
344   - valid_sigs = 0
345   - sigs.each{|sig, hash_type| pubkeys.each{|pubkey|
346   - valid_sigs += 1 if check_callback.call(pubkey, sig, hash_type, drop_sigs, script_code)
347   - }}
348   -
349   - @stack << ((valid_sigs == n_sigs) ? 1 : (invalid; 0))
350   - end
351   -
352   -
353   -
354   -
355   - OPCODES_METHOD = Hash[*instance_methods.grep(/^op_/).map{|m|
356   - [ (OPCODES.find{|k,v| v == m.to_s.upcase }.first rescue nil), m ]
357   - }.flatten]
358   - OPCODES_METHOD[0] = :op_0
359   - OPCODES_METHOD[81] = :op_1
360   -
361   -
362 103 # run the script. +check_callback+ is called for OP_CHECKSIG operations
363 104 def run(&check_callback)
364 105 @debug = []
... ... @@ -391,7 +132,7 @@
391 132 if @script_invalid
392 133 @stack << 0
393 134 @debug << "INVALID TRANSACTION"
394   - require 'pp'; pp @debug
  135 + #require 'pp'; pp @debug
395 136 end
396 137  
397 138 @debug << "RESULT"
398 139  
399 140  
400 141  
401 142  
402 143  
... ... @@ -414,30 +155,101 @@
414 155 script_pubkey = binary_from_string(script)
415 156 end
416 157  
417   - # check if script is in one of the recognized standard formats
418   - def is_standard?
419   - is_pubkey? || is_hash160?
  158 + # Script type definitions used to create scripts an determine script type.
  159 + # Defines the order in which data and opcodes are expected.
  160 + #
  161 + # Alternative options can be given as an array.
  162 + #
  163 + # A String means "that many bytes of data". If the number is prefixed with +?+,
  164 + # the element is optional, meaning if the input element doesn't match,
  165 + # it is ignored and the compared to the next definition element.
  166 + TYPES = {
  167 + :pubkey => ["65", OP_CHECKSIG],
  168 + :address => [OP_DUP, OP_HASH160, "20", OP_EQUALVERIFY, OP_CHECKSIG],
  169 + :multisig => [[OP_1, 82, 83], "65", "65", "?65", [82, 83], OP_CHECKMULTISIG],
  170 + }
  171 +
  172 + # Match an opcode definition +match+ to given opcode +op+.
  173 + def self.match_opcode(match, op)
  174 + if match.is_a?(Array)
  175 + return match.map {|m| match_opcode(m, op)}.any?
  176 + elsif match.is_a?(String)
  177 + if match[0] == "?"
  178 + match = match[1..-1]
  179 + return :omit unless op.is_a?(String)
  180 + end
  181 + return false unless op.is_a?(String)
  182 + return false unless op.bytesize == match.to_i
  183 + else
  184 + return false unless op == match
  185 + end
  186 + true
420 187 end
421 188  
422   - # is this a send-to-ip (pubkey) tx
423   - def is_send_to_ip?
424   - return false if @chunks.size != 2
425   - (@chunks[1] == OP_CHECKSIG) && @chunks[0].size > 1
  189 + # Check if script matches given +type+.
  190 + def is_type?(type)
  191 + i = 0
  192 + TYPES[type.to_sym].each do |c|
  193 + if !match = self.class.match_opcode(c, @chunks[i])
  194 + return false
  195 + elsif match != :omit
  196 + i += 1
  197 + end
  198 + end
  199 + true
426 200 end
427   - alias :is_pubkey? :is_send_to_ip?
428 201  
429   - # is this a hash160 (address) tx
430   - def is_hash160?
431   - return false if @chunks.size != 5
432   - (@chunks[0..1] + @chunks[-2..-1]) ==
433   - [OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG] &&
434   - @chunks[2].is_a?(String) && @chunks[2].bytesize == 20
  202 +
  203 + # Create script of type +type+ using given data.
  204 + # Take opcodes from the script definition and insert +args+ for
  205 + # the data elements, while checking that it matches defined size.
  206 + def self.to_script(type, *args)
  207 + i, script = 0, new("")
  208 + TYPES[type.to_sym].each do |c|
  209 + if c.is_a?(Fixnum)
  210 + script.chunks[i] = c
  211 + next i += 1
  212 + end
  213 + val = args[0]
  214 + if c.is_a?(Array)
  215 + raise "error: expected #{c} opcode, got #{val}" unless match_opcode(c, val)
  216 + elsif c.is_a?(String)
  217 + if c[0] == "?"
  218 + next unless val.is_a?(String) # skip optional data element
  219 + c = c[1..-1]
  220 + end
  221 + raise "error: expected #{c} bytes of data, got #{val.bytesize}" if val.bytesize != c.to_i
  222 + end
  223 + script.chunks[i] = args.shift
  224 + i += 1
  225 + end
  226 + from_string(script.to_string)
435 227 end
436 228  
437   - def is_multisig?
438   - @chunks[-1] == OP_CHECKMULTISIG
  229 + def method_missing(name, *args)
  230 + if name =~ /^is_(.*?)\?/ && TYPES.keys.include?($1.to_sym)
  231 + return is_type?($1, *args)
  232 + end
  233 + super(name, *args)
439 234 end
440 235  
  236 + def self.method_missing(name, *args)
  237 + if name =~ /^(.*?)_script/ && TYPES.keys.include?($1.to_sym)
  238 + return to_script($1, *args)
  239 + end
  240 + super(name, *args)
  241 + end
  242 +
  243 + # check if script is in one of the recognized standard formats
  244 + def is_standard?
  245 + TYPES.keys.map{|t| is_type?(t)}.any?
  246 + end
  247 +
  248 + # Alias for #is_address?
  249 + def is_hash160?
  250 + is_type?(:address)
  251 + end
  252 +
441 253 # get the public key for this script (in generation scripts)
442 254 def get_pubkey
443 255 return @chunks[0].unpack("H*")[0] if @chunks.size == 1
... ... @@ -482,8 +294,7 @@
482 294 def self.to_address_script(address)
483 295 hash160 = Bitcoin.hash160_from_address(address)
484 296 return nil unless hash160
485   - # DUP HASH160 length hash160 EQUALVERIFY CHECKSIG
486   - [ ["76", "a9", "14", hash160, "88", "ac"].join ].pack("H*")
  297 + return to_script(:address, [hash160].pack("H*")).raw
487 298 end
488 299  
489 300 def self.to_signature_pubkey_script(signature, pubkey)
... ... @@ -491,14 +302,6 @@
491 302 #pubkey = [pubkey].pack("H*") if pubkey.bytesize != 65
492 303 raise "pubkey is not in binary form" unless pubkey.bytesize == 65 && pubkey[0] == "\x04"
493 304 [ [signature.bytesize+1].pack("C"), signature, hash_type, [pubkey.bytesize].pack("C"), pubkey ].join
494   - end
495   -
496   - private
497   -
498   - def parse_sig(sig)
499   - hash_type = sig[-1].unpack("C")[0]
500   - sig = sig[0...-1]
501   - return sig, hash_type
502 305 end
503 306  
504 307 end
spec/bitcoin/script_spec.rb
... ... @@ -6,10 +6,16 @@
6 6 ["410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac"].pack("H*"),
7 7 ["47304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901"].pack("H*"),
8 8 ["76a91417977bca1b6287a5e6559c57ef4b6525e9d7ded688ac"].pack("H*"),
9   - ["524104573b6e9f3a714440048a7b87d606bcbf9e45b8586e70a67a3665ea720c095658471a523e5d923f3f3e015626e7c900bd08560ddffeb17d33c5b52c96edb875954104039c2f4e413a26901e67ad4adbb6a4759af87bc16c7120459ecc9482fed3dd4a4502947f7b4c7782dcadc2bed513ed14d5e770452b97ae246ac2030f13b80a5141048b0f9d04e495c3c754f8c3c109196d713d0778882ef098f785570ee6043f8c192d8f84df43ebafbcc168f5d95a074dc4010b62c003e560abc163c312966b74b653ae"].pack("H*"), # multisig 2 of 3
10   - ["5141040ee607b584b36e995f2e96dec35457dbb40845d0ce0782c84002134e816a6b8cbc65e9eed047ae05e10760e4113f690fd49ad73b86b04a1d7813d843f8690ace4104220a78f5f6741bb0739675c2cc200643516b02cfdfda5cba21edeaa62c0f954936b30dfd956e3e99af0a8e7665cff6ac5b429c54c418184c81fbcd4bde4088f552ae"].pack("H*"), # multisig 1 of 2
  9 + ["524104fb0123fe2c399981bc77d522e2ae3268d2ab15e9a84ae49338a4b1db3886a1ea04cdab955d81e9fa1fcb0c062cb9a5af1ad5dd5064f4afcca322402b07030ec2410423b8161514560bc8638054b6637ab78f400b24e5694ec8061db635d1f28a17902b14dbf4f80780da659ab24f11ded3095c780452a4004c30ab58dffac33d839a4104f43e76afac66bf3927638b6c4f7e324513ce56d2d658ac9d24c420d09993a4464eea6141a68a4748c092ad0e8f4ac29c4a2f661ef4d22b21f20110f42fcd6f6d53ae"].pack("H*"), # multisig 2 of 3
  10 + ["514104fb0123fe2c399981bc77d522e2ae3268d2ab15e9a84ae49338a4b1db3886a1ea04cdab955d81e9fa1fcb0c062cb9a5af1ad5dd5064f4afcca322402b07030ec2410423b8161514560bc8638054b6637ab78f400b24e5694ec8061db635d1f28a17902b14dbf4f80780da659ab24f11ded3095c780452a4004c30ab58dffac33d839a52ae"].pack("H*"), # multisig 1 of 2
11 11 ]
12 12  
  13 + @pubkeys = [
  14 + "04fb0123fe2c399981bc77d522e2ae3268d2ab15e9a84ae49338a4b1db3886a1ea04cdab955d81e9fa1fcb0c062cb9a5af1ad5dd5064f4afcca322402b07030ec2",
  15 + "0423b8161514560bc8638054b6637ab78f400b24e5694ec8061db635d1f28a17902b14dbf4f80780da659ab24f11ded3095c780452a4004c30ab58dffac33d839a",
  16 + "04f43e76afac66bf3927638b6c4f7e324513ce56d2d658ac9d24c420d09993a4464eea6141a68a4748c092ad0e8f4ac29c4a2f661ef4d22b21f20110f42fcd6f6d",
  17 + ]
  18 +
13 19 it '#to_string' do
14 20 Bitcoin::Script.new(@script[0]).to_string.should ==
15 21 "0411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3 OP_CHECKSIG"
16 22  
... ... @@ -71,10 +77,10 @@
71 77  
72 78 it "#get multisig_addresses" do
73 79 Bitcoin::Script.new(@script[3]).get_multisig_addresses.should == [
74   - "1JiaVc3N3U3CwwcLtzNX1Q4eYfeYxVjtuj", "19Fm2gY7qDTXriNTEhFY2wjxbHna3Gvenk",
75   - "1B6k6g1d2L975i7beAbiBRxfBWhxomPxvy"]
  80 + "1HxYGsr9qCfg1teney6SxELB8KZ19xfcTV", "177taeJAhJLEZsJua6cErn2Ze6nGqr9YNh",
  81 + "19p3bbfShrkgS1dRWhBUGRVLoSi5CZ6izn"]
76 82 Bitcoin::Script.new(@script[4]).get_multisig_addresses.should == [
77   - "1F2Nnyn7niMcheiYhkHrkc18aDxEkFowy5", "1EE7JGimkV7QqyHwXDJvk3b1yEN4ZUWeqx"]
  83 + "1HxYGsr9qCfg1teney6SxELB8KZ19xfcTV", "177taeJAhJLEZsJua6cErn2Ze6nGqr9YNh"]
78 84 end
79 85  
80 86 it "#get_address" do
81 87  
82 88  
83 89  
84 90  
85 91  
... ... @@ -84,29 +90,32 @@
84 90 Bitcoin::Script.new(@script[2]).get_address.should ==
85 91 "139k1g5rtTsL4aGZbcASH3Fv3fUh9yBEdW"
86 92 Bitcoin::Script.new(@script[3]).get_address.should ==
87   - "1JiaVc3N3U3CwwcLtzNX1Q4eYfeYxVjtuj"
  93 + "1HxYGsr9qCfg1teney6SxELB8KZ19xfcTV"
88 94 Bitcoin::Script.new(@script[4]).get_address.should ==
89   - "1F2Nnyn7niMcheiYhkHrkc18aDxEkFowy5"
  95 + "1HxYGsr9qCfg1teney6SxELB8KZ19xfcTV"
90 96 end
91 97  
92   - it '#is_send_to_ip?' do
93   - Bitcoin::Script.new(@script[0]).is_send_to_ip?.should == true
94   - Bitcoin::Script.new(@script[1]).is_send_to_ip?.should == false
95   - Bitcoin::Script.new(@script[2]).is_send_to_ip?.should == false
  98 + it '#is_pubkey?' do
  99 + Bitcoin::Script.new(@script[0]).is_pubkey?.should == true
  100 + Bitcoin::Script.new(@script[1]).is_pubkey?.should == false
  101 + Bitcoin::Script.new(@script[2]).is_pubkey?.should == false
96 102 end
97 103  
98   - it "#is_hash160?" do
99   - Bitcoin::Script.new(@script[0]).is_hash160?.should == false
100   - Bitcoin::Script.new(@script[1]).is_send_to_ip?.should == false
101   - Bitcoin::Script.new(@script[2]).is_hash160?.should == true
  104 + it "#is_address?" do
  105 + Bitcoin::Script.new(@script[0]).is_address?.should == false
  106 + Bitcoin::Script.new(@script[1]).is_pubkey?.should == false
  107 + Bitcoin::Script.new(@script[2]).is_address?.should == true
102 108 Bitcoin::Script.from_string("OP_DUP OP_HASH160 0 OP_EQUALVERIFY OP_CHECKSIG")
103   - .is_hash160?.should == false
  109 + .is_address?.should == false
104 110 end
105 111  
106 112 it "#is_multisig?" do
107 113 Bitcoin::Script.new(@script[3]).is_multisig?.should == true
108 114 Bitcoin::Script.new(@script[4]).is_multisig?.should == true
109 115 Bitcoin::Script.new(@script[0]).is_multisig?.should == false
  116 + Bitcoin::Script.from_string("2 #{@pubkeys[0]} #{@pubkeys[1]} #{@pubkeys[2]} 3 OP_CHECKMULTISIG").is_multisig?.should == true
  117 + Bitcoin::Script.from_string("1 #{@pubkeys[0]} #{@pubkeys[1]} #{@pubkeys[2]} 3 OP_CHECKMULTISIG").is_multisig?.should == true
  118 + Bitcoin::Script.from_string("1 #{@pubkeys[0]} #{@pubkeys[1]} 2 OP_CHECKMULTISIG").is_multisig?.should == true
110 119 end
111 120  
112 121 it '#run' do
... ... @@ -125,6 +134,11 @@
125 134 Bitcoin::Script.from_string("1 OP_DROP 2").run.should == false
126 135 end
127 136  
  137 + it "should generate pubkey script" do
  138 + Bitcoin::Script.pubkey_script([@pubkeys[0]].pack("H*")).raw.should ==
  139 + Bitcoin::Script.from_string("#{@pubkeys[0]} OP_CHECKSIG").raw
  140 + end
  141 +
128 142 it "should generate address script" do
129 143 Bitcoin::Script.to_address_script('16Tc7znw2mfpWcqS84vBFfJ7PyoeHaXSz9')
130 144 .should == ["76a9143be0c2daaabbf3d53e47352c19d1e8f047e2f94188ac"].pack("H*")
... ... @@ -139,6 +153,11 @@
139 153 .should == ["483045022062437a8f60651cd968137355775fa8bdb83d4ca717fdbc08bf9868a051e0542f022100f5cd626c15ef0de0803ddf299e8895743e7ff484d6335874edfe086ee0a08fec014104bc3e2b520d4be3e2651f2ba554392ea31edd69d2081186ab98acda3c4bf45e41a5f6e093277b774b5893347e38ffafce2b9e82226e6e0b378cf79b8c2eed983c"].pack("H*")
140 154 end
141 155  
  156 + it "should generate multisig script" do
  157 + pubkeys = @pubkeys.map{|k| [k].pack("H*")}
  158 + Bitcoin::Script.multisig_script(82, *pubkeys[0..2], 83).raw.should == @script[3]
  159 + Bitcoin::Script.multisig_script(81, *pubkeys[0..1], 82).raw.should == @script[4]
  160 + end
142 161  
143 162 describe "Bitcoin::Script OPCODES" do
144 163