use core::fmt;
use cdk_common::wallet::{KeysetLoadPolicy, WalletSaga};
use uuid::Uuid;
use crate::amount::SplitTarget;
use crate::nuts::{PreSwap, Proofs, PublicKey, SpendingConditions};
use crate::Amount;
#[derive(Debug)]
pub struct Initial {
pub operation_id: Uuid,
pub keyset_policy: KeysetLoadPolicy,
}
pub struct Prepared {
pub operation_id: Uuid,
pub amount: Option<Amount>,
pub amount_split_target: SplitTarget,
pub input_ys: Vec<PublicKey>,
pub spending_conditions: Option<SpendingConditions>,
pub pre_swap: PreSwap,
pub saga: WalletSaga,
}
impl fmt::Debug for Prepared {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Prepared")
.field("operation_id", &self.operation_id)
.field("amount", &self.amount)
.field("amount_split_target", &self.amount_split_target)
.field("input_ys", &self.input_ys)
.field("spending_conditions", &self.spending_conditions)
.field(
"input_amounts",
&self
.pre_swap
.swap_request
.inputs()
.iter()
.map(|proof| proof.amount)
.collect::<Vec<_>>(),
)
.field(
"output_amounts",
&self
.pre_swap
.swap_request
.outputs()
.iter()
.map(|output| output.amount)
.collect::<Vec<_>>(),
)
.field(
"pre_mint_amounts",
&self
.pre_swap
.pre_mint_secrets
.secrets
.iter()
.map(|secret| secret.amount)
.collect::<Vec<_>>(),
)
.field("derived_secret_count", &self.pre_swap.derived_secret_count)
.field("fee", &self.pre_swap.fee)
.field(
"p2bk_secret_key_count",
&self.pre_swap.p2bk_secret_keys.as_ref().map(Vec::len),
)
.field("saga_id", &self.saga.id)
.field("saga_state", &self.saga.state)
.field("saga_version", &self.saga.version)
.finish()
}
}
pub struct Finalized {
pub send_proofs: Option<Proofs>,
}
impl fmt::Debug for Finalized {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Finalized")
.field(
"send_proof_amounts",
&self
.send_proofs
.as_ref()
.map(|proofs| proofs.iter().map(|proof| proof.amount).collect::<Vec<_>>()),
)
.finish()
}
}
#[cfg(test)]
mod tests {
use cdk_common::wallet::{
OperationData, SwapOperationData, SwapSagaState, WalletSaga, WalletSagaState,
};
use cdk_common::CurrencyUnit;
use uuid::Uuid;
use super::{Finalized, Prepared};
use crate::amount::SplitTarget;
use crate::nuts::{PreMintSecrets, PreSwap, SwapRequest};
use crate::secret::Secret;
use crate::wallet::test_utils::{test_keyset_id, test_mint_url, test_proof};
use crate::Amount;
const SECRET_MARKER: &str = "super_secret_spending_material_xyz";
fn swap_saga(operation_id: Uuid) -> WalletSaga {
WalletSaga::new(
operation_id,
WalletSagaState::Swap(SwapSagaState::ProofsReserved),
Amount::from(1),
test_mint_url(),
CurrencyUnit::Sat,
OperationData::Swap(SwapOperationData {
input_amount: Amount::from(1),
output_amount: Amount::from(1),
counter_start: Some(0),
counter_end: Some(1),
blinded_messages: None,
}),
)
}
#[allow(clippy::use_debug)]
#[test]
fn swap_state_debug_does_not_leak_spending_secrets() {
let operation_id = Uuid::new_v4();
let keyset_id = test_keyset_id();
let mut input_proof = test_proof(keyset_id, 1);
input_proof.secret = Secret::new(SECRET_MARKER);
let pre_mint_secrets = PreMintSecrets::from_secrets(
keyset_id,
vec![Amount::from(1)],
vec![Secret::new(SECRET_MARKER)],
)
.expect("valid pre-mint secret");
let outputs = pre_mint_secrets
.secrets
.iter()
.map(|secret| secret.blinded_message.clone())
.collect();
let prepared = Prepared {
operation_id,
amount: Some(Amount::from(1)),
amount_split_target: SplitTarget::default(),
input_ys: Vec::new(),
spending_conditions: None,
pre_swap: PreSwap {
pre_mint_secrets,
swap_request: SwapRequest::new(vec![input_proof.clone()], outputs),
derived_secret_count: 1,
fee: Amount::from(0),
p2bk_secret_keys: None,
},
saga: swap_saga(operation_id),
};
let finalized = Finalized {
send_proofs: Some(vec![input_proof]),
};
let prepared_debug = format!("{prepared:?}");
let finalized_debug = format!("{finalized:?}");
assert!(!prepared_debug.contains(SECRET_MARKER));
assert!(!finalized_debug.contains(SECRET_MARKER));
}
}