#define SUPERVERBOSE printf #include "config.h" /* Needed before including bolt12_merkle.c: */ #include #include #include "../bolt12_merkle.c" #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_blinded_path */ struct blinded_path *fromwire_blinded_path(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED) { fprintf(stderr, "fromwire_blinded_path called!\n"); abort(); } /* Generated stub for fromwire_channel_id */ bool 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 towire_blinded_path */ void towire_blinded_path(u8 **p UNNEEDED, const struct blinded_path *blinded_path UNNEEDED) { fprintf(stderr, "towire_blinded_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(); } /* 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; } /* Just return type field from tlv */ static const u8 *tlv_type(const void *tlv) { size_t len = tal_bytelen(tlv); const u8 *cursor = tlv; fromwire_bigsize(&cursor, &len); assert(cursor); return tal_dup_arr(tmpctx, u8, tlv, tal_bytelen(tlv) - len, 0); } /* 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_tlv_n1(&v, n1); len = tal_bytelen(v); tmp = fromwire_tlv_n1(tmpctx, cast_const2(const u8 **, &v), &len); assert(len == 0); merkle_tlv(tmp->fields, test_m); } /* As a bonus, you get the bolt12/signature-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, *LnNonce, *LnLeaf; u8 *tlv1, *tlv2, *tlv3; struct tlv_n1 *n1; u8 node_id[PUBKEY_CMPR_LEN]; struct secret alice_secret, bob_secret; struct pubkey alice, bob; struct sha256 sha; secp256k1_keypair kp; 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); LnNonce = tal_dup_arr(tmpctx, char, "LnNonce", strlen("LnNonce"), 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. */ json_out("\"first-tlv\": \"%s\",", tal_hex(tmpctx, tlv1)); json_out("\"leaves\": ["); leaf[0] = H(LnBranch, ordered(H(LnLeaf, tlv1), H(concat(LnNonce, tlv1), tlv_type(tlv1)))); json_out("{ \"H(`LnLeaf`,%s)\": \"%s\", \"H(`LnNonce`|first-tlv,tlv1-type)\": \"%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(LnNonce, tlv1), tlv_type(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("},"); /* 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\","); json_out("\"first-tlv\": \"%s\",", tal_hex(tmpctx, tlv1)); json_out("\"leaves\": ["); leaf[0] = H(LnBranch, ordered(H(LnLeaf, tlv1), H(concat(LnNonce, tlv1), tlv_type(tlv1)))); leaf[1] = H(LnBranch, ordered(H(LnLeaf, tlv2), H(concat(LnNonce, tlv1), tlv_type(tlv2)))); json_out("{ \"H(`LnLeaf`,%s)\": \"%s\", \"H(`LnNonce`|first-tlv,tlv1-type)\": \"%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(LnNonce, tlv1), tlv_type(tlv1))), type_to_string(tmpctx, struct sha256, leaf[0])); json_out("{ \"H(`LnLeaf`,%s)\": \"%s\", \"H(`LnNonce`|first-tlv,tlv2-type)\": \"%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(LnNonce, tlv1), tlv_type(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("},"); 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\","); json_out("\"first-tlv\": \"%s\",", tal_hex(tmpctx, tlv1)); json_out("\"leaves\": ["); leaf[0] = H(LnBranch, ordered(H(LnLeaf, tlv1), H(concat(LnNonce, tlv1), tlv_type(tlv1)))); leaf[1] = H(LnBranch, ordered(H(LnLeaf, tlv2), H(concat(LnNonce, tlv1), tlv_type(tlv2)))); leaf[2] = H(LnBranch, ordered(H(LnLeaf, tlv3), H(concat(LnNonce, tlv1), tlv_type(tlv3)))); json_out("{ \"H(`LnLeaf`,%s)\": \"%s\", \"H(`LnNonce`|first-tlv,1)\": \"%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(LnNonce, tlv1), tlv_type(tlv1))), type_to_string(tmpctx, struct sha256, leaf[0])); json_out("{ \"H(`LnLeaf`,%s)\": \"%s\", \"H(`LnNonce`|first-tlv,2)\": \"%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(LnNonce, tlv1), tlv_type(tlv2))), type_to_string(tmpctx, struct sha256, leaf[1])); json_out("{ \"H(`LnLeaf`,%s)\": \"%s\", \"H(`LnNonce`|first-tlv,3)\": \"%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(LnNonce, tlv1), tlv_type(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("},"); 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 invoice request. */ struct tlv_invoice_request *invreq = tlv_invoice_request_new(tmpctx); memset(&alice_secret, 'A', sizeof(alice_secret)); pubkey_from_secret(&alice_secret, &alice); memset(&bob_secret, 'B', sizeof(bob_secret)); pubkey_from_secret(&bob_secret, &bob); invreq->offer_node_id = tal_dup(invreq, struct pubkey, &alice); invreq->offer_description = tal_dup_arr(invreq, char, "A Mathematical Treatise", strlen("A Mathematical Treatise"), 0); invreq->offer_amount = tal(invreq, u64); *invreq->offer_amount = 100; invreq->offer_currency = tal_dup_arr(invreq, char, "USD", strlen("USD"), 0); invreq->invreq_payer_id = tal_dup(invreq, struct pubkey, &bob); invreq->invreq_metadata = tal_arrz(invreq, u8, 8); /* Populate ->fields array, for merkle routine */ invreq->fields = tlv_make_fields(invreq, tlv_invoice_request); merkle_tlv(invreq->fields, &test_m); /* BOLT-offers #12: * - MUST set `signature`.`sig` as detailed in [Signature Calculation](#signature-calculation) using the `invreq_payer_id`. */ invreq->signature = tal(invreq, struct bip340sig); sighash_from_merkle("invoice_request", "signature", &test_m, &sha); assert(secp256k1_keypair_create(secp256k1_ctx, &kp, bob_secret.data) == 1); assert(secp256k1_schnorrsig_sign32(secp256k1_ctx, invreq->signature->u8, sha.u.u8, &kp, NULL) == 1); char *invreqtext = invrequest_encode(tmpctx, invreq); invreq = invrequest_decode(tmpctx, invreqtext, strlen(invreqtext), NULL, NULL, &fail); json_out("{\"comment\": \"invoice_request test: offer_node_id = Alice (privkey 0x414141...), offer_description = 'A Mathematical Treatise', offer_amount = 100, offer_currency = 'USD', invreq_payer_id = Bob (privkey 0x424242...), invreq_metadata = 0x0000000000000000\","); json_out("\"bolt12\": \"%s\",", invreqtext); json_out("\"tlv\": \"invoice_request\","); assert(tal_count(invreq->fields) == 7); u8 *fieldwires[6]; /* invreq_metadata: 8 bytes of 0 */ fieldwires[0] = tlv(0, "\0\0\0\0\0\0\0\0", 8); /* offer_currency: USD */ fieldwires[1] = tlv(6, "USD", strlen("USD")); /* offer_amount: 100 */ fieldwires[2] = tlv(8, "\x64", 1); /* offer_description: A Mathematical Treatise */ fieldwires[3] = tlv(10, "A Mathematical Treatise", strlen("A Mathematical Treatise")); /* offer_node_id: Alice */ pubkey_to_der(node_id, &alice); fieldwires[4] = tlv(22, node_id, PUBKEY_CMPR_LEN); /* invreq_payer_id: Bob */ pubkey_to_der(node_id, &bob); fieldwires[5] = tlv(88, node_id, PUBKEY_CMPR_LEN); json_out("\"first-tlv\": \"%s\",", tal_hex(tmpctx, fieldwires[0])); json_out("\"leaves\": ["); for (size_t i = 0; i < ARRAY_SIZE(fieldwires); i++) { leaf[i] = H(LnBranch, ordered(H(LnLeaf, fieldwires[i]), H(concat(LnNonce, fieldwires[0]), tlv_type(fieldwires[i])))); json_out("{ \"H(`LnLeaf`,%s)\": \"%s\", \"H(`LnNonce`|first-tlv,%u)\": \"%s\", \"H(`LnBranch`,leaf+nonce)\": \"%s\" }%s", tal_hex(tmpctx, fieldwires[i]), type_to_string(tmpctx, struct sha256, H(LnLeaf, fieldwires[i])), tlv_type(fieldwires[i])[0], /* Works becuase they're all 1-byte types! */ type_to_string(tmpctx, struct sha256, H(concat(LnNonce, fieldwires[0]), tlv_type(fieldwires[i]))), type_to_string(tmpctx, struct sha256, leaf[i]), i == ARRAY_SIZE(fieldwires) - 1 ? "" : ","); } json_out("],"); json_out("\"branches\": ["); json_out("{ \"desc\": \"1: metadata+nonce and currency+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: amount+nonce and descripton+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: node_id+nonce and payer_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])))))); 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])))); json_out("],"); json_out("\"merkle\": \"%s\",", type_to_string(tmpctx, struct sha256, m)); json_out("\"signature_tag\": \"lightninginvoice_requestsignature\","); json_out("\"H(signature_tag,merkle)\": \"%s\",", type_to_string(tmpctx, struct sha256, &sha)); json_out("\"signature\": \"%s\"", type_to_string(tmpctx, struct bip340sig, invreq->signature)); json_out("}]"); assert(sha256_eq(&test_m, m)); common_shutdown(); }