# Class BSV::Script::Script <a id="class-BSV-Script-Script"></a>

**Inherits:** `Object`

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. <code>p2sh?</code>) work for all script
types, but constructors are only provided for types valid on BSV.

**@example Build a P2PKH locking script**
```ruby
script = BSV::Script::Script.p2pkh_lock(pubkey_hash)
script.type #=> "pubkeyhash"
```

**@example Parse from hex and inspect**
```ruby
script = BSV::Script::Script.from_hex('76a914...')
script.p2pkh? #=> true
script.to_asm #=> "OP_DUP OP_HASH160 ... OP_EQUALVERIFY OP_CHECKSIG"
```

## Constants
### `DROP_OPS` <a id="constant-DROP_OPS"></a> <a id="DROP_OPS-constant"></a>
Not documented.

### `RPUZZLE_HASH_OPS` <a id="constant-RPUZZLE_HASH_OPS"></a> <a id="RPUZZLE_HASH_OPS-constant"></a>
Hash type to opcode mapping for RPuzzle scripts.

### `RPUZZLE_OP_TO_TYPE` <a id="constant-RPUZZLE_OP_TO_TYPE"></a> <a id="RPUZZLE_OP_TO_TYPE-constant"></a>
Reverse lookup: opcode → hash type symbol (excludes :raw).

### `RPUZZLE_PREFIX` <a id="constant-RPUZZLE_PREFIX"></a> <a id="RPUZZLE_PREFIX-constant"></a>
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

## Attributes
### `bytes` [R] <a id="attribute-i-bytes"></a> <a id="bytes-instance_method"></a>
- **@note** Scripts are protocol-level immutable; the underlying bytes are
defensively copied + frozen on construction. Downstream callers
that need to build a script incrementally must compose bytes
into a new String and pass it to a fresh +Script.new+.
- **@return** [String] the raw script bytes (frozen, binary encoding)

## Public Class Methods
### `builder()` <a id="method-c-builder"></a> <a id="builder-class_method"></a>
Create a new {Builder} for fluent script construction.
- **@return** [Builder]

### `from_asm(asm_string)` <a id="method-c-from_asm"></a> <a id="from_asm-class_method"></a>
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.
- **@param** `asm_string` [String] space-separated ASM tokens
- **@return** [Script]

### `from_binary(binary)` <a id="method-c-from_binary"></a> <a id="from_binary-class_method"></a>
Parse a script from raw binary bytes.
- **@param** `binary` [String] raw script bytes
- **@return** [Script]

### `from_chunks(chunks)` <a id="method-c-from_chunks"></a> <a id="from_chunks-class_method"></a>
Build a script from an array of {Chunk} objects.
- **@param** `chunks` [Array<Chunk>] script chunks
- **@return** [Script]

### `from_hex(hex)` <a id="method-c-from_hex"></a> <a id="from_hex-class_method"></a>
Parse a script from a hex string.
- **@param** `hex` [String] hex-encoded script
- **@return** [Script]

