remove unnecessary pre key loading

This commit is contained in:
otsmr 2026-03-01 04:20:37 +01:00
parent eb70d7119f
commit 4cbeaf3ff7
20 changed files with 9536 additions and 1632 deletions

File diff suppressed because it is too large Load diff

View file

@ -82,7 +82,4 @@ class MessageHistories extends Table {
TextColumn get content => text().nullable()(); TextColumn get content => text().nullable()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)(); DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
@override
Set<Column> get primaryKey => {id};
} }

View file

@ -1,13 +0,0 @@
import 'package:drift/drift.dart';
import 'package:twonly/src/database/tables/contacts.table.dart';
@DataClassName('SignalContactPreKey')
class SignalContactPreKeys extends Table {
IntColumn get contactId =>
integer().references(Contacts, #userId, onDelete: KeyAction.cascade)();
IntColumn get preKeyId => integer()();
BlobColumn get preKey => blob()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
@override
Set<Column> get primaryKey => {contactId, preKeyId};
}

View file

@ -1,14 +0,0 @@
import 'package:drift/drift.dart';
import 'package:twonly/src/database/tables/contacts.table.dart';
@DataClassName('SignalContactSignedPreKey')
class SignalContactSignedPreKeys extends Table {
IntColumn get contactId =>
integer().references(Contacts, #userId, onDelete: KeyAction.cascade)();
IntColumn get signedPreKeyId => integer()();
BlobColumn get signedPreKey => blob()();
BlobColumn get signedPreKeySignature => blob()();
DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
@override
Set<Column> get primaryKey => {contactId};
}

View file

@ -9,15 +9,12 @@ import 'package:twonly/src/database/daos/mediafiles.dao.dart';
import 'package:twonly/src/database/daos/messages.dao.dart'; import 'package:twonly/src/database/daos/messages.dao.dart';
import 'package:twonly/src/database/daos/reactions.dao.dart'; import 'package:twonly/src/database/daos/reactions.dao.dart';
import 'package:twonly/src/database/daos/receipts.dao.dart'; import 'package:twonly/src/database/daos/receipts.dao.dart';
import 'package:twonly/src/database/daos/signal.dao.dart';
import 'package:twonly/src/database/tables/contacts.table.dart'; import 'package:twonly/src/database/tables/contacts.table.dart';
import 'package:twonly/src/database/tables/groups.table.dart'; import 'package:twonly/src/database/tables/groups.table.dart';
import 'package:twonly/src/database/tables/mediafiles.table.dart'; import 'package:twonly/src/database/tables/mediafiles.table.dart';
import 'package:twonly/src/database/tables/messages.table.dart'; import 'package:twonly/src/database/tables/messages.table.dart';
import 'package:twonly/src/database/tables/reactions.table.dart'; import 'package:twonly/src/database/tables/reactions.table.dart';
import 'package:twonly/src/database/tables/receipts.table.dart'; import 'package:twonly/src/database/tables/receipts.table.dart';
import 'package:twonly/src/database/tables/signal_contact_prekey.table.dart';
import 'package:twonly/src/database/tables/signal_contact_signed_prekey.table.dart';
import 'package:twonly/src/database/tables/signal_identity_key_store.table.dart'; import 'package:twonly/src/database/tables/signal_identity_key_store.table.dart';
import 'package:twonly/src/database/tables/signal_pre_key_store.table.dart'; import 'package:twonly/src/database/tables/signal_pre_key_store.table.dart';
import 'package:twonly/src/database/tables/signal_sender_key_store.table.dart'; import 'package:twonly/src/database/tables/signal_sender_key_store.table.dart';
@ -43,15 +40,12 @@ part 'twonly.db.g.dart';
SignalPreKeyStores, SignalPreKeyStores,
SignalSenderKeyStores, SignalSenderKeyStores,
SignalSessionStores, SignalSessionStores,
SignalContactPreKeys,
SignalContactSignedPreKeys,
MessageActions, MessageActions,
GroupHistories, GroupHistories,
], ],
daos: [ daos: [
MessagesDao, MessagesDao,
ContactsDao, ContactsDao,
SignalDao,
ReceiptsDao, ReceiptsDao,
GroupsDao, GroupsDao,
ReactionsDao, ReactionsDao,
@ -68,7 +62,7 @@ class TwonlyDB extends _$TwonlyDB {
TwonlyDB.forTesting(DatabaseConnection super.connection); TwonlyDB.forTesting(DatabaseConnection super.connection);
@override @override
int get schemaVersion => 7; int get schemaVersion => 8;
static QueryExecutor _openConnection() { static QueryExecutor _openConnection() {
return driftDatabase( return driftDatabase(
@ -99,6 +93,7 @@ class TwonlyDB extends _$TwonlyDB {
}, },
from3To4: (m, schema) async { from3To4: (m, schema) async {
await m.alterTable( await m.alterTable(
// ignore: experimental_member_use
TableMigration( TableMigration(
schema.groupHistories, schema.groupHistories,
columnTransformer: { columnTransformer: {
@ -127,6 +122,15 @@ class TwonlyDB extends _$TwonlyDB {
schema.messages.additionalMessageData, schema.messages.additionalMessageData,
); );
}, },
from7To8: (m, schema) async {
await m.deleteTable('signal_contact_pre_keys');
await m.deleteTable('signal_contact_signed_pre_keys');
// For message_actions
// ignore: experimental_member_use
await m.alterTable(TableMigration(schema.messageHistories));
// ignore: experimental_member_use
await m.alterTable(TableMigration(schema.messageActions));
},
)(m, from, to); )(m, from, to);
}, },
); );
@ -174,8 +178,6 @@ class TwonlyDB extends _$TwonlyDB {
senderProfileCounter: Value(0), senderProfileCounter: Value(0),
), ),
); );
await delete(signalContactPreKeys).go();
await delete(signalContactSignedPreKeys).go();
await (delete(signalPreKeyStores) await (delete(signalPreKeyStores)
..where( ..where(
(t) => (t.createdAt.isSmallerThanValue( (t) => (t.createdAt.isSmallerThanValue(

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -35,7 +35,6 @@ import 'package:twonly/src/services/flame.service.dart';
import 'package:twonly/src/services/group.services.dart'; import 'package:twonly/src/services/group.services.dart';
import 'package:twonly/src/services/notifications/pushkeys.notifications.dart'; import 'package:twonly/src/services/notifications/pushkeys.notifications.dart';
import 'package:twonly/src/services/signal/identity.signal.dart'; import 'package:twonly/src/services/signal/identity.signal.dart';
import 'package:twonly/src/services/signal/prekeys.signal.dart';
import 'package:twonly/src/services/signal/utils.signal.dart'; import 'package:twonly/src/services/signal/utils.signal.dart';
import 'package:twonly/src/services/subscription.service.dart'; import 'package:twonly/src/services/subscription.service.dart';
import 'package:twonly/src/utils/keyvalue.dart'; import 'package:twonly/src/utils/keyvalue.dart';
@ -735,45 +734,6 @@ class ApiService {
return sendRequestSync(req); return sendRequestSync(req);
} }
Future<Response_SignedPreKey?> getSignedKeyByUserId(int userId) async {
final get = ApplicationData_GetSignedPreKeyByUserId()
..userId = Int64(userId);
final appData = ApplicationData()..getSignedPrekeyByUserid = get;
final req = createClientToServerFromApplicationData(appData);
final res = await sendRequestSync(req, contactId: userId);
if (res.isSuccess) {
final ok = res.value as server.Response_Ok;
if (ok.hasSignedprekey()) {
return ok.signedprekey;
}
}
return null;
}
Future<OtherPreKeys?> getPreKeysByUserId(int userId) async {
final get = ApplicationData_GetPrekeysByUserId()..userId = Int64(userId);
final appData = ApplicationData()..getPrekeysByUserId = get;
final req = createClientToServerFromApplicationData(appData);
final res = await sendRequestSync(req, contactId: userId);
if (res.isSuccess) {
final ok = res.value as server.Response_Ok;
if (ok.hasUserdata()) {
final data = ok.userdata;
if (data.hasSignedPrekey() &&
data.hasSignedPrekeyId() &&
data.hasSignedPrekeySignature()) {
return OtherPreKeys(
preKeys: ok.userdata.prekeys,
signedPreKey: data.signedPrekey,
signedPreKeyId: data.signedPrekeyId.toInt(),
signedPreKeySignature: data.signedPrekeySignature,
);
}
}
}
return null;
}
Future<Response_PlanBallance?> loadPlanBalance({bool useCache = true}) async { Future<Response_PlanBallance?> loadPlanBalance({bool useCache = true}) async {
final ballance = await getPlanBallance(); final ballance = await getPlanBallance();
if (ballance != null) { if (ballance != null) {

View file

@ -91,11 +91,6 @@ Future<void> handleClient2ClientMessage(NewMessage newMessage) async {
var retry = false; var retry = false;
if (message.hasPlaintextContent()) { if (message.hasPlaintextContent()) {
if (message.plaintextContent.hasDecryptionErrorMessage()) { if (message.plaintextContent.hasDecryptionErrorMessage()) {
if (message.plaintextContent.decryptionErrorMessage.type ==
PlaintextContent_DecryptionErrorMessage_Type.PREKEY_UNKNOWN) {
// in case there was a pre key error remove all downloaded pre keys. New pre keys will be fetched automatically.
await twonlyDB.signalDao.purgePreKeysFromContact(fromUserId);
}
Log.info( Log.info(
'Got decryption error: ${message.plaintextContent.decryptionErrorMessage.type} for $receiptId', 'Got decryption error: ${message.plaintextContent.decryptionErrorMessage.type} for $receiptId',
); );

View file

@ -4,7 +4,6 @@ import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
import 'package:mutex/mutex.dart'; import 'package:mutex/mutex.dart';
import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart'; import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart';
import 'package:twonly/src/services/signal/consts.signal.dart'; import 'package:twonly/src/services/signal/consts.signal.dart';
import 'package:twonly/src/services/signal/prekeys.signal.dart';
import 'package:twonly/src/services/signal/utils.signal.dart'; import 'package:twonly/src/services/signal/utils.signal.dart';
import 'package:twonly/src/utils/log.dart'; import 'package:twonly/src/utils/log.dart';
@ -19,62 +18,7 @@ Future<CiphertextMessage?> signalEncryptMessage(
try { try {
final signalStore = (await getSignalStore())!; final signalStore = (await getSignalStore())!;
final address = SignalProtocolAddress(target.toString(), defaultDeviceId); final address = SignalProtocolAddress(target.toString(), defaultDeviceId);
final session = SessionCipher.fromStore(signalStore, address); final session = SessionCipher.fromStore(signalStore, address);
final preKey = await getPreKeyByContactId(target);
final signedPreKey = await getSignedPreKeyByContactId(target);
if (signedPreKey != null) {
final sessionBuilder = SessionBuilder.fromSignalStore(
signalStore,
address,
);
ECPublicKey? tempPrePublicKey;
if (preKey != null) {
tempPrePublicKey = Curve.decodePoint(
DjbECPublicKey(
Uint8List.fromList(preKey.preKey),
).serialize(),
1,
);
}
final tempSignedPreKeyPublic = Curve.decodePoint(
DjbECPublicKey(Uint8List.fromList(signedPreKey.signedPreKey))
.serialize(),
1,
);
final tempSignedPreKeySignature = Uint8List.fromList(
signedPreKey.signedPreKeySignature,
);
final tempIdentityKey = await signalStore.getIdentity(address);
if (tempIdentityKey != null) {
final registrationId = await session.getRemoteRegistrationId();
final preKeyBundle = PreKeyBundle(
registrationId,
defaultDeviceId,
preKey?.preKeyId,
tempPrePublicKey,
signedPreKey.signedPreKeyId,
tempSignedPreKeyPublic,
tempSignedPreKeySignature,
tempIdentityKey,
);
try {
await sessionBuilder.processPreKeyBundle(preKeyBundle);
} catch (e) {
Log.error('could not process pre key bundle: $e');
}
} else {
Log.error('did not get the identity of the remote address');
}
}
return await session.encrypt(plaintextContent); return await session.encrypt(plaintextContent);
} catch (e) { } catch (e) {
Log.error(e.toString()); Log.error(e.toString());

View file

@ -1,111 +0,0 @@
import 'dart:async';
import 'package:clock/clock.dart';
import 'package:drift/drift.dart';
import 'package:mutex/mutex.dart';
import 'package:twonly/globals.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart'
as server;
import 'package:twonly/src/utils/log.dart';
class OtherPreKeys {
OtherPreKeys({
required this.preKeys,
required this.signedPreKey,
required this.signedPreKeyId,
required this.signedPreKeySignature,
});
final List<server.Response_PreKey> preKeys;
final int signedPreKeyId;
final List<int> signedPreKey;
final List<int> signedPreKeySignature;
}
Mutex requestNewKeys = Mutex();
DateTime lastPreKeyRequest = clock.now().subtract(const Duration(hours: 1));
DateTime lastSignedPreKeyRequest =
clock.now().subtract(const Duration(hours: 1));
Future<void> requestNewPrekeysForContact(int contactId) async {
if (lastPreKeyRequest
.isAfter(clock.now().subtract(const Duration(seconds: 60)))) {
return;
}
Log.info('[PREKEY] Requesting new PREKEYS for $contactId');
lastPreKeyRequest = clock.now();
await requestNewKeys.protect(() async {
final otherKeys = await apiService.getPreKeysByUserId(contactId);
if (otherKeys != null) {
Log.info(
'[PREKEY] Got fresh ${otherKeys.preKeys.length} pre keys from other $contactId!',
);
final preKeys = otherKeys.preKeys
.map(
(preKey) => SignalContactPreKeysCompanion(
contactId: Value(contactId),
preKey: Value(Uint8List.fromList(preKey.prekey)),
preKeyId: Value(preKey.id.toInt()),
),
)
.toList();
await twonlyDB.signalDao.insertPreKeys(preKeys);
} else {
Log.warn('[PREKEY] Could not load new pre keys for user $contactId');
}
});
}
Future<SignalContactPreKey?> getPreKeyByContactId(int contactId) async {
final count = await twonlyDB.signalDao.countPreKeysByContactId(contactId);
if (count < 10) {
unawaited(requestNewPrekeysForContact(contactId));
}
return twonlyDB.signalDao.popPreKeyByContactId(contactId);
}
Future<void> requestNewSignedPreKeyForContact(int contactId) async {
if (lastSignedPreKeyRequest
.isAfter(clock.now().subtract(const Duration(seconds: 60)))) {
Log.info('last signed pre request was 60s before');
return;
}
lastSignedPreKeyRequest = clock.now();
await requestNewKeys.protect(() async {
final signedPreKey = await apiService.getSignedKeyByUserId(contactId);
if (signedPreKey != null) {
Log.info('got fresh signed pre keys from other $contactId!');
await twonlyDB.signalDao.insertOrUpdateSignedPreKeyByContactId(
SignalContactSignedPreKeysCompanion(
contactId: Value(contactId),
signedPreKey: Value(Uint8List.fromList(signedPreKey.signedPrekey)),
signedPreKeySignature:
Value(Uint8List.fromList(signedPreKey.signedPrekeySignature)),
signedPreKeyId: Value(signedPreKey.signedPrekeyId.toInt()),
),
);
} else {
Log.warn('could not load new signed pre key for user $contactId');
}
});
}
Future<SignalContactSignedPreKey?> getSignedPreKeyByContactId(
int contactId,
) async {
final signedPreKey =
await twonlyDB.signalDao.getSignedPreKeyByContactId(contactId);
if (signedPreKey != null) {
final fortyEightHoursAgo = clock.now().subtract(const Duration(hours: 48));
final isOlderThan48Hours =
signedPreKey.createdAt.isBefore(fortyEightHoursAgo);
if (isOlderThan48Hours) {
unawaited(requestNewSignedPreKeyForContact(contactId));
}
} else {
unawaited(requestNewSignedPreKeyForContact(contactId));
Log.warn('Contact $contactId does not have a signed pre key!');
}
return signedPreKey;
}

View file

@ -1,6 +1,7 @@
// dart format width=80 // dart format width=80
// GENERATED CODE, DO NOT EDIT BY HAND. // GENERATED BY drift_dev, DO NOT MODIFY.
// ignore_for_file: type=lint // ignore_for_file: type=lint,unused_import
//
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:drift/internal/migrations.dart'; import 'package:drift/internal/migrations.dart';
import 'schema_v1.dart' as v1; import 'schema_v1.dart' as v1;
@ -10,6 +11,7 @@ import 'schema_v4.dart' as v4;
import 'schema_v5.dart' as v5; import 'schema_v5.dart' as v5;
import 'schema_v6.dart' as v6; import 'schema_v6.dart' as v6;
import 'schema_v7.dart' as v7; import 'schema_v7.dart' as v7;
import 'schema_v8.dart' as v8;
class GeneratedHelper implements SchemaInstantiationHelper { class GeneratedHelper implements SchemaInstantiationHelper {
@override @override
@ -29,10 +31,12 @@ class GeneratedHelper implements SchemaInstantiationHelper {
return v6.DatabaseAtV6(db); return v6.DatabaseAtV6(db);
case 7: case 7:
return v7.DatabaseAtV7(db); return v7.DatabaseAtV7(db);
case 8:
return v8.DatabaseAtV8(db);
default: default:
throw MissingSchemaException(version, versions); throw MissingSchemaException(version, versions);
} }
} }
static const versions = const [1, 2, 3, 4, 5, 6, 7]; static const versions = const [1, 2, 3, 4, 5, 6, 7, 8];
} }

View file

@ -1,7 +1,8 @@
// dart format width=80 // dart format width=80
import 'dart:typed_data' as i2; import 'dart:typed_data' as i2;
// GENERATED CODE, DO NOT EDIT BY HAND. // GENERATED BY drift_dev, DO NOT MODIFY.
// ignore_for_file: type=lint // ignore_for_file: type=lint,unused_import
//
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
class Contacts extends Table with TableInfo<Contacts, ContactsData> { class Contacts extends Table with TableInfo<Contacts, ContactsData> {

View file

@ -1,7 +1,8 @@
// dart format width=80 // dart format width=80
import 'dart:typed_data' as i2; import 'dart:typed_data' as i2;
// GENERATED CODE, DO NOT EDIT BY HAND. // GENERATED BY drift_dev, DO NOT MODIFY.
// ignore_for_file: type=lint // ignore_for_file: type=lint,unused_import
//
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
class Contacts extends Table with TableInfo<Contacts, ContactsData> { class Contacts extends Table with TableInfo<Contacts, ContactsData> {

View file

@ -1,7 +1,8 @@
// dart format width=80 // dart format width=80
import 'dart:typed_data' as i2; import 'dart:typed_data' as i2;
// GENERATED CODE, DO NOT EDIT BY HAND. // GENERATED BY drift_dev, DO NOT MODIFY.
// ignore_for_file: type=lint // ignore_for_file: type=lint,unused_import
//
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
class Contacts extends Table with TableInfo<Contacts, ContactsData> { class Contacts extends Table with TableInfo<Contacts, ContactsData> {

View file

@ -1,7 +1,8 @@
// dart format width=80 // dart format width=80
import 'dart:typed_data' as i2; import 'dart:typed_data' as i2;
// GENERATED CODE, DO NOT EDIT BY HAND. // GENERATED BY drift_dev, DO NOT MODIFY.
// ignore_for_file: type=lint // ignore_for_file: type=lint,unused_import
//
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
class Contacts extends Table with TableInfo<Contacts, ContactsData> { class Contacts extends Table with TableInfo<Contacts, ContactsData> {

View file

@ -1,7 +1,8 @@
// dart format width=80 // dart format width=80
import 'dart:typed_data' as i2; import 'dart:typed_data' as i2;
// GENERATED CODE, DO NOT EDIT BY HAND. // GENERATED BY drift_dev, DO NOT MODIFY.
// ignore_for_file: type=lint // ignore_for_file: type=lint,unused_import
//
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
class Contacts extends Table with TableInfo<Contacts, ContactsData> { class Contacts extends Table with TableInfo<Contacts, ContactsData> {

View file

@ -1,7 +1,8 @@
// dart format width=80 // dart format width=80
import 'dart:typed_data' as i2; import 'dart:typed_data' as i2;
// GENERATED CODE, DO NOT EDIT BY HAND. // GENERATED BY drift_dev, DO NOT MODIFY.
// ignore_for_file: type=lint // ignore_for_file: type=lint,unused_import
//
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
class Contacts extends Table with TableInfo<Contacts, ContactsData> { class Contacts extends Table with TableInfo<Contacts, ContactsData> {

View file

@ -1,7 +1,8 @@
// dart format width=80 // dart format width=80
import 'dart:typed_data' as i2; import 'dart:typed_data' as i2;
// GENERATED CODE, DO NOT EDIT BY HAND. // GENERATED BY drift_dev, DO NOT MODIFY.
// ignore_for_file: type=lint // ignore_for_file: type=lint,unused_import
//
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
class Contacts extends Table with TableInfo<Contacts, ContactsData> { class Contacts extends Table with TableInfo<Contacts, ContactsData> {

File diff suppressed because it is too large Load diff