address: implement Bech32 support
This commit is contained in:
parent
9884bec577
commit
65d8df08b8
|
@ -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"
|
||||||
|
|
|
@ -90,3 +90,5 @@ See Transaction::verify and Script::verify methods.
|
||||||
|
|
||||||
* Un-reversed the Debug output for Sha256dHash
|
* Un-reversed the Debug output for Sha256dHash
|
||||||
|
|
||||||
|
* Add bech32 support
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -18,7 +18,8 @@
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::string::ToString;
|
use std::string::ToString;
|
||||||
|
|
||||||
use secp256k1::{self, Secp256k1};
|
use bitcoin_bech32::{self, WitnessProgram};
|
||||||
|
use secp256k1::Secp256k1;
|
||||||
use secp256k1::key::{PublicKey, SecretKey};
|
use secp256k1::key::{PublicKey, SecretKey};
|
||||||
|
|
||||||
use blockdata::script;
|
use blockdata::script;
|
||||||
|
@ -26,32 +27,26 @@ use blockdata::opcodes;
|
||||||
use network::constants::Network;
|
use network::constants::Network;
|
||||||
use util::hash::Hash160;
|
use util::hash::Hash160;
|
||||||
use util::base58;
|
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 {
|
||||||
|
@ -59,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(
|
||||||
|
if compressed {
|
||||||
Hash160::from_data(&pk.serialize()[..])
|
Hash160::from_data(&pk.serialize()[..])
|
||||||
} else {
|
} else {
|
||||||
Hash160::from_data(&pk.serialize_uncompressed()[..])
|
Hash160::from_data(&pk.serialize_uncompressed()[..])
|
||||||
}
|
}
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,29 +69,33 @@ 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()
|
||||||
}
|
}
|
||||||
|
@ -103,40 +103,81 @@ impl Address {
|
||||||
|
|
||||||
impl ToString for Address {
|
impl ToString for Address {
|
||||||
fn to_string(&self) -> String {
|
fn to_string(&self) -> String {
|
||||||
|
match self.payload {
|
||||||
|
Payload::PubkeyHash(ref hash) => {
|
||||||
let mut prefixed = [0; 21];
|
let mut prefixed = [0; 21];
|
||||||
prefixed[0] = match (self.network, self.ty) {
|
prefixed[0] = match self.network {
|
||||||
(Network::Bitcoin, Type::PubkeyHash) => 0,
|
Network::Bitcoin => 0,
|
||||||
(Network::Bitcoin, Type::ScriptHash) => 5,
|
Network::Testnet => 111,
|
||||||
(Network::Testnet, Type::PubkeyHash) => 111,
|
|
||||||
(Network::Testnet, Type::ScriptHash) => 196
|
|
||||||
};
|
};
|
||||||
prefixed[1..].copy_from_slice(&self.hash[..]);
|
prefixed[1..].copy_from_slice(&hash[..]);
|
||||||
base58::check_encode_slice(&prefixed[..])
|
base58::check_encode_slice(&prefixed[..])
|
||||||
}
|
}
|
||||||
|
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 FromStr for Address {
|
impl FromStr for Address {
|
||||||
type Err = base58::Error;
|
type Err = Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Address, base58::Error> {
|
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),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Base 58
|
||||||
let data = try!(base58::from_check(s));
|
let data = try!(base58::from_check(s));
|
||||||
|
|
||||||
if data.len() != 21 {
|
if data.len() != 21 {
|
||||||
return Err(base58::Error::InvalidLength(data.len()));
|
return Err(Error::Base58(base58::Error::InvalidLength(data.len())));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (network, ty) = match data[0] {
|
let (network, payload) = match data[0] {
|
||||||
0 => (Network::Bitcoin, Type::PubkeyHash),
|
0 => (
|
||||||
5 => (Network::Bitcoin, Type::ScriptHash),
|
Network::Bitcoin,
|
||||||
111 => (Network::Testnet, Type::PubkeyHash),
|
Payload::PubkeyHash(Hash160::from(&data[1..]))
|
||||||
196 => (Network::Testnet, Type::ScriptHash),
|
),
|
||||||
x => { return Err(base58::Error::InvalidVersion(vec![x])); }
|
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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -172,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))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,21 +260,21 @@ impl ToString for Privkey {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Privkey {
|
impl FromStr for Privkey {
|
||||||
type Err = base58::Error;
|
type Err = Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Privkey, base58::Error> {
|
fn from_str(s: &str) -> Result<Privkey, Error> {
|
||||||
let data = try!(base58::from_check(s));
|
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();
|
||||||
|
@ -269,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_string(), "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM");
|
assert_eq!(&addr.to_string(), "132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM");
|
||||||
assert_eq!(Address::from_str("132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM"), Ok(addr));
|
assert_eq!(Address::from_str("132F25rTsvBdp9JzLLBHP5mvGY66i1xdiM").unwrap(), addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -295,14 +337,15 @@ mod tests {
|
||||||
#[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_string(), "33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k");
|
assert_eq!(&addr.to_string(), "33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k");
|
||||||
assert_eq!(Address::from_str("33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k"), Ok(addr));
|
assert_eq!(Address::from_str("33iFwdLuRpW1uK1RTRqsoi8rR4NpDzk66k").unwrap(), addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -311,7 +354,77 @@ mod tests {
|
||||||
let addr = Address::from_script(Testnet, &script);
|
let addr = Address::from_script(Testnet, &script);
|
||||||
|
|
||||||
assert_eq!(&addr.to_string(), "2N3zXjbwdTcPsJiy8sUK9FhWJhqQCxA8Jjr");
|
assert_eq!(&addr.to_string(), "2N3zXjbwdTcPsJiy8sUK9FhWJhqQCxA8Jjr");
|
||||||
assert_eq!(Address::from_str("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]
|
||||||
|
|
|
@ -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[..])
|
||||||
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue