|
9 | 9 | */ |
10 | 10 |
|
11 | 11 | use crate::models; |
| 12 | +use bitcoin::address::NetworkUnchecked; |
| 13 | +use bitcoin::secp256k1; |
| 14 | +use bitcoin::Address; |
| 15 | +use bitcoin::Amount; |
| 16 | +use bitcoin::Denomination; |
| 17 | +use bitcoin::Network; |
| 18 | +use bitcoin::PublicKey; |
12 | 19 | use serde::Deserialize; |
13 | 20 | use serde::Serialize; |
| 21 | +use std::error::Error as StdError; |
| 22 | +use std::fmt; |
| 23 | +use std::str::FromStr; |
14 | 24 |
|
15 | 25 | #[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)] |
16 | 26 | pub struct V1GetInfoResponse { |
@@ -61,3 +71,111 @@ impl V1GetInfoResponse { |
61 | 71 | } |
62 | 72 | } |
63 | 73 | } |
| 74 | + |
| 75 | +#[derive(Debug)] |
| 76 | +pub enum ConversionError { |
| 77 | + MissingPubkey, |
| 78 | + MissingRoundLifetime, |
| 79 | + MissingUnilateralExitDelay, |
| 80 | + MissingRoundInterval, |
| 81 | + MissingNetwork, |
| 82 | + MissingDust, |
| 83 | + MissingBoardingDescriptorTemplate, |
| 84 | + MissingVtxoDescriptorTemplate, |
| 85 | + MissingForfeitAddress, |
| 86 | + InvalidNetwork, |
| 87 | + ParseError(&'static str), |
| 88 | +} |
| 89 | + |
| 90 | +impl fmt::Display for ConversionError { |
| 91 | + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 92 | + match self { |
| 93 | + ConversionError::MissingPubkey => write!(f, "Missing pubkey field"), |
| 94 | + ConversionError::MissingRoundLifetime => write!(f, "Missing round lifetime field"), |
| 95 | + ConversionError::MissingUnilateralExitDelay => { |
| 96 | + write!(f, "Missing unilateral exit delay field") |
| 97 | + } |
| 98 | + ConversionError::MissingRoundInterval => write!(f, "Missing round interval field"), |
| 99 | + ConversionError::MissingNetwork => write!(f, "Missing network field"), |
| 100 | + ConversionError::MissingDust => write!(f, "Missing dust field"), |
| 101 | + ConversionError::MissingBoardingDescriptorTemplate => { |
| 102 | + write!(f, "Missing boarding descriptor template field") |
| 103 | + } |
| 104 | + ConversionError::InvalidNetwork => write!(f, "Invalid network value"), |
| 105 | + ConversionError::ParseError(msg) => write!(f, "Parse error: {}", msg), |
| 106 | + ConversionError::MissingVtxoDescriptorTemplate => { |
| 107 | + write!(f, "Missing vtxo descriptor template",) |
| 108 | + } |
| 109 | + ConversionError::MissingForfeitAddress => write!(f, "Missing forfeit address"), |
| 110 | + } |
| 111 | + } |
| 112 | +} |
| 113 | + |
| 114 | +impl StdError for ConversionError {} |
| 115 | + |
| 116 | +impl TryFrom<V1GetInfoResponse> for ark_core::server::Info { |
| 117 | + type Error = ConversionError; |
| 118 | + |
| 119 | + fn try_from(value: V1GetInfoResponse) -> Result<Self, Self::Error> { |
| 120 | + let pubkey = value.pubkey.ok_or(ConversionError::MissingPubkey)?; |
| 121 | + let pubkey = secp256k1::PublicKey::from_str(pubkey.as_str()) |
| 122 | + .map_err(|_| ConversionError::ParseError("Failed parsing pubkey"))?; |
| 123 | + |
| 124 | + let round_interval = value |
| 125 | + .round_interval |
| 126 | + .ok_or(ConversionError::MissingRoundInterval)? |
| 127 | + .parse::<i64>() |
| 128 | + .map_err(|_| ConversionError::ParseError("Failed to parse round_interval"))?; |
| 129 | + |
| 130 | + let network = value.network.ok_or(ConversionError::MissingNetwork)?; |
| 131 | + let network = |
| 132 | + Network::from_str(network.as_str()).map_err(|_| ConversionError::InvalidNetwork)?; |
| 133 | + |
| 134 | + let dust = value.dust.ok_or(ConversionError::MissingDust)?; |
| 135 | + let dust = Amount::from_str_in(dust.as_str(), Denomination::Satoshi) |
| 136 | + .map_err(|_| ConversionError::ParseError("Failed to parse dust"))?; |
| 137 | + |
| 138 | + let unilateral_exit_delay = value |
| 139 | + .unilateral_exit_delay |
| 140 | + .ok_or(ConversionError::MissingUnilateralExitDelay)? |
| 141 | + .parse() |
| 142 | + .map_err(|_| ConversionError::ParseError("Failed to parse unilateral_exit_delay"))?; |
| 143 | + |
| 144 | + let vtxo_tree_expiry = value |
| 145 | + .round_lifetime |
| 146 | + // TODO: our ark server doesn't return this value. I'm not sure it is mandatory? |
| 147 | + .unwrap_or("30".to_string()); |
| 148 | + |
| 149 | + let vtxo_tree_expiry_seconds = u32::from_str(vtxo_tree_expiry.as_str()).map_err(|_| { |
| 150 | + ConversionError::ParseError("Failed to parse round_lifetime to seconds") |
| 151 | + })?; |
| 152 | + let vtxo_tree_expiry = bitcoin::Sequence::from_seconds_ceil(vtxo_tree_expiry_seconds) |
| 153 | + .map_err(|_| ConversionError::ParseError("Failed to parse round_lifetime"))?; |
| 154 | + |
| 155 | + let forfeit_address = value |
| 156 | + .forfeit_address |
| 157 | + .ok_or(ConversionError::MissingForfeitAddress)?; |
| 158 | + let forfeit_address: Address<NetworkUnchecked> = forfeit_address |
| 159 | + .parse() |
| 160 | + .map_err(|_| ConversionError::ParseError("Failed parsing forfeit address"))?; |
| 161 | + |
| 162 | + let vtxo_descriptor_templates = value |
| 163 | + .vtxo_descriptor_templates |
| 164 | + .ok_or(ConversionError::MissingVtxoDescriptorTemplate)?; |
| 165 | + let boarding_descriptor_template = value |
| 166 | + .boarding_descriptor_template |
| 167 | + .ok_or(ConversionError::MissingBoardingDescriptorTemplate)?; |
| 168 | + |
| 169 | + Ok(ark_core::server::Info { |
| 170 | + pk: pubkey, |
| 171 | + vtxo_tree_expiry, |
| 172 | + unilateral_exit_delay, |
| 173 | + round_interval, |
| 174 | + network, |
| 175 | + dust, |
| 176 | + boarding_descriptor_template, |
| 177 | + vtxo_descriptor_templates, |
| 178 | + forfeit_address: forfeit_address.assume_checked(), |
| 179 | + }) |
| 180 | + } |
| 181 | +} |
0 commit comments