|
1 | 1 | """
|
2 | 2 | In this example, we:
|
3 |
| -1. connect to the local nillion-devnet |
4 |
| -2. store the secret addition program |
5 |
| -3. store a secret to be used in the computation |
6 |
| -4. compute the secret addition program with the stored secret and another computation time secret |
| 3 | +1. import packages we'll be using |
| 4 | +2. connect to the local nillion-devnet |
| 5 | +3. store the secret addition program |
| 6 | +4. store a secret to be used in the computation |
| 7 | +5. compute the secret addition program with the stored secret and another computation time secret |
| 8 | +6. return the computation result |
7 | 9 | """
|
8 | 10 |
|
9 | 11 | import asyncio
|
10 |
| -import py_nillion_client as nillion |
11 | 12 | import os
|
12 | 13 |
|
13 |
| -from py_nillion_client import NodeKey, UserKey |
| 14 | +from nillion_client import ( |
| 15 | + InputPartyBinding, |
| 16 | + Network, |
| 17 | + NilChainPayer, |
| 18 | + NilChainPrivateKey, |
| 19 | + OutputPartyBinding, |
| 20 | + Permissions, |
| 21 | + SecretInteger, |
| 22 | + VmClient, |
| 23 | + PrivateKey, |
| 24 | +) |
14 | 25 | from dotenv import load_dotenv
|
15 |
| -from nillion_python_helpers import get_quote_and_pay, create_nillion_client, create_payments_config |
16 |
| - |
17 |
| -from cosmpy.aerial.client import LedgerClient |
18 |
| -from cosmpy.aerial.wallet import LocalWallet |
19 |
| -from cosmpy.crypto.keypairs import PrivateKey |
20 | 26 |
|
21 | 27 | home = os.getenv("HOME")
|
22 | 28 | load_dotenv(f"{home}/.config/nillion/nillion-devnet.env")
|
23 | 29 |
|
24 | 30 | async def main():
|
25 |
| - # 1. Initial setup |
26 |
| - # 1.1. Get cluster_id, grpc_endpoint, & chain_id from the .env file |
27 |
| - cluster_id = os.getenv("NILLION_CLUSTER_ID") |
28 |
| - grpc_endpoint = os.getenv("NILLION_NILCHAIN_GRPC") |
29 |
| - chain_id = os.getenv("NILLION_NILCHAIN_CHAIN_ID") |
30 |
| - # 1.2 pick a seed and generate user and node keys |
31 |
| - seed = "my_seed" |
32 |
| - userkey = UserKey.from_seed(seed) |
33 |
| - nodekey = NodeKey.from_seed(seed) |
34 |
| - |
35 |
| - # 2. Initialize NillionClient against nillion-devnet |
36 |
| - # Create Nillion Client for user |
37 |
| - client = create_nillion_client(userkey, nodekey) |
38 |
| - |
39 |
| - party_id = client.party_id |
40 |
| - user_id = client.user_id |
| 31 | + # 2. Initial setup, Initialize NillionClient against nillion-devnet |
| 32 | + # Use the devnet configuration generated by `nillion-devnet` |
| 33 | + network = Network.from_config("devnet") |
| 34 | + |
| 35 | + # Create payments config and set up Nillion wallet with a private key to pay for operations |
| 36 | + nilchain_key: str = os.getenv("NILLION_NILCHAIN_PRIVATE_KEY_0") # type: ignore |
| 37 | + payer = NilChainPayer( |
| 38 | + network, |
| 39 | + wallet_private_key=NilChainPrivateKey(bytes.fromhex(nilchain_key)), |
| 40 | + gas_limit=10000000, |
| 41 | + ) |
41 | 42 |
|
42 |
| - # 3. Pay for and store the program |
43 |
| - # Set the program name and path to the compiled program |
| 43 | + # Use a random key to identify ourselves |
| 44 | + signing_key = PrivateKey() |
| 45 | + client = await VmClient.create(signing_key, network, payer) |
| 46 | + party_name = "Party1" |
44 | 47 | program_name = "secret_addition_complete"
|
45 | 48 | program_mir_path = f"../nada_quickstart_programs/target/{program_name}.nada.bin"
|
46 | 49 |
|
47 |
| - # Create payments config, client and wallet |
48 |
| - payments_config = create_payments_config(chain_id, grpc_endpoint) |
49 |
| - payments_client = LedgerClient(payments_config) |
50 |
| - payments_wallet = LocalWallet( |
51 |
| - PrivateKey(bytes.fromhex(os.getenv("NILLION_NILCHAIN_PRIVATE_KEY_0"))), |
52 |
| - prefix="nillion", |
53 |
| - ) |
54 | 50 |
|
55 |
| - # Pay to store the program and obtain a receipt of the payment |
56 |
| - receipt_store_program = await get_quote_and_pay( |
57 |
| - client, |
58 |
| - nillion.Operation.store_program(program_mir_path), |
59 |
| - payments_wallet, |
60 |
| - payments_client, |
61 |
| - cluster_id, |
62 |
| - ) |
| 51 | + # 3. Pay for and store the program |
| 52 | + print("-----STORE PROGRAM") |
63 | 53 |
|
64 |
| - # Store the program |
65 |
| - action_id = await client.store_program( |
66 |
| - cluster_id, program_name, program_mir_path, receipt_store_program |
67 |
| - ) |
| 54 | + # Store program |
| 55 | + program_mir = open(program_mir_path, "rb").read() |
| 56 | + program_id = await client.store_program(program_name, program_mir).invoke() |
68 | 57 |
|
69 |
| - # Create a variable for the program_id, which is the {user_id}/{program_name}. We will need this later |
70 |
| - program_id = f"{user_id}/{program_name}" |
71 |
| - print("Stored program. action_id:", action_id) |
72 |
| - print("Stored program_id:", program_id) |
| 58 | + # Print details about stored program |
| 59 | + print(f"Stored program_id: {program_id}") |
73 | 60 |
|
74 | 61 | # 4. Create the 1st secret, add permissions, pay for and store it in the network
|
75 |
| - # Create a secret named "my_int1" with any value, ex: 500 |
76 |
| - new_secret = nillion.NadaValues( |
77 |
| - { |
78 |
| - "my_int1": nillion.SecretInteger(500), |
79 |
| - } |
80 |
| - ) |
| 62 | + print("-----STORE SECRETS") |
81 | 63 |
|
82 |
| - # Set the input party for the secret |
83 |
| - # The party name needs to match the party name that is storing "my_int1" in the program |
84 |
| - party_name = "Party1" |
| 64 | + # Create a secret |
| 65 | + values = { |
| 66 | + "my_int1": SecretInteger(500), |
| 67 | + } |
85 | 68 |
|
86 |
| - # Set permissions for the client to compute on the program |
87 |
| - permissions = nillion.Permissions.default_for_user(client.user_id) |
88 |
| - permissions.add_compute_permissions({client.user_id: {program_id}}) |
89 |
| - |
90 |
| - # Pay for and store the secret in the network and print the returned store_id |
91 |
| - receipt_store = await get_quote_and_pay( |
92 |
| - client, |
93 |
| - nillion.Operation.store_values(new_secret, ttl_days=5), |
94 |
| - payments_wallet, |
95 |
| - payments_client, |
96 |
| - cluster_id, |
97 |
| - ) |
98 |
| - # Store a secret |
99 |
| - store_id = await client.store_values( |
100 |
| - cluster_id, new_secret, permissions, receipt_store |
| 69 | + # Create a permissions object to attach to the stored secret |
| 70 | + permissions = Permissions.defaults_for_user(client.user_id).allow_compute( |
| 71 | + client.user_id, program_id |
101 | 72 | )
|
102 |
| - print(f"Computing using program {program_id}") |
103 |
| - print(f"Use secret store_id: {store_id}") |
| 73 | + |
| 74 | + # Store a secret, passing in the receipt that shows proof of payment |
| 75 | + values_id = await client.store_values( |
| 76 | + values, ttl_days=5, permissions=permissions |
| 77 | + ).invoke() |
104 | 78 |
|
105 | 79 | # 5. Create compute bindings to set input and output parties, add a computation time secret and pay for & run the computation
|
106 |
| - compute_bindings = nillion.ProgramBindings(program_id) |
107 |
| - compute_bindings.add_input_party(party_name, party_id) |
108 |
| - compute_bindings.add_output_party(party_name, party_id) |
109 |
| - |
110 |
| - # Add my_int2, the 2nd secret at computation time |
111 |
| - computation_time_secrets = nillion.NadaValues({"my_int2": nillion.SecretInteger(10)}) |
112 |
| - |
113 |
| - # Pay for the compute |
114 |
| - receipt_compute = await get_quote_and_pay( |
115 |
| - client, |
116 |
| - nillion.Operation.compute(program_id, computation_time_secrets), |
117 |
| - payments_wallet, |
118 |
| - payments_client, |
119 |
| - cluster_id, |
120 |
| - ) |
| 80 | + print("-----COMPUTE") |
121 | 81 |
|
122 |
| - # Compute on the secret |
123 |
| - compute_id = await client.compute( |
124 |
| - cluster_id, |
125 |
| - compute_bindings, |
126 |
| - [store_id], |
127 |
| - computation_time_secrets, |
128 |
| - receipt_compute, |
129 |
| - ) |
| 82 | + # Bind the parties in the computation to the client to set input and output parties |
| 83 | + input_bindings = [InputPartyBinding(party_name, client.user_id)] |
| 84 | + output_bindings = [OutputPartyBinding(party_name, [client.user_id])] |
130 | 85 |
|
131 |
| - # 8. Return the computation result |
| 86 | + # Create a computation time secret to use |
| 87 | + compute_time_values = {"my_int2": SecretInteger(10)} |
| 88 | + |
| 89 | + # Compute, passing in the compute time values as well as the previously uploaded value. |
| 90 | + print(f"Invoking computation using program {program_id} and values id {values_id}") |
| 91 | + compute_id = await client.compute( |
| 92 | + program_id, |
| 93 | + input_bindings, |
| 94 | + output_bindings, |
| 95 | + values=compute_time_values, |
| 96 | + value_ids=[values_id], |
| 97 | + ).invoke() |
| 98 | + |
| 99 | + # 6. Return the computation result |
132 | 100 | print(f"The computation was sent to the network. compute_id: {compute_id}")
|
133 |
| - while True: |
134 |
| - compute_event = await client.next_compute_event() |
135 |
| - if isinstance(compute_event, nillion.ComputeFinishedEvent): |
136 |
| - print(f"✅ Compute complete for compute_id {compute_event.uuid}") |
137 |
| - print(f"🖥️ The result is {compute_event.result.value}") |
138 |
| - return compute_event.result.value |
| 101 | + result = await client.retrieve_compute_results(compute_id).invoke() |
| 102 | + print(f"✅ Compute complete for compute_id {compute_id}") |
| 103 | + print(f"🖥️ The result is {result}") |
| 104 | + return result |
139 | 105 |
|
140 | 106 |
|
141 | 107 | if __name__ == "__main__":
|
|
0 commit comments