diff --git a/query.py b/query.py new file mode 100644 index 0000000..1cae0ae --- /dev/null +++ b/query.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 + +# See the file "LICENSE" for information about the copyright +# and warranty status of this software. + +import os +import sys + +from server.env import Env +from server.db import DB + + +def main(): + env = Env() + os.chdir(env.db_dir) + db = DB(env) + coin = db.coin + argc = 1 + try: + limit = int(sys.argv[argc]) + argc += 1 + except: + limit = 10 + for addr in sys.argv[argc:]: + print('Address: ', addr) + hash160 = coin.address_to_hash160(addr) + n = None + for n, (tx_hash, height) in enumerate(db.get_history(hash160, limit)): + print('History #{:d}: hash: {} height: {:d}' + .format(n + 1, bytes(reversed(tx_hash)).hex(), height)) + if n is None: + print('No history') + n = None + for n, utxo in enumerate(db.get_utxos(hash160, limit)): + print('UTXOs #{:d}: hash: {} pos: {:d} height: {:d} value: {:d}' + .format(n, bytes(reversed(utxo.tx_hash)).hex(), + utxo.tx_pos, utxo.height, utxo.value)) + if n is None: + print('No UTXOs') + +if __name__ == '__main__': + main() diff --git a/server/db.py b/server/db.py index 223a887..7f0efde 100644 --- a/server/db.py +++ b/server/db.py @@ -433,27 +433,41 @@ class DB(object): return tx_hash, height + @staticmethod + def resolve_limit(limit): + if limit is None: + return -1 + assert isinstance(limit, int) and limit >= 0 + return limit + + def get_history(self, hash160, limit=1000): + '''Generator that returns an unpruned, sorted list of (tx_hash, + height) tuples of transactions that touched the address, + earliest in the blockchain first. Includes both spending and + receiving transactions. By default yields at most 1000 entries. + Set limit to None to get them all. + ''' + limit = self.resolve_limit(limit) + prefix = b'H' + hash160 + for key, hist in self.db.iterator(prefix=prefix): + a = array.array('I') + a.frombytes(hist) + for tx_num in a: + if limit == 0: + return + yield self.get_tx_hash(tx_num) + limit -= 1 + def get_balance(self, hash160): '''Returns the confirmed balance of an address.''' - utxos = self.get_utxos(hash_160) - return sum(utxo.value for utxo in utxos) + return sum(utxo.value for utxo in self.get_utxos(hash_160, limit=None)) - def get_history(self, hash160): - '''Returns an unpruned, sorted list of (tx_hash, height) tuples of - transactions that touched the address, earliest in the - blockchain first. Includes both spending and receiving - transactions. - ''' - prefix = b'H' + hash160 - a = array.array('I') - for key, hist in self.db.iterator(prefix=prefix): - a.frombytes(hist) - return [self.get_tx_hash(tx_num) for tx_num in a] - - def get_utxos(self, hash160): - '''Returns all UTXOs for an address sorted such that the earliest - in the blockchain comes first. + def get_utxos(self, hash160, limit=1000): + '''Generator that yields all UTXOs for an address sorted in no + particular order. By default yields at most 1000 entries. + Set limit to None to get them all. ''' + limit = self.resolve_limit(limit) unpack = struct.unpack prefix = b'u' + hash160 utxos = [] @@ -461,10 +475,15 @@ class DB(object): (tx_pos, ) = unpack('