diff --git a/channeld/channeld.c b/channeld/channeld.c index 8d26ad1ac9b4..7849a24a83f5 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -2320,8 +2320,8 @@ static void peer_in(struct peer *peer, const u8 *msg) case WIRE_PING: case WIRE_WARNING: case WIRE_ERROR: + case WIRE_OBS2_ONION_MESSAGE: case WIRE_ONION_MESSAGE: - case WIRE_OBS_ONION_MESSAGE: abort(); } diff --git a/common/blindedpath.c b/common/blindedpath.c index 7ab13e303c36..7994813b769e 100644 --- a/common/blindedpath.c +++ b/common/blindedpath.c @@ -11,95 +11,6 @@ #define SUPERVERBOSE(...) #endif -/* Obsolete version: use enctlv() helper. */ -struct onionmsg_path **make_blindedpath(const tal_t *ctx, - const struct pubkey *route, - struct pubkey *initial_blinding, - struct pubkey *final_blinding) -{ - struct privkey e; - struct pubkey *pk_e, *b; - struct secret *rho; - struct onionmsg_path **path; - size_t num = tal_count(route); - - if (!num) - abort(); - - /* E(i) */ - pk_e = tal_arr(tmpctx, struct pubkey, num); - /* B(i) */ - b = tal_arr(tmpctx, struct pubkey, num); - /* rho(i) */ - rho = tal_arr(tmpctx, struct secret, num); - - randombytes_buf(&e, sizeof(e)); - if (!pubkey_from_privkey(&e, &pk_e[0])) - abort(); - - for (size_t i = 0; i < num; i++) { - struct secret ss; - struct secret hmac; - struct sha256 h; - - if (secp256k1_ecdh(secp256k1_ctx, ss.data, - &route[i].pubkey, e.secret.data, - NULL, NULL) != 1) - abort(); - - subkey_from_hmac("blinded_node_id", &ss, &hmac); - b[i] = route[i]; - if (i != 0) { - if (secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx, - &b[i].pubkey, hmac.data) != 1) - abort(); - } - subkey_from_hmac("rho", &ss, &rho[i]); - blinding_hash_e_and_ss(&pk_e[i], &ss, &h); - if (i != num-1) - blinding_next_pubkey(&pk_e[i], &h, &pk_e[i+1]); - blinding_next_privkey(&e, &h, &e); - } - - *initial_blinding = pk_e[0]; - *final_blinding = pk_e[num-1]; - - path = tal_arr(ctx, struct onionmsg_path *, num); - for (size_t i = 0; i < num; i++) { - path[i] = tal(path, struct onionmsg_path); - path[i]->node_id = b[i]; - } - - for (size_t i = 0; i < num - 1; i++) { - const unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - struct tlv_encmsg_tlvs *inner; - int ret; - - /* Inner is encrypted */ - inner = tlv_encmsg_tlvs_new(tmpctx); - /* FIXME: We could support scids, too */ - inner->next_node_id = cast_const(struct pubkey *, &route[i+1]); - - path[i]->enctlv = tal_arr(path, u8, 0); - towire_encmsg_tlvs(&path[i]->enctlv, inner); - towire_pad(&path[i]->enctlv, - crypto_aead_chacha20poly1305_ietf_ABYTES); - - ret = crypto_aead_chacha20poly1305_ietf_encrypt(path[i]->enctlv, NULL, - path[i]->enctlv, - tal_bytelen(path[i]->enctlv) - crypto_aead_chacha20poly1305_ietf_ABYTES, - NULL, 0, - NULL, npub, - rho[i].data); - assert(ret == 0); - } - - /* Final one has no enctlv */ - path[num-1]->enctlv = NULL; - - return path; -} - /* Blinds node_id and calculates next blinding factor. */ static bool blind_node(const struct privkey *blinding, const struct secret *ss, @@ -147,12 +58,12 @@ static bool blind_node(const struct privkey *blinding, return true; } -static u8 *enctlv_from_encmsg(const tal_t *ctx, - const struct privkey *blinding, - const struct pubkey *node, - const struct tlv_encmsg_tlvs *encmsg, - struct privkey *next_blinding, - struct pubkey *node_alias) +static u8 *enctlv_from_encmsg_raw(const tal_t *ctx, + const struct privkey *blinding, + const struct pubkey *node, + const u8 *raw_encmsg TAKES, + struct privkey *next_blinding, + struct pubkey *node_alias) { /* https://github.com/lightningnetwork/lightning-rfc/blob/route-blinding/proposals/route-blinding.md */ struct secret ss, rho; @@ -176,9 +87,7 @@ static u8 *enctlv_from_encmsg(const tal_t *ctx, if (!blind_node(blinding, &ss, node, node_alias, next_blinding)) return NULL; - /* Marshall */ - ret = tal_arr(ctx, u8, 0); - towire_encmsg_tlvs(&ret, encmsg); + ret = tal_dup_talarr(ctx, u8, raw_encmsg); SUPERVERBOSE("\t\"encmsg_hex\": \"%s\",\n", tal_hex(tmpctx, ret)); /* @@ -204,6 +113,32 @@ static u8 *enctlv_from_encmsg(const tal_t *ctx, return ret; } +static u8 *enctlv_from_obs2_encmsg(const tal_t *ctx, + const struct privkey *blinding, + const struct pubkey *node, + const struct tlv_obs2_encmsg_tlvs *encmsg, + struct privkey *next_blinding, + struct pubkey *node_alias) +{ + u8 *encmsg_raw = tal_arr(NULL, u8, 0); + towire_obs2_encmsg_tlvs(&encmsg_raw, encmsg); + return enctlv_from_encmsg_raw(ctx, blinding, node, take(encmsg_raw), + next_blinding, node_alias); +} + +static u8 *enctlv_from_encmsg(const tal_t *ctx, + const struct privkey *blinding, + const struct pubkey *node, + const struct tlv_encrypted_data_tlv *encmsg, + struct privkey *next_blinding, + struct pubkey *node_alias) +{ + u8 *encmsg_raw = tal_arr(NULL, u8, 0); + towire_encrypted_data_tlv(&encmsg_raw, encmsg); + return enctlv_from_encmsg_raw(ctx, blinding, node, take(encmsg_raw), + next_blinding, node_alias); +} + bool unblind_onion(const struct pubkey *blinding, void (*ecdh)(const struct pubkey *point, struct secret *ss), struct pubkey *onion_key, @@ -225,16 +160,13 @@ bool unblind_onion(const struct pubkey *blinding, hmac.data) == 1; } -static struct tlv_encmsg_tlvs *decrypt_encmsg(const tal_t *ctx, - const struct pubkey *blinding, - const struct secret *ss, - const u8 *enctlv) +static u8 *decrypt_encmsg_raw(const tal_t *ctx, + const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv) { struct secret rho; u8 *dec; - const u8 *cursor; - size_t maxlen; - struct tlv_encmsg_tlvs *encmsg; /* All-zero npub */ static const unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES]; @@ -250,7 +182,7 @@ static struct tlv_encmsg_tlvs *decrypt_encmsg(const tal_t *ctx, if (tal_bytelen(enctlv) < crypto_aead_chacha20poly1305_ietf_ABYTES) return NULL; - dec = tal_arr(tmpctx, u8, tal_bytelen(enctlv) + dec = tal_arr(ctx, u8, tal_bytelen(enctlv) - crypto_aead_chacha20poly1305_ietf_ABYTES); if (crypto_aead_chacha20poly1305_ietf_decrypt(dec, NULL, NULL, @@ -258,18 +190,49 @@ static struct tlv_encmsg_tlvs *decrypt_encmsg(const tal_t *ctx, NULL, 0, npub, rho.data) != 0) - return NULL; + return tal_free(dec); + + return dec; +} + +static struct tlv_obs2_encmsg_tlvs *decrypt_obs2_encmsg(const tal_t *ctx, + const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv) +{ + struct tlv_obs2_encmsg_tlvs *encmsg; + const u8 *cursor = decrypt_encmsg_raw(tmpctx, blinding, ss, enctlv); + size_t maxlen = tal_bytelen(cursor); + + /* BOLT-onion-message #4: + * + * - if the `enctlv` is not a valid TLV... + * - MUST drop the message. + */ + encmsg = tlv_obs2_encmsg_tlvs_new(ctx); + if (!fromwire_obs2_encmsg_tlvs(&cursor, &maxlen, encmsg) + || !tlv_fields_valid(encmsg->fields, NULL, NULL)) + return tal_free(encmsg); - cursor = dec; - maxlen = tal_bytelen(dec); + return encmsg; +} + +static struct tlv_encrypted_data_tlv *decrypt_encmsg(const tal_t *ctx, + const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv) +{ + struct tlv_encrypted_data_tlv *encmsg; + const u8 *cursor = decrypt_encmsg_raw(tmpctx, blinding, ss, enctlv); + size_t maxlen = tal_bytelen(cursor); /* BOLT-onion-message #4: * * - if the `enctlv` is not a valid TLV... * - MUST drop the message. */ - encmsg = tlv_encmsg_tlvs_new(ctx); - if (!fromwire_encmsg_tlvs(&cursor, &maxlen, encmsg) + encmsg = tlv_encrypted_data_tlv_new(ctx); + if (!fromwire_encrypted_data_tlv(&cursor, &maxlen, encmsg) || !tlv_fields_valid(encmsg->fields, NULL, NULL)) return tal_free(encmsg); @@ -282,7 +245,7 @@ bool decrypt_enctlv(const struct pubkey *blinding, struct pubkey *next_node, struct pubkey *next_blinding) { - struct tlv_encmsg_tlvs *encmsg; + struct tlv_encrypted_data_tlv *encmsg; encmsg = decrypt_encmsg(tmpctx, blinding, ss, enctlv); if (!encmsg) @@ -304,10 +267,10 @@ bool decrypt_enctlv(const struct pubkey *blinding, * The reader: * - if it is not the final node according to the onion encryption: *... - * - if the `enctlv` contains `self_id`: + * - if the `enctlv` contains `path_id`: * - MUST drop the message. */ - if (encmsg->self_id) + if (encmsg->path_id) return false; /* BOLT-onion-message #4: @@ -322,8 +285,8 @@ bool decrypt_enctlv(const struct pubkey *blinding, * `E(i+1) = H(E(i) || ss(i)) * E(i)`). */ *next_node = *encmsg->next_node_id; - if (encmsg->next_blinding) - *next_blinding = *encmsg->next_blinding; + if (encmsg->next_blinding_override) + *next_blinding = *encmsg->next_blinding_override; else { /* E(i-1) = H(E(i) || ss(i)) * E(i) */ struct sha256 h; @@ -339,9 +302,9 @@ bool decrypt_final_enctlv(const tal_t *ctx, const u8 *enctlv, const struct pubkey *my_id, struct pubkey *alias, - struct secret **self_id) + struct secret **path_id) { - struct tlv_encmsg_tlvs *encmsg; + struct tlv_encrypted_data_tlv *encmsg; struct secret node_id_blinding; /* Repeat the tweak to get the alias it was using for us */ @@ -356,11 +319,11 @@ bool decrypt_final_enctlv(const tal_t *ctx, if (!encmsg) return false; - if (tal_bytelen(encmsg->self_id) == sizeof(**self_id)) { - *self_id = tal(ctx, struct secret); - memcpy(*self_id, encmsg->self_id, sizeof(**self_id)); + if (tal_bytelen(encmsg->path_id) == sizeof(**path_id)) { + *path_id = tal(ctx, struct secret); + memcpy(*path_id, encmsg->path_id, sizeof(**path_id)); } else - *self_id = NULL; + *path_id = NULL; return true; } @@ -370,15 +333,15 @@ u8 *create_enctlv(const tal_t *ctx, const struct pubkey *node, const struct pubkey *next_node, size_t padlen, - const struct pubkey *override_blinding, + const struct pubkey *next_blinding_override, struct privkey *next_blinding, struct pubkey *node_alias) { - struct tlv_encmsg_tlvs *encmsg = tlv_encmsg_tlvs_new(tmpctx); + struct tlv_encrypted_data_tlv *encmsg = tlv_encrypted_data_tlv_new(tmpctx); if (padlen) encmsg->padding = tal_arrz(encmsg, u8, padlen); encmsg->next_node_id = cast_const(struct pubkey *, next_node); - encmsg->next_blinding = cast_const(struct pubkey *, override_blinding); + encmsg->next_blinding_override = cast_const(struct pubkey *, next_blinding_override); return enctlv_from_encmsg(ctx, blinding, node, encmsg, next_blinding, node_alias); @@ -388,17 +351,145 @@ u8 *create_final_enctlv(const tal_t *ctx, const struct privkey *blinding, const struct pubkey *final_node, size_t padlen, - const struct secret *self_id, + const struct secret *path_id, struct pubkey *node_alias) { - struct tlv_encmsg_tlvs *encmsg = tlv_encmsg_tlvs_new(tmpctx); + struct tlv_encrypted_data_tlv *encmsg = tlv_encrypted_data_tlv_new(tmpctx); struct privkey unused_next_blinding; if (padlen) encmsg->padding = tal_arrz(encmsg, u8, padlen); - if (self_id) - encmsg->self_id = (u8 *)tal_dup(encmsg, struct secret, self_id); + if (path_id) + encmsg->path_id = (u8 *)tal_dup(encmsg, struct secret, path_id); return enctlv_from_encmsg(ctx, blinding, final_node, encmsg, &unused_next_blinding, node_alias); } + +/* Obsolete variants */ +bool decrypt_obs2_enctlv(const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv, + struct pubkey *next_node, + struct pubkey *next_blinding) +{ + struct tlv_obs2_encmsg_tlvs *encmsg; + + encmsg = decrypt_obs2_encmsg(tmpctx, blinding, ss, enctlv); + if (!encmsg) + return false; + + /* BOLT-onion-message #4: + * + * The reader: + * - if it is not the final node according to the onion encryption: + *... + * - if the `enctlv` ... does not contain + * `next_node_id`: + * - MUST drop the message. + */ + if (!encmsg->next_node_id) + return false; + + /* BOLT-onion-message #4: + * The reader: + * - if it is not the final node according to the onion encryption: + *... + * - if the `enctlv` contains `self_id`: + * - MUST drop the message. + */ + if (encmsg->self_id) + return false; + + /* BOLT-onion-message #4: + * The reader: + * - if it is not the final node according to the onion encryption: + *... + * - if `blinding` is specified in the `enctlv`: + * - MUST pass that as `blinding` in the `onion_message` + * - otherwise: + * - MUST pass `blinding` derived as in + * [Route Blinding][route-blinding] (i.e. + * `E(i+1) = H(E(i) || ss(i)) * E(i)`). + */ + *next_node = *encmsg->next_node_id; + if (encmsg->next_blinding) + *next_blinding = *encmsg->next_blinding; + else { + /* E(i-1) = H(E(i) || ss(i)) * E(i) */ + struct sha256 h; + blinding_hash_e_and_ss(blinding, ss, &h); + blinding_next_pubkey(blinding, &h, next_blinding); + } + return true; +} + +bool decrypt_obs2_final_enctlv(const tal_t *ctx, + const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv, + const struct pubkey *my_id, + struct pubkey *alias, + struct secret **self_id) +{ + struct tlv_obs2_encmsg_tlvs *encmsg; + struct secret node_id_blinding; + + /* Repeat the tweak to get the alias it was using for us */ + subkey_from_hmac("blinded_node_id", ss, &node_id_blinding); + *alias = *my_id; + if (secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx, + &alias->pubkey, + node_id_blinding.data) != 1) + return false; + + encmsg = decrypt_obs2_encmsg(tmpctx, blinding, ss, enctlv); + if (!encmsg) + return false; + + if (tal_bytelen(encmsg->self_id) == sizeof(**self_id)) { + *self_id = tal(ctx, struct secret); + memcpy(*self_id, encmsg->self_id, sizeof(**self_id)); + } else + *self_id = NULL; + + return true; +} + +u8 *create_obs2_enctlv(const tal_t *ctx, + const struct privkey *blinding, + const struct pubkey *node, + const struct pubkey *next_node, + size_t padlen, + const struct pubkey *override_blinding, + struct privkey *next_blinding, + struct pubkey *node_alias) +{ + struct tlv_obs2_encmsg_tlvs *encmsg = tlv_obs2_encmsg_tlvs_new(tmpctx); + if (padlen) + encmsg->padding = tal_arrz(encmsg, u8, padlen); + encmsg->next_node_id = cast_const(struct pubkey *, next_node); + encmsg->next_blinding = cast_const(struct pubkey *, override_blinding); + + return enctlv_from_obs2_encmsg(ctx, blinding, node, encmsg, + next_blinding, node_alias); +} + +u8 *create_obs2_final_enctlv(const tal_t *ctx, + const struct privkey *blinding, + const struct pubkey *final_node, + size_t padlen, + const struct secret *self_id, + struct pubkey *node_alias) +{ + struct tlv_obs2_encmsg_tlvs *encmsg = tlv_obs2_encmsg_tlvs_new(tmpctx); + struct privkey unused_next_blinding; + + if (padlen) + encmsg->padding = tal_arrz(encmsg, u8, padlen); + if (self_id) + encmsg->self_id = (u8 *)tal_dup(encmsg, struct secret, self_id); + + return enctlv_from_obs2_encmsg(ctx, blinding, final_node, encmsg, + &unused_next_blinding, node_alias); +} diff --git a/common/blindedpath.h b/common/blindedpath.h index d4adb7bec6b0..f3415939551f 100644 --- a/common/blindedpath.h +++ b/common/blindedpath.h @@ -10,13 +10,6 @@ struct pubkey; struct privkey; struct secret; -/* Fills in *initial_blinding and *final_blinding and returns - * onionmsg_path array for this route */ -struct onionmsg_path **make_blindedpath(const tal_t *ctx, - const struct pubkey *route, - struct pubkey *initial_blinding, - struct pubkey *final_blinding); - /** * create_enctlv - Encrypt an encmsg to form an enctlv. * @ctx: tal context @@ -24,7 +17,7 @@ struct onionmsg_path **make_blindedpath(const tal_t *ctx, * @node: the pubkey of the node to encrypt for * @next_node: the pubkey of the next node, to place in enctlv * @padlen: if non-zero, the bytes of padding to add (also adds 2 byte padding hdr) - * @override_blinding: the optional blinding point to place in enctlv + * @next_blinding_override: the optional blinding point to place in enctlv * @next_blinding: (out) e(i+1), the next blinding secret. * @node_alias: (out) the blinded pubkey of the node to tell the recipient. * @@ -35,7 +28,7 @@ u8 *create_enctlv(const tal_t *ctx, const struct pubkey *node, const struct pubkey *next_node, size_t padlen, - const struct pubkey *override_blinding, + const struct pubkey *next_blinding_override, struct privkey *next_blinding, struct pubkey *node_alias) NON_NULL_ARGS(2, 3, 4, 7, 8); @@ -46,7 +39,7 @@ u8 *create_enctlv(const tal_t *ctx, * @blinding: e(i), the blinding secret * @final_node: the pubkey of the node to encrypt for * @padlen: if non-zero, the bytes of padding to add (also adds 2 byte padding hdr) - * @self_id: secret to include in enctlv, if not NULL. + * @path_id: secret to include in enctlv, if not NULL. * @node_alias: (out) the blinded pubkey of the node to tell the recipient. * * If it fails, it means one of the privkeys is bad. @@ -55,7 +48,7 @@ u8 *create_final_enctlv(const tal_t *ctx, const struct privkey *blinding, const struct pubkey *final_node, size_t padlen, - const struct secret *self_id, + const struct secret *path_id, struct pubkey *node_alias) NON_NULL_ARGS(2, 3, 6); @@ -93,13 +86,13 @@ bool decrypt_enctlv(const struct pubkey *blinding, /** * decrypt_final_enctlv - Decrypt an encmsg to form an enctlv. - * @ctx: tal context for @self_id + * @ctx: tal context for @path_id * @blinding: E(i), the blinding pubkey the previous peer gave us. * @ss: the blinding secret from unblind_onion(). * @enctlv: the enctlv from the onion (tal, may be NULL). * @my_id: the pubkey of this node. * @alias: (out) the node_id this was addressed to. - * @self_id: (out) the secret contained in the enctlv, if any. + * @path_id: (out) the secret contained in the enctlv, if any (NULL if invalid or unset) * * Returns false if decryption failed or encmsg was malformed. */ @@ -109,7 +102,39 @@ bool decrypt_final_enctlv(const tal_t *ctx, const u8 *enctlv, const struct pubkey *my_id, struct pubkey *alias, - struct secret **self_id) + struct secret **path_id) + NON_NULL_ARGS(1, 2, 4, 5); + +/* Obsolete variants */ +u8 *create_obs2_enctlv(const tal_t *ctx, + const struct privkey *blinding, + const struct pubkey *node, + const struct pubkey *next_node, + size_t padlen, + const struct pubkey *override_blinding, + struct privkey *next_blinding, + struct pubkey *node_alias) + NON_NULL_ARGS(2, 3, 4, 7, 8); +u8 *create_obs2_final_enctlv(const tal_t *ctx, + const struct privkey *blinding, + const struct pubkey *final_node, + size_t padlen, + const struct secret *self_id, + struct pubkey *node_alias) + NON_NULL_ARGS(2, 3, 6); +bool decrypt_obs2_enctlv(const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv, + struct pubkey *next_node, + struct pubkey *next_blinding) + NON_NULL_ARGS(1, 2, 4, 5); +bool decrypt_obs2_final_enctlv(const tal_t *ctx, + const struct pubkey *blinding, + const struct secret *ss, + const u8 *enctlv, + const struct pubkey *my_id, + struct pubkey *alias, + struct secret **self_id) NON_NULL_ARGS(1, 2, 4, 5); #endif /* LIGHTNING_COMMON_BLINDEDPATH_H */ diff --git a/common/bolt12.c b/common/bolt12.c index 0c95c732e611..5ab64f31e1c6 100644 --- a/common/bolt12.c +++ b/common/bolt12.c @@ -8,11 +8,11 @@ #include #include -bool bolt12_chains_match(const struct bitcoin_blkid *chains, - const struct chainparams *must_be_chain) +/* If chains is NULL, max_num_chains is ignored */ +static bool bolt12_chains_match(const struct bitcoin_blkid *chains, + size_t max_num_chains, + const struct chainparams *must_be_chain) { - size_t num_chains; - /* BOLT-offers #12: * - if the chain for the invoice is not solely bitcoin: * - MUST specify `chains` the offer is valid for. @@ -22,23 +22,17 @@ bool bolt12_chains_match(const struct bitcoin_blkid *chains, /* BOLT-offers #12: * The reader of an invoice_request: *... - * - MUST fail the request if `chains` does not include (or - * imply) a supported chain. - */ - /* BOLT-offers #12: - * - * - if the chain for the invoice is not solely bitcoin: - * - MUST specify `chains` the invoice is valid for. - * - otherwise: - * - the bitcoin chain is implied as the first and only entry. + * - if `chain` is not present: + * - MUST fail the request if bitcoin is not a supported chain. + * - otherwise: + * - MUST fail the request if `chain` is not a supported chain. */ - num_chains = tal_count(chains); - if (num_chains == 0) { - num_chains = 1; + if (!chains) { + max_num_chains = 1; chains = &chainparams_for_network("bitcoin")->genesis_blockhash; } - for (size_t i = 0; i < num_chains; i++) { + for (size_t i = 0; i < max_num_chains; i++) { if (bitcoin_blkid_eq(&chains[i], &must_be_chain->genesis_blockhash)) return true; @@ -48,28 +42,20 @@ bool bolt12_chains_match(const struct bitcoin_blkid *chains, } bool bolt12_chain_matches(const struct bitcoin_blkid *chain, - const struct chainparams *must_be_chain, - const struct bitcoin_blkid *deprecated_chains) + const struct chainparams *must_be_chain) { - /* Obsolete: We used to put an array in here, but we only ever - * used a single value */ - if (deprecated_apis && !chain) - chain = deprecated_chains; - - if (!chain) - chain = &chainparams_for_network("bitcoin")->genesis_blockhash; - - return bitcoin_blkid_eq(chain, &must_be_chain->genesis_blockhash); + return bolt12_chains_match(chain, 1, must_be_chain); } static char *check_features_and_chain(const tal_t *ctx, const struct feature_set *our_features, const struct chainparams *must_be_chain, const u8 *features, - const struct bitcoin_blkid *chains) + const struct bitcoin_blkid *chains, + size_t num_chains) { if (must_be_chain) { - if (!bolt12_chains_match(chains, must_be_chain)) + if (!bolt12_chains_match(chains, num_chains, must_be_chain)) return tal_fmt(ctx, "wrong chain"); } @@ -195,7 +181,8 @@ struct tlv_offer *offer_decode(const tal_t *ctx, *fail = check_features_and_chain(ctx, our_features, must_be_chain, offer->features, - offer->chains); + offer->chains, + tal_count(offer->chains)); if (*fail) return tal_free(offer); @@ -247,9 +234,7 @@ struct tlv_invoice_request *invrequest_decode(const tal_t *ctx, *fail = check_features_and_chain(ctx, our_features, must_be_chain, invrequest->features, - invrequest->chain - ? invrequest->chain - : invrequest->chains); + invrequest->chain, 1); if (*fail) return tal_free(invrequest); @@ -288,8 +273,7 @@ struct tlv_invoice *invoice_decode_nosig(const tal_t *ctx, *fail = check_features_and_chain(ctx, our_features, must_be_chain, invoice->features, - invoice->chain - ? invoice->chain : invoice->chains); + invoice->chain, 1); if (*fail) return tal_free(invoice); @@ -340,7 +324,7 @@ static u64 time_change(u64 prevstart, u32 number, u64 offer_period_start(u64 basetime, size_t n, const struct tlv_offer_recurrence *recur) { - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * 1. A `time_unit` defining 0 (seconds), 1 (days), 2 (months), * 3 (years). */ @@ -365,13 +349,13 @@ void offer_period_paywindow(const struct tlv_offer_recurrence *recurrence, u64 basetime, u64 period_idx, u64 *start, u64 *end) { - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - if the offer contains `recurrence_paywindow`: */ if (recurrence_paywindow) { u64 pstart = offer_period_start(basetime, period_idx, recurrence); - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - if the offer has a `recurrence_basetime` or the * `recurrence_counter` is non-zero: * - SHOULD NOT send an `invoice_request` for a period prior to @@ -389,7 +373,7 @@ void offer_period_paywindow(const struct tlv_offer_recurrence *recurrence, && recurrence_paywindow->seconds_after < 60) *end = pstart + 60; } else { - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - otherwise: * - SHOULD NOT send an `invoice_request` with * `recurrence_counter` is non-zero for a period whose @@ -401,7 +385,7 @@ void offer_period_paywindow(const struct tlv_offer_recurrence *recurrence, *start = offer_period_start(basetime, period_idx-1, recurrence); - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - SHOULD NOT send an `invoice_request` for a period which * has already passed. */ diff --git a/common/bolt12.h b/common/bolt12.h index e1b0c00e71bf..e451c2897685 100644 --- a/common/bolt12.h +++ b/common/bolt12.h @@ -99,14 +99,9 @@ bool bolt12_check_signature(const struct tlv_field *fields, const struct point32 *key, const struct bip340sig *sig); -/* Given a tal_arr of chains, does it contain this chain? */ -bool bolt12_chains_match(const struct bitcoin_blkid *chains, - const struct chainparams *must_be_chain); - /* Given a single bolt12 chain, does it match? (NULL == bitcoin) */ bool bolt12_chain_matches(const struct bitcoin_blkid *chain, - const struct chainparams *must_be_chain, - const struct bitcoin_blkid *deprecated_chains); + const struct chainparams *must_be_chain); /* Given a basetime, when does period N start? */ u64 offer_period_start(u64 basetime, size_t n, diff --git a/common/bolt12_merkle.c b/common/bolt12_merkle.c index 2865b7e5528b..d9085f86c8ee 100644 --- a/common/bolt12_merkle.c +++ b/common/bolt12_merkle.c @@ -54,7 +54,7 @@ static void h_simpletag_ctx(struct sha256_ctx *sctx, const char *tag) /* 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. + * 2. The H(`LnAll`||all-tlvs,tlv) where "all-tlvs" consists of all non-signature TLV entries appended in ascending order. */ /* Create a sha256_ctx which has the tag part done. */ @@ -107,7 +107,7 @@ static void calc_lnleaf(const struct tlv_field *field, struct sha256 *hash) } /* BOLT-offers #12: - * The Merkle tree inner nodes are H(`LnBranch`, lesser-SHA256|greater-SHA256); + * 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) @@ -200,11 +200,11 @@ void merkle_tlv(const struct tlv_field *fields, struct sha256 *merkle) * * Each form is signed using one or more TLV signature elements; TLV * types 240 through 1000 are considered signature elements. For these - * the tag is `lightning` | `messagename` | `fieldname`, and `msg` is the - * Merkle-root; `lightning` is the literal 9-byte ASCII string, - * `messagename` is the name of the TLV stream being signed (i.e. `offer`, - * `invoice_request` or `invoice`) and the `fieldname` is the TLV field - * containing the signature (e.g. `signature` or `payer_signature`). + * the tag is "lightning" || `messagename` || `fieldname`, and `msg` is the + * Merkle-root; "lightning" is the literal 9-byte ASCII string, + * `messagename` is the name of the TLV stream being signed (i.e. "offer", + * "invoice_request" or "invoice") and the `fieldname` is the TLV field + * containing the signature (e.g. "signature" or "payer_signature"). */ void sighash_from_merkle(const char *messagename, const char *fieldname, diff --git a/common/features.c b/common/features.c index c91ab95039e1..f84375989990 100644 --- a/common/features.c +++ b/common/features.c @@ -69,7 +69,7 @@ static const struct feature_style feature_styles[] = { { OPT_ONION_MESSAGES, .copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT, [NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT, - [BOLT11_FEATURE] = FEATURE_REPRESENT, + [BOLT11_FEATURE] = FEATURE_DONT_REPRESENT, [CHANNEL_FEATURE] = FEATURE_DONT_REPRESENT} }, { OPT_SHUTDOWN_WRONG_FUNDING, .copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT, diff --git a/common/features.h b/common/features.h index 4bb2a78ec5b1..0bb9dfea95af 100644 --- a/common/features.h +++ b/common/features.h @@ -135,9 +135,9 @@ const char *fmt_featurebits(const tal_t *ctx, const u8 *featurebits); */ #define OPT_QUIESCE 34 -/* BOLT-1ede04a1a3225581e265b3ce96984ba88253a4a4 #9: +/* BOLT-offers #9: * - * | 38/39 | `option_onion_messages` |... INC+ ... + * | 38/39 | `option_onion_messages` |... IN ... */ #define OPT_ONION_MESSAGES 38 diff --git a/common/json_helpers.c b/common/json_helpers.c index bcde33a6162f..ee7f235318d6 100644 --- a/common/json_helpers.c +++ b/common/json_helpers.c @@ -141,6 +141,42 @@ struct wally_psbt *json_to_psbt(const tal_t *ctx, const char *buffer, return psbt_from_b64(ctx, buffer + tok->start, tok->end - tok->start); } +struct tlv_obs2_onionmsg_payload_reply_path * +json_to_obs2_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok) +{ + struct tlv_obs2_onionmsg_payload_reply_path *rpath; + const jsmntok_t *hops, *t; + size_t i; + const char *err; + + rpath = tal(ctx, struct tlv_obs2_onionmsg_payload_reply_path); + err = json_scan(tmpctx, buffer, tok, "{blinding:%,first_node_id:%}", + JSON_SCAN(json_to_pubkey, &rpath->blinding), + JSON_SCAN(json_to_pubkey, &rpath->first_node_id), + NULL); + if (err) + return tal_free(rpath); + + hops = json_get_member(buffer, tok, "hops"); + if (!hops || hops->size < 1) + return tal_free(rpath); + + rpath->path = tal_arr(rpath, struct onionmsg_path *, hops->size); + json_for_each_arr(i, t, hops) { + rpath->path[i] = tal(rpath->path, struct onionmsg_path); + err = json_scan(tmpctx, buffer, t, "{id:%,encrypted_recipient_data:%}", + JSON_SCAN(json_to_pubkey, + &rpath->path[i]->node_id), + JSON_SCAN_TAL(rpath->path[i], + json_tok_bin_from_hex, + &rpath->path[i]->encrypted_recipient_data)); + if (err) + return tal_free(rpath); + } + + return rpath; +} + struct tlv_onionmsg_payload_reply_path * json_to_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok) { @@ -164,12 +200,12 @@ json_to_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok) rpath->path = tal_arr(rpath, struct onionmsg_path *, hops->size); json_for_each_arr(i, t, hops) { rpath->path[i] = tal(rpath->path, struct onionmsg_path); - err = json_scan(tmpctx, buffer, t, "{id:%,enctlv:%}", + err = json_scan(tmpctx, buffer, t, "{id:%,encrypted_recipient_data:%}", JSON_SCAN(json_to_pubkey, &rpath->path[i]->node_id), JSON_SCAN_TAL(rpath->path[i], json_tok_bin_from_hex, - &rpath->path[i]->enctlv)); + &rpath->path[i]->encrypted_recipient_data)); if (err) return tal_free(rpath); } diff --git a/common/json_helpers.h b/common/json_helpers.h index e3001a0e7b16..3e10642404a2 100644 --- a/common/json_helpers.h +++ b/common/json_helpers.h @@ -83,6 +83,10 @@ bool split_tok(const char *buffer, const jsmntok_t *tok, struct tlv_onionmsg_payload_reply_path * json_to_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok); +/* Obsolete version! */ +struct tlv_obs2_onionmsg_payload_reply_path * +json_to_obs2_reply_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok); + /* Helpers for outputting JSON results */ /* '"fieldname" : "0289abcdef..."' or "0289abcdef..." if fieldname is NULL */ diff --git a/common/onion.c b/common/onion.c index 4743117da2aa..aeab17de1a53 100644 --- a/common/onion.c +++ b/common/onion.c @@ -82,8 +82,8 @@ u8 *onion_nonfinal_hop(const tal_t *ctx, tlv->short_channel_id = cast_const(struct short_channel_id *, scid); #if EXPERIMENTAL_FEATURES - tlv->blinding_seed = cast_const(struct pubkey *, blinding); - tlv->enctlv = cast_const(u8 *, enctlv); + tlv->blinding_point = cast_const(struct pubkey *, blinding); + tlv->encrypted_recipient_data = cast_const(u8 *, enctlv); #endif return make_tlv_hop(ctx, tlv); } else { @@ -135,8 +135,8 @@ u8 *onion_final_hop(const tal_t *ctx, tlv->payment_data = &tlv_pdata; } #if EXPERIMENTAL_FEATURES - tlv->blinding_seed = cast_const(struct pubkey *, blinding); - tlv->enctlv = cast_const(u8 *, enctlv); + tlv->blinding_point = cast_const(struct pubkey *, blinding); + tlv->encrypted_recipient_data = cast_const(u8 *, enctlv); #endif return make_tlv_hop(ctx, tlv); } else { @@ -362,10 +362,10 @@ struct onion_payload *onion_decode(const tal_t *ctx, #if EXPERIMENTAL_FEATURES if (!p->blinding) { /* If we have no blinding, it could be in TLV. */ - if (tlv->blinding_seed) { + if (tlv->blinding_point) { p->blinding = tal_dup(p, struct pubkey, - tlv->blinding_seed); + tlv->blinding_point); ecdh(p->blinding, &p->blinding_ss); } } else @@ -377,12 +377,12 @@ struct onion_payload *onion_decode(const tal_t *ctx, if (rs->nextcase == ONION_FORWARD) { struct tlv_tlv_payload *ntlv; - if (!tlv->enctlv) + if (!tlv->encrypted_recipient_data) goto fail; ntlv = decrypt_tlv(tmpctx, &p->blinding_ss, - tlv->enctlv); + tlv->encrypted_recipient_data); if (!ntlv) goto fail; diff --git a/common/test/Makefile b/common/test/Makefile index e6644f3744ef..d441bfaa74e2 100644 --- a/common/test/Makefile +++ b/common/test/Makefile @@ -20,7 +20,9 @@ ALL_TEST_PROGRAMS += $(COMMON_TEST_PROGRAMS) # Sphinx test wants to decode TLVs. common/test/run-sphinx: wire/onion$(EXP)_wiregen.o wire/towire.o wire/fromwire.o -common/test/run-blindedpath_enctlv: wire/onion$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o +common/test/run-blindedpath_enctlv common/test/run-blindedpath_onion: wire/onion$(EXP)_wiregen.o wire/peer$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o +common/test/run-route_blinding_test: wire/onion$(EXP)_wiregen.o wire/peer$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o common/json.o common/json_helpers.o +common/test/run-route_blinding_override_test: wire/onion$(EXP)_wiregen.o wire/peer$(EXP)_wiregen.o wire/towire.o wire/fromwire.o wire/tlvstream.o common/json.o common/json_helpers.o common/test/run-param \ common/test/run-json: \ diff --git a/common/test/run-blindedpath_enctlv.c b/common/test/run-blindedpath_enctlv.c index ec735ec10eb8..f0e0a36d902e 100644 --- a/common/test/run-blindedpath_enctlv.c +++ b/common/test/run-blindedpath_enctlv.c @@ -2,6 +2,7 @@ #include "../blinding.c" #include "../hmac.c" #include "../type_to_string.c" +#include #include #include @@ -47,12 +48,25 @@ struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max /* Generated stub for fromwire_amount_sat */ struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_amount_sat 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_node_id */ +void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) +{ fprintf(stderr, "fromwire_node_id called!\n"); abort(); } /* Generated stub for towire_amount_msat */ void towire_amount_msat(u8 **pptr UNNEEDED, const struct amount_msat msat UNNEEDED) { fprintf(stderr, "towire_amount_msat called!\n"); abort(); } /* Generated stub for towire_amount_sat */ void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) { fprintf(stderr, "towire_amount_sat 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_node_id */ +void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) +{ fprintf(stderr, "towire_node_id called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ static void json_strfield(const char *name, const char *val) @@ -107,7 +121,7 @@ static void test_final_decrypt(const struct pubkey *blinding, pubkey_from_privkey(me, &my_pubkey); assert(unblind_onion(blinding, test_ecdh, &dummy, &ss)); assert(decrypt_final_enctlv(tmpctx, blinding, &ss, enctlv, &my_pubkey, - &alias, &self_id)); + &alias, &self_id)); assert(pubkey_eq(&alias, expected_alias)); assert(secret_eq_consttime(self_id, expected_self_id)); @@ -136,7 +150,7 @@ int main(int argc, char *argv[]) /* We output the JSON test vectors. */ printf("[{"); - json_strfield("test name", "Simple enctlv for Alice, next is Bob"); + json_strfield("test name", "Simple encrypted_recipient_data for Alice, next is Bob"); json_strfield("node_privkey", type_to_string(tmpctx, struct privkey, &alice)); json_strfield("node_id", @@ -145,14 +159,14 @@ int main(int argc, char *argv[]) type_to_string(tmpctx, struct privkey, &blinding)); json_strfield("blinding", type_to_string(tmpctx, struct pubkey, &blinding_pub)); - printf("\t\"encmsg\": {\n" + printf("\t\"encrypted_data_tlv\": {\n" "\t\t\"next_node_id\": \"%s\"\n" "\t},\n", type_to_string(tmpctx, struct pubkey, &bob_id)); enctlv = create_enctlv(tmpctx, &blinding, &alice_id, &bob_id, 0, NULL, &blinding, &alias); - printf("\t\"enctlv_hex\": \"%s\"\n" + printf("\t\"encrypted_recipient_data_hex\": \"%s\"\n" "},\n", tal_hex(tmpctx, enctlv)); @@ -164,7 +178,7 @@ int main(int argc, char *argv[]) printf("{"); json_strfield("test name", - "Blinding-key-override enctlv for Bob, next is Carol"); + "Blinding-key-override encrypted_recipient_data for Bob, next is Carol"); json_strfield("node_privkey", type_to_string(tmpctx, struct privkey, &bob)); json_strfield("node_id", @@ -173,7 +187,7 @@ int main(int argc, char *argv[]) type_to_string(tmpctx, struct privkey, &blinding)); json_strfield("blinding", type_to_string(tmpctx, struct pubkey, &blinding_pub)); - printf("\t\"encmsg\": {\n" + printf("\t\"encrypted_data_tlv\": {\n" "\t\t\"next_node_id\": \"%s\",\n" "\t\t\"blinding\": \"%s\"\n" "\t},\n", @@ -182,7 +196,7 @@ int main(int argc, char *argv[]) enctlv = create_enctlv(tmpctx, &blinding, &bob_id, &carol_id, 0, &override_blinding_pub, &blinding, &alias); - printf("\t\"enctlv_hex\": \"%s\"\n" + printf("\t\"encrypted_recipient_data_hex\": \"%s\"\n" "},\n", tal_hex(tmpctx, enctlv)); @@ -193,7 +207,7 @@ int main(int argc, char *argv[]) blinding_pub = override_blinding_pub; printf("{"); - json_strfield("test name", "Padded enctlv for Carol, next is Dave"); + json_strfield("test name", "Padded encrypted_recipient_data for Carol, next is Dave"); json_strfield("node_privkey", type_to_string(tmpctx, struct privkey, &carol)); json_strfield("node_id", @@ -202,7 +216,7 @@ int main(int argc, char *argv[]) type_to_string(tmpctx, struct privkey, &blinding)); json_strfield("blinding", type_to_string(tmpctx, struct pubkey, &blinding_pub)); - printf("\t\"encmsg\": {\n" + printf("\t\"encrypted_data_tlv\": {\n" "\t\t\"next_node_id\": \"%s\",\n" "\t\t\"padding\": \"%s\"\n" "\t},\n", @@ -211,19 +225,38 @@ int main(int argc, char *argv[]) enctlv = create_enctlv(tmpctx, &blinding, &carol_id, &dave_id, 35, NULL, &blinding, &alias); - printf("\t\"enctlv_hex\": \"%s\"\n" - "}]\n", + printf("\t\"encrypted_recipient_data_hex\": \"%s\"\n" + "},\n", tal_hex(tmpctx, enctlv)); test_decrypt(&blinding_pub, enctlv, &carol, &dave_id, &blinding); - /* FIXME: Add this to the test vectors! */ - fclose(stdout); - memset(&self_id, 0x77, sizeof(self_id)); + for (size_t i = 0; i < sizeof(self_id); i++) + self_id.data[i] = i+1; + printf("{"); + json_strfield("test name", "Final enctlv for Dave"); + json_strfield("node_privkey", + type_to_string(tmpctx, struct privkey, &dave)); + json_strfield("node_id", + type_to_string(tmpctx, struct pubkey, &dave_id)); + json_strfield("blinding_secret", + type_to_string(tmpctx, struct privkey, &blinding)); + json_strfield("blinding", + type_to_string(tmpctx, struct pubkey, &blinding_pub)); + printf("\t\"encrypted_data_tlv\": {\n" + "\t\t\"self_id\": \"%s\"\n" + "\t},\n", + type_to_string(tmpctx, struct secret, &self_id)); + enctlv = create_final_enctlv(tmpctx, &blinding, &dave_id, 0, &self_id, &alias); + printf("\t\"encrypted_recipient_data_hex\": \"%s\"\n", + tal_hex(tmpctx, enctlv)); + + printf("}]\n"); pubkey_from_privkey(&blinding, &blinding_pub); + test_final_decrypt(&blinding_pub, enctlv, &dave, &alias, &self_id); common_shutdown(); } diff --git a/common/test/run-blindedpath_onion.c b/common/test/run-blindedpath_onion.c new file mode 100644 index 000000000000..f20058b8183a --- /dev/null +++ b/common/test/run-blindedpath_onion.c @@ -0,0 +1,254 @@ +/* Pipe through jq for test vector! */ +#include "../bigsize.c" +#include "../blindedpath.c" +#include "../blinding.c" +#include "../hmac.c" +#include "../onion.c" +#include "../sphinx.c" +#include "../type_to_string.c" +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for amount_asset_is_main */ +bool amount_asset_is_main(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_is_main called!\n"); abort(); } +/* Generated stub for amount_asset_to_sat */ +struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_to_sat called!\n"); abort(); } +/* Generated stub for amount_msat */ +struct amount_msat amount_msat(u64 millisatoshis UNNEEDED) +{ fprintf(stderr, "amount_msat called!\n"); abort(); } +/* Generated stub for amount_msat_eq */ +bool amount_msat_eq(struct amount_msat a UNNEEDED, struct amount_msat b UNNEEDED) +{ fprintf(stderr, "amount_msat_eq called!\n"); abort(); } +/* Generated stub for amount_sat */ +struct amount_sat amount_sat(u64 satoshis UNNEEDED) +{ fprintf(stderr, "amount_sat called!\n"); abort(); } +/* Generated stub for amount_sat_add */ + bool amount_sat_add(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_add called!\n"); abort(); } +/* Generated stub for amount_sat_eq */ +bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_greater_eq */ +bool amount_sat_greater_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_greater_eq called!\n"); abort(); } +/* Generated stub for amount_sat_sub */ + bool amount_sat_sub(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_sub called!\n"); abort(); } +/* Generated stub for amount_sat_to_asset */ +struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u8 *asset UNNEEDED) +{ fprintf(stderr, "amount_sat_to_asset called!\n"); abort(); } +/* Generated stub for amount_tx_fee */ +struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) +{ fprintf(stderr, "amount_tx_fee called!\n"); abort(); } +/* Generated stub for fromwire_amount_msat */ +struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_amount_msat called!\n"); abort(); } +/* Generated stub for fromwire_amount_sat */ +struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_amount_sat 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_node_id */ +void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) +{ fprintf(stderr, "fromwire_node_id called!\n"); abort(); } +/* Generated stub for new_onionreply */ +struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED) +{ fprintf(stderr, "new_onionreply called!\n"); abort(); } +/* Generated stub for pubkey_from_node_id */ +bool pubkey_from_node_id(struct pubkey *key UNNEEDED, const struct node_id *id UNNEEDED) +{ fprintf(stderr, "pubkey_from_node_id called!\n"); abort(); } +/* Generated stub for towire_amount_msat */ +void towire_amount_msat(u8 **pptr UNNEEDED, const struct amount_msat msat UNNEEDED) +{ fprintf(stderr, "towire_amount_msat called!\n"); abort(); } +/* Generated stub for towire_amount_sat */ +void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) +{ fprintf(stderr, "towire_amount_sat 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_node_id */ +void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) +{ fprintf(stderr, "towire_node_id called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +static void json_strfield(const char *name, const char *val) +{ + printf("\t\"%s\": \"%s\",\n", name, val); +} + +#define ALICE 0 +#define BOB 1 +#define CAROL 2 +#define DAVE 3 + +/* Updated each time, as we pretend to be Alice, Bob, Carol */ +static const struct privkey *ecdh_key; + +void ecdh(const struct pubkey *point, struct secret *ss) +{ + if (secp256k1_ecdh(secp256k1_ctx, ss->data, &point->pubkey, + ecdh_key->secret.data, NULL, NULL) != 1) + abort(); +} + +static u8 *next_onion(const tal_t *ctx, u8 *omsg, + const struct privkey *nodekey, + const struct pubkey *expected_blinding) +{ + struct onionpacket *op; + struct pubkey blinding, ephemeral; + struct pubkey next_node, next_blinding; + struct tlv_onionmsg_payload *om; + struct secret ss, onion_ss; + const u8 *cursor; + size_t max, maxlen; + struct route_step *rs; + + assert(fromwire_onion_message(tmpctx, omsg, &blinding, &omsg)); + assert(pubkey_eq(&blinding, expected_blinding)); + + op = parse_onionpacket(tmpctx, omsg, tal_bytelen(omsg), NULL); + ephemeral = op->ephemeralkey; + + ecdh_key = nodekey; + assert(unblind_onion(&blinding, ecdh, &ephemeral, &ss)); + + ecdh(&ephemeral, &onion_ss); + rs = process_onionpacket(tmpctx, op, &onion_ss, NULL, 0, false); + assert(rs); + + /* The raw payload is prepended with length in the modern onion. */ + cursor = rs->raw_payload; + max = tal_bytelen(rs->raw_payload); + maxlen = fromwire_bigsize(&cursor, &max); + om = tlv_onionmsg_payload_new(tmpctx); + assert(fromwire_onionmsg_payload(&cursor, &maxlen, om)); + + if (rs->nextcase == ONION_END) + return NULL; + + assert(decrypt_enctlv(&blinding, &ss, om->encrypted_data_tlv, &next_node, + &next_blinding)); + return towire_onion_message(ctx, &next_blinding, + serialize_onionpacket(tmpctx, rs->next)); +} + +int main(int argc, char *argv[]) +{ + struct privkey nodekey[4], blinding[4], override_blinding; + struct pubkey id[4], blinding_pub[4], override_blinding_pub, alias[4]; + struct secret self_id; + u8 *enctlv[4]; + u8 *onionmsg_payload[4]; + u8 *omsg; + struct sphinx_path *sphinx_path; + struct secret *path_secrets; + struct onionpacket *op; + const char *names[] = {"Alice", "Bob", "Carol", "Dave"}; + + common_setup(argv[0]); + + for (size_t i = 0; i < 4; i++) { + memset(&nodekey[i], names[i][0], sizeof(nodekey[i])); + pubkey_from_privkey(&nodekey[i], &id[i]); + } + + /* Make enctlvs as per enctlv test vectors */ + memset(&blinding[ALICE], 5, sizeof(blinding[ALICE])); + pubkey_from_privkey(&blinding[ALICE], &blinding_pub[ALICE]); + + enctlv[ALICE] = create_enctlv(tmpctx, &blinding[ALICE], + &id[ALICE], &id[BOB], + 0, NULL, &blinding[BOB], &alias[ALICE]); + + pubkey_from_privkey(&blinding[BOB], &blinding_pub[BOB]); + + /* We override blinding for Carol. */ + memset(&override_blinding, 7, sizeof(override_blinding)); + pubkey_from_privkey(&override_blinding, &override_blinding_pub); + enctlv[BOB] = create_enctlv(tmpctx, &blinding[BOB], + &id[BOB], &id[CAROL], + 0, &override_blinding_pub, + &blinding[CAROL], &alias[BOB]); + + /* That replaced the blinding */ + blinding[CAROL] = override_blinding; + blinding_pub[CAROL] = override_blinding_pub; + + enctlv[CAROL] = create_enctlv(tmpctx, &blinding[CAROL], + &id[CAROL], &id[DAVE], + 35, NULL, &blinding[DAVE], &alias[CAROL]); + + for (size_t i = 0; i < sizeof(self_id); i++) + self_id.data[i] = i+1; + + enctlv[DAVE] = create_final_enctlv(tmpctx, &blinding[DAVE], &id[DAVE], + 0, &self_id, &alias[DAVE]); + pubkey_from_privkey(&blinding[DAVE], &blinding_pub[DAVE]); + + /* Create an onion which encodes this. */ + sphinx_path = sphinx_path_new(tmpctx, NULL); + for (size_t i = 0; i < 4; i++) { + struct tlv_onionmsg_payload *payload + = tlv_onionmsg_payload_new(tmpctx); + payload->encrypted_data_tlv = enctlv[i]; + onionmsg_payload[i] = tal_arr(tmpctx, u8, 0); + towire_onionmsg_payload(&onionmsg_payload[i], payload); + sphinx_add_modern_hop(sphinx_path, &alias[i], + onionmsg_payload[i]); + } + op = create_onionpacket(tmpctx, sphinx_path, ROUTING_INFO_SIZE, + &path_secrets); + + /* And finally, the onion message as handed to Alice */ + omsg = towire_onion_message(tmpctx, &blinding_pub[ALICE], + serialize_onionpacket(tmpctx, op)); + + printf("{\n"); + json_strfield("description", + "Onion message encoding enctlv as per enctlvs.json"); + printf("\t\"onionmsgs\": ["); + for (size_t i = 0; i < 4; i++) { + printf("{\n"); + json_strfield("node name", names[i]); + json_strfield("node_secret", + type_to_string(tmpctx, struct privkey, + &nodekey[i])); + json_strfield("node_id", + type_to_string(tmpctx, struct pubkey, &id[i])); + + printf("\t\"onion_message\": {"); + json_strfield("raw", tal_hex(tmpctx, omsg)); + json_strfield("blinding_secret", + type_to_string(tmpctx, struct privkey, + &blinding[i])); + json_strfield("blinding", + type_to_string(tmpctx, struct pubkey, &blinding_pub[i])); + json_strfield("blinded_alias", + type_to_string(tmpctx, struct pubkey, &alias[i])); + json_strfield("onionmsg_payload", + tal_hex(tmpctx, onionmsg_payload[i])); + printf("\"enctlv\": \"%s\"}\n", tal_hex(tmpctx, enctlv[i])); + + printf("}"); + if (i != DAVE) + printf(",\n"); + + /* Unwrap for next hop */ + omsg = next_onion(tmpctx, omsg, &nodekey[i], &blinding_pub[i]); + } + assert(!omsg); + printf("\n]}\n"); + + common_shutdown(); +} diff --git a/common/test/run-route_blinding_override_test.c b/common/test/run-route_blinding_override_test.c new file mode 100644 index 000000000000..5025c8ff7d2f --- /dev/null +++ b/common/test/run-route_blinding_override_test.c @@ -0,0 +1,366 @@ +#include "config.h" +#include "../bigsize.c" +#include "../blindedpath.c" +#include "../blinding.c" +#include "../hmac.c" +#include "../type_to_string.c" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for amount_asset_is_main */ +bool amount_asset_is_main(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_is_main called!\n"); abort(); } +/* Generated stub for amount_asset_to_sat */ +struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_to_sat called!\n"); abort(); } +/* Generated stub for amount_msat */ +struct amount_msat amount_msat(u64 millisatoshis UNNEEDED) +{ fprintf(stderr, "amount_msat called!\n"); abort(); } +/* Generated stub for amount_sat */ +struct amount_sat amount_sat(u64 satoshis UNNEEDED) +{ fprintf(stderr, "amount_sat called!\n"); abort(); } +/* Generated stub for amount_sat_add */ + bool amount_sat_add(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_add called!\n"); abort(); } +/* Generated stub for amount_sat_eq */ +bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_greater_eq */ +bool amount_sat_greater_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_greater_eq called!\n"); abort(); } +/* Generated stub for amount_sat_sub */ + bool amount_sat_sub(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_sub called!\n"); abort(); } +/* Generated stub for amount_sat_to_asset */ +struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u8 *asset UNNEEDED) +{ fprintf(stderr, "amount_sat_to_asset called!\n"); abort(); } +/* Generated stub for amount_sat_to_msat */ + bool amount_sat_to_msat(struct amount_msat *msat UNNEEDED, + struct amount_sat sat UNNEEDED) +{ fprintf(stderr, "amount_sat_to_msat called!\n"); abort(); } +/* Generated stub for amount_tx_fee */ +struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) +{ fprintf(stderr, "amount_tx_fee called!\n"); abort(); } +/* Generated stub for fmt_wireaddr_without_port */ +char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) +{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } +/* Generated stub for fromwire_amount_msat */ +struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_amount_msat called!\n"); abort(); } +/* Generated stub for fromwire_amount_sat */ +struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_amount_sat 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_node_id */ +void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) +{ fprintf(stderr, "fromwire_node_id called!\n"); abort(); } +/* Generated stub for json_add_member */ +void json_add_member(struct json_stream *js UNNEEDED, + const char *fieldname UNNEEDED, + bool quote UNNEEDED, + const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "json_add_member called!\n"); abort(); } +/* Generated stub for json_member_direct */ +char *json_member_direct(struct json_stream *js UNNEEDED, + const char *fieldname UNNEEDED, size_t extra UNNEEDED) +{ fprintf(stderr, "json_member_direct called!\n"); abort(); } +/* Generated stub for json_object_end */ +void json_object_end(struct json_stream *js UNNEEDED) +{ fprintf(stderr, "json_object_end called!\n"); abort(); } +/* Generated stub for json_object_start */ +void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) +{ fprintf(stderr, "json_object_start called!\n"); abort(); } +/* Generated stub for node_id_from_hexstr */ +bool node_id_from_hexstr(const char *str UNNEEDED, size_t slen UNNEEDED, struct node_id *id UNNEEDED) +{ fprintf(stderr, "node_id_from_hexstr called!\n"); abort(); } +/* Generated stub for parse_amount_msat */ +bool parse_amount_msat(struct amount_msat *msat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED) +{ fprintf(stderr, "parse_amount_msat called!\n"); abort(); } +/* Generated stub for parse_amount_sat */ +bool parse_amount_sat(struct amount_sat *sat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED) +{ fprintf(stderr, "parse_amount_sat called!\n"); abort(); } +/* Generated stub for towire_amount_msat */ +void towire_amount_msat(u8 **pptr UNNEEDED, const struct amount_msat msat UNNEEDED) +{ fprintf(stderr, "towire_amount_msat called!\n"); abort(); } +/* Generated stub for towire_amount_sat */ +void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) +{ fprintf(stderr, "towire_amount_sat 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_node_id */ +void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) +{ fprintf(stderr, "towire_node_id called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +static u8 *json_to_enctlvs(const tal_t *ctx, + const char *buf, const jsmntok_t *tlvs) +{ + struct tlv_encrypted_data_tlv *enctlv = tlv_encrypted_data_tlv_new(tmpctx); + size_t i; + const jsmntok_t *t; + u8 *ret, *appended = tal_arr(tmpctx, u8, 0); + + json_for_each_obj(i, t, tlvs) { + if (json_tok_streq(buf, t, "short_channel_id")) { + enctlv->short_channel_id = tal(enctlv, struct short_channel_id); + assert(json_to_short_channel_id(buf, t+1, + enctlv->short_channel_id)); + } else if (json_tok_streq(buf, t, "padding")) { + enctlv->padding = json_tok_bin_from_hex(enctlv, + buf, t+1); + assert(enctlv->padding); + } else if (json_tok_streq(buf, t, "next_node_id")) { + enctlv->next_node_id = tal(enctlv, struct pubkey); + assert(json_to_pubkey(buf, t+1, + enctlv->next_node_id)); + } else if (json_tok_streq(buf, t, "path_id")) { + enctlv->path_id = json_tok_bin_from_hex(enctlv, + buf, t+1); + assert(enctlv->path_id); + } else if (json_tok_streq(buf, t, "next_blinding_override")) { + enctlv->next_blinding_override = tal(enctlv, struct pubkey); + assert(json_to_pubkey(buf, t+1, + enctlv->next_blinding_override)); + } else { + u16 tagnum; + u8 *val; + assert(json_tok_startswith(buf, t, "unknown_tag_")); + tagnum = atoi(buf + t->start + strlen("unknown_tag_")); + assert(tagnum); + val = json_tok_bin_from_hex(enctlv, buf, t+1); + assert(val); + + /* We can't actually represent these in a way towire_ + * will see, so we literally append them */ + towire_bigsize(&appended, tagnum); + towire_bigsize(&appended, tal_bytelen(val)); + towire_u8_array(&appended, val, tal_bytelen(val)); + } + } + ret = tal_arr(ctx, u8, 0); + towire_encrypted_data_tlv(&ret, enctlv); + towire_u8_array(&ret, appended, tal_bytelen(appended)); + return ret; +} + +/* Updated each time, as we pretend to be Alice, Bob, Carol */ +static const struct privkey *mykey; + +static void test_ecdh(const struct pubkey *point, struct secret *ss) +{ + if (secp256k1_ecdh(secp256k1_ctx, ss->data, &point->pubkey, + mykey->secret.data, NULL, NULL) != 1) + abort(); +} + +int main(int argc, char *argv[]) +{ + char *json; + size_t i, num_sender_hops; + jsmn_parser parser; + jsmntok_t toks[5000]; + const jsmntok_t *t, *recip_route_hops, *recip_blinding_hops, + *sender_route_hops, *sender_blinding_hops, *unblinding_hops; + struct pubkey *ids; + u8 **enctlvs, **encrypted_data; + struct privkey blinding; + + common_setup(argv[0]); + + if (argv[1]) + json = grab_file(tmpctx, argv[1]); + else { + char *dir = getenv("BOLTDIR"); + json = grab_file(tmpctx, + path_join(tmpctx, + dir ? dir : "../lightning-rfc", + "bolt04/route-blinding-override-test.json")); + if (!json) { + printf("test file not found, skipping\n"); + goto out; + } + } + + jsmn_init(&parser); + if (jsmn_parse(&parser, json, strlen(json), toks, ARRAY_SIZE(toks)) < 0) + abort(); + + /* We concatenate the sender_route_blinding and the + * recipient_route_blinding to form a contiguous sequence of + * enctlvs */ + recip_route_hops = json_get_member(json, json_get_member(json, toks, "recipient_route"), "hops"); + sender_route_hops = json_get_member(json, json_get_member(json, toks, "sender_route"), "hops"); + recip_blinding_hops = json_get_member(json, json_get_member(json, toks, "recipient_route_blinding"), "hops"); + sender_blinding_hops = json_get_member(json, json_get_member(json, toks, "sender_route_blinding"), "hops"); + unblinding_hops = json_get_member(json, json_get_member(json, toks, "unblinding"), "hops"); + + assert(recip_route_hops->size == recip_blinding_hops->size); + assert(sender_route_hops->size == sender_blinding_hops->size); + num_sender_hops = sender_route_hops->size; + + ids = tal_arr(tmpctx, struct pubkey, + num_sender_hops + recip_route_hops->size); + enctlvs = tal_arr(tmpctx, u8 *, num_sender_hops + recip_route_hops->size); + json_for_each_arr(i, t, sender_route_hops) { + u8 *expected; + assert(json_to_pubkey(json, json_get_member(json, t, "node_id"), + &ids[i])); + enctlvs[i] = json_tok_bin_from_hex(enctlvs, json, + json_get_member(json, t, "encoded_tlvs")); + expected = json_to_enctlvs(tmpctx, json, + json_get_member(json, t, "tlvs")); + assert(memeq(expected, tal_bytelen(expected), + enctlvs[i], tal_bytelen(enctlvs[i]))); + } + + json_for_each_arr(i, t, recip_route_hops) { + u8 *expected; + assert(json_to_pubkey(json, json_get_member(json, t, "node_id"), + &ids[i + num_sender_hops])); + enctlvs[i + num_sender_hops] + = json_tok_bin_from_hex(enctlvs, json, + json_get_member(json, t, "encoded_tlvs")); + expected = json_to_enctlvs(tmpctx, json, + json_get_member(json, t, "tlvs")); + assert(memeq(expected, tal_bytelen(expected), + enctlvs[i + num_sender_hops], + tal_bytelen(enctlvs[i + num_sender_hops]))); + } + + encrypted_data = tal_arr(tmpctx, u8 *, + num_sender_hops + recip_route_hops->size); + + /* Now do the blinding. */ + json_for_each_arr(i, t, sender_blinding_hops) { + struct secret s; + struct pubkey pubkey, expected_pubkey; + u8 *expected_encdata; + struct pubkey alias, expected_alias; + + assert(json_to_secret(json, + json_get_member(json, t, "ephemeral_privkey"), + &s)); + + /* First blinding is stated, remainder are derived! */ + if (i == 0) { + blinding.secret = s; + } else + assert(secret_eq_consttime(&blinding.secret, &s)); + + assert(pubkey_from_privkey(&blinding, &pubkey)); + json_to_pubkey(json, json_get_member(json, t, "ephemeral_pubkey"), + &expected_pubkey); + assert(pubkey_eq(&pubkey, &expected_pubkey)); + + encrypted_data[i] = enctlv_from_encmsg_raw(encrypted_data, + &blinding, + &ids[i], + enctlvs[i], + &blinding, + &alias); + expected_encdata = json_tok_bin_from_hex(tmpctx,json, + json_get_member(json, t, + "encrypted_data")); + assert(memeq(encrypted_data[i], tal_bytelen(encrypted_data[i]), + expected_encdata, tal_bytelen(expected_encdata))); + + json_to_pubkey(json, json_get_member(json, t, "blinded_node_id"), + &expected_alias); + assert(pubkey_eq(&alias, &expected_alias)); + } + + /* At this point, we override the blinding! */ + json_for_each_arr(i, t, recip_blinding_hops) { + struct secret s; + struct pubkey pubkey, expected_pubkey; + u8 *expected_encdata; + struct pubkey alias, expected_alias; + + assert(json_to_secret(json, + json_get_member(json, t, "ephemeral_privkey"), + &s)); + + /* First blinding is from next_blinding_override, + * remainder are derived! */ + if (i == 0) { + blinding.secret = s; + } else + assert(secret_eq_consttime(&blinding.secret, &s)); + + assert(pubkey_from_privkey(&blinding, &pubkey)); + json_to_pubkey(json, json_get_member(json, t, "ephemeral_pubkey"), + &expected_pubkey); + assert(pubkey_eq(&pubkey, &expected_pubkey)); + + encrypted_data[i + num_sender_hops] + = enctlv_from_encmsg_raw(tmpctx, + &blinding, + &ids[i + num_sender_hops], + enctlvs[i + num_sender_hops], + &blinding, + &alias); + expected_encdata = json_tok_bin_from_hex(tmpctx,json, + json_get_member(json, t, + "encrypted_data")); + assert(memeq(encrypted_data[i + num_sender_hops], + tal_bytelen(encrypted_data[i + num_sender_hops]), + expected_encdata, tal_bytelen(expected_encdata))); + + json_to_pubkey(json, json_get_member(json, t, "blinded_node_id"), + &expected_alias); + assert(pubkey_eq(&alias, &expected_alias)); + } + + /* Now try unblinding */ + json_for_each_arr(i, t, unblinding_hops) { + struct privkey me; + struct secret ss; + struct pubkey blinding, expected_blinding; + struct pubkey onion_key, next_node; + + assert(json_to_secret(json, + json_get_member(json, t, "node_privkey"), + &me.secret)); + + mykey = &me; + assert(json_to_pubkey(json, + json_get_member(json, t, "ephemeral_pubkey"), + &blinding)); + + assert(unblind_onion(&blinding, test_ecdh, &onion_key, &ss)); + if (i != unblinding_hops->size - 1) { + assert(decrypt_enctlv(&blinding, &ss, encrypted_data[i], &next_node, &blinding)); + assert(json_to_pubkey(json, + json_get_member(json, t, "next_ephemeral_pubkey"), + &expected_blinding)); + assert(pubkey_eq(&blinding, &expected_blinding)); + } else { + struct secret *path_id; + struct pubkey my_id, alias; + assert(pubkey_from_privkey(&me, &my_id)); + assert(decrypt_final_enctlv(tmpctx, &blinding, &ss, + encrypted_data[i], + &my_id, &alias, + &path_id)); + } + } + +out: + common_shutdown(); +} diff --git a/common/test/run-route_blinding_test.c b/common/test/run-route_blinding_test.c new file mode 100644 index 000000000000..d9f141839536 --- /dev/null +++ b/common/test/run-route_blinding_test.c @@ -0,0 +1,249 @@ +#include "config.h" +#include "../bigsize.c" +#include "../blindedpath.c" +#include "../blinding.c" +#include "../hmac.c" +#include "../type_to_string.c" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for amount_asset_is_main */ +bool amount_asset_is_main(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_is_main called!\n"); abort(); } +/* Generated stub for amount_asset_to_sat */ +struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_to_sat called!\n"); abort(); } +/* Generated stub for amount_msat */ +struct amount_msat amount_msat(u64 millisatoshis UNNEEDED) +{ fprintf(stderr, "amount_msat called!\n"); abort(); } +/* Generated stub for amount_sat */ +struct amount_sat amount_sat(u64 satoshis UNNEEDED) +{ fprintf(stderr, "amount_sat called!\n"); abort(); } +/* Generated stub for amount_sat_add */ + bool amount_sat_add(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_add called!\n"); abort(); } +/* Generated stub for amount_sat_eq */ +bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_greater_eq */ +bool amount_sat_greater_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_greater_eq called!\n"); abort(); } +/* Generated stub for amount_sat_sub */ + bool amount_sat_sub(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_sub called!\n"); abort(); } +/* Generated stub for amount_sat_to_asset */ +struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u8 *asset UNNEEDED) +{ fprintf(stderr, "amount_sat_to_asset called!\n"); abort(); } +/* Generated stub for amount_sat_to_msat */ + bool amount_sat_to_msat(struct amount_msat *msat UNNEEDED, + struct amount_sat sat UNNEEDED) +{ fprintf(stderr, "amount_sat_to_msat called!\n"); abort(); } +/* Generated stub for amount_tx_fee */ +struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) +{ fprintf(stderr, "amount_tx_fee called!\n"); abort(); } +/* Generated stub for fmt_wireaddr_without_port */ +char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) +{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } +/* Generated stub for fromwire_amount_msat */ +struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_amount_msat called!\n"); abort(); } +/* Generated stub for fromwire_amount_sat */ +struct amount_sat fromwire_amount_sat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_amount_sat 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_node_id */ +void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) +{ fprintf(stderr, "fromwire_node_id called!\n"); abort(); } +/* Generated stub for json_add_member */ +void json_add_member(struct json_stream *js UNNEEDED, + const char *fieldname UNNEEDED, + bool quote UNNEEDED, + const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "json_add_member called!\n"); abort(); } +/* Generated stub for json_member_direct */ +char *json_member_direct(struct json_stream *js UNNEEDED, + const char *fieldname UNNEEDED, size_t extra UNNEEDED) +{ fprintf(stderr, "json_member_direct called!\n"); abort(); } +/* Generated stub for json_object_end */ +void json_object_end(struct json_stream *js UNNEEDED) +{ fprintf(stderr, "json_object_end called!\n"); abort(); } +/* Generated stub for json_object_start */ +void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) +{ fprintf(stderr, "json_object_start called!\n"); abort(); } +/* Generated stub for node_id_from_hexstr */ +bool node_id_from_hexstr(const char *str UNNEEDED, size_t slen UNNEEDED, struct node_id *id UNNEEDED) +{ fprintf(stderr, "node_id_from_hexstr called!\n"); abort(); } +/* Generated stub for parse_amount_msat */ +bool parse_amount_msat(struct amount_msat *msat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED) +{ fprintf(stderr, "parse_amount_msat called!\n"); abort(); } +/* Generated stub for parse_amount_sat */ +bool parse_amount_sat(struct amount_sat *sat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED) +{ fprintf(stderr, "parse_amount_sat called!\n"); abort(); } +/* Generated stub for towire_amount_msat */ +void towire_amount_msat(u8 **pptr UNNEEDED, const struct amount_msat msat UNNEEDED) +{ fprintf(stderr, "towire_amount_msat called!\n"); abort(); } +/* Generated stub for towire_amount_sat */ +void towire_amount_sat(u8 **pptr UNNEEDED, const struct amount_sat sat UNNEEDED) +{ fprintf(stderr, "towire_amount_sat 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_node_id */ +void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) +{ fprintf(stderr, "towire_node_id called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +static u8 *json_to_enctlvs(const tal_t *ctx, + const char *buf, const jsmntok_t *tlvs) +{ + struct tlv_encrypted_data_tlv *enctlv = tlv_encrypted_data_tlv_new(tmpctx); + size_t i; + const jsmntok_t *t; + u8 *ret, *appended = tal_arr(tmpctx, u8, 0); + + json_for_each_obj(i, t, tlvs) { + if (json_tok_streq(buf, t, "short_channel_id")) { + enctlv->short_channel_id = tal(enctlv, struct short_channel_id); + assert(json_to_short_channel_id(buf, t+1, + enctlv->short_channel_id)); + } else if (json_tok_streq(buf, t, "padding")) { + enctlv->padding = json_tok_bin_from_hex(enctlv, + buf, t+1); + assert(enctlv->padding); + } else if (json_tok_streq(buf, t, "next_node_id")) { + enctlv->next_node_id = tal(enctlv, struct pubkey); + assert(json_to_pubkey(buf, t+1, + enctlv->next_node_id)); + } else if (json_tok_streq(buf, t, "path_id")) { + enctlv->path_id = json_tok_bin_from_hex(enctlv, + buf, t+1); + assert(enctlv->path_id); + } else { + u16 tagnum; + u8 *val; + assert(json_tok_startswith(buf, t, "unknown_tag_")); + tagnum = atoi(buf + t->start + strlen("unknown_tag_")); + assert(tagnum); + val = json_tok_bin_from_hex(enctlv, buf, t+1); + assert(val); + + /* We can't actually represent these in a way towire_ + * will see, so we literally append them */ + towire_bigsize(&appended, tagnum); + towire_bigsize(&appended, tal_bytelen(val)); + towire_u8_array(&appended, val, tal_bytelen(val)); + } + } + ret = tal_arr(ctx, u8, 0); + towire_encrypted_data_tlv(&ret, enctlv); + towire_u8_array(&ret, appended, tal_bytelen(appended)); + return ret; +} + +int main(int argc, char *argv[]) +{ + char *json; + size_t i; + jsmn_parser parser; + jsmntok_t toks[5000]; + const jsmntok_t *t, *hops_tok; + struct pubkey *ids; + u8 **enctlvs; + struct privkey blinding; + + common_setup(argv[0]); + + if (argv[1]) + json = grab_file(tmpctx, argv[1]); + else { + char *dir = getenv("BOLTDIR"); + json = grab_file(tmpctx, + path_join(tmpctx, + dir ? dir : "../lightning-rfc", + "bolt04/route-blinding-test.json")); + if (!json) { + printf("test file not found, skipping\n"); + goto out; + } + } + + jsmn_init(&parser); + if (jsmn_parse(&parser, json, strlen(json), toks, ARRAY_SIZE(toks)) < 0) + abort(); + + hops_tok = json_get_member(json, json_get_member(json, toks, "route"), "hops"); + ids = tal_arr(tmpctx, struct pubkey, hops_tok->size); + enctlvs = tal_arr(tmpctx, u8 *, hops_tok->size); + + json_for_each_arr(i, t, hops_tok) { + u8 *expected; + assert(json_to_pubkey(json, json_get_member(json, t, "node_id"), + &ids[i])); + enctlvs[i] = json_tok_bin_from_hex(enctlvs, json, + json_get_member(json, t, "encoded_tlvs")); + expected = json_to_enctlvs(tmpctx, json, + json_get_member(json, t, "tlvs")); + assert(memeq(expected, tal_bytelen(expected), + enctlvs[i], tal_bytelen(enctlvs[i]))); + } + + /* Now do the blinding. */ + hops_tok = json_get_member(json, json_get_member(json, toks, "blinding"), "hops"); + assert(hops_tok->size == tal_count(ids)); + + json_for_each_arr(i, t, hops_tok) { + struct secret s; + struct pubkey pubkey, expected_pubkey; + u8 *enctlv, *expected_enctlv; + struct pubkey alias, expected_alias; + + assert(json_to_secret(json, + json_get_member(json, t, "ephemeral_privkey"), + &s)); + + /* First blinding is stated, remainder are derived! */ + if (i == 0) { + blinding.secret = s; + } else + assert(secret_eq_consttime(&blinding.secret, &s)); + + assert(pubkey_from_privkey(&blinding, &pubkey)); + json_to_pubkey(json, json_get_member(json, t, "ephemeral_pubkey"), + &expected_pubkey); + assert(pubkey_eq(&pubkey, &expected_pubkey)); + + enctlv = enctlv_from_encmsg_raw(tmpctx, + &blinding, + &ids[i], + enctlvs[i], + &blinding, + &alias); + expected_enctlv = json_tok_bin_from_hex(tmpctx,json, + json_get_member(json, t, + "encrypted_data")); + assert(memeq(enctlv, tal_bytelen(enctlv), + expected_enctlv, tal_bytelen(expected_enctlv))); + + json_to_pubkey(json, json_get_member(json, t, "blinded_node_id"), + &expected_alias); + assert(pubkey_eq(&alias, &expected_alias)); + } + +out: + common_shutdown(); +} diff --git a/devtools/blindedpath.c b/devtools/blindedpath.c index 759b2781e631..3e46662ab2a8 100644 --- a/devtools/blindedpath.c +++ b/devtools/blindedpath.c @@ -138,25 +138,17 @@ int main(int argc, char **argv) u8 *p; u8 buf[BIGSIZE_MAX_LEN]; const unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - struct tlv_onionmsg_payload *outer; - struct tlv_encmsg_tlvs *inner; + struct tlv_obs2_onionmsg_payload *outer; + struct tlv_obs2_encmsg_tlvs *inner; int ret; /* Inner is encrypted */ - inner = tlv_encmsg_tlvs_new(tmpctx); - /* Use scid if they provided one */ - if (scids[i]) { - inner->obs_next_short_channel_id - = tal_dup(inner, struct short_channel_id, - scids[i]); - } else { - inner->next_node_id - = tal_dup(inner, struct pubkey, &nodes[i+1]); - } + inner = tlv_obs2_encmsg_tlvs_new(tmpctx); + inner->next_node_id = tal_dup(inner, struct pubkey, &nodes[i+1]); p = tal_arr(tmpctx, u8, 0); - towire_encmsg_tlvs(&p, inner); + towire_obs2_encmsg_tlvs(&p, inner); - outer = tlv_onionmsg_payload_new(tmpctx); + outer = tlv_obs2_onionmsg_payload_new(tmpctx); outer->enctlv = tal_arr(outer, u8, tal_count(p) + crypto_aead_chacha20poly1305_ietf_ABYTES); ret = crypto_aead_chacha20poly1305_ietf_encrypt(outer->enctlv, NULL, @@ -168,7 +160,7 @@ int main(int argc, char **argv) assert(ret == 0); p = tal_arr(tmpctx, u8, 0); - towire_onionmsg_payload(&p, outer); + towire_obs2_onionmsg_payload(&p, outer); ret = bigsize_put(buf, tal_bytelen(p)); if (simpleout) { @@ -202,7 +194,7 @@ int main(int argc, char **argv) struct secret hmac, rho; struct route_step *rs; const u8 *cursor; - struct tlv_onionmsg_payload *outer; + struct tlv_obs2_onionmsg_payload *outer; size_t max, len; struct pubkey res; struct sha256 h; @@ -265,8 +257,8 @@ int main(int argc, char **argv) /* Always true since we're non-legacy */ assert(len == max); - outer = tlv_onionmsg_payload_new(tmpctx); - if (!fromwire_onionmsg_payload(&cursor, &max, outer)) + outer = tlv_obs2_onionmsg_payload_new(tmpctx); + if (!fromwire_obs2_onionmsg_payload(&cursor, &max, outer)) errx(1, "Invalid payload %s", tal_hex(tmpctx, rs->raw_payload)); @@ -277,11 +269,11 @@ int main(int argc, char **argv) /* Look for enctlv */ if (!outer->enctlv) - errx(1, "No enctlv field"); + errx(1, "No encrypted_recipient_data field"); if (tal_bytelen(outer->enctlv) < crypto_aead_chacha20poly1305_ietf_ABYTES) - errx(1, "enctlv field too short"); + errx(1, "encrypted_recipient_data field too short"); dec = tal_arr(tmpctx, u8, tal_bytelen(outer->enctlv) @@ -294,7 +286,7 @@ int main(int argc, char **argv) npub, rho.data); if (ret != 0) - errx(1, "Failed to decrypt enctlv field"); + errx(1, "Failed to decrypt encrypted_recipient_data field"); printf("Contents: %s\n", tal_hex(tmpctx, dec)); diff --git a/devtools/bolt12-cli.c b/devtools/bolt12-cli.c index aa4dbf2bbfdb..2e6dc0064058 100644 --- a/devtools/bolt12-cli.c +++ b/devtools/bolt12-cli.c @@ -174,8 +174,8 @@ static bool print_recurrance(const struct tlv_offer_recurrence *recurrence, const char *unit; bool ok = true; - /* BOLT-offers #12: - * Thus, each payment has: + /* BOLT-offers-recurrence #12: + * Thus, each offer containing a recurring payment has: * 1. A `time_unit` defining 0 (seconds), 1 (days), 2 (months), * 3 (years). * 2. A `period`, defining how often (in `time_unit`) it has to be paid. @@ -270,7 +270,7 @@ static bool print_blindedpaths(struct blinded_path **paths, printf(" %s:%s", type_to_string(tmpctx, struct pubkey, &p[j]->node_id), - tal_hex(tmpctx, p[j]->enctlv)); + tal_hex(tmpctx, p[j]->encrypted_recipient_data)); if (blindedpay) { if (bp_idx < tal_count(blindedpay)) printf("fee=%u/%u,cltv=%u,features=%s", diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index c1b94c22e00a..07e04537494a 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -1514,13 +1514,12 @@ type prefix, since c-lightning does not know how to parse the message. Because this is a chained hook, the daemon expects the result to be `{'result': 'continue'}`. It will fail if something else is returned. -### `onion_message`, `onion_message_blinded` and `onion_message_ourpath` +### `onion_message_blinded` and `onion_message_ourpath` **(WARNING: experimental-offers only)** -These three hooks are almost identical, in that they are called when -an onion message is received. The `onion_message` hook is only used -for obsolete unblinded messages, and can be ignored for modern usage. +These two hooks are almost identical, in that they are called when +an onion message is received. `onion_message_blinded` is used for unsolicited messages (where the source knows that it is sending to this node), and @@ -1543,7 +1542,7 @@ The payload for a call follows this format: "reply_first_node": "02df5ffe895c778e10f7742a6c5b8a0cefbe9465df58b92fadeb883752c8107c8f", "reply_blinding": "02df5ffe895c778e10f7742a6c5b8a0cefbe9465df58b92fadeb883752c8107c8f", "reply_path": [ {"id": "02df5ffe895c778e10f7742a6c5b8a0cefbe9465df58b92fadeb883752c8107c8f", - "enctlv": "0a020d0d", + "encrypted_recipient_data": "0a020d0d", "blinding": "02df5ffe895c778e10f7742a6c5b8a0cefbe9465df58b92fadeb883752c8107c8f"} ], "invoice_request": "0a020d0d", "invoice": "0a020d0d", @@ -1556,6 +1555,7 @@ The payload for a call follows this format: All fields shown here are optional. We suggest just returning `{'result': 'continue'}`; any other result +Signed-off-by: Rusty Russell will cause the message not to be handed to any other hooks. ## Bitcoin backend diff --git a/doc/lightning-decode.7.md b/doc/lightning-decode.7.md index 6708071ed4b8..81d8a62b9453 100644 --- a/doc/lightning-decode.7.md +++ b/doc/lightning-decode.7.md @@ -42,7 +42,7 @@ If **type** is "bolt12 offer", and **valid** is *true*: - **blinding** (pubkey): blinding factor for this path - **path** (array of objects): an individual path: - **node_id** (pubkey): node_id of the hop - - **enctlv** (hex): encrypted TLV entry for this hop + - **encrypted_recipient_data** (hex): encrypted TLV entry for this hop - **quantity_min** (u64, optional): the minimum quantity - **quantity_max** (u64, optional): the maximum quantity - **recurrence** (object, optional): how often to this offer should be used: @@ -82,7 +82,7 @@ If **type** is "bolt12 invoice", and **valid** is *true*: - **blinding** (pubkey): blinding factor for this path - **path** (array of objects): an individual path: - **node_id** (pubkey): node_id of the hop - - **enctlv** (hex): encrypted TLV entry for this hop + - **encrypted_recipient_data** (hex): encrypted TLV entry for this hop - **quantity** (u64, optional): the quantity ordered - **recurrence_counter** (u32, optional): the 0-based counter for a recurring payment - **recurrence_start** (u32, optional): the optional start period for a recurring payment @@ -180,4 +180,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:cd4a18d07c23b6a995f8db7cac25344e340e512c786b8cf743f56ecceeb84960) +[comment]: # ( SHA256STAMP:d05b5fc1bf230b3bbd03e2023fb0c6bbefb700f7c3cfb43512da48dbce45f005) diff --git a/doc/schemas/decode.schema.json b/doc/schemas/decode.schema.json index 9e12c1588876..808ef8f4313d 100644 --- a/doc/schemas/decode.schema.json +++ b/doc/schemas/decode.schema.json @@ -146,7 +146,7 @@ "type": "object", "required": [ "node_id", - "enctlv" + "encrypted_recipient_data" ], "additionalProperties": false, "properties": { @@ -154,7 +154,7 @@ "type": "pubkey", "description": "node_id of the hop" }, - "enctlv": { + "encrypted_recipient_data": { "type": "hex", "description": "encrypted TLV entry for this hop" } @@ -388,7 +388,7 @@ "type": "object", "required": [ "node_id", - "enctlv" + "encrypted_recipient_data" ], "additionalProperties": false, "properties": { @@ -396,7 +396,7 @@ "type": "pubkey", "description": "node_id of the hop" }, - "enctlv": { + "encrypted_recipient_data": { "type": "hex", "description": "encrypted TLV entry for this hop" } diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index bbbf4804c972..5491ac0a106d 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -351,18 +351,17 @@ static bool handle_local_channel_announcement(struct daemon *daemon, } /* Peer sends obsolete onion msg. */ -static u8 *handle_obs_onion_message(struct peer *peer, const u8 *msg) +static u8 *handle_obs2_onion_message(struct peer *peer, const u8 *msg) { enum onion_wire badreason; struct onionpacket *op; - struct secret ss, *blinding_ss; - struct pubkey *blinding_in, ephemeral; + struct pubkey blinding, ephemeral; struct route_step *rs; u8 *onion; + struct tlv_obs2_onionmsg_payload *om; + struct secret ss, onion_ss; const u8 *cursor; size_t max, maxlen; - struct tlv_onionmsg_payload *om; - struct tlv_obs_onion_message_tlvs *tlvs = tlv_obs_onion_message_tlvs_new(msg); /* Ignore unless explicitly turned on. */ if (!feature_offered(peer->daemon->our_features->bits[NODE_ANNOUNCE_FEATURE], @@ -370,216 +369,163 @@ static u8 *handle_obs_onion_message(struct peer *peer, const u8 *msg) return NULL; /* FIXME: ratelimit! */ - if (!fromwire_obs_onion_message(msg, msg, &onion, tlvs)) + if (!fromwire_obs2_onion_message(msg, msg, &blinding, &onion)) return towire_warningfmt(peer, NULL, "Bad onion_message"); /* We unwrap the onion now. */ op = parse_onionpacket(tmpctx, onion, tal_bytelen(onion), &badreason); if (!op) { - status_debug("peer %s: onion msg: can't parse onionpacket: %s", - type_to_string(tmpctx, struct node_id, &peer->id), - onion_wire_name(badreason)); + status_peer_debug(&peer->id, "onion msg: can't parse onionpacket: %s", + onion_wire_name(badreason)); return NULL; } ephemeral = op->ephemeralkey; - if (tlvs->blinding) { - struct secret hmac; - - /* E(i) */ - blinding_in = tal_dup(msg, struct pubkey, tlvs->blinding); - status_debug("peer %s: blinding in = %s", - type_to_string(tmpctx, struct node_id, &peer->id), - type_to_string(tmpctx, struct pubkey, blinding_in)); - blinding_ss = tal(msg, struct secret); - ecdh(blinding_in, blinding_ss); - - /* b(i) = HMAC256("blinded_node_id", ss(i)) * k(i) */ - subkey_from_hmac("blinded_node_id", blinding_ss, &hmac); - - /* We instead tweak the *ephemeral* key from the onion and use - * our normal privkey: since hsmd knows only how to ECDH with - * our real key */ - if (secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx, - &ephemeral.pubkey, - hmac.data) != 1) { - status_debug("peer %s: onion msg: can't tweak pubkey", - type_to_string(tmpctx, struct node_id, &peer->id)); - return NULL; - } - } else { - blinding_ss = NULL; - blinding_in = NULL; + if (!unblind_onion(&blinding, ecdh, &ephemeral, &ss)) { + status_peer_debug(&peer->id, "onion msg: can't unblind onionpacket"); + return NULL; } - ecdh(&ephemeral, &ss); - - /* We make sure we can parse onion packet, so we know if shared secret - * is actually valid (this checks hmac). */ - rs = process_onionpacket(tmpctx, op, &ss, NULL, 0, false); + /* Now get onion shared secret and parse it. */ + ecdh(&ephemeral, &onion_ss); + rs = process_onionpacket(tmpctx, op, &onion_ss, NULL, 0, false); if (!rs) { - status_debug("peer %s: onion msg: can't process onionpacket ss=%s", - type_to_string(tmpctx, struct node_id, &peer->id), - type_to_string(tmpctx, struct secret, &ss)); + status_peer_debug(&peer->id, + "onion msg: can't process onionpacket ss=%s", + type_to_string(tmpctx, struct secret, &onion_ss)); return NULL; } - /* The raw payload is prepended with length in the TLV world. */ + /* The raw payload is prepended with length in the modern onion. */ cursor = rs->raw_payload; max = tal_bytelen(rs->raw_payload); maxlen = fromwire_bigsize(&cursor, &max); if (!cursor) { - status_debug("peer %s: onion msg: Invalid hop payload %s", - type_to_string(tmpctx, struct node_id, &peer->id), - tal_hex(tmpctx, rs->raw_payload)); + status_peer_debug(&peer->id, "onion msg: Invalid hop payload %s", + tal_hex(tmpctx, rs->raw_payload)); return NULL; } if (maxlen > max) { - status_debug("peer %s: onion msg: overlong hop payload %s", - type_to_string(tmpctx, struct node_id, &peer->id), - tal_hex(tmpctx, rs->raw_payload)); + status_peer_debug(&peer->id, "onion msg: overlong hop payload %s", + tal_hex(tmpctx, rs->raw_payload)); return NULL; } - om = tlv_onionmsg_payload_new(msg); - if (!fromwire_onionmsg_payload(&cursor, &maxlen, om)) { - status_debug("peer %s: onion msg: invalid onionmsg_payload %s", - type_to_string(tmpctx, struct node_id, &peer->id), - tal_hex(tmpctx, rs->raw_payload)); + om = tlv_obs2_onionmsg_payload_new(msg); + if (!fromwire_obs2_onionmsg_payload(&cursor, &maxlen, om)) { + status_peer_debug(&peer->id, "onion msg: invalid onionmsg_payload %s", + tal_hex(tmpctx, rs->raw_payload)); return NULL; } - /* If we weren't given a blinding factor, tlv can provide one. */ - if (om->obs_blinding && !blinding_ss) { - /* E(i) */ - blinding_in = tal_dup(msg, struct pubkey, om->obs_blinding); - blinding_ss = tal(msg, struct secret); - - ecdh(blinding_in, blinding_ss); - } - - if (om->enctlv) { - const unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - u8 *dec; - struct secret rho; - int ret; - - if (!blinding_ss) { - status_debug("peer %s: enctlv but no blinding?", - type_to_string(tmpctx, struct node_id, &peer->id)); - return NULL; - } - - /* We need this to decrypt enctlv */ - subkey_from_hmac("rho", blinding_ss, &rho); - - /* Overrides next_scid / next_node */ - if (tal_bytelen(om->enctlv) - < crypto_aead_chacha20poly1305_ietf_ABYTES) { - status_debug("peer %s: enctlv too short for mac", - type_to_string(tmpctx, struct node_id, &peer->id)); - return NULL; - } + if (rs->nextcase == ONION_END) { + struct pubkey *reply_blinding, *first_node_id, me, alias; + const struct onionmsg_path **reply_path; + struct secret *self_id; + u8 *omsg; - dec = tal_arr(msg, u8, - tal_bytelen(om->enctlv) - - crypto_aead_chacha20poly1305_ietf_ABYTES); - ret = crypto_aead_chacha20poly1305_ietf_decrypt(dec, NULL, - NULL, - om->enctlv, - tal_bytelen(om->enctlv), - NULL, 0, - npub, - rho.data); - if (ret != 0) { - status_debug("peer %s: Failed to decrypt enctlv field", - type_to_string(tmpctx, struct node_id, &peer->id)); + if (!pubkey_from_node_id(&me, &peer->daemon->id)) { + status_broken("Failed to convert own id"); return NULL; } - status_debug("peer %s: enctlv -> %s", - type_to_string(tmpctx, struct node_id, &peer->id), - tal_hex(tmpctx, dec)); - - /* Replace onionmsg with one from enctlv */ - cursor = dec; - maxlen = tal_bytelen(dec); - - om = tlv_onionmsg_payload_new(msg); - if (!fromwire_onionmsg_payload(&cursor, &maxlen, om)) { - status_debug("peer %s: onion msg: invalid enctlv onionmsg_payload %s", - type_to_string(tmpctx, struct node_id, &peer->id), - tal_hex(tmpctx, dec)); + /* Final enctlv is actually optional */ + if (!om->enctlv) { + alias = me; + self_id = NULL; + } else if (!decrypt_obs2_final_enctlv(tmpctx, &blinding, &ss, + om->enctlv, &me, &alias, + &self_id)) { + status_peer_debug(&peer->id, + "onion msg: failed to decrypt enctlv" + " %s", tal_hex(tmpctx, om->enctlv)); return NULL; } - } else if (blinding_ss && rs->nextcase != ONION_END) { - status_debug("peer %s: Onion had %s, but not enctlv?", - type_to_string(tmpctx, struct node_id, &peer->id), - tlvs->blinding ? "blinding" : "om blinding"); - return NULL; - } - - if (rs->nextcase == ONION_END) { - struct pubkey *blinding; - const struct onionmsg_path **path; - u8 *omsg; - if (om->obs_reply_path) { - blinding = &om->obs_reply_path->blinding; - path = cast_const2(const struct onionmsg_path **, - om->obs_reply_path->path); + if (om->reply_path) { + first_node_id = &om->reply_path->first_node_id; + reply_blinding = &om->reply_path->blinding; + reply_path = cast_const2(const struct onionmsg_path **, + om->reply_path->path); } else { - blinding = NULL; - path = NULL; + first_node_id = NULL; + reply_blinding = NULL; + reply_path = NULL; } /* We re-marshall here by policy, before handing to lightningd */ omsg = tal_arr(tmpctx, u8, 0); towire_tlvstream_raw(&omsg, om->fields); daemon_conn_send(peer->daemon->master, - take(towire_gossipd_got_obs_onionmsg_to_us(NULL, - blinding_in, - blinding, - path, + take(towire_gossipd_got_onionmsg_to_us(NULL, + true, /* obs2 */ + &alias, self_id, + reply_blinding, + first_node_id, + reply_path, omsg))); } else { - struct pubkey *next_blinding; - struct node_id *next_node; - - /* This *MUST* have instructions on where to go next. */ - if (!om->obs_next_short_channel_id && !om->obs_next_node_id) { - status_debug("peer %s: onion msg: no next field in %s", - type_to_string(tmpctx, struct node_id, &peer->id), - tal_hex(tmpctx, rs->raw_payload)); + struct pubkey next_node, next_blinding; + struct peer *next_peer; + struct node_id next_node_id; + + /* This fails as expected if no enctlv. */ + if (!decrypt_obs2_enctlv(&blinding, &ss, om->enctlv, &next_node, + &next_blinding)) { + status_peer_debug(&peer->id, + "onion msg: invalid enctlv %s", + tal_hex(tmpctx, om->enctlv)); return NULL; } - if (blinding_ss) { - /* E(i-1) = H(E(i) || ss(i)) * E(i) */ - struct sha256 h; - blinding_hash_e_and_ss(blinding_in, blinding_ss, &h); - next_blinding = tal(msg, struct pubkey); - blinding_next_pubkey(blinding_in, &h, next_blinding); - } else - next_blinding = NULL; + /* Even though lightningd checks for valid ids, there's a race + * where it might vanish before we read this command. */ + node_id_from_pubkey(&next_node_id, &next_node); + next_peer = find_peer(peer->daemon, &next_node_id); + if (!next_peer) { + status_peer_debug(&peer->id, + "onion msg: unknown next peer %s", + type_to_string(tmpctx, + struct pubkey, + &next_node)); + return NULL; + } + queue_peer_msg(next_peer, + take(towire_obs2_onion_message(NULL, + &next_blinding, + serialize_onionpacket(tmpctx, rs->next)))); + } - if (om->obs_next_node_id) { - next_node = tal(tmpctx, struct node_id); - node_id_from_pubkey(next_node, om->obs_next_node_id); - } else - next_node = NULL; + return NULL; +} - daemon_conn_send(peer->daemon->master, - take(towire_gossipd_got_obs_onionmsg_forward(NULL, - om->obs_next_short_channel_id, - next_node, - next_blinding, - serialize_onionpacket(tmpctx, rs->next)))); +static struct io_plan *onionmsg_req(struct io_conn *conn, struct daemon *daemon, + const u8 *msg) +{ + struct node_id id; + u8 *onionmsg; + struct pubkey blinding; + struct peer *peer; + bool obs2; + + if (!fromwire_gossipd_send_onionmsg(msg, msg, &obs2, &id, &onionmsg, &blinding)) + master_badmsg(WIRE_GOSSIPD_SEND_ONIONMSG, msg); + + /* Even though lightningd checks for valid ids, there's a race + * where it might vanish before we read this command. */ + peer = find_peer(daemon, &id); + if (peer) { + u8 *omsg; + if (obs2) + omsg = towire_obs2_onion_message(NULL, &blinding, onionmsg); + else + omsg = towire_onion_message(NULL, &blinding, onionmsg); + queue_peer_msg(peer, take(omsg)); } - return NULL; + return daemon_conn_read_next(conn, daemon->master); } -/* Peer sends onion msg. */ +/* Peer sends an onion msg. */ static u8 *handle_onion_message(struct peer *peer, const u8 *msg) { enum onion_wire badreason; @@ -659,15 +605,15 @@ static u8 *handle_onion_message(struct peer *peer, const u8 *msg) } /* Final enctlv is actually optional */ - if (!om->enctlv) { + if (!om->encrypted_data_tlv) { alias = me; self_id = NULL; } else if (!decrypt_final_enctlv(tmpctx, &blinding, &ss, - om->enctlv, &me, &alias, + om->encrypted_data_tlv, &me, &alias, &self_id)) { status_peer_debug(&peer->id, "onion msg: failed to decrypt enctlv" - " %s", tal_hex(tmpctx, om->enctlv)); + " %s", tal_hex(tmpctx, om->encrypted_data_tlv)); return NULL; } @@ -687,6 +633,7 @@ static u8 *handle_onion_message(struct peer *peer, const u8 *msg) towire_tlvstream_raw(&omsg, om->fields); daemon_conn_send(peer->daemon->master, take(towire_gossipd_got_onionmsg_to_us(NULL, + false, /* !obs2 */ &alias, self_id, reply_blinding, first_node_id, @@ -698,16 +645,15 @@ static u8 *handle_onion_message(struct peer *peer, const u8 *msg) struct node_id next_node_id; /* This fails as expected if no enctlv. */ - if (!decrypt_enctlv(&blinding, &ss, om->enctlv, &next_node, - &next_blinding)) { + if (!decrypt_enctlv(&blinding, &ss, om->encrypted_data_tlv, &next_node, + &next_blinding)) { status_peer_debug(&peer->id, "onion msg: invalid enctlv %s", - tal_hex(tmpctx, om->enctlv)); + tal_hex(tmpctx, om->encrypted_data_tlv)); return NULL; } - /* Even though lightningd checks for valid ids, there's a race - * where it might vanish before we read this command. */ + /* FIXME: Handle short_channel_id! */ node_id_from_pubkey(&next_node_id, &next_node); next_peer = find_peer(peer->daemon, &next_node_id); if (!next_peer) { @@ -727,60 +673,6 @@ static u8 *handle_onion_message(struct peer *peer, const u8 *msg) return NULL; } -/* We send an obsolete onion msg. */ -static struct io_plan *obs_onionmsg_req(struct io_conn *conn, struct daemon *daemon, - const u8 *msg) -{ - struct node_id id; - u8 *onion_routing_packet; - struct pubkey *blinding; - struct peer *peer; - - if (!fromwire_gossipd_send_obs_onionmsg(msg, msg, &id, &onion_routing_packet, - &blinding)) - master_badmsg(WIRE_GOSSIPD_SEND_OBS_ONIONMSG, msg); - - /* Even if lightningd were to check for valid ids, there's a race - * where it might vanish before we read this command; cleaner to - * handle it here with 'sent' = false. */ - peer = find_peer(daemon, &id); - if (peer) { - struct tlv_obs_onion_message_tlvs *tlvs; - - tlvs = tlv_obs_onion_message_tlvs_new(msg); - if (blinding) - tlvs->blinding = tal_dup(tlvs, struct pubkey, blinding); - - queue_peer_msg(peer, - take(towire_obs_onion_message(NULL, - onion_routing_packet, - tlvs))); - } - return daemon_conn_read_next(conn, daemon->master); -} - -static struct io_plan *onionmsg_req(struct io_conn *conn, struct daemon *daemon, - const u8 *msg) -{ - struct node_id id; - u8 *onionmsg; - struct pubkey blinding; - struct peer *peer; - - if (!fromwire_gossipd_send_onionmsg(msg, msg, &id, &onionmsg, &blinding)) - master_badmsg(WIRE_GOSSIPD_SEND_ONIONMSG, msg); - - /* Even though lightningd checks for valid ids, there's a race - * where it might vanish before we read this command. */ - peer = find_peer(daemon, &id); - if (peer) { - queue_peer_msg(peer, - take(towire_onion_message(NULL, - &blinding, onionmsg))); - } - return daemon_conn_read_next(conn, daemon->master); -} - /*~ This is where the per-peer daemons send us messages. It's either forwarded * gossip, or a request for information. We deliberately use non-overlapping * message types so we can distinguish them. */ @@ -814,8 +706,8 @@ static struct io_plan *peer_msg_in(struct io_conn *conn, case WIRE_REPLY_SHORT_CHANNEL_IDS_END: err = handle_reply_short_channel_ids_end(peer, msg); goto handled_relay; - case WIRE_OBS_ONION_MESSAGE: - err = handle_obs_onion_message(peer, msg); + case WIRE_OBS2_ONION_MESSAGE: + err = handle_obs2_onion_message(peer, msg); goto handled_relay; case WIRE_ONION_MESSAGE: err = handle_onion_message(peer, msg); @@ -1587,9 +1479,6 @@ static struct io_plan *recv_req(struct io_conn *conn, break; #endif /* !DEVELOPER */ - case WIRE_GOSSIPD_SEND_OBS_ONIONMSG: - return obs_onionmsg_req(conn, daemon, msg); - case WIRE_GOSSIPD_SEND_ONIONMSG: return onionmsg_req(conn, daemon, msg); @@ -1599,8 +1488,6 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_GOSSIPD_GET_TXOUT: case WIRE_GOSSIPD_DEV_MEMLEAK_REPLY: case WIRE_GOSSIPD_DEV_COMPACT_STORE_REPLY: - case WIRE_GOSSIPD_GOT_OBS_ONIONMSG_TO_US: - case WIRE_GOSSIPD_GOT_OBS_ONIONMSG_FORWARD: case WIRE_GOSSIPD_GOT_ONIONMSG_TO_US: case WIRE_GOSSIPD_ADDGOSSIP_REPLY: break; diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index 73a1e68210e9..8f49421d0312 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -74,23 +74,8 @@ msgdata,gossipd_dev_compact_store_reply,success,bool, msgtype,gossipd_new_blockheight,3026 msgdata,gossipd_new_blockheight,blockheight,u32, -# Tell lightningd we got an obsolete onion message (for us, or to fwd) -msgtype,gossipd_got_obs_onionmsg_to_us,3142 -msgdata,gossipd_got_obs_onionmsg_to_us,blinding_in,?pubkey, -msgdata,gossipd_got_obs_onionmsg_to_us,reply_blinding,?pubkey, -msgdata,gossipd_got_obs_onionmsg_to_us,reply_path_len,u16, -msgdata,gossipd_got_obs_onionmsg_to_us,reply_path,onionmsg_path,reply_path_len -msgdata,gossipd_got_obs_onionmsg_to_us,rawmsg_len,u16, -msgdata,gossipd_got_obs_onionmsg_to_us,rawmsg,u8,rawmsg_len - -msgtype,gossipd_got_obs_onionmsg_forward,3143 -msgdata,gossipd_got_obs_onionmsg_forward,next_scid,?short_channel_id, -msgdata,gossipd_got_obs_onionmsg_forward,next_node_id,?node_id, -msgdata,gossipd_got_obs_onionmsg_forward,next_blinding,?pubkey, -msgdata,gossipd_got_obs_onionmsg_forward,next_onion_len,u16, -msgdata,gossipd_got_obs_onionmsg_forward,next_onion,u8,next_onion_len - msgtype,gossipd_got_onionmsg_to_us,3145 +msgdata,gossipd_got_onionmsg_to_us,obs2,bool, msgdata,gossipd_got_onionmsg_to_us,node_alias,pubkey, msgdata,gossipd_got_onionmsg_to_us,self_id,?secret, msgdata,gossipd_got_onionmsg_to_us,reply_blinding,?pubkey, @@ -100,14 +85,9 @@ msgdata,gossipd_got_onionmsg_to_us,reply_path,onionmsg_path,reply_path_len msgdata,gossipd_got_onionmsg_to_us,rawmsg_len,u16, msgdata,gossipd_got_onionmsg_to_us,rawmsg,u8,rawmsg_len -# Lightningd tells us to send a onion message. -msgtype,gossipd_send_obs_onionmsg,3040 -msgdata,gossipd_send_obs_onionmsg,id,node_id, -msgdata,gossipd_send_obs_onionmsg,onion_len,u16, -msgdata,gossipd_send_obs_onionmsg,onion,u8,onion_len -msgdata,gossipd_send_obs_onionmsg,blinding,?pubkey, - +# Lightningd tells us to send an onion message. msgtype,gossipd_send_onionmsg,3041 +msgdata,gossipd_send_onionmsg,obs2,bool, msgdata,gossipd_send_onionmsg,id,node_id, msgdata,gossipd_send_onionmsg,onion_len,u16, msgdata,gossipd_send_onionmsg,onion,u8,onion_len diff --git a/gossipd/test/Makefile b/gossipd/test/Makefile index f4057a4cf222..6c4f150b894f 100644 --- a/gossipd/test/Makefile +++ b/gossipd/test/Makefile @@ -10,18 +10,23 @@ GOSSIPD_TEST_COMMON_OBJS := \ common/amount.o \ common/autodata.o \ common/bigsize.o \ + common/blindedpath.o \ common/channel_id.o \ common/features.o \ + common/hmac.o \ common/node_id.o \ common/json.o \ common/json_helpers.o \ common/lease_rates.o \ + common/onion.o \ common/pseudorand.o \ common/setup.o \ + common/sphinx.o \ common/type_to_string.o \ common/utils.o \ gossipd/gossip_store_wiregen.o \ wire/peer$(EXP)_wiregen.o \ + wire/onion$(EXP)_wiregen.o \ wire/fromwire.o \ wire/tlvstream.o \ wire/towire.o @@ -29,6 +34,16 @@ GOSSIPD_TEST_COMMON_OBJS := \ ALL_C_SOURCES += $(GOSSIPD_TEST_SRC) ALL_TEST_PROGRAMS += $(GOSSIPD_TEST_PROGRAMS) +# Extra stuff needed for onion tests +gossipd/test/run-onion_message: \ + wire/onion$(EXP)_wiregen.o \ + common/blindedpath.o \ + common/blinding.o \ + common/hmac.o \ + common/onion.o \ + common/sphinx.o \ + + $(GOSSIPD_TEST_PROGRAMS): $(GOSSIPD_TEST_COMMON_OBJS) $(BITCOIN_OBJS) # Test objects depend on ../ src and headers. diff --git a/gossipd/test/run-check_channel_announcement.c b/gossipd/test/run-check_channel_announcement.c index 20ef9b41919a..10cdd2e1eeb7 100644 --- a/gossipd/test/run-check_channel_announcement.c +++ b/gossipd/test/run-check_channel_announcement.c @@ -29,17 +29,38 @@ In particular, we set feature bit 19. The spec says we should set feature bit 1 #include "../common/wire_error.c" #include "../routing.c" +#include #include +#include #include +#include #include #include /* AUTOGENERATED MOCKS START */ +/* Generated stub for blinding_hash_e_and_ss */ +void blinding_hash_e_and_ss(const struct pubkey *e UNNEEDED, + const struct secret *ss UNNEEDED, + struct sha256 *sha UNNEEDED) +{ fprintf(stderr, "blinding_hash_e_and_ss called!\n"); abort(); } +/* Generated stub for blinding_next_privkey */ +bool blinding_next_privkey(const struct privkey *e UNNEEDED, + const struct sha256 *h UNNEEDED, + struct privkey *next UNNEEDED) +{ fprintf(stderr, "blinding_next_privkey called!\n"); abort(); } +/* Generated stub for blinding_next_pubkey */ +bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, + const struct sha256 *h UNNEEDED, + struct pubkey *next UNNEEDED) +{ fprintf(stderr, "blinding_next_pubkey called!\n"); abort(); } /* Generated stub for cupdate_different */ bool cupdate_different(struct gossip_store *gs UNNEEDED, const struct half_chan *hc UNNEEDED, const u8 *cupdate UNNEEDED) { fprintf(stderr, "cupdate_different called!\n"); abort(); } +/* Generated stub for ecdh */ +void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) +{ fprintf(stderr, "ecdh called!\n"); abort(); } /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } @@ -107,6 +128,9 @@ bool nannounce_different(struct gossip_store *gs UNNEEDED, const u8 *nannounce UNNEEDED, bool *only_missing_tlv UNNEEDED) { fprintf(stderr, "nannounce_different called!\n"); abort(); } +/* Generated stub for new_onionreply */ +struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED) +{ fprintf(stderr, "new_onionreply called!\n"); abort(); } /* Generated stub for new_reltimer_ */ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, const tal_t *ctx UNNEEDED, diff --git a/gossipd/test/run-check_node_announcement.c b/gossipd/test/run-check_node_announcement.c index 3c637a685eb4..87ff8f188869 100644 --- a/gossipd/test/run-check_node_announcement.c +++ b/gossipd/test/run-check_node_announcement.c @@ -1,10 +1,31 @@ #include "../gossip_generation.c" +#include #include +#include #include +#include #include #include /* AUTOGENERATED MOCKS START */ +/* Generated stub for blinding_hash_e_and_ss */ +void blinding_hash_e_and_ss(const struct pubkey *e UNNEEDED, + const struct secret *ss UNNEEDED, + struct sha256 *sha UNNEEDED) +{ fprintf(stderr, "blinding_hash_e_and_ss called!\n"); abort(); } +/* Generated stub for blinding_next_privkey */ +bool blinding_next_privkey(const struct privkey *e UNNEEDED, + const struct sha256 *h UNNEEDED, + struct privkey *next UNNEEDED) +{ fprintf(stderr, "blinding_next_privkey called!\n"); abort(); } +/* Generated stub for blinding_next_pubkey */ +bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, + const struct sha256 *h UNNEEDED, + struct pubkey *next UNNEEDED) +{ fprintf(stderr, "blinding_next_pubkey called!\n"); abort(); } +/* Generated stub for ecdh */ +void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) +{ fprintf(stderr, "ecdh called!\n"); abort(); } /* Generated stub for find_peer */ struct peer *find_peer(struct daemon *daemon UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "find_peer called!\n"); abort(); } @@ -53,6 +74,9 @@ void json_object_end(struct json_stream *js UNNEEDED) /* Generated stub for json_object_start */ void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) { fprintf(stderr, "json_object_start called!\n"); abort(); } +/* Generated stub for new_onionreply */ +struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED) +{ fprintf(stderr, "new_onionreply called!\n"); abort(); } /* Generated stub for new_reltimer_ */ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, const tal_t *ctx UNNEEDED, diff --git a/gossipd/test/run-crc32_of_update.c b/gossipd/test/run-crc32_of_update.c index 1dbf50df000a..86483e8a26a3 100644 --- a/gossipd/test/run-crc32_of_update.c +++ b/gossipd/test/run-crc32_of_update.c @@ -3,12 +3,30 @@ int unused_main(int argc, char *argv[]); #include "../queries.c" #include "../gossip_generation.c" #undef main +#include #include +#include #include +#include #include #include /* AUTOGENERATED MOCKS START */ +/* Generated stub for blinding_hash_e_and_ss */ +void blinding_hash_e_and_ss(const struct pubkey *e UNNEEDED, + const struct secret *ss UNNEEDED, + struct sha256 *sha UNNEEDED) +{ fprintf(stderr, "blinding_hash_e_and_ss called!\n"); abort(); } +/* Generated stub for blinding_next_privkey */ +bool blinding_next_privkey(const struct privkey *e UNNEEDED, + const struct sha256 *h UNNEEDED, + struct privkey *next UNNEEDED) +{ fprintf(stderr, "blinding_next_privkey called!\n"); abort(); } +/* Generated stub for blinding_next_pubkey */ +bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, + const struct sha256 *h UNNEEDED, + struct pubkey *next UNNEEDED) +{ fprintf(stderr, "blinding_next_pubkey called!\n"); abort(); } /* Generated stub for daemon_conn_read_next */ struct io_plan *daemon_conn_read_next(struct io_conn *conn UNNEEDED, struct daemon_conn *dc UNNEEDED) @@ -27,6 +45,9 @@ bigsize_t *decode_scid_query_flags(const tal_t *ctx UNNEEDED, /* Generated stub for decode_short_ids */ struct short_channel_id *decode_short_ids(const tal_t *ctx UNNEEDED, const u8 *encoded UNNEEDED) { fprintf(stderr, "decode_short_ids called!\n"); abort(); } +/* Generated stub for ecdh */ +void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) +{ fprintf(stderr, "ecdh called!\n"); abort(); } /* Generated stub for find_peer */ struct peer *find_peer(struct daemon *daemon UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "find_peer called!\n"); abort(); } @@ -86,6 +107,9 @@ void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UN /* Generated stub for master_badmsg */ void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg) { fprintf(stderr, "master_badmsg called!\n"); abort(); } +/* Generated stub for new_onionreply */ +struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED) +{ fprintf(stderr, "new_onionreply called!\n"); abort(); } /* Generated stub for new_reltimer_ */ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, const tal_t *ctx UNNEEDED, diff --git a/gossipd/test/run-extended-info.c b/gossipd/test/run-extended-info.c index 935dca7c33e0..bf0d460a82ed 100644 --- a/gossipd/test/run-extended-info.c +++ b/gossipd/test/run-extended-info.c @@ -4,9 +4,12 @@ #include "../queries.c" #include +#include #include +#include #include #include +#include #include #include @@ -15,6 +18,21 @@ #endif /* AUTOGENERATED MOCKS START */ +/* Generated stub for blinding_hash_e_and_ss */ +void blinding_hash_e_and_ss(const struct pubkey *e UNNEEDED, + const struct secret *ss UNNEEDED, + struct sha256 *sha UNNEEDED) +{ fprintf(stderr, "blinding_hash_e_and_ss called!\n"); abort(); } +/* Generated stub for blinding_next_privkey */ +bool blinding_next_privkey(const struct privkey *e UNNEEDED, + const struct sha256 *h UNNEEDED, + struct privkey *next UNNEEDED) +{ fprintf(stderr, "blinding_next_privkey called!\n"); abort(); } +/* Generated stub for blinding_next_pubkey */ +bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, + const struct sha256 *h UNNEEDED, + struct pubkey *next UNNEEDED) +{ fprintf(stderr, "blinding_next_pubkey called!\n"); abort(); } /* Generated stub for daemon_conn_read_next */ struct io_plan *daemon_conn_read_next(struct io_conn *conn UNNEEDED, struct daemon_conn *dc UNNEEDED) @@ -33,6 +51,9 @@ bigsize_t *decode_scid_query_flags(const tal_t *ctx UNNEEDED, /* Generated stub for decode_short_ids */ struct short_channel_id *decode_short_ids(const tal_t *ctx UNNEEDED, const u8 *encoded UNNEEDED) { fprintf(stderr, "decode_short_ids called!\n"); abort(); } +/* Generated stub for ecdh */ +void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) +{ fprintf(stderr, "ecdh called!\n"); abort(); } /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } @@ -72,6 +93,9 @@ void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UN /* Generated stub for master_badmsg */ void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg) { fprintf(stderr, "master_badmsg called!\n"); abort(); } +/* Generated stub for new_onionreply */ +struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED) +{ fprintf(stderr, "new_onionreply called!\n"); abort(); } /* Generated stub for peer_supplied_good_gossip */ void peer_supplied_good_gossip(struct peer *peer UNNEEDED, size_t amount UNNEEDED) { fprintf(stderr, "peer_supplied_good_gossip called!\n"); abort(); } diff --git a/gossipd/test/run-next_block_range.c b/gossipd/test/run-next_block_range.c index aad6a39699bd..0be48d45d7eb 100644 --- a/gossipd/test/run-next_block_range.c +++ b/gossipd/test/run-next_block_range.c @@ -1,12 +1,33 @@ #include "../seeker.c" #include +#include #include +#include #include +#include #include #include #include /* AUTOGENERATED MOCKS START */ +/* Generated stub for blinding_hash_e_and_ss */ +void blinding_hash_e_and_ss(const struct pubkey *e UNNEEDED, + const struct secret *ss UNNEEDED, + struct sha256 *sha UNNEEDED) +{ fprintf(stderr, "blinding_hash_e_and_ss called!\n"); abort(); } +/* Generated stub for blinding_next_privkey */ +bool blinding_next_privkey(const struct privkey *e UNNEEDED, + const struct sha256 *h UNNEEDED, + struct privkey *next UNNEEDED) +{ fprintf(stderr, "blinding_next_privkey called!\n"); abort(); } +/* Generated stub for blinding_next_pubkey */ +bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, + const struct sha256 *h UNNEEDED, + struct pubkey *next UNNEEDED) +{ fprintf(stderr, "blinding_next_pubkey called!\n"); abort(); } +/* Generated stub for ecdh */ +void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) +{ fprintf(stderr, "ecdh called!\n"); abort(); } /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } @@ -26,6 +47,9 @@ void json_object_end(struct json_stream *js UNNEEDED) /* Generated stub for json_object_start */ void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) { fprintf(stderr, "json_object_start called!\n"); abort(); } +/* Generated stub for new_onionreply */ +struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED) +{ fprintf(stderr, "new_onionreply called!\n"); abort(); } /* Generated stub for new_reltimer_ */ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, const tal_t *ctx UNNEEDED, diff --git a/gossipd/test/run-onion_message.c b/gossipd/test/run-onion_message.c new file mode 100644 index 000000000000..5289233dc4c4 --- /dev/null +++ b/gossipd/test/run-onion_message.c @@ -0,0 +1,580 @@ +int unused_main(int argc, char *argv[]); +#define main unused_main +#include "../gossipd.c" +#undef main +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if DEVELOPER +bool dev_suppress_gossip; + +/* Generated stub for dev_set_max_scids_encode_size */ +struct io_plan *dev_set_max_scids_encode_size(struct io_conn *conn UNNEEDED, + struct daemon *daemon UNNEEDED, + const u8 *msg UNNEEDED) +{ fprintf(stderr, "dev_set_max_scids_encode_size called!\n"); abort(); } +/* Generated stub for dump_memleak */ +bool dump_memleak(struct htable *memtable UNNEEDED, + void (*print)(const char *fmt UNNEEDED, ...)) +{ fprintf(stderr, "dump_memleak called!\n"); abort(); } +/* Generated stub for memleak_status_broken */ +void memleak_status_broken(const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "memleak_status_broken called!\n"); abort(); } +#endif + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for add_to_txout_failures */ +void add_to_txout_failures(struct routing_state *rstate UNNEEDED, + const struct short_channel_id *scid UNNEEDED) +{ fprintf(stderr, "add_to_txout_failures called!\n"); abort(); } +/* Generated stub for daemon_conn_new_ */ +struct daemon_conn *daemon_conn_new_(const tal_t *ctx UNNEEDED, int fd UNNEEDED, + struct io_plan *(*recv)(struct io_conn * UNNEEDED, + const u8 * UNNEEDED, + void *) UNNEEDED, + void (*outq_empty)(void *) UNNEEDED, + void *arg UNNEEDED) +{ fprintf(stderr, "daemon_conn_new_ called!\n"); abort(); } +/* Generated stub for daemon_conn_read_next */ +struct io_plan *daemon_conn_read_next(struct io_conn *conn UNNEEDED, + struct daemon_conn *dc UNNEEDED) +{ fprintf(stderr, "daemon_conn_read_next called!\n"); abort(); } +/* Generated stub for daemon_conn_send */ +void daemon_conn_send(struct daemon_conn *dc UNNEEDED, const u8 *msg UNNEEDED) +{ fprintf(stderr, "daemon_conn_send called!\n"); abort(); } +/* Generated stub for daemon_conn_send_fd */ +void daemon_conn_send_fd(struct daemon_conn *dc UNNEEDED, int fd UNNEEDED) +{ fprintf(stderr, "daemon_conn_send_fd called!\n"); abort(); } +/* Generated stub for daemon_shutdown */ +void daemon_shutdown(void) +{ fprintf(stderr, "daemon_shutdown called!\n"); abort(); } +/* Generated stub for ecdh */ +void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) +{ fprintf(stderr, "ecdh called!\n"); abort(); } +/* Generated stub for ecdh_hsmd_setup */ +void ecdh_hsmd_setup(int hsm_fd UNNEEDED, + void (*failed)(enum status_failreason UNNEEDED, + const char *fmt UNNEEDED, ...)) +{ fprintf(stderr, "ecdh_hsmd_setup called!\n"); abort(); } +/* Generated stub for first_chan */ +struct chan *first_chan(const struct node *node UNNEEDED, struct chan_map_iter *i UNNEEDED) +{ fprintf(stderr, "first_chan called!\n"); abort(); } +/* Generated stub for fmt_wireaddr_without_port */ +char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) +{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } +/* Generated stub for free_chan */ +void free_chan(struct routing_state *rstate UNNEEDED, struct chan *chan UNNEEDED) +{ fprintf(stderr, "free_chan called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_addgossip */ +bool fromwire_gossipd_addgossip(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **msg UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_addgossip called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_dev_set_time */ +bool fromwire_gossipd_dev_set_time(const void *p UNNEEDED, u32 *dev_gossip_time UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_dev_set_time called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_dev_suppress */ +bool fromwire_gossipd_dev_suppress(const void *p UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_dev_suppress called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_get_addrs */ +bool fromwire_gossipd_get_addrs(const void *p UNNEEDED, struct node_id *id UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_get_addrs called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_get_stripped_cupdate */ +bool fromwire_gossipd_get_stripped_cupdate(const void *p UNNEEDED, struct short_channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_get_stripped_cupdate called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_get_txout_reply */ +bool fromwire_gossipd_get_txout_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, struct amount_sat *satoshis UNNEEDED, u8 **outscript UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_get_txout_reply called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_get_update */ +bool fromwire_gossipd_get_update(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_get_update called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_init */ +bool fromwire_gossipd_init(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, const struct chainparams **chainparams UNNEEDED, struct feature_set **our_features UNNEEDED, struct node_id *id UNNEEDED, u8 rgb[3] UNNEEDED, u8 alias[32] UNNEEDED, struct wireaddr **announcable UNNEEDED, u32 **dev_gossip_time UNNEEDED, bool *dev_fast_gossip UNNEEDED, bool *dev_fast_gossip_prune UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_init called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_local_channel_announcement */ +bool fromwire_gossipd_local_channel_announcement(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **cannount UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_local_channel_announcement called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_local_channel_close */ +bool fromwire_gossipd_local_channel_close(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_local_channel_close called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_new_blockheight */ +bool fromwire_gossipd_new_blockheight(const void *p UNNEEDED, u32 *blockheight UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_new_blockheight called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_new_lease_rates */ +bool fromwire_gossipd_new_lease_rates(const void *p UNNEEDED, struct lease_rates *rates UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_new_lease_rates called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_new_peer */ +bool fromwire_gossipd_new_peer(const void *p UNNEEDED, struct node_id *id UNNEEDED, bool *gossip_queries_feature UNNEEDED, bool *initial_routing_sync UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_new_peer called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_outpoint_spent */ +bool fromwire_gossipd_outpoint_spent(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_outpoint_spent called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_send_onionmsg */ +bool fromwire_gossipd_send_onionmsg(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, bool *obs2 UNNEEDED, struct node_id *id UNNEEDED, u8 **onion UNNEEDED, struct pubkey *blinding UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_send_onionmsg called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr_array */ +struct wireaddr *fromwire_wireaddr_array(const tal_t *ctx UNNEEDED, const u8 *ser UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr_array called!\n"); abort(); } +/* Generated stub for get_node */ +struct node *get_node(struct routing_state *rstate UNNEEDED, + const struct node_id *id UNNEEDED) +{ fprintf(stderr, "get_node called!\n"); abort(); } +/* Generated stub for gossip_store_compact */ +bool gossip_store_compact(struct gossip_store *gs UNNEEDED) +{ fprintf(stderr, "gossip_store_compact called!\n"); abort(); } +/* Generated stub for gossip_store_get */ +const u8 *gossip_store_get(const tal_t *ctx UNNEEDED, + struct gossip_store *gs UNNEEDED, + u64 offset UNNEEDED) +{ fprintf(stderr, "gossip_store_get called!\n"); abort(); } +/* Generated stub for gossip_store_load */ +u32 gossip_store_load(struct routing_state *rstate UNNEEDED, struct gossip_store *gs UNNEEDED) +{ fprintf(stderr, "gossip_store_load called!\n"); abort(); } +/* Generated stub for gossip_store_readonly_fd */ +int gossip_store_readonly_fd(struct gossip_store *gs UNNEEDED) +{ fprintf(stderr, "gossip_store_readonly_fd called!\n"); abort(); } +/* Generated stub for gossip_time_now */ +struct timeabs gossip_time_now(const struct routing_state *rstate UNNEEDED) +{ fprintf(stderr, "gossip_time_now called!\n"); abort(); } +/* Generated stub for gossipd_peerd_wire_name */ +const char *gossipd_peerd_wire_name(int e UNNEEDED) +{ fprintf(stderr, "gossipd_peerd_wire_name called!\n"); abort(); } +/* Generated stub for handle_channel_announcement */ +u8 *handle_channel_announcement(struct routing_state *rstate UNNEEDED, + const u8 *announce TAKES UNNEEDED, + u32 current_blockheight UNNEEDED, + const struct short_channel_id **scid UNNEEDED, + struct peer *peer UNNEEDED) +{ fprintf(stderr, "handle_channel_announcement called!\n"); abort(); } +/* Generated stub for handle_channel_update */ +u8 *handle_channel_update(struct routing_state *rstate UNNEEDED, const u8 *update TAKES UNNEEDED, + struct peer *peer UNNEEDED, + struct short_channel_id *unknown_scid UNNEEDED, + bool force UNNEEDED) +{ fprintf(stderr, "handle_channel_update called!\n"); abort(); } +/* Generated stub for handle_local_channel_update */ +bool handle_local_channel_update(struct daemon *daemon UNNEEDED, + const struct node_id *src UNNEEDED, + const u8 *msg UNNEEDED) +{ fprintf(stderr, "handle_local_channel_update called!\n"); abort(); } +/* Generated stub for handle_node_announcement */ +u8 *handle_node_announcement(struct routing_state *rstate UNNEEDED, const u8 *node UNNEEDED, + struct peer *peer UNNEEDED, bool *was_unknown UNNEEDED) +{ fprintf(stderr, "handle_node_announcement called!\n"); abort(); } +/* Generated stub for handle_pending_cannouncement */ +bool handle_pending_cannouncement(struct daemon *daemon UNNEEDED, + struct routing_state *rstate UNNEEDED, + const struct short_channel_id *scid UNNEEDED, + const struct amount_sat sat UNNEEDED, + const u8 *txscript UNNEEDED) +{ fprintf(stderr, "handle_pending_cannouncement called!\n"); abort(); } +/* Generated stub for handle_query_channel_range */ +const u8 *handle_query_channel_range(struct peer *peer UNNEEDED, const u8 *msg UNNEEDED) +{ fprintf(stderr, "handle_query_channel_range called!\n"); abort(); } +/* Generated stub for handle_query_short_channel_ids */ +const u8 *handle_query_short_channel_ids(struct peer *peer UNNEEDED, const u8 *msg UNNEEDED) +{ fprintf(stderr, "handle_query_short_channel_ids called!\n"); abort(); } +/* Generated stub for handle_reply_channel_range */ +const u8 *handle_reply_channel_range(struct peer *peer UNNEEDED, const u8 *msg UNNEEDED) +{ fprintf(stderr, "handle_reply_channel_range called!\n"); abort(); } +/* Generated stub for handle_reply_short_channel_ids_end */ +const u8 *handle_reply_short_channel_ids_end(struct peer *peer UNNEEDED, const u8 *msg UNNEEDED) +{ fprintf(stderr, "handle_reply_short_channel_ids_end called!\n"); abort(); } +/* Generated stub for json_add_member */ +void json_add_member(struct json_stream *js UNNEEDED, + const char *fieldname UNNEEDED, + bool quote UNNEEDED, + const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "json_add_member called!\n"); abort(); } +/* Generated stub for json_member_direct */ +char *json_member_direct(struct json_stream *js UNNEEDED, + const char *fieldname UNNEEDED, size_t extra UNNEEDED) +{ fprintf(stderr, "json_member_direct called!\n"); abort(); } +/* Generated stub for json_object_end */ +void json_object_end(struct json_stream *js UNNEEDED) +{ fprintf(stderr, "json_object_end called!\n"); abort(); } +/* Generated stub for json_object_start */ +void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) +{ fprintf(stderr, "json_object_start called!\n"); abort(); } +/* Generated stub for master_badmsg */ +void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg) +{ fprintf(stderr, "master_badmsg called!\n"); abort(); } +/* Generated stub for maybe_send_own_node_announce */ +void maybe_send_own_node_announce(struct daemon *daemon UNNEEDED, bool startup UNNEEDED) +{ fprintf(stderr, "maybe_send_own_node_announce called!\n"); abort(); } +/* Generated stub for maybe_send_query_responses */ +void maybe_send_query_responses(struct peer *peer UNNEEDED) +{ fprintf(stderr, "maybe_send_query_responses called!\n"); abort(); } +/* Generated stub for memleak_find_allocations */ +struct htable *memleak_find_allocations(const tal_t *ctx UNNEEDED, + const void *exclude1 UNNEEDED, + const void *exclude2 UNNEEDED) +{ fprintf(stderr, "memleak_find_allocations called!\n"); abort(); } +/* Generated stub for memleak_remove_region */ +void memleak_remove_region(struct htable *memtable UNNEEDED, + const void *p UNNEEDED, size_t bytelen UNNEEDED) +{ fprintf(stderr, "memleak_remove_region called!\n"); abort(); } +/* Generated stub for new_onionreply */ +struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED) +{ fprintf(stderr, "new_onionreply called!\n"); abort(); } +/* Generated stub for new_reltimer_ */ +struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, + const tal_t *ctx UNNEEDED, + struct timerel expire UNNEEDED, + void (*cb)(void *) UNNEEDED, void *arg UNNEEDED) +{ fprintf(stderr, "new_reltimer_ called!\n"); abort(); } +/* Generated stub for new_routing_state */ +struct routing_state *new_routing_state(const tal_t *ctx UNNEEDED, + const struct node_id *local_id UNNEEDED, + struct list_head *peers UNNEEDED, + struct timers *timers UNNEEDED, + const u32 *dev_gossip_time TAKES UNNEEDED, + bool dev_fast_gossip UNNEEDED, + bool dev_fast_gossip_prune UNNEEDED) +{ fprintf(stderr, "new_routing_state called!\n"); abort(); } +/* Generated stub for new_seeker */ +struct seeker *new_seeker(struct daemon *daemon UNNEEDED) +{ fprintf(stderr, "new_seeker called!\n"); abort(); } +/* Generated stub for next_chan */ +struct chan *next_chan(const struct node *node UNNEEDED, struct chan_map_iter *i UNNEEDED) +{ fprintf(stderr, "next_chan called!\n"); abort(); } +/* Generated stub for notleak_ */ +void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) +{ fprintf(stderr, "notleak_ called!\n"); abort(); } +/* Generated stub for query_unknown_channel */ +void query_unknown_channel(struct daemon *daemon UNNEEDED, + struct peer *peer UNNEEDED, + const struct short_channel_id *id UNNEEDED) +{ fprintf(stderr, "query_unknown_channel called!\n"); abort(); } +/* Generated stub for query_unknown_node */ +void query_unknown_node(struct seeker *seeker UNNEEDED, struct peer *peer UNNEEDED) +{ fprintf(stderr, "query_unknown_node called!\n"); abort(); } +/* Generated stub for refresh_local_channel */ +void refresh_local_channel(struct daemon *daemon UNNEEDED, + struct local_chan *local_chan UNNEEDED, + bool even_if_identical UNNEEDED) +{ fprintf(stderr, "refresh_local_channel called!\n"); abort(); } +/* Generated stub for remove_channel_from_store */ +void remove_channel_from_store(struct routing_state *rstate UNNEEDED, + struct chan *chan UNNEEDED) +{ fprintf(stderr, "remove_channel_from_store called!\n"); abort(); } +/* Generated stub for remove_unknown_scid */ +bool remove_unknown_scid(struct seeker *seeker UNNEEDED, + const struct short_channel_id *scid UNNEEDED, + bool found UNNEEDED) +{ fprintf(stderr, "remove_unknown_scid called!\n"); abort(); } +/* Generated stub for route_prune */ +void route_prune(struct routing_state *rstate UNNEEDED) +{ fprintf(stderr, "route_prune called!\n"); abort(); } +/* Generated stub for routing_add_private_channel */ +bool routing_add_private_channel(struct routing_state *rstate UNNEEDED, + const struct peer *peer UNNEEDED, + const u8 *msg UNNEEDED, u64 index UNNEEDED) +{ fprintf(stderr, "routing_add_private_channel called!\n"); abort(); } +/* Generated stub for sanitize_error */ +char *sanitize_error(const tal_t *ctx UNNEEDED, const u8 *errmsg UNNEEDED, + struct channel_id *channel_id UNNEEDED) +{ fprintf(stderr, "sanitize_error called!\n"); abort(); } +/* Generated stub for seeker_setup_peer_gossip */ +void seeker_setup_peer_gossip(struct seeker *seeker UNNEEDED, struct peer *peer UNNEEDED) +{ fprintf(stderr, "seeker_setup_peer_gossip called!\n"); abort(); } +/* Generated stub for status_failed */ +void status_failed(enum status_failreason code UNNEEDED, + const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "status_failed called!\n"); abort(); } +/* Generated stub for status_fmt */ +void status_fmt(enum log_level level UNNEEDED, + const struct node_id *peer UNNEEDED, + const char *fmt UNNEEDED, ...) + +{ fprintf(stderr, "status_fmt called!\n"); abort(); } +/* Generated stub for status_setup_async */ +void status_setup_async(struct daemon_conn *master UNNEEDED) +{ fprintf(stderr, "status_setup_async called!\n"); abort(); } +/* Generated stub for subdaemon_setup */ +void subdaemon_setup(int argc UNNEEDED, char *argv[]) +{ fprintf(stderr, "subdaemon_setup called!\n"); abort(); } +/* Generated stub for timer_expired */ +void timer_expired(tal_t *ctx UNNEEDED, struct timer *timer UNNEEDED) +{ fprintf(stderr, "timer_expired called!\n"); abort(); } +/* Generated stub for towire_gossipd_addgossip_reply */ +u8 *towire_gossipd_addgossip_reply(const tal_t *ctx UNNEEDED, const wirestring *err UNNEEDED) +{ fprintf(stderr, "towire_gossipd_addgossip_reply called!\n"); abort(); } +/* Generated stub for towire_gossipd_dev_compact_store_reply */ +u8 *towire_gossipd_dev_compact_store_reply(const tal_t *ctx UNNEEDED, bool success UNNEEDED) +{ fprintf(stderr, "towire_gossipd_dev_compact_store_reply called!\n"); abort(); } +/* Generated stub for towire_gossipd_dev_memleak_reply */ +u8 *towire_gossipd_dev_memleak_reply(const tal_t *ctx UNNEEDED, bool leak UNNEEDED) +{ fprintf(stderr, "towire_gossipd_dev_memleak_reply called!\n"); abort(); } +/* Generated stub for towire_gossipd_get_addrs_reply */ +u8 *towire_gossipd_get_addrs_reply(const tal_t *ctx UNNEEDED, const struct wireaddr *addrs UNNEEDED) +{ fprintf(stderr, "towire_gossipd_get_addrs_reply called!\n"); abort(); } +/* Generated stub for towire_gossipd_get_stripped_cupdate_reply */ +u8 *towire_gossipd_get_stripped_cupdate_reply(const tal_t *ctx UNNEEDED, const u8 *stripped_update UNNEEDED) +{ fprintf(stderr, "towire_gossipd_get_stripped_cupdate_reply called!\n"); abort(); } +/* Generated stub for towire_gossipd_get_txout */ +u8 *towire_gossipd_get_txout(const tal_t *ctx UNNEEDED, const struct short_channel_id *short_channel_id UNNEEDED) +{ fprintf(stderr, "towire_gossipd_get_txout called!\n"); abort(); } +/* Generated stub for towire_gossipd_get_update_reply */ +u8 *towire_gossipd_get_update_reply(const tal_t *ctx UNNEEDED, const u8 *update UNNEEDED) +{ fprintf(stderr, "towire_gossipd_get_update_reply called!\n"); abort(); } +/* Generated stub for towire_gossipd_got_onionmsg_to_us */ +u8 *towire_gossipd_got_onionmsg_to_us(const tal_t *ctx UNNEEDED, bool obs2 UNNEEDED, const struct pubkey *node_alias UNNEEDED, const struct secret *self_id UNNEEDED, const struct pubkey *reply_blinding UNNEEDED, const struct pubkey *reply_first_node UNNEEDED, const struct onionmsg_path **reply_path UNNEEDED, const u8 *rawmsg UNNEEDED) +{ fprintf(stderr, "towire_gossipd_got_onionmsg_to_us called!\n"); abort(); } +/* Generated stub for towire_gossipd_init_reply */ +u8 *towire_gossipd_init_reply(const tal_t *ctx UNNEEDED) +{ fprintf(stderr, "towire_gossipd_init_reply called!\n"); abort(); } +/* Generated stub for towire_gossipd_new_peer_reply */ +u8 *towire_gossipd_new_peer_reply(const tal_t *ctx UNNEEDED, bool success UNNEEDED, const struct gossip_state *gs UNNEEDED) +{ fprintf(stderr, "towire_gossipd_new_peer_reply called!\n"); abort(); } +/* Generated stub for towire_warningfmt */ +u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, + const struct channel_id *channel UNNEEDED, + const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "towire_warningfmt called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +/* Updated each time, as we pretend to be Alice, Bob, Carol */ +static const struct privkey *mykey; + +static void test_ecdh(const struct pubkey *point, struct secret *ss) +{ + if (secp256k1_ecdh(secp256k1_ctx, ss->data, &point->pubkey, + mykey->secret.data, NULL, NULL) != 1) + abort(); +} + +static void json_strfield(const char *name, const char *val) +{ + printf("\t\"%s\": \"%s\",\n", name, val); +} + +static void json_onionmsg_payload(const struct tlv_obs2_onionmsg_payload *om) +{ + if (om->reply_path) { + printf("\t\"reply_path\": {\n"); + json_strfield("first_node_id", + type_to_string(tmpctx, struct pubkey, + &om->reply_path->first_node_id)); + json_strfield("blinding", + type_to_string(tmpctx, struct pubkey, + &om->reply_path->blinding)); + printf("\t\"path\": [\n"); + for (size_t i = 0; i < tal_count(om->reply_path->path); i++) { + json_strfield("node_id", + type_to_string(tmpctx, struct pubkey, + &om->reply_path->path[i]->node_id)); + json_strfield("encrypted_recipient_data", + tal_hex(tmpctx, + om->reply_path->path[i]->encrypted_recipient_data)); + } + printf("]}\n"); + } + if (om->invoice) + json_strfield("invoice", tal_hex(tmpctx, om->invoice)); + if (om->invoice_request) + json_strfield("invoice_request", + tal_hex(tmpctx, om->invoice_request)); + if (om->invoice_error) + json_strfield("invoice_error", + tal_hex(tmpctx, om->invoice_error)); +} + +/* Return next onion (and updates blinding), or NULL */ +static u8 *json_test(const char *testname, + const u8 *data, + const struct privkey *me, + const struct privkey *blinding_priv, + struct pubkey *blinding) +{ + struct pubkey my_id, next_node; + struct secret ss, onion_ss; + struct pubkey ephemeral; + struct route_step *rs; + const u8 *cursor; + size_t max, maxlen; + struct onionpacket *op; + struct tlv_obs2_onionmsg_payload *om; + + op = parse_onionpacket(tmpctx, data, tal_bytelen(data), NULL); + assert(op); + + pubkey_from_privkey(me, &my_id); + printf("{"); + json_strfield("test name", testname); + json_strfield("reader_privkey", + type_to_string(tmpctx, struct privkey, me)); + json_strfield("reader_id", + type_to_string(tmpctx, struct pubkey, &my_id)); + + if (blinding_priv) + json_strfield("blinding_privkey", + type_to_string(tmpctx, struct privkey, + blinding_priv)); + json_strfield("blinding", + type_to_string(tmpctx, struct pubkey, blinding)); + printf("\"onionmsg\": {\n"); + json_strfield("raw", tal_hex(tmpctx, data)); + json_strfield("version", tal_fmt(tmpctx, "%i", op->version)); + json_strfield("public_key", + type_to_string(tmpctx, struct pubkey, &op->ephemeralkey)); + json_strfield("hop_payloads", + tal_hex(tmpctx, op->routinginfo)); + json_strfield("hmac", + tal_hexstr(tmpctx, &op->hmac, sizeof(op->hmac))); + printf("},\n"); + + ephemeral = op->ephemeralkey; + + /* Set this for test_ecdh */ + mykey = me; + assert(unblind_onion(blinding, test_ecdh, &ephemeral, &ss)); + json_strfield("ECDH shared secret", + type_to_string(tmpctx, struct secret, &ss)); + /* Reproduce internal calc from unblind_onion */ + { + struct secret hmac; + subkey_from_hmac("blinded_node_id", &ss, &hmac); + json_strfield("HMAC256(\\\"blinded_node_id\\\", ss(i)) * k(i)", + type_to_string(tmpctx, struct secret, &hmac)); + } + json_strfield("Tweaked onion pubkey", + type_to_string(tmpctx, struct pubkey, &ephemeral)); + + /* Now get onion shared secret and parse it. */ + test_ecdh(&ephemeral, &onion_ss); + json_strfield("onion shared secret", + type_to_string(tmpctx, struct secret, &onion_ss)); + rs = process_onionpacket(tmpctx, op, &onion_ss, NULL, 0, false); + assert(rs); + + printf("\"onion contents\": {\n"); + json_strfield("raw", tal_hex(tmpctx, rs->raw_payload)); + + cursor = rs->raw_payload; + max = tal_bytelen(rs->raw_payload); + maxlen = fromwire_bigsize(&cursor, &max); + json_strfield("length", tal_fmt(tmpctx, "%zu", maxlen)); + json_strfield("rawtlv", tal_hexstr(tmpctx, cursor, maxlen)); + json_strfield("hmac", tal_hexstr(tmpctx, rs->next->hmac.bytes, + sizeof(rs->next->hmac.bytes))); + om = tlv_obs2_onionmsg_payload_new(tmpctx); + assert(fromwire_obs2_onionmsg_payload(&cursor, &maxlen, om)); + + json_onionmsg_payload(om); + + /* We expect one of these. */ + assert(om->enctlv); + + printf("\t\"encrypted_data_tlv\": {\n"); + json_strfield("raw", tal_hex(tmpctx, om->enctlv)); + + if (rs->nextcase == ONION_END) { + struct secret *self_id; + struct pubkey alias; + assert(decrypt_obs2_final_enctlv(tmpctx, + blinding, &ss, + om->enctlv, + &my_id, &alias, &self_id)); + if (self_id) { + json_strfield("self_id", + type_to_string(tmpctx, struct secret, + self_id)); + } + printf("}\n"); + return NULL; + } else { + assert(decrypt_obs2_enctlv(blinding, &ss, om->enctlv, &next_node, + blinding)); + json_strfield("next_node", + type_to_string(tmpctx, struct pubkey, &next_node)); + json_strfield("next_blinding", + type_to_string(tmpctx, struct pubkey, + blinding)); + printf("}"); + printf("},\n"); + return serialize_onionpacket(tmpctx, rs->next); + } +} + +int main(int argc, char *argv[]) +{ + struct onionpacket *op; + u8 *data; + struct privkey alice, bob, carol, dave, blinding_priv; + struct pubkey alice_id, bob_id, carol_id, dave_id; + struct pubkey blinding; + + common_setup(argv[0]); + + memset(&alice, 'A', sizeof(alice)); + memset(&bob, 'B', sizeof(bob)); + memset(&carol, 'C', sizeof(carol)); + memset(&dave, 'D', sizeof(dave)); + pubkey_from_privkey(&alice, &alice_id); + pubkey_from_privkey(&bob, &bob_id); + pubkey_from_privkey(&carol, &carol_id); + pubkey_from_privkey(&dave, &dave_id); + + /* ThomasH sends via email: + * + * { + * "version":0, + * "public_key": + * "0256b328b30c8bf5839e24058747879408bdb36241dc9c2e7c619faa12b2920967", + * "hop_payloads": + * "37df67dcefdb678725cb8074d3224dfe235ba3f22f71ac8a2c9d1398b1175295b1dd3f14c02d698021e8a8856637306c6f195e01494eb8dc636b4462367533a84786b8592e580086cdf0f1c58b77eb68703a2fb82ecc2e91307a25b6d5e4045174551b1c867264d3905e4f05b2e5bcfed7e7276660bf7e956bce5afa395e7e4c15883b856bc93dd9d6a968838ef51314d38dd41e5ab84b8846dca3c61d87e55780e7a7da336a965a4652263413cdef41daa68f7bb7cd4d566c19a1c4eece369c47e604575f38e7a246a985c3441b60ae33c564395bb7a4bbe28325ccdb07503285dacf90b5e09f4e455fb42459741f9d497000298b99f1e70adc28f59a1be85a96952f27b6a6c5d6a08822b4f5cae05daa6c2ce2f8ca5fdd4e8f0df46b94791b3159fe8eace11bcf8d58be425b49ce2b47c007affefd5cea785c1996ad805f8c8c5ca79f15ab26e2bd4080b1d74328e7ce5bd2a579c71a6bd25f33f2ce475a2cfbe67ed1f4eb8fbd86920f41d573488abe059166aabbc3be187c435423ead6a5473994e0246efe76e419893aa2d7566b2645f3496d97585de9c92b8c5a5226398cc459ce84abc02fe2b45b5ecaf21961730d4a34bbe6fdfe720e71e3d81a494c01080d8039360d534c6ee5a3c47a1874e526969add9126b30d9192f85ba45bcfd7029cc7560f0e25e14b5deaa805360c4967705e85325ac055922863470f5397e8404022488caebf9204acd6cb02a11088aebf7e497b4ff1172f0a9c6bf980914cc4eb42fc78b457add549abf1134f84922b217502938b42d10b35079f44c5168d4c3e9fe7ca8094ef72ed73ef84f1d3530b6b3545f9f4f013e7e8cbcf2619f57754a7380ce6a9532ee14c55990faa43df6c09530a314b5f4ce597f5ec9b776e8597ce258ac47dac43bd3ac9e52788ff3a66b7dc07cd1bc3e6d197339d85fa8d3d6c3054dd1a5e416c714b544de6eb55209e40e3cac412a51748370160d2d73b6d97abd62f7bae70df27cd199c511fa693019c5717d471e934906b98cd974fda4dd1cb5e2d721044a0be2bdf24d0971e09f2f39488fe389fc5230699b4df7cec7447e5be4ea49bd7c3fe1a5ec7358510dc1dd9c1a8da68c0863188d80549e49f7c00f57d2009b2427b2aed1569603fc247734039469f9fdf3ddd3a22fa95c5d8066a468327a02b474c9915419af82c8edc67686984767fe7885207c6820f6c2e57cb8fd0bcb9981ebc8065c74e970a5d593c3b73ee25a0877ca096a9f7edfee6d43bd817c7d415fea9abb6f206c61aa36942df9318762a76b9da26d0d41a0ae9eee042a175f82dc134bf6f2d46a218db358d6852940e6e30df4a58ac6cb409e7ce99afe1e3f42768bd617af4d0a235d0ba0dd5075f9cc091784395d30e7e42d4e006db21bea9b45d1f122b75c051e84e2281573ef54ebad053218fff0cc28ea89a06adc218d4134f407654990592e75462f5ee4a463c1e46425222d48761162da8049613cafd7ecc52ff8024e9d58512b958e3a3d12dede84e1441247700bca0f992875349448b430683c756438fd4e91f3d44f3cf624ed21f3c63cf92615ecc201d0cd3159b1b3fccd8f29d2daba9ac5ba87b1dd2f83323a2b2d3176b803ce9c7bdc4bae615925eb22a213df1eeb2f8ff95586536caf042d565984aacf1425a120a5d8d7a9cbb70bf4852e116b89ff5b198d672220af2be4246372e7c3836cf50d732212a3e3346ff92873ace57fa687b2b1aab3e8dc6cb9f93f865d998cff0a1680d9012a9597c90a070e525f66226cc287814f4ac4157b15a0b25aa110946cd69fd404fafd5656669bfd1d9e509eabc004c5a", + * "hmac": "564bb85911bea8f90d306f4acdafa1c0887619ac72606b11e6b2765734d810ac" + * } + */ + op = tal(tmpctx, struct onionpacket); + op->version = 0; + assert(pubkey_from_hexstr("0256b328b30c8bf5839e24058747879408bdb36241dc9c2e7c619faa12b2920967", strlen("0256b328b30c8bf5839e24058747879408bdb36241dc9c2e7c619faa12b2920967"), &op->ephemeralkey)); + assert(hex_decode("564bb85911bea8f90d306f4acdafa1c0887619ac72606b11e6b2765734d810ac", + strlen("564bb85911bea8f90d306f4acdafa1c0887619ac72606b11e6b2765734d810ac"), + &op->hmac, sizeof(op->hmac))); + op->routinginfo = tal_hexdata(op, "37df67dcefdb678725cb8074d3224dfe235ba3f22f71ac8a2c9d1398b1175295b1dd3f14c02d698021e8a8856637306c6f195e01494eb8dc636b4462367533a84786b8592e580086cdf0f1c58b77eb68703a2fb82ecc2e91307a25b6d5e4045174551b1c867264d3905e4f05b2e5bcfed7e7276660bf7e956bce5afa395e7e4c15883b856bc93dd9d6a968838ef51314d38dd41e5ab84b8846dca3c61d87e55780e7a7da336a965a4652263413cdef41daa68f7bb7cd4d566c19a1c4eece369c47e604575f38e7a246a985c3441b60ae33c564395bb7a4bbe28325ccdb07503285dacf90b5e09f4e455fb42459741f9d497000298b99f1e70adc28f59a1be85a96952f27b6a6c5d6a08822b4f5cae05daa6c2ce2f8ca5fdd4e8f0df46b94791b3159fe8eace11bcf8d58be425b49ce2b47c007affefd5cea785c1996ad805f8c8c5ca79f15ab26e2bd4080b1d74328e7ce5bd2a579c71a6bd25f33f2ce475a2cfbe67ed1f4eb8fbd86920f41d573488abe059166aabbc3be187c435423ead6a5473994e0246efe76e419893aa2d7566b2645f3496d97585de9c92b8c5a5226398cc459ce84abc02fe2b45b5ecaf21961730d4a34bbe6fdfe720e71e3d81a494c01080d8039360d534c6ee5a3c47a1874e526969add9126b30d9192f85ba45bcfd7029cc7560f0e25e14b5deaa805360c4967705e85325ac055922863470f5397e8404022488caebf9204acd6cb02a11088aebf7e497b4ff1172f0a9c6bf980914cc4eb42fc78b457add549abf1134f84922b217502938b42d10b35079f44c5168d4c3e9fe7ca8094ef72ed73ef84f1d3530b6b3545f9f4f013e7e8cbcf2619f57754a7380ce6a9532ee14c55990faa43df6c09530a314b5f4ce597f5ec9b776e8597ce258ac47dac43bd3ac9e52788ff3a66b7dc07cd1bc3e6d197339d85fa8d3d6c3054dd1a5e416c714b544de6eb55209e40e3cac412a51748370160d2d73b6d97abd62f7bae70df27cd199c511fa693019c5717d471e934906b98cd974fda4dd1cb5e2d721044a0be2bdf24d0971e09f2f39488fe389fc5230699b4df7cec7447e5be4ea49bd7c3fe1a5ec7358510dc1dd9c1a8da68c0863188d80549e49f7c00f57d2009b2427b2aed1569603fc247734039469f9fdf3ddd3a22fa95c5d8066a468327a02b474c9915419af82c8edc67686984767fe7885207c6820f6c2e57cb8fd0bcb9981ebc8065c74e970a5d593c3b73ee25a0877ca096a9f7edfee6d43bd817c7d415fea9abb6f206c61aa36942df9318762a76b9da26d0d41a0ae9eee042a175f82dc134bf6f2d46a218db358d6852940e6e30df4a58ac6cb409e7ce99afe1e3f42768bd617af4d0a235d0ba0dd5075f9cc091784395d30e7e42d4e006db21bea9b45d1f122b75c051e84e2281573ef54ebad053218fff0cc28ea89a06adc218d4134f407654990592e75462f5ee4a463c1e46425222d48761162da8049613cafd7ecc52ff8024e9d58512b958e3a3d12dede84e1441247700bca0f992875349448b430683c756438fd4e91f3d44f3cf624ed21f3c63cf92615ecc201d0cd3159b1b3fccd8f29d2daba9ac5ba87b1dd2f83323a2b2d3176b803ce9c7bdc4bae615925eb22a213df1eeb2f8ff95586536caf042d565984aacf1425a120a5d8d7a9cbb70bf4852e116b89ff5b198d672220af2be4246372e7c3836cf50d732212a3e3346ff92873ace57fa687b2b1aab3e8dc6cb9f93f865d998cff0a1680d9012a9597c90a070e525f66226cc287814f4ac4157b15a0b25aa110946cd69fd404fafd5656669bfd1d9e509eabc004c5a", + strlen("37df67dcefdb678725cb8074d3224dfe235ba3f22f71ac8a2c9d1398b1175295b1dd3f14c02d698021e8a8856637306c6f195e01494eb8dc636b4462367533a84786b8592e580086cdf0f1c58b77eb68703a2fb82ecc2e91307a25b6d5e4045174551b1c867264d3905e4f05b2e5bcfed7e7276660bf7e956bce5afa395e7e4c15883b856bc93dd9d6a968838ef51314d38dd41e5ab84b8846dca3c61d87e55780e7a7da336a965a4652263413cdef41daa68f7bb7cd4d566c19a1c4eece369c47e604575f38e7a246a985c3441b60ae33c564395bb7a4bbe28325ccdb07503285dacf90b5e09f4e455fb42459741f9d497000298b99f1e70adc28f59a1be85a96952f27b6a6c5d6a08822b4f5cae05daa6c2ce2f8ca5fdd4e8f0df46b94791b3159fe8eace11bcf8d58be425b49ce2b47c007affefd5cea785c1996ad805f8c8c5ca79f15ab26e2bd4080b1d74328e7ce5bd2a579c71a6bd25f33f2ce475a2cfbe67ed1f4eb8fbd86920f41d573488abe059166aabbc3be187c435423ead6a5473994e0246efe76e419893aa2d7566b2645f3496d97585de9c92b8c5a5226398cc459ce84abc02fe2b45b5ecaf21961730d4a34bbe6fdfe720e71e3d81a494c01080d8039360d534c6ee5a3c47a1874e526969add9126b30d9192f85ba45bcfd7029cc7560f0e25e14b5deaa805360c4967705e85325ac055922863470f5397e8404022488caebf9204acd6cb02a11088aebf7e497b4ff1172f0a9c6bf980914cc4eb42fc78b457add549abf1134f84922b217502938b42d10b35079f44c5168d4c3e9fe7ca8094ef72ed73ef84f1d3530b6b3545f9f4f013e7e8cbcf2619f57754a7380ce6a9532ee14c55990faa43df6c09530a314b5f4ce597f5ec9b776e8597ce258ac47dac43bd3ac9e52788ff3a66b7dc07cd1bc3e6d197339d85fa8d3d6c3054dd1a5e416c714b544de6eb55209e40e3cac412a51748370160d2d73b6d97abd62f7bae70df27cd199c511fa693019c5717d471e934906b98cd974fda4dd1cb5e2d721044a0be2bdf24d0971e09f2f39488fe389fc5230699b4df7cec7447e5be4ea49bd7c3fe1a5ec7358510dc1dd9c1a8da68c0863188d80549e49f7c00f57d2009b2427b2aed1569603fc247734039469f9fdf3ddd3a22fa95c5d8066a468327a02b474c9915419af82c8edc67686984767fe7885207c6820f6c2e57cb8fd0bcb9981ebc8065c74e970a5d593c3b73ee25a0877ca096a9f7edfee6d43bd817c7d415fea9abb6f206c61aa36942df9318762a76b9da26d0d41a0ae9eee042a175f82dc134bf6f2d46a218db358d6852940e6e30df4a58ac6cb409e7ce99afe1e3f42768bd617af4d0a235d0ba0dd5075f9cc091784395d30e7e42d4e006db21bea9b45d1f122b75c051e84e2281573ef54ebad053218fff0cc28ea89a06adc218d4134f407654990592e75462f5ee4a463c1e46425222d48761162da8049613cafd7ecc52ff8024e9d58512b958e3a3d12dede84e1441247700bca0f992875349448b430683c756438fd4e91f3d44f3cf624ed21f3c63cf92615ecc201d0cd3159b1b3fccd8f29d2daba9ac5ba87b1dd2f83323a2b2d3176b803ce9c7bdc4bae615925eb22a213df1eeb2f8ff95586536caf042d565984aacf1425a120a5d8d7a9cbb70bf4852e116b89ff5b198d672220af2be4246372e7c3836cf50d732212a3e3346ff92873ace57fa687b2b1aab3e8dc6cb9f93f865d998cff0a1680d9012a9597c90a070e525f66226cc287814f4ac4157b15a0b25aa110946cd69fd404fafd5656669bfd1d9e509eabc004c5a")); + + data = serialize_onionpacket(tmpctx, op); + printf("[\n"); + + memset(&blinding_priv, 5, sizeof(blinding_priv)); + pubkey_from_privkey(&blinding_priv, &blinding); + + data = json_test("onion message for Alice", + data, + &alice, + &blinding_priv, + &blinding); + + data = json_test("onion message for Bob", + data, + &bob, + NULL, + &blinding); + + data = json_test("onion message for Carol", + data, + &carol, + NULL, + &blinding); + + data = json_test("onion message for Dave", + data, + &dave, + NULL, + &blinding); + + assert(!data); + printf("]\n"); + + common_shutdown(); + return 0; +} diff --git a/gossipd/test/run-txout_failure.c b/gossipd/test/run-txout_failure.c index e07e6d461430..29685664f15d 100644 --- a/gossipd/test/run-txout_failure.c +++ b/gossipd/test/run-txout_failure.c @@ -1,16 +1,37 @@ #include "../routing.c" #include "../common/timeout.c" +#include #include +#include #include +#include #include #include /* AUTOGENERATED MOCKS START */ +/* Generated stub for blinding_hash_e_and_ss */ +void blinding_hash_e_and_ss(const struct pubkey *e UNNEEDED, + const struct secret *ss UNNEEDED, + struct sha256 *sha UNNEEDED) +{ fprintf(stderr, "blinding_hash_e_and_ss called!\n"); abort(); } +/* Generated stub for blinding_next_privkey */ +bool blinding_next_privkey(const struct privkey *e UNNEEDED, + const struct sha256 *h UNNEEDED, + struct privkey *next UNNEEDED) +{ fprintf(stderr, "blinding_next_privkey called!\n"); abort(); } +/* Generated stub for blinding_next_pubkey */ +bool blinding_next_pubkey(const struct pubkey *pk UNNEEDED, + const struct sha256 *h UNNEEDED, + struct pubkey *next UNNEEDED) +{ fprintf(stderr, "blinding_next_pubkey called!\n"); abort(); } /* Generated stub for cupdate_different */ bool cupdate_different(struct gossip_store *gs UNNEEDED, const struct half_chan *hc UNNEEDED, const u8 *cupdate UNNEEDED) { fprintf(stderr, "cupdate_different called!\n"); abort(); } +/* Generated stub for ecdh */ +void ecdh(const struct pubkey *point UNNEEDED, struct secret *ss UNNEEDED) +{ fprintf(stderr, "ecdh called!\n"); abort(); } /* Generated stub for fmt_wireaddr_without_port */ char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) { fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } @@ -74,6 +95,9 @@ bool nannounce_different(struct gossip_store *gs UNNEEDED, const u8 *nannounce UNNEEDED, bool *only_missing_tlv UNNEEDED) { fprintf(stderr, "nannounce_different called!\n"); abort(); } +/* Generated stub for new_onionreply */ +struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED) +{ fprintf(stderr, "new_onionreply called!\n"); abort(); } /* Generated stub for notleak_ */ void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED) { fprintf(stderr, "notleak_ called!\n"); abort(); } diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index fd9caec3afa5..a1bd82203942 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -123,7 +123,6 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_DEV_COMPACT_STORE: case WIRE_GOSSIPD_DEV_SET_TIME: case WIRE_GOSSIPD_NEW_BLOCKHEIGHT: - case WIRE_GOSSIPD_SEND_OBS_ONIONMSG: case WIRE_GOSSIPD_SEND_ONIONMSG: case WIRE_GOSSIPD_ADDGOSSIP: /* This is a reply, so never gets through to here. */ @@ -134,12 +133,6 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_ADDGOSSIP_REPLY: break; - case WIRE_GOSSIPD_GOT_OBS_ONIONMSG_TO_US: - handle_obs_onionmsg_to_us(gossip->ld, msg); - break; - case WIRE_GOSSIPD_GOT_OBS_ONIONMSG_FORWARD: - handle_obs_onionmsg_forward(gossip->ld, msg); - break; case WIRE_GOSSIPD_GOT_ONIONMSG_TO_US: handle_onionmsg_to_us(gossip->ld, msg); break; diff --git a/lightningd/offer.c b/lightningd/offer.c index da81c9f53f23..09734163c399 100644 --- a/lightningd/offer.c +++ b/lightningd/offer.c @@ -310,7 +310,7 @@ static struct command_result *prev_payment(struct command *cmd, if (!inv->recurrence_counter) continue; - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - if the offer contained `recurrence_base` with * `start_any_period` non-zero: * - MUST include `recurrence_start` diff --git a/lightningd/onion_message.c b/lightningd/onion_message.c index 476232f09622..88034edc8006 100644 --- a/lightningd/onion_message.c +++ b/lightningd/onion_message.c @@ -15,17 +15,15 @@ #include struct onion_message_hook_payload { - /* Pre-spec or modern? */ - bool obsolete; - /* Optional */ - struct pubkey *blinding_in; /* obsolete only */ struct pubkey *reply_blinding; struct onionmsg_path **reply_path; - struct pubkey *reply_first_node; /* non-obsolete only */ - struct pubkey *our_alias; /* non-obsolete only */ + struct pubkey *reply_first_node; + struct pubkey *our_alias; + /* Exactly one of these is set! */ struct tlv_onionmsg_payload *om; + struct tlv_obs2_onionmsg_payload *obs2_om; }; static void json_add_blindedpath(struct json_stream *stream, @@ -41,7 +39,8 @@ static void json_add_blindedpath(struct json_stream *stream, for (size_t i = 0; i < tal_count(path); i++) { json_object_start(stream, NULL); json_add_pubkey(stream, "id", &path[i]->node_id); - json_add_hex_talarr(stream, "enctlv", path[i]->enctlv); + json_add_hex_talarr(stream, "encrypted_recipient_data", + path[i]->encrypted_recipient_data); json_object_end(stream); }; json_array_end(stream); @@ -53,57 +52,66 @@ static void onion_message_serialize(struct onion_message_hook_payload *payload, struct plugin *plugin) { json_object_start(stream, "onion_message"); - json_add_bool(stream, "obsolete", payload->obsolete); - if (payload->blinding_in) - json_add_pubkey(stream, "blinding_in", payload->blinding_in); if (payload->our_alias) json_add_pubkey(stream, "our_alias", payload->our_alias); - /* Modern style. */ if (payload->reply_first_node) { json_add_blindedpath(stream, "reply_blindedpath", payload->reply_blinding, payload->reply_first_node, payload->reply_path); - } else if (payload->reply_path) { - json_array_start(stream, "reply_path"); - for (size_t i = 0; i < tal_count(payload->reply_path); i++) { + } + + /* Common convenience fields */ + if (payload->obs2_om) { + json_add_bool(stream, "obs2", true); + if (payload->obs2_om->invoice_request) + json_add_hex_talarr(stream, "invoice_request", + payload->obs2_om->invoice_request); + if (payload->obs2_om->invoice) + json_add_hex_talarr(stream, "invoice", payload->obs2_om->invoice); + + if (payload->obs2_om->invoice_error) + json_add_hex_talarr(stream, "invoice_error", + payload->obs2_om->invoice_error); + + json_array_start(stream, "unknown_fields"); + for (size_t i = 0; i < tal_count(payload->obs2_om->fields); i++) { + if (payload->obs2_om->fields[i].meta) + continue; json_object_start(stream, NULL); - json_add_pubkey(stream, "id", - &payload->reply_path[i]->node_id); - if (tal_bytelen(payload->reply_path[i]->enctlv) != 0) - json_add_hex_talarr(stream, "enctlv", - payload->reply_path[i]->enctlv); - if (i == 0) - json_add_pubkey(stream, "blinding", - payload->reply_blinding); + json_add_u64(stream, "number", payload->obs2_om->fields[i].numtype); + json_add_hex(stream, "value", + payload->obs2_om->fields[i].value, + payload->obs2_om->fields[i].length); + json_object_end(stream); + } + json_array_end(stream); + } else { + json_add_bool(stream, "obs2", false); + if (payload->om->invoice_request) + json_add_hex_talarr(stream, "invoice_request", + payload->om->invoice_request); + if (payload->om->invoice) + json_add_hex_talarr(stream, "invoice", payload->om->invoice); + + if (payload->om->invoice_error) + json_add_hex_talarr(stream, "invoice_error", + payload->om->invoice_error); + + json_array_start(stream, "unknown_fields"); + for (size_t i = 0; i < tal_count(payload->om->fields); i++) { + if (payload->om->fields[i].meta) + continue; + json_object_start(stream, NULL); + json_add_u64(stream, "number", payload->om->fields[i].numtype); + json_add_hex(stream, "value", + payload->om->fields[i].value, + payload->om->fields[i].length); json_object_end(stream); } json_array_end(stream); } - /* Common convenience fields */ - if (payload->om->invoice_request) - json_add_hex_talarr(stream, "invoice_request", - payload->om->invoice_request); - if (payload->om->invoice) - json_add_hex_talarr(stream, "invoice", payload->om->invoice); - - if (payload->om->invoice_error) - json_add_hex_talarr(stream, "invoice_error", - payload->om->invoice_error); - - json_array_start(stream, "unknown_fields"); - for (size_t i = 0; i < tal_count(payload->om->fields); i++) { - if (payload->om->fields[i].meta) - continue; - json_object_start(stream, NULL); - json_add_u64(stream, "number", payload->om->fields[i].numtype); - json_add_hex(stream, "value", - payload->om->fields[i].value, - payload->om->fields[i].length); - json_object_end(stream); - } - json_array_end(stream); json_object_end(stream); } @@ -117,12 +125,6 @@ onion_message_hook_cb(struct onion_message_hook_payload *payload STEALS) /* Two hooks, because it's critical we only accept blinding if we expect that * exact blinding key. Otherwise, we can be probed using old blinded paths. */ -REGISTER_PLUGIN_HOOK(onion_message, - plugin_hook_continue, - onion_message_hook_cb, - onion_message_serialize, - struct onion_message_hook_payload *); - REGISTER_PLUGIN_HOOK(onion_message_blinded, plugin_hook_continue, onion_message_hook_cb, @@ -135,116 +137,20 @@ REGISTER_PLUGIN_HOOK(onion_message_ourpath, onion_message_serialize, struct onion_message_hook_payload *); -void handle_obs_onionmsg_to_us(struct lightningd *ld, const u8 *msg) -{ - struct onion_message_hook_payload *payload; - u8 *submsg; - size_t submsglen; - const u8 *subptr; - -#if DEVELOPER - if (ld->dev_ignore_obsolete_onion) - return; -#endif - - payload = tal(ld, struct onion_message_hook_payload); - payload->obsolete = true; - payload->reply_first_node = NULL; - payload->om = tlv_onionmsg_payload_new(payload); - payload->our_alias = NULL; - - if (!fromwire_gossipd_got_obs_onionmsg_to_us(payload, msg, - &payload->blinding_in, - &payload->reply_blinding, - &payload->reply_path, - &submsg)) { - log_broken(ld->log, "bad got_onionmsg_tous: %s", - tal_hex(tmpctx, msg)); - return; - } - submsglen = tal_bytelen(submsg); - subptr = submsg; - if (!fromwire_onionmsg_payload(&subptr, - &submsglen, payload->om)) { - tal_free(payload); - log_broken(ld->log, "bad got_onionmsg_tous om: %s", - tal_hex(tmpctx, msg)); - return; - } - tal_free(submsg); - - if (payload->reply_path && !payload->reply_blinding) { - log_broken(ld->log, - "No reply blinding, ignoring reply path"); - payload->reply_path = tal_free(payload->reply_path); - } - - log_debug(ld->log, "Got obsolete onionmsg%s%s", - payload->reply_blinding ? " reply_blinding": "", - payload->reply_path ? " reply_path": ""); - - if (payload->blinding_in) - plugin_hook_call_onion_message_blinded(ld, payload); - else - plugin_hook_call_onion_message(ld, payload); -} - -void handle_obs_onionmsg_forward(struct lightningd *ld, const u8 *msg) -{ - struct short_channel_id *next_scid; - struct node_id *next_node; - struct pubkey *next_blinding; - u8 *onion; - - if (!fromwire_gossipd_got_obs_onionmsg_forward(msg, msg, &next_scid, - &next_node, - &next_blinding, &onion)) { - log_broken(ld->log, "bad got_onionmsg_forward: %s", - tal_hex(tmpctx, msg)); - return; - } - - if (next_scid) { - struct channel *outchan = any_channel_by_scid(ld, next_scid); - if (outchan) - next_node = &outchan->peer->id; - } - - if (!next_node) { - log_debug(ld->log, "Cannot forward onionmsg to %s", - next_scid ? type_to_string(tmpctx, - struct short_channel_id, - next_scid) - : "unspecified dest"); - } else { - subd_send_msg(ld->gossip, - take(towire_gossipd_send_obs_onionmsg(NULL, - next_node, - onion, - next_blinding))); - } -} - void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) { struct onion_message_hook_payload *payload; u8 *submsg; struct secret *self_id; + bool obs2; size_t submsglen; const u8 *subptr; -#if DEVELOPER - if (ld->dev_ignore_modern_onion) - return; -#endif - - payload = tal(ld, struct onion_message_hook_payload); - payload->obsolete = false; - payload->om = tlv_onionmsg_payload_new(payload); - payload->blinding_in = NULL; + payload = tal(tmpctx, struct onion_message_hook_payload); payload->our_alias = tal(payload, struct pubkey); if (!fromwire_gossipd_got_onionmsg_to_us(payload, msg, + &obs2, payload->our_alias, &self_id, &payload->reply_blinding, @@ -256,19 +162,38 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) return; } +#if DEVELOPER + if (!obs2 && ld->dev_ignore_modern_onion) + return; + if (obs2 && ld->dev_ignore_obsolete_onion) + return; +#endif + /* If there's no self_id, or it's not correct, ignore alias: alias * means we created the path it's using. */ if (!self_id || !secret_eq_consttime(self_id, &ld->onion_reply_secret)) payload->our_alias = tal_free(payload->our_alias); + tal_free(self_id); submsglen = tal_bytelen(submsg); subptr = submsg; - if (!fromwire_onionmsg_payload(&subptr, - &submsglen, payload->om)) { - tal_free(payload); - log_broken(ld->log, "bad got_onionmsg_tous om: %s", - tal_hex(tmpctx, msg)); - return; + if (obs2) { + payload->om = NULL; + payload->obs2_om = tlv_obs2_onionmsg_payload_new(payload); + if (!fromwire_obs2_onionmsg_payload(&subptr, + &submsglen, payload->obs2_om)) { + log_broken(ld->log, "bad got_onionmsg_tous obs2 om: %s", + tal_hex(tmpctx, msg)); + return; + } + } else { + payload->obs2_om = NULL; + payload->om = tlv_onionmsg_payload_new(payload); + if (!fromwire_onionmsg_payload(&subptr, &submsglen, payload->om)) { + log_broken(ld->log, "bad got_onionmsg_tous om: %s", + tal_hex(tmpctx, msg)); + return; + } } tal_free(submsg); @@ -284,296 +209,14 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg) payload->our_alias ? " via-ourpath": "", payload->reply_path ? " reply_path": ""); + /* We'll free this on return */ + tal_steal(ld, payload); if (payload->our_alias) plugin_hook_call_onion_message_ourpath(ld, payload); else plugin_hook_call_onion_message_blinded(ld, payload); } -struct hop { - struct pubkey id; - struct short_channel_id *scid; - struct pubkey *blinding; - u8 *enctlv; - u8 *invoice; - u8 *invoice_req; - u8 *invoice_err; - u8 *rawtlv; -}; - -static struct command_result *param_hops(struct command *cmd, - const char *name, - const char *buffer, - const jsmntok_t *tok, - struct hop **hops) -{ - size_t i; - const jsmntok_t *t; - - if (tok->type != JSMN_ARRAY || tok->size == 0) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s must be an (non-empty) array", name); - - *hops = tal_arr(cmd, struct hop, tok->size); - json_for_each_arr(i, t, tok) { - const jsmntok_t *tid, *tscid, *tblinding, *tenctlv, *trawtlv, - *tinvoice, *tinvoicereq, *tinvoiceerr; - - tid = json_get_member(buffer, t, "id"); - if (!tid) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s[%zu] does not have 'id'", - name, i); - tscid = json_get_member(buffer, t, "short_channel_id"); - tblinding = json_get_member(buffer, t, "blinding"); - tenctlv = json_get_member(buffer, t, "enctlv"); - tinvoice = json_get_member(buffer, t, "invoice"); - tinvoicereq = json_get_member(buffer, t, "invoice_request"); - tinvoiceerr = json_get_member(buffer, t, "invoice_error"); - trawtlv = json_get_member(buffer, t, "rawtlv"); - - if (trawtlv && (tscid || tblinding || tenctlv || tinvoice || tinvoicereq)) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s[%zu] has 'rawtlv' with other fields", - name, i); - - if (tblinding) { - (*hops)[i].blinding = tal(*hops, struct pubkey); - if (!json_to_pubkey(buffer, tblinding, - (*hops)[i].blinding)) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s[%zu] 'blinding' is invalid", name, i); - } else - (*hops)[i].blinding = NULL; - - if (!json_to_pubkey(buffer, tid, &(*hops)[i].id)) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s[%zu] 'id' is invalid", name, i); - if (tscid) { - (*hops)[i].scid = tal(*hops, struct short_channel_id); - if (!json_to_short_channel_id(buffer, tscid, - (*hops)[i].scid)) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s[%zu] 'short_channel_id' is invalid", name, i); - } else - (*hops)[i].scid = NULL; - - if (tenctlv) { - (*hops)[i].enctlv = - json_tok_bin_from_hex(*hops, buffer, tenctlv); - if (!(*hops)[i].enctlv) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s[%zu] 'enctlv' is invalid", name, i); - } else - (*hops)[i].enctlv = NULL; - - if (tinvoice) { - (*hops)[i].invoice = - json_tok_bin_from_hex(*hops, buffer, tinvoice); - if (!(*hops)[i].invoice) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s[%zu] 'invoice' is invalid", name, i); - } else - (*hops)[i].invoice = NULL; - - if (tinvoicereq) { - (*hops)[i].invoice_req = - json_tok_bin_from_hex(*hops, buffer, tinvoicereq); - if (!(*hops)[i].invoice_req) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s[%zu] 'invoice_request' is invalid", name, i); - } else - (*hops)[i].invoice_req = NULL; - - if (tinvoiceerr) { - (*hops)[i].invoice_err = - json_tok_bin_from_hex(*hops, buffer, tinvoiceerr); - if (!(*hops)[i].invoice_err) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s[%zu] 'invoice_request' is invalid", name, i); - } else - (*hops)[i].invoice_err = NULL; - - if (trawtlv) { - (*hops)[i].rawtlv = - json_tok_bin_from_hex(*hops, buffer, trawtlv); - if (!(*hops)[i].rawtlv) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s[%zu] 'rawtlv' is invalid", name, i); - } else - (*hops)[i].rawtlv = NULL; - } - return NULL; -} - -static struct command_result *param_reply_path(struct command *cmd, - const char *name, - const char *buffer, - const jsmntok_t *tok, - struct tlv_onionmsg_payload_obs_reply_path **reply_path) -{ - const jsmntok_t *tblinding, *tpath, *t; - size_t i; - - *reply_path = tal(cmd, struct tlv_onionmsg_payload_obs_reply_path); - tblinding = json_get_member(buffer, tok, "blinding"); - if (!tblinding) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s has no 'blinding'", name); - if (!json_to_pubkey(buffer, tblinding, &(*reply_path)->blinding)) - return command_fail_badparam(cmd, name, buffer, tblinding, - "'blinding' should be valid pubkey"); - - tpath = json_get_member(buffer, tok, "path"); - if (!tpath || tpath->type != JSMN_ARRAY) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s has no 'path' array", name); - - (*reply_path)->path = tal_arr(*reply_path, struct onionmsg_path *, - tpath->size); - json_for_each_arr(i, t, tpath) { - const jsmntok_t *tid, *tenctlv; - struct onionmsg_path *path; - - path = (*reply_path)->path[i] = tal((*reply_path)->path, - struct onionmsg_path); - tid = json_get_member(buffer, t, "id"); - if (!tid) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s path[%zu] 'id' is missing", - name, i); - if (!json_to_pubkey(buffer, tid, &path->node_id)) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s path[%zu] 'id' is invalid", - name, i); - - tenctlv = json_get_member(buffer, t, "enctlv"); - if (!tenctlv) { - /* Optional for final destination */ - if (i != tpath->size - 1) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s path[%zu] 'enctlv' is missing", - name, i); - path->enctlv = NULL; - } else { - path->enctlv = json_tok_bin_from_hex(path, - buffer, tenctlv); - if (!path->enctlv) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%s path[%zu] 'enctlv' is invalid", - name, i); - } - } - - return NULL; -} - -/* Generate ->rawtlv if not already supplied. */ -static void populate_tlvs(struct hop *hops, - struct tlv_onionmsg_payload_obs_reply_path *reply_path) -{ - for (size_t i = 0; i < tal_count(hops); i++) { - struct tlv_onionmsg_payload *tlv; - - if (hops[i].rawtlv) - continue; - - tlv = tlv_onionmsg_payload_new(tmpctx); - /* If they don't give scid, use next node id */ - if (hops[i].scid) { - tlv->obs_next_short_channel_id - = tal_dup(tlv, struct short_channel_id, - hops[i].scid); - } else if (i != tal_count(hops)-1) { - tlv->obs_next_node_id = tal_dup(tlv, struct pubkey, - &hops[i+1].id); - } - if (hops[i].blinding) { - tlv->obs_blinding = tal_dup(tlv, struct pubkey, - hops[i].blinding); - } - /* Note: tal_dup_talarr returns NULL for NULL */ - tlv->enctlv = tal_dup_talarr(tlv, u8, hops[i].enctlv); - tlv->invoice = tal_dup_talarr(tlv, u8, hops[i].invoice); - tlv->invoice_request = tal_dup_talarr(tlv, u8, - hops[i].invoice_req); - tlv->invoice_error = tal_dup_talarr(tlv, u8, - hops[i].invoice_err); - - if (i == tal_count(hops)-1 && reply_path) - tlv->obs_reply_path = reply_path; - - hops[i].rawtlv = tal_arr(hops, u8, 0); - towire_onionmsg_payload(&hops[i].rawtlv, tlv); - } -} - -static struct command_result *json_send_obs_onion_message(struct command *cmd, - const char *buffer, - const jsmntok_t *obj UNNEEDED, - const jsmntok_t *params) -{ - struct hop *hops; - struct tlv_onionmsg_payload_obs_reply_path *reply_path; - struct sphinx_path *sphinx_path; - struct onionpacket *op; - struct secret *path_secrets; - struct node_id first_id; - size_t onion_size; - - if (!param(cmd, buffer, params, - p_req("hops", param_hops, &hops), - p_opt("reply_path", param_reply_path, &reply_path), - NULL)) - return command_param_failed(); - - if (!feature_offered(cmd->ld->our_features->bits[NODE_ANNOUNCE_FEATURE], - OPT_ONION_MESSAGES)) - return command_fail(cmd, LIGHTNINGD, - "experimental-onion-messages not enabled"); - - node_id_from_pubkey(&first_id, &hops[0].id); - - /* Sanity check first; gossipd doesn't bother telling us if peer - * can't be reached. */ - if (!peer_by_id(cmd->ld, &first_id)) - return command_fail(cmd, LIGHTNINGD, "Unknown first peer"); - - /* Create an onion which encodes this. */ - populate_tlvs(hops, reply_path); - sphinx_path = sphinx_path_new(cmd, NULL); - for (size_t i = 0; i < tal_count(hops); i++) - sphinx_add_modern_hop(sphinx_path, &hops[i].id, hops[i].rawtlv); - - /* BOLT-onion-message #4: - * - SHOULD set `len` to 1366 or 32834. - */ - if (sphinx_path_payloads_size(sphinx_path) <= ROUTING_INFO_SIZE) - onion_size = ROUTING_INFO_SIZE; - else - onion_size = 32768; - - op = create_onionpacket(tmpctx, sphinx_path, onion_size, &path_secrets); - if (!op) - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "Creating onion failed (tlvs too long?)"); - - subd_send_msg(cmd->ld->gossip, - take(towire_gossipd_send_obs_onionmsg(NULL, &first_id, - serialize_onionpacket(tmpctx, op), - NULL))); - - return command_success(cmd, json_stream_success(cmd)); -} - -static const struct json_command send_obs_onion_message_command = { - "sendobsonionmessage", - "utility", - json_send_obs_onion_message, - "Send message over {hops} (id, [short_channel_id], [blinding], [enctlv], [invoice], [invoice_request], [invoice_error], [rawtlv]) with optional {reply_path} (blinding, path[id, enctlv])" -}; -AUTODATA(json_command, &send_obs_onion_message_command); - struct onion_hop { struct pubkey node; u8 *tlv; @@ -607,10 +250,11 @@ static struct command_result *param_onion_hops(struct command *cmd, return NULL; } -static struct command_result *json_sendonionmessage(struct command *cmd, - const char *buffer, - const jsmntok_t *obj UNNEEDED, - const jsmntok_t *params) +static struct command_result *json_sendonionmessage2(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params, + bool obs2) { struct onion_hop *hops; struct node_id *first_id; @@ -656,13 +300,29 @@ static struct command_result *json_sendonionmessage(struct command *cmd, "Creating onion failed (tlvs too long?)"); subd_send_msg(cmd->ld->gossip, - take(towire_gossipd_send_onionmsg(NULL, first_id, + take(towire_gossipd_send_onionmsg(NULL, obs2, first_id, serialize_onionpacket(tmpctx, op), blinding))); return command_success(cmd, json_stream_success(cmd)); } +static struct command_result *json_sendonionmessage(struct command *cmd, + const char *buffer, + const jsmntok_t *obj, + const jsmntok_t *params) +{ + return json_sendonionmessage2(cmd, buffer, obj, params, false); +} + +static struct command_result *json_sendobs2onionmessage(struct command *cmd, + const char *buffer, + const jsmntok_t *obj, + const jsmntok_t *params) +{ + return json_sendonionmessage2(cmd, buffer, obj, params, true); +} + static const struct json_command sendonionmessage_command = { "sendonionmessage", "utility", @@ -671,6 +331,14 @@ static const struct json_command sendonionmessage_command = { }; AUTODATA(json_command, &sendonionmessage_command); +static const struct json_command sendobs2onionmessage_command = { + "sendobs2onionmessage", + "utility", + json_sendobs2onionmessage, + "Send obsolete message to {first_id}, using {blinding}, encoded over {hops} (id, tlv)" +}; +AUTODATA(json_command, &sendobs2onionmessage_command); + static struct command_result *param_pubkeys(struct command *cmd, const char *name, const char *buffer, @@ -700,8 +368,8 @@ static struct command_result *json_blindedpath(struct command *cmd, { struct pubkey *ids; struct onionmsg_path **path; - struct privkey blinding_iter; - struct pubkey first_blinding, first_node, me; + struct privkey first_blinding, blinding_iter; + struct pubkey first_blinding_pubkey, first_node, me; size_t nhops; struct json_stream *response; @@ -725,8 +393,8 @@ static struct command_result *json_blindedpath(struct command *cmd, type_to_string(tmpctx, struct pubkey, &ids[nhops-1])); - randombytes_buf(&blinding_iter, sizeof(blinding_iter)); - if (!pubkey_from_privkey(&blinding_iter, &first_blinding)) + randombytes_buf(&first_blinding, sizeof(first_blinding)); + if (!pubkey_from_privkey(&first_blinding, &first_blinding_pubkey)) /* Should not happen! */ return command_fail(cmd, LIGHTNINGD, "Could not convert blinding to pubkey!"); @@ -734,32 +402,60 @@ static struct command_result *json_blindedpath(struct command *cmd, /* We convert ids into aliases as we go. */ path = tal_arr(cmd, struct onionmsg_path *, nhops); + blinding_iter = first_blinding; for (size_t i = 0; i < nhops - 1; i++) { path[i] = tal(path, struct onionmsg_path); - path[i]->enctlv = create_enctlv(path[i], - &blinding_iter, - &ids[i], - &ids[i+1], - /* FIXME: Pad? */ - 0, - NULL, - &blinding_iter, - &path[i]->node_id); + path[i]->encrypted_recipient_data = create_enctlv(path[i], + &blinding_iter, + &ids[i], + &ids[i+1], + /* FIXME: Pad? */ + 0, + NULL, + &blinding_iter, + &path[i]->node_id); } /* FIXME: Add padding! */ path[nhops-1] = tal(path, struct onionmsg_path); - path[nhops-1]->enctlv = create_final_enctlv(path[nhops-1], - &blinding_iter, - &ids[nhops-1], - /* FIXME: Pad? */ - 0, - &cmd->ld->onion_reply_secret, - &path[nhops-1]->node_id); + path[nhops-1]->encrypted_recipient_data = create_final_enctlv(path[nhops-1], + &blinding_iter, + &ids[nhops-1], + /* FIXME: Pad? */ + 0, + &cmd->ld->onion_reply_secret, + &path[nhops-1]->node_id); response = json_stream_success(cmd); json_add_blindedpath(response, "blindedpath", - &first_blinding, &first_node, path); + &first_blinding_pubkey, &first_node, path); + + /* Now create obsolete one! */ + blinding_iter = first_blinding; + for (size_t i = 0; i < nhops - 1; i++) { + path[i] = tal(path, struct onionmsg_path); + path[i]->encrypted_recipient_data = create_obs2_enctlv(path[i], + &blinding_iter, + &ids[i], + &ids[i+1], + /* FIXME: Pad? */ + 0, + NULL, + &blinding_iter, + &path[i]->node_id); + } + + /* FIXME: Add padding! */ + path[nhops-1] = tal(path, struct onionmsg_path); + path[nhops-1]->encrypted_recipient_data = create_obs2_final_enctlv(path[nhops-1], + &blinding_iter, + &ids[nhops-1], + /* FIXME: Pad? */ + 0, + &cmd->ld->onion_reply_secret, + &path[nhops-1]->node_id); + json_add_blindedpath(response, "obs2blindedpath", + &first_blinding_pubkey, &first_node, path); return command_success(cmd, response); } diff --git a/lightningd/onion_message.h b/lightningd/onion_message.h index 02f93f8d26af..43f55fdf41a7 100644 --- a/lightningd/onion_message.h +++ b/lightningd/onion_message.h @@ -5,9 +5,6 @@ struct lightningd; -void handle_obs_onionmsg_to_us(struct lightningd *ld, const u8 *msg); -void handle_obs_onionmsg_forward(struct lightningd *ld, const u8 *msg); - void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg); #endif /* LIGHTNING_LIGHTNINGD_ONION_MESSAGE_H */ diff --git a/lightningd/pay.c b/lightningd/pay.c index 6f4db608f121..c6f414f2ee87 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -1392,7 +1392,7 @@ static struct command_result *param_route_hops(struct command *cmd, p_opt("direction", param_number, &direction), p_opt("style", param_route_hop_style, &style), p_opt("blinding", param_pubkey, &blinding), - p_opt("enctlv", param_bin_from_hex, &enctlv), + p_opt("encrypted_recipient_data", param_bin_from_hex, &enctlv), NULL)) return command_param_failed(); diff --git a/openingd/dualopend.c b/openingd/dualopend.c index e3eb065282b2..795fce83cb10 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -1285,8 +1285,8 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state) case WIRE_CHANNEL_REESTABLISH: case WIRE_ANNOUNCEMENT_SIGNATURES: case WIRE_GOSSIP_TIMESTAMP_FILTER: + case WIRE_OBS2_ONION_MESSAGE: case WIRE_ONION_MESSAGE: - case WIRE_OBS_ONION_MESSAGE: case WIRE_ACCEPT_CHANNEL2: case WIRE_TX_ADD_INPUT: case WIRE_TX_REMOVE_INPUT: @@ -1632,8 +1632,8 @@ static bool run_tx_interactive(struct state *state, case WIRE_CHANNEL_REESTABLISH: case WIRE_ANNOUNCEMENT_SIGNATURES: case WIRE_GOSSIP_TIMESTAMP_FILTER: + case WIRE_OBS2_ONION_MESSAGE: case WIRE_ONION_MESSAGE: - case WIRE_OBS_ONION_MESSAGE: case WIRE_TX_SIGNATURES: case WIRE_OPEN_CHANNEL2: case WIRE_ACCEPT_CHANNEL2: @@ -3686,8 +3686,8 @@ static u8 *handle_peer_in(struct state *state) case WIRE_CHANNEL_REESTABLISH: case WIRE_ANNOUNCEMENT_SIGNATURES: case WIRE_GOSSIP_TIMESTAMP_FILTER: + case WIRE_OBS2_ONION_MESSAGE: case WIRE_ONION_MESSAGE: - case WIRE_OBS_ONION_MESSAGE: case WIRE_ACCEPT_CHANNEL2: case WIRE_TX_ADD_INPUT: case WIRE_TX_REMOVE_INPUT: diff --git a/plugins/Makefile b/plugins/Makefile index 79bbadea3149..3562243cd8c4 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -141,8 +141,10 @@ PLUGIN_COMMON_OBJS := \ wire/tlvstream.o \ wire/towire.o +# Make all plugins depend on all plugin headers, for simplicity. +$(PLUGIN_ALL_OBJS): $(PLUGIN_ALL_HEADER) + plugins/pay: bitcoin/chainparams.o $(PLUGIN_PAY_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_PAY_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) common/gossmap.o common/fp16.o common/route.o common/dijkstra.o common/bolt12.o common/bolt12_merkle.o wire/bolt12$(EXP)_wiregen.o bitcoin/block.o -$(PLUGIN_PAY_OBJS): $(PLUGIN_PAY_LIB_HEADER) plugins/autoclean: bitcoin/chainparams.o $(PLUGIN_AUTOCLEAN_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) @@ -165,8 +167,6 @@ plugins/fetchinvoice: bitcoin/chainparams.o $(PLUGIN_FETCHINVOICE_OBJS) $(PLUGIN plugins/funder: bitcoin/chainparams.o bitcoin/psbt.o common/psbt_open.o $(PLUGIN_FUNDER_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) -$(PLUGIN_ALL_OBJS): $(PLUGIN_LIB_HEADER) - # Generated from PLUGINS definition in plugins/Makefile ALL_C_HEADERS += plugins/list_of_builtin_plugins_gen.h plugins/list_of_builtin_plugins_gen.h: plugins/Makefile Makefile diff --git a/plugins/fetchinvoice.c b/plugins/fetchinvoice.c index 336d058e8a9a..afc185f0c7d5 100644 --- a/plugins/fetchinvoice.c +++ b/plugins/fetchinvoice.c @@ -28,9 +28,7 @@ static LIST_HEAD(sent_list); struct sent { /* We're in sent_invreqs, awaiting reply. */ struct list_node list; - /* The blinding factor used by reply (obsolete only) */ - struct pubkey *reply_blinding; - /* The alias used by reply (modern only) */ + /* The alias used by reply */ struct pubkey *reply_alias; /* The command which sent us. */ struct command *cmd; @@ -49,17 +47,6 @@ struct sent { u32 wait_timeout; }; -static struct sent *find_sent_by_blinding(const struct pubkey *blinding) -{ - struct sent *i; - - list_for_each(&sent_list, i, list) { - if (i->reply_blinding && pubkey_eq(i->reply_blinding, blinding)) - return i; - } - return NULL; -} - static struct sent *find_sent_by_alias(const struct pubkey *alias) { struct sent *i; @@ -251,6 +238,15 @@ static struct command_result *handle_invreq_response(struct command *cmd, goto badinv; /* BOLT-offers #12: + * - if the invoice is a reply to an `invoice_request`: + *... + * - MUST reject the invoice unless the following fields are equal or + * unset exactly as they are in the `invoice_request:` + * - `quantity` + * - `payer_key` + * - `payer_info` + */ + /* BOLT-offers-recurrence #12: * - if the invoice is a reply to an `invoice_request`: *... * - MUST reject the invoice unless the following fields are equal or @@ -292,7 +288,7 @@ static struct command_result *handle_invreq_response(struct command *cmd, } else expected_amount = NULL; - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - if the offer contained `recurrence`: * - MUST reject the invoice if `recurrence_basetime` is not set. */ @@ -443,48 +439,6 @@ static struct command_result *recv_modern_onion_message(struct command *cmd, return command_hook_success(cmd); } -static struct command_result *recv_obs_onion_message(struct command *cmd, - const char *buf, - const jsmntok_t *params) -{ - const jsmntok_t *om, *blindingtok; - bool obsolete; - struct sent *sent; - struct pubkey blinding; - struct command_result *err; - - om = json_get_member(buf, params, "onion_message"); - json_to_bool(buf, json_get_member(buf, om, "obsolete"), &obsolete); - if (!obsolete) - return command_hook_success(cmd); - - blindingtok = json_get_member(buf, om, "blinding_in"); - if (!blindingtok || !json_to_pubkey(buf, blindingtok, &blinding)) - return command_hook_success(cmd); - - sent = find_sent_by_blinding(&blinding); - if (!sent) { - plugin_log(cmd->plugin, LOG_DBG, - "No match for obsolete onion %.*s", - json_tok_full_len(om), - json_tok_full(buf, om)); - return command_hook_success(cmd); - } - - plugin_log(cmd->plugin, LOG_DBG, "Received onion message: %.*s", - json_tok_full_len(params), - json_tok_full(buf, params)); - - err = handle_error(cmd, sent, buf, om); - if (err) - return err; - - if (sent->invreq) - return handle_invreq_response(cmd, sent, buf, om); - - return command_hook_success(cmd); -} - static void destroy_sent(struct sent *sent) { list_del(&sent->list); @@ -681,71 +635,96 @@ static struct pubkey *path_to_node(const tal_t *ctx, return nodes; } -/* Marshal arguments for sending obsolete and modern onion messages */ +/* Marshal arguments for sending onion messages */ struct sending { struct sent *sent; const char *msgfield; const u8 *msgval; + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path; struct command_result *(*done)(struct command *cmd, const char *buf UNUSED, const jsmntok_t *result UNUSED, struct sent *sent); }; -/* Send this message down this path, with blinded reply path */ -static struct command_result *send_obs_message(struct command *cmd, - const char *buf UNUSED, - const jsmntok_t *result UNUSED, - struct sending *sending) +static struct command_result * +send_obs2_message(struct command *cmd, + const char *buf, + const jsmntok_t *result, + struct sending *sending) { - struct pubkey *backwards; - struct onionmsg_path **path; - struct pubkey blinding; - struct out_req *req; struct sent *sent = sending->sent; + struct privkey blinding_iter; + struct pubkey fwd_blinding, *node_alias; + size_t nhops = tal_count(sent->path); + struct tlv_obs2_onionmsg_payload **payloads; + struct out_req *req; - /* FIXME: Maybe we should allow this? */ - if (tal_count(sent->path) == 1) - return command_fail(cmd, PAY_ROUTE_NOT_FOUND, - "Refusing to talk to ourselves"); + /* Now create enctlvs for *forward* path. */ + randombytes_buf(&blinding_iter, sizeof(blinding_iter)); + if (!pubkey_from_privkey(&blinding_iter, &fwd_blinding)) + return command_fail(cmd, LIGHTNINGD, + "Could not convert blinding %s to pubkey!", + type_to_string(tmpctx, struct privkey, + &blinding_iter)); - /* Reverse path is offset by one. */ - backwards = tal_arr(tmpctx, struct pubkey, tal_count(sent->path) - 1); - for (size_t i = 0; i < tal_count(backwards); i++) - backwards[tal_count(backwards)-1-i] = sent->path[i]; + /* We overallocate: this node (0) doesn't have payload or alias */ + payloads = tal_arr(cmd, struct tlv_obs2_onionmsg_payload *, nhops); + node_alias = tal_arr(cmd, struct pubkey, nhops); + + for (size_t i = 1; i < nhops - 1; i++) { + payloads[i] = tlv_obs2_onionmsg_payload_new(payloads); + payloads[i]->enctlv = create_obs2_enctlv(payloads[i], + &blinding_iter, + &sent->path[i], + &sent->path[i+1], + /* FIXME: Pad? */ + 0, + NULL, + &blinding_iter, + &node_alias[i]); + } + /* Final payload contains the actual data. */ + payloads[nhops-1] = tlv_obs2_onionmsg_payload_new(payloads); - /* Ok, now make reply for onion_message */ - sent->reply_blinding = tal(sent, struct pubkey); - path = make_blindedpath(tmpctx, backwards, &blinding, - sent->reply_blinding); + /* We don't include enctlv in final, but it gives us final alias */ + if (!create_obs2_final_enctlv(tmpctx, &blinding_iter, &sent->path[nhops-1], + /* FIXME: Pad? */ 0, + NULL, + &node_alias[nhops-1])) { + /* Should not happen! */ + return command_fail(cmd, LIGHTNINGD, + "Could create final enctlv"); + } - req = jsonrpc_request_start(cmd->plugin, cmd, "sendobsonionmessage", + /* FIXME: This interface is a string for sendobsonionmessage! */ + if (streq(sending->msgfield, "invoice_request")) { + payloads[nhops-1]->invoice_request + = cast_const(u8 *, sending->msgval); + } else { + assert(streq(sending->msgfield, "invoice")); + payloads[nhops-1]->invoice + = cast_const(u8 *, sending->msgval); + } + payloads[nhops-1]->reply_path = sending->obs2_reply_path; + + req = jsonrpc_request_start(cmd->plugin, cmd, "sendobs2onionmessage", sending->done, forward_error, - sent); + sending->sent); + json_add_pubkey(req->js, "first_id", &sent->path[1]); + json_add_pubkey(req->js, "blinding", &fwd_blinding); json_array_start(req->js, "hops"); - for (size_t i = 1; i < tal_count(sent->path); i++) { - json_object_start(req->js, NULL); - json_add_pubkey(req->js, "id", &sent->path[i]); - if (i == tal_count(sent->path) - 1) - json_add_hex_talarr(req->js, - sending->msgfield, sending->msgval); - json_object_end(req->js); - } - json_array_end(req->js); - - json_object_start(req->js, "reply_path"); - json_add_pubkey(req->js, "blinding", &blinding); - json_array_start(req->js, "path"); - for (size_t i = 0; i < tal_count(path); i++) { + for (size_t i = 1; i < nhops; i++) { + u8 *tlv; json_object_start(req->js, NULL); - json_add_pubkey(req->js, "id", &path[i]->node_id); - if (path[i]->enctlv) - json_add_hex_talarr(req->js, "enctlv", path[i]->enctlv); + json_add_pubkey(req->js, "id", &node_alias[i]); + tlv = tal_arr(tmpctx, u8, 0); + towire_obs2_onionmsg_payload(&tlv, payloads[i]); + json_add_hex_talarr(req->js, "tlv", tlv); json_object_end(req->js); } json_array_end(req->js); - json_object_end(req->js); return send_outreq(cmd->plugin, req); } @@ -775,7 +754,7 @@ send_modern_message(struct command *cmd, for (size_t i = 1; i < nhops - 1; i++) { payloads[i] = tlv_onionmsg_payload_new(payloads); - payloads[i]->enctlv = create_enctlv(payloads[i], + payloads[i]->encrypted_data_tlv = create_enctlv(payloads[i], &blinding_iter, &sent->path[i], &sent->path[i+1], @@ -810,8 +789,8 @@ send_modern_message(struct command *cmd, payloads[nhops-1]->reply_path = reply_path; req = jsonrpc_request_start(cmd->plugin, cmd, "sendonionmessage", - /* We try obsolete msg next */ - send_obs_message, + /* Try sending older version next */ + send_obs2_message, forward_error, sending); json_add_pubkey(req->js, "first_id", &sent->path[1]); @@ -847,6 +826,15 @@ static struct command_result *use_reply_path(struct command *cmd, json_tok_full_len(result), json_tok_full(buf, result)); + sending->obs2_reply_path = json_to_obs2_reply_path(cmd, buf, + json_get_member(buf, result, + "obs2blindedpath")); + if (!sending->obs2_reply_path) + plugin_err(cmd->plugin, + "could not parse obs2 reply path %.*s?", + json_tok_full_len(result), + json_tok_full(buf, result)); + /* Remember our alias we used so we can recognize reply */ sending->sent->reply_alias = tal_dup(sending->sent, struct pubkey, @@ -1101,7 +1089,7 @@ static struct command_result *invreq_done(struct command *cmd, if (sent->invreq->recurrence_start) period_idx += *sent->invreq->recurrence_start; - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - if the offer contained `recurrence_limit`: * - MUST NOT send an `invoice_request` for a period greater * than `max_period` @@ -1114,7 +1102,7 @@ static struct command_result *invreq_done(struct command *cmd, period_idx, *sent->offer->recurrence_limit); - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - SHOULD NOT send an `invoice_request` for a period which has * already passed. */ @@ -1278,11 +1266,11 @@ static struct command_result *json_fetchinvoice(struct command *cmd, && time_now().ts.tv_sec > *sent->offer->absolute_expiry) return command_fail(cmd, OFFER_EXPIRED, "Offer expired"); - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - if the offer did not specify `amount`: * - MUST specify `amount`.`msat` in multiples of the minimum - * lightning-payable unit (e.g. milli-satoshis for bitcoin) for the - * first `chains` entry. + * lightning-payable unit (e.g. milli-satoshis for bitcoin) for + * `chain` (or for bitcoin, if there is no `chain`). * - otherwise: * - MAY omit `amount`. * - if it sets `amount`: @@ -1330,16 +1318,16 @@ static struct command_result *json_fetchinvoice(struct command *cmd, "quantity parameter unnecessary"); } - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - if the offer contained `recurrence`: */ if (sent->offer->recurrence) { - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - for the initial request: *... * - MUST set `recurrence_counter` `counter` to 0. */ - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - for any successive requests: *... * - MUST set `recurrence_counter` `counter` to one greater @@ -1349,7 +1337,7 @@ static struct command_result *json_fetchinvoice(struct command *cmd, return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "needs recurrence_counter"); - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - if the offer contained `recurrence_base` with * `start_any_period` non-zero: * - MUST include `recurrence_start` @@ -1374,7 +1362,7 @@ static struct command_result *json_fetchinvoice(struct command *cmd, return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "needs recurrence_label"); } else { - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - otherwise: * - MUST NOT set `recurrence_counter`. * - MUST NOT set `recurrence_start` @@ -1395,10 +1383,6 @@ static struct command_result *json_fetchinvoice(struct command *cmd, * - the bitcoin chain is implied as the first and only entry. */ if (!streq(chainparams->network_name, "bitcoin")) { - if (deprecated_apis) { - invreq->chains = tal_arr(invreq, struct bitcoin_blkid, 1); - invreq->chains[0] = chainparams->genesis_blockhash; - } invreq->chain = tal_dup(invreq, struct bitcoin_blkid, &chainparams->genesis_blockhash); } @@ -1779,9 +1763,7 @@ static struct command_result *json_sendinvoice(struct command *cmd, */ sent->inv->payer_key = sent->offer->node_id; - /* BOLT-offers #12: - * - FIXME: recurrence! - */ + /* FIXME: recurrence? */ if (sent->offer->recurrence) return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "FIXME: handle recurring send_invoice offer!"); @@ -1794,10 +1776,6 @@ static struct command_result *json_sendinvoice(struct command *cmd, * - the bitcoin chain is implied as the first and only entry. */ if (!streq(chainparams->network_name, "bitcoin")) { - if (deprecated_apis) { - sent->inv->chains = tal_arr(sent->inv, struct bitcoin_blkid, 1); - sent->inv->chains[0] = chainparams->genesis_blockhash; - } sent->inv->chain = tal_dup(sent->inv, struct bitcoin_blkid, &chainparams->genesis_blockhash); } @@ -1942,10 +1920,6 @@ static const char *init(struct plugin *p, const char *buf UNUSED, } static const struct plugin_hook hooks[] = { - { - "onion_message_blinded", - recv_obs_onion_message - }, { "onion_message_ourpath", recv_modern_onion_message diff --git a/plugins/offers.c b/plugins/offers.c index 94c1016504ff..7e6a933a1d4d 100644 --- a/plugins/offers.c +++ b/plugins/offers.c @@ -42,29 +42,29 @@ static struct command_result *sendonionmessage_error(struct command *cmd, /* FIXME: replyfield string interface is to accomodate obsolete API */ static struct command_result * -send_modern_onion_reply(struct command *cmd, - struct tlv_onionmsg_payload_reply_path *reply_path, - const char *replyfield, - const u8 *replydata) +send_obs2_onion_reply(struct command *cmd, + struct tlv_obs2_onionmsg_payload_reply_path *reply_path, + const char *replyfield, + const u8 *replydata) { struct out_req *req; size_t nhops = tal_count(reply_path->path); - req = jsonrpc_request_start(cmd->plugin, cmd, "sendonionmessage", + req = jsonrpc_request_start(cmd->plugin, cmd, "sendobs2onionmessage", finished, sendonionmessage_error, NULL); json_add_pubkey(req->js, "first_id", &reply_path->first_node_id); json_add_pubkey(req->js, "blinding", &reply_path->blinding); json_array_start(req->js, "hops"); for (size_t i = 0; i < nhops; i++) { - struct tlv_onionmsg_payload *omp; + struct tlv_obs2_onionmsg_payload *omp; u8 *tlv; json_object_start(req->js, NULL); json_add_pubkey(req->js, "id", &reply_path->path[i]->node_id); - omp = tlv_onionmsg_payload_new(tmpctx); - omp->enctlv = reply_path->path[i]->enctlv; + omp = tlv_obs2_onionmsg_payload_new(tmpctx); + omp->enctlv = reply_path->path[i]->encrypted_recipient_data; /* Put payload in last hop. */ if (i == nhops - 1) { @@ -76,7 +76,7 @@ send_modern_onion_reply(struct command *cmd, } } tlv = tal_arr(tmpctx, u8, 0); - towire_onionmsg_payload(&tlv, omp); + towire_obs2_onionmsg_payload(&tlv, omp); json_add_hex_talarr(req->js, "tlv", tlv); json_object_end(req->js); } @@ -84,120 +84,96 @@ send_modern_onion_reply(struct command *cmd, return send_outreq(cmd->plugin, req); } -struct command_result *WARN_UNUSED_RESULT +struct command_result * send_onion_reply(struct command *cmd, struct tlv_onionmsg_payload_reply_path *reply_path, - const char *jsonbuf, - const jsmntok_t *replytok, + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path, const char *replyfield, const u8 *replydata) { struct out_req *req; - size_t i; - const jsmntok_t *t; + size_t nhops; - if (reply_path) - return send_modern_onion_reply(cmd, reply_path, - replyfield, replydata); + /* Exactly one must be set! */ + assert(!reply_path != !obs2_reply_path); + if (obs2_reply_path) + return send_obs2_onion_reply(cmd, obs2_reply_path, replyfield, replydata); - plugin_log(cmd->plugin, LOG_DBG, "sending obs reply %s = %s", - replyfield, tal_hex(tmpctx, replydata)); - - /* Send to requester, using return route. */ - req = jsonrpc_request_start(cmd->plugin, cmd, "sendobsonionmessage", + req = jsonrpc_request_start(cmd->plugin, cmd, "sendonionmessage", finished, sendonionmessage_error, NULL); - /* Add reply into last hop. */ + json_add_pubkey(req->js, "first_id", &reply_path->first_node_id); + json_add_pubkey(req->js, "blinding", &reply_path->blinding); json_array_start(req->js, "hops"); - json_for_each_arr(i, t, replytok) { - size_t j; - const jsmntok_t *t2; - plugin_log(cmd->plugin, LOG_DBG, "hops[%zu/%i]", - i, replytok->size); + nhops = tal_count(reply_path->path); + for (size_t i = 0; i < nhops; i++) { + struct tlv_onionmsg_payload *omp; + u8 *tlv; + json_object_start(req->js, NULL); - json_for_each_obj(j, t2, t) - json_add_tok(req->js, - json_strdup(tmpctx, jsonbuf, t2), - t2+1, jsonbuf); - if (i == replytok->size - 1) { - plugin_log(cmd->plugin, LOG_DBG, "... adding %s", - replyfield); - json_add_hex_talarr(req->js, replyfield, replydata); + json_add_pubkey(req->js, "id", &reply_path->path[i]->node_id); + + omp = tlv_onionmsg_payload_new(tmpctx); + omp->encrypted_data_tlv = reply_path->path[i]->encrypted_recipient_data; + + /* Put payload in last hop. */ + if (i == nhops - 1) { + if (streq(replyfield, "invoice")) { + omp->invoice = cast_const(u8 *, replydata); + } else { + assert(streq(replyfield, "invoice_error")); + omp->invoice_error = cast_const(u8 *, replydata); + } } + tlv = tal_arr(tmpctx, u8, 0); + towire_onionmsg_payload(&tlv, omp); + json_add_hex_talarr(req->js, "tlv", tlv); json_object_end(req->js); } json_array_end(req->js); return send_outreq(cmd->plugin, req); } -static struct command_result *onion_message_call(struct command *cmd, - const char *buf, - const jsmntok_t *params) -{ - const jsmntok_t *om, *invreqtok, *invtok; - - if (!offers_enabled) - return command_hook_success(cmd); - - om = json_get_member(buf, params, "onion_message"); - - invreqtok = json_get_member(buf, om, "invoice_request"); - if (invreqtok) { - const jsmntok_t *replytok; - - replytok = json_get_member(buf, om, "reply_path"); - if (replytok && replytok->size > 0) - return handle_invoice_request(cmd, buf, - invreqtok, replytok, NULL); - else - plugin_log(cmd->plugin, LOG_DBG, - "invoice_request without reply_path"); - } - - invtok = json_get_member(buf, om, "invoice"); - if (invtok) { - const jsmntok_t *replytok; - - replytok = json_get_member(buf, om, "reply_path"); - return handle_invoice(cmd, buf, invtok, replytok, NULL); - } - - return command_hook_success(cmd); -} - static struct command_result *onion_message_modern_call(struct command *cmd, const char *buf, const jsmntok_t *params) { const jsmntok_t *om, *replytok, *invreqtok, *invtok; - bool obsolete; - struct tlv_onionmsg_payload_reply_path *reply_path; + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path = NULL; + struct tlv_onionmsg_payload_reply_path *reply_path = NULL; if (!offers_enabled) return command_hook_success(cmd); om = json_get_member(buf, params, "onion_message"); - json_to_bool(buf, json_get_member(buf, om, "obsolete"), &obsolete); - if (obsolete) - return command_hook_success(cmd); - replytok = json_get_member(buf, om, "reply_blindedpath"); if (replytok) { - reply_path = json_to_reply_path(cmd, buf, replytok); - if (!reply_path) - plugin_err(cmd->plugin, "Invalid reply path %.*s?", - json_tok_full_len(replytok), - json_tok_full(buf, replytok)); - } else - reply_path = NULL; + bool obs2; + json_to_bool(buf, json_get_member(buf, om, "obs2"), &obs2); + if (obs2) { + obs2_reply_path = json_to_obs2_reply_path(cmd, buf, replytok); + if (!obs2_reply_path) + plugin_err(cmd->plugin, "Invalid obs2 reply path %.*s?", + json_tok_full_len(replytok), + json_tok_full(buf, replytok)); + } else { + reply_path = json_to_reply_path(cmd, buf, replytok); + if (!reply_path) + plugin_err(cmd->plugin, "Invalid reply path %.*s?", + json_tok_full_len(replytok), + json_tok_full(buf, replytok)); + } + } invreqtok = json_get_member(buf, om, "invoice_request"); if (invreqtok) { - if (reply_path) - return handle_invoice_request(cmd, buf, - invreqtok, - NULL, reply_path); + const u8 *invreqbin = json_tok_bin_from_hex(tmpctx, buf, invreqtok); + if (reply_path || obs2_reply_path) + return handle_invoice_request(cmd, + invreqbin, + reply_path, + obs2_reply_path); else plugin_log(cmd->plugin, LOG_DBG, "invoice_request without reply_path"); @@ -205,17 +181,15 @@ static struct command_result *onion_message_modern_call(struct command *cmd, invtok = json_get_member(buf, om, "invoice"); if (invtok) { - return handle_invoice(cmd, buf, invtok, NULL, reply_path); + const u8 *invbin = json_tok_bin_from_hex(tmpctx, buf, invtok); + if (invbin) + return handle_invoice(cmd, invbin, reply_path, obs2_reply_path); } return command_hook_success(cmd); } static const struct plugin_hook hooks[] = { - { - "onion_message", - onion_message_call - }, { "onion_message_blinded", onion_message_modern_call @@ -315,7 +289,7 @@ static void json_add_onionmsg_path(struct json_stream *js, { json_object_start(js, fieldname); json_add_pubkey(js, "node_id", &path->node_id); - json_add_hex_talarr(js, "enctlv", path->enctlv); + json_add_hex_talarr(js, "encrypted_recipient_data", path->encrypted_recipient_data); if (payinfo) { json_add_u32(js, "fee_base_msat", payinfo->fee_base_msat); json_add_u32(js, "fee_proportional_millionths", @@ -365,7 +339,7 @@ static bool json_add_blinded_paths(struct json_stream *js, static const char *recurrence_time_unit_name(u8 time_unit) { - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * `time_unit` defining 0 (seconds), 1 (days), 2 (months), 3 (years). */ switch (time_unit) { @@ -565,8 +539,6 @@ static void json_add_b12_invoice(struct json_stream *js, { bool valid = true; - if (invoice->chains) - json_add_chains(js, invoice->chains); if (invoice->chain) json_add_sha256(js, "chain", &invoice->chain->shad.sha); if (invoice->offer_id) @@ -634,7 +606,7 @@ static void json_add_b12_invoice(struct json_stream *js, if (invoice->recurrence_start) json_add_u32(js, "recurrence_start", *invoice->recurrence_start); - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - if the offer contained `recurrence`: * - MUST reject the invoice if `recurrence_basetime` is not * set. @@ -705,7 +677,7 @@ static void json_add_b12_invoice(struct json_stream *js, if (invoice->fallbacks) valid &= json_add_fallbacks(js, - invoice->chain ? invoice->chain : invoice->chains, + invoice->chain, invoice->fallbacks->fallbacks); /* BOLT-offers #12: @@ -752,14 +724,12 @@ static void json_add_invoice_request(struct json_stream *js, { bool valid = true; - if (invreq->chains) - json_add_chains(js, invreq->chains); if (invreq->chain) json_add_sha256(js, "chain", &invreq->chain->shad.sha); /* BOLT-offers #12: * - MUST fail the request if `payer_key` is not present. - * - MUST fail the request if `chains` does not include (or imply) a supported chain. + *... * - MUST fail the request if `features` contains unknown even bits. * - MUST fail the request if `offer_id` is not present. */ diff --git a/plugins/offers.h b/plugins/offers.h index 2c58df73d0d9..68feefa12d69 100644 --- a/plugins/offers.h +++ b/plugins/offers.h @@ -9,11 +9,8 @@ struct command; /* Helper to send a reply */ struct command_result *WARN_UNUSED_RESULT send_onion_reply(struct command *cmd, - /* Preferred */ struct tlv_onionmsg_payload_reply_path *reply_path, - /* Used if reply_path is NULL */ - const char *jsonbuf, - const jsmntok_t *replytok, + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path, const char *replyfield, const u8 *replydata); #endif /* LIGHTNING_PLUGINS_OFFERS_H */ diff --git a/plugins/offers_inv_hook.c b/plugins/offers_inv_hook.c index a2fa9e56b1d6..765e2da3e46b 100644 --- a/plugins/offers_inv_hook.c +++ b/plugins/offers_inv_hook.c @@ -10,9 +10,8 @@ struct inv { struct tlv_invoice *inv; - const char *buf; /* May be NULL */ - const jsmntok_t *replytok; + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path; struct tlv_onionmsg_payload_reply_path *reply_path; /* The offer, once we've looked it up. */ @@ -41,7 +40,7 @@ fail_inv_level(struct command *cmd, plugin_log(cmd->plugin, l, "%s", msg); /* Only reply if they gave us a path */ - if (!inv->replytok && !inv->reply_path) + if (!inv->reply_path && !inv->obs2_reply_path) return command_hook_success(cmd); /* Don't send back internal error details. */ @@ -55,7 +54,7 @@ fail_inv_level(struct command *cmd, errdata = tal_arr(cmd, u8, 0); towire_invoice_error(&errdata, err); - return send_onion_reply(cmd, inv->reply_path, inv->buf, inv->replytok, + return send_onion_reply(cmd, inv->reply_path, inv->obs2_reply_path, "invoice_error", errdata); } @@ -315,12 +314,10 @@ static struct command_result *listoffers_error(struct command *cmd, } struct command_result *handle_invoice(struct command *cmd, - const char *buf, - const jsmntok_t *invtok, - const jsmntok_t *replytok, - struct tlv_onionmsg_payload_reply_path *reply_path) + const u8 *invbin, + struct tlv_onionmsg_payload_reply_path *reply_path STEALS, + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path STEALS) { - const u8 *invbin = json_tok_bin_from_hex(cmd, buf, invtok); size_t len = tal_count(invbin); struct inv *inv = tal(cmd, struct inv); struct out_req *req; @@ -328,17 +325,8 @@ struct command_result *handle_invoice(struct command *cmd, int bad_feature; struct sha256 m, shash; - if (reply_path) { - inv->buf = NULL; - inv->replytok = NULL; - inv->reply_path = reply_path; - } else { - /* Make a copy of entire buffer, for later. */ - inv->buf = tal_dup_arr(inv, char, buf, replytok->end, 0); - inv->replytok = tal_dup_arr(inv, jsmntok_t, replytok, - json_next(replytok) - replytok, 0); - inv->reply_path = NULL; - } + inv->obs2_reply_path = tal_steal(inv, obs2_reply_path); + inv->reply_path = tal_steal(inv, reply_path); inv->inv = tlv_invoice_new(cmd); if (!fromwire_invoice(&invbin, &len, inv->inv)) { @@ -366,12 +354,14 @@ struct command_result *handle_invoice(struct command *cmd, * * The reader of an invoice_request: *... - * - MUST fail the request if `chains` does not include (or imply) a - * supported chain. + * - if `chain` is not present: + * - MUST fail the request if bitcoin is not a supported chain. + * - otherwise: + * - MUST fail the request if `chain` is not a supported chain. */ - if (!bolt12_chain_matches(inv->inv->chain, chainparams, inv->inv->chains)) { + if (!bolt12_chain_matches(inv->inv->chain, chainparams)) { return fail_inv(cmd, inv, - "Wrong chains %s", + "Wrong chain %s", tal_hex(tmpctx, inv->inv->chain)); } diff --git a/plugins/offers_inv_hook.h b/plugins/offers_inv_hook.h index 4e862d30abee..a733d6c4626e 100644 --- a/plugins/offers_inv_hook.h +++ b/plugins/offers_inv_hook.h @@ -3,10 +3,9 @@ #include "config.h" #include -/* We got an onionmessage with an invoice! replytok/reply_path could be NULL. */ +/* We got an onionmessage with an invoice! reply_path could be NULL. */ struct command_result *handle_invoice(struct command *cmd, - const char *buf, - const jsmntok_t *invtok, - const jsmntok_t *replytok, - struct tlv_onionmsg_payload_reply_path *reply_path); + const u8 *invbin, + struct tlv_onionmsg_payload_reply_path *reply_path STEALS, + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path STEALS); #endif /* LIGHTNING_PLUGINS_OFFERS_INV_HOOK_H */ diff --git a/plugins/offers_invreq_hook.c b/plugins/offers_invreq_hook.c index 0391b84e9b90..4562c7d7dbce 100644 --- a/plugins/offers_invreq_hook.c +++ b/plugins/offers_invreq_hook.c @@ -15,11 +15,8 @@ /* We need to keep the reply path around so we can reply with invoice */ struct invreq { struct tlv_invoice_request *invreq; - const char *buf; - /* If obsolete style */ - const jsmntok_t *replytok; - /* If modern style. */ struct tlv_onionmsg_payload_reply_path *reply_path; + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path; /* The offer, once we've looked it up. */ struct tlv_offer *offer; @@ -63,8 +60,7 @@ fail_invreq_level(struct command *cmd, errdata = tal_arr(cmd, u8, 0); towire_invoice_error(&errdata, err); - return send_onion_reply(cmd, invreq->reply_path, - invreq->buf, invreq->replytok, + return send_onion_reply(cmd, invreq->reply_path, invreq->obs2_reply_path, "invoice_error", errdata); } @@ -113,7 +109,7 @@ test_field(struct command *cmd, return NULL; } -/* BOLT-offers #12: +/* BOLT-offers-recurrence #12: * - if the invoice corresponds to an offer with `recurrence`: * ... * - if it sets `relative_expiry`: @@ -184,7 +180,7 @@ static struct command_result *createinvoice_done(struct command *cmd, json_tok_full(buf, t)); } - return send_onion_reply(cmd, ir->reply_path, ir->buf, ir->replytok, + return send_onion_reply(cmd, ir->reply_path, ir->obs2_reply_path, "invoice", rawinv); } @@ -234,7 +230,7 @@ static struct command_result *check_period(struct command *cmd, if (ir->offer->recurrence_base) basetime = ir->offer->recurrence_base->basetime; - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - if the invoice corresponds to an offer with `recurrence`: * - MUST set `recurrence_basetime` to the start of period #0 as * calculated by [Period Calculation](#offer-period-calculation). @@ -243,7 +239,7 @@ static struct command_result *check_period(struct command *cmd, period_idx = *ir->invreq->recurrence_counter; - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - if the offer had `recurrence_base` and `start_any_period` * was 1: * - MUST fail the request if there is no `recurrence_start` @@ -259,14 +255,14 @@ static struct command_result *check_period(struct command *cmd, return err; period_idx += *ir->invreq->recurrence_start; - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - MUST set (or not set) `recurrence_start` exactly as the * invoice_request did. */ ir->inv->recurrence_start = tal_dup(ir->inv, u32, ir->invreq->recurrence_start); } else { - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * * - otherwise: * - MUST fail the request if there is a `recurrence_start` @@ -279,7 +275,7 @@ static struct command_result *check_period(struct command *cmd, return err; } - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - if the offer has a `recurrence_limit`: * - MUST fail the request if the period index is greater than * `max_period`. @@ -313,7 +309,7 @@ static struct command_result *check_period(struct command *cmd, set_recurring_inv_expiry(ir->inv, paywindow_end); - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * * - if `recurrence_counter` is non-zero: *... @@ -479,7 +475,7 @@ static struct command_result *invreq_base_amount_simple(struct command *cmd, *amt = amount_msat(raw_amount); } else { - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * * - otherwise: * - MUST fail the request if it does not contain `amount`. @@ -538,7 +534,7 @@ static struct command_result *handle_amount_and_recurrence(struct command *cmd, /* Last of all, we handle recurrence details, which often requires * further lookups. */ - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - MUST set (or not set) `recurrence_counter` exactly as the * invoice_request did. */ @@ -727,7 +723,7 @@ static struct command_result *listoffers_done(struct command *cmd, } if (ir->offer->recurrence) { - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * * - if the offer had a `recurrence`: * - MUST fail the request if there is no `recurrence_counter` @@ -737,7 +733,7 @@ static struct command_result *listoffers_done(struct command *cmd, if (err) return err; } else { - /* BOLT-offers #12: + /* BOLT-offers-recurrence #12: * - otherwise (the offer had no `recurrence`): * - MUST fail the request if there is a `recurrence_counter` * field. @@ -758,10 +754,6 @@ static struct command_result *listoffers_done(struct command *cmd, * - MUST specify `chains` the offer is valid for. */ if (!streq(chainparams->network_name, "bitcoin")) { - if (deprecated_apis) { - ir->inv->chains = tal_arr(ir->inv, struct bitcoin_blkid, 1); - ir->inv->chains[0] = chainparams->genesis_blockhash; - } ir->inv->chain = tal_dup(ir->inv, struct bitcoin_blkid, &chainparams->genesis_blockhash); } @@ -836,28 +828,17 @@ static struct command_result *handle_offerless_request(struct command *cmd, } struct command_result *handle_invoice_request(struct command *cmd, - const char *buf, - const jsmntok_t *invreqtok, - const jsmntok_t *replytok, - struct tlv_onionmsg_payload_reply_path *reply_path) + const u8 *invreqbin, + struct tlv_onionmsg_payload_reply_path *reply_path, + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path) { - const u8 *invreqbin = json_tok_bin_from_hex(cmd, buf, invreqtok); size_t len = tal_count(invreqbin); struct invreq *ir = tal(cmd, struct invreq); struct out_req *req; int bad_feature; - /* Make a copy of entire buffer, for later. */ - if (reply_path) { - ir->buf = NULL; - ir->replytok = NULL; - ir->reply_path = reply_path; - } else { - ir->buf = tal_dup_arr(ir, char, buf, replytok->end, 0); - ir->replytok = tal_dup_arr(ir, jsmntok_t, replytok, - json_next(replytok) - replytok, 0); - ir->reply_path = NULL; - } + ir->obs2_reply_path = tal_steal(ir, obs2_reply_path); + ir->reply_path = tal_steal(ir, reply_path); ir->invreq = tlv_invoice_request_new(cmd); if (!fromwire_invoice_request(&invreqbin, &len, ir->invreq)) { @@ -885,11 +866,12 @@ struct command_result *handle_invoice_request(struct command *cmd, * * The reader of an invoice_request: *... - * - MUST fail the request if `chains` does not include (or imply) a - * supported chain. + * - if `chain` is not present: + * - MUST fail the request if bitcoin is not a supported chain. + * - otherwise: + * - MUST fail the request if `chain` is not a supported chain. */ - if (!bolt12_chain_matches(ir->invreq->chain, chainparams, - ir->invreq->chains)) { + if (!bolt12_chain_matches(ir->invreq->chain, chainparams)) { return fail_invreq(cmd, ir, "Wrong chain %s", tal_hex(tmpctx, ir->invreq->chain)); diff --git a/plugins/offers_invreq_hook.h b/plugins/offers_invreq_hook.h index 0ea8f023be1a..b33c8d86015e 100644 --- a/plugins/offers_invreq_hook.h +++ b/plugins/offers_invreq_hook.h @@ -7,10 +7,7 @@ extern u32 cltv_final; /* We got an onionmessage with an invreq! */ struct command_result *handle_invoice_request(struct command *cmd, - const char *buf, - const jsmntok_t *invreqtok, - /* Obsolete onion */ - const jsmntok_t *replytok, - /* Modern onion */ - struct tlv_onionmsg_payload_reply_path *reply_path); + const u8 *invreqbin, + struct tlv_onionmsg_payload_reply_path *reply_path STEALS, + struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path STEALS); #endif /* LIGHTNING_PLUGINS_OFFERS_INVREQ_HOOK_H */ diff --git a/tests/test_misc.py b/tests/test_misc.py index f26ddc2b3af8..b7e2120cd62a 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -2252,77 +2252,6 @@ def test_sendcustommsg(node_factory): ]) -def test_sendobsonionmessage(node_factory): - l1, l2, l3 = node_factory.line_graph(3, opts={'experimental-onion-messages': None}) - - blindedpathtool = os.path.join(os.path.dirname(__file__), "..", "devtools", "blindedpath") - - l1.rpc.call('sendobsonionmessage', - {'hops': - [{'id': l2.info['id']}, - {'id': l3.info['id']}]}) - assert l3.daemon.wait_for_log('Got obsolete onionmsg') - - # Now by SCID. - l1.rpc.call('sendobsonionmessage', - {'hops': - [{'id': l2.info['id'], - 'short_channel_id': l2.get_channel_scid(l3)}, - {'id': l3.info['id']}]}) - assert l3.daemon.wait_for_log('Got obsolete onionmsg') - - # Now test blinded path. - output = subprocess.check_output( - [blindedpathtool, '--simple-output', 'create', l2.info['id'], l3.info['id']] - ).decode('ASCII').strip() - - # First line is blinding, then then . - blinding, p1, p1enc, p2 = output.split('\n') - # First hop can't be blinded! - assert p1 == l2.info['id'] - - l1.rpc.call('sendobsonionmessage', - {'hops': - [{'id': l2.info['id'], - 'blinding': blinding, - 'enctlv': p1enc}, - {'id': p2}]}) - assert l3.daemon.wait_for_log('Got obsolete onionmsg') - - -@unittest.skipIf(not EXPERIMENTAL_FEATURES, "Needs sendobsonionmessage") -def test_sendobsonionmessage_reply(node_factory): - blindedpathtool = os.path.join(os.path.dirname(__file__), "..", "devtools", "blindedpath") - - plugin = os.path.join(os.path.dirname(__file__), "plugins", "onionmessage-reply.py") - l1, l2, l3 = node_factory.line_graph(3, opts={'plugin': plugin}) - - # Make reply path - output = subprocess.check_output( - [blindedpathtool, '--simple-output', 'create', l2.info['id'], l1.info['id']] - ).decode('ASCII').strip() - - # First line is blinding, then then . - blinding, p1, p1enc, p2 = output.split('\n') - # First hop can't be blinded! - assert p1 == l2.info['id'] - - # Also tests oversize payload which won't fit in 1366-byte onion. - l1.rpc.call('sendobsonionmessage', - {'hops': - [{'id': l2.info['id']}, - {'id': l3.info['id'], - 'invoice': '77' * 15000}], - 'reply_path': - {'blinding': blinding, - 'path': [{'id': p1, 'enctlv': p1enc}, {'id': p2}]}}) - - assert l3.daemon.wait_for_log('Got obsolete onionmsg reply_blinding reply_path') - assert l3.daemon.wait_for_log("Got onion_message invoice '{}'".format('77' * 15000)) - assert l3.daemon.wait_for_log('Sent reply via') - assert l1.daemon.wait_for_log('Got obsolete onionmsg') - - @pytest.mark.developer("needs --dev-force-privkey") def test_getsharedsecret(node_factory): """ diff --git a/tests/test_pay.py b/tests/test_pay.py index 709978ae82ca..2213066b3bb9 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -3259,6 +3259,7 @@ def test_reject_invalid_payload(node_factory): l1.rpc.waitsendpay(inv['payment_hash']) +@pytest.mark.skip("Needs to be updated for modern onion") @unittest.skipIf(not EXPERIMENTAL_FEATURES, "Needs blinding args to sendpay") def test_sendpay_blinding(node_factory): l1, l2, l3, l4 = node_factory.line_graph(4) diff --git a/wire/bolt12_exp_wire.csv b/wire/bolt12_exp_wire.csv index a53ca3cdf8a5..4077edb8d222 100644 --- a/wire/bolt12_exp_wire.csv +++ b/wire/bolt12_exp_wire.csv @@ -42,8 +42,6 @@ subtypedata,blinded_path,first_node_id,point, subtypedata,blinded_path,blinding,point, subtypedata,blinded_path,num_hops,byte, subtypedata,blinded_path,path,onionmsg_path,num_hops -tlvtype,invoice_request,chains,2 -tlvdata,invoice_request,chains,chains,chain_hash,... tlvtype,invoice_request,chain,3 tlvdata,invoice_request,chain,chain,chain_hash, tlvtype,invoice_request,offer_id,4 @@ -68,8 +66,6 @@ tlvtype,invoice_request,replace_invoice,56 tlvdata,invoice_request,replace_invoice,payment_hash,sha256, tlvtype,invoice_request,payer_signature,240 tlvdata,invoice_request,payer_signature,sig,bip340sig, -tlvtype,invoice,chains,2 -tlvdata,invoice,chains,chains,chain_hash,... tlvtype,invoice,chain,3 tlvdata,invoice,chain,chain,chain_hash, tlvtype,invoice,offer_id,4 diff --git a/wire/bolt12_wire.csv b/wire/bolt12_wire.csv index a53ca3cdf8a5..4077edb8d222 100644 --- a/wire/bolt12_wire.csv +++ b/wire/bolt12_wire.csv @@ -42,8 +42,6 @@ subtypedata,blinded_path,first_node_id,point, subtypedata,blinded_path,blinding,point, subtypedata,blinded_path,num_hops,byte, subtypedata,blinded_path,path,onionmsg_path,num_hops -tlvtype,invoice_request,chains,2 -tlvdata,invoice_request,chains,chains,chain_hash,... tlvtype,invoice_request,chain,3 tlvdata,invoice_request,chain,chain,chain_hash, tlvtype,invoice_request,offer_id,4 @@ -68,8 +66,6 @@ tlvtype,invoice_request,replace_invoice,56 tlvdata,invoice_request,replace_invoice,payment_hash,sha256, tlvtype,invoice_request,payer_signature,240 tlvdata,invoice_request,payer_signature,sig,bip340sig, -tlvtype,invoice,chains,2 -tlvdata,invoice,chains,chains,chain_hash,... tlvtype,invoice,chain,3 tlvdata,invoice,chain,chain,chain_hash, tlvtype,invoice,offer_id,4 diff --git a/wire/extracted_bolt12_01_recurrence.patch b/wire/extracted_bolt12_01_recurrence.patch new file mode 100644 index 000000000000..186841e476dc --- /dev/null +++ b/wire/extracted_bolt12_01_recurrence.patch @@ -0,0 +1,48 @@ +diff --git b/wire/bolt12_wire.csv a/wire/bolt12_wire.csv +index 726c3c0a1..a53ca3cdf 100644 +--- b/wire/bolt12_wire.csv ++++ a/wire/bolt12_wire.csv +@@ -18,6 +18,18 @@ tlvtype,offer,quantity_min,22 + tlvdata,offer,quantity_min,min,tu64, + tlvtype,offer,quantity_max,24 + tlvdata,offer,quantity_max,max,tu64, ++tlvtype,offer,recurrence,26 ++tlvdata,offer,recurrence,time_unit,byte, ++tlvdata,offer,recurrence,period,tu32, ++tlvtype,offer,recurrence_paywindow,64 ++tlvdata,offer,recurrence_paywindow,seconds_before,u32, ++tlvdata,offer,recurrence_paywindow,proportional_amount,byte, ++tlvdata,offer,recurrence_paywindow,seconds_after,tu32, ++tlvtype,offer,recurrence_limit,66 ++tlvdata,offer,recurrence_limit,max_period,tu32, ++tlvtype,offer,recurrence_base,28 ++tlvdata,offer,recurrence_base,start_any_period,byte, ++tlvdata,offer,recurrence_base,basetime,tu64, + tlvtype,offer,node_id,30 + tlvdata,offer,node_id,node_id,point32, + tlvtype,offer,send_invoice,54 +@@ -40,6 +54,10 @@ tlvtype,invoice_request,features,12 + tlvdata,invoice_request,features,features,byte,... + tlvtype,invoice_request,quantity,32 + tlvdata,invoice_request,quantity,quantity,tu64, ++tlvtype,invoice_request,recurrence_counter,36 ++tlvdata,invoice_request,recurrence_counter,counter,tu32, ++tlvtype,invoice_request,recurrence_start,68 ++tlvdata,invoice_request,recurrence_start,period_offset,tu32, + tlvtype,invoice_request,payer_key,38 + tlvdata,invoice_request,payer_key,key,point32, + tlvtype,invoice_request,payer_note,39 +@@ -74,6 +94,13 @@ tlvtype,invoice,quantity,32 + tlvdata,invoice,quantity,quantity,tu64, + tlvtype,invoice,refund_for,34 + tlvdata,invoice,refund_for,refunded_payment_hash,sha256, ++tlvtype,invoice,recurrence_counter,36 ++tlvdata,invoice,recurrence_counter,counter,tu32, ++tlvtype,invoice,send_invoice,54 ++tlvtype,invoice,recurrence_start,68 ++tlvdata,invoice,recurrence_start,period_offset,tu32, ++tlvtype,invoice,recurrence_basetime,64 ++tlvdata,invoice,recurrence_basetime,basetime,tu64, + tlvtype,invoice,payer_key,38 + tlvdata,invoice,payer_key,key,point32, + tlvtype,invoice,payer_note,39 diff --git a/wire/extracted_onion_01_offers.patch b/wire/extracted_onion_01_offers.patch index 24e876856b93..90c0cb6c41ac 100644 --- a/wire/extracted_onion_01_offers.patch +++ b/wire/extracted_onion_01_offers.patch @@ -1,30 +1,29 @@ --- wire/extracted_onion_wire_csv 2020-03-25 10:24:12.861645774 +1030 +++ - 2020-03-26 13:47:13.498294435 +1030 -@@ -8,6 +8,31 @@ +@@ -8,6 +8,30 @@ tlvtype,tlv_payload,payment_data,8 tlvdata,tlv_payload,payment_data,payment_secret,byte,32 tlvdata,tlv_payload,payment_data,total_msat,tu64, -+tlvtype,onionmsg_payload,obs_next_node_id,4 -+tlvdata,onionmsg_payload,obs_next_node_id,node_id,point, -+tlvtype,onionmsg_payload,obs_next_short_channel_id,6 -+tlvdata,onionmsg_payload,obs_next_short_channel_id,short_channel_id,short_channel_id, -+tlvtype,onionmsg_payload,obs_reply_path,8 -+tlvdata,onionmsg_payload,obs_reply_path,blinding,point, -+tlvdata,onionmsg_payload,obs_reply_path,path,onionmsg_path,... -+tlvtype,onionmsg_payload,enctlv,10 -+tlvdata,onionmsg_payload,enctlv,enctlv,byte,... -+tlvtype,onionmsg_payload,obs_blinding,12 -+tlvdata,onionmsg_payload,obs_blinding,blinding,point, -+tlvtype,onionmsg_payload,invoice_request,64 -+tlvdata,onionmsg_payload,invoice_request,invoice_request,byte,... -+tlvtype,onionmsg_payload,invoice,66 -+tlvdata,onionmsg_payload,invoice,invoice,byte,... -+tlvtype,onionmsg_payload,invoice_error,68 -+tlvdata,onionmsg_payload,invoice_error,invoice_error,byte,... -+tlvtype,encmsg_tlvs,next_node_id,4 -+tlvdata,encmsg_tlvs,next_node_id,node_id,point, -+tlvtype,encmsg_tlvs,obs_next_short_channel_id,6 -+tlvdata,encmsg_tlvs,obs_next_short_channel_id,short_channel_id,short_channel_id, ++tlvtype,obs2_onionmsg_payload,reply_path,2 ++tlvdata,obs2_onionmsg_payload,reply_path,first_node_id,point, ++tlvdata,obs2_onionmsg_payload,reply_path,blinding,point, ++tlvdata,obs2_onionmsg_payload,reply_path,path,onionmsg_path,... ++tlvtype,obs2_onionmsg_payload,enctlv,10 ++tlvdata,obs2_onionmsg_payload,enctlv,enctlv,byte,... ++tlvtype,obs2_onionmsg_payload,invoice_request,64 ++tlvdata,obs2_onionmsg_payload,invoice_request,invoice_request,byte,... ++tlvtype,obs2_onionmsg_payload,invoice,66 ++tlvdata,obs2_onionmsg_payload,invoice,invoice,byte,... ++tlvtype,obs2_onionmsg_payload,invoice_error,68 ++tlvdata,obs2_onionmsg_payload,invoice_error,invoice_error,byte,... ++tlvtype,obs2_encmsg_tlvs,padding,1 ++tlvdata,obs2_encmsg_tlvs,padding,pad,byte,... ++tlvtype,obs2_encmsg_tlvs,next_node_id,4 ++tlvdata,obs2_encmsg_tlvs,next_node_id,node_id,point, ++tlvtype,obs2_encmsg_tlvs,next_blinding,12 ++tlvdata,obs2_encmsg_tlvs,next_blinding,blinding,point, ++tlvtype,obs2_encmsg_tlvs,self_id,14 ++tlvdata,obs2_encmsg_tlvs,self_id,data,byte,... +subtype,onionmsg_path +subtypedata,onionmsg_path,node_id,point, +subtypedata,onionmsg_path,enclen,u16, diff --git a/wire/extracted_onion_02_newonion.patch b/wire/extracted_onion_02_newonion.patch deleted file mode 100644 index 6a36d3516a99..000000000000 --- a/wire/extracted_onion_02_newonion.patch +++ /dev/null @@ -1,30 +0,0 @@ ---- onion_wire.csv 2021-08-25 12:41:02.872253965 +0930 -+++ onion_wire.csv.raw 2021-08-25 13:52:00.748767887 +0930 -@@ -8,6 +8,10 @@ tlvdata,tlv_payload,short_channel_id,short_channel_id,short_channel_id, - tlvtype,tlv_payload,payment_data,8 - tlvdata,tlv_payload,payment_data,payment_secret,byte,32 - tlvdata,tlv_payload,payment_data,total_msat,tu64, -+tlvtype,onionmsg_payload,reply_path,2 -+tlvdata,onionmsg_payload,reply_path,first_node_id,point, -+tlvdata,onionmsg_payload,reply_path,blinding,point, -+tlvdata,onionmsg_payload,reply_path,path,onionmsg_path,... - tlvtype,onionmsg_payload,obs_next_node_id,4 - tlvdata,onionmsg_payload,obs_next_node_id,node_id,point, - tlvtype,onionmsg_payload,obs_next_short_channel_id,6 -@@ -29,10 +29,16 @@ tlvtype,onionmsg_payload,invoice,66 - tlvdata,onionmsg_payload,invoice,invoice,byte,... - tlvtype,onionmsg_payload,invoice_error,68 - tlvdata,onionmsg_payload,invoice_error,invoice_error,byte,... -+tlvtype,encmsg_tlvs,padding,1 -+tlvdata,encmsg_tlvs,padding,pad,byte,... - tlvtype,encmsg_tlvs,next_node_id,4 - tlvdata,encmsg_tlvs,next_node_id,node_id,point, - tlvtype,encmsg_tlvs,obs_next_short_channel_id,6 - tlvdata,encmsg_tlvs,obs_next_short_channel_id,short_channel_id,short_channel_id, -+tlvtype,encmsg_tlvs,next_blinding,12 -+tlvdata,encmsg_tlvs,next_blinding,blinding,point, -+tlvtype,encmsg_tlvs,self_id,14 -+tlvdata,encmsg_tlvs,self_id,data,byte,... - subtype,onionmsg_path - subtypedata,onionmsg_path,node_id,point, - subtypedata,onionmsg_path,enclen,u16, diff --git a/wire/extracted_onion_exp_enctlv.patch b/wire/extracted_onion_exp_enctlv.patch index ea55b7704492..1897193b8c8b 100644 --- a/wire/extracted_onion_exp_enctlv.patch +++ b/wire/extracted_onion_exp_enctlv.patch @@ -1,13 +1,15 @@ --- wire/extracted_onion_wire_csv 2020-02-25 05:52:39.612291156 +1030 +++ - 2020-03-20 15:11:55.763880895 +1030 -@@ -8,6 +8,10 @@ tlvdata,tlv_payload,short_channel_id,short_channel_id,short_channel_id, +--- wire/onion_exp_wire.csv.~1~ 2021-11-17 10:56:59.947630815 +1030 ++++ wire/onion_exp_wire.csv 2021-11-17 10:59:39.304581244 +1030 +@@ -8,6 +8,10 @@ tlvtype,tlv_payload,payment_data,8 tlvdata,tlv_payload,payment_data,payment_secret,byte,32 tlvdata,tlv_payload,payment_data,total_msat,tu64, -+tlvtype,tlv_payload,enctlv,10 -+tlvdata,tlv_payload,enctlv,enctlv,byte,... -+tlvtype,tlv_payload,blinding_seed,12 -+tlvdata,tlv_payload,blinding_seed,blinding_seed,pubkey, - tlvtype,onionmsg_payload,reply_path,2 - tlvdata,onionmsg_payload,reply_path,first_node_id,point, - tlvdata,onionmsg_payload,reply_path,blinding,point, ++tlvtype,tlv_payload,encrypted_recipient_data,10 ++tlvdata,tlv_payload,encrypted_recipient_data,encrypted_data,byte,... ++tlvtype,tlv_payload,blinding_point,12 ++tlvdata,tlv_payload,blinding_point,blinding,point, + tlvtype,obs2_onionmsg_payload,reply_path,2 + tlvdata,obs2_onionmsg_payload,reply_path,first_node_id,point, + tlvdata,obs2_onionmsg_payload,reply_path,blinding,point, diff --git a/wire/extracted_peer_01_offers.patch b/wire/extracted_peer_01_offers.patch deleted file mode 100644 index ca910f6ad25a..000000000000 --- a/wire/extracted_peer_01_offers.patch +++ /dev/null @@ -1,12 +0,0 @@ ---- wire/extracted_peer_wire_csv 2020-03-11 10:30:35.744376417 +1030 -+++ - 2020-03-26 13:47:13.409755567 +1030 -@@ -211,3 +211,9 @@ - msgdata,gossip_timestamp_filter,chain_hash,chain_hash, - msgdata,gossip_timestamp_filter,first_timestamp,u32, - msgdata,gossip_timestamp_filter,timestamp_range,u32, -+msgtype,obs_onion_message,385,option_onion_messages -+msgdata,obs_onion_message,len,u16, -+msgdata,obs_onion_message,onionmsg,byte,len -+msgdata,obs_onion_message,obs_onion_message_tlvs,obs_onion_message_tlvs, -+tlvtype,obs_onion_message_tlvs,blinding,2 -+tlvdata,obs_onion_message_tlvs,blinding,blinding,point, diff --git a/wire/extracted_peer_05_newonion.patch b/wire/extracted_peer_05_newonion.patch index 97c1629e5d62..b14d5471e381 100644 --- a/wire/extracted_peer_05_newonion.patch +++ b/wire/extracted_peer_05_newonion.patch @@ -1,10 +1,14 @@ --- peer_wire.csv 2021-08-25 12:41:02.876254003 +0930 +++ peer_wire.csv.raw 2021-08-25 13:42:31.991693809 +0930 -@@ -320,3 +210,7 @@ - msgdata,obs_onion_message,obs_onion_message_tlvs,obs_onion_message_tlvs, - tlvtype,obs_onion_message_tlvs,blinding,2 - tlvdata,obs_onion_message_tlvs,blinding,blinding,point, -+msgtype,onion_message,387,option_onion_messages +@@ -320,3 +210,11 @@ + msgdata,gossip_timestamp_filter,chain_hash,chain_hash, + msgdata,gossip_timestamp_filter,first_timestamp,u32, + msgdata,gossip_timestamp_filter,timestamp_range,u32, ++msgtype,obs2_onion_message,387,option_onion_messages ++msgdata,obs2_onion_message,blinding,point, ++msgdata,obs2_onion_message,len,u16, ++msgdata,obs2_onion_message,onionmsg,byte,len ++msgtype,onion_message,513,option_onion_messages +msgdata,onion_message,blinding,point, +msgdata,onion_message,len,u16, +msgdata,onion_message,onionmsg,byte,len diff --git a/wire/onion_wire.csv b/wire/onion_wire.csv index e60849a87cd3..2ac0c4cff516 100644 --- a/wire/onion_wire.csv +++ b/wire/onion_wire.csv @@ -8,41 +8,56 @@ tlvdata,tlv_payload,short_channel_id,short_channel_id,short_channel_id, tlvtype,tlv_payload,payment_data,8 tlvdata,tlv_payload,payment_data,payment_secret,byte,32 tlvdata,tlv_payload,payment_data,total_msat,tu64, +tlvtype,obs2_onionmsg_payload,reply_path,2 +tlvdata,obs2_onionmsg_payload,reply_path,first_node_id,point, +tlvdata,obs2_onionmsg_payload,reply_path,blinding,point, +tlvdata,obs2_onionmsg_payload,reply_path,path,onionmsg_path,... +tlvtype,obs2_onionmsg_payload,enctlv,10 +tlvdata,obs2_onionmsg_payload,enctlv,enctlv,byte,... +tlvtype,obs2_onionmsg_payload,invoice_request,64 +tlvdata,obs2_onionmsg_payload,invoice_request,invoice_request,byte,... +tlvtype,obs2_onionmsg_payload,invoice,66 +tlvdata,obs2_onionmsg_payload,invoice,invoice,byte,... +tlvtype,obs2_onionmsg_payload,invoice_error,68 +tlvdata,obs2_onionmsg_payload,invoice_error,invoice_error,byte,... +tlvtype,obs2_encmsg_tlvs,padding,1 +tlvdata,obs2_encmsg_tlvs,padding,pad,byte,... +tlvtype,obs2_encmsg_tlvs,next_node_id,4 +tlvdata,obs2_encmsg_tlvs,next_node_id,node_id,point, +tlvtype,obs2_encmsg_tlvs,next_blinding,12 +tlvdata,obs2_encmsg_tlvs,next_blinding,blinding,point, +tlvtype,obs2_encmsg_tlvs,self_id,14 +tlvdata,obs2_encmsg_tlvs,self_id,data,byte,... +tlvtype,tlv_payload,encrypted_recipient_data,10 +tlvdata,tlv_payload,encrypted_recipient_data,encrypted_data,byte,... +tlvtype,tlv_payload,blinding_point,12 +tlvdata,tlv_payload,blinding_point,blinding,point, +tlvtype,encrypted_data_tlv,padding,1 +tlvdata,encrypted_data_tlv,padding,padding,byte,... +tlvtype,encrypted_data_tlv,short_channel_id,2 +tlvdata,encrypted_data_tlv,short_channel_id,short_channel_id,short_channel_id, +tlvtype,encrypted_data_tlv,next_node_id,4 +tlvdata,encrypted_data_tlv,next_node_id,node_id,point, +tlvtype,encrypted_data_tlv,path_id,6 +tlvdata,encrypted_data_tlv,path_id,data,byte,... +tlvtype,encrypted_data_tlv,next_blinding_override,8 +tlvdata,encrypted_data_tlv,next_blinding_override,blinding,point, tlvtype,onionmsg_payload,reply_path,2 tlvdata,onionmsg_payload,reply_path,first_node_id,point, tlvdata,onionmsg_payload,reply_path,blinding,point, tlvdata,onionmsg_payload,reply_path,path,onionmsg_path,... -tlvtype,onionmsg_payload,obs_next_node_id,4 -tlvdata,onionmsg_payload,obs_next_node_id,node_id,point, -tlvtype,onionmsg_payload,obs_next_short_channel_id,6 -tlvdata,onionmsg_payload,obs_next_short_channel_id,short_channel_id,short_channel_id, -tlvtype,onionmsg_payload,obs_reply_path,8 -tlvdata,onionmsg_payload,obs_reply_path,blinding,point, -tlvdata,onionmsg_payload,obs_reply_path,path,onionmsg_path,... -tlvtype,onionmsg_payload,enctlv,10 -tlvdata,onionmsg_payload,enctlv,enctlv,byte,... -tlvtype,onionmsg_payload,obs_blinding,12 -tlvdata,onionmsg_payload,obs_blinding,blinding,point, +tlvtype,onionmsg_payload,encrypted_data_tlv,4 +tlvdata,onionmsg_payload,encrypted_data_tlv,encrypted_data_tlv,byte,... tlvtype,onionmsg_payload,invoice_request,64 tlvdata,onionmsg_payload,invoice_request,invoice_request,byte,... tlvtype,onionmsg_payload,invoice,66 tlvdata,onionmsg_payload,invoice,invoice,byte,... tlvtype,onionmsg_payload,invoice_error,68 tlvdata,onionmsg_payload,invoice_error,invoice_error,byte,... -tlvtype,encmsg_tlvs,padding,1 -tlvdata,encmsg_tlvs,padding,pad,byte,... -tlvtype,encmsg_tlvs,next_node_id,4 -tlvdata,encmsg_tlvs,next_node_id,node_id,point, -tlvtype,encmsg_tlvs,obs_next_short_channel_id,6 -tlvdata,encmsg_tlvs,obs_next_short_channel_id,short_channel_id,short_channel_id, -tlvtype,encmsg_tlvs,next_blinding,12 -tlvdata,encmsg_tlvs,next_blinding,blinding,point, -tlvtype,encmsg_tlvs,self_id,14 -tlvdata,encmsg_tlvs,self_id,data,byte,... subtype,onionmsg_path subtypedata,onionmsg_path,node_id,point, subtypedata,onionmsg_path,enclen,u16, -subtypedata,onionmsg_path,enctlv,byte,enclen +subtypedata,onionmsg_path,encrypted_recipient_data,byte,enclen msgtype,invalid_realm,PERM|1 msgtype,temporary_node_failure,NODE|2 msgtype,permanent_node_failure,PERM|NODE|2 diff --git a/wire/peer_wire.c b/wire/peer_wire.c index d0515fda5f15..aa3f3133eb50 100644 --- a/wire/peer_wire.c +++ b/wire/peer_wire.c @@ -33,8 +33,8 @@ static bool unknown_type(enum peer_wire t) case WIRE_QUERY_CHANNEL_RANGE: case WIRE_REPLY_CHANNEL_RANGE: case WIRE_GOSSIP_TIMESTAMP_FILTER: + case WIRE_OBS2_ONION_MESSAGE: case WIRE_ONION_MESSAGE: - case WIRE_OBS_ONION_MESSAGE: case WIRE_TX_ADD_INPUT: case WIRE_TX_REMOVE_INPUT: case WIRE_TX_ADD_OUTPUT: @@ -63,8 +63,8 @@ bool is_msg_for_gossipd(const u8 *cursor) case WIRE_REPLY_SHORT_CHANNEL_IDS_END: case WIRE_QUERY_CHANNEL_RANGE: case WIRE_REPLY_CHANNEL_RANGE: + case WIRE_OBS2_ONION_MESSAGE: case WIRE_ONION_MESSAGE: - case WIRE_OBS_ONION_MESSAGE: return true; case WIRE_WARNING: case WIRE_INIT: diff --git a/wire/peer_wire.csv b/wire/peer_wire.csv index 85cf20b4d267..4043c63509bf 100644 --- a/wire/peer_wire.csv +++ b/wire/peer_wire.csv @@ -328,13 +328,11 @@ msgtype,gossip_timestamp_filter,265,gossip_queries msgdata,gossip_timestamp_filter,chain_hash,chain_hash, msgdata,gossip_timestamp_filter,first_timestamp,u32, msgdata,gossip_timestamp_filter,timestamp_range,u32, -msgtype,obs_onion_message,385,option_onion_messages -msgdata,obs_onion_message,len,u16, -msgdata,obs_onion_message,onionmsg,byte,len -msgdata,obs_onion_message,obs_onion_message_tlvs,obs_onion_message_tlvs, -tlvtype,obs_onion_message_tlvs,blinding,2 -tlvdata,obs_onion_message_tlvs,blinding,blinding,point, -msgtype,onion_message,387,option_onion_messages +msgtype,obs2_onion_message,387,option_onion_messages +msgdata,obs2_onion_message,blinding,point, +msgdata,obs2_onion_message,len,u16, +msgdata,obs2_onion_message,onionmsg,byte,len +msgtype,onion_message,513,option_onion_messages msgdata,onion_message,blinding,point, msgdata,onion_message,len,u16, msgdata,onion_message,onionmsg,byte,len