def _broadcast(v): def _deliver(j): buffers[j].put((i,v)) for j in range(N): greenletPacker(Greenlet(_deliver, j), 'random_delay_acs._deliver', (N, t, inputs)).start_later(random.random()*maxdelay)
def _broadcast(v): def _deliver(j): buffers[j].put((i, v)) for j in range(N): greenletPacker( Greenlet(_deliver, j), 'random_delay_acs._deliver', (N, t, inputs)).start_later(random.random() * maxdelay)
def _callback(val): # Get notified for i # Greenlet(callBackWrap(binary_consensus, callbackFactory(i)), pid, # N, t, 1, make_bc(i), reliableBroadcastReceiveQueue[i].get).start() if not i in receivedChannelsFlags: receivedChannelsFlags.append(i) # mylog('B[%d]binary consensus_%d_starts with 1 at %f' % (pid, i, time.time()), verboseLevel=-1) greenletPacker(Greenlet(binary_consensus, i, pid, N, t, 1, decideChannel[i], make_bc(i), reliableBroadcastReceiveQueue[i].get), 'acs.callbackFactory.binary_consensus', (pid, N, t, Q, broadcast, receive)).start()
def _listener(): while True: sender, (tag, m) = receive() if tag == 'B': greenletPacker(Greenlet(CBChannel.put, (sender, m)), 'includeTransaction.CBChannel.put', (pid, N, t, setToInclude, broadcast, receive)).start() elif tag == 'A': greenletPacker(Greenlet(ACSChannel.put, (sender, m) ), 'includeTransaction.ACSChannel.put', (pid, N, t, setToInclude, broadcast, receive)).start()
def _callback(val): # Get notified for i # Greenlet(callBackWrap(binary_consensus, callbackFactory(i)), pid, # N, t, 1, make_bc(i), reliableBroadcastReceiveQueue[i].get).start() if not i in receivedChannelsFlags: receivedChannelsFlags.append(i) # mylog('B[%d]binary consensus_%d_starts with 1 at %f' % (pid, i, time.time()), verboseLevel=-1) greenletPacker( Greenlet(binary_consensus, i, pid, N, t, 1, decideChannel[i], make_bc(i), reliableBroadcastReceiveQueue[i].get), 'acs.callbackFactory.binary_consensus', (pid, N, t, Q, broadcast, receive)).start()
def includeTransaction(pid, N, t, setToInclude, broadcast, receive, send): CBChannel = Queue() ACSChannel = Queue() TXSet = [{} for _ in range(N)] def make_bc_br(i): def _bc_br(m): broadcast(('B', m)) return _bc_br def make_acs_br(i): def _acs_br(m): broadcast(('A', m)) return _acs_br def make_bc_send(i): def _layer_send(j, m): send(j, ('B', m)) return _layer_send def _listener(): while True: sender, (tag, m) = receive() if tag == 'B': greenletPacker(Greenlet(CBChannel.put, (sender, m)), 'includeTransaction.CBChannel.put', (pid, N, t, setToInclude, broadcast, receive)).start() elif tag == 'A': greenletPacker(Greenlet(ACSChannel.put, (sender, m) ), 'includeTransaction.ACSChannel.put', (pid, N, t, setToInclude, broadcast, receive)).start() outputChannel = [Queue(1) for _ in range(N)] def outputCallBack(i): TXSet[i] = outputChannel[i].get() monitoredIntList[i].data = 1 for i in range(N): greenletPacker(Greenlet(outputCallBack, i), 'includeTransaction.outputCallBack', (pid, N, t, setToInclude, broadcast, receive)).start() def callbackFactoryACS(): def _callback(commonSet): # now I know player j has succeeded in broadcasting locker.put(commonSet) return _callback greenletPacker(Greenlet(_listener), 'includeTransaction._listener', (pid, N, t, setToInclude, broadcast, receive)).start() locker = Queue(1) includeTransaction.callbackCounter = 0 monitoredIntList = [MonitoredInt() for _ in range(N)] greenletPacker(Greenlet(consensusBroadcast, pid, N, t, setToInclude, make_bc_br(pid), CBChannel.get, outputChannel, make_bc_send(pid)), 'includeTransaction.consensusBroadcast', (pid, N, t, setToInclude, broadcast, receive)).start() greenletPacker(Greenlet(callBackWrap(acs, callbackFactoryACS()), pid, N, t, monitoredIntList, make_acs_br(pid), ACSChannel.get), 'includeTransaction.callBackWrap(acs, callbackFactoryACS())', (pid, N, t, setToInclude, broadcast, receive)).start() commonSet = locker.get() return commonSet, TXSet
def shared_coin(instance, pid, N, t, broadcast, receive): ''' A dummy version of the Shared Coin :param pid: my id number :param N: the number of parties :param t: the number of byzantine parties :param broadcast: broadcast channel :param receive: receive channel :return: yield values b ''' received = defaultdict(set) outputQueue = defaultdict(lambda: Queue(1)) PK, SKs = getKeys() def _recv(): while True: # New shares for some round r (i, (r, sig)) = receive() assert i in range(N) assert r >= 0 received[r].add((i, serialize(sig))) # After reaching the threshold, compute the output and # make it available locally if len(received[r]) == t + 1: h = PK.hash_message(str((r, instance))) def tmpFunc(r, t): combine_and_verify( h, dict( tuple((t, deserialize1(sig)) for t, sig in received[r])[:t + 1])) outputQueue[r].put(ord(serialize(h)[0]) & 1) # explicitly convert to int Greenlet(tmpFunc, r, t).start() greenletPacker(Greenlet(_recv), 'shared_coin_dummy', (pid, N, t, broadcast, receive)).start() def getCoin(round): broadcast((round, SKs[pid].sign(PK.hash_message(str( (round, instance)))))) # I have to do mapping to 1..l return outputQueue[round].get() return getCoin
def random_delay_acs(N, t, inputs): assert (isinstance(inputs, list)) maxdelay = 0.01 # Instantiate the "broadcast" instruction def makeBroadcast(i): def _broadcast(v): def _deliver(j): buffers[j].put((i, v)) for j in range(N): greenletPacker( Greenlet(_deliver, j), 'random_delay_acs._deliver', (N, t, inputs)).start_later(random.random() * maxdelay) return _broadcast def modifyMonitoredInt(monitoredInt): monitoredInt.data = 1 while True: initBeforeBinaryConsensus() buffers = map(lambda _: Queue(1), range(N)) ts = [] for i in range(N): bc = makeBroadcast(i) recv = buffers[i].get input_clone = [MonitoredInt() for _ in range(N)] for j in range(N): greenletPacker(Greenlet(modifyMonitoredInt, input_clone[j]), 'random_delay_acs.modifyMonitoredInt', (N, t, inputs)).start_later(maxdelay * random.random()) th = greenletPacker(Greenlet(acs, i, N, t, input_clone, bc, recv), 'random_delay_acs.acs', (N, t, inputs)) th.start( ) # start_later(random.random() * maxdelay) is not necessary here ts.append(th) #if True: try: gevent.joinall(ts) break except gevent.hub.LoopExit: # Manual fix for early stop print "End"
def random_delay_acs(N, t, inputs): assert(isinstance(inputs, list)) maxdelay = 0.01 # Instantiate the "broadcast" instruction def makeBroadcast(i): def _broadcast(v): def _deliver(j): buffers[j].put((i,v)) for j in range(N): greenletPacker(Greenlet(_deliver, j), 'random_delay_acs._deliver', (N, t, inputs)).start_later(random.random()*maxdelay) return _broadcast def modifyMonitoredInt(monitoredInt): monitoredInt.data = 1 while True: initBeforeBinaryConsensus() buffers = map(lambda _: Queue(1), range(N)) ts = [] for i in range(N): bc = makeBroadcast(i) recv = buffers[i].get input_clone = [MonitoredInt() for _ in range(N)] for j in range(N): greenletPacker(Greenlet(modifyMonitoredInt, input_clone[j]), 'random_delay_acs.modifyMonitoredInt', (N, t, inputs)).start_later(maxdelay * random.random()) th = greenletPacker(Greenlet(acs, i, N, t, input_clone, bc, recv), 'random_delay_acs.acs', (N, t, inputs)) th.start() # start_later(random.random() * maxdelay) is not necessary here ts.append(th) #if True: try: gevent.joinall(ts) break except gevent.hub.LoopExit: # Manual fix for early stop print "End"
def shared_coin(instance, pid, N, t, broadcast, receive): ''' A dummy version of the Shared Coin :param pid: my id number :param N: the number of parties :param t: the number of byzantine parties :param broadcast: broadcast channel :param receive: receive channel :return: yield values b ''' received = defaultdict(set) outputQueue = defaultdict(lambda: Queue(1)) PK, SKs = getKeys() def _recv(): while True: # New shares for some round r (i, (r, sig)) = receive() assert i in range(N) assert r >= 0 received[r].add((i, serialize(sig))) # After reaching the threshold, compute the output and # make it available locally if len(received[r]) == t + 1: h = PK.hash_message(str((r, instance))) def tmpFunc(r, t): combine_and_verify(h, dict(tuple((t, deserialize1(sig)) for t, sig in received[r])[:t+1])) outputQueue[r].put(ord(serialize(h)[0]) & 1) # explicitly convert to int Greenlet( tmpFunc, r, t ).start() greenletPacker(Greenlet(_recv), 'shared_coin_dummy', (pid, N, t, broadcast, receive)).start() def getCoin(round): broadcast((round, SKs[pid].sign(PK.hash_message(str((round,instance)))))) # I have to do mapping to 1..l return outputQueue[round].get() return getCoin
def _recv(): while True: #not finished[pid]: (i, (tag, m)) = receive() if tag == 'B': # Broadcast message r, msg = m greenletPacker(Greenlet(bcQ[r].put, (i, msg)), 'binary_consensus.bcQ[%d].put' % r, (pid, N, t, vi, decide, broadcast, receive)).start() # In case they block the router elif tag == 'C': # A share of a coin greenletPacker(Greenlet(coinQ.put, (i, m)), 'binary_consensus.coinQ.put', (pid, N, t, vi, decide, broadcast, receive)).start() elif tag == 'A': # Aux message r, msg = m greenletPacker(Greenlet(auxQ[r].put, (i, msg)), 'binary_consensus.auxQ[%d].put' % r, (pid, N, t, vi, decide, broadcast, receive)).start() pass
def _release(): greenletPacker( Greenlet(garbageCleaner, channel), 'binary_consensus.garbageCleaner', (pid, N, t, vi, decide, broadcast, receive)).start()
def binary_consensus(instance, pid, N, t, vi, decide, broadcast, receive): ''' Binary consensus from [MMR 13]. It takes an input vi and will finally write the decided value into _decide_ channel. :param pid: my id number :param N: the number of parties :param t: the number of byzantine parties :param vi: input value, an integer :param decide: deciding channel :param broadcast: broadcast channel :param receive: receive channel :return: ''' #print "********************Sisi step 5: BA,pid: %s, instance: %d******************"%(pid, instance) # Messages received are routed to either a shared coin, the broadcast, or AUX coinQ = Queue(1) bcQ = defaultdict(lambda: Queue(1)) auxQ = defaultdict(lambda: Queue(1)) def _recv(): while True: #not finished[pid]: (i, (tag, m)) = receive() if tag == 'B': # Broadcast message r, msg = m greenletPacker( Greenlet(bcQ[r].put, (i, msg)), 'binary_consensus.bcQ[%d].put' % r, (pid, N, t, vi, decide, broadcast, receive)).start() # In case they block the router elif tag == 'C': # A share of a coin greenletPacker( Greenlet(coinQ.put, (i, m)), 'binary_consensus.coinQ.put', (pid, N, t, vi, decide, broadcast, receive)).start() elif tag == 'A': # Aux message r, msg = m greenletPacker( Greenlet(auxQ[r].put, (i, msg)), 'binary_consensus.auxQ[%d].put' % r, (pid, N, t, vi, decide, broadcast, receive)).start() pass greenletPacker(Greenlet(_recv), 'binary_consensus._recv', (pid, N, t, vi, decide, broadcast, receive)).start() def brcast_get(r): def _recv(*args, **kargs): return bcQ[r].get(*args, **kargs) return _recv received = [defaultdict(set), defaultdict(set)] coin = shared_coin(instance, pid, N, t, makeBroadcastWithTag('C', broadcast), coinQ.get) def getWithProcessing(r, binValues, callBackWaiter): def _recv(*args, **kargs): sender, v = auxQ[r].get(*args, **kargs) assert v in (0, 1) assert sender in range(N) received[v][r].add(sender) # Check if conditions are satisfied threshold = N - t # 2*t + 1 # N - t if True: #not finished[pid]: if len(binValues) == 1: if len(received[binValues[0]] [r]) >= threshold and not callBackWaiter[r].full(): # Check passed callBackWaiter[r].put(binValues) elif len(binValues) == 2: if len(received[0][r].union(received[1][r]) ) >= threshold and not callBackWaiter[r].full(): callBackWaiter[r].put(binValues) elif len(received[0][r] ) >= threshold and not callBackWaiter[r].full(): callBackWaiter[r].put([0]) elif len(received[1][r] ) >= threshold and not callBackWaiter[r].full(): callBackWaiter[r].put([1]) return sender, v return _recv round = 0 est = vi decided = False decidedNum = 0 callBackWaiter = defaultdict(lambda: Queue(1)) while True: # checkFinishedWithGlobalState(N): <- for distributed experiment we don't need this round += 1 # Broadcast EST # TODO: let bv_broadcast receive bvOutputHolder = Queue(2) # 2 possible values binValues = [] def bvOutput(m): if not m in binValues: binValues.append(m) bvOutputHolder.put(m) def getRelease(channel): def _release(): greenletPacker( Greenlet(garbageCleaner, channel), 'binary_consensus.garbageCleaner', (pid, N, t, vi, decide, broadcast, receive)).start() return _release br1 = greenletPacker( Greenlet( bv_broadcast( pid, N, t, makeBroadcastWithTagAndRound('B', broadcast, round), brcast_get(round), bvOutput, getRelease(bcQ[round])), est), 'binary_consensus.bv_broadcast(%d, %d, %d)' % (pid, N, t), (pid, N, t, vi, decide, broadcast, receive)) br1.start() w = bvOutputHolder.get() # Wait until output is not empty broadcast(('A', (round, w))) greenletPacker( Greenlet( loopWrapper(getWithProcessing(round, binValues, callBackWaiter))), 'binary_consensus.loopWrapper(getWithProcessing(round, binValues, callBackWaiter))', (pid, N, t, vi, decide, broadcast, receive)).start() values = callBackWaiter[round].get( ) # wait until the conditions are satisfied s = coin(round) # Here corresponds to a proof that if one party decides at round r, # then in all the following rounds, everybody will propose r as an estimation. (Lemma 2, Lemma 1) # An abandoned party is a party who has decided but no enough peers to help him end the loop. # Lemma: # of abandoned party <= t if decided and decidedNum == s: # infinite-message fix break if len(values) == 1: if values[0] == s: # decide s if not decided: globalState[pid] = "%d" % s decide.put(s) decided = True decidedNum = s else: pass # mylog('[%d] advances rounds from %d caused by values[0](%d)!=s(%d)' % (pid, round, values[0], s), verboseLevel=-1) est = values[0] else: # mylog('[%d] advances rounds from %d caused by len(values)>1 where values=%s' % (pid, round, repr(values)), verboseLevel=-1) est = s
def mv84consensus(pid, N, t, vi, broadcast, receive): ''' Implementation of the multivalue consensus of [TURPIN, COAN, 1984] This will achieve a consensus among all the inputs provided by honest parties, or raise an alert if failed to achieve one. :param pid: my id number :param N: the number of parties :param t: the number of byzantine parties :param vi: input value, an integer :param broadcast: broadcast channel :param receive: receive channel :return: decided value or 0 (default value if failed to reach a concensus) ''' # initialize v and p (same meaning as in the paper) mv84v = defaultdict(lambda: 'Empty') mv84p = defaultdict(lambda: False) # Initialize the locks and local variables mv84WaiterLock = Queue() mv84WaiterLock2 = Queue() mv84ReceiveDiff = set() mv84GetPerplex = set() reliableBroadcastReceiveQueue = Queue() def _listener(): # Hard-working Router for this layer while True: sender, (tag, m) = receive() if tag == 'V': mv84v[sender] = m if m != vi: mv84ReceiveDiff.add(sender) if len(mv84ReceiveDiff) >= (N - t) / 2.0: mv84WaiterLock.put(True) # Fast-Stop: We don't need to wait for the rest (possibly) # malicious parties. if len(mv84v.keys()) >= N - t: mv84WaiterLock.put(False) elif tag == 'B': mv84p[sender] = m if m: mv84GetPerplex.add(sender) if len(mv84GetPerplex) >= N - 2 * t: mv84WaiterLock2.put(True) # Fast-Stop: We don't need to wait for the rest (possibly) # malicious parties. if len(mv84p.keys()) >= N - t: mv84WaiterLock2.put(False) else: # Re-route the msg to inner layer reliableBroadcastReceiveQueue.put((sender, (tag, m))) greenletPacker(Greenlet(_listener), 'mv84consensus._listener', (pid, N, t, vi, broadcast, receive)).start() makeBroadcastWithTag('V', broadcast)(vi) perplexed = mv84WaiterLock.get() # See if I am perplexed makeBroadcastWithTag('B', broadcast)(perplexed) alert = mv84WaiterLock2.get() and 1 or 0 # See if we should alert decideChannel = Queue(1) greenletPacker( Greenlet(binary_consensus, pid, N, t, alert, decideChannel, broadcast, reliableBroadcastReceiveQueue.get), 'mv84consensus.binary_consensus', (pid, N, t, vi, broadcast, receive)).start() agreedAlert = decideChannel.get() if agreedAlert: return 0 # some pre-defined default consensus value else: return vi
def acs(pid, N, t, Q, broadcast, receive): assert (isinstance(Q, list)) assert (len(Q) == N) decideChannel = [Queue(1) for _ in range(N)] receivedChannelsFlags = [] def callbackFactory(i): def _callback(val): # Get notified for i # Greenlet(callBackWrap(binary_consensus, callbackFactory(i)), pid, # N, t, 1, make_bc(i), reliableBroadcastReceiveQueue[i].get).start() if not i in receivedChannelsFlags: receivedChannelsFlags.append(i) # mylog('B[%d]binary consensus_%d_starts with 1 at %f' % (pid, i, time.time()), verboseLevel=-1) greenletPacker( Greenlet(binary_consensus, i, pid, N, t, 1, decideChannel[i], make_bc(i), reliableBroadcastReceiveQueue[i].get), 'acs.callbackFactory.binary_consensus', (pid, N, t, Q, broadcast, receive)).start() return _callback for i, q in enumerate(Q): assert (isinstance(q, MonitoredInt)) q.registerSetCallBack(callbackFactory(i)) def make_bc(i): def _bc(m): broadcast((i, m)) return _bc reliableBroadcastReceiveQueue = [Queue() for x in range(N)] def _listener(): while True: sender, (instance, m) = receive() reliableBroadcastReceiveQueue[instance].put((sender, m)) greenletPacker(Greenlet(_listener), 'acs._listener', (pid, N, t, Q, broadcast, receive)).start() BA = [0] * N locker = Queue(1) locker2 = Queue(1) callbackCounter = [0] def listenerFactory(i, channel): def _listener(): BA[i] = channel.get() if callbackCounter[0] >= 2 * t and (not locker2.full()): locker2.put("Key") # Now we've got 2t+1 1's callbackCounter[0] += 1 if callbackCounter[0] == N and ( not locker.full()): # if we have all of them responded locker.put("Key") return _listener for i in range(N): greenletPacker(Greenlet(listenerFactory(i, decideChannel[i])), 'acs.listenerFactory(i, decideChannel[i])', (pid, N, t, Q, broadcast, receive)).start() locker2.get() # Now we feed 0 to all the other binary consensus protocols for i in range(N): if not i in receivedChannelsFlags: receivedChannelsFlags.append(i) greenletPacker( Greenlet(binary_consensus, i, pid, N, t, 0, decideChannel[i], make_bc(i), reliableBroadcastReceiveQueue[i].get), 'acs.binary_consensus', (pid, N, t, Q, broadcast, receive)).start() locker.get() # Now we can check''' BA = checkBA(BA, N, t) return BA
def multiSigBr(pid, N, t, msg, broadcast, receive, outputs, send): # Since all the parties we have are symmetric, so I implement this function for N instances of A-cast as a whole # Here msg is a set of transactions assert(isinstance(outputs, list)) for i in outputs: assert(isinstance(i, Queue)) keys = getECDSAKeys() Threshold = N - 2 * t Threshold2 = N - t zfecEncoder = zfec.Encoder(Threshold, N) zfecDecoder = zfec.Decoder(Threshold, N) def merkleTree(strList, someHash = coolSHA256Hash): # someHash is a mapping from a int to a int treeLength = 2 ** ceil(math.log(len(strList)) / math.log(2)) mt = [0] * (treeLength * 2) # find a place to put our leaves for i in range(len(strList)): mt[i + treeLength] = someHash(strList[i]) # TODO: need to change strList[i] from a string to an integer here. for i in range(treeLength - 1, 0, -1): # 1, 2, 3, ..., treeLength - 1 # mt[i] = someHash(''.join([chr(ord(a) ^ ord(b)) for a, b in zip(mt[i*2], mt[i*2+1])])) # XOR is commutative mt[i] = someHash(mt[i*2] + mt[i*2+1]) # concat is not commutative return mt def getMerkleBranch(index, mt): res = [] t = index + (len(mt) >> 1) while t > 1: res.append(mt[t ^ 1]) # we are picking up the sibling t /= 2 return res def merkleVerify(val, rootHash, branch, someHash, index): # index has information on whether we are facing a left sibling or a right sibling tmp = someHash(val) tindex = index for br in branch: tmp = someHash((tindex & 1) and br + tmp or tmp + br) tindex >>= 1 if tmp != rootHash: print "Verification failed with", someHash(val), rootHash, branch, tmp == rootHash return tmp == rootHash def Listener(): opinions = [defaultdict(lambda: 0) for _ in range(N)] rootHashes = dict() readyCounter = [defaultdict(lambda: 0) for _ in range(N)] signed = [False]*N readySent = [False] * N reconstDone = [False] * N while True: # main loop sender, msgBundle = receive() if msgBundle[0] == 'i' and not signed[sender]: if keys[sender].verify(sha1hash(''.join([msgBundle[1][0], msgBundle[1][1], ''.join(msgBundle[1][2])])), msgBundle[2]): assert isinstance(msgBundle[1], tuple) if not merkleVerify(msgBundle[1][0], msgBundle[1][1], msgBundle[1][2], coolSHA256Hash, pid): continue if sender in rootHashes: if rootHashes[sender]!= msgBundle[1][1]: print "Cheating caught, exiting" sys.exit(0) else: rootHashes[sender] = msgBundle[1][1] newBundle = (sender, msgBundle[1][0], msgBundle[1][1], msgBundle[1][2]) # assert each frag has a length of step broadcast(('e', newBundle, keys[pid].sign( sha1hash(''.join([str(newBundle[0]), newBundle[1], newBundle[2], ''.join(newBundle[3])])) ))) signed[sender] = True else: raise ECDSASignatureError() elif msgBundle[0] == 'e': if keys[sender].verify(sha1hash(''.join([str(msgBundle[1][0]), msgBundle[1][1], msgBundle[1][2], ''.join(msgBundle[1][3])])), msgBundle[2]): originBundle = msgBundle[1] if not merkleVerify(originBundle[1], originBundle[2], originBundle[3], coolSHA256Hash, sender): continue if originBundle[0] in rootHashes: if rootHashes[originBundle[0]]!= originBundle[2]: print "Cheating caught, exiting" sys.exit(0) else: rootHashes[originBundle[0]] = originBundle[2] opinions[originBundle[0]][sender] = originBundle[1] # We are going to move this part to kekeketktktktk if len(opinions[originBundle[0]]) >= Threshold2 and not readySent[originBundle[0]]: readySent[originBundle[0]] = True broadcast(('r', originBundle[0], originBundle[2])) # We are broadcasting its hash else: raise ECDSASignatureError() elif msgBundle[0] == 'r': readyCounter[msgBundle[1]][msgBundle[2]] += 1 tmp = readyCounter[msgBundle[1]][msgBundle[2]] if tmp >= t+1 and not readySent[msgBundle[1]]: readySent[msgBundle[1]] = True broadcast(('r', msgBundle[1], msgBundle[2])) if tmp >= Threshold2 and not outputs[msgBundle[1]].full() and \ not reconstDone[msgBundle[1]] and len(opinions[msgBundle[1]]) >= Threshold: reconstDone[msgBundle[1]] = True if msgBundle[1] in rootHashes: if rootHashes[msgBundle[1]]!= msgBundle[2]: print "Cheating caught, exiting" sys.exit(0) else: rootHashes[msgBundle[1]] = msgBundle[2] if opinions[msgBundle[1]].values()[0] == '': reconstruction = [''] else: reconstruction = zfecDecoder.decode(opinions[msgBundle[1]].values()[:Threshold], opinions[msgBundle[1]].keys()[:Threshold]) # We only take the first [Threshold] fragments rawbuf = ''.join(reconstruction) buf = rawbuf[:-ord(rawbuf[-1])] # Check root hash step = len(buf) / Threshold + 1 # len(buf) % Threshold == 0 and len(buf) / Threshold or (len(buf) / Threshold + 1) assert step * Threshold - len(buf) < 256 # assumption buf_ = buf.ljust(step * Threshold - 1, '\xFF') + chr(step * Threshold - len(buf)) fragList = [buf_[i*step : (i+1)*step] for i in range(Threshold)] encodedFragList = zfecEncoder.encode(fragList) mt = merkleTree(encodedFragList, coolSHA256Hash) assert rootHashes[msgBundle[1]] == mt[1] # full binary tree if outputs[msgBundle[1]].empty(): outputs[msgBundle[1]].put(buf) greenletPacker(Greenlet(Listener), 'multiSigBr.Listener', (pid, N, t, msg, broadcast, receive, outputs)).start() buf = msg # We already assumed the proposals are byte strings step = len(buf) / Threshold + 1 # len(buf) % Threshold == 0 and len(buf) / Threshold or (len(buf) / Threshold + 1) assert step * Threshold - len(buf) < 256 # assumption buf = buf.ljust(step * Threshold - 1, '\xFF') + chr(step * Threshold - len(buf)) fragList = [buf[i*step : (i+1)*step] for i in range(Threshold)] encodedFragList = zfecEncoder.encode(fragList) mt = merkleTree(encodedFragList, coolSHA256Hash) rootHash = mt[1] # full binary tree for i in range(N): mb = getMerkleBranch(i, mt) # notice that index starts from 1 and pid starts from 0 newBundle = (encodedFragList[i], rootHash, mb) send(i, ('i', newBundle, keys[pid].sign(sha1hash(''.join([newBundle[0], newBundle[1], ''.join(newBundle[2])])))))
def acs(pid, N, t, Q, broadcast, receive): assert(isinstance(Q, list)) assert(len(Q) == N) decideChannel = [Queue(1) for _ in range(N)] receivedChannelsFlags = [] def callbackFactory(i): def _callback(val): # Get notified for i # Greenlet(callBackWrap(binary_consensus, callbackFactory(i)), pid, # N, t, 1, make_bc(i), reliableBroadcastReceiveQueue[i].get).start() if not i in receivedChannelsFlags: receivedChannelsFlags.append(i) # mylog('B[%d]binary consensus_%d_starts with 1 at %f' % (pid, i, time.time()), verboseLevel=-1) greenletPacker(Greenlet(binary_consensus, i, pid, N, t, 1, decideChannel[i], make_bc(i), reliableBroadcastReceiveQueue[i].get), 'acs.callbackFactory.binary_consensus', (pid, N, t, Q, broadcast, receive)).start() return _callback for i, q in enumerate(Q): assert(isinstance(q, MonitoredInt)) q.registerSetCallBack(callbackFactory(i)) def make_bc(i): def _bc(m): broadcast( (i, m) ) return _bc reliableBroadcastReceiveQueue = [Queue() for x in range(N)] def _listener(): while True: sender, (instance, m) = receive() reliableBroadcastReceiveQueue[instance].put( (sender, m) ) greenletPacker(Greenlet(_listener), 'acs._listener', (pid, N, t, Q, broadcast, receive)).start() BA = [0]*N locker = Queue(1) locker2 = Queue(1) callbackCounter = [0] def listenerFactory(i, channel): def _listener(): BA[i] = channel.get() if callbackCounter[0] >= 2*t and (not locker2.full()): locker2.put("Key") # Now we've got 2t+1 1's callbackCounter[0] += 1 if callbackCounter[0] == N and (not locker.full()): # if we have all of them responded locker.put("Key") return _listener for i in range(N): greenletPacker(Greenlet(listenerFactory(i, decideChannel[i])), 'acs.listenerFactory(i, decideChannel[i])', (pid, N, t, Q, broadcast, receive)).start() locker2.get() # Now we feed 0 to all the other binary consensus protocols for i in range(N): if not i in receivedChannelsFlags: receivedChannelsFlags.append(i) greenletPacker(Greenlet(binary_consensus, i, pid, N, t, 0, decideChannel[i], make_bc(i), reliableBroadcastReceiveQueue[i].get), 'acs.binary_consensus', (pid, N, t, Q, broadcast, receive)).start() locker.get() # Now we can check''' BA = checkBA(BA, N, t) return BA
def mv84consensus(pid, N, t, vi, broadcast, receive): ''' Implementation of the multivalue consensus of [TURPIN, COAN, 1984] This will achieve a consensus among all the inputs provided by honest parties, or raise an alert if failed to achieve one. :param pid: my id number :param N: the number of parties :param t: the number of byzantine parties :param vi: input value, an integer :param broadcast: broadcast channel :param receive: receive channel :return: decided value or 0 (default value if failed to reach a concensus) ''' # initialize v and p (same meaning as in the paper) mv84v = defaultdict(lambda: 'Empty') mv84p = defaultdict(lambda: False) # Initialize the locks and local variables mv84WaiterLock = Queue() mv84WaiterLock2 = Queue() mv84ReceiveDiff = set() mv84GetPerplex = set() reliableBroadcastReceiveQueue = Queue() def _listener(): # Hard-working Router for this layer while True: sender, (tag, m) = receive() if tag == 'V': mv84v[sender] = m if m != vi: mv84ReceiveDiff.add(sender) if len(mv84ReceiveDiff) >= (N - t) / 2.0: mv84WaiterLock.put(True) # Fast-Stop: We don't need to wait for the rest (possibly) # malicious parties. if len(mv84v.keys()) >= N - t: mv84WaiterLock.put(False) elif tag == 'B': mv84p[sender] = m if m: mv84GetPerplex.add(sender) if len(mv84GetPerplex) >= N - 2 * t: mv84WaiterLock2.put(True) # Fast-Stop: We don't need to wait for the rest (possibly) # malicious parties. if len(mv84p.keys()) >= N - t: mv84WaiterLock2.put(False) else: # Re-route the msg to inner layer reliableBroadcastReceiveQueue.put( (sender, (tag, m)) ) greenletPacker(Greenlet(_listener), 'mv84consensus._listener', (pid, N, t, vi, broadcast, receive)).start() makeBroadcastWithTag('V', broadcast)(vi) perplexed = mv84WaiterLock.get() # See if I am perplexed makeBroadcastWithTag('B', broadcast)(perplexed) alert = mv84WaiterLock2.get() and 1 or 0 # See if we should alert decideChannel = Queue(1) greenletPacker(Greenlet(binary_consensus, pid, N, t, alert, decideChannel, broadcast, reliableBroadcastReceiveQueue.get), 'mv84consensus.binary_consensus', (pid, N, t, vi, broadcast, receive)).start() agreedAlert = decideChannel.get() if agreedAlert: return 0 # some pre-defined default consensus value else: return vi
def multiSigBr(pid, N, t, msg, broadcast, receive, outputs, send): # Since all the parties we have are symmetric, so I implement this function for N instances of A-cast as a whole # Here msg is a set of transactions assert (isinstance(outputs, list)) for i in outputs: assert (isinstance(i, Queue)) keys = getECDSAKeys() Threshold = N - 2 * t Threshold2 = N - t zfecEncoder = zfec.Encoder(Threshold, N) zfecDecoder = zfec.Decoder(Threshold, N) def merkleTree(strList, someHash=coolSHA256Hash): # someHash is a mapping from a int to a int treeLength = 2**ceil(math.log(len(strList)) / math.log(2)) mt = [0] * (treeLength * 2) # find a place to put our leaves for i in range(len(strList)): mt[i + treeLength] = someHash( strList[i] ) # TODO: need to change strList[i] from a string to an integer here. for i in range(treeLength - 1, 0, -1): # 1, 2, 3, ..., treeLength - 1 # mt[i] = someHash(''.join([chr(ord(a) ^ ord(b)) for a, b in zip(mt[i*2], mt[i*2+1])])) # XOR is commutative mt[i] = someHash(mt[i * 2] + mt[i * 2 + 1]) # concat is not commutative return mt def getMerkleBranch(index, mt): res = [] t = index + (len(mt) >> 1) while t > 1: res.append(mt[t ^ 1]) # we are picking up the sibling t /= 2 return res def merkleVerify(val, rootHash, branch, someHash, index): # index has information on whether we are facing a left sibling or a right sibling tmp = someHash(val) tindex = index for br in branch: tmp = someHash((tindex & 1) and br + tmp or tmp + br) tindex >>= 1 if tmp != rootHash: print "Verification failed with", someHash( val), rootHash, branch, tmp == rootHash return tmp == rootHash def Listener(): opinions = [defaultdict(lambda: 0) for _ in range(N)] rootHashes = dict() readyCounter = [defaultdict(lambda: 0) for _ in range(N)] signed = [False] * N readySent = [False] * N reconstDone = [False] * N while True: # main loop sender, msgBundle = receive() if msgBundle[0] == 'i' and not signed[sender]: if keys[sender].verify( sha1hash(''.join([ msgBundle[1][0], msgBundle[1][1], ''.join(msgBundle[1][2]) ])), msgBundle[2]): assert isinstance(msgBundle[1], tuple) if not merkleVerify(msgBundle[1][0], msgBundle[1][1], msgBundle[1][2], coolSHA256Hash, pid): continue if sender in rootHashes: if rootHashes[sender] != msgBundle[1][1]: print "Cheating caught, exiting" sys.exit(0) else: rootHashes[sender] = msgBundle[1][1] newBundle = (sender, msgBundle[1][0], msgBundle[1][1], msgBundle[1][2] ) # assert each frag has a length of step broadcast(('e', newBundle, keys[pid].sign( sha1hash(''.join([ str(newBundle[0]), newBundle[1], newBundle[2], ''.join(newBundle[3]) ]))))) signed[sender] = True else: raise ECDSASignatureError() elif msgBundle[0] == 'e': if keys[sender].verify( sha1hash(''.join([ str(msgBundle[1][0]), msgBundle[1][1], msgBundle[1][2], ''.join(msgBundle[1][3]) ])), msgBundle[2]): originBundle = msgBundle[1] if not merkleVerify(originBundle[1], originBundle[2], originBundle[3], coolSHA256Hash, sender): continue if originBundle[0] in rootHashes: if rootHashes[originBundle[0]] != originBundle[2]: print "Cheating caught, exiting" sys.exit(0) else: rootHashes[originBundle[0]] = originBundle[2] opinions[originBundle[0]][sender] = originBundle[ 1] # We are going to move this part to kekeketktktktk if len(opinions[originBundle[0]] ) >= Threshold2 and not readySent[originBundle[0]]: readySent[originBundle[0]] = True broadcast( ('r', originBundle[0], originBundle[2])) # We are broadcasting its hash else: raise ECDSASignatureError() elif msgBundle[0] == 'r': readyCounter[msgBundle[1]][msgBundle[2]] += 1 tmp = readyCounter[msgBundle[1]][msgBundle[2]] if tmp >= t + 1 and not readySent[msgBundle[1]]: readySent[msgBundle[1]] = True broadcast(('r', msgBundle[1], msgBundle[2])) if tmp >= Threshold2 and not outputs[msgBundle[1]].full() and \ not reconstDone[msgBundle[1]] and len(opinions[msgBundle[1]]) >= Threshold: reconstDone[msgBundle[1]] = True if msgBundle[1] in rootHashes: if rootHashes[msgBundle[1]] != msgBundle[2]: print "Cheating caught, exiting" sys.exit(0) else: rootHashes[msgBundle[1]] = msgBundle[2] if opinions[msgBundle[1]].values()[0] == '': reconstruction = [''] else: reconstruction = zfecDecoder.decode( opinions[msgBundle[1]].values()[:Threshold], opinions[msgBundle[1]].keys()[:Threshold] ) # We only take the first [Threshold] fragments rawbuf = ''.join(reconstruction) buf = rawbuf[:-ord(rawbuf[-1])] # Check root hash step = len( buf ) / Threshold + 1 # len(buf) % Threshold == 0 and len(buf) / Threshold or (len(buf) / Threshold + 1) assert step * Threshold - len(buf) < 256 # assumption buf_ = buf.ljust(step * Threshold - 1, '\xFF') + chr(step * Threshold - len(buf)) fragList = [ buf_[i * step:(i + 1) * step] for i in range(Threshold) ] encodedFragList = zfecEncoder.encode(fragList) mt = merkleTree(encodedFragList, coolSHA256Hash) assert rootHashes[msgBundle[1]] == mt[ 1] # full binary tree if outputs[msgBundle[1]].empty(): outputs[msgBundle[1]].put(buf) greenletPacker(Greenlet(Listener), 'multiSigBr.Listener', (pid, N, t, msg, broadcast, receive, outputs)).start() buf = msg # We already assumed the proposals are byte strings step = len( buf ) / Threshold + 1 # len(buf) % Threshold == 0 and len(buf) / Threshold or (len(buf) / Threshold + 1) assert step * Threshold - len(buf) < 256 # assumption buf = buf.ljust(step * Threshold - 1, '\xFF') + chr(step * Threshold - len(buf)) fragList = [buf[i * step:(i + 1) * step] for i in range(Threshold)] encodedFragList = zfecEncoder.encode(fragList) mt = merkleTree(encodedFragList, coolSHA256Hash) rootHash = mt[1] # full binary tree for i in range(N): mb = getMerkleBranch( i, mt) # notice that index starts from 1 and pid starts from 0 newBundle = (encodedFragList[i], rootHash, mb) send(i, ('i', newBundle, keys[pid].sign( sha1hash(''.join( [newBundle[0], newBundle[1], ''.join(newBundle[2])])))))
def binary_consensus(instance, pid, N, t, vi, decide, broadcast, receive): ''' Binary consensus from [MMR 13]. It takes an input vi and will finally write the decided value into _decide_ channel. :param pid: my id number :param N: the number of parties :param t: the number of byzantine parties :param vi: input value, an integer :param decide: deciding channel :param broadcast: broadcast channel :param receive: receive channel :return: ''' # Messages received are routed to either a shared coin, the broadcast, or AUX coinQ = Queue(1) bcQ = defaultdict(lambda: Queue(1)) auxQ = defaultdict(lambda: Queue(1)) def _recv(): while True: #not finished[pid]: (i, (tag, m)) = receive() if tag == 'B': # Broadcast message r, msg = m greenletPacker(Greenlet(bcQ[r].put, (i, msg)), 'binary_consensus.bcQ[%d].put' % r, (pid, N, t, vi, decide, broadcast, receive)).start() # In case they block the router elif tag == 'C': # A share of a coin greenletPacker(Greenlet(coinQ.put, (i, m)), 'binary_consensus.coinQ.put', (pid, N, t, vi, decide, broadcast, receive)).start() elif tag == 'A': # Aux message r, msg = m greenletPacker(Greenlet(auxQ[r].put, (i, msg)), 'binary_consensus.auxQ[%d].put' % r, (pid, N, t, vi, decide, broadcast, receive)).start() pass greenletPacker(Greenlet(_recv), 'binary_consensus._recv', (pid, N, t, vi, decide, broadcast, receive)).start() def brcast_get(r): def _recv(*args, **kargs): return bcQ[r].get(*args, **kargs) return _recv received = [defaultdict(set), defaultdict(set)] coin = shared_coin(instance, pid, N, t, makeBroadcastWithTag('C', broadcast), coinQ.get) def getWithProcessing(r, binValues, callBackWaiter): def _recv(*args, **kargs): sender, v = auxQ[r].get(*args, **kargs) assert v in (0, 1) assert sender in range(N) received[v][r].add(sender) # Check if conditions are satisfied threshold = N - t # 2*t + 1 # N - t if True: #not finished[pid]: if len(binValues) == 1: if len(received[binValues[0]][r]) >= threshold and not callBackWaiter[r].full(): # Check passed callBackWaiter[r].put(binValues) elif len(binValues) == 2: if len(received[0][r].union(received[1][r])) >= threshold and not callBackWaiter[r].full(): callBackWaiter[r].put(binValues) elif len(received[0][r]) >= threshold and not callBackWaiter[r].full(): callBackWaiter[r].put([0]) elif len(received[1][r]) >= threshold and not callBackWaiter[r].full(): callBackWaiter[r].put([1]) return sender, v return _recv round = 0 est = vi decided = False decidedNum = 0 callBackWaiter = defaultdict(lambda: Queue(1)) while True: # checkFinishedWithGlobalState(N): <- for distributed experiment we don't need this round += 1 # Broadcast EST # TODO: let bv_broadcast receive bvOutputHolder = Queue(2) # 2 possible values binValues = [] def bvOutput(m): if not m in binValues: binValues.append(m) bvOutputHolder.put(m) def getRelease(channel): def _release(): greenletPacker(Greenlet(garbageCleaner, channel), 'binary_consensus.garbageCleaner', (pid, N, t, vi, decide, broadcast, receive)).start() return _release br1 = greenletPacker(Greenlet( bv_broadcast( pid, N, t, makeBroadcastWithTagAndRound('B', broadcast, round), brcast_get(round), bvOutput, getRelease(bcQ[round])), est), 'binary_consensus.bv_broadcast(%d, %d, %d)' % (pid, N, t), (pid, N, t, vi, decide, broadcast, receive)) br1.start() w = bvOutputHolder.get() # Wait until output is not empty broadcast(('A', (round, w))) greenletPacker(Greenlet(loopWrapper(getWithProcessing(round, binValues, callBackWaiter))), 'binary_consensus.loopWrapper(getWithProcessing(round, binValues, callBackWaiter))', (pid, N, t, vi, decide, broadcast, receive)).start() values = callBackWaiter[round].get() # wait until the conditions are satisfied s = coin(round) # Here corresponds to a proof that if one party decides at round r, # then in all the following rounds, everybody will propose r as an estimation. (Lemma 2, Lemma 1) # An abandoned party is a party who has decided but no enough peers to help him end the loop. # Lemma: # of abandoned party <= t if decided and decidedNum == s: # infinite-message fix break if len(values) == 1: if values[0] == s: # decide s if not decided: globalState[pid] = "%d" % s decide.put(s) decided = True decidedNum = s else: pass # mylog('[%d] advances rounds from %d caused by values[0](%d)!=s(%d)' % (pid, round, values[0], s), verboseLevel=-1) est = values[0] else: # mylog('[%d] advances rounds from %d caused by len(values)>1 where values=%s' % (pid, round, repr(values)), verboseLevel=-1) est = s
def _release(): greenletPacker(Greenlet(garbageCleaner, channel), 'binary_consensus.garbageCleaner', (pid, N, t, vi, decide, broadcast, receive)).start()