Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 98 additions & 3 deletions src/custom_serde.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use bitcoin::{
consensus::{deserialize_partial, encode::deserialize_hex},
hex::FromHex,
consensus::{deserialize_partial, encode::deserialize_hex, Encodable},
hex::{DisplayHex, FromHex},
};
use serde::{
de::{Error, Unexpected},
Deserialize, Deserializer,
Deserialize, Deserializer, Serialize, Serializer,
};
use serde_json::Value;

Expand All @@ -19,6 +19,14 @@ where
deserialize_hex(&hex_str).map_err(serde::de::Error::custom)
}

pub fn to_consensus_hex<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
where
T: Encodable,
S: Serializer,
{
bitcoin::consensus::encode::serialize_hex(value).serialize(serializer)
}

pub fn from_cancat_consensus_hex<'de, T, D>(deserializer: D) -> Result<Vec<T>, D::Error>
where
T: bitcoin::consensus::encode::Decodable,
Expand All @@ -38,6 +46,20 @@ where
Ok(items)
}

pub fn to_cancat_consensus_hex<T, S>(values: &[T], serializer: S) -> Result<S::Ok, S::Error>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this should be _concat_ right?

where
T: Encodable,
S: Serializer,
{
let mut data = Vec::<u8>::new();
for value in values {
value
.consensus_encode(&mut data)
.map_err(serde::ser::Error::custom)?;
}
data.to_lower_hex_string().serialize(serializer)
}

pub fn feerate_opt_from_btc_per_kb<'de, D>(
deserializer: D,
) -> Result<Option<bitcoin::FeeRate>, D::Error>
Expand All @@ -52,6 +74,22 @@ where
Ok(Some(bitcoin::FeeRate::from_sat_per_kwu(sat_per_kwu as _)))
}

/// The Electrum API signals "no estimate available" with a negative number, so [`None`] is written
/// back out as `-1.0`.
pub fn feerate_opt_to_btc_per_kb<S>(
fee_rate: &Option<bitcoin::FeeRate>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let btc_per_kvb = match fee_rate {
Some(fee_rate) => fee_rate.to_sat_per_kwu() as f32 / (100_000_000.0 / 4.0),
None => -1.0,
};
btc_per_kvb.serialize(serializer)
}

pub fn feerate_from_sat_per_byte<'de, D>(deserializer: D) -> Result<bitcoin::FeeRate, D::Error>
where
D: Deserializer<'de>,
Expand All @@ -61,6 +99,17 @@ where
Ok(bitcoin::FeeRate::from_sat_per_kwu(sat_per_kwu as _))
}

pub fn feerate_to_sat_per_byte<S>(
fee_rate: &bitcoin::FeeRate,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let sat_per_vb = fee_rate.to_sat_per_kwu() as f32 / (1000.0 / 4.0);
sat_per_vb.serialize(serializer)
}

pub fn weight_from_vb<'de, D>(deserializer: D) -> Result<bitcoin::Weight, D::Error>
where
D: Deserializer<'de>,
Expand All @@ -72,6 +121,13 @@ where
Ok(weight)
}

pub fn weight_to_vb<S>(weight: &bitcoin::Weight, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
weight.to_vbytes_floor().serialize(serializer)
}

pub fn amount_from_btc<'de, D>(deserializer: D) -> Result<bitcoin::Amount, D::Error>
where
D: Deserializer<'de>,
Expand All @@ -80,6 +136,13 @@ where
bitcoin::Amount::from_btc(btc).map_err(serde::de::Error::custom)
}

pub fn amount_to_btc<S>(amount: &bitcoin::Amount, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
amount.to_btc().serialize(serializer)
}

pub fn amount_from_sats<'de, D>(deserializer: D) -> Result<bitcoin::Amount, D::Error>
where
D: Deserializer<'de>,
Expand All @@ -88,6 +151,13 @@ where
Ok(bitcoin::Amount::from_sat(sats))
}

pub fn amount_to_sats<S>(amount: &bitcoin::Amount, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
amount.to_sat().serialize(serializer)
}

pub fn amount_from_maybe_negative_sats<'de, D>(deserializer: D) -> Result<bitcoin::Amount, D::Error>
where
D: Deserializer<'de>,
Expand All @@ -96,6 +166,19 @@ where
Ok(bitcoin::Amount::from_sat(sats))
}

/// Note that [`amount_from_maybe_negative_sats`] takes the absolute value of the wire number, so a
/// negative balance cannot be recovered here and is always written back as non-negative.
pub fn amount_to_maybe_negative_sats<S>(
amount: &bitcoin::Amount,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let sats = i64::try_from(amount.to_sat()).map_err(serde::ser::Error::custom)?;
sats.serialize(serializer)
}

pub fn all_inputs_confirmed_bool_from_height<'de, D>(deserializer: D) -> Result<bool, D::Error>
where
D: Deserializer<'de>,
Expand All @@ -110,6 +193,18 @@ where
}
}

/// Writes back the Electrum `height` field: `0` when all inputs are confirmed, `-1` otherwise.
pub fn all_inputs_confirmed_bool_to_height<S>(
all_inputs_confirmed: &bool,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let height: i64 = if *all_inputs_confirmed { 0 } else { -1 };
height.serialize(serializer)
}

pub fn result<'de, D>(deserializer: D) -> Result<Result<Value, Value>, D::Error>
where
D: Deserializer<'de>,
Expand Down
Loading