Example #1
0
    def push(self):
        tx = btc.serialize(self.latest_tx)
        log.debug('\n' + tx)
        self.txid = btc.txhash(tx)
        log.debug('txid = ' + self.txid)
        
        tx_broadcast = jm_single().config.get('POLICY', 'tx_broadcast')
        if tx_broadcast == 'self':
            pushed = jm_single().bc_interface.pushtx(tx)
        elif tx_broadcast in ['random-peer', 'not-self']:
            n = len(self.active_orders)
            if tx_broadcast == 'random-peer':
                i = random.randrange(n + 1)
            else:
                i = random.randrange(n)
            if i == n:
                pushed = jm_single().bc_interface.pushtx(tx)
            else:
                self.msgchan.push_tx(self.active_orders.keys()[i], tx)
                pushed = True
        elif tx_broadcast == 'random-maker':
            crow = self.db.execute(
                'SELECT DISTINCT counterparty FROM orderbook ORDER BY ' +
                'RANDOM() LIMIT 1;'
            ).fetchone()
            counterparty = crow['counterparty']
            log.debug('pushing tx to ' + counterparty)
            self.msgchan.push_tx(counterparty, tx)
            pushed = True

        if not pushed:
            log.debug('unable to pushtx')
        return pushed
    def self_sign(self):
        # now sign it ourselves
        our_inputs = {}
        for index, ins in enumerate(self.latest_tx['ins']):
            utxo = ins['outpoint']['hash'] + ':' + str(ins['outpoint']['index'])
            if utxo not in self.input_utxos.keys():
                continue
            script = self.wallet.addr_to_script(self.input_utxos[utxo]['address'])
            amount = self.input_utxos[utxo]['value']
            our_inputs[index] = (script, amount)

        # FIXME: ugly hack
        tx_bin = btc.deserialize(unhexlify(btc.serialize(self.latest_tx)))
        self.wallet.sign_tx(tx_bin, our_inputs)

        self.latest_tx = btc.deserialize(hexlify(btc.serialize(tx_bin)))
Example #3
0
 def push(self):
     tx = btc.serialize(self.latest_tx)
     jlog.debug('\n' + tx)
     self.txid = btc.txhash(tx)
     jlog.debug('txid = ' + self.txid)
     tx_broadcast = jm_single().config.get('POLICY', 'tx_broadcast')
     nick_to_use = None
     if tx_broadcast == 'self':
         pushed = jm_single().bc_interface.pushtx(tx)
     elif tx_broadcast in ['random-peer', 'not-self']:
         n = len(self.maker_utxo_data)
         if tx_broadcast == 'random-peer':
             i = random.randrange(n + 1)
         else:
             i = random.randrange(n)
         if i == n:
             pushed = jm_single().bc_interface.pushtx(tx)
         else:
             nick_to_use = self.maker_utxo_data.keys()[i]
             pushed = True
     else:
         jlog.info("Only self, random-peer and not-self broadcast "
                   "methods supported. Reverting to self-broadcast.")
         pushed = jm_single().bc_interface.pushtx(tx)
     if not pushed:
         self.on_finished_callback(False, fromtx=True)
     else:
         jm_single().bc_interface.add_tx_notify(self.latest_tx,
                                                self.unconfirm_callback,
                                                self.confirm_callback,
                                                self.my_cj_addr)
         if nick_to_use:
             return (nick_to_use, tx)
