Skip to content

Instantly share code, notes, and snippets.

@JeremyRubin
Last active April 29, 2021 02:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JeremyRubin/d9f146475f53673cd03c26ab46492504 to your computer and use it in GitHub Desktop.
Save JeremyRubin/d9f146475f53673cd03c26ab46492504 to your computer and use it in GitHub Desktop.
Value Checking Opcodes

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment