Class: BSV::Script::Script
- Inherits:
-
Object
- Object
- BSV::Script::Script
- Defined in:
- lib/bsv/script/script.rb
Overview
A Bitcoin script — a sequence of opcodes and data pushes.
Scripts are the programmable spending conditions attached to transaction outputs (locking scripts) and inputs (unlocking scripts). This class provides construction from multiple formats, type detection, data extraction, and template constructors for standard script types.
Follows the SDK’s “recognise everything, construct only what’s valid” principle — detection methods (e.g. p2sh?) work for all script types, but constructors are only provided for types valid on BSV.
Constant Summary collapse
- RPUZZLE_HASH_OPS =
Hash type to opcode mapping for RPuzzle scripts.
{ raw: nil, sha1: Opcodes::OP_SHA1, ripemd160: Opcodes::OP_RIPEMD160, sha256: Opcodes::OP_SHA256, hash160: Opcodes::OP_HASH160, hash256: Opcodes::OP_HASH256 }.freeze
- RPUZZLE_OP_TO_TYPE =
Reverse lookup: opcode → hash type symbol (excludes :raw).
RPUZZLE_HASH_OPS.reject { |k, _| k == :raw }.invert.freeze
- RPUZZLE_PREFIX =
The fixed opcode prefix shared by all RPuzzle locking scripts. OP_OVER OP_3 OP_SPLIT OP_NIP OP_1 OP_SPLIT OP_SWAP OP_SPLIT OP_DROP
[ Opcodes::OP_OVER, Opcodes::OP_3, Opcodes::OP_SPLIT, Opcodes::OP_NIP, Opcodes::OP_1, Opcodes::OP_SPLIT, Opcodes::OP_SWAP, Opcodes::OP_SPLIT, Opcodes::OP_DROP ].freeze
Instance Attribute Summary collapse
-
#bytes ⇒ String
readonly
The raw script bytes.
Class Method Summary collapse
-
.builder ⇒ Builder
Create a new Builder for fluent script construction.
-
.from_asm(asm_string) ⇒ Script
Parse a script from ASM notation.
-
.from_binary(binary) ⇒ Script
Parse a script from raw binary bytes.
-
.from_chunks(chunks) ⇒ Script
Build a script from an array of Chunk objects.
-
.from_hex(hex) ⇒ Script
Parse a script from a hex string.
-
.op_cat_lock(expected_data) ⇒ Script
Construct an OP_CAT locking script.
-
.op_cat_unlock(data1, data2) ⇒ Script
Construct an OP_CAT unlocking script.
-
.op_return(*data_items) ⇒ Script
Construct an OP_RETURN data carrier script.
-
.p2ms_lock(required, pubkeys) ⇒ Script
Construct an M-of-N bare multisig locking script.
-
.p2ms_unlock(*signatures) ⇒ Script
Construct a bare multisig unlocking script.
-
.p2pk_lock(pubkey_bytes) ⇒ Script
Construct a Pay-to-Public-Key (P2PK) locking script.
-
.p2pk_unlock(signature_der) ⇒ Script
Construct a P2PK unlocking script.
-
.p2pkh_lock(pubkey_hash_or_address) ⇒ Script
Construct a Pay-to-Public-Key-Hash (P2PKH) locking script.
-
.p2pkh_unlock(signature_der, pubkey_bytes) ⇒ Script
Construct a P2PKH unlocking script.
-
.pushdrop_lock(fields, lock_script, lock_position: :before) ⇒ Script
Construct a PushDrop locking script.
-
.pushdrop_unlock(unlock_script) ⇒ Script
Construct a PushDrop unlocking script.
-
.rpuzzle_lock(hash_value, hash_type: :hash160) ⇒ Script
Construct an RPuzzle locking script.
-
.rpuzzle_unlock(signature_der, pubkey_bytes) ⇒ Script
Construct an RPuzzle unlocking script.
Instance Method Summary collapse
-
#==(other) ⇒ Boolean
trueif both scripts have identical bytes. -
#addresses(network: :mainnet) ⇒ Array<String>
Derive Bitcoin addresses from this script.
-
#chunks ⇒ Array<Chunk>
Parse the script into an array of Chunk objects.
-
#initialize(bytes = ''.b) ⇒ Script
constructor
A new instance of Script.
-
#length ⇒ Integer
Script length in bytes.
-
#multisig? ⇒ Boolean
Whether this is a bare multisig script.
-
#op_cat? ⇒ Boolean
Whether this is an OP_CAT puzzle script.
-
#op_return? ⇒ Boolean
Whether this is an OP_RETURN data carrier script.
-
#op_return_data ⇒ Array<String>?
Extract data payloads from an OP_RETURN script.
-
#p2pk? ⇒ Boolean
Whether this is a Pay-to-Public-Key (P2PK) script.
-
#p2pkh? ⇒ Boolean
Whether this is a Pay-to-Public-Key-Hash (P2PKH) script.
-
#p2sh? ⇒ Boolean
Whether this is a Pay-to-Script-Hash (P2SH) script.
-
#pubkey_hash ⇒ String?
Extract the 20-byte public key hash from a P2PKH script.
-
#pushdrop? ⇒ Boolean
Whether this is a PushDrop script.
-
#pushdrop_fields ⇒ Array<String>?
Extract the embedded data fields from a PushDrop script.
-
#pushdrop_lock_script ⇒ Script?
Extract the underlying lock script from a PushDrop script.
-
#rpuzzle? ⇒ Boolean
Whether this is an RPuzzle script.
-
#rpuzzle_hash ⇒ String?
Extract the hash value from an RPuzzle script.
-
#rpuzzle_hash_type ⇒ Symbol?
Detect the hash type used in an RPuzzle script.
-
#script_hash ⇒ String?
Extract the 20-byte script hash from a P2SH script.
-
#to_asm ⇒ String
Human-readable ASM representation.
-
#to_binary ⇒ String
A copy of the raw script bytes.
-
#to_hex ⇒ String
Hex-encoded script.
-
#type ⇒ String
Classify the script as a standard type.
Constructor Details
#initialize(bytes = ''.b) ⇒ Script
Returns a new instance of Script.
29 30 31 32 |
# File 'lib/bsv/script/script.rb', line 29 def initialize(bytes = ''.b) @bytes = bytes.b @chunks = nil end |
Instance Attribute Details
#bytes ⇒ String (readonly)
Returns the raw script bytes.
26 27 28 |
# File 'lib/bsv/script/script.rb', line 26 def bytes @bytes end |
Class Method Details
.builder ⇒ Builder
Create a new Builder for fluent script construction.
127 128 129 |
# File 'lib/bsv/script/script.rb', line 127 def self.builder Builder.new end |
.from_asm(asm_string) ⇒ Script
Parse a script from ASM notation.
Opcodes are given by name (e.g. “OP_DUP”), data pushes as hex. Supports the canonical aliases “0” (OP_0) and “-1” (OP_1NEGATE). Explicit PUSHDATA sequences (+OP_PUSHDATA1 <len> <hex>+, etc.) are consumed as a unit.
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 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 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/bsv/script/script.rb', line 59 def self.from_asm(asm_string) buf = ''.b tokens = asm_string.split i = 0 while i < tokens.length token = tokens[i] # Canonical short-hand aliases if token == '0' buf << [Opcodes::OP_0].pack('C') i += 1 next end if token == '-1' buf << [Opcodes::OP_1NEGATE].pack('C') i += 1 next end opcode = resolve_opcode(token) if [Opcodes::OP_PUSHDATA1, Opcodes::OP_PUSHDATA2, Opcodes::OP_PUSHDATA4].include?(opcode) # Explicit PUSHDATA sequence: OP_PUSHDATAn <len> <hex> # Consume the following length token and hex token as a unit. raise ArgumentError, "#{token} requires <length> <hex> tokens" unless tokens[i + 1] && tokens[i + 2] hex_token = tokens[i + 2] data = BSV::Primitives::Hex.decode(hex_token.to_s, name: 'ASM PUSHDATA hex token') max_len = { Opcodes::OP_PUSHDATA1 => 0xFF, Opcodes::OP_PUSHDATA2 => 0xFFFF, Opcodes::OP_PUSHDATA4 => 0xFFFFFFFF } raise ArgumentError, "data too long for #{token}: #{data.bytesize} > #{max_len[opcode]}" if data.bytesize > max_len[opcode] case opcode when Opcodes::OP_PUSHDATA1 buf << ([Opcodes::OP_PUSHDATA1, data.bytesize].pack('CC') + data) when Opcodes::OP_PUSHDATA2 buf << ([Opcodes::OP_PUSHDATA2].pack('C') + [data.bytesize].pack('v') + data) when Opcodes::OP_PUSHDATA4 buf << ([Opcodes::OP_PUSHDATA4].pack('C') + [data.bytesize].pack('V') + data) end i += 3 elsif opcode buf << [opcode].pack('C') i += 1 else # Data push — token is hex data = BSV::Primitives::Hex.decode(token, name: 'ASM hex token') buf << encode_push_data(data) i += 1 end end new(buf) end |
.from_binary(binary) ⇒ Script
Parse a script from raw binary bytes.
38 39 40 |
# File 'lib/bsv/script/script.rb', line 38 def self.from_binary(binary) new(binary) end |
.from_chunks(chunks) ⇒ Script
Build a script from an array of Chunk objects.
117 118 119 120 121 122 |
# File 'lib/bsv/script/script.rb', line 117 def self.from_chunks(chunks) buf = chunks.map(&:to_binary).join script = new(buf) script.instance_variable_set(:@chunks, chunks.dup) script end |
.from_hex(hex) ⇒ Script
Parse a script from a hex string.
46 47 48 |
# File 'lib/bsv/script/script.rb', line 46 def self.from_hex(hex) new(BSV::Primitives::Hex.decode(hex, name: 'script hex')) end |
.op_cat_lock(expected_data) ⇒ Script
Construct an OP_CAT locking script.
The script concatenates two stack items and compares the result against the expected data. The spender must push two values whose concatenation equals expected_data.
389 390 391 392 393 394 |
# File 'lib/bsv/script/script.rb', line 389 def self.op_cat_lock(expected_data) buf = [Opcodes::OP_CAT].pack('C') buf << encode_push_data(expected_data.b) buf << [Opcodes::OP_EQUAL].pack('C') new(buf) end |
.op_cat_unlock(data1, data2) ⇒ Script
Construct an OP_CAT unlocking script.
Pushes two data items onto the stack. The locking script’s OP_CAT will concatenate them and compare against the expected value.
404 405 406 407 408 |
# File 'lib/bsv/script/script.rb', line 404 def self.op_cat_unlock(data1, data2) buf = encode_push_data(data1.b) buf << encode_push_data(data2.b) new(buf) end |
.op_return(*data_items) ⇒ Script
Construct an OP_RETURN data carrier script.
Uses the safe OP_FALSE OP_RETURN prefix (provably unspendable).
139 140 141 142 143 |
# File 'lib/bsv/script/script.rb', line 139 def self.op_return(*data_items) buf = [Opcodes::OP_FALSE, Opcodes::OP_RETURN].pack('CC') data_items.each { |d| buf << encode_push_data(d.b) } new(buf) end |
.p2ms_lock(required, pubkeys) ⇒ Script
Construct an M-of-N bare multisig locking script.
242 243 244 245 246 247 248 249 250 251 252 |
# File 'lib/bsv/script/script.rb', line 242 def self.p2ms_lock(required, pubkeys) n = pubkeys.length raise ArgumentError, 'm must be between 1 and n' unless required.between?(1, n) raise ArgumentError, 'n must be <= 16' unless n <= 16 buf = [Opcodes::OP_1 + required - 1].pack('C') pubkeys.each { |pk| buf << encode_push_data(pk.b) } buf << [Opcodes::OP_1 + n - 1].pack('C') buf << [Opcodes::OP_CHECKMULTISIG].pack('C') new(buf) end |
.p2ms_unlock(*signatures) ⇒ Script
Construct a bare multisig unlocking script.
258 259 260 261 262 |
# File 'lib/bsv/script/script.rb', line 258 def self.p2ms_unlock(*signatures) buf = [Opcodes::OP_0].pack('C') signatures.each { |sig| buf << encode_push_data(sig.b) } new(buf) end |
.p2pk_lock(pubkey_bytes) ⇒ Script
Construct a Pay-to-Public-Key (P2PK) locking script.
220 221 222 223 224 225 226 |
# File 'lib/bsv/script/script.rb', line 220 def self.p2pk_lock(pubkey_bytes) raise ArgumentError, 'pubkey must be 33 or 65 bytes' unless [33, 65].include?(pubkey_bytes.bytesize) buf = encode_push_data(pubkey_bytes) buf << [Opcodes::OP_CHECKSIG].pack('C') new(buf) end |
.p2pk_unlock(signature_der) ⇒ Script
Construct a P2PK unlocking script.
232 233 234 |
# File 'lib/bsv/script/script.rb', line 232 def self.p2pk_unlock(signature_der) new(encode_push_data(signature_der)) end |
.p2pkh_lock(pubkey_hash_or_address) ⇒ Script
Construct a Pay-to-Public-Key-Hash (P2PKH) locking script.
Accepts either a raw 20-byte binary hash or a Base58Check address string. When given an address string, the version prefix is validated: 0x00 (mainnet) and 0x6f (testnet) are accepted; 0x05 (P2SH) is rejected with a clear error message.
158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/bsv/script/script.rb', line 158 def self.p2pkh_lock(pubkey_hash_or_address) pubkey_hash = resolve_pubkey_hash(pubkey_hash_or_address) buf = [ Opcodes::OP_DUP, Opcodes::OP_HASH160 ].pack('CC') buf << encode_push_data(pubkey_hash) buf << [Opcodes::OP_EQUALVERIFY, Opcodes::OP_CHECKSIG].pack('CC') new(buf) end |
.p2pkh_unlock(signature_der, pubkey_bytes) ⇒ Script
Construct a P2PKH unlocking script.
209 210 211 212 213 |
# File 'lib/bsv/script/script.rb', line 209 def self.p2pkh_unlock(signature_der, pubkey_bytes) buf = encode_push_data(signature_der) buf << encode_push_data(pubkey_bytes) new(buf) end |
.pushdrop_lock(fields, lock_script, lock_position: :before) ⇒ Script
Construct a PushDrop locking script.
Pushes arbitrary data fields onto the stack, then drops them all before the locking condition executes. Used for token protocols where data must be embedded in spendable outputs.
The lock_position parameter controls where the locking script is placed relative to the push/drop sequence:
-
:‘before’ (default) — lock script appears before the data pushes and drops. Matches the ts-sdk default and the canonical PushDrop layout used by overlay token protocols. Structure: [lock_script] [field0] … [fieldN] [OP_2DROP…] [OP_DROP?]
-
:‘after’ — lock script appears after the drops (legacy behaviour). Structure: [field0] … [fieldN] [OP_2DROP…] [OP_DROP?] [lock_script]
**Breaking change (v0.9):** the default changed from :‘after’ to :‘before’ to match the ts-sdk. Callers that relied on the old default must pass lock_position: :after explicitly.
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
# File 'lib/bsv/script/script.rb', line 290 def self.pushdrop_lock(fields, lock_script, lock_position: :before) raise ArgumentError, 'fields must not be empty' if fields.empty? raise ArgumentError, 'lock_script must be a Script' unless lock_script.is_a?(Script) raise ArgumentError, "lock_position must be :before or :after, got #{lock_position.inspect}" \ unless %i[before after].include?(lock_position) field_chunks = fields.map { |f| encode_minimally(f.b) } drop_chunks = [] remaining = fields.length while remaining > 1 drop_chunks << Chunk.new(opcode: Opcodes::OP_2DROP) remaining -= 2 end drop_chunks << Chunk.new(opcode: Opcodes::OP_DROP) if remaining == 1 all_chunks = if lock_position == :before lock_script.chunks + field_chunks + drop_chunks else field_chunks + drop_chunks + lock_script.chunks end from_chunks(all_chunks) end |
.pushdrop_unlock(unlock_script) ⇒ Script
Construct a PushDrop unlocking script.
Pass-through wrapper — the data fields are dropped during execution, so the unlocking script just needs to satisfy the underlying lock.
322 323 324 |
# File 'lib/bsv/script/script.rb', line 322 def self.pushdrop_unlock(unlock_script) unlock_script end |
.rpuzzle_lock(hash_value, hash_type: :hash160) ⇒ Script
Construct an RPuzzle locking script.
RPuzzle enables hash-puzzle-based spending where the spender proves knowledge of the ECDSA K-value (nonce) that produced a signature’s R component.
358 359 360 361 362 363 364 365 366 367 |
# File 'lib/bsv/script/script.rb', line 358 def self.rpuzzle_lock(hash_value, hash_type: :hash160) raise ArgumentError, "unknown hash_type: #{hash_type}" unless RPUZZLE_HASH_OPS.key?(hash_type) buf = RPUZZLE_PREFIX.pack('C*') hash_op = RPUZZLE_HASH_OPS[hash_type] buf << [hash_op].pack('C') if hash_op buf << encode_push_data(hash_value.b) buf << [Opcodes::OP_EQUALVERIFY, Opcodes::OP_CHECKSIG].pack('CC') new(buf) end |
.rpuzzle_unlock(signature_der, pubkey_bytes) ⇒ Script
Construct an RPuzzle unlocking script.
Same wire format as P2PKH: signature + public key.
376 377 378 |
# File 'lib/bsv/script/script.rb', line 376 def self.rpuzzle_unlock(signature_der, pubkey_bytes) p2pkh_unlock(signature_der, pubkey_bytes) end |
Instance Method Details
#==(other) ⇒ Boolean
Returns true if both scripts have identical bytes.
701 702 703 |
# File 'lib/bsv/script/script.rb', line 701 def ==(other) other.is_a?(self.class) && @bytes == other.bytes end |
#addresses(network: :mainnet) ⇒ Array<String>
Derive Bitcoin addresses from this script.
Currently supports P2PKH scripts only.
679 680 681 682 683 684 685 686 |
# File 'lib/bsv/script/script.rb', line 679 def addresses(network: :mainnet) if p2pkh? prefix = network == :testnet ? BSV::Primitives::PublicKey::TESTNET_PUBKEY_HASH : BSV::Primitives::PublicKey::MAINNET_PUBKEY_HASH [BSV::Primitives::Base58.check_encode(prefix + pubkey_hash)] else [] end end |
#chunks ⇒ Array<Chunk>
Parse the script into an array of Chunk objects.
Results are cached after first parse.
695 696 697 |
# File 'lib/bsv/script/script.rb', line 695 def chunks @chunks ||= parse_chunks end |
#length ⇒ Integer
Returns script length in bytes.
428 429 430 |
# File 'lib/bsv/script/script.rb', line 428 def length @bytes.bytesize end |
#multisig? ⇒ Boolean
Whether this is a bare multisig script.
Pattern: OP_M <pubkey1> … <pubkeyN> OP_N OP_CHECKMULTISIG
551 552 553 554 555 556 557 558 |
# File 'lib/bsv/script/script.rb', line 551 def multisig? c = chunks return false if c.length < 3 return false unless small_int_opcode?(c[0].opcode) return false unless small_int_opcode?(c[-2].opcode) && c[-1].opcode == Opcodes::OP_CHECKMULTISIG c[1..-3].all?(&:data?) end |
#op_cat? ⇒ Boolean
Whether this is an OP_CAT puzzle script.
Pattern: OP_CAT <expected_data> OP_EQUAL
538 539 540 541 542 543 544 |
# File 'lib/bsv/script/script.rb', line 538 def op_cat? c = chunks c.length == 3 && c[0].opcode == Opcodes::OP_CAT && c[1].data? && c[2].opcode == Opcodes::OP_EQUAL end |
#op_return? ⇒ Boolean
Whether this is an OP_RETURN data carrier script.
Matches both OP_RETURN … and OP_FALSE OP_RETURN … forms.
468 469 470 471 472 |
# File 'lib/bsv/script/script.rb', line 468 def op_return? b = @bytes (b.bytesize.positive? && b.getbyte(0) == Opcodes::OP_RETURN) || (b.bytesize > 1 && b.getbyte(0) == Opcodes::OP_FALSE && b.getbyte(1) == Opcodes::OP_RETURN) end |
#op_return_data ⇒ Array<String>?
Extract data payloads from an OP_RETURN script.
After F3.1’s fix, the parser absorbs all bytes following a top-level OP_RETURN into a single raw-data chunk. This method re-parses that tail so callers receive one entry per push (including bare opcodes that appear as raw bytes in the tail).
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 |
# File 'lib/bsv/script/script.rb', line 609 def op_return_data return unless op_return? # Determine where the payload starts (after OP_RETURN or OP_FALSE OP_RETURN) start = @bytes.getbyte(0) == Opcodes::OP_RETURN ? 1 : 2 # Parse the tail bytes as a sub-script to recover individual push items. # The tail was stored as a raw-data chunk by parse_chunks; parsing it again # yields all the contained push operations. # # IMPORTANT: disable OP_RETURN termination during re-parse — the tail # may legitimately contain 0x6a bytes as data, and we don't want the # parser to stop at them. tail_bytes = @bytes.byteslice(start, @bytes.bytesize - start) return [] if tail_bytes.nil? || tail_bytes.empty? tail_script = Script.new(tail_bytes) tail_script.send(:parse_chunks, terminate_on_op_return: false).map do |ch| ch.data? ? ch.data : [ch.opcode].pack('C') end end |
#p2pk? ⇒ Boolean
Whether this is a Pay-to-Public-Key (P2PK) script.
Pattern: <pubkey> OP_CHECKSIG
479 480 481 482 483 484 485 486 487 |
# File 'lib/bsv/script/script.rb', line 479 def p2pk? c = chunks return false unless c.length == 2 && c[0].data? && c[1].opcode == Opcodes::OP_CHECKSIG pubkey = c[0].data version = pubkey.getbyte(0) ([0x02, 0x03].include?(version) && pubkey.bytesize == 33) || ([0x04, 0x06, 0x07].include?(version) && pubkey.bytesize == 65) end |
#p2pkh? ⇒ Boolean
Whether this is a Pay-to-Public-Key-Hash (P2PKH) script.
Pattern: OP_DUP OP_HASH160 <20 bytes> OP_EQUALVERIFY OP_CHECKSIG
439 440 441 442 443 444 445 446 447 |
# File 'lib/bsv/script/script.rb', line 439 def p2pkh? b = @bytes b.bytesize == 25 && b.getbyte(0) == Opcodes::OP_DUP && b.getbyte(1) == Opcodes::OP_HASH160 && b.getbyte(2) == 0x14 && b.getbyte(23) == Opcodes::OP_EQUALVERIFY && b.getbyte(24) == Opcodes::OP_CHECKSIG end |
#p2sh? ⇒ Boolean
Whether this is a Pay-to-Script-Hash (P2SH) script.
Detection only — P2SH is not valid on BSV, so no constructor is provided. Pattern: OP_HASH160 <20 bytes> OP_EQUAL
455 456 457 458 459 460 461 |
# File 'lib/bsv/script/script.rb', line 455 def p2sh? b = @bytes b.bytesize == 23 && b.getbyte(0) == Opcodes::OP_HASH160 && b.getbyte(1) == 0x14 && b.getbyte(22) == Opcodes::OP_EQUAL end |
#pubkey_hash ⇒ String?
Extract the 20-byte public key hash from a P2PKH script.
586 587 588 589 590 |
# File 'lib/bsv/script/script.rb', line 586 def pubkey_hash return unless p2pkh? @bytes.byteslice(3, 20) end |
#pushdrop? ⇒ Boolean
Whether this is a PushDrop script.
Detects both lock_position: :before and lock_position: :after layouts:
-
:before: [lock_chunks…] [field0] … [fieldN] [OP_2DROP…] [OP_DROP?] -
:after: [field0] … [fieldN] [OP_2DROP…] [OP_DROP?] [lock_chunks…]
497 498 499 |
# File 'lib/bsv/script/script.rb', line 497 def pushdrop? pushdrop_layout ? true : false end |
#pushdrop_fields ⇒ Array<String>?
Extract the embedded data fields from a PushDrop script.
Works for both :before and :after lock positions.
654 655 656 657 658 659 |
# File 'lib/bsv/script/script.rb', line 654 def pushdrop_fields layout = pushdrop_layout return unless layout layout[:field_chunks].map { |ch| decode_minimal_push(ch) } end |
#pushdrop_lock_script ⇒ Script?
Extract the underlying lock script from a PushDrop script.
Works for both :before and :after lock positions.
666 667 668 669 670 671 |
# File 'lib/bsv/script/script.rb', line 666 def pushdrop_lock_script layout = pushdrop_layout return unless layout self.class.from_chunks(layout[:lock_chunks]) end |
#rpuzzle? ⇒ Boolean
Whether this is an RPuzzle script.
Detects the fixed R-value extraction prefix followed by an optional hash opcode, a data push, OP_EQUALVERIFY, and OP_CHECKSIG.
507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 |
# File 'lib/bsv/script/script.rb', line 507 def rpuzzle? c = chunks # Minimum: 9 prefix + hash_data + OP_EQUALVERIFY + OP_CHECKSIG = 12 # With hash op: 13 return false unless c.length >= 12 # Verify the 9-opcode prefix RPUZZLE_PREFIX.each_with_index do |op, i| return false unless c[i].opcode == op end # After prefix: optional hash op, then data push, OP_EQUALVERIFY, OP_CHECKSIG return false unless c[-1].opcode == Opcodes::OP_CHECKSIG return false unless c[-2].opcode == Opcodes::OP_EQUALVERIFY return false unless c[-3].data? # Either exactly 12 chunks (raw) or 13 chunks (with hash op) if c.length == 12 true elsif c.length == 13 RPUZZLE_HASH_OPS.values.compact.include?(c[9].opcode) else false end end |
#rpuzzle_hash ⇒ String?
Extract the hash value from an RPuzzle script.
634 635 636 637 638 |
# File 'lib/bsv/script/script.rb', line 634 def rpuzzle_hash return unless rpuzzle? chunks[-3].data end |
#rpuzzle_hash_type ⇒ Symbol?
Detect the hash type used in an RPuzzle script.
643 644 645 646 647 |
# File 'lib/bsv/script/script.rb', line 643 def rpuzzle_hash_type return unless rpuzzle? chunks.length == 12 ? :raw : RPUZZLE_OP_TO_TYPE[chunks[9].opcode] end |
#script_hash ⇒ String?
Extract the 20-byte script hash from a P2SH script.
595 596 597 598 599 |
# File 'lib/bsv/script/script.rb', line 595 def script_hash return unless p2sh? @bytes.byteslice(2, 20) end |
#to_asm ⇒ String
Returns human-readable ASM representation.
423 424 425 |
# File 'lib/bsv/script/script.rb', line 423 def to_asm chunks.map(&:to_asm).join(' ') end |
#to_binary ⇒ String
Returns a copy of the raw script bytes.
413 414 415 |
# File 'lib/bsv/script/script.rb', line 413 def to_binary @bytes.dup end |
#to_hex ⇒ String
Returns hex-encoded script.
418 419 420 |
# File 'lib/bsv/script/script.rb', line 418 def to_hex @bytes.unpack1('H*') end |
#type ⇒ String
Classify the script as a standard type.
567 568 569 570 571 572 573 574 575 576 577 578 579 |
# File 'lib/bsv/script/script.rb', line 567 def type if @bytes.empty? then 'empty' elsif p2pkh? then 'pubkeyhash' elsif p2pk? then 'pubkey' elsif p2sh? then 'scripthash' elsif op_return? then 'nulldata' elsif multisig? then 'multisig' elsif pushdrop? then 'pushdrop' elsif rpuzzle? then 'rpuzzle' elsif op_cat? then 'opcat' else 'nonstandard' end end |