Example #4
0
    def push(self):
        tx = btc.serialize(self.latest_tx)
        log.debug('\n' + tx)
        self.txid = btc.txhash(tx)
        log.debug('txid = ' + self.txid)

        tx_broadcast = jm_single().config.get('POLICY', 'tx_broadcast')
        if tx_broadcast == 'self':
            pushed = jm_single().bc_interface.pushtx(tx)
        elif tx_broadcast in ['random-peer', 'not-self']:
            n = len(self.active_orders)
            if tx_broadcast == 'random-peer':
                i = random.randrange(n + 1)
            else:
                i = random.randrange(n)
            if i == n:
                pushed = jm_single().bc_interface.pushtx(tx)
            else:
                self.msgchan.push_tx(self.active_orders.keys()[i], tx)
                pushed = True
        elif tx_broadcast == 'random-maker':
            crow = self.db.execute(
                'SELECT DISTINCT counterparty FROM orderbook ORDER BY ' +
                'RANDOM() LIMIT 1;').fetchone()
            counterparty = crow['counterparty']
            log.debug('pushing tx to ' + counterparty)
            self.msgchan.push_tx(counterparty, tx)
            pushed = True

        if not pushed:
            log.debug('unable to pushtx')
        return pushed
Example #5
0
    def on_sig(self, nick, sigb64):
        """Processes transaction signatures from counterparties.
        Returns True if all signatures received correctly, else
        returns False
        """
        if self.aborted:
            return False
        sig = base64.b64decode(sigb64).encode('hex')
        inserted_sig = False
        txhex = btc.serialize(self.latest_tx)

        # batch retrieval of utxo data
        utxo = {}
        ctr = 0
        for index, ins in enumerate(self.latest_tx['ins']):
            utxo_for_checking = ins['outpoint']['hash'] + ':' + str(
                ins['outpoint']['index'])
            #'deadbeef' markers mean our own input scripts are not ''
            if (ins['script'] != ''):
                continue
            utxo[ctr] = [index, utxo_for_checking]
            ctr += 1
        utxo_data = jm_single().bc_interface.query_utxo_set(
            [x[1] for x in utxo.values()])

        # insert signatures
        for i, u in utxo.iteritems():
            if utxo_data[i] is None:
                continue
            sig_good = btc.verify_tx_input(txhex, u[0], utxo_data[i]['script'],
                                           *btc.deserialize_script(sig))
            if sig_good:
                jlog.debug('found good sig at index=%d' % (u[0]))
                self.latest_tx['ins'][u[0]]['script'] = sig
                inserted_sig = True
                # check if maker has sent everything possible
                self.utxos[nick].remove(u[1])
                if len(self.utxos[nick]) == 0:
                    jlog.debug(('nick = {} sent all sigs, removing from '
                                'nonrespondant list').format(nick))
                    self.nonrespondants.remove(nick)
                break
        if not inserted_sig:
            jlog.debug('signature did not match anything in the tx')
            # TODO what if the signature doesnt match anything
            # nothing really to do except drop it, carry on and wonder why the
            # other guy sent a failed signature

        tx_signed = True
        for ins in self.latest_tx['ins']:
            if ins['script'] == '':
                tx_signed = False
        if not tx_signed:
            return False
        assert not len(self.nonrespondants)
        jlog.debug('all makers have sent their signatures')
        self.taker_info_callback("INFO", "Transaction is valid, signing..")
        jlog.debug("schedule item was: " +
                   str(self.schedule[self.schedule_index]))
        return self.self_sign_and_push()
