anychain_bitcoin/
witness_program.rs

1//!
2//! WitnessProgram
3//!
4//! This module contains the representation of a Bitcoin witness program and utility functions
5//! related to the representation of such programs.
6//!
7//! If the version byte is 0, and the witness program is 20 bytes:
8//! - It is interpreted as a pay-to-witness-public-key-hash (P2WPKH) program.
9//! - The witness must consist of exactly 2 items (≤ 520 bytes each). The first one a signature, and the second one a public key.
10//! - The HASH160 of the public key must match the 20-byte witness program.
11//! - After normal script evaluation, the signature is verified against the public key with CHECKSIG operation. The verification must result in a single TRUE on the stack.
12//!
13//! If the version byte is 0, and the witness program is 32 bytes:
14//! - It is interpreted as a pay-to-witness-script-hash (P2WSH) program.
15//! - The witness must consist of an input stack to feed to the script, followed by a serialized script (witnessScript).
16//! - The witnessScript (≤ 10,000 bytes) is popped off the initial witness stack. SHA256 of the witnessScript must match the 32-byte witness program.
17//! - The witnessScript is deserialized, and executed after normal script evaluation with the remaining witness stack (≤ 520 bytes for each stack item).
18//! - The script must not fail, and result in exactly a single TRUE on the stack.
19//!
20//! If the version byte is 0, but the witness program is neither 20 nor 32 bytes, the script must fail.
21//!
22
23use anychain_core::no_std::*;
24use anychain_core::{hex, AddressError, TransactionError};
25
26use core::str::FromStr;
27
28#[derive(Debug, Error, PartialEq, Eq)]
29pub enum WitnessProgramError {
30    #[error("invalid program length {0}")]
31    InvalidProgramLength(usize),
32
33    #[error("invalid program length {0} for script version {1}")]
34    InvalidProgramLengthForVersion(usize, u8),
35
36    #[error("invalid version {0}")]
37    InvalidVersion(u8),
38
39    #[error("invalid program length: {{ expected: {0:?}, found: {1:?} }}")]
40    MismatchedProgramLength(usize, usize),
41
42    #[error("error decoding program from hex string")]
43    ProgramDecodingError,
44}
45
46impl From<WitnessProgramError> for AddressError {
47    fn from(error: WitnessProgramError) -> Self {
48        AddressError::Crate("WitnessProgram", format!("{:?}", error))
49    }
50}
51
52impl From<WitnessProgramError> for TransactionError {
53    fn from(error: WitnessProgramError) -> Self {
54        TransactionError::Crate("WitnessProgram", format!("{:?}", error))
55    }
56}
57
58#[derive(Debug, Clone, PartialEq, Eq)]
59pub struct WitnessProgram {
60    /// The version byte
61    pub version: u8,
62    /// The witness program bytes
63    pub program: Vec<u8>,
64}
65
66impl WitnessProgram {
67    /// Returns a new witness program given a program with a version, data size, and data.
68    pub fn new(program: &[u8]) -> Result<Self, WitnessProgramError> {
69        if program.len() < 2 {
70            return Err(WitnessProgramError::InvalidProgramLength(program.len()));
71        }
72
73        // https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#Decoding
74        let data_size = program[1] as usize;
75        let data = program[2..].to_vec();
76        if data_size != data.len() {
77            return Err(WitnessProgramError::MismatchedProgramLength(
78                data.len(),
79                data_size,
80            ));
81        }
82
83        let program = Self {
84            version: program[0],
85            program: data,
86        };
87        match program.validate() {
88            Ok(()) => Ok(program),
89            Err(e) => Err(e),
90        }
91    }
92
93    pub fn validate(&self) -> Result<(), WitnessProgramError> {
94        if self.program.len() < 2 || self.program.len() > 40 {
95            return Err(WitnessProgramError::InvalidProgramLength(
96                self.program.len(),
97            ));
98        }
99
100        if self.version > 16 {
101            return Err(WitnessProgramError::InvalidVersion(self.version));
102        }
103
104        // P2SH_P2WPKH start with 0x0014
105        // P2SH_P2WSH starts with 0x0020
106        // https://bitcoincore.org/en/segwit_wallet_dev/#creation-of-p2sh-p2wpkh-address
107        if self.version == 0 && !(self.program.len() == 20 || self.program.len() == 32) {
108            return Err(WitnessProgramError::InvalidProgramLengthForVersion(
109                self.program.len(),
110                self.version,
111            ));
112        }
113
114        Ok(())
115    }
116
117    /// Returns the witness program's scriptpubkey as a byte vector.
118    pub fn to_scriptpubkey(&self) -> Vec<u8> {
119        let mut output = Vec::with_capacity(self.program.len() + 2);
120        let encoded_version = if self.version > 0 {
121            self.version + 0x50
122        } else {
123            self.version
124        };
125        output.push(encoded_version);
126        output.push(self.program.len() as u8);
127        output.extend_from_slice(&self.program);
128        output
129    }
130}
131
132impl FromStr for WitnessProgram {
133    type Err = WitnessProgramError;
134
135    /// Returns a witness program given its hex representation.
136    fn from_str(s: &str) -> Result<Self, Self::Err> {
137        WitnessProgram::new(&match hex::decode(s) {
138            Ok(bytes) => bytes,
139            Err(_) => return Err(WitnessProgramError::ProgramDecodingError),
140        })
141    }
142}
143
144#[cfg(test)]
145mod tests {
146    use super::*;
147
148    fn test_from_str(program_str: &str, expected_version: u8, expected_program: &[u8]) {
149        let witness_program = WitnessProgram::from_str(program_str).unwrap();
150        assert_eq!(expected_version, witness_program.version);
151        assert_eq!(expected_program.to_vec(), witness_program.program);
152    }
153
154    fn test_to_scriptpubkey(version: u8, program: &[u8], expected_scriptpubkey: &[u8]) {
155        let witness_program = WitnessProgram {
156            version,
157            program: program.to_vec(),
158        };
159        assert_eq!(
160            expected_scriptpubkey.to_vec(),
161            witness_program.to_scriptpubkey()
162        );
163    }
164
165    mod p2sh_p2wpkh {
166        use super::*;
167
168        const VALID_P2SH_P2WPKH_PROGRAMS: [(&str, u8, &[u8], &[u8]); 1] = [(
169            "0014751e76e8199196d454941c45d1b3a323f1433bd6",
170            0x00,
171            &[
172                0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45, 0xd1, 0xb3,
173                0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6,
174            ],
175            &[
176                0x00, 0x14, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45,
177                0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6,
178            ],
179        )];
180
181        #[test]
182        fn from_str() {
183            VALID_P2SH_P2WPKH_PROGRAMS.iter().for_each(
184                |&(program_str, expected_version, expected_program, _)| {
185                    test_from_str(program_str, expected_version, expected_program);
186                },
187            );
188        }
189
190        #[test]
191        fn to_scriptpubkey() {
192            VALID_P2SH_P2WPKH_PROGRAMS.iter().for_each(
193                |&(_, version, program, expected_scriptpubkey)| {
194                    test_to_scriptpubkey(version, program, expected_scriptpubkey);
195                },
196            );
197        }
198    }
199
200    mod p2sh_p2wsh {
201        use super::*;
202
203        const VALID_P2SH_P2WSH_PROGRAMS: [(&str, u8, &[u8], &[u8]); 1] = [(
204            "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262",
205            0x00,
206            &[
207                0x18, 0x63, 0x14, 0x3c, 0x14, 0xc5, 0x16, 0x68, 0x04, 0xbd, 0x19, 0x20, 0x33, 0x56,
208                0xda, 0x13, 0x6c, 0x98, 0x56, 0x78, 0xcd, 0x4d, 0x27, 0xa1, 0xb8, 0xc6, 0x32, 0x96,
209                0x04, 0x90, 0x32, 0x62,
210            ],
211            &[
212                0x00, 0x20, 0x18, 0x63, 0x14, 0x3c, 0x14, 0xc5, 0x16, 0x68, 0x04, 0xbd, 0x19, 0x20,
213                0x33, 0x56, 0xda, 0x13, 0x6c, 0x98, 0x56, 0x78, 0xcd, 0x4d, 0x27, 0xa1, 0xb8, 0xc6,
214                0x32, 0x96, 0x04, 0x90, 0x32, 0x62,
215            ],
216        )];
217
218        #[test]
219        fn from_str() {
220            VALID_P2SH_P2WSH_PROGRAMS.iter().for_each(
221                |&(program_str, expected_version, expected_program, _)| {
222                    test_from_str(program_str, expected_version, expected_program);
223                },
224            );
225        }
226
227        #[test]
228        fn to_scriptpubkey() {
229            VALID_P2SH_P2WSH_PROGRAMS.iter().for_each(
230                |&(_, version, program, expected_scriptpubkey)| {
231                    test_to_scriptpubkey(version, program, expected_scriptpubkey);
232                },
233            );
234        }
235    }
236
237    mod version_1 {
238        use super::*;
239
240        const VALID_OP_1_PROGRAMS: [(&str, u8, &[u8], &[u8]); 1] = [(
241            "0128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6",
242            0x01,
243            &[
244                0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45, 0xd1, 0xb3,
245                0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4,
246                0x54, 0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6,
247            ],
248            &[
249                0x51, 0x28, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45,
250                0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91,
251                0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6,
252            ],
253        )];
254
255        #[test]
256        fn from_str() {
257            VALID_OP_1_PROGRAMS.iter().for_each(
258                |&(program_str, expected_version, expected_program, _)| {
259                    test_from_str(program_str, expected_version, expected_program);
260                },
261            );
262        }
263
264        #[test]
265        fn to_scriptpubkey() {
266            VALID_OP_1_PROGRAMS
267                .iter()
268                .for_each(|&(_, version, program, expected_scriptpubkey)| {
269                    test_to_scriptpubkey(version, program, expected_scriptpubkey);
270                });
271        }
272    }
273
274    mod test_invalid {
275        use super::*;
276
277        mod new {
278            use super::*;
279
280            const INVALID_VERSION_PROGRAM: &[u8] = &[0x19, 0x03, 0x00, 0x00, 0x00];
281            const INVALID_LENGTH_FOR_VERSION: &[u8] = &[
282                0x00, 0x0f, // Version 0, data length is incorrect
283                0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45, 0xd1, 0xb3,
284                0xa3,
285            ];
286            const INVALID_LENGTH_PROGRAM: &[u8] = &[0x19];
287            const INVALID_LENGTH_PROGRAM_TOO_LONG: &[u8] = &[
288                0x00, 0x29, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45,
289                0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91,
290                0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6,
291                0x00,
292            ];
293
294            #[test]
295            fn new_invalid_version() {
296                let witness_program_error =
297                    WitnessProgram::new(INVALID_VERSION_PROGRAM).unwrap_err();
298                assert_eq!(
299                    WitnessProgramError::InvalidVersion(0x19),
300                    witness_program_error
301                );
302            }
303
304            #[test]
305            fn new_invalid_length() {
306                let witness_program_error =
307                    WitnessProgram::new(INVALID_LENGTH_PROGRAM).unwrap_err();
308                assert_eq!(
309                    WitnessProgramError::InvalidProgramLength(1),
310                    witness_program_error
311                );
312            }
313
314            #[test]
315            fn new_invalid_program_length_for_version() {
316                let witness_program_error =
317                    WitnessProgram::new(INVALID_LENGTH_FOR_VERSION).unwrap_err();
318                assert_eq!(
319                    WitnessProgramError::InvalidProgramLengthForVersion(15, 0x00),
320                    witness_program_error
321                );
322            }
323
324            #[test]
325            fn new_invalid_program_length_too_long() {
326                let witness_program_error =
327                    WitnessProgram::new(INVALID_LENGTH_PROGRAM_TOO_LONG).unwrap_err();
328                assert_eq!(
329                    WitnessProgramError::InvalidProgramLength(41),
330                    witness_program_error
331                );
332            }
333        }
334
335        mod from_str {
336            use super::*;
337
338            const INVALID_P2SH_P2WPKH_PROGRAM_LENGTH: &str =
339                "0014751e76e8199196d454941c45d1b3a323f143";
340            const INVALID_P2SH_P2WSH_PROGRAM_LENGTH: &str =
341                "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c632960490";
342            const INVALID_OP_1_PROGRAM_LENGTH: &str =
343                "0128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f143";
344            const INVALID_HEX_STR: &str = "001122zzxxyy";
345
346            #[test]
347            fn from_str_invalid_p2sh_p2wpkh_program_len() {
348                let witness_program_error =
349                    WitnessProgram::from_str(INVALID_P2SH_P2WPKH_PROGRAM_LENGTH).unwrap_err();
350                assert_eq!(
351                    WitnessProgramError::MismatchedProgramLength(18, 20),
352                    witness_program_error
353                );
354            }
355
356            #[test]
357            fn from_str_invalid_p2sh_p2wsh_program_len() {
358                let witness_program_error =
359                    WitnessProgram::from_str(INVALID_P2SH_P2WSH_PROGRAM_LENGTH).unwrap_err();
360                assert_eq!(
361                    WitnessProgramError::MismatchedProgramLength(30, 32),
362                    witness_program_error
363                );
364            }
365
366            #[test]
367            fn from_str_invalid_op_1_program_len() {
368                let witness_program_error =
369                    WitnessProgram::from_str(INVALID_OP_1_PROGRAM_LENGTH).unwrap_err();
370                assert_eq!(
371                    WitnessProgramError::MismatchedProgramLength(38, 40),
372                    witness_program_error
373                );
374            }
375
376            #[test]
377            fn from_str_invalid_hex_str() {
378                let witness_program_error = WitnessProgram::from_str(INVALID_HEX_STR).unwrap_err();
379                assert_eq!(
380                    WitnessProgramError::ProgramDecodingError,
381                    witness_program_error
382                );
383            }
384        }
385    }
386}