lnsocket

A minimal C library for connecting to the lightning network
git clone git://jb55.com/lnsocket
Log | Files | Refs | Submodules | README | LICENSE

commit 1eb206600d47b3eb1aadaaa4090f0a4c8d19077b
parent cd612be67e9c79e2d66d879dd6c0a65944694896
Author: William Casarin <jb55@jb55.com>
Date:   Sat, 22 Jan 2022 12:30:49 -0800

lnrpc + commando support

Diffstat:
M.gitignore | 1+
MMakefile | 4++++
MTODO | 3+++
Acommando.c | 41+++++++++++++++++++++++++++++++++++++++++
Acommando.h | 9+++++++++
Mendian.h | 9+++++++++
Mlnsocket.c | 46++++++++++++++++++++++++++++++++++++++++++----
Mlnsocket.h | 11++++++++---
Arpc.c | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 185 insertions(+), 7 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,4 +1,5 @@ .build-result +/lnrpc .buildcmd /.direnv /tags diff --git a/Makefile b/Makefile @@ -54,6 +54,10 @@ test: test.o $(DEPS) $(ARS) @echo "ld test" @$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@ +lnrpc: rpc.o commando.o $(DEPS) $(ARS) + @echo "ld lnrpc" + @$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@ + tags: fake find . -name '*.c' -or -name '*.h' | xargs ctags diff --git a/TODO b/TODO @@ -1,3 +1,6 @@ pull only what is needed from secp and sodium static lib dynamic lib +automatic towire/fromwire serialization +better helpers for building tlvs +(A) getinfo request diff --git a/commando.c b/commando.c @@ -0,0 +1,41 @@ + +#include "lnsocket.h" +#include "cursor.h" +#include "endian.h" + +#define COMMANDO_CMD 0x4c4f +#define COMMANDO_REPLY_CONTINUES 0x594b +#define COMMANDO_REPLY_TERM 0x594d + +int commando_make_rpc_msg(const char *method, const char *params, + const char *rune, uint64_t req_id, unsigned char *buf, + int buflen, unsigned short *outlen) +{ + struct cursor msgbuf; + int ok; + + make_cursor(buf, buf+buflen, &msgbuf); + + params = (params == NULL || params[0] == '\0') ? "[]" : params; + + if (!cursor_push_u16(&msgbuf, COMMANDO_CMD)) + return 0; + + if (!cursor_push_u64(&msgbuf, req_id)) + return 0; + + ok = cursor_push_str(&msgbuf, "{\"method\":\"") && + cursor_push_str(&msgbuf, method) && + cursor_push_str(&msgbuf, "\",\"params\":") && + cursor_push_str(&msgbuf, params) && + cursor_push_str(&msgbuf, ",\"rune\":\"") && + cursor_push_str(&msgbuf, rune) && + cursor_push_str(&msgbuf, "\"}"); + + if (!ok) + return 0; + + *outlen = msgbuf.p - msgbuf.start; + + return 1; +} diff --git a/commando.h b/commando.h @@ -0,0 +1,9 @@ + +#ifndef LNSOCKET_COMMANDO +#define LNSOCKET_COMMANDO + +#include <inttypes.h> + +int commando_make_rpc_msg(const char *method, const char *params, const char *rune, uint64_t req_id, unsigned char *buf, int buflen, unsigned short *outlen); + +#endif /* LNSOCKET_COMMANDO */ diff --git a/endian.h b/endian.h @@ -365,6 +365,15 @@ static inline int cursor_push_u16(struct cursor *cursor, u16 i) return cursor_push(cursor, &v, sizeof(v)); } +static inline int cursor_pull_u16(struct cursor *cursor, u16 *i) +{ + be16 ret; + if (!cursor_pull(cursor, (u8*)&ret, sizeof(ret))) + return 0; + *i = be16_to_cpu(ret); + return 1; +} + static inline int cursor_push_u64(struct cursor *cur, u64 v) { be64 l = cpu_to_be64(v); diff --git a/lnsocket.c b/lnsocket.c @@ -107,7 +107,7 @@ int lnsocket_perform_init(struct lnsocket *ln) u8 global_features[2] = {0}; u8 features[5] = {0}; struct tlv network_tlv; - int len; + u16 len; u8 *buf; if (!lnsocket_read(ln, &buf, &len)) @@ -140,7 +140,45 @@ int lnsocket_perform_init(struct lnsocket *ln) return 1; } -int lnsocket_read(struct lnsocket *ln, unsigned char **buf, int *len) +// simple helper that pushes a message type and payload +int lnsocket_send(struct lnsocket *ln, unsigned short msg_type, const unsigned char *payload, unsigned short payload_len) +{ + reset_cursor(&ln->msgbuf); + + if (!cursor_push_u16(&ln->msgbuf, msg_type)) + return note_error(&ln->errs, "could not write type to msgbuf?"); + + if (!cursor_push(&ln->msgbuf, payload, payload_len)) + return note_error(&ln->errs, "payload too big"); + + return lnsocket_write(ln, ln->msgbuf.start, ln->msgbuf.p - ln->msgbuf.start); +} + +// simple helper that receives a message type and payload +int lnsocket_recv(struct lnsocket *ln, u16 *msg_type, unsigned char **payload, u16 *payload_len) +{ + struct cursor cur; + u8 *msg; + u16 msglen; + + if (!lnsocket_read(ln, &msg, &msglen)) + return 0; + + make_cursor(msg, msg + msglen, &cur); + + if (!cursor_pull_u16(&cur, msg_type)) + return note_error(&ln->errs, "could not read msgtype"); + + *payload_len = msglen - 2; + *payload = cur.p; + + if (*payload + *payload_len > cur.end) + return note_error(&ln->errs, "recv buffer overflow?"); + + return 1; +} + +int lnsocket_read(struct lnsocket *ln, unsigned char **buf, unsigned short *len) { struct cursor enc, dec; u8 hdr[18]; @@ -280,7 +318,7 @@ int lnsocket_make_init_msg(unsigned char *buf, int buflen, const unsigned char *features, u16 flen, const struct tlv **tlvs, unsigned short num_tlvs, - int *outlen) + unsigned short *outlen) { struct cursor msg; @@ -309,7 +347,7 @@ int lnsocket_make_init_msg(unsigned char *buf, int buflen, return 1; } -int lnsocket_write(struct lnsocket *ln, const u8 *msg, int msglen) +int lnsocket_write(struct lnsocket *ln, const u8 *msg, unsigned short msglen) { ssize_t writelen, outcap; size_t outlen; diff --git a/lnsocket.h b/lnsocket.h @@ -64,13 +64,18 @@ struct lnsocket *lnsocket_create(); int lnsocket_make_network_tlv(unsigned char *buf, int buflen, const unsigned char **blockids, int num_blockids, struct tlv *tlv_out); int lnsocket_make_ping_msg(unsigned char *buf, int buflen, unsigned short num_pong_bytes, unsigned short ignored_bytes, int *outlen); -int lnsocket_make_init_msg(unsigned char *buf, int buflen, const unsigned char *globalfeatures, unsigned short gflen, const unsigned char *features, unsigned short flen, const struct tlv **tlvs, unsigned short num_tlvs, int *outlen); +int lnsocket_make_init_msg(unsigned char *buf, int buflen, const unsigned char *globalfeatures, unsigned short gflen, const unsigned char *features, unsigned short flen, const struct tlv **tlvs, unsigned short num_tlvs, unsigned short *outlen); int lnsocket_perform_init(struct lnsocket *ln); int lnsocket_connect(struct lnsocket *, const char *node_id, const char *host); -int lnsocket_write(struct lnsocket *, const unsigned char *msg, int msg_len); -int lnsocket_read(struct lnsocket *, unsigned char **buf, int *len); + +int lnsocket_write(struct lnsocket *, const unsigned char *msg, unsigned short msg_len); +int lnsocket_read(struct lnsocket *, unsigned char **buf, unsigned short *len); + +int lnsocket_send(struct lnsocket *, unsigned short msg_type, const unsigned char *payload, unsigned short payload_len); +int lnsocket_recv(struct lnsocket *, unsigned short *msg_type, unsigned char **payload, unsigned short *payload_len); + void lnsocket_genkey(struct lnsocket *); void lnsocket_destroy(struct lnsocket *); void lnsocket_print_errors(struct lnsocket *); diff --git a/rpc.c b/rpc.c @@ -0,0 +1,68 @@ + +#include <stdlib.h> + +#include "lnsocket.h" +#include "endian.h" +#include "typedefs.h" +#include "commando.h" + +#include <stdio.h> +#include <assert.h> + +int usage() +{ + printf("lnrpc <nodeid> <ip/hostname> <method> <params> <rune>\n\n"); + return 0; +} + +int main(int argc, const char *argv[]) +{ + static u8 msgbuf[4096]; + u8 *buf; + struct lnsocket *ln; + u16 len, msgtype; + int ok = 1; + int verbose = getenv("VERBOSE") != 0; + //int verbose = 1; + + if (argc < 6) + return usage(); + + ln = lnsocket_create(); + assert(ln); + + lnsocket_genkey(ln); + + const char *nodeid = argv[1]; + const char *host = argv[2]; + const char *method = argv[3]; + const char *params = argv[4]; + const char *rune = argv[5]; + + if (!(ok = lnsocket_connect(ln, nodeid, host))) + goto done; + + if (!(ok = lnsocket_perform_init(ln))) + goto done; + + if (verbose) + fprintf(stderr, "init success\n"); + + if (!(ok = commando_make_rpc_msg(method, params, rune, 1, msgbuf, sizeof(msgbuf), &len))) + goto done; + + if (!(ok = lnsocket_write(ln, msgbuf, len))) + goto done; + + if (verbose) + fprintf(stderr, "waiting for response...\n"); + + if (!(ok = lnsocket_recv(ln, &msgtype, &buf, &len))) + goto done; + + printf("%.*s\n", len - 8, buf + 8); +done: + lnsocket_print_errors(ln); + lnsocket_destroy(ln); + return !ok; +}