Implemented the inundation, strange type error encountered when testing

This commit is contained in:
eichhornchen 2020-12-29 13:48:55 +01:00
parent c01fd697cd
commit 04f6fb6002
2 changed files with 95 additions and 8 deletions

View File

@ -1,7 +1,7 @@
# Copyright (C) 2020 by eichhornchen, ÿnérant # Copyright (C) 2020 by eichhornchen, ÿnérant
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from datetime import datetime from datetime import datetime
from random import randint from random import randint, uniform
from typing import Any, Tuple from typing import Any, Tuple
#from ipaddress import IPv6Address #from ipaddress import IPv6Address
from threading import Thread, RLock from threading import Thread, RLock
@ -61,6 +61,7 @@ class Squirrel(Hazelnut):
self.history = [] self.history = []
self.received_messages = dict() self.received_messages = dict()
self.recent_messages = dict() #of the form [Pkt(DataTLV), date of first reception, dict(neighbour, date of the next send, nb of times it has already been sent)]
self.history_pad = curses.newpad(curses.LINES - 2, curses.COLS) self.history_pad = curses.newpad(curses.LINES - 2, curses.COLS)
self.input_pad = curses.newpad(1, curses.COLS) self.input_pad = curses.newpad(1, curses.COLS)
self.emoji_pad = curses.newpad(18, 12) self.emoji_pad = curses.newpad(18, 12)
@ -75,7 +76,7 @@ class Squirrel(Hazelnut):
self.activehazelnuts = dict() #of the form [hazelnut, time of last self.activehazelnuts = dict() #of the form [hazelnut, time of last
#hello, time of last long hello, is symmetric] #hello, time of last long hello, is symmetric]
self.nbNS = 0 self.nbNS = 0
self.minNS = 3 #minimal number of symetric neighbours a squirrel needs self.minNS = 3 #minimal number of symmetric neighbours a squirrel needs
#to have. #to have.
self.add_system_message(f"Listening on {self.address}:{self.port}") self.add_system_message(f"Listening on {self.address}:{self.port}")
@ -294,19 +295,94 @@ class Squirrel(Hazelnut):
if self.last_line == len(self.history) - 2: if self.last_line == len(self.history) - 2:
self.last_line += 1 self.last_line += 1
def receive_message_from(self, msg: str, sender_id: int, nonce: int) -> bool: def receive_message_from(self, tlv: DataTLV, msg: str, sender_id: int, nonce: int, relay: Hazelnut) -> bool:
""" """
This method is called by a DataTLV, sent by a real person. This method is called by a DataTLV, sent by a real person.
This add the message in the history if not already done. This add the message in the history if not already done.
Returns True iff the message was not already received previously. Returns True iff the message was not already received previously.
""" """
if (sender_id, nonce) not in self.recent_messages:
#If it is a new message, add it to recent_messages
d = self.make_inundation_dict()
pkt = Packet().construct(tlv)
self.recent_messages[(sender_id, nonce)] = [pkt, time.time(), d]
#in all cases, remove the sender from the list of neighbours to be inundated
self.remove_from_inundation(relay, sender_id, nonce)
if (sender_id, nonce) in self.received_messages: if (sender_id, nonce) in self.received_messages:
return False return False
self.add_message(msg) self.add_message(msg) #for display purposes
self.received_messages[(sender_id, nonce)] = Message(msg, sender_id, nonce) self.received_messages[(sender_id, nonce)] = Message(msg, sender_id, nonce)
return True return True
def make_inundation_dict(self) -> dict:
"""
Takes the activehazels dictionnary and returns a list of [hazel, date+random, 0]
"""
res = dict()
l = list(self.activehazelnuts.items())
for key, hazel in l:
if hazel[3]: #Only if the neighbour is symmetric
next_send = uniform(1, 2)
res[key] = [hazel[0], time.time()+next_send, 0]
return res
def remove_from_inundation(self, hazel: Hazelnut, sender_id: int, nonce: int) -> None:
"""
Remove the sender from the list of neighbours to be inundated
"""
if (sender_id, nonce) in self.recent_messages:
#If a peer is late in its acknowledgement, the absence of the previous if causes an error.
self.recent_messages[(sender_id, nonce)][2].pop((hazel.address, hazel.port), None)
if not self.recent_messages[(sender_id, nonce)][2] : #If dictionnary is empty, remove the message
self.recent_messages.pop((sender_id, nonce), None)
def clean_inundation(self):
"""
Remove messages which are overdue (older than 2 minutes) from the inundation dictionnary.
"""
self.refresh_lock.acquire()
for key in self.recent_messages:
if time.time()-self.recent_messages[key][1] > 120:
self.recent_messages.pop(key)
self.refresh_lock.release()
def main_inundation(self):
"""
The main inundation function.
"""
self.refresh_lock.acquire()
for key in self.recent_messages:
for key2 in self.recent_messages[key][2]:
if time.time()-self.recent_messages[key][2][key2][1] >= 0:
#send the packet if it is overdue
self.send_packet(self.recent_messages[key][2][key2][0], self.recent_messages[key][0])
a = self.recent_messages[key][2][key2][2]
if a==5: #the neighbour is not reactive enough
gatlv = GoAwayTLV().construct(GoAwayType.TIMEOUT, "No acknowledge")
pkt = Packet().construct(gatlv)
self.send_packet(
self.recent_messages[key][2][key2][0], pkt)
self.activehazelnuts.pop(key2)
self.potentialhazelnuts[key2] = self.recent_messages[key][2][key2][0]
self.recent_messages[key][2].pop(key2)
#change the time until the next send
self.recent_messages[key][2][key2][2] = a+1
next_send = uniform(2**(a-1), 2**a)
self.recent_messages[key][2][key2][1] = time.time()+next_send
self.refresh_lock.release()
def add_system_message(self, msg: str) -> None: def add_system_message(self, msg: str) -> None:
""" """
Add a new system log message. Add a new system log message.
@ -649,6 +725,11 @@ class Inondator(Thread):
def run(self) -> None: def run(self) -> None:
while True: while True:
#clean the dictionnary
self.squirrel.clean_inundation()
#inundate
self.squirrel.main_inundation()
class Message: class Message:

