|
| 1 | +// SPDX-License-Identifier: CC0-1.0 |
| 2 | + |
| 3 | +//! BIP-375: Support for silent payments in PSBTs. |
| 4 | +//! |
| 5 | +//! This module provides type-safe wrapper for BIP-374 dleq proof field. |
| 6 | +
|
| 7 | +use core::fmt; |
| 8 | + |
| 9 | +use crate::prelude::*; |
| 10 | +use crate::serialize::{Deserialize, Serialize}; |
| 11 | + |
| 12 | +/// A 64-byte DLEQ proof (BIP-374). |
| 13 | +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
| 14 | +pub struct DleqProof(pub [u8; 64]); |
| 15 | + |
| 16 | +#[cfg(feature = "serde")] |
| 17 | +impl actual_serde::Serialize for DleqProof { |
| 18 | + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |
| 19 | + where |
| 20 | + S: actual_serde::Serializer, |
| 21 | + { |
| 22 | + if serializer.is_human_readable() { |
| 23 | + serializer.serialize_str(&bitcoin::hex::DisplayHex::to_lower_hex_string(&self.0[..])) |
| 24 | + } else { |
| 25 | + serializer.serialize_bytes(&self.0[..]) |
| 26 | + } |
| 27 | + } |
| 28 | +} |
| 29 | + |
| 30 | +#[cfg(feature = "serde")] |
| 31 | +impl<'de> actual_serde::Deserialize<'de> for DleqProof { |
| 32 | + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |
| 33 | + where |
| 34 | + D: actual_serde::Deserializer<'de>, |
| 35 | + { |
| 36 | + if deserializer.is_human_readable() { |
| 37 | + struct HexVisitor; |
| 38 | + impl actual_serde::de::Visitor<'_> for HexVisitor { |
| 39 | + type Value = DleqProof; |
| 40 | + |
| 41 | + fn expecting(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
| 42 | + f.write_str("a 64-byte hex string") |
| 43 | + } |
| 44 | + |
| 45 | + fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> |
| 46 | + where |
| 47 | + E: actual_serde::de::Error, |
| 48 | + { |
| 49 | + use bitcoin::hex::FromHex; |
| 50 | + let vec = Vec::<u8>::from_hex(s).map_err(E::custom)?; |
| 51 | + DleqProof::try_from(vec).map_err(|e| { |
| 52 | + E::custom(format!("expected {} bytes, got {}", e.expected, e.got)) |
| 53 | + }) |
| 54 | + } |
| 55 | + } |
| 56 | + deserializer.deserialize_str(HexVisitor) |
| 57 | + } else { |
| 58 | + struct BytesVisitor; |
| 59 | + impl actual_serde::de::Visitor<'_> for BytesVisitor { |
| 60 | + type Value = DleqProof; |
| 61 | + |
| 62 | + fn expecting(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { |
| 63 | + f.write_str("64 bytes") |
| 64 | + } |
| 65 | + |
| 66 | + fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E> |
| 67 | + where |
| 68 | + E: actual_serde::de::Error, |
| 69 | + { |
| 70 | + DleqProof::try_from(v).map_err(|e| { |
| 71 | + E::custom(format!("expected {} bytes, got {}", e.expected, e.got)) |
| 72 | + }) |
| 73 | + } |
| 74 | + } |
| 75 | + deserializer.deserialize_bytes(BytesVisitor) |
| 76 | + } |
| 77 | + } |
| 78 | +} |
| 79 | + |
| 80 | +impl DleqProof { |
| 81 | + /// Creates a new [`DleqProof`] from a 64-byte array. |
| 82 | + pub fn new(bytes: [u8; 64]) -> Self { DleqProof(bytes) } |
| 83 | + |
| 84 | + /// Returns the inner 64-byte array. |
| 85 | + pub fn as_bytes(&self) -> &[u8; 64] { &self.0 } |
| 86 | +} |
| 87 | + |
| 88 | +impl From<[u8; 64]> for DleqProof { |
| 89 | + fn from(bytes: [u8; 64]) -> Self { DleqProof(bytes) } |
| 90 | +} |
| 91 | + |
| 92 | +impl AsRef<[u8]> for DleqProof { |
| 93 | + fn as_ref(&self) -> &[u8] { &self.0 } |
| 94 | +} |
| 95 | + |
| 96 | +impl TryFrom<&[u8]> for DleqProof { |
| 97 | + type Error = InvalidLengthError; |
| 98 | + |
| 99 | + fn try_from(slice: &[u8]) -> Result<Self, Self::Error> { |
| 100 | + <[u8; 64]>::try_from(slice) |
| 101 | + .map(DleqProof) |
| 102 | + .map_err(|_| InvalidLengthError { got: slice.len(), expected: 64 }) |
| 103 | + } |
| 104 | +} |
| 105 | + |
| 106 | +impl TryFrom<Vec<u8>> for DleqProof { |
| 107 | + type Error = InvalidLengthError; |
| 108 | + |
| 109 | + fn try_from(v: Vec<u8>) -> Result<Self, Self::Error> { Self::try_from(v.as_slice()) } |
| 110 | +} |
| 111 | + |
| 112 | +impl Serialize for DleqProof { |
| 113 | + fn serialize(&self) -> Vec<u8> { self.0.to_vec() } |
| 114 | +} |
| 115 | + |
| 116 | +impl Deserialize for DleqProof { |
| 117 | + fn deserialize(bytes: &[u8]) -> Result<Self, crate::serialize::Error> { |
| 118 | + DleqProof::try_from(bytes).map_err(|e| crate::serialize::Error::InvalidDleqProof { |
| 119 | + got: e.got, |
| 120 | + expected: e.expected, |
| 121 | + }) |
| 122 | + } |
| 123 | +} |
| 124 | + |
| 125 | +/// Error returned when a byte array has an invalid length for a dleq proof. |
| 126 | +#[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 127 | +pub struct InvalidLengthError { |
| 128 | + /// The length that was provided. |
| 129 | + pub got: usize, |
| 130 | + /// The expected length. |
| 131 | + pub expected: usize, |
| 132 | +} |
| 133 | + |
| 134 | +impl fmt::Display for InvalidLengthError { |
| 135 | + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 136 | + write!(f, "invalid length for BIP-375 type: got {}, expected {}", self.got, self.expected) |
| 137 | + } |
| 138 | +} |
| 139 | + |
| 140 | +#[cfg(feature = "std")] |
| 141 | +impl std::error::Error for InvalidLengthError {} |
0 commit comments