Class: Bitcoin::Script

Inherits:
Object
  • Object
show all
Defined in:
lib/bitcoin/script.rb

Defined Under Namespace

Classes: ScriptOpcodeError

Constant Summary collapse

OP_1 =
81
OP_TRUE =
81
OP_0 =
0
OP_FALSE =
0
OP_PUSHDATA1 =
76
OP_PUSHDATA2 =
77
OP_PUSHDATA4 =
78
OP_NOP =
97
OP_DUP =
118
OP_HASH160 =
169
OP_EQUAL =
135
OP_VERIFY =
105
OP_EQUALVERIFY =
136
OP_CHECKSIG =
172
OP_CHECKSIGVERIFY =
173
OP_CHECKMULTISIG =
174
OP_CHECKMULTISIGVERIFY =
175
OP_TOALTSTACK =
107
OP_FROMALTSTACK =
108
OP_TUCK =
125
OP_SWAP =
124
OP_BOOLAND =
154
OP_ADD =
147
OP_SUB =
148
OP_GREATERTHANOREQUAL =
162
OP_DROP =
117
OP_HASH256 =
170
OP_SHA256 =
168
OP_SHA1 =
167
OP_RIPEMD160 =
166
OP_EVAL =
176
OP_NOP2 =
177
OP_CHECKHASHVERIFY =
177
OP_CODESEPARATOR =
171
OP_MIN =
163
OP_MAX =
164
OP_2OVER =
112
OP_2SWAP =
114
OP_IFDUP =
115
OP_DEPTH =
116
OP_1NEGATE =
79
OPCODES =

OP_IF = 99 OP_NOTIF = 100 OP_ELSE = 103 OP_ENDIF = 104