View File

@ -165,8 +165,11 @@ class HelloTLV(TLV):
timeHL = squirrel.activehazelnuts[(sender.address, sender.port)] timeHL = squirrel.activehazelnuts[(sender.address, sender.port)]
if self.is_long and self.dest_id == squirrel.id : if self.is_long and self.dest_id == squirrel.id :
timeHL = time.time() timeHL = time.time()
#Make sure the sender is not in the potential hazelnuts
squirrel.remove_from_potential(sender.address, sender.port) squirrel.remove_from_potential(sender.address, sender.port)
#Add entry to/actualize the active hazelnuts dictionnary
squirrel.activehazelnuts[(sender.address, sender.port)] = [sender, timeH,\ squirrel.activehazelnuts[(sender.address, sender.port)] = [sender, timeH,\
timeHL, True] timeHL, True]
squirrel.nbNS += 1 squirrel.nbNS += 1
@ -260,7 +263,7 @@ class DataTLV(TLV):
# Acknowledge the packet # Acknowledge the packet
squirrel.send_packet(sender, Packet.construct(AckTLV.construct(self.sender_id, self.nonce))) squirrel.send_packet(sender, Packet.construct(AckTLV.construct(self.sender_id, self.nonce)))
if not squirrel.receive_message_from(msg, self.sender_id, self.nonce): if not squirrel.receive_message_from(self, msg, self.sender_id, self.nonce, sender):
# The message was already received, do not print it # The message was already received, do not print it
return return
@ -312,8 +315,11 @@ class AckTLV(TLV):
socket.htonl(self.nonce).to_bytes(4, sys.byteorder) socket.htonl(self.nonce).to_bytes(4, sys.byteorder)
def handle(self, squirrel: Any, sender: Any) -> None: def handle(self, squirrel: Any, sender: Any) -> None:
# TODO Implement AckTLV """
squirrel.add_system_message("I received an AckTLV. I don't know what to do with it. Please implement me!") When an AckTLV is received, we know that we do not have to inundate that neighbour anymore.
"""
squirrel.add_system_message("I received an AckTLV")
squirrel.remove_from_inundation(sender, self.sender_id, self.nonce)
@staticmethod @staticmethod
def construct(sender_id: int, nonce: int) -> "AckTLV": def construct(sender_id: int, nonce: int) -> "AckTLV":