Skip to content

Commit

Permalink
small test fixes and new test for force close
Browse files Browse the repository at this point in the history
  • Loading branch information
daywalker90 committed Oct 6, 2023
1 parent 0afded2 commit fe8fdf5
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 17 deletions.
130 changes: 124 additions & 6 deletions plugins/holdinvoice/tests/holdinvoicetest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#!/usr/bin/python

from pyln.testing.fixtures import *
from pyln.testing.utils import only_one, mine_funding_to_announce
from pyln.testing.utils import only_one, wait_for
from pyln.client import Millisatoshi
import secrets
import threading
import time
Expand All @@ -15,7 +16,7 @@ def test_inputs(node_factory):
node = node_factory.get_node(
options={
'important-plugin': os.path.join(
os.getcwd(), '../../target/release/holdinvoice'
os.getcwd(), 'target/release/holdinvoice'
)
}
)
Expand Down Expand Up @@ -156,7 +157,7 @@ def test_valid_hold_then_settle(node_factory, bitcoind):
opts={
'important-plugin': os.path.join(
os.getcwd(),
'../../target/release/holdinvoice'
'target/release/holdinvoice'
)
}
)
Expand Down Expand Up @@ -194,7 +195,7 @@ def test_valid_hold_then_settle(node_factory, bitcoind):
assert result_settle["message"] == expected_message

threading.Thread(target=pay_with_thread, args=(
l1.rpc, invoice["bolt11"])).start()
l1, invoice["bolt11"])).start()

timeout = 10
start_time = time.time()
Expand Down Expand Up @@ -247,12 +248,129 @@ def test_valid_hold_then_settle(node_factory, bitcoind):
assert result_cancel_settled["message"] == expected_message


def test_fc_hold_then_settle(node_factory, bitcoind):
l1, l2 = node_factory.get_nodes(2,
opts={
'important-plugin': os.path.join(
os.getcwd(),
'target/release/holdinvoice'
)
}
)
l1.rpc.connect(l2.info['id'], 'localhost', l2.port)
cl1, _ = l1.fundchannel(l2, 1_000_000)

l1.wait_channel_active(cl1)

fundsres = l2.rpc.call("listfunds")["outputs"]
total_funds = 0
for utxo in fundsres:
total_funds += utxo["amount_msat"]
assert total_funds == Millisatoshi(0)

invoice_amt = 10_000_000

invoice = l2.rpc.call("holdinvoice", {
"amount_msat": invoice_amt,
"description": "test_valid_hold_then_settle",
"label": generate_random_label(),
"cltv": 50}
)
assert invoice is not None
assert isinstance(invoice, dict) is True
assert "payment_hash" in invoice

threading.Thread(target=pay_with_thread, args=(
l1, invoice["bolt11"])).start()

timeout = 10
start_time = time.time()

while time.time() - start_time < timeout:
result_lookup = l2.rpc.call("holdinvoicelookup", {
"payment_hash": invoice["payment_hash"]})
assert result_lookup is not None
assert isinstance(result_lookup, dict) is True

if result_lookup["state"] == "accepted":
break
else:
time.sleep(1)

assert result_lookup["state"] == "accepted"
assert "htlc_expiry" in result_lookup

# test that it's actually holding the htlcs
# and not letting them through
doublecheck = only_one(l2.rpc.call("listinvoices", {
"payment_hash": invoice["payment_hash"]})["invoices"])
assert doublecheck["status"] == "unpaid"

fc = l1.rpc.close(cl1, 1)
bitcoind.generate_block(1, wait_for_mempool=fc['txid'])
wait_for(lambda: l1.rpc.listpeerchannels(l2.info['id'])[
'channels'][0]["state"] == 'ONCHAIN')
wait_for(lambda: l2.rpc.listpeerchannels(l1.info['id'])[
'channels'][0]["state"] == 'ONCHAIN')
assert l2.channel_state(l1) == 'ONCHAIN'

result_settle = l2.rpc.call("holdinvoicesettle", {
"payment_hash": invoice["payment_hash"]})
assert result_settle is not None
assert isinstance(result_settle, dict) is True
assert result_settle["state"] == "settled"

result_lookup = l2.rpc.call("holdinvoicelookup", {
"payment_hash": invoice["payment_hash"]})
assert result_lookup is not None
assert isinstance(result_lookup, dict) is True
assert result_lookup["state"] == "settled"
assert "htlc_expiry" not in result_lookup

# ask cln if the invoice is actually paid
# should not be necessary because lookup does this aswell
doublecheck = only_one(l2.rpc.call("listinvoices", {
"payment_hash": invoice["payment_hash"]})["invoices"])
assert doublecheck["status"] == "paid"

# payres = only_one(l1.rpc.call(
# "listpays", {"payment_hash": invoice["payment_hash"]})["pays"])
# assert payres["status"] == "complete"

fundsres = l2.rpc.call("listfunds")["outputs"]
total_funds = 0
for utxo in fundsres:
total_funds += utxo["amount_msat"]
assert total_funds == Millisatoshi(0)

