Skip to content

Commit

Permalink
Merge pull request #35 from NillionNetwork/release
Browse files Browse the repository at this point in the history
Release 0.2.1
  • Loading branch information
oceans404 authored May 7, 2024
2 parents 74c9ce9 + 938f0c7 commit cda32d6
Show file tree
Hide file tree
Showing 22 changed files with 1,369 additions and 210 deletions.
61 changes: 0 additions & 61 deletions docs/limitations.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ Binaries are not yet available for Windows.
### JavaScript Client

- Currently the JS client is only tested on Chromium browsers (Chrome, Brave & Edge) and production deployments will likely require [activating COOP and COEP headers](https://web.dev/coop-coep/).
- The JS client does not yet have SecretArray functionality.
- The JS client is a browser client and does not yet work in NodeJs.

### CLI Client
Expand All @@ -57,36 +56,6 @@ Running the [`nillion-devnet`](/nillion-devnet) command (SDK tool) will spin up

Check the [Nada language docs](/nada-lang) for the current [data types](/nada-lang-types) and [operations](/nada-lang-operators) available.

### No recursion and/or looping constructs

You cannot write loops or recursion in Nada. You can circumvent this problem in some cases by using a Python loop so long as the data controlling the loop is not a Nada value. Consider the fragment below:

```python
voters = []
for i in range(nr_voters):
voters.append(Party(name="Voter" + str(i)))
outparty = Party(name="OutParty")
```

The loop is providing a sequence of numbers to name party voters, therefore it works as you'd expect.

Now, consider the following code fragment:

```python

my_array = Array(SecretInteger(Input(name="my_array_1", party=party1)), size=3)

def inc(a: SecretInteger, my_int: SecretInteger) -> SecretInteger:
return a + my_int

new_array = []
for i in range(3)
new_array[i] = inc(my_array[i], SecretInteger(1))

```

This implementation won't work. It expects to run the `for … in` in the network, which is not possible. For that, you must use a map function. All array-related iterations must use either [map or reduce](/nada-lang-operators#array-operations-experimental-feature).

### No random value generation in Nada

- We expect random value generation in Nada in the future.
Expand All @@ -110,36 +79,6 @@ The Nada compiler will throw an error as `inc2` does not have access to `inc`.
- Currently you should not use 0 as an input value as it may leak other secrets. We are working on fixing this.
- Ensure that your programs do not attempt to divide by 0, this is not supported in our language currently.

### Storing and running large nada programs

- Currently certain forms of large programs cannot be stored or computed on the network.

### Arrays (experimental)

- Arrays are statically allocated, and be updated.
- Arrays only exist with statically defined lengths.
- Arrays cannot be nested, so matrix programs cannot be ergonomically written in Nada.

There are 3 ways to define an array

1. Array via input

```python
my_array = Array(SecretInteger(Input(name="my_array_1", party=party1)), size=3)
```

2. Array via known list of values

```python
my_array = Array(SecretInteger(1), my_int)
```

3. Array via an existing array

```python
new_array = my_array.map(your_function)
```

## Authentication in scaffold-nillion

- We have not yet implemented a proper authentication solution. Currently in the JavaScript Quickstart [Scaffold-Nillion repo](https://github.com/NillionNetwork/scaffold-nillion) we are using an experimental [MetaMask Snap](https://metamask.io/snaps/), [nillion-user-key-manager](https://www.npmjs.com/package/nillion-user-key-manager), which is installable [here](https://nillion-snap-site.vercel.app/), as a temporary mechanism to allow a user to generate a Nillion user key, and then locally store their user key for use in Nillion apps, which require a user key for client initialization. We do not recommend doing this in a production environment.
Expand Down
20 changes: 3 additions & 17 deletions docs/nada-lang-operators.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,11 @@ Overview of the primitive and the array Nada operations.
| Power | `x ** y` | `P ← P ** P` |
| Division | `x / y` | `P ← P / P`,<br/> `S ← P / S`,<br/> `S ← S / P`,<br/> `S ← S / S` |
| Modulo | `x % y` | `P ← P % P`,<br/> `S ← P % S`,<br/> `S ← S % P`,<br/> `S ← S % S` |
| Shifts | `x << y`,<br/> `x >> y` | `P ← P << P`,<br/> `S ← S << P` |
| Probabilistic truncation | `x.trunc_pr(y)` | `S ← S.trunc_pr(P)` |
| Comparisons | `x < y`,<br/> `x <= y`,<br/> `x > y`,<br/> `x >= y` | `P ← P < P`,<br/> `S ← P < S`,<br/> `S ← S < P`,<br/> `S ← S < S` |
| Private Equality | `x == y` | `S ← S == S`,<br/> `S ← S == P`,<br/> `S ← P == S`,<br/> `P ← P == P` |
| Ternary _"if else"_<br/> (public condition) | `cond.if_else(x, y)` | `P ← P.if_else(P, P)`,<br/> `S ← P.if_else(P, S)`,<br/> `S ← P.if_else(S, P)`,<br/> `S ← P.if_else(S, S)` |
| Ternary _"if else"_<br/> (secret condition) | `cond.if_else(x, y)` | `S ← S.if_else(P, P)`,<br/> `S ← S.if_else(P, S)`,<br/> `S ← S.if_else(S, P)`,<br/> `S ← S.if_else(S, S)` |
| Reveal _(convert a private<br/> value into a public value)_ | `x.reveal()` | `P ← S.reveal()` |
| Private Equality | `x == y` | `S ← S == S`,<br/> `S ← S == P`,<br/> `S ← P == S`,<br/> `P ← P == P` |
| Public Equality _(publicly output<br/> if two secrets are equal)_ | `x.public_equals(y)` | `P ← S.public_equals(S)` |

## Array Operations _(Experimental Feature)_

:::warning

Arrays are a new feature in the Nada language. We hope you find them handy, though there might still be undiscovered bugs. If you encounter anything, please let us know by reporting them in [Bugs](https://github.com/orgs/NillionNetwork/discussions/categories/bugs).

:::

| Name | Syntax | Description |
| -------- | ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| nada_fn | `nada_fn(lambda x: x, args_ty={'x': SecretInteger}, return_ty=SecretInteger)` | Inline function, only to be used with `map` and `reduce`. |
| map() | `array.map(nada_fn(...))` | Applies the provided `nada_fn` to every element in the array. |
| reduce() | `array.reduce(nada_fn(...), acc)` | The `reduce()` method executes a provided function (`nada_fn`) once for each element in the array, resulting in a single output value. The `nada_fn` accepts two arguments: 1. The accumulator `acc` accumulates the return values. 2. Each element of the array. |
| zip() | `array_1.zip(array_2)` | Combines two arrays into an array of Tuples. |
| unzip() | `unzip(array_1.zip(array_2))` | Returns a tuple of two arrays. |
33 changes: 15 additions & 18 deletions docs/nada-lang-programs.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Nada Programs
# Programming with Nada

Check out Nada program examples and learn how to compile and run Nada programs.
This is a basic introduction to programming with the Nada embedded domain-specific language (DSL). Use this page to learn how to compile and run a basic Nada program. Once you are ready, proceed to the other tutorials to learn Nada features via simple example programs!

## Program examples
## Example Programs

### Tiny addition
### Tiny Addition (with a Single Party)

Here's a tiny Nada program that adds two secret integer inputs.
Below is a tiny Nada program `tiny_addition.py` that adds two secret integer inputs. Both of these integer inputs belong to `Nilla 🐶`, who is using the Nillion Network to compute their total. Most importantly, the secret integers are never revealed to the Nillion Network nodes!

```python
from nada_dsl import *
Expand All @@ -22,23 +22,20 @@ def nada_main():
return [Output(total, "my_output", nilla_the_dog)]
```

[`tiny_addition.py`](https://github.com/nillion-oss/nillion-python-starter/blob/main/programs/tiny_secret_addition_complete.py) takes in two [Inputs](concepts.md#inputs) from the same [Party](concepts.md#party)
The [`tiny_addition.py`](https://github.com/nillion-oss/nillion-python-starter/blob/main/programs/tiny_secret_addition_complete.py) program takes in two [Inputs](concepts.md#inputs) from the same [Party](concepts.md#party).

<table><thead><tr><th width="162">Input name</th><th width="145">Input type</th><th>Party name</th></tr></thead><tbody><tr><td>`my_secret_1`</td><td>`SecretInteger`</td><td>`Nilla 🐶`</td></tr><tr><td>`my_secret_2`</td><td>`SecretInteger`</td><td>`Nilla 🐶`</td></tr></tbody></table>

[`tiny_addition.py`](https://github.com/nillion-oss/nillion-python-starter/blob/main/programs/tiny_secret_addition_complete.py) returns an [Output](concepts.md#outputs) to a [Party](concepts.md#party)
The program [`tiny_addition.py`](https://github.com/nillion-oss/nillion-python-starter/blob/main/programs/tiny_secret_addition_complete.py) returns an [Output](concepts.md#outputs) to a [Party](concepts.md#party). Only that party sees the output because it it of type `SecretInteger`.

<table><thead><tr><th>Output value</th><th>Output name</th><th>Output Type</th><th>Party name</th><th data-hidden>Output type</th><th data-hidden>Party name</th><th data-hidden>Output name</th></tr></thead><tbody><tr><td>`total`</td><td>`my_output`</td><td>`SecretInteger`</td><td>`Nilla 🐶`</td><td>`SecretInteger`</td><td>`Nilla 🐶`</td><td>`my_output`</td></tr></tbody></table>

### Addition by reducing an array

Let's do something more interesting!

The following program reduces an array to add up every element

```python reference showGithubLink
https://github.com/nillion-oss/nillion-python-starter/blob/main/programs/reduce_simple.py
```
<table>
<thead>
<tr><th>Output value</th><th>Output Name</th><th>Output Type</th><th>Party name</th></tr>
</thead>
<tbody>
<tr><td>`total`</td><td>`my_output`</td><td>`SecretInteger`</td><td>`Nilla 🐶`</td></tr>
</tbody>
</table>

### Ternary conditional operators

Expand Down
116 changes: 116 additions & 0 deletions docs/nada-lang-tutorial-arithmetic-and-logic.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Arithmetic and Logic

Nada programs can work with common arithmetic and logic values (such as integers and booleans) and operations (such as addition and conditional expressions).

## Integers

The example below computes the revenue generated from the sales of two categories of product. In this example, all inputs are of type `SecretInteger`, so the result `revenue` is also of this type.

<iframe src='img/nada-lang-tutorial-arithmetic-and-logic-0.html' height='350px' width='100%'></iframe>
<!--```python
from nada_dsl import *
def nada_main():
pricing = Party(name="pricing")
inventory = Party(name="inventory")
accounting = Party(name="accounting")
price_potato = SecretInteger(Input(name="price_potato", party=pricing))
price_tomato = SecretInteger(Input(name="price_tomato", party=pricing))
quantity_potato = SecretInteger(Input(name="quantity_potato", party=inventory))
quantity_tomato = SecretInteger(Input(name="quantity_tomato", party=inventory))
revenue = (price_potato * quantity_potato) + (price_tomato + quantity_tomato)
return [Output(revenue, "revenue", accounting)]
````-->

Suppose that the price information is public. In this case, `revenue` is still of type [`SecretInteger`](nada-lang-types#primitive-data-types) because the quantity information is private. If `revenue` were of type [`PublicInteger`](nada-lang-types#primitive-data-types), it would (in some cases) be possible to determine the quantity information from the revenue by working backwards.

<iframe src='img/nada-lang-tutorial-arithmetic-and-logic-1.html' height='350px' width='100%'></iframe>
<!--```python
from nada_dsl import *
def nada_main():
pricing = Party(name="pricing")
inventory = Party(name="inventory")
accounting = Party(name="accounting")
price_potato = PublicInteger(Input(name="price_potato", party=pricing))
price_tomato = PublicInteger(Input(name="price_tomato", party=pricing))
quantity_potato = SecretInteger(Input(name="quantity_potato", party=inventory))
quantity_tomato = SecretInteger(Input(name="quantity_tomato", party=inventory))
revenue = (price_potato * quantity_potato) + (price_tomato + quantity_tomato)
return [Output(revenue, "revenue", accounting)]
```-->

What about integer values that appear in the program as literals (*i.e.*, they are not secret or public inputs) but are used within calculations involving inputs? These should be of type [`Integer`](nada-lang-types#primitive-data-types).

<iframe src='img/nada-lang-tutorial-arithmetic-and-logic-2.html' height='300px' width='100%'></iframe>
<!--```python
from nada_dsl import *
def nada_main():
pricing = Party(name="pricing")
inventory = Party(name="inventory")
accounting = Party(name="accounting")
price = PublicInteger(Input(name="price", party=pricing))
quantity = SecretInteger(Input(name="quantity", party=inventory))
revenue_in_cents = Integer(100) * price * quantity
return [Output(revenue_in_cents, "revenue_in_cents", accounting)]
```-->

## Boolean Values and Comparison of Integers

Comparison operations can be applied to integers. Such comparison expressions evaluate to Nada boolean values. Whether this resulting value is secret depends on whether the integers being compared are secret. Furthermore, Nada boolean values support the `if_else` method, which implements a variant of the [ternary conditional operator](https://en.wikipedia.org/wiki/Ternary_conditional_operator) that can work with Nada values (even if they are secret).

The example below leverages both an integer comparison operator and the ternary operator to determine the larger of two secret inputs.

<iframe src='img/nada-lang-tutorial-arithmetic-and-logic-3.html' height='278px' width='100%'></iframe>
<!--```python
from nada_dsl import *
def nada_main():
data_owner = Party(name="data_owner")
x = SecretInteger(Input(name="x", party=data_owner))
y = SecretInteger(Input(name="y", party=data_owner))
condition = x > y
maximum = condition.if_else(x, y)
return [Output(maximum, "maximum", data_owner)]
```-->

## Built-in Python Constants and Operations

Because Nada is a DSL embedded inside Python, [built-in constants of type `int` and `bool`](https://docs.python.org/3/library/stdtypes.html) (and the operators associated with these types) can be used directly. However, it is important to understand that these cannot be used interchangeably or mixed. The example below demonstrates both correct and incorrect usage of built-in and Nada values.

<iframe src='img/nada-lang-tutorial-arithmetic-and-logic-4.html' height='350px' width='100%'></iframe>
<!--```python
from nada_dsl import *
def nada_main():
data_owner = Party(name="data_owner")
x = SecretInteger(Input(name="x", party=data_owner))
# Permitted.
a = x + Integer(123) + Integer(456)
b = x + Integer(123 + 456)
# Not permitted.
c = x + 123 + 456
d = x + 123
return [Output(a, "a", data_owner), Output(b, "b", data_owner)]
```-->
````
26 changes: 26 additions & 0 deletions docs/nada-lang-tutorial-functions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Functions

The Nada DSL supports a limited form of user-defined functions.

## Basic Example

The example below introduces a function that calculates the total of three secret integer values. Notice that the arguments and the function itself both have [Python type annotations](https://docs.python.org/3/library/typing.html).

<iframe src='img/nada-lang-tutorial-functions-0.html' height='334px' width='100%'></iframe>
<!--```python
from nada_dsl import *
def total(x: SecretInteger, y: SecretInteger, z: SecretInteger) -> SecretInteger:
return x + y + z
def nada_main():
data_owner = Party(name="data_owner")
x = SecretInteger(Input(name="x", party=data_owner))
y = SecretInteger(Input(name="y", party=data_owner))
z = SecretInteger(Input(name="z", party=data_owner))
t = total(x, y, z)
return [Output(t, "t", data_owner)]
```-->
Loading

0 comments on commit cda32d6

Please sign in to comment.