Skip to content

perf: Cache self fingerprint instead of loading it every time #6865

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 4, 2025
Merged
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
9 changes: 5 additions & 4 deletions src/contact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ use crate::constants::{Blocked, Chattype, DC_GCL_ADD_SELF, DC_GCL_VERIFIED_ONLY}
use crate::context::Context;
use crate::events::EventType;
use crate::key::{
load_self_public_key, load_self_public_key_opt, DcKey, Fingerprint, SignedPublicKey,
load_self_public_key, self_fingerprint, self_fingerprint_opt, DcKey, Fingerprint,
SignedPublicKey,
};
use crate::log::LogExt;
use crate::message::MessageState;
Expand Down Expand Up @@ -623,8 +624,8 @@ impl Contact {
.get_config(Config::ConfiguredAddr)
.await?
.unwrap_or_default();
if let Some(public_key) = load_self_public_key_opt(context).await? {
contact.fingerprint = Some(public_key.dc_fingerprint().hex());
if let Some(self_fp) = self_fingerprint_opt(context).await? {
contact.fingerprint = Some(self_fp.to_string());
}
contact.status = context
.get_config(Config::Selfstatus)
Expand Down Expand Up @@ -855,7 +856,7 @@ impl Contact {
}

if !fingerprint.is_empty() {
let fingerprint_self = load_self_public_key(context).await?.dc_fingerprint().hex();
let fingerprint_self = self_fingerprint(context).await?;
if fingerprint == fingerprint_self {
return Ok((ContactId::SELF, sth_modified));
}
Expand Down
14 changes: 10 additions & 4 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::ffi::OsString;
use std::ops::Deref;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::sync::{Arc, OnceLock};
use std::time::Duration;

use anyhow::{bail, ensure, Context as _, Result};
Expand All @@ -25,7 +25,7 @@ use crate::debug_logging::DebugLogging;
use crate::download::DownloadState;
use crate::events::{Event, EventEmitter, EventType, Events};
use crate::imap::{FolderMeaning, Imap, ServerMetadata};
use crate::key::{load_self_public_key, load_self_secret_key, DcKey as _};
use crate::key::{load_self_secret_key, self_fingerprint};
use crate::login_param::{ConfiguredLoginParam, EnteredLoginParam};
use crate::message::{self, Message, MessageState, MsgId};
use crate::param::{Param, Params};
Expand Down Expand Up @@ -297,6 +297,11 @@ pub struct InnerContext {

/// Iroh for realtime peer channels.
pub(crate) iroh: Arc<RwLock<Option<Iroh>>>,

/// The own fingerprint, if it was computed already.
/// tokio::sync::OnceCell would be possible to use, but overkill for our usecase;
/// the standard library's OnceLock is enough, and it's a lot smaller in memory.
pub(crate) self_fingerprint: OnceLock<String>,
}

/// The state of ongoing process.
Expand Down Expand Up @@ -456,6 +461,7 @@ impl Context {
push_subscriber,
push_subscribed: AtomicBool::new(false),
iroh: Arc::new(RwLock::new(None)),
self_fingerprint: OnceLock::new(),
};

let ctx = Context {
Expand Down Expand Up @@ -813,8 +819,8 @@ impl Context {
.sql
.count("SELECT COUNT(*) FROM public_keys;", ())
.await?;
let fingerprint_str = match load_self_public_key(self).await {
Ok(key) => key.dc_fingerprint().hex(),
let fingerprint_str = match self_fingerprint(self).await {
Ok(fp) => fp.to_string(),
Err(err) => format!("<key failure: {err}>"),
};

Expand Down
32 changes: 32 additions & 0 deletions src/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,38 @@ pub(crate) async fn load_self_public_keyring(context: &Context) -> Result<Vec<Si
Ok(keys)
}

/// Returns own public key fingerprint in (not human-readable) hex representation.
/// This is the fingerprint format that is used in the database.
///
/// If no key is generated yet, generates a new one.
///
/// For performance reasons, the fingerprint is cached after the first invocation.
pub(crate) async fn self_fingerprint(context: &Context) -> Result<&str> {
if let Some(fp) = context.self_fingerprint.get() {
Ok(fp)
} else {
let fp = load_self_public_key(context).await?.dc_fingerprint().hex();
Ok(context.self_fingerprint.get_or_init(|| fp))
}
}

/// Returns own public key fingerprint in (not human-readable) hex representation.
/// This is the fingerprint format that is used in the database.
///
/// Returns `None` if no key is generated yet.
///
/// For performance reasons, the fingerprint is cached after the first invocation.
pub(crate) async fn self_fingerprint_opt(context: &Context) -> Result<Option<&str>> {
if let Some(fp) = context.self_fingerprint.get() {
Ok(Some(fp))
} else if let Some(key) = load_self_public_key_opt(context).await? {
let fp = key.dc_fingerprint().hex();
Ok(Some(context.self_fingerprint.get_or_init(|| fp)))
} else {
Ok(None)
}
}

pub(crate) async fn load_self_secret_key(context: &Context) -> Result<SignedSecretKey> {
let private_key = context
.sql
Expand Down
9 changes: 4 additions & 5 deletions src/mimefactory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use crate::contact::{Contact, ContactId, Origin};
use crate::context::Context;
use crate::e2ee::EncryptHelper;
use crate::ephemeral::Timer as EphemeralTimer;
use crate::key::load_self_public_key;
use crate::key::self_fingerprint;
use crate::key::{DcKey, SignedPublicKey};
use crate::location;
use crate::message::{self, Message, MsgId, Viewtype};
Expand Down Expand Up @@ -204,8 +204,7 @@ impl MimeFactory {

let encryption_keys;

let self_public_key = load_self_public_key(context).await?;
let self_fingerprint = self_public_key.dc_fingerprint().hex();
let self_fingerprint = self_fingerprint(context).await?;

if chat.is_self_talk() {
to.push((from_displayname.to_string(), from_addr.to_string()));
Expand Down Expand Up @@ -311,7 +310,7 @@ impl MimeFactory {
if !fingerprint.is_empty() {
member_fingerprints.push(fingerprint);
} else if id == ContactId::SELF {
member_fingerprints.push(self_fingerprint.clone());
member_fingerprints.push(self_fingerprint.to_string());
} else {
debug_assert!(member_fingerprints.is_empty(), "If some past member is a PGP-contact, all other past members should be PGP-contacts too");
}
Expand Down Expand Up @@ -362,7 +361,7 @@ impl MimeFactory {
} else if id == ContactId::SELF {
// It's fine to have self in past members
// if we are leaving the group.
past_member_fingerprints.push(self_fingerprint.clone());
past_member_fingerprints.push(self_fingerprint.to_string());
} else {
debug_assert!(past_member_fingerprints.is_empty(), "If some past member is a PGP-contact, all other past members should be PGP-contacts too");
}
Expand Down
2 changes: 1 addition & 1 deletion src/mimeparser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ impl MimeMessage {
);
headers.retain(|k, _| {
!is_hidden(k) || {
headers_removed.insert(k.clone());
headers_removed.insert(k.to_string());
false
}
});
Expand Down
6 changes: 3 additions & 3 deletions src/receive_imf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use crate::ephemeral::{stock_ephemeral_timer_changed, Timer as EphemeralTimer};
use crate::events::EventType;
use crate::headerdef::{HeaderDef, HeaderDefMap};
use crate::imap::{markseen_on_imap_table, GENERATED_PREFIX};
use crate::key::load_self_public_key_opt;
use crate::key::self_fingerprint_opt;
use crate::key::{DcKey, Fingerprint, SignedPublicKey};
use crate::log::LogExt;
use crate::message::{
Expand Down Expand Up @@ -3366,8 +3366,8 @@ async fn lookup_pgp_contact_by_fingerprint(
.await?
{
Ok(Some(contact_id))
} else if let Some(self_public_key) = load_self_public_key_opt(context).await? {
if self_public_key.dc_fingerprint().hex() == fingerprint {
} else if let Some(self_fp) = self_fingerprint_opt(context).await? {
if self_fp == fingerprint {
Ok(Some(ContactId::SELF))
} else {
Ok(None)
Expand Down
6 changes: 3 additions & 3 deletions src/securejoin/bob.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::constants::{Blocked, Chattype};
use crate::contact::Origin;
use crate::context::Context;
use crate::events::EventType;
use crate::key::{load_self_public_key, DcKey};
use crate::key::self_fingerprint;
use crate::message::{Message, Viewtype};
use crate::mimeparser::{MimeMessage, SystemMessage};
use crate::param::Param;
Expand Down Expand Up @@ -273,8 +273,8 @@ pub(crate) async fn send_handshake_message(
msg.param.set_int(Param::GuaranteeE2ee, 1);

// Sends our own fingerprint in the Secure-Join-Fingerprint header.
let bob_fp = load_self_public_key(context).await?.dc_fingerprint();
msg.param.set(Param::Arg3, bob_fp.hex());
let bob_fp = self_fingerprint(context).await?;
msg.param.set(Param::Arg3, bob_fp);

// Sends the grpid in the Secure-Join-Group header.
//
Expand Down
16 changes: 7 additions & 9 deletions src/securejoin/securejoin_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use super::*;
use crate::chat::{remove_contact_from_chat, CantSendReason};
use crate::chatlist::Chatlist;
use crate::constants::Chattype;
use crate::key::self_fingerprint;
use crate::receive_imf::receive_imf;
use crate::stock_str::{self, chat_protection_enabled};
use crate::test_utils::{
Expand Down Expand Up @@ -177,13 +178,10 @@ async fn test_setup_contact_ex(case: SetupContactCase) {
"vc-request-with-auth"
);
assert!(msg.get_header(HeaderDef::SecureJoinAuth).is_some());
let bob_fp = load_self_public_key(&bob.ctx)
.await
.unwrap()
.dc_fingerprint();
let bob_fp = self_fingerprint(&bob).await.unwrap();
assert_eq!(
*msg.get_header(HeaderDef::SecureJoinFingerprint).unwrap(),
bob_fp.hex()
msg.get_header(HeaderDef::SecureJoinFingerprint).unwrap(),
bob_fp
);

if case == SetupContactCase::WrongAliceGossip {
Expand Down Expand Up @@ -498,10 +496,10 @@ async fn test_secure_join() -> Result<()> {
"vg-request-with-auth"
);
assert!(msg.get_header(HeaderDef::SecureJoinAuth).is_some());
let bob_fp = load_self_public_key(&bob).await?.dc_fingerprint();
let bob_fp = self_fingerprint(&bob).await?;
assert_eq!(
*msg.get_header(HeaderDef::SecureJoinFingerprint).unwrap(),
bob_fp.hex()
msg.get_header(HeaderDef::SecureJoinFingerprint).unwrap(),
bob_fp
);

// Alice should not yet have Bob verified
Expand Down
18 changes: 6 additions & 12 deletions src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use crate::contact::{
};
use crate::context::Context;
use crate::events::{Event, EventEmitter, EventType, Events};
use crate::key::{self, load_self_public_key, DcKey, DcSecretKey};
use crate::key::{self, self_fingerprint, DcKey, DcSecretKey};
use crate::message::{update_msg_state, Message, MessageState, MsgId, Viewtype};
use crate::mimeparser::{MimeMessage, SystemMessage};
use crate::pgp::KeyPair;
Expand Down Expand Up @@ -770,18 +770,12 @@ impl TestContext {
pub async fn add_or_lookup_pgp_contact(&self, other: &TestContext) -> Contact {
let primary_self_addr = other.ctx.get_primary_self_addr().await.unwrap();
let addr = ContactAddress::new(&primary_self_addr).unwrap();
let public_key = load_self_public_key(other).await.unwrap();
let fingerprint = public_key.dc_fingerprint();
let fingerprint = self_fingerprint(other).await.unwrap();

let (contact_id, _modified) = Contact::add_or_lookup_ex(
self,
"",
&addr,
&fingerprint.hex(),
Origin::MailinglistAddress,
)
.await
.expect("add_or_lookup");
let (contact_id, _modified) =
Contact::add_or_lookup_ex(self, "", &addr, fingerprint, Origin::MailinglistAddress)
.await
.expect("add_or_lookup");
Contact::get_by_id(&self.ctx, contact_id).await.unwrap()
}

Expand Down
4 changes: 2 additions & 2 deletions src/webxdc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ use crate::constants::Chattype;
use crate::contact::ContactId;
use crate::context::Context;
use crate::events::EventType;
use crate::key::{load_self_public_key, DcKey};
use crate::key::self_fingerprint;
use crate::message::{Message, MessageState, MsgId, Viewtype};
use crate::mimefactory::RECOMMENDED_FILE_SIZE;
use crate::mimeparser::SystemMessage;
Expand Down Expand Up @@ -962,7 +962,7 @@ impl Message {
}

async fn get_webxdc_self_addr(&self, context: &Context) -> Result<String> {
let fingerprint = load_self_public_key(context).await?.dc_fingerprint().hex();
let fingerprint = self_fingerprint(context).await?;
let data = format!("{}-{}", fingerprint, self.rfc724_mid);
let hash = Sha256::digest(data.as_bytes());
Ok(format!("{hash:x}"))
Expand Down
Loading