예제 #1
0
    def into_transaction(self, dust_secret: CBitcoinSecret,
                         dust_outpoint: OutPointWithTx, feerate):
        if dust_outpoint.prevout.scriptPubKey != pubkey_to_P2PKH_scriptPubkey(
                dust_secret.pub):
            print(b2x(dust_outpoint.prevout.scriptPubKey))
            print(b2x(pubkey_to_P2PKH_scriptPubkey(dust_secret.pub)))
            raise Exception("Outpoint has incorrect scriptPubKey")

        sum_in = dust_outpoint.prevout.nValue

        fees = int(tx_size(1, 2) / 1000 * feerate)
        refund = sum_in - fees - self._life_signal_amount

        print('fee: %f' % fees)
        print('amount: %f' % (sum_in - fees))

        redeemScript = self.redeemScript

        unsigned_tx = CTransaction([CTxIn(dust_outpoint.outpoint)], [
            CTxOut(self._life_signal_amount,
                   redeemScript.to_p2sh_scriptPubKey()),
            CTxOut(refund, pubkey_to_P2PKH_scriptPubkey(dust_secret.pub))
        ])

        # spend the dust input
        sighash = SignatureHash(dust_outpoint.prevout.scriptPubKey,
                                unsigned_tx, 0, SIGHASH_ALL)
        sig = dust_secret.sign(sighash) + bytes([SIGHASH_ALL])
        sigScript = CScript([sig, dust_secret.pub])

        signed_input = [CTxIn(unsigned_tx.vin[0].prevout, sigScript)]

        return CTransaction(signed_input, unsigned_tx.vout,
                            unsigned_tx.nLockTime)
예제 #2
0
def spend_p2sh_mediator(redeemScript, txins_str, amounts, daddrs, sig, rein):
    txin_redeemScript = CScript(x(redeemScript))
    txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey()
    txins_obj = []
    for txin_str in txins_str.split():
        txin_list = txin_str.split("-")
        txins_obj.append(
            CMutableTxIn(COutPoint(lx(txin_list[0]), int(txin_list[1]))))
    txouts = []
    len_amounts = len(amounts)
    for i in range(0, len_amounts):
        txouts.append(
            CMutableTxOut(
                round(amounts[i], 8) * COIN,
                CBitcoinAddress(daddrs[i]).to_scriptPubKey()))
    tx = CMutableTransaction(txins_obj, txouts)
    seckey = CBitcoinSecret(rein.user.dkey)
    ntxins = len(txins_obj)
    sig_list = []
    for s in sig.split():
        sig_list.append(x(s))
    sig2_str = ""
    for i in range(0, ntxins):
        sighash = SignatureHash(txin_redeemScript, tx, i, SIGHASH_ALL)
        sig2 = seckey.sign(sighash) + x("01")
        sig2_str += " " + b2x(sig2)
        txins_obj[i].scriptSig = CScript(
            [OP_0, sig2, sig_list[i], txin_redeemScript])
        VerifyScript(txins_obj[i].scriptSig, txin_scriptPubKey, tx, i,
                     (SCRIPT_VERIFY_P2SH, ))
    tx_bytes = tx.serialize()
    hash = sha256(sha256(tx_bytes).digest()).digest()
    txid = b2x(hash[::-1])
    txid_causeway = broadcast_tx(b2x(tx_bytes), rein)
    return (txid, sig2_str[1:])
예제 #3
0
def partial_spend_p2sh_mediator(redeemScript,
                                rein,
                                mediator_address,
                                mediator_sig=False):
    txin_redeemScript = CScript(x(redeemScript))
    txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey()
    txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey)
    (txins, total_value) = unspent_txins(txin_p2sh_address, rein.testnet)
    if len(txins) == 0:
        raise ValueError('No unspent txins found')
    txins_str = ""
    txins_obj = []
    for txid, vout in txins:
        txins_str += " " + txid + "-" + str(vout)
        txins_obj.append(CMutableTxIn(COutPoint(lx(txid), vout)))
    fee = 0.00025
    amount = round(total_value - fee, 8)
    if amount <= 0:
        raise ValueError('Not enough value in the inputs')
    if mediator_sig:
        txout = CMutableTxOut(
            amount * COIN,
            CBitcoinAddress(mediator_address).to_scriptPubKey())
        tx = CMutableTransaction(txins_obj, [txout])
        seckey = CBitcoinSecret(rein.user.dkey)
        ntxins = len(txins_obj)
        sig = ""
        for i in range(0, ntxins):
            sighash = SignatureHash(txin_redeemScript, tx, i, SIGHASH_ALL)
            sig += " " + b2x(seckey.sign(sighash) + x("01"))
        return (txins_str[1:], "{:.8f}".format(amount), str(mediator_address),
                sig[1:])
    return (txins_str[1:], "{:.8f}".format(amount), str(mediator_address))
