Skip to content

Commit 5f6086b

Browse files
committed
test: ensure ln channels and payments with rpc commands
1 parent 63f2274 commit 5f6086b

File tree

4 files changed

+125
-21
lines changed

4 files changed

+125
-21
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ jobs:
4444
- dag_connection_test.py
4545
- graph_test.py
4646
- logging_test.py
47+
- ln_basic_test.py
4748
- rpc_test.py
4849
- services_test.py
4950
- signet_test.py

src/warnet/ln.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import json
22

33
import click
4-
5-
from .k8s import get_pod
4+
from typing import Optional
5+
from .k8s import get_pod, get_default_namespace_or
66
from .process import run_command
77

88

@@ -13,24 +13,26 @@ def ln():
1313

1414
@ln.command(context_settings={"ignore_unknown_options": True})
1515
@click.argument("pod", type=str)
16-
@click.argument("command", type=str, required=True)
17-
def rpc(pod: str, command: str):
16+
@click.argument("method", type=str)
17+
@click.argument("params", type=str, nargs=-1) # this will capture all remaining arguments
18+
@click.option("--namespace", default=None, show_default=True)
19+
def rpc(pod: str, method: str, params: str, namespace: Optional[str]):
1820
"""
1921
Call lightning cli rpc <command> on <ln pod name>
2022
"""
21-
print(_rpc(pod, command))
23+
print(_rpc(pod, method, params, namespace))
2224

2325

24-
def _rpc(pod_name: str, command: str):
26+
def _rpc(pod_name: str, method: str, params: str = "", namespace: Optional[str] = None):
2527
# TODO: when we add back cln we'll need to describe the pod,
2628
# get a label with implementation type and then adjust command
2729
pod = get_pod(pod_name)
30+
namespace = get_default_namespace_or(namespace)
2831
chain = pod.metadata.labels["chain"]
29-
cmd = f"kubectl exec {pod_name} -- lncli --network {chain} {command}"
32+
cmd = f"kubectl -n {namespace} exec {pod_name} -- lncli --network {chain} {method} {' '.join(map(str, params))}"
3033
return run_command(cmd)
3134

