lightningd: deprecate decodepay.

It only works on BOLT11, and has long been replaced by the more
generic "decode".

Removing it will stop the confusion!

(Note: documentation claims it was introduced in 23.08, but that was
 wrong, as it's been in CLN since the beginning).

[ Fixup from: niftynei <niftynei@gmail.com> ]
Fixes: https://github.com/ElementsProject/lightning/issues/6419
Changelog-Deprecated: JSON-RPC: `decodepay`: use `decode`.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2024-11-12 09:12:25 +10:30
parent 374db239be
commit fcebb33180
12 changed files with 115 additions and 80 deletions

View File

@ -10523,9 +10523,15 @@
"$schema": "../rpc-schema-draft.json",
"type": "object",
"added": "v23.05",
"depecated": [
"v24.11",
"v25.11"
],
"rpc": "decodepay",
"title": "Command for decoding a bolt11 string (low-level)",
"description": [
"WARNING: deprecated: use *decode* which also handles bolt12.",
"",
"The **decodepay** RPC command checks and parses a *bolt11* string as specified by the BOLT 11 specification."
],
"request": {

View File

@ -1212,8 +1212,7 @@ class LightningNode(object):
# make an invoice
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']
psecret = inv['payment_secret']
rhash = inv['payment_hash']
invoices = dst.rpc.listinvoices(label)['invoices']
assert len(invoices) == 1 and invoices[0]['status'] == 'unpaid'

View File

@ -33,7 +33,7 @@ hidden: false
| experimental-onion-messages | Config | v24.08 | v25.02 | Now the default |
| decode.blinding | Field | v24.11 | v25.05 | Renamed to `first_path_key` in BOLT 4 (available in `decode` from v24.11) |
| onion_message_recv.blinding | Hook Field | v24.11 | v25.05 | Renamed to `first_path_key` in BOLT 4 (available in hook from v24.11) |
| decodepay | Command | v24.11 | v25.11 | Use `decode` which is more powerful (since v23.05) |
Inevitably there are features which need to change: either to be generalized, or removed when they can no longer be supported.

View File

@ -2,9 +2,15 @@
"$schema": "../rpc-schema-draft.json",
"type": "object",
"added": "v23.05",
"depecated": [
"v24.11",
"v25.11"
],
"rpc": "decodepay",
"title": "Command for decoding a bolt11 string (low-level)",
"description": [
"WARNING: deprecated: use *decode* which also handles bolt12.",
"",
"The **decodepay** RPC command checks and parses a *bolt11* string as specified by the BOLT 11 specification."
],
"request": {

View File

@ -1585,6 +1585,8 @@ static struct command_result *json_decodepay(struct command *cmd,
static const struct json_command decodepay_command = {
"decodepay",
json_decodepay,
.depr_start = "v24.11",
.depr_end = "v25.11"
};
AUTODATA(json_command, &decodepay_command);

View File

@ -700,7 +700,6 @@ def generate_utils_examples(l1, l2, l3, l4, l5, l6, c23, c34, inv_l11, inv_l22,
update_example(node=l2, method='signmessage', params={'message': 'message for you'})
update_example(node=l2, method='checkmessage', params={'message': 'testcase to check new rpc error', 'zbase': 'd66bqz3qsku5fxtqsi37j11pci47ydxa95iusphutggz9ezaxt56neh77kxe5hyr41kwgkncgiu94p9ecxiexgpgsz8daoq4tw8kj8yx', 'pubkey': '03be3b0e9992153b1d5a6e1623670b6c3663f72ce6cf2e0dd39c0a373a7de5a3b7'})
update_example(node=l2, method='checkmessage', params={'message': 'this is a test!', 'zbase': 'd6tqaeuonjhi98mmont9m4wag7gg4krg1f4txonug3h31e9h6p6k6nbwjondnj46dkyausobstnk7fhyy998bhgc1yr98dfmhb4k54d7'})
update_example(node=l2, method='decodepay', params={'bolt11': inv_l11['bolt11']})
update_example(node=l2, method='decode', params=[rune_l21['rune']])
update_example(node=l2, method='decode', params=[inv_l22['bolt11']])

View File

@ -375,12 +375,6 @@ def test_grpc_decode(node_factory):
label="label",
))
res = l1.grpc.DecodePay(clnpb.DecodepayRequest(
bolt11=inv.bolt11
))
# If we get here we're good, conversions work
print(res)
res = l1.grpc.Decode(clnpb.DecodeRequest(
string=inv.bolt11
))

View File

@ -24,7 +24,8 @@ def test_invoice(node_factory, chainparams):
l1.daemon.wait_for_log(r': "{}:invoice#[0-9]*/cln:listincoming#[0-9]*"\[OUT\]'.format(myname))
after = int(time.time())
b11 = l1.rpc.decodepay(inv['bolt11'])
b11 = l1.rpc.decode(inv['bolt11'])
assert b11['type'] == 'bolt11 invoice'
assert b11['currency'] == chainparams['bip173_prefix']
assert b11['created_at'] >= before
assert b11['created_at'] <= after
@ -63,7 +64,7 @@ def test_invoice(node_factory, chainparams):
# Test cltv option.
inv = l1.rpc.invoice(123000, 'label3', 'description', 3700, cltv=99)
b11 = l1.rpc.decodepay(inv['bolt11'])
b11 = l1.rpc.decode(inv['bolt11'])
assert b11['min_final_cltv_expiry'] == 99
@ -103,7 +104,7 @@ def test_invoice_weirdstring(node_factory):
inv = only_one(l1.rpc.listinvoices()['invoices'])
assert inv['label'] == weird_label
b11 = l1.rpc.decodepay(inv['bolt11'])
b11 = l1.rpc.decode(inv['bolt11'])
assert b11['description'] == weird_desc
# Can delete by weird label.
@ -123,7 +124,7 @@ def test_invoice_weirdstring(node_factory):
inv = only_one(l1.rpc.listinvoices()['invoices'])
assert inv['label'] == str(weird_label)
b11 = l1.rpc.decodepay(inv['bolt11'])
b11 = l1.rpc.decode(inv['bolt11'])
assert b11['description'] == weird_desc
# Can delete by weird label.
@ -167,7 +168,7 @@ def test_invoice_routeboost(node_factory, bitcoind):
assert 'warning_deadends' not in inv
assert 'warning_mpp' not in inv
# Route array has single route with single element.
r = only_one(only_one(l2.rpc.decodepay(inv['bolt11'])['routes']))
r = only_one(only_one(l2.rpc.decode(inv['bolt11'])['routes']))
assert r['pubkey'] == l2.info['id']
assert r['short_channel_id'] == l3.rpc.listpeerchannels(l2.info['id'])['channels'][0]['short_channel_id']
assert r['fee_base_msat'] == 1
@ -241,7 +242,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind):
assert 'warning_deadends' not in inv
assert 'warning_mpp' not in inv
# Route array has single route with single element.
r = only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes']))
r = only_one(only_one(l1.rpc.decode(inv['bolt11'])['routes']))
assert r['pubkey'] == l1.info['id']
# It uses our private alias!
assert r['short_channel_id'] != l1.rpc.listchannels()['channels'][0]['short_channel_id']
@ -257,7 +258,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind):
assert 'warning_offline' not in inv
assert 'warning_deadends' not in inv
assert 'warning_mpp' not in inv
assert 'routes' not in l1.rpc.decodepay(inv['bolt11'])
assert 'routes' not in l1.rpc.decode(inv['bolt11'])
# If we ask for it, we get it.
inv = l2.rpc.invoice(amount_msat=123456, label="inv1a", description="?", exposeprivatechannels=scid)
@ -267,7 +268,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind):
assert 'warning_deadends' not in inv
assert 'warning_mpp' not in inv
# Route array has single route with single element.
r = only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes']))
r = only_one(only_one(l1.rpc.decode(inv['bolt11'])['routes']))
assert r['pubkey'] == l1.info['id']
assert r['short_channel_id'] == alias
assert r['fee_base_msat'] == 1
@ -282,7 +283,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind):
assert 'warning_deadends' not in inv
assert 'warning_mpp' not in inv
# Route array has single route with single element.
r = only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes']))
r = only_one(only_one(l1.rpc.decode(inv['bolt11'])['routes']))
assert r['pubkey'] == l1.info['id']
assert r['short_channel_id'] == alias
assert r['fee_base_msat'] == 1
@ -314,7 +315,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind):
assert 'warning_deadends' not in inv
assert 'warning_mpp' not in inv
# Route array has single route with single element.
r = only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes']))
r = only_one(only_one(l1.rpc.decode(inv['bolt11'])['routes']))
assert r['pubkey'] == l1.info['id']
assert r['short_channel_id'] == alias
assert r['fee_base_msat'] == 1
@ -328,7 +329,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind):
assert 'warning_deadends' not in inv
assert 'warning_mpp' not in inv
# Route array has single route with single element.
r = only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes']))
r = only_one(only_one(l1.rpc.decode(inv['bolt11'])['routes']))
assert r['pubkey'] == l1.info['id']
assert r['short_channel_id'] == alias
assert r['fee_base_msat'] == 1
@ -351,7 +352,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind):
assert 'warning_deadends' not in inv
assert 'warning_mpp' not in inv
# Route array has single route with single element.
r = only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes']))
r = only_one(only_one(l1.rpc.decode(inv['bolt11'])['routes']))
assert r['pubkey'] == l1.info['id']
assert r['short_channel_id'] == alias
assert r['fee_base_msat'] == 1
@ -371,7 +372,7 @@ def test_invoice_routeboost_private(node_factory, bitcoind):
assert 'warning_deadends' not in inv
assert 'warning_mpp' not in inv
# Route array has single route with single element.
r = only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes']))
r = only_one(only_one(l1.rpc.decode(inv['bolt11'])['routes']))
assert r['pubkey'] == l1.info['id']
assert r['short_channel_id'] == alias
assert r['fee_base_msat'] == 1
@ -535,11 +536,11 @@ def test_signinvoice(node_factory, executor):
# Create an invoice for l1
inv1 = l1.rpc.invoice(1000, 'inv1', 'inv1')['bolt11']
assert l1.rpc.decodepay(inv1)['payee'] == l1.info['id']
assert l1.rpc.decode(inv1)['payee'] == l1.info['id']
# Have l2 re-sign the invoice
inv2 = l2.rpc.signinvoice(inv1)['bolt11']
assert l1.rpc.decodepay(inv2)['payee'] == l2.info['id']
assert l1.rpc.decode(inv2)['payee'] == l2.info['id']
def test_waitanyinvoice_reversed(node_factory, executor):
@ -570,7 +571,8 @@ def test_waitanyinvoice_reversed(node_factory, executor):
def test_decode_unknown(node_factory):
l1 = node_factory.get_node()
b11 = l1.rpc.decodepay('lntb30m1pw2f2yspp5s59w4a0kjecw3zyexm7zur8l8n4scw674w8sftjhwec33km882gsdpa2pshjmt9de6zqun9w96k2um5ypmkjargypkh2mr5d9cxzun5ypeh2ursdae8gxqruyqvzddp68gup69uhnzwfj9cejuvf3xshrwde68qcrswf0d46kcarfwpshyaplw3skw0tdw4k8g6tsv9e8gu2etcvsym36pdjpz04wm9nn96f9ntc3t3h5r08pe9d62p3js5wt5rkurqnrl7zkj2fjpvl3rmn7wwazt80letwxlm22hngu8n88g7hsp542qpl')
b11 = l1.rpc.decode('lntb30m1pw2f2yspp5s59w4a0kjecw3zyexm7zur8l8n4scw674w8sftjhwec33km882gsdpa2pshjmt9de6zqun9w96k2um5ypmkjargypkh2mr5d9cxzun5ypeh2ursdae8gxqruyqvzddp68gup69uhnzwfj9cejuvf3xshrwde68qcrswf0d46kcarfwpshyaplw3skw0tdw4k8g6tsv9e8gu2etcvsym36pdjpz04wm9nn96f9ntc3t3h5r08pe9d62p3js5wt5rkurqnrl7zkj2fjpvl3rmn7wwazt80letwxlm22hngu8n88g7hsp542qpl')
assert b11['type'] == 'bolt11 invoice'
assert b11['currency'] == 'tb'
assert b11['created_at'] == 1554294928
assert b11['payment_hash'] == '850aeaf5f69670e8889936fc2e0cff3ceb0c3b5eab8f04ae57767118db673a91'
@ -596,7 +598,7 @@ def test_amountless_invoice(node_factory):
assert(len(i) == 1)
assert('amount_received_msat' not in i[0])
assert(i[0]['status'] == 'unpaid')
details = l1.rpc.decodepay(inv)
details = l1.rpc.decode(inv)
assert('msatoshi' not in details)
l1.rpc.pay(inv, amount_msat=1337)
@ -781,7 +783,7 @@ def test_invoice_deschash(node_factory, chainparams):
inv = l2.rpc.invoice(42, 'label', 'One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon', deschashonly=True)
assert '8yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs' in inv['bolt11']
b11 = l2.rpc.decodepay(inv['bolt11'])
b11 = l2.rpc.decode(inv['bolt11'])
assert 'description' not in b11
assert b11['description_hash'] == '3925b6f67e2c340036ed12093dd44e0368df1b6ea26c53dbe4811f58fd5db8c1'
@ -864,7 +866,7 @@ def test_unified_invoices(node_factory, executor, bitcoind):
l1, l2 = node_factory.line_graph(2, opts={'invoices-onchain-fallback': None})
amount_sat = 1000
inv = l1.rpc.invoice(amount_sat * 1000, "inv1", "test_unified_invoices")
b11 = l1.rpc.decodepay(inv['bolt11'])
b11 = l1.rpc.decode(inv['bolt11'])
assert len(b11['fallbacks']) == 1

