1 module tagion.testbench.hashgraph.swap;
2 // Default import list for bdd
3 import std.algorithm;
4 import std.datetime;
5 import std.format;
6 import std.path : buildPath, extension, setExtension;
7 import std.stdio;
8 import std.typecons : Tuple;
9 import std.typecons;
10 import tagion.basic.Types : FileExtension;
11 import tagion.behaviour;
12 import tagion.crypto.SecureInterfaceNet : SecureNet;
13 import tagion.crypto.SecureNet : StdSecureNet;
14 import tagion.crypto.Types : Pubkey;
15 import tagion.hibon.Document;
16 import tagion.testbench.hashgraph.hashgraph_test_network;
17 import tagion.testbench.tools.Environment;
18 import tagion.utils.Miscellaneous : cutHex;
19 import tagion.utils.Miscellaneous : cutHex;
20 
21 enum feature = Feature(
22             "Hashgraph Swapping",
23             ["This test is meant to test that a node can be swapped out at a specific epoch"]);
24 
25 alias FeatureContext = Tuple!(
26         NodeSwap, "NodeSwap",
27         FeatureGroup*, "result"
28 );
29 
30 @safe @Scenario("node swap",
31         [])
32 class NodeSwap {
33 
34     string[] node_names;
35     TestNetwork network;
36     string module_path;
37     uint MAX_CALLS;
38     Pubkey offline_key;
39     enum new_node = "NEW_NODE";
40 
41     this(string[] node_names, const uint calls, const string module_path) {
42         this.node_names = node_names;
43         this.module_path = module_path;
44         MAX_CALLS = cast(uint) node_names.length * calls;
45     }
46 
47     @Given("i have a hashgraph testnetwork with n number of nodes.")
48     Document nodes() {
49 
50         network = new TestNetwork(node_names);
51         network.networks.byValue.each!((ref _net) => _net._hashgraph.scrap_depth = 0);
52         network.random.seed(123456789);
53         writeln(network.random);
54 
55         network.global_time = SysTime.fromUnixTime(1_614_355_286);
56         offline_key = Pubkey(network.channels[1]);
57 
58         auto net = new StdSecureNet();
59         net.generateKeyPair(format("very secret %s", new_node));
60         TestRefinement.swap = TestRefinement.Swap(offline_key, net.pubkey, 10);
61         return result_ok;
62     }
63 
64     @Given("that all nodes knows a node should be swapped.")
65     Document swapped() {
66         return result_ok;
67     }
68 
69     @When("a node has created a specific amount of epochs, it swaps in the new node.")
70     Document node() {
71         foreach (i; 0 .. MAX_CALLS) {
72             const channel_number = network.random.value(0, network.channels.length);
73             const channel = network.channels[channel_number];
74             network.current = Pubkey(channel);
75             auto current = network.networks[channel];
76 
77             if (TestRefinement.epoch_events.length == node_names.length && TestRefinement.epoch_events
78                     .byValue
79                     .map!((ep) => ep.length)
80                     .all!((ep_length) => ep_length > 2)) {
81                 break;
82             }
83             (() @trusted { current.call; })();
84         }
85 
86         TestNetwork.TestGossipNet.online_states[offline_key] = !TestNetwork.TestGossipNet.online_states[offline_key];
87         // TestNetwork.TestGossipNet.online_states[offline_key] = false;
88 
89         foreach (i; 0 .. MAX_CALLS) {
90             const channel_number = network.random.value(0, network.channels.length);
91             const channel = network.channels[channel_number];
92             network.current = Pubkey(channel);
93             auto current = network.networks[channel];
94 
95             (() @trusted { current.call; })();
96         }
97 
98         writefln("ADDING NEW NODE");
99         network.addNode(node_names.length, new_node, Yes.joining);
100 
101         foreach (i; 0 .. MAX_CALLS) {
102             const channel_number = network.random.value(0, network.channels.length);
103             const channel = network.channels[channel_number];
104             network.current = Pubkey(channel);
105             auto current = network.networks[channel];
106 
107             (() @trusted { current.call; })();
108         }
109 
110         return result_ok;
111     }
112 
113     @Then("the new node should come in graph.")
114     Document graph() {
115         return result_ok;
116     }
117 
118     @Then("compare the epochs created from the point of the swap.")
119     Document swap() {
120         return result_ok;
121     }
122 
123     @Then("stop the network.")
124     Document _network() {
125         Pubkey[string] node_labels;
126         foreach (channel, _net; network.networks) {
127             node_labels[_net._hashgraph.name] = channel;
128         }
129         foreach (_net; network.networks) {
130             const filename = buildPath(module_path, "ripple-" ~ _net._hashgraph.name
131                     .setExtension(FileExtension.hibon));
132             writeln(filename);
133             _net
134                 ._hashgraph.fwrite(filename, node_labels);
135         }
136         return result_ok;
137     }
138 
139 }