use guardian_client::delta_status::Status;
use guardian_shared::SignatureScheme;
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 mut guardian_client = self.create_authenticated_guardian_client().await?;
let response = guardian_client
.get_delta_proposal(&account_id, proposal_id)
.await
.map_err(|e| MultisigError::GuardianServer(format!("failed to get proposal: {}", e)))?;
let raw_proposal = response
.proposal
.as_ref()
.ok_or_else(|| MultisigError::ProposalNotFound(proposal_id.to_string()))?;
Self::ensure_proposal_account_id(&raw_proposal.account_id, &account_id)?;
let proposal = crate::proposal::Proposal::from(raw_proposal)?;
self.verify_proposal_summary_binding(&proposal).await?;
let status = raw_proposal.status.as_ref().ok_or_else(|| {
MultisigError::GuardianServer(format!("proposal {} has no status field", proposal_id))
})?;
let status_oneof = status.status.as_ref().ok_or_else(|| {
MultisigError::GuardianServer(format!("proposal {} has empty status", proposal_id))
})?;
let pending = match status_oneof {
Status::Pending(p) => p,
_ => {
return Err(MultisigError::GuardianServer(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 {
let scheme = if sig.scheme.eq_ignore_ascii_case("ecdsa") {
SignatureScheme::Ecdsa
} else {
SignatureScheme::Falcon
};
signatures.push(ExportedSignature {
signer_commitment: cosigner_sig.signer_id.clone(),
signature: sig.signature.clone(),
scheme,
public_key_hex: sig.public_key.clone(),
});
}
}
let exported =
ExportedProposal::from_proposal(&proposal, account_id)?.with_signatures(signatures);
Ok(exported)
}
pub async fn import_proposal(&mut 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).await
}
pub async fn import_proposal_from_string(&mut self, json: &str) -> Result<ExportedProposal> {
let exported = ExportedProposal::from_json(json)?;
exported.validate(self.account.as_ref().map(|account| account.id()))?;
let proposal = exported.to_proposal()?;
self.verify_proposal_summary_binding(&proposal).await?;
Ok(exported)
}
}