1 module tagion.testbench.hashgraph.synchron_network; 2 // Default import list for bdd 3 import core.sys.posix.sys.resource; 4 import std.algorithm; 5 import std.array; 6 import std.array; 7 import std.conv; 8 import std.datetime; 9 import std.exception; 10 import std.format; 11 import std.functional : toDelegate; 12 import std.path : buildPath; 13 import std.path : extension, setExtension; 14 import std.range; 15 import std.stdio; 16 import std.typecons : Tuple; 17 import tagion.basic.Types : FileExtension; 18 import tagion.basic.Types; 19 import tagion.basic.basic; 20 import tagion.behaviour; 21 import tagion.crypto.Types : Pubkey; 22 import tagion.hashgraph.Event; 23 import tagion.hashgraph.HashGraph; 24 import tagion.hashgraph.HashGraphBasic; 25 import tagion.hashgraph.Refinement; 26 import tagion.hashgraphview.Compare; 27 import tagion.hibon.Document; 28 import tagion.hibon.HiBONJSON; 29 import tagion.testbench.hashgraph.hashgraph_test_network; 30 import tagion.testbench.tools.Environment; 31 import tagion.utils.Miscellaneous : cutHex; 32 33 enum feature = Feature( 34 "Bootstrap of hashgraph", 35 []); 36 37 alias FeatureContext = Tuple!( 38 StartNetworkWithNAmountOfNodes, "StartNetworkWithNAmountOfNodes", 39 FeatureGroup*, "result" 40 ); 41 42 @safe @Scenario("Start network with n amount of nodes", 43 []) 44 class StartNetworkWithNAmountOfNodes { 45 string[] node_names; 46 TestNetwork network; 47 string module_path; 48 uint MAX_CALLS; 49 this(string[] node_names, const uint calls, const(string) module_path) { 50 this.node_names = node_names; 51 this.module_path = module_path; 52 MAX_CALLS = cast(uint) node_names.length * calls; 53 } 54 55 bool coherent; 56 57 @Given("i have a HashGraph TestNetwork with n number of nodes") 58 Document nodes() { 59 rlimit limit; 60 (() @trusted { getrlimit(RLIMIT_STACK, &limit); })(); 61 writefln("RESOURCE LIMIT = %s", limit); 62 63 network = new TestNetwork(node_names); 64 network.networks.byValue.each!((ref _net) => _net._hashgraph.scrap_depth = 0); 65 network.random.seed(123456789); 66 writeln(network.random); 67 68 network.global_time = SysTime.fromUnixTime(1_614_355_286); 69 70 return result_ok; 71 } 72 73 @When("the network has started") 74 Document started() { 75 76 foreach (channel; network.channels) { 77 auto current = network.networks[channel]; 78 (() @trusted { current.call; })(); 79 } 80 return result_ok; 81 82 } 83 84 @When("all nodes are sending ripples") 85 Document ripples() { 86 foreach (i; 0 .. MAX_CALLS) { 87 const channel_number = network.random.value(0, network.channels.length); 88 const channel = network.channels[channel_number]; 89 auto current = network.networks[channel]; 90 (() @trusted { current.call; })(); 91 92 // printStates(network); 93 if (network.allInGraph) { 94 coherent = true; 95 break; 96 } 97 } 98 99 return result_ok; 100 } 101 102 @When("all nodes are coherent") 103 Document _coherent() { 104 check(coherent, "Nodes not coherent"); 105 return result_ok; 106 } 107 108 @Then("wait until the first epoch") 109 Document epoch() @trusted { 110 { 111 uint i = 0; 112 while (i < MAX_CALLS) { 113 114 const channel_number = network.random.value(0, network.channels.length); 115 network.current = Pubkey(network.channels[channel_number]); 116 auto current = network.networks[network.current]; 117 (() @trusted { current.call; })(); 118 119 // if (network.epoch_events.length == node_names.length) { 120 // // all nodes have created at least one epoch 121 // break; 122 // } 123 // printStates(network); 124 i++; 125 } 126 check(TestRefinement.epoch_events.length == node_names.length, 127 format("Max calls %d reached, not all nodes have created epochs only %d", 128 MAX_CALLS, TestRefinement.epoch_events.length)); 129 } 130 131 // compare ordering 132 auto names = network.networks.byValue 133 .map!((net) => net._hashgraph.name) 134 .array.dup 135 .sort 136 .array; 137 138 HashGraph[string] hashgraphs; 139 foreach (net; network.networks) { 140 hashgraphs[net._hashgraph.name] = net._hashgraph; 141 } 142 foreach (i, name_h1; names[0 .. $ - 1]) { 143 const h1 = hashgraphs[name_h1]; 144 foreach (name_h2; names[i + 1 .. $]) { 145 const h2 = hashgraphs[name_h2]; 146 auto comp = Compare(h1, h2, toDelegate(&event_error)); 147 // writefln("%s %s round_offset=%d order_offset=%d", 148 // h1.name, h2.name, comp.round_offset, comp.order_offset); 149 const result = comp.compare; 150 check(result, format("HashGraph %s and %s is not the same", h1.name, h2.name)); 151 } 152 } 153 154 // compare epochs 155 foreach (i, compare_epoch; TestRefinement.epoch_events.byKeyValue.front.value) { 156 auto compare_events = compare_epoch 157 .events 158 .map!(e => cast(Buffer) e.event_package.fingerprint) 159 .array; 160 auto compare_round_fingerprint = hashLastDecidedRound(compare_epoch.decided_round); 161 162 // compare_events.sort!((a,b) => a < b); 163 // compare_events.each!writeln; 164 // writefln("%s", compare_events.map!(f => f.cutHex)); 165 foreach (channel_epoch; TestRefinement.epoch_events.byKeyValue) { 166 // writefln("epoch: %s", i); 167 if (channel_epoch.value.length - 1 < i) { 168 break; 169 } 170 auto events = channel_epoch.value[i] 171 .events 172 .map!(e => cast(Buffer) e.event_package.fingerprint) 173 .array; 174 auto channel_round_fingerprint = hashLastDecidedRound(compare_epoch.decided_round); 175 // events.sort!((a,b) => a < b); 176 177 // writefln("%s", events.map!(f => f.cutHex)); 178 // events.each!writeln; 179 // writefln("channel %s time: %s", channel_epoch.key.cutHex, channel_epoch.value[i].epoch_time); 180 181 check(compare_events.length == events.length, "event_packages not the same length"); 182 183 const sameEvents = equal(compare_events.sort, events.sort); 184 check(sameEvents, "events lists does not contain same events"); 185 186 const isSame = equal(compare_events, events); 187 // writefln("isSame: %s", isSame); 188 check(isSame, "event_packages not the same"); 189 190 const sameRoundFingerprints = equal(compare_round_fingerprint.fingerprints, channel_round_fingerprint 191 .fingerprints); 192 check(sameRoundFingerprints, "round fingerprints not the same"); 193 } 194 } 195 196 return result_ok; 197 } 198 199 @Then("stop the network") 200 Document _network() { 201 Pubkey[string] node_labels; 202 foreach (channel, _net; network.networks) { 203 node_labels[_net._hashgraph.name] = channel; 204 } 205 foreach (_net; network.networks) { 206 const filename = buildPath(module_path, "ripple-" ~ _net._hashgraph.name.setExtension(FileExtension.hibon)); 207 writeln(filename); 208 _net._hashgraph.fwrite(filename, node_labels); 209 } 210 return result_ok; 211 } 212 213 }