|
26 | 26 | from flask_jsonrpc.app import JSONRPC
|
27 | 27 | from flask_jsonrpc.exceptions import ServerError
|
28 | 28 | from warnet.utils import (
|
| 29 | + DEFAULT_TAG, |
29 | 30 | create_cycle_graph,
|
30 | 31 | gen_config_dir,
|
31 | 32 | validate_graph_schema,
|
@@ -170,6 +171,7 @@ def setup_rpc(self):
|
170 | 171 | self.jsonrpc.register(self.network_export)
|
171 | 172 | # Graph
|
172 | 173 | self.jsonrpc.register(self.graph_generate)
|
| 174 | + self.jsonrpc.register(self.graph_import) |
173 | 175 | self.jsonrpc.register(self.graph_validate)
|
174 | 176 | # Debug
|
175 | 177 | self.jsonrpc.register(self.generate_deployment)
|
@@ -529,6 +531,74 @@ def graph_generate(
|
529 | 531 | self.logger.error(msg)
|
530 | 532 | raise ServerError(message=msg) from e
|
531 | 533 |
|
| 534 | + def graph_import( |
| 535 | + self, |
| 536 | + infile: str, |
| 537 | + outfile: str |
| 538 | + ) -> str: |
| 539 | + with open(infile) as f: |
| 540 | + json_graph = json.loads(f.read()) |
| 541 | + |
| 542 | + # Start with a connected L1 graph with the right amount of tanks |
| 543 | + graph = create_cycle_graph(len(json_graph["nodes"]), version=DEFAULT_TAG, bitcoin_conf=None, random_version=False) |
| 544 | + |
| 545 | + # Initialize all the tanks with basic LN node configurations |
| 546 | + for index, n in enumerate(graph.nodes()): |
| 547 | + graph.nodes[n]["bitcoin_config"] = f"-uacomment=tank{index:06}" |
| 548 | + graph.nodes[n]["ln"] = "lnd" |
| 549 | + graph.nodes[n]["ln_cb_image"] = "pinheadmz/circuitbreaker:278737d" |
| 550 | + graph.nodes[n]["ln_config"] = "--protocol.wumbo-channels" |
| 551 | + |
| 552 | + # Save a map of LN pubkey -> Tank index |
| 553 | + ln_ids = {} |
| 554 | + for index, node in enumerate(json_graph["nodes"]): |
| 555 | + ln_ids[node["id"]] = index |
| 556 | + |
| 557 | + # Offset for edge IDs |
| 558 | + # Note create_cycle_graph() creates L1 edges all with the same id "0" |
| 559 | + L1_edges = len(graph.edges) |
| 560 | + |
| 561 | + # Insert LN channels |
| 562 | + # Ensure channels are in order by channel ID like lnd describegraph output |
| 563 | + sorted_edges = sorted(json_graph["edges"], key=lambda chan: int(chan['channel_id'])) |
| 564 | + for ln_index, channel in enumerate(sorted_edges): |
| 565 | + src = ln_ids[channel["node1_pub"]] |
| 566 | + tgt = ln_ids[channel["node2_pub"]] |
| 567 | + cap = int(channel["capacity"]) |
| 568 | + push = cap // 2 |
| 569 | + openp = f"--local_amt={cap} --push_amt={push}" |
| 570 | + srcp = "" |
| 571 | + tgtp = "" |
| 572 | + if channel["node1_policy"]: |
| 573 | + srcp += f" --base_fee_msat={channel['node1_policy']['fee_base_msat']}" |
| 574 | + srcp += f" --fee_rate_ppm={channel['node1_policy']['fee_rate_milli_msat']}" |
| 575 | + srcp += f" --time_lock_delta={channel['node1_policy']['time_lock_delta']}" |
| 576 | + srcp += f" --min_htlc_msat={channel['node1_policy']['min_htlc']}" |
| 577 | + srcp += f" --max_htlc_msat={push * 1000}" |
| 578 | + if channel["node2_policy"]: |
| 579 | + tgtp += f" --base_fee_msat={channel['node2_policy']['fee_base_msat']}" |
| 580 | + tgtp += f" --fee_rate_ppm={channel['node2_policy']['fee_rate_milli_msat']}" |
| 581 | + tgtp += f" --time_lock_delta={channel['node2_policy']['time_lock_delta']}" |
| 582 | + tgtp += f" --min_htlc_msat={channel['node2_policy']['min_htlc']}" |
| 583 | + tgtp += f" --max_htlc_msat={push * 1000}" |
| 584 | + |
| 585 | + graph.add_edge( |
| 586 | + src, |
| 587 | + tgt, |
| 588 | + key = ln_index+L1_edges, |
| 589 | + channel_open = openp, |
| 590 | + source_policy = srcp, |
| 591 | + target_policy = tgtp) |
| 592 | + |
| 593 | + if outfile: |
| 594 | + file_path = Path(outfile) |
| 595 | + nx.write_graphml(graph, file_path, named_key_ids=True) |
| 596 | + return f"Generated graph written to file: {outfile}" |
| 597 | + bio = BytesIO() |
| 598 | + nx.write_graphml(graph, bio, named_key_ids=True) |
| 599 | + xml_data = bio.getvalue() |
| 600 | + return xml_data.decode("utf-8") |
| 601 | + |
532 | 602 | def graph_validate(self, graph_path: str) -> str:
|
533 | 603 | with open(graph_path) as f:
|
534 | 604 | graph = nx.parse_graphml(f.read(), node_type=int, force_multigraph=True)
|
|
0 commit comments