From 5a9341bfc5bbc9ebac730e7415e7d1c34d014947 Mon Sep 17 00:00:00 2001 From: Jiri Jakes Date: Sat, 14 Sep 2024 11:19:34 +0800 Subject: [PATCH 1/3] 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'. --- bitcoin/examples/bip32.rs | 6 +- bitcoin/examples/ecdsa-psbt-simple.rs | 14 ++--- bitcoin/examples/ecdsa-psbt.rs | 18 +++--- bitcoin/examples/taproot-psbt-simple.rs | 14 ++--- bitcoin/examples/taproot-psbt.rs | 16 +++--- bitcoin/src/bip32.rs | 76 ++++++++++++++++++++----- bitcoin/src/psbt/mod.rs | 12 ++-- bitcoin/tests/bip_174.rs | 4 +- 8 files changed, 103 insertions(+), 57 deletions(-) diff --git a/bitcoin/examples/bip32.rs b/bitcoin/examples/bip32.rs index 18a11fc76..f2f36d409 100644 --- a/bitcoin/examples/bip32.rs +++ b/bitcoin/examples/bip32.rs @@ -40,15 +40,15 @@ fn main() { // derive child xpub let path = "84h/0h/0h".parse::().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); } diff --git a/bitcoin/examples/ecdsa-psbt-simple.rs b/bitcoin/examples/ecdsa-psbt-simple.rs index 1acfab8e9..d801cff37 100644 --- a/bitcoin/examples/ecdsa-psbt-simple.rs +++ b/bitcoin/examples/ecdsa-psbt-simple.rs @@ -60,11 +60,11 @@ fn get_external_address_xpriv( ) -> 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( ) -> 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 = pk_inputs.iter().map(|pk| pk.wpubkey_hash()).collect(); diff --git a/bitcoin/examples/ecdsa-psbt.rs b/bitcoin/examples/ecdsa-psbt.rs index d7813518c..2423a6c1f 100644 --- a/bitcoin/examples/ecdsa-psbt.rs +++ b/bitcoin/examples/ecdsa-psbt.rs @@ -112,17 +112,17 @@ impl ColdStorage { /// The newly created signer along with the data needed to configure a watch-only wallet. fn new(secp: &Secp256k1, xpriv: &str) -> Result { let master_xpriv = xpriv.parse::()?; - 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 { 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, ) -> 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()?; diff --git a/bitcoin/examples/taproot-psbt-simple.rs b/bitcoin/examples/taproot-psbt-simple.rs index 90fc3a602..23828b566 100644 --- a/bitcoin/examples/taproot-psbt-simple.rs +++ b/bitcoin/examples/taproot-psbt-simple.rs @@ -59,11 +59,11 @@ fn get_external_address_xpriv( ) -> 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( ) -> 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. diff --git a/bitcoin/examples/taproot-psbt.rs b/bitcoin/examples/taproot-psbt.rs index f111e9fb1..7c72fcaba 100644 --- a/bitcoin/examples/taproot-psbt.rs +++ b/bitcoin/examples/taproot-psbt.rs @@ -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::()?; 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::()?; 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( diff --git a/bitcoin/src/bip32.rs b/bitcoin/src/bip32.rs index 55ad3ae80..973add9aa 100644 --- a/bitcoin/src/bip32.rs +++ b/bitcoin/src/bip32.rs @@ -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`. + #[deprecated(since = "TBD", note = "use `derive_xpriv()`")] pub fn derive_priv>( &self, secp: &Secp256k1, 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`. + pub fn derive_xpriv>( + &self, + secp: &Secp256k1, + 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(&self, secp: &Secp256k1) -> 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(secp: &Secp256k1, sk: &Xpriv) -> Xpub { + Self::from_xpriv(secp, sk) + } + + /// Derives a public key from a private key + pub fn from_xpriv(secp: &Secp256k1, 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`, such as `DerivationPath`, for instance. + #[deprecated(since = "TBD", note = "use `derive_xpub()`")] pub fn derive_pub>( &self, secp: &Secp256k1, path: &P, ) -> Result { + + self.derive_xpub(secp, path) + } + + /// Attempts to derive an extended public key from a path. + /// + /// The `path` argument can be any type implementing `AsRef`, such as `DerivationPath`, for instance. + pub fn derive_xpub>( + &self, + secp: &Secp256k1, + path: &P, + ) -> Result { 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); } } } diff --git a/bitcoin/src/psbt/mod.rs b/bitcoin/src/psbt/mod.rs index 08830e4f5..77eb01e56 100644 --- a/bitcoin/src/psbt/mod.rs +++ b/bitcoin/src/psbt/mod.rs @@ -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())); diff --git a/bitcoin/tests/bip_174.rs b/bitcoin/tests/bip_174.rs index 7a674728f..396fc6f3a 100644 --- a/bitcoin/tests/bip_174.rs +++ b/bitcoin/tests/bip_174.rs @@ -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); From 0dcba9838250bb285d6cf096cb742bd1f31b309a Mon Sep 17 00:00:00 2001 From: Jiri Jakes Date: Sat, 14 Sep 2024 11:20:52 +0800 Subject: [PATCH 2/3] Add Xpriv::to_xpub --- bitcoin/src/bip32.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bitcoin/src/bip32.rs b/bitcoin/src/bip32.rs index 973add9aa..e1c881c2f 100644 --- a/bitcoin/src/bip32.rs +++ b/bitcoin/src/bip32.rs @@ -602,6 +602,11 @@ impl Xpriv { PrivateKey { compressed: true, network: self.network, inner: self.private_key } } + /// Creates new extended public key from this extended private key. + pub fn to_xpub(&self, secp: &Secp256k1,) -> Xpub { + Xpub::from_xpriv(secp, self) + } + /// Constructs BIP340 keypair for Schnorr signatures and Taproot use matching the internal /// secret key representation. pub fn to_keypair(self, secp: &Secp256k1) -> Keypair { From e215a39dba173cdc616ab63b16a3eb8e02ac18ee Mon Sep 17 00:00:00 2001 From: Jiri Jakes Date: Sat, 14 Sep 2024 11:29:32 +0800 Subject: [PATCH 3/3] Improve documentation of Xpub::from_xpriv --- bitcoin/src/bip32.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bitcoin/src/bip32.rs b/bitcoin/src/bip32.rs index e1c881c2f..1b15a1022 100644 --- a/bitcoin/src/bip32.rs +++ b/bitcoin/src/bip32.rs @@ -732,13 +732,13 @@ impl Xpriv { } impl Xpub { - /// Derives a public key from a private key + /// Creates extended public key from an extended private key. #[deprecated(since = "TBD", note = "use `from_xpriv()`")] pub fn from_priv(secp: &Secp256k1, sk: &Xpriv) -> Xpub { Self::from_xpriv(secp, sk) } - /// Derives a public key from a private key + /// Creates extended public key from an extended private key. pub fn from_xpriv(secp: &Secp256k1, xpriv: &Xpriv) -> Xpub { Xpub { network: xpriv.network,