Skip to content

Commit

Permalink
Merge pull request #809 from fluidvanadium/note_interface_overhaul_pa…
Browse files Browse the repository at this point in the history
…rt_1_trait

Note interface overhaul part 1 trait
  • Loading branch information
Oscar-Pepper authored Feb 19, 2024
2 parents 23d51ee + 28feb27 commit c63a75b
Show file tree
Hide file tree
Showing 20 changed files with 785 additions and 685 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.

1 change: 1 addition & 0 deletions integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ hex = "0.3"
itertools = "0.10.5"
bip0039 = "0.10.1"
serde_json = "1.0.107"
http.workspace = true
289 changes: 139 additions & 150 deletions integration-tests/tests/integrations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,156 +480,6 @@ mod fast {
);
}

#[tokio::test]
async fn load_wallet_from_v26_dat_file() {
// We test that the LightWallet can be read from v26 .dat file
// Changes in version 27:
// - The wallet does not have to have a mnemonic.
// Absence of mnemonic is represented by an empty byte vector in v27.
// v26 serialized wallet is always loaded with `Some(mnemonic)`.
// - The wallet capabilities can be restricted from spending to view-only or none.
// We introduce `Capability` type represent different capability types in v27.
// v26 serialized wallet is always loaded with `Capability::Spend(sk)`.

// A testnet wallet initiated with
// --seed "chimney better bulb horror rebuild whisper improve intact letter giraffe brave rib appear bulk aim burst snap salt hill sad merge tennis phrase raise"
// --birthday 0
// --nosync
// with 3 addresses containing all receivers.
let data = include_bytes!("zingo-wallet-v26.dat");

let config = zingoconfig::ZingoConfig::build(ChainType::Testnet).create();
let wallet = LightWallet::read_internal(&data[..], &config)
.await
.map_err(|e| format!("Cannot deserialize LightWallet version 26 file: {}", e))
.unwrap();

let expected_mnemonic = (
Mnemonic::from_phrase(CHIMNEY_BETTER_SEED.to_string()).unwrap(),
0,
);
assert_eq!(wallet.mnemonic(), Some(&expected_mnemonic));

let expected_wc =
WalletCapability::new_from_phrase(&config, &expected_mnemonic.0, expected_mnemonic.1)
.unwrap();
let wc = wallet.wallet_capability();

// We don't want the WalletCapability to impl. `Eq` (because it stores secret keys)
// so we have to compare each component instead

// Compare Orchard
let Capability::Spend(orchard_sk) = &wc.orchard else {
panic!("Expected Orchard Spending Key");
};
assert_eq!(
orchard_sk.to_bytes(),
orchard::keys::SpendingKey::try_from(&expected_wc)
.unwrap()
.to_bytes()
);

// Compare Sapling
let Capability::Spend(sapling_sk) = &wc.sapling else {
panic!("Expected Sapling Spending Key");
};
assert_eq!(
sapling_sk,
&zcash_client_backend::keys::sapling::ExtendedSpendingKey::try_from(&expected_wc)
.unwrap()
);

// Compare transparent
let Capability::Spend(transparent_sk) = &wc.transparent else {
panic!("Expected transparent extended private key");
};
assert_eq!(
transparent_sk,
&ExtendedPrivKey::try_from(&expected_wc).unwrap()
);

assert_eq!(wc.addresses().len(), 3);
for addr in wc.addresses().iter() {
assert!(addr.orchard().is_some());
assert!(addr.sapling().is_some());
assert!(addr.transparent().is_some());
}
}

#[tokio::test]
async fn load_wallet_from_v28_dat_file() {
// We test that the LightWallet can be read from v28 .dat file
// A testnet wallet initiated with
// --seed "chimney better bulb horror rebuild whisper improve intact letter giraffe brave rib appear bulk aim burst snap salt hill sad merge tennis phrase raise"
// --birthday 0
// --nosync
// with 3 addresses containing all receivers.
let data = include_bytes!("zingo-wallet-v28.dat");

let config = zingoconfig::ZingoConfig::build(ChainType::Testnet).create();
let wallet = LightWallet::read_internal(&data[..], &config)
.await
.map_err(|e| format!("Cannot deserialize LightWallet version 28 file: {}", e))
.unwrap();

let expected_mnemonic = (
Mnemonic::from_phrase(CHIMNEY_BETTER_SEED.to_string()).unwrap(),
0,
);
assert_eq!(wallet.mnemonic(), Some(&expected_mnemonic));

let expected_wc =
WalletCapability::new_from_phrase(&config, &expected_mnemonic.0, expected_mnemonic.1)
.unwrap();
let wc = wallet.wallet_capability();

// We don't want the WalletCapability to impl. `Eq` (because it stores secret keys)
// so we have to compare each component instead

// Compare Orchard
let Capability::Spend(orchard_sk) = &wc.orchard else {
panic!("Expected Orchard Spending Key");
};
assert_eq!(
orchard_sk.to_bytes(),
orchard::keys::SpendingKey::try_from(&expected_wc)
.unwrap()
.to_bytes()
);

// Compare Sapling
let Capability::Spend(sapling_sk) = &wc.sapling else {
panic!("Expected Sapling Spending Key");
};
assert_eq!(
sapling_sk,
&zcash_client_backend::keys::sapling::ExtendedSpendingKey::try_from(&expected_wc)
.unwrap()
);

// Compare transparent
let Capability::Spend(transparent_sk) = &wc.transparent else {
panic!("Expected transparent extended private key");
};
assert_eq!(
transparent_sk,
&ExtendedPrivKey::try_from(&expected_wc).unwrap()
);

assert_eq!(wc.addresses().len(), 3);
for addr in wc.addresses().iter() {
assert!(addr.orchard().is_some());
assert!(addr.sapling().is_some());
assert!(addr.transparent().is_some());
}

let client = LightClient::create_from_wallet_async(wallet, config)
.await
.unwrap();
let balance = client.do_balance().await;
assert_eq!(balance.orchard_balance, Some(10342837));
}

