Merge branch 'testnet3' into testnet3

Signed-off-by: d0cd <23022326+d0cd@users.noreply.github.com>
This commit is contained in:
d0cd 2024-01-16 12:08:10 +08:00 committed by GitHub
commit e6d1a23402
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
970 changed files with 11315 additions and 6462 deletions

View File

@ -4,7 +4,7 @@ ls -la
cd foo && ls -la
# Run `leo run`.
$LEO run || exit
$LEO run main 0u32 1u32 || exit
# Assert that the 'build' folder exists.
if [ "$(ls -A build)" ]; then

View File

@ -4,11 +4,10 @@
ls -la
cd lottery && ls -la
# Run the play function.
$LEO run play || exit
# Execute the play function.
$LEO execute play || exit
# Run the script.
chmod +x ./run.sh || exit
export -f leo
./run.sh || exit
)
(
@ -17,14 +16,10 @@
ls -la
cd tictactoe && ls -la
# Create a new game.
$LEO run new || exit
# Run the make_move function.
$LEO run make_move || exit
# Execute the make_move function.
$LEO execute make_move || exit
# Run the script.
chmod +x ./run.sh || exit
export -f leo
./run.sh || exit
)
(
@ -33,9 +28,8 @@
ls -la
cd token && ls -la
# Run the mint_public function.
$LEO run mint_public || exit
# Execute the mint_public function.
$LEO execute mint_public || exit
# Run the script.
chmod +x ./run.sh || exit
export -f leo
./run.sh || exit
)

View File

@ -20,7 +20,7 @@ do
done
# Try to run `leo run`.
$LEO run || exit
$LEO run main 0u32 1u32 || exit
# Remove the dummy program.
cd .. && rm -rf dummy

5
.circleci/lottery/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.env
*.avm
*.prover
*.verifier
outputs/

View File

@ -0,0 +1,19 @@
# lottery.aleo
## Run Guide
To run this program, run:
```bash
leo run play
or
./run.sh
```
## Execute Guide
To execute this program, run:
```bash
leo execute play
```

View File

@ -0,0 +1,26 @@
program lottery.aleo;
record Ticket:
owner as address.private;
mapping num_winners:
key as u8.public;
value as u8.public;
function play:
cast self.caller into r0 as Ticket.record;
async play into r1;
output r0 as Ticket.record;
output r1 as lottery.aleo/play.future;
finalize play:
lte block.height 1000u32 into r0;
assert.eq r0 true;
rand.chacha into r1 as boolean;
assert.eq r1 true;
get.or_use num_winners[0u8] 0u8 into r2;
lt r2 5u8 into r3;
assert.eq r3 true;
add r2 1u8 into r4;
set r4 into num_winners[0u8];

View File

@ -0,0 +1,6 @@
{
"program": "lottery.aleo",
"version": "0.0.0",
"description": "",
"license": "MIT"
}

View File

@ -0,0 +1 @@
package = []

View File

@ -0,0 +1,6 @@
{
"program": "lottery.aleo",
"version": "0.0.0",
"description": "",
"license": "MIT"
}

10
.circleci/lottery/run.sh Normal file
View File

@ -0,0 +1,10 @@
#!/bin/bash
# First check that Leo is installed.
if ! command -v leo &> /dev/null
then
echo "leo is not installed."
exit
fi
# Run the lottery example
leo run play || exit

View File

@ -0,0 +1,30 @@
// The 'lottery' program.
program lottery.aleo {
mapping num_winners: u8 => u8;
record Ticket {
owner: address,
}
transition play() -> Ticket {
let ticket: Ticket = Ticket {
owner: self.caller,
};
return ticket then finalize();
}
finalize play() {
// Check that the lottery has not expired.
assert(block.height <= 1000u32);
// Randomly select whether or not the ticket is a winner.
assert(ChaCha::rand_bool());
// Check that the maximum number of winners have not been reached.
let winners: u8 = num_winners.get_or_use(0u8, 0u8);
assert(winners < 5u8);
num_winners.set(0u8, winners + 1u8);
}
}

View File

