use std::error::Error;
use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use indicatif::{ProgressBar, ProgressStyle};
use bitcoin::psbt::PartiallySignedTransaction;
use bitcoin_protocol::validator::validate_psbt;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum InstallationSource {
Local {
path: PathBuf,
checksum: String,
},
GitHubRelease {
repo: String,
version: String,
asset: String,
},
CustomRepository {
url: String,
package: String,
version: String,
},
PackageRepository {
name: String,
version: String,
},
EnterpriseCluster {
license_key: String,
cluster_url: String,
psbt_contract: Option<PartiallySignedTransaction>,
},
}
impl InstallationSource {
pub fn validate(&self) -> Result<()> {
match self {
Self::EnterpriseCluster { psbt_contract, .. } => {
if let Some(psbt) = psbt_contract {
validate_psbt(psbt)
.context("PSBT contract validation failed")?;
}
Ok(())
}
_ => Ok(())
}
}
}
pub struct InstallationHandler {
source: InstallationSource,
bitcoin_config: BitcoinConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BitcoinConfig {
pub network: BitcoinNetwork,
pub required_bips: Vec<u32>,
pub taproot_enabled: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum BitcoinNetwork {
Mainnet,
Testnet,
Signet,
Regtest,
}
impl InstallationHandler {
pub fn new(source: InstallationSource, bitcoin_config: BitcoinConfig) -> Result<Self> {
source.validate()?;
Ok(Self { source, bitcoin_config })
}
pub async fn install(&self, target_dir: &Path) -> Result<()> {
let pb = ProgressBar::new_spinner()
.with_style(ProgressStyle::default_spinner().template("{spinner} {msg}")?);
match &self.source {
InstallationSource::Local { path, checksum } => {
pb.set_message("Verifying local package...");
self.validate_checksum(path, checksum).await?;
self.install_local(path, target_dir).await
}
InstallationSource::GitHubRelease { repo, version, asset } => {
pb.set_message("Downloading from GitHub...");
let downloaded = self.download_github(repo, version, asset).await?;
self.install_from_archive(&downloaded, target_dir).await
}
InstallationSource::CustomRepository { url, package, version } => {
pb.set_message("Fetching from custom repository...");
self.install_from_repo(url, package, version, target_dir).await
}
InstallationSource::PackageRepository { name, version } => {
pb.set_message("Installing from package repository...");
self.install_from_package(name, version, target_dir).await
}
InstallationSource::EnterpriseCluster { license_key, cluster_url, psbt_contract } => {
pb.set_message("Configuring enterprise cluster...");
self.setup_enterprise_cluster(license_key, cluster_url, psbt_contract, target_dir).await
}
}?;
pb.finish_with_message("Installation complete");
self.post_install_checks(target_dir).await
}
async fn validate_checksum(&self, path: &Path, expected: &str) -> Result<()> {
use sha2::{Sha256, Digest};
use std::fs::File;
use std::io::Read;
let mut file = File::open(path)?;
let mut hasher = Sha256::new();
let mut buffer = [0; 1024];
loop {
let count = file.read(&mut buffer)?;
if count == 0 { break; }
hasher.update(&buffer[..count]);
}
let actual = hex::encode(hasher.finalize());
if actual != *expected {
anyhow::bail!("Checksum mismatch: expected {}, got {}", expected, actual);
}
Ok(())
}
async fn install_local(&self, path: &Path, target: &Path) -> Result<()> {
Ok(())
}
async fn download_github(&self, repo: &str, version: &str, asset: &str) -> Result<PathBuf> {
Ok(PathBuf::from("/tmp/downloaded-package.tar.gz"))
}
async fn install_from_archive(&self, archive: &Path, target: &Path) -> Result<()> {
Ok(())
}
async fn install_from_repo(&self, url: &str, package: &str, version: &str, target: &Path) -> Result<()> {
Ok(())
}
async fn install_from_package(&self, name: &str, version: &str, target: &Path) -> Result<()> {
Ok(())
}
async fn setup_enterprise_cluster(
&self,
license_key: &str,
cluster_url: &str,
psbt_contract: &Option<PartiallySignedTransaction>,
target: &Path
) -> Result<()> {
if let Some(psbt) = psbt_contract {
self.validate_psbt_contract(psbt).await?;
}
Ok(())
}
async fn validate_psbt_contract(&self, psbt: &PartiallySignedTransaction) -> Result<()> {
bitcoin_protocol::validator::validate_psbt(psbt)
.context("PSBT contract validation failed")
}
async fn post_install_checks(&self, target: &Path) -> Result<()> {
self.check_bip_compliance(target).await?;
self.check_taproot_support(target).await?;
Ok(())
}
async fn check_bip_compliance(&self, target: &Path) -> Result<()> {
Ok(())
}
async fn check_taproot_support(&self, target: &Path) -> Result<()> {
Ok(())
}
}