use std::fmt;
use std::ops::Deref;
use bitcoin::Weight;
use ark::Vtxo;
use ark::vtxo::{Bare, Full, VtxoRef};
use crate::actions::WalletActionId;
use crate::movement::MovementId;
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "kebab-case")]
pub enum VtxoLockHolder {
Action { id: WalletActionId },
Movement { id: MovementId },
}
const SPENDABLE: &'static str = "Spendable";
const LOCKED: &'static str = "Locked";
const SPENT: &'static str = "Spent";
#[derive(Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum VtxoStateKind {
Spendable,
Locked,
Spent,
}
impl VtxoStateKind {
pub fn as_str(&self) -> &str {
match self {
VtxoStateKind::Spendable => SPENDABLE,
VtxoStateKind::Locked => LOCKED,
VtxoStateKind::Spent => SPENT,
}
}
pub fn as_byte(&self) -> u8 {
match self {
VtxoStateKind::Spendable => 0,
VtxoStateKind::Locked { .. } => 1,
VtxoStateKind::Spent => 2,
}
}
pub const ALL: &[VtxoStateKind] = &[
VtxoStateKind::Spendable,
VtxoStateKind::Locked,
VtxoStateKind::Spent,
];
pub const UNSPENT_STATES: &[VtxoStateKind] = &[
VtxoStateKind::Spendable,
VtxoStateKind::Locked,
];
}
impl fmt::Display for VtxoStateKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl fmt::Debug for VtxoStateKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "kebab-case")]
pub enum VtxoState {
Spendable,
Locked {
holder: Option<VtxoLockHolder>,
},
Spent,
}
impl VtxoState {
pub fn kind(&self) -> VtxoStateKind {
match self {
VtxoState::Spendable => VtxoStateKind::Spendable,
VtxoState::Locked { .. } => VtxoStateKind::Locked,
VtxoState::Spent => VtxoStateKind::Spent,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct WalletVtxo {
#[serde(with = "ark::encode::serde")]
pub vtxo: Vtxo<Bare>,
pub state: VtxoState,
pub exit_depth: u16,
pub exit_tx_weight: Weight,
}
impl VtxoRef for WalletVtxo {
fn vtxo_id(&self) -> ark::VtxoId { self.vtxo.id() }
fn as_bare_vtxo(&self) -> Option<std::borrow::Cow<'_, Vtxo<Bare>>> {
Some(std::borrow::Cow::Borrowed(&self.vtxo))
}
fn as_full_vtxo(&self) -> Option<&Vtxo<Full>> { None }
fn into_full_vtxo(self) -> Option<Vtxo<Full>> { None }
}
impl<'a> VtxoRef for &'a WalletVtxo {
fn vtxo_id(&self) -> ark::VtxoId { self.vtxo.id() }
fn as_bare_vtxo(&self) -> Option<std::borrow::Cow<'_, Vtxo<Bare>>> {
Some(std::borrow::Cow::Borrowed(&self.vtxo))
}
fn as_full_vtxo(&self) -> Option<&Vtxo<Full>> { None }
fn into_full_vtxo(self) -> Option<Vtxo<Full>> { None }
}
impl AsRef<Vtxo<Bare>> for WalletVtxo {
fn as_ref(&self) -> &Vtxo<Bare> {
&self.vtxo
}
}
impl Deref for WalletVtxo {
type Target = Vtxo<Bare>;
fn deref(&self) -> &Vtxo<Bare> {
&self.vtxo
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn convert_serialize() {
let states = [
VtxoStateKind::Spendable,
VtxoStateKind::Spent,
VtxoStateKind::Locked,
];
assert_eq!(
serde_json::to_string(&states).unwrap(),
serde_json::to_string(&[SPENDABLE, SPENT, LOCKED]).unwrap(),
);
match VtxoState::Spent {
VtxoState::Spendable => {},
VtxoState::Spent => {},
VtxoState::Locked { .. } => {},
}
}
}