1#![allow(unknown_lints)]
55#![allow(bare_trait_objects)]
56#![deny(missing_docs)]
57#![deny(non_upper_case_globals)]
58#![deny(non_camel_case_types)]
59#![deny(non_snake_case)]
60#![deny(unused_mut)]
61#![cfg_attr(feature = "strict", deny(warnings))]
62
63extern crate bech32;
64pub use bech32::u5;
65use bech32::{decode, encode, FromBase32, ToBase32, Variant};
66
67use std::str::FromStr;
68use std::string::ToString;
69use std::{error, fmt};
70
71pub mod constants;
72use constants::Network;
73
74#[derive(PartialEq, Eq, Debug, Clone, PartialOrd, Ord, Hash)]
76pub struct WitnessProgram {
77 version: u5,
79 program: Vec<u8>,
81 network: Network,
83 bech32: String,
85}
86
87impl WitnessProgram {
88 pub fn new(version: u5, program: Vec<u8>, network: Network) -> Result<WitnessProgram, Error> {
90 let hrp = constants::hrp(&network);
92 let mut b32_data: Vec<u5> = vec![version];
93 let p5 = program.to_base32();
94 b32_data.extend_from_slice(&p5);
95 let variant = program_version_to_variant(version).ok_or(Error::InvalidScriptVersion)?;
96 let bech32 = encode(&hrp, b32_data, variant)?;
97
98 let ret = WitnessProgram {
100 version,
101 program,
102 network,
103 bech32,
104 };
105
106 ret.validate()?;
108 Ok(ret)
109 }
110
111 pub fn to_address(&self) -> String {
113 self.to_string()
114 }
115
116 pub fn from_address(address: &str) -> Result<WitnessProgram, Error> {
122 WitnessProgram::from_str(address)
123 }
124
125 pub fn to_scriptpubkey(&self) -> Vec<u8> {
130 let mut pubkey: Vec<u8> = Vec::new();
131 let mut v: u8 = self.version.into();
132 if v > 0 {
133 v += 0x50;
134 }
135 pubkey.push(v);
136 pubkey.push(self.program.len() as u8);
137 pubkey.extend_from_slice(&self.program);
138 pubkey
139 }
140
141 pub fn from_scriptpubkey(pubkey: &[u8], network: Network) -> Result<WitnessProgram, Error> {
143 if pubkey.len() < 4 {
146 return Err(Error::ScriptPubkeyTooShort);
147 }
148 let proglen: usize = pubkey[1] as usize;
149 if pubkey.len() != 2 + proglen {
151 return Err(Error::ScriptPubkeyInvalidLength);
152 }
153 let mut v: u8 = pubkey[0];
155 if v > 0x50 {
156 v -= 0x50;
157 }
158
159 let v = u5::try_from_u8(v).expect("range is already guaranteed by code above");
160 let program = &pubkey[2..];
161
162 WitnessProgram::new(v, program.to_vec(), network)
163 }
164
165 pub fn validate(&self) -> Result<(), Error> {
167 if self.version.to_u8() > 16 {
168 return Err(Error::InvalidScriptVersion);
170 }
171 if self.program.len() < 2 || self.program.len() > 40 {
172 return Err(Error::InvalidLength);
173 }
174 if self.version.to_u8() == 0 && self.program.len() != 20 && self.program.len() != 32 {
176 return Err(Error::InvalidVersionLength);
177 }
178 Ok(())
179 }
180
181 pub fn version(&self) -> u5 {
183 self.version
184 }
185
186 pub fn program(&self) -> &[u8] {
188 &self.program
189 }
190
191 pub fn network(&self) -> Network {
193 self.network
194 }
195}
196
197impl ToString for WitnessProgram {
198 fn to_string(&self) -> String {
199 self.bech32.to_string()
200 }
201}
202
203impl FromStr for WitnessProgram {
204 type Err = Error;
205
206 fn from_str(s: &str) -> Result<WitnessProgram, Error> {
207 let (hrp, data, variant) = decode(s)?;
208 let network_classified = match constants::classify(&hrp) {
209 Some(nc) => nc,
210 None => return Err(Error::InvalidHumanReadablePart),
211 };
212 if data.is_empty() || data.len() > 65 {
213 return Err(Error::Bech32(bech32::Error::InvalidLength));
214 }
215 let (version, program) = {
217 let (v, p5) = data.split_at(1);
218 let program = Vec::from_base32(p5)?;
219 (v[0], program)
220 };
221 let wp = WitnessProgram {
222 version,
223 program,
224 network: network_classified,
225 bech32: s.to_string(),
226 };
227 wp.validate()?;
228 if let Some(version_variant) = program_version_to_variant(version) {
229 if version_variant != variant {
230 return Err(Error::InvalidEncoding);
231 }
232 }
233 Ok(wp)
234 }
235}
236
237fn program_version_to_variant(version: u5) -> Option<Variant> {
238 match version.to_u8() {
239 0 => Some(Variant::Bech32),
240 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 => {
241 Some(Variant::Bech32m)
242 }
243 _ => None,
244 }
245}
246
247#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
252pub enum Error {
253 Bech32(bech32::Error),
255 InvalidHumanReadablePart,
257 ScriptPubkeyTooShort,
259 ScriptPubkeyInvalidLength,
261 InvalidLength,
265 InvalidVersionLength,
269 InvalidScriptVersion,
271 InvalidEncoding,
276}
277
278impl From<bech32::Error> for Error {
279 fn from(e: bech32::Error) -> Error {
280 Error::Bech32(e)
281 }
282}
283
284impl fmt::Display for Error {
285 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
286 match *self {
287 Error::Bech32(ref e) => write!(f, "{}", e),
288 Error::InvalidHumanReadablePart => write!(f, "invalid human-readable part"),
289 Error::ScriptPubkeyTooShort => write!(f, "scriptpubkey too short"),
290 Error::ScriptPubkeyInvalidLength => write!(f, "scriptpubkey length mismatch"),
291 Error::InvalidLength => write!(f, "invalid length"),
292 Error::InvalidVersionLength => write!(f, "program length incompatible with version"),
293 Error::InvalidScriptVersion => write!(f, "invalid script version"),
294 Error::InvalidEncoding => write!(f, "invalid Bech32 encoding"),
295 }
296 }
297}
298
299impl error::Error for Error {
300 fn description(&self) -> &str {
301 match *self {
302 Error::Bech32(_) => "Bech32 error",
303 Error::InvalidHumanReadablePart => "invalid human-readable part",
304 Error::ScriptPubkeyTooShort => "scriptpubkey too short",
305 Error::ScriptPubkeyInvalidLength => "scriptpubkey length mismatch",
306 Error::InvalidLength => "invalid length",
307 Error::InvalidVersionLength => "program length incompatible with version",
308 Error::InvalidScriptVersion => "invalid script version",
309 Error::InvalidEncoding => "invalid Bech32 encoding",
310 }
311 }
312
313 fn cause(&self) -> Option<&std::error::Error> {
314 match *self {
315 Error::Bech32(ref e) => Some(e),
316 _ => None,
317 }
318 }
319}
320
321#[cfg(test)]
322mod tests {
323 use bech32;
324 use constants::Network;
325 use *;
326
327 #[test]
328 fn valid_address() {
329 let pairs: Vec<(&str, Vec<u8>, Network)> = vec![
330 (
331 "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4",
332 vec![
333 0x00, 0x14, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c,
334 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6,
335 ],
336 Network::Bitcoin,
337 ),
338 (
339 "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7",
340 vec![
341 0x00, 0x20, 0x18, 0x63, 0x14, 0x3c, 0x14, 0xc5, 0x16, 0x68, 0x04, 0xbd, 0x19,
342 0x20, 0x33, 0x56, 0xda, 0x13, 0x6c, 0x98, 0x56, 0x78, 0xcd, 0x4d, 0x27, 0xa1,
343 0xb8, 0xc6, 0x32, 0x96, 0x04, 0x90, 0x32, 0x62,
344 ],
345 Network::Testnet,
346 ),
347 (
348 "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y",
349 vec![
350 0x51, 0x28, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c,
351 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6, 0x75, 0x1e, 0x76, 0xe8,
352 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1,
353 0x43, 0x3b, 0xd6,
354 ],
355 Network::Bitcoin,
356 ),
357 (
358 "BC1SW50QGDZ25J",
359 vec![0x60, 0x02, 0x75, 0x1e],
360 Network::Bitcoin,
361 ),
362 (
363 "bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs",
364 vec![
365 0x52, 0x10, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c,
366 0x45, 0xd1, 0xb3, 0xa3, 0x23,
367 ],
368 Network::Bitcoin,
369 ),
370 (
371 "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy",
372 vec![
373 0x00, 0x20, 0x00, 0x00, 0x00, 0xc4, 0xa5, 0xca, 0xd4, 0x62, 0x21, 0xb2, 0xa1,
374 0x87, 0x90, 0x5e, 0x52, 0x66, 0x36, 0x2b, 0x99, 0xd5, 0xe9, 0x1c, 0x6c, 0xe2,
375 0x4d, 0x16, 0x5d, 0xab, 0x93, 0xe8, 0x64, 0x33,
376 ],
377 Network::Testnet,
378 ),
379 (
380 "bcrt1qn3h68k2u0rr49skx05qw7veynpf4lfppd2demt",
381 vec![
382 0x00, 0x14, 0x9c, 0x6f, 0xa3, 0xd9, 0x5c, 0x78, 0xc7, 0x52, 0xc2, 0xc6, 0x7d,
383 0x00, 0xef, 0x33, 0x24, 0x98, 0x53, 0x5f, 0xa4, 0x21,
384 ],
385 Network::Regtest,
386 ),
387 (
388 "MONA1Q4KPN6PSTHGD5UR894AUHJJ2G02WLGMP8KE08NE",
389 vec![
390 0x00, 0x14, 0xad, 0x83, 0x3d, 0x06, 0x0b, 0xba, 0x1b, 0x4e, 0x0c, 0xe5, 0xaf,
391 0x79, 0x79, 0x49, 0x48, 0x7a, 0x9d, 0xf4, 0x6c, 0x27,
392 ],
393 Network::Monacoin,
394 ),
395 (
396 "tmona1qfj8lu0rafk2mpvk7jj62q8eerjpex3xlcadtupkrkhh5a73htmhs68e55m",
397 vec![
398 0x00, 0x20, 0x4c, 0x8f, 0xfe, 0x3c, 0x7d, 0x4d, 0x95, 0xb0, 0xb2, 0xde, 0x94,
399 0xb4, 0xa0, 0x1f, 0x39, 0x1c, 0x83, 0x93, 0x44, 0xdf, 0xc7, 0x5a, 0xbe, 0x06,
400 0xc3, 0xb5, 0xef, 0x4e, 0xfa, 0x37, 0x5e, 0xef,
401 ],
402 Network::MonacoinTestnet,
403 ),
404 (
405 "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7",
406 vec![
407 0x00, 0x20, 0x18, 0x63, 0x14, 0x3c, 0x14, 0xc5, 0x16, 0x68, 0x04, 0xbd, 0x19,
408 0x20, 0x33, 0x56, 0xda, 0x13, 0x6c, 0x98, 0x56, 0x78, 0xcd, 0x4d, 0x27, 0xa1,
409 0xb8, 0xc6, 0x32, 0x96, 0x04, 0x90, 0x32, 0x62,
410 ],
411 Network::Testnet,
412 ),
413 (
414 "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y",
415 vec![
416 0x51, 0x28, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c,
417 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6, 0x75, 0x1e, 0x76, 0xe8,
418 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1,
419 0x43, 0x3b, 0xd6,
420 ],
421 Network::Bitcoin,
422 ),
423 (
424 "BC1SW50QGDZ25J",
425 vec![0x60, 0x02, 0x75, 0x1e],
426 Network::Bitcoin,
427 ),
428 (
429 "tmona1q0p29rfu7ap3duzqj5t9e0jzgqzwdtd97pa5rhuz4r38t5a6dknyqxmyyaz",
430 vec![
431 0x00, 0x20, 0x78, 0x54, 0x51, 0xa7, 0x9e, 0xe8, 0x62, 0xde, 0x08, 0x12, 0xa2,
432 0xcb, 0x97, 0xc8, 0x48, 0x00, 0x9c, 0xd5, 0xb4, 0xbe, 0x0f, 0x68, 0x3b, 0xf0,
433 0x55, 0x1c, 0x4e, 0xba, 0x77, 0x4d, 0xb4, 0xc8,
434 ],
435 Network::MonacoinTestnet,
436 ),
437 (
438 "tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c",
439 vec![
440 0x51, 0x20, 0x00, 0x00, 0x00, 0xc4, 0xa5, 0xca, 0xd4, 0x62, 0x21, 0xb2, 0xa1,
441 0x87, 0x90, 0x5e, 0x52, 0x66, 0x36, 0x2b, 0x99, 0xd5, 0xe9, 0x1c, 0x6c, 0xe2,
442 0x4d, 0x16, 0x5d, 0xab, 0x93, 0xe8, 0x64, 0x33,
443 ],
444 Network::Testnet,
445 ),
446 (
447 "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0",
448 vec![
449 0x51, 0x20, 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62,
450 0x95, 0xce, 0x87, 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9,
451 0x59, 0xf2, 0x81, 0x5b, 0x16, 0xf8, 0x17, 0x98,
452 ],
453 Network::Bitcoin,
454 ),
455 ];
456 for p in pairs {
457 let (address, scriptpubkey, network) = p;
458 let version = if scriptpubkey[0] == 0 {
459 0
460 } else {
461 scriptpubkey[0] - 0x50
462 };
463 let prog = match WitnessProgram::from_address(&address) {
464 Ok(prog) => prog,
465 Err(e) => panic!("{}, {:?}", address, e),
466 };
467
468 let pubkey = prog.to_scriptpubkey();
469 assert_eq!(pubkey, scriptpubkey);
470
471 assert_eq!(prog.network(), network);
472 assert_eq!(prog.version().to_u8(), version);
473 assert_eq!(prog.program(), &scriptpubkey[2..]); match WitnessProgram::from_scriptpubkey(&scriptpubkey, prog.network) {
476 Ok(prog) => {
477 assert_eq!(
478 prog.to_string().to_lowercase(),
479 prog.to_string().to_lowercase()
480 );
481 let enc_address = prog.to_address();
482 assert_eq!(address.to_lowercase(), enc_address.to_lowercase());
483 }
484 Err(e) => panic!("{:?}, {:?}", scriptpubkey, e),
485 }
486 }
487 }
488
489 #[test]
490 fn invalid_address() {
491 let pairs: Vec<(&str, Error)> = vec![
492 (
494 "tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty",
495 Error::InvalidHumanReadablePart,
496 ),
497 (
498 "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5",
499 Error::Bech32(bech32::Error::InvalidChecksum),
500 ),
501 (
502 "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2",
503 Error::InvalidScriptVersion,
504 ),
505 ("bc1rw5uspcuh", Error::InvalidLength),
506 (
507 "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90",
508 Error::Bech32(bech32::Error::InvalidLength),
509 ),
510 (
511 "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7",
512 Error::Bech32(bech32::Error::MixedCase),
513 ),
514 (
515 "tb1pw508d6qejxtdg4y5r3zarqfsj6c3",
516 Error::Bech32(bech32::Error::InvalidPadding),
517 ),
518 (
519 "bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du",
520 Error::Bech32(bech32::Error::InvalidPadding),
521 ),
522 (
523 "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv",
524 Error::Bech32(bech32::Error::InvalidPadding),
525 ),
526 (
528 "tc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq5zuyut",
529 Error::InvalidHumanReadablePart,
530 ),
531 (
532 "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqh2y7hd",
533 Error::InvalidEncoding,
534 ),
535 (
536 "tb1z0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqglt7rf",
537 Error::InvalidEncoding,
538 ),
539 (
540 "BC1S0XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ54WELL",
541 Error::InvalidEncoding,
542 ),
543 (
544 "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kemeawh",
545 Error::InvalidEncoding,
546 ),
547 (
548 "tb1q0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq24jc47",
549 Error::InvalidEncoding,
550 ),
551 (
552 "bc1p38j9r5y49hruaue7wxjce0updqjuyyx0kh56v8s25huc6995vvpql3jow4",
553 Error::Bech32(bech32::Error::InvalidChar('o')),
554 ),
555 (
556 "BC130XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ7ZWS8R",
557 Error::InvalidScriptVersion,
558 ),
559 ("bc1pw5dgrnzv", Error::InvalidLength),
560 (
561 "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v8n0nx0muaewav253zgeav",
562 Error::Bech32(bech32::Error::InvalidLength),
563 ),
564 (
565 "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P",
566 Error::InvalidVersionLength,
567 ),
568 (
569 "tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq47Zagq",
570 Error::Bech32(bech32::Error::MixedCase),
571 ),
572 (
573 "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v07qwwzcrf",
574 Error::Bech32(bech32::Error::InvalidPadding),
575 ),
576 (
577 "tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vpggkg4j",
578 Error::Bech32(bech32::Error::InvalidPadding),
579 ),
580 ("bc1gmk9yu", Error::Bech32(bech32::Error::InvalidLength)),
581 ];
582 for p in pairs {
583 let (address, desired_error) = p;
584 match WitnessProgram::from_address(&address) {
585 Ok(_) => panic!("Should be invalid: {:?}", address),
586 Err(e) => assert_eq!(e, desired_error, "{}", address),
587 }
588 }
589 }
590}