Ejemplo n.º 1
0
    def add_tx_notify(self,
                      txd,
                      unconfirmfun,
                      confirmfun,
                      spentfun,
                      notifyaddr,
                      timeoutfun=None):
        if not self.notifythread:
            self.notifythread = BitcoinCoreNotifyThread(self)
            self.notifythread.start()
        one_addr_imported = False
        for outs in txd['outs']:
            addr = btc.script_to_address(outs['script'], get_p2pk_vbyte())
            if self.rpc('getaccount', [addr]) != '':
                one_addr_imported = True
                break
        if not one_addr_imported:
            self.rpc('importaddress', [notifyaddr, 'joinmarket-notify', False])
        tx_output_set = set([(sv['script'], sv['value'])
                             for sv in txd['outs']])
        self.txnotify_fun.append(
            (btc.txhash(btc.serialize(txd)), tx_output_set, unconfirmfun,
             confirmfun, spentfun, timeoutfun, False))

        #create unconfirm timeout here, create confirm timeout in the other thread
        if timeoutfun:
            threading.Timer(cs_single().config.getint('TIMEOUT',
                                                      'unconfirm_timeout_sec'),
                            bitcoincore_timeout_callback,
                            args=(False, tx_output_set, self.txnotify_fun,
                                  timeoutfun)).start()
Ejemplo n.º 2
0
def cli_receive(filename):
    wif_privkey = raw_input("Enter private key in WIF compressed format: ")
    try:
        privkey = btc.from_wif_privkey(wif_privkey, vbyte=get_p2pk_vbyte())
    except:
        print("Could not parse WIF privkey, quitting.")
        return
    amount = raw_input("Enter amount of utxo being spent, in satoshis: ")
    valid_coinjoins = scan_for_coinjoins(privkey, int(amount), filename)
    if not valid_coinjoins:
        print("Found no valid coinjoins")
        return
    for vc in valid_coinjoins:
        addr, priv, tx = vc
        print("Found signable coinjoin with destination address: ", addr)
        #TODO find a more sensible file naming
        fn = btc.txhash(tx) + ".txt"
        with open(fn, "wb") as f:
            f.write("SNICKER output file for receiver\n"
                    "================================\n")
            f.write("The serialized transaction in hex:\n")
            f.write(tx + "\n")
            f.write("YOUR DESTINATION: " + addr + "\n")
            f.write("PRIVATE KEY FOR THIS DESTINATION ADDRESS:\n")
            f.write(
                btc.wif_compressed_privkey(priv, vbyte=get_p2pk_vbyte()) +
                "\n")
            f.write("The decoded transaction:\n")
            f.write(pformat(btc.deserialize(tx)) + "\n")
        print("The partially signed transaction and the private key for your "
              "output are stored in the file: " + fn)
        print(
            "Pass the transaction hex to `signrawtransaction` in Bitcoin Core "
            "or similar if you wish to broadcast the transaction.")
Ejemplo n.º 3
0
 def watch_for_tx3_spends(self, redeeming_txid):
     """Function used to check whether our, or a competing
     tx, successfully spends out of TX3. Meant to be polled.
     """
     assert self.sm.state in [6, 7, 8]
     if self.tx3redeem.is_confirmed:
         self.carol_watcher_loop.stop()
         cslog.info(
             "Redeemed funds via TX3 OK, txid of redeeming transaction "
             "is: " + self.tx3redeem.txid)
         self.quit(complete=False, failed=False)
         return
     if self.tx3.is_spent:
         if btc.txhash(self.tx3.spending_tx) != redeeming_txid:
             cslog.info(
                 "Detected TX3 spent by other party; backing out to TX2")
             retval = self.find_secret_from_tx3_redeem()
             if not retval:
                 cslog.info(
                     "CRITICAL ERROR: Failed to find secret from TX3 redeem."
                 )
                 self.quit(False, True)
                 return
             rt2s_success = self.redeem_tx2_with_secret()
             self.quit(False, not rt2s_success)
             return
