kee

Offline IOU signer with QR as transport
git clone https://holbrook.no/src/kee
Info | Log | Files | Refs | README | LICENSE

commit 74793de06b885955b59f0b6a3f0e35ac47eefd35
parent 96539f09287c86af39108d0f06c5a0e889e46657
Author: lash <dev@holbrook.no>
Date:   Thu, 28 Mar 2024 18:05:55 +0000

Implement database backend, deserialization to entry, list item

Diffstat:
Msrc/db.c | 43+++++++++++++++++++++++++++----------------
Msrc/db.h | 4+++-
Msrc/gtk/kee-entry-list.c | 33++++++++++++++++++++++++---------
Msrc/gtk/kee-entry-store.c | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/gtk/kee-entry.c | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/gtk/kee-entry.h | 7+++++++
Mtestdata.py | 21++++++++++++---------
7 files changed, 198 insertions(+), 41 deletions(-)

diff --git a/src/db.c b/src/db.c @@ -3,10 +3,12 @@ #include <lmdb.h> #include <gcrypt.h> #include <time.h> + #include "db.h" #include "digest.h" #include "err.h" #include "endian.h" +#include "debug.h" int db_connect(struct db_ctx *ctx, char *conn) { int r; @@ -22,6 +24,8 @@ int db_connect(struct db_ctx *ctx, char *conn) { return ERR_FAIL; } + debug_log(DEBUG_INFO, "db connected"); + return ERR_OK; } @@ -122,12 +126,13 @@ int db_next(struct db_ctx *ctx, enum DbKey pfx, char **key, size_t *key_len, cha return ERR_DB_INVALID; } - if (ctx->current_key == DbNoKey) { - if (ctx->started) { - mdb_cursor_close(ctx->crsr); - mdb_dbi_close(ctx->env, ctx->dbi); - mdb_txn_abort(ctx->tx); - } + //if (ctx->current_key == DbNoKey) { + if (!ctx->browsing) { +// if (ctx->started) { +// mdb_cursor_close(ctx->crsr); +// mdb_dbi_close(ctx->env, ctx->dbi); +// mdb_txn_abort(ctx->tx); +// } r = mdb_txn_begin(ctx->env, NULL, MDB_RDONLY, &ctx->tx); if (r) { @@ -146,18 +151,20 @@ int db_next(struct db_ctx *ctx, enum DbKey pfx, char **key, size_t *key_len, cha start[0] = (char)pfx; ctx->k.mv_size = 1; - if (!ctx->browsing) { - if (*key != 0) { - memcpy(start+1, *key, *key_len); - ctx->k.mv_size += *key_len; - } - } +// if (!ctx->browsing) { +// if (*key != 0) { +// memcpy(start+1, *key, *key_len); +// ctx->k.mv_size += *key_len; +// } +// } ctx->k.mv_data = start; - } - if (!ctx->browsing) { - r = mdb_cursor_get(ctx->crsr, &ctx->k, &ctx->v, MDB_SET_RANGE); ctx->browsing = 1; + if (*key != 0) { + memcpy(ctx->k.mv_data, *key, *key_len); + ctx->k.mv_size += *key_len; + } + r = mdb_cursor_get(ctx->crsr, &ctx->k, &ctx->v, MDB_SET_RANGE); } else { r = mdb_cursor_get(ctx->crsr, &ctx->k, &ctx->v, MDB_NEXT_NODUP); } @@ -166,7 +173,8 @@ int db_next(struct db_ctx *ctx, enum DbKey pfx, char **key, size_t *key_len, cha } start[0] = (char)*((char*)ctx->k.mv_data); if (start[0] != ctx->current_key) { - db_reset(ctx); + //db_reset(ctx); + ctx->browsing = 0; return ERR_DB_NOMATCH; } @@ -181,5 +189,8 @@ int db_next(struct db_ctx *ctx, enum DbKey pfx, char **key, size_t *key_len, cha void db_reset(struct db_ctx *ctx) { + mdb_cursor_close(ctx->crsr); + mdb_dbi_close(ctx->env, ctx->dbi); + mdb_txn_abort(ctx->tx); memset(ctx, 0, sizeof(struct db_ctx)); } diff --git a/src/db.h b/src/db.h @@ -25,7 +25,9 @@ enum DbKey { /// Noop value, used for default value of a DbKey variable DbNoKey = 0x00, /// A credit item record - DbKeyCreditItem = 0x01, + DbKeyLedgerHead = 0x01, + /// A credit item record + DbKeyLedgerEntry = 0x02, /// A reverse lookup record; resolves the content hash to the content entry in the database. DbKeyReverse = 0xff, }; diff --git a/src/gtk/kee-entry-list.c b/src/gtk/kee-entry-list.c @@ -2,6 +2,7 @@ #include <gtk/gtk.h> #include "kee-entry-list.h" +#include "kee-entry.h" #include "err.h" typedef struct { @@ -16,18 +17,30 @@ struct _KeeEntryList { G_DEFINE_TYPE(KeeEntryList, kee_entry_list, GTK_TYPE_BOX); static void kee_entry_handle_setup(KeeEntryList* o, GtkListItem *item) { - g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "setup"); + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "entry list setup"); } -static void kee_entry_handle_bind(KeeEntryList* o, GtkListItem *item) { - const char *s; - GObject *go; - GtkWidget *label; +static void kee_entry_handle_bind(KeeEntryList *o, GtkListItem *item) { + GtkWidget *widget; + char *s; + KeeEntry *go; - go = G_OBJECT(gtk_list_item_get_item(item)); - s = g_object_get_data(go, "foo"); - label = gtk_label_new(s); - gtk_list_item_set_child(item, label); + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "entry list bind"); + go = gtk_list_item_get_item(item); + g_object_take_ref(G_OBJECT(go)); + gtk_list_item_set_child(item, GTK_WIDGET(go)); +} + +static void kee_entry_handle_unbind(KeeEntryList* o, GtkListItem *item) { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "entry list unbind"); + //GObject *go; + //go = gtk_list_item_get_child(item); + gtk_list_item_set_child(item, NULL); + //g_object_unref(go); +} + +static void kee_entry_handle_teardown(KeeEntryList* o, GtkListItem *item) { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "entry list teardown"); } static void kee_entry_list_class_init(KeeEntryListClass *kls) { @@ -37,6 +50,8 @@ static void kee_entry_list_init(KeeEntryList *o) { o->factory = gtk_signal_list_item_factory_new(); g_signal_connect(o->factory, "setup", G_CALLBACK(kee_entry_handle_setup), NULL); g_signal_connect(o->factory, "bind", G_CALLBACK(kee_entry_handle_bind), NULL); + g_signal_connect(o->factory, "unbind", G_CALLBACK(kee_entry_handle_unbind), NULL); + g_signal_connect(o->factory, "teardown", G_CALLBACK(kee_entry_handle_teardown), NULL); } GtkWidget* kee_entry_list_new(GListModel *model) { diff --git a/src/gtk/kee-entry-store.c b/src/gtk/kee-entry-store.c @@ -1,8 +1,11 @@ #include <glib-object.h> #include <gtk/gtk.h> #include <gio/gio.h> +#include <limits.h> #include "kee-entry-store.h" +#include "kee-entry.h" +#include "err.h" typedef struct { @@ -11,6 +14,14 @@ typedef struct { struct _KeeEntryStore { GObject parent; struct db_ctx *db; + int last_idx; + int last_state; + int last_count; + char *last; + char *last_key; + char *last_digest; + char *last_value; + size_t last_value_length; }; @@ -22,40 +33,96 @@ static void kee_entry_store_class_init(KeeEntryStoreClass *kls) { } +static int kee_entry_store_seek(KeeEntryStore *o, int idx); + static void kee_entry_store_init(KeeEntryStore *o) { } static GType kee_entry_store_get_item_type(GListModel *list) { - return G_TYPE_OBJECT; + return KEE_TYPE_ENTRY; } static guint kee_entry_store_get_n_items(GListModel *list) { - return 1; + return KEE_ENTRY_STORE(list)->last_count; } static gpointer kee_entry_store_get_item(GListModel *list, guint index) { - GObject *o; + KeeEntry *o; + KeeEntryStore *store; + char s; + + o = g_object_new(KEE_TYPE_ENTRY, NULL); - o = g_object_new(G_TYPE_OBJECT, NULL); + //kee_entry_load(o, list->db); + store = KEE_ENTRY_STORE(list); + kee_entry_store_seek(store, index); + kee_entry_deserialize(o, store->last_key, 9, store->last_value, store->last_value_length); + + //return o; + kee_entry_apply_list_item_widget(o); - g_object_set_data(o, "foo", "bar"); return o; } - static void kee_entry_store_iface_init(GListModelInterface *ifc) { ifc->get_item_type = kee_entry_store_get_item_type; ifc->get_n_items = kee_entry_store_get_n_items; ifc->get_item = kee_entry_store_get_item; } +/// \todo always scans from 0, inefficient +/// \todo enum lookup states +static int kee_entry_store_seek(KeeEntryStore *o, int idx) { + int c; + int r; + int i; + size_t key_len; + //int direction; + + //c = o->last_idx; + //if (idx == c) { + // return; + //} + + //direction = 0; + key_len = 9; + o->last_key = o->last; + o->last_value = o->last_digest + 64; + *o->last_key = DbKeyLedgerHead; + i = 0; + o->last_state = 2; + while (i <= idx) { + o->last_idx = i; + o->last_value_length = 1024; + r = db_next(o->db, DbKeyLedgerHead, &o->last_key, &key_len, &o->last_value, &o->last_value_length); + if (r) { + o->last_state = 0; + return i; + } + o->last_state = 1; + i++; + } + return i; +} + KeeEntryStore* kee_entry_store_new(struct db_ctx *db) { KeeEntryStore *o; o = g_object_new(KEE_TYPE_ENTRY_STORE, NULL); o->db = db; + o->last_value_length = 1024; + o->last = calloc(2048, 1); + o->last_digest = o->last + DB_KEY_SIZE_LIMIT; + + o->last_count = kee_entry_store_seek(o, INT_MAX); + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "max key index is: %d", o->last_idx - 1); return o; } + +void kee_entry_store_finalize(KeeEntryStore *o) { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "freeing entry store"); + free(o->last); +} diff --git a/src/gtk/kee-entry.c b/src/gtk/kee-entry.c @@ -4,23 +4,75 @@ #include "kee-entry.h" #include "db.h" #include "err.h" +#include "export.h" typedef struct { } KeeEntryPrivate; +struct _KeeEntryClass { + GtkWidget parent_class; +}; + struct _KeeEntry { GtkWidget parent; char *current_id; + int state; + long long timestamp; + char mem[4096]; + char *unit_of_account; }; G_DEFINE_TYPE(KeeEntry, kee_entry, GTK_TYPE_BOX); +static void kee_entry_finalize(GObject *o) { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "tearing down entry"); + //G_OBJECT_CLASS(kee_entry_parent_class)->finalize(o); +} + static void kee_entry_class_init(KeeEntryClass *kls) { + GObjectClass *object_class = G_OBJECT_CLASS(kls); + object_class->finalize = kee_entry_finalize; } static void kee_entry_init(KeeEntry *o) { + o->current_id = (char*)o->mem; + o->unit_of_account = (char*)((o->mem)+64); + o->state = 2; } + int kee_entry_load(KeeEntry *o, struct db_ctx *db, const char *id) { return ERR_OK; } + +/// \todo enum state +int kee_entry_deserialize(KeeEntry *o, const char *key, size_t key_len, const char *data, size_t data_len) { + int r; + struct kee_import im; + char out[4096]; + size_t out_len; + + o->state = 1; + import_init(&im, data, data_len); + + out_len = 4096; + r = import_read(&im, o->unit_of_account, out_len); + *(o->unit_of_account + r) = 0; + + o->state = 0; + + return ERR_OK; +} + +void kee_entry_apply_list_item_widget(KeeEntry *o) { + GtkWidget *widget; + + if (o->state) { + g_log(G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, "entry must be loaded first"); + return; + } + + widget = gtk_label_new(o->unit_of_account); + gtk_box_append(GTK_BOX(o), widget); + return; +} diff --git a/src/gtk/kee-entry.h b/src/gtk/kee-entry.h @@ -8,10 +8,17 @@ G_BEGIN_DECLS +enum KEE_ENTRY_PROPS { + KEE_P_ENTRY_UNIT_OF_ACCOUNT = 1, + KEE_N_ENTRY_PROPS, +}; + #define KEE_TYPE_ENTRY kee_entry_get_type() G_DECLARE_FINAL_TYPE(KeeEntry, kee_entry, KEE, ENTRY, GtkBox); int kee_entry_load(KeeEntry *o, struct db_ctx *db, const char *id); +int kee_entry_deserialize(KeeEntry *o, const char *key, size_t key_len, const char *data, size_t data_len); +void kee_entry_apply_unit_of_account(KeeEntry *o); G_END_DECLS diff --git a/testdata.py b/testdata.py @@ -210,20 +210,23 @@ if __name__ == '__main__': env = lmdb.open(d) dbi = env.open_db() - with env.begin(write=True) as tx: - c = random.randint(1, 20) - r = generate_ledger(entry_count=c) + count_ledgers = os.environ.get('COUNT', '1') - v = r.pop(0) + with env.begin(write=True) as tx: + for i in range(int(count_ledgers)): + c = random.randint(1, 20) + r = generate_ledger(entry_count=c) - z = v[0] - k = LedgerHead.to_key(v[0]) - tx.put(k, v[1]) + v = r.pop(0) - for v in r: - k = LedgerEntry.to_key(v[1], z) + z = v[0] + k = LedgerHead.to_key(v[0]) tx.put(k, v[1]) + for v in r: + k = LedgerEntry.to_key(v[1], z) + tx.put(k, v[1]) + #pfx = b'\x00\x00\x00' #pfx_two = b'\x00\x00\x01'