This week I worked on the definitions of the functions and structure of the rated-list. The detailed can be found as follows. Next week I will fanalize it and check the next step for implementation or verification.
#### Constants and Aliases
The below constants assume a max node degree of 100
| Name | Value |
|------------------|-----------|
| MAX_TREE_DEPTH | 3 |
| MAX_ID_LIST | 100 |
| MAX_CHILDREN | 100 |
| MAX_PARENTS | 100 |
We assume all peer ids (interchangeably called node ids) are 256-bit strings represented as `Bytes32`
```python
NODE_ID = Bytes32
```
#### `Peer`
```python
@dataclass
class Peer:
peer_id: NODE_ID
level: uint8
status_active: bool # redundant variable to last_queried_slot but can be useful.
last_queried_slot: Slot # tracks the last slot the peer was seens as active
is_evicted: bool # helps to mark a peer for eviction so that a routine task can remove it
children: List[Peer, MAX_CHILDREN]
parents: List[Peer, MAX_PARENTS] # creates a doubly linked list
score: int32
```
#### `RatedList`
```python
RatedList = Dict[NODE_ID, Peer] # A flat routing table makes it easier to manage queries
```
#### `Node`
```python
@dataclass
class Node:
rated_list: RatedList
tree_root: Peer
```
#### `IdList`
```python
IdList = List[NODE_ID, MAX_ID_LIST]
```
#### `create_empty_peer`
```python
def create_empty_peer(id: NODE_ID) -> Peer:
""" TODO: Add a description here """
peer = Peer(
peer_id: id,
level: 255,
status_active: False,
last_queried_slot: 0,
is_evicted: False,
children: None,
parents: None,
score: float
)
return peer_list
```
#### `add_children`
TODO: Check the sacntity of this function.
TODO: I'm not a pythonista and I have assumed that objects are always referred (as pointers) instead of copied. we should check this to either keep or remove the last if condition
```python
def add_children(rated_list: RatedList, peer: Peer, id_list: IdList):
if peer.level >= MAX_TREE_DEPTH:
return
for id in id_list:
child_peer: Peer = None
if id not in rated_list:
child_peer = create_empty_peer(id)
rated_list[id] = child_peer
elif not rated_list[id].level <= peer.level:
child_peer = rated_list[id]
if rated_list[id].level != peer.level + 1:
child_peer.parents = []
else:
continue
child_peer.level = peer.level + 1
child_peer.parents.append(peer)
if child_peer.last_queried_slot != CURRENT_SLOT:
success, res_list = get_peers(id)
if success and len(res_list) > 0:
child_peer.last_queried_slot = CURRENT_SLOT
child_peer.status_active = True
# makes the algorithm depth-first. advantage is we don't require hold information in memory
# but we do spend stack memory for it (recursion)
add_children(rated_list, child_peer, res_list)
else:
child_peer.last_queried_slot = CURRENT_SLOT
child_peer.status_active = False
if id not in rated_list:
rated_list[id] = child_peer
peer.children.append(child_peer)
return
```
#### `compute_score`
```python
def compute_score(peer: Peer) -> float:
# Interact with the block to determine its status
# self.block.interact()
if peer.children: # If the node has children, compute the average score
total_score = sum(compute_score(child) for child in peer.children)
peer.score = total_score / len(peer.children)
else:
# score = self.block.get_status() # Leaf node uses its own block's status
peer.score = 1.0 if peer.status_active else 0.0
return peer.score
```
#### `evict_nodes`
```python
def evict_nodes(parent: Peer, threshold: float):
for child in parent.children:
if peer.score < threshold:
parent.children.remove(child)
child.parents.remove(parent)
evict_nodes(child, threshold)
```
#### `get_peers`
This function is abstracted out to be defined by the underlying p2p network. However, it should be of the below signature
```python
def get_peers(id: NODE_ID) -> IdList
```