Exemplo n.º 1
0
 def script_to_address(self, script):
     """Return the address for a given output script,
     which will be p2sh-p2wpkh for the segwit (currently).
     The underlying witness is however invisible at this layer;
     so it's just a p2sh address.
     """
     return btc.script_to_address(script, get_p2sh_vbyte())
Exemplo n.º 2
0
 def on_tx_confirmed(self, cjorder, confirmations, txid):
     to_announce = []
     for i, out in enumerate(cjorder.tx['outs']):
         addr = btc.script_to_address(out['script'], get_p2pk_vbyte())
         if addr == cjorder.change_addr:
             neworder = {
                 'oid': self.get_next_oid(),
                 'ordertype': 'absorder',
                 'minsize': 12000,
                 'maxsize': out['value'],
                 'txfee': 10000,
                 'cjfee': 100000,
                 'utxo': txid + ':' + str(i)
             }
             to_announce.append(neworder)
         if addr == cjorder.cj_addr:
             neworder = {
                 'oid': self.get_next_oid(),
                 'ordertype': 'absorder',
                 'minsize': 12000,
                 'maxsize': out['value'],
                 'txfee': 10000,
                 'cjfee': 100000,
                 'utxo': txid + ':' + str(i)
             }
             to_announce.append(neworder)
     return [], to_announce
 def __init__(self, blockchaininterface, txd, unconfirmfun, confirmfun):
     threading.Thread.__init__(self)
     self.daemon = True
     self.blockchaininterface = blockchaininterface
     self.unconfirmfun = unconfirmfun
     self.confirmfun = confirmfun
     self.tx_output_set = set([(sv['script'], sv['value']) for sv in txd['outs']])
     self.output_addresses = [btc.script_to_address(scrval[0], get_p2pk_vbyte()) for scrval in self.tx_output_set]
     log.debug('txoutset=' + pprint.pformat(self.tx_output_set))
     log.debug('outaddrs=' + ','.join(self.output_addresses))
Exemplo n.º 4
0
 def add_new_utxos(self, tx, txid):
     added_utxos = {}
     for index, outs in enumerate(tx['outs']):
         addr = btc.script_to_address(outs['script'], self.get_vbyte())
         if addr not in self.addr_cache:
             continue
         addrdict = {'address': addr, 'value': outs['value']}
         utxo = txid + ':' + str(index)
         added_utxos[utxo] = addrdict
         self.unspent[utxo] = addrdict
     log.debug('added utxos, wallet now is \n' +
               pprint.pformat(self.get_utxos_by_mixdepth()))
     return added_utxos
Exemplo n.º 5
0
 def add_new_utxos(self, tx, txid):
     added_utxos = {}
     for index, outs in enumerate(tx['outs']):
         addr = btc.script_to_address(outs['script'], get_p2pk_vbyte())
         if addr not in self.addr_cache:
             continue
         addrdict = {'address': addr, 'value': outs['value']}
         utxo = txid + ':' + str(index)
         added_utxos[utxo] = addrdict
         self.unspent[utxo] = addrdict
     log.debug('added utxos, wallet now is \n' + pprint.pformat(
             self.get_utxos_by_mixdepth()))
     return added_utxos
 def __init__(self, blockr_domain, txd, unconfirmfun, confirmfun, timeoutfun):
     threading.Thread.__init__(self, name='BlockrNotifyThread')
     self.daemon = True
     self.blockr_domain = blockr_domain
     self.unconfirmfun = unconfirmfun
     self.confirmfun = confirmfun
     self.timeoutfun = timeoutfun
     self.tx_output_set = set([(sv['script'], sv['value'])
                               for sv in txd['outs']])
     self.output_addresses = [
         btc.script_to_address(scrval[0], get_p2pk_vbyte())
         for scrval in self.tx_output_set]
     log.debug('txoutset=' + pprint.pformat(self.tx_output_set))
     log.debug('outaddrs=' + ','.join(self.output_addresses))
