Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Colab starter changes #31

Merged
merged 6 commits into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 152 additions & 0 deletions bootstrap-local-environment-colab.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
#!/usr/bin/env bash

# set number of node and user keys to be created
num_node_keys=5
num_user_keys=5

# set env file to update
ENV_TO_UPDATE=".env"

NILLION_DEVNET="/root/.nilup/sdks/latest/nillion-devnet"
NILLION_CLI="/root/.nilup/sdks/latest/nillion"
NILLION_CLI_COMMAND_USER_KEYGEN="user-key-gen"
NILLION_CLI_COMMAND_NODE_KEYGEN="node-key-gen"

# kill any other nillion-devnet processes
pkill -9 -f $NILLION_DEVNET

for var in NILLION_DEVNET NILLION_CLI; do
printf "ℹ️ found bin %-18s -> [${!var:?Failed to discover $var}]\n" "$var"
done

OUTFILE=$(mktemp);
PIDFILE=$(mktemp);

"$NILLION_DEVNET" >"$OUTFILE" & echo $! >"$PIDFILE";
echo "--------------------"
echo "Updating your ${ENV_TO_UPDATE} files with nillion-devnet environment info... This may take a minute."
echo "--------------------"
time_limit=160
while true; do
# Use 'wait' to check if the log file contains the string
if grep "cluster is running, bootnode is at" "$OUTFILE"; then
break
fi

# If the time limit has been reached, print an error message and exit
if [[ $SECONDS -ge $time_limit ]]; then
echo "Timeout reached while waiting for cluster to be ready in '$OUTFILE'" >&2
exit 1
fi
sleep 5
done

echo "ℹ️ Cluster has been STARTED (see $OUTFILE)"
cat "$OUTFILE"

# grep cluster info from nillion-devnet
CLUSTER_ID=$(grep "cluster id is" "$OUTFILE" | awk '{print $4}');
WEBSOCKET=$(grep "websocket:" "$OUTFILE" | awk '{print $2}');
BOOT_MULTIADDR=$(grep "cluster is running, bootnode is at" "$OUTFILE" | awk '{print $7}');
PAYMENTS_CONFIG_FILE=$(grep "payments configuration written to" "$OUTFILE" | awk '{print $5}');
WALLET_KEYS_FILE=$(grep "wallet keys written to" "$OUTFILE" | awk '{print $5}');
PAYMENTS_RPC=$(grep "blockchain_rpc_endpoint:" "$PAYMENTS_CONFIG_FILE" | awk '{print $2}');
PAYMENTS_CHAIN=$(grep "chain_id:" "$PAYMENTS_CONFIG_FILE" | awk '{print $2}');
PAYMENTS_SC_ADDR=$(grep "payments_sc_address:" "$PAYMENTS_CONFIG_FILE" | awk '{print $2}');
PAYMENTS_BF_ADDR=$(grep "blinding_factors_manager_sc_address:" "$PAYMENTS_CONFIG_FILE" | awk '{print $2}');
WALLET_PRIVATE_KEY=$(tail -n1 "$WALLET_KEYS_FILE")

# update or add an environment variable to one or more files
update_env() {
local key=$1
local value=$2
# Skip the first two arguments to get the file paths
local files=("${@:3}")

for file in "${files[@]}"; do
if [ -f "$file" ]; then # Check if file exists
# Check if the key exists in the file and remove it
if grep -q "^$key=" "$file"; then
# Key exists, remove it
grep -v "^$key=" "$file" > temp.txt && mv temp.txt "$file"
fi

# Append the new key-value pair to the file
echo "$key=$value" >> "$file"
else
echo "File $file not found. Creating $file"
touch $file
echo "$key=$value" >> "$file"
fi
done
}

# log file contents of key files to add to .env
log_file_contents() {
local file_path=$1 # Direct path to the target file

# Check if the file exists at the path
if [[ ! -f "$file_path" ]]; then
echo "File $file_path does not exist."
return 1 # Exit the function with an error status if the file does not exist
fi

# If the file exists, cat its contents
cat "$file_path"
}

# Generate node keys and add to .env - ex: NILLION_NODEKEY_PATH_PARTY_1
for ((i=1; i<=$num_node_keys; i++)); do
nodekey_file=$(mktemp)
"$NILLION_CLI" "$NILLION_CLI_COMMAND_NODE_KEYGEN" "$nodekey_file"
NODEKEY_FILES+=("$nodekey_file")
update_env "NILLION_NODEKEY_PATH_PARTY_$i" "$nodekey_file" $ENV_TO_UPDATE
update_env "NILLION_NODEKEY_TEXT_PARTY_$i" "$(log_file_contents $nodekey_file)" $ENV_TO_UPDATE
done

# Generate user keys and add to .env - ex: NILLION_USERKEY_PATH_PARTY_1
for ((i=1; i<=$num_user_keys; i++)); do
userkey_file=$(mktemp)
"$NILLION_CLI" "$NILLION_CLI_COMMAND_USER_KEYGEN" "$userkey_file"
USERKEY_FILES+=("$userkey_file")
update_env "NILLION_USERKEY_PATH_PARTY_$i" "$userkey_file" $ENV_TO_UPDATE
update_env "NILLION_USERKEY_TEXT_PARTY_$i" "$(log_file_contents $userkey_file)" $ENV_TO_UPDATE
done

echo "🔑 Node key and user keys have been generated and added to .env"

# Add environment variables to .env
update_env "NILLION_WEBSOCKETS" "$WEBSOCKET" $ENV_TO_UPDATE
update_env "NILLION_CLUSTER_ID" "$CLUSTER_ID" $ENV_TO_UPDATE
update_env "NILLION_BLOCKCHAIN_RPC_ENDPOINT" "$PAYMENTS_RPC" $ENV_TO_UPDATE
update_env "NILLION_BLINDING_FACTORS_MANAGER_SC_ADDRESS" "$PAYMENTS_BF_ADDR" $ENV_TO_UPDATE
update_env "NILLION_PAYMENTS_SC_ADDRESS" "$PAYMENTS_SC_ADDR" $ENV_TO_UPDATE
update_env "NILLION_CHAIN_ID" "$PAYMENTS_CHAIN" $ENV_TO_UPDATE
update_env "NILLION_WALLET_PRIVATE_KEY" "$WALLET_PRIVATE_KEY" $ENV_TO_UPDATE
update_env "NILLION_BOOTNODE_MULTIADDRESS" "$BOOT_MULTIADDR" $ENV_TO_UPDATE

echo "Running at process pid: $(pgrep -f $NILLION_DEVNET)"

echo "-------------------------------------------------------"
echo " 7MM 7MM "
echo " MM MM "
echo " db MM MM db "
echo " MM MM "
echo ".7MMpMMMb. 7MM MM MM 7MM ,pW-Wq. 7MMpMMMb. "
echo " MM MM MM MM MM MM 6W' Wb MM MM "
echo " MM MM MM MM MM MM 8M M8 MM MM "
echo " MM MM MM MM MM MM YA. ,A9 MM MM "
echo ".JMML JMML..JMML..JMML..JMML..JMML. Ybmd9 .JMML JMML."
echo "-------------------------------------------------------"
echo "-------------------------------------------------------"
echo "-----------🦆 CONNECTED TO NILLION-DEVNET 🦆-----------"
echo "-------------------------------------------------------"

echo "ℹ️ Your $ENV_TO_UPDATE file configurations were updated with nillion-devnet connection variables: websocket, cluster id, keys, blockchain info"
echo "💻 The Nillion devnet is still running behind the scenes; to spin down the Nillion devnet at any time, run 'killall nillion-devnet'"

echo "--------------------"
echo "💻 Your Nillion local cluster is still running - process pid: $(pgrep -f $NILLION_DEVNET)"
echo "ℹ️ Updated your .env file configuration variables: bootnode, cluster id, keys, blockchain info"

exit 0
35 changes: 35 additions & 0 deletions compile_programs_colab.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env bash

# This script compiles all $PROGRAMS_FOLDER programs to mir
PROGRAMS_FOLDER="programs"
COMPILED_PROGRAMS_FOLDER="programs-compiled"

SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}" 2>/dev/null)" && pwd -P)"
TARGET_PATH="${SCRIPT_PATH}/${COMPILED_PROGRAMS_FOLDER}"
PROGRAMS_PATH="${SCRIPT_PATH}/${PROGRAMS_FOLDER}"

PYNADAC="/root/.nilup/sdks/latest/pynadac"

cd "${PROGRAMS_PATH}" || exit 1

for file in *.py ; do
echo "Compiling ${file}"
"$PYNADAC" --target-dir "${TARGET_PATH}" \
--generate-mir-json \
"${file}"
done

echo "------------------------"
echo "Compiled programs: all files in the programs directory were compiled to mir: [$TARGET_PATH]"

echo "Now try running an example:"

echo "----------single party compute --------------"

echo "Code for single party compute lives in the examples_and_tutorials/core_concept_client_single_party_compute folder"
echo "📋 to run single party compute - addition_simple program: 'cd examples_and_tutorials/core_concept_client_single_party_compute && python3 addition_simple.py'"

echo "----------multi party compute --------------"

echo "Code for multi party compute lives in the examples_and_tutorials/core_concept_multi_party_compute folder"
echo "📋 to run multi party compute in 3 steps - addition_simple_multi_party_3: 'cd examples_and_tutorials/core_concept_multi_party_compute && python3 01_store_secret_party1.py'"
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import asyncio
import py_nillion_client as nillion
import os
import sys
import pytest

from dotenv import load_dotenv

sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')))
from helpers.nillion_client_helper import create_nillion_client
from helpers.nillion_keypath_helper import getUserKeyFromFile, getNodeKeyFromFile

load_dotenv()


# 1 Party running your first program on 1 stored secret and 1 compute time secret
async def main():
cluster_id = os.getenv("NILLION_CLUSTER_ID")
userkey = getUserKeyFromFile(os.getenv("NILLION_USERKEY_PATH_PARTY_1"))
nodekey = getNodeKeyFromFile(os.getenv("NILLION_NODEKEY_PATH_PARTY_1"))
client = create_nillion_client(userkey, nodekey)
party_id = client.party_id
user_id = client.user_id
party_name = "Party1"
program_name = "my_first_program"
program_mir_path = f"../../programs-compiled/{program_name}.nada.bin"

# store program
action_id = await client.store_program(
cluster_id, program_name, program_mir_path
)

program_id = f"{user_id}/{program_name}"
print('Stored program. action_id:', action_id)
print('Stored program_id:', program_id)

# Create a secret
stored_secret = nillion.Secrets({
"my_int1": nillion.SecretInteger(500),
})
secret_bindings = nillion.ProgramBindings(program_id)
secret_bindings.add_input_party(party_name, party_id)

# Store a secret
store_id = await client.store_secrets(
cluster_id, secret_bindings, stored_secret, None
)

# Bind the parties in the computation to the client to set input and output parties
compute_bindings = nillion.ProgramBindings(program_id)
compute_bindings.add_input_party(party_name, party_id)
compute_bindings.add_output_party(party_name, party_id)

print(f"Computing using program {program_id}")
print(f"Use secret store_id: {store_id}")

computation_time_secrets = nillion.Secrets({"my_int2": nillion.SecretInteger(10)})

# Compute on the secret
compute_id = await client.compute(
cluster_id,
compute_bindings,
[store_id],
computation_time_secrets,
nillion.PublicVariables({}),
)

# Print compute result
print(f"The computation was sent to the network. compute_id: {compute_id}")
while True:
compute_event = await client.next_compute_event()
if isinstance(compute_event, nillion.ComputeFinishedEvent):
print(f"✅ Compute complete for compute_id {compute_event.uuid}")
print(f"🖥️ The result is {compute_event.result.value}")
return compute_event.result.value


if __name__ == "__main__":
asyncio.run(main())


@pytest.mark.asyncio
async def test_main():
result = await main()
assert result == {'my_output': 510}
12 changes: 12 additions & 0 deletions programs/my_first_program.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from nada_dsl import *


def nada_main():
party1 = Party(name="Party1")
my_int1 = SecretInteger(Input(name="my_int1", party=party1))
my_int2 = SecretInteger(Input(name="my_int2", party=party1))

# write the computation for your program here - use my_int1 and my_int2 as inputs
# make sure you change the output below to be your new output

return [Output(my_int1, "my_output", party1)]