Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve error handling in anomac utils join-network #1050

Closed
wants to merge 11 commits into from
335 changes: 181 additions & 154 deletions apps/src/lib/client/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,161 +44,188 @@ pub const NET_OTHER_ACCOUNTS_DIR: &str = "other";
const RELEASE_PREFIX: &str =
"https://github.com/heliaxdev/anoma-network-config/releases/download";

fn log_and_exit<E: std::error::Error>(error: E) {
eprintln!("ERROR: {}", error);
tracing::error!("{:?}", error); // ?: is using {:?} the best way to pass the error to tracing?
cli::safe_exit(1);
}

// TODO: can these two exit_if_error helpers be combined?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can, maybe with map_err(AsRef::as_ref) on the result

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried some things out with map_err though was getting compiler errors about returning a reference to an owned error, will have to come back to this

fn exit_if_error_ref<E: AsRef<dyn std::error::Error>>(result: Result<(), E>) {
if let Err(error) = result {
log_and_exit(error.as_ref());
}
}

fn exit_if_error<E: std::error::Error>(result: Result<(), E>) {
if let Err(error) = result {
log_and_exit(error);
}
}

/// Configure Anoma to join an existing network. The chain must be released in
/// the <https://github.com/heliaxdev/anoma-network-config> repository.
pub async fn join_network(
global_args: args::Global,
args::JoinNetwork { chain_id }: args::JoinNetwork,
) {
use tokio::fs;

let base_dir = &global_args.base_dir;
let wasm_dir = global_args.wasm_dir.as_ref().cloned().or_else(|| {
if let Ok(wasm_dir) = env::var(ENV_VAR_WASM_DIR) {
let wasm_dir: PathBuf = wasm_dir.into();
Some(wasm_dir)
} else {
None
}
});
if let Some(wasm_dir) = wasm_dir.as_ref() {
if wasm_dir.is_absolute() {
eprintln!(
"The arg `--wasm-dir` cannot be an absolute path. It is \
nested inside the chain directory."
);
cli::safe_exit(1);
}
}
if let Err(err) = fs::canonicalize(base_dir).await {
if err.kind() == std::io::ErrorKind::NotFound {
// If the base-dir doesn't exist yet, create it
fs::create_dir_all(base_dir).await.unwrap();
}
} else {
// If the base-dir exists, check if it's already got this chain ID
if fs::canonicalize(base_dir.join(chain_id.as_str()))
.await
.is_ok()
{
eprintln!("The chain directory for {} already exists.", chain_id);
cli::safe_exit(1);
}
}
let base_dir_full = fs::canonicalize(base_dir).await.unwrap();

let release_filename = format!("{}.tar.gz", chain_id);
let release_url =
format!("{}/{}/{}", RELEASE_PREFIX, chain_id, release_filename);
let cwd = env::current_dir().unwrap();

// Read or download the release archive
println!("Downloading config release from {} ...", release_url);
let release = download_file(release_url).await;

// Decode and unpack the archive
let mut decoder = GzDecoder::new(&release[..]);
let mut tar = String::new();
decoder.read_to_string(&mut tar).unwrap();
let mut archive = tar::Archive::new(tar.as_bytes());

// If the base-dir is non-default, unpack the archive into a temp dir inside
// first.
let (unpack_dir, non_default_dir) =
if base_dir_full != cwd.join(config::DEFAULT_BASE_DIR) {
(base_dir.clone(), true)
} else {
(PathBuf::from_str(".").unwrap(), false)
};
archive.unpack(&unpack_dir).unwrap();

// Rename the base-dir from the default and rename wasm-dir, if non-default.
if non_default_dir {
// For compatibility for networks released with Anoma <= v0.4:
// The old releases include the WASM directory at root path of the
// archive. This has been moved into the chain directory, so if the
// WASM dir is found at the old path, we move it to the new path.
if let Ok(wasm_dir) =
fs::canonicalize(unpack_dir.join(config::DEFAULT_WASM_DIR)).await
{
fs::rename(
&wasm_dir,
unpack_dir
.join(config::DEFAULT_BASE_DIR)
.join(chain_id.as_str())
.join(config::DEFAULT_WASM_DIR),
)
.await
.unwrap();
}

// Move the chain dir
fs::rename(
unpack_dir
.join(config::DEFAULT_BASE_DIR)
.join(chain_id.as_str()),
base_dir_full.join(chain_id.as_str()),
)
.await
.unwrap();

// Move the genesis file
fs::rename(
unpack_dir
.join(config::DEFAULT_BASE_DIR)
.join(format!("{}.toml", chain_id.as_str())),
base_dir_full.join(format!("{}.toml", chain_id.as_str())),
)
.await
.unwrap();
exit_if_error_ref(
async {
use tokio::fs;

let base_dir = &global_args.base_dir;
let wasm_dir =
global_args.wasm_dir.as_ref().cloned().or_else(|| {
if let Ok(wasm_dir) = env::var(ENV_VAR_WASM_DIR) {
let wasm_dir: PathBuf = wasm_dir.into();
Some(wasm_dir)
} else {
None
}
});
if let Some(wasm_dir) = wasm_dir.as_ref() {
if wasm_dir.is_absolute() {
eprintln!(
"The arg `--wasm-dir` cannot be an absolute path. It \
is nested inside the chain directory."
);
cli::safe_exit(1);
}
}
if let Err(err) = fs::canonicalize(base_dir).await {
if err.kind() == std::io::ErrorKind::NotFound {
// If the base-dir doesn't exist yet, create it
fs::create_dir_all(base_dir).await?;
}
} else {
// If the base-dir exists, check if it's already got this chain
// ID
if fs::canonicalize(base_dir.join(chain_id.as_str()))
.await
.is_ok()
{
eprintln!(
"The chain directory for {} already exists.",
chain_id
);
cli::safe_exit(1);
}
}
let base_dir_full = fs::canonicalize(base_dir).await?;

let release_filename = format!("{}.tar.gz", chain_id);
let release_url =
format!("{}/{}/{}", RELEASE_PREFIX, chain_id, release_filename);
let cwd = env::current_dir()?;

// Read or download the release archive
println!("Downloading config release from {} ...", release_url);
let release = download_file(release_url).await;

// Decode and unpack the archive
let mut decoder = GzDecoder::new(&release[..]);
let mut tar = String::new();
decoder.read_to_string(&mut tar)?;
let mut archive = tar::Archive::new(tar.as_bytes());

// If the base-dir is non-default, unpack the archive into a temp
// dir inside first.
let (unpack_dir, non_default_dir) =
if base_dir_full != cwd.join(config::DEFAULT_BASE_DIR) {
(base_dir.clone(), true)
} else {
(PathBuf::from_str(".")?, false)
};
archive.unpack(&unpack_dir)?;

// Rename the base-dir from the default and rename wasm-dir, if
// non-default.
if non_default_dir {
// For compatibility for networks released with Anoma <= v0.4:
// The old releases include the WASM directory at root path of
// the archive. This has been moved into the
// chain directory, so if the WASM dir is found
// at the old path, we move it to the new path.
if let Ok(wasm_dir) =
fs::canonicalize(unpack_dir.join(config::DEFAULT_WASM_DIR))
.await
{
fs::rename(
&wasm_dir,
unpack_dir
.join(config::DEFAULT_BASE_DIR)
.join(chain_id.as_str())
.join(config::DEFAULT_WASM_DIR),
)
.await?;
}

// Move the global config
fs::rename(
unpack_dir
.join(config::DEFAULT_BASE_DIR)
.join(config::global::FILENAME),
base_dir_full.join(config::global::FILENAME),
)
.await
.unwrap();
// Move the chain dir
fs::rename(
unpack_dir
.join(config::DEFAULT_BASE_DIR)
.join(chain_id.as_str()),
base_dir_full.join(chain_id.as_str()),
)
.await?;

// Move the genesis file
fs::rename(
unpack_dir
.join(config::DEFAULT_BASE_DIR)
.join(format!("{}.toml", chain_id.as_str())),
base_dir_full.join(format!("{}.toml", chain_id.as_str())),
)
.await?;

// Move the global config
fs::rename(
unpack_dir
.join(config::DEFAULT_BASE_DIR)
.join(config::global::FILENAME),
base_dir_full.join(config::global::FILENAME),
)
.await?;

// Remove the default dir
fs::remove_dir_all(unpack_dir.join(config::DEFAULT_BASE_DIR))
.await?;
}

// Remove the default dir
fs::remove_dir_all(unpack_dir.join(config::DEFAULT_BASE_DIR))
.await
.unwrap();
}
// Move wasm-dir and update config if it's non-default
if let Some(wasm_dir) = wasm_dir.as_ref() {
if wasm_dir.to_string_lossy() != config::DEFAULT_WASM_DIR {
tokio::fs::rename(
base_dir_full
.join(chain_id.as_str())
.join(config::DEFAULT_WASM_DIR),
base_dir_full.join(chain_id.as_str()).join(wasm_dir),
)
.await?;

// Update the config
let wasm_dir = wasm_dir.clone();
let base_dir = base_dir.clone();
let chain_id = chain_id.clone();
tokio::task::spawn_blocking(move || {
let mut config = Config::load(
&base_dir,
&chain_id,
global_args.mode.clone(),
);
config.wasm_dir = wasm_dir;

// Move wasm-dir and update config if it's non-default
if let Some(wasm_dir) = wasm_dir.as_ref() {
if wasm_dir.to_string_lossy() != config::DEFAULT_WASM_DIR {
tokio::fs::rename(
base_dir_full
.join(chain_id.as_str())
.join(config::DEFAULT_WASM_DIR),
base_dir_full.join(chain_id.as_str()).join(wasm_dir),
)
.await
.unwrap();
exit_if_error(config.write(&base_dir, &chain_id, true));
})
.await?;
}
}

// Update the config
let wasm_dir = wasm_dir.clone();
let base_dir = base_dir.clone();
let chain_id = chain_id.clone();
tokio::task::spawn_blocking(move || {
let mut config = Config::load(
&base_dir,
&chain_id,
global_args.mode.clone(),
);
config.wasm_dir = wasm_dir;
config.write(&base_dir, &chain_id, true).unwrap();
})
.await
.unwrap();
println!("Successfully configured for chain ID {}", chain_id);
eyre::Result::<()>::Ok(())
}
}

println!("Successfully configured for chain ID {}", chain_id);
.await,
);
}

/// Length of a Tendermint Node ID in bytes
Expand Down Expand Up @@ -666,16 +693,16 @@ pub fn init_network(
config.ledger.shell.base_dir = config::DEFAULT_BASE_DIR.into();
// Add a ledger P2P persistent peers
config.ledger.tendermint.p2p_persistent_peers = persistent_peers
.iter()
.enumerate()
.filter_map(|(index, peer)|
// we do not add the validator in its own persistent peer list
if index != ix {
Some(peer.to_owned())
} else {
None
})
.collect();
.iter()
.enumerate()
.filter_map(|(index, peer)|
// we do not add the validator in its own persistent peer list
if index != ix {
Some(peer.to_owned())
} else {
None
})
.collect();
Comment on lines +696 to +705
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unrelated formatting change

config.ledger.tendermint.consensus_timeout_commit =
consensus_timeout_commit;
config.ledger.tendermint.p2p_allow_duplicate_ip =
Expand Down