X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=backends%2Fbitcoind%2Fstorage.py;h=6dcf0277265dac70880004a407ac0b2753d194c3;hb=2a8c19f5f3bdae38e9ef89bf7515dc10a7bb1893;hp=2aba8bcb713015e7f1402dd463da8e38ca6071ec;hpb=36d4d33f87edeeb17bb3c03037e3b45afe21f2f2;p=electrum-server.git diff --git a/backends/bitcoind/storage.py b/backends/bitcoind/storage.py index 2aba8bc..6dcf027 100644 --- a/backends/bitcoind/storage.py +++ b/backends/bitcoind/storage.py @@ -25,8 +25,8 @@ class Storage(object): self.test_reorgs = test_reorgs try: - self.db_tree = plyvel.DB(os.path.join(self.dbpath,'addr'), create_if_missing=True, compression=None) - self.db = plyvel.DB(os.path.join(self.dbpath,'utxo'), create_if_missing=True, compression=None) + self.db_utxo = plyvel.DB(os.path.join(self.dbpath,'utxo'), create_if_missing=True, compression=None) + self.db_addr = plyvel.DB(os.path.join(self.dbpath,'addr'), create_if_missing=True, compression=None) self.db_hist = plyvel.DB(os.path.join(self.dbpath,'hist'), create_if_missing=True, compression=None) self.db_undo = plyvel.DB(os.path.join(self.dbpath,'undo'), create_if_missing=True, compression=None) except: @@ -54,6 +54,11 @@ class Storage(object): return + # compute root hash + d = self.get_node('') + self.root_hash, v = self.get_node_hash('',d,None) + print_log("UTXO tree root hash:", self.root_hash.encode('hex')) + print_log("Coins in database:", v) # convert between bitcoin addresses and 20 bytes keys used for storage. def address_to_key(self, addr): @@ -63,23 +68,83 @@ class Storage(object): return hash_160_to_bc_address(addr) + def get_proof(self, addr): + key = self.address_to_key(addr) + i = self.db_utxo.iterator(start=key) + k, _ = i.next() + + p = self.get_path(k) + p.append(k) + + out = [] + for item in p: + v = self.db_utxo.get(item) + out.append((item.encode('hex'), v.encode('hex'))) + + return out + + + def get_balance(self, addr): + key = self.address_to_key(addr) + i = self.db_utxo.iterator(start=key) + k, _ = i.next() + if not k.startswith(key): + return 0 + p = self.get_parent(k) + d = self.get_node(p) + letter = k[len(p)] + return d[letter][1] + + + def listunspent(self, addr): + key = self.address_to_key(addr) + + out = [] + for k, v in self.db_utxo.iterator(start=key): + if not k.startswith(key): + break + if len(k) == KEYLENGTH: + txid = k[20:52].encode('hex') + txpos = hex_to_int(k[52:56]) + h = hex_to_int(v[8:12]) + v = hex_to_int(v[0:8]) + out.append({'tx_hash': txid, 'tx_pos':txpos, 'height': h, 'value':v}) + + out.sort(key=lambda x:x['height']) + return out + + def get_history(self, addr): - addr = self.address_to_key(addr) - x = self.db_tree.get(addr) - if x is None: - return '' - try: - _hash, v, h = x - return h - except: - traceback.print_exc(file=sys.stdout) - self.shared.stop() - raise + out = [] + + o = self.listunspent(addr) + for item in o: + out.append((item['tx_hash'], item['height'])) + + h = self.db_hist.get(addr) + + while h: + item = h[0:80] + h = h[80:] + txi = item[0:32].encode('hex') + hi = hex_to_int(item[36:40]) + txo = item[40:72].encode('hex') + ho = hex_to_int(item[76:80]) + out.append((txi, hi)) + out.append((txo, ho)) + + # sort + out.sort(key=lambda x:x[1]) + + # uniqueness + out = set(out) + + return map(lambda x: {'tx_hash':x[0], 'height':x[1]}, out) + def get_address(self, txi): - addr = self.db.get(txi) - return self.key_to_address(addr) if addr else None + return self.db_addr.get(txi) def get_undo_info(self, height): @@ -124,12 +189,12 @@ class Storage(object): if batch: batch.put(key, out) else: - self.db_tree.put(key, out) + self.db_utxo.put(key, out) def get_node(self, key): - s = self.db_tree.get(key) + s = self.db_utxo.get(key) if s is None: return @@ -156,7 +221,7 @@ class Storage(object): word = target key = '' path = [ '' ] - i = self.db_tree.iterator() + i = self.db_utxo.iterator() while key != target: @@ -204,7 +269,7 @@ class Storage(object): # write s = (int_to_hex(value, 8) + int_to_hex(height,4)).decode('hex') - self.db_tree.put(target, s) + self.db_utxo.put(target, s) # the hash of a node is the txid _hash = target[20:52] self.update_node_hash(target, path, _hash, value) @@ -263,7 +328,7 @@ class Storage(object): # batch write modified nodes - batch = self.db_tree.write_batch() + batch = self.db_utxo.write_batch() for k, v in nodes.items(): self.put_node(k, v, batch) batch.write() @@ -293,7 +358,7 @@ class Storage(object): word = target key = '' path = [ '' ] - i = self.db_tree.iterator(start='') + i = self.db_utxo.iterator(start='') while key != target: @@ -316,7 +381,7 @@ class Storage(object): assert key not in path path.append(key) else: - print_log('not in tree', self.db_tree.get(key+word[0]), new_key.encode('hex')) + print_log('not in tree', self.db_utxo.get(key+word[0]), new_key.encode('hex')) return False else: assert key in path @@ -328,12 +393,12 @@ class Storage(object): def delete_address(self, leaf): path = self.get_path(leaf) if path is False: - print_log("addr not in tree", leaf.encode('hex'), self.key_to_address(leaf[0:20]), self.db_tree.get(leaf)) + print_log("addr not in tree", leaf.encode('hex'), self.key_to_address(leaf[0:20]), self.db_utxo.get(leaf)) raise - s = self.db_tree.get(leaf) + s = self.db_utxo.get(leaf) - self.db_tree.delete(leaf) + self.db_utxo.delete(leaf) if leaf in self.hash_list: self.hash_list.pop(leaf) @@ -346,12 +411,12 @@ class Storage(object): if len(items) == 1: letter, v = items.items()[0] - self.db_tree.delete(parent) + self.db_utxo.delete(parent) if parent in self.hash_list: self.hash_list.pop(parent) # we need the exact length for the iteration - i = self.db_tree.iterator() + i = self.db_utxo.iterator() i.seek(parent+letter) k, v = i.next() @@ -372,7 +437,7 @@ class Storage(object): def get_children(self, x): - i = self.db_tree.iterator() + i = self.db_utxo.iterator() l = 0 while l <256: i.seek(x+chr(l)) @@ -391,7 +456,7 @@ class Storage(object): def get_parent(self, x): """ return parent and skip string""" - i = self.db_tree.iterator() + i = self.db_utxo.iterator() for j in range(len(x)): p = x[0:-j-1] i.seek(p) @@ -412,8 +477,8 @@ class Storage(object): def close(self): - self.db_tree.close() - self.db.close() + self.db_utxo.close() + self.db_addr.close() self.db_hist.close() self.db_undo.close() @@ -426,7 +491,7 @@ class Storage(object): self.add_address(key + txo, value, tx_height) # backlink - self.db.put(txo, key) + self.db_addr.put(txo, addr) @@ -438,9 +503,16 @@ class Storage(object): self.delete_address(key + txo) # backlink - self.db.delete(txo) + self.db_addr.delete(txo) + def get_utxo_value(self, addr, txi): + key = self.address_to_key(addr) + leaf = key + txi + s = self.db_utxo.get(leaf) + value = hex_to_int(s[0:8]) + return value + def set_spent(self, addr, txi, txid, index, height, undo): key = self.address_to_key(addr) @@ -452,7 +524,7 @@ class Storage(object): undo[leaf] = value, in_height # delete backlink txi-> addr - self.db.delete(txi) + self.db_addr.delete(txi) # add to history s = self.db_hist.get(addr) @@ -469,7 +541,7 @@ class Storage(object): leaf = key + txi # restore backlink - self.db.put(txi, key) + self.db_addr.put(txi, addr) v, height = undo.pop(leaf) self.add_address(leaf, v, height)