usawa

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

commit 934c3348fb71f48db933c88fd9f7b79efa1494f3
parent ba4c3c2b080f2e8d7e235bacce82c6150dedd339
Author: lash <dev@holbrook.no>
Date:   Sun,  7 Jun 2026 16:08:03 -0600

Remove need for wallet files

Diffstat:
Mdummy/usawa/context.py | 19+++++++++++++++----
Mdummy/usawa/gui/__init__.py | 71+++++++++++++++++++++++++++++++++++++----------------------------------
Mdummy/usawa/gui/main_window.py | 61++++++++++++-------------------------------------------------
Mdummy/usawa/runnable/gui.py | 24+++++++++++++++++++++++-
Mdummy/usawa/runnable/server.py | 42+++++++++++++++++++++++-------------------
Mdummy/usawa/storage/ledger_repository.py | 49++++++-------------------------------------------
6 files changed, 116 insertions(+), 150 deletions(-)

diff --git a/dummy/usawa/context.py b/dummy/usawa/context.py @@ -108,16 +108,27 @@ class UsawaContext: return pw - def load_wallet(self): + def check_wallet(self): if self.wallet != None: + return True + self.keystore.get_default_key(DemoWallet) + return True + + + def load_wallet(self, signing=False, replace=False): + if self.wallet != None and not replace: raise AttributeError('wallet set') - if not self.signing: + if not self.signing and not signing: + self.wallet = self.keystore.get_default_key(DemoWallet) + elif not signing: self.wallet = self.keystore.get_default_key(DemoWallet) else: + logg.debug('decrypting wallet') ops = int(self.cfg.get('WALLET_OPSLIMIT', 0)) mem = int(self.cfg.get('WALLET_MEMLIMIT', 0)) pw = self.getpw() self.wallet = self.keystore.get_key(DemoWallet, passphrase=pw, opslimit=ops, memlimit=mem) + logg.debug('have wallet {}'.format(self.wallet)) if self.ledger != None: self.ledger.set_wallet(self.wallet) @@ -165,11 +176,11 @@ class UsawaContext: if store_scope == 'ledger': self.store = LedgerStore(self.db, self.ledger) self.keystore = self.store - elif store_scope == 'asset' or store_scope == 'entry': + elif store_scope == 'asset' or store_scope == 'entry' or store_scope == 'key': self.keystore = KeyStore(self.db) if store_scope == 'asset': self.store = AssetStore(self.db) - else: + elif store_scope == 'entry': self.store = EntryStore(self.db) diff --git a/dummy/usawa/gui/__init__.py b/dummy/usawa/gui/__init__.py @@ -14,19 +14,21 @@ logg = logging.getLogger("gui.app") class Usawa(Adw.Application): - def __init__(self, *args, **kwargs): + def __init__(self, ctx, *args, **kwargs): super().__init__( flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE, *args, **kwargs ) + self.ctx = ctx self.win = None self.ledger_file = None - self.cfg = None + self.cfg = ctx.cfg self.account_list = None self.connect("activate", self.on_activate) signal.signal(signal.SIGINT, self._handle_sigint) def on_activate(self, app): - self.win = UsawaMainWindow(application=app, ledger_path=self.ledger_file, account_list=self.account_list) + #self.win = UsawaMainWindow(application=app, ledger_path=self.ledger_file, account_list=self.account_list) + self.win = UsawaMainWindow(app, self.ctx) self.win.present() def _handle_sigint(self, *_): @@ -34,36 +36,37 @@ class Usawa(Adw.Application): self.quit() def do_command_line(self, cli): - args = cli.get_arguments() - - config_dir = None - filtered_args = [] - i = 1 - while i < len(args): - if args[i] == "-c": - if i + 1 < len(args): - config_dir = args[i + 1] - i += 2 - else: - logg.error("-c flag requires a directory argument") - return 1 - else: - filtered_args.append(args[i]) - i += 1 - self.cfg = load_config(config_dir=config_dir) - logg.debug('config-dump:\n{}'.format(self.cfg)) - - if len(filtered_args) == 0: - logg.error("missing ledger file argument") - return 1 - - if filtered_args[0] == "setup-wallet": - wallet_dir = filtered_args[1] if len(filtered_args) > 1 else None - setup_wallet(self.cfg, wallet_dir=wallet_dir) - return 0 - - self.ledger_file = filtered_args[0] - for k in self.cfg.all(): - logg.debug("config {} => {}".format(k, self.cfg.get(k))) +# args = cli.get_arguments() +# +# config_dir = None +# filtered_args = [] +# i = 1 +# while i < len(args): +# if args[i] == "-c": +# if i + 1 < len(args): +# config_dir = args[i + 1] +# i += 2 +# else: +# logg.error("-c flag requires a directory argument") +# return 1 +# else: +# filtered_args.append(args[i]) +# i += 1 +# self.cfg = load_config(config_dir=config_dir) +# logg.debug('config-dump:\n{}'.format(self.cfg)) +# +# if len(filtered_args) == 0: +# logg.error("missing ledger file argument") +# return 1 +# +# #if filtered_args[0] == "setup-wallet": +# # wallet_dir = filtered_args[1] if len(filtered_args) > 1 else None +# # setup_wallet(self.cfg, wallet_dir=wallet_dir) +# # return 0 +# +# self.ledger_file = filtered_args[0] +# for k in self.cfg.all(): +# logg.debug("config {} => {}".format(k, self.cfg.get(k))) +# self.activate() self.activate() return 0 diff --git a/dummy/usawa/gui/main_window.py b/dummy/usawa/gui/main_window.py @@ -19,17 +19,15 @@ from datetime import datetime from usawa.store import LedgerStore from whee.valkey import ValkeyStore from whee.fs import FsStore +import usawa.error logg = logging.getLogger("gui.mainwindow") class UsawaMainWindow(Adw.ApplicationWindow): - def __init__(self, application, ledger_path=None, account_list=None, **kwargs): + def __init__(self, application, ctx, **kwargs): super().__init__(application=application, **kwargs) - logg.debug('foo') - - self.account_list = account_list self.set_title("Usawa") self.set_default_size(1000, 600) @@ -47,23 +45,7 @@ class UsawaMainWindow(Adw.ApplicationWindow): self.toast_overlay = Adw.ToastOverlay() toolbar_view.set_content(self.toast_overlay) - self.cfg = self.get_application().cfg - self.unix_client = UnixClient(path=self.cfg.get("SERVER_SOCKET_FILE_PATH")) - store_type = self.cfg.get('STORE_TYPE') - if store_type == 'valkey': - self.store = ValkeyStore( - "", - host=self.cfg.get("VALKEY_HOST"), - port=self.cfg.get("VALKEY_PORT"), - ) - elif store_type == 'fs': - self.store = FsStore( - base=self.cfg.get('FSSTORE_BASE'), - ) - else: - raise ValueError('invalid store type: ' + store_type) - - self.ledger_path = ledger_path + self.ctx = ctx self.nav_view = Adw.NavigationView() self.toast_overlay.set_child(self.nav_view) @@ -133,7 +115,7 @@ class UsawaMainWindow(Adw.ApplicationWindow): entries=entries, refresh_callback=self.refresh_entries, toast_overlay=self.toast_overlay, - account_list=self.account_list, + #account_list=self.account_list, ) self.entry_list_view._load_entries() page.set_child(self.entry_list_view) @@ -145,43 +127,24 @@ class UsawaMainWindow(Adw.ApplicationWindow): self.entry_list_view._load_entries() def _check_wallet_status(self): - wallet_path = StateManager.get("wallet_path") - if not wallet_path or not Path(wallet_path).exists(): - logg.info("no wallet in store, showing import dialog") - dialog = ImportWalletDialog(self) - dialog.present(self) - else: - ledger_tree = load(self.ledger_path) - ledger = Ledger.from_tree(ledger_tree) - store = LedgerStore(self.store, ledger) + try: + self.ctx.load_wallet(replace=True, signing=True) + self._init_with_wallet() + except usawa.error.VerifyError: dialog = PassphraseDialog( - store=store, + store=self.ctx.keystore, wallet_class=DemoWallet, - on_success=lambda wallet, passphrase: self._init_with_wallet( - wallet, passphrase, False - ), + on_success=self._init_with_wallet, on_cancel=self._on_wallet_cancelled, ) dialog.present(self) - def _init_with_wallet(self, wallet, passphrase, save_wallet: bool = True): - """Called after wallet is successfully imported.""" - self.wallet = wallet - - repository = LedgerRepository( - ledger_path=self.ledger_path, - unix_client=self.unix_client, - valkey_store=self.store, - cfg=self.cfg, - wallet=self.wallet, - ) + def _init_with_wallet(self): + repository = LedgerRepository(self.ctx) entry_service = EntryService(repository=repository) self.entry_controller = EntryController(entry_service=entry_service) self.entry_controller.add_entry_created_listener(self.refresh_entries) - if save_wallet: - self.entry_controller.save_wallet(wallet, passphrase) - entry_list_page = self._create_entry_list_page() self.nav_view.add(entry_list_page) diff --git a/dummy/usawa/runnable/gui.py b/dummy/usawa/runnable/gui.py @@ -1,13 +1,35 @@ import logging import sys +import argparse from usawa.gui import Usawa +import usawa.config +from usawa.context import UsawaContext logging.basicConfig(level=logging.DEBUG) +logg = logging.getLogger() + + +def emptypw(): + return '' def main(): - app = Usawa(application_id='org.usawa.app') + argp = argparse.ArgumentParser() + argp.add_argument('-c', type=str, help='override config dir') + argp.add_argument('-i', type=str, help='input ledger state') + argp.add_argument('-v', type=str, choices=['info','debug','warning','error'], help='be verbose') + args = argp.parse_args() + + if args.v: + logg.setLevel(getattr(logging, args.v.upper())) + + cfg = usawa.config.load_config(config_dir=args.c) + ctx = UsawaContext(cfg, signing=False, pwgetter=emptypw) + ctx.init(args) + ctx.askpass = True + + app = Usawa(ctx, application_id='org.usawa.app') app.run(sys.argv) diff --git a/dummy/usawa/runnable/server.py b/dummy/usawa/runnable/server.py @@ -37,35 +37,39 @@ Public keys used to verify entries and ledger states are provided using the -k f argp.add_argument('-c', type=str, help='override config dir') argp.add_argument('-u', type=str, default='./usawa.socket', help='UNIX Socket file to listen on') argp.add_argument('-v', type=str, choices=['info','debug','warning','error'], help='be verbose') - argp.add_argument('ledger_xml_file', type=str, help='load ledger metadata from XML file') + argp.add_argument('-i', type=str, help='input ledger state') + #argp.add_argument('ledger_xml_file', type=str, help='load ledger metadata from XML file') arg = argp.parse_args() if arg.v: logg.setLevel(getattr(logging, arg.v.upper())) - #ctx = UsawaContext(arg) + cfg = usawa.config.load_config(config_dir=arg.c) + ctx = UsawaContext(cfg) + ctx.init(arg) - acl = ACL() - for k in arg.k: - acl.add(k) + #acl = ACL() + #for k in arg.k: + # acl.add(k) - ledger = None - ledger_tree = load(arg.ledger_xml_file) - uidx = UnitIndex.from_tree(ledger_tree) - ledger = Ledger.from_tree(ledger_tree, acl=acl) + #ledger = None + #ledger_tree = load(arg.ledger_xml_file) + #uidx = UnitIndex.from_tree(ledger_tree) + #ledger = Ledger.from_tree(ledger_tree, acl=acl) #db = ValkeyStore('', host=arg.valkey_host, port=arg.valkey_port) #srv = TCPServer(db, ledger, acl=acl) - db = None - if cfg.get('STORE_TYPE') == 'valkey': - dbid = cfg.get('VALKEY_ID') - host = self.cfg.get('VALKEY_HOST') - port = self.cfg.get('VALKEY_PORT') - db = ValkeyStore('', host=host, port=port) - elif self.cfg.get('STORE_TYPE') == 'fs': - base = self.cfg.get('FSSTORE_BASE') - db = FsStore(base=base, dbname='usawa') - srv = UnixServer(db, ledger, acl=acl, path=arg.u) +# db = None +# if cfg.get('STORE_TYPE') == 'valkey': +# dbid = cfg.get('VALKEY_ID') +# host = self.cfg.get('VALKEY_HOST') +# port = self.cfg.get('VALKEY_PORT') +# db = ValkeyStore('', host=host, port=port) +# elif self.cfg.get('STORE_TYPE') == 'fs': +# base = self.cfg.get('FSSTORE_BASE') +# db = FsStore(base=base, dbname='usawa') + #srv = UnixServer(ctx.db, ctx.ledger, acl=acl, path=arg.u) + srv = UnixServer(ctx.db, ctx.ledger, path=arg.u) def stop_server(sig, stack): srv.stop() diff --git a/dummy/usawa/storage/ledger_repository.py b/dummy/usawa/storage/ledger_repository.py @@ -29,11 +29,7 @@ class LedgerRepository: def __init__( self, - ledger_path=None, - unix_client: UnixClient = None, - valkey_store: ValkeyStore = None, - cfg=None, - wallet=None, + ctx, ): """ Initialize the LedgerRepository. @@ -45,40 +41,15 @@ class LedgerRepository: :type unix_client: usawa.UnixClient """ - self.valkey_store = valkey_store - self.unix_client = unix_client - self._wallet = wallet + self.resolver = ctx.resolver + self.wallet = ctx.wallet + self.store = ctx.store + self.ledger = ctx.ledger - self.ledger_path = ledger_path - self.cfg = cfg - - logg.debug("fs resolver path: %s", self.cfg.get("FS_RESOLVER_STORE_PATH")) - logg.debug( - "fs_resolver as URI: %s", - path_from_uri(self.cfg.get("FS_RESOLVER_STORE_PATH")), - ) - - self.resolver = FSResolver( - path_from_uri(self.cfg.get("FS_RESOLVER_STORE_PATH")) - ) def _init_store(self, write=False) -> tuple[LedgerStore, Ledger, Wallet]: - ledger_tree = load(self.ledger_path) - ledger = Ledger.from_tree(ledger_tree) - - if write: - logg.info("init store for write") - self.store = LedgerStore(self.valkey_store, ledger) - else: - logg.info("init store for read") - self.store = LedgerStore(self.valkey_store, ledger) - - logg.info("wallet ready, pubkey: %s...", self._wallet.pubkey().hex()[:16]) + return self.store, self.ledger, self.wallet - ledger.set_wallet(self._wallet) - ledger.acl = ACL.from_wallet(self._wallet) - self.store.load(acl=ledger.acl) - return self.store, ledger, self._wallet def save(self, domain_entry: LedgerEntry) -> None: """ @@ -144,14 +115,6 @@ class LedgerRepository: logg.debug(f"Failed to save entry: {e}", exc_info=True) raise - def save_wallet(self, wallet, passphrase): - store, _, _ = self._init_store() - try: - store.get_key(DemoWallet, passphrase=passphrase) - logg.info("key already exists in store, skipping") - except FileNotFoundError: - logg.info("key written to store") - store.add_key(wallet, passphrase=passphrase) def get_all_entries(self) -> List[LedgerEntry]: """Get all entries"""