lnvis

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

json.c (9457B)


      1 
      2 #include "jsmn.h"
      3 #include "json.h"
      4 #include <stdio.h>
      5 #include <assert.h>
      6 #include <stdio.h>
      7 #include <ctype.h>
      8 #include <stdlib.h>
      9 #include <string.h>
     10 
     11 #define ERROR_READING_CHANNELS -2
     12 #define ERROR_ALLOCATING_CHANNELS -3
     13 
     14 struct channel_parser {
     15 	const char *field;
     16 	enum channel_parsing_state state;
     17 };
     18 
     19 
     20 struct node_parser {
     21 	const char *field;
     22 	enum node_parsing_state state;
     23 };
     24 
     25 /* "source": "02fc7ef232bd958209a2180fb18844388babc48a81d31434e343ecc4d7dd1a9186",  */
     26 /* "destination": "03b7ca940bc33b882dc1f1bee353a6cf205b1a7472d8ae24d45370a8f510c27d23",  */
     27 /* "short_channel_id": "505273:1478:0",  */
     28 /* "public": true,  */
     29 /* "satoshis": 80000,  */
     30 /* "flags": 0,  */
     31 /* "active": true,  */
     32 /* "last_update": 1531178014,  */
     33 /* "base_fee_millisatoshi": 1,  */
     34 /* "fee_per_millionth": 1,  */
     35 /* "delay": 14 */
     36 static const struct channel_parser channel_parsers[] = {
     37 	{ "source",                PARSING_CHAN_SOURCE },
     38 	{ "destination",           PARSING_CHAN_DESTINATION },
     39 	{ "short_channel_id",      PARSING_CHAN_SHORTID },
     40 	{ "public",                PARSING_CHAN_PUBLIC },
     41 	{ "satoshis",              PARSING_CHAN_SATOSHIS },
     42 	{ "channel_flags",         PARSING_CHAN_FLAGS },
     43 	{ "active",                PARSING_CHAN_ACTIVE },
     44 	{ "last_update",           PARSING_CHAN_LAST_UPDATE },
     45 	{ "base_fee_millisatoshi", PARSING_CHAN_BASE_FEE },
     46 	{ "fee_per_millionth",     PARSING_CHAN_FEE_PER },
     47 	{ "delay",                 PARSING_CHAN_DELAY },
     48 };
     49 
     50 /* "nodeid": "03a8334aba5660e241468e2f0deb2526bfd50d0e3fe808d882913e39094dc1a028", */
     51 /* "alias": "cosmicApotheosis", */
     52 /* "color": "33cccc", */
     53 /* "last_timestamp": 1533693585, */
     54 /* "global_features": "", */
     55 /* "addresses": [ */
     56 /* 	{ "type": "ipv4", */
     57 /* 	  "address": "138.229.205.237", */
     58 /* 	  "port": 9735 */
     59 /* 	}] */
     60 static const struct node_parser node_parsers[] = {
     61 	{ "nodeid",    PARSING_NODE_ID },
     62 	{ "alias",     PARSING_NODE_ALIAS },
     63 	{ "color",     PARSING_NODE_COLOR },
     64 	{ "addresses", PARSING_NODE_ADDRESSES },
     65 };
     66 
     67 #define MKTMPBUF \
     68 	char buf[toklen + 1]; \
     69 	buf[toklen] = '\0'; \
     70 	memcpy(buf, tokstr, toklen)
     71 
     72 static inline u32 parse_u32(int toklen, const char *tokstr)
     73 {
     74 	MKTMPBUF;
     75 	return strtoul(buf, NULL, 10);
     76 }
     77 
     78 static inline u64 parse_u64(int toklen, const char *tokstr)
     79 {
     80 	MKTMPBUF;
     81 	return strtoll(buf, NULL, 10);
     82 }
     83 
     84 static int parse_short_channel_id(int toklen, const char *tokstr,
     85 				   struct short_channel_id *chanid)
     86 {
     87 	char buf[toklen + 1];
     88 	buf[toklen] = '\0';
     89 	memcpy(buf, tokstr, toklen);
     90 	return sscanf(buf, "%ux%ux%hu",
     91 		      &chanid->blocknum,
     92 		      &chanid->txnum,
     93 		      &chanid->outnum);
     94 }
     95 
     96 static inline int tokeq(size_t toklen, const char* tok, const char *str)
     97 {
     98 	if (toklen != strlen(str))
     99 		return 0;
    100 	return memcmp(tok, str, toklen) == 0;
    101 }
    102 
    103 int parse_json(FILE *fd, jsmntok_t **ptoks, int *ntoks, char **pbuffer) {
    104 	int off, parserr, i;
    105 	void *res;
    106 	char *buffer;
    107 	jsmntok_t *toks;
    108 	jsmn_parser parser;
    109 	jsmn_init(&parser);
    110 
    111 	int bufsize = 4096 * 4096;
    112 	int toksize = 512 * 4096;
    113 
    114 	buffer = malloc(bufsize);
    115 	toks = calloc(toksize, sizeof(jsmntok_t));
    116 
    117 	off = 0;
    118 	parserr = 0;
    119 
    120 	while (parserr <= 0) {
    121 		/* Read more if parser says, or we have 0 tokens. */
    122 		if (parserr == 0 || parserr == JSMN_ERROR_PART) {
    123 			i = fread(buffer + off, 1, bufsize - 1 - off, fd);
    124 			if (i <= 0)
    125 				return ERROR_READING_CHANNELS;
    126 			off += i;
    127 			/* NUL terminate */
    128 			buffer[off] = '\0';
    129 		}
    130 
    131 		/* (Continue) parsing */
    132 		parserr = jsmn_parse(&parser, buffer, off, toks, toksize);
    133 
    134 		switch (parserr) {
    135 		case JSMN_ERROR_INVAL:
    136 			return ERROR_READING_CHANNELS;
    137 		case JSMN_ERROR_NOMEM:
    138 			/* Need more tokens, double it */
    139 			toksize *= 2;
    140 			res = realloc(toks, toksize * sizeof(jsmntok_t));
    141 			if (res == NULL) {
    142 				free(toks);
    143 				return ERROR_ALLOCATING_CHANNELS;
    144 			}
    145 			toks = res;
    146 			break;
    147 		case JSMN_ERROR_PART:
    148 			/* Need more data: make room if necessary */
    149 			if (off == bufsize - 1) {
    150 				bufsize *= 2;
    151 				res = realloc(buffer, bufsize);
    152 				if (res == NULL) {
    153 					free(buffer);
    154 					return ERROR_ALLOCATING_CHANNELS;
    155 				}
    156 				buffer = res;
    157 			}
    158 			break;
    159 		}
    160 	}
    161 
    162 	*ntoks = parserr;
    163 	*ptoks = toks;
    164 	*pbuffer = buffer;
    165 
    166 	return 0;
    167 }
    168 
    169 
    170 static inline u8 hexdigit(char hex) {
    171 	return (hex <= '9') ? hex - '0' : toupper(hex) - 'A' + 10 ;
    172 }
    173 
    174 
    175 static void parse_color(int toklen, const char *tokstr, union color *color) {
    176 	color->a = 1.0f;
    177 
    178 	if (toklen != 6)
    179 		goto colorerr;
    180 
    181 	for (int i = 0; i < 3; i++) {
    182 		u8 c1 = tokstr[i*2];
    183 		u8 c2 = tokstr[i*2+1];
    184 
    185 		if (!isxdigit(c1) || !isxdigit(c2))
    186 			goto colorerr;
    187 
    188 		u8 val = hexdigit(c1) << 4 | hexdigit(c2);
    189 		color->rgba[i] = ((float)val) / 255.0f;
    190 	}
    191 
    192 	return;
    193 colorerr:
    194 	color->r = 0.0f;
    195 	color->g = 0.0f;
    196 	color->b = 0.0f;
    197 	return;
    198 }
    199 
    200 int parse_clightning_nodes(FILE *fd, int *node_count, struct node **pnodes)
    201 {
    202 	struct node *node = NULL, *nodes;
    203 	char *buffer, *tokstr;
    204 	void *res;
    205 	jsmntok_t *toks;
    206 	jsmntok_t *tok;
    207 	int i, ntoks = 0, toklen = 0, objs = 0;
    208 	int nodecap = 4096;
    209 	enum node_parsing_state state = PARSING_NODE_TOKEN;
    210 
    211 	*node_count = 0;
    212 
    213 	nodes = calloc(nodecap, sizeof(struct node));
    214 
    215 	int acount = 0;
    216 
    217 	int rez = parse_json(fd, &toks, &ntoks, &buffer);
    218 	if (rez < 0)
    219 		return rez;
    220 
    221 	for (i = 0; i < ntoks; i++) {
    222 		tok = &toks[i];
    223 
    224 		if (tok->type == JSMN_ARRAY) {
    225 			if (state == PARSING_NODE_ADDRESSES) {
    226 				if (++acount == 1) {
    227 					acount = 0;
    228 					state = PARSING_NODE_TOKEN;
    229 				}
    230 			}
    231 			continue;
    232 		}
    233 
    234 		if (tok->type == JSMN_OBJECT) {
    235 			if (state == PARSING_NODE_ADDRESSES)
    236 				continue;
    237 
    238 			if (++objs > 2)
    239 				(*node_count)++;
    240 
    241 			continue;
    242 		}
    243 
    244 		toklen = tok->end - tok->start;
    245 		tokstr = &buffer[tok->start];
    246 
    247 		node = &nodes[*node_count];
    248 
    249 		// allocate more channels if needed
    250 		if (*node_count > nodecap) {
    251 			nodecap *= 2;
    252 			res = realloc(nodes, nodecap * sizeof(struct channel));
    253 			if (res == NULL) {
    254 				free(nodes);
    255 				return ERROR_ALLOCATING_CHANNELS;
    256 			}
    257 			nodes = res;
    258 		}
    259 
    260 		switch (state) {
    261 		case PARSING_NODE_ID:
    262 			strncpy(node->id, tokstr, min(toklen, PUBKEY_SIZE));
    263 			state = PARSING_NODE_TOKEN;
    264 			break;
    265 
    266 		case PARSING_NODE_ALIAS:
    267 			strncpy(node->alias, tokstr, min(toklen, MAX_ALIAS_SIZE));
    268 			state = PARSING_NODE_TOKEN;
    269 			break;
    270 
    271 		case PARSING_NODE_COLOR:
    272 			parse_color(toklen, tokstr, &node->color);
    273 			state = PARSING_NODE_TOKEN;
    274 			break;
    275 
    276 		case PARSING_NODE_ADDRESSES:
    277 			// TODO: parse addresses
    278 			state = PARSING_NODE_TOKEN;
    279 			break;
    280 
    281 		case PARSING_NODE_TOKEN:
    282 			for (size_t i = 0; i < ARRAY_SIZE(node_parsers); i++) {
    283 				if (tokeq(toklen, tokstr,
    284 					  node_parsers[i].field)) {
    285 					state = node_parsers[i].state;
    286 					continue;
    287 				}
    288 			}
    289 			break;
    290 		}
    291 
    292 	}
    293 
    294 	*pnodes = nodes;
    295 	*node_count = *node_count + 1;
    296 
    297 	return 0;
    298 }
    299 
    300 
    301 int parse_clightning_channels(FILE *fd, int *nchans, struct channel **pchannels)
    302 {
    303 	char *buffer;
    304 	void *res;
    305 	struct channel *chan = NULL, *channels;
    306 	struct short_channel_id last_chan;
    307 	int chancap = 4096;
    308 	jsmntok_t *toks;
    309 	jsmntok_t *tok;
    310 	char *tokstr;
    311 	int ntoks = 0, toklen = 0, objs = 0;
    312 	enum channel_parsing_state state = PARSING_CHAN_TOKEN;
    313 
    314 	int rez = parse_json(fd, &toks, &ntoks, &buffer);
    315 	if (rez < 0)
    316 		return rez;
    317 
    318 	channels = calloc(chancap, sizeof(struct channel));
    319 
    320 	// parse tokens
    321 	for (int i = 0; i < ntoks; ++i) {
    322 		tok = &toks[i];
    323 
    324 		if (tok->type == JSMN_ARRAY)
    325 			continue;
    326 
    327 		if (tok->type == JSMN_OBJECT) {
    328 			if (++objs > 2)
    329 				(*nchans)++;
    330 
    331 			continue;
    332 		}
    333 
    334 		toklen = tok->end - tok->start;
    335 		tokstr = &buffer[tok->start];
    336 
    337 		// allocate more channels if needed
    338 		if (*nchans >= chancap) {
    339 			chancap *= 2;
    340 			res = realloc(channels, chancap * sizeof(struct channel));
    341 			if (res == NULL) {
    342 				free(channels);
    343 				return ERROR_ALLOCATING_CHANNELS;
    344 			}
    345 			channels = res;
    346 		}
    347 
    348 		chan = &channels[*nchans];
    349 
    350 		switch (state) {
    351 		case PARSING_CHAN_SOURCE:
    352 			strncpy(chan->source, tokstr, min(PUBKEY_SIZE, toklen));
    353 			state = PARSING_CHAN_TOKEN;
    354 			break;
    355 
    356 		case PARSING_CHAN_DESTINATION:
    357 			strncpy(chan->destination, tokstr, min(PUBKEY_SIZE, toklen));
    358 			state = PARSING_CHAN_TOKEN;
    359 			break;
    360 
    361 		case PARSING_CHAN_SHORTID:
    362 			parse_short_channel_id(toklen, tokstr,
    363 					       &chan->short_channel_id);
    364 			state = PARSING_CHAN_TOKEN;
    365 			break;
    366 
    367 		case PARSING_CHAN_PUBLIC:
    368 			chan->public = tokeq(toklen, tokstr, "true");
    369 			state = PARSING_CHAN_TOKEN;
    370 			break;
    371 
    372 		case PARSING_CHAN_SATOSHIS:
    373 			chan->satoshis = parse_u64(toklen, tokstr);
    374 			state = PARSING_CHAN_TOKEN;
    375 			break;
    376 
    377 		case PARSING_CHAN_FLAGS:
    378 			chan->flags = parse_u32(toklen, tokstr);
    379 			state = PARSING_CHAN_TOKEN;
    380 			break;
    381 
    382 		case PARSING_CHAN_ACTIVE:
    383 			chan->active = tokeq(toklen, tokstr, "true");
    384 			state = PARSING_CHAN_TOKEN;
    385 			break;
    386 
    387 		case PARSING_CHAN_LAST_UPDATE:
    388 			chan->last_update_timestamp = parse_u32(toklen, tokstr);
    389 			state = PARSING_CHAN_TOKEN;
    390 			break;
    391 
    392 		case PARSING_CHAN_BASE_FEE:
    393 			chan->base_fee_msat = parse_u32(toklen, tokstr);
    394 			state = PARSING_CHAN_TOKEN;
    395 			break;
    396 
    397 		case PARSING_CHAN_FEE_PER:
    398 			chan->fee_per_millionth = parse_u32(toklen, tokstr);
    399 			state = PARSING_CHAN_TOKEN;
    400 			break;
    401 
    402 		case PARSING_CHAN_DELAY:
    403 			chan->delay = parse_u32(toklen, tokstr);
    404 			state = PARSING_CHAN_TOKEN;
    405 			break;
    406 
    407 		case PARSING_CHAN_TOKEN:
    408 			for (size_t i = 0; i < ARRAY_SIZE(channel_parsers); i++) {
    409 				if (tokeq(toklen, tokstr,
    410 					  channel_parsers[i].field)) {
    411 					state = channel_parsers[i].state;
    412 					continue;
    413 				}
    414 			}
    415 			break;
    416 		}
    417 
    418 	}
    419 
    420 	*pchannels = channels;
    421 	*nchans = *nchans + 1;
    422 
    423 	free(buffer);
    424 	free(toks);
    425 
    426 	return 0;
    427 }