Ejemplo n.º 4
0
def make_sign_and_push(ins_full,
                       wallet,
                       amount,
                       output_addr=None,
                       change_addr=None,
                       hashcode=btc.SIGHASH_ALL,
                       estimate_fee = False):
    """Utility function for easily building transactions
    from wallets
    """
    total = sum(x['value'] for x in ins_full.values())
    ins = list(ins_full.keys())
    #random output address and change addr
    output_addr = wallet.get_new_addr(1, 1) if not output_addr else output_addr
    change_addr = wallet.get_new_addr(1, 0) if not change_addr else change_addr
    fee_est = estimate_tx_fee(len(ins), 2) if estimate_fee else 10000
    outs = [{'value': amount,
             'address': output_addr}, {'value': total - amount - fee_est,
                                       'address': change_addr}]

    de_tx = btc.deserialize(btc.mktx(ins, outs))
    scripts = {}
    for index, ins in enumerate(de_tx['ins']):
        utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index'])
        script = wallet.addr_to_script(ins_full[utxo]['address'])
        scripts[index] = (script, ins_full[utxo]['value'])
    binarize_tx(de_tx)
    de_tx = wallet.sign_tx(de_tx, scripts, hashcode=hashcode)
    #pushtx returns False on any error
    tx = binascii.hexlify(btc.serialize(de_tx)).decode('ascii')
    push_succeed = jm_single().bc_interface.pushtx(tx)
    if push_succeed:
        return btc.txhash(tx)
    else:
        return False
Ejemplo n.º 5
0
 def set_txid(self):
     """Creates the txid of the transaction,
     if it is fully signed, else an Exception is raised.
     This is used for passing to counterparty for in-advance
     signing (e.g. backouts, or here a whole chain).
     """
     assert self.fully_signed_tx
     self.txid = btc.txhash(self.fully_signed_tx)
 def outputs_watcher(self, wallet_name, notifyaddr, tx_output_set,
                     unconfirmfun, confirmfun, timeoutfun):
     """Given a key for the watcher loop (notifyaddr), a wallet name (label),
     a set of outputs, and unconfirm, confirm and timeout callbacks,
     check to see if a transaction matching that output set has appeared in
     the wallet. Call the callbacks and update the watcher loop state.
     End the loop when the confirmation has been seen (no spent monitoring here).
     """
     wl = self.tx_watcher_loops[notifyaddr]
     txlist = self.rpc("listtransactions", ["*", 100, 0, True])
     for tx in txlist[::-1]:
         #changed syntax in 0.14.0; allow both syntaxes
         try:
             res = self.rpc("gettransaction", [tx["txid"], True])
         except:
             try:
                 res = self.rpc("gettransaction", [tx["txid"], 1])
             except JsonRpcError as e:
                 #This should never happen (gettransaction is a wallet rpc).
                 log.warn("Failed gettransaction call; JsonRpcError")
                 res = None
             except Exception as e:
                 log.warn("Failed gettransaction call; unexpected error:")
                 log.warn(str(e))
                 res = None
         if not res:
             continue
         if "confirmations" not in res:
             log.debug("Malformed gettx result: " + str(res))
             return
         txd = self.get_deser_from_gettransaction(res)
         if txd is None:
             continue
         txos = set([(sv['script'], sv['value']) for sv in txd['outs']])
         if not txos == tx_output_set:
             continue
         #Here we have found a matching transaction in the wallet.
         real_txid = btc.txhash(btc.serialize(txd))
         if not wl[1] and res["confirmations"] == 0:
             log.debug("Tx: " + str(real_txid) + " seen on network.")
             unconfirmfun(txd, real_txid)
             wl[1] = True
             return
         if not wl[2] and res["confirmations"] > 0:
             log.debug("Tx: " + str(real_txid) + " has " +
                       str(res["confirmations"]) + " confirmations.")
             confirmfun(txd, real_txid, res["confirmations"])
             wl[2] = True
             wl[0].stop()
             return
         if res["confirmations"] < 0:
             log.debug("Tx: " + str(real_txid) +
                       " has a conflict. Abandoning.")
             wl[0].stop()
             return
Ejemplo n.º 7
0
 def broadcast_fallback(self):
     self.user_info("Broadcasting non-coinjoin fallback transaction.")
     txid = btc.txhash(self.fallback_tx)
     success = jm_single().bc_interface.pushtx(self.fallback_tx)
     if not success:
         self.user_info("ERROR: the fallback transaction did not broadcast. "
                   "The payment has NOT been made.")
     else:
         self.user_info("Payment received successfully, but it was NOT a coinjoin.")
         self.user_info("Txid: " + txid)
     reactor.stop()
