Skip to content

Commit b4f244f

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

File tree

4 files changed

+139
-19
lines changed

4 files changed

+139
-19
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: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import json
2+
from typing import Optional
23

34
import click
45

5-
from .k8s import get_pod
6+
from .k8s import get_default_namespace_or, get_pod
67
from .process import run_command
78

89

@@ -13,24 +14,27 @@ def ln():
1314

1415
@ln.command(context_settings={"ignore_unknown_options": True})
1516
@click.argument("pod", type=str)
16-
@click.argument("command", type=str, required=True)
17-
def rpc(pod: str, command: str):
17+
@click.argument("method", type=str)
18+
@click.argument("params", type=str, nargs=-1) # this will capture all remaining arguments
19+
@click.option("--namespace", default=None, show_default=True)
20+
def rpc(pod: str, method: str, params: str, namespace: Optional[str]):
1821
"""
1922
Call lightning cli rpc <command> on <ln pod name>
2023
"""
21-
print(_rpc(pod, command))
24+
print(_rpc(pod, method, params, namespace))
2225

2326

24-
def _rpc(pod_name: str, command: str):
27+
def _rpc(pod_name: str, method: str, params: str = "", namespace: Optional[str] = None):
2528
# TODO: when we add back cln we'll need to describe the pod,
2629
# get a label with implementation type and then adjust command
2730
pod = get_pod(pod_name)
31+
namespace = get_default_namespace_or(namespace)
2832
chain = pod.metadata.labels["chain"]
29-
cmd = f"kubectl exec {pod_name} -- lncli --network {chain} {command}"
33+
cmd = f"kubectl -n {namespace} exec {pod_name} -- lncli --network {chain} {method} {' '.join(map(str, params))}"
3034
return run_command(cmd)
3135

3236

33-
@ln.command(context_settings={"ignore_unknown_options": True})
37+
@ln.command()
3438
@click.argument("pod", type=str)
3539
def pubkey(
3640
pod: str,
@@ -41,3 +45,18 @@ def pubkey(
4145
# TODO: again here, cln will need a different command
4246
info = _rpc(pod, "getinfo")
4347
print(json.loads(info)["identity_pubkey"])
48+
49+
50+
@ln.command()
51+
@click.argument("pod", type=str)
52+
def host(
53+
pod: str,
54+
):
55+
"""
56+
Get lightning node host from <ln pod name>
57+
"""
58+
# TODO: again here, cln will need a different command
59+
info = _rpc(pod, "getinfo")
60+
uris = json.loads(info)["uris"]
61+
if uris and len(uris) >= 0:
62+
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: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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(
36+
lambda: int(self.warnet("bitcoin rpc tank-0000 getblockcount")) > 100
37+
)
38+
39+
addrs = []
40+
for lnd in ["tank-0000-lnd", "tank-0001-lnd", "tank-0002-lnd"]:
41+
addrs.append(json.loads(self.warnet(f"ln rpc {lnd} newaddress p2wkh"))["address"])
42+
43+
self.warnet(
44+
"bitcoin rpc tank-0000 sendmany '' '{"
45+
+ f'"{addrs[0]}":10,"{addrs[1]}":10,"{addrs[2]}":10'
46+
+ "}'"
47+
)
48+
self.warnet("bitcoin rpc tank-0000 -generate 1")
49+
50+
def manual_open_channels(self):
51+
# 0 -> 1 -> 2
52+
pk1 = self.warnet("ln pubkey tank-0001-lnd")
53+
pk2 = self.warnet("ln pubkey tank-0002-lnd")
54+
55+
host1 = None
56+
host2 = None
57+
58+
while not host1 or not host2:
59+
if not host1:
60+
host1 = self.warnet("ln host tank-0001-lnd")
61+
if not host2:
62+
host2 = self.warnet("ln host tank-0002-lnd")
63+
sleep(1)
64+
65+
print(
66+
self.warnet(
67+
f"ln rpc tank-0000-lnd openchannel --node_key {pk1} --local_amt 100000 --connect {host1}"
68+
)
69+
)
70+
print(
71+
self.warnet(
72+
f"ln rpc tank-0001-lnd openchannel --node_key {pk2} --local_amt 100000 --connect {host2}"
73+
)
74+
)
75+
76+
def wait_for_two_txs():
77+
return json.loads(self.warnet("bitcoin rpc tank-0000 getmempoolinfo"))["size"] == 2
78+
79+
self.wait_for_predicate(wait_for_two_txs)
80+
81+
self.warnet("bitcoin rpc tank-0000 -generate 10")
82+
83+
def wait_for_gossip_sync(self):
84+
chs0 = []
85+
chs1 = []
86+
chs2 = []
87+
88+
while len(chs0) != 2 or len(chs1) != 2 or len(chs2) != 2:
89+
if len(chs0) != 2:
90+
chs0 = json.loads(self.warnet("ln rpc tank-0000-lnd describegraph"))["edges"]
91+
if len(chs1) != 2:
92+
chs1 = json.loads(self.warnet("ln rpc tank-0001-lnd describegraph"))["edges"]
93+
if len(chs2) != 2:
94+
chs2 = json.loads(self.warnet("ln rpc tank-0002-lnd describegraph"))["edges"]
95+
sleep(1)
96+
97+
def pay_invoice(self):
98+
inv = json.loads(self.warnet("ln rpc tank-0002-lnd addinvoice --amt 1000"))
99+
print(inv)
100+
print(self.warnet(f"ln rpc tank-0000-lnd payinvoice -f {inv['payment_request']}"))
101+
102+
def wait_for_success():
103+
return json.loads(self.warnet("ln rpc tank-0002-lnd channelbalance"))["balance"] == 1000
104+
self.wait_for_predicate(wait_for_success)
105+
106+
107+
if __name__ == "__main__":
108+
test = LNBasicTest()
109+
test.run_test()

0 commit comments

Comments
 (0)