Skip to content

Commit

Permalink
mpc: phase-specific config, paths in config file, consistent option n…
Browse files Browse the repository at this point in the history
…aming
  • Loading branch information
dtebbs committed Nov 11, 2019
1 parent 854c885 commit d630e86
Show file tree
Hide file tree
Showing 17 changed files with 257 additions and 119 deletions.
20 changes: 12 additions & 8 deletions mpc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,17 +153,21 @@ specifying properties of the MPC:
```json
// server_config.json
{
"contributors_file": "contributors.json",
"start_time": "2019-10-02 17:00:00", # Time (server-local)
"contribution_interval": "86400", # 24 hours (in seconds)
"tls_key": "key.pem",
"tls_certificate": "cert.pem",
"port": 8001
"server": {
"contributors_file": "contributors.json",
"start_time": "2019-10-02 17:00:00", # Time (server-local)
"contribution_interval": "86400", # 24 hours (in seconds)
"tls_key": "key.pem",
"tls_certificate": "cert.pem",
"port": 8001
}
}
```

See the [test configuration](../testdata/mpc_server_config.json) for a full
example configuration file.
The servers for each phase (phase1 and phase2) also support options specific to
that phase, which can be set in the config file. See the test configurations for
[phase1](../testdata/mpc_phase1_server_config.json) and
[phase2](../testdata/mpc_phase2_server_config.json) for full examples.

The `contributors_file` field must point to a file specifying the ordered set
of contributors in the MPC. This file takes the form:
Expand Down
3 changes: 3 additions & 0 deletions mpc/commands/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env python3

DEFAULT_CONFIG_FILE = "server_config.json"
28 changes: 22 additions & 6 deletions mpc/commands/phase1_server
Original file line number Diff line number Diff line change
@@ -1,26 +1,42 @@
#!/usr/bin/env python3

"""
Script to launch a phase1 coordinator server.
"""

from commands import DEFAULT_CONFIG_FILE
from click import command, option
from typing import Optional


@command()
@option("--path", default=".", help="Server working path")
@option("--config", "-n", default=DEFAULT_CONFIG_FILE, help="Configuration file")
@option("--pot-path", default=None, help="Path to poweroftau repository")
@option("--num-powers", "-n", default=None, type=int, help="Number of powers")
def phase1_server(
path: str,
config: str,
pot_path: Optional[str],
num_powers: Optional[int]) -> None:
"""
Phase1 MPC coordinator process. Reads the given configuration file and
listens for contributor connections. Command line parameters override
any values set in the config file.
"""

from coordinator.phase1_contribution_handler import \
Phase1ContributionHandler
Phase1ServerConfig, Phase1ContributionHandler
from coordinator.server import Server
import time

# Load config file, overriding with any command line params.
with open(config, "r") as config_f:
phase1_config = Phase1ServerConfig.from_json(config_f.read())
if pot_path:
phase1_config.powersoftau_path = pot_path
if num_powers:
phase1_config.num_powers = num_powers

