1
1
mirror of https://github.com/anoma/juvix.git synced 2024-12-03 09:41:10 +03:00
Commit Graph

16 Commits

Author SHA1 Message Date
Łukasz Czajka
7760267bcd
Fix JuvixTree unification (#3087)
* Closes #3016 
* Fixes the `curryType` function
* Changes the behaviour of `unifyTypes` and `isSubtype` to always curry
first
2024-10-09 15:33:42 +02:00
Łukasz Czajka
eb5b2e4595
Fix JuvixTree type unification (#2972)
* Closes #2954 
* The problem was that the type validation algorithm was too strict for
higher-order functions with a dynamic (unknown) target.
2024-08-27 10:31:14 +02:00
Paul Cadman
ce5c2c5c55
Add builtin ByteArray type (#2933)
This PR adds support for a builtin `ByteArray` type and associated
functions for constructing a `ByteArray` from a list of bytes and a
function to query the size of the `ByteArray`. It is only available in
the Anoma backend.

In Core / Tree, ByteArray constant is stored using a Haskell ByteString.

In Anoma the ByteArray is stored as a cell where the head is the length
of the ByteArray and the tail is an integer is an integer formed by
concatenating the bytes in the array using little-endian byte ordering.

The Nock for constructing a `ByteArray` uses the `length`, `add`,
`folder` and `lsh` functions from the Anoma hoon stdlib. See the [code
comment](fa068a30e7/src/Juvix/Compiler/Nockma/StdlibFunction.hs (L37))
for more details.

Example:

```
module test082;

import Stdlib.Prelude open;
import Stdlib.Debug.Trace open;

builtin bytearray
axiom ByteArray : Type;

builtin bytearray-from-list-byte
axiom mkByteArray : List Byte -> ByteArray;

builtin bytearray-size
axiom size : ByteArray -> Nat;

bs0 : ByteArray := mkByteArray [];

bs1 : ByteArray := mkByteArray [0x0; 0x0; 0x0];

bs2 : ByteArray := mkByteArray [0x1; 0x0; 0x0; 0x0];

bs3 : ByteArray := mkByteArray [0x2; 0x1];

bs4 : ByteArray := mkByteArray [0x100];

main : ByteArray :=
  trace (size bs0)
   >-> trace bs0
   >-> trace (size bs1)
    >-> trace bs1
    >-> trace (size bs2)
    >-> trace bs2
    >-> trace (size bs3)
    >-> trace bs3
    >-> trace (size bs4)
    >-> bs4;
```

Output using `tests/Anoma/Compilation/positive/test082.juvix`

```
$ juvix compile anoma -g test082.juvix
$ juvix dev nockma run test082.pretty.nockma
0
[0 0]
3
[3 0]
4
[4 1]
2
[2 258]
1
[1 0]
```
2024-08-13 11:13:27 +01:00
Paul Cadman
93b76ce7f0
Adapt Anoma builtins to new Anoma Node API (#2861) 2024-07-01 18:44:02 +01:00
Paul Cadman
371f8f2258
Support Anoma stdlib sign-detached API (#2798)
This PR adds support for the Anoma stdlib `sign-detached` API.

```
builtin anoma-sign-detached
axiom anomaSignDetached : {A : Type}
  -- message to sign
  -> A
  -- private key
  -> Nat
 -- signature
  -> Nat;
```

This corresponds to the
[sign_detached](https://hexdocs.pm/enacl/enacl.html#sign_detached-2)
libsodium API.

This is requried to support to new Anoma nullifier format:


d6a61451ae

Previously resource nullifiers were defined using `anomaSign`:

```
nullifier (r : Resource) (secretKey : Nat) : Nat :=
  anomaSign (anomaEncode (nullifierHeader, r)) secretKey;
```

They are now defined using `anomaSignDetached`:

```
nullifier (r : Resource) (secretKey : Nat) : Nat :=
  let encodedResource : Nat := anomaEncode (nullifierHeader, r) in
  anomaEncode (encodedResource , anomaSignDetached encodedResource secretKey);
```

This is so that a logic function can access the nullified resources
directly from the `nullifier` field.

## Evaluator Note

When decoding a public key, private key or signature from an integer
atom to a bytestring it's important to pad the bytestring to the
appropriate number of bytes. For example a private key must be 64 bytes
but the corresponding encoded integer may fit into 63 bytes or fewer
bytes (depending on leading zeros). This PR also fixes this issue by
adding a
[`atomToByteStringLen`](c68c7187b1/src/Juvix/Compiler/Nockma/Encoding/ByteString.hs (L14))
function with also accepts the expected size of the resulting
bytestring.
2024-06-03 19:07:56 +02:00
Paul Cadman
e30905ae95
Support Anoma stdlib APIs sign and verify (#2788)
This PR adds support for the Anoma stdlib `sign` and `verify` APIs.

```
builtin anoma-sign
axiom anomaSign : {A : Type}
  -- message to sign
  -> A
  -- secret key
  -> Nat
  -- signed message
  -> Nat;

builtin anoma-verify
axiom anomaVerify : {A : Type}
  -- signed message to verify 
  -> Nat 
  -- public key
  -> Nat 
  -- message with signature removed
  -> A;
```

These correspond to the
[`sign`](https://hexdocs.pm/enacl/enacl.html#sign-2) and
[`sign_open`](https://hexdocs.pm/enacl/enacl.html#sign_open-2) APIs from
libsodium respectively.

If signature verification fails in `anomaVerify`, the Anoma program
exits. We copy this behaviour in the evaluator by throwing an error in
this case.

## Notes

The Haskell Ed25519 library does not support `sign_open`. Its
verification function returns Bool, i.e it checks that the signature is
valid. The signed message is simply the concatenation of the signature
(64 bytes) and the original message so I added a function to remove the
signature from a signed message.
2024-05-28 09:02:03 +01:00
Paul Cadman
830bf04275
Support Anoma stdlib API verifyDetached (#2785)
This PR adds support for`anomaVerifyDetached` stdlib API via a Juvix
builtin.

It has signature:

```
builtin anoma-verify-detached
axiom anomaVerifyDetached : {A : Type}
   --- signature
  -> Nat 
   --- message
  -> A
   --- public key
  -> Nat
 -> Bool;
```

The [ed25519](https://hackage.haskell.org/package/ed25519) library is
used in the evaluator becuase Anoma uses ed25519 signatures
(https://hexdocs.pm/enacl/enacl.html).

---------

Co-authored-by: Jan Mas Rovira <janmasrovira@gmail.com>
2024-05-23 13:40:05 +01:00
Paul Cadman
60bffcfeb8
refactor: Add Anoma Node in Tree language (#2784)
Similarly to how the Cairo operations are handled we add a separate Tree
language Node for Anoma operations instead of handling them as an Unop
Node.

This is necessary because we need to add support for new Anoma
operations that are not unary.

This PR also adds support for `anoma-encode` and `anoma-decode`
functions in `jvt` tree source files which was missed in the previous
PRs.
2024-05-17 09:14:05 +01:00
Łukasz Czajka
ee2f8aefbc
Fix memory access order in the JuvixReg to CASM translation. (#2697)
Cairo VM imposes restrictions on memory access order stricter than
described in the documentation, which necessitates changing the
compilation concept for local variables.

Summary
-------------

To ensure that memory is accessed sequentially at all times, we divide
instructions into basic blocks. Within each basic block, the `ap` offset
(i.e. how much `ap` increased since the beginning of the block) is known
at each instruction, which allows to statically associate `fp` offsets
to local variables while still generating only sequential assignments to
`[ap]` with increasing `ap`. When the `ap` offset can no longer be
statically determined for new local variables (e.g. due to an
intervening recursive call), we switch to the next basic block by
calling it with the `call` instruction. The arguments of the basic block
call are the variables live at the beginning of the called block. Note
that the `fp` offsets of "old" variables are still statically determined
even after the current `ap` offset becomes unknown -- the arbitrary
increase of `ap` does not influence the previous variable associations.
Hence, we can transfer the needed local variables to the next basic
block.

Example
-----------

The JuvixReg function
```
function f(integer) : integer {
  tmp[0] = add arg[0] 1;
  tmp[1] = call g(tmp[0]);
  tmp[2] = add tmp[1] arg[0];
  tmp[3] = mul tmp[2] 2;
  tmp[4] = call g(tmp[2]);
  tmp[5] = add tmp[4] tmp[3];
  ret tmp[5];
}
```
is compiled to
```
f:
  -- code for basic block 1
  [ap] = [fp - 3] + 1; ap++
  -- now [fp] is tmp[0], because fp = ap at function start (ap offset is zero) 
  -- transfer call argument (in this case, could be optimized away)
  [ap] = [fp]; ap++
  call g
  -- now [ap - 1] contains the result tmp[1] (it is already a call argument now)
  -- we additionally transfer arg[0] which is live in the next block
  [ap] = [fp - 3]; ap++
  call rel 3
  ret
  nop

  -- code for basic block 2
  -- the above "call rel" jumps here
  -- [fp - 4] is tmp[1] 
  -- [fp - 3] is arg[0]
  [ap] = [fp - 4] + [fp - 3]; ap++
  -- now [fp] is tmp[2]
  [ap] = [fp] * 2; ap++
  -- now [fp + 1] is tmp[3]
  [ap] = [fp]; ap++
  call g
  -- now [ap - 1] is tmp[4]
  [ap] = [fp + 1]; ap++
  call rel 3
  ret
  nop

  -- code for basic block 3
  -- [fp - 4] is tmp[4]
  -- [fp - 3] is tmp[3]
  [ap] = [fp - 4] + [fp - 3]; ap++
  -- now [fp] is tmp[5]
  -- the next assignment could be optimized away in this case
  [ap] = [fp]; ap++
  ret  
```
There are three basic blocks separated by the `call` instructions. In
each basic block, we know statically the `ap` offset at each instruction
(i.e. how much `ap` increased since the beginning of the block). We can
therefore associate the temporary variables with `[fp + k]` for
appropriate `k`. At basic block boundaries we transfer live temporary
variables as arguments for the call to the next basic block.

Checklist
------------
- [x] Divide JuvixReg instructions into [basic
blocks](https://en.wikipedia.org/wiki/Basic_block).
- [x] Implement liveness analysis for each basic block.
- [x] Translate transitions between basic blocks into CASM relative
calls with local live variable transfer.
- [x] Tests for the translation from JuvixReg to Cairo bytecode executed
with the Cairo VM
2024-03-27 10:40:24 +01:00
Paul Cadman
2e006421a5
Support nockma scry (#2678)
This PR adds support for Anoma/Nockma scry OP. It is used for obtaining
values from the Anoma storage (key value store). See the [linked
issue](https://github.com/anoma/juvix/issues/2672) for details on scry.

This PR adds support for scry to the Nockma language and compilation
from the frontend via a builtin: `anoma-get`:

```
builtin anoma-get
axiom anomaGet : {Value Key : Type} -> Key -> Value
```

In the backend, the `Value` and `Key` types could be anything, they will
depend on choices of Anoma applications. The type of the returned
`Value` is unchecked. It's currently the responsibility of the user to
match the annotated type with the type of data in storage.

We will not put this builtin in the standard library. It will be exposed
in the anoma-juvix library. It's likely that the frontend `anomaGet`
function will evolve as we use it to write Anoma applications and learn
how they work.

* Closes https://github.com/anoma/juvix/issues/2672

---------

Co-authored-by: Jan Mas Rovira <janmasrovira@gmail.com>
2024-03-21 16:44:14 +01:00
Łukasz Czajka
0f713c7c84
JuvixReg to CASM translation (#2671)
* Closes #2562 

Checklist
---------

- [x] Translation from JuvixReg to CASM
- [x] CASM runtime
- [x] Juvix to CASM pipeline: combine the right transformations and
check prerequisites
- [x] CLI commands: add target `casm` to the `compile` commands
- [x] Tests:
   - [x] Test the translation from JuvixReg to CASM
   - [x] Test the entire pipeline from Juvix to CASM
2024-03-20 12:14:12 +01:00
Łukasz Czajka
795212b092
JuvixTree validation (#2616)
* Validation (type checking) of JuvixTree. Similar to JuvixAsm
validation, will help with debugging.
* Depends on #2608
2024-02-06 15:46:55 +01:00
Jan Mas Rovira
10e2a23239
Translation from Juvix Tree to Nockma (#2614)
This PR replaces the JuvixAsm -> Nockma translation with a JuvixTree ->
Nockma translation.

We can now enable some of the JuvixTree tests that did not work with
JuvixAsm->Nockma because they were too slow.


## Notes

We have changed [test031: temp stack with
branching](22ee87f0e7/tests/Tree/positive/test031.jvt)
to avoid using negative numbers (because negative integers are not
supported in Nockma).

Three tree tests trace/output lists. Lists are serialised differently by
the asm and nockma pretty printers so they cannot share a single test
output file. We have created separate nockma output files for these
tests (see eg.
[test028.nockma.out](22ee87f0e7/tests/Tree/positive/out/test028.nockma.out)).

* Closes https://github.com/anoma/juvix/issues/2606

---------

Co-authored-by: Paul Cadman <git@paulcadman.dev>
Co-authored-by: Lukasz Czajka <lukasz@heliax.dev>
2024-02-06 08:33:14 +00:00
Łukasz Czajka
7b0a11d570
JuvixTree negative evaluation tests (#2601)
* Adds negative tests for the JuvixTree evaluator
* Depends on #2600 
* Depends on #2599 
* Depends on #2598 
* Depends on #2597 
* Depends on #2596 
* Depends on #2595 
* Depends on #2594 
* Depends on #2590
2024-02-01 12:58:38 +00:00
Łukasz Czajka
15c223d0f1
Filter out unreachable functions in JuvixTree (#2597)
* Adds the `FilterUnreachable` transformation in JuvixTree.
* Depends on #2596 
* Depends on #2595 
* Depends on #2594 
* Depends on #2590 
* Depends on #2589 
* Depends on #2587
2024-01-30 20:01:07 +00:00
Łukasz Czajka
c95fcb38c8
JuvixTree tests (#2587)
* Implements a translation from JuvixAsm to JuvixTree. It does not work
in general, but works for all code generated from Juvix and all JuvixAsm
tests.
* Adds the `juvix dev tree from-asm` command.
* Adds tests automatically converted from JuvixAsm tests.
* Depends on #2583
2024-01-25 18:02:06 +00:00