diff --git a/examples/bip32.rs b/examples/bip32.rs
index 04816f49..3c73501d 100644
--- a/examples/bip32.rs
+++ b/examples/bip32.rs
@@ -4,7 +4,7 @@ use std::{env, process};
use std::str::FromStr;
use bitcoin::secp256k1::Secp256k1;
-use bitcoin::util::key::PrivateKey;
+use bitcoin::util::ecdsa::PrivateKey;
use bitcoin::util::bip32::ExtendedPrivKey;
use bitcoin::util::bip32::ExtendedPubKey;
use bitcoin::util::bip32::DerivationPath;
diff --git a/src/blockdata/script.rs b/src/blockdata/script.rs
index 729707d1..1486bc6d 100644
--- a/src/blockdata/script.rs
+++ b/src/blockdata/script.rs
@@ -37,7 +37,7 @@ use hashes::{Hash, hex};
#[cfg(feature="bitcoinconsensus")] use std::convert;
#[cfg(feature="bitcoinconsensus")] use OutPoint;
-use util::key::PublicKey;
+use util::ecdsa::PublicKey;
#[derive(Clone, Default, PartialOrd, Ord, PartialEq, Eq, Hash)]
/// A Bitcoin script
@@ -889,7 +889,7 @@ mod test {
use hashes::hex::{FromHex, ToHex};
use consensus::encode::{deserialize, serialize};
use blockdata::opcodes;
- use util::key::PublicKey;
+ use util::ecdsa::PublicKey;
use util::psbt::serialize::Serialize;
#[test]
diff --git a/src/lib.rs b/src/lib.rs
index e7387de4..9937fca4 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -85,8 +85,8 @@ pub use util::address::AddressType;
pub use util::amount::Amount;
pub use util::amount::Denomination;
pub use util::amount::SignedAmount;
-pub use util::key::PrivateKey;
-pub use util::key::PublicKey;
+pub use util::ecdsa::PrivateKey;
+pub use util::ecdsa::PublicKey;
pub use util::merkleblock::MerkleBlock;
#[cfg(all(test, feature = "unstable"))] use tests::EmptyWrite;
diff --git a/src/util/address.rs b/src/util/address.rs
index 2c26e7cf..ed082761 100644
--- a/src/util/address.rs
+++ b/src/util/address.rs
@@ -21,13 +21,13 @@
//!
//! use bitcoin::network::constants::Network;
//! use bitcoin::util::address::Address;
-//! use bitcoin::util::key;
+//! use bitcoin::util::ecdsa;
//! use bitcoin::secp256k1::Secp256k1;
//! use bitcoin::secp256k1::rand::thread_rng;
//!
//! // Generate random key pair
//! let s = Secp256k1::new();
-//! let public_key = key::PublicKey {
+//! let public_key = ecdsa::PublicKey {
//! compressed: true,
//! key: s.generate_keypair(&mut thread_rng()).1,
//! };
@@ -46,7 +46,7 @@ use hash_types::{PubkeyHash, WPubkeyHash, ScriptHash, WScriptHash};
use blockdata::script;
use network::constants::Network;
use util::base58;
-use util::key;
+use util::ecdsa;
/// Address error.
#[derive(Debug, PartialEq)]
@@ -220,7 +220,7 @@ impl Address {
/// Creates a pay to (compressed) public key hash address from a public key
/// This is the preferred non-witness type address
#[inline]
- pub fn p2pkh(pk: &key::PublicKey, network: Network) -> Address {
+ pub fn p2pkh(pk: &ecdsa::PublicKey, network: Network) -> Address {
let mut hash_engine = PubkeyHash::engine();
pk.write_into(&mut hash_engine).expect("engines don't error");
@@ -244,7 +244,7 @@ impl Address {
/// This is the native segwit address type for an output redeemable with a single signature
///
/// Will only return an Error when an uncompressed public key is provided.
- pub fn p2wpkh(pk: &key::PublicKey, network: Network) -> Result
{
+ pub fn p2wpkh(pk: &ecdsa::PublicKey, network: Network) -> Result {
if !pk.compressed {
return Err(Error::UncompressedPubkey);
}
@@ -265,7 +265,7 @@ impl Address {
/// This is a segwit address type that looks familiar (as p2sh) to legacy clients
///
/// Will only return an Error when an uncompressed public key is provided.
- pub fn p2shwpkh(pk: &key::PublicKey, network: Network) -> Result {
+ pub fn p2shwpkh(pk: &ecdsa::PublicKey, network: Network) -> Result {
if !pk.compressed {
return Err(Error::UncompressedPubkey);
}
@@ -500,7 +500,7 @@ mod tests {
use blockdata::script::Script;
use network::constants::Network::{Bitcoin, Testnet};
- use util::key::PublicKey;
+ use util::ecdsa::PublicKey;
use super::*;
diff --git a/src/util/bip143.rs b/src/util/bip143.rs
index d12477db..78f2dd7b 100644
--- a/src/util/bip143.rs
+++ b/src/util/bip143.rs
@@ -274,7 +274,7 @@ mod tests {
use consensus::encode::deserialize;
use network::constants::Network;
use util::address::Address;
- use util::key::PublicKey;
+ use util::ecdsa::PublicKey;
use hashes::hex::FromHex;
use super::*;
diff --git a/src/util/bip32.rs b/src/util/bip32.rs
index 9e30ba6d..7e544d3a 100644
--- a/src/util/bip32.rs
+++ b/src/util/bip32.rs
@@ -26,8 +26,8 @@ use hashes::{sha512, Hash, HashEngine, Hmac, HmacEngine};
use secp256k1::{self, Secp256k1};
use network::constants::Network;
-use util::{base58, endian};
-use util::key::{self, PublicKey, PrivateKey};
+use util::{base58, endian, key};
+use util::ecdsa::{PublicKey, PrivateKey};
/// A chain code
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
diff --git a/src/util/ecdsa.rs b/src/util/ecdsa.rs
new file mode 100644
index 00000000..e8b2d78e
--- /dev/null
+++ b/src/util/ecdsa.rs
@@ -0,0 +1,511 @@
+// Rust Bitcoin Library
+// Written in 2014 by
+// Andrew Poelstra
+// To the extent possible under law, the author(s) have dedicated all
+// copyright and related and neighboring rights to this software to
+// the public domain worldwide. This software is distributed without
+// any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication
+// along with this software.
+// If not, see .
+//
+
+//! ECDSA Bitcoin Keys
+//!
+//! ECDSA keys used in Bitcoin that can be roundtrip (de)serialized.
+//!
+
+use std::fmt::{self, Write};
+use std::{io, ops};
+use std::str::FromStr;
+
+use secp256k1::{self, Secp256k1};
+use network::constants::Network;
+use hashes::{Hash, hash160};
+use hash_types::{PubkeyHash, WPubkeyHash};
+use util::base58;
+use util::key::Error;
+
+/// A Bitcoin ECDSA public key
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct PublicKey {
+ /// Whether this public key should be serialized as compressed
+ pub compressed: bool,
+ /// The actual ECDSA key
+ pub key: secp256k1::PublicKey,
+}
+
+impl PublicKey {
+ /// Returns bitcoin 160-bit hash of the public key
+ pub fn pubkey_hash(&self) -> PubkeyHash {
+ if self.compressed {
+ PubkeyHash::hash(&self.key.serialize())
+ } else {
+ PubkeyHash::hash(&self.key.serialize_uncompressed())
+ }
+ }
+
+ /// Returns bitcoin 160-bit hash of the public key for witness program
+ pub fn wpubkey_hash(&self) -> Option {
+ if self.compressed {
+ Some(WPubkeyHash::from_inner(
+ hash160::Hash::hash(&self.key.serialize()).into_inner()
+ ))
+ } else {
+ // We can't create witness pubkey hashes for an uncompressed
+ // public keys
+ None
+ }
+ }
+
+ /// Write the public key into a writer
+ pub fn write_into(&self, mut writer: W) -> Result<(), io::Error> {
+ if self.compressed {
+ writer.write_all(&self.key.serialize())
+ } else {
+ writer.write_all(&self.key.serialize_uncompressed())
+ }
+ }
+
+ /// Read the public key from a reader
+ ///
+ /// This internally reads the first byte before reading the rest, so
+ /// use of a `BufReader` is recommended.
+ pub fn read_from(mut reader: R) -> Result {
+ let mut bytes = [0; 65];
+
+ reader.read_exact(&mut bytes[0..1])?;
+ let bytes = if bytes[0] < 4 {
+ &mut bytes[..33]
+ } else {
+ &mut bytes[..65]
+ };
+
+ reader.read_exact(&mut bytes[1..])?;
+ Self::from_slice(bytes).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
+ }
+
+ /// Serialize the public key to bytes
+ pub fn to_bytes(&self) -> Vec {
+ let mut buf = Vec::new();
+ self.write_into(&mut buf).expect("vecs don't error");
+ buf
+ }
+
+ /// Deserialize a public key from a slice
+ pub fn from_slice(data: &[u8]) -> Result {
+ let compressed: bool = match data.len() {
+ 33 => true,
+ 65 => false,
+ len => { return Err(base58::Error::InvalidLength(len).into()); },
+ };
+
+ Ok(PublicKey {
+ compressed: compressed,
+ key: secp256k1::PublicKey::from_slice(data)?,
+ })
+ }
+
+ /// Computes the public key as supposed to be used with this secret
+ pub fn from_private_key(secp: &Secp256k1, sk: &PrivateKey) -> PublicKey {
+ sk.public_key(secp)
+ }
+}
+
+impl fmt::Display for PublicKey {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if self.compressed {
+ for ch in &self.key.serialize()[..] {
+ write!(f, "{:02x}", ch)?;
+ }
+ } else {
+ for ch in &self.key.serialize_uncompressed()[..] {
+ write!(f, "{:02x}", ch)?;
+ }
+ }
+ Ok(())
+ }
+}
+
+impl FromStr for PublicKey {
+ type Err = Error;
+ fn from_str(s: &str) -> Result {
+ let key = secp256k1::PublicKey::from_str(s)?;
+ Ok(PublicKey {
+ key: key,
+ compressed: s.len() == 66
+ })
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq)]
+/// A Bitcoin ECDSA private key
+pub struct PrivateKey {
+ /// Whether this private key should be serialized as compressed
+ pub compressed: bool,
+ /// The network on which this key should be used
+ pub network: Network,
+ /// The actual ECDSA key
+ pub key: secp256k1::SecretKey,
+}
+
+impl PrivateKey {
+ /// Creates a public key from this private key
+ pub fn public_key(&self, secp: &Secp256k1) -> PublicKey {
+ PublicKey {
+ compressed: self.compressed,
+ key: secp256k1::PublicKey::from_secret_key(secp, &self.key)
+ }
+ }
+
+ /// Serialize the private key to bytes
+ pub fn to_bytes(&self) -> Vec {
+ self.key[..].to_vec()
+ }
+
+ /// Format the private key to WIF format.
+ pub fn fmt_wif(&self, fmt: &mut dyn fmt::Write) -> fmt::Result {
+ let mut ret = [0; 34];
+ ret[0] = match self.network {
+ Network::Bitcoin => 128,
+ Network::Testnet | Network::Signet | Network::Regtest => 239,
+ };
+ ret[1..33].copy_from_slice(&self.key[..]);
+ let privkey = if self.compressed {
+ ret[33] = 1;
+ base58::check_encode_slice(&ret[..])
+ } else {
+ base58::check_encode_slice(&ret[..33])
+ };
+ fmt.write_str(&privkey)
+ }
+
+ /// Get WIF encoding of this private key.
+ pub fn to_wif(&self) -> String {
+ let mut buf = String::new();
+ buf.write_fmt(format_args!("{}", self)).unwrap();
+ buf.shrink_to_fit();
+ buf
+ }
+
+ /// Parse WIF encoded private key.
+ pub fn from_wif(wif: &str) -> Result {
+ let data = base58::from_check(wif)?;
+
+ let compressed = match data.len() {
+ 33 => false,
+ 34 => true,
+ _ => { return Err(Error::Base58(base58::Error::InvalidLength(data.len()))); }
+ };
+
+ let network = match data[0] {
+ 128 => Network::Bitcoin,
+ 239 => Network::Testnet,
+ x => { return Err(Error::Base58(base58::Error::InvalidVersion(vec![x]))); }
+ };
+
+ Ok(PrivateKey {
+ compressed: compressed,
+ network: network,
+ key: secp256k1::SecretKey::from_slice(&data[1..33])?,
+ })
+ }
+}
+
+impl fmt::Display for PrivateKey {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.fmt_wif(f)
+ }
+}
+
+impl fmt::Debug for PrivateKey {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "[private key data]")
+ }
+}
+
+impl FromStr for PrivateKey {
+ type Err = Error;
+ fn from_str(s: &str) -> Result {
+ PrivateKey::from_wif(s)
+ }
+}
+
+impl ops::Index for PrivateKey {
+ type Output = [u8];
+ fn index(&self, _: ops::RangeFull) -> &[u8] {
+ &self.key[..]
+ }
+}
+
+#[cfg(feature = "serde")]
+impl ::serde::Serialize for PrivateKey {
+ fn serialize(&self, s: S) -> Result {
+ s.collect_str(self)
+ }
+}
+
+#[cfg(feature = "serde")]
+impl<'de> ::serde::Deserialize<'de> for PrivateKey {
+ fn deserialize>(d: D) -> Result {
+ struct WifVisitor;
+
+ impl<'de> ::serde::de::Visitor<'de> for WifVisitor {
+ type Value = PrivateKey;
+
+ fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ formatter.write_str("an ASCII WIF string")
+ }
+
+ fn visit_bytes(self, v: &[u8]) -> Result
+ where
+ E: ::serde::de::Error,
+ {
+ if let Ok(s) = ::std::str::from_utf8(v) {
+ PrivateKey::from_str(s).map_err(E::custom)
+ } else {
+ Err(E::invalid_value(::serde::de::Unexpected::Bytes(v), &self))
+ }
+ }
+
+ fn visit_str(self, v: &str) -> Result
+ where
+ E: ::serde::de::Error,
+ {
+ PrivateKey::from_str(v).map_err(E::custom)
+ }
+ }
+
+ d.deserialize_str(WifVisitor)
+ }
+}
+
+#[cfg(feature = "serde")]
+impl ::serde::Serialize for PublicKey {
+ fn serialize(&self, s: S) -> Result {
+ if s.is_human_readable() {
+ s.collect_str(self)
+ } else {
+ if self.compressed {
+ s.serialize_bytes(&self.key.serialize()[..])
+ } else {
+ s.serialize_bytes(&self.key.serialize_uncompressed()[..])
+ }
+ }
+ }
+}
+
+#[cfg(feature = "serde")]
+impl<'de> ::serde::Deserialize<'de> for PublicKey {
+ fn deserialize>(d: D) -> Result {
+ if d.is_human_readable() {
+ struct HexVisitor;
+
+ impl<'de> ::serde::de::Visitor<'de> for HexVisitor {
+ type Value = PublicKey;
+
+ fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ formatter.write_str("an ASCII hex string")
+ }
+
+ fn visit_bytes(self, v: &[u8]) -> Result
+ where
+ E: ::serde::de::Error,
+ {
+ if let Ok(hex) = ::std::str::from_utf8(v) {
+ PublicKey::from_str(hex).map_err(E::custom)
+ } else {
+ Err(E::invalid_value(::serde::de::Unexpected::Bytes(v), &self))
+ }
+ }
+
+ fn visit_str(self, v: &str) -> Result
+ where
+ E: ::serde::de::Error,
+ {
+ PublicKey::from_str(v).map_err(E::custom)
+ }
+ }
+ d.deserialize_str(HexVisitor)
+ } else {
+ struct BytesVisitor;
+
+ impl<'de> ::serde::de::Visitor<'de> for BytesVisitor {
+ type Value = PublicKey;
+
+ fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ formatter.write_str("a bytestring")
+ }
+
+ fn visit_bytes(self, v: &[u8]) -> Result
+ where
+ E: ::serde::de::Error,
+ {
+ PublicKey::from_slice(v).map_err(E::custom)
+ }
+ }
+
+ d.deserialize_bytes(BytesVisitor)
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::{PrivateKey, PublicKey};
+ use secp256k1::Secp256k1;
+ use std::io;
+ use std::str::FromStr;
+ use hashes::hex::ToHex;
+ use network::constants::Network::Testnet;
+ use network::constants::Network::Bitcoin;
+ use util::address::Address;
+
+ #[test]
+ fn test_key_derivation() {
+ // testnet compressed
+ let sk = PrivateKey::from_wif("cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy").unwrap();
+ assert_eq!(sk.network, Testnet);
+ assert_eq!(sk.compressed, true);
+ assert_eq!(&sk.to_wif(), "cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy");
+
+ let secp = Secp256k1::new();
+ let pk = Address::p2pkh(&sk.public_key(&secp), sk.network);
+ assert_eq!(&pk.to_string(), "mqwpxxvfv3QbM8PU8uBx2jaNt9btQqvQNx");
+
+ // test string conversion
+ assert_eq!(&sk.to_string(), "cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy");
+ let sk_str =
+ PrivateKey::from_str("cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy").unwrap();
+ assert_eq!(&sk.to_wif(), &sk_str.to_wif());
+
+ // mainnet uncompressed
+ let sk = PrivateKey::from_wif("5JYkZjmN7PVMjJUfJWfRFwtuXTGB439XV6faajeHPAM9Z2PT2R3").unwrap();
+ assert_eq!(sk.network, Bitcoin);
+ assert_eq!(sk.compressed, false);
+ assert_eq!(&sk.to_wif(), "5JYkZjmN7PVMjJUfJWfRFwtuXTGB439XV6faajeHPAM9Z2PT2R3");
+
+ let secp = Secp256k1::new();
+ let mut pk = sk.public_key(&secp);
+ assert_eq!(pk.compressed, false);
+ assert_eq!(&pk.to_string(), "042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133");
+ assert_eq!(pk, PublicKey::from_str("042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133").unwrap());
+ let addr = Address::p2pkh(&pk, sk.network);
+ assert_eq!(&addr.to_string(), "1GhQvF6dL8xa6wBxLnWmHcQsurx9RxiMc8");
+ pk.compressed = true;
+ assert_eq!(&pk.to_string(), "032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af");
+ assert_eq!(pk, PublicKey::from_str("032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af").unwrap());
+ }
+
+ #[test]
+ fn test_pubkey_hash() {
+ let pk = PublicKey::from_str("032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af").unwrap();
+ let upk = PublicKey::from_str("042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133").unwrap();
+ assert_eq!(pk.pubkey_hash().to_hex(), "9511aa27ef39bbfa4e4f3dd15f4d66ea57f475b4");
+ assert_eq!(upk.pubkey_hash().to_hex(), "ac2e7daf42d2c97418fd9f78af2de552bb9c6a7a");
+ }
+
+ #[test]
+ fn test_wpubkey_hash() {
+ let pk = PublicKey::from_str("032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af").unwrap();
+ let upk = PublicKey::from_str("042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133").unwrap();
+ assert_eq!(pk.wpubkey_hash().unwrap().to_hex(), "9511aa27ef39bbfa4e4f3dd15f4d66ea57f475b4");
+ assert_eq!(upk.wpubkey_hash(), None);
+ }
+
+ #[cfg(feature = "serde")]
+ #[test]
+ fn test_key_serde() {
+ use serde_test::{Configure, Token, assert_tokens};
+
+ static KEY_WIF: &'static str = "cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy";
+ static PK_STR: &'static str = "039b6347398505f5ec93826dc61c19f47c66c0283ee9be980e29ce325a0f4679ef";
+ static PK_STR_U: &'static str = "\
+ 04\
+ 9b6347398505f5ec93826dc61c19f47c66c0283ee9be980e29ce325a0f4679ef\
+ 87288ed73ce47fc4f5c79d19ebfa57da7cff3aff6e819e4ee971d86b5e61875d\
+ ";
+ static PK_BYTES: [u8; 33] = [
+ 0x03,
+ 0x9b, 0x63, 0x47, 0x39, 0x85, 0x05, 0xf5, 0xec,
+ 0x93, 0x82, 0x6d, 0xc6, 0x1c, 0x19, 0xf4, 0x7c,
+ 0x66, 0xc0, 0x28, 0x3e, 0xe9, 0xbe, 0x98, 0x0e,
+ 0x29, 0xce, 0x32, 0x5a, 0x0f, 0x46, 0x79, 0xef,
+ ];
+ static PK_BYTES_U: [u8; 65] = [
+ 0x04,
+ 0x9b, 0x63, 0x47, 0x39, 0x85, 0x05, 0xf5, 0xec,
+ 0x93, 0x82, 0x6d, 0xc6, 0x1c, 0x19, 0xf4, 0x7c,
+ 0x66, 0xc0, 0x28, 0x3e, 0xe9, 0xbe, 0x98, 0x0e,
+ 0x29, 0xce, 0x32, 0x5a, 0x0f, 0x46, 0x79, 0xef,
+ 0x87, 0x28, 0x8e, 0xd7, 0x3c, 0xe4, 0x7f, 0xc4,
+ 0xf5, 0xc7, 0x9d, 0x19, 0xeb, 0xfa, 0x57, 0xda,
+ 0x7c, 0xff, 0x3a, 0xff, 0x6e, 0x81, 0x9e, 0x4e,
+ 0xe9, 0x71, 0xd8, 0x6b, 0x5e, 0x61, 0x87, 0x5d,
+ ];
+
+ let s = Secp256k1::new();
+ let sk = PrivateKey::from_str(&KEY_WIF).unwrap();
+ let pk = PublicKey::from_private_key(&s, &sk);
+ let pk_u = PublicKey {
+ key: pk.key,
+ compressed: false,
+ };
+
+ assert_tokens(&sk, &[Token::BorrowedStr(KEY_WIF)]);
+ assert_tokens(&pk.compact(), &[Token::BorrowedBytes(&PK_BYTES[..])]);
+ assert_tokens(&pk.readable(), &[Token::BorrowedStr(PK_STR)]);
+ assert_tokens(&pk_u.compact(), &[Token::BorrowedBytes(&PK_BYTES_U[..])]);
+ assert_tokens(&pk_u.readable(), &[Token::BorrowedStr(PK_STR_U)]);
+ }
+
+ fn random_key(mut seed: u8) -> PublicKey {
+ loop {
+ let mut data = [0; 65];
+ for byte in &mut data[..] {
+ *byte = seed;
+ // totally a rng
+ seed = seed.wrapping_mul(41).wrapping_add(43);
+ }
+ if data[0] % 2 == 0 {
+ data[0] = 4;
+ if let Ok(key) = PublicKey::from_slice(&data[..]) {
+ return key;
+ }
+ } else {
+ data[0] = 2 + (data[0] >> 7);
+ if let Ok(key) = PublicKey::from_slice(&data[..33]) {
+ return key;
+ }
+ }
+ }
+ }
+
+ #[test]
+ fn pubkey_read_write() {
+ const N_KEYS: usize = 20;
+ let keys: Vec<_> = (0..N_KEYS).map(|i| random_key(i as u8)).collect();
+
+ let mut v = vec![];
+ for k in &keys {
+ k.write_into(&mut v).expect("writing into vec");
+ }
+
+ let mut dec_keys = vec![];
+ let mut cursor = io::Cursor::new(&v);
+ for _ in 0..N_KEYS {
+ dec_keys.push(PublicKey::read_from(&mut cursor).expect("reading from vec"));
+ }
+
+ assert_eq!(keys, dec_keys);
+
+ // sanity checks
+ assert!(PublicKey::read_from(&mut cursor).is_err());
+ assert!(PublicKey::read_from(io::Cursor::new(&[])).is_err());
+ assert!(PublicKey::read_from(io::Cursor::new(&[0; 33][..])).is_err());
+ assert!(PublicKey::read_from(io::Cursor::new(&[2; 32][..])).is_err());
+ assert!(PublicKey::read_from(io::Cursor::new(&[0; 65][..])).is_err());
+ assert!(PublicKey::read_from(io::Cursor::new(&[4; 64][..])).is_err());
+ }
+}
diff --git a/src/util/key.rs b/src/util/key.rs
index b20ff93b..d71f7065 100644
--- a/src/util/key.rs
+++ b/src/util/key.rs
@@ -16,14 +16,12 @@
//! Keys used in Bitcoin that can be roundtrip (de)serialized.
//!
-use std::fmt::{self, Write};
-use std::{io, ops, error};
-use std::str::FromStr;
+pub use util::ecdsa::{PrivateKey, PublicKey};
-use secp256k1::{self, Secp256k1};
-use network::constants::Network;
-use hashes::{Hash, hash160};
-use hash_types::{PubkeyHash, WPubkeyHash};
+use std::fmt;
+use std::error;
+
+use secp256k1;
use util::base58;
/// A key-related error.
@@ -67,486 +65,3 @@ impl From for Error {
Error::Secp256k1(e)
}
}
-
-/// A Bitcoin ECDSA public key
-#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct PublicKey {
- /// Whether this public key should be serialized as compressed
- pub compressed: bool,
- /// The actual ECDSA key
- pub key: secp256k1::PublicKey,
-}
-
-impl PublicKey {
- /// Returns bitcoin 160-bit hash of the public key
- pub fn pubkey_hash(&self) -> PubkeyHash {
- if self.compressed {
- PubkeyHash::hash(&self.key.serialize())
- } else {
- PubkeyHash::hash(&self.key.serialize_uncompressed())
- }
- }
-
- /// Returns bitcoin 160-bit hash of the public key for witness program
- pub fn wpubkey_hash(&self) -> Option {
- if self.compressed {
- Some(WPubkeyHash::from_inner(
- hash160::Hash::hash(&self.key.serialize()).into_inner()
- ))
- } else {
- // We can't create witness pubkey hashes for an uncompressed
- // public keys
- None
- }
- }
-
- /// Write the public key into a writer
- pub fn write_into(&self, mut writer: W) -> Result<(), io::Error> {
- if self.compressed {
- writer.write_all(&self.key.serialize())
- } else {
- writer.write_all(&self.key.serialize_uncompressed())
- }
- }
-
- /// Read the public key from a reader
- ///
- /// This internally reads the first byte before reading the rest, so
- /// use of a `BufReader` is recommended.
- pub fn read_from(mut reader: R) -> Result {
- let mut bytes = [0; 65];
-
- reader.read_exact(&mut bytes[0..1])?;
- let bytes = if bytes[0] < 4 {
- &mut bytes[..33]
- } else {
- &mut bytes[..65]
- };
-
- reader.read_exact(&mut bytes[1..])?;
- Self::from_slice(bytes).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
- }
-
- /// Serialize the public key to bytes
- pub fn to_bytes(&self) -> Vec {
- let mut buf = Vec::new();
- self.write_into(&mut buf).expect("vecs don't error");
- buf
- }
-
- /// Deserialize a public key from a slice
- pub fn from_slice(data: &[u8]) -> Result {
- let compressed: bool = match data.len() {
- 33 => true,
- 65 => false,
- len => { return Err(base58::Error::InvalidLength(len).into()); },
- };
-
- Ok(PublicKey {
- compressed: compressed,
- key: secp256k1::PublicKey::from_slice(data)?,
- })
- }
-
- /// Computes the public key as supposed to be used with this secret
- pub fn from_private_key(secp: &Secp256k1, sk: &PrivateKey) -> PublicKey {
- sk.public_key(secp)
- }
-}
-
-impl fmt::Display for PublicKey {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- if self.compressed {
- for ch in &self.key.serialize()[..] {
- write!(f, "{:02x}", ch)?;
- }
- } else {
- for ch in &self.key.serialize_uncompressed()[..] {
- write!(f, "{:02x}", ch)?;
- }
- }
- Ok(())
- }
-}
-
-impl FromStr for PublicKey {
- type Err = Error;
- fn from_str(s: &str) -> Result {
- let key = secp256k1::PublicKey::from_str(s)?;
- Ok(PublicKey {
- key: key,
- compressed: s.len() == 66
- })
- }
-}
-
-#[derive(Copy, Clone, PartialEq, Eq)]
-/// A Bitcoin ECDSA private key
-pub struct PrivateKey {
- /// Whether this private key should be serialized as compressed
- pub compressed: bool,
- /// The network on which this key should be used
- pub network: Network,
- /// The actual ECDSA key
- pub key: secp256k1::SecretKey,
-}
-
-impl PrivateKey {
- /// Creates a public key from this private key
- pub fn public_key(&self, secp: &Secp256k1) -> PublicKey {
- PublicKey {
- compressed: self.compressed,
- key: secp256k1::PublicKey::from_secret_key(secp, &self.key)
- }
- }
-
- /// Serialize the private key to bytes
- pub fn to_bytes(&self) -> Vec {
- self.key[..].to_vec()
- }
-
- /// Format the private key to WIF format.
- pub fn fmt_wif(&self, fmt: &mut dyn fmt::Write) -> fmt::Result {
- let mut ret = [0; 34];
- ret[0] = match self.network {
- Network::Bitcoin => 128,
- Network::Testnet | Network::Signet | Network::Regtest => 239,
- };
- ret[1..33].copy_from_slice(&self.key[..]);
- let privkey = if self.compressed {
- ret[33] = 1;
- base58::check_encode_slice(&ret[..])
- } else {
- base58::check_encode_slice(&ret[..33])
- };
- fmt.write_str(&privkey)
- }
-
- /// Get WIF encoding of this private key.
- pub fn to_wif(&self) -> String {
- let mut buf = String::new();
- buf.write_fmt(format_args!("{}", self)).unwrap();
- buf.shrink_to_fit();
- buf
- }
-
- /// Parse WIF encoded private key.
- pub fn from_wif(wif: &str) -> Result {
- let data = base58::from_check(wif)?;
-
- let compressed = match data.len() {
- 33 => false,
- 34 => true,
- _ => { return Err(Error::Base58(base58::Error::InvalidLength(data.len()))); }
- };
-
- let network = match data[0] {
- 128 => Network::Bitcoin,
- 239 => Network::Testnet,
- x => { return Err(Error::Base58(base58::Error::InvalidVersion(vec![x]))); }
- };
-
- Ok(PrivateKey {
- compressed: compressed,
- network: network,
- key: secp256k1::SecretKey::from_slice(&data[1..33])?,
- })
- }
-}
-
-impl fmt::Display for PrivateKey {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- self.fmt_wif(f)
- }
-}
-
-impl fmt::Debug for PrivateKey {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "[private key data]")
- }
-}
-
-impl FromStr for PrivateKey {
- type Err = Error;
- fn from_str(s: &str) -> Result {
- PrivateKey::from_wif(s)
- }
-}
-
-impl ops::Index for PrivateKey {
- type Output = [u8];
- fn index(&self, _: ops::RangeFull) -> &[u8] {
- &self.key[..]
- }
-}
-
-#[cfg(feature = "serde")]
-impl ::serde::Serialize for PrivateKey {
- fn serialize(&self, s: S) -> Result {
- s.collect_str(self)
- }
-}
-
-#[cfg(feature = "serde")]
-impl<'de> ::serde::Deserialize<'de> for PrivateKey {
- fn deserialize>(d: D) -> Result {
- struct WifVisitor;
-
- impl<'de> ::serde::de::Visitor<'de> for WifVisitor {
- type Value = PrivateKey;
-
- fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
- formatter.write_str("an ASCII WIF string")
- }
-
- fn visit_bytes(self, v: &[u8]) -> Result
- where
- E: ::serde::de::Error,
- {
- if let Ok(s) = ::std::str::from_utf8(v) {
- PrivateKey::from_str(s).map_err(E::custom)
- } else {
- Err(E::invalid_value(::serde::de::Unexpected::Bytes(v), &self))
- }
- }
-
- fn visit_str(self, v: &str) -> Result
- where
- E: ::serde::de::Error,
- {
- PrivateKey::from_str(v).map_err(E::custom)
- }
- }
-
- d.deserialize_str(WifVisitor)
- }
-}
-
-#[cfg(feature = "serde")]
-impl ::serde::Serialize for PublicKey {
- fn serialize(&self, s: S) -> Result {
- if s.is_human_readable() {
- s.collect_str(self)
- } else {
- if self.compressed {
- s.serialize_bytes(&self.key.serialize()[..])
- } else {
- s.serialize_bytes(&self.key.serialize_uncompressed()[..])
- }
- }
- }
-}
-
-#[cfg(feature = "serde")]
-impl<'de> ::serde::Deserialize<'de> for PublicKey {
- fn deserialize>(d: D) -> Result {
- if d.is_human_readable() {
- struct HexVisitor;
-
- impl<'de> ::serde::de::Visitor<'de> for HexVisitor {
- type Value = PublicKey;
-
- fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
- formatter.write_str("an ASCII hex string")
- }
-
- fn visit_bytes(self, v: &[u8]) -> Result
- where
- E: ::serde::de::Error,
- {
- if let Ok(hex) = ::std::str::from_utf8(v) {
- PublicKey::from_str(hex).map_err(E::custom)
- } else {
- Err(E::invalid_value(::serde::de::Unexpected::Bytes(v), &self))
- }
- }
-
- fn visit_str(self, v: &str) -> Result
- where
- E: ::serde::de::Error,
- {
- PublicKey::from_str(v).map_err(E::custom)
- }
- }
- d.deserialize_str(HexVisitor)
- } else {
- struct BytesVisitor;
-
- impl<'de> ::serde::de::Visitor<'de> for BytesVisitor {
- type Value = PublicKey;
-
- fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
- formatter.write_str("a bytestring")
- }
-
- fn visit_bytes(self, v: &[u8]) -> Result
- where
- E: ::serde::de::Error,
- {
- PublicKey::from_slice(v).map_err(E::custom)
- }
- }
-
- d.deserialize_bytes(BytesVisitor)
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::{PrivateKey, PublicKey};
- use secp256k1::Secp256k1;
- use std::io;
- use std::str::FromStr;
- use hashes::hex::ToHex;
- use network::constants::Network::Testnet;
- use network::constants::Network::Bitcoin;
- use util::address::Address;
-
- #[test]
- fn test_key_derivation() {
- // testnet compressed
- let sk = PrivateKey::from_wif("cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy").unwrap();
- assert_eq!(sk.network, Testnet);
- assert_eq!(sk.compressed, true);
- assert_eq!(&sk.to_wif(), "cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy");
-
- let secp = Secp256k1::new();
- let pk = Address::p2pkh(&sk.public_key(&secp), sk.network);
- assert_eq!(&pk.to_string(), "mqwpxxvfv3QbM8PU8uBx2jaNt9btQqvQNx");
-
- // test string conversion
- assert_eq!(&sk.to_string(), "cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy");
- let sk_str =
- PrivateKey::from_str("cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy").unwrap();
- assert_eq!(&sk.to_wif(), &sk_str.to_wif());
-
- // mainnet uncompressed
- let sk = PrivateKey::from_wif("5JYkZjmN7PVMjJUfJWfRFwtuXTGB439XV6faajeHPAM9Z2PT2R3").unwrap();
- assert_eq!(sk.network, Bitcoin);
- assert_eq!(sk.compressed, false);
- assert_eq!(&sk.to_wif(), "5JYkZjmN7PVMjJUfJWfRFwtuXTGB439XV6faajeHPAM9Z2PT2R3");
-
- let secp = Secp256k1::new();
- let mut pk = sk.public_key(&secp);
- assert_eq!(pk.compressed, false);
- assert_eq!(&pk.to_string(), "042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133");
- assert_eq!(pk, PublicKey::from_str("042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133").unwrap());
- let addr = Address::p2pkh(&pk, sk.network);
- assert_eq!(&addr.to_string(), "1GhQvF6dL8xa6wBxLnWmHcQsurx9RxiMc8");
- pk.compressed = true;
- assert_eq!(&pk.to_string(), "032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af");
- assert_eq!(pk, PublicKey::from_str("032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af").unwrap());
- }
-
- #[test]
- fn test_pubkey_hash() {
- let pk = PublicKey::from_str("032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af").unwrap();
- let upk = PublicKey::from_str("042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133").unwrap();
- assert_eq!(pk.pubkey_hash().to_hex(), "9511aa27ef39bbfa4e4f3dd15f4d66ea57f475b4");
- assert_eq!(upk.pubkey_hash().to_hex(), "ac2e7daf42d2c97418fd9f78af2de552bb9c6a7a");
- }
-
- #[test]
- fn test_wpubkey_hash() {
- let pk = PublicKey::from_str("032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af").unwrap();
- let upk = PublicKey::from_str("042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133").unwrap();
- assert_eq!(pk.wpubkey_hash().unwrap().to_hex(), "9511aa27ef39bbfa4e4f3dd15f4d66ea57f475b4");
- assert_eq!(upk.wpubkey_hash(), None);
- }
-
- #[cfg(feature = "serde")]
- #[test]
- fn test_key_serde() {
- use serde_test::{Configure, Token, assert_tokens};
-
- static KEY_WIF: &'static str = "cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy";
- static PK_STR: &'static str = "039b6347398505f5ec93826dc61c19f47c66c0283ee9be980e29ce325a0f4679ef";
- static PK_STR_U: &'static str = "\
- 04\
- 9b6347398505f5ec93826dc61c19f47c66c0283ee9be980e29ce325a0f4679ef\
- 87288ed73ce47fc4f5c79d19ebfa57da7cff3aff6e819e4ee971d86b5e61875d\
- ";
- static PK_BYTES: [u8; 33] = [
- 0x03,
- 0x9b, 0x63, 0x47, 0x39, 0x85, 0x05, 0xf5, 0xec,
- 0x93, 0x82, 0x6d, 0xc6, 0x1c, 0x19, 0xf4, 0x7c,
- 0x66, 0xc0, 0x28, 0x3e, 0xe9, 0xbe, 0x98, 0x0e,
- 0x29, 0xce, 0x32, 0x5a, 0x0f, 0x46, 0x79, 0xef,
- ];
- static PK_BYTES_U: [u8; 65] = [
- 0x04,
- 0x9b, 0x63, 0x47, 0x39, 0x85, 0x05, 0xf5, 0xec,
- 0x93, 0x82, 0x6d, 0xc6, 0x1c, 0x19, 0xf4, 0x7c,
- 0x66, 0xc0, 0x28, 0x3e, 0xe9, 0xbe, 0x98, 0x0e,
- 0x29, 0xce, 0x32, 0x5a, 0x0f, 0x46, 0x79, 0xef,
- 0x87, 0x28, 0x8e, 0xd7, 0x3c, 0xe4, 0x7f, 0xc4,
- 0xf5, 0xc7, 0x9d, 0x19, 0xeb, 0xfa, 0x57, 0xda,
- 0x7c, 0xff, 0x3a, 0xff, 0x6e, 0x81, 0x9e, 0x4e,
- 0xe9, 0x71, 0xd8, 0x6b, 0x5e, 0x61, 0x87, 0x5d,
- ];
-
- let s = Secp256k1::new();
- let sk = PrivateKey::from_str(&KEY_WIF).unwrap();
- let pk = PublicKey::from_private_key(&s, &sk);
- let pk_u = PublicKey {
- key: pk.key,
- compressed: false,
- };
-
- assert_tokens(&sk, &[Token::BorrowedStr(KEY_WIF)]);
- assert_tokens(&pk.compact(), &[Token::BorrowedBytes(&PK_BYTES[..])]);
- assert_tokens(&pk.readable(), &[Token::BorrowedStr(PK_STR)]);
- assert_tokens(&pk_u.compact(), &[Token::BorrowedBytes(&PK_BYTES_U[..])]);
- assert_tokens(&pk_u.readable(), &[Token::BorrowedStr(PK_STR_U)]);
- }
-
- fn random_key(mut seed: u8) -> PublicKey {
- loop {
- let mut data = [0; 65];
- for byte in &mut data[..] {
- *byte = seed;
- // totally a rng
- seed = seed.wrapping_mul(41).wrapping_add(43);
- }
- if data[0] % 2 == 0 {
- data[0] = 4;
- if let Ok(key) = PublicKey::from_slice(&data[..]) {
- return key;
- }
- } else {
- data[0] = 2 + (data[0] >> 7);
- if let Ok(key) = PublicKey::from_slice(&data[..33]) {
- return key;
- }
- }
- }
- }
-
- #[test]
- fn pubkey_read_write() {
- const N_KEYS: usize = 20;
- let keys: Vec<_> = (0..N_KEYS).map(|i| random_key(i as u8)).collect();
-
- let mut v = vec![];
- for k in &keys {
- k.write_into(&mut v).expect("writing into vec");
- }
-
- let mut dec_keys = vec![];
- let mut cursor = io::Cursor::new(&v);
- for _ in 0..N_KEYS {
- dec_keys.push(PublicKey::read_from(&mut cursor).expect("reading from vec"));
- }
-
- assert_eq!(keys, dec_keys);
-
- // sanity checks
- assert!(PublicKey::read_from(&mut cursor).is_err());
- assert!(PublicKey::read_from(io::Cursor::new(&[])).is_err());
- assert!(PublicKey::read_from(io::Cursor::new(&[0; 33][..])).is_err());
- assert!(PublicKey::read_from(io::Cursor::new(&[2; 32][..])).is_err());
- assert!(PublicKey::read_from(io::Cursor::new(&[0; 65][..])).is_err());
- assert!(PublicKey::read_from(io::Cursor::new(&[4; 64][..])).is_err());
- }
-}
diff --git a/src/util/misc.rs b/src/util/misc.rs
index f99761a8..571889ad 100644
--- a/src/util/misc.rs
+++ b/src/util/misc.rs
@@ -35,7 +35,7 @@ mod message_signing {
use secp256k1;
use secp256k1::recovery::{RecoveryId, RecoverableSignature};
- use util::key::PublicKey;
+ use util::ecdsa::PublicKey;
use util::address::{Address, AddressType};
/// An error used for dealing with Bitcoin Signed Messages.
diff --git a/src/util/mod.rs b/src/util/mod.rs
index e102c586..7f92156c 100644
--- a/src/util/mod.rs
+++ b/src/util/mod.rs
@@ -16,7 +16,7 @@
//!
//! Functions needed by all parts of the Bitcoin library
-pub mod key;
+pub mod ecdsa;
pub mod address;
pub mod amount;
pub mod base58;
@@ -30,6 +30,7 @@ pub mod psbt;
pub mod taproot;
pub mod uint;
pub mod bip158;
+pub mod key;
pub(crate) mod endian;
diff --git a/src/util/psbt/map/input.rs b/src/util/psbt/map/input.rs
index ab114e0c..ddcc1f8a 100644
--- a/src/util/psbt/map/input.rs
+++ b/src/util/psbt/map/input.rs
@@ -20,7 +20,7 @@ use blockdata::transaction::{SigHashType, Transaction, TxOut};
use consensus::encode;
use util::bip32::KeySource;
use hashes::{self, hash160, ripemd160, sha256, sha256d};
-use util::key::PublicKey;
+use util::ecdsa::PublicKey;
use util::psbt;
use util::psbt::map::Map;
use util::psbt::raw;
diff --git a/src/util/psbt/map/output.rs b/src/util/psbt/map/output.rs
index 1c40ec1b..6eaa9d78 100644
--- a/src/util/psbt/map/output.rs
+++ b/src/util/psbt/map/output.rs
@@ -19,7 +19,7 @@ use std::collections::btree_map::Entry;
use blockdata::script::Script;
use consensus::encode;
use util::bip32::KeySource;
-use util::key::PublicKey;
+use util::ecdsa::PublicKey;
use util::psbt;
use util::psbt::map::Map;
use util::psbt::raw;
diff --git a/src/util/psbt/mod.rs b/src/util/psbt/mod.rs
index 3cd2ef89..9e7d9110 100644
--- a/src/util/psbt/mod.rs
+++ b/src/util/psbt/mod.rs
@@ -222,7 +222,7 @@ mod tests {
use network::constants::Network::Bitcoin;
use consensus::encode::{deserialize, serialize, serialize_hex};
use util::bip32::{ChildNumber, ExtendedPrivKey, ExtendedPubKey, Fingerprint, KeySource};
- use util::key::PublicKey;
+ use util::ecdsa::PublicKey;
use util::psbt::map::{Global, Output, Input};
use util::psbt::raw;
diff --git a/src/util/psbt/serialize.rs b/src/util/psbt/serialize.rs
index 5e530a45..d20a3b26 100644
--- a/src/util/psbt/serialize.rs
+++ b/src/util/psbt/serialize.rs
@@ -24,7 +24,7 @@ use blockdata::transaction::{SigHashType, Transaction, TxOut};
use consensus::encode::{self, serialize, Decodable};
use util::bip32::{ChildNumber, Fingerprint, KeySource};
use hashes::{hash160, ripemd160, sha256, sha256d, Hash};
-use util::key::PublicKey;
+use util::ecdsa::PublicKey;
use util::psbt;
/// A trait for serializing a value as raw data for insertion into PSBT