Skip to content
Closed
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
134bb45
stashing all refs on cmd
damirka Jan 27, 2021
8b0e3f5
added build and deploy commands, cleanup, synths and logger moved
damirka Jan 29, 2021
63c0198
fixes anyhow version
damirka Jan 29, 2021
27b7a08
added prove,run,setup,test,watch
Feb 2, 2021
c257b02
Only update and debug left
damirka Feb 2, 2021
e0fe32f
fixed add command behavior and err print
damirka Feb 3, 2021
747fbe7
wrapping up - most of the jobs are done
damirka Feb 3, 2021
48811e0
almost there - update left
damirka Feb 3, 2021
10a3234
added api
damirka Feb 3, 2021
6e4b06d
cosmetics
damirka Feb 3, 2021
20679be
added login <token> feature
damirka Feb 4, 2021
a4b492c
more cleanups
damirka Feb 4, 2021
768320c
Merge branch 'master' into leo-ref
damirka Feb 4, 2021
1ccd9e8
merged master, updated licenses
damirka Feb 4, 2021
0fccb19
fixes Pedersen hash example
damirka Feb 4, 2021
b82767f
more anyhow in results, started tests
damirka Feb 4, 2021
d17f4f0
added tests for build/run/setup and login
damirka Feb 4, 2021
cf6910e
adds leo package group of commands
damirka Feb 4, 2021
f269912
cleanup and few improvements
damirka Feb 4, 2021
13256bf
finished with update and update automatic
damirka Feb 4, 2021
7426a15
fixes CI - added subcommand
damirka Feb 4, 2021
195765c
clippy fix
damirka Feb 4, 2021
edb2325
Update .rustfmt.toml
howardwu Feb 4, 2021
cdba80e
removed leo-package, cmd to Command
damirka Feb 5, 2021
8ca17dd
Merge branch 'leo-ref' of github.com:damirka/leo into leo-ref
damirka Feb 5, 2021
cb245f0
Merge branch 'master' into leo-ref
damirka Feb 5, 2021
56ec74d
implements #277
damirka Feb 8, 2021
5a031d1
few minor patches
damirka Feb 8, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/leo-add-remove.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
- name: 'leo add (w/o login) & remove'
run: |
cd .. && leo new my-app && cd my-app
leo add argus4130/xnor
leo remove xnor
leo package add argus4130/xnor
leo package remove xnor
leo clean

9 changes: 4 additions & 5 deletions .github/workflows/leo-login-logout.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,8 @@ jobs:
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 clean
leo logout
leo package login -u "$USER" -p "$PASS"
leo package add argus4130/xnor
leo package remove xnor
leo package logout

1 change: 1 addition & 0 deletions .rustfmt.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use_try_shorthand = true
# Nightly configurations
imports_layout = "HorizontalVertical"
license_template_path = ".resources/license_header"
# merge_imports = true
imports_granularity = "Crate"
overflow_delimited_expr = true
reorder_impl_items = true
Expand Down
69 changes: 67 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ default-features = false
[dependencies.snarkvm-utilities]
version = "0.0.2"

[dependencies.anyhow]
version = "1.0"

[dependencies.structopt]
version = "0.3"

[dependencies.clap]
version = "2.33.3"

Expand Down
2 changes: 1 addition & 1 deletion examples/pedersen-hash/src/main.leo
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ circuit PedersenHash {
}

