owned this note
owned this note
Published
Linked with GitHub
# circom-MP-SPDZ
[ToC]
## Abstract
circom-MP-SPDZ allows parties to perform Multi-Party Computation (MPC) by writing Circom code using the MP-SPDZ framework. Circom code is converted to an arithmetic circuit and then gate by gate translated to the corresponding MP-SPDZ operators.
In the current structure, input files are not limited to MP-SPDZ and can be applied to other MPC frameworks. We can adapt `generate_mpspdz_circuit` and `generate_mpspdz_inputs_for_party` to support other MPC frameworks.
## Input files
### Computation
A computation is defined by two files: `circuit.circom` and `mpc_settings.json`.
- `circuit.circom` is an extended circom syntax, which supports more operators than the original circom. The list of supported operators can be found [here](https://github.com/namnc/circom-2-arithc/blob/e41f4bfa3b95010fe713a352b4df8e6af1e7c0ae/src/circuit.rs#L25-L37). For example, it could be
```circom
pragma circom 2.0.0;
template Main() {
signal input a;
signal input b;
signal input c <== 3;
signal output a_add_b <== a + b;
signal output a_mul_c <== a * c;
}
component main = Main();
```
- `mpc_settings.json` specifies which inputs a party should supply and which outputs a party can learn. It is an array of entries where the first entry is for party 0, the second entry is for party 1, etc. For example, it could be:
```json
[
{
"name": "alice",
"inputs": ["a"],
"outputs": ["a_add_b", "a_mul_c"]
},
{
"name": "bob",
"inputs": ["b"],
"outputs": ["a_add_b", "a_mul_c"]
}
]
```
This indicates that
- party 0
- name is `alice`
- should supply input `a`.
- should learn the value of outputs `a_add_b` and `a_mul_c` from the computation
- party 1
- name is `bob`
- should supply input `b`
- should learn the value of outputs `a_add_b` and `a_mul_c` from the computation
For a computation to be successfully carried out, parties must use the same `circuit.circom` and `mpc_settings.json` in order to generate the same MP-SPDZ circuit.
### Input values
- `inputs.json`: each party has an input file, containing all the values of the inputs they should supply according to `mpc_settings.json`.
For example, in `mpc_settings.json` party 0 is assigned to supply `a` and party 1 assigned to supply `b`.
`inputs.json` for party 0 should contain the value of `a`
```json
{
"a": 1
}
```
`inputs.json` for party 1 should contain the value of `b`
```json
{
"b": 2
}
```
## Workflow
Each party follows the following steps to perform a computation:
- [1. Generate MP-SPDZ circuit](#1-Generate-MP-SPDZ-circuit)
- [2. Generate MP-SPDZ inputs](#2-Generate-MP-SPDZ-inputs)
- [3. Run MPC with MP-SPDZ circuits and inputs](#3-Run-MPC-with-MP-SPDZ-circuits-and-inputs)
### 1. Generate MP-SPDZ circuit
![Flow to generate MP-SPDZ circuit from circom](https://hackmd.io/_uploads/rkVcjDnq0.png)
#### 1.1. Circom to bristol fashion circuit
[circom-2-arithc](https://github.com/namnc/circom-2-arithc) converts `circuit.circom` to `circuit.txt`, a bristol fashion circuit with gates sorted in topological order. With this order, all inputs to a gate are available when the gate is executed. We can safely execute gates in this order. For example, `circuit.txt` would look like this:
```
# {num_gates} {num_wires}
2 5
# {num_inputs} {'1' * num_inputs}
3 1 1 1
# {num_outputs} {'1' * num_outputs}
2 1 1
# Below are {num_gates} lines.
# Each line is "{num_inputs} {num_outputs} {input_wire_index_0} {input_wire_index_1} {output_wire} {gate_type}"
2 1 1 0 3 AAdd
2 1 1 2 4 AMul
```
Another file `circuit_info.json` is also generated. It tells us how to interpret the wires in the bristol fashion circuit - which wires the inputs and outputs correspond to, and which wires are constants and can be assigned with values directly. For example,
```json
{
"input_name_to_wire_index": {
"a": 1,
"b": 0
},
"constants": {
"0.c": {
"value": 3,
"wire_index": 2
}
},
"output_name_to_wire_index": {
"a_add_b": 3,
"a_mul_c": 4
}
}
```
This means that the wire with index 1 is the input `a`, the wire with index 0 is the input `b`, the wire with index 2 is the constant `c` with value `3`, the wire with index 3 is the output `a_add_b`, and the wire with index 4 is the output `a_mul_c`.
#### 1.2. Bristol fashion circuit to MP-SPDZ circuit
[`generate_mpspdz_circuit`](https://github.com/mhchia/MP-SPDZ/blob/19018f1cfbba8641f005cdc8fa07aa3e14434db0/arithc_executor.py#L59-L142) generates a MP-SPDZ circuit `Circuits/circuit.mpc` under the MP-SPDZ project root. It translates each gate to their corresponding MP-SPDZ operator. For example, the above bristol fashion circuit is converted to the following MP-SPDZ circuit:
```python
wires = [sint.get_input_from(1), sint.get_input_from(0), cint(3), None, None]
# wires[3] = a_add_b (2 1 1 0 3 AAdd) and is translated to + operator in MP-SPDZ
wires[3] = wires[1] + wires[0]
# wires[4] = a_mul_c (2 1 1 2 4 AMul) and is translated to * operator in MP-SPDZ
wires[4] = wires[1] * wires[2]
# Print outputs according to mpc_settings.json
print_ln_to(0, 'outputs[0]: a_add_b=%s', wires[3].reveal_to(0))
print_ln_to(0, 'outputs[1]: a_mul_c=%s', wires[4].reveal_to(0))
print_ln_to(1, 'outputs[0]: a_add_b=%s', wires[3].reveal_to(1))
print_ln_to(1, 'outputs[1]: a_mul_c=%s', wires[4].reveal_to(1))
```
In the code above,
- `wires` is a list with 5 elements. According to `circuit_info.json` and `mpc_settings.json`,
- `wires[0]` is `b` and it should be supplied by party 1
- `wires[1]` is `a` and it should be supplied by party 0
- `wires[2]` is `c` and it's a constant with value `3`
- `wires[3]` is `a_add_b` and it is `None` because it should be computed in the following gates
- `wires[4]` is `a_mul_c` and it is `None` because it should be computed in the following gates
- `wires[3] = wires[1] + wires[0]` is the translation of the `AAdd` gate to the `+` operator in MP-SPDZ. `wires[4] = wires[1] * wires[2]` is the translation of the `AMul` gate to the `*` operator in MP-SPDZ.
- `print_ln_to(0, 'outputs[0]: a_add_b=%s', wires[3].reveal_to(0))` prints the value of `a_add_b` to party 0, as defined in `mpc_settings.json`, etc.
### 2. Generate MP-SPDZ inputs
Each party translates their `inputs.json` to the format MP-SPDZ requires by running [`generate_mpspdz_inputs_for_party`](https://github.com/mhchia/MP-SPDZ/blob/19018f1cfbba8641f005cdc8fa07aa3e14434db0/arithc_executor.py#L205-L246) with `circuit_info.json` and `mpc_settings.json` as inputs. The generated input file is stored in `Player-Data/Input-P{party}-0` under the MP-SPDZ project root.
![Flow to generate MP-SPDZ inputs from inputs.json](https://hackmd.io/_uploads/B1ILZMbQA.png)
For example, the above `inputs.json` for party 0 is converted to `Player-Data/Input-P0-0`
```bash
1
```
and the above `inputs.json` for party 1 is converted to `Player-Data/Input-P1-0`
```bash
2
```
### 3. Run MPC with MP-SPDZ circuits and inputs
With the generated circuit and inputs for MP-SPDZ, parties can run MP-SPDZ and get the computation result.
![3_run_mpc_and_get_result.drawio](https://hackmd.io/_uploads/Hkou73xmR.png)
For testing, you can run all parties in your local machine. Or, each party can run on different machines and interact with each other through the network.
#### 1. Run all parties in the same machine
First, go to the project root of MP-SPDZ.
```bash
cd /path/to/MP-SPDZ
```
Run all parties in your local machine with the script
```bash
./Scripts/compile-run.py -E {mpc_protocol} {circuit_name}
```
where `{mpc_protocol}` is a protocol supported by MP-SPDZ you want to use (e.g., `semi`, `mascot`, [etc](https://mp-spdz.readthedocs.io/en/latest/readme.html#protocols)). `{circuit_name}` is the name of the circuit you want to run. For example, to run `circuit.mpc` with `semi` protocol:
```bash
./Scripts/compile-run.py -E semi circuit
```
#### 2: Run parties in different machines
For each party, run the following commands in the MP-SPDZ project root:
**Step 1:** Compile the VM for `semi` protocol
```bash
make -j8 semi-party.x
```
**Step 2:** Compile the circuit `circuit.mpc` to bytecode
```bash
./compile.py circuit
```
**Step 3:** Create a file with the IP addresses of all parties. For example, a file `hosts` with the following content:
```
18.183.238.119:3000
43.207.105.60:4000
```
it means party 0 will listen to `18.183.238.119:3000` and party 1 to `43.207.105.60:4000`. Note that all parties must use the same file.
**Step 4:** Run the party with the VM and bytecode. For example, to run party 0 with `circuit.mpc`:
```bash
./semi-party.x -N 2 -p 0 -OF . circuit -ip hosts
```
and party 1:
```bash
./semi-party.x -N 2 -p 1 -OF . circuit -ip hosts
```
where
- `-N 2`: there are two parties
- `-p 1`: this is party 0
- `-OF .`: outputs are printed to stdout
Outputs are printed to the console. For example, both party 0 and 1 will see the outputs`a_add_b` and `a_mul_c`:
```
outputs[0]: a_add_b=3
outputs[1]: a_mul_c=3
```
due to the settings we have in `mpc_settings.json`.
<!--
Old content of circom-2-arithc
![Flow to generate MP-SPDZ circuit from circom](https://hackmd.io/_uploads/ByBZQhe7R.png)
#### 1.1. Circom to arithmetic circuit
[circom-2-arithc](https://github.com/namnc/circom-2-arithc) converts `circuit.circom` to `circuit.json`, which is an arithmetic representation of the circuit and is easier to work with than circom. For example, the above circom code is converted to:
```json
{
...,
"gates": [
{
"id": 0,
"gate_type": "AAdd",
"lh_input": 339827882353457231733281877774418513,
"rh_input": 261273425065800872138523378011643318856,
"output": 230661140909065778448104897333394910284
},
{
"id": 1,
"gate_type": "AMul",
"lh_input": 339827882353457231733281877774418513,
"rh_input": 224358152556191904480812644829082501076,
"output": 176538014183051407881132466597081583306
}
]
}
```
#### 1.2. Arithmetic circuit to bristol fashion circuit
Inputs to a gate can be outputs from other gates. Executing gates in the order listed in `circuit.json` might lead to errors if a gate's inputs have not yet been computed. The script [json_abristol.py](https://github.com/mhchia/MP-SPDZ/blob/19018f1cfbba8641f005cdc8fa07aa3e14434db0/json_abristol.py) converts `circuit.json` to `circuit.txt`, a bristol fashion circuit with gates sorted in topological order. With this order, all inputs to a gate are available when the gate is executed. We can safely execute gates in this order. For example, `circuit.txt` would look like this:
```
# {num_gates} {num_wires}
2 5
# {num_inputs} {'1' * num_inputs}
3 1 1 1
# {num_outputs} {'1' * num_outputs}
2 1 1
# Below are {num_gates} lines.
# Each line is "{num_inputs} {num_outputs} {input_wire_index_0} {input_wire_index_1} {output_wire} {gate_type}"
2 1 1 0 3 AAdd
2 1 1 2 4 AMul
```
Another file `circuit_info.json` is also generated. It tells us how to interpret the wires in the bristol fashion circuit - which wires the inputs and outputs correspond to, and which wires are constants and can be assigned with values directly. For example, we have the following `circuit_info.json` generated from the `circuit.json` above:
```json
{
"input_name_to_wire_index": {
"a": 1,
"b": 0
},
"constants": {
"0.c": {
"value": 3,
"wire_index": 2
}
},
"output_name_to_wire_index": {
"a_add_b": 3,
"a_mul_c": 4
}
}
```
This means that the wire with index 1 is the input `a`, the wire with index 0 is the input `b`, the wire with index 2 is the constant `c` with value `3`, the wire with index 3 is the output `a_add_b`, and the wire with index 4 is the output `a_mul_c`.
-->