Merge rust-bitcoin/rust-bitcoin#757: Minimally-invasive separation of bitcoin keys from ECDSA signature types
8a993e8a58
Properly deprecate util::ecdsa key re-exports (Dr Maxim Orlovsky)bcb8932ccf
Re-org keys and ecdsa mods - pt.3 (Dr Maxim Orlovsky)d1c2213d3b
Re-org keys and ecdsa mods - pt.2 (Dr Maxim Orlovsky)b9170162d5
Re-org keys and ecdsa mods - pt.1 (Dr Maxim Orlovsky)2d9de78725
Re-export all key types under `util::key`. Deprecate other exports. (Dr Maxim Orlovsky) Pull request description: This PR tries to do a minimally-invazive separation of signature- and key-related types, previously mixed in a single `util::ecdsa` module. Rationale: bitcoin key types are not specific for signature algorithm. See discussion at #588. This PR became possible after we moved on new `secp256k1` version exposing `XonlyPublicKey` type, since now all key types may co-exist in a single module under different names The PR goal is achieved through - Renaming ecdsa mod into private ec module such that the code is not copied and diff size is small; - Introducing dummy ecdsa mod back in the next commit and re-exporiting only signature types from internal `ec` mod in it; - Re-exporting all key types under `key` module, removing previous depreciation message for bitcoin keys. ACKs for top commit: apoelstra: ACK8a993e8a58
sanket1729: utACK8a993e8a58
Tree-SHA512: 9f71edaa2cf4cdab4b239cb1d57576e2ba0fc3c2ec0ea19ae232005967b9400da6ded992b33d10b190ca617a66dca9b99be430bc5058a064f0be1489723c4a3a
This commit is contained in:
commit
d1f051c95a
|
@ -39,7 +39,7 @@ use policy::DUST_RELAY_TX_FEE;
|
|||
#[cfg(feature="bitcoinconsensus")] use core::convert::From;
|
||||
#[cfg(feature="bitcoinconsensus")] use OutPoint;
|
||||
|
||||
use util::ecdsa::PublicKey;
|
||||
use util::key::PublicKey;
|
||||
use util::address::WitnessVersion;
|
||||
use util::taproot::{LeafVersion, TapBranchHash, TapLeafHash};
|
||||
use secp256k1::{Secp256k1, Verification};
|
||||
|
@ -1031,7 +1031,7 @@ mod test {
|
|||
use hashes::hex::{FromHex, ToHex};
|
||||
use consensus::encode::{deserialize, serialize};
|
||||
use blockdata::opcodes;
|
||||
use util::ecdsa::PublicKey;
|
||||
use util::key::PublicKey;
|
||||
use util::psbt::serialize::Serialize;
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -138,10 +138,7 @@ pub use util::sighash::SchnorrSigHashType;
|
|||
|
||||
pub use util::ecdsa::{self, EcdsaSig, EcdsaSigError};
|
||||
pub use util::schnorr::{self, SchnorrSig, SchnorrSigError};
|
||||
#[deprecated(since = "0.26.1", note = "Please use `ecdsa::PrivateKey` instead")]
|
||||
pub use util::ecdsa::PrivateKey;
|
||||
#[deprecated(since = "0.26.1", note = "Please use `ecdsa::PublicKey` instead")]
|
||||
pub use util::ecdsa::PublicKey;
|
||||
pub use util::key::{PrivateKey, PublicKey, XOnlyPublicKey, KeyPair};
|
||||
#[allow(deprecated)]
|
||||
pub use blockdata::transaction::SigHashType;
|
||||
|
||||
|
|
|
@ -20,13 +20,13 @@
|
|||
//! ```rust
|
||||
//! use bitcoin::network::constants::Network;
|
||||
//! use bitcoin::util::address::Address;
|
||||
//! use bitcoin::util::ecdsa;
|
||||
//! use bitcoin::PublicKey;
|
||||
//! use bitcoin::secp256k1::Secp256k1;
|
||||
//! use bitcoin::secp256k1::rand::thread_rng;
|
||||
//!
|
||||
//! // Generate random key pair.
|
||||
//! let s = Secp256k1::new();
|
||||
//! let public_key = ecdsa::PublicKey::new(s.generate_keypair(&mut thread_rng()).1);
|
||||
//! let public_key = PublicKey::new(s.generate_keypair(&mut thread_rng()).1);
|
||||
//!
|
||||
//! // Generate pay-to-pubkey-hash address.
|
||||
//! let address = Address::p2pkh(&public_key, Network::Bitcoin);
|
||||
|
@ -47,8 +47,8 @@ use blockdata::{script, opcodes};
|
|||
use blockdata::constants::{PUBKEY_ADDRESS_PREFIX_MAIN, SCRIPT_ADDRESS_PREFIX_MAIN, PUBKEY_ADDRESS_PREFIX_TEST, SCRIPT_ADDRESS_PREFIX_TEST, MAX_SCRIPT_ELEMENT_SIZE};
|
||||
use network::constants::Network;
|
||||
use util::base58;
|
||||
use util::ecdsa;
|
||||
use util::taproot::TapBranchHash;
|
||||
use util::key::PublicKey;
|
||||
use blockdata::script::Instruction;
|
||||
use util::schnorr::{TapTweak, UntweakedPublicKey, TweakedPublicKey};
|
||||
|
||||
|
@ -408,7 +408,7 @@ impl Payload {
|
|||
|
||||
/// Creates a pay to (compressed) public key hash payload from a public key
|
||||
#[inline]
|
||||
pub fn p2pkh(pk: &ecdsa::PublicKey) -> Payload {
|
||||
pub fn p2pkh(pk: &PublicKey) -> Payload {
|
||||
Payload::PubkeyHash(pk.pubkey_hash())
|
||||
}
|
||||
|
||||
|
@ -422,7 +422,7 @@ impl Payload {
|
|||
}
|
||||
|
||||
/// Create a witness pay to public key payload from a public key
|
||||
pub fn p2wpkh(pk: &ecdsa::PublicKey) -> Result<Payload, Error> {
|
||||
pub fn p2wpkh(pk: &PublicKey) -> Result<Payload, Error> {
|
||||
Ok(Payload::WitnessProgram {
|
||||
version: WitnessVersion::V0,
|
||||
program: pk.wpubkey_hash().ok_or(Error::UncompressedPubkey)?.to_vec(),
|
||||
|
@ -430,7 +430,7 @@ impl Payload {
|
|||
}
|
||||
|
||||
/// Create a pay to script payload that embeds a witness pay to public key
|
||||
pub fn p2shwpkh(pk: &ecdsa::PublicKey) -> Result<Payload, Error> {
|
||||
pub fn p2shwpkh(pk: &PublicKey) -> Result<Payload, Error> {
|
||||
let builder = script::Builder::new()
|
||||
.push_int(0)
|
||||
.push_slice(&pk.wpubkey_hash().ok_or(Error::UncompressedPubkey)?);
|
||||
|
@ -543,7 +543,7 @@ impl Address {
|
|||
///
|
||||
/// This is the preferred non-witness type address.
|
||||
#[inline]
|
||||
pub fn p2pkh(pk: &ecdsa::PublicKey, network: Network) -> Address {
|
||||
pub fn p2pkh(pk: &PublicKey, network: Network) -> Address {
|
||||
Address {
|
||||
network,
|
||||
payload: Payload::p2pkh(pk),
|
||||
|
@ -568,7 +568,7 @@ impl Address {
|
|||
///
|
||||
/// # Errors
|
||||
/// Will only return an error if an uncompressed public key is provided.
|
||||
pub fn p2wpkh(pk: &ecdsa::PublicKey, network: Network) -> Result<Address, Error> {
|
||||
pub fn p2wpkh(pk: &PublicKey, network: Network) -> Result<Address, Error> {
|
||||
Ok(Address {
|
||||
network,
|
||||
payload: Payload::p2wpkh(pk)?,
|
||||
|
@ -581,7 +581,7 @@ impl Address {
|
|||
///
|
||||
/// # Errors
|
||||
/// Will only return an Error if an uncompressed public key is provided.
|
||||
pub fn p2shwpkh(pk: &ecdsa::PublicKey, network: Network) -> Result<Address, Error> {
|
||||
pub fn p2shwpkh(pk: &PublicKey, network: Network) -> Result<Address, Error> {
|
||||
Ok(Address {
|
||||
network,
|
||||
payload: Payload::p2shwpkh(pk)?,
|
||||
|
@ -878,7 +878,7 @@ mod tests {
|
|||
|
||||
use blockdata::script::Script;
|
||||
use network::constants::Network::{Bitcoin, Testnet};
|
||||
use util::ecdsa::PublicKey;
|
||||
use util::key::PublicKey;
|
||||
use secp256k1::XOnlyPublicKey;
|
||||
|
||||
use super::*;
|
||||
|
|
|
@ -194,7 +194,7 @@ mod tests {
|
|||
use consensus::encode::deserialize;
|
||||
use network::constants::Network;
|
||||
use util::address::Address;
|
||||
use util::ecdsa::PublicKey;
|
||||
use util::key::PublicKey;
|
||||
use hashes::hex::FromHex;
|
||||
|
||||
use super::*;
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
use prelude::*;
|
||||
|
||||
use io::Write;
|
||||
use core::{fmt, str::FromStr, default::Default};
|
||||
#[cfg(feature = "std")] use std::error;
|
||||
#[cfg(feature = "serde")] use serde;
|
||||
|
@ -28,9 +29,8 @@ use hashes::{sha512, Hash, HashEngine, Hmac, HmacEngine};
|
|||
use secp256k1::{self, Secp256k1, XOnlyPublicKey};
|
||||
|
||||
use network::constants::Network;
|
||||
use util::{base58, endian};
|
||||
use util::{key, ecdsa, schnorr};
|
||||
use io::Write;
|
||||
use util::{base58, endian, key};
|
||||
use util::key::{PublicKey, PrivateKey, KeyPair};
|
||||
|
||||
/// A chain code
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
|
@ -528,8 +528,8 @@ impl ExtendedPrivKey {
|
|||
}
|
||||
|
||||
/// Constructs ECDSA compressed private key matching internal secret key representation.
|
||||
pub fn to_priv(&self) -> ecdsa::PrivateKey {
|
||||
ecdsa::PrivateKey {
|
||||
pub fn to_priv(&self) -> PrivateKey {
|
||||
PrivateKey {
|
||||
compressed: true,
|
||||
network: self.network,
|
||||
inner: self.private_key
|
||||
|
@ -538,8 +538,8 @@ impl ExtendedPrivKey {
|
|||
|
||||
/// Constructs BIP340 keypair for Schnorr signatures and Taproot use matching the internal
|
||||
/// secret key representation.
|
||||
pub fn to_keypair<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>) -> schnorr::KeyPair {
|
||||
schnorr::KeyPair::from_seckey_slice(secp, &self.private_key[..]).expect("BIP32 internal private key representation is broken")
|
||||
pub fn to_keypair<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>) -> KeyPair {
|
||||
KeyPair::from_seckey_slice(secp, &self.private_key[..]).expect("BIP32 internal private key representation is broken")
|
||||
}
|
||||
|
||||
/// Attempts to derive an extended private key from a path.
|
||||
|
@ -660,8 +660,8 @@ impl ExtendedPubKey {
|
|||
}
|
||||
|
||||
/// Constructs ECDSA compressed public key matching internal public key representation.
|
||||
pub fn to_pub(&self) -> ecdsa::PublicKey {
|
||||
ecdsa::PublicKey {
|
||||
pub fn to_pub(&self) -> PublicKey {
|
||||
PublicKey {
|
||||
compressed: true,
|
||||
inner: self.public_key
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// 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
|
||||
|
@ -11,412 +12,17 @@
|
|||
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
//
|
||||
|
||||
//! ECDSA Bitcoin keys.
|
||||
//!
|
||||
//! This module provides ECDSA keys used in Bitcoin that can be roundtrip
|
||||
//! (de)serialized.
|
||||
//! ECDSA Bitcoin signatures.
|
||||
//!
|
||||
//! This module provides ECDSA signatures used Bitcoin that can be roundtrip (de)serialized.
|
||||
|
||||
use prelude::*;
|
||||
|
||||
use core::{ops, str::FromStr};
|
||||
use core::fmt::{self, Write as _fmtWrite};
|
||||
use io;
|
||||
|
||||
use secp256k1::{self, Secp256k1};
|
||||
use network::constants::Network;
|
||||
use hashes::{Hash, hash160, hex};
|
||||
use hashes::hex::FromHex;
|
||||
use hash_types::{PubkeyHash, WPubkeyHash};
|
||||
use util::base58;
|
||||
use util::key::Error;
|
||||
use blockdata::transaction::{EcdsaSigHashType, NonStandardSigHashType};
|
||||
|
||||
|
||||
/// 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 inner: secp256k1::PublicKey,
|
||||
}
|
||||
|
||||
impl PublicKey {
|
||||
/// Constructs compressed ECDSA public key from the provided generic Secp256k1 public key
|
||||
pub fn new(key: secp256k1::PublicKey) -> PublicKey {
|
||||
PublicKey {
|
||||
compressed: true,
|
||||
inner: key,
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs uncompressed (legacy) ECDSA public key from the provided generic Secp256k1
|
||||
/// public key
|
||||
pub fn new_uncompressed(key: secp256k1::PublicKey) -> PublicKey {
|
||||
PublicKey {
|
||||
compressed: false,
|
||||
inner: key,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns bitcoin 160-bit hash of the public key
|
||||
pub fn pubkey_hash(&self) -> PubkeyHash {
|
||||
if self.compressed {
|
||||
PubkeyHash::hash(&self.inner.serialize())
|
||||
} else {
|
||||
PubkeyHash::hash(&self.inner.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.inner.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.inner.serialize())
|
||||
} else {
|
||||
writer.write_all(&self.inner.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|{
|
||||
// Need a static string for core2
|
||||
#[cfg(feature = "std")]
|
||||
let reason = e;
|
||||
#[cfg(not(feature = "std"))]
|
||||
let reason = match e {
|
||||
Error::Base58(_) => "base58 error",
|
||||
Error::Secp256k1(_) => "secp256k1 error",
|
||||
};
|
||||
io::Error::new(io::ErrorKind::InvalidData, reason)
|
||||
})
|
||||
}
|
||||
|
||||
/// 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,
|
||||
inner: 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.inner.serialize()[..] {
|
||||
write!(f, "{:02x}", ch)?;
|
||||
}
|
||||
} else {
|
||||
for ch in &self.inner.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 {
|
||||
inner: key,
|
||||
compressed: s.len() == 66
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A Bitcoin ECDSA private key
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
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 inner: secp256k1::SecretKey,
|
||||
}
|
||||
|
||||
impl PrivateKey {
|
||||
/// Constructs compressed ECDSA private key from the provided generic Secp256k1 private key
|
||||
/// and the specified network
|
||||
pub fn new(key: secp256k1::SecretKey, network: Network) -> PrivateKey {
|
||||
PrivateKey {
|
||||
compressed: true,
|
||||
network,
|
||||
inner: key,
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs uncompressed (legacy) ECDSA private key from the provided generic Secp256k1
|
||||
/// private key and the specified network
|
||||
pub fn new_uncompressed(key: secp256k1::SecretKey, network: Network) -> PrivateKey {
|
||||
PrivateKey {
|
||||
compressed: false,
|
||||
network,
|
||||
inner: key,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a public key from this private key
|
||||
pub fn public_key<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>) -> PublicKey {
|
||||
PublicKey {
|
||||
compressed: self.compressed,
|
||||
inner: secp256k1::PublicKey::from_secret_key(secp, &self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
/// Serialize the private key to bytes
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.inner[..].to_vec()
|
||||
}
|
||||
|
||||
/// Deserialize a private key from a slice
|
||||
pub fn from_slice(data: &[u8], network: Network) -> Result<PrivateKey, Error> {
|
||||
Ok(PrivateKey::new(
|
||||
secp256k1::SecretKey::from_slice(data)?,
|
||||
network,
|
||||
))
|
||||
}
|
||||
|
||||
/// 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.inner[..]);
|
||||
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::InvalidAddressVersion(x))); }
|
||||
};
|
||||
|
||||
Ok(PrivateKey {
|
||||
compressed,
|
||||
network,
|
||||
inner: 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)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
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.inner[..]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(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")]
|
||||
#[cfg_attr(docsrs, doc(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 ::core::fmt::Formatter) -> ::core::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) = ::core::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")]
|
||||
#[cfg_attr(docsrs, doc(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.inner.serialize()[..])
|
||||
} else {
|
||||
s.serialize_bytes(&self.inner.serialize_uncompressed()[..])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(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 ::core::fmt::Formatter) -> ::core::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) = ::core::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 ::core::fmt::Formatter) -> ::core::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)
|
||||
}
|
||||
}
|
||||
}
|
||||
use core::str::FromStr;
|
||||
use core::fmt;
|
||||
use hashes::hex::{self, FromHex};
|
||||
use blockdata::transaction::NonStandardSigHashType;
|
||||
use secp256k1;
|
||||
use EcdsaSigHashType;
|
||||
|
||||
/// An ECDSA signature with the corresponding hash type.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
|
@ -527,162 +133,3 @@ impl From<hex::Error> for EcdsaSigError {
|
|||
EcdsaSigError::HexEncoding(err)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use io;
|
||||
use super::{PrivateKey, PublicKey};
|
||||
use secp256k1::Secp256k1;
|
||||
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 {
|
||||
inner: pk.inner,
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
564
src/util/key.rs
564
src/util/key.rs
|
@ -13,18 +13,25 @@
|
|||
|
||||
//! Bitcoin keys.
|
||||
//!
|
||||
//! This module provides keys used in Bitcoin that can be roundtrip (de)serialized.
|
||||
//!
|
||||
//! This module provides keys used in Bitcoin that can be roundtrip
|
||||
//! (de)serialized.
|
||||
|
||||
#[deprecated(since = "0.26.1", note = "Please use `util::ecdsa` instead")]
|
||||
pub use util::ecdsa::{PrivateKey, PublicKey};
|
||||
pub use secp256k1::{XOnlyPublicKey, KeyPair};
|
||||
|
||||
use core::fmt;
|
||||
use prelude::*;
|
||||
|
||||
use core::{ops, str::FromStr};
|
||||
use core::fmt::{self, Write as _fmtWrite};
|
||||
use io;
|
||||
#[cfg(feature = "std")] use std::error;
|
||||
|
||||
use secp256k1;
|
||||
use secp256k1::{self, Secp256k1};
|
||||
use network::constants::Network;
|
||||
use hashes::{Hash, hash160};
|
||||
use hash_types::{PubkeyHash, WPubkeyHash};
|
||||
use util::base58;
|
||||
|
||||
|
||||
/// A key-related error.
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
pub enum Error {
|
||||
|
@ -68,3 +75,548 @@ impl From<secp256k1::Error> 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 inner: secp256k1::PublicKey,
|
||||
}
|
||||
|
||||
impl PublicKey {
|
||||
/// Constructs compressed ECDSA public key from the provided generic Secp256k1 public key
|
||||
pub fn new(key: secp256k1::PublicKey) -> PublicKey {
|
||||
PublicKey {
|
||||
compressed: true,
|
||||
inner: key,
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs uncompressed (legacy) ECDSA public key from the provided generic Secp256k1
|
||||
/// public key
|
||||
pub fn new_uncompressed(key: secp256k1::PublicKey) -> PublicKey {
|
||||
PublicKey {
|
||||
compressed: false,
|
||||
inner: key,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns bitcoin 160-bit hash of the public key
|
||||
pub fn pubkey_hash(&self) -> PubkeyHash {
|
||||
if self.compressed {
|
||||
PubkeyHash::hash(&self.inner.serialize())
|
||||
} else {
|
||||
PubkeyHash::hash(&self.inner.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.inner.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.inner.serialize())
|
||||
} else {
|
||||
writer.write_all(&self.inner.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|{
|
||||
// Need a static string for core2
|
||||
#[cfg(feature = "std")]
|
||||
let reason = e;
|
||||
#[cfg(not(feature = "std"))]
|
||||
let reason = match e {
|
||||
Error::Base58(_) => "base58 error",
|
||||
Error::Secp256k1(_) => "secp256k1 error",
|
||||
};
|
||||
io::Error::new(io::ErrorKind::InvalidData, reason)
|
||||
})
|
||||
}
|
||||
|
||||
/// 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,
|
||||
inner: 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.inner.serialize()[..] {
|
||||
write!(f, "{:02x}", ch)?;
|
||||
}
|
||||
} else {
|
||||
for ch in &self.inner.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 {
|
||||
inner: key,
|
||||
compressed: s.len() == 66
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A Bitcoin ECDSA private key
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
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 inner: secp256k1::SecretKey,
|
||||
}
|
||||
|
||||
impl PrivateKey {
|
||||
/// Constructs compressed ECDSA private key from the provided generic Secp256k1 private key
|
||||
/// and the specified network
|
||||
pub fn new(key: secp256k1::SecretKey, network: Network) -> PrivateKey {
|
||||
PrivateKey {
|
||||
compressed: true,
|
||||
network,
|
||||
inner: key,
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs uncompressed (legacy) ECDSA private key from the provided generic Secp256k1
|
||||
/// private key and the specified network
|
||||
pub fn new_uncompressed(key: secp256k1::SecretKey, network: Network) -> PrivateKey {
|
||||
PrivateKey {
|
||||
compressed: false,
|
||||
network,
|
||||
inner: key,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a public key from this private key
|
||||
pub fn public_key<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>) -> PublicKey {
|
||||
PublicKey {
|
||||
compressed: self.compressed,
|
||||
inner: secp256k1::PublicKey::from_secret_key(secp, &self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
/// Serialize the private key to bytes
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.inner[..].to_vec()
|
||||
}
|
||||
|
||||
/// Deserialize a private key from a slice
|
||||
pub fn from_slice(data: &[u8], network: Network) -> Result<PrivateKey, Error> {
|
||||
Ok(PrivateKey::new(
|
||||
secp256k1::SecretKey::from_slice(data)?,
|
||||
network,
|
||||
))
|
||||
}
|
||||
|
||||
/// 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.inner[..]);
|
||||
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::InvalidAddressVersion(x))); }
|
||||
};
|
||||
|
||||
Ok(PrivateKey {
|
||||
compressed,
|
||||
network,
|
||||
inner: 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)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
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.inner[..]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(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")]
|
||||
#[cfg_attr(docsrs, doc(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 ::core::fmt::Formatter) -> ::core::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) = ::core::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")]
|
||||
#[cfg_attr(docsrs, doc(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.inner.serialize()[..])
|
||||
} else {
|
||||
s.serialize_bytes(&self.inner.serialize_uncompressed()[..])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[cfg_attr(docsrs, doc(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 ::core::fmt::Formatter) -> ::core::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) = ::core::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 ::core::fmt::Formatter) -> ::core::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 io;
|
||||
use super::{PrivateKey, PublicKey};
|
||||
use secp256k1::Secp256k1;
|
||||
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 {
|
||||
inner: pk.inner,
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ mod message_signing {
|
|||
use secp256k1;
|
||||
use secp256k1::ecdsa::{RecoveryId, RecoverableSignature};
|
||||
|
||||
use util::ecdsa::PublicKey;
|
||||
use util::key::PublicKey;
|
||||
use util::address::{Address, AddressType};
|
||||
|
||||
/// An error used for dealing with Bitcoin Signed Messages.
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
//! Functions needed by all parts of the Bitcoin library.
|
||||
//!
|
||||
|
||||
pub mod ecdsa;
|
||||
pub mod key;
|
||||
pub mod ecdsa;
|
||||
pub mod schnorr;
|
||||
pub mod address;
|
||||
pub mod amount;
|
||||
|
|
|
@ -20,18 +20,27 @@
|
|||
use core::fmt;
|
||||
use prelude::*;
|
||||
|
||||
pub use secp256k1::{XOnlyPublicKey, KeyPair};
|
||||
use secp256k1::{XOnlyPublicKey as _XOnlyPublicKey, KeyPair as _KeyPair};
|
||||
|
||||
use secp256k1::{self, Secp256k1, Verification, constants};
|
||||
use hashes::Hash;
|
||||
use util::taproot::{TapBranchHash, TapTweakHash};
|
||||
use SchnorrSigHashType;
|
||||
|
||||
/// Deprecated re-export of [`secp256k1::XOnlyPublicKey`]
|
||||
#[deprecated(since = "0.28.0", note = "Please use `util::key::XOnlyPublicKey` instead")]
|
||||
pub type XOnlyPublicKey = _XOnlyPublicKey;
|
||||
|
||||
/// Deprecated re-export of [`secp256k1::KeyPair`]
|
||||
#[deprecated(since = "0.28.0", note = "Please use `util::key::KeyPair` instead")]
|
||||
pub type KeyPair = _KeyPair;
|
||||
|
||||
/// Untweaked BIP-340 X-coord-only public key
|
||||
pub type UntweakedPublicKey = XOnlyPublicKey;
|
||||
pub type UntweakedPublicKey = ::XOnlyPublicKey;
|
||||
|
||||
/// Tweaked BIP-340 X-coord-only public key
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct TweakedPublicKey(XOnlyPublicKey);
|
||||
pub struct TweakedPublicKey(::XOnlyPublicKey);
|
||||
|
||||
impl fmt::LowerHex for TweakedPublicKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
@ -46,13 +55,13 @@ impl fmt::Display for TweakedPublicKey {
|
|||
}
|
||||
|
||||
/// Untweaked BIP-340 key pair
|
||||
pub type UntweakedKeyPair = KeyPair;
|
||||
pub type UntweakedKeyPair = ::KeyPair;
|
||||
|
||||
/// Tweaked BIP-340 key pair
|
||||
#[derive(Clone)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
// TODO: Add other derives once secp256k1 v0.21.3 released
|
||||
pub struct TweakedKeyPair(KeyPair);
|
||||
pub struct TweakedKeyPair(::KeyPair);
|
||||
|
||||
/// A trait for tweaking BIP340 key types (x-only public keys and key pairs).
|
||||
pub trait TapTweak {
|
||||
|
@ -129,7 +138,7 @@ impl TapTweak for UntweakedKeyPair {
|
|||
/// # Returns
|
||||
/// The tweaked key and its parity.
|
||||
fn tap_tweak<C: Verification>(mut self, secp: &Secp256k1<C>, merkle_root: Option<TapBranchHash>) -> TweakedKeyPair {
|
||||
let pubkey = XOnlyPublicKey::from_keypair(&self);
|
||||
let pubkey = ::XOnlyPublicKey::from_keypair(&self);
|
||||
let tweak_value = TapTweakHash::from_key_and_tweak(pubkey, merkle_root).into_inner();
|
||||
self.tweak_add_assign(&secp, &tweak_value).expect("Tap tweak failed");
|
||||
TweakedKeyPair(self)
|
||||
|
@ -147,17 +156,17 @@ impl TweakedPublicKey {
|
|||
/// This method is dangerous and can lead to loss of funds if used incorrectly.
|
||||
/// Specifically, in multi-party protocols a peer can provide a value that allows them to steal.
|
||||
#[inline]
|
||||
pub fn dangerous_assume_tweaked(key: XOnlyPublicKey) -> TweakedPublicKey {
|
||||
pub fn dangerous_assume_tweaked(key: ::XOnlyPublicKey) -> TweakedPublicKey {
|
||||
TweakedPublicKey(key)
|
||||
}
|
||||
|
||||
/// Returns the underlying public key.
|
||||
pub fn into_inner(self) -> XOnlyPublicKey {
|
||||
pub fn into_inner(self) -> ::XOnlyPublicKey {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Returns a reference to underlying public key.
|
||||
pub fn as_inner(&self) -> &XOnlyPublicKey {
|
||||
pub fn as_inner(&self) -> &::XOnlyPublicKey {
|
||||
&self.0
|
||||
}
|
||||
|
||||
|
@ -177,25 +186,25 @@ impl TweakedKeyPair {
|
|||
/// This method is dangerous and can lead to loss of funds if used incorrectly.
|
||||
/// Specifically, in multi-party protocols a peer can provide a value that allows them to steal.
|
||||
#[inline]
|
||||
pub fn dangerous_assume_tweaked(pair: KeyPair) -> TweakedKeyPair {
|
||||
pub fn dangerous_assume_tweaked(pair: ::KeyPair) -> TweakedKeyPair {
|
||||
TweakedKeyPair(pair)
|
||||
}
|
||||
|
||||
/// Returns the underlying key pair
|
||||
#[inline]
|
||||
pub fn into_inner(self) -> KeyPair {
|
||||
pub fn into_inner(self) -> ::KeyPair {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TweakedPublicKey> for XOnlyPublicKey {
|
||||
impl From<TweakedPublicKey> for ::XOnlyPublicKey {
|
||||
#[inline]
|
||||
fn from(pair: TweakedPublicKey) -> Self {
|
||||
pair.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TweakedKeyPair> for KeyPair {
|
||||
impl From<TweakedKeyPair> for ::KeyPair {
|
||||
#[inline]
|
||||
fn from(pair: TweakedKeyPair) -> Self {
|
||||
pair.0
|
||||
|
|
Loading…
Reference in New Issue