script_util.py raw

   1  #!/usr/bin/env python3
   2  # Copyright (c) 2019-2022 The Limenka developers
   3  # Distributed under the MIT software license, see the accompanying
   4  # file COPYING or http://www.opensource.org/licenses/mit-license.php.
   5  """Useful Script constants and utils."""
   6  import unittest
   7  
   8  from test_framework.script import (
   9      CScript,
  10      OP_0,
  11      OP_1,
  12      OP_15,
  13      OP_16,
  14      OP_CHECKMULTISIG,
  15      OP_CHECKSIG,
  16      OP_DUP,
  17      OP_EQUAL,
  18      OP_EQUALVERIFY,
  19      OP_HASH160,
  20      OP_RETURN,
  21      hash160,
  22      sha256,
  23  )
  24  
  25  # Maximum number of potentially executed legacy signature operations in validating a transaction.
  26  MAX_STD_LEGACY_SIGOPS = 2_500
  27  
  28  # Maximum number of sigops per standard P2SH redeemScript.
  29  MAX_STD_P2SH_SIGOPS = 15
  30  
  31  # To prevent a "tx-size-small" policy rule error, a transaction has to have a
  32  # non-witness size of at least 65 bytes (MIN_STANDARD_TX_NONWITNESS_SIZE in
  33  # src/policy/policy.h). Considering a Tx with the smallest possible single
  34  # input (blank, empty scriptSig), and with an output omitting the scriptPubKey,
  35  # we get to a minimum size of 60 bytes:
  36  #
  37  # Tx Skeleton: 4 [Version] + 1 [InCount] + 1 [OutCount] + 4 [LockTime] = 10 bytes
  38  # Blank Input: 32 [PrevTxHash] + 4 [Index] + 1 [scriptSigLen] + 4 [SeqNo] = 41 bytes
  39  # Output:      8 [Amount] + 1 [scriptPubKeyLen] = 9 bytes
  40  #
  41  # Hence, the scriptPubKey of the single output has to have a size of at
  42  # least 5 bytes.
  43  MIN_STANDARD_TX_NONWITNESS_SIZE = 65
  44  MIN_PADDING = MIN_STANDARD_TX_NONWITNESS_SIZE - 10 - 41 - 9
  45  assert MIN_PADDING == 5
  46  
  47  # This script cannot be spent, allowing dust output values under
  48  # standardness checks
  49  DUMMY_MIN_OP_RETURN_SCRIPT = CScript([OP_RETURN] + ([OP_0] * (MIN_PADDING - 1)))
  50  assert len(DUMMY_MIN_OP_RETURN_SCRIPT) == MIN_PADDING
  51  
  52  PAY_TO_ANCHOR = CScript([OP_1, bytes.fromhex("4e73")])
  53  ANCHOR_ADDRESS = "bcrt1pfeesnyr2tx"
  54  
  55  def key_to_p2pk_script(key):
  56      key = check_key(key)
  57      return CScript([key, OP_CHECKSIG])
  58  
  59  
  60  def keys_to_multisig_script(keys, *, k=None):
  61      n = len(keys)
  62      if k is None:  # n-of-n multisig by default
  63          k = n
  64      assert k <= n
  65      checked_keys = [check_key(key) for key in keys]
  66      return CScript([k] + checked_keys + [n, OP_CHECKMULTISIG])
  67  
  68  
  69  def keyhash_to_p2pkh_script(hash):
  70      assert len(hash) == 20
  71      return CScript([OP_DUP, OP_HASH160, hash, OP_EQUALVERIFY, OP_CHECKSIG])
  72  
  73  
  74  def scripthash_to_p2sh_script(hash):
  75      assert len(hash) == 20
  76      return CScript([OP_HASH160, hash, OP_EQUAL])
  77  
  78  
  79  def key_to_p2pkh_script(key):
  80      key = check_key(key)
  81      return keyhash_to_p2pkh_script(hash160(key))
  82  
  83  
  84  def script_to_p2sh_script(script):
  85      script = check_script(script)
  86      return scripthash_to_p2sh_script(hash160(script))
  87  
  88  
  89  def key_to_p2sh_p2wpkh_script(key):
  90      key = check_key(key)
  91      p2shscript = CScript([OP_0, hash160(key)])
  92      return script_to_p2sh_script(p2shscript)
  93  
  94  
  95  def program_to_witness_script(version, program):
  96      if isinstance(program, str):
  97          program = bytes.fromhex(program)
  98      assert 0 <= version <= 16
  99      assert 2 <= len(program) <= 40
 100      assert version > 0 or len(program) in [20, 32]
 101      return CScript([version, program])
 102  
 103  
 104  def script_to_p2wsh_script(script):
 105      script = check_script(script)
 106      return program_to_witness_script(0, sha256(script))
 107  
 108  
 109  def key_to_p2wpkh_script(key):
 110      key = check_key(key)
 111      return program_to_witness_script(0, hash160(key))
 112  
 113  
 114  def script_to_p2sh_p2wsh_script(script):
 115      script = check_script(script)
 116      p2shscript = CScript([OP_0, sha256(script)])
 117      return script_to_p2sh_script(p2shscript)
 118  
 119  
 120  def output_key_to_p2tr_script(key):
 121      assert len(key) == 32
 122      return program_to_witness_script(1, key)
 123  
 124  
 125  def check_key(key):
 126      if isinstance(key, str):
 127          key = bytes.fromhex(key)  # Assuming this is hex string
 128      if isinstance(key, bytes) and (len(key) == 33 or len(key) == 65):
 129          return key
 130      assert False
 131  
 132  
 133  def check_script(script):
 134      if isinstance(script, str):
 135          script = bytes.fromhex(script)  # Assuming this is hex string
 136      if isinstance(script, bytes) or isinstance(script, CScript):
 137          return script
 138      assert False
 139  
 140  
 141  class TestFrameworkScriptUtil(unittest.TestCase):
 142      def test_multisig(self):
 143          fake_pubkey = bytes([0]*33)
 144          # check correct encoding of P2MS script with n,k <= 16
 145          normal_ms_script = keys_to_multisig_script([fake_pubkey]*16, k=15)
 146          self.assertEqual(len(normal_ms_script), 1 + 16*34 + 1 + 1)
 147          self.assertTrue(normal_ms_script.startswith(bytes([OP_15])))
 148          self.assertTrue(normal_ms_script.endswith(bytes([OP_16, OP_CHECKMULTISIG])))
 149  
 150          # check correct encoding of P2MS script with n,k > 16
 151          max_ms_script = keys_to_multisig_script([fake_pubkey]*20, k=19)
 152          self.assertEqual(len(max_ms_script), 2 + 20*34 + 2 + 1)
 153          self.assertTrue(max_ms_script.startswith(bytes([1, 19])))  # using OP_PUSH1
 154          self.assertTrue(max_ms_script.endswith(bytes([1, 20, OP_CHECKMULTISIG])))
 155