Class: Tapyrus::Script

Inherits:
Object
  • Object
show all
Includes:
HexConverter, Opcodes
Defined in:
lib/tapyrus/script/script.rb

Overview

tapyrus script

Constant Summary

Constants included from Opcodes

Opcodes::DUPLICATE_KEY, Opcodes::NAME_MAP, Opcodes::OPCODES_MAP, Opcodes::OP_0, Opcodes::OP_0NOTEQUAL, Opcodes::OP_1, Opcodes::OP_10, Opcodes::OP_11, Opcodes::OP_12, Opcodes::OP_13, Opcodes::OP_14, Opcodes::OP_15, Opcodes::OP_16, Opcodes::OP_1ADD, Opcodes::OP_1NEGATE, Opcodes::OP_1SUB, Opcodes::OP_2, Opcodes::OP_2DIV, Opcodes::OP_2DROP, Opcodes::OP_2DUP, Opcodes::OP_2MUL, Opcodes::OP_2OVER, Opcodes::OP_2ROT, Opcodes::OP_2SWAP, Opcodes::OP_3, Opcodes::OP_3DUP, Opcodes::OP_4, Opcodes::OP_5, Opcodes::OP_6, Opcodes::OP_7, Opcodes::OP_8, Opcodes::OP_9, Opcodes::OP_ABS, Opcodes::OP_ADD, Opcodes::OP_AND, Opcodes::OP_BOOLAND, Opcodes::OP_BOOLOR, Opcodes::OP_CAT, Opcodes::OP_CHECKDATASIG, Opcodes::OP_CHECKDATASIGVERIFY, Opcodes::OP_CHECKMULTISIG, Opcodes::OP_CHECKMULTISIGVERIFY, Opcodes::OP_CHECKSIG, Opcodes::OP_CHECKSIGVERIFY, Opcodes::OP_CODESEPARATOR, Opcodes::OP_COLOR, Opcodes::OP_DEPTH, Opcodes::OP_DIV, Opcodes::OP_DROP, Opcodes::OP_DUP, Opcodes::OP_ELSE, Opcodes::OP_ENDIF, Opcodes::OP_EQUAL, Opcodes::OP_EQUALVERIFY, Opcodes::OP_FROMALTSTACK, Opcodes::OP_GREATERTHAN, Opcodes::OP_GREATERTHANOREQUAL, Opcodes::OP_HASH160, Opcodes::OP_HASH256, Opcodes::OP_IF, Opcodes::OP_IFDUP, Opcodes::OP_INVALIDOPCODE, Opcodes::OP_INVERT, Opcodes::OP_LEFT, Opcodes::OP_LESSTHAN, Opcodes::OP_LESSTHANOREQUAL, Opcodes::OP_LSHIFT, Opcodes::OP_MAX, Opcodes::OP_MIN, Opcodes::OP_MOD, Opcodes::OP_MUL, Opcodes::OP_NEGATE, Opcodes::OP_NIP, Opcodes::OP_NOP, Opcodes::OP_NOP1, Opcodes::OP_NOP10, Opcodes::OP_NOP2, Opcodes::OP_NOP3, Opcodes::OP_NOP4, Opcodes::OP_NOP5, Opcodes::OP_NOP6, Opcodes::OP_NOP7, Opcodes::OP_NOP8, Opcodes::OP_NOP9, Opcodes::OP_NOT, Opcodes::OP_NOTIF, Opcodes::OP_NUMEQUAL, Opcodes::OP_NUMEQUALVERIFY, Opcodes::OP_NUMNOTEQUAL, Opcodes::OP_OR, Opcodes::OP_OVER, Opcodes::OP_PICK, Opcodes::OP_PUBKEY, Opcodes::OP_PUBKEYHASH, Opcodes::OP_PUSHDATA1, Opcodes::OP_PUSHDATA2, Opcodes::OP_PUSHDATA4, Opcodes::OP_RESERVED, Opcodes::OP_RESERVED1, Opcodes::OP_RESERVED2, Opcodes::OP_RETURN, Opcodes::OP_RIGHT, Opcodes::OP_RIPEMD160, Opcodes::OP_ROLL, Opcodes::OP_ROT, Opcodes::OP_RSHIFT, Opcodes::OP_SHA1, Opcodes::OP_SHA256, Opcodes::OP_SIZE, Opcodes::OP_SUB, Opcodes::OP_SUBSTR, Opcodes::OP_SWAP, Opcodes::OP_TOALTSTACK, Opcodes::OP_TUCK, Opcodes::OP_VER, Opcodes::OP_VERIF, Opcodes::OP_VERIFY, Opcodes::OP_VERNOTIF, Opcodes::OP_WITHIN, Opcodes::OP_XOR

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Opcodes