@ -8,9 +8,6 @@ leo() {
echo "Building and running the \`auction\` program..."
(
cd $EXAMPLES/auction || exit
$LEO run place_bid || exit
$LEO run resolve || exit
$LEO run finish || exit
chmod +x $EXAMPLES/auction/run.sh || exit
export -f leo || exit
@ -27,9 +24,6 @@ fi
echo "Building and running the \`basic_bank\` program..."
(
cd $EXAMPLES/basic_bank || exit
$LEO run issue || exit
$LEO run deposit || exit
$LEO run withdraw || exit
chmod +x $EXAMPLES/basic_bank/run.sh || exit
export -f leo || exit
@ -63,7 +57,7 @@ fi
echo "Building and running the \`bubblesort\` program..."
(
cd $EXAMPLES/bubblesort || exit
$LEO run bubble_sort || exit
$LEO run bubble_sort --file $EXAMPLES/bubblesort/inputs/bubblesort.in || exit
)
# Check that the bubblesort program ran successfully.
EXITCODE=$?
@ -76,7 +70,7 @@ fi
echo "Building and running the \`core\` program..."
(
cd $EXAMPLES/core || exit
$LEO run main || exit
$LEO run main --file $EXAMPLES/core/inputs/core.in || exit
)
# Check that the core program ran successfully.
EXITCODE=$?
@ -89,7 +83,7 @@ fi
echo "Building and running the \`groups\` program..."
(
cd $EXAMPLES/groups || exit
$LEO run main || exit
$LEO run main --file $EXAMPLES/groups/inputs/groups.in || exit
)
# Check that the groups program ran successfully.
EXITCODE=$?
@ -102,7 +96,7 @@ fi
echo "Building and running the \`hackers-delight/ntzdebruijn\` program..."
(
cd $EXAMPLES/hackers-delight/ntzdebruijn || exit
$LEO run || exit
$LEO run main --file $EXAMPLES/hackers-delight/ntzdebruijn/inputs/ntzdebruijn.in || exit
)
# Check that the hackers-delight/ntzdebruijn program ran successfully.
EXITCODE=$?
@ -115,7 +109,7 @@ fi
echo "Building and running the \`hackers-delight/ntzgaudet\` program..."
(
cd $EXAMPLES/hackers-delight/ntzgaudet || exit
$LEO run || exit
$LEO run main --file $EXAMPLES/hackers-delight/ntzgaudet/inputs/ntzgaudet.in || exit
)
# Check that the hackers-delight/ntzgaudet program ran successfully.
EXITCODE=$?
@ -128,7 +122,7 @@ fi
echo "Building and running the \`hackers-delight/ntzloops\` program..."
(
cd $EXAMPLES/hackers-delight/ntzloops || exit
$LEO run || exit
$LEO run main --file $EXAMPLES/hackers-delight/ntzloops/inputs/ntzloops.in || exit
)
# Check that the hackers-delight/ntzloops program ran successfully.
EXITCODE=$?
@ -141,7 +135,7 @@ fi
echo "Building and running the \`hackers-delight/ntzmasks\` program..."
(
cd $EXAMPLES/hackers-delight/ntzmasks || exit
$LEO run || exit
$LEO run main --file $EXAMPLES/hackers-delight/ntzmasks/inputs/ntzmasks.in || exit
)
# Check that the hackers-delight/ntzmasks program ran successfully.
EXITCODE=$?
@ -154,7 +148,7 @@ fi
echo "Building and running the \`hackers-delight/ntzreisers\` program..."
(
cd $EXAMPLES/hackers-delight/ntzreisers || exit
$LEO run || exit
$LEO run main --file $EXAMPLES/hackers-delight/ntzreisers/inputs/ntzreisers.in || exit
)
# Check that the hackers-delight/ntzreisers program ran successfully.
EXITCODE=$?
@ -167,7 +161,7 @@ fi
echo "Building and running the \`hackers-delight/ntzseals\` program..."
(
cd $EXAMPLES/hackers-delight/ntzseals || exit
$LEO run || exit
$LEO run main --file $EXAMPLES/hackers-delight/ntzseals/inputs/ntzseals.in || exit
)
# Check that the hackers-delight/ntzseals program ran successfully.
EXITCODE=$?
@ -180,7 +174,7 @@ fi
echo "Building and running the \`hackers-delight/ntzsearchtree\` program..."
(
cd $EXAMPLES/hackers-delight/ntzsearchtree || exit
$LEO run || exit
$LEO run main --file $EXAMPLES/hackers-delight/ntzsearchtree/inputs/ntzsearchtree.in || exit
)
# Check that the hackers-delight/ntzsearchtree program ran successfully.
EXITCODE=$?
@ -193,7 +187,7 @@ fi
echo "Building and running the \`hackers-delight/ntzsmallvals\` program..."
(
cd $EXAMPLES/hackers-delight/ntzsmallvals || exit
$LEO run || exit
$LEO run main --file $EXAMPLES/hackers-delight/ntzsmallvals/inputs/ntzsmallvals.in || exit
)
# Check that the hackers-delight/ntzsmallvals program ran successfully.
EXITCODE=$?
@ -206,7 +200,7 @@ fi
echo "Building and running the \`helloworld\` program..."
(
cd $EXAMPLES/helloworld || exit
$LEO run main || exit
$LEO run main --file $EXAMPLES/helloworld/inputs/helloworld.in || exit
)
# Check that the helloworld program ran successfully.
EXITCODE=$?
@ -222,10 +216,10 @@ echo "Building and running the \`interest\` programs..."
cd $EXAMPLES/interest || exit
# Run the fixed period interest program.
$LEO run fixed_iteration_interest || exit
$LEO run fixed_iteration_interest --file $EXAMPLES/interest/inputs/fixed.in || exit
# Run the bounded period interest program.
$LEO run bounded_iteration_interest || exit
$LEO run bounded_iteration_interest --file $EXAMPLES/interest/inputs/bounded.in || exit
)
# Check that the interest programs ran successfully.
EXITCODE=$?
@ -238,7 +232,7 @@ fi
echo "Building and running the \`message\` program..."
(
cd $EXAMPLES/message || exit
$LEO run main || exit
$LEO run main --file $EXAMPLES/message/inputs/message.in || exit
)
# Check that the message program ran successfully.
EXITCODE=$?
@ -252,7 +246,7 @@ echo "Building and running the \`tictactoe\` program..."
(
cd $EXAMPLES/tictactoe || exit
$LEO run new || exit
$LEO run make_move || exit
$LEO run make_move --file $EXAMPLES/tictactoe/inputs/tictactoe.in || exit
chmod +x $EXAMPLES/tictactoe/run.sh || exit
export -f leo
@ -271,10 +265,10 @@ echo "Building and running the \`simple_token\` programs..."
cd $EXAMPLES/simple_token || exit
# Run the mint program.
$LEO run mint
$LEO run mint --file $EXAMPLES/simple_token/inputs/mint.in || exit
# Run the transfer program.
$LEO run transfer
$LEO run transfer --file $EXAMPLES/simple_token/inputs/transfer.in || exit
)
# Check that the simple token programs ran successfully.
EXITCODE=$?
@ -288,23 +282,9 @@ echo "Building and running the \`token\` program..."
(
cd $EXAMPLES/token || exit
# Run the mint_public function.
$LEO run mint_public || exit
# Run the mint_private function.
$LEO run mint_private || exit
# Run the transfer_public function.
$LEO run transfer_public || exit
# Run the transfer_private function.
$LEO run transfer_private || exit
# Run the transfer_private_to_public function.
$LEO run transfer_private_to_public || exit
# Run the transfer_public_to_private function.
$LEO run transfer_public_to_private || exit
chmod +x $EXAMPLES/token/run.sh || exit
export -f leo
$EXAMPLES/token/run.sh || exit
)
# Check that the token program ran successfully.
EXITCODE=$?
@ -317,7 +297,7 @@ fi
echo "Building and running the \`twoadicity\` program..."
(
cd $EXAMPLES/twoadicity || exit
$LEO run main || exit
$LEO run main --file $EXAMPLES/twoadicity/inputs/twoadicity.in || exit
)
# Check that the two-adicity program ran successfully.
EXITCODE=$?
@ -332,7 +312,7 @@ echo "Building and running the \`vote\` program..."
cd $EXAMPLES/vote || exit
chmod +x $EXAMPLES/vote/run.sh || exit
export -f leo
export -f leo || exit
$EXAMPLES/vote/run.sh || exit
)
# Check that the vote program ran successfully.

5
.circleci/tictactoe/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.env
*.avm
*.prover
*.verifier
outputs/

View File

@ -0,0 +1,82 @@
<!-- # ⭕ Tic-Tac-Toe -->
[//]: # (<img alt="workshop/tictactoe" width="1412" src="../.resources/tictactoe.png">)
A standard game of Tic-Tac-Toe in Leo.
⭕ ❕ ⭕ ❕ ❌
⭕ ❕ ⁣❌ ❕ ⭕
❌ ❕ ❌ ❕ ⭕
## Representing State
Leo allows users to define composite data types with the `struct` keyword.
The game board is represented by a struct called `Board`, which contains three `Row`s.
An alternative representation would be to use an array, however, these are not yet supported in Leo.
## Language Features
- `struct` declarations
- conditional statements
- early termination. Leo allows users to return from a function early using the `return` keyword.
## Running the Program
Leo provides users with a command line interface for compiling and running Leo programs.
Users may either specify input values via the command line or provide an input file in `inputs/`.
### Providing inputs via the command line.
1. Run
```bash
leo run <function_name> <input_1> <input_2> ...
```
See `./run.sh` for an example.
### Using an input file.
1. Modify `inputs/tictactoe.in` with the desired inputs.
2. Run
```bash
leo run <function_name>
```
## Executing the Program
```bash
leo execute <function_name> <input_1> <input_2> ...
```
## Playing the Game
### 1. Create a new game board
```bash
leo run new
```
| | | |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 0 | 0 |
| 0 | 0 | 0 |
### 2. Player 1 makes a move
```bash
leo run make_move 1u8 1u8 1u8 "{ r1: { c1: 0u8, c2: 0u8, c3: 0u8 }, r2: { c1: 0u8, c2: 0u8, c3: 0u8 }, r3: { c1: 0u8, c2: 0u8, c3: 0u8 } }"
```
| | | |
|---|---|---|
| 1 | 0 | 0 |
| 0 | 0 | 0 |
| 0 | 0 | 0 |
### 3. Player 2 makes a move
```bash
leo run make_move 2u8 2u8 2u8 "{ r1: { c1: 1u8, c2: 0u8, c3: 0u8 }, r2: { c1: 0u8, c2: 0u8, c3: 0u8 }, r3: { c1: 0u8, c2: 0u8, c3: 0u8 } }"
```
| | | |
|---|---|---|
| 1 | 0 | 0 |
| 0 | 2 | 0 |
| 0 | 0 | 0 |

View File

@ -0,0 +1,237 @@
program tictactoe.aleo;
struct Row:
c1 as u8;
c2 as u8;
c3 as u8;
struct Board:
r1 as Row;
r2 as Row;
r3 as Row;
function new:
cast 0u8 0u8 0u8 into r0 as Row;
cast 0u8 0u8 0u8 into r1 as Row;
cast 0u8 0u8 0u8 into r2 as Row;
cast r0 r1 r2 into r3 as Board;
output r3 as Board.private;
closure check_for_win:
input r0 as Board;
input r1 as u8;
is.eq r0.r1.c1 r1 into r2;
is.eq r0.r1.c2 r1 into r3;
and r2 r3 into r4;
is.eq r0.r1.c3 r1 into r5;
and r4 r5 into r6;
is.eq r0.r2.c1 r1 into r7;
is.eq r0.r2.c2 r1 into r8;
and r7 r8 into r9;
is.eq r0.r2.c3 r1 into r10;
and r9 r10 into r11;
or r6 r11 into r12;
is.eq r0.r3.c1 r1 into r13;
is.eq r0.r3.c3 r1 into r14;
and r13 r14 into r15;
is.eq r0.r3.c3 r1 into r16;
and r15 r16 into r17;
or r12 r17 into r18;
is.eq r0.r1.c1 r1 into r19;
is.eq r0.r2.c1 r1 into r20;
and r19 r20 into r21;
is.eq r0.r3.c1 r1 into r22;
and r21 r22 into r23;
or r18 r23 into r24;
is.eq r0.r1.c2 r1 into r25;
is.eq r0.r2.c3 r1 into r26;
and r25 r26 into r27;
is.eq r0.r3.c2 r1 into r28;
and r27 r28 into r29;
or r24 r29 into r30;
is.eq r0.r1.c3 r1 into r31;
is.eq r0.r2.c3 r1 into r32;
and r31 r32 into r33;
is.eq r0.r3.c3 r1 into r34;
and r33 r34 into r35;
or r30 r35 into r36;
is.eq r0.r1.c1 r1 into r37;
is.eq r0.r2.c2 r1 into r38;
and r37 r38 into r39;
is.eq r0.r3.c3 r1 into r40;
and r39 r40 into r41;
or r36 r41 into r42;
is.eq r0.r1.c3 r1 into r43;
is.eq r0.r2.c2 r1 into r44;
and r43 r44 into r45;
is.eq r0.r3.c1 r1 into r46;
and r45 r46 into r47;
or r42 r47 into r48;
output r48 as boolean;
function make_move:
input r0 as u8.private;
input r1 as u8.private;
input r2 as u8.private;
input r3 as Board.private;
is.eq r0 1u8 into r4;
is.eq r0 2u8 into r5;
or r4 r5 into r6;
assert.eq r6 true;
lte 1u8 r1 into r7;
lte r1 3u8 into r8;
and r7 r8 into r9;
assert.eq r9 true;
lte 1u8 r2 into r10;
lte r2 3u8 into r11;
and r10 r11 into r12;
assert.eq r12 true;
is.eq r1 1u8 into r13;
is.eq r2 1u8 into r14;
and r13 r14 into r15;
is.eq r3.r1.c1 0u8 into r16;
and r15 r16 into r17;
is.eq r1 1u8 into r18;
is.eq r2 2u8 into r19;
and r18 r19 into r20;
is.eq r3.r1.c2 0u8 into r21;
and r20 r21 into r22;
is.eq r1 1u8 into r23;
is.eq r2 3u8 into r24;
and r23 r24 into r25;
is.eq r3.r1.c3 0u8 into r26;
and r25 r26 into r27;
is.eq r1 2u8 into r28;
is.eq r2 1u8 into r29;
and r28 r29 into r30;
is.eq r3.r2.c1 0u8 into r31;
and r30 r31 into r32;
is.eq r1 2u8 into r33;
is.eq r2 2u8 into r34;
and r33 r34 into r35;
is.eq r3.r2.c2 0u8 into r36;
and r35 r36 into r37;
is.eq r1 2u8 into r38;
is.eq r2 3u8 into r39;
and r38 r39 into r40;
is.eq r3.r2.c3 0u8 into r41;
and r40 r41 into r42;
is.eq r1 3u8 into r43;
is.eq r2 1u8 into r44;
and r43 r44 into r45;
is.eq r3.r3.c1 0u8 into r46;
and r45 r46 into r47;
is.eq r1 3u8 into r48;
is.eq r2 2u8 into r49;
and r48 r49 into r50;
is.eq r3.r3.c2 0u8 into r51;
and r50 r51 into r52;
is.eq r1 3u8 into r53;
is.eq r2 3u8 into r54;
and r53 r54 into r55;
is.eq r3.r3.c3 0u8 into r56;
and r55 r56 into r57;
ternary r57 r0 r3.r3.c3 into r58;
ternary r52 r0 r3.r3.c2 into r59;
ternary r52 r3.r3.c3 r58 into r60;
ternary r47 r0 r3.r3.c1 into r61;
ternary r47 r3.r3.c2 r59 into r62;
ternary r47 r3.r3.c3 r60 into r63;
ternary r42 r0 r3.r2.c3 into r64;
ternary r42 r3.r3.c1 r61 into r65;
ternary r42 r3.r3.c2 r62 into r66;
ternary r42 r3.r3.c3 r63 into r67;
ternary r37 r0 r3.r2.c2 into r68;
ternary r37 r3.r2.c3 r64 into r69;
ternary r37 r3.r3.c1 r65 into r70;
ternary r37 r3.r3.c2 r66 into r71;
ternary r37 r3.r3.c3 r67 into r72;
ternary r32 r0 r3.r2.c1 into r73;
ternary r32 r3.r2.c2 r68 into r74;
ternary r32 r3.r2.c3 r69 into r75;
ternary r32 r3.r3.c1 r70 into r76;
ternary r32 r3.r3.c2 r71 into r77;
ternary r32 r3.r3.c3 r72 into r78;
ternary r27 r0 r3.r1.c3 into r79;
ternary r27 r3.r2.c1 r73 into r80;
ternary r27 r3.r2.c2 r74 into r81;
ternary r27 r3.r2.c3 r75 into r82;
ternary r27 r3.r3.c1 r76 into r83;
ternary r27 r3.r3.c2 r77 into r84;
ternary r27 r3.r3.c3 r78 into r85;
ternary r22 r0 r3.r1.c2 into r86;
ternary r22 r3.r1.c3 r79 into r87;
ternary r22 r3.r2.c1 r80 into r88;
ternary r22 r3.r2.c2 r81 into r89;
ternary r22 r3.r2.c3 r82 into r90;
ternary r22 r3.r3.c1 r83 into r91;
ternary r22 r3.r3.c2 r84 into r92;
ternary r22 r3.r3.c3 r85 into r93;
ternary r17 r0 r3.r1.c1 into r94;
ternary r17 r3.r1.c2 r86 into r95;
ternary r17 r3.r1.c3 r87 into r96;
ternary r17 r3.r2.c1 r88 into r97;
ternary r17 r3.r2.c2 r89 into r98;
ternary r17 r3.r2.c3 r90 into r99;
ternary r17 r3.r3.c1 r91 into r100;
ternary r17 r3.r3.c2 r92 into r101;
ternary r17 r3.r3.c3 r93 into r102;
cast r94 r95 r96 into r103 as Row;
cast r97 r98 r99 into r104 as Row;
cast r100 r101 r102 into r105 as Row;
cast r103 r104 r105 into r106 as Board;
call check_for_win r106 1u8 into r107;
call check_for_win r106 2u8 into r108;
not r107 into r109;
and r109 r108 into r110;
ternary r110 r106.r1.c1 r106.r1.c1 into r111;
not r107 into r112;
and r112 r108 into r113;
ternary r113 r106.r1.c2 r106.r1.c2 into r114;
not r107 into r115;
and r115 r108 into r116;
ternary r116 r106.r1.c3 r106.r1.c3 into r117;
cast r111 r114 r117 into r118 as Row;
not r107 into r119;
and r119 r108 into r120;
ternary r120 r106.r2.c1 r106.r2.c1 into r121;
not r107 into r122;
and r122 r108 into r123;
ternary r123 r106.r2.c2 r106.r2.c2 into r124;
not r107 into r125;
and r125 r108 into r126;
ternary r126 r106.r2.c3 r106.r2.c3 into r127;
cast r121 r124 r127 into r128 as Row;
not r107 into r129;
and r129 r108 into r130;
ternary r130 r106.r3.c1 r106.r3.c1 into r131;
not r107 into r132;
and r132 r108 into r133;
ternary r133 r106.r3.c2 r106.r3.c2 into r134;
not r107 into r135;
and r135 r108 into r136;
ternary r136 r106.r3.c3 r106.r3.c3 into r137;
cast r131 r134 r137 into r138 as Row;
cast r118 r128 r138 into r139 as Board;
not r107 into r140;
and r140 r108 into r141;
ternary r141 2u8 0u8 into r142;
ternary r107 r106.r1.c1 r139.r1.c1 into r143;
ternary r107 r106.r1.c2 r139.r1.c2 into r144;
ternary r107 r106.r1.c3 r139.r1.c3 into r145;
cast r143 r144 r145 into r146 as Row;
ternary r107 r106.r2.c1 r139.r2.c1 into r147;
ternary r107 r106.r2.c2 r139.r2.c2 into r148;
ternary r107 r106.r2.c3 r139.r2.c3 into r149;
cast r147 r148 r149 into r150 as Row;
ternary r107 r106.r3.c1 r139.r3.c1 into r151;
ternary r107 r106.r3.c2 r139.r3.c2 into r152;
ternary r107 r106.r3.c3 r139.r3.c3 into r153;
cast r151 r152 r153 into r154 as Row;
cast r146 r150 r154 into r155 as Board;
ternary r107 1u8 r142 into r156;
output r155 as Board.private;
output r156 as u8.private;

View File

@ -0,0 +1,6 @@
{
"program": "tictactoe.aleo",
"version": "0.0.0",
"description": "",
"license": "MIT"
}

View File

@ -0,0 +1,18 @@
// The `new` function does not take any inputs.
[new]
// Inputs for the `make_move` function.
// - `player` : A u8 representing the player making the move. 1 for player 1, 2 for player 2.
// - `row` : A u8 representing the row to make the move in.
// - `column` : A u8 representing the column to make the move in.
// - `board` : A representation of the board state.
[make_move]
player: u8 = 1u8;
row: u8 = 1u8;
col: u8 = 1u8;
board: Board = Board {
r1: Row { c1: 0u8, c2: 0u8, c3: 0u8 },
r2: Row { c1: 0u8, c2: 0u8, c3: 0u8 },
r3: Row { c1: 0u8, c2: 0u8, c3: 0u8 },
};

View File

@ -0,0 +1 @@
package = []

View File

@ -0,0 +1,6 @@
{
"program": "tictactoe.aleo",
"version": "0.0.0",
"description": "",
"license": "MIT"
}

157
.circleci/tictactoe/run.sh Normal file
View File

@ -0,0 +1,157 @@
#!/bin/bash
# First check that Leo is installed.
if ! command -v leo &> /dev/null
then
echo "leo is not installed."
exit
fi
# Create a new game.
echo "
###############################################################################
######## ########
######## STEP 0: Creating a new game of Tic-Tac-Toe ########
######## ########
######## | | | | ########
######## | | | | ########
######## | | | | ########
######## ########
###############################################################################
"
leo run new || exit
# Have the Player 1 make a move.
echo "
###############################################################################
######## ########
######## STEP 1: Player 1 makes the 1st move. ########
######## ########
######## | x | | | ########
######## | | | | ########
######## | | | | ########
######## ########
###############################################################################
"
leo run make_move 1u8 1u8 1u8 "{ r1: { c1: 0u8, c2: 0u8, c3: 0u8 }, r2: { c1: 0u8, c2: 0u8, c3: 0u8 }, r3: { c1: 0u8, c2: 0u8, c3: 0u8 } }" || exit
# Have the Player 2 make a move.
echo "
###############################################################################
######## ########
######## STEP 2: Player 2 makes the 2nd move. ########
######## ########
######## | x | | | ########
######## | | o | | ########
######## | | | | ########
######## ########
###############################################################################
"
leo run make_move 2u8 2u8 2u8 "{ r1: { c1: 1u8, c2: 0u8, c3: 0u8 }, r2: { c1: 0u8, c2: 0u8, c3: 0u8 }, r3: { c1: 0u8, c2: 0u8, c3: 0u8 } }" || exit
# Have the Player 1 make a move.
echo "
###############################################################################
######## ########
######## STEP 3: Player 1 makes the 3rd move. ########
######## ########
######## | x | | | ########
######## | | o | | ########
######## | x | | | ########
######## ########
###############################################################################
"
leo run make_move 1u8 3u8 1u8 "{ r1: { c1: 1u8, c2: 0u8, c3: 0u8 }, r2: { c1: 0u8, c2: 2u8, c3: 0u8 }, r3: { c1: 0u8, c2: 0u8, c3: 0u8 } }" || exit
# Have the Player 2 make a move.
echo "
###############################################################################
######## ########
######## STEP 4: Player 2 makes the 4th move. ########
######## ########
######## | x | | | ########
######## | o | o | | ########
######## | x | | | ########
######## ########
###############################################################################
"
leo run make_move 2u8 2u8 1u8 "{ r1: { c1: 1u8, c2: 0u8, c3: 0u8 }, r2: { c1: 0u8, c2: 2u8, c3: 0u8 }, r3: { c1: 1u8, c2: 0u8, c3: 0u8 } }" || exit
# Have the Player 1 make a move.
echo "
###############################################################################
######## ########
######## STEP 5: Player 1 makes the 5th move. ########
######## ########
######## | x | | | ########
######## | o | o | x | ########
######## | x | | | ########
######## ########
###############################################################################
"
leo run make_move 1u8 2u8 3u8 "{ r1: { c1: 1u8, c2: 0u8, c3: 0u8 }, r2: { c1: 2u8, c2: 2u8, c3: 0u8 }, r3: { c1: 1u8, c2: 0u8, c3: 0u8 } }" || exit
# Have the Player 2 make a move.
echo "
###############################################################################
######## ########
######## STEP 6: Player 2 makes the 6th move. ########
######## ########
######## | x | o | | ########
######## | o | o | x | ########
######## | x | | | ########
######## ########
###############################################################################
"
leo run make_move 2u8 1u8 2u8 "{ r1: { c1: 1u8, c2: 0u8, c3: 0u8 }, r2: { c1: 2u8, c2: 2u8, c3: 1u8 }, r3: { c1: 1u8, c2: 0u8, c3: 0u8 } }" || exit
# Have the Player 1 make a move.
echo "
###############################################################################
######## ########
######## STEP 7: Player 1 makes the 7th move. ########
######## ########
######## | x | o | | ########
######## | o | o | x | ########
######## | x | x | | ########
######## ########
###############################################################################
"
leo run make_move 1u8 3u8 2u8 "{ r1: { c1: 1u8, c2: 2u8, c3: 0u8 }, r2: { c1: 2u8, c2: 2u8, c3: 1u8 }, r3: { c1: 1u8, c2: 0u8, c3: 0u8 } }" || exit
# Have the Player 2 make a move.
echo "
###############################################################################
######## ########
######## STEP 8: Player 2 makes the 8th move. ########
######## ########
######## | x | o | | ########
######## | o | o | x | ########
######## | x | x | o | ########
######## ########
###############################################################################
"
leo run make_move 2u8 3u8 3u8 "{ r1: { c1: 1u8, c2: 2u8, c3: 0u8 }, r2: { c1: 2u8, c2: 2u8, c3: 1u8 }, r3: { c1: 1u8, c2: 1u8, c3: 0u8 } }" || exit
echo "
###############################################################################
######## ########
######## STEP 9: Player 1 makes the 9th move. ########
######## ########
######## | x | o | x | ########
######## | o | o | x | ########
######## | x | x | o | ########
######## ########
###############################################################################
"
leo run make_move 1u8 1u8 3u8 "{ r1: { c1: 1u8, c2: 2u8, c3: 0u8 }, r2: { c1: 2u8, c2: 2u8, c3: 1u8 }, r3: { c1: 1u8, c2: 1u8, c3: 2u8 } }" || exit
echo "
###############################################################################
######## ########
######## Game Complete! Players 1 & 2 Tied ########
######## ########
######## | x | o | x | ########
######## | o | o | x | ########
######## | x | x | o | ########
######## ########
###############################################################################
"

View File

@ -0,0 +1,111 @@
program tictactoe.aleo {
// A row in a tic tac toe board.
// - `c1` : The first entry in the row.
// - `c2` : The second entry in the row.
// - `c3` : The third entry in the row.
// A valid entry is either 0, 1, or 2, where 0 is empty, 1 corresponds to player 1, and 2 corresponds to player 2.
// Any other values are invalid.
struct Row {
c1: u8,
c2: u8,
c3: u8
}
// A tic tac toe board.
// - `r1` : The first row in the board.
// - `r2` : The second row in the board.
// - `r3` : The third row in the board.
struct Board {
r1: Row,
r2: Row,
r3: Row,
}
// Returns an empty board.
transition new() -> Board {
return Board {
r1: Row { c1: 0u8, c2: 0u8, c3: 0u8 },
r2: Row { c1: 0u8, c2: 0u8, c3: 0u8 },
r3: Row { c1: 0u8, c2: 0u8, c3: 0u8 },
};
}
// Returns `true` if there exists a row, column, or diagonal with all entries occupied by the same player.
// - `b` : A tic tac toe board.
// - `p` : A number corresponding to a player.
function check_for_win(b: Board, p: u8) -> bool {
return
(b.r1.c1 == p && b.r1.c2 == p && b.r1.c3 == p) || // row 1
(b.r2.c1 == p && b.r2.c2 == p && b.r2.c3 == p) || // row 2
(b.r3.c1 == p && b.r3.c3 == p && b.r3.c3 == p) || // row 3
(b.r1.c1 == p && b.r2.c1 == p && b.r3.c1 == p) || // column 1
(b.r1.c2 == p && b.r2.c3 == p && b.r3.c2 == p) || // column 2
(b.r1.c3 == p && b.r2.c3 == p && b.r3.c3 == p) || // column 3
(b.r1.c1 == p && b.r2.c2 == p && b.r3.c3 == p) || // diagonal
(b.r1.c3 == p && b.r2.c2 == p && b.r3.c1 == p); // other diagonal
}
// Returns an updated tic tac toe board with a move made by a player.
// Returns a `u8` corresponding to the player who won the game, or 0 if no one has won yet.
// - `player` : A number corresponding to a player.
// - `row` : The row of the move.
// - `col` : The column of the move.
// - `board` : A tic tac toe board.
// Assumes that `player` is either 1 or 2.
// Assumes that `row` and `col` are valid indices into the board.
// If an entry is already occupied, the move is invalid and the board is returned unchanged.
transition make_move(player: u8, row: u8, col: u8, board: Board) -> (Board, u8) {
// Check that inputs are valid.
assert(player == 1u8 || player == 2u8);
assert(1u8 <= row && row <= 3u8);
assert(1u8 <= col && col <= 3u8);
// Unpack the entries in the board into variables.
let r1c1: u8 = board.r1.c1;
let r1c2: u8 = board.r1.c2;
let r1c3: u8 = board.r1.c3;
let r2c1: u8 = board.r2.c1;
let r2c2: u8 = board.r2.c2;
let r2c3: u8 = board.r2.c3;
let r3c1: u8 = board.r3.c1;
let r3c2: u8 = board.r3.c2;
let r3c3: u8 = board.r3.c3;
// Update the appropriate entry with the given move.
if row == 1u8 && col == 1u8 && r1c1 == 0u8 {
r1c1 = player;
} else if row == 1u8 && col == 2u8 && r1c2 == 0u8 {
r1c2 = player;
} else if row == 1u8 && col == 3u8 && r1c3 == 0u8 {
r1c3 = player;
} else if row == 2u8 && col == 1u8 && r2c1 == 0u8 {
r2c1 = player;
} else if row == 2u8 && col == 2u8 && r2c2 == 0u8 {
r2c2 = player;
} else if row == 2u8 && col == 3u8 && r2c3 == 0u8 {
r2c3 = player;
} else if row == 3u8 && col == 1u8 && r3c1 == 0u8 {
r3c1 = player;
} else if row == 3u8 && col == 2u8 && r3c2 == 0u8 {
r3c2 = player;
} else if row == 3u8 && col == 3u8 && r3c3 == 0u8 {
r3c3 = player;
}
// Construct the updated game board.
let updated: Board = Board {
r1: Row { c1: r1c1, c2: r1c2, c3: r1c3 },
r2: Row { c1: r2c1, c2: r2c2, c3: r2c3 },
r3: Row { c1: r3c1, c2: r3c2, c3: r3c3 },
};
// Check if the game is over.
if check_for_win(updated, 1u8) {
return (updated, 1u8);
} else if check_for_win(updated, 2u8) {
return (updated, 2u8);
} else {
return (updated, 0u8);
}
}
}

5
.circleci/token/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.env
*.avm
*.prover
*.verifier
outputs/

12
.circleci/token/README.md Normal file
View File

@ -0,0 +1,12 @@
<!-- # 🪙 Token -->
[//]: # (<img alt="workshop/token" width="1412" src="../.resources/token.png">)
A transparent & shielded custom token in Leo.
## Run Guide
To run this program, run:
```bash
./run.sh
```

View File

@ -0,0 +1,93 @@
program token.aleo;
record token:
owner as address.private;
amount as u64.private;
mapping account:
key as address.public;
value as u64.public;
function mint_public:
input r0 as address.public;
input r1 as u64.public;
async mint_public r0 r1 into r2;
output r2 as token.aleo/mint_public.future;
finalize mint_public:
input r0 as address.public;
input r1 as u64.public;
get.or_use account[r0] 0u64 into r2;
add r2 r1 into r3;
set r3 into account[r0];
function mint_private:
input r0 as address.private;
input r1 as u64.private;
cast r0 r1 into r2 as token.record;
output r2 as token.record;
function transfer_public:
input r0 as address.public;
input r1 as u64.public;
async transfer_public self.caller r0 r1 into r2;
output r2 as token.aleo/transfer_public.future;
finalize transfer_public:
input r0 as address.public;
input r1 as address.public;
input r2 as u64.public;
get.or_use account[r0] 0u64 into r3;
sub r3 r2 into r4;
set r4 into account[r0];
get.or_use account[r1] 0u64 into r5;
add r5 r2 into r6;
set r6 into account[r1];
function transfer_private:
input r0 as token.record;
input r1 as address.private;
input r2 as u64.private;
sub r0.amount r2 into r3;
cast r0.owner r3 into r4 as token.record;
cast r1 r2 into r5 as token.record;
output r4 as token.record;
output r5 as token.record;
function transfer_private_to_public:
input r0 as token.record;
input r1 as address.public;
input r2 as u64.public;
sub r0.amount r2 into r3;
cast r0.owner r3 into r4 as token.record;
async transfer_private_to_public r1 r2 into r5;
output r4 as token.record;
output r5 as token.aleo/transfer_private_to_public.future;
finalize transfer_private_to_public:
input r0 as address.public;
input r1 as u64.public;
get.or_use account[r0] 0u64 into r2;
add r2 r1 into r3;
set r3 into account[r0];
function transfer_public_to_private:
input r0 as address.public;
input r1 as u64.public;
cast r0 r1 into r2 as token.record;
async transfer_public_to_private self.caller r1 into r3;
output r2 as token.record;
output r3 as token.aleo/transfer_public_to_private.future;
finalize transfer_public_to_private:
input r0 as address.public;
input r1 as u64.public;
get.or_use account[r0] 0u64 into r2;
sub r2 r1 into r3;
set r3 into account[r0];

View File

@ -0,0 +1,6 @@
{
"program": "token.aleo",
"version": "0.0.0",
"description": "",
"license": "MIT"
}

1
.circleci/token/leo.lock Normal file
View File

@ -0,0 +1 @@
package = []

View File

@ -0,0 +1,6 @@
{
"program": "token.aleo",
"version": "0.0.0",
"description": "",
"license": "MIT"
}

239
.circleci/token/run.sh Normal file
View File

@ -0,0 +1,239 @@
#!/bin/bash
# First check that Leo is installed.
if ! command -v leo &> /dev/null
then
echo "leo is not installed."
exit
fi
# The private key and address of Alice.
# Swap these into program.json, when running transactions as the first bidder.
# "private_key": "APrivateKey1zkp1w8PTxrRgGfAtfKUSq43iQyVbdQHfhGbiNPEg2LVSEXR",
# "address": "aleo13ssze66adjjkt795z9u5wpq8h6kn0y2657726h4h3e3wfnez4vqsm3008q"
# The private key and address of Bob.
# Swap these into program.json, when running transactions as the second bidder.
# "private_key": "APrivateKey1zkpFo72g7N9iFt3JzzeG8CqsS5doAiXyFvNCgk2oHvjRCzF"
# "address": "aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z"
# Swap in the private key of Alice.
echo "
NETWORK=testnet3
PRIVATE_KEY=APrivateKey1zkp1w8PTxrRgGfAtfKUSq43iQyVbdQHfhGbiNPEg2LVSEXR
" > .env
# Publicly mint 100 tokens for Alice.
echo "
###############################################################################
######## ########
######## STEP 1: Publicly mint 100 tokens for Alice ########
######## ########
######## ----------------------------------------- ########
######## | PUBLIC BALANCES | ########
######## ----------------------------------------- ########
######## ----------------------------------------- ########
######## | Alice | 100 | ########
######## ----------------------------------------- ########
######## | Bob | 0 | ########
######## ----------------------------------------- ########
######## ########
######## ----------------------------------------- ########
######## | PRIVATE BALANCES | ########
######## ----------------------------------------- ########
######## ----------------------------------------- ########
######## | Alice | 0 | ########
######## ----------------------------------------- ########
######## | Bob | 0 | ########
######## ----------------------------------------- ########
######## ########
###############################################################################
"
leo run mint_public aleo13ssze66adjjkt795z9u5wpq8h6kn0y2657726h4h3e3wfnez4vqsm3008q 100u64
# Swap in the private key of Bob.
echo "
NETWORK=testnet3
PRIVATE_KEY=APrivateKey1zkpFo72g7N9iFt3JzzeG8CqsS5doAiXyFvNCgk2oHvjRCzF
" > .env
# Privately mint 100 tokens for Bob.
echo "
###############################################################################
######## ########
######## STEP 2: Privately mint 100 tokens for Bob ########
######## ########
######## ----------------------------------------- ########
######## | PUBLIC BALANCES | ########
######## ----------------------------------------- ########
######## ----------------------------------------- ########
######## | Alice | 100 | ########
######## ----------------------------------------- ########
######## | Bob | 0 | ########
######## ----------------------------------------- ########
######## ########
######## ----------------------------------------- ########
######## | PRIVATE BALANCES | ########
######## ----------------------------------------- ########
######## ----------------------------------------- ########
######## | Alice | 0 | ########
######## ----------------------------------------- ########
######## | Bob | 100 | ########
######## ----------------------------------------- ########
######## ########
###############################################################################
"
leo run mint_private aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z 100u64
# Swap in the private key of Alice.
echo "
NETWORK=testnet3
PRIVATE_KEY=APrivateKey1zkp1w8PTxrRgGfAtfKUSq43iQyVbdQHfhGbiNPEg2LVSEXR
" > .env
# Publicly transfer 10 tokens from Alice to Bob.
echo "
###############################################################################
######## ########
######## STEP 3: Publicly transfer 10 tokens from Alice to Bob ########
######## ########
######## ----------------------------------------- ########
######## | PUBLIC BALANCES | ########
######## ----------------------------------------- ########
######## ----------------------------------------- ########
######## | Alice | 90 | ########
######## ----------------------------------------- ########
######## | Bob | 10 | ########
######## ----------------------------------------- ########
######## ########
######## ----------------------------------------- ########
######## | PRIVATE BALANCES | ########
######## ----------------------------------------- ########
######## ----------------------------------------- ########
######## | Alice | 0 | ########
######## ----------------------------------------- ########
######## | Bob | 100 | ########
######## ----------------------------------------- ########
######## ########
###############################################################################
"
leo run transfer_public aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z 10u64
# Swap in the private key of Bob.
echo "
NETWORK=testnet3
PRIVATE_KEY=APrivateKey1zkpFo72g7N9iFt3JzzeG8CqsS5doAiXyFvNCgk2oHvjRCzF
" > .env
# Privately transfer 20 tokens from Bob to Alice.
echo "
###############################################################################
######## ########
######## STEP 4: Privately transfer 20 tokens from Bob to Alice ########
######## ########
######## ----------------------------------------- ########
######## | PUBLIC BALANCES | ########
######## ----------------------------------------- ########
######## ----------------------------------------- ########
######## | Alice | 90 | ########
######## ----------------------------------------- ########
######## | Bob | 10 | ########
######## ----------------------------------------- ########
######## ########
######## ----------------------------------------- ########
######## | PRIVATE BALANCES | ########
######## ----------------------------------------- ########
######## ----------------------------------------- ########
######## | Alice | 20 | ########
######## ----------------------------------------- ########
######## | Bob | 80 | ########
######## ----------------------------------------- ########
######## ########
###############################################################################
"
leo run transfer_private "{
owner: aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z.private,
amount: 100u64.private,
_nonce: 6586771265379155927089644749305420610382723873232320906747954786091923851913group.public
}" aleo13ssze66adjjkt795z9u5wpq8h6kn0y2657726h4h3e3wfnez4vqsm3008q 20u64
# Swap in the private key of Alice.
echo "
NETWORK=testnet3
PRIVATE_KEY=APrivateKey1zkp1w8PTxrRgGfAtfKUSq43iQyVbdQHfhGbiNPEg2LVSEXR
" > .env
# Convert 30 public tokens from Alice into 30 private tokens for Bob.
echo "
###############################################################################
######## ########
######## STEP 5: Convert 30 public tokens from Alice into 30 ########
######## private tokens for Bob. ########
######## ########
######## ----------------------------------------- ########
######## | PUBLIC BALANCES | ########
######## ----------------------------------------- ########
######## ----------------------------------------- ########
######## | Alice | 60 | ########
######## ----------------------------------------- ########
######## | Bob | 10 | ########
######## ----------------------------------------- ########
######## ########
######## ----------------------------------------- ########
######## | PRIVATE BALANCES | ########
######## ----------------------------------------- ########
######## ----------------------------------------- ########
######## | Alice | 20 | ########
######## ----------------------------------------- ########
######## | Bob | 110 | ########
######## ----------------------------------------- ########
######## ########
###############################################################################
"
leo run transfer_public_to_private aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z 30u64
# Swap in the private key of Bob.
echo "
NETWORK=testnet3
PRIVATE_KEY=APrivateKey1zkpFo72g7N9iFt3JzzeG8CqsS5doAiXyFvNCgk2oHvjRCzF
" > .env
# Convert 40 private tokens from Bob into 40 public tokens for Alice.
echo "
###############################################################################
######## ########
######## STEP 6: Convert 40 private tokens from Bob into 40 ########
######## public tokens for Alice. ########
######## ########
######## ----------------------------------------- ########
######## | PUBLIC BALANCES | ########
######## ----------------------------------------- ########
######## ----------------------------------------- ########
######## | Alice | 100 | ########
######## ----------------------------------------- ########
######## | Bob | 10 | ########
######## ----------------------------------------- ########
######## ########
######## ----------------------------------------- ########
######## | PRIVATE BALANCES | ########
######## ----------------------------------------- ########
######## ----------------------------------------- ########
######## | Alice | 20 | ########
######## ----------------------------------------- ########
######## | Bob | 70 | ########
######## ----------------------------------------- ########
######## ########
###############################################################################
"
leo run transfer_private_to_public "{
owner: aleo17vy26rpdhqx4598y5gp7nvaa9rk7tnvl6ufhvvf4calsrrqdaqyshdsf5z.private,
amount: 80u64.private,
_nonce: 1852830456042139988098466781381363679605019151318121788109768539956661608520group.public
}" aleo13ssze66adjjkt795z9u5wpq8h6kn0y2657726h4h3e3wfnez4vqsm3008q 40u64
# Swap in the private key of Alice.
# This is done to ensure that program.json is the same after every execution of ./run.sh.
echo "
NETWORK=testnet3
PRIVATE_KEY=APrivateKey1zkp1w8PTxrRgGfAtfKUSq43iQyVbdQHfhGbiNPEg2LVSEXR
" > .env

View File

@ -0,0 +1,128 @@
program token.aleo {
// On-chain storage of an `account` map, with `address` as the key,
// and `u64` as the value.
mapping account: address => u64;
record token {
// The token owner.
owner: address,
// The token amount.
amount: u64,
}
/* Mint */
// The function `mint_public` issues the specified token amount for the token receiver publicly on the network.
transition mint_public(public receiver: address, public amount: u64) {
// Mint the tokens publicly by invoking the computation on-chain.
return then finalize(receiver, amount);
}
finalize mint_public(public receiver: address, public amount: u64) {
// Increments `account[receiver]` by `amount`.
// If `account[receiver]` does not exist, it will be created.
// If `account[receiver] + amount` overflows, `mint_public` is reverted.
let current_amount: u64 = Mapping::get_or_use(account, receiver, 0u64);
Mapping::set(account, receiver, current_amount + amount);
}
// The function `mint_private` initializes a new record with the specified amount of tokens for the receiver.
transition mint_private(receiver: address, amount: u64) -> token {
return token {
owner: receiver,
amount: amount,
};
}
/* Transfer */
transition transfer_public(public receiver: address, public amount: u64) {
// Transfer the tokens publicly, by invoking the computation on-chain.
return then finalize(self.caller, receiver, amount);
}
finalize transfer_public(public sender: address, public receiver: address, public amount: u64) {
// Decrements `account[sender]` by `amount`.
// If `account[sender]` does not exist, it will be created.
// If `account[sender] - amount` underflows, `transfer_public` is reverted.
let sender_amount: u64 = Mapping::get_or_use(account, sender, 0u64);
Mapping::set(account, sender, sender_amount - amount);
// Increments `account[receiver]` by `amount`.
// If `account[receiver]` does not exist, it will be created.
// If `account[receiver] + amount` overflows, `transfer_public` is reverted.
let receiver_amount: u64 = Mapping::get_or_use(account, receiver, 0u64);
Mapping::set(account, receiver, receiver_amount + amount);
}
// The function `transfer_private` sends the specified token amount to the token receiver from the specified token record.
transition transfer_private(sender: token, receiver: address, amount: u64) -> (token, token) {
// Checks the given token record has sufficient balance.
// This `sub` operation is safe, and the proof will fail if an overflow occurs.
// `difference` holds the change amount to be returned to sender.
let difference: u64 = sender.amount - amount;
// Produce a token record with the change amount for the sender.
let remaining: token = token {
owner: sender.owner,
amount: difference,
};
// Produce a token record for the specified receiver.
let transferred: token = token {
owner: receiver,
amount: amount,
};
// Output the sender's change record and the receiver's record.
return (remaining, transferred);
}
// The function `transfer_private_to_public` turns a specified token amount from a token record into public tokens for the specified receiver.
// This function preserves privacy for the sender's record, however it publicly reveals the token receiver and the token amount.
transition transfer_private_to_public(sender: token, public receiver: address, public amount: u64) -> token {
// Checks the given token record has a sufficient token amount.
// This `sub` operation is safe, and the proof will fail if an underflow occurs.
// `difference` holds the change amount for the caller.
let difference: u64 = sender.amount - amount;
// Produces a token record with the change amount for the caller.
let remaining: token = token {
owner: sender.owner,
amount: difference,
};
// Output the sender's change record.
// Increment the token amount publicly for the token receiver.
return remaining then finalize(receiver, amount);
}
finalize transfer_private_to_public(public receiver: address, public amount: u64) {
// Increments `account[receiver]` by `amount`.
// If `account[receiver]` does not exist, it will be created.
// If `account[receiver] + amount` overflows, `transfer_private_to_public` is reverted.
let current_amount: u64 = Mapping::get_or_use(account, receiver, 0u64);
Mapping::set(account, receiver, current_amount + amount);
}
// The function `transfer_public_to_private` turns a specified token amount from `account` into a token record for the specified receiver.
// This function preserves privacy for the receiver's record, however it publicly reveals the caller and the specified token amount.
transition transfer_public_to_private(public receiver: address, public amount: u64) -> token {
// Produces a token record for the token receiver.
let transferred: token = token {
owner: receiver,
amount: amount,
};
// Output the receiver's record.
// Decrement the token amount of the caller publicly.
return transferred then finalize(self.caller, amount);
}
finalize transfer_public_to_private(public sender: address, public amount: u64) {
// Decrements `account[sender]` by `amount`.
// If `account[sender]` does not exist, it will be created.
// If `account[sender] - amount` underflows, `transfer_public_to_private` is reverted.
let current_amount: u64 = Mapping::get_or_use(account, sender, 0u64);
Mapping::set(account, sender, current_amount - amount);
}
}

View File

@ -64,7 +64,7 @@ Leo is a big project, so (non-)adherence to best practices related to performanc
### Memory handling
- If the final size is known, pre-allocate the collections (`Vec`, `HashMap` etc.) using `with_capacity` or `reserve` - this ensures that there are both fewer allocations (which involve system calls) and that the final allocated capacity is as close to the required size as possible.
- Create the collections right before they are populated/used, as opposed to e.g. creating a few big ones at the beginning of a function and only using them later on; this reduces the amount of time they occupy memory.
- If an intermediate vector is avoidable, use an `Iterator` instead; most of the time this just amounts to omitting the call to `.collect()` if a single-pass iteraton follows afterwards, or returning an `impl Iterator<Item = T>` from a function when the caller only needs to iterate over that result once.
- If an intermediate vector is avoidable, use an `Iterator` instead; most of the time this just amounts to omitting the call to `.collect()` if a single-pass iteration follows afterwards, or returning an `impl Iterator<Item = T>` from a function when the caller only needs to iterate over that result once.
- When possible, fill/resize collections "in bulk" instead of pushing a single element in a loop; this is usually (but not always) detected by `clippy`, suggesting to create vectors containing a repeated value with `vec![x; N]` or extending them with `.resize(N, x)`.
- When a value is to eventually be consumed in a chain of function calls, pass it by value instead of by reference; this has the following benefits:
* It makes the fact that the value is needed by value clear to the caller, who can then potentially reclaim it from the object afterwards if it is "heavy", limiting allocations.

211
Cargo.lock generated
View File

@ -154,9 +154,9 @@ dependencies = [
[[package]]
name = "anstream"
version = "0.6.4"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
checksum = "4cd2405b3ac1faab2990b74d728624cd9fd115651fcecc7c2d8daf01376275ba"
dependencies = [
"anstyle",
"anstyle-parse",
@ -220,9 +220,9 @@ checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "assert_cmd"
version = "2.0.12"
version = "2.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6"
checksum = "00ad3f3a942eee60335ab4342358c161ee296829e0d16ff42fc1d6cb07815467"
dependencies = [
"anstyle",
"bstr",
@ -462,9 +462,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.4.13"
version = "4.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52bdc885e4cacc7f7c9eedc1ef6da641603180c783c41a15c264944deeaab642"
checksum = "58e54881c004cec7895b0068a0a954cd5d62da01aef83fa35b1e594497bf5445"
dependencies = [
"clap_builder",
"clap_derive",
@ -472,9 +472,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.4.12"
version = "4.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9"
checksum = "59cb82d7f531603d2fd1f507441cdd35184fa81beff7bd489570de7f773460bb"
dependencies = [
"anstream",
"anstyle",
@ -529,15 +529,15 @@ dependencies = [
[[package]]
name = "console"
version = "0.15.7"
version = "0.15.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8"
checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
dependencies = [
"encode_unicode",
"lazy_static",
"libc",
"unicode-width",
"windows-sys 0.45.0",
"windows-sys 0.52.0",
]
[[package]]
@ -735,6 +735,19 @@ dependencies = [
"syn 2.0.46",
]
[[package]]
name = "dashmap"
version = "5.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
dependencies = [
"cfg-if",
"hashbrown 0.14.2",
"lock_api",
"once_cell",
"parking_lot_core",
]
[[package]]
name = "der"
version = "0.7.8"
@ -823,6 +836,16 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "disassembler"
version = "1.10.0"
dependencies = [
"leo-ast",
"leo-errors",
"leo-span",
"snarkvm",
]
[[package]]
name = "doc-comment"
version = "0.3.3"
@ -990,6 +1013,21 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1fd087255f739f4f1aeea69f11b72f8080e9c2e7645cd06955dad4a178a49e3"
[[package]]
name = "futures"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.29"
@ -997,6 +1035,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
@ -1005,6 +1044,17 @@ version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"
[[package]]
name = "futures-executor"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.29"
@ -1029,8 +1079,10 @@ version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
@ -1380,6 +1432,7 @@ dependencies = [
"serde",
"serde_json",
"smallvec",
"snarkvm",
]
[[package]]
@ -1387,6 +1440,7 @@ name = "leo-compiler"
version = "1.10.0"
dependencies = [
"dotenvy",
"indexmap 1.9.3",
"leo-ast",
"leo-errors",
"leo-package",
@ -1413,6 +1467,7 @@ dependencies = [
"colored",
"derivative",
"leo-span",
"reqwest",
"serde",
"thiserror",
]
@ -1421,6 +1476,7 @@ dependencies = [
name = "leo-lang"
version = "1.10.0"
dependencies = [
"aleo-std",
"ansi_term",
"assert_cmd",
"backtrace",
@ -1442,10 +1498,12 @@ dependencies = [
"rand_chacha",
"rand_core",
"reqwest",
"retriever",
"rusty-hook",
"self_update 0.39.0",
"serde",
"serde_json",
"serial_test",
"snarkvm",
"sys-info",
"test_dir",
@ -1460,11 +1518,14 @@ dependencies = [
name = "leo-package"
version = "1.10.0"
dependencies = [
"aleo-std",
"indexmap 1.9.3",
"lazy_static",
"leo-errors",
"rand",
"retriever",
"serde",
"serial_test",
"snarkvm",
"toml 0.8.8",
"tracing",
@ -1522,6 +1583,7 @@ dependencies = [
"backtrace",
"clap",
"criterion",
"indexmap 1.9.3",
"leo-compiler",
"leo-errors",
"leo-span",
@ -2180,6 +2242,26 @@ dependencies = [
"winreg",
]
[[package]]
name = "retriever"
version = "1.10.0"
dependencies = [
"aleo-std",
"disassembler",
"indexmap 1.9.3",
"leo-ast",
"leo-errors",
"leo-passes",
"leo-span",
"serde",
"serde_json",
"serial_test",
"sha2",
"tempfile",
"toml 0.8.8",
"ureq",
]
[[package]]
name = "ring"
version = "0.17.5"
@ -2384,18 +2466,18 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
[[package]]
name = "serde"
version = "1.0.194"
version = "1.0.195"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773"
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.194"
version = "1.0.195"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0"
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
dependencies = [
"proc-macro2",
"quote 1.0.35",
@ -2447,6 +2529,31 @@ dependencies = [
"yaml-rust",
]
[[package]]
name = "serial_test"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "953ad9342b3aaca7cb43c45c097dd008d4907070394bd0751a0aa8817e5a018d"
dependencies = [
"dashmap",
"futures",
"lazy_static",
"log",
"parking_lot",
"serial_test_derive",
]
[[package]]
name = "serial_test_derive"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b93fb4adc70021ac1b47f7d45e8cc4169baaa7ea58483bc5b721d19a26202212"
dependencies = [
"proc-macro2",
"quote 1.0.35",
"syn 2.0.46",
]
[[package]]
name = "sha1"
version = "0.10.6"
@ -2499,9 +2606,9 @@ dependencies = [
[[package]]
name = "smallvec"
version = "1.11.2"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
checksum = "2593d31f82ead8df961d8bd23a64c2ccf2eb5dd34b0a34bfb4dd54011c72009e"
dependencies = [
"serde",
]
@ -3829,9 +3936,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "ureq"
version = "2.9.0"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7830e33f6e25723d41a63f77e434159dad02919f18f55a512b5f16f3b1d77138"
checksum = "f8cdd25c339e200129fe4de81451814e5228c9b771d57378817d6117cc2b3f97"
dependencies = [
"base64",
"flate2",
@ -4033,15 +4140,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets 0.42.2",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
@ -4060,21 +4158,6 @@ dependencies = [
"windows-targets 0.52.0",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm 0.42.2",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm 0.42.2",
"windows_x86_64_msvc 0.42.2",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
@ -4105,12 +4188,6 @@ dependencies = [
"windows_x86_64_msvc 0.52.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
@ -4123,12 +4200,6 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
@ -4141,12 +4212,6 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
@ -4159,12 +4224,6 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
@ -4177,12 +4236,6 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
@ -4195,12 +4248,6 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
@ -4213,12 +4260,6 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"

View File

@ -18,13 +18,10 @@ include = [
"leo",
"README.md",
"LICENSE.md",
"examples/lottery/inputs/lottery.in",
"examples/lottery/src/main.leo",
"examples/lottery/run.sh",
"examples/tictactoe/inputs/tictactoe.in",
"examples/tictactoe/src/main.leo",
"examples/tictactoe/run.sh",
"examples/token/inputs/token.in",
"examples/token/src/main.leo",
"examples/token/run.sh"
]
@ -41,7 +38,9 @@ members = [
"docs/grammar",
"errors",
"leo/package",
"tests/test-framework"
"tests/test-framework",
"utils/disassembler",
"utils/retriever"
]
[workspace.dependencies.snarkvm]
@ -59,6 +58,10 @@ default = [ ]
ci_skip = [ "leo-compiler/ci_skip" ]
noconfig = [ ]
[dependencies.aleo-std]
version = "0.1.18"
default-features = false
[dependencies.leo-ast]
path = "./compiler/ast"
version = "=1.10.0"
@ -83,6 +86,10 @@ version = "=1.10.0"
path = "./compiler/span"
version = "=1.10.0"
[dependencies.retriever]
path = "./utils/retriever"
version = "1.10.0"
[dependencies.backtrace]
version = "0.3.68"
@ -97,7 +104,7 @@ version = "0.6.1"
version = "2.0"
[dependencies.console]
version = "0.15.7"
version = "0.15.8"
[dependencies.dirs]
version = "5.0.0"
@ -137,6 +144,9 @@ features = [ "derive" ]
[dependencies.serde_json]
version = "1.0"
[dependencies.serial_test]
version = "3.0.0"
[dependencies.snarkvm]
workspace = true
features = [ "circuit", "console" ]
@ -161,7 +171,7 @@ version = "^0.6"
version = "0.12.1"
[dev-dependencies.assert_cmd]
version = "2.0.12"
version = "2.0.13"
[dev-dependencies.rusty-hook]
version = "0.11.2"

View File

@ -7,8 +7,9 @@
<p align="center">
<a href="https://circleci.com/gh/AleoHQ/leo"><img src="https://circleci.com/gh/AleoHQ/leo.svg?style=svg&circle-token=00960191919c40be0774e00ce8f7fa1fcaa20c00"></a>
<a href="https://codecov.io/gh/AleoHQ/leo"><img src="https://codecov.io/gh/AleoHQ/leo/branch/testnet3/graph/badge.svg?token=S6MWO60SYL"/></a>
<a href="https://discord.gg/5v2ynrw2ds"><img src="https://img.shields.io/discord/700454073459015690?logo=discord"/></a>
<a href="https://discord.gg/aleo"><img src="https://img.shields.io/discord/700454073459015690?logo=discord"/></a>
<a href="https://github.com/AleoHQ/leo/blob/testnet3/CONTRIBUTORS.md"><img src="https://img.shields.io/badge/contributors-393-ee8449"/></a>
<a href="https://twitter.com/AleoHQ"><img src="https://img.shields.io/twitter/follow/AleoHQ?style=social"/></a>
</p>
<div id="top"></div>
Leo is a functional, statically-typed programming language built for writing private applications.
@ -87,7 +88,7 @@ leo new helloworld
cd helloworld
# build & setup & prove & verify
leo run
leo run main 0u32 1u32
```
The `leo new` command creates a new Leo project with a given name.

View File

@ -18,6 +18,9 @@ license = "GPL-3.0"
edition = "2021"
rust-version = "1.69"
[dependencies.snarkvm]
workspace = true
[dependencies.leo-errors]
path = "../../errors"
version = "=1.10.0"
@ -42,7 +45,7 @@ version = "1.0"
features = [ "preserve_order" ]
[dependencies.smallvec]
version = "1.11.2"
version = "1.12.0"
features = [ "serde" ]
[dev-dependencies.criterion]

View File

@ -16,6 +16,7 @@
use leo_errors::Result;
use leo_span::{Span, Symbol};
use snarkvm::console::program::Identifier as IdentifierCore;
use crate::{simple_node_impl, Node, NodeID};
use serde::{
@ -28,6 +29,7 @@ use serde::{
Serialize,
Serializer,
};
use snarkvm::prelude::Network;
use std::{
collections::BTreeMap,
fmt,
@ -152,3 +154,8 @@ impl<'de> Deserialize<'de> for Identifier {
deserializer.deserialize_str(IdentifierVisitor)
}
}
impl<N: Network> From<&IdentifierCore<N>> for Identifier {
fn from(id: &IdentifierCore<N>) -> Self {
Self { name: Symbol::intern(&id.to_string()), span: Default::default(), id: Default::default() }
}
}

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{Block, Identifier, Input, Node, NodeID, Output, TupleType, Type};
use crate::{Block, FinalizeStub, Identifier, Input, Node, NodeID, Output, TupleType, Type};
use leo_span::Span;
@ -72,4 +72,17 @@ impl fmt::Display for Finalize {
}
}
impl From<FinalizeStub> for Finalize {
fn from(finalize_stub: FinalizeStub) -> Self {
Self::new(
finalize_stub.identifier,
finalize_stub.input,
finalize_stub.output,
Block::default(),
finalize_stub.span,
finalize_stub.id,
)
}
}
crate::simple_node_impl!(Finalize);

View File

@ -38,7 +38,7 @@ pub use output::*;
pub mod mode;
pub use mode::*;
use crate::{Block, Identifier, Node, NodeID, TupleType, Type};
use crate::{Block, FunctionStub, Identifier, Node, NodeID, TupleType, Type};
use leo_span::{Span, Symbol};
use serde::{Deserialize, Serialize};
@ -139,6 +139,24 @@ impl Function {
}
}
impl From<FunctionStub> for Function {
fn from(function: FunctionStub) -> Self {
let finalize = function.finalize_stub.map(Finalize::from);
Self {
annotations: function.annotations,
variant: function.variant,
identifier: function.identifier,
input: function.input,
output: function.output,
output_type: function.output_type,
block: Block::default(),
finalize,
span: function.span,
id: function.id,
}
}
}
impl fmt::Debug for Function {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.format(f)

View File

@ -1,29 +0,0 @@
// Copyright (C) 2019-2023 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use super::*;
use crate::{Expression, Identifier, Mode, Type};
/// A single definition inside a section in a state or an input file.
/// Definitions should be structured as: `<name>: <type_> = <value>;`
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Definition {
pub mode: Mode,
pub type_: Type,
pub name: Identifier,
pub value: Expression,
pub span: Span,
}

View File

@ -1,108 +0,0 @@
// Copyright (C) 2019-2023 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{normalize_json_value, remove_key_from_json, Expression, Struct, Type};
use super::*;
use leo_errors::{AstError, Result};
/// Input data which includes [`ProgramInput`].
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct InputData {
pub program_input: ProgramInput,
}
impl InputData {
/// Serializes the ast into a JSON string.
pub fn to_json_string(&self) -> Result<String> {
Ok(serde_json::to_string_pretty(&self).map_err(|e| AstError::failed_to_convert_ast_to_json_string(&e))?)
}
}
/// A raw unprocessed input or state file data. Used for future conversion
/// into [`ProgramInput`].
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct InputAst {
pub sections: Vec<Section>,
}
impl InputAst {
/// Returns all values of the input AST for execution with `leo run`.
pub fn program_inputs(&self, program_name: &str, structs: IndexMap<Symbol, Struct>) -> Vec<String> {
self.sections
.iter()
.filter(|section| section.name() == program_name)
.flat_map(|section| {
section.definitions.iter().map(|definition| match &definition.type_ {
// Handle case where the input may be record.
Type::Identifier(identifier) => {
match structs.get(&identifier.name) {
// TODO: Better error handling.
None => panic!(
"Input error: A struct or record declaration does not exist for {}.",
identifier.name
),
Some(struct_) => match struct_.is_record {
false => definition.value.to_string(),
true => match &definition.value {
// Print out the record interface with visibility.
Expression::Struct(struct_expression) => struct_expression.to_record_string(),
_ => panic!("Input error: Expected a struct expression."),
},
},
}
}
_ => definition.value.to_string(),
})
})
.collect::<Vec<_>>()
}
/// Serializes the `Input` into a JSON Value.
pub fn to_json_value(&self) -> Result<serde_json::Value> {
Ok(serde_json::to_value(self).map_err(|e| AstError::failed_to_convert_ast_to_json_value(&e))?)
}
/// Serializes the input into a JSON file.
pub fn to_json_file(&self, mut path: std::path::PathBuf, file_name: &str) -> Result<()> {
path.push(file_name);
let file = std::fs::File::create(&path).map_err(|e| AstError::failed_to_create_ast_json_file(&path, &e))?;
let writer = std::io::BufWriter::new(file);
Ok(serde_json::to_writer_pretty(writer, &self)
.map_err(|e| AstError::failed_to_write_ast_to_json_file(&path, &e))?)
}
/// Serializes the `Input` into a JSON value and removes keys from object mappings before writing to a file.
pub fn to_json_file_without_keys(
&self,
mut path: std::path::PathBuf,
file_name: &str,
excluded_keys: &[&str],
) -> Result<()> {
path.push(file_name);
let file = std::fs::File::create(&path).map_err(|e| AstError::failed_to_create_ast_json_file(&path, &e))?;
let writer = std::io::BufWriter::new(file);
let mut value = self.to_json_value().unwrap();
for key in excluded_keys {
value = remove_key_from_json(value, key);
}
value = normalize_json_value(value);
Ok(serde_json::to_writer_pretty(writer, &value)
.map_err(|e| AstError::failed_to_write_ast_to_json_file(&path, &e))?)
}
}

View File

@ -1,71 +0,0 @@
// Copyright (C) 2019-2023 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{Expression, GroupLiteral, IntegerType, Literal, Node, Type, UnaryOperation};
use leo_errors::{InputError, LeoError, Result};
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum InputValue {
Address(String),
Boolean(bool),
Field(String),
Group(GroupLiteral),
Integer(IntegerType, String),
}
impl TryFrom<(Type, Expression)> for InputValue {
type Error = LeoError;
fn try_from(value: (Type, Expression)) -> Result<Self> {
Ok(match value {
(type_, Expression::Literal(lit)) => match (type_, lit) {
(Type::Address, Literal::Address(value, _, _)) => Self::Address(value),
(Type::Boolean, Literal::Boolean(value, _, _)) => Self::Boolean(value),
(Type::Field, Literal::Field(value, _, _)) => Self::Field(value),
(Type::Group, Literal::Group(value)) => Self::Group(*value),
(Type::Integer(expected), Literal::Integer(actual, value, span, _)) => {
if expected == actual {
Self::Integer(expected, value)
} else {
return Err(InputError::unexpected_type(expected.to_string(), actual, span).into());
}
}
(x, y) => {
return Err(InputError::unexpected_type(x, &y, y.span()).into());
}
},
(type_, Expression::Unary(unary)) if unary.op == UnaryOperation::Negate => {
InputValue::try_from((type_, *unary.receiver))?
}
(_type_, expr) => return Err(InputError::illegal_expression(&expr, expr.span()).into()),
})
}
}
impl fmt::Display for InputValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
InputValue::Address(ref address) => write!(f, "{address}"),
InputValue::Boolean(ref boolean) => write!(f, "{boolean}"),
InputValue::Group(ref group) => write!(f, "{group}"),
InputValue::Field(ref field) => write!(f, "{field}"),
InputValue::Integer(ref type_, ref number) => write!(f, "{number}{type_:?}"),
}
}
}

View File

@ -1,44 +0,0 @@
// Copyright (C) 2019-2023 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use super::*;
/// Processed Program input.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ProgramInput {
pub main: Definitions,
}
impl TryFrom<InputAst> for ProgramInput {
type Error = LeoError;
fn try_from(input: InputAst) -> Result<Self> {
let mut main = IndexMap::new();
for section in input.sections {
let target = match section.name {
sym::main => &mut main,
_ => return Err(InputError::unexpected_section(&["main"], section.name, section.span).into()),
};
for definition in section.definitions {
target.insert(definition.name.name, InputValue::try_from((definition.type_, definition.value))?);
}
}
Ok(ProgramInput { main })
}
}

View File

@ -1,32 +0,0 @@
// Copyright (C) 2019-2023 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use super::*;
/// A single section in an input or a state file.
/// An example of a section would be: `[main]`.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Section {
pub name: Symbol,
pub definitions: Vec<Definition>,
pub span: Span,
}
impl Section {
pub fn name(&self) -> String {
self.name.to_string()
}
}

View File

@ -40,9 +40,6 @@ pub use self::functions::*;
pub mod groups;
pub use self::groups::*;
pub mod input;
pub use self::input::*;
pub mod mapping;
pub use self::mapping::*;
@ -59,6 +56,10 @@ pub mod types;
pub use self::types::*;
pub mod value;
pub mod stub;
pub use self::stub::*;
pub use self::value::*;
pub use common::node::*;

View File

@ -19,6 +19,7 @@ use crate::{Identifier, Node, NodeID, Type};
use leo_span::Span;
use serde::{Deserialize, Serialize};
use snarkvm::prelude::{Mapping as MappingCore, Network};
use std::fmt;
/// A mapping declaration, e.g `mapping balances: address => u128`.
@ -36,6 +37,17 @@ pub struct Mapping {
pub id: NodeID,
}
impl<N: Network> From<&MappingCore<N>> for Mapping {
fn from(mapping: &MappingCore<N>) -> Self {
Self {
identifier: Identifier::from(mapping.name()),
key_type: Type::from(mapping.key().plaintext_type()),
value_type: Type::from(mapping.value().plaintext_type()),
span: Default::default(),
id: Default::default(),
}
}
}
impl fmt::Display for Mapping {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "mapping {}: {} => {}", self.identifier, self.key_type, self.value_type)

View File

@ -417,6 +417,7 @@ pub trait ProgramReconstructor: StatementReconstructor {
.into_iter()
.map(|(id, import)| (id, (self.reconstruct_import(import.0), import.1)))
.collect(),
stubs: input.stubs.into_iter().map(|(id, stub)| (id, self.reconstruct_stub(stub))).collect(),
program_scopes: input
.program_scopes
.into_iter()
@ -425,6 +426,18 @@ pub trait ProgramReconstructor: StatementReconstructor {
}
}
fn reconstruct_stub(&mut self, input: Stub) -> Stub {
Stub {
imports: input.imports,
stub_id: input.stub_id,
consts: input.consts,
structs: input.structs,
mappings: input.mappings,
span: input.span,
functions: input.functions.into_iter().map(|(i, f)| (i, self.reconstruct_function_stub(f))).collect(),
}
}
fn reconstruct_program_scope(&mut self, input: ProgramScope) -> ProgramScope {
ProgramScope {
program_id: input.program_id,
@ -466,6 +479,10 @@ pub trait ProgramReconstructor: StatementReconstructor {
}
}
fn reconstruct_function_stub(&mut self, input: FunctionStub) -> FunctionStub {
input
}
fn reconstruct_struct(&mut self, input: Struct) -> Struct {
input
}

View File

@ -222,7 +222,7 @@ pub trait StatementVisitor<'a>: ExpressionVisitor<'a> {
pub trait ProgramVisitor<'a>: StatementVisitor<'a> {
fn visit_program(&mut self, input: &'a Program) {
input.imports.values().for_each(|import| self.visit_import(&import.0));
input.stubs.values().for_each(|stub| self.visit_stub(stub));
input.program_scopes.values().for_each(|scope| self.visit_program_scope(scope));
}
@ -236,6 +236,8 @@ pub trait ProgramVisitor<'a>: StatementVisitor<'a> {
input.consts.iter().for_each(|(_, c)| (self.visit_const(c)));
}
fn visit_stub(&mut self, _input: &'a Stub) {}
fn visit_import(&mut self, input: &'a Program) {
self.visit_program(input)
}
@ -250,4 +252,8 @@ pub trait ProgramVisitor<'a>: StatementVisitor<'a> {
self.visit_block(&finalize.block);
}
}
fn visit_function_stub(&mut self, _input: &'a FunctionStub) {}
fn visit_struct_stub(&mut self, _input: &'a Struct) {}
}

View File

@ -24,6 +24,7 @@ pub use program_scope::*;
use leo_span::{Span, Symbol};
use crate::Stub;
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
use std::fmt;
@ -33,6 +34,8 @@ use std::fmt;
pub struct Program {
/// A map from import names to import definitions.
pub imports: IndexMap<Symbol, (Program, Span)>,
/// A map from program stub names to program stub scopes.
pub stubs: IndexMap<Symbol, Stub>,
/// A map from program names to program scopes.
pub program_scopes: IndexMap<Symbol, ProgramScope>,
}
@ -42,6 +45,10 @@ impl fmt::Display for Program {
for (id, _import) in self.imports.iter() {
writeln!(f, "import {id}.leo;")?;
}
for (_, stub) in self.stubs.iter() {
stub.fmt(f)?;
writeln!(f,)?;
}
for (_, program_scope) in self.program_scopes.iter() {
program_scope.fmt(f)?;
writeln!(f,)?;
@ -53,6 +60,6 @@ impl fmt::Display for Program {
impl Default for Program {
/// Constructs an empty program node.
fn default() -> Self {
Self { imports: IndexMap::new(), program_scopes: IndexMap::new() }
Self { imports: IndexMap::new(), stubs: IndexMap::new(), program_scopes: IndexMap::new() }
}
}

View File

@ -18,6 +18,7 @@ use crate::Identifier;
use core::fmt;
use serde::{de, de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
use snarkvm::{console::program::ProgramID, prelude::Network};
use std::collections::BTreeMap;
/// An identifier for a program that is eventually deployed to the network.
@ -92,3 +93,9 @@ impl<'de> Deserialize<'de> for ProgramId {
deserializer.deserialize_str(ProgramIdVisitor)
}
}
impl<N: Network> From<&ProgramID<N>> for ProgramId {
fn from(program: &ProgramID<N>) -> Self {
Self { name: Identifier::from(program.name()), network: Identifier::from(program.network()) }
}
}

View File

@ -16,7 +16,7 @@
//! A Leo program scope consists of struct, function, and mapping definitions.
use crate::{ConstDeclaration, Function, Mapping, ProgramId, Struct};
use crate::{ConstDeclaration, Function, Mapping, ProgramId, Struct, Stub};
use leo_span::{Span, Symbol};
use serde::{Deserialize, Serialize};
@ -39,6 +39,23 @@ pub struct ProgramScope {
pub span: Span,
}
impl From<Stub> for ProgramScope {
fn from(stub: Stub) -> Self {
Self {
program_id: stub.stub_id,
consts: stub.consts,
structs: stub.structs,
mappings: stub.mappings,
functions: stub
.functions
.into_iter()
.map(|(symbol, function)| (symbol, Function::from(function)))
.collect(),
span: stub.span,
}
}
}
impl fmt::Display for ProgramScope {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "program {} {{", self.program_id)?;

View File

@ -21,7 +21,7 @@ use serde::{Deserialize, Serialize};
use std::fmt;
/// A block `{ [stmt]* }` consisting of a list of statements to execute in order.
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug, Default)]
pub struct Block {
/// The list of statements to execute.
pub statements: Vec<Statement>,

View File

@ -17,12 +17,21 @@
pub mod member;
pub use member::*;
use crate::{Identifier, Node, NodeID};
use crate::{Identifier, Mode, Node, NodeID, Type};
use leo_span::{Span, Symbol};
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use std::fmt;
use snarkvm::{
console::program::{RecordType, StructType},
prelude::{
EntryType::{Constant, Private, Public},
Network,
},
};
/// A struct type definition, e.g., `struct Foo { my_field: Bar }`.
/// In some languages these are called `struct`s.
///
@ -72,8 +81,66 @@ impl fmt::Display for Struct {
for field in self.members.iter() {
writeln!(f, " {field}")?;
}
write!(f, "}}")
write!(f, " }}")
}
}
crate::simple_node_impl!(Struct);
impl<N: Network> From<&StructType<N>> for Struct {
fn from(input: &StructType<N>) -> Self {
Self {
identifier: Identifier::from(input.name()),
members: input
.members()
.iter()
.map(|(id, type_)| Member {
mode: Mode::None,
identifier: Identifier::from(id),
type_: Type::from(type_),
span: Default::default(),
id: Default::default(),
})
.collect(),
is_record: false,
span: Default::default(),
id: Default::default(),
}
}
}
impl<N: Network> From<&RecordType<N>> for Struct {
fn from(input: &RecordType<N>) -> Self {
Self {
identifier: Identifier::from(input.name()),
members: [
vec![Member {
mode: if input.owner().is_private() { Mode::Public } else { Mode::Private },
identifier: Identifier::new(Symbol::intern("owner"), Default::default()),
type_: Type::Address,
span: Default::default(),
id: Default::default(),
}],
input
.entries()
.iter()
.map(|(id, entry)| Member {
mode: if input.owner().is_public() { Mode::Public } else { Mode::Private },
identifier: Identifier::from(id),
type_: match entry {
Public(t) => Type::from(t),
Private(t) => Type::from(t),
Constant(t) => Type::from(t),
},
span: Default::default(),
id: Default::default(),
})
.collect_vec(),
]
.concat(),
is_record: true,
span: Default::default(),
id: Default::default(),
}
}
}

View File

@ -0,0 +1,101 @@
// Copyright (C) 2019-2023 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{Finalize, FunctionInput, Identifier, Input, Mode, Node, NodeID, Output, TupleType, Type};
use leo_span::{Span, Symbol};
use core::fmt;
use serde::{Deserialize, Serialize};
use snarkvm::{
prelude::{
FinalizeType::{Future, Plaintext},
Network,
},
synthesizer::program::{CommandTrait, FinalizeCore},
};
/// A finalize stub.
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug)]
pub struct FinalizeStub {
/// The finalize identifier.
pub identifier: Identifier,
/// The finalize block's input parameters.
pub input: Vec<Input>,
/// The finalize blocks's output declaration.
pub output: Vec<Output>,
/// The finalize block's output type.
pub output_type: Type,
/// The entire span of the finalize stub.
pub span: Span,
/// The ID of the node.
pub id: NodeID,
}
impl FinalizeStub {
/// Create a new finalize stub.
pub fn new(identifier: Identifier, input: Vec<Input>, output: Vec<Output>, span: Span, id: NodeID) -> Self {
let output_type = match output.len() {
0 => Type::Unit,
1 => output[0].type_(),
_ => Type::Tuple(TupleType::new(output.iter().map(|output| output.type_()).collect())),
};
Self { identifier, input, output, output_type, span, id }
}
}
impl<N: Network, Command: CommandTrait<N>> From<&FinalizeCore<N, Command>> for FinalizeStub {
fn from(finalize: &FinalizeCore<N, Command>) -> Self {
let mut inputs = Vec::new();
finalize.inputs().iter().enumerate().for_each(|(index, input)| {
let arg_name = Identifier::new(Symbol::intern(&format!("a{}", index + 1)), Default::default());
match input.finalize_type() {
Plaintext(val) => inputs.push(Input::Internal(FunctionInput {
identifier: arg_name,
mode: Mode::None,
type_: Type::from(val),
span: Default::default(),
id: Default::default(),
})),
Future(_) => {} // Don't need to worry about nested futures
}
});
Self::new(Identifier::from(finalize.name()), inputs, Vec::new(), Default::default(), Default::default())
}
}
impl From<Finalize> for FinalizeStub {
fn from(finalize: Finalize) -> Self {
Self::new(finalize.identifier, finalize.input, finalize.output, Default::default(), Default::default())
}
}
impl fmt::Display for FinalizeStub {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let parameters = self.input.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(",");
let returns = match self.output.len() {
0 => "()".to_string(),
1 => self.output[0].to_string(),
_ => format!("({})", self.output.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(",")),
};
write!(f, " finalize {}({parameters}) -> {returns}", self.identifier)
}
}
crate::simple_node_impl!(FinalizeStub);

View File

@ -0,0 +1,349 @@
// Copyright (C) 2019-2023 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{
finalize_stub::*,
Annotation,
External,
Function,
FunctionInput,
FunctionOutput,
Identifier,
Input,
Mode,
Node,
NodeID,
Output,
ProgramId,
TupleType,
Type,
Variant,
};
use leo_span::{sym, Span, Symbol};
use crate::Type::Identifier as IdentifierType;
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use snarkvm::{
console::program::RegisterType::{ExternalRecord, Future, Plaintext, Record},
prelude::{Network, ValueType},
synthesizer::program::{ClosureCore, CommandTrait, FunctionCore, InstructionTrait},
};
use std::fmt;
/// A function stub definition.
#[derive(Clone, Serialize, Deserialize)]
pub struct FunctionStub {
/// Annotations on the function.
pub annotations: Vec<Annotation>,
/// Is this function a transition, inlined, or a regular function?.
pub variant: Variant,
/// The function identifier, e.g., `foo` in `function foo(...) { ... }`.
pub identifier: Identifier,
/// The function's input parameters.
pub input: Vec<Input>,
/// The function's output declarations.
pub output: Vec<Output>,
/// The function's output type.
pub output_type: Type,
/// An optional finalize stub
pub finalize_stub: Option<FinalizeStub>,
/// The entire span of the function definition.
pub span: Span,
/// The ID of the node.
pub id: NodeID,
}
impl PartialEq for FunctionStub {
fn eq(&self, other: &Self) -> bool {
self.identifier == other.identifier
}
}
impl Eq for FunctionStub {}
impl FunctionStub {
/// Initialize a new function.
#[allow(clippy::too_many_arguments)]
pub fn new(
annotations: Vec<Annotation>,
variant: Variant,
identifier: Identifier,
input: Vec<Input>,
output: Vec<Output>,
finalize_stub: Option<FinalizeStub>,
span: Span,
id: NodeID,
) -> Self {
// Determine the output type of the function
let get_output_type = |output: &Output| match &output {
Output::Internal(output) => output.type_.clone(),
Output::External(output) => output.type_(),
};
let output_type = match output.len() {
0 => Type::Unit,
1 => get_output_type(&output[0]),
_ => Type::Tuple(TupleType::new(output.iter().map(get_output_type).collect())),
};
FunctionStub { annotations, variant, identifier, input, output, output_type, finalize_stub, span, id }
}
/// Returns function name.
pub fn name(&self) -> Symbol {
self.identifier.name
}
/// Returns `true` if the function name is `main`.
pub fn is_main(&self) -> bool {
self.name() == sym::main
}
///
/// Private formatting method used for optimizing [fmt::Debug] and [fmt::Display] implementations.
///
fn format(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.variant {
Variant::Inline => write!(f, "inline ")?,
Variant::Standard => write!(f, "function ")?,
Variant::Transition => write!(f, "transition ")?,
}
write!(f, "{}", self.identifier)?;
let parameters = self.input.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(",");
let returns = match self.output.len() {
0 => "()".to_string(),
1 => self.output[0].to_string(),
_ => self.output.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(","),
};
write!(f, "({parameters}) -> {returns}")?;
if let Some(finalize) = &self.finalize_stub {
let parameters = finalize.input.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(",");
write!(f, " finalize ({parameters})")
} else {
Ok(())
}
}
}
impl From<Function> for FunctionStub {
fn from(function: Function) -> Self {
Self {
annotations: function.annotations,
variant: function.variant,
identifier: function.identifier,
input: function.input,
output: function.output,
output_type: function.output_type,
finalize_stub: function.finalize.map(FinalizeStub::from),
span: function.span,
id: function.id,
}
}
}
impl<N: Network, Instruction: InstructionTrait<N>> From<&ClosureCore<N, Instruction>> for FunctionStub {
fn from(closure: &ClosureCore<N, Instruction>) -> Self {
let outputs = closure
.outputs()
.iter()
.map(|output| match output.register_type() {
Plaintext(val) => Output::Internal(FunctionOutput {
mode: Mode::None,
type_: Type::from(val),
span: Default::default(),
id: Default::default(),
}),
Record(_) => panic!("Closures do not return records"),
ExternalRecord(_) => panic!("Closures do not return external records"),
Future(_) => panic!("Closures do not return futures"),
})
.collect_vec();
let output_vec = outputs
.iter()
.map(|output| match output {
Output::Internal(output) => output.type_.clone(),
Output::External(_) => panic!("Closures do not return external records"),
})
.collect_vec();
let output_type = match output_vec.len() {
0 => Type::Unit,
1 => output_vec[0].clone(),
_ => Type::Tuple(TupleType::new(output_vec)),
};
Self {
annotations: Vec::new(),
variant: Variant::Standard,
identifier: Identifier::from(closure.name()),
input: closure
.inputs()
.iter()
.enumerate()
.map(|(index, input)| {
let arg_name = Identifier::new(Symbol::intern(&format!("a{}", index + 1)), Default::default());
match input.register_type() {
Plaintext(val) => Input::Internal(FunctionInput {
identifier: arg_name,
mode: Mode::None,
type_: Type::from(val),
span: Default::default(),
id: Default::default(),
}),
Record(_) => panic!("Closures do not contain records as inputs"),
ExternalRecord(_) => panic!("Closures do not contain external records as inputs"),
Future(_) => panic!("Closures do not contain futures as inputs"),
}
})
.collect_vec(),
output: outputs,
output_type,
span: Default::default(),
id: Default::default(),
finalize_stub: None,
}
}
}
impl<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>>
From<&FunctionCore<N, Instruction, Command>> for FunctionStub
{
fn from(function: &FunctionCore<N, Instruction, Command>) -> Self {
let outputs = function
.outputs()
.iter()
.map(|output| match output.value_type() {
ValueType::Constant(val) => vec![Output::Internal(FunctionOutput {
mode: Mode::Constant,
type_: Type::from(val),
span: Default::default(),
id: Default::default(),
})],
ValueType::Public(val) => vec![Output::Internal(FunctionOutput {
mode: Mode::Public,
type_: Type::from(val),
span: Default::default(),
id: Default::default(),
})],
ValueType::Private(val) => vec![Output::Internal(FunctionOutput {
mode: Mode::Private,
type_: Type::from(val),
span: Default::default(),
id: Default::default(),
})],
ValueType::Record(id) => vec![Output::Internal(FunctionOutput {
mode: Mode::None,
type_: IdentifierType(Identifier::from(id)),
span: Default::default(),
id: Default::default(),
})],
ValueType::ExternalRecord(loc) => vec![Output::External(External {
identifier: Identifier::new(Symbol::intern("dummy"), Default::default()),
program_name: ProgramId::from(loc.program_id()).name,
record: Identifier::from(loc.resource()),
span: Default::default(),
id: Default::default(),
})],
ValueType::Future(_) => Vec::new(), // Don't include futures in the output signature
})
.collect_vec()
.concat();
let output_vec = outputs
.iter()
.map(|output| match output {
Output::Internal(output) => output.type_.clone(),
Output::External(output) => Type::Identifier(output.record),
})
.collect_vec();
let output_type = match output_vec.len() {
0 => Type::Unit,
1 => output_vec[0].clone(),
_ => Type::Tuple(TupleType::new(output_vec)),
};
Self {
annotations: Vec::new(),
variant: Variant::Transition,
identifier: Identifier::from(function.name()),
input: function
.inputs()
.iter()
.enumerate()
.map(|(index, input)| {
let arg_name = Identifier::new(Symbol::intern(&format!("a{}", index + 1)), Default::default());
match input.value_type() {
ValueType::Constant(val) => Input::Internal(FunctionInput {
identifier: arg_name,
mode: Mode::Constant,
type_: Type::from(val),
span: Default::default(),
id: Default::default(),
}),
ValueType::Public(val) => Input::Internal(FunctionInput {
identifier: arg_name,
mode: Mode::Public,
type_: Type::from(val),
span: Default::default(),
id: Default::default(),
}),
ValueType::Private(val) => Input::Internal(FunctionInput {
identifier: arg_name,
mode: Mode::Private,
type_: Type::from(val),
span: Default::default(),
id: Default::default(),
}),
ValueType::Record(id) => Input::Internal(FunctionInput {
identifier: arg_name,
mode: Mode::None,
type_: IdentifierType(Identifier::from(id)),
span: Default::default(),
id: Default::default(),
}),
ValueType::ExternalRecord(loc) => Input::External(External {
identifier: Identifier::new(Symbol::intern("dummy"), Default::default()),
program_name: ProgramId::from(loc.program_id()).name,
record: Identifier::from(loc.resource()),
span: Default::default(),
id: Default::default(),
}),
ValueType::Future(_) => panic!("Functions do not contain futures as inputs"),
}
})
.collect_vec(),
output: outputs,
output_type,
finalize_stub: function.finalize_logic().map(FinalizeStub::from),
span: Default::default(),
id: Default::default(),
}
}
}
impl fmt::Debug for FunctionStub {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.format(f)
}
}
impl fmt::Display for FunctionStub {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.format(f)
}
}
crate::simple_node_impl!(FunctionStub);

View File

@ -0,0 +1,84 @@
// Copyright (C) 2019-2023 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
//! A stub contains function templates as well as definitions for mappings, structs, records, and constants.
pub mod finalize_stub;
pub use finalize_stub::*;
pub mod function_stub;
pub use function_stub::*;
use crate::{ConstDeclaration, Identifier, Mapping, NodeID, ProgramId, Struct};
use leo_span::{Span, Symbol};
use serde::{Deserialize, Serialize};
use std::fmt;
/// Stores the Leo stub abstract syntax tree.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct Stub {
/// A vector of imported programs.
pub imports: Vec<ProgramId>,
/// The stub id
pub stub_id: ProgramId,
/// A vector of const definitions.
pub consts: Vec<(Symbol, ConstDeclaration)>,
/// A vector of struct definitions.
pub structs: Vec<(Symbol, Struct)>,
/// A vector of mapping definitions.
pub mappings: Vec<(Symbol, Mapping)>,
/// A vector of function stub definitions.
pub functions: Vec<(Symbol, FunctionStub)>,
/// The span associated with the stub.
pub span: Span,
}
impl Default for Stub {
/// Constructs an empty program stub
fn default() -> Self {
Self {
imports: Vec::new(),
stub_id: ProgramId {
name: Identifier::new(Symbol::intern(""), NodeID::default()),
network: Identifier::new(Symbol::intern(""), NodeID::default()),
},
consts: Vec::new(),
structs: Vec::new(),
mappings: Vec::new(),
functions: Vec::new(),
span: Span::default(),
}
}
}
impl fmt::Display for Stub {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "stub {} {{", self.stub_id)?;
for import in self.imports.iter() {
writeln!(f, " import {import}")?;
}
for (_, mapping) in self.mappings.iter() {
writeln!(f, " {mapping}")?;
}
for (_, struct_) in self.structs.iter() {
writeln!(f, " {struct_}")?;
}
for (_, function) in self.functions.iter() {
writeln!(f, " {function}")?;
}
writeln!(f, "}}")?;
Ok(())
}
}

View File

@ -15,8 +15,10 @@
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{NonNegativeNumber, Type};
use snarkvm::console::program::ArrayType as ConsoleArrayType;
use serde::{Deserialize, Serialize};
use snarkvm::prelude::Network;
use std::fmt;
/// An array type.
@ -51,6 +53,15 @@ impl ArrayType {
}
}
impl<N: Network> From<&ConsoleArrayType<N>> for ArrayType {
fn from(array_type: &ConsoleArrayType<N>) -> Self {
Self {
element_type: Box::new(Type::from(array_type.next_element_type())),
length: NonNegativeNumber::from(array_type.length().to_string().replace("u32", "")),
}
}
}
impl fmt::Display for ArrayType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}; {}]", self.element_type, self.length)

View File

@ -14,10 +14,15 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{ArrayType, Identifier, IntegerType, MappingType, TupleType};
use crate::{common, ArrayType, Identifier, IntegerType, MappingType, TupleType};
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use snarkvm::prelude::{
Network,
PlaintextType,
PlaintextType::{Array, Literal, Struct},
};
use std::fmt;
/// Explicit type used for defining a variable or expression type
@ -108,3 +113,31 @@ impl fmt::Display for Type {
}
}
}
impl<N: Network> From<&PlaintextType<N>> for Type {
fn from(t: &PlaintextType<N>) -> Self {
match t {
Literal(lit) => match lit {
snarkvm::prelude::LiteralType::Address => Type::Address,
snarkvm::prelude::LiteralType::Boolean => Type::Boolean,
snarkvm::prelude::LiteralType::Field => Type::Field,
snarkvm::prelude::LiteralType::Group => Type::Group,
snarkvm::prelude::LiteralType::U8 => Type::Integer(IntegerType::U8),
snarkvm::prelude::LiteralType::U16 => Type::Integer(IntegerType::U16),
snarkvm::prelude::LiteralType::U32 => Type::Integer(IntegerType::U32),
snarkvm::prelude::LiteralType::U64 => Type::Integer(IntegerType::U64),
snarkvm::prelude::LiteralType::U128 => Type::Integer(IntegerType::U128),
snarkvm::prelude::LiteralType::I8 => Type::Integer(IntegerType::I8),
snarkvm::prelude::LiteralType::I16 => Type::Integer(IntegerType::I16),
snarkvm::prelude::LiteralType::I32 => Type::Integer(IntegerType::I32),
snarkvm::prelude::LiteralType::I64 => Type::Integer(IntegerType::I64),
snarkvm::prelude::LiteralType::I128 => Type::Integer(IntegerType::I128),
snarkvm::prelude::LiteralType::Scalar => Type::Scalar,
snarkvm::prelude::LiteralType::Signature => Type::Signature,
snarkvm::prelude::LiteralType::String => Type::String,
},
Struct(s) => Type::Identifier(common::Identifier::from(s)),
Array(array) => Type::Array(ArrayType::from(array)),
}
}
}

View File

@ -41,6 +41,10 @@ version = "=1.10.0"
[dependencies.sha2]
version = "0.10"
[dependencies.indexmap]
version = "1.9"
features = []
[dev-dependencies.leo-test-framework]
path = "../../tests/test-framework"
@ -61,7 +65,7 @@ workspace = true
version = "1.10.2"
[dev-dependencies.serde]
version = "1.0.194"
version = "1.0.195"
features = [ "derive" ]
[dev-dependencies.serde_yaml]

View File

@ -17,17 +17,18 @@
//! The compiler for Leo programs.
//!
//! The [`Compiler`] type compiles Leo programs into R1CS circuits.
pub use leo_ast::{Ast, InputAst};
use leo_ast::{NodeBuilder, Program};
pub use leo_ast::Ast;
use leo_ast::{NodeBuilder, Program, Stub};
use leo_errors::{emitter::Handler, CompilerError, Result};
pub use leo_passes::SymbolTable;
use leo_passes::*;
use leo_span::{source_map::FileName, symbol::with_session_globals};
use leo_span::{source_map::FileName, symbol::with_session_globals, Symbol};
use sha2::{Digest, Sha256};
use std::{fs, path::PathBuf};
use crate::CompilerOptions;
use indexmap::{IndexMap, IndexSet};
/// The primary entry point of the Leo compiler.
#[derive(Clone)]
@ -44,8 +45,6 @@ pub struct Compiler<'a> {
pub network: String,
/// The AST for the program.
pub ast: Ast,
/// The input ast for the program if it exists.
pub input_ast: Option<InputAst>,
/// Options configuring compilation.
compiler_options: CompilerOptions,
/// The `NodeCounter` used to generate sequentially increasing `NodeID`s.
@ -54,6 +53,8 @@ pub struct Compiler<'a> {
assigner: Assigner,
/// The type table.
type_table: TypeTable,
/// The stubs for imported programs. Produced by `Retriever` module.
import_stubs: IndexMap<Symbol, Stub>,
}
impl<'a> Compiler<'a> {
@ -65,6 +66,7 @@ impl<'a> Compiler<'a> {
main_file_path: PathBuf,
output_directory: PathBuf,
compiler_options: Option<CompilerOptions>,
import_stubs: IndexMap<Symbol, Stub>,
) -> Self {
let node_builder = NodeBuilder::default();
let assigner = Assigner::default();
@ -76,10 +78,10 @@ impl<'a> Compiler<'a> {
program_name,
network,
ast: Ast::new(Program::default()),
input_ast: None,
compiler_options: compiler_options.unwrap_or_default(),
node_builder,
assigner,
import_stubs,
type_table,
}
}
@ -136,37 +138,6 @@ impl<'a> Compiler<'a> {
self.parse_program_from_string(&program_string, FileName::Real(self.main_file_path.clone()))
}
/// Parses and stores the input file, constructs a syntax tree, and generates a program input.
pub fn parse_input(&mut self, input_file_path: PathBuf) -> Result<()> {
if input_file_path.exists() {
// Load the input file into the source map.
let input_sf = with_session_globals(|s| s.source_map.load_file(&input_file_path))
.map_err(|e| CompilerError::file_read_error(&input_file_path, e))?;
// Parse and serialize it.
let input_ast =
leo_parser::parse_input(self.handler, &self.node_builder, &input_sf.src, input_sf.start_pos)?;
if self.compiler_options.output.initial_ast {
// Write the input AST snapshot post parsing.
if self.compiler_options.output.ast_spans_enabled {
input_ast.to_json_file(
self.output_directory.clone(),
&format!("{}.initial_input_ast.json", self.program_name),
)?;
} else {
input_ast.to_json_file_without_keys(
self.output_directory.clone(),
&format!("{}.initial_input_ast.json", self.program_name),
&["span"],
)?;
}
}
self.input_ast = Some(input_ast);
}
Ok(())
}
/// Runs the symbol table pass.
pub fn symbol_table_pass(&self) -> Result<SymbolTable> {
let symbol_table = SymbolTableCreator::do_pass((&self.ast, self.handler))?;
@ -321,14 +292,16 @@ impl<'a> Compiler<'a> {
}
/// Returns a compiled Leo program.
pub fn compile(&mut self) -> Result<(SymbolTable, String)> {
pub fn compile(&mut self) -> Result<String> {
// Parse the program.
self.parse_program()?;
// Copy the dependencies specified in `program.json` into the AST.
self.add_import_stubs()?;
// Run the intermediate compiler stages.
let (symbol_table, struct_graph, call_graph) = self.compiler_stages()?;
// Run code generation.
let bytecode = self.code_generation_pass(&symbol_table, &struct_graph, &call_graph)?;
Ok((symbol_table, bytecode))
Ok(bytecode)
}
/// Writes the AST to a JSON file.
@ -361,4 +334,41 @@ impl<'a> Compiler<'a> {
}
Ok(())
}
/// Merges the dependencies defined in `program.json` with the dependencies imported in `.leo` file
fn add_import_stubs(&mut self) -> Result<()> {
// Create a list of both the explicit dependencies specified in the `.leo` file, as well as the implicit ones derived from those dependencies.
let (mut unexplored, mut explored): (IndexSet<Symbol>, IndexSet<Symbol>) =
(self.ast.ast.imports.keys().cloned().collect(), IndexSet::new());
while !unexplored.is_empty() {
let mut current_dependencies: IndexSet<Symbol> = IndexSet::new();
for program_name in unexplored.iter() {
if let Some(stub) = self.import_stubs.get(program_name) {
// Add the program to the explored set
explored.insert(*program_name);
for dependency in stub.imports.iter() {
// If dependency is already explored then don't need to re-explore it
if explored.insert(dependency.name.name) {
current_dependencies.insert(dependency.name.name);
}
}
} else {
return Err(CompilerError::imported_program_not_found(
self.program_name.clone(),
*program_name,
self.ast.ast.imports[program_name].1,
)
.into());
}
}
// Create next batch to explore
unexplored = current_dependencies;
}
// Combine the dependencies from `program.json` and `.leo` file while preserving the post-order
self.ast.ast.stubs =
self.import_stubs.clone().into_iter().filter(|(program_name, _)| explored.contains(program_name)).collect();
Ok(())
}
}

View File

@ -44,8 +44,6 @@ pub struct OutputOptions {
pub ast_spans_enabled: bool,
/// If enabled writes the AST after parsing.
pub initial_ast: bool,
/// If enabled writes the input AST after parsing.
pub initial_input_ast: bool,
/// If enabled writes the AST after loop unrolling.
pub unrolled_ast: bool,
/// If enabled writes the AST after static single assignment.

View File

@ -91,7 +91,6 @@ fn run_test(test: Test, handler: &Handler, buf: &BufferEmitter) -> Result<Value,
type_checked_symbol_table: true,
unrolled_symbol_table: true,
ast_spans_enabled: false,
initial_input_ast: true,
initial_ast: true,
unrolled_ast: true,
ssa_ast: true,

View File

@ -105,7 +105,6 @@ fn run_test(test: Test, handler: &Handler, err_buf: &BufferEmitter) -> Result<Va
type_checked_symbol_table: true,
unrolled_symbol_table: true,
ast_spans_enabled: false,
initial_input_ast: true,
initial_ast: true,
unrolled_ast: true,
ssa_ast: true,

View File

@ -29,6 +29,7 @@ use leo_test_framework::{test::TestConfig, Test};
use snarkvm::prelude::*;
use indexmap::IndexMap;
use leo_ast::ProgramVisitor;
use snarkvm::{file::Manifest, package::Package};
use std::{
@ -142,7 +143,15 @@ pub fn new_compiler(
let output_dir = PathBuf::from("/tmp/output/");
fs::create_dir_all(output_dir.clone()).unwrap();
Compiler::new(String::from("test"), String::from("aleo"), handler, main_file_path, output_dir, compiler_options)
Compiler::new(
String::from("test"),
String::from("aleo"),
handler,
main_file_path,
output_dir,
compiler_options,
IndexMap::new(),
)
}
pub fn parse_program<'a>(

View File

@ -48,7 +48,7 @@ version = "1.0"
features = [ "derive" ]
[dependencies.smallvec]
version = "1.11"
version = "1.12"
[dependencies.tracing]
version = "0.1"

View File

@ -1,70 +0,0 @@
// Copyright (C) 2019-2023 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
#![forbid(unsafe_code)]
use leo_ast::NodeBuilder;
use leo_errors::{emitter::Handler, Result};
use leo_span::symbol::create_session_if_not_set_then;
use clap::Parser;
use std::{
fs,
path::{Path, PathBuf},
};
#[derive(Debug, Parser)]
#[clap(name = "input parser", about = "Parse an Input file and save its JSON representation")]
struct Opt {
/// Path to the input file.
input_path: PathBuf,
/// Optional path to the output directory.
out_dir_path: Option<PathBuf>,
/// Whether to print result to STDOUT.
#[clap(short, long)]
print_stdout: bool,
}
fn main() -> Result<(), String> {
let opt = Opt::parse();
let input_tree = create_session_if_not_set_then(|s| {
let input_string = s.source_map.load_file(&opt.input_path).expect("failed to open an input file");
Handler::with(|handler| {
let node_builder = NodeBuilder::default();
let input =
leo_parser::parse_program_inputs(handler, &node_builder, &input_string.src, input_string.start_pos)?;
input.to_json_string()
})
.map_err(|e| e.to_string())
})?;
if opt.print_stdout {
println!("{input_tree}");
}
let out_path = if let Some(out_dir) = opt.out_dir_path {
format!("{}/{}.json", out_dir.as_path().display(), opt.input_path.file_stem().unwrap().to_str().unwrap())
} else {
format!("./{}.json", opt.input_path.file_stem().unwrap().to_str().unwrap())
};
fs::write(Path::new(&out_path), input_tree).expect("failed to write output");
Ok(())
}

View File

@ -31,7 +31,7 @@ pub(crate) use tokenizer::*;
pub mod parser;
pub use parser::*;
use leo_ast::{input::InputData, Ast, NodeBuilder, ProgramInput};
use leo_ast::{Ast, NodeBuilder};
use leo_errors::{emitter::Handler, Result};
#[cfg(test)]
@ -41,16 +41,3 @@ mod test;
pub fn parse_ast(handler: &Handler, node_builder: &NodeBuilder, source: &str, start_pos: BytePos) -> Result<Ast> {
Ok(Ast::new(parser::parse(handler, node_builder, source, start_pos)?))
}
/// Parses program inputs from the input file path
pub fn parse_program_inputs(
handler: &Handler,
node_builder: &NodeBuilder,
input_string: &str,
start_pos: BytePos,
) -> Result<InputData> {
let program_input: ProgramInput =
parser::parse_input(handler, node_builder, input_string, start_pos)?.try_into()?;
Ok(InputData { program_input })
}

View File

@ -39,8 +39,6 @@ pub(crate) struct ParserContext<'a> {
pub(crate) prev_token: SpannedToken,
/// true if parsing an expression for if and loop statements -- means struct inits are not legal
pub(crate) disallow_struct_construction: bool,
/// true if parsing an identifier inside an input file.
pub(crate) allow_identifier_underscores: bool,
}
/// Dummy span used to appease borrow checker.
@ -59,7 +57,6 @@ impl<'a> ParserContext<'a> {
handler,
node_builder,
disallow_struct_construction: false,
allow_identifier_underscores: false,
prev_token: token.clone(),
token,
tokens,

View File

@ -17,7 +17,7 @@
use super::*;
use leo_errors::{ParserError, Result};
use leo_span::{sym, Symbol};
use leo_span::sym;
use snarkvm::console::{account::Address, network::Testnet3};
const INT_TYPES: &[Token] = &[
@ -429,6 +429,25 @@ impl ParserContext<'_> {
self.parse_paren_comma_list(|p| p.parse_expression().map(Some))
}
// Parses an externa function call `credits.aleo/transfer()` or `board.leo/make_move()`
fn parse_external_call(&mut self, expr: Expression) -> Result<Expression> {
// Eat an external function call.
self.eat(&Token::Div); // todo: Make `/` a more general token.
// Parse function name.
let name = self.expect_identifier()?;
// Parse the function call.
let (arguments, _, span) = self.parse_paren_comma_list(|p| p.parse_expression().map(Some))?;
Ok(Expression::Call(CallExpression {
span: expr.span() + span,
function: Box::new(Expression::Identifier(name)),
external: Some(Box::new(expr)),
arguments,
id: self.node_builder.next_id(),
}))
}
/// Returns an [`Expression`] AST node if the next tokens represent an
/// array access, struct member access, function call, or static function call expression.
///
@ -450,21 +469,9 @@ impl ParserContext<'_> {
id: self.node_builder.next_id(),
}))
} else if self.eat(&Token::Leo) {
// Eat an external function call.
self.eat(&Token::Div); // todo: Make `/` a more general token.
// Parse function name.
let name = self.expect_identifier()?;
// Parse the function call.
let (arguments, _, span) = self.parse_paren_comma_list(|p| p.parse_expression().map(Some))?;
expr = Expression::Call(CallExpression {
span: expr.span() + span,
function: Box::new(Expression::Identifier(name)),
external: Some(Box::new(expr)),
arguments,
id: self.node_builder.next_id(),
});
return Err(ParserError::only_aleo_external_calls(expr.span()).into());
} else if self.eat(&Token::Aleo) {
expr = self.parse_external_call(expr)?;
} else {
// Parse identifier name.
let name = self.expect_identifier()?;
@ -616,16 +623,7 @@ impl ParserContext<'_> {
}
fn parse_struct_member(&mut self) -> Result<StructVariableInitializer> {
let identifier = if self.allow_identifier_underscores && self.eat(&Token::Underscore) {
// Allow `_nonce` for struct records.
let identifier_without_underscore = self.expect_identifier()?;
Identifier::new(
Symbol::intern(&format!("_{}", identifier_without_underscore.name)),
self.node_builder.next_id(),
)
} else {
self.expect_identifier()?
};
let identifier = self.expect_identifier()?;
let (expression, span) = if self.eat(&Token::Colon) {
// Parse individual struct variable declarations.

View File

@ -15,11 +15,8 @@
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use super::*;
use crate::parse_ast;
use leo_errors::{CompilerError, ParserError, Result};
use leo_span::{source_map::FileName, symbol::with_session_globals};
use std::fs;
use leo_errors::{ParserError, Result};
impl ParserContext<'_> {
/// Returns a [`Program`] AST if all tokens can be consumed and represent a valid Leo program.
@ -56,7 +53,7 @@ impl ParserContext<'_> {
return Err(ParserError::missing_program_scope(self.token.span).into());
}
Ok(Program { imports, program_scopes })
Ok(Program { imports, stubs: IndexMap::new(), program_scopes })
}
fn unexpected_item(token: &SpannedToken, expected: &[Token]) -> ParserError {
@ -76,45 +73,21 @@ impl ParserContext<'_> {
// Parse `foo`.
let import_name = self.expect_identifier()?;
// Parse `.leo`.
// Parse `.aleo`.
self.expect(&Token::Dot)?;
if !self.eat(&Token::Leo) {
// Throw error for non-leo files.
return Err(ParserError::leo_imports_only(self.token.span).into());
if !self.eat(&Token::Aleo) {
// Throw error for non-aleo files.
return Err(ParserError::invalid_network(self.token.span).into());
}
let end = self.expect(&Token::Semicolon)?;
// Tokenize and parse import file.
// Todo: move this to a different module.
let mut import_file_path =
std::env::current_dir().map_err(|err| CompilerError::cannot_open_cwd(err, self.token.span))?;
import_file_path.push("imports");
import_file_path.push(format!("{}.leo", import_name.name));
// Throw an error if the import file doesn't exist.
if !import_file_path.exists() {
return Err(CompilerError::import_not_found(import_file_path.display(), self.prev_token.span).into());
// Return the import name and the span.
Ok((import_name.name, (Program::default(), start + end)))
}
// Read the import file into string.
// Todo: protect against cyclic imports.
let program_string =
fs::read_to_string(&import_file_path).map_err(|e| CompilerError::file_read_error(&import_file_path, e))?;
// Create import file name.
let name: FileName = FileName::Real(import_file_path);
// Register the source (`program_string`) in the source map.
let prg_sf = with_session_globals(|s| s.source_map.new_source(&program_string, name));
// Use the parser to construct the imported abstract syntax tree (ast).
let program_ast = parse_ast(self.handler, self.node_builder, &prg_sf.src, prg_sf.start_pos)?;
Ok((import_name.name, (program_ast.into_repr(), start + end)))
}
/// Parsers a program scope `program foo.aleo { ... }`.
/// Parses a program scope `program foo.aleo { ... }`.
fn parse_program_scope(&mut self) -> Result<ProgramScope> {
// Parse `program` keyword.
let start = self.expect(&Token::Program)?;
@ -124,15 +97,13 @@ impl ParserContext<'_> {
// Parse the program network.
self.expect(&Token::Dot)?;
let network = self.expect_identifier()?;
// Otherwise throw parser error
self.expect(&Token::Aleo).map_err(|_| ParserError::invalid_network(self.token.span))?;
// Construct the program id.
let program_id = ProgramId { name, network };
// Check that the program network is valid.
if network.name != sym::aleo {
return Err(ParserError::invalid_network(network.span).into());
}
let program_id =
ProgramId { name, network: Identifier::new(Symbol::intern("aleo"), self.node_builder.next_id()) };
// Parse `{`.
self.expect(&Token::LeftCurly)?;
@ -235,6 +206,13 @@ impl ParserContext<'_> {
pub(super) fn parse_struct(&mut self) -> Result<(Symbol, Struct)> {
let is_record = matches!(&self.token.token, Token::Record);
let start = self.expect_any(&[Token::Struct, Token::Record])?;
// Check if using external type
let file_type = self.look_ahead(1, |t| &t.token);
if self.token.token == Token::Dot && (file_type == &Token::Aleo) {
return Err(ParserError::cannot_declare_external_struct(self.token.span).into());
}
let struct_name = self.expect_identifier()?;
self.expect(&Token::LeftCurly)?;
@ -302,9 +280,9 @@ impl ParserContext<'_> {
let external = self.expect_identifier()?;
let mut span = name.span + external.span;
// Parse `.leo/`.
// Parse `.leo/` or `.aleo/`.
self.eat(&Token::Dot);
self.eat(&Token::Leo);
self.eat_any(&[Token::Leo, Token::Aleo]);
self.eat(&Token::Div);
// Parse record name.
@ -349,9 +327,9 @@ impl ParserContext<'_> {
let external = self.expect_identifier()?;
let mut span = external.span;
// Parse `.leo/`.
// Parse `.leo/` or `.aleo/`.
self.eat(&Token::Dot);
self.eat(&Token::Leo);
self.eat_any(&[Token::Leo, Token::Aleo]);
self.eat(&Token::Div);
// Parse record name.
@ -374,7 +352,7 @@ impl ParserContext<'_> {
}
}
fn peek_is_external(&self) -> bool {
pub fn peek_is_external(&self) -> bool {
matches!((&self.token.token, self.look_ahead(1, |t| &t.token)), (Token::Identifier(_), Token::Dot))
}
@ -433,8 +411,15 @@ impl ParserContext<'_> {
}
};
// Parse the function body.
let block = self.parse_block()?;
// Parse the function body. Allow empty blocks. `fn foo(a:u8);`
let (_has_empty_block, block) = match &self.token.token {
Token::LeftCurly => (false, self.parse_block()?),
Token::Semicolon => {
let semicolon = self.expect(&Token::Semicolon)?;
(true, Block { statements: Vec::new(), span: semicolon, id: self.node_builder.next_id() })
}
_ => self.unexpected("block or semicolon")?,
};
// Parse the `finalize` block if it exists.
let finalize = match self.eat(&Token::Finalize) {

View File

@ -1,76 +0,0 @@
// Copyright (C) 2019-2023 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use super::*;
use leo_errors::{ParserError, Result};
impl ParserContext<'_> {
/// Returns a [`ParsedInputFile`] struct filled with the data acquired in the file.
pub(crate) fn parse_input_file(&mut self) -> Result<InputAst> {
// Allow underscores in identifiers for input record declarations.
self.allow_identifier_underscores = true;
let mut sections = Vec::new();
while self.has_next() {
if self.check(&Token::LeftSquare) {
sections.push(self.parse_section()?);
} else {
return Err(ParserError::unexpected_token(self.token.token.clone(), self.token.span).into());
}
}
// Do not allow underscores in identifiers outside of input files.
self.allow_identifier_underscores = false;
Ok(InputAst { sections })
}
/// Parses particular section in the Input or State file.
/// `
/// [<identifier>]
/// <...definition>
/// `
/// Returns [`Section`].
fn parse_section(&mut self) -> Result<Section> {
self.expect(&Token::LeftSquare)?;
let section = self.expect_identifier()?;
self.expect(&Token::RightSquare)?;
let mut definitions = Vec::new();
while let Token::Constant | Token::Public | Token::Identifier(_) = self.token.token {
definitions.push(self.parse_input_definition()?);
}
Ok(Section { name: section.name, span: section.span, definitions })
}
/// Parses a single parameter definition:
/// `<identifier> : <type> = <expression>;`
/// Returns [`Definition`].
fn parse_input_definition(&mut self) -> Result<Definition> {
let mode = self.parse_mode()?;
let name = self.expect_identifier()?;
self.expect(&Token::Colon)?;
let (type_, span) = self.parse_type()?;
self.expect(&Token::Assign)?;
let value = self.parse_unary_expression()?;
self.expect(&Token::Semicolon)?;
Ok(Definition { mode, name, type_, value, span })
}
}

View File

@ -34,7 +34,6 @@ pub(super) use context::ParserContext;
mod expression;
mod file;
mod input;
mod statement;
pub(super) mod type_;
@ -44,15 +43,3 @@ pub fn parse(handler: &Handler, node_builder: &NodeBuilder, source: &str, start_
tokens.parse_program()
}
/// Parses an input file at the given file `path` and `source` code text.
pub fn parse_input(
handler: &Handler,
node_builder: &NodeBuilder,
source: &str,
start_pos: BytePos,
) -> Result<InputAst> {
let mut tokens = ParserContext::new(handler, node_builder, crate::tokenize(source, start_pos)?);
tokens.parse_input_file()
}

View File

@ -79,6 +79,28 @@ impl ParserContext<'_> {
/// Also returns the span of the parsed token.
pub fn parse_type(&mut self) -> Result<(Type, Span)> {
if let Some(ident) = self.eat_identifier() {
// Check if using external type
let file_type = self.look_ahead(1, |t| &t.token);
if self.token.token == Token::Dot && (file_type == &Token::Aleo) {
// Only allow `.aleo` as the network identifier
if file_type == &Token::Leo {
return Err(ParserError::invalid_network(self.token.span).into());
}
// Parse `.aleo/`
self.expect(&Token::Dot)?;
self.expect(&Token::Aleo)?;
self.expect(&Token::Div)?;
// Parse the record name
if let Some(record_name) = self.eat_identifier() {
// Return the external type
return Ok((Type::Identifier(record_name), ident.span + record_name.span));
} else {
return Err(ParserError::invalid_external_type(self.token.span).into());
}
}
Ok((Type::Identifier(ident), ident.span))
} else if self.token.token == Token::LeftSquare {
// Parse the left bracket.

View File

@ -202,18 +202,6 @@ impl Namespace for SerializeNamespace {
}
}
struct InputNamespace;
impl Namespace for InputNamespace {
fn parse_type(&self) -> ParseType {
ParseType::Whole
}
fn run_test(&self, test: Test) -> Result<Value, String> {
create_session_if_not_set_then(|s| with_handler(tokenize(test, s)?, |p| p.parse_input_file()).map(yaml_or_fail))
}
}
struct TestRunner;
impl Runner for TestRunner {
@ -223,7 +211,6 @@ impl Runner for TestRunner {
"ParseExpression" => Box::new(ParseExpressionNamespace),
"ParseStatement" => Box::new(ParseStatementNamespace),
"Serialize" => Box::new(SerializeNamespace),
"Input" => Box::new(InputNamespace),
"Token" => Box::new(TokenNamespace),
_ => return None,
})

View File

@ -379,6 +379,7 @@ impl Token {
match &*identifier {
x if x.starts_with("aleo1") => Token::AddressLit(identifier),
"address" => Token::Address,
"aleo" => Token::Aleo,
"as" => Token::As,
"assert" => Token::Assert,
"assert_eq" => Token::AssertEq,

View File

@ -138,6 +138,7 @@ pub enum Token {
Transition,
// Meta Tokens
Aleo,
Block,
Eof,
Leo,
@ -149,6 +150,7 @@ pub enum Token {
/// because true and false are also boolean literals, which are different tokens from keywords.
pub const KEYWORD_TOKENS: &[Token] = &[
Token::Address,
Token::Aleo,
Token::As,
Token::Assert,
Token::AssertEq,
@ -205,6 +207,7 @@ impl Token {
pub fn keyword_to_symbol(&self) -> Option<Symbol> {
Some(match self {
Token::Address => sym::address,
Token::Aleo => sym::aleo,
Token::As => sym::As,
Token::Assert => sym::assert,
Token::AssertEq => sym::assert_eq,
@ -341,6 +344,7 @@ impl fmt::Display for Token {
U128 => write!(f, "u128"),
Record => write!(f, "record"),
Aleo => write!(f, "aleo"),
As => write!(f, "as"),
Assert => write!(f, "assert"),
AssertEq => write!(f, "assert_eq"),

View File

@ -30,6 +30,7 @@ use leo_ast::{
Identifier,
Literal,
MemberAccess,
ProgramScope,
StructExpression,
TernaryExpression,
TupleExpression,
@ -518,7 +519,6 @@ impl<'a> CodeGenerator<'a> {
}
}
// TODO: Cleanup
fn visit_call(&mut self, input: &'a CallExpression) -> (String, String) {
let (mut call_instruction, has_finalize) = match &input.external {
Some(external) => {
@ -528,7 +528,9 @@ impl<'a> CodeGenerator<'a> {
Expression::Identifier(identifier) => identifier.name,
_ => unreachable!("Parsing guarantees that a program name is always an identifier."),
};
let stub_scope: ProgramScope;
// Lookup the imported program scope.
// TODO: Needs refactor. All imports are stubs now.
let imported_program_scope = match self
.program
.imports
@ -536,7 +538,14 @@ impl<'a> CodeGenerator<'a> {
.and_then(|(program, _)| program.program_scopes.get(&program_name))
{
Some(program) => program,
None => unreachable!("Type checking guarantees that imported programs are well defined."),
None => {
if let Some(stub_program) = self.program.stubs.get(&program_name) {
stub_scope = ProgramScope::from(stub_program.clone());
&stub_scope
} else {
unreachable!("Type checking guarantees that imported and stub programs are well defined.")
}
}
};
// Check if the external function has a finalize block.
let function_name = match *input.function {

View File

@ -28,19 +28,10 @@ impl<'a> CodeGenerator<'a> {
// Accumulate instructions into a program string.
let mut program_string = String::new();
if !input.imports.is_empty() {
// Visit each import statement and produce a Aleo import instruction.
program_string.push_str(
&input
.imports
.iter()
.map(|(identifier, (imported_program, _))| self.visit_import(identifier, imported_program))
.join("\n"),
);
// Newline separator.
program_string.push('\n');
}
// Print out the dependencies of the program. Already arranged in post order by Retriever module.
input.stubs.iter().for_each(|(program_name, _)| {
program_string.push_str(&format!("import {}.aleo;\n", program_name));
});
// Retrieve the program scope.
// Note that type checking guarantees that there is exactly one program scope.
@ -109,15 +100,6 @@ impl<'a> CodeGenerator<'a> {
program_string
}
fn visit_import(&mut self, import_name: &'a Symbol, import_program: &'a Program) -> String {
// Load symbols into composite mapping.
let _import_program_string = self.visit_program(import_program);
// todo: We do not need the import program string because we generate instructions for imports separately during leo build.
// Generate string for import statement.
format!("import {import_name}.aleo;")
}
fn visit_struct_or_record(&mut self, struct_: &'a Struct) -> String {
if struct_.is_record { self.visit_record(struct_) } else { self.visit_struct(struct_) }
}

View File

@ -23,7 +23,7 @@ pub use variable_symbol::*;
use std::cell::RefCell;
use leo_ast::{normalize_json_value, remove_key_from_json, Function, Struct};
use leo_errors::{AstError, Result};
use leo_errors::{AstError, LeoMessageCode, Result};
use leo_span::{Span, Symbol};
use indexmap::IndexMap;
@ -89,12 +89,47 @@ impl SymbolTable {
Ok(())
}
/// Check if the struct is a duplicate of the existing struct.
/// This is used to allow redefinitions of external structs.
pub fn check_duplicate_struct(&self, old: &Struct, new: &Struct) -> bool {
if old.members.len() != new.members.len() {
return false;
}
for (old_member, new_member) in old.members.iter().zip(new.members.iter()) {
if old_member.identifier.name != new_member.identifier.name {
return false;
}
if old_member.type_ != new_member.type_ {
return false;
}
}
true
}
/// Inserts a struct into the symbol table.
pub fn insert_struct(&mut self, symbol: Symbol, insert: &Struct) -> Result<()> {
self.check_shadowing(symbol, insert.span)?;
match self.check_shadowing(symbol, insert.span) {
Ok(_) => {
self.structs.insert(symbol, insert.clone());
Ok(())
}
Err(e) => {
if e.error_code() == AstError::shadowed_struct(symbol, insert.span).error_code() {
if self.check_duplicate_struct(
self.structs.get(&symbol).expect("Must be in symbol table since struct already referenced"),
insert,
) {
Ok(())
} else {
Err(AstError::redefining_external_struct(symbol).into())
}
} else {
Err(e)
}
}
}
}
/// Inserts a variable into the symbol table.
pub fn insert_variable(&mut self, symbol: Symbol, insert: VariableSymbol) -> Result<()> {

View File

@ -34,6 +34,21 @@ impl ProgramReconstructor for Unroller<'_> {
}
}
// Don't need to reconstruct anything, just need to add child scopes for constant propagation table
fn reconstruct_function_stub(&mut self, input: FunctionStub) -> FunctionStub {
// Lookup function metadata in the symbol table.
// Note that this unwrap is safe since function metadata is stored in a prior pass.
let function_index = self.symbol_table.borrow().lookup_fn_symbol(input.identifier.name).unwrap().id;
// Enter the function's scope.
let previous_function_index = self.enter_scope(function_index);
// Exit the function's scope.
self.exit_scope(previous_function_index);
input
}
fn reconstruct_function(&mut self, function: Function) -> Function {
// Lookup function metadata in the symbol table.
// Note that this unwrap is safe since function metadata is stored in a prior pass.

View File

@ -151,6 +151,7 @@ impl ProgramConsumer for StaticSingleAssigner<'_> {
.into_iter()
.map(|(name, (import, span))| (name, (self.consume_program(import), span)))
.collect(),
stubs: input.stubs,
program_scopes: input
.program_scopes
.into_iter()

View File

@ -72,4 +72,16 @@ impl<'a> ProgramVisitor<'a> for SymbolTableCreator<'a> {
self.handler.emit_err(err);
}
}
fn visit_stub(&mut self, input: &'a Stub) {
input.functions.iter().for_each(|(_, c)| (self.visit_function_stub(c)));
input.structs.iter().for_each(|(_, c)| (self.visit_struct(c)));
}
fn visit_function_stub(&mut self, input: &'a FunctionStub) {
if let Err(err) = self.symbol_table.insert_fn(input.name(), &Function::from(input.clone())) {
self.handler.emit_err(err);
}
}
}

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{DiGraphError, TypeChecker, VariableSymbol, VariableType};
use crate::{DiGraphError, TypeChecker};
use leo_ast::*;
use leo_errors::TypeCheckerError;
@ -28,30 +28,80 @@ use std::collections::HashSet;
impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
fn visit_program(&mut self, input: &'a Program) {
match self.is_imported {
// If the program is imported, then it is not allowed to import any other programs.
true => {
input.imports.values().for_each(|(_, span)| {
self.emit_err(TypeCheckerError::imported_program_cannot_import_program(*span))
// Calculate the intersection of the imports specified in the `.leo` file and the dependencies derived from the `program.json` file.
// Typecheck the program's stubs.
input.stubs.iter().for_each(|(symbol, stub)| {
if symbol != &stub.stub_id.name.name {
self.emit_err(TypeCheckerError::stub_name_mismatch(
symbol,
stub.stub_id.name,
stub.stub_id.network.span,
));
}
self.visit_stub(stub)
});
}
// Otherwise, typecheck the imported programs.
false => {
// Set `self.is_imported`.
let previous_is_imported = core::mem::replace(&mut self.is_imported, true);
// Typecheck the imported programs.
input.imports.values().for_each(|import| self.visit_import(&import.0));
// Set `self.is_imported` to its previous state.
self.is_imported = previous_is_imported;
}
}
// Typecheck the program scopes.
input.program_scopes.values().for_each(|scope| self.visit_program_scope(scope));
}
fn visit_stub(&mut self, input: &'a Stub) {
// Cannot have constant declarations in stubs.
if !input.consts.is_empty() {
self.emit_err(TypeCheckerError::stubs_cannot_have_const_declarations(input.consts.get(0).unwrap().1.span));
}
// Typecheck the program's structs.
input.structs.iter().for_each(|(_, function)| self.visit_struct_stub(function));
// Typecheck the program's functions.
input.functions.iter().for_each(|(_, function)| self.visit_function_stub(function));
}
fn visit_function_stub(&mut self, input: &'a FunctionStub) {
// Must not be an inline function
if input.variant == Variant::Inline {
self.emit_err(TypeCheckerError::stub_functions_must_not_be_inlines(input.span));
}
// Lookup function metadata in the symbol table.
// Note that this unwrap is safe since function metadata is stored in a prior pass.
let function_index = self.symbol_table.borrow().lookup_fn_symbol(input.identifier.name).unwrap().id;
// Enter the function's scope.
self.enter_scope(function_index);
// Create a new child scope for the function's parameters and body.
let scope_index = self.create_child_scope();
// Query helper function to type check function parameters and outputs.
self.check_function_signature(&Function::from(input.clone()));
// Exit the scope for the function's parameters and body.
self.exit_scope(scope_index);
// Check that the finalize scope is valid
if input.finalize_stub.is_some() {
// Create a new child scope for the finalize block.
let scope_index = self.create_child_scope();
// Check the finalize signature.
let function = &Function::from(input.clone());
self.check_finalize_signature(function.finalize.as_ref().unwrap(), function);
// Exit the scope for the finalize block.
self.exit_scope(scope_index);
}
// Exit the function's scope.
self.exit_scope(function_index);
}
fn visit_struct_stub(&mut self, input: &'a Struct) {
self.visit_struct(input);
}
fn visit_program_scope(&mut self, input: &'a ProgramScope) {
// Typecheck each const definition, and append to symbol table.
input.consts.iter().for_each(|(_, c)| self.visit_const(c));
@ -232,77 +282,8 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
// Create a new child scope for the function's parameters and body.
let scope_index = self.create_child_scope();
// Type check the function's parameters.
function.input.iter().for_each(|input_var| {
// Check that the type of input parameter is defined.
self.assert_type_is_valid(&input_var.type_(), input_var.span());
// Check that the type of the input parameter is not a tuple.
if matches!(input_var.type_(), Type::Tuple(_)) {
self.emit_err(TypeCheckerError::function_cannot_take_tuple_as_input(input_var.span()))
}
// Note that this unwrap is safe since we assign to `self.variant` above.
match self.variant.unwrap() {
// If the function is a transition function, then check that the parameter mode is not a constant.
Variant::Transition if input_var.mode() == Mode::Constant => {
self.emit_err(TypeCheckerError::transition_function_inputs_cannot_be_const(input_var.span()))
}
// If the function is not a transition function, then check that the parameters do not have an associated mode.
Variant::Standard | Variant::Inline if input_var.mode() != Mode::None => {
self.emit_err(TypeCheckerError::regular_function_inputs_cannot_have_modes(input_var.span()))
}
_ => {} // Do nothing.
}
// Check for conflicting variable names.
if let Err(err) =
self.symbol_table.borrow_mut().insert_variable(input_var.identifier().name, VariableSymbol {
type_: input_var.type_(),
span: input_var.identifier().span(),
declaration: VariableType::Input(input_var.mode()),
})
{
self.handler.emit_err(err);
}
});
// Type check the function's return type.
// Note that checking that each of the component types are defined is sufficient to check that `output_type` is defined.
function.output.iter().for_each(|output| {
match output {
Output::External(external) => {
// If the function is not a transition function, then it cannot output a record.
// Note that an external output must always be a record.
if !matches!(function.variant, Variant::Transition) {
self.emit_err(TypeCheckerError::function_cannot_output_record(external.span()));
}
// Otherwise, do not type check external record function outputs.
// TODO: Verify that this is not needed when the import system is updated.
}
Output::Internal(function_output) => {
// Check that the type of output is defined.
if self.assert_type_is_valid(&function_output.type_, function_output.span) {
// If the function is not a transition function, then it cannot output a record.
if let Type::Identifier(identifier) = function_output.type_ {
if !matches!(function.variant, Variant::Transition)
&& self.symbol_table.borrow().lookup_struct(identifier.name).unwrap().is_record
{
self.emit_err(TypeCheckerError::function_cannot_output_record(function_output.span));
}
}
}
// Check that the type of the output is not a tuple. This is necessary to forbid nested tuples.
if matches!(&function_output.type_, Type::Tuple(_)) {
self.emit_err(TypeCheckerError::nested_tuple_type(function_output.span))
}
// Check that the mode of the output is valid.
// For functions, only public and private outputs are allowed
if function_output.mode == Mode::Constant {
self.emit_err(TypeCheckerError::cannot_have_constant_output_mode(function_output.span));
}
}
}
});
// Query helper function to type check function parameters and outputs.
self.check_function_signature(function);
self.visit_block(&function.block);
@ -324,90 +305,16 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
self.is_finalize = true;
// The function's finalize block does not have a return statement.
self.has_return = false;
// The function;s finalize block does not have a finalize statement.
// The function's finalize block does not have a finalize statement.
self.has_finalize = false;
// Check that the function is a transition function.
if !matches!(function.variant, Variant::Transition) {
self.emit_err(TypeCheckerError::only_transition_functions_can_have_finalize(finalize.span));
}
// Check that the name of the finalize block matches the function name.
if function.identifier.name != finalize.identifier.name {
self.emit_err(TypeCheckerError::finalize_name_mismatch(
function.identifier.name,
finalize.identifier.name,
finalize.span,
));
}
// Create a new child scope for the finalize block.
let scope_index = self.create_child_scope();
finalize.input.iter().for_each(|input_var| {
// Check that the type of input parameter is defined.
if self.assert_type_is_valid(&input_var.type_(), input_var.span()) {
// Check that the input parameter is not a tuple.
if matches!(input_var.type_(), Type::Tuple(_)) {
self.emit_err(TypeCheckerError::finalize_cannot_take_tuple_as_input(input_var.span()))
}
// Check that the input parameter is not a record.
if let Type::Identifier(identifier) = input_var.type_() {
// Note that this unwrap is safe, as the type is defined.
if self.symbol_table.borrow().lookup_struct(identifier.name).unwrap().is_record {
self.emit_err(TypeCheckerError::finalize_cannot_take_record_as_input(input_var.span()))
}
}
// Check that the input parameter is not constant or private.
if input_var.mode() == Mode::Constant || input_var.mode() == Mode::Private {
self.emit_err(TypeCheckerError::finalize_input_mode_must_be_public(input_var.span()));
}
// Check for conflicting variable names.
if let Err(err) =
self.symbol_table.borrow_mut().insert_variable(input_var.identifier().name, VariableSymbol {
type_: input_var.type_(),
span: input_var.identifier().span(),
declaration: VariableType::Input(input_var.mode()),
})
{
self.handler.emit_err(err);
}
}
});
// Check the finalize signature.
self.check_finalize_signature(finalize, function);
// Check that the finalize block's return type is a unit type.
// Note: This is a temporary restriction to be compatible with the current version of snarkVM.
// Note: This restriction may be lifted in the future.
// Note: This check is still compatible with the other checks below.
if finalize.output_type != Type::Unit {
self.emit_err(TypeCheckerError::finalize_cannot_return_value(finalize.span));
}
// Type check the finalize block's return type.
// Note that checking that each of the component types are defined is sufficient to guarantee that the `output_type` is defined.
finalize.output.iter().for_each(|output_type| {
// Check that the type of output is defined.
if self.assert_type_is_valid(&output_type.type_(), output_type.span()) {
// Check that the output is not a tuple. This is necessary to forbid nested tuples.
if matches!(&output_type.type_(), Type::Tuple(_)) {
self.emit_err(TypeCheckerError::nested_tuple_type(output_type.span()))
}
// Check that the output is not a record.
if let Type::Identifier(identifier) = output_type.type_() {
// Note that this unwrap is safe, as the type is defined.
if self.symbol_table.borrow().lookup_struct(identifier.name).unwrap().is_record {
self.emit_err(TypeCheckerError::finalize_cannot_output_record(output_type.span()))
}
}
// Check that the mode of the output is valid.
// Note that a finalize block can have only public outputs.
if matches!(output_type.mode(), Mode::Constant | Mode::Private) {
self.emit_err(TypeCheckerError::finalize_output_mode_must_be_public(output_type.span()));
}
}
});
// TODO: Remove if this restriction is relaxed at Aleo instructions level.
// TODO: Remove if this restriction is relaxed at Aleo instructions level
// Check that the finalize block is not empty.
if finalize.block.statements.is_empty() {
self.emit_err(TypeCheckerError::finalize_block_must_not_be_empty(finalize.span));
@ -416,9 +323,6 @@ impl<'a> ProgramVisitor<'a> for TypeChecker<'a> {
// Type check the finalize block.
self.visit_block(&finalize.block);
// Check that the return type is defined. Note that the component types are already checked.
self.assert_type_is_valid(&finalize.output_type, finalize.span);
// If the function has a return type, then check that it has a return.
if finalize.output_type != Type::Unit && !self.has_return {
self.emit_err(TypeCheckerError::missing_return(finalize.span));

View File

@ -14,9 +14,22 @@
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::{CallGraph, StructGraph, SymbolTable, TypeTable};
use crate::{CallGraph, StructGraph, SymbolTable, TypeTable, VariableSymbol, VariableType};
use leo_ast::{CoreConstant, CoreFunction, Identifier, IntegerType, MappingType, Node, Type, Variant};
use leo_ast::{
CoreConstant,
CoreFunction,
Finalize,
Function,
Identifier,
IntegerType,
MappingType,
Mode,
Node,
Output,
Type,
Variant,
};
use leo_errors::{emitter::Handler, TypeCheckerError};
use leo_span::{Span, Symbol};
@ -47,8 +60,6 @@ pub struct TypeChecker<'a> {
/// Whether or not we are currently traversing a finalize block.
pub(crate) is_finalize: bool,
/// Whether or not we are currently traversing an imported program.
pub(crate) is_imported: bool,
/// Whether or not we are currently traversing a return statement.
pub(crate) is_return: bool,
}
@ -116,7 +127,6 @@ impl<'a> TypeChecker<'a> {
has_return: false,
has_finalize: false,
is_finalize: false,
is_imported: false,
is_return: false,
}
}
@ -1130,6 +1140,176 @@ impl<'a> TypeChecker<'a> {
pub(crate) fn assert_array_type(&self, type_: &Option<Type>, span: Span) {
self.check_type(|type_| matches!(type_, Type::Array(_)), "array".to_string(), type_, span);
}
/// Helper function to check that the input and output of function are valid
pub(crate) fn check_function_signature(&mut self, function: &Function) {
self.variant = Some(function.variant);
// Type check the function's parameters.
function.input.iter().for_each(|input_var| {
// Check that the type of input parameter is defined.
self.assert_type_is_valid(&input_var.type_(), input_var.span());
// Check that the type of the input parameter is not a tuple.
if matches!(input_var.type_(), Type::Tuple(_)) {
self.emit_err(TypeCheckerError::function_cannot_take_tuple_as_input(input_var.span()))
}
// Note that this unwrap is safe since we assign to `self.variant` above.
match self.variant.unwrap() {
// If the function is a transition function, then check that the parameter mode is not a constant.
Variant::Transition if input_var.mode() == Mode::Constant => {
self.emit_err(TypeCheckerError::transition_function_inputs_cannot_be_const(input_var.span()))
}
// If the function is not a transition function, then check that the parameters do not have an associated mode.
Variant::Standard | Variant::Inline if input_var.mode() != Mode::None => {
self.emit_err(TypeCheckerError::regular_function_inputs_cannot_have_modes(input_var.span()))
}
_ => {} // Do nothing.
}
// If the function is not a transition function, then it cannot have a record as input
if let Type::Identifier(identifier) = input_var.type_() {
if let Some(val) = self.symbol_table.borrow().lookup_struct(identifier.name) {
if val.is_record && !matches!(function.variant, Variant::Transition) {
self.emit_err(TypeCheckerError::function_cannot_input_or_output_a_record(input_var.span()));
}
}
}
// Check for conflicting variable names.
if let Err(err) =
self.symbol_table.borrow_mut().insert_variable(input_var.identifier().name, VariableSymbol {
type_: input_var.type_(),
span: input_var.identifier().span(),
declaration: VariableType::Input(input_var.mode()),
})
{
self.handler.emit_err(err);
}
});
// Type check the function's return type.
// Note that checking that each of the component types are defined is sufficient to check that `output_type` is defined.
function.output.iter().for_each(|output| {
match output {
Output::External(external) => {
// If the function is not a transition function, then it cannot output a record.
// Note that an external output must always be a record.
if !matches!(function.variant, Variant::Transition) {
self.emit_err(TypeCheckerError::function_cannot_input_or_output_a_record(external.span()));
}
// Otherwise, do not type check external record function outputs.
// TODO: Verify that this is not needed when the import system is updated.
}
Output::Internal(function_output) => {
// Check that the type of output is defined.
if self.assert_type_is_valid(&function_output.type_, function_output.span) {
// If the function is not a transition function, then it cannot output a record.
if let Type::Identifier(identifier) = function_output.type_ {
if !matches!(function.variant, Variant::Transition)
&& self.symbol_table.borrow().lookup_struct(identifier.name).unwrap().is_record
{
self.emit_err(TypeCheckerError::function_cannot_input_or_output_a_record(
function_output.span,
));
}
}
}
// Check that the type of the output is not a tuple. This is necessary to forbid nested tuples.
if matches!(&function_output.type_, Type::Tuple(_)) {
self.emit_err(TypeCheckerError::nested_tuple_type(function_output.span))
}
// Check that the mode of the output is valid.
// For functions, only public and private outputs are allowed
if function_output.mode == Mode::Constant {
self.emit_err(TypeCheckerError::cannot_have_constant_output_mode(function_output.span));
}
}
}
});
}
pub(crate) fn check_finalize_signature(&mut self, finalize: &Finalize, function: &Function) {
// Check that the function is a transition function.
if !matches!(function.variant, Variant::Transition) {
self.emit_err(TypeCheckerError::only_transition_functions_can_have_finalize(finalize.span));
}
// Check that the name of the finalize block matches the function name.
if function.identifier.name != finalize.identifier.name {
self.emit_err(TypeCheckerError::finalize_name_mismatch(
function.identifier.name,
finalize.identifier.name,
finalize.span,
));
}
finalize.input.iter().for_each(|input_var| {
// Check that the type of input parameter is defined.
if self.assert_type_is_valid(&input_var.type_(), input_var.span()) {
// Check that the input parameter is not a tuple.
if matches!(input_var.type_(), Type::Tuple(_)) {
self.emit_err(TypeCheckerError::finalize_cannot_take_tuple_as_input(input_var.span()))
}
// Check that the input parameter is not a record.
if let Type::Identifier(identifier) = input_var.type_() {
// Note that this unwrap is safe, as the type is defined.
if self.symbol_table.borrow().lookup_struct(identifier.name).unwrap().is_record {
self.emit_err(TypeCheckerError::finalize_cannot_take_record_as_input(input_var.span()))
}
}
// Check that the input parameter is not constant or private.
if input_var.mode() == Mode::Constant || input_var.mode() == Mode::Private {
self.emit_err(TypeCheckerError::finalize_input_mode_must_be_public(input_var.span()));
}
// Check for conflicting variable names.
if let Err(err) =
self.symbol_table.borrow_mut().insert_variable(input_var.identifier().name, VariableSymbol {
type_: input_var.type_(),
span: input_var.identifier().span(),
declaration: VariableType::Input(input_var.mode()),
})
{
self.handler.emit_err(err);
}
}
});
// Check that the finalize block's return type is a unit type.
// Note: This is a temporary restriction to be compatible with the current version of snarkVM.
// Note: This restriction may be lifted in the future.
// Note: This check is still compatible with the other checks below.
if finalize.output_type != Type::Unit {
self.emit_err(TypeCheckerError::finalize_cannot_return_value(finalize.span));
}
// Type check the finalize block's return type.
// Note that checking that each of the component types are defined is sufficient to guarantee that the `output_type` is defined.
finalize.output.iter().for_each(|output_type| {
// Check that the type of output is defined.
if self.assert_type_is_valid(&output_type.type_(), output_type.span()) {
// Check that the output is not a tuple. This is necessary to forbid nested tuples.
if matches!(&output_type.type_(), Type::Tuple(_)) {
self.emit_err(TypeCheckerError::nested_tuple_type(output_type.span()))
}
// Check that the output is not a record.
if let Type::Identifier(identifier) = output_type.type_() {
// Note that this unwrap is safe, as the type is defined.
if self.symbol_table.borrow().lookup_struct(identifier.name).unwrap().is_record {
self.emit_err(TypeCheckerError::finalize_cannot_output_record(output_type.span()))
}
}
// Check that the mode of the output is valid.
// Note that a finalize block can have only public outputs.
if matches!(output_type.mode(), Mode::Constant | Mode::Private) {
self.emit_err(TypeCheckerError::finalize_output_mode_must_be_public(output_type.span()));
}
}
});
// Check that the return type is defined. Note that the component types are already checked.
self.assert_type_is_valid(&finalize.output_type, finalize.span);
}
}
fn types_to_string(types: &[Type]) -> String {

View File

@ -29,5 +29,5 @@ version = "0.2.1"
version = "1.0.1"
[dependencies.serde]
version = "1.0.194"
version = "1.0.195"
features = [ "derive", "rc" ]

View File

@ -268,6 +268,7 @@ symbols! {
owner,
_nonce,
program,
stub,
block,
height,
}

View File

@ -1,6 +1,6 @@
# Summary
This is an RFC to propose RFC format for Leo language.
This is a RFC to propose RFC format for Leo language.
# Motivation
@ -12,11 +12,11 @@ This section describes proposed solution.
## Store RFCs inside Leo repository.
At early stages it is for better to see changes with the code eliminating the need to keep track of a different repository.
At early stages it is better to see changes with the code eliminating the need to keep track of a different repository.
## Use standard PR mechanics for submitting new RFCs
New RFCs should be submitted as a PRs into Leo repository. PRs should be correctly labeled for easier search. Yet they should not have number unless PR is accepted by leo maintainers.
New RFCs should be submitted as PRs into the Leo repository. PRs should be correctly labeled for easier search. Yet they should not have a number unless the PR is accepted by leo maintainers.
## Increase approvals count for RFCs
@ -24,7 +24,7 @@ RFCs may propose changes affecting multiple systems or projects. They also intro
## Format
For bootstrapping new requests template is made and placed into RFC folder.
For bootstrapping new requests, a template is made and placed into the RFC folder.
## Number

View File

@ -37,8 +37,11 @@ version = "0.6.1"
[dependencies.derivative]
version = "2.2.0"
[dependencies.reqwest]
version = "0.11.22"
[dependencies.serde]
version = "1.0.194"
version = "1.0.195"
features = [ "derive", "rc" ]
[dependencies.thiserror]

View File

@ -2,8 +2,6 @@
## Parser Errors: Error Code Range 370_000 - 370_999
## State Errors: Error Code Range 371_000 - 371_999
## AST Errors: Error Code Range 372_000 - 372_999
## ASG Errors: Error Code Range 373_000 - 373_999

View File

@ -49,10 +49,6 @@ The errors for the `leo-compiler` crate. Its error codes will range from 6_000-6
The errors for the `leo-imports` crate. Its error codes will range from 4_000-4_999 and be prefixed with the characters `IMP`.
### Input
The errors for the `leo-ast` crate. Its error codes will range from 8_000-8_999 and be prefixed with the characters `INP`.
### Loop Unrolling
The errors for loop unrolling in the `leo-passes` crate. Its error codes will range from 9_000-9_999 and be prefixed with the characters `LUN`.
@ -70,6 +66,6 @@ The errors for the `leo-parser` crate. Its error codes will range from 0-999 and
The errors from SnarkVM that bubble up into Leo in some situations. For right now, they have an exit code of 1.
When SnarkVM implements better error codes and messages, we can bubble them up.
### State
### Utils
The errors for the `leo-state` crate. Its error codes will range from 1_000-1_999 and be prefixed with the characters `STA`.
The errors related to dependency retrieval in the `utils` crate. Its error codes will range from 10_000-10_999 and be prefixed with the characters `DEP`.

View File

@ -145,4 +145,11 @@ create_messages!(
msg: format!("failed to convert symbol_table to a json value {error}"),
help: None,
}
@backtraced
redefining_external_struct {
args: (struct_: impl Display),
msg: format!("There are two mismatched definitions of struct `{struct_}`."),
help: Some("Duplicate definitions of structs are required to use external structs, but each field's name and type must match exactly.".to_string()),
}
);

View File

@ -70,4 +70,11 @@ create_messages!(
msg: format!("The program scope name `{program_scope_name}` must match `{file_name}`."),
help: None,
}
@formatted
imported_program_not_found {
args: (main_program_name: impl Display, dependency_name: impl Display),
msg: format!("`{main_program_name}` imports `{dependency_name}.aleo`, but `{dependency_name}.aleo` is not found in `program.json`."),
help: None,
}
);

View File

@ -1,58 +0,0 @@
// Copyright (C) 2019-2023 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::create_messages;
use std::fmt::{Debug, Display};
create_messages!(
/// InputError enum that represents all the errors for the inputs part of `leo-ast` crate.
InputError,
code_mask: 1000i32,
code_prefix: "INP",
/// For when declared variable type mismatches actual type.
@formatted
unexpected_type {
args: (expected: impl Display, received: impl Display),
msg: format!(
"unexpected type, expected: '{expected}', received: '{received}'",
),
help: None,
}
/// For when the expression is not allowed in an input file.
@formatted
illegal_expression {
args: (expr: impl Display),
msg: format!("expression '{expr}' is not allowed in inputs"),
help: None,
}
/// For when section name is not an allowed one.
@formatted
unexpected_section {
args: (expected: &[impl Display], received: impl Display),
msg: format!(
"unexpected section: expected {} -- got '{received}'",
expected
.iter()
.map(|x| format!("'{x}'"))
.collect::<Vec<_>>()
.join(", ")
),
help: None,
}
);

View File

@ -33,10 +33,7 @@ pub use self::compiler::*;
pub mod flattener;
pub use self::flattener::*;
/// Contains the Input error definitions.
pub mod input;
pub use self::input::*;
/// Contains the Loop Unroller error definitions.
pub mod loop_unroller;
pub use self::loop_unroller::*;
@ -50,9 +47,12 @@ pub use self::parser::*;
/// Contains the Type Checker error definitions.
pub mod type_checker;
pub use self::type_checker::*;
/// Contains the Utils error definitions.
pub mod utils;
pub use self::utils::*;
/// The LeoError type that contains all sub error types.
/// This allows a unified error type throughout the Leo crates.
#[derive(Debug, Error)]
@ -66,9 +66,6 @@ pub enum LeoError {
/// Represents an Compiler Error in a Leo Error.
#[error(transparent)]
CompilerError(#[from] CompilerError),
/// Represents an Input Error in a Leo Error.
#[error(transparent)]
InputError(#[from] InputError),
/// Represents an Package Error in a Leo Error.
#[error(transparent)]
PackageError(#[from] PackageError),
@ -88,6 +85,9 @@ pub enum LeoError {
/// not re-displaying an error.
#[error("")]
LastErrorCode(i32),
/// Represents a Utils Error in a Leo Error
#[error(transparent)]
UtilError(#[from] UtilError),
/// Anyhow errors.
#[error(transparent)]
Anyhow(#[from] anyhow::Error),
@ -102,12 +102,12 @@ impl LeoError {
AstError(error) => error.error_code(),
CompilerError(error) => error.error_code(),
CliError(error) => error.error_code(),
InputError(error) => error.error_code(),
ParserError(error) => error.error_code(),
PackageError(error) => error.error_code(),
TypeCheckerError(error) => error.error_code(),
LoopUnrollerError(error) => error.error_code(),
FlattenError(error) => error.error_code(),
UtilError(error) => error.error_code(),
LastErrorCode(_) => unreachable!(),
Anyhow(_) => unimplemented!(), // todo: implement error codes for snarkvm errors.
}
@ -121,12 +121,12 @@ impl LeoError {
AstError(error) => error.exit_code(),
CompilerError(error) => error.exit_code(),
CliError(error) => error.exit_code(),
InputError(error) => error.exit_code(),
ParserError(error) => error.exit_code(),
PackageError(error) => error.exit_code(),
TypeCheckerError(error) => error.exit_code(),
LoopUnrollerError(error) => error.exit_code(),
FlattenError(error) => error.exit_code(),
UtilError(error) => error.exit_code(),
LastErrorCode(code) => *code,
Anyhow(_) => unimplemented!(), // todo: implement exit codes for snarkvm errors.
}

View File

@ -264,6 +264,27 @@ create_messages!(
help: Some("Create a package by running `leo new`.".to_string()),
}
@backtraced
failed_to_read_manifest {
args: (error: impl Display),
msg: format!("Failed to read manifest file: {error}"),
help: Some("Create a package by running `leo new`.".to_string()),
}
@backtraced
failed_to_write_manifest {
args: (error: impl Display),
msg: format!("Failed to write manifest file: {error}"),
help: Some("Create a package by running `leo new`.".to_string()),
}
@backtraced
failed_to_create_manifest {
args: (error: impl Display),
msg: format!("Failed to create manifest file: {error}"),
help: Some("Create a package by running `leo new`.".to_string()),
}
@backtraced
failed_to_open_aleo_file {
args: (error: impl Display),
@ -313,4 +334,18 @@ create_messages!(
msg: format!("IO error env file from the provided file path - {error}"),
help: None,
}
@backtraced
failed_to_deserialize_manifest_file {
args: (path: impl Display, error: impl ErrorArg),
msg: format!("Failed to deserialize `program.json` from the provided file path {path} - {error}"),
help: None,
}
@backtraced
failed_to_serialize_manifest_file {
args: (path: impl Display, error: impl ErrorArg),
msg: format!("Failed to update `program.json` from the provided file path {path} - {error}"),
help: None,
}
);

View File

@ -215,10 +215,10 @@ create_messages!(
}
@formatted
leo_imports_only {
leo_and_aleo_imports_only {
args: (),
msg: "Invalid import call to non-leo file.",
help: Some("Only imports of Leo `.leo` files are currently supported.".to_string()),
msg: "Invalid import call to non-leo non-aleo file.",
help: Some("Only imports of Leo `.leo` and Aleo `.aleo` files are currently supported.".to_string()),
}
@formatted
@ -252,7 +252,7 @@ create_messages!(
@formatted
invalid_network {
args: (),
msg: "Invalid network identifier. The only supported identifier is `aleo`.",
msg: "Invalid network identifier. The only supported identifier is `.aleo`.",
help: None,
}
@ -298,4 +298,41 @@ create_messages!(
msg: format!("An array {kind} must have at least one element."),
help: None,
}
@formatted
invalid_external_type {
args: (),
msg: format!("Invalid external type."),
help: Some("External type should have the form `<program>.aleo/<record>`. For example `bank.aleo/loan`".to_string()),
}
@formatted
cannot_declare_external_struct {
args: (),
msg: format!("Cannot declare external struct."),
help: None,
}
/// Enforce that cannot use an external type to do anything except input/output of function
@formatted
external_type_cannot_be_used_inside_function {
args: (program: impl Display, file_type: impl Display),
msg: format!("External types cannot be used inside function (only as input/output types) -- found exported type from '{program}.{file_type}'."),
help: None,
}
/// Enforce that cannot use import in program scope
@formatted
cannot_import_inside_program_body {
args: (),
msg: format!("Cannot use import inside program body."),
help: None,
}
@formatted
only_aleo_external_calls {
args: (),
msg: format!("Only external calls to `.aleo` programs are supported."),
help: None,
}
);

View File

@ -1,19 +0,0 @@
// Copyright (C) 2019-2023 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
/// This module contains the State error definitions.
pub mod state_errors;
pub use self::state_errors::*;

View File

@ -454,7 +454,7 @@ create_messages!(
help: None,
}
// TODO: Consider chainging this to a warning.
// TODO: Consider changing this to a warning.
@formatted
assign_unit_expression_to_variable {
@ -534,9 +534,9 @@ create_messages!(
}
@formatted
function_cannot_output_record {
function_cannot_input_or_output_a_record {
args: (),
msg: format!("A `function` cannot output a record."),
msg: format!("A `function` cannot have a record as input or output."),
help: None,
}
@ -685,6 +685,20 @@ create_messages!(
help: None,
}
@formatted
stub_functions_must_not_be_inlines {
args: (),
msg: format!("Function stubs must be transitions or functions not inlines"),
help: None,
}
@formatted
stub_functions_must_be_empty {
args: (),
msg: format!("Functions stubs must be empty"),
help: None,
}
@formatted
array_empty {
args: (),
@ -712,4 +726,25 @@ create_messages!(
msg: format!("An array cannot have a record as an element type"),
help: None,
}
@formatted
stubs_cannot_have_non_record_structs {
args: (),
msg: format!("Stubs can only have records, transitions, functions, mappings and imports -- found non-record struct"),
help: None,
}
@formatted
stubs_cannot_have_const_declarations {
args: (),
msg: format!("Stubs can only have records, transitions, functions, mappings and imports -- found const declaration"),
help: None,
}
@formatted
stub_name_mismatch {
args: (stub_name: impl Display, program_name: impl Display),
msg: format!("`stub` name `{stub_name}` does not match program name `{program_name}`"),
help: Some("Check that the name you used as a dependency in program.json matches the name you used to import the program in the main leo file.".to_string()),
}
);

View File

@ -15,5 +15,5 @@
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
/// This module contains the Input error definitions.
pub mod input_errors;
pub use self::input_errors::*;
pub mod util_errors;
pub use self::util_errors::*;

View File

@ -0,0 +1,147 @@
// Copyright (C) 2019-2023 Aleo Systems Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::create_messages;
use std::{
error::Error as ErrorArg,
fmt::{Debug, Display},
};
create_messages!(
/// InputError enum that represents all the errors for the `utils` crate.
UtilError,
code_mask: 10000i32,
code_prefix: "UTL",
@formatted
util_file_io_error {
args: (msg: impl Display, err: impl ErrorArg),
msg: format!("File system io error: {msg}. Error: {err}"),
help: None,
}
@formatted
toml_serizalization_error {
args: (error: impl ErrorArg),
msg: format!("TOML serialization error: {error}"),
help: None,
}
@formatted
json_serialization_error {
args: (error: impl ErrorArg),
msg: format!("JSON serialization error: {error}"),
help: None,
}
@formatted
snarkvm_parsing_error {
args: (),
msg: format!("SnarkVM failure to parse `.aleo` program"),
help: None,
}
@formatted
circular_dependency_error {
args: (),
msg: format!("Circular dependency detected"),
help: None,
}
@formatted
network_error {
args: (url: impl Display, status: impl Display),
msg: format!("Failed network request to {url}. Status: {status}"),
help: None,
}
@formatted
duplicate_dependency_name_error {
args: (dependency: impl Display),
msg: format!("Duplicate dependency found: {dependency}"),
help: None,
}
@backtraced
reqwest_error {
args: (error: impl Display),
msg: format!("{}", error),
help: None,
}
@backtraced
failed_to_open_file {
args: (error: impl Display),
msg: format!("Failed to open file {error}"),
help: None,
}
@backtraced
failed_to_read_file {
args: (error: impl Display),
msg: format!("Failed to read file {error}"),
help: None,
}
@backtraced
failed_to_deserialize_file {
args: (error: impl Display),
msg: format!("Failed to deserialize file {error}"),
help: None,
}
@formatted
failed_to_retrieve_dependencies {
args: (error: impl Display),
msg: format!("Failed to retrieve dependencies. {error}"),
help: None,
}
@formatted
missing_network_error {
args: (dependency: impl Display),
msg: format!("Dependency {dependency} is missing a network specification"),
help: Some("Add a network specification to the dependency in the `program.json` file. Example: `network: \"testnet3\"`".to_string()),
}
@formatted
missing_path_error {
args: (dependency: impl Display),
msg: format!("Local dependency {dependency} is missing a path specification"),
help: Some("Add a path in the `program.json` file to the dependency project root . Example: `path: \"../../board\"`".to_string()),
}
@formatted
program_name_mismatch_error {
args: (program_json_name: impl Display, dep_name: impl Display, path: impl Display),
msg: format!("Name mismatch: Local program at path `{path}` is named `{program_json_name}` in `program.json` but `{dep_name}` in the program that imports it"),
help: Some("Change one of the names to match the other".to_string()),
}
@formatted
snarkvm_error_building_program_id {
args: (),
msg: format!("Snarkvm error building program id"),
help: None,
}
@formatted
failed_to_retrieve_from_endpoint {
args: (endpoint: impl Display, error: impl ErrorArg),
msg: format!("Failed to retrieve from endpoint `{endpoint}`. Error: {error}"),
help: None,
}
);

Some files were not shown because too many files have changed in this diff Show More