Skip to content

Commit

Permalink
Implement Leo Account (ProvableHQ#2513)
Browse files Browse the repository at this point in the history
* implement leo account new --seed

* implement leo account new --write

* implement leo account import --write

* gitignore
  • Loading branch information
collinc97 authored Aug 7, 2023
1 parent b22232c commit af1f7f9
Show file tree
Hide file tree
Showing 11 changed files with 159 additions and 24 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,7 @@ sccache*/
.\#*

# code coverage scripts
*.bat
*.bat

# environment
.env
2 changes: 2 additions & 0 deletions Cargo.lock

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

7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ version = "0.15.7"
[dependencies.dirs]
version = "5.0.0"

[dependencies.dotenvy]
version = "0.15.7"

[dependencies.indexmap]
version = "1.9"
features = [ "serde" ]
Expand All @@ -101,6 +104,10 @@ version = "1.4.0"
[dependencies.rand]
version = "0.8"

[dependencies.rand_chacha]
version = "0.3.0"
default-features = false

[dependencies.rand_core]
version = "0.6.4"

Expand Down
2 changes: 1 addition & 1 deletion compiler/compiler/tests/utilities/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ pub fn setup_build_directory(program_name: &str, bytecode: &String, handler: &Ha
let _manifest_file = Manifest::create(&directory, &program_id).unwrap();

// Create the environment file.
Env::<Network>::new().write_to(&directory).unwrap();
Env::<Network>::new().unwrap().write_to(&directory).unwrap();
if Env::<Network>::exists_at(&directory) {
println!(".env file created at {:?}", &directory);
}
Expand Down
7 changes: 7 additions & 0 deletions errors/src/errors/cli/cli_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,4 +164,11 @@ create_messages!(
msg: format!("Failed to execute the `execute` command.\nSnarkVM Error: {error}"),
help: None,
}

@backtraced
failed_to_parse_seed {
args: (error: impl Display),
msg: format!("Failed to parse the seed string for account.\nSnarkVM Error: {error}"),
help: None,
}
);
4 changes: 2 additions & 2 deletions errors/src/errors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ use crate::LeoMessageCode;
pub mod ast;
pub use self::ast::*;

/// Contains the AST error definitions.
/// Contains the CLI error definitions.
pub mod cli;
pub use self::cli::*;

/// Contains the AST error definitions.
/// Contains the Compiler error definitions.
pub mod compiler;
pub use self::compiler::*;

Expand Down
21 changes: 6 additions & 15 deletions leo/cli/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,11 @@ pub struct CLI {
///Leo compiler and package manager
#[derive(Parser, Debug)]
enum Commands {
// #[clap(about = "Create a new Leo package in an existing directory")]
// Init {
// #[clap(flatten)]
// command: Init,
// },
//
#[clap(about = "Create a new Aleo account")]
Account {
#[clap(subcommand)]
command: Account,
},
#[clap(about = "Create a new Leo package in a new directory")]
New {
#[clap(flatten)]
Expand Down Expand Up @@ -80,13 +79,6 @@ enum Commands {
#[clap(flatten)]
command: Update,
},
// #[clap(subcommand)]
// Node(Node),
// #[clap(about = "Deploy a program")]
// Deploy {
// #[clap(flatten)]
// command: Deploy,
// },
}

pub fn handle_error<T>(res: Result<T>) -> T {
Expand Down Expand Up @@ -114,6 +106,7 @@ pub fn run_with_args(cli: CLI) -> Result<()> {
let context = handle_error(Context::new(cli.path));

match cli.command {
Commands::Account { command } => command.try_execute(context),
Commands::New { command } => command.try_execute(context),
Commands::Build { command } => {
// Enter tracing span
Expand All @@ -135,7 +128,5 @@ pub fn run_with_args(cli: CLI) -> Result<()> {
Commands::Run { command } => command.try_execute(context),
Commands::Execute { command } => command.try_execute(context),
Commands::Update { command } => command.try_execute(context),
// Commands::Node(command) => command.try_execute(context),
// Commands::Deploy { command } => command.try_execute(context),
}
}
117 changes: 117 additions & 0 deletions leo/cli/commands/account.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// 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_package::root::Env;
use snarkvm::prelude::{Address, PrivateKey, ViewKey};

use rand::SeedableRng;
use rand_chacha::ChaChaRng;

/// Commands to manage Aleo accounts.
#[derive(Parser, Debug)]
pub enum Account {
/// Generates a new Aleo account
New {
/// Seed the RNG with a numeric value.
#[clap(short = 's', long)]
seed: Option<u64>,
/// Write the private key to the .env file.
#[clap(short = 'w', long)]
write: bool,
},
/// Derive an Aleo account from a private key.
Import {
/// Private key plaintext
private_key: PrivateKey<CurrentNetwork>,
/// Write the private key to the .env file.
#[clap(short = 'w', long)]
write: bool,
},
}

impl Command for Account {
type Input = ();
type Output = ();

fn prelude(&self, _: Context) -> Result<Self::Input>
where
Self: Sized,
{
Ok(())
}

fn apply(self, ctx: Context, _: Self::Input) -> Result<Self::Output>
where
Self: Sized,
{
match self {
Account::New { seed, write } => {
// Sample a new Aleo account.
let private_key = match seed {
// Recover the field element deterministically.
Some(seed) => PrivateKey::new(&mut ChaChaRng::seed_from_u64(seed)),
// Sample a random field element.
None => PrivateKey::new(&mut ChaChaRng::from_entropy()),
}
.map_err(CliError::failed_to_parse_seed)?;

// Derive the view key and address and print to stdout.
print_keys(private_key)?;

// Save key data to .env file.
if write {
write_to_env_file(private_key, &ctx)?;
}
}
Account::Import { private_key, write } => {
// Derive the view key and address and print to stdout.
print_keys(private_key)?;

// Save key data to .env file.
if write {
write_to_env_file(private_key, &ctx)?;
}
}
}
Ok(())
}
}

// Helper functions

// Print keys as a formatted string without log level.
fn print_keys(private_key: PrivateKey<CurrentNetwork>) -> Result<()> {
let view_key = ViewKey::try_from(&private_key)?;
let address = Address::<CurrentNetwork>::try_from(&view_key)?;

println!(
"\n {:>12} {private_key}\n {:>12} {view_key}\n {:>12} {address}\n",
"Private Key".cyan().bold(),
"View Key".cyan().bold(),
"Address".cyan().bold(),
);
Ok(())
}

// Write the network and private key to the .env file in project directory.
fn write_to_env_file(private_key: PrivateKey<CurrentNetwork>, ctx: &Context) -> Result<()> {
let data = format!("NETWORK=testnet3\nPRIVATE_KEY={private_key}\n");
let program_dir = ctx.dir()?;
Env::<CurrentNetwork>::from(data).write_to(&program_dir)?;
tracing::info!("✅ Private Key written to {}", program_dir.join(".env").display());
Ok(())
}
3 changes: 3 additions & 0 deletions leo/cli/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
// 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/>.

pub mod account;
pub use account::Account;

pub mod build;
pub use build::Build;

Expand Down
2 changes: 1 addition & 1 deletion leo/package/src/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ impl<N: Network> Package<N> {
// Verify that the .env file does not exist.
if !Env::<N>::exists_at(path) {
// Create the .env file.
Env::<N>::new().write_to(path)?;
Env::<N>::new()?.write_to(path)?;
}

// Create the source directory.
Expand Down
13 changes: 9 additions & 4 deletions leo/package/src/root/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,17 @@ pub static ENV_FILENAME: &str = ".env";

#[derive(Deserialize, Default)]
pub struct Env<N: Network> {
data: String,
_phantom: PhantomData<N>,
}

impl<N: Network> Env<N> {
pub fn new() -> Self {
Self { _phantom: PhantomData }
pub fn new() -> Result<Self> {
Ok(Self { data: Self::template()?, _phantom: PhantomData })
}

pub fn from(data: String) -> Self {
Self { data, _phantom: PhantomData }
}

pub fn exists_at(path: &Path) -> bool {
Expand All @@ -48,11 +53,11 @@ impl<N: Network> Env<N> {
}

let mut file = File::create(&path).map_err(PackageError::io_error_env_file)?;
file.write_all(self.template()?.as_bytes()).map_err(PackageError::io_error_env_file)?;
file.write_all(self.data.as_bytes()).map_err(PackageError::io_error_env_file)?;
Ok(())
}

fn template(&self) -> Result<String> {
fn template() -> Result<String> {
// Initialize an RNG.
let rng = &mut rand::thread_rng();

Expand Down

0 comments on commit af1f7f9

Please sign in to comment.