예제 #4
0
    def _scriptSig_by_sgx(self, seckey_sgx: CBitcoinSecret, unsigned_tx, n_in):
        # sgx spends the true branch
        branch = OP_TRUE
        sighash = SignatureHash(self._redeemScript(), unsigned_tx, n_in,
                                SIGHASH_ALL)

        sig = seckey_sgx.sign(sighash) + bytes([SIGHASH_ALL])
        return CScript([sig, seckey_sgx.pub, branch, self._redeemScript()])
예제 #5
0
    def scriptSig_by_key1(self, secret_key: CBitcoinSecret,
                          unsigned_tx: CTransaction, which_to_sign):
        assert secret_key.pub == self._key1
        branch = OP_TRUE
        sighash = SignatureHash(self.redeemScript, unsigned_tx, which_to_sign,
                                SIGHASH_ALL)

        sig = secret_key.sign(sighash) + bytes([SIGHASH_ALL])

        return CScript([sig, self._key1, branch, self.redeemScript])
예제 #6
0
def partial_spend_p2sh(redeemScript,
                       rein,
                       daddr=None,
                       alt_amount=None,
                       alt_daddr=None):
    if daddr is None:
        daddr = rein.user.daddr
    txin_redeemScript = CScript(x(redeemScript))
    txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey()
    txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey)
    (txins, total_value) = unspent_txins(txin_p2sh_address, rein.testnet)
    if len(txins) == 0:
        raise ValueError(
            'Primary escrow is empty. Please inform client to add funds.')
    txins_str = ""
    txins_obj = []
    for txid, vout in txins:
        txins_str += " " + txid + "-" + str(vout)
        txins_obj.append(CMutableTxIn(COutPoint(lx(txid), vout)))
    fee = float(PersistConfig.get(rein, 'fee', 0.001))
    amount = round(total_value - fee, 8)
    if alt_amount:
        amount = round(amount - alt_amount, 8)
    if amount <= 0. or alt_amount > total_value - fee:
        click.echo("amount: " + str(amount) + " alt_amount: " +
                   str(alt_amount) + " total_value: " + str(total_value))
        raise ValueError(
            'Primary escrow balance too low. Please inform client to add funds.'
        )
    txouts = []
    txout = CMutableTxOut(amount * COIN,
                          CBitcoinAddress(daddr).to_scriptPubKey())
    txouts.append(txout)
    if alt_amount:
        txout_alt = CMutableTxOut(
            round(alt_amount, 8) * COIN,
            CBitcoinAddress(alt_daddr).to_scriptPubKey())
        txouts.append(txout_alt)
    tx = CMutableTransaction(txins_obj, txouts)
    ntxins = len(txins_obj)
    seckey = CBitcoinSecret(rein.user.dkey)
    sig = ""
    for i in range(0, ntxins):
        sighash = SignatureHash(txin_redeemScript, tx, i, SIGHASH_ALL)
        sig += " " + b2x(seckey.sign(sighash)) + "01"
    if alt_amount:
        return (txins_str[1:], "{:.8f}".format(amount), daddr,
                "{:.8f}".format(alt_amount), alt_daddr, sig[1:])
    return (txins_str[1:], "{:.8f}".format(amount), daddr, sig[1:])
예제 #7
0
def partial_spend_p2sh_mediator_2 (redeemScript,txins_str,amount,daddr,rein):
    txin_redeemScript = CScript(x(redeemScript))
    txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey()
    txins_obj = []
    for txin_str in txins_str.split():
        txin_list = txin_str.split("-")
        txins_obj.append(CMutableTxIn(COutPoint(lx(txin_list[0]),int(txin_list[1]))))
    txout = CMutableTxOut(amount*COIN,CBitcoinAddress(daddr).to_scriptPubKey())
    tx = CMutableTransaction(txins_obj,[txout])
    seckey = CBitcoinSecret(rein.user.dkey)
    ntxins = len(txins_obj)
    for i in range(0,ntxins):
        sighash = SignatureHash(txin_redeemScript,tx,i,SIGHASH_ALL)
        sig = seckey.sign(sighash)+x("01")
        txins_obj[i].scriptSig = CScript([OP_0, sig, txin_redeemScript])
        #VerifyScript(txins_obj[i].scriptSig, txin_scriptPubKey, tx, i, (SCRIPT_VERIFY_P2SH,))
    tx_bytes = tx.serialize()
    return b2x(tx_bytes)
    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
