usawa

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

commit 429b73bec76553ec64e9053fe0c0b6fb768aef7e
parent 6c9a15e011660f7abdc877fd1ddeaf9544db4c98
Author: lash <dev@holbrook.no>
Date:   Wed, 18 Mar 2026 13:13:13 -0600

WIP attempt to add setup-wallet key to store directly

Diffstat:
Mdummy/setup.cfg | 7+++++++
Adummy/tests/wallet.py | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Mdummy/usawa/data/usawa.ini | 4++--
Mdummy/usawa/runnable/create.py | 6+++---
Mdummy/usawa/runnable/setup_wallet.py | 12++++++++++++
Mdummy/usawa/storage/file_utils.py | 5+++++
Mdummy/usawa/store.py | 158+++++++++++++++++++++++++++++++++++++++++--------------------------------------
7 files changed, 162 insertions(+), 81 deletions(-)

diff --git a/dummy/setup.cfg b/dummy/setup.cfg @@ -25,8 +25,15 @@ include_package_data = True python_requires = >= 3.7 packages = usawa + usawa.core usawa.resolve usawa.runnable + usawa.gui + usawa.gui.components + usawa.gui.controllers + usawa.gui.models + usawa.gui.views + usawa.storage [options.entry_points] console_scripts = diff --git a/dummy/tests/wallet.py b/dummy/tests/wallet.py @@ -0,0 +1,51 @@ +import logging +import unittest +import os + +from usawa import DemoWallet +from usawa.error import VerifyError + +logging.basicConfig(level=logging.DEBUG) +logg = logging.getLogger() + +testdir = os.path.realpath(os.path.dirname(__file__)) + +class TestWallet(unittest.TestCase): + + def test_wallet_create(self): + wallet = DemoWallet() + v = b'foo' + r = wallet.sign(v) + wallet.verify(v, r) + + + def test_wallet_verify(self): + wallet = DemoWallet() + v = b'foo' + r = wallet.sign(v) + + k = wallet.pubkey() + wallet = DemoWallet(publickey=k) + wallet.verify(v, r) + + + def test_wallet_export(self): + wallet = DemoWallet() + v = b'foo' + r = wallet.sign(v) + + b = wallet.export() + with self.assertRaises(VerifyError): + wallet = DemoWallet.from_export(b, passphrase='baz') + wallet = DemoWallet.from_export(b) + wallet.verify(v, r) + + b = wallet.export(passphrase='bar') + with self.assertRaises(VerifyError): + wallet = DemoWallet.from_export(b, passphrase='baz') + wallet = DemoWallet.from_export(b, passphrase='bar') + wallet.verify(v, r) + + +if __name__ == '__main__': + unittest.main() diff --git a/dummy/usawa/data/usawa.ini b/dummy/usawa/data/usawa.ini @@ -2,8 +2,8 @@ gpg_dir = [valkey] -host = -port = +host = localhost +port = 6379 [server] socket_file_path= diff --git a/dummy/usawa/runnable/create.py b/dummy/usawa/runnable/create.py @@ -143,15 +143,15 @@ pk = None wallet = None dt = datetime.datetime.now() -cfg = usawa.config.load() +cfg = usawa.config.load_config() try: #pk = store.get_key() - wallet = store.get_key(DemoWallet, passphrase=cfg.get('SIGS_KEY_PASSPHRASE')) + wallet = store.get_key(DemoWallet, passphrase=cfg.get('WALLET_KEY_PASSPHRASE')) except FileNotFoundError: logg.info('no default key found') wallet = DemoWallet() - store.add_key(wallet, passphrase=cfg.get('SIGS_KEY_PASSPHRASE')) + store.add_key(wallet, passphrase=cfg.get('WALLET_KEY_PASSPHRASE')) if wallet == None: wallet = DemoWallet(privatekey=pk) logg.info('loaded existing key. {}'.format(wallet.pubkey().hex())) diff --git a/dummy/usawa/runnable/setup_wallet.py b/dummy/usawa/runnable/setup_wallet.py @@ -5,6 +5,10 @@ from pathlib import Path from nacl.signing import SigningKey from nacl.secret import SecretBox from nacl.pwhash import argon2i +from whee.valkey import ValkeyStore + +from usawa.store import KeyStore +from usawa.crypto import DemoWallet logg = logging.getLogger("core.setup_wallet") @@ -31,6 +35,9 @@ def encrypt_seed(seed: bytes, passphrase: str) -> bytes: def setup_wallet(wallet_dir=None): + db = ValkeyStore('') + store = KeyStore(db) + wallet_dir = Path(wallet_dir) if wallet_dir else DEFAULT_WALLET_DIR wallet_dir.mkdir(parents=True, exist_ok=True) logg.info("wallet directory: %s", wallet_dir) @@ -58,7 +65,12 @@ def setup_wallet(wallet_dir=None): f.write(pk.encode()) logg.info("public key saved to: %s", publickey_path) + wallet = DemoWallet(privatekey=random_bytes) + store.add_key(wallet, passphrase=passphrase_confirm) + logg.info("key written to store") + logg.info("setup complete.") logg.info("your 32-byte public key (hex): %s", pk.encode().hex()) + return 0 diff --git a/dummy/usawa/storage/file_utils.py b/dummy/usawa/storage/file_utils.py @@ -1,9 +1,14 @@ +import os + from urllib.request import url2pathname from urllib.parse import urlparse def path_from_uri(uri: str) -> str: parsed = urlparse(uri) + if parsed.scheme == '': + uri = 'file://' + os.path.realpath(uri) + parsed = urlparse(uri) if parsed.scheme != "file": raise ValueError("unsupported scheme: {}".format(parsed.scheme)) return url2pathname(parsed.path) diff --git a/dummy/usawa/store.py b/dummy/usawa/store.py @@ -91,8 +91,79 @@ def pfx_asset(asset): return PFX_ASSET + asset.get_digest(binary=True) +class KeyStore(Interface): -class LedgerStore(Interface): + def __init__(self, implementation): + if not isinstance(implementation, Interface): + raise ValueError('store must be whee interface instance') + self.db = implementation + + + """Add signing key to the store. + + If this is the first key in the store, it will be set as default. + + :param wallet: The wallet object to store keys for. + :type wallet: usawa.Wallet implementation + :param acl: Access control list data to retrieve the allowance and label for the key. + :type acl: usawa.ACL + :param default: If True, this key will be set as default key. + :type default: bool + :param passphrase: Passphrase to encrypt the key with. + :type passphrase: bytes + :todo: Implement the ACL lookup + """ + def add_key(self, wallet, acl=None, default=False, passphrase=None): + k = pfx_key() + try: + self.db.get(k) + except FileNotFoundError: + default = True + pubkey = wallet.pubkey() + if default: + self.db.put(k, pubkey, exist_ok=True) + k = pfx_key(pubkey=pubkey) + v = wallet.export(passphrase=passphrase) + self.db.put(k, v) + + + """Get a newly instantiated wallet object from a private key in the store. + + If public key is not supplied, will retrieve the default private key. + + :param wallet_class: Wallet class to use to instantiate a Wallet object from private key material. + :type: usawa.crypto.Wallet + :param pubkey: Public key to retrieve private key for. + :type pubkey: bytes + :param passphrase: Passphrase to decrypt the key with. + :type passphrase: bytes + :raises FileNotFoundError: No key exists. + :raises usawa.error.VerifyError: Key decryption failed. + :return: Resulting wallet + :rtype: usawa.crypto.Wallet + """ + def get_key(self, wallet_class, pubkey=None, passphrase=None): + if pubkey == None: + k = pfx_key() + pubkey = self.db.get(k) + k = pfx_key(pubkey=pubkey) + #return self.db.get(k) + r = self.db.get(k) + return wallet_class.from_export(r, passphrase=passphrase) + + + """Implements whee.Interface.put + """ + def put(self, k, v): + return self.db.put(k, v) + + + """Implements whee.Interface.get + """ + def get(self, k): + return self.db.get(k) + +class LedgerStore(KeyStore): """Wrapper for an implementation of the whee store that handles encoding of ledgers and entries. :param implementation: Store implementation. @@ -101,12 +172,10 @@ class LedgerStore(Interface): :type ledger: usawa.Ledger """ def __init__(self, implementation, ledger): - if not isinstance(implementation, Interface): - raise ValueError('store must be whee interface instance') + super(LedgerStore, self).__init__(implementation) if not isinstance(ledger, Ledger): raise ValueError('invalid ledger') self.ledger = ledger - self.__o = implementation """Implements whee.Interface.start @@ -115,11 +184,11 @@ class LedgerStore(Interface): serial = 0 k = pfx_ledger_topic(self.ledger.topic) try: - b = self.__o.get(k) + b = self.db.get(k) serial = int.from_bytes(8, byteorder='big') except FileNotFoundError: v = serial.to_bytes(8, byteorder='big') - self.__o.put(k, v) + self.db.put(k, v) self.ledger.serial = serial @@ -130,10 +199,10 @@ class LedgerStore(Interface): v = None # TODO: needs to be an atomic routine try: - v = self.__o.get(k) + v = self.db.get(k) except KeyError: raise PermissionError() - self.__o.put(k, 0x01, exist_ok) + self.db.put(k, 0x01, exist_ok) # atomic until here @@ -141,7 +210,7 @@ class LedgerStore(Interface): """ def unlock(self): k = pfx_ledger_lock(self.ledger.topic) - v = self.__o.delete(k) + v = self.db.delete(k) """Add an entry to the store. @@ -156,7 +225,7 @@ class LedgerStore(Interface): def add_entry(self, entry, update_ledger=False): k = pfx_entry(self.ledger, entry) v = entry.wrap() - self.__o.put(k, v) + self.db.put(k, v) if update_ledger: self.ledger.add_entry(entry) @@ -176,7 +245,7 @@ class LedgerStore(Interface): """ def get_entry(self, entry, acl=None): k = pfx_entry(self.ledger, entry) - v = self.__o.get(k) + v = self.db.get(k) entry = Entry.unwrap(v) # TODO: hacky! i = 0 @@ -198,14 +267,14 @@ class LedgerStore(Interface): def add_asset(self, asset): k = pfx_asset(asset) v = asset.serialize() - self.__o.put(k, v) + self.db.put(k, v) """Restore an entry attachment asset from the store. """ def get_asset(self, asset): k = pfx_asset(asset) - v = self.__o.get(k) + v = self.db.get(k) digest = asset.get_digest(binary=True) return Asset.deserialize(v, digest) @@ -246,69 +315,6 @@ class LedgerStore(Interface): i -= 1 - """Add signing key to the store. - - If this is the first key in the store, it will be set as default. - - :param wallet: The wallet object to store keys for. - :type wallet: usawa.Wallet implementation - :param acl: Access control list data to retrieve the allowance and label for the key. - :type acl: usawa.ACL - :param default: If True, this key will be set as default key. - :type default: bool - :param passphrase: Passphrase to encrypt the key with. - :type passphrase: bytes - :todo: Implement the ACL lookup - """ - def add_key(self, wallet, acl=None, default=False, passphrase=None): - k = pfx_key() - try: - self.__o.get(k) - except FileNotFoundError: - default = True - pubkey = wallet.pubkey() - if default: - self.__o.put(k, pubkey, exist_ok=True) - k = pfx_key(pubkey=pubkey) - v = wallet.export(passphrase=passphrase) - self.__o.put(k, v) - - - """Get a newly instantiated wallet object from a private key in the store. - - If public key is not supplied, will retrieve the default private key. - - :param wallet_class: Wallet class to use to instantiate a Wallet object from private key material. - :type: usawa.crypto.Wallet - :param pubkey: Public key to retrieve private key for. - :type pubkey: bytes - :param passphrase: Passphrase to decrypt the key with. - :type passphrase: bytes - :raises FileNotFoundError: No key exists. - :raises usawa.error.VerifyError: Key decryption failed. - :return: Resulting wallet - :rtype: usawa.crypto.Wallet - """ - def get_key(self, wallet_class, pubkey=None, passphrase=None): - if pubkey == None: - k = pfx_key() - pubkey = self.__o.get(k) - k = pfx_key(pubkey=pubkey) - #return self.__o.get(k) - r = self.__o.get(k) - return wallet_class.from_export(r, passphrase=passphrase) - - - """Implements whee.Interface.put - """ - def put(self, k, v): - return self.__o.put(k, v) - - - """Implements whee.Interface.get - """ - def get(self, k): - return self.__o.get(k) """Store all entries in the ledger state.