function hash(self, bits: [bool; 256]) -> group {
let mut digest: group = 0;
let mut digest: group = 0group;
for i in 0..256 {
if bits[i] {
digest += self.parameters[i];
Expand Down
203 changes: 203 additions & 0 deletions leo/api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
// Copyright (C) 2019-2021 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 serde::Serialize;

use anyhow::{anyhow, Error, Result};
use reqwest::{
blocking::{Client, Response},
Method,
StatusCode,
};

/// Trait describes API Routes and Request bodies, struct which implements
/// Route MUST also support Serialize to be usable in Api::run_route(r: Route)
pub trait Route {
/// Whether to use bearer auth or not. Some routes may have additional
/// features for logged-in users, so authorization token should be sent
/// if it is created of course
const AUTH: bool;

/// HTTP method to use when requesting
const METHOD: Method;

/// URL path without first forward slash (e.g. v1/package/fetch)
const PATH: &'static str;

/// Output type for this route. For login it is simple - String
/// But for other routes may be more complex.
type Output;

/// Process reqwest Response and turn it into Output
fn process(&self, res: Response) -> Result<Self::Output>;

/// Transform specific status codes into correct errors for this route.
/// For example 404 on package fetch should mean that 'Package is not found'
fn status_to_err(&self, _status: StatusCode) -> Error {
anyhow!("Unidentified API error")
}
}

/// REST API handler with reqwest::blocking inside
#[derive(Clone, Debug)]
pub struct Api {
host: String,
client: Client,
/// Authorization token for API requests
auth_token: Option<String>,
}

impl Api {
/// Create new instance of API, set host and Client is going to be
/// created and set automatically
pub fn new(host: String, auth_token: Option<String>) -> Api {
Api {
client: Client::new(),
auth_token,
host,
}
}

/// Get token for bearer auth, should be passed into Api through Context
pub fn auth_token(&self) -> Option<String> {
self.auth_token.clone()
}

/// Set authorization token for future requests
pub fn set_auth_token(&mut self, token: String) {
self.auth_token = Some(token);
}

/// Run specific route struct. Turn struct into request body
/// and use type constants and Route implementation to get request params
pub fn run_route<T>(&self, route: T) -> Result<T::Output>
where
T: Route,
T: Serialize,
{
let mut res = self.client.request(T::METHOD, &format!("{}{}", self.host, T::PATH));

// add body for POST and PUT requests
if T::METHOD == Method::POST || T::METHOD == Method::PUT {
res = res.json(&route);
};

// if Route::Auth is true and token is present - pass it
if T::AUTH && self.auth_token().is_some() {
res = res.bearer_auth(&self.auth_token().unwrap());
};

// only one error is possible here
let res = res.send().map_err(|_| anyhow!("Unable to connect to Aleo PM"))?;

// where magic begins
route.process(res)
}
}

// --------------------------------------------------
// | Defining routes |
// --------------------------------------------------

/// Handler for 'fetch' route - fetch packages from Aleo PM
/// Route: POST /v1/package/fetch
#[derive(Serialize, Debug)]
pub struct Fetch {
pub author: String,
pub package_name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
}

impl Route for Fetch {
type Output = Response;

const AUTH: bool = true;
const METHOD: Method = Method::POST;
const PATH: &'static str = "api/package/fetch";

fn process(&self, res: Response) -> Result<Self::Output> {
// check status code first
if res.status() != 200 {
return Err(self.status_to_err(res.status()));
};

Ok(res)
}

fn status_to_err(&self, status: StatusCode) -> Error {
match status {
StatusCode::BAD_REQUEST => anyhow!("Package is not found - check author and/or package name"),
// TODO: we should return 404 on not found author/package
// and return BAD_REQUEST if data format is incorrect or some of the arguments
// were not passed
StatusCode::NOT_FOUND => anyhow!("Package is hidden"),
_ => anyhow!("Unknown API error: {}", status),
}
}
}

/// Handler for 'login' route - send username and password and receive JWT
/// Route: POST /v1/account/authenticate
#[derive(Serialize)]
pub struct Login {
pub email_username: String,
pub password: String,
}

impl Route for Login {
type Output = Response;

const AUTH: bool = false;
const METHOD: Method = Method::POST;
const PATH: &'static str = "api/account/authenticate";

fn process(&self, res: Response) -> Result<Self::Output> {
if res.status() != 200 {
return Err(self.status_to_err(res.status()));
}

Ok(res)
}

fn status_to_err(&self, status: StatusCode) -> Error {
match status {
StatusCode::BAD_REQUEST => anyhow!("This username is not yet registered or the password is incorrect"),
// TODO: NOT_FOUND here should be replaced, this error code has no relation to what this route is doing
StatusCode::NOT_FOUND => anyhow!("Incorrect password"),
_ => anyhow!("Unknown API error: {}", status),
}
}
}

/// Handler for 'my_profile' route. Meant to be used to get profile details but
/// in current application is used to check if user is logged in. Any non-200 response
/// is treated as Unauthorized
#[derive(Serialize)]
pub struct Profile {}

impl Route for Profile {
type Output = bool;

const AUTH: bool = true;
const METHOD: Method = Method::GET;
const PATH: &'static str = "api/account/my_profile";

fn process(&self, res: Response) -> Result<Self::Output> {
// this may be extended for more precise error handling
Ok(res.status() == 200)
}
}
Loading