-
Notifications
You must be signed in to change notification settings - Fork 64
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
remove redundant gen_test_bit Update tutorial Create README.md Update README.md make more foolproof Update README.md Update README.md Fixes to tutorial (thx sbellem)
- Loading branch information
Showing
6 changed files
with
269 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,4 +2,5 @@ | |
max_line_length=89 | ||
exclude = | ||
charm/, | ||
.eggs/ | ||
.eggs/, | ||
apps/tutorial/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
HoneyBadgerMPC Tutorial | ||
== | ||
This folder [(`apps/tutorial`)](./) contains a few tutorials as a starting point for developing with HoneyBadgerMPC. The tutorials assume you have a working development environment (instructions). | ||
|
||
|
||
I. [`hbmpc-tutorial-1.py`](./hbmpc-tutorial-1.py) | ||
-- | ||
The first tutorial gives a tour of the MPC programming environment. To summarize: | ||
- Secret shares are represented by a `Share` object. | ||
- linear operations on `Share` objects are just computed locally | ||
- `share.open()`: causes the servers to communicate with each other to open a share | ||
- `ctx.preproc.get_random()`: (and related functions) can be used to get fetch random `Share`s from preprocessing. This is used when multiplying two `Share` objects | ||
- a `Share` supports dataflow programming with Futures, like in Viff | ||
- `SharreArray(shares).open()`: batch methods (TODO) | ||
- an MPC program run is always run in an MPC context, like `def my_mpc_program(ctx, ...)`. When it's running, `ctx.myid` gives the name of the currently running server | ||
- The simplest way to run the MPC program is with `TaskProgramRunner`, which runs each server as a lightweight process in a simulated network. This is the simplest operating mode, so it's the way to start when writing an MPC program and testing it | ||
- There are some lines you can uncomment to simulate Byzantine faults and see how HoneyBadgerMPC handles them | ||
|
||
Simple MPC programs: | ||
- `beaver_multiply` the hello world of MPC program | ||
- `random_permute_pair` as used in the mixing application | ||
- `dot product` | ||
|
||
More examples of this programming model can be found in: | ||
- [`honeybadgermpc/progs/mixins/share_arithmetic.py`](../../honeybadgermpc/progs/mixins/share_arithmetic.py) | ||
- operations on fixed-point numbers (rather than field elements) [honeybadgermpc/progs/fixedpoint.py](../../honeybadgermpc/progs/fixedpoint.py) (TODO) | ||
- [`honeybadgermpc/progs/mimc.py`](../../honeybadgermpc/progs/mimc.py) symmetric key cryptography | ||
- [`honeybadgermpc/progs/jubjub.py`](../../honeybadgermpc/progs/jubjub.py) public key cryptography | ||
- [`apps/asynchromix/butterfly_network.py`](../../apps/asynchromix/butterfly_network.py) switching network based on the random pair permutation | ||
|
||
To check the development environment works: | ||
- Follow [these instructions](../../docs/development/getting-started.rst#managing-your-development-environment-with-docker-compose) to set up the `docker-compose` development environment | ||
- The tutorials assume you have a shell session in the development container, so run: | ||
``` | ||
$ docker-compose run --rm honeybadgermpc bash | ||
root@{containerid}:/usr/src/HoneyBadgerMPC# python apps/tutorial/hbmpc-tutorial-1.py | ||
``` | ||
and look for the output `Tutorial 1 ran successfully` | ||
|
||
II. [`hbmpc-tutorial-2.py`](./hbmpc-tutorial-2.py) | ||
--- | ||
The second tutorial shows how to run the MPC program in different processes that communicate over sockets. Run it with | ||
``` | ||
scripts/launch-tmuxlocal.sh apps/tutorial/hbmpc-tutorial-2.py conf/mpc/local | ||
``` | ||
This scripts launches `4` processes (in the `n=4,t=1` setting) each in its own terminal subwindow. | ||
You can crash one other terminals at any time, the rest will still be available. | ||
|
||
This script also creates a simulated latency using the `tc` tool (look for the call to [`scripts/latency-control.sh`](../../scripts/latency-control.sh). The latency and jitter can be changed by modifying the script. | ||
|
||
III. Blockchain integration | ||
--- | ||
Tutorial coming soon... but for now you can run and look at the examples in [`apps/asynchromix`](../asynchromix) which uses Ethereum (`web3py` library, and a Solidity smart contract) as an MPC coordinator and broadcast channel. | ||
``` | ||
python apps/asynchromix/asynchromix.py | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
""" | ||
hbMPC tutorial 1. Running sample MPC programs in the testing simulator | ||
""" | ||
import asyncio | ||
from honeybadgermpc.mpc import TaskProgramRunner | ||
from honeybadgermpc.progs.mixins.dataflow import ( | ||
Share, ShareArray, ShareFuture, GFElementFuture) | ||
from honeybadgermpc.preprocessing import ( | ||
PreProcessedElements as FakePreProcessedElements) | ||
from honeybadgermpc.utils.typecheck import TypeCheck | ||
from honeybadgermpc.progs.mixins.share_arithmetic import ( | ||
MixinConstants, BeaverMultiply, BeaverMultiplyArrays) | ||
config = {MixinConstants.MultiplyShareArray: BeaverMultiplyArrays(), | ||
MixinConstants.MultiplyShare: BeaverMultiply(), } | ||
|
||
|
||
@TypeCheck() | ||
async def beaver_multiply(ctx, x: Share, y: Share): | ||
"""The hello world of MPC: beaver multiplication | ||
- Linear operations on Share objects are easy | ||
- Shares of random values are available from preprocessing | ||
- Opening a Share returns a GFElementFuture | ||
""" | ||
a, b, ab = ctx.preproc.get_triple(ctx) | ||
D = await (x - a).open() | ||
E = await (y - b).open() | ||
|
||
# D*E is multiplying GFElements | ||
# D*b, E*a are multiplying GFElement x Share -> Share | ||
# ab is a Share | ||
# overall the sum is a Share | ||
|
||
xy = (D * E) + (D * b) + (E * a) + ab | ||
return xy | ||
|
||
|
||
async def random_permute_pair(ctx, x, y): | ||
""" | ||
Randomly permute a pair of secret shared values. | ||
Input: `x`, `y` are `Share` objects | ||
Output: A pair of `Share` objects `(o1,o2)`, which are fresh | ||
shares that take on the value `(x,y)` or `(y,x)` with equal | ||
probability | ||
Preprocessing: | ||
- One random bit | ||
- One beaver multiplication | ||
""" | ||
b = ctx.preproc.get_bit(ctx) | ||
# just a local scalar multiplication | ||
one_or_minus_one = ctx.field(2) * b - ctx.field(1) | ||
m = one_or_minus_one * (x - y) | ||
o1 = (x + y + m) * (1 / ctx.field(2)) | ||
o2 = (x + y - m) * (1 / ctx.field(2)) | ||
return (o1, o2) | ||
|
||
|
||
# Working with arrays | ||
def dot_product(ctx, x_shares, y_shares): | ||
"""Although the above example of Beaver multiplication is perfectly valid, | ||
you can also just use the `*` operator of the Share object, which does | ||
the same thing. | ||
This is also an example of dataflow programming. The return value of this | ||
operation is a `ShareFuture`, which defines addition and multiplication | ||
operations as well (like in Viff). As a result, all of these multiplications | ||
can take place in parallel. | ||
""" | ||
res = ctx.ShareFuture() | ||
res.set_result(ctx.Share(0)) | ||
for x, y in zip(x_shares, y_shares): | ||
res += x * y | ||
return res | ||
|
||
|
||
async def prog(ctx): | ||
# Test with random sharings of hardcoded values | ||
ctx.preproc = FakePreProcessedElements() | ||
x = ctx.Share(5) + ctx.preproc.get_zero(ctx) | ||
y = ctx.Share(7) + ctx.preproc.get_zero(ctx) | ||
xy = await beaver_multiply(ctx, x, y) | ||
|
||
# Check openings of the multiplied values | ||
X = await x.open() | ||
Y = await y.open() | ||
XY = await xy.open() | ||
assert XY == X * Y | ||
print(f'[{ctx.myid}] Beaver Multiplication OK') | ||
# print(f'x:{y} y:{x}: xy:{xy}') | ||
# print(f'x.open(): {X} y.open(): {Y} xy.open(): {XY}') | ||
|
||
# Sample dot product (4 * 5 + 8 * 10) == 100 | ||
# Each product of two Shares returns a ShareFuture | ||
a = ctx.Share(4) + ctx.preproc.get_zero(ctx) | ||
b = ctx.Share(8) + ctx.preproc.get_zero(ctx) | ||
c = ctx.Share(5) + ctx.preproc.get_zero(ctx) | ||
d = ctx.Share(10) + ctx.preproc.get_zero(ctx) | ||
res = dot_product(ctx, (a, b), (c, d)) | ||
res_ = await res.open() | ||
assert res_ == res | ||
print(f'[{ctx.myid}] Dot Product OK') | ||
|
||
# Randomly permute (x,y) or (y,x) | ||
o1, o2 = await random_permute_pair(ctx, x, y) | ||
# Unless you open it, no one knows which permutation it is | ||
O1 = await o1.open() | ||
O2 = await o2.open() | ||
# print(f'O1:{O1} O2:{O2}') | ||
assert O1 in (X, Y) and O2 in (X, Y) | ||
print(f'[{ctx.myid}] Permute Pair OK') | ||
|
||
|
||
async def tutorial_1(): | ||
# Create a test network of 4 nodes (no sockets, just asyncio tasks) | ||
n, t = 4, 1 | ||
pp = FakePreProcessedElements() | ||
pp.generate_zeros(100, n, t) | ||
pp.generate_triples(100, n, t) | ||
pp.generate_bits(100, n, t) | ||
program_runner = TaskProgramRunner(n, t, config) | ||
program_runner.add(prog) | ||
results = await program_runner.join() | ||
return results | ||
|
||
|
||
def main(): | ||
# Run the tutorials | ||
asyncio.set_event_loop(asyncio.new_event_loop()) | ||
loop = asyncio.get_event_loop() | ||
loop.run_until_complete(tutorial_1()) | ||
# loop.run_until_complete(tutorial_2()) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() | ||
print("Tutorial 1 ran successfully") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
""" | ||
hbMPC tutorial 2. | ||
Instructions: | ||
run this with | ||
``` | ||
scripts/launch-tmuxlocal.sh apps/tutorial/hbmpc-tutorial-2.py conf/mpc/local | ||
``` | ||
""" | ||
import asyncio | ||
import logging | ||
from honeybadgermpc.preprocessing import ( | ||
PreProcessedElements as FakePreProcessedElements, | ||
wait_for_preprocessing, preprocessing_done) | ||
from honeybadgermpc.progs.mixins.dataflow import ( | ||
Share, ShareArray, ShareFuture, GFElementFuture) | ||
from honeybadgermpc.utils.typecheck import TypeCheck | ||
from honeybadgermpc.progs.mixins.share_arithmetic import ( | ||
MixinConstants, BeaverMultiply, BeaverMultiplyArrays) | ||
mpc_config = {MixinConstants.MultiplyShareArray: BeaverMultiplyArrays(), | ||
MixinConstants.MultiplyShare: BeaverMultiply(), } | ||
|
||
|
||
async def dot_product(ctx, xs, ys): | ||
return sum((x * y for x, y in zip(xs, ys)), ctx.Share(0)) | ||
|
||
async def prog(ctx, k=50): | ||
# Computing a dot product by MPC (k openings) | ||
ctx.preproc = FakePreProcessedElements() | ||
xs = [ctx.preproc.get_bit(ctx) for _ in range(k)] | ||
ys = [ctx.preproc.get_bit(ctx) for _ in range(k)] | ||
logging.info(f"[{ctx.myid}] Running prog 1.") | ||
res = await dot_product(ctx, xs, ys) | ||
|
||
R = await res.open() | ||
XS = await ctx.ShareArray(xs).open() | ||
YS = await ctx.ShareArray(ys).open() | ||
assert R == sum([X * Y for X, Y in zip(XS, YS)]) | ||
logging.info(f"[{ctx.myid}] done") | ||
|
||
|
||
async def _run(peers, n, t, my_id): | ||
from honeybadgermpc.ipc import ProcessProgramRunner | ||
async with ProcessProgramRunner(peers, n, t, my_id, mpc_config) as runner: | ||
await runner.execute('0', prog) | ||
bytes_sent = runner.node_communicator.bytes_sent | ||
print(f'[{my_id}] Total bytes sent out: {bytes_sent}') | ||
|
||
|
||
if __name__ == "__main__": | ||
from honeybadgermpc.config import HbmpcConfig | ||
import sys | ||
import os | ||
if not HbmpcConfig.peers: | ||
print(f'WARNING: the $CONFIG_PATH environment variable wasn\'t set. Please run this file with `scripts/launch_tmuxlocal.sh apps/tutorial/hbmpc-tutorial-2.py conf/local/mpc`') | ||
sys.exit(1) | ||
|
||
asyncio.set_event_loop(asyncio.new_event_loop()) | ||
loop = asyncio.get_event_loop() | ||
loop.set_debug(True) | ||
try: | ||
if HbmpcConfig.my_id == 0: | ||
k = 100 # How many of each kind of preproc | ||
pp_elements = FakePreProcessedElements() | ||
pp_elements.generate_bits(k, HbmpcConfig.N, HbmpcConfig.t) | ||
pp_elements.generate_triples(k, HbmpcConfig.N, HbmpcConfig.t) | ||
preprocessing_done() | ||
else: | ||
loop.run_until_complete(wait_for_preprocessing()) | ||
|
||
loop.run_until_complete( | ||
_run(HbmpcConfig.peers, HbmpcConfig.N, HbmpcConfig.t, HbmpcConfig.my_id)) | ||
finally: | ||
loop.close() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters