Skip to content

Commit

Permalink
Ledger WalletBase, KeyError, WalletCapability::new_with_ledger
Browse files Browse the repository at this point in the history
  • Loading branch information
pacu committed Nov 14, 2024
1 parent 38d222f commit 2a20318
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 11 deletions.
7 changes: 7 additions & 0 deletions zingolib/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ pub fn load_clientconfig(
wallet_name: DEFAULT_WALLET_NAME.into(),
logfile_name: DEFAULT_LOGFILE_NAME.into(),
accept_server_txids: false,
#[cfg(feature = "ledger-support")]
use_ledger: false,
};

Ok(config)
Expand Down Expand Up @@ -159,6 +161,9 @@ pub struct ZingoConfig {
pub logfile_name: PathBuf,
/// If this option is enabled, the LightClient will replace outgoing TxId records with the TxId picked by the server. necessary for darkside.
pub accept_server_txids: bool,
#[cfg(feature = "ledger-support")]
/// if this option is enabled, the LightClient will look for a ledger device to initialize or resume the wallet
pub use_ledger: bool,
}

impl ZingoConfigBuilder {
Expand Down Expand Up @@ -215,6 +220,8 @@ impl ZingoConfigBuilder {
wallet_name: DEFAULT_WALLET_NAME.into(),
logfile_name: DEFAULT_LOGFILE_NAME.into(),
accept_server_txids: self.accept_server_txids,
#[cfg(feature = "ledger-support")]
use_ledger: false,
}
}
}
Expand Down
15 changes: 15 additions & 0 deletions zingolib/src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@ pub enum WalletBase {
Ufvk(String),
/// Unified spending key
Usk(Vec<u8>),
#[cfg(feature = "ledger-support")]
/// A hardware wallet
Ledger
}

impl WalletBase {
Expand Down Expand Up @@ -378,6 +381,18 @@ impl LightWallet {
)?;
(wc, None)
}
#[cfg(feature = "ledger-support")]
WalletBase::Ledger => {
let wc = WalletCapability::new_with_ledger(&config).map_err(
|e| {
Error::new(
ErrorKind::Other,
format!("Error initilizing Ledger Device: {}", e),
)
},
)?;
(wc, None)
}
};

if let Err(e) = wc.new_address(wc.can_view(), false) {
Expand Down
68 changes: 62 additions & 6 deletions zingolib/src/wallet/disk/testing/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,16 @@ use zcash_client_backend::{PoolType, ShieldedProtocol};
use zcash_keys::keys::Era;

use crate::{
lightclient::LightClient,
wallet::{
lightclient::LightClient, wallet::{
disk::testing::{
assert_wallet_capability_matches_seed,
examples::{
AbandonAbandonVersion, AbsurdAmountVersion, ChimneyBetterVersion,
HospitalMuseumVersion, HotelHumorVersion, MainnetSeedVersion, MobileShuffleVersion,
NetworkSeedVersion, RegtestSeedVersion, TestnetSeedVersion, VillageTargetVersion,
},
},
keys::unified::UnifiedKeyStore,
LightWallet,
},
}, error::KeyError, keys::unified::UnifiedKeyStore, LightWallet
}
};

// moving toward completeness: each of these tests should assert everything known about the LightWallet without network.
Expand Down Expand Up @@ -293,3 +290,62 @@ async fn reload_wallet_from_buffer() {
let balance = client.do_balance().await;
assert_eq!(balance.orchard_balance, Some(10342837));
}

#[tokio::test]
async fn test_ledger_initialization() {
use crate::wallet::WalletCapability;

let mid_wallet =
NetworkSeedVersion::Testnet(TestnetSeedVersion::ChimneyBetter(ChimneyBetterVersion::V28))
.load_example_wallet_with_verification()
.await;

let mid_client = LightClient::create_from_wallet_async(mid_wallet)
.await
.unwrap();

let mut config = mid_client.wallet.transaction_context.config;
config.use_ledger = true;

let expected_wc = WalletCapability::new_with_ledger(&config);
assert!(expected_wc.is_ok());

match expected_wc {
Ok(w) => {
assert!(w.is_ledger())
},
Err(_) => assert!(false),
}

}