Ejemplo n.º 8
0
def cli_broadcast(wallet_name, partial_tx_hex):
    """Given a partially signed transaction retrieved by running
    this script with the -r flag, and assuming that the utxo with
    which the transaction was made is in a Joinmarket wallet, this
    function will complete the signing and then broadcast the transaction.
    This function is useful if the *receiver*'s wallet is Joinmarket; if
    it is Core then the workflow is just `signrawtransaction` then
    `sendrawtransaction`; should be similar for Electrum although haven't tried.
    """
    wallet = cli_get_wallet(wallet_name)
    tx = btc.deserialize(partial_tx_hex)
    num_sigs = 0
    for index, ins in enumerate(tx['ins']):
        utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index'])
        #is the utxo in our utxos?
        in_wallet_utxos = wallet.get_utxos_by_mixdepth(False)
        for m, um in in_wallet_utxos.iteritems():
            for k, v in um.iteritems():
                if k == utxo:
                    print("Found utxo in mixdepth: ", m)
                    if isinstance(wallet, SegwitWallet):
                        amount = v['value']
                    else:
                        amount = None
                    signed_tx = btc.sign(partial_tx_hex,
                                         index,
                                         wallet.get_key_from_addr(
                                             v['address']),
                                         amount=amount)
                    num_sigs += 1
    if num_sigs != 1:
        print("Something wrong, expected to get 1 sig, got: ", num_sigs)
        return
    #should be fully signed; broadcast?
    print("Signed tx in hex:")
    print(signed_tx)
    print("In decoded form:")
    print(pformat(btc.deserialize(signed_tx)))
    if not raw_input("Broadcast to network? (y/n): ") == "y":
        print("You chose not to broadcast, quitting.")
        return
    txid = btc.txhash(signed_tx)
    print('txid = ' + txid)
    pushed = jm_single().bc_interface.pushtx(signed_tx)
    if not pushed:
        print("Broadcast failed.")
    else:
        print("Broadcast was successful.")
    def tx_watcher(self, txd, unconfirmfun, confirmfun, spentfun, c, n):
        """Called at a polling interval, checks if the given deserialized
        transaction (which must be fully signed) is (a) broadcast, (b) confirmed
        and (c) spent from. (c, n ignored in electrum version, just supports
        registering first confirmation).
        TODO: There is no handling of conflicts here.
        """
        txid = btc.txhash(btc.serialize(txd))
        wl = self.tx_watcher_loops[txid]
        #first check if in mempool (unconfirmed)
        #choose an output address for the query. Filter out
        #p2pkh addresses, assume p2sh (thus would fail to find tx on
        #some nonstandard script type)
        addr = None
        for i in range(len(txd['outs'])):
            if not btc.is_p2pkh_script(txd['outs'][i]['script']):
                addr = btc.script_to_address(txd['outs'][i]['script'],
                                             get_p2sh_vbyte())
                break
        if not addr:
            log.error("Failed to find any p2sh output, cannot be a standard "
                      "joinmarket transaction, fatal error!")
            reactor.stop()
            return
        unconftxs_res = self.get_from_electrum(
            'blockchain.address.get_mempool', addr,
            blocking=True).get('result')
        unconftxs = [str(t['tx_hash']) for t in unconftxs_res]

        if not wl[1] and txid in unconftxs:
            jmprint("Tx: " + str(txid) + " seen on network.", "info")
            unconfirmfun(txd, txid)
            wl[1] = True
            return
        conftx = self.get_from_electrum('blockchain.address.listunspent',
                                        addr,
                                        blocking=True).get('result')
        conftxs = [str(t['tx_hash']) for t in conftx]
        if not wl[2] and len(conftxs) and txid in conftxs:
            jmprint("Tx: " + str(txid) + " is confirmed.", "info")
            confirmfun(txd, txid, 1)
            wl[2] = True
            #Note we do not stop the monitoring loop when
            #confirmations occur, since we are also monitoring for spending.
            return
        if not spentfun or wl[3]:
            return
Ejemplo n.º 10
0
def make_sign_and_push(ins_full,
                       wallet_service,
                       amount,
                       output_addr=None,
                       change_addr=None,
                       hashcode=btc.SIGHASH_ALL,
                       estimate_fee=False):
    """Utility function for easily building transactions
    from wallets.
    """
    assert isinstance(wallet_service, WalletService)
    total = sum(x['value'] for x in ins_full.values())
    ins = ins_full.keys()
    #random output address and change addr
    output_addr = wallet_service.get_new_addr(
        1,
        BaseWallet.ADDRESS_TYPE_INTERNAL) if not output_addr else output_addr
    change_addr = wallet_service.get_new_addr(
        0,
        BaseWallet.ADDRESS_TYPE_INTERNAL) if not change_addr else change_addr
    fee_est = estimate_tx_fee(len(ins), 2) if estimate_fee else 10000
    outs = [{
        'value': amount,
        'address': output_addr
    }, {
        'value': total - amount - fee_est,
        'address': change_addr
    }]

    tx = btc.mktx(ins, outs)
    de_tx = btc.deserialize(tx)
    for index, ins in enumerate(de_tx['ins']):
        utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index'])
        addr = ins_full[utxo]['address']
        priv = wallet_service.get_key_from_addr(addr)
        if index % 2:
            priv = binascii.unhexlify(priv)
        tx = btc.sign(tx, index, priv, hashcode=hashcode)
    #pushtx returns False on any error
    print(btc.deserialize(tx))
    push_succeed = jm_single().bc_interface.pushtx(tx)
    if push_succeed:
        return btc.txhash(tx)
    else:
        return False