defined?, name_to_opcode, opcode_to_name, opcode_to_small_int, small_int_to_opcode

Methods included from HexConverter

#to_hex

Constructor Details

#initializeScript

Returns a new instance of Script.



12
13
14
# File 'lib/tapyrus/script/script.rb', line 12

def initialize
  @chunks = []
end

Instance Attribute Details

#chunksObject

Returns the value of attribute chunks.



10
11
12
# File 'lib/tapyrus/script/script.rb', line 10

def chunks
  @chunks
end

Class Method Details

.decode_number(s) ⇒ Object

decode script number hex to int value



426
427
428
429
430
431
432
433
434
# File 'lib/tapyrus/script/script.rb', line 426

def self.decode_number(s)
  v = s.htb.reverse
  return 0 if v.length.zero?
  mbs = v[0].unpack("C").first
  v[0] = [mbs - 0x80].pack("C") unless (mbs & 0x80) == 0
  result = v.bth.to_i(16)
  result = -result unless (mbs & 0x80) == 0
  result
end

.encode_number(i) ⇒ Object

encode int value to script number hex. The stacks hold byte vectors. When used as numbers, byte vectors are interpreted as little-endian variable-length integers with the most significant bit determining the sign of the integer. Thus 0x81 represents -1. 0x80 is another representation of zero (so called negative 0). Positive 0 is represented by a null-length vector. Byte vectors are interpreted as Booleans where False is represented by any representation of zero, and True is represented by any representation of non-zero.



412
413
414
415
416
417
418
419
420
421
422
423
# File 'lib/tapyrus/script/script.rb', line 412

def self.encode_number(i)
  return "" if i == 0
  negative = i < 0

  hex = i.abs.to_even_length_hex
  hex = "0" + hex unless (hex.length % 2).zero?
  v = hex.htb.reverse # change endian

  v = v << (negative ? 0x80 : 0x00) unless (v[-1].unpack("C").first & 0x80) == 0
  v[-1] = [v[-1].unpack("C").first | 0x80].pack("C") if negative
  v.bth
end

.from_string(string) ⇒ Object

generate script from string.



101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/tapyrus/script/script.rb', line 101

def self.from_string(string)
  script = new
  string
    .split(" ")
    .each do |v|
      opcode = Opcodes.name_to_opcode(v)
      if opcode
        script << (v =~ /^\d/ && Opcodes.small_int_to_opcode(v.ord) ? v.ord : opcode)
      else
        script << (v =~ /^[0-9]+$/ ? v.to_i : v)
      end
    end
  script
end

.pack_pushdata(data) ⇒ Object

binary data convert pushdata which contains data length and append PUSHDATA opcode if necessary.



437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
# File 'lib/tapyrus/script/script.rb', line 437

def self.pack_pushdata(data)
  size = data.bytesize
  header =
    if size < OP_PUSHDATA1
      [size].pack("C")
    elsif size < 0xff
      [OP_PUSHDATA1, size].pack("CC")
    elsif size < 0xffff
      [OP_PUSHDATA2, size].pack("Cv")
    elsif size < 0xffffffff
      [OP_PUSHDATA4, size].pack("CV")
    else
      raise ArgumentError, "data size is too big."
    end
  header + data
end

.parse_from_addr(addr) ⇒ Tapyrus::Script

generate script from addr.

Parameters:

Returns:



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/tapyrus/script/script.rb', line 119

