Implementation details
MCP contract template
Satisfier Args in main stack = subscript (SS), MP, A, CD, EFGH, IJKLMNOP, SS_ARGS*, DUMMY
Constraint Args in altstack = C_ARGS
SS_ARGS* being SS_ARG_N, …, SS_ARG_1, FunctionIndex?, C_ARG_N, …, C_ARG_1 (inversed order)
C_ARGS being C_ARG_N, …, C_ARG_1 (inversed order)
Note, that satisfier args contain the same copy of constraint args. And constraint args in the altstack are effectively ignored, not used in the template body
SS, MP, A, CD, EFGH, IJKLMNOP, SS_ARGS, DUMMY || C_ARGS
OP_DUP = Duplicates the subscript.
SS, SS, MP, A, CD, EFGH, IJKLMNOP, SS_ARGS, DUMMY || C_ARGS
OP_TOALTSTACK = Move the subscript to the altstack ready for execution later.
SS, MP, A, CD, EFGH, IJKLMNOP, SS_ARGS, DUMMY || SS, C_ARGS
OP_HASH160 = Generates the leaf hash of the script.
H(SS), MP, A, CD, EFGH, IJKLMNOP, SS_ARGS, DUMMY || SS, C_ARGS
OP_TOALTSTACK = Move the hash to altstack to prepare op_exec params.
MP, A, CD, EFGH, IJKLMNOP, SS_ARGS, DUMMY || H(SS), SS, C_ARGS
OP_PUSHDATA <LOOP_BYTECODE>
LOOP_BYTECODE, MP, A, CD, EFGH, IJKLMNOP, SS_ARGS, DUMMY || H(SS), SS, C_ARGS
OP_SWAP = move LOOP_BYTECODE behind MP.
MP, LOOP_BYTECODE, A, CD, EFGH, IJKLMNOP, SS_ARGS, DUMMY || H(SS), SS, C_ARGS
OP_ROT = rotate a to the top of the stack
A, MP, LOOP_BYTECODE, CD, EFGH, IJKLMNOP, SS_ARGS, DUMMY || H(SS), SS, C_ARGS
OP_FROMALTSTACK = Get H(SS) back from alt stack. we now have proper (inversed) order of arguments for OP_EXEC
H(SS), A, MP, LOOP_BYTECODE, CD, EFGH, IJKLMNOP, SS_ARGS, DUMMY || SS, C_ARGS
OP_3 = Add exec M_Params value of 3.
3, H(SS), A, MP, LOOP_BYTECODE, CD, EFGH, IJKLMNOP, SS_ARGS, DUMMY || SS, C_ARGS
OP_3 = Add exec M_Returns value of 3.
3, 3, H(SS), A, MP, LOOP_BYTECODE, CD, EFGH, IJKLMNOP, SS_ARGS, DUMMY || SS, C_ARGS
OP_EXEC - execute the repeated part, note the produced stack in the loop context
----- Start of repeated section ------
MP, A, H(SS) ||
OP_DUP OP_TOALTSTACK = duplicate the merkle path and stash its copy to altstack
MP, A, H(SS) || MP
OP_2 OP_MOD = Gets the remainder of the merkle_path divided by 2.
0, A, H(SS) || MP
OP_NOTIF OP_SWAP OP_ENDIF = if remainder is 1, swap A and H(SS), otherwise do nothing
A, H(SS) || MP
OP_CAT = Concatenate subscript hash and first Merkle proof hash.
AH(SS) || MP
OP_HASH160 = Hash the concatenated subscript hash and the first merkle proof hash.
AB || MP
OP_ACTIVEBYTECODE = Get the LOOP_BYTECODE
LOOP_BYTECODE, AB || MP
OP_SWAP = swap bytecode with merkle branch
AB, LOOP_BYTECODE || MP
OP_FROMALTSTACK = get back merkle path from alt stack
MP, AB, LOOP_BYTECODE ||
OP_2 OP_DIV = divide merkle path by two (substitute for OP_RSHIFT, which only works with BIGNUMs)
MP’, AB, LOOP_BYTECODE ||
OP_SWAP = swap MP` with AB to prepare the next OP_EXEC argument order (inversed)
AB, MP’, LOOP_BYTECODE ||
----- End of repeated section ------
AB, MP’, LOOP_BYTECODE, CD, EFGH, IJKLMNOP, SS_ARGS, DUMMY || SS
OP_3 OP_ROLL = move CD to the top of the stack
CD, AB, MP’, LOOP_BYTECODE, EFGH, IJKLMNOP, SS_ARGS, DUMMY || SS
OP_3 OP_3 = Add exec M_Params value of 3, Add exec M_Returns value of 3.
3, 3, CD, AB, MP’, LOOP_BYTECODE, EFGH, IJKLMNOP, SS_ARGS, DUMMY || SS
// same as above
OP_EXEC
OP_3 OP_ROLL OP_3 OP_3
3, 3, EFGH, ABCD, MP’, LOOP_BYTECODE, IJKLMNOP, SS_ARGS, DUMMY || SS, C_ARGS
// same as above
OP_EXEC
OP_3 OP_ROLL OP_3 OP_3
3, 3, IJKLMNOP, ABCDEFGH, MP’, LOOP_BYTECODE, SS_ARGS, DUMMY || SS, C_ARGS
// last exec
OP_EXEC
ABCDEFGHIJKLMNOP, MP’, LOOP_BYTECODE, SS_ARGS, DUMMY || SS, C_ARGS
OP_NIP OP_NIP = remove MP’ and LOOP_BYTECODE from the stack
ABCDEFGHIJKLMNOP, SS_ARGS, DUMMY || SS, C_ARGS
OP_PUSH20 = Push the predefined subscript hash to the stack.
<0x...>
= ABCDEFGHIJKLMNOP i.e. the Merkle root hash.
<0x...>
, ABCDEFGHIJKLMNOP, SS_ARGS, DUMMY || SS, C_ARGS
OP_EQUALVERIFY = Check if the generated Merkle root matches the defined Merkle root.
SS_ARGS, DUMMY || SS, C_ARGS
OP_FROMALTSTACK = Get subscript from altstack.
SS, SS_ARGS, DUMMY || C_ARGS
OP_1NEGATE = Push -1 to stack
<-1>
, SS, SS_ARGS, DUMMY || C_ARGS
OP_PLACE = place SS at DUMMY
SS, SS_ARGS, SS || C_ARGS
OP_DROP = Drop SS from top of the stack
SS_ARGS, SS || C_ARGS
OP_DEPTH = get stack depth (N_Params + 1)
<N_Params+1>
, SS_ARGS, SS || C_ARGS
OP_1SUB = get true N_Params value
<N_Params>
, SS_ARGS, SS || C_ARGS
OP_0 = Add exec M_Returns value of 0.
<0>
, <N_Params>
, SS_ARGS, SS || C_ARGS
OP_EXEC = Execute subscript.
Example
<0>
, <N_Params>
, SS_ARG_3, SS_ARG_2, SS_ARG_1, FunctionIndex, C_ARG_3, C_ARG_2, C_ARG_1, SS || C_ARGS
if evaluated left to right, they are “pushed” in their appearance order to SS stack
Produced stack in SS: C_ARG_1, C_ARG_2, C_ARG_3, FunctionIndex, SS_ARG_1, SS_ARG_2, SS_ARG_3
which is correct ordering - (C_ARGS, SS_ARGS needed, low to high)
Because of this pre-formed stack layout, unused arguments should be dropped first.
I.e., this means that a mast contract using “visible unused” constraints should be declared like following: contract A(int visible unused a, int b)…
instead of the required ordering for normal contracts:
contract A(int a, int visible unused b)…