Example #6
0
def sign_donation_tx(tx, i, priv):
    from bitcoin.main import fast_multiply, decode_privkey, G, inv, N
    from bitcoin.transaction import der_encode_sig
    k = sign_k
    hashcode = btc.SIGHASH_ALL
    i = int(i)
    if len(priv) <= 33:
        priv = btc.safe_hexlify(priv)
    pub = btc.privkey_to_pubkey(priv)
    address = btc.pubkey_to_address(pub)
    signing_tx = btc.signature_form(tx, i, btc.mk_pubkey_script(address),
                                    hashcode)

    msghash = btc.bin_txhash(signing_tx, hashcode)
    z = btc.hash_to_int(msghash)
    # k = deterministic_generate_k(msghash, priv)
    r, y = fast_multiply(G, k)
    s = inv(k, N) * (z + r * decode_privkey(priv)) % N
    rawsig = 27 + (y % 2), r, s

    sig = der_encode_sig(*rawsig) + btc.encode(hashcode, 16, 2)
    # sig = ecdsa_tx_sign(signing_tx, priv, hashcode)
    txobj = btc.deserialize(tx)
    txobj["ins"][i]["script"] = btc.serialize_script([sig, pub])
    return btc.serialize(txobj)
 def self_sign(self):
     # now sign it ourselves
     tx = btc.serialize(self.latest_tx)
     if self.sign_method == "wallet":
         #Currently passes addresses of to-be-signed inputs
         #to backend wallet; this is correct for Electrum, may need
         #different info for other backends.
         addrs = {}
         for index, ins in enumerate(self.latest_tx['ins']):
             utxo = ins['outpoint']['hash'] + ':' + str(
                 ins['outpoint']['index'])
             if utxo not in self.input_utxos.keys():
                 continue
             addrs[index] = self.input_utxos[utxo]['address']
         tx = self.wallet.sign_tx(tx, addrs)
     else:
         for index, ins in enumerate(self.latest_tx['ins']):
             utxo = ins['outpoint']['hash'] + ':' + str(
                 ins['outpoint']['index'])
             if utxo not in self.input_utxos.keys():
                 continue
             addr = self.input_utxos[utxo]['address']
             tx = self.sign_tx(tx, index,
                               self.wallet.get_key_from_addr(addr))
     self.latest_tx = btc.deserialize(tx)
Example #8
0
def sign_donation_tx(tx, i, priv):
    from bitcoin.main import fast_multiply, decode_privkey, G, inv, N
    from bitcoin.transaction import der_encode_sig
    k = sign_k
    hashcode = btc.SIGHASH_ALL
    i = int(i)
    if len(priv) <= 33:
        priv = btc.safe_hexlify(priv)
    pub = btc.privkey_to_pubkey(priv)
    address = btc.pubkey_to_address(pub)
    signing_tx = btc.signature_form(
            tx, i, btc.mk_pubkey_script(address), hashcode)

    msghash = btc.bin_txhash(signing_tx, hashcode)
    z = btc.hash_to_int(msghash)
    # k = deterministic_generate_k(msghash, priv)
    r, y = fast_multiply(G, k)
    s = inv(k, N) * (z + r * decode_privkey(priv)) % N
    rawsig = 27 + (y % 2), r, s

    sig = der_encode_sig(*rawsig) + btc.encode(hashcode, 16, 2)
    # sig = ecdsa_tx_sign(signing_tx, priv, hashcode)
    txobj = btc.deserialize(tx)
    txobj["ins"][i]["script"] = btc.serialize_script([sig, pub])
    return btc.serialize(txobj)
