commit f9524dceb62b126823d9efdfe37bac85e186b1ac
parent 302499f6b28b276311161651aa69c7052794554b
Author: lash <dev@holbrook.no>
Date: Wed, 14 Jan 2026 14:03:21 +0000
Make legder create cli tool xml validate with schema
Diffstat:
4 files changed, 106 insertions(+), 29 deletions(-)
diff --git a/dummy/schema.xsd b/dummy/schema.xsd
@@ -132,7 +132,7 @@
<xs:complexType name="EntryPart">
<xs:sequence>
<xs:element name="account" type="xs:string" />
- <xs:element name="amount" type="xs:positiveInteger" />
+ <xs:element name="amount" type="xs:nonNegativeInteger" />
</xs:sequence>
<xs:attribute name="type" type="xs:string" />
</xs:complexType>
diff --git a/dummy/usawa/crypto.py b/dummy/usawa/crypto.py
@@ -12,6 +12,27 @@ class Wallet:
"""Wallet is an unimplemented class defining the interface for wallet operations.
"""
+ """Get the did URI for the wallet identity.
+
+ :returns: DID URI
+ :rtype: str
+ """
+ def __init__(self, did_type='usawalocal'):
+ self.did_type = did_type
+
+
+ def did(self):
+ return self.did_type
+
+
+ def did_uri(self):
+ return 'did:' + self.did_type + ':' + self.address()
+
+
+ def address(self):
+ return self.pubkey()
+
+
def sign(self, v):
"""Sign data with the wallet's private key.
@@ -56,6 +77,7 @@ class DemoWallet(Wallet):
"""
def __init__(self, privatekey=None, publickey=None):
+ super(DemoWallet, self).__init__()
self.pk = None
publickey_chk = None
if privatekey == None:
diff --git a/dummy/usawa/ledger.py b/dummy/usawa/ledger.py
@@ -163,7 +163,7 @@ class Ledger:
:todo: Add warnings for ignored parameters
"""
- def __init__(self, unitindex, tree=None, acl=None, serial=0, base=DEFAULTPARENT, topic=None, src=None):
+ def __init__(self, unitindex, tree=None, acl=None, serial=1, base=DEFAULTPARENT, topic=None, src=None, wallet=None):
self.uidx = unitindex
self.sigs = {}
self.entries = {}
@@ -174,6 +174,7 @@ class Ledger:
self.src = src
self.topic = topic
self.acl = acl
+ self.wallet = wallet
if self.topic == None:
self.topic = os.urandom(64)
if base == None:
@@ -188,6 +189,25 @@ class Ledger:
logg.debug('ledger base {} serial {} from topic {}'.format(self.base.hex(), self.serial, self.topic.hex()))
+ """Wallet to add public key for in identities element in ledger XML.
+
+ :param v: Wallet to set
+ :type v: usawa.Wallet
+ """
+ def set_wallet(self, v):
+ self.wallet = v
+ self.apply_wallet()
+
+
+ def apply_wallet(self):
+ incoming = self.tree.find('incoming', namespaces=nsmap())
+ v = self.wallet.pubkey()
+ o = lxml.etree.Element(NSPREFIX + 'identity', nsmap=nsmap())
+ o.set('keyid', v.hex())
+ o.set('didtype', self.wallet.did())
+ incoming.addprevious(o)
+
+
"""Retrieve the serial that will be assigned to the next entry.
:rtype: int
@@ -217,7 +237,9 @@ class Ledger:
:rtype: None
:todo: swapping tree keeps two trees in memory, perhaps it can be more efficient
"""
- def reset(self, src=None, topic=None):
+ def reset(self, src=None, topic=None, wallet=None):
+ if wallet != None:
+ self.set_wallet(wallet)
self.serial = self.base_serial
self.cur = self.base
self.entries[self.uidx.base] = []
@@ -258,42 +280,74 @@ class Ledger:
#units.append(unit)
#self.tree.append(units)
+
+ ns = {'ns': nsmap()[None]}
+
+ # TODO: move identity tree generation to wallet object
+ identities = []
+ for tree_identity in self.tree.xpath('ns:identity', namespaces=ns):
+ v = tree_identity.text
+ identities.append(v)
+ identity = lxml.etree.SubElement(tree, NSPREFIX + 'identity', nsmap=nsmap())
+ identity.text = v
+ identity.set('keyid', tree_identity.get('keyid'))
+ identity.set('didtype', tree_identity.get('didtype'))
+
+ if wallet != None:
+ v = wallet.address()
+ if wallet.pubkey() not in identities:
+ identities.append(v)
+ identity = lxml.etree.SubElement(tree, NSPREFIX + 'identity', nsmap=nsmap())
+ identity.set('keyid', v.hex())
+ identity.set('didtype', wallet.did())
+
+ if len(identities) == 0:
+ logg.warning('no identities in xml, need at least one to validate against schema')
+
incoming = lxml.etree.SubElement(tree, NSPREFIX + 'incoming', nsmap=nsmap())
incoming.set('serial', str(self.serial))
-
+
# swap running and apply all bases
- ns = {'ns': nsmap()[None]}
self.running = {}
incoming_old = self.tree.find('incoming', namespaces=nsmap())
logg.debug('inc {}'.format(lxml.etree.tostring(incoming_old)))
- i = 0
- for tree_real in incoming_old.xpath('ns:real[@unit]', namespaces=ns):
- unit = tree_real.get('unit')
- v = tree_real.find('asset', namespaces=nsmap())
- asset = int(v.text)
- v = tree_real.find('liability', namespaces=nsmap())
- liability = int(v.text)
- self.running[unit] = RunningTotal(unit, self.uidx, asset=asset, liability=liability)
- real = lxml.etree.SubElement(incoming, NSPREFIX + 'real', nsmap=nsmap())
- real.attrib['unit'] = unit
- o = lxml.etree.SubElement(real, NSPREFIX + 'asset', nsmap=nsmap())
- o.text = str(self.running[unit].asset)
- o = lxml.etree.SubElement(real, NSPREFIX + 'liability', nsmap=nsmap())
- o.text = str(self.running[unit].liability)
- i += 1
-
- if i == 0:
- real = lxml.etree.SubElement(incoming, NSPREFIX + 'real', nsmap=nsmap())
- real.set('unit', self.uidx.default_unit)
- o = lxml.etree.SubElement(real, NSPREFIX + 'asset', nsmap=nsmap())
- o.text = '0'
- o = lxml.etree.SubElement(real, NSPREFIX + 'liability', nsmap=nsmap())
- o.text = '0'
+ if incoming_old != None:
+ i = 0
+ for tree_real in incoming_old.xpath('ns:real[@unit]', namespaces=ns):
+ unit = tree_real.get('unit')
+ v = tree_real.find('asset', namespaces=nsmap())
+ asset = int(v.text)
+ v = tree_real.find('liability', namespaces=nsmap())
+ liability = int(v.text)
+ self.running[unit] = RunningTotal(unit, self.uidx, asset=asset, liability=liability)
+ real = lxml.etree.SubElement(incoming, NSPREFIX + 'real', nsmap=nsmap())
+ real.attrib['unit'] = unit
+ o = lxml.etree.SubElement(real, NSPREFIX + 'asset', nsmap=nsmap())
+ o.text = str(self.running[unit].asset)
+ o = lxml.etree.SubElement(real, NSPREFIX + 'liability', nsmap=nsmap())
+ o.text = str(self.running[unit].liability)
+ i += 1
+
+ if i == 0:
+ real = lxml.etree.SubElement(incoming, NSPREFIX + 'real', nsmap=nsmap())
+ real.set('unit', self.uidx.default_unit)
+ o = lxml.etree.SubElement(real, NSPREFIX + 'asset', nsmap=nsmap())
+ o.text = '0'
+ o = lxml.etree.SubElement(real, NSPREFIX + 'liability', nsmap=nsmap())
+ o.text = '0'
o = lxml.etree.SubElement(incoming, NSPREFIX + 'digest', nsmap=nsmap())
o.attrib['algo'] = 'sha512'
o.text = self.base.hex()
+
+ if wallet != None:
+ logg.warning("the incoming state signature is not yet implemented and is currently just a zero-value string.")
+ o = lxml.etree.SubElement(incoming, NSPREFIX + 'sig', nsmap=nsmap())
+ o.set('keyid', self.wallet.address().hex())
+ o.set('type', 'ed25519')
+ o.text = '00' * 64
+
self.tree = tree
#incoming.append(o)
#self.tree.append(incoming)
@@ -473,7 +527,7 @@ class Ledger:
"""
def apply_tree(self, tree):
start = self.serial
- last = 0
+ last = 1
i = 0
for v in tree.iter(NSPREFIX + 'entry'):
i += 1
diff --git a/dummy/usawa/runnable/create.py b/dummy/usawa/runnable/create.py
@@ -149,5 +149,6 @@ if wallet == None:
wallet = DemoWallet(privatekey=pk)
logg.info('loaded existing key. {}'.format(wallet.pubkey().hex()))
+ledger.reset(topic=ctx.topic, src=ctx.uri, wallet=wallet)
ctx.f.write(ledger.to_string())
ctx.close()