EntropyOverlay refactor (#30)

* Refactor FlatOverlay

* Refactor EntropyOverlay and usages
This commit is contained in:
Daniel Sanchez 2023-05-30 21:15:56 +02:00 committed by GitHub
parent 34617dc911
commit 630dd3ac5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 44 additions and 39 deletions

View File

@ -18,7 +18,6 @@ class BeaconizedCarnot(Carnot):
self.random_beacon = RandomBeaconHandler(
RecoveryMode.generate_beacon(entropy, -1)
)
overlay.set_entropy(self.random_beacon.last_beacon.entropy())
super().__init__(self.pk, overlay=overlay)
def approve_block(self, block: BeaconizedBlock, votes: Set[Vote]) -> Event:
@ -45,13 +44,13 @@ class BeaconizedCarnot(Carnot):
# root members send votes to next leader, we update our beacon first
if self.overlay.is_member_of_root_committee(self.id):
assert(self.random_beacon.verify_happy(block.beacon, block.pk, block.qc.view))
self.overlay.set_entropy(self.random_beacon.last_beacon.entropy())
self.overlay = self.overlay.advance(self.random_beacon.last_beacon.entropy())
return Send(to=self.overlay.leader(), payload=vote)
# otherwise we send to the parent committee and update the beacon second
return_event = Send(to=self.overlay.parent_committee(self.id), payload=vote)
assert(self.random_beacon.verify_happy(block.beacon, block.pk, block.qc.view))
self.overlay.set_entropy(self.random_beacon.last_beacon.entropy())
self.overlay = self.overlay.advance(self.random_beacon.last_beacon.entropy())
return return_event
def receive_timeout_qc(self, timeout_qc: TimeoutQc):
@ -60,12 +59,12 @@ class BeaconizedCarnot(Carnot):
return
new_beacon = RecoveryMode.generate_beacon(self.random_beacon.last_beacon.entropy(), timeout_qc.view)
self.random_beacon.verify_unhappy(new_beacon, timeout_qc.view)
self.overlay.set_entropy(self.random_beacon.last_beacon.entropy())
self.overlay = self.overlay.advance(self.random_beacon.last_beacon.entropy())
def propose_block(self, view: View, quorum: Quorum) -> Event:
event: Event = super().propose_block(view, quorum)
block = event.payload
beacon = NormalMode.generate_beacon(self.sk, block.qc.view)
block = BeaconizedBlock(view=block.view, qc=block.qc, _id=block._id, beacon=beacon, pk = G1Element.from_bytes(self.pk))
event.payload = block
return event
event: Event = super().propose_block(view, quorum)
block = event.payload
beacon = NormalMode.generate_beacon(self.sk, block.qc.view)
block = BeaconizedBlock(view=block.view, qc=block.qc, _id=block._id, beacon=beacon, pk = G1Element.from_bytes(self.pk))
event.payload = block
return event

View File

@ -1,25 +1,34 @@
import random
from abc import abstractmethod
from typing import Set, Optional, List
from typing import Set, Optional, List, Self
from carnot import Overlay, Id, Committee, View
class EntropyOverlay(Overlay):
@abstractmethod
def set_entropy(self, entropy: bytes):
def advance(self, entropy: bytes) -> Self:
pass
class FlatOverlay(EntropyOverlay):
def set_entropy(self, entropy: bytes):
def __init__(self, current_leader: Id, nodes: List[Id], entropy: bytes):
self.current_leader = current_leader
self.nodes = nodes
self.entropy = entropy
def next_leader(self) -> Id:
random.seed(a=self.entropy, version=2)
return random.choice(self.nodes)
def advance(self, entropy: bytes):
return FlatOverlay(self.next_leader(), self.nodes, entropy)
def is_leader(self, _id: Id):
return _id == self.leader()
def leader(self) -> Id:
random.seed(a=self.entropy, version=2)
return random.choice(self.nodes)
return self.current_leader
def is_member_of_leaf_committee(self, _id: Id) -> bool:
return True
@ -48,9 +57,5 @@ class FlatOverlay(EntropyOverlay):
def super_majority_threshold(self, _id: Id) -> int:
return 0
def __init__(self, nodes: List[Id]):
self.nodes = nodes
self.entropy = None

View File

@ -1,3 +1,4 @@
import random
from typing import Dict, List
from unittest import TestCase
from itertools import chain
@ -5,21 +6,19 @@ from itertools import chain
from blspy import PrivateKey
from carnot import Id, Carnot, Block, Overlay, Vote, StandardQc, NewView
from beacon import generate_random_sk, RandomBeacon, NormalMode
from beacon import generate_random_sk, RandomBeacon, NormalMode, RecoveryMode
from beaconized_carnot import BeaconizedCarnot, BeaconizedBlock
from overlay import FlatOverlay, EntropyOverlay
from test_unhappy_path import parents_from_childs
def gen_node(sk: PrivateKey, overlay: Overlay, entropy: bytes = b""):
node = BeaconizedCarnot(sk, overlay)
node = BeaconizedCarnot(sk, overlay, entropy)
return node.id, node
def succeed(nodes: Dict[Id, BeaconizedCarnot], proposed_block: BeaconizedBlock) -> (List[Vote], EntropyOverlay):
overlay = FlatOverlay(list(nodes.keys()))
overlay.set_entropy(proposed_block.beacon.entropy())
def succeed(nodes: Dict[Id, BeaconizedCarnot], proposed_block: BeaconizedBlock, overlay: EntropyOverlay) -> (List[Vote], EntropyOverlay):
overlay = overlay.advance(proposed_block.beacon.entropy())
# broadcast the block
for node in nodes.values():
node.receive_block(proposed_block)
@ -47,9 +46,8 @@ def succeed(nodes: Dict[Id, BeaconizedCarnot], proposed_block: BeaconizedBlock)
return root_votes, overlay
def fail(nodes: Dict[Id, BeaconizedCarnot], proposed_block: BeaconizedBlock) -> (List[NewView], EntropyOverlay):
overlay = FlatOverlay(list(nodes.keys()))
overlay.set_entropy(proposed_block.beacon.entropy)
def fail(nodes: Dict[Id, BeaconizedCarnot], proposed_block: BeaconizedBlock, overlay: EntropyOverlay) -> (List[NewView], EntropyOverlay):
overlay = overlay.advance(proposed_block.beacon.entropy())
# broadcast the block
for node in nodes.values():
node.receive_block(proposed_block)
@ -106,7 +104,7 @@ def add_genesis_block(carnot: BeaconizedCarnot, sk: PrivateKey) -> Block:
carnot.receive_block(genesis_block)
carnot.local_high_qc = genesis_block.qc
carnot.current_view = 1
carnot.overlay.set_entropy(beacon.entropy())
carnot.overlay = carnot.overlay.advance(beacon.entropy())
return genesis_block
@ -114,10 +112,14 @@ def initial_setup(test_case: TestCase, size: int) -> (Dict[Id, Carnot], Carnot,
keys = [generate_random_sk() for _ in range(size)]
nodes_ids = [bytes(key.get_g1()) for key in keys]
genesis_sk = generate_random_sk()
nodes = dict(gen_node(key, FlatOverlay(nodes_ids), bytes(genesis_sk.get_g1())) for key in keys)
entropy = RecoveryMode.generate_beacon(bytes(genesis_sk), -1).entropy()
random.seed(a=entropy, version=2)
current_leader = random.choice(nodes_ids)
nodes = dict(gen_node(key, FlatOverlay(current_leader, nodes_ids, entropy), entropy) for key in keys)
genesis_block = None
overlay = FlatOverlay(nodes_ids)
overlay.set_entropy(NormalMode.generate_beacon(genesis_sk, -1).entropy())
overlay = FlatOverlay(current_leader, nodes_ids, entropy)
leader: Carnot = nodes[overlay.leader()]
for node in nodes.values():
genesis_block = add_genesis_block(node, genesis_sk)
@ -135,8 +137,7 @@ def initial_setup(test_case: TestCase, size: int) -> (Dict[Id, Carnot], Carnot,
)
proposed_block = leader.propose_block(1, genesis_votes).payload
test_case.assertIsNotNone(proposed_block)
overlay = FlatOverlay(nodes_ids)
overlay.set_entropy(genesis_block.beacon.entropy())
overlay = overlay.advance(genesis_block.beacon.entropy())
return nodes, leader, proposed_block, overlay
@ -151,25 +152,25 @@ class TestBeaconizedCarnot(TestCase):
nodes, leader, proposed_block, overlay = initial_setup(self, 5)
for view in range(2, 5):
root_votes, overlay = succeed(nodes, proposed_block)
root_votes, overlay = succeed(nodes, proposed_block, overlay)
leader = nodes[overlay.leader()]
proposed_block = leader.propose_block(view, root_votes).payload
root_votes, overlay = fail(nodes, proposed_block)
root_votes, overlay = fail(nodes, proposed_block, overlay)
leader = nodes[overlay.leader()]
proposed_block = leader.propose_block(6, root_votes).payload
for view in range(7, 8):
root_votes, overlay = succeed(nodes, proposed_block)
root_votes, overlay = succeed(nodes, proposed_block, overlay)
leader = nodes[overlay.leader()]
proposed_block = leader.propose_block(view, root_votes).payload
root_votes, overlay = fail(nodes, proposed_block)
root_votes, overlay = fail(nodes, proposed_block, overlay)
leader = nodes[overlay.leader()]
proposed_block = leader.propose_block(9, root_votes).payload
for view in range(10, 15):
root_votes, overlay = succeed(nodes, proposed_block)
root_votes, overlay = succeed(nodes, proposed_block, overlay)
leader = nodes[overlay.leader()]
proposed_block = leader.propose_block(view, root_votes).payload