diff --git a/.env.sample b/.env.sample deleted file mode 100644 index f843ceec..00000000 --- a/.env.sample +++ /dev/null @@ -1,18 +0,0 @@ -# Leave these blank - the env variables will be populated when you run ./bootstrap-local-environment.sh -NILLION_BOOTNODE_MULTIADDRESS= -NILLION_CLUSTER_ID= -NILLION_USERKEY_PATH_PARTY_1= -NILLION_USERKEY_PATH_PARTY_2= -NILLION_USERKEY_PATH_PARTY_3= -NILLION_USERKEY_PATH_PARTY_4= -NILLION_USERKEY_PATH_PARTY_5= -NILLION_NODEKEY_PATH_PARTY_1= -NILLION_NODEKEY_PATH_PARTY_2= -NILLION_NODEKEY_PATH_PARTY_3= -NILLION_NODEKEY_PATH_PARTY_4= -NILLION_NODEKEY_PATH_PARTY_5= -NILLION_BLOCKCHAIN_RPC_ENDPOINT= -NILLION_CHAIN_ID= -NILLION_PAYMENTS_SC_ADDRESS= -NILLION_BLINDING_FACTORS_MANAGER_SC_ADDRESS= -NILLION_WALLET_PRIVATE_KEY= diff --git a/.gitignore b/.gitignore index 23729864..57248ff4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,12 @@ **/*.pyc **/__pycache__ -programs-compiled/ nillion-venv/ permissions/.nillion-config.json +examples_and_tutorials/nada_programs/target +quickstart_complete/nada_quickstart_programs/target .env +.idea .DS_Store *.key + .venv \ No newline at end of file diff --git a/.nil-sdk.toml b/.nil-sdk.toml new file mode 100644 index 00000000..f3183813 --- /dev/null +++ b/.nil-sdk.toml @@ -0,0 +1 @@ +version = "v0.9.0" \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index ed99c932..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,77 +0,0 @@ -# Contributing Guide - -Thank you for investing your time in contributing. - -This guide aims to provide an overview of the contribution workflow to help us make the contribution process effective for everyone involved. - -Read the [README](README.md) to get an overview of the project. - -### Project Status - -The project is under active development. - -You can view the open Issues, follow the development process and contribute to the project. - -## Getting started - -You can contribute to this repo in many ways: - -- Solve open issues -- Report bugs or feature requests -- Improve the documentation - -Contributions are made via Issues and Pull Requests (PRs). A few general guidelines for contributions: - -- Search for existing Issues and PRs before creating your own. -- Contributions should only fix/add the functionality in the issue OR address style issues, not both. -- If you're running into an error, please give context. Explain what you're trying to do and how to reproduce the error. -- Please use the same formatting in the code repository. You can configure your IDE to do it by using the prettier / linting config files included in each package. -- If applicable, please edit the README.md file to reflect the changes. - -### Issues - -Issues should be used to report problems, request a new feature, or discuss potential changes before a PR is created. - -#### Solve an issue - -Scan through our [existing issues](https://github.com/NillionNetwork/nillion-python-starter/issues) to find one that interests you. - -If a contributor is working on the issue, they will be assigned to the individual. If you find an issue to work on, you are welcome to assign it to yourself and open a PR with a fix for it. - -#### Create a new issue - -If a related issue doesn't exist, you can open a new issue. - -Some tips to follow when you are creating an issue: - -- Provide as much context as possible. Over-communicate to give the most details to the reader. -- Include the steps to reproduce the issue or the reason for adding the feature. -- Screenshots, videos etc., are highly appreciated. - -### Pull Requests - -#### Pull Request Process - -We follow the ["fork-and-pull" Git workflow](https://github.com/susam/gitpr) - -1. Fork the repo -2. Clone the project -3. Create a new branch with a descriptive name -4. Commit your changes to the new branch -5. Push changes to your fork -6. Open a PR in our repository by [creating a pull request from your fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork). Use the "main" branch as the base branch, the branch in the repository that you want to merge your changes into. -7. Tag one of the maintainers to review your PR - -Here are some tips for a high-quality pull request: - -- Create a title for the PR that accurately defines the work done. -- Structure the description neatly to make it easy to consume by the readers. For example, you can include bullet points and screenshots instead of having one large paragraph. -- Add the link to the issue if applicable. -- Have a good commit message that summarises the work done. - -Once you submit your PR: - -- We may ask questions, request additional information or ask for changes to be made before a PR can be merged. Please note that these are to make the PR clear for everyone involved and aims to create a frictionless interaction process. -- As you update your PR and apply changes, mark each conversation resolved. - -Once the PR is approved, we'll "squash-and-merge" to keep the git commit history clean. diff --git a/README.md b/README.md index ab1eda19..996460f0 100644 --- a/README.md +++ b/README.md @@ -1,76 +1,14 @@ # Nillion Python Starter -This is a python starter repo for building on the Nillion Network. Complete environment setup, then run the examples: - -- To run multi party examples, go to the [multi party compute](examples_and_tutorials/core_concept_multi_party_compute) folder. - -- To run single party examples, go to the [single party compute](examples_and_tutorials/core_concept_single_party_compute) folder. - -- To run permissions examples (storing and retrieving permissioned secrets, revoking permissions, etc.), go to the [permissions](examples_and_tutorials/core_concept_permissions) folder. - -### Prerequisites: Install the CLI Dependencies - -The `nillion-devnet` tool spins up `anvil` under the hood, so you need to have `foundry` installed. The [`bootstrap-local-environment.sh`](./bootstrap-local-environment.sh) file uses `pidof` and `grep`. - -- [Install `foundry`](https://book.getfoundry.sh/getting-started/installation) -- [Install `pidof`](https://command-not-found.com/pidof) -- [Install `grep`](https://command-not-found.com/grep) - -## Environment Setup - -1. Create a `.env` file by copying the sample: - - ```shell - cp .env.sample .env - ``` - -2. Create the virtual environment (`.venv`), install dependencies, and activate the virtual environment - - ```shell - bash ./create_venv.sh && source .venv/bin/activate - ``` - - Run the [`bootstrap-local-environment.sh`](./bootstrap-local-environment.sh) script to spin up `nillion-devnet`, generate keys, and get bootnodes, cluster, and payment info: - - ```shell - ./bootstrap-local-environment.sh - ``` - -3. Check `.env` file - keys, bootnodes, cluster, and payment info should now be present. If you want to run against a local cluster, use this configuration. Otherwise, replace values with testnet bootnodes, cluster, and payment info. - -4. Look through the [programs](./programs/) folder to see examples of Nada programs. - -## Compiling Programs - -Nada programs need to be compiled ahead of being stored. Compile all programs in the [programs](./programs/) folder with the script [`compile_programs.sh`](./compile_programs.sh): - -```shell -bash compile_programs.sh -``` - -This generates a `programs-compiled` folder containing the compiled programs. - -## Store a Compiled Program - -Store a compiled program in the network with this script: - -```shell -bash store_program.sh {RELATIVE_COMPILED_PROGRAM_PATH} -``` - -To store the compiled [`addition_simple`](./programs/addition_simple.py) program you can run: - -```shell -bash store_program.sh programs-compiled/addition_simple.nada.bin -``` - -Storing a program results in the stored `program_id`, the network's reference to the program. The `program_id` is the `{user_id}/{program_name}`. - -## Testing - -Most examples and tutorials within this repository can be tested. Docker is required to run the tests. - -```shell -cd testing -bash run_tests.sh -``` +Welcome to the start of your Nillion developer journey. + +This repo corresponds to the Nillion Python quickstart. To get started with Nillion head over to the [Python QuickStart docs](https://docs.nillion.com/python-quickstart) and follow the quickstart guide. + +For more python examples, check out https://github.com/NillionNetwork/python-examples which contains the following: +- core_concept_multi_party_compute +- core_concept_permissions +- core_concept_single_party_compute +- core_concept_store_and_retrieve_secrets +- millionaires_problem_example +- nada_programs +- voting_tutorial diff --git a/bootstrap-local-environment.sh b/bootstrap-local-environment.sh deleted file mode 100755 index 2ad6f7f1..00000000 --- a/bootstrap-local-environment.sh +++ /dev/null @@ -1,152 +0,0 @@ -#!/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="nillion-devnet" -NILLION_CLI="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/compile_programs.sh b/compile_programs.sh deleted file mode 100755 index 5c7d647e..00000000 --- a/compile_programs.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/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="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 diff --git a/create_venv.sh b/create_venv.sh deleted file mode 100755 index 8e5bb12e..00000000 --- a/create_venv.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash - -function create_venv () { - if [[ ! -z "${VIRTUAL_ENV:-}" ]]; then - echo "Virtualenv is already active! Run 'deactivate' to deactivate the virtualenv." - return 0 - fi - - echo "Creating virtualenv" - python3 -m pip install --user virtualenv==20.24.6 - - NILLION_VENV=".venv" - mkdir -p "$NILLION_VENV" - python3 -m virtualenv -p python3 "$NILLION_VENV" - source "$NILLION_VENV/bin/activate" - python3 -m pip install -r requirements.txt - - echo "Virtualenv: $NILLION_VENV" - echo "Check the $NILLION_VENV/lib/python3.1X/site-packages folder to make sure you have py_nillion_client and nada_dsl packages" - echo "📋 Copy and run the following command to activate your environment:" - echo "source $NILLION_VENV/bin/activate" -} - -create_venv diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index a5fa73dc..00000000 --- a/docker-compose.yml +++ /dev/null @@ -1,38 +0,0 @@ -version: '3.8' -services: - demo: - container_name: nillion - build: - context: . - dockerfile_inline: | - FROM python:3.10-slim - ARG DEBIAN_FRONTEND="noninteractive" - SHELL ["/bin/bash", "-o", "pipefail", "-xe", "-c"] - RUN apt update; \ - apt install -y --no-install-recommends \ - jq; \ - apt clean; \ - rm -rf /var/lib/apt/lists/*; \ - pip install --upgrade pip; \ - groupadd -r nillion; \ - useradd -rm -g nillion nillion; - USER nillion - network_mode: 'bridge' - environment: - - PUID=1000 - - PGID=1000 - - TZ=Europe/London - - NILLION_SDK_ROOT=/sdk - volumes: - - '$PWD/..:/host' - - $NILLION_SDK_ROOT:/sdk - working_dir: /host - command: - - /bin/bash - - '-c' - - | - source ./utils.sh - check_for_sdk_root - install_nada_dsl - pushd permissions - ./bootstrap-local-environment.sh diff --git a/examples_and_tutorials/core_concept_multi_party_compute/01_store_secret_party1.py b/examples_and_tutorials/core_concept_multi_party_compute/01_store_secret_party1.py deleted file mode 100644 index a1025c66..00000000 --- a/examples_and_tutorials/core_concept_multi_party_compute/01_store_secret_party1.py +++ /dev/null @@ -1,66 +0,0 @@ -import asyncio -import py_nillion_client as nillion -import os -import sys -import pytest - -from dotenv import load_dotenv -from config import ( - CONFIG_PROGRAM_NAME, - CONFIG_PARTY_1 -) - -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() - -# The 1st Party stores a secret -async def main(): - cluster_id = os.getenv("NILLION_CLUSTER_ID") - client_1 = create_nillion_client( - getUserKeyFromFile(CONFIG_PARTY_1["userkey_file"]), getNodeKeyFromFile(CONFIG_PARTY_1["nodekey_file"]) - ) - party_id_1 = client_1.party_id - user_id_1 = client_1.user_id - - - program_mir_path=f"../../programs-compiled/{CONFIG_PROGRAM_NAME}.nada.bin" - - # 1st Party stores program - action_id = await client_1.store_program( - cluster_id, CONFIG_PROGRAM_NAME, program_mir_path - ) - - program_id=f"{user_id_1}/{CONFIG_PROGRAM_NAME}" - print('Stored program. action_id:', action_id) - print('Stored program_id:', program_id) - - - # 1st Party creates a secret - stored_secret_1 = nillion.Secrets({ - key: nillion.SecretInteger(value) - for key, value in CONFIG_PARTY_1["secrets"].items() - }) - - # 1st Party creates input bindings for the program - secret_bindings_1 = nillion.ProgramBindings(program_id) - secret_bindings_1.add_input_party(CONFIG_PARTY_1["party_name"], party_id_1) - - # 1st Party stores a secret - store_id_1 = await client_1.store_secrets( - cluster_id, secret_bindings_1, stored_secret_1, None - ) - secrets_string = ", ".join(f"{key}: {value}" for key, value in CONFIG_PARTY_1["secrets"].items()) - print(f"\n🎉1️⃣ Party {CONFIG_PARTY_1['party_name']} stored {secrets_string} at store id: {store_id_1}") - print("\n📋⬇️ Copy and run the following command to store N other party secrets") - print(f"\npython3 02_store_secret_party_n.py --user_id_1 {user_id_1} --store_id_1 {store_id_1}") - return [user_id_1, store_id_1] - -if __name__ == "__main__": - asyncio.run(main()) - -@pytest.mark.asyncio -async def test_main(): - pass diff --git a/examples_and_tutorials/core_concept_multi_party_compute/02_store_secret_party_n.py b/examples_and_tutorials/core_concept_multi_party_compute/02_store_secret_party_n.py deleted file mode 100644 index 45dd79c7..00000000 --- a/examples_and_tutorials/core_concept_multi_party_compute/02_store_secret_party_n.py +++ /dev/null @@ -1,98 +0,0 @@ -import argparse -import asyncio -import py_nillion_client as nillion -import os -import sys -import pytest - -from dotenv import load_dotenv -from config import ( - CONFIG_PROGRAM_NAME, - CONFIG_N_PARTIES -) - -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() - -# N other parties store a secret -async def main(args = None): - parser = argparse.ArgumentParser( - description="Create a secret on the Nillion network with set read/retrieve permissions" - ) - parser.add_argument( - "--user_id_1", - required=True, - type=str, - help="User ID of the user who will compute with the secret being stored", - ) - parser.add_argument( - "--store_id_1", - required=True, - type=str, - help="Store ID of the 1st secret", - ) - - args = parser.parse_args(args) - - cluster_id = os.getenv("NILLION_CLUSTER_ID") - program_id=f"{args.user_id_1}/{CONFIG_PROGRAM_NAME}" - - # start a list of store ids to keep track of stored secrets - store_ids = [] - party_ids = [] - - for party_info in CONFIG_N_PARTIES: - client_n = create_nillion_client( - getUserKeyFromFile(party_info["userkey_file"]), - getNodeKeyFromFile(party_info["nodekey_file"]) - ) - party_id_n = client_n.party_id - user_id_n = client_n.user_id - party_name = party_info["party_name"] - secret_name = party_info["secret_name"] - secret_value = party_info["secret_value"] - - # Create a secret for the current party - stored_secret = nillion.Secrets({ - secret_name: nillion.SecretInteger(secret_value) - }) - - # Create input bindings for the program - secret_bindings = nillion.ProgramBindings(program_id) - secret_bindings.add_input_party(party_name, party_id_n) - - # Create permissions object - permissions = nillion.Permissions.default_for_user(user_id_n) - - # Give compute permissions to the first party - compute_permissions = { - args.user_id_1: {program_id}, - } - permissions.add_compute_permissions(compute_permissions) - - # Store the permissioned secret - store_id = await client_n.store_secrets( - cluster_id, secret_bindings, stored_secret, permissions - ) - - store_ids.append(store_id) - party_ids.append(party_id_n) - - print(f"\n🎉N Party {party_name} stored {secret_name}: {secret_value} at store id: {store_id}") - print(f"\n🎉Compute permission on the secret granted to user_id: {args.user_id_1}") - - party_ids_to_store_ids = ' '.join([f'{party_id}:{store_id}' for party_id, store_id in zip(party_ids, store_ids)]) - - print("\n📋⬇️ Copy and run the following command to run multi party computation using the secrets") - print(f"\npython3 03_multi_party_compute.py --store_id_1 {args.store_id_1} --party_ids_to_store_ids {party_ids_to_store_ids}") - return [args.store_id_1, party_ids_to_store_ids] - -if __name__ == "__main__": - asyncio.run(main()) - -@pytest.mark.asyncio -async def test_main(): - pass diff --git a/examples_and_tutorials/core_concept_multi_party_compute/03_multi_party_compute.py b/examples_and_tutorials/core_concept_multi_party_compute/03_multi_party_compute.py deleted file mode 100644 index 04591710..00000000 --- a/examples_and_tutorials/core_concept_multi_party_compute/03_multi_party_compute.py +++ /dev/null @@ -1,108 +0,0 @@ -import argparse -import asyncio -import py_nillion_client as nillion -import os -import sys -import pytest -import importlib - -from dotenv import load_dotenv - -from config import ( - CONFIG_PROGRAM_NAME, - CONFIG_PARTY_1, - CONFIG_N_PARTIES -) - -store_secret_party_1 = importlib.import_module("01_store_secret_party1") -store_secret_party_n = importlib.import_module("02_store_secret_party_n") - -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() - -async def main(args = None): - parser = argparse.ArgumentParser( - description="Create a secret on the Nillion network with set read/retrieve permissions" - ) - - parser.add_argument( - "--store_id_1", - required=True, - type=str, - help="Store ID of the 1st secret", - ) - - parser.add_argument( - "--party_ids_to_store_ids", - required=True, - nargs='+', - type=str, - help="List of partyid:storeid pairs of the secrets, with each pair separated by a space", - ) - - args = parser.parse_args(args) - - cluster_id = os.getenv("NILLION_CLUSTER_ID") - - # 1st party computes on secrets - client_1 = create_nillion_client( - getUserKeyFromFile(CONFIG_PARTY_1["userkey_file"]), - getNodeKeyFromFile(CONFIG_PARTY_1["nodekey_alternate_file"]), - ) - user_id_1 = client_1.user_id - party_id_1 = client_1.party_id - - program_id=f"{user_id_1}/{CONFIG_PROGRAM_NAME}" - - - # Bind the parties in the computation to the client to set inputs and output parties - compute_bindings = nillion.ProgramBindings(program_id) - compute_bindings.add_input_party(CONFIG_PARTY_1["party_name"], party_id_1) - compute_bindings.add_output_party(CONFIG_PARTY_1["party_name"], party_id_1) - store_id_1 = args.store_id_1 - - print(f"Computing using program {program_id}") - print(f"Party 1 secret store_id: {store_id_1}") - - party_ids_to_store_ids = {} - i=0 - for pair in args.party_ids_to_store_ids: - party_id, store_id = pair.split(':') - party_name = CONFIG_N_PARTIES[i]['party_name'] - compute_bindings.add_input_party(party_name, party_id) - party_ids_to_store_ids[party_id] = store_id - i=i+1 - - # Compute on the secret with all store ids. Note that there are no compute time secrets or public variables - compute_id = await client_1.compute( - cluster_id, - compute_bindings, - [store_id_1] + list(party_ids_to_store_ids.values()), - nillion.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_1.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 store_secret_party_1.main() - args = ['--user_id_1', result[0], '--store_id_1', result[1]] - result = await store_secret_party_n.main(args) - store_ids = result[1].split(' ', 1) - args = ['--store_id_1', result[0], '--party_ids_to_store_ids', store_ids[0], store_ids[1]] - result = await main(args) - assert result == {'my_output': 8} diff --git a/examples_and_tutorials/core_concept_multi_party_compute/README.md b/examples_and_tutorials/core_concept_multi_party_compute/README.md deleted file mode 100644 index 856a997c..00000000 --- a/examples_and_tutorials/core_concept_multi_party_compute/README.md +++ /dev/null @@ -1,52 +0,0 @@ -# Multi Party Example - -This is an example using the Nillion Python Client to store a program, and run multi party compute, computation involving secret inputs from multiple parties. - -## Assumptions - -The 3-part Multi Party example assumes there is a stored program that meets the following criteria: - -- there are N+1 parties -- each party contributes secret inputs of type SecretInteger, and each party stores at least one secret an input -- the 1st party stores a secret -- N other parties store secrets and give the 1st party permission to compute on their secrets -- the 1st party runs compute -- the 1st party reads the output - -## Run the example - -1. Set up requirements following the repo README to populate the .env file with environment variables. - -2. Follow README instructions to compile a program that meets the above assumptions. Take note of the program name. Here are some programs that meet the multi party example program assumptions: - -- addition_simple_multi_party.nada.bin -- circuit_simple_multi_party.nada.bin -- complex_operation_mix -- greater_or_equal_than -- greater_than -- import_file -- less_or_equal_than -- less_than -- multiplication_simple_multi_party.nada.bin -- single_addition_multi_party.nada.bin -- subtraction_simple_multi_party.nada.bin -- subtraction_simple_neg_multi_party.nada.bin -- reuse_simple_1_multi_party.nada.bin -- reuse_simple_sub_multi_party.nada.bin - -For example before running addition_simple_multi_party.py, compile all programs and check that addition_simple_multi_party.nada.bin exists in the compiled-programs folder. - -```bash -cd ../.. -./compile_programs.sh -``` - -3. Update values in config.py to set the stored program name, party names, secret names, and secret values. - -4. Start the 3 part example by running - -```bash -python3 01_store_secret_party1.py -``` - -5. Follow the cli directions to run part 2 and 3 of the example. diff --git a/examples_and_tutorials/core_concept_multi_party_compute/config.py b/examples_and_tutorials/core_concept_multi_party_compute/config.py deleted file mode 100644 index 2c4ef4af..00000000 --- a/examples_and_tutorials/core_concept_multi_party_compute/config.py +++ /dev/null @@ -1,35 +0,0 @@ -import os -from dotenv import load_dotenv -load_dotenv() - -# replace this with your program_id -CONFIG_PROGRAM_NAME="addition_simple_multi_party_3" - -# 1st party -CONFIG_PARTY_1={ - "userkey_file": os.getenv("NILLION_USERKEY_PATH_PARTY_1"), - "nodekey_file": os.getenv("NILLION_NODEKEY_PATH_PARTY_1"), - "nodekey_alternate_file": os.getenv("NILLION_NODEKEY_PATH_PARTY_4"), - "party_name": "Party1", - "secrets": { - "my_int1": 1, - } -} - -# N other parties -CONFIG_N_PARTIES=[ - { - "userkey_file": os.getenv("NILLION_USERKEY_PATH_PARTY_2"), - "nodekey_file": os.getenv("NILLION_NODEKEY_PATH_PARTY_2"), - "party_name": "Party2", - "secret_name": "my_int2", - "secret_value": 5, - }, - { - "userkey_file": os.getenv("NILLION_USERKEY_PATH_PARTY_3"), - "nodekey_file": os.getenv("NILLION_NODEKEY_PATH_PARTY_3"), - "party_name": "Party3", - "secret_name": "my_int3", - "secret_value": 2, - }, -] \ No newline at end of file diff --git a/examples_and_tutorials/core_concept_permissions/01_fetch_reader_userid.py b/examples_and_tutorials/core_concept_permissions/01_fetch_reader_userid.py deleted file mode 100644 index f41f4636..00000000 --- a/examples_and_tutorials/core_concept_permissions/01_fetch_reader_userid.py +++ /dev/null @@ -1,33 +0,0 @@ -import asyncio -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() - -async def main(): - userkey = getUserKeyFromFile(os.getenv("NILLION_USERKEY_PATH_PARTY_1")) - nodekey = getNodeKeyFromFile(os.getenv("NILLION_NODEKEY_PATH_PARTY_1")) - - # Reader Nillion client - reader = create_nillion_client(userkey, nodekey) - # Get the reader's user id - reader_user_id = reader.user_id - - print("ℹ️ Fetched the reader's USER ID:", reader_user_id) - print("\n\nRun the following command to store a secret and give read/retrieve permissions to the READER USER ID") - print(f"\n📋 python3 02_store_permissioned_secret.py --retriever_user_id {reader_user_id}") - return reader_user_id - -if __name__ == "__main__": - asyncio.run(main()) - -@pytest.mark.asyncio -async def test_main(): - pass diff --git a/examples_and_tutorials/core_concept_permissions/02_store_permissioned_secret.py b/examples_and_tutorials/core_concept_permissions/02_store_permissioned_secret.py deleted file mode 100644 index 82c344a0..00000000 --- a/examples_and_tutorials/core_concept_permissions/02_store_permissioned_secret.py +++ /dev/null @@ -1,76 +0,0 @@ -import argparse -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() - - -async def main(args = None): - parser = argparse.ArgumentParser( - description="Create a secret on the Nillion network with set read/retrieve permissions" - ) - parser.add_argument( - "--retriever_user_id", - required=True, - type=str, - help="User ID of the reader python client (derived from user key)", - ) - args = parser.parse_args(args) - - cluster_id = os.getenv("NILLION_CLUSTER_ID") - userkey = getUserKeyFromFile(os.getenv("NILLION_USERKEY_PATH_PARTY_2")) - nodekey = getNodeKeyFromFile(os.getenv("NILLION_NODEKEY_PATH_PARTY_2")) - - # Writer Nillion client - writer = create_nillion_client(userkey, nodekey) - writer_user_id = writer.user_id - print(writer_user_id, args.retriever_user_id) - - # Writer gives themself default core_concept_permissions - permissions = nillion.Permissions.default_for_user(writer_user_id) - # Writer gives the reader permission to read/retrieve secret - permissions.add_retrieve_permissions(set([args.retriever_user_id, writer_user_id])) - - result = ( - "allowed" - if permissions.is_retrieve_allowed(args.retriever_user_id) - else "not allowed" - ) - if result == "not allowed": - raise Exception("failed to set core_concept_permissions") - - print(f"ℹ️ Permissions set: Reader {args.retriever_user_id} is {result} to retrieve the secret") - - secret_name_1 = "my_int1" - secret_1 = nillion.SecretInteger(10) - - secret_name_2 = "my_int2" - secret_2 = nillion.SecretInteger(32) - secrets_object = nillion.Secrets({secret_name_1: secret_1, secret_name_2: secret_2}) - - # Writer stores the permissioned secret, resulting in the secret's store id - print(f"ℹ️ Storing permissioned secret: {secrets_object}") - store_id = await writer.store_secrets( - cluster_id, None, secrets_object, permissions - ) - - print("ℹ️ STORE ID:", store_id) - print("\n\nRun the following command to retrieve the secret by store id as the reader") - print(f"\n📋 python3 03_retrieve_secret.py --store_id {store_id}") - return store_id - -if __name__ == "__main__": - asyncio.run(main()) - -@pytest.mark.asyncio -async def test_main(): - pass diff --git a/examples_and_tutorials/core_concept_permissions/03_retrieve_secret.py b/examples_and_tutorials/core_concept_permissions/03_retrieve_secret.py deleted file mode 100644 index 5076a968..00000000 --- a/examples_and_tutorials/core_concept_permissions/03_retrieve_secret.py +++ /dev/null @@ -1,51 +0,0 @@ -import argparse -import asyncio -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() - -async def main(args = None): - parser = argparse.ArgumentParser( - description="Use read permissions to retrieve a secret owned by another user on the Nillion network" - ) - parser.add_argument( - "--store_id", - required=True, - type=str, - help="Store ID from the writer client store operation", - ) - args = parser.parse_args(args) - - cluster_id = os.getenv("NILLION_CLUSTER_ID") - userkey = getUserKeyFromFile(os.getenv("NILLION_USERKEY_PATH_PARTY_1")) - nodekey = getNodeKeyFromFile(os.getenv("NILLION_NODEKEY_PATH_PARTY_3")) - - # Reader Nillion client - reader = create_nillion_client(userkey, nodekey) - reader_user_id = reader.user_id - - secret_name = "my_int1" - - # Reader retrieves the named secret by store id - print(f"Retrieving secret as reader: {reader_user_id}") - result = await reader.retrieve_secret(cluster_id, args.store_id, secret_name) - - print(f"🦄 Retrieved {secret_name} secret, value = {result[1].value}", file=sys.stderr) - print("\n\nRun the following command to revoke the reader's retrieve permissions to the secret") - print(f"\n📋 python3 04_revoke_read_permissions.py --store_id {args.store_id} --revoked_user_id {reader_user_id}") - return [args.store_id, reader_user_id] - -if __name__ == "__main__": - asyncio.run(main()) - -@pytest.mark.asyncio -async def test_main(): - pass diff --git a/examples_and_tutorials/core_concept_permissions/04_revoke_read_permissions.py b/examples_and_tutorials/core_concept_permissions/04_revoke_read_permissions.py deleted file mode 100644 index 3a6d73ca..00000000 --- a/examples_and_tutorials/core_concept_permissions/04_revoke_read_permissions.py +++ /dev/null @@ -1,65 +0,0 @@ -import argparse -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() - -async def main(args = None): - parser = argparse.ArgumentParser( - description="Revoke user read/retrieve permissions from a secret on the Nillion network" - ) - parser.add_argument( - "--store_id", - required=True, - type=str, - help="Store ID from the writer client store operation", - ) - parser.add_argument( - "--revoked_user_id", - required=True, - type=str, - help="User ID of the reader python client (derived from user key)", - ) - args = parser.parse_args(args) - - cluster_id = os.getenv("NILLION_CLUSTER_ID") - userkey = getUserKeyFromFile(os.getenv("NILLION_USERKEY_PATH_PARTY_2")) - nodekey = getNodeKeyFromFile(os.getenv("NILLION_NODEKEY_PATH_PARTY_4")) - - # Writer Nillion client - writer = create_nillion_client(userkey, nodekey) - - # Create new permissions object to rewrite permissions (reader no longer has retrieve permission) - new_permissions = nillion.Permissions.default_for_user(writer.user_id) - result = ( - "allowed" - if new_permissions.is_retrieve_allowed(args.revoked_user_id) - else "not allowed" - ) - if result != "not allowed": - raise Exception("failed to create valid permissions object") - - # Update the permission - print(f"ℹ️ Updating permissions for secret: {args.store_id}.") - print(f"ℹ️ Reset permissions so that user id {args.revoked_user_id} is {result} to retrieve object.", file=sys.stderr) - await writer.update_permissions( cluster_id, args.store_id , new_permissions) - - print("\n\nRun the following command to test that permissions have been properly revoked") - print(f"\n📋 python3 05_test_revoked_permissions.py --store_id {args.store_id}") - return args.store_id - -if __name__ == "__main__": - asyncio.run(main()) - -@pytest.mark.asyncio -async def test_main(): - pass diff --git a/examples_and_tutorials/core_concept_permissions/05_test_revoked_permissions.py b/examples_and_tutorials/core_concept_permissions/05_test_revoked_permissions.py deleted file mode 100644 index 21c257c1..00000000 --- a/examples_and_tutorials/core_concept_permissions/05_test_revoked_permissions.py +++ /dev/null @@ -1,64 +0,0 @@ -import argparse -import asyncio -import os -import sys -import pytest -import importlib - -from dotenv import load_dotenv - -fetch_reader_userid = importlib.import_module("01_fetch_reader_userid") -store_permissioned_secret = importlib.import_module("02_store_permissioned_secret") -retrieve_secret = importlib.import_module("03_retrieve_secret") -revoke_read_permissions = importlib.import_module("04_revoke_read_permissions") - -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() - -async def main(args = None): - parser = argparse.ArgumentParser( - description="Check that retrieval permissions on a Secret have been revoked" - ) - parser.add_argument( - "--store_id", - required=True, - type=str, - help="Store ID from the writer client store operation", - ) - args = parser.parse_args(args) - - cluster_id = os.getenv("NILLION_CLUSTER_ID") - userkey = getUserKeyFromFile(os.getenv("NILLION_USERKEY_PATH_PARTY_1")) - nodekey = getNodeKeyFromFile(os.getenv("NILLION_NODEKEY_PATH_PARTY_5")) - - # Reader Nillion client - reader = create_nillion_client(userkey, nodekey) - reader_user_id = reader.user_id - - try: - secret_name = "my_int1" - await reader.retrieve_secret(cluster_id, args.store_id, secret_name) - print(f"⛔ FAIL: {reader_user_id} user id with revoked permissions was allowed to access secret", file=sys.stderr) - except Exception as e: - if str(e) == "retrieving secret: the user is not authorized to access the secret": - print(f"🦄 Success: After user permissions were revoked, {reader_user_id} was not allowed to access secret", file=sys.stderr) - else: - raise(e) - -if __name__ == "__main__": - asyncio.run(main()) - -@pytest.mark.asyncio -async def test_main(): - result = await fetch_reader_userid.main() - args = ['--retriever_user_id', result] - result = await store_permissioned_secret.main(args) - args = ['--store_id', result] - result = await retrieve_secret.main(args) - args = ['--store_id', result[0], '--revoked_user_id', result[1]] - result = await revoke_read_permissions.main(args) - args = ['--store_id', result] - await main(args) diff --git a/examples_and_tutorials/core_concept_permissions/README.md b/examples_and_tutorials/core_concept_permissions/README.md deleted file mode 100644 index 1be1b83b..00000000 --- a/examples_and_tutorials/core_concept_permissions/README.md +++ /dev/null @@ -1,19 +0,0 @@ -### Run permissions examples (storing and retrieving permissioned secrets, revoking permissions) - -Before running through examples, `./bootstrap-local-environment.sh` creates user keys for the secret writer and for the reader who the writer will allow to read the secret. Permissions examples are labeled 1-5: - -1. The reader fetches their user id -2. The writer stores a secret and gives the reader retrieve permissions on the secret based on the reader's user id, resulting in a store id for the secret -3. The reader retrieves the secret with the store id -4. The writer revokes secret permissions by rewriting them -5. The reader tries to retrieve the secret, but no longer has access to it - -To run through the example flow, simply run the python scripts in order. The output of a script will show you what to run next. Below gives the structure of the commands needed. - -```shell -python3 01_fetch_reader_userid.py -python3 02_store_permissioned_secret.py --retriever_user_id {READER_USER_ID} -python3 03_retrieve_secret.py --store_id {STORE_ID} -python3 04_revoke_read_permissions.py --store_id {STORE_ID} --revoked_user_id {READER_USER_ID} -python3 05_test_revoked_permissions.py --store_id {STORE_ID} -``` diff --git a/examples_and_tutorials/core_concept_single_party_compute/README.md b/examples_and_tutorials/core_concept_single_party_compute/README.md deleted file mode 100644 index a719c9e1..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Single Party Compute Examples - -These compute examples only involve one party. - -## Run an example - -1. Set up requirements following the repo README to populate the .env file with environment variables. - -2. Follow README instructions to compile and store the program of the same name as the file - -For example before running addition_simple.py, compile all programs and check that addition_simple.nada.bin exists in the compiled-programs folder. - -```bash -cd ../.. -./compile_programs.sh -``` - -3. Run the python example - -```bash -python3 addition_simple.py -``` diff --git a/examples_and_tutorials/core_concept_single_party_compute/addition_simple.py b/examples_and_tutorials/core_concept_single_party_compute/addition_simple.py deleted file mode 100644 index 400cc1c3..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/addition_simple.py +++ /dev/null @@ -1,82 +0,0 @@ -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="addition_simple" - 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} diff --git a/examples_and_tutorials/core_concept_single_party_compute/circuit_simple.py b/examples_and_tutorials/core_concept_single_party_compute/circuit_simple.py deleted file mode 100644 index 86194bcd..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/circuit_simple.py +++ /dev/null @@ -1,86 +0,0 @@ -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 a simple circuit on 2 stored secrets and 2 compute time secrets -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="circuit_simple" - 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(3), - "my_int2": nillion.SecretInteger(4), - }) - 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_int3": nillion.SecretInteger(2), - "my_int4": nillion.SecretInteger(5) - }) - - # 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': 22} diff --git a/examples_and_tutorials/core_concept_single_party_compute/circuit_simple_2.py b/examples_and_tutorials/core_concept_single_party_compute/circuit_simple_2.py deleted file mode 100644 index 0fe9db9d..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/circuit_simple_2.py +++ /dev/null @@ -1,91 +0,0 @@ -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 a simple circuit on 6 stored secrets and 3 compute time secrets -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="circuit_simple_2" - 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({ - "A": nillion.SecretInteger(3), - "B": nillion.SecretInteger(14), - "C": nillion.SecretInteger(5), - "D": nillion.SecretInteger(6), - "E": nillion.SecretInteger(2), - "F": nillion.SecretInteger(1), - }) - 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({ - "G": nillion.SecretInteger(9), - "H": nillion.SecretInteger(10), - "I": nillion.SecretInteger(7), - }) - - # Compute on the secrets - 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': 319} diff --git a/examples_and_tutorials/core_concept_single_party_compute/complex.py b/examples_and_tutorials/core_concept_single_party_compute/complex.py deleted file mode 100644 index 5cb0d2e7..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/complex.py +++ /dev/null @@ -1,88 +0,0 @@ -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 complex program on 5 stored secrets and 1 runtime 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="complex" - 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({ - "A": nillion.SecretInteger(10), - "B": nillion.SecretInteger(2), - "C": nillion.SecretInteger(1), - "D": nillion.SecretInteger(5), - "E": nillion.SecretInteger(4), - }) - 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({ - "F": nillion.SecretInteger(3), - }) - - # 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 == {'FINAL': 52} diff --git a/examples_and_tutorials/core_concept_single_party_compute/correlation_coefficient.py b/examples_and_tutorials/core_concept_single_party_compute/correlation_coefficient.py deleted file mode 100644 index d4f3d964..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/correlation_coefficient.py +++ /dev/null @@ -1,132 +0,0 @@ -from pdb import set_trace as bp -import argparse -import asyncio -import py_nillion_client as nillion -import os -import sys -from dotenv import load_dotenv -import random -from math import sqrt -import pytest - -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_0_name="Party0" - party_1_name="Party1" - out_party_name="OutParty" - program_name="correlation_coefficient" - 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) - - # Config elements - linear = lambda x: 30*x + 21 - p0_points = 10 - p1_points = 10 - precision = 5 - - random.seed(42) - - # Create inputs for both parties - party_0_dict = {} - for i in range(p0_points): - party_0_dict[f"x{i}"] = nillion.SecretInteger(i+1) - party_0_dict[f"y{i}"] = nillion.SecretInteger(linear(i+1) + random.randint(0, 10)) - - party_1_dict = {} - for i in range(p0_points, p0_points + p1_points): - party_1_dict[f"x{i}"] = nillion.SecretInteger(i+1) - party_1_dict[f"y{i}"] = nillion.SecretInteger(linear(i+1) + random.randint(0, 10)) - - - # Parties store the secrets - party_0_secrets = nillion.Secrets(party_0_dict) - party_1_secrets = nillion.Secrets(party_1_dict) - - # Bind to the corresponding pary in the program - secret_bindings = nillion.ProgramBindings(program_id) - secret_bindings.add_input_party(party_0_name, party_id) - secret_bindings.add_input_party(party_1_name, party_id) - - # Give core_concept_permissions to owner to compute with my vote - secret_permissions = nillion.Permissions.default_for_user(user_id) - secret_permissions.add_compute_permissions({ - user_id: {program_id}, - }) - - store_ids = [] - # Store in the network - print(f"Storing party 0: {party_0_secrets}") - store_id = await client.store_secrets( - cluster_id, secret_bindings, party_0_secrets, secret_permissions - ) - store_ids.append(store_id) - print(f"Stored party 0 with store_id ={store_id}") - - - print(f"Storing party 1: {party_1_secrets}") - store_id = await client.store_secrets( - cluster_id, secret_bindings, party_1_secrets, secret_permissions - ) - store_ids.append(store_id) - print(f"Stored party 1 with store_id ={store_id}") - - # 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_0_name, party_id) - compute_bindings.add_input_party(party_1_name, party_id) - compute_bindings.add_output_party(out_party_name, party_id) - computation_time_secrets = nillion.Secrets({}) - - print(f"Computing using program {program_id}") - print(f"Use secret store_id: {store_id}") - - # Compute on the secret - compute_id = await client.compute( - cluster_id, - compute_bindings, - store_ids, - 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}") - corr_coeff_squared = compute_event.result.value["correlation_coefficient_squared"] / 10**precision - sign = 1 if compute_event.result.value["sign"] else -1 - corr_coeff = round(sign * sqrt(corr_coeff_squared), precision) - print(f"📈 Correlation coefficient = {corr_coeff} with precision {precision}.") - return compute_event.result.value - - -if __name__ == "__main__": - asyncio.run(main()) - -@pytest.mark.asyncio -async def test_main(): - result = await main() - assert result == {'correlation_coefficient_squared': 99958, 'sign': 1} \ No newline at end of file diff --git a/examples_and_tutorials/core_concept_single_party_compute/division_simple.py b/examples_and_tutorials/core_concept_single_party_compute/division_simple.py deleted file mode 100644 index 10749918..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/division_simple.py +++ /dev/null @@ -1,82 +0,0 @@ -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 division on 2 stored secrets -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="division_simple" - 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(10), - }) - 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({}) - - # Compute on the secret - compute_id = await client.compute( - cluster_id, - compute_bindings, - [store_id], - computation_time_secrets, - nillion.PublicVariables({"my_int3": nillion.PublicVariableInteger(2),}), - ) - - # 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': 5} diff --git a/examples_and_tutorials/core_concept_single_party_compute/input_integer.py b/examples_and_tutorials/core_concept_single_party_compute/input_integer.py deleted file mode 100644 index ab468c81..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/input_integer.py +++ /dev/null @@ -1,81 +0,0 @@ -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, returns the secret input -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="input_integer" - 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({ - "a": nillion.SecretInteger(16), - }) - 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}") - - - # Compute on the secret - compute_id = await client.compute( - cluster_id, - compute_bindings, - [store_id], - nillion.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': 16} diff --git a/examples_and_tutorials/core_concept_single_party_compute/input_single.py b/examples_and_tutorials/core_concept_single_party_compute/input_single.py deleted file mode 100644 index da14b96f..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/input_single.py +++ /dev/null @@ -1,81 +0,0 @@ -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, returns the secret input -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="input_single" - 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(91), - }) - 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}") - - - # Compute on the secret - compute_id = await client.compute( - cluster_id, - compute_bindings, - [store_id], - nillion.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': 91} diff --git a/examples_and_tutorials/core_concept_single_party_compute/modulo_simple.py b/examples_and_tutorials/core_concept_single_party_compute/modulo_simple.py deleted file mode 100644 index 6dc55160..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/modulo_simple.py +++ /dev/null @@ -1,85 +0,0 @@ -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 modulo on 1 stored secrets and a public variable -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="modulo_simple" - 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(37), - }) - 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({}) - public_variables = nillion.PublicVariables({ - "public_my_int2": nillion.PublicVariableInteger(12) - }) - - # Compute on the secret - compute_id = await client.compute( - cluster_id, - compute_bindings, - [store_id], - computation_time_secrets, - public_variables, - ) - - # 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': 1} diff --git a/examples_and_tutorials/core_concept_single_party_compute/multiplication_simple.py b/examples_and_tutorials/core_concept_single_party_compute/multiplication_simple.py deleted file mode 100644 index 2f682ddf..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/multiplication_simple.py +++ /dev/null @@ -1,83 +0,0 @@ -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 multiplication 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 - user_id = client.user_id - party_name="Party1" - program_name="multiplication_simple" - 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': 5000} diff --git a/examples_and_tutorials/core_concept_single_party_compute/nada_fn_composition.py b/examples_and_tutorials/core_concept_single_party_compute/nada_fn_composition.py deleted file mode 100644 index a4987ff0..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/nada_fn_composition.py +++ /dev/null @@ -1,81 +0,0 @@ -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() - -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="nada_fn_composition" - 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': 255000} diff --git a/examples_and_tutorials/core_concept_single_party_compute/nada_fn_simple.py b/examples_and_tutorials/core_concept_single_party_compute/nada_fn_simple.py deleted file mode 100644 index f45734c9..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/nada_fn_simple.py +++ /dev/null @@ -1,81 +0,0 @@ -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() - -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="nada_fn_simple" - 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} diff --git a/examples_and_tutorials/core_concept_single_party_compute/reduce_simple.py b/examples_and_tutorials/core_concept_single_party_compute/reduce_simple.py deleted file mode 100644 index 52766d9d..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/reduce_simple.py +++ /dev/null @@ -1,88 +0,0 @@ -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() - -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="reduce_simple" - 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 array - secret_name = "my_array_1" - - secret_array= nillion.SecretArray([ - nillion.SecretInteger(1), - nillion.SecretInteger(2), - nillion.SecretInteger(3), - nillion.SecretInteger(4), - ]) - - stored_secret_array = nillion.Secrets({secret_name: secret_array}) - secret_bindings = nillion.ProgramBindings(program_id) - secret_bindings.add_input_party(party_name, party_id) - - # Store a secret array - store_id = await client.store_secrets( - cluster_id, secret_bindings, stored_secret_array, 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_int1": nillion.SecretInteger(5)}) - - # 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 == {'reduce.addition': 15} diff --git a/examples_and_tutorials/core_concept_single_party_compute/reuse.py b/examples_and_tutorials/core_concept_single_party_compute/reuse.py deleted file mode 100644 index 55d61343..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/reuse.py +++ /dev/null @@ -1,85 +0,0 @@ -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() - - -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="reuse" - 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({ - "A": nillion.SecretInteger(3), - "B": nillion.SecretInteger(4), - }) - 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({ - "C": nillion.SecretInteger(5), - }) - - # Compute on the secrets - 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 == {'R': 27} diff --git a/examples_and_tutorials/core_concept_single_party_compute/reuse_flipped1.py b/examples_and_tutorials/core_concept_single_party_compute/reuse_flipped1.py deleted file mode 100644 index 57e74be8..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/reuse_flipped1.py +++ /dev/null @@ -1,86 +0,0 @@ -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() - - -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="reuse_flipped1" - 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({ - "A": nillion.SecretInteger(3), - "B": nillion.SecretInteger(14), - - }) - 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({ - "C": nillion.SecretInteger(5), - }) - - # Compute on the secrets - 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 == {'R': 57} diff --git a/examples_and_tutorials/core_concept_single_party_compute/reuse_flipped2.py b/examples_and_tutorials/core_concept_single_party_compute/reuse_flipped2.py deleted file mode 100644 index 006d8f89..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/reuse_flipped2.py +++ /dev/null @@ -1,86 +0,0 @@ -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() - - -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="reuse_flipped2" - 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({ - "A": nillion.SecretInteger(3), - "B": nillion.SecretInteger(14), - - }) - 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({ - "C": nillion.SecretInteger(5), - }) - - # Compute on the secrets - 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 == {'R': 112} diff --git a/examples_and_tutorials/core_concept_single_party_compute/reuse_simple_1.py b/examples_and_tutorials/core_concept_single_party_compute/reuse_simple_1.py deleted file mode 100644 index 44716cee..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/reuse_simple_1.py +++ /dev/null @@ -1,83 +0,0 @@ -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() - - -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="reuse_simple_1" - 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({ - "A": nillion.SecretInteger(3), - "B": nillion.SecretInteger(14), - }) - 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({}) - - # Compute on the secrets - 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': 84} diff --git a/examples_and_tutorials/core_concept_single_party_compute/reuse_simple_2.py b/examples_and_tutorials/core_concept_single_party_compute/reuse_simple_2.py deleted file mode 100644 index a10f2617..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/reuse_simple_2.py +++ /dev/null @@ -1,83 +0,0 @@ -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() - - -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="reuse_simple_2" - 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({ - "A": nillion.SecretInteger(3), - "B": nillion.SecretInteger(4), - }) - 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({}) - - # Compute on the secrets - 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': 49} diff --git a/examples_and_tutorials/core_concept_single_party_compute/reuse_simple_sub.py b/examples_and_tutorials/core_concept_single_party_compute/reuse_simple_sub.py deleted file mode 100644 index 3becc296..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/reuse_simple_sub.py +++ /dev/null @@ -1,84 +0,0 @@ -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() - - -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="reuse_simple_sub" - 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({ - "A": nillion.SecretInteger(50), - }) - 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({ - "B": nillion.SecretInteger(20), - }) - - # Compute on the secrets - 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': 2100} diff --git a/examples_and_tutorials/core_concept_single_party_compute/simple.py b/examples_and_tutorials/core_concept_single_party_compute/simple.py deleted file mode 100644 index ef2b1fef..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/simple.py +++ /dev/null @@ -1,84 +0,0 @@ -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() - -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="simple" - 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({ - "A": nillion.SecretInteger(3), - "B": nillion.SecretInteger(14), - "C": nillion.SecretInteger(5), - "D": nillion.SecretInteger(6), - }) - 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({}) - - # Compute on the secrets - 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 == {'O': 72} diff --git a/examples_and_tutorials/core_concept_single_party_compute/simple_literals.py b/examples_and_tutorials/core_concept_single_party_compute/simple_literals.py deleted file mode 100644 index 7dba12cf..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/simple_literals.py +++ /dev/null @@ -1,82 +0,0 @@ -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() - -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="simple_literals" - 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({ - "A": nillion.SecretInteger(3), - "C": nillion.SecretInteger(5), - }) - 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({}) - - # Compute on the secrets - 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 == {'O': 1015} diff --git a/examples_and_tutorials/core_concept_single_party_compute/simple_public_variables.py b/examples_and_tutorials/core_concept_single_party_compute/simple_public_variables.py deleted file mode 100644 index 95354635..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/simple_public_variables.py +++ /dev/null @@ -1,86 +0,0 @@ -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 compute with only public variables -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="simple_public_variables" - 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({ - "A": nillion.SecretInteger(3), - "C": nillion.SecretInteger(5), - }) - 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}") - - public_variables = nillion.PublicVariables({ - "B": nillion.PublicVariableInteger(10), - "D": nillion.PublicVariableInteger(6), - }) - - # Compute on the secrets - compute_id = await client.compute( - cluster_id, - compute_bindings, - [store_id], - nillion.Secrets({}), - public_variables, - ) - - # 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 == {'O': 119} diff --git a/examples_and_tutorials/core_concept_single_party_compute/simple_public_variables_only.py b/examples_and_tutorials/core_concept_single_party_compute/simple_public_variables_only.py deleted file mode 100644 index ba97ffba..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/simple_public_variables_only.py +++ /dev/null @@ -1,72 +0,0 @@ -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 compute with only public variables -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="simple_public_variables_only" - 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) - - # 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}") - - public_variables = nillion.PublicVariables({ - "A": nillion.PublicVariableInteger(10), - "B": nillion.PublicVariableInteger(6), - }) - - # Compute on the secrets - compute_id = await client.compute( - cluster_id, - compute_bindings, - [], - nillion.Secrets({}), - public_variables, - ) - - # 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 == {'O': 60} diff --git a/examples_and_tutorials/core_concept_single_party_compute/simple_sub.py b/examples_and_tutorials/core_concept_single_party_compute/simple_sub.py deleted file mode 100644 index 261a66df..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/simple_sub.py +++ /dev/null @@ -1,84 +0,0 @@ -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() - -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="simple_sub" - 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({ - "A": nillion.SecretInteger(3), - "B": nillion.SecretInteger(14), - "C": nillion.SecretInteger(5), - "D": nillion.SecretInteger(6), - }) - 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({}) - - # Compute on the secrets - 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 == {'O': 12} diff --git a/examples_and_tutorials/core_concept_single_party_compute/single_addition.py b/examples_and_tutorials/core_concept_single_party_compute/single_addition.py deleted file mode 100644 index ff36d500..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/single_addition.py +++ /dev/null @@ -1,84 +0,0 @@ -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 a simple circuit on 6 stored secrets and 3 compute time secrets -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="single_addition" - 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({ - "A": nillion.SecretInteger(3), - }) - 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({ - "B": nillion.SecretInteger(14), - }) - - # Compute on the secrets - 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 == {'R': 17} diff --git a/examples_and_tutorials/core_concept_single_party_compute/subtraction_simple.py b/examples_and_tutorials/core_concept_single_party_compute/subtraction_simple.py deleted file mode 100644 index e25df271..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/subtraction_simple.py +++ /dev/null @@ -1,84 +0,0 @@ -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 a simple circuit on 6 stored secrets and 3 compute time secrets -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="subtraction_simple" - 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(30), - }) - 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(90), - }) - - # Compute on the secrets - 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': 60} diff --git a/examples_and_tutorials/core_concept_single_party_compute/subtraction_simple_neg.py b/examples_and_tutorials/core_concept_single_party_compute/subtraction_simple_neg.py deleted file mode 100644 index 8bdb441a..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/subtraction_simple_neg.py +++ /dev/null @@ -1,83 +0,0 @@ -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() - -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="subtraction_simple_neg" - 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_int4": nillion.SecretInteger(3), - }) - 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(9) - }) - - # Compute on the secrets - 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': 6} diff --git a/examples_and_tutorials/core_concept_single_party_compute/tiny_secret_addition.py b/examples_and_tutorials/core_concept_single_party_compute/tiny_secret_addition.py deleted file mode 100644 index 2ad87f97..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/tiny_secret_addition.py +++ /dev/null @@ -1,62 +0,0 @@ -import asyncio -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() - -# Complete the 🎯 TODOs to store the tiny_secret_addition program in the network, store secrets, and compute -async def main(): - # 0. The bootstrap-local-environment.sh script put nillion-devnet config variables into the .env file - # Get cluster_id, user_key, and node_key from the .env file - 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")) - - # 🎯 TODO 1. Initialize NillionClient against nillion-devnet - # Create Nillion Client for user - client = create_nillion_client(userkey, nodekey) - - # 🎯 TODO 2. Get the user id and party id from NillionClient - - - # 🎯 TODO 3. Store a compiled Nada program in the network - # Set the program name - program_name="tiny_secret_addition" - # Set the path to the compiled program - program_mir_path=f"../../programs-compiled/{program_name}.nada.bin" - # Store the program - # Create a variable for the program_id, which is the {user_id}/{program_name} - - - # 🎯 TODO 4. Create the 1st secret with bindings to the program - # Create a secret named "my_int1" with any value, ex: 500 - # Create secret bindings object to bind the secret to the program and set the input party - # Set the input party for the secret - # The party name needs to match the party name that is storing "my_int1" in the program - - - # 🎯 TODO 5. Store the secret in the network and print the returned store_id - - - # 🎯 TODO 6. Create compute bindings to set input and output parties - - - # 🎯 TODO 7. Compute on the program with 1st secret from the network, and the 2nd secret, provided at compute time - # Add my_int2, the 2nd secret at computation time - # Compute on the secret - - # 🎯 TODO 8. Print the computation result - -if __name__ == "__main__": - asyncio.run(main()) - -@pytest.mark.asyncio -async def test_main(): - pass diff --git a/examples_and_tutorials/core_concept_single_party_compute/tiny_secret_addition_complete.py b/examples_and_tutorials/core_concept_single_party_compute/tiny_secret_addition_complete.py deleted file mode 100644 index 84213fe7..00000000 --- a/examples_and_tutorials/core_concept_single_party_compute/tiny_secret_addition_complete.py +++ /dev/null @@ -1,101 +0,0 @@ -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() - -# This python script stores the tiny_secret_addition_complete program in the network, store secrets, and compute -async def main(): - # 0. The bootstrap-local-environment.sh script put nillion-devnet config variables into the .env file - # Get cluster_id, user_key, and node_key from the .env file - 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")) - - # ✅ 1. Initialize NillionClient against nillion-devnet - # Create Nillion Client for user - client = create_nillion_client(userkey, nodekey) - - # ✅ 2. Get the user id and party id from NillionClient - party_id = client.party_id - user_id = client.user_id - - # ✅ 3. Store a compiled Nada program in the network - # Set the program name - program_name="tiny_secret_addition_complete" - # Set the path to the compiled program - program_mir_path=f"../../programs-compiled/{program_name}.nada.bin" - # Store the program - action_id = await client.store_program( - cluster_id, program_name, program_mir_path - ) - # Create a variable for the program_id, which is the {user_id}/{program_name} - program_id=f"{user_id}/{program_name}" - print('Stored program. action_id:', action_id) - print('Stored program_id:', program_id) - - - # ✅ 4. Create the 1st secret with bindings to the program - # Create a secret named "my_int1" with any value, ex: 500 - new_secret = nillion.Secrets({ - "my_int1": nillion.SecretInteger(500), - }) - - # Create secret bindings object to bind the secret to the program and set the input party - secret_bindings = nillion.ProgramBindings(program_id) - - # Set the input party for the secret - # The party name needs to match the party name that is storing "my_int1" in the program - party_name="Party1" - secret_bindings.add_input_party(party_name, party_id) - - # ✅ 5. Store the secret in the network and print the returned store_id - store_id = await client.store_secrets( - cluster_id, secret_bindings, new_secret, None - ) - print(f"Computing using program {program_id}") - print(f"Use secret store_id: {store_id}") - - # ✅ 6. Create compute bindings 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) - - # ✅ 7. Compute on the program with 1st secret from the network, and the 2nd secret, provided at compute time - - # Add my_int2, the 2nd secret at computation time - 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({}), - ) - - # ✅ 8. Print the computation 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} diff --git a/examples_and_tutorials/core_concept_store_and_retrieve_secrets/store_and_retrieve_blob.py b/examples_and_tutorials/core_concept_store_and_retrieve_secrets/store_and_retrieve_blob.py deleted file mode 100644 index 90ca81fb..00000000 --- a/examples_and_tutorials/core_concept_store_and_retrieve_secrets/store_and_retrieve_blob.py +++ /dev/null @@ -1,54 +0,0 @@ -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() - -# Store and retrieve a SecretBlob using the Python Client -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) - - # Create a SecretBlob - secret_name = "my_blob" - - # create a bytearray from the string using UTF-8 encoding - secret_value = bytearray("gm, builder!", "utf-8") - secret_integer = nillion.Secrets({ - secret_name: nillion.SecretBlob(secret_value), - }) - - # Store a SecretBlob - # Notice that both bindings and permissions are set to None - # Secrets of type SecretBlob don't need bindings because they aren't used in programs - # Permissions need to be set to allow users other than the secret creator to use the secret - store_id = await client.store_secrets( - cluster_id, None, secret_integer, None - ) - - print(f"The secret is stored at store_id: {store_id}") - - result_tuple = await client.retrieve_secret(cluster_id, store_id, secret_name) - print(f"The secret name as a uuid is {result_tuple[0]}") - - decoded_secret_value = result_tuple[1].value.decode('utf-8') - print(f"The secret value is '{decoded_secret_value}'") - return decoded_secret_value - -if __name__ == "__main__": - asyncio.run(main()) - -@pytest.mark.asyncio -async def test_main(): - result = await main() - assert result == 'gm, builder!' diff --git a/examples_and_tutorials/core_concept_store_and_retrieve_secrets/store_and_retrieve_integer.py b/examples_and_tutorials/core_concept_store_and_retrieve_secrets/store_and_retrieve_integer.py deleted file mode 100644 index 781dcabb..00000000 --- a/examples_and_tutorials/core_concept_store_and_retrieve_secrets/store_and_retrieve_integer.py +++ /dev/null @@ -1,50 +0,0 @@ -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() - -# Store and retrieve a SecretInteger using the Python Client -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) - - # Create a SecretInteger - secret_name = "my_int1" - secret_value = 100 - secret_integer = nillion.Secrets({ - secret_name: nillion.SecretInteger(secret_value), - }) - - # Store a SecretInteger - # Notice that both bindings and permissions are set to None - # Bindings need to be set to use secrets in programs - # Permissions need to be set to allow users other than the secret creator to use the secret - store_id = await client.store_secrets( - cluster_id, None, secret_integer, None - ) - - print(f"The secret is stored at store_id: {store_id}") - - result_tuple = await client.retrieve_secret(cluster_id, store_id, secret_name) - print(f"The secret name as a uuid is {result_tuple[0]}") - print(f"The secret value is {result_tuple[1].value}") - return result_tuple[1].value - -if __name__ == "__main__": - asyncio.run(main()) - -@pytest.mark.asyncio -async def test_main(): - result = await main() - assert result == 100 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 deleted file mode 100644 index f97083ba..00000000 --- a/examples_and_tutorials/millionaires_problem_example/01_store_secret_party1.py +++ /dev/null @@ -1,50 +0,0 @@ -import asyncio -import os -import sys -import pytest - -from dotenv import load_dotenv -from config import ( - CONFIG_PARTY_1 -) - -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() - -# Alice stores the millionaires program in the network -async def main(): - cluster_id = os.getenv("NILLION_CLUSTER_ID") - client_alice = create_nillion_client( - getUserKeyFromFile(CONFIG_PARTY_1["userkey_file"]), getNodeKeyFromFile(CONFIG_PARTY_1["nodekey_file"]) - ) - - millionaires_program_name = "millionaires" - - # Note: check out the code for the full millionaires program in the programs folder - program_mir_path = "millionaires.nada.bin" - - # Store millionaires program in the network - print(f"Storing program in the network: {millionaires_program_name}") - await client_alice.store_program( - cluster_id, millionaires_program_name, program_mir_path - ) - - user_id_alice = client_alice.user_id - program_id = f"{user_id_alice}/{millionaires_program_name}" - - print(f"Alice stores millionaires program at program_id: {program_id}") - print(f"Alice tells Bob and Charlie her user_id and the millionaires program_id") - - print("\n📋⬇️ Copy and run the following command to store Bob and Charlie's salaries in the network") - print(f"\npython3 02_store_secret_party_n.py --user_id_1 {user_id_alice} --program_id {program_id}") - return [user_id_alice, program_id] - -if __name__ == "__main__": - asyncio.run(main()) - -@pytest.mark.asyncio -async def test_main(): - pass diff --git a/examples_and_tutorials/millionaires_problem_example/02_store_secret_party_n.py b/examples_and_tutorials/millionaires_problem_example/02_store_secret_party_n.py deleted file mode 100644 index 63f5e910..00000000 --- a/examples_and_tutorials/millionaires_problem_example/02_store_secret_party_n.py +++ /dev/null @@ -1,100 +0,0 @@ -import argparse -import asyncio -import py_nillion_client as nillion -import os -import sys -import pytest - -from dotenv import load_dotenv -from config import ( - CONFIG_N_PARTIES -) - -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() - -# Bob and Charlie store their salaries in the network -async def main(args = None): - parser = argparse.ArgumentParser( - description="Create a secret on the Nillion network with set read/retrieve permissions" - ) - parser.add_argument( - "--user_id_1", - required=True, - type=str, - help="User ID of the user who will compute with the secret being stored", - ) - parser.add_argument( - "--program_id", - required=True, - type=str, - help="Program ID of the millionaires program", - ) - - args = parser.parse_args(args) - - cluster_id = os.getenv("NILLION_CLUSTER_ID") - - # start a list of store ids to keep track of stored secrets - store_ids = [] - party_ids = [] - - for party_info in CONFIG_N_PARTIES: - client_n = create_nillion_client( - getUserKeyFromFile(party_info["userkey_file"]), - getNodeKeyFromFile(party_info["nodekey_file"]) - ) - party_id_n = client_n.party_id - user_id_n = client_n.user_id - party_name = party_info["party_name"] - secret_name = party_info["secret_name"] - secret_value = party_info["secret_value"] - - # Create a secret for the current party - stored_secret = nillion.Secrets({ - secret_name: nillion.SecretInteger(secret_value) - }) - - # Create input bindings for the specific millionaires program by program id - secret_bindings = nillion.ProgramBindings(args.program_id) - - # Add the respective input party to say who will provide the input to the program - secret_bindings.add_input_party(party_name, party_id_n) - print(f"\n🔗 {party_name} sets bindings so that the secret can be input to a specific program (program_id: {args.program_id}) by a specific party (party_id: {party_id_n})") - - # Create permissions object with default permissions for the current user - permissions = nillion.Permissions.default_for_user(user_id_n) - - # Give compute permissions to Alice so she can use the secret in the specific millionionaires program by program id - compute_permissions = { - args.user_id_1: {args.program_id}, - } - permissions.add_compute_permissions(compute_permissions) - print(f"\n👍 {party_name} gives compute permissions on their secret to Alice's user_id: {args.user_id_1}") - - # Store the permissioned secret - store_id = await client_n.store_secrets( - cluster_id, secret_bindings, stored_secret, permissions - ) - - store_ids.append(store_id) - party_ids.append(party_id_n) - - print(f"\n🎉 {party_name} stored {secret_name}: {secret_value} at store id: {store_id}") - - - party_ids_to_store_ids = ' '.join([f'{party_id}:{store_id}' for party_id, store_id in zip(party_ids, store_ids)]) - - print("\n📋⬇️ Copy and run the following command to run multi party computation using the secrets") - print(f"\npython3 03_multi_party_compute.py --program_id {args.program_id} --party_ids_to_store_ids {party_ids_to_store_ids}") - return [args.program_id, party_ids_to_store_ids] - -if __name__ == "__main__": - asyncio.run(main()) - -@pytest.mark.asyncio -async def test_main(): - pass diff --git a/examples_and_tutorials/millionaires_problem_example/03_multi_party_compute.py b/examples_and_tutorials/millionaires_problem_example/03_multi_party_compute.py deleted file mode 100644 index d8ac2776..00000000 --- a/examples_and_tutorials/millionaires_problem_example/03_multi_party_compute.py +++ /dev/null @@ -1,127 +0,0 @@ -import argparse -import asyncio -import py_nillion_client as nillion -import os -import sys -import pytest -import importlib - -from dotenv import load_dotenv - -from config import ( - CONFIG_PARTY_1, - CONFIG_N_PARTIES -) - -store_secret_party_1 = importlib.import_module("01_store_secret_party1") -store_secret_party_n = importlib.import_module("02_store_secret_party_n") - -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() - -async def main(args = None): - parser = argparse.ArgumentParser( - description="Create a secret on the Nillion network with set read/retrieve permissions" - ) - - parser.add_argument( - "--program_id", - required=True, - type=str, - help="Program ID of the millionaires program", - ) - - parser.add_argument( - "--party_ids_to_store_ids", - required=True, - nargs='+', - type=str, - help="List of partyid:storeid pairs of the secrets, with each pair separated by a space", - ) - - - args = parser.parse_args(args) - - cluster_id = os.getenv("NILLION_CLUSTER_ID") - - # Alice initializes a client - client_alice = create_nillion_client( - getUserKeyFromFile(CONFIG_PARTY_1["userkey_file"]), - getNodeKeyFromFile(CONFIG_PARTY_1["nodekey_alternate_file"]) - ) - party_id_alice = client_alice.party_id - - # Create computation bindings for millionaires program - compute_bindings = nillion.ProgramBindings(args.program_id) - - # Add Alice as an input party - compute_bindings.add_input_party(CONFIG_PARTY_1["party_name"], party_id_alice) - - # Add an output party (Alice). - # The output party reads the result of the blind computation - compute_bindings.add_output_party(CONFIG_PARTY_1["party_name"], party_id_alice) - - print(f"Computing using program {args.program_id}") - - # Also add Bob and Charlie as input parties - party_ids_to_store_ids = {} - i=0 - for pair in args.party_ids_to_store_ids: - party_id, store_id = pair.split(':') - party_name = CONFIG_N_PARTIES[i]['party_name'] - compute_bindings.add_input_party(party_name, party_id) - party_ids_to_store_ids[party_id] = store_id - i=i+1 - - # Add any computation time secrets - # Alice provides her salary at compute time - party_name_alice = CONFIG_PARTY_1["party_name"] - secret_name_alice = CONFIG_PARTY_1["secret_name"] - secret_value_alice = CONFIG_PARTY_1["secret_value"] - compute_time_secrets = nillion.Secrets({ - secret_name_alice: nillion.SecretInteger(secret_value_alice) - }) - - print(f"\n🎉 {party_name_alice} provided {secret_name_alice}: {secret_value_alice} as a compute time secret") - - # Compute on the secret with all store ids. Note that there are no compute time secrets or public variables - compute_id = await client_alice.compute( - cluster_id, - compute_bindings, - list(party_ids_to_store_ids.values()), # Bob and Charlie's stored secrets - compute_time_secrets, # Alice's computation time secret - nillion.PublicVariables({}), - ) - - # Print compute result - print(f"The computation was sent to the network. compute_id: {compute_id}") - while True: - compute_event = await client_alice.next_compute_event() - if isinstance(compute_event, nillion.ComputeFinishedEvent): - print(f"✅ Compute complete for compute_id {compute_event.uuid}") - print(f"🖥️ The output result is {compute_event.result.value}") - - # The compute result is an index - # Map it to the corresponding party name who should pay for lunch - # ['Alice', 'Bob', 'Charlie'] - my_parties = CONFIG_N_PARTIES - my_parties.insert(0, CONFIG_PARTY_1) - richest_party = my_parties[compute_event.result.value["largest_position"]]["party_name"] - print(f"The richest friend is {richest_party}") - return richest_party - -if __name__ == "__main__": - asyncio.run(main()) - -@pytest.mark.asyncio -async def test_main(): - result = await store_secret_party_1.main() - args = ['--user_id_1', result[0], '--program_id', result[1]] - result = await store_secret_party_n.main(args) - store_ids = result[1].split(' ', 1) - args = ['--program_id', result[0], '--party_ids_to_store_ids', store_ids[0], store_ids[1]] - result = await main(args) - assert result == 'Charlie' diff --git a/examples_and_tutorials/millionaires_problem_example/README.md b/examples_and_tutorials/millionaires_problem_example/README.md deleted file mode 100644 index 181ec1a1..00000000 --- a/examples_and_tutorials/millionaires_problem_example/README.md +++ /dev/null @@ -1,51 +0,0 @@ -# MPC Millionaires Problem Example - -In our version of the [Millionaires Problem](https://docs.nillion.com/multi-party-computation#classic-scenario-the-millionaires-problem), three friends, Alice, Bob & Charlie finish lunch and decide that the richest friend should pay the bill. - -They want to figure out who has the highest salary without telling each other how much they earn. - -Alice suggests that this is the perfect opportunity for the friends to use Nillion for blind computation on high value data to determine who should pay for lunch. She offers to create a millionaires problem program, compile it, and store it in Nillion so that the friends can run multi party blind computation to figure out who should pay for lunch. - -- Alice's millionaires program in NADA: [../../programs/millionaires.py](https://github.com/NillionNetwork/nillion-python-starter/blob/main/programs/millionaires.py) -- Compiled program: [./millionaires.nada.bin](https://github.com/NillionNetwork/nillion-python-starter/blob/main/millionaires_problem_example/millionaires.nada.bin) - -## Setup - -Before running the example, [follow the repo README](https://github.com/NillionNetwork/nillion-python-starter/blob/main/README.md) to install cli dependencies, complete environment setup, and activate your .venv environment. - -## Run the example - -### Step 1 - -- Alice creates and stores program in the network -- Alice shares her user id and the resulting program id with Bob and Charlie - -Run the command to perform step 1. - -```bash -python3 01_store_secret_party1.py -``` - -### Step 2 - -- Bob and Charlie store their salaries in the network as secrets. Each secret is stored with - - **bindings** to the millionaires program id - - **permissions** so Alice can compute using the secret -- Bob and Charlie each share their party_id and secret store_id with Alice. - -The script will provide the command to perform step 2. - -```bash -python3 02_store_secret_party_n.py --user_id_1 {user_id} --program_id {program_id} -``` - -### Step 3 - -- Alice runs millionaires program computation using Bob and Charlie's secret salaries. She provides her own salary as a secret at computation time. -- Alice receives the output result of the program, and the friends know that Charlie should pay for lunch. - -The script will provide the command to perform step 3. - -```bash -python3 03_multi_party_compute.py --program_id {program_id} --party_ids_to_store_ids {party_ids_to_store_ids} -``` diff --git a/examples_and_tutorials/millionaires_problem_example/config.py b/examples_and_tutorials/millionaires_problem_example/config.py deleted file mode 100644 index 24fb49d8..00000000 --- a/examples_and_tutorials/millionaires_problem_example/config.py +++ /dev/null @@ -1,32 +0,0 @@ -import os -import py_nillion_client as nillion -from dotenv import load_dotenv -load_dotenv() - -# Alice -CONFIG_PARTY_1={ - "userkey_file": os.getenv("NILLION_USERKEY_PATH_PARTY_1"), - "nodekey_file": os.getenv("NILLION_NODEKEY_PATH_PARTY_1"), - "nodekey_alternate_file": os.getenv("NILLION_NODEKEY_PATH_PARTY_4"), - "party_name": "Alice", - "secret_name": "alice_salary", - "secret_value": 10000, -} - -# Bob and Charlie -CONFIG_N_PARTIES=[ - { - "userkey_file": os.getenv("NILLION_USERKEY_PATH_PARTY_2"), - "nodekey_file": os.getenv("NILLION_NODEKEY_PATH_PARTY_2"), - "party_name": "Bob", - "secret_name": "bob_salary", - "secret_value": 8000, - }, - { - "userkey_file": os.getenv("NILLION_USERKEY_PATH_PARTY_3"), - "nodekey_file": os.getenv("NILLION_NODEKEY_PATH_PARTY_3"), - "party_name": "Charlie", - "secret_name": "charlie_salary", - "secret_value": 12000, - }, -] \ No newline at end of file diff --git a/examples_and_tutorials/millionaires_problem_example/millionaires.nada.bin b/examples_and_tutorials/millionaires_problem_example/millionaires.nada.bin deleted file mode 100644 index b2ac6e1f..00000000 Binary files a/examples_and_tutorials/millionaires_problem_example/millionaires.nada.bin and /dev/null differ diff --git a/examples_and_tutorials/voting_tutorial/01_store_program_party1.py b/examples_and_tutorials/voting_tutorial/01_store_program_party1.py deleted file mode 100644 index afbe2cbb..00000000 --- a/examples_and_tutorials/voting_tutorial/01_store_program_party1.py +++ /dev/null @@ -1,115 +0,0 @@ -########################################################################################## - -# VOTING -- PART 1 - -########################################################################################## - - -import asyncio -import os -import sys -from dotenv import load_dotenv -from config import ( - CONFIG_PARTY_1 -) - -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() - -# Alice stores the voting program in the network -async def main(): - cluster_id = os.getenv("NILLION_CLUSTER_ID") - - while True: - - # Below, you can choose which voting program to use. In case you choose a voting program - # different from the robust version ('voting_dishonest_robust_6'), you can complete - # either 'digest_plurality_vote_honest_result()' or 'digest_plurality_vote_dishonest_with_abort_result()' - # functions to digest the result. - # - # Existing voting programs: - # - # program_name = "voting_honest_1" - # program_name = "voting_honest_2" - # program_name = "voting_dishonest_abort_5" - # program_name = "voting_dishonest_robust_6" - - print("Choose a program to test:") - print("1. voting_honest_1") - print("2. voting_honest_2") - print("3. voting_dishonest_abort_5") - print("4. voting_dishonest_robust_6") - - choice = input("Enter the number corresponding to your choice: ") - - programs = { - "1": "voting_honest_1", - "2": "voting_honest_2", - "3": "voting_dishonest_abort_5", - "4": "voting_dishonest_robust_6" - } - - if choice in programs: - program_name = programs[choice] - print("You have chosen:", program_name) - print(" _ _ _ _ _ _ _ ") - print("| | ___| |_( )___ __ _____ | |_ ___ __ _(_) |_| |__ ") - print("| | / _ \\ __|// __| \\ \\ / / _ \\| __/ _ \\ \\ \\ /\\ / / | __| '_ \\ ") - print("| |__| __/ |_ \\__ \\ \\ V / (_) | || __/ \\ V V /| | |_| | | |") - print("|_____\\___|\\__| |___/ \\_/_\\___/ \\__\\___| \\_/\\_/ |_|\\__|_| |_|") - print(" _____(_) | (_) ___ _____| | ") - print(" | _ | | | | |/ _ \\| _ | | ") - print(" | | | | | | | | (_) | | | |_| ") - print(" |_| |_|_|_|_|_|\\___/|_| |_(_) ") - print(" ") - break # Exit the loop if a valid choice is made - else: - print("Invalid choice. Please enter a number between 1 and 4.") - - ##################################### - # 1. Parties initialization # - ##################################### - - ############################# - # 1.1 Owner initialization # - ############################# - - client_alice = create_nillion_client( - getUserKeyFromFile(CONFIG_PARTY_1["userkey_file"]), getNodeKeyFromFile(CONFIG_PARTY_1["nodekey_file"]) - ) - - ##################################### - # 2. Storing program # - ##################################### - - program_mir_path = f"../../programs-compiled/{program_name}.nada.bin" - if os.path.exists(program_mir_path): - None - else: - raise FileNotFoundError(f"The file '{program_mir_path}' does not exist.\nMake sure you compiled the PyNada programs with './compile_programs.sh'.\nCheck README.md for more details.") - - # Store program in the Network - print(f"Storing program in the network: {program_name}") - action_id = await client_alice.store_program( - cluster_id, program_name, program_mir_path - ) - print("action_id is: ", action_id) - user_id_alice = client_alice.user_id - program_id = f"{user_id_alice}/{program_name}" - print("program_id is: ", program_id) - - ##################################### - # 3. Send program ID # - ##################################### - - # This requires its own mechanism in a real environment. - print(f"Alice stored {program_name} program at program_id: {program_id}") - print(f"Alice tells Bob and Charlie her user_id and the voting program_id") - - print("\n📋⬇️ Copy and run the following command to store Bob and Charlie's votes in the network") - print(f"\npython3 02_store_secret_party_n.py --user_id_1 {user_id_alice} --program_id {program_id}") - -asyncio.run(main()) \ No newline at end of file diff --git a/examples_and_tutorials/voting_tutorial/02_store_secret_party_n.py b/examples_and_tutorials/voting_tutorial/02_store_secret_party_n.py deleted file mode 100644 index 8a5ce0a5..00000000 --- a/examples_and_tutorials/voting_tutorial/02_store_secret_party_n.py +++ /dev/null @@ -1,118 +0,0 @@ -########################################################################################## - -# VOTING -- PART 2 - -########################################################################################## - -import argparse -import asyncio -import py_nillion_client as nillion -import os -import sys -from dotenv import load_dotenv -from config import ( - CONFIG_N_PARTIES -) - -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() - -parser = argparse.ArgumentParser( - description="Create a secret on the Nillion network with set read/retrieve permissions" -) -parser.add_argument( - "--user_id_1", - required=True, - type=str, - help="User ID of the user who will compute with the secret being stored", -) -parser.add_argument( - "--program_id", - required=True, - type=str, - help="Program ID of the voting program", -) - -args = parser.parse_args() - -# Bob and Charlie store their votes in the network -async def main(): - cluster_id = os.getenv("NILLION_CLUSTER_ID") - - # start a list of store ids to keep track of stored secrets - store_ids = [] - party_ids = [] - - for party_info in CONFIG_N_PARTIES: - - ##################################### - # 1. Parties initialization # - ##################################### - - ############################# - # 1.2 Voters initialization # - ############################# - client_n = create_nillion_client( - getUserKeyFromFile(party_info["userkey_file"]), - getNodeKeyFromFile(party_info["nodekey_file"]) - ) - party_id_n = client_n.party_id - user_id_n = client_n.user_id - party_name = party_info["party_name"] - party_role = party_info["party_role"] - secret_votes = party_info["secret_votes"] - - ##################################### - # 4. Storing votes # - ##################################### - # Create a secret for the current party - secret_votes = {key: nillion.SecretUnsignedInteger(value) for key, value in secret_votes.items()} - stored_secret = nillion.Secrets(secret_votes) - - ########################################### - # 4.1 Bind voter to party in the program # - ########################################### - # Create input bindings for the specific voting program by program id - secret_bindings = nillion.ProgramBindings(args.program_id) - - # Add the respective input party to say who will provide the input to the program - secret_bindings.add_input_party(party_role, party_id_n) - print(f"\n🔗 {party_name} sets bindings so that the secret can be input to a specific program (program_id: {args.program_id}) by a specific party (party_id: {party_id_n})") - - ########################################### - # 4.2 Set compute permissions to owner # - ########################################### - # Create permissions object with default permissions for the current user - permissions = nillion.Permissions.default_for_user(user_id_n) - - # Give compute permissions to Alice so she can use the secret in the specific voting program by program id - compute_permissions = { - args.user_id_1: {args.program_id}, - } - permissions.add_compute_permissions(compute_permissions) - print(f"\n👍 {party_name} gives compute permissions on their secret to Alice's user_id: {args.user_id_1}") - - # Store the permissioned secret - store_id = await client_n.store_secrets( - cluster_id, secret_bindings, stored_secret, permissions - ) - - store_ids.append(store_id) - party_ids.append(party_id_n) - - print(f"\n🎉 {party_name} stored its vote at store id: {store_id}") - - ##################################### - # 5. Send party IDs and store IDs # - ##################################### - - # This requires its own mechanism in a real environment. - party_ids_to_store_ids = ' '.join([f'{party_id}:{store_id}' for party_id, store_id in zip(party_ids, store_ids)]) - - print("\n📋⬇️ Copy and run the following command to run multi party computation using the secrets") - print(f"\npython3 03_multi_party_compute.py --program_id {args.program_id} --party_ids_to_store_ids {party_ids_to_store_ids}") - -asyncio.run(main()) diff --git a/examples_and_tutorials/voting_tutorial/03_multi_party_compute.py b/examples_and_tutorials/voting_tutorial/03_multi_party_compute.py deleted file mode 100644 index f357de86..00000000 --- a/examples_and_tutorials/voting_tutorial/03_multi_party_compute.py +++ /dev/null @@ -1,153 +0,0 @@ -########################################################################################## - -# VOTING -- PART 3 - -########################################################################################## - -import argparse -import asyncio -import py_nillion_client as nillion -import os -import sys -from dotenv import load_dotenv -from config import ( - CONFIG, - CONFIG_CANDIDATES, - CONFIG_PARTY_1, - CONFIG_N_PARTIES -) - -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 -from digest_result import digest_plurality_vote_honest_result, digest_plurality_vote_dishonest_with_abort_result, digest_plurality_vote_robust_result - - -load_dotenv() - -parser = argparse.ArgumentParser( - description="Create a secret on the Nillion network with set read/retrieve permissions" -) - -parser.add_argument( - "--program_id", - required=True, - type=str, - help="Program ID of the voting program", -) - -parser.add_argument( - "--party_ids_to_store_ids", - required=True, - nargs='+', - type=str, - help="List of partyid:storeid pairs of the secrets, with each pair separated by a space", -) - -args = parser.parse_args() - -async def main(): - cluster_id = os.getenv("NILLION_CLUSTER_ID") - - ##################################### - # 1. Parties initialization # - ##################################### - - ############################# - # 1.1 Owner initialization # - ############################# - # Alice initializes a client - client_alice = create_nillion_client( - getUserKeyFromFile(CONFIG_PARTY_1["userkey_file"]), - getNodeKeyFromFile(CONFIG_PARTY_1["nodekey_file"]) - ) - party_id_alice = client_alice.party_id - - ##################################### - # 4. Storing votes # - ##################################### - # Add any computation time secrets - # Alice provides her vote at compute time - - party_name_alice = CONFIG_PARTY_1["party_name"] - secret_votes = CONFIG_PARTY_1["secret_votes"] - secret_votes = {key: nillion.SecretUnsignedInteger(value) for key, value in secret_votes.items()} - compute_time_secrets = nillion.Secrets(secret_votes) - - print(f"\n🎉 {party_name_alice} provided her vote as a compute time secret.") - - ##################################### - # 6. Owner execute computation # - ##################################### - # Create computation bindings for voting program - compute_bindings = nillion.ProgramBindings(args.program_id) - - ################################################# - # 6.1 Bind voter to input party in the program # - ################################################# - # Add Alice as an input party - compute_bindings.add_input_party(CONFIG_PARTY_1["party_role"], party_id_alice) - - # Also add Bob and Charlie as input parties - party_ids_to_store_ids = {} - i=0 - for pair in args.party_ids_to_store_ids: - party_id, store_id = pair.split(':') - party_role = CONFIG_N_PARTIES[i]['party_role'] - compute_bindings.add_input_party(party_role, party_id) - party_ids_to_store_ids[party_id] = store_id - i=i+1 - - ################################################## - # 6.2 Bind owner to output party in the program # - ################################################## - # Add an output party (Alice). - # The output party reads the result of the blind computation - compute_bindings.add_output_party("OutParty", party_id_alice) - - print(f"Computing using program {args.program_id}") - - # Compute on the secret with all store ids. Note that there are no compute time secrets or public variables - compute_id = await client_alice.compute( - cluster_id, - compute_bindings, - list(party_ids_to_store_ids.values()), # Bob and Charlie's stored secrets - compute_time_secrets, # Alice's computation time secret - nillion.PublicVariables({}), - ) - - # Print compute result - print(f"The computation was sent to the network. compute_id: {compute_id}") - print("Waiting computation response...") - while True: - compute_event = await client_alice.next_compute_event() - if isinstance(compute_event, nillion.ComputeFinishedEvent): - print(f"✅ Compute complete for compute_id {compute_event.uuid}") - dict_result = compute_event.result.value - print(f"🖥️ The output result is {dict_result}\n") - - # Digest the result - program_name = os.path.basename(args.program_id) - nr_candidates = CONFIG["nr_candidates"] - nr_voters = CONFIG["nr_voters"] - voters = CONFIG_N_PARTIES - voters.insert(0, CONFIG_PARTY_1) - candidates = CONFIG_CANDIDATES - if program_name == "voting_dishonest_robust_6": - print("Let use digest the result given by the network:") - winner, total_votes, cheaters = digest_plurality_vote_robust_result(dict_result, nr_candidates, nr_voters) - winner_name = candidates[winner] - cheaters_names = [voters[voter]["party_name"] for voter in cheaters] - print(f"🏆 Winner is {winner_name}") - print(f"🔢 Total number of votes per candidate:") - print(f" Dave: {total_votes[0]}") - print(f" Emma: {total_votes[1]}") - print(f"🕵️‍♂️ List of cheaters: {cheaters_names}") - elif program_name in ["voting_honest_1", "voting_honest_2"]: - digest_plurality_vote_honest_result(dict_result, nr_candidates, nr_voters) - elif program_name == "voting_dishonest_abort_5": - digest_plurality_vote_dishonest_with_abort_result(dict_result, nr_candidates, nr_voters) - - break - -asyncio.run(main()) diff --git a/examples_and_tutorials/voting_tutorial/README.md b/examples_and_tutorials/voting_tutorial/README.md deleted file mode 100644 index f4ea3380..00000000 --- a/examples_and_tutorials/voting_tutorial/README.md +++ /dev/null @@ -1,87 +0,0 @@ -# Voting tutorial - -This tutorial aims to introduce you to the features of PyNada embedded DSL through the use of voting systems. We'll start simple and gradually increase the complexity of the programs to show you some interesting techniques. - -Additionally, we've provided two sets of Python scripts for you to explore the flow of a voting system running in `devnet-nillion`: - -1. Single File: One script emulates the behavior of all parties. -2. Multiple Files: Each party runs in its own separate script. - -In a real-world scenario, the roles should be distributed across multiple machines. - -## Tutorial - -Please go to the [tutorial page](tutorial.md) to dive deeper into PyNada. - -## Setup - -Before running the example, [follow the repo README](../../README.md) to install cli dependencies, complete environment setup, and activate your .venv environment. - -## Single file example - -In the single file version, the different roles are played by a single entity (`general_client`). The script has the following flow: - - 1. Parties initialization. - 2. Owner stores a program. - 3. (Real environment:) Owner sends the program ID to all voters. - 4. Voters store votes: - 4.1 Bind voter to party in the program - 4.2 Set compute permission to owner - 5. (Real environment:) Voters send their their party IDs and store IDs to the owner. - 6. Owner compute voting system using votes from voters. - -Also, we present a function [`digest_plurality_vote_robust_result()`](digest_result.py) that digests the result output by the [voting_dishonest_robust_6.py](../../programs/voting_dishonest_robust_6.py). - -### Run - -Run the command to start the example and follow the steps described: -```bash -python3 client_voting.py -``` - -## Multiple files example - -In this voting example, we maintain the same structure as in previous multiparty computation examples: - -- Alice assumes the role of the owner and acts as voter 0. -- Bob serves as voter 1. -- Charlie takes on the role of voter 2. - -Furthermore, the voters will cast their votes for two candidates: Dave and Emma. - -### Step 1 - -- Alice creates and stores program in the network -- Alice shares her user id and the resulting program id with Bob and Charlie - -Run the command to perform step 1. - -```bash -python3 01_store_secret_party1.py -``` - -### Step 2 - -- Bob and Charlie store their VOTES in the network as secrets. Each secret is stored with - - **bindings** to the voting program id. - - **permissions** so Alice (the owner) can compute using the secret. -- Bob and Charlie each share their `party_id` and secret `store_id` with Alice. - -The script will provide the command to perform step 2. - -```bash -python3 02_store_secret_party_n.py --user_id_1 {user_id} --program_id {program_id} -``` - -### Step 3 - -- Alice runs voting program computation using Bob and Charlie's secret votes. She provides her own vote as a secret at computation time. -- Alice receives the output result of the program. - -The script will provide the command to perform step 3. - -```bash -python3 03_multi_party_compute.py --program_id {program_id} --party_ids_to_store_ids {party_ids_to_store_ids} -``` - -Also, we present a function [`digest_plurality_vote_robust_result()`](digest_result.py) that digests the result output by the [voting_dishonest_robust_6.py](../../programs/voting_dishonest_robust_6.py). diff --git a/examples_and_tutorials/voting_tutorial/client_voting.py b/examples_and_tutorials/voting_tutorial/client_voting.py deleted file mode 100644 index 8ac02a7d..00000000 --- a/examples_and_tutorials/voting_tutorial/client_voting.py +++ /dev/null @@ -1,294 +0,0 @@ -########################################################################################## - -# SINGLE FILE VOTING - -########################################################################################## - - -import asyncio -import py_nillion_client as nillion -import os -import sys -from dotenv import load_dotenv -from config import ( - CONFIG -) - -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 -from digest_result import digest_plurality_vote_honest_result, digest_plurality_vote_dishonest_with_abort_result, digest_plurality_vote_robust_result - -load_dotenv() - -async def main(): - - cluster_id = os.getenv("NILLION_CLUSTER_ID") - - while True: - - # Below, you can choose which voting program to use. In case you choose a voting program - # different from the robust version ('voting_dishonest_robust_6'), you can complete - # either 'digest_plurality_vote_honest_result()' or 'digest_plurality_vote_dishonest_with_abort_result()' - # functions above to digest the result. - # - # Existing voting programs: - # - # program_name = "voting_honest_1" - # program_name = "voting_honest_2" - # program_name = "voting_dishonest_abort_5" - # program_name = "voting_dishonest_robust_6" - - print("Choose a program to test:") - print("1. voting_honest_1") - print("2. voting_honest_2") - print("3. voting_dishonest_abort_5") - print("4. voting_dishonest_robust_6") - - choice = input("Enter the number corresponding to your choice: ") - - programs = { - "1": "voting_honest_1", - "2": "voting_honest_2", - "3": "voting_dishonest_abort_5", - "4": "voting_dishonest_robust_6" - } - - if choice in programs: - program_name = programs[choice] - print("You have chosen:", program_name) - print(" _ _ _ _ _ _ _ ") - print("| | ___| |_( )___ __ _____ | |_ ___ __ _(_) |_| |__ ") - print("| | / _ \\ __|// __| \\ \\ / / _ \\| __/ _ \\ \\ \\ /\\ / / | __| '_ \\ ") - print("| |__| __/ |_ \\__ \\ \\ V / (_) | || __/ \\ V V /| | |_| | | |") - print("|_____\\___|\\__| |___/ \\_/_\\___/ \\__\\___| \\_/\\_/ |_|\\__|_| |_|") - print(" _____(_) | (_) ___ _____| | ") - print(" | _ | | | | |/ _ \\| _ | | ") - print(" | | | | | | | | (_) | | | |_| ") - print(" |_| |_|_|_|_|_|\\___/|_| |_(_) ") - print(" ") - break # Exit the loop if a valid choice is made - else: - print("Invalid choice. Please enter a number between 1 and 4.") - - # We initialize one party 'general_client' that represents all different parties. - # - # In a real environment, the clients must run in different machines with - # different node and user keys. - # - # The script has the following flow: - # 1. Parties initialization - # 2. Owner stores a program. - # 3. (Real environment:) Owner sends the program ID to all voters. - # 4. Voters store votes: - # 4.1 Bind voter to party in the program - # 4.2 Set compute permission to owner - # 5. (Real environment:) Voters send their their party IDs and store IDs to the owner. - # 6. Owner compute voting system using votes from voters. - - nr_candidates = CONFIG["nr_candidates"] - nr_voters = CONFIG["nr_voters"] - - ##################################### - # 1. Parties initialization # - ##################################### - - ###################################### - # 1.0 General client initialization # - ###################################### - userkey = getUserKeyFromFile(os.getenv("NILLION_USERKEY_PATH_PARTY_1")) - nodekey = getNodeKeyFromFile(os.getenv("NILLION_NODEKEY_PATH_PARTY_1")) - general_client = create_nillion_client(userkey, nodekey) - - # ############################# - # # 1.1 Owner initialization # - # ############################# - # # Path to the user and node key generated in previous step - # owner_nodekey_path = "/path/to/owner_node.key" - # owner_nodekey = nillion.NodeKey.from_file(owner_nodekey_path) - # owner_userkey_path = "/path/to/owner_user.key" - # owner_userkey = nillion.UserKey.from_file(owner_userkey_path) - # owner = nillion.NillionClient( - # owner_nodekey, - # bootnodes, - # nillion.ConnectionMode.relay(), - # owner_userkey, - # payments_config=payments_config - # ) - - # ############################# - # # 1.2 Voters initialization # - # ############################# - # # Initialize voters - # voters = [] - # for v in range(nr_voters): - # # Path to the user and node key generated in previous step - # voter_nodekey_path = "/path/to/voter"+str(v)+"_node.key" - # voter_nodekey = nillion.NodeKey.from_file(voter_nodekey_path) - # voter_userkey_path = "/path/to/voter"+str(v)+"_user.key" - # voter_userkey = nillion.UserKey.from_file(voter_userkey_path) - # voter = nillion.NillionClient( - # voter_nodekey, - # bootnodes, - # nillion.ConnectionMode.relay(), - # voter_userkey, - # payments_config=payments_config - # ) - # voters.append(voter) - - - ##################################### - # 2. Storing program # - ##################################### - - # Note: do not forget to compile the programs and store the corresponding .nada.bin file. - program_mir_path = f"../../programs-compiled/{program_name}.nada.bin" - if os.path.exists(program_mir_path): - None - else: - raise FileNotFoundError(f"The file '{program_mir_path}' does not exist.\nMake sure you compiled the PyNada programs with './compile_programs.sh'.\nCheck README.md for more details.") - - # Store program in the Network - print(f"Storing program in the network: {program_name}") - # action_id = await owner.store_program( - action_id = await general_client.store_program( - cluster_id, program_name, program_mir_path - ) - print("action_id is: ", action_id) - # program_id = owner.user_id + "/" + program_name - program_id = general_client.user_id + "/" + program_name - print("program_id is: ", program_id) - - ##################################### - # 3. Send program ID # - ##################################### - - # This requires its own mechanism in a real environment. - # In this demo, we just reuse the variable 'program_id'. - - ##################################### - # 4. Storing votes # - ##################################### - - # Each voter stores its vote. In this demo, we assume each voter has a file - # containing their votes for each candidate. E.g. voter 0 has a file 'inputs/v0_input.txt' - # with the following format: - # ============== - # 1 - # 2 - # ============== - # This means, voter 0 assigns 1 to candidate 0 and 2 to candidate 1. - - # Voters store the secrets - store_ids = [] - for v in range(nr_voters): - voter_v = general_client - # voter_v = voters[v] - print("voter_v: ", voter_v) - # structure v's votes - v_vote_dic = {} - v_input_file = "inputs/v"+str(v)+"_input.txt" - with open(v_input_file, 'r') as file: - c = 0 - for line in file: - # Remove leading and trailing whitespaces, then convert to integer - v_c_vote_value = int(line.strip()) - v_c_vote = nillion.SecretUnsignedInteger(v_c_vote_value) - v_vote_dic["v"+str(v)+"_c"+str(c)] = v_c_vote - c += 1 - v_to_be_store_secrets = nillion.Secrets(v_vote_dic) - - ########################################### - # 4.1 Bind voter to party in the program # - ########################################### - v_bindings = nillion.ProgramBindings(program_id) - v_bindings.add_input_party("Voter"+str(v), voter_v.party_id) - - ########################################### - # 4.2 Set compute permissions to owner # - ########################################### - # Give permissions to owner to compute with my vote - v_permissions = nillion.Permissions.default_for_user(voter_v.user_id) - v_permissions.add_compute_permissions({ - # owner.user_id: {program_id}, - general_client.user_id: {program_id}, - }) - - # Store in the network - print("Storing vote by voter "+str(v)+f": {v_to_be_store_secrets}") - store_id = await voter_v.store_secrets( - cluster_id, v_bindings, v_to_be_store_secrets, v_permissions - ) - store_ids.append(store_id) - print(f"Stored vote by voter "+str(v)+f" with store_id ={store_id}") - - ##################################### - # 5. Send party IDs and store IDs # - ##################################### - - # This requires its own mechanism in a real environment. - # In this demo, we just reuse the variable 'store_ids' and - # the voters' clients to extra their party IDs. - - ##################################### - # 6. Owner execute computation # - ##################################### - - ################################################# - # 6.1 Bind voter to input party in the program # - ################################################# - owner_bindings = nillion.ProgramBindings(program_id) - for v in range(nr_voters): - # owner_bindings_0.add_input_party("Voter"+str(v), voters[v].party_id) - owner_bindings.add_input_party("Voter"+str(v), general_client.party_id) - - ################################################## - # 6.2 Bind owner to output party in the program # - ################################################## - # Bind the "OutParty" party in the computation to the owner's client - # owner_bindings.add_output_party("OutParty", owner.party_id) - owner_bindings.add_output_party("OutParty", general_client.party_id) - - # No secret is directly passed to 'compute'. It only uses - # stored secrets. - to_be_used_in_computation = nillion.Secrets({}) - - print(f"Computing using program {program_id}") - print(f"Stored secrets: {store_ids}") - print(f"Provided secret: {to_be_used_in_computation}") - - # Owner can execute the computation - # compute_id = await owner.compute( - compute_id = await general_client.compute( - cluster_id, - owner_bindings, - store_ids, - to_be_used_in_computation, - nillion.PublicVariables({}), - ) - - print(f"Computation sent to the network, compute_id = {compute_id}") - print("Waiting computation response...") - while True: - compute_event = await general_client.next_compute_event() - if isinstance(compute_event, nillion.ComputeFinishedEvent): - print(f"✅ Compute complete for compute_id {compute_event.uuid}") - dict_result = compute_event.result.value - print(f"🖥️ The result is: {dict_result}\n") - - if program_name == "voting_dishonest_robust_6": - print("Let use digest the result given by the network:") - winner, total_votes, cheaters = digest_plurality_vote_robust_result(dict_result, nr_candidates, nr_voters) - print(f"🏆 Winner is candidate with ID={winner}") - print(f"🔢 Total number of votes per candidate: {total_votes}") - print(f"🕵️‍♂️ List of cheaters' IDs: {cheaters}") - elif program_name in ["voting_honest_1", "voting_honest_2"]: - digest_plurality_vote_honest_result(dict_result, nr_candidates, nr_voters) - elif program_name == "voting_dishonest_abort_5": - digest_plurality_vote_dishonest_with_abort_result(dict_result, nr_candidates, nr_voters) - - break - - - -asyncio.run(main()) \ No newline at end of file diff --git a/examples_and_tutorials/voting_tutorial/config.py b/examples_and_tutorials/voting_tutorial/config.py deleted file mode 100644 index e6cb7386..00000000 --- a/examples_and_tutorials/voting_tutorial/config.py +++ /dev/null @@ -1,50 +0,0 @@ -import os -from dotenv import load_dotenv -load_dotenv() - -CONFIG = { - "nr_candidates": 2, - "nr_voters": 3, -} - -CONFIG_CANDIDATES=[ - "Dave", - "Emma" -] - - -# Alice -CONFIG_PARTY_1={ - "userkey_file": os.getenv("NILLION_USERKEY_PATH_PARTY_1"), - "nodekey_file": os.getenv("NILLION_NODEKEY_PATH_PARTY_1"), - "party_name": "Alice", - "party_role": "Voter0", - "secret_votes": { - "v0_c0": 1, - "v0_c1": 2, - }, -} - -# Bob and Charlie -CONFIG_N_PARTIES=[ - { - "userkey_file": os.getenv("NILLION_USERKEY_PATH_PARTY_2"), - "nodekey_file": os.getenv("NILLION_NODEKEY_PATH_PARTY_2"), - "party_name": "Bob", - "party_role": "Voter1", - "secret_votes": { - "v1_c0": 1, - "v1_c1": 2, - }, - }, - { - "userkey_file": os.getenv("NILLION_USERKEY_PATH_PARTY_3"), - "nodekey_file": os.getenv("NILLION_NODEKEY_PATH_PARTY_3"), - "party_name": "Charlie", - "party_role": "Voter2", - "secret_votes": { - "v2_c0": 2, - "v2_c1": 1, - }, - }, -] \ No newline at end of file diff --git a/examples_and_tutorials/voting_tutorial/digest_result.py b/examples_and_tutorials/voting_tutorial/digest_result.py deleted file mode 100644 index 8f85cadb..00000000 --- a/examples_and_tutorials/voting_tutorial/digest_result.py +++ /dev/null @@ -1,70 +0,0 @@ -def digest_plurality_vote_honest_result(dict_result, nr_candidates, nr_voters): - - # TODO: complete there the code to digest the output of the honest version - - print("Update 'digest_plurality_vote_honest_result()' functions to digest the result.") - - return None - - -def digest_plurality_vote_dishonest_with_abort_result(dict_result, nr_candidates, nr_voters): - - # TODO: complete there the code to digest the output of the dishonest with abort version - - print("Update 'digest_plurality_vote_dishonest_with_abort_result()' functions to digest the result.") - - return None - - -def digest_plurality_vote_robust_result(dict_result, nr_candidates, nr_voters): - """ - Digests the robust result of a plurality voting process, identifying the winner, - the votes per candidate, and any potential cheaters. - - Parameters: - - dict_result (dict): A dictionary containing the robust result of the plurality - voting process. It should contain keys for final vote counts - for each candidate, checks for sum and product rules per voter, - and actions if cheating is detected. - - nr_candidates (int): The total number of candidates in the voting process. - - nr_voters (int): The total number of voters in the voting process. - - Returns: - - winner (int): The index of the winning candidate. - - votes_per_candidate (list): A list containing the number of votes received by - each candidate. - - cheaters (list): A list containing the IDs of any potential cheaters. - """ - - # check cheaters - set_of_cheaters = set() - for v in range(nr_voters): - if dict_result["check_sum_v"+str(v)] != nr_candidates + 1: - # add the voter id to set_of_cheaters - set_of_cheaters.add(v) - for c in range(nr_candidates): - if dict_result["check_prod_v"+str(v)+"_c"+str(c)] != 0: - # add the voter id to set_of_cheaters - set_of_cheaters.add(v) - - # collect final votes - votes_per_candidate = [] - for c in range(nr_candidates): - # read nr of votes for candidate 'c' - votes_per_candidate.append(dict_result["final_vote_count_c"+str(c)]) - # revert action of cheaters for all candidates - for cheater in set_of_cheaters: - # if sum rule was broken - if dict_result["check_sum_v"+str(cheater)] != nr_candidates + 1: - for c in range(nr_candidates): - votes_per_candidate[c] -= dict_result["if_sum_cheat_open_v"+str(cheater)+"_c"+str(c)] - # if product rule was broken - else: - for c in range(nr_candidates): - votes_per_candidate[c] -= dict_result["if_prod_cheat_open_v"+str(cheater)+"_c"+str(c)] - - # define winner - winner = max(range(len(votes_per_candidate)), key=lambda i: votes_per_candidate[i]) - - return winner, votes_per_candidate, list(set_of_cheaters) - diff --git a/examples_and_tutorials/voting_tutorial/image/architecture.png b/examples_and_tutorials/voting_tutorial/image/architecture.png deleted file mode 100644 index 033dd5c6..00000000 Binary files a/examples_and_tutorials/voting_tutorial/image/architecture.png and /dev/null differ diff --git a/examples_and_tutorials/voting_tutorial/inputs/v0_input.txt b/examples_and_tutorials/voting_tutorial/inputs/v0_input.txt deleted file mode 100644 index 7a754f41..00000000 --- a/examples_and_tutorials/voting_tutorial/inputs/v0_input.txt +++ /dev/null @@ -1,2 +0,0 @@ -1 -2 \ No newline at end of file diff --git a/examples_and_tutorials/voting_tutorial/inputs/v1_input.txt b/examples_and_tutorials/voting_tutorial/inputs/v1_input.txt deleted file mode 100644 index 7a754f41..00000000 --- a/examples_and_tutorials/voting_tutorial/inputs/v1_input.txt +++ /dev/null @@ -1,2 +0,0 @@ -1 -2 \ No newline at end of file diff --git a/examples_and_tutorials/voting_tutorial/inputs/v2_input.txt b/examples_and_tutorials/voting_tutorial/inputs/v2_input.txt deleted file mode 100644 index 6bf8d174..00000000 --- a/examples_and_tutorials/voting_tutorial/inputs/v2_input.txt +++ /dev/null @@ -1,2 +0,0 @@ -2 -1 \ No newline at end of file diff --git a/examples_and_tutorials/voting_tutorial/tutorial.md b/examples_and_tutorials/voting_tutorial/tutorial.md deleted file mode 100644 index 0fd3674b..00000000 --- a/examples_and_tutorials/voting_tutorial/tutorial.md +++ /dev/null @@ -1,909 +0,0 @@ -# Voting tutorial - -## Introduction - -This tutorial provides a generic template for creating several voting systems in `PyNada`. We will explore five types of voting systems: Plurality, Range, Veto, Approval and Borda. For a more detailed exposition on the voting systems, we can refer to [[DTY21]](https://www.sciencedirect.com/science/article/abs/pii/S0957417420310964). The tutorial is specifically focused on PyNada programs for these voting systems. If you want to run the voting programs in `nillion-devnet`, check the [README](README.md) file. - -Let us consider the following voting scenario: three voters and two potential candidates. Voters transmit their votes to the Nillion Network in a secret-shared form, which processes the votes for each candidate without having access to their underlying values. The result of the computation is then transmitted to the owner of the voting system, who is able to reconstruct the result and read the winner. - -
- Architecture -

