Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BOLT update, including payment metadata support #5086

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
bolt11: support payment_metadata.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
  • Loading branch information
rustyrussell committed Mar 31, 2022
commit 42f5df5f0018c479d81fff7ea130a050da93e7d8
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ CCANDIR := ccan

# Where we keep the BOLT RFCs
BOLTDIR := ../lightning-rfc/
DEFAULT_BOLTVERSION := 93909f67f6a48ee3f155a6224c182e612dd5f187
DEFAULT_BOLTVERSION := f6c4d7604150986894bcb46d67c5c88680740b12
# Can be overridden on cmdline.
BOLTVERSION := $(DEFAULT_BOLTVERSION)

Expand Down
53 changes: 50 additions & 3 deletions common/bolt11.c
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,33 @@ static char *decode_9(struct bolt11 *b11,
return NULL;
}

/* BOLT #11:
*
* `m` (27): `data_length` variable. Additional metadata to attach to
* the payment. Note that the size of this field is limited by the
* maximum hop payload size. Long metadata fields reduce the maximum
* route length.
*/
static char *decode_m(struct bolt11 *b11,
struct hash_u5 *hu5,
u5 **data, size_t *data_len,
size_t data_length,
bool *have_m)
{
size_t mlen = (data_length * 5) / 8;

if (*have_m)
return unknown_field(b11, hu5, data, data_len, 'm',
data_length);

b11->metadata = tal_arr(b11, u8, mlen);
pull_bits_certain(hu5, data, data_len, b11->metadata,
data_length * 5, false);

*have_m = true;
return NULL;
}