Hash[*constants.grep(/^OP_/).map{|i| [const_get(i), i.to_s]
OPCODES_ALIAS =
{
  "OP_TRUE"  => OP_1,
  "OP_FALSE" => OP_0,
  "OP_NOP1" => OP_EVAL,
  "OP_NOP2" => OP_CHECKHASHVERIFY
}
OP_2_16 =
(82..96).to_a
OPCODES_METHOD =

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(bytes, offset = 0) ⇒ Script

create a new script. bytes is typically input_script + output_script



67
68
69
70
71
# File 'lib/bitcoin/script.rb', line 67

def initialize(bytes, offset=0)
  @raw = bytes
  @stack, @stack_alt = [], []
  @chunks = parse(bytes, offset)
end

Instance Attribute Details

#chunksObject (readonly)

Returns the value of attribute chunks.



64
65
66
# File 'lib/bitcoin/script.rb', line 64

def chunks
  @chunks
end

#debugObject (readonly)

Returns the value of attribute debug.



64
65
66
# File 'lib/bitcoin/script.rb', line 64

def debug
  @debug
end

#rawObject (readonly)

Returns the value of attribute raw.



64
65
66
# File 'lib/bitcoin/script.rb', line 64

def raw
  @raw
end

Class Method Details

.binary_from_string(script_string) ⇒ Object

raw script binary of a string representation



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/bitcoin/script.rb', line 126

def self.binary_from_string(script_string)
  script_string.split(" ").map{|i|
    case i
    when /^OP_PUSHDATA[124]$/;     # skip
    when *OPCODES.values;          OPCODES.find{|k,v| v == i }.first
    when *OPCODES_ALIAS.keys;      OPCODES_ALIAS.find{|k,v| k == i }.last
    when /^([2-9]|1[0-6])$/;       OP_2_16[$1.to_i-2]
    when /\(opcode (\d+)\)/;       $1.to_i
    when /OP_(.+)$/;               raise ScriptOpcodeError, "#{i} not defined!"
    else 
      data = [i].pack("H*")
      size = data.bytesize

      head = if size < OP_PUSHDATA1
               [size].pack("C")
             elsif size > OP_PUSHDATA1 && size <= 0xff
               [OP_PUSHDATA1, size].pack("CC")
             elsif size > 0xff && size <= 0xffff
               [OP_PUSHDATA2, size].pack("Cv")
             elsif size > 0xffff && size <= 0xffffffff
               [OP_PUSHDATA4, size].pack("CV")
             end

      head + data
    end
  }.map{|i|
    i.is_a?(Fixnum) ? [i].pack("C*") : i # TODO yikes, implement/pack 2 byte opcodes.
  }.join
end

.drop_signatures(script_pubkey, drop_signatures) ⇒ Object



213
214
215
216
# File 'lib/bitcoin/script.rb', line 213

def self.drop_signatures(script_pubkey, drop_signatures)
  script = new(script_pubkey).to_string.split(" ").delete_if{|c| drop_signatures.include?(c) }.join(" ")
  script_pubkey = binary_from_string(script)
end

.from_string(script_string) ⇒ Object

script object of a string representation



119
120
121
# File 'lib/bitcoin/script.rb', line 119

def self.from_string(script_string)
  new(binary_from_string(script_string))
end

.to_address_script(address) ⇒ Object



337
338
339
340
341
342
343
# File 'lib/bitcoin/script.rb', line 337

def self.to_address_script(address)
  hash160 = Bitcoin.hash160_from_address(address)
  case Bitcoin.address_type(address)
  when :hash160; to_hash160_script(hash160)
  when :p2sh;    to_p2sh_script(hash160)
  end
end

.to_hash160_script(hash160) ⇒ Object

generate hash160 tx for given address



325
326
327
328
329
# File 'lib/bitcoin/script.rb', line 325

def self.to_hash160_script(hash160)
  return nil  unless hash160
  #  DUP   HASH160  length  hash160    EQUALVERIFY  CHECKSIG
  [ ["76", "a9",    "14",   hash160,   "88",        "ac"].join ].pack("H*")
end

.to_multisig_script(m, *pubkeys) ⇒ Object

generate multisig tx for given pubkeys, expecting m signatures



346
347
348
349
# File 'lib/bitcoin/script.rb', line 346

def self.to_multisig_script(m, *pubkeys)
  pubs = pubkeys.map{|pk|p=[pk].pack("H*"); [p.bytesize].pack("C") + p}
  [ [80 + m.to_i].pack("C"), *pubs, [80 + pubs.size].pack("C"), "\xAE"].join
end

.to_multisig_script_sig(*sigs) ⇒ Object



364
365
366
# File 'lib/bitcoin/script.rb', line 364

def self.to_multisig_script_sig(*sigs)
  from_string("0 #{sigs.map{|s|s.unpack('H*')[0]}.join(' ')}").raw
end

.to_p2sh_script(p2sh) ⇒ Object



331
332
333
334
335
# File 'lib/bitcoin/script.rb', line 331

def self.to_p2sh_script(p2sh)
  return nil  unless p2sh
  # HASH160  length  hash  EQUAL
  [ ["a9",   "14",   p2sh, "87"].join ].pack("H*")
end

.to_pubkey_script(pubkey) ⇒ Object

generate pubkey tx script for given pubkey



319
320
321
322
# File 'lib/bitcoin/script.rb', line 319

def self.to_pubkey_script(pubkey)
  pk = [pubkey].pack("H*")
  [[pk.bytesize].pack("C"), pk, "\xAC"].join
end

.to_pubkey_script_sig(signature, pubkey) ⇒ Object

generate pubkey script sig for given signature and pubkey



352
353
354
355
356
357
# File 'lib/bitcoin/script.rb', line 352

def self.to_pubkey_script_sig(signature, pubkey)
  hash_type = "\x01"
  #pubkey = [pubkey].pack("H*") if pubkey.bytesize != 65
  raise "pubkey is not in binary form" unless pubkey.bytesize == 65  && pubkey[0] == "\x04"
  [ [signature.bytesize+1].pack("C"), signature, hash_type, [pubkey.bytesize].pack("C"), pubkey ].join
end

.to_signature_pubkey_script(*a) ⇒ Object

alias for #to_pubkey_script_sig



360
361
362
# File 'lib/bitcoin/script.rb', line 360

def self.to_signature_pubkey_script(*a)
  to_pubkey_script_sig(*a)
end

Instance Method Details

#codehash_script(opcode) ⇒ Object



206
207
208
209
210
211
# File 'lib/bitcoin/script.rb', line 206

def codehash_script(opcode)
  # CScript scriptCode(pbegincodehash, pend);
  script    = to_string(@chunks[(@codehash_start||0)...@chunks.size-@chunks.reverse.index(opcode)])
  checkhash = Bitcoin.hash160(Bitcoin::Script.binary_from_string(script).unpack("H*")[0])
  [script, checkhash]
end

#get_addressObject

get single address, or first for multisig script



313
314
315
316
# File 'lib/bitcoin/script.rb', line 313

def get_address
  addrs = get_addresses
  addrs.is_a?(Array) ? addrs[0] : addrs
end

#get_addressesObject

get all addresses this script corresponds to (if possible)



306
307
308
309
310
# File 'lib/bitcoin/script.rb', line 306

def get_addresses
  return [get_pubkey_address]  if is_pubkey?
  return [get_hash160_address] if is_hash160?
  return get_multisig_addresses  if is_multisig?
end

#get_hash160Object

get the hash160 for this hash160 script



285
286
287
288
# File 'lib/bitcoin/script.rb', line 285

def get_hash160
  return @chunks[2..-3][0].unpack("H*")[0]  if is_hash160?
  return Bitcoin.hash160(get_pubkey)        if is_pubkey?
end

#get_hash160_addressObject

get the hash160 address for this hash160 script



291
292
293
# File 'lib/bitcoin/script.rb', line 291

def get_hash160_address
  Bitcoin.hash160_to_address(get_hash160)
end

#get_multisig_addressesObject

get the pubkey addresses for this multisig script



301
302
303
# File 'lib/bitcoin/script.rb', line 301

def get_multisig_addresses
  get_multisig_pubkeys.map {|p| Bitcoin::Key.new(nil, p.unpack("H*")[0]).addr}
end

#get_multisig_pubkeysObject

get the public keys for this multisig script



296
297
298
# File 'lib/bitcoin/script.rb', line 296

def get_multisig_pubkeys
  1.upto(@chunks[-2] - 80).map {|i| @chunks[i]}
end

#get_pubkeyObject

get the public key for this pubkey script



274
275
276
277
# File 'lib/bitcoin/script.rb', line 274

def get_pubkey
  return @chunks[0].unpack("H*")[0] if @chunks.size == 1
  is_pubkey? ? @chunks[0].unpack("H*")[0] : nil
end

#get_pubkey_addressObject

get the pubkey address for this pubkey script



280
281
282
# File 'lib/bitcoin/script.rb', line 280

def get_pubkey_address
  Bitcoin.pubkey_to_address(get_pubkey)
end

#get_signatures_requiredObject



368
369
370
371
# File 'lib/bitcoin/script.rb', line 368

def get_signatures_required
  return false unless is_multisig?
  @chunks[0] - 80
end

#invalidObject



202
203
204
# File 'lib/bitcoin/script.rb', line 202

def invalid
  @script_invalid = true; nil
end

#invalid?Boolean

Returns:

  • (Boolean)


156
157
158
# File 'lib/bitcoin/script.rb', line 156

def invalid?
  @script_invalid ||= false
end

#is_hash160?Boolean

is this a hash160 (address) tx

Returns:

  • (Boolean)


251
252
253
254
255
256
# File 'lib/bitcoin/script.rb', line 251

def is_hash160?
  return false  if @chunks.size != 5
  (@chunks[0..1] + @chunks[-2..-1]) ==
    [OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG] &&
    @chunks[2].is_a?(String) && @chunks[2].bytesize == 20
end

#is_multisig?Boolean

is this a multisig tx

Returns:

  • (Boolean)


259
260
261
262
# File 'lib/bitcoin/script.rb', line 259

def is_multisig?
  return false  if @chunks.size > 6 || @chunks.size < 4
  @chunks[-1] == OP_CHECKMULTISIG
end

#is_pay_to_script_hash?Boolean Also known as: is_p2sh?

Returns:

  • (Boolean)


232
233
234
235
# File 'lib/bitcoin/script.rb', line 232

def is_pay_to_script_hash?
  @chunks.size >= 3 && @chunks[-3] == OP_HASH160 &&
    @chunks[-2].bytesize == 20 && @chunks[-1] == OP_EQUAL
end

#is_pubkey?Boolean Also known as: is_send_to_ip?

is this a pubkey tx

Returns:

  • (Boolean)


244
245
246
247
# File 'lib/bitcoin/script.rb', line 244

def is_pubkey?
  return false if @chunks.size != 2
  (@chunks[1] == OP_CHECKSIG) && @chunks[0].size > 1
end

#is_standard?Boolean

check if script is in one of the recognized standard formats

Returns:

  • (Boolean)


239
240
241
# File 'lib/bitcoin/script.rb', line 239

def is_standard?
  is_pubkey? || is_hash160? || is_multisig? || is_p2sh?
end

#op_0Object

An empty array of bytes is pushed onto the stack.



486
487
488
# File 'lib/bitcoin/script.rb', line 486

def op_0
  @stack << "" # []
end

#op_1Object

The number 1 is pushed onto the stack. Same as OP_TRUE



491
492
493
# File 'lib/bitcoin/script.rb', line 491

def op_1
  @stack << 1
end

#op_1negateObject

The number -1 is pushed onto the stack.



526
527
528
# File 'lib/bitcoin/script.rb', line 526

def op_1negate
  @stack << -1
end

#op_2overObject

Copies the pair of items two spaces back in the stack to the front.



506
507
508
509
# File 'lib/bitcoin/script.rb', line 506

def op_2over
  @stack << @stack[-4]
  @stack << @stack[-4]
end

#op_2swapObject

Swaps the top two pairs of items.



512
513
514
515
516
# File 'lib/bitcoin/script.rb', line 512

def op_2swap
  p1 = @stack.pop(2)
  p2 = @stack.pop(2)
  @stack += p1 += p2
end

#op_addObject

a is added to b.



441
442
443
444
# File 'lib/bitcoin/script.rb', line 441

def op_add
  a, b = @stack.pop(2).reverse
  @stack << a + b
end

#op_boolandObject

If both a and b are not 0, the output is 1. Otherwise 0.



435
436
437
438
# File 'lib/bitcoin/script.rb', line 435

def op_booland
  a, b = @stack.pop(2)
  @stack << (![a,b].any?{|n| n == 0 } ? 1 : 0)
end

#op_checkhashverifyObject

en.bitcoin.it/wiki/BIP_0017 (old OP_NOP2) TODO: don’t rely on it yet. add guards from wikipage too.



537
538
539
540
541
# File 'lib/bitcoin/script.rb', line 537

def op_checkhashverify
  unless @checkhash && (@checkhash == @stack[-1].unpack("H*")[0])
    @script_invalid = true
  end
end

#op_checkmultisig(check_callback) ⇒ Object

do a CHECKMULTISIG operation on the current stack, asking check_callback to do the actual signature verification.

CHECKMULTISIG does a m-of-n signatures verification on scripts of the form:

0 <sig1> <sig2> | 2 <pub1> <pub2> 2 OP_CHECKMULTISIG
0 <sig1> <sig2> | 2 <pub1> <pub2> <pub3> 3 OP_CHECKMULTISIG
0 <sig1> <sig2> <sig3> | 3 <pub1> <pub2> <pub3> 3 OP_CHECKMULTISIG

see en.bitcoin.it/wiki/BIP_0011 for details. see github.com/bitcoin/bitcoin/blob/master/src/script.cpp#L931

TODO: validate signature order TODO: take global opcode count



591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
# File 'lib/bitcoin/script.rb', line 591

def op_checkmultisig(check_callback)
  n_pubkeys = @stack.pop
  return invalid  unless (0..20).include?(n_pubkeys)
  return invalid  unless @stack.last(n_pubkeys).all?{|e| e.is_a?(String) && e != '' }
  #return invalid  if ((@op_count ||= 0) += n_pubkeys) > 201
  pubkeys = @stack.pop(n_pubkeys)

  n_sigs = @stack.pop
  return invalid  unless (0..n_pubkeys).include?(n_sigs)
  return invalid  unless @stack.last(n_sigs).all?{|e| e.is_a?(String) && e != '' }
  sigs = (drop_sigs = @stack.pop(n_sigs)).map{|s| parse_sig(s) }

  @stack.pop if @stack[-1] == '' # remove OP_NOP from stack

  if @chunks.include?(OP_CHECKHASHVERIFY)
    # Subset of script starting at the most recent codeseparator to OP_CHECKMULTISIG
    script_code, @checkhash = codehash_script(OP_CHECKMULTISIG)
    drop_sigs.map!{|i| i.unpack("H*")[0] }
  else
    script_code, drop_sigs = nil, nil
  end

  valid_sigs = 0
  sigs.each{|sig, hash_type| pubkeys.each{|pubkey|
      valid_sigs += 1  if check_callback.call(pubkey, sig, hash_type, drop_sigs, script_code)
    }}

  @stack << ((valid_sigs == n_sigs) ? 1 : (invalid; 0))
end

#op_checksig(check_callback) ⇒ Object

do a CHECKSIG operation on the current stack, asking check_callback to do the actual signature verification. This is used by Protocol::Tx#verify_input_signature



552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
# File 'lib/bitcoin/script.rb', line 552

def op_checksig(check_callback)
  return invalid if @stack.size < 2
  pubkey = @stack.pop
  drop_sigs      = [@stack[-1].unpack("H*")[0]]
  sig, hash_type = parse_sig(@stack.pop)

  if @chunks.include?(OP_CHECKHASHVERIFY)
    # Subset of script starting at the most recent codeseparator to OP_CHECKSIG
    script_code, @checkhash = codehash_script(OP_CHECKSIG)
  else
    script_code, drop_sigs = nil, nil
  end

  if check_callback == nil # for tests
    @stack << 1
  else # real signature check callback
    @stack <<
      ((check_callback.call(pubkey, sig, hash_type, drop_sigs, script_code) == true) ? 1 : 0)
  end
end

#op_checksigverify(check_callback) ⇒ Object



573
574
575
576
# File 'lib/bitcoin/script.rb', line 573

def op_checksigverify(check_callback)
  op_checksig(check_callback)
  op_verify
end

#op_codeseparatorObject

All of the signature checking words will only match signatures to the data after the most recently-executed OP_CODESEPARATOR.



545
546
547
# File 'lib/bitcoin/script.rb', line 545

def op_codeseparator
  @codehash_start = @chunks.size - @chunks.reverse.index(OP_CODESEPARATOR)
end

#op_depthObject

Puts the number of stack items onto the stack.



531
532
533
# File 'lib/bitcoin/script.rb', line 531

def op_depth
  @stack << @stack.size
end

#op_dropObject

Removes the top stack item.



459
460
461
# File 'lib/bitcoin/script.rb', line 459

def op_drop
  @stack.pop
end

#op_dupObject

Duplicates the top stack item.



380
381
382
# File 'lib/bitcoin/script.rb', line 380

def op_dup
  @stack << (@stack[-1].dup rescue @stack[-1])
end

#op_equalObject

Returns 1 if the inputs are exactly equal, 0 otherwise.



464
465
466
467
# File 'lib/bitcoin/script.rb', line 464

def op_equal
  a, b = @stack.pop(2).reverse
  @stack << (a == b ? 1 : 0)
end

#op_equalverifyObject

Same as OP_EQUAL, but runs OP_VERIFY afterward.



481
482
483
# File 'lib/bitcoin/script.rb', line 481

def op_equalverify
  op_equal; op_verify
end

#op_fromaltstackObject

Puts the input onto the top of the main stack. Removes it from the alt stack.



420
421
422
# File 'lib/bitcoin/script.rb', line 420

def op_fromaltstack
  @stack << @stack_alt.pop
end

#op_greaterthanorequalObject

Returns 1 if a is greater than or equal to b, 0 otherwise.



453
454
455
456
# File 'lib/bitcoin/script.rb', line 453

def op_greaterthanorequal
  a, b = @stack.pop(2).reverse
  @stack << (a >= b ? 1 : 0)
end

#op_hash160Object

The input is hashed twice: first with SHA-256 and then with RIPEMD-160.



397
398
399
400
# File 'lib/bitcoin/script.rb', line 397

def op_hash160
  buf = @stack.pop
  @stack << Digest::RMD160.digest(Digest::SHA256.digest(buf))
end

#op_hash256Object

The input is hashed two times with SHA-256.



409
410
411
412
# File 'lib/bitcoin/script.rb', line 409

def op_hash256
  buf = @stack.pop
  @stack << Digest::SHA256.digest(Digest::SHA256.digest(buf))
end

#op_ifdupObject

If the input is true, duplicate it.



519
520
521
522
523
# File 'lib/bitcoin/script.rb', line 519

def op_ifdup
  if @stack.last != 0
    @stack << @stack.last
  end
end

#op_maxObject

Returns the larger of a and b.



501
502
503
# File 'lib/bitcoin/script.rb', line 501

def op_max
  @stack << @stack.pop(2).max
end

#op_minObject

Returns the smaller of a and b.



496
497
498
# File 'lib/bitcoin/script.rb', line 496

def op_min
  @stack << @stack.pop(2).min
end

#op_nopObject

Does nothing



376
377
# File 'lib/bitcoin/script.rb', line 376

def op_nop
end

#op_ripemd160Object

The input is hashed using RIPEMD-160.



403
404
405
406
# File 'lib/bitcoin/script.rb', line 403

def op_ripemd160
  buf = @stack.pop
  @stack << Digest::RMD160.digest(buf)
end

#op_sha1Object

The input is hashed using SHA-1.



391
392
393
394
# File 'lib/bitcoin/script.rb', line 391

def op_sha1
  buf = @stack.pop
  @stack << Digest::SHA1.digest(buf)
end

#op_sha256Object

The input is hashed using SHA-256.



385
386
387
388
# File 'lib/bitcoin/script.rb', line 385

def op_sha256
  buf = @stack.pop
  @stack << Digest::SHA256.digest(buf)
end

#op_subObject

b is subtracted from a.



447
448
449
450
# File 'lib/bitcoin/script.rb', line 447

def op_sub
  a, b = @stack.pop(2).reverse
  @stack << a - b
end

#op_swapObject

The top two items on the stack are swapped.



430
431
432
# File 'lib/bitcoin/script.rb', line 430

def op_swap
  @stack[-2..-1] = @stack[-2..-1].reverse
end

#op_toaltstackObject

Puts the input onto the top of the alt stack. Removes it from the main stack.



415
416
417
# File 'lib/bitcoin/script.rb', line 415

def op_toaltstack
  @stack_alt << @stack.pop
end

#op_tuckObject

The item at the top of the stack is copied and inserted before the second-to-top item.



425
426
427
# File 'lib/bitcoin/script.rb', line 425

def op_tuck
  @stack[-2..-1] = [ @stack[-1], *@stack[-2..-1] ]
end

#op_verifyObject

Marks transaction as invalid if top stack value is not true. True is removed, but false is not.



470
471
472
473
474
475
476
477
478
# File 'lib/bitcoin/script.rb', line 470

def op_verify
  res = @stack.pop
  if res == 0
    @stack << res
    @script_invalid = true # raise 'transaction invalid' ?
  else
    @script_invalid = false
  end
end

#parse(bytes, offset = 0) ⇒ Object

parse raw script



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/bitcoin/script.rb', line 74

def parse(bytes, offset=0)
  program = bytes.unpack("C*")
  chunks = []
  until program.empty?
    opcode = program.shift(1)[0]
    if opcode >= 0xf0
      opcode = (opcode << 8) | program.shift(1)[0]
    end

    if (opcode > 0) && (opcode < OP_PUSHDATA1)
      len = opcode
      chunks << program.shift(len).pack("C*")
    elsif (opcode == OP_PUSHDATA1)
      len = program.shift(1)[0]
      chunks << program.shift(len).pack("C*")
    elsif (opcode == OP_PUSHDATA2)
      len = program.shift(2).pack("C*").unpack("n")[0]
      chunks << program.shift(len).pack("C*")
    elsif (opcode == OP_PUSHDATA4)
      len = program.shift(4).pack("C*").unpack("N")[0]
      chunks << program.shift(len).pack("C*")
    else
      chunks << opcode
    end
  end
  chunks
end

#pay_to_script_hash(check_callback) ⇒ Object

pay_to_script_hash: en.bitcoin.it/wiki/BIP_0016

<sig> OP_CHECKSIG | OP_HASH160 <script_hash> OP_EQUAL



221
222
223
224
225
226
227
228
229
230
# File 'lib/bitcoin/script.rb', line 221

def pay_to_script_hash(check_callback)
  return false  unless @chunks.size == 5
  script_hash = @chunks[-2]
  script = @chunks[-4]
  sig = self.class.from_string(@chunks[0].unpack("H*")[0]).raw

  return false unless Bitcoin.hash160(script.unpack("H*")[0]) == script_hash.unpack("H*")[0]
  script = self.class.new(sig + script)
  script.run(&check_callback)
end

#run(&check_callback) ⇒ Object

run the script. check_callback is called for OP_CHECKSIG operations



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/bitcoin/script.rb', line 161

def run(&check_callback)
  return pay_to_script_hash(check_callback)  if is_p2sh?
  @debug = []
  @chunks.each{|chunk|
    break if invalid?
    @debug << @stack.map{|i| i.unpack("H*") rescue i}
    
    case chunk
    when Fixnum
      case chunk

      when *OPCODES_METHOD.keys
        m = method( n=OPCODES_METHOD[chunk] )
        @debug << n.to_s.upcase
        (m.arity == 1) ? m.call(check_callback) : m.call  # invoke opcode method

      when *OP_2_16
        @stack << OP_2_16.index(chunk) + 2
        @debug << "OP_#{chunk-80}"
      else
        name = OPCODES[chunk] || chunk
        raise "opcode #{name} unkown or not implemented"
      end
    when String
      @debug << "PUSH DATA #{chunk.unpack("H*")[0]}"
      @stack << chunk
    end
  }
  @debug << @stack.map{|i| i.unpack("H*") rescue i }

  if @script_invalid
    @stack << 0
    @debug << "INVALID TRANSACTION"
  end

  @debug << "RESULT"
  return false if @stack.empty?
  return false if @stack.pop == 0
  true
end

#to_string(chunks = nil) ⇒ Object

string representation of the script



103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/bitcoin/script.rb', line 103

def to_string(chunks=nil)
  (chunks || @chunks).map{|i|
    case i
    when Fixnum
      case i
      when *OPCODES.keys;          OPCODES[i]
      when *OP_2_16;               (OP_2_16.index(i)+2).to_s
      else "(opcode #{i})"
      end
    when String
      i.unpack("H*")[0]
    end
  }.join(" ")
end

#typeObject



264
265
266
267
268
269
270
271
# File 'lib/bitcoin/script.rb', line 264

def type
     if is_hash160?;   :hash160
  elsif is_pubkey?;    :pubkey
  elsif is_multisig?;  :multisig
  elsif is_p2sh?;      :p2sh
  else;                :unknown
  end
end