Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added VABA Implementation #53

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,16 @@ services:
dockerfile: Dockerfile
volumes:
- .:/usr/local/src/HoneyBadgerBFT
command: pytest -v --cov=honeybadgerbft
command: pytest -s test/test_vaba.py

honeybadger-test:
build:
context: .
dockerfile: Dockerfile
volumes:
- .:/usr/local/src/HoneyBadgerBFT
command: bash

builddocs:
build:
context: .
Expand Down
104 changes: 104 additions & 0 deletions honeybadgerbft/core/leaderelection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import logging

from honeybadgerbft.crypto.threshsig.boldyreva import serialize
from collections import defaultdict
from gevent import Greenlet
from gevent.queue import Queue
import hashlib

logger = logging.getLogger(__name__)


def hash(x):
return hashlib.sha256(x).digest()

class CommonCoinFailureException(Exception):
"""Raised for common coin failures."""
pass


def hash(x):
return hashlib.sha256(x).digest()


def leader_election(sid, pid, N, f, PK, SK, broadcast, receive):
"""A leader election phase same as common coin logic

:param sid: a unique instance id
:param pid: my id number
:param N: number of parties
:param f: fault tolerance, :math:`f+1` shares needed to get the coin
:param PK: ``boldyreva.TBLSPublicKey``
:param SK: ``boldyreva.TBLSPrivateKey``
:param broadcast: broadcast channel
:param receive: receive channel
:return: a function ``getCoin()``, where ``getCoin(r)`` blocks
"""
assert PK.k == f+1
assert PK.l == N # noqa: E741
received = defaultdict(dict)
outputQueue = defaultdict(lambda: Queue(1))

def _recv():
while True: # main receive loop
logger.debug(f'entering loop',
extra={'nodeid': pid, 'epoch': '?'})
# New shares for some round r, from sender i
(i, (_, r, sig)) = receive()
logger.debug(f'received i, _, r, sig: {i, _, r, sig}',
extra={'nodeid': pid, 'epoch': r})
assert i in range(N)
assert r >= 0
if i in received[r]:
print("redundant coin sig received", (sid, pid, i, r))
continue

h = PK.hash_message(str((sid, r)))

# TODO: Accountability: Optimistically skip verifying
# each share, knowing evidence available later
try:
PK.verify_share(sig, i, h)
except AssertionError:
print("Signature share failed!", (sid, pid, i, r))
continue

received[r][i] = sig

# After reaching the threshold, compute the output and
# make it available locally
logger.debug(
f'if len(received[r]) == f + 1: {len(received[r]) == f + 1}',
extra={'nodeid': pid, 'epoch': r},
)
if len(received[r]) == f + 1:

# Verify and get the combined signature
sigs = dict(list(received[r].items())[:f+1])
sig = PK.combine_shares(sigs)
assert PK.verify_signature(sig, h)

# Compute the bit from the least bit of the hash
bit = hash(serialize(sig))[0] % N
logger.debug(f'put bit {bit} in output queue',
extra={'nodeid': pid, 'epoch': r})
outputQueue[r].put_nowait(bit)

# greenletPacker(Greenlet(_recv), 'shared_coin', (pid, N, f, broadcast, receive)).start()
Greenlet(_recv).start()

def getCoin(round):
"""Gets a coin.

:param round: the epoch/round.
:returns: a coin.

"""
# I have to do mapping to 1..l
h = PK.hash_message(str((sid, round)))
logger.debug(f"broadcast {('COIN', round, SK.sign(h))}",
extra={'nodeid': pid, 'epoch': round})
broadcast(('COIN', round, SK.sign(h)))
return outputQueue[round].get()

return getCoin
72 changes: 72 additions & 0 deletions honeybadgerbft/core/pbbroadcast.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import logging
from gevent import Greenlet
from gevent.queue import Queue

logger = logging.getLogger(__name__)


def pbbroadcast(id, j, pid, N, f, leader, input, receive, send, broadcast, PK, SK):
S = {}
output_queue = Queue(1)
input_value = []
stop = False

def _recv():
(i, (_, cmd, v)) = receive()
assert i == leader
nonlocal stop

logger.debug(f'follower received i, _, r, sig: {i, _, cmd, v}',
extra={'nodeid': pid, 'epoch': j})
if cmd == 'send':
if stop == False:
h = PK.hash_message(str((id, v)))
stop = True
output_queue.put_nowait(v)
# logger.debug(f'follower acked i, id, ack, sig: {i, id, "ack", SK.sign(h)}',
# extra={'nodeid': pid, 'epoch': j})
send(leader, (id, 'ack', SK.sign(h)))


else:
assert cmd == 'abandon'
stop = True

