Skip to content

Commit

Permalink
Refactor to remove temporary CONF_PHASE switch
Browse files Browse the repository at this point in the history
  • Loading branch information
sbellem committed Aug 21, 2018
1 parent b2b297a commit 09fec40
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 61 deletions.
115 changes: 67 additions & 48 deletions honeybadgerbft/core/binaryagreement.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,58 @@
from gevent.event import Event

from collections import defaultdict
from distutils.util import strtobool
from os import environ
import logging

from honeybadgerbft.exceptions import RedundantMessageError, AbandonedNodeError


logger = logging.getLogger(__name__)
CONF_PHASE = strtobool(environ.get('CONF_PHASE', '1'))


def handle_conf_messages(*, sender, message, conf_values, pid, bv_signal):
_, r, v = message
assert v in ((0,), (1,), (0, 1))
if sender in conf_values[r][v]:
logger.warn(f'Redundant CONF received {message} by {sender}',
extra={'nodeid': pid, 'epoch': r})
# FIXME: Raise for now to simplify things & be consistent
# with how other TAGs are handled. Will replace the raise
# with a continue statement as part of
# https://github.com/initc3/HoneyBadgerBFT-Python/issues/10
raise RedundantMessageError(
'Redundant CONF received {}'.format(message))

conf_values[r][v].add(sender)
logger.debug(
f'add v = {v} to conf_value[{r}] = {conf_values[r]}',
extra={'nodeid': pid, 'epoch': r},
)

bv_signal.set()


def wait_for_conf_values(*, pid, N, f, epoch, conf_sent, bin_values,
values, conf_values, bv_signal, broadcast):
conf_sent[epoch][tuple(values)] = True
logger.debug(f"broadcast {('CONF', epoch, tuple(values))}",
extra={'nodeid': pid, 'epoch': epoch})
broadcast(('CONF', epoch, tuple(bin_values[epoch])))
while True:
logger.debug(
f'looping ... conf_values[epoch] is: {conf_values[epoch]}',
extra={'nodeid': pid, 'epoch': epoch},
)
if 1 in bin_values[epoch] and len(conf_values[epoch][(1,)]) >= N - f:
return set((1,))
if 0 in bin_values[epoch] and len(conf_values[epoch][(0,)]) >= N - f:
return set((0,))
if (sum(len(senders) for conf_value, senders in
conf_values[epoch].items() if senders and
set(conf_value).issubset(bin_values[epoch])) >= N - f):
return set((0, 1))

bv_signal.clear()
bv_signal.wait()


def binaryagreement(sid, pid, N, f, coin, input, decide, broadcast, receive):
Expand Down Expand Up @@ -105,27 +148,15 @@ def _recv():

bv_signal.set()

elif msg[0] == 'CONF' and CONF_PHASE:
_, r, v = msg
assert v in ((0,), (1,), (0, 1))
if sender in conf_values[r][v]:
logger.warn(f'Redundant CONF received {msg} by {sender}',
extra={'nodeid': pid, 'epoch': r})
# FIXME: Raise for now to simplify things & be consistent
# with how other TAGs are handled. Will replace the raise
# with a continue statement as part of
# https://github.com/initc3/HoneyBadgerBFT-Python/issues/10
raise RedundantMessageError(
'Redundant CONF received {}'.format(msg))

