Understanding Bitcoin script opcodes and their usage in the BSV TypeScript SDK.
OP codes (operation codes) are the fundamental building blocks of Bitcoin’s scripting language. They are operational instructions that manipulate data on the script execution stack to define the conditions under which Bitcoin can be spent. Each opcode performs a specific function such as arithmetic operations, logical comparisons, cryptographic operations, or stack manipulation.
Bitcoin scripts operate in a stack-based execution environment with two primary data structures:
Scripts consist of two parts:
The execution begins with the unlocking script placing data on the stack, followed by the locking script operating on the same stack. A spend is valid if the top of the stack contains a “true” value (non-zero) after execution.
The BSV TypeScript SDK provides comprehensive opcode support through the OP
object and script execution engine:
import { OP, Script, LockingScript, UnlockingScript } from '@bsv/sdk'
// Access opcodes by name
console.log(OP.OP_DUP) // 0x76
console.log(OP.OP_HASH160) // 0xa9
console.log(OP.OP_CHECKSIG) // 0xac
// Create scripts using opcodes
const lockingScript = new LockingScript([
{ op: OP.OP_DUP },
{ op: OP.OP_HASH160 },
{ op: 20, data: publicKeyHash }, // Push 20 bytes
{ op: OP.OP_EQUALVERIFY },
{ op: OP.OP_CHECKSIG }
])
// Create from ASM (Assembly) format
const script = Script.fromASM('OP_DUP OP_HASH160 ' + publicKeyHash + ' OP_EQUALVERIFY OP_CHECKSIG')
Push data onto the stack:
// Push small numbers (0-16)
OP.OP_0 // Push empty array (false)
OP.OP_1 // Push number 1 (true)
OP.OP_2 // Push number 2
// ... up to OP_16
// Push data of various sizes
OP.OP_PUSHDATA1 // Push up to 255 bytes
OP.OP_PUSHDATA2 // Push up to 65,535 bytes
OP.OP_PUSHDATA4 // Push up to 4,294,967,295 bytes
Manipulate stack contents:
OP.OP_DUP // Duplicate top stack item
OP.OP_DROP // Remove top stack item
OP.OP_SWAP // Swap top two stack items
OP.OP_ROT // Rotate top three stack items
OP.OP_2DUP // Duplicate top two stack items
OP.OP_TOALTSTACK // Move item to alt stack
Perform mathematical operations:
OP.OP_ADD // Add top two stack items
OP.OP_SUB // Subtract second from top
OP.OP_MUL // Multiply top two items
OP.OP_DIV // Divide second by top
OP.OP_MOD // Modulo operation
OP.OP_MIN // Return minimum of two values
OP.OP_MAX // Return maximum of two values
Compare values and return boolean results:
OP.OP_EQUAL // Check if top two items are equal
OP.OP_EQUALVERIFY // Equal check + verify (fail if false)
OP.OP_NUMEQUAL // Numeric equality check
OP.OP_LESSTHAN // Check if second < top
OP.OP_GREATERTHAN // Check if second > top
OP.OP_WITHIN // Check if value is within range
Perform cryptographic functions:
OP.OP_SHA1 // SHA-1 hash
OP.OP_SHA256 // SHA-256 hash
OP.OP_HASH160 // RIPEMD160(SHA256(x))
OP.OP_HASH256 // SHA256(SHA256(x))
OP.OP_CHECKSIG // Verify signature
OP.OP_CHECKMULTISIG // Verify multiple signatures
Control script execution:
OP.OP_IF // Conditional execution
OP.OP_ELSE // Alternative branch
OP.OP_ENDIF // End conditional
OP.OP_VERIFY // Fail if top stack item is false
OP.OP_RETURN // Mark output as unspendable
The most common Bitcoin script pattern:
// Locking script: OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
const p2pkhLock = new LockingScript([
{ op: OP.OP_DUP },
{ op: OP.OP_HASH160 },
{ op: 20, data: publicKeyHash },
{ op: OP.OP_EQUALVERIFY },
{ op: OP.OP_CHECKSIG }
])
// Unlocking script: <signature> <publicKey>
const p2pkhUnlock = new UnlockingScript([
{ op: signature.length, data: signature },
{ op: publicKey.length, data: publicKey }
])
Store arbitrary data on the blockchain:
// OP_RETURN <data>
const dataScript = new LockingScript([
{ op: OP.OP_RETURN },
{ op: data.length, data: data }
])
Require multiple signatures:
// 2-of-3 multisig: OP_2 <pubKey1> <pubKey2> <pubKey3> OP_3 OP_CHECKMULTISIG
const multisigScript = new LockingScript([
{ op: OP.OP_2 },
{ op: pubKey1.length, data: pubKey1 },
{ op: pubKey2.length, data: pubKey2 },
{ op: pubKey3.length, data: pubKey3 },
{ op: OP.OP_3 },
{ op: OP.OP_CHECKMULTISIG }
])
Some opcodes are disabled for security reasons:
// These opcodes will cause script failure
OP.OP_2MUL // Disabled: multiply by 2
OP.OP_2DIV // Disabled: divide by 2
OP.OP_VERIF // Disabled: conditional verification
OP.OP_VERNOTIF // Disabled: inverse conditional verification
Leverage SDK script templates for common patterns:
import { P2PKH, MultiSig, RPuzzle } from '@bsv/sdk'
// Use templates instead of manual opcode construction
const p2pkh = new P2PKH()
const lockingScript = p2pkh.lock(publicKeyHash)
Always validate scripts before use:
try {
const script = Script.fromASM(asmString)
// Script is valid
} catch (error) {
console.error('Invalid script:', error.message)
}
Implement proper error handling:
const spend = new Spend(params)
try {
while (!spend.isFinished()) {
if (!spend.step()) {
throw new Error(`Script execution failed: ${spend.getDebugString()}`)
}
}
} catch (error) {
console.error('Script execution error:', error.message)
}
Standard payment to public key hash:
const paymentScript = Script.fromASM(`OP_DUP OP_HASH160 ${pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG`)
Store application data on-chain:
const dataScript = Script.fromASM(`OP_RETURN ${Buffer.from(jsonData).toString('hex')}`)
Create conditional spending logic:
const contractScript = Script.fromASM(`
OP_IF
${timelock} OP_CHECKLOCKTIMEVERIFY OP_DROP
OP_DUP OP_HASH160 ${ownerHash} OP_EQUALVERIFY OP_CHECKSIG
OP_ELSE
OP_DUP OP_HASH160 ${beneficiaryHash} OP_EQUALVERIFY OP_CHECKSIG
OP_ENDIF
`)
Create cryptographic puzzles:
// Hash puzzle: provide preimage to unlock
const puzzleScript = Script.fromASM(`OP_HASH256 ${targetHash} OP_EQUAL`)
Monitor script execution step by step:
const spend = new Spend(params)
console.log('Initial stack:', spend.getDebugString())
while (!spend.isFinished()) {
const success = spend.step()
console.log('After step:', spend.getDebugString())
if (!success) {
console.log('Execution failed at:', spend.getCurrentOpcode())
break
}
}
Identify and fix common script issues:
// Check stack state
if (spend.getStackSize() === 0) {
console.log('Stack is empty - missing data?')
}
// Check for script errors
if (spend.hasError()) {
console.log('Script error:', spend.getErrorMessage())
}
// Verify final state
if (spend.isSuccess() && spend.getStackTop() !== 1) {
console.log('Script succeeded but top stack value is not true')
}
Understanding OP codes enables you to create sophisticated Bitcoin applications with custom spending conditions, smart contracts, and advanced cryptographic protocols using the BSV TypeScript SDK.