Merge rust-bitcoin/rust-bitcoin#2232: Add `NetworkKind`
4354f37f51
Use NetworkKind in bip32 module (Tobin C. Harding)35bbfcded7
Use NetworkKind in PrivateKey (Tobin C. Harding)d22f3828f6
Use NetworkKind in address module (Tobin C. Harding)6d5ef23e61
Add NetworkKind (Tobin C. Harding) Pull request description: Add a new type `NetworkKind` the describes the kind of network we are on, ether mainnet or one of the test nets (testnet, regtest, signet). Use it in `Address`, `PrivateKey`, and `bip32`. ACKs for top commit: Kixunil: ACK4354f37f51
apoelstra: ACK4354f37f51
Tree-SHA512: d015283b55b2dc68c0a5cc5f17332fce648d3e4fb4e63e0b61943b7edfe4112e8ace760cb743cd3fd5ad0c34636e51a6a12382b34b6e62dfd7dab894d2ceefc0
This commit is contained in:
commit
a90104af2d
|
@ -8,7 +8,7 @@ use bitcoin::bip32::{ChildNumber, DerivationPath, Xpriv, Xpub};
|
||||||
use bitcoin::hex::FromHex;
|
use bitcoin::hex::FromHex;
|
||||||
use bitcoin::secp256k1::ffi::types::AlignedType;
|
use bitcoin::secp256k1::ffi::types::AlignedType;
|
||||||
use bitcoin::secp256k1::Secp256k1;
|
use bitcoin::secp256k1::Secp256k1;
|
||||||
use bitcoin::CompressedPublicKey;
|
use bitcoin::{Network, NetworkKind, CompressedPublicKey};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// This example derives root xprv from a 32-byte seed,
|
// This example derives root xprv from a 32-byte seed,
|
||||||
|
@ -26,10 +26,7 @@ fn main() {
|
||||||
|
|
||||||
let seed_hex = &args[1];
|
let seed_hex = &args[1];
|
||||||
println!("Seed: {}", seed_hex);
|
println!("Seed: {}", seed_hex);
|
||||||
|
println!("Using mainnet network");
|
||||||
// default network as mainnet
|
|
||||||
let network = bitcoin::Network::Bitcoin;
|
|
||||||
println!("Network: {:?}", network);
|
|
||||||
|
|
||||||
let seed = Vec::from_hex(seed_hex).unwrap();
|
let seed = Vec::from_hex(seed_hex).unwrap();
|
||||||
|
|
||||||
|
@ -39,7 +36,7 @@ fn main() {
|
||||||
let secp = Secp256k1::preallocated_new(buf.as_mut_slice()).unwrap();
|
let secp = Secp256k1::preallocated_new(buf.as_mut_slice()).unwrap();
|
||||||
|
|
||||||
// calculate root key from seed
|
// calculate root key from seed
|
||||||
let root = Xpriv::new_master(network, &seed).unwrap();
|
let root = Xpriv::new_master(NetworkKind::Main, &seed).unwrap();
|
||||||
println!("Root key: {}", root);
|
println!("Root key: {}", root);
|
||||||
|
|
||||||
// derive child xpub
|
// derive child xpub
|
||||||
|
@ -53,6 +50,6 @@ fn main() {
|
||||||
// manually creating indexes this time
|
// manually creating indexes this time
|
||||||
let zero = ChildNumber::from_normal_idx(0).unwrap();
|
let zero = ChildNumber::from_normal_idx(0).unwrap();
|
||||||
let public_key = xpub.derive_pub(&secp, &[zero, zero]).unwrap().public_key;
|
let public_key = xpub.derive_pub(&secp, &[zero, zero]).unwrap().public_key;
|
||||||
let address = Address::p2wpkh(&CompressedPublicKey(public_key), network);
|
let address = Address::p2wpkh(&CompressedPublicKey(public_key), Network::Bitcoin);
|
||||||
println!("First receiving address: {}", address);
|
println!("First receiving address: {}", address);
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ use crate::blockdata::script::witness_program::WitnessProgram;
|
||||||
use crate::blockdata::script::witness_version::WitnessVersion;
|
use crate::blockdata::script::witness_version::WitnessVersion;
|
||||||
use crate::blockdata::script::{self, PushBytesBuf, Script, ScriptBuf, ScriptHash};
|
use crate::blockdata::script::{self, PushBytesBuf, Script, ScriptBuf, ScriptHash};
|
||||||
use crate::crypto::key::{PubkeyHash, PublicKey, CompressedPublicKey, TweakedPublicKey, UntweakedPublicKey};
|
use crate::crypto::key::{PubkeyHash, PublicKey, CompressedPublicKey, TweakedPublicKey, UntweakedPublicKey};
|
||||||
use crate::network::Network;
|
use crate::network::{Network, NetworkKind};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::taproot::TapNodeHash;
|
use crate::taproot::TapNodeHash;
|
||||||
|
|
||||||
|
@ -135,8 +135,8 @@ impl NetworkValidation for NetworkUnchecked {
|
||||||
/// addresses are used only on the appropriate network.
|
/// addresses are used only on the appropriate network.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
enum AddressInner {
|
enum AddressInner {
|
||||||
P2pkh { hash: PubkeyHash, prefix: LegacyP2pkhPrefix },
|
P2pkh { hash: PubkeyHash, network: NetworkKind },
|
||||||
P2sh { hash: ScriptHash, prefix: LegacyP2shPrefix },
|
P2sh { hash: ScriptHash, network: NetworkKind },
|
||||||
Segwit { program: WitnessProgram, hrp: KnownHrp },
|
Segwit { program: WitnessProgram, hrp: KnownHrp },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,15 +145,21 @@ impl fmt::Display for AddressInner {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
use AddressInner::*;
|
use AddressInner::*;
|
||||||
match self {
|
match self {
|
||||||
P2pkh { hash, prefix } => {
|
P2pkh { hash, network } => {
|
||||||
let mut prefixed = [0; 21];
|
let mut prefixed = [0; 21];
|
||||||
prefixed[0] = prefix.to_u8();
|
prefixed[0] = match network {
|
||||||
|
NetworkKind::Main => PUBKEY_ADDRESS_PREFIX_MAIN,
|
||||||
|
NetworkKind::Test => PUBKEY_ADDRESS_PREFIX_TEST,
|
||||||
|
};
|
||||||
prefixed[1..].copy_from_slice(&hash[..]);
|
prefixed[1..].copy_from_slice(&hash[..]);
|
||||||
base58::encode_check_to_fmt(fmt, &prefixed[..])
|
base58::encode_check_to_fmt(fmt, &prefixed[..])
|
||||||
}
|
}
|
||||||
P2sh { hash, prefix } => {
|
P2sh { hash, network } => {
|
||||||
let mut prefixed = [0; 21];
|
let mut prefixed = [0; 21];
|
||||||
prefixed[0] = prefix.to_u8();
|
prefixed[0] = match network {
|
||||||
|
NetworkKind::Main => SCRIPT_ADDRESS_PREFIX_MAIN,
|
||||||
|
NetworkKind::Test => SCRIPT_ADDRESS_PREFIX_TEST,
|
||||||
|
};
|
||||||
prefixed[1..].copy_from_slice(&hash[..]);
|
prefixed[1..].copy_from_slice(&hash[..]);
|
||||||
base58::encode_check_to_fmt(fmt, &prefixed[..])
|
base58::encode_check_to_fmt(fmt, &prefixed[..])
|
||||||
}
|
}
|
||||||
|
@ -172,64 +178,6 @@ impl fmt::Display for AddressInner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prefix byte used for legacy P2PKH addresses.
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
enum LegacyP2pkhPrefix {
|
|
||||||
/// Prefix used for legacy addresses on the main Bitcoin network.
|
|
||||||
Mainnet,
|
|
||||||
/// Prefix used for legacy addresses on all other test networks (testnet, signet, regtest).
|
|
||||||
AllTestnets,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LegacyP2pkhPrefix {
|
|
||||||
/// Creates a legacy prefix from the associated `network`.
|
|
||||||
fn from_network(network: Network) -> Self {
|
|
||||||
use Network::*;
|
|
||||||
|
|
||||||
match network {
|
|
||||||
Bitcoin => Self::Mainnet,
|
|
||||||
Signet | Testnet | Regtest => Self::AllTestnets,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts this prefix enum to the respective byte value.
|
|
||||||
fn to_u8(self) -> u8 {
|
|
||||||
match self {
|
|
||||||
Self::Mainnet => PUBKEY_ADDRESS_PREFIX_MAIN,
|
|
||||||
Self::AllTestnets => PUBKEY_ADDRESS_PREFIX_TEST,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prefix byte used for legacy P2SH addresses.
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
enum LegacyP2shPrefix {
|
|
||||||
/// Prefix used for legacy addresses on the main Bitcoin network.
|
|
||||||
Mainnet,
|
|
||||||
/// Prefix used for legacy addresses on all other test networks (testnet, signet, regtest).
|
|
||||||
AllTestnets,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LegacyP2shPrefix {
|
|
||||||
/// Creates a legacy prefix from the associated `network`.
|
|
||||||
fn from_network(network: Network) -> Self {
|
|
||||||
use Network::*;
|
|
||||||
|
|
||||||
match network {
|
|
||||||
Bitcoin => Self::Mainnet,
|
|
||||||
Signet | Testnet | Regtest => Self::AllTestnets,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts this prefix enum to the respective byte value.
|
|
||||||
fn to_u8(self) -> u8 {
|
|
||||||
match self {
|
|
||||||
Self::Mainnet => SCRIPT_ADDRESS_PREFIX_MAIN,
|
|
||||||
Self::AllTestnets => SCRIPT_ADDRESS_PREFIX_TEST,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Known bech32 human-readable parts.
|
/// Known bech32 human-readable parts.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
|
@ -277,6 +225,10 @@ impl KnownHrp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Network> for KnownHrp {
|
||||||
|
fn from(n: Network) -> Self { Self::from_network(n) }
|
||||||
|
}
|
||||||
|
|
||||||
/// A Bitcoin address.
|
/// A Bitcoin address.
|
||||||
///
|
///
|
||||||
/// ### Parsing addresses
|
/// ### Parsing addresses
|
||||||
|
@ -412,10 +364,9 @@ impl Address {
|
||||||
///
|
///
|
||||||
/// This is the preferred non-witness type address.
|
/// This is the preferred non-witness type address.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn p2pkh(pk: impl Into<PubkeyHash>, network: Network) -> Address {
|
pub fn p2pkh(pk: impl Into<PubkeyHash>, network: impl Into<NetworkKind>) -> Address {
|
||||||
let hash = pk.into();
|
let hash = pk.into();
|
||||||
let prefix = LegacyP2pkhPrefix::from_network(network);
|
Self(AddressInner::P2pkh { hash, network: network.into() }, PhantomData)
|
||||||
Self(AddressInner::P2pkh { hash, prefix }, PhantomData)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a pay to script hash P2SH address from a script.
|
/// Creates a pay to script hash P2SH address from a script.
|
||||||
|
@ -423,7 +374,7 @@ impl Address {
|
||||||
/// This address type was introduced with BIP16 and is the popular type to implement multi-sig
|
/// This address type was introduced with BIP16 and is the popular type to implement multi-sig
|
||||||
/// these days.
|
/// these days.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn p2sh(script: &Script, network: Network) -> Result<Address, Error> {
|
pub fn p2sh(script: &Script, network: impl Into<NetworkKind>) -> Result<Address, Error> {
|
||||||
if script.len() > MAX_SCRIPT_ELEMENT_SIZE {
|
if script.len() > MAX_SCRIPT_ELEMENT_SIZE {
|
||||||
return Err(Error::ExcessiveScriptSize);
|
return Err(Error::ExcessiveScriptSize);
|
||||||
}
|
}
|
||||||
|
@ -432,9 +383,8 @@ impl Address {
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is intentionally not public so we enforce script length checks.
|
// This is intentionally not public so we enforce script length checks.
|
||||||
fn p2sh_from_hash(hash: ScriptHash, network: Network) -> Address {
|
fn p2sh_from_hash(hash: ScriptHash, network: impl Into<NetworkKind>) -> Address {
|
||||||
let prefix = LegacyP2shPrefix::from_network(network);
|
Self(AddressInner::P2sh { hash, network: network.into() }, PhantomData)
|
||||||
Self(AddressInner::P2sh { hash, prefix }, PhantomData)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a witness pay to public key address from a public key.
|
/// Creates a witness pay to public key address from a public key.
|
||||||
|
@ -453,8 +403,8 @@ 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.
|
||||||
pub fn p2shwpkh(
|
pub fn p2shwpkh(
|
||||||
pk: &CompressedPublicKey,
|
pk: &CompressedPublicKey,
|
||||||
network: Network,
|
network: impl Into<NetworkKind>,
|
||||||
) -> Self {
|
) -> Address {
|
||||||
let builder = script::Builder::new().push_int(0).push_slice(pk.wpubkey_hash());
|
let builder = script::Builder::new().push_int(0).push_slice(pk.wpubkey_hash());
|
||||||
let script_hash = builder.as_script().script_hash();
|
let script_hash = builder.as_script().script_hash();
|
||||||
Address::p2sh_from_hash(script_hash, network)
|
Address::p2sh_from_hash(script_hash, network)
|
||||||
|
@ -469,7 +419,7 @@ impl Address {
|
||||||
/// Creates a pay to script address that embeds a witness pay to script hash address.
|
/// Creates a pay to script address that embeds a witness pay to script hash 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.
|
||||||
pub fn p2shwsh(script: &Script, network: Network) -> Address {
|
pub fn p2shwsh(script: &Script, network: impl Into<NetworkKind>) -> Address {
|
||||||
let builder = script::Builder::new().push_int(0).push_slice(script.wscript_hash());
|
let builder = script::Builder::new().push_int(0).push_slice(script.wscript_hash());
|
||||||
let script_hash = builder.as_script().script_hash();
|
let script_hash = builder.as_script().script_hash();
|
||||||
Address::p2sh_from_hash(script_hash, network)
|
Address::p2sh_from_hash(script_hash, network)
|
||||||
|
@ -496,6 +446,7 @@ impl Address {
|
||||||
///
|
///
|
||||||
/// This only exists to support future witness versions. If you are doing normal mainnet things
|
/// This only exists to support future witness versions. If you are doing normal mainnet things
|
||||||
/// then you likely do not need this constructor.
|
/// then you likely do not need this constructor.
|
||||||
|
// TODO: This is still arguably wrong, could take a KnownHrp/Hrp instead of Network.
|
||||||
pub fn from_witness_program(program: WitnessProgram, network: Network) -> Address {
|
pub fn from_witness_program(program: WitnessProgram, network: Network) -> Address {
|
||||||
let hrp = KnownHrp::from_network(network);
|
let hrp = KnownHrp::from_network(network);
|
||||||
let inner = AddressInner::Segwit { program, hrp };
|
let inner = AddressInner::Segwit { program, hrp };
|
||||||
|
@ -530,7 +481,7 @@ impl Address {
|
||||||
use AddressInner::*;
|
use AddressInner::*;
|
||||||
|
|
||||||
match self.0 {
|
match self.0 {
|
||||||
P2pkh { ref hash, prefix: _ } => Some(*hash),
|
P2pkh { ref hash, network: _ } => Some(*hash),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -540,7 +491,7 @@ impl Address {
|
||||||
use AddressInner::*;
|
use AddressInner::*;
|
||||||
|
|
||||||
match self.0 {
|
match self.0 {
|
||||||
P2sh { ref hash, prefix: _ } => Some(*hash),
|
P2sh { ref hash, network: _ } => Some(*hash),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -587,9 +538,13 @@ impl Address {
|
||||||
pub fn script_pubkey(&self) -> ScriptBuf {
|
pub fn script_pubkey(&self) -> ScriptBuf {
|
||||||
use AddressInner::*;
|
use AddressInner::*;
|
||||||
match self.0 {
|
match self.0 {
|
||||||
P2pkh { ref hash, prefix: _ } => ScriptBuf::new_p2pkh(hash),
|
P2pkh { ref hash, network: _ } => ScriptBuf::new_p2pkh(hash),
|
||||||
P2sh { ref hash, prefix: _ } => ScriptBuf::new_p2sh(hash),
|
P2sh { ref hash, network: _ } => ScriptBuf::new_p2sh(hash),
|
||||||
Segwit { ref program, hrp: _ } => ScriptBuf::new_witness_program(program),
|
Segwit { ref program, hrp: _ } => {
|
||||||
|
let prog = program.program();
|
||||||
|
let version = program.version();
|
||||||
|
ScriptBuf::new_witness_program_unchecked(version, prog)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -650,9 +605,9 @@ impl Address {
|
||||||
pub fn matches_script_pubkey(&self, script: &Script) -> bool {
|
pub fn matches_script_pubkey(&self, script: &Script) -> bool {
|
||||||
use AddressInner::*;
|
use AddressInner::*;
|
||||||
match self.0 {
|
match self.0 {
|
||||||
P2pkh { ref hash, prefix: _ } if script.is_p2pkh() =>
|
P2pkh { ref hash, network: _ } if script.is_p2pkh() =>
|
||||||
&script.as_bytes()[3..23] == <PubkeyHash as AsRef<[u8; 20]>>::as_ref(hash),
|
&script.as_bytes()[3..23] == <PubkeyHash as AsRef<[u8; 20]>>::as_ref(hash),
|
||||||
P2sh { ref hash, prefix: _ } if script.is_p2sh() =>
|
P2sh { ref hash, network: _ } if script.is_p2sh() =>
|
||||||
&script.as_bytes()[2..22] == <ScriptHash as AsRef<[u8; 20]>>::as_ref(hash),
|
&script.as_bytes()[2..22] == <ScriptHash as AsRef<[u8; 20]>>::as_ref(hash),
|
||||||
Segwit { ref program, hrp: _ } if script.is_witness_program() =>
|
Segwit { ref program, hrp: _ } if script.is_witness_program() =>
|
||||||
&script.as_bytes()[2..] == program.program().as_bytes(),
|
&script.as_bytes()[2..] == program.program().as_bytes(),
|
||||||
|
@ -671,8 +626,8 @@ impl Address {
|
||||||
fn payload_as_bytes(&self) -> &[u8] {
|
fn payload_as_bytes(&self) -> &[u8] {
|
||||||
use AddressInner::*;
|
use AddressInner::*;
|
||||||
match self.0 {
|
match self.0 {
|
||||||
P2sh { ref hash, prefix: _ } => hash.as_ref(),
|
P2sh { ref hash, network: _ } => hash.as_ref(),
|
||||||
P2pkh { ref hash, prefix: _ } => hash.as_ref(),
|
P2pkh { ref hash, network: _ } => hash.as_ref(),
|
||||||
Segwit { ref program, hrp: _ } => program.program().as_bytes(),
|
Segwit { ref program, hrp: _ } => program.program().as_bytes(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -706,12 +661,12 @@ impl Address<NetworkUnchecked> {
|
||||||
/// assert!(address.is_valid_for_network(Network::Bitcoin));
|
/// assert!(address.is_valid_for_network(Network::Bitcoin));
|
||||||
/// assert_eq!(address.is_valid_for_network(Network::Testnet), false);
|
/// assert_eq!(address.is_valid_for_network(Network::Testnet), false);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn is_valid_for_network(&self, network: Network) -> bool {
|
pub fn is_valid_for_network(&self, n: Network) -> bool {
|
||||||
use AddressInner::*;
|
use AddressInner::*;
|
||||||
match self.0 {
|
match self.0 {
|
||||||
P2pkh { hash: _, ref prefix } => *prefix == LegacyP2pkhPrefix::from_network(network),
|
P2pkh { hash: _, ref network } => *network == NetworkKind::from(n),
|
||||||
P2sh { hash: _, ref prefix } => *prefix == LegacyP2shPrefix::from_network(network),
|
P2sh { hash: _, ref network } => *network == NetworkKind::from(n),
|
||||||
Segwit { program: _, ref hrp } => *hrp == KnownHrp::from_network(network),
|
Segwit { program: _, ref hrp } => *hrp == KnownHrp::from_network(n),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -739,8 +694,8 @@ impl Address<NetworkUnchecked> {
|
||||||
use AddressInner::*;
|
use AddressInner::*;
|
||||||
|
|
||||||
let inner = match self.0 {
|
let inner = match self.0 {
|
||||||
P2pkh { hash, prefix } => P2pkh { hash, prefix },
|
P2pkh { hash, network } => P2pkh { hash, network },
|
||||||
P2sh { hash, prefix } => P2sh { hash, prefix },
|
P2sh { hash, network } => P2sh { hash, network },
|
||||||
Segwit { program, hrp } => Segwit { program, hrp },
|
Segwit { program, hrp } => Segwit { program, hrp },
|
||||||
};
|
};
|
||||||
Address(inner, PhantomData)
|
Address(inner, PhantomData)
|
||||||
|
@ -801,19 +756,19 @@ impl FromStr for Address<NetworkUnchecked> {
|
||||||
let inner = match *prefix {
|
let inner = match *prefix {
|
||||||
PUBKEY_ADDRESS_PREFIX_MAIN => {
|
PUBKEY_ADDRESS_PREFIX_MAIN => {
|
||||||
let hash = PubkeyHash::from_byte_array(data);
|
let hash = PubkeyHash::from_byte_array(data);
|
||||||
AddressInner::P2pkh { hash, prefix: LegacyP2pkhPrefix::Mainnet }
|
AddressInner::P2pkh { hash, network: NetworkKind::Main }
|
||||||
}
|
}
|
||||||
PUBKEY_ADDRESS_PREFIX_TEST => {
|
PUBKEY_ADDRESS_PREFIX_TEST => {
|
||||||
let hash = PubkeyHash::from_byte_array(data);
|
let hash = PubkeyHash::from_byte_array(data);
|
||||||
AddressInner::P2pkh { hash, prefix: LegacyP2pkhPrefix::AllTestnets }
|
AddressInner::P2pkh { hash, network: NetworkKind::Test }
|
||||||
}
|
}
|
||||||
SCRIPT_ADDRESS_PREFIX_MAIN => {
|
SCRIPT_ADDRESS_PREFIX_MAIN => {
|
||||||
let hash = ScriptHash::from_byte_array(data);
|
let hash = ScriptHash::from_byte_array(data);
|
||||||
AddressInner::P2sh { hash, prefix: LegacyP2shPrefix::Mainnet }
|
AddressInner::P2sh { hash, network: NetworkKind::Main }
|
||||||
}
|
}
|
||||||
SCRIPT_ADDRESS_PREFIX_TEST => {
|
SCRIPT_ADDRESS_PREFIX_TEST => {
|
||||||
let hash = ScriptHash::from_byte_array(data);
|
let hash = ScriptHash::from_byte_array(data);
|
||||||
AddressInner::P2sh { hash, prefix: LegacyP2shPrefix::AllTestnets }
|
AddressInner::P2sh { hash, network: NetworkKind::Test }
|
||||||
}
|
}
|
||||||
x => return Err(ParseError::Base58(base58::Error::InvalidAddressVersion(x))),
|
x => return Err(ParseError::Base58(base58::Error::InvalidAddressVersion(x))),
|
||||||
};
|
};
|
||||||
|
@ -868,7 +823,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_p2pkh_address_58() {
|
fn test_p2pkh_address_58() {
|
||||||
let hash = "162c5ea71c0b23f5b9022ef047c4a86470a5b070".parse::<PubkeyHash>().unwrap();
|
let hash = "162c5ea71c0b23f5b9022ef047c4a86470a5b070".parse::<PubkeyHash>().unwrap();
|
||||||
let addr = Address::p2pkh(hash, Bitcoin);
|
let addr = Address::p2pkh(hash, NetworkKind::Main);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
addr.script_pubkey(),
|
addr.script_pubkey(),
|
||||||
|
@ -882,13 +837,13 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_p2pkh_from_key() {
|
fn test_p2pkh_from_key() {
|
||||||
let key = "048d5141948c1702e8c95f438815794b87f706a8d4cd2bffad1dc1570971032c9b6042a0431ded2478b5c9cf2d81c124a5e57347a3c63ef0e7716cf54d613ba183".parse::<PublicKey>().unwrap();
|
let key = "048d5141948c1702e8c95f438815794b87f706a8d4cd2bffad1dc1570971032c9b6042a0431ded2478b5c9cf2d81c124a5e57347a3c63ef0e7716cf54d613ba183".parse::<PublicKey>().unwrap();
|
||||||
let addr = Address::p2pkh(key, Bitcoin);
|
let addr = Address::p2pkh(key, NetworkKind::Main);
|
||||||
assert_eq!(&addr.to_string(), "1QJVDzdqb1VpbDK7uDeyVXy9mR27CJiyhY");
|
assert_eq!(&addr.to_string(), "1QJVDzdqb1VpbDK7uDeyVXy9mR27CJiyhY");
|
||||||
|
|
||||||
let key = "03df154ebfcf29d29cc10d5c2565018bce2d9edbab267c31d2caf44a63056cf99f"
|
let key = "03df154ebfcf29d29cc10d5c2565018bce2d9edbab267c31d2caf44a63056cf99f"
|
||||||
.parse::<PublicKey>()
|
.parse::<PublicKey>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let addr = Address::p2pkh(key, Testnet);
|
let addr = Address::p2pkh(key, NetworkKind::Test);
|
||||||
assert_eq!(&addr.to_string(), "mqkhEMH6NCeYjFybv7pvFC22MFeaNT9AQC");
|
assert_eq!(&addr.to_string(), "mqkhEMH6NCeYjFybv7pvFC22MFeaNT9AQC");
|
||||||
assert_eq!(addr.address_type(), Some(AddressType::P2pkh));
|
assert_eq!(addr.address_type(), Some(AddressType::P2pkh));
|
||||||
roundtrips(&addr, Testnet);
|
roundtrips(&addr, Testnet);
|
||||||
|
@ -897,7 +852,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_p2sh_address_58() {
|
fn test_p2sh_address_58() {
|
||||||
let hash = "162c5ea71c0b23f5b9022ef047c4a86470a5b070".parse::<ScriptHash>().unwrap();
|
let hash = "162c5ea71c0b23f5b9022ef047c4a86470a5b070".parse::<ScriptHash>().unwrap();
|
||||||
let addr = Address::p2sh_from_hash(hash, Bitcoin);
|
let addr = Address::p2sh_from_hash(hash, NetworkKind::Main);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
addr.script_pubkey(),
|
addr.script_pubkey(),
|
||||||
|
@ -911,7 +866,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_p2sh_parse() {
|
fn test_p2sh_parse() {
|
||||||
let script = ScriptBuf::from_hex("552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae").unwrap();
|
let script = ScriptBuf::from_hex("552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae").unwrap();
|
||||||
let addr = Address::p2sh(&script, Testnet).unwrap();
|
let addr = Address::p2sh(&script, NetworkKind::Test).unwrap();
|
||||||
assert_eq!(&addr.to_string(), "2N3zXjbwdTcPsJiy8sUK9FhWJhqQCxA8Jjr");
|
assert_eq!(&addr.to_string(), "2N3zXjbwdTcPsJiy8sUK9FhWJhqQCxA8Jjr");
|
||||||
assert_eq!(addr.address_type(), Some(AddressType::P2sh));
|
assert_eq!(addr.address_type(), Some(AddressType::P2sh));
|
||||||
roundtrips(&addr, Testnet);
|
roundtrips(&addr, Testnet);
|
||||||
|
@ -920,7 +875,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_p2sh_parse_for_large_script() {
|
fn test_p2sh_parse_for_large_script() {
|
||||||
let script = ScriptBuf::from_hex("552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae12123122313123123ac1231231231231313123131231231231313212313213123123552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae12123122313123123ac1231231231231313123131231231231313212313213123123552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae12123122313123123ac1231231231231313123131231231231313212313213123123").unwrap();
|
let script = ScriptBuf::from_hex("552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae12123122313123123ac1231231231231313123131231231231313212313213123123552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae12123122313123123ac1231231231231313123131231231231313212313213123123552103a765fc35b3f210b95223846b36ef62a4e53e34e2925270c2c7906b92c9f718eb2103c327511374246759ec8d0b89fa6c6b23b33e11f92c5bc155409d86de0c79180121038cae7406af1f12f4786d820a1466eec7bc5785a1b5e4a387eca6d797753ef6db2103252bfb9dcaab0cd00353f2ac328954d791270203d66c2be8b430f115f451b8a12103e79412d42372c55dd336f2eb6eb639ef9d74a22041ba79382c74da2338fe58ad21035049459a4ebc00e876a9eef02e72a3e70202d3d1f591fc0dd542f93f642021f82102016f682920d9723c61b27f562eb530c926c00106004798b6471e8c52c60ee02057ae12123122313123123ac1231231231231313123131231231231313212313213123123").unwrap();
|
||||||
assert_eq!(Address::p2sh(&script, Testnet), Err(Error::ExcessiveScriptSize));
|
assert_eq!(Address::p2sh(&script, NetworkKind::Test), Err(Error::ExcessiveScriptSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -954,7 +909,7 @@ mod tests {
|
||||||
let key = "026c468be64d22761c30cd2f12cbc7de255d592d7904b1bab07236897cc4c2e766"
|
let key = "026c468be64d22761c30cd2f12cbc7de255d592d7904b1bab07236897cc4c2e766"
|
||||||
.parse::<CompressedPublicKey>()
|
.parse::<CompressedPublicKey>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let addr = Address::p2shwpkh(&key, Bitcoin);
|
let addr = Address::p2shwpkh(&key, NetworkKind::Main);
|
||||||
assert_eq!(&addr.to_string(), "3QBRmWNqqBGme9er7fMkGqtZtp4gjMFxhE");
|
assert_eq!(&addr.to_string(), "3QBRmWNqqBGme9er7fMkGqtZtp4gjMFxhE");
|
||||||
assert_eq!(addr.address_type(), Some(AddressType::P2sh));
|
assert_eq!(addr.address_type(), Some(AddressType::P2sh));
|
||||||
roundtrips(&addr, Bitcoin);
|
roundtrips(&addr, Bitcoin);
|
||||||
|
@ -964,7 +919,7 @@ mod tests {
|
||||||
fn test_p2shwsh() {
|
fn test_p2shwsh() {
|
||||||
// stolen from Bitcoin transaction f9ee2be4df05041d0e0a35d7caa3157495ca4f93b233234c9967b6901dacf7a9
|
// stolen from Bitcoin transaction f9ee2be4df05041d0e0a35d7caa3157495ca4f93b233234c9967b6901dacf7a9
|
||||||
let script = ScriptBuf::from_hex("522103e5529d8eaa3d559903adb2e881eb06c86ac2574ffa503c45f4e942e2a693b33e2102e5f10fcdcdbab211e0af6a481f5532536ec61a5fdbf7183770cf8680fe729d8152ae").unwrap();
|
let script = ScriptBuf::from_hex("522103e5529d8eaa3d559903adb2e881eb06c86ac2574ffa503c45f4e942e2a693b33e2102e5f10fcdcdbab211e0af6a481f5532536ec61a5fdbf7183770cf8680fe729d8152ae").unwrap();
|
||||||
let addr = Address::p2shwsh(&script, Bitcoin);
|
let addr = Address::p2shwsh(&script, NetworkKind::Main);
|
||||||
assert_eq!(&addr.to_string(), "36EqgNnsWW94SreZgBWc1ANC6wpFZwirHr");
|
assert_eq!(&addr.to_string(), "36EqgNnsWW94SreZgBWc1ANC6wpFZwirHr");
|
||||||
assert_eq!(addr.address_type(), Some(AddressType::P2sh));
|
assert_eq!(addr.address_type(), Some(AddressType::P2sh));
|
||||||
roundtrips(&addr, Bitcoin);
|
roundtrips(&addr, Bitcoin);
|
||||||
|
|
|
@ -21,7 +21,7 @@ use serde;
|
||||||
use crate::base58;
|
use crate::base58;
|
||||||
use crate::crypto::key::{self, Keypair, PrivateKey, CompressedPublicKey};
|
use crate::crypto::key::{self, Keypair, PrivateKey, CompressedPublicKey};
|
||||||
use crate::internal_macros::impl_bytes_newtype;
|
use crate::internal_macros::impl_bytes_newtype;
|
||||||
use crate::network::Network;
|
use crate::network::NetworkKind;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
/// Version bytes for extended public keys on the Bitcoin network.
|
/// Version bytes for extended public keys on the Bitcoin network.
|
||||||
|
@ -69,7 +69,7 @@ hash_newtype! {
|
||||||
#[cfg_attr(feature = "std", derive(Debug))]
|
#[cfg_attr(feature = "std", derive(Debug))]
|
||||||
pub struct Xpriv {
|
pub struct Xpriv {
|
||||||
/// The network this key is to be used on
|
/// The network this key is to be used on
|
||||||
pub network: Network,
|
pub network: NetworkKind,
|
||||||
/// How many derivations this key is from the master (which is 0)
|
/// How many derivations this key is from the master (which is 0)
|
||||||
pub depth: u8,
|
pub depth: u8,
|
||||||
/// Fingerprint of the parent key (0 for master)
|
/// Fingerprint of the parent key (0 for master)
|
||||||
|
@ -101,8 +101,8 @@ impl fmt::Debug for Xpriv {
|
||||||
/// Extended public key
|
/// Extended public key
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)]
|
||||||
pub struct Xpub {
|
pub struct Xpub {
|
||||||
/// The network this key is to be used on
|
/// The network kind this key is to be used on
|
||||||
pub network: Network,
|
pub network: NetworkKind,
|
||||||
/// How many derivations this key is from the master (which is 0)
|
/// How many derivations this key is from the master (which is 0)
|
||||||
pub depth: u8,
|
pub depth: u8,
|
||||||
/// Fingerprint of the parent key
|
/// Fingerprint of the parent key
|
||||||
|
@ -558,13 +558,13 @@ impl From<base58::Error> for Error {
|
||||||
|
|
||||||
impl Xpriv {
|
impl Xpriv {
|
||||||
/// Construct a new master key from a seed value
|
/// Construct a new master key from a seed value
|
||||||
pub fn new_master(network: Network, seed: &[u8]) -> Result<Xpriv, Error> {
|
pub fn new_master(network: impl Into<NetworkKind>, seed: &[u8]) -> Result<Xpriv, Error> {
|
||||||
let mut hmac_engine: HmacEngine<sha512::Hash> = HmacEngine::new(b"Bitcoin seed");
|
let mut hmac_engine: HmacEngine<sha512::Hash> = HmacEngine::new(b"Bitcoin seed");
|
||||||
hmac_engine.input(seed);
|
hmac_engine.input(seed);
|
||||||
let hmac_result: Hmac<sha512::Hash> = Hmac::from_engine(hmac_engine);
|
let hmac_result: Hmac<sha512::Hash> = Hmac::from_engine(hmac_engine);
|
||||||
|
|
||||||
Ok(Xpriv {
|
Ok(Xpriv {
|
||||||
network,
|
network: network.into(),
|
||||||
depth: 0,
|
depth: 0,
|
||||||
parent_fingerprint: Default::default(),
|
parent_fingerprint: Default::default(),
|
||||||
child_number: ChildNumber::from_normal_idx(0)?,
|
child_number: ChildNumber::from_normal_idx(0)?,
|
||||||
|
@ -645,9 +645,9 @@ impl Xpriv {
|
||||||
}
|
}
|
||||||
|
|
||||||
let network = if data.starts_with(&VERSION_BYTES_MAINNET_PRIVATE) {
|
let network = if data.starts_with(&VERSION_BYTES_MAINNET_PRIVATE) {
|
||||||
Network::Bitcoin
|
NetworkKind::Main
|
||||||
} else if data.starts_with(&VERSION_BYTES_TESTNETS_PRIVATE) {
|
} else if data.starts_with(&VERSION_BYTES_TESTNETS_PRIVATE) {
|
||||||
Network::Testnet
|
NetworkKind::Test
|
||||||
} else {
|
} else {
|
||||||
let (b0, b1, b2, b3) = (data[0], data[1], data[2], data[3]);
|
let (b0, b1, b2, b3) = (data[0], data[1], data[2], data[3]);
|
||||||
return Err(Error::UnknownVersion([b0, b1, b2, b3]));
|
return Err(Error::UnknownVersion([b0, b1, b2, b3]));
|
||||||
|
@ -671,8 +671,8 @@ impl Xpriv {
|
||||||
pub fn encode(&self) -> [u8; 78] {
|
pub fn encode(&self) -> [u8; 78] {
|
||||||
let mut ret = [0; 78];
|
let mut ret = [0; 78];
|
||||||
ret[0..4].copy_from_slice(&match self.network {
|
ret[0..4].copy_from_slice(&match self.network {
|
||||||
Network::Bitcoin => VERSION_BYTES_MAINNET_PRIVATE,
|
NetworkKind::Main => VERSION_BYTES_MAINNET_PRIVATE,
|
||||||
Network::Testnet | Network::Signet | Network::Regtest => VERSION_BYTES_TESTNETS_PRIVATE,
|
NetworkKind::Test => VERSION_BYTES_TESTNETS_PRIVATE,
|
||||||
});
|
});
|
||||||
ret[4] = self.depth;
|
ret[4] = self.depth;
|
||||||
ret[5..9].copy_from_slice(&self.parent_fingerprint[..]);
|
ret[5..9].copy_from_slice(&self.parent_fingerprint[..]);
|
||||||
|
@ -777,9 +777,9 @@ impl Xpub {
|
||||||
}
|
}
|
||||||
|
|
||||||
let network = if data.starts_with(&VERSION_BYTES_MAINNET_PUBLIC) {
|
let network = if data.starts_with(&VERSION_BYTES_MAINNET_PUBLIC) {
|
||||||
Network::Bitcoin
|
NetworkKind::Main
|
||||||
} else if data.starts_with(&VERSION_BYTES_TESTNETS_PUBLIC) {
|
} else if data.starts_with(&VERSION_BYTES_TESTNETS_PUBLIC) {
|
||||||
Network::Testnet
|
NetworkKind::Test
|
||||||
} else {
|
} else {
|
||||||
let (b0, b1, b2, b3) = (data[0], data[1], data[2], data[3]);
|
let (b0, b1, b2, b3) = (data[0], data[1], data[2], data[3]);
|
||||||
return Err(Error::UnknownVersion([b0, b1, b2, b3]));
|
return Err(Error::UnknownVersion([b0, b1, b2, b3]));
|
||||||
|
@ -803,8 +803,8 @@ impl Xpub {
|
||||||
pub fn encode(&self) -> [u8; 78] {
|
pub fn encode(&self) -> [u8; 78] {
|
||||||
let mut ret = [0; 78];
|
let mut ret = [0; 78];
|
||||||
ret[0..4].copy_from_slice(&match self.network {
|
ret[0..4].copy_from_slice(&match self.network {
|
||||||
Network::Bitcoin => VERSION_BYTES_MAINNET_PUBLIC,
|
NetworkKind::Main => VERSION_BYTES_MAINNET_PUBLIC,
|
||||||
Network::Testnet | Network::Signet | Network::Regtest => VERSION_BYTES_TESTNETS_PUBLIC,
|
NetworkKind::Test => VERSION_BYTES_TESTNETS_PUBLIC,
|
||||||
});
|
});
|
||||||
ret[4] = self.depth;
|
ret[4] = self.depth;
|
||||||
ret[5..9].copy_from_slice(&self.parent_fingerprint[..]);
|
ret[5..9].copy_from_slice(&self.parent_fingerprint[..]);
|
||||||
|
@ -884,7 +884,6 @@ mod tests {
|
||||||
|
|
||||||
use super::ChildNumber::{Hardened, Normal};
|
use super::ChildNumber::{Hardened, Normal};
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::network::Network::{self, Bitcoin};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_derivation_path() {
|
fn test_parse_derivation_path() {
|
||||||
|
@ -965,7 +964,7 @@ mod tests {
|
||||||
|
|
||||||
fn test_path<C: secp256k1::Signing + secp256k1::Verification>(
|
fn test_path<C: secp256k1::Signing + secp256k1::Verification>(
|
||||||
secp: &Secp256k1<C>,
|
secp: &Secp256k1<C>,
|
||||||
network: Network,
|
network: NetworkKind,
|
||||||
seed: &[u8],
|
seed: &[u8],
|
||||||
path: DerivationPath,
|
path: DerivationPath,
|
||||||
expected_sk: &str,
|
expected_sk: &str,
|
||||||
|
@ -1060,32 +1059,32 @@ mod tests {
|
||||||
let seed = hex!("000102030405060708090a0b0c0d0e0f");
|
let seed = hex!("000102030405060708090a0b0c0d0e0f");
|
||||||
|
|
||||||
// m
|
// m
|
||||||
test_path(&secp, Bitcoin, &seed, "m".parse().unwrap(),
|
test_path(&secp, NetworkKind::Main, &seed, "m".parse().unwrap(),
|
||||||
"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
|
"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi",
|
||||||
"xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8");
|
"xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8");
|
||||||
|
|
||||||
// m/0h
|
// m/0h
|
||||||
test_path(&secp, Bitcoin, &seed, "m/0h".parse().unwrap(),
|
test_path(&secp, NetworkKind::Main, &seed, "m/0h".parse().unwrap(),
|
||||||
"xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7",
|
"xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7",
|
||||||
"xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw");
|
"xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw");
|
||||||
|
|
||||||
// m/0h/1
|
// m/0h/1
|
||||||
test_path(&secp, Bitcoin, &seed, "m/0h/1".parse().unwrap(),
|
test_path(&secp, NetworkKind::Main, &seed, "m/0h/1".parse().unwrap(),
|
||||||
"xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs",
|
"xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs",
|
||||||
"xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ");
|
"xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ");
|
||||||
|
|
||||||
// m/0h/1/2h
|
// m/0h/1/2h
|
||||||
test_path(&secp, Bitcoin, &seed, "m/0h/1/2h".parse().unwrap(),
|
test_path(&secp, NetworkKind::Main, &seed, "m/0h/1/2h".parse().unwrap(),
|
||||||
"xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM",
|
"xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM",
|
||||||
"xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5");
|
"xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5");
|
||||||
|
|
||||||
// m/0h/1/2h/2
|
// m/0h/1/2h/2
|
||||||
test_path(&secp, Bitcoin, &seed, "m/0h/1/2h/2".parse().unwrap(),
|
test_path(&secp, NetworkKind::Main, &seed, "m/0h/1/2h/2".parse().unwrap(),
|
||||||
"xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334",
|
"xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334",
|
||||||
"xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV");
|
"xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV");
|
||||||
|
|
||||||
// m/0h/1/2h/2/1000000000
|
// m/0h/1/2h/2/1000000000
|
||||||
test_path(&secp, Bitcoin, &seed, "m/0h/1/2h/2/1000000000".parse().unwrap(),
|
test_path(&secp, NetworkKind::Main, &seed, "m/0h/1/2h/2/1000000000".parse().unwrap(),
|
||||||
"xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76",
|
"xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76",
|
||||||
"xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy");
|
"xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy");
|
||||||
}
|
}
|
||||||
|
@ -1096,32 +1095,32 @@ mod tests {
|
||||||
let seed = hex!("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542");
|
let seed = hex!("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542");
|
||||||
|
|
||||||
// m
|
// m
|
||||||
test_path(&secp, Bitcoin, &seed, "m".parse().unwrap(),
|
test_path(&secp, NetworkKind::Main, &seed, "m".parse().unwrap(),
|
||||||
"xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U",
|
"xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U",
|
||||||
"xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB");
|
"xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB");
|
||||||
|
|
||||||
// m/0
|
// m/0
|
||||||
test_path(&secp, Bitcoin, &seed, "m/0".parse().unwrap(),
|
test_path(&secp, NetworkKind::Main, &seed, "m/0".parse().unwrap(),
|
||||||
"xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt",
|
"xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt",
|
||||||
"xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH");
|
"xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH");
|
||||||
|
|
||||||
// m/0/2147483647h
|
// m/0/2147483647h
|
||||||
test_path(&secp, Bitcoin, &seed, "m/0/2147483647h".parse().unwrap(),
|
test_path(&secp, NetworkKind::Main, &seed, "m/0/2147483647h".parse().unwrap(),
|
||||||
"xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9",
|
"xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9",
|
||||||
"xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a");
|
"xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a");
|
||||||
|
|
||||||
// m/0/2147483647h/1
|
// m/0/2147483647h/1
|
||||||
test_path(&secp, Bitcoin, &seed, "m/0/2147483647h/1".parse().unwrap(),
|
test_path(&secp, NetworkKind::Main, &seed, "m/0/2147483647h/1".parse().unwrap(),
|
||||||
"xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef",
|
"xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef",
|
||||||
"xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon");
|
"xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon");
|
||||||
|
|
||||||
// m/0/2147483647h/1/2147483646h
|
// m/0/2147483647h/1/2147483646h
|
||||||
test_path(&secp, Bitcoin, &seed, "m/0/2147483647h/1/2147483646h".parse().unwrap(),
|
test_path(&secp, NetworkKind::Main, &seed, "m/0/2147483647h/1/2147483646h".parse().unwrap(),
|
||||||
"xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc",
|
"xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc",
|
||||||
"xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL");
|
"xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL");
|
||||||
|
|
||||||
// m/0/2147483647h/1/2147483646h/2
|
// m/0/2147483647h/1/2147483646h/2
|
||||||
test_path(&secp, Bitcoin, &seed, "m/0/2147483647h/1/2147483646h/2".parse().unwrap(),
|
test_path(&secp, NetworkKind::Main, &seed, "m/0/2147483647h/1/2147483646h/2".parse().unwrap(),
|
||||||
"xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j",
|
"xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j",
|
||||||
"xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt");
|
"xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt");
|
||||||
}
|
}
|
||||||
|
@ -1132,12 +1131,12 @@ mod tests {
|
||||||
let seed = hex!("4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be");
|
let seed = hex!("4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be");
|
||||||
|
|
||||||
// m
|
// m
|
||||||
test_path(&secp, Bitcoin, &seed, "m".parse().unwrap(),
|
test_path(&secp, NetworkKind::Main, &seed, "m".parse().unwrap(),
|
||||||
"xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6",
|
"xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6",
|
||||||
"xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13");
|
"xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13");
|
||||||
|
|
||||||
// m/0h
|
// m/0h
|
||||||
test_path(&secp, Bitcoin, &seed, "m/0h".parse().unwrap(),
|
test_path(&secp, NetworkKind::Main, &seed, "m/0h".parse().unwrap(),
|
||||||
"xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L",
|
"xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L",
|
||||||
"xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y");
|
"xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y");
|
||||||
}
|
}
|
||||||
|
@ -1200,7 +1199,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
let xpriv = Xpriv {
|
let xpriv = Xpriv {
|
||||||
network: Network::Bitcoin,
|
network: NetworkKind::Main,
|
||||||
depth: 0,
|
depth: 0,
|
||||||
parent_fingerprint: Default::default(),
|
parent_fingerprint: Default::default(),
|
||||||
child_number: ChildNumber::Normal { index: 0 },
|
child_number: ChildNumber::Normal { index: 0 },
|
||||||
|
|
|
@ -16,7 +16,7 @@ use io::{Read, Write};
|
||||||
|
|
||||||
use crate::crypto::ecdsa;
|
use crate::crypto::ecdsa;
|
||||||
use crate::internal_macros::impl_asref_push_bytes;
|
use crate::internal_macros::impl_asref_push_bytes;
|
||||||
use crate::network::Network;
|
use crate::network::NetworkKind;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::taproot::{TapNodeHash, TapTweakHash};
|
use crate::taproot::{TapNodeHash, TapTweakHash};
|
||||||
use crate::{base58, io};
|
use crate::{base58, io};
|
||||||
|
@ -390,8 +390,8 @@ impl From<&CompressedPublicKey> for WPubkeyHash {
|
||||||
pub struct PrivateKey {
|
pub struct PrivateKey {
|
||||||
/// Whether this private key should be serialized as compressed
|
/// Whether this private key should be serialized as compressed
|
||||||
pub compressed: bool,
|
pub compressed: bool,
|
||||||
/// The network on which this key should be used
|
/// The network kind on which this key should be used
|
||||||
pub network: Network,
|
pub network: NetworkKind,
|
||||||
/// The actual ECDSA key
|
/// The actual ECDSA key
|
||||||
pub inner: secp256k1::SecretKey,
|
pub inner: secp256k1::SecretKey,
|
||||||
}
|
}
|
||||||
|
@ -400,20 +400,23 @@ impl PrivateKey {
|
||||||
/// Constructs new compressed ECDSA private key using the secp256k1 algorithm and
|
/// Constructs new compressed ECDSA private key using the secp256k1 algorithm and
|
||||||
/// a secure random number generator.
|
/// a secure random number generator.
|
||||||
#[cfg(feature = "rand-std")]
|
#[cfg(feature = "rand-std")]
|
||||||
pub fn generate(network: Network) -> PrivateKey {
|
pub fn generate(network: impl Into<NetworkKind>) -> PrivateKey {
|
||||||
let secret_key = secp256k1::SecretKey::new(&mut rand::thread_rng());
|
let secret_key = secp256k1::SecretKey::new(&mut rand::thread_rng());
|
||||||
PrivateKey::new(secret_key, network)
|
PrivateKey::new(secret_key, network.into())
|
||||||
}
|
}
|
||||||
/// Constructs compressed ECDSA private key from the provided generic Secp256k1 private key
|
/// Constructs compressed ECDSA private key from the provided generic Secp256k1 private key
|
||||||
/// and the specified network
|
/// and the specified network
|
||||||
pub fn new(key: secp256k1::SecretKey, network: Network) -> PrivateKey {
|
pub fn new(key: secp256k1::SecretKey, network: impl Into<NetworkKind>) -> PrivateKey {
|
||||||
PrivateKey { compressed: true, network, inner: key }
|
PrivateKey { compressed: true, network: network.into(), inner: key }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs uncompressed (legacy) ECDSA private key from the provided generic Secp256k1
|
/// Constructs uncompressed (legacy) ECDSA private key from the provided generic Secp256k1
|
||||||
/// private key and the specified network
|
/// private key and the specified network
|
||||||
pub fn new_uncompressed(key: secp256k1::SecretKey, network: Network) -> PrivateKey {
|
pub fn new_uncompressed(
|
||||||
PrivateKey { compressed: false, network, inner: key }
|
key: secp256k1::SecretKey,
|
||||||
|
network: impl Into<NetworkKind>,
|
||||||
|
) -> PrivateKey {
|
||||||
|
PrivateKey { compressed: false, network: network.into(), inner: key }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a public key from this private key
|
/// Creates a public key from this private key
|
||||||
|
@ -428,17 +431,16 @@ impl PrivateKey {
|
||||||
pub fn to_bytes(self) -> Vec<u8> { self.inner[..].to_vec() }
|
pub fn to_bytes(self) -> Vec<u8> { self.inner[..].to_vec() }
|
||||||
|
|
||||||
/// Deserialize a private key from a slice
|
/// Deserialize a private key from a slice
|
||||||
pub fn from_slice(data: &[u8], network: Network) -> Result<PrivateKey, Error> {
|
pub fn from_slice(data: &[u8], network: impl Into<NetworkKind>) -> Result<PrivateKey, Error> {
|
||||||
Ok(PrivateKey::new(secp256k1::SecretKey::from_slice(data)?, network))
|
Ok(PrivateKey::new(secp256k1::SecretKey::from_slice(data)?, network))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Format the private key to WIF format.
|
/// Format the private key to WIF format.
|
||||||
|
#[rustfmt::skip]
|
||||||
pub fn fmt_wif(&self, fmt: &mut dyn fmt::Write) -> fmt::Result {
|
pub fn fmt_wif(&self, fmt: &mut dyn fmt::Write) -> fmt::Result {
|
||||||
let mut ret = [0; 34];
|
let mut ret = [0; 34];
|
||||||
ret[0] = match self.network {
|
ret[0] = if self.network.is_mainnet() { 128 } else { 239 };
|
||||||
Network::Bitcoin => 128,
|
|
||||||
Network::Testnet | Network::Signet | Network::Regtest => 239,
|
|
||||||
};
|
|
||||||
ret[1..33].copy_from_slice(&self.inner[..]);
|
ret[1..33].copy_from_slice(&self.inner[..]);
|
||||||
let privkey = if self.compressed {
|
let privkey = if self.compressed {
|
||||||
ret[33] = 1;
|
ret[33] = 1;
|
||||||
|
@ -470,8 +472,8 @@ impl PrivateKey {
|
||||||
};
|
};
|
||||||
|
|
||||||
let network = match data[0] {
|
let network = match data[0] {
|
||||||
128 => Network::Bitcoin,
|
128 => NetworkKind::Main,
|
||||||
239 => Network::Testnet,
|
239 => NetworkKind::Test,
|
||||||
x => {
|
x => {
|
||||||
return Err(Error::Base58(base58::Error::InvalidAddressVersion(x)));
|
return Err(Error::Base58(base58::Error::InvalidAddressVersion(x)));
|
||||||
}
|
}
|
||||||
|
@ -953,14 +955,14 @@ mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::address::Address;
|
use crate::address::Address;
|
||||||
use crate::network::Network::{Bitcoin, Testnet};
|
use crate::network::NetworkKind;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_key_derivation() {
|
fn test_key_derivation() {
|
||||||
// testnet compressed
|
// testnet compressed
|
||||||
let sk =
|
let sk =
|
||||||
PrivateKey::from_wif("cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy").unwrap();
|
PrivateKey::from_wif("cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy").unwrap();
|
||||||
assert_eq!(sk.network, Testnet);
|
assert_eq!(sk.network, NetworkKind::Test);
|
||||||
assert!(sk.compressed);
|
assert!(sk.compressed);
|
||||||
assert_eq!(&sk.to_wif(), "cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy");
|
assert_eq!(&sk.to_wif(), "cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy");
|
||||||
|
|
||||||
|
@ -977,7 +979,7 @@ mod tests {
|
||||||
// mainnet uncompressed
|
// mainnet uncompressed
|
||||||
let sk =
|
let sk =
|
||||||
PrivateKey::from_wif("5JYkZjmN7PVMjJUfJWfRFwtuXTGB439XV6faajeHPAM9Z2PT2R3").unwrap();
|
PrivateKey::from_wif("5JYkZjmN7PVMjJUfJWfRFwtuXTGB439XV6faajeHPAM9Z2PT2R3").unwrap();
|
||||||
assert_eq!(sk.network, Bitcoin);
|
assert_eq!(sk.network, NetworkKind::Main);
|
||||||
assert!(!sk.compressed);
|
assert!(!sk.compressed);
|
||||||
assert_eq!(&sk.to_wif(), "5JYkZjmN7PVMjJUfJWfRFwtuXTGB439XV6faajeHPAM9Z2PT2R3");
|
assert_eq!(&sk.to_wif(), "5JYkZjmN7PVMjJUfJWfRFwtuXTGB439XV6faajeHPAM9Z2PT2R3");
|
||||||
|
|
||||||
|
@ -1295,7 +1297,7 @@ mod tests {
|
||||||
fn private_key_debug_is_obfuscated() {
|
fn private_key_debug_is_obfuscated() {
|
||||||
let sk =
|
let sk =
|
||||||
PrivateKey::from_str("cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy").unwrap();
|
PrivateKey::from_str("cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy").unwrap();
|
||||||
let want = "PrivateKey { compressed: true, network: Testnet, inner: SecretKey(#32014e414fdce702) }";
|
let want = "PrivateKey { compressed: true, network: Test, inner: SecretKey(#32014e414fdce702) }";
|
||||||
let got = format!("{:?}", sk);
|
let got = format!("{:?}", sk);
|
||||||
assert_eq!(got, want)
|
assert_eq!(got, want)
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,7 +129,7 @@ pub use crate::{
|
||||||
crypto::key::{self, PrivateKey, PubkeyHash, PublicKey, CompressedPublicKey, WPubkeyHash, XOnlyPublicKey},
|
crypto::key::{self, PrivateKey, PubkeyHash, PublicKey, CompressedPublicKey, WPubkeyHash, XOnlyPublicKey},
|
||||||
crypto::sighash::{self, LegacySighash, SegwitV0Sighash, TapSighash, TapSighashTag},
|
crypto::sighash::{self, LegacySighash, SegwitV0Sighash, TapSighash, TapSighashTag},
|
||||||
merkle_tree::MerkleBlock,
|
merkle_tree::MerkleBlock,
|
||||||
network::Network,
|
network::{Network, NetworkKind},
|
||||||
pow::{CompactTarget, Target, Work},
|
pow::{CompactTarget, Target, Work},
|
||||||
psbt::Psbt,
|
psbt::Psbt,
|
||||||
sighash::{EcdsaSighashType, TapSighashType},
|
sighash::{EcdsaSighashType, TapSighashType},
|
||||||
|
|
|
@ -31,6 +31,33 @@ use crate::constants::ChainHash;
|
||||||
use crate::p2p::Magic;
|
use crate::p2p::Magic;
|
||||||
use crate::prelude::{String, ToOwned};
|
use crate::prelude::{String, ToOwned};
|
||||||
|
|
||||||
|
/// What kind of network we are on.
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub enum NetworkKind {
|
||||||
|
/// The Bitcoin mainnet network.
|
||||||
|
Main,
|
||||||
|
/// Some kind of testnet network.
|
||||||
|
Test,
|
||||||
|
}
|
||||||
|
|
||||||
|
// We explicitly do not provide `is_testnet`, using `!network.is_mainnet()` is less
|
||||||
|
// ambiguous due to confusion caused by signet/testnet/regtest.
|
||||||
|
impl NetworkKind {
|
||||||
|
/// Returns true if this is real mainnet bitcoin.
|
||||||
|
pub fn is_mainnet(&self) -> bool { *self == NetworkKind::Main }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Network> for NetworkKind {
|
||||||
|
fn from(n: Network) -> Self {
|
||||||
|
use Network::*;
|
||||||
|
|
||||||
|
match n {
|
||||||
|
Bitcoin => NetworkKind::Main,
|
||||||
|
Testnet | Signet | Regtest => NetworkKind::Test,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The cryptocurrency network to act on.
|
/// The cryptocurrency network to act on.
|
||||||
#[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Debug)]
|
#[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Debug)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
|
|
@ -1014,7 +1014,7 @@ mod tests {
|
||||||
use crate::blockdata::script::ScriptBuf;
|
use crate::blockdata::script::ScriptBuf;
|
||||||
use crate::blockdata::transaction::{self, OutPoint, Sequence, Transaction, TxIn, TxOut};
|
use crate::blockdata::transaction::{self, OutPoint, Sequence, Transaction, TxIn, TxOut};
|
||||||
use crate::blockdata::witness::Witness;
|
use crate::blockdata::witness::Witness;
|
||||||
use crate::network::Network::Bitcoin;
|
use crate::network::NetworkKind;
|
||||||
use crate::psbt::map::{Input, Output};
|
use crate::psbt::map::{Input, Output};
|
||||||
use crate::psbt::raw;
|
use crate::psbt::raw;
|
||||||
use crate::psbt::serialize::{Deserialize, Serialize};
|
use crate::psbt::serialize::{Deserialize, Serialize};
|
||||||
|
@ -1151,7 +1151,7 @@ mod tests {
|
||||||
|
|
||||||
let mut hd_keypaths: BTreeMap<secp256k1::PublicKey, KeySource> = Default::default();
|
let mut hd_keypaths: BTreeMap<secp256k1::PublicKey, KeySource> = Default::default();
|
||||||
|
|
||||||
let mut sk: Xpriv = Xpriv::new_master(Bitcoin, &seed).unwrap();
|
let mut sk: Xpriv = Xpriv::new_master(NetworkKind::Main, &seed).unwrap();
|
||||||
|
|
||||||
let fprint = sk.fingerprint(secp);
|
let fprint = sk.fingerprint(secp);
|
||||||
|
|
||||||
|
@ -1926,7 +1926,7 @@ mod tests {
|
||||||
let secp = Secp256k1::new();
|
let secp = Secp256k1::new();
|
||||||
|
|
||||||
let sk = SecretKey::new(&mut thread_rng());
|
let sk = SecretKey::new(&mut thread_rng());
|
||||||
let priv_key = PrivateKey::new(sk, crate::Network::Regtest);
|
let priv_key = PrivateKey::new(sk, NetworkKind::Test);
|
||||||
let pk = PublicKey::from_private_key(&secp, &priv_key);
|
let pk = PublicKey::from_private_key(&secp, &priv_key);
|
||||||
|
|
||||||
(priv_key, pk, secp)
|
(priv_key, pk, secp)
|
||||||
|
|
|
@ -226,7 +226,7 @@ mod tests {
|
||||||
|
|
||||||
use secp256k1;
|
use secp256k1;
|
||||||
|
|
||||||
use crate::{Address, AddressType, Network};
|
use crate::{Address, AddressType, Network, NetworkKind};
|
||||||
|
|
||||||
let secp = secp256k1::Secp256k1::new();
|
let secp = secp256k1::Secp256k1::new();
|
||||||
let message = "rust-bitcoin MessageSignature test";
|
let message = "rust-bitcoin MessageSignature test";
|
||||||
|
@ -243,12 +243,14 @@ mod tests {
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("compressed was set to true");
|
.expect("compressed was set to true");
|
||||||
|
|
||||||
|
let p2pkh = Address::p2pkh(pubkey, NetworkKind::Main);
|
||||||
|
assert_eq!(signature2.is_signed_by_address(&secp, &p2pkh, msg_hash), Ok(true));
|
||||||
let p2wpkh = Address::p2wpkh(&pubkey, Network::Bitcoin);
|
let p2wpkh = Address::p2wpkh(&pubkey, Network::Bitcoin);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
signature2.is_signed_by_address(&secp, &p2wpkh, msg_hash),
|
signature2.is_signed_by_address(&secp, &p2wpkh, msg_hash),
|
||||||
Err(MessageSignatureError::UnsupportedAddressType(AddressType::P2wpkh))
|
Err(MessageSignatureError::UnsupportedAddressType(AddressType::P2wpkh))
|
||||||
);
|
);
|
||||||
let p2shwpkh = Address::p2shwpkh(&pubkey, Network::Bitcoin);
|
let p2shwpkh = Address::p2shwpkh(&pubkey, NetworkKind::Main);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
signature2.is_signed_by_address(&secp, &p2shwpkh, msg_hash),
|
signature2.is_signed_by_address(&secp, &p2shwpkh, msg_hash),
|
||||||
Err(MessageSignatureError::UnsupportedAddressType(AddressType::P2sh))
|
Err(MessageSignatureError::UnsupportedAddressType(AddressType::P2sh))
|
||||||
|
@ -266,7 +268,7 @@ mod tests {
|
||||||
use secp256k1;
|
use secp256k1;
|
||||||
|
|
||||||
use crate::crypto::key::PublicKey;
|
use crate::crypto::key::PublicKey;
|
||||||
use crate::{Address, Network};
|
use crate::{Address, NetworkKind};
|
||||||
|
|
||||||
let secp = secp256k1::Secp256k1::new();
|
let secp = secp256k1::Secp256k1::new();
|
||||||
let message = "a different message from what was signed";
|
let message = "a different message from what was signed";
|
||||||
|
@ -283,7 +285,7 @@ mod tests {
|
||||||
PublicKey::from_slice(&BASE64_STANDARD.decode(pubkey_base64).expect("base64 string"))
|
PublicKey::from_slice(&BASE64_STANDARD.decode(pubkey_base64).expect("base64 string"))
|
||||||
.expect("pubkey slice");
|
.expect("pubkey slice");
|
||||||
|
|
||||||
let p2pkh = Address::p2pkh(pubkey, Network::Bitcoin);
|
let p2pkh = Address::p2pkh(pubkey, NetworkKind::Main);
|
||||||
assert_eq!(signature.is_signed_by_address(&secp, &p2pkh, msg_hash), Ok(false));
|
assert_eq!(signature.is_signed_by_address(&secp, &p2pkh, msg_hash), Ok(false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,12 +13,10 @@ use bitcoin::psbt::{Psbt, PsbtSighashType};
|
||||||
use bitcoin::script::PushBytes;
|
use bitcoin::script::PushBytes;
|
||||||
use bitcoin::secp256k1::{self, Secp256k1};
|
use bitcoin::secp256k1::{self, Secp256k1};
|
||||||
use bitcoin::{
|
use bitcoin::{
|
||||||
absolute, Amount, Denomination, Network, OutPoint, PrivateKey, PublicKey, ScriptBuf, Sequence,
|
absolute, Amount, Denomination, NetworkKind, OutPoint, PrivateKey, PublicKey, ScriptBuf,
|
||||||
Transaction, TxIn, TxOut, Witness,
|
Sequence, Transaction, TxIn, TxOut, Witness,
|
||||||
};
|
};
|
||||||
|
|
||||||
const NETWORK: Network = Network::Testnet;
|
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn hex_psbt(s: &str) -> Psbt {
|
fn hex_psbt(s: &str) -> Psbt {
|
||||||
let v: Vec<u8> = Vec::from_hex(s).expect("valid hex digits");
|
let v: Vec<u8> = Vec::from_hex(s).expect("valid hex digits");
|
||||||
|
@ -124,7 +122,7 @@ fn build_extended_private_key() -> Xpriv {
|
||||||
let xpriv = Xpriv::from_str(extended_private_key).unwrap();
|
let xpriv = Xpriv::from_str(extended_private_key).unwrap();
|
||||||
|
|
||||||
let sk = PrivateKey::from_wif(seed).unwrap();
|
let sk = PrivateKey::from_wif(seed).unwrap();
|
||||||
let seeded = Xpriv::new_master(NETWORK, &sk.inner.secret_bytes()).unwrap();
|
let seeded = Xpriv::new_master(NetworkKind::Test, &sk.inner.secret_bytes()).unwrap();
|
||||||
assert_eq!(xpriv, seeded);
|
assert_eq!(xpriv, seeded);
|
||||||
|
|
||||||
xpriv
|
xpriv
|
||||||
|
|
|
@ -38,7 +38,7 @@ use bitcoin::psbt::{Input, Output, Psbt, PsbtSighashType};
|
||||||
use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
|
use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
|
||||||
use bitcoin::taproot::{self, ControlBlock, LeafVersion, TapTree, TaprootBuilder};
|
use bitcoin::taproot::{self, ControlBlock, LeafVersion, TapTree, TaprootBuilder};
|
||||||
use bitcoin::{
|
use bitcoin::{
|
||||||
ecdsa, transaction, Address, Amount, Block, Network, OutPoint, PrivateKey, PublicKey,
|
ecdsa, transaction, Address, Amount, Block, NetworkKind, OutPoint, PrivateKey, PublicKey,
|
||||||
ScriptBuf, Sequence, Target, Transaction, TxIn, TxOut, Txid, Work,
|
ScriptBuf, Sequence, Target, Transaction, TxIn, TxOut, Txid, Work,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -145,7 +145,7 @@ fn serde_regression_witness() {
|
||||||
fn serde_regression_address() {
|
fn serde_regression_address() {
|
||||||
let s = include_str!("data/serde/public_key_hex");
|
let s = include_str!("data/serde/public_key_hex");
|
||||||
let pk = PublicKey::from_str(s.trim()).unwrap();
|
let pk = PublicKey::from_str(s.trim()).unwrap();
|
||||||
let addr = Address::p2pkh(pk, Network::Bitcoin);
|
let addr = Address::p2pkh(pk, NetworkKind::Main);
|
||||||
|
|
||||||
let got = serialize(&addr).unwrap();
|
let got = serialize(&addr).unwrap();
|
||||||
let want = include_bytes!("data/serde/address_bincode") as &[_];
|
let want = include_bytes!("data/serde/address_bincode") as &[_];
|
||||||
|
|
Loading…
Reference in New Issue