mod account;
mod helpers;
mod io;
mod notes;
mod offline;
mod proposals;
use std::path::PathBuf;
use std::sync::Arc;
use guardian_client::GetStateResponse;
use miden_client::rpc::Endpoint;
use miden_protocol::Word;
use miden_protocol::account::AccountId;
use crate::MidenSdkClient;
use crate::account::MultisigAccount;
use crate::builder::MultisigClientBuilder;
use crate::error::{MultisigError, Result};
use crate::export::ExportedProposal;
use crate::keystore::KeyManager;
use crate::proposal::Proposal;
pub use notes::{ConsumableNote, NoteFilter};
#[derive(Debug)]
pub enum ProposalResult {
Online(Box<Proposal>),
Offline(Box<ExportedProposal>),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StateVerificationResult {
pub account_id: AccountId,
pub local_commitment_hex: String,
pub on_chain_commitment_hex: String,
}
pub struct MultisigClient {
pub(crate) miden_client: MidenSdkClient,
pub(crate) key_manager: Arc<dyn KeyManager>,
pub(crate) guardian_endpoint: String,
pub(crate) account: Option<MultisigAccount>,
pub(crate) account_dir: PathBuf,
pub(crate) miden_endpoint: Endpoint,
}
impl MultisigClient {
pub fn builder() -> MultisigClientBuilder {
MultisigClientBuilder::new()
}
pub(crate) fn new(
miden_client: MidenSdkClient,
key_manager: Arc<dyn KeyManager>,
guardian_endpoint: String,
account_dir: PathBuf,
miden_endpoint: Endpoint,
) -> Self {
Self {
miden_client,
key_manager,
guardian_endpoint,
account: None,
account_dir,
miden_endpoint,
}
}
pub fn guardian_endpoint(&self) -> &str {
&self.guardian_endpoint
}
pub fn account(&self) -> Option<&MultisigAccount> {
self.account.as_ref()
}
pub fn account_id(&self) -> Option<AccountId> {
self.account.as_ref().map(|a| a.id())
}
pub fn has_account(&self) -> bool {
self.account.is_some()
}
pub fn user_commitment(&self) -> Word {
self.key_manager.commitment()
}
pub fn user_commitment_hex(&self) -> String {
self.key_manager.commitment_hex()
}
pub fn key_manager(&self) -> &dyn KeyManager {
self.key_manager.as_ref()
}
pub async fn recover_by_key(&self) -> Result<Vec<RecoveredAccount>> {
let mut guardian_client = self.create_authenticated_guardian_client().await?;
let commitment_hex = self.user_commitment_hex();
let lookup = guardian_client
.lookup_account_by_key_commitment(&commitment_hex)
.await
.map_err(|e| MultisigError::GuardianServer(format!("lookup failed: {}", e)))?;
let mut recovered = Vec::with_capacity(lookup.accounts.len());
for entry in lookup.accounts {
let account_id = AccountId::from_hex(&entry.account_id).map_err(|e| {
MultisigError::InvalidConfig(format!(
"GUARDIAN returned non-AccountId hex '{}': {}",
entry.account_id, e
))
})?;
let state = guardian_client.get_state(&account_id).await.map_err(|e| {
MultisigError::GuardianServer(format!(
"get_state failed for {}: {}",
entry.account_id, e
))
})?;
recovered.push(RecoveredAccount {
account_id: entry.account_id,
state,
});
}
Ok(recovered)
}
}
#[derive(Debug, Clone)]
pub struct RecoveredAccount {
pub account_id: String,
pub state: GetStateResponse,
}