1use 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 pub version: u8,
62 pub program: Vec<u8>,
64}
65
66impl WitnessProgram {
67 pub fn new(program: &[u8]) -> Result<Self, WitnessProgramError> {
69 if program.len() < 2 {
70 return Err(WitnessProgramError::InvalidProgramLength(program.len()));
71 }
72
73 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 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 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 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, 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}