def _leader_recv():
while True:
(i, (_, cmd, v)) = receive()
if cmd == 'send':
h = PK.hash_message(str((id, v)))
send(leader, (id, 'ack', SK.sign(h)))
continue
(i, (_, ack, sig_share)) = (i, (_, cmd, v))
logger.debug(f'leader received i, _, r, sig: {i, _, ack, sig_share}',
extra={'nodeid': pid, 'epoch': j})
assert ack == 'ack'
h = PK.hash_message(str((id, input_value[0])))
try:
PK.verify_share(sig_share, i, h)
except AssertionError:
print("Signature share failed!", (id, pid, i, j))
continue
S[i] = sig_share

if len(S) == 2 * f + 1:
sigs = dict(list(S.items())[:2 * f + 1])
sig = PK.combine_shares(sigs)
assert PK.verify_signature(sig, h)
logger.debug(f'put sig {sig} in output queue',
extra={'nodeid': pid, 'epoch': j})
output_queue.put_nowait(sig)

if leader == pid:
# Todo: add signature value
v = input()
input_value.append(v)
Greenlet(_leader_recv).start()
broadcast((id, 'send', v))

else:
Greenlet(_recv).start()

return output_queue.get()
115 changes: 115 additions & 0 deletions honeybadgerbft/core/pbbroadcast_4s.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import logging
from gevent import Greenlet
from gevent.queue import Queue

logger = logging.getLogger(__name__)


def pbbroadcast_4s(id, pid, N, f, leader, input, receive, send, broadcast, PK, SK):
S = [{} for _ in range(4)]
output_queue = Queue(1)
key = Queue(1)
lock = Queue(1)
commit = Queue(1)
input_value = []
stop = [False, False, False, False]

def _recv():
while True:
(i, (_, j, cmd, (v, sig_ex, sig_in))) = receive()
assert i == leader
assert _ == id
nonlocal stop

logger.debug(f'follower received i, _, r, sig: {i, _, j, cmd, v, sig_ex, sig_in}',
extra={'nodeid': pid, 'epoch': j})
if cmd == 'send':
if stop[j] == False and ex_sbc_validation(_, j, v, sig_ex, sig_in):
h = PK.hash_message(str((id, j, v)))
stop[j] = True
if j == 1:
key.put_nowait((id, (v, sig_in)))
elif j == 2:
lock.put_nowait((id, (v, sig_in)))
elif j == 3:
commit.put_nowait((id, (v, sig_in)))
output_queue.put_nowait(v)
logger.debug(f'follower acked i, id, ack, sig: {i, id, j, "ack", SK.sign(h)}',
extra={'nodeid': pid, 'epoch': j})
send(leader, (id, j, 'ack', SK.sign(h)))


else:
assert cmd == 'abandon'
stop[j] = True

def ex_sbc_validation(id, j, v, sig_ex, sig_in):
if j == 0 and ex_bc_validation(id, v, sig_ex):
return True
if j > 0 and threshold_validate(id, j - 1, v, sig_in):
return True
return False

def ex_bc_validation(id, v, sig_ex):
# TODO: add external validation logic
return True

def threshold_validate(id, j, v, sig_in):
h = PK.hash_message(str((id, j, v)))
try:
PK.verify_signature(sig_in, h)
return True
except AssertionError:
print("threshold_validate failure!")
return False

def _leader_recv():
while True:
(i, (_, j, cmd, v)) = receive()

assert _ == id
if cmd == 'send':
logger.debug(f'leader received i, _, r, sig: {i, _, j, cmd, v}',
extra={'nodeid': pid, 'epoch': j})
h = PK.hash_message(str((id, j, v[0])))
send(leader, (id, j, 'ack', SK.sign(h)))
continue

# if message command is ack
(i, (_, j, ack, sig_share)) = (i, (_, j, cmd, v))
# logger.debug(f'leader received i, _, r, sig: {i, _, j, ack, sig_share}',
# extra={'nodeid': pid, 'epoch': j})
assert ack == 'ack'
h = PK.hash_message(str((id, j, input_value[0])))
try:
PK.verify_share(sig_share, i, h)
print("Signature share succeeded!", (id, pid, i, j, input_value[0]))
except AssertionError:
print("Signature share failed!", (id, pid, i, j, input_value[0]))
continue
S[j][i] = sig_share

if len(S[j]) == 2 * f + 1:
sigs = dict(list(S[j].items())[:2 * f + 1])
sig = PK.combine_shares(sigs)
assert PK.verify_signature(sig, h)
logger.debug(f'put sig {sig} in output queue',
extra={'nodeid': pid, 'epoch': j})
output_queue.put_nowait(sig)

if leader == pid:
v = input()
sig_ex = None
sig_in = None
input_value.append(v)
Greenlet(_leader_recv).start()
for j in range(4):
print("round %d broadcast start\n" % (j))
broadcast((id, j, 'send', (v, sig_ex, sig_in)))
sig_in = output_queue.get()
return sig_in

else:
Greenlet(_recv).start()

return output_queue.get()
Loading