use dynamic_waas_sdk_core::{
api::{ExportKeyReq, KeygenCompleteEvent},
sse::{stream_sse_with_callback, SseEventData},
Error, Result, ServerKeyShare,
};
use dynamic_waas_sdk_mpc::{EcdsaSigner, Ed25519Signer, KeygenId, RoomUuid, SecretShare};
use tracing::{debug, instrument};
use crate::client::DynamicWalletClient;
#[instrument(skip(client, share), fields(wallet_id))]
pub async fn run_export_ecdsa(
client: &DynamicWalletClient,
wallet_id: &str,
share: ServerKeyShare,
) -> Result<String> {
if !client.is_authenticated() {
return Err(Error::Authentication(crate::AUTH_REQUIRED_MSG.into()));
}
let signer = EcdsaSigner::new(client.base_mpc_relay_url().to_string());
let secret_share = SecretShare::from_string(share.secret_share);
let export_id = signer.export_id(&secret_share)?;
let export_id_str = export_id.as_str().to_owned();
let body = ExportKeyReq {
export_id: export_id_str.clone(),
address_type: None,
};
let response = client.api().export_key(wallet_id, &body).await?;
let host_url = client.base_mpc_relay_url().to_string();
let secret_share_for_cb = secret_share.clone();
let export_id_for_cb = KeygenId::new(export_id_str);
let (xpriv_opt, _ceremony_data) =
stream_sse_with_callback(response, "room_created", move |trigger| async move {
let event: KeygenCompleteEvent = match trigger {
SseEventData::Json(v) => serde_json::from_value(v).map_err(Error::from)?,
SseEventData::Raw(s) => {
return Err(Error::Sse(format!(
"room_created payload was not JSON: {s}"
)))
}
};
debug!(room_id = %event.room_id, "running ECDSA export");
let signer = EcdsaSigner::new(host_url);
let room = RoomUuid::new(event.room_id);
let result = signer
.export_full_private_key(&room, &secret_share_for_cb, &export_id_for_cb)
.await?;
Ok::<_, Error>(result)
})
.await?;
xpriv_opt.ok_or_else(|| {
Error::InvalidArgument(
"ECDSA export ceremony returned no xpriv (non-exporter party)".into(),
)
})
}
#[instrument(skip(client, share), fields(wallet_id))]
pub async fn run_export_ed25519(
client: &DynamicWalletClient,
wallet_id: &str,
share: ServerKeyShare,
) -> Result<Vec<u8>> {
if !client.is_authenticated() {
return Err(Error::Authentication(crate::AUTH_REQUIRED_MSG.into()));
}
let signer = Ed25519Signer::new(client.base_mpc_relay_url().to_string());
let secret_share = SecretShare::from_string(share.secret_share);
let export_id = signer.export_id(&secret_share)?;
let export_id_str = export_id.as_str().to_owned();
let body = ExportKeyReq {
export_id: export_id_str.clone(),
address_type: None,
};
let response = client.api().export_key(wallet_id, &body).await?;
let host_url = client.base_mpc_relay_url().to_string();
let secret_share_for_cb = secret_share.clone();
let export_id_for_cb = KeygenId::new(export_id_str);
let (privkey_opt, _ceremony_data) =
stream_sse_with_callback(response, "room_created", move |trigger| async move {
let event: KeygenCompleteEvent = match trigger {
SseEventData::Json(v) => serde_json::from_value(v).map_err(Error::from)?,
SseEventData::Raw(s) => {
return Err(Error::Sse(format!(
"room_created payload was not JSON: {s}"
)))
}
};
debug!(room_id = %event.room_id, "running Ed25519 export");
let signer = Ed25519Signer::new(host_url);
let room = RoomUuid::new(event.room_id);
let result = signer
.export_full_private_key(&room, &secret_share_for_cb, &export_id_for_cb)
.await?;
Ok::<_, Error>(result)
})
.await?;
privkey_opt.ok_or_else(|| {
Error::InvalidArgument(
"Ed25519 export ceremony returned no private key (non-exporter party)".into(),
)
})
}