mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-04-19 00:52:53 +00:00
documentation and new modules
This commit is contained in:
parent
51f51f768b
commit
fc73e313ea
8 changed files with 304 additions and 146 deletions
29
rust/src/key_verification/mod.rs
Normal file
29
rust/src/key_verification/mod.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
use crate::key_verification::traits::KeyVerificationStore;
|
||||||
|
|
||||||
|
pub mod traits;
|
||||||
|
|
||||||
|
pub struct KeyVerificationConfig {
|
||||||
|
/// The link prefix for the qr code which should be registered as a deeplink on Android and a universal link on iOS
|
||||||
|
deeplink_prefix: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct KeyVerification<Store: KeyVerificationStore> {
|
||||||
|
store: Store,
|
||||||
|
config: KeyVerificationConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Store: KeyVerificationStore> KeyVerification<Store> {
|
||||||
|
pub fn new(store: Store, config: KeyVerificationConfig) -> KeyVerification<Store> {
|
||||||
|
Self { store, config }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates the a string which should be displayed in the UI so others can scan it.
|
||||||
|
pub fn generate_qr_code_data() -> String {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates the a string which should be displayed in the UI so others can scan it.
|
||||||
|
pub fn handle_qr_code_data() -> String {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
}
|
||||||
1
rust/src/key_verification/traits.rs
Normal file
1
rust/src/key_verification/traits.rs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
pub trait KeyVerificationStore {}
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
mod frb_generated;
|
mod frb_generated;
|
||||||
|
mod key_verification;
|
||||||
|
mod passwordless_recovery;
|
||||||
mod twonly;
|
mod twonly;
|
||||||
mod user_discovery;
|
mod user_discovery;
|
||||||
|
|
|
||||||
1
rust/src/passwordless_recovery/mod.rs
Normal file
1
rust/src/passwordless_recovery/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
mod traits;
|
||||||
0
rust/src/passwordless_recovery/traits.rs
Normal file
0
rust/src/passwordless_recovery/traits.rs
Normal file
|
|
@ -28,7 +28,7 @@ pub(crate) fn init_tracing(logs_dir: &std::path::Path) {
|
||||||
fn build_writers(logs_dir: &std::path::Path) -> (NonBlocking, NonBlocking) {
|
fn build_writers(logs_dir: &std::path::Path) -> (NonBlocking, NonBlocking) {
|
||||||
let file_appender = tracing_appender::rolling::RollingFileAppender::builder()
|
let file_appender = tracing_appender::rolling::RollingFileAppender::builder()
|
||||||
.rotation(tracing_appender::rolling::Rotation::DAILY)
|
.rotation(tracing_appender::rolling::Rotation::DAILY)
|
||||||
.filename_prefix("whitenoise")
|
.filename_prefix("twonly")
|
||||||
.filename_suffix("log")
|
.filename_suffix("log")
|
||||||
.build(logs_dir)
|
.build(logs_dir)
|
||||||
.expect("Failed to create file appender");
|
.expect("Failed to create file appender");
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,74 @@
|
||||||
|
|
||||||
|
# User Discovery
|
||||||
|
|
||||||
|
User Discovery is a feature that allows users to discover other users in a decentralized system without any central authority. It uses Shamir's Secret Sharing to securely share and discover user information.
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
The User Discovery module is composed of the following components:
|
||||||
|
|
||||||
|
- **UserDiscovery** - The main struct which initializes the user discovery and provides access to the user discovery functionality.
|
||||||
|
|
||||||
|
- **UserDiscoveryStore** - A trait which has to be implemented. It is used to store and retrieve the user discovery data.
|
||||||
|
- **UserDiscoveryUtils** - A trait which has to be implemented. It is used to perform signature verification and signing.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use crate::user_discovery::{UserDiscovery, UserID};
|
||||||
|
use crate::user_discovery::stores::InMemoryStore; // Replace with your persistent store
|
||||||
|
use crate::user_discovery::traits::tests::TestingUtils; // Replace with your utils
|
||||||
|
|
||||||
|
const THRESHOLD: u8 = 2;
|
||||||
|
|
||||||
|
// Initialize user discovery for Alice
|
||||||
|
const ALICE_ID: UserID = 1;
|
||||||
|
let alice_ud = UserDiscovery::new(InMemoryStore::default(), TestingUtils::default()).unwrap();
|
||||||
|
|
||||||
|
// Set threshold, user ID, and the user's public key
|
||||||
|
alice_ud.initialize_or_update(THRESHOLD, ALICE_ID, vec![0; 32]).unwrap();
|
||||||
|
|
||||||
|
// Initialize user discovery for Bob
|
||||||
|
const BOB_ID: UserID = 2;
|
||||||
|
let bob_ud = UserDiscovery::new(InMemoryStore::default(), TestingUtils::default()).unwrap();
|
||||||
|
bob_ud.initialize_or_update(THRESHOLD, BOB_ID, vec![0; 32]).unwrap();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// **Store** promotions:
|
// Simulate network communication: Alice sends her current version to Bob
|
||||||
// version_id, contact_id, Option<UserDiscoveryMessage>
|
let bob_received_version_from_alice = alice_ud.get_current_version().unwrap();
|
||||||
// -> In case a contact_id deleted his account deleted or was removed
|
|
||||||
// - Remove the previous row (version_id must be increased...)
|
// SEND FROM ALICE TO BOB: bob_received_version_from_alice
|
||||||
// - Create a new entry with User Discovery Recall
|
|
||||||
// -> Otherwise this promotion contains the Promotion
|
// Bob checks if he should request new messages
|
||||||
//
|
if bob_ud.should_request_new_messages(ALICE_ID, &bob_received_version_from_alice).unwrap() {
|
||||||
|
|
||||||
|
// Bob has a old version and must now request to get the new messages
|
||||||
|
|
||||||
|
// Bob fetches his current known version and sends it via the network to Alice
|
||||||
|
let bob_stored_alice_version = bob_ud.get_contact_version(ALICE_ID)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap_or_else(|| vec![0, 0]); // Note: In practice use actual default encoded version
|
||||||
|
|
||||||
|
// SEND FROM BOB TO ALICE: bob_stored_alice_version
|
||||||
|
|
||||||
|
// Alice loads the new messages for Bob. These only conclude changes since the provided version.
|
||||||
|
let new_messages = alice_ud.get_new_messages(BOB_ID, &bob_stored_alice_version).unwrap();
|
||||||
|
|
||||||
|
// SEND FROM ALICE TO BOB: new_messages
|
||||||
|
|
||||||
|
// Bob processes the received user discovery messages
|
||||||
|
bob_ud.handle_user_discovery_messages(ALICE_ID, new_messages).unwrap();
|
||||||
|
|
||||||
|
// BOB is now able to promote ALICE to his other contacts
|
||||||
|
}
|
||||||
|
|
||||||
|
// <Involve more users>
|
||||||
|
|
||||||
|
// 4. Retrieve all newly discovered users and relationships
|
||||||
|
// In this example now new users where discovered, to see a more comprehensive example
|
||||||
|
// see the test in the `mod.rs` fil.
|
||||||
|
let discovered_users = bob_ud.get_all_announced_users().unwrap();
|
||||||
|
for (user, connections) in discovered_users {
|
||||||
|
println!("Discovered User: {} (Public ID: {})", user.user_id, user.public_id);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
mod error;
|
mod error;
|
||||||
pub mod stores;
|
pub mod stores;
|
||||||
mod traits;
|
pub mod traits;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use blahaj::{Share, Sharks};
|
use blahaj::{Share, Sharks};
|
||||||
use postcard::{from_bytes, to_allocvec};
|
use postcard::{from_bytes, to_allocvec};
|
||||||
use prost::Message;
|
use prost::Message;
|
||||||
|
|
@ -19,6 +18,8 @@ pub use traits::UserDiscoveryStore;
|
||||||
static TRANSMITTED_NETWORK_BYTES: std::sync::OnceLock<std::sync::Mutex<usize>> =
|
static TRANSMITTED_NETWORK_BYTES: std::sync::OnceLock<std::sync::Mutex<usize>> =
|
||||||
std::sync::OnceLock::new();
|
std::sync::OnceLock::new();
|
||||||
|
|
||||||
|
/// Type of the user id, this must be consistent with the user id defined in
|
||||||
|
/// the types.proto
|
||||||
pub type UserID = i64;
|
pub type UserID = i64;
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/_.rs"));
|
include!(concat!(env!("OUT_DIR"), "/_.rs"));
|
||||||
|
|
@ -41,20 +42,11 @@ struct UserDiscoveryConfig {
|
||||||
user_id: UserID,
|
user_id: UserID,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for UserDiscoveryConfig {
|
///
|
||||||
fn default() -> Self {
|
/// The main struct to access the user discovery functionality.
|
||||||
Self {
|
///
|
||||||
threshold: 2,
|
/// As generic values it requires a UserDiscoveryStore and a UserDiscoveryUtils.
|
||||||
total_number_of_shares: 255,
|
///
|
||||||
announcement_version: 0,
|
|
||||||
promotion_version: 0,
|
|
||||||
verification_shares: vec![],
|
|
||||||
public_id: 0,
|
|
||||||
user_id: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct UserDiscovery<Store, Utils>
|
pub struct UserDiscovery<Store, Utils>
|
||||||
where
|
where
|
||||||
Store: UserDiscoveryStore,
|
Store: UserDiscoveryStore,
|
||||||
|
|
@ -65,10 +57,26 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Store: UserDiscoveryStore, Utils: UserDiscoveryUtils> UserDiscovery<Store, Utils> {
|
impl<Store: UserDiscoveryStore, Utils: UserDiscoveryUtils> UserDiscovery<Store, Utils> {
|
||||||
|
/// Creates a new instance of the user discovery.
|
||||||
pub fn new(store: Store, utils: Utils) -> Result<Self> {
|
pub fn new(store: Store, utils: Utils) -> Result<Self> {
|
||||||
Ok(Self { store, utils })
|
Ok(Self { store, utils })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Initializes or updates the user discovery.
|
||||||
|
///
|
||||||
|
/// This function will generate new verification shares and update the config.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `threshold` - The number of required shares to get the secret
|
||||||
|
/// * `user_id` - The owner's user id
|
||||||
|
/// * `public_key` - The owner's public key
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(())` - If the user discovery was initialized or updated successfully
|
||||||
|
/// * `Err(UserDiscoveryError)` - If the user discovery was not initialized or updated successfully
|
||||||
|
///
|
||||||
pub fn initialize_or_update(
|
pub fn initialize_or_update(
|
||||||
&self,
|
&self,
|
||||||
threshold: u8,
|
threshold: u8,
|
||||||
|
|
@ -109,6 +117,167 @@ impl<Store: UserDiscoveryStore, Utils: UserDiscoveryUtils> UserDiscovery<Store,
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Returns the current version of the owner's user discovery state.
|
||||||
|
///
|
||||||
|
/// The version is incremented every time the user discovery is initialized or updated.
|
||||||
|
/// It should be send contacts which participate in the user discovery, so they can
|
||||||
|
/// check if there is new data available for them using the `get_new_messages` function
|
||||||
|
/// on there side.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(Vec<u8>)` - The current version of the user discovery
|
||||||
|
/// * `Err(UserDiscoveryError)` - If there where errors in the store.
|
||||||
|
///
|
||||||
|
pub fn get_current_version(&self) -> Result<Vec<u8>> {
|
||||||
|
let config = self.get_config()?;
|
||||||
|
Ok(UserDiscoveryVersion {
|
||||||
|
announcement: config.announcement_version,
|
||||||
|
promotion: config.promotion_version,
|
||||||
|
}
|
||||||
|
.encode_to_vec())
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Returns all users discovery though the user discovery and there relations
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(HashMap<AnnouncedUser, Vec<(UserID, Option<i64>)>>)` - All connections the user has discovered
|
||||||
|
/// * `Err(UserDiscoveryError)` - If there where erros in the store.
|
||||||
|
///
|
||||||
|
pub fn get_all_announced_users(
|
||||||
|
&self,
|
||||||
|
) -> Result<HashMap<AnnouncedUser, Vec<(UserID, Option<i64>)>>> {
|
||||||
|
self.store.get_all_announced_users()
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Returns all new user discovery messages for the provided contact and his current version.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `contact_id` - The contact id of the user
|
||||||
|
/// * `received_version` - The version of the user discovery the contact has received so far
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(Vec<Vec<u8>>)` - The new user discovery messages
|
||||||
|
/// * `Err(UserDiscoveryError)` - If there where errors in the store or if the received version is invalid.
|
||||||
|
///
|
||||||
|
pub fn get_new_messages(
|
||||||
|
&self,
|
||||||
|
contact_id: UserID,
|
||||||
|
received_version: &[u8],
|
||||||
|
) -> Result<Vec<Vec<u8>>> {
|
||||||
|
let mut messages = vec![];
|
||||||
|
let received_version = UserDiscoveryVersion::decode(received_version)?;
|
||||||
|
|
||||||
|
let config = self.get_config()?;
|
||||||
|
let version = Some(UserDiscoveryVersion {
|
||||||
|
announcement: config.announcement_version,
|
||||||
|
promotion: config.promotion_version,
|
||||||
|
});
|
||||||
|
|
||||||
|
if received_version.announcement < config.announcement_version {
|
||||||
|
tracing::info!("New announcement message available for {}", contact_id);
|
||||||
|
|
||||||
|
let announcement_share = self.store.get_share_for_contact(contact_id)?;
|
||||||
|
|
||||||
|
let user_discovery_announcement = Some(UserDiscoveryAnnouncement {
|
||||||
|
public_id: config.public_id,
|
||||||
|
threshold: config.threshold as u32,
|
||||||
|
announcement_share,
|
||||||
|
verification_shares: config.verification_shares,
|
||||||
|
});
|
||||||
|
|
||||||
|
messages.push(
|
||||||
|
UserDiscoveryMessage {
|
||||||
|
user_discovery_announcement,
|
||||||
|
version,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
.encode_to_vec(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if received_version.promotion < config.promotion_version {
|
||||||
|
tracing::info!("New promotion message available for user {}", contact_id);
|
||||||
|
let promoting_messages = self
|
||||||
|
.store
|
||||||
|
.get_own_promotions_after_version(received_version.promotion)?;
|
||||||
|
messages.extend_from_slice(&promoting_messages);
|
||||||
|
}
|
||||||
|
#[cfg(test)]
|
||||||
|
{
|
||||||
|
let mut count = TRANSMITTED_NETWORK_BYTES.get().unwrap().lock().unwrap();
|
||||||
|
for message in &messages {
|
||||||
|
*count += message.len();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(messages)
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Checks if the provided user has new announcements and a request of update should be send.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `contact_id` - The contact id of the user
|
||||||
|
/// * `version` - The current version of the user discovery from the contact
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// * `Ok(bool)` - True if the user has new announcements
|
||||||
|
/// * `Err(UserDiscoveryError)` - If there where errors in the store or if the received version is invalid.
|
||||||
|
///
|
||||||
|
pub fn should_request_new_messages(&self, contact_id: UserID, version: &[u8]) -> Result<bool> {
|
||||||
|
let received_version = UserDiscoveryVersion::decode(version)?;
|
||||||
|
let stored_version = match self.store.get_contact_version(contact_id)? {
|
||||||
|
Some(buf) => UserDiscoveryVersion::decode(buf.as_slice())?,
|
||||||
|
None => UserDiscoveryVersion {
|
||||||
|
announcement: 0,
|
||||||
|
promotion: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Ok(received_version.announcement > stored_version.announcement
|
||||||
|
|| received_version.promotion > stored_version.promotion)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_contact_version(&self, contact_id: UserID) -> Result<Option<Vec<u8>>> {
|
||||||
|
self.store.get_contact_version(contact_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the latest version for this discovery.
|
||||||
|
/// Before calling this function the application must sure that contact_id is qualified to be announced.
|
||||||
|
pub fn handle_user_discovery_messages(
|
||||||
|
&self,
|
||||||
|
contact_id: UserID,
|
||||||
|
messages: Vec<Vec<u8>>,
|
||||||
|
) -> Result<()> {
|
||||||
|
for message in messages {
|
||||||
|
let message = UserDiscoveryMessage::decode(message.as_slice())?;
|
||||||
|
let Some(version) = message.version else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(uda) = message.user_discovery_announcement {
|
||||||
|
self.handle_user_discovery_announcement(contact_id, uda)?;
|
||||||
|
} else if let Some(udp) = message.user_discovery_promotion {
|
||||||
|
self.handle_user_discovery_promotion(contact_id, udp)?;
|
||||||
|
} else {
|
||||||
|
tracing::warn!("Got unknown user discovery messaging. Ignoring it.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.store
|
||||||
|
.set_contact_version(contact_id, version.encode_to_vec())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn setup_announcements(
|
fn setup_announcements(
|
||||||
&self,
|
&self,
|
||||||
config: &UserDiscoveryConfig,
|
config: &UserDiscoveryConfig,
|
||||||
|
|
@ -164,128 +333,6 @@ impl<Store: UserDiscoveryStore, Utils: UserDiscoveryUtils> UserDiscovery<Store,
|
||||||
Ok(from_bytes(&self.store.get_config()?)?)
|
Ok(from_bytes(&self.store.get_config()?)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the current version of the user discovery.
|
|
||||||
pub fn get_current_version(&self) -> Result<Vec<u8>> {
|
|
||||||
let config = self.get_config()?;
|
|
||||||
Ok(UserDiscoveryVersion {
|
|
||||||
announcement: config.announcement_version,
|
|
||||||
promotion: config.promotion_version,
|
|
||||||
}
|
|
||||||
.encode_to_vec())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns all users discovery though the user discovery and there relations
|
|
||||||
pub fn get_all_announced_users(
|
|
||||||
&self,
|
|
||||||
) -> Result<HashMap<AnnouncedUser, Vec<(UserID, Option<i64>)>>> {
|
|
||||||
self.store.get_all_announced_users()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns all new user discovery messages for the provided contact
|
|
||||||
pub fn get_new_messages(
|
|
||||||
&self,
|
|
||||||
contact_id: UserID,
|
|
||||||
received_version: &[u8],
|
|
||||||
) -> Result<Vec<Vec<u8>>> {
|
|
||||||
let mut messages = vec![];
|
|
||||||
let received_version = UserDiscoveryVersion::decode(received_version)?;
|
|
||||||
|
|
||||||
let config = self.get_config()?;
|
|
||||||
let version = Some(UserDiscoveryVersion {
|
|
||||||
announcement: config.announcement_version,
|
|
||||||
promotion: config.promotion_version,
|
|
||||||
});
|
|
||||||
|
|
||||||
if received_version.announcement < config.announcement_version {
|
|
||||||
tracing::info!("New announcement message available for {}", contact_id);
|
|
||||||
|
|
||||||
let announcement_share = self.store.get_share_for_contact(contact_id)?;
|
|
||||||
|
|
||||||
let user_discovery_announcement = Some(UserDiscoveryAnnouncement {
|
|
||||||
public_id: config.public_id,
|
|
||||||
threshold: config.threshold as u32,
|
|
||||||
announcement_share,
|
|
||||||
verification_shares: config.verification_shares,
|
|
||||||
});
|
|
||||||
|
|
||||||
messages.push(
|
|
||||||
UserDiscoveryMessage {
|
|
||||||
user_discovery_announcement,
|
|
||||||
version,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.encode_to_vec(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// messages.push(value);
|
|
||||||
}
|
|
||||||
if received_version.promotion < config.promotion_version {
|
|
||||||
tracing::info!("New promotion message available for user {}", contact_id);
|
|
||||||
let promoting_messages = self
|
|
||||||
.store
|
|
||||||
.get_own_promotions_after_version(received_version.promotion)?;
|
|
||||||
messages.extend_from_slice(&promoting_messages);
|
|
||||||
}
|
|
||||||
#[cfg(test)]
|
|
||||||
{
|
|
||||||
let mut count = TRANSMITTED_NETWORK_BYTES.get().unwrap().lock().unwrap();
|
|
||||||
for message in &messages {
|
|
||||||
*count += message.len();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// static TRANSMITTED_NETWORK_BYTES: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
|
|
||||||
Ok(messages)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if the provided user has new announcements.
|
|
||||||
/// In this case the user should be requested to send there updates.
|
|
||||||
pub fn should_request_new_messages(&self, contact_id: UserID, version: &[u8]) -> Result<bool> {
|
|
||||||
let received_version = UserDiscoveryVersion::decode(version)?;
|
|
||||||
let stored_version = match self.store.get_contact_version(contact_id)? {
|
|
||||||
Some(buf) => UserDiscoveryVersion::decode(buf.as_slice())?,
|
|
||||||
None => UserDiscoveryVersion {
|
|
||||||
announcement: 0,
|
|
||||||
promotion: 0,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
// tracing::debug!("{received_version:?} > {stored_version:?}");
|
|
||||||
Ok(received_version.announcement > stored_version.announcement
|
|
||||||
|| received_version.promotion > stored_version.promotion)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get_contact_version(&self, contact_id: UserID) -> Result<Option<Vec<u8>>> {
|
|
||||||
self.store.get_contact_version(contact_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the latest version for this discovery.
|
|
||||||
/// Before calling this function the application must sure that contact_id is qualified to be announced.
|
|
||||||
pub fn handle_user_discovery_messages(
|
|
||||||
&self,
|
|
||||||
contact_id: UserID,
|
|
||||||
messages: Vec<Vec<u8>>,
|
|
||||||
) -> Result<()> {
|
|
||||||
for message in messages {
|
|
||||||
let message = UserDiscoveryMessage::decode(message.as_slice())?;
|
|
||||||
let Some(version) = message.version else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(uda) = message.user_discovery_announcement {
|
|
||||||
self.handle_user_discovery_announcement(contact_id, uda)?;
|
|
||||||
} else if let Some(udp) = message.user_discovery_promotion {
|
|
||||||
self.handle_user_discovery_promotion(contact_id, udp)?;
|
|
||||||
} else {
|
|
||||||
tracing::warn!("Got unknown user discovery messaging. Ignoring it.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.store
|
|
||||||
.set_contact_version(contact_id, version.encode_to_vec())?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_user_discovery_announcement(
|
fn handle_user_discovery_announcement(
|
||||||
&self,
|
&self,
|
||||||
contact_id: UserID,
|
contact_id: UserID,
|
||||||
|
|
@ -505,6 +552,20 @@ impl<Store: UserDiscoveryStore, Utils: UserDiscoveryUtils> UserDiscovery<Store,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for UserDiscoveryConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
threshold: 2,
|
||||||
|
total_number_of_shares: 255,
|
||||||
|
announcement_version: 0,
|
||||||
|
promotion_version: 0,
|
||||||
|
verification_shares: vec![],
|
||||||
|
public_id: 0,
|
||||||
|
user_id: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue