Skip to content

Commit

Permalink
Merge pull request #31 from NillionNetwork/colab_starter_changes
Browse files Browse the repository at this point in the history
Colab starter changes
  • Loading branch information
Davetbutler authored Jun 18, 2024
2 parents 7f04be6 + b31f61d commit 382d128
Show file tree
Hide file tree
Showing 4 changed files with 284 additions and 0 deletions.
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)]

0 comments on commit 382d128

Please sign in to comment.