Skip to content

Commit 802698f

Browse files
committed
scenarios: ensure ln_init can handle larger scale networks
1 parent 7171ec4 commit 802698f

File tree

4 files changed

+273
-55
lines changed

4 files changed

+273
-55
lines changed

Diff for: src/graphs/attackathon_10.graphml

+202
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
<?xml version="1.0" encoding="UTF-8"?><graphml xmlns="http://graphml.graphdrawing.org/xmlns">
2+
<key attr.name="version" attr.type="string" for="node" id="version"/>
3+
<key attr.name="bitcoin_config" attr.type="string" for="node" id="bitcoin_config"/>
4+
<key attr.name="tc_netem" attr.type="string" for="node" id="tc_netem"/>
5+
<key attr.name="ln" attr.type="string" for="node" id="ln"/>
6+
<key attr.name="ln_image" attr.type="string" for="node" id="ln_image"/>
7+
<key attr.name="ln_cb_image" attr.type="string" for="node" id="ln_cb_image"/>
8+
<key attr.name="ln_config" attr.type="string" for="node" id="ln_config"/>
9+
<key attr.name="source-policy" attr.type="string" for="edge" id="source-policy"/>
10+
<key attr.name="target-policy" attr.type="string" for="edge" id="target-policy"/>
11+
<key attr.name="collect_logs" attr.type="boolean" for="node" id="collect_logs"/>
12+
<key attr.name="image" attr.type="string" for="node" id="image"/>
13+
<graph edgedefault="directed">
14+
<node id="0">
15+
<data key="version">26.0</data>
16+
<data key="bitcoin_config">-uacomment=w0</data>
17+
<data key="ln">lnd</data>
18+
<data key="ln_cb_image">pinheadmz/circuitbreaker:278737d</data>
19+
<data key="ln_config">--protocol.wumbo-channels --bitcoin.timelockdelta=80</data>
20+
</node>
21+
<node id="1">
22+
<data key="version">26.0</data>
23+
<data key="bitcoin_config">-uacomment=w1</data>
24+
<data key="ln">lnd</data>
25+
<data key="ln_cb_image">pinheadmz/circuitbreaker:278737d</data>
26+
<data key="ln_config">--protocol.wumbo-channels --bitcoin.timelockdelta=80</data>
27+
</node>
28+
<node id="2">
29+
<data key="version">26.0</data>
30+
<data key="bitcoin_config">-uacomment=w2</data>
31+
<data key="ln">lnd</data>
32+
<data key="ln_cb_image">pinheadmz/circuitbreaker:278737d</data>
33+
<data key="ln_config">--protocol.wumbo-channels --bitcoin.timelockdelta=80</data>
34+
</node>
35+
<node id="3">
36+
<data key="version">26.0</data>
37+
<data key="bitcoin_config">-uacomment=w3</data>
38+
<data key="ln">lnd</data>
39+
<data key="ln_cb_image">pinheadmz/circuitbreaker:278737d</data>
40+
<data key="ln_config">--protocol.wumbo-channels --bitcoin.timelockdelta=30</data>
41+
</node>
42+
<node id="4">
43+
<data key="version">26.0</data>
44+
<data key="bitcoin_config">-uacomment=w4</data>
45+
<data key="ln">lnd</data>
46+
<data key="ln_cb_image">pinheadmz/circuitbreaker:278737d</data>
47+
<data key="ln_config">--protocol.wumbo-channels --bitcoin.timelockdelta=30 --default-remote-max-htlcs=20</data>
48+
</node>
49+
<node id="5">
50+
<data key="version">26.0</data>
51+
<data key="bitcoin_config">-uacomment=w5</data>
52+
<data key="ln">lnd</data>
53+
<data key="ln_cb_image">pinheadmz/circuitbreaker:278737d</data>
54+
<data key="ln_config">--protocol.wumbo-channels --bitcoin.timelockdelta=45</data>
55+
</node>
56+
<node id="6">
57+
<data key="version">26.0</data>
58+
<data key="bitcoin_config">-uacomment=w6</data>
59+
<data key="ln">lnd</data>
60+
<data key="ln_cb_image">pinheadmz/circuitbreaker:278737d</data>
61+
<data key="ln_config">--protocol.wumbo-channels --bitcoin.timelockdelta=45</data>
62+
</node>
63+
<node id="7">
64+
<data key="version">26.0</data>
65+
<data key="bitcoin_config">-uacomment=w7</data>
66+
<data key="ln">lnd</data>
67+
<data key="ln_cb_image">pinheadmz/circuitbreaker:278737d</data>
68+
<data key="ln_config">--protocol.wumbo-channels --bitcoin.timelockdelta=45</data>
69+
</node>
70+
<node id="8">
71+
<data key="version">26.0</data>
72+
<data key="bitcoin_config">-uacomment=w8</data>
73+
<data key="ln">lnd</data>
74+
<data key="ln_cb_image">pinheadmz/circuitbreaker:278737d</data>
75+
<data key="ln_config">--protocol.wumbo-channels --bitcoin.timelockdelta=45</data>
76+
</node>
77+
<node id="9">
78+
<data key="version">26.0</data>
79+
<data key="bitcoin_config">-uacomment=w9</data>
80+
<data key="ln">lnd</data>
81+
<data key="ln_cb_image">pinheadmz/circuitbreaker:278737d</data>
82+
<data key="ln_config">--protocol.wumbo-channels --bitcoin.timelockdelta=80</data>
83+
</node>
84+
<node id="10">
85+
<data key="version">26.0</data>
86+
<data key="bitcoin_config">-uacomment=w10_evil</data>
87+
<data key="ln">lnd</data>
88+
</node>
89+
<node id="11">
90+
<data key="version">26.0</data>
91+
<data key="bitcoin_config">-uacomment=w11_evil</data>
92+
<data key="ln">lnd</data>
93+
</node>
94+
<node id="12">
95+
<data key="version">26.0</data>
96+
<data key="bitcoin_config">-uacomment=w12_evil</data>
97+
<data key="ln">lnd</data>
98+
</node>
99+
<node id="13">
100+
<data key="version">26.0</data>
101+
<data key="bitcoin_config">-uacomment=w13_evil</data>
102+
<data key="ln">lnd</data>
103+
</node>
104+
105+
<!-- Bitcoin nodes, connect in a ring to start -->
106+
<edge id="0" source="0" target="1"></edge>
107+
<edge id="1" source="1" target="2"></edge>
108+
<edge id="2" source="2" target="3"></edge>
109+
<edge id="3" source="3" target="4"></edge>
110+
<edge id="4" source="4" target="5"></edge>
111+
<edge id="5" source="5" target="6"></edge>
112+
<edge id="6" source="6" target="7"></edge>
113+
<edge id="7" source="7" target="8"></edge>
114+
<edge id="8" source="8" target="9"></edge>
115+
<edge id="9" source="9" target="10"></edge>
116+
<edge id="10" source="10" target="11"></edge>
117+
<edge id="11" source="11" target="12"></edge>
118+
<edge id="12" source="12" target="13"></edge>
119+
<edge id="13" source="13" target="0"></edge>
120+
<!-- connect everyone to miner (node 0) -->
121+
<edge id="14" source="0" target="2"></edge>
122+
<edge id="14" source="0" target="3"></edge>
123+
<edge id="14" source="0" target="4"></edge>
124+
<edge id="14" source="0" target="5"></edge>
125+
<edge id="14" source="0" target="6"></edge>
126+
<edge id="14" source="0" target="7"></edge>
127+
<edge id="14" source="0" target="8"></edge>
128+
<edge id="14" source="0" target="9"></edge>
129+
<edge id="14" source="0" target="10"></edge>
130+
<edge id="14" source="0" target="11"></edge>
131+
<edge id="14" source="0" target="12"></edge>
132+
133+
<!-- LN channels -->
134+
<edge id="0:1" source="0" target="1">
135+
<data key="source-policy">--base_fee_msat=1000 --fee_rate_ppm=600 --local_amt=02500000 --push_amt=01250000</data>
136+
<data key="target-policy">--base_fee_msat=1000 --fee_rate_ppm=600 --time_lock_delta=80</data>
137+
</edge>
138+
139+
<edge id="1:2" source="1" target="2">
140+
<data key="source-policy">--base_fee_msat=1000 --fee_rate_ppm=600 --local_amt=00500000 --push_amt=00250000</data>
141+
<data key="target-policy">--base_fee_msat=1000 --fee_rate_ppm=600 --time_lock_delta=80</data>
142+
</edge>
143+
<edge id="1:4" source="1" target="4">
144+
<data key="source-policy">--base_fee_msat=1000 --fee_rate_ppm=600 --local_amt=200000000 --push_amt=100000000</data>
145+
<data key="target-policy">--base_fee_msat=10000 --fee_rate_ppm=725 --time_lock_delta=30</data>
146+
</edge>
147+
<edge id="1:6" source="1" target="6">
148+
<data key="source-policy">--base_fee_msat=1000 --fee_rate_ppm=600 --local_amt=100000000 --push_amt=50000000</data>
149+
<data key="target-policy">--base_fee_msat=1000 --fee_rate_ppm=1 --time_lock_delta=45</data>
150+
</edge>
151+
152+
<edge id="2:3" source="2" target="3">
153+
<data key="source-policy">--base_fee_msat=1000 --fee_rate_ppm=600 --local_amt=50000000 --push_amt=02500000</data>
154+
<data key="target-policy">--base_fee_msat=1000 --fee_rate_ppm=999 --time_lock_delta=30</data>
155+
</edge>
156+
<edge id="2:5" source="2" target="5">
157+
<data key="source-policy">--base_fee_msat=1000 --fee_rate_ppm=600 --local_amt=00500000 --push_amt=00250000</data>
158+
<data key="target-policy">--base_fee_msat=1000 --fee_rate_ppm=1 --time_lock_delta=45</data>
159+
</edge>
160+
161+
<edge id="3:4" source="3" target="4">
162+
<data key="source-policy">--base_fee_msat=1000 --fee_rate_ppm=999 --local_amt=300000000 --push_amt=125000000</data>
163+
<data key="target-policy">--base_fee_msat=10000 --fee_rate_ppm=725 --time_lock_delta=30</data>
164+
</edge>
165+
<edge id="3:5" source="3" target="5">
166+
<data key="source-policy">--base_fee_msat=1000 --fee_rate_ppm=999 --local_amt=100000000 --push_amt=50000000</data>
167+
<data key="target-policy">--base_fee_msat=1000 --fee_rate_ppm=1 --time_lock_delta=45</data>
168+
</edge>
169+
170+
<edge id="4:5" source="4" target="5">
171+
<data key="source-policy">--base_fee_msat=10000 --fee_rate_ppm=725 --local_amt=500000000 --push_amt=250000000</data>
172+
<data key="target-policy">--base_fee_msat=1000 --fee_rate_ppm=1 --time_lock_delta=45</data>
173+
</edge>
174+
<edge id="4:6" source="4" target="6">
175+
<data key="source-policy">--base_fee_msat=10000 --fee_rate_ppm=725 --local_amt=100000000 --push_amt=50000000</data>
176+
<data key="target-policy">--base_fee_msat=1000 --fee_rate_ppm=1 --time_lock_delta=45</data>
177+
</edge>
178+
<edge id="4:7" source="4" target="7">
179+
<data key="source-policy">--base_fee_msat=10000 --fee_rate_ppm=725 --local_amt=50000000 --push_amt=25000000</data>
180+
<data key="target-policy">--base_fee_msat=1000 --fee_rate_ppm=1 --time_lock_delta=45</data>
181+
</edge>
182+
<edge id="4:8" source="4" target="8">
183+
<data key="source-policy">--base_fee_msat=10000 --fee_rate_ppm=725 --local_amt=50000000 --push_amt=25000000</data>
184+
<data key="target-policy">--base_fee_msat=1000 --fee_rate_ppm=1 --time_lock_delta=45</data>
185+
</edge>
186+
187+
<edge id="5:9" source="5" target="9">
188+
<data key="source-policy">--base_fee_msat=1000 --fee_rate_ppm=1 --local_amt=150000000 --push_amt=75000000</data>
189+
<data key="target-policy">--base_fee_msat=0 --fee_rate_ppm=0 --time_lock_delta=80</data>
190+
</edge>
191+
192+
<edge id="6:9" source="6" target="9">
193+
<data key="source-policy">--base_fee_msat=1000 --fee_rate_ppm=1 --local_amt=200000000 --push_amt=100000000</data>
194+
<data key="target-policy">--base_fee_msat=0 --fee_rate_ppm=0 --time_lock_delta=80</data>
195+
</edge>
196+
197+
<edge id="7:8" source="7" target="8">
198+
<data key="source-policy">--base_fee_msat=1000 --fee_rate_ppm=1 --local_amt=00400000 --push_amt=00200000</data>
199+
<data key="target-policy">--base_fee_msat=1000 --fee_rate_ppm=1 --time_lock_delta=45</data>
200+
</edge>
201+
</graph>
202+
</graphml>

