URI: 
       tstore deserialized tx in/out in wallet file for fast computation - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit e3de121be9a56c84ec80ce79a84f7ce1ae6b9b20
   DIR parent d9b1271f6571ea3f93519245c8405d249c7fd548
  HTML Author: ThomasV <thomasv@gitorious>
       Date:   Fri, 20 Mar 2015 22:37:06 +0100
       
       store deserialized tx in/out in wallet file for fast computation
       
       Diffstat:
         M gui/gtk.py                          |       4 ++--
         M gui/qt/lite_window.py               |       4 ++--
         M gui/qt/main_window.py               |      44 ++++++++++++++++----------------
         M gui/qt/transaction_dialog.py        |      11 +++++------
         M gui/text.py                         |       2 +-
         M lib/commands.py                     |       2 +-
         M lib/synchronizer.py                 |      65 ++++++++++++++++---------------
         M lib/transaction.py                  |      93 +++++--------------------------
         M lib/wallet.py                       |     478 ++++++++++++++++++-------------
         M plugins/exchange_rate.py            |      15 ++++++++-------
         M plugins/plot.py                     |      42 +++++++++++---------------------
       
       11 files changed, 390 insertions(+), 370 deletions(-)
       ---
   DIR diff --git a/gui/gtk.py b/gui/gtk.py
       t@@ -1170,7 +1170,7 @@ class ElectrumWindow:
                cursor = self.history_treeview.get_cursor()[0]
                self.history_list.clear()
        
       -        for item in self.wallet.get_tx_history():
       +        for item in self.wallet.get_history():
                    tx_hash, conf, is_mine, value, fee, balance, timestamp = item
                    if conf > 0:
                        try:
       t@@ -1199,7 +1199,7 @@ class ElectrumWindow:
                import datetime
                if not tx_hash: return ''
                tx = self.wallet.transactions.get(tx_hash)
       -        is_relevant, is_mine, v, fee = self.wallet.get_tx_value(tx)
       +        is_relevant, is_mine, v, fee = self.wallet.get_wallet_delta(tx)
                conf, timestamp = self.wallet.verifier.get_confirmations(tx_hash)
        
                if timestamp:
   DIR diff --git a/gui/qt/lite_window.py b/gui/qt/lite_window.py
       t@@ -450,7 +450,7 @@ class MiniWindow(QDialog):
                self.history_list.empty()
        
                for item in tx_history[-10:]:
       -            tx_hash, conf, is_mine, value, fee, balance, timestamp = item
       +            tx_hash, conf, value, timestamp = item
                    label = self.actuator.g.wallet.get_label(tx_hash)[0]
                    v_str = self.actuator.g.format_amount(value, True)
                    self.history_list.append(label, v_str, age(timestamp))
       t@@ -862,7 +862,7 @@ class MiniDriver(QObject):
                self.window.update_completions(completions)
        
            def update_history(self):
       -        tx_history = self.g.wallet.get_tx_history()
       +        tx_history = self.g.wallet.get_history()
                self.window.update_history(tx_history)
        
        
   DIR diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
       t@@ -568,7 +568,7 @@ class ElectrumWindow(QMainWindow):
                for i,width in enumerate(self.column_widths['history']):
                    l.setColumnWidth(i, width)
                l.setHeaderLabels( [ '', _('Date'), _('Description') , _('Amount'), _('Balance')] )
       -        l.itemDoubleClicked.connect(self.tx_label_clicked)
       +        l.itemDoubleClicked.connect(self.edit_tx_label)
                l.itemChanged.connect(self.tx_label_changed)
                l.customContextMenuRequested.connect(self.create_history_menu)
                return l
       t@@ -593,7 +593,7 @@ class ElectrumWindow(QMainWindow):
                menu = QMenu()
                menu.addAction(_("Copy ID to Clipboard"), lambda: self.app.clipboard().setText(tx_hash))
                menu.addAction(_("Details"), lambda: self.show_transaction(self.wallet.transactions.get(tx_hash)))
       -        menu.addAction(_("Edit description"), lambda: self.tx_label_clicked(item,2))
       +        menu.addAction(_("Edit description"), lambda: self.edit_tx_label(item,2))
                menu.addAction(_("View on block explorer"), lambda: webbrowser.open(block_explorer + tx_hash))
                menu.exec_(self.contacts_list.viewport().mapToGlobal(position))
        
       t@@ -603,21 +603,25 @@ class ElectrumWindow(QMainWindow):
                d = transaction_dialog.TxDialog(tx, self)
                d.exec_()
        
       -    def tx_label_clicked(self, item, column):
       +    def edit_tx_label(self, item, column):
                if column==2 and item.isSelected():
       -            self.is_edit=True
       +            text = unicode(item.text(column))
       +            tx_hash = str(item.data(0, Qt.UserRole).toString())
       +            self.is_edit = True
       +            if text == self.wallet.get_default_label(tx_hash):
       +                item.setText(column, '')
                    item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
                    self.history_list.editItem( item, column )
                    item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
       -            self.is_edit=False
       +            self.is_edit = False
        
            def tx_label_changed(self, item, column):
                if self.is_edit:
                    return
       -        self.is_edit=True
       +        self.is_edit = True
                tx_hash = str(item.data(0, Qt.UserRole).toString())
                tx = self.wallet.transactions.get(tx_hash)
       -        text = unicode( item.text(2) )
       +        text = unicode(item.text(2))
                self.wallet.set_label(tx_hash, text)
                if text:
                    item.setForeground(2, QBrush(QColor('black')))
       t@@ -625,7 +629,7 @@ class ElectrumWindow(QMainWindow):
                    text = self.wallet.get_default_label(tx_hash)
                    item.setText(2, text)
                    item.setForeground(2, QBrush(QColor('gray')))
       -        self.is_edit=False
       +        self.is_edit = False
        
        
            def edit_label(self, is_recv):
       t@@ -682,8 +686,9 @@ class ElectrumWindow(QMainWindow):
            def update_history_tab(self):
        
                self.history_list.clear()
       -        for item in self.wallet.get_tx_history(self.current_account):
       -            tx_hash, conf, is_mine, value, fee, balance, timestamp = item
       +        balance = 0
       +        for item in self.wallet.get_history(self.current_account):
       +            tx_hash, conf, value, timestamp = item
                    time_str = _("unknown")
                    if conf > 0:
                        time_str = self.format_time(timestamp)
       t@@ -703,6 +708,7 @@ class ElectrumWindow(QMainWindow):
                    else:
                        v_str = '--'
        
       +            balance += value
                    balance_str = self.format_amount(balance, whitespaces=True)
        
                    if tx_hash:
       t@@ -721,7 +727,7 @@ class ElectrumWindow(QMainWindow):
                        item.setData(0, Qt.UserRole, tx_hash)
                        item.setToolTip(0, "%d %s\nTxId:%s" % (conf, _('Confirmations'), tx_hash) )
                    if is_default_label:
       -                item.setForeground(2, QBrush(QColor('grey')))
       +                item.setForeground(2, QBrush(QColor('lightgrey')))
        
                    item.setIcon(0, icon)
                    self.history_list.insertTopLevelItem(0,item)
       t@@ -1020,7 +1026,7 @@ class ElectrumWindow(QMainWindow):
                    for i in inputs: self.wallet.add_input_info(i)
                    addr = self.payto_e.payto_address if self.payto_e.payto_address else self.dummy_address
                    output = ('address', addr, sendable)
       -            dummy_tx = Transaction(inputs, [output])
       +            dummy_tx = Transaction.from_io(inputs, [output])
                    fee = self.wallet.estimated_fee(dummy_tx)
                    self.amount_e.setAmount(max(0,sendable-fee))
                    self.amount_e.textEdited.emit("")
       t@@ -2510,10 +2516,10 @@ class ElectrumWindow(QMainWindow):
        
        
            def do_export_history(self, wallet, fileName, is_csv):
       -        history = wallet.get_tx_history()
       +        history = wallet.get_history()
                lines = []
                for item in history:
       -            tx_hash, confirmations, is_mine, value, fee, balance, timestamp = item
       +            tx_hash, confirmations, value, timestamp = item
                    if confirmations:
                        if timestamp is not None:
                            try:
       t@@ -2531,27 +2537,21 @@ class ElectrumWindow(QMainWindow):
                    else:
                        value_string = '--'
        
       -            if fee is not None:
       -                fee_string = format_satoshis(fee, True)
       -            else:
       -                fee_string = '0'
       -
                    if tx_hash:
                        label, is_default_label = wallet.get_label(tx_hash)
                        label = label.encode('utf-8')
                    else:
                        label = ""
        
       -            balance_string = format_satoshis(balance, False)
                    if is_csv:
       -                lines.append([tx_hash, label, confirmations, value_string, fee_string, balance_string, time_string])
       +                lines.append([tx_hash, label, confirmations, value_string, time_string])
                    else:
                        lines.append({'txid':tx_hash, 'date':"%16s"%time_string, 'label':label, 'value':value_string})
        
                with open(fileName, "w+") as f:
                    if is_csv:
                        transaction = csv.writer(f, lineterminator='\n')
       -                transaction.writerow(["transaction_hash","label", "confirmations", "value", "fee", "balance", "timestamp"])
       +                transaction.writerow(["transaction_hash","label", "confirmations", "value", "timestamp"])
                        for line in lines:
                            transaction.writerow(line)
                    else:
   DIR diff --git a/gui/qt/transaction_dialog.py b/gui/qt/transaction_dialog.py
       t@@ -134,8 +134,8 @@ class TxDialog(QDialog):
        
        
            def update(self):
       -
       -        is_relevant, is_mine, v, fee = self.wallet.get_tx_value(self.tx)
       +        is_relevant, is_mine, v, fee = self.wallet.get_wallet_delta(self.tx)
       +        tx_hash = self.tx.hash()
                if self.wallet.can_sign(self.tx):
                    self.sign_button.show()
                else:
       t@@ -143,7 +143,6 @@ class TxDialog(QDialog):
        
                if self.tx.is_complete():
                    status = _("Signed")
       -            tx_hash = self.tx.hash()
        
                    if tx_hash in self.wallet.transactions.keys():
                        conf, timestamp = self.wallet.verifier.get_confirmations(tx_hash)
       t@@ -182,10 +181,10 @@ class TxDialog(QDialog):
                if is_relevant:
                    if is_mine:
                        if fee is not None:
       -                    self.amount_label.setText(_("Amount sent:")+' %s'% self.parent.format_amount(v-fee) + ' ' + self.parent.base_unit())
       -                    self.fee_label.setText(_("Transaction fee")+': %s'% self.parent.format_amount(fee) + ' ' + self.parent.base_unit())
       +                    self.amount_label.setText(_("Amount sent:")+' %s'% self.parent.format_amount(-v+fee) + ' ' + self.parent.base_unit())
       +                    self.fee_label.setText(_("Transaction fee")+': %s'% self.parent.format_amount(-fee) + ' ' + self.parent.base_unit())
                        else:
       -                    self.amount_label.setText(_("Amount sent:")+' %s'% self.parent.format_amount(v) + ' ' + self.parent.base_unit())
       +                    self.amount_label.setText(_("Amount sent:")+' %s'% self.parent.format_amount(-v) + ' ' + self.parent.base_unit())
                            self.fee_label.setText(_("Transaction fee")+': '+ _("unknown"))
                    else:
                        self.amount_label.setText(_("Amount received:")+' %s'% self.parent.format_amount(v) + ' ' + self.parent.base_unit())
   DIR diff --git a/gui/text.py b/gui/text.py
       t@@ -107,7 +107,7 @@ class ElectrumGui:
                b = 0 
                self.history = []
        
       -        for item in self.wallet.get_tx_history():
       +        for item in self.wallet.get_history():
                    tx_hash, conf, is_mine, value, fee, balance, timestamp = item
                    if conf:
                        try:
   DIR diff --git a/lib/commands.py b/lib/commands.py
       t@@ -342,7 +342,7 @@ class Commands:
            def history(self):
                balance = 0
                out = []
       -        for item in self.wallet.get_tx_history():
       +        for item in self.wallet.get_history():
                    tx_hash, conf, is_mine, value, fee, balance, timestamp = item
                    try:
                        time_str = datetime.datetime.fromtimestamp( timestamp).isoformat(' ')[:-3]
   DIR diff --git a/lib/synchronizer.py b/lib/synchronizer.py
       t@@ -101,6 +101,7 @@ class WalletSynchronizer(util.DaemonThread):
                        if not self.wallet.is_up_to_date():
                            self.wallet.set_up_to_date(True)
                            self.was_updated = True
       +                    self.wallet.save_transactions()
                    else:
                        if self.wallet.is_up_to_date():
                            self.wallet.set_up_to_date(False)
       t@@ -127,7 +128,7 @@ class WalletSynchronizer(util.DaemonThread):
        
                    if method == 'blockchain.address.subscribe':
                        addr = params[0]
       -                if self.wallet.get_status(self.wallet.get_history(addr)) != result:
       +                if self.wallet.get_status(self.wallet.get_address_history(addr)) != result:
                            if requested_histories.get(addr) is None:
                                self.network.send([('blockchain.address.get_history', [addr])], self.queue.put)
                                requested_histories[addr] = result
       t@@ -135,41 +136,43 @@ class WalletSynchronizer(util.DaemonThread):
                    elif method == 'blockchain.address.get_history':
                        addr = params[0]
                        self.print_error("receiving history", addr, result)
       -                if result == ['*']:
       -                    assert requested_histories.pop(addr) == '*'
       -                    self.wallet.receive_history_callback(addr, result)
       -                else:
       -                    hist = []
       -                    # check that txids are unique
       -                    txids = []
       -                    for item in result:
       -                        tx_hash = item['tx_hash']
       -                        if tx_hash not in txids:
       -                            txids.append(tx_hash)
       -                            hist.append( (tx_hash, item['height']) )
       -
       -                    if len(hist) != len(result):
       -                        raise Exception("error: server sent history with non-unique txid", result)
       -
       -                    # check that the status corresponds to what was announced
       -                    rs = requested_histories.pop(addr)
       -                    if self.wallet.get_status(hist) != rs:
       -                        raise Exception("error: status mismatch: %s"%addr)
       -
       -                    # store received history
       -                    self.wallet.receive_history_callback(addr, hist)
       -
       -                    # request transactions that we don't have
       -                    for tx_hash, tx_height in hist:
       -                        if self.wallet.transactions.get(tx_hash) is None:
       -                            if (tx_hash, tx_height) not in requested_tx and (tx_hash, tx_height) not in missing_tx:
       -                                missing_tx.append( (tx_hash, tx_height) )
       +                hist = []
       +                # check that txids are unique
       +                txids = []
       +                for item in result:
       +                    tx_hash = item['tx_hash']
       +                    if tx_hash not in txids:
       +                        txids.append(tx_hash)
       +                        hist.append( (tx_hash, item['height']) )
       +
       +                if len(hist) != len(result):
       +                    raise Exception("error: server sent history with non-unique txid", result)
       +
       +                # check that the status corresponds to what was announced
       +                rs = requested_histories.pop(addr)
       +                if self.wallet.get_status(hist) != rs:
       +                    raise Exception("error: status mismatch: %s"%addr)
       +
       +                # store received history
       +                self.wallet.receive_history_callback(addr, hist)
       +
       +                # request transactions that we don't have
       +                for tx_hash, tx_height in hist:
       +                    if self.wallet.transactions.get(tx_hash) is None:
       +                        if (tx_hash, tx_height) not in requested_tx and (tx_hash, tx_height) not in missing_tx:
       +                            missing_tx.append( (tx_hash, tx_height) )
        
                    elif method == 'blockchain.transaction.get':
                        tx_hash = params[0]
                        tx_height = params[1]
                        assert tx_hash == bitcoin.hash_encode(bitcoin.Hash(result.decode('hex')))
       -                tx = Transaction.deserialize(result)
       +                tx = Transaction(result)
       +                try:
       +                    tx.deserialize()
       +                except Exception:
       +                    self.print_msg("Warning: Cannot deserialize transactions. skipping")
       +                    continue
       +
                        self.wallet.receive_tx_callback(tx_hash, tx, tx_height)
                        self.was_updated = True
                        requested_tx.remove( (tx_hash, tx_height) )
   DIR diff --git a/lib/transaction.py b/lib/transaction.py
       t@@ -482,26 +482,25 @@ class Transaction:
                    self.raw = self.serialize()
                return self.raw
        
       -    def __init__(self, inputs, outputs, locktime=0):
       -        self.inputs = inputs
       -        self.outputs = outputs
       -        self.locktime = locktime
       -        self.raw = None
       -
       -    @classmethod
       -    def deserialize(klass, raw):
       -        self = klass([],[])
       -        self.update(raw)
       -        return self
       -
       -    def update(self, raw):
       -        d = deserialize(raw)
       +    def __init__(self, raw):
                self.raw = raw
       +
       +    def deserialize(self):
       +        d = deserialize(self.raw)
                self.inputs = d['inputs']
                self.outputs = [(x['type'], x['address'], x['value']) for x in d['outputs']]
                self.locktime = d['lockTime']
        
            @classmethod
       +    def from_io(klass, inputs, outputs, locktime=0):
       +        self = klass(None)
       +        self.inputs = inputs
       +        self.outputs = outputs
       +        self.locktime = locktime
       +        #self.raw = self.serialize()
       +        return self
       +
       +    @classmethod
            def sweep(klass, privkeys, network, to_address, fee):
                inputs = []
                for privkey in privkeys:
       t@@ -526,7 +525,7 @@ class Transaction:
        
                total = sum(i.get('value') for i in inputs) - fee
                outputs = [('address', to_address, total)]
       -        self = klass(inputs, outputs)
       +        self = klass.from_io(inputs, outputs)
                self.sign({ pubkey:privkey })
                return self
        
       t@@ -736,16 +735,6 @@ class Transaction:
                self.raw = self.serialize()
        
        
       -    def add_pubkey_addresses(self, txdict):
       -        for txin in self.inputs:
       -            if txin.get('address') == "(pubkey)":
       -                prev_tx = txdict.get(txin.get('prevout_hash'))
       -                if prev_tx:
       -                    address, value = prev_tx.get_outputs()[txin.get('prevout_n')]
       -                    print_error("found pay-to-pubkey address:", address)
       -                    txin["address"] = address
       -
       -
            def get_outputs(self):
                """convert pubkeys to addresses"""
                o = []
       t@@ -767,60 +756,8 @@ class Transaction:
                return (addr in self.get_output_addresses()) or (addr in (tx.get("address") for tx in self.inputs))
        
        
       -    def get_value(self, addresses, prevout_values):
       -        # return the balance for that tx
       -        is_relevant = False
       -        is_send = False
       -        is_pruned = False
       -        is_partial = False
       -        v_in = v_out = v_out_mine = 0
       -
       -        for item in self.inputs:
       -            addr = item.get('address')
       -            if addr in addresses:
       -                is_send = True
       -                is_relevant = True
       -                key = item['prevout_hash']  + ':%d'%item['prevout_n']
       -                value = prevout_values.get( key )
       -                if value is None:
       -                    is_pruned = True
       -                else:
       -                    v_in += value
       -            else:
       -                is_partial = True
       -
       -        if not is_send: is_partial = False
       -
       -        for addr, value in self.get_outputs():
       -            v_out += value
       -            if addr in addresses:
       -                v_out_mine += value
       -                is_relevant = True
       -
       -        if is_pruned:
       -            # some inputs are mine:
       -            fee = None
       -            if is_send:
       -                v = v_out_mine - v_out
       -            else:
       -                # no input is mine
       -                v = v_out_mine
       -
       -        else:
       -            v = v_out_mine - v_in
       -
       -            if is_partial:
       -                # some inputs are mine, but not all
       -                fee = None
       -                is_send = v < 0
       -            else:
       -                # all inputs are mine
       -                fee = v_out - v_in
       -
       -        return is_relevant, is_send, v, fee
       -
       -
            def as_dict(self):
       +        self.deserialize()
                import json
                out = {
                    "hex":str(self),
   DIR diff --git a/lib/wallet.py b/lib/wallet.py
       t@@ -28,6 +28,7 @@ import json
        import copy
        
        from util import print_msg, print_error, NotEnoughFunds
       +from util import profiler
        
        from bitcoin import *
        from account import *
       t@@ -173,9 +174,6 @@ class Abstract_Wallet(object):
        
                self.load_transactions()
        
       -        # not saved
       -        self.prevout_values = {}     # my own transaction outputs
       -        self.spent_outputs = []
                # spv
                self.verifier = None
                # there is a difference between wallet.up_to_date and interface.is_up_to_date()
       t@@ -185,42 +183,35 @@ class Abstract_Wallet(object):
                self.lock = threading.Lock()
                self.transaction_lock = threading.Lock()
                self.tx_event = threading.Event()
       -        for tx_hash, tx in self.transactions.items():
       -            self.update_tx_outputs(tx_hash)
        
                # save wallet type the first time
                if self.storage.get('wallet_type') is None:
                    self.storage.put('wallet_type', self.wallet_type, True)
        
       -
       +    @profiler
            def load_transactions(self):
       +        self.txi = self.storage.get('txi', {})
       +        self.txo = self.storage.get('txo', {})
       +        self.reverse_txo = self.storage.get('reverse_utxo', {})
       +        tx_list = self.storage.get('transactions', {})
                self.transactions = {}
       -        tx_list = self.storage.get('transactions',{})
       -        for k, raw in tx_list.items():
       -            try:
       -                tx = Transaction.deserialize(raw)
       -            except Exception:
       -                print_msg("Warning: Cannot deserialize transactions. skipping")
       -                continue
       -            self.add_pubkey_addresses(tx)
       -            self.transactions[k] = tx
       -        for h,tx in self.transactions.items():
       -            if not self.check_new_tx(h, tx):
       -                print_error("removing unreferenced tx", h)
       -                self.transactions.pop(h)
       -
       -    def add_pubkey_addresses(self, tx):
       -        # find the address corresponding to pay-to-pubkey inputs
       -        h = tx.hash()
       -
       -        # inputs
       -        tx.add_pubkey_addresses(self.transactions)
       -
       -        # outputs of tx: inputs of tx2
       -        for type, x, v in tx.outputs:
       -            if type == 'pubkey':
       -                for tx2 in self.transactions.values():
       -                    tx2.add_pubkey_addresses({h:tx})
       +        for tx_hash, raw in tx_list.items():
       +            tx = Transaction(raw)
       +            self.transactions[tx_hash] = tx
       +            if self.txi.get(tx_hash) is None and self.txo.get(tx_hash) is None:
       +                print_error("removing unreferenced tx", tx_hash)
       +                self.transactions.pop(tx_hash)
       +
       +    @profiler
       +    def save_transactions(self):
       +        with self.transaction_lock:
       +            tx = {}
       +            for k,v in self.transactions.items():
       +                tx[k] = str(v)
       +            self.storage.put('transactions', tx)
       +            self.storage.put('txi', self.txi)
       +            self.storage.put('txo', self.txo)
       +            self.storage.put('reverse_txo', self.reverse_txo)
        
            def get_action(self):
                pass
       t@@ -362,7 +353,6 @@ class Abstract_Wallet(object):
                account_id, sequence = self.get_address_index(address)
                return self.accounts[account_id].get_pubkeys(*sequence)
        
       -
            def sign_message(self, address, message, password):
                keys = self.get_private_key(address, password)
                assert len(keys) == 1
       t@@ -396,7 +386,7 @@ class Abstract_Wallet(object):
            def fill_addressbook(self):
                # todo: optimize this
                for tx_hash, tx in self.transactions.viewitems():
       -            is_relevant, is_send, _, _ = self.get_tx_value(tx)
       +            _, is_send, _, _ = self.get_tx_value(tx)
                    if is_send:
                        for addr in tx.get_output_addresses():
                            if not self.is_mine(addr) and addr not in self.addressbook:
       t@@ -405,66 +395,139 @@ class Abstract_Wallet(object):
                # self.update_tx_labels()
        
            def get_num_tx(self, address):
       -        n = 0
       -        for tx in self.transactions.values():
       -            if address in tx.get_output_addresses(): n += 1
       -        return n
       -
       -    def get_tx_value(self, tx, account=None):
       -        domain = self.get_account_addresses(account)
       -        return tx.get_value(domain, self.prevout_values)
       -
       -    def update_tx_outputs(self, tx_hash):
       -        tx = self.transactions.get(tx_hash)
       -
       -        for i, (addr, value) in enumerate(tx.get_outputs()):
       -            key = tx_hash+ ':%d'%i
       -            self.prevout_values[key] = value
       -
       +        """ return number of transactions where address is involved """
       +        return len(self.history.get(address, []))
       +        #n = 0
       +        #for tx in self.transactions.values():
       +        #    if address in tx.get_output_addresses(): n += 1
       +        #return n
       +
       +    def get_tx_delta(self, tx_hash, address):
       +        "effect of tx on address"
       +        delta = 0
       +        # substract the value of coins sent from address
       +        d = self.txi.get(tx_hash, {}).get(address, [])
       +        for n, v in d:
       +            delta -= v
       +        # add the value of the coins received at address
       +        d = self.txo.get(tx_hash, {}).get(address, [])
       +        for n, v, cb in d:
       +            delta += v
       +        return delta
       +
       +    def get_wallet_delta(self, tx):
       +        """ effect of tx on wallet """
       +        addresses = self.addresses(True)
       +        is_relevant = False
       +        is_send = False
       +        is_pruned = False
       +        is_partial = False
       +        v_in = v_out = v_out_mine = 0
                for item in tx.inputs:
       -            if self.is_mine(item.get('address')):
       -                key = item['prevout_hash'] + ':%d'%item['prevout_n']
       -                self.spent_outputs.append(key)
       +            addr = item.get('address')
       +            if addr in addresses:
       +                is_send = True
       +                is_relevant = True
       +                d = self.txo.get(item['prevout_hash'], {}).get(addr, [])
       +                for n, v, cb in d:
       +                    if n == item['prevout_n']:
       +                        value = v
       +                        break
       +                else:
       +                    value = None
       +                if value is None:
       +                    is_pruned = True
       +                else:
       +                    v_in += value
       +            else:
       +                is_partial = True
       +        if not is_send:
       +            is_partial = False
       +        for addr, value in tx.get_outputs():
       +            v_out += value
       +            if addr in addresses:
       +                v_out_mine += value
       +                is_relevant = True
       +        if is_pruned:
       +            # some inputs are mine:
       +            fee = None
       +            if is_send:
       +                v = v_out_mine - v_out
       +            else:
       +                # no input is mine
       +                v = v_out_mine
       +        else:
       +            v = v_out_mine - v_in
       +            if is_partial:
       +                # some inputs are mine, but not all
       +                fee = None
       +                is_send = v < 0
       +            else:
       +                # all inputs are mine
       +                fee = v_out - v_in
       +        return is_relevant, is_send, v, fee
        
            def get_addr_balance(self, address):
       -        'returns the confirmed balance and pending (unconfirmed) balance change of this bitcoin address'
       -        #assert self.is_mine(address)
       -        h = self.history.get(address,[])
       -        if h == ['*']: return 0,0
       +        "returns the confirmed balance and pending (unconfirmed) balance change of a bitcoin address"
       +        h = self.history.get(address, [])
                c = u = 0
       -        received_coins = []   # list of coins received at address
       -        # go through all tx in history of this address and collect the coins arriving on this address
       -        for tx_hash, tx_height in h:
       -            tx = self.transactions.get(tx_hash)
       -            if not tx: continue
       +        for tx_hash, height in h:
       +            v = self.get_tx_delta(tx_hash, address)
       +            if height > 0:
       +                c += v
       +            else:
       +                u += v
       +        return c, u
        
       -            for i, (addr, value) in enumerate(tx.get_outputs()):
       -                if addr == address:
       -                    key = tx_hash + ':%d'%i
       -                    received_coins.append(key)
       -        # go through all tx in history of this address again
       -        for tx_hash, tx_height in h:
       -            tx = self.transactions.get(tx_hash)
       -            if not tx: continue
       -            v = 0
       -            # substract the value of coins leaving from this address
       -            for item in tx.inputs:
       -                addr = item.get('address')
       -                if addr == address:
       -                    key = item['prevout_hash']  + ':%d'%item['prevout_n']
       -                    value = self.prevout_values.get( key )
       -                    if key in received_coins:
       -                        v -= value
       -            # add the value of the coins arriving in this address
       -            for i, (addr, value) in enumerate(tx.get_outputs()):
       -                key = tx_hash + ':%d'%i
       -                if addr == address:
       -                    v += value
       -
       -            if tx_height:
       -                c += v  # confirmed coins value
       +    def get_addr_utxo(self, address):
       +        h = self.history.get(address, [])
       +        coins = {}
       +        for tx_hash, height in h:
       +            l = self.txo.get(tx_hash, {}).get(address, [])
       +            for n, v, is_cb in l:
       +                coins[tx_hash + ':%d'%n] = (height, v, is_cb)
       +        for tx_hash, height in h:
       +            l = self.txi.get(tx_hash, {}).get(address, [])
       +            for txi, v in l:
       +                coins.pop(txi)
       +        return coins.items()
       +
       +    def get_unspent_coins(self, domain=None):
       +        coins = []
       +        if domain is None:
       +            domain = self.addresses(True)
       +        for addr in domain:
       +            c = self.get_addr_utxo(addr)
       +            for txo, v in c:
       +                tx_height, value, is_cb = v
       +                prevout_hash, prevout_n = txo.split(':')
       +                output = {
       +                    'address':addr,
       +                    'value':value,
       +                    'prevout_n':int(prevout_n),
       +                    'prevout_hash':prevout_hash,
       +                    'height':tx_height,
       +                    'coinbase':is_cb
       +                }
       +                coins.append((tx_height, output))
       +                continue
       +        # sort by age
       +        if coins:
       +            coins = sorted(coins)
       +            if coins[-1][0] != 0:
       +                while coins[0][0] == 0:
       +                    coins = coins[1:] + [ coins[0] ]
       +        return [value for height, value in coins]
       +
       +    def get_addr_balance2(self, address):
       +        "returns the confirmed balance and pending (unconfirmed) balance change of a bitcoin address"
       +        coins = self.get_addr_utxo(address)
       +        c = u = 0
       +        for txo, v, height in coins:
       +            if height > 0:
       +                c += v
                    else:
       -                u += v  # unconfirmed coins value
       +                u += v
                return c, u
        
            def get_account_name(self, k):
       t@@ -508,133 +571,170 @@ class Abstract_Wallet(object):
                    uu += u
                return cc, uu
        
       -    def get_unspent_coins(self, domain=None):
       -        coins = []
       -        if domain is None: domain = self.addresses(True)
       -        for addr in domain:
       -            h = self.history.get(addr, [])
       -            if h == ['*']: continue
       -            for tx_hash, tx_height in h:
       -                tx = self.transactions.get(tx_hash)
       -                if tx is None: raise Exception("Wallet not synchronized")
       -                is_coinbase = tx.inputs[0].get('prevout_hash') == '0'*64
       -                for i, (address, value) in enumerate(tx.get_outputs()):
       -                    output = {'address':address, 'value':value, 'prevout_n':i}
       -                    if address != addr: continue
       -                    key = tx_hash + ":%d"%i
       -                    if key in self.spent_outputs: continue
       -                    output['prevout_hash'] = tx_hash
       -                    output['height'] = tx_height
       -                    output['coinbase'] = is_coinbase
       -                    coins.append((tx_height, output))
       -
       -        # sort by age
       -        if coins:
       -            coins = sorted(coins)
       -            if coins[-1][0] != 0:
       -                while coins[0][0] == 0:
       -                    coins = coins[1:] + [ coins[0] ]
       -        return [value for height, value in coins]
       -
       -
       -
            def set_fee(self, fee):
                if self.fee_per_kb != fee:
                    self.fee_per_kb = fee
                    self.storage.put('fee_per_kb', self.fee_per_kb, True)
        
       -
       -    def get_history(self, address):
       +    def get_address_history(self, address):
                with self.lock:
       -            return self.history.get(address)
       +            return self.history.get(address, [])
        
            def get_status(self, h):
       -        if not h: return None
       -        if h == ['*']: return '*'
       +        if not h:
       +            return None
                status = ''
                for tx_hash, height in h:
                    status += tx_hash + ':%d:' % height
                return hashlib.sha256( status ).digest().encode('hex')
        
       -    def receive_tx_callback(self, tx_hash, tx, tx_height):
       -
       +    def add_transaction(self, tx_hash, tx, tx_height):
       +        is_coinbase = tx.inputs[0].get('prevout_hash') == '0'*64
                with self.transaction_lock:
       -            self.add_pubkey_addresses(tx)
       -            if not self.check_new_tx(tx_hash, tx):
       -                # may happen due to pruning
       -                print_error("received transaction that is no longer referenced in history", tx_hash)
       -                return
       +            # add inputs
       +            self.txi[tx_hash] = d = {}
       +            for txi in tx.inputs:
       +                addr = txi.get('address')
       +                if addr and self.is_mine(addr):
       +                    prevout_hash = txi['prevout_hash']
       +                    prevout_n = txi['prevout_n']
       +                    ser = prevout_hash + ':%d'%prevout_n
       +                    dd = self.txo.get(prevout_hash, {})
       +                    for n, v, is_cb in dd.get(addr, []):
       +                        if n == prevout_n:
       +                            if d.get(addr) is None:
       +                                d[addr] = []
       +                            d[addr].append((ser, v))
       +                            break
       +                    else:
       +                        self.reverse_txo[ser] = tx_hash
       +                elif addr == "(pubkey)":
       +                    prevout_hash = txi['prevout_hash']
       +                    prevout_n = txi['prevout_n']
       +                    ser = prevout_hash + ':%d'%prevout_n
       +                    dd = self.txo.get(prevout_hash, {})
       +                    found = False
       +                    for _addr, l in dd.items():
       +                        for n, v, is_cb in l:
       +                            if n == prevout_n:
       +                                print_error("found pay-to-pubkey address:", _addr)
       +                                if d.get(_addr) is None:
       +                                    d[_addr] = []
       +                                d[_addr].append((ser, v))
       +                                found = True
       +                    if not found:
       +                        self.reverse_txo[ser] = tx_hash
       +
       +            # add outputs
       +            self.txo[tx_hash] = d = {}
       +            for n, txo in enumerate(tx.outputs):
       +                ser = tx_hash + ':%d'%n
       +                _type, x, v = txo
       +                if _type == 'address':
       +                    addr = x
       +                elif _type == 'pubkey': 
       +                    addr = public_key_to_bc_address(x.decode('hex'))
       +                else:
       +                    addr = None
       +                if addr and self.is_mine(addr):
       +                    if d.get(addr) is None:
       +                        d[addr] = []
       +                    d[addr].append((n, v, is_coinbase))
       +
       +                next_tx = self.reverse_txo.get(ser)
       +                if next_tx is not None:
       +                    self.reverse_txo.pop(ser)
       +                    dd = self.txi.get(next_tx, {})
       +                    if dd.get(addr) is None:
       +                        dd[addr] = []
       +                    dd[addr].append((ser, v))
       +            # save
                    self.transactions[tx_hash] = tx
       -            self.network.pending_transactions_for_notifications.append(tx)
       -            self.save_transactions()
       -            if self.verifier and tx_height>0:
       -                self.verifier.add(tx_hash, tx_height)
       -            self.update_tx_outputs(tx_hash)
        
       -    def save_transactions(self):
       -        tx = {}
       -        for k,v in self.transactions.items():
       -            tx[k] = str(v)
       -        self.storage.put('transactions', tx, True)
       +    def receive_tx_callback(self, tx_hash, tx, tx_height):
       +        if not self.check_new_tx(tx_hash, tx):
       +            # may happen due to pruning
       +            print_error("received transaction that is no longer referenced in history", tx_hash)
       +            return
       +        self.add_transaction(tx_hash, tx, tx_height)
       +        #self.network.pending_transactions_for_notifications.append(tx)
       +        if self.verifier and tx_height>0:
       +            self.verifier.add(tx_hash, tx_height)
       +
        
            def receive_history_callback(self, addr, hist):
        
       -        if not self.check_new_history(addr, hist):
       -            raise Exception("error: received history for %s is not consistent with known transactions"%addr)
       +        #if not self.check_new_history(addr, hist):
       +        #    raise Exception("error: received history for %s is not consistent with known transactions"%addr)
        
                with self.lock:
                    self.history[addr] = hist
                    self.storage.put('addr_history', self.history, True)
        
       -        if hist != ['*']:
       -            for tx_hash, tx_height in hist:
       -                if tx_height>0:
       -                    # add it in case it was previously unconfirmed
       -                    if self.verifier: self.verifier.add(tx_hash, tx_height)
       -
       -    def get_tx_history(self, account=None):
       -        if not self.verifier:
       -            return []
       -
       -        with self.transaction_lock:
       -            history = self.transactions.items()
       -            history.sort(key = lambda x: self.verifier.get_txpos(x[0]))
       -            result = []
       +        for tx_hash, tx_height in hist:
       +            if tx_height>0:
       +                # add it in case it was previously unconfirmed
       +                if self.verifier:
       +                    self.verifier.add(tx_hash, tx_height)
        
       -            balance = 0
       -            for tx_hash, tx in history:
       -                is_relevant, is_mine, v, fee = self.get_tx_value(tx, account)
       -                if v is not None: balance += v
       +            # if addr is new, we have to recompute txi and txo
       +            # fixme: bad interaction with server hist limit?
       +            tx = self.transactions.get(tx_hash)
       +            if tx is not None and self.txi.get(tx_hash, {}).get(addr) is None and self.txo.get(tx_hash, {}).get(addr) is None:
       +                tx.deserialize()
       +                self.add_transaction(tx_hash, tx, tx_height)
        
       -            c, u = self.get_account_balance(account)
        
       -            if balance != c+u:
       -                result.append( ('', 1000, 0, c+u-balance, None, c+u-balance, None ) )
       +    def get_history(self, account=None):
       +        # get domain
       +        domain = self.get_account_addresses(account)
        
       -            balance = c + u - balance
       -            for tx_hash, tx in history:
       -                is_relevant, is_mine, value, fee = self.get_tx_value(tx, account)
       -                if not is_relevant:
       -                    continue
       -                if value is not None:
       -                    balance += value
       +        hh = []
       +        # 1. Get the history of each address in the domain
       +        for addr in domain:
       +            h = self.get_address_history(addr)
       +            for tx_hash, height in h:
       +                delta = self.get_tx_delta(tx_hash, addr)
       +                hh.append([addr, tx_hash, height, delta])
       +
       +        # 2. merge 
       +        # the delta of a tx on the domain is the sum of its deltas on addresses
       +        merged = {}
       +        for addr, tx_hash, height, delta in hh:
       +            if tx_hash not in merged:
       +                merged[tx_hash] = (height, delta)
       +            else:
       +                h, d = merged.get(tx_hash)
       +                merged[tx_hash] = (h, d + delta)
       +
       +        # 3. create sorted list
       +        history = []
       +        for tx_hash, v in merged.items():
       +            height, value = v
       +            is_mine = 1
       +            fee = 0
       +            balance = 0
       +            conf, timestamp = self.verifier.get_confirmations(tx_hash) if self.verifier else (None, None)
       +            history.append( (tx_hash, conf, value, timestamp) )
        
       -                conf, timestamp = self.verifier.get_confirmations(tx_hash) if self.verifier else (None, None)
       -                result.append( (tx_hash, conf, is_mine, value, fee, balance, timestamp) )
       +        history.sort(key = lambda x: self.verifier.get_txpos(x[0]))
       +        return history
        
       -        return result
        
            def get_label(self, tx_hash):
                label = self.labels.get(tx_hash)
                is_default = (label == '') or (label is None)
       -        if is_default: label = self.get_default_label(tx_hash)
       +        if is_default:
       +            label = self.get_default_label(tx_hash)
                return label, is_default
        
            def get_default_label(self, tx_hash):
       +        return tx_hash
       +
                tx = self.transactions.get(tx_hash)
                default_label = ''
                if tx:
       -            is_relevant, is_mine, _, _ = self.get_tx_value(tx)
       +            _, is_mine, _, _ = self.get_wallet_delta(tx)
                    if is_mine:
                        for o_addr in tx.get_output_addresses():
                            if not self.is_mine(o_addr):
       t@@ -702,7 +802,7 @@ class Abstract_Wallet(object):
                amount = sum( map(lambda x:x[2], outputs) )
                total = fee = 0
                inputs = []
       -        tx = Transaction(inputs, outputs)
       +        tx = Transaction.from_io(inputs, outputs)
                for item in coins:
                    if item.get('coinbase') and item.get('height') + COINBASE_MATURITY > self.network.get_local_height():
                        continue
       t@@ -860,7 +960,6 @@ class Abstract_Wallet(object):
        
                # review transactions that are in the history
                for addr, hist in self.history.items():
       -            if hist == ['*']: continue
                    for tx_hash, tx_height in hist:
                        if tx_height>0:
                            # add it in case it was previously unconfirmed
       t@@ -874,23 +973,22 @@ class Abstract_Wallet(object):
        
            def check_new_history(self, addr, hist):
                # check that all tx in hist are relevant
       -        if hist != ['*']:
       -            for tx_hash, height in hist:
       -                tx = self.transactions.get(tx_hash)
       -                if not tx: continue
       -                if not tx.has_address(addr):
       -                    return False
       +        for tx_hash, height in hist:
       +            tx = self.transactions.get(tx_hash)
       +            if not tx:
       +                continue
       +            if not tx.has_address(addr):
       +                return False
        
                # check that we are not "orphaning" a transaction
                old_hist = self.history.get(addr,[])
       -        if old_hist == ['*']: return True
       -
                for tx_hash, height in old_hist:
       -            if tx_hash in map(lambda x:x[0], hist): continue
       +            if tx_hash in map(lambda x:x[0], hist):
       +                continue
                    found = False
                    for _addr, _hist in self.history.items():
       -                if _addr == addr: continue
       -                if _hist == ['*']: continue
       +                if _addr == addr:
       +                    continue
                        _tx_hist = map(lambda x:x[0], _hist)
                        if tx_hash in _tx_hist:
                            found = True
       t@@ -916,7 +1014,6 @@ class Abstract_Wallet(object):
                        print_error("sync:", ext_requests, ext_h)
                        height = None
                        for h in ext_h:
       -                    if h == ['*']: continue
                            for item in h:
                                if item.get('tx_hash') == tx_hash:
                                    height = item.get('height')
       t@@ -933,7 +1030,6 @@ class Abstract_Wallet(object):
                # 1 check that tx is referenced in addr_history.
                addresses = []
                for addr, hist in self.history.items():
       -            if hist == ['*']:continue
                    for txh, height in hist:
                        if txh == tx_hash:
                            addresses.append(addr)
       t@@ -996,8 +1092,6 @@ class Abstract_Wallet(object):
            def address_is_old(self, address, age_limit=2):
                age = -1
                h = self.history.get(address, [])
       -        if h == ['*']:
       -            return True
                for tx_hash, tx_height in h:
                    if tx_height == 0:
                        tx_age = 0
   DIR diff --git a/plugins/exchange_rate.py b/plugins/exchange_rate.py
       t@@ -499,9 +499,9 @@ class Plugin(BasePlugin):
            @hook
            def load_wallet(self, wallet):
                tx_list = {}
       -        for item in self.wallet.get_tx_history(self.wallet.storage.get("current_account", None)):
       -            tx_hash, conf, is_mine, value, fee, balance, timestamp = item
       -            tx_list[tx_hash] = {'value': value, 'timestamp': timestamp, 'balance': balance}
       +        for item in self.wallet.get_history(self.wallet.storage.get("current_account", None)):
       +            tx_hash, conf, value, timestamp = item
       +            tx_list[tx_hash] = {'value': value, 'timestamp': timestamp }
        
                self.tx_list = tx_list
                self.cur_exchange = self.config.get('use_exchange', "Blockchain")
       t@@ -572,20 +572,21 @@ class Plugin(BasePlugin):
                    except Exception:
                        newtx = self.wallet.get_tx_history()
                        v = newtx[[x[0] for x in newtx].index(str(item.data(0, Qt.UserRole).toPyObject()))][3]
       -                tx_info = {'timestamp':int(time.time()), 'value': v }
       +                tx_info = {'timestamp':int(time.time()), 'value': v}
                        pass
                    tx_time = int(tx_info['timestamp'])
       +            tx_value = Decimal(str(tx_info['value'])) / 100000000
                    if self.cur_exchange == "CoinDesk":
                        tx_time_str = datetime.datetime.fromtimestamp(tx_time).strftime('%Y-%m-%d')
                        try:
       -                    tx_fiat_val = "%.2f %s" % (Decimal(str(tx_info['value'])) / 100000000 * Decimal(self.resp_hist['bpi'][tx_time_str]), "USD")
       +                    tx_fiat_val = "%.2f %s" % (value * Decimal(self.resp_hist['bpi'][tx_time_str]), "USD")
                        except KeyError:
                            tx_fiat_val = "%.2f %s" % (self.btc_rate * Decimal(str(tx_info['value']))/100000000 , "USD")
                    elif self.cur_exchange == "Winkdex":
                        tx_time_str = datetime.datetime.fromtimestamp(tx_time).strftime('%Y-%m-%d') + "T16:00:00-04:00"
                        try:
                            tx_rate = self.resp_hist[[x['timestamp'] for x in self.resp_hist].index(tx_time_str)]['price']
       -                    tx_fiat_val = "%.2f %s" % (Decimal(tx_info['value']) / 100000000 * Decimal(tx_rate)/Decimal("100.0"), "USD")
       +                    tx_fiat_val = "%.2f %s" % (tx_value * Decimal(tx_rate)/Decimal("100.0"), "USD")
                        except ValueError:
                            tx_fiat_val = "%.2f %s" % (self.btc_rate * Decimal(tx_info['value'])/100000000 , "USD")
                        except KeyError:
       t@@ -594,7 +595,7 @@ class Plugin(BasePlugin):
                        tx_time_str = datetime.datetime.fromtimestamp(tx_time).strftime('%Y-%m-%d')
                        try:
                            num = self.resp_hist[tx_time_str].replace(',','')
       -                    tx_fiat_val = "%.2f %s" % (Decimal(str(tx_info['value'])) / 100000000 * Decimal(num), self.fiat_unit())
       +                    tx_fiat_val = "%.2f %s" % (tx_value * Decimal(num), self.fiat_unit())
                        except KeyError:
                            tx_fiat_val = _("No data")
        
   DIR diff --git a/plugins/plot.py b/plugins/plot.py
       t@@ -42,30 +42,28 @@ class Plugin(BasePlugin):
            @hook
            def export_history_dialog(self, d,hbox):
                self.wallet = d.wallet
       -
       -        history = self.wallet.get_tx_history()
       -
       +        history = self.wallet.get_history()
                if len(history) > 0:
                    b = QPushButton(_("Preview plot"))
                    hbox.addWidget(b)
       -            b.clicked.connect(lambda: self.do_plot(self.wallet))
       +            b.clicked.connect(lambda: self.do_plot(self.wallet, history))
                else:
                    b = QPushButton(_("No history to plot"))
                    hbox.addWidget(b)
        
        
       -
       -    def do_plot(self,wallet):
       -        history = wallet.get_tx_history()
       +    def do_plot(self, wallet, history):
                balance_Val=[]
                fee_val=[]
                value_val=[]
                datenums=[]
       -        unknown_trans=0
       -        pending_trans=0
       -        counter_trans=0
       +        unknown_trans = 0
       +        pending_trans = 0
       +        counter_trans = 0
       +        balance = 0
                for item in history:
       -            tx_hash, confirmations, is_mine, value, fee, balance, timestamp = item
       +            tx_hash, confirmations, value, timestamp = item
       +            balance += value
                    if confirmations:
                        if timestamp is not None:
                            try:
       t@@ -73,24 +71,15 @@ class Plugin(BasePlugin):
                                balance_string = format_satoshis(balance, False)
                                balance_Val.append(float((format_satoshis(balance,False)))*1000.0)
                            except [RuntimeError, TypeError, NameError] as reason:
       -                        unknown_trans=unknown_trans+1
       +                        unknown_trans += 1
                                pass
                        else:
       -                    unknown_trans=unknown_trans+1
       -            else:
       -                pending_trans=pending_trans+1
       -
       -            if value is not None:
       -                value_string = format_satoshis(value, True)
       -                value_val.append(float(value_string)*1000.0)
       +                    unknown_trans += 1
                    else:
       -                value_string = '--'
       +                pending_trans += 1
        
       -            if fee is not None:
       -                fee_string = format_satoshis(fee, True)
       -                fee_val.append(float(fee_string))
       -            else:
       -                fee_string = '0'
       +            value_string = format_satoshis(value, True)
       +            value_val.append(float(value_string)*1000.0)
        
                    if tx_hash:
                        label, is_default_label = wallet.get_label(tx_hash)
       t@@ -139,12 +128,9 @@ class Plugin(BasePlugin):
        
                xfmt = md.DateFormatter('%Y-%m-%d')
                ax.xaxis.set_major_formatter(xfmt)
       -        axarr[1].plot(datenums,fee_val,marker='o',linestyle='-',color='red',label='Fee')
                axarr[1].plot(datenums,value_val,marker='o',linestyle='-',color='green',label='Value')
        
        
       -
       -
                axarr[1].legend(loc='upper left')
             #   plt.annotate('unknown transaction = %d \n pending transactions = %d' %(unknown_trans,pending_trans),xy=(0.7,0.05),xycoords='axes fraction',size=12)
                plt.show()