forked from whisperfish/presage
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loading status checks…
Add skeleton for sqlite store implementation (whisperfish#286)
Showing
12 changed files
with
640 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[package] | ||
name = "presage-store-sqlite" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
async-trait = "0.1.83" | ||
chrono = "0.4.38" | ||
presage = { path = "../presage" } | ||
presage-store-cipher = { path = "../presage-store-cipher", optional = true } | ||
|
||
sqlx = { version = "0.8.2", features = ["sqlite"] } | ||
thiserror = "1.0.65" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
use std::marker::PhantomData; | ||
|
||
use presage::{ | ||
libsignal_service::{prelude::Content, zkgroup::GroupMasterKeyBytes}, | ||
model::{contacts::Contact, groups::Group}, | ||
store::{ContentsStore, StickerPack}, | ||
}; | ||
|
||
use crate::{SqliteStore, SqliteStoreError}; | ||
|
||
impl ContentsStore for SqliteStore { | ||
type ContentsStoreError = SqliteStoreError; | ||
|
||
type ContactsIter = DummyIter<Result<Contact, Self::ContentsStoreError>>; | ||
|
||
type GroupsIter = DummyIter<Result<(GroupMasterKeyBytes, Group), Self::ContentsStoreError>>; | ||
|
||
type MessagesIter = DummyIter<Result<Content, Self::ContentsStoreError>>; | ||
|
||
type StickerPacksIter = DummyIter<Result<StickerPack, Self::ContentsStoreError>>; | ||
|
||
async fn clear_profiles(&mut self) -> Result<(), Self::ContentsStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn clear_contents(&mut self) -> Result<(), Self::ContentsStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn clear_messages(&mut self) -> Result<(), Self::ContentsStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn clear_thread( | ||
&mut self, | ||
thread: &presage::store::Thread, | ||
) -> Result<(), Self::ContentsStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn save_message( | ||
&self, | ||
thread: &presage::store::Thread, | ||
message: presage::libsignal_service::prelude::Content, | ||
) -> Result<(), Self::ContentsStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn delete_message( | ||
&mut self, | ||
thread: &presage::store::Thread, | ||
timestamp: u64, | ||
) -> Result<bool, Self::ContentsStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn message( | ||
&self, | ||
thread: &presage::store::Thread, | ||
timestamp: u64, | ||
) -> Result<Option<presage::libsignal_service::prelude::Content>, Self::ContentsStoreError> | ||
{ | ||
todo!() | ||
} | ||
|
||
async fn messages( | ||
&self, | ||
thread: &presage::store::Thread, | ||
range: impl std::ops::RangeBounds<u64>, | ||
) -> Result<Self::MessagesIter, Self::ContentsStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn clear_contacts(&mut self) -> Result<(), Self::ContentsStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn save_contact( | ||
&mut self, | ||
contacts: &presage::model::contacts::Contact, | ||
) -> Result<(), Self::ContentsStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn contacts(&self) -> Result<Self::ContactsIter, Self::ContentsStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn contact_by_id( | ||
&self, | ||
id: &presage::libsignal_service::prelude::Uuid, | ||
) -> Result<Option<presage::model::contacts::Contact>, Self::ContentsStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn clear_groups(&mut self) -> Result<(), Self::ContentsStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn save_group( | ||
&self, | ||
master_key: presage::libsignal_service::zkgroup::GroupMasterKeyBytes, | ||
group: impl Into<presage::model::groups::Group>, | ||
) -> Result<(), Self::ContentsStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn groups(&self) -> Result<Self::GroupsIter, Self::ContentsStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn group( | ||
&self, | ||
master_key: presage::libsignal_service::zkgroup::GroupMasterKeyBytes, | ||
) -> Result<Option<presage::model::groups::Group>, Self::ContentsStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn save_group_avatar( | ||
&self, | ||
master_key: presage::libsignal_service::zkgroup::GroupMasterKeyBytes, | ||
avatar: &presage::AvatarBytes, | ||
) -> Result<(), Self::ContentsStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn group_avatar( | ||
&self, | ||
master_key: presage::libsignal_service::zkgroup::GroupMasterKeyBytes, | ||
) -> Result<Option<presage::AvatarBytes>, Self::ContentsStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn upsert_profile_key( | ||
&mut self, | ||
uuid: &presage::libsignal_service::prelude::Uuid, | ||
key: presage::libsignal_service::prelude::ProfileKey, | ||
) -> Result<bool, Self::ContentsStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn profile_key( | ||
&self, | ||
uuid: &presage::libsignal_service::prelude::Uuid, | ||
) -> Result<Option<presage::libsignal_service::prelude::ProfileKey>, Self::ContentsStoreError> | ||
{ | ||
todo!() | ||
} | ||
|
||
async fn save_profile( | ||
&mut self, | ||
uuid: presage::libsignal_service::prelude::Uuid, | ||
key: presage::libsignal_service::prelude::ProfileKey, | ||
profile: presage::libsignal_service::Profile, | ||
) -> Result<(), Self::ContentsStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn profile( | ||
&self, | ||
uuid: presage::libsignal_service::prelude::Uuid, | ||
key: presage::libsignal_service::prelude::ProfileKey, | ||
) -> Result<Option<presage::libsignal_service::Profile>, Self::ContentsStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn save_profile_avatar( | ||
&mut self, | ||
uuid: presage::libsignal_service::prelude::Uuid, | ||
key: presage::libsignal_service::prelude::ProfileKey, | ||
profile: &presage::AvatarBytes, | ||
) -> Result<(), Self::ContentsStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn profile_avatar( | ||
&self, | ||
uuid: presage::libsignal_service::prelude::Uuid, | ||
key: presage::libsignal_service::prelude::ProfileKey, | ||
) -> Result<Option<presage::AvatarBytes>, Self::ContentsStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn add_sticker_pack( | ||
&mut self, | ||
pack: &presage::store::StickerPack, | ||
) -> Result<(), Self::ContentsStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn sticker_pack( | ||
&self, | ||
id: &[u8], | ||
) -> Result<Option<presage::store::StickerPack>, Self::ContentsStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn remove_sticker_pack(&mut self, id: &[u8]) -> Result<bool, Self::ContentsStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn sticker_packs(&self) -> Result<Self::StickerPacksIter, Self::ContentsStoreError> { | ||
todo!() | ||
} | ||
} | ||
|
||
pub struct DummyIter<T> { | ||
_data: PhantomData<T>, | ||
} | ||
|
||
impl<T> Iterator for DummyIter<T> { | ||
type Item = T; | ||
|
||
fn next(&mut self) -> Option<Self::Item> { | ||
todo!() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
use presage::store::StoreError; | ||
|
||
#[derive(Debug, thiserror::Error)] | ||
pub enum SqliteStoreError { | ||
#[error("database migration is not supported")] | ||
MigrationConflict, | ||
#[error("data store error: {0}")] | ||
Db(#[from] sqlx::Error), | ||
} | ||
|
||
impl StoreError for SqliteStoreError {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
#![allow(warnings)] | ||
|
||
use std::path::Path; | ||
|
||
use presage::{ | ||
model::identity::OnNewIdentity, | ||
store::{StateStore, Store}, | ||
}; | ||
use protocol::SqliteProtocolStore; | ||
use sqlx::{sqlite::SqliteConnectOptions, SqlitePool}; | ||
|
||
mod content; | ||
mod error; | ||
mod protocol; | ||
|
||
pub use error::SqliteStoreError; | ||
|
||
#[derive(Debug, Clone)] | ||
pub struct SqliteStore { | ||
db: SqlitePool, | ||
/// Whether to trust new identities automatically (for instance, when a somebody's phone has changed) | ||
trust_new_identities: OnNewIdentity, | ||
} | ||
|
||
impl SqliteStore { | ||
pub async fn open( | ||
db_path: impl AsRef<Path>, | ||
trust_new_identities: OnNewIdentity, | ||
) -> Result<Self, SqliteStoreError> { | ||
let connect_options = SqliteConnectOptions::new().filename(db_path); | ||
let pool = SqlitePool::connect_with(connect_options).await?; | ||
|
||
Ok(Self { | ||
db: pool, | ||
trust_new_identities, | ||
}) | ||
} | ||
} | ||
|
||
impl Store for SqliteStore { | ||
type Error = SqliteStoreError; | ||
|
||
type AciStore = SqliteProtocolStore; | ||
|
||
type PniStore = SqliteProtocolStore; | ||
|
||
async fn clear(&mut self) -> Result<(), SqliteStoreError> { | ||
todo!() | ||
} | ||
|
||
fn aci_protocol_store(&self) -> Self::AciStore { | ||
SqliteProtocolStore { | ||
store: self.clone(), | ||
} | ||
} | ||
|
||
fn pni_protocol_store(&self) -> Self::PniStore { | ||
SqliteProtocolStore { | ||
store: self.clone(), | ||
} | ||
} | ||
} | ||
|
||
impl StateStore for SqliteStore { | ||
type StateStoreError = SqliteStoreError; | ||
|
||
async fn load_registration_data( | ||
&self, | ||
) -> Result<Option<presage::manager::RegistrationData>, Self::StateStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn set_aci_identity_key_pair( | ||
&self, | ||
key_pair: presage::libsignal_service::protocol::IdentityKeyPair, | ||
) -> Result<(), Self::StateStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn set_pni_identity_key_pair( | ||
&self, | ||
key_pair: presage::libsignal_service::protocol::IdentityKeyPair, | ||
) -> Result<(), Self::StateStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn save_registration_data( | ||
&mut self, | ||
state: &presage::manager::RegistrationData, | ||
) -> Result<(), Self::StateStoreError> { | ||
todo!() | ||
} | ||
|
||
async fn is_registered(&self) -> bool { | ||
todo!() | ||
} | ||
|
||
async fn clear_registration(&mut self) -> Result<(), Self::StateStoreError> { | ||
todo!() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,283 @@ | ||
use async_trait::async_trait; | ||
use chrono::{DateTime, Utc}; | ||
use presage::libsignal_service::{ | ||
pre_keys::{KyberPreKeyStoreExt, PreKeysStore}, | ||
prelude::{IdentityKeyStore, SessionStoreExt, Uuid}, | ||
protocol::{ | ||
Direction, IdentityKey, IdentityKeyPair, KyberPreKeyId, KyberPreKeyRecord, | ||
KyberPreKeyStore, PreKeyId, PreKeyRecord, PreKeyStore, ProtocolAddress, ProtocolStore, | ||
SenderKeyRecord, SenderKeyStore, SessionRecord, SessionStore, | ||
SignalProtocolError as ProtocolError, SignedPreKeyId, SignedPreKeyRecord, | ||
SignedPreKeyStore, | ||
}, | ||
ServiceAddress, | ||
}; | ||
|
||
use crate::SqliteStore; | ||
|
||
#[derive(Clone)] | ||
pub struct SqliteProtocolStore { | ||
pub(crate) store: SqliteStore, | ||
} | ||
|
||
impl ProtocolStore for SqliteProtocolStore {} | ||
|
||
#[async_trait(?Send)] | ||
impl SessionStore for SqliteProtocolStore { | ||
/// Look up the session corresponding to `address`. | ||
async fn load_session( | ||
&self, | ||
address: &ProtocolAddress, | ||
) -> Result<Option<SessionRecord>, ProtocolError> { | ||
todo!() | ||
} | ||
|
||
/// Set the entry for `address` to the value of `record`. | ||
async fn store_session( | ||
&mut self, | ||
address: &ProtocolAddress, | ||
record: &SessionRecord, | ||
) -> Result<(), ProtocolError> { | ||
todo!(); | ||
} | ||
} | ||
|
||
#[async_trait(?Send)] | ||
impl SessionStoreExt for SqliteProtocolStore { | ||
/// Get the IDs of all known sub devices with active sessions for a recipient. | ||
/// | ||
/// This should return every device except for the main device [DEFAULT_DEVICE_ID]. | ||
async fn get_sub_device_sessions( | ||
&self, | ||
name: &ServiceAddress, | ||
) -> Result<Vec<u32>, ProtocolError> { | ||
todo!() | ||
} | ||
|
||
/// Remove a session record for a recipient ID + device ID tuple. | ||
async fn delete_session(&self, address: &ProtocolAddress) -> Result<(), ProtocolError> { | ||
todo!() | ||
} | ||
|
||
/// Remove the session records corresponding to all devices of a recipient | ||
/// ID. | ||
/// | ||
/// Returns the number of deleted sessions. | ||
async fn delete_all_sessions(&self, address: &ServiceAddress) -> Result<usize, ProtocolError> { | ||
todo!() | ||
} | ||
} | ||
|
||
#[async_trait(?Send)] | ||
impl PreKeyStore for SqliteProtocolStore { | ||
/// Look up the pre-key corresponding to `prekey_id`. | ||
async fn get_pre_key(&self, prekey_id: PreKeyId) -> Result<PreKeyRecord, ProtocolError> { | ||
todo!() | ||
} | ||
|
||
/// Set the entry for `prekey_id` to the value of `record`. | ||
async fn save_pre_key( | ||
&mut self, | ||
prekey_id: PreKeyId, | ||
record: &PreKeyRecord, | ||
) -> Result<(), ProtocolError> { | ||
todo!() | ||
} | ||
|
||
/// Remove the entry for `prekey_id`. | ||
async fn remove_pre_key(&mut self, prekey_id: PreKeyId) -> Result<(), ProtocolError> { | ||
todo!() | ||
} | ||
} | ||
|
||
#[async_trait(?Send)] | ||
impl PreKeysStore for SqliteProtocolStore { | ||
/// ID of the next pre key | ||
async fn next_pre_key_id(&self) -> Result<u32, ProtocolError> { | ||
todo!() | ||
} | ||
|
||
/// ID of the next signed pre key | ||
async fn next_signed_pre_key_id(&self) -> Result<u32, ProtocolError> { | ||
todo!() | ||
} | ||
|
||
/// ID of the next PQ pre key | ||
async fn next_pq_pre_key_id(&self) -> Result<u32, ProtocolError> { | ||
todo!() | ||
} | ||
|
||
/// number of signed pre-keys we currently have in store | ||
async fn signed_pre_keys_count(&self) -> Result<usize, ProtocolError> { | ||
todo!() | ||
} | ||
|
||
/// number of kyber pre-keys we currently have in store | ||
async fn kyber_pre_keys_count(&self, last_resort: bool) -> Result<usize, ProtocolError> { | ||
todo!() | ||
} | ||
} | ||
|
||
#[async_trait(?Send)] | ||
impl SignedPreKeyStore for SqliteProtocolStore { | ||
/// Look up the signed pre-key corresponding to `signed_prekey_id`. | ||
async fn get_signed_pre_key( | ||
&self, | ||
signed_prekey_id: SignedPreKeyId, | ||
) -> Result<SignedPreKeyRecord, ProtocolError> { | ||
todo!() | ||
} | ||
|
||
/// Set the entry for `signed_prekey_id` to the value of `record`. | ||
async fn save_signed_pre_key( | ||
&mut self, | ||
signed_prekey_id: SignedPreKeyId, | ||
record: &SignedPreKeyRecord, | ||
) -> Result<(), ProtocolError> { | ||
todo!() | ||
} | ||
} | ||
|
||
#[async_trait(?Send)] | ||
impl KyberPreKeyStore for SqliteProtocolStore { | ||
/// Look up the signed kyber pre-key corresponding to `kyber_prekey_id`. | ||
async fn get_kyber_pre_key( | ||
&self, | ||
kyber_prekey_id: KyberPreKeyId, | ||
) -> Result<KyberPreKeyRecord, ProtocolError> { | ||
todo!() | ||
} | ||
|
||
/// Set the entry for `kyber_prekey_id` to the value of `record`. | ||
async fn save_kyber_pre_key( | ||
&mut self, | ||
kyber_prekey_id: KyberPreKeyId, | ||
record: &KyberPreKeyRecord, | ||
) -> Result<(), ProtocolError> { | ||
todo!() | ||
} | ||
|
||
/// Mark the entry for `kyber_prekey_id` as "used". | ||
/// This would mean different things for one-time and last-resort Kyber keys. | ||
async fn mark_kyber_pre_key_used( | ||
&mut self, | ||
kyber_prekey_id: KyberPreKeyId, | ||
) -> Result<(), ProtocolError> { | ||
todo!() | ||
} | ||
} | ||
|
||
#[async_trait(?Send)] | ||
impl KyberPreKeyStoreExt for SqliteProtocolStore { | ||
async fn store_last_resort_kyber_pre_key( | ||
&mut self, | ||
kyber_prekey_id: KyberPreKeyId, | ||
record: &KyberPreKeyRecord, | ||
) -> Result<(), ProtocolError> { | ||
todo!() | ||
} | ||
|
||
async fn load_last_resort_kyber_pre_keys( | ||
&self, | ||
) -> Result<Vec<KyberPreKeyRecord>, ProtocolError> { | ||
todo!() | ||
} | ||
|
||
async fn remove_kyber_pre_key( | ||
&mut self, | ||
kyber_prekey_id: KyberPreKeyId, | ||
) -> Result<(), ProtocolError> { | ||
todo!() | ||
} | ||
|
||
/// Analogous to markAllOneTimeKyberPreKeysStaleIfNecessary | ||
async fn mark_all_one_time_kyber_pre_keys_stale_if_necessary( | ||
&mut self, | ||
stale_time: DateTime<Utc>, | ||
) -> Result<(), ProtocolError> { | ||
todo!() | ||
} | ||
|
||
/// Analogue of deleteAllStaleOneTimeKyberPreKeys | ||
async fn delete_all_stale_one_time_kyber_pre_keys( | ||
&mut self, | ||
threshold: DateTime<Utc>, | ||
min_count: usize, | ||
) -> Result<(), ProtocolError> { | ||
todo!() | ||
} | ||
} | ||
|
||
#[async_trait(?Send)] | ||
impl IdentityKeyStore for SqliteProtocolStore { | ||
/// Return the single specific identity the store is assumed to represent, with private key. | ||
async fn get_identity_key_pair(&self) -> Result<IdentityKeyPair, ProtocolError> { | ||
todo!() | ||
} | ||
|
||
/// Return a [u32] specific to this store instance. | ||
/// | ||
/// This local registration id is separate from the per-device identifier used in | ||
/// [ProtocolAddress] and should not change run over run. | ||
/// | ||
/// If the same *device* is unregistered, then registers again, the [ProtocolAddress::device_id] | ||
/// may be the same, but the store registration id returned by this method should | ||
/// be regenerated. | ||
async fn get_local_registration_id(&self) -> Result<u32, ProtocolError> { | ||
todo!() | ||
} | ||
|
||
// TODO: make this into an enum instead of a bool! | ||
/// Record an identity into the store. The identity is then considered "trusted". | ||
/// | ||
/// The return value represents whether an existing identity was replaced (`Ok(true)`). If it is | ||
/// new or hasn't changed, the return value should be `Ok(false)`. | ||
async fn save_identity( | ||
&mut self, | ||
address: &ProtocolAddress, | ||
identity: &IdentityKey, | ||
) -> Result<bool, ProtocolError> { | ||
todo!() | ||
} | ||
|
||
/// Return whether an identity is trusted for the role specified by `direction`. | ||
async fn is_trusted_identity( | ||
&self, | ||
address: &ProtocolAddress, | ||
identity: &IdentityKey, | ||
direction: Direction, | ||
) -> Result<bool, ProtocolError> { | ||
todo!() | ||
} | ||
|
||
/// Return the public identity for the given `address`, if known. | ||
async fn get_identity( | ||
&self, | ||
address: &ProtocolAddress, | ||
) -> Result<Option<IdentityKey>, ProtocolError> { | ||
todo!() | ||
} | ||
} | ||
|
||
#[async_trait(?Send)] | ||
impl SenderKeyStore for SqliteProtocolStore { | ||
/// Assign `record` to the entry for `(sender, distribution_id)`. | ||
async fn store_sender_key( | ||
&mut self, | ||
sender: &ProtocolAddress, | ||
distribution_id: Uuid, | ||
// TODO: pass this by value! | ||
record: &SenderKeyRecord, | ||
) -> Result<(), ProtocolError> { | ||
todo!() | ||
} | ||
|
||
/// Look up the entry corresponding to `(sender, distribution_id)`. | ||
async fn load_sender_key( | ||
&mut self, | ||
sender: &ProtocolAddress, | ||
distribution_id: Uuid, | ||
) -> Result<Option<SenderKeyRecord>, ProtocolError> { | ||
todo!() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
/// Whether to trust or reject new identities | ||
#[derive(Debug, Clone)] | ||
pub enum OnNewIdentity { | ||
Reject, | ||
Trust, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters