usawa

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

commit 0c7e0ebfc3c2b176fcaad8dbf0039feb43a03169
parent 8febfdeb102f1bbafaebdbe5671f3d88294d74f4
Author: lash <dev@holbrook.no>
Date:   Tue, 10 Mar 2026 13:12:32 -0600

Add ledger restore (from tip) to store

Diffstat:
Mdummy/tests/store.py | 36++++++++++++++++++++++++++++++++++++
Mdummy/usawa/ledger.py | 9+++++++++
Mdummy/usawa/store.py | 25+++++++++++++++++++++----
3 files changed, 66 insertions(+), 4 deletions(-)

diff --git a/dummy/tests/store.py b/dummy/tests/store.py @@ -121,5 +121,41 @@ class TestStore(unittest.TestCase): self.assertEqual(len(ledger.entries), 2) + def test_store_restore(self): + uidx = UnitIndex('FOO') + wallet = DemoWallet() + acl = ACL.from_wallet(wallet) + ledger = Ledger(uidx, base=self.parent, acl=acl, wallet=wallet) + store = LedgerStore(self.store, ledger) + + dst = EntryPart('FOO', 'asset', 'foo', 1337) + src = EntryPart('FOO', 'income', 'foo', 1337, debit=True) + o = Entry(ledger.next_serial(), datetime.datetime.strptime('2025-11-11', '%Y-%m-%d'), parent=self.parent, ref=self.ref, description=self.description, tx_datereg=self.dtreg, unitindex=uidx) + o.add_part(src, debit=True) + o.add_part(dst) + o.sign(wallet) + store.add_entry(o) + + ref = str(uuid.uuid4()) + parent = o.sum()[0] + description = 'barbarbar' + dtreg = datetime.datetime.now() + dst = EntryPart('FOO', 'expense', 'bar', 4200) + src = EntryPart('FOO', 'liability', 'bar', 4200, debit=True) + o = Entry(ledger.next_serial(), datetime.datetime.strptime('2025-11-12', '%Y-%m-%d'), parent=parent, ref=ref, description=description, tx_datereg=dtreg, unitindex=uidx) + o.add_part(src, debit=True) + o.add_part(dst) + o.sign(wallet) + store.add_entry(o) + + ledger.truncate() + s = ledger.to_string() + ledger = Ledger.from_string(s) + store = LedgerStore(self.store, ledger) + store.restore() + + self.assertEqual(len(ledger.entries), 2) + + if __name__ == '__main__': unittest.main() diff --git a/dummy/usawa/ledger.py b/dummy/usawa/ledger.py @@ -720,6 +720,15 @@ class Ledger: return self.cur + """Returns the serial of the latest entry added to the ledger. + + :return: Serial. + :rtype: int + """ + def current_serial(self): + return self.serial + + """Generate canonical XML for signature material. :return: Signature material. diff --git a/dummy/usawa/store.py b/dummy/usawa/store.py @@ -210,11 +210,9 @@ class LedgerStore(Interface): return Asset.deserialize(v, digest) - """Flush ledger and load all entries from store. + """Load all entries from store, oldest to newest. - The existing state will always be lost. - - If the load fails, the ledger will be reset before returning. + Must be called on an unused ledger instance. Using with a ledger that contains or has contains entries is undefined. :raises FileNotFoundError: If an entry cannot be found. """ @@ -229,6 +227,25 @@ class LedgerStore(Interface): self.ledger.add_entry(o) + """Load all entries from store, newest to oldest. + + Must be called on an unused ledger instance. Using with a ledger that contains or has contains entries is undefined. + + :raises FileNotFoundError: If an entry cannot be found. + """ + def restore(self, until=0, acl=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) + #except FileNotFoundError: + # break + self.ledger.add_entry(o, check_parent=False) + i -= 1 + + """Add signing key to the store. If this is the first key in the store, it will be set as default.