def self.parse_from_addr(addr)
  hex, addr_version = Tapyrus.decode_base58_address(addr)
  case addr_version
  when Tapyrus.chain_params.address_version
    Tapyrus::Script.to_p2pkh(hex)
  when Tapyrus.chain_params.p2sh_version
    Tapyrus::Script.to_p2sh(hex)
  when Tapyrus.chain_params.cp2pkh_version
    color = Tapyrus::Color::ColorIdentifier.parse_from_payload(hex[0..65].htb)
    Tapyrus::Script.to_cp2pkh(color, hex[66..-1])
  when Tapyrus.chain_params.cp2sh_version
    color = Tapyrus::Color::ColorIdentifier.parse_from_payload(hex[0..65].htb)
    Tapyrus::Script.to_cp2sh(color, hex[66..-1])
  end
end

.parse_from_payload(payload) ⇒ Object



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/tapyrus/script/script.rb', line 135

def self.parse_from_payload(payload)
  s = new
  buf = StringIO.new(payload)
  until buf.eof?
    opcode = buf.read(1)
    if opcode.pushdata?
      pushcode = opcode.ord
      packed_size = nil
      len =
        case pushcode
        when OP_PUSHDATA1
          packed_size = buf.read(1)
          packed_size.unpack("C").first
        when OP_PUSHDATA2
          packed_size = buf.read(2)
          packed_size.unpack("v").first
        when OP_PUSHDATA4
          packed_size = buf.read(4)
          packed_size.unpack("V").first
        else
          pushcode if pushcode < OP_PUSHDATA1
        end
      if len
        s.chunks << [len].pack("C") if buf.eof?
        unless buf.eof?
          chunk = (packed_size ? (opcode + packed_size) : (opcode)) + buf.read(len)
          s.chunks << chunk
        end
      end
    else
      if Opcodes.defined?(opcode.ord)
        s << opcode.ord
      else
        s.chunks << (opcode + buf.read) # If opcode is invalid, put all remaining data in last chunk.
      end
    end
  end
  s
end

.to_cp2pkh(color_id, pubkey_hash) ⇒ Script

generate cp2pkh script

Parameters:

  • color (ColorIdentifier)

    identifier

  • hash160 (String)

    of pubkey

Returns:

Raises:

  • (ArgumentError)

    if color_id is nil or invalid



48
49
50
51
# File 'lib/tapyrus/script/script.rb', line 48

def self.to_cp2pkh(color_id, pubkey_hash)
  raise ArgumentError, "Specified color identifier is invalid" unless color_id&.valid?
  new << color_id.to_payload << OP_COLOR << OP_DUP << OP_HASH160 << pubkey_hash << OP_EQUALVERIFY << OP_CHECKSIG
end

.to_cp2sh(color_id, script_hash) ⇒ Script

generate cp2sh script

Parameters:

  • color (ColorIdentifier)

    identifier

  • hash160 (String)

    of script

Returns:

Raises:

  • (ArgumentError)

    if color_id is nil or invalid



58
59
60
61
# File 'lib/tapyrus/script/script.rb', line 58

def self.to_cp2sh(color_id, script_hash)
  raise ArgumentError, "Specified color identifier is invalid" unless color_id&.valid?
  new << color_id.to_payload << OP_COLOR << OP_HASH160 << script_hash << OP_EQUAL
end

.to_multisig_script(m, pubkeys, sort: false) ⇒ Script

generate m of n multisig script

Parameters:

  • m (String)

    the number of signatures required for multisig

  • pubkeys (Array)

    array of public keys that compose multisig

Returns:

  • (Script)

    multisig script.



95
96
97
98
# File 'lib/tapyrus/script/script.rb', line 95

def self.to_multisig_script(m, pubkeys, sort: false)
  pubkeys = pubkeys.sort if sort
  new << m << pubkeys << pubkeys.size << OP_CHECKMULTISIG
end

.to_p2pkh(pubkey_hash) ⇒ Object

generate P2PKH script



17
18
19
# File 'lib/tapyrus/script/script.rb', line 17

def self.to_p2pkh(pubkey_hash)
  new << OP_DUP << OP_HASH160 << pubkey_hash << OP_EQUALVERIFY << OP_CHECKSIG
end

.to_p2sh(script_hash) ⇒ Script

generate p2sh script.

