commit 98cff8e039832040fa3fd55730724ada01312e05
parent d84c3737b7da930feded7c56b966dd9669a38185
Author: lash <dev@holbrook.no>
Date: Sun, 24 May 2026 10:26:38 -0600
Add link index to store
Diffstat:
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.