Value Checking with OP_CHECKTEMPLATEVERIFY:
Here we propose defining semantics with OP_CHECKTEMPLATEVERIFY for 2 items on the stack
First some constants:
case 0: case 1: case 2: case 3: case 4: case 5:
int64_t a = CScriptNum(stacktop(-1));
bool a_is_sats = a >= 0;
uint8_t TXN_MASK = 1<<19; // 1<<ceil(log2(488944 = (1+21000000*100000000)>>(4*8)))
uint8_t FEE_MASK = 1<<20;
if (a_is_sats) {
checker.CheckOutputAmount(a)
} else {
// -a fits in int64_t since int64_t is wider than 4 bytes
a = -a;
bool check_tx_amount = a & BTC_MASK;
bool check_fee_amount = a & FEE_MASK;
// todo: require a valid combo of flags?
a = a & ~(BTC_MASK | FEE_MASK);
a = a << (4*8);
// todo: either get flags from b or require b > 0
int64_t b = CScriptNum(stacktop(-2));
if (check_fee_amount) {
checker.CheckFeeAmount(a+b);
} else if (check_tx_amount) {
checker.CheckTransactionAmount(a+b);
} else {
checker.CheckOutputAmount(a+b)
}
}
The CheckAmount checkers verify that field * is an exact match for the value passed in. Since 5 byte CScriptNum is allowed, this means up to (2**((58) -1)-1)/100e6 = 5497.55813887 Bitcoin may be output amount checked without requiring a second byte argument. In normal cases (e.g., for the miniscript fragment below) this amount limit is 21.47483647.Transaction or Fee amount checking, less common operations, always require a 2 byte verification.
This type of amount checker can be used with scripts like:
Check static value:program: <x> OP_CHECKTEMPLATEVERIFY
Check value within range:
witness stack: [<x>]
program: <min> <max> OP_WITHIN OP_CHECKTEMPLATEVERIFY
Check at least min:
witness stack: [<x>]
program: <min> OP_MAX OP_CHECKTEMPLATEVERIFY
Check at most max:
witness stack: [<x>]
program: <max> OP_MIN OP_CHECKTEMPLATEVERIFY
There are similar (more complex) examples for transactions with more than than 21 BTC. E.g. Check at most max (unoptimized, not closely checked):
witness stack: [<lt_21_part>, <gt_21_part>]program:
<max gt_21_part> OP_DUP TOALTSTACK OP_MIN // Set value to smaller
DUP FROMALTSTACK OP_NUMEQUAL
OP_IF SWAP
<max lt_21_part> OP_MIN SWAP
OP_ENDIF
NEGATE OP_CHECKTEMPLATEVERIFY
Flags can be set within program via addition.
Alternative Approaches:
- OP_SUCCESSX: Would be super simple (just push value!) but CScriptNum is limited to 5 bytes (wah), would probably want OP_UPGRADEMATH or something or a leaf version
- Picking a different range for single arg version than 21 BTC to 1 sat.
- More explicit version byte + separate data push version