Merge pull request #428 from stevenroose/no-witness-uncompressed
Don't allow uncompressed pks in witness addresses
This commit is contained in:
commit
45da3add10
|
@ -64,6 +64,8 @@ pub enum Error {
|
||||||
InvalidWitnessProgramLength(usize),
|
InvalidWitnessProgramLength(usize),
|
||||||
/// A v0 witness program must be either of length 20 or 32.
|
/// A v0 witness program must be either of length 20 or 32.
|
||||||
InvalidSegwitV0ProgramLength(usize),
|
InvalidSegwitV0ProgramLength(usize),
|
||||||
|
/// An uncompressed pubkey was used where it is not allowed.
|
||||||
|
UncompressedPubkey,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
|
@ -73,15 +75,14 @@ impl fmt::Display for Error {
|
||||||
Error::Bech32(ref e) => write!(f, "bech32: {}", e),
|
Error::Bech32(ref e) => write!(f, "bech32: {}", e),
|
||||||
Error::EmptyBech32Payload => write!(f, "the bech32 payload was empty"),
|
Error::EmptyBech32Payload => write!(f, "the bech32 payload was empty"),
|
||||||
Error::InvalidWitnessVersion(v) => write!(f, "invalid witness script version: {}", v),
|
Error::InvalidWitnessVersion(v) => write!(f, "invalid witness script version: {}", v),
|
||||||
Error::InvalidWitnessProgramLength(l) => write!(
|
Error::InvalidWitnessProgramLength(l) => write!(f,
|
||||||
f,
|
"the witness program must be between 2 and 40 bytes in length: length={}", l,
|
||||||
"the witness program must be between 2 and 40 bytes in length: lengh={}",
|
|
||||||
l
|
|
||||||
),
|
),
|
||||||
Error::InvalidSegwitV0ProgramLength(l) => write!(
|
Error::InvalidSegwitV0ProgramLength(l) => write!(f,
|
||||||
f,
|
"a v0 witness program must be either of length 20 or 32 bytes: length={}", l,
|
||||||
"a v0 witness program must be either of length 20 or 32 bytes: length={}",
|
),
|
||||||
l
|
Error::UncompressedPubkey => write!(f,
|
||||||
|
"an uncompressed pubkey was used where it is not allowed",
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -261,22 +262,34 @@ impl Address {
|
||||||
|
|
||||||
/// Create a witness pay to public key address from a public key
|
/// Create a witness pay to public key address from a public key
|
||||||
/// This is the native segwit address type for an output redeemable with a single signature
|
/// This is the native segwit address type for an output redeemable with a single signature
|
||||||
pub fn p2wpkh(pk: &key::PublicKey, network: Network) -> Address {
|
///
|
||||||
|
/// Will only return an Error when an uncompressed public key is provided.
|
||||||
|
pub fn p2wpkh(pk: &key::PublicKey, network: Network) -> Result<Address, Error> {
|
||||||
|
if !pk.compressed {
|
||||||
|
return Err(Error::UncompressedPubkey);
|
||||||
|
}
|
||||||
|
|
||||||
let mut hash_engine = WPubkeyHash::engine();
|
let mut hash_engine = WPubkeyHash::engine();
|
||||||
pk.write_into(&mut hash_engine);
|
pk.write_into(&mut hash_engine);
|
||||||
|
|
||||||
Address {
|
Ok(Address {
|
||||||
network: network,
|
network: network,
|
||||||
payload: Payload::WitnessProgram {
|
payload: Payload::WitnessProgram {
|
||||||
version: bech32::u5::try_from_u8(0).expect("0<32"),
|
version: bech32::u5::try_from_u8(0).expect("0<32"),
|
||||||
program: WPubkeyHash::from_engine(hash_engine)[..].to_vec(),
|
program: WPubkeyHash::from_engine(hash_engine)[..].to_vec(),
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a pay to script address that embeds a witness pay to public key
|
/// Create a pay to script address that embeds a witness pay to public key
|
||||||
/// This is a segwit address type that looks familiar (as p2sh) to legacy clients
|
/// This is a segwit address type that looks familiar (as p2sh) to legacy clients
|
||||||
pub fn p2shwpkh(pk: &key::PublicKey, network: Network) -> Address {
|
///
|
||||||
|
/// Will only return an Error when an uncompressed public key is provided.
|
||||||
|
pub fn p2shwpkh(pk: &key::PublicKey, network: Network) -> Result<Address, Error> {
|
||||||
|
if !pk.compressed {
|
||||||
|
return Err(Error::UncompressedPubkey);
|
||||||
|
}
|
||||||
|
|
||||||
let mut hash_engine = WPubkeyHash::engine();
|
let mut hash_engine = WPubkeyHash::engine();
|
||||||
pk.write_into(&mut hash_engine);
|
pk.write_into(&mut hash_engine);
|
||||||
|
|
||||||
|
@ -284,10 +297,10 @@ impl Address {
|
||||||
.push_int(0)
|
.push_int(0)
|
||||||
.push_slice(&WPubkeyHash::from_engine(hash_engine)[..]);
|
.push_slice(&WPubkeyHash::from_engine(hash_engine)[..]);
|
||||||
|
|
||||||
Address {
|
Ok(Address {
|
||||||
network: network,
|
network: network,
|
||||||
payload: Payload::ScriptHash(ScriptHash::hash(builder.into_script().as_bytes())),
|
payload: Payload::ScriptHash(ScriptHash::hash(builder.into_script().as_bytes())),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a witness pay to script hash address
|
/// Create a witness pay to script hash address
|
||||||
|
@ -591,11 +604,15 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_p2wpkh() {
|
fn test_p2wpkh() {
|
||||||
// stolen from Bitcoin transaction: b3c8c2b6cfc335abbcb2c7823a8453f55d64b2b5125a9a61e8737230cdb8ce20
|
// stolen from Bitcoin transaction: b3c8c2b6cfc335abbcb2c7823a8453f55d64b2b5125a9a61e8737230cdb8ce20
|
||||||
let key = hex_key!("033bc8c83c52df5712229a2f72206d90192366c36428cb0c12b6af98324d97bfbc");
|
let mut key = hex_key!("033bc8c83c52df5712229a2f72206d90192366c36428cb0c12b6af98324d97bfbc");
|
||||||
let addr = Address::p2wpkh(&key, Bitcoin);
|
let addr = Address::p2wpkh(&key, Bitcoin).unwrap();
|
||||||
assert_eq!(&addr.to_string(), "bc1qvzvkjn4q3nszqxrv3nraga2r822xjty3ykvkuw");
|
assert_eq!(&addr.to_string(), "bc1qvzvkjn4q3nszqxrv3nraga2r822xjty3ykvkuw");
|
||||||
assert_eq!(addr.address_type(), Some(AddressType::P2wpkh));
|
assert_eq!(addr.address_type(), Some(AddressType::P2wpkh));
|
||||||
roundtrips(&addr);
|
roundtrips(&addr);
|
||||||
|
|
||||||
|
// Test uncompressed pubkey
|
||||||
|
key.compressed = false;
|
||||||
|
assert_eq!(Address::p2wpkh(&key, Bitcoin), Err(Error::UncompressedPubkey));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -614,11 +631,15 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_p2shwpkh() {
|
fn test_p2shwpkh() {
|
||||||
// stolen from Bitcoin transaction: ad3fd9c6b52e752ba21425435ff3dd361d6ac271531fc1d2144843a9f550ad01
|
// stolen from Bitcoin transaction: ad3fd9c6b52e752ba21425435ff3dd361d6ac271531fc1d2144843a9f550ad01
|
||||||
let key = hex_key!("026c468be64d22761c30cd2f12cbc7de255d592d7904b1bab07236897cc4c2e766");
|
let mut key = hex_key!("026c468be64d22761c30cd2f12cbc7de255d592d7904b1bab07236897cc4c2e766");
|
||||||
let addr = Address::p2shwpkh(&key, Bitcoin);
|
let addr = Address::p2shwpkh(&key, Bitcoin).unwrap();
|
||||||
assert_eq!(&addr.to_string(), "3QBRmWNqqBGme9er7fMkGqtZtp4gjMFxhE");
|
assert_eq!(&addr.to_string(), "3QBRmWNqqBGme9er7fMkGqtZtp4gjMFxhE");
|
||||||
assert_eq!(addr.address_type(), Some(AddressType::P2sh));
|
assert_eq!(addr.address_type(), Some(AddressType::P2sh));
|
||||||
roundtrips(&addr);
|
roundtrips(&addr);
|
||||||
|
|
||||||
|
// Test uncompressed pubkey
|
||||||
|
key.compressed = false;
|
||||||
|
assert_eq!(Address::p2wpkh(&key, Bitcoin), Err(Error::UncompressedPubkey));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Reference in New Issue