示例#1
0
    def __init__(self,
                 paypubkey,
                 refundpubkey,
                 secret,
                 timeout,
                 revoke,
                 revoke_side,
                 delay,
                 privkey=None,
                 secret_pre=None,
                 revoke_pre=None,
                 secret_type='sha'):

        assert revoke_side in ["pay", "refund"]
        assert secret_type == 'sha'

        assert len(secret) == 20
        assert secret_pre is None or len(secret_pre) == 32

        assert len(revoke) == 20

        if revoke_pre is not None:
            assert len(revoke_pre) == 32
            assert Hash160(revoke_pre) == revoke
            assert secret_pre is None
            assert privkey is not None
            assert privkey.pub == (paypubkey
                                   if revoke_side == "pay" else refundpubkey)
        elif secret_pre is not None:
            assert privkey is not None
            assert privkey.pub == paypubkey
            assert Hash160(secret_pre) == secret
        elif privkey is not None:
            assert privkey.pub == refundpubkey
            if self._h_nil not in [revoke, secret]:
                self.timeout_pre = OP_FALSE
            elif self._h_1 not in [revoke, secret]:
                self.timeout_pre = OP_TRUE
            else:
                self.timeout_pre = OP_2

        self.paypubkey = paypubkey
        self.refundpubkey = refundpubkey
        self.secret = secret
        self.timeout = timeout
        self.privkey = privkey
        self.secret_pre = secret_pre
        self.secret_type = secret_type
        self.delay = delay
        self.revoke_side = revoke_side
        self.revoke = revoke
        self.revoke_pre = revoke_pre
示例#2
0
    def get_public_key(self, f="b64", public_key=None):
        if public_key == None:
            public_key = self.public_key

        #O4 = uncompressed.
        public_key_hex = "04" + binascii.hexlify(public_key).decode("utf-8")
        if self.use_compression:
            public_key = binascii.unhexlify(public_key_hex)
            public_key = self.compress_public_key(public_key)
            public_key_hex = binascii.hexlify(public_key).decode("utf-8")
        else:
            public_key = binascii.unhexlify(public_key_hex)

        cpub = CPubKey(x(public_key_hex))

        if f == "bin":
            return public_key

        elif f == "hex":
            return public_key_hex

        elif f == "cpub":
            return cpub

        elif f == "hash":
            return Hash160(cpub)

        else:
            return base64.b64encode(public_key).decode("utf-8")
示例#3
0
    def close_tx(self, fee: int, privkey_dest: str) -> str:
        """Create a (mutual) close tx"""
        txin = CTxIn(COutPoint(bytes.fromhex(self.txid), self.output_index))

        out_privkey = privkey_expand(privkey_dest)

        txout = CTxOut(self.amount - fee,
                       CScript([script.OP_0,
                                Hash160(coincurve.PublicKey.from_secret(out_privkey.secret).format())]))

        tx = CMutableTransaction(vin=[txin], vout=[txout])
        sighash = script.SignatureHash(self.redeemscript(), tx, inIdx=0,
                                       hashtype=script.SIGHASH_ALL,
                                       amount=self.amount,
                                       sigversion=script.SIGVERSION_WITNESS_V0)

        sigs = [key.sign(sighash, hasher=None) for key in self.funding_privkeys_for_tx()]
        # BOLT #3:
        # ## Closing Transaction
        # ...
        #    * `txin[0]` witness: `0 <signature_for_pubkey1> <signature_for_pubkey2>`
        witness = CScriptWitness([bytes(),
                                  sigs[0] + bytes([script.SIGHASH_ALL]),
                                  sigs[1] + bytes([script.SIGHASH_ALL]),
                                  self.redeemscript()])
        tx.wit = CTxWitness([CTxInWitness(witness)])
        return tx.serialize().hex()
示例#4
0
def creates_add_input(bitcoind, tx):
    """Creates and add an input to a CMutableTransaction, SIGHASH_ALL.

    :returns: The txid of the first stage fee bumping tx (for convenience)
    """
    # First we get some coins
    privkey = CKey(os.urandom(32))
    scriptPubKey = CScript([OP_0, Hash160(privkey.pub)])
    address = CBitcoinAddress.from_scriptPubKey(scriptPubKey)
    # Let's say we want to increase the fees by 5000 sats
    amount = 5000

    # Bitcoind is nice and will create the first stage transaction
    first_txid = bitcoind.rpc.sendtoaddress(str(address), amount / COIN)
    vout_index = get_output_index(
        bitcoind.rpc.getrawtransaction(first_txid, 1), amount)
    # === We don't generate a block yet ! ===

    tx.vin.append(
        CTxIn(COutPoint(lx(first_txid), vout_index), nSequence=0xfffffffe))
    # Sign the new input with ALL
    tx_hash = SignatureHash(address.to_redeemScript(), tx, 1, SIGHASH_ALL,
                            amount, SIGVERSION_WITNESS_V0)
    sig = privkey.sign(tx_hash) + bytes([SIGHASH_ALL])
    tx.wit.vtxinwit.append(CTxInWitness(CScriptWitness([sig, privkey.pub])))

    return first_txid
示例#5
0
def analyze_key_pair(key_pair):
    """
    Converts a key pair to different formats which is useful
    for working with Bitcoin Script.
    """
    if "priv" not in key_pair:
        key_pair["priv"] = None
    pub = CPubKey(x(key_pair["pub"]))

    addr = None
    if "addr" in key_pair:
        addr = key_pair["addr"]
    """
    The Hash160 function in Python-bitcoin lib actually
    wraps around sha256 hash so every call to Hash160
    also sha256 hashes the input before ripemd160 hashing,
    meaning it the output is valid for address hashes.
    """
    return {
        "addr": {
            "base58": addr
        },
        "pub": {
            "hash": Hash160(pub),
            "hex": key_pair["pub"],
            "bin": pub
        },
        "priv": {
            "wif": key_pair["priv"],
            "hex": None,
            "bin": None
        }
    }
示例#6
0
def get_payment_address(time_lock, secret_hash, pub_0, pub_1):
    pubkey_script = payment_script(time_lock, secret_hash, pub_0, pub_1)
    print(pubkey_script.hex())
    pubkey_hash = Hash160(pubkey_script)
    data = b'\x55' + pubkey_hash
    checksum = sha256(sha256(data).digest()).digest()[:4]
    byte_address = data + checksum
    address = bitcoin.base58.encode(byte_address)
    return address
示例#7
0
    def get_address_from_pubkey(cls, pubkey):
        """
        Get bitcoin address for a specific hrp (human readable part)
        bech32 encoded from a public key(secp256k1).

        :param string pubkey: public key
        :returns: string bech32 encoded address
        """
        script_pubkey = CScript([OP_0, Hash160(pubkey)])
        return str(P2WPKHBitcoinAddress.from_scriptPubKey(script_pubkey))
示例#8
0
文件: models.py 项目: arshbot/ssss
    def htlc_p2sh(self) -> str:
        # TODO: cache the generated address
        # We create connections on the fly because they'll time out quickly if
        # we don't
        bitcoind = rpc.Proxy()

        # We can decipher the hash of the preimage without explicitly asking
        # for it by taking it out of the payment request supplied
        decoded_pr = json.loads(
            to_json(lnd.decode_payment_request(self.bolt11_invoice)))
        hashed_preimage = decoded_pr['payment_hash']

        # Once these assignments are made, we want to lock them in so this
        # functions generates deterministically
        if not self.final_address_pubkey:
            final_address = bitcoind.getnewaddress()
            seckey = bitcoind.dumpprivkey(final_address)
            self.final_address_pubkey = seckey.pub.hex()

        if not self.redeemblock:
            curr_blockheight = bitcoind.getblockcount()
            self.redeemblock = curr_blockheight + self.lockduration

        # HTLC locking script
        txin_redeemScript = CScript([
            OP_IF, OP_SHA256,
            bytes(hashed_preimage, 'utf8'), OP_EQUALVERIFY, OP_DUP, OP_HASH160,
            bytes(Hash160(bytes(self.final_address_pubkey,
                                'utf8'))), OP_ELSE, self.redeemblock,
            OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_DUP, OP_HASH160,
            bytes(Hash160(bytes(self.refund_address, 'utf8'))), OP_ENDIF,
            OP_EQUALVERIFY, OP_CHECKSIG
        ])

        self.save()

        # Generate a P2SH address from the locking script
        txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey()
        txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(
            txin_scriptPubKey)
        return str(txin_p2sh_address)
示例#9
0
    def make_paytopubkeyhash(self, n = None):
        if n is None:
            n = random.randrange(0, len(self.keypairs))

        secret_bytes = Hash(self.seed + struct.pack('>L', n))
        secret_key = CBitcoinSecret.from_secret_bytes(secret_bytes)

        # pay-to-pubkeyhash
        scriptPubKey = CScript([OP_DUP, OP_HASH160, Hash160(secret_key.pub), OP_EQUALVERIFY, OP_CHECKSIG])

        self.keypairs[scriptPubKey] = secret_key

        return scriptPubKey
示例#10
0
    def from_utxo(txid_in: str,
                  tx_index_in: int,
                  sats: int,
                  privkey: str,
                  fee: int,
                  local_node_privkey: str,
                  local_funding_privkey: str,
                  remote_node_privkey: str,
                  remote_funding_privkey: str,
                  chain_hash: str = regtest_hash) -> Tuple['Funding', str]:
        """Make a funding transaction by spending this utxo using privkey: return Funding, tx."""

        # Create dummy one to start: we will fill in txid at the end.
        funding = Funding('', 0, sats - fee, local_node_privkey,
                          local_funding_privkey, remote_node_privkey,
                          remote_funding_privkey, chain_hash)

        # input private key.
        inkey = privkey_expand(privkey)
        inkey_pub = coincurve.PublicKey.from_secret(inkey.secret)

        # use RBF'able input (requirement for dual-funded things)
        txin = CTxIn(COutPoint(bytes.fromhex(txid_in), tx_index_in),
                     nSequence=0xFFFFFFFD)
        txout = CTxOut(
            sats - fee,
            CScript([script.OP_0,
                     sha256(funding.redeemscript()).digest()]))
        tx = CMutableTransaction([txin], [txout],
                                 nVersion=2,
                                 nLockTime=funding.locktime)

        # now fill in funding txid.
        funding.txid = tx.GetTxid().hex()
        funding.tx = tx

        # while we're here, sign the transaction.
        address = P2WPKHBitcoinAddress.from_scriptPubKey(
            CScript([script.OP_0, Hash160(inkey_pub.format())]))

        sighash = script.SignatureHash(address.to_redeemScript(),
                                       tx,
                                       0,
                                       script.SIGHASH_ALL,
                                       amount=sats,
                                       sigversion=script.SIGVERSION_WITNESS_V0)
        sig = inkey.sign(sighash, hasher=None) + bytes([script.SIGHASH_ALL])

        tx.wit = CTxWitness(
            [CTxInWitness(CScriptWitness([sig, inkey_pub.format()]))])
        return funding, tx.serialize().hex()
示例#11
0
def maketx(tx):
    #txid from blockr. bitcoind does not support tx index!
    txid = lx(tx)
    vout = 0

    outp = COutPoint(txid, vout)
    print("output: %s" % outp)
    # Create the txin structure, which includes the outpoint
    txin = CMutableTxIn(outp)
    print(txin)

    txin_scriptPubKey = CScript(
        [OP_DUP, OP_HASH160,
         Hash160(seckey.pub), OP_EQUALVERIFY, OP_CHECKSIG])

    print(txin_scriptPubKey)

    amount = 0.001 * COIN
    txout = CMutableTxOut(amount, CBitcoinAddress(a).to_scriptPubKey())

    print(txout)

    # Create the unsigned transaction.
    newtx = CMutableTransaction([txin], [txout])
    print(newtx)

    sighash = SignatureHash(txin_scriptPubKey, newtx, 0, SIGHASH_ALL)
    print(sighash)

    # Now sign it. We have to append the type of signature we want to the end, in
    # this case the usual SIGHASH_ALL.
    sig = seckey.sign(sighash) + bytes([SIGHASH_ALL])
    print(sig)

    # Set the scriptSig of our transaction input appropriately.
    txin.scriptSig = CScript([sig, seckey.pub])

    try:
        VerifyScript(txin.scriptSig, txin_scriptPubKey, newtx, 0,
                     (SCRIPT_VERIFY_P2SH, ))
    except:
        pass

    print('*' * 20)
    print(b2x(newtx.serialize()))
    """
示例#12
0
def add_input_output(bitcoind, tx):
    """Add an input and an output to a CMutableTransaction, SIGHASH_ALL."""
    # First we get some coins
    privkey = CKey(os.urandom(32))
    scriptPubKey = CScript([OP_0, Hash160(privkey.pub)])
    address = CBitcoinAddress.from_scriptPubKey(scriptPubKey)
    amount = Decimal("50") * Decimal(COIN) - Decimal("500")
    # This creates a one-output transaction
    txid = bitcoind.pay_to(str(address), amount / Decimal(COIN))
    # We bump the fees by 5000
    tx.vout.append(CTxOut(amount - Decimal("5000"), scriptPubKey))
    tx.vin.append(CTxIn(COutPoint(lx(txid), 0)))
    # Sign the new output with ALL
    tx_hash = SignatureHash(address.to_redeemScript(), tx, 1, SIGHASH_ALL,
                            int(amount), SIGVERSION_WITNESS_V0)
    sig = privkey.sign(tx_hash) + bytes([SIGHASH_ALL])
    tx.wit.vtxinwit.append(CTxInWitness(CScriptWitness([sig, privkey.pub])))
示例#13
0
    def generate_tx(self):
        tx_chain = self.get_next_tx_chain()
        txid = lx(tx_chain.current_unspent_tx)
        txins = [
            CMutableTxIn(COutPoint(txid, 0)),
            CMutableTxIn(COutPoint(txid, 1))
        ]
        txin_seckeys = [tx_chain.seckey, self._spent_to.seckey]

        amount_in = tx_chain.amount
        tx_chain.amount -= int(config.transaction_fee / 2)

        txout1 = CMutableTxOut(
            tx_chain.amount,
            CBitcoinAddress(tx_chain.address).to_scriptPubKey())
        txout2 = CMutableTxOut(
            tx_chain.amount,
            CBitcoinAddress(self._spent_to.address).to_scriptPubKey())

        tx = CMutableTransaction(txins, [txout1, txout2], nVersion=2)

        for i, txin in enumerate(txins):
            txin_scriptPubKey = CScript([
                OP_DUP, OP_HASH160,
                Hash160(txin_seckeys[i].pub), OP_EQUALVERIFY, OP_CHECKSIG
            ])
            sighash = SignatureHash(txin_scriptPubKey, tx, i, SIGHASH_ALL)
            sig = txin_seckeys[i].sign(sighash) + bytes([SIGHASH_ALL])
            txin.scriptSig = CScript([sig, txin_seckeys[i].pub])

        tx_serialized = tx.serialize()
        logging.debug('{} trying to sendrawtransaction'
                      ' (in=2x{} out=2x{} fee={} bytes={})'
                      ' using tx_chain number={}'.format(
                          self._name, amount_in, txout1.nValue,
                          (amount_in * 2) - (txout1.nValue * 2),
                          len(tx_serialized), self._current_tx_chain_index))
        tx_hash = self.execute_rpc('sendrawtransaction', b2x(tx_serialized))
        tx_chain.current_unspent_tx = tx_hash
        logging.info(
            '{} sendrawtransaction was successful; tx got hash={}'.format(
                self._name, tx_hash))
示例#14
0
    def _received_htlc_output(self, htlc: HTLC,
                              side: Side) -> Tuple[script.CScript, int]:
        # BOLT #3: This output sends funds to either the remote node after the
        # HTLC-timeout or using the revocation key, or to an HTLC-success
        # transaction with a successful payment preimage. The output is a
        # P2WSH, with a witness script:
        #
        # # To remote node with revocation key
        # OP_DUP OP_HASH160 <RIPEMD160(SHA256(revocationpubkey))> OP_EQUAL
        # OP_IF
        #     OP_CHECKSIG
        # OP_ELSE
        #     <remote_htlcpubkey> OP_SWAP OP_SIZE 32 OP_EQUAL
        #     OP_IF
        #         # To local node via HTLC-success transaction.
        #         OP_HASH160 <RIPEMD160(payment_hash)> OP_EQUALVERIFY
        #         2 OP_SWAP <local_htlcpubkey> 2 OP_CHECKMULTISIG
        #     OP_ELSE
        #         # To remote node after timeout.
        #         OP_DROP <cltv_expiry> OP_CHECKLOCKTIMEVERIFY OP_DROP
        #         OP_CHECKSIG
        #     OP_ENDIF
        # OP_ENDIF
        htlc_script = script.CScript([
            script.OP_DUP, script.OP_HASH160,
            Hash160(self.revocation_pubkey(side).format()), script.OP_EQUAL,
            script.OP_IF, script.OP_CHECKSIG, script.OP_ELSE,
            self.remote_htlc_pubkey(side).format(), script.OP_SWAP,
            script.OP_SIZE, 32, script.OP_EQUAL, script.OP_IF,
            script.OP_HASH160,
            self.ripemd160(htlc.raw_payment_hash()), script.OP_EQUALVERIFY, 2,
            script.OP_SWAP,
            self.local_htlc_pubkey(side).format(), 2, script.OP_CHECKMULTISIG,
            script.OP_ELSE, script.OP_DROP, htlc.cltv_expiry,
            script.OP_CHECKLOCKTIMEVERIFY, script.OP_DROP, script.OP_CHECKSIG,
            script.OP_ENDIF, script.OP_ENDIF
        ])

        # BOLT #3: The amounts for each output MUST be rounded down to whole
        # satoshis.
        return htlc_script, htlc.amount_msat // 1000
示例#15
0
def generate_p2pkh(Params, pubkey, inputs, inIndex, outputs, details):
    # 1. SelectParams
    SelectParams(Params)
    # 2. make trx input scriptPubKey
    txin_scriptPubKey = CScript(
        [OP_DUP, OP_HASH160,
         Hash160(pubkey), OP_EQUALVERIFY, OP_CHECKSIG])

    current_input = 0
    current_output = 0
    txout = [None] * len(outputs)
    txin_all = []
    txout_all = []

    # 3. make inputs data
    while current_input < len(inputs):
        txid = lx(list(inputs)[current_input].prev_hash.hex())
        vout = list(inputs)[current_input].prev_index
        txin = CMutableTxIn(COutPoint(txid, vout),
                            nSequence=list(inputs)[current_input].sequence)
        txin_all.append(txin)
        current_input += 1

    # 4. make outputs data
    while current_output < len(outputs):
        out_address = list(outputs)[current_output].address
        out_amount = list(outputs)[current_output].amount
        txout[current_output] = CMutableTxOut(
            out_amount,
            CBitcoinAddress(out_address).to_scriptPubKey())
        txout_all.append(txout[current_output])
        current_output += 1

    # 5. make Trx data
    tmp_trx = CMutableTransaction(txin_all,
                                  txout_all,
                                  nLockTime=details.lock_time,
                                  nVersion=details.version)
    # 6. generate Trx Hash
    sighash = SignatureHash(txin_scriptPubKey, tmp_trx, inIndex, SIGHASH_ALL)
    return sighash
示例#16
0
def make_signed_tx(ins, outs, priv):
    print('####### in make_signed_tx #######')
    print('ins: {}'.format(ins))
    print('outs: {}'.format(outs))
    print('priv: {}'.format(priv))
    txins = []
    txouts = []
    txin_scriptPubKeys = []
    # txin_scriptPubKeys = []
    for i, inp in enumerate(ins):
        print('inp[tx_id]: {}'.format(inp['tx_id']))
        txin = CMutableTxIn(COutPoint(lx(inp['tx_id']), inp['index']))
        seckey = CBitcoinSecret.from_secret_bytes(
            pygcoinlib.script_to_address(inp['script']).encode('utf-8'))
        txin_scriptPubKeys.append(
            CScript([
                OP_DUP, OP_HASH160,
                Hash160(seckey.pub), OP_EQUALVERIFY, OP_CHECKSIG
            ]))
        # txin_scriptPubKeys.append(CScript([OP_DUP, OP_HASH160, Hash160(seckey.pub), OP_EQUALVERIFY, OP_CHECKSIG]))
        txins.append(txin)
    for o, out in enumerate(outs):
        # print('out[\'address\']: {}'.format(out['address']))
        if 'script' in out:
            # txouts.append(CMutableTxOut(0, CScript([bytes(out['script'], encoding='UTF-8')]), 2))
            print('song')
        else:
            txouts.append(
                CMutableTxOut(
                    out['value'],
                    CBitcoinAddress(out['address']).to_scriptPubKey(),
                    out['color']))
        # print('address: {}'.format(pygcoinlib.script_to_address(spk['script'])))
    tx = CMutableTransaction(txins, txouts)
    for i, inp in enumerate(ins):
        sighash = SignatureHash(txin_scriptPubKeys[i], tx, i, SIGHASH_ALL)
        sig = seckey.sign(sighash) + bytes([SIGHASH_ALL])
        txins[i].scriptSig = CScript([sig, seckey.pub])
        VerifyScript(txins[i].scriptSig, txin_scriptPubKeys[i], tx, i,
                     (SCRIPT_VERIFY_P2SH, ))
    return b2x(tx.serialize())
示例#17
0
    def add_witnesses(self, witness_stack) -> str:
        wits = []
        for idx, _in in enumerate(self.inputs):
            privkey = _in['privkey']
            serial_id = _in['serial_id']

            if privkey:
                inkey = privkey_expand(privkey)
                inkey_pub = coincurve.PublicKey.from_secret(inkey.secret)

                address = P2WPKHBitcoinAddress.from_scriptPubKey(
                    CScript([script.OP_0,
                             Hash160(inkey_pub.format())]))

                sighash = script.SignatureHash(
                    address.to_redeemScript(),
                    self.tx,
                    idx,
                    script.SIGHASH_ALL,
                    amount=_in['sats'],
                    sigversion=script.SIGVERSION_WITNESS_V0)
                sig = inkey.sign(sighash, hasher=None) + bytes(
                    [script.SIGHASH_ALL])

                wits.append(
                    CTxInWitness(CScriptWitness([sig, inkey_pub.format()])))
                continue

            # Every input from the witness stack will be the accepter's
            # which is always an odd serial
            assert (serial_id % 2 == 1)
            elems = witness_stack.pop(0)['witness_element']

            stack = []
            for elem in elems:
                stack.append(bytes.fromhex(elem['witness']))

            wits.append(CTxInWitness(CScriptWitness(stack)))

        self.tx.wit = CTxWitness(wits)
        return self.tx.serialize().hex()
示例#18
0
def prepare_signatures(transaction, public_keys):
    """
	Create the hashes of transaction
	:param transaction: unsigned transaction hex code
	:param pubkeys: list of public keys
	:return: list of hashes to be signed
	"""
    tx_inputs = transaction.vin
    hashes = []
    if len(tx_inputs) != len(public_keys):
        raise ValueError(
            "Mismatching transaction inputs and list of public keys")
    for i in range(len(tx_inputs)):
        txin_scriptPubKey = CScript([
            OP_DUP, OP_HASH160,
            Hash160(binascii.unhexlify(public_keys[i])), OP_EQUALVERIFY,
            OP_CHECKSIG
        ])
        sighash = SignatureHash(txin_scriptPubKey, transaction, i, SIGHASH_ALL)
        hashes.append(sighash)
    return hashes
示例#19
0
def add_input_output(bitcoind, tx, coin, fees):
    """Add another input to the transaction to bump the feerate."""
    coin_amount = Decimal(coin["amount"]) * Decimal(COIN)
    # First get the private key from bitcoind's wallet.
    privkey = CKey(wif_decode(bitcoind.dumpprivkey(coin["address"])))

    # Add the fetched coin as a new input.
    tx.vin.append(CTxIn(COutPoint(lx(coin["txid"]), coin["vout"])))
    # And likely add an output, otherwise all goes to the fees.
    scriptPubKey = CScript([OP_0, Hash160(privkey.pub)])
    if coin_amount > fees + 294:
        # For simplicity, pay to the same script
        tx.vout.append(CTxOut(coin_amount - Decimal(fees), scriptPubKey))
    address = CBitcoinAddress.from_scriptPubKey(scriptPubKey)
    # We only do this once, sign it with ALL
    tx_hash = SignatureHash(address.to_redeemScript(), tx, 1, SIGHASH_ALL,
                            int(coin_amount), SIGVERSION_WITNESS_V0)
    sig = privkey.sign(tx_hash) + bytes([SIGHASH_ALL])
    tx.wit.vtxinwit.append(
        CTxInWitness(CScriptWitness([sig, privkey.pub]))
    )
    return tx
示例#20
0
    def _offered_htlc_output(self, htlc: HTLC,
                             side: Side) -> Tuple[script.CScript, int]:
        # BOLT #3: This output sends funds to either an HTLC-timeout
        # transaction after the HTLC-timeout or to the remote node
        # using the payment preimage or the revocation key. The output
        # is a P2WSH, with a witness script:
        #
        # # To remote node with revocation key
        # OP_DUP OP_HASH160 <RIPEMD160(SHA256(revocationpubkey))> OP_EQUAL
        # OP_IF
        #     OP_CHECKSIG
        # OP_ELSE
        #     <remote_htlcpubkey> OP_SWAP OP_SIZE 32 OP_EQUAL
        #     OP_NOTIF
        #         # To local node via HTLC-timeout transaction (timelocked).
        #         OP_DROP 2 OP_SWAP <local_htlcpubkey> 2 OP_CHECKMULTISIG
        #     OP_ELSE
        #         # To remote node with preimage.
        #         OP_HASH160 <RIPEMD160(payment_hash)> OP_EQUALVERIFY
        #         OP_CHECKSIG
        #     OP_ENDIF
        # OP_ENDIF
        htlc_script = script.CScript([
            script.OP_DUP, script.OP_HASH160,
            Hash160(self.revocation_pubkey(side).format()), script.OP_EQUAL,
            script.OP_IF, script.OP_CHECKSIG, script.OP_ELSE,
            self.remote_htlc_pubkey(side).format(), script.OP_SWAP,
            script.OP_SIZE, 32, script.OP_EQUAL, script.OP_NOTIF,
            script.OP_DROP, 2, script.OP_SWAP,
            self.local_htlc_pubkey(side).format(), 2, script.OP_CHECKMULTISIG,
            script.OP_ELSE, script.OP_HASH160,
            self.ripemd160(htlc.raw_payment_hash()), script.OP_EQUALVERIFY,
            script.OP_CHECKSIG, script.OP_ENDIF, script.OP_ENDIF
        ])

        # BOLT #3: The amounts for each output MUST be rounded down to whole
        # satoshis.
        return htlc_script, htlc.amount_msat // 1000
    def transfer(self,
                 src_addrs_key_map: OrderedDict,
                 dst_addrs_amount_map: dict,
                 txfee: Decimal,
                 auto_calc_pay_back: bool,
                 pay_back_index: int = 0xfffffff,
                 ensure_one_txout: bool = False) -> str:
        """

        :param src_addrs_key_map: {'addr1': 'privkey1', 'addr2': 'privkey2'  }   私钥为  hex字符串
        :param dst_addrs_amount_map: {'addr1':Decimal(0.1234), 'addr2':Deciaml(0.234) }
        :param txfee:  矿工费
        :param auto_calc_pay_back: 是否自动计算并找零
        :param pay_back_index: 找零地址索引  即在 src_addrs_key_map 中的索引
        :param ensure_one_txout:   确认只有一个交易输出(比如: 提币不需要找零的情况, 归集)
        :return: txid
        """
        #仅支持 P2PKH 类型的from地址

        assert isinstance(src_addrs_key_map,
                          OrderedDict), 'src_addrs_key_map is not OrderedDict'
        assert isinstance(dst_addrs_amount_map,
                          dict), 'dst_addrs_amount_map is not dict'
        assert Decimal('0.00001') <= txfee <= Decimal(
            '0.001'), 'invalid txfee, please check txfee'
        assert len(
            src_addrs_key_map) >= 1, 'src_addrs_key_map length must >= 1'
        assert  not (True == auto_calc_pay_back  == ensure_one_txout) , \
                'True == auto_calc_pay_back  == ensure_one_txout , must be mutex '

        if ensure_one_txout:
            assert (len(dst_addrs_amount_map) == 1
                    ), 'dst_addrs_amount_map length must equal 1'
        elif not auto_calc_pay_back:
            assert (len(dst_addrs_amount_map) >=
                    1), 'dst_addrs_amount_map length must >= 2'

        if auto_calc_pay_back and pay_back_index >= len(src_addrs_key_map):
            raise Exception('pay_back_index is to large')

        self.logger.info(
            f'dst_addrs_amount_map is { json.dumps(dst_addrs_amount_map, indent=4, default=decimal_default) }'
        )

        assert self.ping() == True, 'bitcoind rpc is gone'

        total_amount = sum(dst_addrs_amount_map.values()) + txfee
        self.logger.info(f'total_amount is {total_amount}')

        source_addrs = list(src_addrs_key_map.keys())  #WARNING: 禁止打印私钥!!!

        is_enough, founded_utxos = self.search_utxo(addrs=source_addrs,
                                                    total_amount=total_amount)
        if not is_enough:
            msg = 'balance is not enough'
            self.logger.error(msg)
            raise Exception(msg)

        self.logger.info(
            f'founded_utxos is { json.dumps(founded_utxos, indent=4, default=decimal_default) }'
        )

        #设置全局变量
        SelectParams(self.net_type)

        #构造inputs
        txins = []
        utxo_owner_map = dict()
        for addr, utxos in founded_utxos.items():

            assert addr in src_addrs_key_map, 'addr is not in src_addrs_key_map'

            for utxo in utxos:
                txin = CMutableTxIn(
                    prevout=COutPoint(hash=lx(utxo['txid']), n=utxo['vout']))
                txins.append(txin)

                #因为顺序不会被打乱, 所以可以直接使用 索引进行对应
                utxo_owner_map[len(txins) - 1] = addr

        #构造outputs
        txouts = []
        for to_addr, amount in dst_addrs_amount_map.items():
            out = CMutableTxOut(
                nValue=amount * COIN,
                scriptPubKey=CBitcoinAddress(to_addr).to_scriptPubKey())
            txouts.append(out)

        #自动结算
        if auto_calc_pay_back:
            sum_in_satoshi = 0
            for addr, utxos in founded_utxos.items():
                for utxo in utxos:
                    sum_in_satoshi += utxo['value']
            pay_back_in_satoshi = int(sum_in_satoshi -
                                      int(total_amount * COIN))

            #判断找零的金额, 如果太小就不要了, 作为矿工费
            if pay_back_in_satoshi >= MIN_AVAILABLE_UTXO_VALUE_IN_SATOSHI:
                pay_back_addr = list(src_addrs_key_map.keys())[pay_back_index]
                pay_back_out = CMutableTxOut(
                    nValue=pay_back_in_satoshi,
                    scriptPubKey=CBitcoinAddress(
                        pay_back_addr).to_scriptPubKey())
                txouts.append(pay_back_out)

        muttx = CMutableTransaction(vin=txins, vout=txouts)

        #对每个 input 进行签名
        for n in range(len(txins)):

            #查找这个utxo属于哪个地址
            owner_addr = utxo_owner_map[n]
            privkey = src_addrs_key_map[owner_addr]

            if len(privkey) == 64:  # hex
                wif_key = PrivKeyToWIFCompress(privkey,
                                               self.net_type != 'mainnet')
                seckey = CBitcoinSecret(s=wif_key)
            elif len(privkey) == 52:  #base58格式
                seckey = CBitcoinSecret(s=privkey)
            else:
                raise Exception("invalid privkey")

            txin_script_pubkey = CScript([
                OP_DUP, OP_HASH160,
                Hash160(seckey.pub), OP_EQUALVERIFY, OP_CHECKSIG
            ])
            sig_hash = SignatureHash(txin_script_pubkey, muttx, n, SIGHASH_ALL)
            sig = seckey.sign(sig_hash) + bytes([SIGHASH_ALL])
            muttx.vin[n].scriptSig = CScript([sig, seckey.pub])

            #TODO: 处理验签失败抛异常
            VerifyScript(muttx.vin[n].scriptSig, txin_script_pubkey, muttx, n,
                         (SCRIPT_VERIFY_P2SH, ))
            pass

        raw_tx_hex = b2x(muttx.serialize())
        self.logger.info(f'raw_tx_hex is: {raw_tx_hex}')

        # local_tx_hash = muttx.GetTxid()
        # self.logger.info(f'local_tx_hash is: {b2x(local_tx_hash)}')

        #广播交易
        assert self.ping(
        ) == True, 'bitcoind rpc is gone'  #测试 bitcoind的 rpc服务是否还在
        txid = self.send_raw_tx(raw_tx_hex=raw_tx_hex)
        self.logger.info(f'send_raw_tx txid is: {txid}')

        return txid
示例#22
0
    def sign_our_inputs(self) -> None:
        assert self.tx is not None
        for idx, _in in enumerate(self.inputs):
            privkey = _in['privkey']

            if privkey and 'sig' not in _in:
                print('signing our input for tx', self.tx.serialize().hex())
                inkey = privkey_expand(privkey)
                inkey_pub = coincurve.PublicKey.from_secret(inkey.secret)

                # Really horrid hack to produce a signature for the
                # multisig utxo in tests/helpers.py
                if privkey == '38204720bc4f9647fd58c6d0a4bd3a6dd2be16d8e4273c4d1bdd5774e8c51eaf':
                    redeemscript = bytes.fromhex('51210253cdf835e328346a4f19de099cf3d42d4a7041e073cd4057a1c4fd7cdbb1228f2103ae903722f21f85e651b8f9b18fc854084fb90eeb76452bdcfd0cb43a16a382a221036c264d68a9727afdc75949f7d7fa71910ae9ae8001a1fbffa6f7ce000976597c21036429fa8a4ef0b2b1d5cb553e34eeb90a32ab19fae1f0024f332ab4f74283a7282103d4232f19ea85051e7b76bf5f01d03e17eea8751463dee36d71413a739de1a92755ae')
                else:
                    address = P2WPKHBitcoinAddress.from_scriptPubKey(CScript([script.OP_0, Hash160(inkey_pub.format())]))
                    redeemscript = address.to_redeemScript()

                sighash = script.SignatureHash(redeemscript,
                                               self.tx, idx, script.SIGHASH_ALL,
                                               amount=_in['sats'],
                                               sigversion=script.SIGVERSION_WITNESS_V0)
                sig = inkey.sign(sighash, hasher=None) + bytes([script.SIGHASH_ALL])

                if privkey == '38204720bc4f9647fd58c6d0a4bd3a6dd2be16d8e4273c4d1bdd5774e8c51eaf':
                    _in['sig'] = CTxInWitness(CScriptWitness([bytes([]), sig, redeemscript]))
                else:
                    _in['sig'] = CTxInWitness(CScriptWitness([sig, inkey_pub.format()]))
示例#23
0
# hex and converts it to bytes.
txid = lx('7e195aa3de827814f172c362fcf838d92ba10e3f9fdd9c3ecaf79522b311b22d')
vout = 0

# Create the txin structure, which includes the outpoint. The scriptSig
# defaults to being empty.
txin = CMutableTxIn(COutPoint(txid, vout))

# We also need the scriptPubKey of the output we're spending because
# SignatureHash() replaces the transaction scriptSig's with it.
#
# Here we'll create that scriptPubKey from scratch using the pubkey that
# corresponds to the secret key we generated above.
txin_scriptPubKey = CScript(
    [OP_DUP, OP_HASH160,
     Hash160(seckey.pub), OP_EQUALVERIFY, OP_CHECKSIG])

# Create the txout. This time we create the scriptPubKey from a Bitcoin
# address.
txout = CMutableTxOut(
    0.001 * COIN,
    CBitcoinAddress('1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8').to_scriptPubKey())

# Create the unsigned transaction.
tx = CMutableTransaction([txin], [txout])

# Calculate the signature hash for that transaction.
sighash = SignatureHash(txin_scriptPubKey, tx, 0, SIGHASH_ALL)

# Now sign it. We have to append the type of signature we want to the end, in
# this case the usual SIGHASH_ALL.
示例#24
0
        for n, (_, _, redeemer) in enumerate(self.prevouts):
            txin = CMutableTxIn.from_txin(tx.vin[n])
            txin.scriptSig = redeemer.sign_spend(unsigned_tx, n)
            tx.vin[n] = CTxIn.from_txin(txin)

        print(b2x(tx.serialize()))


bitcoin.SelectParams('regtest')

# parameters

spend = CBitcoinSecret("cVFfsB2h1KHgPEWtpXrnZ5qjk18xw2o2fuxCTaf7BN2Z5PSvhq4M")
refund = CBitcoinSecret("cRKSxo1yJKP1RwaHULWaumNYiyXiQu2tRGdTmUxzP1s4YeSM4ks1")
sec = b'A pair of boiled eggs for lunch?'
sechash = Hash160(sec)
rev = x('4a120469b397556363c4e47f45d8f81b381f721af89baba372425f820ae7077c')
revhash = Hash160(rev)

# the two scripts: paying me or paying them (or refunding to me)
htlcA = HTLCScript(spend.pub, refund.pub, sechash, 230, revhash, 'pay', 10)
htlcB = HTLCScript(spend.pub, refund.pub, sechash, 230, revhash, 'refund', 10)

# the six ways of resolving the scripts: spending, refunding or revoking,
# for both types of script
htlcAs = HTLCScript(spend.pub,
                    refund.pub,
                    sechash,
                    230,
                    revhash,
                    'pay',
示例#25
0
class HTLCScript(Script):
    _h_nil = Hash160(b"")
    _h_1 = Hash160(b"\x01")

    def __init__(self,
                 paypubkey,
                 refundpubkey,
                 secret,
                 timeout,
                 revoke,
                 revoke_side,
                 delay,
                 privkey=None,
                 secret_pre=None,
                 revoke_pre=None,
                 secret_type='sha'):

        assert revoke_side in ["pay", "refund"]
        assert secret_type == 'sha'

        assert len(secret) == 20
        assert secret_pre is None or len(secret_pre) == 32

        assert len(revoke) == 20

        if revoke_pre is not None:
            assert len(revoke_pre) == 32
            assert Hash160(revoke_pre) == revoke
            assert secret_pre is None
            assert privkey is not None
            assert privkey.pub == (paypubkey
                                   if revoke_side == "pay" else refundpubkey)
        elif secret_pre is not None:
            assert privkey is not None
            assert privkey.pub == paypubkey
            assert Hash160(secret_pre) == secret
        elif privkey is not None:
            assert privkey.pub == refundpubkey
            if self._h_nil not in [revoke, secret]:
                self.timeout_pre = OP_FALSE
            elif self._h_1 not in [revoke, secret]:
                self.timeout_pre = OP_TRUE
            else:
                self.timeout_pre = OP_2

        self.paypubkey = paypubkey
        self.refundpubkey = refundpubkey
        self.secret = secret
        self.timeout = timeout
        self.privkey = privkey
        self.secret_pre = secret_pre
        self.secret_type = secret_type
        self.delay = delay
        self.revoke_side = revoke_side
        self.revoke = revoke
        self.revoke_pre = revoke_pre

    @property
    def script(self):
        if self.revoke_side == "pay":
            # ((revoke|secret) & pay) | (csv & cltv & refund)
            return CScript([
                OP_HASH160, OP_DUP, self.secret, OP_EQUAL, OP_SWAP,
                self.revoke, OP_EQUAL, OP_BOOLOR, OP_IF, self.paypubkey,
                OP_ELSE, self.delay, OP_CSV, self.timeout, OP_CLTV, OP_2DROP,
                self.refundpubkey, OP_ENDIF, OP_CHECKSIG
            ])
        else:
            # (csv & secret & pay) | ((revoke | cltv) & refund)
            return CScript([
                OP_HASH160, OP_DUP, self.secret, OP_EQUAL, OP_IF, self.delay,
                OP_CSV, OP_2DROP, self.paypubkey, OP_ELSE, self.revoke,
                OP_EQUAL, OP_NOTIF, self.timeout, OP_CLTV, OP_DROP, OP_ENDIF,
                self.refundpubkey, OP_ENDIF, OP_CHECKSIG
            ])

    def mutate_spend(self, tx, vin):
        if self.revoke_pre is None:
            if self.secret_pre is not None:
                self.bump_locktime(tx, self.timeout)
            if self.revoke_side == "pay":
                if self.secret_pre is None:
                    self.bump_delay(tx, vin, self.delay)
            else:
                if self.secret_pre is not None:
                    self.bump_delay(tx, vin, self.delay)

    def sign_spend(self, tx, vin):
        assert self.privkey is not None

        txhash = mkhash(self.script, tx, vin, SIGHASH_ALL)
        sig = mksig(self.privkey, txhash, SIGHASH_ALL)
        if self.revoke_pre is not None:
            return CScript([sig, self.revoke_pre, self.script])
        elif self.secret_pre is not None:
            return CScript([sig, self.secret_pre, self.script])
        else:
            return CScript([sig, self.timeout_pre, self.script])
示例#26
0
OP_CHECKSEQUENCEVERIFY = OP_NOP3  #unlike other op-codes, python-bitcoinlib doesn't define OP_CHECKSEQUENCEVERIFY, so I define it myself here

SelectParams('testnet')

# parameterize these
privKeySender = str(sys.argv[1])
addressReceiver = str(sys.argv[2])

secret = CBitcoinSecret(privKeySender)

# per instructor message in the COMP541-01-S-2019 Moodle classroom forum, use '0xc800' (200 blocks) for relative timelock amount
# 0xc800 is calculated as little-endian hex of 200, plus '00' tacked on the end because it's necessary to add sign for values which are less than half a byte, otherwise interpreted as negative
redeem_script = CScript([
    0xc800, OP_CHECKSEQUENCEVERIFY, OP_DROP, OP_DUP, OP_HASH160,
    Hash160(secret.pub), OP_EQUALVERIFY, OP_CHECKSIG
])

txin_scriptPubKey = redeem_script.to_p2sh_scriptPubKey()
txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey)
print('Pay to:', str(txin_p2sh_address))

proxy = bitcoin.rpc.Proxy()

# the below functionality is to be run from cmd line in between send_to_p2sh_timelock.py and this program
# uncomment to run programmatically for testing purposes
# transactionid = proxy.sendtoaddress(str(txin_p2sh_address), 0.0001*COIN)
# print('transaction id: ' + b2lx(transactionid))

# find the set of UTXO transactions that have been sent to the script address
txins = []  # inputs to the spending transaction we will create
示例#27
0
def hash_of_secret():
    return Hash160(alice_secret_x)
示例#28
0
    def _unsigned_tx(
            self,
            side: Side) -> Tuple[CMutableTransaction, List[Optional[HTLC]]]:
        """Create the commitment transaction.