Architecture example.

-
- -## Voting systems - -Let us first define the voting systems we are aiming at. Currently, PyNada does not support the input as zero. For this reason, we shift the input votes by one when compared to the description in [[DTY21]](https://www.sciencedirect.com/science/article/abs/pii/S0957417420310964). - -#### Plurality vote - -**Definition:** Each voter votes for their preferred candidate. They assign a score of 2 to their preferred candidate and a score of 1 to all others. - ->🤓 More specifically, we have the following constraints: ->1. Vote $v_i = (c_1, c_2)$ where $c_j \in \{1,2\}$ and $c_1 + c_2 = 3$, for all $i\in[2]$. ->2. Candidate winner: $\text{arg max} (C_1, C_2) = \text{arg max}(\sum v_i)$. - -#### Range vote - -**Definition:** Each voter gives a score, ranging from 1 to a public predetermined value L, to each candidate. They assign a higher score to their preferred candidates. - ->🤓 More specifically, we have the following constraints: ->1. Vote $v_i = (c_1, c_2)$ where $c_j \in \{1,\ldots, L\}$ for all $i\in[2]$. ->2. Candidate winner: $\text{arg max} (C_1, C_2) = \text{arg max} (\sum v_i)$. - -#### Approval vote - -**Definition:** Each voter approves up to K candidates. They assign a score of 2 to up to K candidates and a score of 1 to all others. - ->🤓 More specifically, we have the following constraints for a scenario where there are 4 candidates and $K=2$: ->1. Vote $v_i = (c_1, c_2, c_3, c_4)$ where $c_j\in\{1,2\}$ and $$c_1 + c_2 + c_3 + c_4 = 6\,\, (= K*2 + (4-K)).$$ ->2. $K$ candidates win: $\text{arg max} (C_1, C_2, C_3, C_4) = \text{arg max} (\sum v_i)$. - -#### Veto vote - -**Definition:** Each voter chooses his least preferred candidate. They assign a score of 1 to their least preferred candidate and a score of 2 to all others. - ->🤓 More specifically, we have the following constraints: ->1. Vote $v_i = (c_1, c_2)$ where $c_j \in \{1,2\}$ and $c_1 + c_2 = 3$, for all $i\in[2]$. Note that this system is only equivalent to the Plurality system in the two candidate setting. ->2. Candidate winner: $\text{arg max} (C_1, C_2) = \text{arg max} (\sum v_i)$. - -#### Borda vote - -**Definition:** Each voter ranks the candidates in preferred order. For M candidates, the most favourite candidate is assigned the score M and the least favourite is assigned 1. - ->🤓 More specifically, we have the following constraints when $M=4$: ->1. Vote $v_i = (c_1, c_2, c_3, c_4)$ where $c_j\in\{1, 2, 3, 4\}$ and $c_k ≠ c_l$ $\forall i≠j$. ->2. Candidate winner: $\text{arg max} (C_1, C_2) = \text{arg max} (\sum v_i)$. - -Each voting system have their constraints and can be applied to different scenarios. In the following sections, we will explore how these can be executed by a PyNada program. - -## Honest voters - -We start with a simple program as a warm-up. We assume voters are honest and provide sound input votes, which simplifies the requirements. - -Under this assumption, **all** the above voting systems can be reduced to **one** simple program where the votes are added for each candidate. The only difference between these voting systems is the input and output interpretation. - -### Hard-coded example - -Let us start with a simple program with 3 voters and 2 candidates. Recall that the following program can be applied to any type of voting system described in the previous [chapter](#voting-systems). - -Program: [voting_honest_1.py](../../programs/voting_honest_1.py) -```python -""" -PROGRAM 1 - -nr of voters: m = 3 -nr of candidates: n = 2 -""" -from nada_dsl import * - -def nada_main(): - - # 1. Parties initialization - voter0 = Party(name="Voter0") - voter1 = Party(name="Voter1") - voter2 = Party(name="Voter2") - outparty = Party(name="OutParty") - - # 2. Inputs initialization - ## Votes from voter 0 - v0_c0 = SecretUnsignedInteger(Input(name="v0_c0", party=voter0)) - v0_c1 = SecretUnsignedInteger(Input(name="v0_c1", party=voter0)) - ## Votes from voter 1 - v1_c0 = SecretUnsignedInteger(Input(name="v1_c0", party=voter1)) - v1_c1 = SecretUnsignedInteger(Input(name="v1_c1", party=voter1)) - ## Votes from voter 2 - v2_c0 = SecretUnsignedInteger(Input(name="v2_c0", party=voter2)) - v2_c1 = SecretUnsignedInteger(Input(name="v2_c1", party=voter2)) - - # 3. Computation - ## Add votes for candidate 0 - result_c0 = v0_c0 + v1_c0 + v2_c0 - ## Add votes for candidate 1 - result_c1 = v0_c1 + v1_c1 + v2_c1 - - # 4. Output - result_c0 = Output(result_c0, "final_vote_count_c0", outparty) - result_c1 = Output(result_c1, "final_vote_count_c1", outparty) - - return [result_c0, result_c1] -``` - -Let us breakdown the above program 1. All PyNada programs get compiled from the `nada_main()` function which can be divided in four major sections: - -1. **Parties initialization:** all parties involved in the computation should be initialized to allow them to either provide some input or to receive the output of a computation. In our voting example, we have 3 parties that will provide inputs and 1 party receiving the result. - -```python - voter0 = Party(name="Voter0") - voter1 = Party(name="Voter1") - voter2 = Party(name="Voter2") - outparty = Party(name="OutParty") -``` - -> 💡 The parties created within PyNada are just placeholders identified by a name tag. Real input parties are assigned to the placeholders only when a program is run in the network. - - -2. **Inputs initialization:** after specifying the parties, we can use the `Input` function to assign inputs to specific parties. Then, we need to identify the type of the input. In our example, we use `SecretUnsignedInteger`. - -```python - ## Votes from voter 0 - v0_c0 = SecretUnsignedInteger(Input(name="v0_c0", party=voter0)) - v0_c1 = SecretUnsignedInteger(Input(name="v0_c1", party=voter0)) - ## Votes from voter 1 - v1_c0 = SecretUnsignedInteger(Input(name="v1_c0", party=voter1)) - v1_c1 = SecretUnsignedInteger(Input(name="v1_c1", party=voter1)) - ## Votes from voter 2 - v2_c0 = SecretUnsignedInteger(Input(name="v2_c0", party=voter2)) - v2_c1 = SecretUnsignedInteger(Input(name="v2_c1", party=voter2)) -``` - -3. **Computation:** we can use all the initialized inputs and other elements to proceed with some computation. In our case, we simply add all the votes for candidate 0 and all the votes for candidate 1 separately. - -```python - ## Add votes for candidate 0 - result_c0 = v0_c0 + v1_c0 + v2_c0 - ## Add votes for candidate 1 - result_c1 = v0_c1 + v1_c1 + v2_c1 -``` - -4. **Output:** after the logic bulk we can output multiple values using the `Output` function and returning the list of output elements. - -```python - result_c0 = Output(result_c0, "final_vote_count_c0", outparty) - result_c1 = Output(result_c1, "final_vote_count_c1", outparty) - - return [result_c0, result_c1] -``` - -The above program 1 requires hard-coding every voter, vote and candidates’ result. In case we have a big number of voters and candidates, we need to use more handy methods. Next, we show two approaches on how to generalize the above program. - -### Compiled-time `for` loops - -We can make use of the Python run-time `for` loop to unroll the variables and create them for us. - - -> ⚠️ Please note that PyNada does not currently support run-time for loops. In PyNada, Python loops are executed solely at compile time. Consequently, any for loop will not run during program execution and will lack access to input and computed variables, as these are only defined at run-time. - -Below, program 2 shows how we could use compiled-time `for` loops to assist us creating all parties, input votes and building the computation section. - -Program: [voting_honest_2.py](../../programs/voting_honest_2.py) -```python -""" -PROGRAM 2 - -nr of voters: m = 5 -nr of candidates: n = 3 -""" -from nada_dsl import * - -def nada_main(): - - # 0. Compiled-time constants - nr_voters = 3 - nr_candidates = 2 - - # 1. Parties initialization - voters = [] - for i in range(nr_voters): - voters.append(Party(name="Voter" + str(i))) - outparty = Party(name="OutParty") - - # 2. Inputs initialization - votes_per_candidate = [] - for c in range(nr_candidates): - votes_per_candidate.append([]) - for v in range(nr_voters): - votes_per_candidate[c].append(SecretUnsignedInteger(Input(name="v" + str(v) + "_c" + str(c), party=voters[v]))) - - # 3. Computation - results = [] - for c in range(nr_candidates): - result = votes_per_candidate[c][0] - for v in range(1, nr_voters): - ## Add votes for candidate c - result += votes_per_candidate[c][v] - # 4. Output - results.append(Output(result, "final_vote_count_c" + str(c), outparty)) - - return results -``` -> 🔍 It is interesting to observe that the [MIR](https://docs.nillion.com/concepts#programs) representation of both programs (1 and 2) are exactly the same. You can inspect MIR files in the [`programs-compiled`](/nillion-python-starter/programs-compiled/) folder. - -Let us breakdown the above program 2. - -0. **Compiled-time constants:** we can initialize compiled-time constants and use them for compiled-time related tasks. In our example, both `nr_votes` and `nr_candidates` are used to specify the length of the compiled-time `for` loop unrolling. - -```python - nr_voters = 3 - nr_candidates = 2 -``` - -1. **Parties initialization:** the compiled-time `for` loop is used here to create a python list of parties. -```python - # 1. Parties initialization - voters = [] - for i in range(nr_voters): - voters.append(Party(name="Voter" + str(i))) - outparty = Party(name="OutParty") -``` -After running the above lines, the variable `voters` is a list containing the different voters: - -```python -voters = [Party(name="Voter0"), Party(name="Voter1"), Party(name="Voter0")] -``` -2. **Inputs initialization:** the compiled-time `for` loop is used here to create a python list of lists with all the votes for each candidate. - -```python - # 2. Inputs initialization - votes_per_candidate = [] - for c in range(nr_candidates): - votes_per_candidate.append([]) - for v in range(nr_voters): - votes_per_candidate[c].append(SecretUnsignedInteger(Input(name="v" + str(v) + "_c" + str(c), party=voters[v]))) -``` -After running the above lines, the variable `votes_per_candidate` should have the following structure: - -```python - votes_per_candidate = [[v0_c0, v1_c0, v2_c0], - [v0_c1, v1_c1, v2_c1]] - """ - where vi_cj = SecretUnsignedInteger(Input(name="vi_cj", party=voters[i]))] - """ -``` -3. **Computation:** at this point we use the compiled-time `for` loop to create the expression that represents the votes for each candidate. - -```python - # 3. Computation - results = [] - for c in range(nr_candidates): - result = votes_per_candidate[c][0] - for v in range(1, nr_voters): - ## Add votes for candidate c - result += votes_per_candidate[c][v] -``` - -Let use explicitly show how the `result` variable is updated. We consider the case of the first candidate `c = 0`: - -```python - # Initialization - result = votes_per_candidate[0][0] # = v0_c0 - # for v = 1 - result = votes_per_candidate[0][0] - + votes_per_candidate[0][1] - # for v = 2 - result = votes_per_candidate[0][0] - + votes_per_candidate[0][1] - + votes_per_candidate[0][2] -``` - -> ⚠️ Note that the variable `result` is **not updated** during the execution of the program. In fact, the bytecode generated does not have the concept of variables. This process only builds the intended expression for each candidate. - -4. **Output:** for each candidate, the `result` variable is appended into the list of final results `results`, which is the list to be returned. - -```python - # 3. Computation - results = [] - ... - # 4. Output - results.append(Output(result, "final_vote_count_c" + str(c), outparty)) - - return results -``` - -The `results` list is as follows: - -```python -results = [Output(votes_per_candidate[0][0] - + votes_per_candidate[0][1] - + votes_per_candidate[0][2], "final_vote_count_c0", outparty), - Output(votes_per_candidate[1][0] - + votes_per_candidate[1][1] - + votes_per_candidate[1][2], "final_vote_count_c1", outparty)] -``` - -### Functional programming - -PyNada provides a set of tools to code functional type programs. More specifically, it supports `zip`, `map` and `reduce` over two elements we haven’t come across before: - -- `Array`: a fixed-sized object that holds basic types (e.g. `SecretInteger`, `SecretUnsignedInteger`) -- Nada functions: functions to be executed during run-time. These receive a special annotator `@nada_fn`. - -```python -""" -PROGRAM 3 - -nr of voters: m = 5 -nr of candidates: n = 3 -""" -from nada_dsl import * - -def nada_main(): - - # 0. Compiled-time constants - nr_candidates = 3 - - # 1. Parties initialization - voter1 = Party(name="Voter1") - voter2 = Party(name="Voter2") - voter3 = Party(name="Voter3") - voter4 = Party(name="Voter4") - voter5 = Party(name="Voter5") - outparty = Party(name="OutParty") - - # 2. Inputs initialization - vote1 = Array(SecretInteger(Input(name="Voter1", party=voter1)), size=nr_candidates) - vote2 = Array(SecretInteger(Input(name="Voter2", party=voter2)), size=nr_candidates) - vote3 = Array(SecretInteger(Input(name="Voter3", party=voter3)), size=nr_candidates) - vote4 = Array(SecretInteger(Input(name="Voter4", party=voter4)), size=nr_candidates) - vote5 = Array(SecretInteger(Input(name="Voter5", party=voter5)), size=nr_candidates) - - # 3. Computation - @nada_fn - def add(a: SecretInteger, b: SecretInteger) -> SecretInteger: - return a + b - - final_count = vote1 - .zip(vote2) - .map(add) - .zip(vote3) - .map(add) - .zip(vote4) - .map(add) - .zip(vote5) - .map(add) - - # 4. Output - final_count = Output(final_count, "final_vote_count", outparty) - return [final_count] -``` - -## Dishonest voters - -In the real world, we cannot expect input parties to follow the rules of the voting system. The previous programs (1, 2 and 3) were simply adding the input elements provided by the input parties. This means voters could pump the classification of their preferred candidate. Considering the plurality voting system, in case voter 1 prefers candidate 1, it can input `v1_c0 = 1` and `v1_c1 = 10` instead of providing the (sound) value $2$. - -We can avoid this scenario by checking within the PyNada program that inputs are sound. At this point we can have two types of programs: - -- *Dishonest with abort:* in this scenario voters can provide invalid inputs and the program is able to identify dishonest input parties. However, it does not guarantee correctness of result if there are dishonest input parties. -- *Robust dishonest:* in this scenario voters can also provide invalid inputs and the program is also able to identify dishonest input parties. In this case, the program guarantee correctness of result if there are dishonest input parties. - -Below is a summary of the features for each variant. - - -| Features | Honest | Dishonest with abort | Robust dishonest | -| --- | --- | --- | --- | -| Allow invalid input | ❌ | ✅ | ✅ | -| Identify cheaters | ❌ | ✅ | ✅ | -| Correctness guaranteed | ❌ | ❌ | ✅ | - -Each type of voting system has different ways to identify input soundness. For the sake of brevity, we show two PyNada programs for the **plurality voting system**: dishonest with abort setting and robust dishonest setting. - -### Plurality | Dishonest with abort - -We recall that the plurality voting system requires the following constraint for $M$ candidates: - -- Vote $v_i = (c_1, \dots, c_M)$ where $c_j \in \{1,2\}$ and $c_1 + \ldots + c_M = M + 1$. - -This requires us to check two rules: - -1. Check sum: for every voter $v_i$, the addition of its votes must be exactly $M+1$ -2. Check product: every $c_j\in\{1,2\}$. We can check this by computing the expression $$(1-c_j)\times(2-c_j)$$and check if it is equal to zero. The above expression is equal to zero if and only if $c_j\in\{1,2\}$. - -For three voters and following the notation of Program 1, we code the checks as follows: - -1. Check sum: - -```python - # Check votes: - # 1. Sum - # Voter 0 - check_v0_sum = v0_c0 + v0_c1 - # Voter 1 - check_v1_sum = v1_c1 + v1_c2 - # Voter 2 - check_v2_sum = v2_c1 + v2_c2 -``` - -2. Check product: - -```python - # 2. Product - # Voter 0 - check_v0_c0_product = (UnsignedInteger(1) - v0_c0)*(UnsignedInteger(2) - v0_c0) - check_v0_c1_product = (UnsignedInteger(1) - v0_c1)*(UnsignedInteger(2) - v0_c1) - # Voter 1 - check_v1_c0_product = (UnsignedInteger(1) - v1_c0)*(UnsignedInteger(2) - v1_c0) - check_v1_c1_product = (UnsignedInteger(1) - v1_c1)*(UnsignedInteger(2) - v1_c1) - # Voter 2 - check_v2_c0_product = (UnsignedInteger(1) - v2_c0)*(UnsignedInteger(2) - v2_c0) - check_v2_c1_product = (UnsignedInteger(1) - v2_c1)*(UnsignedInteger(2) - v2_c1) -``` -Below, we put together the above two checks with our protocol: - -```python -""" -PROGRAM 4 - -nr of voters: m = 3 -nr of candidates: n = 2 -""" -from nada_dsl import * - -def nada_main(): - - # 1. Parties initialization - voter0 = Party(name="Voter0") - voter1 = Party(name="Voter1") - voter2 = Party(name="Voter2") - outparty = Party(name="OutParty") - - # 2. Inputs initialization - ## Votes from voter 0 - v0_c0 = SecretUnsignedInteger(Input(name="v0_c0", party=voter0)) - v0_c1 = SecretUnsignedInteger(Input(name="v0_c1", party=voter0)) - ## Votes from voter 1 - v1_c0 = SecretUnsignedInteger(Input(name="v1_c0", party=voter1)) - v1_c1 = SecretUnsignedInteger(Input(name="v1_c1", party=voter1)) - ## Votes from voter 2 - v2_c0 = SecretUnsignedInteger(Input(name="v2_c0", party=voter2)) - v2_c1 = SecretUnsignedInteger(Input(name="v2_c1", party=voter2)) - - # 3. Computation - ## Add votes for candidate 0 - result_c0 = v0_c0 + v1_c0 + v2_c0 - ## Add votes for candidate 1 - result_c1 = v0_c1 + v1_c1 + v2_c1 - - # Check votes: - # 1. Sum - # Voter 0 - check_v0_sum = v0_c0 + v0_c1 - # Voter 1 - check_v1_sum = v1_c1 + v1_c2 - # Voter 2 - check_v2_sum = v2_c1 + v2_c2 - # 2. Product - # Voter 0 - check_v0_c0_product = (UnsignedInteger(1) - v0_c0)*(UnsignedInteger(2) - v0_c0) - check_v0_c1_product = (UnsignedInteger(1) - v0_c1)*(UnsignedInteger(2) - v0_c1) - # Voter 1 - check_v1_c0_product = (UnsignedInteger(1) - v1_c0)*(UnsignedInteger(2) - v1_c0) - check_v1_c1_product = (UnsignedInteger(1) - v1_c1)*(UnsignedInteger(2) - v1_c1) - # Voter 2 - check_v2_c0_product = (UnsignedInteger(1) - v2_c0)*(UnsignedInteger(2) - v2_c0) - check_v2_c1_product = (UnsignedInteger(1) - v2_c1)*(UnsignedInteger(2) - v2_c1) - - # 4. Output - return [ - Output(result_c0, "final_vote_count_c0", outparty), - Output(result_c1, "final_vote_count_c1", outparty), - Output(check_v0_sum, "v0_sum", outparty), - Output(check_v1_sum, "v1_sum", outparty), - Output(check_v2_sum, "v2_sum", outparty), - Output(check_v0_c0_product, "v0_c0_prod", outparty), - Output(check_v0_c1_product, "v0_c1_prod", outparty), - Output(check_v1_c0_product, "v1_c0_prod", outparty), - Output(check_v1_c1_product, "v1_c1_prod", outparty), - Output(check_v2_c0_product, "v2_c0_prod", outparty), - Output(check_v2_c1_product, "v2_c1_prod", outparty), - ] -``` - -Similarly, we can utilize compiled-time `for` loops and **Python functions** to streamline the above code, particularly when scaling the number of voters and candidates. - ->💡 In addition to compiled-time `for` loops, leveraging **Python functions** can lead to more organized and cleaner code. Python functions are executed by the interpreter during compilation and cannot rely on runtime variables. In contrast, **Nada functions** rely on run-time variables and are only executed during run-time. This distinction becomes apparent when comparing with the example provided in program 3. - -Program: [voting_dishonest_abort_5.py](../../programs/voting_dishonest_abort_5.py) -```python -""" -PROGRAM 5 - -nr of voters: m = 3 -nr of candidates: n = 2 -""" -from nada_dsl import * - -def initialize_voters(nr_voters): - """ - Initializes the list of voters with unique identifiers. - - Args: - nr_voters (int): Number of voters. - - Returns: - list: List of Party objects representing each voter. - """ - voters = [] - for i in range(nr_voters): - voters.append(Party(name="Voter" + str(i))) - - return voters - -def inputs_initialization(nr_voters, nr_candidates, voters): - """ - Initializes the input for each candidate, collecting votes from each voter securely. - - Args: - nr_voters (int): Number of voters. - nr_candidates (int): Number of candidates. - - Returns: - list: List of lists containing SecretUnsignedInteger objects representing votes per candidate. - """ - votes_per_candidate = [] - for c in range(nr_candidates): - votes_per_candidate.append([]) - for v in range(nr_voters): - votes_per_candidate[c].append(SecretUnsignedInteger(Input(name="v" + str(v) + "_c" + str(c), party=voters[v]))) - - return votes_per_candidate - -def count_votes(nr_voters, nr_candidates, votes_per_candidate, outparty): - """ - Counts the votes for each candidate. - - Args: - nr_voters (int): Number of voters. - nr_candidates (int): Number of candidates. - votes_per_candidate (list): List of lists containing SecretUnsignedInteger objects representing votes per candidate. - - Returns: - list: List of Output objects representing the final vote count for each candidate. - """ - votes = [] - for c in range(nr_candidates): - result = votes_per_candidate[c][0] - for v in range(1, nr_voters): - result += votes_per_candidate[c][v] - votes.append(Output(result, "final_vote_count_c" + str(c), outparty)) - - return votes - -def fn_check_sum(nr_voters, nr_candidates, votes_per_candidate, outparty): - """ - Verifies the sum of votes for each voter to ensure correctness. - - Args: - nr_voters (int): Number of voters. - nr_candidates (int): Number of candidates. - votes_per_candidate (list): List of lists containing SecretUnsignedInteger objects representing votes per candidate. - - Returns: - list: List of Output objects representing the sum verification for each voter. - """ - check_sum = [] - for v in range(nr_voters): - check = votes_per_candidate[0][v] - for c in range(1, nr_candidates): - vote_v_c = votes_per_candidate[c][v] - check += vote_v_c - check_sum.append(Output(check, "check_sum_v" + str(v), outparty)) - - return check_sum - -def fn_check_prod(nr_voters, nr_candidates, votes_per_candidate, outparty): - """ - Verifies the product of vote values for each voter and candidate. - - Args: - nr_voters (int): Number of voters. - nr_candidates (int): Number of candidates. - votes_per_candidate (list): List of lists containing SecretUnsignedInteger objects representing votes per candidate. - - Returns: - list: List of Output objects representing the product verification for each voter and candidate. - """ - check_prod = [] - for v in range(nr_voters): - for c in range(nr_candidates): - vote_v_c = votes_per_candidate[c][v] - check_v_c_product = (UnsignedInteger(1) - vote_v_c)*(UnsignedInteger(2) - vote_v_c) - check_prod.append(Output(check_v_c_product, "check_prod_v" + str(v) + "_c" + str(c), outparty)) - - return check_prod - -def nada_main(): - - # 0. Compiled-time constants - nr_voters = 3 - nr_candidates = 2 - - # 1. Parties initialization - voters = initialize_voters(nr_voters) - outparty = Party(name="OutParty") - - # 2. Inputs initialization - votes_per_candidate = inputs_initialization(nr_voters, nr_candidates, voters) - - # 3. Computation - # Count the votes - votes = count_votes(nr_voters, nr_candidates, votes_per_candidate, outparty) - # Check input soundness - check_sum = fn_check_sum(nr_voters, nr_candidates, votes_per_candidate, outparty) - check_prod = fn_check_prod(nr_voters, nr_candidates, votes_per_candidate, outparty) - - # 4. Output - # Concatenate lists - results = votes + check_sum + check_prod - return results -``` - -### Plurality | Robust dishonest - -In the previous section, we were only able to identify cheating voters. Moreover, we were not able to recover the final result without the influence of cheating voters. In this section, we propose a way to achieve that with some features like `if_else` and comparisons `<`. We use unsigned integers (`UnsignedInteger` type) to make the program simpler. - -The 5 types of voting systems rely on **adding** all the votes. So, in case we have access to the votes of cheating voters, we can easily subtract them from the final result and retrieve the result without counting with cheaters’ votes. We can enforce that by adding the following piece of code to output the vote of a cheating voter: - -```python - # Voter 1 check sum - comp_v1_sum = check_v1_sum <= UnsignedInteger(3) # nr_candidates + 1 - - # Candidate 0 check product - comp_v1_c0_prod = check_v1_c0_product < UnsignedInteger(1) - - # Candidate 1 check product - comp_v1_c1_prod = check_v1_c1_product < UnsignedInteger(1) - - # Candidate 0 reveal if voter 1 cheated - if_sum_cheat_open_v1_c0 = comp_v1_sum.if_else(UnsignedInteger(0), v1_c0) - if_prod_cheat_open_v1_c0 = comp_v1_c0_prod.if_else( - comp_v1_c1_prod.if_else( - UnsignedInteger(0), - v1_c0 - ), - v1_c0 - ) - - # Candidate 1 reveal if voter 1 cheated - if_sum_cheat_open_v1_c1 = comp_v1_sum.if_else(UnsignedInteger(0), v1_c1) - if_prod_cheat_open_v1_c1 = comp_v1_c0_prod.if_else( - comp_v1_c1_prod.if_else( - UnsignedInteger(0), - v1_c1 - ), - v1_c1 - ) -``` - -In the code above, we start by checking if the value under `check_v1_sum` follows the required constraint (should be smaller than the number of candidates plus one). The result `comp_v1_sum` is a secret boolean that can then be used in conjunction with the `if_else` function. - -```python - # Voter 1 - comp_v1_sum = check_v1_sum <= UnsignedInteger(3) # nr_candidates + 1 -``` - -Based on the value of `comp_v1_sum`, we can either return the true value of a voter’s vote or a value `0` with no meaning. - -```python - # Candidate 0 reveal if voter 1 cheated - if_sum_cheat_open_v1_c0 = comp_v1_sum.if_else(UnsignedInteger(0), v1_c0) - ... - # Candidate 1 reveal if voter 1 cheated - if_sum_cheat_open_v1_c1 = comp_v1_sum.if_else(UnsignedInteger(0), v1_c1) - ... -``` - -Now, we check if the check product variables `check_v1_c0_product` and `check_v1_c1_product` are zero. Since we are using unsigned integers, than can be achieved by the `. < 1` operation. If that is the case, we use again `if_else` to return a value without meaning `0`, otherwise we return the true value of a voter’s vote. Note that we only reveal the votes in case any of the boolean values (`comp_v1_c0_prod` or `comp_v1_c1_prod`) is false. This is ensured by chaining `if_else` operations - -```python - # Candidate 0 reveal if voter 1 cheated - ... - if_prod_cheat_open_v1_c0 = comp_v1_c0_prod.if_else( - comp_v1_c1_prod.if_else( - UnsignedInteger(0), - v1_c0 - ), - v1_c0 - ) - # Candidate 1 reveal if voter 1 cheated - ... - if_prod_cheat_open_v1_c1 = comp_v1_c0_prod.if_else( - comp_v1_c1_prod.if_else( - UnsignedInteger(0), - v1_c1 - ), - v1_c1 - ) -``` - -Then, we output the cheating conditions `comp_v1_sum`, `comp_v1_c0_prod` and `comp_v1_c1_prod` along with the values `if_sum_cheat_open_v1_c0`, `if_sum_cheat_open_v1_c1`, `if_prod_cheat_open_v1_c0` and `if_prod_cheat_open_v1_c1` . - -Below, we put together the above two checks with our protocol. For readability purposes, we use a function `return_val_if_any_true(list_of_bool, val)` which returns the value `val` if at least on boolean in the list is false. - -Program: [voting_dishonest_robust_6.py](../../programs/voting_dishonest_robust_6.py) -```python -""" -PROGRAM 6 - -nr of voters: m = 3 -nr of candidates: n = 2 -""" -from nada_dsl import * - -def return_val_if_any_true(list_of_bool, val): - """ - Returns val if any boolean inside list_of_bool is false. - - Parameters: - - list_of_bool (list of bool): List of boolean values to be checked. - - val: Value to be returned if any boolean in the list is false. - - Returns: - - val: If any boolean in the list is false. - - 0: If none of the booleans in the list are false. - """ - - final_value = UnsignedInteger(0) - for bool in list_of_bool: - # Use if_else method to check if the current boolean is true, - # if true, return val, otherwise return the current final_value - final_value = bool.if_else(final_value, val) - - return final_value - -def initialize_voters(nr_voters): - """ - Initialize voters with unique identifiers. - - Parameters: - - nr_voters (int): Number of voters. - - Returns: - - voters (list): List of Party objects representing voters. - """ - voters = [] - for i in range(nr_voters): - voters.append(Party(name="Voter" + str(i))) - - return voters - -def inputs_initialization(nr_voters, nr_candidates, voters): - """ - Initialize inputs for votes per candidate. - - Parameters: - - nr_voters (int): Number of voters. - - nr_candidates (int): Number of candidates. - - Returns: - - votes_per_candidate (list): List of lists representing votes per candidate. - """ - votes_per_candidate = [] - for c in range(nr_candidates): - votes_per_candidate.append([]) - for v in range(nr_voters): - votes_per_candidate[c].append(SecretUnsignedInteger(Input(name="v" + str(v) + "_c" + str(c), party=voters[v]))) - - return votes_per_candidate - -def count_votes(nr_voters, nr_candidates, votes_per_candidate, outparty): - """ - Count votes for each candidate. - - Parameters: - - nr_voters (int): Number of voters. - - nr_candidates (int): Number of candidates. - - votes_per_candidate (list): List of lists representing votes per candidate. - - Returns: - - votes (list): List of Output objects representing vote counts for each candidate. - """ - votes = [] - for c in range(nr_candidates): - result = votes_per_candidate[c][0] - for v in range(1, nr_voters): - result += votes_per_candidate[c][v] - votes.append(Output(result, "final_vote_count_c" + str(c), outparty)) - - return votes - -def fn_check_sum(nr_voters, nr_candidates, votes_per_candidate, outparty): - """ - Check the sum of votes for each voter. - - Parameters: - - nr_voters (int): Number of voters. - - nr_candidates (int): Number of candidates. - - votes_per_candidate (list): List of lists representing votes per candidate. - - Returns: - - check_sum (list): List of Output objects representing the sum checks for each voter. - - if_sum_cheat_open (list): List of Output objects representing revealed votes of cheating voters. - """ - check_sum = [] - if_sum_cheat_open = [] - for v in range(nr_voters): - check = votes_per_candidate[0][v] - for c in range(1, nr_candidates): - vote_v_c = votes_per_candidate[c][v] - check += vote_v_c - check_sum.append(Output(check, "check_sum_v" + str(v), outparty)) - # Reveal if cheat - comp_v_sum = check <= UnsignedInteger(nr_candidates + 1) - for c in range(nr_candidates): - vote_v_c = votes_per_candidate[c][v] - if_sum_cheat_open_v_c = comp_v_sum.if_else(UnsignedInteger(0), vote_v_c) - if_sum_cheat_open.append(Output(if_sum_cheat_open_v_c, "if_sum_cheat_open_v" + str(v) + "_c" + str(c), outparty)) - - return check_sum, if_sum_cheat_open - -def fn_check_prod(nr_voters, nr_candidates, votes_per_candidate, outparty): - """ - Check the product of votes for each voter. - - Parameters: - - nr_voters (int): Number of voters. - - nr_candidates (int): Number of candidates. - - votes_per_candidate (list): List of lists representing votes per candidate. - - Returns: - - check_prod (list): List of Output objects representing the product checks for each voter. - - if_prod_cheat_open (list): List of Output objects representing revealed votes of cheating voters. - """ - check_prod = [] - if_prod_cheat_open = [] - all_comp_prod = [] - for v in range(nr_voters): - all_comp_v_prod = [] - for c in range(nr_candidates): - vote_v_c = votes_per_candidate[c][v] - check_v_c_product = (UnsignedInteger(1) - vote_v_c)*(UnsignedInteger(2) - vote_v_c) - check_prod.append(Output(check_v_c_product, "check_prod_v" + str(v) + "_c" + str(c), outparty)) - # collect all reveal conditions - comp_v_c_prod = check_v_c_product < UnsignedInteger(1) - all_comp_v_prod.append(comp_v_c_prod) - all_comp_prod.append(all_comp_v_prod) - # reveal all votes from voter v if - for v in range(nr_voters): - all_comp_v_prod = all_comp_prod[v] - for c in range(nr_candidates): - vote_v_c = votes_per_candidate[c][v] - if_prod_cheat_open_v_c = return_val_if_any_true(all_comp_v_prod, vote_v_c) - if_prod_cheat_open.append(Output(if_prod_cheat_open_v_c, "if_prod_cheat_open_v" + str(v) + "_c" + str(c), outparty)) - - return check_prod, if_prod_cheat_open - - -def nada_main(): - - # 0. Compiled-time constants - nr_voters = 3 - nr_candidates = 2 - - # 1. Parties initialization - voters = initialize_voters(nr_voters) - outparty = Party(name="OutParty") - - # 2. Inputs initialization - votes_per_candidate = inputs_initialization(nr_voters, nr_candidates, voters) - - # 3. Computation - # Count the votes - votes = count_votes(nr_voters, nr_candidates, votes_per_candidate, outparty) - # Check input soundness - check_sum, if_sum_cheat_open = fn_check_sum(nr_voters, nr_candidates, votes_per_candidate, outparty) - check_prod, if_prod_cheat_open = fn_check_prod(nr_voters, nr_candidates, votes_per_candidate, outparty) - - # 4. Output - # Concatenate lists - results = votes + check_sum + if_sum_cheat_open + check_prod + if_prod_cheat_open - return results -``` - -## Extra tip - -The above examples represent a scenario where the output is delivered to only one party. However, in some situations, we may want to output the same value to multiple parties. Usually, this can be done as follows: - -```python -return [Output(value, "value_output", outparty1), - Output(value, "value_output", outparty2)] -``` - -In case the number of output parties is big, we can make use of the Python interpreter to generate a list of outputs for us. Using the list of `voters` and `outparty` from program 6, in case we want to output the voting result to all voters we can do as follows: - -```python -[Output(result, "final_vote_count_c" + str(c), p) for p in voters + [outparty]] -``` - -# References - -[DTY21] [Fear not, vote truthfully: Secure Multiparty Computation of score based rules](https://www.sciencedirect.com/science/article/abs/pii/S0957417420310964), 2021 \ No newline at end of file diff --git a/helpers/nillion_client_helper.py b/helpers/nillion_client_helper.py deleted file mode 100644 index bf40cdab..00000000 --- a/helpers/nillion_client_helper.py +++ /dev/null @@ -1,20 +0,0 @@ -import os -import py_nillion_client as nillion - -def create_nillion_client(userkey, nodekey): - bootnodes = [os.getenv("NILLION_BOOTNODE_MULTIADDRESS")] - payments_config = nillion.PaymentsConfig( - os.getenv("NILLION_BLOCKCHAIN_RPC_ENDPOINT"), - os.getenv("NILLION_WALLET_PRIVATE_KEY"), - int(os.getenv("NILLION_CHAIN_ID")), - os.getenv("NILLION_PAYMENTS_SC_ADDRESS"), - os.getenv("NILLION_BLINDING_FACTORS_MANAGER_SC_ADDRESS"), - ) - - return nillion.NillionClient( - nodekey, - bootnodes, - nillion.ConnectionMode.relay(), - userkey, - payments_config, - ) \ No newline at end of file diff --git a/helpers/nillion_keypath_helper.py b/helpers/nillion_keypath_helper.py deleted file mode 100644 index 47e08120..00000000 --- a/helpers/nillion_keypath_helper.py +++ /dev/null @@ -1,8 +0,0 @@ -import os -import py_nillion_client as nillion - -def getUserKeyFromFile(userkey_filepath): - return nillion.UserKey.from_file(userkey_filepath) - -def getNodeKeyFromFile(nodekey_filepath): - return nillion.NodeKey.from_file(nodekey_filepath) diff --git a/programs/addition_simple.py b/programs/addition_simple.py deleted file mode 100644 index 8da7dcc3..00000000 --- a/programs/addition_simple.py +++ /dev/null @@ -1,11 +0,0 @@ -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)) - - new_int = my_int1 + my_int2 - - return [Output(new_int, "my_output", party1)] diff --git a/programs/addition_simple_multi_party.py b/programs/addition_simple_multi_party.py deleted file mode 100644 index 66cc0738..00000000 --- a/programs/addition_simple_multi_party.py +++ /dev/null @@ -1,12 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - party2 = Party(name="Party2") - my_int1 = SecretInteger(Input(name="my_int1", party=party1)) - my_int2 = SecretInteger(Input(name="my_int2", party=party2)) - - new_int = my_int1 + my_int2 - - return [Output(new_int, "my_output", party1)] diff --git a/programs/addition_simple_multi_party_3.py b/programs/addition_simple_multi_party_3.py deleted file mode 100644 index 932a214b..00000000 --- a/programs/addition_simple_multi_party_3.py +++ /dev/null @@ -1,13 +0,0 @@ -from nada_dsl import * - -def nada_main(): - party1 = Party(name="Party1") - party2 = Party(name="Party2") - party3 = Party(name="Party3") - my_int1 = SecretInteger(Input(name="my_int1", party=party1)) - my_int2 = SecretInteger(Input(name="my_int2", party=party2)) - my_int3 = SecretInteger(Input(name="my_int3", party=party3)) - - new_int = my_int1 + my_int2 + my_int3 - - return [Output(new_int, "my_output", party1)] diff --git a/programs/array_complex.py b/programs/array_complex.py deleted file mode 100644 index 25585e95..00000000 --- a/programs/array_complex.py +++ /dev/null @@ -1,22 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - party2 = Party(name="Party2") - my_int1 = SecretInteger(Input(name="my_int1", party=party1)) - my_array_1 = Array(SecretInteger(Input(name="my_array_1", party=party1)), size=10) - my_array_2 = Array(SecretInteger(Input(name="my_array_2", party=party2)), size=10) - - unzipped = unzip(my_array_2.zip(my_array_1)) - - @nada_fn - def add(a: SecretInteger, b: SecretInteger) -> SecretInteger: - return a + b - - new_array = my_array_1.zip(my_array_2).map(add).reduce(add, my_int1) - - out1 = Output(unzipped, "zip.unzip.tuple", party1) - out2 = Output(new_array, "zip.map.reduce.array", party1) - - return [out1, out2] diff --git a/programs/circuit_simple.py b/programs/circuit_simple.py deleted file mode 100644 index 1592ab9b..00000000 --- a/programs/circuit_simple.py +++ /dev/null @@ -1,15 +0,0 @@ -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)) - my_int3 = SecretInteger(Input(name="my_int3", party=party1)) - my_int4 = SecretInteger(Input(name="my_int4", party=party1)) - - new_int1 = my_int1 * my_int2 - new_int2 = my_int3 * my_int4 - new_int3 = new_int1 + new_int2 - - return [Output(new_int3, "my_output", party1)] diff --git a/programs/circuit_simple_2.py b/programs/circuit_simple_2.py deleted file mode 100644 index fd398127..00000000 --- a/programs/circuit_simple_2.py +++ /dev/null @@ -1,27 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - A = SecretInteger(Input(name="A", party=party1)) - B = SecretInteger(Input(name="B", party=party1)) - C = SecretInteger(Input(name="C", party=party1)) - D = SecretInteger(Input(name="D", party=party1)) - E = SecretInteger(Input(name="E", party=party1)) - F = SecretInteger(Input(name="F", party=party1)) - G = SecretInteger(Input(name="G", party=party1)) - H = SecretInteger(Input(name="H", party=party1)) - I = SecretInteger(Input(name="I", party=party1)) - - # A * B * C + D * E + F * G * H + I - - Mul0 = A * B - Mul1 = Mul0 * C - Mul2 = D * E - Add0 = Mul1 + Mul2 - Mul3 = F * G - Mul4 = Mul3 * H - Add1 = Add0 + Mul4 - Add2 = Add1 + I - - return [Output(Add2, "my_output", party1)] diff --git a/programs/circuit_simple_multi_party.py b/programs/circuit_simple_multi_party.py deleted file mode 100644 index 0fc41472..00000000 --- a/programs/circuit_simple_multi_party.py +++ /dev/null @@ -1,16 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - party2 = Party(name="Party2") - my_int1 = SecretInteger(Input(name="my_int1", party=party1)) - my_int2 = SecretInteger(Input(name="my_int2", party=party2)) - my_int3 = SecretInteger(Input(name="my_int3", party=party1)) - my_int4 = SecretInteger(Input(name="my_int4", party=party2)) - - new_int1 = my_int1 * my_int2 - new_int2 = my_int3 * my_int4 - new_int3 = new_int1 + new_int2 - - return [Output(new_int3, "my_output", party1)] diff --git a/programs/complex.py b/programs/complex.py deleted file mode 100644 index eff03864..00000000 --- a/programs/complex.py +++ /dev/null @@ -1,21 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - A = SecretInteger(Input(name="A", party=party1)) - B = SecretInteger(Input(name="B", party=party1)) - C = SecretInteger(Input(name="C", party=party1)) - D = SecretInteger(Input(name="D", party=party1)) - E = SecretInteger(Input(name="E", party=party1)) - F = SecretInteger(Input(name="F", party=party1)) - - TMP1 = A * B - PRODUCT1 = TMP1 * C - TMP2 = C * D - PRODUCT2 = TMP2 * E - PRODUCT3 = E * F - PARTIAL = PRODUCT1 + PRODUCT2 - FINAL = PARTIAL + PRODUCT3 - - return [Output(FINAL, "FINAL", party1)] diff --git a/programs/complex_operation_mix.py b/programs/complex_operation_mix.py deleted file mode 100644 index 66f93580..00000000 --- a/programs/complex_operation_mix.py +++ /dev/null @@ -1,21 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - party2 = Party(name="Party2") - A = SecretInteger(Input(name="A", party=party1)) - B = SecretInteger(Input(name="B", party=party2)) - C = SecretInteger(Input(name="C", party=party1)) - D = SecretInteger(Input(name="D", party=party2)) - E = SecretInteger(Input(name="E", party=party2)) - F = SecretInteger(Input(name="F", party=party2)) - G = SecretInteger(Input(name="G", party=party2)) - - result = ( - ((A * B) + C + D) * (E * (F + G)) - + (A * B * (C + D) + E) * F - + (A + (B * (C + (D * (E + F))))) - ) - - return [Output(result, "my_output", party1)] diff --git a/programs/correlation_coefficient.py b/programs/correlation_coefficient.py deleted file mode 100644 index 71c8c013..00000000 --- a/programs/correlation_coefficient.py +++ /dev/null @@ -1,73 +0,0 @@ -from nada_dsl import * - -def nada_main(): - """ - This program computes the correlation coefficient between two vectors. - - In this example we assume there are two parties P0 and P1. - P0 provides 10 points (x_i, y_i) and P1 provides 10 pointss (x_j, y_j). - """ - - nr_parties = 2 - p0_points = 10 - p1_points = 10 - precision = 5 - total_points = p0_points + p1_points - - # Create parties - parties = [] - for i in range(nr_parties): - parties.append(Party(name="Party" + str(i))) - outparty = Party(name="OutParty") - - - # Build x and y vector - xi_vector = [] - yi_vector = [] - # Party 0 input - for i in range(p0_points): - xi_vector.append(SecretInteger(Input(name="x" + str(i), party=parties[0]))) - yi_vector.append(SecretInteger(Input(name="y" + str(i), party=parties[0]))) - # Party 1 input - for i in range(p1_points): - xi_vector.append(SecretInteger(Input(name="x" + str(i + p0_points), party=parties[1]))) - yi_vector.append(SecretInteger(Input(name="y" + str(i + p0_points), party=parties[1]))) - - # Compute the following values: - # sum_x = Σ x_i - # sum_y = Σ y_i - # sum_xy = Σ x_i.y_i - # sum_xx = Σ x_i.x_i - # sum_yy = Σ y_i.y_i - sum_x = xi_vector[0] - sum_y = yi_vector[0] - sum_xy = xi_vector[0] * yi_vector[0] - sum_xx = xi_vector[0] * xi_vector[0] - sum_yy = yi_vector[0] * yi_vector[0] - for i in range(1, total_points): - sum_x += xi_vector[i] - sum_y += yi_vector[i] - sum_xy += xi_vector[i] * yi_vector[i] - sum_xx += xi_vector[i] * xi_vector[i] - sum_yy += yi_vector[i] * yi_vector[i] - - # Build the formula: - # (n * sum_xy - sum_x * sum_y) - # (r_xy)^2 = ------------------------------------------------------------- - # (n * sum_xx - sum_x * sum_x) * (n * sum_yy - sum_y * sum_y) - n = Integer(total_points) - n_times_sum_xy = n * sum_xy - sum_x_times_sum_y = sum_x * sum_y - ld = n * sum_xx - sum_x * sum_x - rd = n * sum_yy - sum_y * sum_y - - numerator = n_times_sum_xy - sum_x_times_sum_y - denominator = ld * rd - sq_numerator = numerator * numerator * Integer(10**precision) - r2 = sq_numerator / denominator - - # If sign == true, sign is positive - # else, sign is negative - sign = n_times_sum_xy > sum_x_times_sum_y - - return [(Output(r2, "correlation_coefficient_squared", outparty)), (Output(sign, "sign", outparty))] \ No newline at end of file diff --git a/programs/division_simple.py b/programs/division_simple.py deleted file mode 100644 index 2ec9da1c..00000000 --- a/programs/division_simple.py +++ /dev/null @@ -1,11 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - my_int1 = SecretInteger(Input(name="my_int1", party=party1)) - my_int3 = PublicInteger(Input(name="my_int3", party=party1)) - - new_int = my_int1 / my_int3 - - return [Output(new_int, "my_output", party1)] diff --git a/programs/greater_or_equal_than.py b/programs/greater_or_equal_than.py deleted file mode 100644 index a06ebf21..00000000 --- a/programs/greater_or_equal_than.py +++ /dev/null @@ -1,14 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - party2 = Party(name="Party2") - A = SecretInteger(Input(name="A", party=party1)) - B = SecretInteger(Input(name="B", party=party2)) - C = SecretInteger(Input(name="C", party=party1)) - D = SecretInteger(Input(name="D", party=party2)) - - result = A * B + C >= B * D - - return [Output(result, "my_output", party1)] diff --git a/programs/greater_than.py b/programs/greater_than.py deleted file mode 100644 index d82e13d1..00000000 --- a/programs/greater_than.py +++ /dev/null @@ -1,14 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - party2 = Party(name="Party2") - A = SecretInteger(Input(name="A", party=party1)) - B = SecretInteger(Input(name="B", party=party2)) - C = SecretInteger(Input(name="C", party=party1)) - D = SecretInteger(Input(name="D", party=party2)) - - result = A * B + C > B * D - - return [Output(result, "my_output", party1)] diff --git a/programs/import_file.py b/programs/import_file.py deleted file mode 100644 index db0ed4a5..00000000 --- a/programs/import_file.py +++ /dev/null @@ -1,13 +0,0 @@ -from nada_dsl import * -from lib.library import add - - -def nada_main(): - party1 = Party(name="Party1") - party2 = Party(name="Party2") - my_int1 = SecretInteger(Input(name="my_int1", party=party1)) - my_int2 = SecretInteger(Input(name="my_int2", party=party2)) - - new_int1 = add(my_int1, my_int2) - - return [Output(new_int1, "my_output", party1)] diff --git a/programs/incubation/array2dimensional.py b/programs/incubation/array2dimensional.py deleted file mode 100644 index e0158522..00000000 --- a/programs/incubation/array2dimensional.py +++ /dev/null @@ -1,27 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - @nada_fn - def add(a: SecretInteger, b: SecretInteger) -> SecretInteger: - return a + b - - @nada_fn - def matrix_addition( - a: Array[SecretInteger], b: Array[SecretInteger] - ) -> SecretInteger: - return a.zip(b).map(add).reduce(add) - - party1 = Party(name="Party1") - party2 = Party(name="Party2") - - my_array_1 = Array( - Array(SecretInteger(Input(name="my_array_1", party=party1)), size=10), size=10 - ) - my_array_2 = Array( - Array(SecretInteger(Input(name="my_array_2", party=party2)), size=10), size=10 - ) - - output = my_array_1.zip(my_array_2).map(matrix_addition) - - return [Output(output, "output", party1)] diff --git a/programs/input_2_dimensional_array.py b/programs/input_2_dimensional_array.py deleted file mode 100644 index ef364a52..00000000 --- a/programs/input_2_dimensional_array.py +++ /dev/null @@ -1,7 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - a = Array(Array(SecretInteger(Input(name="a", party=party1)), size=5), size=5) - return [Output(a, "my_output", party1)] diff --git a/programs/input_integer.py b/programs/input_integer.py deleted file mode 100644 index 917eb6af..00000000 --- a/programs/input_integer.py +++ /dev/null @@ -1,7 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - a = SecretInteger(Input(name="a", party=party1)) - return [Output(a, "my_output", party1)] diff --git a/programs/input_n_dimensional_array.py b/programs/input_n_dimensional_array.py deleted file mode 100644 index 7f81b972..00000000 --- a/programs/input_n_dimensional_array.py +++ /dev/null @@ -1,7 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - a = Array(Array(Array(SecretInteger(Input(name="a", party=party1)), size=5), size=5), size=5) - return [Output(a, "my_output", party1)] diff --git a/programs/input_single.py b/programs/input_single.py deleted file mode 100644 index 19f27340..00000000 --- a/programs/input_single.py +++ /dev/null @@ -1,10 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - my_int1 = SecretInteger(Input(name="my_int1", party=party1)) - x = my_int1 - y = x - z = y - return [Output(z, "my_output", party1)] diff --git a/programs/less_or_equal_than.py b/programs/less_or_equal_than.py deleted file mode 100644 index 88707fda..00000000 --- a/programs/less_or_equal_than.py +++ /dev/null @@ -1,14 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - party2 = Party(name="Party2") - A = SecretInteger(Input(name="A", party=party1)) - B = SecretInteger(Input(name="B", party=party2)) - C = SecretInteger(Input(name="C", party=party1)) - D = SecretInteger(Input(name="D", party=party2)) - - result = A * B + C <= B * D - - return [Output(result, "my_output", party1)] diff --git a/programs/less_than.py b/programs/less_than.py deleted file mode 100644 index 1ea64bfd..00000000 --- a/programs/less_than.py +++ /dev/null @@ -1,14 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - party2 = Party(name="Party2") - A = SecretInteger(Input(name="A", party=party1)) - B = SecretInteger(Input(name="B", party=party2)) - C = SecretInteger(Input(name="C", party=party1)) - D = SecretInteger(Input(name="D", party=party2)) - - result = A * B + C < B * D - - return [Output(result, "my_output", party1)] diff --git a/programs/lib/library.py b/programs/lib/library.py deleted file mode 100644 index a2ff07b5..00000000 --- a/programs/lib/library.py +++ /dev/null @@ -1,3 +0,0 @@ -def add(int1, int2): - result = int1 + int2 - return result diff --git a/programs/map_simple.py b/programs/map_simple.py deleted file mode 100644 index 9ae283da..00000000 --- a/programs/map_simple.py +++ /dev/null @@ -1,17 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - my_array_1 = Array(SecretInteger(Input(name="my_array_1", party=party1)), size=10) - my_int = SecretInteger(Input(name="my_int", party=party1)) - - @nada_fn - def inc(a: SecretInteger) -> SecretInteger: - return a + my_int - - new_array = my_array_1.map(inc) - - out = Output(new_array, "out", party1) - - return [out] diff --git a/programs/millionaires.py b/programs/millionaires.py deleted file mode 100644 index 5db81266..00000000 --- a/programs/millionaires.py +++ /dev/null @@ -1,21 +0,0 @@ -from nada_dsl import * - -def nada_main(): - alice = Party(name="Alice") # party 0 - bob = Party(name="Bob") # party 1 - charlie = Party(name="Charlie") # party 2 - - alice_salary = SecretInteger(Input(name="alice_salary", party=alice)) - bob_salary = SecretInteger(Input(name="bob_salary", party=bob)) - charlie_salary = SecretInteger(Input(name="charlie_salary", party=charlie)) - - largest_position = ( - (alice_salary > bob_salary).if_else( - (alice_salary > charlie_salary).if_else(Integer(0), Integer(2)), - (bob_salary > charlie_salary).if_else(Integer(1), Integer(2)) - ) - ) - - out = Output(largest_position, "largest_position", alice) - - return [out] \ No newline at end of file diff --git a/programs/modulo_simple.py b/programs/modulo_simple.py deleted file mode 100644 index 33ff5160..00000000 --- a/programs/modulo_simple.py +++ /dev/null @@ -1,11 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - my_int1 = SecretInteger(Input(name="my_int1", party=party1)) - public_my_int2 = PublicInteger(Input(name="public_my_int2", party=party1)) - - new_int = my_int1 % public_my_int2 - - return [Output(new_int, "my_output", party1)] diff --git a/programs/multiplication_simple.py b/programs/multiplication_simple.py deleted file mode 100644 index 3694ab00..00000000 --- a/programs/multiplication_simple.py +++ /dev/null @@ -1,11 +0,0 @@ -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)) - - new_int = my_int1 * my_int2 - - return [Output(new_int, "my_output", party1)] diff --git a/programs/multiplication_simple_multi_party.py b/programs/multiplication_simple_multi_party.py deleted file mode 100644 index 49d18aed..00000000 --- a/programs/multiplication_simple_multi_party.py +++ /dev/null @@ -1,12 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - party2 = Party(name="Party2") - my_int1 = SecretInteger(Input(name="my_int1", party=party1)) - my_int2 = SecretInteger(Input(name="my_int2", party=party2)) - - new_int = my_int1 * my_int2 - - return [Output(new_int, "my_output", party1)] diff --git a/programs/nada_fn_composition.py b/programs/nada_fn_composition.py deleted file mode 100644 index 50381077..00000000 --- a/programs/nada_fn_composition.py +++ /dev/null @@ -1,14 +0,0 @@ -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)) - - @nada_fn - def add_times(a: SecretInteger, b: SecretInteger) -> SecretInteger: - return a * (a + b) - - new_int = add_times(my_int1, my_int2) - return [Output(new_int, "my_output", party1)] diff --git a/programs/nada_fn_max.py b/programs/nada_fn_max.py deleted file mode 100644 index f312cb16..00000000 --- a/programs/nada_fn_max.py +++ /dev/null @@ -1,13 +0,0 @@ -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)) - - def max(a: SecretInteger, b: SecretInteger) -> SecretInteger: - return (a < b).if_else(b, a) - - new_int = max(my_int1, my_int2) - return [Output(new_int, "my_output", party1)] diff --git a/programs/nada_fn_simple.py b/programs/nada_fn_simple.py deleted file mode 100644 index cf53bbf8..00000000 --- a/programs/nada_fn_simple.py +++ /dev/null @@ -1,14 +0,0 @@ -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)) - - @nada_fn - def add(a: SecretInteger, b: SecretInteger) -> SecretInteger: - return a + b - - new_int = add(my_int1, my_int2) - return [Output(new_int, "my_output", party1)] diff --git a/programs/reduce_simple.py b/programs/reduce_simple.py deleted file mode 100644 index e0043b2e..00000000 --- a/programs/reduce_simple.py +++ /dev/null @@ -1,17 +0,0 @@ -from nada_dsl import Array, Party, SecretInteger, Output, Input, nada_fn - - -def nada_main(): - party1 = Party(name="Party1") - my_array_1 = Array(SecretInteger(Input(name="my_array_1", party=party1)), size=4) - my_int1 = SecretInteger(Input(name="my_int1", party=party1)) - - @nada_fn - def add(a: SecretInteger, b: SecretInteger) -> SecretInteger: - return a + b - - addition = my_array_1.reduce(add, my_int1) - - out2 = Output(addition, "reduce.addition", party1) - - return [out2] diff --git a/programs/reuse.py b/programs/reuse.py deleted file mode 100644 index 21e1bafc..00000000 --- a/programs/reuse.py +++ /dev/null @@ -1,14 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - A = SecretInteger(Input(name="A", party=party1)) - B = SecretInteger(Input(name="B", party=party1)) - C = SecretInteger(Input(name="C", party=party1)) - - TMP1 = A * B - TMP2 = A * C - R = TMP1 + TMP2 - - return [Output(R, "R", party1)] diff --git a/programs/reuse_flipped1.py b/programs/reuse_flipped1.py deleted file mode 100644 index 038f2540..00000000 --- a/programs/reuse_flipped1.py +++ /dev/null @@ -1,14 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - A = SecretInteger(Input(name="A", party=party1)) - B = SecretInteger(Input(name="B", party=party1)) - C = SecretInteger(Input(name="C", party=party1)) - - TMP1 = A * B - TMP2 = C * A - R = TMP1 + TMP2 - - return [Output(R, "R", party1)] diff --git a/programs/reuse_flipped2.py b/programs/reuse_flipped2.py deleted file mode 100644 index d42ea1b2..00000000 --- a/programs/reuse_flipped2.py +++ /dev/null @@ -1,14 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - A = SecretInteger(Input(name="A", party=party1)) - B = SecretInteger(Input(name="B", party=party1)) - C = SecretInteger(Input(name="C", party=party1)) - - TMP1 = A * B - TMP2 = C * B - R = TMP1 + TMP2 - - return [Output(R, "R", party1)] diff --git a/programs/reuse_simple_1.py b/programs/reuse_simple_1.py deleted file mode 100644 index 251b1132..00000000 --- a/programs/reuse_simple_1.py +++ /dev/null @@ -1,11 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - a = SecretInteger(Input(name="A", party=party1)) - b = SecretInteger(Input(name="B", party=party1)) - - result = a * b + a * b - - return [Output(result, "my_output", party1)] diff --git a/programs/reuse_simple_1_multi_party.py b/programs/reuse_simple_1_multi_party.py deleted file mode 100644 index a1aeb85f..00000000 --- a/programs/reuse_simple_1_multi_party.py +++ /dev/null @@ -1,12 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - party2 = Party(name="Party2") - a = SecretInteger(Input(name="my_int1", party=party1)) - b = SecretInteger(Input(name="my_int2", party=party2)) - - result = a * b + a * b - - return [Output(result, "my_output", party1)] diff --git a/programs/reuse_simple_2.py b/programs/reuse_simple_2.py deleted file mode 100644 index 753470d3..00000000 --- a/programs/reuse_simple_2.py +++ /dev/null @@ -1,11 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - a = SecretInteger(Input(name="A", party=party1)) - b = SecretInteger(Input(name="B", party=party1)) - - result = (a + b) * (a + b) - - return [Output(result, "my_output", party1)] diff --git a/programs/reuse_simple_sub.py b/programs/reuse_simple_sub.py deleted file mode 100644 index b2431ac7..00000000 --- a/programs/reuse_simple_sub.py +++ /dev/null @@ -1,11 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - a = SecretInteger(Input(name="A", party=party1)) - b = SecretInteger(Input(name="B", party=party1)) - - result = (a + b) * (a - b) - - return [Output(result, "my_output", party1)] diff --git a/programs/reuse_simple_sub_multi_party.py b/programs/reuse_simple_sub_multi_party.py deleted file mode 100644 index 03033436..00000000 --- a/programs/reuse_simple_sub_multi_party.py +++ /dev/null @@ -1,12 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - party2 = Party(name="Party2") - a = SecretInteger(Input(name="my_int1", party=party1)) - b = SecretInteger(Input(name="my_int2", party=party2)) - - result = (a + b) * (a - b) - - return [Output(result, "my_output", party1)] diff --git a/programs/simple.py b/programs/simple.py deleted file mode 100644 index 1cd684f1..00000000 --- a/programs/simple.py +++ /dev/null @@ -1,15 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - A = SecretInteger(Input(name="A", party=party1)) - B = SecretInteger(Input(name="B", party=party1)) - C = SecretInteger(Input(name="C", party=party1)) - D = SecretInteger(Input(name="D", party=party1)) - - TMP1 = A * B - TMP2 = C * D - O = TMP1 + TMP2 - - return [Output(O, "O", party1)] diff --git a/programs/simple_literals.py b/programs/simple_literals.py deleted file mode 100644 index 6b476d3f..00000000 --- a/programs/simple_literals.py +++ /dev/null @@ -1,16 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - A = SecretInteger(Input(name="A", party=party1)) - C = SecretInteger(Input(name="C", party=party1)) - - TMP0 = A * Integer(13) + Integer(13) # secret * literal + literal (checks literal re-use) - TMP1 = C * Integer(50) # secret * literal - TMP2 = Integer(13) + Integer(50) # literal + literal - TMP3 = Integer(13) * Integer(50) # literal * literal - - O = TMP0 + TMP1 + TMP2 + TMP3 - - return [Output(O, "O", party1)] diff --git a/programs/simple_public_variables.py b/programs/simple_public_variables.py deleted file mode 100644 index c499b839..00000000 --- a/programs/simple_public_variables.py +++ /dev/null @@ -1,18 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - A = SecretInteger(Input(name="A", party=party1)) - B = PublicInteger(Input(name="B", party=party1)) - C = SecretInteger(Input(name="C", party=party1)) - D = PublicInteger(Input(name="D", party=party1)) - - TMP0 = A + B # secret + public - TMP1 = C * D # secret * public - TMP2 = B + D # public + public - TMP3 = B * D # public * public - - O = TMP0 + TMP1 + TMP2 + TMP3 - - return [Output(O, "O", party1)] diff --git a/programs/simple_public_variables_only.py b/programs/simple_public_variables_only.py deleted file mode 100644 index 6a7e477e..00000000 --- a/programs/simple_public_variables_only.py +++ /dev/null @@ -1,11 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - A = PublicInteger(Input(name="A", party=party1)) - B = PublicInteger(Input(name="B", party=party1)) - - O = A * B - - return [Output(O, "O", party1)] diff --git a/programs/simple_sub.py b/programs/simple_sub.py deleted file mode 100644 index d15b4091..00000000 --- a/programs/simple_sub.py +++ /dev/null @@ -1,15 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - A = SecretInteger(Input(name="A", party=party1)) - B = SecretInteger(Input(name="B", party=party1)) - C = SecretInteger(Input(name="C", party=party1)) - D = SecretInteger(Input(name="D", party=party1)) - - TMP1 = A * B - TMP2 = C * D - O = TMP1 - TMP2 - - return [Output(O, "O", party1)] diff --git a/programs/single_addition.py b/programs/single_addition.py deleted file mode 100644 index a627ec53..00000000 --- a/programs/single_addition.py +++ /dev/null @@ -1,11 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - A = SecretInteger(Input(name="A", party=party1)) - B = SecretInteger(Input(name="B", party=party1)) - - R = A + B - - return [Output(R, "R", party1)] diff --git a/programs/single_addition_multi_party.py b/programs/single_addition_multi_party.py deleted file mode 100644 index 393e469d..00000000 --- a/programs/single_addition_multi_party.py +++ /dev/null @@ -1,12 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - party2 = Party(name="Party2") - A = SecretInteger(Input(name="A", party=party2)) - B = SecretInteger(Input(name="B", party=party2)) - - R = A + B - - return [Output(R, "R", party1)] diff --git a/programs/subtraction_simple.py b/programs/subtraction_simple.py deleted file mode 100644 index 75fc21d3..00000000 --- a/programs/subtraction_simple.py +++ /dev/null @@ -1,11 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - my_int1 = SecretInteger(Input(name="my_int1", party=party1)) # 32 - my_int2 = SecretInteger(Input(name="my_int2", party=party1)) # 81 - - new_int = my_int2 - my_int1 - - return [Output(new_int, "my_output", party1)] diff --git a/programs/subtraction_simple_multi_party.py b/programs/subtraction_simple_multi_party.py deleted file mode 100644 index 93c635b3..00000000 --- a/programs/subtraction_simple_multi_party.py +++ /dev/null @@ -1,12 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - party2 = Party(name="Party2") - my_int1 = SecretInteger(Input(name="my_int1", party=party1)) # 32 - my_int2 = SecretInteger(Input(name="my_int2", party=party2)) # 81 - - new_int = my_int2 - my_int1 - - return [Output(new_int, "my_output", party1)] diff --git a/programs/subtraction_simple_neg.py b/programs/subtraction_simple_neg.py deleted file mode 100644 index 59f79b51..00000000 --- a/programs/subtraction_simple_neg.py +++ /dev/null @@ -1,11 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - my_int4 = SecretInteger(Input(name="my_int4", party=party1)) # 102 - my_int2 = SecretInteger(Input(name="my_int2", party=party1)) # 81 - - new_int = my_int2 - my_int4 # 81 - 102 = -21 - - return [Output(new_int, "my_output", party1)] diff --git a/programs/subtraction_simple_neg_multi_party.py b/programs/subtraction_simple_neg_multi_party.py deleted file mode 100644 index 77f76262..00000000 --- a/programs/subtraction_simple_neg_multi_party.py +++ /dev/null @@ -1,12 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - party2 = Party(name="Party2") - my_int1 = SecretInteger(Input(name="my_int1", party=party1)) # 102 - my_int2 = SecretInteger(Input(name="my_int2", party=party2)) # 81 - - new_int = my_int2 - my_int1 # 81 - 102 = -21 - - return [Output(new_int, "my_output", party1)] diff --git a/programs/tuple_new_unzip.py b/programs/tuple_new_unzip.py deleted file mode 100644 index 7ea01fec..00000000 --- a/programs/tuple_new_unzip.py +++ /dev/null @@ -1,16 +0,0 @@ -from nada_dsl import * - - -def nada_main(): - party1 = Party(name="Party1") - a = SecretInteger(Input(name="a", party=party1)) - b = SecretInteger(Input(name="b", party=party1)) - c = SecretInteger(Input(name="c", party=party1)) - d = SecretInteger(Input(name="d", party=party1)) - - my_tuple_1 = Tuple.new(a, b) - my_tuple_2 = Tuple.new(c, d) - my_array_1 = Array.new(my_tuple_1, my_tuple_2) - result = unzip(my_array_1) - - return [Output(result, "my_output", party1)] diff --git a/programs/voting_dishonest_abort_5.py b/programs/voting_dishonest_abort_5.py deleted file mode 100644 index 93c05da3..00000000 --- a/programs/voting_dishonest_abort_5.py +++ /dev/null @@ -1,131 +0,0 @@ -""" -PROGRAM 5 - -nr of voters: m = 3 -nr of candidates: n = 2 -""" -from nada_dsl import * - -def initialize_voters(nr_voters): - """ - Initializes the list of voters with unique identifiers. - - Args: - nr_voters (int): Number of voters. - - Returns: - list: List of Party objects representing each voter. - """ - voters = [] - for i in range(nr_voters): - voters.append(Party(name="Voter" + str(i))) - - return voters - -def inputs_initialization(nr_voters, nr_candidates, voters): - """ - Initializes the input for each candidate, collecting votes from each voter securely. - - Args: - nr_voters (int): Number of voters. - nr_candidates (int): Number of candidates. - - Returns: - list: List of lists containing SecretUnsignedInteger objects representing votes per candidate. - """ - votes_per_candidate = [] - for c in range(nr_candidates): - votes_per_candidate.append([]) - for v in range(nr_voters): - votes_per_candidate[c].append(SecretUnsignedInteger(Input(name="v" + str(v) + "_c" + str(c), party=voters[v]))) - - return votes_per_candidate - -def count_votes(nr_voters, nr_candidates, votes_per_candidate, outparty): - """ - Counts the votes for each candidate. - - Args: - nr_voters (int): Number of voters. - nr_candidates (int): Number of candidates. - votes_per_candidate (list): List of lists containing SecretUnsignedInteger objects representing votes per candidate. - - Returns: - list: List of Output objects representing the final vote count for each candidate. - """ - votes = [] - for c in range(nr_candidates): - result = votes_per_candidate[c][0] - for v in range(1, nr_voters): - result += votes_per_candidate[c][v] - votes.append(Output(result, "final_vote_count_c" + str(c), outparty)) - - return votes - -def fn_check_sum(nr_voters, nr_candidates, votes_per_candidate, outparty): - """ - Verifies the sum of votes for each voter to ensure correctness. - - Args: - nr_voters (int): Number of voters. - nr_candidates (int): Number of candidates. - votes_per_candidate (list): List of lists containing SecretUnsignedInteger objects representing votes per candidate. - - Returns: - list: List of Output objects representing the sum verification for each voter. - """ - check_sum = [] - for v in range(nr_voters): - check = votes_per_candidate[0][v] - for c in range(1, nr_candidates): - vote_v_c = votes_per_candidate[c][v] - check += vote_v_c - check_sum.append(Output(check, "check_sum_v" + str(v), outparty)) - - return check_sum - -def fn_check_prod(nr_voters, nr_candidates, votes_per_candidate, outparty): - """ - Verifies the product of vote values for each voter and candidate. - - Args: - nr_voters (int): Number of voters. - nr_candidates (int): Number of candidates. - votes_per_candidate (list): List of lists containing SecretUnsignedInteger objects representing votes per candidate. - - Returns: - list: List of Output objects representing the product verification for each voter and candidate. - """ - check_prod = [] - for v in range(nr_voters): - for c in range(nr_candidates): - vote_v_c = votes_per_candidate[c][v] - check_v_c_product = (UnsignedInteger(1) - vote_v_c)*(UnsignedInteger(2) - vote_v_c) - check_prod.append(Output(check_v_c_product, "check_prod_v" + str(v) + "_c" + str(c), outparty)) - - return check_prod - -def nada_main(): - - # 0. Compiled-time constants - nr_voters = 3 - nr_candidates = 2 - - # 1. Parties initialization - voters = initialize_voters(nr_voters) - outparty = Party(name="OutParty") - - # 2. Inputs initialization - votes_per_candidate = inputs_initialization(nr_voters, nr_candidates, voters) - - # 3. Computation - # Count the votes - votes = count_votes(nr_voters, nr_candidates, votes_per_candidate, outparty) - # Check input soundness - check_sum = fn_check_sum(nr_voters, nr_candidates, votes_per_candidate, outparty) - check_prod = fn_check_prod(nr_voters, nr_candidates, votes_per_candidate, outparty) - - # 4. Output - # Concatenate lists - results = votes + check_sum + check_prod - return results \ No newline at end of file diff --git a/programs/voting_dishonest_robust_6.py b/programs/voting_dishonest_robust_6.py deleted file mode 100644 index 90fc7784..00000000 --- a/programs/voting_dishonest_robust_6.py +++ /dev/null @@ -1,176 +0,0 @@ -""" -PROGRAM 6 - -nr of voters: m = 3 -nr of candidates: n = 2 -""" -from nada_dsl import * - -def return_val_if_any_false(list_of_bool, val): - """ - Returns val if any boolean inside list_of_bool is false. - - Parameters: - - list_of_bool (list of bool): List of boolean values to be checked. - - val: Value to be returned if any boolean in the list is false. - - Returns: - - val: If any boolean in the list is false. - - 0: If none of the booleans in the list are false. - """ - - final_value = UnsignedInteger(0) - for bool in list_of_bool: - # Use if_else method to check if the current boolean is true, - # if true, return vfinal_valueal, otherwise return the current val - final_value = bool.if_else(final_value, val) - - return final_value - -def initialize_voters(nr_voters): - """ - Initialize voters with unique identifiers. - - Parameters: - - nr_voters (int): Number of voters. - - Returns: - - voters (list): List of Party objects representing voters. - """ - voters = [] - for i in range(nr_voters): - voters.append(Party(name="Voter" + str(i))) - - return voters - -def inputs_initialization(nr_voters, nr_candidates, voters): - """ - Initialize inputs for votes per candidate. - - Parameters: - - nr_voters (int): Number of voters. - - nr_candidates (int): Number of candidates. - - Returns: - - votes_per_candidate (list): List of lists representing votes per candidate. - """ - votes_per_candidate = [] - for c in range(nr_candidates): - votes_per_candidate.append([]) - for v in range(nr_voters): - votes_per_candidate[c].append(SecretUnsignedInteger(Input(name="v" + str(v) + "_c" + str(c), party=voters[v]))) - - return votes_per_candidate - -def count_votes(nr_voters, nr_candidates, votes_per_candidate, outparty): - """ - Count votes for each candidate. - - Parameters: - - nr_voters (int): Number of voters. - - nr_candidates (int): Number of candidates. - - votes_per_candidate (list): List of lists representing votes per candidate. - - Returns: - - votes (list): List of Output objects representing vote counts for each candidate. - """ - votes = [] - for c in range(nr_candidates): - result = votes_per_candidate[c][0] - for v in range(1, nr_voters): - result += votes_per_candidate[c][v] - votes.append(Output(result, "final_vote_count_c" + str(c), outparty)) - - return votes - -def fn_check_sum(nr_voters, nr_candidates, votes_per_candidate, outparty): - """ - Check the sum of votes for each voter. - - Parameters: - - nr_voters (int): Number of voters. - - nr_candidates (int): Number of candidates. - - votes_per_candidate (list): List of lists representing votes per candidate. - - Returns: - - check_sum (list): List of Output objects representing the sum checks for each voter. - - if_sum_cheat_open (list): List of Output objects representing revealed votes of cheating voters. - """ - check_sum = [] - if_sum_cheat_open = [] - for v in range(nr_voters): - check = votes_per_candidate[0][v] - for c in range(1, nr_candidates): - vote_v_c = votes_per_candidate[c][v] - check += vote_v_c - check_sum.append(Output(check, "check_sum_v" + str(v), outparty)) - # Reveal if cheat - comp_v_sum = check <= UnsignedInteger(nr_candidates + 1) - for c in range(nr_candidates): - vote_v_c = votes_per_candidate[c][v] - if_sum_cheat_open_v_c = comp_v_sum.if_else(UnsignedInteger(0), vote_v_c) - if_sum_cheat_open.append(Output(if_sum_cheat_open_v_c, "if_sum_cheat_open_v" + str(v) + "_c" + str(c), outparty)) - - return check_sum, if_sum_cheat_open - -def fn_check_prod(nr_voters, nr_candidates, votes_per_candidate, outparty): - """ - Check the product of votes for each voter. - - Parameters: - - nr_voters (int): Number of voters. - - nr_candidates (int): Number of candidates. - - votes_per_candidate (list): List of lists representing votes per candidate. - - Returns: - - check_prod (list): List of Output objects representing the product checks for each voter. - - if_prod_cheat_open (list): List of Output objects representing revealed votes of cheating voters. - """ - check_prod = [] - if_prod_cheat_open = [] - all_comp_prod = [] - for v in range(nr_voters): - all_comp_v_prod = [] - for c in range(nr_candidates): - vote_v_c = votes_per_candidate[c][v] - check_v_c_product = (UnsignedInteger(1) - vote_v_c)*(UnsignedInteger(2) - vote_v_c) - check_prod.append(Output(check_v_c_product, "check_prod_v" + str(v) + "_c" + str(c), outparty)) - # collect all reveal conditions - comp_v_c_prod = check_v_c_product < UnsignedInteger(1) - all_comp_v_prod.append(comp_v_c_prod) - all_comp_prod.append(all_comp_v_prod) - # reveal all votes from voter v if - for v in range(nr_voters): - all_comp_v_prod = all_comp_prod[v] - for c in range(nr_candidates): - vote_v_c = votes_per_candidate[c][v] - if_prod_cheat_open_v_c = return_val_if_any_false(all_comp_v_prod, vote_v_c) - if_prod_cheat_open.append(Output(if_prod_cheat_open_v_c, "if_prod_cheat_open_v" + str(v) + "_c" + str(c), outparty)) - - return check_prod, if_prod_cheat_open - - -def nada_main(): - - # 0. Compiled-time constants - nr_voters = 3 - nr_candidates = 2 - - # 1. Parties initialization - voters = initialize_voters(nr_voters) - outparty = Party(name="OutParty") - - # 2. Inputs initialization - votes_per_candidate = inputs_initialization(nr_voters, nr_candidates, voters) - - # 3. Computation - # Count the votes - votes = count_votes(nr_voters, nr_candidates, votes_per_candidate, outparty) - # Check input soundness - check_sum, if_sum_cheat_open = fn_check_sum(nr_voters, nr_candidates, votes_per_candidate, outparty) - check_prod, if_prod_cheat_open = fn_check_prod(nr_voters, nr_candidates, votes_per_candidate, outparty) - - # 4. Output - # Concatenate lists - results = votes + check_sum + if_sum_cheat_open + check_prod + if_prod_cheat_open - return results \ No newline at end of file diff --git a/programs/voting_honest_1.py b/programs/voting_honest_1.py deleted file mode 100644 index fe3a9df2..00000000 --- a/programs/voting_honest_1.py +++ /dev/null @@ -1,38 +0,0 @@ -""" -PROGRAM 1 - -nr of voters: m = 3 -nr of candidates: n = 2 -""" -from nada_dsl import * - -def nada_main(): - - # 1. Parties initialization - voter0 = Party(name="Voter0") - voter1 = Party(name="Voter1") - voter2 = Party(name="Voter2") - outparty = Party(name="OutParty") - - # 2. Inputs initialization - ## Votes from voter 0 - v0_c0 = SecretUnsignedInteger(Input(name="v0_c0", party=voter0)) - v0_c1 = SecretUnsignedInteger(Input(name="v0_c1", party=voter0)) - ## Votes from voter 1 - v1_c0 = SecretUnsignedInteger(Input(name="v1_c0", party=voter1)) - v1_c1 = SecretUnsignedInteger(Input(name="v1_c1", party=voter1)) - ## Votes from voter 2 - v2_c0 = SecretUnsignedInteger(Input(name="v2_c0", party=voter2)) - v2_c1 = SecretUnsignedInteger(Input(name="v2_c1", party=voter2)) - - # 3. Computation - ## Add votes for candidate 0 - result_c0 = v0_c0 + v1_c0 + v2_c0 - ## Add votes for candidate 1 - result_c1 = v0_c1 + v1_c1 + v2_c1 - - # 4. Output - result_c0 = Output(result_c0, "final_vote_count_c0", outparty) - result_c1 = Output(result_c1, "final_vote_count_c1", outparty) - - return [result_c0, result_c1] \ No newline at end of file diff --git a/programs/voting_honest_2.py b/programs/voting_honest_2.py deleted file mode 100644 index 617007be..00000000 --- a/programs/voting_honest_2.py +++ /dev/null @@ -1,38 +0,0 @@ -""" -PROGRAM 2 - -nr of voters: m = 5 -nr of candidates: n = 3 -""" -from nada_dsl import * - -def nada_main(): - - # 0. Compiled-time constants - nr_voters = 3 - nr_candidates = 2 - - # 1. Parties initialization - voters = [] - for i in range(nr_voters): - voters.append(Party(name="Voter" + str(i))) - outparty = Party(name="OutParty") - - # 2. Inputs initialization - votes_per_candidate = [] - for c in range(nr_candidates): - votes_per_candidate.append([]) - for v in range(nr_voters): - votes_per_candidate[c].append(SecretUnsignedInteger(Input(name="v" + str(v) + "_c" + str(c), party=voters[v]))) - - # 3. Computation - results = [] - for c in range(nr_candidates): - result = votes_per_candidate[c][0] - for v in range(1, nr_voters): - ## Add votes for candidate c - result += votes_per_candidate[c][v] - # 4. Output - results.append(Output(result, "final_vote_count_c" + str(c), outparty)) - - return results \ No newline at end of file diff --git a/pyvenv.cfg b/pyvenv.cfg deleted file mode 100644 index 8177f1e4..00000000 --- a/pyvenv.cfg +++ /dev/null @@ -1,8 +0,0 @@ -home = /opt/homebrew/opt/python@3.11/bin -implementation = CPython -version_info = 3.11.6.final.0 -virtualenv = 20.24.5 -include-system-site-packages = false -base-prefix = /opt/homebrew/opt/python@3.11/Frameworks/Python.framework/Versions/3.11 -base-exec-prefix = /opt/homebrew/opt/python@3.11/Frameworks/Python.framework/Versions/3.11 -base-executable = /opt/homebrew/opt/python@3.11/bin/python3.11 diff --git a/helpers/__init__.py b/quickstart/client_code/run_my_first_program.py similarity index 100% rename from helpers/__init__.py rename to quickstart/client_code/run_my_first_program.py diff --git a/quickstart_complete/client_code/secret_addition_complete.py b/quickstart_complete/client_code/secret_addition_complete.py new file mode 100644 index 00000000..a8105c1f --- /dev/null +++ b/quickstart_complete/client_code/secret_addition_complete.py @@ -0,0 +1,116 @@ +""" +In this example, we: +1. import packages we'll be using +2. connect to the local nillion-devnet +3. store the secret addition program +4. store a secret to be used in the computation +5. compute the secret addition program with the stored secret and another computation time secret +6. return the computation result +""" + +import asyncio +import os + +from nillion_client import ( + InputPartyBinding, + Network, + NilChainPayer, + NilChainPrivateKey, + OutputPartyBinding, + Permissions, + SecretInteger, + VmClient, + PrivateKey, +) +from dotenv import load_dotenv + +home = os.getenv("HOME") +load_dotenv(f"{home}/.config/nillion/nillion-devnet.env") + + +async def main(): + # 2. Initial setup/config, then initialize the NillionClient against nillion-devnet + # Use the devnet configuration generated by `nillion-devnet` + network = Network.from_config("devnet") + + # Create payments config and set up Nillion wallet with a private key to pay for operations + nilchain_key: str = os.getenv("NILLION_NILCHAIN_PRIVATE_KEY_0") # type: ignore + payer = NilChainPayer( + network, + wallet_private_key=NilChainPrivateKey(bytes.fromhex(nilchain_key)), + gas_limit=10000000, + ) + + # Use a random key to identify ourselves + signing_key = PrivateKey() + client = await VmClient.create(signing_key, network, payer) + party_name = "Party1" + program_name = "secret_addition_complete" + program_mir_path = f"../nada_quickstart_programs/target/{program_name}.nada.bin" + + # Adding funds to the client balance so the upcoming operations can be paid for + funds_amount = 3000000 + print(f"💰 Adding some funds to the client balance: {funds_amount} uNIL") + await client.add_funds(funds_amount) + + # 3. Store the program + print("-----STORE PROGRAM") + + # Store program + program_mir = open(program_mir_path, "rb").read() + program_id = await client.store_program(program_name, program_mir).invoke() + + # Print details about stored program + print(f"Stored program_id: {program_id}") + + # 4. Create the 1st secret, add permissions and store it in the network + print("-----STORE SECRETS") + + # Create a secret + values = { + "my_int1": SecretInteger(500), + } + + # Create a permissions object to attach to the stored secret + permissions = Permissions.defaults_for_user(client.user_id).allow_compute( + client.user_id, program_id + ) + + # Store a secret + values_id = await client.store_values( + values, ttl_days=5, permissions=permissions + ).invoke() + + # 5. Create compute bindings to set input and output parties, add a computation time secret and pay for & run the computation + print("-----COMPUTE") + + # Bind the parties in the computation to the client to set input and output parties + input_bindings = [InputPartyBinding(party_name, client.user_id)] + output_bindings = [OutputPartyBinding(party_name, [client.user_id])] + + # Create a computation time secret to use + compute_time_values = {"my_int2": SecretInteger(10)} + + # Compute, passing in the compute time values as well as the previously uploaded value. + print(f"Invoking computation using program {program_id} and values id {values_id}") + compute_id = await client.compute( + program_id, + input_bindings, + output_bindings, + values=compute_time_values, + value_ids=[values_id], + ).invoke() + + # 6. Return the computation result + print(f"The computation was sent to the network. compute_id: {compute_id}") + result = await client.retrieve_compute_results(compute_id).invoke() + print(f"✅ Compute complete for compute_id {compute_id}") + print(f"🖥️ The result is {result}") + balance = await client.balance() + print(f"💰 Final client balance: {balance.balance} Credits") + client.close() + return result + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/quickstart_complete/nada_quickstart_programs/nada-project.toml b/quickstart_complete/nada_quickstart_programs/nada-project.toml new file mode 100644 index 00000000..f66f59d9 --- /dev/null +++ b/quickstart_complete/nada_quickstart_programs/nada-project.toml @@ -0,0 +1,7 @@ +name = "quick_start_programs" +version = "0.1.0" +authors = [""] + +[[programs]] +path = "src/secret_addition_complete.py" +prime_size = 128 diff --git a/programs/tiny_secret_addition_complete.py b/quickstart_complete/nada_quickstart_programs/src/secret_addition_complete.py similarity index 100% rename from programs/tiny_secret_addition_complete.py rename to quickstart_complete/nada_quickstart_programs/src/secret_addition_complete.py diff --git a/quickstart_complete/nada_quickstart_programs/target/secret_addition_complete.nada.bin b/quickstart_complete/nada_quickstart_programs/target/secret_addition_complete.nada.bin new file mode 100644 index 00000000..60e60e39 Binary files /dev/null and b/quickstart_complete/nada_quickstart_programs/target/secret_addition_complete.nada.bin differ diff --git a/quickstart_complete/nada_quickstart_programs/tests/secret_addition_complete.yaml b/quickstart_complete/nada_quickstart_programs/tests/secret_addition_complete.yaml new file mode 100644 index 00000000..2e66372a --- /dev/null +++ b/quickstart_complete/nada_quickstart_programs/tests/secret_addition_complete.yaml @@ -0,0 +1,7 @@ +--- +program: secret_addition_complete +inputs: + my_int1: 3 + my_int2: 3 +expected_outputs: + my_output: 6 diff --git a/requirements.txt b/requirements.txt index b5f8c0a2..de3d849f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ +nillion-client==0.2.0 +nada-dsl==0.8.0 python-dotenv==1.0.0 -nada-dsl>=0.2.1 -py-nillion-client>=0.2.1 -pytest-asyncio>=0.23.6 diff --git a/store_program.sh b/store_program.sh deleted file mode 100755 index 7500d933..00000000 --- a/store_program.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -# This script stores a compiled program as NILLION_USERKEY_PATH_PARTY_1 -SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}" 2>/dev/null)" && pwd -P)" - -set -a # Automatically export all variables -source "$SCRIPT_PATH/.env" -set +a # Stop automatically exporting - -# Check for 1 argument (RELATIVE_PROGRAM_PATH) -if [ "$#" -ne 1 ]; then - echo "Usage: $0 " - exit 1 -fi - -RELATIVE_PROGRAM_PATH="$SCRIPT_PATH/$1" -# Extract the program name from the path (e.g., addition_simple from programs-compiled/addition_simple.nada.bin) -PROGRAM_NAME=$(basename "$RELATIVE_PROGRAM_PATH" .nada.bin) - -# Execute the command -nillion \ - --user-key-path $NILLION_USERKEY_PATH_PARTY_1 \ - --node-key-path $NILLION_NODEKEY_PATH_PARTY_1 \ - -b $NILLION_BOOTNODE_MULTIADDRESS \ - --payments-private-key $NILLION_WALLET_PRIVATE_KEY \ - --payments-chain-id $NILLION_CHAIN_ID \ - --payments-rpc-endpoint $NILLION_BLOCKCHAIN_RPC_ENDPOINT \ - --payments-sc-address $NILLION_PAYMENTS_SC_ADDRESS \ - --blinding-factors-manager-sc-address $NILLION_BLINDING_FACTORS_MANAGER_SC_ADDRESS \ - store-program \ - --cluster-id $NILLION_CLUSTER_ID \ - $RELATIVE_PROGRAM_PATH \ - $PROGRAM_NAME diff --git a/testing/Dockerfile b/testing/Dockerfile deleted file mode 100644 index 468192ad..00000000 --- a/testing/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -FROM mcr.microsoft.com/devcontainers/python - -USER vscode - -# Add Foundry and Nilup to Path -ENV PATH="$PATH:/home/vscode/.foundry/bin:/home/vscode/.nilup/bin:/home/vscode/.local/bin" - -# Install Nilup -RUN curl https://nilup.nilogy.xyz/install.sh | bash && \ - nilup install latest && \ - nilup use latest && \ - nilup init - -# Install foundry -RUN curl -L https://foundry.paradigm.xyz | bash && \ - bash -c "foundryup" - -WORKDIR /home/vscode -COPY --chown=vscode:vscode . . diff --git a/testing/docker_main.sh b/testing/docker_main.sh deleted file mode 100644 index 3fef4690..00000000 --- a/testing/docker_main.sh +++ /dev/null @@ -1,41 +0,0 @@ -run_pytest_in_dir() { - local dir_name="$1" - - # Check if the directory exists - if [ ! -d "$dir_name" ]; then - echo "Directory does not exist: $dir_name" - return 1 - fi - - cd "$dir_name" || return - - for file in *.py; do - # Check if the pattern matches at least one file - if [ -e "$file" ]; then - echo -e "$file\n\n" - pytest "$file" - echo "[DONE] $file" - else - echo "No Python files found in $dir_name." - break - fi - done - - cd .. # Return to the original directory -} - -cp .env.sample .env && \ -python3 -m pip install -r requirements.txt && \ -bash ./create_venv.sh && \ -./bootstrap-local-environment.sh && \ -echo "Waiting 60 seconds for preprocessing elements" && sleep 60 && \ -sh compile_programs.sh && \ - -run_pytest_in_dir client_single_party_compute && \ -cd examples_and_tutorials && \ -run_pytest_in_dir core_concept_multi_party_compute && \ -run_pytest_in_dir core_concept_permissions && \ -run_pytest_in_dir core_concept_single_party_compute && \ -run_pytest_in_dir core_concept_permissions && \ -run_pytest_in_dir core_concept_store_and_retrieve_secrets && \ -run_pytest_in_dir millionaires_problem_example diff --git a/testing/run_tests.sh b/testing/run_tests.sh deleted file mode 100755 index 9d7dffb5..00000000 --- a/testing/run_tests.sh +++ /dev/null @@ -1,5 +0,0 @@ -# This file presents how to automatically run the tests - -cd .. -docker build -t nillion-python-starter -f testing/Dockerfile . -docker run -it --rm nillion-python-starter bash /home/vscode/testing/docker_main.sh diff --git a/utils.sh b/utils.sh deleted file mode 100644 index d12d880c..00000000 --- a/utils.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env bash -function __echo_red_bold { - echo -e "\033[1;31m${1}\033[0m" -} - -function __nillion_pip_install() { - WHLPATH=$(find "$NILLION_WHL_ROOT" -iname "$1" -type f -print | head -n1) - echo $WHLPATH - pip install --force-reinstall "${WHLPATH:?could not find $1 in $NILLION_WHL_ROOT}" -} - -function install_py_nillion_client() { - __nillion_pip_install "${NILLION_PYCLIENT_WHL_FILE_NAME}" -} - -function install_nada_dsl() { - __nillion_pip_install "nada_dsl-*-any.whl" -} - -function discover_sdk_bin_path() { - - BINPATH=$(find "$NILLION_SDK_ROOT" -name "$1" -type f -print | head -n1) - - if ! command -v "$BINPATH" > /dev/null; then - echo "${1} was not discovered. Check NILLION_SDK_ROOT $NILLION_SDK_ROOT $BINPATH" 1>&2 - exit 1 - fi - echo "$BINPATH" -} - -function check_for_sdk_root() { - echo "NILLION_SDK_ROOT is set to: '$NILLION_SDK_ROOT'" - if [ -z "$NILLION_SDK_ROOT" -a ! -d "$NILLION_SDK_ROOT" ]; then - echo "Error: NILLION_SDK_ROOT is not set to a directory" - exit 1 - fi -}