1 /// HashGraph Event
2 module tagion.hashgraph.Event;
3 
4 //import std.stdio;
5 
6 import std.datetime; // Date, DateTime
7 import std.algorithm.iteration : cache, each, filter, fold, joiner, map, reduce;
8 import std.algorithm.searching : all, any, canFind, count, until;
9 import std.algorithm.sorting : sort;
10 import std.array : array;
11 import std.conv;
12 import std.format;
13 import std.range;
14 import std.range : enumerate, tee;
15 import std.range.primitives : isBidirectionalRange, isForwardRange, isInputRange, walkLength;
16 import std.stdio;
17 import std.traits : ReturnType, Unqual;
18 import std.traits;
19 import std.typecons;
20 import tagion.basic.Debug;
21 import tagion.basic.Types : Buffer;
22 import tagion.basic.basic : EnumText, basename, buf_idup, this_dot;
23 import tagion.crypto.Types : Pubkey;
24 import tagion.hashgraph.HashGraph : HashGraph;
25 import tagion.hashgraph.HashGraphBasic : EvaPayload, EventBody, EventPackage, Tides, higher, isAllVotes, isMajority;
26 import tagion.hashgraph.Round;
27 import tagion.hashgraphview.EventMonitorCallbacks;
28 import tagion.hibon.Document : Document;
29 import tagion.hibon.HiBON : HiBON;
30 import tagion.hibon.HiBONRecord;
31 import tagion.logger.Logger;
32 import tagion.utils.BitMask : BitMask;
33 import tagion.utils.Miscellaneous;
34 import tagion.utils.StdTime;
35 
36 /// HashGraph Event
37 @safe
38 class Event {
39     package static bool scrapping;
40 
41     import tagion.basic.ConsensusExceptions;
42 
43     alias check = Check!EventConsensusException;
44     protected static uint _count;
45 
46     package Event[] _youngest_son_ancestors;
47 
48     package int pseudo_time_counter;
49 
50     package {
51         // This is the internal pointer to the connected Event's
52         Event _mother;
53         Event _father;
54         Event _daughter;
55         Event _son;
56 
57         int _order;
58         // The withness mask contains the mask of the nodes
59         // Which can be seen by the next rounds witness
60         Witness _witness;
61         BitMask _round_seen_mask;
62     }
63 
64     @nogc
65     static uint count() nothrow {
66         return _count;
67     }
68 
69     bool error;
70 
71     Topic topic = Topic("hashgraph_event");
72 
73     /**
74      * Builds an event from an eventpackage
75      * Params:
76      *   epack = event-package to build from
77      *   hashgraph = the hashgraph which produce the event
78      */
79     package this(
80             immutable(EventPackage)* epack,
81             HashGraph hashgraph,
82     )
83     in (epack !is null)
84     do {
85         event_package = epack;
86         this.id = hashgraph.next_event_id;
87         this.node_id = hashgraph.getNode(channel).node_id;
88         _count++;
89     }
90 
91     protected ~this() {
92         _count--;
93     }
94 
95     invariant {
96         if (!scrapping && this !is null) {
97             if (_mother) {
98                 // assert(!_witness_mask[].empty);
99                 assert(_mother._daughter is this);
100                 assert(
101                         event_package.event_body.altitude - _mother
102                         .event_package.event_body.altitude is 1);
103                 assert(_order is int.init || (_order - _mother._order > 0));
104             }
105             if (_father) {
106                 pragma(msg, "fixme(bbh) this test should be reimplemented once new witness def works");
107                 // assert(_father._son is this, "fathers is not me");
108                 assert(_order is int.init || (_order - _father._order > 0));
109             }
110         }
111     }
112 
113     /**
114      * The witness event will point to the witness object
115      * This object contains infomation about the voting etc. for the witness event
116      */
117     @safe
118     class Witness {
119         protected static uint _count;
120         @nogc static uint count() nothrow {
121             return _count;
122         }
123 
124         private {
125             BitMask _vote_on_earliest_witnesses;
126             BitMask _prev_strongly_seen_witnesses;
127             BitMask _prev_seen_witnesses;
128         }
129 
130         /**
131          * Contsruct a witness of an event
132          * Params:
133          *   owner_event = the event which is voted to be a witness
134          *   seeing_witness_in_previous_round_mask = The witness seen from this event to the privious witness.
135          */
136         this(Event owner_event, ulong node_size) nothrow
137         in {
138             assert(owner_event);
139         }
140         do {
141             _count++;
142         }
143 
144         ~this() {
145             _count--;
146         }
147 
148     }
149 
150     static EventMonitorCallbacks callbacks;
151 
152     // The altitude increases by one from mother to daughter
153     immutable(EventPackage*) event_package;
154 
155     /**
156   * The rounds see forward from this event
157   * Returns:  round seen mask
158   */
159     const(BitMask) round_seen_mask() const pure nothrow @nogc {
160         return _round_seen_mask;
161     }
162 
163     package {
164         Round _round; /// The where the event has been created
165         Round _round_received; /// The round in which the event has been voted to be received
166         BitMask _round_received_mask; /// Voting mask for the received rounds
167     }
168 
169     /**
170      * Attach the mother round to this event
171      * Params:
172      *   hashgraph = the graph which produces this event
173      */
174     package void attach_round(HashGraph hashgraph) pure nothrow {
175         if (!_round) {
176             _round = _mother._round;
177         }
178     }
179 
180     immutable uint id;
181 
182     /**
183     *  Makes the event a witness  
184     */
185     package void witness_event(ulong node_size) nothrow
186     in {
187         assert(!_witness);
188     }
189     do {
190         _witness = new Witness(this, node_size);
191         _youngest_son_ancestors = new Event[node_size];
192         _youngest_son_ancestors[node_id] = this;
193     }
194 
195     immutable size_t node_id; /// Node number of the event
196 
197     void initializeOrder() pure nothrow @nogc {
198         if (order is int.init) {
199             _order = -1;
200         }
201     }
202 
203     /**
204       * Connect the event to the hashgraph
205       * Params:
206       *   hashgraph = event owner 
207       */
208     package final void connect(HashGraph hashgraph)
209     in {
210         assert(hashgraph.areWeInGraph);
211     }
212     out {
213         assert(event_package.event_body.mother && _mother || !_mother);
214         assert(event_package.event_body.father && _father || !_father);
215     }
216     do {
217         if (connected) {
218             return;
219         }
220         scope (exit) {
221             if (_mother) {
222                 Event.check(this.altitude - _mother.altitude is 1,
223                         ConsensusFailCode.EVENT_ALTITUDE);
224                 Event.check(channel == _mother.channel,
225                         ConsensusFailCode.EVENT_MOTHER_CHANNEL);
226             }
227             hashgraph.front_seat(this);
228             if (Event.callbacks) {
229                 Event.callbacks.connect(this);
230             }
231             // refinement
232             hashgraph.refinement.payload(event_package);
233         }
234 
235         _mother = hashgraph.register(event_package.event_body.mother);
236         if (!_mother) {
237             if (!isEva && !hashgraph.joining && !hashgraph.rounds.isEventInLastDecidedRound(this)) {
238                 check(false, ConsensusFailCode.EVENT_MOTHER_LESS);
239             }
240             return;
241         }
242 
243         check(!_mother._daughter, ConsensusFailCode.EVENT_MOTHER_FORK);
244         _mother._daughter = this;
245         _father = hashgraph.register(event_package.event_body.father);
246         _round = ((father) && higher(father.round.number, mother.round.number)) ? _father._round : _mother._round;
247         if (_father) {
248             check(!_father._son, ConsensusFailCode.EVENT_FATHER_FORK);
249             _father._son = this;
250         }
251         if (callbacks) {
252             callbacks.round(this);
253         }
254         _order = (_father && higher(_father.order, _mother.order)) ? _father.order + 1 : _mother.order + 1;
255 
256         // pseudo_time_counter = (_mother._witness) ? 0 : _mother.pseudo_time_counter;
257         // if (_father) { pseudo_time_counter += 1; }
258         pseudo_time_counter = (_mother._father) ? _mother.pseudo_time_counter + 1 : _mother.pseudo_time_counter;
259         with (hashgraph) {
260             log(topic, received_order_statistic.stringof, received_order_statistic);
261         }
262 
263         calc_youngest_son_ancestors(hashgraph);
264         BitMask strongly_seen_nodes = calc_strongly_seen_nodes(hashgraph);
265         if (strongly_seen_nodes.isMajority(hashgraph)) {
266             hashgraph._rounds.next_round(this);
267         }
268 
269         // if (hashgraph.__debug_print) {
270         //     __write("EVENT: %s, %s", id, _youngest_son_ancestors.filter!(e => e !is null).map!(e => e.id));
271         // }
272         if (!higher(round.number, mother.round.number)) {
273             return;
274         }
275 
276         _witness = new Witness(this, hashgraph.node_size);
277 
278         pseudo_time_counter = 0;
279 
280         _witness._prev_strongly_seen_witnesses = strongly_seen_nodes;
281         _witness._prev_seen_witnesses = BitMask(_youngest_son_ancestors.map!(e => (e !is null && !higher(round.number - 1, e
282                 .round.number))));
283         if (!strongly_seen_nodes.isMajority(hashgraph)) {
284             _round.add(this);
285         }
286         with (hashgraph) {
287             log(topic, strong_seeing_statistic.stringof, strong_seeing_statistic);
288         }
289         if (callbacks) {
290             callbacks.witness(this);
291         }
292         foreach (i; 0 .. hashgraph.node_size) {
293             calc_vote(hashgraph, i);
294         }
295         // if (hashgraph.__debug_print) {
296         //     __write("EVENT: %s, Youngest_ancestors: %s", id, _youngest_son_ancestors.filter!(e => e !is null).map!(e => e.id));
297         // }
298     }
299 
300     private BitMask calc_strongly_seen_nodes(const HashGraph hashgraph) {
301         auto see_through_matrix = _youngest_son_ancestors
302             .filter!(e => e !is null && e.round is round)
303             .map!(e => e._youngest_son_ancestors
304                     .map!(e => e !is null && e.round is round));
305 
306         scope strongly_seen_votes = new size_t[hashgraph.node_size];
307         see_through_matrix.each!(row => row.enumerate.each!(elm => strongly_seen_votes[elm.index] += elm.value));
308         return BitMask(strongly_seen_votes.map!(votes => hashgraph.isMajority(votes)));
309     }
310 
311     private void calc_youngest_son_ancestors(const HashGraph hashgraph) {
312         if (!_father) {
313             _youngest_son_ancestors = _mother._youngest_son_ancestors;
314             return;
315         }
316 
317         _youngest_son_ancestors = _mother._youngest_son_ancestors.dup();
318         _youngest_son_ancestors[node_id] = this;
319         iota(hashgraph.node_size)
320             .filter!(node_id => _father._youngest_son_ancestors[node_id]!is null)
321             .filter!(node_id => _youngest_son_ancestors[node_id] is null || _father._youngest_son_ancestors[node_id]
322                     .order > _youngest_son_ancestors[node_id].order)
323             .each!(node_id => _youngest_son_ancestors[node_id] = _father._youngest_son_ancestors[node_id]);
324     }
325 
326     package void calc_vote(HashGraph hashgraph, size_t vote_node_id) {
327         Round voting_round = hashgraph._rounds.voting_round_per_node[vote_node_id];
328         Event voting_event = voting_round._events[vote_node_id];
329 
330         if (!higher(round.number, voting_round.number)) {
331             return;
332         }
333         if (voting_round.number + 1 == round.number) {
334             _witness._vote_on_earliest_witnesses[vote_node_id] = _witness._prev_seen_witnesses[vote_node_id];
335             return;
336         }
337         if (voting_event is null) {
338             hashgraph._rounds.vote(hashgraph, vote_node_id);
339             return;
340         }
341         auto votes = _witness._prev_strongly_seen_witnesses[].map!(
342                 i => round.previous.events[i]._witness._vote_on_earliest_witnesses[vote_node_id]);
343         const yes_votes = votes.count;
344         const no_votes = votes.walkLength - yes_votes;
345         _witness._vote_on_earliest_witnesses[vote_node_id] = (yes_votes >= no_votes);
346         if (hashgraph.isMajority(yes_votes) || hashgraph.isMajority(no_votes)) {
347             voting_round.famous_mask[vote_node_id] = (yes_votes >= no_votes);
348             hashgraph._rounds.vote(hashgraph, vote_node_id);
349         }
350     }
351 
352     /**
353      * Disconnect this event from hashgraph
354      * Used to remove events which are no longer needed 
355      * Params:
356      *   hashgraph = event owner
357      */
358     final package void disconnect(HashGraph hashgraph) nothrow @trusted
359     in {
360         assert(!_mother, "Event with a mother can not be disconnected");
361     }
362     do {
363         hashgraph.eliminate(fingerprint);
364         if (_witness) {
365             _round.remove(this);
366             _witness.destroy;
367             _witness = null;
368         }
369         if (_daughter) {
370             _daughter._mother = null;
371         }
372         if (_son) {
373             _son._father = null;
374         }
375         _daughter = _son = null;
376     }
377 
378     const bool sees(Event b) pure {
379 
380         if (_youngest_son_ancestors[b.node_id] is null) {
381             return false;
382         }
383         if (!higher(b.order, _youngest_son_ancestors[b.node_id].order)) {
384             return true;
385         }
386         if (node_id == b.node_id && !higher(b.order, order)) {
387             return true;
388         }
389 
390         auto see_through_candidates = b[].retro
391             .until!(e => e.pseudo_time_counter != b.pseudo_time_counter)
392             .filter!(e => e._son)
393             .map!(e => e._son);
394 
395         foreach (e; see_through_candidates) {
396             if (_youngest_son_ancestors[e.node_id] is null) {
397                 continue;
398             }
399             if (!higher(e.order, _youngest_son_ancestors[e.node_id].order)) {
400                 return true;
401             }
402         }
403         return false;
404     }
405 
406     /**
407      * Mother event
408      * Throws: EventException if the mother has been grounded
409      * Returns: mother event 
410      */
411     final const(Event) mother() const pure {
412         Event.check(!isGrounded, ConsensusFailCode.EVENT_MOTHER_GROUNDED);
413         return _mother;
414     }
415 
416     /**
417      * Mother event
418      * Throws: EventException if the mother has been grounded
419      * Returns: mother event 
420      */
421     final const(Event) father() const pure {
422         Event.check(!isGrounded, ConsensusFailCode.EVENT_FATHER_GROUNDED);
423         return _father;
424     }
425 
426     @nogc pure nothrow const final {
427         /**
428   * The event-body from this event 
429   * Returns: event-body
430   */
431         ref const(EventBody) event_body() {
432             return event_package.event_body;
433         }
434 
435         /**
436      * The recived round for this event
437      * Returns: received round
438      */
439         const(Round) round_received() {
440             return _round_received;
441         }
442 
443         /**
444      * Channel from which this event has received
445      * Returns: channel
446      */
447         immutable(Pubkey) channel() {
448             return event_package.pubkey;
449         }
450 
451         /**
452      * Get the mask of the received rounds
453      * Returns: received round mask 
454      */
455         const(BitMask) round_received_mask() {
456             return _round_received_mask;
457         }
458 
459         /**
460      * Checks if this event is the last one on this node
461      * Returns: true if the event is in front
462      */
463         bool isFront() {
464             return _daughter is null;
465         }
466 
467         /**
468      * Check if an evnet has around 
469      * Returns: true if an round exist for this event
470      */
471 
472         bool hasRound() {
473             return (_round !is null);
474         }
475 
476         /**
477      * Round of this event
478      * Returns: round
479      */
480         const(Round) round()
481         out (result) {
482             assert(result, "Round must be set before this function is called");
483         }
484         do {
485             return _round;
486         }
487         /**
488      * Gets the witness infomatioin of the event
489      * Returns: 
490      * if this event is a witness the witness is returned
491      * else null is returned
492      */
493         const(Witness) witness() {
494             return _witness;
495         }
496 
497         bool isWitness() {
498             return _witness !is null;
499         }
500 
501         bool isFamous() {
502             return isWitness && round.famous_mask[node_id];
503         }
504         /**
505          * Get the altitude of the event
506          * Returns: altitude
507          */
508         immutable(int) altitude() {
509             return event_package.event_body.altitude;
510         }
511 
512         /**
513           * Is this event owner but this node 
514           * Returns: true if the evnet is owned
515           */
516         bool nodeOwner() const pure nothrow @nogc {
517             return node_id is 0;
518         }
519 
520         /**
521          * Gets the event order number 
522          * Returns: order
523          */
524         int order() const pure nothrow @nogc {
525             return _order;
526         }
527 
528         /**
529        * Checks if the event is connected in the graph 
530        * Returns: true if the event is corrected 
531        */
532         bool connected() const pure @nogc {
533             return (_mother !is null);
534         }
535 
536         /**
537        * Gets the daughter event
538        * Returns: the daughter
539        */
540 
541         const(Event) daughter() {
542             return _daughter;
543         }
544 
545         /**
546        * Gets the son of this event
547        * Returns: the son
548        */
549         const(Event) son() {
550             return _son;
551         }
552         /**
553        * Get 
554        * Returns: 
555        */
556         const(Document) payload() {
557             return event_package.event_body.payload;
558         }
559 
560         ref const(EventBody) eventbody() {
561             return event_package.event_body;
562         }
563 
564         //True if Event contains a payload or is the initial Event of its creator
565         bool containPayload() {
566             return !payload.empty;
567         }
568 
569         // is true if the event does not have a mother or a father
570         bool isEva()
571         out (result) {
572             if (result) {
573                 assert(event_package.event_body.father is null);
574             }
575         }
576         do {
577             return (_mother is null) && (event_package.event_body.mother is null);
578         }
579 
580         /// A father less event is an event where the ancestor event is connect to an Eva event without an father event
581         /// An Eva is is also defined as han father less event
582         /// This also means that the event has not valid order and must not be included in the epoch order.
583         bool isFatherLess() {
584             return isEva || !isGrounded && (event_package.event_body.father is null) && _mother
585                 .isFatherLess;
586         }
587 
588         bool isGrounded() {
589             return (_mother is null) && (event_package.event_body.mother !is null) ||
590                 (_father is null) && (event_package.event_body.father !is null);
591         }
592 
593         immutable(Buffer) fingerprint() {
594             return event_package.fingerprint;
595         }
596 
597         Range!true opSlice() {
598             return Range!true(this);
599         }
600     }
601 
602     @nogc
603     package Range!false opSlice() pure nothrow {
604         return Range!false(this);
605     }
606 
607     @nogc
608     struct Range(bool CONST = true) {
609         private Event current;
610         static if (CONST) {
611             this(const Event event) pure nothrow @trusted {
612                 current = cast(Event) event;
613             }
614         }
615         else {
616             this(Event event) pure nothrow {
617                 current = event;
618             }
619         }
620         pure nothrow {
621             bool empty() const {
622                 return current is null;
623             }
624 
625             static if (CONST) {
626                 const(Event) front() const {
627                     return current;
628                 }
629             }
630             else {
631                 ref Event front() {
632                     return current;
633                 }
634             }
635 
636             alias back = front;
637 
638             void popFront() {
639                 if (current) {
640                     current = current._mother;
641                 }
642             }
643 
644             void popBack() {
645                 if (current) {
646                     current = current._daughter;
647                 }
648             }
649 
650             Range save() {
651                 return Range(current);
652             }
653         }
654     }
655 
656     static assert(isInputRange!(Range!true));
657     static assert(isForwardRange!(Range!true));
658     static assert(isBidirectionalRange!(Range!true));
659 }