lwk/blockdata/
script.rs

1//! Liquid script
2
3use elements::{hex::ToHex, pset::serialize::Deserialize};
4
5use crate::{types::Hex, LwkError};
6use std::{fmt::Display, sync::Arc};
7
8/// A Liquid script
9#[derive(uniffi::Object)]
10#[uniffi::export(Display)]
11pub struct Script {
12    inner: elements::Script,
13}
14
15impl From<elements::Script> for Script {
16    fn from(inner: elements::Script) -> Self {
17        Self { inner }
18    }
19}
20
21impl From<Script> for elements::Script {
22    fn from(script: Script) -> elements::Script {
23        script.inner
24    }
25}
26
27impl From<&Script> for elements::Script {
28    fn from(script: &Script) -> elements::Script {
29        script.inner.clone()
30    }
31}
32
33impl Display for Script {
34    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35        write!(f, "{}", self.inner.to_hex())
36    }
37}
38
39#[uniffi::export]
40impl Script {
41    /// Construct a Script object from its hex representation.
42    /// To create the hex representation of a script use `to_string()`.
43    #[uniffi::constructor]
44    pub fn new(hex: &Hex) -> Result<Arc<Self>, LwkError> {
45        let inner = elements::Script::deserialize(hex.as_ref())?;
46        Ok(Arc::new(Self { inner }))
47    }
48
49    /// Return the consensus encoded bytes of the script.
50    pub fn bytes(&self) -> Vec<u8> {
51        self.inner.as_bytes().to_vec()
52    }
53
54    /// Return the string representation of the script showing op codes and their arguments.
55    /// For example: "OP_0 OP_PUSHBYTES_32 d2e99f0c38089c08e5e1080ff6658c6075afaa7699d384333d956c470881afde"
56    pub fn asm(&self) -> String {
57        self.inner.asm()
58    }
59
60    /// Whether a script pubkey is provably unspendable (like a burn script)
61    pub fn is_provably_unspendable(&self) -> bool {
62        self.inner.is_provably_unspendable()
63    }
64}
65
66/// Whether a script pubkey is provably segwit
67#[uniffi::export]
68pub fn is_provably_segwit(script_pubkey: &Script, redeem_script: &Option<Arc<Script>>) -> bool {
69    lwk_common::is_provably_segwit(
70        &script_pubkey.into(),
71        &redeem_script.as_ref().map(|s| s.as_ref().into()),
72    )
73}
74
75#[cfg(test)]
76mod tests {
77    use elements::hashes::hex::FromHex;
78
79    use super::{is_provably_segwit, Script};
80
81    #[test]
82    fn script() {
83        let script_str = "0020d2e99f0c38089c08e5e1080ff6658c6075afaa7699d384333d956c470881afde";
84
85        let script = Script::new(&script_str.parse().unwrap()).unwrap();
86        assert_eq!(script.to_string(), script_str);
87
88        let script_bytes = Vec::<u8>::from_hex(script_str).unwrap();
89        assert_eq!(script.bytes(), script_bytes);
90
91        assert_eq!(
92            script.asm(),
93            "OP_0 OP_PUSHBYTES_32 d2e99f0c38089c08e5e1080ff6658c6075afaa7699d384333d956c470881afde"
94        );
95
96        assert!(is_provably_segwit(&script, &None));
97
98        let burn = Script::new(&"6a".parse().unwrap()).unwrap();
99        assert!(burn.is_provably_unspendable());
100    }
101}