Skip to content

Commit

Permalink
refactor: clean up some panics in favor of returning a Result (#5967)
Browse files Browse the repository at this point in the history
* Return a Result from download_{config,genesis}()

* Return a Result from Config::write_to_file

* Return a Result from the signer write_to_file() functions

* Log init_configs() errors and exit normally rather than panicking

Also make the error message whehn --genesis and --download-genesis are
both given a little more clear.
  • Loading branch information
marcelo-gonzalez committed Jan 6, 2022
1 parent 6645853 commit f39a30d
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 69 deletions.
12 changes: 5 additions & 7 deletions core/crypto/src/key_file.rs
Expand Up @@ -16,18 +16,16 @@ pub struct KeyFile {
}

impl KeyFile {
pub fn write_to_file(&self, path: &Path) {
let mut file = File::create(path).expect("Failed to create / write a key file.");
pub fn write_to_file(&self, path: &Path) -> std::io::Result<()> {
let mut file = File::create(path)?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let perm = std::fs::Permissions::from_mode(u32::from(libc::S_IWUSR | libc::S_IRUSR));
file.set_permissions(perm).expect("Failed to set permissions for a key file.");
}
let str = serde_json::to_string_pretty(self).expect("Error serializing the key file.");
if let Err(err) = file.write_all(str.as_bytes()) {
panic!("Failed to write a key file {}", err);
file.set_permissions(perm)?;
}
let str = serde_json::to_string_pretty(self)?;
file.write_all(str.as_bytes())
}

pub fn from_file(path: &Path) -> Self {
Expand Down
6 changes: 3 additions & 3 deletions core/crypto/src/signer.rs
Expand Up @@ -20,7 +20,7 @@ pub trait Signer: Sync + Send {
fn compute_vrf_with_proof(&self, _data: &[u8]) -> (crate::vrf::Value, crate::vrf::Proof);

/// Used by test infrastructure, only implement if make sense for testing otherwise raise `unimplemented`.
fn write_to_file(&self, _path: &Path) {
fn write_to_file(&self, _path: &Path) -> std::io::Result<()> {
unimplemented!();
}
}
Expand Down Expand Up @@ -79,8 +79,8 @@ impl Signer for InMemorySigner {
secret_key.compute_vrf_with_proof(&data)
}

fn write_to_file(&self, path: &Path) {
KeyFile::from(self).write_to_file(path);
fn write_to_file(&self, path: &Path) -> std::io::Result<()> {
KeyFile::from(self).write_to_file(path)
}
}

Expand Down
8 changes: 4 additions & 4 deletions core/primitives/src/validator_signer.rs
Expand Up @@ -55,7 +55,7 @@ pub trait ValidatorSigner: Sync + Send {
) -> (near_crypto::vrf::Value, near_crypto::vrf::Proof);

/// Used by test infrastructure, only implement if make sense for testing otherwise raise `unimplemented`.
fn write_to_file(&self, path: &Path);
fn write_to_file(&self, path: &Path) -> std::io::Result<()>;
}

/// Test-only signer that "signs" everything with 0s.
Expand Down Expand Up @@ -118,7 +118,7 @@ impl ValidatorSigner for EmptyValidatorSigner {
unimplemented!()
}

fn write_to_file(&self, _path: &Path) {
fn write_to_file(&self, _path: &Path) -> std::io::Result<()> {
unimplemented!()
}
}
Expand Down Expand Up @@ -208,7 +208,7 @@ impl ValidatorSigner for InMemoryValidatorSigner {
self.signer.compute_vrf_with_proof(data)
}

fn write_to_file(&self, path: &Path) {
self.signer.write_to_file(path);
fn write_to_file(&self, path: &Path) -> std::io::Result<()> {
self.signer.write_to_file(path)
}
}
Expand Up @@ -86,6 +86,6 @@ pub fn csv_to_json_configs(home: &Path, chain_id: String, tracked_shards: Vec<Sh
verify_total_supply(genesis.config.total_supply, &chain_id);

// Write all configs to files.
config.write_to_file(&home.join(CONFIG_FILENAME));
config.write_to_file(&home.join(CONFIG_FILENAME)).expect("Error writing config");
genesis.to_file(&home.join(GENESIS_CONFIG_FILENAME));
}
19 changes: 14 additions & 5 deletions genesis-tools/keypair-generator/src/main.rs
Expand Up @@ -6,9 +6,9 @@ use clap::{App, AppSettings, Arg, SubCommand};
use near_crypto::{InMemorySigner, KeyType, SecretKey, Signer};
use nearcore::get_default_home;

fn generate_key_to_file(account_id: &str, key: SecretKey, path: PathBuf) {
fn generate_key_to_file(account_id: &str, key: SecretKey, path: &PathBuf) -> std::io::Result<()> {
let signer = InMemorySigner::from_secret_key(account_id.parse().unwrap(), key);
signer.write_to_file(path.as_path());
signer.write_to_file(path.as_path())
}

fn main() {
Expand Down Expand Up @@ -72,7 +72,10 @@ fn main() {
let key_file_name = format!("signer{}_key.json", i);
let mut path = home_dir.to_path_buf();
path.push(&key_file_name);
generate_key_to_file(account_id, key.clone(), path);
if let Err(e) = generate_key_to_file(account_id, key.clone(), &path) {
eprintln!("Error writing key to {}: {}", path.display(), e);
return;
}
}

pks.push(key.public_key());
Expand All @@ -89,7 +92,10 @@ fn main() {
account_id.expect("Account id must be specified if --generate-config is used");
let mut path = home_dir.to_path_buf();
path.push(nearcore::config::VALIDATOR_KEY_FILE);
generate_key_to_file(account_id, key, path);
if let Err(e) = generate_key_to_file(account_id, key, &path) {
eprintln!("Error writing key to {}: {}", path.display(), e);
return;
}
}
}
("node-key", Some(_args)) => {
Expand All @@ -98,7 +104,10 @@ fn main() {
if generate_config {
let mut path = home_dir.to_path_buf();
path.push(nearcore::config::NODE_KEY_FILE);
generate_key_to_file("node", key, path);
if let Err(e) = generate_key_to_file("node", key, &path) {
eprintln!("Error writing key to {}: {}", path.display(), e);
return;
}
}
}
(_, _) => unreachable!(),
Expand Down
119 changes: 79 additions & 40 deletions nearcore/src/config.rs
Expand Up @@ -472,12 +472,10 @@ impl Config {
Ok(config)
}

pub fn write_to_file(&self, path: &Path) {
let mut file = File::create(path).expect("Failed to create / write a config file.");
let str = serde_json::to_string_pretty(self).expect("Error serializing the config.");
if let Err(err) = file.write_all(str.as_bytes()) {
panic!("Failed to write a config file {}", err);
}
pub fn write_to_file(&self, path: &Path) -> std::io::Result<()> {
let mut file = File::create(path)?;
let str = serde_json::to_string_pretty(self)?;
file.write_all(str.as_bytes())
}

pub fn rpc_addr(&self) -> Option<&str> {
Expand Down Expand Up @@ -733,17 +731,21 @@ impl NearConfig {
pub fn save_to_dir(&self, dir: &Path) {
fs::create_dir_all(dir).expect("Failed to create directory");

self.config.write_to_file(&dir.join(CONFIG_FILENAME));
self.config.write_to_file(&dir.join(CONFIG_FILENAME)).expect("Error writing config");

if let Some(validator_signer) = &self.validator_signer {
validator_signer.write_to_file(&dir.join(&self.config.validator_key_file));
validator_signer
.write_to_file(&dir.join(&self.config.validator_key_file))
.expect("Error writing validator key file");
}

let network_signer = InMemorySigner::from_secret_key(
"node".parse().unwrap(),
self.network_config.secret_key.clone(),
);
network_signer.write_to_file(&dir.join(&self.config.node_key_file));
network_signer
.write_to_file(&dir.join(&self.config.node_key_file))
.expect("Error writing key file");

self.genesis.to_file(&dir.join(&self.config.genesis_file));
}
Expand Down Expand Up @@ -789,10 +791,12 @@ fn add_account_with_key(
}

/// Generate a validator key and save it to the file path.
fn generate_validator_key(account_id: AccountId, path: &Path) {
fn generate_validator_key(account_id: AccountId, path: &Path) -> anyhow::Result<()> {
let signer = InMemoryValidatorSigner::from_random(account_id.clone(), KeyType::ED25519);
info!(target: "near", "Use key {} for {} to stake.", signer.public_key(), account_id);
signer.write_to_file(path);
signer
.write_to_file(path)
.with_context(|| format!("Error writing key file to {}", path.display()))
}

pub fn mainnet_genesis() -> Genesis {
Expand Down Expand Up @@ -838,11 +842,13 @@ pub fn init_configs(
.unwrap_or_else(random_chain_id);

if let Some(url) = download_config_url {
download_config(&url.to_string(), &dir.join(CONFIG_FILENAME));
download_config(&url.to_string(), &dir.join(CONFIG_FILENAME))
.context("Failed to download the config file")?;
config = Config::from_file(&dir.join(CONFIG_FILENAME))?;
} else if should_download_config {
let url = get_config_url(&chain_id);
download_config(&url, &dir.join(CONFIG_FILENAME));
download_config(&url, &dir.join(CONFIG_FILENAME))
.context("Failed to download the config file")?;
config = Config::from_file(&dir.join(CONFIG_FILENAME))?;
}

Expand All @@ -860,15 +866,20 @@ pub fn init_configs(
bail!("Test seed is not supported for MainNet");
}
config.telemetry.endpoints.push(MAINNET_TELEMETRY_URL.to_string());
config.write_to_file(&dir.join(CONFIG_FILENAME));
config.write_to_file(&dir.join(CONFIG_FILENAME)).with_context(|| {
format!("Error writing config to {}", dir.join(CONFIG_FILENAME).display())
})?;

let genesis = mainnet_genesis();
if let Some(account_id) = account_id {
generate_validator_key(account_id, &dir.join(config.validator_key_file));
generate_validator_key(account_id, &dir.join(config.validator_key_file))?;
}

let path = dir.join(config.node_key_file);
let network_signer = InMemorySigner::from_random("node".parse()?, KeyType::ED25519);
network_signer.write_to_file(&dir.join(config.node_key_file));
network_signer
.write_to_file(&path)
.with_context(|| format!("Error writing key file to {}", path.display()))?;

genesis.to_file(&dir.join(config.genesis_file));
info!(target: "near", "Generated mainnet genesis file in {}", dir.display());
Expand All @@ -878,33 +889,43 @@ pub fn init_configs(
bail!("Test seed is not supported for official testnet");
}
config.telemetry.endpoints.push(NETWORK_TELEMETRY_URL.replace("{}", &chain_id));
config.write_to_file(&dir.join(CONFIG_FILENAME));
config.write_to_file(&dir.join(CONFIG_FILENAME)).with_context(|| {
format!("Error writing config to {}", dir.join(CONFIG_FILENAME).display())
})?;

if let Some(account_id) = account_id {
generate_validator_key(account_id, &dir.join(config.validator_key_file));
generate_validator_key(account_id, &dir.join(config.validator_key_file))?;
}

let path = dir.join(config.node_key_file);
let network_signer = InMemorySigner::from_random("node".parse()?, KeyType::ED25519);
network_signer.write_to_file(&dir.join(config.node_key_file));
network_signer
.write_to_file(&path)
.with_context(|| format!("Error writing key file to {}", path.display()))?;

// download genesis from s3
let genesis_path = dir.join("genesis.json");
let mut genesis_path_str =
genesis_path.to_str().with_context(|| "Genesis path must be initialized")?;

if let Some(url) = download_genesis_url {
download_genesis(&url.to_string(), &genesis_path);
download_genesis(&url.to_string(), &genesis_path)
.context("Failed to download the genesis file")?;
} else if should_download_genesis {
let url = get_genesis_url(&chain_id);
download_genesis(&url, &genesis_path);
download_genesis(&url, &genesis_path)
.context("Failed to download the genesis file")?;
} else {
genesis_path_str = genesis.unwrap_or_else(|| {
panic!(
"Genesis file is required for {}.\
Use <--genesis|--download-genesis>",
&chain_id
);
});
genesis_path_str = match genesis {
Some(g) => g,
None => {
bail!(
"Genesis file is required for {}.\
Use <--genesis|--download-genesis>",
&chain_id
);
}
};
}

let mut genesis = Genesis::from_file(&genesis_path_str);
Expand All @@ -922,7 +943,9 @@ pub fn init_configs(
config.consensus.max_block_production_delay =
Duration::from_millis(FAST_MAX_BLOCK_PRODUCTION_DELAY);
}
config.write_to_file(&dir.join(CONFIG_FILENAME));
config.write_to_file(&dir.join(CONFIG_FILENAME)).with_context(|| {
format!("Error writing config to {}", dir.join(CONFIG_FILENAME).display())
})?;

let account_id = account_id.unwrap_or_else(|| "test.near".parse().unwrap());

Expand All @@ -931,10 +954,16 @@ pub fn init_configs(
} else {
InMemoryValidatorSigner::from_random(account_id.clone(), KeyType::ED25519)
};
signer.write_to_file(&dir.join(config.validator_key_file));
let validator_path = dir.join(config.validator_key_file);
signer.write_to_file(&validator_path).with_context(|| {
format!("Error writing validator key file to {}", validator_path.display())
})?;

let node_path = dir.join(config.node_key_file);
let network_signer = InMemorySigner::from_random("node".parse()?, KeyType::ED25519);
network_signer.write_to_file(&dir.join(config.node_key_file));
network_signer
.write_to_file(&node_path)
.with_context(|| format!("Error writing key file to {}", node_path.display()))?;
let mut records = vec![];
add_account_with_key(
&mut records,
Expand Down Expand Up @@ -1079,11 +1108,15 @@ pub fn init_testnet_configs(
let node_dir = dir.join(format!("{}{}", prefix, i));
fs::create_dir_all(node_dir.clone()).expect("Failed to create directory");

validator_signers[i].write_to_file(&node_dir.join(&configs[i].validator_key_file));
network_signers[i].write_to_file(&node_dir.join(&configs[i].node_key_file));
validator_signers[i]
.write_to_file(&node_dir.join(&configs[i].validator_key_file))
.expect("Error writing validator key file");
network_signers[i]
.write_to_file(&node_dir.join(&configs[i].node_key_file))
.expect("Error writing key file");

genesis.to_file(&node_dir.join(&configs[i].genesis_file));
configs[i].write_to_file(&node_dir.join(CONFIG_FILENAME));
configs[i].write_to_file(&node_dir.join(CONFIG_FILENAME)).expect("Error writing config");
info!(target: "near", "Generated node key, validator key, genesis file in {}", node_dir.display());
}
}
Expand Down Expand Up @@ -1165,16 +1198,22 @@ pub fn download_file(url: &str, path: &Path) -> anyhow::Result<(), FileDownloadE
})
}

pub fn download_genesis(url: &str, path: &Path) {
pub fn download_genesis(url: &str, path: &Path) -> Result<(), FileDownloadError> {
info!(target: "near", "Downloading genesis file from: {} ...", url);
download_file(url, path).expect("Failed to download the genesis file");
info!(target: "near", "Saved the genesis file to: {} ...", path.display());
let result = download_file(url, path);
if result.is_ok() {
info!(target: "near", "Saved the genesis file to: {} ...", path.display());
}
result
}

pub fn download_config(url: &str, path: &Path) {
pub fn download_config(url: &str, path: &Path) -> Result<(), FileDownloadError> {
info!(target: "near", "Downloading config file from: {} ...", url);
download_file(url, path).expect("Failed to download the configuration file");
info!(target: "near", "Saved the config file to: {} ...", path.display());
let result = download_file(url, path);
if result.is_ok() {
info!(target: "near", "Saved the config file to: {} ...", path.display());
}
result
}

#[derive(Deserialize)]
Expand Down

0 comments on commit f39a30d

Please sign in to comment.