1 /// Module for handling callbacks from the hashgraph 2 module tagion.hashgraph.Refinement; 3 4 import tagion.basic.Debug; 5 import tagion.basic.Types : Buffer; 6 import tagion.crypto.Types : Pubkey; 7 import tagion.hashgraph.Event; 8 import tagion.hashgraph.HashGraph; 9 import tagion.hashgraph.HashGraphBasic; 10 import tagion.hashgraph.RefinementInterface; 11 import tagion.hashgraph.Round; 12 import tagion.hibon.Document; 13 import tagion.hibon.HiBON; 14 import tagion.hibon.HiBONRecord; 15 import tagion.logger.Logger; 16 import tagion.services.messages : consensusEpoch; 17 import tagion.utils.BitMask; 18 import tagion.utils.StdTime; 19 20 // std 21 import std.algorithm : filter, map, reduce, sort, until; 22 import std.array; 23 import std.stdio; 24 import tagion.hibon.HiBONRecord; 25 import tagion.script.standardnames; 26 import tagion.services.options : TaskNames; 27 import tagion.utils.pretend_safe_concurrency; 28 29 @safe 30 @recordType("finishedEpoch") 31 struct FinishedEpoch { 32 @label("events") const(EventPackage)[] events; 33 @label(StdNames.time) sdt_t time; 34 @label("epoch") long epoch; 35 mixin HiBONRecord!(q{ 36 this(const(Event)[] events, sdt_t time, long epoch) pure { 37 this.events = events 38 .map!((e) => *(e.event_package)) 39 .array; 40 41 this.time = time; 42 this.epoch = epoch; 43 } 44 }); 45 } 46 47 @safe 48 class StdRefinement : Refinement { 49 50 static Topic epoch_created = Topic("epoch_creator/epoch_created"); 51 52 enum MAX_ORDER_COUNT = 10; /// Max recursion count for order_less function 53 protected { 54 HashGraph hashgraph; 55 TaskNames task_names; 56 } 57 58 void setOwner(HashGraph hashgraph) 59 in (this.hashgraph is null) 60 do { 61 this.hashgraph = hashgraph; 62 } 63 64 void setTasknames(TaskNames task_names) { 65 this.task_names = task_names; 66 } 67 68 Tid collector_service; 69 void payload(immutable(EventPackage*) epack) { 70 if (!epack.event_body.payload.empty) { 71 // send to collector payload. 72 73 } 74 } 75 76 void finishedEpoch( 77 const(Event[]) events, 78 const sdt_t epoch_time, 79 const Round decided_round) { 80 auto event_payload = FinishedEpoch(events, epoch_time, decided_round.number); 81 82 83 log(epoch_created, "epoch_succesful", event_payload); 84 85 if (task_names is TaskNames.init) { 86 return; 87 } 88 89 immutable(EventPackage*)[] epacks = events 90 .map!((e) => e.event_package) 91 .array; 92 93 auto transcript_tid = locate(task_names.transcript); 94 if (transcript_tid !is Tid.init) { 95 transcript_tid.send(consensusEpoch(), epacks, cast(immutable(long)) decided_round.number, epoch_time); 96 } 97 } 98 99 void excludedNodes(ref BitMask excluded_mask) { 100 // should be implemented 101 } 102 103 void epack(immutable(EventPackage*) epack) { 104 // log.trace("epack.event_body.payload.empty %s", epack.event_body.payload.empty); 105 } 106 107 void epoch(Event[] event_collection, const(Round) decided_round) { 108 109 import std.bigint; 110 import std.numeric : gcd; 111 import std.range : back, retro, tee; 112 113 pragma(msg, "fixme(bbh): move pseudotime out and add function labels"); 114 struct PseudoTime { 115 BigInt num; //fraction representing the avg received round 116 BigInt denom; 117 BigInt order; //sum of received orders 118 sdt_t time; //avg received time 119 120 this(BigInt num, BigInt denom, BigInt order, long time) { 121 this.num = num; 122 this.denom = denom; 123 this.order = order; 124 this.time = time; 125 } 126 127 this(int num, int denom, int order, sdt_t time, long round_number, ulong witness_count) { 128 this.num = BigInt(num + denom * round_number); 129 this.denom = BigInt(denom * witness_count); 130 this.order = BigInt(order); 131 this.time = time / witness_count; 132 } 133 134 PseudoTime opBinary(string op)(PseudoTime other) if (op == "+") { 135 BigInt d = gcd(denom, other.denom); 136 return PseudoTime(other.denom / d * num + denom / d * other.num, 137 denom / d * other.denom, 138 order + other.order, 139 time + other.time); 140 } 141 } 142 143 const famous_witnesses = decided_round 144 ._events 145 .filter!(e => e !is null) 146 .filter!(e => decided_round.famous_mask[e.node_id]) 147 .array; 148 149 PseudoTime calc_pseudo_time(Event event) { 150 auto receivers = famous_witnesses 151 .map!(e => e[].until!(e => !e.sees(event)) 152 .array.back); 153 154 return receivers.map!(e => PseudoTime(e.pseudo_time_counter, 155 (e[].retro.filter!(e => e._witness) 156 .front._mother.pseudo_time_counter + 1), 157 e.order, 158 e.event_body.time, 159 e.round.number, 160 decided_round.famous_mask.count)) 161 .array 162 .reduce!((a, b) => a + b); 163 } 164 165 bool order_less(Event a, Event b) { 166 PseudoTime at = calc_pseudo_time(a); 167 PseudoTime bt = calc_pseudo_time(b); 168 169 if (at.num * bt.denom == at.denom * bt.num) { 170 if (at.order == bt.order) { 171 if (a.order == b.order) { 172 return a.fingerprint < b.fingerprint; 173 } 174 return a.order < b.order; 175 } 176 return at.order < bt.order; 177 } 178 return at.num * bt.denom < at.denom * bt.num; 179 } 180 181 sdt_t[] times; 182 auto events = event_collection 183 .tee!((e) => times ~= e.event_body.time) 184 .filter!((e) => !e.event_body.payload.empty) 185 .array 186 .sort!((a, b) => order_less(a, b)) 187 .release; 188 189 times.sort; 190 const epoch_time = times[times.length / 2]; 191 192 version(EPOCH_LOG) { 193 log.trace("%s Epoch round %d event.count=%d witness.count=%d event in epoch=%d time=%s", 194 hashgraph.name, decided_round.number, 195 Event.count, Event.Witness.count, events.length, epoch_time); 196 } 197 log.trace("event.count=%d witness.count=%d event in epoch=%d", Event.count, Event.Witness.count, events.length); 198 199 finishedEpoch(events, epoch_time, decided_round); 200 201 excludedNodes(hashgraph._excluded_nodes_mask); 202 } 203 204 version (none) //SHOULD NOT BE DELETED SO WE CAN REVERT TO OLD ORDERING IF NEEDED 205 void epoch(Event[] event_collection, const(Round) decided_round) { 206 import std.algorithm; 207 import std.range; 208 209 bool order_less(const Event a, const Event b, const(int) order_count) @safe { 210 bool rare_less(Buffer a_print, Buffer b_print) { 211 // rare_order_compare_count++; 212 pragma(msg, "review(cbr): Concensus order changed"); 213 return a_print < b_print; 214 } 215 216 if (order_count < 0) { 217 return rare_less(a.fingerprint, b.fingerprint); 218 } 219 if (a.order is b.order) { 220 if (a.father && b.father) { 221 return order_less(a.father, b.father, order_count - 1); 222 } 223 if (a.father) { 224 return true; 225 } 226 if (b.father) { 227 return false; 228 } 229 230 if (!a.isFatherLess && !b.isFatherLess) { 231 return order_less(a.mother, b.mother, order_count - 1); 232 } 233 234 return rare_less(a.fingerprint, b.fingerprint); 235 } 236 return a.order < b.order; 237 } 238 239 sdt_t[] times; 240 auto events = event_collection 241 .filter!((e) => e !is null) 242 .tee!((e) => times ~= e.event_body.time) 243 .filter!((e) => !e.event_body.payload.empty) 244 .array 245 .sort!((a, b) => order_less(a, b, MAX_ORDER_COUNT)) 246 .release; 247 times.sort; 248 const mid = times.length / 2 + (times.length % 1); 249 const epoch_time = times[mid]; 250 251 log.trace("%s Epoch round %d event.count=%d witness.count=%d event in epoch=%d time=%s", 252 hashgraph.name, decided_round.number, 253 Event.count, Event.Witness.count, events.length, epoch_time); 254 255 finishedEpoch(events, epoch_time, decided_round); 256 257 excludedNodes(hashgraph._excluded_nodes_mask); 258 } 259 260 } 261 262 @safe 263 struct RoundFingerprint { 264 Buffer[] fingerprints; 265 mixin HiBONRecord; 266 } 267 268 @safe 269 const(RoundFingerprint) hashLastDecidedRound(const Round last_decided_round) pure nothrow { 270 import std.algorithm : filter; 271 272 RoundFingerprint round_fingerprint; 273 round_fingerprint.fingerprints = last_decided_round.events 274 .filter!(e => e !is null) 275 .map!(e => cast(Buffer) e.event_package.fingerprint) 276 .array 277 .sort 278 .array; 279 return round_fingerprint; 280 }