Skip to content

Commit

Permalink
Merge pull request #1414 from Oscar-Pepper/unify_keys
Browse files Browse the repository at this point in the history
Unify keys
  • Loading branch information
zancas authored Oct 7, 2024
2 parents f63b7e7 + 173ea7f commit fd86965
Show file tree
Hide file tree
Showing 21 changed files with 1,043 additions and 733 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 16 additions & 12 deletions libtonode-tests/tests/concrete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use zingolib::testutils::lightclient::from_inputs;
use zingolib::testutils::{build_fvk_client, increase_height_and_wait_for_client, scenarios};
use zingolib::utils::conversion::address_from_str;
use zingolib::wallet::data::summaries::TransactionSummaryInterface;
use zingolib::wallet::keys::unified::UnifiedKeyStore;
use zingolib::wallet::propose::ProposeSendError;
use zingolib::{check_client_balances, get_base_address_macro, get_otd, validate_otds};

Expand Down Expand Up @@ -61,23 +62,26 @@ fn check_view_capability_bounds(
balance: &PoolBalances,
watch_wc: &WalletCapability,
fvks: &[&Fvk],
ovk: &Fvk,
svk: &Fvk,
tvk: &Fvk,
orchard_fvk: &Fvk,
sapling_fvk: &Fvk,
transparent_fvk: &Fvk,
sent_o_value: Option<u64>,
sent_s_value: Option<u64>,
sent_t_value: Option<u64>,
notes: &JsonValue,
) {
let UnifiedKeyStore::View(ufvk) = watch_wc.unified_key_store() else {
panic!("should be viewing key!")
};
//Orchard
if !fvks.contains(&ovk) {
assert!(!watch_wc.orchard.can_view());
if !fvks.contains(&orchard_fvk) {
assert!(ufvk.orchard().is_none());
assert_eq!(balance.orchard_balance, None);
assert_eq!(balance.verified_orchard_balance, None);
assert_eq!(balance.unverified_orchard_balance, None);
assert_eq!(notes["unspent_orchard_notes"].members().count(), 0);
} else {
assert!(watch_wc.orchard.can_view());
assert!(ufvk.orchard().is_some());
assert_eq!(balance.orchard_balance, sent_o_value);
assert_eq!(balance.verified_orchard_balance, sent_o_value);
assert_eq!(balance.unverified_orchard_balance, Some(0));
Expand All @@ -86,25 +90,25 @@ fn check_view_capability_bounds(
assert!((1..=2).contains(&orchard_notes_count));
}
//Sapling
if !fvks.contains(&svk) {
assert!(!watch_wc.sapling.can_view());
if !fvks.contains(&sapling_fvk) {
assert!(ufvk.sapling().is_none());
assert_eq!(balance.sapling_balance, None);
assert_eq!(balance.verified_sapling_balance, None);
assert_eq!(balance.unverified_sapling_balance, None);
assert_eq!(notes["unspent_sapling_notes"].members().count(), 0);
} else {
assert!(watch_wc.sapling.can_view());
assert!(ufvk.sapling().is_some());
assert_eq!(balance.sapling_balance, sent_s_value);
assert_eq!(balance.verified_sapling_balance, sent_s_value);
assert_eq!(balance.unverified_sapling_balance, Some(0));
assert_eq!(notes["unspent_sapling_notes"].members().count(), 1);
}
if !fvks.contains(&tvk) {
assert!(!watch_wc.transparent.can_view());
if !fvks.contains(&transparent_fvk) {
assert!(ufvk.transparent().is_none());
assert_eq!(balance.transparent_balance, None);
assert_eq!(notes["utxos"].members().count(), 0);
} else {
assert!(watch_wc.transparent.can_view());
assert!(ufvk.transparent().is_some());
assert_eq!(balance.transparent_balance, sent_t_value);
assert_eq!(notes["utxos"].members().count(), 1);
}
Expand Down
2 changes: 2 additions & 0 deletions zingolib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ zcash_proofs = { workspace = true }
zip32.workspace = true

bip0039.workspace = true
bip32 = { version = "0.5", default-features = false, features = ["secp256k1-ffi"] }
bs58 = { version = "0.5", features = ["check"] }

append-only-vec = { workspace = true }
base58 = { workspace = true }
Expand Down
14 changes: 8 additions & 6 deletions zingolib/src/blaze/trial_decryptions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,12 @@ impl TrialDecryptions {
let mut workers = FuturesUnordered::new();
let mut cbs = vec![];

let sapling_ivk = sapling_crypto::zip32::DiversifiableFullViewingKey::try_from(&*wc)
.ok()
.map(|key| key.derive_ivk());
let orchard_ivk = orchard::keys::FullViewingKey::try_from(&*wc)
let sapling_ivk = sapling_crypto::zip32::DiversifiableFullViewingKey::try_from(
wc.unified_key_store(),
)
.ok()
.map(|key| key.derive_ivk());
let orchard_ivk = orchard::keys::FullViewingKey::try_from(wc.unified_key_store())
.ok()
.map(|key| key.derive_ivk());

Expand Down Expand Up @@ -315,7 +317,7 @@ impl TrialDecryptions {
let config = config.clone();

workers.push(tokio::spawn(async move {
let Ok(fvk) = D::wc_to_fvk(&wc) else {
let Ok(fvk) = D::unified_key_store_to_fvk(wc.unified_key_store()) else {
// skip any scanning if the wallet doesn't have viewing capability
return Ok::<_, String>(());
};
Expand Down Expand Up @@ -450,7 +452,7 @@ where
transaction_id,
Some(output_index),
position + i as u64,
&D::wc_to_fvk(wc).unwrap(),
&D::unified_key_store_to_fvk(wc.unified_key_store()).unwrap(),
)?;
}
nodes_retention.push((node, retention));
Expand Down
63 changes: 43 additions & 20 deletions zingolib/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//! upgrade-or-replace
use crate::data::proposal;
use crate::wallet::keys::unified::UnifiedKeyStore;
use crate::wallet::MemoDownloadOption;
use crate::{lightclient::LightClient, wallet};
use indoc::indoc;
Expand All @@ -13,7 +14,7 @@ use std::str::FromStr;
use tokio::runtime::Runtime;
use zcash_address::unified::{Container, Encoding, Ufvk};
use zcash_keys::address::Address;
use zcash_primitives::consensus::Parameters;
use zcash_keys::keys::UnifiedFullViewingKey;
use zcash_primitives::transaction::components::amount::NonNegativeAmount;
use zcash_primitives::transaction::fees::zip317::MINIMUM_FEE;

Expand Down Expand Up @@ -134,16 +135,36 @@ impl Command for WalletKindCommand {
fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String {
RT.block_on(async move {
if lightclient.do_seed_phrase().await.is_ok() {
object! {"kind" => "Seeded"}.pretty(4)
} else {
let capability = lightclient.wallet.wallet_capability();
object! {
"kind" => "Loaded from key",
"transparent" => capability.transparent.kind_str(),
"sapling" => capability.sapling.kind_str(),
"orchard" => capability.orchard.kind_str(),
object! {"kind" => "Loaded from seed phrase",
"transparent" => true,
"sapling" => true,
"orchard" => true,
}
.pretty(4)
} else {
match lightclient.wallet.wallet_capability().unified_key_store() {
UnifiedKeyStore::Spend(_) => object! {
"kind" => "Loaded from unified spending key",
"transparent" => true,
"sapling" => true,
"orchard" => true,
}
.pretty(4),
UnifiedKeyStore::View(ufvk) => object! {
"kind" => "Loaded from unified full viewing key",
"transparent" => ufvk.transparent().is_some(),
"sapling" => ufvk.sapling().is_some(),
"orchard" => ufvk.orchard().is_some(),
}
.pretty(4),
UnifiedKeyStore::Empty => object! {
"kind" => "No keys found",
"transparent" => false,
"sapling" => false,
"orchard" => false,
}
.pretty(4),
}
}
})
}
Expand Down Expand Up @@ -690,18 +711,20 @@ impl Command for ExportUfvkCommand {
}

fn exec(&self, _args: &[&str], lightclient: &LightClient) -> String {
let ufvk_res = lightclient.wallet.transaction_context.key.ufvk();
match ufvk_res {
Ok(ufvk) => {
use zcash_address::unified::Encoding as _;
object! {
"ufvk" => ufvk.encode(&lightclient.config().chain.network_type()),
"birthday" => RT.block_on(lightclient.wallet.get_birthday())
}
.pretty(2)
}
Err(e) => format!("Error: {e}"),
let ufvk: UnifiedFullViewingKey = match lightclient
.wallet
.wallet_capability()
.unified_key_store()
.try_into()
{
Ok(ufvk) => ufvk,
Err(e) => return e.to_string(),
};
object! {
"ufvk" => ufvk.encode(&lightclient.config().chain),
"birthday" => RT.block_on(lightclient.wallet.get_birthday())
}
.pretty(2)
}
}

Expand Down
2 changes: 1 addition & 1 deletion zingolib/src/lightclient.rs
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ impl LightClient {
let new_address = self
.wallet
.wallet_capability()
.new_address(desired_receivers)?;
.new_address(desired_receivers, false)?;

// self.save_internal_rust().await?;

Expand Down
46 changes: 17 additions & 29 deletions zingolib/src/testutils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use std::time::Duration;
use tokio::task::JoinHandle;
use zcash_address::unified::{Fvk, Ufvk};
use zcash_address::unified::Fvk;

use crate::config::ZingoConfig;
use crate::lightclient::LightClient;
Expand All @@ -46,31 +46,27 @@ pub mod regtest;

/// TODO: Add Doc Comment Here!
pub fn build_fvks_from_wallet_capability(wallet_capability: &WalletCapability) -> [Fvk; 3] {
let o_fvk = Fvk::Orchard(
orchard::keys::FullViewingKey::try_from(wallet_capability)
.unwrap()
.to_bytes(),
);
let s_fvk = Fvk::Sapling(
zcash_client_backend::keys::sapling::DiversifiableFullViewingKey::try_from(
wallet_capability,
)
.unwrap()
.to_bytes(),
);
let mut t_fvk_bytes = [0u8; 65];
let t_ext_pk: crate::wallet::keys::extended_transparent::ExtendedPubKey =
(wallet_capability).try_into().unwrap();
t_fvk_bytes[0..32].copy_from_slice(&t_ext_pk.chain_code[..]);
t_fvk_bytes[32..65].copy_from_slice(&t_ext_pk.public_key.serialize()[..]);
let t_fvk = Fvk::P2pkh(t_fvk_bytes);
[o_fvk, s_fvk, t_fvk]
let orchard_vk: orchard::keys::FullViewingKey =
wallet_capability.unified_key_store().try_into().unwrap();
let sapling_vk: sapling_crypto::zip32::DiversifiableFullViewingKey =
wallet_capability.unified_key_store().try_into().unwrap();
let transparent_vk: zcash_primitives::legacy::keys::AccountPubKey =
wallet_capability.unified_key_store().try_into().unwrap();

let mut transparent_vk_bytes = [0u8; 65];
transparent_vk_bytes.copy_from_slice(&transparent_vk.serialize());

[
Fvk::Orchard(orchard_vk.to_bytes()),
Fvk::Sapling(sapling_vk.to_bytes()),
Fvk::P2pkh(transparent_vk_bytes),
]
}

/// TODO: Add Doc Comment Here!
pub async fn build_fvk_client(fvks: &[&Fvk], zingoconfig: &ZingoConfig) -> LightClient {
let ufvk = zcash_address::unified::Encoding::encode(
&<Ufvk as zcash_address::unified::Encoding>::try_from_items(
&<zcash_address::unified::Ufvk as zcash_address::unified::Encoding>::try_from_items(
fvks.iter().copied().cloned().collect(),
)
.unwrap(),
Expand All @@ -80,14 +76,6 @@ pub async fn build_fvk_client(fvks: &[&Fvk], zingoconfig: &ZingoConfig) -> Light
.await
.unwrap()
}

/// Converts a Lightclient with spending capability to a Lightclient with only viewing capability
pub async fn sk_client_to_fvk_client(client: &LightClient) -> LightClient {
let [o_fvk, s_fvk, t_fvk] =
build_fvks_from_wallet_capability(&client.wallet.wallet_capability().clone());
build_fvk_client(&[&o_fvk, &s_fvk, &t_fvk], client.config()).await
}

async fn get_synced_wallet_height(client: &LightClient) -> Result<u32, String> {
client.do_sync(true).await?;
Ok(client
Expand Down
23 changes: 15 additions & 8 deletions zingolib/src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
//! TODO: Add Mod Description Here
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use error::KeyError;
use getset::{Getters, MutGetters};
use zcash_keys::keys::UnifiedFullViewingKey;
#[cfg(feature = "sync")]
use zcash_primitives::consensus::BlockHeight;
use zcash_primitives::memo::Memo;
Expand All @@ -12,8 +14,6 @@ use log::{info, warn};
use rand::rngs::OsRng;
use rand::Rng;

use sapling_crypto::zip32::DiversifiableFullViewingKey;

#[cfg(feature = "sync")]
use zingo_sync::{
primitives::{NullifierMap, SyncState, WalletBlock},
Expand All @@ -35,7 +35,6 @@ use crate::config::ZingoConfig;
use zcash_client_backend::proto::service::TreeState;
use zcash_encoding::Optional;

use self::keys::unified::Fvk as _;
use self::keys::unified::WalletCapability;

use self::{
Expand Down Expand Up @@ -268,10 +267,18 @@ impl LightWallet {

///TODO: Make this work for orchard too
pub async fn decrypt_message(&self, enc: Vec<u8>) -> Result<Message, String> {
let sapling_ivk = DiversifiableFullViewingKey::try_from(&*self.wallet_capability())?
.derive_ivk::<keys::unified::External>();
let ufvk: UnifiedFullViewingKey =
match self.wallet_capability().unified_key_store().try_into() {
Ok(ufvk) => ufvk,
Err(e) => return Err(e.to_string()),
};
let sapling_ivk = if let Some(ivk) = ufvk.sapling() {
ivk.to_external_ivk().prepare()
} else {
return Err(KeyError::NoViewCapability.to_string());
};

if let Ok(msg) = Message::decrypt(&enc, &sapling_ivk.ivk) {
if let Ok(msg) = Message::decrypt(&enc, &sapling_ivk) {
// If decryption succeeded for this IVK, return the decrypted memo and the matched address
return Ok(msg);
}
Expand Down Expand Up @@ -367,13 +374,13 @@ impl LightWallet {
}
};

if let Err(e) = wc.new_address(wc.can_view()) {
if let Err(e) = wc.new_address(wc.can_view(), false) {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("could not create initial address: {e}"),
));
};
let transaction_metadata_set = if wc.can_spend_from_all_pools() {
let transaction_metadata_set = if wc.unified_key_store().is_spending_key() {
Arc::new(RwLock::new(TxMap::new_with_witness_trees(
wc.transparent_child_addresses().clone(),
)))
Expand Down
Loading

0 comments on commit fd86965

Please sign in to comment.