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 }