lnvis

nanovg lightning network visualizer
git clone git://jb55.com/lnvis
Log | Files | Refs | README | LICENSE

commit 45a0fb59cd00eaf54295dc9dcea5116de03291c4
parent e4bca9fdb2cde1e67f1bfbaf7e5eb2515b0d53a2
Author: William Casarin <jb55@jb55.com>
Date:   Sat, 11 Aug 2018 13:43:23 -0700

channel parsing works

Diffstat:
MMakefile | 3+--
Mdemo.c | 2++
Mjson.c | 189+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mjson.h | 17++++++++++++++++-
Mln.h | 22+++++++++++++++++-----
Mmain.c | 34++++++++++++++++++++++++++++++++--
Mupdate.c | 50+++++++++++++++++++++++++-------------------------
7 files changed, 278 insertions(+), 39 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,8 +1,7 @@ BIN = lnvis PREFIX ?= /usr/local -CFLAGS = -Ideps -ggdb -O2 -Wall -Wextra -std=c99 \ - -Wno-implicit-fallthrough +CFLAGS = -Ideps -ggdb -O2 -Wall -Wextra -Werror -std=c99 LDFLAGS = -lglfw -lGL diff --git a/demo.c b/demo.c @@ -1,3 +1,5 @@ +#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" + #include "demo.h" #include <stdio.h> #include <string.h> diff --git a/json.c b/json.c @@ -1,16 +1,80 @@ #include "jsmn.h" -#include "ln.h" +#include "json.h" #include <stdio.h> #include <assert.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #define ERROR_READING_CHANNELS -2 #define ERROR_ALLOCATING_CHANNELS -3 -int parse_clightning_channels(FILE *fd, int *nchannels, struct channel **channels) +struct parser { + const char *field; + enum parsing_state state; +}; + +/* "source": "02fc7ef232bd958209a2180fb18844388babc48a81d31434e343ecc4d7dd1a9186", */ +/* "destination": "03b7ca940bc33b882dc1f1bee353a6cf205b1a7472d8ae24d45370a8f510c27d23", */ +/* "short_channel_id": "505273:1478:0", */ +/* "public": true, */ +/* "satoshis": 80000, */ +/* "flags": 0, */ +/* "active": true, */ +/* "last_update": 1531178014, */ +/* "base_fee_millisatoshi": 1, */ +/* "fee_per_millionth": 1, */ +/* "delay": 14 */ +static const struct parser channel_parsers[] = { + { "short_channel_id", PARSING_SHORTID }, + { "public", PARSING_PUBLIC }, + { "satoshis", PARSING_SATOSHIS }, + { "flags", PARSING_FLAGS }, + { "active", PARSING_ACTIVE }, + { "last_update", PARSING_LAST_UPDATE }, + { "base_fee_millisatoshi", PARSING_BASE_FEE }, + { "fee_per_millionth", PARSING_FEE_PER }, + { "delay", PARSING_DELAY }, +}; + +#define MKTMPBUF \ + char buf[toklen + 1]; \ + buf[toklen] = '\0'; \ + memcpy(buf, tokstr, toklen) + +static inline u32 parse_u32(int toklen, const char *tokstr) +{ + MKTMPBUF; + return strtoul(buf, NULL, 10); +} + +static inline u64 parse_u64(int toklen, const char *tokstr) { + MKTMPBUF; + return strtoll(buf, NULL, 10); +} + +static int parse_short_channel_id(int toklen, const char *tokstr, + struct short_channel_id *chanid) +{ + char buf[toklen + 1]; + buf[toklen] = '\0'; + memcpy(buf, tokstr, toklen); + return sscanf(buf, "%u:%u:%hu", + &chanid->blocknum, + &chanid->txnum, + &chanid->outnum); +} + +static inline int tokeq(size_t toklen, const char* tok, const char *str) +{ + if (toklen != strlen(str)) + return 0; + return memcmp(tok, str, toklen) == 0; +} + +int parse_json(FILE *fd, jsmntok_t **ptoks, int *ntoks, char **pbuffer) { int off, parserr, i; void *res; char *buffer; @@ -23,6 +87,7 @@ int parse_clightning_channels(FILE *fd, int *nchannels, struct channel **channel buffer = malloc(bufsize); toks = calloc(toksize, sizeof(jsmntok_t)); + off = 0; parserr = 0; @@ -68,12 +133,128 @@ int parse_clightning_channels(FILE *fd, int *nchannels, struct channel **channel } } - printf("got %d tokens\n", parserr); + *ntoks = parserr; + *ptoks = toks; + *pbuffer = buffer; + + return 0; +} + +int parse_clightning_channels(FILE *fd, int *nchannels, struct channel **pchannels) +{ + char *buffer; + void *res; + struct channel *chan = NULL, *channels; + int nchans = 0; + int chancap = 4096; + jsmntok_t *toks; + jsmntok_t *tok; + char *tokstr; + int ntoks; + int toklen; + enum parsing_state state = PARSING_TOKEN; + int rez = parse_json(fd, &toks, &ntoks, &buffer); + if (rez < 0) + return rez; + + channels = calloc(chancap, sizeof(struct channel)); + + int objs = 0; // parse tokens - for (i = 0; i < parserr; ++i) { + for (int i = 0; i < ntoks; ++i) { + tok = &toks[i]; + + if (tok->type == JSMN_ARRAY) + continue; + + if (tok->type == JSMN_OBJECT) { + if (++objs > 2) + nchans++; + + continue; + } + + toklen = tok->end - tok->start; + tokstr = &buffer[tok->start]; + + // allocate more channels if needed + if (nchans > chancap) { + chancap *= 2; + res = realloc(channels, chancap * sizeof(struct channel)); + if (res == NULL) { + free(channels); + return ERROR_ALLOCATING_CHANNELS; + } + channels = res; + } + + chan = &channels[nchans]; + + // TODO: lookup node id, assign nodes + switch (state) { + case PARSING_SHORTID: + parse_short_channel_id(toklen, tokstr, + &chan->short_channel_id); + state = PARSING_TOKEN; + break; + + case PARSING_PUBLIC: + chan->public = tokeq(toklen, tokstr, "true"); + state = PARSING_TOKEN; + break; + + case PARSING_SATOSHIS: + chan->satoshis = parse_u64(toklen, tokstr); + state = PARSING_TOKEN; + break; + + case PARSING_FLAGS: + chan->satoshis = parse_u32(toklen, tokstr); + state = PARSING_TOKEN; + break; + + case PARSING_ACTIVE: + chan->active = tokeq(toklen, tokstr, "true"); + state = PARSING_TOKEN; + break; + + case PARSING_LAST_UPDATE: + chan->last_update_timestamp = parse_u32(toklen, tokstr); + state = PARSING_TOKEN; + break; + + case PARSING_BASE_FEE: + chan->base_fee_msat = parse_u32(toklen, tokstr); + state = PARSING_TOKEN; + break; + + case PARSING_FEE_PER: + chan->fee_per_millionth = parse_u32(toklen, tokstr); + state = PARSING_TOKEN; + break; + + case PARSING_DELAY: + chan->delay = parse_u32(toklen, tokstr); + state = PARSING_TOKEN; + break; + + case PARSING_TOKEN: + for (size_t i = 0; i < ARRAY_SIZE(channel_parsers); i++) { + if (tokeq(toklen, tokstr, + channel_parsers[i].field)) { + state = channel_parsers[i].state; + continue; + } + } + break; + } + } + *pchannels = channels; + *nchannels = nchans + 1; + free(buffer); free(toks); diff --git a/json.h b/json.h @@ -4,7 +4,22 @@ #define LNVIS_JSON_H #include <stdio.h> +#include "ln.h" -int parse_clightning_channels(FILE *fd); +enum parsing_state { + PARSING_TOKEN, + PARSING_SHORTID, + PARSING_PUBLIC, + PARSING_SATOSHIS, + PARSING_FLAGS, + PARSING_ACTIVE, + PARSING_LAST_UPDATE, + PARSING_BASE_FEE, + PARSING_FEE_PER, + PARSING_DELAY, +}; + +int parse_clightning_channels(FILE *fd, int *nchannels, + struct channel **channels); #endif /* LNVIS_JSON_H */ diff --git a/ln.h b/ln.h @@ -66,15 +66,27 @@ enum side { * - [`4`:`fee_proportional_millionths`] */ +struct short_channel_id { + u32 blocknum; + u32 txnum; + u16 outnum; +}; + struct channel { struct node *nodes[2]; - u64 our_msatoshi; - /* Statistics for min and max our_msatoshi. */ - u64 msatoshi_to_us_min; - u64 msatoshi_to_us_max; + struct short_channel_id short_channel_id; + u8 public; + u8 active; + + u16 flags; + + u64 satoshis; - u32 last_update; + u32 last_update_timestamp; + u32 delay; + u32 base_fee_msat; + u32 fee_per_millionth; }; enum display_flags { diff --git a/main.c b/main.c @@ -101,11 +101,41 @@ void key_callback(GLFWwindow* window, int key, int scancode, int action, int mod } } +static void print_channel(struct channel *chan) +{ + printf("chan shortid=%u:%u:%hu public=%d sats=%llu active=%d " + "last_update=%u base_fee_msat=%u fee_per_millionth=%u " + "delay=%u\n", + chan->short_channel_id.blocknum, + chan->short_channel_id.txnum, + chan->short_channel_id.outnum, + chan->public, + chan->satoshis, + chan->active, + chan->last_update_timestamp, + chan->base_fee_msat, + chan->fee_per_millionth, + chan->delay); +} + + void test_read_json() { FILE *f = fopen("clightning-channels.json", "r"); - int res = parse_clightning_channels(f); - printf("test_read_json res %d\n", res); + int nchans = 0; + struct channel *channels; + int res = parse_clightning_channels(f, &nchans, &channels); + + if (res != 0) { + printf("test_read_json res %d\n", res); + exit(1); + } + + for (int i = 0; i < nchans; i++) + print_channel(&channels[i]); + + printf("%d channels\n", nchans); + fclose(f); exit(0); } diff --git a/update.c b/update.c @@ -86,31 +86,31 @@ static void force_graph(struct ln *ln, double dt) { } -static void repel_nearby(struct node *node, double dt) -{ - struct node *n = NULL; - struct cell *cell = node->cell; - - // might happen the first iteration? - if (cell == NULL) - return; - - // we're the only one in this cell, there's nothing to repel - if (cell->node_count == 1) - return; - - for (int i = 0; i < cell->node_count; ++i) { - n = cell->nodes[i]; - - // dont repel against ourselves - if (n == node) - continue; - - assert(n); - assert(node); - repel_nodes(n, node, dt); - } -} +/* static void repel_nearby(struct node *node, double dt) */ +/* { */ +/* struct node *n = NULL; */ +/* struct cell *cell = node->cell; */ + +/* // might happen the first iteration? */ +/* if (cell == NULL) */ +/* return; */ + +/* // we're the only one in this cell, there's nothing to repel */ +/* if (cell->node_count == 1) */ +/* return; */ + +/* for (int i = 0; i < cell->node_count; ++i) { */ +/* n = cell->nodes[i]; */ + +/* // dont repel against ourselves */ +/* if (n == node) */ +/* continue; */ + +/* assert(n); */ +/* assert(node); */ +/* repel_nodes(n, node, dt); */ +/* } */ +/* } */ static void physics(struct ln *ln, double dt)