def mk_ls(h, r): ls = LockSet() votes = [ Locked(Signature(i, h, r), '0' * 32) for i in range(ls.eligible_votes) ] for i, v in enumerate(votes): ls.add(v) return ls
def test_LockSet_isvalid(): ls = LockSet() votes = [Locked(Signature(i, 2, 3), '0'*32) for i in range(ls.eligible_votes)] for i, v in enumerate(votes): ls.add(v) assert len(ls) == i + 1 if len(ls) < ls.eligible_votes * 2/3.: assert not ls.is_valid else: assert ls.is_valid assert ls.has_quorum # same blockhash
def __init__(self, heightmanager, round=0): self.hm = heightmanager self.cm = heightmanager.cm self.log = self.hm.log assert isinstance(round, int) self.round = round self.height = heightmanager.height self.lockset = LockSet() self.proposal = None self.lock = None self.timeout_time = None print('A:%d Created RoundManager H:%d R:%d' % (self.cm.coinbase, self.hm.height, self.round))
def test_LockSet_isvalid(): ls = LockSet() votes = [ Locked(Signature(i, 2, 3), '0' * 32) for i in range(ls.eligible_votes) ] for i, v in enumerate(votes): ls.add(v) assert len(ls) == i + 1 if len(ls) < ls.eligible_votes * 2 / 3.: assert not ls.is_valid else: assert ls.is_valid assert ls.has_quorum # same blockhash
def test_LockSet(): ls = LockSet() assert not ls assert len(ls) == 0 v1 = Locked(Signature(1, 2, 3), '0' * 32) v2 = Locked(Signature(1, 2, 3), '0' * 32) ls.add(v1) assert ls assert len(ls) == 1 lsh = ls.hash ls.add(v1) assert lsh == ls.hash assert len(ls) == 1 ls.add(v2) assert lsh == ls.hash assert len(ls) == 1
def test_BlockProposal(): ls = LockSet() for i in range(10): s = Signature(i, 2, 3) ls.add(Locked(s, '0' * 32)) bls = LockSet() for i in range(10): s = Signature(i, 1, 2) bls.add(Locked(s, '0' * 32)) assert len(ls) == 10 assert ls.has_quorum block = Block(1, 2, 4, '0' * 32, bls) p = BlockProposal(Signature(1, 2, 4), ls, block) p2 = BlockProposal(Signature(1, 2, 4), ls, block) assert p == p2
def test_LockSet(): ls = LockSet() assert not ls assert len(ls) == 0 v1 = Locked(Signature(1, 2, 3), '0'*32) v2 = Locked(Signature(1, 2, 3), '0'*32) ls.add(v1) assert ls assert len(ls) == 1 lsh = ls.hash ls.add(v1) assert lsh == ls.hash assert len(ls) == 1 ls.add(v2) assert lsh == ls.hash assert len(ls) == 1
def test_BlockProposal(): ls = LockSet() for i in range(10): s = Signature(i, 2, 3) ls.add(Locked(s, '0'*32)) bls = LockSet() for i in range(10): s = Signature(i, 1, 2) bls.add(Locked(s, '0'*32)) assert len(ls) == 10 assert ls.has_quorum block = Block(1, 2, 4, '0'*32, bls) p = BlockProposal(Signature(1, 2, 4), ls, block) p2 = BlockProposal(Signature(1, 2, 4), ls, block) assert p == p2
def test_LockSet_quorums(): combinations = dict(has_quorum=[ [1]*7, [1]*7 + [2]*3, [1]*7 + [None]*3, ], has_noquorum=[ [1]*3 + [2]*3 + [None], [None] * 7, [None] * 10, range(10), range(7) ], has_quorum_possible=[ [1] * 4 + [None]*3, [1] * 4 + [2]*4, [1] * 4 + [2]*3 + [3]*3, [1] * 6 + [2] ]) r, h = 1, 2 hash_ = lambda v: sha3(str(v)) for method, permutations in combinations.items(): for set_ in permutations: assert len(set_) >= 7 ls = LockSet() for address, p in enumerate(set_): if p is not None: ls.add(Locked(Signature(address, h, r), hash_(p))) else: ls.add(NotLocked(Signature(address, h, r))) assert len(ls) >= 7 assert getattr(ls, method) # check stable sort bhs = ls.blockhashes() if len(bhs) > 1: assert ishash(bhs[0][0]) assert isinstance(bhs[0][1], int) if bhs[0][1] == bhs[1][1]: assert bhs[0][0] > bhs[1][0] else: assert bhs[0][1] > bhs[1][1]
def test_LockSet_quorums(): combinations = dict(has_quorum=[ [1] * 7, [1] * 7 + [2] * 3, [1] * 7 + [None] * 3, ], has_noquorum=[[1] * 3 + [2] * 3 + [None], [None] * 7, [None] * 10, range(10), range(7)], has_quorum_possible=[[1] * 4 + [None] * 3, [1] * 4 + [2] * 4, [1] * 4 + [2] * 3 + [3] * 3, [1] * 6 + [2]]) r, h = 1, 2 hash_ = lambda v: sha3(str(v)) for method, permutations in combinations.items(): for set_ in permutations: assert len(set_) >= 7 ls = LockSet() for address, p in enumerate(set_): if p is not None: ls.add(Locked(Signature(address, h, r), hash_(p))) else: ls.add(NotLocked(Signature(address, h, r))) assert len(ls) >= 7 assert getattr(ls, method) # check stable sort bhs = ls.blockhashes() if len(bhs) > 1: assert ishash(bhs[0][0]) assert isinstance(bhs[0][1], int) if bhs[0][1] == bhs[1][1]: assert bhs[0][0] > bhs[1][0] else: assert bhs[0][1] > bhs[1][1]
def mk_ls(h, r): ls = LockSet() votes = [Locked(Signature(i, h, r), '0'*32) for i in range(ls.eligible_votes)] for i, v in enumerate(votes): ls.add(v) return ls
class RoundManager(object): timeout = 1 # secs timeout_round_factor = 1.2 def __init__(self, heightmanager, round=0): self.hm = heightmanager self.cm = heightmanager.cm self.log = self.hm.log assert isinstance(round, int) self.round = round self.height = heightmanager.height self.lockset = LockSet() self.proposal = None self.lock = None self.timeout_time = None print('A:%d Created RoundManager H:%d R:%d' % (self.cm.coinbase, self.hm.height, self.round)) def setup_timeout(self): "setup a timeout for waiting for a proposal" if self.timeout_time is not None or self.proposal: return now = self.cm.env.now delay = self.timeout * self.timeout_round_factor**self.round self.timeout_time = now + delay return delay def add_vote(self, v, force_replace=False): self.log('rm.adding', vote=v, proposal=self.proposal, pid=id(self.proposal)) self.lockset.add(v, force_replace) def add_proposal(self, p): self.log('rm.adding', proposal=p, old=self.proposal) assert not self.proposal self.proposal = p def process(self): self.log('in rm.process', height=self.hm.height, round=self.round) assert self.cm.round == self.round assert self.cm.height == self.hm.height == self.height if self.cm.stopped: self.log('stopped not creating proposal') return p = self.propose() if isinstance(p, BlockProposal): self.cm.add_block(p.block) if p: self.cm.broadcast(p) v = self.vote() if v: self.cm.broadcast(v) assert not self.proposal or self.lock def propose(self): proposer = self.cm.proposer(self.height, self.round) self.log('in propose', proposer=proposer, proposal=self.proposal, lock=self.lock) if proposer != self.cm.coinbase: return if self.proposal: assert self.proposal.signature.address == self.cm.coinbase assert self.lock return lockset = self.cm.last_valid_lockset self.log('in creating proposal', lockset=lockset) if self.round == 0 or lockset.has_noquorum: cl = self.cm.last_committing_lockset # quorum which signs prev block assert cl.has_quorum block = Block(self.cm.coinbase, self.hm.height, self.round, self.cm.head.hash, cl) proposal = BlockProposal(self.signature(), lockset, block) elif lockset.has_quorum_possible: bh = lockset.has_quorum_possible proposal = VotingInstruction(self.signature(), lockset, bh) else: raise Exception('invalid lockset') self.log('created proposal', p=proposal) self.proposal = proposal return proposal def vote(self): if self.lock: return # voted in this round self.log('in vote', proposal=self.proposal, pid=id(self.proposal)) # get last lock on height last_lock = self.hm.last_lock if self.proposal: if isinstance(self.proposal, VotingInstruction): assert self.proposal.lockset.has_quorum_possible self.log('voting on instruction') v = Locked(self.signature(), self.proposal.blockhash) elif not isinstance(last_lock, Locked): assert isinstance(self.proposal, BlockProposal) assert self.proposal.lockset.has_noquorum or self.round == 0 assert self.proposal.block.prevhash == self.cm.head.hash assert self.cm.chainmanager.validate_block( self.proposal.block) # fixme self.log('voting proposed block') v = Locked(self.signature(), self.proposal.block.hash) else: # repeat vote self.log('voting on last vote') v = Locked(self.signature(), last_lock.blockhash) elif self.timeout_time is not None and self.cm.env.now >= self.timeout_time: if isinstance(last_lock, Locked): # repeat vote self.log('timeout voting on last vote') v = Locked(self.signature(), last_lock.blockhash) else: self.log('timeout voting not locked') v = NotLocked(self.signature()) else: return self.log('voted', vote=v) self.lock = v assert self.hm.last_lock == self.lock self.lockset.add(v) return v def signature(self): return Signature(self.cm.coinbase, self.cm.height, self.cm.round)