1# -*- coding: utf-8 -*-
2#
3# Electrum - lightweight Bitcoin client
4# Copyright (C) 2011 thomasv@gitorious
5#
6# Permission is hereby granted, free of charge, to any person
7# obtaining a copy of this software and associated documentation files
8# (the "Software"), to deal in the Software without restriction,
9# including without limitation the rights to use, copy, modify, merge,
10# publish, distribute, sublicense, and/or sell copies of the Software,
11# and to permit persons to whom the Software is furnished to do so,
12# subject to the following conditions:
13#
14# The above copyright notice and this permission notice shall be
15# included in all copies or substantial portions of the Software.
16#
17# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24# SOFTWARE.
25
26import hashlib
27from typing import List, Tuple, TYPE_CHECKING, Optional, Union, Sequence
28import enum
29from enum import IntEnum, Enum
30
31from .util import bfh, bh2u, BitcoinException, assert_bytes, to_bytes, inv_dict, is_hex_str
32from . import version
33from . import segwit_addr
34from . import constants
35from . import ecc
36from .crypto import sha256d, sha256, hash_160, hmac_oneshot
37
38if TYPE_CHECKING:
39    from .network import Network
40
41
42################################## transactions
43
44COINBASE_MATURITY = 100
45COIN = 100000000
46TOTAL_COIN_SUPPLY_LIMIT_IN_BTC = 21000000
47
48NLOCKTIME_MIN = 0
49NLOCKTIME_BLOCKHEIGHT_MAX = 500_000_000 - 1
50NLOCKTIME_MAX = 2 ** 32 - 1
51
52# supported types of transaction outputs
53# TODO kill these with fire
54TYPE_ADDRESS = 0
55TYPE_PUBKEY  = 1
56TYPE_SCRIPT  = 2
57
58
59class opcodes(IntEnum):
60    # push value
61    OP_0 = 0x00
62    OP_FALSE = OP_0
63    OP_PUSHDATA1 = 0x4c
64    OP_PUSHDATA2 = 0x4d
65    OP_PUSHDATA4 = 0x4e
66    OP_1NEGATE = 0x4f
67    OP_RESERVED = 0x50
68    OP_1 = 0x51
69    OP_TRUE = OP_1
70    OP_2 = 0x52
71    OP_3 = 0x53
72    OP_4 = 0x54
73    OP_5 = 0x55
74    OP_6 = 0x56
75    OP_7 = 0x57
76    OP_8 = 0x58
77    OP_9 = 0x59
78    OP_10 = 0x5a
79    OP_11 = 0x5b
80    OP_12 = 0x5c
81    OP_13 = 0x5d
82    OP_14 = 0x5e
83    OP_15 = 0x5f
84    OP_16 = 0x60
85
86    # control
87    OP_NOP = 0x61
88    OP_VER = 0x62
89    OP_IF = 0x63
90    OP_NOTIF = 0x64
91    OP_VERIF = 0x65
92    OP_VERNOTIF = 0x66
93    OP_ELSE = 0x67
94    OP_ENDIF = 0x68
95    OP_VERIFY = 0x69
96    OP_RETURN = 0x6a
97
98    # stack ops
99    OP_TOALTSTACK = 0x6b
100    OP_FROMALTSTACK = 0x6c
101    OP_2DROP = 0x6d
102    OP_2DUP = 0x6e
103    OP_3DUP = 0x6f
104    OP_2OVER = 0x70
105    OP_2ROT = 0x71
106    OP_2SWAP = 0x72
107    OP_IFDUP = 0x73
108    OP_DEPTH = 0x74
109    OP_DROP = 0x75
110    OP_DUP = 0x76
111    OP_NIP = 0x77
112    OP_OVER = 0x78
113    OP_PICK = 0x79
114    OP_ROLL = 0x7a
115    OP_ROT = 0x7b
116    OP_SWAP = 0x7c
117    OP_TUCK = 0x7d
118
119    # splice ops
120    OP_CAT = 0x7e
121    OP_SUBSTR = 0x7f
122    OP_LEFT = 0x80
123    OP_RIGHT = 0x81
124    OP_SIZE = 0x82
125
126    # bit logic
127    OP_INVERT = 0x83
128    OP_AND = 0x84
129    OP_OR = 0x85
130    OP_XOR = 0x86
131    OP_EQUAL = 0x87
132    OP_EQUALVERIFY = 0x88
133    OP_RESERVED1 = 0x89
134    OP_RESERVED2 = 0x8a
135
136    # numeric
137    OP_1ADD = 0x8b
138    OP_1SUB = 0x8c
139    OP_2MUL = 0x8d
140    OP_2DIV = 0x8e
141    OP_NEGATE = 0x8f
142    OP_ABS = 0x90
143    OP_NOT = 0x91
144    OP_0NOTEQUAL = 0x92
145
146    OP_ADD = 0x93
147    OP_SUB = 0x94
148    OP_MUL = 0x95
149    OP_DIV = 0x96
150    OP_MOD = 0x97
151    OP_LSHIFT = 0x98
152    OP_RSHIFT = 0x99
153
154    OP_BOOLAND = 0x9a
155    OP_BOOLOR = 0x9b
156    OP_NUMEQUAL = 0x9c
157    OP_NUMEQUALVERIFY = 0x9d
158    OP_NUMNOTEQUAL = 0x9e
159    OP_LESSTHAN = 0x9f
160    OP_GREATERTHAN = 0xa0
161    OP_LESSTHANOREQUAL = 0xa1
162    OP_GREATERTHANOREQUAL = 0xa2
163    OP_MIN = 0xa3
164    OP_MAX = 0xa4
165
166    OP_WITHIN = 0xa5
167
168    # crypto
169    OP_RIPEMD160 = 0xa6
170    OP_SHA1 = 0xa7
171    OP_SHA256 = 0xa8
172    OP_HASH160 = 0xa9
173    OP_HASH256 = 0xaa
174    OP_CODESEPARATOR = 0xab
175    OP_CHECKSIG = 0xac
176    OP_CHECKSIGVERIFY = 0xad
177    OP_CHECKMULTISIG = 0xae
178    OP_CHECKMULTISIGVERIFY = 0xaf
179
180    # expansion
181    OP_NOP1 = 0xb0
182    OP_CHECKLOCKTIMEVERIFY = 0xb1
183    OP_NOP2 = OP_CHECKLOCKTIMEVERIFY
184    OP_CHECKSEQUENCEVERIFY = 0xb2
185    OP_NOP3 = OP_CHECKSEQUENCEVERIFY
186    OP_NOP4 = 0xb3
187    OP_NOP5 = 0xb4
188    OP_NOP6 = 0xb5
189    OP_NOP7 = 0xb6
190    OP_NOP8 = 0xb7
191    OP_NOP9 = 0xb8
192    OP_NOP10 = 0xb9
193
194    OP_INVALIDOPCODE = 0xff
195
196    def hex(self) -> str:
197        return bytes([self]).hex()
198
199
200def rev_hex(s: str) -> str:
201    return bh2u(bfh(s)[::-1])
202
203
204def int_to_hex(i: int, length: int=1) -> str:
205    """Converts int to little-endian hex string.
206    `length` is the number of bytes available
207    """
208    if not isinstance(i, int):
209        raise TypeError('{} instead of int'.format(i))
210    range_size = pow(256, length)
211    if i < -(range_size//2) or i >= range_size:
212        raise OverflowError('cannot convert int {} to hex ({} bytes)'.format(i, length))
213    if i < 0:
214        # two's complement
215        i = range_size + i
216    s = hex(i)[2:].rstrip('L')
217    s = "0"*(2*length - len(s)) + s
218    return rev_hex(s)
219
220def script_num_to_hex(i: int) -> str:
221    """See CScriptNum in Bitcoin Core.
222    Encodes an integer as hex, to be used in script.
223
224    ported from https://github.com/bitcoin/bitcoin/blob/8cbc5c4be4be22aca228074f087a374a7ec38be8/src/script/script.h#L326
225    """
226    if i == 0:
227        return ''
228
229    result = bytearray()
230    neg = i < 0
231    absvalue = abs(i)
232    while absvalue > 0:
233        result.append(absvalue & 0xff)
234        absvalue >>= 8
235
236    if result[-1] & 0x80:
237        result.append(0x80 if neg else 0x00)
238    elif neg:
239        result[-1] |= 0x80
240
241    return bh2u(result)
242
243
244def var_int(i: int) -> str:
245    # https://en.bitcoin.it/wiki/Protocol_specification#Variable_length_integer
246    # https://github.com/bitcoin/bitcoin/blob/efe1ee0d8d7f82150789f1f6840f139289628a2b/src/serialize.h#L247
247    # "CompactSize"
248    assert i >= 0, i
249    if i<0xfd:
250        return int_to_hex(i)
251    elif i<=0xffff:
252        return "fd"+int_to_hex(i,2)
253    elif i<=0xffffffff:
254        return "fe"+int_to_hex(i,4)
255    else:
256        return "ff"+int_to_hex(i,8)
257
258
259def witness_push(item: str) -> str:
260    """Returns data in the form it should be present in the witness.
261    hex -> hex
262    """
263    return var_int(len(item) // 2) + item
264
265
266def _op_push(i: int) -> str:
267    if i < opcodes.OP_PUSHDATA1:
268        return int_to_hex(i)
269    elif i <= 0xff:
270        return opcodes.OP_PUSHDATA1.hex() + int_to_hex(i, 1)
271    elif i <= 0xffff:
272        return opcodes.OP_PUSHDATA2.hex() + int_to_hex(i, 2)
273    else:
274        return opcodes.OP_PUSHDATA4.hex() + int_to_hex(i, 4)
275
276
277def push_script(data: str) -> str:
278    """Returns pushed data to the script, automatically
279    choosing canonical opcodes depending on the length of the data.
280    hex -> hex
281
282    ported from https://github.com/btcsuite/btcd/blob/fdc2bc867bda6b351191b5872d2da8270df00d13/txscript/scriptbuilder.go#L128
283    """
284    data = bfh(data)
285    data_len = len(data)
286
287    # "small integer" opcodes
288    if data_len == 0 or data_len == 1 and data[0] == 0:
289        return opcodes.OP_0.hex()
290    elif data_len == 1 and data[0] <= 16:
291        return bh2u(bytes([opcodes.OP_1 - 1 + data[0]]))
292    elif data_len == 1 and data[0] == 0x81:
293        return opcodes.OP_1NEGATE.hex()
294
295    return _op_push(data_len) + bh2u(data)
296
297
298def make_op_return(x:bytes) -> bytes:
299    return bytes([opcodes.OP_RETURN]) + bytes.fromhex(push_script(x.hex()))
300
301
302def add_number_to_script(i: int) -> bytes:
303    return bfh(push_script(script_num_to_hex(i)))
304
305
306def construct_witness(items: Sequence[Union[str, int, bytes]]) -> str:
307    """Constructs a witness from the given stack items."""
308    witness = var_int(len(items))
309    for item in items:
310        if type(item) is int:
311            item = script_num_to_hex(item)
312        elif isinstance(item, (bytes, bytearray)):
313            item = bh2u(item)
314        else:
315            assert is_hex_str(item)
316        witness += witness_push(item)
317    return witness
318
319
320def construct_script(items: Sequence[Union[str, int, bytes, opcodes]]) -> str:
321    """Constructs bitcoin script from given items."""
322    script = ''
323    for item in items:
324        if isinstance(item, opcodes):
325            script += item.hex()
326        elif type(item) is int:
327            script += add_number_to_script(item).hex()
328        elif isinstance(item, (bytes, bytearray)):
329            script += push_script(item.hex())
330        elif isinstance(item, str):
331            assert is_hex_str(item)
332            script += push_script(item)
333        else:
334            raise Exception(f'unexpected item for script: {item!r}')
335    return script
336
337
338def relayfee(network: 'Network' = None) -> int:
339    """Returns feerate in sat/kbyte."""
340    from .simple_config import FEERATE_DEFAULT_RELAY, FEERATE_MAX_RELAY
341    if network and network.relay_fee is not None:
342        fee = network.relay_fee
343    else:
344        fee = FEERATE_DEFAULT_RELAY
345    # sanity safeguards, as network.relay_fee is coming from a server:
346    fee = min(fee, FEERATE_MAX_RELAY)
347    fee = max(fee, FEERATE_DEFAULT_RELAY)
348    return fee
349
350
351# see https://github.com/bitcoin/bitcoin/blob/a62f0ed64f8bbbdfe6467ac5ce92ef5b5222d1bd/src/policy/policy.cpp#L14
352DUST_LIMIT_DEFAULT_SAT_LEGACY = 546
353DUST_LIMIT_DEFAULT_SAT_SEGWIT = 294
354
355
356def dust_threshold(network: 'Network' = None) -> int:
357    """Returns the dust limit in satoshis."""
358    # Change <= dust threshold is added to the tx fee
359    dust_lim = 182 * 3 * relayfee(network)  # in msat
360    # convert to sat, but round up:
361    return (dust_lim // 1000) + (dust_lim % 1000 > 0)
362
363
364def hash_encode(x: bytes) -> str:
365    return bh2u(x[::-1])
366
367
368def hash_decode(x: str) -> bytes:
369    return bfh(x)[::-1]
370
371
372############ functions from pywallet #####################
373
374def hash160_to_b58_address(h160: bytes, addrtype: int) -> str:
375    s = bytes([addrtype]) + h160
376    s = s + sha256d(s)[0:4]
377    return base_encode(s, base=58)
378
379
380def b58_address_to_hash160(addr: str) -> Tuple[int, bytes]:
381    addr = to_bytes(addr, 'ascii')
382    _bytes = DecodeBase58Check(addr)
383    if len(_bytes) != 21:
384        raise Exception(f'expected 21 payload bytes in base58 address. got: {len(_bytes)}')
385    return _bytes[0], _bytes[1:21]
386
387
388def hash160_to_p2pkh(h160: bytes, *, net=None) -> str:
389    if net is None: net = constants.net
390    return hash160_to_b58_address(h160, net.ADDRTYPE_P2PKH)
391
392def hash160_to_p2sh(h160: bytes, *, net=None) -> str:
393    if net is None: net = constants.net
394    return hash160_to_b58_address(h160, net.ADDRTYPE_P2SH)
395
396def public_key_to_p2pkh(public_key: bytes, *, net=None) -> str:
397    if net is None: net = constants.net
398    return hash160_to_p2pkh(hash_160(public_key), net=net)
399
400def hash_to_segwit_addr(h: bytes, witver: int, *, net=None) -> str:
401    if net is None: net = constants.net
402    addr = segwit_addr.encode_segwit_address(net.SEGWIT_HRP, witver, h)
403    assert addr is not None
404    return addr
405
406def public_key_to_p2wpkh(public_key: bytes, *, net=None) -> str:
407    if net is None: net = constants.net
408    return hash_to_segwit_addr(hash_160(public_key), witver=0, net=net)
409
410def script_to_p2wsh(script: str, *, net=None) -> str:
411    if net is None: net = constants.net
412    return hash_to_segwit_addr(sha256(bfh(script)), witver=0, net=net)
413
414def p2wpkh_nested_script(pubkey: str) -> str:
415    pkh = hash_160(bfh(pubkey))
416    return construct_script([0, pkh])
417
418def p2wsh_nested_script(witness_script: str) -> str:
419    wsh = sha256(bfh(witness_script))
420    return construct_script([0, wsh])
421
422def pubkey_to_address(txin_type: str, pubkey: str, *, net=None) -> str:
423    if net is None: net = constants.net
424    if txin_type == 'p2pkh':
425        return public_key_to_p2pkh(bfh(pubkey), net=net)
426    elif txin_type == 'p2wpkh':
427        return public_key_to_p2wpkh(bfh(pubkey), net=net)
428    elif txin_type == 'p2wpkh-p2sh':
429        scriptSig = p2wpkh_nested_script(pubkey)
430        return hash160_to_p2sh(hash_160(bfh(scriptSig)), net=net)
431    else:
432        raise NotImplementedError(txin_type)
433
434
435# TODO this method is confusingly named
436def redeem_script_to_address(txin_type: str, scriptcode: str, *, net=None) -> str:
437    if net is None: net = constants.net
438    if txin_type == 'p2sh':
439        # given scriptcode is a redeem_script
440        return hash160_to_p2sh(hash_160(bfh(scriptcode)), net=net)
441    elif txin_type == 'p2wsh':
442        # given scriptcode is a witness_script
443        return script_to_p2wsh(scriptcode, net=net)
444    elif txin_type == 'p2wsh-p2sh':
445        # given scriptcode is a witness_script
446        redeem_script = p2wsh_nested_script(scriptcode)
447        return hash160_to_p2sh(hash_160(bfh(redeem_script)), net=net)
448    else:
449        raise NotImplementedError(txin_type)
450
451
452def script_to_address(script: str, *, net=None) -> str:
453    from .transaction import get_address_from_output_script
454    return get_address_from_output_script(bfh(script), net=net)
455
456
457def address_to_script(addr: str, *, net=None) -> str:
458    if net is None: net = constants.net
459    if not is_address(addr, net=net):
460        raise BitcoinException(f"invalid bitcoin address: {addr}")
461    witver, witprog = segwit_addr.decode_segwit_address(net.SEGWIT_HRP, addr)
462    if witprog is not None:
463        if not (0 <= witver <= 16):
464            raise BitcoinException(f'impossible witness version: {witver}')
465        return construct_script([witver, bytes(witprog)])
466    addrtype, hash_160_ = b58_address_to_hash160(addr)
467    if addrtype == net.ADDRTYPE_P2PKH:
468        script = pubkeyhash_to_p2pkh_script(bh2u(hash_160_))
469    elif addrtype == net.ADDRTYPE_P2SH:
470        script = construct_script([opcodes.OP_HASH160, hash_160_, opcodes.OP_EQUAL])
471    else:
472        raise BitcoinException(f'unknown address type: {addrtype}')
473    return script
474
475
476class OnchainOutputType(Enum):
477    """Opaque types of scriptPubKeys.
478    In case of p2sh, p2wsh and similar, no knowledge of redeem script, etc.
479    """
480    P2PKH = enum.auto()
481    P2SH = enum.auto()
482    WITVER0_P2WPKH = enum.auto()
483    WITVER0_P2WSH = enum.auto()
484
485
486def address_to_hash(addr: str, *, net=None) -> Tuple[OnchainOutputType, bytes]:
487    """Return (type, pubkey hash / witness program) for an address."""
488    if net is None: net = constants.net
489    if not is_address(addr, net=net):
490        raise BitcoinException(f"invalid bitcoin address: {addr}")
491    witver, witprog = segwit_addr.decode_segwit_address(net.SEGWIT_HRP, addr)
492    if witprog is not None:
493        if witver != 0:
494            raise BitcoinException(f"not implemented handling for witver={witver}")
495        if len(witprog) == 20:
496            return OnchainOutputType.WITVER0_P2WPKH, bytes(witprog)
497        elif len(witprog) == 32:
498            return OnchainOutputType.WITVER0_P2WSH, bytes(witprog)
499        else:
500            raise BitcoinException(f"unexpected length for segwit witver=0 witprog: len={len(witprog)}")
501    addrtype, hash_160_ = b58_address_to_hash160(addr)
502    if addrtype == net.ADDRTYPE_P2PKH:
503        return OnchainOutputType.P2PKH, hash_160_
504    elif addrtype == net.ADDRTYPE_P2SH:
505        return OnchainOutputType.P2SH, hash_160_
506    raise BitcoinException(f"unknown address type: {addrtype}")
507
508
509def address_to_scripthash(addr: str, *, net=None) -> str:
510    script = address_to_script(addr, net=net)
511    return script_to_scripthash(script)
512
513
514def script_to_scripthash(script: str) -> str:
515    h = sha256(bfh(script))[0:32]
516    return bh2u(bytes(reversed(h)))
517
518def public_key_to_p2pk_script(pubkey: str) -> str:
519    return construct_script([pubkey, opcodes.OP_CHECKSIG])
520
521def pubkeyhash_to_p2pkh_script(pubkey_hash160: str) -> str:
522    return construct_script([
523        opcodes.OP_DUP,
524        opcodes.OP_HASH160,
525        pubkey_hash160,
526        opcodes.OP_EQUALVERIFY,
527        opcodes.OP_CHECKSIG
528    ])
529
530
531__b58chars = b'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
532assert len(__b58chars) == 58
533
534__b43chars = b'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ$*+-./:'
535assert len(__b43chars) == 43
536
537
538class BaseDecodeError(BitcoinException): pass
539
540
541def base_encode(v: bytes, *, base: int) -> str:
542    """ encode v, which is a string of bytes, to base58."""
543    assert_bytes(v)
544    if base not in (58, 43):
545        raise ValueError('not supported base: {}'.format(base))
546    chars = __b58chars
547    if base == 43:
548        chars = __b43chars
549    long_value = 0
550    power_of_base = 1
551    for c in v[::-1]:
552        # naive but slow variant:   long_value += (256**i) * c
553        long_value += power_of_base * c
554        power_of_base <<= 8
555    result = bytearray()
556    while long_value >= base:
557        div, mod = divmod(long_value, base)
558        result.append(chars[mod])
559        long_value = div
560    result.append(chars[long_value])
561    # Bitcoin does a little leading-zero-compression:
562    # leading 0-bytes in the input become leading-1s
563    nPad = 0
564    for c in v:
565        if c == 0x00:
566            nPad += 1
567        else:
568            break
569    result.extend([chars[0]] * nPad)
570    result.reverse()
571    return result.decode('ascii')
572
573
574def base_decode(v: Union[bytes, str], *, base: int, length: int = None) -> Optional[bytes]:
575    """ decode v into a string of len bytes."""
576    # assert_bytes(v)
577    v = to_bytes(v, 'ascii')
578    if base not in (58, 43):
579        raise ValueError('not supported base: {}'.format(base))
580    chars = __b58chars
581    if base == 43:
582        chars = __b43chars
583    long_value = 0
584    power_of_base = 1
585    for c in v[::-1]:
586        digit = chars.find(bytes([c]))
587        if digit == -1:
588            raise BaseDecodeError('Forbidden character {} for base {}'.format(c, base))
589        # naive but slow variant:   long_value += digit * (base**i)
590        long_value += digit * power_of_base
591        power_of_base *= base
592    result = bytearray()
593    while long_value >= 256:
594        div, mod = divmod(long_value, 256)
595        result.append(mod)
596        long_value = div
597    result.append(long_value)
598    nPad = 0
599    for c in v:
600        if c == chars[0]:
601            nPad += 1
602        else:
603            break
604    result.extend(b'\x00' * nPad)
605    if length is not None and len(result) != length:
606        return None
607    result.reverse()
608    return bytes(result)
609
610
611class InvalidChecksum(BaseDecodeError):
612    pass
613
614
615def EncodeBase58Check(vchIn: bytes) -> str:
616    hash = sha256d(vchIn)
617    return base_encode(vchIn + hash[0:4], base=58)
618
619
620def DecodeBase58Check(psz: Union[bytes, str]) -> bytes:
621    vchRet = base_decode(psz, base=58)
622    payload = vchRet[0:-4]
623    csum_found = vchRet[-4:]
624    csum_calculated = sha256d(payload)[0:4]
625    if csum_calculated != csum_found:
626        raise InvalidChecksum(f'calculated {bh2u(csum_calculated)}, found {bh2u(csum_found)}')
627    else:
628        return payload
629
630
631# backwards compat
632# extended WIF for segwit (used in 3.0.x; but still used internally)
633# the keys in this dict should be a superset of what Imported Wallets can import
634WIF_SCRIPT_TYPES = {
635    'p2pkh':0,
636    'p2wpkh':1,
637    'p2wpkh-p2sh':2,
638    'p2sh':5,
639    'p2wsh':6,
640    'p2wsh-p2sh':7
641}
642WIF_SCRIPT_TYPES_INV = inv_dict(WIF_SCRIPT_TYPES)
643
644
645def is_segwit_script_type(txin_type: str) -> bool:
646    return txin_type in ('p2wpkh', 'p2wpkh-p2sh', 'p2wsh', 'p2wsh-p2sh')
647
648
649def serialize_privkey(secret: bytes, compressed: bool, txin_type: str, *,
650                      internal_use: bool = False) -> str:
651    # we only export secrets inside curve range
652    secret = ecc.ECPrivkey.normalize_secret_bytes(secret)
653    if internal_use:
654        prefix = bytes([(WIF_SCRIPT_TYPES[txin_type] + constants.net.WIF_PREFIX) & 255])
655    else:
656        prefix = bytes([constants.net.WIF_PREFIX])
657    suffix = b'\01' if compressed else b''
658    vchIn = prefix + secret + suffix
659    base58_wif = EncodeBase58Check(vchIn)
660    if internal_use:
661        return base58_wif
662    else:
663        return '{}:{}'.format(txin_type, base58_wif)
664
665
666def deserialize_privkey(key: str) -> Tuple[str, bytes, bool]:
667    if is_minikey(key):
668        return 'p2pkh', minikey_to_private_key(key), False
669
670    txin_type = None
671    if ':' in key:
672        txin_type, key = key.split(sep=':', maxsplit=1)
673        if txin_type not in WIF_SCRIPT_TYPES:
674            raise BitcoinException('unknown script type: {}'.format(txin_type))
675    try:
676        vch = DecodeBase58Check(key)
677    except Exception as e:
678        neutered_privkey = str(key)[:3] + '..' + str(key)[-2:]
679        raise BaseDecodeError(f"cannot deserialize privkey {neutered_privkey}") from e
680
681    if txin_type is None:
682        # keys exported in version 3.0.x encoded script type in first byte
683        prefix_value = vch[0] - constants.net.WIF_PREFIX
684        try:
685            txin_type = WIF_SCRIPT_TYPES_INV[prefix_value]
686        except KeyError as e:
687            raise BitcoinException('invalid prefix ({}) for WIF key (1)'.format(vch[0])) from None
688    else:
689        # all other keys must have a fixed first byte
690        if vch[0] != constants.net.WIF_PREFIX:
691            raise BitcoinException('invalid prefix ({}) for WIF key (2)'.format(vch[0]))
692
693    if len(vch) not in [33, 34]:
694        raise BitcoinException('invalid vch len for WIF key: {}'.format(len(vch)))
695    compressed = False
696    if len(vch) == 34:
697        if vch[33] == 0x01:
698            compressed = True
699        else:
700            raise BitcoinException(f'invalid WIF key. length suggests compressed pubkey, '
701                                   f'but last byte is {vch[33]} != 0x01')
702
703    if is_segwit_script_type(txin_type) and not compressed:
704        raise BitcoinException('only compressed public keys can be used in segwit scripts')
705
706    secret_bytes = vch[1:33]
707    # we accept secrets outside curve range; cast into range here:
708    secret_bytes = ecc.ECPrivkey.normalize_secret_bytes(secret_bytes)
709    return txin_type, secret_bytes, compressed
710
711
712def is_compressed_privkey(sec: str) -> bool:
713    return deserialize_privkey(sec)[2]
714
715
716def address_from_private_key(sec: str) -> str:
717    txin_type, privkey, compressed = deserialize_privkey(sec)
718    public_key = ecc.ECPrivkey(privkey).get_public_key_hex(compressed=compressed)
719    return pubkey_to_address(txin_type, public_key)
720
721def is_segwit_address(addr: str, *, net=None) -> bool:
722    if net is None: net = constants.net
723    try:
724        witver, witprog = segwit_addr.decode_segwit_address(net.SEGWIT_HRP, addr)
725    except Exception as e:
726        return False
727    return witprog is not None
728
729def is_b58_address(addr: str, *, net=None) -> bool:
730    if net is None: net = constants.net
731    try:
732        # test length, checksum, encoding:
733        addrtype, h = b58_address_to_hash160(addr)
734    except Exception as e:
735        return False
736    if addrtype not in [net.ADDRTYPE_P2PKH, net.ADDRTYPE_P2SH]:
737        return False
738    return True
739
740def is_address(addr: str, *, net=None) -> bool:
741    if net is None: net = constants.net
742    return is_segwit_address(addr, net=net) \
743           or is_b58_address(addr, net=net)
744
745
746def is_private_key(key: str, *, raise_on_error=False) -> bool:
747    try:
748        deserialize_privkey(key)
749        return True
750    except BaseException as e:
751        if raise_on_error:
752            raise
753        return False
754
755
756########### end pywallet functions #######################
757
758def is_minikey(text: str) -> bool:
759    # Minikeys are typically 22 or 30 characters, but this routine
760    # permits any length of 20 or more provided the minikey is valid.
761    # A valid minikey must begin with an 'S', be in base58, and when
762    # suffixed with '?' have its SHA256 hash begin with a zero byte.
763    # They are widely used in Casascius physical bitcoins.
764    return (len(text) >= 20 and text[0] == 'S'
765            and all(ord(c) in __b58chars for c in text)
766            and sha256(text + '?')[0] == 0x00)
767
768def minikey_to_private_key(text: str) -> bytes:
769    return sha256(text)
770