#[cfg(feature = "ledger-support")]
#[tokio::test]
async fn test_ledger_initialization_fails_with_wrong_config() {
use crate::wallet::WalletCapability;

let mid_wallet =
NetworkSeedVersion::Testnet(TestnetSeedVersion::ChimneyBetter(ChimneyBetterVersion::V28))
.load_example_wallet_with_verification()
.await;

let mid_client = LightClient::create_from_wallet_async(mid_wallet)
.await
.unwrap();

let mut config = mid_client.wallet.transaction_context.config;
config.use_ledger = false; // this should make things fail

let expected_wc = WalletCapability::new_with_ledger(&config);
assert!(expected_wc.is_err());

match expected_wc {
Ok(_) => {
assert!(false)
},
Err(e) => match e {
KeyError::LedgerNotSet => assert!(true),
_ => assert!(false)
}
}
}
4 changes: 4 additions & 0 deletions zingolib/src/wallet/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,8 @@ pub enum KeyError {
/// Invalid format
#[error("Viewing keys must be imported in the unified format")]
InvalidFormat,
#[cfg(feature = "ledger-support")]
#[error("Ledger device not set.")]
/// Ledger flag was not set or device not found
LedgerNotSet
}
24 changes: 19 additions & 5 deletions zingolib/src/wallet/keys/ledger.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,34 @@
///
/// Holds information related to the ledger


use std::io;
use secp256k1::PublicKey as SecpPublicKey;
use tracing_subscriber::field::debug;
use secp256k1::{PublicKey, Secp256k1, SecretKey};
use crate::wallet::traits::ReadableWriteable;

/// Holds ledger things
#[derive(Debug)]
pub struct LedgerKeys {
ledger_id: SecpPublicKey,
ledger_id: PublicKey,
_app: ZcashApp
}

//TODO! this is all mocked code
impl LedgerKeys {
/// TODO! this is all mocked code
pub fn new() -> LedgerKeys{
// Create a new secp256k1 context
let secp = Secp256k1::new();

// Generate a secret key for testing purposes (not secure)
let secret_key = SecretKey::from_slice(&[0x01; 32]).expect("32 bytes, within curve order");

// Derive the corresponding public key
let public_key = PublicKey::from_secret_key(&secp, &secret_key);
LedgerKeys { ledger_id: public_key, _app: ZcashApp::new() }
}
}

/// Placeholder for the real thing
#[derive(Debug)]
pub struct ZcashApp {}
Expand Down Expand Up @@ -42,7 +56,7 @@ impl ReadableWriteable for LedgerKeys {
let mut buf = [0; secp256k1::constants::PUBLIC_KEY_SIZE];
reader.read_exact(&mut buf)?;

SecpPublicKey::from_slice(&buf).map_err(|e| {
PublicKey::from_slice(&buf).map_err(|e| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("Bad public key stored for ledger id: {:?}", e),
Expand Down
20 changes: 20 additions & 0 deletions zingolib/src/wallet/keys/unified.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,12 @@ fn read_write_receiver_selections() {
}

impl WalletCapability {
#[cfg(feature = "ledger-support")]
/// checks whether this WalletCapability is a Ledger device or not
pub fn is_ledger(&self) -> bool {
self.ledger.is_some()
}

pub(crate) fn get_ua_from_contained_transparent_receiver(
&self,
receiver: &TransparentAddress,
Expand Down Expand Up @@ -562,6 +568,20 @@ impl WalletCapability {
})
}

#[cfg(feature = "ledger-support")]
/// initializes a new wallet with a ledger
pub fn new_with_ledger(config: &ZingoConfig) -> Result<Self, KeyError> {
if !config.use_ledger {
return Err(KeyError::LedgerNotSet)
}

let mut wc = WalletCapability::default();

wc.ledger = Some(LedgerKeys::new());

Ok(wc)
}

/// external here refers to HD keys:
/// <https://zips.z.cash/zip-0032>
/// where external and internal were inherited from the BIP44 conventions
Expand Down

0 comments on commit 2a20318

Please sign in to comment.