Skip to content

Commit

Permalink
Merge pull request #459 from AArnott/wallet_spending_key
Browse files Browse the repository at this point in the history
Initialize wallet with unified spending key
  • Loading branch information
AloeareV authored Sep 4, 2023
2 parents 408826c + 1de0df1 commit e4ef4e1
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 2 deletions.
2 changes: 1 addition & 1 deletion zingolib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ orchard = { workspace = true }
subtle = "2.4.1"
incrementalmerkletree = { workspace = true, features = ["test-dependencies"] }
zcash_address = { workspace = true }
zcash_client_backend = { workspace = true }
zcash_client_backend = { workspace = true, features = ["unstable", "transparent-inputs"] }
zcash_encoding = { workspace = true }
zcash_note_encryption = { workspace = true }
zcash_primitives = { workspace = true }
Expand Down
14 changes: 14 additions & 0 deletions zingolib/src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,10 @@ pub enum WalletBase {
SeedBytes([u8; 32]),
MnemonicPhrase(String),
Mnemonic(Mnemonic),
/// Unified full viewing key
Ufvk(String),
/// Unified spending key
Usk(Vec<u8>),
}
impl WalletBase {
pub fn from_string(base: String) -> WalletBase {
Expand Down Expand Up @@ -616,6 +619,17 @@ impl LightWallet {
})?;
(wc, None)
}
WalletBase::Usk(unified_spending_key) => {
let wc = WalletCapability::new_from_usk(unified_spending_key.as_slice()).map_err(
|e| {
Error::new(
ErrorKind::InvalidData,
format!("Error parsing unified spending key: {}", e),
)
},
)?;
(wc, None)
}
};

if let Err(e) = wc.new_address(wc.can_view()) {
Expand Down
37 changes: 36 additions & 1 deletion zingolib/src/wallet/keys/unified.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ use bip0039::Mnemonic;
use byteorder::{ReadBytesExt, WriteBytesExt};
use orchard::keys::Scope;

use secp256k1::SecretKey;
use zcash_address::unified::{Container, Encoding, Fvk, Ufvk};
use zcash_client_backend::address::UnifiedAddress;
use zcash_client_backend::keys::{Era, UnifiedSpendingKey};
use zcash_encoding::Vector;
use zcash_primitives::{
legacy::TransparentAddress, sapling::note_encryption::PreparedIncomingViewingKey,
Expand All @@ -22,7 +24,7 @@ use zingoconfig::ZingoConfig;
use crate::wallet::traits::ReadableWriteable;

use super::{
extended_transparent::{ExtendedPubKey, KeyIndex},
extended_transparent::{ExtendedPrivKey, ExtendedPubKey, KeyIndex},
get_zaddr_from_bip39seed, ToBase58Check,
};

Expand Down Expand Up @@ -352,6 +354,25 @@ impl WalletCapability {
Ok(Self::new_from_seed(config, &bip39_seed, position))
}

/// Creates a new `WalletCapability` from a unified spending key.
pub fn new_from_usk(usk: &[u8]) -> Result<Self, String> {
// Decode unified spending key
let usk = UnifiedSpendingKey::from_bytes(Era::Orchard, usk)
.map_err(|_| "Error decoding unified spending key.")?;

// Workaround https://github.com/zcash/librustzcash/issues/929 by serializing and deserializing the transparent key.
let transparent_bytes = usk.transparent().to_bytes();
let transparent_ext_key = transparent_key_from_bytes(transparent_bytes.as_slice())
.map_err(|e| format!("Error processing transparent key: {}", e))?;

Ok(Self {
orchard: Capability::Spend(usk.orchard().to_owned()),
sapling: Capability::Spend(usk.sapling().to_owned()),
transparent: Capability::Spend(transparent_ext_key),
..Default::default()
})
}

pub fn new_from_ufvk(config: &ZingoConfig, ufvk_encoded: String) -> Result<Self, String> {
// Decode UFVK
if ufvk_encoded.starts_with(config.hrp_sapling_viewing_key()) {
Expand Down Expand Up @@ -459,6 +480,20 @@ impl WalletCapability {
}
}

/// Reads a transparent ExtendedPrivKey from a buffer that has a 32 byte private key and 32 byte chain code.
fn transparent_key_from_bytes(bytes: &[u8]) -> Result<ExtendedPrivKey, std::io::Error> {
let mut reader = std::io::Cursor::new(bytes);

let private_key = SecretKey::read(&mut reader, ())?;
let mut chain_code = [0; 32];
reader.read_exact(&mut chain_code)?;

Ok(ExtendedPrivKey {
chain_code: chain_code.to_vec(),
private_key,
})
}

impl<V, S> ReadableWriteable<()> for Capability<V, S>
where
V: ReadableWriteable<()>,
Expand Down

0 comments on commit e4ef4e1

Please sign in to comment.