"grin wallet init" to create the initial wallet.seed file (#198)

* "wallet init" to create the initial wallet.seed file
* cleanup and better error msgs
* add some basic wallet docs
* cleanup and make passphrase optional
This commit is contained in:
AntiochP
2017-10-24 13:34:34 -04:00
committed by Ignotus Peverell
parent 1582ec4e73
commit 68cfbbecad
5 changed files with 205 additions and 29 deletions
+1 -1
View File
@@ -39,4 +39,4 @@ mod types;
pub use info::show_info;
pub use receiver::{WalletReceiver, receive_json_tx};
pub use sender::{issue_send_tx, issue_burn_tx};
pub use types::{WalletConfig, WalletReceiveRequest, BlockFees, CbData};
pub use types::{BlockFees, CbData, WalletConfig, WalletReceiveRequest, WalletSeed};
+115 -5
View File
@@ -12,13 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use blake2;
use rand::{thread_rng, Rng};
use std::{fmt, num, thread, time};
use std::convert::From;
use std::fs::{self, File, OpenOptions};
use std::io::Write;
use std::io::{self, Read, Write};
use std::path::Path;
use std::path::MAIN_SEPARATOR;
use std::collections::HashMap;
use std::cmp::min;
use serde_json;
use secp;
@@ -32,6 +35,7 @@ use util::LOGGER;
const DAT_FILE: &'static str = "wallet.dat";
const LOCK_FILE: &'static str = "wallet.lock";
const SEED_FILE: &'static str = "wallet.seed";
const DEFAULT_BASE_FEE: u64 = 10;
@@ -60,6 +64,8 @@ pub enum Error {
WalletData(String),
/// An error in the format of the JSON structures exchanged by the wallet
Format(String),
/// An IO Error
IOError(io::Error),
/// Error when contacting a node through its API
Node(api::Error),
}
@@ -100,6 +106,12 @@ impl From<api::Error> for Error {
}
}
impl From<io::Error> for Error {
fn from(e: io::Error) -> Error {
Error::IOError(e)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WalletConfig {
// Whether to run a wallet
@@ -200,6 +212,98 @@ impl OutputData {
}
}
#[derive(Clone, PartialEq)]
pub struct WalletSeed([u8; 32]);
impl WalletSeed {
pub fn from_bytes(bytes: &[u8]) -> WalletSeed {
let mut seed = [0; 32];
for i in 0..min(32, bytes.len()) {
seed[i] = bytes[i];
}
WalletSeed(seed)
}
fn from_hex(hex: &str) -> Result<WalletSeed, Error> {
let bytes = util::from_hex(hex.to_string())?;
Ok(WalletSeed::from_bytes(&bytes))
}
pub fn to_hex(&self) -> String {
util::to_hex(self.0.to_vec())
}
pub fn derive_keychain(&self, password: &str) -> Result<keychain::Keychain, Error> {
let seed = blake2::blake2b::blake2b(64, &password.as_bytes(), &self.0);
let result = keychain::Keychain::from_seed(seed.as_bytes())?;
Ok(result)
}
pub fn init_new() -> WalletSeed {
let seed: [u8; 32] = thread_rng().gen();
WalletSeed(seed)
}
pub fn init_file(wallet_config: &WalletConfig) -> Result<WalletSeed, Error> {
// create directory if it doesn't exist
fs::create_dir_all(&wallet_config.data_file_dir)?;
let seed_file_path = &format!(
"{}{}{}",
wallet_config.data_file_dir,
MAIN_SEPARATOR,
SEED_FILE,
);
debug!(
LOGGER,
"Generating wallet seed file at: {}",
seed_file_path,
);
if Path::new(seed_file_path).exists() {
panic!("wallet seed file already exists");
} else {
let seed = WalletSeed::init_new();
let mut file = File::create(seed_file_path)?;
file.write_all(&seed.to_hex().as_bytes())?;
Ok(seed)
}
}
pub fn from_file(wallet_config: &WalletConfig) -> Result<WalletSeed, Error> {
// create directory if it doesn't exist
fs::create_dir_all(&wallet_config.data_file_dir)?;
let seed_file_path = &format!(
"{}{}{}",
wallet_config.data_file_dir,
MAIN_SEPARATOR,
SEED_FILE,
);
debug!(
LOGGER,
"Using wallet seed file at: {}",
seed_file_path,
);
if Path::new(seed_file_path).exists() {
let mut file = File::open(seed_file_path)?;
let mut buffer = String::new();
file.read_to_string(&mut buffer)?;
let wallet_seed = WalletSeed::from_hex(&buffer)?;
Ok(wallet_seed)
} else {
error!(
LOGGER,
"Run: \"grin wallet init\" to initialize a new wallet.",
);
panic!("wallet seed file does not yet exist (grin wallet init)");
}
}
}
/// Wallet information tracking all our outputs. Based on HD derivation and
/// avoids storing any key data, only storing output amounts and child index.
/// This data structure is directly based on the JSON representation stored
@@ -242,7 +346,7 @@ impl WalletData {
.map_err(|_| {
Error::WalletData(format!(
"Could not create wallet lock file. Either \
some other process is using the wallet or there's a write access issue."
some other process is using the wallet or there is a write access issue."
))
});
match result {
@@ -250,16 +354,22 @@ impl WalletData {
break;
}
Err(e) => {
if retries >= 3 {
if retries >= 6 {
info!(
LOGGER,
"failed to obtain wallet.lock after {} retries, \
unable to successfully open the wallet",
retries
);
return Err(e);
}
debug!(
LOGGER,
"failed to obtain wallet.lock, retries - {}, sleeping",
"failed to obtain wallet.lock, retries - {}, sleeping and retrying",
retries
);
retries += 1;
thread::sleep(time::Duration::from_millis(500));
thread::sleep(time::Duration::from_millis(1000));
}
}
}