Script Opcodes & Macros¶
In addition to Bitcoin's native opcodes,
Minsc also ships with some built-in composite opcodes for common multi-opcode operations,
as well as function macros that generate dynamic Script.
Also see:
Composite Opcodes¶
Minsc's custom opcodes are prefixed with
OP::, to differentiate them from native opcodes.
Verify that the two top stack elements are not equal
stack in: <a> <b> stack out: (none) (or fail)
OP::MINIMAL_BOOL
Script
¶
OP::MINIMAL_BOOL = `OP_DUP OP_SIZE OP_EQUALVERIFY`
Verify that the top stack element is exactly 1 or 0
(0x01 or the empty byte-array 0x)
stack in: <n> stack out: <n> (or fail)
OP::PICK_BOTTOM
Script
¶
OP::PICK_BOTTOM = `OP_TOALTSTACK OP_DEPTH OP_FROMALTSTACK OP_SUB OP_PICK`
PICK (copy) from the bottom of the stack (1 for the bottom-most stack element, not 0)
OP::ROLL_BOTTOM
Script
¶
OP::ROLL_BOTTOM = `OP_TOALTSTACK OP_DEPTH OP_FROMALTSTACK OP_SUB OP_ROLL`
ROLL (move) from the bottom of the stack (1 for the bottom-most stack element, not 0)
Shift the bottom-most stack element to the top of the stack
Sort the top 2 stack elements (higher placed on top)
OP::𝘯MUL
¶
Minsc source code: src/stdlib/btc.minsc:143
Script Macros¶
Loop Unrolling¶
Run the body script as long as the condition is met, up to max_iterations times.
If max_iterations is reached but the condition is still met, execution fails.
The body can be a Script or a Function that takes the iteration count and returns a Script.
For example, this will count from <num> down to 0 (for numbers up to 10):
`<num> unrollLoop(10, `OP_DUP 0 OP_GREATERTHANOREQUAL`, OP_1SUB)`
`
<5>
OP_DUP
<0>
OP_GREATERTHANOREQUAL
OP_IF
OP_1SUB
OP_DUP
<0>
OP_GREATERTHANOREQUAL
OP_IF
OP_1SUB
OP_DUP
<0>
OP_GREATERTHANOREQUAL
OP_IF
OP_1SUB
OP_DUP
<0>
OP_GREATERTHANOREQUAL
OP_IF
OP_1SUB
OP_DUP
<0>
OP_GREATERTHANOREQUAL
OP_IF
OP_1SUB
OP_DUP
<0>
OP_GREATERTHANOREQUAL
OP_IF
OP_1SUB
OP_DUP
<0>
OP_GREATERTHANOREQUAL
OP_IF
OP_1SUB
OP_DUP
<0>
OP_GREATERTHANOREQUAL
OP_IF
OP_1SUB
OP_DUP
<0>
OP_GREATERTHANOREQUAL
OP_IF
OP_1SUB
OP_DUP
<0>
OP_GREATERTHANOREQUAL
OP_IF
OP_1SUB
OP_DUP
<0>
OP_GREATERTHANOREQUAL
OP_NOT
OP_VERIFY
OP_ENDIF
OP_ENDIF
OP_ENDIF
OP_ENDIF
OP_ENDIF
OP_ENDIF
OP_ENDIF
OP_ENDIF
OP_ENDIF
OP_ENDIF
`
A more advanced example that sums the total inputs amount using the Liquid introspection opcodes is available here.
Minsc source code: src/stdlib/btc.minsc:161
Pop the top stack element as the number of times to run the script body, up to max_iterations.
For example, this will sum the numbers between 1 and <num> (for numbers up to 10):
`
<0>
<5>
OP_DUP
OP_0NOTEQUAL
OP_IF
OP_DUP
OP_ROT
OP_ADD
OP_SWAP
OP_1SUB
OP_DUP
OP_0NOTEQUAL
OP_IF
OP_DUP
OP_ROT
OP_ADD
OP_SWAP
OP_1SUB
OP_DUP
OP_0NOTEQUAL
OP_IF
OP_DUP
OP_ROT
OP_ADD
OP_SWAP
OP_1SUB
OP_DUP
OP_0NOTEQUAL
OP_IF
OP_DUP
OP_ROT
OP_ADD
OP_SWAP
OP_1SUB
OP_DUP
OP_0NOTEQUAL
OP_IF
OP_DUP
OP_ROT
OP_ADD
OP_SWAP
OP_1SUB
OP_DUP
OP_0NOTEQUAL
OP_IF
OP_DUP
OP_ROT
OP_ADD
OP_SWAP
OP_1SUB
OP_DUP
OP_0NOTEQUAL
OP_IF
OP_DUP
OP_ROT
OP_ADD
OP_SWAP
OP_1SUB
OP_DUP
OP_0NOTEQUAL
OP_IF
OP_DUP
OP_ROT
OP_ADD
OP_SWAP
OP_1SUB
OP_DUP
OP_0NOTEQUAL
OP_IF
OP_DUP
OP_ROT
OP_ADD
OP_SWAP
OP_1SUB
OP_DUP
OP_0NOTEQUAL
OP_IF
OP_DUP
OP_ROT
OP_ADD
OP_SWAP
OP_1SUB
OP_DUP
OP_0NOTEQUAL
OP_NOT
OP_VERIFY
OP_ENDIF
OP_ENDIF
OP_ENDIF
OP_ENDIF
OP_ENDIF
OP_ENDIF
OP_ENDIF
OP_ENDIF
OP_ENDIF
OP_ENDIF
OP_DROP
`
Minsc source code: src/stdlib/btc.minsc:173
Control Structures¶
Check a series of nested OP_IF..OP_ELSE conditions, running the script associated with the first
match or the optional default branch. If there's no match and no default, execution fails.
Given an array of clauses, where each clause is a tuple of the Script checking the condition (or default), plus the Script to execute as the clause body.
For example, to match the number on the top of the stack and run some code accordingly:
`
OP_DUP
<0>
OP_EQUAL
OP_IF
OP_DROP
<0x022a46eb3296a185c5887405fe4ff8ff2cb5003980c624b173520cacb37e958e3d>
OP_CHECKSIG
OP_ELSE
OP_DUP
<1>
OP_EQUAL
OP_IF
OP_DROP
<0x627840>
OP_CHECKSEQUENCEVERIFY
OP_ELSE
OP_DROP
<0x034d14a48fa6c307d9c79e8a7c3164c6cb6279d2369cae32b4e1deebbb3c0b971c>
OP_CHECKSIG
OP_ENDIF
OP_ENDIF
`
Minsc source code: src/stdlib/btc.minsc:243
Match the element at the top of the stack against the clauses and run the first match,
keeping the item being matched around as necessary (without requiring manual OP_DUP/OP_DROP).
If there's no match and no default, execution fails.
For example, this is equivalent to the ifelseif example above:
match[
`0 OP_EQUAL`: `$alice OP_CHECKSIG`,
`1 OP_EQUAL`: `<6 months> OP_CSV`,
default: `OP_DROP $bob OP_CHECKSIG`,
]
`
OP_DUP
<0>
OP_EQUAL
OP_IF
OP_DROP
<0x022a46eb3296a185c5887405fe4ff8ff2cb5003980c624b173520cacb37e958e3d>
OP_CHECKSIG
OP_ELSE
OP_DUP
<1>
OP_EQUAL
OP_IF
OP_DROP
<0x627840>
OP_CHECKSEQUENCEVERIFY
OP_ELSE
OP_DROP
<0x034d14a48fa6c307d9c79e8a7c3164c6cb6279d2369cae32b4e1deebbb3c0b971c>
OP_CHECKSIG
OP_ENDIF
OP_ENDIF
`
Minsc source code: src/stdlib/btc.minsc:260
Match the item at the top of the stack for equality.
Given an array of clauses, where each clause is a tuple of the Value to match against
(any Bytes-coercible) or default, plus the Script to execute as the clause body.
For example, this is equvalent to the ifelseif/match examples above:
switch[
0: `$alice OP_CHECKSIG`,
1: `<6 months> OP_CSV`,
default: `OP_DROP $bob OP_CHECKSIG`,
]
`
OP_DUP
<0>
OP_EQUAL
OP_IF
OP_DROP
<0x022a46eb3296a185c5887405fe4ff8ff2cb5003980c624b173520cacb37e958e3d>
OP_CHECKSIG
OP_ELSE
OP_DUP
<1>
OP_EQUAL
OP_IF
OP_DROP
<0x627840>
OP_CHECKSEQUENCEVERIFY
OP_ELSE
OP_DROP
<0x034d14a48fa6c307d9c79e8a7c3164c6cb6279d2369cae32b4e1deebbb3c0b971c>
OP_CHECKSIG
OP_ENDIF
OP_ENDIF
`
Minsc source code: src/stdlib/btc.minsc:273
Pop an index number off the stack and execute the scripts branch with that index.
Does not support a default branch.
This can typically be better accomplished with separate Taproot leaves for the script branches, but branching within a single Script may be preferable in some cases.
For example:
select[ `$alice OP_CHECKSIG`, `<6 months> OP_CSV` ]
`OP_IF <0x627840> OP_CHECKSEQUENCEVERIFY OP_ELSE <0x022a46eb3296a185c5887405fe4ff8ff2cb5003980c624b173520cacb37e958e3d> OP_CHECKSIG OP_ENDIF`
Optimized to a simple
OP_IF..OP_ELSE..OP_ENDIFwhen there are only 2 branches. This relies onMINIMALIFfor non-malleability, which is consensus-enforced in Taproot but only a standardness policy for Segwitv0/P2SH, whereOP::MINIMAL_BOOLis required to ensure non-malleability.
Minsc source code: src/stdlib/btc.minsc:283
(Alt)Stack Manipulation¶
PICK (copy) from the bottom of the stack, for statically known depth (1 for the bottom-most stack element, not 0)
Like OP::PICK_BOTTOM, but more weight-efficient when the depth is static.
Minsc source code: src/stdlib/btc.minsc:124
ROLL (move) from the bottom of the stack, for statically known depth (1 for the bottom-most stack element, not 0)
Like OP::ROLL_BOTTOM, but more weight-efficient when the depth is static.
Minsc source code: src/stdlib/btc.minsc:123
PICK (copy) from the altstack, for statically known depth
Minsc source code: src/stdlib/btc.minsc:190
ROLL (move) from the altstack, for statically known depth
Minsc source code: src/stdlib/btc.minsc:186
PICK (copy) from the altstack, using the number at the top of the stack as the depth (loop unrolled up to max_depth)
Minsc source code: src/stdlib/btc.minsc:203
ROLL (move) from the altstack, using the number at the top of the stack as the depth (loop unrolled up to max_elements)
Minsc source code: src/stdlib/btc.minsc:197
Send multiple stack elements the the altstack, using the number at the top of the stack as the number of elements to send (loop unrolled up to max_elements). nFromAlt() can be used to bring them back.
stack in: <el1> <el2> .. <elN> <N total> stack out: (none)
altstack out: <elN> .. <el2> <el1> <N total>
When the number of elements to send is static, this can be done much more efficiently using simple Script repetition, e.g.
OP_TOALTSTACK*5
Minsc source code: src/stdlib/btc.minsc:213
Bring back multiple stack elements from the altstack, using the number at the top of the altstack as the number of elements to bring (loop unrolled up to max_elements). The opposite of nToAlt().
altstack in: <elN> .. <el2> <el1> <N total> altstack out: (none)
stack out: <el1> <el2> .. <elN> <N total>
When the number of elements to bring is static, this can be done much more efficiently using simple Script repetition, e.g.
OP_FROMALTSTACK*5
Minsc source code: src/stdlib/btc.minsc:223
Drop num_elements from the stack, using the minimal number of OP_2DROP/OP_DROP's
Minsc source code: src/stdlib/btc.minsc:140
Drop num_elements from the altstack
Minsc source code: src/stdlib/btc.minsc:141
Native Opcodes¶
OP_PUSHNUM_1-OP_PUSHNUM_16
OP_PUSHBYTES_0-OP_PUSHBYTES_75
OP_PUSHDATA1, OP_PUSHDATA2, OP_PUSHDATA4
Alias of: OP_PUSHBYTES_0
Aliased as: OP_CHECKSEQUENCEVERIFY
OP_NOP1-OP_NOP10
OP_RETURN_187-OP_RETURN_254
Disabled Opcodes¶
Proposed Opcodes¶
Aliased as: OP_CHECKTEMPLATEVERIFY
Also see: the CTV reference