try:
server = Server(Phase1ContributionHandler(pot_path, num_powers), ".")
handler = Phase1ContributionHandler(phase1_config)
server = Server(handler, phase1_config.server_configuration, path)
while True:
time.sleep(1)
except KeyboardInterrupt:
Expand Down
6 changes: 3 additions & 3 deletions mpc/commands/phase2_contribute
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ RESPONSE_DIGEST_FILE = "response.bin.digest"
@option("--challenge", default=CHALLENGE_FILE)
@option("--response", default=RESPONSE_FILE)
@option("--response-digest", default=RESPONSE_DIGEST_FILE)
@option("--mpc-executable", default=None, help="path to mpc executable")
@option("--mpc-tool", default=None, help="path to mpc tool")
@option("--skip-user-input", is_flag=True, default=False)
@option("--server-certificate", default=None, help="server certificate")
@option("--insecure", is_flag=True, help="allow missing certificate chain")
Expand All @@ -34,7 +34,7 @@ def phase2_contribute(
challenge: str,
response: str,
response_digest: str,
mpc_executable: Optional[str],
mpc_tool: Optional[str],
skip_user_input: bool,
server_certificate: Optional[str],
insecure: bool) -> None:
Expand All @@ -44,7 +44,7 @@ def phase2_contribute(

# Callback to compute contribution
def _contribute() -> str:
mpc = MPCCommand(mpc_executable)
mpc = MPCCommand(mpc_tool)
contribution_success = mpc.phase2_contribute(
challenge,
response,
Expand Down
12 changes: 6 additions & 6 deletions mpc/commands/phase2_prepare
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,23 @@ LINEAR_COMBINATION_FILE = "linear_combination.bin"
@command()
@argument("pot-file")
@argument("degree", type=int)
@option("--pot-executable", default=None, help="path to pot-process executable")
@option("--mpc-executable", default=None, help="path to mpc executable")
@option("--pot-tool", default=None, help="path to pot-process tool")
@option("--mpc-tool", default=None, help="path to mpc tool")
def phase2_prepare(
pot_file: str,
degree: int,
pot_executable: Optional[str],
mpc_executable: Optional[str]) -> None:
pot_tool: Optional[str],
mpc_tool: Optional[str]) -> None:
"""
Process powersoftau (Phase1) output to create input data required for
Phase2.
"""

pot_process = PowersOfTauProcessCommand(pot_executable)
pot_process = PowersOfTauProcessCommand(pot_tool)
if not pot_process.compute_lagrange(pot_file, degree, LAGRANGE_FILE):
raise Exception("Lagrange computation failed")

mpc = MPCCommand(mpc_executable)
mpc = MPCCommand(mpc_tool)
if not mpc.linear_combination(
pot_file, LAGRANGE_FILE, LINEAR_COMBINATION_FILE):
raise Exception("linear combination failed")
Expand Down
24 changes: 20 additions & 4 deletions mpc/commands/phase2_server
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,37 @@
Script to launch a phase2 coordinator server.
"""

from commands import DEFAULT_CONFIG_FILE
from click import command, option
from typing import Optional


@command()
@option("--path", default=".", help="Server working path")
@option("--bin-path", default=None, help="Path to mpc binaries")
def phase2_server(path: str, bin_path: Optional[str]) -> None:
@option("--config", default=DEFAULT_CONFIG_FILE, help="Configuration file")
@option("--mpc-tool", default=None, help="Path to mpc executable")
def phase2_server(
path: str,
config: str,
mpc_tool: Optional[str]) -> None:
"""
Phase2 MPC coordinator process. Reads the given configuration file and
listens for contributor connections. Command line parameters override
any values set in the config file.
"""
from coordinator.phase2_contribution_handler import \
Phase2ContributionHandler
Phase2ServerConfig, Phase2ContributionHandler
from coordinator.server import Server
import time

with open(config, "r") as config_f:
phase2_config = Phase2ServerConfig.from_json(config_f.read())
if mpc_tool:
phase2_config.mpc_tool = mpc_tool

try:
server = Server(Phase2ContributionHandler(), path)
handler = Phase2ContributionHandler(phase2_config)
server = Server(handler, phase2_config.server_configuration, path)
while True:
time.sleep(1)
except KeyboardInterrupt:
Expand Down
10 changes: 5 additions & 5 deletions mpc/coordinator/mpc_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ class MPCCommand(object):
Wrapper around the 'mpc' utility.
"""

def __init__(self, mpc_exe: Optional[str] = ""):
self.mpc_exe = mpc_exe or _default_mpc_bin()
assert exists(self.mpc_exe)
def __init__(self, mpc_tool: Optional[str] = ""):
self.mpc_tool = mpc_tool or _default_mpc_tool()
assert exists(self.mpc_tool)

def linear_combination(
self,
Expand Down Expand Up @@ -65,12 +65,12 @@ def phase2_contribute(
return self._exec(args)

def _exec(self, args: List[str]) -> bool:
cmd = [self.mpc_exe] + args
cmd = [self.mpc_tool] + args
print(f"CMD: {' '.join(cmd)}")
comp = subprocess.run(cmd)
return 0 == comp.returncode


def _default_mpc_bin() -> str:
def _default_mpc_tool() -> str:
return os.path.join(
os.path.dirname(__file__), "..", "..", "build", "src", "mpc", "mpc")
47 changes: 40 additions & 7 deletions mpc/coordinator/phase1_contribution_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"""

from __future__ import annotations
from .server_configuration import JsonDict
from .server_configuration import Configuration, JsonDict
from .icontributionhandler import IContributionHandler
from .powersoftau_command import \
PowersOfTauCommand, CHALLENGE_FILE, NEW_CHALLENGE_FILE, RESPONSE_FILE
Expand All @@ -21,6 +21,42 @@
FINAL_TRANSCRIPT = "final_transcript.bin"


class Phase1ServerConfig(object):
"""
Configuration for Phase1 server
"""
def __init__(
self,
server_configuration: Configuration,
pot_path: Optional[str],
num_powers: Optional[int]):
self.server_configuration = server_configuration
self.powersoftau_path = pot_path
self.num_powers = num_powers

def to_json(self) -> str:
return json.dumps(self._to_json_dict(), indent=4)

@staticmethod
def from_json(phase1_config_json: str) -> Phase1ServerConfig:
return Phase1ServerConfig._from_json_dict(json.loads(phase1_config_json))

def _to_json_dict(self) -> JsonDict:
return {
"server": self.server_configuration._to_json_dict(),
"powersoftau_path": self.powersoftau_path,
"num_powers": self.num_powers,
}

@staticmethod
def _from_json_dict(json_dict: JsonDict) -> Phase1ServerConfig:
return Phase1ServerConfig(
server_configuration=Configuration._from_json_dict(
cast(JsonDict, json_dict["server"])),
pot_path=cast(Optional[str], json_dict.get("powersoftau_path", None)),
num_powers=cast(Optional[int], json_dict.get("num_powers", None)))


class _Phase1State(object):
"""
Internal persisted state model for this handler.
Expand Down Expand Up @@ -61,12 +97,9 @@ class Phase1ContributionHandler(IContributionHandler):
contributions that have been made.
"""

def __init__(
self,
powersoftau_path: Optional[str] = None,
num_powers: Optional[int] = None) -> None:

self.powersoftau = PowersOfTauCommand(powersoftau_path, num_powers)
def __init__(self, phase1_config: Phase1ServerConfig):
self.powersoftau = PowersOfTauCommand(
phase1_config.powersoftau_path, phase1_config.num_powers)

if exists(PHASE1_STATE_FILE):
with open(PHASE1_STATE_FILE, "r") as state_f:
Expand Down
51 changes: 43 additions & 8 deletions mpc/coordinator/phase2_contribution_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,61 @@
Implementation of Phase2ContributionHandler
"""

from coordinator.icontributionhandler import IContributionHandler
from coordinator.mpc_command import MPCCommand
from coordinator.phase1_contribution_handler import \
from __future__ import annotations
from .server_configuration import Configuration, JsonDict
from .icontributionhandler import IContributionHandler
from .mpc_command import MPCCommand
from .phase1_contribution_handler import \
NEW_CHALLENGE_FILE, TRANSCRIPT_FILE, FINAL_OUTPUT, FINAL_TRANSCRIPT

from os.path import exists, join
from os.path import exists
from os import rename
from typing import Optional
from typing import Optional, cast
import json

CHALLENGE_0_FILE = "challenge_0.bin"
NEXT_CHALLENGE_FILE = "next_challenge.bin"


class Phase2ServerConfig(object):
"""
Configuration object for phase2 server.
"""

def __init__(
self,
server_configuration: Configuration,
mpc_tool: Optional[str]):
self.server_configuration = server_configuration
self.mpc_tool = mpc_tool

def to_json(self) -> str:
return json.dumps(self._to_json_dict(), indent=4)

@staticmethod
def from_json(phase2_config_json: str) -> Phase2ServerConfig:
return Phase2ServerConfig._from_json_dict(json.loads(phase2_config_json))

def _to_json_dict(self) -> JsonDict:
return {
"server": self.server_configuration._to_json_dict(),
"mpc_tool": self.mpc_tool,
}

@staticmethod
def _from_json_dict(json_dict: JsonDict) -> Phase2ServerConfig:
return Phase2ServerConfig(
server_configuration=Configuration._from_json_dict(
cast(JsonDict, json_dict["server"])),
mpc_tool=cast(Optional[str], json_dict.get("mpc_tool", None)))


class Phase2ContributionHandler(IContributionHandler):
"""
Handler processing phase2 challenges and contributions.
"""

def __init__(self, bin_path: Optional[str] = None) -> None:
def __init__(self, phase2_config: Phase2ServerConfig):
# Sanity check
if not exists(CHALLENGE_0_FILE):
raise Exception(f"no {CHALLENGE_0_FILE} found in server dir")
Expand All @@ -32,8 +68,7 @@ def __init__(self, bin_path: Optional[str] = None) -> None:
if exists(TRANSCRIPT_FILE):
raise Exception(f"unexpected {TRANSCRIPT_FILE} in server dir")

mpc_exe = join(bin_path, "mpc") if bin_path else None
self.mpc = MPCCommand(mpc_exe)
self.mpc = MPCCommand(phase2_config.mpc_tool)

def get_current_challenge_file(self, contributor_idx: int) -> str:
# If there is no NEXT_CHALLENGE_FILE, use CHALLENGE_0_FILE. (Note,
Expand Down
8 changes: 4 additions & 4 deletions mpc/coordinator/powersoftau_process_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ class PowersOfTauProcessCommand(object):
Wrapper around the pot-process command.
"""

def __init__(self, pot_process_executable: Optional[str] = None):
self.bin_path = pot_process_executable or _default_executable()
def __init__(self, pot_process_tool: Optional[str] = None):
self.pot_process_tool = pot_process_tool or _default_tool()

def compute_lagrange(
self,
Expand All @@ -23,11 +23,11 @@ def compute_lagrange(

def _exec(self, args: List[str]) -> bool:
import subprocess
args = [self.bin_path] + args
args = [self.pot_process_tool] + args
print(f"CMD: {' '.join(args)}")
return 0 == subprocess.run(args=args).returncode


def _default_executable() -> str:
def _default_tool() -> str:
from os.path import join, dirname
return join(dirname(__file__), "..", "..", "build", "src", "pot-process")
Loading

0 comments on commit d630e86

Please sign in to comment.