Improve naming of methods on Xpub and Xpriv

This change makes method names on Xpub and Xpriv more consistent and
easier to discover by following two patterns:

  - if the method deals with extended key, it contains 'xpub' or
    'xpriv' in its name
  - if the method deals with non-extended key, it contains
    'public_key' or 'private_key'

One exception is 'ckd_*' methods, which are lower-level and their names
come from BIP32; these keep using 'priv' and 'pub'.
This commit is contained in:
Jiri Jakes 2024-09-14 11:19:34 +08:00
parent 10f19683cb
commit 5a9341bfc5
No known key found for this signature in database
GPG Key ID: 01F5B01A5D686F31
8 changed files with 103 additions and 57 deletions

View File

@ -40,15 +40,15 @@ fn main() {
// derive child xpub
let path = "84h/0h/0h".parse::<DerivationPath>().unwrap();
let child = root.derive_priv(&secp, &path);
let child = root.derive_xpriv(&secp, &path);
println!("Child at {}: {}", path, child);
let xpub = Xpub::from_priv(&secp, &child);
let xpub = Xpub::from_xpriv(&secp, &child);
println!("Public key at {}: {}", path, xpub);
// generate first receiving address at m/0/0
// manually creating indexes this time
let zero = ChildNumber::ZERO_NORMAL;
let public_key = xpub.derive_pub(&secp, &[zero, zero]).unwrap().public_key;
let public_key = xpub.derive_xpub(&secp, &[zero, zero]).unwrap().public_key;
let address = Address::p2wpkh(CompressedPublicKey(public_key), KnownHrp::Mainnet);
println!("First receiving address: {}", address);
}

View File