Parameters:

  • script_hash (String)

    script hash for P2SH

Returns:



33
34
35
# File 'lib/tapyrus/script/script.rb', line 33

def self.to_p2sh(script_hash)
  Script.new << OP_HASH160 << script_hash << OP_EQUAL
end

.to_p2sh_multisig_script(m, pubkeys) ⇒ Script

generate m of n multisig p2sh script

Parameters:

  • m (String)

    the number of signatures required for multisig

  • pubkeys (Array)

    array of public keys that compose multisig

Returns:

  • (Script, Script)

    first element is p2sh script, second one is redeem script.



25
26
27
28
# File 'lib/tapyrus/script/script.rb', line 25

def self.to_p2sh_multisig_script(m, pubkeys)
  redeem_script = to_multisig_script(m, pubkeys)
  [redeem_script.to_p2sh, redeem_script]
end

Instance Method Details

#<<(obj) ⇒ Object

append object to payload



302
303
304
305
306
307
308
309
310
311
# File 'lib/tapyrus/script/script.rb', line 302

def <<(obj)
  if obj.is_a?(Integer)
    push_int(obj)
  elsif obj.is_a?(String)
    append_data(obj)
  elsif obj.is_a?(Array)
    obj.each { |o| self.<< o }
    self
  end
end

#==(other) ⇒ Object



516
517
518
519
# File 'lib/tapyrus/script/script.rb', line 516

def ==(other)
  return false unless other
  chunks == other.chunks
end

#add_color(color_id) ⇒ Script

Add color identifier to existing p2pkh or p2sh

Parameters:

  • color_id (ColorIdentifier)

    color identifier

Returns:

  • (Script)

    CP2PKH or CP2SH script

Raises:

  • (ArgumentError)

    if color_id is nil or invalid

  • (RuntimeError)

    if script is neither p2pkh nor p2sh



68
69
70
71
72
73
74
75
# File 'lib/tapyrus/script/script.rb', line 68

def add_color(color_id)
  raise ArgumentError, "Specified color identifier is invalid" unless color_id&.valid?
  raise RuntimeError, "Only p2pkh and p2sh can add color" unless p2pkh? or p2sh?
  Tapyrus::Script.new.tap do |s|
    s << color_id.to_payload << OP_COLOR
    s.chunks += self.chunks
  end
end

#addressesObject

Deprecated.

use #to_addr method.



194
195
196
197
198
199
200
201
# File 'lib/tapyrus/script/script.rb', line 194

def addresses
  return [p2pkh_addr] if p2pkh?
  return [p2sh_addr] if p2sh?
  return [cp2pkh_addr] if cp2pkh?
  return [cp2sh_addr] if cp2sh?
  return get_multisig_pubkeys.map { |pubkey| Tapyrus::Key.new(pubkey: pubkey.bth).to_p2pkh } if multisig?
  []
end

#append_data(data) ⇒ Script

append data to payload with pushdata opcode

Parameters:

  • data (String)

    append data. this data is not binary

Returns:



336
337
338
339
340
# File 'lib/tapyrus/script/script.rb', line 336

def append_data(data)
  data = Encoding::ASCII_8BIT == data.encoding ? data : data.htb
  chunks << Tapyrus::Script.pack_pushdata(data)
  self
end

#append_opcode(opcode) ⇒ Script

append opcode to payload

Parameters:

  • opcode (Integer)

    append opcode which defined by Tapyrus::Opcodes

Returns:

Raises:

  • (ArgumentError)


326
327
328
329
330
331
# File 'lib/tapyrus/script/script.rb', line 326

def append_opcode(opcode)
  opcode = Opcodes.small_int_to_opcode(opcode) if -1 <= opcode && opcode <= 16
  raise ArgumentError, "specified invalid opcode #{opcode}." unless Opcodes.defined?(opcode)
  chunks << opcode.chr
  self
end

#color_idColorIdentifer

Return color identifier for this script.

Returns:

  • (ColorIdentifer)

    color identifier for this script if this script is colored. return nil if this script is not colored.



265
266
267
268
269
# File 'lib/tapyrus/script/script.rb', line 265

