switch to smpl key

This commit is contained in:
Christien Rioux 2023-08-07 11:02:29 -04:00
parent e68cbb26eb
commit ee80dbf3a5
13 changed files with 192 additions and 136 deletions

View File

@ -1,6 +1,5 @@
import 'dart:async';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:flutter/material.dart';
import 'package:flutter_chat_types/flutter_chat_types.dart' as types;
import 'package:flutter_chat_ui/flutter_chat_ui.dart';
@ -61,13 +60,13 @@ class ChatComponentState extends ConsumerState<ChatComponent> {
}
Future<void> _loadMessages() async {
final localConversationOwned = proto.OwnedDHTRecordPointerProto.fromProto(
widget.activeChatContact.localConversation);
final localConversationRecordKey = proto.TypedKeyProto.fromProto(
widget.activeChatContact.localConversationRecordKey);
final remoteIdentityPublicKey = proto.TypedKeyProto.fromProto(
widget.activeChatContact.identityPublicKey);
final protoMessages = await getLocalConversationMessages(
activeAccountInfo: widget.activeAccountInfo,
localConversationOwned: localConversationOwned,
localConversationRecordKey: localConversationRecordKey,
remoteIdentityPublicKey: remoteIdentityPublicKey);
if (protoMessages == null) {
return;
@ -108,14 +107,14 @@ class ChatComponentState extends ConsumerState<ChatComponent> {
});
// Now add the message to the conversation messages
final localConversationOwned = proto.OwnedDHTRecordPointerProto.fromProto(
widget.activeChatContact.localConversation);
final localConversationRecordKey = proto.TypedKeyProto.fromProto(
widget.activeChatContact.localConversationRecordKey);
final remoteIdentityPublicKey = proto.TypedKeyProto.fromProto(
widget.activeChatContact.identityPublicKey);
await addLocalConversationMessage(
activeAccountInfo: widget.activeAccountInfo,
localConversationOwned: localConversationOwned,
localConversationRecordKey: localConversationRecordKey,
remoteIdentityPublicKey: remoteIdentityPublicKey,
message: protoMessage);
}

View File

@ -23,8 +23,9 @@ class ChatSingleContactItemWidget extends ConsumerWidget {
final scale = theme.extension<ScaleScheme>()!;
final activeChat = ref.watch(activeChatStateProvider).asData?.value;
final selected = activeChat ==
proto.TypedKeyProto.fromProto(contact.remoteConversationKey);
final remoteConversationRecordKey =
proto.TypedKeyProto.fromProto(contact.remoteConversationRecordKey);
final selected = activeChat == remoteConversationRecordKey;
return Container(
margin: const EdgeInsets.fromLTRB(4, 4, 4, 0),
@ -47,8 +48,7 @@ class ChatSingleContactItemWidget extends ConsumerWidget {
await deleteChat(
activeAccountInfo: activeAccountInfo,
remoteConversationRecordKey:
proto.TypedKeyProto.fromProto(
contact.remoteConversationKey));
remoteConversationRecordKey);
ref.invalidate(fetchChatListProvider);
}
},
@ -71,8 +71,7 @@ class ChatSingleContactItemWidget extends ConsumerWidget {
// component is not dragged.
child: ListTile(
onTap: () async {
activeChatState.add(proto.TypedKeyProto.fromProto(
contact.remoteConversationKey));
activeChatState.add(remoteConversationRecordKey);
ref.invalidate(fetchChatListProvider);
},
title: Text(contact.editedProfile.name),

View File

@ -19,7 +19,8 @@ class ChatSingleContactListWidget extends ConsumerWidget {
required this.chatList,
super.key})
: contactMap = IMap.fromIterable(contactList,
keyMapper: (c) => c.remoteConversationKey, valueMapper: (c) => c);
keyMapper: (c) => c.remoteConversationRecordKey,
valueMapper: (c) => c);
final IMap<proto.TypedKey, proto.Contact> contactMap;
final IList<proto.Chat> chatList;

View File

