main.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. from hashlib import sha256
  2. from datetime import datetime
  3. import asyncio
  4. EventId = str
  5. EventIds = list[EventId]
  6. class Event:
  7. def __init__(self, parents: EventIds):
  8. self.timestamp = datetime.now().timestamp
  9. self.parents = parents
  10. def hash(self) -> str:
  11. m = sha256()
  12. m.update(str.encode(str(self.timestamp)))
  13. for p in self.parents:
  14. m.update(str.encode(str(p)))
  15. return m.digest().hex()
  16. def __str__(self):
  17. res = f"{self.hash()}"
  18. for p in self.parents:
  19. res += f"\n |"
  20. res += f"\n - {p}"
  21. res += f"\n"
  22. return res
  23. """
  24. ## Graph Example
  25. E1: []
  26. E2: [E1]
  27. E3: [E1]
  28. E4: [E3]
  29. E5: [E3]
  30. E6: [E4, E5]
  31. E7: [E4]
  32. E8: [E2]
  33. """
  34. class Graph:
  35. def __init__(self):
  36. self.events = dict()
  37. def add_event(self, event: Event):
  38. self.events[event.hash()] = event
  39. def remove_event(self, event_id: EventId):
  40. if event_id in self.events:
  41. del self.events[event_id]
  42. # check if given events are exist in the graph
  43. # return a list of missing events
  44. def check(self, events: EventIds) -> EventIds:
  45. missing_events = []
  46. for e in events:
  47. if e not in self.events:
  48. missing_events.append(e)
  49. return missing_events
  50. def __str__(self):
  51. res = ""
  52. for event in self.events.values():
  53. res += f"\n {event}"
  54. return res
  55. class Node:
  56. def __init__(self, name: str):
  57. self.name = name
  58. self.orphan_pool = Graph()
  59. self.active_pool = Graph()
  60. def receive_new_event(self, event: Event):
  61. # check if the event has no parents, and the active pool
  62. # is empty, then add the event directly to the active pool
  63. if len(event.parents) == 0:
  64. if len(self.active_pool.events) == 0:
  65. self.active_pool.add_event(event)
  66. self.relink(event)
  67. return
  68. missing_parents = self.active_pool.check(event.parents)
  69. if len(missing_parents) == 0:
  70. # if there are no missing parents
  71. # add the event to active pool
  72. self.active_pool.add_event(event)
  73. self.relink(event)
  74. else:
  75. # add the received event to the orphan pool
  76. self.orphan_pool.add_event(event)
  77. # check if all the missing parents are in orphan pool
  78. # if the missing parents and their links not in orphan pool, request
  79. # them from the network
  80. request_list = []
  81. self.check_parents(request_list, missing_parents)
  82. # XXX
  83. # send all the missing parents in request_list
  84. # to the node who send this event
  85. def check_parents(self, request_list, parents: EventIds):
  86. for parent_hash in parents:
  87. if parent_hash in self.orphan_pool.events:
  88. parent = self.orphan_pool.events[parent_hash]
  89. # recursive call
  90. self.check_parents(request_list, parent.parents)
  91. else:
  92. request_list.append(parent_hash)
  93. def relink(self, event: Event):
  94. # check if the orphan pool has an event linked
  95. # to the new added event
  96. for (orphan_hash, orphan) in dict(self.orphan_pool.events).items():
  97. if event.hash() not in orphan.parents:
  98. continue
  99. missing_parents = self.active_pool.check(orphan.parents)
  100. if len(missing_parents) == 0:
  101. self.active_pool.add_event(orphan)
  102. self.orphan_pool.remove_event(orphan_hash)
  103. # recursive call
  104. self.relink(orphan)
  105. def __str__(self):
  106. return f"""------
  107. \n Name: {self.name}
  108. \n Active Pool: {self.active_pool}
  109. \n Orphan Pool: {self.orphan_pool}"""
  110. async def run_node(name):
  111. print(f"{name} Started")
  112. node = Node(name)
  113. print(f"{name} End")
  114. async def main():
  115. tasks = await asyncio.gather(
  116. run_node("NodeA"),
  117. run_node("NodeB"),
  118. run_node("NodeC"))
  119. def test_node():
  120. node_a = Node("NodeA")
  121. event0 = Event([])
  122. event1 = Event([event0.hash()])
  123. event2 = Event([event1.hash()])
  124. event3 = Event([event2.hash(), event0.hash()])
  125. event4 = Event([event1.hash(), event3.hash()])
  126. event5 = Event([event4.hash(), "FAKEHASH"])
  127. event6 = Event([event5.hash(), event3.hash()])
  128. node_a.receive_new_event(event0)
  129. node_a.receive_new_event(event3)
  130. node_a.receive_new_event(event2)
  131. node_a.receive_new_event(event1)
  132. node_a.receive_new_event(event5)
  133. node_a.receive_new_event(event6)
  134. node_a.receive_new_event(event4)
  135. print(node_a)
  136. if __name__ == "__main__":
  137. test_node()
  138. asyncio.run(main())