xpay: make the xpay layer persistent.

As the first user of a persistent layer, this tripped tests which
assumed the datastore would be empty!

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2024-11-17 16:21:06 +10:30
parent c93153ec37
commit 229fc3f2b4
5 changed files with 57 additions and 8 deletions

View File

@ -1582,6 +1582,7 @@ static const char *init(struct command *init_cmd,
plugin_broken_cb,
"askrene-create-layer");
json_add_string(req->js, "layer", "xpay");
json_add_bool(req->js, "persistent", true);
send_outreq(req);
start_aging_timer(plugin);

View File

@ -294,7 +294,8 @@ def test_layers(node_factory):
def test_layer_persistence(node_factory):
"""Test persistence of layers across restart"""
l1, l2 = node_factory.line_graph(2, wait_for_announce=True)
l1, l2 = node_factory.line_graph(2, wait_for_announce=True,
opts={'disable-plugin': 'cln-xpay'})
assert l1.rpc.askrene_listlayers() == {'layers': []}
with pytest.raises(RpcError, match="Unknown layer"):

View File

@ -3509,7 +3509,8 @@ def test_datastore_escapeing(node_factory):
def test_datastore(node_factory):
l1 = node_factory.get_node()
# Suppress xpay, which makes a layer
l1 = node_factory.get_node(options={"disable-plugin": "cln-xpay"})
# Starts empty
assert l1.rpc.listdatastore() == {'datastore': []}
@ -3623,7 +3624,8 @@ def test_datastore(node_factory):
def test_datastore_keylist(node_factory):
l1 = node_factory.get_node()
# Suppress xpay, which makes a layer
l1 = node_factory.get_node(options={"disable-plugin": "cln-xpay"})
# Starts empty
assert l1.rpc.listdatastore() == {'datastore': []}
@ -3685,7 +3687,7 @@ def test_datastore_keylist(node_factory):
def test_datastoreusage(node_factory):
l1: LightningNode = node_factory.get_node()
l1: LightningNode = node_factory.get_node(options={"disable-plugin": "cln-xpay"})
assert l1.rpc.datastoreusage() == {'datastoreusage': {'key': '[]', 'total_bytes': 0}}
data = 'somedatatostoreinthedatastore' # len 29

View File

@ -576,6 +576,10 @@ def test_v2_rbf_liquidity_ad(node_factory, bitcoind, chainparams):
l1, l2 = node_factory.get_nodes(2, opts=opts)
# Other plugins use datastore, but we want to make sure our own
# data is cleared!
empty_datastore = l1.rpc.listdatastore()
# what happens when we RBF?
feerate = 2000
amount = 500000
@ -632,7 +636,7 @@ def test_v2_rbf_liquidity_ad(node_factory, bitcoind, chainparams):
l1.rpc.openchannel_signed(chan_id, signed_psbt)
# There's data in the datastore now (l2 only)
assert l1.rpc.listdatastore() == {'datastore': []}
assert l1.rpc.listdatastore() == empty_datastore
only_one(l2.rpc.listdatastore("funder/{}".format(chan_id))['datastore'])
# what happens when the channel opens?
@ -640,8 +644,8 @@ def test_v2_rbf_liquidity_ad(node_factory, bitcoind, chainparams):
l1.daemon.wait_for_log('to CHANNELD_NORMAL')
# Datastore should be cleaned up!
assert l1.rpc.listdatastore() == {'datastore': []}
wait_for(lambda: l2.rpc.listdatastore() == {'datastore': []})
assert l1.rpc.listdatastore() == empty_datastore
wait_for(lambda: l2.rpc.listdatastore() == empty_datastore)
# This should be the accepter's amount
fundings = only_one(l1.rpc.listpeerchannels()['channels'])['funding']

View File

@ -229,6 +229,7 @@ def test_xpay_fake_channeld(node_factory, bitcoind, chainparams):
shaseed = subprocess.check_output(["tools/hsmtool", "dumpcommitments", l1.info['id'], "1", "0", hsmfile]).decode('utf-8').strip().partition(": ")[2]
l1.rpc.dev_peer_shachain(l2.info['id'], shaseed)
failed_parts = []
for n in range(0, 100):
if n in (62, 76, 80, 97):
continue
@ -246,7 +247,47 @@ def test_xpay_fake_channeld(node_factory, bitcoind, chainparams):
f"d=Paying node {n}",
f"amount={AMOUNT}msat"]).decode('utf-8').strip()
assert l1.rpc.decode(inv)['payee'] == nodeids[n]
l1.rpc.xpay(inv)
failed_parts.append(l1.rpc.xpay(inv)['failed_parts'])
# Should be no reservations left (clean up happens after return though)
wait_for(lambda: l1.rpc.askrene_listreservations() == {'reservations': []})
# It should remember the information it learned across restarts!
# FIXME: channeld_fakenet doesn't restart properly, so just redo xpay.
layers = l1.rpc.askrene_listlayers()
# Temporary layers should be gone.
assert len(layers['layers']) == 1
l1.rpc.plugin_stop("cln-askrene")
l1.rpc.plugin_start(os.path.join(os.getcwd(), 'plugins/cln-askrene'))
layers_after = l1.rpc.askrene_listlayers()
assert layers == layers_after
failed_parts_retry = []
for n in range(0, 100):
if n in (62, 76, 80, 97):
continue
print(f"PAYING Node #{n}")
preimage_hex = bytes([n + 100]).hex() + '00' * 31
hash_hex = sha256(bytes.fromhex(preimage_hex)).hexdigest()
inv = subprocess.check_output(["devtools/bolt11-cli",
"encode",
n.to_bytes(length=8, byteorder=sys.byteorder).hex() + '01' * 24,
f"p={hash_hex}",
f"s={'00' * 32}",
f"d=Paying node {n}",
f"amount={AMOUNT}msat"]).decode('utf-8').strip()
assert l1.rpc.decode(inv)['payee'] == nodeids[n]
failed_parts_retry.append(l1.rpc.xpay(inv)['failed_parts'])
# At least some will have improved!
assert failed_parts_retry != failed_parts
# Now, we should be as good *or better* than the first time, since we remembered!
for p in zip(failed_parts_retry, failed_parts):
assert p[0] <= p[1]
def test_xpay_timeout(node_factory, executor):