mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-09-11 05:45:30 +03:00
WIP
This commit is contained in:
parent
48f135cb7c
commit
2cc2177158
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1888,6 +1888,7 @@ dependencies = [
|
||||
"leo-package",
|
||||
"leo-parser",
|
||||
"leo-passes",
|
||||
"leo-retriever",
|
||||
"leo-span",
|
||||
"leo-test-framework",
|
||||
"rand",
|
||||
|
@ -51,12 +51,15 @@ features = [ ]
|
||||
[dev-dependencies.leo-disassembler]
|
||||
path = "../../utils/disassembler"
|
||||
|
||||
[dev-dependencies.leo-test-framework]
|
||||
path = "../../tests/test-framework"
|
||||
|
||||
[dev-dependencies.leo-package]
|
||||
path = "../../leo/package"
|
||||
|
||||
[dev-dependencies.leo-retriever]
|
||||
path = "../../utils/retriever"
|
||||
|
||||
[dev-dependencies.leo-test-framework]
|
||||
path = "../../tests/test-framework"
|
||||
|
||||
[dev-dependencies.aleo-std-storage]
|
||||
version = "0.1.7"
|
||||
default-features = false
|
||||
|
@ -27,6 +27,7 @@ use leo_errors::{
|
||||
LeoWarning,
|
||||
};
|
||||
use leo_package::root::env::Env;
|
||||
use leo_retriever::NetworkName;
|
||||
use leo_span::source_map::FileName;
|
||||
use leo_test_framework::{test::TestConfig, Test};
|
||||
|
||||
@ -136,9 +137,16 @@ pub fn setup_build_directory(
|
||||
// Create the manifest file.
|
||||
let _manifest_file = Manifest::create(&directory, &program_id).unwrap();
|
||||
|
||||
// Get the network name.
|
||||
let network = match CurrentNetwork::ID {
|
||||
MainnetV0::ID => NetworkName::MainnetV0,
|
||||
TestnetV0::ID => NetworkName::TestnetV0,
|
||||
_ => panic!("Invalid network"),
|
||||
};
|
||||
|
||||
// Create the environment file.
|
||||
Env::<CurrentNetwork>::new().unwrap().write_to(&directory).unwrap();
|
||||
if Env::<CurrentNetwork>::exists_at(&directory) {
|
||||
Env::new(network).unwrap().write_to(&directory).unwrap();
|
||||
if Env::exists_at(&directory) {
|
||||
println!(".env file created at {:?}", &directory);
|
||||
}
|
||||
|
||||
|
@ -26,8 +26,6 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
type CurrentNetwork = snarkvm::prelude::MainnetV0;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[clap(name = "leo parser", about = "Parse Leo AST and store it as a JSON")]
|
||||
struct Opt {
|
||||
@ -40,6 +38,10 @@ struct Opt {
|
||||
/// Whether to print result to STDOUT.
|
||||
#[clap(short, long)]
|
||||
print_stdout: bool,
|
||||
|
||||
/// The network to use. Defaults to mainnet.
|
||||
#[clap(long, default_value = "mainnet")]
|
||||
pub(crate) network: String,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), String> {
|
||||
@ -50,7 +52,15 @@ fn main() -> Result<(), String> {
|
||||
|
||||
Handler::with(|h| {
|
||||
let node_builder = NodeBuilder::default();
|
||||
let ast = leo_parser::parse_ast::<CurrentNetwork>(h, &node_builder, &code.src, code.start_pos)?;
|
||||
let ast = match opt.network.as_str() {
|
||||
"mainnet" => {
|
||||
leo_parser::parse_ast::<snarkvm::prelude::MainnetV0>(h, &node_builder, &code.src, code.start_pos)
|
||||
}
|
||||
"testnet" => {
|
||||
leo_parser::parse_ast::<snarkvm::prelude::TestnetV0>(h, &node_builder, &code.src, code.start_pos)
|
||||
}
|
||||
_ => panic!("Invalid network"),
|
||||
}?;
|
||||
let json = Ast::to_json_string(&ast)?;
|
||||
println!("{json}");
|
||||
Ok(json)
|
||||
|
@ -222,4 +222,11 @@ create_messages!(
|
||||
msg: "Cannot combine recursive deploy with private fee.".to_string(),
|
||||
help: None,
|
||||
}
|
||||
|
||||
@backtraced
|
||||
invalid_network_name {
|
||||
args: (network: impl Display),
|
||||
msg: format!("Invalid network name: {network}"),
|
||||
help: Some("Valid network names are `testnet` and `mainnet`.".to_string()),
|
||||
}
|
||||
);
|
||||
|
@ -314,6 +314,8 @@ mod test_helpers {
|
||||
use leo_span::symbol::create_session_if_not_set_then;
|
||||
use std::path::Path;
|
||||
|
||||
const NETWORK: &str = "mainnet";
|
||||
|
||||
pub(crate) fn sample_nested_package(temp_dir: &Path) {
|
||||
let name = "nested";
|
||||
|
||||
@ -327,7 +329,7 @@ mod test_helpers {
|
||||
let new = CLI {
|
||||
debug: false,
|
||||
quiet: false,
|
||||
command: Commands::New { command: New { name: name.to_string() } },
|
||||
command: Commands::New { command: New { name: name.to_string(), network: "mainnet".to_string() } },
|
||||
path: Some(project_directory.clone()),
|
||||
home: None,
|
||||
};
|
||||
@ -428,7 +430,7 @@ function external_nested_function:
|
||||
let create_grandparent_project = CLI {
|
||||
debug: false,
|
||||
quiet: false,
|
||||
command: Commands::New { command: New { name: "grandparent".to_string() } },
|
||||
command: Commands::New { command: New { name: "grandparent".to_string(), network: "mainnet".to_string() } },
|
||||
path: Some(grandparent_directory.clone()),
|
||||
home: None,
|
||||
};
|
||||
@ -436,7 +438,7 @@ function external_nested_function:
|
||||
let create_parent_project = CLI {
|
||||
debug: false,
|
||||
quiet: false,
|
||||
command: Commands::New { command: New { name: "parent".to_string() } },
|
||||
command: Commands::New { command: New { name: "parent".to_string(), network: "mainnet".to_string() } },
|
||||
path: Some(parent_directory.clone()),
|
||||
home: None,
|
||||
};
|
||||
@ -444,7 +446,7 @@ function external_nested_function:
|
||||
let create_child_project = CLI {
|
||||
debug: false,
|
||||
quiet: false,
|
||||
command: Commands::New { command: New { name: "child".to_string() } },
|
||||
command: Commands::New { command: New { name: "child".to_string(), network: "mainnet".to_string() } },
|
||||
path: Some(child_directory.clone()),
|
||||
home: None,
|
||||
};
|
||||
@ -559,7 +561,7 @@ program child.aleo {
|
||||
let create_outer_project = CLI {
|
||||
debug: false,
|
||||
quiet: false,
|
||||
command: Commands::New { command: New { name: "outer".to_string() } },
|
||||
command: Commands::New { command: New { name: "outer".to_string(), network: "mainnet".to_string() } },
|
||||
path: Some(outer_directory.clone()),
|
||||
home: None,
|
||||
};
|
||||
@ -567,7 +569,7 @@ program child.aleo {
|
||||
let create_inner_1_project = CLI {
|
||||
debug: false,
|
||||
quiet: false,
|
||||
command: Commands::New { command: New { name: "inner_1".to_string() } },
|
||||
command: Commands::New { command: New { name: "inner_1".to_string(), network: "mainnet".to_string() } },
|
||||
path: Some(inner_1_directory.clone()),
|
||||
home: None,
|
||||
};
|
||||
@ -575,7 +577,7 @@ program child.aleo {
|
||||
let create_inner_2_project = CLI {
|
||||
debug: false,
|
||||
quiet: false,
|
||||
command: Commands::New { command: New { name: "inner_2".to_string() } },
|
||||
command: Commands::New { command: New { name: "inner_2".to_string(), network: "mainnet".to_string() } },
|
||||
path: Some(inner_2_directory.clone()),
|
||||
home: None,
|
||||
};
|
||||
@ -697,7 +699,7 @@ program outer.aleo {
|
||||
let create_outer_project = CLI {
|
||||
debug: false,
|
||||
quiet: false,
|
||||
command: Commands::New { command: New { name: "outer_2".to_string() } },
|
||||
command: Commands::New { command: New { name: "outer_2".to_string(), network: "mainnet".to_string() } },
|
||||
path: Some(outer_directory.clone()),
|
||||
home: None,
|
||||
};
|
||||
@ -705,7 +707,7 @@ program outer.aleo {
|
||||
let create_inner_1_project = CLI {
|
||||
debug: false,
|
||||
quiet: false,
|
||||
command: Commands::New { command: New { name: "inner_1".to_string() } },
|
||||
command: Commands::New { command: New { name: "inner_1".to_string(), network: "mainnet".to_string() } },
|
||||
path: Some(inner_1_directory.clone()),
|
||||
home: None,
|
||||
};
|
||||
@ -713,7 +715,7 @@ program outer.aleo {
|
||||
let create_inner_2_project = CLI {
|
||||
debug: false,
|
||||
quiet: false,
|
||||
command: Commands::New { command: New { name: "inner_2".to_string() } },
|
||||
command: Commands::New { command: New { name: "inner_2".to_string(), network: "mainnet".to_string() } },
|
||||
path: Some(inner_2_directory.clone()),
|
||||
home: None,
|
||||
};
|
||||
|
@ -18,14 +18,15 @@ use super::*;
|
||||
use leo_errors::UtilError;
|
||||
use leo_package::root::Env;
|
||||
use snarkvm::{
|
||||
cli::dotenv_private_key,
|
||||
console::program::{Signature, ToFields, Value},
|
||||
prelude::{Address, PrivateKey, ViewKey},
|
||||
};
|
||||
|
||||
use crossterm::ExecutableCommand;
|
||||
use leo_retriever::NetworkName;
|
||||
use rand::SeedableRng;
|
||||
use rand_chacha::ChaChaRng;
|
||||
use snarkvm::prelude::{MainnetV0, Network, TestnetV0};
|
||||
use std::{
|
||||
io::{self, Read, Write},
|
||||
path::PathBuf,
|
||||
@ -46,23 +47,27 @@ pub enum Account {
|
||||
/// Print sensitive information (such as private key) discreetly to an alternate screen
|
||||
#[clap(long)]
|
||||
discreet: bool,
|
||||
#[clap(short = 'n', long, help = "Name of the network to use", default_value = "mainnet")]
|
||||
network: String,
|
||||
},
|
||||
/// Derive an Aleo account from a private key.
|
||||
Import {
|
||||
/// Private key plaintext
|
||||
private_key: Option<PrivateKey<CurrentNetwork>>,
|
||||
private_key: Option<String>,
|
||||
/// Write the private key to the .env file.
|
||||
#[clap(short = 'w', long)]
|
||||
write: bool,
|
||||
/// Print sensitive information (such as private key) discreetly to an alternate screen
|
||||
#[clap(long)]
|
||||
discreet: bool,
|
||||
#[clap(short = 'n', long, help = "Name of the network to use", default_value = "mainnet")]
|
||||
network: String,
|
||||
},
|
||||
/// Sign a message using your Aleo private key.
|
||||
Sign {
|
||||
/// Specify the account private key of the node
|
||||
#[clap(long = "private-key")]
|
||||
private_key: Option<PrivateKey<CurrentNetwork>>,
|
||||
private_key: Option<String>,
|
||||
/// Specify the path to a file containing the account private key of the node
|
||||
#[clap(long = "private-key-file")]
|
||||
private_key_file: Option<String>,
|
||||
@ -75,12 +80,14 @@ pub enum Account {
|
||||
/// When enabled, parses the message as bytes instead of Aleo literals
|
||||
#[clap(short = 'r', long)]
|
||||
raw: bool,
|
||||
#[clap(short = 'n', long, help = "Name of the network to use", default_value = "mainnet")]
|
||||
network: String,
|
||||
},
|
||||
/// Verify a message from an Aleo address.
|
||||
Verify {
|
||||
/// Address to use for verification
|
||||
#[clap(short = 'a', long)]
|
||||
address: Address<CurrentNetwork>,
|
||||
address: String,
|
||||
/// Signature to verify
|
||||
#[clap(short = 's', long)]
|
||||
signature: String,
|
||||
@ -90,6 +97,8 @@ pub enum Account {
|
||||
/// When enabled, parses the message as bytes instead of Aleo literals
|
||||
#[clap(short = 'r', long)]
|
||||
raw: bool,
|
||||
#[clap(short = 'n', long, help = "Name of the network to use", default_value = "mainnet")]
|
||||
network: String,
|
||||
},
|
||||
}
|
||||
|
||||
@ -109,79 +118,43 @@ impl Command for Account {
|
||||
Self: Sized,
|
||||
{
|
||||
match self {
|
||||
Account::New { seed, write, discreet } => {
|
||||
// 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, discreet)?;
|
||||
|
||||
// Save key data to .env file.
|
||||
if write {
|
||||
write_to_env_file(private_key, &ctx)?;
|
||||
}
|
||||
Account::New { seed, write, discreet, network } => {
|
||||
// Parse the network.
|
||||
let network = NetworkName::try_from(network.as_str())?;
|
||||
match network {
|
||||
NetworkName::MainnetV0 => generate_new_account::<MainnetV0>(seed, write, discreet, &ctx),
|
||||
NetworkName::TestnetV0 => generate_new_account::<TestnetV0>(seed, write, discreet, &ctx),
|
||||
}?
|
||||
}
|
||||
Account::Import { private_key, write, discreet } => {
|
||||
let priv_key = match discreet {
|
||||
true => {
|
||||
let private_key_input = rpassword::prompt_password("Please enter your private key: ").unwrap();
|
||||
snarkvm::prelude::FromStr::from_str(&private_key_input)
|
||||
.map_err(CliError::failed_to_parse_private_key)?
|
||||
}
|
||||
false => match private_key {
|
||||
Some(private_key) => private_key,
|
||||
None => {
|
||||
return Err(CliError::failed_to_execute_account(
|
||||
"PRIVATE_KEY shouldn't be empty when --discreet is false",
|
||||
)
|
||||
.into());
|
||||
}
|
||||
},
|
||||
};
|
||||
// Derive the view key and address and print to stdout.
|
||||
print_keys(priv_key, discreet)?;
|
||||
|
||||
// Save key data to .env file.
|
||||
if write {
|
||||
write_to_env_file(priv_key, &ctx)?;
|
||||
}
|
||||
Account::Import { private_key, write, discreet, network } => {
|
||||
// Parse the network.
|
||||
let network = NetworkName::try_from(network.as_str())?;
|
||||
match network {
|
||||
NetworkName::MainnetV0 => import_account::<MainnetV0>(private_key, write, discreet, &ctx),
|
||||
NetworkName::TestnetV0 => import_account::<TestnetV0>(private_key, write, discreet, &ctx),
|
||||
}?
|
||||
}
|
||||
Self::Sign { message, seed, raw, private_key, private_key_file } => {
|
||||
let key = match (private_key, private_key_file) {
|
||||
(Some(private_key), None) => private_key,
|
||||
(None, Some(private_key_file)) => {
|
||||
let path = private_key_file
|
||||
.parse::<PathBuf>()
|
||||
.map_err(|e| CliError::cli_invalid_input(format!("invalid path - {e}")))?;
|
||||
let key_str = std::fs::read_to_string(path).map_err(UtilError::failed_to_read_file)?;
|
||||
PrivateKey::<CurrentNetwork>::from_str(key_str.trim())
|
||||
.map_err(|e| CliError::cli_invalid_input(format!("could not parse private key: {e}")))?
|
||||
Self::Sign { message, seed, raw, private_key, private_key_file, network } => {
|
||||
// Parse the network.
|
||||
let network = NetworkName::try_from(network.as_str())?;
|
||||
let result = match network {
|
||||
NetworkName::MainnetV0 => {
|
||||
sign_message::<MainnetV0>(message, seed, raw, private_key, private_key_file)
|
||||
}
|
||||
(None, None) => {
|
||||
// Attempt to pull private key from env, then .env file
|
||||
match dotenvy::var("PRIVATE_KEY")
|
||||
.map_or_else(|_| dotenv_private_key(), |key| PrivateKey::<CurrentNetwork>::from_str(&key))
|
||||
{
|
||||
Ok(key) => key,
|
||||
Err(_) => Err(CliError::cli_invalid_input(
|
||||
"missing the '--private-key', '--private-key-file', PRIVATE_KEY env, or .env",
|
||||
))?,
|
||||
}
|
||||
NetworkName::TestnetV0 => {
|
||||
sign_message::<TestnetV0>(message, seed, raw, private_key, private_key_file)
|
||||
}
|
||||
(Some(_), Some(_)) => Err(CliError::cli_invalid_input(
|
||||
"cannot specify both the '--private-key' and '--private-key-file' flags",
|
||||
))?,
|
||||
};
|
||||
println!("{}", sign_message(key, message, seed, raw)?);
|
||||
}?;
|
||||
println!("{result}")
|
||||
}
|
||||
Self::Verify { address, signature, message, raw } => {
|
||||
println!("{}", verify_message(address, signature, message, raw)?)
|
||||
Self::Verify { address, signature, message, raw, network } => {
|
||||
// Parse the network.
|
||||
let network = NetworkName::try_from(network.as_str())?;
|
||||
let result = match network {
|
||||
NetworkName::MainnetV0 => verify_message::<MainnetV0>(address, signature, message, raw),
|
||||
NetworkName::TestnetV0 => verify_message::<TestnetV0>(address, signature, message, raw),
|
||||
}?;
|
||||
println!("{result}")
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@ -190,10 +163,60 @@ impl Command for Account {
|
||||
|
||||
// Helper functions
|
||||
|
||||
// Generate a new account.
|
||||
fn generate_new_account<N: Network>(seed: Option<u64>, write: bool, discreet: bool, ctx: &Context) -> Result<()> {
|
||||
// Sample a new Aleo account.
|
||||
let private_key = match seed {
|
||||
// Recover the field element deterministically.
|
||||
Some(seed) => PrivateKey::<N>::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, discreet)?;
|
||||
|
||||
// Save key data to .env file.
|
||||
if write {
|
||||
write_to_env_file(private_key, ctx)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Import an account.
|
||||
fn import_account<N: Network>(private_key: Option<String>, write: bool, discreet: bool, ctx: &Context) -> Result<()> {
|
||||
let priv_key = match discreet {
|
||||
true => {
|
||||
let private_key_input = rpassword::prompt_password("Please enter your private key: ").unwrap();
|
||||
FromStr::from_str(&private_key_input).map_err(CliError::failed_to_parse_private_key)?
|
||||
}
|
||||
false => match private_key {
|
||||
Some(private_key) => FromStr::from_str(&private_key).map_err(CliError::failed_to_parse_private_key)?,
|
||||
None => {
|
||||
return Err(CliError::failed_to_execute_account(
|
||||
"PRIVATE_KEY shouldn't be empty when --discreet is false",
|
||||
)
|
||||
.into());
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Derive the view key and address and print to stdout.
|
||||
print_keys::<N>(priv_key, discreet)?;
|
||||
|
||||
// Save key data to .env file.
|
||||
if write {
|
||||
write_to_env_file::<N>(priv_key, ctx)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Print keys as a formatted string without log level.
|
||||
fn print_keys(private_key: PrivateKey<CurrentNetwork>, discreet: bool) -> Result<()> {
|
||||
fn print_keys<N: Network>(private_key: PrivateKey<N>, discreet: bool) -> Result<()> {
|
||||
let view_key = ViewKey::try_from(&private_key)?;
|
||||
let address = Address::<CurrentNetwork>::try_from(&view_key)?;
|
||||
let address = Address::<N>::try_from(&view_key)?;
|
||||
|
||||
if !discreet {
|
||||
println!(
|
||||
@ -209,14 +232,101 @@ fn print_keys(private_key: PrivateKey<CurrentNetwork>, discreet: bool) -> Result
|
||||
"### Do not share or lose this private key! Press any key to complete. ###",
|
||||
)?;
|
||||
println!("\n {:>12} {view_key}\n {:>12} {address}\n", "View Key".cyan().bold(), "Address".cyan().bold(),);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Sign a message with an Aleo private key
|
||||
pub(crate) fn sign_message<N: Network>(
|
||||
message: String,
|
||||
seed: Option<u64>,
|
||||
raw: bool,
|
||||
private_key: Option<String>,
|
||||
private_key_file: Option<String>,
|
||||
) -> Result<String> {
|
||||
let private_key = match (private_key, private_key_file) {
|
||||
(Some(private_key), None) => PrivateKey::<N>::from_str(private_key.trim())
|
||||
.map_err(|e| CliError::cli_invalid_input(format!("could not parse private key: {e}")))?,
|
||||
(None, Some(private_key_file)) => {
|
||||
let path = private_key_file
|
||||
.parse::<PathBuf>()
|
||||
.map_err(|e| CliError::cli_invalid_input(format!("invalid path - {e}")))?;
|
||||
let key_str = std::fs::read_to_string(path).map_err(UtilError::failed_to_read_file)?;
|
||||
PrivateKey::<N>::from_str(key_str.trim())
|
||||
.map_err(|e| CliError::cli_invalid_input(format!("could not parse private key: {e}")))?
|
||||
}
|
||||
(None, None) => {
|
||||
// Attempt to pull private key from env, then .env file
|
||||
match dotenvy::var("PRIVATE_KEY") {
|
||||
Ok(key) => PrivateKey::<N>::from_str(key.trim())
|
||||
.map_err(|e| CliError::cli_invalid_input(format!("could not parse private key: {e}")))?,
|
||||
Err(_) => Err(CliError::cli_invalid_input(
|
||||
"missing the '--private-key', '--private-key-file', PRIVATE_KEY env, or .env",
|
||||
))?,
|
||||
}
|
||||
}
|
||||
(Some(_), Some(_)) => {
|
||||
Err(CliError::cli_invalid_input("cannot specify both the '--private-key' and '--private-key-file' flags"))?
|
||||
}
|
||||
};
|
||||
// Recover the seed.
|
||||
let mut rng = match seed {
|
||||
// Recover the field element deterministically.
|
||||
Some(seed) => ChaChaRng::seed_from_u64(seed),
|
||||
// Sample a random field element.
|
||||
None => ChaChaRng::from_entropy(),
|
||||
};
|
||||
|
||||
// Sign the message
|
||||
let signature = if raw {
|
||||
private_key.sign_bytes(message.as_bytes(), &mut rng)
|
||||
} else {
|
||||
let fields = Value::<N>::from_str(&message)?
|
||||
.to_fields()
|
||||
.map_err(|_| CliError::cli_invalid_input("Failed to parse a valid Aleo value"))?;
|
||||
private_key.sign(&fields, &mut rng)
|
||||
}
|
||||
.map_err(|_| CliError::cli_runtime_error("Failed to sign the message"))?
|
||||
.to_string();
|
||||
// Return the signature as a string
|
||||
Ok(signature)
|
||||
}
|
||||
|
||||
// Verify a signature with an Aleo address
|
||||
pub(crate) fn verify_message<N: Network>(
|
||||
address: String,
|
||||
signature: String,
|
||||
message: String,
|
||||
raw: bool,
|
||||
) -> Result<String> {
|
||||
// Parse the address.
|
||||
let address = Address::<N>::from_str(&address)?;
|
||||
|
||||
let signature = Signature::<N>::from_str(&signature)
|
||||
.map_err(|e| CliError::cli_invalid_input(format!("Failed to parse a valid signature: {e}")))?;
|
||||
|
||||
// Verify the signature
|
||||
let verified = if raw {
|
||||
signature.verify_bytes(&address, message.as_bytes())
|
||||
} else {
|
||||
let fields = Value::<N>::from_str(&message)?
|
||||
.to_fields()
|
||||
.map_err(|_| CliError::cli_invalid_input("Failed to parse a valid Aleo value"))?;
|
||||
signature.verify(&address, &fields)
|
||||
};
|
||||
|
||||
// Return the verification result
|
||||
match verified {
|
||||
true => Ok("✅ The signature is valid".to_string()),
|
||||
false => Err(CliError::cli_runtime_error("❌ The signature is invalid"))?,
|
||||
}
|
||||
}
|
||||
|
||||
// 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=mainnet\nPRIVATE_KEY={private_key}\n");
|
||||
fn write_to_env_file<N: Network>(private_key: PrivateKey<N>, ctx: &Context) -> Result<()> {
|
||||
let data = format!("PRIVATE_KEY={private_key}\n");
|
||||
let program_dir = ctx.dir()?;
|
||||
Env::<CurrentNetwork>::from(data).write_to(&program_dir)?;
|
||||
Env::from(data).write_to(&program_dir)?;
|
||||
tracing::info!("✅ Private Key written to {}", program_dir.join(".env").display());
|
||||
Ok(())
|
||||
}
|
||||
@ -242,86 +352,31 @@ fn wait_for_keypress() {
|
||||
std::io::stdin().read_exact(&mut single_key).unwrap();
|
||||
}
|
||||
|
||||
// Sign a message with an Aleo private key
|
||||
pub(crate) fn sign_message(
|
||||
private_key: PrivateKey<CurrentNetwork>,
|
||||
message: String,
|
||||
seed: Option<u64>,
|
||||
raw: bool,
|
||||
) -> Result<String> {
|
||||
// Recover the seed.
|
||||
let mut rng = match seed {
|
||||
// Recover the field element deterministically.
|
||||
Some(seed) => ChaChaRng::seed_from_u64(seed),
|
||||
// Sample a random field element.
|
||||
None => ChaChaRng::from_entropy(),
|
||||
};
|
||||
|
||||
// Sign the message
|
||||
let signature = if raw {
|
||||
private_key.sign_bytes(message.as_bytes(), &mut rng)
|
||||
} else {
|
||||
let fields = Value::<CurrentNetwork>::from_str(&message)?
|
||||
.to_fields()
|
||||
.map_err(|_| CliError::cli_invalid_input("Failed to parse a valid Aleo value"))?;
|
||||
private_key.sign(&fields, &mut rng)
|
||||
}
|
||||
.map_err(|_| CliError::cli_runtime_error("Failed to sign the message"))?
|
||||
.to_string();
|
||||
// Return the signature as a string
|
||||
Ok(signature)
|
||||
}
|
||||
|
||||
// Verify a signature with an Aleo address
|
||||
pub(crate) fn verify_message(
|
||||
address: Address<CurrentNetwork>,
|
||||
signature: String,
|
||||
message: String,
|
||||
raw: bool,
|
||||
) -> Result<String> {
|
||||
let signature = Signature::<CurrentNetwork>::from_str(&signature)
|
||||
.map_err(|e| CliError::cli_invalid_input(format!("Failed to parse a valid signature: {e}")))?;
|
||||
|
||||
// Verify the signature
|
||||
let verified = if raw {
|
||||
signature.verify_bytes(&address, message.as_bytes())
|
||||
} else {
|
||||
let fields = Value::<CurrentNetwork>::from_str(&message)?
|
||||
.to_fields()
|
||||
.map_err(|_| CliError::cli_invalid_input("Failed to parse a valid Aleo value"))?;
|
||||
signature.verify(&address, &fields)
|
||||
};
|
||||
|
||||
// Return the verification result
|
||||
match verified {
|
||||
true => Ok("✅ The signature is valid".to_string()),
|
||||
false => Err(CliError::cli_runtime_error("❌ The signature is invalid"))?,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{sign_message, verify_message};
|
||||
|
||||
type CurrentNetwork = snarkvm::prelude::MainnetV0;
|
||||
|
||||
#[test]
|
||||
fn test_signature_raw() {
|
||||
let key = "APrivateKey1zkp61PAYmrYEKLtRWeWhUoDpFnGLNuHrCciSqN49T86dw3p".parse().unwrap();
|
||||
let message = "Hello, world!".to_string();
|
||||
assert!(sign_message(key, message, None, true).is_ok());
|
||||
assert!(sign_message::<CurrentNetwork>(message, None, true, Some(key), None).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signature() {
|
||||
let key = "APrivateKey1zkp61PAYmrYEKLtRWeWhUoDpFnGLNuHrCciSqN49T86dw3p".parse().unwrap();
|
||||
let message = "5field".to_string();
|
||||
assert!(sign_message(key, message, None, false).is_ok());
|
||||
assert!(sign_message::<CurrentNetwork>(message, None, false, Some(key), None).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signature_fail() {
|
||||
let key = "APrivateKey1zkp61PAYmrYEKLtRWeWhUoDpFnGLNuHrCciSqN49T86dw3p".parse().unwrap();
|
||||
let message = "not a literal value".to_string();
|
||||
assert!(sign_message(key, message, None, false).is_err());
|
||||
assert!(sign_message::<CurrentNetwork>(message, None, false, Some(key), None).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -330,7 +385,7 @@ mod tests {
|
||||
let key = "APrivateKey1zkp61PAYmrYEKLtRWeWhUoDpFnGLNuHrCciSqN49T86dw3p".parse().unwrap();
|
||||
let message = "Hello, world!".to_string();
|
||||
let expected = "sign175pmqldmkqw2nwp7wz7tfmpyqdnvzaq06mh8t2g22frsmrdtuvpf843p0wzazg27rwrjft8863vwn5a5cqgr97ldw69cyq53l0zlwqhesm5elrqqunzqzmac7kzutl6zk7mqht3c0m9kg4hklv7h2js0qmxavwnpuwyl4lzldl6prs4qeqy9wxyp8y44nnydg3h8sg6ue99qkevd26g";
|
||||
let actual = sign_message(key, message, seed, true).unwrap();
|
||||
let actual = sign_message::<CurrentNetwork>(message, seed, true, Some(key), None).unwrap();
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
@ -340,7 +395,7 @@ mod tests {
|
||||
let key = "APrivateKey1zkp61PAYmrYEKLtRWeWhUoDpFnGLNuHrCciSqN49T86dw3p".parse().unwrap();
|
||||
let message = "5field".to_string();
|
||||
let expected = "sign1ad29myqy8gv6xve2r6tuly39m63l2mpfpyvqkwdl2umxqek6q5qxmy63zmhjx75x90sqxq69u5ntzp25kp59e0hp4hj8l8085sg7vqlesm5elrqqunzqzmac7kzutl6zk7mqht3c0m9kg4hklv7h2js0qmxavwnpuwyl4lzldl6prs4qeqy9wxyp8y44nnydg3h8sg6ue99qk7v46re";
|
||||
let actual = sign_message(key, message, seed, false).unwrap();
|
||||
let actual = sign_message::<CurrentNetwork>(message, seed, false, Some(key), None).unwrap();
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
@ -350,23 +405,23 @@ mod tests {
|
||||
let address = "aleo1zecnqchckrzw7dlsyf65g6z5le2rmys403ecwmcafrag0e030yxqrnlg8j".parse().unwrap();
|
||||
let signature = "sign1nnvrjlksrkxdpwsrw8kztjukzhmuhe5zf3srk38h7g32u4kqtqpxn3j5a6k8zrqcfx580a96956nsjvluzt64cqf54pdka9mgksfqp8esm5elrqqunzqzmac7kzutl6zk7mqht3c0m9kg4hklv7h2js0qmxavwnpuwyl4lzldl6prs4qeqy9wxyp8y44nnydg3h8sg6ue99qkwsnaqq".to_string();
|
||||
let message = "Hello, world!".to_string();
|
||||
assert!(verify_message(address, signature, message, true).is_ok());
|
||||
assert!(verify_message::<CurrentNetwork>(address, signature, message, true).is_ok());
|
||||
|
||||
// test signature of "Hello, world!" against the message "Different Message"
|
||||
let signature = "sign1nnvrjlksrkxdpwsrw8kztjukzhmuhe5zf3srk38h7g32u4kqtqpxn3j5a6k8zrqcfx580a96956nsjvluzt64cqf54pdka9mgksfqp8esm5elrqqunzqzmac7kzutl6zk7mqht3c0m9kg4hklv7h2js0qmxavwnpuwyl4lzldl6prs4qeqy9wxyp8y44nnydg3h8sg6ue99qkwsnaqq".to_string();
|
||||
let message = "Different Message".to_string();
|
||||
assert!(verify_message(address, signature, message, true).is_err());
|
||||
assert!(verify_message::<CurrentNetwork>(address, signature, message, true).is_err());
|
||||
|
||||
// test signature of "Hello, world!" against the wrong address
|
||||
let signature = "sign1nnvrjlksrkxdpwsrw8kztjukzhmuhe5zf3srk38h7g32u4kqtqpxn3j5a6k8zrqcfx580a96956nsjvluzt64cqf54pdka9mgksfqp8esm5elrqqunzqzmac7kzutl6zk7mqht3c0m9kg4hklv7h2js0qmxavwnpuwyl4lzldl6prs4qeqy9wxyp8y44nnydg3h8sg6ue99qkwsnaqq".to_string();
|
||||
let message = "Hello, world!".to_string();
|
||||
let wrong_address = "aleo1uxl69laseuv3876ksh8k0nd7tvpgjt6ccrgccedpjk9qwyfensxst9ftg5".parse().unwrap();
|
||||
assert!(verify_message(wrong_address, signature, message, true).is_err());
|
||||
assert!(verify_message::<CurrentNetwork>(wrong_address, signature, message, true).is_err());
|
||||
|
||||
// test a valid signature of "Different Message"
|
||||
let signature = "sign1424ztyt9hcm77nq450gvdszrvtg9kvhc4qadg4nzy9y0ah7wdqq7t36cxal42p9jj8e8pjpmc06lfev9nvffcpqv0cxwyr0a2j2tjqlesm5elrqqunzqzmac7kzutl6zk7mqht3c0m9kg4hklv7h2js0qmxavwnpuwyl4lzldl6prs4qeqy9wxyp8y44nnydg3h8sg6ue99qk3yrr50".to_string();
|
||||
let message = "Different Message".to_string();
|
||||
assert!(verify_message(address, signature, message, true).is_ok());
|
||||
assert!(verify_message::<CurrentNetwork>(address, signature, message, true).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -375,22 +430,22 @@ mod tests {
|
||||
let address = "aleo1zecnqchckrzw7dlsyf65g6z5le2rmys403ecwmcafrag0e030yxqrnlg8j".parse().unwrap();
|
||||
let signature = "sign1j7swjfnyujt2vme3ulu88wdyh2ddj85arh64qh6c6khvrx8wvsp8z9wtzde0sahqj2qwz8rgzt803c0ceega53l4hks2mf5sfsv36qhesm5elrqqunzqzmac7kzutl6zk7mqht3c0m9kg4hklv7h2js0qmxavwnpuwyl4lzldl6prs4qeqy9wxyp8y44nnydg3h8sg6ue99qkdetews".to_string();
|
||||
let message = "5field".to_string();
|
||||
assert!(verify_message(address, signature, message, false).is_ok());
|
||||
assert!(verify_message::<CurrentNetwork>(address, signature, message, false).is_ok());
|
||||
|
||||
// test signature of 5u8 against the message 10u8
|
||||
let signature = "sign1j7swjfnyujt2vme3ulu88wdyh2ddj85arh64qh6c6khvrx8wvsp8z9wtzde0sahqj2qwz8rgzt803c0ceega53l4hks2mf5sfsv36qhesm5elrqqunzqzmac7kzutl6zk7mqht3c0m9kg4hklv7h2js0qmxavwnpuwyl4lzldl6prs4qeqy9wxyp8y44nnydg3h8sg6ue99qkdetews".to_string();
|
||||
let message = "10field".to_string();
|
||||
assert!(verify_message(address, signature, message, false).is_err());
|
||||
assert!(verify_message::<CurrentNetwork>(address, signature, message, false).is_err());
|
||||
|
||||
// test signature of 5u8 against the wrong address
|
||||
let signature = "sign1j7swjfnyujt2vme3ulu88wdyh2ddj85arh64qh6c6khvrx8wvsp8z9wtzde0sahqj2qwz8rgzt803c0ceega53l4hks2mf5sfsv36qhesm5elrqqunzqzmac7kzutl6zk7mqht3c0m9kg4hklv7h2js0qmxavwnpuwyl4lzldl6prs4qeqy9wxyp8y44nnydg3h8sg6ue99qkdetews".to_string();
|
||||
let message = "5field".to_string();
|
||||
let wrong_address = "aleo1uxl69laseuv3876ksh8k0nd7tvpgjt6ccrgccedpjk9qwyfensxst9ftg5".parse().unwrap();
|
||||
assert!(verify_message(wrong_address, signature, message, false).is_err());
|
||||
assert!(verify_message::<CurrentNetwork>(wrong_address, signature, message, false).is_err());
|
||||
|
||||
// test a valid signature of 10u8
|
||||
let signature = "sign1t9v2t5tljk8pr5t6vkcqgkus0a3v69vryxmfrtwrwg0xtj7yv5qj2nz59e5zcyl50w23lhntxvt6vzeqfyu6dt56698zvfj2l6lz6q0esm5elrqqunzqzmac7kzutl6zk7mqht3c0m9kg4hklv7h2js0qmxavwnpuwyl4lzldl6prs4qeqy9wxyp8y44nnydg3h8sg6ue99qk8rh9kt".to_string();
|
||||
let message = "10field".to_string();
|
||||
assert!(verify_message(address, signature, message, false).is_ok());
|
||||
assert!(verify_message::<CurrentNetwork>(address, signature, message, false).is_ok());
|
||||
}
|
||||
}
|
||||
|
@ -59,12 +59,10 @@ impl Command for Add {
|
||||
// Make sure the program name is valid.
|
||||
// Allow both `credits.aleo` and `credits` syntax.
|
||||
let name: String = match &self.name {
|
||||
name if name.ends_with(".aleo")
|
||||
&& Package::<CurrentNetwork>::is_aleo_name_valid(&name[0..self.name.len() - 5]) =>
|
||||
{
|
||||
name if name.ends_with(".aleo") && Package::is_aleo_name_valid(&name[0..self.name.len() - 5]) => {
|
||||
name.clone()
|
||||
}
|
||||
name if Package::<CurrentNetwork>::is_aleo_name_valid(name) => format!("{name}.aleo"),
|
||||
name if Package::is_aleo_name_valid(name) => format!("{name}.aleo"),
|
||||
name => return Err(PackageError::invalid_file_name_dependency(name).into()),
|
||||
};
|
||||
|
||||
@ -113,7 +111,7 @@ impl Command for Add {
|
||||
}
|
||||
None => {
|
||||
tracing::info!("✅ Added network dependency to program `{name}` from network `{}`.", self.network);
|
||||
Dependency::new(name, Location::Network, Some(NetworkName::from(&self.network)), None)
|
||||
Dependency::new(name, Location::Network, Some(NetworkName::try_from(self.network.as_str())?), None)
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -20,12 +20,13 @@ use leo_ast::Stub;
|
||||
use leo_compiler::{Compiler, CompilerOptions, OutputOptions};
|
||||
use leo_errors::UtilError;
|
||||
use leo_package::{build::BuildDirectory, outputs::OutputsDirectory, source::SourceDirectory};
|
||||
use leo_retriever::Retriever;
|
||||
use leo_retriever::{NetworkName, Retriever};
|
||||
use leo_span::Symbol;
|
||||
|
||||
use snarkvm::{package::Package, prelude::ProgramID};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use snarkvm::prelude::{MainnetV0, Network, TestnetV0};
|
||||
use std::{
|
||||
io::Write,
|
||||
path::{Path, PathBuf},
|
||||
@ -88,101 +89,92 @@ impl Command for Build {
|
||||
}
|
||||
|
||||
fn apply(self, context: Context, _: Self::Input) -> Result<Self::Output> {
|
||||
// Get the package path.
|
||||
let package_path = context.dir()?;
|
||||
let home_path = context.home()?;
|
||||
// Parse the network.
|
||||
let network = NetworkName::try_from(self.options.network.as_str())?;
|
||||
match network {
|
||||
NetworkName::MainnetV0 => handle_build::<MainnetV0>(&self, context),
|
||||
NetworkName::TestnetV0 => handle_build::<TestnetV0>(&self, context),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Open the build directory.
|
||||
let build_directory = BuildDirectory::create(&package_path)?;
|
||||
// A helper function to handle the build command.
|
||||
fn handle_build<N: Network>(command: &Build, context: Context) -> Result<<Build as Command>::Output> {
|
||||
// Get the package path.
|
||||
let package_path = context.dir()?;
|
||||
let home_path = context.home()?;
|
||||
|
||||
// Get the program id.
|
||||
let manifest = context.open_manifest()?;
|
||||
let program_id = manifest.program_id();
|
||||
// Open the build directory.
|
||||
let build_directory = BuildDirectory::create(&package_path)?;
|
||||
|
||||
// Initialize error handler
|
||||
let handler = Handler::default();
|
||||
// Get the program id.
|
||||
let manifest = context.open_manifest::<N>()?;
|
||||
let program_id = manifest.program_id();
|
||||
|
||||
// Retrieve all local dependencies in post order
|
||||
let main_sym = Symbol::intern(&program_id.name().to_string());
|
||||
let mut retriever =
|
||||
Retriever::<CurrentNetwork>::new(main_sym, &package_path, &home_path, self.options.endpoint.clone())
|
||||
.map_err(|err| UtilError::failed_to_retrieve_dependencies(err, Default::default()))?;
|
||||
let mut local_dependencies =
|
||||
retriever.retrieve().map_err(|err| UtilError::failed_to_retrieve_dependencies(err, Default::default()))?;
|
||||
// Initialize error handler
|
||||
let handler = Handler::default();
|
||||
|
||||
// Push the main program at the end of the list to be compiled after all of its dependencies have been processed
|
||||
local_dependencies.push(main_sym);
|
||||
// Retrieve all local dependencies in post order
|
||||
let main_sym = Symbol::intern(&program_id.name().to_string());
|
||||
let mut retriever = Retriever::<N>::new(main_sym, &package_path, &home_path, command.options.endpoint.clone())
|
||||
.map_err(|err| UtilError::failed_to_retrieve_dependencies(err, Default::default()))?;
|
||||
let mut local_dependencies =
|
||||
retriever.retrieve().map_err(|err| UtilError::failed_to_retrieve_dependencies(err, Default::default()))?;
|
||||
|
||||
// Recursive build will recursively compile all local dependencies. Can disable to save compile time.
|
||||
let recursive_build = !self.options.non_recursive;
|
||||
// Push the main program at the end of the list to be compiled after all of its dependencies have been processed
|
||||
local_dependencies.push(main_sym);
|
||||
|
||||
// Loop through all local dependencies and compile them in order
|
||||
for dependency in local_dependencies.into_iter() {
|
||||
if recursive_build || dependency == main_sym {
|
||||
// Get path to the local project
|
||||
let (local_path, stubs) = retriever.prepare_local(dependency)?;
|
||||
// Recursive build will recursively compile all local dependencies. Can disable to save compile time.
|
||||
let recursive_build = !command.options.non_recursive;
|
||||
|
||||
// Create the outputs directory.
|
||||
let local_outputs_directory = OutputsDirectory::create(&local_path)?;
|
||||
// Loop through all local dependencies and compile them in order
|
||||
for dependency in local_dependencies.into_iter() {
|
||||
if recursive_build || dependency == main_sym {
|
||||
// Get path to the local project
|
||||
let (local_path, stubs) = retriever.prepare_local(dependency)?;
|
||||
|
||||
// Open the build directory.
|
||||
let local_build_directory = BuildDirectory::create(&local_path)?;
|
||||
// Create the outputs directory.
|
||||
let local_outputs_directory = OutputsDirectory::create(&local_path)?;
|
||||
|
||||
// Fetch paths to all .leo files in the source directory.
|
||||
let local_source_files = SourceDirectory::files(&local_path)?;
|
||||
// Open the build directory.
|
||||
let local_build_directory = BuildDirectory::create(&local_path)?;
|
||||
|
||||
// Check the source files.
|
||||
SourceDirectory::check_files(&local_source_files)?;
|
||||
// Fetch paths to all .leo files in the source directory.
|
||||
let local_source_files = SourceDirectory::files(&local_path)?;
|
||||
|
||||
// Compile all .leo files into .aleo files.
|
||||
for file_path in local_source_files {
|
||||
compile_leo_file(
|
||||
file_path,
|
||||
&ProgramID::<CurrentNetwork>::try_from(format!("{}.aleo", dependency))
|
||||
.map_err(|_| UtilError::snarkvm_error_building_program_id(Default::default()))?,
|
||||
&local_outputs_directory,
|
||||
&local_build_directory,
|
||||
&handler,
|
||||
self.options.clone(),
|
||||
stubs.clone(),
|
||||
)?;
|
||||
}
|
||||
// Check the source files.
|
||||
SourceDirectory::check_files(&local_source_files)?;
|
||||
|
||||
// Compile all .leo files into .aleo files.
|
||||
for file_path in local_source_files {
|
||||
compile_leo_file(
|
||||
file_path,
|
||||
&ProgramID::<N>::try_from(format!("{}.aleo", dependency))
|
||||
.map_err(|_| UtilError::snarkvm_error_building_program_id(Default::default()))?,
|
||||
&local_outputs_directory,
|
||||
&local_build_directory,
|
||||
&handler,
|
||||
command.options.clone(),
|
||||
stubs.clone(),
|
||||
)?;
|
||||
}
|
||||
|
||||
// Writes `leo.lock` as well as caches objects (when target is an intermediate dependency)
|
||||
retriever.process_local(dependency, recursive_build)?;
|
||||
}
|
||||
|
||||
// `Package::open` checks that the build directory and that `main.aleo` and all imported files are well-formed.
|
||||
Package::<CurrentNetwork>::open(&build_directory).map_err(CliError::failed_to_execute_build)?;
|
||||
|
||||
// // Unset the Leo panic hook.
|
||||
// let _ = std::panic::take_hook();
|
||||
//
|
||||
// // Change the cwd to the build directory to compile aleo files.
|
||||
// std::env::set_current_dir(&build_directory)
|
||||
// .map_err(|err| PackageError::failed_to_set_cwd(build_directory.display(), err))?;
|
||||
//
|
||||
// // Call the `build` command.
|
||||
// let mut args = vec![SNARKVM_COMMAND];
|
||||
// if self.options.offline {
|
||||
// args.push("--offline");
|
||||
// }
|
||||
// let command = AleoBuild::try_parse_from(&args).map_err(CliError::failed_to_execute_aleo_build)?;
|
||||
// let result = command.parse().map_err(CliError::failed_to_execute_aleo_build)?;
|
||||
//
|
||||
// // Log the result of the build
|
||||
// tracing::info!("{}", result);
|
||||
|
||||
Ok(())
|
||||
// Writes `leo.lock` as well as caches objects (when target is an intermediate dependency)
|
||||
retriever.process_local(dependency, recursive_build)?;
|
||||
}
|
||||
|
||||
// `Package::open` checks that the build directory and that `main.aleo` and all imported files are well-formed.
|
||||
Package::<N>::open(&build_directory).map_err(CliError::failed_to_execute_build)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Compiles a Leo file in the `src/` directory.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn compile_leo_file(
|
||||
fn compile_leo_file<N: Network>(
|
||||
file_path: PathBuf,
|
||||
program_id: &ProgramID<CurrentNetwork>,
|
||||
program_id: &ProgramID<N>,
|
||||
outputs: &Path,
|
||||
build: &Path,
|
||||
handler: &Handler,
|
||||
@ -197,7 +189,7 @@ fn compile_leo_file(
|
||||
aleo_file_path.push(format!("main.{}", program_id.network()));
|
||||
|
||||
// Create a new instance of the Leo compiler.
|
||||
let mut compiler = Compiler::<CurrentNetwork>::new(
|
||||
let mut compiler = Compiler::<N>::new(
|
||||
program_name.clone(),
|
||||
program_id.network().to_string(),
|
||||
handler,
|
||||
|
@ -15,8 +15,12 @@
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use super::*;
|
||||
use leo_retriever::NetworkName;
|
||||
use snarkos_cli::commands::{Deploy as SnarkOSDeploy, Developer};
|
||||
use snarkvm::cli::helpers::dotenv_private_key;
|
||||
use snarkvm::{
|
||||
cli::helpers::dotenv_private_key,
|
||||
prelude::{MainnetV0, TestnetV0},
|
||||
};
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Deploys an Aleo program.
|
||||
@ -54,8 +58,13 @@ impl Command for Deploy {
|
||||
}
|
||||
|
||||
fn apply(self, context: Context, _: Self::Input) -> Result<Self::Output> {
|
||||
// Parse the network.
|
||||
let network = NetworkName::try_from(self.compiler_options.network.as_str())?;
|
||||
// Get the program name.
|
||||
let project_name = context.open_manifest()?.program_id().to_string();
|
||||
let project_name = match network {
|
||||
NetworkName::MainnetV0 => context.open_manifest::<MainnetV0>()?.program_id().to_string(),
|
||||
NetworkName::TestnetV0 => context.open_manifest::<TestnetV0>()?.program_id().to_string(),
|
||||
};
|
||||
|
||||
// Get the private key.
|
||||
let mut private_key = self.fee_options.private_key;
|
||||
@ -91,7 +100,7 @@ impl Command for Deploy {
|
||||
"--path".to_string(),
|
||||
path.to_str().unwrap().parse().unwrap(),
|
||||
"--broadcast".to_string(),
|
||||
format!("{}/{}/transaction/broadcast", self.compiler_options.endpoint, self.fee_options.network)
|
||||
format!("{}/{}/transaction/broadcast", self.compiler_options.endpoint, self.compiler_options.network)
|
||||
.to_string(),
|
||||
name.clone(),
|
||||
];
|
||||
|
@ -18,15 +18,13 @@ use super::*;
|
||||
|
||||
use std::fs;
|
||||
|
||||
/// The example programs that can be generated.
|
||||
/// Initialize a new Leo example.
|
||||
#[derive(Parser, Debug)]
|
||||
pub enum Example {
|
||||
#[clap(name = "lottery", about = "A public lottery program")]
|
||||
Lottery,
|
||||
#[clap(name = "tictactoe", about = "A standard tic-tac-toe game program")]
|
||||
TicTacToe,
|
||||
#[clap(name = "token", about = "A transparent & shielded custom token program")]
|
||||
Token,
|
||||
pub struct Example {
|
||||
#[clap(name = "EXAMPLE", help = "The example to initialize.")]
|
||||
pub(crate) example: ExampleVariant,
|
||||
#[clap(short = 'n', long, help = "Name of the network to use", default_value = "mainnet")]
|
||||
pub(crate) network: String,
|
||||
}
|
||||
|
||||
impl Command for Example {
|
||||
@ -35,7 +33,7 @@ impl Command for Example {
|
||||
|
||||
fn prelude(&self, context: Context) -> Result<Self::Input> {
|
||||
// Run leo new EXAMPLE_NAME
|
||||
(New { name: self.name() }).execute(context)
|
||||
(New { name: self.example.name(), network: self.network }).execute(context)
|
||||
}
|
||||
|
||||
fn apply(self, context: Context, _: Self::Input) -> Result<Self::Output>
|
||||
@ -46,20 +44,20 @@ impl Command for Example {
|
||||
|
||||
// Write the main file.
|
||||
let main_file_path = package_dir.join("src").join("main.leo");
|
||||
fs::write(main_file_path, self.main_file_string()).map_err(CliError::failed_to_write_file)?;
|
||||
fs::write(main_file_path, self.example.main_file_string()).map_err(CliError::failed_to_write_file)?;
|
||||
|
||||
// Write the README file.
|
||||
let readme_file_path = package_dir.join("README.md");
|
||||
let readme_file_path_string = readme_file_path.display().to_string();
|
||||
fs::write(readme_file_path, self.readme_file_string()).map_err(CliError::failed_to_write_file)?;
|
||||
fs::write(readme_file_path, self.example.readme_file_string()).map_err(CliError::failed_to_write_file)?;
|
||||
|
||||
// Write the run.sh file.
|
||||
let run_file_path = package_dir.join("run.sh");
|
||||
fs::write(run_file_path, self.run_file_string()).map_err(CliError::failed_to_write_file)?;
|
||||
fs::write(run_file_path, self.example.run_file_string()).map_err(CliError::failed_to_write_file)?;
|
||||
|
||||
tracing::info!(
|
||||
"🚀 To run the '{}' program follow the instructions at {}",
|
||||
self.name().bold(),
|
||||
self.example.name().bold(),
|
||||
readme_file_path_string
|
||||
);
|
||||
|
||||
@ -67,12 +65,23 @@ impl Command for Example {
|
||||
}
|
||||
}
|
||||
|
||||
impl Example {
|
||||
/// The example programs that can be generated.
|
||||
#[derive(Parser, Debug, Copy, Clone)]
|
||||
pub enum ExampleVariant {
|
||||
#[clap(name = "lottery", about = "A public lottery program")]
|
||||
Lottery,
|
||||
#[clap(name = "tictactoe", about = "A standard tic-tac-toe game program")]
|
||||
TicTacToe,
|
||||
#[clap(name = "token", about = "A transparent & shielded custom token program")]
|
||||
Token,
|
||||
}
|
||||
|
||||
impl ExampleVariant {
|
||||
fn name(&self) -> String {
|
||||
match self {
|
||||
Example::Lottery => "lottery".to_string(),
|
||||
Example::TicTacToe => "tictactoe".to_string(),
|
||||
Example::Token => "token".to_string(),
|
||||
Self::Lottery => "lottery".to_string(),
|
||||
Self::TicTacToe => "tictactoe".to_string(),
|
||||
Self::Token => "token".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,10 +16,11 @@
|
||||
|
||||
use super::*;
|
||||
use clap::Parser;
|
||||
use leo_retriever::NetworkName;
|
||||
use snarkos_cli::commands::{Developer, Execute as SnarkOSExecute};
|
||||
use snarkvm::{
|
||||
cli::{helpers::dotenv_private_key, Execute as SnarkVMExecute},
|
||||
prelude::Parser as SnarkVMParser,
|
||||
prelude::{MainnetV0, Network, Parser as SnarkVMParser, TestnetV0},
|
||||
};
|
||||
|
||||
/// Build, Prove and Run Leo program with inputs
|
||||
@ -60,132 +61,142 @@ impl Command for Execute {
|
||||
}
|
||||
|
||||
fn apply(self, context: Context, _input: Self::Input) -> Result<Self::Output> {
|
||||
// If the `broadcast` flag is set, then broadcast the transaction.
|
||||
if self.broadcast {
|
||||
// Get the program name.
|
||||
let program_name = match (self.program, self.local) {
|
||||
(Some(name), true) => {
|
||||
let local = context.open_manifest()?.program_id().to_string();
|
||||
// Throw error if local name doesn't match the specified name.
|
||||
if name == local {
|
||||
local
|
||||
} else {
|
||||
return Err(PackageError::conflicting_on_chain_program_name(local, name).into());
|
||||
}
|
||||
}
|
||||
(Some(name), false) => name,
|
||||
(None, true) => context.open_manifest()?.program_id().to_string(),
|
||||
(None, false) => return Err(PackageError::missing_on_chain_program_name().into()),
|
||||
};
|
||||
|
||||
// Get the private key.
|
||||
let private_key = match self.fee_options.private_key {
|
||||
Some(private_key) => private_key,
|
||||
None => dotenv_private_key().map_err(CliError::failed_to_read_environment_private_key)?.to_string(),
|
||||
};
|
||||
|
||||
// Set deploy arguments.
|
||||
let mut fee_args = vec![
|
||||
"snarkos".to_string(),
|
||||
"--private-key".to_string(),
|
||||
private_key.clone(),
|
||||
"--query".to_string(),
|
||||
self.compiler_options.endpoint.clone(),
|
||||
"--priority-fee".to_string(),
|
||||
self.fee_options.priority_fee.to_string(),
|
||||
"--broadcast".to_string(),
|
||||
format!("{}/{}/transaction/broadcast", self.compiler_options.endpoint, self.fee_options.network)
|
||||
.to_string(),
|
||||
];
|
||||
|
||||
// Use record as payment option if it is provided.
|
||||
if let Some(record) = self.fee_options.record.clone() {
|
||||
fee_args.push("--record".to_string());
|
||||
fee_args.push(record);
|
||||
};
|
||||
|
||||
// Execute program.
|
||||
Developer::Execute(
|
||||
SnarkOSExecute::try_parse_from(
|
||||
[
|
||||
// The arguments for determining fee.
|
||||
fee_args,
|
||||
// The program ID and function name.
|
||||
vec![program_name, self.name],
|
||||
// The function inputs.
|
||||
self.inputs,
|
||||
]
|
||||
.concat(),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.parse()
|
||||
.map_err(CliError::failed_to_execute_deploy)?;
|
||||
|
||||
return Ok(());
|
||||
// Parse the network.
|
||||
let network = NetworkName::try_from(self.compiler_options.network.as_str())?;
|
||||
match network {
|
||||
NetworkName::MainnetV0 => handle_execute::<MainnetV0>(&self, context),
|
||||
NetworkName::TestnetV0 => handle_execute::<TestnetV0>(&self, context),
|
||||
}
|
||||
|
||||
// If input values are provided, then run the program with those inputs.
|
||||
// Otherwise, use the input file.
|
||||
let mut inputs = self.inputs;
|
||||
|
||||
// Compose the `execute` command.
|
||||
let mut arguments = vec![SNARKVM_COMMAND.to_string(), self.name];
|
||||
|
||||
// Add the inputs to the arguments.
|
||||
match self.file {
|
||||
Some(file) => {
|
||||
// Get the contents from the file.
|
||||
let path = context.dir()?.join(file);
|
||||
let raw_content = std::fs::read_to_string(&path)
|
||||
.map_err(|err| PackageError::failed_to_read_file(path.display(), err))?;
|
||||
// Parse the values from the file.
|
||||
let mut content = raw_content.as_str();
|
||||
let mut values = vec![];
|
||||
while let Ok((remaining, value)) = snarkvm::prelude::Value::<CurrentNetwork>::parse(content) {
|
||||
content = remaining;
|
||||
values.push(value);
|
||||
}
|
||||
// Check that the remaining content is empty.
|
||||
if !content.trim().is_empty() {
|
||||
return Err(PackageError::failed_to_read_input_file(path.display()).into());
|
||||
}
|
||||
// Convert the values to strings.
|
||||
let mut inputs_from_file = values.into_iter().map(|value| value.to_string()).collect::<Vec<String>>();
|
||||
// Add the inputs from the file to the arguments.
|
||||
arguments.append(&mut inputs_from_file);
|
||||
}
|
||||
None => arguments.append(&mut inputs),
|
||||
}
|
||||
|
||||
// Add the compiler options to the arguments.
|
||||
if self.compiler_options.offline {
|
||||
arguments.push(String::from("--offline"));
|
||||
}
|
||||
|
||||
// Add the endpoint to the arguments.
|
||||
arguments.push(String::from("--endpoint"));
|
||||
arguments.push(self.compiler_options.endpoint.clone());
|
||||
|
||||
// Open the Leo build/ directory.
|
||||
let path = context.dir()?;
|
||||
let build_directory = BuildDirectory::open(&path)?;
|
||||
|
||||
// Change the cwd to the Leo build/ directory to compile aleo files.
|
||||
std::env::set_current_dir(&build_directory)
|
||||
.map_err(|err| PackageError::failed_to_set_cwd(build_directory.display(), err))?;
|
||||
|
||||
// Unset the Leo panic hook.
|
||||
let _ = std::panic::take_hook();
|
||||
|
||||
// Call the `execute` command.
|
||||
println!();
|
||||
let command = SnarkVMExecute::try_parse_from(&arguments).map_err(CliError::failed_to_parse_execute)?;
|
||||
let res = command.parse().map_err(CliError::failed_to_execute_execute)?;
|
||||
|
||||
// Log the output of the `execute` command.
|
||||
tracing::info!("{}", res);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// A helper function to handle the `execute` command.
|
||||
fn handle_execute<N: Network>(command: &Execute, context: Context) -> Result<<Execute as Command>::Output> {
|
||||
// If the `broadcast` flag is set, then broadcast the transaction.
|
||||
if command.broadcast {
|
||||
// Get the program name.
|
||||
let program_name = match (command.program, command.local) {
|
||||
(Some(name), true) => {
|
||||
let local = context.open_manifest::<N>()?.program_id().to_string();
|
||||
// Throw error if local name doesn't match the specified name.
|
||||
if name == local {
|
||||
local
|
||||
} else {
|
||||
return Err(PackageError::conflicting_on_chain_program_name(local, name).into());
|
||||
}
|
||||
}
|
||||
(Some(name), false) => name,
|
||||
(None, true) => context.open_manifest::<N>()?.program_id().to_string(),
|
||||
(None, false) => return Err(PackageError::missing_on_chain_program_name().into()),
|
||||
};
|
||||
|
||||
// Get the private key.
|
||||
let private_key = match command.fee_options.private_key {
|
||||
Some(private_key) => private_key,
|
||||
None => dotenv_private_key().map_err(CliError::failed_to_read_environment_private_key)?.to_string(),
|
||||
};
|
||||
|
||||
// Set deploy arguments.
|
||||
let mut fee_args = vec![
|
||||
"snarkos".to_string(),
|
||||
"--private-key".to_string(),
|
||||
private_key.clone(),
|
||||
"--query".to_string(),
|
||||
command.compiler_options.endpoint.clone(),
|
||||
"--priority-fee".to_string(),
|
||||
command.fee_options.priority_fee.to_string(),
|
||||
"--broadcast".to_string(),
|
||||
format!("{}/{}/transaction/broadcast", command.compiler_options.endpoint, command.compiler_options.network)
|
||||
.to_string(),
|
||||
];
|
||||
|
||||
// Use record as payment option if it is provided.
|
||||
if let Some(record) = command.fee_options.record.clone() {
|
||||
fee_args.push("--record".to_string());
|
||||
fee_args.push(record);
|
||||
};
|
||||
|
||||
// Execute program.
|
||||
Developer::Execute(
|
||||
SnarkOSExecute::try_parse_from(
|
||||
[
|
||||
// The arguments for determining fee.
|
||||
fee_args,
|
||||
// The program ID and function name.
|
||||
vec![program_name, command.name],
|
||||
// The function inputs.
|
||||
command.inputs,
|
||||
]
|
||||
.concat(),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.parse()
|
||||
.map_err(CliError::failed_to_execute_deploy)?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// If input values are provided, then run the program with those inputs.
|
||||
// Otherwise, use the input file.
|
||||
let mut inputs = command.inputs;
|
||||
|
||||
// Compose the `execute` command.
|
||||
let mut arguments = vec![SNARKVM_COMMAND.to_string(), command.name];
|
||||
|
||||
// Add the inputs to the arguments.
|
||||
match command.file {
|
||||
Some(file) => {
|
||||
// Get the contents from the file.
|
||||
let path = context.dir()?.join(file);
|
||||
let raw_content =
|
||||
std::fs::read_to_string(&path).map_err(|err| PackageError::failed_to_read_file(path.display(), err))?;
|
||||
// Parse the values from the file.
|
||||
let mut content = raw_content.as_str();
|
||||
let mut values = vec![];
|
||||
while let Ok((remaining, value)) = snarkvm::prelude::Value::<N>::parse(content) {
|
||||
content = remaining;
|
||||
values.push(value);
|
||||
}
|
||||
// Check that the remaining content is empty.
|
||||
if !content.trim().is_empty() {
|
||||
return Err(PackageError::failed_to_read_input_file(path.display()).into());
|
||||
}
|
||||
// Convert the values to strings.
|
||||
let mut inputs_from_file = values.into_iter().map(|value| value.to_string()).collect::<Vec<String>>();
|
||||
// Add the inputs from the file to the arguments.
|
||||
arguments.append(&mut inputs_from_file);
|
||||
}
|
||||
None => arguments.append(&mut inputs),
|
||||
}
|
||||
|
||||
// Add the compiler options to the arguments.
|
||||
if command.compiler_options.offline {
|
||||
arguments.push(String::from("--offline"));
|
||||
}
|
||||
|
||||
// Add the endpoint to the arguments.
|
||||
arguments.push(String::from("--endpoint"));
|
||||
arguments.push(command.compiler_options.endpoint.clone());
|
||||
|
||||
// Open the Leo build/ directory.
|
||||
let path = context.dir()?;
|
||||
let build_directory = BuildDirectory::open(&path)?;
|
||||
|
||||
// Change the cwd to the Leo build/ directory to compile aleo files.
|
||||
std::env::set_current_dir(&build_directory)
|
||||
.map_err(|err| PackageError::failed_to_set_cwd(build_directory.display(), err))?;
|
||||
|
||||
// Unset the Leo panic hook.
|
||||
let _ = std::panic::take_hook();
|
||||
|
||||
// Call the `execute` command.
|
||||
println!();
|
||||
let command = SnarkVMExecute::try_parse_from(&arguments).map_err(CliError::failed_to_parse_execute)?;
|
||||
let res = command.parse().map_err(CliError::failed_to_execute_execute)?;
|
||||
|
||||
// Log the output of the `execute` command.
|
||||
tracing::info!("{}", res);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -130,6 +130,8 @@ pub trait Command {
|
||||
pub struct BuildOptions {
|
||||
#[clap(long, help = "Endpoint to retrieve network state from.", default_value = "http://api.explorer.aleo.org/v1")]
|
||||
pub endpoint: String,
|
||||
#[clap(long, help = "Network to broadcast to. Defaults to mainnet.", default_value = "mainnet")]
|
||||
pub(crate) network: String,
|
||||
#[clap(long, help = "Does not recursively compile dependencies.")]
|
||||
pub non_recursive: bool,
|
||||
#[clap(long, help = "Enables offline mode.")]
|
||||
@ -174,8 +176,6 @@ pub struct BuildOptions {
|
||||
pub struct FeeOptions {
|
||||
#[clap(long, help = "Priority fee in microcredits. Defaults to 0.", default_value = "0")]
|
||||
pub(crate) priority_fee: String,
|
||||
#[clap(long, help = "Network to broadcast to. Defaults to mainnet.", default_value = "mainnet")]
|
||||
pub(crate) network: String,
|
||||
#[clap(long, help = "Private key to authorize fee expenditure.")]
|
||||
pub(crate) private_key: Option<String>,
|
||||
#[clap(
|
||||
|
@ -16,13 +16,15 @@
|
||||
|
||||
use super::*;
|
||||
|
||||
use snarkvm::{cli::New as SnarkVMNew, file::AleoFile};
|
||||
use leo_retriever::NetworkName;
|
||||
|
||||
/// Create new Leo project
|
||||
#[derive(Parser, Debug)]
|
||||
pub struct New {
|
||||
#[clap(name = "NAME", help = "Set package name")]
|
||||
pub(crate) name: String,
|
||||
#[clap(short = 'n', long, help = "Name of the network to use", default_value = "mainnet")]
|
||||
pub(crate) network: String,
|
||||
}
|
||||
|
||||
impl Command for New {
|
||||
@ -38,6 +40,9 @@ impl Command for New {
|
||||
}
|
||||
|
||||
fn apply(self, context: Context, _: Self::Input) -> Result<Self::Output> {
|
||||
// Parse the network.
|
||||
let network = NetworkName::try_from(self.network.as_str())?;
|
||||
|
||||
// Derive the location of the parent directory to the project.
|
||||
let mut package_path = context.parent_dir()?;
|
||||
|
||||
@ -45,48 +50,15 @@ impl Command for New {
|
||||
std::env::set_current_dir(&package_path)
|
||||
.map_err(|err| PackageError::failed_to_set_cwd(package_path.display(), err))?;
|
||||
|
||||
// Call the `aleo new` command from the Aleo SDK.
|
||||
let command =
|
||||
SnarkVMNew::try_parse_from([SNARKVM_COMMAND, &self.name]).map_err(CliError::failed_to_parse_new)?;
|
||||
let result = command.parse().map_err(CliError::failed_to_execute_new)?;
|
||||
|
||||
// Log the output of the `aleo new` command.
|
||||
tracing::info!("{}", result);
|
||||
|
||||
// Initialize the Leo package in the directory created by `aleo new`.
|
||||
package_path.push(&self.name);
|
||||
std::env::set_current_dir(&package_path)
|
||||
.map_err(|err| PackageError::failed_to_set_cwd(package_path.display(), err))?;
|
||||
Package::<CurrentNetwork>::initialize(&self.name, &package_path)?;
|
||||
|
||||
// Open the program manifest.
|
||||
let manifest = context.open_manifest()?;
|
||||
|
||||
// Create a path to the build directory.
|
||||
let mut build_directory = package_path.clone();
|
||||
build_directory.push(BUILD_DIRECTORY_NAME);
|
||||
|
||||
// Write the Aleo file into the build directory.
|
||||
AleoFile::create(&build_directory, manifest.program_id(), true)
|
||||
.map_err(PackageError::failed_to_create_aleo_file)?;
|
||||
|
||||
// build_aleo_file.push(AleoFile::<Network>::main_file_name());
|
||||
//
|
||||
// println!("{}", build_aleo_file.display());
|
||||
//
|
||||
//
|
||||
// std::fs::File::create(build_aleo_file).map_err()
|
||||
// aleo_file.write_to(&build_aleo_file).map_err(PackageError::failed_to_write_aleo_file)?;
|
||||
|
||||
// Open the `main.aleo` file path.
|
||||
let aleo_file = AleoFile::open(&package_path, manifest.program_id(), true)
|
||||
.map_err(PackageError::failed_to_open_aleo_file)?;
|
||||
|
||||
let mut aleo_file_path = package_path.clone();
|
||||
aleo_file_path.push(AleoFile::<CurrentNetwork>::main_file_name());
|
||||
|
||||
// Remove the Aleo file from the package directory.
|
||||
aleo_file.remove(&aleo_file_path).map_err(PackageError::failed_to_remove_aleo_file)?;
|
||||
// Initialize the package.
|
||||
match network {
|
||||
NetworkName::MainnetV0 => Package::initialize(&self.name, network, &package_path),
|
||||
NetworkName::TestnetV0 => Package::initialize(&self.name, network, &package_path),
|
||||
}?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ impl Command for Program {
|
||||
// Build custom url to fetch from based on the flags and user's input.
|
||||
let url = if let Some(mapping_info) = self.mapping_value {
|
||||
// Check that the mapping name is valid.
|
||||
Package::<CurrentNetwork>::is_aleo_name_valid(&mapping_info[0]);
|
||||
Package::is_aleo_name_valid(&mapping_info[0]);
|
||||
format!("program/{}/mapping/{}/{}", program, mapping_info[0], mapping_info[1])
|
||||
} else if self.mappings {
|
||||
format!("program/{}/mappings", program)
|
||||
|
@ -85,10 +85,10 @@ pub fn is_valid_field(field: &str) -> Result<String, LeoError> {
|
||||
// Checks if the string is a valid program name in Aleo.
|
||||
pub fn check_valid_program_name(name: String) -> String {
|
||||
if name.ends_with(".aleo") {
|
||||
Package::<CurrentNetwork>::is_aleo_name_valid(&name[0..name.len() - 5]);
|
||||
Package::is_aleo_name_valid(&name[0..name.len() - 5]);
|
||||
name
|
||||
} else {
|
||||
Package::<CurrentNetwork>::is_aleo_name_valid(&name);
|
||||
Package::is_aleo_name_valid(&name);
|
||||
format!("{}.aleo", name)
|
||||
}
|
||||
}
|
||||
|
@ -29,12 +29,6 @@ pub struct Remove {
|
||||
)]
|
||||
pub(crate) name: Option<String>,
|
||||
|
||||
#[clap(short = 'l', long, help = "Path to local dependency")]
|
||||
pub(crate) local: Option<PathBuf>,
|
||||
|
||||
#[clap(short = 'n', long, help = "Name of the network to use", default_value = "testnet3")]
|
||||
pub(crate) network: String,
|
||||
|
||||
#[clap(long, help = "Clear all previous dependencies.", default_value = "false")]
|
||||
pub(crate) all: bool,
|
||||
}
|
||||
@ -62,18 +56,8 @@ impl Command for Remove {
|
||||
.map_err(|err| PackageError::failed_to_deserialize_manifest_file(path.to_str().unwrap(), err))?;
|
||||
|
||||
let dependencies: Vec<Dependency> = if !self.all {
|
||||
// Make sure the program name is valid.
|
||||
// Allow both `credits.aleo` and `credits` syntax.
|
||||
let name: String = match &self.name {
|
||||
Some(name)
|
||||
if name.ends_with(".aleo")
|
||||
&& Package::<CurrentNetwork>::is_aleo_name_valid(&name[0..name.len() - 5]) =>
|
||||
{
|
||||
name.clone()
|
||||
}
|
||||
Some(name) if Package::<CurrentNetwork>::is_aleo_name_valid(name) => format!("{name}.aleo"),
|
||||
name => return Err(PackageError::invalid_file_name_dependency(name.clone().unwrap()).into()),
|
||||
};
|
||||
// Note that this unwrap is safe since `name` is required if `all` is `false`.
|
||||
let name: String = self.name.unwrap().clone();
|
||||
|
||||
let mut found_match = false;
|
||||
let dep = match manifest.dependencies() {
|
||||
|
@ -16,7 +16,11 @@
|
||||
|
||||
use super::*;
|
||||
|
||||
use snarkvm::{cli::Run as SnarkVMRun, prelude::Parser as SnarkVMParser};
|
||||
use leo_retriever::NetworkName;
|
||||
use snarkvm::{
|
||||
cli::Run as SnarkVMRun,
|
||||
prelude::{MainnetV0, Network, Parser as SnarkVMParser, TestnetV0},
|
||||
};
|
||||
|
||||
/// Build, Prove and Run Leo program with inputs
|
||||
#[derive(Parser, Debug)]
|
||||
@ -47,56 +51,66 @@ impl Command for Run {
|
||||
}
|
||||
|
||||
fn apply(self, context: Context, _: Self::Input) -> Result<Self::Output> {
|
||||
let mut inputs = self.inputs;
|
||||
|
||||
// Compose the `run` command.
|
||||
let mut arguments = vec![SNARKVM_COMMAND.to_string(), self.name];
|
||||
|
||||
// Add the inputs to the arguments.
|
||||
match self.file {
|
||||
Some(file) => {
|
||||
// Get the contents from the file.
|
||||
let path = context.dir()?.join(file);
|
||||
let raw_content = std::fs::read_to_string(&path)
|
||||
.map_err(|err| PackageError::failed_to_read_file(path.display(), err))?;
|
||||
// Parse the values from the file.
|
||||
let mut content = raw_content.as_str();
|
||||
let mut values = vec![];
|
||||
while let Ok((remaining, value)) = snarkvm::prelude::Value::<CurrentNetwork>::parse(content) {
|
||||
content = remaining;
|
||||
values.push(value);
|
||||
}
|
||||
// Check that the remaining content is empty.
|
||||
if !content.trim().is_empty() {
|
||||
return Err(PackageError::failed_to_read_input_file(path.display()).into());
|
||||
}
|
||||
// Convert the values to strings.
|
||||
let mut inputs_from_file = values.into_iter().map(|value| value.to_string()).collect::<Vec<String>>();
|
||||
// Add the inputs from the file to the arguments.
|
||||
arguments.append(&mut inputs_from_file);
|
||||
}
|
||||
None => arguments.append(&mut inputs),
|
||||
// Parse the network.
|
||||
let network = NetworkName::try_from(self.compiler_options.network.as_str())?;
|
||||
match network {
|
||||
NetworkName::MainnetV0 => handle_run::<MainnetV0>(&self, context),
|
||||
NetworkName::TestnetV0 => handle_run::<TestnetV0>(&self, context),
|
||||
}
|
||||
|
||||
// Open the Leo build/ directory
|
||||
let path = context.dir()?;
|
||||
let build_directory = BuildDirectory::open(&path)?;
|
||||
|
||||
// Change the cwd to the Leo build/ directory to compile aleo files.
|
||||
std::env::set_current_dir(&build_directory)
|
||||
.map_err(|err| PackageError::failed_to_set_cwd(build_directory.display(), err))?;
|
||||
|
||||
// Unset the Leo panic hook
|
||||
let _ = std::panic::take_hook();
|
||||
|
||||
// Call the `run` command.
|
||||
println!();
|
||||
let command = SnarkVMRun::try_parse_from(&arguments).map_err(CliError::failed_to_parse_run)?;
|
||||
let res = command.parse().map_err(CliError::failed_to_execute_run)?;
|
||||
|
||||
// Log the output of the `run` command.
|
||||
tracing::info!("{}", res);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// A helper function to handle the run command.
|
||||
fn handle_run<N: Network>(command: &Run, context: Context) -> Result<<Run as Command>::Output> {
|
||||
let mut inputs = command.inputs;
|
||||
|
||||
// Compose the `run` command.
|
||||
let mut arguments = vec![SNARKVM_COMMAND.to_string(), command.name];
|
||||
|
||||
// Add the inputs to the arguments.
|
||||
match command.file {
|
||||
Some(file) => {
|
||||
// Get the contents from the file.
|
||||
let path = context.dir()?.join(file);
|
||||
let raw_content =
|
||||
std::fs::read_to_string(&path).map_err(|err| PackageError::failed_to_read_file(path.display(), err))?;
|
||||
// Parse the values from the file.
|
||||
let mut content = raw_content.as_str();
|
||||
let mut values = vec![];
|
||||
while let Ok((remaining, value)) = snarkvm::prelude::Value::<N>::parse(content) {
|
||||
content = remaining;
|
||||
values.push(value);
|
||||
}
|
||||
// Check that the remaining content is empty.
|
||||
if !content.trim().is_empty() {
|
||||
return Err(PackageError::failed_to_read_input_file(path.display()).into());
|
||||
}
|
||||
// Convert the values to strings.
|
||||
let mut inputs_from_file = values.into_iter().map(|value| value.to_string()).collect::<Vec<String>>();
|
||||
// Add the inputs from the file to the arguments.
|
||||
arguments.append(&mut inputs_from_file);
|
||||
}
|
||||
None => arguments.append(&mut inputs),
|
||||
}
|
||||
|
||||
// Open the Leo build/ directory
|
||||
let path = context.dir()?;
|
||||
let build_directory = BuildDirectory::open(&path)?;
|
||||
|
||||
// Change the cwd to the Leo build/ directory to compile aleo files.
|
||||
std::env::set_current_dir(&build_directory)
|
||||
.map_err(|err| PackageError::failed_to_set_cwd(build_directory.display(), err))?;
|
||||
|
||||
// Unset the Leo panic hook
|
||||
let _ = std::panic::take_hook();
|
||||
|
||||
// Call the `run` command.
|
||||
println!();
|
||||
let command = SnarkVMRun::try_parse_from(&arguments).map_err(CliError::failed_to_parse_run)?;
|
||||
let res = command.parse().map_err(CliError::failed_to_execute_run)?;
|
||||
|
||||
// Log the output of the `run` command.
|
||||
tracing::info!("{}", res);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ use snarkvm::file::Manifest;
|
||||
|
||||
use aleo_std::aleo_dir;
|
||||
use indexmap::IndexMap;
|
||||
use snarkvm::prelude::Network;
|
||||
use std::{
|
||||
env::current_dir,
|
||||
fs::File,
|
||||
@ -76,10 +77,10 @@ impl Context {
|
||||
|
||||
/// Returns the package name as a String.
|
||||
/// Opens the manifest file `program.json` and creates the build directory if it doesn't exist.
|
||||
pub fn open_manifest(&self) -> Result<Manifest<CurrentNetwork>> {
|
||||
pub fn open_manifest<N: Network>(&self) -> Result<Manifest<N>> {
|
||||
// Open the manifest file.
|
||||
let path = self.dir()?;
|
||||
let manifest = Manifest::<CurrentNetwork>::open(&path).map_err(PackageError::failed_to_open_manifest)?;
|
||||
let manifest = Manifest::<N>::open(&path).map_err(PackageError::failed_to_open_manifest)?;
|
||||
|
||||
// Lookup the program id.
|
||||
// let program_id = manifest.program_id();
|
||||
@ -97,7 +98,7 @@ impl Context {
|
||||
std::fs::read_to_string(manifest.path()).map_err(PackageError::failed_to_read_manifest)?;
|
||||
|
||||
// Construct the file path.
|
||||
let build_manifest_path = build_path.join(Manifest::<CurrentNetwork>::file_name());
|
||||
let build_manifest_path = build_path.join(Manifest::<N>::file_name());
|
||||
|
||||
// Write the file.
|
||||
File::create(build_manifest_path)
|
||||
|
@ -23,7 +23,6 @@ pub use commands::*;
|
||||
mod helpers;
|
||||
pub use helpers::*;
|
||||
|
||||
pub(crate) type CurrentNetwork = snarkvm::prelude::MainnetV0;
|
||||
pub(crate) const SNARKVM_COMMAND: &str = "snarkvm";
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -21,22 +21,22 @@ use crate::{
|
||||
source::{MainFile, SourceDirectory},
|
||||
};
|
||||
use leo_errors::{PackageError, Result};
|
||||
use snarkvm::console::prelude::Network;
|
||||
|
||||
use leo_retriever::NetworkName;
|
||||
use serde::Deserialize;
|
||||
use std::{marker::PhantomData, path::Path};
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct Package<N: Network> {
|
||||
pub struct Package {
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
pub description: Option<String>,
|
||||
pub license: Option<String>,
|
||||
_phantom: PhantomData<N>,
|
||||
pub network: NetworkName,
|
||||
}
|
||||
|
||||
impl<N: Network> Package<N> {
|
||||
pub fn new(package_name: &str) -> Result<Self> {
|
||||
impl Package {
|
||||
pub fn new(package_name: &str, network: NetworkName) -> Result<Self> {
|
||||
// Check that the package name is a valid Aleo program name.
|
||||
if !Self::is_aleo_name_valid(package_name) {
|
||||
return Err(PackageError::invalid_package_name(package_name).into());
|
||||
@ -47,7 +47,7 @@ impl<N: Network> Package<N> {
|
||||
version: "0.1.0".to_owned(),
|
||||
description: None,
|
||||
license: None,
|
||||
_phantom: PhantomData,
|
||||
network,
|
||||
})
|
||||
}
|
||||
|
||||
@ -126,7 +126,7 @@ impl<N: Network> Package<N> {
|
||||
}
|
||||
|
||||
/// Creates a Leo package at the given path
|
||||
pub fn initialize(package_name: &str, path: &Path) -> Result<()> {
|
||||
pub fn initialize(package_name: &str, network: NetworkName, path: &Path) -> Result<()> {
|
||||
// Verify that the .gitignore file does not exist.
|
||||
if !Gitignore::exists_at(path) {
|
||||
// Create the .gitignore file.
|
||||
@ -134,9 +134,9 @@ impl<N: Network> Package<N> {
|
||||
}
|
||||
|
||||
// Verify that the .env file does not exist.
|
||||
if !Env::<N>::exists_at(path) {
|
||||
if !Env::exists_at(path) {
|
||||
// Create the .env file.
|
||||
Env::<N>::new()?.write_to(path)?;
|
||||
Env::new(network)?.write_to(path)?;
|
||||
}
|
||||
|
||||
// Create the source directory.
|
||||
@ -169,29 +169,27 @@ impl<N: Network> Package<N> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
type CurrentNetwork = snarkvm::prelude::MainnetV0;
|
||||
|
||||
#[test]
|
||||
fn test_is_package_name_valid() {
|
||||
assert!(Package::<CurrentNetwork>::is_aleo_name_valid("foo"));
|
||||
assert!(Package::<CurrentNetwork>::is_aleo_name_valid("foo_bar"));
|
||||
assert!(Package::<CurrentNetwork>::is_aleo_name_valid("foo1"));
|
||||
assert!(Package::<CurrentNetwork>::is_aleo_name_valid("foo_bar___baz_"));
|
||||
assert!(Package::is_aleo_name_valid("foo"));
|
||||
assert!(Package::is_aleo_name_valid("foo_bar"));
|
||||
assert!(Package::is_aleo_name_valid("foo1"));
|
||||
assert!(Package::is_aleo_name_valid("foo_bar___baz_"));
|
||||
|
||||
assert!(!Package::<CurrentNetwork>::is_aleo_name_valid("foo-bar"));
|
||||
assert!(!Package::<CurrentNetwork>::is_aleo_name_valid("foo-bar-baz"));
|
||||
assert!(!Package::<CurrentNetwork>::is_aleo_name_valid("foo-1"));
|
||||
assert!(!Package::<CurrentNetwork>::is_aleo_name_valid(""));
|
||||
assert!(!Package::<CurrentNetwork>::is_aleo_name_valid("-"));
|
||||
assert!(!Package::<CurrentNetwork>::is_aleo_name_valid("-foo"));
|
||||
assert!(!Package::<CurrentNetwork>::is_aleo_name_valid("-foo-"));
|
||||
assert!(!Package::<CurrentNetwork>::is_aleo_name_valid("_foo"));
|
||||
assert!(!Package::<CurrentNetwork>::is_aleo_name_valid("foo--bar"));
|
||||
assert!(!Package::<CurrentNetwork>::is_aleo_name_valid("foo---bar"));
|
||||
assert!(!Package::<CurrentNetwork>::is_aleo_name_valid("foo--bar--baz"));
|
||||
assert!(!Package::<CurrentNetwork>::is_aleo_name_valid("foo---bar---baz"));
|
||||
assert!(!Package::<CurrentNetwork>::is_aleo_name_valid("foo*bar"));
|
||||
assert!(!Package::<CurrentNetwork>::is_aleo_name_valid("foo,bar"));
|
||||
assert!(!Package::<CurrentNetwork>::is_aleo_name_valid("1-foo"));
|
||||
assert!(!Package::is_aleo_name_valid("foo-bar"));
|
||||
assert!(!Package::is_aleo_name_valid("foo-bar-baz"));
|
||||
assert!(!Package::is_aleo_name_valid("foo-1"));
|
||||
assert!(!Package::is_aleo_name_valid(""));
|
||||
assert!(!Package::is_aleo_name_valid("-"));
|
||||
assert!(!Package::is_aleo_name_valid("-foo"));
|
||||
assert!(!Package::is_aleo_name_valid("-foo-"));
|
||||
assert!(!Package::is_aleo_name_valid("_foo"));
|
||||
assert!(!Package::is_aleo_name_valid("foo--bar"));
|
||||
assert!(!Package::is_aleo_name_valid("foo---bar"));
|
||||
assert!(!Package::is_aleo_name_valid("foo--bar--baz"));
|
||||
assert!(!Package::is_aleo_name_valid("foo---bar---baz"));
|
||||
assert!(!Package::is_aleo_name_valid("foo*bar"));
|
||||
assert!(!Package::is_aleo_name_valid("foo,bar"));
|
||||
assert!(!Package::is_aleo_name_valid("1-foo"));
|
||||
}
|
||||
}
|
||||
|
@ -16,26 +16,28 @@
|
||||
|
||||
//! The `.env` file.
|
||||
use leo_errors::{PackageError, Result};
|
||||
use snarkvm::console::{account::PrivateKey, prelude::Network};
|
||||
use snarkvm::console::account::PrivateKey;
|
||||
|
||||
use leo_retriever::NetworkName;
|
||||
use serde::Deserialize;
|
||||
use std::{borrow::Cow, fs::File, io::Write, marker::PhantomData, path::Path};
|
||||
use snarkvm::prelude::{MainnetV0, TestnetV0};
|
||||
use std::{borrow::Cow, fs::File, io::Write, path::Path};
|
||||
|
||||
pub static ENV_FILENAME: &str = ".env";
|
||||
|
||||
// TODO: Should this be generic over network?
|
||||
#[derive(Deserialize, Default)]
|
||||
pub struct Env<N: Network> {
|
||||
pub struct Env {
|
||||
data: String,
|
||||
_phantom: PhantomData<N>,
|
||||
}
|
||||
|
||||
impl<N: Network> Env<N> {
|
||||
pub fn new() -> Result<Self> {
|
||||
Ok(Self { data: Self::template()?, _phantom: PhantomData })
|
||||
impl Env {
|
||||
pub fn new(network: NetworkName) -> Result<Self> {
|
||||
Ok(Self { data: Self::template(network)? })
|
||||
}
|
||||
|
||||
pub fn from(data: String) -> Self {
|
||||
Self { data, _phantom: PhantomData }
|
||||
Self { data }
|
||||
}
|
||||
|
||||
pub fn exists_at(path: &Path) -> bool {
|
||||
@ -57,13 +59,16 @@ impl<N: Network> Env<N> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn template() -> Result<String> {
|
||||
fn template(network: NetworkName) -> Result<String> {
|
||||
// Initialize an RNG.
|
||||
let rng = &mut rand::thread_rng();
|
||||
|
||||
// Initialize a new development private key.
|
||||
let private_key = PrivateKey::<N>::new(rng)?;
|
||||
let private_key = match network {
|
||||
NetworkName::MainnetV0 => PrivateKey::<MainnetV0>::new(rng)?.to_string(),
|
||||
NetworkName::TestnetV0 => PrivateKey::<TestnetV0>::new(rng)?.to_string(),
|
||||
};
|
||||
|
||||
Ok(format!("NETWORK=mainnet\nPRIVATE_KEY={private_key}\n"))
|
||||
Ok(format!("NETWORK={network}\nPRIVATE_KEY={private_key}\n"))
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use leo_errors::{CliError, LeoError};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
||||
@ -26,12 +27,14 @@ pub enum NetworkName {
|
||||
MainnetV0,
|
||||
}
|
||||
|
||||
impl From<&String> for NetworkName {
|
||||
fn from(network: &String) -> Self {
|
||||
match network.to_ascii_lowercase().as_str() {
|
||||
"testnet" => NetworkName::TestnetV0,
|
||||
"mainnet" => NetworkName::MainnetV0,
|
||||
_ => panic!("Invalid network"),
|
||||
impl TryFrom<&str> for NetworkName {
|
||||
type Error = LeoError;
|
||||
|
||||
fn try_from(network: &str) -> Result<Self, LeoError> {
|
||||
match network {
|
||||
"testnet" => Ok(NetworkName::TestnetV0),
|
||||
"mainnet" => Ok(NetworkName::MainnetV0),
|
||||
_ => Err(LeoError::CliError(CliError::invalid_network_name(network))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user