remove old version and fixes duplicated shares

This commit is contained in:
otsmr 2026-04-21 22:47:21 +02:00
parent 954eedd40e
commit e1f28e1b87
21 changed files with 567 additions and 9608 deletions

View file

@ -21,7 +21,7 @@ Future<void> initFlutterCallbacks({
required FutureOr<Uint8List?> Function(PlatformInt64) required FutureOr<Uint8List?> Function(PlatformInt64)
userDiscoveryGetShareForContact, userDiscoveryGetShareForContact,
required FutureOr<bool> Function(PlatformInt64, PlatformInt64, Uint8List) required FutureOr<bool> Function(PlatformInt64, PlatformInt64, Uint8List)
userDiscoveryPushOwnPromotion, userDiscoveryPushOwnPromotionAndClearOldVersion,
required FutureOr<List<Uint8List>?> Function(PlatformInt64) required FutureOr<List<Uint8List>?> Function(PlatformInt64)
userDiscoveryGetOwnPromotionsAfterVersion, userDiscoveryGetOwnPromotionsAfterVersion,
required FutureOr<bool> Function(OtherPromotion) required FutureOr<bool> Function(OtherPromotion)
@ -43,7 +43,8 @@ Future<void> initFlutterCallbacks({
userDiscoveryVerifyStoredPubkey: userDiscoveryVerifyStoredPubkey, userDiscoveryVerifyStoredPubkey: userDiscoveryVerifyStoredPubkey,
userDiscoverySetShares: userDiscoverySetShares, userDiscoverySetShares: userDiscoverySetShares,
userDiscoveryGetShareForContact: userDiscoveryGetShareForContact, userDiscoveryGetShareForContact: userDiscoveryGetShareForContact,
userDiscoveryPushOwnPromotion: userDiscoveryPushOwnPromotion, userDiscoveryPushOwnPromotionAndClearOldVersion:
userDiscoveryPushOwnPromotionAndClearOldVersion,
userDiscoveryGetOwnPromotionsAfterVersion: userDiscoveryGetOwnPromotionsAfterVersion:
userDiscoveryGetOwnPromotionsAfterVersion, userDiscoveryGetOwnPromotionsAfterVersion,
userDiscoveryStoreOtherPromotion: userDiscoveryStoreOtherPromotion, userDiscoveryStoreOtherPromotion: userDiscoveryStoreOtherPromotion,

View file

@ -121,7 +121,7 @@ abstract class RustLibApi extends BaseApi {
required FutureOr<Uint8List?> Function(PlatformInt64) required FutureOr<Uint8List?> Function(PlatformInt64)
userDiscoveryGetShareForContact, userDiscoveryGetShareForContact,
required FutureOr<bool> Function(PlatformInt64, PlatformInt64, Uint8List) required FutureOr<bool> Function(PlatformInt64, PlatformInt64, Uint8List)
userDiscoveryPushOwnPromotion, userDiscoveryPushOwnPromotionAndClearOldVersion,
required FutureOr<List<Uint8List>?> Function(PlatformInt64) required FutureOr<List<Uint8List>?> Function(PlatformInt64)
userDiscoveryGetOwnPromotionsAfterVersion, userDiscoveryGetOwnPromotionsAfterVersion,
required FutureOr<bool> Function(OtherPromotion) required FutureOr<bool> Function(OtherPromotion)
@ -354,7 +354,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
required FutureOr<Uint8List?> Function(PlatformInt64) required FutureOr<Uint8List?> Function(PlatformInt64)
userDiscoveryGetShareForContact, userDiscoveryGetShareForContact,
required FutureOr<bool> Function(PlatformInt64, PlatformInt64, Uint8List) required FutureOr<bool> Function(PlatformInt64, PlatformInt64, Uint8List)
userDiscoveryPushOwnPromotion, userDiscoveryPushOwnPromotionAndClearOldVersion,
required FutureOr<List<Uint8List>?> Function(PlatformInt64) required FutureOr<List<Uint8List>?> Function(PlatformInt64)
userDiscoveryGetOwnPromotionsAfterVersion, userDiscoveryGetOwnPromotionsAfterVersion,
required FutureOr<bool> Function(OtherPromotion) required FutureOr<bool> Function(OtherPromotion)
@ -403,7 +403,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
serializer, serializer,
); );
sse_encode_DartFn_Inputs_i_64_i_64_list_prim_u_8_strict_Output_bool_AnyhowException( sse_encode_DartFn_Inputs_i_64_i_64_list_prim_u_8_strict_Output_bool_AnyhowException(
userDiscoveryPushOwnPromotion, userDiscoveryPushOwnPromotionAndClearOldVersion,
serializer, serializer,
); );
sse_encode_DartFn_Inputs_i_64_Output_opt_list_list_prim_u_8_strict_AnyhowException( sse_encode_DartFn_Inputs_i_64_Output_opt_list_list_prim_u_8_strict_AnyhowException(
@ -453,7 +453,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
userDiscoveryVerifyStoredPubkey, userDiscoveryVerifyStoredPubkey,
userDiscoverySetShares, userDiscoverySetShares,
userDiscoveryGetShareForContact, userDiscoveryGetShareForContact,
userDiscoveryPushOwnPromotion, userDiscoveryPushOwnPromotionAndClearOldVersion,
userDiscoveryGetOwnPromotionsAfterVersion, userDiscoveryGetOwnPromotionsAfterVersion,
userDiscoveryStoreOtherPromotion, userDiscoveryStoreOtherPromotion,
userDiscoveryGetOtherPromotionsByPublicId, userDiscoveryGetOtherPromotionsByPublicId,
@ -477,7 +477,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
"userDiscoveryVerifyStoredPubkey", "userDiscoveryVerifyStoredPubkey",
"userDiscoverySetShares", "userDiscoverySetShares",
"userDiscoveryGetShareForContact", "userDiscoveryGetShareForContact",
"userDiscoveryPushOwnPromotion", "userDiscoveryPushOwnPromotionAndClearOldVersion",
"userDiscoveryGetOwnPromotionsAfterVersion", "userDiscoveryGetOwnPromotionsAfterVersion",
"userDiscoveryStoreOtherPromotion", "userDiscoveryStoreOtherPromotion",
"userDiscoveryGetOtherPromotionsByPublicId", "userDiscoveryGetOtherPromotionsByPublicId",

View file

@ -8,7 +8,8 @@ Future<void> initFlutterCallbacksForRust() async {
userDiscoverySetShares: UserDiscoveryCallbacks.setShares, userDiscoverySetShares: UserDiscoveryCallbacks.setShares,
userDiscoveryGetShareForContact: userDiscoveryGetShareForContact:
UserDiscoveryCallbacks.userDiscoveryGetShareForContact, UserDiscoveryCallbacks.userDiscoveryGetShareForContact,
userDiscoveryPushOwnPromotion: UserDiscoveryCallbacks.pushOwnPromotion, userDiscoveryPushOwnPromotionAndClearOldVersion:
UserDiscoveryCallbacks.userDiscoveryPushOwnPromotionAndClearOldVersion,
userDiscoveryPushNewUserRelation: userDiscoveryPushNewUserRelation:
UserDiscoveryCallbacks.pushNewUserRelation, UserDiscoveryCallbacks.pushNewUserRelation,
userDiscoveryGetOwnPromotionsAfterVersion: userDiscoveryGetOwnPromotionsAfterVersion:

View file

@ -110,12 +110,18 @@ class UserDiscoveryCallbacks {
}); });
} }
static Future<bool> pushOwnPromotion( static Future<bool> userDiscoveryPushOwnPromotionAndClearOldVersion(
int contactId, int contactId,
int version, // Maps to versionId or logic control int version,
Uint8List promotion, Uint8List promotion,
) async { ) async {
try { try {
// Old promotions from this users should be removed...
await (twonlyDB.update(
twonlyDB.userDiscoveryOwnPromotions,
)..where((t) => t.contactId.equals(contactId))).write(
UserDiscoveryOwnPromotionsCompanion(promotion: Value(Uint8List(0))),
);
await twonlyDB await twonlyDB
.into(twonlyDB.userDiscoveryOwnPromotions) .into(twonlyDB.userDiscoveryOwnPromotions)
.insert( .insert(

View file

@ -178,6 +178,40 @@
"default_dart": null, "default_dart": null,
"default_client_dart": null, "default_client_dart": null,
"dsl_features": [] "dsl_features": []
},
{
"name": "user_discovery_excluded",
"getter_name": "userDiscoveryExcluded",
"moor_type": "bool",
"nullable": false,
"customConstraints": null,
"defaultConstraints": "CHECK (\"user_discovery_excluded\" IN (0, 1))",
"dialectAwareDefaultConstraints": {
"sqlite": "CHECK (\"user_discovery_excluded\" IN (0, 1))"
},
"default_dart": "const CustomExpression('0')",
"default_client_dart": null,
"dsl_features": []
},
{
"name": "media_send_counter",
"getter_name": "mediaSendCounter",
"moor_type": "int",
"nullable": false,
"customConstraints": null,
"default_dart": "const CustomExpression('0')",
"default_client_dart": null,
"dsl_features": []
},
{
"name": "media_received_counter",
"getter_name": "mediaReceivedCounter",
"moor_type": "int",
"nullable": false,
"customConstraints": null,
"default_dart": "const CustomExpression('0')",
"default_client_dart": null,
"dsl_features": []
} }
], ],
"is_virtual": false, "is_virtual": false,
@ -2126,6 +2160,44 @@
"dsl_features": [ "dsl_features": [
"unique" "unique"
] ]
},
{
"name": "username",
"getter_name": "username",
"moor_type": "string",
"nullable": true,
"customConstraints": null,
"default_dart": null,
"default_client_dart": null,
"dsl_features": []
},
{
"name": "was_shown_to_the_user",
"getter_name": "wasShownToTheUser",
"moor_type": "bool",
"nullable": false,
"customConstraints": null,
"defaultConstraints": "CHECK (\"was_shown_to_the_user\" IN (0, 1))",
"dialectAwareDefaultConstraints": {
"sqlite": "CHECK (\"was_shown_to_the_user\" IN (0, 1))"
},
"default_dart": "const CustomExpression('0')",
"default_client_dart": null,
"dsl_features": []
},
{
"name": "is_hidden",
"getter_name": "isHidden",
"moor_type": "bool",
"nullable": false,
"customConstraints": null,
"defaultConstraints": "CHECK (\"is_hidden\" IN (0, 1))",
"dialectAwareDefaultConstraints": {
"sqlite": "CHECK (\"is_hidden\" IN (0, 1))"
},
"default_dart": "const CustomExpression('0')",
"default_client_dart": null,
"dsl_features": []
} }
], ],
"is_virtual": false, "is_virtual": false,
@ -2311,7 +2383,7 @@
"constraints": [], "constraints": [],
"explicit_pk": [ "explicit_pk": [
"from_contact_id", "from_contact_id",
"promotion_id" "public_id"
] ]
} }
}, },
@ -2458,7 +2530,7 @@
"sql": [ "sql": [
{ {
"dialect": "sqlite", "dialect": "sqlite",
"sql": "CREATE TABLE IF NOT EXISTS \"contacts\" (\"user_id\" INTEGER NOT NULL, \"username\" TEXT NOT NULL, \"display_name\" TEXT NULL, \"nick_name\" TEXT NULL, \"avatar_svg_compressed\" BLOB NULL, \"sender_profile_counter\" INTEGER NOT NULL DEFAULT 0, \"accepted\" INTEGER NOT NULL DEFAULT 0 CHECK (\"accepted\" IN (0, 1)), \"deleted_by_user\" INTEGER NOT NULL DEFAULT 0 CHECK (\"deleted_by_user\" IN (0, 1)), \"requested\" INTEGER NOT NULL DEFAULT 0 CHECK (\"requested\" IN (0, 1)), \"blocked\" INTEGER NOT NULL DEFAULT 0 CHECK (\"blocked\" IN (0, 1)), \"verified\" INTEGER NOT NULL DEFAULT 0 CHECK (\"verified\" IN (0, 1)), \"account_deleted\" INTEGER NOT NULL DEFAULT 0 CHECK (\"account_deleted\" IN (0, 1)), \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), \"user_discovery_version\" BLOB NULL, PRIMARY KEY (\"user_id\"));" "sql": "CREATE TABLE IF NOT EXISTS \"contacts\" (\"user_id\" INTEGER NOT NULL, \"username\" TEXT NOT NULL, \"display_name\" TEXT NULL, \"nick_name\" TEXT NULL, \"avatar_svg_compressed\" BLOB NULL, \"sender_profile_counter\" INTEGER NOT NULL DEFAULT 0, \"accepted\" INTEGER NOT NULL DEFAULT 0 CHECK (\"accepted\" IN (0, 1)), \"deleted_by_user\" INTEGER NOT NULL DEFAULT 0 CHECK (\"deleted_by_user\" IN (0, 1)), \"requested\" INTEGER NOT NULL DEFAULT 0 CHECK (\"requested\" IN (0, 1)), \"blocked\" INTEGER NOT NULL DEFAULT 0 CHECK (\"blocked\" IN (0, 1)), \"verified\" INTEGER NOT NULL DEFAULT 0 CHECK (\"verified\" IN (0, 1)), \"account_deleted\" INTEGER NOT NULL DEFAULT 0 CHECK (\"account_deleted\" IN (0, 1)), \"created_at\" INTEGER NOT NULL DEFAULT (CAST(strftime('%s', CURRENT_TIMESTAMP) AS INTEGER)), \"user_discovery_version\" BLOB NULL, \"user_discovery_excluded\" INTEGER NOT NULL DEFAULT 0 CHECK (\"user_discovery_excluded\" IN (0, 1)), \"media_send_counter\" INTEGER NOT NULL DEFAULT 0, \"media_received_counter\" INTEGER NOT NULL DEFAULT 0, PRIMARY KEY (\"user_id\"));"
} }
] ]
}, },
@ -2611,7 +2683,7 @@
"sql": [ "sql": [
{ {
"dialect": "sqlite", "dialect": "sqlite",
"sql": "CREATE TABLE IF NOT EXISTS \"user_discovery_announced_users\" (\"announced_user_id\" INTEGER NOT NULL, \"announced_public_key\" BLOB NOT NULL, \"public_id\" INTEGER NOT NULL UNIQUE, PRIMARY KEY (\"announced_user_id\"));" "sql": "CREATE TABLE IF NOT EXISTS \"user_discovery_announced_users\" (\"announced_user_id\" INTEGER NOT NULL, \"announced_public_key\" BLOB NOT NULL, \"public_id\" INTEGER NOT NULL UNIQUE, \"username\" TEXT NULL, \"was_shown_to_the_user\" INTEGER NOT NULL DEFAULT 0 CHECK (\"was_shown_to_the_user\" IN (0, 1)), \"is_hidden\" INTEGER NOT NULL DEFAULT 0 CHECK (\"is_hidden\" IN (0, 1)), PRIMARY KEY (\"announced_user_id\"));"
} }
] ]
}, },
@ -2629,7 +2701,7 @@
"sql": [ "sql": [
{ {
"dialect": "sqlite", "dialect": "sqlite",
"sql": "CREATE TABLE IF NOT EXISTS \"user_discovery_other_promotions\" (\"from_contact_id\" INTEGER NOT NULL REFERENCES contacts (user_id) ON DELETE CASCADE, \"promotion_id\" INTEGER NOT NULL, \"public_id\" INTEGER NOT NULL, \"threshold\" INTEGER NOT NULL, \"announcement_share\" BLOB NOT NULL, \"public_key_verified_timestamp\" INTEGER NULL, PRIMARY KEY (\"from_contact_id\", \"promotion_id\"));" "sql": "CREATE TABLE IF NOT EXISTS \"user_discovery_other_promotions\" (\"from_contact_id\" INTEGER NOT NULL REFERENCES contacts (user_id) ON DELETE CASCADE, \"promotion_id\" INTEGER NOT NULL, \"public_id\" INTEGER NOT NULL, \"threshold\" INTEGER NOT NULL, \"announcement_share\" BLOB NOT NULL, \"public_key_verified_timestamp\" INTEGER NULL, PRIMARY KEY (\"from_contact_id\", \"public_id\"));"
} }
] ]
}, },

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -70,7 +70,7 @@ class UserDiscoveryOtherPromotions extends Table {
DateTimeColumn get publicKeyVerifiedTimestamp => dateTime().nullable()(); DateTimeColumn get publicKeyVerifiedTimestamp => dateTime().nullable()();
@override @override
Set<Column> get primaryKey => {fromContactId, promotionId}; Set<Column> get primaryKey => {fromContactId, publicId};
} }
// unused_shares: Vec<Vec<u8>>, // unused_shares: Vec<Vec<u8>>,

View file

@ -72,7 +72,7 @@ class TwonlyDB extends _$TwonlyDB {
TwonlyDB.forTesting(DatabaseConnection super.connection); TwonlyDB.forTesting(DatabaseConnection super.connection);
@override @override
int get schemaVersion => 15; int get schemaVersion => 12;
static QueryExecutor _openConnection() { static QueryExecutor _openConnection() {
return driftDatabase( return driftDatabase(
@ -177,8 +177,6 @@ class TwonlyDB extends _$TwonlyDB {
schema.contacts, schema.contacts,
schema.contacts.userDiscoveryVersion, schema.contacts.userDiscoveryVersion,
); );
},
from12To13: (m, schema) async {
await m.addColumn( await m.addColumn(
schema.contacts, schema.contacts,
schema.contacts.mediaReceivedCounter, schema.contacts.mediaReceivedCounter,
@ -187,22 +185,6 @@ class TwonlyDB extends _$TwonlyDB {
schema.contacts, schema.contacts,
schema.contacts.mediaSendCounter, schema.contacts.mediaSendCounter,
); );
},
from13To14: (m, schema) async {
await m.addColumn(
schema.userDiscoveryAnnouncedUsers,
schema.userDiscoveryAnnouncedUsers.wasShownToTheUser,
);
await m.addColumn(
schema.userDiscoveryAnnouncedUsers,
schema.userDiscoveryAnnouncedUsers.isHidden,
);
await m.addColumn(
schema.userDiscoveryAnnouncedUsers,
schema.userDiscoveryAnnouncedUsers.username,
);
},
from14To15: (m, schema) async {
await m.addColumn( await m.addColumn(
schema.contacts, schema.contacts,
schema.contacts.userDiscoveryExcluded, schema.contacts.userDiscoveryExcluded,

View file

@ -10478,7 +10478,7 @@ class $UserDiscoveryOtherPromotionsTable extends UserDiscoveryOtherPromotions
} }
@override @override
Set<GeneratedColumn> get $primaryKey => {fromContactId, promotionId}; Set<GeneratedColumn> get $primaryKey => {fromContactId, publicId};
@override @override
UserDiscoveryOtherPromotion map( UserDiscoveryOtherPromotion map(
Map<String, dynamic> data, { Map<String, dynamic> data, {

File diff suppressed because it is too large Load diff

View file

@ -28,7 +28,7 @@ callback_generator! {
// UserDiscoveryStore // UserDiscoveryStore
set_shares: (Vec<Vec<u8>>) => bool, set_shares: (Vec<Vec<u8>>) => bool,
get_share_for_contact: (i64) => Option<Vec<u8>>, get_share_for_contact: (i64) => Option<Vec<u8>>,
push_own_promotion: (i64, i64, Vec<u8>) => bool, push_own_promotion_and_clear_old_version: (i64, i64, Vec<u8>) => bool,
get_own_promotions_after_version: (i64) => Option<Vec<Vec<u8>>>, get_own_promotions_after_version: (i64) => Option<Vec<Vec<u8>>>,
store_other_promotion: (OtherPromotion) => bool, store_other_promotion: (OtherPromotion) => bool,
get_other_promotions_by_public_id: (i64) => Option<Vec<OtherPromotion>>, get_other_promotions_by_public_id: (i64) => Option<Vec<OtherPromotion>>,

View file

@ -79,13 +79,13 @@ impl UserDiscoveryStore for UserDiscoveryStoreFlutter {
} }
} }
async fn push_own_promotion( async fn push_own_promotion_and_clear_old_version(
&self, &self,
contact_id: i64, contact_id: i64,
version: u32, version: u32,
promotion: Vec<u8>, promotion: Vec<u8>,
) -> Result<()> { ) -> Result<()> {
(get_callbacks()?.user_discovery.push_own_promotion)(contact_id, version as i64, promotion) (get_callbacks()?.user_discovery.push_own_promotion_and_clear_old_version)(contact_id, version as i64, promotion)
.await .await
.then_some(()) .then_some(())
.ok_or(TwonlyError::DartError.into()) .ok_or(TwonlyError::DartError.into())

View file

@ -141,7 +141,7 @@ let api_user_discovery_verify_signature = decode_DartFn_Inputs_list_prim_u_8_str
let api_user_discovery_verify_stored_pubkey = decode_DartFn_Inputs_i_64_list_prim_u_8_strict_Output_bool_AnyhowException(<flutter_rust_bridge::DartOpaque>::sse_decode(&mut deserializer)); let api_user_discovery_verify_stored_pubkey = decode_DartFn_Inputs_i_64_list_prim_u_8_strict_Output_bool_AnyhowException(<flutter_rust_bridge::DartOpaque>::sse_decode(&mut deserializer));
let api_user_discovery_set_shares = decode_DartFn_Inputs_list_list_prim_u_8_strict_Output_bool_AnyhowException(<flutter_rust_bridge::DartOpaque>::sse_decode(&mut deserializer)); let api_user_discovery_set_shares = decode_DartFn_Inputs_list_list_prim_u_8_strict_Output_bool_AnyhowException(<flutter_rust_bridge::DartOpaque>::sse_decode(&mut deserializer));
let api_user_discovery_get_share_for_contact = decode_DartFn_Inputs_i_64_Output_opt_list_prim_u_8_strict_AnyhowException(<flutter_rust_bridge::DartOpaque>::sse_decode(&mut deserializer)); let api_user_discovery_get_share_for_contact = decode_DartFn_Inputs_i_64_Output_opt_list_prim_u_8_strict_AnyhowException(<flutter_rust_bridge::DartOpaque>::sse_decode(&mut deserializer));
let api_user_discovery_push_own_promotion = decode_DartFn_Inputs_i_64_i_64_list_prim_u_8_strict_Output_bool_AnyhowException(<flutter_rust_bridge::DartOpaque>::sse_decode(&mut deserializer)); let api_user_discovery_push_own_promotion_and_clear_old_version = decode_DartFn_Inputs_i_64_i_64_list_prim_u_8_strict_Output_bool_AnyhowException(<flutter_rust_bridge::DartOpaque>::sse_decode(&mut deserializer));
let api_user_discovery_get_own_promotions_after_version = decode_DartFn_Inputs_i_64_Output_opt_list_list_prim_u_8_strict_AnyhowException(<flutter_rust_bridge::DartOpaque>::sse_decode(&mut deserializer)); let api_user_discovery_get_own_promotions_after_version = decode_DartFn_Inputs_i_64_Output_opt_list_list_prim_u_8_strict_AnyhowException(<flutter_rust_bridge::DartOpaque>::sse_decode(&mut deserializer));
let api_user_discovery_store_other_promotion = decode_DartFn_Inputs_other_promotion_Output_bool_AnyhowException(<flutter_rust_bridge::DartOpaque>::sse_decode(&mut deserializer)); let api_user_discovery_store_other_promotion = decode_DartFn_Inputs_other_promotion_Output_bool_AnyhowException(<flutter_rust_bridge::DartOpaque>::sse_decode(&mut deserializer));
let api_user_discovery_get_other_promotions_by_public_id = decode_DartFn_Inputs_i_64_Output_opt_list_other_promotion_AnyhowException(<flutter_rust_bridge::DartOpaque>::sse_decode(&mut deserializer)); let api_user_discovery_get_other_promotions_by_public_id = decode_DartFn_Inputs_i_64_Output_opt_list_other_promotion_AnyhowException(<flutter_rust_bridge::DartOpaque>::sse_decode(&mut deserializer));
@ -150,7 +150,7 @@ let api_user_discovery_get_contact_version = decode_DartFn_Inputs_i_64_Output_op
let api_user_discovery_set_contact_version = decode_DartFn_Inputs_i_64_list_prim_u_8_strict_Output_bool_AnyhowException(<flutter_rust_bridge::DartOpaque>::sse_decode(&mut deserializer)); let api_user_discovery_set_contact_version = decode_DartFn_Inputs_i_64_list_prim_u_8_strict_Output_bool_AnyhowException(<flutter_rust_bridge::DartOpaque>::sse_decode(&mut deserializer));
let api_user_discovery_push_new_user_relation = decode_DartFn_Inputs_i_64_announced_user_opt_box_autoadd_i_64_Output_bool_AnyhowException(<flutter_rust_bridge::DartOpaque>::sse_decode(&mut deserializer));deserializer.end(); move |context| { let api_user_discovery_push_new_user_relation = decode_DartFn_Inputs_i_64_announced_user_opt_box_autoadd_i_64_Output_bool_AnyhowException(<flutter_rust_bridge::DartOpaque>::sse_decode(&mut deserializer));deserializer.end(); move |context| {
transform_result_sse::<_, ()>((move || { transform_result_sse::<_, ()>((move || {
let output_ok = Result::<_,()>::Ok({ crate::bridge::callbacks::init_flutter_callbacks(api_logging_get_stream_sink, api_user_discovery_sign_data, api_user_discovery_verify_signature, api_user_discovery_verify_stored_pubkey, api_user_discovery_set_shares, api_user_discovery_get_share_for_contact, api_user_discovery_push_own_promotion, api_user_discovery_get_own_promotions_after_version, api_user_discovery_store_other_promotion, api_user_discovery_get_other_promotions_by_public_id, api_user_discovery_get_announced_user_by_public_id, api_user_discovery_get_contact_version, api_user_discovery_set_contact_version, api_user_discovery_push_new_user_relation); })?; Ok(output_ok) let output_ok = Result::<_,()>::Ok({ crate::bridge::callbacks::init_flutter_callbacks(api_logging_get_stream_sink, api_user_discovery_sign_data, api_user_discovery_verify_signature, api_user_discovery_verify_stored_pubkey, api_user_discovery_set_shares, api_user_discovery_get_share_for_contact, api_user_discovery_push_own_promotion_and_clear_old_version, api_user_discovery_get_own_promotions_after_version, api_user_discovery_store_other_promotion, api_user_discovery_get_other_promotions_by_public_id, api_user_discovery_get_announced_user_by_public_id, api_user_discovery_get_contact_version, api_user_discovery_set_contact_version, api_user_discovery_push_new_user_relation); })?; Ok(output_ok)
})()) })())
} }) } })
} }

View file

@ -4,7 +4,7 @@ pub mod stores;
pub mod tests; pub mod tests;
pub mod traits; pub mod traits;
use std::collections::HashMap; use std::collections::{HashMap, HashSet};
use std::u8; use std::u8;
use blahaj::{Share, Sharks}; use blahaj::{Share, Sharks};
use prost::Message; use prost::Message;
@ -447,7 +447,7 @@ impl<Store: UserDiscoveryStore, Utils: UserDiscoveryUtils> UserDiscovery<Store,
}; };
self.store self.store
.push_own_promotion( .push_own_promotion_and_clear_old_version(
contact_id, contact_id,
config.promotion_version, config.promotion_version,
message.encode_to_vec(), message.encode_to_vec(),
@ -525,10 +525,21 @@ impl<Store: UserDiscoveryStore, Utils: UserDiscoveryUtils> UserDiscovery<Store,
.get_other_promotions_by_public_id(udp.public_id) .get_other_promotions_by_public_id(udp.public_id)
.await?; .await?;
if promotions.len() < udp.threshold as usize { // Deduplicate shares by their raw bytes to prevent invalid Shamir's Secret Sharing recoveries.
// Multiple identical shares (e.g. due to contact resending promotions, or DB duplicate writes)
// will cause `recover` to interpolate incorrectly and return garbage bytes.
let mut unique_shares_set = HashSet::new();
let mut unique_promotions = Vec::new();
for p in promotions {
if unique_shares_set.insert(p.announcement_share.clone()) {
unique_promotions.push(p);
}
}
if unique_promotions.len() < udp.threshold as usize {
tracing::debug!( tracing::debug!(
"Not enough shares ({} < {}) to decrypt announcement. Waiting for next share.", "Not enough unique shares ({} < {}) to decrypt announcement. Waiting for next share.",
promotions.len(), unique_promotions.len(),
udp.threshold udp.threshold
); );
return Ok(()); return Ok(());
@ -536,7 +547,7 @@ impl<Store: UserDiscoveryStore, Utils: UserDiscoveryUtils> UserDiscovery<Store,
tracing::debug!("Enough shares decrypting announcement."); tracing::debug!("Enough shares decrypting announcement.");
let shares: Vec<_> = promotions let shares: Vec<_> = unique_promotions
.iter() .iter()
.map(|x| x.announcement_share.to_owned()) .map(|x| x.announcement_share.to_owned())
.filter_map(|x| Share::try_from(x.as_slice()).ok()) .filter_map(|x| Share::try_from(x.as_slice()).ok())
@ -577,7 +588,7 @@ impl<Store: UserDiscoveryStore, Utils: UserDiscoveryUtils> UserDiscovery<Store,
}; };
let user_id = self.get_config().await?.user_id; let user_id = self.get_config().await?.user_id;
for promotion in promotions { for promotion in unique_promotions {
// Do not store the announcement of the users itself. // Do not store the announcement of the users itself.
// Or in case the promotion promotes myself // Or in case the promotion promotes myself
if promotion.from_contact_id == announced_user.user_id if promotion.from_contact_id == announced_user.user_id

View file

@ -66,17 +66,24 @@ impl UserDiscoveryStore for InMemoryStore {
Ok(()) Ok(())
} }
async fn push_own_promotion( async fn push_own_promotion_and_clear_old_version(
&self, &self,
contact_id: UserID, contact_id: UserID,
version: u32, version: u32,
promotion: Vec<u8>, promotion: Vec<u8>,
) -> Result<()> { ) -> Result<()> {
let mut storage = self.storage(); let mut storage = self.storage();
// println!("{} != {}", version, storage.promotions.len());
if version as usize != storage.own_promotions.len() + 1 { if version as usize != storage.own_promotions.len() + 1 {
return Err(UserDiscoveryError::PushedInvalidVersion); return Err(UserDiscoveryError::PushedInvalidVersion);
} }
for (old_contact_id, promotion) in storage.own_promotions.iter_mut() {
if *old_contact_id == contact_id {
promotion.clear();
}
}
storage.own_promotions.push((contact_id, promotion)); storage.own_promotions.push((contact_id, promotion));
Ok(()) Ok(())
} }

View file

@ -112,7 +112,7 @@ async fn get_with_five_users<S: UserDiscoveryStore + Default + Clone>() -> TestN
} }
#[tokio::test] #[tokio::test]
async fn test_user_discovery_dynamic_threshold_in_memory_store() { async fn test_user_discovery_decreased_threshold_in_memory_store() {
let _ = pretty_env_logger::try_init(); let _ = pretty_env_logger::try_init();
let mut network = TestNetwork::<InMemoryStore>::new(); let mut network = TestNetwork::<InMemoryStore>::new();
@ -196,6 +196,84 @@ async fn test_user_discovery_dynamic_threshold_in_memory_store() {
); );
} }
#[tokio::test]
async fn test_user_discovery_increased_threshold_in_memory_store() {
let _ = pretty_env_logger::try_init();
let mut network = TestNetwork::<InMemoryStore>::new();
// Start ALICE with a more strict threshold of 3.
// David only has 2 paths to Alice (via Bob and Charlie). Since 2 < 3, David cannot discover Alice.
network.add_user("ALICE", 2).await;
network.add_user("BOB", 2).await;
network.add_user("CHARLIE", 2).await;
network.add_user("DAVID", 2).await;
network.add_user("FRANK", 2).await;
// Same topology as the initial 5 users
network.set_friends("ALICE", &["BOB", "CHARLIE"]);
network.set_friends("BOB", &["ALICE", "CHARLIE", "DAVID"]);
// CHARLIE IS NOT YET A FRIEND OF DAVID -> DAVID SHOULD NOT BE ABLE TO BE DECODE ALICE
network.set_friends("CHARLIE", &["ALICE", "BOB", "FRANK"]);
network.set_friends("DAVID", &["BOB", "CHARLIE"]);
network.set_friends("FRANK", &["CHARLIE"]);
let david_idx = network.ids_by_name["DAVID"];
let alice_idx = network.ids_by_name["ALICE"];
// Phase 1: Exchange with ALICE threshold = 2
step0_exchange_random::<InMemoryStore>(&network).await;
step1_verify_no_new_messages::<InMemoryStore>(&network).await;
// DAVID should NOT know ALICE yet because ALICE's threshold is 2, and David has only 1 shares.
{
let david_knows = network.uds[david_idx]
.get_all_announced_users()
.await
.unwrap();
let knows_alice = david_knows
.iter()
.any(|(u, _)| u.user_id == alice_idx as UserID);
assert!(!knows_alice, "David should not know Alice yet because Alice's threshold is 3 and David only receives 2 shares");
}
// Phase 2: Update ALICE's threshold to 3
network.uds[alice_idx]
.initialize_or_update(3, alice_idx as UserID, vec![alice_idx as u8; 32])
.await
.unwrap();
// ALICE's new announcement with threshold 3 should propagate further.
// This SHOULD REPLACE THE OLD VERSION...
step0_exchange_random::<InMemoryStore>(&network).await;
step1_verify_no_new_messages::<InMemoryStore>(&network).await;
// Now Charlie is a friend of David, so he should exchange ALICE with him
network.set_friends("CHARLIE", &["ALICE", "BOB", "DAVID", "FRANK"]);
// ALICE's new announcement with threshold 3 should propagate further.
// This SHOULD REPLACE THE OLD VERSION...
step0_exchange_random::<InMemoryStore>(&network).await;
step1_verify_no_new_messages::<InMemoryStore>(&network).await;
// DAVID should still NOT know ALICE yet because ALICE's new threshold is 3, and David has only 2 shares.
{
let david_knows = network.uds[david_idx]
.get_all_announced_users()
.await
.unwrap();
let knows_alice = david_knows
.iter()
.any(|(u, _)| u.user_id == alice_idx as UserID);
assert!(!knows_alice, "David should not know Alice yet because Alice's threshold is 3 and David only receives 2 shares");
}
}
#[tokio::test] #[tokio::test]
async fn test_user_discovery_in_memory_store() { async fn test_user_discovery_in_memory_store() {
let _ = pretty_env_logger::try_init(); let _ = pretty_env_logger::try_init();

View file

@ -31,7 +31,7 @@ pub trait UserDiscoveryStore {
contact_id: UserID, contact_id: UserID,
) -> impl Future<Output = Result<Vec<u8>>> + Send; ) -> impl Future<Output = Result<Vec<u8>>> + Send;
fn push_own_promotion( fn push_own_promotion_and_clear_old_version(
&self, &self,
contact_id: UserID, contact_id: UserID,
version: u32, version: u32,

View file

@ -16,9 +16,6 @@ import 'schema_v9.dart' as v9;
import 'schema_v10.dart' as v10; import 'schema_v10.dart' as v10;
import 'schema_v11.dart' as v11; import 'schema_v11.dart' as v11;
import 'schema_v12.dart' as v12; import 'schema_v12.dart' as v12;
import 'schema_v13.dart' as v13;
import 'schema_v14.dart' as v14;
import 'schema_v15.dart' as v15;
class GeneratedHelper implements SchemaInstantiationHelper { class GeneratedHelper implements SchemaInstantiationHelper {
@override @override
@ -48,32 +45,10 @@ class GeneratedHelper implements SchemaInstantiationHelper {
return v11.DatabaseAtV11(db); return v11.DatabaseAtV11(db);
case 12: case 12:
return v12.DatabaseAtV12(db); return v12.DatabaseAtV12(db);
case 13:
return v13.DatabaseAtV13(db);
case 14:
return v14.DatabaseAtV14(db);
case 15:
return v15.DatabaseAtV15(db);
default: default:
throw MissingSchemaException(version, versions); throw MissingSchemaException(version, versions);
} }
} }
static const versions = const [ static const versions = const [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
];
} }

View file

@ -135,6 +135,34 @@ class Contacts extends Table with TableInfo<Contacts, ContactsData> {
requiredDuringInsert: false, requiredDuringInsert: false,
$customConstraints: 'NULL', $customConstraints: 'NULL',
); );
late final GeneratedColumn<int> userDiscoveryExcluded = GeneratedColumn<int>(
'user_discovery_excluded',
aliasedName,
false,
type: DriftSqlType.int,
requiredDuringInsert: false,
$customConstraints:
'NOT NULL DEFAULT 0 CHECK (user_discovery_excluded IN (0, 1))',
defaultValue: const CustomExpression('0'),
);
late final GeneratedColumn<int> mediaSendCounter = GeneratedColumn<int>(
'media_send_counter',
aliasedName,
false,
type: DriftSqlType.int,
requiredDuringInsert: false,
$customConstraints: 'NOT NULL DEFAULT 0',
defaultValue: const CustomExpression('0'),
);
late final GeneratedColumn<int> mediaReceivedCounter = GeneratedColumn<int>(
'media_received_counter',
aliasedName,
false,
type: DriftSqlType.int,
requiredDuringInsert: false,
$customConstraints: 'NOT NULL DEFAULT 0',
defaultValue: const CustomExpression('0'),
);
@override @override
List<GeneratedColumn> get $columns => [ List<GeneratedColumn> get $columns => [
userId, userId,
@ -151,6 +179,9 @@ class Contacts extends Table with TableInfo<Contacts, ContactsData> {
accountDeleted, accountDeleted,
createdAt, createdAt,
userDiscoveryVersion, userDiscoveryVersion,
userDiscoveryExcluded,
mediaSendCounter,
mediaReceivedCounter,
]; ];
@override @override
String get aliasedName => _alias ?? actualTableName; String get aliasedName => _alias ?? actualTableName;
@ -219,6 +250,18 @@ class Contacts extends Table with TableInfo<Contacts, ContactsData> {
DriftSqlType.blob, DriftSqlType.blob,
data['${effectivePrefix}user_discovery_version'], data['${effectivePrefix}user_discovery_version'],
), ),
userDiscoveryExcluded: attachedDatabase.typeMapping.read(
DriftSqlType.int,
data['${effectivePrefix}user_discovery_excluded'],
)!,
mediaSendCounter: attachedDatabase.typeMapping.read(
DriftSqlType.int,
data['${effectivePrefix}media_send_counter'],
)!,
mediaReceivedCounter: attachedDatabase.typeMapping.read(
DriftSqlType.int,
data['${effectivePrefix}media_received_counter'],
)!,
); );
} }
@ -248,6 +291,9 @@ class ContactsData extends DataClass implements Insertable<ContactsData> {
final int accountDeleted; final int accountDeleted;
final int createdAt; final int createdAt;
final i2.Uint8List? userDiscoveryVersion; final i2.Uint8List? userDiscoveryVersion;
final int userDiscoveryExcluded;
final int mediaSendCounter;
final int mediaReceivedCounter;
const ContactsData({ const ContactsData({
required this.userId, required this.userId,
required this.username, required this.username,
@ -263,6 +309,9 @@ class ContactsData extends DataClass implements Insertable<ContactsData> {
required this.accountDeleted, required this.accountDeleted,
required this.createdAt, required this.createdAt,
this.userDiscoveryVersion, this.userDiscoveryVersion,
required this.userDiscoveryExcluded,
required this.mediaSendCounter,
required this.mediaReceivedCounter,
}); });
@override @override
Map<String, Expression> toColumns(bool nullToAbsent) { Map<String, Expression> toColumns(bool nullToAbsent) {
@ -293,6 +342,9 @@ class ContactsData extends DataClass implements Insertable<ContactsData> {
userDiscoveryVersion, userDiscoveryVersion,
); );
} }
map['user_discovery_excluded'] = Variable<int>(userDiscoveryExcluded);
map['media_send_counter'] = Variable<int>(mediaSendCounter);
map['media_received_counter'] = Variable<int>(mediaReceivedCounter);
return map; return map;
} }
@ -320,6 +372,9 @@ class ContactsData extends DataClass implements Insertable<ContactsData> {
userDiscoveryVersion: userDiscoveryVersion == null && nullToAbsent userDiscoveryVersion: userDiscoveryVersion == null && nullToAbsent
? const Value.absent() ? const Value.absent()
: Value(userDiscoveryVersion), : Value(userDiscoveryVersion),
userDiscoveryExcluded: Value(userDiscoveryExcluded),
mediaSendCounter: Value(mediaSendCounter),
mediaReceivedCounter: Value(mediaReceivedCounter),
); );
} }
@ -349,6 +404,13 @@ class ContactsData extends DataClass implements Insertable<ContactsData> {
userDiscoveryVersion: serializer.fromJson<i2.Uint8List?>( userDiscoveryVersion: serializer.fromJson<i2.Uint8List?>(
json['userDiscoveryVersion'], json['userDiscoveryVersion'],
), ),
userDiscoveryExcluded: serializer.fromJson<int>(
json['userDiscoveryExcluded'],
),
mediaSendCounter: serializer.fromJson<int>(json['mediaSendCounter']),
mediaReceivedCounter: serializer.fromJson<int>(
json['mediaReceivedCounter'],
),
); );
} }
@override @override
@ -373,6 +435,9 @@ class ContactsData extends DataClass implements Insertable<ContactsData> {
'userDiscoveryVersion': serializer.toJson<i2.Uint8List?>( 'userDiscoveryVersion': serializer.toJson<i2.Uint8List?>(
userDiscoveryVersion, userDiscoveryVersion,
), ),
'userDiscoveryExcluded': serializer.toJson<int>(userDiscoveryExcluded),
'mediaSendCounter': serializer.toJson<int>(mediaSendCounter),
'mediaReceivedCounter': serializer.toJson<int>(mediaReceivedCounter),
}; };
} }
@ -391,6 +456,9 @@ class ContactsData extends DataClass implements Insertable<ContactsData> {
int? accountDeleted, int? accountDeleted,
int? createdAt, int? createdAt,
Value<i2.Uint8List?> userDiscoveryVersion = const Value.absent(), Value<i2.Uint8List?> userDiscoveryVersion = const Value.absent(),
int? userDiscoveryExcluded,
int? mediaSendCounter,
int? mediaReceivedCounter,
}) => ContactsData( }) => ContactsData(
userId: userId ?? this.userId, userId: userId ?? this.userId,
username: username ?? this.username, username: username ?? this.username,
@ -410,6 +478,9 @@ class ContactsData extends DataClass implements Insertable<ContactsData> {
userDiscoveryVersion: userDiscoveryVersion.present userDiscoveryVersion: userDiscoveryVersion.present
? userDiscoveryVersion.value ? userDiscoveryVersion.value
: this.userDiscoveryVersion, : this.userDiscoveryVersion,
userDiscoveryExcluded: userDiscoveryExcluded ?? this.userDiscoveryExcluded,
mediaSendCounter: mediaSendCounter ?? this.mediaSendCounter,
mediaReceivedCounter: mediaReceivedCounter ?? this.mediaReceivedCounter,
); );
ContactsData copyWithCompanion(ContactsCompanion data) { ContactsData copyWithCompanion(ContactsCompanion data) {
return ContactsData( return ContactsData(
@ -439,6 +510,15 @@ class ContactsData extends DataClass implements Insertable<ContactsData> {
userDiscoveryVersion: data.userDiscoveryVersion.present userDiscoveryVersion: data.userDiscoveryVersion.present
? data.userDiscoveryVersion.value ? data.userDiscoveryVersion.value
: this.userDiscoveryVersion, : this.userDiscoveryVersion,
userDiscoveryExcluded: data.userDiscoveryExcluded.present
? data.userDiscoveryExcluded.value
: this.userDiscoveryExcluded,
mediaSendCounter: data.mediaSendCounter.present
? data.mediaSendCounter.value
: this.mediaSendCounter,
mediaReceivedCounter: data.mediaReceivedCounter.present
? data.mediaReceivedCounter.value
: this.mediaReceivedCounter,
); );
} }
@ -458,7 +538,10 @@ class ContactsData extends DataClass implements Insertable<ContactsData> {
..write('verified: $verified, ') ..write('verified: $verified, ')
..write('accountDeleted: $accountDeleted, ') ..write('accountDeleted: $accountDeleted, ')
..write('createdAt: $createdAt, ') ..write('createdAt: $createdAt, ')
..write('userDiscoveryVersion: $userDiscoveryVersion') ..write('userDiscoveryVersion: $userDiscoveryVersion, ')
..write('userDiscoveryExcluded: $userDiscoveryExcluded, ')
..write('mediaSendCounter: $mediaSendCounter, ')
..write('mediaReceivedCounter: $mediaReceivedCounter')
..write(')')) ..write(')'))
.toString(); .toString();
} }
@ -479,6 +562,9 @@ class ContactsData extends DataClass implements Insertable<ContactsData> {
accountDeleted, accountDeleted,
createdAt, createdAt,
$driftBlobEquality.hash(userDiscoveryVersion), $driftBlobEquality.hash(userDiscoveryVersion),
userDiscoveryExcluded,
mediaSendCounter,
mediaReceivedCounter,
); );
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
@ -503,7 +589,10 @@ class ContactsData extends DataClass implements Insertable<ContactsData> {
$driftBlobEquality.equals( $driftBlobEquality.equals(
other.userDiscoveryVersion, other.userDiscoveryVersion,
this.userDiscoveryVersion, this.userDiscoveryVersion,
)); ) &&
other.userDiscoveryExcluded == this.userDiscoveryExcluded &&
other.mediaSendCounter == this.mediaSendCounter &&
other.mediaReceivedCounter == this.mediaReceivedCounter);
} }
class ContactsCompanion extends UpdateCompanion<ContactsData> { class ContactsCompanion extends UpdateCompanion<ContactsData> {
@ -521,6 +610,9 @@ class ContactsCompanion extends UpdateCompanion<ContactsData> {
final Value<int> accountDeleted; final Value<int> accountDeleted;
final Value<int> createdAt; final Value<int> createdAt;
final Value<i2.Uint8List?> userDiscoveryVersion; final Value<i2.Uint8List?> userDiscoveryVersion;
final Value<int> userDiscoveryExcluded;
final Value<int> mediaSendCounter;
final Value<int> mediaReceivedCounter;
const ContactsCompanion({ const ContactsCompanion({
this.userId = const Value.absent(), this.userId = const Value.absent(),
this.username = const Value.absent(), this.username = const Value.absent(),
@ -536,6 +628,9 @@ class ContactsCompanion extends UpdateCompanion<ContactsData> {
this.accountDeleted = const Value.absent(), this.accountDeleted = const Value.absent(),
this.createdAt = const Value.absent(), this.createdAt = const Value.absent(),
this.userDiscoveryVersion = const Value.absent(), this.userDiscoveryVersion = const Value.absent(),
this.userDiscoveryExcluded = const Value.absent(),
this.mediaSendCounter = const Value.absent(),
this.mediaReceivedCounter = const Value.absent(),
}); });
ContactsCompanion.insert({ ContactsCompanion.insert({
this.userId = const Value.absent(), this.userId = const Value.absent(),
@ -552,6 +647,9 @@ class ContactsCompanion extends UpdateCompanion<ContactsData> {
this.accountDeleted = const Value.absent(), this.accountDeleted = const Value.absent(),
this.createdAt = const Value.absent(), this.createdAt = const Value.absent(),
this.userDiscoveryVersion = const Value.absent(), this.userDiscoveryVersion = const Value.absent(),
this.userDiscoveryExcluded = const Value.absent(),
this.mediaSendCounter = const Value.absent(),
this.mediaReceivedCounter = const Value.absent(),
}) : username = Value(username); }) : username = Value(username);
static Insertable<ContactsData> custom({ static Insertable<ContactsData> custom({
Expression<int>? userId, Expression<int>? userId,
@ -568,6 +666,9 @@ class ContactsCompanion extends UpdateCompanion<ContactsData> {
Expression<int>? accountDeleted, Expression<int>? accountDeleted,
Expression<int>? createdAt, Expression<int>? createdAt,
Expression<i2.Uint8List>? userDiscoveryVersion, Expression<i2.Uint8List>? userDiscoveryVersion,
Expression<int>? userDiscoveryExcluded,
Expression<int>? mediaSendCounter,
Expression<int>? mediaReceivedCounter,
}) { }) {
return RawValuesInsertable({ return RawValuesInsertable({
if (userId != null) 'user_id': userId, if (userId != null) 'user_id': userId,
@ -587,6 +688,11 @@ class ContactsCompanion extends UpdateCompanion<ContactsData> {
if (createdAt != null) 'created_at': createdAt, if (createdAt != null) 'created_at': createdAt,
if (userDiscoveryVersion != null) if (userDiscoveryVersion != null)
'user_discovery_version': userDiscoveryVersion, 'user_discovery_version': userDiscoveryVersion,
if (userDiscoveryExcluded != null)
'user_discovery_excluded': userDiscoveryExcluded,
if (mediaSendCounter != null) 'media_send_counter': mediaSendCounter,
if (mediaReceivedCounter != null)
'media_received_counter': mediaReceivedCounter,
}); });
} }
@ -605,6 +711,9 @@ class ContactsCompanion extends UpdateCompanion<ContactsData> {
Value<int>? accountDeleted, Value<int>? accountDeleted,
Value<int>? createdAt, Value<int>? createdAt,
Value<i2.Uint8List?>? userDiscoveryVersion, Value<i2.Uint8List?>? userDiscoveryVersion,
Value<int>? userDiscoveryExcluded,
Value<int>? mediaSendCounter,
Value<int>? mediaReceivedCounter,
}) { }) {
return ContactsCompanion( return ContactsCompanion(
userId: userId ?? this.userId, userId: userId ?? this.userId,
@ -621,6 +730,10 @@ class ContactsCompanion extends UpdateCompanion<ContactsData> {
accountDeleted: accountDeleted ?? this.accountDeleted, accountDeleted: accountDeleted ?? this.accountDeleted,
createdAt: createdAt ?? this.createdAt, createdAt: createdAt ?? this.createdAt,
userDiscoveryVersion: userDiscoveryVersion ?? this.userDiscoveryVersion, userDiscoveryVersion: userDiscoveryVersion ?? this.userDiscoveryVersion,
userDiscoveryExcluded:
userDiscoveryExcluded ?? this.userDiscoveryExcluded,
mediaSendCounter: mediaSendCounter ?? this.mediaSendCounter,
mediaReceivedCounter: mediaReceivedCounter ?? this.mediaReceivedCounter,
); );
} }
@ -673,6 +786,17 @@ class ContactsCompanion extends UpdateCompanion<ContactsData> {
userDiscoveryVersion.value, userDiscoveryVersion.value,
); );
} }
if (userDiscoveryExcluded.present) {
map['user_discovery_excluded'] = Variable<int>(
userDiscoveryExcluded.value,
);
}
if (mediaSendCounter.present) {
map['media_send_counter'] = Variable<int>(mediaSendCounter.value);
}
if (mediaReceivedCounter.present) {
map['media_received_counter'] = Variable<int>(mediaReceivedCounter.value);
}
return map; return map;
} }
@ -692,7 +816,10 @@ class ContactsCompanion extends UpdateCompanion<ContactsData> {
..write('verified: $verified, ') ..write('verified: $verified, ')
..write('accountDeleted: $accountDeleted, ') ..write('accountDeleted: $accountDeleted, ')
..write('createdAt: $createdAt, ') ..write('createdAt: $createdAt, ')
..write('userDiscoveryVersion: $userDiscoveryVersion') ..write('userDiscoveryVersion: $userDiscoveryVersion, ')
..write('userDiscoveryExcluded: $userDiscoveryExcluded, ')
..write('mediaSendCounter: $mediaSendCounter, ')
..write('mediaReceivedCounter: $mediaReceivedCounter')
..write(')')) ..write(')'))
.toString(); .toString();
} }
@ -7813,11 +7940,41 @@ class UserDiscoveryAnnouncedUsers extends Table
requiredDuringInsert: true, requiredDuringInsert: true,
$customConstraints: 'NOT NULL UNIQUE', $customConstraints: 'NOT NULL UNIQUE',
); );
late final GeneratedColumn<String> username = GeneratedColumn<String>(
'username',
aliasedName,
true,
type: DriftSqlType.string,
requiredDuringInsert: false,
$customConstraints: 'NULL',
);
late final GeneratedColumn<int> wasShownToTheUser = GeneratedColumn<int>(
'was_shown_to_the_user',
aliasedName,
false,
type: DriftSqlType.int,
requiredDuringInsert: false,
$customConstraints:
'NOT NULL DEFAULT 0 CHECK (was_shown_to_the_user IN (0, 1))',
defaultValue: const CustomExpression('0'),
);
late final GeneratedColumn<int> isHidden = GeneratedColumn<int>(
'is_hidden',
aliasedName,
false,
type: DriftSqlType.int,
requiredDuringInsert: false,
$customConstraints: 'NOT NULL DEFAULT 0 CHECK (is_hidden IN (0, 1))',
defaultValue: const CustomExpression('0'),
);
@override @override
List<GeneratedColumn> get $columns => [ List<GeneratedColumn> get $columns => [
announcedUserId, announcedUserId,
announcedPublicKey, announcedPublicKey,
publicId, publicId,
username,
wasShownToTheUser,
isHidden,
]; ];
@override @override
String get aliasedName => _alias ?? actualTableName; String get aliasedName => _alias ?? actualTableName;
@ -7845,6 +8002,18 @@ class UserDiscoveryAnnouncedUsers extends Table
DriftSqlType.int, DriftSqlType.int,
data['${effectivePrefix}public_id'], data['${effectivePrefix}public_id'],
)!, )!,
username: attachedDatabase.typeMapping.read(
DriftSqlType.string,
data['${effectivePrefix}username'],
),
wasShownToTheUser: attachedDatabase.typeMapping.read(
DriftSqlType.int,
data['${effectivePrefix}was_shown_to_the_user'],
)!,
isHidden: attachedDatabase.typeMapping.read(
DriftSqlType.int,
data['${effectivePrefix}is_hidden'],
)!,
); );
} }
@ -7866,10 +8035,16 @@ class UserDiscoveryAnnouncedUsersData extends DataClass
final int announcedUserId; final int announcedUserId;
final i2.Uint8List announcedPublicKey; final i2.Uint8List announcedPublicKey;
final int publicId; final int publicId;
final String? username;
final int wasShownToTheUser;
final int isHidden;
const UserDiscoveryAnnouncedUsersData({ const UserDiscoveryAnnouncedUsersData({
required this.announcedUserId, required this.announcedUserId,
required this.announcedPublicKey, required this.announcedPublicKey,
required this.publicId, required this.publicId,
this.username,
required this.wasShownToTheUser,
required this.isHidden,
}); });
@override @override
Map<String, Expression> toColumns(bool nullToAbsent) { Map<String, Expression> toColumns(bool nullToAbsent) {
@ -7877,6 +8052,11 @@ class UserDiscoveryAnnouncedUsersData extends DataClass
map['announced_user_id'] = Variable<int>(announcedUserId); map['announced_user_id'] = Variable<int>(announcedUserId);
map['announced_public_key'] = Variable<i2.Uint8List>(announcedPublicKey); map['announced_public_key'] = Variable<i2.Uint8List>(announcedPublicKey);
map['public_id'] = Variable<int>(publicId); map['public_id'] = Variable<int>(publicId);
if (!nullToAbsent || username != null) {
map['username'] = Variable<String>(username);
}
map['was_shown_to_the_user'] = Variable<int>(wasShownToTheUser);
map['is_hidden'] = Variable<int>(isHidden);
return map; return map;
} }
@ -7885,6 +8065,11 @@ class UserDiscoveryAnnouncedUsersData extends DataClass
announcedUserId: Value(announcedUserId), announcedUserId: Value(announcedUserId),
announcedPublicKey: Value(announcedPublicKey), announcedPublicKey: Value(announcedPublicKey),
publicId: Value(publicId), publicId: Value(publicId),
username: username == null && nullToAbsent
? const Value.absent()
: Value(username),
wasShownToTheUser: Value(wasShownToTheUser),
isHidden: Value(isHidden),
); );
} }
@ -7899,6 +8084,9 @@ class UserDiscoveryAnnouncedUsersData extends DataClass
json['announcedPublicKey'], json['announcedPublicKey'],
), ),
publicId: serializer.fromJson<int>(json['publicId']), publicId: serializer.fromJson<int>(json['publicId']),
username: serializer.fromJson<String?>(json['username']),
wasShownToTheUser: serializer.fromJson<int>(json['wasShownToTheUser']),
isHidden: serializer.fromJson<int>(json['isHidden']),
); );
} }
@override @override
@ -7908,6 +8096,9 @@ class UserDiscoveryAnnouncedUsersData extends DataClass
'announcedUserId': serializer.toJson<int>(announcedUserId), 'announcedUserId': serializer.toJson<int>(announcedUserId),
'announcedPublicKey': serializer.toJson<i2.Uint8List>(announcedPublicKey), 'announcedPublicKey': serializer.toJson<i2.Uint8List>(announcedPublicKey),
'publicId': serializer.toJson<int>(publicId), 'publicId': serializer.toJson<int>(publicId),
'username': serializer.toJson<String?>(username),
'wasShownToTheUser': serializer.toJson<int>(wasShownToTheUser),
'isHidden': serializer.toJson<int>(isHidden),
}; };
} }
@ -7915,10 +8106,16 @@ class UserDiscoveryAnnouncedUsersData extends DataClass
int? announcedUserId, int? announcedUserId,
i2.Uint8List? announcedPublicKey, i2.Uint8List? announcedPublicKey,
int? publicId, int? publicId,
Value<String?> username = const Value.absent(),
int? wasShownToTheUser,
int? isHidden,
}) => UserDiscoveryAnnouncedUsersData( }) => UserDiscoveryAnnouncedUsersData(
announcedUserId: announcedUserId ?? this.announcedUserId, announcedUserId: announcedUserId ?? this.announcedUserId,
announcedPublicKey: announcedPublicKey ?? this.announcedPublicKey, announcedPublicKey: announcedPublicKey ?? this.announcedPublicKey,
publicId: publicId ?? this.publicId, publicId: publicId ?? this.publicId,
username: username.present ? username.value : this.username,
wasShownToTheUser: wasShownToTheUser ?? this.wasShownToTheUser,
isHidden: isHidden ?? this.isHidden,
); );
UserDiscoveryAnnouncedUsersData copyWithCompanion( UserDiscoveryAnnouncedUsersData copyWithCompanion(
UserDiscoveryAnnouncedUsersCompanion data, UserDiscoveryAnnouncedUsersCompanion data,
@ -7931,6 +8128,11 @@ class UserDiscoveryAnnouncedUsersData extends DataClass
? data.announcedPublicKey.value ? data.announcedPublicKey.value
: this.announcedPublicKey, : this.announcedPublicKey,
publicId: data.publicId.present ? data.publicId.value : this.publicId, publicId: data.publicId.present ? data.publicId.value : this.publicId,
username: data.username.present ? data.username.value : this.username,
wasShownToTheUser: data.wasShownToTheUser.present
? data.wasShownToTheUser.value
: this.wasShownToTheUser,
isHidden: data.isHidden.present ? data.isHidden.value : this.isHidden,
); );
} }
@ -7939,7 +8141,10 @@ class UserDiscoveryAnnouncedUsersData extends DataClass
return (StringBuffer('UserDiscoveryAnnouncedUsersData(') return (StringBuffer('UserDiscoveryAnnouncedUsersData(')
..write('announcedUserId: $announcedUserId, ') ..write('announcedUserId: $announcedUserId, ')
..write('announcedPublicKey: $announcedPublicKey, ') ..write('announcedPublicKey: $announcedPublicKey, ')
..write('publicId: $publicId') ..write('publicId: $publicId, ')
..write('username: $username, ')
..write('wasShownToTheUser: $wasShownToTheUser, ')
..write('isHidden: $isHidden')
..write(')')) ..write(')'))
.toString(); .toString();
} }
@ -7949,6 +8154,9 @@ class UserDiscoveryAnnouncedUsersData extends DataClass
announcedUserId, announcedUserId,
$driftBlobEquality.hash(announcedPublicKey), $driftBlobEquality.hash(announcedPublicKey),
publicId, publicId,
username,
wasShownToTheUser,
isHidden,
); );
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
@ -7959,7 +8167,10 @@ class UserDiscoveryAnnouncedUsersData extends DataClass
other.announcedPublicKey, other.announcedPublicKey,
this.announcedPublicKey, this.announcedPublicKey,
) && ) &&
other.publicId == this.publicId); other.publicId == this.publicId &&
other.username == this.username &&
other.wasShownToTheUser == this.wasShownToTheUser &&
other.isHidden == this.isHidden);
} }
class UserDiscoveryAnnouncedUsersCompanion class UserDiscoveryAnnouncedUsersCompanion
@ -7967,27 +8178,42 @@ class UserDiscoveryAnnouncedUsersCompanion
final Value<int> announcedUserId; final Value<int> announcedUserId;
final Value<i2.Uint8List> announcedPublicKey; final Value<i2.Uint8List> announcedPublicKey;
final Value<int> publicId; final Value<int> publicId;
final Value<String?> username;
final Value<int> wasShownToTheUser;
final Value<int> isHidden;
const UserDiscoveryAnnouncedUsersCompanion({ const UserDiscoveryAnnouncedUsersCompanion({
this.announcedUserId = const Value.absent(), this.announcedUserId = const Value.absent(),
this.announcedPublicKey = const Value.absent(), this.announcedPublicKey = const Value.absent(),
this.publicId = const Value.absent(), this.publicId = const Value.absent(),
this.username = const Value.absent(),
this.wasShownToTheUser = const Value.absent(),
this.isHidden = const Value.absent(),
}); });
UserDiscoveryAnnouncedUsersCompanion.insert({ UserDiscoveryAnnouncedUsersCompanion.insert({
this.announcedUserId = const Value.absent(), this.announcedUserId = const Value.absent(),
required i2.Uint8List announcedPublicKey, required i2.Uint8List announcedPublicKey,
required int publicId, required int publicId,
this.username = const Value.absent(),
this.wasShownToTheUser = const Value.absent(),
this.isHidden = const Value.absent(),
}) : announcedPublicKey = Value(announcedPublicKey), }) : announcedPublicKey = Value(announcedPublicKey),
publicId = Value(publicId); publicId = Value(publicId);
static Insertable<UserDiscoveryAnnouncedUsersData> custom({ static Insertable<UserDiscoveryAnnouncedUsersData> custom({
Expression<int>? announcedUserId, Expression<int>? announcedUserId,
Expression<i2.Uint8List>? announcedPublicKey, Expression<i2.Uint8List>? announcedPublicKey,
Expression<int>? publicId, Expression<int>? publicId,
Expression<String>? username,
Expression<int>? wasShownToTheUser,
Expression<int>? isHidden,
}) { }) {
return RawValuesInsertable({ return RawValuesInsertable({
if (announcedUserId != null) 'announced_user_id': announcedUserId, if (announcedUserId != null) 'announced_user_id': announcedUserId,
if (announcedPublicKey != null) if (announcedPublicKey != null)
'announced_public_key': announcedPublicKey, 'announced_public_key': announcedPublicKey,
if (publicId != null) 'public_id': publicId, if (publicId != null) 'public_id': publicId,
if (username != null) 'username': username,
if (wasShownToTheUser != null) 'was_shown_to_the_user': wasShownToTheUser,
if (isHidden != null) 'is_hidden': isHidden,
}); });
} }
@ -7995,11 +8221,17 @@ class UserDiscoveryAnnouncedUsersCompanion
Value<int>? announcedUserId, Value<int>? announcedUserId,
Value<i2.Uint8List>? announcedPublicKey, Value<i2.Uint8List>? announcedPublicKey,
Value<int>? publicId, Value<int>? publicId,
Value<String?>? username,
Value<int>? wasShownToTheUser,
Value<int>? isHidden,
}) { }) {
return UserDiscoveryAnnouncedUsersCompanion( return UserDiscoveryAnnouncedUsersCompanion(
announcedUserId: announcedUserId ?? this.announcedUserId, announcedUserId: announcedUserId ?? this.announcedUserId,
announcedPublicKey: announcedPublicKey ?? this.announcedPublicKey, announcedPublicKey: announcedPublicKey ?? this.announcedPublicKey,
publicId: publicId ?? this.publicId, publicId: publicId ?? this.publicId,
username: username ?? this.username,
wasShownToTheUser: wasShownToTheUser ?? this.wasShownToTheUser,
isHidden: isHidden ?? this.isHidden,
); );
} }
@ -8017,6 +8249,15 @@ class UserDiscoveryAnnouncedUsersCompanion
if (publicId.present) { if (publicId.present) {
map['public_id'] = Variable<int>(publicId.value); map['public_id'] = Variable<int>(publicId.value);
} }
if (username.present) {
map['username'] = Variable<String>(username.value);
}
if (wasShownToTheUser.present) {
map['was_shown_to_the_user'] = Variable<int>(wasShownToTheUser.value);
}
if (isHidden.present) {
map['is_hidden'] = Variable<int>(isHidden.value);
}
return map; return map;
} }
@ -8025,7 +8266,10 @@ class UserDiscoveryAnnouncedUsersCompanion
return (StringBuffer('UserDiscoveryAnnouncedUsersCompanion(') return (StringBuffer('UserDiscoveryAnnouncedUsersCompanion(')
..write('announcedUserId: $announcedUserId, ') ..write('announcedUserId: $announcedUserId, ')
..write('announcedPublicKey: $announcedPublicKey, ') ..write('announcedPublicKey: $announcedPublicKey, ')
..write('publicId: $publicId') ..write('publicId: $publicId, ')
..write('username: $username, ')
..write('wasShownToTheUser: $wasShownToTheUser, ')
..write('isHidden: $isHidden')
..write(')')) ..write(')'))
.toString(); .toString();
} }
@ -8377,7 +8621,7 @@ class UserDiscoveryOtherPromotions extends Table
String get actualTableName => $name; String get actualTableName => $name;
static const String $name = 'user_discovery_other_promotions'; static const String $name = 'user_discovery_other_promotions';
@override @override
Set<GeneratedColumn> get $primaryKey => {fromContactId, promotionId}; Set<GeneratedColumn> get $primaryKey => {fromContactId, publicId};
@override @override
UserDiscoveryOtherPromotionsData map( UserDiscoveryOtherPromotionsData map(
Map<String, dynamic> data, { Map<String, dynamic> data, {
@ -8419,7 +8663,7 @@ class UserDiscoveryOtherPromotions extends Table
@override @override
List<String> get customConstraints => const [ List<String> get customConstraints => const [
'PRIMARY KEY(from_contact_id, promotion_id)', 'PRIMARY KEY(from_contact_id, public_id)',
]; ];
@override @override
bool get dontWriteConstraints => true; bool get dontWriteConstraints => true;