URI: 
       tstorage upgrade: convert lists to dict (txi, txo, revocation_store channels) - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit b08947a506109a74757216f121456c7ab88dafeb
   DIR parent 63963323be8052fc19e51ce15e50ea9bcc1a955f
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Tue,  4 Feb 2020 12:11:18 +0100
       
       storage upgrade: convert lists to dict (txi, txo, revocation_store channels)
       
       Diffstat:
         M electrum/lnutil.py                  |      15 +++++++--------
         M electrum/lnworker.py                |       4 ++--
         M electrum/wallet_db.py               |      61 ++++++++++++++++++++++++-------
       
       3 files changed, 56 insertions(+), 24 deletions(-)
       ---
   DIR diff --git a/electrum/lnutil.py b/electrum/lnutil.py
       t@@ -180,12 +180,10 @@ class RevocationStore:
            START_INDEX = 2 ** 48 - 1
        
            def __init__(self, storage):
       -        self.buckets = [None] * 49
       -        self.index = self.START_INDEX
       -        if storage:
       -            decode = lambda to_decode: ShachainElement(bfh(to_decode[0]), int(to_decode[1]))
       -            self.buckets = [k if k is None else decode(k) for k in storage["buckets"]]
       -            self.index = storage["index"]
       +        self.index = storage.get('index', self.START_INDEX)
       +        buckets = storage.get('buckets', {})
       +        decode = lambda to_decode: ShachainElement(bfh(to_decode[0]), int(to_decode[1]))
       +        self.buckets = dict((int(k), decode(v)) for k, v in buckets.items())
        
            def add_next_entry(self, hsh):
                new_element = ShachainElement(index=self.index, secret=hsh)
       t@@ -200,7 +198,8 @@ class RevocationStore:
        
            def retrieve_secret(self, index: int) -> bytes:
                assert index <= self.START_INDEX, index
       -        for bucket in self.buckets:
       +        for i in range(0, 49):
       +            bucket = self.buckets.get(i)
                    if bucket is None:
                        raise UnableToDeriveSecret()
                    try:
       t@@ -211,7 +210,7 @@ class RevocationStore:
                raise UnableToDeriveSecret()
        
            def serialize(self):
       -        return {"index": self.index, "buckets": [[bh2u(k.secret), k.index] if k is not None else None for k in self.buckets]}
       +        return {"index": self.index, "buckets": dict( (k, [bh2u(v.secret), v.index]) for k, v in self.buckets.items()) }
        
            def __eq__(self, o):
                return type(o) is RevocationStore and self.serialize() == o.serialize()
   DIR diff --git a/electrum/lnworker.py b/electrum/lnworker.py
       t@@ -355,7 +355,7 @@ class LNWallet(LNWorker):
        
                # note: accessing channels (besides simple lookup) needs self.lock!
                self.channels = {}  # type: Dict[bytes, Channel]
       -        for x in wallet.storage.get("channels", []):
       +        for x in wallet.storage.get("channels", {}).values():
                    c = Channel(x, sweep_address=self.sweep_address, lnworker=self)
                    self.channels[c.channel_id] = c
                # timestamps of opening and closing transactions
       t@@ -617,7 +617,7 @@ class LNWallet(LNWorker):
        
            def save_channels(self):
                with self.lock:
       -            dumped = [x.serialize() for x in self.channels.values()]
       +            dumped = dict( (k.hex(), c.serialize()) for k, c in self.channels.items() )
                self.storage.put("channels", dumped)
                self.storage.write()
        
   DIR diff --git a/electrum/wallet_db.py b/electrum/wallet_db.py
       t@@ -40,7 +40,7 @@ from .json_db import JsonDB, locked, modifier
        
        OLD_SEED_VERSION = 4        # electrum versions < 2.0
        NEW_SEED_VERSION = 11       # electrum versions >= 2.0
       -FINAL_SEED_VERSION = 23     # electrum >= 2.7 will set this to prevent
       +FINAL_SEED_VERSION = 24     # electrum >= 2.7 will set this to prevent
                                    # old versions from overwriting new format
        
        
       t@@ -164,6 +164,7 @@ class WalletDB(JsonDB):
                self._convert_version_21()
                self._convert_version_22()
                self._convert_version_23()
       +        self._convert_version_24()
                self.put('seed_version', FINAL_SEED_VERSION)  # just to be sure
        
                self._after_upgrade_tasks()
       t@@ -489,6 +490,43 @@ class WalletDB(JsonDB):
        
                self.data['seed_version'] = 23
        
       +    def _convert_version_24(self):
       +        if not self._is_upgrade_method_needed(23, 23):
       +            return
       +        channels = self.get('channels', [])
       +        for c in channels:
       +            # convert revocation store to dict
       +            r = c['revocation_store']
       +            d = {}
       +            for i in range(49):
       +                v = r['buckets'][i]
       +                if v is not None:
       +                    d[str(i)] = v
       +            r['buckets'] = d
       +            c['revocation_store'] = r
       +        # convert channels to dict
       +        self.data['channels'] = { x['channel_id']: x for x in channels }
       +        # convert txi & txo
       +        txi = self.get('txi', {})
       +        for tx_hash, d in txi.items():
       +            d2 = {}
       +            for addr, l in d.items():
       +                d2[addr] = {}
       +                for ser, v in l:
       +                    d2[addr][ser] = v
       +            txi[tx_hash] = d2
       +        self.data['txi'] = txi
       +        txo = self.get('txo', {})
       +        for tx_hash, d in txo.items():
       +            d2 = {}
       +            for addr, l in d.items():
       +                d2[addr] = {}
       +                for n, v, cb in l:
       +                    d2[addr][str(n)] = (v, cb)
       +            txo[tx_hash] = d2
       +        self.data['txo'] = txo
       +
       +        self.data['seed_version'] = 24
        
            def _convert_imported(self):
                if not self._is_upgrade_method_needed(0, 13):
       t@@ -581,12 +619,14 @@ class WalletDB(JsonDB):
            @locked
            def get_txi_addr(self, tx_hash, address) -> Iterable[Tuple[str, int]]:
                """Returns an iterable of (prev_outpoint, value)."""
       -        return self.txi.get(tx_hash, {}).get(address, []).copy()
       +        d = self.txi.get(tx_hash, {}).get(address, {})
       +        return list(d.items())
        
            @locked
            def get_txo_addr(self, tx_hash, address) -> Iterable[Tuple[int, int, bool]]:
                """Returns an iterable of (output_index, value, is_coinbase)."""
       -        return self.txo.get(tx_hash, {}).get(address, []).copy()
       +        d = self.txo.get(tx_hash, {}).get(address, {})
       +        return [(int(n), v, cb) for (n, (v, cb)) in d.items()]
        
            @modifier
            def add_txi_addr(self, tx_hash, addr, ser, v):
       t@@ -594,9 +634,8 @@ class WalletDB(JsonDB):
                    self.txi[tx_hash] = {}
                d = self.txi[tx_hash]
                if addr not in d:
       -            # note that as this is a set, we can ignore "duplicates"
       -            d[addr] = set()
       -        d[addr].add((ser, v))
       +            d[addr] = {}
       +        d[addr][ser] = v
        
            @modifier
            def add_txo_addr(self, tx_hash, addr, n, v, is_coinbase):
       t@@ -604,9 +643,8 @@ class WalletDB(JsonDB):
                    self.txo[tx_hash] = {}
                d = self.txo[tx_hash]
                if addr not in d:
       -            # note that as this is a set, we can ignore "duplicates"
       -            d[addr] = set()
       -        d[addr].add((n, v, is_coinbase))
       +            d[addr] = {}
       +        d[addr][n] = (v, is_coinbase)
        
            @locked
            def list_txi(self):
       t@@ -890,11 +928,6 @@ class WalletDB(JsonDB):
                for tx_hash, raw_tx in self.transactions.items():
                    # note: for performance, "deserialize=False" so that we will deserialize these on-demand
                    self.transactions[tx_hash] = tx_from_any(raw_tx, deserialize=False)
       -        # convert txi, txo: list to set
       -        for t in self.txi, self.txo:
       -            for d in t.values():
       -                for addr, lst in d.items():
       -                    d[addr] = set([tuple(x) for x in lst])
                # convert prevouts_by_scripthash: list to set, list to tuple
                for scripthash, lst in self._prevouts_by_scripthash.items():
                    self._prevouts_by_scripthash[scripthash] = {(prevout, value) for prevout, value in lst}