Преглед изворни кода

script/research/event_graph: POC for event graph

ghassmo пре 3 година
родитељ
комит
b116b60ed8
1 измењених фајлова са 182 додато и 0 уклоњено
  1. 182 0
      script/research/event_graph/main.py

+ 182 - 0
script/research/event_graph/main.py

@@ -0,0 +1,182 @@
+from hashlib import sha256
+from datetime import datetime
+import asyncio
+
+EventId = str
+EventIds = list[EventId]
+
+
+class Event:
+    def __init__(self, parents: EventIds):
+        self.timestamp = datetime.now().timestamp
+        self.parents = parents
+
+    def hash(self) -> str:
+        m = sha256()
+        m.update(str.encode(str(self.timestamp)))
+        for p in self.parents:
+            m.update(str.encode(str(p)))
+        return m.digest().hex()
+
+    def __str__(self):
+
+        res = f"{self.hash()}"
+        for p in self.parents:
+            res += f"\n    |"
+            res += f"\n    - {p}"
+
+        res += f"\n"
+        return res
+
+
+"""
+## Graph Example
+ E1: [] 
+ E2: [E1]
+ E3: [E1]
+ E4: [E3]
+ E5: [E3]
+ E6: [E4, E5]
+ E7: [E4]
+ E8: [E2] 
+"""
+class Graph:
+    def __init__(self):
+        self.events = dict()
+
+    def add_event(self, event: Event):
+        self.events[event.hash()] = event
+
+    def remove_event(self, event_id: EventId):
+        if event_id in self.events:
+            del self.events[event_id]
+
+    # check if given events are exist in the graph
+    # return a list of missing events
+    def check(self, events: EventIds) -> EventIds:
+        missing_events = []
+
+        for e in events:
+            if e not in self.events:
+                missing_events.append(e)
+
+        return missing_events
+
+    def __str__(self):
+        res = ""
+        for event in self.events.values():
+            res += f"\n {event}"
+        return res
+
+
+class Node:
+    def __init__(self, name: str):
+        self.name = name
+        self.orphan_pool = Graph()
+        self.active_pool = Graph()
+
+    def receive_new_event(self, event: Event):
+
+        # check if the event has no parents, and the active pool
+        # is empty, then add the event directly to the active pool
+        if len(event.parents) == 0:
+            if len(self.active_pool.events) == 0:
+                self.active_pool.add_event(event)
+                self.relink(event)
+
+            return
+
+        missing_parents = self.active_pool.check(event.parents)
+
+        if len(missing_parents) == 0:
+            # if there are no missing parents
+            # add the event to active pool
+            self.active_pool.add_event(event)
+            self.relink(event)
+        else:
+            # add the received event to the orphan pool
+            self.orphan_pool.add_event(event)
+
+            # check if all the missing parents are in orphan pool
+            # if the missing parents and their links not in orphan pool, request
+            # them from the network
+            request_list = []
+            self.check_parents(request_list, missing_parents)
+
+            # XXX
+            # send all the missing parents in request_list
+            # to the node who send this event
+
+    def check_parents(self, request_list, parents: EventIds):
+        for parent_hash in parents:
+            if parent_hash in self.orphan_pool.events:
+                parent = self.orphan_pool.events[parent_hash]
+
+                # recursive call
+                self.check_parents(request_list, parent.parents)
+            else:
+                request_list.append(parent_hash)
+
+    def relink(self, event: Event):
+        # check if the orphan pool has an event linked
+        # to the new added event
+        for (orphan_hash, orphan) in dict(self.orphan_pool.events).items():
+
+            if event.hash() not in orphan.parents:
+                continue
+
+            missing_parents = self.active_pool.check(orphan.parents)
+
+            if len(missing_parents) == 0:
+                self.active_pool.add_event(orphan)
+                self.orphan_pool.remove_event(orphan_hash)
+
+                # recursive call
+                self.relink(orphan)
+
+    def __str__(self):
+        return f"""------
+            \n Name: {self.name}
+            \n Active Pool: {self.active_pool}
+            \n Orphan Pool: {self.orphan_pool}"""
+
+
+async def run_node(name):
+    print(f"{name} Started")
+    node = Node(name)
+
+    print(f"{name} End")
+
+
+async def main():
+    tasks = await asyncio.gather(
+        run_node("NodeA"),
+        run_node("NodeB"),
+        run_node("NodeC"))
+
+
+def test_node():
+    node_a = Node("NodeA")
+
+    event0 = Event([])
+    event1 = Event([event0.hash()])
+    event2 = Event([event1.hash()])
+    event3 = Event([event2.hash(), event0.hash()])
+    event4 = Event([event1.hash(), event3.hash()])
+    event5 = Event([event4.hash(), "FAKEHASH"])
+    event6 = Event([event5.hash(), event3.hash()])
+
+    node_a.receive_new_event(event0)
+    node_a.receive_new_event(event3)
+    node_a.receive_new_event(event2)
+    node_a.receive_new_event(event1)
+    node_a.receive_new_event(event5)
+    node_a.receive_new_event(event6)
+    node_a.receive_new_event(event4)
+
+    print(node_a)
+
+
+if __name__ == "__main__":
+    test_node()
+    asyncio.run(main())