Merge pull request #60 from rust-bitcoin/bech32

Add bech32 support
This commit is contained in:
Andrew Poelstra 2018-03-13 16:13:25 +00:00 committed by GitHub
commit b15438b441
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 457 additions and 271 deletions

View File

@ -19,6 +19,7 @@ path = "src/lib.rs"
bitcoinconsenus = ["bitcoinconsensus"] bitcoinconsenus = ["bitcoinconsensus"]
[dependencies] [dependencies]
bitcoin-bech32 = "0.5.1"
byteorder = "1.1" byteorder = "1.1"
rand = "0.3" rand = "0.3"
rust-crypto = "0.2" rust-crypto = "0.2"

View File

@ -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. * 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

View File

@ -41,6 +41,7 @@
#![deny(unused_mut)] #![deny(unused_mut)]
#![deny(missing_docs)] #![deny(missing_docs)]
extern crate bitcoin_bech32;
extern crate byteorder; extern crate byteorder;
extern crate crypto; extern crate crypto;
extern crate rand; extern crate rand;

View File

@ -15,40 +15,38 @@
//! Support for ordinary base58 Bitcoin addresses and private keys //! 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 secp256k1::key::{PublicKey, SecretKey};
use blockdata::script; use blockdata::script;
use blockdata::opcodes; use blockdata::opcodes;
use network::constants::Network; use network::constants::Network;
use util::hash::Hash160; use util::hash::Hash160;
use util::base58::{self, FromBase58, ToBase58}; use util::base58;
use util::Error;
/// The method used to produce an address /// The method used to produce an address
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub enum Type { pub enum Payload {
/// Standard pay-to-pkhash address /// pay-to-pkhash address
PubkeyHash, PubkeyHash(Hash160),
/// New-fangled P2SH address /// P2SH address
ScriptHash ScriptHash(Hash160),
/// Segwit address
WitnessProgram(WitnessProgram),
} }
/// An address-related error #[derive(Clone, PartialEq)]
#[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)]
/// A Bitcoin address /// A Bitcoin address
pub struct Address { pub struct Address {
/// The type of the address /// The type of the address
pub ty: Type, pub payload: Payload,
/// The network on which this address is usable /// The network on which this address is usable
pub network: Network, pub network: Network,
/// The pubkeyhash that this address encodes
pub hash: Hash160
} }
impl Address { impl Address {
@ -56,13 +54,14 @@ impl Address {
#[inline] #[inline]
pub fn from_key(network: Network, pk: &PublicKey, compressed: bool) -> Address { pub fn from_key(network: Network, pk: &PublicKey, compressed: bool) -> Address {
Address { Address {
ty: Type::PubkeyHash,
network: network, network: network,
hash: if compressed { payload: Payload::PubkeyHash(
Hash160::from_data(&pk.serialize()[..]) if compressed {
} else { Hash160::from_data(&pk.serialize()[..])
Hash160::from_data(&pk.serialize_uncompressed()[..]) } else {
} Hash160::from_data(&pk.serialize_uncompressed()[..])
}
),
} }
} }
@ -70,74 +69,122 @@ impl Address {
#[inline] #[inline]
pub fn from_script(network: Network, script: &script::Script) -> Address { pub fn from_script(network: Network, script: &script::Script) -> Address {
Address { Address {
ty: Type::ScriptHash,
network: network, network: network,
hash: Hash160::from_data(&script[..]) payload: Payload::ScriptHash(Hash160::from_data(&script[..])),
} }
} }
/// Generates a script pubkey spending to this address /// Generates a script pubkey spending to this address
#[inline] #[inline]
pub fn script_pubkey(&self) -> script::Script { pub fn script_pubkey(&self) -> script::Script {
match self.ty { match self.payload {
Type::PubkeyHash => { Payload::PubkeyHash(ref hash) => {
script::Builder::new() script::Builder::new()
.push_opcode(opcodes::All::OP_DUP) .push_opcode(opcodes::All::OP_DUP)
.push_opcode(opcodes::All::OP_HASH160) .push_opcode(opcodes::All::OP_HASH160)
.push_slice(&self.hash[..]) .push_slice(&hash[..])
.push_opcode(opcodes::All::OP_EQUALVERIFY) .push_opcode(opcodes::All::OP_EQUALVERIFY)
.push_opcode(opcodes::All::OP_CHECKSIG) .push_opcode(opcodes::All::OP_CHECKSIG)
} },
Type::ScriptHash => { Payload::ScriptHash(ref hash) => {
script::Builder::new() script::Builder::new()
.push_opcode(opcodes::All::OP_HASH160) .push_opcode(opcodes::All::OP_HASH160)
.push_slice(&self.hash[..]) .push_slice(&hash[..])
.push_opcode(opcodes::All::OP_EQUAL) .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() }.into_script()
} }
} }
impl ToBase58 for Address { impl ToString for Address {
fn base58_layout(&self) -> Vec<u8> { fn to_string(&self) -> String {
let mut ret = vec![ match self.payload {
match (self.network, self.ty) { Payload::PubkeyHash(ref hash) => {
(Network::Bitcoin, Type::PubkeyHash) => 0, let mut prefixed = [0; 21];
(Network::Bitcoin, Type::ScriptHash) => 5, prefixed[0] = match self.network {
(Network::Testnet, Type::PubkeyHash) => 111, Network::Bitcoin => 0,
(Network::Testnet, Type::ScriptHash) => 196 Network::Testnet => 111,
};
prefixed[1..].copy_from_slice(&hash[..]);
base58::check_encode_slice(&prefixed[..])
} }
]; Payload::ScriptHash(ref hash) => {
ret.extend(self.hash[..].iter().cloned()); let mut prefixed = [0; 21];
ret 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 { impl FromStr for Address {
fn from_base58_layout(data: Vec<u8>) -> Result<Address, base58::Error> { type Err = Error;
if data.len() != 21 {
return Err(base58::Error::InvalidLength(data.len())); fn from_str(s: &str) -> Result<Address, Error> {
// 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] { // Base 58
0 => (Network::Bitcoin, Type::PubkeyHash), let data = try!(base58::from_check(s));
5 => (Network::Bitcoin, Type::ScriptHash),
111 => (Network::Testnet, Type::PubkeyHash), if data.len() != 21 {
196 => (Network::Testnet, Type::ScriptHash), return Err(Error::Base58(base58::Error::InvalidLength(data.len())));
x => { return Err(base58::Error::InvalidVersion(vec![x])); } }
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 { Ok(Address {
ty: ty,
network: network, network: network,
hash: Hash160::from(&data[1..]) payload: payload,
}) })
} }
} }
impl ::std::fmt::Debug for Address { impl ::std::fmt::Debug for Address {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { 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 /// Converts a private key to an address
#[inline] #[inline]
pub fn to_address(&self, secp: &Secp256k1) -> Result<Address, Error> { pub fn to_address(&self, secp: &Secp256k1) -> Result<Address, Error> {
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)) Ok(Address::from_key(self.network, &key, self.compressed))
} }
@ -195,32 +242,39 @@ impl Privkey {
} }
} }
impl ToBase58 for Privkey { impl ToString for Privkey {
fn base58_layout(&self) -> Vec<u8> { fn to_string(&self) -> String {
let mut ret = vec![ let mut ret = [0; 34];
match self.network { ret[0] = match self.network {
Network::Bitcoin => 128, Network::Bitcoin => 128,
Network::Testnet => 239 Network::Testnet => 239
} };
]; ret[1..33].copy_from_slice(&self.key[..]);
ret.extend(&self.key[..]); if self.compressed {
if self.compressed { ret.push(1); } ret[33] = 1;
ret base58::check_encode_slice(&ret[..])
} else {
base58::check_encode_slice(&ret[..33])
}
} }
} }
impl FromBase58 for Privkey { impl FromStr for Privkey {
fn from_base58_layout(data: Vec<u8>) -> Result<Privkey, base58::Error> { type Err = Error;
fn from_str(s: &str) -> Result<Privkey, Error> {
let data = try!(base58::from_check(s));
let compressed = match data.len() { let compressed = match data.len() {
33 => false, 33 => false,
34 => true, 34 => true,
_ => { return Err(base58::Error::InvalidLength(data.len())); } _ => { return Err(Error::Base58(base58::Error::InvalidLength(data.len()))); }
}; };
let network = match data[0] { let network = match data[0] {
128 => Network::Bitcoin, 128 => Network::Bitcoin,
239 => Network::Testnet, 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(); let secp = Secp256k1::without_caps();
@ -237,6 +291,9 @@ impl FromBase58 for Privkey {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::str::FromStr;
use std::string::ToString;
use secp256k1::Secp256k1; use secp256k1::Secp256k1;
use secp256k1::key::PublicKey; use secp256k1::key::PublicKey;
use serialize::hex::FromHex; use serialize::hex::FromHex;
@ -244,7 +301,6 @@ mod tests {
use blockdata::script::Script; use blockdata::script::Script;
use network::constants::Network::{Bitcoin, Testnet}; use network::constants::Network::{Bitcoin, Testnet};
use util::hash::Hash160; use util::hash::Hash160;
use util::base58::{FromBase58, ToBase58};
use super::*; use super::*;
macro_rules! hex (($hex:expr) => ($hex.from_hex().unwrap())); macro_rules! hex (($hex:expr) => ($hex.from_hex().unwrap()));
@ -254,14 +310,15 @@ mod tests {
#[test] #[test]
fn test_p2pkh_address_58() { fn test_p2pkh_address_58() {
let addr = Address { let addr = Address {
ty: Type::PubkeyHash,
network: Bitcoin, 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.script_pubkey(), hex_script!("76a914162c5ea71c0b23f5b9022ef047c4a86470a5b07088ac"));
assert_eq!(&addr.to_base58check(), "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM"); assert_eq!(&addr.to_string(), "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM");
assert_eq!(FromBase58::from_base58check("132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM"), Ok(addr)); assert_eq!(Address::from_str("132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM").unwrap(), addr);
} }
#[test] #[test]
@ -270,24 +327,25 @@ mod tests {
let key = hex_key!(&secp, "048d5141948c1702e8c95f438815794b87f706a8d4cd2bffad1dc1570971032c9b6042a0431ded2478b5c9cf2d81c124a5e57347a3c63ef0e7716cf54d613ba183"); let key = hex_key!(&secp, "048d5141948c1702e8c95f438815794b87f706a8d4cd2bffad1dc1570971032c9b6042a0431ded2478b5c9cf2d81c124a5e57347a3c63ef0e7716cf54d613ba183");
let addr = Address::from_key(Bitcoin, &key, false); 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 key = hex_key!(&secp, &"03df154ebfcf29d29cc10d5c2565018bce2d9edbab267c31d2caf44a63056cf99f");
let addr = Address::from_key(Testnet, &key, true); let addr = Address::from_key(Testnet, &key, true);
assert_eq!(&addr.to_base58check(), "mqkhEMH6NCeYjFybv7pvFC22MFeaNT9AQC"); assert_eq!(&addr.to_string(), "mqkhEMH6NCeYjFybv7pvFC22MFeaNT9AQC");
} }
#[test] #[test]
fn test_p2sh_address_58() { fn test_p2sh_address_58() {
let addr = Address { let addr = Address {
ty: Type::ScriptHash,
network: Bitcoin, 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.script_pubkey(), hex_script!("a914162c5ea71c0b23f5b9022ef047c4a86470a5b07087"));
assert_eq!(&addr.to_base58check(), "33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k"); assert_eq!(&addr.to_string(), "33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k");
assert_eq!(FromBase58::from_base58check("33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k"), Ok(addr)); assert_eq!(Address::from_str("33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k").unwrap(), addr);
} }
#[test] #[test]
@ -295,31 +353,101 @@ mod tests {
let script = hex_script!("552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae"); let script = hex_script!("552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae");
let addr = Address::from_script(Testnet, &script); let addr = Address::from_script(Testnet, &script);
assert_eq!(&addr.to_base58check(), "2N3zXjbwdTcPsJiy8sUK9FhWJhqQCxA8Jjr"); assert_eq!(&addr.to_string(), "2N3zXjbwdTcPsJiy8sUK9FhWJhqQCxA8Jjr");
assert_eq!(FromBase58::from_base58check("2N3zXjbwdTcPsJiy8sUK9FhWJhqQCxA8Jjr"), Ok(addr)); 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] #[test]
fn test_key_derivation() { fn test_key_derivation() {
// testnet compressed // 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.network(), Testnet);
assert_eq!(sk.is_compressed(), true); assert_eq!(sk.is_compressed(), true);
assert_eq!(&sk.to_base58check(), "cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy"); assert_eq!(&sk.to_string(), "cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy");
let secp = Secp256k1::new(); let secp = Secp256k1::new();
let pk = sk.to_address(&secp).unwrap(); let pk = sk.to_address(&secp).unwrap();
assert_eq!(&pk.to_base58check(), "mqwpxxvfv3QbM8PU8uBx2jaNt9btQqvQNx"); assert_eq!(&pk.to_string(), "mqwpxxvfv3QbM8PU8uBx2jaNt9btQqvQNx");
// mainnet uncompressed // 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.network(), Bitcoin);
assert_eq!(sk.is_compressed(), false); assert_eq!(sk.is_compressed(), false);
assert_eq!(&sk.to_base58check(), "5JYkZjmN7PVMjJUfJWfRFwtuXTGB439XV6faajeHPAM9Z2PT2R3"); assert_eq!(&sk.to_string(), "5JYkZjmN7PVMjJUfJWfRFwtuXTGB439XV6faajeHPAM9Z2PT2R3");
let secp = Secp256k1::new(); let secp = Secp256k1::new();
let pk = sk.to_address(&secp).unwrap(); let pk = sk.to_address(&secp).unwrap();
assert_eq!(&pk.to_base58check(), "1GhQvF6dL8xa6wBxLnWmHcQsurx9RxiMc8"); assert_eq!(&pk.to_string(), "1GhQvF6dL8xa6wBxLnWmHcQsurx9RxiMc8");
} }
} }

View File

@ -16,7 +16,7 @@
use std::{error, fmt}; use std::{error, fmt};
use byteorder::{ByteOrder, LittleEndian, WriteBytesExt}; use byteorder::{ByteOrder, LittleEndian};
use util::hash::Sha256dHash; use util::hash::Sha256dHash;
/// An error that might occur during base58 decoding /// An error that might occur during base58 decoding
@ -84,171 +84,158 @@ static BASE58_DIGITS: [Option<u8>; 128] = [
Some(55), Some(56), Some(57), None, None, None, None, None, // 120-127 Some(55), Some(56), Some(57), None, None, None, None, None, // 120-127
]; ];
/// Trait for objects which can be read as base58 /// Decode base58-encoded string into a byte vector
pub trait FromBase58: Sized { pub fn from(data: &str) -> Result<Vec<u8>, Error> {
/// Constructs an object from the byte-encoding (base 256) // 11/15 is just over log_256(58)
/// representation of its base58 format let mut scratch = vec![0u8; 1 + data.len() * 11 / 15];
fn from_base58_layout(data: Vec<u8>) -> Result<Self, Error>; // Build in base 256
for d58 in data.bytes() {
/// Obtain an object from its base58 encoding // Compute "X = X * 58 + next_digit" in base 256
fn from_base58(data: &str) -> Result<Self, Error> { if d58 as usize > BASE58_DIGITS.len() {
// 11/15 is just over log_256(58) return Err(Error::BadByte(d58));
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);
} }
let mut carry = match BASE58_DIGITS[d58 as usize] {
// Copy leading zeroes directly Some(d58) => d58 as u32,
let mut ret: Vec<u8> = data.bytes().take_while(|&x| x == BASE58_CHARS[0]) None => { return Err(Error::BadByte(d58)); }
.map(|_| 0) };
.collect(); for d256 in scratch.iter_mut().rev() {
// Copy rest of string carry += *d256 as u32 * 58;
ret.extend(scratch.into_iter().skip_while(|&x| x == 0)); *d256 = carry as u8;
FromBase58::from_base58_layout(ret) carry /= 256;
}
/// Obtain an object from its base58check encoding
fn from_base58check(data: &str) -> Result<Self, Error> {
let mut ret: Vec<u8> = 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;
} }
assert_eq!(carry, 0); assert_eq!(carry, 0);
} }
// Copy leading zeroes directly // Copy leading zeroes directly
let mut ret: Vec<u8> = data.iter().take_while(|&&x| x == 0) let mut ret: Vec<u8> = data.bytes().take_while(|&x| x == BASE58_CHARS[0])
.map(|_| BASE58_CHARS[0]) .map(|_| 0)
.collect(); .collect();
// Copy rest of string // Copy rest of string
ret.extend(scratch.into_iter().skip_while(|&x| x == 0) ret.extend(scratch.into_iter().skip_while(|&x| x == 0));
.map(|x| BASE58_CHARS[x as usize])); Ok(ret)
}
/// Decode a base58check-encoded string
pub fn from_check(data: &str) -> Result<Vec<u8>, Error> {
let mut ret: Vec<u8> = 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<I>(data: I) -> String
where
I: Iterator<Item = u8> + 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() String::from_utf8(ret).unwrap()
} }
/// Trait for objects which can be written as base58 /// Directly encode a slice as base58
pub trait ToBase58 { pub fn encode_slice(data: &[u8]) -> String {
/// The serialization to be converted into base58 encode_iter(data.iter().cloned())
fn base58_layout(&self) -> Vec<u8>;
/// 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::<LittleEndian>(checksum).unwrap();
base58_encode_slice(&data)
}
} }
// Trivial implementations for slices and vectors /// Obtain a string with the base58check encoding of a slice
impl<'a> ToBase58 for &'a [u8] { /// (Tack the first 4 256-digits of the object's Bitcoin hash onto the end.)
fn base58_layout(&self) -> Vec<u8> { self.to_vec() } pub fn check_encode_slice(data: &[u8]) -> String {
fn to_base58(&self) -> String { base58_encode_slice(*self) } let checksum = Sha256dHash::from_data(&data);
} encode_iter(
data.iter()
impl<'a> ToBase58 for Vec<u8> { .cloned()
fn base58_layout(&self) -> Vec<u8> { self.clone() } .chain(checksum[0..4].iter().cloned())
fn to_base58(&self) -> String { base58_encode_slice(&self[..]) } )
}
impl FromBase58 for Vec<u8> {
fn from_base58_layout(data: Vec<u8>) -> Result<Vec<u8>, Error> {
Ok(data)
}
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use serialize::hex::FromHex; use serialize::hex::FromHex;
use super::ToBase58; use super::*;
use super::FromBase58;
#[test] #[test]
fn test_base58_encode() { fn test_base58_encode() {
// Basics // Basics
assert_eq!(&(&[0][..]).to_base58(), "1"); assert_eq!(&encode_slice(&[0][..]), "1");
assert_eq!(&(&[1][..]).to_base58(), "2"); assert_eq!(&encode_slice(&[1][..]), "2");
assert_eq!(&(&[58][..]).to_base58(), "21"); assert_eq!(&encode_slice(&[58][..]), "21");
assert_eq!(&(&[13, 36][..]).to_base58(), "211"); assert_eq!(&encode_slice(&[13, 36][..]), "211");
// Leading zeroes // Leading zeroes
assert_eq!(&(&[0, 13, 36][..]).to_base58(), "1211"); assert_eq!(&encode_slice(&[0, 13, 36][..]), "1211");
assert_eq!(&(&[0, 0, 0, 0, 13, 36][..]).to_base58(), "1111211"); assert_eq!(&encode_slice(&[0, 0, 0, 0, 13, 36][..]), "1111211");
// Addresses // Addresses
assert_eq!(&"00f8917303bfa8ef24f292e8fa1419b20460ba064d".from_hex().unwrap().to_base58check(), let addr = "00f8917303bfa8ef24f292e8fa1419b20460ba064d".from_hex().unwrap();
"1PfJpZsjreyVrqeoAfabrRwwjQyoSQMmHH"); assert_eq!(&check_encode_slice(&addr[..]), "1PfJpZsjreyVrqeoAfabrRwwjQyoSQMmHH");
} }
#[test] #[test]
fn test_base58_decode() { fn test_base58_decode() {
// Basics // Basics
assert_eq!(FromBase58::from_base58("1").ok(), Some(vec![0u8])); assert_eq!(from("1").ok(), Some(vec![0u8]));
assert_eq!(FromBase58::from_base58("2").ok(), Some(vec![1u8])); assert_eq!(from("2").ok(), Some(vec![1u8]));
assert_eq!(FromBase58::from_base58("21").ok(), Some(vec![58u8])); assert_eq!(from("21").ok(), Some(vec![58u8]));
assert_eq!(FromBase58::from_base58("211").ok(), Some(vec![13u8, 36])); assert_eq!(from("211").ok(), Some(vec![13u8, 36]));
// Leading zeroes // Leading zeroes
assert_eq!(FromBase58::from_base58("1211").ok(), Some(vec![0u8, 13, 36])); assert_eq!(from("1211").ok(), Some(vec![0u8, 13, 36]));
assert_eq!(FromBase58::from_base58("111211").ok(), Some(vec![0u8, 0, 0, 13, 36])); assert_eq!(from("111211").ok(), Some(vec![0u8, 0, 0, 13, 36]));
// Addresses // Addresses
assert_eq!(FromBase58::from_base58check("1PfJpZsjreyVrqeoAfabrRwwjQyoSQMmHH").ok(), assert_eq!(from_check("1PfJpZsjreyVrqeoAfabrRwwjQyoSQMmHH").ok(),
Some("00f8917303bfa8ef24f292e8fa1419b20460ba064d".from_hex().unwrap())) Some("00f8917303bfa8ef24f292e8fa1419b20460ba064d".from_hex().unwrap()))
} }
#[test] #[test]
fn test_base58_roundtrip() { fn test_base58_roundtrip() {
let s = "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs"; let s = "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs";
let v: Vec<u8> = FromBase58::from_base58check(s).unwrap(); let v: Vec<u8> = from_check(s).unwrap();
assert_eq!(&v.to_base58check(), s); assert_eq!(check_encode_slice(&v[..]), s);
assert_eq!(FromBase58::from_base58check(&v.to_base58check()).ok(), Some(v)); assert_eq!(from_check(&check_encode_slice(&v[..])).ok(), Some(v));
} }
} }

View File

@ -19,9 +19,11 @@
use std::default::Default; use std::default::Default;
use std::io::Cursor; use std::io::Cursor;
use std::{error, fmt}; use std::{error, fmt};
use std::str::FromStr;
use std::string::ToString;
use serde::{Serialize, Deserialize, Serializer, Deserializer}; use serde::{Serialize, Deserialize, Serializer, Deserializer};
use byteorder::{BigEndian, ByteOrder, ReadBytesExt, WriteBytesExt}; use byteorder::{BigEndian, ByteOrder, ReadBytesExt};
use crypto::digest::Digest; use crypto::digest::Digest;
use crypto::hmac::Hmac; use crypto::hmac::Hmac;
use crypto::mac::Mac; use crypto::mac::Mac;
@ -33,7 +35,6 @@ use secp256k1::{self, Secp256k1};
use network::constants::Network; use network::constants::Network;
use util::base58; use util::base58;
use util::base58::{FromBase58, ToBase58};
/// A chain code /// A chain code
pub struct ChainCode([u8; 32]); pub struct ChainCode([u8; 32]);
@ -337,33 +338,36 @@ impl ExtendedPubKey {
} }
} }
impl ToBase58 for ExtendedPrivKey { impl ToString for ExtendedPrivKey {
fn base58_layout(&self) -> Vec<u8> { fn to_string(&self) -> String {
let mut ret = Vec::with_capacity(78); let mut ret = [0; 78];
ret.extend(match self.network { ret[0..4].copy_from_slice(&match self.network {
Network::Bitcoin => [0x04, 0x88, 0xAD, 0xE4], Network::Bitcoin => [0x04, 0x88, 0xAD, 0xE4],
Network::Testnet => [0x04, 0x35, 0x83, 0x94] Network::Testnet => [0x04, 0x35, 0x83, 0x94],
}.iter().cloned()); }[..]);
ret.push(self.depth as u8); ret[4] = self.depth as u8;
ret.extend(self.parent_fingerprint[..].iter().cloned()); ret[5..9].copy_from_slice(&self.parent_fingerprint[..]);
match self.child_number { match self.child_number {
ChildNumber::Hardened(n) => { ChildNumber::Hardened(n) => {
ret.write_u32::<BigEndian>(n + (1 << 31)).unwrap(); BigEndian::write_u32(&mut ret[9..13], n + (1 << 31));
} }
ChildNumber::Normal(n) => { ChildNumber::Normal(n) => {
ret.write_u32::<BigEndian>(n).unwrap(); BigEndian::write_u32(&mut ret[9..13], n);
} }
} }
ret.extend(self.chain_code[..].iter().cloned()); ret[13..45].copy_from_slice(&self.chain_code[..]);
ret.push(0); ret[45] = 0;
ret.extend(self.secret_key[..].iter().cloned()); ret[46..78].copy_from_slice(&self.secret_key[..]);
ret base58::check_encode_slice(&ret[..])
} }
} }
impl FromBase58 for ExtendedPrivKey { impl FromStr for ExtendedPrivKey {
fn from_base58_layout(data: Vec<u8>) -> Result<ExtendedPrivKey, base58::Error> { type Err = base58::Error;
fn from_str(inp: &str) -> Result<ExtendedPrivKey, base58::Error> {
let s = Secp256k1::with_caps(secp256k1::ContextFlag::None); let s = Secp256k1::with_caps(secp256k1::ContextFlag::None);
let data = try!(base58::from_check(inp));
if data.len() != 78 { if data.len() != 78 {
return Err(base58::Error::InvalidLength(data.len())); return Err(base58::Error::InvalidLength(data.len()));
@ -386,40 +390,41 @@ impl FromBase58 for ExtendedPrivKey {
child_number: child_number, child_number: child_number,
chain_code: ChainCode::from(&data[13..45]), chain_code: ChainCode::from(&data[13..45]),
secret_key: try!(SecretKey::from_slice(&s, secret_key: try!(SecretKey::from_slice(&s,
&data[46..78]).map_err(|e| &data[46..78]).map_err(|e|
base58::Error::Other(e.to_string()))) base58::Error::Other(e.to_string())))
}) })
} }
} }
impl ToBase58 for ExtendedPubKey { impl ToString for ExtendedPubKey {
fn base58_layout(&self) -> Vec<u8> { fn to_string(&self) -> String {
let mut ret = Vec::with_capacity(78); let mut ret = [0; 78];
ret.extend(match self.network { ret[0..4].copy_from_slice(&match self.network {
Network::Bitcoin => [0x04u8, 0x88, 0xB2, 0x1E], Network::Bitcoin => [0x04u8, 0x88, 0xB2, 0x1E],
Network::Testnet => [0x04u8, 0x35, 0x87, 0xCF] Network::Testnet => [0x04u8, 0x35, 0x87, 0xCF],
}.iter().cloned()); }[..]);
ret.push(self.depth as u8); ret[4] = self.depth as u8;
ret.extend(self.parent_fingerprint[..].iter().cloned()); ret[5..9].copy_from_slice(&self.parent_fingerprint[..]);
let mut be_n = [0; 4];
match self.child_number { match self.child_number {
ChildNumber::Hardened(n) => { 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) => { 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[13..45].copy_from_slice(&self.chain_code[..]);
ret.extend(self.chain_code[..].iter().cloned()); ret[45..78].copy_from_slice(&self.public_key.serialize()[..]);
ret.extend(self.public_key.serialize()[..].iter().cloned()); base58::check_encode_slice(&ret[..])
ret
} }
} }
impl FromBase58 for ExtendedPubKey { impl FromStr for ExtendedPubKey {
fn from_base58_layout(data: Vec<u8>) -> Result<ExtendedPubKey, base58::Error> { type Err = base58::Error;
fn from_str(inp: &str) -> Result<ExtendedPubKey, base58::Error> {
let s = Secp256k1::with_caps(secp256k1::ContextFlag::None); let s = Secp256k1::with_caps(secp256k1::ContextFlag::None);
let data = try!(base58::from_check(inp));
if data.len() != 78 { if data.len() != 78 {
return Err(base58::Error::InvalidLength(data.len())); return Err(base58::Error::InvalidLength(data.len()));
@ -442,19 +447,21 @@ impl FromBase58 for ExtendedPubKey {
child_number: child_number, child_number: child_number,
chain_code: ChainCode::from(&data[13..45]), chain_code: ChainCode::from(&data[13..45]),
public_key: try!(PublicKey::from_slice(&s, public_key: try!(PublicKey::from_slice(&s,
&data[45..78]).map_err(|e| &data[45..78]).map_err(|e|
base58::Error::Other(e.to_string()))) base58::Error::Other(e.to_string())))
}) })
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::str::FromStr;
use std::string::ToString;
use secp256k1::Secp256k1; use secp256k1::Secp256k1;
use serialize::hex::FromHex; use serialize::hex::FromHex;
use network::constants::Network::{self, Bitcoin}; use network::constants::Network::{self, Bitcoin};
use util::base58::{FromBase58, ToBase58};
use super::{ChildNumber, ExtendedPrivKey, ExtendedPubKey}; use super::{ChildNumber, ExtendedPrivKey, ExtendedPubKey};
use super::ChildNumber::{Hardened, Normal}; use super::ChildNumber::{Hardened, Normal};
@ -484,11 +491,11 @@ mod tests {
} }
// Check result against expected base58 // Check result against expected base58
assert_eq!(&sk.to_base58check()[..], expected_sk); assert_eq!(&sk.to_string()[..], expected_sk);
assert_eq!(&pk.to_base58check()[..], expected_pk); assert_eq!(&pk.to_string()[..], expected_pk);
// Check decoded base58 against result // Check decoded base58 against result
let decoded_sk = FromBase58::from_base58check(expected_sk); let decoded_sk = ExtendedPrivKey::from_str(expected_sk);
let decoded_pk = FromBase58::from_base58check(expected_pk); let decoded_pk = ExtendedPubKey::from_str(expected_pk);
assert_eq!(Ok(sk), decoded_sk); assert_eq!(Ok(sk), decoded_sk);
assert_eq!(Ok(pk), decoded_pk); assert_eq!(Ok(pk), decoded_pk);
} }

View File

@ -213,8 +213,9 @@ pub fn create_address(secp: &Secp256k1,
let script = try!(template.to_script(&keys)); let script = try!(template.to_script(&keys));
Ok(address::Address { Ok(address::Address {
network: network, network: network,
ty: address::Type::ScriptHash, payload: address::Payload::ScriptHash(
hash: hash::Hash160::from_data(&script[..]) hash::Hash160::from_data(&script[..])
),
}) })
} }
@ -295,7 +296,6 @@ mod tests {
use blockdata::script::Script; use blockdata::script::Script;
use network::constants::Network; use network::constants::Network;
use util::base58::ToBase58;
use super::*; use super::*;
@ -320,7 +320,7 @@ mod tests {
let contract = hex!("5032534894ffbf32c1f1c0d3089b27c98fd991d5d7329ebd7d711223e2cde5a9417a1fa3e852c576"); let contract = hex!("5032534894ffbf32c1f1c0d3089b27c98fd991d5d7329ebd7d711223e2cde5a9417a1fa3e852c576");
let addr = create_address(&secp, Network::Testnet, &contract, keys, &alpha_template!()).unwrap(); 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] #[test]

View File

@ -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!(Hash32);
impl_newtype_consensus_encoding!(Hash48); impl_newtype_consensus_encoding!(Hash48);
impl_newtype_consensus_encoding!(Hash64); impl_newtype_consensus_encoding!(Hash64);
impl_newtype_consensus_encoding!(Sha256dHash); impl_newtype_consensus_encoding!(Sha256dHash);
impl fmt::Debug for Sha256dHash { // User RPC/display encoding (reversed)
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) }
}
impl fmt::Display for Sha256dHash { 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) } fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(self, f) }
} }
impl fmt::LowerHex for Sha256dHash { 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 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let &Sha256dHash(data) = self; let &Sha256dHash(data) = self;
for ch in data.iter().rev() { for ch in data.iter().rev() {
@ -377,6 +399,7 @@ impl fmt::LowerHex for Sha256dHash {
} }
impl fmt::UpperHex 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 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let &Sha256dHash(data) = self; let &Sha256dHash(data) = self;
for ch in data.iter().rev() { for ch in data.iter().rev() {
@ -449,7 +472,7 @@ mod tests {
assert_eq!(format!("{}", Sha256dHash::from_data(&[])), assert_eq!(format!("{}", Sha256dHash::from_data(&[])),
"56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d"); "56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d");
assert_eq!(format!("{:?}", Sha256dHash::from_data(&[])), assert_eq!(format!("{:?}", Sha256dHash::from_data(&[])),
"56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d"); "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456");
assert_eq!(format!("{:x}", Sha256dHash::from_data(&[])), assert_eq!(format!("{:x}", Sha256dHash::from_data(&[])),
"56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d"); "56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d");
assert_eq!(format!("{:X}", Sha256dHash::from_data(&[])), assert_eq!(format!("{:X}", Sha256dHash::from_data(&[])),

View File

@ -29,6 +29,9 @@ pub mod uint;
use std::{error, fmt, io}; use std::{error, fmt, io};
use bitcoin_bech32;
use secp256k1;
/// A trait which allows numbers to act as fixed-size bit arrays /// A trait which allows numbers to act as fixed-size bit arrays
pub trait BitArray { pub trait BitArray {
/// Is bit set? /// Is bit set?
@ -55,6 +58,10 @@ pub trait BitArray {
pub enum Error { pub enum Error {
/// An I/O error /// An I/O error
Io(io::Error), Io(io::Error),
/// Base58 encoding error
Base58(base58::Error),
/// Bech32 encoding error
Bech32(bitcoin_bech32::Error),
/// Error from the `byteorder` crate /// Error from the `byteorder` crate
ByteOrder(io::Error), ByteOrder(io::Error),
/// Network magic was not what we expected /// Network magic was not what we expected
@ -69,6 +76,8 @@ pub enum Error {
ParseFailed, ParseFailed,
/// An object was added but it does not link into existing history /// An object was added but it does not link into existing history
PrevHashNotFound, PrevHashNotFound,
/// secp-related error
Secp256k1(secp256k1::Error),
/// The `target` field of a block header did not match the expected difficulty /// The `target` field of a block header did not match the expected difficulty
SpvBadTarget, SpvBadTarget,
/// The header hash is not below the target /// 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 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self { match *self {
Error::Io(ref e) => fmt::Display::fmt(e, f), 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::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::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::BadNetworkMessage(ref got) => write!(f, "incorrect network message {}", got),
Error::Detail(ref s, ref e) => write!(f, "{}: {}", s, e), 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)) ref x => f.write_str(error::Error::description(x))
} }
} }
@ -94,8 +106,11 @@ impl error::Error for Error {
fn cause(&self) -> Option<&error::Error> { fn cause(&self) -> Option<&error::Error> {
match *self { match *self {
Error::Io(ref e) => Some(e), 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::ByteOrder(ref e) => Some(e),
Error::Detail(_, ref e) => Some(e), Error::Detail(_, ref e) => Some(e),
Error::Secp256k1(ref e) => Some(e),
_ => None _ => None
} }
} }
@ -103,6 +118,8 @@ impl error::Error for Error {
fn description(&self) -> &str { fn description(&self) -> &str {
match *self { match *self {
Error::Io(ref e) => e.description(), 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::ByteOrder(ref e) => e.description(),
Error::BadNetworkMagic(_, _) => "incorrect network magic", Error::BadNetworkMagic(_, _) => "incorrect network magic",
Error::BadNetworkMessage(_) => "incorrect/unexpected network message", Error::BadNetworkMessage(_) => "incorrect/unexpected network message",
@ -110,6 +127,7 @@ impl error::Error for Error {
Error::BlockNotFound => "no such block", Error::BlockNotFound => "no such block",
Error::ParseFailed => "parsing error", Error::ParseFailed => "parsing error",
Error::PrevHashNotFound => "prevhash not found", Error::PrevHashNotFound => "prevhash not found",
Error::Secp256k1(ref e) => e.description(),
Error::SpvBadTarget => "target incorrect", Error::SpvBadTarget => "target incorrect",
Error::SpvBadProofOfWork => "target correct but not attained", Error::SpvBadProofOfWork => "target correct but not attained",
Error::Detail(_, ref e) => e.description() Error::Detail(_, ref e) => e.description()
@ -122,4 +140,21 @@ pub fn propagate_err<T>(s: String, res: Result<T, Error>) -> Result<T, Error> {
res.map_err(|err| Error::Detail(s, Box::new(err))) res.map_err(|err| Error::Detail(s, Box::new(err)))
} }
impl From<base58::Error> for Error {
fn from(e: base58::Error) -> Error {
Error::Base58(e)
}
}
impl From<bitcoin_bech32::Error> for Error {
fn from(e: bitcoin_bech32::Error) -> Error {
Error::Bech32(e)
}
}
impl From<secp256k1::Error> for Error {
fn from(e: secp256k1::Error) -> Error {
Error::Secp256k1(e)
}
}