conf_values[r][v].add(sender)
logger.debug(
f'add v = {v} to conf_value[{r}] = {conf_values[r]}',
extra={'nodeid': pid, 'epoch': r},
elif msg[0] == 'CONF':
handle_conf_messages(
sender=sender,
message=msg,
conf_values=conf_values,
pid=pid,
bv_signal=bv_signal,
)

bv_signal.set()

# Translate mmr14 broadcast into coin.broadcast
# _coin_broadcast = lambda (r, sig): broadcast(('COIN', r, sig))
# _coin_recv = Queue()
Expand Down Expand Up @@ -186,35 +217,23 @@ def _recv():
logger.debug(f'Completed AUX phase with values = {values}',
extra={'nodeid': pid, 'epoch': r})

# XXX CONF phase
# CONF phase
logger.debug(
f'block until at least N-f ({N-f}) CONF values are received',
extra={'nodeid': pid, 'epoch': r})
if CONF_PHASE and not conf_sent[r][tuple(values)]:
conf_sent[r][tuple(values)] = True
logger.debug(f"broadcast {('CONF', r, tuple(values))}",
extra={'nodeid': pid, 'epoch': r})
broadcast(('CONF', r, tuple(bin_values[r])))
while True:
logger.debug(
f'looping ... conf_values[r] is: {conf_values[r]}',
extra={'nodeid': pid, 'epoch': r},
)
if 1 in bin_values[r] and len(conf_values[r][(1,)]) >= N - f:
values = set((1,))
break
if 0 in bin_values[r] and len(conf_values[r][(0,)]) >= N - f:
values = set((0,))
break
if (sum(len(senders) for conf_value, senders in
conf_values[r].items() if senders and
set(conf_value).issubset(bin_values[r])) >= N - f):
values = set((0, 1))
break

bv_signal.clear()
bv_signal.wait()

if not conf_sent[r][tuple(values)]:
values = wait_for_conf_values(
pid=pid,
N=N,
f=f,
epoch=r,
conf_sent=conf_sent,
bin_values=bin_values,
values=values,
conf_values=conf_values,
bv_signal=bv_signal,
broadcast=broadcast,
)
logger.debug(f'Completed CONF phase with values = {values}',
extra={'nodeid': pid, 'epoch': r})

Expand Down
21 changes: 9 additions & 12 deletions test/byzantine.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import logging
from collections import defaultdict
from distutils.util import strtobool
from os import environ

import gevent
from gevent.event import AsyncResult, Event
Expand All @@ -11,10 +9,10 @@


logger = logging.getLogger(__name__)
CONF_PHASE = strtobool(environ.get('CONF_PHASE', '1'))

a0, a1, bob, x = 0, 1, 2, 3


def byz_ba_issue_59(sid, pid, N, f, coin, input, decide, broadcast, receive):
"""Modified binary consensus from [MMR14], so that it exhibits a
byzantine behavior as per issue #59
Expand Down Expand Up @@ -46,7 +44,7 @@ def _recv():
while True: # not finished[pid]:
(sender, msg) = receive()
logger.debug(f'receive {msg} from node {sender}',
extra={'nodeid': pid, 'epoch': msg[1]})
extra={'nodeid': pid, 'epoch': msg[1]})
assert sender in range(N)
if msg[0] == 'EST':
# BV_Broadcast message
Expand Down Expand Up @@ -96,7 +94,7 @@ def _recv():

bv_signal.set()

elif msg[0] == 'CONF' and CONF_PHASE:
elif msg[0] == 'CONF':
# CONF message
_, r, v = msg
assert v in ((0,), (1,), (0, 1))
Expand All @@ -117,17 +115,16 @@ def _recv():
bv_signal.set()

# Run the receive loop in the background
_thread_recv = gevent.spawn(_recv)
gevent.spawn(_recv)

# Block waiting for the input
vi = input()
assert vi in (0, 1)
est = vi
r = 0
already_decided = None
while True: # Unbounded number of rounds
logger.info(f'starting round {r} with est set to {est}',
extra={'nodeid': pid, 'epoch': r})
extra={'nodeid': pid, 'epoch': r})
not_est = int(not bool(est))
if not est_sent[r][est]:
est_sent[r][est] = True
Expand Down Expand Up @@ -166,7 +163,7 @@ def _recv():
broadcast(('EST', r, int(not bool(est))), receiver=1)

# XXX CONF phase
if CONF_PHASE and not conf_sent[r][(0, 1)]:
if not conf_sent[r][(0, 1)]:
conf_sent[r][(0, 1)] = True
logger.debug(f"broadcast {('CONF', r, (0, 1))}",
extra={'nodeid': pid, 'epoch': r})
Expand All @@ -189,7 +186,7 @@ def _recv():
extra={'nodeid': pid, 'epoch': r})
broadcast(('AUX', r, not_s), receiver=2)
logger.info(f'exiting round {r}, setting est = s ({s})',
extra={'nodeid': pid, 'epoch': r})
extra={'nodeid': pid, 'epoch': r})
est = s
r += 1

Expand Down Expand Up @@ -388,8 +385,8 @@ def _bc(o, receiver=None):

def makeRecv(j):
def _recv():
(i,o) = queues[j].get()
return (i,o)
i, o = queues[j].get()
return i, o
return _recv

return ([makeBroadcast(i) for i in range(N)],
Expand Down
12 changes: 11 additions & 1 deletion test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,17 @@

from logutils.colorize import ColorizingStreamHandler

from pytest import fixture
import pytest


def pytest_collection_modifyitems(config, items):
if config.getoption('-m') == 'demo':
# do not skip demo tests
return
skip_demo = pytest.mark.skip(reason='need "-m demo" option to run')
for item in items:
if 'demo' in item.keywords:
item.add_marker(skip_demo)


class BadgerColoredLogs(ColorizingStreamHandler):
Expand Down

0 comments on commit 09fec40

Please sign in to comment.