async def _random_vote(vote_factory: DefaultVoteFactory): r = random.randint(0, 15) if r < 5: return DefaultVote(id_=os.urandom(16), data_id=os.urandom(16), commit_id=os.urandom(16), voter_id=vote_factory._node_id, epoch_num=random.randint(0, 10), round_num=random.randint(0, 10)) elif r < 10: return vote_factory.create_lazy_vote(voter_id=vote_factory._node_id, epoch_num=random.randint(0, 10), round_num=random.randint(0, 10)) else: return vote_factory.create_none_vote(epoch_num=random.randint(0, 10), round_num=random.randint(0, 10))
async def test_round_data_vote_sync(): round_num = 0 voter_num = 7 async with setup_items(voter_num, round_num) as (voters, event_system, round_, election, epoch, candidate_data, candidate_votes): data = await round_._data_factory.create_data( data_number=candidate_data.number + 1, prev_id=candidate_data.id, epoch_num=epoch.num, round_num=round_num, prev_votes=candidate_votes) vote_factories = [DefaultVoteFactory(voter) for voter in voters] votes = [] for vote_factory in vote_factories: vote = await vote_factory.create_vote(data.id, candidate_data.id, epoch.num, round_num) votes.append(vote) await round_.receive_vote(vote) election.receive_vote.assert_not_called() await round_.receive_data(data) assert len(votes) == len(election.receive_vote.call_args_list) for vote, call_args in zip(votes, election.receive_vote.call_args_list): arg_vote, = call_args[0] assert arg_vote is vote
async def setup(voter_num: int): voters = [os.urandom(16) for _ in range(voter_num)] vote_factories = [DefaultVoteFactory(voter) for voter in voters] random.shuffle(vote_factories) quorum = random.randint(2, len(vote_factories)) return voters, vote_factories, quorum, RoundMessages()
async def test_round_reach_quorum_consensus(voter_num: int): round_num = 0 async with setup_items(voter_num, round_num) as (voters, event_system, round_, election, epoch, candidate_data, candidate_votes): # Propose LazyData await round_.round_start() mediator = event_system.get_mediator(DelayedEventMediator) mediator.execute.assert_called_once() delay, event = mediator.execute.call_args_list[0][0] assert delay == TIMEOUT_PROPOSE assert isinstance(event, ReceiveDataEvent) assert event.data.is_lazy() mediator.execute.reset_mock() vote_factories = [DefaultVoteFactory(voter) for voter in voters] random.shuffle(vote_factories) quorum_vote_factories = vote_factories[:epoch.quorum_num] for vote_factory in quorum_vote_factories: vote = await vote_factory.create_vote(b'test', candidate_data.id, epoch.num, round_num) await round_.receive_vote(vote) mediator.execute.assert_not_called()
async def do_votes(election, success_vote_num, none_vote_num, lazy_vote_num, voters): validator_vote_factories = [DefaultVoteFactory(x) for x in voters] for i in range(success_vote_num): await do_vote( election, await validator_vote_factories[i].create_vote( data_id=PROPOSE_ID, commit_id=CANDIDATE_ID, epoch_num=0, round_num=1 ) ) for i in range(none_vote_num): await do_vote( election, validator_vote_factories[success_vote_num + i].create_none_vote( epoch_num=0, round_num=1 ) ) for i in range(lazy_vote_num): await do_vote( election, validator_vote_factories[i].create_lazy_vote( voter_id=voters[i], epoch_num=0, round_num=1 ) )
async def setup_items(voter_num: int, round_num: int): voters = [os.urandom(16) for _ in range(voter_num)] voter = voters[0] epoch = RotateEpoch(1, voters) event_system = MagicMock(EventSystem(use_priority=True)) data_factory = DefaultDataFactory(voter) vote_factory = DefaultVoteFactory(voter) election = MagicMock( Election(voter, epoch, round_num, event_system, data_factory, vote_factory, DataPool(), VotePool())) round_ = Round(election, voter, epoch, round_num, event_system, data_factory, vote_factory) try: genesis_epoch_num = 0 genesis_round_num = 0 genesis_data = await data_factory.create_data(0, b'', genesis_epoch_num, genesis_round_num, ()) genesis_votes = () candidate_data = genesis_data candidate_votes = genesis_votes yield voters, event_system, round_, election, epoch, candidate_data, candidate_votes finally: pass
async def setup_consensus(): node_id = b'x' event_system = MagicMock(EventSystem()) data_factory = MagicMock(DefaultDataFactory(node_id)) vote_factory = MagicMock(DefaultVoteFactory(node_id)) consensus = Consensus(event_system, node_id=node_id, data_factory=data_factory, vote_factory=vote_factory) return event_system, consensus
async def setup_consensus(): def _new_round_mock(self, epoch_num: int, round_num: int, candidate_id: bytes): epoch = self._get_epoch(epoch_num) new_round = RoundMock(epoch, round_num) new_round.candidate_id = candidate_id self._round_pool.add_round(new_round) return new_round voters = [bytes([x]) for x in range(4)] vote_factories = [DefaultVoteFactory(voter) for voter in voters] event_system = MagicMock(EventSystem()) data_factory = MagicMock(DefaultDataFactory(voters[0])) vote_factory = MagicMock(DefaultVoteFactory(voters[0])) consensus = Consensus(event_system, voters[0], data_factory, vote_factory) consensus._new_round = partial(_new_round_mock, consensus) genesis_epoch = RotateEpoch(0, []) now_epoch = RotateEpoch(1, voters) epochs = [genesis_epoch, now_epoch] genesis_data = DefaultData( id_=b"genesis", prev_id=None, proposer_id=None, number=0, epoch_num=0, round_num=0, prev_votes=[] ) datums = [genesis_data] votes = [] await consensus.initialize(commit_id=genesis_data.prev_id, epoch_pool=epochs, data_pool=datums, vote_pool=votes) return consensus, voters, vote_factories, now_epoch, genesis_data
async def setup(voter_num: int): voters = [os.urandom(16) for _ in range(voter_num)] vote_factories = [DefaultVoteFactory(voter) for voter in voters] random.shuffle(vote_factories) data_factory = DefaultDataFactory(voters[0]) data = await data_factory.create_data(data_number=0, prev_id=os.urandom(16), epoch_num=0, round_num=0, prev_votes=()) quorum = random.randint(1, len(vote_factories)) return voters, vote_factories, quorum, data, RoundMessages()
async def test_round_reach_quorum(voter_num: int): round_num = 0 async with setup_items(voter_num, round_num) as (voters, event_system, round_, election, epoch, candidate_data, candidate_votes): # Propose LazyData await round_.round_start() mediator = event_system.get_mediator(DelayedEventMediator) mediator.execute.assert_called_once() delay, event = mediator.execute.call_args_list[0][0] assert delay == TIMEOUT_PROPOSE assert isinstance(event, ReceiveDataEvent) assert event.data.is_lazy() mediator.execute.reset_mock() random.shuffle(voters) vote_factories = [DefaultVoteFactory(voter) for voter in voters] quorum_vote_factories = vote_factories[:epoch.quorum_num] for vote_factory in quorum_vote_factories[:-1]: vote = await vote_factory.create_vote(b"test", candidate_data.id, epoch.num, round_num) await round_.receive_vote(vote) mediator.execute.assert_not_called() none_vote = quorum_vote_factories[-1].create_none_vote( epoch.num, round_num) await round_.receive_vote(none_vote) assert len(mediator.execute.call_args_list) == len(voters) for voter, call_args in zip(voters, mediator.execute.call_args_list): timeout, event = call_args[0] assert timeout == TIMEOUT_VOTE assert isinstance(event, ReceiveVoteEvent) assert event.vote.is_lazy() mediator.execute.reset_mock() none_vote = vote_factories[-1].create_none_vote(epoch.num, round_num) await round_.receive_vote(none_vote) mediator.execute.assert_not_called()
async def test_round_failure_lazy(): epoch, round_num, election_messages, data, voters = await setup() # Round must add LazyData on RoundStart lazy_data = DefaultDataFactory(voters[0]).create_lazy_data( epoch.num, round_num, epoch.get_proposer_id(round_num)) election_messages.add_data(lazy_data) for voter in voters[:epoch.quorum_num]: vote = DefaultVoteFactory(voter).create_lazy_vote( voter, epoch.num, round_num) election_messages.add_vote(vote) election_messages.update() candidate = election_messages.result assert candidate is None
async def setup_election( peer_num: int) -> Tuple[EventSystem, Election, List[bytes]]: event_system = MagicMock(EventSystem()) voters = [bytes([x]) for x in range(peer_num)] data_factory = DefaultDataFactory(TEST_NODE_ID) vote_factory = DefaultVoteFactory(TEST_NODE_ID) data_pool = DataPool() vote_pool = VotePool() genesis_data = DefaultData(id_=CANDIDATE_ID, prev_id=b'', proposer_id=TEST_NODE_ID, number=0, epoch_num=0, round_num=0, prev_votes=()) data_pool.add_data(genesis_data) election = Election(TEST_NODE_ID, RotateEpoch(0, voters), genesis_data.round_num + 1, event_system, data_factory, vote_factory, data_pool, vote_pool) election._candidate_id = CANDIDATE_ID return event_system, election, voters
async def test_round_none_vote_received(): round_num = 0 voter_num = 7 async with setup_items(voter_num, round_num) as (voters, event_system, round_, election, epoch, candidate_data, candidate_votes): # Propose NoneData await round_.round_start() random.shuffle(voters) none_votes = [] for voter in voters[:epoch.quorum_num]: none_vote = DefaultVoteFactory(voter).create_none_vote( epoch.num, round_num) none_votes.append(none_vote) await round_.receive_vote(none_vote) assert len(election.receive_vote.call_args_list) == epoch.quorum_num for none_vote, call_args in zip(none_votes, election.receive_vote.call_args_list): vote, = call_args[0] assert vote is none_vote
RoundEndEvent(True, epoch_num=1, round_num=1, candidate_id=b'd2', commit_id=b'd1'), ] params["normal"]["commit_id"] = b'' params["lazy"] = {} params["lazy"]["epochs"] = [ RotateEpoch(num=0, voters=[]), RotateEpoch(num=1, voters=[b'a']) ] params["lazy"]["votes"] = [ DefaultVoteFactory(b'').create_lazy_vote(voter_id=b'a', epoch_num=1, round_num=0), DefaultVoteFactory(b'').create_lazy_vote(voter_id=b'a', epoch_num=1, round_num=1), DefaultVote(b'v0', data_id=b'd1', commit_id=b'd0', voter_id=b'a', epoch_num=1, round_num=3), ] params["lazy"]["datums"] = [ DefaultData(b'd0', prev_id=b'', proposer_id=b'',