Ejemplo n.º 11
0
def make_sign_and_push(ins_full,
                       wallet_service,
                       amount,
                       output_addr=None,
                       change_addr=None,
                       hashcode=btc.SIGHASH_ALL,
                       estimate_fee=False):
    """Utility function for easily building transactions
    from wallets
    """
    assert isinstance(wallet_service, WalletService)
    total = sum(x['value'] for x in ins_full.values())
    ins = list(ins_full.keys())
    #random output address and change addr
    output_addr = wallet_service.get_new_addr(
        1, 1) if not output_addr else output_addr
    change_addr = wallet_service.get_new_addr(
        0, 1) if not change_addr else change_addr
    fee_est = estimate_tx_fee(len(ins), 2) if estimate_fee else 10000
    outs = [{
        'value': amount,
        'address': output_addr
    }, {
        'value': total - amount - fee_est,
        'address': change_addr
    }]

    de_tx = btc.deserialize(btc.mktx(ins, outs))
    scripts = {}
    for index, ins in enumerate(de_tx['ins']):
        utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index'])
        script = wallet_service.addr_to_script(ins_full[utxo]['address'])
        scripts[index] = (script, ins_full[utxo]['value'])
    binarize_tx(de_tx)
    de_tx = wallet_service.sign_tx(de_tx, scripts, hashcode=hashcode)
    #pushtx returns False on any error
    push_succeed = jm_single().bc_interface.pushtx(btc.serialize(de_tx))
    if push_succeed:
        txid = btc.txhash(btc.serialize(de_tx))
        # in normal operation this happens automatically
        # but in some tests there is no monitoring loop:
        wallet_service.process_new_tx(de_tx, txid)
        return txid
    else:
        return False
Ejemplo n.º 12
0
    def on_JM_TX_RECEIVED(self, nick, txhex, offer):
        # "none" flags p2ep protocol; pass through to the generic
        # on_tx handler for that:
        if offer == "none":
            return self.on_p2ep_tx_received(nick, txhex)

        offer = json.loads(offer)
        retval = self.client.on_tx_received(nick, txhex, offer)
        if not retval[0]:
            jlog.info("Maker refuses to continue on receipt of tx")
        else:
            sigs = retval[1]
            self.finalized_offers[nick] = offer
            tx = btc.deserialize(txhex)
            self.finalized_offers[nick]["txd"] = tx
            txid = btc.txhash(btc.serialize(tx))
            # we index the callback by the out-set of the transaction,
            # because the txid is not known until all scriptSigs collected
            # (hence this is required for Makers, but not Takers).
            # For more info see WalletService.transaction_monitor():
            txinfo = tuple((x["script"], x["value"]) for x in tx["outs"])
            self.client.wallet_service.register_callbacks(
                [self.unconfirm_callback], txinfo, "unconfirmed")
            self.client.wallet_service.register_callbacks(
                [self.confirm_callback], txinfo, "confirmed")

            task.deferLater(
                reactor,
                float(jm_single().config.getint("TIMEOUT",
                                                "unconfirm_timeout_sec")),
                self.client.wallet_service.check_callback_called, txinfo,
                self.unconfirm_callback, "unconfirmed",
                "transaction with outputs: " + str(txinfo) + " not broadcast.")

            d = self.callRemote(commands.JMTXSigs,
                                nick=nick,
                                sigs=json.dumps(sigs))
            self.defaultCallbacks(d)
        return {"accepted": True}
