def query_utxo_set(self, txout, includeconf=False):
     self.current_height = self.get_from_electrum(
         "blockchain.numblocks.subscribe")['result']
     if not isinstance(txout, list):
         txout = [txout]
     utxos = [[t[:64],int(t[65:])] for t in txout]
     result = []
     for ut in utxos:
         address = self.get_from_electrum("blockchain.utxo.get_address", ut)['result']
         utxo_info = self.get_from_electrum("blockchain.address.listunspent", address)['result']
         utxo = None
         for u in utxo_info:
             if u['tx_hash'] == ut[0] and u['tx_pos'] == ut[1]:
                 utxo = u
         if utxo is None:
             raise Exception("UTXO Not Found")
         r = {
             'value': utxo['value'],
             'address': address,
             'script': btc.address_to_script(address)
         }
         if includeconf:
             if int(utxo['height']) in [0, -1]:
                 #-1 means unconfirmed inputs
                 r['confirms'] = 0
             else:
                 #+1 because if current height = tx height, that's 1 conf
                 r['confirms'] = int(self.current_height) - int(
                     utxo['height']) + 1            
         result.append(r)
     return result
Exemple #2
0
    def query_utxo_set(self, txout):
	"""Needed even for a simple dummy interface.
	"""
        if not isinstance(txout, list):
            txout = [txout]
        utxos = [[t[:64],int(t[65:])] for t in txout]
        result = []
        for ut in utxos:
	    address = self.wallet.network.synchronous_get(
	        ('blockchain.utxo.get_address', ut))
	    try:
		utxo_info = self.wallet.network.synchronous_get(
	        ("blockchain.address.listunspent", [address]))
	    except Exception as e:
		log.debug("Got exception calling listunspent: " + repr(e))
		raise
	    utxo = None
            for u in utxo_info:
                if u['tx_hash'] == ut[0] and u['tx_pos'] == ut[1]:
                    utxo = u
	    if utxo is None:
		raise Exception("UTXO Not Found")
	    r = {
	        'value': u['value'],
	        'address': address,
	        'script': btc.address_to_script(address)
	    }
	    result.append(r)
        return result
    def verify_unsigned_tx(self, txd, offerinfo):
        tx_utxo_set = set(ins['outpoint']['hash'] + ':' +
                          str(ins['outpoint']['index']) for ins in txd['ins'])

        utxos = offerinfo["utxos"]
        cjaddr = offerinfo["cjaddr"]
        cjaddr_script = btc.address_to_script(cjaddr)
        changeaddr = offerinfo["changeaddr"]
        changeaddr_script = btc.address_to_script(changeaddr)
        amount = offerinfo["amount"]
        cjfee = offerinfo["offer"]["cjfee"]
        txfee = offerinfo["offer"]["txfee"]
        ordertype = offerinfo["offer"]["ordertype"]
        my_utxo_set = set(utxos.keys())
        if not tx_utxo_set.issuperset(my_utxo_set):
            return (False, 'my utxos are not contained')

        my_total_in = sum([va['value'] for va in utxos.values()])
        real_cjfee = calc_cj_fee(ordertype, cjfee, amount)
        expected_change_value = (my_total_in - amount - txfee + real_cjfee)
        jlog.info('potentially earned = {}'.format(real_cjfee - txfee))
        jlog.info('mycjaddr, mychange = {}, {}'.format(cjaddr, changeaddr))

        times_seen_cj_addr = 0
        times_seen_change_addr = 0
        for outs in txd['outs']:
            if outs['script'] == cjaddr_script:
                times_seen_cj_addr += 1
                if outs['value'] != amount:
                    return (False, 'Wrong cj_amount. I expect ' + str(amount))
            if outs['script'] == changeaddr_script:
                times_seen_change_addr += 1
                if outs['value'] != expected_change_value:
                    return (False, 'wrong change, i expect ' +
                            str(expected_change_value))
        if times_seen_cj_addr != 1 or times_seen_change_addr != 1:
            fmt = ('cj or change addr not in tx '
                   'outputs once, #cjaddr={}, #chaddr={}').format
            return (False, (fmt(times_seen_cj_addr, times_seen_change_addr)))
        return (True, None)
 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)