Example #9
0
 def outputs_watcher(self, wallet_name, notifyaddr, tx_output_set,
                     unconfirmfun, confirmfun, timeoutfun):
     """Given a key for the watcher loop (notifyaddr), a wallet name (account),
     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]
     account_name = wallet_name if wallet_name else "*"
     txlist = self.rpc("listtransactions", [wallet_name, 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.info("Failed any gettransaction call")
                 res = None
             except Exception as e:
                 log.info(str(e))
         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
 def push(self):
     tx = btc.serialize(self.latest_tx)
     jlog.debug('\n' + tx)
     self.txid = btc.txhash(tx)
     jlog.debug('txid = ' + self.txid)
     pushed = jm_single().bc_interface.pushtx(tx)
     if not pushed:
         self.on_finished_callback(False, fromtx=True)
     else:
         jm_single().bc_interface.add_tx_notify(self.latest_tx,
                                                self.unconfirm_callback,
                                                self.confirm_callback,
                                                self.my_cj_addr)
Example #11
0
    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:
            print("Tx: " + str(txid) + " seen on network.")
            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:
            print("Tx: " + str(txid) + " is confirmed.")
            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
Example #12
0
 def self_sign(self):
     # now sign it ourselves
     tx = btc.serialize(self.latest_tx)
     if isinstance(jm_single().bc_interface, ElectrumWalletInterface):
         #can defer signing to our wallet; it will handle
         #all relevant inputs
         tx = self.wallet.sign_tx(tx)
         log.debug("Back in self_sign in cjtx, tx is: " + str(tx))
     else:
         for index, ins in enumerate(self.latest_tx['ins']):
             utxo = ins['outpoint']['hash'] + ':' + str(
                     ins['outpoint']['index'])
             if utxo not in self.input_utxos.keys():
                 continue
             addr = self.input_utxos[utxo]['address']
             tx = self.sign_tx(tx, index, self.wallet.get_key_from_addr(addr))
     self.latest_tx = btc.deserialize(tx)
 def push(self):
     tx = btc.serialize(self.latest_tx)
     jlog.debug('\n' + tx)
     self.txid = btc.txhash(tx)
     jlog.info('txid = ' + self.txid)
     #If we are sending to a bech32 address, in case of sweep, will
     #need to use that bech32 for address import, which requires
     #converting to script (Core does not allow import of bech32)
     if self.my_cj_addr.lower()[:2] in ['bc', 'tb']:
         notify_addr = btc.address_to_script(self.my_cj_addr)
     else:
         notify_addr = self.my_cj_addr
     #add the txnotify callbacks *before* pushing in case the
     #walletnotify is triggered before the notify callbacks are set up;
     #this does leave a dangling notify callback if the push fails, but
     #that doesn't cause problems.
     jm_single().bc_interface.add_tx_notify(self.latest_tx,
                                            self.unconfirm_callback,
                                            self.confirm_callback,
                                            notify_addr,
                                            vb=get_p2sh_vbyte())
     tx_broadcast = jm_single().config.get('POLICY', 'tx_broadcast')
     nick_to_use = None
     if tx_broadcast == 'self':
         pushed = jm_single().bc_interface.pushtx(tx)
     elif tx_broadcast in ['random-peer', 'not-self']:
         n = len(self.maker_utxo_data)
         if tx_broadcast == 'random-peer':
             i = random.randrange(n + 1)
         else:
             i = random.randrange(n)
         if i == n:
             pushed = jm_single().bc_interface.pushtx(tx)
         else:
             nick_to_use = self.maker_utxo_data.keys()[i]
             pushed = True
     else:
         jlog.info("Only self, random-peer and not-self broadcast "
                   "methods supported. Reverting to self-broadcast.")
         pushed = jm_single().bc_interface.pushtx(tx)
     if not pushed:
         self.on_finished_callback(False, fromtx=True)
     else:
         if nick_to_use:
             return (nick_to_use, tx)
Example #14
0
 def self_sign(self):
     # now sign it ourselves
     tx = btc.serialize(self.latest_tx)
     if isinstance(jm_single().bc_interface, ElectrumWalletInterface):
         #can defer signing to our wallet; it will handle
         #all relevant inputs
         tx = self.wallet.sign_tx(tx)
         log.debug("Back in self_sign in cjtx, tx is: " + str(tx))
     else:
         for index, ins in enumerate(self.latest_tx['ins']):
             utxo = ins['outpoint']['hash'] + ':' + str(
                 ins['outpoint']['index'])
             if utxo not in self.input_utxos.keys():
                 continue
             addr = self.input_utxos[utxo]['address']
             tx = self.sign_tx(tx, index,
                               self.wallet.get_key_from_addr(addr))
     self.latest_tx = btc.deserialize(tx)
Example #15
0
 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
Example #16
0
    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)
                if self.rpc('getaccount', [addr]) != '':
                    one_addr_imported = True
                    break
            if not one_addr_imported:
                self.rpc('importaddress',
                         [notifyaddr, 'joinmarket-notify', False])

        #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)
Example #17
0
    def add_signature(self, nick, sigb64):
        if nick not in self.nonrespondants:
            log.debug(
                ('add_signature => nick={} '
                 'not in nonrespondants {}').format(nick, self.nonrespondants))
            return
        sig = base64.b64decode(sigb64).encode('hex')
        inserted_sig = False
        txhex = btc.serialize(self.latest_tx)

        # batch retrieval of utxo data
        utxo = {}
        ctr = 0
        for index, ins in enumerate(self.latest_tx['ins']):
            utxo_for_checking = ins['outpoint']['hash'] + ':' + str(
                ins['outpoint']['index'])
            if (ins['script'] != ''
                    or utxo_for_checking in self.input_utxos.keys()):
                continue
            utxo[ctr] = [index, utxo_for_checking]
            ctr += 1
        utxo_data = jm_single().bc_interface.query_utxo_set(
            [x[1] for x in utxo.values()])

        # insert signatures
        for i, u in utxo.iteritems():
            if utxo_data[i] is None:
                continue
            sig_good = btc.verify_tx_input(txhex, u[0], utxo_data[i]['script'],
                                           *btc.deserialize_script(sig))
            if sig_good:
                log.debug('found good sig at index=%d' % (u[0]))
                self.latest_tx['ins'][u[0]]['script'] = sig
                inserted_sig = True
                # check if maker has sent everything possible
                self.utxos[nick].remove(u[1])
                if len(self.utxos[nick]) == 0:
                    log.debug(('nick = {} sent all sigs, removing from '
                               'nonrespondant list').format(nick))
                    self.nonrespondants.remove(nick)
                break
        if not inserted_sig:
            log.debug('signature did not match anything in the tx')
            # TODO what if the signature doesnt match anything
            # nothing really to do except drop it, carry on and wonder why the
            # other guy sent a failed signature

        tx_signed = True
        for ins in self.latest_tx['ins']:
            if ins['script'] == '':
                tx_signed = False
        if not tx_signed:
            return
        self.end_timeout_thread = True
        self.all_responded = True
        with self.timeout_lock:
            self.timeout_lock.notify()
        log.debug('all makers have sent their signatures')
        for index, ins in enumerate(self.latest_tx['ins']):
            # remove placeholders
            if ins['script'] == 'deadbeef':
                ins['script'] = ''
        if self.finishcallback is not None:
            log.debug("About to call finishcallback")
            self.finishcallback(self)
Example #18
0
    def add_signature(self, nick, sigb64):
        if nick not in self.nonrespondants:
            log.debug(('add_signature => nick={} '
                       'not in nonrespondants {}').format(
                    nick, self.nonrespondants))
            return
        sig = base64.b64decode(sigb64).encode('hex')
        inserted_sig = False
        txhex = btc.serialize(self.latest_tx)

        # batch retrieval of utxo data
        utxo = {}
        ctr = 0
        for index, ins in enumerate(self.latest_tx['ins']):
            utxo_for_checking = ins['outpoint']['hash'] + ':' + str(
                    ins['outpoint']['index'])
            if (ins['script'] != '' or
                        utxo_for_checking in self.input_utxos.keys()):
                continue
            utxo[ctr] = [index, utxo_for_checking]
            ctr += 1
        utxo_data = jm_single().bc_interface.query_utxo_set(
                [x[1] for x in utxo.values()])

        # insert signatures
        for i, u in utxo.iteritems():
            if utxo_data[i] is None:
                continue
            sig_good = btc.verify_tx_input(
                    txhex, u[0], utxo_data[i]['script'],
                    *btc.deserialize_script(sig))
            if sig_good:
                log.debug('found good sig at index=%d' % (u[0]))
                self.latest_tx['ins'][u[0]]['script'] = sig
                inserted_sig = True
                # check if maker has sent everything possible
                self.utxos[nick].remove(u[1])
                if len(self.utxos[nick]) == 0:
                    log.debug(('nick = {} sent all sigs, removing from '
                               'nonrespondant list').format(nick))
                    self.nonrespondants.remove(nick)
                break
        if not inserted_sig:
            log.debug('signature did not match anything in the tx')
            # TODO what if the signature doesnt match anything
            # nothing really to do except drop it, carry on and wonder why the
            # other guy sent a failed signature

        tx_signed = True
        for ins in self.latest_tx['ins']:
            if ins['script'] == '':
                tx_signed = False
        if not tx_signed:
            return
        self.end_timeout_thread = True
        self.all_responded = True
        with self.timeout_lock:
            self.timeout_lock.notify()
        log.debug('all makers have sent their signatures')
        for index, ins in enumerate(self.latest_tx['ins']):
            # remove placeholders
            if ins['script'] == 'deadbeef':
                ins['script'] = ''
        if self.finishcallback is not None:
            log.debug("About to call finishcallback")
            self.finishcallback(self)
    def on_sig(self, nick, sigb64):
        """Processes transaction signatures from counterparties.
        Returns True if all signatures received correctly, else
        returns False
        """
        if self.aborted:
            return False
        sig = base64.b64decode(sigb64).encode('hex')
        inserted_sig = False
        txhex = btc.serialize(self.latest_tx)

        # batch retrieval of utxo data
        utxo = {}
        ctr = 0
        for index, ins in enumerate(self.latest_tx['ins']):
            utxo_for_checking = ins['outpoint']['hash'] + ':' + str(
                ins['outpoint']['index'])
            #'deadbeef' markers mean our own input scripts are not ''
            if (ins['script'] != ''):
                continue
            utxo[ctr] = [index, utxo_for_checking]
            ctr += 1
        utxo_data = jm_single().bc_interface.query_utxo_set(
            [x[1] for x in utxo.values()])

        # insert signatures
        for i, u in utxo.iteritems():
            if utxo_data[i] is None:
                continue
            #Check if the sender serialize_scripted the witness
            #item into the sig message; if so, also pick up the amount
            #from the utxo data retrieved from the blockchain to verify
            #the segwit-style signature. Note that this allows a mixed
            #SW/non-SW transaction as each utxo is interpreted separately.
            sig_deserialized = btc.deserialize_script(sig)
            if len(sig_deserialized) == 2:
                ver_sig, ver_pub = sig_deserialized
                wit = None
            elif len(sig_deserialized) == 3:
                ver_sig, ver_pub, wit = sig_deserialized
            else:
                jlog.debug("Invalid signature message - more than 3 items")
                break
            ver_amt = utxo_data[i]['value'] if wit else None
            sig_good = btc.verify_tx_input(txhex,
                                           u[0],
                                           utxo_data[i]['script'],
                                           ver_sig,
                                           ver_pub,
                                           witness=wit,
                                           amount=ver_amt)
            if sig_good:
                jlog.debug('found good sig at index=%d' % (u[0]))
                if wit:
                    self.latest_tx["ins"][u[0]]["txinwitness"] = [
                        ver_sig, ver_pub
                    ]
                    self.latest_tx["ins"][u[0]]["script"] = "16" + wit
                else:
                    self.latest_tx["ins"][u[0]]["script"] = sig
                inserted_sig = True
                # check if maker has sent everything possible
                self.utxos[nick].remove(u[1])
                if len(self.utxos[nick]) == 0:
                    jlog.debug(('nick = {} sent all sigs, removing from '
                                'nonrespondant list').format(nick))
                    self.nonrespondants.remove(nick)
                break
        if not inserted_sig:
            jlog.debug('signature did not match anything in the tx')
            # TODO what if the signature doesnt match anything
            # nothing really to do except drop it, carry on and wonder why the
            # other guy sent a failed signature

        tx_signed = True
        for ins in self.latest_tx['ins']:
            if ins['script'] == '':
                tx_signed = False
        if not tx_signed:
            return False
        assert not len(self.nonrespondants)
        jlog.info('all makers have sent their signatures')
        self.taker_info_callback("INFO", "Transaction is valid, signing..")
        jlog.debug("schedule item was: " +
                   str(self.schedule[self.schedule_index]))
        return self.self_sign_and_push()