def color_id
  return nil unless colored?

  Tapyrus::Color::ColorIdentifier.parse_from_payload(chunks[0].pushed_data)
end

#colored?Boolean

Return whether this script represents colored coin.

Returns:

  • (Boolean)

    true if this script is colored, otherwise false.



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

def colored?
  cp2pkh? || cp2sh?
end

#cp2pkh?Boolean

Return whether this script is a CP2PKH format script or not.

Returns:

  • (Boolean)

    true if this script is cp2pkh, otherwise false.



238
239
240
241
242
243
244
245
# File 'lib/tapyrus/script/script.rb', line 238

def cp2pkh?
  return false unless chunks.size == 7
  return false unless chunks[0].bytesize == 34
  return false unless Tapyrus::Color::ColorIdentifier.parse_from_payload(chunks[0].pushed_data)&.valid?
  return false unless chunks[1].ord == OP_COLOR
  [OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG] == (chunks[2..3] + chunks[5..6]).map(&:ord) &&
    chunks[4].bytesize == 21
end

#cp2sh?Boolean

Return whether this script is a CP2SH format script or not.

Returns:

  • (Boolean)

    true if this script is cp2pkh, otherwise false.



249
250
251
252
253
254
255
# File 'lib/tapyrus/script/script.rb', line 249

def cp2sh?
  return false unless chunks.size == 5
  return false unless chunks[0].bytesize == 34
  return false unless Tapyrus::Color::ColorIdentifier.parse_from_payload(chunks[0].pushed_data)&.valid?
  return false unless chunks[1].ord == OP_COLOR
  OP_HASH160 == chunks[2].ord && OP_EQUAL == chunks[4].ord && chunks[3].bytesize == 21
end

#delete_opcode(opcode) ⇒ Object

remove all occurences of opcode. Typically it’s OP_CODESEPARATOR.



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

def delete_opcode(opcode)
  @chunks = chunks.select { |chunk| chunk.ord != opcode }
  self
end

#empty?Boolean

Returns:

  • (Boolean)


179
180
181
# File 'lib/tapyrus/script/script.rb', line 179

def empty?
  chunks.size == 0
end

#find_and_delete(subscript) ⇒ Object

removes chunks matching subscript byte-for-byte and returns as a new object.

Raises:

  • (ArgumentError)


462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
# File 'lib/tapyrus/script/script.rb', line 462

def find_and_delete(subscript)
  raise ArgumentError, "subscript must be Tapyrus::Script" unless subscript.is_a?(Script)
  return self if subscript.chunks.empty?
  buf = []
  i = 0
  result = Script.new
  chunks.each do |chunk|
    sub_chunk = subscript.chunks[i]
    if chunk.start_with?(sub_chunk)
      if chunk == sub_chunk
        buf << chunk
        i += 1
        (
          i = 0
          buf.clear
        ) if i == subscript.chunks.size # matched the whole subscript
      else
        # matched the part of head
        i = 0
        tmp = chunk.dup
        tmp.slice!(sub_chunk)
        result.chunks << tmp
      end
    else
      result.chunks << buf.join unless buf.empty?
      if buf.first == chunk
        i = 1
        buf = [chunk]
      else
        i = 0
        result.chunks << chunk
      end
    end
  end
  result
end

#get_multisig_pubkeysObject



86
87
88
89
# File 'lib/tapyrus/script/script.rb', line 86

def get_multisig_pubkeys
  num = Tapyrus::Opcodes.opcode_to_small_int(chunks[-2].bth.to_i(16))
  (1..num).map { |i| chunks[i].pushed_data }
end

#get_pubkeysObject

get public keys in the stack. @return[Array] an array of the pubkeys with hex format.



285
286
287
288
289
290
291
292
# File 'lib/tapyrus/script/script.rb', line 285

def get_pubkeys
  chunks
    .select do |c|
      c.pushdata? && [33, 65].include?(c.pushed_data.bytesize) &&
        [2, 3, 4, 6, 7].include?(c.pushed_data[0].bth.to_i(16))
    end
    .map { |c| c.pushed_data.bth }
end

#include?(item) ⇒ Boolean

Check the item is in the chunk of the script.