예제 #9
0
    txins, [txout]
)  # maybe need to add parameter 'nLockTime=200'? It seems to work without this parameter, but slide 74 says it's needed
#print("raw unsigned transaction: " + b2x(tx.serialize()))

for index, txin in enumerate(txins):
    # Calculate the signature hash for that transaction. Note how the script we use
    # is the redeemScript, not the scriptPubKey. That's because when the CHECKSIG
    # operation happens EvalScript() will be evaluating the redeemScript, so the
    # corresponding SignatureHash() function will use that same script when it
    # replaces the scriptSig in the transaction being hashed with the script being
    # executed.
    sighash = SignatureHash(redeem_script, tx, index, 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.
    sig = secret.sign(sighash) + bytes([SIGHASH_ALL])

    # Set the scriptSig of our transaction input appropriately.
    txin.scriptSig = CScript([sig, redeem_script])
    # Verify the signature worked. This calls EvalScript() and actually executes
    # the opcodes in the scripts to see if everything worked out. If it doesn't an
    # exception will be raised.
    # this is commented out so that locktime requirement error is not hidden when this script is run before locktime requirement is satisfied
    # VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, index, (SCRIPT_VERIFY_P2SH,))

# Done setting up the transaction. Display it.
# print('transaction: ' + b2x(tx.serialize()))

# actually execute the transaction
transactionid = proxy.sendrawtransaction(tx)
# decode and print the resulting transaction id. Using bytes to little-endian hex function.
예제 #10
0
tx = CMutableTransaction([txin], [txout])
tx.nLockTime = nLockTime

# Calculate the signature hash for that transaction. Note how the script we use
# is the redeemScript, not the scriptPubKey. That's because when the CHECKSIG
# operation happens EvalScript() will be evaluating the redeemScript, so the
# corresponding SignatureHash() function will use that same script when it
# replaces the scriptSig in the transaction being hashed with the script being
# executed.
sighash = SignatureHash(txin_redeemScript, tx, 0, SIGHASH_ALL)
print("hash:", b2x(sighash))
print(b2x(bytes([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.
sig = seckey.sign(sighash) + bytes([SIGHASH_ALL])

print("sig:", b2x(sig))
# Set the scriptSig of our transaction input appropriately.
txin.scriptSig = CScript([sig, txin_redeemScript])

# Verify the signature worked. This calls EvalScript() and actually executes
# the opcodes in the scripts to see if everything worked out. If it doesn't an
# exception will be raised.
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH, ))

# Done! Print the transaction to standard output with the bytes-to-hex
# function.
txn = tx.serialize()
print("raw size:", len(txn))
print("txid:", b2lx(tx.GetTxid()))
 def sign_bounce_tx(self, sighash_hex):
     sighash = binascii.unhexlify(sighash_hex)
     seckey = CBitcoinSecret(self.key_pairs[self.my]["priv"]["wif"])
     sig = seckey.sign(sighash) + bytes([SIGHASH_ALL])
     return sig
    def build_claim_tx(self, txid_hex):
        if self.secret_info["plaintext"] == None:
            raise Exception("We don't know the secret yet.")

        #Create redeem script.
        redeem_script = self.fund_redeem_script(self.their)

        #Convert to p2sh address.
        their_fund_address = self.script_to_address(redeem_script, self.their)

        #Check there is enough in the p2sh address.
        their_fund_tx = self.jsonrpc[self.their].gettransaction(
            txid_hex)["details"]
        found = 0
        for tx_input in their_fund_tx:
            #Check it's the right input.
            if tx_input["address"] == their_fund_address:
                found = 1
                if tx_input["amount"] + self.recv_amount > decimal.Decimal(
                        coinbend.config["mining_fee"]["standard"]):
                    raise Exception(
                        "Their contract has not been sufficently funded.")
                break
            else:
                continue

        #Their side of the contract hasn't been funded.
        if not found:
            raise Exception("Their contract fund output was not detected.")

        #Generate address to receive redeemed output.
        if "receive" not in self.key_pairs:
            self.key_pairs["receive"] = self.key_pair_from_address(
                self.jsonrpc[self.their].getnewaddress(), self.their)

        #Load private key for signing.
        seckey = CBitcoinSecret(self.key_pairs[self.my]["priv"]["wif"])

        #Generate p2sh script pub key.
        redeem_script_hash160 = self.hash160_script(redeem_script)
        txin_script_pub_key = CScript(
            [OP_HASH160, redeem_script_hash160["bin"], OP_EQUAL])

        #Setup tx inputs and outputs.
        txid = lx(txid_hex)
        vout = 0
        txin = CTxIn(COutPoint(txid, vout))
        txout = CTxOut(
            (self.recv_amount -
             decimal.Decimal(coinbend.config["mining_fee"]["standard"])) *
            COIN,
            CBitcoinAddress(
                self.key_pairs["receive"]["addr"]["base58"]).to_scriptPubKey())

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

        #Sign transactions.
        sighash = SignatureHash(redeem_script["bin"], tx, 0, SIGHASH_ALL)
        sig = seckey.sign(sighash) + bytes([SIGHASH_ALL])
        txin.scriptSig = CScript([
            bytes(self.secret_info["plaintext"].encode("ascii")), sig, OP_3,
            redeem_script["bin"]
        ])

        #Return signed transaction hex.
        return b2x(tx.serialize())
예제 #13
0
# to a newly created address.
dest_addr = bitcoin_rpc.getnewaddress()
coinbase_amount = coinbase["vout"][0]["value"]
fee = 10000  # in satoshi
out_amount = coinbase_amount * COIN + burn_amount * COIN - fee
txout = CMutableTxOut(out_amount, CBitcoinAddress(dest_addr).to_scriptPubKey())

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

# Sign inputs. Start with the coinbase input.
coinbase_address = coinbase["vout"][0]["scriptPubKey"]["addresses"][0]
seckey_coinbase = CBitcoinSecret(bitcoin_rpc.dumpprivkey(coinbase_address))
sighash = SignatureHash(CScript(x(coinbase["vout"][0]["scriptPubKey"]["hex"])),
                        tx, 0, SIGHASH_ALL)
sig = seckey_coinbase.sign(sighash) + bytes([SIGHASH_ALL])
txin.scriptSig = CScript([sig])

# Sign "burn" address input.
sighash = SignatureHash(CScript(multisig_script), tx, 1, SIGHASH_ALL)
sig = seckey_multisig.sign(sighash) + bytes([SIGHASH_ALL])
txin2.scriptSig = CScript([OP_0, sig, redeemScript])
scriptPubKey = redeemScript.to_p2sh_scriptPubKey()

# Submit transaction to bitcoind, create a block and check received amount.
txid = bitcoin_rpc.sendrawtransaction(b2x(tx.serialize()))
bitcoin_rpc.generate(1)
print("Sent coins from \"burn\" address to own address (txid: %s)" % txid)
assert (int(bitcoin_rpc.getreceivedbyaddress(str(dest_addr)) *
            COIN) == out_amount)
print("Received %.8f (coinbase) + %.8f coins (\"burned\") - %.8f (fee)" %
예제 #14
0
    def pay(self,
            pay,
            change_address=None,
            allow_zero_conf=False,
            randomize_change_idx=True,
            fee_strategy='optimal'):
        send = {}

        if isinstance(pay, list):
            for address, value in pay:
                send[address] = value
        else:
            send = pay

        coin_selection = self.client.coin_selection(
            self.identifier,
            send,
            lockUTXO=True,
            allow_zero_conf=allow_zero_conf,
            fee_strategy=fee_strategy)

        utxos = coin_selection['utxos']
        fee = coin_selection['fee']
        change = coin_selection['change']

        if change > 0:
            if change_address is None:
                _, change_address = self.get_new_address_pair()

            send[change_address] = change

        txins = []
        for utxo in utxos:
            txins.append(CMutableTxIn(COutPoint(lx(utxo['hash']),
                                                utxo['idx'])))

        txouts = []
        change_txout = None
        for address, value in send.items():
            txout = CMutableTxOut(value,
                                  CBitcoinAddress(address).to_scriptPubKey())
            if address == change_address:
                change_txout = txout
            txouts.append(txout)

        # randomly move the change_txout
        if randomize_change_idx and change_txout:
            txouts.remove(change_txout)
            txouts.insert(random.randrange(len(txouts) + 1), change_txout)

        tx = CMutableTransaction(txins, txouts)

        for idx, utxo in enumerate(utxos):
            path = utxo['path'].replace("M/", "")

            key = self.primary_private_key.subkey_for_path(path)
            redeemScript = CScript(x(utxo['redeem_script']))
            sighash = SignatureHash(redeemScript, tx, idx, SIGHASH_ALL)

            ckey = CBitcoinSecret(key.wif())

            sig = ckey.sign(sighash) + struct.pack("B", SIGHASH_ALL)

            txins[idx].scriptSig = CScript([OP_0, sig, redeemScript])

        signed = self.client.send_transaction(self.identifier,
                                              b2x(tx.serialize()),
                                              [utxo['path'] for utxo in utxos],
                                              check_fee=True)

        return signed['txid']