commit 860878c1662b1df65daaab78ea6118d7dd38ca04
parent b49cf0268181ccabca1f71686bfd39201ae35947
Author: William Casarin <jb55@jb55.com>
Date: Sat, 23 Dec 2023 14:46:00 -0800
block: add bolt11 invoice encoding/decoding
Diffstat:
5 files changed, 132 insertions(+), 2 deletions(-)
diff --git a/Makefile b/Makefile
@@ -2,7 +2,7 @@ CFLAGS = -Wall -Wno-misleading-indentation -Wno-unused-function -Werror -O2 -g -
HEADERS = deps/lmdb/lmdb.h deps/secp256k1/include/secp256k1.h src/sha256.h src/nostrdb.h src/cursor.h src/hex.h src/jsmn.h src/config.h src/sha256.h src/random.h src/memchr.h src/cpu.h $(C_BINDINGS)
FLATCC_SRCS=deps/flatcc/src/runtime/json_parser.c deps/flatcc/src/runtime/verifier.c deps/flatcc/src/runtime/builder.c deps/flatcc/src/runtime/emitter.c deps/flatcc/src/runtime/refmap.c
BOLT11_SRCS = src/bolt11/bolt11.c src/bolt11/bech32.c src/bolt11/tal.c src/bolt11/talstr.c src/bolt11/take.c src/bolt11/list.c src/bolt11/utf8.c src/bolt11/amount.c src/bolt11/hash_u5.c
-SRCS = src/nostrdb.c src/sha256.c $(BOLT11_SRCS) $(FLATCC_SRCS)
+SRCS = src/nostrdb.c src/sha256.c src/invoice.c $(BOLT11_SRCS) $(FLATCC_SRCS)
LDS = $(OBJS) $(ARS)
OBJS = $(SRCS:.c=.o)
DEPS = $(OBJS) $(HEADERS) $(ARS)
diff --git a/src/bolt11/bolt11.h b/src/bolt11/bolt11.h
@@ -4,6 +4,7 @@
#include "short_types.h"
#include "hash_u5.h"
#include "list.h"
+#include "amount.h"
#include "node_id.h"
//#include <secp256k1_recovery.h>
diff --git a/src/invoice.c b/src/invoice.c
@@ -0,0 +1,69 @@
+
+#include "cursor.h"
+#include "invoice.h"
+#include "bolt11/bolt11.h"
+#include "bolt11/amount.h"
+
+int ndb_encode_invoice(struct cursor *cur, struct bolt11 *invoice) {
+ if (!invoice->description && !invoice->description_hash)
+ return 0;
+
+ if (!cursor_push_byte(cur, 1))
+ return 0;
+
+ // TODO: make cursor_cursor_push_varint uint64_t
+ if (!cursor_push_varint(cur, invoice->msat->millisatoshis))
+ return 0;
+
+ if (!cursor_push_varint(cur, invoice->timestamp))
+ return 0;
+
+ if (!cursor_push_varint(cur, invoice->expiry))
+ return 0;
+
+ if (invoice->description) {
+ if (!cursor_push_byte(cur, 1))
+ return 0;
+ if (!cursor_push_c_str(cur, invoice->description))
+ return 0;
+ } else {
+ if (!cursor_push_byte(cur, 2))
+ return 0;
+ if (!cursor_push(cur, invoice->description_hash->u.u8, 32))
+ return 0;
+ }
+
+ return 1;
+}
+
+int ndb_decode_invoice(struct cursor *cur, struct ndb_invoice *invoice)
+{
+ unsigned char desc_type;
+ if (!cursor_pull_byte(cur, &invoice->version))
+ return 0;
+
+ if (!cursor_pull_varint(cur, &invoice->amount))
+ return 0;
+
+ if (!cursor_pull_varint(cur, &invoice->timestamp))
+ return 0;
+
+ if (!cursor_pull_varint(cur, &invoice->expiry))
+ return 0;
+
+ if (!cursor_pull_byte(cur, &desc_type))
+ return 0;
+
+ if (desc_type == 1) {
+ if (!cursor_pull_c_str(cur, (const char**)&invoice->description))
+ return 0;
+ } else if (desc_type == 2) {
+ invoice->description_hash = cur->p;
+ if (!cursor_skip(cur, 32))
+ return 0;
+ } else {
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/src/invoice.h b/src/invoice.h
@@ -0,0 +1,20 @@
+
+#ifndef NDB_INVOICE_H
+#define NDB_INVOICE_H
+
+struct bolt11;
+
+struct ndb_invoice {
+ unsigned char version;
+ uint64_t amount;
+ uint64_t timestamp;
+ uint64_t expiry;
+ char *description;
+ unsigned char *description_hash;
+};
+
+// ENCODING
+int ndb_encode_invoice(struct cursor *cur, struct bolt11 *invoice);
+int ndb_decode_invoice(struct cursor *cur, struct ndb_invoice *invoice);
+
+#endif /* NDB_INVOICE_H */
diff --git a/test.c b/test.c
@@ -3,7 +3,7 @@
#include "hex.h"
#include "io.h"
#include "bolt11/bolt11.h"
-#include "bolt11/amount.h"
+#include "invoice.h"
#include "protected_queue.h"
#include "memchr.h"
#include "print_util.h"
@@ -108,6 +108,45 @@ static void test_filters()
ndb_filter_free(f);
}
+static void test_invoice_encoding(const char *bolt11_str)
+{
+ unsigned char buf[4096];
+ char *fail = NULL;
+ struct cursor cur;
+ struct ndb_invoice invoice;
+ struct bolt11 *bolt11;
+
+ bolt11 = bolt11_decode(NULL, bolt11_str, &fail);
+ make_cursor(buf, buf + sizeof(buf), &cur);
+
+ assert(fail == NULL);
+ assert(ndb_encode_invoice(&cur, bolt11));
+ cur.p = cur.start;
+ assert(ndb_decode_invoice(&cur, &invoice));
+
+ assert(bolt11->msat->millisatoshis == invoice.amount);
+ assert(bolt11->timestamp == invoice.timestamp);
+ assert(bolt11->expiry == invoice.expiry);
+
+ if (bolt11->description != NULL && invoice.description != NULL)
+ assert(!strcmp(bolt11->description, invoice.description));
+ else if (bolt11->description_hash != NULL && invoice.description_hash != NULL)
+ assert(!memcmp(bolt11->description_hash->u.u8, invoice.description_hash, 32));
+ else
+ assert(0);
+
+ tal_free(bolt11);
+}
+
+static void test_encode_decode_invoice()
+{
+ const char *deschash = "lnbc12n1pjctuljsp57l6za0xry37prkrz7vuv4324ljnssm8ukr2vrf6qvvrgclsmpyhspp5xqfuk89duzjlt2yg56ym7p3enrfxxltyfpc364qc8nsu3kznkl8shp5eugmd894yph7wq68u09gke5x2hmn7mg3zrwd06fs57gmcrjm0uxsxqyjw5qcqpjrzjqd7yw3w4kvhx8uvcj7qusfw4uqre3j56zjz9t07nd2u55yuya3awsrqdlcqqdzcqqqqqqqqqqqqqqzqqyg9qxpqysgqwm2tsc448ellvf5xem2c95hfvc07lakph9r8hffh704uxqhs22r9s4ly0jel48zv6f7fy8zjkgmjt5h2l4jc9gyj4av42s40qvve2ysqwuega8";
+ const char *desc = "lnbc12u1pjctuklsp5lg8wdhq2g5xfphkqd5k6gf0femt06wfevu94uuqfprc4ggyqma7spp54lmpmz0mhv3lczepdckr0acf3gdany2654u4k2s8fp5xh0yanjhsdq5w3jhxapdd9h8vmmfvdjsxqyjw5qcqpjrzjqgtsq68q0s9wdadpg32gcfu7hslgkhdpaysj2ha3dtnm8882wa6jyzahpqqqpsgqqyqqqqlgqqqqqpsq9q9qxpqysgqdqzhl8gz46nmalhg27stl25z2u7mqtclv3zz223mjwut90m24fa46xqprjewsqys78j2uljfznz5vtefctu6fw7375ee66e62tj965gpcs85tc";
+
+ test_invoice_encoding(deschash);
+ test_invoice_encoding(desc);
+}
+
// Test fetched_at profile records. These are saved when new profiles are
// processed, or the last time we've fetched the profile.
static void test_fetched_at()
@@ -1021,6 +1060,7 @@ static int test_varints() {
int main(int argc, const char *argv[]) {
test_varints();
+ test_encode_decode_invoice();
test_filters();
//test_migrate();
test_fetched_at();