bitcoin.py (23572B)
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 26 import hashlib 27 from typing import List, Tuple, TYPE_CHECKING, Optional, Union, Sequence 28 import enum 29 from enum import IntEnum, Enum 30 31 from .util import bfh, bh2u, BitcoinException, assert_bytes, to_bytes, inv_dict, is_hex_str 32 from . import version 33 from . import segwit_addr 34 from . import constants 35 from . import ecc 36 from .crypto import sha256d, sha256, hash_160, hmac_oneshot 37 38 if TYPE_CHECKING: 39 from .network import Network 40 41 42 ################################## transactions 43 44 COINBASE_MATURITY = 100 45 COIN = 100000000 46 TOTAL_COIN_SUPPLY_LIMIT_IN_BTC = 21000000 47 48 NLOCKTIME_MIN = 0 49 NLOCKTIME_BLOCKHEIGHT_MAX = 500_000_000 - 1 50 NLOCKTIME_MAX = 2 ** 32 - 1 51 52 # supported types of transaction outputs 53 # TODO kill these with fire 54 TYPE_ADDRESS = 0 55 TYPE_PUBKEY = 1 56 TYPE_SCRIPT = 2 57 58 59 class 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 200 def rev_hex(s: str) -> str: 201 return bh2u(bfh(s)[::-1]) 202 203 204 def 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 220 def 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 244 def 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 259 def 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 266 def _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 277 def 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 298 def add_number_to_script(i: int) -> bytes: 299 return bfh(push_script(script_num_to_hex(i))) 300 301 302 def construct_witness(items: Sequence[Union[str, int, bytes]]) -> str: 303 """Constructs a witness from the given stack items.""" 304 witness = var_int(len(items)) 305 for item in items: 306 if type(item) is int: 307 item = script_num_to_hex(item) 308 elif isinstance(item, (bytes, bytearray)): 309 item = bh2u(item) 310 else: 311 assert is_hex_str(item) 312 witness += witness_push(item) 313 return witness 314 315 316 def construct_script(items: Sequence[Union[str, int, bytes, opcodes]]) -> str: 317 """Constructs bitcoin script from given items.""" 318 script = '' 319 for item in items: 320 if isinstance(item, opcodes): 321 script += item.hex() 322 elif type(item) is int: 323 script += add_number_to_script(item).hex() 324 elif isinstance(item, (bytes, bytearray)): 325 script += push_script(item.hex()) 326 elif isinstance(item, str): 327 assert is_hex_str(item) 328 script += push_script(item) 329 else: 330 raise Exception(f'unexpected item for script: {item!r}') 331 return script 332 333 334 def relayfee(network: 'Network' = None) -> int: 335 """Returns feerate in sat/kbyte.""" 336 from .simple_config import FEERATE_DEFAULT_RELAY, FEERATE_MAX_RELAY 337 if network and network.relay_fee is not None: 338 fee = network.relay_fee 339 else: 340 fee = FEERATE_DEFAULT_RELAY 341 # sanity safeguards, as network.relay_fee is coming from a server: 342 fee = min(fee, FEERATE_MAX_RELAY) 343 fee = max(fee, FEERATE_DEFAULT_RELAY) 344 return fee 345 346 347 # see https://github.com/bitcoin/bitcoin/blob/a62f0ed64f8bbbdfe6467ac5ce92ef5b5222d1bd/src/policy/policy.cpp#L14 348 DUST_LIMIT_DEFAULT_SAT_LEGACY = 546 349 DUST_LIMIT_DEFAULT_SAT_SEGWIT = 294 350 351 352 def dust_threshold(network: 'Network' = None) -> int: 353 """Returns the dust limit in satoshis.""" 354 # Change <= dust threshold is added to the tx fee 355 dust_lim = 182 * 3 * relayfee(network) # in msat 356 # convert to sat, but round up: 357 return (dust_lim // 1000) + (dust_lim % 1000 > 0) 358 359 360 def hash_encode(x: bytes) -> str: 361 return bh2u(x[::-1]) 362 363 364 def hash_decode(x: str) -> bytes: 365 return bfh(x)[::-1] 366 367 368 ############ functions from pywallet ##################### 369 370 def hash160_to_b58_address(h160: bytes, addrtype: int) -> str: 371 s = bytes([addrtype]) + h160 372 s = s + sha256d(s)[0:4] 373 return base_encode(s, base=58) 374 375 376 def b58_address_to_hash160(addr: str) -> Tuple[int, bytes]: 377 addr = to_bytes(addr, 'ascii') 378 _bytes = DecodeBase58Check(addr) 379 if len(_bytes) != 21: 380 raise Exception(f'expected 21 payload bytes in base58 address. got: {len(_bytes)}') 381 return _bytes[0], _bytes[1:21] 382 383 384 def hash160_to_p2pkh(h160: bytes, *, net=None) -> str: 385 if net is None: net = constants.net 386 return hash160_to_b58_address(h160, net.ADDRTYPE_P2PKH) 387 388 def hash160_to_p2sh(h160: bytes, *, net=None) -> str: 389 if net is None: net = constants.net 390 return hash160_to_b58_address(h160, net.ADDRTYPE_P2SH) 391 392 def public_key_to_p2pkh(public_key: bytes, *, net=None) -> str: 393 if net is None: net = constants.net 394 return hash160_to_p2pkh(hash_160(public_key), net=net) 395 396 def hash_to_segwit_addr(h: bytes, witver: int, *, net=None) -> str: 397 if net is None: net = constants.net 398 return segwit_addr.encode(net.SEGWIT_HRP, witver, h) 399 400 def public_key_to_p2wpkh(public_key: bytes, *, net=None) -> str: 401 if net is None: net = constants.net 402 return hash_to_segwit_addr(hash_160(public_key), witver=0, net=net) 403 404 def script_to_p2wsh(script: str, *, net=None) -> str: 405 if net is None: net = constants.net 406 return hash_to_segwit_addr(sha256(bfh(script)), witver=0, net=net) 407 408 def p2wpkh_nested_script(pubkey: str) -> str: 409 pkh = hash_160(bfh(pubkey)) 410 return construct_script([0, pkh]) 411 412 def p2wsh_nested_script(witness_script: str) -> str: 413 wsh = sha256(bfh(witness_script)) 414 return construct_script([0, wsh]) 415 416 def pubkey_to_address(txin_type: str, pubkey: str, *, net=None) -> str: 417 if net is None: net = constants.net 418 if txin_type == 'p2pkh': 419 return public_key_to_p2pkh(bfh(pubkey), net=net) 420 elif txin_type == 'p2wpkh': 421 return public_key_to_p2wpkh(bfh(pubkey), net=net) 422 elif txin_type == 'p2wpkh-p2sh': 423 scriptSig = p2wpkh_nested_script(pubkey) 424 return hash160_to_p2sh(hash_160(bfh(scriptSig)), net=net) 425 else: 426 raise NotImplementedError(txin_type) 427 428 429 # TODO this method is confusingly named 430 def redeem_script_to_address(txin_type: str, scriptcode: str, *, net=None) -> str: 431 if net is None: net = constants.net 432 if txin_type == 'p2sh': 433 # given scriptcode is a redeem_script 434 return hash160_to_p2sh(hash_160(bfh(scriptcode)), net=net) 435 elif txin_type == 'p2wsh': 436 # given scriptcode is a witness_script 437 return script_to_p2wsh(scriptcode, net=net) 438 elif txin_type == 'p2wsh-p2sh': 439 # given scriptcode is a witness_script 440 redeem_script = p2wsh_nested_script(scriptcode) 441 return hash160_to_p2sh(hash_160(bfh(redeem_script)), net=net) 442 else: 443 raise NotImplementedError(txin_type) 444 445 446 def script_to_address(script: str, *, net=None) -> str: 447 from .transaction import get_address_from_output_script 448 return get_address_from_output_script(bfh(script), net=net) 449 450 451 def address_to_script(addr: str, *, net=None) -> str: 452 if net is None: net = constants.net 453 if not is_address(addr, net=net): 454 raise BitcoinException(f"invalid bitcoin address: {addr}") 455 witver, witprog = segwit_addr.decode(net.SEGWIT_HRP, addr) 456 if witprog is not None: 457 if not (0 <= witver <= 16): 458 raise BitcoinException(f'impossible witness version: {witver}') 459 return construct_script([witver, bytes(witprog)]) 460 addrtype, hash_160_ = b58_address_to_hash160(addr) 461 if addrtype == net.ADDRTYPE_P2PKH: 462 script = pubkeyhash_to_p2pkh_script(bh2u(hash_160_)) 463 elif addrtype == net.ADDRTYPE_P2SH: 464 script = construct_script([opcodes.OP_HASH160, hash_160_, opcodes.OP_EQUAL]) 465 else: 466 raise BitcoinException(f'unknown address type: {addrtype}') 467 return script 468 469 470 class OnchainOutputType(Enum): 471 """Opaque types of scriptPubKeys. 472 In case of p2sh, p2wsh and similar, no knowledge of redeem script, etc. 473 """ 474 P2PKH = enum.auto() 475 P2SH = enum.auto() 476 WITVER0_P2WPKH = enum.auto() 477 WITVER0_P2WSH = enum.auto() 478 479 480 def address_to_hash(addr: str, *, net=None) -> Tuple[OnchainOutputType, bytes]: 481 """Return (type, pubkey hash / witness program) for an address.""" 482 if net is None: net = constants.net 483 if not is_address(addr, net=net): 484 raise BitcoinException(f"invalid bitcoin address: {addr}") 485 witver, witprog = segwit_addr.decode(net.SEGWIT_HRP, addr) 486 if witprog is not None: 487 if witver != 0: 488 raise BitcoinException(f"not implemented handling for witver={witver}") 489 if len(witprog) == 20: 490 return OnchainOutputType.WITVER0_P2WPKH, bytes(witprog) 491 elif len(witprog) == 32: 492 return OnchainOutputType.WITVER0_P2WSH, bytes(witprog) 493 else: 494 raise BitcoinException(f"unexpected length for segwit witver=0 witprog: len={len(witprog)}") 495 addrtype, hash_160_ = b58_address_to_hash160(addr) 496 if addrtype == net.ADDRTYPE_P2PKH: 497 return OnchainOutputType.P2PKH, hash_160_ 498 elif addrtype == net.ADDRTYPE_P2SH: 499 return OnchainOutputType.P2SH, hash_160_ 500 raise BitcoinException(f"unknown address type: {addrtype}") 501 502 503 def address_to_scripthash(addr: str) -> str: 504 script = address_to_script(addr) 505 return script_to_scripthash(script) 506 507 def script_to_scripthash(script: str) -> str: 508 h = sha256(bfh(script))[0:32] 509 return bh2u(bytes(reversed(h))) 510 511 def public_key_to_p2pk_script(pubkey: str) -> str: 512 return construct_script([pubkey, opcodes.OP_CHECKSIG]) 513 514 def pubkeyhash_to_p2pkh_script(pubkey_hash160: str) -> str: 515 return construct_script([ 516 opcodes.OP_DUP, 517 opcodes.OP_HASH160, 518 pubkey_hash160, 519 opcodes.OP_EQUALVERIFY, 520 opcodes.OP_CHECKSIG 521 ]) 522 523 524 __b58chars = b'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' 525 assert len(__b58chars) == 58 526 527 __b43chars = b'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ$*+-./:' 528 assert len(__b43chars) == 43 529 530 531 class BaseDecodeError(BitcoinException): pass 532 533 534 def base_encode(v: bytes, *, base: int) -> str: 535 """ encode v, which is a string of bytes, to base58.""" 536 assert_bytes(v) 537 if base not in (58, 43): 538 raise ValueError('not supported base: {}'.format(base)) 539 chars = __b58chars 540 if base == 43: 541 chars = __b43chars 542 long_value = 0 543 power_of_base = 1 544 for c in v[::-1]: 545 # naive but slow variant: long_value += (256**i) * c 546 long_value += power_of_base * c 547 power_of_base <<= 8 548 result = bytearray() 549 while long_value >= base: 550 div, mod = divmod(long_value, base) 551 result.append(chars[mod]) 552 long_value = div 553 result.append(chars[long_value]) 554 # Bitcoin does a little leading-zero-compression: 555 # leading 0-bytes in the input become leading-1s 556 nPad = 0 557 for c in v: 558 if c == 0x00: 559 nPad += 1 560 else: 561 break 562 result.extend([chars[0]] * nPad) 563 result.reverse() 564 return result.decode('ascii') 565 566 567 def base_decode(v: Union[bytes, str], *, base: int, length: int = None) -> Optional[bytes]: 568 """ decode v into a string of len bytes.""" 569 # assert_bytes(v) 570 v = to_bytes(v, 'ascii') 571 if base not in (58, 43): 572 raise ValueError('not supported base: {}'.format(base)) 573 chars = __b58chars 574 if base == 43: 575 chars = __b43chars 576 long_value = 0 577 power_of_base = 1 578 for c in v[::-1]: 579 digit = chars.find(bytes([c])) 580 if digit == -1: 581 raise BaseDecodeError('Forbidden character {} for base {}'.format(c, base)) 582 # naive but slow variant: long_value += digit * (base**i) 583 long_value += digit * power_of_base 584 power_of_base *= base 585 result = bytearray() 586 while long_value >= 256: 587 div, mod = divmod(long_value, 256) 588 result.append(mod) 589 long_value = div 590 result.append(long_value) 591 nPad = 0 592 for c in v: 593 if c == chars[0]: 594 nPad += 1 595 else: 596 break 597 result.extend(b'\x00' * nPad) 598 if length is not None and len(result) != length: 599 return None 600 result.reverse() 601 return bytes(result) 602 603 604 class InvalidChecksum(BaseDecodeError): 605 pass 606 607 608 def EncodeBase58Check(vchIn: bytes) -> str: 609 hash = sha256d(vchIn) 610 return base_encode(vchIn + hash[0:4], base=58) 611 612 613 def DecodeBase58Check(psz: Union[bytes, str]) -> bytes: 614 vchRet = base_decode(psz, base=58) 615 payload = vchRet[0:-4] 616 csum_found = vchRet[-4:] 617 csum_calculated = sha256d(payload)[0:4] 618 if csum_calculated != csum_found: 619 raise InvalidChecksum(f'calculated {bh2u(csum_calculated)}, found {bh2u(csum_found)}') 620 else: 621 return payload 622 623 624 # backwards compat 625 # extended WIF for segwit (used in 3.0.x; but still used internally) 626 # the keys in this dict should be a superset of what Imported Wallets can import 627 WIF_SCRIPT_TYPES = { 628 'p2pkh':0, 629 'p2wpkh':1, 630 'p2wpkh-p2sh':2, 631 'p2sh':5, 632 'p2wsh':6, 633 'p2wsh-p2sh':7 634 } 635 WIF_SCRIPT_TYPES_INV = inv_dict(WIF_SCRIPT_TYPES) 636 637 638 def is_segwit_script_type(txin_type: str) -> bool: 639 return txin_type in ('p2wpkh', 'p2wpkh-p2sh', 'p2wsh', 'p2wsh-p2sh') 640 641 642 def serialize_privkey(secret: bytes, compressed: bool, txin_type: str, *, 643 internal_use: bool = False) -> str: 644 # we only export secrets inside curve range 645 secret = ecc.ECPrivkey.normalize_secret_bytes(secret) 646 if internal_use: 647 prefix = bytes([(WIF_SCRIPT_TYPES[txin_type] + constants.net.WIF_PREFIX) & 255]) 648 else: 649 prefix = bytes([constants.net.WIF_PREFIX]) 650 suffix = b'\01' if compressed else b'' 651 vchIn = prefix + secret + suffix 652 base58_wif = EncodeBase58Check(vchIn) 653 if internal_use: 654 return base58_wif 655 else: 656 return '{}:{}'.format(txin_type, base58_wif) 657 658 659 def deserialize_privkey(key: str) -> Tuple[str, bytes, bool]: 660 if is_minikey(key): 661 return 'p2pkh', minikey_to_private_key(key), False 662 663 txin_type = None 664 if ':' in key: 665 txin_type, key = key.split(sep=':', maxsplit=1) 666 if txin_type not in WIF_SCRIPT_TYPES: 667 raise BitcoinException('unknown script type: {}'.format(txin_type)) 668 try: 669 vch = DecodeBase58Check(key) 670 except Exception as e: 671 neutered_privkey = str(key)[:3] + '..' + str(key)[-2:] 672 raise BaseDecodeError(f"cannot deserialize privkey {neutered_privkey}") from e 673 674 if txin_type is None: 675 # keys exported in version 3.0.x encoded script type in first byte 676 prefix_value = vch[0] - constants.net.WIF_PREFIX 677 try: 678 txin_type = WIF_SCRIPT_TYPES_INV[prefix_value] 679 except KeyError as e: 680 raise BitcoinException('invalid prefix ({}) for WIF key (1)'.format(vch[0])) from None 681 else: 682 # all other keys must have a fixed first byte 683 if vch[0] != constants.net.WIF_PREFIX: 684 raise BitcoinException('invalid prefix ({}) for WIF key (2)'.format(vch[0])) 685 686 if len(vch) not in [33, 34]: 687 raise BitcoinException('invalid vch len for WIF key: {}'.format(len(vch))) 688 compressed = False 689 if len(vch) == 34: 690 if vch[33] == 0x01: 691 compressed = True 692 else: 693 raise BitcoinException(f'invalid WIF key. length suggests compressed pubkey, ' 694 f'but last byte is {vch[33]} != 0x01') 695 696 if is_segwit_script_type(txin_type) and not compressed: 697 raise BitcoinException('only compressed public keys can be used in segwit scripts') 698 699 secret_bytes = vch[1:33] 700 # we accept secrets outside curve range; cast into range here: 701 secret_bytes = ecc.ECPrivkey.normalize_secret_bytes(secret_bytes) 702 return txin_type, secret_bytes, compressed 703 704 705 def is_compressed_privkey(sec: str) -> bool: 706 return deserialize_privkey(sec)[2] 707 708 709 def address_from_private_key(sec: str) -> str: 710 txin_type, privkey, compressed = deserialize_privkey(sec) 711 public_key = ecc.ECPrivkey(privkey).get_public_key_hex(compressed=compressed) 712 return pubkey_to_address(txin_type, public_key) 713 714 def is_segwit_address(addr: str, *, net=None) -> bool: 715 if net is None: net = constants.net 716 try: 717 witver, witprog = segwit_addr.decode(net.SEGWIT_HRP, addr) 718 except Exception as e: 719 return False 720 return witprog is not None 721 722 def is_b58_address(addr: str, *, net=None) -> bool: 723 if net is None: net = constants.net 724 try: 725 # test length, checksum, encoding: 726 addrtype, h = b58_address_to_hash160(addr) 727 except Exception as e: 728 return False 729 if addrtype not in [net.ADDRTYPE_P2PKH, net.ADDRTYPE_P2SH]: 730 return False 731 return True 732 733 def is_address(addr: str, *, net=None) -> bool: 734 if net is None: net = constants.net 735 return is_segwit_address(addr, net=net) \ 736 or is_b58_address(addr, net=net) 737 738 739 def is_private_key(key: str, *, raise_on_error=False) -> bool: 740 try: 741 deserialize_privkey(key) 742 return True 743 except BaseException as e: 744 if raise_on_error: 745 raise 746 return False 747 748 749 ########### end pywallet functions ####################### 750 751 def is_minikey(text: str) -> bool: 752 # Minikeys are typically 22 or 30 characters, but this routine 753 # permits any length of 20 or more provided the minikey is valid. 754 # A valid minikey must begin with an 'S', be in base58, and when 755 # suffixed with '?' have its SHA256 hash begin with a zero byte. 756 # They are widely used in Casascius physical bitcoins. 757 return (len(text) >= 20 and text[0] == 'S' 758 and all(ord(c) in __b58chars for c in text) 759 and sha256(text + '?')[0] == 0x00) 760 761 def minikey_to_private_key(text: str) -> bytes: 762 return sha256(text)