#[tokio::test]
async fn reload_wallet_from_buffer() {
// We test that the LightWallet can be read from v28 .dat file
Expand Down Expand Up @@ -3585,6 +3435,145 @@ mod slow {

assert_eq!(bala_sim, bala_syn);
}
async fn load_wallet_from_data_and_assert(
data: &[u8],
expected_balance: u64,
num_addresses: usize,
) {
let config = zingoconfig::ZingoConfig::build(ChainType::Testnet)
.set_lightwalletd_uri(
("https://zcash.mysideoftheweb.com:19067")
.parse::<http::Uri>()
.unwrap(),
)
.create();
let wallet = LightWallet::read_internal(&data[..], &config)
.await
.map_err(|e| format!("Cannot deserialize LightWallet file!: {}", e))
.unwrap();

let expected_mnemonic = (
Mnemonic::from_phrase(CHIMNEY_BETTER_SEED.to_string()).unwrap(),
0,
);
assert_eq!(wallet.mnemonic(), Some(&expected_mnemonic));

let expected_wc =
WalletCapability::new_from_phrase(&config, &expected_mnemonic.0, expected_mnemonic.1)
.unwrap();
let wc = wallet.wallet_capability();

// We don't want the WalletCapability to impl. `Eq` (because it stores secret keys)
// so we have to compare each component instead

// Compare Orchard
let Capability::Spend(orchard_sk) = &wc.orchard else {
panic!("Expected Orchard Spending Key");
};
assert_eq!(
orchard_sk.to_bytes(),
orchard::keys::SpendingKey::try_from(&expected_wc)
.unwrap()
.to_bytes()
);

// Compare Sapling
let Capability::Spend(sapling_sk) = &wc.sapling else {
panic!("Expected Sapling Spending Key");
};
assert_eq!(
sapling_sk,
&zcash_client_backend::keys::sapling::ExtendedSpendingKey::try_from(&expected_wc)
.unwrap()
);

// Compare transparent
let Capability::Spend(transparent_sk) = &wc.transparent else {
panic!("Expected transparent extended private key");
};
assert_eq!(
transparent_sk,
&ExtendedPrivKey::try_from(&expected_wc).unwrap()
);

assert_eq!(wc.addresses().len(), num_addresses);
for addr in wc.addresses().iter() {
assert!(addr.orchard().is_some());
assert!(addr.sapling().is_some());
assert!(addr.transparent().is_some());
}

let client = LightClient::create_from_wallet_async(wallet, config)
.await
.unwrap();
let balance = client.do_balance().await;
assert_eq!(balance.orchard_balance, Some(expected_balance));
if expected_balance > 0 {
let _ = client
.do_send(vec![(&get_base_address!(client, "sapling"), 11011, None)])
.await
.unwrap();
let _ = client.do_sync(true).await.unwrap();
let _ = client
.do_send(vec![(
&get_base_address!(client, "transparent"),
28000,
None,
)])
.await
.unwrap();
}
}

#[tokio::test]
async fn load_wallet_from_v26_dat_file() {
// We test that the LightWallet can be read from v26 .dat file
// Changes in version 27:
// - The wallet does not have to have a mnemonic.
// Absence of mnemonic is represented by an empty byte vector in v27.
// v26 serialized wallet is always loaded with `Some(mnemonic)`.
// - The wallet capabilities can be restricted from spending to view-only or none.
// We introduce `Capability` type represent different capability types in v27.
// v26 serialized wallet is always loaded with `Capability::Spend(sk)`.

// A testnet wallet initiated with
// --seed "chimney better bulb horror rebuild whisper improve intact letter giraffe brave rib appear bulk aim burst snap salt hill sad merge tennis phrase raise"
// with 3 addresses containing all receivers.
// including orchard and sapling transactions
let data = include_bytes!("zingo-wallet-v26.dat");

load_wallet_from_data_and_assert(data, 0, 3).await;
}

#[tokio::test]
async fn load_wallet_from_v26_2_dat_file() {
// We test that the LightWallet can be read from v26 .dat file
// Changes in version 27:
// - The wallet does not have to have a mnemonic.
// Absence of mnemonic is represented by an empty byte vector in v27.
// v26 serialized wallet is always loaded with `Some(mnemonic)`.
// - The wallet capabilities can be restricted from spending to view-only or none.
// We introduce `Capability` type represent different capability types in v27.
// v26 serialized wallet is always loaded with `Capability::Spend(sk)`.

// A testnet wallet initiated with
// --seed "chimney better bulb horror rebuild whisper improve intact letter giraffe brave rib appear bulk aim burst snap salt hill sad merge tennis phrase raise"
// with 3 addresses containing all receivers.
// including orchard and sapling transactions
let data = include_bytes!("zingo-wallet-v26-2.dat");

load_wallet_from_data_and_assert(data, 10177826, 1).await;
}

#[tokio::test]
async fn load_wallet_from_v28_dat_file() {
// We test that the LightWallet can be read from v28 .dat file
// --seed "chimney better bulb horror rebuild whisper improve intact letter giraffe brave rib appear bulk aim burst snap salt hill sad merge tennis phrase raise"
// with 3 addresses containing all receivers.
let data = include_bytes!("zingo-wallet-v28.dat");

load_wallet_from_data_and_assert(data, 10342837, 3).await;
}
}