Diff for: src/scenarios/ln_init.py

+66-54
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#!/usr/bin/env python3
2+
23
from time import sleep
34

45
from scenarios.utils import ensure_miner
@@ -15,83 +16,94 @@ def set_test_params(self):
1516

1617
def run_test(self):
1718
self.log.info("Get LN nodes and wallet addresses")
19+
ln_nodes = []
1820
recv_addrs = []
1921
for tank in self.warnet.tanks:
2022
if tank.lnnode is not None:
2123
recv_addrs.append(tank.lnnode.getnewaddress())
24+
ln_nodes.append(tank.index)
2225

2326
self.log.info("Fund LN wallets")
24-
miner = ensure_miner(self.nodes[3])
25-
addr = miner.getnewaddress()
26-
self.generatetoaddress(self.nodes[3], 110, addr)
27+
miner = ensure_miner(self.nodes[0])
28+
miner_addr = miner.getnewaddress()
29+
# 298 block base
30+
self.generatetoaddress(self.nodes[0], 298, miner_addr)
31+
# divvy up the goods
32+
split = miner.getbalance() // len(recv_addrs)
2733
for addr in recv_addrs:
28-
miner.sendtoaddress(addr, 50)
29-
self.generatetoaddress(self.nodes[3], 1, addr)
30-
31-
self.log.info("Waiting for funds to be spendable")
32-
ready = False
33-
while not ready:
34-
sleep(1)
35-
ready = True
34+
miner.sendtoaddress(addr, split)
35+
# confirm funds in block 299
36+
self.generatetoaddress(self.nodes[0], 1, miner_addr)
37+
38+
self.log.info(f"Waiting for funds to be spendable: {split} BTC each for {len(recv_addrs)} LN nodes")
39+
40+
def funded_lnnodes():
3641
for tank in self.warnet.tanks:
3742
if tank.lnnode is None:
3843
continue
39-
bal = tank.lnnode.get_wallet_balance()
40-
if int(bal["confirmed_balance"]) >= 50:
41-
continue
42-
ready = False
43-
break
44-
45-
self.log.info("Open channels")
46-
# TODO: This might belong in Warnet class as connect_ln_edges()
47-
# but that would need to ensure spendable funds first.
48-
# For now we consider this scenario "special".
49-
chan_pts = []
50-
ln_edges = []
44+
if int(tank.lnnode.get_wallet_balance()["confirmed_balance"]) < (split * 100000000):
45+
return False
46+
return True
47+
self.wait_until(funded_lnnodes, timeout=5*60)
48+
49+
ln_nodes_uri = ln_nodes.copy()
50+
while len(ln_nodes_uri) > 0:
51+
self.log.info(f"Waiting for all LN nodes to have URI, LN nodes remaining: {ln_nodes_uri}")
52+
for index in ln_nodes_uri:
53+
lnnode = self.warnet.tanks[index].lnnode
54+
if lnnode.getURI():
55+
ln_nodes_uri.remove(index)
56+
sleep(5)
57+
58+
self.log.info("Adding p2p connections to LN nodes")
59+
for edge in self.warnet.graph.edges(data=True):
60+
(src, dst, data) = edge
61+
# Copy the L1 p2p topology (where applicable) to L2
62+
# so we get a more robust p2p graph for lightning
63+
if "source-policy" not in data and self.warnet.tanks[src].lnnode and self.warnet.tanks[dst].lnnode:
64+
self.warnet.tanks[src].lnnode.connect_to_tank(dst)
65+
66+
# Start confirming channel opens in block 300
67+
self.log.info("Opening channels, one per block")
68+
chan_opens = []
5169
for edge in self.warnet.graph.edges(data=True):
5270
(src, dst, data) = edge
5371
if "source-policy" in data:
5472
src_node = self.warnet.get_ln_node_from_tank(src)
5573
assert src_node is not None
5674
assert self.warnet.get_ln_node_from_tank(dst) is not None
57-
ln_edges.append(edge)
75+
self.log.info(f"opening channel {src}->{dst}")
5876
chan_pt = src_node.open_channel_to_tank(dst, data["source-policy"])
59-
chan_pts.append(chan_pt)
60-
61-
self.log.info("Waiting for all channel open txs in mempool")
62-
while True:
63-
all_set = True
64-
mp = self.nodes[3].getrawmempool()
65-
for chan_pt in chan_pts:
66-
if chan_pt[:64] not in mp:
67-
all_set = False
68-
if all_set:
69-
break
70-
sleep(2)
71-
72-
self.log.info("Confirming channel opens")
73-
self.generatetoaddress(self.nodes[3], 6, addr)
74-
75-
self.log.info("Waiting for graph gossip sync")
76-
while True:
77-
all_set = True
78-
for tank in self.warnet.tanks:
79-
if tank.lnnode is not None:
80-
edges = tank.lnnode.lncli("describegraph")["edges"]
81-
if len(edges) != len(ln_edges):
82-
all_set = False
83-
if all_set:
84-
break
85-
sleep(2)
77+
# We can guarantee deterministic short channel IDs as long as
78+
# the change output is greater than the channel funding output,
79+
# which will then be output 0
80+
assert chan_pt[64:] == ":0"
81+
chan_opens.append((edge, chan_pt))
82+
self.log.info(f" pending channel point: {chan_pt}")
83+
self.wait_until(lambda chan_pt: chan_pt[:64] in self.nodes[0].getrawmempool())
84+
self.generatetoaddress(self.nodes[0], 1, miner_addr)
85+
assert chan_pt[:64] not in self.nodes[0].getrawmempool()
86+
self.log.info(f" confirmed in block {self.nodes[0].getblockcount()}")
87+
88+
# Ensure all channel opens are sufficiently confirmed
89+
self.generatetoaddress(self.nodes[0], 10, miner_addr)
90+
ln_nodes_gossip = ln_nodes.copy()
91+
while len(ln_nodes_gossip) > 0:
92+
self.log.info(f"Waiting for graph gossip sync, LN nodes remaining: {ln_nodes_gossip}")
93+
for index in ln_nodes_gossip:
94+
lnnode = self.warnet.tanks[index].lnnode
95+
if len(lnnode.lncli("describegraph")["edges"]) == len(chan_opens):
96+
ln_nodes_gossip.remove(index)
97+
sleep(5)
8698