Ejemplo n.º 13
0
 def mktx(self):
     """First, construct input and output lists
     as for a normal transaction construction,
     using the OCCTemplateTx corresponding inputs
     and outputs as information.
     To do this completely requires txids for all inputs.
     Thus, this must be called for this OCCTx *after*
     it has been called for all parent txs.
     We ensure that the txid for this Tx is set here,
     and is attached to all the Outpoint objects for its
     outputs.
     """
     self.build_ins_from_template()
     self.build_outs_from_template()
     assert all([self.ins, self.outs])
     self.base_form = btc.mktx([x[0] for x in self.ins], self.outs)
     dtx = btc.deserialize(self.base_form)
     if self.locktime:
         dtx["ins"][0]["sequence"] = 0
         dtx["locktime"] = self.locktime
     #To set the txid, it's required that we set the
     #scriptSig and scriptPubkey objects. We don't yet
     #need to flag it segwit (we're not yet attaching
     #signatures) since we want txid not wtxid and the
     #former doesn't use segwit formatting anyway.
     for i, inp in enumerate(dtx["ins"]):
         sti = self.template.ins[i]
         if sti.spk_type == "p2sh-p2wpkh":
             inp["script"] = "16" + btc.pubkey_to_p2sh_p2wpkh_script(
                 self.keys["ins"][i][sti.counterparty])
         elif sti.spk_type == "NN":
             inp["script"] = ""
     self.txid = btc.txhash(btc.serialize(dtx))
     #by setting the txid of the outpoints, we allow child
     #transactions to know the outpoint references for their inputs.
     for to in self.template.outs:
         to.txid = self.txid
Ejemplo n.º 14
0
def direct_send(wallet,
                amount,
                mixdepth,
                destaddr,
                answeryes=False,
                accept_callback=None,
                info_callback=None):
    """Send coins directly from one mixdepth to one destination address;
    does not need IRC. Sweep as for normal sendpayment (set amount=0).
    If answeryes is True, callback/command line query is not performed.
    If accept_callback is None, command line input for acceptance is assumed,
    else this callback is called:
    accept_callback:
    ====
    args:
    deserialized tx, destination address, amount in satoshis, fee in satoshis
    returns:
    True if accepted, False if not
    ====
    The info_callback takes one parameter, the information message (when tx is
    pushed), and returns nothing.

    This function returns:
    The txid if transaction is pushed, False otherwise
    """
    #Sanity checks
    assert validate_address(destaddr)[0]
    assert isinstance(mixdepth, numbers.Integral)
    assert mixdepth >= 0
    assert isinstance(amount, numbers.Integral)
    assert amount >= 0
    assert isinstance(wallet, BaseWallet)

    from pprint import pformat
    txtype = wallet.get_txtype()
    if amount == 0:
        utxos = wallet.get_utxos_by_mixdepth()[mixdepth]
        if utxos == {}:
            log.error("There are no utxos in mixdepth: " + str(mixdepth) +
                      ", quitting.")
            return
        total_inputs_val = sum([va['value'] for u, va in iteritems(utxos)])
        fee_est = estimate_tx_fee(len(utxos), 1, txtype=txtype)
        outs = [{"address": destaddr, "value": total_inputs_val - fee_est}]
    else:
        #8 inputs to be conservative
        initial_fee_est = estimate_tx_fee(8, 2, txtype=txtype)
        utxos = wallet.select_utxos(mixdepth, amount + initial_fee_est)
        if len(utxos) < 8:
            fee_est = estimate_tx_fee(len(utxos), 2, txtype=txtype)
        else:
            fee_est = initial_fee_est
        total_inputs_val = sum([va['value'] for u, va in iteritems(utxos)])
        changeval = total_inputs_val - fee_est - amount
        outs = [{"value": amount, "address": destaddr}]
        change_addr = wallet.get_internal_addr(mixdepth)
        import_new_addresses(wallet, [change_addr])
        outs.append({"value": changeval, "address": change_addr})

    #Now ready to construct transaction
    log.info("Using a fee of : " + str(fee_est) + " satoshis.")
    if amount != 0:
        log.info("Using a change value of: " + str(changeval) + " satoshis.")
    txsigned = sign_tx(wallet, mktx(list(utxos.keys()), outs), utxos)
    log.info("Got signed transaction:\n")
    log.info(pformat(txsigned))
    tx = serialize(txsigned)
    log.info("In serialized form (for copy-paste):")
    log.info(tx)
    actual_amount = amount if amount != 0 else total_inputs_val - fee_est
    log.info("Sends: " + str(actual_amount) + " satoshis to address: " +
             destaddr)
    if not answeryes:
        if not accept_callback:
            if input(
                    'Would you like to push to the network? (y/n):')[0] != 'y':
                log.info(
                    "You chose not to broadcast the transaction, quitting.")
                return False
        else:
            accepted = accept_callback(pformat(txsigned), destaddr,
                                       actual_amount, fee_est)
            if not accepted:
                return False
    jm_single().bc_interface.pushtx(tx)
    txid = txhash(tx)
    successmsg = "Transaction sent: " + txid
    cb = log.info if not info_callback else info_callback
    cb(successmsg)
    return txid
