diff --git a/common/route.c b/common/route.c index 63a881bf7907..a772aaa1d95c 100644 --- a/common/route.c +++ b/common/route.c @@ -107,7 +107,9 @@ static bool dijkstra_to_hops(struct route_hop **hops, /* Find other end of channel. */ next = gossmap_nth_node(gossmap, c, !(*hops)[num_hops].direction); gossmap_node_get_id(gossmap, next, &(*hops)[num_hops].node_id); - if (gossmap_node_get_feature(gossmap, next, OPT_VAR_ONION) != -1) + /* If we don't have a node_announcement, we *assume* modern */ + if (next->nann_off == 0 + || gossmap_node_get_feature(gossmap, next, OPT_VAR_ONION) != -1) (*hops)[num_hops].style = ROUTE_HOP_TLV; else (*hops)[num_hops].style = ROUTE_HOP_LEGACY; diff --git a/connectd/connectd.c b/connectd/connectd.c index f9ccdafabecd..80f97661f738 100644 --- a/connectd/connectd.c +++ b/connectd/connectd.c @@ -479,6 +479,7 @@ struct io_plan *peer_connected(struct io_conn *conn, unsup = features_unsupported(daemon->our_features, their_features, INIT_FEATURE); if (unsup != -1) { + status_peer_unusual(id, "Unsupported feature %u", unsup); msg = towire_warningfmt(NULL, NULL, "Unsupported feature %u", unsup); msg = cryptomsg_encrypt_msg(tmpctx, cs, take(msg)); @@ -486,6 +487,8 @@ struct io_plan *peer_connected(struct io_conn *conn, } if (!feature_check_depends(their_features, &depender, &missing)) { + status_peer_unusual(id, "Feature %zu requires feature %zu", + depender, missing); msg = towire_warningfmt(NULL, NULL, "Feature %zu requires feature %zu", depender, missing); diff --git a/contrib/pyln-testing/pyln/testing/utils.py b/contrib/pyln-testing/pyln/testing/utils.py index 20288e58d3ed..7f8cd0fb457c 100644 --- a/contrib/pyln-testing/pyln/testing/utils.py +++ b/contrib/pyln-testing/pyln/testing/utils.py @@ -996,7 +996,10 @@ def pay(self, dst, amt, label=None): assert len(self.rpc.listpeers(dst_id).get('peers')) == 1 # make an invoice - rhash = dst.rpc.invoice(amt, label, label)['payment_hash'] + inv = dst.rpc.invoice(amt, label, label) + # FIXME: pre 0.10.1 invoice calls didn't have payment_secret field + psecret = dst.rpc.decodepay(inv['bolt11'])['payment_secret'] + rhash = inv['payment_hash'] invoices = dst.rpc.listinvoices(label)['invoices'] assert len(invoices) == 1 and invoices[0]['status'] == 'unpaid' @@ -1008,7 +1011,7 @@ def pay(self, dst, amt, label=None): } # sendpay is async now - self.rpc.sendpay([routestep], rhash) + self.rpc.sendpay([routestep], rhash, payment_secret=psecret) # wait for sendpay to comply result = self.rpc.waitsendpay(rhash) assert(result.get('status') == 'complete') diff --git a/doc/lightning-invoice.7 b/doc/lightning-invoice.7 index 7037717367a0..891ff658bbb7 100644 --- a/doc/lightning-invoice.7 +++ b/doc/lightning-invoice.7 @@ -85,6 +85,8 @@ On success, an object is returned, containing: .IP \[bu] \fBpayment_hash\fR (hex): the hash of the \fIpayment_preimage\fR which will prove payment (always 64 characters) .IP \[bu] +\fBpayment_secret\fR (hex): the \fIpayment_secret\fR to place in the onion (always 64 characters) +.IP \[bu] \fBexpires_at\fR (u64): UNIX timestamp of when invoice expires .RE @@ -136,4 +138,4 @@ Rusty Russell \fI is mainly responsible\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:f27f015d8d612d280a8abca6930dbd42ef37fdac00bff1b114d1fd9138b15097 +\" SHA256STAMP:1617a52d2c9c5adbee4b410d15889a5f1c32e0a2fb2982dac82c5e74fb028ff3 diff --git a/doc/lightning-invoice.7.md b/doc/lightning-invoice.7.md index 6ffab01acc1a..3bd8502a10b5 100644 --- a/doc/lightning-invoice.7.md +++ b/doc/lightning-invoice.7.md @@ -75,6 +75,7 @@ RETURN VALUE On success, an object is returned, containing: - **bolt11** (string): the bolt11 string - **payment_hash** (hex): the hash of the *payment_preimage* which will prove payment (always 64 characters) +- **payment_secret** (hex): the *payment_secret* to place in the onion (always 64 characters) - **expires_at** (u64): UNIX timestamp of when invoice expires The following warnings may also be returned: @@ -111,4 +112,4 @@ RESOURCES Main web site: -[comment]: # ( SHA256STAMP:e63e87b91a14b8ae823ae67fc25315bc31b15a14378c05f4c972830bf3515af1) +[comment]: # ( SHA256STAMP:cba838098a7f95ab74906f387b2d67777b33ccc40349cb9a8ef1f7c9cc28c6ef) diff --git a/doc/schemas/invoice.schema.json b/doc/schemas/invoice.schema.json index b4f6284959cd..4e79765559ea 100644 --- a/doc/schemas/invoice.schema.json +++ b/doc/schemas/invoice.schema.json @@ -2,7 +2,7 @@ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, - "required": [ "payment_hash", "expires_at", "bolt11" ], + "required": [ "payment_hash", "expires_at", "bolt11", "payment_secret" ], "properties": { "bolt11": { "type": "string", @@ -14,6 +14,12 @@ "maxLength": 64, "minLength": 64 }, + "payment_secret": { + "type": "hex", + "description": "the *payment_secret* to place in the onion", + "maxLength": 64, + "minLength": 64 + }, "expires_at": { "type": "u64", "description": "UNIX timestamp of when invoice expires" diff --git a/lightningd/htlc_set.c b/lightningd/htlc_set.c index 6afe60e76a4c..618b46ba1677 100644 --- a/lightningd/htlc_set.c +++ b/lightningd/htlc_set.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -114,6 +115,17 @@ void htlc_set_add(struct lightningd *ld, return; } + /* If we insist on a payment secret, it must always have it */ + if (feature_is_set(details->features, COMPULSORY_FEATURE(OPT_PAYMENT_SECRET)) + && !payment_secret) { + log_debug(ld->log, "Missing payment_secret, but required for %s", + type_to_string(tmpctx, struct sha256, + &hin->payment_hash)); + local_fail_in_htlc(hin, + take(failmsg_incorrect_or_unknown(NULL, ld, hin))); + return; + } + /* BOLT #4: * - otherwise, if it supports `basic_mpp`: * - MUST add it to the HTLC set corresponding to that `payment_hash`. diff --git a/lightningd/invoice.c b/lightningd/invoice.c index 67d335154d56..b4434fe8973b 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -855,6 +855,7 @@ invoice_complete(struct invoice_info *info, struct invoice invoice; char *b11enc; const struct invoice_details *details; + struct secret payment_secret; struct wallet *wallet = info->cmd->ld->wallet; b11enc = bolt11_encode(info, info->b11, false, @@ -904,6 +905,8 @@ invoice_complete(struct invoice_info *info, json_add_sha256(response, "payment_hash", &details->rhash); json_add_u64(response, "expires_at", details->expiry_time); json_add_string(response, "bolt11", details->invstring); + invoice_secret(&details->r, &payment_secret); + json_add_secret(response, "payment_secret", &payment_secret); notify_invoice_creation(info->cmd->ld, info->b11->msat, info->payment_preimage, info->label); diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 96303dc7514a..d68598d8ced5 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -811,7 +811,7 @@ static struct feature_set *default_features(const tal_t *ctx) OPTIONAL_FEATURE(OPT_UPFRONT_SHUTDOWN_SCRIPT), OPTIONAL_FEATURE(OPT_GOSSIP_QUERIES), OPTIONAL_FEATURE(OPT_VAR_ONION), - OPTIONAL_FEATURE(OPT_PAYMENT_SECRET), + COMPULSORY_FEATURE(OPT_PAYMENT_SECRET), OPTIONAL_FEATURE(OPT_BASIC_MPP), OPTIONAL_FEATURE(OPT_GOSSIP_QUERIES_EX), OPTIONAL_FEATURE(OPT_STATIC_REMOTEKEY), diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 1fd3a3dfa60d..e5ccd4c75fe6 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -334,6 +334,11 @@ void json_add_node_id(struct json_stream *response UNNEEDED, void json_add_preimage(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, const struct preimage *preimage UNNEEDED) { fprintf(stderr, "json_add_preimage called!\n"); abort(); } +/* Generated stub for json_add_secret */ +void json_add_secret(struct json_stream *response UNNEEDED, + const char *fieldname UNNEEDED, + const struct secret *secret UNNEEDED) +{ fprintf(stderr, "json_add_secret called!\n"); abort(); } /* Generated stub for json_add_sha256 */ void json_add_sha256(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, const struct sha256 *hash UNNEEDED) diff --git a/plugins/keysend.c b/plugins/keysend.c index aacd86c6c2ec..4b7a127c3da1 100644 --- a/plugins/keysend.c +++ b/plugins/keysend.c @@ -1,5 +1,7 @@ #include #include +#include +#include #include #include #include @@ -259,17 +261,55 @@ struct keysend_in { struct tlv_field *preimage_field; }; +static int tlvfield_cmp(const struct tlv_field *a, + const struct tlv_field *b, void *unused) +{ + if (a->numtype > b->numtype) + return 1; + else if (a->numtype < b->numtype) + return -1; + return 0; +} + static struct command_result * -htlc_accepted_invoice_created(struct command *cmd, const char *buf UNUSED, - const jsmntok_t *result UNUSED, +htlc_accepted_invoice_created(struct command *cmd, const char *buf, + const jsmntok_t *result, struct keysend_in *ki) { + struct tlv_field field; int preimage_field_idx = ki->preimage_field - ki->payload->fields; /* Remove the preimage field so `lightningd` knows how to handle * this. */ tal_arr_remove(&ki->payload->fields, preimage_field_idx); + /* Now we can fill in the payment secret, from invoice. */ + ki->payload->payment_data = tal(ki->payload, + struct tlv_tlv_payload_payment_data); + json_to_secret(buf, json_get_member(buf, result, "payment_secret"), + &ki->payload->payment_data->payment_secret); + + /* We checked that amt_to_forward was non-NULL before */ + ki->payload->payment_data->total_msat = *ki->payload->amt_to_forward; + + /* In order to put payment_data into ->fields, I'd normally re-serialize, + * but we can have completely unknown fields. So insert manually. */ + /* BOLT #4: + * 1. type: 8 (`payment_data`) + * 2. data: + * * [`32*byte`:`payment_secret`] + * * [`tu64`:`total_msat`] + */ + field.numtype = 8; + field.value = tal_arr(ki->payload, u8, 0); + towire_secret(&field.value, &ki->payload->payment_data->payment_secret); + towire_tu64(&field.value, ki->payload->payment_data->total_msat); + field.length = tal_bytelen(field.value); + tal_arr_expand(&ki->payload->fields, field); + + asort(ki->payload->fields, tal_count(ki->payload->fields), + tlvfield_cmp, NULL); + /* Finally we can resolve the payment with the preimage. */ plugin_log(cmd->plugin, LOG_INFORM, "Resolving incoming HTLC with preimage for payment_hash %s " @@ -279,6 +319,20 @@ htlc_accepted_invoice_created(struct command *cmd, const char *buf UNUSED, } +static struct command_result * +htlc_accepted_invoice_failed(struct command *cmd, const char *buf, + const jsmntok_t *error, + struct keysend_in *ki) +{ + plugin_log(cmd->plugin, LOG_BROKEN, + "Could not create invoice for keysend: %.*s", + json_tok_full_len(error), + json_tok_full(buf, error)); + /* Continue, but don't change it: it will fail. */ + return htlc_accepted_continue(cmd, NULL); + +} + static struct command_result *htlc_accepted_call(struct command *cmd, const char *buf, const jsmntok_t *params) @@ -349,6 +403,12 @@ static struct command_result *htlc_accepted_call(struct command *cmd, #endif } + /* If malformed (amt is compulsory), let lightningd handle it. */ + if (!payload->amt_to_forward) { + plugin_log(cmd->plugin, LOG_UNUSUAL, + "Sender omitted amount. Ignoring this HTLC."); + return htlc_accepted_continue(cmd, NULL); + } /* If the preimage is not 32 bytes long then we can't accept the * payment. */ @@ -391,7 +451,7 @@ static struct command_result *htlc_accepted_call(struct command *cmd, * will be nice and reject the payment. */ req = jsonrpc_request_start(cmd->plugin, cmd, "invoice", &htlc_accepted_invoice_created, - &htlc_accepted_invoice_created, + &htlc_accepted_invoice_failed, ki); plugin_log(cmd->plugin, LOG_INFORM, "Inserting a new invoice for keysend with payment_hash %s", type_to_string(tmpctx, struct sha256, &payment_hash)); diff --git a/tests/benchmark.py b/tests/benchmark.py index edc4475c265a..030cb85781de 100644 --- a/tests/benchmark.py +++ b/tests/benchmark.py @@ -30,19 +30,20 @@ def test_single_hop(node_factory, executor): fs = [] invoices = [] for i in tqdm(range(num_payments)): - invoices.append(l2.rpc.invoice(1000, 'invoice-%d' % (i), 'desc')['payment_hash']) + inv = l2.rpc.invoice(1000, 'invoice-%d' % (i), 'desc') + invoices.append((inv['payment_hash'], inv['payment_secret'])) route = l1.rpc.getroute(l2.rpc.getinfo()['id'], 1000, 1)['route'] print("Sending payments") start_time = time() - def do_pay(i): - p = l1.rpc.sendpay(route, i) + def do_pay(i, s): + p = l1.rpc.sendpay(route, i, payment_secret=s) r = l1.rpc.waitsendpay(p['payment_hash']) return r - for i in invoices: - fs.append(executor.submit(do_pay, i)) + for i, s in invoices: + fs.append(executor.submit(do_pay, i, s)) for f in tqdm(futures.as_completed(fs), total=len(fs)): f.result() diff --git a/tests/test_closing.py b/tests/test_closing.py index 0bb6c170bc41..4a2f4ba61f5a 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -777,7 +777,7 @@ def test_penalty_htlc_tx_fulfill(node_factory, bitcoind, chainparams): amt = 10**8 // 2 sticky_inv = l1.rpc.invoice(amt, '2', 'sticky') route = l4.rpc.getroute(l1.info['id'], amt, 1)['route'] - l4.rpc.sendpay(route, sticky_inv['payment_hash']) + l4.rpc.sendpay(route, sticky_inv['payment_hash'], payment_secret=sticky_inv['payment_secret']) l1.daemon.wait_for_log('dev_disconnect: -WIRE_UPDATE_FULFILL_HTLC') wait_for(lambda: len(l2.rpc.listpeers(l3.info['id'])['peers'][0]['channels'][0]['htlcs']) == 1) @@ -927,12 +927,12 @@ def test_penalty_htlc_tx_timeout(node_factory, bitcoind, chainparams): amt = 10**8 // 2 sticky_inv_1 = l5.rpc.invoice(amt, '2', 'sticky') route = l1.rpc.getroute(l5.info['id'], amt, 1)['route'] - l1.rpc.sendpay(route, sticky_inv_1['payment_hash']) + l1.rpc.sendpay(route, sticky_inv_1['payment_hash'], payment_secret=sticky_inv_1['payment_secret']) l5.daemon.wait_for_log('dev_disconnect: -WIRE_UPDATE_FULFILL_HTLC') sticky_inv_2 = l1.rpc.invoice(amt, '2', 'sticky') route = l4.rpc.getroute(l1.info['id'], amt, 1)['route'] - l4.rpc.sendpay(route, sticky_inv_2['payment_hash']) + l4.rpc.sendpay(route, sticky_inv_2['payment_hash'], payment_secret=sticky_inv_2['payment_secret']) l1.daemon.wait_for_log('dev_disconnect: -WIRE_UPDATE_FULFILL_HTLC') wait_for(lambda: len(l2.rpc.listpeers(l3.info['id'])['peers'][0]['channels'][0]['htlcs']) == 2) @@ -1350,14 +1350,15 @@ def test_onchaind_replay(node_factory, bitcoind): 'feerates': (7500, 7500, 7500, 7500)}, {'watchtime-blocks': 201, 'cltv-delta': 101}]) - rhash = l2.rpc.invoice(10**8, 'onchaind_replay', 'desc')['payment_hash'] + inv = l2.rpc.invoice(10**8, 'onchaind_replay', 'desc') + rhash = inv['payment_hash'] routestep = { 'msatoshi': 10**8 - 1, 'id': l2.info['id'], 'delay': 101, 'channel': '1x1x1' } - l1.rpc.sendpay([routestep], rhash) + l1.rpc.sendpay([routestep], rhash, payment_secret=inv['payment_secret']) l1.daemon.wait_for_log('sendrawtx exit 0') bitcoind.generate_block(1, wait_for_mempool=1) @@ -1409,7 +1410,8 @@ def test_onchain_dust_out(node_factory, bitcoind, executor): channel_id = first_channel_id(l1, l2) # Must be dust! - rhash = l2.rpc.invoice(1, 'onchain_dust_out', 'desc')['payment_hash'] + inv = l2.rpc.invoice(1, 'onchain_dust_out', 'desc') + rhash = inv['payment_hash'] routestep = { 'msatoshi': 1, 'id': l2.info['id'], @@ -1417,7 +1419,7 @@ def test_onchain_dust_out(node_factory, bitcoind, executor): 'channel': '1x1x1' } - l1.rpc.sendpay([routestep], rhash) + l1.rpc.sendpay([routestep], rhash, payment_secret=inv['payment_secret']) payfuture = executor.submit(l1.rpc.waitsendpay, rhash) # l1 will drop to chain. @@ -1437,7 +1439,7 @@ def test_onchain_dust_out(node_factory, bitcoind, executor): # Retry payment, this should fail (and, as a side-effect, tickle a # bug). with pytest.raises(RpcError, match=r'WIRE_UNKNOWN_NEXT_PEER'): - l1.rpc.sendpay([routestep], rhash) + l1.rpc.sendpay([routestep], rhash, payment_secret=inv['payment_secret']) # 6 later, l1 should collect its to-self payment. bitcoind.generate_block(6) @@ -1479,7 +1481,8 @@ def test_onchain_timeout(node_factory, bitcoind, executor): channel_id = first_channel_id(l1, l2) - rhash = l2.rpc.invoice(10**8, 'onchain_timeout', 'desc')['payment_hash'] + inv = l2.rpc.invoice(10**8, 'onchain_timeout', 'desc') + rhash = inv['payment_hash'] # We underpay, so it fails. routestep = { 'msatoshi': 10**8 - 1, @@ -1488,7 +1491,7 @@ def test_onchain_timeout(node_factory, bitcoind, executor): 'channel': '1x1x1' } - l1.rpc.sendpay([routestep], rhash) + l1.rpc.sendpay([routestep], rhash, payment_secret=inv['payment_secret']) with pytest.raises(RpcError): l1.rpc.waitsendpay(rhash) @@ -1497,7 +1500,7 @@ def test_onchain_timeout(node_factory, bitcoind, executor): sync_blockheight(bitcoind, [l1]) # Second one will cause drop to chain. - l1.rpc.sendpay([routestep], rhash) + l1.rpc.sendpay([routestep], rhash, payment_secret=inv['payment_secret']) payfuture = executor.submit(l1.rpc.waitsendpay, rhash) # l1 will drop to chain. @@ -1575,7 +1578,8 @@ def test_onchain_middleman(node_factory, bitcoind): l2.pay(l1, 2 * 10**8) # Must be bigger than dust! - rhash = l3.rpc.invoice(10**8, 'middleman', 'desc')['payment_hash'] + inv = l3.rpc.invoice(10**8, 'middleman', 'desc') + rhash = inv['payment_hash'] route = l1.rpc.getroute(l3.info['id'], 10**8, 1)["route"] assert len(route) == 2 @@ -1584,7 +1588,7 @@ def test_onchain_middleman(node_factory, bitcoind): def try_pay(): try: - l1.rpc.sendpay(route, rhash) + l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(rhash) q.put(None) except Exception as err: @@ -1665,7 +1669,8 @@ def test_onchain_middleman_their_unilateral_in(node_factory, bitcoind): l2.pay(l1, 2 * 10**8) # Must be bigger than dust! - rhash = l3.rpc.invoice(10**8, 'middleman', 'desc')['payment_hash'] + inv = l3.rpc.invoice(10**8, 'middleman', 'desc') + rhash = inv['payment_hash'] route = l1.rpc.getroute(l3.info['id'], 10**8, 1)["route"] assert len(route) == 2 @@ -1674,7 +1679,7 @@ def test_onchain_middleman_their_unilateral_in(node_factory, bitcoind): def try_pay(): try: - l1.rpc.sendpay(route, rhash) + l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(rhash) q.put(None) except Exception as err: @@ -1743,9 +1748,9 @@ def test_onchain_their_unilateral_out(node_factory, bitcoind): def try_pay(): try: - # rhash is fake + # rhash is fake (so is payment_secret) rhash = 'B1' * 32 - l1.rpc.sendpay(route, rhash) + l1.rpc.sendpay(route, rhash, payment_secret=rhash) q.put(None) except Exception as err: q.put(err) @@ -1834,7 +1839,8 @@ def test_onchain_feechange(node_factory, bitcoind, executor): } ]) - rhash = l2.rpc.invoice(10**8, 'onchain_timeout', 'desc')['payment_hash'] + inv = l2.rpc.invoice(10**8, 'onchain_timeout', 'desc') + rhash = inv['payment_hash'] # We underpay, so it fails. routestep = { 'msatoshi': 10**8 - 1, @@ -1843,7 +1849,7 @@ def test_onchain_feechange(node_factory, bitcoind, executor): 'channel': '1x1x1' } - executor.submit(l1.rpc.sendpay, [routestep], rhash) + executor.submit(l1.rpc.sendpay, [routestep], rhash, payment_secret=inv['payment_secret']) # l2 will drop to chain. l2.daemon.wait_for_log('permfail') @@ -1917,7 +1923,8 @@ def test_onchain_all_dust(node_factory, bitcoind, executor): l1.fundchannel(l2, 10**6) channel_id = first_channel_id(l1, l2) - rhash = l2.rpc.invoice(10**8, 'onchain_timeout', 'desc')['payment_hash'] + inv = l2.rpc.invoice(10**8, 'onchain_timeout', 'desc') + rhash = inv['payment_hash'] # We underpay, so it fails. routestep = { 'msatoshi': 10**7 - 1, @@ -1926,7 +1933,7 @@ def test_onchain_all_dust(node_factory, bitcoind, executor): 'channel': '1x1x1' } - executor.submit(l1.rpc.sendpay, [routestep], rhash) + executor.submit(l1.rpc.sendpay, [routestep], rhash, payment_secret=inv['payment_secret']) # l2 will drop to chain. l2.daemon.wait_for_log('permfail') @@ -2088,19 +2095,20 @@ def setup_multihtlc_test(node_factory, bitcoind): nodes[-1].rpc.dev_ignore_htlcs(id=nodes[-2].info['id'], ignore=True) preimage = "0" * 64 - h = nodes[0].rpc.invoice(msatoshi=10**8, label='x', description='desc', - preimage=preimage)['payment_hash'] + inv = nodes[0].rpc.invoice(msatoshi=10**8, label='x', description='desc', + preimage=preimage) + h = inv['payment_hash'] nodes[-1].rpc.invoice(msatoshi=10**8, label='x', description='desc', preimage=preimage)['payment_hash'] # First, the failed attempts (paying wrong node). CLTV1 r = nodes[0].rpc.getroute(nodes[-2].info['id'], 10**8, 1)["route"] - nodes[0].rpc.sendpay(r, h) + nodes[0].rpc.sendpay(r, h, payment_secret=inv['payment_secret']) with pytest.raises(RpcError, match=r'INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS'): nodes[0].rpc.waitsendpay(h) r = nodes[-1].rpc.getroute(nodes[1].info['id'], 10**8, 1)["route"] - nodes[-1].rpc.sendpay(r, h) + nodes[-1].rpc.sendpay(r, h, payment_secret=inv['payment_secret']) with pytest.raises(RpcError, match=r'INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS'): nodes[-1].rpc.waitsendpay(h) @@ -2110,25 +2118,25 @@ def setup_multihtlc_test(node_factory, bitcoind): # Now, the live attempts with CLTV2 (blackholed by end nodes) r = nodes[0].rpc.getroute(nodes[-1].info['id'], 10**8, 1)["route"] - nodes[0].rpc.sendpay(r, h) + nodes[0].rpc.sendpay(r, h, payment_secret=inv['payment_secret']) r = nodes[-1].rpc.getroute(nodes[0].info['id'], 10**8, 1)["route"] - nodes[-1].rpc.sendpay(r, h) + nodes[-1].rpc.sendpay(r, h, payment_secret=inv['payment_secret']) # We send second HTLC from different node, since they refuse to send # multiple with same hash. r = nodes[1].rpc.getroute(nodes[-1].info['id'], 10**8, 1)["route"] - nodes[1].rpc.sendpay(r, h) + nodes[1].rpc.sendpay(r, h, payment_secret=inv['payment_secret']) r = nodes[-2].rpc.getroute(nodes[0].info['id'], 10**8, 1)["route"] - nodes[-2].rpc.sendpay(r, h) + nodes[-2].rpc.sendpay(r, h, payment_secret=inv['payment_secret']) # Now increment CLTV -> CLTV3. bitcoind.generate_block(1) sync_blockheight(bitcoind, nodes) r = nodes[2].rpc.getroute(nodes[-1].info['id'], 10**8, 1)["route"] - nodes[2].rpc.sendpay(r, h) + nodes[2].rpc.sendpay(r, h, payment_secret=inv['payment_secret']) r = nodes[-3].rpc.getroute(nodes[0].info['id'], 10**8, 1)["route"] - nodes[-3].rpc.sendpay(r, h) + nodes[-3].rpc.sendpay(r, h, payment_secret=inv['payment_secret']) # Make sure HTLCs have reached the end. nodes[0].daemon.wait_for_logs(['peer_in WIRE_UPDATE_ADD_HTLC'] * 3) diff --git a/tests/test_connection.py b/tests/test_connection.py index 9db21ac9f11a..ad008fd278d5 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -668,21 +668,22 @@ def test_reconnect_sender_add1(node_factory): l1.fundchannel(l2, 10**6) amt = 200000000 - rhash = l2.rpc.invoice(amt, 'test_reconnect_sender_add1', 'desc')['payment_hash'] + inv = l2.rpc.invoice(amt, 'test_reconnect_sender_add1', 'desc') + rhash = inv['payment_hash'] assert only_one(l2.rpc.listinvoices('test_reconnect_sender_add1')['invoices'])['status'] == 'unpaid' route = [{'msatoshi': amt, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'}] for i in range(0, len(disconnects)): with pytest.raises(RpcError): - l1.rpc.sendpay(route, rhash) + l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(rhash) # Wait for reconnection. l1.daemon.wait_for_log('Already have funding locked in') # This will send commit, so will reconnect as required. - l1.rpc.sendpay(route, rhash) + l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) @pytest.mark.developer @@ -708,13 +709,14 @@ def test_reconnect_sender_add(node_factory): l1.fundchannel(l2, 10**6) amt = 200000000 - rhash = l2.rpc.invoice(amt, 'testpayment', 'desc')['payment_hash'] + inv = l2.rpc.invoice(amt, 'testpayment', 'desc') + rhash = inv['payment_hash'] assert only_one(l2.rpc.listinvoices('testpayment')['invoices'])['status'] == 'unpaid' route = [{'msatoshi': amt, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'}] # This will send commit, so will reconnect as required. - l1.rpc.sendpay(route, rhash) + l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) # Should have printed this for every reconnect. for i in range(0, len(disconnects)): l1.daemon.wait_for_log('Already have funding locked in') @@ -743,11 +745,12 @@ def test_reconnect_receiver_add(node_factory): l1.fundchannel(l2, 10**6) amt = 200000000 - rhash = l2.rpc.invoice(amt, 'testpayment2', 'desc')['payment_hash'] + inv = l2.rpc.invoice(amt, 'testpayment2', 'desc') + rhash = inv['payment_hash'] assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['status'] == 'unpaid' route = [{'msatoshi': amt, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'}] - l1.rpc.sendpay(route, rhash) + l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) for i in range(len(disconnects)): l1.daemon.wait_for_log('Already have funding locked in') assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['status'] == 'paid' @@ -775,11 +778,12 @@ def test_reconnect_receiver_fulfill(node_factory): l1.fundchannel(l2, 10**6) amt = 200000000 - rhash = l2.rpc.invoice(amt, 'testpayment2', 'desc')['payment_hash'] + inv = l2.rpc.invoice(amt, 'testpayment2', 'desc') + rhash = inv['payment_hash'] assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['status'] == 'unpaid' route = [{'msatoshi': amt, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'}] - l1.rpc.sendpay(route, rhash) + l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) for i in range(len(disconnects)): l1.daemon.wait_for_log('Already have funding locked in') assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['status'] == 'paid' @@ -2980,7 +2984,7 @@ def test_restart_many_payments(node_factory, bitcoind): wait_for(lambda: [c['public'] for c in n.rpc.listchannels()['channels']] == [True] * len(nodes) * 2) # Manually create routes, get invoices - Payment = namedtuple('Payment', ['innode', 'route', 'payment_hash']) + Payment = namedtuple('Payment', ['innode', 'route', 'payment_hash', 'payment_secret']) to_pay = [] for i in range(len(innodes)): @@ -2993,8 +2997,9 @@ def test_restart_many_payments(node_factory, bitcoind): 'id': outnodes[i].info['id'], 'delay': 5, 'channel': outchans[i]}] - payment_hash = outnodes[i].rpc.invoice(100000000, "invoice", "invoice")['payment_hash'] - to_pay.append(Payment(innodes[i], route, payment_hash)) + inv = outnodes[i].rpc.invoice(100000000, "invoice", "invoice") + payment_hash = inv['payment_hash'] + to_pay.append(Payment(innodes[i], route, payment_hash, inv['payment_secret'])) # This one should be routed through to the outnode. route = [{'msatoshi': 100001001, @@ -3005,12 +3010,13 @@ def test_restart_many_payments(node_factory, bitcoind): 'id': outnodes[i].info['id'], 'delay': 5, 'channel': outchans[i]}] - payment_hash = outnodes[i].rpc.invoice(100000000, "invoice2", "invoice2")['payment_hash'] - to_pay.append(Payment(innodes[i], route, payment_hash)) + inv = outnodes[i].rpc.invoice(100000000, "invoice2", "invoice2") + payment_hash = inv['payment_hash'] + to_pay.append(Payment(innodes[i], route, payment_hash, inv['payment_secret'])) # sendpay is async. for p in to_pay: - p.innode.rpc.sendpay(p.route, p.payment_hash) + p.innode.rpc.sendpay(p.route, p.payment_hash, p.payment_secret) # Now restart l1 while traffic is flowing... l1.restart() @@ -3247,14 +3253,15 @@ def test_pay_disconnect_stress(node_factory, executor): routel2l1 = [{'msatoshi': '10000msat', 'id': l1.info['id'], 'delay': 5, 'channel': scid12}] # Get invoice from l1 to pay. - payhash1 = l1.rpc.invoice(10000, "invoice", "invoice")['payment_hash'] + inv = l1.rpc.invoice(10000, "invoice", "invoice") + payhash1 = inv['payment_hash'] # Start balancing payment. fut = executor.submit(l1.pay, l2, 10**9 // 2) # As soon as reverse payment is accepted, reconnect. while True: - l2.rpc.sendpay(routel2l1, payhash1) + l2.rpc.sendpay(routel2l1, payhash1, payment_secret=inv['payment_secret']) try: # This will usually fail with Capacity exceeded l2.rpc.waitsendpay(payhash1, timeout=TIMEOUT) @@ -3369,7 +3376,9 @@ def test_nonstatic_channel(node_factory, bitcoind): """Smoke test for a channel without option_static_remotekey""" l1, l2 = node_factory.line_graph(2, opts=[{}, - {'dev-force-features': '////'}]) + # needs at least 15 to connect + # (and 9 is a dependent) + {'dev-force-features': '9,15////'}]) chan = only_one(only_one(l1.rpc.listpeers()['peers'])['channels']) assert 'option_static_remotekey' not in chan['features'] assert 'option_anchor_outputs' not in chan['features'] @@ -3406,7 +3415,7 @@ def test_htlc_retransmit_order(node_factory, executor): '=WIRE_UPDATE_ADD_HTLC*' + str(NUM_HTLCS - 1), '-WIRE_COMMITMENT_SIGNED']}, {'may_reconnect': True}]) - payment_hashes = [l2.rpc.invoice(1000, str(x), str(x))['payment_hash'] for x in range(NUM_HTLCS)] + invoices = [l2.rpc.invoice(1000, str(x), str(x)) for x in range(NUM_HTLCS)] routestep = { 'msatoshi': 1000, @@ -3414,8 +3423,8 @@ def test_htlc_retransmit_order(node_factory, executor): 'delay': 5, 'channel': '1x1x1' # note: can be bogus for 1-hop direct payments } - for p in payment_hashes: - executor.submit(l1.rpc.sendpay, [routestep], p) + for inv in invoices: + executor.submit(l1.rpc.sendpay, [routestep], inv['payment_hash'], payment_secret=inv['payment_secret']) l1.daemon.wait_for_logs(['dev_disconnect'] * 2) l1.rpc.call('dev-reenable-commit', [l2.info['id']]) @@ -3424,8 +3433,8 @@ def test_htlc_retransmit_order(node_factory, executor): # Now reconnect. l1.rpc.connect(l2.info['id'], 'localhost', port=l2.port) - for p in payment_hashes: - result = l1.rpc.waitsendpay(p) + for inv in invoices: + result = l1.rpc.waitsendpay(inv['payment_hash']) assert(result['status'] == 'complete') # If order was wrong, we'll get a LOG_BROKEN and fixtures will complain. @@ -3631,7 +3640,7 @@ def test_upgrade_statickey_fail(node_factory, executor, bitcoind): 'disconnect': l2_disconnects}]) # This HTLC will fail - l1.rpc.sendpay([{'msatoshi': 1000, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'}], '00' * 32) + l1.rpc.sendpay([{'msatoshi': 1000, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'}], '00' * 32, payment_secret='00' * 32) # Each one should cause one disconnection, no upgrade. for d in l1_disconnects + l2_disconnects[:-1]: @@ -3681,7 +3690,7 @@ def test_htlc_failed_noclose(node_factory): """Test a bug where the htlc timeout would kick in even if the HTLC failed""" l1, l2 = node_factory.line_graph(2) - payment_hash = l2.rpc.invoice(1000, "test", "test")['payment_hash'] + inv = l2.rpc.invoice(1000, "test", "test") routestep = { 'msatoshi': FUNDAMOUNT * 1000, 'id': l2.info['id'], @@ -3690,14 +3699,14 @@ def test_htlc_failed_noclose(node_factory): } # This fails at channeld - l1.rpc.sendpay([routestep], payment_hash) + l1.rpc.sendpay([routestep], inv['payment_hash'], payment_secret=inv['payment_secret']) with pytest.raises(RpcError, match="Capacity exceeded"): - l1.rpc.waitsendpay(payment_hash) + l1.rpc.waitsendpay(inv['payment_hash']) # Send a second one, too: make sure we don't crash. - l1.rpc.sendpay([routestep], payment_hash) + l1.rpc.sendpay([routestep], inv['payment_hash'], payment_secret=inv['payment_secret']) with pytest.raises(RpcError, match="Capacity exceeded"): - l1.rpc.waitsendpay(payment_hash) + l1.rpc.waitsendpay(inv['payment_hash']) time.sleep(35) assert l1.rpc.getpeer(l2.info['id'])['connected'] diff --git a/tests/test_misc.py b/tests/test_misc.py index 4e76c847f062..486d2b37b441 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1700,7 +1700,7 @@ def test_bad_onion(node_factory, bitcoind): l1, l2, l3, l4 = node_factory.line_graph(4, wait_for_announce=True, opts={'log-level': 'io'}) - h = l4.rpc.invoice(123000, 'test_bad_onion', 'description')['payment_hash'] + inv = l4.rpc.invoice(123000, 'test_bad_onion', 'description') route = l1.rpc.getroute(l4.info['id'], 123000, 1)['route'] assert len(route) == 3 @@ -1709,9 +1709,9 @@ def test_bad_onion(node_factory, bitcoind): # Replace id with a different pubkey, so onion encoded badly at third hop. route[2]['id'] = mangled_nodeid - l1.rpc.sendpay(route, h) + l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret']) with pytest.raises(RpcError) as err: - l1.rpc.waitsendpay(h) + l1.rpc.waitsendpay(inv['payment_hash']) # FIXME: #define PAY_TRY_OTHER_ROUTE 204 PAY_TRY_OTHER_ROUTE = 204 @@ -1733,9 +1733,9 @@ def test_bad_onion(node_factory, bitcoind): # Replace id with a different pubkey, so onion encoded badly at second hop. route[1]['id'] = mangled_nodeid - l1.rpc.sendpay(route, h) + l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret']) with pytest.raises(RpcError) as err: - l1.rpc.waitsendpay(h) + l1.rpc.waitsendpay(inv['payment_hash']) # FIXME: #define PAY_TRY_OTHER_ROUTE 204 PAY_TRY_OTHER_ROUTE = 204 @@ -1750,13 +1750,13 @@ def test_bad_onion_immediate_peer(node_factory, bitcoind): """Test that we handle the malformed msg when we're the origin""" l1, l2 = node_factory.line_graph(2, opts={'dev-fail-process-onionpacket': None}) - h = l2.rpc.invoice(123000, 'test_bad_onion_immediate_peer', 'description')['payment_hash'] + inv = l2.rpc.invoice(123000, 'test_bad_onion_immediate_peer', 'description') route = l1.rpc.getroute(l2.info['id'], 123000, 1)['route'] assert len(route) == 1 - l1.rpc.sendpay(route, h) + l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret']) with pytest.raises(RpcError) as err: - l1.rpc.waitsendpay(h) + l1.rpc.waitsendpay(inv['payment_hash']) # FIXME: #define PAY_UNPARSEABLE_ONION 202 PAY_UNPARSEABLE_ONION = 202 @@ -1893,7 +1893,7 @@ def test_list_features_only(node_factory): 'option_var_onion_optin/odd', 'option_gossip_queries_ex/odd', 'option_static_remotekey/odd', - 'option_payment_secret/odd', + 'option_payment_secret/even', 'option_basic_mpp/odd', ] if EXPERIMENTAL_FEATURES: @@ -2478,14 +2478,14 @@ def test_listforwards(node_factory, bitcoind): l1.rpc.pay(i41['bolt11']) # failed payment - failed_payment_hash = l3.rpc.invoice(4000, 'failed', 'desc')['payment_hash'] + failed_inv = l3.rpc.invoice(4000, 'failed', 'desc') failed_route = l1.rpc.getroute(l3.info['id'], 4000, 1)['route'] l2.rpc.close(c23, 1) with pytest.raises(RpcError): - l1.rpc.sendpay(failed_route, failed_payment_hash) - l1.rpc.waitsendpay(failed_payment_hash) + l1.rpc.sendpay(failed_route, failed_inv['payment_hash'], payment_secret=failed_inv['payment_secret']) + l1.rpc.waitsendpay(failed_inv['payment_hash']) all_forwards = l2.rpc.listforwards()['forwards'] print(json.dumps(all_forwards, indent=True)) @@ -2493,7 +2493,7 @@ def test_listforwards(node_factory, bitcoind): assert len(all_forwards) == 3 assert i31['payment_hash'] in map(lambda x: x['payment_hash'], all_forwards) assert i41['payment_hash'] in map(lambda x: x['payment_hash'], all_forwards) - assert failed_payment_hash in map(lambda x: x['payment_hash'], all_forwards) + assert failed_inv['payment_hash'] in map(lambda x: x['payment_hash'], all_forwards) # status=settled settled_forwards = l2.rpc.listforwards(status='settled')['forwards'] diff --git a/tests/test_pay.py b/tests/test_pay.py index 997a588de1ed..6f73e788cdec 100644 --- a/tests/test_pay.py +++ b/tests/test_pay.py @@ -2,6 +2,7 @@ from fixtures import * # noqa: F401,F403 from fixtures import TEST_NETWORK from flaky import flaky # noqa: F401 +from io import BytesIO from pyln.client import RpcError, Millisatoshi from pyln.proto.onion import TlvPayload from pyln.testing.utils import EXPERIMENTAL_DUAL_FUND, FUNDAMOUNT @@ -228,7 +229,7 @@ def test_pay0(node_factory): } # Amount must be nonzero! - l1.rpc.sendpay([routestep], rhash) + l1.rpc.sendpay([routestep], rhash, payment_secret=inv['payment_secret']) with pytest.raises(RpcError, match=r'WIRE_AMOUNT_BELOW_MINIMUM'): l1.rpc.waitsendpay(rhash) @@ -256,7 +257,7 @@ def test_pay_disconnect(node_factory, bitcoind): # Can't pay while its offline. with pytest.raises(RpcError, match=r'failed: WIRE_TEMPORARY_CHANNEL_FAILURE \(First peer not ready\)'): - l1.rpc.sendpay(route, rhash) + l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) l2.start() l1.daemon.wait_for_log('peer_out WIRE_CHANNEL_REESTABLISH') @@ -275,7 +276,7 @@ def test_pay_disconnect(node_factory, bitcoind): # Should fail due to permenant channel fail with pytest.raises(RpcError, match=r'WIRE_UNKNOWN_NEXT_PEER'): - l1.rpc.sendpay(route, rhash) + l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) assert not l1.daemon.is_in_log('Payment is still in progress') @@ -512,7 +513,8 @@ def test_sendpay(node_factory): l1, l2 = node_factory.line_graph(2, fundamount=10**6) amt = 200000000 - rhash = l2.rpc.invoice(amt, 'testpayment2', 'desc')['payment_hash'] + inv = l2.rpc.invoice(amt, 'testpayment2', 'desc') + rhash = inv['payment_hash'] def invoice_unpaid(dst, label): invoices = dst.rpc.listinvoices(label)['invoices'] @@ -533,7 +535,7 @@ def only_one(arr): with pytest.raises(RpcError): rs = copy.deepcopy(routestep) rs['msatoshi'] = rs['msatoshi'] - 1 - l1.rpc.sendpay([rs], rhash) + l1.rpc.sendpay([rs], rhash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(rhash) assert invoice_unpaid(l2, 'testpayment2') @@ -541,7 +543,7 @@ def only_one(arr): with pytest.raises(RpcError): rs = copy.deepcopy(routestep) rs['msatoshi'] = rs['msatoshi'] * 2 + 1 - l1.rpc.sendpay([rs], rhash) + l1.rpc.sendpay([rs], rhash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(rhash) assert invoice_unpaid(l2, 'testpayment2') @@ -549,7 +551,7 @@ def only_one(arr): with pytest.raises(RpcError): rs = copy.deepcopy(routestep) rs['delay'] = rs['delay'] - 2 - l1.rpc.sendpay([rs], rhash) + l1.rpc.sendpay([rs], rhash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(rhash) assert invoice_unpaid(l2, 'testpayment2') @@ -557,7 +559,19 @@ def only_one(arr): with pytest.raises(RpcError): rs = copy.deepcopy(routestep) rs['id'] = '00000000000000000000000000000000' - l1.rpc.sendpay([rs], rhash) + l1.rpc.sendpay([rs], rhash, payment_secret=inv['payment_secret']) + assert invoice_unpaid(l2, 'testpayment2') + + # Bad payment_secret + l1.rpc.sendpay([routestep], rhash, payment_secret="00" * 32) + with pytest.raises(RpcError): + l1.rpc.waitsendpay(rhash) + assert invoice_unpaid(l2, 'testpayment2') + + # Missing payment_secret + l1.rpc.sendpay([routestep], rhash) + with pytest.raises(RpcError): + l1.rpc.waitsendpay(rhash) assert invoice_unpaid(l2, 'testpayment2') # FIXME: test paying via another node, should fail to pay twice. @@ -570,7 +584,7 @@ def only_one(arr): # This works. before = int(time.time()) - details = l1.rpc.sendpay([routestep], rhash) + details = l1.rpc.sendpay([routestep], rhash, payment_secret=inv['payment_secret']) after = int(time.time()) preimage = l1.rpc.waitsendpay(rhash)['payment_preimage'] # Check details @@ -599,7 +613,7 @@ def check_balances(): # Repeat will "succeed", but won't actually send anything (duplicate) assert not l1.daemon.is_in_log('Payment 0/1: .* COMPLETE') - details = l1.rpc.sendpay([routestep], rhash) + details = l1.rpc.sendpay([routestep], rhash, payment_secret=inv['payment_secret']) assert details['status'] == "complete" preimage2 = details['payment_preimage'] assert preimage == preimage2 @@ -608,10 +622,11 @@ def check_balances(): assert only_one(l2.rpc.listinvoices('testpayment2')['invoices'])['msatoshi_received'] == rs['msatoshi'] # Overpaying by "only" a factor of 2 succeeds. - rhash = l2.rpc.invoice(amt, 'testpayment3', 'desc')['payment_hash'] + inv = l2.rpc.invoice(amt, 'testpayment3', 'desc') + rhash = inv['payment_hash'] assert only_one(l2.rpc.listinvoices('testpayment3')['invoices'])['status'] == 'unpaid' routestep = {'msatoshi': amt * 2, 'id': l2.info['id'], 'delay': 5, 'channel': '1x1x1'} - l1.rpc.sendpay([routestep], rhash) + l1.rpc.sendpay([routestep], rhash, payment_secret=inv['payment_secret']) preimage3 = l1.rpc.waitsendpay(rhash)['payment_preimage'] assert only_one(l2.rpc.listinvoices('testpayment3')['invoices'])['status'] == 'paid' assert only_one(l2.rpc.listinvoices('testpayment3')['invoices'])['msatoshi_received'] == amt * 2 @@ -1030,7 +1045,8 @@ def test_forward(node_factory, bitcoind): assert only_one(l2.rpc.getpeer(l1.info['id'])['channels'])['short_channel_id'] == chanid1 assert only_one(l3.rpc.getpeer(l2.info['id'])['channels'])['short_channel_id'] == chanid2 - rhash = l3.rpc.invoice(100000000, 'testpayment1', 'desc')['payment_hash'] + inv = l3.rpc.invoice(100000000, 'testpayment1', 'desc') + rhash = inv['payment_hash'] assert only_one(l3.rpc.listinvoices('testpayment1')['invoices'])['status'] == 'unpaid' # Fee for node2 is 10 millionths, plus 1. @@ -1049,27 +1065,27 @@ def test_forward(node_factory, bitcoind): # Unknown other peer route = copy.deepcopy(baseroute) route[1]['id'] = '031a8dc444e41bb989653a4501e11175a488a57439b0c4947704fd6e3de5dca607' - l1.rpc.sendpay(route, rhash) + l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) with pytest.raises(RpcError): l1.rpc.waitsendpay(rhash) # Delay too short (we always add one internally anyway, so subtract 2 here). route = copy.deepcopy(baseroute) route[0]['delay'] = 8 - l1.rpc.sendpay(route, rhash) + l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) with pytest.raises(RpcError): l1.rpc.waitsendpay(rhash) # Final delay too short route = copy.deepcopy(baseroute) route[1]['delay'] = 3 - l1.rpc.sendpay(route, rhash) + l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) with pytest.raises(RpcError): l1.rpc.waitsendpay(rhash) # This one works route = copy.deepcopy(baseroute) - l1.rpc.sendpay(route, rhash) + l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(rhash) @@ -1181,11 +1197,12 @@ def test_forward_different_fees_and_cltv(node_factory, bitcoind): assert route[1]['msatoshi'] == 4999999 assert route[1]['delay'] == 9 + shadow_route - rhash = l3.rpc.invoice(4999999, 'test_forward_different_fees_and_cltv', 'desc')['payment_hash'] + inv = l3.rpc.invoice(4999999, 'test_forward_different_fees_and_cltv', 'desc') + rhash = inv['payment_hash'] assert only_one(l3.rpc.listinvoices('test_forward_different_fees_and_cltv')['invoices'])['status'] == 'unpaid' # This should work. - l1.rpc.sendpay(route, rhash) + l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(rhash) # We add one to the blockcount for a bit of fuzz (FIXME: Shadowroute would fix this!) @@ -1254,8 +1271,9 @@ def test_forward_pad_fees_and_cltv(node_factory, bitcoind): route[1]['delay'] += 10 # This should work. - rhash = l3.rpc.invoice(4999999, 'test_forward_pad_fees_and_cltv', 'desc')['payment_hash'] - l1.rpc.sendpay(route, rhash) + inv = l3.rpc.invoice(4999999, 'test_forward_pad_fees_and_cltv', 'desc') + rhash = inv['payment_hash'] + l1.rpc.sendpay(route, rhash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(rhash) assert only_one(l3.rpc.listinvoices('test_forward_pad_fees_and_cltv')['invoices'])['status'] == 'paid' @@ -1282,24 +1300,26 @@ def test_forward_stats(node_factory, bitcoind): wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 8) - payment_hash = l3.rpc.invoice(amount, "first", "desc")['payment_hash'] + inv = l3.rpc.invoice(amount, "first", "desc") + payment_hash = inv['payment_hash'] route = l1.rpc.getroute(l3.info['id'], amount, 1)['route'] - l1.rpc.sendpay(route, payment_hash) + l1.rpc.sendpay(route, payment_hash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(payment_hash) # l4 rejects since it doesn't know the payment_hash route = l1.rpc.getroute(l4.info['id'], amount, 1)['route'] payment_hash = "F" * 64 with pytest.raises(RpcError): - l1.rpc.sendpay(route, payment_hash) + l1.rpc.sendpay(route, payment_hash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(payment_hash) # l5 will hold the HTLC hostage. l5.rpc.dev_ignore_htlcs(id=l2.info['id'], ignore=True) route = l1.rpc.getroute(l5.info['id'], amount, 1)['route'] - payment_hash = l5.rpc.invoice(amount, "first", "desc")['payment_hash'] - l1.rpc.sendpay(route, payment_hash) + inv = l5.rpc.invoice(amount, "first", "desc") + payment_hash = inv['payment_hash'] + l1.rpc.sendpay(route, payment_hash, payment_secret=inv['payment_secret']) l5.daemon.wait_for_log(r'their htlc .* dev_ignore_htlcs') @@ -1420,13 +1440,14 @@ def test_forward_local_failed_stats(node_factory, bitcoind, executor): because of WIRE_UNKNOWN_NEXT_PEER; """ - payment_hash = l3.rpc.invoice(amount, "first", "desc")['payment_hash'] + inv = l3.rpc.invoice(amount, "first", "desc") + payment_hash = inv['payment_hash'] route = l1.rpc.getroute(l3.info['id'], amount, 1)['route'] l2.rpc.close(c23, 1) with pytest.raises(RpcError): - l1.rpc.sendpay(route, payment_hash) + l1.rpc.sendpay(route, payment_hash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(payment_hash) """2. When Master handle the forward process with the htlc_in and @@ -1437,7 +1458,8 @@ def test_forward_local_failed_stats(node_factory, bitcoind, executor): payment will fail in l2 becase of WIRE_FEE_INSUFFICIENT; """ - payment_hash = l4.rpc.invoice(amount, "third", "desc")['payment_hash'] + inv = l4.rpc.invoice(amount, "third", "desc") + payment_hash = inv['payment_hash'] fee = amount * 10 // 1000000 + 1 route = [{'msatoshi': amount, @@ -1450,7 +1472,7 @@ def test_forward_local_failed_stats(node_factory, bitcoind, executor): 'channel': c24}] with pytest.raises(RpcError): - l1.rpc.sendpay(route, payment_hash) + l1.rpc.sendpay(route, payment_hash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(payment_hash) """3. When we send htlc_out, Master asks Channeld to add a new htlc @@ -1462,7 +1484,8 @@ def test_forward_local_failed_stats(node_factory, bitcoind, executor): fail in l2 because of CHANNEL_ERR_MAX_HTLC_VALUE_EXCEEDED; """ - payment_hash = l5.rpc.invoice(amount, "second", "desc")['payment_hash'] + inv = l5.rpc.invoice(amount, "second", "desc") + payment_hash = inv['payment_hash'] fee = amount * 10 // 1000000 + 1 route = [{'msatoshi': amount + fee, @@ -1475,7 +1498,7 @@ def test_forward_local_failed_stats(node_factory, bitcoind, executor): 'channel': c25}] with pytest.raises(RpcError): - l1.rpc.sendpay(route, payment_hash) + l1.rpc.sendpay(route, payment_hash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(payment_hash) """4. When Channeld receives a new revoke message, if the state of @@ -1488,7 +1511,8 @@ def test_forward_local_failed_stats(node_factory, bitcoind, executor): fail in l2 because of WIRE_INVALID_ONION_KEY; """ - payment_hash = l4.rpc.invoice(amount, 'fourth', 'desc')['payment_hash'] + inv = l4.rpc.invoice(amount, 'fourth', 'desc') + payment_hash = inv['payment_hash'] route = l6.rpc.getroute(l4.info['id'], amount, 1)['route'] mangled_nodeid = '0265b6ab5ec860cd257865d61ef0bbf5b3339c36cbda8b26b74e7f1dca490b6510' @@ -1497,7 +1521,7 @@ def test_forward_local_failed_stats(node_factory, bitcoind, executor): route[1]['id'] = mangled_nodeid with pytest.raises(RpcError): - l6.rpc.sendpay(route, payment_hash) + l6.rpc.sendpay(route, payment_hash, payment_secret=inv['payment_secret']) l6.rpc.waitsendpay(payment_hash) """5. When Onchaind finds the htlc time out or missing htlc, Master @@ -1509,7 +1533,8 @@ def test_forward_local_failed_stats(node_factory, bitcoind, executor): before sending update_fail_htlc, so the htlc will be holding until l2 meets timeout and handle it as local_fail. """ - payment_hash = l4.rpc.invoice(amount, 'onchain_timeout', 'desc')['payment_hash'] + inv = l4.rpc.invoice(amount, 'onchain_timeout', 'desc') + payment_hash = inv['payment_hash'] fee = amount * 10 // 1000000 + 1 # We underpay, so it fails. @@ -1522,7 +1547,7 @@ def test_forward_local_failed_stats(node_factory, bitcoind, executor): 'delay': 5, 'channel': c24}] - executor.submit(l1.rpc.sendpay, route, payment_hash) + executor.submit(l1.rpc.sendpay, route, payment_hash, payment_secret=inv['payment_secret']) l4.daemon.wait_for_log('permfail') l4.wait_for_channel_onchain(l2.info['id']) @@ -1568,12 +1593,13 @@ def test_htlcs_cltv_only_difference(node_factory, bitcoind): # l3 will see a reconnect from l4 when l4 restarts. l1, l2, l3, l4 = node_factory.line_graph(4, wait_for_announce=True, opts=[{}] * 2 + [{'dev-no-reconnect': None, 'may_reconnect': True}] * 2) - h = l4.rpc.invoice(msatoshi=10**8, label='x', description='desc')['payment_hash'] + inv = l4.rpc.invoice(msatoshi=10**8, label='x', description='desc') + h = inv['payment_hash'] l4.rpc.dev_ignore_htlcs(id=l3.info['id'], ignore=True) # L2 tries to pay r = l2.rpc.getroute(l4.info['id'], 10**8, 1)["route"] - l2.rpc.sendpay(r, h) + l2.rpc.sendpay(r, h, payment_secret=inv['payment_secret']) # Now increment CLTV bitcoind.generate_block(1) @@ -1581,7 +1607,7 @@ def test_htlcs_cltv_only_difference(node_factory, bitcoind): # L1 tries to pay r = l1.rpc.getroute(l4.info['id'], 10**8, 1)["route"] - l1.rpc.sendpay(r, h) + l1.rpc.sendpay(r, h, payment_secret=inv['payment_secret']) # Now increment CLTV bitcoind.generate_block(1) @@ -1589,7 +1615,7 @@ def test_htlcs_cltv_only_difference(node_factory, bitcoind): # L3 tries to pay r = l3.rpc.getroute(l4.info['id'], 10**8, 1)["route"] - l3.rpc.sendpay(r, h) + l3.rpc.sendpay(r, h, payment_secret=inv['payment_secret']) # Give them time to go through. time.sleep(5) @@ -1656,7 +1682,7 @@ def exhaust_channel(opener, peer, scid, already_spent=0): 'delay': 10, 'channel': scid } - opener.rpc.sendpay([routestep], inv['payment_hash']) + opener.rpc.sendpay([routestep], inv['payment_hash'], payment_secret=inv['payment_secret']) opener.rpc.waitsendpay(inv['payment_hash']) # We connect every node to l5; in a line and individually. @@ -2240,12 +2266,13 @@ def test_channel_spendable(node_factory, bitcoind): l1, l2 = node_factory.line_graph(2, fundamount=sats, wait_for_announce=True, opts={'plugin': os.path.join(os.getcwd(), 'tests/plugins/hold_invoice.py'), 'holdtime': '30'}) - payment_hash = l2.rpc.invoice('any', 'inv', 'for testing')['payment_hash'] + inv = l2.rpc.invoice('any', 'inv', 'for testing') + payment_hash = inv['payment_hash'] # We should be able to spend this much, and not one msat more! amount = l1.rpc.listpeers()['peers'][0]['channels'][0]['spendable_msat'] route = l1.rpc.getroute(l2.info['id'], amount + 1, riskfactor=1, fuzzpercent=0)['route'] - l1.rpc.sendpay(route, payment_hash) + l1.rpc.sendpay(route, payment_hash, payment_secret=inv['payment_secret']) # This should fail locally with "capacity exceeded" with pytest.raises(RpcError, match=r"Capacity exceeded.*'erring_index': 0"): @@ -2253,7 +2280,7 @@ def test_channel_spendable(node_factory, bitcoind): # Exact amount should succeed. route = l1.rpc.getroute(l2.info['id'], amount, riskfactor=1, fuzzpercent=0)['route'] - l1.rpc.sendpay(route, payment_hash) + l1.rpc.sendpay(route, payment_hash, payment_secret=inv['payment_secret']) # Amount should drop to 0 once HTLC is sent; we have time, thanks to # hold_invoice.py plugin. @@ -2264,12 +2291,13 @@ def test_channel_spendable(node_factory, bitcoind): # Make sure l2 thinks it's all over. wait_for(lambda: len(l2.rpc.listpeers()['peers'][0]['channels'][0]['htlcs']) == 0) # Now, reverse should work similarly. - payment_hash = l1.rpc.invoice('any', 'inv', 'for testing')['payment_hash'] + inv = l1.rpc.invoice('any', 'inv', 'for testing') + payment_hash = inv['payment_hash'] amount = l2.rpc.listpeers()['peers'][0]['channels'][0]['spendable_msat'] # Turns out we won't route this, as it's over max - reserve: route = l2.rpc.getroute(l1.info['id'], amount + 1, riskfactor=1, fuzzpercent=0)['route'] - l2.rpc.sendpay(route, payment_hash) + l2.rpc.sendpay(route, payment_hash, payment_secret=inv['payment_secret']) # This should fail locally with "capacity exceeded" with pytest.raises(RpcError, match=r"Capacity exceeded.*'erring_index': 0"): @@ -2277,7 +2305,7 @@ def test_channel_spendable(node_factory, bitcoind): # Exact amount should succeed. route = l2.rpc.getroute(l1.info['id'], amount, riskfactor=1, fuzzpercent=0)['route'] - l2.rpc.sendpay(route, payment_hash) + l2.rpc.sendpay(route, payment_hash, payment_secret=inv['payment_secret']) # Amount should drop to 0 once HTLC is sent; we have time, thanks to # hold_invoice.py plugin. @@ -2293,12 +2321,13 @@ def test_channel_receivable(node_factory, bitcoind): l1, l2 = node_factory.line_graph(2, fundamount=sats, wait_for_announce=True, opts={'plugin': os.path.join(os.getcwd(), 'tests/plugins/hold_invoice.py'), 'holdtime': '30'}) - payment_hash = l2.rpc.invoice('any', 'inv', 'for testing')['payment_hash'] + inv = l2.rpc.invoice('any', 'inv', 'for testing') + payment_hash = inv['payment_hash'] # We should be able to receive this much, and not one msat more! amount = l2.rpc.listpeers()['peers'][0]['channels'][0]['receivable_msat'] route = l1.rpc.getroute(l2.info['id'], amount + 1, riskfactor=1, fuzzpercent=0)['route'] - l1.rpc.sendpay(route, payment_hash) + l1.rpc.sendpay(route, payment_hash, payment_secret=inv['payment_secret']) # This should fail locally with "capacity exceeded" with pytest.raises(RpcError, match=r"Capacity exceeded.*'erring_index': 0"): @@ -2306,7 +2335,7 @@ def test_channel_receivable(node_factory, bitcoind): # Exact amount should succeed. route = l1.rpc.getroute(l2.info['id'], amount, riskfactor=1, fuzzpercent=0)['route'] - l1.rpc.sendpay(route, payment_hash) + l1.rpc.sendpay(route, payment_hash, payment_secret=inv['payment_secret']) # Amount should drop to 0 once HTLC is sent; we have time, thanks to # hold_invoice.py plugin. @@ -2317,12 +2346,13 @@ def test_channel_receivable(node_factory, bitcoind): # Make sure l2 thinks it's all over. wait_for(lambda: len(l2.rpc.listpeers()['peers'][0]['channels'][0]['htlcs']) == 0) # Now, reverse should work similarly. - payment_hash = l1.rpc.invoice('any', 'inv', 'for testing')['payment_hash'] + inv = l1.rpc.invoice('any', 'inv', 'for testing') + payment_hash = inv['payment_hash'] amount = l1.rpc.listpeers()['peers'][0]['channels'][0]['receivable_msat'] # Turns out we won't route this, as it's over max - reserve: route = l2.rpc.getroute(l1.info['id'], amount + 1, riskfactor=1, fuzzpercent=0)['route'] - l2.rpc.sendpay(route, payment_hash) + l2.rpc.sendpay(route, payment_hash, payment_secret=inv['payment_secret']) # This should fail locally with "capacity exceeded" with pytest.raises(RpcError, match=r"Capacity exceeded.*'erring_index': 0"): @@ -2330,7 +2360,7 @@ def test_channel_receivable(node_factory, bitcoind): # Exact amount should succeed. route = l2.rpc.getroute(l1.info['id'], amount, riskfactor=1, fuzzpercent=0)['route'] - l2.rpc.sendpay(route, payment_hash) + l2.rpc.sendpay(route, payment_hash, payment_secret=inv['payment_secret']) # Amount should drop to 0 once HTLC is sent; we have time, thanks to # hold_invoice.py plugin. @@ -2354,7 +2384,8 @@ def test_channel_spendable_large(node_factory, bitcoind): } ) - payment_hash = l2.rpc.invoice('any', 'inv', 'for testing')['payment_hash'] + inv = l2.rpc.invoice('any', 'inv', 'for testing') + payment_hash = inv['payment_hash'] # We should be able to spend this much, and not one msat more! spendable = l1.rpc.listpeers()['peers'][0]['channels'][0]['spendable_msat'] @@ -2366,12 +2397,12 @@ def test_channel_spendable_large(node_factory, bitcoind): # route or waitsendpay fill fail. with pytest.raises(RpcError): route = l1.rpc.getroute(l2.info['id'], spendable + 1, riskfactor=1, fuzzpercent=0)['route'] - l1.rpc.sendpay(route, payment_hash) + l1.rpc.sendpay(route, payment_hash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(payment_hash, TIMEOUT) # Exact amount should succeed. route = l1.rpc.getroute(l2.info['id'], spendable, riskfactor=1, fuzzpercent=0)['route'] - l1.rpc.sendpay(route, payment_hash) + l1.rpc.sendpay(route, payment_hash, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(payment_hash, TIMEOUT) @@ -2417,7 +2448,7 @@ def test_error_returns_blockheight(node_factory, bitcoind): 'id': l2.info['id'], 'delay': 10, 'channel': l1.get_channel_scid(l2)}], - '00' * 32) + '00' * 32, payment_secret='00' * 32) with pytest.raises(RpcError, match=r"INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS.*'erring_index': 1") as err: l1.rpc.waitsendpay('00' * 32, TIMEOUT) @@ -2433,6 +2464,8 @@ def test_error_returns_blockheight(node_factory, bitcoind): @pytest.mark.developer('Needs dev-routes') def test_tlv_or_legacy(node_factory, bitcoind): + # Ideally we'd test with l2 NOT var-onion, but then it can no longer connect + # to us! l1, l2, l3 = node_factory.line_graph(3, opts={'plugin': os.path.join(os.getcwd(), 'tests/plugins/print_htlc_onion.py')}) @@ -2451,8 +2484,8 @@ def test_tlv_or_legacy(node_factory, bitcoind): 'cltv_expiry_delta': 6}]]})['bolt11'] l1.rpc.pay(inv) - # Since L1 hasn't seen broadcast, it doesn't know L2 is TLV, but invoice tells it about L3 - l2.daemon.wait_for_log("Got onion.*'type': 'legacy'") + # Since L1 hasn't seen broadcast, it doesn't know L2 isn't TLV, but invoice tells it about L3 + l2.daemon.wait_for_log("Got onion.*'type': 'tlv'") l3.daemon.wait_for_log("Got onion.*'type': 'tlv'") # We need 5 more blocks to announce l1->l2 channel. @@ -2487,9 +2520,8 @@ def test_pay_no_secret(node_factory, bitcoind): # Produced from old version (no secret!) inv_nosecret = 'lnbcrt1u1pwue4vapp5ve584t0cv27hwmy0cx9ca8uwyqyfw9y9dm3r8vus9fv36r2l9yjsdqaw3jhxazlwpshjhmwda0hxetrwfjhgxq8pmnt9qqcqp9570xsjyykvssa6ty8fjth6f2y8h09myngad9utesttwjwclv95fz3lgd402f9e5yzpnxmkypg55rkvpg522gcz4ymsjl2w3m4jhw4jsp55m7tl' - # This succeeds until we make secrets compulsory. - l1.rpc.pay(inv_nosecret) - l2.daemon.wait_for_log(r'HTLC set contains 1 HTLCs, for a total of 100000msat out of 100000msat \(no payment_secret\)') + with pytest.raises(RpcError, match=r"INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS.*'erring_index': 1"): + l1.rpc.pay(inv_nosecret) @flaky @@ -2575,6 +2607,40 @@ def serialize_payload(n): payload += "00" * 12 return payload + def serialize_payload_final_tlv(n, payment_secret: str): + def truncate_encode(i: int): + """Encode a tu64 (or tu32 etc) value""" + ret = struct.pack("!Q", i) + while ret.startswith(b'\0'): + ret = ret[1:] + return ret + + payload = TlvPayload() + # BOLT #4: + # 1. type: 2 (`amt_to_forward`) + # 2. data: + # * [`tu64`:`amt_to_forward`] + b = BytesIO() + b.write(truncate_encode(int(n['amount_msat']))) + payload.add_field(2, b.getvalue()) + # BOLT #4: + # 1. type: 4 (`outgoing_cltv_value`) + # 2. data: + # * [`tu32`:`outgoing_cltv_value`] + b = BytesIO() + b.write(truncate_encode(n['delay'])) + payload.add_field(4, b.getvalue()) + # BOLT #4: + # 1. type: 8 (`payment_data`) + # 2. data: + # * [`32*byte`:`payment_secret`] + # * [`tu64`:`total_msat`] + b = BytesIO() + b.write(bytes.fromhex(payment_secret)) + b.write(truncate_encode(int(n['amount_msat']))) + payload.add_field(8, b.getvalue()) + return payload.to_bytes().hex() + # Need to shift the parameters by one hop hops = [] for h, n in zip(route[:-1], route[1:]): @@ -2587,7 +2653,7 @@ def serialize_payload(n): # The last hop has a special payload: hops.append({ "pubkey": route[-1]['id'], - "payload": serialize_payload(route[-1]) + "payload": serialize_payload_final_tlv(route[-1], inv['payment_secret']) }) onion = l1.rpc.createonion(hops=hops, assocdata=inv['payment_hash']) @@ -2925,18 +2991,18 @@ def test_sendpay_msatoshi_arg(node_factory): with pytest.raises(RpcError, match=r'Do not specify msatoshi \(1001msat\) without' ' partid: if you do, it must be exactly' r' the final amount \(1000msat\)'): - l1.rpc.sendpay(route=l1.rpc.getroute(l2.info['id'], 1000, 1)['route'], payment_hash=inv['payment_hash'], msatoshi=1001, bolt11=inv['bolt11']) + l1.rpc.sendpay(route=l1.rpc.getroute(l2.info['id'], 1000, 1)['route'], payment_hash=inv['payment_hash'], msatoshi=1001, bolt11=inv['bolt11'], payment_secret=inv['payment_secret']) with pytest.raises(RpcError, match=r'Do not specify msatoshi \(999msat\) without' ' partid: if you do, it must be exactly' r' the final amount \(1000msat\)'): - l1.rpc.sendpay(route=l1.rpc.getroute(l2.info['id'], 1000, 1)['route'], payment_hash=inv['payment_hash'], msatoshi=999, bolt11=inv['bolt11']) + l1.rpc.sendpay(route=l1.rpc.getroute(l2.info['id'], 1000, 1)['route'], payment_hash=inv['payment_hash'], msatoshi=999, bolt11=inv['bolt11'], payment_secret=inv['payment_secret']) # Can't send MPP payment which pays any more than amount. with pytest.raises(RpcError, match=r'Final amount 1001msat is greater than 1000msat, despite MPP'): - l1.rpc.sendpay(route=l1.rpc.getroute(l2.info['id'], 1001, 1)['route'], payment_hash=inv['payment_hash'], msatoshi=1000, bolt11=inv['bolt11'], partid=1) + l1.rpc.sendpay(route=l1.rpc.getroute(l2.info['id'], 1001, 1)['route'], payment_hash=inv['payment_hash'], msatoshi=1000, bolt11=inv['bolt11'], partid=1, payment_secret=inv['payment_secret']) # But this works - l1.rpc.sendpay(route=l1.rpc.getroute(l2.info['id'], 1001, 1)['route'], payment_hash=inv['payment_hash'], msatoshi=1001, bolt11=inv['bolt11']) + l1.rpc.sendpay(route=l1.rpc.getroute(l2.info['id'], 1001, 1)['route'], payment_hash=inv['payment_hash'], msatoshi=1001, bolt11=inv['bolt11'], payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(inv['payment_hash']) inv = only_one(l2.rpc.listinvoices('inv')['invoices']) @@ -3017,7 +3083,7 @@ def test_sendpay_blinding(node_factory): 'style': 'tlv'}] l1.rpc.sendpay(route=route, payment_hash=inv['payment_hash'], - bolt11=inv['bolt11']) + bolt11=inv['bolt11'], payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(inv['payment_hash']) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 5ad43a318b13..342e4381e1cc 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -1001,13 +1001,14 @@ def test_htlc_accepted_hook_fail(node_factory): ], wait_for_announce=True) # This must fail - phash = l2.rpc.invoice(1000, "lbl", "desc")['payment_hash'] + inv = l2.rpc.invoice(1000, "lbl", "desc") + phash = inv['payment_hash'] route = l1.rpc.getroute(l2.info['id'], 1000, 1)['route'] # Here shouldn't use `pay` command because l2 rejects with WIRE_TEMPORARY_NODE_FAILURE, # then it will be excluded when l1 try another pay attempt. # Note if the destination is excluded, the route result is undefined. - l1.rpc.sendpay(route, phash) + l1.rpc.sendpay(route, phash, payment_secret=inv['payment_secret']) with pytest.raises(RpcError) as excinfo: l1.rpc.waitsendpay(phash) assert excinfo.value.error['data']['failcode'] == 0x2002 @@ -1231,22 +1232,24 @@ def test_forward_event_notification(node_factory, bitcoind, executor): wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 8) - payment_hash13 = l3.rpc.invoice(amount, "first", "desc")['payment_hash'] + inv = l3.rpc.invoice(amount, "first", "desc") + payment_hash13 = inv['payment_hash'] route = l1.rpc.getroute(l3.info['id'], amount, 1)['route'] # status: offered -> settled - l1.rpc.sendpay(route, payment_hash13) + l1.rpc.sendpay(route, payment_hash13, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(payment_hash13) # status: offered -> failed route = l1.rpc.getroute(l4.info['id'], amount, 1)['route'] payment_hash14 = "f" * 64 with pytest.raises(RpcError): - l1.rpc.sendpay(route, payment_hash14) + l1.rpc.sendpay(route, payment_hash14, payment_secret="f" * 64) l1.rpc.waitsendpay(payment_hash14) # status: offered -> local_failed - payment_hash15 = l5.rpc.invoice(amount, 'onchain_timeout', 'desc')['payment_hash'] + inv = l5.rpc.invoice(amount, 'onchain_timeout', 'desc') + payment_hash15 = inv['payment_hash'] fee = amount * 10 // 1000000 + 1 c12 = l1.get_channel_scid(l2) c25 = l2.get_channel_scid(l5) @@ -1259,7 +1262,7 @@ def test_forward_event_notification(node_factory, bitcoind, executor): 'delay': 5, 'channel': c25}] - executor.submit(l1.rpc.sendpay, route, payment_hash15) + executor.submit(l1.rpc.sendpay, route, payment_hash15, payment_secret=inv['payment_secret']) l5.daemon.wait_for_log('permfail') l5.wait_for_channel_onchain(l2.info['id']) @@ -1320,16 +1323,18 @@ def test_sendpay_notifications(node_factory, bitcoind): l1, l2, l3 = node_factory.line_graph(3, opts=opts, wait_for_announce=True) chanid23 = l2.get_channel_scid(l3) - payment_hash1 = l3.rpc.invoice(amount, "first", "desc")['payment_hash'] - payment_hash2 = l3.rpc.invoice(amount, "second", "desc")['payment_hash'] + inv1 = l3.rpc.invoice(amount, "first", "desc") + payment_hash1 = inv1['payment_hash'] + inv2 = l3.rpc.invoice(amount, "second", "desc") + payment_hash2 = inv2['payment_hash'] route = l1.rpc.getroute(l3.info['id'], amount, 1)['route'] - l1.rpc.sendpay(route, payment_hash1) + l1.rpc.sendpay(route, payment_hash1, payment_secret=inv1['payment_secret']) response1 = l1.rpc.waitsendpay(payment_hash1) l2.rpc.close(chanid23, 1) - l1.rpc.sendpay(route, payment_hash2) + l1.rpc.sendpay(route, payment_hash2, payment_secret=inv2['payment_secret']) with pytest.raises(RpcError) as err: l1.rpc.waitsendpay(payment_hash2) @@ -1349,16 +1354,18 @@ def test_sendpay_notifications_nowaiter(node_factory): chanid23 = l2.get_channel_scid(l3) amount = 10**8 - payment_hash1 = l3.rpc.invoice(amount, "first", "desc")['payment_hash'] - payment_hash2 = l3.rpc.invoice(amount, "second", "desc")['payment_hash'] + inv1 = l3.rpc.invoice(amount, "first", "desc") + payment_hash1 = inv1['payment_hash'] + inv2 = l3.rpc.invoice(amount, "second", "desc") + payment_hash2 = inv2['payment_hash'] route = l1.rpc.getroute(l3.info['id'], amount, 1)['route'] - l1.rpc.sendpay(route, payment_hash1) + l1.rpc.sendpay(route, payment_hash1, payment_secret=inv1['payment_secret']) l1.daemon.wait_for_log(r'Received a sendpay_success') l2.rpc.close(chanid23, 1) - l1.rpc.sendpay(route, payment_hash2) + l1.rpc.sendpay(route, payment_hash2, payment_secret=inv2['payment_secret']) l1.daemon.wait_for_log(r'Received a sendpay_failure') results = l1.rpc.call('listsendpays_plugin') @@ -1980,36 +1987,40 @@ def test_coin_movement_notices(node_factory, bitcoind, chainparams): wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 4) amount = 10**8 - payment_hash13 = l3.rpc.invoice(amount, "first", "desc")['payment_hash'] + inv = l3.rpc.invoice(amount, "first", "desc") + payment_hash13 = inv['payment_hash'] route = l1.rpc.getroute(l3.info['id'], amount, 1)['route'] # status: offered -> settled - l1.rpc.sendpay(route, payment_hash13) + l1.rpc.sendpay(route, payment_hash13, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(payment_hash13) # status: offered -> failed route = l1.rpc.getroute(l3.info['id'], amount, 1)['route'] payment_hash13 = "f" * 64 with pytest.raises(RpcError): - l1.rpc.sendpay(route, payment_hash13) + l1.rpc.sendpay(route, payment_hash13, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(payment_hash13) # go the other direction - payment_hash31 = l1.rpc.invoice(amount // 2, "first", "desc")['payment_hash'] + inv = l1.rpc.invoice(amount // 2, "first", "desc") + payment_hash31 = inv['payment_hash'] route = l3.rpc.getroute(l1.info['id'], amount // 2, 1)['route'] - l3.rpc.sendpay(route, payment_hash31) + l3.rpc.sendpay(route, payment_hash31, payment_secret=inv['payment_secret']) l3.rpc.waitsendpay(payment_hash31) # receive a payment (endpoint) - payment_hash12 = l2.rpc.invoice(amount, "first", "desc")['payment_hash'] + inv = l2.rpc.invoice(amount, "first", "desc") + payment_hash12 = inv['payment_hash'] route = l1.rpc.getroute(l2.info['id'], amount, 1)['route'] - l1.rpc.sendpay(route, payment_hash12) + l1.rpc.sendpay(route, payment_hash12, payment_secret=inv['payment_secret']) l1.rpc.waitsendpay(payment_hash12) # send a payment (originator) - payment_hash21 = l1.rpc.invoice(amount // 2, "second", "desc")['payment_hash'] + inv = l1.rpc.invoice(amount // 2, "second", "desc") + payment_hash21 = inv['payment_hash'] route = l2.rpc.getroute(l1.info['id'], amount // 2, 1)['route'] - l2.rpc.sendpay(route, payment_hash21) + l2.rpc.sendpay(route, payment_hash21, payment_secret=inv['payment_secret']) l2.rpc.waitsendpay(payment_hash21) # restart to test index diff --git a/tests/utils.py b/tests/utils.py index 446ec0c816ef..81f34f111b55 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -20,7 +20,7 @@ def hex_bits(features): def expected_peer_features(wumbo_channels=False, extra=[]): """Return the expected peer features hexstring for this configuration""" - features = [1, 5, 7, 9, 11, 13, 15, 17, 27] + features = [1, 5, 7, 9, 11, 13, 14, 17, 27] if EXPERIMENTAL_FEATURES: # OPT_ONION_MESSAGES features += [39] @@ -40,7 +40,7 @@ def expected_peer_features(wumbo_channels=False, extra=[]): # features for the 'node' and the 'peer' feature sets def expected_node_features(wumbo_channels=False, extra=[]): """Return the expected node features hexstring for this configuration""" - features = [1, 5, 7, 9, 11, 13, 15, 17, 27, 55] + features = [1, 5, 7, 9, 11, 13, 14, 17, 27, 55] if EXPERIMENTAL_FEATURES: # OPT_ONION_MESSAGES features += [39]