diff --git a/README.md b/README.md index 627859d01..9ae117611 100644 --- a/README.md +++ b/README.md @@ -5,14 +5,13 @@ Monitor and analyze the emergent behaviors of Bitcoin networks. ## Major Features -* Launch a bitcoin network with a specified number of nodes connected to each other according to a network topology from a specification file. -* Nodes are assigned random "reachable" IP addresses in the 100.0.0.0/8 subnet which ensures randomized `addrman` bucketing [by Bitcoin Core.](https://github.com/bitcoin/bitcoin/blob/8372ab0ea3c88308aabef476e3b9d023ba3fd4b7/src/addrman.h#L66) (docker only) +* Launch a bitcoin network with a specified number of nodes connected to each other according to a network topology from a graphml file. * Scenarios can be run across the network which can be programmed using the Bitcoin Core functional [test_framework language](https://github.com/bitcoin/bitcoin/tree/master/test/functional). * Nodes can have traffic shaping parameters assigned to them via the graph using [tc-netem](https://manpages.ubuntu.com/manpages/trusty/man8/tc-netem.8.html) tool. -* Log files from nodes can be accessed directly -* A unified log file can be grepped using regex -* Container resource metrics are reported via a Graphana dashboard. -* P2P messages between any two nodes can be retrieved in chronological order. +* Data from nodes can be collected and searched including log files and p2p messages. +* Performance data from containers can be monitored and visualized. +* Lightning Network nodes can be deployed and operated. +* Small networks can be deployed locally with Docker Compose, larger networks can be deployed remotely with Kubernetes ## Documentation diff --git a/docs/data.md b/docs/data.md index 73c14ed7b..3d04e7291 100644 --- a/docs/data.md +++ b/docs/data.md @@ -40,5 +40,3 @@ warnet_test_uhynisdj_tank_000002: 2023-10-11T17:44:52.325691Z [mempool] AcceptTo warnet_test_uhynisdj_tank_000002: 2023-10-11T17:44:52.325838Z [validation] TransactionAddedToMempool: txid=94cacabc09b024b56dcbed9ccad15c90340c596e883159bcb5f1d2152997322d wtxid=0cc875e73bb0bd8f892b70b8d1e5154aab64daace8d571efac94c62b8c1da3cf ... (etc) ``` - -# Next: [Monitoring](monitoring.md) diff --git a/docs/graph.md b/docs/graph.md index 75ab654d8..0c972003b 100644 --- a/docs/graph.md +++ b/docs/graph.md @@ -5,8 +5,7 @@ Warnet creates a Bitcoin network using a network topology from a [graphml](https Before any scenarios or RPC commands can be executed, a Warnet network must be started from a graph. See [warcli.md](warcli.md) for more details on these commands. -To start a network called `"warnet"` from the [default graph file](../src/graphs/default.graphml) -which consists of 12 Bitcoin Core v26.0 nodes connected in a ring: +To start a network called `"warnet"` from the [default graph file](../src/graphs/default.graphml): ``` warcli network start ``` @@ -16,92 +15,7 @@ To start a network with custom configurations: warcli network start --network="network_name" ``` -## GraphML file specification - -```graphml - - - - - - - - - - - - - - - -``` -### Node attributes - -* `id` should be a unique integer identifier -* `label` [optional] specifies the node's label -* `x` specifies the node's x position when rendered in a GUI -* `y` specifies the node's y position when rendered in a GUI -* `version` specifies the node's Bitcoin Core release version, or GitHub branch -* `bitcoin_config` is a comma-separated list of values the node should apply to it's bitcoin.conf, using bitcoin.conf syntax -* `tc_netem` is a `tc-netem` command as a string beginning with "tc qdisc add dev eth0 root netem" - -`version` should be either a version number from the pre-compiled binary list on https://bitcoincore.org/bin/ **or** a branch to be compiled from GitHub using `/#` syntax. - -Nodes can be added to the graph as follows: - - -#### Release binaries - -```graphml - -5.5 -2.5 -26.0 -uacomment=warnet0_v24,debugexclude=libevent - - -``` -#### Pre-built custom image - -For a pre-built custom image, remove the `version` tag, and add the image repository to an `image` tag. -These should be built using the `warcli image build` command to ensure they have the needed patches. - -```graphml - -5.5 -2.5 -bitcoindevproject/bitcoin:26.0 -uacomment=warnet0_v24,debugexclude=libevent - - -``` - -#### On-demand built branch - -For an on-demand built branch with traffic shaping rules applied using `tc_netem`: - -```graphml - -2.5 -5.0 -vasild/bitcoin#relay_tx_to_priv_nets -uacomment=warnet1_custom,debug=1 -tc qdisc add dev eth0 root netem delay 100ms - -``` - -`x`, `y`, `version`, `bitcoin_config` and `tc_netem` data fields are optional for all nodes. - -### Edges - -Edges can be added between the nodes as follows: - -```graphml - - -``` - -## Creating graphs +## Creating graphs automatically Graphs can be created via the graph menu: @@ -109,20 +23,55 @@ Graphs can be created via the graph menu: # show graph commands warcli graph --help -# Create an erdos renyi graph of 12 nodes using edge connection probability of 0.3 and default bitcoin version (v26.0) -warcli graph create n=12 p=0.3 --outfile=erdos-renyi_n12_v26.0.graphml +# Create a cycle graph of 12 nodes using default Bitcoin Core version (v26.0) +warcli graph create 12 --outfile=./12_x_v26.0.graphml -# Create an erdos renyi graph of 15 nodes using default edge connection probability of p=0.2 and using random bitcoin versions -warcli graph create n=15 --outfile=erdos-renyi_n15_random.graphml --random +# Start network with default name "warnet" +warcli network start ./12_x_v26.0.graphml ``` -## Examples +## Warnet graph nodes and edges + +Nodes in a Warnet graph MUST have either a `"version"` key or an `"image"` key. +These dictate what version of Bitcoin Core to deploy in a fiven tank. -1. [example.graphml](../src/graphs/default.graphml) -- This is the default graph. -2. [random_internet_as_graph_n100_pos.graphml](../src/graphs/random_internet_as_graph_n100_pos.graphml) +Edges without additional properties are interpreted as Bitcoin p2p connections. +If an edge has additional key-value properties, it will be interpreted as a +lightning network channel (see [lightning.md](lightning.md)). -![random_internet_as_graph_n100](../docs/random_internet_as_graph_n100.png) -*random_internet_as_graph_n100* +## GraphML file specification +### GraphML file format and headers +```xml + + + + + + + + + + + + + + + + + +``` -# Next: [`warcli` commands](warcli.md) +| key | for | type | default | explanation | +|----------------|-------|---------|-----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| version | node | string | | Bitcoin Core version with an available Warnet tank image on Dockerhub. May also be a GitHub repository with format user/repository:branch to build from source code | +| image | node | string | | Bitcoin Core Warnet tank image on Dockerhub with the format repository/image:tag | +| bitcoin_config | node | string | | A string of Bitcoin Core options in command-line format, e.g. '-debug=net -blocksonly' | +| tc_netem | node | string | | A tc-netem command as a string beginning with 'tc qdisc add dev eth0 root netem' | +| exporter | node | boolean | False | Whether to attach a Prometheus data exporter to the tank | +| collect_logs | node | boolean | False | Whether to collect Bitcoin Core debug logs with Promtail | +| build_args | node | string | | A string of configure options used when building Bitcoin Core from source code, e.g. '--without-gui --disable-tests' | +| ln | node | string | | Attach a lightning network node of this implementation (currently only supports 'lnd') | +| ln-image | node | string | | Specify a lightning network node image from Dockerhub with the format repository/image:tag | +| ln-cb-image | node | string | | Specify a lnd Circuit Breaker image from Dockerhub with the format repository/image:tag | +| channel | edge | number | | Indicate that this edge is a lightning channel with this specified capacity | diff --git a/docs/install.md b/docs/install.md index e9cb5b06f..8f2fea085 100644 --- a/docs/install.md +++ b/docs/install.md @@ -81,5 +81,3 @@ source .venv/bin/activate pip install --upgrade pip pip install -e . ``` - -# Next: [Running Warnet](running.md) diff --git a/docs/lightning.md b/docs/lightning.md index 9b1bcccd3..964b3701c 100644 --- a/docs/lightning.md +++ b/docs/lightning.md @@ -89,7 +89,3 @@ $ sim-cli --data-dir /Users/bitcoin-dev-project/.warnet/warnet/warnet/simln 2023-11-18T16:58:28.826Z INFO [sim_lib] Started random activity producer for warnet_ln_000001(02318b...c7bf05): activity generator for capacity: 100000000 with multiplier 2: 52.63157894736842 payments per month (0.07309941520467836 per hour). 2023-11-18T16:58:28.826Z INFO [sim_lib] Started random activity producer for warnet_ln_000002(0393aa...9804d5): activity generator for capacity: 50000000 with multiplier 2: 26.31578947368421 payments per month (0.03654970760233918 per hour). ``` - - - - diff --git a/docs/monitoring.md b/docs/monitoring.md index 8e9bdeab4..122483c3f 100644 --- a/docs/monitoring.md +++ b/docs/monitoring.md @@ -30,5 +30,3 @@ The default dashboard is called "Docker Container & Host Metrics" and can be accessed via the "dashboards" tab, or from the bottom right of the home screen. Additional dashboards and datasources may be added in the future. - -# Next: [Lightning Network](lightning.md) diff --git a/docs/running.md b/docs/running.md index df88ff0c9..0d88c0cf2 100644 --- a/docs/running.md +++ b/docs/running.md @@ -115,5 +115,3 @@ sudo systemctl restart docker ``` On Ubuntu this file is located at `/lib/systemd/system/docker.service` but you can find it using `sudo systemctl status docker`. - -# Next: [Network Topology](graph.md) diff --git a/docs/scenarios.md b/docs/scenarios.md index 81dda08ca..2da81b25d 100644 --- a/docs/scenarios.md +++ b/docs/scenarios.md @@ -45,5 +45,3 @@ Stopped scenario with PID 14683. To add your own scenario make a copy of one of the existing python tests in src/scenarios/ and write the desired scenario. Save this file back into the same src/scenarios/ directory and it will be listed and available for running using the aforementioned commands. - -# Next: [Collecting Data](data.md) diff --git a/docs/warcli.md b/docs/warcli.md index b42aef990..6e1af1dfd 100644 --- a/docs/warcli.md +++ b/docs/warcli.md @@ -10,6 +10,9 @@ to simplify default operation. Execute `warcli --help` or `warcli help` to see a list of command categories. +Help text is provided, with optional parameters in [square brackets] and required +parameters in . + `warcli` commands are organized in a hierarchy of categories and subcommands. ## API Commands @@ -20,201 +23,202 @@ Fetch the Bitcoin Core debug log from \ in [network] options: | name | type | required | default | |---------|--------|------------|-----------| -| node | Int | True | | -| network | String | False | warnet | -| help | Bool | False | False | +| node | Int | yes | | +| network | String | | "warnet" | ### `warcli grep-logs` -Grep combined logs via fluentd using regex [pattern] +Grep combined logs via fluentd using regex \ options: | name | type | required | default | |---------|--------|------------|-----------| -| pattern | String | True | | -| network | String | False | warnet | -| help | Bool | False | False | +| pattern | String | yes | | +| network | String | | "warnet" | ### `warcli help` -Display help information for the given command. +Display help information for the given [command] (and sub-command). If no command is given, display help for the main CLI. options: | name | type | required | default | |----------|--------|------------|-----------| -| commands | String | False | | -| help | Bool | False | False | +| commands | String | | | ### `warcli lncli` -Call lightning cli \ on \ in \<--network> +Call lightning cli \ on \ in [network] options: | name | type | required | default | |---------|--------|------------|-----------| -| node | Int | True | | -| command | String | True | | -| network | String | False | warnet | -| help | Bool | False | False | +| node | Int | yes | | +| command | String | yes | | +| network | String | | "warnet" | ### `warcli messages` -Fetch messages sent between \ and \ in \ +Fetch messages sent between \ and \ in [network] options: | name | type | required | default | |---------|--------|------------|-----------| -| node_a | Int | True | | -| node_b | Int | True | | -| network | String | False | warnet | -| help | Bool | False | False | +| node_a | Int | yes | | +| node_b | Int | yes | | +| network | String | | "warnet" | ### `warcli rpc` -Call bitcoin-cli \ \ on \ in \<--network> +Call bitcoin-cli \ [params] on \ in [network] options: | name | type | required | default | |---------|--------|------------|-----------| -| node | Int | True | | -| method | String | False | | -| params | String | False | () | -| network | String | False | warnet | -| help | Bool | False | False | +| node | Int | yes | | +| method | String | yes | | +| params | String | | | +| network | String | | "warnet" | ### `warcli stop` Stop warnet. -options: -| name | type | required | default | -|--------|--------|------------|-----------| -| help | Bool | False | False | ## Debug ### `warcli debug generate-compose` -Generate the docker-compose file for a given \ and \<--network> (default: "warnet") name and return it. +Generate the docker-compose file for a given \ and [network] and return it. options: | name | type | required | default | |------------|--------|------------|-----------| -| graph_file | String | True | | -| network | String | False | warnet | -| help | Bool | False | False | +| graph_file | String | yes | | +| network | String | | "warnet" | ## Graph ### `warcli graph create` -Create a graph file of type random AS graph with [params] +Create a cycle graph with \ nodes, and additionally include 7 extra random outbounds per node. + Returns XML file as string with or without --outfile option options: -| name | type | required | default | +| name | type | required | default | |--------------|--------|------------|-----------| -| params | String | False | | -| outfile | Func | False | | -| version | String | False | 26 | -| bitcoin_conf | Func | False | | -| random | Bool | False | 0 | -| help | Bool | False | 0 | +| number | Int | yes | | +| outfile | Path | | | +| version | String | | "26.0" | +| bitcoin_conf | Path | | | +| random | Bool | | False | + +### `warcli graph validate` +Validate a \ against the schema. + +options: +| name | type | required | default | +|--------|--------|------------|-----------| +| graph | Path | yes | | ## Image ### `warcli image build` -Build bitcoind and bitcoin-cli from \/\ and deploy to \ - This requires docker and buildkit to be enabled. +Build bitcoind and bitcoin-cli from \/\ as \:\. + Optionally deploy to remote registry using --action=push, otherwise image is loaded to local registry. options: | name | type | required | default | |------------|--------|------------|-----------| -| registry | String | True | | -| repo | String | True | | -| branch | String | True | | -| build_args | String | False | | -| arches | String | False | | -| help | Bool | False | False | +| repo | String | yes | | +| branch | String | yes | | +| registry | String | yes | | +| tag | String | yes | | +| build_args | String | | | +| arches | String | | | +| action | String | | | ## Network +### `warcli network connected` +Indicate whether the all of the edges in the gaph file are connected in [network] + +options: +| name | type | required | default | +|---------|--------|------------|-----------| +| network | String | | "warnet" | + ### `warcli network down` -Bring down a running warnet named \<--network> (default: "warnet"). +Bring down a running warnet named [network] options: | name | type | required | default | |---------|--------|------------|-----------| -| network | String | False | warnet | -| help | Bool | False | False | +| network | String | | "warnet" | ### `warcli network export` -Export all data for sim-ln to subdirectory +Export all [network] data for sim-ln to subdirectory options: | name | type | required | default | |---------|--------|------------|-----------| -| network | String | False | warnet | -| help | Bool | False | False | +| network | String | | "warnet" | ### `warcli network info` -Get info about a warnet named \<--network> (default: "warnet"). +Get info about a warnet named [network] options: | name | type | required | default | |---------|--------|------------|-----------| -| network | String | False | warnet | -| help | Bool | False | False | +| network | String | | "warnet" | ### `warcli network start` -Start a warnet with topology loaded from a \ into \<--network> (default: "warnet") +Start a warnet with topology loaded from a \ into [network] options: | name | type | required | default | |------------|--------|------------|----------------------------| -| graph_file | Path | False | src/graphs/default.graphml | -| force | Bool | False | False | -| network | String | False | warnet | -| help | Bool | False | False | +| graph_file | Path | | src/graphs/default.graphml | +| force | Bool | | False | +| network | String | | "warnet" | ### `warcli network status` -Get status of a warnet named \<--network> (default: "warnet"). +Get status of a warnet named [network] options: | name | type | required | default | |---------|--------|------------|-----------| -| network | String | False | warnet | -| help | Bool | False | False | +| network | String | | "warnet" | ### `warcli network up` -Bring up a previously-stopped warnet named \<--network> (default: "warnet"). +Bring up a previously-stopped warnet named [network] options: | name | type | required | default | |---------|--------|------------|-----------| -| network | String | False | warnet | -| help | Bool | False | False | +| network | String | | "warnet" | ## Scenarios ### `warcli scenarios active` List running scenarios "name": "pid" pairs -options: -| name | type | required | default | -|--------|--------|------------|-----------| -| help | Bool | False | False | ### `warcli scenarios available` List available scenarios in the Warnet Test Framework -options: -| name | type | required | default | -|--------|--------|------------|-----------| -| help | Bool | False | False | ### `warcli scenarios run` -Run \ from the Warnet Test Framework on \<--network> with optional arguments +Run \ from the Warnet Test Framework on [network] with optional arguments + +options: +| name | type | required | default | +|-----------------|--------|------------|-----------| +| scenario | String | yes | | +| additional_args | String | | | +| network | String | | "warnet" | + +### `warcli scenarios run-file` +Run \ from the Warnet Test Framework on [network] with optional arguments options: -| name | type | required | default | -|-----------------|-------------|------------|-----------| -| scenario | String | True | | -| additional_args | Unprocessed | False | | -| network | String | False | warnet | -| help | Bool | False | False | +| name | type | required | default | +|-----------------|--------|------------|-----------| +| scenario_path | String | yes | | +| additional_args | String | | | +| network | String | | "warnet" | ### `warcli scenarios stop` Stop scenario with PID \ from running @@ -222,8 +226,6 @@ Stop scenario with PID \ from running options: | name | type | required | default | |--------|--------|------------|-----------| -| pid | Int | True | | -| help | Bool | False | False | +| pid | Int | yes | | -# Next: [Scenarios](scenarios.md) diff --git a/scripts/apidocs.py b/scripts/apidocs.py old mode 100644 new mode 100755 index 48cb9e0ca..8e4b247e6 --- a/scripts/apidocs.py +++ b/scripts/apidocs.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 + import os import re from pathlib import Path @@ -6,6 +8,8 @@ from click import Context from tabulate import tabulate +file_path = Path(os.path.dirname(os.path.abspath(__file__))) / ".." / "docs" / "warcli.md" + doc = "" @@ -13,20 +17,21 @@ def print_cmd(cmd, super=""): global doc doc += f"### `warcli{super} {cmd['name']}`" + "\n" doc += cmd["help"].strip().replace("<", "\\<") + "\n" - doc += "\noptions:\n" - headers = ["name", "type", "required", "default"] - data = [ - [ - p["name"], - p["type"]["param_type"], - p["required"], - p["default"] - if p["type"]["param_type"] != "Path" - else Path(p["default"]).relative_to(Path.cwd()), + if len(cmd["params"]) > 1: + doc += "\noptions:\n" + headers = ["name", "type", "required", "default"] + data = [ + [ + p["name"], + p["type"]["param_type"] if p["type"]["param_type"] != "Unprocessed" else "String", + "yes" if p["required"] else "", + '"' + p["default"] + '"' if p["default"] and p["type"]["param_type"] == "String" + else Path(p["default"]).relative_to(Path.cwd()) if p["default"] and p["type"]["param_type"] == "Path" + else p["default"] + ] + for p in cmd["params"] if p["name"] != "help" ] - for p in cmd["params"] - ] - doc += tabulate(data, headers=headers, tablefmt="github") + doc += tabulate(data, headers=headers, tablefmt="github") doc += "\n\n" @@ -43,13 +48,11 @@ def print_cmd(cmd, super=""): for subcmd in cmd["commands"].values(): print_cmd(subcmd, " " + cmd["name"]) -file_path = Path(os.path.dirname(os.path.abspath(__file__))) / ".." / "docs" / "warcli.md" - with open(file_path) as file: text = file.read() -pattern = r"(## API Commands\n)(.*\n)*?(# Next)" -updated_text = re.sub(pattern, rf"\1\n{doc}\n\3", text) +pattern = r"(## API Commands\n)(.*\n)*?\Z" +updated_text = re.sub(pattern, rf"\1\n{doc}\n", text) with open(file_path, "w") as file: file.write(updated_text) diff --git a/scripts/graphdocs.py b/scripts/graphdocs.py new file mode 100755 index 000000000..f47dbc73f --- /dev/null +++ b/scripts/graphdocs.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 + +import os +import re +from pathlib import Path + +from tabulate import tabulate +from warnet.utils import load_schema + +graph_schema = load_schema() + +file_path = Path(os.path.dirname(os.path.abspath(__file__))) / ".." / "docs" / "graph.md" + +doc = "" + +doc += "### GraphML file format and headers\n" +doc += "```xml\n" +doc += '\n' +for name, details in graph_schema["node"]["properties"].items(): + vname = f'"{name}"' + vtype = f'"{details["type"]}"' + doc += f' \n' +for name, details in graph_schema["edge"]["properties"].items(): + vname = f'"{name}"' + vtype = f'"{details["type"]}"' + doc += f' \n' +doc += ' \n \n \n \n\n' +doc += "```\n\n" + +headers = ["key", "for", "type", "default", "explanation"] +data = [ + [ + name, + "node", + p["type"], + p.get("default", ""), + p["comment"] + ] + for name, p in graph_schema["node"]["properties"].items() +] +data += [ + [ + name, + "edge", + p["type"], + p.get("default", ""), + p["comment"] + ] + for name, p in graph_schema["edge"]["properties"].items() +] +doc += tabulate(data, headers=headers, tablefmt="github") + + +with open(file_path) as file: + text = file.read() + +pattern = r"(## GraphML file specification\n)(.*\n)*?\Z" +updated_text = re.sub(pattern, rf"\1\n{doc}\n", text) + +with open(file_path, "w") as file: + file.write(updated_text) diff --git a/src/cli/debug.py b/src/cli/debug.py index 1f640a7c1..2586380c9 100644 --- a/src/cli/debug.py +++ b/src/cli/debug.py @@ -13,6 +13,6 @@ def debug(): @click.option("--network", default="warnet", show_default=True) def generate_compose(graph_file: str, network: str): """ - Generate the docker-compose file for a given and <--network> (default: "warnet") name and return it. + Generate the docker-compose file for a given and [network] and return it. """ print(rpc_call("generate_compose", {"graph_file": graph_file, "network": network})) diff --git a/src/cli/graph.py b/src/cli/graph.py index 82c1920c2..088dba739 100644 --- a/src/cli/graph.py +++ b/src/cli/graph.py @@ -13,13 +13,13 @@ def graph(): @graph.command() @click.argument("number", type=int) -@click.option("--outfile", type=Path) +@click.option("--outfile", type=click.Path()) @click.option("--version", type=str, default=DEFAULT_TAG) -@click.option("--bitcoin_conf", type=Path) +@click.option("--bitcoin_conf", type=click.Path()) @click.option("--random", is_flag=True) def create(number: int, outfile: Path, version: str, bitcoin_conf: Path, random: bool = False): """ - Create a cycle graph with [n] nodes, and additionally include 7 extra random outbounds per node. + Create a cycle graph with nodes, and additionally include 7 extra random outbounds per node. Returns XML file as string with or without --outfile option """ print( @@ -37,9 +37,9 @@ def create(number: int, outfile: Path, version: str, bitcoin_conf: Path, random: @graph.command() -@click.argument("graph", type=Path) +@click.argument("graph", type=click.Path()) def validate(graph: Path): """ - Validate a graph file against the schema. + Validate a against the schema. """ - print(rpc_call("graph_validate", {"graph_path": graph.as_posix()})) + print(rpc_call("graph_validate", {"graph_path": Path(graph).as_posix()})) diff --git a/src/cli/main.py b/src/cli/main.py index 438c26419..40fb3cec3 100644 --- a/src/cli/main.py +++ b/src/cli/main.py @@ -26,7 +26,7 @@ def cli(): @click.pass_context def help_command(ctx, commands): """ - Display help information for the given command. + Display help information for the given [command] (and sub-command). If no command is given, display help for the main CLI. """ if not commands: @@ -64,7 +64,7 @@ def help_command(ctx, commands): @click.option("--network", default="warnet", show_default=True) def rpc(node, method, params, network): """ - Call bitcoin-cli on in <--network> + Call bitcoin-cli [params] on in [network] """ print( rpc_call( @@ -79,7 +79,7 @@ def rpc(node, method, params, network): @click.option("--network", default="warnet", show_default=True, type=str) def lncli(node: int, command: tuple, network: str): """ - Call lightning cli on in <--network> + Call lightning cli on in [network] """ print( rpc_call( @@ -105,7 +105,7 @@ def debug_log(node, network): @click.option("--network", default="warnet", show_default=True) def messages(node_a, node_b, network): """ - Fetch messages sent between and in + Fetch messages sent between and in [network] """ print(rpc_call("tank_messages", {"network": network, "node_a": node_a, "node_b": node_b})) @@ -115,7 +115,7 @@ def messages(node_a, node_b, network): @click.option("--network", default="warnet", show_default=True) def grep_logs(pattern, network): """ - Grep combined logs via fluentd using regex [pattern] + Grep combined logs via fluentd using regex """ print(rpc_call("logs_grep", {"network": network, "pattern": pattern})) diff --git a/src/cli/network.py b/src/cli/network.py index 77bd3c8ee..b05880987 100644 --- a/src/cli/network.py +++ b/src/cli/network.py @@ -49,7 +49,7 @@ def network(): @click.option("--network", default="warnet", show_default=True) def start(graph_file: Path, force: bool, network: str): """ - Start a warnet with topology loaded from a into <--network> (default: "warnet") + Start a warnet with topology loaded from a into [network] """ try: encoded_graph_file = "" @@ -71,7 +71,7 @@ def start(graph_file: Path, force: bool, network: str): @click.option("--network", default="warnet", show_default=True) def up(network: str): """ - Bring up a previously-stopped warnet named <--network> (default: "warnet"). + Bring up a previously-stopped warnet named [network] """ print(rpc_call("network_up", {"network": network})) @@ -80,7 +80,7 @@ def up(network: str): @click.option("--network", default="warnet", show_default=True) def down(network: str): """ - Bring down a running warnet named <--network> (default: "warnet"). + Bring down a running warnet named [network] """ running_scenarios = rpc_call("scenarios_list_running", {}) @@ -105,7 +105,7 @@ def down(network: str): @click.option("--network", default="warnet", show_default=True) def info(network: str): """ - Get info about a warnet named <--network> (default: "warnet"). + Get info about a warnet named [network] """ result = rpc_call("network_info", {"network": network}) assert isinstance(result, dict), "Result is not a dict" # Make mypy happy @@ -116,7 +116,7 @@ def info(network: str): @click.option("--network", default="warnet", show_default=True) def status(network: str): """ - Get status of a warnet named <--network> (default: "warnet"). + Get status of a warnet named [network] """ result = rpc_call("network_status", {"network": network}) assert isinstance(result, list), "Result is not a list" # Make mypy happy @@ -136,7 +136,7 @@ def status(network: str): @click.option("--network", default="warnet", show_default=True) def connected(network: str): """ - Indicate whether the all of the edges in the gaph file are connected in + Indicate whether the all of the edges in the gaph file are connected in [network] """ print(rpc_call("network_connected", {"network": network})) @@ -145,6 +145,6 @@ def connected(network: str): @click.option("--network", default="warnet", show_default=True) def export(network): """ - Export all data for sim-ln to subdirectory + Export all [network] data for sim-ln to subdirectory """ print(rpc_call("network_export", {"network": network})) diff --git a/src/cli/scenarios.py b/src/cli/scenarios.py index 71258480d..9d19e32f5 100644 --- a/src/cli/scenarios.py +++ b/src/cli/scenarios.py @@ -40,7 +40,7 @@ def available(): @click.option("--network", default="warnet", show_default=True) def run(scenario, network, additional_args): """ - Run from the Warnet Test Framework on <--network> with optional arguments + Run from the Warnet Test Framework on [network] with optional arguments """ params = { "scenario": scenario, @@ -56,7 +56,7 @@ def run(scenario, network, additional_args): @click.option("--network", default="warnet", show_default=True) def run_file(scenario_path, network, additional_args): """ - Run from the Warnet Test Framework on <--network> with optional arguments + Run from the Warnet Test Framework on [network] with optional arguments """ scenario_base64 = "" with open(scenario_path, "rb") as f: diff --git a/src/graphs/random_internet_as_graph_n100_pos.graphml b/src/graphs/random_internet_as_graph_n100_pos.graphml deleted file mode 100644 index 14d665447..000000000 --- a/src/graphs/random_internet_as_graph_n100_pos.graphml +++ /dev/null @@ -1,2007 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - 0 - 25.0 - T - 8 - 10.0 - 167 - 200 - 187 - -345.33 - -65.136536 - - - 1 - 25.0 - T - 8 - 10.0 - 167 - 200 - 187 - -108.87451 - 311.5217 - - - 2 - 25.0 - T - 11 - 10.0 - 219 - 233 - 189 - 437.07748 - -35.161407 - - - 3 - 25.0 - T - 10 - 10.0 - 202 - 222 - 188 - 397.81857 - -413.4293 - - - 4 - 25.0 - T - 5 - 10.0 - 114 - 167 - 185 - -113.606476 - 395.54526 - - - 5 - 1 - 25.0 - M - 20 - 10.0 - 231 - 120 - 95 - 387.9503 - 36.760162 - - - 6 - 7 - 25.0 - M - 25 - 10.0 - 215 - 25 - 28 - -186.1907 - -450.80423 - - - 7 - 0 - 25.0 - M - 9 - 10.0 - 184 - 211 - 188 - 388.4562 - -456.53162 - - - 8 - 3 - 25.0 - M - 21 - 10.0 - 228 - 101 - 82 - -14.722537 - 247.53622 - - - 9 - 2 - 25.0 - M - 5 - 10.0 - 114 - 167 - 185 - -459.20715 - 215.68076 - - - 10 - 4 - 25.0 - M - 5 - 10.0 - 114 - 167 - 185 - 315.40298 - -31.24808 - - - 11 - 2 - 25.0 - M - 8 - 10.0 - 167 - 200 - 187 - -307.605 - -438.86588 - - - 12 - 1 - 25.0 - M - 9 - 10.0 - 184 - 211 - 188 - -94.60611 - 424.12473 - - - 13 - 3 - 25.0 - M - 21 - 10.0 - 228 - 101 - 82 - 124.99362 - -108.37976 - - - 14 - 1 - 25.0 - M - 4 - 10.0 - 96 - 156 - 184 - 113.45743 - -399.50806 - - - 15 - 1 - 25.0 - M - 4 - 10.0 - 96 - 156 - 184 - -354.22214 - -293.7481 - - - 16 - 1 - 25.0 - M - 5 - 10.0 - 114 - 167 - 185 - -102.21649 - 249.12918 - - - 17 - 1 - 25.0 - M - 3 - 10.0 - 79 - 145 - 183 - 397.26535 - 434.1937 - - - 18 - 0 - 25.0 - M - 2 - 10.0 - 61 - 134 - 182 - 130.2667 - 392.49802 - - - 19 - 4 - 25.0 - M - 7 - 10.0 - 149 - 189 - 186 - 277.88034 - -287.6986 - - - 20 - 1 - 25.0 - CP - 3 - 10.0 - 79 - 145 - 183 - 443.37527 - -332.6286 - - - 21 - 1 - 25.0 - CP - 2 - 10.0 - 61 - 134 - 182 - 307.61978 - 323.31137 - - - 22 - 1 - 25.0 - CP - 2 - 10.0 - 61 - 134 - 182 - 443.08322 - 226.16763 - - - 23 - 0 - 25.0 - CP - 1 - 10.0 - 44 - 123 - 182 - -378.17783 - 26.66696 - - - 24 - 0 - 25.0 - CP - 3 - 10.0 - 79 - 145 - 183 - -202.4607 - -522.1029 - - - 25 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 30.935024 - -223.2381 - - - 26 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -205.84445 - 165.94357 - - - 27 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 245.31113 - 242.32877 - - - 28 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 447.0541 - 151.97354 - - - 29 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -68.55429 - 115.89407 - - - 30 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 132.32077 - -196.41245 - - - 31 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -260.30045 - 433.6675 - - - 32 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 30.269741 - 67.89664 - - - 33 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 323.43637 - 13.927947 - - - 34 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 361.93356 - 154.02853 - - - 35 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 155.63492 - 165.91464 - - - 36 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -478.75186 - 244.63815 - - - 37 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -358.58023 - 21.426481 - - - 38 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -275.0871 - -190.18326 - - - 39 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -376.41342 - 197.77847 - - - 40 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -271.3386 - -93.63507 - - - 41 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -116.91821 - 13.459075 - - - 42 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 491.68777 - 311.21536 - - - 43 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -352.47153 - 146.77792 - - - 44 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 410.2302 - -540.1397 - - - 45 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 438.27292 - -547.01166 - - - 46 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -113.177185 - -61.039642 - - - 47 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -189.93967 - -363.18503 - - - 48 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -446.5178 - -283.11966 - - - 49 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -332.67093 - -384.9888 - - - 50 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 425.10568 - 386.71994 - - - 51 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -122.752014 - -302.48257 - - - 52 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -376.3867 - -556.52545 - - - 53 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -157.99829 - 165.98569 - - - 54 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 83.87674 - 20.285307 - - - 55 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -458.53607 - 434.88242 - - - 56 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -170.76828 - -431.69592 - - - 57 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 312.27945 - -256.92538 - - - 58 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -12.616763 - -319.4767 - - - 59 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 290.5101 - 242.75296 - - - 60 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -293.2333 - -368.01303 - - - 61 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 12.381864 - 427.73727 - - - 62 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -471.06195 - 237.12271 - - - 63 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 141.30313 - 74.65799 - - - 64 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -425.67673 - 309.50302 - - - 65 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -330.793 - 258.2007 - - - 66 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 106.66513 - 350.16507 - - - 67 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 226.4042 - -454.4526 - - - 68 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -344.23645 - 87.68869 - - - 69 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 326.5137 - 175.3417 - - - 70 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -101.38733 - 146.55173 - - - 71 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 471.6743 - -117.04648 - - - 72 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -74.203 - 429.8538 - - - 73 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 48.11984 - 46.510223 - - - 74 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 353.01517 - -231.68225 - - - 75 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -146.66397 - -2.8900466 - - - 76 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -23.140415 - 9.55075 - - - 77 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -86.60162 - -338.17355 - - - 78 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 319.48563 - -0.1352858 - - - 79 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -27.276249 - 189.55936 - - - 80 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 458.5643 - -154.47165 - - - 81 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -331.64835 - -152.51788 - - - 82 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -3.0810578 - 431.1426 - - - 83 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -62.99738 - 269.78098 - - - 84 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -109.496735 - 419.90024 - - - 85 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -354.4543 - 90.57742 - - - 86 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 123.8887 - -136.18655 - - - 87 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -215.86176 - 99.36972 - - - 88 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -100.81653 - 111.56064 - - - 89 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -183.81451 - 320.02475 - - - 90 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 87.316864 - -82.57358 - - - 91 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -59.60172 - -166.70242 - - - 92 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 179.87686 - -473.12268 - - - 93 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 87.612885 - -261.94946 - - - 94 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -124.80032 - 42.63687 - - - 95 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 448.6666 - 377.78897 - - - 96 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -265.8336 - -440.30582 - - - 97 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 209.5079 - -153.17294 - - - 98 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - 49.169155 - 398.49985 - - - 99 - 0 - 25.0 - C - 1 - 10.0 - 44 - 123 - 182 - -44.147953 - -165.659 - - - 1.0 - none - peer - - - 1.0 - none - peer - - - 1.0 - none - peer - - - 1.0 - none - peer - - - 1.0 - 5 - transit - - - 1.0 - 9 - transit - - - 1.0 - 19 - transit - - - 1.0 - 21 - transit - - - 1.0 - none - peer - - - 1.0 - none - peer - - - 1.0 - none - peer - - - 1.0 - 6 - transit - - - 1.0 - 11 - transit - - - 1.0 - 12 - transit - - - 1.0 - 42 - transit - - - 1.0 - none - peer - - - 1.0 - none - peer - - - 1.0 - 5 - transit - - - 1.0 - 6 - transit - - - 1.0 - 8 - transit - - - 1.0 - 12 - transit - - - 1.0 - 13 - transit - - - 1.0 - 17 - transit - - - 1.0 - 19 - transit - - - 1.0 - none - peer - - - 1.0 - 7 - transit - - - 1.0 - 17 - transit - - - 1.0 - 20 - transit - - - 1.0 - 36 - transit - - - 1.0 - 39 - transit - - - 1.0 - 40 - transit - - - 1.0 - 24 - transit - - - 1.0 - 7 - transit - - - 1.0 - 8 - transit - - - 1.0 - 9 - transit - - - 1.0 - 10 - transit - - - 1.0 - 18 - transit - - - 1.0 - 22 - transit - - - 1.0 - 34 - transit - - - 1.0 - 46 - transit - - - 1.0 - 48 - transit - - - 1.0 - 51 - transit - - - 1.0 - 62 - transit - - - 1.0 - 69 - transit - - - 1.0 - 70 - transit - - - 1.0 - 83 - transit - - - 1.0 - 84 - transit - - - 1.0 - 89 - transit - - - 1.0 - 90 - transit - - - 1.0 - none - peer - - - 1.0 - 19 - transit - - - 1.0 - 20 - transit - - - 1.0 - 25 - transit - - - 1.0 - 43 - transit - - - 1.0 - 47 - transit - - - 1.0 - 56 - transit - - - 1.0 - 57 - transit - - - 1.0 - 58 - transit - - - 1.0 - 68 - transit - - - 1.0 - 76 - transit - - - 1.0 - 77 - transit - - - 1.0 - 78 - transit - - - 1.0 - 79 - transit - - - 1.0 - 94 - transit - - - 1.0 - 97 - transit - - - 1.0 - 98 - transit - - - 1.0 - none - peer - - - 1.0 - none - peer - - - 1.0 - none - peer - - - 1.0 - none - peer - - - 1.0 - none - peer - - - 1.0 - none - peer - - - 1.0 - 11 - transit - - - 1.0 - 13 - transit - - - 1.0 - 14 - transit - - - 1.0 - 52 - transit - - - 1.0 - 61 - transit - - - 1.0 - 72 - transit - - - 1.0 - 74 - transit - - - 1.0 - 16 - transit - - - 1.0 - 18 - transit - - - 1.0 - 37 - transit - - - 1.0 - 44 - transit - - - 1.0 - 49 - transit - - - 1.0 - 53 - transit - - - 1.0 - 59 - transit - - - 1.0 - 67 - transit - - - 1.0 - 71 - transit - - - 1.0 - 75 - transit - - - 1.0 - 82 - transit - - - 1.0 - 85 - transit - - - 1.0 - 87 - transit - - - 1.0 - 88 - transit - - - 1.0 - 92 - transit - - - 1.0 - 96 - transit - - - 1.0 - none - peer - - - 1.0 - none - peer - - - 1.0 - none - peer - - - 1.0 - 30 - transit - - - 1.0 - none - peer - - - 1.0 - none - peer - - - 1.0 - 14 - transit - - - 1.0 - 15 - transit - - - 1.0 - 26 - transit - - - 1.0 - 60 - transit - - - 1.0 - none - peer - - - 1.0 - 24 - transit - - - 1.0 - 35 - transit - - - 1.0 - 81 - transit - - - 1.0 - 86 - transit - - - 1.0 - 91 - transit - - - 1.0 - 99 - transit - - - 1.0 - 24 - transit - - - 1.0 - 27 - transit - - - 1.0 - 28 - transit - - - 1.0 - 29 - transit - - - 1.0 - 31 - transit - - - 1.0 - 32 - transit - - - 1.0 - 38 - transit - - - 1.0 - 41 - transit - - - 1.0 - 50 - transit - - - 1.0 - 54 - transit - - - 1.0 - 63 - transit - - - 1.0 - 64 - transit - - - 1.0 - 73 - transit - - - 1.0 - 80 - transit - - - 1.0 - 93 - transit - - - 1.0 - 95 - transit - - - 1.0 - none - peer - - - 1.0 - none - peer - - - 1.0 - 66 - transit - - - 1.0 - 23 - transit - - - 1.0 - 65 - transit - - - 1.0 - none - peer - - - 1.0 - 33 - transit - - - 1.0 - 45 - transit - - - 1.0 - 55 - transit - - - 1.0 - none - peer - - - diff --git a/src/schema/graph_schema.json b/src/schema/graph_schema.json new file mode 100644 index 000000000..4ba0c8b66 --- /dev/null +++ b/src/schema/graph_schema.json @@ -0,0 +1,57 @@ +{ + "node": { + "type": "object", + "properties": { + "version": { + "type": "string", + "comment": "Bitcoin Core version with an available Warnet tank image on Dockerhub. May also be a GitHub repository with format user/repository:branch to build from source code"}, + "image": { + "type": "string", + "comment": "Bitcoin Core Warnet tank image on Dockerhub with the format repository/image:tag"}, + "bitcoin_config": { + "type": "string", + "default": "", + "comment": "A string of Bitcoin Core options in command-line format, e.g. '-debug=net -blocksonly'"}, + "tc_netem": { + "type": "string", + "comment": "A tc-netem command as a string beginning with 'tc qdisc add dev eth0 root netem'"}, + "exporter": { + "type": "boolean", + "default": false, + "comment": "Whether to attach a Prometheus data exporter to the tank"}, + "collect_logs": { + "type": "boolean", + "default": false, + "comment": "Whether to collect Bitcoin Core debug logs with Promtail"}, + "build_args": { + "type": "string", + "default": "", + "comment": "A string of configure options used when building Bitcoin Core from source code, e.g. '--without-gui --disable-tests'"}, + "ln": { + "type": "string", + "comment": "Attach a lightning network node of this implementation (currently only supports 'lnd')"}, + "ln-image": { + "type": "string", + "comment": "Specify a lightning network node image from Dockerhub with the format repository/image:tag"}, + "ln-cb-image": { + "type": "string", + "comment": "Specify a lnd Circuit Breaker image from Dockerhub with the format repository/image:tag"} + }, + "additionalProperties": false, + "oneOf": [ + {"required": ["version"]}, + {"required": ["image"]} + ], + "required": [] + }, + "edge": { + "type": "object", + "properties": { + "channel": { + "type": "number", + "comment": "Indicate that this edge is a lightning channel with this specified capacity"} + }, + "additionalProperties": false, + "required": [] + } +} diff --git a/src/schema/node_schema.json b/src/schema/node_schema.json deleted file mode 100644 index 108906c73..000000000 --- a/src/schema/node_schema.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "type": "object", - "properties": { - "degree": {"type": "number"}, - "x": {"type": "number"}, - "y": {"type": "number"}, - "version": {"type": "string"}, - "image": {"type": "string"}, - "bitcoin_config": {"type": "string", "default": ""}, - "tc_netem": {"type": "string"}, - "exporter": {"type": "boolean", "default": false}, - "collect_logs": {"type": "boolean", "default": false}, - "build_args": {"type": "string", "default": ""}, - "ln": {"type": "string"}, - "ln-image": {"type": "string"}, - "ln-cb-image": {"type": "string"} - }, - "additionalProperties": false, - "oneOf": [ - {"required": ["version"]}, - {"required": ["image"]} - ], - "required": [] -} diff --git a/src/warnet/server.py b/src/warnet/server.py index e0bbab6cb..3f9b30adb 100644 --- a/src/warnet/server.py +++ b/src/warnet/server.py @@ -28,7 +28,6 @@ from warnet.utils import ( create_cycle_graph, gen_config_dir, - load_schema, validate_graph_schema, ) from warnet.warnet import Warnet @@ -531,11 +530,10 @@ def graph_generate( raise ServerError(message=msg) from e def graph_validate(self, graph_path: str) -> str: - schema = load_schema() with open(graph_path) as f: - graph = nx.parse_graphml(f.read(), node_type=int) + graph = nx.parse_graphml(f.read(), node_type=int, force_multigraph=True) try: - validate_graph_schema(schema, graph) + validate_graph_schema(graph) except (jsonschema.ValidationError, jsonschema.SchemaError) as e: raise ServerError(message=f"Schema of {graph_path} is invalid: {e}") from e return f"Schema of {graph_path} is valid" diff --git a/src/warnet/tank.py b/src/warnet/tank.py index a4794b6c1..7d3306b59 100644 --- a/src/warnet/tank.py +++ b/src/warnet/tank.py @@ -61,7 +61,7 @@ def _parse_version(self, version): def parse_graph_node(self, node): # Dynamically parse properties based on the schema graph_properties = {} - for property, specs in self.warnet.node_schema["properties"].items(): + for property, specs in self.warnet.graph_schema["node"]["properties"].items(): value = node.get(property, specs.get("default")) if property == "version": self._parse_version(value) diff --git a/src/warnet/utils.py b/src/warnet/utils.py index 83963a376..fb38158bd 100644 --- a/src/warnet/utils.py +++ b/src/warnet/utils.py @@ -33,7 +33,7 @@ WEIGHTED_TAGS = [ tag for index, tag in enumerate(reversed(SUPPORTED_TAGS)) for _ in range(index + 1) ] -NODE_SCHEMA_PATH = SCHEMA / "node_schema.json" +GRAPH_SCHEMA_PATH = SCHEMA / "graph_schema.json" class NonErrorFilter(logging.Filter): @@ -433,16 +433,6 @@ def create_cycle_graph(n: int, version: str, bitcoin_conf: str | None, random_ve logger.debug(f"Added edge: {src_node}:{chosen_node}") logger.debug(f"Node {src_node} edges: {graph.edges(src_node)}") - # calculate degree - degree_dict = dict(graph.degree(graph.nodes())) - nx.set_node_attributes(graph, degree_dict, "degree") - - # add a default layout - pos = nx.spring_layout(graph) - for node in graph.nodes(): - graph.nodes[node]["x"] = float(pos[node][0]) - graph.nodes[node]["y"] = float(pos[node][1]) - # parse and process conf file conf_contents = "" if bitcoin_conf is not None: @@ -496,13 +486,16 @@ def convert_unsupported_attributes(graph: nx.Graph): def load_schema(): - with open(NODE_SCHEMA_PATH) as schema_file: + with open(GRAPH_SCHEMA_PATH) as schema_file: return json.load(schema_file) -def validate_graph_schema(node_schema: dict, graph: nx.Graph): +def validate_graph_schema(graph: nx.Graph): """ Validate a networkx.Graph against the node schema """ - for i in list(graph.nodes): - validate(instance=graph.nodes[i], schema=node_schema) + graph_schema = load_schema() + for n in list(graph.nodes): + validate(instance=graph.nodes[n], schema=graph_schema["node"]) + for e in list(graph.edges): + validate(instance=graph.edges[e], schema=graph_schema["edge"]) diff --git a/src/warnet/warnet.py b/src/warnet/warnet.py index 5e6ba2735..6bad4d5ac 100644 --- a/src/warnet/warnet.py +++ b/src/warnet/warnet.py @@ -36,7 +36,7 @@ def __init__(self, config_dir, backend, network_name: str): self.tanks: list[Tank] = [] self.deployment_file: Path | None = None self.backend = backend - self.node_schema = load_schema() + self.graph_schema = load_schema() def _warnet_dict_representation(self) -> dict: repr = {} @@ -106,8 +106,8 @@ def from_graph_file( with open(destination, "wb") as f: f.write(graph_file) self.network_name = network - self.graph = networkx.parse_graphml(graph_file.decode("utf-8"), node_type=int) - validate_graph_schema(self.node_schema, self.graph) + self.graph = networkx.parse_graphml(graph_file.decode("utf-8"), node_type=int, force_multigraph=True) + validate_graph_schema(self.graph) self.tanks_from_graph() logger.info(f"Created Warnet using directory {self.config_dir}") return self @@ -116,7 +116,7 @@ def from_graph_file( def from_graph(cls, graph, backend="compose", network="warnet"): self = cls(Path(), backend, network) self.graph = graph - validate_graph_schema(self.node_schema, self.graph) + validate_graph_schema(self.graph) self.tanks_from_graph() logger.info(f"Created Warnet using directory {self.config_dir}") return self @@ -127,8 +127,8 @@ def from_network(cls, network_name, backend="compose"): self = cls(config_dir, backend, network_name) self.network_name = network_name # Get network graph edges from graph file (required for network restarts) - self.graph = networkx.read_graphml(Path(self.config_dir / self.graph_name), node_type=int) - validate_graph_schema(self.node_schema, self.graph) + self.graph = networkx.read_graphml(Path(self.config_dir / self.graph_name), node_type=int, force_multigraph=True) + validate_graph_schema(self.graph) self.tanks_from_graph() for tank in self.tanks: tank._ipv4 = self.container_interface.get_tank_ipv4(tank.index) diff --git a/test/data/ln.graphml b/test/data/ln.graphml index 739d90d76..452a69ff5 100644 --- a/test/data/ln.graphml +++ b/test/data/ln.graphml @@ -5,7 +5,7 @@ - +