diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000..d758e2dadd --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,163 @@ +version: 2.1 +commands: + setup_environment: + description: "Setup environment" + parameters: + cache_key: + type: string + default: leo-stable-cache + steps: + - run: set -e + - setup_remote_docker + - run: + name: Prepare environment and install dependencies + command: | + export SCCACHE_CACHE_SIZE=200M + export WORK_DIR="$CIRCLE_WORKING_DIRECTORY/.cache/sccache" + export SCCACHE_DIR="$CIRCLE_WORKING_DIRECTORY/.cache/sccache" + mkdir -p "$CIRCLE_WORKING_DIRECTORY/.bin" + wget https://github.com/mozilla/sccache/releases/download/0.2.13/sccache-0.2.13-x86_64-unknown-linux-musl.tar.gz + tar -C "$CIRCLE_WORKING_DIRECTORY/.bin" -xvf sccache-0.2.13-x86_64-unknown-linux-musl.tar.gz + mv $CIRCLE_WORKING_DIRECTORY/.bin/sccache-0.2.13-x86_64-unknown-linux-musl/sccache $CIRCLE_WORKING_DIRECTORY/.bin/sccache + export PATH="$PATH:$CIRCLE_WORKING_DIRECTORY/.bin" + export RUSTC_WRAPPER="sccache" + rm -rf "$CIRCLE_WORKING_DIRECTORY/.cargo/registry" + sudo apt-get update && sudo apt-get install -y clang llvm-dev llvm pkg-config xz-utils make libssl-dev libssl-dev + - restore_cache: + keys: + - << parameters.cache_key >> + clear_environment: + description: "Clear environment" + parameters: + cache_key: + type: string + default: leo-stable-cache + steps: + - run: (sccache -s||true) + - run: set +e + - save_cache: + key: << parameters.cache_key >> + paths: + - .cache/sccache + - .cargo +jobs: + + rust-stable: + docker: + - image: cimg/rust:1.50.0 + resource_class: xlarge + steps: + - checkout + - setup_environment: + cache_key: leo-stable-cache + - run: + name: Build and run tests + no_output_timeout: 30m + command: cargo install --path . --root . + - persist_to_workspace: + root: ~/ + paths: project/ + - clear_environment: + cache_key: leo-stable-cache + + leo-new: + docker: + - image: cimg/rust:1.50.0 + resource_class: xlarge + steps: + - attach_workspace: + at: /home/circleci/project/ + - run: + name: leo new + command: | + export LEO=/home/circleci/project/project/bin/leo + ./project/.circleci/leo-new.sh + + leo-init: + docker: + - image: cimg/rust:1.50.0 + resource_class: xlarge + steps: + - attach_workspace: + at: /home/circleci/project/ + - run: + name: leo init + command: | + export LEO=/home/circleci/project/project/bin/leo + ./project/.circleci/leo-init.sh + + leo-clean: + docker: + - image: cimg/rust:1.50.0 + resource_class: xlarge + steps: + - attach_workspace: + at: /home/circleci/project/ + - run: + name: leo clean + command: | + export LEO=/home/circleci/project/project/bin/leo + ./project/.circleci/leo-clean.sh + + leo-setup: + docker: + - image: cimg/rust:1.50.0 + resource_class: xlarge + steps: + - attach_workspace: + at: /home/circleci/project/ + - run: + name: leo setup + command: | + export LEO=/home/circleci/project/project/bin/leo + ./project/.circleci/leo-setup.sh + + leo-add-remove: + docker: + - image: cimg/rust:1.50.0 + resource_class: xlarge + steps: + - attach_workspace: + at: /home/circleci/project/ + - run: + name: leo add & remove + command: | + export LEO=/home/circleci/project/project/bin/leo + ./project/.circleci/leo-add-remove.sh + + leo-login-logout: + docker: + - image: cimg/rust:1.50.0 + resource_class: xlarge + steps: + - attach_workspace: + at: /home/circleci/project/ + - run: + name: leo login & logout + command: | + export LEO=/home/circleci/project/project/bin/leo + ./project/.circleci/leo-login-logout.sh + +workflows: + version: 2 + main-workflow: + jobs: + - rust-stable + - leo-new: + requires: + - rust-stable + - leo-init: + requires: + - rust-stable + - leo-clean: + requires: + - rust-stable + - leo-setup: + requires: + - rust-stable + - leo-add-remove: + requires: + - rust-stable + - leo-login-logout: + requires: + - rust-stable \ No newline at end of file diff --git a/.circleci/leo-add-remove.sh b/.circleci/leo-add-remove.sh new file mode 100755 index 0000000000..e7eb352934 --- /dev/null +++ b/.circleci/leo-add-remove.sh @@ -0,0 +1,6 @@ +# leo add (w/o login) & remove + +$LEO new my-app && cd my-app +$LEO add howard/silly-sudoku +$LEO remove silly-sudoku +$LEO clean diff --git a/.circleci/leo-clean.sh b/.circleci/leo-clean.sh new file mode 100755 index 0000000000..46bd90880c --- /dev/null +++ b/.circleci/leo-clean.sh @@ -0,0 +1,34 @@ +# leo new hello-world + +$LEO new hello-world +ls -la +cd hello-world && ls -la +$LEO run + +# Assert that the 'outputs' folder is not empty + +cd outputs || exit 1 +if [ "$(ls -A $DIR)" ]; then + echo "$DIR is not empty" +else + echo "$DIR is empty" + exit 1 +fi +cd .. + +# leo clean + +$LEO clean +cd outputs && ls -la +cd .. + +# Assert that the 'outputs' folder is empty + +cd outputs || exit 1 +if [ "$(ls -A $DIR)" ]; then + echo "$DIR is not empty" + exit 1 +else + echo "$DIR is empty" + exit 0 +fi diff --git a/.circleci/leo-init.sh b/.circleci/leo-init.sh new file mode 100755 index 0000000000..ab7724cd4a --- /dev/null +++ b/.circleci/leo-init.sh @@ -0,0 +1,4 @@ +mkdir hello-world && cd hello-world || exit 1 +$LEO init +ls -la +$LEO run diff --git a/.circleci/leo-login-logout.sh b/.circleci/leo-login-logout.sh new file mode 100755 index 0000000000..1353b18d72 --- /dev/null +++ b/.circleci/leo-login-logout.sh @@ -0,0 +1,7 @@ +# leo login & logout + +$LEO new my-app && cd my-app || exit 1 +$LEO login -u "$ALEO_PM_USERNAME" -p "$ALEO_PM_PASSWORD" +$LEO add howard/silly-sudoku +$LEO remove silly-sudoku +$LEO logout diff --git a/.circleci/leo-new.sh b/.circleci/leo-new.sh new file mode 100755 index 0000000000..cb24618546 --- /dev/null +++ b/.circleci/leo-new.sh @@ -0,0 +1,4 @@ +$LEO new hello-world +ls -la +cd hello-world && ls -la +$LEO run diff --git a/.circleci/leo-setup.sh b/.circleci/leo-setup.sh new file mode 100755 index 0000000000..ae6e23803f --- /dev/null +++ b/.circleci/leo-setup.sh @@ -0,0 +1,7 @@ +# leo setup + +cd ./project/examples/pedersen-hash || exit 1 +$LEO setup +$LEO setup +$LEO setup --skip-key-check +$LEO clean diff --git a/.github/workflows/leo-add-remove.yml b/.github/workflows/leo-add-remove.yml deleted file mode 100644 index 8a0912ba96..0000000000 --- a/.github/workflows/leo-add-remove.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: leo-add-remove -on: - pull_request: - push: - branches: - - master - paths-ignore: - - 'docs/**' - - 'documentation/**' -env: - RUST_BACKTRACE: 1 - -jobs: - add: - name: Add Package ('leo add') - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v1 - - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: nightly - override: true - components: rustfmt - - - name: Install Leo - uses: actions-rs/cargo@v1 - env: - CARGO_NET_GIT_FETCH_WITH_CLI: true - with: - command: install - args: --path . - - - name: 'leo add (w/o login) & remove' - run: | - cd .. && leo new my-app && cd my-app - leo add argus4130/xnor - leo remove xnor - leo clean - diff --git a/.github/workflows/leo-clean.yml b/.github/workflows/leo-clean.yml deleted file mode 100644 index 177ed73fdc..0000000000 --- a/.github/workflows/leo-clean.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: leo-clean -on: - pull_request: - push: - branches: - - master - paths-ignore: - - 'docs/**' - - 'documentation/**' -env: - RUST_BACKTRACE: 1 - -jobs: - new: - name: Hello Leo ('leo new hello-world') - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v1 - - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: nightly - override: true - components: rustfmt - - - uses: actions/cache@v2 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - - name: Install Leo - uses: actions-rs/cargo@v1 - with: - command: install - args: --path . - - - name: 'leo new hello-world' - run: | - cd .. - leo new hello-world - ls -la - cd hello-world && ls -la - leo run - - - name: Assert that the 'outputs' folder is not empty - run: | - cd ../hello-world/outputs - if [ "$(ls -A $DIR)" ]; then - echo "$DIR is not empty" - else - echo "$DIR is empty" - exit 1 - fi - - - name: 'leo clean' - run: | - cd ../hello-world - leo clean - cd outputs && ls -la - - - name: Assert that the 'outputs' folder is empty - run: | - cd ../hello-world/outputs - if [ "$(ls -A $DIR)" ]; then - echo "$DIR is not empty" - exit 1 - else - echo "$DIR is empty" - exit 0 - fi diff --git a/.github/workflows/leo-init.yml b/.github/workflows/leo-init.yml deleted file mode 100644 index 1821d1de86..0000000000 --- a/.github/workflows/leo-init.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: leo-init -on: - pull_request: - push: - branches: - - master - paths-ignore: - - 'docs/**' - - 'documentation/**' -env: - RUST_BACKTRACE: 1 - -jobs: - init: - name: Hello Leo ('leo init') - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v1 - - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: nightly - override: true - components: rustfmt - - - uses: actions/cache@v2 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - - name: Install Leo - uses: actions-rs/cargo@v1 - with: - command: install - args: --path . - - - name: 'leo init' - run: | - cd .. && mkdir hello-world && cd hello-world - leo init - ls -la - leo run diff --git a/.github/workflows/leo-login-logout.yml b/.github/workflows/leo-login-logout.yml deleted file mode 100644 index 95a17e42ed..0000000000 --- a/.github/workflows/leo-login-logout.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: leo-login-logout -on: - pull_request: - push: - branches: - - master - paths-ignore: - - 'docs/**' - - 'documentation/**' -env: - RUST_BACKTRACE: 1 - -jobs: - add: - name: Add Package ('leo add') - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v1 - - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: nightly - override: true - components: rustfmt - - - name: Install Leo - uses: actions-rs/cargo@v1 - env: - CARGO_NET_GIT_FETCH_WITH_CLI: true - with: - command: install - args: --path . - - - name: 'leo login & logout' - env: - USER: ${{ secrets.ALEO_PM_USERNAME }} - PASS: ${{ secrets.ALEO_PM_PASSWORD }} - run: | - cd .. && leo new my-app && cd my-app - leo login -u "$USER" -p "$PASS" - leo add argus4130/xnor - leo remove xnor - leo logout - diff --git a/.github/workflows/leo-new.yml b/.github/workflows/leo-new.yml deleted file mode 100644 index 842d553897..0000000000 --- a/.github/workflows/leo-new.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: leo-new -on: - pull_request: - push: - branches: - - master - paths-ignore: - - 'docs/**' - - 'documentation/**' -env: - RUST_BACKTRACE: 1 - -jobs: - new: - name: Hello Leo ('leo new hello-world') - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v1 - - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: nightly - override: true - components: rustfmt - - - uses: actions/cache@v2 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - - name: Install Leo - uses: actions-rs/cargo@v1 - with: - command: install - args: --path . - - - name: 'leo new hello-world' - run: | - cd .. - leo new hello-world - ls -la - cd hello-world && ls -la - leo run diff --git a/.github/workflows/leo-setup.yml b/.github/workflows/leo-setup.yml deleted file mode 100644 index 7bad2924c2..0000000000 --- a/.github/workflows/leo-setup.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: leo-setup -on: - pull_request: - push: - branches: - - master - paths-ignore: - - 'docs/**' - - 'documentation/**' -env: - RUST_BACKTRACE: 1 - -jobs: - add: - name: Add Package ('leo add') - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v1 - - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: nightly - override: true - components: rustfmt - - - name: Install Leo - uses: actions-rs/cargo@v1 - env: - CARGO_NET_GIT_FETCH_WITH_CLI: true - with: - command: install - args: --path . - - - name: 'leo setup for examples' - env: - USER: ${{ secrets.ALEO_PM_USERNAME }} - PASS: ${{ secrets.ALEO_PM_PASSWORD }} - run: | - cd examples/pedersen-hash - leo setup - leo setup - leo setup --skip-key-check - leo clean - diff --git a/.gitignore b/.gitignore index 169d72f25d..f9e224d6a5 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,9 @@ /tmp/ **.idea/ *.DS_Store + +**/process.yml + +**/.crates.toml +**/.crates2.json +**/bin/ diff --git a/.resources/leo.png b/.resources/banner.png similarity index 100% rename from .resources/leo.png rename to .resources/banner.png diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md new file mode 100644 index 0000000000..357d549c32 --- /dev/null +++ b/DEVELOPMENT.md @@ -0,0 +1,31 @@ +# Development Guide + +## Running CircleCI locally + +### Step 1: Install CircleCI + +If you wish to run CircleCI locally, start by installing it: + +- macOS +``` +brew install circleci +``` + +- Linux (via Snap) +``` +sudo snap install docker circleci +sudo snap connect circleci:docker docker +``` + +- Windows (via Chocolatey) +``` +choco install circleci-cli -y +``` + +### Step 2: Run CircleCI + +To run a job, export the config to `process.yml`, and specify it when executing: +```shell +circleci config process .circleci/config.yml > process.yml +circleci local execute -c process.yml --job JOB_NAME +``` diff --git a/README.md b/README.md index ca19dd7fe9..e09580624c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- +

