1use amplify::confinement;
23use amplify::confinement::Confined;
24
25use crate::opcodes::*;
26use crate::{ScriptHash, VarInt, VarIntBytes, WitnessVer, LIB_NAME_BITCOIN};
27
28#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From, Default)]
29#[wrapper(Deref, AsSlice, Hex)]
30#[wrapper_mut(DerefMut, AsSliceMut)]
31#[derive(StrictType, StrictEncode, StrictDecode)]
32#[strict_type(lib = LIB_NAME_BITCOIN)]
33#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
34pub struct SigScript(ScriptBytes);
35
36impl TryFrom<Vec<u8>> for SigScript {
37 type Error = confinement::Error;
38 fn try_from(script_bytes: Vec<u8>) -> Result<Self, Self::Error> {
39 ScriptBytes::try_from(script_bytes).map(Self)
40 }
41}
42
43impl SigScript {
44 #[inline]
45 pub fn empty() -> Self { SigScript::default() }
46
47 #[inline]
48 pub fn new() -> Self { Self::default() }
49
50 #[inline]
51 pub fn with_capacity(capacity: usize) -> Self {
52 Self(ScriptBytes::from(Confined::with_capacity(capacity)))
53 }
54
55 #[inline]
58 pub fn from_checked(script_bytes: Vec<u8>) -> Self {
59 Self(ScriptBytes::from_checked(script_bytes))
60 }
61
62 #[inline]
63 pub fn as_script_bytes(&self) -> &ScriptBytes { &self.0 }
64}
65
66#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From, Default)]
67#[wrapper(Deref, AsSlice, Hex)]
68#[wrapper_mut(DerefMut, AsSliceMut)]
69#[derive(StrictType, StrictEncode, StrictDecode)]
70#[strict_type(lib = LIB_NAME_BITCOIN)]
71#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
72pub struct ScriptPubkey(ScriptBytes);
73
74impl TryFrom<Vec<u8>> for ScriptPubkey {
75 type Error = confinement::Error;
76 fn try_from(script_bytes: Vec<u8>) -> Result<Self, Self::Error> {
77 ScriptBytes::try_from(script_bytes).map(Self)
78 }
79}
80
81impl ScriptPubkey {
82 #[inline]
83 pub fn new() -> Self { Self::default() }
84
85 #[inline]
86 pub fn with_capacity(capacity: usize) -> Self {
87 Self(ScriptBytes::from(Confined::with_capacity(capacity)))
88 }
89
90 #[inline]
93 pub fn from_checked(script_bytes: Vec<u8>) -> Self {
94 Self(ScriptBytes::from_checked(script_bytes))
95 }
96
97 pub fn p2pkh(hash: impl Into<[u8; 20]>) -> Self {
98 let mut script = Self::with_capacity(25);
99 script.push_opcode(OpCode::Dup);
100 script.push_opcode(OpCode::Hash160);
101 script.push_slice(&hash.into());
102 script.push_opcode(OpCode::EqualVerify);
103 script.push_opcode(OpCode::CheckSig);
104 script
105 }
106
107 pub fn p2sh(hash: impl Into<[u8; 20]>) -> Self {
108 let mut script = Self::with_capacity(23);
109 script.push_opcode(OpCode::Hash160);
110 script.push_slice(&hash.into());
111 script.push_opcode(OpCode::Equal);
112 script
113 }
114
115 pub fn op_return(data: &[u8]) -> Self {
116 let mut script = Self::with_capacity(ScriptBytes::len_for_slice(data.len()) + 1);
117 script.push_opcode(OpCode::Return);
118 script.push_slice(data);
119 script
120 }
121
122 #[inline]
124 pub fn is_p2pkh(&self) -> bool {
125 self.0.len() == 25
126 && self.0[0] == OP_DUP
127 && self.0[1] == OP_HASH160
128 && self.0[2] == OP_PUSHBYTES_20
129 && self.0[23] == OP_EQUALVERIFY
130 && self.0[24] == OP_CHECKSIG
131 }
132
133 #[inline]
135 pub fn is_p2sh(&self) -> bool {
136 self.0.len() == 23
137 && self.0[0] == OP_HASH160
138 && self.0[1] == OP_PUSHBYTES_20
139 && self.0[22] == OP_EQUAL
140 }
141
142 #[inline]
143 pub fn is_op_return(&self) -> bool { !self.is_empty() && self[0] == OpCode::Return as u8 }
144
145 #[inline]
147 pub fn push_opcode(&mut self, op_code: OpCode) { self.0.push(op_code as u8) }
148
149 #[inline]
150 pub fn as_script_bytes(&self) -> &ScriptBytes { &self.0 }
151}
152
153#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From, Default)]
154#[wrapper(Deref, AsSlice, Hex)]
155#[wrapper_mut(DerefMut, AsSliceMut)]
156#[derive(StrictType, StrictEncode, StrictDecode)]
157#[strict_type(lib = LIB_NAME_BITCOIN)]
158#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
159pub struct RedeemScript(ScriptBytes);
160
161impl TryFrom<Vec<u8>> for RedeemScript {
162 type Error = confinement::Error;
163 fn try_from(script_bytes: Vec<u8>) -> Result<Self, Self::Error> {
164 ScriptBytes::try_from(script_bytes).map(Self)
165 }
166}
167
168impl RedeemScript {
169 #[inline]
170 pub fn new() -> Self { Self::default() }
171
172 #[inline]
173 pub fn with_capacity(capacity: usize) -> Self {
174 Self(ScriptBytes::from(Confined::with_capacity(capacity)))
175 }
176
177 #[inline]
180 pub fn from_checked(script_bytes: Vec<u8>) -> Self {
181 Self(ScriptBytes::from_checked(script_bytes))
182 }
183
184 pub fn p2sh_wpkh(hash: impl Into<[u8; 20]>) -> Self {
185 Self::with_witness_program_unchecked(WitnessVer::V0, &hash.into())
186 }
187
188 pub fn p2sh_wsh(hash: impl Into<[u8; 32]>) -> Self {
189 Self::with_witness_program_unchecked(WitnessVer::V0, &hash.into())
190 }
191
192 fn with_witness_program_unchecked(ver: WitnessVer, prog: &[u8]) -> Self {
193 let mut script = Self::with_capacity(ScriptBytes::len_for_slice(prog.len()) + 2);
194 script.push_opcode(ver.op_code());
195 script.push_slice(prog);
196 script
197 }
198
199 pub fn is_p2sh_wpkh(&self) -> bool {
200 self.len() == 22 && self[0] == WitnessVer::V0.op_code() as u8 && self[1] == OP_PUSHBYTES_20
201 }
202
203 pub fn is_p2sh_wsh(&self) -> bool {
204 self.len() == 34 && self[0] == WitnessVer::V0.op_code() as u8 && self[1] == OP_PUSHBYTES_32
205 }
206
207 #[inline]
209 pub fn push_opcode(&mut self, op_code: OpCode) { self.0.push(op_code as u8); }
210
211 pub fn to_script_pubkey(&self) -> ScriptPubkey { ScriptPubkey::p2sh(ScriptHash::from(self)) }
212
213 #[inline]
214 pub fn as_script_bytes(&self) -> &ScriptBytes { &self.0 }
215}
216
217#[derive(Wrapper, WrapperMut, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Default, Debug, From)]
218#[wrapper(Deref, AsSlice, Hex)]
219#[wrapper_mut(DerefMut, AsSliceMut)]
220#[derive(StrictType, StrictEncode, StrictDecode)]
221#[strict_type(lib = LIB_NAME_BITCOIN)]
222pub struct ScriptBytes(VarIntBytes);
223
224impl TryFrom<Vec<u8>> for ScriptBytes {
225 type Error = confinement::Error;
226 fn try_from(script_bytes: Vec<u8>) -> Result<Self, Self::Error> {
227 Confined::try_from(script_bytes).map(Self)
228 }
229}
230
231impl ScriptBytes {
232 #[inline]
235 pub fn from_checked(script_bytes: Vec<u8>) -> Self {
236 Self(Confined::try_from(script_bytes).expect("script exceeding 4GB"))
237 }
238
239 pub fn push_slice(&mut self, data: &[u8]) {
246 match data.len() as u64 {
248 n if n < OP_PUSHDATA1 as u64 => {
249 self.push(n as u8);
250 }
251 n if n < 0x100 => {
252 self.push(OP_PUSHDATA1);
253 self.push(n as u8);
254 }
255 n if n < 0x10000 => {
256 self.push(OP_PUSHDATA2);
257 self.push((n % 0x100) as u8);
258 self.push((n / 0x100) as u8);
259 }
260 n if n < 0x100000000 => {
261 self.push(OP_PUSHDATA4);
262 self.push((n % 0x100) as u8);
263 self.push(((n / 0x100) % 0x100) as u8);
264 self.push(((n / 0x10000) % 0x100) as u8);
265 self.push((n / 0x1000000) as u8);
266 }
267 _ => panic!("tried to put a 4bn+ sized object into a script!"),
268 }
269 self.extend(data);
271 }
272
273 #[inline]
274 pub(crate) fn push(&mut self, data: u8) { self.0.push(data).expect("script exceeds 4GB") }
275
276 #[inline]
277 pub(crate) fn extend(&mut self, data: &[u8]) {
278 self.0.extend(data.iter().copied()).expect("script exceeds 4GB")
279 }
280
281 pub fn len_for_slice(len: usize) -> usize {
284 len + match len {
285 0..=0x4b => 1,
286 0x4c..=0xff => 2,
287 0x100..=0xffff => 3,
288 _ => 5,
290 }
291 }
292
293 pub fn len_var_int(&self) -> VarInt { VarInt(self.len() as u64) }
294
295 pub fn into_vec(self) -> Vec<u8> { self.0.release() }
296
297 pub(crate) fn as_var_int_bytes(&self) -> &VarIntBytes { &self.0 }
298}
299
300#[cfg(feature = "serde")]
301mod _serde {
302 use amplify::hex::{FromHex, ToHex};
303 use serde::de::Error;
304 use serde::{Deserialize, Deserializer, Serialize, Serializer};
305
306 use super::*;
307
308 impl Serialize for ScriptBytes {
309 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
310 where S: Serializer {
311 if serializer.is_human_readable() {
312 serializer.serialize_str(&self.to_hex())
313 } else {
314 serializer.serialize_bytes(self.as_slice())
315 }
316 }
317 }
318
319 impl<'de> Deserialize<'de> for ScriptBytes {
320 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
321 where D: Deserializer<'de> {
322 if deserializer.is_human_readable() {
323 String::deserialize(deserializer).and_then(|string| {
324 Self::from_hex(&string).map_err(|_| D::Error::custom("wrong hex data"))
325 })
326 } else {
327 let bytes = Vec::<u8>::deserialize(deserializer)?;
328 Self::try_from(bytes)
329 .map_err(|_| D::Error::custom("invalid script length exceeding 4GB"))
330 }
331 }
332 }
333}
334
335#[cfg(test)]
336mod test {
337 #![cfg_attr(coverage_nightly, coverage(off))]
338
339 use amplify::hex::ToHex;
340
341 use super::*;
342
343 #[test]
344 fn script_index() {
345 let mut script = ScriptPubkey::op_return(&[0u8; 40]);
346 assert_eq!(script[0], OP_RETURN);
347 assert_eq!(&script[..2], &[OP_RETURN, OP_PUSHBYTES_40]);
348 assert_eq!(&script[40..], &[0u8, 0u8]);
349 assert_eq!(&script[2..4], &[0u8, 0u8]);
350 assert_eq!(&script[2..=3], &[0u8, 0u8]);
351
352 script[0] = 0xFF;
353 script[..2].copy_from_slice(&[0xFF, 0xFF]);
354 script[40..].copy_from_slice(&[0xFF, 0xFF]);
355 script[2..4].copy_from_slice(&[0xFF, 0xFF]);
356 script[2..=3].copy_from_slice(&[0xFF, 0xFF]);
357
358 assert_eq!(script[0], 0xFF);
359 assert_eq!(&script[..2], &[0xFF, 0xFF]);
360 assert_eq!(&script[40..], &[0xFF, 0xFF]);
361 assert_eq!(&script[2..4], &[0xFF, 0xFF]);
362 assert_eq!(&script[2..=3], &[0xFF, 0xFF]);
363
364 assert_eq!(
365 &script.to_hex(),
366 "ffffffff000000000000000000000000000000000000000000000000000000000000000000000000ffff"
367 );
368 }
369}