Exemple #5
0
    def query_utxo_set(self, txout, includeconf=False):
        """Behaves as for Core; TODO make it faster if possible.
        Note in particular a failed connection should result in
        a result list containing at least one "None" which the
        caller can use as a flag for failure.
	"""
        self.current_height = self.wallet.network.blockchain.local_height
        if not isinstance(txout, list):
            txout = [txout]
        utxos = [[t[:64], int(t[65:])] for t in txout]
        result = []
        for ut in utxos:
            address = self.wallet.network.synchronous_get(
                ('blockchain.utxo.get_address', ut))
            try:
                utxo_info = self.wallet.network.synchronous_get(
                    ("blockchain.address.listunspent", [address]))
            except Exception as e:
                log.debug("Got exception calling listunspent: " + repr(e))
                raise
            utxo = None
            for u in utxo_info:
                if u['tx_hash'] == ut[0] and u['tx_pos'] == ut[1]:
                    utxo = u
            if utxo is None:
                result.append(None)
                continue
            r = {
                'value': u['value'],
                'address': address,
                'script': btc.address_to_script(address)
            }
            if includeconf:
                if int(u['height']) in [0, -1]:
                    #-1 means unconfirmed inputs
                    r['confirms'] = 0
                else:
                    #+1 because if current height = tx height, that's 1 conf
                    r['confirms'] = int(self.current_height) - int(
                        u['height']) + 1
            result.append(r)
        return result
    def verify_unsigned_tx(self, txd, offerinfo):
        """This code is security-critical.
        Before signing the transaction the Maker must ensure
        that all details are as expected, and most importantly
        that it receives the exact number of coins to expected
        in total. The data is taken from the offerinfo dict and
        compared with the serialized txhex.
        """
        tx_utxo_set = set(ins['outpoint']['hash'] + ':' +
                          str(ins['outpoint']['index']) for ins in txd['ins'])

        utxos = offerinfo["utxos"]
        cjaddr = offerinfo["cjaddr"]
        cjaddr_script = btc.address_to_script(cjaddr)
        changeaddr = offerinfo["changeaddr"]
        changeaddr_script = btc.address_to_script(changeaddr)
        #Note: this value is under the control of the Taker,
        #see comment below.
        amount = offerinfo["amount"]
        cjfee = offerinfo["offer"]["cjfee"]
        txfee = offerinfo["offer"]["txfee"]
        ordertype = offerinfo["offer"]["ordertype"]
        my_utxo_set = set(utxos.keys())
        if not tx_utxo_set.issuperset(my_utxo_set):
            return (False, 'my utxos are not contained')

        #The three lines below ensure that the Maker receives
        #back what he puts in, minus his bitcointxfee contribution,
        #plus his expected fee. These values are fully under
        #Maker control so no combination of messages from the Taker
        #can change them.
        #(mathematically: amount + expected_change_value is independent
        #of amount); there is not a (known) way for an attacker to
        #alter the amount (note: !fill resubmissions *overwrite*
        #the active_orders[dict] entry in daemon), but this is an
        #extra layer of safety.
        my_total_in = sum([va['value'] for va in utxos.values()])
        real_cjfee = calc_cj_fee(ordertype, cjfee, amount)
        expected_change_value = (my_total_in - amount - txfee + real_cjfee)
        jlog.info('potentially earned = {}'.format(real_cjfee - txfee))
        jlog.info('mycjaddr, mychange = {}, {}'.format(cjaddr, changeaddr))

        #The remaining checks are needed to ensure
        #that the coinjoin and change addresses occur
        #exactly once with the required amts, in the output.
        times_seen_cj_addr = 0
        times_seen_change_addr = 0
        for outs in txd['outs']:
            if outs['script'] == cjaddr_script:
                times_seen_cj_addr += 1
                if outs['value'] != amount:
                    return (False, 'Wrong cj_amount. I expect ' + str(amount))
            if outs['script'] == changeaddr_script:
                times_seen_change_addr += 1
                if outs['value'] != expected_change_value:
                    return (False, 'wrong change, i expect ' +
                            str(expected_change_value))
        if times_seen_cj_addr != 1 or times_seen_change_addr != 1:
            fmt = ('cj or change addr not in tx '
                   'outputs once, #cjaddr={}, #chaddr={}').format
            return (False, (fmt(times_seen_cj_addr, times_seen_change_addr)))
        return (True, None)