@ -24,7 +24,7 @@ class ContactItemWidget extends ConsumerWidget {
final scale = theme.extension<ScaleScheme>()!;
final remoteConversationKey =
proto.TypedKeyProto.fromProto(contact.remoteConversationKey);
proto.TypedKeyProto.fromProto(contact.remoteConversationRecordKey);
return Container(
margin: const EdgeInsets.fromLTRB(4, 4, 4, 0),
@ -80,8 +80,12 @@ class ContactItemWidget extends ConsumerWidget {
remoteConversationRecordKey: remoteConversationKey);
// Click over to chats
await MainPager.of(context)?.pageController.animateToPage(1,
duration: 250.ms, curve: Curves.easeInOut);
if (context.mounted) {
await MainPager.of(context)?.pageController.animateToPage(
1,
duration: 250.ms,
curve: Curves.easeInOut);
}
}
// // ignore: use_build_context_synchronously

View File

@ -116,8 +116,10 @@ class PasteInviteDialogState extends ConsumerState<PasteInviteDialog> {
activeAccountInfo: activeAccountInfo,
profile: acceptedContact.profile,
remoteIdentity: acceptedContact.remoteIdentity,
remoteConversationKey: acceptedContact.remoteConversationKey,
localConversation: acceptedContact.localConversation,
remoteConversationRecordKey:
acceptedContact.remoteConversationRecordKey,
localConversationRecordKey:
acceptedContact.localConversationRecordKey,
);
ref
..invalidate(fetchContactInvitationRecordsProvider)

View File

@ -1000,8 +1000,8 @@ class Contact extends $pb.GeneratedMessage {
..aOM<Profile>(2, _omitFieldNames ? '' : 'remoteProfile', subBuilder: Profile.create)
..aOS(3, _omitFieldNames ? '' : 'identityMasterJson')
..aOM<TypedKey>(4, _omitFieldNames ? '' : 'identityPublicKey', subBuilder: TypedKey.create)
..aOM<TypedKey>(5, _omitFieldNames ? '' : 'remoteConversationKey', subBuilder: TypedKey.create)
..aOM<OwnedDHTRecordPointer>(6, _omitFieldNames ? '' : 'localConversation', subBuilder: OwnedDHTRecordPointer.create)
..aOM<TypedKey>(5, _omitFieldNames ? '' : 'remoteConversationRecordKey', subBuilder: TypedKey.create)
..aOM<TypedKey>(6, _omitFieldNames ? '' : 'localConversationRecordKey', subBuilder: TypedKey.create)
..aOB(7, _omitFieldNames ? '' : 'showAvailability')
..hasRequiredFields = false
;
@ -1070,26 +1070,26 @@ class Contact extends $pb.GeneratedMessage {
TypedKey ensureIdentityPublicKey() => $_ensure(3);
@$pb.TagNumber(5)
TypedKey get remoteConversationKey => $_getN(4);
TypedKey get remoteConversationRecordKey => $_getN(4);
@$pb.TagNumber(5)
set remoteConversationKey(TypedKey v) { setField(5, v); }
set remoteConversationRecordKey(TypedKey v) { setField(5, v); }
@$pb.TagNumber(5)
$core.bool hasRemoteConversationKey() => $_has(4);
$core.bool hasRemoteConversationRecordKey() => $_has(4);
@$pb.TagNumber(5)
void clearRemoteConversationKey() => clearField(5);
void clearRemoteConversationRecordKey() => clearField(5);
@$pb.TagNumber(5)
TypedKey ensureRemoteConversationKey() => $_ensure(4);
TypedKey ensureRemoteConversationRecordKey() => $_ensure(4);
@$pb.TagNumber(6)
OwnedDHTRecordPointer get localConversation => $_getN(5);
TypedKey get localConversationRecordKey => $_getN(5);
@$pb.TagNumber(6)
set localConversation(OwnedDHTRecordPointer v) { setField(6, v); }
set localConversationRecordKey(TypedKey v) { setField(6, v); }
@$pb.TagNumber(6)
$core.bool hasLocalConversation() => $_has(5);
$core.bool hasLocalConversationRecordKey() => $_has(5);
@$pb.TagNumber(6)
void clearLocalConversation() => clearField(6);
void clearLocalConversationRecordKey() => clearField(6);
@$pb.TagNumber(6)
OwnedDHTRecordPointer ensureLocalConversation() => $_ensure(5);
TypedKey ensureLocalConversationRecordKey() => $_ensure(5);
@$pb.TagNumber(7)
$core.bool get showAvailability => $_getBF(6);
@ -1654,7 +1654,7 @@ class ContactResponse extends $pb.GeneratedMessage {
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ContactResponse', createEmptyInstance: create)
..aOB(1, _omitFieldNames ? '' : 'accept')
..aOM<TypedKey>(2, _omitFieldNames ? '' : 'identityMasterRecordKey', subBuilder: TypedKey.create)
..aOM<TypedKey>(3, _omitFieldNames ? '' : 'remoteConversationKey', subBuilder: TypedKey.create)
..aOM<TypedKey>(3, _omitFieldNames ? '' : 'remoteConversationRecordKey', subBuilder: TypedKey.create)
..hasRequiredFields = false
;
@ -1700,15 +1700,15 @@ class ContactResponse extends $pb.GeneratedMessage {
TypedKey ensureIdentityMasterRecordKey() => $_ensure(1);
@$pb.TagNumber(3)
TypedKey get remoteConversationKey => $_getN(2);
TypedKey get remoteConversationRecordKey => $_getN(2);
@$pb.TagNumber(3)
set remoteConversationKey(TypedKey v) { setField(3, v); }
set remoteConversationRecordKey(TypedKey v) { setField(3, v); }
@$pb.TagNumber(3)
$core.bool hasRemoteConversationKey() => $_has(2);
$core.bool hasRemoteConversationRecordKey() => $_has(2);
@$pb.TagNumber(3)
void clearRemoteConversationKey() => clearField(3);
void clearRemoteConversationRecordKey() => clearField(3);
@$pb.TagNumber(3)
TypedKey ensureRemoteConversationKey() => $_ensure(2);
TypedKey ensureRemoteConversationRecordKey() => $_ensure(2);
}
class SignedContactResponse extends $pb.GeneratedMessage {
@ -1775,7 +1775,7 @@ class ContactInvitationRecord extends $pb.GeneratedMessage {
..aOM<OwnedDHTRecordPointer>(1, _omitFieldNames ? '' : 'contactRequestInbox', subBuilder: OwnedDHTRecordPointer.create)
..aOM<CryptoKey>(2, _omitFieldNames ? '' : 'writerKey', subBuilder: CryptoKey.create)
..aOM<CryptoKey>(3, _omitFieldNames ? '' : 'writerSecret', subBuilder: CryptoKey.create)
..aOM<OwnedDHTRecordPointer>(4, _omitFieldNames ? '' : 'localConversation', subBuilder: OwnedDHTRecordPointer.create)
..aOM<TypedKey>(4, _omitFieldNames ? '' : 'localConversationRecordKey', subBuilder: TypedKey.create)
..a<$fixnum.Int64>(5, _omitFieldNames ? '' : 'expiration', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO)
..a<$core.List<$core.int>>(6, _omitFieldNames ? '' : 'invitation', $pb.PbFieldType.OY)
..aOS(7, _omitFieldNames ? '' : 'message')
@ -1837,15 +1837,15 @@ class ContactInvitationRecord extends $pb.GeneratedMessage {
CryptoKey ensureWriterSecret() => $_ensure(2);
@$pb.TagNumber(4)
OwnedDHTRecordPointer get localConversation => $_getN(3);
TypedKey get localConversationRecordKey => $_getN(3);
@$pb.TagNumber(4)
set localConversation(OwnedDHTRecordPointer v) { setField(4, v); }
set localConversationRecordKey(TypedKey v) { setField(4, v); }
@$pb.TagNumber(4)
$core.bool hasLocalConversation() => $_has(3);
$core.bool hasLocalConversationRecordKey() => $_has(3);
@$pb.TagNumber(4)
void clearLocalConversation() => clearField(4);
void clearLocalConversationRecordKey() => clearField(4);
@$pb.TagNumber(4)
OwnedDHTRecordPointer ensureLocalConversation() => $_ensure(3);
TypedKey ensureLocalConversationRecordKey() => $_ensure(3);
@$pb.TagNumber(5)
$fixnum.Int64 get expiration => $_getI64(4);

View File

@ -303,8 +303,8 @@ const Contact$json = {
{'1': 'remote_profile', '3': 2, '4': 1, '5': 11, '6': '.Profile', '10': 'remoteProfile'},
{'1': 'identity_master_json', '3': 3, '4': 1, '5': 9, '10': 'identityMasterJson'},
{'1': 'identity_public_key', '3': 4, '4': 1, '5': 11, '6': '.TypedKey', '10': 'identityPublicKey'},
{'1': 'remote_conversation_key', '3': 5, '4': 1, '5': 11, '6': '.TypedKey', '10': 'remoteConversationKey'},
{'1': 'local_conversation', '3': 6, '4': 1, '5': 11, '6': '.OwnedDHTRecordPointer', '10': 'localConversation'},
{'1': 'remote_conversation_record_key', '3': 5, '4': 1, '5': 11, '6': '.TypedKey', '10': 'remoteConversationRecordKey'},
{'1': 'local_conversation_record_key', '3': 6, '4': 1, '5': 11, '6': '.TypedKey', '10': 'localConversationRecordKey'},
{'1': 'show_availability', '3': 7, '4': 1, '5': 8, '10': 'showAvailability'},
],
};
@ -314,11 +314,11 @@ final $typed_data.Uint8List contactDescriptor = $convert.base64Decode(
'CgdDb250YWN0Ei8KDmVkaXRlZF9wcm9maWxlGAEgASgLMgguUHJvZmlsZVINZWRpdGVkUHJvZm'
'lsZRIvCg5yZW1vdGVfcHJvZmlsZRgCIAEoCzIILlByb2ZpbGVSDXJlbW90ZVByb2ZpbGUSMAoU'
'aWRlbnRpdHlfbWFzdGVyX2pzb24YAyABKAlSEmlkZW50aXR5TWFzdGVySnNvbhI5ChNpZGVudG'
'l0eV9wdWJsaWNfa2V5GAQgASgLMgkuVHlwZWRLZXlSEWlkZW50aXR5UHVibGljS2V5EkEKF3Jl'
'bW90ZV9jb252ZXJzYXRpb25fa2V5GAUgASgLMgkuVHlwZWRLZXlSFXJlbW90ZUNvbnZlcnNhdG'
'lvbktleRJFChJsb2NhbF9jb252ZXJzYXRpb24YBiABKAsyFi5Pd25lZERIVFJlY29yZFBvaW50'
'ZXJSEWxvY2FsQ29udmVyc2F0aW9uEisKEXNob3dfYXZhaWxhYmlsaXR5GAcgASgIUhBzaG93QX'
'ZhaWxhYmlsaXR5');
'l0eV9wdWJsaWNfa2V5GAQgASgLMgkuVHlwZWRLZXlSEWlkZW50aXR5UHVibGljS2V5Ek4KHnJl'
'bW90ZV9jb252ZXJzYXRpb25fcmVjb3JkX2tleRgFIAEoCzIJLlR5cGVkS2V5UhtyZW1vdGVDb2'
'52ZXJzYXRpb25SZWNvcmRLZXkSTAodbG9jYWxfY29udmVyc2F0aW9uX3JlY29yZF9rZXkYBiAB'
'KAsyCS5UeXBlZEtleVIabG9jYWxDb252ZXJzYXRpb25SZWNvcmRLZXkSKwoRc2hvd19hdmFpbG'
'FiaWxpdHkYByABKAhSEHNob3dBdmFpbGFiaWxpdHk=');
@$core.Deprecated('Use profileDescriptor instead')
const Profile$json = {
@ -462,7 +462,7 @@ const ContactResponse$json = {
'2': [
{'1': 'accept', '3': 1, '4': 1, '5': 8, '10': 'accept'},
{'1': 'identity_master_record_key', '3': 2, '4': 1, '5': 11, '6': '.TypedKey', '10': 'identityMasterRecordKey'},
{'1': 'remote_conversation_key', '3': 3, '4': 1, '5': 11, '6': '.TypedKey', '10': 'remoteConversationKey'},
{'1': 'remote_conversation_record_key', '3': 3, '4': 1, '5': 11, '6': '.TypedKey', '10': 'remoteConversationRecordKey'},
],
};
@ -470,8 +470,8 @@ const ContactResponse$json = {
final $typed_data.Uint8List contactResponseDescriptor = $convert.base64Decode(
'Cg9Db250YWN0UmVzcG9uc2USFgoGYWNjZXB0GAEgASgIUgZhY2NlcHQSRgoaaWRlbnRpdHlfbW'
'FzdGVyX3JlY29yZF9rZXkYAiABKAsyCS5UeXBlZEtleVIXaWRlbnRpdHlNYXN0ZXJSZWNvcmRL'
'ZXkSQQoXcmVtb3RlX2NvbnZlcnNhdGlvbl9rZXkYAyABKAsyCS5UeXBlZEtleVIVcmVtb3RlQ2'
'9udmVyc2F0aW9uS2V5');
'ZXkSTgoecmVtb3RlX2NvbnZlcnNhdGlvbl9yZWNvcmRfa2V5GAMgASgLMgkuVHlwZWRLZXlSG3'
'JlbW90ZUNvbnZlcnNhdGlvblJlY29yZEtleQ==');
@$core.Deprecated('Use signedContactResponseDescriptor instead')
const SignedContactResponse$json = {
@ -495,7 +495,7 @@ const ContactInvitationRecord$json = {
{'1': 'contact_request_inbox', '3': 1, '4': 1, '5': 11, '6': '.OwnedDHTRecordPointer', '10': 'contactRequestInbox'},
{'1': 'writer_key', '3': 2, '4': 1, '5': 11, '6': '.CryptoKey', '10': 'writerKey'},
{'1': 'writer_secret', '3': 3, '4': 1, '5': 11, '6': '.CryptoKey', '10': 'writerSecret'},
{'1': 'local_conversation', '3': 4, '4': 1, '5': 11, '6': '.OwnedDHTRecordPointer', '10': 'localConversation'},
{'1': 'local_conversation_record_key', '3': 4, '4': 1, '5': 11, '6': '.TypedKey', '10': 'localConversationRecordKey'},
{'1': 'expiration', '3': 5, '4': 1, '5': 4, '10': 'expiration'},
{'1': 'invitation', '3': 6, '4': 1, '5': 12, '10': 'invitation'},
{'1': 'message', '3': 7, '4': 1, '5': 9, '10': 'message'},
@ -507,8 +507,8 @@ final $typed_data.Uint8List contactInvitationRecordDescriptor = $convert.base64D
'ChdDb250YWN0SW52aXRhdGlvblJlY29yZBJKChVjb250YWN0X3JlcXVlc3RfaW5ib3gYASABKA'
'syFi5Pd25lZERIVFJlY29yZFBvaW50ZXJSE2NvbnRhY3RSZXF1ZXN0SW5ib3gSKQoKd3JpdGVy'
'X2tleRgCIAEoCzIKLkNyeXB0b0tleVIJd3JpdGVyS2V5Ei8KDXdyaXRlcl9zZWNyZXQYAyABKA'
'syCi5DcnlwdG9LZXlSDHdyaXRlclNlY3JldBJFChJsb2NhbF9jb252ZXJzYXRpb24YBCABKAsy'
'Fi5Pd25lZERIVFJlY29yZFBvaW50ZXJSEWxvY2FsQ29udmVyc2F0aW9uEh4KCmV4cGlyYXRpb2'
'4YBSABKARSCmV4cGlyYXRpb24SHgoKaW52aXRhdGlvbhgGIAEoDFIKaW52aXRhdGlvbhIYCgdt'
'ZXNzYWdlGAcgASgJUgdtZXNzYWdl');
'syCi5DcnlwdG9LZXlSDHdyaXRlclNlY3JldBJMCh1sb2NhbF9jb252ZXJzYXRpb25fcmVjb3Jk'
'X2tleRgEIAEoCzIJLlR5cGVkS2V5Uhpsb2NhbENvbnZlcnNhdGlvblJlY29yZEtleRIeCgpleH'
'BpcmF0aW9uGAUgASgEUgpleHBpcmF0aW9uEh4KCmludml0YXRpb24YBiABKAxSCmludml0YXRp'
'b24SGAoHbWVzc2FnZRgHIAEoCVIHbWVzc2FnZQ==');

View File

@ -204,9 +204,9 @@ message Contact {
// Copy of friend's most recent identity public key from their identityMaster
TypedKey identity_public_key = 4;
// Remote conversation key to sync from friend
TypedKey remote_conversation_key = 5;
TypedKey remote_conversation_record_key = 5;
// Our conversation key for friend to sync
OwnedDHTRecordPointer local_conversation = 6;
TypedKey local_conversation_record_key = 6;
// Show availability
bool show_availability = 7;
}
@ -343,7 +343,7 @@ message ContactResponse {
// Remote identity master DHT record key
TypedKey identity_master_record_key = 2;
// Remote chat DHT record key if accepted
TypedKey remote_conversation_key = 3;
TypedKey remote_conversation_record_key = 3;
}
// Signature of response with identity
@ -364,7 +364,7 @@ message ContactInvitationRecord {
// Writer secret sent encrypted in the invitation
CryptoKey writer_secret = 3;
// Local chat DHT record key (parent is accountkey, will be moved to Contact if accepted)
OwnedDHTRecordPointer local_conversation = 4;
TypedKey local_conversation_record_key = 4;
// Expiration timestamp
uint64 expiration = 5;
// A copy of the raw SignedContactInvitation invitation bytes post-encryption and signing

View File

@ -14,6 +14,7 @@ import '../providers/account.dart';
import '../providers/chat.dart';
import '../providers/contact.dart';
import '../providers/contact_invite.dart';
import '../providers/conversation.dart';
import '../providers/window_control.dart';
import '../tools/tools.dart';
import 'main_pager/main_pager.dart';
@ -113,8 +114,10 @@ class HomePageState extends ConsumerState<HomePage>
activeAccountInfo: activeAccountInfo,
profile: acceptedContact.profile,
remoteIdentity: acceptedContact.remoteIdentity,
remoteConversationKey: acceptedContact.remoteConversationKey,
localConversation: acceptedContact.localConversation,
remoteConversationRecordKey:
acceptedContact.remoteConversationRecordKey,
localConversationRecordKey:
acceptedContact.localConversationRecordKey,
);
ref
..invalidate(fetchContactInvitationRecordsProvider)
@ -134,19 +137,34 @@ class HomePageState extends ConsumerState<HomePage>
if (activeChat == null) {
return;
}
final activeAccountInfo = await ref.read(fetchActiveAccountProvider.future);
if (activeAccountInfo == null) {
return;
}
final contactList = ref.read(fetchContactListProvider).asData?.value ??
const IListConst([]);
final activeChatContactIdx = contactList.indexWhere(
(c) =>
proto.TypedKeyProto.fromProto(c.remoteConversationKey) == activeChat,
proto.TypedKeyProto.fromProto(c.remoteConversationRecordKey) ==
activeChat,
);
if (activeChatContactIdx == -1) {
return;
}
final activeChatContact = contactList[activeChatContactIdx];
final remoteIdentityPublicKey =
proto.TypedKeyProto.fromProto(activeChatContact.identityPublicKey);
final remoteConversationRecordKey = proto.TypedKeyProto.fromProto(
activeChatContact.remoteConversationRecordKey);
//activeChatContact.rem
await getRemoteConversationMessages(
activeAccountInfo: activeAccountInfo,
remoteIdentityPublicKey: remoteIdentityPublicKey,
remoteConversationRecordKey: remoteConversationRecordKey);
// xxx add messages
}
// ignore: prefer_expression_function_bodies
@ -186,7 +204,8 @@ class HomePageState extends ConsumerState<HomePage>
final activeChatContactIdx = contactList.indexWhere(
(c) =>
proto.TypedKeyProto.fromProto(c.remoteConversationKey) == activeChat,
proto.TypedKeyProto.fromProto(c.remoteConversationRecordKey) ==
activeChat,
);
if (activeChatContactIdx == -1) {
activeChatState.add(null);

View File

@ -17,8 +17,8 @@ Future<void> createContact({
required ActiveAccountInfo activeAccountInfo,
required proto.Profile profile,
required IdentityMaster remoteIdentity,
required TypedKey remoteConversationKey,
required OwnedDHTRecordPointer localConversation,
required TypedKey remoteConversationRecordKey,
required TypedKey localConversationRecordKey,
}) async {
final accountRecordKey =
activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;
@ -32,8 +32,8 @@ Future<void> createContact({
kind: remoteIdentity.identityRecordKey.kind,
value: remoteIdentity.identityPublicKey)
.toProto()
..remoteConversationKey = remoteConversationKey.toProto()
..localConversation = localConversation.toProto()
..remoteConversationRecordKey = remoteConversationRecordKey.toProto()
..localConversationRecordKey = localConversationRecordKey.toProto()
..showAvailability = false;
// Add Contact to account's list
@ -56,7 +56,7 @@ Future<void> deleteContact(
final accountRecordKey =
activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;
final remoteConversationKey =
proto.TypedKeyProto.fromProto(contact.remoteConversationKey);
proto.TypedKeyProto.fromProto(contact.remoteConversationRecordKey);
// Remove any chats for this contact
await deleteChat(
@ -75,14 +75,14 @@ Future<void> deleteContact(
if (item == null) {
throw Exception('Failed to get contact');
}
if (item.remoteConversationKey == contact.remoteConversationKey) {
if (item.remoteConversationRecordKey ==
contact.remoteConversationRecordKey) {
await contactList.tryRemoveItem(i);
break;
}
}
await (await pool.openOwned(
proto.OwnedDHTRecordPointerProto.fromProto(
contact.localConversation),
await (await pool.openRead(
proto.TypedKeyProto.fromProto(contact.localConversationRecordKey),
parent: accountRecordKey))
.delete();
await (await pool.openRead(remoteConversationKey, parent: accountRecordKey))

View File

@ -28,14 +28,14 @@ class AcceptedContact {
AcceptedContact({
required this.profile,
required this.remoteIdentity,
required this.remoteConversationKey,
required this.localConversation,
required this.remoteConversationRecordKey,
required this.localConversationRecordKey,
});
proto.Profile profile;
IdentityMaster remoteIdentity;
TypedKey remoteConversationKey;
OwnedDHTRecordPointer localConversation;
TypedKey remoteConversationRecordKey;
TypedKey localConversationRecordKey;
}
class AcceptedOrRejectedContact {
@ -92,33 +92,33 @@ Future<AcceptedOrRejectedContact?> checkAcceptRejectContact(
contactResponseBytes, signature);
// Pull profile from remote conversation key
final remoteConversationKey =
proto.TypedKeyProto.fromProto(contactResponse.remoteConversationKey);
final remoteConversationRecordKey = proto.TypedKeyProto.fromProto(
contactResponse.remoteConversationRecordKey);
final remoteConversation = await readRemoteConversation(
activeAccountInfo: activeAccountInfo,
remoteIdentityPublicKey:
contactIdentityMaster.identityPublicTypedKey(),
remoteConversationKey: remoteConversationKey);
remoteConversationRecordKey: remoteConversationRecordKey);
if (remoteConversation == null) {
log.info('Remote conversation could not be read. Waiting...');
return null;
}
// Complete the local conversation now that we have the remote profile
final localConversationOwned = proto.OwnedDHTRecordPointerProto.fromProto(
contactInvitationRecord.localConversation);
final localConversationRecordKey = proto.TypedKeyProto.fromProto(
contactInvitationRecord.localConversationRecordKey);
return createConversation(
activeAccountInfo: activeAccountInfo,
remoteIdentityPublicKey:
contactIdentityMaster.identityPublicTypedKey(),
existingConversationOwned: localConversationOwned,
existingConversationRecordKey: localConversationRecordKey,
// ignore: prefer_expression_function_bodies
callback: (localConversation) async {
return AcceptedOrRejectedContact(
acceptedContact: AcceptedContact(
profile: remoteConversation.profile,
remoteIdentity: contactIdentityMaster,
remoteConversationKey: remoteConversationKey,
localConversation: localConversationOwned));
remoteConversationRecordKey: remoteConversationRecordKey,
localConversationRecordKey: localConversationRecordKey));
});
});
@ -182,9 +182,9 @@ Future<void> deleteContactInvitation(
await contactRequestInbox.delete();
});
if (!accepted) {
await (await pool.openOwned(
proto.OwnedDHTRecordPointerProto.fromProto(
contactInvitationRecord.localConversation),
await (await pool.openRead(
proto.TypedKeyProto.fromProto(
contactInvitationRecord.localConversationRecordKey),
parent: accountRecordKey))
.delete();
}
@ -206,11 +206,13 @@ Future<Uint8List> createContactInvitation(
// Generate writer keypair to share with new contact
final cs = await pool.veilid.bestCryptoSystem();
final writer = await cs.generateKeyPair();
final contactRequestWriter = await cs.generateKeyPair();
final conversationWriter =
getConversationWriter(activeAccountInfo: activeAccountInfo);
// Encrypt the writer secret with the encryption key
final encryptedSecret = await encryptSecretToBytes(
secret: writer.secret,
secret: contactRequestWriter.secret,
cryptoKind: cs.kind(),
encryptionKey: encryptionKey,
encryptionKeyType: encryptionKeyType);
@ -220,19 +222,24 @@ Future<Uint8List> createContactInvitation(
// to and it will be eventually encrypted with the DH of the contact's
// identity key
late final Uint8List signedContactInvitationBytes;
await (await pool.create(parent: accountRecordKey))
await (await pool.create(
parent: accountRecordKey,
schema: DHTSchema.smpl(oCnt: 0, members: [
DHTSchemaMember(mKey: conversationWriter.key, mCnt: 1)
])))
.deleteScope((localConversation) async {
// dont bother reopening localConversation with writer
// Make ContactRequestPrivate and encrypt with the writer secret
final crpriv = ContactRequestPrivate()
..writerKey = writer.key.toProto()
..writerKey = contactRequestWriter.key.toProto()
..profile = activeAccountInfo.account.profile
..identityMasterRecordKey =
activeAccountInfo.userLogin.accountMasterRecordKey.toProto()
..chatRecordKey = localConversation.key.toProto()
..expiration = expiration?.toInt64() ?? Int64.ZERO;
final crprivbytes = crpriv.writeToBuffer();
final encryptedContactRequestPrivate =
await cs.encryptNoAuthWithNonce(crprivbytes, writer.secret);
final encryptedContactRequestPrivate = await cs.encryptNoAuthWithNonce(
crprivbytes, contactRequestWriter.secret);
// Create ContactRequest and embed contactrequestprivate
final creq = ContactRequest()
@ -242,8 +249,9 @@ Future<Uint8List> createContactInvitation(
// Create DHT unicast inbox for ContactRequest
await (await pool.create(
parent: accountRecordKey,
schema: DHTSchema.smpl(
oCnt: 1, members: [DHTSchemaMember(mCnt: 1, mKey: writer.key)]),
schema: DHTSchema.smpl(oCnt: 1, members: [
DHTSchemaMember(mCnt: 1, mKey: contactRequestWriter.key)
]),
crypto: const DHTRecordCryptoPublic()))
.deleteScope((contactRequestInbox) async {
// Store ContactRequest in owner subkey
@ -264,9 +272,9 @@ Future<Uint8List> createContactInvitation(
final cinvrec = ContactInvitationRecord()
..contactRequestInbox =
contactRequestInbox.ownedDHTRecordPointer.toProto()
..writerKey = writer.key.toProto()
..writerSecret = writer.secret.toProto()
..localConversation = localConversation.ownedDHTRecordPointer.toProto()
..writerKey = contactRequestWriter.key.toProto()
..writerSecret = contactRequestWriter.secret.toProto()
..localConversationRecordKey = localConversation.key.toProto()
..expiration = expiration?.toInt64() ?? Int64.ZERO
..invitation = signedContactInvitationBytes
..message = message;
@ -390,7 +398,7 @@ Future<AcceptedContact?> acceptContactInvitation(
callback: (localConversation) async {
final contactResponse = ContactResponse()
..accept = true
..remoteConversationKey = localConversation.key.toProto()
..remoteConversationRecordKey = localConversation.key.toProto()
..identityMasterRecordKey = activeAccountInfo
.localAccount.identityMaster.masterRecordKey
.toProto();
@ -418,9 +426,9 @@ Future<AcceptedContact?> acceptContactInvitation(
return AcceptedContact(
profile: validContactInvitation.contactRequestPrivate.profile,
remoteIdentity: validContactInvitation.contactIdentityMaster,
remoteConversationKey: proto.TypedKeyProto.fromProto(
remoteConversationRecordKey: proto.TypedKeyProto.fromProto(
validContactInvitation.contactRequestPrivate.chatRecordKey),
localConversation: localConversation.ownedDHTRecordPointer,
localConversationRecordKey: localConversation.key,
);
});
});

View File

@ -24,6 +24,15 @@ Future<DHTRecordCrypto> getConversationCrypto({
return DHTRecordCryptoPrivate.fromSecret(identitySecret.kind, sharedSecret);
}
KeyPair getConversationWriter({
required ActiveAccountInfo activeAccountInfo,
}) {
final identityKey =
activeAccountInfo.localAccount.identityMaster.identityPublicKey;
final identitySecret = activeAccountInfo.userLogin.identitySecret;
return KeyPair(key: identityKey, secret: identitySecret.value);
}
// Create a conversation
// If we were the initator of the conversation there may be an
// incomplete 'existingConversationRecord' that we need to fill
@ -32,7 +41,7 @@ Future<T> createConversation<T>(
{required ActiveAccountInfo activeAccountInfo,
required TypedKey remoteIdentityPublicKey,
required FutureOr<T> Function(DHTRecord) callback,
OwnedDHTRecordPointer? existingConversationOwned}) async {
TypedKey? existingConversationRecordKey}) async {
final pool = await DHTRecordPool.instance();
final accountRecordKey =
activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;
@ -40,28 +49,38 @@ Future<T> createConversation<T>(
final crypto = await getConversationCrypto(
activeAccountInfo: activeAccountInfo,
remoteIdentityPublicKey: remoteIdentityPublicKey);
final writer = getConversationWriter(activeAccountInfo: activeAccountInfo);
// Open with SMPL scheme for identity writer
late final DHTRecord localConversationRecord;
if (existingConversationOwned != null) {
localConversationRecord = await pool.openOwned(existingConversationOwned,
if (existingConversationRecordKey != null) {
localConversationRecord = await pool.openWrite(
existingConversationRecordKey, writer,
parent: accountRecordKey, crypto: crypto);
} else {
localConversationRecord =
await pool.create(parent: accountRecordKey, crypto: crypto);
final localConversationRecordCreate = await pool.create(
parent: accountRecordKey,
crypto: crypto,
schema: DHTSchema.smpl(
oCnt: 0, members: [DHTSchemaMember(mKey: writer.key, mCnt: 1)]));
await localConversationRecordCreate.close();
localConversationRecord = await pool.openWrite(
localConversationRecordCreate.key, writer,
parent: accountRecordKey, crypto: crypto);
}
return localConversationRecord
// ignore: prefer_expression_function_bodies
.deleteScope((localConversation) async {
// Make messages log
return (await DHTShortArray.create(
parent: localConversation.key, crypto: crypto))
parent: localConversation.key, crypto: crypto, smplWriter: writer))
.deleteScope((messages) async {
// Write local conversation key
final conversation = Conversation()
..profile = activeAccountInfo.account.profile
..identityMasterJson =
jsonEncode(activeAccountInfo.localAccount.identityMaster.toJson())
..messages = messages.record.ownedDHTRecordPointer.toProto();
..messages = messages.record.key.toProto();
//
final update = await localConversation.tryWriteProtobuf(
@ -76,8 +95,8 @@ Future<T> createConversation<T>(
Future<Conversation?> readRemoteConversation({
required ActiveAccountInfo activeAccountInfo,
required TypedKey remoteConversationRecordKey,
required TypedKey remoteIdentityPublicKey,
required TypedKey remoteConversationKey,
}) async {
final accountRecordKey =
activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;
@ -86,7 +105,7 @@ Future<Conversation?> readRemoteConversation({
final crypto = await getConversationCrypto(
activeAccountInfo: activeAccountInfo,
remoteIdentityPublicKey: remoteIdentityPublicKey);
return (await pool.openRead(remoteConversationKey,
return (await pool.openRead(remoteConversationRecordKey,
parent: accountRecordKey, crypto: crypto))
.scope((remoteConversation) async {
//
@ -98,7 +117,7 @@ Future<Conversation?> readRemoteConversation({
Future<Conversation?> writeLocalConversation({
required ActiveAccountInfo activeAccountInfo,
required OwnedDHTRecordPointer localConversationOwned,
required TypedKey localConversationRecordKey,
required TypedKey remoteIdentityPublicKey,
required Conversation conversation,
}) async {
@ -109,8 +128,9 @@ Future<Conversation?> writeLocalConversation({
final crypto = await getConversationCrypto(
activeAccountInfo: activeAccountInfo,
remoteIdentityPublicKey: remoteIdentityPublicKey);
final writer = getConversationWriter(activeAccountInfo: activeAccountInfo);
return (await pool.openOwned(localConversationOwned,
return (await pool.openWrite(localConversationRecordKey, writer,
parent: accountRecordKey, crypto: crypto))
.scope((localConversation) async {
//
@ -125,7 +145,7 @@ Future<Conversation?> writeLocalConversation({
Future<Conversation?> readLocalConversation({
required ActiveAccountInfo activeAccountInfo,
required OwnedDHTRecordPointer localConversationOwned,
required TypedKey localConversationRecordKey,
required TypedKey remoteIdentityPublicKey,
}) async {
final accountRecordKey =
@ -136,7 +156,7 @@ Future<Conversation?> readLocalConversation({
activeAccountInfo: activeAccountInfo,
remoteIdentityPublicKey: remoteIdentityPublicKey);
return (await pool.openOwned(localConversationOwned,
return (await pool.openRead(localConversationRecordKey,
parent: accountRecordKey, crypto: crypto))
.scope((localConversation) async {
//
@ -150,24 +170,25 @@ Future<Conversation?> readLocalConversation({
Future<void> addLocalConversationMessage(
{required ActiveAccountInfo activeAccountInfo,
required OwnedDHTRecordPointer localConversationOwned,
required TypedKey localConversationRecordKey,
required TypedKey remoteIdentityPublicKey,
required proto.Message message}) async {
final conversation = await readLocalConversation(
activeAccountInfo: activeAccountInfo,
localConversationOwned: localConversationOwned,
localConversationRecordKey: localConversationRecordKey,
remoteIdentityPublicKey: remoteIdentityPublicKey);
if (conversation == null) {
return;
}
final messagesOwned =
proto.OwnedDHTRecordPointerProto.fromProto(conversation.messages);
final messagesRecordKey =
proto.TypedKeyProto.fromProto(conversation.messages);
final crypto = await getConversationCrypto(
activeAccountInfo: activeAccountInfo,
remoteIdentityPublicKey: remoteIdentityPublicKey);
final writer = getConversationWriter(activeAccountInfo: activeAccountInfo);
await (await DHTShortArray.openOwned(messagesOwned,
parent: localConversationOwned.recordKey, crypto: crypto))
await (await DHTShortArray.openWrite(messagesRecordKey, writer,
parent: localConversationRecordKey, crypto: crypto))
.scope((messages) async {
await messages.tryAddItem(message.writeToBuffer());
});
@ -175,24 +196,24 @@ Future<void> addLocalConversationMessage(
Future<IList<proto.Message>?> getLocalConversationMessages({
required ActiveAccountInfo activeAccountInfo,
required OwnedDHTRecordPointer localConversationOwned,
required TypedKey localConversationRecordKey,
required TypedKey remoteIdentityPublicKey,
}) async {
final conversation = await readLocalConversation(
activeAccountInfo: activeAccountInfo,
localConversationOwned: localConversationOwned,
localConversationRecordKey: localConversationRecordKey,
remoteIdentityPublicKey: remoteIdentityPublicKey);
if (conversation == null) {
return null;
}
final messagesOwned =
proto.OwnedDHTRecordPointerProto.fromProto(conversation.messages);
final messagesRecordKey =
proto.TypedKeyProto.fromProto(conversation.messages);
final crypto = await getConversationCrypto(
activeAccountInfo: activeAccountInfo,
remoteIdentityPublicKey: remoteIdentityPublicKey);
return (await DHTShortArray.openOwned(messagesOwned,
parent: localConversationOwned.recordKey, crypto: crypto))
return (await DHTShortArray.openRead(messagesRecordKey,
parent: localConversationRecordKey, crypto: crypto))
.scope((messages) async {
var out = IList<proto.Message>();
for (var i = 0; i < messages.length; i++) {
@ -208,24 +229,24 @@ Future<IList<proto.Message>?> getLocalConversationMessages({
Future<IList<proto.Message>?> getRemoteConversationMessages({
required ActiveAccountInfo activeAccountInfo,
required TypedKey remoteConversationKey,
required TypedKey remoteConversationRecordKey,
required TypedKey remoteIdentityPublicKey,
}) async {
final conversation = await readRemoteConversation(
activeAccountInfo: activeAccountInfo,
remoteConversationKey: remoteConversationKey,
remoteConversationRecordKey: remoteConversationRecordKey,
remoteIdentityPublicKey: remoteIdentityPublicKey);
if (conversation == null) {
return null;
}
final messagesOwned =
proto.OwnedDHTRecordPointerProto.fromProto(conversation.messages);
final messagesRecordKey =
proto.TypedKeyProto.fromProto(conversation.messages);
final crypto = await getConversationCrypto(
activeAccountInfo: activeAccountInfo,
remoteIdentityPublicKey: remoteIdentityPublicKey);
return (await DHTShortArray.openOwned(messagesOwned,
parent: localConversationOwned.recordKey, crypto: crypto))
return (await DHTShortArray.openRead(messagesRecordKey,
parent: remoteConversationRecordKey, crypto: crypto))
.scope((messages) async {
var out = IList<proto.Message>();
for (var i = 0; i < messages.length; i++) {

View File

@ -60,6 +60,9 @@ class DHTShortArray {
// Cached representation refreshed from head record
_DHTShortArrayCache _head;
// Create a DHTShortArray
// if smplWriter is specified, uses a SMPL schema with a single writer
// rather than the key owner
static Future<DHTShortArray> create(
{int stride = maxElements,
VeilidRoutingContext? routingContext,