From ee1dfcf4a2f01f2f7bd20b60a99133aec240c18a Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Fri, 9 Mar 2018 17:53:20 +0000 Subject: [PATCH] base58: remove Base58 traits, replace with encode/decode functions --- src/util/address.rs | 103 +++++++++-------- src/util/base58.rs | 237 ++++++++++++++++++--------------------- src/util/bip32.rs | 95 ++++++++-------- src/util/contracthash.rs | 3 +- 4 files changed, 223 insertions(+), 215 deletions(-) diff --git a/src/util/address.rs b/src/util/address.rs index 9503abc4..0a17c6ac 100644 --- a/src/util/address.rs +++ b/src/util/address.rs @@ -15,6 +15,9 @@ //! Support for ordinary base58 Bitcoin addresses and private keys //! +use std::str::FromStr; +use std::string::ToString; + use secp256k1::{self, Secp256k1}; use secp256k1::key::{PublicKey, SecretKey}; @@ -22,7 +25,7 @@ use blockdata::script; use blockdata::opcodes; use network::constants::Network; use util::hash::Hash160; -use util::base58::{self, FromBase58, ToBase58}; +use util::base58; /// The method used to produce an address #[derive(Copy, Clone, PartialEq, Eq, Debug)] @@ -98,23 +101,26 @@ impl Address { } } -impl ToBase58 for Address { - fn base58_layout(&self) -> Vec { - let mut ret = vec![ - match (self.network, self.ty) { - (Network::Bitcoin, Type::PubkeyHash) => 0, - (Network::Bitcoin, Type::ScriptHash) => 5, - (Network::Testnet, Type::PubkeyHash) => 111, - (Network::Testnet, Type::ScriptHash) => 196 - } - ]; - ret.extend(self.hash[..].iter().cloned()); - ret +impl ToString for Address { + fn to_string(&self) -> String { + let mut prefixed = [0; 21]; + prefixed[0] = match (self.network, self.ty) { + (Network::Bitcoin, Type::PubkeyHash) => 0, + (Network::Bitcoin, Type::ScriptHash) => 5, + (Network::Testnet, Type::PubkeyHash) => 111, + (Network::Testnet, Type::ScriptHash) => 196 + }; + prefixed[1..].copy_from_slice(&self.hash[..]); + base58::check_encode_slice(&prefixed[..]) } } -impl FromBase58 for Address { - fn from_base58_layout(data: Vec) -> Result { +impl FromStr for Address { + type Err = base58::Error; + + fn from_str(s: &str) -> Result { + let data = try!(base58::from_check(s)); + if data.len() != 21 { return Err(base58::Error::InvalidLength(data.len())); } @@ -137,7 +143,7 @@ impl FromBase58 for Address { impl ::std::fmt::Debug for Address { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - write!(f, "{}", self.to_base58check()) + write!(f, "{}", self.to_string()) } } @@ -195,22 +201,29 @@ impl Privkey { } } -impl ToBase58 for Privkey { - fn base58_layout(&self) -> Vec { - let mut ret = vec![ - match self.network { - Network::Bitcoin => 128, - Network::Testnet => 239 - } - ]; - ret.extend(&self.key[..]); - if self.compressed { ret.push(1); } - ret +impl ToString for Privkey { + fn to_string(&self) -> String { + let mut ret = [0; 34]; + ret[0] = match self.network { + Network::Bitcoin => 128, + Network::Testnet => 239 + }; + ret[1..33].copy_from_slice(&self.key[..]); + if self.compressed { + ret[33] = 1; + base58::check_encode_slice(&ret[..]) + } else { + base58::check_encode_slice(&ret[..33]) + } } } -impl FromBase58 for Privkey { - fn from_base58_layout(data: Vec) -> Result { +impl FromStr for Privkey { + type Err = base58::Error; + + fn from_str(s: &str) -> Result { + let data = try!(base58::from_check(s)); + let compressed = match data.len() { 33 => false, 34 => true, @@ -237,6 +250,9 @@ impl FromBase58 for Privkey { #[cfg(test)] mod tests { + use std::str::FromStr; + use std::string::ToString; + use secp256k1::Secp256k1; use secp256k1::key::PublicKey; use serialize::hex::FromHex; @@ -244,7 +260,6 @@ mod tests { use blockdata::script::Script; use network::constants::Network::{Bitcoin, Testnet}; use util::hash::Hash160; - use util::base58::{FromBase58, ToBase58}; use super::*; macro_rules! hex (($hex:expr) => ($hex.from_hex().unwrap())); @@ -260,8 +275,8 @@ mod tests { }; assert_eq!(addr.script_pubkey(), hex_script!("76a914162c5ea71c0b23f5b9022ef047c4a86470a5b07088ac")); - assert_eq!(&addr.to_base58check(), "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM"); - assert_eq!(FromBase58::from_base58check("132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM"), Ok(addr)); + assert_eq!(&addr.to_string(), "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM"); + assert_eq!(Address::from_str("132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM"), Ok(addr)); } #[test] @@ -270,11 +285,11 @@ mod tests { let key = hex_key!(&secp, "048d5141948c1702e8c95f438815794b87f706a8d4cd2bffad1dc1570971032c9b6042a0431ded2478b5c9cf2d81c124a5e57347a3c63ef0e7716cf54d613ba183"); let addr = Address::from_key(Bitcoin, &key, false); - assert_eq!(&addr.to_base58check(), "1QJVDzdqb1VpbDK7uDeyVXy9mR27CJiyhY"); + assert_eq!(&addr.to_string(), "1QJVDzdqb1VpbDK7uDeyVXy9mR27CJiyhY"); let key = hex_key!(&secp, &"03df154ebfcf29d29cc10d5c2565018bce2d9edbab267c31d2caf44a63056cf99f"); let addr = Address::from_key(Testnet, &key, true); - assert_eq!(&addr.to_base58check(), "mqkhEMH6NCeYjFybv7pvFC22MFeaNT9AQC"); + assert_eq!(&addr.to_string(), "mqkhEMH6NCeYjFybv7pvFC22MFeaNT9AQC"); } #[test] @@ -286,8 +301,8 @@ mod tests { }; assert_eq!(addr.script_pubkey(), hex_script!("a914162c5ea71c0b23f5b9022ef047c4a86470a5b07087")); - assert_eq!(&addr.to_base58check(), "33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k"); - assert_eq!(FromBase58::from_base58check("33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k"), Ok(addr)); + assert_eq!(&addr.to_string(), "33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k"); + assert_eq!(Address::from_str("33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k"), Ok(addr)); } #[test] @@ -295,31 +310,31 @@ mod tests { let script = hex_script!("552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae"); let addr = Address::from_script(Testnet, &script); - assert_eq!(&addr.to_base58check(), "2N3zXjbwdTcPsJiy8sUK9FhWJhqQCxA8Jjr"); - assert_eq!(FromBase58::from_base58check("2N3zXjbwdTcPsJiy8sUK9FhWJhqQCxA8Jjr"), Ok(addr)); + assert_eq!(&addr.to_string(), "2N3zXjbwdTcPsJiy8sUK9FhWJhqQCxA8Jjr"); + assert_eq!(Address::from_str("2N3zXjbwdTcPsJiy8sUK9FhWJhqQCxA8Jjr"), Ok(addr)); } #[test] fn test_key_derivation() { // testnet compressed - let sk: Privkey = FromBase58::from_base58check("cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy").unwrap(); + let sk = Privkey::from_str("cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy").unwrap(); assert_eq!(sk.network(), Testnet); assert_eq!(sk.is_compressed(), true); - assert_eq!(&sk.to_base58check(), "cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy"); + assert_eq!(&sk.to_string(), "cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy"); let secp = Secp256k1::new(); let pk = sk.to_address(&secp).unwrap(); - assert_eq!(&pk.to_base58check(), "mqwpxxvfv3QbM8PU8uBx2jaNt9btQqvQNx"); + assert_eq!(&pk.to_string(), "mqwpxxvfv3QbM8PU8uBx2jaNt9btQqvQNx"); // mainnet uncompressed - let sk: Privkey = FromBase58::from_base58check("5JYkZjmN7PVMjJUfJWfRFwtuXTGB439XV6faajeHPAM9Z2PT2R3").unwrap(); + let sk = Privkey::from_str("5JYkZjmN7PVMjJUfJWfRFwtuXTGB439XV6faajeHPAM9Z2PT2R3").unwrap(); assert_eq!(sk.network(), Bitcoin); assert_eq!(sk.is_compressed(), false); - assert_eq!(&sk.to_base58check(), "5JYkZjmN7PVMjJUfJWfRFwtuXTGB439XV6faajeHPAM9Z2PT2R3"); + assert_eq!(&sk.to_string(), "5JYkZjmN7PVMjJUfJWfRFwtuXTGB439XV6faajeHPAM9Z2PT2R3"); let secp = Secp256k1::new(); let pk = sk.to_address(&secp).unwrap(); - assert_eq!(&pk.to_base58check(), "1GhQvF6dL8xa6wBxLnWmHcQsurx9RxiMc8"); + assert_eq!(&pk.to_string(), "1GhQvF6dL8xa6wBxLnWmHcQsurx9RxiMc8"); } } diff --git a/src/util/base58.rs b/src/util/base58.rs index b917f20b..91badc9d 100644 --- a/src/util/base58.rs +++ b/src/util/base58.rs @@ -16,7 +16,7 @@ use std::{error, fmt}; -use byteorder::{ByteOrder, LittleEndian, WriteBytesExt}; +use byteorder::{ByteOrder, LittleEndian}; use util::hash::Sha256dHash; /// An error that might occur during base58 decoding @@ -84,171 +84,158 @@ static BASE58_DIGITS: [Option; 128] = [ Some(55), Some(56), Some(57), None, None, None, None, None, // 120-127 ]; -/// Trait for objects which can be read as base58 -pub trait FromBase58: Sized { - /// Constructs an object from the byte-encoding (base 256) - /// representation of its base58 format - fn from_base58_layout(data: Vec) -> Result; - - /// Obtain an object from its base58 encoding - fn from_base58(data: &str) -> Result { - // 11/15 is just over log_256(58) - let mut scratch = vec![0u8; 1 + data.len() * 11 / 15]; - // Build in base 256 - for d58 in data.bytes() { - // Compute "X = X * 58 + next_digit" in base 256 - if d58 as usize > BASE58_DIGITS.len() { - return Err(Error::BadByte(d58)); - } - let mut carry = match BASE58_DIGITS[d58 as usize] { - Some(d58) => d58 as u32, - None => { return Err(Error::BadByte(d58)); } - }; - for d256 in scratch.iter_mut().rev() { - carry += *d256 as u32 * 58; - *d256 = carry as u8; - carry /= 256; - } - assert_eq!(carry, 0); +/// Decode base58-encoded string into a byte vector +pub fn from(data: &str) -> Result, Error> { + // 11/15 is just over log_256(58) + let mut scratch = vec![0u8; 1 + data.len() * 11 / 15]; + // Build in base 256 + for d58 in data.bytes() { + // Compute "X = X * 58 + next_digit" in base 256 + if d58 as usize > BASE58_DIGITS.len() { + return Err(Error::BadByte(d58)); } - - // Copy leading zeroes directly - let mut ret: Vec = data.bytes().take_while(|&x| x == BASE58_CHARS[0]) - .map(|_| 0) - .collect(); - // Copy rest of string - ret.extend(scratch.into_iter().skip_while(|&x| x == 0)); - FromBase58::from_base58_layout(ret) - } - - /// Obtain an object from its base58check encoding - fn from_base58check(data: &str) -> Result { - let mut ret: Vec = try!(FromBase58::from_base58(data)); - if ret.len() < 4 { - return Err(Error::TooShort(ret.len())); - } - let ck_start = ret.len() - 4; - let expected = Sha256dHash::from_data(&ret[..ck_start]).into_le().low_u32(); - let actual = LittleEndian::read_u32(&ret[ck_start..(ck_start + 4)]); - if expected != actual { - return Err(Error::BadChecksum(expected, actual)); - } - - ret.truncate(ck_start); - FromBase58::from_base58_layout(ret) - } -} - -/// Directly encode a slice as base58 -pub fn base58_encode_slice(data: &[u8]) -> String { - // 7/5 is just over log_58(256) - let mut scratch = vec![0u8; 1 + data.len() * 7 / 5]; - // Build in base 58 - for &d256 in &data.base58_layout() { - // Compute "X = X * 256 + next_digit" in base 58 - let mut carry = d256 as u32; - for d58 in scratch.iter_mut().rev() { - carry += (*d58 as u32) << 8; - *d58 = (carry % 58) as u8; - carry /= 58; + let mut carry = match BASE58_DIGITS[d58 as usize] { + Some(d58) => d58 as u32, + None => { return Err(Error::BadByte(d58)); } + }; + for d256 in scratch.iter_mut().rev() { + carry += *d256 as u32 * 58; + *d256 = carry as u8; + carry /= 256; } assert_eq!(carry, 0); } // Copy leading zeroes directly - let mut ret: Vec = data.iter().take_while(|&&x| x == 0) - .map(|_| BASE58_CHARS[0]) - .collect(); + let mut ret: Vec = data.bytes().take_while(|&x| x == BASE58_CHARS[0]) + .map(|_| 0) + .collect(); // Copy rest of string - ret.extend(scratch.into_iter().skip_while(|&x| x == 0) - .map(|x| BASE58_CHARS[x as usize])); + ret.extend(scratch.into_iter().skip_while(|&x| x == 0)); + Ok(ret) +} + +/// Decode a base58check-encoded string +pub fn from_check(data: &str) -> Result, Error> { + let mut ret: Vec = try!(from(data)); + if ret.len() < 4 { + return Err(Error::TooShort(ret.len())); + } + let ck_start = ret.len() - 4; + let expected = Sha256dHash::from_data(&ret[..ck_start]).into_le().low_u32(); + let actual = LittleEndian::read_u32(&ret[ck_start..(ck_start + 4)]); + if expected != actual { + return Err(Error::BadChecksum(expected, actual)); + } + + ret.truncate(ck_start); + Ok(ret) +} + +fn encode_iter(data: I) -> String +where + I: Iterator + Clone, +{ + let (len, _) = data.size_hint(); + + // 7/5 is just over log_58(256) + let mut ret = Vec::with_capacity(1 + len * 7 / 5); + + let mut leading_zero_count = 0; + let mut leading_zeroes = true; + // Build string in little endian with 0-58 in place of characters... + for d256 in data { + let mut carry = d256 as usize; + if leading_zeroes && carry == 0 { + leading_zero_count += 1; + } else { + leading_zeroes = false; + } + + for ch in ret.iter_mut() { + let new_ch = *ch as usize * 256 + carry; + *ch = (new_ch % 58) as u8; + carry = new_ch / 58; + } + while carry > 0 { + ret.push((carry % 58) as u8); + carry /= 58; + } + } + + // ... then reverse it and convert to chars + for _ in 0..leading_zero_count { + ret.push(0); + } + ret.reverse(); + for ch in ret.iter_mut() { + *ch = BASE58_CHARS[*ch as usize]; + } String::from_utf8(ret).unwrap() } -/// Trait for objects which can be written as base58 -pub trait ToBase58 { - /// The serialization to be converted into base58 - fn base58_layout(&self) -> Vec; - - /// Obtain a string with the base58 encoding of the object - fn to_base58(&self) -> String { - base58_encode_slice(&self.base58_layout()[..]) - } - - /// Obtain a string with the base58check encoding of the object - /// (Tack the first 4 256-digits of the object's Bitcoin hash onto the end.) - fn to_base58check(&self) -> String { - let mut data = self.base58_layout(); - let checksum = Sha256dHash::from_data(&data).into_le().low_u32(); - data.write_u32::(checksum).unwrap(); - base58_encode_slice(&data) - } +/// Directly encode a slice as base58 +pub fn encode_slice(data: &[u8]) -> String { + encode_iter(data.iter().cloned()) } -// Trivial implementations for slices and vectors -impl<'a> ToBase58 for &'a [u8] { - fn base58_layout(&self) -> Vec { self.to_vec() } - fn to_base58(&self) -> String { base58_encode_slice(*self) } -} - -impl<'a> ToBase58 for Vec { - fn base58_layout(&self) -> Vec { self.clone() } - fn to_base58(&self) -> String { base58_encode_slice(&self[..]) } -} - -impl FromBase58 for Vec { - fn from_base58_layout(data: Vec) -> Result, Error> { - Ok(data) - } +/// Obtain a string with the base58check encoding of a slice +/// (Tack the first 4 256-digits of the object's Bitcoin hash onto the end.) +pub fn check_encode_slice(data: &[u8]) -> String { + let checksum = Sha256dHash::from_data(&data); + encode_iter( + data.iter() + .cloned() + .chain(checksum[0..4].iter().cloned()) + ) } #[cfg(test)] mod tests { use serialize::hex::FromHex; - use super::ToBase58; - use super::FromBase58; + use super::*; #[test] fn test_base58_encode() { // Basics - assert_eq!(&(&[0][..]).to_base58(), "1"); - assert_eq!(&(&[1][..]).to_base58(), "2"); - assert_eq!(&(&[58][..]).to_base58(), "21"); - assert_eq!(&(&[13, 36][..]).to_base58(), "211"); + assert_eq!(&encode_slice(&[0][..]), "1"); + assert_eq!(&encode_slice(&[1][..]), "2"); + assert_eq!(&encode_slice(&[58][..]), "21"); + assert_eq!(&encode_slice(&[13, 36][..]), "211"); // Leading zeroes - assert_eq!(&(&[0, 13, 36][..]).to_base58(), "1211"); - assert_eq!(&(&[0, 0, 0, 0, 13, 36][..]).to_base58(), "1111211"); + assert_eq!(&encode_slice(&[0, 13, 36][..]), "1211"); + assert_eq!(&encode_slice(&[0, 0, 0, 0, 13, 36][..]), "1111211"); // Addresses - assert_eq!(&"00f8917303bfa8ef24f292e8fa1419b20460ba064d".from_hex().unwrap().to_base58check(), - "1PfJpZsjreyVrqeoAfabrRwwjQyoSQMmHH"); + let addr = "00f8917303bfa8ef24f292e8fa1419b20460ba064d".from_hex().unwrap(); + assert_eq!(&check_encode_slice(&addr[..]), "1PfJpZsjreyVrqeoAfabrRwwjQyoSQMmHH"); } #[test] fn test_base58_decode() { // Basics - assert_eq!(FromBase58::from_base58("1").ok(), Some(vec![0u8])); - assert_eq!(FromBase58::from_base58("2").ok(), Some(vec![1u8])); - assert_eq!(FromBase58::from_base58("21").ok(), Some(vec![58u8])); - assert_eq!(FromBase58::from_base58("211").ok(), Some(vec![13u8, 36])); + assert_eq!(from("1").ok(), Some(vec![0u8])); + assert_eq!(from("2").ok(), Some(vec![1u8])); + assert_eq!(from("21").ok(), Some(vec![58u8])); + assert_eq!(from("211").ok(), Some(vec![13u8, 36])); // Leading zeroes - assert_eq!(FromBase58::from_base58("1211").ok(), Some(vec![0u8, 13, 36])); - assert_eq!(FromBase58::from_base58("111211").ok(), Some(vec![0u8, 0, 0, 13, 36])); + assert_eq!(from("1211").ok(), Some(vec![0u8, 13, 36])); + assert_eq!(from("111211").ok(), Some(vec![0u8, 0, 0, 13, 36])); // Addresses - assert_eq!(FromBase58::from_base58check("1PfJpZsjreyVrqeoAfabrRwwjQyoSQMmHH").ok(), + assert_eq!(from_check("1PfJpZsjreyVrqeoAfabrRwwjQyoSQMmHH").ok(), Some("00f8917303bfa8ef24f292e8fa1419b20460ba064d".from_hex().unwrap())) } #[test] fn test_base58_roundtrip() { let s = "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs"; - let v: Vec = FromBase58::from_base58check(s).unwrap(); - assert_eq!(&v.to_base58check(), s); - assert_eq!(FromBase58::from_base58check(&v.to_base58check()).ok(), Some(v)); + let v: Vec = from_check(s).unwrap(); + assert_eq!(check_encode_slice(&v[..]), s); + assert_eq!(from_check(&check_encode_slice(&v[..])).ok(), Some(v)); } } diff --git a/src/util/bip32.rs b/src/util/bip32.rs index 668f2981..7834c7e2 100644 --- a/src/util/bip32.rs +++ b/src/util/bip32.rs @@ -19,9 +19,11 @@ use std::default::Default; use std::io::Cursor; use std::{error, fmt}; +use std::str::FromStr; +use std::string::ToString; use serde::{Serialize, Deserialize, Serializer, Deserializer}; -use byteorder::{BigEndian, ByteOrder, ReadBytesExt, WriteBytesExt}; +use byteorder::{BigEndian, ByteOrder, ReadBytesExt}; use crypto::digest::Digest; use crypto::hmac::Hmac; use crypto::mac::Mac; @@ -33,7 +35,6 @@ use secp256k1::{self, Secp256k1}; use network::constants::Network; use util::base58; -use util::base58::{FromBase58, ToBase58}; /// A chain code pub struct ChainCode([u8; 32]); @@ -337,33 +338,36 @@ impl ExtendedPubKey { } } -impl ToBase58 for ExtendedPrivKey { - fn base58_layout(&self) -> Vec { - let mut ret = Vec::with_capacity(78); - ret.extend(match self.network { +impl ToString for ExtendedPrivKey { + fn to_string(&self) -> String { + let mut ret = [0; 78]; + ret[0..4].copy_from_slice(&match self.network { Network::Bitcoin => [0x04, 0x88, 0xAD, 0xE4], - Network::Testnet => [0x04, 0x35, 0x83, 0x94] - }.iter().cloned()); - ret.push(self.depth as u8); - ret.extend(self.parent_fingerprint[..].iter().cloned()); + Network::Testnet => [0x04, 0x35, 0x83, 0x94], + }[..]); + ret[4] = self.depth as u8; + ret[5..9].copy_from_slice(&self.parent_fingerprint[..]); match self.child_number { ChildNumber::Hardened(n) => { - ret.write_u32::(n + (1 << 31)).unwrap(); + BigEndian::write_u32(&mut ret[9..13], n + (1 << 31)); } ChildNumber::Normal(n) => { - ret.write_u32::(n).unwrap(); + BigEndian::write_u32(&mut ret[9..13], n); } } - ret.extend(self.chain_code[..].iter().cloned()); - ret.push(0); - ret.extend(self.secret_key[..].iter().cloned()); - ret + ret[13..45].copy_from_slice(&self.chain_code[..]); + ret[45] = 0; + ret[46..78].copy_from_slice(&self.secret_key[..]); + base58::check_encode_slice(&ret[..]) } } -impl FromBase58 for ExtendedPrivKey { - fn from_base58_layout(data: Vec) -> Result { +impl FromStr for ExtendedPrivKey { + type Err = base58::Error; + + fn from_str(inp: &str) -> Result { let s = Secp256k1::with_caps(secp256k1::ContextFlag::None); + let data = try!(base58::from_check(inp)); if data.len() != 78 { return Err(base58::Error::InvalidLength(data.len())); @@ -386,40 +390,41 @@ impl FromBase58 for ExtendedPrivKey { child_number: child_number, chain_code: ChainCode::from(&data[13..45]), secret_key: try!(SecretKey::from_slice(&s, - &data[46..78]).map_err(|e| - base58::Error::Other(e.to_string()))) + &data[46..78]).map_err(|e| + base58::Error::Other(e.to_string()))) }) } } -impl ToBase58 for ExtendedPubKey { - fn base58_layout(&self) -> Vec { - let mut ret = Vec::with_capacity(78); - ret.extend(match self.network { +impl ToString for ExtendedPubKey { + fn to_string(&self) -> String { + let mut ret = [0; 78]; + ret[0..4].copy_from_slice(&match self.network { Network::Bitcoin => [0x04u8, 0x88, 0xB2, 0x1E], - Network::Testnet => [0x04u8, 0x35, 0x87, 0xCF] - }.iter().cloned()); - ret.push(self.depth as u8); - ret.extend(self.parent_fingerprint[..].iter().cloned()); - let mut be_n = [0; 4]; + Network::Testnet => [0x04u8, 0x35, 0x87, 0xCF], + }[..]); + ret[4] = self.depth as u8; + ret[5..9].copy_from_slice(&self.parent_fingerprint[..]); match self.child_number { ChildNumber::Hardened(n) => { - BigEndian::write_u32(&mut be_n, n + (1 << 31)); + BigEndian::write_u32(&mut ret[9..13], n + (1 << 31)); } ChildNumber::Normal(n) => { - BigEndian::write_u32(&mut be_n, n); + BigEndian::write_u32(&mut ret[9..13], n); } } - ret.extend(be_n.iter().cloned()); - ret.extend(self.chain_code[..].iter().cloned()); - ret.extend(self.public_key.serialize()[..].iter().cloned()); - ret + ret[13..45].copy_from_slice(&self.chain_code[..]); + ret[45..78].copy_from_slice(&self.public_key.serialize()[..]); + base58::check_encode_slice(&ret[..]) } } -impl FromBase58 for ExtendedPubKey { - fn from_base58_layout(data: Vec) -> Result { +impl FromStr for ExtendedPubKey { + type Err = base58::Error; + + fn from_str(inp: &str) -> Result { let s = Secp256k1::with_caps(secp256k1::ContextFlag::None); + let data = try!(base58::from_check(inp)); if data.len() != 78 { return Err(base58::Error::InvalidLength(data.len())); @@ -442,19 +447,21 @@ impl FromBase58 for ExtendedPubKey { child_number: child_number, chain_code: ChainCode::from(&data[13..45]), public_key: try!(PublicKey::from_slice(&s, - &data[45..78]).map_err(|e| - base58::Error::Other(e.to_string()))) + &data[45..78]).map_err(|e| + base58::Error::Other(e.to_string()))) }) } } #[cfg(test)] mod tests { + use std::str::FromStr; + use std::string::ToString; + use secp256k1::Secp256k1; use serialize::hex::FromHex; use network::constants::Network::{self, Bitcoin}; - use util::base58::{FromBase58, ToBase58}; use super::{ChildNumber, ExtendedPrivKey, ExtendedPubKey}; use super::ChildNumber::{Hardened, Normal}; @@ -484,11 +491,11 @@ mod tests { } // Check result against expected base58 - assert_eq!(&sk.to_base58check()[..], expected_sk); - assert_eq!(&pk.to_base58check()[..], expected_pk); + assert_eq!(&sk.to_string()[..], expected_sk); + assert_eq!(&pk.to_string()[..], expected_pk); // Check decoded base58 against result - let decoded_sk = FromBase58::from_base58check(expected_sk); - let decoded_pk = FromBase58::from_base58check(expected_pk); + let decoded_sk = ExtendedPrivKey::from_str(expected_sk); + let decoded_pk = ExtendedPubKey::from_str(expected_pk); assert_eq!(Ok(sk), decoded_sk); assert_eq!(Ok(pk), decoded_pk); } diff --git a/src/util/contracthash.rs b/src/util/contracthash.rs index 935e2bb6..b430ed4a 100644 --- a/src/util/contracthash.rs +++ b/src/util/contracthash.rs @@ -295,7 +295,6 @@ mod tests { use blockdata::script::Script; use network::constants::Network; - use util::base58::ToBase58; use super::*; @@ -320,7 +319,7 @@ mod tests { let contract = hex!("5032534894ffbf32c1f1c0d3089b27c98fd991d5d7329ebd7d711223e2cde5a9417a1fa3e852c576"); let addr = create_address(&secp, Network::Testnet, &contract, keys, &alpha_template!()).unwrap(); - assert_eq!(addr.to_base58check(), "2N3zXjbwdTcPsJiy8sUK9FhWJhqQCxA8Jjr".to_owned()); + assert_eq!(addr.to_string(), "2N3zXjbwdTcPsJiy8sUK9FhWJhqQCxA8Jjr".to_owned()); } #[test]