Returns:

  • (Boolean)


343
344
345
346
347
348
349
350
351
352
353
# File 'lib/tapyrus/script/script.rb', line 343

def include?(item)
  chunk_item =
    if item.is_a?(Integer)
      item.chr
    elsif item.is_a?(String)
      data = Encoding::ASCII_8BIT == item.encoding ? item : item.htb
      Tapyrus::Script.pack_pushdata(data)
    end
  return false unless chunk_item
  chunks.include?(chunk_item)
end

#multisig?Boolean

Returns:

  • (Boolean)


220
221
222
223
224
225
226
# File 'lib/tapyrus/script/script.rb', line 220

def multisig?
  return false if chunks.size < 4 || chunks.last.ord != OP_CHECKMULTISIG
  pubkey_count = Opcodes.opcode_to_small_int(chunks[-2].opcode)
  sig_count = Opcodes.opcode_to_small_int(chunks[0].opcode)
  return false unless pubkey_count || sig_count
  sig_count <= pubkey_count
end

#op_return?Boolean

Returns:

  • (Boolean)


228
229
230
# File 'lib/tapyrus/script/script.rb', line 228

def op_return?
  chunks.size >= 1 && chunks[0].ord == OP_RETURN
end

#op_return_dataObject



271
272
273
274
275
# File 'lib/tapyrus/script/script.rb', line 271

def op_return_data
  return nil unless op_return?
  return nil if chunks.size == 1
  chunks[1].pushed_data
end

#p2pkh?Boolean

whether this script is a P2PKH format script.

Returns:

  • (Boolean)


209
210
211
212
213
# File 'lib/tapyrus/script/script.rb', line 209

def p2pkh?
  return false unless chunks.size == 5
  [OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG] == (chunks[0..1] + chunks[3..4]).map(&:ord) &&
    chunks[2].bytesize == 21
end

#p2sh?Boolean

Returns:

  • (Boolean)


215
216
217
218
# File 'lib/tapyrus/script/script.rb', line 215

def p2sh?
  return false unless chunks.size == 3
  OP_HASH160 == chunks[0].ord && OP_EQUAL == chunks[2].ord && chunks[1].bytesize == 21
end

#push_int(n) ⇒ Object

push integer to stack.



314
315
316
317
318
319
320
321
# File 'lib/tapyrus/script/script.rb', line 314

def push_int(n)
  begin
    append_opcode(n)
  rescue ArgumentError
    append_data(Script.encode_number(n))
  end
  self
end

#push_only?Boolean

whether data push only script which dose not include other opcode

Returns:

  • (Boolean)


278
279
280
281
# File 'lib/tapyrus/script/script.rb', line 278

def push_only?
  chunks.each { |c| return false if !c.opcode.nil? && c.opcode > OP_16 }
  true
end

#remove_colorScript

Remove color identifier from cp2pkh or cp2sh

Returns:

  • (Script)

    P2PKH or P2SH script

Raises:

  • (RuntimeError)

    if script is neither cp2pkh nor cp2sh



80
81
82
83
84
# File 'lib/tapyrus/script/script.rb', line 80

def remove_color
  raise RuntimeError, "Only cp2pkh and cp2sh can remove color" unless cp2pkh? or cp2sh?

  Tapyrus::Script.new.tap { |s| s.chunks = self.chunks[2..-1] }
end

#runObject

execute script interpreter using this script for development.



400
401
402
# File 'lib/tapyrus/script/script.rb', line 400

def run
  Tapyrus::ScriptInterpreter.eval(Tapyrus::Script.new, self.dup)
end

#sizeObject

script size



395
396
397
# File 'lib/tapyrus/script/script.rb', line 395

def size
  to_payload.bytesize
end

#standard?Boolean

check whether standard script.

Returns:

  • (Boolean)


204
205
206
# File 'lib/tapyrus/script/script.rb', line 204

def standard?
  p2pkh? | p2sh? | multisig? | standard_op_return?
end

#standard_op_return?Boolean

Returns:

  • (Boolean)


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

def standard_op_return?
  op_return? && size <= MAX_OP_RETURN_RELAY && (chunks.size == 1 || chunks[1].opcode <= OP_16)
