address.py raw

   1  #!/usr/bin/env python3
   2  # Copyright (c) 2016-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  """Encode and decode Limenka addresses.
   6  
   7  - base58 P2PKH and P2SH addresses.
   8  - bech32 segwit v0 P2WPKH and P2WSH addresses.
   9  - bech32m segwit v1 P2TR addresses."""
  10  
  11  import enum
  12  import unittest
  13  
  14  from .script import (
  15      CScript,
  16      OP_0,
  17      OP_TRUE,
  18      hash160,
  19      hash256,
  20      sha256,
  21      taproot_construct,
  22  )
  23  from .util import assert_equal
  24  from test_framework.script_util import (
  25      keyhash_to_p2pkh_script,
  26      program_to_witness_script,
  27      scripthash_to_p2sh_script,
  28  )
  29  from test_framework.segwit_addr import (
  30      decode_segwit_address,
  31      encode_segwit_address,
  32  )
  33  
  34  
  35  ADDRESS_BCRT1_UNSPENDABLE = 'bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj'
  36  ADDRESS_BCRT1_UNSPENDABLE_DESCRIPTOR = 'addr(bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj)#juyq9d97'
  37  # Coins sent to this address can be spent with a witness stack of just OP_TRUE
  38  ADDRESS_BCRT1_P2WSH_OP_TRUE = 'bcrt1qft5p2uhsdcdc3l2ua4ap5qqfg4pjaqlp250x7us7a8qqhrxrxfsqseac85'
  39  
  40  
  41  class AddressType(enum.Enum):
  42      bech32 = 'bech32'
  43      p2sh_segwit = 'p2sh-segwit'
  44      legacy = 'legacy'  # P2PKH
  45  
  46  
  47  b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
  48  
  49  
  50  def create_deterministic_address_bcrt1_p2tr_op_true(explicit_internal_key=None):
  51      """
  52      Generates a deterministic bech32m address (segwit v1 output) that
  53      can be spent with a witness stack of OP_TRUE and the control block
  54      with internal public key (script-path spending).
  55  
  56      Returns a tuple with the generated address and the TaprootInfo object.
  57      """
  58      internal_key = explicit_internal_key or (1).to_bytes(32, 'big')
  59      taproot_info = taproot_construct(internal_key, [("only-path", CScript([OP_TRUE]))])
  60      address = output_key_to_p2tr(taproot_info.output_pubkey)
  61      if explicit_internal_key is None:
  62          assert_equal(address, 'bcrt1p9yfmy5h72durp7zrhlw9lf7jpwjgvwdg0jr0lqmmjtgg83266lqsekaqka')
  63      return (address, taproot_info)
  64  
  65  
  66  def byte_to_base58(b, version):
  67      result = ''
  68      b = bytes([version]) + b  # prepend version
  69      b += hash256(b)[:4]       # append checksum
  70      value = int.from_bytes(b, 'big')
  71      while value > 0:
  72          result = b58chars[value % 58] + result
  73          value //= 58
  74      while b[0] == 0:
  75          result = b58chars[0] + result
  76          b = b[1:]
  77      return result
  78  
  79  
  80  def base58_to_byte(s):
  81      """Converts a base58-encoded string to its data and version.
  82  
  83      Throws if the base58 checksum is invalid."""
  84      if not s:
  85          return b''
  86      n = 0
  87      for c in s:
  88          n *= 58
  89          assert c in b58chars
  90          digit = b58chars.index(c)
  91          n += digit
  92      h = '%x' % n
  93      if len(h) % 2:
  94          h = '0' + h
  95      res = n.to_bytes((n.bit_length() + 7) // 8, 'big')
  96      pad = 0
  97      for c in s:
  98          if c == b58chars[0]:
  99              pad += 1
 100          else:
 101              break
 102      res = b'\x00' * pad + res
 103  
 104      if hash256(res[:-4])[:4] != res[-4:]:
 105          raise ValueError('Invalid Base58Check checksum')
 106  
 107      return res[1:-4], int(res[0])
 108  
 109  
 110  def keyhash_to_p2pkh(hash, main=False):
 111      assert len(hash) == 20
 112      version = 0 if main else 111
 113      return byte_to_base58(hash, version)
 114  
 115  def scripthash_to_p2sh(hash, main=False):
 116      assert len(hash) == 20
 117      version = 5 if main else 196
 118      return byte_to_base58(hash, version)
 119  
 120  def key_to_p2pkh(key, main=False):
 121      key = check_key(key)
 122      return keyhash_to_p2pkh(hash160(key), main)
 123  
 124  def script_to_p2sh(script, main=False):
 125      script = check_script(script)
 126      return scripthash_to_p2sh(hash160(script), main)
 127  
 128  def key_to_p2sh_p2wpkh(key, main=False):
 129      key = check_key(key)
 130      p2shscript = CScript([OP_0, hash160(key)])
 131      return script_to_p2sh(p2shscript, main)
 132  
 133  def program_to_witness(version, program, main=False):
 134      if (type(program) is str):
 135          program = bytes.fromhex(program)
 136      assert 0 <= version <= 16
 137      assert 2 <= len(program) <= 40
 138      assert version > 0 or len(program) in [20, 32]
 139      return encode_segwit_address("bc" if main else "bcrt", version, program)
 140  
 141  def script_to_p2wsh(script, main=False):
 142      script = check_script(script)
 143      return program_to_witness(0, sha256(script), main)
 144  
 145  def key_to_p2wpkh(key, main=False):
 146      key = check_key(key)
 147      return program_to_witness(0, hash160(key), main)
 148  
 149  def script_to_p2sh_p2wsh(script, main=False):
 150      script = check_script(script)
 151      p2shscript = CScript([OP_0, sha256(script)])
 152      return script_to_p2sh(p2shscript, main)
 153  
 154  def output_key_to_p2tr(key, main=False):
 155      assert len(key) == 32
 156      return program_to_witness(1, key, main)
 157  
 158  def p2a(main=False):
 159      return program_to_witness(1, "4e73", main)
 160  
 161  def check_key(key):
 162      if (type(key) is str):
 163          key = bytes.fromhex(key)  # Assuming this is hex string
 164      if (type(key) is bytes and (len(key) == 33 or len(key) == 65)):
 165          return key
 166      assert False
 167  
 168  def check_script(script):
 169      if (type(script) is str):
 170          script = bytes.fromhex(script)  # Assuming this is hex string
 171      if (type(script) is bytes or type(script) is CScript):
 172          return script
 173      assert False
 174  
 175  
 176  def bech32_to_bytes(address):
 177      hrp = address.split('1')[0]
 178      if hrp not in ['bc', 'tb', 'bcrt']:
 179          return (None, None)
 180      version, payload = decode_segwit_address(hrp, address)
 181      if version is None:
 182          return (None, None)
 183      return version, bytearray(payload)
 184  
 185  
 186  def address_to_scriptpubkey(address):
 187      """Converts a given address to the corresponding output script (scriptPubKey)."""
 188      version, payload = bech32_to_bytes(address)
 189      if version is not None:
 190          return program_to_witness_script(version, payload) # testnet segwit scriptpubkey
 191      payload, version = base58_to_byte(address)
 192      if version == 111:  # testnet pubkey hash
 193          return keyhash_to_p2pkh_script(payload)
 194      elif version == 196:  # testnet script hash
 195          return scripthash_to_p2sh_script(payload)
 196      # TODO: also support other address formats
 197      else:
 198          assert False
 199  
 200  
 201  class TestFrameworkScript(unittest.TestCase):
 202      def test_base58encodedecode(self):
 203          def check_base58(data, version):
 204              self.assertEqual(base58_to_byte(byte_to_base58(data, version)), (data, version))
 205  
 206          check_base58(bytes.fromhex('1f8ea1702a7bd4941bca0941b852c4bbfedb2e05'), 111)
 207          check_base58(bytes.fromhex('3a0b05f4d7f66c3ba7009f453530296c845cc9cf'), 111)
 208          check_base58(bytes.fromhex('41c1eaf111802559bad61b60d62b1f897c63928a'), 111)
 209          check_base58(bytes.fromhex('0041c1eaf111802559bad61b60d62b1f897c63928a'), 111)
 210          check_base58(bytes.fromhex('000041c1eaf111802559bad61b60d62b1f897c63928a'), 111)
 211          check_base58(bytes.fromhex('00000041c1eaf111802559bad61b60d62b1f897c63928a'), 111)
 212          check_base58(bytes.fromhex('1f8ea1702a7bd4941bca0941b852c4bbfedb2e05'), 0)
 213          check_base58(bytes.fromhex('3a0b05f4d7f66c3ba7009f453530296c845cc9cf'), 0)
 214          check_base58(bytes.fromhex('41c1eaf111802559bad61b60d62b1f897c63928a'), 0)
 215          check_base58(bytes.fromhex('0041c1eaf111802559bad61b60d62b1f897c63928a'), 0)
 216          check_base58(bytes.fromhex('000041c1eaf111802559bad61b60d62b1f897c63928a'), 0)
 217          check_base58(bytes.fromhex('00000041c1eaf111802559bad61b60d62b1f897c63928a'), 0)
 218  
 219  
 220      def test_bech32_decode(self):
 221          def check_bech32_decode(payload, version):
 222              hrp = "tb"
 223              self.assertEqual(bech32_to_bytes(encode_segwit_address(hrp, version, payload)), (version, payload))
 224  
 225          check_bech32_decode(bytes.fromhex('36e3e2a33f328de12e4b43c515a75fba2632ecc3'), 0)
 226          check_bech32_decode(bytes.fromhex('823e9790fc1d1782321140d4f4aa61aabd5e045b'), 0)
 227          check_bech32_decode(bytes.fromhex('79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798'), 1)
 228          check_bech32_decode(bytes.fromhex('39cf8ebd95134f431c39db0220770bd127f5dd3cc103c988b7dcd577ae34e354'), 1)
 229          check_bech32_decode(bytes.fromhex('708244006d27c757f6f1fc6f853b6ec26268b727866f7ce632886e34eb5839a3'), 1)
 230          check_bech32_decode(bytes.fromhex('616211ab00dffe0adcb6ce258d6d3fd8cbd901e2'), 0)
 231          check_bech32_decode(bytes.fromhex('b6a7c98b482d7fb21c9fa8e65692a0890410ff22'), 0)
 232          check_bech32_decode(bytes.fromhex('f0c2109cb1008cfa7b5a09cc56f7267cd8e50929'), 0)
 233