use std::num::NonZeroUsize;
use std::path::{Path, PathBuf};
use anyhow::Context;
use miden_node_store::BlockStore;
use miden_node_store::genesis::config::{AccountFileWithName, GenesisConfig};
use miden_node_utils::fs::ensure_empty_directory;
use miden_protocol::utils::serde::Serializable;
use miden_validator::{DataDirectory, ValidatorSigner};
use super::ValidatorKey;
pub async fn bootstrap(
genesis_block_directory: &Path,
accounts_directory: &Path,
data_directory: &Path,
sqlite_connection_pool_size: NonZeroUsize,
genesis_config: Option<&PathBuf>,
validator_key: ValidatorKey,
) -> anyhow::Result<()> {
let config = genesis_config
.map(|file_path| {
GenesisConfig::read_toml_file(file_path).with_context(|| {
format!("failed to parse genesis config from file {}", file_path.display())
})
})
.transpose()?
.unwrap_or_default();
for directory in [accounts_directory, genesis_block_directory, data_directory] {
ensure_empty_directory(directory)?;
}
let signer = validator_key.into_signer().await?;
let dirs = DataDirectory::load_bootstrap(
genesis_block_directory.to_path_buf(),
accounts_directory.to_path_buf(),
data_directory.to_path_buf(),
)
.context("failed to load bootstrap directories")?;
build_and_write_genesis(config, signer, dirs, sqlite_connection_pool_size).await
}
async fn build_and_write_genesis(
config: GenesisConfig,
signer: ValidatorSigner,
dirs: DataDirectory,
sqlite_connection_pool_size: NonZeroUsize,
) -> anyhow::Result<()> {
let (genesis_state, secrets) = config.into_state(signer.public_key())?;
for item in secrets.as_account_files(&genesis_state) {
let AccountFileWithName { account_file, name } = item?;
let account_path = dirs.accounts_dir().expect("bootstrap directories").join(name);
fs_err::OpenOptions::new()
.create_new(true)
.write(true)
.open(&account_path)
.context("key file already exists")?;
account_file.write(account_path)?;
}
let unsigned_genesis_block = genesis_state
.into_unsigned_block()
.context("failed to build the unsigned genesis block")?;
let signature = signer
.sign(unsigned_genesis_block.header())
.await
.context("failed to sign the genesis block")?;
let genesis_block = unsigned_genesis_block
.into_block(signature)
.context("failed to build the genesis block")?;
let block_bytes = genesis_block.inner().to_bytes();
fs_err::write(dirs.genesis_block_path().expect("bootstrap directories"), block_bytes)
.context("failed to write genesis block")?;
let _ = BlockStore::bootstrap(dirs.block_store_dir(), &genesis_block)?;
let (genesis_header, ..) = genesis_block.into_inner().into_parts();
let db = miden_validator::db::setup_with_pool_size(
dirs.database_path(),
sqlite_connection_pool_size,
)
.await
.context("failed to initialize validator database during bootstrap")?;
db.write("upsert_block_header", move |tx| {
miden_validator::db::upsert_block_header(tx, &genesis_header)
})
.await
.context("failed to persist genesis block header as chain tip")?;
Ok(())
}