Don't allow uncompressed pks in witness addresses

This commit is contained in:
Steven Roose 2020-05-23 23:20:46 +01:00
parent 59b5a73a55
commit ed9bf41ff5
No known key found for this signature in database
GPG Key ID: 2F2A88D7F8D68E87
1 changed files with 34 additions and 10 deletions

View File

@ -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 {
@ -79,6 +81,8 @@ impl fmt::Display for Error {
Error::InvalidSegwitV0ProgramLength(l) => write!(f, Error::InvalidSegwitV0ProgramLength(l) => write!(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",
), ),
} }
} }
@ -257,22 +261,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);
@ -280,10 +296,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
@ -587,11 +603,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]
@ -610,11 +630,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]