commit 45a0fb59cd00eaf54295dc9dcea5116de03291c4
parent e4bca9fdb2cde1e67f1bfbaf7e5eb2515b0d53a2
Author: William Casarin <jb55@jb55.com>
Date: Sat, 11 Aug 2018 13:43:23 -0700
channel parsing works
Diffstat:
M | Makefile | | | 3 | +-- |
M | demo.c | | | 2 | ++ |
M | json.c | | | 189 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- |
M | json.h | | | 17 | ++++++++++++++++- |
M | ln.h | | | 22 | +++++++++++++++++----- |
M | main.c | | | 34 | ++++++++++++++++++++++++++++++++-- |
M | update.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)