View File

@ -3733,21 +3733,21 @@ def test_field_filter(node_factory, chainparams):
inv = l1.rpc.invoice(123000, 'label', 'description', 3700, [addr1, addr2])
# Simple case: single field
dec = l1.rpc.call('decodepay', {'bolt11': inv['bolt11']}, filter={"currency": True})
dec = l1.rpc.call('decode', {'string': inv['bolt11']}, filter={"currency": True})
assert dec == {"currency": chainparams['bip173_prefix']}
# Use context manager:
with l1.rpc.reply_filter({"currency": True}):
dec = l1.rpc.decodepay(bolt11=inv['bolt11'])
dec = l1.rpc.decode(string=inv['bolt11'])
assert dec == {"currency": chainparams['bip173_prefix']}
# Two fields
dec = l1.rpc.call('decodepay', {'bolt11': inv['bolt11']}, filter={"currency": True, "payment_hash": True})
dec = l1.rpc.call('decode', {'string': inv['bolt11']}, filter={"currency": True, "payment_hash": True})
assert dec == {"currency": chainparams['bip173_prefix'],
"payment_hash": inv['payment_hash']}
# Nested fields
dec = l1.rpc.call('decodepay', {'bolt11': inv['bolt11']},
dec = l1.rpc.call('decode', {'string': inv['bolt11']},
filter={"currency": True,
"payment_hash": True,
"fallbacks": [{"type": True}]})
@ -3756,12 +3756,12 @@ def test_field_filter(node_factory, chainparams):
"fallbacks": [{"type": 'P2WPKH'}, {"type": 'P2SH'}]}
# Nonexistent fields.
dec = l1.rpc.call('decodepay', {'bolt11': inv['bolt11']},
dec = l1.rpc.call('decode', {'string': inv['bolt11']},
filter={"foobar": True})
assert dec == {}
# Bad filters
dec = l1.rpc.call('decodepay', {'bolt11': inv['bolt11']},
dec = l1.rpc.call('decode', {'string': inv['bolt11']},
filter={"currency": True,
"payment_hash": True,
"fallbacks": {'type': True}})

View File

@ -7,7 +7,6 @@ from utils import (
from pyln.testing.utils import FUNDAMOUNT
from pathlib import Path
from pprint import pprint
import pytest
import re
import unittest
@ -1665,8 +1664,7 @@ def test_zeroconf_open(bitcoind, node_factory):
wait_for(lambda: l3.rpc.listchannels() != {'channels': []})
inv = l3.rpc.invoice(10**8, 'lbl', 'desc')['bolt11']
details = l2.rpc.decodepay(inv)
pprint(details)
details = l2.rpc.decode(inv)
assert('routes' in details and len(details['routes']) == 1)
hop = details['routes'][0][0] # First (and only) hop of hint 0
l2alias = only_one(l2.rpc.listpeerchannels(l3.info['id'])['channels'])['alias']['local']
@ -1679,7 +1677,6 @@ def test_zeroconf_open(bitcoind, node_factory):
l3.daemon.wait_for_log(r'Balance [0-9]+msat -> [0-9]+msat')
# Inverse payments should work too
pprint(l3.rpc.listpeers())
inv = l2.rpc.invoice(10**5, 'lbl', 'desc')['bolt11']
l3.rpc.pay(inv)

View File

@ -182,7 +182,7 @@ def test_pay_exclude_node(node_factory, bitcoind):
# Get a fresh invoice, but do it before other routes exist, so routehint
# will be via l2.
inv = l3.rpc.invoice(amount, "test2", 'description')['bolt11']
assert only_one(l1.rpc.decodepay(inv)['routes'])[0]['pubkey'] == l2.info['id']
assert only_one(l1.rpc.decode(inv)['routes'])[0]['pubkey'] == l2.info['id']
# l1->l4->l5->l3 is the longer route. This makes sure this route won't be
# tried for the first pay attempt. Just to be sure we also raise the fees
@ -809,7 +809,7 @@ def test_sendpay_cant_afford(node_factory, anchors):
l2.pay(l1, available - reserve * 2)
def test_decodepay(node_factory):
def test_decode(node_factory):
l1 = node_factory.get_node()
# BOLT #11:
@ -829,7 +829,7 @@ def test_decodepay(node_factory):
# * `2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq`: 'Please consider supporting this project'
# * `32vjcgqxyuj7nqphl3xmmhls2rkl3t97uan4j0xa87gj5779czc8p0z58zf5wpt9ggem6adl64cvawcxlef9djqwp2jzzfvs272504sp`: signature
# * `0lkg3c`: Bech32 checksum
b11 = l1.rpc.decodepay(
b11 = l1.rpc.decode(
'lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqd'
'pl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rk'
'x3yf5tcsyz3d73gafnh3cax9rn449d9p5uxz9ezhhypd0elx87sjle52x86fux2ypatg'
@ -861,7 +861,7 @@ def test_decodepay(node_factory):
# * `pu`: 60 seconds (`p` = 1, `u` = 28. 1 * 32 + 28 == 60)
# * `azh8qt5w7qeewkmxtv55khqxvdfs9zzradsvj7rcej9knpzdwjykcq8gv4v2dl705pjadhpsc967zhzdpuwn5qzjm0s4hqm2u0vuhhqq`: signature
# * `7vc09u`: Bech32 checksum
b11 = l1.rpc.decodepay(
b11 = l1.rpc.decode(
'lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqf'
'qypqdq5xysxxatsyp3k7enxv4jsxqzpuaztrnwngzn3kdzw5hydlzf03qdgm2hdq27cq'
'v3agm2awhz5se903vruatfhq77w3ls4evs3ch9zw97j25emudupq63nyw24cg27h2rsp'
@ -891,14 +891,11 @@ def test_decodepay(node_factory):
# * `8yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs`: SHA256 of 'One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon'
# * `vjfls3ljx9e93jkw0kw40yxn4pevgzflf83qh2852esjddv4xk4z70nehrdcxa4fk0t6hlcc6vrxywke6njenk7yzkzw0quqcwxphkcp`: signature
# * `vam37w`: Bech32 checksum
b11 = l1.rpc.decodepay(
b11 = l1.rpc.decode(
'lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqy'
'pqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqscc6gd6ql3jr'
'c5yzme8v4ntcewwz5cnw92tz0pc8qcuufvq7khhr8wpald05e92xw006sq94mg8v2ndf'
'4sefvf9sygkshp5zfem29trqq2yxxz7',
'One piece of chocolate cake, one icecream cone, one pickle, one slic'
'e of swiss cheese, one slice of salami, one lollypop, one piece of c'
'herry pie, one sausage, one cupcake, and one slice of watermelon'
'4sefvf9sygkshp5zfem29trqq2yxxz7'
)
assert b11['currency'] == 'bc'
assert b11['amount_msat'] == Millisatoshi(str(20 * 10**11 // 1000) + 'msat')
@ -923,14 +920,8 @@ def test_decodepay(node_factory):
# * `h`: tagged field: hash of description...
# * `qh84fmvn2klvglsjxfy0vq2mz6t9kjfzlxfwgljj35w2kwa60qv49k7jlsgx43yhs9nuutllkhhnt090mmenuhp8ue33pv4klmrzlcqp`: signature
# * `us2s2r`: Bech32 checksum
b11 = l1.rpc.decodepay(
'lntb20m1pvjluezhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahr'
'qspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfpp3x9et2e2'
'0v6pu37c5d9vax37wxq72un98kmzzhznpurw9sgl2v0nklu2g4d0keph5t7tj9tcqd8r'
'exnd07ux4uv2cjvcqwaxgj7v4uwn5wmypjd5n69z2xm3xgksg28nwht7f6zspwp3f9t',
'One piece of chocolate cake, one icecream cone, one pickle, one slic'
'e of swiss cheese, one slice of salami, one lollypop, one piece of c'
'herry pie, one sausage, one cupcake, and one slice of watermelon'
b11 = l1.rpc.decode(
'lntb20m1pvjluezhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfpp3x9et2e20v6pu37c5d9vax37wxq72un98kmzzhznpurw9sgl2v0nklu2g4d0keph5t7tj9tcqd8rexnd07ux4uv2cjvcqwaxgj7v4uwn5wmypjd5n69z2xm3xgksg28nwht7f6zspwp3f9t'
)
assert b11['currency'] == 'tb'
assert b11['amount_msat'] == Millisatoshi(20 * 10**11 // 1000)
@ -962,7 +953,7 @@ def test_decodepay(node_factory):
# `q20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqqqqqqq9qqqvpeuqafqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqqqqqqq7qqzq`: pubkey `029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255`, `short_channel_id` 0102030405060708, `fee_base_msat` 1 millisatoshi, `fee_proportional_millionths` 20, `cltv_expiry_delta` 3. pubkey `039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255`, `short_channel_id` 030405060708090a, `fee_base_msat` 2 millisatoshi, `fee_proportional_millionths` 30, `cltv_expiry_delta` 4.
# * `j9n4evl6mr5aj9f58zp6fyjzup6ywn3x6sk8akg5v4tgn2q8g4fhx05wf6juaxu9760yp46454gpg5mtzgerlzezqcqvjnhjh8z3g2qq`: signature
# * `dhhwkj`: Bech32 checksum
b11 = l1.rpc.decodepay('lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfpp3qjmp7lwpagxun9pygexvgpjdc4jdj85fr9yq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqafqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzqj9n4evl6mr5aj9f58zp6fyjzup6ywn3x6sk8akg5v4tgn2q8g4fhx05wf6juaxu9760yp46454gpg5mtzgerlzezqcqvjnhjh8z3g2qqdhhwkj', 'One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon')
b11 = l1.rpc.decode('lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfpp3qjmp7lwpagxun9pygexvgpjdc4jdj85fr9yq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqafqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzqj9n4evl6mr5aj9f58zp6fyjzup6ywn3x6sk8akg5v4tgn2q8g4fhx05wf6juaxu9760yp46454gpg5mtzgerlzezqcqvjnhjh8z3g2qqdhhwkj')
assert b11['currency'] == 'bc'
assert b11['amount_msat'] == Millisatoshi(20 * 10**11 // 1000)
assert b11['created_at'] == 1496314658
@ -1004,7 +995,7 @@ def test_decodepay(node_factory):
# * `h`: tagged field: hash of description...
# * `2jhz8j78lv2jynuzmz6g8ve53he7pheeype33zlja5azae957585uu7x59w0f2l3rugyva6zpu394y4rh093j6wxze0ldsvk757a9msq`: signature
# * `mf9swh`: Bech32 checksum
b11 = l1.rpc.decodepay('lnbc20m1pvjluezhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfppj3a24vwu6r8ejrss3axul8rxldph2q7z9kmrgvr7xlaqm47apw3d48zm203kzcq357a4ls9al2ea73r8jcceyjtya6fu5wzzpe50zrge6ulk4nvjcpxlekvmxl6qcs9j3tz0469gq5g658y', 'One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon')
b11 = l1.rpc.decode('lnbc20m1pvjluezhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfppj3a24vwu6r8ejrss3axul8rxldph2q7z9kmrgvr7xlaqm47apw3d48zm203kzcq357a4ls9al2ea73r8jcceyjtya6fu5wzzpe50zrge6ulk4nvjcpxlekvmxl6qcs9j3tz0469gq5g658y')
assert b11['currency'] == 'bc'
assert b11['amount_msat'] == Millisatoshi(20 * 10**11 // 1000)
assert b11['created_at'] == 1496314658
@ -1030,7 +1021,7 @@ def test_decodepay(node_factory):
# * `h`: tagged field: hash of description...
# * `gw6tk8z0p0qdy9ulggx65lvfsg3nxxhqjxuf2fvmkhl9f4jc74gy44d5ua9us509prqz3e7vjxrftn3jnk7nrglvahxf7arye5llphgq`: signature
# * `qdtpa4`: Bech32 checksum
b11 = l1.rpc.decodepay('lnbc20m1pvjluezhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfppqw508d6qejxtdg4y5r3zarvary0c5xw7kepvrhrm9s57hejg0p662ur5j5cr03890fa7k2pypgttmh4897d3raaq85a293e9jpuqwl0rnfuwzam7yr8e690nd2ypcq9hlkdwdvycqa0qza8', 'One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon')
b11 = l1.rpc.decode('lnbc20m1pvjluezhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfppqw508d6qejxtdg4y5r3zarvary0c5xw7kepvrhrm9s57hejg0p662ur5j5cr03890fa7k2pypgttmh4897d3raaq85a293e9jpuqwl0rnfuwzam7yr8e690nd2ypcq9hlkdwdvycqa0qza8')
assert b11['currency'] == 'bc'
assert b11['amount_msat'] == Millisatoshi(20 * 10**11 // 1000)
assert b11['created_at'] == 1496314658
@ -1056,7 +1047,7 @@ def test_decodepay(node_factory):
# * `h`: tagged field: hash of description...
# * `5yps56lmsvgcrf476flet6js02m93kgasews8q3jhtp7d6cqckmh70650maq4u65tk53ypszy77v9ng9h2z3q3eqhtc3ewgmmv2grasp`: signature
# * `akvd7y`: Bech32 checksum
b11 = l1.rpc.decodepay('lnbc20m1pvjluezhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfp4qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q28j0v3rwgy9pvjnd48ee2pl8xrpxysd5g44td63g6xcjcu003j3qe8878hluqlvl3km8rm92f5stamd3jw763n3hck0ct7p8wwj463cql26ava', 'One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon')
b11 = l1.rpc.decode('lnbc20m1pvjluezhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfp4qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q28j0v3rwgy9pvjnd48ee2pl8xrpxysd5g44td63g6xcjcu003j3qe8878hluqlvl3km8rm92f5stamd3jw763n3hck0ct7p8wwj463cql26ava')
assert b11['currency'] == 'bc'
assert b11['amount_msat'] == Millisatoshi(20 * 10**11 // 1000)
assert b11['created_at'] == 1496314658
@ -1085,7 +1076,7 @@ def test_decodepay(node_factory):
# * `sz`: b1000000010
# * `e992adudgku8p05pstl6zh7av6rx2f297pv89gu5q93a0hf3g7lynl3xq56t23dpvah6u7y9qey9lccrdml3gaqwc6nxsl5ktzm464sq`: signature
# * `73t7cl`: Bech32 checksum
b11 = l1.rpc.decodepay('lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdees9qzsze992adudgku8p05pstl6zh7av6rx2f297pv89gu5q93a0hf3g7lynl3xq56t23dpvah6u7y9qey9lccrdml3gaqwc6nxsl5ktzm464sq73t7cl')
b11 = l1.rpc.decode('lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdees9qzsze992adudgku8p05pstl6zh7av6rx2f297pv89gu5q93a0hf3g7lynl3xq56t23dpvah6u7y9qey9lccrdml3gaqwc6nxsl5ktzm464sq73t7cl')
assert b11['currency'] == 'bc'
assert b11['amount_msat'] == Millisatoshi(25 * 10**11 // 1000)
assert b11['created_at'] == 1496314658
@ -1114,7 +1105,7 @@ def test_decodepay(node_factory):
# * `k3ed62snp73037h4py4gry05eltlp0uezm2w9ajnerhmxzhzhsu40g9mgyx5v3ad4aqwkmvyftzk4k9zenz90mhjcy9hcevc7r3lx2sp`: signature
# * `hzfxz7`: Bech32 checksum
with pytest.raises(RpcError, match='unknown feature.*100'):
l1.rpc.decodepay('lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdees9q4pqqqqqqqqqqqqqqqqqqszk3ed62snp73037h4py4gry05eltlp0uezm2w9ajnerhmxzhzhsu40g9mgyx5v3ad4aqwkmvyftzk4k9zenz90mhjcy9hcevc7r3lx2sphzfxz7')
l1.rpc.decode('lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdees9q4pqqqqqqqqqqqqqqqqqqszk3ed62snp73037h4py4gry05eltlp0uezm2w9ajnerhmxzhzhsu40g9mgyx5v3ad4aqwkmvyftzk4k9zenz90mhjcy9hcevc7r3lx2sphzfxz7')
# Example of an invoice without a multiplier suffix to the amount. This
# should then be interpreted as 7 BTC according to the spec:
@ -1124,12 +1115,12 @@ def test_decodepay(node_factory):
# a payment unit -- in the case of Bitcoin the unit is 'bitcoin' NOT
# satoshis.
b11 = "lnbcrt71p0g4u8upp5xn4k45tsp05akmn65s5k2063d5fyadhjse9770xz5sk7u4x6vcmqdqqcqzynxqrrssx94cf4p727jamncsvcd8m99n88k423ruzq4dxwevfatpp5gx2mksj2swshjlx4pe3j5w9yed5xjktrktzd3nc2a04kq8yu84l7twhwgpxjn3pw"
b11 = l1.rpc.decodepay(b11)
b11 = l1.rpc.decode(b11)
sat_per_btc = 10**8
assert(b11['amount_msat'] == 7 * sat_per_btc * 1000)
with pytest.raises(RpcError):
l1.rpc.decodepay('1111111')
l1.rpc.decode('1111111')
def test_forward(node_factory, bitcoind):
@ -1773,17 +1764,17 @@ def test_pay_variants(node_factory):
# Upper case is allowed
b11 = l2.rpc.invoice(123000, 'test_pay_variants upper', 'description')['bolt11'].upper()
l1.rpc.decodepay(b11)
l1.rpc.decode(b11)
l1.rpc.pay(b11)
# lightning: prefix is allowed
b11 = 'lightning:' + l2.rpc.invoice(123000, 'test_pay_variants with prefix', 'description')['bolt11']
l1.rpc.decodepay(b11)
l1.rpc.decode(b11)
l1.rpc.pay(b11)
# BOTH is allowed.
b11 = 'LIGHTNING:' + l2.rpc.invoice(123000, 'test_pay_variants upper with prefix', 'description')['bolt11'].upper()
l1.rpc.decodepay(b11)
l1.rpc.decode(b11)
l1.rpc.pay(b11)
@ -1979,7 +1970,7 @@ def test_pay_routeboost(node_factory, bitcoind):
exposeprivatechannels=True)
assert 'warning_capacity' not in inv
assert 'warning_offline' not in inv
assert only_one(only_one(l1.rpc.decodepay(inv['bolt11'])['routes']))
assert only_one(only_one(l1.rpc.decode(inv['bolt11'])['routes']))
# Now we should be able to pay it.
l1.dev_pay(inv['bolt11'], dev_use_shadow=False)
@ -2390,7 +2381,7 @@ def test_setchannel_routing(node_factory, bitcoind):
# do and check actual payment
inv = l3.rpc.invoice(4000000, 'test_setchannel_2', 'desc')
# Check that routehint from l3 incorporated new feerate!
decoded = l1.rpc.decodepay(inv['bolt11'])
decoded = l1.rpc.decode(inv['bolt11'])
assert decoded['routes'] == [[{'pubkey': l2.info['id'], 'short_channel_id': scid, 'fee_base_msat': 1337, 'fee_proportional_millionths': 137, 'cltv_expiry_delta': 6}]]
# This will fail.
@ -3066,7 +3057,7 @@ def test_partial_payment(node_factory, bitcoind, executor):
wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 8)
inv = l4.rpc.invoice(1000, 'inv', 'inv')
paysecret = l4.rpc.decodepay(inv['bolt11'])['payment_secret']
paysecret = l4.rpc.decode(inv['bolt11'])['payment_secret']
# Separate routes for each part of the payment.
r134 = l1.rpc.getroute(l4.info['id'], 501, 1, exclude=[scid24 + '/0', scid24 + '/1'])['route']
@ -3216,7 +3207,7 @@ def test_partial_payment_timeout(node_factory, bitcoind):
l1, l2 = node_factory.line_graph(2)
inv = l2.rpc.invoice(1000, 'inv', 'inv')
paysecret = l2.rpc.decodepay(inv['bolt11'])['payment_secret']
paysecret = l2.rpc.decode(inv['bolt11'])['payment_secret']
route = l1.rpc.getroute(l2.info['id'], 500, 1)['route']
l1.rpc.sendpay(
@ -3270,7 +3261,7 @@ def test_partial_payment_restart(node_factory, bitcoind):
+ [{'may_reconnect': True}] * 2)
inv = l3.rpc.invoice(1000, 'inv', 'inv')
paysecret = l3.rpc.decodepay(inv['bolt11'])['payment_secret']
paysecret = l3.rpc.decode(inv['bolt11'])['payment_secret']
route = l1.rpc.getroute(l3.info['id'], 500, 1)['route']
@ -3315,7 +3306,7 @@ def test_partial_payment_htlc_loss(node_factory, bitcoind):
{}])
inv = l3.rpc.invoice(1000, 'inv', 'inv')
paysecret = l3.rpc.decodepay(inv['bolt11'])['payment_secret']
paysecret = l3.rpc.decode(inv['bolt11'])['payment_secret']
route = l1.rpc.getroute(l3.info['id'], 500, 1)['route']
@ -4043,7 +4034,7 @@ def test_mpp_waitblockheight_routehint_conflict(node_factory, bitcoind, executor
sync_blockheight(bitcoind, [l3])
inv = l3.rpc.invoice(Millisatoshi(2 * 10000 * 1000), 'i', 'i', exposeprivatechannels=True)['bolt11']
assert 'routes' in l3.rpc.decodepay(inv)
assert 'routes' in l3.rpc.decode(inv)
# Have l1 pay l3
def pay(l1, inv):
@ -4167,7 +4158,7 @@ def test_mpp_interference_2(node_factory, bitcoind, executor):
# Buyers check out some purchaseable stuff from the merchant.
i2 = l1.rpc.invoice(unit * 6, ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(20)), 'i2')['bolt11']
i3 = l1.rpc.invoice(unit * 6, ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(20)), 'i3')['bolt11']
if len(l1.rpc.decodepay(i2)['routes'] + l1.rpc.decodepay(i3)['routes']) <= 4:
if len(l1.rpc.decode(i2)['routes'] + l1.rpc.decode(i3)['routes']) <= 4:
break
# Pay simultaneously!
@ -4960,7 +4951,7 @@ def test_unreachable_routehint(node_factory, bitcoind):
# Generate an invoice with exactly one routehint.
for i in range(100):
invoice = l5.rpc.invoice(10, 'attempt{}'.format(i), 'description')['bolt11']
decoded = l1.rpc.decodepay(invoice)
decoded = l1.rpc.decode(invoice)
if 'routes' in decoded and len(decoded['routes']) == 1:
break
@ -5014,7 +5005,7 @@ gives a routehint straight to us causes an issue
l3.wait_channel_active(scid12)
inv = l3.rpc.invoice(10, "test", "test")['bolt11']
decoded = l3.rpc.decodepay(inv)
decoded = l3.rpc.decode(inv)
assert(only_one(only_one(decoded['routes']))['short_channel_id']
== only_one(l3.rpc.listpeerchannels()['channels'])['alias']['remote'])
@ -5382,7 +5373,7 @@ def test_fetchinvoice_with_no_quantity(node_factory):
def test_invoice_pay_desc_with_quotes(node_factory):
"""Test that we can decode and pay invoice where hashed description contains double quotes"""
l1, l2 = node_factory.line_graph(2)
l1, l2 = node_factory.line_graph(2, opts={'allow-deprecated-apis': True})
description = '[["text/plain","Funding @odell on stacker.news"],["text/identifier","odell@stacker.news"]]'
invoice = l2.rpc.invoice(label="test12345", amount_msat=1000,
@ -5603,7 +5594,7 @@ def test_pay_routehint_minhtlc(node_factory, bitcoind):
wait_for(lambda: l1.rpc.listchannels(scid)['channels'][0]['htlc_minimum_msat'] == Millisatoshi(1000))
inv = l4.rpc.invoice(100000, "inv", "inv")
assert only_one(l1.rpc.decodepay(inv['bolt11'])['routes'])
assert only_one(l1.rpc.decode(inv['bolt11'])['routes'])
# You should be able to pay the invoice!
l1.rpc.pay(inv['bolt11'])
@ -5969,6 +5960,45 @@ def test_fetch_no_description_with_amount(node_factory):
_ = l2.rpc.call('offer', {'amount': '2msat'})
def test_decodepay(node_factory, chainparams):
"""Test we don't break (deprecated) decodepay command"""
l1 = node_factory.get_node(options={'allow-deprecated-apis': True})
addr1 = l1.rpc.newaddr('bech32')['bech32']
addr2 = '2MxqzNANJNAdMjHQq8ZLkwzooxAFiRzXvEz' if not chainparams['elements'] else 'XGx1E2JSTLZLmqYMAo3CGpsco85aS7so33'
before = int(time.time())
inv = l1.rpc.invoice(123000, 'label', 'description', 3700, [addr1, addr2])
after = int(time.time())
b11 = l1.rpc.decodepay(inv['bolt11'])
# This can vary within a range.
created = b11['created_at']
assert created >= before
assert created <= after
# Don't bother checking these
del b11['fallbacks'][0]['hex']
del b11['fallbacks'][1]['hex']
del b11['payment_secret']
del b11['signature']
assert b11 == {
'amount_msat': 123000,
'currency': chainparams['bip173_prefix'],
'created_at': created,
'payment_hash': inv['payment_hash'],
'description': 'description',
'expiry': 3700,
'payee': l1.info['id'],
'fallbacks': [{'addr': addr1,
'type': 'P2WPKH'},
{'addr': addr2,
'type': 'P2SH'}],
'features': '02024100',
'min_final_cltv_expiry': 5}
def test_enableoffer(node_factory):
l1, l2 = node_factory.line_graph(2, opts={'experimental-offers': None})

View File

@ -1540,7 +1540,7 @@ def test_rpc_command_hook(node_factory):
# Both plugins will replace calls made for the "invoice" command
# The first will win, for the second a warning should be logged
invoice = l1.rpc.invoice(10**6, "test_side", "test_input")
decoded = l1.rpc.decodepay(invoice["bolt11"])
decoded = l1.rpc.decode(invoice["bolt11"])
assert decoded["description"] == "rpc_command_1 modified this description"
l1.daemon.wait_for_log("rpc_command hook 'invoice' already modified, ignoring.")