Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,6 @@ be considered breaking changes.
during migration.

### Fixed

- The wallet no longer permanently stops following the chain after a few
hundred blocks of history. The embedded indexer's finalised-state database
was configured with a size limit of 0 (a workaround for slow start-up,
zingolabs/zaino#249), so its sync loop eventually failed with `MDB_MAP_FULL`
and gave up; running the finalised state ephemerally removes the database
(and the workaround) entirely.
- `listaddresses` no longer returns an internal error when the wallet contains
standalone imported transparent keys (e.g. from a `zcashd` migration).
- No longer crashes in regtest mode when a Sapling or NU5 activation height is
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ sha2 = "0.10"

# Key storage
age = { version = "0.11", features = ["armor", "cli-common", "plugin"] }
x25519-dalek = { version = "2", features = ["static_secrets", "zeroize"] }
bip0039 = "0.12"

# Localization
Expand Down
1 change: 1 addition & 0 deletions zallet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ tracing-subscriber.workspace = true
transparent.workspace = true
uuid.workspace = true
which = { workspace = true, optional = true }
x25519-dalek.workspace = true
xdg.workspace = true
zaino-common.workspace = true
zaino-fetch.workspace = true
Expand Down
37 changes: 34 additions & 3 deletions zallet/src/components/keystore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,20 @@ pub(crate) use error::KeystoreError;

type RelockTask = (SystemTime, JoinHandle<()>);

/// Maximum duration the keystore may remain unlocked via [`KeyStore::unlock`].
///
/// Matches Bitcoin Core's `walletpassphrase` timeout cap.
const MAX_UNLOCK_TIMEOUT_SECS: u64 = 100_000_000;

fn clamp_unlock_timeout(timeout: u64) -> u64 {
timeout.min(MAX_UNLOCK_TIMEOUT_SECS)
}

fn clear_cached_identities(identities: &mut Vec<Box<dyn age::Identity + Send + Sync>>) {
identities.drain(..);
identities.shrink_to_fit();
}

#[derive(Clone)]
pub(crate) struct KeyStore {
db: Database,
Expand Down Expand Up @@ -355,13 +369,14 @@ impl KeyStore {
*self.identities.write().await = decrypted_identities;

// Start a task to relock the keystore after the given timeout.
let duration = Duration::from_secs(timeout);
let duration = Duration::from_secs(clamp_unlock_timeout(timeout));
let identities = self.identities.clone();
*relock_task = Some((
SystemTime::now() + duration,
crate::spawn!("Keystore relock", async move {
time::sleep(duration).await;
identities.write().await.clear();
let mut identities = identities.write().await;
clear_cached_identities(&mut identities);
}),
));

Expand All @@ -381,7 +396,8 @@ impl KeyStore {
existing_timeout.abort();
}

self.identities.write().await.clear();
let mut identities = self.identities.write().await;
clear_cached_identities(&mut identities);
}

async fn with_db<T>(
Expand Down Expand Up @@ -958,3 +974,18 @@ fn decrypt_standalone_transparent_privkey(

Ok(secret_key)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn unlock_timeout_is_clamped_to_bitcoin_core_max() {
assert_eq!(clamp_unlock_timeout(u64::MAX), MAX_UNLOCK_TIMEOUT_SECS);
assert_eq!(clamp_unlock_timeout(60), 60);
assert_eq!(
clamp_unlock_timeout(MAX_UNLOCK_TIMEOUT_SECS),
MAX_UNLOCK_TIMEOUT_SECS,
);
}
}
Loading