end

#subscript(*args) ⇒ Object

subscript this script to the specified range.



455
456
457
458
459
# File 'lib/tapyrus/script/script.rb', line 455

def subscript(*args)
  s = self.class.new
  s.chunks = chunks[*args]
  s
end

#subscript_codeseparator(separator_index) ⇒ Object

Returns a script that deleted the script before the index specified by separator_index.



506
507
508
509
510
511
512
513
514
# File 'lib/tapyrus/script/script.rb', line 506

def subscript_codeseparator(separator_index)
  buf = []
  process_separator_index = 0
  chunks.each do |chunk|
    buf << chunk if process_separator_index == separator_index
    process_separator_index += 1 if chunk.ord == OP_CODESEPARATOR && process_separator_index < separator_index
  end
  buf.join
end

#to_addrString

Returns the address corresponding to this script. Return nil if there is no corresponding address.

Returns:



185
186
187
188
189
190
191
# File 'lib/tapyrus/script/script.rb', line 185

def to_addr
  return p2pkh_addr if p2pkh?
  return p2sh_addr if p2sh?
  return cp2pkh_addr if cp2pkh?
  return cp2sh_addr if cp2sh?
  nil
end

#to_hObject



528
529
530
531
532
533
534
535
536
# File 'lib/tapyrus/script/script.rb', line 528

def to_h
  h = { asm: to_s, hex: to_hex, type: type }
  addrs = addresses
  unless addrs.empty?
    h[:req_sigs] = multisig? ? Tapyrus::Opcodes.opcode_to_small_int(chunks[0].bth.to_i(16)) : addrs.size
    h[:addresses] = addrs
  end
  h
end

#to_hash160Object

generate hash160 hash for payload



390
391
392
# File 'lib/tapyrus/script/script.rb', line 390

def to_hash160
  Tapyrus.hash160(to_hex)
end

#to_p2shScript

generate p2sh script with this as a redeem script

Returns:



39
40
41
# File 'lib/tapyrus/script/script.rb', line 39

def to_p2sh
  Script.to_p2sh(to_hash160)
end

#to_payloadObject



175
176
177
# File 'lib/tapyrus/script/script.rb', line 175

def to_payload
  chunks.join
end

#to_sObject



355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
# File 'lib/tapyrus/script/script.rb', line 355

def to_s
  chunks
    .map do |c|
      case c
      when Integer
        opcode_to_name(c)
      when String
        return c if c.empty?
        if c.pushdata?
          v = Opcodes.opcode_to_small_int(c.ord)
          if v
            v
          else
            data = c.pushed_data
            if data.bytesize <= 4
              Script.decode_number(data.bth) # for scriptnum
            else
              data.bth
            end
          end
        else
          opcode = Opcodes.opcode_to_name(c.ord)
          opcode ? opcode : "OP_UNKNOWN [error]"
        end
      end
    end
    .join(" ")
end

#to_script_code(skip_separator_index = 0) ⇒ Object

returns the self payload. ScriptInterpreter does not use this.



295
296
297
298
299
# File 'lib/tapyrus/script/script.rb', line 295

def to_script_code(skip_separator_index = 0)
  payload = to_payload
  payload = subscript_codeseparator(skip_separator_index) if skip_separator_index > 0
  Tapyrus.pack_var_string(payload)
end

#to_sha256Object

generate sha-256 hash for payload



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

def to_sha256
  Tapyrus.sha256(to_payload).bth
end

#typeObject



521
522
523
524
525
526
# File 'lib/tapyrus/script/script.rb', line 521

def type
  return "pubkeyhash" if p2pkh?
  return "scripthash" if p2sh?
  return "multisig" if multisig?
  "nonstandard"
end

#unspendable?Boolean

Returns whether the script is guaranteed to fail at execution, regardless of the initial stack. This allows outputs to be pruned instantly when entering the UTXO set.

Returns:

  • (Boolean)

    whether the script is guaranteed to fail at execution



541
542
543
# File 'lib/tapyrus/script/script.rb', line 541

def unspendable?
  (size > 0 && op_return?) || size > Tapyrus::MAX_SCRIPT_SIZE
end