@ -60,11 +60,11 @@ fn get_external_address_xpriv<C: Signing>(
) -> Xpriv {
let derivation_path =
BIP84_DERIVATION_PATH.into_derivation_path().expect("valid derivation path");
let child_xpriv = master_xpriv.derive_priv(secp, &derivation_path);
let child_xpriv = master_xpriv.derive_xpriv(secp, &derivation_path);
let external_index = ChildNumber::ZERO_NORMAL;
let idx = ChildNumber::from_normal_idx(index).expect("valid index number");
child_xpriv.derive_priv(secp, &[external_index, idx])
child_xpriv.derive_xpriv(secp, &[external_index, idx])
}
// Derive the internal address xpriv.
@ -75,11 +75,11 @@ fn get_internal_address_xpriv<C: Signing>(
) -> Xpriv {
let derivation_path =
BIP84_DERIVATION_PATH.into_derivation_path().expect("valid derivation path");
let child_xpriv = master_xpriv.derive_priv(secp, &derivation_path);
let child_xpriv = master_xpriv.derive_xpriv(secp, &derivation_path);
let internal_index = ChildNumber::ONE_NORMAL;
let idx = ChildNumber::from_normal_idx(index).expect("valid index number");
child_xpriv.derive_priv(secp, &[internal_index, idx])
child_xpriv.derive_xpriv(secp, &[internal_index, idx])
}
// The address to send to.
@ -133,10 +133,10 @@ fn main() {
let xpriv_change = get_internal_address_xpriv(&secp, master_xpriv, 1);
// Get the PKs
let pk_input_1 = Xpub::from_priv(&secp, &xpriv_input_1).to_pub();
let pk_input_2 = Xpub::from_priv(&secp, &xpriv_input_2).to_pub();
let pk_input_1 = Xpub::from_xpriv(&secp, &xpriv_input_1).to_public_key();
let pk_input_2 = Xpub::from_xpriv(&secp, &xpriv_input_2).to_public_key();
let pk_inputs = [pk_input_1, pk_input_2];
let pk_change = Xpub::from_priv(&secp, &xpriv_change).to_pub();
let pk_change = Xpub::from_xpriv(&secp, &xpriv_change).to_public_key();
// Get the Witness Public Key Hashes (WPKHs)
let wpkhs: Vec<WPubkeyHash> = pk_inputs.iter().map(|pk| pk.wpubkey_hash()).collect();

View File

@ -112,17 +112,17 @@ impl ColdStorage {
/// The newly created signer along with the data needed to configure a watch-only wallet.
fn new<C: Signing>(secp: &Secp256k1<C>, xpriv: &str) -> Result<ExportData> {
let master_xpriv = xpriv.parse::<Xpriv>()?;
let master_xpub = Xpub::from_priv(secp, &master_xpriv);
let master_xpub = Xpub::from_xpriv(secp, &master_xpriv);
// Hardened children require secret data to derive.
let path = "84h/0h/0h".into_derivation_path()?;
let account_0_xpriv = master_xpriv.derive_priv(secp, &path);
let account_0_xpub = Xpub::from_priv(secp, &account_0_xpriv);
let account_0_xpriv = master_xpriv.derive_xpriv(secp, &path);
let account_0_xpub = Xpub::from_xpriv(secp, &account_0_xpriv);
let path = INPUT_UTXO_DERIVATION_PATH.into_derivation_path()?;
let input_xpriv = master_xpriv.derive_priv(secp, &path);
let input_xpub = Xpub::from_priv(secp, &input_xpriv);
let input_xpriv = master_xpriv.derive_xpriv(secp, &path);
let input_xpub = Xpub::from_xpriv(secp, &input_xpriv);
let wallet = ColdStorage { master_xpriv, master_xpub };
let fingerprint = wallet.master_fingerprint();
@ -205,7 +205,7 @@ impl WatchOnly {
fn update_psbt(&self, mut psbt: Psbt) -> Result<Psbt> {
let mut input = Input { witness_utxo: Some(previous_output()), ..Default::default() };
let pk = self.input_xpub.to_pub();
let pk = self.input_xpub.to_public_key();
let wpkh = pk.wpubkey_hash();
let redeem_script = ScriptBuf::new_p2wpkh(wpkh);
@ -235,7 +235,7 @@ impl WatchOnly {
let sigs: Vec<_> = psbt.inputs[0].partial_sigs.values().collect();
let mut script_witness: Witness = Witness::new();
script_witness.push(sigs[0].serialize());
script_witness.push(self.input_xpub.to_pub().to_bytes());
script_witness.push(self.input_xpub.to_public_key().to_bytes());
psbt.inputs[0].final_script_witness = Some(script_witness);
@ -258,9 +258,9 @@ impl WatchOnly {
secp: &Secp256k1<C>,
) -> Result<(CompressedPublicKey, Address, DerivationPath)> {
let path = [ChildNumber::ONE_NORMAL, ChildNumber::ZERO_NORMAL];
let derived = self.account_0_xpub.derive_pub(secp, &path)?;
let derived = self.account_0_xpub.derive_xpub(secp, &path)?;
let pk = derived.to_pub();
let pk = derived.to_public_key();
let addr = Address::p2wpkh(pk, NETWORK);
let path = path.into_derivation_path()?;

View File

@ -59,11 +59,11 @@ fn get_external_address_xpriv<C: Signing>(
) -> Xpriv {
let derivation_path =
BIP86_DERIVATION_PATH.into_derivation_path().expect("valid derivation path");
let child_xpriv = master_xpriv.derive_priv(secp, &derivation_path);
let child_xpriv = master_xpriv.derive_xpriv(secp, &derivation_path);
let external_index = ChildNumber::ZERO_NORMAL;
let idx = ChildNumber::from_normal_idx(index).expect("valid index number");
child_xpriv.derive_priv(secp, &[external_index, idx])
child_xpriv.derive_xpriv(secp, &[external_index, idx])
}
// Derive the internal address xpriv.
@ -74,11 +74,11 @@ fn get_internal_address_xpriv<C: Signing>(
) -> Xpriv {
let derivation_path =
BIP86_DERIVATION_PATH.into_derivation_path().expect("valid derivation path");
let child_xpriv = master_xpriv.derive_priv(secp, &derivation_path);
let child_xpriv = master_xpriv.derive_xpriv(secp, &derivation_path);
let internal_index = ChildNumber::ONE_NORMAL;
let idx = ChildNumber::from_normal_idx(index).expect("valid index number");
child_xpriv.derive_priv(secp, &[internal_index, idx])
child_xpriv.derive_xpriv(secp, &[internal_index, idx])
}
// Get the Taproot Key Origin.
@ -143,9 +143,9 @@ fn main() {
let xpriv_change = get_internal_address_xpriv(&secp, master_xpriv, 1);
// Get the PKs
let (pk_input_1, _) = Xpub::from_priv(&secp, &xpriv_input_1).public_key.x_only_public_key();
let (pk_input_2, _) = Xpub::from_priv(&secp, &xpriv_input_2).public_key.x_only_public_key();
let (pk_change, _) = Xpub::from_priv(&secp, &xpriv_change).public_key.x_only_public_key();
let (pk_input_1, _) = Xpub::from_xpriv(&secp, &xpriv_input_1).public_key.x_only_public_key();
let (pk_input_2, _) = Xpub::from_xpriv(&secp, &xpriv_input_2).public_key.x_only_public_key();
let (pk_change, _) = Xpub::from_xpriv(&secp, &xpriv_change).public_key.x_only_public_key();
// Get the Tap Key Origins
// Map of tap root X-only keys to origin info and leaf hashes contained in it.

View File

@ -297,7 +297,7 @@ fn generate_bip86_key_spend_tx(
.get(&input.tap_internal_key.ok_or("internal key missing in PSBT")?)
.ok_or("missing Taproot key origin")?;
let secret_key = master_xpriv.derive_priv(secp, &derivation_path).to_priv().inner;
let secret_key = master_xpriv.derive_xpriv(secp, &derivation_path).to_private_key().inner;
sign_psbt_taproot(
secret_key,
input.tap_internal_key.unwrap(),
@ -392,9 +392,9 @@ impl BenefactorWallet {
// that we use an unhardened path so we can make use of xpubs.
let derivation_path = format!("101/1/0/0/{}", self.next).parse::<DerivationPath>()?;
let internal_keypair =
self.master_xpriv.derive_priv(&self.secp, &derivation_path).to_keypair(&self.secp);
self.master_xpriv.derive_xpriv(&self.secp, &derivation_path).to_keypair(&self.secp);
let beneficiary_key =
self.beneficiary_xpub.derive_pub(&self.secp, &derivation_path)?.to_x_only_pub();
self.beneficiary_xpub.derive_xpub(&self.secp, &derivation_path)?.to_x_only_public_key();
// Build up the leaf script and combine with internal key into a Taproot commitment
let script = Self::time_lock_script(lock_time, beneficiary_key);
@ -482,10 +482,10 @@ impl BenefactorWallet {
format!("101/1/0/0/{}", self.next).parse::<DerivationPath>()?;
let new_internal_keypair = self
.master_xpriv
.derive_priv(&self.secp, &new_derivation_path)
.derive_xpriv(&self.secp, &new_derivation_path)
.to_keypair(&self.secp);
let beneficiary_key =
self.beneficiary_xpub.derive_pub(&self.secp, &new_derivation_path)?.to_x_only_pub();
self.beneficiary_xpub.derive_xpub(&self.secp, &new_derivation_path)?.to_x_only_public_key();
// Build up the leaf script and combine with internal key into a Taproot commitment
let lock_time = absolute::LockTime::from_height(
@ -531,7 +531,7 @@ impl BenefactorWallet {
.get(&input.tap_internal_key.ok_or("internal key missing in PSBT")?)
.ok_or("missing Taproot key origin")?;
let secret_key =
self.master_xpriv.derive_priv(&self.secp, &derivation_path).to_priv().inner;
self.master_xpriv.derive_xpriv(&self.secp, &derivation_path).to_private_key().inner;
sign_psbt_taproot(
secret_key,
spend_info.internal_key(),
@ -628,7 +628,7 @@ impl BeneficiaryWallet {
Ok(Self { master_xpriv, secp: Secp256k1::new() })
}
fn master_xpub(&self) -> Xpub { Xpub::from_priv(&self.secp, &self.master_xpriv) }
fn master_xpub(&self) -> Xpub { Xpub::from_xpriv(&self.secp, &self.master_xpriv) }
fn spend_inheritance(
&self,
@ -652,7 +652,7 @@ impl BeneficiaryWallet {
&psbt.inputs[0].tap_key_origins.clone()
{
let secret_key =
self.master_xpriv.derive_priv(&self.secp, &derivation_path).to_priv().inner;
self.master_xpriv.derive_xpriv(&self.secp, &derivation_path).to_private_key().inner;
for lh in leaf_hashes {
let sighash_type = TapSighashType::All;
let hash = SighashCache::new(&unsigned_tx).taproot_script_spend_signature_hash(

View File

@ -592,7 +592,13 @@ impl Xpriv {
}
/// Constructs ECDSA compressed private key matching internal secret key representation.
#[deprecated(since = "TBD", note = "use `to_private_key()`")]
pub fn to_priv(self) -> PrivateKey {
self.to_private_key()
}
/// Constructs ECDSA compressed private key matching internal secret key representation.
pub fn to_private_key(self) -> PrivateKey {
PrivateKey { compressed: true, network: self.network, inner: self.private_key }
}
@ -606,10 +612,22 @@ impl Xpriv {
/// Derives an extended private key from a path.
///
/// The `path` argument can be both of type `DerivationPath` or `Vec<ChildNumber>`.
#[deprecated(since = "TBD", note = "use `derive_xpriv()`")]
pub fn derive_priv<C: secp256k1::Signing, P: AsRef<[ChildNumber]>>(
&self,
secp: &Secp256k1<C>,
path: &P,
) -> Xpriv {
self.derive_xpriv(secp, path)
}
/// Derives an extended private key from a path.
///
/// The `path` argument can be both of type `DerivationPath` or `Vec<ChildNumber>`.
pub fn derive_xpriv<C: secp256k1::Signing, P: AsRef<[ChildNumber]>>(
&self,
secp: &Secp256k1<C>,
path: &P,
) -> Xpriv {
let mut sk: Xpriv = *self;
for cnum in path.as_ref() {
@ -699,7 +717,7 @@ impl Xpriv {
/// Returns the HASH160 of the public key belonging to the xpriv
pub fn identifier<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>) -> XKeyIdentifier {
Xpub::from_priv(secp, self).identifier()
Xpub::from_xpriv(secp, self).identifier()
}
/// Returns the first four bytes of the identifier
@ -710,32 +728,60 @@ impl Xpriv {
impl Xpub {
/// Derives a public key from a private key
#[deprecated(since = "TBD", note = "use `from_xpriv()`")]
pub fn from_priv<C: secp256k1::Signing>(secp: &Secp256k1<C>, sk: &Xpriv) -> Xpub {
Self::from_xpriv(secp, sk)
}
/// Derives a public key from a private key
pub fn from_xpriv<C: secp256k1::Signing>(secp: &Secp256k1<C>, xpriv: &Xpriv) -> Xpub {
Xpub {
network: sk.network,
depth: sk.depth,
parent_fingerprint: sk.parent_fingerprint,
child_number: sk.child_number,
public_key: secp256k1::PublicKey::from_secret_key(secp, &sk.private_key),
chain_code: sk.chain_code,
network: xpriv.network,
depth: xpriv.depth,
parent_fingerprint: xpriv.parent_fingerprint,
child_number: xpriv.child_number,
public_key: secp256k1::PublicKey::from_secret_key(secp, &xpriv.private_key),
chain_code: xpriv.chain_code,
}
}
/// Constructs ECDSA compressed public key matching internal public key representation.
pub fn to_pub(self) -> CompressedPublicKey { CompressedPublicKey(self.public_key) }
#[deprecated(since = "TBD", note = "use `to_public_key()`")]
pub fn to_pub(self) -> CompressedPublicKey { self.to_public_key() }
/// Constructs ECDSA compressed public key matching internal public key representation.
pub fn to_public_key(self) -> CompressedPublicKey { CompressedPublicKey(self.public_key) }
/// Constructs BIP340 x-only public key for BIP-340 signatures and Taproot use matching
/// the internal public key representation.
pub fn to_x_only_pub(self) -> XOnlyPublicKey { XOnlyPublicKey::from(self.public_key) }
#[deprecated(since = "TBD", note = "use `to_x_only_public_key()`")]
pub fn to_x_only_pub(self) -> XOnlyPublicKey { self.to_x_only_public_key() }
/// Constructs BIP340 x-only public key for BIP-340 signatures and Taproot use matching
/// the internal public key representation.
pub fn to_x_only_public_key(self) -> XOnlyPublicKey { XOnlyPublicKey::from(self.public_key) }
/// Attempts to derive an extended public key from a path.
///
/// The `path` argument can be any type implementing `AsRef<ChildNumber>`, such as `DerivationPath`, for instance.
#[deprecated(since = "TBD", note = "use `derive_xpub()`")]
pub fn derive_pub<C: secp256k1::Verification, P: AsRef<[ChildNumber]>>(
&self,
secp: &Secp256k1<C>,
path: &P,
) -> Result<Xpub, Error> {
self.derive_xpub(secp, path)
}
/// Attempts to derive an extended public key from a path.
///
/// The `path` argument can be any type implementing `AsRef<ChildNumber>`, such as `DerivationPath`, for instance.
pub fn derive_xpub<C: secp256k1::Verification, P: AsRef<[ChildNumber]>>(
&self,
secp: &Secp256k1<C>,
path: &P,
) -> Result<Xpub, Error> {
let mut pk: Xpub = *self;
for cnum in path.as_ref() {
pk = pk.ckd_pub(secp, *cnum)?
@ -1004,17 +1050,17 @@ mod tests {
expected_pk: &str,
) {
let mut sk = Xpriv::new_master(network, seed).unwrap();
let mut pk = Xpub::from_priv(secp, &sk);
let mut pk = Xpub::from_xpriv(secp, &sk);
// Check derivation convenience method for Xpriv
assert_eq!(&sk.derive_priv(secp, &path).to_string()[..], expected_sk);
assert_eq!(&sk.derive_xpriv(secp, &path).to_string()[..], expected_sk);
// Check derivation convenience method for Xpub, should error
// appropriately if any ChildNumber is hardened
if path.0.iter().any(|cnum| cnum.is_hardened()) {
assert_eq!(pk.derive_pub(secp, &path), Err(Error::CannotDeriveFromHardenedKey));
assert_eq!(pk.derive_xpub(secp, &path), Err(Error::CannotDeriveFromHardenedKey));
} else {
assert_eq!(&pk.derive_pub(secp, &path).unwrap().to_string()[..], expected_pk);
assert_eq!(&pk.derive_xpub(secp, &path).unwrap().to_string()[..], expected_pk);
}
// Derive keys, checking hardened and non-hardened derivation one-by-one
@ -1023,12 +1069,12 @@ mod tests {
match num {
Normal { .. } => {
let pk2 = pk.ckd_pub(secp, num).unwrap();
pk = Xpub::from_priv(secp, &sk);
pk = Xpub::from_xpriv(secp, &sk);
assert_eq!(pk, pk2);
}
Hardened { .. } => {
assert_eq!(pk.ckd_pub(secp, num), Err(Error::CannotDeriveFromHardenedKey));
pk = Xpub::from_priv(secp, &sk);
pk = Xpub::from_xpriv(secp, &sk);
}
}
}

View File

@ -762,15 +762,15 @@ impl GetKey for Xpriv {
KeyRequest::Pubkey(_) => Err(GetKeyError::NotSupported),
KeyRequest::Bip32((fingerprint, path)) => {
let key = if self.fingerprint(secp) == *fingerprint {
let k = self.derive_priv(secp, &path);
Some(k.to_priv())
let k = self.derive_xpriv(secp, &path);
Some(k.to_private_key())
} else if self.parent_fingerprint == *fingerprint
&& !path.is_empty()
&& path[0] == self.child_number
{
let path = DerivationPath::from_iter(path.into_iter().skip(1).copied());
let k = self.derive_priv(secp, &path);
Some(k.to_priv())
let k = self.derive_xpriv(secp, &path);
Some(k.to_private_key())
} else {
None
};
@ -1369,9 +1369,9 @@ mod tests {
ChildNumber::from_normal_idx(31337).unwrap(),
];
sk = sk.derive_priv(secp, &dpath);
sk = sk.derive_xpriv(secp, &dpath);
let pk = Xpub::from_priv(secp, &sk);
let pk = Xpub::from_xpriv(secp, &sk);
hd_keypaths.insert(pk.public_key, (fprint, dpath.into()));

View File

@ -33,7 +33,7 @@ fn bip174_psbt_workflow() {
//
let ext_priv = build_extended_private_key();
let ext_pub = Xpub::from_priv(&secp, &ext_priv);
let ext_pub = Xpub::from_xpriv(&secp, &ext_priv);
let parent_fingerprint = ext_pub.fingerprint();
//
@ -315,7 +315,7 @@ fn parse_and_verify_keys(
let path =
derivation_path.into_derivation_path().expect("failed to convert derivation path");
let derived_priv = ext_priv.derive_priv(secp, &path).to_priv();
let derived_priv = ext_priv.derive_xpriv(secp, &path).to_private_key();
assert_eq!(wif_priv, derived_priv);
let derived_pub = derived_priv.public_key(secp);
key_map.insert(derived_pub, derived_priv);