usawa

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

commit 7f2dadcfc9d407e16559f4f36b36023c6ee10cc4
parent 6778818f21d28e73f8068fdff505c54c56004b57
Author: lash <dev@holbrook.no>
Date:   Sun, 17 May 2026 19:51:26 -0600

Enable link handling in store

Diffstat:
Mdummy/tests/link.py | 43+++++++++++++++++++++++++++++++++++++------
Mdummy/usawa/entry.py | 10+++++-----
Mdummy/usawa/store.py | 16++++++++--------
3 files changed, 50 insertions(+), 19 deletions(-)

diff --git a/dummy/tests/link.py b/dummy/tests/link.py @@ -4,7 +4,9 @@ import unittest import os import uuid -from usawa import Entry, Ledger, UnitIndex +from whee.mem import MemStore +from usawa.store import LedgerStore +from usawa import Entry, Ledger, UnitIndex, DemoWallet from usawa.link import EntryLink logging.basicConfig(level=logging.DEBUG) @@ -20,7 +22,7 @@ class TestLink(unittest.TestCase): self.linker = EntryLink(self.ledger) - def test_dup(self): + def test_link_dup(self): o = Entry(serial=42, tx_date=datetime.datetime.now(datetime.UTC)) self.linker.link(o) o = Entry(serial=666, tx_date=datetime.datetime.now(datetime.UTC)) @@ -30,7 +32,7 @@ class TestLink(unittest.TestCase): self.linker.link(o) - def test_explicit(self): + def test_link_explicit(self): uu = uuid.uuid4() o = Entry(serial=42, tx_date=datetime.datetime.now(datetime.UTC), ref=str(uu)) self.linker.link(o) @@ -46,7 +48,7 @@ class TestLink(unittest.TestCase): self.assertEqual(r, str(uu)) - def test_implicit(self): + def test_link_implicit(self): uu = uuid.uuid4() 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)) @@ -59,7 +61,7 @@ class TestLink(unittest.TestCase): self.assertEqual(r[0], 42) - def test_implicit_ref(self): + def test_link_implicit_ref(self): linker = EntryLink(self.ledger, refs=True) uu = uuid.uuid4() entry_a = Entry(serial=42, tx_date=datetime.datetime.now(datetime.UTC), ref=str(uu)) @@ -72,7 +74,7 @@ class TestLink(unittest.TestCase): self.assertEqual(r[1], 666) - def test_entry_serialize(self): + def test_link_entry_serialize(self): uu = uuid.uuid4() 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)) @@ -84,5 +86,34 @@ class TestLink(unittest.TestCase): self.assertEqual(v, str(uu)) + + def test_link_store(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)) + self.linker.link_to(entry_a, entry_b) + entry_a.sign(wallet) + store.add_entry(entry_a, linker=self.linker) + entry_b.sign(wallet) + store.add_entry(entry_b, linker=self.linker) + + linker = EntryLink(self.ledger) + entry_a_recover = store.get_entry(entry_a, linker=linker) + v = linker.get(entry_a_recover) + self.assertEqual(v, str(uu)) + + entry_b_recover = store.get_entry(entry_b, linker=linker) + r = linker.get_for(entry_b_recover) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], 42) + + r = linker.get_for(entry_a_recover) + self.assertEqual(len(r), 1) + self.assertEqual(r[0], 666) + + if __name__ == '__main__': unittest.main() diff --git a/dummy/usawa/entry.py b/dummy/usawa/entry.py @@ -555,17 +555,17 @@ class Entry(UsawaElement): :type wallet: usawa.Wallet :todo: Current specifying wallet has no effect. """ - def wrap(self, wallet=None): + def wrap(self, wallet=None, linker=None): digest = None data = None if wallet != None: (digest, _sig, _data) = self.sign(wallet) - data = self.serialize() + data = self.serialize(linker=linker) elif len(self.sigs) == 0: raise PermissionError('at least one signature required') else: (digest, _data) = self.sum() - data = self.serialize() + data = self.serialize(linker=linker) hdr = [] sigs = [] @@ -603,9 +603,9 @@ class Entry(UsawaElement): :todo: Current version only takes into account single signature """ @staticmethod - def unwrap(data, acl=None, unitindex=None): + def unwrap(data, acl=None, unitindex=None, linker=None): v = rencode.loads(data) - entry = Entry.deserialize(v[2], unitindex=unitindex) + entry = Entry.deserialize(v[2], unitindex=unitindex, linker=linker) pubkey_bytes = v[0][0][1] sig = v[1][0] entry.add_signature(pubkey_bytes, sig) diff --git a/dummy/usawa/store.py b/dummy/usawa/store.py @@ -295,9 +295,9 @@ 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): + def add_entry(self, entry, update_ledger=False, overwrite=False, linker=None): k = pfx_entry(self.ledger, entry) - v = entry.wrap() + v = entry.wrap(linker=linker) self.db.put(k, v, exist_ok=overwrite) if update_ledger: self.ledger.add_entry(entry) @@ -317,10 +317,10 @@ class EntryStore(KeyStore): :raises: FileExistsError if entry is already in store. :todo: optimize replacing asset stub with deserialized asset """ - def get_entry(self, entry, acl=None, unitindex=None): + def get_entry(self, entry, acl=None, unitindex=None, linker=None): k = pfx_entry(self.ledger, entry) v = self.db.get(k) - entry = Entry.unwrap(v, unitindex=unitindex) + entry = Entry.unwrap(v, unitindex=unitindex, linker=linker) # TODO: hacky! i = 0 for o in entry.attachment: @@ -366,7 +366,7 @@ class LedgerStore(EntryStore, AssetStore, KeyStore): :raises FileNotFoundError: If an entry cannot be found. """ - def load(self, acl=None, until=0, unitindex=None, entry_callback_pre=None, entry_callback_post=None): + def load(self, acl=None, until=0, unitindex=None, entry_callback_pre=None, entry_callback_post=None, linker=None): logg.debug('load ledger from store {} until {}'.format(self.ledger, until)) v = 0 while True: @@ -376,7 +376,7 @@ class LedgerStore(EntryStore, AssetStore, KeyStore): if until == v: break v = self.ledger.peek() - o = self.get_entry(v, acl=acl, unitindex=unitindex) + o = self.get_entry(v, acl=acl, unitindex=unitindex, linker=linker) self.ledger.next_serial() except FileNotFoundError as e: logg.debug('entry serial {} not found, terminating ({})'.format(v, e)) @@ -396,13 +396,13 @@ class LedgerStore(EntryStore, AssetStore, KeyStore): :raises FileNotFoundError: If an entry cannot be found. """ - def restore(self, until=0, acl=None): + def restore(self, until=0, acl=None, linker=None): logg.debug('restore ledger from store {}'.format(self.ledger)) i = self.ledger.current_serial() while i > until: logg.debug('get entry serial {} ledger {}'.format(i, self.ledger)) #try: - o = self.get_entry(i, acl=acl) + o = self.get_entry(i, acl=acl, linker=linker) #except FileNotFoundError: # break self.ledger.add_entry(o, check_parent=False)