usawa

Signed, immutable accounting.
Log | Files | Refs | Submodules | LICENSE

commit 98cff8e039832040fa3fd55730724ada01312e05
parent d84c3737b7da930feded7c56b966dd9669a38185
Author: lash <dev@holbrook.no>
Date:   Sun, 24 May 2026 10:26:38 -0600

Add link index to store

Diffstat:
Mdummy/tests/link.py | 25++++++++++++++++++++++++-
Mdummy/tests/store.py | 21+++++++++++++++++++++
Mdummy/usawa/link.py | 24+++++++++++++++++++++++-
Mdummy/usawa/store.py | 45++++++++++++++++++++++++++++++++++++++++++---
4 files changed, 110 insertions(+), 5 deletions(-)

diff --git a/dummy/tests/link.py b/dummy/tests/link.py @@ -136,7 +136,6 @@ class TestLink(unittest.TestCase): entry_a_recover = store.get_entry(entry_a, linker=linker) store.get_entry(entry_b, linker=linker) linker.link_to(entry_a, entry_c) - print(">>>>>>>>>> have links {}".format(linker.links)) store.add_entry(entry_c, overwrite=True, linker=linker) linker_updated = EntryLink(self.ledger) @@ -154,5 +153,29 @@ class TestLink(unittest.TestCase): self.assertEqual(r[1], 1337) + def test_link_store_index(self): + wallet = DemoWallet() + uu = uuid.uuid4() + db = MemStore() + store = LedgerStore(db, self.ledger) + entry_a = Entry(serial=42, tx_date=datetime.datetime.now(datetime.UTC), ref=str(uu)) + entry_b = Entry(serial=666, tx_date=datetime.datetime.now(datetime.UTC)) + entry_c = Entry(serial=1337, tx_date=datetime.datetime.now(datetime.UTC)) + self.linker.link_to(entry_a, entry_b) + entry_a.sign(wallet) + store.add_entry(entry_a, linker=self.linker, update_link=True) + entry_b.sign(wallet) + store.add_entry(entry_b, linker=self.linker, update_link=True) + entry_c.sign(wallet) + store.add_entry(entry_c) + + link = EntryLink(self.ledger) + store.get_link(link, str(uu)) + r = link.get_for(entry_a) + self.assertEqual(len(r), 1) + r = link.get_for(entry_b) + self.assertEqual(len(r), 1) + + if __name__ == '__main__': unittest.main() diff --git a/dummy/tests/store.py b/dummy/tests/store.py @@ -11,6 +11,7 @@ from whee.mem import MemStore from usawa import Ledger, UnitIndex, EntryPart, Entry, DemoWallet, Asset from usawa.store import LedgerStore, AssetStore from usawa.crypto import ACL +from usawa.link import EntryLink logging.basicConfig(level=logging.DEBUG) logg = logging.getLogger() @@ -232,5 +233,25 @@ class TestStore(unittest.TestCase): #self.assertEqual(r.attachment[0].description, 'foobar') + def test_store_link(self): + uidx = UnitIndex('FOO') + ledger = Ledger(uidx, topic=b'foobar') + store = LedgerStore(self.store, ledger) + ref_a = uuid.uuid4() + ref_b = uuid.uuid4() + link = EntryLink(ledger) + entry_a = Entry.empty(serial=1, ref=str(ref_a)) + entry_b = Entry.empty(serial=2, ref=str(ref_b)) + link.link_to(entry_a, entry_b) + store.put_link(link) + + link = EntryLink(ledger) + store.get_link(link, str(ref_a)) + r = link.get_for(entry_a) + self.assertEqual(len(r), 1) + r = link.get_for(entry_b) + self.assertEqual(len(r), 1) + + if __name__ == '__main__': unittest.main() diff --git a/dummy/usawa/link.py b/dummy/usawa/link.py @@ -1,6 +1,8 @@ import uuid import logging +import rencode + from .entry import Entry logg = logging.getLogger('link') @@ -32,7 +34,11 @@ class EntryLink: self.idx_ref[entry.ref] = s logg.info('link {} added entry {}'.format(link_uuid, entry)) - + + def refs(self): + return list(self.links.keys()) + + def link_to(self, anchor, entry): if not isinstance(anchor, Entry): anchor = Entry.empty(serial=anchor.serial) @@ -45,6 +51,22 @@ class EntryLink: self.link(entry, link_uuid=v) return v + + def to_list_for(self, ref): + d = self.links[ref] + return d + + + def serialize_for(self, ref): + b = self.to_list_for(ref) + return rencode.dumps(b) + + + def deserialize_for(self, ref, data): + for v in rencode.loads(data): + entry = Entry.empty(serial=v) + self.link(entry, link_uuid=ref) + # serial to serial list def get_for(self, entry): diff --git a/dummy/usawa/store.py b/dummy/usawa/store.py @@ -1,12 +1,14 @@ import enum import os import logging +import uuid from whee import Interface from .ledger import Ledger from .entry import Entry from .asset import Asset +from usawa.link import EntryLink PFX_KEY = b'\x00' @@ -14,6 +16,7 @@ PFX_LEDGER = b'\x01' PFX_LEDGER_INDEX = b'\x02' PFX_LEDGER_LOCK = b'\x03' PFX_ENTRY = b'\x04' +PFX_ENTRY_LINK = b'\x05' PFX_UNIT_INDEX = b'\x08' PFX_ASSET = b'\x10' PFX_ASSET_INDEX = b'\x11' @@ -114,6 +117,17 @@ def pfx_asset_index(asset): return PFX_ASSET_INDEX + asset.get_ref(binary=True) +def pfx_entry_link(ref): + if isinstance(ref, str): + ref = uuid.UUID(ref) + if isinstance(ref, uuid.UUID): + ref = ref.bytes + else: + uu = uuid.UUID(bytes=ref) + ref = uu.bytes + return PFX_ENTRY_LINK + ref + + class BaseStore(Interface): def __init__(self, implementation): @@ -295,13 +309,14 @@ class EntryStore(KeyStore): :raises: ValueError if the entry is not the right object type. :raises: FileExistsError if entry is already in store. """ - def add_entry(self, entry, update_ledger=False, overwrite=False, linker=None): + def add_entry(self, entry, update_ledger=False, overwrite=False, linker=None, update_link=False): k = pfx_entry(self.ledger, entry) v = entry.wrap(linker=linker) self.db.put(k, v, exist_ok=overwrite) if update_ledger: self.ledger.add_entry(entry) - + if update_link: + self.put_link(linker, refs=linker.get(entry)) """Restore an entry from data from the store. @@ -331,7 +346,31 @@ class EntryStore(KeyStore): return entry -class LedgerStore(EntryStore, AssetStore, KeyStore): + def put_link(self, link, refs=None): + if refs == None: + refs = link.refs() + elif isinstance(refs, str): + refs = [refs] + for ref in refs: + v = link.serialize_for(ref) + k = pfx_entry_link(ref) + self.db.put(k, v, exist_ok=True) + + + def get_link(self, link, refs=None): + if isinstance(refs, str): + refs = [refs] + elif link == None or refs == None: + raise NotImplementedError('load all links not implemented yet') + for ref in refs: + k = pfx_entry_link(ref) + v = self.db.get(k) + link.deserialize_for(ref, v) + + + +#class LedgerStore(EntryStore, AssetStore, KeyStore): +class LedgerStore(EntryStore, AssetStore): """Wrapper for an implementation of the whee store that handles encoding of ledgers and entries. :param implementation: Store implementation.