1 module tagion.testbench.services.epoch_creator; 2 // Default import list for bdd 3 import core.thread; 4 import core.time; 5 import std.algorithm; 6 import std.array; 7 import std.format; 8 import std.range : empty; 9 import std.stdio; 10 import std.typecons : Tuple; 11 import tagion.actor; 12 import tagion.actor.exceptions; 13 import tagion.behaviour; 14 import tagion.crypto.SecureInterfaceNet : SecureNet; 15 import tagion.crypto.SecureNet : StdSecureNet; 16 import tagion.crypto.Types : Pubkey; 17 import tagion.gossip.AddressBook : NodeAddress, addressbook; 18 import tagion.hashgraph.HashGraphBasic; 19 import tagion.hibon.Document; 20 import tagion.hibon.HiBON; 21 import tagion.hibon.HiBONJSON; 22 import tagion.logger.LogRecords : LogInfo; 23 import tagion.logger.Logger; 24 import tagion.services.epoch_creator; 25 import tagion.services.messages; 26 import tagion.services.monitor; 27 import tagion.services.options; 28 import tagion.services.options : NetworkMode; 29 import tagion.testbench.actor.util; 30 import tagion.testbench.tools.Environment; 31 import tagion.utils.Miscellaneous : cutHex; 32 import tagion.utils.pretend_safe_concurrency; 33 34 enum feature = Feature( 35 "EpochCreator service", 36 [ 37 "This service is responsbile for resolving the Hashgraph and producing a consensus ordered list of events, an Epoch." 38 ]); 39 40 alias FeatureContext = Tuple!( 41 SendPayloadAndCreateEpoch, "SendPayloadAndCreateEpoch", 42 FeatureGroup*, "result" 43 ); 44 45 @safe @Scenario("Send payload and create epoch", 46 []) 47 class SendPayloadAndCreateEpoch { 48 struct Node { 49 shared(StdSecureNet) node_net; 50 string name; 51 EpochCreatorOptions opts; 52 MonitorOptions monitor_opts; 53 } 54 55 immutable(size_t) number_of_nodes; 56 57 Node[] nodes; 58 ActorHandle[] handles; 59 Document send_payload; 60 61 this(EpochCreatorOptions epoch_creator_options, MonitorOptions monitor_opts, immutable size_t number_of_nodes) { 62 import tagion.services.options; 63 64 this.number_of_nodes = number_of_nodes; 65 66 addressbook.number_of_active_nodes = number_of_nodes; 67 foreach (i; 0 .. number_of_nodes) { 68 immutable prefix = format("Node_%s", i); 69 immutable task_names = TaskNames(prefix); 70 auto net = new StdSecureNet(); 71 net.generateKeyPair(task_names.epoch_creator); 72 shared shared_net = (() @trusted => cast(shared) net)(); 73 scope (exit) { 74 net = null; 75 } 76 writefln("node task name %s", task_names.epoch_creator); 77 auto monitor_local_options = monitor_opts; 78 nodes ~= Node(shared_net, task_names.epoch_creator, epoch_creator_options, monitor_local_options); 79 addressbook[net.pubkey] = NodeAddress(task_names.epoch_creator); 80 } 81 82 } 83 84 @Given("I have 5 nodes and start them in mode0") 85 Document mode0() @trusted { 86 register("epoch_creator_tester", thisTid); 87 88 foreach (n; nodes) { 89 handles ~= spawn!EpochCreatorService( 90 cast(immutable) n.name, 91 cast(immutable) n.opts, 92 NetworkMode.INTERNAL, 93 number_of_nodes, 94 n.node_net, 95 cast(immutable) n.monitor_opts, 96 TaskNames(), 97 ); 98 } 99 100 waitforChildren(Ctrl.ALIVE, 15.seconds); 101 102 return result_ok; 103 } 104 105 @When("i sent a payload to node0") 106 Document node0() @trusted { 107 log.registerSubscriptionTask("epoch_creator_tester"); 108 109 submask.subscribe("epoch_creator/epoch_created"); 110 111 import tagion.hibon.Document; 112 import tagion.hibon.HiBON; 113 114 auto h = new HiBON; 115 h["node0"] = "TEST PAYLOAD"; 116 send_payload = Document(h); 117 writefln("SENDING TEST DOC"); 118 handles[1].send(Payload(), const Document(h)); 119 120 return result_ok; 121 } 122 123 @Then("all the nodes should create an epoch containing the payload") 124 Document payload() { 125 writefln("BEFORE TIMEOUT"); 126 127 bool stop; 128 const max_attempts = 30; 129 uint counter; 130 do { 131 const received = receiveOnlyTimeout!(LogInfo, const(Document))(27.seconds); 132 check(received[0].symbol_name.canFind("epoch_succesful"), "Event should have been epoch_succesful"); 133 const epoch = received[1]; 134 135 import tagion.hashgraph.Refinement : FinishedEpoch; 136 import tagion.hibon.HiBONRecord; 137 138 check(epoch.isRecord!FinishedEpoch, "received event should be an FinishedEpoch record"); 139 const events = FinishedEpoch(epoch).events; 140 writefln("Received epoch %s \n event_length %s", epoch.toPretty, events.length); 141 142 if (events.length == 1) { 143 const received_payload = events[0].event_body.payload; 144 check(received_payload == send_payload, "Payloads not the same"); 145 stop = true; 146 } 147 counter++; 148 } 149 while (!stop && counter < max_attempts); 150 check(stop, "no epoch found"); 151 152 foreach (handle; handles) { 153 handle.send(Sig.STOP); 154 } 155 156 waitforChildren(Ctrl.END); 157 return result_ok; 158 } 159 }