diff --git a/Cargo.toml b/Cargo.toml index cc5fc7c0..4ef3fe0b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ path = "src/lib.rs" bitcoinconsenus = ["bitcoinconsensus"] [dependencies] +bitcoin-bech32 = "0.5.1" byteorder = "1.1" rand = "0.3" rust-crypto = "0.2" diff --git a/README.md b/README.md index 153e1798..f12e22c7 100644 --- a/README.md +++ b/README.md @@ -88,3 +88,7 @@ See Transaction::verify and Script::verify methods. * Replaced Base58 traits with encode_slice, check_encode_slice, from and from_check functions in the base58 module. +* Un-reversed the Debug output for Sha256dHash + +* Add bech32 support + diff --git a/src/lib.rs b/src/lib.rs index 97c89611..af4fd673 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,6 +41,7 @@ #![deny(unused_mut)] #![deny(missing_docs)] +extern crate bitcoin_bech32; extern crate byteorder; extern crate crypto; extern crate rand; diff --git a/src/util/address.rs b/src/util/address.rs index 9503abc4..18d381dd 100644 --- a/src/util/address.rs +++ b/src/util/address.rs @@ -15,40 +15,38 @@ //! Support for ordinary base58 Bitcoin addresses and private keys //! -use secp256k1::{self, Secp256k1}; +use std::str::FromStr; +use std::string::ToString; + +use bitcoin_bech32::{self, WitnessProgram}; +use secp256k1::Secp256k1; use secp256k1::key::{PublicKey, SecretKey}; use blockdata::script; use blockdata::opcodes; use network::constants::Network; use util::hash::Hash160; -use util::base58::{self, FromBase58, ToBase58}; +use util::base58; +use util::Error; /// The method used to produce an address -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum Type { - /// Standard pay-to-pkhash address - PubkeyHash, - /// New-fangled P2SH address - ScriptHash +#[derive(Clone, PartialEq, Debug)] +pub enum Payload { + /// pay-to-pkhash address + PubkeyHash(Hash160), + /// P2SH address + ScriptHash(Hash160), + /// Segwit address + WitnessProgram(WitnessProgram), } -/// An address-related error -#[derive(Clone, PartialEq, Eq, Debug)] -pub enum Error { - /// Private key did not represent a valid ECDSA secret key - Secp(secp256k1::Error) -} - -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, PartialEq)] /// A Bitcoin address pub struct Address { /// The type of the address - pub ty: Type, + pub payload: Payload, /// The network on which this address is usable pub network: Network, - /// The pubkeyhash that this address encodes - pub hash: Hash160 } impl Address { @@ -56,13 +54,14 @@ impl Address { #[inline] pub fn from_key(network: Network, pk: &PublicKey, compressed: bool) -> Address { Address { - ty: Type::PubkeyHash, network: network, - hash: if compressed { - Hash160::from_data(&pk.serialize()[..]) - } else { - Hash160::from_data(&pk.serialize_uncompressed()[..]) - } + payload: Payload::PubkeyHash( + if compressed { + Hash160::from_data(&pk.serialize()[..]) + } else { + Hash160::from_data(&pk.serialize_uncompressed()[..]) + } + ), } } @@ -70,74 +69,122 @@ impl Address { #[inline] pub fn from_script(network: Network, script: &script::Script) -> Address { Address { - ty: Type::ScriptHash, network: network, - hash: Hash160::from_data(&script[..]) + payload: Payload::ScriptHash(Hash160::from_data(&script[..])), } } /// Generates a script pubkey spending to this address #[inline] pub fn script_pubkey(&self) -> script::Script { - match self.ty { - Type::PubkeyHash => { + match self.payload { + Payload::PubkeyHash(ref hash) => { script::Builder::new() .push_opcode(opcodes::All::OP_DUP) .push_opcode(opcodes::All::OP_HASH160) - .push_slice(&self.hash[..]) + .push_slice(&hash[..]) .push_opcode(opcodes::All::OP_EQUALVERIFY) .push_opcode(opcodes::All::OP_CHECKSIG) - } - Type::ScriptHash => { + }, + Payload::ScriptHash(ref hash) => { script::Builder::new() .push_opcode(opcodes::All::OP_HASH160) - .push_slice(&self.hash[..]) + .push_slice(&hash[..]) .push_opcode(opcodes::All::OP_EQUAL) + }, + Payload::WitnessProgram(ref witprog) => { + script::Builder::new() + .push_int(witprog.version() as i64) + .push_slice(witprog.program()) } }.into_script() } } -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 +impl ToString for Address { + fn to_string(&self) -> String { + match self.payload { + Payload::PubkeyHash(ref hash) => { + let mut prefixed = [0; 21]; + prefixed[0] = match self.network { + Network::Bitcoin => 0, + Network::Testnet => 111, + }; + prefixed[1..].copy_from_slice(&hash[..]); + base58::check_encode_slice(&prefixed[..]) } - ]; - ret.extend(self.hash[..].iter().cloned()); - ret + Payload::ScriptHash(ref hash) => { + let mut prefixed = [0; 21]; + prefixed[0] = match self.network { + Network::Bitcoin => 5, + Network::Testnet => 196, + }; + prefixed[1..].copy_from_slice(&hash[..]); + base58::check_encode_slice(&prefixed[..]) + } + Payload::WitnessProgram(ref witprog) => { + witprog.to_address() + }, + } } } -impl FromBase58 for Address { - fn from_base58_layout(data: Vec) -> Result { - if data.len() != 21 { - return Err(base58::Error::InvalidLength(data.len())); +impl FromStr for Address { + type Err = Error; + + fn from_str(s: &str) -> Result { + // bech32 (note that upper or lowercase is allowed but NOT mixed case) + if &s.as_bytes()[0..3] == b"bc1" || &s.as_bytes()[0..3] == b"tb1" || + &s.as_bytes()[0..3] == b"BC1" || &s.as_bytes()[0..3] == b"TB1" { + let witprog = try!(WitnessProgram::from_address(s)); + let network = match witprog.network() { + bitcoin_bech32::constants::Network::Bitcoin => Network::Bitcoin, + bitcoin_bech32::constants::Network::Testnet => Network::Testnet, + _ => panic!("unknown network") + }; + return Ok(Address { + network: network, + payload: Payload::WitnessProgram(witprog), + }); } - let (network, ty) = match data[0] { - 0 => (Network::Bitcoin, Type::PubkeyHash), - 5 => (Network::Bitcoin, Type::ScriptHash), - 111 => (Network::Testnet, Type::PubkeyHash), - 196 => (Network::Testnet, Type::ScriptHash), - x => { return Err(base58::Error::InvalidVersion(vec![x])); } + // Base 58 + let data = try!(base58::from_check(s)); + + if data.len() != 21 { + return Err(Error::Base58(base58::Error::InvalidLength(data.len()))); + } + + let (network, payload) = match data[0] { + 0 => ( + Network::Bitcoin, + Payload::PubkeyHash(Hash160::from(&data[1..])) + ), + 5 => ( + Network::Bitcoin, + Payload::ScriptHash(Hash160::from(&data[1..])) + ), + 111 => ( + Network::Testnet, + Payload::PubkeyHash(Hash160::from(&data[1..])) + ), + 196 => ( + Network::Testnet, + Payload::ScriptHash(Hash160::from(&data[1..])) + ), + x => return Err(Error::Base58(base58::Error::InvalidVersion(vec![x]))) }; Ok(Address { - ty: ty, network: network, - hash: Hash160::from(&data[1..]) + payload: payload, }) } } 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()) } } @@ -166,7 +213,7 @@ impl Privkey { /// Converts a private key to an address #[inline] pub fn to_address(&self, secp: &Secp256k1) -> Result { - let key = try!(PublicKey::from_secret_key(secp, &self.key).map_err(Error::Secp)); + let key = try!(PublicKey::from_secret_key(secp, &self.key)); Ok(Address::from_key(self.network, &key, self.compressed)) } @@ -195,32 +242,39 @@ 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 = Error; + + fn from_str(s: &str) -> Result { + let data = try!(base58::from_check(s)); + let compressed = match data.len() { 33 => false, 34 => true, - _ => { return Err(base58::Error::InvalidLength(data.len())); } + _ => { return Err(Error::Base58(base58::Error::InvalidLength(data.len()))); } }; let network = match data[0] { 128 => Network::Bitcoin, 239 => Network::Testnet, - x => { return Err(base58::Error::InvalidVersion(vec![x])); } + x => { return Err(Error::Base58(base58::Error::InvalidVersion(vec![x]))); } }; let secp = Secp256k1::without_caps(); @@ -237,6 +291,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 +301,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())); @@ -254,14 +310,15 @@ mod tests { #[test] fn test_p2pkh_address_58() { let addr = Address { - ty: Type::PubkeyHash, network: Bitcoin, - hash: Hash160::from(&"162c5ea71c0b23f5b9022ef047c4a86470a5b070".from_hex().unwrap()[..]) + payload: Payload::PubkeyHash( + Hash160::from(&"162c5ea71c0b23f5b9022ef047c4a86470a5b070".from_hex().unwrap()[..]) + ), }; 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").unwrap(), addr); } #[test] @@ -270,24 +327,25 @@ 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] fn test_p2sh_address_58() { let addr = Address { - ty: Type::ScriptHash, network: Bitcoin, - hash: Hash160::from(&"162c5ea71c0b23f5b9022ef047c4a86470a5b070".from_hex().unwrap()[..]) + payload: Payload::ScriptHash( + Hash160::from(&"162c5ea71c0b23f5b9022ef047c4a86470a5b070".from_hex().unwrap()[..]) + ), }; 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").unwrap(), addr); } #[test] @@ -295,31 +353,101 @@ 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").unwrap(), addr); + } + + #[test] + fn test_bip173_vectors() { + let addrstr = "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4"; + let addr = Address::from_str(addrstr).unwrap(); + assert_eq!(addr.network, Bitcoin); + assert_eq!(addr.script_pubkey(), hex_script!("0014751e76e8199196d454941c45d1b3a323f1433bd6")); + // skip round-trip because we'll serialize to lowercase which won't match + + let addrstr = "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7"; + let addr = Address::from_str(addrstr).unwrap(); + assert_eq!(addr.network, Testnet); + assert_eq!(addr.script_pubkey(), hex_script!("00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262")); + assert_eq!(addr.to_string(), addrstr); + + let addrstr = "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx"; + let addr = Address::from_str(addrstr).unwrap(); + assert_eq!(addr.network, Bitcoin); + assert_eq!(addr.script_pubkey(), hex_script!("5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6")); + assert_eq!(addr.to_string(), addrstr); + + let addrstr = "BC1SW50QA3JX3S"; + let addr = Address::from_str(addrstr).unwrap(); + assert_eq!(addr.network, Bitcoin); + assert_eq!(addr.script_pubkey(), hex_script!("6002751e")); + // skip round trip cuz caps + + let addrstr = "bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj"; + let addr = Address::from_str(addrstr).unwrap(); + assert_eq!(addr.network, Bitcoin); + assert_eq!(addr.script_pubkey(), hex_script!("5210751e76e8199196d454941c45d1b3a323")); + assert_eq!(addr.to_string(), addrstr); + + let addrstr = "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy"; + let addr = Address::from_str(addrstr).unwrap(); + assert_eq!(addr.network, Testnet); + assert_eq!(addr.script_pubkey(), hex_script!("0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433")); + assert_eq!(addr.to_string(), addrstr); + + // bad vectors + let addrstr = "tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty"; // invalid hrp + assert!(Address::from_str(addrstr).is_err()); + + let addrstr = "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5"; // invalid checksum + assert!(Address::from_str(addrstr).is_err()); + + let addrstr = "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2"; // invalid witness version + assert!(Address::from_str(addrstr).is_err()); + + let addrstr = "bc1rw5uspcuh"; // invalid program length + assert!(Address::from_str(addrstr).is_err()); + + let addrstr = "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90"; // invalid program length + assert!(Address::from_str(addrstr).is_err()); + + let addrstr = "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P"; // invalid program length for wit v0 + assert!(Address::from_str(addrstr).is_err()); + + let addrstr = "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7"; // mixed case + assert!(Address::from_str(addrstr).is_err()); + + let addrstr = "bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du"; // zero padding of more than 4 bits + assert!(Address::from_str(addrstr).is_err()); + + let addrstr = "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv"; // nonzero padding + assert!(Address::from_str(addrstr).is_err()); + + let addrstr = "bc1gmk9yu"; // empty data section + assert!(Address::from_str(addrstr).is_err()); } #[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..bd1a68b7 100644 --- a/src/util/contracthash.rs +++ b/src/util/contracthash.rs @@ -213,8 +213,9 @@ pub fn create_address(secp: &Secp256k1, let script = try!(template.to_script(&keys)); Ok(address::Address { network: network, - ty: address::Type::ScriptHash, - hash: hash::Hash160::from_data(&script[..]) + payload: address::Payload::ScriptHash( + hash::Hash160::from_data(&script[..]) + ), }) } @@ -295,7 +296,6 @@ mod tests { use blockdata::script::Script; use network::constants::Network; - use util::base58::ToBase58; use super::*; @@ -320,7 +320,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] diff --git a/src/util/hash.rs b/src/util/hash.rs index 24bd6387..5126734d 100644 --- a/src/util/hash.rs +++ b/src/util/hash.rs @@ -352,21 +352,43 @@ impl serde::Deserialize for Sha256dHash { } } -// Consensus encoding (little-endian) +// Debug encodings (no reversing) +impl fmt::Debug for Sha256dHash { + /// Output the raw sha256d hash, not reversing it (unlike Display and what Core does for user display) + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let &Sha256dHash(data) = self; + for ch in data.iter() { + try!(write!(f, "{:02x}", ch)); + } + Ok(()) + } +} + +impl fmt::Debug for Hash160 { + /// Output the raw hash160 hash, not reversing it (nothing reverses the output of ripemd160 in Bitcoin) + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let &Hash160(data) = self; + for ch in data.iter() { + try!(write!(f, "{:02x}", ch)); + } + Ok(()) + } +} + +// Consensus encoding (no reversing) impl_newtype_consensus_encoding!(Hash32); impl_newtype_consensus_encoding!(Hash48); impl_newtype_consensus_encoding!(Hash64); impl_newtype_consensus_encoding!(Sha256dHash); -impl fmt::Debug for Sha256dHash { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) } -} - +// User RPC/display encoding (reversed) impl fmt::Display for Sha256dHash { + /// Output the sha256d hash in reverse, copying Bitcoin Core's behaviour fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) } } impl fmt::LowerHex for Sha256dHash { + /// Output the sha256d hash in reverse, copying Bitcoin Core's behaviour fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let &Sha256dHash(data) = self; for ch in data.iter().rev() { @@ -377,6 +399,7 @@ impl fmt::LowerHex for Sha256dHash { } impl fmt::UpperHex for Sha256dHash { + /// Output the sha256d hash in reverse, copying Bitcoin Core's behaviour fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let &Sha256dHash(data) = self; for ch in data.iter().rev() { @@ -449,7 +472,7 @@ mod tests { assert_eq!(format!("{}", Sha256dHash::from_data(&[])), "56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d"); assert_eq!(format!("{:?}", Sha256dHash::from_data(&[])), - "56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d"); + "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456"); assert_eq!(format!("{:x}", Sha256dHash::from_data(&[])), "56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d"); assert_eq!(format!("{:X}", Sha256dHash::from_data(&[])), diff --git a/src/util/mod.rs b/src/util/mod.rs index 1a83a6df..903c7e5a 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -29,6 +29,9 @@ pub mod uint; use std::{error, fmt, io}; +use bitcoin_bech32; +use secp256k1; + /// A trait which allows numbers to act as fixed-size bit arrays pub trait BitArray { /// Is bit set? @@ -55,6 +58,10 @@ pub trait BitArray { pub enum Error { /// An I/O error Io(io::Error), + /// Base58 encoding error + Base58(base58::Error), + /// Bech32 encoding error + Bech32(bitcoin_bech32::Error), /// Error from the `byteorder` crate ByteOrder(io::Error), /// Network magic was not what we expected @@ -69,6 +76,8 @@ pub enum Error { ParseFailed, /// An object was added but it does not link into existing history PrevHashNotFound, + /// secp-related error + Secp256k1(secp256k1::Error), /// The `target` field of a block header did not match the expected difficulty SpvBadTarget, /// The header hash is not below the target @@ -81,10 +90,13 @@ impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Error::Io(ref e) => fmt::Display::fmt(e, f), + Error::Base58(ref e) => fmt::Display::fmt(e, f), + Error::Bech32(ref e) => fmt::Display::fmt(e, f), Error::ByteOrder(ref e) => fmt::Display::fmt(e, f), Error::BadNetworkMagic(exp, got) => write!(f, "expected network magic 0x{:x}, got 0x{:x}", exp, got), Error::BadNetworkMessage(ref got) => write!(f, "incorrect network message {}", got), Error::Detail(ref s, ref e) => write!(f, "{}: {}", s, e), + Error::Secp256k1(ref e) => fmt::Display::fmt(e, f), ref x => f.write_str(error::Error::description(x)) } } @@ -94,8 +106,11 @@ impl error::Error for Error { fn cause(&self) -> Option<&error::Error> { match *self { Error::Io(ref e) => Some(e), + Error::Base58(ref e) => Some(e), + Error::Bech32(ref e) => Some(e), Error::ByteOrder(ref e) => Some(e), Error::Detail(_, ref e) => Some(e), + Error::Secp256k1(ref e) => Some(e), _ => None } } @@ -103,6 +118,8 @@ impl error::Error for Error { fn description(&self) -> &str { match *self { Error::Io(ref e) => e.description(), + Error::Base58(ref e) => e.description(), + Error::Bech32(ref e) => e.description(), Error::ByteOrder(ref e) => e.description(), Error::BadNetworkMagic(_, _) => "incorrect network magic", Error::BadNetworkMessage(_) => "incorrect/unexpected network message", @@ -110,6 +127,7 @@ impl error::Error for Error { Error::BlockNotFound => "no such block", Error::ParseFailed => "parsing error", Error::PrevHashNotFound => "prevhash not found", + Error::Secp256k1(ref e) => e.description(), Error::SpvBadTarget => "target incorrect", Error::SpvBadProofOfWork => "target correct but not attained", Error::Detail(_, ref e) => e.description() @@ -122,4 +140,21 @@ pub fn propagate_err(s: String, res: Result) -> Result { res.map_err(|err| Error::Detail(s, Box::new(err))) } +impl From for Error { + fn from(e: base58::Error) -> Error { + Error::Base58(e) + } +} + +impl From for Error { + fn from(e: bitcoin_bech32::Error) -> Error { + Error::Bech32(e) + } +} + +impl From for Error { + fn from(e: secp256k1::Error) -> Error { + Error::Secp256k1(e) + } +}