mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-11-22 22:44:47 +03:00
merge
This commit is contained in:
parent
753f789bae
commit
8254320846
1242
Cargo.lock
generated
1242
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -43,14 +43,10 @@ members = [
|
||||
"utils/retriever"
|
||||
]
|
||||
|
||||
[workspace.dependencies.snarkos-cli]
|
||||
git = "https://github.com/AleoNet/snarkOS.git"
|
||||
rev = "01ea476"
|
||||
|
||||
[workspace.dependencies.snarkvm]
|
||||
#version = "0.16.19"
|
||||
git = "https://github.com/AleoNet/snarkVM.git"
|
||||
rev = "140ff26"
|
||||
rev = "fddd8b9"
|
||||
|
||||
[lib]
|
||||
path = "leo/lib.rs"
|
||||
@ -156,9 +152,6 @@ version = "1.0"
|
||||
[dependencies.serial_test]
|
||||
version = "3.1.1"
|
||||
|
||||
[dependencies.snarkos-cli]
|
||||
workspace = true
|
||||
|
||||
[dependencies.snarkvm]
|
||||
workspace = true
|
||||
features = [ "circuit", "console" ]
|
||||
|
@ -243,4 +243,25 @@ create_messages!(
|
||||
msg: format!("Failed to build program: {error}"),
|
||||
help: None,
|
||||
}
|
||||
|
||||
@backtraced
|
||||
failed_to_parse_record {
|
||||
args: (error: impl Display),
|
||||
msg: format!("Failed to parse the record string.\nSnarkVM Error: {error}"),
|
||||
help: None,
|
||||
}
|
||||
|
||||
@backtraced
|
||||
string_parse_error {
|
||||
args: (error: impl Display),
|
||||
msg: format!("{error}"),
|
||||
help: None,
|
||||
}
|
||||
|
||||
@backtraced
|
||||
broadcast_error {
|
||||
args: (error: impl Display),
|
||||
msg: format!("{error}"),
|
||||
help: None,
|
||||
}
|
||||
);
|
||||
|
@ -15,13 +15,24 @@
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use super::*;
|
||||
use aleo_std::StorageMode;
|
||||
use leo_retriever::NetworkName;
|
||||
use snarkos_cli::commands::{Deploy as SnarkOSDeploy, Developer};
|
||||
use snarkvm::{
|
||||
circuit::{Aleo, AleoTestnetV0, AleoV0},
|
||||
cli::helpers::dotenv_private_key,
|
||||
prelude::{MainnetV0, TestnetV0},
|
||||
ledger::query::Query as SnarkVMQuery,
|
||||
package::Package as SnarkVMPackage,
|
||||
prelude::{
|
||||
deployment_cost,
|
||||
store::{helpers::memory::ConsensusMemory, ConsensusStore},
|
||||
MainnetV0,
|
||||
PrivateKey,
|
||||
ProgramOwner,
|
||||
TestnetV0,
|
||||
VM,
|
||||
},
|
||||
};
|
||||
use std::path::PathBuf;
|
||||
use std::{path::PathBuf, str::FromStr};
|
||||
|
||||
/// Deploys an Aleo program.
|
||||
#[derive(Parser, Debug)]
|
||||
@ -39,7 +50,7 @@ pub struct Deploy {
|
||||
)]
|
||||
pub(crate) wait: u64,
|
||||
#[clap(flatten)]
|
||||
pub(crate) compiler_options: BuildOptions,
|
||||
pub(crate) options: BuildOptions,
|
||||
}
|
||||
|
||||
impl Command for Deploy {
|
||||
@ -52,79 +63,121 @@ impl Command for Deploy {
|
||||
|
||||
fn prelude(&self, context: Context) -> Result<Self::Input> {
|
||||
if !self.no_build {
|
||||
(Build { options: self.compiler_options.clone() }).execute(context)?;
|
||||
(Build { options: self.options.clone() }).execute(context)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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 = 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;
|
||||
if private_key.is_none() {
|
||||
private_key =
|
||||
Some(dotenv_private_key().map_err(CliError::failed_to_read_environment_private_key)?.to_string());
|
||||
let network = NetworkName::try_from(self.options.network.as_str())?;
|
||||
match network {
|
||||
NetworkName::MainnetV0 => handle_deploy::<AleoV0, MainnetV0>(&self, context),
|
||||
NetworkName::TestnetV0 => handle_deploy::<AleoTestnetV0, TestnetV0>(&self, context),
|
||||
}
|
||||
|
||||
let mut all_paths: Vec<(String, PathBuf)> = Vec::new();
|
||||
|
||||
// Extract post-ordered list of local dependencies' paths from `leo.lock`.
|
||||
if self.recursive {
|
||||
// Cannot combine with private fee.
|
||||
if self.fee_options.record.is_some() {
|
||||
return Err(CliError::recursive_deploy_with_record().into());
|
||||
}
|
||||
all_paths = context.local_dependency_paths()?;
|
||||
}
|
||||
|
||||
// Add the parent program to be deployed last.
|
||||
all_paths.push((project_name, context.dir()?.join("build")));
|
||||
|
||||
for (index, (name, path)) in all_paths.iter().enumerate() {
|
||||
// Set the deploy arguments.
|
||||
let mut deploy_args = vec![
|
||||
"snarkos".to_string(),
|
||||
"--private-key".to_string(),
|
||||
private_key.as_ref().unwrap().clone(),
|
||||
"--query".to_string(),
|
||||
self.compiler_options.endpoint.clone(),
|
||||
"--priority-fee".to_string(),
|
||||
self.fee_options.priority_fee.to_string(),
|
||||
"--network".to_string(),
|
||||
network.id().to_string(),
|
||||
"--path".to_string(),
|
||||
path.to_str().unwrap().parse().unwrap(),
|
||||
"--broadcast".to_string(),
|
||||
format!("{}/{}/transaction/broadcast", self.compiler_options.endpoint, self.compiler_options.network)
|
||||
.to_string(),
|
||||
name.clone(),
|
||||
];
|
||||
|
||||
// Use record as payment option if it is provided.
|
||||
if let Some(record) = self.fee_options.record.clone() {
|
||||
deploy_args.push("--record".to_string());
|
||||
deploy_args.push(record);
|
||||
};
|
||||
|
||||
let deploy = SnarkOSDeploy::try_parse_from(deploy_args).unwrap();
|
||||
|
||||
// Deploy program.
|
||||
Developer::Deploy(deploy).parse().map_err(CliError::failed_to_execute_deploy)?;
|
||||
|
||||
// Sleep for `wait_gap` seconds.
|
||||
// This helps avoid parents from being serialized before children.
|
||||
if index < all_paths.len() - 1 {
|
||||
std::thread::sleep(std::time::Duration::from_secs(self.wait));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// A helper function to handle deployment logic.
|
||||
fn handle_deploy<A: Aleo<Network = N, BaseField = N::Field>, N: Network>(
|
||||
command: &Deploy,
|
||||
context: Context,
|
||||
) -> Result<<Deploy as Command>::Output> {
|
||||
// Get the program name.
|
||||
let project_name = context.open_manifest::<N>()?.program_id().to_string();
|
||||
|
||||
// Get the private key.
|
||||
let private_key = match &command.fee_options.private_key {
|
||||
Some(key) => PrivateKey::from_str(key)?,
|
||||
None => PrivateKey::from_str(
|
||||
&dotenv_private_key().map_err(CliError::failed_to_read_environment_private_key)?.to_string(),
|
||||
)?,
|
||||
};
|
||||
|
||||
// Specify the query
|
||||
let query = SnarkVMQuery::from(&command.options.endpoint);
|
||||
|
||||
let mut all_paths: Vec<(String, PathBuf)> = Vec::new();
|
||||
|
||||
// Extract post-ordered list of local dependencies' paths from `leo.lock`.
|
||||
if command.recursive {
|
||||
// Cannot combine with private fee.
|
||||
if command.fee_options.record.is_some() {
|
||||
return Err(CliError::recursive_deploy_with_record().into());
|
||||
}
|
||||
all_paths = context.local_dependency_paths()?;
|
||||
}
|
||||
|
||||
// Add the parent program to be deployed last.
|
||||
all_paths.push((project_name, context.dir()?.join("build")));
|
||||
|
||||
for (index, (name, path)) in all_paths.iter().enumerate() {
|
||||
// Fetch the package from the directory.
|
||||
let package = SnarkVMPackage::<N>::open(path)?;
|
||||
|
||||
println!("📦 Creating deployment transaction for '{}'...\n", &name.bold());
|
||||
|
||||
// Generate the deployment
|
||||
let deployment = package.deploy::<A>(None)?;
|
||||
let deployment_id = deployment.to_deployment_id()?;
|
||||
|
||||
// Generate the deployment transaction.
|
||||
let transaction = {
|
||||
// Initialize an RNG.
|
||||
let rng = &mut rand::thread_rng();
|
||||
|
||||
let store = ConsensusStore::<N, ConsensusMemory<N>>::open(StorageMode::Production)?;
|
||||
|
||||
// Initialize the VM.
|
||||
let vm = VM::from(store)?;
|
||||
|
||||
// Compute the minimum deployment cost.
|
||||
let (minimum_deployment_cost, _) = deployment_cost(&deployment)?;
|
||||
|
||||
// Prepare the fees.
|
||||
let fee = match &command.fee_options.record {
|
||||
Some(record) => {
|
||||
let fee_record = parse_record(&private_key, record)?;
|
||||
let fee_authorization = vm.authorize_fee_private(
|
||||
&private_key,
|
||||
fee_record,
|
||||
minimum_deployment_cost,
|
||||
command.fee_options.priority_fee,
|
||||
deployment_id,
|
||||
rng,
|
||||
)?;
|
||||
vm.execute_fee_authorization(fee_authorization, Some(query.clone()), rng)?
|
||||
}
|
||||
None => {
|
||||
let fee_authorization = vm.authorize_fee_public(
|
||||
&private_key,
|
||||
minimum_deployment_cost,
|
||||
command.fee_options.priority_fee,
|
||||
deployment_id,
|
||||
rng,
|
||||
)?;
|
||||
vm.execute_fee_authorization(fee_authorization, Some(query.clone()), rng)?
|
||||
}
|
||||
};
|
||||
// Construct the owner.
|
||||
let owner = ProgramOwner::new(&private_key, deployment_id, rng)?;
|
||||
|
||||
// Create a new transaction.
|
||||
Transaction::from_deployment(owner, deployment, fee)?
|
||||
};
|
||||
println!("✅ Created deployment transaction for '{}'", name.bold());
|
||||
|
||||
// Determine if the transaction should be broadcast, stored, or displayed to the user.
|
||||
handle_broadcast(
|
||||
&format!("{}/{}/transaction/broadcast", command.options.endpoint, command.fee_options.network),
|
||||
transaction,
|
||||
name,
|
||||
)?;
|
||||
|
||||
if index < all_paths.len() - 1 {
|
||||
std::thread::sleep(std::time::Duration::from_secs(command.wait));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
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::{MainnetV0, Network, Parser as SnarkVMParser, TestnetV0},
|
||||
@ -74,69 +73,67 @@ impl Command for Execute {
|
||||
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(),
|
||||
"--network".to_string(),
|
||||
N::ID.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(());
|
||||
// // 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.
|
||||
|
@ -57,11 +57,15 @@ use super::*;
|
||||
use crate::cli::helpers::context::*;
|
||||
use leo_errors::{emitter::Handler, CliError, PackageError, Result};
|
||||
use leo_package::{build::*, outputs::OutputsDirectory, package::*};
|
||||
use snarkvm::prelude::{block::Transaction, Ciphertext, Plaintext, PrivateKey, Record, ViewKey};
|
||||
|
||||
use clap::Parser;
|
||||
use colored::Colorize;
|
||||
use std::str::FromStr;
|
||||
use tracing::span::Span;
|
||||
|
||||
use snarkvm::{console::network::Network, prelude::ToBytes};
|
||||
|
||||
/// Base trait for the Leo CLI, see methods and their documentation for details.
|
||||
pub trait Command {
|
||||
/// If the current command requires running another command beforehand
|
||||
@ -202,7 +206,9 @@ impl Default for BuildOptions {
|
||||
#[derive(Parser, Clone, Debug)]
|
||||
pub struct FeeOptions {
|
||||
#[clap(long, help = "Priority fee in microcredits. Defaults to 0.", default_value = "0")]
|
||||
pub(crate) priority_fee: String,
|
||||
pub(crate) priority_fee: u64,
|
||||
#[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(
|
||||
@ -213,8 +219,90 @@ pub struct FeeOptions {
|
||||
record: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for FeeOptions {
|
||||
fn default() -> Self {
|
||||
Self { priority_fee: "0".to_string(), private_key: None, record: None }
|
||||
/// Parses the record string. If the string is a ciphertext, then attempt to decrypt it. Lifted from snarkOS.
|
||||
pub fn parse_record<N: Network>(private_key: &PrivateKey<N>, record: &str) -> Result<Record<N, Plaintext<N>>> {
|
||||
match record.starts_with("record1") {
|
||||
true => {
|
||||
// Parse the ciphertext.
|
||||
let ciphertext = Record::<N, Ciphertext<N>>::from_str(record)?;
|
||||
// Derive the view key.
|
||||
let view_key = ViewKey::try_from(private_key)?;
|
||||
// Decrypt the ciphertext.
|
||||
Ok(ciphertext.decrypt(&view_key)?)
|
||||
}
|
||||
false => Ok(Record::<N, Plaintext<N>>::from_str(record)?),
|
||||
}
|
||||
}
|
||||
|
||||
/// Determine if the transaction should be broadcast or displayed to user.
|
||||
fn handle_broadcast<N: Network>(endpoint: &String, transaction: Transaction<N>, operation: &String) -> Result<()> {
|
||||
println!("Broadcasting transaction to {}...", endpoint.clone());
|
||||
// Get the transaction id.
|
||||
let transaction_id = transaction.id();
|
||||
|
||||
// TODO: remove
|
||||
println!("Transaction {:?}", transaction);
|
||||
let tx_bytes = transaction.to_bytes_le()?;
|
||||
println!("Transaction bytes: {:?}", tx_bytes);
|
||||
let tx_json = serde_json::to_string(&transaction).unwrap();
|
||||
println!("Transaction JSON: {:?}", tx_json);
|
||||
let deserialize_tx_json = serde_json::from_str::<Transaction<N>>(&tx_json).unwrap();
|
||||
println!("Deserialized transaction: {:?}", deserialize_tx_json);
|
||||
|
||||
// Send the deployment request to the local development node.
|
||||
return match ureq::post(endpoint).send_json(&transaction) {
|
||||
Ok(id) => {
|
||||
// Remove the quotes from the response.
|
||||
let _response_string =
|
||||
id.into_string().map_err(CliError::string_parse_error)?.trim_matches('\"').to_string();
|
||||
|
||||
match transaction {
|
||||
Transaction::Deploy(..) => {
|
||||
println!(
|
||||
"⌛ Deployment {transaction_id} ('{}') has been broadcast to {}.",
|
||||
operation.bold(),
|
||||
endpoint
|
||||
)
|
||||
}
|
||||
Transaction::Execute(..) => {
|
||||
println!(
|
||||
"⌛ Execution {transaction_id} ('{}') has been broadcast to {}.",
|
||||
operation.bold(),
|
||||
endpoint
|
||||
)
|
||||
}
|
||||
Transaction::Fee(..) => {
|
||||
println!("❌ Failed to broadcast fee '{}' to the {}.", operation.bold(), endpoint)
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Err(error) => {
|
||||
let error_message = match error {
|
||||
ureq::Error::Status(code, response) => {
|
||||
format!("(status code {code}: {:?})", response.into_string().map_err(CliError::string_parse_error)?)
|
||||
}
|
||||
ureq::Error::Transport(err) => format!("({err})"),
|
||||
};
|
||||
|
||||
let msg = match transaction {
|
||||
Transaction::Deploy(..) => {
|
||||
format!("❌ Failed to deploy '{}' to {}: {}", operation.bold(), &endpoint, error_message)
|
||||
}
|
||||
Transaction::Execute(..) => {
|
||||
format!(
|
||||
"❌ Failed to broadcast execution '{}' to {}: {}",
|
||||
operation.bold(),
|
||||
&endpoint,
|
||||
error_message
|
||||
)
|
||||
}
|
||||
Transaction::Fee(..) => {
|
||||
format!("❌ Failed to broadcast fee '{}' to {}: {}", operation.bold(), &endpoint, error_message)
|
||||
}
|
||||
};
|
||||
|
||||
Err(CliError::broadcast_error(msg).into())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -26,9 +26,6 @@ version = "=1.12.0"
|
||||
path = "../../utils/retriever"
|
||||
version = "1.12.0"
|
||||
|
||||
[dependencies.snarkos-cli]
|
||||
workspace = true
|
||||
|
||||
[dependencies.snarkvm]
|
||||
workspace = true
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user