Skip to content

Commit

Permalink
feat(nargo): Add commands to install and uninstall custom backends. (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
TomAFrench committed Sep 6, 2023
1 parent e6c139b commit 28a413c
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 48 deletions.
45 changes: 1 addition & 44 deletions crates/acvm_backend_barretenberg/src/bb.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::{io::Cursor, path::Path};

use const_format::formatcp;

const USERNAME: &str = "AztecProtocol";
Expand All @@ -10,11 +8,7 @@ const TAG: &str = formatcp!("barretenberg-v{}", VERSION);
const API_URL: &str =
formatcp!("https://github.com/{}/{}/releases/download/{}", USERNAME, REPO, TAG);

fn get_bb_download_url() -> String {
if let Ok(path) = std::env::var("BB_BINARY_URL") {
return path;
}

pub(crate) fn get_bb_download_url() -> String {
let target_os = env!("TARGET_OS");
let target_arch = env!("TARGET_ARCH");

Expand All @@ -30,40 +24,3 @@ fn get_bb_download_url() -> String {

format!("{API_URL}/{archive_name}")
}

pub(crate) fn download_bb_binary(binary_path: &Path) {
use flate2::read::GzDecoder;
use tar::Archive;
use tempfile::tempdir;

// Create directory to place binary in.
std::fs::create_dir_all(binary_path.parent().unwrap()).unwrap();

// Download sources
let compressed_file: Cursor<Vec<u8>> = download_binary_from_url(&get_bb_download_url())
.unwrap_or_else(|error| panic!("\n\nDownload error: {error}\n\n"));

// Unpack the tarball
let gz_decoder = GzDecoder::new(compressed_file);
let mut archive = Archive::new(gz_decoder);

let temp_directory = tempdir().expect("could not create a temporary directory");
archive.unpack(&temp_directory).unwrap();
let temp_binary_path = temp_directory.path().join("bb");

// Rename the binary to the desired name
std::fs::copy(temp_binary_path, binary_path).unwrap();

drop(temp_directory);
}

/// Try to download the specified URL into a buffer which is returned.
fn download_binary_from_url(url: &str) -> Result<Cursor<Vec<u8>>, String> {
let response = reqwest::blocking::get(url).map_err(|error| error.to_string())?;

let bytes = response.bytes().unwrap();

// TODO: Check SHA of downloaded binary

Ok(Cursor::new(bytes.to_vec()))
}
49 changes: 49 additions & 0 deletions crates/acvm_backend_barretenberg/src/download.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use std::{io::Cursor, path::Path};

/// Downloads a zipped archive and unpacks the backend binary to `destination_path`.
///
/// # Backend Requirements
///
/// In order for a backend to be compatible with this function:
/// - `backend_url` must serve a gzipped tarball.
/// - The tarball must only contain the backend's binary.
/// - The binary file must be located at the archive root.
pub fn download_backend(backend_url: &str, destination_path: &Path) {
use flate2::read::GzDecoder;
use tar::Archive;
use tempfile::tempdir;

// Download sources
let compressed_file: Cursor<Vec<u8>> = download_binary_from_url(backend_url)
.unwrap_or_else(|error| panic!("\n\nDownload error: {error}\n\n"));

// Unpack the tarball
let gz_decoder = GzDecoder::new(compressed_file);
let mut archive = Archive::new(gz_decoder);

let temp_directory = tempdir().expect("could not create a temporary directory");
archive.unpack(&temp_directory).unwrap();

// Assume that the archive contains a single file which is the backend binary.
let mut archive_files = std::fs::read_dir(&temp_directory).unwrap();
let temp_binary_path = archive_files.next().unwrap().unwrap().path();

// Create directory to place binary in.
std::fs::create_dir_all(destination_path.parent().unwrap()).unwrap();

// Rename the binary to the desired name
std::fs::copy(temp_binary_path, destination_path).unwrap();

drop(temp_directory);
}

/// Try to download the specified URL into a buffer which is returned.
fn download_binary_from_url(url: &str) -> Result<Cursor<Vec<u8>>, String> {
let response = reqwest::blocking::get(url).map_err(|error| error.to_string())?;

let bytes = response.bytes().unwrap();

// TODO: Check SHA of downloaded binary

Ok(Cursor::new(bytes.to_vec()))
}
6 changes: 5 additions & 1 deletion crates/acvm_backend_barretenberg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ use std::path::PathBuf;

mod bb;
mod cli;
mod download;
mod proof_system;
mod smart_contract;

pub use download::download_backend;

const BACKENDS_DIR: &str = ".nargo/backends";
pub const ACVM_BACKEND_BARRETENBERG: &str = "acvm-backend-barretenberg";

pub fn backends_directory() -> PathBuf {
let home_directory = dirs::home_dir().unwrap();
Expand All @@ -33,7 +37,7 @@ fn assert_binary_exists(backend: &Backend) -> PathBuf {
let binary_path = backend.binary_path();

if !binary_path.is_file() {
bb::download_bb_binary(&binary_path)
download_backend(&bb::get_bb_download_url(), &binary_path)
}
binary_path
}
Expand Down
15 changes: 12 additions & 3 deletions crates/nargo_cli/src/backends.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ fn active_backend_file_path() -> PathBuf {
backends_directory().join(".selected_backend")
}

pub(crate) const ACVM_BACKEND_BARRETENBERG: &str = "acvm-backend-barretenberg";

pub(crate) fn clear_active_backend() {
let active_backend_file = active_backend_file_path();
if active_backend_file.is_file() {
std::fs::remove_file(active_backend_file_path())
.expect("should delete active backend file");
}
}

pub(crate) fn set_active_backend(backend_name: &str) {
std::fs::create_dir_all(
active_backend_file_path().parent().expect("active backend file should have parent"),
Expand All @@ -19,9 +29,8 @@ pub(crate) fn get_active_backend() -> String {
let active_backend_file = active_backend_file_path();

if !active_backend_file.is_file() {
let barretenberg = "acvm-backend-barretenberg";
set_active_backend(barretenberg);
return barretenberg.to_string();
set_active_backend(ACVM_BACKEND_BARRETENBERG);
return ACVM_BACKEND_BARRETENBERG.to_string();
}

std::fs::read_to_string(active_backend_file).unwrap()
Expand Down
13 changes: 13 additions & 0 deletions crates/nargo_cli/src/cli/backend_cmd/current_cmd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use clap::Args;

use crate::{backends::get_active_backend, errors::CliError};

/// Prints the name of the currently active backend
#[derive(Debug, Clone, Args)]
pub(crate) struct CurrentCommand;

pub(crate) fn run(_args: CurrentCommand) -> Result<(), CliError> {
println!("{}", get_active_backend());

Ok(())
}
25 changes: 25 additions & 0 deletions crates/nargo_cli/src/cli/backend_cmd/install_cmd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use clap::Args;

use acvm_backend_barretenberg::{backends_directory, download_backend};

use crate::errors::CliError;

use super::ls_cmd::get_available_backends;

/// Install a new backend
#[derive(Debug, Clone, Args)]
pub(crate) struct InstallCommand {
backend: String,

url: String,
}

pub(crate) fn run(args: InstallCommand) -> Result<(), CliError> {
let installed_backends = get_available_backends();

assert!(!installed_backends.contains(&args.backend), "backend is already installed");

download_backend(&args.url, &backends_directory().join(args.backend).join("backend_binary"));

Ok(())
}
1 change: 1 addition & 0 deletions crates/nargo_cli/src/cli/backend_cmd/ls_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub(crate) fn run(_args: LsCommand) -> Result<(), CliError> {
pub(super) fn get_available_backends() -> Vec<String> {
let backend_directory_contents = std::fs::read_dir(backends_directory()).unwrap();

// TODO: Highlight the currently active backend.
backend_directory_contents
.into_iter()
.filter_map(|entry| {
Expand Down
9 changes: 9 additions & 0 deletions crates/nargo_cli/src/cli/backend_cmd/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ use clap::{Args, Subcommand};

use crate::errors::CliError;

mod current_cmd;
mod install_cmd;
mod ls_cmd;
mod uninstall_cmd;
mod use_cmd;

#[non_exhaustive]
Expand All @@ -15,16 +18,22 @@ pub(crate) struct BackendCommand {
#[non_exhaustive]
#[derive(Subcommand, Clone, Debug)]
pub(crate) enum BackendCommands {
Current(current_cmd::CurrentCommand),
Ls(ls_cmd::LsCommand),
Use(use_cmd::UseCommand),
Install(install_cmd::InstallCommand),
Uninstall(uninstall_cmd::UninstallCommand),
}

pub(crate) fn run(cmd: BackendCommand) -> Result<(), CliError> {
let BackendCommand { command } = cmd;

match command {
BackendCommands::Current(args) => current_cmd::run(args),
BackendCommands::Ls(args) => ls_cmd::run(args),
BackendCommands::Use(args) => use_cmd::run(args),
BackendCommands::Install(args) => install_cmd::run(args),
BackendCommands::Uninstall(args) => uninstall_cmd::run(args),
}?;

Ok(())
Expand Down
55 changes: 55 additions & 0 deletions crates/nargo_cli/src/cli/backend_cmd/uninstall_cmd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use clap::Args;

use acvm_backend_barretenberg::backends_directory;

use crate::{
backends::{
clear_active_backend, get_active_backend, set_active_backend, ACVM_BACKEND_BARRETENBERG,
},
errors::CliError,
};

use super::ls_cmd::get_available_backends;

/// Uninstall a backend
#[derive(Debug, Clone, Args)]
pub(crate) struct UninstallCommand {
backend: String,
}

pub(crate) fn run(args: UninstallCommand) -> Result<(), CliError> {
let installed_backends = get_available_backends();

assert!(installed_backends.contains(&args.backend), "backend does not exist");
let active_backend = get_active_backend();

// Handle the case where we're uninstalling the currently active backend.
if active_backend == args.backend {
let barretenberg_is_installed =
installed_backends.iter().any(|backend_name| backend_name == ACVM_BACKEND_BARRETENBERG);

let new_active_backend =
if args.backend != ACVM_BACKEND_BARRETENBERG && barretenberg_is_installed {
// Prefer switching to barretenberg if possible.
Some(ACVM_BACKEND_BARRETENBERG)
} else {
// Otherwise pick the first backend which isn't being uninstalled.
installed_backends
.iter()
.find(|&backend_name| backend_name != &args.backend)
.map(|name| name.as_str())
};

if let Some(backend) = new_active_backend {
set_active_backend(backend)
} else {
// We've deleted the last backend. Clear the active backend file to be recreated once we install a new one.
clear_active_backend()
}
}

std::fs::remove_dir_all(&backends_directory().join(args.backend))
.expect("backend directory should be deleted");

Ok(())
}

0 comments on commit 28a413c

Please sign in to comment.