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

View File

@ -1,25 +1,34 @@
import random import random
from abc import abstractmethod from abc import abstractmethod
from typing import Set, Optional, List from typing import Set, Optional, List, Self
from carnot import Overlay, Id, Committee, View from carnot import Overlay, Id, Committee, View
class EntropyOverlay(Overlay): class EntropyOverlay(Overlay):
@abstractmethod @abstractmethod
def set_entropy(self, entropy: bytes): def advance(self, entropy: bytes) -> Self:
pass pass
class FlatOverlay(EntropyOverlay): 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 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): def is_leader(self, _id: Id):
return _id == self.leader() return _id == self.leader()
def leader(self) -> Id: def leader(self) -> Id:
random.seed(a=self.entropy, version=2) return self.current_leader
return random.choice(self.nodes)
def is_member_of_leaf_committee(self, _id: Id) -> bool: def is_member_of_leaf_committee(self, _id: Id) -> bool:
return True return True
@ -48,9 +57,5 @@ class FlatOverlay(EntropyOverlay):
def super_majority_threshold(self, _id: Id) -> int: def super_majority_threshold(self, _id: Id) -> int:
return 0 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 typing import Dict, List
from unittest import TestCase from unittest import TestCase
from itertools import chain from itertools import chain
@ -5,21 +6,19 @@ from itertools import chain
from blspy import PrivateKey from blspy import PrivateKey
from carnot import Id, Carnot, Block, Overlay, Vote, StandardQc, NewView 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 beaconized_carnot import BeaconizedCarnot, BeaconizedBlock
from overlay import FlatOverlay, EntropyOverlay from overlay import FlatOverlay, EntropyOverlay
from test_unhappy_path import parents_from_childs from test_unhappy_path import parents_from_childs
def gen_node(sk: PrivateKey, overlay: Overlay, entropy: bytes = b""): def gen_node(sk: PrivateKey, overlay: Overlay, entropy: bytes = b""):
node = BeaconizedCarnot(sk, overlay) node = BeaconizedCarnot(sk, overlay, entropy)
return node.id, node return node.id, node
def succeed(nodes: Dict[Id, BeaconizedCarnot], proposed_block: BeaconizedBlock) -> (List[Vote], EntropyOverlay): def succeed(nodes: Dict[Id, BeaconizedCarnot], proposed_block: BeaconizedBlock, overlay: EntropyOverlay) -> (List[Vote], EntropyOverlay):
overlay = FlatOverlay(list(nodes.keys())) overlay = overlay.advance(proposed_block.beacon.entropy())
overlay.set_entropy(proposed_block.beacon.entropy())
# broadcast the block # broadcast the block
for node in nodes.values(): for node in nodes.values():
node.receive_block(proposed_block) node.receive_block(proposed_block)
@ -47,9 +46,8 @@ def succeed(nodes: Dict[Id, BeaconizedCarnot], proposed_block: BeaconizedBlock)
return root_votes, overlay return root_votes, overlay
def fail(nodes: Dict[Id, BeaconizedCarnot], proposed_block: BeaconizedBlock) -> (List[NewView], EntropyOverlay): def fail(nodes: Dict[Id, BeaconizedCarnot], proposed_block: BeaconizedBlock, overlay: EntropyOverlay) -> (List[NewView], EntropyOverlay):
overlay = FlatOverlay(list(nodes.keys())) overlay = overlay.advance(proposed_block.beacon.entropy())
overlay.set_entropy(proposed_block.beacon.entropy)
# broadcast the block # broadcast the block
for node in nodes.values(): for node in nodes.values():
node.receive_block(proposed_block) node.receive_block(proposed_block)
@ -106,7 +104,7 @@ def add_genesis_block(carnot: BeaconizedCarnot, sk: PrivateKey) -> Block:
carnot.receive_block(genesis_block) carnot.receive_block(genesis_block)
carnot.local_high_qc = genesis_block.qc carnot.local_high_qc = genesis_block.qc
carnot.current_view = 1 carnot.current_view = 1
carnot.overlay.set_entropy(beacon.entropy()) carnot.overlay = carnot.overlay.advance(beacon.entropy())
return genesis_block 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)] keys = [generate_random_sk() for _ in range(size)]
nodes_ids = [bytes(key.get_g1()) for key in keys] nodes_ids = [bytes(key.get_g1()) for key in keys]
genesis_sk = generate_random_sk() 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 genesis_block = None
overlay = FlatOverlay(nodes_ids) overlay = FlatOverlay(current_leader, nodes_ids, entropy)
overlay.set_entropy(NormalMode.generate_beacon(genesis_sk, -1).entropy())
leader: Carnot = nodes[overlay.leader()] leader: Carnot = nodes[overlay.leader()]
for node in nodes.values(): for node in nodes.values():
genesis_block = add_genesis_block(node, genesis_sk) 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 proposed_block = leader.propose_block(1, genesis_votes).payload
test_case.assertIsNotNone(proposed_block) test_case.assertIsNotNone(proposed_block)
overlay = FlatOverlay(nodes_ids) overlay = overlay.advance(genesis_block.beacon.entropy())
overlay.set_entropy(genesis_block.beacon.entropy())
return nodes, leader, proposed_block, overlay return nodes, leader, proposed_block, overlay
@ -151,25 +152,25 @@ class TestBeaconizedCarnot(TestCase):
nodes, leader, proposed_block, overlay = initial_setup(self, 5) nodes, leader, proposed_block, overlay = initial_setup(self, 5)
for view in range(2, 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()] leader = nodes[overlay.leader()]
proposed_block = leader.propose_block(view, root_votes).payload 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()] leader = nodes[overlay.leader()]
proposed_block = leader.propose_block(6, root_votes).payload proposed_block = leader.propose_block(6, root_votes).payload
for view in range(7, 8): 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()] leader = nodes[overlay.leader()]
proposed_block = leader.propose_block(view, root_votes).payload 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()] leader = nodes[overlay.leader()]
proposed_block = leader.propose_block(9, root_votes).payload proposed_block = leader.propose_block(9, root_votes).payload
for view in range(10, 15): 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()] leader = nodes[overlay.leader()]
proposed_block = leader.propose_block(view, root_votes).payload proposed_block = leader.propose_block(view, root_votes).payload