mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-11-22 05:00:59 +03:00
Implement Leo Account (#2513)
* implement leo account new --seed * implement leo account new --write * implement leo account import --write * gitignore
This commit is contained in:
parent
b22232c02c
commit
af1f7f96fb
5
.gitignore
vendored
5
.gitignore
vendored
@ -20,4 +20,7 @@ sccache*/
|
|||||||
.\#*
|
.\#*
|
||||||
|
|
||||||
# code coverage scripts
|
# code coverage scripts
|
||||||
*.bat
|
*.bat
|
||||||
|
|
||||||
|
# environment
|
||||||
|
.env
|
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1342,6 +1342,7 @@ dependencies = [
|
|||||||
"colored",
|
"colored",
|
||||||
"console",
|
"console",
|
||||||
"dirs 5.0.1",
|
"dirs 5.0.1",
|
||||||
|
"dotenvy",
|
||||||
"indexmap 1.9.3",
|
"indexmap 1.9.3",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"leo-ast",
|
"leo-ast",
|
||||||
@ -1351,6 +1352,7 @@ dependencies = [
|
|||||||
"leo-parser",
|
"leo-parser",
|
||||||
"leo-span",
|
"leo-span",
|
||||||
"rand",
|
"rand",
|
||||||
|
"rand_chacha",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"rusty-hook",
|
"rusty-hook",
|
||||||
|
@ -91,6 +91,9 @@ version = "0.15.7"
|
|||||||
[dependencies.dirs]
|
[dependencies.dirs]
|
||||||
version = "5.0.0"
|
version = "5.0.0"
|
||||||
|
|
||||||
|
[dependencies.dotenvy]
|
||||||
|
version = "0.15.7"
|
||||||
|
|
||||||
[dependencies.indexmap]
|
[dependencies.indexmap]
|
||||||
version = "1.9"
|
version = "1.9"
|
||||||
features = [ "serde" ]
|
features = [ "serde" ]
|
||||||
@ -101,6 +104,10 @@ version = "1.4.0"
|
|||||||
[dependencies.rand]
|
[dependencies.rand]
|
||||||
version = "0.8"
|
version = "0.8"
|
||||||
|
|
||||||
|
[dependencies.rand_chacha]
|
||||||
|
version = "0.3.0"
|
||||||
|
default-features = false
|
||||||
|
|
||||||
[dependencies.rand_core]
|
[dependencies.rand_core]
|
||||||
version = "0.6.4"
|
version = "0.6.4"
|
||||||
|
|
||||||
|
@ -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();
|
let _manifest_file = Manifest::create(&directory, &program_id).unwrap();
|
||||||
|
|
||||||
// Create the environment file.
|
// 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) {
|
if Env::<Network>::exists_at(&directory) {
|
||||||
println!(".env file created at {:?}", &directory);
|
println!(".env file created at {:?}", &directory);
|
||||||
}
|
}
|
||||||
|
@ -164,4 +164,11 @@ create_messages!(
|
|||||||
msg: format!("Failed to execute the `execute` command.\nSnarkVM Error: {error}"),
|
msg: format!("Failed to execute the `execute` command.\nSnarkVM Error: {error}"),
|
||||||
help: None,
|
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,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
@ -21,11 +21,11 @@ use crate::LeoMessageCode;
|
|||||||
pub mod ast;
|
pub mod ast;
|
||||||
pub use self::ast::*;
|
pub use self::ast::*;
|
||||||
|
|
||||||
/// Contains the AST error definitions.
|
/// Contains the CLI error definitions.
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
pub use self::cli::*;
|
pub use self::cli::*;
|
||||||
|
|
||||||
/// Contains the AST error definitions.
|
/// Contains the Compiler error definitions.
|
||||||
pub mod compiler;
|
pub mod compiler;
|
||||||
pub use self::compiler::*;
|
pub use self::compiler::*;
|
||||||
|
|
||||||
|
@ -44,12 +44,11 @@ pub struct CLI {
|
|||||||
///Leo compiler and package manager
|
///Leo compiler and package manager
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
enum Commands {
|
enum Commands {
|
||||||
// #[clap(about = "Create a new Leo package in an existing directory")]
|
#[clap(about = "Create a new Aleo account")]
|
||||||
// Init {
|
Account {
|
||||||
// #[clap(flatten)]
|
#[clap(subcommand)]
|
||||||
// command: Init,
|
command: Account,
|
||||||
// },
|
},
|
||||||
//
|
|
||||||
#[clap(about = "Create a new Leo package in a new directory")]
|
#[clap(about = "Create a new Leo package in a new directory")]
|
||||||
New {
|
New {
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
@ -80,13 +79,6 @@ enum Commands {
|
|||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
command: Update,
|
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 {
|
pub fn handle_error<T>(res: Result<T>) -> T {
|
||||||
@ -114,6 +106,7 @@ pub fn run_with_args(cli: CLI) -> Result<()> {
|
|||||||
let context = handle_error(Context::new(cli.path));
|
let context = handle_error(Context::new(cli.path));
|
||||||
|
|
||||||
match cli.command {
|
match cli.command {
|
||||||
|
Commands::Account { command } => command.try_execute(context),
|
||||||
Commands::New { command } => command.try_execute(context),
|
Commands::New { command } => command.try_execute(context),
|
||||||
Commands::Build { command } => {
|
Commands::Build { command } => {
|
||||||
// Enter tracing span
|
// Enter tracing span
|
||||||
@ -135,7 +128,5 @@ pub fn run_with_args(cli: CLI) -> Result<()> {
|
|||||||
Commands::Run { command } => command.try_execute(context),
|
Commands::Run { command } => command.try_execute(context),
|
||||||
Commands::Execute { command } => command.try_execute(context),
|
Commands::Execute { command } => command.try_execute(context),
|
||||||
Commands::Update { 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
leo/cli/commands/account.rs
Normal file
117
leo/cli/commands/account.rs
Normal file
@ -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(())
|
||||||
|
}
|
@ -14,6 +14,9 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// 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/>.
|
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
pub mod account;
|
||||||
|
pub use account::Account;
|
||||||
|
|
||||||
pub mod build;
|
pub mod build;
|
||||||
pub use build::Build;
|
pub use build::Build;
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ impl<N: Network> Package<N> {
|
|||||||
// Verify that the .env file does not exist.
|
// Verify that the .env file does not exist.
|
||||||
if !Env::<N>::exists_at(path) {
|
if !Env::<N>::exists_at(path) {
|
||||||
// Create the .env file.
|
// Create the .env file.
|
||||||
Env::<N>::new().write_to(path)?;
|
Env::<N>::new()?.write_to(path)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the source directory.
|
// Create the source directory.
|
||||||
|
@ -25,12 +25,17 @@ pub static ENV_FILENAME: &str = ".env";
|
|||||||
|
|
||||||
#[derive(Deserialize, Default)]
|
#[derive(Deserialize, Default)]
|
||||||
pub struct Env<N: Network> {
|
pub struct Env<N: Network> {
|
||||||
|
data: String,
|
||||||
_phantom: PhantomData<N>,
|
_phantom: PhantomData<N>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: Network> Env<N> {
|
impl<N: Network> Env<N> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Result<Self> {
|
||||||
Self { _phantom: PhantomData }
|
Ok(Self { data: Self::template()?, _phantom: PhantomData })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from(data: String) -> Self {
|
||||||
|
Self { data, _phantom: PhantomData }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exists_at(path: &Path) -> bool {
|
pub fn exists_at(path: &Path) -> bool {
|
||||||
@ -48,11 +53,11 @@ impl<N: Network> Env<N> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut file = File::create(&path).map_err(PackageError::io_error_env_file)?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn template(&self) -> Result<String> {
|
fn template() -> Result<String> {
|
||||||
// Initialize an RNG.
|
// Initialize an RNG.
|
||||||
let rng = &mut rand::thread_rng();
|
let rng = &mut rand::thread_rng();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user