Ejemplo n.º 15
0
def direct_send(wallet_service, amount, mixdepth, destination, answeryes=False,
                accept_callback=None, info_callback=None):
    """Send coins directly from one mixdepth to one destination address;
    does not need IRC. Sweep as for normal sendpayment (set amount=0).
    If answeryes is True, callback/command line query is not performed.
    If accept_callback is None, command line input for acceptance is assumed,
    else this callback is called:
    accept_callback:
    ====
    args:
    deserialized tx, destination address, amount in satoshis, fee in satoshis
    returns:
    True if accepted, False if not
    ====
    The info_callback takes one parameter, the information message (when tx is
    pushed), and returns nothing.

    This function returns:
    The txid if transaction is pushed, False otherwise
    """
    #Sanity checks
    assert validate_address(destination)[0] or is_burn_destination(destination)
    assert isinstance(mixdepth, numbers.Integral)
    assert mixdepth >= 0
    assert isinstance(amount, numbers.Integral)
    assert amount >=0
    assert isinstance(wallet_service.wallet, BaseWallet)

    if is_burn_destination(destination):
        #Additional checks
        if not isinstance(wallet_service.wallet, FidelityBondMixin):
            log.error("Only fidelity bond wallets can burn coins")
            return
        if answeryes:
            log.error("Burning coins not allowed without asking for confirmation")
            return
        if mixdepth != FidelityBondMixin.FIDELITY_BOND_MIXDEPTH:
            log.error("Burning coins only allowed from mixdepth " + str(
                FidelityBondMixin.FIDELITY_BOND_MIXDEPTH))
            return
        if amount != 0:
            log.error("Only sweeping allowed when burning coins, to keep the tx " +
                "small. Tip: use the coin control feature to freeze utxos")
            return

    from pprint import pformat
    txtype = wallet_service.get_txtype()
    if amount == 0:
        utxos = wallet_service.get_utxos_by_mixdepth()[mixdepth]
        if utxos == {}:
            log.error(
                "There are no available utxos in mixdepth: " + str(mixdepth) + ", quitting.")
            return

        total_inputs_val = sum([va['value'] for u, va in iteritems(utxos)])

        if is_burn_destination(destination):
            if len(utxos) > 1:
                log.error("Only one input allowed when burning coins, to keep "
                    + "the tx small. Tip: use the coin control feature to freeze utxos")
                return
            address_type = FidelityBondMixin.BIP32_BURN_ID
            index = wallet_service.wallet.get_next_unused_index(mixdepth, address_type)
            path = wallet_service.wallet.get_path(mixdepth, address_type, index)
            privkey, engine = wallet_service.wallet._get_key_from_path(path)
            pubkey = engine.privkey_to_pubkey(privkey)
            pubkeyhash = bin_hash160(pubkey)

            #size of burn output is slightly different from regular outputs
            burn_script = mk_burn_script(pubkeyhash) #in hex
            fee_est = estimate_tx_fee(len(utxos), 0, txtype=txtype, extra_bytes=len(burn_script)/2)

            outs = [{"script": burn_script, "value": total_inputs_val - fee_est}]
            destination = "BURNER OUTPUT embedding pubkey at " \
                + wallet_service.wallet.get_path_repr(path) \
                + "\n\nWARNING: This transaction if broadcasted will PERMANENTLY DESTROY your bitcoins\n"
        else:
            #regular send (non-burn)
            fee_est = estimate_tx_fee(len(utxos), 1, txtype=txtype)
            outs = [{"address": destination, "value": total_inputs_val - fee_est}]
    else:
        #8 inputs to be conservative
        initial_fee_est = estimate_tx_fee(8,2, txtype=txtype)
        utxos = wallet_service.select_utxos(mixdepth, amount + initial_fee_est)
        if len(utxos) < 8:
            fee_est = estimate_tx_fee(len(utxos), 2, txtype=txtype)
        else:
            fee_est = initial_fee_est
        total_inputs_val = sum([va['value'] for u, va in iteritems(utxos)])
        changeval = total_inputs_val - fee_est - amount
        outs = [{"value": amount, "address": destination}]
        change_addr = wallet_service.get_internal_addr(mixdepth)
        outs.append({"value": changeval, "address": change_addr})

    #compute transaction locktime, has special case for spending timelocked coins
    tx_locktime = compute_tx_locktime()
    if mixdepth == FidelityBondMixin.FIDELITY_BOND_MIXDEPTH and \
            isinstance(wallet_service.wallet, FidelityBondMixin):
        for outpoint, utxo in utxos.items():
            path = wallet_service.script_to_path(
                wallet_service.addr_to_script(utxo["address"]))
            if not FidelityBondMixin.is_timelocked_path(path):
                continue
            path_locktime = path[-1]
            tx_locktime = max(tx_locktime, path_locktime+1)
            #compute_tx_locktime() gives a locktime in terms of block height
            #timelocked addresses use unix time instead
            #OP_CHECKLOCKTIMEVERIFY can only compare like with like, so we
            #must use unix time as the transaction locktime

    #Now ready to construct transaction
    log.info("Using a fee of : " + amount_to_str(fee_est) + ".")
    if amount != 0:
        log.info("Using a change value of: " + amount_to_str(changeval) + ".")
    txsigned = sign_tx(wallet_service, make_shuffled_tx(
        list(utxos.keys()), outs, False, 2, tx_locktime), utxos)
    log.info("Got signed transaction:\n")
    log.info(pformat(txsigned))
    tx = serialize(txsigned)
    log.info("In serialized form (for copy-paste):")
    log.info(tx)
    actual_amount = amount if amount != 0 else total_inputs_val - fee_est
    log.info("Sends: " + amount_to_str(actual_amount) + " to destination: " + destination)
    if not answeryes:
        if not accept_callback:
            if input('Would you like to push to the network? (y/n):')[0] != 'y':
                log.info("You chose not to broadcast the transaction, quitting.")
                return False
        else:
            accepted = accept_callback(pformat(txsigned), destination, actual_amount,
                                       fee_est)
            if not accepted:
                return False
    jm_single().bc_interface.pushtx(tx)
    txid = txhash(tx)
    successmsg = "Transaction sent: " + txid
    cb = log.info if not info_callback else info_callback
    cb(successmsg)
    return txid
 def tx_watcher(self, txd, unconfirmfun, confirmfun, spentfun, c, n):
     """Called at a polling interval, checks if the given deserialized
     transaction (which must be fully signed) is (a) broadcast, (b) confirmed
     and (c) spent from at index n, and notifies confirmation if number
     of confs = c.
     TODO: Deal with conflicts correctly. Here just abandons monitoring.
     """
     txid = btc.txhash(btc.serialize(txd))
     wl = self.tx_watcher_loops[txid]
     try:
         res = self.rpc('gettransaction', [txid, True])
     except JsonRpcError as e:
         return
     if not res:
         return
     if "confirmations" not in res:
         log.debug("Malformed gettx result: " + str(res))
         return
     if not wl[1] and res["confirmations"] == 0:
         log.debug("Tx: " + str(txid) + " seen on network.")
         unconfirmfun(txd, txid)
         wl[1] = True
         return
     if not wl[2] and res["confirmations"] > 0:
         log.debug("Tx: " + str(txid) + " has " +
                   str(res["confirmations"]) + " confirmations.")
         confirmfun(txd, txid, res["confirmations"])
         if c <= res["confirmations"]:
             wl[2] = True
             #Note we do not stop the monitoring loop when
             #confirmations occur, since we are also monitoring for spending.
         return
     if res["confirmations"] < 0:
         log.debug("Tx: " + str(txid) + " has a conflict. Abandoning.")
         wl[0].stop()
         return
     if not spentfun or wl[3]:
         return
     #To trigger the spent callback, we check if this utxo outpoint appears in
     #listunspent output with 0 or more confirmations. Note that this requires
     #we have added the destination address to the watch-only wallet, otherwise
     #that outpoint will not be returned by listunspent.
     res2 = self.rpc('listunspent', [0, 999999])
     if not res2:
         return
     txunspent = False
     for r in res2:
         if "txid" not in r:
             continue
         if txid == r["txid"] and n == r["vout"]:
             txunspent = True
             break
     if not txunspent:
         #We need to find the transaction which spent this one;
         #assuming the address was added to the wallet, then this
         #transaction must be in the recent list retrieved via listunspent.
         #For each one, use gettransaction to check its inputs.
         #This is a bit expensive, but should only occur once.
         txlist = self.rpc("listtransactions", ["*", 1000, 0, True])
         for tx in txlist[::-1]:
             #changed syntax in 0.14.0; allow both syntaxes
             try:
                 res = self.rpc("gettransaction", [tx["txid"], True])
             except:
                 try:
                     res = self.rpc("gettransaction", [tx["txid"], 1])
                 except:
                     #This should never happen (gettransaction is a wallet rpc).
                     log.info("Failed any gettransaction call")
                     res = None
             if not res:
                 continue
             deser = self.get_deser_from_gettransaction(res)
             if deser is None:
                 continue
             for vin in deser["ins"]:
                 if not "outpoint" in vin:
                     #coinbases
                     continue
                 if vin["outpoint"]["hash"] == txid and vin["outpoint"][
                         "index"] == n:
                     #recover the deserialized form of the spending transaction.
                     log.info("We found a spending transaction: " + \
                                btc.txhash(binascii.unhexlify(res["hex"])))
                     res2 = self.rpc("gettransaction", [tx["txid"], True])
                     spending_deser = self.get_deser_from_gettransaction(
                         res2)
                     if not spending_deser:
                         log.info(
                             "ERROR: could not deserialize spending tx.")
                         #Should never happen, it's a parsing bug.
                         #No point continuing to monitor, we just hope we
                         #can extract the secret by scanning blocks.
                         wl[3] = True
                         return
                     spentfun(spending_deser, vin["outpoint"]["hash"])
                     wl[3] = True
                     return
    def add_tx_notify(self,
                      txd,
                      unconfirmfun,
                      confirmfun,
                      notifyaddr,
                      wallet_name=None,
                      timeoutfun=None,
                      spentfun=None,
                      txid_flag=True,
                      n=0,
                      c=1,
                      vb=None):
        """Given a deserialized transaction txd,
        callback functions for broadcast and confirmation of the transaction,
        an address to import, and a callback function for timeout, set up
        a polling loop to check for events on the transaction. Also optionally set
        to trigger "confirmed" callback on number of confirmations c. Also checks
        for spending (if spentfun is not None) of the outpoint n.
        If txid_flag is True, we create a watcher loop on the txid (hence only
        really usable in a segwit context, and only on fully formed transactions),
        else we create a watcher loop on the output set of the transaction (taken
        from the outs field of the txd).
        """
        if not vb:
            vb = get_p2pk_vbyte()
        if isinstance(self, BitcoinCoreInterface) or isinstance(
                self, RegtestBitcoinCoreInterface):
            #This code ensures that a walletnotify is triggered, by
            #ensuring that at least one of the output addresses is
            #imported into the wallet (note the sweep special case, where
            #none of the output addresses belong to me).
            one_addr_imported = False
            for outs in txd['outs']:
                addr = btc.script_to_address(outs['script'], vb)
                try:
                    if self.is_address_imported(addr):
                        one_addr_imported = True
                        break
                except JsonRpcError as e:
                    log.debug("Failed to getaccount for address: " + addr)
                    log.debug("This is normal for bech32 addresses.")
                    continue
            if not one_addr_imported:
                try:
                    self.rpc('importaddress',
                             [notifyaddr, 'joinmarket-notify', False])
                except JsonRpcError as e:
                    #In edge case of address already controlled
                    #by another account, warn but do not quit in middle of tx.
                    #Can occur if destination is owned in Core wallet.
                    if e.code == -4 and e.message == "The wallet already " + \
                       "contains the private key for this address or script":
                        log.warn("WARNING: Failed to import address: " +
                                 notifyaddr)
                    #No other error should be possible
                    else:
                        raise

        #Warning! In case of txid_flag false, this is *not* a valid txid,
        #but only a hash of an incomplete transaction serialization.
        txid = btc.txhash(btc.serialize(txd))
        if not txid_flag:
            tx_output_set = set([(sv['script'], sv['value'])
                                 for sv in txd['outs']])
            loop = task.LoopingCall(self.outputs_watcher, wallet_name,
                                    notifyaddr, tx_output_set, unconfirmfun,
                                    confirmfun, timeoutfun)
            log.debug("Created watcher loop for address: " + notifyaddr)
            loopkey = notifyaddr
        else:
            loop = task.LoopingCall(self.tx_watcher, txd, unconfirmfun,
                                    confirmfun, spentfun, c, n)
            log.debug("Created watcher loop for txid: " + txid)
            loopkey = txid
        self.tx_watcher_loops[loopkey] = [loop, False, False, False]
        #Hardcoded polling interval, but in any case it can be very short.
        loop.start(5.0)
        #Give up on un-broadcast transactions and broadcast but not confirmed
        #transactions as per settings in the config.
        reactor.callLater(
            float(jm_single().config.get("TIMEOUT", "unconfirm_timeout_sec")),
            self.tx_network_timeout, loopkey)
        confirm_timeout_sec = int(jm_single().config.get(
            "TIMEOUT", "confirm_timeout_hours")) * 3600
        reactor.callLater(confirm_timeout_sec, self.tx_timeout, txd, loopkey,
                          timeoutfun)