Skip to content

Commit 7a4ff3c

Browse files
committed
add/modify documentation; bump SimLN version
1 parent fcd871d commit 7a4ff3c

File tree

6 files changed

+148
-40
lines changed

6 files changed

+148
-40
lines changed

docs/plugins.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Plugins
2+
3+
Plugins allow users to extend Warnet. Plugin authors can import commands from Warnet and plugin users can run plugin commands from the command line or on each invocation of `warnet deploy`.
4+
5+
## Activating plugins from 'network.yaml'
6+
7+
You can activate a plugin command by placing it in the `plugin` section at the bottom of each `network.yaml` file like so:
8+
9+
````yaml
10+
nodes:
11+
<<snip>>
12+
13+
plugins:
14+
- path/to/plugin/file/relative/to/the/network/dot/yaml/file/plugin.py
15+
````
16+
17+
Warnet will execute these plugin commands after each invocation of `warnet deploy`.
18+
19+
## Example: SimLN
20+
21+
To get started with an example plugin, review the `README` of the `simln` plugin found in any initialized Warnet directory:
22+
23+
1. `warnet init`
24+
2. `cd plugins/simln/`
25+

resources/plugins/simln/README.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# SimLN Plugin
2+
3+
## SimLN
4+
SimLN helps you generate lightning payment activity.
5+
6+
* Website: https://simln.dev/
7+
* Github: https://github.com/bitcoin-dev-project/sim-ln
8+
9+
## Usage
10+
SimLN uses "activity" definitions to create payment activity between lightning nodes. These definitions are in JSON format.
11+
12+
SimLN also requires access details for each node; however, the SimLN plugin will automatically generate these access details for each LND node. The access details look like this:
13+
14+
```` JSON
15+
{
16+
"id": <node_id>,
17+
"address": https://<ip:port or domain:port>,
18+
"macaroon": <path_to_selected_macaroon>,
19+
"cert": <path_to_tls_cert>
20+
}
21+
````
22+
23+
Since SimLN already has access to those LND connection details, it means you can focus on the "activity" definitions.
24+
25+
### Launch activity definitions from the command line
26+
The SimLN plugin takes "activity" definitions like so:
27+
28+
`./simln/plugin.py launch-activiy '[{\"source\": \"tank-0003-ln\", \"destination\": \"tank-0005-ln\", \"interval_secs\": 1, \"amount_msat\": 2000}]'"''`
29+
30+
### Launch activity definitions from within `network.yaml`
31+
When you initialize a new Warnet network, Warnet will create a new `network.yaml` file. If your `network.yaml` file includes lightning nodes, then you can use SimLN to produce activity between those nodes like this:
32+
33+
<details>
34+
<summary>network.yaml</summary>
35+
36+
````yaml
37+
nodes:
38+
- name: tank-0000
39+
addnode:
40+
- tank-0001
41+
ln:
42+
lnd: true
43+
44+
- name: tank-0001
45+
addnode:
46+
- tank-0002
47+
ln:
48+
lnd: true
49+
50+
- name: tank-0002
51+
addnode:
52+
- tank-0000
53+
ln:
54+
lnd: true
55+
56+
- name: tank-0003
57+
addnode:
58+
- tank-0000
59+
ln:
60+
lnd: true
61+
lnd:
62+
config: |
63+
bitcoin.timelockdelta=33
64+
channels:
65+
- id:
66+
block: 300
67+
index: 1
68+
target: tank-0004-ln
69+
capacity: 100000
70+
push_amt: 50000
71+
72+
- name: tank-0004
73+
addnode:
74+
- tank-0000
75+
ln:
76+
lnd: true
77+
lnd:
78+
channels:
79+
- id:
80+
block: 300
81+
index: 2
82+
target: tank-0005-ln
83+
capacity: 50000
84+
push_amt: 25000
85+
86+
- name: tank-0005
87+
addnode:
88+
- tank-0000
89+
ln:
90+
lnd: true
91+
92+
plugins:
93+
# Take note: the path to the plugin file is relative to the `network.yaml` file. The location of your `simln.py` file and `network.yaml` file may differ than what is shown below.
94+
- "../../../resources/plugins/simln/plugin.py launch-activity '[{\"source\": \"tank-0003-ln\", \"destination\": \"tank-0005-ln\", \"interval_secs\": 1, \"amount_msat\": 2000}]'"
95+
````
96+
97+
</details>
98+
99+
## Generating your own SimLn image
100+
The SimLN plugin fetches a SimLN docker image from dockerhub. You can generate your own docker image if you choose:
101+
102+
1. Clone SimLN: `git clone [email protected]:bitcoin-dev-project/sim-ln.git`
103+
2. Follow the instructions to build a docker image as detailed int the SimLn repository.
104+
3. Tag the resulting docker image: `docker tag IMAGEID YOURUSERNAME/sim-ln:VERSION`
105+
4. Push the tagged image to you dockerhub account.
106+
5Modify the `values.yaml` file in the plugin's chart to reflect your username and version number:
107+
```YAML
108+
repository: "YOURUSERNAME/sim-ln"
109+
tag: "VERSION"
110+
```

resources/plugins/simln/charts/simln/values.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: "simln"
22
image:
33
repository: "mplsgrant/sim-ln"
4-
tag: "d8c165d"
4+
tag: "4d33f24"
55
pullPolicy: IfNotPresent
66

