mod error;
mod templates;
use crate::{ham::Ham, CommonOpts};
use anyhow::Result;
use colored::*;
use error::{ProgenitorError, ProgenitorResult};
use holochain_types::prelude::*;
use indicatif::{ProgressBar, ProgressStyle};
use rave_engine::types::entries::{
AddressBook, CodeTemplateExt, ExecutableAgreement, ExecutableAgreementExt, PieceDefinition,
};
use serde_json::Value;
pub async fn init(value: CommonOpts, library_path: String) -> ProgenitorResult<()> {
let agent = Ham::connect(value.clone()).await.map_err(|e| {
ProgenitorError::TemplateCreationError(format!("Failed to connect to conductor: {}", e))
})?;
println!("\n{}", "Starting initialization...".bold());
let pb = ProgressBar::new(4);
pb.set_style(
ProgressStyle::default_bar()
.template("[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg}")
.unwrap(),
);
pb.set_message("Checking initialization status...");
match check_initialization_status(value.clone()).await {
Ok(_) => println!("System not initialized, proceeding with initialization..."),
Err(ProgenitorError::AlreadyInitialized) => {
println!("System already initialized, checking remaining steps...")
}
Err(e) => return Err(e),
}
pb.inc(1);
pb.set_message("Creating system templates...");
let (__system_credit_limit_computation, __system_transaction_fee_collection) =
match create_system_templates(&agent, library_path).await {
Ok(templates) => templates,
Err(e) => {
println!(
"Templates might already exist, attempting to retrieve existing ones... {}",
e
);
get_existing_system_templates(&agent).await.map_err(|e| {
ProgenitorError::TemplateCreationError(format!(
"Failed to create or retrieve system templates: {}",
e
))
})?
}
};
pb.inc(1);
pb.set_message("Initializing pool definition...");
match initialize_pool_definition(
value.clone(),
__system_credit_limit_computation,
__system_transaction_fee_collection,
)
.await
{
Ok(_) => println!("Pool definition initialized successfully"),
Err(e) => {
if e.to_string().contains("already exists") {
println!("Pool definition already exists, continuing...");
} else {
return Err(ProgenitorError::TemplateCreationError(format!(
"Failed to initialize pool definition: {}",
e
)));
}
}
}
pb.inc(1);
pb.set_message("Setting up pool pieces...");
match add_pool_pieces(&agent, value.clone()).await {
Ok(_) => println!("Pool pieces added successfully"),
Err(e) => {
if e.to_string().contains("already exists") {
println!("Pool pieces already exist, continuing...");
} else {
return Err(ProgenitorError::TemplateCreationError(format!(
"Failed to add pool pieces: {}",
e
)));
}
}
}
pb.finish_with_message("Initialization complete!");
print_initialization_summary(value).await.map_err(|e| {
ProgenitorError::TemplateCreationError(format!(
"Failed to print initialization summary: {}",
e
))
})?;
Ok(())
}
async fn check_initialization_status(value: CommonOpts) -> ProgenitorResult<()> {
let library: Vec<CodeTemplateExt> =
super::zome_call::rave::get_code_templates_lib(value.clone())
.await
.map_err(|e| ProgenitorError::TemplateLoadError(e.to_string()))?;
if !library.is_empty() {
return Err(ProgenitorError::AlreadyInitialized);
}
Ok(())
}
async fn create_system_templates(
agent: &Ham,
library_path: String,
) -> Result<(ActionHashB64, ActionHashB64)> {
println!("\nCreating system templates...");
let templates = templates::SystemTemplateConfig::load(library_path)?;
let mut __system_credit_limit_computation = None;
let mut __system_transaction_fee_collection = None;
for template_config in templates {
let title = template_config.template.title.clone();
let template_id: ActionHashB64 = agent
.zome_call(
"alliance",
"transactor",
"create_code_template",
template_config.template,
)
.await?;
let mut template_agreement_ids = Vec::new();
for agreement in template_config.agreements {
let agreement_id: ActionHashB64 = agent
.zome_call(
"alliance",
"transactor",
"create_executable_agreement",
ExecutableAgreement {
title: agreement.title.clone(),
code_template_id: template_id.clone().into(),
input_rules: agreement.input_rules,
roles: agreement.roles,
executor_rules: agreement.executor_rules,
one_time_run: agreement.one_time_run,
aggregate_execution: agreement.aggregate_execution,
},
)
.await?;
template_agreement_ids.push(agreement_id);
}
match title.as_str() {
"__system_credit_limit_computation" => {
__system_credit_limit_computation = Some(
template_agreement_ids
.first()
.ok_or_else(|| {
anyhow::anyhow!("No agreements created for credit limit computation")
})?
.clone(),
);
}
"__system_transaction_fee_collection" => {
__system_transaction_fee_collection = Some(
template_agreement_ids
.first()
.ok_or_else(|| {
anyhow::anyhow!("No agreements created for transaction fee collection")
})?
.clone(),
);
}
_ => {}
}
}
Ok((
__system_credit_limit_computation.unwrap(),
__system_transaction_fee_collection.unwrap(),
))
}
async fn initialize_pool_definition(
value: CommonOpts,
__system_credit_limit_computation: ActionHashB64,
__system_transaction_fee_collection: ActionHashB64,
) -> Result<()> {
println!("\nInitializing pool definition...");
let agent = super::dashboard::whoami(value.clone()).await?;
let effective_start_date = Timestamp::now().as_micros() as u64;
super::zome_call::pool::initialize_pool_definition(
value,
Some(effective_start_date),
Some(30),
Some(__system_transaction_fee_collection),
Some(__system_credit_limit_computation),
Some(Some(AddressBook {
pub_key: agent,
address_book_data: Value::Null,
})),
Some(vec![]),
)
.await?;
Ok(())
}
async fn add_pool_pieces(agent: &Ham, value: CommonOpts) -> Result<()> {
println!("\nChecking pool pieces...");
let current_pieces: Vec<PieceDefinition> = agent
.zome_call("alliance", "transactor", "get_pool_pieces_details", ())
.await?;
if current_pieces.iter().any(|p| p.piece_symbol == "Z") {
let pieces = vec![
PieceDefinition {
piece_symbol: "HF".to_string(),
piece_name: "Holofuel".to_string(),
piece_description: "a mutual credit accounting unit".to_string(),
},
PieceDefinition {
piece_symbol: "BW".to_string(),
piece_name: "Bandwidth".to_string(),
piece_description: "your bandwidth logs".to_string(),
},
PieceDefinition {
piece_symbol: "REQ".to_string(),
piece_name: "Request".to_string(),
piece_description: "your request logs".to_string(),
},
];
for piece in pieces {
super::zome_call::pool::add_pool_pieces(value.clone(), piece).await?;
}
}
Ok(())
}
async fn print_initialization_summary(value: CommonOpts) -> Result<()> {
println!("\n{}", "Initialization Summary:".bold().underline());
super::zome_call::rave::get_code_templates_lib(value.clone()).await?;
super::zome_call::pool::get_pool_pieces_details(value).await?;
Ok(())
}
async fn get_existing_system_templates(agent: &Ham) -> Result<(ActionHashB64, ActionHashB64)> {
let templates: Vec<CodeTemplateExt> = agent
.zome_call("alliance", "transactor", "get_code_templates_lib", ())
.await?;
let mut credit_limit_computation = None;
let mut transaction_fee_collection = None;
for template in templates {
match template.title.as_str() {
"__system_credit_limit_computation" => {
let agreements: Vec<ExecutableAgreementExt> = agent
.zome_call(
"alliance",
"transactor",
"get_executable_agreements_for_code_template",
(),
)
.await?;
credit_limit_computation = Some(agreements[0].id.clone())
}
"__system_transaction_fee_collection" => {
let agreements: Vec<ExecutableAgreementExt> = agent
.zome_call(
"alliance",
"transactor",
"get_executable_agreements_for_code_template",
(),
)
.await?;
transaction_fee_collection = Some(agreements[0].id.clone())
}
_ => continue,
}
}
match (credit_limit_computation, transaction_fee_collection) {
(Some(clc), Some(tfc)) => Ok((clc, tfc)),
_ => Err(anyhow::anyhow!("Could not find existing system templates")),
}
}