Skip to main content

satellite_bitcoin/
script.rs

1use borsh::io::{Error as IoError, ErrorKind as IoErrorKind};
2use borsh::{BorshDeserialize, BorshSerialize};
3
4/// Maximum script length accepted by the protocol.
5pub const MAX_BTC_SCRIPT_BYTES: usize =
6    satellite_bitcoin_transactions::constants::MAX_BTC_SCRIPT_BYTES;
7
8/// Strongly-typed Bitcoin script pubkey with length validation.
9#[derive(Debug, Clone, PartialEq, Eq)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub struct ScriptPubkey(Vec<u8>);
12
13impl ScriptPubkey {
14    #[inline]
15    pub fn as_bytes(&self) -> &[u8] {
16        &self.0
17    }
18
19    #[inline]
20    pub fn into_vec(self) -> Vec<u8> {
21        self.0
22    }
23
24    pub fn is_empty(&self) -> bool {
25        self.0.is_empty()
26    }
27}
28
29impl TryFrom<Vec<u8>> for ScriptPubkey {
30    type Error = ScriptPubkeyError;
31    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
32        if value.is_empty() || value.len() > MAX_BTC_SCRIPT_BYTES {
33            return Err(ScriptPubkeyError::InvalidLength);
34        }
35        Ok(ScriptPubkey(value))
36    }
37}
38
39impl BorshSerialize for ScriptPubkey {
40    fn serialize<W: borsh::io::Write>(&self, writer: &mut W) -> std::result::Result<(), IoError> {
41        borsh::BorshSerialize::serialize(&self.0, writer)
42    }
43}
44
45impl BorshDeserialize for ScriptPubkey {
46    fn deserialize_reader<R: borsh::io::Read>(
47        reader: &mut R,
48    ) -> std::result::Result<Self, IoError> {
49        let value = Vec::<u8>::deserialize_reader(reader)?;
50        if value.is_empty() || value.len() > MAX_BTC_SCRIPT_BYTES {
51            return Err(IoError::new(
52                IoErrorKind::InvalidData,
53                "script_pubkey length invalid",
54            ));
55        }
56        Ok(ScriptPubkey(value))
57    }
58}
59
60impl From<ScriptPubkey> for bitcoin::ScriptBuf {
61    fn from(value: ScriptPubkey) -> Self {
62        bitcoin::ScriptBuf::from_bytes(value.0)
63    }
64}
65
66impl TryFrom<bitcoin::ScriptBuf> for ScriptPubkey {
67    type Error = ScriptPubkeyError;
68    fn try_from(value: bitcoin::ScriptBuf) -> Result<Self, Self::Error> {
69        if value.is_empty() || value.to_bytes().len() > MAX_BTC_SCRIPT_BYTES {
70            return Err(ScriptPubkeyError::InvalidLength);
71        }
72        Ok(ScriptPubkey(value.to_bytes()))
73    }
74}
75
76impl Default for ScriptPubkey {
77    fn default() -> Self {
78        ScriptPubkey(Vec::new())
79    }
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83pub enum ScriptPubkeyError {
84    InvalidLength,
85}
86
87impl core::fmt::Display for ScriptPubkeyError {
88    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
89        match self {
90            ScriptPubkeyError::InvalidLength => write!(f, "invalid script_pubkey length"),
91        }
92    }
93}