usawa

Unnamed repository; edit this file 'description' to name the repository.
Info | Log | Files | Refs | Submodules | LICENSE

commit b7ae7b3d91fda1296ba615bea6dfcd7888e3017f
parent 8d2bcaf34de48c2dc16847b9bc9abb3b6c217509
Author: lash <dev@holbrook.no>
Date:   Wed,  5 Nov 2025 16:10:59 +0000

Make verifier example

Diffstat:
Adummy/demo.py | 230+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdummy/present.xsl | 11+++++------
Mdummy/running.xml | 32+++++++++++++++++---------------
3 files changed, 252 insertions(+), 21 deletions(-)

diff --git a/dummy/demo.py b/dummy/demo.py @@ -0,0 +1,230 @@ +import sys +import logging +import datetime + +from lxml import etree + +logging.basicConfig(level=logging.DEBUG) +logg = logging.getLogger() + + +class NoopSigVerifier: + + def verify(self, msg, key, sig): + logg.warning('using noop verifier') + return True + + +class UnitIndex: + + def __init__(self, base): + self.base = base + self.detail = {} + self.exchange = {} + + + @staticmethod + def from_tree(tree): + r = UnitIndex(tree.get('base')) + for o in tree.iter('unit'): + r.detail[o.get('sym')] = int(o.find('precision').text) + r.exchange[o.get('sym')] = int(o.find('ex').text) + r.check() + return r + + + def check(self): + self.get(self.base) + return self + + + def get(self, k): + self.detail[k] + return k + + + def to_floatstring(self, sym, v): + c = self.detail[sym] + i = c * -1 + s = str(v) + l = len(s) + if l < c: + ss = '0' * c + s = '0' + ss[:c-l] + s + return s[:i] + '.' + s[i:] + + +class Entry: + + def __init__(self, typ, amount, unit, serial, account, dt, description=None, parent=None): + self.typ = typ + self.parent = parent + self.amount = amount + self.unit = unit + self.serial = serial + self.account = account + self.dt = dt + self.attachment = [] + self.sigs = {} + + + def attach(self, mime, algo, digest, description=None, slug=None): + self.attachment.append((mime, algo, digest, description, slug,)) + + + def add_signature(self, keyid, sigdata): + self.sigs[keyid] = sigdata + + + @staticmethod + def from_tree(tree, unitindex): + o = tree.find('data') + amount = int(o.find('amount').text) + unit = unitindex.get(o.find('unit').text) + serial = int(o.find('serial').text) + account = o.find('account').text + dt = datetime.date.fromisoformat(o.find('date').text) + r = Entry(tree.get('type'), amount, unit, serial, account, dt) + return r + + +class RunningTotal: + + def __init__(self, sym, unitindex, asset=0, liability=0, income=0, expense=0): + self.sym = sym + self.asset = asset + self.liability = liability + self.income = income + self.expense = expense + self.unitindex = unitindex + + + def get_balance(self): + return self.asset - self.liability + + + def get_result(self): + return self.income - self.expense + + + def income_delta(self, v): + self.income += v + self.asset += v + + + def expense_delta(self, v): + self.expense += v + self.asset -= v + + + def asset_delta(self, v): + self.asset += v + + + def liability_delta(self, v): + self.liability += v + + + def apply_entry(self, entry): + fn = getattr(self, entry.typ + '_delta') + fn(entry.amount) + logg.debug('applied entry {} typ {} amount {} total {} balance {}'.format(entry.serial, entry.typ, entry.amount, getattr(self, entry.typ), self.unitindex.to_floatstring(self.sym, self.get_balance()))) + + + def __str__(self): + return 'running total {}: income {} expense {} asset {} liability {}'.format(self.sym, self.income, self.expense, self.asset, self.liability) + + +class Ledger: + + def __init__(self, base, unitindex, verifier=None): + self.uidx = unitindex + self.base = bytes.fromhex(base) + self.sigs = {} + if verifier == None: + verifier = NoopSigVerifier() + self.verifier = verifier + self.entries = {} + self.running = {} + + + def add_entry_from_tree(self, tree): + o = tree.find('data/parent') + + + def add_signature(self, sigdata, identity): + self.verifier.verify(self.base, identity, sigdata) + self.sigs[identity] = sigdata + logg.debug('add sig from key{}: {}'.format(identity, sigdata)) + + + @staticmethod + def from_tree(tree, unitindex, verifier=None): + o = tree.get('digest') + r = Ledger(o, unitindex, verifier=verifier) + for sig in tree.iter('sig'): + keyid = sig.get('keyid') + digest = sig.text + r.add_signature(digest, keyid) + + o = tree.find('real') + asset = int(o.find('asset').text) + liability = int(o.find('liability').text) + r.real = RunningTotal('.', unitindex, asset=asset, liability=liability) + logg.debug(r.real) + + for v in tree.iter('virt'): + income = int(v.find('income').text) + expense = int(v.find('expense').text) + asset = int(v.find('asset').text) + liability = int(v.find('liability').text) + sym = v.get('symbol') + r.running[sym] = RunningTotal(sym, unitindex, income=income, expense=expense, asset=asset, liability=liability) + logg.debug(r.running[sym]) + + return r.check() + + + def apply_tree(self, tree): + for v in tree.iter('entry'): + o = Entry.from_tree(v, self.uidx) + self.entries[o.serial] = o + self.running[o.unit].apply_entry(o) + + + def check(self): + return self + + + def __str__(self): + return "state: " + self.base.hex() + + +def init_ledger(tree, units): + o = tree.find('incoming') + return Ledger.from_tree(o, units) + + +def get_units(tree): + o = tree.find('units') + return UnitIndex.from_tree(o) + + +def load(fp): + f = open(fp, 'r') + tree = etree.parse(f) + f.close() + return tree.getroot() + + +if __name__ == '__main__': + fp = 'running.xml' + try: + fp = sys.argv[1] + except IndexError: + pass + root = load(fp) + un = get_units(root) + st = init_ledger(root, un) + st.apply_tree(root) + print(st) diff --git a/dummy/present.xsl b/dummy/present.xsl @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> +<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/xpath-functions"> <xsl:template match="/"> <html> <head> @@ -12,11 +12,10 @@ </xsl:template> <xsl:template match="/ledger"> - <p>Document retrieved: <xsl:value-of select="retrieved" /></p> - <xsl:apply-templates select="entry" /> + <p>Document retrieved: <xsl:value-of select="retrieved" /> from <xsl:value-of select="src" /></p> + <xsl:for-each select="entry"> + <p><xsl:value-of select="data/serial" />:<xsl:value-of select="data/date" />:<xsl:value-of select="data/ref" /></p> + </xsl:for-each> </xsl:template> - <xsl:template match="entry"> - <p>ref: <xsl:value-of select="data/ref" /></p> - </xsl:template> </xsl:stylesheet> diff --git a/dummy/running.xml b/dummy/running.xml @@ -3,28 +3,25 @@ <ledger> <retrieved>2025-11-02T13:09:55Z</retrieved> <src>playalastunas.org</src> - <units vs="BTC"> - <unit> - <sym>USD</sym> + <units base="BTC"> + <unit sym="USD"> <precision>2</precision> <ex>10999310</ex> </unit> - <unit> - <sym>BTC</sym> + <unit sym="BTC"> <precision>8</precision> <ex>1</ex> </unit> </units> - <incoming serial="231"> - <state> - <serial>231</serial> - <digest>b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c</digest> - <sig pubkey="58851ae2166b3f1454193e0a7e821402dd1c1a91">1f74a9c0196a025f27d4e940c4abedfa2d37504f268aea359659cb65b85bd4d7974369507950006964c93391d3d4580ab8064f6d30a62908468ef771be952e95</sig> - <sig pubkey="566c38287d3f31c7e50836cae58e426c6bccc52d">117a57c72ed210b91469307a1c2e73fe2d5ee306cd8ccf1a9db4ecb15d38ecbbfc97d62fec4ab8aadb08c531f2d1ede34cb6e4d3987bcba63322a0767e532e13</sig> - </state> + <resolver algo="sha256" proto="https">g33k.holbrook.no</resolver> + <identity keyid="f1d2d2f924e986ac86fdf7b36c94bcdf32beec15" didtype="web">nondominium.org/lash/</identity> + <identity keyid="f1d2d2f924e986ac86fdf7b36c94bcdf32beec15" didtype="web">holbrook.no</identity> + <incoming serial="231" digest="b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c"> + <sig keyid="58851ae2166b3f1454193e0a7e821402dd1c1a91">1f74a9c0196a025f27d4e940c4abedfa2d37504f268aea359659cb65b85bd4d7974369507950006964c93391d3d4580ab8064f6d30a62908468ef771be952e95</sig> + <sig keyid="566c38287d3f31c7e50836cae58e426c6bccc52d">117a57c72ed210b91469307a1c2e73fe2d5ee306cd8ccf1a9db4ecb15d38ecbbfc97d62fec4ab8aadb08c531f2d1ede34cb6e4d3987bcba63322a0767e532e13</sig> <real unit="BTC"> <asset>6323141</asset> - <liabilities>0</liabilities> + <liability>0</liability> </real> <virt symbol="USD"> <income>4213</income> @@ -49,8 +46,13 @@ <account>Donations</account> <description>Alice</description> <amount>524</amount> + <attachment mime="image/jpeg"> + <slug>troll</slug> + <description>The face of a troll</description> + <digest algo="sha256">777b30c8fc40aea3c717777831a05c9f29c7b6735f1573e9b0b55373c264f6f3</digest> + </attachment> </data> - <sig pubkey="e242ed3bffccdf271b7fbaf34ed72d089537b42f">0cf9180a764aba863a67b6d72f0918bc131c6772642cb2dce5a34f0a702f9470ddc2bf125c12198b1995c233c34b4afd346c54a2334c350a948a51b6e8b4e6b6</sig> + <sig keyid="e242ed3bffccdf271b7fbaf34ed72d089537b42f">0cf9180a764aba863a67b6d72f0918bc131c6772642cb2dce5a34f0a702f9470ddc2bf125c12198b1995c233c34b4afd346c54a2334c350a948a51b6e8b4e6b6/</sig> </entry> <entry type="liability"> <data> @@ -63,6 +65,6 @@ <description>Install AC</description> <amount>1000000</amount> </data> - <sig pubkey="f1d2d2f924e986ac86fdf7b36c94bcdf32beec15">cc06808cbbee0510331aa97974132e8dc296aeb795be229d064bae784b0a87a5cf4281d82e8c99271b75db2148f08a026c1a60ed9cabdb8cac6d24242dac4063</sig> + <sig keyid="f1d2d2f924e986ac86fdf7b36c94bcdf32beec15">cc06808cbbee0510331aa97974132e8dc296aeb795be229d064bae784b0a87a5cf4281d82e8c99271b75db2148f08a026c1a60ed9cabdb8cac6d24242dac4063</sig> </entry> </ledger>