struct bolt11 *new_bolt11(const tal_t *ctx,
const struct amount_msat *msat TAKES)
{
Expand All @@ -535,6 +562,7 @@ struct bolt11 *new_bolt11(const tal_t *ctx,
*/
b11->min_final_cltv_expiry = 18;
b11->payment_secret = NULL;
b11->metadata = NULL;

if (msat)
b11->msat = tal_dup(b11, struct amount_msat, msat);
Expand All @@ -557,7 +585,7 @@ struct bolt11 *bolt11_decode_nosig(const tal_t *ctx, const char *str,
struct bolt11 *b11 = new_bolt11(ctx, NULL);
struct hash_u5 hu5;
bool have_p = false, have_d = false, have_h = false,
have_x = false, have_c = false, have_s = false;
have_x = false, have_c = false, have_s = false, have_m = false;

*have_n = false;
b11->routes = tal_arr(b11, struct route_info *, 0);
Expand Down Expand Up @@ -760,6 +788,10 @@ struct bolt11 *bolt11_decode_nosig(const tal_t *ctx, const char *str,
problem = decode_s(b11, &hu5, &data, &data_len,
data_length, &have_s);
break;
case 'm':
problem = decode_m(b11, &hu5, &data, &data_len,
data_length, &have_m);
break;
default:
unknown_field(b11, &hu5, &data, &data_len,
bech32_charset[type], data_length);
Expand Down Expand Up @@ -936,6 +968,11 @@ static void encode_p(u5 **data, const struct sha256 *hash)
push_field(data, 'p', hash, 256);
}

static void encode_m(u5 **data, const u8 *metadata)
{
push_field(data, 'm', metadata, tal_bytelen(metadata) * CHAR_BIT);
}

static void encode_d(u5 **data, const char *description)
{
push_field(data, 'd', description, strlen(description) * CHAR_BIT);
Expand Down Expand Up @@ -1011,13 +1048,20 @@ static void encode_r(u5 **data, const struct route_info *r)
tal_free(rinfo);
}

static void maybe_encode_9(u5 **data, const u8 *features)
static void maybe_encode_9(u5 **data, const u8 *features,
bool have_payment_metadata)
{
u5 *f5 = tal_arr(NULL, u5, 0);

for (size_t i = 0; i < tal_count(features) * CHAR_BIT; i++) {
if (!feature_is_set(features, i))
continue;

/* Don't set option_payment_metadata unless we acually use it */
if (!have_payment_metadata
&& COMPULSORY_FEATURE(i) == OPT_PAYMENT_METADATA)
continue;

/* We expand it out so it makes a BE 5-bit/btye bitfield */
set_feature_bit(&f5, (i / 5) * 8 + (i % 5));
}
Expand Down Expand Up @@ -1136,6 +1180,9 @@ char *bolt11_encode_(const tal_t *ctx,
if (b11->expiry != DEFAULT_X)
encode_x(&data, b11->expiry);

if (b11->metadata)
encode_m(&data, b11->metadata);

/* BOLT #11:
* - MUST include one `c` field (`min_final_cltv_expiry`).
*/
Expand All @@ -1150,7 +1197,7 @@ char *bolt11_encode_(const tal_t *ctx,
for (size_t i = 0; i < tal_count(b11->routes); i++)
encode_r(&data, b11->routes[i]);

maybe_encode_9(&data, b11->features);
maybe_encode_9(&data, b11->features, b11->metadata != NULL);

list_for_each(&b11->extra_fields, extra, list)
if (!encode_extra(&data, extra))
Expand Down
3 changes: 3 additions & 0 deletions common/bolt11.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ struct bolt11 {
/* Features bitmap, if any. */
u8 *features;

/* Optional metadata to send with payment. */
u8 *metadata;

struct list_head extra_fields;
};

Expand Down
2 changes: 2 additions & 0 deletions common/bolt11_json.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ void json_add_bolt11(struct json_stream *response,
b11->payment_secret);
if (b11->features)
json_add_hex_talarr(response, "features", b11->features);
if (b11->metadata)
json_add_hex_talarr(response, "payment_metadata", b11->metadata);
if (tal_count(b11->fallbacks)) {
json_array_start(response, "fallbacks");
for (size_t i = 0; i < tal_count(b11->fallbacks); i++)
Expand Down
10 changes: 9 additions & 1 deletion common/features.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ static const struct feature_style feature_styles[] = {
.copy_style = { [INIT_FEATURE] = FEATURE_REPRESENT,
[NODE_ANNOUNCE_FEATURE] = FEATURE_REPRESENT,
[CHANNEL_FEATURE] = FEATURE_DONT_REPRESENT } },
{ OPT_PAYMENT_METADATA,
.copy_style = { [INIT_FEATURE] = FEATURE_DONT_REPRESENT,
[NODE_ANNOUNCE_FEATURE] = FEATURE_DONT_REPRESENT,
/* Note: we don't actually set this in invoices, since
* we don't need to use it, but if we don't set it here
* we refuse to parse it. */
[BOLT11_FEATURE] = FEATURE_REPRESENT,
[CHANNEL_FEATURE] = FEATURE_DONT_REPRESENT } },
};

struct dependency {
Expand Down Expand Up @@ -416,7 +424,7 @@ const char *feature_name(const tal_t *ctx, size_t f)
"option_provide_peer_backup", /* https://github.com/lightningnetwork/lightning-rfc/pull/881 */
NULL,
NULL,
NULL,
"option_payment_metadata",
NULL, /* 50/51 */
NULL,
"option_keysend",
Expand Down
2 changes: 2 additions & 0 deletions common/features.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,15 @@ const char *fmt_featurebits(const tal_t *ctx, const u8 *featurebits);
* | 20/21 | `option_anchor_outputs` |... IN ...
* | 22/23 | `option_anchors_zero_fee_htlc_tx` |... IN ...
* | 26/27 | `option_shutdown_anysegwit` |... IN ...
* | 48/49 | `option_payment_metadata` |... 9 ...
*/
#define OPT_PAYMENT_SECRET 14
#define OPT_BASIC_MPP 16
#define OPT_LARGE_CHANNELS 18
#define OPT_ANCHOR_OUTPUTS 20
#define OPT_ANCHORS_ZERO_FEE_HTLC_TX 22
#define OPT_SHUTDOWN_ANYSEGWIT 26
#define OPT_PAYMENT_METADATA 48

/* BOLT-f53ca2301232db780843e894f55d95d512f297f9 #9:
* | 28/29 | `option_dual_fund` | ... IN9 ...
Expand Down
46 changes: 46 additions & 0 deletions common/test/run-bolt11.c
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,52 @@ int main(int argc, char *argv[])
memset(b11->payment_secret, 0x11, sizeof(*b11->payment_secret));
test_b11("lnbc9678785340p1pwmna7lpp5gc3xfm08u9qy06djf8dfflhugl6p7lgza6dsjxq454gxhj9t7a0sd8dgfkx7cmtwd68yetpd5s9xar0wfjn5gpc8qhrsdfq24f5ggrxdaezqsnvda3kkum5wfjkzmfqf3jkgem9wgsyuctwdus9xgrcyqcjcgpzgfskx6eqf9hzqnteypzxz7fzypfhg6trddjhygrcyqezcgpzfysywmm5ypxxjemgw3hxjmn8yptk7untd9hxwg3q2d6xjcmtv4ezq7pqxgsxzmnyyqcjqmt0wfjjq6t5v4khxsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsxqyjw5qcqp2rzjq0gxwkzc8w6323m55m4jyxcjwmy7stt9hwkwe2qxmy8zpsgg7jcuwz87fcqqeuqqqyqqqqlgqqqqn3qq9q9qrsgqrvgkpnmps664wgkp43l22qsgdw4ve24aca4nymnxddlnp8vh9v2sdxlu5ywdxefsfvm0fq3sesf08uf6q9a2ke0hc9j6z6wlxg5z5kqpu2v9wz", b11, NULL);

/* BOLT #11:
* > ### Please send 0.01 BTC with payment metadata 0x01fafaf0
* > lnbc10m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp9wpshjmt9de6zqmt9w3skgct5vysxjmnnd9jx2mq8q8a04uqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q2gqqqqqqsgq7hf8he7ecf7n4ffphs6awl9t6676rrclv9ckg3d3ncn7fct63p6s365duk5wrk202cfy3aj5xnnp5gs3vrdvruverwwq7yzhkf5a3xqpd05wjc
*
* Breakdown:
*
* * `lnbc`: prefix, Lightning on Bitcoin mainnet
* * `10m`: amount (10 milli-bitcoin)
* * `1`: Bech32 separator
* * `pvjluez`: timestamp (1496314658)
* * `p`: payment hash
* * `p5`: `data_length` (`p` = 1, `5` = 20; 1 * 32 + 20 == 52)
* * `qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypq`: payment hash 0001020304050607080900010203040506070809000102030405060708090102
* * `d`: short description
* * `p9`: `data_length` (`p` = 1, `9` = 5; 1 * 32 + 5 == 37)
* * `wpshjmt9de6zqmt9w3skgct5vysxjmnnd9jx2`: 'payment metadata inside'
* * `m`: metadata
* * `q8`: `data_length` (`q` = 0, `8` = 7; 0 * 32 + 7 == 7)
* * `q8a04uq`: 0x01fafaf0
* * `s`: payment secret
* * `p5`: `data_length` (`p` = 1, `5` = 20; 1 * 32 + 20 == 52)
* * `zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs`: 0x1111111111111111111111111111111111111111111111111111111111111111
* * `9`: features
* * `q2`: `data_length` (`q` = 0, `2` = 10; 0 * 32 + 10 == 10)
* * `gqqqqqqsgq`: [b01000000000000000000000000000000000100000100000000] = 8 + 14 + 48
* * `7hf8he7ecf7n4ffphs6awl9t6676rrclv9ckg3d3ncn7fct63p6s365duk5wrk202cfy3aj5xnnp5gs3vrdvruverwwq7yzhkf5a3xqp`: signature
* * `d05wjc`: Bech32 checksum
*/
msatoshi = AMOUNT_MSAT(1000000000);
b11 = new_bolt11(tmpctx, &msatoshi);
b11->chain = chainparams_for_network("bitcoin");
b11->timestamp = 1496314658;
if (!hex_decode("0001020304050607080900010203040506070809000102030405060708090102",
strlen("0001020304050607080900010203040506070809000102030405060708090102"),
&b11->payment_hash, sizeof(b11->payment_hash)))
abort();
b11->receiver_id = node;
b11->description = "payment metadata inside";
b11->metadata = tal_hexdata(b11, "01fafaf0", strlen("01fafaf0"));
set_feature_bit(&b11->features, 8);
set_feature_bit(&b11->features, 14);
set_feature_bit(&b11->features, 48);
b11->payment_secret = tal(b11, struct secret);
memset(b11->payment_secret, 0x11, sizeof(*b11->payment_secret));
test_b11("lnbc10m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp9wpshjmt9de6zqmt9w3skgct5vysxjmnnd9jx2mq8q8a04uqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q2gqqqqqqsgq7hf8he7ecf7n4ffphs6awl9t6676rrclv9ckg3d3ncn7fct63p6s365duk5wrk202cfy3aj5xnnp5gs3vrdvruverwwq7yzhkf5a3xqpd05wjc", b11, NULL);

/* BOLT #11:
*
* > ### Bech32 checksum is invalid.
Expand Down
4 changes: 4 additions & 0 deletions devtools/bolt11-cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ int main(int argc, char *argv[])
printf("\n");
}

if (b11->metadata)
printf("metadata: %s\n",
tal_hex(ctx, b11->metadata));

list_for_each(&b11->extra_fields, extra, list) {
char *data = tal_arr(ctx, char, tal_count(extra->data)+1);

Expand Down
3 changes: 2 additions & 1 deletion doc/lightning-decode.7.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ If **type** is "bolt11 invoice", and **valid** is *true*:
- **description_hash** (hex, optional): the hash of the description, in place of *description* (always 64 characters)
- **payment_secret** (hex, optional): the secret to hand to the payee node (always 64 characters)
- **features** (hex, optional): the features bitmap for this invoice
- **payment_metadata** (hex, optional): the payment_metadata to put in the payment
- **fallbacks** (array of objects, optional): onchain addresses:
- **type** (string): the address type (if known) (one of "P2PKH", "P2SH", "P2WPKH", "P2WSH")
- **hex** (hex): Raw encoded address
Expand Down Expand Up @@ -180,4 +181,4 @@ RESOURCES

Main web site: <https://github.com/ElementsProject/lightning>

[comment]: # ( SHA256STAMP:d05b5fc1bf230b3bbd03e2023fb0c6bbefb700f7c3cfb43512da48dbce45f005)
[comment]: # ( SHA256STAMP:6ccf1b9195e64f897f65198a81c47bbd7e16387e4bdc74d624e2ec04a24e9873)
3 changes: 2 additions & 1 deletion doc/lightning-decodepay.7.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ On success, an object is returned, containing:
- **description_hash** (hex, optional): the hash of the description, in place of *description* (always 64 characters)
- **payment_secret** (hex, optional): the secret to hand to the payee node (always 64 characters)
- **features** (hex, optional): the features bitmap for this invoice
- **payment_metadata** (hex, optional): the payment_metadata to put in the payment
- **fallbacks** (array of objects, optional): onchain addresses:
- **type** (string): the address type (if known) (one of "P2PKH", "P2SH", "P2WPKH", "P2WSH")
- **hex** (hex): Raw encoded address
Expand Down Expand Up @@ -69,4 +70,4 @@ RESOURCES

Main web site: <https://github.com/ElementsProject/lightning>

[comment]: # ( SHA256STAMP:d92e1197708fff40f8ad71ccec3c0d8122d8088da1803c02bb042b09dbf2ee33)
[comment]: # ( SHA256STAMP:17cb6c66c75e907f3a2583d702aec2fc6e5a7b6026d05a3ed9957304799c9aef)
6 changes: 6 additions & 0 deletions doc/schemas/decode.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,8 @@
],
"additionalProperties": false,
"properties": {
"type": {},
"valid": {},
"currency": {
"type": "string",
"description": "the BIP173 name for the currency"
Expand Down Expand Up @@ -804,6 +806,10 @@
"type": "hex",
"description": "the features bitmap for this invoice"
},
"payment_metadata": {
"type": "hex",
"description": "the payment_metadata to put in the payment"
},
"fallbacks": {
"type": "array",
"description": "onchain addresses",
Expand Down
4 changes: 4 additions & 0 deletions doc/schemas/decodepay.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@
"type": "hex",
"description": "the features bitmap for this invoice"
},
"payment_metadata": {
"type": "hex",
"description": "the payment_metadata to put in the payment"
},
"fallbacks": {
"type": "array",
"description": "onchain addresses",
Expand Down
1 change: 1 addition & 0 deletions lightningd/lightningd.c
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,7 @@ static struct feature_set *default_features(const tal_t *ctx)
OPTIONAL_FEATURE(OPT_GOSSIP_QUERIES_EX),
OPTIONAL_FEATURE(OPT_STATIC_REMOTEKEY),
OPTIONAL_FEATURE(OPT_SHUTDOWN_ANYSEGWIT),
OPTIONAL_FEATURE(OPT_PAYMENT_METADATA),
#if EXPERIMENTAL_FEATURES
OPTIONAL_FEATURE(OPT_ANCHOR_OUTPUTS),
OPTIONAL_FEATURE(OPT_QUIESCE),
Expand Down
6 changes: 6 additions & 0 deletions tests/test_pay.py
Original file line number Diff line number Diff line change
Expand Up @@ -5236,3 +5236,9 @@ def test_pay_manual_exclude(node_factory, bitcoind):
# Exclude direct channel id
with pytest.raises(RpcError, match=r'is not reachable directly and all routehints were unusable.'):
l2.rpc.pay(inv, exclude=[scid23])


def test_pay_bolt11_metadata(node_factory, bitcoind):
l1 = node_factory.get_node()

l1.rpc.decode('lnbcrt10m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp9wpshjmt9de6zqmt9w3skgct5vysxjmnnd9jx2mq8q8a04uqcqpjsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q2gqqqqqqsgqrk6hdutpaetmm3afjn0vfczgeyv0cy739rr939kwd4h5j3khxcskhgf59eaqy8wyq82tsnaqc5y32ed4jg34jw7rmeva9u6kfhymawgptmy5f6')
4 changes: 2 additions & 2 deletions wire/extracted_onion_01_offers.patch
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
--- wire/extracted_onion_wire_csv 2020-03-25 10:24:12.861645774 +1030
+++ - 2020-03-26 13:47:13.498294435 +1030
@@ -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,tlv_payload,payment_metadata,16
tlvdata,tlv_payload,payment_metadata,payment_metadata,byte,...
+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,
Expand Down
4 changes: 3 additions & 1 deletion wire/extracted_onion_exp_enctlv.patch
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
+++ - 2020-03-20 15:11:55.763880895 +1030
--- 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 @@
@@ -8,8 +8,12 @@
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,payment_metadata,16
tlvdata,tlv_payload,payment_metadata,payment_metadata,byte,...
+tlvtype,tlv_payload,encrypted_recipient_data,10
+tlvdata,tlv_payload,encrypted_recipient_data,encrypted_data,byte,...
+tlvtype,tlv_payload,blinding_point,12
Expand Down
2 changes: 2 additions & 0 deletions wire/onion_wire.csv
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ 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,tlv_payload,payment_metadata,16
tlvdata,tlv_payload,payment_metadata,payment_metadata,byte,...
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,
Expand Down