Exemplo n.º 7
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
Exemplo n.º 8
0
    def verify_unsigned_tx(self, txd):
        tx_utxo_set = set(ins['outpoint']['hash'] + ':' + str(
                ins['outpoint']['index']) for ins in txd['ins'])
        # complete authentication: check the tx input uses the authing pubkey
        input_utxo_data = jm_single().bc_interface.query_utxo_set(
                list(tx_utxo_set))

        if None in input_utxo_data:
            return False, 'some utxos already spent or not confirmed yet'
        input_addresses = [u['address'] for u in input_utxo_data]
        if btc.pubtoaddr(
                self.i_utxo_pubkey, get_p2pk_vbyte()) not in input_addresses:
            return False, "authenticating bitcoin address is not contained"

        my_utxo_set = set(self.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 self.utxos.values()])
        self.real_cjfee = calc_cj_fee(
                self.ordertype, self.cjfee, self.cj_amount)
        expected_change_value = (
            my_total_in - self.cj_amount - self.txfee + self.real_cjfee)
        log.debug('potentially earned = {}'.format(
                self.real_cjfee - self.txfee))
        log.debug('mycjaddr, mychange = {}, {}'.format(
                self.cj_addr, self.change_addr))

        times_seen_cj_addr = 0
        times_seen_change_addr = 0
        for outs in txd['outs']:
            addr = btc.script_to_address(outs['script'], get_p2pk_vbyte())
            if addr == self.cj_addr:
                times_seen_cj_addr += 1
                if outs['value'] != self.cj_amount:
                    return False, 'Wrong cj_amount. I expect ' + str(
                            self.cj_amount)
            if addr == self.change_addr:
                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
Exemplo n.º 9
0
    def verify_unsigned_tx(self, txd):
        tx_utxo_set = set(ins['outpoint']['hash'] + ':' +
                          str(ins['outpoint']['index']) for ins in txd['ins'])
        # complete authentication: check the tx input uses the authing pubkey
        input_utxo_data = jm_single().bc_interface.query_utxo_set(
            list(tx_utxo_set))

        if None in input_utxo_data:
            return False, 'some utxos already spent or not confirmed yet'
        input_addresses = [u['address'] for u in input_utxo_data]
        if btc.pubtoaddr(self.i_utxo_pubkey,
                         get_p2pk_vbyte()) not in input_addresses:
            return False, "authenticating bitcoin address is not contained"

        my_utxo_set = set(self.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 self.utxos.values()])
        self.real_cjfee = calc_cj_fee(self.ordertype, self.cjfee,
                                      self.cj_amount)
        expected_change_value = (my_total_in - self.cj_amount - self.txfee +
                                 self.real_cjfee)
        log.debug('potentially earned = {}'.format(self.real_cjfee -
                                                   self.txfee))
        log.debug('mycjaddr, mychange = {}, {}'.format(self.cj_addr,
                                                       self.change_addr))

        times_seen_cj_addr = 0
        times_seen_change_addr = 0
        for outs in txd['outs']:
            addr = btc.script_to_address(outs['script'], get_p2pk_vbyte())
            if addr == self.cj_addr:
                times_seen_cj_addr += 1
                if outs['value'] != self.cj_amount:
                    return False, 'Wrong cj_amount. I expect ' + str(
                        self.cj_amount)
            if addr == self.change_addr:
                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
Exemplo n.º 10
0
    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"]
        changeaddr = offerinfo["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']:
            addr = btc.script_to_address(outs['script'], get_p2sh_vbyte())
            if addr == cjaddr:
                times_seen_cj_addr += 1
                if outs['value'] != amount:
                    return (False, 'Wrong cj_amount. I expect ' + str(amount))
            if addr == changeaddr:
                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)
Exemplo n.º 11
0
 def on_tx_confirmed(self, cjorder, confirmations, txid):
     to_announce = []
     for i, out in enumerate(cjorder.tx['outs']):
         addr = btc.script_to_address(out['script'], get_p2pk_vbyte())
         if addr == cjorder.change_addr:
             neworder = {'oid': self.get_next_oid(),
                         'ordertype': 'absorder',
                         'minsize': 12000,
                         'maxsize': out['value'],
                         'txfee': 10000,
                         'cjfee': 100000,
                         'utxo': txid + ':' + str(i)}
             to_announce.append(neworder)
         if addr == cjorder.cj_addr:
             neworder = {'oid': self.get_next_oid(),
                         'ordertype': 'absorder',
                         'minsize': 12000,
                         'maxsize': out['value'],
                         'txfee': 10000,
                         'cjfee': 100000,
                         'utxo': txid + ':' + str(i)}
             to_announce.append(neworder)
     return [], to_announce
Exemplo n.º 12
0
    def add_tx_notify(self, txd, unconfirmfun, confirmfun, 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((tx_output_set, unconfirmfun, confirmfun,
            timeoutfun, False))

        #create unconfirm timeout here, create confirm timeout in the other thread
        if timeoutfun:
            threading.Timer(jm_single().config.getint('TIMEOUT',
                'unconfirm_timeout_sec'), bitcoincore_timeout_callback,
                args=(False, tx_output_set, self.txnotify_fun, timeoutfun)
                ).start()
Exemplo n.º 13
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)
Exemplo n.º 14
0
 def script_to_address(self, script):
     """Return the address for a given output script,
     which will be p2pkh for the default Wallet object,
     and reading the correct network byte from the config.
     """
     return btc.script_to_address(script, get_p2pk_vbyte())