The Leo Programming Language

@@ -27,6 +27,7 @@ Leo is a functional, statically-typed programming language built for writing pri ## 1. Overview + Welcome to the Leo programming language. Leo provides a high-level language that abstracts low-level cryptographic concepts and makes it easy to diff --git a/asg/tests/mod.rs b/asg/tests/mod.rs index 1eb54e4854..8135675649 100644 --- a/asg/tests/mod.rs +++ b/asg/tests/mod.rs @@ -40,7 +40,7 @@ fn load_asg_imports<'a, T: ImportResolver<'a>>( InternalProgram::new(context, &ast.as_repr(), imports) } -fn mocked_resolver<'a>(_ctx: AsgContext<'a>) -> MockedImportResolver<'a> { +fn mocked_resolver<'a>(_context: AsgContext<'a>) -> MockedImportResolver<'a> { let packages = indexmap::IndexMap::new(); MockedImportResolver { packages } } diff --git a/asg/tests/pass/form_ast.rs b/asg/tests/pass/form_ast.rs index 5248b0ecf9..15ba86942b 100644 --- a/asg/tests/pass/form_ast.rs +++ b/asg/tests/pass/form_ast.rs @@ -25,8 +25,8 @@ use std::path::Path; #[test] fn test_basic() { let program_string = include_str!("./circuits/pedersen_mock.leo"); - let ctx = new_context(); - let asg = load_asg(&ctx, program_string).unwrap(); + let context = new_context(); + let asg = load_asg(&context, program_string).unwrap(); let reformed_ast = leo_asg::reform_ast(&asg); println!("{}", reformed_ast); // panic!(); @@ -51,8 +51,8 @@ fn test_function_rename() { console.assert(total == 20); } "#; - let ctx = new_context(); - let asg = load_asg(&ctx, program_string).unwrap(); + let context = new_context(); + let asg = load_asg(&context, program_string).unwrap(); let reformed_ast = leo_asg::reform_ast(&asg); println!("{}", reformed_ast); // panic!(); @@ -60,8 +60,8 @@ fn test_function_rename() { #[test] fn test_imports() { - let ctx = new_context(); - let mut imports = crate::mocked_resolver(&ctx); + let context = new_context(); + let mut imports = crate::mocked_resolver(&context); let test_import = r#" circuit Point { x: u32 @@ -74,7 +74,7 @@ fn test_imports() { "#; imports .packages - .insert("test-import".to_string(), load_asg(&ctx, test_import).unwrap()); + .insert("test-import".to_string(), load_asg(&context, test_import).unwrap()); let program_string = r#" import test-import.foo; @@ -95,7 +95,7 @@ fn test_imports() { serde_json::to_string(Ast::new("test", &test_grammar).unwrap().as_repr()).unwrap() ); - let asg = crate::load_asg_imports(&ctx, program_string, &mut imports).unwrap(); + let asg = crate::load_asg_imports(&context, program_string, &mut imports).unwrap(); let reformed_ast = leo_asg::reform_ast(&asg); println!("{}", serde_json::to_string(&reformed_ast).unwrap()); // panic!(); diff --git a/asg/tests/pass/import/mod.rs b/asg/tests/pass/import/mod.rs index b7ecea72c2..176f806658 100644 --- a/asg/tests/pass/import/mod.rs +++ b/asg/tests/pass/import/mod.rs @@ -20,138 +20,139 @@ use crate::{load_asg, load_asg_imports, mocked_resolver}; #[test] fn test_basic() { - let ctx = new_context(); - let mut imports = mocked_resolver(&ctx); + let context = new_context(); + let mut imports = mocked_resolver(&context); imports.packages.insert( "test-import".to_string(), - load_asg(&ctx, include_str!("src/test-import.leo")).unwrap(), + load_asg(&context, include_str!("src/test-import.leo")).unwrap(), ); let program_string = include_str!("basic.leo"); - load_asg_imports(&ctx, program_string, &mut imports).unwrap(); + load_asg_imports(&context, program_string, &mut imports).unwrap(); } #[test] fn test_multiple() { - let ctx = new_context(); - let mut imports = mocked_resolver(&ctx); + let context = new_context(); + let mut imports = mocked_resolver(&context); imports.packages.insert( "test-import".to_string(), - load_asg(&ctx, include_str!("src/test-import.leo")).unwrap(), + load_asg(&context, include_str!("src/test-import.leo")).unwrap(), ); let program_string = include_str!("multiple.leo"); - load_asg_imports(&ctx, program_string, &mut imports).unwrap(); + load_asg_imports(&context, program_string, &mut imports).unwrap(); } #[test] fn test_star() { - let ctx = new_context(); - let mut imports = mocked_resolver(&ctx); + let context = new_context(); + let mut imports = mocked_resolver(&context); imports.packages.insert( "test-import".to_string(), - load_asg(&ctx, include_str!("src/test-import.leo")).unwrap(), + load_asg(&context, include_str!("src/test-import.leo")).unwrap(), ); let program_string = include_str!("star.leo"); - load_asg_imports(&ctx, program_string, &mut imports).unwrap(); + load_asg_imports(&context, program_string, &mut imports).unwrap(); } #[test] fn test_alias() { - let ctx = new_context(); - let mut imports = mocked_resolver(&ctx); + let context = new_context(); + let mut imports = mocked_resolver(&context); imports.packages.insert( "test-import".to_string(), - load_asg(&ctx, include_str!("src/test-import.leo")).unwrap(), + load_asg(&context, include_str!("src/test-import.leo")).unwrap(), ); let program_string = include_str!("alias.leo"); - load_asg_imports(&ctx, program_string, &mut imports).unwrap(); + load_asg_imports(&context, program_string, &mut imports).unwrap(); } // naming tests #[test] fn test_name() { - let ctx = new_context(); - let mut imports = mocked_resolver(&ctx); + let context = new_context(); + let mut imports = mocked_resolver(&context); imports.packages.insert( "hello-world".to_string(), - load_asg(&ctx, include_str!("src/hello-world.leo")).unwrap(), + load_asg(&context, include_str!("src/hello-world.leo")).unwrap(), ); imports.packages.insert( "a0-f".to_string(), - load_asg(&ctx, include_str!("src/a0-f.leo")).unwrap(), + load_asg(&context, include_str!("src/a0-f.leo")).unwrap(), + ); + imports.packages.insert( + "a-9".to_string(), + load_asg(&context, include_str!("src/a-9.leo")).unwrap(), ); - imports - .packages - .insert("a-9".to_string(), load_asg(&ctx, include_str!("src/a-9.leo")).unwrap()); let program_string = include_str!("names.leo"); - load_asg_imports(&ctx, program_string, &mut imports).unwrap(); + load_asg_imports(&context, program_string, &mut imports).unwrap(); } // more complex tests #[test] fn test_many_import() { - let ctx = new_context(); - let mut imports = mocked_resolver(&ctx); + let context = new_context(); + let mut imports = mocked_resolver(&context); imports.packages.insert( "test-import".to_string(), - load_asg(&ctx, include_str!("src/test-import.leo")).unwrap(), + load_asg(&context, include_str!("src/test-import.leo")).unwrap(), ); imports.packages.insert( "bar".to_string(), - load_asg(&ctx, include_str!("imports/bar/src/lib.leo")).unwrap(), + load_asg(&context, include_str!("imports/bar/src/lib.leo")).unwrap(), ); imports.packages.insert( "bar.baz".to_string(), - load_asg(&ctx, include_str!("imports/bar/src/baz.leo")).unwrap(), + load_asg(&context, include_str!("imports/bar/src/baz.leo")).unwrap(), ); imports.packages.insert( "bar.baz".to_string(), - load_asg(&ctx, include_str!("imports/bar/src/baz.leo")).unwrap(), + load_asg(&context, include_str!("imports/bar/src/baz.leo")).unwrap(), ); imports.packages.insert( "bar.bat.bat".to_string(), - load_asg(&ctx, include_str!("imports/bar/src/bat/bat.leo")).unwrap(), + load_asg(&context, include_str!("imports/bar/src/bat/bat.leo")).unwrap(), ); imports.packages.insert( "car".to_string(), - load_asg(&ctx, include_str!("imports/car/src/lib.leo")).unwrap(), + load_asg(&context, include_str!("imports/car/src/lib.leo")).unwrap(), ); let program_string = include_str!("many_import.leo"); - load_asg_imports(&ctx, program_string, &mut imports).unwrap(); + load_asg_imports(&context, program_string, &mut imports).unwrap(); } #[test] fn test_many_import_star() { - let ctx = new_context(); - let mut imports = mocked_resolver(&ctx); + let context = new_context(); + let mut imports = mocked_resolver(&context); imports.packages.insert( "test-import".to_string(), - load_asg(&ctx, include_str!("src/test-import.leo")).unwrap(), + load_asg(&context, include_str!("src/test-import.leo")).unwrap(), ); imports.packages.insert( "bar".to_string(), - load_asg(&ctx, include_str!("imports/bar/src/lib.leo")).unwrap(), + load_asg(&context, include_str!("imports/bar/src/lib.leo")).unwrap(), ); imports.packages.insert( "bar.baz".to_string(), - load_asg(&ctx, include_str!("imports/bar/src/baz.leo")).unwrap(), + load_asg(&context, include_str!("imports/bar/src/baz.leo")).unwrap(), ); imports.packages.insert( "bar.baz".to_string(), - load_asg(&ctx, include_str!("imports/bar/src/baz.leo")).unwrap(), + load_asg(&context, include_str!("imports/bar/src/baz.leo")).unwrap(), ); imports.packages.insert( "bar.bat.bat".to_string(), - load_asg(&ctx, include_str!("imports/bar/src/bat/bat.leo")).unwrap(), + load_asg(&context, include_str!("imports/bar/src/bat/bat.leo")).unwrap(), ); imports.packages.insert( "car".to_string(), - load_asg(&ctx, include_str!("imports/car/src/lib.leo")).unwrap(), + load_asg(&context, include_str!("imports/car/src/lib.leo")).unwrap(), ); let program_string = include_str!("many_import_star.leo"); - load_asg_imports(&ctx, program_string, &mut imports).unwrap(); + load_asg_imports(&context, program_string, &mut imports).unwrap(); } diff --git a/compiler/src/compiler.rs b/compiler/src/compiler.rs index 0a92f251de..bda752ed86 100644 --- a/compiler/src/compiler.rs +++ b/compiler/src/compiler.rs @@ -63,7 +63,7 @@ pub struct Compiler<'a, F: PrimeField, G: GroupType> { output_directory: PathBuf, program: Program, program_input: Input, - ctx: AsgContext<'a>, + context: AsgContext<'a>, asg: Option>, _engine: PhantomData, _group: PhantomData, @@ -73,7 +73,12 @@ impl<'a, F: PrimeField, G: GroupType> Compiler<'a, F, G> { /// /// Returns a new Leo program compiler. /// - pub fn new(package_name: String, main_file_path: PathBuf, output_directory: PathBuf, ctx: AsgContext<'a>) -> Self { + pub fn new( + package_name: String, + main_file_path: PathBuf, + output_directory: PathBuf, + context: AsgContext<'a>, + ) -> Self { Self { program_name: package_name.clone(), main_file_path, @@ -81,7 +86,7 @@ impl<'a, F: PrimeField, G: GroupType> Compiler<'a, F, G> { program: Program::new(package_name), program_input: Input::new(), asg: None, - ctx, + context, _engine: PhantomData, _group: PhantomData, } @@ -98,9 +103,9 @@ impl<'a, F: PrimeField, G: GroupType> Compiler<'a, F, G> { package_name: String, main_file_path: PathBuf, output_directory: PathBuf, - ctx: AsgContext<'a>, + context: AsgContext<'a>, ) -> Result { - let mut compiler = Self::new(package_name, main_file_path, output_directory, ctx); + let mut compiler = Self::new(package_name, main_file_path, output_directory, context); compiler.parse_program()?; @@ -124,9 +129,9 @@ impl<'a, F: PrimeField, G: GroupType> Compiler<'a, F, G> { input_path: &Path, state_string: &str, state_path: &Path, - ctx: AsgContext<'a>, + context: AsgContext<'a>, ) -> Result { - let mut compiler = Self::new(package_name, main_file_path, output_directory, ctx); + let mut compiler = Self::new(package_name, main_file_path, output_directory, context); compiler.parse_input(input_string, input_path, state_string, state_path)?; @@ -205,7 +210,7 @@ impl<'a, F: PrimeField, G: GroupType> Compiler<'a, F, G> { tracing::debug!("Program parsing complete\n{:#?}", self.program); // Create a new symbol table from the program, imported_programs, and program_input. - let asg = Asg::new(self.ctx, &core_ast, &mut leo_imports::ImportParser::default())?; + let asg = Asg::new(self.context, &core_ast, &mut leo_imports::ImportParser::default())?; tracing::debug!("ASG generation complete"); diff --git a/imports/src/parser/import_parser.rs b/imports/src/parser/import_parser.rs index b8376265bb..300408cffb 100644 --- a/imports/src/parser/import_parser.rs +++ b/imports/src/parser/import_parser.rs @@ -34,7 +34,7 @@ pub struct ImportParser<'a> { impl<'a> ImportResolver<'a> for ImportParser<'a> { fn resolve_package( &mut self, - ctx: AsgContext<'a>, + context: AsgContext<'a>, package_segments: &[&str], span: &Span, ) -> Result>, AsgConvertError> { @@ -51,7 +51,7 @@ impl<'a> ImportResolver<'a> for ImportParser<'a> { self.partial_imports.insert(full_path.clone()); let program = imports - .parse_package(ctx, path, package_segments, span) + .parse_package(context, path, package_segments, span) .map_err(|x| -> AsgConvertError { x.into() })?; self.partial_imports.remove(&full_path); self.imports.insert(full_path, program.clone()); diff --git a/imports/src/parser/parse_package.rs b/imports/src/parser/parse_package.rs index 38e7025910..3acd05e41b 100644 --- a/imports/src/parser/parse_package.rs +++ b/imports/src/parser/parse_package.rs @@ -26,16 +26,16 @@ static IMPORTS_DIRECTORY_NAME: &str = "imports/"; impl<'a> ImportParser<'a> { fn parse_package_access( &mut self, - ctx: AsgContext<'a>, + context: AsgContext<'a>, package: &DirEntry, remaining_segments: &[&str], span: &Span, ) -> Result, ImportParserError> { if !remaining_segments.is_empty() { - return self.parse_package(ctx, package.path(), remaining_segments, span); + return self.parse_package(context, package.path(), remaining_segments, span); } let program = Self::parse_import_file(package, span)?; - let asg = leo_asg::InternalProgram::new(ctx, &program, self)?; + let asg = leo_asg::InternalProgram::new(context, &program, self)?; Ok(asg) } @@ -47,7 +47,7 @@ impl<'a> ImportParser<'a> { /// pub(crate) fn parse_package( &mut self, - ctx: AsgContext<'a>, + context: AsgContext<'a>, mut path: PathBuf, segments: &[&str], span: &Span, @@ -113,8 +113,8 @@ impl<'a> ImportParser<'a> { package_name, span, ))), - (Some(source_entry), None) => self.parse_package_access(ctx, &source_entry, &segments[1..], span), - (None, Some(import_entry)) => self.parse_package_access(ctx, &import_entry, &segments[1..], span), + (Some(source_entry), None) => self.parse_package_access(context, &source_entry, &segments[1..], span), + (None, Some(import_entry)) => self.parse_package_access(context, &import_entry, &segments[1..], span), (None, None) => Err(ImportParserError::unknown_package(Identifier::new_with_span( package_name, span, @@ -123,7 +123,7 @@ impl<'a> ImportParser<'a> { } else { // Enforce local package access with no found imports directory match matched_source_entry { - Some(source_entry) => self.parse_package_access(ctx, &source_entry, &segments[1..], span), + Some(source_entry) => self.parse_package_access(context, &source_entry, &segments[1..], span), None => Err(ImportParserError::unknown_package(Identifier::new_with_span( package_name, span, diff --git a/leo/commands/build.rs b/leo/commands/build.rs index be11d31d4b..9f562dd85a 100644 --- a/leo/commands/build.rs +++ b/leo/commands/build.rs @@ -36,16 +36,10 @@ use structopt::StructOpt; use tracing::span::Span; /// Compile and build program command -#[derive(StructOpt, Debug, Default)] +#[derive(StructOpt, Debug)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] pub struct Build {} -impl Build { - pub fn new() -> Build { - Build {} - } -} - impl Command for Build { type Input = (); type Output = Option<(Compiler<'static, Fq, EdwardsGroupType>, bool)>; @@ -58,9 +52,9 @@ impl Command for Build { Ok(()) } - fn apply(self, ctx: Context, _: Self::Input) -> Result { - let path = ctx.dir()?; - let package_name = ctx.manifest()?.get_package_name(); + fn apply(self, context: Context, _: Self::Input) -> Result { + let path = context.dir()?; + let package_name = context.manifest()?.get_package_name(); // Sanitize the package path to the root directory let mut package_path = path.clone(); @@ -140,8 +134,8 @@ impl Command for Build { let temporary_program = program.clone(); let output = temporary_program.compile_constraints(&mut cs)?; - tracing::debug!("Compiled constraints - {:#?}", output); - tracing::debug!("Number of constraints - {:#?}", cs.num_constraints()); + tracing::debug!("Compiled output - {:#?}", output); + tracing::info!("Number of constraints - {:#?}", cs.num_constraints()); // Serialize the circuit let circuit_object = SerializedCircuit::from(cs); diff --git a/leo/commands/clean.rs b/leo/commands/clean.rs index 91240f1be7..0b39661f07 100644 --- a/leo/commands/clean.rs +++ b/leo/commands/clean.rs @@ -23,16 +23,10 @@ use structopt::StructOpt; use tracing::span::Span; /// Clean outputs folder command -#[derive(StructOpt, Debug, Default)] +#[derive(StructOpt, Debug)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] pub struct Clean {} -impl Clean { - pub fn new() -> Clean { - Clean {} - } -} - impl Command for Clean { type Input = (); type Output = (); @@ -45,9 +39,9 @@ impl Command for Clean { Ok(()) } - fn apply(self, ctx: Context, _: Self::Input) -> Result { - let path = ctx.dir()?; - let package_name = ctx.manifest()?.get_package_name(); + fn apply(self, context: Context, _: Self::Input) -> Result { + let path = context.dir()?; + let package_name = context.manifest()?.get_package_name(); // Remove the checksum from the output directory ChecksumFile::new(&package_name).remove(&path)?; diff --git a/leo/commands/deploy.rs b/leo/commands/deploy.rs index f27da1dae5..06eff0c2e6 100644 --- a/leo/commands/deploy.rs +++ b/leo/commands/deploy.rs @@ -21,16 +21,10 @@ use structopt::StructOpt; use tracing::span::Span; /// Deploy Leo program to the network -#[derive(StructOpt, Debug, Default)] +#[derive(StructOpt, Debug)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] pub struct Deploy {} -impl Deploy { - pub fn new() -> Deploy { - Deploy {} - } -} - impl Command for Deploy { type Input = (); type Output = (); diff --git a/leo/commands/init.rs b/leo/commands/init.rs index 7ebe2a3214..7c6b3e7745 100644 --- a/leo/commands/init.rs +++ b/leo/commands/init.rs @@ -23,16 +23,10 @@ use structopt::StructOpt; use tracing::span::Span; /// Init Leo project command within current directory -#[derive(StructOpt, Debug, Default)] +#[derive(StructOpt, Debug)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] pub struct Init {} -impl Init { - pub fn new() -> Init { - Init {} - } -} - impl Command for Init { type Input = (); type Output = (); @@ -46,13 +40,20 @@ impl Command for Init { } fn apply(self, _: Context, _: Self::Input) -> Result { + // Derive the package directory path. let path = current_dir()?; + + // Check that the given package name is valid. let package_name = path .file_stem() .ok_or_else(|| anyhow!("Project name invalid"))? .to_string_lossy() .to_string(); + if !LeoPackage::is_package_name_valid(&package_name) { + return Err(anyhow!("Invalid Leo project name")); + } + // Check that the current package directory path exists. if !path.exists() { return Err(anyhow!("Directory does not exist")); } diff --git a/leo/commands/lint.rs b/leo/commands/lint.rs index 0aeda51844..3e60d7ec6e 100644 --- a/leo/commands/lint.rs +++ b/leo/commands/lint.rs @@ -21,16 +21,10 @@ use structopt::StructOpt; use tracing::span::Span; /// Lint Leo code command -#[derive(StructOpt, Debug, Default)] +#[derive(StructOpt, Debug)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] pub struct Lint {} -impl Lint { - pub fn new() -> Lint { - Lint {} - } -} - impl Command for Lint { type Input = (); type Output = (); diff --git a/leo/commands/mod.rs b/leo/commands/mod.rs index 8a6ca36171..47d5994575 100644 --- a/leo/commands/mod.rs +++ b/leo/commands/mod.rs @@ -52,7 +52,7 @@ pub mod test; pub use test::Test; pub mod update; -pub use update::{Sub as UpdateAutomatic, Update}; +pub use update::{Automatic as UpdateAutomatic, Update}; pub mod watch; pub use watch::Watch; @@ -60,57 +60,55 @@ pub use watch::Watch; // Aleo PM related commands pub mod package; -/// Base trait for Leo CLI, see methods and their documentation for details +/// Base trait for the Leo CLI, see methods and their documentation for details. pub trait Command { - /// If current command requires running another command before - /// and needs its output results, this is the place to set. + /// If the current command requires running another command beforehand + /// and needs its output result, this is where the result type is defined. /// Example: type Input: ::Out type Input; - /// Define output of the command to be reused as an Input for another - /// command. If this command is not used as a prelude for another, keep empty + /// Defines the output of this command, which may be used as `Input` for another + /// command. If this command is not used as a prelude for another command, + /// this field may be left empty. type Output; - /// Returns project context, currently keeping it simple but it is possible - /// that in the future leo will not depend on current directory, and we're keeping - /// option for extending current core + /// Returns the project context, which is defined as the current directory. fn context(&self) -> Result { get_context() } - /// Add span to the logger tracing::span. - /// Due to specifics of macro implementation it is impossible to set - /// span name with non-literal i.e. dynamic variable even if this - /// variable is &'static str + /// Adds a span to the logger via `tracing::span`. + /// Because of the specifics of the macro implementation, it is not possible + /// to set the span name with a non-literal i.e. a dynamic variable even if this + /// variable is a &'static str. fn log_span(&self) -> Span { tracing::span!(tracing::Level::INFO, "Leo") } - /// Run prelude and get Input for current command. As simple as that. - /// But due to inability to pass default implementation of a type, this - /// method must be present in every trait implementation. + /// Runs the prelude and returns the Input of the current command. fn prelude(&self) -> Result where Self: std::marker::Sized; - /// Core of the execution - do what is necessary. This function is run within - /// context of 'execute' function, which sets logging and timers - fn apply(self, ctx: Context, input: Self::Input) -> Result + /// Runs the main operation of this command. This function is run within + /// context of 'execute' function, which sets logging and timers. + fn apply(self, context: Context, input: Self::Input) -> Result where Self: std::marker::Sized; - /// Wrapper around apply function, sets up tracing, time tracking and context + /// A wrapper around the `apply` method. + /// This function sets up tracing, timing, and the context. fn execute(self) -> Result where Self: std::marker::Sized, { let input = self.prelude()?; - // create span for this command + // Create the span for this command. let span = self.log_span(); let span = span.enter(); - // calculate execution time for each run + // Calculate the execution time for this command. let timer = Instant::now(); let context = self.context()?; @@ -118,7 +116,7 @@ pub trait Command { drop(span); - // use done context to print time + // Use the done context to print the execution time for this command. tracing::span!(tracing::Level::INFO, "Done").in_scope(|| { tracing::info!("Finished in {} milliseconds \n", timer.elapsed().as_millis()); }); @@ -126,7 +124,7 @@ pub trait Command { out } - /// Execute command but empty the result. Comes in handy where there's a + /// Executes command but empty the result. Comes in handy where there's a /// need to make match arms compatible while keeping implementation-specific /// output possible. Errors however are all of the type Error fn try_execute(self) -> Result<()> diff --git a/leo/commands/new.rs b/leo/commands/new.rs index 2509e7563e..a4da67c309 100644 --- a/leo/commands/new.rs +++ b/leo/commands/new.rs @@ -30,12 +30,6 @@ pub struct New { name: String, } -impl New { - pub fn new(name: String) -> New { - New { name } - } -} - impl Command for New { type Input = (); type Output = (); @@ -49,13 +43,17 @@ impl Command for New { } fn apply(self, _: Context, _: Self::Input) -> Result { - let mut path = current_dir()?; + // Check that the given package name is valid. let package_name = self.name; + if !LeoPackage::is_package_name_valid(&package_name) { + return Err(anyhow!("Invalid Leo project name")); + } - // Derive the package directory path + // Derive the package directory path. + let mut path = current_dir()?; path.push(&package_name); - // Verify the package directory path does not exist yet + // Verify the package directory path does not exist yet. if path.exists() { return Err(anyhow!("Directory already exists {:?}", path)); } diff --git a/leo/commands/package/add.rs b/leo/commands/package/add.rs index 6d2da368b4..0b97c67bb4 100644 --- a/leo/commands/package/add.rs +++ b/leo/commands/package/add.rs @@ -90,9 +90,9 @@ impl Command for Add { Ok(()) } - fn apply(self, ctx: Context, _: Self::Input) -> Result { + fn apply(self, context: Context, _: Self::Input) -> Result { // checking that manifest exists... - if ctx.manifest().is_err() { + if context.manifest().is_err() { return Err(anyhow!("Package Manifest not found, try running leo init or leo new")); }; @@ -109,8 +109,8 @@ impl Command for Add { version, }; - let bytes = ctx.api.run_route(fetch)?.bytes()?; - let mut path = ctx.dir()?; + let bytes = context.api.run_route(fetch)?.bytes()?; + let mut path = context.dir()?; { // setup directory structure since request was success diff --git a/leo/commands/package/login.rs b/leo/commands/package/login.rs index 7e3950b03c..25425fccb0 100644 --- a/leo/commands/package/login.rs +++ b/leo/commands/package/login.rs @@ -58,14 +58,14 @@ impl Command for Login { Ok(()) } - fn apply(self, ctx: Context, _: Self::Input) -> Result { + fn apply(self, context: Context, _: Self::Input) -> Result { // quick hack to check if user is already logged in. ;) - if ctx.api.auth_token().is_some() { + if context.api.auth_token().is_some() { tracing::info!("You are already logged in"); - return Ok(ctx.api.auth_token().unwrap()); + return Ok(context.api.auth_token().unwrap()); }; - let mut api = ctx.api; + let mut api = context.api; // ...or trying to use arguments to either get token or user-pass let token = match (self.token, self.user, self.pass) { diff --git a/leo/commands/package/logout.rs b/leo/commands/package/logout.rs index 0d844ff7ed..6bd76a2575 100644 --- a/leo/commands/package/logout.rs +++ b/leo/commands/package/logout.rs @@ -22,16 +22,10 @@ use structopt::StructOpt; use tracing::Span; /// Remove credentials for Aleo PM from .leo directory -#[derive(StructOpt, Debug, Default)] +#[derive(StructOpt, Debug)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] pub struct Logout {} -impl Logout { - pub fn new() -> Logout { - Logout {} - } -} - impl Command for Logout { type Input = (); type Output = (); @@ -44,7 +38,7 @@ impl Command for Logout { Ok(()) } - fn apply(self, _ctx: Context, _: Self::Input) -> Result { + fn apply(self, _context: Context, _: Self::Input) -> Result { // the only error we're interested here is NotFound // however err in this case can also be of kind PermissionDenied or other if let Err(err) = remove_token() { diff --git a/leo/commands/package/publish.rs b/leo/commands/package/publish.rs index 97bce33719..ffcc1725e2 100644 --- a/leo/commands/package/publish.rs +++ b/leo/commands/package/publish.rs @@ -37,29 +37,23 @@ struct ResponseJson { } /// Publish package to Aleo Package Manager -#[derive(StructOpt, Debug, Default)] +#[derive(StructOpt, Debug)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] pub struct Publish {} -impl Publish { - pub fn new() -> Publish { - Publish {} - } -} - impl Command for Publish { type Input = ::Output; type Output = Option; /// Build program before publishing fn prelude(&self) -> Result { - Build::new().execute() + (Build {}).execute() } - fn apply(self, ctx: Context, _input: Self::Input) -> Result { + fn apply(self, context: Context, _input: Self::Input) -> Result { // Get the package manifest - let path = ctx.dir()?; - let manifest = ctx.manifest()?; + let path = context.dir()?; + let manifest = context.manifest()?; let package_name = manifest.get_package_name(); let package_version = manifest.get_package_version(); @@ -99,7 +93,7 @@ impl Command for Publish { // Client for make POST request let client = Client::new(); - let token = match ctx.api.auth_token() { + let token = match context.api.auth_token() { Some(token) => token, None => return Err(anyhow!("Login before publishing package: try leo login --help")), }; diff --git a/leo/commands/package/remove.rs b/leo/commands/package/remove.rs index 2d988fd55e..f0df722146 100644 --- a/leo/commands/package/remove.rs +++ b/leo/commands/package/remove.rs @@ -22,19 +22,13 @@ use structopt::StructOpt; use tracing::span::Span; /// Remove imported package -#[derive(StructOpt, Debug, Default)] +#[derive(StructOpt, Debug)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] pub struct Remove { #[structopt(name = "PACKAGE")] name: String, } -impl Remove { - pub fn new(name: String) -> Remove { - Remove { name } - } -} - impl Command for Remove { type Input = (); type Output = (); @@ -47,8 +41,8 @@ impl Command for Remove { Ok(()) } - fn apply(self, ctx: Context, _: Self::Input) -> Result { - let path = ctx.dir()?; + fn apply(self, context: Context, _: Self::Input) -> Result { + let path = context.dir()?; let package_name = self.name; LeoPackage::remove_imported_package(&package_name, &path)?; diff --git a/leo/commands/prove.rs b/leo/commands/prove.rs index bd240be342..dbe19b5777 100644 --- a/leo/commands/prove.rs +++ b/leo/commands/prove.rs @@ -28,17 +28,11 @@ use structopt::StructOpt; use tracing::span::Span; /// Run the program and produce a proof -#[derive(StructOpt, Debug, Default)] +#[derive(StructOpt, Debug)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] pub struct Prove { #[structopt(long = "skip-key-check", help = "Skip key verification on Setup stage")] - skip_key_check: bool, -} - -impl Prove { - pub fn new(skip_key_check: bool) -> Prove { - Prove { skip_key_check } - } + pub(crate) skip_key_check: bool, } impl Command for Prove { @@ -50,15 +44,16 @@ impl Command for Prove { } fn prelude(&self) -> Result { - Setup::new(self.skip_key_check).execute() + let skip_key_check = self.skip_key_check; + (Setup { skip_key_check }).execute() } - fn apply(self, ctx: Context, input: Self::Input) -> Result { + fn apply(self, context: Context, input: Self::Input) -> Result { let (program, parameters, prepared_verifying_key) = input; // Get the package name - let path = ctx.dir()?; - let package_name = ctx.manifest()?.get_package_name(); + let path = context.dir()?; + let package_name = context.manifest()?.get_package_name(); tracing::info!("Starting..."); diff --git a/leo/commands/run.rs b/leo/commands/run.rs index ffb05896fa..b1bf652ed7 100644 --- a/leo/commands/run.rs +++ b/leo/commands/run.rs @@ -26,17 +26,11 @@ use structopt::StructOpt; use tracing::span::Span; /// Build, Prove and Run Leo program with inputs -#[derive(StructOpt, Debug, Default)] +#[derive(StructOpt, Debug)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] pub struct Run { #[structopt(long = "skip-key-check", help = "Skip key verification on Setup stage")] - skip_key_check: bool, -} - -impl Run { - pub fn new(skip_key_check: bool) -> Run { - Run { skip_key_check } - } + pub(crate) skip_key_check: bool, } impl Command for Run { @@ -48,10 +42,11 @@ impl Command for Run { } fn prelude(&self) -> Result { - Prove::new(self.skip_key_check).execute() + let skip_key_check = self.skip_key_check; + (Prove { skip_key_check }).execute() } - fn apply(self, _ctx: Context, input: Self::Input) -> Result { + fn apply(self, _context: Context, input: Self::Input) -> Result { let (proof, prepared_verifying_key) = input; tracing::info!("Starting..."); diff --git a/leo/commands/setup.rs b/leo/commands/setup.rs index fafb8467a7..fad11bbe20 100644 --- a/leo/commands/setup.rs +++ b/leo/commands/setup.rs @@ -31,17 +31,11 @@ use structopt::StructOpt; use tracing::span::Span; /// Executes the setup command for a Leo program -#[derive(StructOpt, Debug, Default)] +#[derive(StructOpt, Debug)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] pub struct Setup { #[structopt(long = "skip-key-check", help = "Skip key verification")] - skip_key_check: bool, -} - -impl Setup { - pub fn new(skip_key_check: bool) -> Setup { - Setup { skip_key_check } - } + pub(crate) skip_key_check: bool, } impl Command for Setup { @@ -57,12 +51,12 @@ impl Command for Setup { } fn prelude(&self) -> Result { - Build::new().execute() + (Build {}).execute() } - fn apply(self, ctx: Context, input: Self::Input) -> Result { - let path = ctx.dir()?; - let package_name = ctx.manifest()?.get_package_name(); + fn apply(self, context: Context, input: Self::Input) -> Result { + let path = context.dir()?; + let package_name = context.manifest()?.get_package_name(); match input { Some((program, checksum_differs)) => { @@ -70,7 +64,6 @@ impl Command for Setup { let keys_exist = ProvingKeyFile::new(&package_name).exists_at(&path) && VerificationKeyFile::new(&package_name).exists_at(&path); - // If keys do not exist or the checksum differs, run the program setup // If keys do not exist or the checksum differs, run the program setup let (proving_key, prepared_verifying_key) = if !keys_exist || checksum_differs { tracing::info!("Starting..."); diff --git a/leo/commands/test.rs b/leo/commands/test.rs index be683a41c6..f5de8672f2 100644 --- a/leo/commands/test.rs +++ b/leo/commands/test.rs @@ -32,17 +32,11 @@ use structopt::StructOpt; use tracing::span::Span; /// Build program and run tests command -#[derive(StructOpt, Debug, Default)] +#[derive(StructOpt, Debug)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] pub struct Test { #[structopt(short = "f", long = "file", name = "file")] - files: Vec, -} - -impl Test { - pub fn new(files: Vec) -> Test { - Test { files } - } + pub(crate) files: Vec, } impl Command for Test { @@ -57,12 +51,12 @@ impl Command for Test { Ok(()) } - fn apply(self, ctx: Context, _: Self::Input) -> Result { + fn apply(self, context: Context, _: Self::Input) -> Result { // Get the package name - let package_name = ctx.manifest()?.get_package_name(); + let package_name = context.manifest()?.get_package_name(); // Sanitize the package path to the root directory - let mut package_path = ctx.dir()?; + let mut package_path = context.dir()?; if package_path.is_file() { package_path.pop(); } diff --git a/leo/commands/update.rs b/leo/commands/update.rs index 09e0ca640a..f3027e5907 100644 --- a/leo/commands/update.rs +++ b/leo/commands/update.rs @@ -22,7 +22,7 @@ use tracing::span::Span; /// Setting for automatic updates of Leo #[derive(Debug, StructOpt, PartialEq)] -pub enum Sub { +pub enum Automatic { Automatic { #[structopt(name = "bool", help = "Boolean value: true or false", parse(try_from_str))] value: bool, @@ -30,30 +30,20 @@ pub enum Sub { } /// Update Leo to the latest version -#[derive(StructOpt, Debug, Default)] +#[derive(StructOpt, Debug)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] pub struct Update { /// List all available versions of Leo #[structopt(short, long)] - list: bool, + pub(crate) list: bool, /// For Aleo Studio only #[structopt(short, long)] - studio: bool, + pub(crate) studio: bool, /// Setting for automatic updates of Leo #[structopt(subcommand)] - automatic: Option, -} - -impl Update { - pub fn new(list: bool, studio: bool, automatic: Option) -> Update { - Update { - list, - studio, - automatic, - } - } + pub(crate) automatic: Option, } impl Command for Update { @@ -69,13 +59,13 @@ impl Command for Update { } fn apply(self, _: Context, _: Self::Input) -> Result { - // if --list is passed - simply list everything and exit + // If --list is passed, list all available versions and return. if self.list { return Updater::show_available_releases().map_err(|e| anyhow!("Could not fetch versions: {}", e)); } - // in case automatic subcommand was called - if let Some(Sub::Automatic { value }) = self.automatic { + // Handles enabling and disabling automatic updates in the config file. + if let Some(Automatic::Automatic { value }) = self.automatic { Config::set_update_automatic(value)?; match value { diff --git a/leo/commands/watch.rs b/leo/commands/watch.rs index 16ae65c2a5..7a98b9604f 100644 --- a/leo/commands/watch.rs +++ b/leo/commands/watch.rs @@ -27,7 +27,7 @@ use tracing::span::Span; const LEO_SOURCE_DIR: &str = "src/"; /// Watch file changes in src/ directory and run Build Command -#[derive(StructOpt, Debug, Default)] +#[derive(StructOpt, Debug)] #[structopt(setting = structopt::clap::AppSettings::ColoredHelp)] pub struct Watch { /// Set up watch interval @@ -35,12 +35,6 @@ pub struct Watch { interval: u64, } -impl Watch { - pub fn new(interval: u64) -> Watch { - Watch { interval } - } -} - impl Command for Watch { type Input = (); type Output = (); @@ -53,7 +47,7 @@ impl Command for Watch { Ok(()) } - fn apply(self, _ctx: Context, _: Self::Input) -> Result { + fn apply(self, _context: Context, _: Self::Input) -> Result { let (tx, rx) = channel(); let mut watcher = watcher(tx, Duration::from_secs(self.interval)).unwrap(); @@ -70,7 +64,7 @@ impl Command for Watch { match rx.recv() { // See changes on the write event Ok(DebouncedEvent::Write(_write)) => { - match Build::new().execute() { + match (Build {}).execute() { Ok(_output) => { tracing::info!("Built successfully"); } diff --git a/leo/logger.rs b/leo/logger.rs index c6da4abdd8..a2f81bc4c1 100644 --- a/leo/logger.rs +++ b/leo/logger.rs @@ -158,7 +158,12 @@ where N: for<'a> FormatFields<'a> + 'static, T: FormatTime, { - fn format_event(&self, ctx: &FmtContext<'_, S, N>, writer: &mut dyn fmt::Write, event: &Event<'_>) -> fmt::Result { + fn format_event( + &self, + context: &FmtContext<'_, S, N>, + writer: &mut dyn fmt::Write, + event: &Event<'_>, + ) -> fmt::Result { let meta = event.metadata(); if self.display_level { @@ -174,7 +179,7 @@ where let mut message = "".to_string(); - let scope = ctx.scope(); + let scope = context.scope(); for span in scope { message += span.metadata().name(); @@ -190,7 +195,7 @@ where write!(writer, "{:>10} ", colored_string(meta.level(), &message)).expect("Error writing event"); } - ctx.format_fields(writer, event)?; + context.format_fields(writer, event)?; writeln!(writer) } } diff --git a/leo/tests/mod.rs b/leo/tests/mod.rs index 9ecbf11522..7bc611f49d 100644 --- a/leo/tests/mod.rs +++ b/leo/tests/mod.rs @@ -39,34 +39,34 @@ const PEDERSEN_HASH_PATH: &str = "./examples/pedersen-hash/"; #[test] pub fn build_pedersen_hash() -> Result<()> { - Build::new().apply(ctx()?, ())?; + (Build {}).apply(context()?, ())?; Ok(()) } #[test] pub fn setup_pedersen_hash() -> Result<()> { - let build = Build::new().apply(ctx()?, ())?; - Setup::new(false).apply(ctx()?, build.clone())?; - Setup::new(true).apply(ctx()?, build)?; + let build = (Build {}).apply(context()?, ())?; + (Setup { skip_key_check: false }).apply(context()?, build.clone())?; + (Setup { skip_key_check: true }).apply(context()?, build)?; Ok(()) } #[test] pub fn prove_pedersen_hash() -> Result<()> { - let build = Build::new().apply(ctx()?, ())?; - let setup = Setup::new(false).apply(ctx()?, build)?; - Prove::new(false).apply(ctx()?, setup.clone())?; - Prove::new(true).apply(ctx()?, setup)?; + let build = (Build {}).apply(context()?, ())?; + let setup = (Setup { skip_key_check: false }).apply(context()?, build)?; + (Prove { skip_key_check: false }).apply(context()?, setup.clone())?; + (Prove { skip_key_check: true }).apply(context()?, setup)?; Ok(()) } #[test] pub fn run_pedersen_hash() -> Result<()> { - let build = Build::new().apply(ctx()?, ())?; - let setup = Setup::new(false).apply(ctx()?, build)?; - let prove = Prove::new(false).apply(ctx()?, setup)?; - Run::new(false).apply(ctx()?, prove.clone())?; - Run::new(true).apply(ctx()?, prove)?; + let build = (Build {}).apply(context()?, ())?; + let setup = (Setup { skip_key_check: false }).apply(context()?, build)?; + let prove = (Prove { skip_key_check: false }).apply(context()?, setup)?; + (Run { skip_key_check: false }).apply(context()?, prove.clone())?; + (Run { skip_key_check: true }).apply(context()?, prove)?; Ok(()) } @@ -75,14 +75,14 @@ pub fn test_pedersen_hash() -> Result<()> { let mut main_file = PathBuf::from(PEDERSEN_HASH_PATH); main_file.push("src/main.leo"); - Test::new(Vec::new()).apply(ctx()?, ())?; - Test::new(vec![main_file]).apply(ctx()?, ())?; + (Test { files: vec![] }).apply(context()?, ())?; + (Test { files: vec![main_file] }).apply(context()?, ())?; Ok(()) } #[test] pub fn test_logout() -> Result<()> { - Logout::new().apply(ctx()?, ())?; + (Logout {}).apply(context()?, ())?; Ok(()) } @@ -91,19 +91,19 @@ pub fn test_logout() -> Result<()> { #[test] pub fn login_incorrect_credentials_or_token() -> Result<()> { // no credentials passed - let login = Login::new(None, None, None).apply(ctx()?, ()); + let login = Login::new(None, None, None).apply(context()?, ()); assert!(login.is_err()); // incorrect token - let login = Login::new(Some("none".to_string()), None, None).apply(ctx()?, ()); + let login = Login::new(Some("none".to_string()), None, None).apply(context()?, ()); assert!(login.is_err()); // only user, no pass - let login = Login::new(None, Some("user".to_string()), None).apply(ctx()?, ()); + let login = Login::new(None, Some("user".to_string()), None).apply(context()?, ()); assert!(login.is_err()); // no user, only pass - let login = Login::new(None, None, Some("pass".to_string())).apply(ctx()?, ()); + let login = Login::new(None, None, Some("pass".to_string())).apply(context()?, ()); assert!(login.is_err()); Ok(()) @@ -111,20 +111,48 @@ pub fn login_incorrect_credentials_or_token() -> Result<()> { #[test] pub fn leo_update_and_update_automatic() -> Result<()> { - Update::new(true, true, None).apply(ctx()?, ())?; - Update::new(false, true, None).apply(ctx()?, ())?; - Update::new(false, false, None).apply(ctx()?, ())?; + let update = Update { + list: true, + studio: true, + automatic: None, + }; + update.apply(context()?, ())?; - Update::new(false, false, Some(UpdateAutomatic::Automatic { value: true })).apply(ctx()?, ())?; - Update::new(false, false, Some(UpdateAutomatic::Automatic { value: false })).apply(ctx()?, ())?; + let update = Update { + list: false, + studio: true, + automatic: None, + }; + update.apply(context()?, ())?; + + let update = Update { + list: false, + studio: false, + automatic: None, + }; + update.apply(context()?, ())?; + + let update = Update { + list: false, + studio: false, + automatic: Some(UpdateAutomatic::Automatic { value: true }), + }; + update.apply(context()?, ())?; + + let update = Update { + list: false, + studio: false, + automatic: Some(UpdateAutomatic::Automatic { value: false }), + }; + update.apply(context()?, ())?; Ok(()) } /// Create context for Pedersen Hash example -fn ctx() -> Result { +fn context() -> Result { let path = PathBuf::from(&PEDERSEN_HASH_PATH); - let ctx = create_context(path)?; + let context = create_context(path)?; - Ok(ctx) + Ok(context) } diff --git a/package/src/errors/package.rs b/package/src/errors/package.rs index 63bab79fd8..f0a07ea1ee 100644 --- a/package/src/errors/package.rs +++ b/package/src/errors/package.rs @@ -26,6 +26,9 @@ pub enum PackageError { #[error("Failed to initialize package {:?} ({:?})", _0, _1)] FailedToInitialize(String, OsString), + #[error("Invalid project name: {:?}", _0)] + InvalidPackageName(String), + #[error("`{}` metadata: {}", _0, _1)] Removing(&'static str, io::Error), } diff --git a/package/src/errors/root/manifest.rs b/package/src/errors/root/manifest.rs index b9645830d3..1e732a817f 100644 --- a/package/src/errors/root/manifest.rs +++ b/package/src/errors/root/manifest.rs @@ -18,6 +18,9 @@ use std::io; #[derive(Debug, Error)] pub enum ManifestError { + #[error("{}: {}", _0, _1)] + Crate(&'static str, String), + #[error("`{}` creating: {}", _0, _1)] Creating(&'static str, io::Error), @@ -36,3 +39,9 @@ pub enum ManifestError { #[error("`{}` writing: {}", _0, _1)] Writing(&'static str, io::Error), } + +impl From for ManifestError { + fn from(error: crate::errors::PackageError) -> Self { + ManifestError::Crate("leo-package", error.to_string()) + } +} diff --git a/package/src/lib.rs b/package/src/lib.rs index fb3d000893..eb7cf4bb00 100644 --- a/package/src/lib.rs +++ b/package/src/lib.rs @@ -37,6 +37,11 @@ impl LeoPackage { package::Package::initialize(package_name, is_lib, path) } + /// Returns `true` if the given Leo package name is valid. + pub fn is_package_name_valid(package_name: &str) -> bool { + package::Package::is_package_name_valid(package_name) + } + /// Removes an imported Leo package pub fn remove_imported_package(package_name: &str, path: &Path) -> Result<(), PackageError> { package::Package::remove_imported_package(package_name, path) diff --git a/package/src/package.rs b/package/src/package.rs index f149c4ddf1..f5c46e2696 100644 --- a/package/src/package.rs +++ b/package/src/package.rs @@ -34,17 +34,79 @@ pub struct Package { } impl Package { - pub fn new(package_name: &str) -> Self { - Self { + pub fn new(package_name: &str) -> Result { + // Check that the package name is valid. + if !Self::is_package_name_valid(package_name) { + return Err(PackageError::InvalidPackageName(package_name.to_string())); + } + + Ok(Self { name: package_name.to_owned(), version: "0.1.0".to_owned(), description: None, license: None, + }) + } + + /// Returns `true` if the package name is valid. + /// + /// Package names must be lowercase and composed solely + /// of ASCII alphanumeric characters, and may be word-separated + /// by a single dash '-'. + pub fn is_package_name_valid(package_name: &str) -> bool { + // Check that the package name is nonempty. + if package_name.is_empty() { + tracing::error!("Project names must be nonempty"); + return false; } + + let mut previous = package_name.chars().next().unwrap(); + + // Check that the first character is not a dash. + if previous == '-' { + tracing::error!("Project names cannot begin with a dash"); + return false; + } + + // Iterate and check that the package name is valid. + for current in package_name.chars() { + // Check that the package name is lowercase. + if !current.is_ascii_lowercase() && current != '-' { + tracing::error!("Project names must be all lowercase"); + return false; + } + + // Check that the package name is only ASCII alphanumeric or a dash. + if !current.is_ascii_alphanumeric() && current != '-' { + tracing::error!("Project names must be ASCII alphanumeric, and may be word-separated with a dash"); + return false; + } + + // If the previous character was a dash, check that the current character is not a dash. + if previous == '-' && current == '-' { + tracing::error!("Project names may only be word-separated by one dash"); + return false; + } + + previous = current; + } + + // Check that the last character is not a dash. + if previous == '-' { + tracing::error!("Project names cannot end with a dash"); + return false; + } + + true } /// Returns `true` if a package is can be initialized at a given path. pub fn can_initialize(package_name: &str, is_lib: bool, path: &Path) -> bool { + // Check that the package name is valid. + if !Self::is_package_name_valid(package_name) { + return false; + } + let mut result = true; let mut existing_files = vec![]; @@ -91,6 +153,11 @@ impl Package { /// Returns `true` if a package is initialized at the given path pub fn is_initialized(package_name: &str, is_lib: bool, path: &Path) -> bool { + // Check that the package name is valid. + if !Self::is_package_name_valid(package_name) { + return false; + } + // Check if the manifest file exists. if !Manifest::exists_at(&path) { return false; @@ -137,7 +204,7 @@ impl Package { // Next, initialize this directory as a Leo package. { // Create the manifest file. - Manifest::new(&package_name).write_to(&path)?; + Manifest::new(&package_name)?.write_to(&path)?; // Verify that the .gitignore file does not exist. if !Gitignore::exists_at(&path) { @@ -190,3 +257,27 @@ impl Package { Ok(ImportsDirectory::remove_import(path, package_name)?) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_is_package_name_valid() { + assert!(Package::is_package_name_valid("foo")); + assert!(Package::is_package_name_valid("foo-bar")); + assert!(Package::is_package_name_valid("foo-bar-baz")); + + assert!(!Package::is_package_name_valid("")); + assert!(!Package::is_package_name_valid("-")); + assert!(!Package::is_package_name_valid("-foo")); + assert!(!Package::is_package_name_valid("-foo-")); + assert!(!Package::is_package_name_valid("foo--bar")); + assert!(!Package::is_package_name_valid("foo---bar")); + assert!(!Package::is_package_name_valid("foo--bar--baz")); + assert!(!Package::is_package_name_valid("foo---bar---baz")); + assert!(!Package::is_package_name_valid("foo*bar")); + assert!(!Package::is_package_name_valid("foo,bar")); + assert!(!Package::is_package_name_valid("foo_bar")); + } +} diff --git a/package/src/root/manifest.rs b/package/src/root/manifest.rs index 3a85f60447..540749fcdc 100644 --- a/package/src/root/manifest.rs +++ b/package/src/root/manifest.rs @@ -39,11 +39,11 @@ pub struct Manifest { } impl Manifest { - pub fn new(package_name: &str) -> Self { - Self { - project: Package::new(package_name), + pub fn new(package_name: &str) -> Result { + Ok(Self { + project: Package::new(package_name)?, remote: None, - } + }) } pub fn filename() -> String { diff --git a/package/tests/initialize/initialize.rs b/package/tests/initialize/initialize.rs index e3cffcfd8c..5e1b98bde8 100644 --- a/package/tests/initialize/initialize.rs +++ b/package/tests/initialize/initialize.rs @@ -52,7 +52,10 @@ fn initialize_fails_with_existing_manifest() { assert!(Package::can_initialize(TEST_PACKAGE_NAME, false, &test_directory)); // Manually add a manifest file to the `test_directory` - Manifest::new(TEST_PACKAGE_NAME).write_to(&test_directory).unwrap(); + Manifest::new(TEST_PACKAGE_NAME) + .unwrap() + .write_to(&test_directory) + .unwrap(); // Attempt to initialize a package at the `test_directory` assert!(Package::initialize(TEST_PACKAGE_NAME, false, &test_directory).is_err());