Moving keys under `util::ecdsa`, re-exporting them at `util::key`
This is the first step in introducing Schnorr key support as per #588
This commit is contained in:
parent
dc0e2b0a52
commit
b17d7fc31c
|
@ -4,7 +4,7 @@ use std::{env, process};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use bitcoin::secp256k1::Secp256k1;
|
use bitcoin::secp256k1::Secp256k1;
|
||||||
use bitcoin::util::key::PrivateKey;
|
use bitcoin::util::ecdsa::PrivateKey;
|
||||||
use bitcoin::util::bip32::ExtendedPrivKey;
|
use bitcoin::util::bip32::ExtendedPrivKey;
|
||||||
use bitcoin::util::bip32::ExtendedPubKey;
|
use bitcoin::util::bip32::ExtendedPubKey;
|
||||||
use bitcoin::util::bip32::DerivationPath;
|
use bitcoin::util::bip32::DerivationPath;
|
||||||
|
|
|
@ -37,7 +37,7 @@ use hashes::{Hash, hex};
|
||||||
#[cfg(feature="bitcoinconsensus")] use std::convert;
|
#[cfg(feature="bitcoinconsensus")] use std::convert;
|
||||||
#[cfg(feature="bitcoinconsensus")] use OutPoint;
|
#[cfg(feature="bitcoinconsensus")] use OutPoint;
|
||||||
|
|
||||||
use util::key::PublicKey;
|
use util::ecdsa::PublicKey;
|
||||||
|
|
||||||
#[derive(Clone, Default, PartialOrd, Ord, PartialEq, Eq, Hash)]
|
#[derive(Clone, Default, PartialOrd, Ord, PartialEq, Eq, Hash)]
|
||||||
/// A Bitcoin script
|
/// A Bitcoin script
|
||||||
|
@ -889,7 +889,7 @@ mod test {
|
||||||
use hashes::hex::{FromHex, ToHex};
|
use hashes::hex::{FromHex, ToHex};
|
||||||
use consensus::encode::{deserialize, serialize};
|
use consensus::encode::{deserialize, serialize};
|
||||||
use blockdata::opcodes;
|
use blockdata::opcodes;
|
||||||
use util::key::PublicKey;
|
use util::ecdsa::PublicKey;
|
||||||
use util::psbt::serialize::Serialize;
|
use util::psbt::serialize::Serialize;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -85,8 +85,8 @@ pub use util::address::AddressType;
|
||||||
pub use util::amount::Amount;
|
pub use util::amount::Amount;
|
||||||
pub use util::amount::Denomination;
|
pub use util::amount::Denomination;
|
||||||
pub use util::amount::SignedAmount;
|
pub use util::amount::SignedAmount;
|
||||||
pub use util::key::PrivateKey;
|
pub use util::ecdsa::PrivateKey;
|
||||||
pub use util::key::PublicKey;
|
pub use util::ecdsa::PublicKey;
|
||||||
pub use util::merkleblock::MerkleBlock;
|
pub use util::merkleblock::MerkleBlock;
|
||||||
|
|
||||||
#[cfg(all(test, feature = "unstable"))] use tests::EmptyWrite;
|
#[cfg(all(test, feature = "unstable"))] use tests::EmptyWrite;
|
||||||
|
|
|
@ -21,13 +21,13 @@
|
||||||
//!
|
//!
|
||||||
//! use bitcoin::network::constants::Network;
|
//! use bitcoin::network::constants::Network;
|
||||||
//! use bitcoin::util::address::Address;
|
//! use bitcoin::util::address::Address;
|
||||||
//! use bitcoin::util::key;
|
//! use bitcoin::util::ecdsa;
|
||||||
//! use bitcoin::secp256k1::Secp256k1;
|
//! use bitcoin::secp256k1::Secp256k1;
|
||||||
//! use bitcoin::secp256k1::rand::thread_rng;
|
//! use bitcoin::secp256k1::rand::thread_rng;
|
||||||
//!
|
//!
|
||||||
//! // Generate random key pair
|
//! // Generate random key pair
|
||||||
//! let s = Secp256k1::new();
|
//! let s = Secp256k1::new();
|
||||||
//! let public_key = key::PublicKey {
|
//! let public_key = ecdsa::PublicKey {
|
||||||
//! compressed: true,
|
//! compressed: true,
|
||||||
//! key: s.generate_keypair(&mut thread_rng()).1,
|
//! key: s.generate_keypair(&mut thread_rng()).1,
|
||||||
//! };
|
//! };
|
||||||
|
@ -46,7 +46,7 @@ use hash_types::{PubkeyHash, WPubkeyHash, ScriptHash, WScriptHash};
|
||||||
use blockdata::script;
|
use blockdata::script;
|
||||||
use network::constants::Network;
|
use network::constants::Network;
|
||||||
use util::base58;
|
use util::base58;
|
||||||
use util::key;
|
use util::ecdsa;
|
||||||
|
|
||||||
/// Address error.
|
/// Address error.
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
|
@ -220,7 +220,7 @@ impl Address {
|
||||||
/// Creates a pay to (compressed) public key hash address from a public key
|
/// Creates a pay to (compressed) public key hash address from a public key
|
||||||
/// This is the preferred non-witness type address
|
/// This is the preferred non-witness type address
|
||||||
#[inline]
|
#[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();
|
let mut hash_engine = PubkeyHash::engine();
|
||||||
pk.write_into(&mut hash_engine).expect("engines don't error");
|
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
|
/// 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.
|
/// Will only return an Error when an uncompressed public key is provided.
|
||||||
pub fn p2wpkh(pk: &key::PublicKey, network: Network) -> Result<Address, Error> {
|
pub fn p2wpkh(pk: &ecdsa::PublicKey, network: Network) -> Result<Address, Error> {
|
||||||
if !pk.compressed {
|
if !pk.compressed {
|
||||||
return Err(Error::UncompressedPubkey);
|
return Err(Error::UncompressedPubkey);
|
||||||
}
|
}
|
||||||
|
@ -265,7 +265,7 @@ impl Address {
|
||||||
/// 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
|
||||||
///
|
///
|
||||||
/// Will only return an Error when an uncompressed public key is provided.
|
/// Will only return an Error when an uncompressed public key is provided.
|
||||||
pub fn p2shwpkh(pk: &key::PublicKey, network: Network) -> Result<Address, Error> {
|
pub fn p2shwpkh(pk: &ecdsa::PublicKey, network: Network) -> Result<Address, Error> {
|
||||||
if !pk.compressed {
|
if !pk.compressed {
|
||||||
return Err(Error::UncompressedPubkey);
|
return Err(Error::UncompressedPubkey);
|
||||||
}
|
}
|
||||||
|
@ -500,7 +500,7 @@ mod tests {
|
||||||
|
|
||||||
use blockdata::script::Script;
|
use blockdata::script::Script;
|
||||||
use network::constants::Network::{Bitcoin, Testnet};
|
use network::constants::Network::{Bitcoin, Testnet};
|
||||||
use util::key::PublicKey;
|
use util::ecdsa::PublicKey;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
|
@ -274,7 +274,7 @@ mod tests {
|
||||||
use consensus::encode::deserialize;
|
use consensus::encode::deserialize;
|
||||||
use network::constants::Network;
|
use network::constants::Network;
|
||||||
use util::address::Address;
|
use util::address::Address;
|
||||||
use util::key::PublicKey;
|
use util::ecdsa::PublicKey;
|
||||||
use hashes::hex::FromHex;
|
use hashes::hex::FromHex;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -26,8 +26,8 @@ use hashes::{sha512, Hash, HashEngine, Hmac, HmacEngine};
|
||||||
use secp256k1::{self, Secp256k1};
|
use secp256k1::{self, Secp256k1};
|
||||||
|
|
||||||
use network::constants::Network;
|
use network::constants::Network;
|
||||||
use util::{base58, endian};
|
use util::{base58, endian, key};
|
||||||
use util::key::{self, PublicKey, PrivateKey};
|
use util::ecdsa::{PublicKey, PrivateKey};
|
||||||
|
|
||||||
/// A chain code
|
/// A chain code
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
|
|
@ -0,0 +1,511 @@
|
||||||
|
// Rust Bitcoin Library
|
||||||
|
// Written in 2014 by
|
||||||
|
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||||
|
// 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 <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
//! 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<WPubkeyHash> {
|
||||||
|
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<W: io::Write>(&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<R: io::Read>(mut reader: R) -> Result<Self, io::Error> {
|
||||||
|
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<u8> {
|
||||||
|
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<PublicKey, Error> {
|
||||||
|
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<C: secp256k1::Signing>(secp: &Secp256k1<C>, 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<PublicKey, Error> {
|
||||||
|
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<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>) -> 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<u8> {
|
||||||
|
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<PrivateKey, Error> {
|
||||||
|
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, Error> {
|
||||||
|
PrivateKey::from_wif(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Index<ops::RangeFull> for PrivateKey {
|
||||||
|
type Output = [u8];
|
||||||
|
fn index(&self, _: ops::RangeFull) -> &[u8] {
|
||||||
|
&self.key[..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl ::serde::Serialize for PrivateKey {
|
||||||
|
fn serialize<S: ::serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
|
||||||
|
s.collect_str(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<'de> ::serde::Deserialize<'de> for PrivateKey {
|
||||||
|
fn deserialize<D: ::serde::Deserializer<'de>>(d: D) -> Result<PrivateKey, D::Error> {
|
||||||
|
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<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
||||||
|
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<E>(self, v: &str) -> Result<Self::Value, E>
|
||||||
|
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<S: ::serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
|
||||||
|
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: ::serde::Deserializer<'de>>(d: D) -> Result<PublicKey, D::Error> {
|
||||||
|
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<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
||||||
|
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<E>(self, v: &str) -> Result<Self::Value, E>
|
||||||
|
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<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
495
src/util/key.rs
495
src/util/key.rs
|
@ -16,14 +16,12 @@
|
||||||
//! Keys used in Bitcoin that can be roundtrip (de)serialized.
|
//! Keys used in Bitcoin that can be roundtrip (de)serialized.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use std::fmt::{self, Write};
|
pub use util::ecdsa::{PrivateKey, PublicKey};
|
||||||
use std::{io, ops, error};
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use secp256k1::{self, Secp256k1};
|
use std::fmt;
|
||||||
use network::constants::Network;
|
use std::error;
|
||||||
use hashes::{Hash, hash160};
|
|
||||||
use hash_types::{PubkeyHash, WPubkeyHash};
|
use secp256k1;
|
||||||
use util::base58;
|
use util::base58;
|
||||||
|
|
||||||
/// A key-related error.
|
/// A key-related error.
|
||||||
|
@ -67,486 +65,3 @@ impl From<secp256k1::Error> for Error {
|
||||||
Error::Secp256k1(e)
|
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<WPubkeyHash> {
|
|
||||||
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<W: io::Write>(&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<R: io::Read>(mut reader: R) -> Result<Self, io::Error> {
|
|
||||||
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<u8> {
|
|
||||||
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<PublicKey, Error> {
|
|
||||||
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<C: secp256k1::Signing>(secp: &Secp256k1<C>, 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<PublicKey, Error> {
|
|
||||||
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<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>) -> 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<u8> {
|
|
||||||
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<PrivateKey, Error> {
|
|
||||||
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, Error> {
|
|
||||||
PrivateKey::from_wif(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ops::Index<ops::RangeFull> for PrivateKey {
|
|
||||||
type Output = [u8];
|
|
||||||
fn index(&self, _: ops::RangeFull) -> &[u8] {
|
|
||||||
&self.key[..]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl ::serde::Serialize for PrivateKey {
|
|
||||||
fn serialize<S: ::serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
|
|
||||||
s.collect_str(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
|
||||||
impl<'de> ::serde::Deserialize<'de> for PrivateKey {
|
|
||||||
fn deserialize<D: ::serde::Deserializer<'de>>(d: D) -> Result<PrivateKey, D::Error> {
|
|
||||||
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<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
|
||||||
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<E>(self, v: &str) -> Result<Self::Value, E>
|
|
||||||
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<S: ::serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
|
|
||||||
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: ::serde::Deserializer<'de>>(d: D) -> Result<PublicKey, D::Error> {
|
|
||||||
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<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
|
||||||
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<E>(self, v: &str) -> Result<Self::Value, E>
|
|
||||||
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<E>(self, v: &[u8]) -> Result<Self::Value, E>
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ mod message_signing {
|
||||||
use secp256k1;
|
use secp256k1;
|
||||||
use secp256k1::recovery::{RecoveryId, RecoverableSignature};
|
use secp256k1::recovery::{RecoveryId, RecoverableSignature};
|
||||||
|
|
||||||
use util::key::PublicKey;
|
use util::ecdsa::PublicKey;
|
||||||
use util::address::{Address, AddressType};
|
use util::address::{Address, AddressType};
|
||||||
|
|
||||||
/// An error used for dealing with Bitcoin Signed Messages.
|
/// An error used for dealing with Bitcoin Signed Messages.
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
//!
|
//!
|
||||||
//! Functions needed by all parts of the Bitcoin library
|
//! Functions needed by all parts of the Bitcoin library
|
||||||
|
|
||||||
pub mod key;
|
pub mod ecdsa;
|
||||||
pub mod address;
|
pub mod address;
|
||||||
pub mod amount;
|
pub mod amount;
|
||||||
pub mod base58;
|
pub mod base58;
|
||||||
|
@ -30,6 +30,7 @@ pub mod psbt;
|
||||||
pub mod taproot;
|
pub mod taproot;
|
||||||
pub mod uint;
|
pub mod uint;
|
||||||
pub mod bip158;
|
pub mod bip158;
|
||||||
|
pub mod key;
|
||||||
|
|
||||||
pub(crate) mod endian;
|
pub(crate) mod endian;
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ use blockdata::transaction::{SigHashType, Transaction, TxOut};
|
||||||
use consensus::encode;
|
use consensus::encode;
|
||||||
use util::bip32::KeySource;
|
use util::bip32::KeySource;
|
||||||
use hashes::{self, hash160, ripemd160, sha256, sha256d};
|
use hashes::{self, hash160, ripemd160, sha256, sha256d};
|
||||||
use util::key::PublicKey;
|
use util::ecdsa::PublicKey;
|
||||||
use util::psbt;
|
use util::psbt;
|
||||||
use util::psbt::map::Map;
|
use util::psbt::map::Map;
|
||||||
use util::psbt::raw;
|
use util::psbt::raw;
|
||||||
|
|
|
@ -19,7 +19,7 @@ use std::collections::btree_map::Entry;
|
||||||
use blockdata::script::Script;
|
use blockdata::script::Script;
|
||||||
use consensus::encode;
|
use consensus::encode;
|
||||||
use util::bip32::KeySource;
|
use util::bip32::KeySource;
|
||||||
use util::key::PublicKey;
|
use util::ecdsa::PublicKey;
|
||||||
use util::psbt;
|
use util::psbt;
|
||||||
use util::psbt::map::Map;
|
use util::psbt::map::Map;
|
||||||
use util::psbt::raw;
|
use util::psbt::raw;
|
||||||
|
|
|
@ -222,7 +222,7 @@ mod tests {
|
||||||
use network::constants::Network::Bitcoin;
|
use network::constants::Network::Bitcoin;
|
||||||
use consensus::encode::{deserialize, serialize, serialize_hex};
|
use consensus::encode::{deserialize, serialize, serialize_hex};
|
||||||
use util::bip32::{ChildNumber, ExtendedPrivKey, ExtendedPubKey, Fingerprint, KeySource};
|
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::map::{Global, Output, Input};
|
||||||
use util::psbt::raw;
|
use util::psbt::raw;
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ use blockdata::transaction::{SigHashType, Transaction, TxOut};
|
||||||
use consensus::encode::{self, serialize, Decodable};
|
use consensus::encode::{self, serialize, Decodable};
|
||||||
use util::bip32::{ChildNumber, Fingerprint, KeySource};
|
use util::bip32::{ChildNumber, Fingerprint, KeySource};
|
||||||
use hashes::{hash160, ripemd160, sha256, sha256d, Hash};
|
use hashes::{hash160, ripemd160, sha256, sha256d, Hash};
|
||||||
use util::key::PublicKey;
|
use util::ecdsa::PublicKey;
|
||||||
use util::psbt;
|
use util::psbt;
|
||||||
|
|
||||||
/// A trait for serializing a value as raw data for insertion into PSBT
|
/// A trait for serializing a value as raw data for insertion into PSBT
|
||||||
|
|
Loading…
Reference in New Issue