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