# 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`. -->