8799
self.log.info("Updating channel policies")
88-
for i, edge in enumerate(ln_edges):
100+
for edge, chan_pt in chan_opens:
89101
(src, dst, data) = edge
90102
if "target-policy" in data:
91103
target_node = self.warnet.get_ln_node_from_tank(dst)
92-
target_node.update_channel_policy(chan_pts[i], data["target-policy"])
104+
target_node.update_channel_policy(chan_pt, data["target-policy"])
93105

94-
self.log.info(f"Warnet LN ready with {len(recv_addrs)} nodes and {len(ln_edges)} channels.")
106+
self.log.info(f"Warnet LN ready with {len(recv_addrs)} nodes and {len(chan_opens)} channels.")
95107

96108
if __name__ == "__main__":
97109
LNInit().main()

Diff for: src/warnet/lnnode.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"--accept-keysend",
1313
"--bitcoin.active",
1414
"--bitcoin.regtest",
15-
"--bitcoin.node=bitcoind"
15+
"--bitcoin.node=bitcoind",
16+
"--maxpendingchannels=64"
1617
])
1718

1819
class LNNode:
@@ -67,6 +68,8 @@ def getnewaddress(self):
6768

6869
def getURI(self):
6970
res = self.lncli("getinfo")
71+
if len(res["uris"]) < 1:
72+
return None
7073
return res["uris"][0]
7174

7275
def get_wallet_balance(self):

Diff for: test/data/ln.graphml

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
<edge id="1" source="0" target="1"></edge>
4040
<edge id="2" source="1" target="2"></edge>
4141
<edge id="3" source="2" target="3"></edge>
42+
<edge id="4" source="2" target="0"></edge>
4243
<edge id="4" source="3" target="0"></edge>
4344
<!-- LN channels -->
4445
<edge id="5" source="0" target="1">

0 commit comments

Comments
 (0)