def address_type(addr): if addr.startswith(('1', '2', '3', 'm', 'n')): try: address = base58.decode(addr).rjust(25, b'\x00') except Base58DecodeError as e: raise InvalidAddress(f"{addr} : {e}") from None payload, checksum = address[:-4], address[-4:] version_byte, digest = payload[0:1], payload[1:].rjust(20, b'\x00') if len(digest) != 20: raise InvalidAddress(f"{addr} : Bad Payload") from None if sha256(sha256(payload))[:4] != checksum: raise InvalidAddress(f"{addr} : Invalid checksum") from None try: return {network('keyhash'): ADDRESS.P2PKH, network('scripthash'): ADDRESS.P2SH}[version_byte] except KeyError: raise InvalidAddress(f"{addr} : Invalid version byte") from None elif addr.startswith(network('hrp')): try: witness_version, witness_program = bech32.decode(network('hrp'), addr) except Bech32DecodeError as e: raise InvalidAddress(f"{addr} : {e}") from None if not witness_version == 0x00: raise InvalidAddress(f"{addr} : Invalid witness version") from None if len(witness_program) == 20: return ADDRESS.P2WPKH elif len(witness_program) == 32: return ADDRESS.P2WSH else: raise InvalidAddress(f"{addr} : Invalid witness program") from None else: raise InvalidAddress(f"{addr} : Invalid leading character") from None
def sighash(self, i, script=b'', hashcode=SIGHASH.ALL): inp = self.inputs[i] tx_type = inp.ref().type() if tx_type in (TX.P2PK, TX.P2PKH) or (tx_type == TX.P2SH and not inp.is_nested()): preimage = self.signature_form_legacy(i=i, script=script, hashcode=hashcode) else: preimage = self.signature_form_segwit(i=i, hashcode=hashcode) return sha256(sha256(preimage))
def signature_form_segwit(self, i, hashcode=SIGHASH.ALL): """Segwit version of signature_form""" # https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki#specification tx = deepcopy(self) ref = tx.inputs[i].ref() nversion = tx._version[::-1] hashprevouts = sha256(sha256(concat((inp.outpoint() for inp in tx.inputs)))) if not hashcode.is_anyonecanpay() else pad(0, 32) hashsequence = sha256(sha256(concat(inp._sequence[::-1] for inp in tx.inputs))) if hashcode == SIGHASH.ALL else pad(0, 32) outpoint = tx.inputs[i].outpoint() scriptcode = serialize(tx.inputs[i].scriptcode()) value = ref._value[::-1] nsequence = tx.inputs[i]._sequence[::-1] if not (hashcode.is_single() or hashcode.is_none()): hashoutputs = sha256(sha256(concat(out._value[::-1] + push(out.script) for out in tx.outputs))) elif hashcode.is_single() and i < len(tx.outputs): hashoutputs = sha256(sha256(tx.outputs[i]._value[::-1] + push(tx.outputs[i].script))) else: hashoutputs = pad(0, 32) nlocktime = tx._lock_time[::-1] sighash = pad(hashcode.value, 4)[::-1] return concat([nversion, hashprevouts, hashsequence, outpoint, scriptcode, value, nsequence, hashoutputs, nlocktime, sighash])
def check(mnemonic): mnemonic = mnemonic.lower().split() if len(mnemonic) not in {12, 15, 18, 21, 24}: return False try: indexes = [binary_search(word) for word in mnemonic] except LookupError: return False bits = ''.join(int_to_bin(idx).zfill(11) for idx in indexes) checksum_length = len(mnemonic)//3 data, checksum = bin_to_bytes(bits[:-checksum_length]), bits[-checksum_length:] return bytes_to_bin(sha256(data)).zfill(256)[:checksum_length] == checksum
def verify_p2wsh(self): if not version_byte(self.scriptPubKey) == 0x00: raise InvalidTransaction('Unknown witness version') witness = deepcopy(self.input.witness) self.stack = list(witness) witness_script = self.pop() if not len(witness_script) <= 10000: raise InvalidTransaction('Witness script too long') if not witness_program(self.scriptPubKey) == sha256(witness_script): raise InvalidTransaction( 'Redeem script hash does not match scriptPubKey') self.script = witness_script if any((len(item) > 520 for item in witness)): raise InvalidTransaction( f'Invalid witness for a {TX.P2WSH} transaction') return self.verify_legacy() and len(self.stack) == 0
def wtxid(self): if not self.segwit: return self.txid() return sha256(sha256(self.serialize(segwit=True)))
def txid(self): return sha256(sha256(self.serialize(segwit=False)))
def script_to_bech32(script: bytes, witver: int) -> str: """https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#witness-program""" witprog = sha256(script) return bech32.encode(network('hrp'), witver, witprog)
def hashed_payload_to_address(payload): checksum = sha256(sha256(payload))[:4] address = payload + checksum return base58.encode(address)
"""https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#witness-program""" witprog = hash160(pub.encode(compressed=True)) return bech32.encode(network('hrp'), witver, witprog) key_to_addr_versions = { ADDRESS.P2PKH: lambda pub: legacy_address(pub, version_byte=network('keyhash')), # 'P2WPKH': partial(pubkey_to_p2wpkh, version_byte=0x06, witver=0x00), # WAS REPLACED BY BIP 173 ADDRESS.P2WPKH_P2SH: lambda pub: legacy_address(witness_byte(witver=0) + push(hash160(pub.encode(compressed=True))), version_byte=network('scripthash')), ADDRESS.P2WPKH: partial(pubkey_to_bech32, witver=0x00), } script_to_addr_versions = { ADDRESS.P2SH: lambda script: legacy_address(script, version_byte=network('scripthash')), # 'P2WSH': partial(script_to_p2wsh, version_byte=0x0A, witver=0x00), # WAS REPLACED BY BIP 173 ADDRESS.P2WSH_P2SH: lambda script: legacy_address(witness_byte(witver=0) + push(sha256(script)), version_byte=network('scripthash')), ADDRESS.P2WSH: partial(script_to_bech32, witver=0x00), } def pubkey_to_address(pub: PublicKey, version='P2PKH') -> str: converter = key_to_addr_versions[ADDRESS(version.upper())] return converter(pub) def script_to_address(script: bytes, version='P2SH') -> str: """Redeem script to address""" converter = script_to_addr_versions[ADDRESS(version.upper())] return converter(script)
def encode(self): data = self.serialize() assert len(data) == 78 checksum = sha256(sha256(data))[:4] return base58.encode(data + checksum)
def decode(cls, string: str) -> 'ExtendedKey': bts = base58.decode(string) assert len(bts) == 82, f'Invalid length {len(bts)})' data, checksum = bts[:78], bts[78:] assert sha256(sha256(data)).startswith(checksum), 'Invalid checksum' return cls.deserialize(data)