### `op_cat_lock(expected_data)` <a id="method-c-op_cat_lock"></a> <a id="op_cat_lock-class_method"></a>
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`.
- **@param** `expected_data` [String] binary string — the expected result of
concatenating the two unlocking values
- **@return** [Script]

### `op_cat_unlock(data1, data2)` <a id="method-c-op_cat_unlock"></a> <a id="op_cat_unlock-class_method"></a>
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.
- **@param** `data1` [String] binary string — first item (pushed first, deeper on stack)
- **@param** `data2` [String] binary string — second item (pushed second, top of stack)
- **@return** [Script]

### `op_return(*data_items)` <a id="method-c-op_return"></a> <a id="op_return-class_method"></a>
Construct an OP_RETURN data carrier script.

Uses the safe OP_FALSE OP_RETURN prefix (provably unspendable).
- **@param** `data_items` [Array<String>] one or more data payloads to embed
- **@return** [Script]

### `p2ms_lock(required, pubkeys)` <a id="method-c-p2ms_lock"></a> <a id="p2ms_lock-class_method"></a>
Construct an M-of-N bare multisig locking script.
- **@param** `required` [Integer] number of required signatures (M)
- **@param** `pubkeys` [Array<String>] array of public key byte strings (N keys)
- **@raise** [ArgumentError] if M or N is out of range
- **@return** [Script]

### `p2ms_unlock(*signatures)` <a id="method-c-p2ms_unlock"></a> <a id="p2ms_unlock-class_method"></a>
Construct a bare multisig unlocking script.
- **@param** `signatures` [Array<String>] DER-encoded signatures with sighash bytes
- **@return** [Script]

### `p2pk_lock(pubkey_bytes)` <a id="method-c-p2pk_lock"></a> <a id="p2pk_lock-class_method"></a>
Construct a Pay-to-Public-Key (P2PK) locking script.
- **@param** `pubkey_bytes` [String] 33-byte compressed or 65-byte uncompressed public key
- **@raise** [ArgumentError] if pubkey_bytes is not 33 or 65 bytes
- **@return** [Script]

### `p2pk_unlock(signature_der)` <a id="method-c-p2pk_unlock"></a> <a id="p2pk_unlock-class_method"></a>
Construct a P2PK unlocking script.
- **@param** `signature_der` [String] DER-encoded signature with sighash byte appended
- **@return** [Script]

### `p2pkh_lock(pubkey_hash_or_address)` <a id="method-c-p2pkh_lock"></a> <a id="p2pkh_lock-class_method"></a>
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.
- **@param** `pubkey_hash_or_address` [String] 20-byte binary pubkey hash, or a
Base58Check address string
- **@raise** [ArgumentError] if the argument is not a valid 20-byte hash, if the
address has an unrecognised prefix, or if a P2SH address is supplied
- **@raise** [BSV::Primitives::Base58::ChecksumError] if the address checksum is invalid
- **@return** [Script]

### `p2pkh_unlock(signature_der, pubkey_bytes)` <a id="method-c-p2pkh_unlock"></a> <a id="p2pkh_unlock-class_method"></a>
Construct a P2PKH unlocking script.
- **@param** `signature_der` [String] DER-encoded signature with sighash byte appended
- **@param** `pubkey_bytes` [String] compressed or uncompressed public key bytes
- **@return** [Script]

### `pushdrop_lock(fields, lock_script, lock_position: = :before)` <a id="method-c-pushdrop_lock"></a> <a id="pushdrop_lock-class_method"></a>
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.
- **@param** `fields` [Array<String>] data payloads to embed (binary strings)
- **@param** `lock_script` [Script] the underlying locking condition (e.g. P2PKH)
- **@param** `lock_position` [Symbol] +:before+ (default) or +:after+
- **@raise** [ArgumentError] if fields is empty, lock_script is not a Script,
or lock_position is not +:before+ or +:after+
- **@return** [Script]

### `pushdrop_unlock(unlock_script)` <a id="method-c-pushdrop_unlock"></a> <a id="pushdrop_unlock-class_method"></a>
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.
- **@param** `unlock_script` [Script] unlocking script for the underlying condition
- **@return** [Script]

### `rpuzzle_lock(hash_value, hash_type: = :hash160)` <a id="method-c-rpuzzle_lock"></a> <a id="rpuzzle_lock-class_method"></a>
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.
- **@param** `hash_value` [String] the R-value or hash of R-value to lock against
- **@param** `hash_type` [Symbol] one of +:raw+, +:sha1+, +:ripemd160+,
+:sha256+, +:hash160+, +:hash256+
- **@raise** [ArgumentError] if hash_type is invalid
- **@return** [Script]

### `rpuzzle_unlock(signature_der, pubkey_bytes)` <a id="method-c-rpuzzle_unlock"></a> <a id="rpuzzle_unlock-class_method"></a>
Construct an RPuzzle unlocking script.

Same wire format as P2PKH: signature + public key.
- **@param** `signature_der` [String] DER-encoded signature with sighash byte
- **@param** `pubkey_bytes` [String] compressed or uncompressed public key bytes
- **@return** [Script]

## Public Instance Methods
### `==(other)` <a id="method-i--3D-3D"></a> <a id="==-instance_method"></a>
- **@param** `other` [Object] the object to compare
- **@return** [Boolean] +true+ if both scripts have identical bytes

### `addresses(network: = :mainnet)` <a id="method-i-addresses"></a> <a id="addresses-instance_method"></a>
Derive Bitcoin addresses from this script.

Currently supports P2PKH scripts only.
- **@param** `network` [Symbol] +:mainnet+ or +:testnet+
- **@return** [Array<String>] array of derived addresses (empty if unsupported type)

### `chunks()` <a id="method-i-chunks"></a> <a id="chunks-instance_method"></a>
Parse the script into an array of {Chunk} objects.

Results are cached after first parse.
- **@return** [Array<Chunk>] the parsed chunks

### `initialize(bytes = ''.b)` <a id="method-i-initialize"></a> <a id="initialize-instance_method"></a>
- **@param** `bytes` [String] raw script bytes (default: empty)
- **@return** [Script] a new instance of Script

### `length()` <a id="method-i-length"></a> <a id="length-instance_method"></a>
- **@return** [Integer] script length in bytes

### `multisig?()` <a id="method-i-multisig-3F"></a> <a id="multisig?-instance_method"></a>
Whether this is a bare multisig script.

Pattern: +OP_M <pubkey1> ... <pubkeyN> OP_N OP_CHECKMULTISIG+
- **@return** [Boolean]

### `op_cat?()` <a id="method-i-op_cat-3F"></a> <a id="op_cat?-instance_method"></a>
Whether this is an OP_CAT puzzle script.

Pattern: +OP_CAT <expected_data> OP_EQUAL+
- **@return** [Boolean]

### `op_return?()` <a id="method-i-op_return-3F"></a> <a id="op_return?-instance_method"></a>
Whether this is an OP_RETURN data carrier script.

Matches both +OP_RETURN ...+ and +OP_FALSE OP_RETURN ...+ forms.
- **@return** [Boolean]

### `op_return_data()` <a id="method-i-op_return_data"></a> <a id="op_return_data-instance_method"></a>
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).
- **@return** [Array<String>, nil] array of data pushes, or +nil+ if not OP_RETURN

### `p2pk?()` <a id="method-i-p2pk-3F"></a> <a id="p2pk?-instance_method"></a>
Whether this is a Pay-to-Public-Key (P2PK) script.

Pattern: +<pubkey> OP_CHECKSIG+
- **@return** [Boolean]

### `p2pkh?()` <a id="method-i-p2pkh-3F"></a> <a id="p2pkh?-instance_method"></a>
Whether this is a Pay-to-Public-Key-Hash (P2PKH) script.

Pattern: +OP_DUP OP_HASH160 <20 bytes> OP_EQUALVERIFY OP_CHECKSIG+
- **@return** [Boolean]

### `p2sh?()` <a id="method-i-p2sh-3F"></a> <a id="p2sh?-instance_method"></a>
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+
- **@return** [Boolean]

### `pubkey_hash()` <a id="method-i-pubkey_hash"></a> <a id="pubkey_hash-instance_method"></a>
Extract the 20-byte public key hash from a P2PKH script.
- **@return** [String, nil] the pubkey hash, or +nil+ if not P2PKH

### `push_only?()` <a id="method-i-push_only-3F"></a> <a id="push_only?-instance_method"></a>
Whether the script consists entirely of push-data operations.

A script is push-only iff every chunk's opcode is at most `OP_16` (0x60). Used
by the script interpreter to enforce the `SIGPUSHONLY` verification flag on
unlock scripts.
- **@return** [Boolean]

### `pushdrop?()` <a id="method-i-pushdrop-3F"></a> <a id="pushdrop?-instance_method"></a>
Whether this is a PushDrop script.

Detects both +lock_position: :before+ and +lock_position: :after+ layouts:

*   <code>:before</code>: +[lock_chunks...] [field0] ... [fieldN]
    [OP_2DROP...] [OP_DROP?]+
*   <code>:after</code>:  +[field0] ... [fieldN] [OP_2DROP...] [OP_DROP?]
    [lock_chunks...]+
- **@return** [Boolean]

### `pushdrop_fields()` <a id="method-i-pushdrop_fields"></a> <a id="pushdrop_fields-instance_method"></a>
Extract the embedded data fields from a PushDrop script.

Works for both <code>:before</code> and <code>:after</code> lock positions.
- **@return** [Array<String>, nil] array of field data, or +nil+ if not PushDrop

### `pushdrop_lock_script()` <a id="method-i-pushdrop_lock_script"></a> <a id="pushdrop_lock_script-instance_method"></a>
Extract the underlying lock script from a PushDrop script.

Works for both <code>:before</code> and <code>:after</code> lock positions.
- **@return** [Script, nil] the lock script portion, or +nil+ if not PushDrop

### `rpuzzle?()` <a id="method-i-rpuzzle-3F"></a> <a id="rpuzzle?-instance_method"></a>
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.
- **@return** [Boolean]

### `rpuzzle_hash()` <a id="method-i-rpuzzle_hash"></a> <a id="rpuzzle_hash-instance_method"></a>
Extract the hash value from an RPuzzle script.
- **@return** [String, nil] the locked hash/R-value, or +nil+ if not RPuzzle

### `rpuzzle_hash_type()` <a id="method-i-rpuzzle_hash_type"></a> <a id="rpuzzle_hash_type-instance_method"></a>
Detect the hash type used in an RPuzzle script.
- **@return** [Symbol, nil] the hash type (e.g. +:hash160+, +:raw+), or +nil+ if not RPuzzle

### `script_hash()` <a id="method-i-script_hash"></a> <a id="script_hash-instance_method"></a>
Extract the 20-byte script hash from a P2SH script.
- **@return** [String, nil] the script hash, or +nil+ if not P2SH

### `to_asm()` <a id="method-i-to_asm"></a> <a id="to_asm-instance_method"></a>
- **@return** [String] human-readable ASM representation

### `to_binary()` <a id="method-i-to_binary"></a> <a id="to_binary-instance_method"></a>
- **@return** [String] a copy of the raw script bytes

### `to_hex()` <a id="method-i-to_hex"></a> <a id="to_hex-instance_method"></a>
- **@return** [String] hex-encoded script

### `type()` <a id="method-i-type"></a> <a id="type-instance_method"></a>
Classify the script as a standard type.
- **@return** [String] one of +"empty"+, +"pubkeyhash"+, +"pubkey"+,
+"scripthash"+, +"nulldata"+, +"multisig"+, +"pushdrop"+,
+"rpuzzle"+, or +"nonstandard"+
