Skip to content

Commit 09fec40

Browse files
committed
Refactor to remove temporary CONF_PHASE switch
1 parent b2b297a commit 09fec40

File tree

3 files changed

+87
-61
lines changed

3 files changed

+87
-61
lines changed

honeybadgerbft/core/binaryagreement.py

Lines changed: 67 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,58 @@
22
from gevent.event import Event
33

44
from collections import defaultdict
5-
from distutils.util import strtobool
6-
from os import environ
75
import logging
86

97
from honeybadgerbft.exceptions import RedundantMessageError, AbandonedNodeError
108

119

1210
logger = logging.getLogger(__name__)
13-
CONF_PHASE = strtobool(environ.get('CONF_PHASE', '1'))
11+
12+
13+
def handle_conf_messages(*, sender, message, conf_values, pid, bv_signal):
14+
_, r, v = message
15+
assert v in ((0,), (1,), (0, 1))
16+
if sender in conf_values[r][v]:
17+
logger.warn(f'Redundant CONF received {message} by {sender}',
18+
extra={'nodeid': pid, 'epoch': r})
19+
# FIXME: Raise for now to simplify things & be consistent
20+
# with how other TAGs are handled. Will replace the raise
21+
# with a continue statement as part of
22+
# https://github.com/initc3/HoneyBadgerBFT-Python/issues/10
23+
raise RedundantMessageError(
24+
'Redundant CONF received {}'.format(message))
25+
26+
conf_values[r][v].add(sender)
27+
logger.debug(
28+
f'add v = {v} to conf_value[{r}] = {conf_values[r]}',
29+
extra={'nodeid': pid, 'epoch': r},
30+
)
31+
32+
bv_signal.set()
33+
34+
35+
def wait_for_conf_values(*, pid, N, f, epoch, conf_sent, bin_values,
36+
values, conf_values, bv_signal, broadcast):
37+
conf_sent[epoch][tuple(values)] = True
38+
logger.debug(f"broadcast {('CONF', epoch, tuple(values))}",
39+
extra={'nodeid': pid, 'epoch': epoch})
40+
broadcast(('CONF', epoch, tuple(bin_values[epoch])))
41+
while True:
42+
logger.debug(
43+
f'looping ... conf_values[epoch] is: {conf_values[epoch]}',
44+
extra={'nodeid': pid, 'epoch': epoch},
45+
)
46+
if 1 in bin_values[epoch] and len(conf_values[epoch][(1,)]) >= N - f:
47+
return set((1,))
48+
if 0 in bin_values[epoch] and len(conf_values[epoch][(0,)]) >= N - f:
49+
return set((0,))
50+
if (sum(len(senders) for conf_value, senders in
51+
conf_values[epoch].items() if senders and
52+
set(conf_value).issubset(bin_values[epoch])) >= N - f):
53+
return set((0, 1))
54+
55+
bv_signal.clear()
56+
bv_signal.wait()
1457

1558

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

106149
bv_signal.set()
107150

