-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: fixed testing and added more examples (#10)
* feat: added new examples and organized existing ones * fix: fixed testing not correct results
- Loading branch information
Showing
25 changed files
with
586 additions
and
70 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# Broadcasting Tutorial | ||
|
||
This tutorial shows how to efficiently use broadcasting in Nada using Nada Algebra. | ||
|
||
```python | ||
from nada_dsl import * | ||
|
||
# Step 0: Nada Algebra is imported with this line | ||
import nada_algebra as na | ||
|
||
|
||
def nada_main(): | ||
# Step 1: We use Nada Algebra wrapper to create "Party0", "Party1" and "Party2" | ||
parties = na.parties(3) | ||
|
||
# Step 2: Party0 creates an array of dimension (3, ) with name "A" | ||
a = na.array([3], parties[0], "A") | ||
|
||
# Step 3: Party1 creates an array of dimension (3, ) with name "B" | ||
b = na.array([3], parties[1], "B") | ||
|
||
# Step 4: Party0 creates an array of dimension (3, ) with name "C" | ||
c = na.array([3], parties[0], "C") | ||
|
||
# Step 5: Party1 creates an array of dimension (3, ) with name "D" | ||
d = na.array([3], parties[1], "D") | ||
|
||
# Step 4: The result is of computing SIMD operations on top of the elements of the array | ||
# SIMD operations are performed on all the elements of the array. | ||
# The equivalent would be: for i in range(3): result += a[i] + b[i] - c[i] * d[i] | ||
result = a + b - c * d | ||
# Step 5: We can use result.output() to produce the output for Party2 and variable name "my_output" | ||
return result.output(parties[2], "my_output") | ||
``` | ||
|
||
0. We import Nada algebra using `import nada_algebra as na`. | ||
1. We create an array of parties, with our wrapper using `parties = na.parties(3)` which creates an array of parties named: `Party0`, `Party1` and `Party2`. | ||
2. We create our input array `a` with `na.array([3], parties[0], "A")`, meaning our array will have dimension 3, `Party0` will be in charge of giving its inputs and the name of the variable is `"A"`. | ||
3. We create our input array `b` with `na.array([3], parties[1], "B")`, meaning our array will have dimension 3, `Party1` will be in charge of giving its inputs and the name of the variable is `"B"`. | ||
4. We create our input array `c` with `na.array([3], parties[1], "C")`, meaning our array will have dimension 3, `Party0` will be in charge of giving its inputs and the name of the variable is `"C"`. | ||
5. We create our input array `d` with `na.array([3], parties[1], "D")`, meaning our array will have dimension 3, `Party1` will be in charge of giving its inputs and the name of the variable is `"D"`. | ||
5. Finally, we use Nada Algebra to produce the outputs of the array like: `result.output(parties[2], "my_output")` establishing that the output party will be `Party2`and the name of the output variable will be `my_output`. | ||
# How to run the tutorial. | ||
|
||
1. First, we need to compile the nada program running: `nada build`. | ||
2. Then, we can test our program is running with: `nada test`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
name = "broadcasting" | ||
version = "0.1.0" | ||
authors = [""] | ||
|
||
[[programs]] | ||
path = "src/broadcasting.py" | ||
prime_size = 128 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
# Import necessary libraries and modules | ||
import asyncio | ||
import py_nillion_client as nillion | ||
import os | ||
import sys | ||
import pytest | ||
import numpy as np | ||
import time | ||
from dotenv import load_dotenv | ||
|
||
# Add the parent directory to the system path to import modules from it | ||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))) | ||
|
||
# Import helper functions for creating nillion client and getting keys | ||
from dot_product.network.helpers.nillion_client_helper import create_nillion_client | ||
from dot_product.network.helpers.nillion_keypath_helper import ( | ||
getUserKeyFromFile, | ||
getNodeKeyFromFile, | ||
) | ||
import nada_algebra.client as na_client | ||
|
||
# Load environment variables from a .env file | ||
load_dotenv() | ||
from dot_product.config.parameters import DIM | ||
|
||
|
||
# Main asynchronous function to coordinate the process | ||
async def main(): | ||
print(f"USING: {DIM}") | ||
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_names = na_client.parties(3) | ||
program_name = "main" | ||
program_mir_path = f"./target/{program_name}.nada.bin" | ||
|
||
# Store the 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 and store secrets for two parties | ||
A = np.ones([DIM]) | ||
C = np.ones([DIM]) | ||
stored_secret = nillion.Secrets( | ||
na_client.concat( | ||
[ | ||
na_client.array(A, "A"), | ||
na_client.array(C, "C"), | ||
] | ||
) | ||
) | ||
secret_bindings = nillion.ProgramBindings(program_id) | ||
secret_bindings.add_input_party(party_names[0], party_id) | ||
|
||
# Store the secret for the specified party | ||
A_store_id = await client.store_secrets( | ||
cluster_id, secret_bindings, stored_secret, None | ||
) | ||
|
||
B = np.ones([DIM]) | ||
D = np.ones([DIM]) | ||
stored_secret = nillion.Secrets( | ||
na_client.concat( | ||
[ | ||
na_client.array(B, "B"), | ||
na_client.array(D, "D"), | ||
] | ||
) | ||
) | ||
secret_bindings = nillion.ProgramBindings(program_id) | ||
secret_bindings.add_input_party(party_names[1], party_id) | ||
|
||
# Store the secret for the specified party | ||
B_store_id = await client.store_secrets( | ||
cluster_id, secret_bindings, stored_secret, None | ||
) | ||
|
||
# Set up the compute bindings for the parties | ||
compute_bindings = nillion.ProgramBindings(program_id) | ||
[ | ||
compute_bindings.add_input_party(party_name, party_id) | ||
for party_name in party_names[:-1] | ||
] | ||
compute_bindings.add_output_party(party_names[-1], party_id) | ||
|
||
print(f"Computing using program {program_id}") | ||
print(f"Use secret store_id: {A_store_id}, {B_store_id}") | ||
|
||
computation_time_secrets = nillion.Secrets({"my_int2": nillion.SecretInteger(10)}) | ||
|
||
# Perform the computation and return the result | ||
compute_id = await client.compute( | ||
cluster_id, | ||
compute_bindings, | ||
[A_store_id, B_store_id], | ||
computation_time_secrets, | ||
nillion.PublicVariables({}), | ||
) | ||
|
||
# Monitor and 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 | ||
return result | ||
|
||
|
||
# Run the main function if the script is executed directly | ||
if __name__ == "__main__": | ||
asyncio.run(main()) |
12 changes: 12 additions & 0 deletions
12
examples/broadcasting/network/helpers/nillion_client_helper.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import os | ||
import py_nillion_client as nillion | ||
from helpers.nillion_payments_helper import create_payments_config | ||
|
||
|
||
def create_nillion_client(userkey, nodekey): | ||
bootnodes = [os.getenv("NILLION_BOOTNODE_MULTIADDRESS")] | ||
payments_config = create_payments_config() | ||
|
||
return nillion.NillionClient( | ||
nodekey, bootnodes, nillion.ConnectionMode.relay(), userkey, payments_config | ||
) |
10 changes: 10 additions & 0 deletions
10
examples/broadcasting/network/helpers/nillion_keypath_helper.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
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) |
12 changes: 12 additions & 0 deletions
12
examples/broadcasting/network/helpers/nillion_payments_helper.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import os | ||
import py_nillion_client as nillion | ||
|
||
|
||
def create_payments_config(): | ||
return 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"), | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
from nada_dsl import * | ||
|
||
# Step 0: Nada Algebra is imported with this line | ||
import nada_algebra as na | ||
|
||
|
||
def nada_main(): | ||
# Step 1: We use Nada Algebra wrapper to create "Party0", "Party1" and "Party2" | ||
parties = na.parties(3) | ||
|
||
# Step 2: Party0 creates an array of dimension (3, ) with name "A" | ||
a = na.array([3], parties[0], "A") | ||
|
||
# Step 3: Party1 creates an array of dimension (3, ) with name "B" | ||
b = na.array([3], parties[1], "B") | ||
|
||
# Step 4: Party0 creates an array of dimension (3, ) with name "C" | ||
c = na.array([3], parties[0], "C") | ||
|
||
# Step 5: Party1 creates an array of dimension (3, ) with name "D" | ||
d = na.array([3], parties[1], "D") | ||
|
||
# Step 4: The result is of computing SIMD operations on top of the elements of the array | ||
# SIMD operations are performed on all the elements of the array. | ||
# The equivalent would be: for i in range(3): result += a[i] + b[i] - c[i] * d[i] | ||
result = a + b - c * d | ||
# Step 5: We can use result.output() to produce the output for Party2 and variable name "my_output" | ||
return result.output(parties[2], "my_output") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# This directory is kept purposely, so that no compilation errors arise. | ||
# Ignore everything in this directory | ||
* | ||
# Except this file | ||
!.gitignore |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
--- | ||
program: broadcasting | ||
inputs: | ||
secrets: | ||
B_1: | ||
SecretInteger: "3" | ||
A_0: | ||
SecretInteger: "3" | ||
C_1: | ||
SecretInteger: "3" | ||
B_0: | ||
SecretInteger: "3" | ||
D_0: | ||
SecretInteger: "3" | ||
C_2: | ||
SecretInteger: "3" | ||
C_0: | ||
SecretInteger: "3" | ||
A_2: | ||
SecretInteger: "3" | ||
A_1: | ||
SecretInteger: "3" | ||
D_1: | ||
SecretInteger: "3" | ||
B_2: | ||
SecretInteger: "3" | ||
D_2: | ||
SecretInteger: "3" | ||
public_variables: {} | ||
expected_outputs: | ||
my_output_1: | ||
SecretInteger: "-3" | ||
my_output_2: | ||
SecretInteger: "-3" | ||
my_output_0: | ||
SecretInteger: "-3" |
Oops, something went wrong.