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_num(&mut self, num: i64) {
245 pub fn write_scriptint(out: &mut [u8; 8], n: i64) -> usize {
257 let mut len = 0;
258 if n == 0 {
259 return len;
260 }
261
262 let neg = n < 0;
263
264 let mut abs = n.unsigned_abs();
265 while abs > 0xFF {
266 out[len] = (abs & 0xFF) as u8;
267 len += 1;
268 abs >>= 8;
269 }
270 if abs & 0x80 != 0 {
273 out[len] = abs as u8;
274 len += 1;
275 out[len] = if neg { 0x80u8 } else { 0u8 };
276 len += 1;
277 }
278 else {
280 abs |= if neg { 0x80 } else { 0 };
281 out[len] = abs as u8;
282 len += 1;
283 }
284 len
285 }
286
287 match num {
288 -1 => self.push(OP_PUSHNUM_NEG1),
289 0 => self.push(OP_PUSHBYTES_0),
290 1..=16 => self.push(num as u8 + (OP_PUSHNUM_1 - 1)),
291 _ => {
292 let mut buf = [0u8; 8];
293 let len = write_scriptint(&mut buf, num);
294 self.push_slice(&buf[..len]);
295 }
296 };
297 }
298
299 pub fn push_slice(&mut self, data: &[u8]) {
305 match data.len() as u64 {
307 n if n < OP_PUSHDATA1 as u64 => {
308 self.push(n as u8);
309 }
310 n if n < 0x100 => {
311 self.push(OP_PUSHDATA1);
312 self.push(n as u8);
313 }
314 n if n < 0x10000 => {
315 self.push(OP_PUSHDATA2);
316 self.push((n % 0x100) as u8);
317 self.push((n / 0x100) as u8);
318 }
319 n if n < 0x100000000 => {
320 self.push(OP_PUSHDATA4);
321 self.push((n % 0x100) as u8);
322 self.push(((n / 0x100) % 0x100) as u8);
323 self.push(((n / 0x10000) % 0x100) as u8);
324 self.push((n / 0x1000000) as u8);
325 }
326 _ => panic!("tried to put a 4bn+ sized object into a script!"),
327 }
328 self.extend(data);
330 }
331
332 #[inline]
333 pub(crate) fn push(&mut self, data: u8) { self.0.push(data).expect("script exceeds 4GB") }
334
335 #[inline]
336 pub(crate) fn extend(&mut self, data: &[u8]) {
337 self.0.extend(data.iter().copied()).expect("script exceeds 4GB")
338 }
339
340 pub fn len_for_slice(len: usize) -> usize {
343 len + match len {
344 0..=0x4b => 1,
345 0x4c..=0xff => 2,
346 0x100..=0xffff => 3,
347 _ => 5,
349 }
350 }
351
352 pub fn len_var_int(&self) -> VarInt { VarInt(self.len() as u64) }
353
354 pub fn into_vec(self) -> Vec<u8> { self.0.release() }
355
356 pub(crate) fn as_var_int_bytes(&self) -> &VarIntBytes { &self.0 }
357}
358
359#[cfg(feature = "serde")]
360mod _serde {
361 use amplify::hex::{FromHex, ToHex};
362 use serde::de::Error;
363 use serde::{Deserialize, Deserializer, Serialize, Serializer};
364
365 use super::*;
366
367 impl Serialize for ScriptBytes {
368 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
369 where S: Serializer {
370 if serializer.is_human_readable() {
371 serializer.serialize_str(&self.to_hex())
372 } else {
373 serializer.serialize_bytes(self.as_slice())
374 }
375 }
376 }
377
378 impl<'de> Deserialize<'de> for ScriptBytes {
379 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
380 where D: Deserializer<'de> {
381 if deserializer.is_human_readable() {
382 String::deserialize(deserializer).and_then(|string| {
383 Self::from_hex(&string).map_err(|_| D::Error::custom("wrong hex data"))
384 })
385 } else {
386 let bytes = Vec::<u8>::deserialize(deserializer)?;
387 Self::try_from(bytes)
388 .map_err(|_| D::Error::custom("invalid script length exceeding 4GB"))
389 }
390 }
391 }
392}
393
394#[cfg(test)]
395mod test {
396 #![cfg_attr(coverage_nightly, coverage(off))]
397
398 use amplify::hex::ToHex;
399
400 use super::*;
401
402 #[test]
403 fn script_index() {
404 let mut script = ScriptPubkey::op_return(&[0u8; 40]);
405 assert_eq!(script[0], OP_RETURN);
406 assert_eq!(&script[..2], &[OP_RETURN, OP_PUSHBYTES_40]);
407 assert_eq!(&script[40..], &[0u8, 0u8]);
408 assert_eq!(&script[2..4], &[0u8, 0u8]);
409 assert_eq!(&script[2..=3], &[0u8, 0u8]);
410
411 script[0] = 0xFF;
412 script[..2].copy_from_slice(&[0xFF, 0xFF]);
413 script[40..].copy_from_slice(&[0xFF, 0xFF]);
414 script[2..4].copy_from_slice(&[0xFF, 0xFF]);
415 script[2..=3].copy_from_slice(&[0xFF, 0xFF]);
416
417 assert_eq!(script[0], 0xFF);
418 assert_eq!(&script[..2], &[0xFF, 0xFF]);
419 assert_eq!(&script[40..], &[0xFF, 0xFF]);
420 assert_eq!(&script[2..4], &[0xFF, 0xFF]);
421 assert_eq!(&script[2..=3], &[0xFF, 0xFF]);
422
423 assert_eq!(
424 &script.to_hex(),
425 "ffffffff000000000000000000000000000000000000000000000000000000000000000000000000ffff"
426 );
427 }
428}