diff --git a/plugins/xpay/xpay.c b/plugins/xpay/xpay.c index efe84608a..d4a45ee49 100644 --- a/plugins/xpay/xpay.c +++ b/plugins/xpay/xpay.c @@ -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); diff --git a/tests/test_askrene.py b/tests/test_askrene.py index 8f174c057..2607beb6f 100644 --- a/tests/test_askrene.py +++ b/tests/test_askrene.py @@ -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"): diff --git a/tests/test_misc.py b/tests/test_misc.py index 358f68cd9..280c7207c 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -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 diff --git a/tests/test_opening.py b/tests/test_opening.py index 88c991ac3..af9920404 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -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'] diff --git a/tests/test_xpay.py b/tests/test_xpay.py index a851cd977..afe1ff5f0 100644 --- a/tests/test_xpay.py +++ b/tests/test_xpay.py @@ -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):