use private_state_manager_client::delta_status::Status;
use super::MultisigClient;
use crate::error::{MultisigError, Result};
use crate::export::{ExportedProposal, ExportedSignature};
impl MultisigClient {
pub async fn export_proposal(
&mut self,
proposal_id: &str,
path: &std::path::Path,
) -> Result<()> {
let exported = self.export_proposal_to_exported(proposal_id).await?;
let json = exported.to_json()?;
std::fs::write(path, json)
.map_err(|e| MultisigError::InvalidConfig(format!("failed to write file: {}", e)))?;
Ok(())
}
pub async fn export_proposal_to_string(&mut self, proposal_id: &str) -> Result<String> {
let exported = self.export_proposal_to_exported(proposal_id).await?;
exported.to_json()
}
async fn export_proposal_to_exported(&mut self, proposal_id: &str) -> Result<ExportedProposal> {
let account = self.require_account()?.clone();
let account_id = account.id();
let proposals = self.list_proposals().await?;
let proposal = proposals
.iter()
.find(|p| p.id == proposal_id)
.ok_or_else(|| MultisigError::ProposalNotFound(proposal_id.to_string()))?;
let mut psm_client = self.create_authenticated_psm_client().await?;
let proposals_response = psm_client
.get_delta_proposals(&account_id)
.await
.map_err(|e| MultisigError::PsmServer(format!("failed to get proposals: {}", e)))?;
let raw_proposal = proposals_response
.proposals
.iter()
.find(|p| p.nonce == proposal.nonce)
.ok_or_else(|| {
MultisigError::ProposalNotFound(format!(
"raw delta not found for proposal {} (nonce {})",
proposal_id, proposal.nonce
))
})?;
let status = raw_proposal.status.as_ref().ok_or_else(|| {
MultisigError::PsmServer(format!("proposal {} has no status field", proposal_id))
})?;
let status_oneof = status.status.as_ref().ok_or_else(|| {
MultisigError::PsmServer(format!("proposal {} has empty status", proposal_id))
})?;
let pending = match status_oneof {
Status::Pending(p) => p,
_ => {
return Err(MultisigError::PsmServer(format!(
"proposal {} is not in pending state",
proposal_id
)));
}
};
let mut signatures = Vec::new();
for cosigner_sig in pending.cosigner_sigs.iter() {
if let Some(ref sig) = cosigner_sig.signature {
signatures.push(ExportedSignature {
signer_commitment: cosigner_sig.signer_id.clone(),
signature: sig.signature.clone(),
});
}
}
let exported =
ExportedProposal::from_proposal(proposal, account_id).with_signatures(signatures);
Ok(exported)
}
pub fn import_proposal(&self, path: &std::path::Path) -> Result<ExportedProposal> {
let json = std::fs::read_to_string(path)
.map_err(|e| MultisigError::InvalidConfig(format!("failed to read file: {}", e)))?;
self.import_proposal_from_string(&json)
}
pub fn import_proposal_from_string(&self, json: &str) -> Result<ExportedProposal> {
let exported = ExportedProposal::from_json(json)?;
if let Some(account) = &self.account {
let expected_id = account.id().to_string();
if !exported.account_id.eq_ignore_ascii_case(&expected_id) {
return Err(MultisigError::InvalidConfig(format!(
"proposal account {} does not match loaded account {}",
exported.account_id, expected_id
)));
}
}
Ok(exported)
}
}