Skip to main content

bsv_rs/script/
unlocking_script.rs

1//! Unlocking Script (scriptSig).
2//!
3//! The UnlockingScript class represents an unlocking script in a Bitcoin SV transaction.
4//! It extends the Script class and is used specifically for input scripts that unlock funds.
5
6use super::chunk::ScriptChunk;
7use super::script::Script;
8use crate::Result;
9
10/// Represents an unlocking script (input script / scriptSig).
11///
12/// An unlocking script provides the data needed to satisfy a locking script's
13/// conditions in order to spend the output.
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct UnlockingScript(Script);
16
17impl UnlockingScript {
18    /// Creates a new empty unlocking script.
19    pub fn new() -> Self {
20        Self(Script::new())
21    }
22
23    /// Creates an unlocking script from a vector of chunks.
24    pub fn from_chunks(chunks: Vec<ScriptChunk>) -> Self {
25        Self(Script::from_chunks(chunks))
26    }
27
28    /// Constructs an UnlockingScript from an ASM formatted string.
29    pub fn from_asm(asm: &str) -> Result<Self> {
30        Ok(Self(Script::from_asm(asm)?))
31    }
32
33    /// Constructs an UnlockingScript from a hexadecimal string.
34    pub fn from_hex(hex: &str) -> Result<Self> {
35        Ok(Self(Script::from_hex(hex)?))
36    }
37
38    /// Constructs an UnlockingScript from binary data.
39    pub fn from_binary(bin: &[u8]) -> Result<Self> {
40        Ok(Self(Script::from_binary(bin)?))
41    }
42
43    /// Constructs an UnlockingScript from a Script.
44    pub fn from_script(script: Script) -> Self {
45        Self(script)
46    }
47
48    /// Returns a reference to the underlying Script.
49    pub fn as_script(&self) -> &Script {
50        &self.0
51    }
52
53    /// Converts this UnlockingScript into its underlying Script.
54    pub fn into_script(self) -> Script {
55        self.0
56    }
57
58    /// Serializes to ASM string.
59    pub fn to_asm(&self) -> String {
60        self.0.to_asm()
61    }
62
63    /// Serializes to hex string.
64    pub fn to_hex(&self) -> String {
65        self.0.to_hex()
66    }
67
68    /// Serializes to binary.
69    pub fn to_binary(&self) -> Vec<u8> {
70        self.0.to_binary()
71    }
72
73    /// Returns the chunks.
74    pub fn chunks(&self) -> Vec<ScriptChunk> {
75        self.0.chunks()
76    }
77
78    /// Returns the number of chunks.
79    pub fn len(&self) -> usize {
80        self.0.len()
81    }
82
83    /// Returns true if empty.
84    pub fn is_empty(&self) -> bool {
85        self.0.is_empty()
86    }
87
88    /// Checks if push-only.
89    pub fn is_push_only(&self) -> bool {
90        self.0.is_push_only()
91    }
92
93    /// Returns false (this is not a locking script).
94    pub fn is_locking_script(&self) -> bool {
95        false
96    }
97
98    /// Returns true (this is an unlocking script).
99    pub fn is_unlocking_script(&self) -> bool {
100        true
101    }
102}
103
104impl Default for UnlockingScript {
105    fn default() -> Self {
106        Self::new()
107    }
108}
109
110impl From<Script> for UnlockingScript {
111    fn from(script: Script) -> Self {
112        Self(script)
113    }
114}
115
116impl From<UnlockingScript> for Script {
117    fn from(unlocking: UnlockingScript) -> Self {
118        unlocking.0
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    #[test]
127    fn test_is_unlocking_script() {
128        let script = UnlockingScript::new();
129        assert!(!script.is_locking_script());
130        assert!(script.is_unlocking_script());
131    }
132
133    #[test]
134    fn test_from_hex() {
135        let script = UnlockingScript::from_hex("00").unwrap();
136        assert_eq!(script.to_hex(), "00");
137    }
138
139    #[test]
140    fn test_from_asm() {
141        let asm = "0 abcdef1234";
142        let script = UnlockingScript::from_asm(asm).unwrap();
143        assert!(script.is_unlocking_script());
144    }
145
146    #[test]
147    fn test_conversion() {
148        let script = Script::from_hex("00").unwrap();
149        let unlocking = UnlockingScript::from_script(script.clone());
150        let back: Script = unlocking.into();
151        assert_eq!(back.to_hex(), script.to_hex());
152    }
153
154    #[test]
155    fn test_is_push_only() {
156        // Unlocking scripts should typically be push-only
157        let script = UnlockingScript::from_asm("0 OP_1 OP_2").unwrap();
158        assert!(script.is_push_only());
159    }
160}