def multisig_script(public_keys: Sequence[str], m: int) -> str: n = len(public_keys) assert 1 <= m <= n <= 15, f'm {m}, n {n}' op_m = bh2u(add_number_to_script(m)) op_n = bh2u(add_number_to_script(n)) keylist = [push_script(k) for k in public_keys] return op_m + ''.join(keylist) + op_n + opcodes.OP_CHECKMULTISIG.hex()
def get_address_from_output_script(_bytes: bytes, *, net=None) -> Tuple[int, str]: try: decoded = [x for x in script_GetOp(_bytes)] except MalformedBitcoinScript: decoded = None # p2pk match = [OPPushDataPubkey, opcodes.OP_CHECKSIG] if match_decoded(decoded, match) and ecc.ECPubkey.is_pubkey_bytes( decoded[0][1]): return TYPE_PUBKEY, bh2u(decoded[0][1]) # p2pkh match = [ opcodes.OP_DUP, opcodes.OP_HASH160, OPPushDataGeneric(lambda x: x == 20), opcodes.OP_EQUALVERIFY, opcodes.OP_CHECKSIG ] if match_decoded(decoded, match): return TYPE_ADDRESS, hash160_to_p2pkh(decoded[2][1], net=net) # p2sh match = [ opcodes.OP_HASH160, OPPushDataGeneric(lambda x: x == 20), opcodes.OP_EQUAL ] if match_decoded(decoded, match): return TYPE_ADDRESS, hash160_to_p2sh(decoded[1][1], net=net) # segwit address (version 0) match = [opcodes.OP_0, OPPushDataGeneric(lambda x: x in (20, 32))] if match_decoded(decoded, match): return TYPE_ADDRESS, hash_to_segwit_addr(decoded[1][1], witver=0, net=net) # segwit address (version 1-16) future_witness_versions = list(range(opcodes.OP_1, opcodes.OP_16 + 1)) for witver, opcode in enumerate(future_witness_versions, start=1): match = [opcode, OPPushDataGeneric(lambda x: 2 <= x <= 40)] if match_decoded(decoded, match): return TYPE_ADDRESS, hash_to_segwit_addr(decoded[1][1], witver=witver, net=net) return TYPE_SCRIPT, bh2u(_bytes)
def txid(self): self.deserialize() all_segwit = all(self.is_segwit_input(x) for x in self.inputs()) if not all_segwit and not self.is_complete(): return None ser = self.serialize_to_network(witness=False) return bh2u(sha256d(bfh(ser))[::-1])
def _calc_bip143_shared_txdigest_fields( self) -> BIP143SharedTxDigestFields: inputs = self.inputs() outputs = self.outputs() hashPrevouts = bh2u( sha256d( bfh(''.join(self.serialize_outpoint(txin) for txin in inputs)))) hashSequence = bh2u( sha256d( bfh(''.join( int_to_hex(txin.get('sequence', 0xffffffff - 1), 4) for txin in inputs)))) hashOutputs = bh2u( sha256d(bfh(''.join(self.serialize_output(o) for o in outputs)))) return BIP143SharedTxDigestFields(hashPrevouts=hashPrevouts, hashSequence=hashSequence, hashOutputs=hashOutputs)
def construct_witness(items: Sequence[Union[str, int, bytes]]) -> str: """Constructs a witness from the given stack items.""" witness = var_int(len(items)) for item in items: if type(item) is int: item = bitcoin.script_num_to_hex(item) elif type(item) is bytes: item = bh2u(item) witness += bitcoin.witness_push(item) return witness
def serialize(self, estimate_size=False, witness=True): network_ser = self.serialize_to_network(estimate_size, witness) if estimate_size: return network_ser if self.is_partial_originally and not self.is_complete(): partial_format_version = '00' return bh2u(PARTIAL_TXN_HEADER_MAGIC ) + partial_format_version + network_ser else: return network_ser
def parse_witness(vds, txin, full_parse: bool): n = vds.read_compact_size() if n == 0: txin['witness'] = '00' return if n == 0xffffffff: txin['value'] = vds.read_uint64() txin['witness_version'] = vds.read_uint16() n = vds.read_compact_size() # now 'n' is the number of items in the witness w = list(bh2u(vds.read_bytes(vds.read_compact_size())) for i in range(n)) txin['witness'] = construct_witness(w) if not full_parse: return try: if txin.get('witness_version', 0) != 0: raise UnknownTxinType() if txin['type'] == 'coinbase': pass elif txin['type'] == 'address': pass elif txin['type'] == 'p2wsh-p2sh' or n > 2: witness_script_unsanitized = w[ -1] # for partial multisig txn, this has x_pubkeys try: m, n, x_pubkeys, pubkeys, witness_script = parse_redeemScript_multisig( bfh(witness_script_unsanitized)) except NotRecognizedRedeemScript: raise UnknownTxinType() txin['signatures'] = parse_sig(w[1:-1]) txin['num_sig'] = m txin['x_pubkeys'] = x_pubkeys txin['pubkeys'] = pubkeys txin['witness_script'] = witness_script if not txin.get('scriptSig'): # native segwit script txin['type'] = 'p2wsh' txin['address'] = bitcoin.script_to_p2wsh(witness_script) elif txin['type'] == 'p2wpkh-p2sh' or n == 2: txin['num_sig'] = 1 txin['x_pubkeys'] = [w[1]] txin['pubkeys'] = [safe_parse_pubkey(w[1])] txin['signatures'] = parse_sig([w[0]]) if not txin.get('scriptSig'): # native segwit script txin['type'] = 'p2wpkh' txin['address'] = bitcoin.public_key_to_p2wpkh( bfh(txin['pubkeys'][0])) else: raise UnknownTxinType() except UnknownTxinType: txin['type'] = 'unknown' except BaseException: txin['type'] = 'unknown' _logger.exception(f"failed to parse witness {txin.get('witness')}")
def parse_output(vds, i): d = {} d['value'] = vds.read_int64() if d['value'] > TOTAL_COIN_SUPPLY_LIMIT_IN_BTC * COIN: raise SerializationError('invalid output amount (too large)') if d['value'] < 0: raise SerializationError('invalid output amount (negative)') scriptPubKey = vds.read_bytes(vds.read_compact_size()) d['type'], d['address'] = get_address_from_output_script(scriptPubKey) d['scriptPubKey'] = bh2u(scriptPubKey) d['prevout_n'] = i return d
def sign_txin(self, txin_index, privkey_bytes, *, bip143_shared_txdigest_fields=None) -> str: pre_hash = sha256d( bfh( self.serialize_preimage( txin_index, bip143_shared_txdigest_fields=bip143_shared_txdigest_fields ))) privkey = ecc.ECPrivkey(privkey_bytes) sig = privkey.sign_transaction(pre_hash) sig = bh2u(sig) + '01' return sig
def get_preimage_script(self, txin): preimage_script = txin.get('preimage_script', None) if preimage_script is not None: return preimage_script pubkeys, x_pubkeys = self.get_sorted_pubkeys(txin) if txin['type'] == 'p2pkh': return bitcoin.address_to_script(txin['address']) elif txin['type'] in ['p2sh', 'p2wsh', 'p2wsh-p2sh']: return multisig_script(pubkeys, txin['num_sig']) elif txin['type'] in ['p2wpkh', 'p2wpkh-p2sh']: pubkey = pubkeys[0] pkh = bh2u(hash_160(bfh(pubkey))) return '76a9' + push_script(pkh) + '88ac' elif txin['type'] == 'p2pk': pubkey = pubkeys[0] return bitcoin.public_key_to_p2pk_script(pubkey) else: raise TypeError('Unknown txin type', txin['type'])
def parse_input(vds, full_parse: bool): d = {} prevout_hash = hash_encode(vds.read_bytes(32)) prevout_n = vds.read_uint32() scriptSig = vds.read_bytes(vds.read_compact_size()) sequence = vds.read_uint32() d['prevout_hash'] = prevout_hash d['prevout_n'] = prevout_n d['scriptSig'] = bh2u(scriptSig) d['sequence'] = sequence d['type'] = 'unknown' if prevout_hash != '00' * 32 else 'coinbase' d['address'] = None d['num_sig'] = 0 if not full_parse: return d d['x_pubkeys'] = [] d['pubkeys'] = [] d['signatures'] = {} if d['type'] != 'coinbase' and scriptSig: try: parse_scriptSig(d, scriptSig) except BaseException: _logger.exception(f'failed to parse scriptSig {bh2u(scriptSig)}') return d
def parse_redeemScript_multisig(redeem_script: bytes): try: dec2 = [x for x in script_GetOp(redeem_script)] except MalformedBitcoinScript: raise NotRecognizedRedeemScript() try: m = dec2[0][0] - opcodes.OP_1 + 1 n = dec2[-2][0] - opcodes.OP_1 + 1 except IndexError: raise NotRecognizedRedeemScript() op_m = opcodes.OP_1 + m - 1 op_n = opcodes.OP_1 + n - 1 match_multisig = [op_m] + [OPPushDataGeneric] * n + [ op_n, opcodes.OP_CHECKMULTISIG ] if not match_decoded(dec2, match_multisig): raise NotRecognizedRedeemScript() x_pubkeys = [bh2u(x[1]) for x in dec2[1:-2]] pubkeys = [safe_parse_pubkey(x) for x in x_pubkeys] redeem_script2 = bfh(multisig_script(x_pubkeys, m)) if redeem_script2 != redeem_script: raise NotRecognizedRedeemScript() redeem_script_sanitized = multisig_script(pubkeys, m) return m, n, x_pubkeys, pubkeys, redeem_script_sanitized
def parse_scriptSig(d, _bytes): try: decoded = [x for x in script_GetOp(_bytes)] except Exception as e: # coinbase transactions raise an exception _logger.info( f"parse_scriptSig: cannot find address in input script (coinbase?) {bh2u(_bytes)}" ) return match = [OPPushDataGeneric] if match_decoded(decoded, match): item = decoded[0][1] if item[0] == 0: # segwit embedded into p2sh # witness version 0 d['address'] = bitcoin.hash160_to_p2sh(hash_160(item)) if len(item) == 22: d['type'] = 'p2wpkh-p2sh' elif len(item) == 34: d['type'] = 'p2wsh-p2sh' else: _logger.info(f"unrecognized txin type {bh2u(item)}") elif opcodes.OP_1 <= item[0] <= opcodes.OP_16: # segwit embedded into p2sh # witness version 1-16 pass else: # assert item[0] == 0x30 # pay-to-pubkey d['type'] = 'p2pk' d['address'] = "(pubkey)" d['signatures'] = [bh2u(item)] d['num_sig'] = 1 d['x_pubkeys'] = ["(pubkey)"] d['pubkeys'] = ["(pubkey)"] return # p2pkh TxIn transactions push a signature # (71-73 bytes) and then their public key # (33 or 65 bytes) onto the stack: match = [OPPushDataGeneric, OPPushDataGeneric] if match_decoded(decoded, match): sig = bh2u(decoded[0][1]) x_pubkey = bh2u(decoded[1][1]) try: signatures = parse_sig([sig]) pubkey, address = xpubkey_to_address(x_pubkey) except: _logger.info( f"parse_scriptSig: cannot find address in input script (p2pkh?) {bh2u(_bytes)}" ) return d['type'] = 'p2pkh' d['signatures'] = signatures d['x_pubkeys'] = [x_pubkey] d['num_sig'] = 1 d['pubkeys'] = [pubkey] d['address'] = address return # p2sh transaction, m of n match = [opcodes.OP_0] + [OPPushDataGeneric] * (len(decoded) - 1) if match_decoded(decoded, match): x_sig = [bh2u(x[1]) for x in decoded[1:-1]] redeem_script_unsanitized = decoded[-1][ 1] # for partial multisig txn, this has x_pubkeys try: m, n, x_pubkeys, pubkeys, redeem_script = parse_redeemScript_multisig( redeem_script_unsanitized) except NotRecognizedRedeemScript: _logger.info( f"parse_scriptSig: cannot find address in input script (p2sh?) {bh2u(_bytes)}" ) # we could still guess: # d['address'] = hash160_to_p2sh(hash_160(decoded[-1][1])) return # write result in d d['type'] = 'p2sh' d['num_sig'] = m d['signatures'] = parse_sig(x_sig) d['x_pubkeys'] = x_pubkeys d['pubkeys'] = pubkeys d['redeem_script'] = redeem_script d['address'] = hash160_to_p2sh(hash_160(bfh(redeem_script))) return # custom partial format for imported addresses match = [opcodes.OP_INVALIDOPCODE, opcodes.OP_0, OPPushDataGeneric] if match_decoded(decoded, match): x_pubkey = bh2u(decoded[2][1]) pubkey, address = xpubkey_to_address(x_pubkey) d['type'] = 'address' d['address'] = address d['num_sig'] = 1 d['x_pubkeys'] = [x_pubkey] d['pubkeys'] = None # get_sorted_pubkeys will populate this d['signatures'] = [None] return _logger.info( f"parse_scriptSig: cannot find address in input script (unknown) {bh2u(_bytes)}" )
def wtxid(self): self.deserialize() if not self.is_complete(): return None ser = self.serialize_to_network(witness=True) return bh2u(sha256d(bfh(ser))[::-1])
def serialize_outpoint(self, txin): return bh2u(bfh(txin['prevout_hash'])[::-1]) + int_to_hex( txin['prevout_n'], 4)