From 0ca744abb22a9309a9ab4317b82c77b86d2543cd Mon Sep 17 00:00:00 2001 From: Vlad Leustean Date: Tue, 11 Jun 2024 22:48:03 +0100 Subject: [PATCH 01/12] Update config.py Fixed the pathing for the .nada.bin file --- examples_and_tutorials/millionaires_problem_example/config.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples_and_tutorials/millionaires_problem_example/config.py b/examples_and_tutorials/millionaires_problem_example/config.py index 24fb49d8..02503147 100644 --- a/examples_and_tutorials/millionaires_problem_example/config.py +++ b/examples_and_tutorials/millionaires_problem_example/config.py @@ -2,6 +2,7 @@ import py_nillion_client as nillion from dotenv import load_dotenv load_dotenv() +CONFIG_PROGRAM_NAME="millionaires" # Alice CONFIG_PARTY_1={ @@ -29,4 +30,4 @@ "secret_name": "charlie_salary", "secret_value": 12000, }, -] \ No newline at end of file +] From ee7ba335661fb863c204146a8034cdc7dbbfd887 Mon Sep 17 00:00:00 2001 From: Vlad Leustean Date: Tue, 11 Jun 2024 22:49:45 +0100 Subject: [PATCH 02/12] Update 01_store_secret_party1.py Updated the .nada.bin path to use the Config file path. Approach similar to one used in the following project: /nillion-python-starter/examples_and_tutorials/core_concept_multi_party_compute --- .../millionaires_problem_example/01_store_secret_party1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples_and_tutorials/millionaires_problem_example/01_store_secret_party1.py b/examples_and_tutorials/millionaires_problem_example/01_store_secret_party1.py index f97083ba..337857b6 100644 --- a/examples_and_tutorials/millionaires_problem_example/01_store_secret_party1.py +++ b/examples_and_tutorials/millionaires_problem_example/01_store_secret_party1.py @@ -24,7 +24,7 @@ async def main(): millionaires_program_name = "millionaires" # Note: check out the code for the full millionaires program in the programs folder - program_mir_path = "millionaires.nada.bin" + program_mir_path=f"../../programs-compiled/{CONFIG_PROGRAM_NAME}.nada.bin" # Store millionaires program in the network print(f"Storing program in the network: {millionaires_program_name}") From c96dba50dbf92897a4d9202572e93a81f1bac62c Mon Sep 17 00:00:00 2001 From: Vlad Leustean Date: Tue, 11 Jun 2024 22:58:09 +0100 Subject: [PATCH 03/12] Update config.py missed paranthesis --- examples_and_tutorials/millionaires_problem_example/config.py | 1 - 1 file changed, 1 deletion(-) diff --git a/examples_and_tutorials/millionaires_problem_example/config.py b/examples_and_tutorials/millionaires_problem_example/config.py index 02503147..f6655cbc 100644 --- a/examples_and_tutorials/millionaires_problem_example/config.py +++ b/examples_and_tutorials/millionaires_problem_example/config.py @@ -30,4 +30,3 @@ "secret_name": "charlie_salary", "secret_value": 12000, }, -] From 6c79f7a8e3a30c20cd71e41fb68ddcab0ece0118 Mon Sep 17 00:00:00 2001 From: Vlad Leustean Date: Tue, 11 Jun 2024 23:02:57 +0100 Subject: [PATCH 04/12] Update 01_store_secret_party1.py Forgot to add CONFIG_PROGRAM_NAME variable --- .../millionaires_problem_example/01_store_secret_party1.py | 1 + 1 file changed, 1 insertion(+) diff --git a/examples_and_tutorials/millionaires_problem_example/01_store_secret_party1.py b/examples_and_tutorials/millionaires_problem_example/01_store_secret_party1.py index 337857b6..3f7882d9 100644 --- a/examples_and_tutorials/millionaires_problem_example/01_store_secret_party1.py +++ b/examples_and_tutorials/millionaires_problem_example/01_store_secret_party1.py @@ -5,6 +5,7 @@ from dotenv import load_dotenv from config import ( + CONFIG_PROGRAM_NAME, CONFIG_PARTY_1 ) From 2456eb423e382835555b9909697d3858bea0e127 Mon Sep 17 00:00:00 2001 From: Vlad Leustean Date: Wed, 12 Jun 2024 09:33:54 +0100 Subject: [PATCH 05/12] Update config.py Added missing bracket --- examples_and_tutorials/millionaires_problem_example/config.py | 1 + 1 file changed, 1 insertion(+) diff --git a/examples_and_tutorials/millionaires_problem_example/config.py b/examples_and_tutorials/millionaires_problem_example/config.py index f6655cbc..02503147 100644 --- a/examples_and_tutorials/millionaires_problem_example/config.py +++ b/examples_and_tutorials/millionaires_problem_example/config.py @@ -30,3 +30,4 @@ "secret_name": "charlie_salary", "secret_value": 12000, }, +] From 1ec424e7d3d4ea5cea927a339971088735b50263 Mon Sep 17 00:00:00 2001 From: davetbutler Date: Mon, 17 Jun 2024 17:38:40 +0100 Subject: [PATCH 06/12] add bootstrap script for colab paths --- bootstrap-local-environment-colab.sh | 152 +++++++++++++++++++++++++++ bootstrap-local-environment.sh | 4 +- 2 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 bootstrap-local-environment-colab.sh diff --git a/bootstrap-local-environment-colab.sh b/bootstrap-local-environment-colab.sh new file mode 100644 index 00000000..35497ebe --- /dev/null +++ b/bootstrap-local-environment-colab.sh @@ -0,0 +1,152 @@ +#!/usr/bin/env bash + +# set number of node and user keys to be created +num_node_keys=5 +num_user_keys=5 + +# set env file to update +ENV_TO_UPDATE=".env" + +NILLION_DEVNET="/root/.nilup/sdks/latest/nillion-devnet" +NILLION_CLI="/root/.nilup/sdks/latest/nillion" +NILLION_CLI_COMMAND_USER_KEYGEN="user-key-gen" +NILLION_CLI_COMMAND_NODE_KEYGEN="node-key-gen" + +# kill any other nillion-devnet processes +pkill -9 -f $NILLION_DEVNET + +for var in NILLION_DEVNET NILLION_CLI; do + printf "â„šī¸ found bin %-18s -> [${!var:?Failed to discover $var}]\n" "$var" +done + +OUTFILE=$(mktemp); +PIDFILE=$(mktemp); + +"$NILLION_DEVNET" >"$OUTFILE" & echo $! >"$PIDFILE"; +echo "--------------------" +echo "Updating your ${ENV_TO_UPDATE} files with nillion-devnet environment info... This may take a minute." +echo "--------------------" +time_limit=160 +while true; do + # Use 'wait' to check if the log file contains the string + if grep "cluster is running, bootnode is at" "$OUTFILE"; then + break + fi + + # If the time limit has been reached, print an error message and exit + if [[ $SECONDS -ge $time_limit ]]; then + echo "Timeout reached while waiting for cluster to be ready in '$OUTFILE'" >&2 + exit 1 + fi + sleep 5 +done + +echo "â„šī¸ Cluster has been STARTED (see $OUTFILE)" +cat "$OUTFILE" + +# grep cluster info from nillion-devnet +CLUSTER_ID=$(grep "cluster id is" "$OUTFILE" | awk '{print $4}'); +WEBSOCKET=$(grep "websocket:" "$OUTFILE" | awk '{print $2}'); +BOOT_MULTIADDR=$(grep "cluster is running, bootnode is at" "$OUTFILE" | awk '{print $7}'); +PAYMENTS_CONFIG_FILE=$(grep "payments configuration written to" "$OUTFILE" | awk '{print $5}'); +WALLET_KEYS_FILE=$(grep "wallet keys written to" "$OUTFILE" | awk '{print $5}'); +PAYMENTS_RPC=$(grep "blockchain_rpc_endpoint:" "$PAYMENTS_CONFIG_FILE" | awk '{print $2}'); +PAYMENTS_CHAIN=$(grep "chain_id:" "$PAYMENTS_CONFIG_FILE" | awk '{print $2}'); +PAYMENTS_SC_ADDR=$(grep "payments_sc_address:" "$PAYMENTS_CONFIG_FILE" | awk '{print $2}'); +PAYMENTS_BF_ADDR=$(grep "blinding_factors_manager_sc_address:" "$PAYMENTS_CONFIG_FILE" | awk '{print $2}'); +WALLET_PRIVATE_KEY=$(tail -n1 "$WALLET_KEYS_FILE") + +# update or add an environment variable to one or more files +update_env() { + local key=$1 + local value=$2 + # Skip the first two arguments to get the file paths + local files=("${@:3}") + + for file in "${files[@]}"; do + if [ -f "$file" ]; then # Check if file exists + # Check if the key exists in the file and remove it + if grep -q "^$key=" "$file"; then + # Key exists, remove it + grep -v "^$key=" "$file" > temp.txt && mv temp.txt "$file" + fi + + # Append the new key-value pair to the file + echo "$key=$value" >> "$file" + else + echo "File $file not found. Creating $file" + touch $file + echo "$key=$value" >> "$file" + fi + done +} + +# log file contents of key files to add to .env +log_file_contents() { + local file_path=$1 # Direct path to the target file + + # Check if the file exists at the path + if [[ ! -f "$file_path" ]]; then + echo "File $file_path does not exist." + return 1 # Exit the function with an error status if the file does not exist + fi + + # If the file exists, cat its contents + cat "$file_path" +} + +# Generate node keys and add to .env - ex: NILLION_NODEKEY_PATH_PARTY_1 +for ((i=1; i<=$num_node_keys; i++)); do + nodekey_file=$(mktemp) + "$NILLION_CLI" "$NILLION_CLI_COMMAND_NODE_KEYGEN" "$nodekey_file" + NODEKEY_FILES+=("$nodekey_file") + update_env "NILLION_NODEKEY_PATH_PARTY_$i" "$nodekey_file" $ENV_TO_UPDATE + update_env "NILLION_NODEKEY_TEXT_PARTY_$i" "$(log_file_contents $nodekey_file)" $ENV_TO_UPDATE +done + +# Generate user keys and add to .env - ex: NILLION_USERKEY_PATH_PARTY_1 +for ((i=1; i<=$num_user_keys; i++)); do + userkey_file=$(mktemp) + "$NILLION_CLI" "$NILLION_CLI_COMMAND_USER_KEYGEN" "$userkey_file" + USERKEY_FILES+=("$userkey_file") + update_env "NILLION_USERKEY_PATH_PARTY_$i" "$userkey_file" $ENV_TO_UPDATE + update_env "NILLION_USERKEY_TEXT_PARTY_$i" "$(log_file_contents $userkey_file)" $ENV_TO_UPDATE +done + +echo "🔑 Node key and user keys have been generated and added to .env" + +# Add environment variables to .env +update_env "NILLION_WEBSOCKETS" "$WEBSOCKET" $ENV_TO_UPDATE +update_env "NILLION_CLUSTER_ID" "$CLUSTER_ID" $ENV_TO_UPDATE +update_env "NILLION_BLOCKCHAIN_RPC_ENDPOINT" "$PAYMENTS_RPC" $ENV_TO_UPDATE +update_env "NILLION_BLINDING_FACTORS_MANAGER_SC_ADDRESS" "$PAYMENTS_BF_ADDR" $ENV_TO_UPDATE +update_env "NILLION_PAYMENTS_SC_ADDRESS" "$PAYMENTS_SC_ADDR" $ENV_TO_UPDATE +update_env "NILLION_CHAIN_ID" "$PAYMENTS_CHAIN" $ENV_TO_UPDATE +update_env "NILLION_WALLET_PRIVATE_KEY" "$WALLET_PRIVATE_KEY" $ENV_TO_UPDATE +update_env "NILLION_BOOTNODE_MULTIADDRESS" "$BOOT_MULTIADDR" $ENV_TO_UPDATE + +echo "Running at process pid: $(pgrep -f $NILLION_DEVNET)" + +echo "-------------------------------------------------------" +echo " 7MM 7MM " +echo " MM MM " +echo " db MM MM db " +echo " MM MM " +echo ".7MMpMMMb. 7MM MM MM 7MM ,pW-Wq. 7MMpMMMb. " +echo " MM MM MM MM MM MM 6W' Wb MM MM " +echo " MM MM MM MM MM MM 8M M8 MM MM " +echo " MM MM MM MM MM MM YA. ,A9 MM MM " +echo ".JMML JMML..JMML..JMML..JMML..JMML. Ybmd9 .JMML JMML." +echo "-------------------------------------------------------" +echo "-------------------------------------------------------" +echo "-----------đŸĻ† CONNECTED TO NILLION-DEVNET đŸĻ†-----------" +echo "-------------------------------------------------------" + +echo "â„šī¸ Your $ENV_TO_UPDATE file configurations were updated with nillion-devnet connection variables: websocket, cluster id, keys, blockchain info" +echo "đŸ’ģ The Nillion devnet is still running behind the scenes; to spin down the Nillion devnet at any time, run 'killall nillion-devnet'" + +echo "--------------------" +echo "đŸ’ģ Your Nillion local cluster is still running - process pid: $(pgrep -f $NILLION_DEVNET)" +echo "â„šī¸ Updated your .env file configuration variables: bootnode, cluster id, keys, blockchain info" + +exit 0 \ No newline at end of file diff --git a/bootstrap-local-environment.sh b/bootstrap-local-environment.sh index 2ad6f7f1..35497ebe 100755 --- a/bootstrap-local-environment.sh +++ b/bootstrap-local-environment.sh @@ -7,8 +7,8 @@ num_user_keys=5 # set env file to update ENV_TO_UPDATE=".env" -NILLION_DEVNET="nillion-devnet" -NILLION_CLI="nillion" +NILLION_DEVNET="/root/.nilup/sdks/latest/nillion-devnet" +NILLION_CLI="/root/.nilup/sdks/latest/nillion" NILLION_CLI_COMMAND_USER_KEYGEN="user-key-gen" NILLION_CLI_COMMAND_NODE_KEYGEN="node-key-gen" From c49ce35bfdec61e4aeb3a58d17356f1da1567f59 Mon Sep 17 00:00:00 2001 From: davetbutler Date: Mon, 17 Jun 2024 20:31:26 +0100 Subject: [PATCH 07/12] update compile script --- compile_programs_colab.sh | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 compile_programs_colab.sh diff --git a/compile_programs_colab.sh b/compile_programs_colab.sh new file mode 100644 index 00000000..2c4bfe22 --- /dev/null +++ b/compile_programs_colab.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +# This script compiles all $PROGRAMS_FOLDER programs to mir +PROGRAMS_FOLDER="programs" +COMPILED_PROGRAMS_FOLDER="programs-compiled" + +SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}" 2>/dev/null)" && pwd -P)" +TARGET_PATH="${SCRIPT_PATH}/${COMPILED_PROGRAMS_FOLDER}" +PROGRAMS_PATH="${SCRIPT_PATH}/${PROGRAMS_FOLDER}" + +PYNADAC="/root/.nilup/sdks/latest/pynadac" + +cd "${PROGRAMS_PATH}" || exit 1 + +for file in *.py ; do + echo "Compiling ${file}" + "$PYNADAC" --target-dir "${TARGET_PATH}" \ + --generate-mir-json \ + "${file}" +done + +echo "------------------------" +echo "Compiled programs: all files in the programs directory were compiled to mir: [$TARGET_PATH]" + +echo "Now try running an example:" + +echo "----------single party compute --------------" + +echo "Code for single party compute lives in the examples_and_tutorials/core_concept_client_single_party_compute folder" +echo "📋 to run single party compute - addition_simple program: 'cd examples_and_tutorials/core_concept_client_single_party_compute && python3 addition_simple.py'" + +echo "----------multi party compute --------------" + +echo "Code for multi party compute lives in the examples_and_tutorials/core_concept_multi_party_compute folder" +echo "📋 to run multi party compute in 3 steps - addition_simple_multi_party_3: 'cd examples_and_tutorials/core_concept_multi_party_compute && python3 01_store_secret_party1.py'" \ No newline at end of file From 9fb727bf4109811c695c8d8f0c90dcb66915c27c Mon Sep 17 00:00:00 2001 From: davetbutler Date: Tue, 18 Jun 2024 07:59:44 +0100 Subject: [PATCH 08/12] add my first program file --- programs/my_first_program.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 programs/my_first_program.py diff --git a/programs/my_first_program.py b/programs/my_first_program.py new file mode 100644 index 00000000..e930708d --- /dev/null +++ b/programs/my_first_program.py @@ -0,0 +1,12 @@ +from nada_dsl import * + + +def nada_main(): + party1 = Party(name="Party1") + my_int1 = SecretInteger(Input(name="my_int1", party=party1)) + my_int2 = SecretInteger(Input(name="my_int2", party=party1)) + + # write the computation for your program here - use my_int1 and my_int2 as inputs + # make sure you change the output below to be your new output + + return [Output(my_int1, "my_output", party1)] \ No newline at end of file From d3fbd40d75eb31342785c10a5de28eef779e620b Mon Sep 17 00:00:00 2001 From: davetbutler Date: Tue, 18 Jun 2024 08:11:46 +0100 Subject: [PATCH 09/12] my first program client code --- .../my_first_program.py | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 examples_and_tutorials/core_concept_single_party_compute/my_first_program.py diff --git a/examples_and_tutorials/core_concept_single_party_compute/my_first_program.py b/examples_and_tutorials/core_concept_single_party_compute/my_first_program.py new file mode 100644 index 00000000..9a579d9a --- /dev/null +++ b/examples_and_tutorials/core_concept_single_party_compute/my_first_program.py @@ -0,0 +1,85 @@ +import asyncio +import py_nillion_client as nillion +import os +import sys +import pytest + +from dotenv import load_dotenv + +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))) +from helpers.nillion_client_helper import create_nillion_client +from helpers.nillion_keypath_helper import getUserKeyFromFile, getNodeKeyFromFile + +load_dotenv() + + +# 1 Party running simple addition on 1 stored secret and 1 compute time secret +async def main(): + cluster_id = os.getenv("NILLION_CLUSTER_ID") + userkey = getUserKeyFromFile(os.getenv("NILLION_USERKEY_PATH_PARTY_1")) + nodekey = getNodeKeyFromFile(os.getenv("NILLION_NODEKEY_PATH_PARTY_1")) + client = create_nillion_client(userkey, nodekey) + party_id = client.party_id + user_id = client.user_id + party_name = "Party1" + program_name = "my_first_program" + program_mir_path = f"../../programs-compiled/{program_name}.nada.bin" + + # store program + action_id = await client.store_program( + cluster_id, program_name, program_mir_path + ) + + program_id = f"{user_id}/{program_name}" + print('Stored program. action_id:', action_id) + print('Stored program_id:', program_id) + + # Create a secret + stored_secret = nillion.Secrets({ + "my_int1": nillion.SecretInteger(500), + }) + secret_bindings = nillion.ProgramBindings(program_id) + secret_bindings.add_input_party(party_name, party_id) + + # Store a secret + store_id = await client.store_secrets( + cluster_id, secret_bindings, stored_secret, None + ) + + # Bind the parties in the computation to the client to set input and output parties + compute_bindings = nillion.ProgramBindings(program_id) + compute_bindings.add_input_party(party_name, party_id) + compute_bindings.add_output_party(party_name, party_id) + + print(f"Computing using program {program_id}") + print(f"Use secret store_id: {store_id}") + + computation_time_secrets = nillion.Secrets({"my_int2": nillion.SecretInteger(10)}) + + # Compute on the secret + compute_id = await client.compute( + cluster_id, + compute_bindings, + [store_id], + computation_time_secrets, + nillion.PublicVariables({}), + ) + + # Print compute result + print(f"The computation was sent to the network. compute_id: {compute_id}") + while True: + compute_event = await client.next_compute_event() + if isinstance(compute_event, nillion.ComputeFinishedEvent): + print(f"✅ Compute complete for compute_id {compute_event.uuid}") + print(f"đŸ–Ĩī¸ The result is {compute_event.result.value}") + return compute_event.result.value + + +if __name__ == "__main__": + asyncio.run(main()) + + +@pytest.mark.asyncio +async def test_main(): + result = await main() + assert result == {'my_output': 510} From 22778a2f68723d0757c514314430c1d92a07b80c Mon Sep 17 00:00:00 2001 From: davetbutler Date: Tue, 18 Jun 2024 08:16:27 +0100 Subject: [PATCH 10/12] change comment --- .../core_concept_single_party_compute/my_first_program.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples_and_tutorials/core_concept_single_party_compute/my_first_program.py b/examples_and_tutorials/core_concept_single_party_compute/my_first_program.py index 9a579d9a..e14971a5 100644 --- a/examples_and_tutorials/core_concept_single_party_compute/my_first_program.py +++ b/examples_and_tutorials/core_concept_single_party_compute/my_first_program.py @@ -13,7 +13,7 @@ load_dotenv() -# 1 Party running simple addition on 1 stored secret and 1 compute time secret +# 1 Party running your first program on 1 stored secret and 1 compute time secret async def main(): cluster_id = os.getenv("NILLION_CLUSTER_ID") userkey = getUserKeyFromFile(os.getenv("NILLION_USERKEY_PATH_PARTY_1")) From b31f61d70cc7af399e6256e8aafd092e5c331ac2 Mon Sep 17 00:00:00 2001 From: davetbutler Date: Tue, 18 Jun 2024 08:17:44 +0100 Subject: [PATCH 11/12] revert back to original for standard bootstrap --- bootstrap-local-environment.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bootstrap-local-environment.sh b/bootstrap-local-environment.sh index 35497ebe..2ad6f7f1 100755 --- a/bootstrap-local-environment.sh +++ b/bootstrap-local-environment.sh @@ -7,8 +7,8 @@ num_user_keys=5 # set env file to update ENV_TO_UPDATE=".env" -NILLION_DEVNET="/root/.nilup/sdks/latest/nillion-devnet" -NILLION_CLI="/root/.nilup/sdks/latest/nillion" +NILLION_DEVNET="nillion-devnet" +NILLION_CLI="nillion" NILLION_CLI_COMMAND_USER_KEYGEN="user-key-gen" NILLION_CLI_COMMAND_NODE_KEYGEN="node-key-gen" From 14bde59ffddcc0f69d8a41185c1d8bf1b9c94910 Mon Sep 17 00:00:00 2001 From: LOKESH2622 Date: Wed, 26 Jun 2024 07:54:48 +0000 Subject: [PATCH 12/12] my new nada program --- programs/my_first_program.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/programs/my_first_program.py b/programs/my_first_program.py index e930708d..2f351439 100644 --- a/programs/my_first_program.py +++ b/programs/my_first_program.py @@ -1,12 +1,13 @@ from nada_dsl import * - def nada_main(): party1 = Party(name="Party1") my_int1 = SecretInteger(Input(name="my_int1", party=party1)) my_int2 = SecretInteger(Input(name="my_int2", party=party1)) # write the computation for your program here - use my_int1 and my_int2 as inputs - # make sure you change the output below to be your new output + # New computation: multiply my_int1 and my_int2 + result = my_int1 * my_int2 - return [Output(my_int1, "my_output", party1)] \ No newline at end of file + # make sure you change the output below to be your new output + return [Output(result, "my_output", party1)]