for _ in range(15):
bitcoind.generate_block(5)
time.sleep(0.2)

wait_for(lambda: any('ONCHAIN:All outputs resolved' in status_str
for status_str in l1.rpc.listpeerchannels(
l2.info['id'])['channels'][0]["status"]))
wait_for(lambda: any('ONCHAIN:All outputs resolved' in status_str
for status_str in l2.rpc.listpeerchannels(
l1.info['id'])['channels'][0]["status"]))

payres = only_one(l1.rpc.call(
"listpays", {"payment_hash": invoice["payment_hash"]})["pays"])
assert payres["status"] == "complete"

fundsres = l2.rpc.call("listfunds")["outputs"]
total_funds = 0
for utxo in fundsres:
total_funds += utxo["amount_msat"]
assert total_funds > Millisatoshi(0)


def test_valid_hold_then_cancel(node_factory, bitcoind):
l1, l2 = node_factory.get_nodes(2,
opts={
'important-plugin': os.path.join(
os.getcwd(),
'../../target/release/holdinvoice'
'target/release/holdinvoice'
)
}
)
Expand Down Expand Up @@ -282,7 +400,7 @@ def test_valid_hold_then_cancel(node_factory, bitcoind):
assert "htlc_expiry" not in result_lookup

threading.Thread(target=pay_with_thread, args=(
l1.rpc, invoice["bolt11"])).start()
l1, invoice["bolt11"])).start()

timeout = 10
start_time = time.time()
Expand Down
24 changes: 15 additions & 9 deletions plugins/holdinvoice/tests/stresstest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/python

from pyln.testing.fixtures import *
from pyln.testing.utils import only_one, mine_funding_to_announce
from pyln.testing.utils import wait_for, mine_funding_to_announce
import time
import threading
import os
Expand All @@ -10,9 +10,9 @@


# number of invoices to create, pay, hold and then cancel
num_iterations = 5
num_iterations = 100
# seconds to hold the invoices with inflight htlcs
delay_seconds = 12
delay_seconds = 120
# amount to be used in msat
amount_msat = 1_000_100_000

Expand All @@ -38,16 +38,14 @@ def test_stress(node_factory, bitcoind):
opts={
'important-plugin': os.path.join(
os.getcwd(),
'../../target/release/holdinvoice'
'target/release/holdinvoice'
)
}
)
LOGGER.info("holdinvoice: Nodes created")
l1.fundwallet((amount_msat/1000)*num_iterations*20)
LOGGER.info("holdinvoice: Funding secured")
l1.rpc.connect(l2.info['id'], 'localhost', l2.port)
LOGGER.info("holdinvoice: Nodes connected")
for _ in range(int(num_iterations/10)+1):
for _ in range(int(num_iterations/7)+1):
for _ in range(10):
res = l1.rpc.fundchannel(l2.info['id'], int(
(amount_msat*0.95)/1000), minconf=0)
Expand All @@ -63,6 +61,10 @@ def test_stress(node_factory, bitcoind):
LOGGER.info("holdinvoice: Funded 10 channels")

l1.wait_channel_active(scid)
wait_for(lambda: all(channel['state'] == 'CHANNELD_NORMAL'
for channel in
l1.rpc.listpeerchannels(l2.info['id'])['channels']))

payment_hashes = []

LOGGER.info(
Expand All @@ -83,14 +85,15 @@ def test_stress(node_factory, bitcoind):

# Pay the invoice using a separate thread
threading.Thread(target=pay_with_thread, args=(
l1.rpc, invoice["bolt11"])).start()
l1, invoice["bolt11"])).start()
time.sleep(1)
except Exception as e:
LOGGER.error("holdinvoice: Error executing command:", e)

LOGGER.info(f"holdinvoice: Done paying {num_iterations} invoices!")
# wait a little more for payments to arrive
time.sleep(10)
wait_for(lambda: lookup_stats(l2.rpc, payment_hashes)
["accepted"] == num_iterations)

stats = lookup_stats(l2.rpc, payment_hashes)
LOGGER.info(stats)
Expand All @@ -114,6 +117,9 @@ def test_stress(node_factory, bitcoind):
f"holdinvoice: holdinvoice:Error cancelling "
f"payment hash {payment_hash}:", e)

wait_for(lambda: lookup_stats(l2.rpc, payment_hashes)
["canceled"] == num_iterations)

stats = lookup_stats(l2.rpc, payment_hashes)
LOGGER.info(stats)
assert stats["canceled"] == num_iterations
6 changes: 4 additions & 2 deletions plugins/holdinvoice/tests/util.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import string
import random
import logging


def generate_random_label():
Expand All @@ -14,8 +15,9 @@ def generate_random_number():


def pay_with_thread(rpc, bolt11):
LOGGER = logging.getLogger(__name__)
try:
rpc.pay(bolt11)
rpc.dev_pay(bolt11, dev_use_shadow=False)
except Exception as e:
print(f"holdinvoice: Error paying payment hash:{e}")
LOGGER.debug(f"holdinvoice: Error paying payment hash:{e}")
pass

0 comments on commit fe8fdf5

Please sign in to comment.