32-
33-
@ln.command(context_settings={"ignore_unknown_options": True})
35+
@ln.command()
3436
@click.argument("pod", type=str)
3537
def pubkey(
3638
pod: str,
@@ -41,3 +43,17 @@ def pubkey(
4143
# TODO: again here, cln will need a different command
4244
info = _rpc(pod, "getinfo")
4345
print(json.loads(info)["identity_pubkey"])
46+
47+
@ln.command()
48+
@click.argument("pod", type=str)
49+
def host(
50+
pod: str,
51+
):
52+
"""
53+
Get lightning node host from <ln pod name>
54+
"""
55+
# TODO: again here, cln will need a different command
56+
info = _rpc(pod, "getinfo")
57+
uris = json.loads(info)["uris"]
58+
if uris and len(uris) >= 0:
59+
print(uris[0].split("@")[1])

test/data/ln/network.yaml

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,13 @@
11
nodes:
22
- name: tank-0000
3-
connect:
3+
addnode:
44
- tank-0001
55
lnd: true
66
- name: tank-0001
7-
connect:
7+
addnode:
88
- tank-0002
99
lnd: true
1010
- name: tank-0002
11-
connect:
11+
addnode:
1212
- tank-0000
13-
- tank-0003
1413
lnd: true
15-
- name: tank-0003
16-
connect:
17-
- tank-0004
18-
lnd: true
19-
- name: tank-0004
20-
connect:
21-
- tank-0000
22-
lnd: true

test/ln_basic_test.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#!/usr/bin/env python3
2+
3+
import json
4+
import os
5+
from pathlib import Path
6+
from time import sleep
7+
8+
from test_base import TestBase
9+
10+
11+
class LNBasicTest(TestBase):
12+
def __init__(self):
13+
super().__init__()
14+
self.network_dir = Path(os.path.dirname(__file__)) / "data" / "ln"
15+
self.miner_addr = ""
16+
17+
def run_test(self):
18+
try:
19+
self.setup_network()
20+
self.fund_wallets()
21+
self.manual_open_channels()
22+
self.wait_for_gossip_sync()
23+
self.pay_invoice()
24+
finally:
25+
self.cleanup()
26+
27+
def setup_network(self):
28+
self.log.info("Setting up network")
29+
self.log.info(self.warnet(f"deploy {self.network_dir}"))
30+
self.wait_for_all_tanks_status(target="running")
31+
32+
def fund_wallets(self):
33+
self.warnet("bitcoin rpc tank-0000 createwallet miner")
34+
self.warnet("bitcoin rpc tank-0000 -generate 110")
35+
self.wait_for_predicate(lambda: int(self.warnet("bitcoin rpc tank-0000 getblockcount")) > 100)
36+
37+
addrs = []
38+
for lnd in ["tank-0000-lnd", "tank-0001-lnd", "tank-0002-lnd"]:
39+
addrs.append(json.loads(self.warnet(f"ln rpc {lnd} newaddress p2wkh"))["address"])
40+
41+
self.warnet("bitcoin rpc tank-0000 sendmany '' '{" + f"\"{addrs[0]}\":10,\"{addrs[1]}\":10,\"{addrs[2]}\":10" + "}'")
42+
self.warnet("bitcoin rpc tank-0000 -generate 1")
43+
44+
def manual_open_channels(self):
45+
# 0 -> 1 -> 2
46+
pk1 = self.warnet("ln pubkey tank-0001-lnd")
47+
pk2 = self.warnet("ln pubkey tank-0002-lnd")
48+
49+
host1 = None
50+
host2 = None
51+
52+
while not host1 or not host2:
53+
if not host1:
54+
host1 = self.warnet("ln host tank-0001-lnd")
55+
if not host2:
56+
host2 = self.warnet("ln host tank-0002-lnd")
57+
sleep(1)
58+
59+
print(self.warnet(f"ln rpc tank-0000-lnd openchannel --node_key {pk1} --local_amt 100000 --connect {host1}"))
60+
print(self.warnet(f"ln rpc tank-0001-lnd openchannel --node_key {pk2} --local_amt 100000 --connect {host2}"))
61+
62+
def wait_for_two_txs():
63+
return 2 == json.loads(self.warnet("bitcoin rpc tank-0000 getmempoolinfo"))["size"]
64+
self.wait_for_predicate(wait_for_two_txs)
65+
66+
self.warnet("bitcoin rpc tank-0000 -generate 10")
67+
68+
69+
def wait_for_gossip_sync(self):
70+
chs0 = []
71+
chs1 = []
72+
chs2 = []
73+
74+
while len(chs0) != 2 or len(chs1) != 2 or len(chs2) != 2:
75+
if len(chs0) != 2:
76+
chs0 = json.loads(self.warnet("ln rpc tank-0000-lnd describegraph"))["edges"]
77+
if len(chs1) != 2:
78+
chs1 = json.loads(self.warnet("ln rpc tank-0001-lnd describegraph"))["edges"]
79+
if len(chs2) != 2:
80+
chs2 = json.loads(self.warnet("ln rpc tank-0002-lnd describegraph"))["edges"]
81+
sleep(1)
82+
83+
84+
def pay_invoice(self):
85+
inv = json.loads(self.warnet("ln rpc tank-0002-lnd addinvoice --amt 1000"))
86+
print(inv)
87+
print(self.warnet(f"ln rpc tank-0000-lnd payinvoice -f {inv['payment_request']}"))
88+
89+
def wait_for_success():
90+
return 1000 == json.loads(self.warnet("ln rpc tank-0002-lnd channelbalance"))["balance"]
91+
self.wait_for_predicate(wait_for_success)
92+
93+
94+
if __name__ == "__main__":
95+
test = LNBasicTest()
96+
test.run_test()

0 commit comments

Comments
 (0)