Returns it and a list of matching HTLCs for each output

        """
        ocn = self.obscured_commit_num(
            self.keyset[self.opener].raw_payment_basepoint(),
            self.keyset[not self.opener].raw_payment_basepoint(),
            self.commitnum)

        # BOLT #3:
        # ## Commitment Transaction
        # ...
        # * txin count: 1
        #    * `txin[0]` outpoint: `txid` and `output_index` from `funding_created` message
        #    * `txin[0]` sequence: upper 8 bits are 0x80, lower 24 bits are upper 24 bits of the obscured commitment number
        #    * `txin[0]` script bytes: 0
        #    * `txin[0]` witness: `0 <signature_for_pubkey1> <signature_for_pubkey2>`
        txin = CTxIn(COutPoint(bytes.fromhex(self.funding.txid),
                               self.funding.output_index),
                     nSequence=0x80000000 | (ocn >> 24))

        # txouts, with ctlv_timeouts (for htlc output tiebreak) and htlc
        txouts: List[Tuple[CTxOut, int, Optional[HTLC]]] = []

        for htlc in self.untrimmed_htlcs(side):
            if htlc.owner == side:
                redeemscript, sats = self._offered_htlc_output(htlc, side)
            else:
                redeemscript, sats = self._received_htlc_output(htlc, side)
            print("*** Got htlc redeemscript {} / {}".format(
                redeemscript, redeemscript.hex()))
            txouts.append(
                (CTxOut(sats,
                        CScript([script.OP_0,
                                 sha256(redeemscript).digest()])),
                 htlc.cltv_expiry, htlc))

        num_untrimmed_htlcs = len(txouts)
        fee = self._fee(num_untrimmed_htlcs)

        out_redeemscript, sats = self._to_local_output(fee, side)
        if sats >= self.dust_limit[side]:
            txouts.append((CTxOut(
                sats, CScript([script.OP_0,
                               sha256(out_redeemscript).digest()])), 0, None))

        # BOLT #3:
        # #### `to_remote` Output
        #
        # This output sends funds to the other peer and thus is a simple
        # P2WPKH to `remotepubkey`.
        amount_to_other = self.amounts[not side] // 1000
        if not side == self.opener:
            amount_to_other -= fee

        if amount_to_other >= self.dust_limit[side]:
            txouts.append((CTxOut(
                amount_to_other,
                CScript([
                    script.OP_0,
                    Hash160(self.to_remote_pubkey(side).format())
                ])), 0, None))

        # BOLT #3:
        # ## Transaction Input and Output Ordering
        #
        # Lexicographic ordering: see
        # [BIP69](https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki).
        # In the case of identical HTLC outputs, the outputs are ordered in
        # increasing `cltv_expiry` order.

        # First sort by cltv_expiry
        txouts.sort(key=lambda txout: txout[1])
        # Now sort by BIP69
        txouts.sort(key=lambda txout: txout[0].serialize())

        # BOLT #3:
        # ## Commitment Transaction
        #
        # * version: 2
        # * locktime: upper 8 bits are 0x20, lower 24 bits are the
        #   lower 24 bits of the obscured commitment number
        return (CMutableTransaction(vin=[txin],
                                    vout=[txout[0] for txout in txouts],
                                    nVersion=2,
                                    nLockTime=0x20000000 | (ocn & 0x00FFFFFF)),
                [txout[2] for txout in txouts])
    def build_fund_tx(self, redeem_script):
        if type(self.send_amount) != decimal.Decimal:
            raise Exception("Please only use decimal types for the amount.")
        if self.send_amount < decimal.Decimal(
                coinbend.config["minimum_trade"]):
            raise Exception("Amount is too small.")

        #Because every Satoshi counts.
        decimal.getcontext().prec = 50

        #Create a change address.
        if "change" not in self.key_pairs:
            self.key_pairs["change"] = self.key_pair_from_address(
                self.jsonrpc[self.my].getnewaddress(), self.my)

        #Get wallet balance.
        balance = self.jsonrpc[self.my].getbalance()

        #Check we have enough.
        if balance < self.send_amount:
            raise Exception("Insufficent balance to cover fund.")

        #List unclaimed outputs.
        unspent_outputs = self.jsonrpc[self.my].listunspent()

        #Setup tx inputs.
        change_amount = decimal.Decimal("0")
        txins = []
        total = decimal.Decimal("0")
        indexes = []
        i = 0
        for unspent_output in unspent_outputs:
            #Skip outputs that don't satisfy min confirmations.
            if unspent_output["confirmations"] < self.minimum_confirmations:
                i += 1
                continue

            #Check scriptPubKey is pay to pub key hash.
            if self.jsonrpc[self.my].decodescript(
                    unspent_output["scriptPubKey"])["type"] != "pubkeyhash":
                i += 1
                continue

            #Record key pairs.
            if unspent_output["address"] not in self.key_pairs:
                self.key_pairs[
                    unspent_output["address"]] = self.key_pair_from_address(
                        unspent_output["address"], self.my)

            #Build new txin.
            txid = lx(unspent_output["txid"])
            vout = unspent_output["vout"]
            txin = CTxIn(COutPoint(txid, vout))
            txins.append(txin)
            indexes.append(i)
            total += unspent_output["amount"]
            i += 1

            if total > self.send_amount:
                break

            if total == self.send_amount:
                break

        #Insufficent funds.
        if total < self.send_amount:
            raise Exception("Not enough valid inputs to fund contract.")

        #Calculate change.
        change = total - self.send_amount

        #Build txouts.
        txouts = []
        redeem_script_hash160 = self.hash160_script(redeem_script)
        p2sh_script_pub_key = CScript(
            [OP_HASH160, redeem_script_hash160["bin"], OP_EQUAL])
        txouts.append(
            CTxOut(
                (self.send_amount -
                 decimal.Decimal(coinbend.config["mining_fee"]["standard"])) *
                COIN, p2sh_script_pub_key))
        if change > decimal.Decimal("0"):
            change_seckey = CBitcoinSecret(
                self.key_pairs["change"]["priv"]["wif"])
            change_script_pub_key = CScript([
                OP_DUP, OP_HASH160,
                Hash160(change_seckey.pub), OP_EQUALVERIFY, OP_CHECKSIG
            ])
            txouts.append(CTxOut(change * COIN, change_script_pub_key))

        #Build unsigned transaction.
        tx = CTransaction(txins, txouts)
        unsigned_tx_hex = b2x(tx.serialize())

        #Sign transaction.
        signed_tx_hex = self.jsonrpc[self.my].signrawtransaction(
            unsigned_tx_hex)["hex"]
        return signed_tx_hex
 def hash160_script(self, script):
     bin_hash = Hash160(script["bin"])
     return {
         "hex": binascii.hexlify(bin_hash).decode("ascii"),
         "bin": bin_hash
     }