108-
elif msg[0] == 'CONF' and CONF_PHASE:
109-
_, r, v = msg
110-
assert v in ((0,), (1,), (0, 1))
111-
if sender in conf_values[r][v]:
112-
logger.warn(f'Redundant CONF received {msg} by {sender}',
113-
extra={'nodeid': pid, 'epoch': r})
114-
# FIXME: Raise for now to simplify things & be consistent
115-
# with how other TAGs are handled. Will replace the raise
116-
# with a continue statement as part of
117-
# https://github.com/initc3/HoneyBadgerBFT-Python/issues/10
118-
raise RedundantMessageError(
119-
'Redundant CONF received {}'.format(msg))
120-
121-
conf_values[r][v].add(sender)
122-
logger.debug(
123-
f'add v = {v} to conf_value[{r}] = {conf_values[r]}',
124-
extra={'nodeid': pid, 'epoch': r},
151+
elif msg[0] == 'CONF':
152+
handle_conf_messages(
153+
sender=sender,
154+
message=msg,
155+
conf_values=conf_values,
156+
pid=pid,
157+
bv_signal=bv_signal,
125158
)
126159

127-
bv_signal.set()
128-
129160
# Translate mmr14 broadcast into coin.broadcast
130161
# _coin_broadcast = lambda (r, sig): broadcast(('COIN', r, sig))
131162
# _coin_recv = Queue()
@@ -186,35 +217,23 @@ def _recv():
186217
logger.debug(f'Completed AUX phase with values = {values}',
187218
extra={'nodeid': pid, 'epoch': r})
188219

189-
# XXX CONF phase
220+
# CONF phase
190221
logger.debug(
191222
f'block until at least N-f ({N-f}) CONF values are received',
192223
extra={'nodeid': pid, 'epoch': r})
193-
if CONF_PHASE and not conf_sent[r][tuple(values)]:
194-
conf_sent[r][tuple(values)] = True
195-
logger.debug(f"broadcast {('CONF', r, tuple(values))}",
196-
extra={'nodeid': pid, 'epoch': r})
197-
broadcast(('CONF', r, tuple(bin_values[r])))
198-
while True:
199-
logger.debug(
200-
f'looping ... conf_values[r] is: {conf_values[r]}',
201-
extra={'nodeid': pid, 'epoch': r},
202-
)
203-
if 1 in bin_values[r] and len(conf_values[r][(1,)]) >= N - f:
204-
values = set((1,))
205-
break
206-
if 0 in bin_values[r] and len(conf_values[r][(0,)]) >= N - f:
207-
values = set((0,))
208-
break
209-
if (sum(len(senders) for conf_value, senders in
210-
conf_values[r].items() if senders and
211-
set(conf_value).issubset(bin_values[r])) >= N - f):
212-
values = set((0, 1))
213-
break
214-
215-
bv_signal.clear()
216-
bv_signal.wait()
217-
224+
if not conf_sent[r][tuple(values)]:
225+
values = wait_for_conf_values(
226+
pid=pid,
227+
N=N,
228+
f=f,
229+
epoch=r,
230+
conf_sent=conf_sent,
231+
bin_values=bin_values,
232+
values=values,
233+
conf_values=conf_values,
234+
bv_signal=bv_signal,
235+
broadcast=broadcast,
236+
)
218237
logger.debug(f'Completed CONF phase with values = {values}',
219238
extra={'nodeid': pid, 'epoch': r})
220239

test/byzantine.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import logging
22
from collections import defaultdict
3-
from distutils.util import strtobool
4-
from os import environ
53

64
import gevent
75
from gevent.event import AsyncResult, Event
@@ -11,10 +9,10 @@
119

1210

1311
logger = logging.getLogger(__name__)
14-
CONF_PHASE = strtobool(environ.get('CONF_PHASE', '1'))
1512

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

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

9795
bv_signal.set()
9896

99-
elif msg[0] == 'CONF' and CONF_PHASE:
97+
elif msg[0] == 'CONF':
10098
# CONF message
10199
_, r, v = msg
102100
assert v in ((0,), (1,), (0, 1))
@@ -117,17 +115,16 @@ def _recv():
117115
bv_signal.set()
118116

119117
# Run the receive loop in the background
120-
_thread_recv = gevent.spawn(_recv)
118+
gevent.spawn(_recv)
121119

122120
# Block waiting for the input
123121
vi = input()
124122
assert vi in (0, 1)
125123
est = vi
126124
r = 0
127-
already_decided = None
128125
while True: # Unbounded number of rounds
129126
logger.info(f'starting round {r} with est set to {est}',
130-
extra={'nodeid': pid, 'epoch': r})
127+
extra={'nodeid': pid, 'epoch': r})
131128
not_est = int(not bool(est))
132129
if not est_sent[r][est]:
133130
est_sent[r][est] = True
@@ -166,7 +163,7 @@ def _recv():
166163
broadcast(('EST', r, int(not bool(est))), receiver=1)
167164

168165
# XXX CONF phase
169-
if CONF_PHASE and not conf_sent[r][(0, 1)]:
166+
if not conf_sent[r][(0, 1)]:
170167
conf_sent[r][(0, 1)] = True
171168
logger.debug(f"broadcast {('CONF', r, (0, 1))}",
172169
extra={'nodeid': pid, 'epoch': r})
@@ -189,7 +186,7 @@ def _recv():
189186
extra={'nodeid': pid, 'epoch': r})
190187
broadcast(('AUX', r, not_s), receiver=2)
191188
logger.info(f'exiting round {r}, setting est = s ({s})',
192-
extra={'nodeid': pid, 'epoch': r})
189+
extra={'nodeid': pid, 'epoch': r})
193190
est = s
194191
r += 1
195192

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

389386
def makeRecv(j):
390387
def _recv():
391-
(i,o) = queues[j].get()
392-
return (i,o)
388+
i, o = queues[j].get()
389+
return i, o
393390
return _recv
394391

395392
return ([makeBroadcast(i) for i in range(N)],

test/conftest.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,17 @@
22

33
from logutils.colorize import ColorizingStreamHandler
44

5-
from pytest import fixture
5+
import pytest
6+
7+
8+
def pytest_collection_modifyitems(config, items):
9+
if config.getoption('-m') == 'demo':
10+
# do not skip demo tests
11+
return
12+
skip_demo = pytest.mark.skip(reason='need "-m demo" option to run')
13+
for item in items:
14+
if 'demo' in item.keywords:
15+
item.add_marker(skip_demo)
616

717

818
class BadgerColoredLogs(ColorizingStreamHandler):

0 commit comments

Comments
 (0)