use super::wallet::LocalWallet;
use crate::{
CashNote, Error as CashNoteError, Hash, Input, MainSecretKey, NanoTokens, Transaction,
TransactionBuilder,
};
use bls::SecretKey;
use lazy_static::lazy_static;
use std::{fmt::Debug, path::PathBuf};
use thiserror::Error;
pub(super) const GENESIS_CASHNOTE_AMOUNT: u64 = (0.3 * TOTAL_SUPPLY as f64) as u64;
pub(super) type GenesisResult<T> = Result<T, Error>;
pub const TOTAL_SUPPLY: u64 = u32::MAX as u64 * u64::pow(10, 9);
const GENESIS_CASHNOTE_SK: &str =
"5f15ae2ea589007e1474e049bbc32904d583265f12ce1f8153f955076a9af49b";
#[derive(Error, Debug, Clone)]
pub enum Error {
#[error("Genesis CashNote error:: {0}")]
GenesisCashNoteError(String),
#[error("Failed to parse reason: {0}")]
FailedToParseReason(#[from] Box<CashNoteError>),
#[error("Failed to perform wallet action: {0}")]
WalletError(String),
}
lazy_static! {
pub static ref GENESIS_CASHNOTE: CashNote = {
let main_key = match SecretKey::from_hex(GENESIS_CASHNOTE_SK) {
Ok(sk) => MainSecretKey::new(sk),
Err(err) => panic!("Failed to parse hard-coded genesis CashNote SK: {err:?}"),
};
match create_first_cash_note_from_key(&main_key) {
Ok(cash_note) => cash_note,
Err(err) => panic!("Failed to create genesis CashNote: {err:?}"),
}
};
}
pub fn is_genesis_parent_tx(parent_tx: &Transaction) -> bool {
parent_tx == &GENESIS_CASHNOTE.src_tx
}
pub fn load_genesis_wallet() -> Result<LocalWallet, Error> {
info!("Loading genesis...");
let mut genesis_wallet = create_genesis_wallet();
info!(
"Depositing genesis CashNote: {:#?}",
GENESIS_CASHNOTE.unique_pubkey()
);
genesis_wallet
.deposit_and_store_to_disk(&vec![GENESIS_CASHNOTE.clone()])
.map_err(|err| Error::WalletError(err.to_string()))
.expect("Genesis wallet shall be stored successfully.");
let genesis_balance = genesis_wallet.balance();
info!("Genesis wallet balance: {genesis_balance}");
Ok(genesis_wallet)
}
fn create_genesis_wallet() -> LocalWallet {
let root_dir = get_genesis_dir();
let wallet_dir = root_dir.join("wallet");
std::fs::create_dir_all(&wallet_dir).expect("Genesis wallet path to be successfully created.");
let secret_key = bls::SecretKey::from_hex(GENESIS_CASHNOTE_SK)
.expect("Genesis key hex shall be successfully parsed.");
debug!("genesis wallet pubkey: {:?}", secret_key.public_key());
let main_key = MainSecretKey::new(secret_key);
crate::wallet::store_new_keypair(&wallet_dir, &main_key)
.expect("Genesis key shall be successfully stored.");
LocalWallet::load_from(&root_dir)
.expect("Faucet wallet (after genesis) shall be created successfully.")
}
pub fn create_first_cash_note_from_key(
first_cash_note_key: &MainSecretKey,
) -> GenesisResult<CashNote> {
let main_pubkey = first_cash_note_key.main_pubkey();
debug!("genesis cashnote main_pubkey: {:?}", main_pubkey);
let derivation_index = [0u8; 32];
let derived_key = first_cash_note_key.derive_key(&derivation_index);
let genesis_input = Input {
unique_pubkey: derived_key.unique_pubkey(),
amount: NanoTokens::from(GENESIS_CASHNOTE_AMOUNT),
};
let reason = Hash::hash(b"GENESIS");
let cash_note_builder = TransactionBuilder::default()
.add_input(genesis_input, derived_key, Transaction::empty())
.add_output(
NanoTokens::from(GENESIS_CASHNOTE_AMOUNT),
main_pubkey,
derivation_index,
)
.build(reason)
.map_err(|err| {
Error::GenesisCashNoteError(format!(
"Failed to build the CashNote transaction for genesis CashNote: {err}",
))
})?;
let output_cash_notes = cash_note_builder.build_without_verifying().map_err(|err| {
Error::GenesisCashNoteError(format!(
"CashNote builder failed to create output genesis CashNote: {err}",
))
})?;
let (genesis_cash_note, _) = output_cash_notes.into_iter().next().ok_or_else(|| {
Error::GenesisCashNoteError(
"CashNote builder (unexpectedly) contains an empty set of outputs.".to_string(),
)
})?;
Ok(genesis_cash_note)
}
pub fn create_faucet_wallet() -> LocalWallet {
let root_dir = get_faucet_dir();
println!("Loading faucet wallet... {:#?}", root_dir);
LocalWallet::load_from(&root_dir).expect("Faucet wallet shall be created successfully.")
}
fn get_genesis_dir() -> PathBuf {
let mut data_dirs = dirs_next::data_dir().expect("A homedir to exist.");
data_dirs.push("safe");
data_dirs.push("test_genesis");
std::fs::create_dir_all(data_dirs.as_path())
.expect("Genesis test path to be successfully created.");
data_dirs
}
fn get_faucet_dir() -> PathBuf {
let mut data_dirs = dirs_next::data_dir().expect("A homedir to exist.");
data_dirs.push("safe");
data_dirs.push("test_faucet");
std::fs::create_dir_all(data_dirs.as_path())
.expect("Faucet test path to be successfully created.");
data_dirs
}