77
workingVolume:

resources/plugins/simln/simln.py renamed to resources/plugins/simln/plugin.py

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,11 @@
2121
)
2222
from warnet.process import run_command
2323

24+
# Tt is common for Warnet objects to have a "mission" tag to query them in the cluster.
2425
# To make a "mission" tag for your plugin, declare it using the variable name MISSION. This will
2526
# be read by the warnet log system and status system.
26-
# This should match the pod's "mission" value in this plugin's associated helm file.
27+
# This must match the pod's "mission" value in the plugin's associated helm file.
2728
MISSION = "simln"
28-
29-
# Each pod we deploy should have a primary container. We make the name of that primary container
30-
# explicit here using the variable name CONTAINER which Warnet uses internally in its log and status
31-
# systems.
32-
# Again, this should match the container name provided in the associated helm file.
3329
PRIMARY_CONTAINER = MISSION
3430

3531
PLUGIN_DIR_TAG = "plugin_dir"
@@ -50,7 +46,7 @@ class SimLNError(Exception):
5046

5147
# Warnet uses a python package called "click" to manage terminal interactions with the user.
5248
# Each plugin must declare a click "group" by decorating a function named after the plugin.
53-
# This makes your plugin available in the plugin section of Warnet.
49+
# Using click makes it easy for users to interact with your plugin.
5450
@click.group()
5551
@click.pass_context
5652
def simln(ctx):
@@ -60,13 +56,8 @@ def simln(ctx):
6056
ctx.obj[PLUGIN_DIR_TAG] = Path(plugin_dir)
6157

6258

63-
# Make sure to register your plugin by adding the group function like so:
64-
def warnet_register_plugin(register_command):
65-
register_command(simln) # <-- We added the group function here.
66-
67-
68-
# The group function name is then used in decorators to create commands. These commands are
69-
# available to users when they access your plugin from the command line in Warnet.
59+
# The group name is then used in decorators to create commands. These commands are
60+
# available to users when they access your plugin from the command line.
7061
@simln.command()
7162
def list_pod_names():
7263
"""Get a list of SimLN pod names"""
@@ -81,11 +72,10 @@ def download_results(pod_name: str):
8172
print(f"Downloaded results to: {dest}")
8273

8374

84-
# When we want to use a command inside our plugin and also provide that command to the user, we like
85-
# to create a private function whose name starts with an underscore. We also make a public function
86-
# with the same name except that we leave off the underscore, decorate it with the command
87-
# decorator, and also provide an instructive doc string which Warnet will display in the help
88-
# section of the command line program.
75+
# When we want to use a command inside our plugin and also provide that command to the user, it
76+
# helps to create a private function whose name starts with an underscore. We also make a public
77+
# function with the same name except that we leave off the underscore, decorate it with the command
78+
# decorator, and also provide an instructive doc string for the user.
8979
def _get_example_activity() -> list[dict]:
9080
pods = get_mission(LIGHTNING_MISSION)
9181
try:

test/ln_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def __init__(self):
1818
self.imported_network_dir = self.tmpdir / "imported_network"
1919
self.scen_dir = Path(os.path.dirname(__file__)).parent / "resources" / "scenarios"
2020
self.plugins_dir = Path(os.path.dirname(__file__)).parent / "resources" / "plugins"
21-
self.simln_exec = Path("simln/simln.py")
21+
self.simln_exec = Path("simln/plugin.py")
2222

2323
def run_test(self):
2424
try:

test/simln_test.py

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
#!/usr/bin/env python3
22
import ast
3-
import json
43
import os
54
from functools import partial
65
from pathlib import Path
7-
from time import sleep
86
from typing import Optional
97

108
import pexpect
119
from test_base import TestBase
1210

13-
from warnet.constants import LIGHTNING_MISSION
14-
from warnet.k8s import download, get_mission, wait_for_pod
11+
from warnet.k8s import download, wait_for_pod
1512
from warnet.process import run_command
1613

1714

@@ -20,7 +17,7 @@ def __init__(self):
2017
super().__init__()
2118
self.network_dir = Path(os.path.dirname(__file__)) / "data" / "ln"
2219
self.plugins_dir = Path(os.path.dirname(__file__)).parent / "resources" / "plugins"
23-
self.simln_exec = "plugins/simln/simln.py"
20+
self.simln_exec = "plugins/simln/plugin.py"
2421

2522
def run_test(self):
2623
try:
@@ -52,20 +49,6 @@ def copy_results(self):
5249
download(pod, Path("/working/results"), Path("."))
5350
self.wait_for_predicate(self.found_results_locally)
5451

55-
def wait_for_gossip_sync(self, expected: int):
56-
self.log.info(f"Waiting for sync (expecting {expected})...")
57-
current = 0
58-
while current < expected:
59-
current = 0
60-
pods = get_mission(LIGHTNING_MISSION)
61-
for v1_pod in pods:
62-
node = v1_pod.metadata.name
63-
chs = json.loads(run_command(f"warnet ln rpc {node} describegraph"))["edges"]
64-
self.log.info(f"{node}: {len(chs)} channels")
65-
current += len(chs)
66-
sleep(1)
67-
self.log.info("Synced")
68-
6952
def found_results_remotely(self, pod: Optional[str] = None) -> bool:
7053
if pod is None:
7154
pod = self.get_first_simln_pod()

0 commit comments

Comments
 (0)