commit 03ef1626d39f8bced8eb79ee82a2b9041b0d8c52
parent 4fd0bf5db3a41814259960ba41af22bdae0e09f4
Author: lash <dev@holbrook.no>
Date: Sat, 17 Jan 2026 12:10:37 +0000
Correct serial and state for ledger for viewing entries
Diffstat:
7 files changed, 50 insertions(+), 27 deletions(-)
diff --git a/dummy/tests/store.py b/dummy/tests/store.py
@@ -48,14 +48,15 @@ class TestStore(unittest.TestCase):
def test_store_ledger(self):
uidx = UnitIndex('FOO')
wallet = DemoWallet()
- acl = ACL()
- acl.add(wallet.pubkey())
- ledger = Ledger(uidx, serial=42, base=self.parent)
+ #acl = ACL()
+ #acl.add(wallet.pubkey())
+ acl = ACL.from_wallet(wallet)
+ ledger = Ledger(uidx, serial=41, 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(42, 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 = 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)
@@ -67,13 +68,14 @@ class TestStore(unittest.TestCase):
dtreg = datetime.datetime.now()
dst = EntryPart('FOO', 'expense', 'bar', 4200)
src = EntryPart('FOO', 'liability', 'bar', 4200, debit=True)
- o = Entry(43, datetime.datetime.strptime('2025-11-12', '%Y-%m-%d'), parent=parent, ref=ref, description=description, tx_datereg=dtreg, unitindex=uidx)
+ 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 = Ledger(uidx, serial=42, base=self.parent, acl=acl, topic=ledger.topic)
+
+ ledger.sign()
+ #ledger.reset()
store.load()
diff --git a/dummy/usawa/data/schema.xsd b/dummy/usawa/data/schema.xsd
@@ -27,7 +27,7 @@
<xs:element name="digest" type="Digest" minOccurs="1" maxOccurs="1"/>
<xs:element name="sig" type="Signature" minOccurs="1" maxOccurs="unbounded" />
</xs:sequence>
- <xs:attribute name="serial" type="xs:positiveInteger" />
+ <xs:attribute name="serial" type="xs:nonNegativeInteger" />
</xs:complexType>
<xs:complexType name="Balance">
diff --git a/dummy/usawa/entry.py b/dummy/usawa/entry.py
@@ -107,12 +107,8 @@ class Entry:
If parent is not specified, the entry will be expected to be the first entry in the ledger, and the serial number must be zero.
- :param src: The account debited, as a path-like string.
- :type src: str
- :param dst: The account credited, as a path-like string.
- :type dst: str
- :param unit: The symbol of the unit of account for the transation.
- :type dst: str
+ If unitindex is not specified, unit symbols in entry parts will not be validated as they are added.
+
:param serial: The entry's serial number in the ledger.
:type serial: int
:param tx_date: The date of the transaction.
@@ -125,6 +121,8 @@ class Entry:
:type parent: str
:param tx_datereg: Date and time entry was added to the ledger. If not set, the current date and time will be used.
:type tx_datereg: datetime.datetime
+ :param unitindex: Unix index top validate unit symbols and exchange rates against.
+ :type unit: UnitIndex
:todo: Add an optional time part to the entry, e.g. for POS items.
:todo: Implement throw error if non-zero serial has zero-value digest.
:todo: Check hashlen of parent against actual digest length defined in digest_algo.
@@ -154,6 +152,16 @@ class Entry:
self.credit = []
+ """Add an entry part to the entry.
+
+ At least one debit and one credit item must be added to be valid.
+
+ :param part: Entry part to add
+ :type part: EntryPart
+ :param debit: If true, entry part will be added to debits. If false, credits.
+ :type debit: boolean
+ :raises KeyError: Symbol does not exist in unit index.
+ """
def add_part(self, part, debit=False):
if self.uidx != None:
self.uidx.sym(part.unit)
@@ -183,11 +191,13 @@ class Entry:
"""Add a signature over the ledger state of the entry.
:param keyid: Key identifier, e.g. as used in a DID.
- :type keyid: str
+ :type keyid: str or bytes
:param sigdata: Key identifier, e.g. as used in a DID.
:type keyid: bytes
"""
def add_signature(self, keyid, sigdata):
+ if isinstance(keyid, bytes):
+ keyid = keyid.hex()
self.sigs[keyid] = sigdata
@@ -355,6 +365,7 @@ class Entry:
sigs,
data,
]
+
return rencode.dumps(d)
@@ -387,6 +398,7 @@ class Entry:
wallet = DemoWallet(publickey=pubkey_bytes)
# TODO: demo only takes into account single signature
sig = v[1][0]
+ logg.debug('our sig {} {}'.format(pubkey_bytes.hex(), sig.hex()))
entry = Entry.deserialize(v[2])
(z, b) = entry.sum()
if not wallet.verify(z, sig):
diff --git a/dummy/usawa/error.py b/dummy/usawa/error.py
@@ -4,3 +4,7 @@ class ACLError(Exception):
class VerifyError(Exception):
pass
+
+
+class ValidateError(Exception):
+ pass
diff --git a/dummy/usawa/ledger.py b/dummy/usawa/ledger.py
@@ -166,7 +166,7 @@ class Ledger:
:todo: Add warnings for ignored parameters
"""
- def __init__(self, unitindex, tree=None, acl=None, serial=1, base=DEFAULTPARENT, topic=None, src=None, wallet=None):
+ def __init__(self, unitindex, tree=None, acl=None, serial=0, base=DEFAULTPARENT, topic=None, src=None, wallet=None):
self.uidx = unitindex
self.sigs = {}
self.entries = {}
@@ -618,12 +618,16 @@ class Ledger:
"""
def truncate(self, modify_tree=True):
self.base = self.cur
- self.serial_base = self.serial
+ self.base_serial = self.serial
if not modify_tree:
return
inc_tree = self.tree.find('incoming', namespaces=nsmap())
+ inc_tree.set('serial', str(self.base_serial))
+ o = inc_tree.find('digest', namespaces=nsmap())
+ o.text = self.base.hex()
+
# xpath does not support empty namespace names
ns = {'ns': nsmap()[None]}
for k in self.running:
@@ -727,4 +731,4 @@ class Ledger:
def __str__(self):
- return "state: " + self.base.hex()
+ return "state: " + self.base.hex() + " serial " + str(self.serial)
diff --git a/dummy/usawa/runnable/add.py b/dummy/usawa/runnable/add.py
@@ -155,13 +155,12 @@ def do_interactive(ctx):
v = input_or_default('Entry {} account'.format(k), o[k][1])
o[k][1] = parse_account(v)
- if amount != None:
- amount *= -1
- else:
+ if amount == None:
v = input_or_default('Entry {} amount'.format(k), ctx.amount)
amount = parse_amount(uidx, ctx.unit, v)
+ amount *= -1
- ctx.part.append(EntryPart(o[k][0], o[k][1], amount, src=k=='src'))
+ ctx.part.append(EntryPart(ctx.unit, o[k][0], o[k][1], amount, debit=k=='src'))
ctx.ref = input_or_default('External ref', ctx.ref)
@@ -174,11 +173,14 @@ if arg.i:
ctx = do_interactive(ctx)
ctx.validate()
-entry = Entry(ctx.part[0], ctx.part[1], ctx.unit, ledger.next_serial(), dt, parent=ledger.current(), description=ctx.description, ref=ctx.ref)
+entry = Entry(ledger.next_serial(), dt, parent=ledger.current(), description=ctx.description, ref=ctx.ref, unitindex=ctx.uidx)
+entry.add_part(ctx.part[0], debit=True)
+entry.add_part(ctx.part[1])
entry.sign(wallet)
+logg.debug('storing entry {}'.format(entry))
store.add_entry(entry)
ledger.add_entry(entry, modify_tree=True)
-ledger.truncate()
+ledger.truncate(modify_tree=True)
ledger.sign()
ctx.f.write(ledger.to_string())
ctx.close()
diff --git a/dummy/usawa/store.py b/dummy/usawa/store.py
@@ -62,7 +62,7 @@ def pfx_entry(ledger, entry):
:returns: Prefix.
:rtype: bytes
"""
- serial = -1
+ serial = 0
if isinstance(entry, Entry):
serial = entry.serial
elif isinstance(entry, int):
@@ -164,11 +164,10 @@ class LedgerStore(Interface):
while True:
o = None
try:
- o = self.get_entry(self.ledger.serial)
+ o = self.get_entry(self.ledger.next_serial())
except FileNotFoundError:
break
self.ledger.add_entry(o, modify_tree=True)
- self.ledger.next_serial()
"""Add signing key to the store.