1#![deny(missing_docs)]
54#![deny(non_upper_case_globals)]
55#![deny(non_camel_case_types)]
56#![deny(non_snake_case)]
57#![deny(unused_mut)]
58
59extern crate bech32;
60use bech32::{Bech32, ToBase32, FromBase32};
61pub use bech32::u5;
62
63use std::{error, fmt};
64use std::str::FromStr;
65use std::string::ToString;
66
67pub mod constants;
68use constants::Network;
69
70#[derive(PartialEq, Eq, Debug, Clone, PartialOrd, Ord, Hash)]
72pub struct WitnessProgram {
73 version: u5,
75 program: Vec<u8>,
77 network: Network,
79 bech32: Bech32,
81}
82
83impl WitnessProgram {
84 pub fn new(version: u5, program: Vec<u8>, network: Network) -> Result<WitnessProgram, Error> {
86 let hrp = constants::hrp(&network);
88 let mut b32_data: Vec<u5> = vec![version];
89 let p5 = program.to_base32();
90 b32_data.extend_from_slice(&p5);
91 let bech32 = Bech32::new(hrp, b32_data)?;
92
93 let ret = WitnessProgram {
95 version: version,
96 program: program,
97 network: network,
98 bech32: bech32,
99 };
100
101 ret.validate()?;
103 Ok(ret)
104 }
105
106 pub fn to_address(&self) -> String {
108 self.to_string()
109 }
110
111 pub fn from_address(address: &str) -> Result<WitnessProgram, Error> {
117 WitnessProgram::from_str(address)
118 }
119
120 pub fn to_scriptpubkey(&self) -> Vec<u8> {
125 let mut pubkey: Vec<u8> = Vec::new();
126 let mut v: u8 = self.version.into();
127 if v > 0 {
128 v += 0x50;
129 }
130 pubkey.push(v);
131 pubkey.push(self.program.len() as u8);
132 pubkey.extend_from_slice(&self.program);
133 pubkey
134 }
135
136 pub fn from_scriptpubkey(pubkey: &[u8], network: Network) -> Result<WitnessProgram, Error> {
138 if pubkey.len() < 4 {
141 return Err(Error::ScriptPubkeyTooShort)
142 }
143 let proglen: usize = pubkey[1] as usize;
144 if pubkey.len() != 2 + proglen {
146 return Err(Error::ScriptPubkeyInvalidLength)
147 }
148 let mut v: u8 = pubkey[0];
150 if v > 0x50 {
151 v -= 0x50;
152 }
153
154 let v = u5::try_from_u8(v).expect("range is already guaranteed by code above");
155 let program = &pubkey[2..];
156
157 WitnessProgram::new(v, program.to_vec(), network)
158 }
159
160 pub fn validate(&self) -> Result<(), Error> {
162 if self.version.to_u8() > 16 {
163 return Err(Error::InvalidScriptVersion)
165 }
166 if self.program.len() < 2 || self.program.len() > 40 {
167 return Err(Error::InvalidLength)
168 }
169 if self.version.to_u8() == 0 &&
171 self.program.len() != 20 && self.program.len() != 32 {
172 return Err(Error::InvalidVersionLength)
173 }
174 Ok(())
175 }
176
177 pub fn version(&self) -> u5 {
179 self.version
180 }
181
182 pub fn program(&self) -> &[u8] {
184 &self.program
185 }
186
187 pub fn network(&self) -> Network {
189 self.network
190 }
191}
192
193impl ToString for WitnessProgram {
194 fn to_string(&self) -> String {
195 self.bech32.to_string()
196 }
197}
198
199impl FromStr for WitnessProgram {
200 type Err = Error;
201
202 fn from_str(s: &str) -> Result<WitnessProgram, Error> {
203 let b32 = s.parse::<Bech32>()?;
204 let network_classified = match constants::classify(b32.hrp()) {
205 Some(nc) => nc,
206 None => return Err(Error::InvalidHumanReadablePart)
207 };
208 if b32.data().is_empty() || b32.data().len() > 65 {
209 return Err(Error::Bech32(bech32::Error::InvalidLength))
210 }
211 let (version, program) = {
213 let (v, p5) = b32.data().split_at(1);
214 let program = Vec::from_base32(p5)?;
215 (v[0], program)
216 };
217 let wp = WitnessProgram {
218 version: version,
219 program: program,
220 network: network_classified,
221 bech32: b32,
222 };
223 wp.validate()?;
224 Ok(wp)
225 }
226}
227
228#[derive(PartialEq, Debug)]
233pub enum Error {
234 Bech32(bech32::Error),
236 InvalidHumanReadablePart,
238 ScriptPubkeyTooShort,
240 ScriptPubkeyInvalidLength,
242 InvalidLength,
246 InvalidVersionLength,
250 InvalidScriptVersion,
252}
253
254impl From<bech32::Error> for Error {
255 fn from(e: bech32::Error) -> Error {
256 Error::Bech32(e)
257 }
258}
259
260impl fmt::Display for Error {
261 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
262 match *self {
263 Error::Bech32(ref e) => write!(f, "{}", e),
264 Error::InvalidHumanReadablePart => write!(f, "invalid human-readable part"),
265 Error::ScriptPubkeyTooShort => write!(f, "scriptpubkey too short"),
266 Error::ScriptPubkeyInvalidLength => write!(f, "scriptpubkey length mismatch"),
267 Error::InvalidLength => write!(f, "invalid length"),
268 Error::InvalidVersionLength => write!(f, "program length incompatible with version"),
269 Error::InvalidScriptVersion => write!(f, "invalid script versio"),
270 }
271 }
272}
273
274impl error::Error for Error {
275 fn description(&self) -> &str {
276 match *self {
277 Error::Bech32(_) => "Bech32 error",
278 Error::InvalidHumanReadablePart => "invalid human-readable part",
279 Error::ScriptPubkeyTooShort => "scriptpubkey too short",
280 Error::ScriptPubkeyInvalidLength => "scriptpubkey length mismatch",
281 Error::InvalidLength => "invalid length",
282 Error::InvalidVersionLength => "program length incompatible with version",
283 Error::InvalidScriptVersion => "invalid script version"
284 }
285 }
286
287 fn cause(&self) -> Option<&std::error::Error> {
288 match *self {
289 Error::Bech32(ref e) => Some(e),
290 _ => None,
291 }
292 }
293}
294
295#[cfg(test)]
296mod tests {
297 use ::*;
298 use ::constants::Network;
299 use bech32;
300
301 #[test]
302 fn valid_address() {
303 let pairs: Vec<(&str, Vec<u8>, Network)> = vec![
304 (
305 "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4",
306 vec![
307 0x00, 0x14, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54,
308 0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6
309 ],
310 Network::Bitcoin,
311 ),
312 (
313 "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7",
314 vec![
315 0x00, 0x20, 0x18, 0x63, 0x14, 0x3c, 0x14, 0xc5, 0x16, 0x68, 0x04,
316 0xbd, 0x19, 0x20, 0x33, 0x56, 0xda, 0x13, 0x6c, 0x98, 0x56, 0x78,
317 0xcd, 0x4d, 0x27, 0xa1, 0xb8, 0xc6, 0x32, 0x96, 0x04, 0x90, 0x32,
318 0x62
319 ],
320 Network::Testnet
321 ),
322 (
323 "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx",
324 vec![
325 0x51, 0x28, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54,
326 0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6,
327 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54, 0x94, 0x1c,
328 0x45, 0xd1, 0xb3, 0xa3, 0x23, 0xf1, 0x43, 0x3b, 0xd6
329 ],
330 Network::Bitcoin,
331 ),
332 (
333 "BC1SW50QA3JX3S",
334 vec![
335 0x60, 0x02, 0x75, 0x1e
336 ],
337 Network::Bitcoin,
338 ),
339 (
340 "bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj",
341 vec![
342 0x52, 0x10, 0x75, 0x1e, 0x76, 0xe8, 0x19, 0x91, 0x96, 0xd4, 0x54,
343 0x94, 0x1c, 0x45, 0xd1, 0xb3, 0xa3, 0x23
344 ],
345 Network::Bitcoin,
346 ),
347 (
348 "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy",
349 vec![
350 0x00, 0x20, 0x00, 0x00, 0x00, 0xc4, 0xa5, 0xca, 0xd4, 0x62, 0x21,
351 0xb2, 0xa1, 0x87, 0x90, 0x5e, 0x52, 0x66, 0x36, 0x2b, 0x99, 0xd5,
352 0xe9, 0x1c, 0x6c, 0xe2, 0x4d, 0x16, 0x5d, 0xab, 0x93, 0xe8, 0x64,
353 0x33
354 ],
355 Network::Testnet,
356 ),
357 (
358 "bcrt1qn3h68k2u0rr49skx05qw7veynpf4lfppd2demt",
359 vec![
360 0x00, 0x14, 0x9c, 0x6f, 0xa3, 0xd9, 0x5c, 0x78, 0xc7, 0x52, 0xc2,
361 0xc6, 0x7d, 0x00, 0xef, 0x33, 0x24, 0x98, 0x53, 0x5f, 0xa4, 0x21,
362 ],
363 Network::Regtest,
364 ),
365 ];
366 for p in pairs {
367 let (address, scriptpubkey, network) = p;
368 let version = if scriptpubkey[0] == 0 { 0 } else { scriptpubkey[0] - 0x50 };
369 let dec_result = WitnessProgram::from_address(&address);
370 assert!(dec_result.is_ok());
371
372 let prog = dec_result.unwrap();
373 let pubkey = prog.to_scriptpubkey();
374 assert_eq!(pubkey, scriptpubkey);
375
376 assert_eq!(prog.network(), network);
377 assert_eq!(prog.version().to_u8(), version);
378 assert_eq!(prog.program(), &scriptpubkey[2..]); let spk_result = WitnessProgram::from_scriptpubkey(&scriptpubkey, prog.network);
381 assert!(spk_result.is_ok());
382 assert_eq!(prog, spk_result.unwrap());
383
384 let enc_address = prog.to_address();
385 assert_eq!(address.to_lowercase(), enc_address.to_lowercase());
386 }
387 }
388
389 #[test]
390 fn invalid_address() {
391 let pairs: Vec<(&str, Error)> = vec!(
392 ("tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty",
393 Error::InvalidHumanReadablePart),
394 ("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5",
395 Error::Bech32(bech32::Error::InvalidChecksum)),
396 ("BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2",
397 Error::InvalidScriptVersion),
398 ("bc1rw5uspcuh",
399 Error::InvalidLength),
400 ("bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90",
401 Error::Bech32(bech32::Error::InvalidLength)),
402 ("BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P",
403 Error::InvalidVersionLength),
404 ("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7",
405 Error::Bech32(bech32::Error::MixedCase)),
406 ("tb1pw508d6qejxtdg4y5r3zarqfsj6c3",
407 Error::Bech32(bech32::Error::InvalidPadding)),
408 ("bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du",
409 Error::Bech32(bech32::Error::InvalidPadding)),
410 ("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv",
411 Error::Bech32(bech32::Error::InvalidPadding)),
412 ("bc1gmk9yu",
413 Error::Bech32(bech32::Error::InvalidLength)),
414 );
415 for p in pairs {
416 let (address, desired_error) = p;
417 let dec_result = WitnessProgram::from_address(&address);
418 if dec_result.is_ok() {
419 panic!("Should be invalid: {:?}", address);
420 }
421 assert_eq!(dec_result.unwrap_err(), desired_error);
422 }
423 }
424}