From 3b40bfa80173b333b7eadf0251464ef291dd91f4 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 5 Jul 2021 20:56:39 +0930 Subject: [PATCH] bolt12_merkle: change offer merkle to latest spec. And fix up the mess we'd made: 1. We didn't order merkles by lesser-first. 2. We didn't correctly construct tree with last nodes on shortest path. Now we have tests! Signed-off-by: Rusty Russell Changelog-EXPERIMENTAL: protocol: offer signature format changed. --- common/bolt12_merkle.c | 147 +++++++++--- common/test/Makefile | 15 ++ common/test/run-bolt12_merkle.c | 404 ++++++++++++++++++++++++++++++++ 3 files changed, 532 insertions(+), 34 deletions(-) create mode 100644 common/test/run-bolt12_merkle.c diff --git a/common/bolt12_merkle.c b/common/bolt12_merkle.c index 11de4b4e4..980b5ee3e 100644 --- a/common/bolt12_merkle.c +++ b/common/bolt12_merkle.c @@ -1,8 +1,14 @@ #include +#include #include +#include #include #include +#ifndef SUPERVERBOSE +#define SUPERVERBOSE(...) +#endif + /* BOLT-offers #12: * TLV types 240 through 1000 are considered signature elements. */ @@ -17,6 +23,7 @@ static void sha256_update_bigsize(struct sha256_ctx *ctx, u64 bigsize) size_t len; len = bigsize_put(buf, bigsize); + SUPERVERBOSE("%s", tal_hexstr(tmpctx, buf, len)); sha256_update(ctx, buf, len); } @@ -26,93 +33,165 @@ static void sha256_update_tlvfield(struct sha256_ctx *ctx, /* We don't keep it raw, so reconstruct. */ sha256_update_bigsize(ctx, field->numtype); sha256_update_bigsize(ctx, field->length); + SUPERVERBOSE("%s", tal_hexstr(tmpctx, field->value, field->length)); sha256_update(ctx, field->value, field->length); } /* BOLT-offers #12: - * The Merkle Tree's leaves are, in TLV-ascending order: - * 1. The SHA256 of: `LnLeaf` followed by the TLV entry. - * 2. The SHA256 of: `LnAll` followed all non-signature TLV entries appended - * in ascending order. + * Thus we define H(`tag`,`msg`) as SHA256(SHA256(`tag`) || SHA256(`tag`) || `msg`)*/ +/* Create a sha256_ctx which has the tag part done. */ +static void h_simpletag_ctx(struct sha256_ctx *sctx, const char *tag) +{ + struct sha256 sha; + sha256(&sha, tag, strlen(tag)); + sha256_init(sctx); + sha256_update(sctx, &sha, sizeof(sha)); + sha256_update(sctx, &sha, sizeof(sha)); + SUPERVERBOSE("tag=SHA256(%s) -> %s", + tal_hexstr(tmpctx, tag, strlen(tag)), + type_to_string(tmpctx, struct sha256, &sha)); +} + + +/* BOLT-offers #12: + * The Merkle tree's leaves are, in TLV-ascending order for each tlv: + * 1. The H(`LnLeaf`,tlv). + * 2. The H(`LnAll`|all-tlvs,tlv) where "all-tlvs" consists of all non-signature TLV entries appended in ascending order. */ -static void calc_lnall(const struct tlv_field *fields, struct sha256 *hash) +/* Create a sha256_ctx which has the tag part done. */ +static void h_lnall_ctx(struct sha256_ctx *sctx, const struct tlv_field *fields) { - struct sha256_ctx sctx; + struct sha256_ctx inner_sctx; + struct sha256 sha; - sha256_init(&sctx); - sha256_update(&sctx, "LnAll", 5); + sha256_init(&inner_sctx); + sha256_update(&inner_sctx, "LnAll", 5); + SUPERVERBOSE("tag=SHA256(%s", tal_hexstr(tmpctx, "LnAll", 5)); for (size_t i = 0; i < tal_count(fields); i++) { if (!is_signature_field(&fields[i])) - sha256_update_tlvfield(&sctx, &fields[i]); + sha256_update_tlvfield(&inner_sctx, &fields[i]); } - sha256_done(&sctx, hash); + sha256_done(&inner_sctx, &sha); + SUPERVERBOSE(") -> %s\n", + type_to_string(tmpctx, struct sha256, &sha)); + + sha256_init(sctx); + sha256_update(sctx, &sha, sizeof(sha)); + sha256_update(sctx, &sha, sizeof(sha)); +} + +/* Use h_lnall_ctx to create nonce */ +static void calc_nonce(const struct sha256_ctx *lnall_ctx, + const struct tlv_field *field, + struct sha256 *hash) +{ + /* Copy context, to add field */ + struct sha256_ctx ctx = *lnall_ctx; + + SUPERVERBOSE("nonce: H(noncetag,"); + sha256_update_tlvfield(&ctx, field); + + sha256_done(&ctx, hash); + SUPERVERBOSE(") = %s\n", type_to_string(tmpctx, struct sha256, hash)); } static void calc_lnleaf(const struct tlv_field *field, struct sha256 *hash) { struct sha256_ctx sctx; - sha256_init(&sctx); - sha256_update(&sctx, "LnLeaf", 6); + SUPERVERBOSE("leaf: H("); + h_simpletag_ctx(&sctx, "LnLeaf"); + SUPERVERBOSE(","); sha256_update_tlvfield(&sctx, field); sha256_done(&sctx, hash); + SUPERVERBOSE(") -> %s\n", type_to_string(tmpctx, struct sha256, hash)); } -static struct sha256 merkle_pair(const struct sha256 a, const struct sha256 b) +/* BOLT-offers #12: + * The Merkle tree inner nodes are H(`LnBranch`, lesser-SHA256|greater-SHA256); + */ +static struct sha256 *merkle_pair(const tal_t *ctx, + const struct sha256 *a, const struct sha256 *b) { - struct sha256 res; + struct sha256 *res; struct sha256_ctx sctx; - sha256_init(&sctx); - sha256_update(&sctx, "LnBranch", 8); - sha256_update(&sctx, a.u.u8, sizeof(a.u.u8)); - sha256_update(&sctx, b.u.u8, sizeof(b.u.u8)); - sha256_done(&sctx, &res); + /* Make sure a < b */ + if (memcmp(a->u.u8, b->u.u8, sizeof(a->u.u8)) > 0) + return merkle_pair(ctx, b, a); + SUPERVERBOSE("branch: H("); + h_simpletag_ctx(&sctx, "LnBranch"); + SUPERVERBOSE(",%s %s", + tal_hexstr(tmpctx, a->u.u8, sizeof(a->u.u8)), + tal_hexstr(tmpctx, b->u.u8, sizeof(b->u.u8))); + sha256_update(&sctx, a->u.u8, sizeof(a->u.u8)); + sha256_update(&sctx, b->u.u8, sizeof(b->u.u8)); + + res = tal(ctx, struct sha256); + sha256_done(&sctx, res); + SUPERVERBOSE(") -> %s\n", type_to_string(tmpctx, struct sha256, res)); return res; } -static struct sha256 merkle_recurse(const struct sha256 *arr, size_t len) +static const struct sha256 *merkle_recurse(const struct sha256 **base, + const struct sha256 **arr, size_t len) { + const struct sha256 *left, *right; if (len == 1) return arr[0]; - return merkle_pair(merkle_recurse(arr, len / 2), - merkle_recurse(arr + len / 2, len - len / 2)); + SUPERVERBOSE("Merkle recurse [%zu - %zu] and [%zu - %zu]\n", + arr - base, arr + len / 2 - 1 - base, + arr + len / 2 - base, arr + len - base); + left = merkle_recurse(base, arr, len / 2); + right = merkle_recurse(base, arr + len / 2, len / 2); + /* left is never NULL if right is not NULL */ + if (!right) + return left; + return merkle_pair(base, left, right); } +/* This is not the fastest way, but it is the most intuitive. */ void merkle_tlv(const struct tlv_field *fields, struct sha256 *merkle) { - struct sha256 lnall, *arr; + struct sha256 **arr; + struct sha256_ctx lnall_ctx; size_t n; - calc_lnall(fields, &lnall); - arr = tal_arr(NULL, struct sha256, tal_count(fields)); + SUPERVERBOSE("nonce tag:"); + h_lnall_ctx(&lnall_ctx, fields); + /* NULL-pad to next power of 2 */ + arr = tal_arrz(NULL, struct sha256 *, + 1ULL << (ilog64(tal_count(fields)) + 1)); n = 0; for (size_t i = 0; i < tal_count(fields); i++) { - struct sha256 s; + struct sha256 leaf, nonce; if (is_signature_field(&fields[i])) continue; - calc_lnleaf(&fields[i], &s); - arr[n++] = merkle_pair(s, lnall); + calc_lnleaf(&fields[i], &leaf); + calc_nonce(&lnall_ctx, &fields[i], &nonce); + arr[n++] = merkle_pair(arr, &leaf, &nonce); } - /* This should never happen, but define it as lnall in this case */ + /* This should never happen, but define it a distinctive all-zeroes */ if (n == 0) - *merkle = lnall; + memset(merkle, 0, sizeof(*merkle)); else - *merkle = merkle_recurse(arr, n); + *merkle = *merkle_recurse(cast_const2(const struct sha256 **, arr), + cast_const2(const struct sha256 **, arr), + tal_count(arr)); tal_free(arr); } /* BOLT-offers #12: * All signatures are created as per * [BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki), - * and tagged as recommended there. Thus to sign a message `msg` with - * `tag`, `m` is SHA256(SHA256(`tag`) || SHA256(`tag`) || `msg`). The - * notation used here is `SIG(tag,msg,key)`. + * and tagged as recommended there. Thus we define H(`tag`,`msg`) as + * SHA256(SHA256(`tag`) || SHA256(`tag`) || `msg`), and SIG(`tag`,`msg`,`key`) + * as the signature of H(`tag`,`msg`) using `key`. * * Each form is signed using one or more TLV signature elements; TLV * types 240 through 1000 are considered signature elements. For these diff --git a/common/test/Makefile b/common/test/Makefile index 596801d82..b37adfeab 100644 --- a/common/test/Makefile +++ b/common/test/Makefile @@ -50,4 +50,19 @@ common/test/run-gossmap_local: \ wire/peer$(EXP)_wiregen.o \ wire/towire.o +common/test/run-bolt12_merkle: \ + common/amount.o \ + common/bigsize.o \ + common/bech32.o \ + common/bech32_util.o \ + common/bolt12.o \ + common/node_id.o \ + common/type_to_string.o \ + wire/bolt12$(EXP)_wiregen.o \ + wire/fromwire.o \ + wire/tlvstream.o \ + wire/peer$(EXP)_wiregen.o \ + wire/towire.o + + check-units: $(COMMON_TEST_PROGRAMS:%=unittest/%) diff --git a/common/test/run-bolt12_merkle.c b/common/test/run-bolt12_merkle.c new file mode 100644 index 000000000..662ccfab0 --- /dev/null +++ b/common/test/run-bolt12_merkle.c @@ -0,0 +1,404 @@ +#define SUPERVERBOSE printf +/* Needed before including bolt12_merkle.c: */ + #include + #include +#include "../bolt12_merkle.c" +#include +#include +#include +#include +#include +#include +#include + +/* Definition of n1 from the spec */ +#include + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for features_unsupported */ +int features_unsupported(const struct feature_set *our_features UNNEEDED, + const u8 *their_features UNNEEDED, + enum feature_place p UNNEEDED) +{ fprintf(stderr, "features_unsupported called!\n"); abort(); } +/* Generated stub for fromwire_channel_id */ +void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } +/* Generated stub for fromwire_onionmsg_path */ +struct onionmsg_path *fromwire_onionmsg_path(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) +{ fprintf(stderr, "fromwire_onionmsg_path called!\n"); abort(); } +/* Generated stub for towire_channel_id */ +void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "towire_channel_id called!\n"); abort(); } +/* Generated stub for towire_onionmsg_path */ +void towire_onionmsg_path(u8 **p UNNEEDED, const struct onionmsg_path *onionmsg_path UNNEEDED) +{ fprintf(stderr, "towire_onionmsg_path called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +/* Contat several tal objects */ +#define concat(p, ...) concat_((p), __VA_ARGS__, NULL) + +static LAST_ARG_NULL void *concat_(const void *p, ...) +{ + va_list ap; + size_t len = 0; + u8 *ret = tal_arr(tmpctx, u8, len); + + va_start(ap, p); + do { + tal_resize(&ret, len + tal_bytelen(p)); + memcpy(ret + len, p, tal_bytelen(p)); + len += tal_bytelen(p); + } while ((p = va_arg(ap, const void *)) != NULL); + va_end(ap); + return ret; +} + +/* Hashes a tal object */ +static struct sha256 *SHA256(const void *obj) +{ + struct sha256 *ret = tal(tmpctx, struct sha256); + sha256(ret, obj, tal_bytelen(obj)); + return ret; +} + +/* Concatenate these two in lesser, greater order. */ +static u8 *ordered(const struct sha256 *a, const struct sha256 *b) +{ + u8 *ret = tal_arr(tmpctx, u8, sizeof(*a) + sizeof(*b)); + + if (memcmp(a, b, sizeof(*a)) < 0) { + memcpy(ret, a, sizeof(*a)); + memcpy(ret + sizeof(*a), b, sizeof(*b)); + } else { + memcpy(ret, b, sizeof(*b)); + memcpy(ret + sizeof(*b), a, sizeof(*a)); + } + return ret; +} + +static u8 *tlv(u64 type, const void *contents, size_t len) +{ + u8 *ret = tal_arr(tmpctx, u8, 0); + + towire_bigsize(&ret, type); + towire_bigsize(&ret, len); + towire(&ret, contents, len); + return ret; +} + +/* BOLT-offers #12: + * Thus we define H(`tag`,`msg`) as SHA256(SHA256(`tag`) || + * SHA256(`tag`) || `msg`) */ + +static struct sha256 *H(const void *tag, const void *msg) +{ + const struct sha256 *taghash = SHA256(tag); + const u8 *full = concat(taghash, taghash, msg); + struct sha256 *ret = SHA256(full); + + printf("test: H(tag=%s,msg=%s) -> SHA256(%s|%s|msg) -> %s\n", + tal_hex(tmpctx, tag), tal_hex(tmpctx, msg), + type_to_string(tmpctx, struct sha256, taghash), + type_to_string(tmpctx, struct sha256, taghash), + type_to_string(tmpctx, struct sha256, ret)); + return ret; +} + +static void merkle_n1(const struct tlv_n1 *n1, struct sha256 *test_m) +{ + u8 *v; + size_t len; + struct tlv_n1 *tmp; + + /* Linearize to populate ->fields */ + v = tal_arr(tmpctx, u8, 0); + towire_n1(&v, n1); + + len = tal_bytelen(v); + tmp = tlv_n1_new(tmpctx); + if (!fromwire_n1(cast_const2(const u8 **, &v), &len, tmp)) + abort(); + assert(len == 0); + + merkle_tlv(tmp->fields, test_m); +} + +/* As a bonus, you get the merkle-test.json by running: + * common/test/run-bolt12_merkle | grep '^JSON:' | cut -d: -f2- | jq */ +#define json_out(fmt, ...) printf("JSON: " fmt "\n" , ## __VA_ARGS__) + +int main(int argc, char *argv[]) +{ + struct sha256 *m, test_m, *leaf[6]; + const char *LnBranch, *LnAll, *LnLeaf; + u8 *tlv1, *tlv2, *tlv3, *all; + struct tlv_n1 *n1; + char *fail; + + common_setup(argv[0]); + /* Note: no nul term */ + LnBranch = tal_dup_arr(tmpctx, char, "LnBranch", strlen("LnBranch"), 0); + LnLeaf = tal_dup_arr(tmpctx, char, "LnLeaf", strlen("LnLeaf"), 0); + LnAll = tal_dup_arr(tmpctx, char, "LnAll", strlen("LnAll"), 0); + + /* Create the tlvs, as per example `n1` in spec */ + { + u8 *v; + struct short_channel_id scid; + struct node_id nid; + + v = tal_arr(tmpctx, u8, 0); + towire_tu64(&v, 1000); + tlv1 = tlv(1, v, tal_bytelen(v)); + + v = tal_arr(tmpctx, u8, 0); + if (!mk_short_channel_id(&scid, 1, 2, 3)) + abort(); + towire_short_channel_id(&v, &scid); + tlv2 = tlv(2, v, tal_bytelen(v)); + + node_id_from_hexstr("0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518", strlen("0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518"), &nid); + v = tal_arr(tmpctx, u8, 0); + towire_node_id(&v, &nid); + towire_amount_msat(&v, AMOUNT_MSAT(1)); + towire_amount_msat(&v, AMOUNT_MSAT(2)); + tlv3 = tlv(3, v, tal_bytelen(v)); + } + + json_out("["); + + json_out("{\"comment\": \"Simple n1 test, tlv1 = 1000\","); + json_out("\"tlv\": \"n1\","); + /* Simplest case, a single (msat) element. */ + all = tlv1; + json_out("\"all-tlvs\": \"%s\",", tal_hex(tmpctx, all)); + json_out("\"leaves\": ["); + + leaf[0] = H(LnBranch, + ordered(H(LnLeaf, tlv1), + H(concat(LnAll, all), tlv1))); + json_out("{ \"H(`LnLeaf`,%s)\": \"%s\", \"H(`LnAll`|all-tlvs,tlv1)\": \"%s\", \"H(`LnBranch`,leaf+nonce)\": \"%s\" }", + tal_hex(tmpctx, tlv1), + type_to_string(tmpctx, struct sha256, H(LnLeaf, tlv1)), + type_to_string(tmpctx, struct sha256, H(concat(LnAll, all), tlv1)), + type_to_string(tmpctx, struct sha256, leaf[0])); + json_out("],"); + + m = leaf[0]; + + json_out("\"branches\": [],"); + json_out("\"merkle\": \"%s\"", + type_to_string(tmpctx, struct sha256, m)); + json_out("},"); + + printf("n1 = %s, merkle = %s\n", + tal_hex(tmpctx, all), + type_to_string(tmpctx, struct sha256, m)); + + /* Create, linearize (populates ->fields) */ + n1 = tlv_n1_new(tmpctx); + n1->tlv1 = tal(n1, u64); + *n1->tlv1 = 1000; + merkle_n1(n1, &test_m); + assert(sha256_eq(&test_m, m)); + + /* Two elements. */ + json_out("{\"comment\": \"n1 test, tlv1 = 1000, tlv2 = 1x2x3\","); + json_out("\"tlv\": \"n1\","); + all = concat(tlv1, tlv2); + + json_out("\"all-tlvs\": \"%s\",", tal_hex(tmpctx, all)); + json_out("\"leaves\": ["); + + leaf[0] = H(LnBranch, ordered(H(LnLeaf, tlv1), + H(concat(LnAll, all), tlv1))); + leaf[1] = H(LnBranch, ordered(H(LnLeaf, tlv2), + H(concat(LnAll, all), tlv2))); + + json_out("{ \"H(`LnLeaf`,%s)\": \"%s\", \"H(`LnAll`|all-tlvs,tlv1)\": \"%s\", \"H(`LnBranch`,leaf+nonce)\": \"%s\" },", + tal_hex(tmpctx, tlv1), + type_to_string(tmpctx, struct sha256, H(LnLeaf, tlv1)), + type_to_string(tmpctx, struct sha256, H(concat(LnAll, all), tlv1)), + type_to_string(tmpctx, struct sha256, leaf[0])); + json_out("{ \"H(`LnLeaf`,%s)\": \"%s\", \"H(`LnAll`|all-tlvs,tlv2)\": \"%s\", \"H(`LnBranch`,leaf+nonce)\": \"%s\" }", + tal_hex(tmpctx, tlv2), + type_to_string(tmpctx, struct sha256, H(LnLeaf, tlv2)), + type_to_string(tmpctx, struct sha256, H(concat(LnAll, all), tlv2)), + type_to_string(tmpctx, struct sha256, leaf[1])); + json_out("],"); + + json_out("\"branches\": ["); + json_out("{ \"desc\": \"1: tlv1+nonce and tlv2+nonce\", \"H(`LnBranch`,%s)\": \"%s\" }", + tal_hex(tmpctx, ordered(leaf[0], leaf[1])), + tal_hex(tmpctx, H(LnBranch, ordered(leaf[0], leaf[1])))); + json_out("],"); + + m = H(LnBranch, ordered(leaf[0], leaf[1])); + + json_out("\"merkle\": \"%s\"", + type_to_string(tmpctx, struct sha256, m)); + json_out("},"); + + printf("n1 = %s, merkle = %s\n", + tal_hex(tmpctx, all), + type_to_string(tmpctx, struct sha256, m)); + + n1->tlv2 = tal(n1, struct short_channel_id); + if (!mk_short_channel_id(n1->tlv2, 1, 2, 3)) + abort(); + merkle_n1(n1, &test_m); + assert(sha256_eq(&test_m, m)); + + /* Three elements. */ + json_out("{\"comment\": \"n1 test, tlv1 = 1000, tlv2 = 1x2x3, tlv3 = 0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518, 1, 2\","); + json_out("\"tlv\": \"n1\","); + all = concat(tlv1, tlv2, tlv3); + json_out("\"all-tlvs\": \"%s\",", tal_hex(tmpctx, all)); + json_out("\"leaves\": ["); + + leaf[0] = H(LnBranch, ordered(H(LnLeaf, tlv1), + H(concat(LnAll, all), tlv1))); + leaf[1] = H(LnBranch, ordered(H(LnLeaf, tlv2), + H(concat(LnAll, all), tlv2))); + leaf[2] = H(LnBranch, ordered(H(LnLeaf, tlv3), + H(concat(LnAll, all), tlv3))); + json_out("{ \"H(`LnLeaf`,%s)\": \"%s\", \"H(`LnAll`|all-tlvs,tlv1)\": \"%s\", \"H(`LnBranch`,leaf+nonce)\": \"%s\" },", + tal_hex(tmpctx, tlv1), + type_to_string(tmpctx, struct sha256, H(LnLeaf, tlv1)), + type_to_string(tmpctx, struct sha256, H(concat(LnAll, all), tlv1)), + type_to_string(tmpctx, struct sha256, leaf[0])); + json_out("{ \"H(`LnLeaf`,%s)\": \"%s\", \"H(`LnAll`|all-tlvs,tlv2)\": \"%s\", \"H(`LnBranch`,leaf+nonce)\": \"%s\" },", + tal_hex(tmpctx, tlv2), + type_to_string(tmpctx, struct sha256, H(LnLeaf, tlv2)), + type_to_string(tmpctx, struct sha256, H(concat(LnAll, all), tlv2)), + type_to_string(tmpctx, struct sha256, leaf[1])); + json_out("{ \"H(`LnLeaf`,%s)\": \"%s\", \"H(`LnAll`|all-tlvs,tlv3)\": \"%s\", \"H(`LnBranch`,leaf+nonce)\": \"%s\" }", + tal_hex(tmpctx, tlv3), + type_to_string(tmpctx, struct sha256, H(LnLeaf, tlv3)), + type_to_string(tmpctx, struct sha256, H(concat(LnAll, all), tlv3)), + type_to_string(tmpctx, struct sha256, leaf[2])); + json_out("],"); + + json_out("\"branches\": ["); + json_out("{ \"desc\": \"1: tlv1+nonce and tlv2+nonce\", \"H(`LnBranch`,%s)\": \"%s\" },", + tal_hex(tmpctx, ordered(leaf[0], leaf[1])), + tal_hex(tmpctx, H(LnBranch, ordered(leaf[0], leaf[1])))); + json_out("{ \"desc\": \"1 and tlv3+nonce\", \"H(`LnBranch`,%s)\": \"%s\" }", + tal_hex(tmpctx, ordered(H(LnBranch, ordered(leaf[0], leaf[1])), + leaf[2])), + tal_hex(tmpctx, H(LnBranch, + ordered(H(LnBranch, + ordered(leaf[0], leaf[1])), + leaf[2])))); + json_out("],"); + + m = H(LnBranch, + ordered(H(LnBranch, ordered(leaf[0], leaf[1])), + leaf[2])); + json_out("\"merkle\": \"%s\"", + type_to_string(tmpctx, struct sha256, m)); + json_out("},"); + + printf("n1 = %s, merkle = %s\n", + tal_hex(tmpctx, all), + type_to_string(tmpctx, struct sha256, m)); + + n1->tlv3 = tal(n1, struct tlv_n1_tlv3); + pubkey_from_hexstr("0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518", strlen("0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518"), &n1->tlv3->node_id); + n1->tlv3->amount_msat_1 = AMOUNT_MSAT(1); + n1->tlv3->amount_msat_2 = AMOUNT_MSAT(2); + merkle_n1(n1, &test_m); + assert(sha256_eq(&test_m, m)); + + /* Now try with an actual offer, with 6 fields. */ + struct tlv_offer *offer = offer_decode(tmpctx, + "lno1qcp4256ypqpq86q2pucnq42ngssx2an9wfujqerp0y2pqun4wd68jtn00fkxzcnn9ehhyec6qgqsz83qfwdpl28qqmc78ymlvhmxcsywdk5wrjnj36jryg488qwlrnzyjczs", + strlen("lno1qcp4256ypqpq86q2pucnq42ngssx2an9wfujqerp0y2pqun4wd68jtn00fkxzcnn9ehhyec6qgqsz83qfwdpl28qqmc78ymlvhmxcsywdk5wrjnj36jryg488qwlrnzyjczs"), + NULL, NULL, &fail); + + assert(tal_count(offer->fields) == 6); + u8 *fieldwires[6]; + + /* currency: USD */ + fieldwires[0] = tlv(6, "USD", strlen("USD")); + /* amount: 1000 */ + fieldwires[1] = tlv(8, "\x03\xe8", 2); + /* description: 10USD every day */ + fieldwires[2] = tlv(10, "10USD every day", strlen("10USD every day")); + /* vendor: rusty.ozlabs.org */ + fieldwires[3] = tlv(20, "rusty.ozlabs.org", strlen("rusty.ozlabs.org")); + /* recurrence: time_unit = 1, period = 1 */ + fieldwires[4] = tlv(26, "\x01\x01", 2); + /* node_id: 4b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605 */ + fieldwires[5] = tlv(30, "\x4b\x9a\x1f\xa8\xe0\x06\xf1\xe3\x93\x7f\x65\xf6\x6c\x40\x8e\x6d\xa8\xe1\xca\x72\x8e\xa4\x32\x22\xa7\x38\x1d\xf1\xcc\x44\x96\x05", 32); + + json_out("{\"comment\": \"offer test, currency = USD, amount = 1000, description = 10USD every day, vendor = rusty.ozlabs.org, recurrence = time_unit = 1, period = 1, node_id = 4b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605\","); + json_out("\"tlv\": \"offer\","); + + all = concat(fieldwires[0], fieldwires[1], fieldwires[2], + fieldwires[3], fieldwires[4], fieldwires[5]); + json_out("\"all-tlvs\": \"%s\",", tal_hex(tmpctx, all)); + + json_out("\"leaves\": ["); + for (size_t i = 0; i < ARRAY_SIZE(fieldwires); i++) { + leaf[i] = H(LnBranch, + ordered(H(LnLeaf, fieldwires[i]), + H(concat(LnAll, all), fieldwires[i]))); + json_out("{ \"H(`LnLeaf`,%s)\": \"%s\", \"H(`LnAll`|all-tlvs,tlv)\": \"%s\", \"H(`LnBranch`,leaf+nonce)\": \"%s\" }%s", + tal_hex(tmpctx, fieldwires[i]), + type_to_string(tmpctx, struct sha256, + H(LnLeaf, fieldwires[i])), + type_to_string(tmpctx, struct sha256, + H(concat(LnAll, all), fieldwires[0])), + type_to_string(tmpctx, struct sha256, leaf[i]), + i == ARRAY_SIZE(fieldwires) - 1 ? "" : ","); + } + json_out("],"); + + json_out("\"branches\": ["); + json_out("{ \"desc\": \"1: currency+nonce and amount+nonce\", \"H(`LnBranch`,%s)\": \"%s\" },", + tal_hex(tmpctx, ordered(leaf[0], leaf[1])), + tal_hex(tmpctx, H(LnBranch, ordered(leaf[0], leaf[1])))); + json_out("{ \"desc\": \"2: description+nonce and vendor+nonce\", \"H(`LnBranch`,%s)\": \"%s\"},", + tal_hex(tmpctx, ordered(leaf[2], leaf[3])), + tal_hex(tmpctx, H(LnBranch, ordered(leaf[2], leaf[3])))); + struct sha256 *b12 = H(LnBranch, + ordered(H(LnBranch, + ordered(leaf[0], leaf[1])), + H(LnBranch, + ordered(leaf[2], leaf[3])))); + json_out("{ \"desc\": \"3: 1 and 2\", \"H(`LnBranch`,%s)\": \"%s\" },", + tal_hex(tmpctx, ordered(H(LnBranch, ordered(leaf[0], leaf[1])), + H(LnBranch, ordered(leaf[2], leaf[3])))), + tal_hex(tmpctx, b12)); + json_out("{ \"desc\": \"4: recurrence+nonce and node_id+nonce\", \"H(`LnBranch`,%s)\": \"%s\" },", + tal_hex(tmpctx, ordered(leaf[4], leaf[5])), + tal_hex(tmpctx, H(LnBranch, ordered(leaf[4], leaf[5])))); + json_out("{ \"desc\": \"5: 3 and 4\", \"H(`LnBranch`,%s)\": \"%s\" }", + tal_hex(tmpctx, ordered(b12, + H(LnBranch, + ordered(leaf[4], leaf[5])))), + tal_hex(tmpctx, H(LnBranch, + ordered(b12, + H(LnBranch, + ordered(leaf[4], leaf[5])))))); + json_out("],"); + json_out("\"merkle\": \"%s\"", + type_to_string(tmpctx, struct sha256, m)); + json_out("}]"); + + m = H(LnBranch, + ordered(H(LnBranch, + ordered(H(LnBranch, ordered(leaf[0], leaf[1])), + H(LnBranch, ordered(leaf[2], leaf[3])))), + H(LnBranch, ordered(leaf[4], leaf[5])))); + printf("offer = %s, merkle = %s\n", + tal_hex(tmpctx, all), + type_to_string(tmpctx, struct sha256, m)); + + merkle_tlv(offer->fields, &test_m); + assert(sha256_eq(&test_m, m)); + + common_shutdown(); +}