#[tokio::test]
Expand Down
Binary file added integration-tests/tests/zingo-wallet-v26-2.dat
Binary file not shown.
1 change: 1 addition & 0 deletions zingoconfig/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ log4rs = "1.1.1"
log = "0.4.14"
http = "0.2.4"
dirs = "3.0.2"
tempdir.workspace = true
42 changes: 38 additions & 4 deletions zingoconfig/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,42 @@ pub struct ZingoConfig {
pub logfile_name: PathBuf,
}
impl ZingoConfigBuilder {
pub fn set_wallet_dir(mut self, dir: PathBuf) -> Self {
self.wallet_dir = Some(dir);
/// Set the URI of the proxy server we download blockchain information from.
/// # Examples
/// ```
/// use zingoconfig::ZingoConfigBuilder;
/// use http::Uri;
/// assert_eq!(ZingoConfigBuilder::default().set_lightwalletd_uri(("https://zcash.mysideoftheweb.com:19067").parse::<Uri>().unwrap()).lightwalletd_uri.clone().unwrap(), "https://zcash.mysideoftheweb.com:19067");
/// ```
pub fn set_lightwalletd_uri(&mut self, lightwalletd_uri: http::Uri) -> &Self {
self.lightwalletd_uri = Some(lightwalletd_uri);
self
}
pub fn set_lightwalletd(mut self, lightwalletd_uri: http::Uri) -> Self {
self.lightwalletd_uri = Some(lightwalletd_uri);
/// Set the chain the consuming client will interact with.
/// See <https://github.com/bitcoin/bips/blob/master/bip-0087.mediawiki#coin-type>
/// for chain types.
/// Note "chain type" is not a formal standard.
/// # Examples
/// ```
/// use zingoconfig::ZingoConfigBuilder;
/// use zingoconfig::ChainType::Testnet;
/// assert_eq!(ZingoConfigBuilder::default().set_chain(Testnet).create().chain, Testnet);
/// ```
pub fn set_chain(&mut self, chain: ChainType) -> &Self {
self.chain = chain;
self
}
/// Set the wallet directory where client transaction data will be stored in a wallet.
/// # Examples
/// ```
/// use zingoconfig::ZingoConfigBuilder;
/// use tempdir::TempDir;
/// let dir = TempDir::new("zingo_doc_test").unwrap().into_path();
/// let config = ZingoConfigBuilder::default().set_wallet_dir(dir.clone()).create();
/// assert_eq!(config.wallet_dir.clone().unwrap(), dir);
/// ```
pub fn set_wallet_dir(&mut self, dir: PathBuf) -> &Self {
self.wallet_dir = Some(dir);
self
}
pub fn create(&self) -> ZingoConfig {
Expand Down Expand Up @@ -363,6 +393,10 @@ impl ZingoConfig {
log_path.into_boxed_path()
}

/// Coin Types are specified in public registries to disambiguate coin variants
/// so that HD wallets can manage multiple currencies.
/// <https://github.com/satoshilabs/slips/blob/master/slip-0044.md>
/// ZEC is registered as 133 (0x80000085) for MainNet and 1 (0x80000001) for TestNet (all coins)
pub fn get_coin_type(&self) -> u32 {
self.chain.coin_type()
}
Expand Down
2 changes: 2 additions & 0 deletions zingolib/src/blaze/fetch_full_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ impl TransactionContext {
// Process t-address outputs
// If this transaction in outgoing, i.e., we received sent some money in this transaction, then we need to grab all transparent outputs
// that don't belong to us as the outgoing metadata
// the assumption is either we already decrypted a compact output and filled in some data
// or transparent something
if self
.transaction_metadata_set
.read()
Expand Down
Loading

0 comments on commit c63a75b

Please sign in to comment.