mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2025-02-23 08:19:49 -05:00
tabledb array work
This commit is contained in:
parent
83c8715742
commit
5d89de9bfe
@ -24,7 +24,7 @@ class ActiveAccountInfo {
|
|||||||
return KeyPair(key: identityKey, secret: identitySecret.value);
|
return KeyPair(key: identityKey, secret: identitySecret.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<DHTRecordCrypto> makeConversationCrypto(
|
Future<VeilidCrypto> makeConversationCrypto(
|
||||||
TypedKey remoteIdentityPublicKey) async {
|
TypedKey remoteIdentityPublicKey) async {
|
||||||
final identitySecret = userLogin.identitySecret;
|
final identitySecret = userLogin.identitySecret;
|
||||||
final cs = await Veilid.instance.getCryptoSystem(identitySecret.kind);
|
final cs = await Veilid.instance.getCryptoSystem(identitySecret.kind);
|
||||||
@ -33,8 +33,8 @@ class ActiveAccountInfo {
|
|||||||
identitySecret.value,
|
identitySecret.value,
|
||||||
utf8.encode('VeilidChat Conversation'));
|
utf8.encode('VeilidChat Conversation'));
|
||||||
|
|
||||||
final messagesCrypto = await DHTRecordCryptoPrivate.fromSecret(
|
final messagesCrypto =
|
||||||
identitySecret.kind, sharedSecret);
|
await VeilidCryptoPrivate.fromSecret(identitySecret.kind, sharedSecret);
|
||||||
return messagesCrypto;
|
return messagesCrypto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,9 +120,8 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
|||||||
Future<void> _initSentMessagesCubit() async {
|
Future<void> _initSentMessagesCubit() async {
|
||||||
final writer = _activeAccountInfo.conversationWriter;
|
final writer = _activeAccountInfo.conversationWriter;
|
||||||
|
|
||||||
_sentMessagesCubit = DHTShortArrayCubit(
|
_sentMessagesCubit = DHTLogCubit(
|
||||||
open: () async => DHTShortArray.openWrite(
|
open: () async => DHTLog.openWrite(_localMessagesRecordKey, writer,
|
||||||
_localMessagesRecordKey, writer,
|
|
||||||
debugName: 'SingleContactMessagesCubit::_initSentMessagesCubit::'
|
debugName: 'SingleContactMessagesCubit::_initSentMessagesCubit::'
|
||||||
'SentMessages',
|
'SentMessages',
|
||||||
parent: _localConversationRecordKey,
|
parent: _localConversationRecordKey,
|
||||||
@ -135,8 +134,8 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
|||||||
|
|
||||||
// Open remote messages key
|
// Open remote messages key
|
||||||
Future<void> _initRcvdMessagesCubit() async {
|
Future<void> _initRcvdMessagesCubit() async {
|
||||||
_rcvdMessagesCubit = DHTShortArrayCubit(
|
_rcvdMessagesCubit = DHTLogCubit(
|
||||||
open: () async => DHTShortArray.openRead(_remoteMessagesRecordKey,
|
open: () async => DHTLog.openRead(_remoteMessagesRecordKey,
|
||||||
debugName: 'SingleContactMessagesCubit::_initRcvdMessagesCubit::'
|
debugName: 'SingleContactMessagesCubit::_initRcvdMessagesCubit::'
|
||||||
'RcvdMessages',
|
'RcvdMessages',
|
||||||
parent: _remoteConversationRecordKey,
|
parent: _remoteConversationRecordKey,
|
||||||
@ -152,8 +151,8 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
|||||||
final accountRecordKey =
|
final accountRecordKey =
|
||||||
_activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;
|
_activeAccountInfo.userLogin.accountRecordInfo.accountRecord.recordKey;
|
||||||
|
|
||||||
_reconciledMessagesCubit = DHTShortArrayCubit(
|
_reconciledMessagesCubit = DHTLogCubit(
|
||||||
open: () async => DHTShortArray.openOwned(_reconciledChatRecord,
|
open: () async => DHTLog.openOwned(_reconciledChatRecord,
|
||||||
debugName:
|
debugName:
|
||||||
'SingleContactMessagesCubit::_initReconciledMessagesCubit::'
|
'SingleContactMessagesCubit::_initReconciledMessagesCubit::'
|
||||||
'ReconciledMessages',
|
'ReconciledMessages',
|
||||||
@ -166,10 +165,24 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
|||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Set the tail position of the log for pagination.
|
||||||
|
// If tail is 0, the end of the log is used.
|
||||||
|
// If tail is negative, the position is subtracted from the current log
|
||||||
|
// length.
|
||||||
|
// If tail is positive, the position is absolute from the head of the log
|
||||||
|
// If follow is enabled, the tail offset will update when the log changes
|
||||||
|
Future<void> setWindow(
|
||||||
|
{int? tail, int? count, bool? follow, bool forceRefresh = false}) async {
|
||||||
|
await _initWait();
|
||||||
|
await _reconciledMessagesCubit!.setWindow(
|
||||||
|
tail: tail, count: count, follow: follow, forceRefresh: forceRefresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Called when the sent messages cubit gets a change
|
// Called when the sent messages cubit gets a change
|
||||||
// This will re-render when messages are sent from another machine
|
// This will re-render when messages are sent from another machine
|
||||||
void _updateSentMessagesState(
|
void _updateSentMessagesState(DHTLogBusyState<proto.Message> avmessages) {
|
||||||
DHTShortArrayBusyState<proto.Message> avmessages) {
|
|
||||||
final sentMessages = avmessages.state.asData?.value;
|
final sentMessages = avmessages.state.asData?.value;
|
||||||
if (sentMessages == null) {
|
if (sentMessages == null) {
|
||||||
return;
|
return;
|
||||||
@ -182,27 +195,52 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Called when the received messages cubit gets a change
|
// Called when the received messages cubit gets a change
|
||||||
void _updateRcvdMessagesState(
|
void _updateRcvdMessagesState(DHTLogBusyState<proto.Message> avmessages) {
|
||||||
DHTShortArrayBusyState<proto.Message> avmessages) {
|
|
||||||
final rcvdMessages = avmessages.state.asData?.value;
|
final rcvdMessages = avmessages.state.asData?.value;
|
||||||
if (rcvdMessages == null) {
|
if (rcvdMessages == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add remote messages updates to queue to process asynchronously
|
singleFuture(_rcvdMessagesCubit!, () async {
|
||||||
// Ignore offline state because remote messages are always fully delivered
|
// Get the timestamp of our most recent reconciled message
|
||||||
// This may happen once per client but should be idempotent
|
final lastReconciledMessageTs =
|
||||||
_unreconciledMessagesQueue.addAllSync(rcvdMessages.map((x) => x.value));
|
await _reconciledMessagesCubit!.operate((r) async {
|
||||||
|
final len = r.length;
|
||||||
|
if (len == 0) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
final lastMessage =
|
||||||
|
await r.getItemProtobuf(proto.Message.fromBuffer, len - 1);
|
||||||
|
if (lastMessage == null) {
|
||||||
|
throw StateError('should have gotten last message');
|
||||||
|
}
|
||||||
|
return lastMessage.timestamp;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Find oldest message we have not yet reconciled
|
||||||
|
|
||||||
|
// // Go through all the ones from the cubit state first since we've already
|
||||||
|
// // gotten them from the DHT
|
||||||
|
// for (var rn = rcvdMessages.elements.length; rn >= 0; rn--) {
|
||||||
|
// //
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Add remote messages updates to queue to process asynchronously
|
||||||
|
// // Ignore offline state because remote messages are always fully delivered
|
||||||
|
// // This may happen once per client but should be idempotent
|
||||||
|
// _unreconciledMessagesQueue.addAllSync(rcvdMessages.map((x) => x.value));
|
||||||
|
|
||||||
// Update the view
|
// Update the view
|
||||||
_renderState();
|
_renderState();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called when the reconciled messages list gets a change
|
// Called when the reconciled messages list gets a change
|
||||||
// This can happen when multiple clients for the same identity are
|
// This can happen when multiple clients for the same identity are
|
||||||
// reading and reconciling the same remote chat
|
// reading and reconciling the same remote chat
|
||||||
void _updateReconciledMessagesState(
|
void _updateReconciledMessagesState(
|
||||||
DHTShortArrayBusyState<proto.Message> avmessages) {
|
DHTLogBusyState<proto.Message> avmessages) {
|
||||||
// Update the view
|
// Update the view
|
||||||
_renderState();
|
_renderState();
|
||||||
}
|
}
|
||||||
@ -210,85 +248,85 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
|||||||
// Async process to reconcile messages sent or received in the background
|
// Async process to reconcile messages sent or received in the background
|
||||||
Future<void> _processUnreconciledMessages(
|
Future<void> _processUnreconciledMessages(
|
||||||
IList<proto.Message> messages) async {
|
IList<proto.Message> messages) async {
|
||||||
await _reconciledMessagesCubit!
|
// await _reconciledMessagesCubit!
|
||||||
.operateWrite((reconciledMessagesWriter) async {
|
// .operateAppendEventual((reconciledMessagesWriter) async {
|
||||||
await _reconcileMessagesInner(
|
// await _reconcileMessagesInner(
|
||||||
reconciledMessagesWriter: reconciledMessagesWriter,
|
// reconciledMessagesWriter: reconciledMessagesWriter,
|
||||||
messages: messages);
|
// messages: messages);
|
||||||
});
|
// });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Async process to send messages in the background
|
// Async process to send messages in the background
|
||||||
Future<void> _processSendingMessages(IList<proto.Message> messages) async {
|
Future<void> _processSendingMessages(IList<proto.Message> messages) async {
|
||||||
for (final message in messages) {
|
await _sentMessagesCubit!.operateAppendEventual((writer) =>
|
||||||
await _sentMessagesCubit!.operateWriteEventual(
|
writer.tryAddItems(messages.map((m) => m.writeToBuffer()).toList()));
|
||||||
(writer) => writer.tryAddItem(message.writeToBuffer()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _reconcileMessagesInner(
|
Future<void> _reconcileMessagesInner(
|
||||||
{required DHTRandomReadWrite reconciledMessagesWriter,
|
{required DHTLogWriteOperations reconciledMessagesWriter,
|
||||||
required IList<proto.Message> messages}) async {
|
required IList<proto.Message> messages}) async {
|
||||||
// Ensure remoteMessages is sorted by timestamp
|
// // Ensure remoteMessages is sorted by timestamp
|
||||||
final newMessages = messages
|
// final newMessages = messages
|
||||||
.sort((a, b) => a.timestamp.compareTo(b.timestamp))
|
// .sort((a, b) => a.timestamp.compareTo(b.timestamp))
|
||||||
.removeDuplicates();
|
// .removeDuplicates();
|
||||||
|
|
||||||
// Existing messages will always be sorted by timestamp so merging is easy
|
// // Existing messages will always be sorted by timestamp so merging is easy
|
||||||
final existingMessages = await reconciledMessagesWriter
|
// final existingMessages = await reconciledMessagesWriter
|
||||||
.getItemRangeProtobuf(proto.Message.fromBuffer, 0);
|
// .getItemRangeProtobuf(proto.Message.fromBuffer, 0);
|
||||||
if (existingMessages == null) {
|
// if (existingMessages == null) {
|
||||||
throw Exception(
|
// throw Exception(
|
||||||
'Could not load existing reconciled messages at this time');
|
// 'Could not load existing reconciled messages at this time');
|
||||||
}
|
// }
|
||||||
|
|
||||||
var ePos = 0;
|
// var ePos = 0;
|
||||||
var nPos = 0;
|
// var nPos = 0;
|
||||||
while (ePos < existingMessages.length && nPos < newMessages.length) {
|
// while (ePos < existingMessages.length && nPos < newMessages.length) {
|
||||||
final existingMessage = existingMessages[ePos];
|
// final existingMessage = existingMessages[ePos];
|
||||||
final newMessage = newMessages[nPos];
|
// final newMessage = newMessages[nPos];
|
||||||
|
|
||||||
// If timestamp to insert is less than
|
// // If timestamp to insert is less than
|
||||||
// the current position, insert it here
|
// // the current position, insert it here
|
||||||
final newTs = Timestamp.fromInt64(newMessage.timestamp);
|
// final newTs = Timestamp.fromInt64(newMessage.timestamp);
|
||||||
final existingTs = Timestamp.fromInt64(existingMessage.timestamp);
|
// final existingTs = Timestamp.fromInt64(existingMessage.timestamp);
|
||||||
final cmp = newTs.compareTo(existingTs);
|
// final cmp = newTs.compareTo(existingTs);
|
||||||
if (cmp < 0) {
|
// if (cmp < 0) {
|
||||||
// New message belongs here
|
// // New message belongs here
|
||||||
|
|
||||||
// Insert into dht backing array
|
// // Insert into dht backing array
|
||||||
await reconciledMessagesWriter.tryInsertItem(
|
// await reconciledMessagesWriter.tryInsertItem(
|
||||||
ePos, newMessage.writeToBuffer());
|
// ePos, newMessage.writeToBuffer());
|
||||||
// Insert into local copy as well for this operation
|
// // Insert into local copy as well for this operation
|
||||||
existingMessages.insert(ePos, newMessage);
|
// existingMessages.insert(ePos, newMessage);
|
||||||
|
|
||||||
// Next message
|
// // Next message
|
||||||
nPos++;
|
// nPos++;
|
||||||
ePos++;
|
// ePos++;
|
||||||
} else if (cmp == 0) {
|
// } else if (cmp == 0) {
|
||||||
// Duplicate, skip
|
// // Duplicate, skip
|
||||||
nPos++;
|
// nPos++;
|
||||||
ePos++;
|
// ePos++;
|
||||||
} else if (cmp > 0) {
|
// } else if (cmp > 0) {
|
||||||
// New message belongs later
|
// // New message belongs later
|
||||||
ePos++;
|
// ePos++;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
// If there are any new messages left, append them all
|
// // If there are any new messages left, append them all
|
||||||
while (nPos < newMessages.length) {
|
// while (nPos < newMessages.length) {
|
||||||
final newMessage = newMessages[nPos];
|
// final newMessage = newMessages[nPos];
|
||||||
|
|
||||||
// Append to dht backing array
|
// // Append to dht backing array
|
||||||
await reconciledMessagesWriter.tryAddItem(newMessage.writeToBuffer());
|
// await reconciledMessagesWriter.tryAddItem(newMessage.writeToBuffer());
|
||||||
// Insert into local copy as well for this operation
|
// // Insert into local copy as well for this operation
|
||||||
existingMessages.add(newMessage);
|
// existingMessages.add(newMessage);
|
||||||
|
|
||||||
nPos++;
|
// nPos++;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Produce a state for this cubit from the input cubits and queues
|
// Produce a state for this cubit from the input cubits and queues
|
||||||
void _renderState() {
|
void _renderState() {
|
||||||
|
// xxx move into a singlefuture
|
||||||
|
|
||||||
// Get all reconciled messages
|
// Get all reconciled messages
|
||||||
final reconciledMessages =
|
final reconciledMessages =
|
||||||
_reconciledMessagesCubit?.state.state.asData?.value;
|
_reconciledMessagesCubit?.state.state.asData?.value;
|
||||||
@ -307,14 +345,14 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
|||||||
|
|
||||||
// Generate state for each message
|
// Generate state for each message
|
||||||
final sentMessagesMap =
|
final sentMessagesMap =
|
||||||
IMap<Int64, DHTShortArrayElementState<proto.Message>>.fromValues(
|
IMap<Int64, DHTLogElementState<proto.Message>>.fromValues(
|
||||||
keyMapper: (x) => x.value.timestamp,
|
keyMapper: (x) => x.value.timestamp,
|
||||||
values: sentMessages,
|
values: sentMessages.elements,
|
||||||
);
|
);
|
||||||
final reconciledMessagesMap =
|
final reconciledMessagesMap =
|
||||||
IMap<Int64, DHTShortArrayElementState<proto.Message>>.fromValues(
|
IMap<Int64, DHTLogElementState<proto.Message>>.fromValues(
|
||||||
keyMapper: (x) => x.value.timestamp,
|
keyMapper: (x) => x.value.timestamp,
|
||||||
values: reconciledMessages,
|
values: reconciledMessages.elements,
|
||||||
);
|
);
|
||||||
final sendingMessagesMap = IMap<Int64, proto.Message>.fromValues(
|
final sendingMessagesMap = IMap<Int64, proto.Message>.fromValues(
|
||||||
keyMapper: (x) => x.timestamp,
|
keyMapper: (x) => x.timestamp,
|
||||||
@ -372,9 +410,8 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
|||||||
.sort((x, y) => x.key.compareTo(y.key));
|
.sort((x, y) => x.key.compareTo(y.key));
|
||||||
final renderedState = messageKeys
|
final renderedState = messageKeys
|
||||||
.map((x) => MessageState(
|
.map((x) => MessageState(
|
||||||
author: x.value.message.author.toVeilid(),
|
content: x.value.message,
|
||||||
timestamp: Timestamp.fromInt64(x.key),
|
timestamp: Timestamp.fromInt64(x.key),
|
||||||
text: x.value.message.text,
|
|
||||||
sendState: x.value.sendState))
|
sendState: x.value.sendState))
|
||||||
.toIList();
|
.toIList();
|
||||||
|
|
||||||
@ -400,17 +437,16 @@ class SingleContactMessagesCubit extends Cubit<SingleContactMessagesState> {
|
|||||||
final TypedKey _remoteMessagesRecordKey;
|
final TypedKey _remoteMessagesRecordKey;
|
||||||
final OwnedDHTRecordPointer _reconciledChatRecord;
|
final OwnedDHTRecordPointer _reconciledChatRecord;
|
||||||
|
|
||||||
late final DHTRecordCrypto _messagesCrypto;
|
late final VeilidCrypto _messagesCrypto;
|
||||||
|
|
||||||
DHTShortArrayCubit<proto.Message>? _sentMessagesCubit;
|
DHTLogCubit<proto.Message>? _sentMessagesCubit;
|
||||||
DHTShortArrayCubit<proto.Message>? _rcvdMessagesCubit;
|
DHTLogCubit<proto.Message>? _rcvdMessagesCubit;
|
||||||
DHTShortArrayCubit<proto.Message>? _reconciledMessagesCubit;
|
DHTLogCubit<proto.Message>? _reconciledMessagesCubit;
|
||||||
|
|
||||||
late final PersistentQueue<proto.Message> _unreconciledMessagesQueue;
|
late final PersistentQueue<proto.Message> _unreconciledMessagesQueue;
|
||||||
late final PersistentQueue<proto.Message> _sendingMessagesQueue;
|
late final PersistentQueue<proto.Message> _sendingMessagesQueue;
|
||||||
|
|
||||||
StreamSubscription<DHTShortArrayBusyState<proto.Message>>? _sentSubscription;
|
StreamSubscription<DHTLogBusyState<proto.Message>>? _sentSubscription;
|
||||||
StreamSubscription<DHTShortArrayBusyState<proto.Message>>? _rcvdSubscription;
|
StreamSubscription<DHTLogBusyState<proto.Message>>? _rcvdSubscription;
|
||||||
StreamSubscription<DHTShortArrayBusyState<proto.Message>>?
|
StreamSubscription<DHTLogBusyState<proto.Message>>? _reconciledSubscription;
|
||||||
_reconciledSubscription;
|
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:veilid_support/veilid_support.dart';
|
import 'package:veilid_support/veilid_support.dart';
|
||||||
|
|
||||||
|
import '../../proto/proto.dart' as proto;
|
||||||
|
|
||||||
part 'message_state.freezed.dart';
|
part 'message_state.freezed.dart';
|
||||||
part 'message_state.g.dart';
|
part 'message_state.g.dart';
|
||||||
|
|
||||||
@ -23,9 +25,12 @@ enum MessageSendState {
|
|||||||
@freezed
|
@freezed
|
||||||
class MessageState with _$MessageState {
|
class MessageState with _$MessageState {
|
||||||
const factory MessageState({
|
const factory MessageState({
|
||||||
required TypedKey author,
|
// Content of the message
|
||||||
|
@JsonKey(fromJson: proto.messageFromJson, toJson: proto.messageToJson)
|
||||||
|
required proto.Message content,
|
||||||
|
// Received or delivered timestamp
|
||||||
required Timestamp timestamp,
|
required Timestamp timestamp,
|
||||||
required String text,
|
// The state of the mssage
|
||||||
required MessageSendState? sendState,
|
required MessageSendState? sendState,
|
||||||
}) = _MessageState;
|
}) = _MessageState;
|
||||||
|
|
||||||
|
@ -20,9 +20,12 @@ MessageState _$MessageStateFromJson(Map<String, dynamic> json) {
|
|||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$MessageState {
|
mixin _$MessageState {
|
||||||
Typed<FixedEncodedString43> get author => throw _privateConstructorUsedError;
|
// Content of the message
|
||||||
Timestamp get timestamp => throw _privateConstructorUsedError;
|
@JsonKey(fromJson: proto.messageFromJson, toJson: proto.messageToJson)
|
||||||
String get text => throw _privateConstructorUsedError;
|
proto.Message get content =>
|
||||||
|
throw _privateConstructorUsedError; // Received or delivered timestamp
|
||||||
|
Timestamp get timestamp =>
|
||||||
|
throw _privateConstructorUsedError; // The state of the mssage
|
||||||
MessageSendState? get sendState => throw _privateConstructorUsedError;
|
MessageSendState? get sendState => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||||
@ -38,9 +41,9 @@ abstract class $MessageStateCopyWith<$Res> {
|
|||||||
_$MessageStateCopyWithImpl<$Res, MessageState>;
|
_$MessageStateCopyWithImpl<$Res, MessageState>;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call(
|
$Res call(
|
||||||
{Typed<FixedEncodedString43> author,
|
{@JsonKey(fromJson: proto.messageFromJson, toJson: proto.messageToJson)
|
||||||
|
proto.Message content,
|
||||||
Timestamp timestamp,
|
Timestamp timestamp,
|
||||||
String text,
|
|
||||||
MessageSendState? sendState});
|
MessageSendState? sendState});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,24 +60,19 @@ class _$MessageStateCopyWithImpl<$Res, $Val extends MessageState>
|
|||||||
@pragma('vm:prefer-inline')
|
@pragma('vm:prefer-inline')
|
||||||
@override
|
@override
|
||||||
$Res call({
|
$Res call({
|
||||||
Object? author = null,
|
Object? content = null,
|
||||||
Object? timestamp = null,
|
Object? timestamp = null,
|
||||||
Object? text = null,
|
|
||||||
Object? sendState = freezed,
|
Object? sendState = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(_value.copyWith(
|
return _then(_value.copyWith(
|
||||||
author: null == author
|
content: null == content
|
||||||
? _value.author
|
? _value.content
|
||||||
: author // ignore: cast_nullable_to_non_nullable
|
: content // ignore: cast_nullable_to_non_nullable
|
||||||
as Typed<FixedEncodedString43>,
|
as proto.Message,
|
||||||
timestamp: null == timestamp
|
timestamp: null == timestamp
|
||||||
? _value.timestamp
|
? _value.timestamp
|
||||||
: timestamp // ignore: cast_nullable_to_non_nullable
|
: timestamp // ignore: cast_nullable_to_non_nullable
|
||||||
as Timestamp,
|
as Timestamp,
|
||||||
text: null == text
|
|
||||||
? _value.text
|
|
||||||
: text // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,
|
|
||||||
sendState: freezed == sendState
|
sendState: freezed == sendState
|
||||||
? _value.sendState
|
? _value.sendState
|
||||||
: sendState // ignore: cast_nullable_to_non_nullable
|
: sendState // ignore: cast_nullable_to_non_nullable
|
||||||
@ -92,9 +90,9 @@ abstract class _$$MessageStateImplCopyWith<$Res>
|
|||||||
@override
|
@override
|
||||||
@useResult
|
@useResult
|
||||||
$Res call(
|
$Res call(
|
||||||
{Typed<FixedEncodedString43> author,
|
{@JsonKey(fromJson: proto.messageFromJson, toJson: proto.messageToJson)
|
||||||
|
proto.Message content,
|
||||||
Timestamp timestamp,
|
Timestamp timestamp,
|
||||||
String text,
|
|
||||||
MessageSendState? sendState});
|
MessageSendState? sendState});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,24 +107,19 @@ class __$$MessageStateImplCopyWithImpl<$Res>
|
|||||||
@pragma('vm:prefer-inline')
|
@pragma('vm:prefer-inline')
|
||||||
@override
|
@override
|
||||||
$Res call({
|
$Res call({
|
||||||
Object? author = null,
|
Object? content = null,
|
||||||
Object? timestamp = null,
|
Object? timestamp = null,
|
||||||
Object? text = null,
|
|
||||||
Object? sendState = freezed,
|
Object? sendState = freezed,
|
||||||
}) {
|
}) {
|
||||||
return _then(_$MessageStateImpl(
|
return _then(_$MessageStateImpl(
|
||||||
author: null == author
|
content: null == content
|
||||||
? _value.author
|
? _value.content
|
||||||
: author // ignore: cast_nullable_to_non_nullable
|
: content // ignore: cast_nullable_to_non_nullable
|
||||||
as Typed<FixedEncodedString43>,
|
as proto.Message,
|
||||||
timestamp: null == timestamp
|
timestamp: null == timestamp
|
||||||
? _value.timestamp
|
? _value.timestamp
|
||||||
: timestamp // ignore: cast_nullable_to_non_nullable
|
: timestamp // ignore: cast_nullable_to_non_nullable
|
||||||
as Timestamp,
|
as Timestamp,
|
||||||
text: null == text
|
|
||||||
? _value.text
|
|
||||||
: text // ignore: cast_nullable_to_non_nullable
|
|
||||||
as String,
|
|
||||||
sendState: freezed == sendState
|
sendState: freezed == sendState
|
||||||
? _value.sendState
|
? _value.sendState
|
||||||
: sendState // ignore: cast_nullable_to_non_nullable
|
: sendState // ignore: cast_nullable_to_non_nullable
|
||||||
@ -139,26 +132,28 @@ class __$$MessageStateImplCopyWithImpl<$Res>
|
|||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class _$MessageStateImpl with DiagnosticableTreeMixin implements _MessageState {
|
class _$MessageStateImpl with DiagnosticableTreeMixin implements _MessageState {
|
||||||
const _$MessageStateImpl(
|
const _$MessageStateImpl(
|
||||||
{required this.author,
|
{@JsonKey(fromJson: proto.messageFromJson, toJson: proto.messageToJson)
|
||||||
|
required this.content,
|
||||||
required this.timestamp,
|
required this.timestamp,
|
||||||
required this.text,
|
|
||||||
required this.sendState});
|
required this.sendState});
|
||||||
|
|
||||||
factory _$MessageStateImpl.fromJson(Map<String, dynamic> json) =>
|
factory _$MessageStateImpl.fromJson(Map<String, dynamic> json) =>
|
||||||
_$$MessageStateImplFromJson(json);
|
_$$MessageStateImplFromJson(json);
|
||||||
|
|
||||||
|
// Content of the message
|
||||||
@override
|
@override
|
||||||
final Typed<FixedEncodedString43> author;
|
@JsonKey(fromJson: proto.messageFromJson, toJson: proto.messageToJson)
|
||||||
|
final proto.Message content;
|
||||||
|
// Received or delivered timestamp
|
||||||
@override
|
@override
|
||||||
final Timestamp timestamp;
|
final Timestamp timestamp;
|
||||||
@override
|
// The state of the mssage
|
||||||
final String text;
|
|
||||||
@override
|
@override
|
||||||
final MessageSendState? sendState;
|
final MessageSendState? sendState;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
|
String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) {
|
||||||
return 'MessageState(author: $author, timestamp: $timestamp, text: $text, sendState: $sendState)';
|
return 'MessageState(content: $content, timestamp: $timestamp, sendState: $sendState)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -166,9 +161,8 @@ class _$MessageStateImpl with DiagnosticableTreeMixin implements _MessageState {
|
|||||||
super.debugFillProperties(properties);
|
super.debugFillProperties(properties);
|
||||||
properties
|
properties
|
||||||
..add(DiagnosticsProperty('type', 'MessageState'))
|
..add(DiagnosticsProperty('type', 'MessageState'))
|
||||||
..add(DiagnosticsProperty('author', author))
|
..add(DiagnosticsProperty('content', content))
|
||||||
..add(DiagnosticsProperty('timestamp', timestamp))
|
..add(DiagnosticsProperty('timestamp', timestamp))
|
||||||
..add(DiagnosticsProperty('text', text))
|
|
||||||
..add(DiagnosticsProperty('sendState', sendState));
|
..add(DiagnosticsProperty('sendState', sendState));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,18 +171,16 @@ class _$MessageStateImpl with DiagnosticableTreeMixin implements _MessageState {
|
|||||||
return identical(this, other) ||
|
return identical(this, other) ||
|
||||||
(other.runtimeType == runtimeType &&
|
(other.runtimeType == runtimeType &&
|
||||||
other is _$MessageStateImpl &&
|
other is _$MessageStateImpl &&
|
||||||
(identical(other.author, author) || other.author == author) &&
|
(identical(other.content, content) || other.content == content) &&
|
||||||
(identical(other.timestamp, timestamp) ||
|
(identical(other.timestamp, timestamp) ||
|
||||||
other.timestamp == timestamp) &&
|
other.timestamp == timestamp) &&
|
||||||
(identical(other.text, text) || other.text == text) &&
|
|
||||||
(identical(other.sendState, sendState) ||
|
(identical(other.sendState, sendState) ||
|
||||||
other.sendState == sendState));
|
other.sendState == sendState));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode => Object.hash(runtimeType, content, timestamp, sendState);
|
||||||
Object.hash(runtimeType, author, timestamp, text, sendState);
|
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
@override
|
@override
|
||||||
@ -206,21 +198,20 @@ class _$MessageStateImpl with DiagnosticableTreeMixin implements _MessageState {
|
|||||||
|
|
||||||
abstract class _MessageState implements MessageState {
|
abstract class _MessageState implements MessageState {
|
||||||
const factory _MessageState(
|
const factory _MessageState(
|
||||||
{required final Typed<FixedEncodedString43> author,
|
{@JsonKey(fromJson: proto.messageFromJson, toJson: proto.messageToJson)
|
||||||
|
required final proto.Message content,
|
||||||
required final Timestamp timestamp,
|
required final Timestamp timestamp,
|
||||||
required final String text,
|
|
||||||
required final MessageSendState? sendState}) = _$MessageStateImpl;
|
required final MessageSendState? sendState}) = _$MessageStateImpl;
|
||||||
|
|
||||||
factory _MessageState.fromJson(Map<String, dynamic> json) =
|
factory _MessageState.fromJson(Map<String, dynamic> json) =
|
||||||
_$MessageStateImpl.fromJson;
|
_$MessageStateImpl.fromJson;
|
||||||
|
|
||||||
@override
|
@override // Content of the message
|
||||||
Typed<FixedEncodedString43> get author;
|
@JsonKey(fromJson: proto.messageFromJson, toJson: proto.messageToJson)
|
||||||
@override
|
proto.Message get content;
|
||||||
|
@override // Received or delivered timestamp
|
||||||
Timestamp get timestamp;
|
Timestamp get timestamp;
|
||||||
@override
|
@override // The state of the mssage
|
||||||
String get text;
|
|
||||||
@override
|
|
||||||
MessageSendState? get sendState;
|
MessageSendState? get sendState;
|
||||||
@override
|
@override
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
|
@ -8,9 +8,8 @@ part of 'message_state.dart';
|
|||||||
|
|
||||||
_$MessageStateImpl _$$MessageStateImplFromJson(Map<String, dynamic> json) =>
|
_$MessageStateImpl _$$MessageStateImplFromJson(Map<String, dynamic> json) =>
|
||||||
_$MessageStateImpl(
|
_$MessageStateImpl(
|
||||||
author: Typed<FixedEncodedString43>.fromJson(json['author']),
|
content: messageFromJson(json['content'] as Map<String, dynamic>),
|
||||||
timestamp: Timestamp.fromJson(json['timestamp']),
|
timestamp: Timestamp.fromJson(json['timestamp']),
|
||||||
text: json['text'] as String,
|
|
||||||
sendState: json['send_state'] == null
|
sendState: json['send_state'] == null
|
||||||
? null
|
? null
|
||||||
: MessageSendState.fromJson(json['send_state']),
|
: MessageSendState.fromJson(json['send_state']),
|
||||||
@ -18,8 +17,7 @@ _$MessageStateImpl _$$MessageStateImplFromJson(Map<String, dynamic> json) =>
|
|||||||
|
|
||||||
Map<String, dynamic> _$$MessageStateImplToJson(_$MessageStateImpl instance) =>
|
Map<String, dynamic> _$$MessageStateImplToJson(_$MessageStateImpl instance) =>
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
'author': instance.author.toJson(),
|
'content': messageToJson(instance.content),
|
||||||
'timestamp': instance.timestamp.toJson(),
|
'timestamp': instance.timestamp.toJson(),
|
||||||
'text': instance.text,
|
|
||||||
'send_state': instance.sendState?.toJson(),
|
'send_state': instance.sendState?.toJson(),
|
||||||
};
|
};
|
||||||
|
@ -65,7 +65,7 @@ class ChatListCubit extends DHTShortArrayCubit<proto.Chat>
|
|||||||
.userLogin.accountRecordInfo.accountRecord.recordKey;
|
.userLogin.accountRecordInfo.accountRecord.recordKey;
|
||||||
|
|
||||||
// Make a record that can store the reconciled version of the chat
|
// Make a record that can store the reconciled version of the chat
|
||||||
final reconciledChatRecord = await (await DHTShortArray.create(
|
final reconciledChatRecord = await (await DHTLog.create(
|
||||||
debugName:
|
debugName:
|
||||||
'ChatListCubit::getOrCreateChatSingleContact::ReconciledChat',
|
'ChatListCubit::getOrCreateChatSingleContact::ReconciledChat',
|
||||||
parent: accountRecordKey))
|
parent: accountRecordKey))
|
||||||
|
@ -121,7 +121,7 @@ class ContactInvitationListCubit
|
|||||||
schema: DHTSchema.smpl(oCnt: 1, members: [
|
schema: DHTSchema.smpl(oCnt: 1, members: [
|
||||||
DHTSchemaMember(mCnt: 1, mKey: contactRequestWriter.key)
|
DHTSchemaMember(mCnt: 1, mKey: contactRequestWriter.key)
|
||||||
]),
|
]),
|
||||||
crypto: const DHTRecordCryptoPublic()))
|
crypto: const VeilidCryptoPublic()))
|
||||||
.deleteScope((contactRequestInbox) async {
|
.deleteScope((contactRequestInbox) async {
|
||||||
// Store ContactRequest in owner subkey
|
// Store ContactRequest in owner subkey
|
||||||
await contactRequestInbox.eventualWriteProtobuf(creq);
|
await contactRequestInbox.eventualWriteProtobuf(creq);
|
||||||
@ -129,7 +129,7 @@ class ContactInvitationListCubit
|
|||||||
await contactRequestInbox.eventualWriteBytes(Uint8List(0),
|
await contactRequestInbox.eventualWriteBytes(Uint8List(0),
|
||||||
subkey: 1,
|
subkey: 1,
|
||||||
writer: contactRequestWriter,
|
writer: contactRequestWriter,
|
||||||
crypto: await DHTRecordCryptoPrivate.fromTypedKeyPair(
|
crypto: await VeilidCryptoPrivate.fromTypedKeyPair(
|
||||||
TypedKeyPair.fromKeyPair(
|
TypedKeyPair.fromKeyPair(
|
||||||
contactRequestInbox.key.kind, contactRequestWriter)));
|
contactRequestInbox.key.kind, contactRequestWriter)));
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ class ContactRequestInboxCubit
|
|||||||
return pool.openRecordRead(recordKey,
|
return pool.openRecordRead(recordKey,
|
||||||
debugName: 'ContactRequestInboxCubit::_open::'
|
debugName: 'ContactRequestInboxCubit::_open::'
|
||||||
'ContactRequestInbox',
|
'ContactRequestInbox',
|
||||||
crypto: await DHTRecordCryptoPrivate.fromTypedKeyPair(writer),
|
crypto: await VeilidCryptoPrivate.fromTypedKeyPair(writer),
|
||||||
parent: accountRecordKey,
|
parent: accountRecordKey,
|
||||||
defaultSubkey: 1);
|
defaultSubkey: 1);
|
||||||
}
|
}
|
||||||
|
@ -285,13 +285,13 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
|||||||
required ActiveAccountInfo activeAccountInfo,
|
required ActiveAccountInfo activeAccountInfo,
|
||||||
required TypedKey remoteIdentityPublicKey,
|
required TypedKey remoteIdentityPublicKey,
|
||||||
required TypedKey localConversationKey,
|
required TypedKey localConversationKey,
|
||||||
required FutureOr<T> Function(DHTShortArray) callback,
|
required FutureOr<T> Function(DHTLog) callback,
|
||||||
}) async {
|
}) async {
|
||||||
final crypto =
|
final crypto =
|
||||||
await activeAccountInfo.makeConversationCrypto(remoteIdentityPublicKey);
|
await activeAccountInfo.makeConversationCrypto(remoteIdentityPublicKey);
|
||||||
final writer = activeAccountInfo.conversationWriter;
|
final writer = activeAccountInfo.conversationWriter;
|
||||||
|
|
||||||
return (await DHTShortArray.create(
|
return (await DHTLog.create(
|
||||||
debugName: 'ConversationCubit::initLocalMessages::LocalMessages',
|
debugName: 'ConversationCubit::initLocalMessages::LocalMessages',
|
||||||
parent: localConversationKey,
|
parent: localConversationKey,
|
||||||
crypto: crypto,
|
crypto: crypto,
|
||||||
@ -327,7 +327,7 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
|||||||
return update;
|
return update;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<DHTRecordCrypto> _cachedConversationCrypto() async {
|
Future<VeilidCrypto> _cachedConversationCrypto() async {
|
||||||
var conversationCrypto = _conversationCrypto;
|
var conversationCrypto = _conversationCrypto;
|
||||||
if (conversationCrypto != null) {
|
if (conversationCrypto != null) {
|
||||||
return conversationCrypto;
|
return conversationCrypto;
|
||||||
@ -350,6 +350,6 @@ class ConversationCubit extends Cubit<AsyncValue<ConversationState>> {
|
|||||||
ConversationState _incrementalState = const ConversationState(
|
ConversationState _incrementalState = const ConversationState(
|
||||||
localConversation: null, remoteConversation: null);
|
localConversation: null, remoteConversation: null);
|
||||||
//
|
//
|
||||||
DHTRecordCrypto? _conversationCrypto;
|
VeilidCrypto? _conversationCrypto;
|
||||||
final WaitSet<void> _initWait = WaitSet();
|
final WaitSet<void> _initWait = WaitSet();
|
||||||
}
|
}
|
||||||
|
12
lib/proto/extensions.dart
Normal file
12
lib/proto/extensions.dart
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import 'proto.dart' as proto;
|
||||||
|
|
||||||
|
proto.Message messageFromJson(Map<String, dynamic> j) =>
|
||||||
|
proto.Message.create()..mergeFromJsonMap(j);
|
||||||
|
|
||||||
|
Map<String, dynamic> messageToJson(proto.Message m) => m.writeToJsonMap();
|
||||||
|
|
||||||
|
proto.ReconciledMessage reconciledMessageFromJson(Map<String, dynamic> j) =>
|
||||||
|
proto.ReconciledMessage.create()..mergeFromJsonMap(j);
|
||||||
|
|
||||||
|
Map<String, dynamic> reconciledMessageToJson(proto.ReconciledMessage m) =>
|
||||||
|
m.writeToJsonMap();
|
@ -1,5 +1,7 @@
|
|||||||
export 'package:veilid_support/dht_support/proto/proto.dart';
|
export 'package:veilid_support/dht_support/proto/proto.dart';
|
||||||
export 'package:veilid_support/proto/proto.dart';
|
export 'package:veilid_support/proto/proto.dart';
|
||||||
|
|
||||||
|
export 'extensions.dart';
|
||||||
export 'veilidchat.pb.dart';
|
export 'veilidchat.pb.dart';
|
||||||
export 'veilidchat.pbenum.dart';
|
export 'veilidchat.pbenum.dart';
|
||||||
export 'veilidchat.pbjson.dart';
|
export 'veilidchat.pbjson.dart';
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -13,23 +13,6 @@ import 'dart:core' as $core;
|
|||||||
|
|
||||||
import 'package:protobuf/protobuf.dart' as $pb;
|
import 'package:protobuf/protobuf.dart' as $pb;
|
||||||
|
|
||||||
class AttachmentKind extends $pb.ProtobufEnum {
|
|
||||||
static const AttachmentKind ATTACHMENT_KIND_UNSPECIFIED = AttachmentKind._(0, _omitEnumNames ? '' : 'ATTACHMENT_KIND_UNSPECIFIED');
|
|
||||||
static const AttachmentKind ATTACHMENT_KIND_FILE = AttachmentKind._(1, _omitEnumNames ? '' : 'ATTACHMENT_KIND_FILE');
|
|
||||||
static const AttachmentKind ATTACHMENT_KIND_IMAGE = AttachmentKind._(2, _omitEnumNames ? '' : 'ATTACHMENT_KIND_IMAGE');
|
|
||||||
|
|
||||||
static const $core.List<AttachmentKind> values = <AttachmentKind> [
|
|
||||||
ATTACHMENT_KIND_UNSPECIFIED,
|
|
||||||
ATTACHMENT_KIND_FILE,
|
|
||||||
ATTACHMENT_KIND_IMAGE,
|
|
||||||
];
|
|
||||||
|
|
||||||
static final $core.Map<$core.int, AttachmentKind> _byValue = $pb.ProtobufEnum.initByValue(values);
|
|
||||||
static AttachmentKind? valueOf($core.int value) => _byValue[value];
|
|
||||||
|
|
||||||
const AttachmentKind._($core.int v, $core.String n) : super(v, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
class Availability extends $pb.ProtobufEnum {
|
class Availability extends $pb.ProtobufEnum {
|
||||||
static const Availability AVAILABILITY_UNSPECIFIED = Availability._(0, _omitEnumNames ? '' : 'AVAILABILITY_UNSPECIFIED');
|
static const Availability AVAILABILITY_UNSPECIFIED = Availability._(0, _omitEnumNames ? '' : 'AVAILABILITY_UNSPECIFIED');
|
||||||
static const Availability AVAILABILITY_OFFLINE = Availability._(1, _omitEnumNames ? '' : 'AVAILABILITY_OFFLINE');
|
static const Availability AVAILABILITY_OFFLINE = Availability._(1, _omitEnumNames ? '' : 'AVAILABILITY_OFFLINE');
|
||||||
@ -51,23 +34,6 @@ class Availability extends $pb.ProtobufEnum {
|
|||||||
const Availability._($core.int v, $core.String n) : super(v, n);
|
const Availability._($core.int v, $core.String n) : super(v, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChatType extends $pb.ProtobufEnum {
|
|
||||||
static const ChatType CHAT_TYPE_UNSPECIFIED = ChatType._(0, _omitEnumNames ? '' : 'CHAT_TYPE_UNSPECIFIED');
|
|
||||||
static const ChatType SINGLE_CONTACT = ChatType._(1, _omitEnumNames ? '' : 'SINGLE_CONTACT');
|
|
||||||
static const ChatType GROUP = ChatType._(2, _omitEnumNames ? '' : 'GROUP');
|
|
||||||
|
|
||||||
static const $core.List<ChatType> values = <ChatType> [
|
|
||||||
CHAT_TYPE_UNSPECIFIED,
|
|
||||||
SINGLE_CONTACT,
|
|
||||||
GROUP,
|
|
||||||
];
|
|
||||||
|
|
||||||
static final $core.Map<$core.int, ChatType> _byValue = $pb.ProtobufEnum.initByValue(values);
|
|
||||||
static ChatType? valueOf($core.int value) => _byValue[value];
|
|
||||||
|
|
||||||
const ChatType._($core.int v, $core.String n) : super(v, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
class EncryptionKeyType extends $pb.ProtobufEnum {
|
class EncryptionKeyType extends $pb.ProtobufEnum {
|
||||||
static const EncryptionKeyType ENCRYPTION_KEY_TYPE_UNSPECIFIED = EncryptionKeyType._(0, _omitEnumNames ? '' : 'ENCRYPTION_KEY_TYPE_UNSPECIFIED');
|
static const EncryptionKeyType ENCRYPTION_KEY_TYPE_UNSPECIFIED = EncryptionKeyType._(0, _omitEnumNames ? '' : 'ENCRYPTION_KEY_TYPE_UNSPECIFIED');
|
||||||
static const EncryptionKeyType ENCRYPTION_KEY_TYPE_NONE = EncryptionKeyType._(1, _omitEnumNames ? '' : 'ENCRYPTION_KEY_TYPE_NONE');
|
static const EncryptionKeyType ENCRYPTION_KEY_TYPE_NONE = EncryptionKeyType._(1, _omitEnumNames ? '' : 'ENCRYPTION_KEY_TYPE_NONE');
|
||||||
@ -87,5 +53,26 @@ class EncryptionKeyType extends $pb.ProtobufEnum {
|
|||||||
const EncryptionKeyType._($core.int v, $core.String n) : super(v, n);
|
const EncryptionKeyType._($core.int v, $core.String n) : super(v, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Scope extends $pb.ProtobufEnum {
|
||||||
|
static const Scope WATCHERS = Scope._(0, _omitEnumNames ? '' : 'WATCHERS');
|
||||||
|
static const Scope MODERATED = Scope._(1, _omitEnumNames ? '' : 'MODERATED');
|
||||||
|
static const Scope TALKERS = Scope._(2, _omitEnumNames ? '' : 'TALKERS');
|
||||||
|
static const Scope MODERATORS = Scope._(3, _omitEnumNames ? '' : 'MODERATORS');
|
||||||
|
static const Scope ADMINS = Scope._(4, _omitEnumNames ? '' : 'ADMINS');
|
||||||
|
|
||||||
|
static const $core.List<Scope> values = <Scope> [
|
||||||
|
WATCHERS,
|
||||||
|
MODERATED,
|
||||||
|
TALKERS,
|
||||||
|
MODERATORS,
|
||||||
|
ADMINS,
|
||||||
|
];
|
||||||
|
|
||||||
|
static final $core.Map<$core.int, Scope> _byValue = $pb.ProtobufEnum.initByValue(values);
|
||||||
|
static Scope? valueOf($core.int value) => _byValue[value];
|
||||||
|
|
||||||
|
const Scope._($core.int v, $core.String n) : super(v, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names');
|
const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names');
|
||||||
|
@ -13,21 +13,6 @@ import 'dart:convert' as $convert;
|
|||||||
import 'dart:core' as $core;
|
import 'dart:core' as $core;
|
||||||
import 'dart:typed_data' as $typed_data;
|
import 'dart:typed_data' as $typed_data;
|
||||||
|
|
||||||
@$core.Deprecated('Use attachmentKindDescriptor instead')
|
|
||||||
const AttachmentKind$json = {
|
|
||||||
'1': 'AttachmentKind',
|
|
||||||
'2': [
|
|
||||||
{'1': 'ATTACHMENT_KIND_UNSPECIFIED', '2': 0},
|
|
||||||
{'1': 'ATTACHMENT_KIND_FILE', '2': 1},
|
|
||||||
{'1': 'ATTACHMENT_KIND_IMAGE', '2': 2},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Descriptor for `AttachmentKind`. Decode as a `google.protobuf.EnumDescriptorProto`.
|
|
||||||
final $typed_data.Uint8List attachmentKindDescriptor = $convert.base64Decode(
|
|
||||||
'Cg5BdHRhY2htZW50S2luZBIfChtBVFRBQ0hNRU5UX0tJTkRfVU5TUEVDSUZJRUQQABIYChRBVF'
|
|
||||||
'RBQ0hNRU5UX0tJTkRfRklMRRABEhkKFUFUVEFDSE1FTlRfS0lORF9JTUFHRRAC');
|
|
||||||
|
|
||||||
@$core.Deprecated('Use availabilityDescriptor instead')
|
@$core.Deprecated('Use availabilityDescriptor instead')
|
||||||
const Availability$json = {
|
const Availability$json = {
|
||||||
'1': 'Availability',
|
'1': 'Availability',
|
||||||
@ -46,21 +31,6 @@ final $typed_data.Uint8List availabilityDescriptor = $convert.base64Decode(
|
|||||||
'lMSVRZX09GRkxJTkUQARIVChFBVkFJTEFCSUxJVFlfRlJFRRACEhUKEUFWQUlMQUJJTElUWV9C'
|
'lMSVRZX09GRkxJTkUQARIVChFBVkFJTEFCSUxJVFlfRlJFRRACEhUKEUFWQUlMQUJJTElUWV9C'
|
||||||
'VVNZEAMSFQoRQVZBSUxBQklMSVRZX0FXQVkQBA==');
|
'VVNZEAMSFQoRQVZBSUxBQklMSVRZX0FXQVkQBA==');
|
||||||
|
|
||||||
@$core.Deprecated('Use chatTypeDescriptor instead')
|
|
||||||
const ChatType$json = {
|
|
||||||
'1': 'ChatType',
|
|
||||||
'2': [
|
|
||||||
{'1': 'CHAT_TYPE_UNSPECIFIED', '2': 0},
|
|
||||||
{'1': 'SINGLE_CONTACT', '2': 1},
|
|
||||||
{'1': 'GROUP', '2': 2},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Descriptor for `ChatType`. Decode as a `google.protobuf.EnumDescriptorProto`.
|
|
||||||
final $typed_data.Uint8List chatTypeDescriptor = $convert.base64Decode(
|
|
||||||
'CghDaGF0VHlwZRIZChVDSEFUX1RZUEVfVU5TUEVDSUZJRUQQABISCg5TSU5HTEVfQ09OVEFDVB'
|
|
||||||
'ABEgkKBUdST1VQEAI=');
|
|
||||||
|
|
||||||
@$core.Deprecated('Use encryptionKeyTypeDescriptor instead')
|
@$core.Deprecated('Use encryptionKeyTypeDescriptor instead')
|
||||||
const EncryptionKeyType$json = {
|
const EncryptionKeyType$json = {
|
||||||
'1': 'EncryptionKeyType',
|
'1': 'EncryptionKeyType',
|
||||||
@ -78,43 +48,249 @@ final $typed_data.Uint8List encryptionKeyTypeDescriptor = $convert.base64Decode(
|
|||||||
'ASHAoYRU5DUllQVElPTl9LRVlfVFlQRV9OT05FEAESGwoXRU5DUllQVElPTl9LRVlfVFlQRV9Q'
|
'ASHAoYRU5DUllQVElPTl9LRVlfVFlQRV9OT05FEAESGwoXRU5DUllQVElPTl9LRVlfVFlQRV9Q'
|
||||||
'SU4QAhIgChxFTkNSWVBUSU9OX0tFWV9UWVBFX1BBU1NXT1JEEAM=');
|
'SU4QAhIgChxFTkNSWVBUSU9OX0tFWV9UWVBFX1BBU1NXT1JEEAM=');
|
||||||
|
|
||||||
|
@$core.Deprecated('Use scopeDescriptor instead')
|
||||||
|
const Scope$json = {
|
||||||
|
'1': 'Scope',
|
||||||
|
'2': [
|
||||||
|
{'1': 'WATCHERS', '2': 0},
|
||||||
|
{'1': 'MODERATED', '2': 1},
|
||||||
|
{'1': 'TALKERS', '2': 2},
|
||||||
|
{'1': 'MODERATORS', '2': 3},
|
||||||
|
{'1': 'ADMINS', '2': 4},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Descriptor for `Scope`. Decode as a `google.protobuf.EnumDescriptorProto`.
|
||||||
|
final $typed_data.Uint8List scopeDescriptor = $convert.base64Decode(
|
||||||
|
'CgVTY29wZRIMCghXQVRDSEVSUxAAEg0KCU1PREVSQVRFRBABEgsKB1RBTEtFUlMQAhIOCgpNT0'
|
||||||
|
'RFUkFUT1JTEAMSCgoGQURNSU5TEAQ=');
|
||||||
|
|
||||||
@$core.Deprecated('Use attachmentDescriptor instead')
|
@$core.Deprecated('Use attachmentDescriptor instead')
|
||||||
const Attachment$json = {
|
const Attachment$json = {
|
||||||
'1': 'Attachment',
|
'1': 'Attachment',
|
||||||
'2': [
|
'2': [
|
||||||
{'1': 'kind', '3': 1, '4': 1, '5': 14, '6': '.veilidchat.AttachmentKind', '10': 'kind'},
|
{'1': 'media', '3': 1, '4': 1, '5': 11, '6': '.veilidchat.AttachmentMedia', '9': 0, '10': 'media'},
|
||||||
{'1': 'mime', '3': 2, '4': 1, '5': 9, '10': 'mime'},
|
{'1': 'signature', '3': 2, '4': 1, '5': 11, '6': '.veilid.Signature', '10': 'signature'},
|
||||||
{'1': 'name', '3': 3, '4': 1, '5': 9, '10': 'name'},
|
],
|
||||||
{'1': 'content', '3': 4, '4': 1, '5': 11, '6': '.dht.DataReference', '10': 'content'},
|
'8': [
|
||||||
{'1': 'signature', '3': 5, '4': 1, '5': 11, '6': '.veilid.Signature', '10': 'signature'},
|
{'1': 'kind'},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Descriptor for `Attachment`. Decode as a `google.protobuf.DescriptorProto`.
|
/// Descriptor for `Attachment`. Decode as a `google.protobuf.DescriptorProto`.
|
||||||
final $typed_data.Uint8List attachmentDescriptor = $convert.base64Decode(
|
final $typed_data.Uint8List attachmentDescriptor = $convert.base64Decode(
|
||||||
'CgpBdHRhY2htZW50Ei4KBGtpbmQYASABKA4yGi52ZWlsaWRjaGF0LkF0dGFjaG1lbnRLaW5kUg'
|
'CgpBdHRhY2htZW50EjMKBW1lZGlhGAEgASgLMhsudmVpbGlkY2hhdC5BdHRhY2htZW50TWVkaW'
|
||||||
'RraW5kEhIKBG1pbWUYAiABKAlSBG1pbWUSEgoEbmFtZRgDIAEoCVIEbmFtZRIsCgdjb250ZW50'
|
'FIAFIFbWVkaWESLwoJc2lnbmF0dXJlGAIgASgLMhEudmVpbGlkLlNpZ25hdHVyZVIJc2lnbmF0'
|
||||||
'GAQgASgLMhIuZGh0LkRhdGFSZWZlcmVuY2VSB2NvbnRlbnQSLwoJc2lnbmF0dXJlGAUgASgLMh'
|
'dXJlQgYKBGtpbmQ=');
|
||||||
'EudmVpbGlkLlNpZ25hdHVyZVIJc2lnbmF0dXJl');
|
|
||||||
|
@$core.Deprecated('Use attachmentMediaDescriptor instead')
|
||||||
|
const AttachmentMedia$json = {
|
||||||
|
'1': 'AttachmentMedia',
|
||||||
|
'2': [
|
||||||
|
{'1': 'mime', '3': 1, '4': 1, '5': 9, '10': 'mime'},
|
||||||
|
{'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'},
|
||||||
|
{'1': 'content', '3': 3, '4': 1, '5': 11, '6': '.dht.DataReference', '10': 'content'},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Descriptor for `AttachmentMedia`. Decode as a `google.protobuf.DescriptorProto`.
|
||||||
|
final $typed_data.Uint8List attachmentMediaDescriptor = $convert.base64Decode(
|
||||||
|
'Cg9BdHRhY2htZW50TWVkaWESEgoEbWltZRgBIAEoCVIEbWltZRISCgRuYW1lGAIgASgJUgRuYW'
|
||||||
|
'1lEiwKB2NvbnRlbnQYAyABKAsyEi5kaHQuRGF0YVJlZmVyZW5jZVIHY29udGVudA==');
|
||||||
|
|
||||||
|
@$core.Deprecated('Use permissionsDescriptor instead')
|
||||||
|
const Permissions$json = {
|
||||||
|
'1': 'Permissions',
|
||||||
|
'2': [
|
||||||
|
{'1': 'can_add_members', '3': 1, '4': 1, '5': 14, '6': '.veilidchat.Scope', '10': 'canAddMembers'},
|
||||||
|
{'1': 'can_edit_info', '3': 2, '4': 1, '5': 14, '6': '.veilidchat.Scope', '10': 'canEditInfo'},
|
||||||
|
{'1': 'moderated', '3': 3, '4': 1, '5': 8, '10': 'moderated'},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Descriptor for `Permissions`. Decode as a `google.protobuf.DescriptorProto`.
|
||||||
|
final $typed_data.Uint8List permissionsDescriptor = $convert.base64Decode(
|
||||||
|
'CgtQZXJtaXNzaW9ucxI5Cg9jYW5fYWRkX21lbWJlcnMYASABKA4yES52ZWlsaWRjaGF0LlNjb3'
|
||||||
|
'BlUg1jYW5BZGRNZW1iZXJzEjUKDWNhbl9lZGl0X2luZm8YAiABKA4yES52ZWlsaWRjaGF0LlNj'
|
||||||
|
'b3BlUgtjYW5FZGl0SW5mbxIcCgltb2RlcmF0ZWQYAyABKAhSCW1vZGVyYXRlZA==');
|
||||||
|
|
||||||
|
@$core.Deprecated('Use membershipDescriptor instead')
|
||||||
|
const Membership$json = {
|
||||||
|
'1': 'Membership',
|
||||||
|
'2': [
|
||||||
|
{'1': 'watchers', '3': 1, '4': 3, '5': 11, '6': '.veilid.TypedKey', '10': 'watchers'},
|
||||||
|
{'1': 'moderated', '3': 2, '4': 3, '5': 11, '6': '.veilid.TypedKey', '10': 'moderated'},
|
||||||
|
{'1': 'talkers', '3': 3, '4': 3, '5': 11, '6': '.veilid.TypedKey', '10': 'talkers'},
|
||||||
|
{'1': 'moderators', '3': 4, '4': 3, '5': 11, '6': '.veilid.TypedKey', '10': 'moderators'},
|
||||||
|
{'1': 'admins', '3': 5, '4': 3, '5': 11, '6': '.veilid.TypedKey', '10': 'admins'},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Descriptor for `Membership`. Decode as a `google.protobuf.DescriptorProto`.
|
||||||
|
final $typed_data.Uint8List membershipDescriptor = $convert.base64Decode(
|
||||||
|
'CgpNZW1iZXJzaGlwEiwKCHdhdGNoZXJzGAEgAygLMhAudmVpbGlkLlR5cGVkS2V5Ugh3YXRjaG'
|
||||||
|
'VycxIuCgltb2RlcmF0ZWQYAiADKAsyEC52ZWlsaWQuVHlwZWRLZXlSCW1vZGVyYXRlZBIqCgd0'
|
||||||
|
'YWxrZXJzGAMgAygLMhAudmVpbGlkLlR5cGVkS2V5Ugd0YWxrZXJzEjAKCm1vZGVyYXRvcnMYBC'
|
||||||
|
'ADKAsyEC52ZWlsaWQuVHlwZWRLZXlSCm1vZGVyYXRvcnMSKAoGYWRtaW5zGAUgAygLMhAudmVp'
|
||||||
|
'bGlkLlR5cGVkS2V5UgZhZG1pbnM=');
|
||||||
|
|
||||||
|
@$core.Deprecated('Use chatSettingsDescriptor instead')
|
||||||
|
const ChatSettings$json = {
|
||||||
|
'1': 'ChatSettings',
|
||||||
|
'2': [
|
||||||
|
{'1': 'title', '3': 1, '4': 1, '5': 9, '10': 'title'},
|
||||||
|
{'1': 'description', '3': 2, '4': 1, '5': 9, '10': 'description'},
|
||||||
|
{'1': 'icon', '3': 3, '4': 1, '5': 11, '6': '.dht.DataReference', '9': 0, '10': 'icon', '17': true},
|
||||||
|
{'1': 'default_expiration', '3': 4, '4': 1, '5': 4, '10': 'defaultExpiration'},
|
||||||
|
],
|
||||||
|
'8': [
|
||||||
|
{'1': '_icon'},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Descriptor for `ChatSettings`. Decode as a `google.protobuf.DescriptorProto`.
|
||||||
|
final $typed_data.Uint8List chatSettingsDescriptor = $convert.base64Decode(
|
||||||
|
'CgxDaGF0U2V0dGluZ3MSFAoFdGl0bGUYASABKAlSBXRpdGxlEiAKC2Rlc2NyaXB0aW9uGAIgAS'
|
||||||
|
'gJUgtkZXNjcmlwdGlvbhIrCgRpY29uGAMgASgLMhIuZGh0LkRhdGFSZWZlcmVuY2VIAFIEaWNv'
|
||||||
|
'bogBARItChJkZWZhdWx0X2V4cGlyYXRpb24YBCABKARSEWRlZmF1bHRFeHBpcmF0aW9uQgcKBV'
|
||||||
|
'9pY29u');
|
||||||
|
|
||||||
@$core.Deprecated('Use messageDescriptor instead')
|
@$core.Deprecated('Use messageDescriptor instead')
|
||||||
const Message$json = {
|
const Message$json = {
|
||||||
'1': 'Message',
|
'1': 'Message',
|
||||||
'2': [
|
'2': [
|
||||||
{'1': 'author', '3': 1, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'author'},
|
{'1': 'id', '3': 1, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'id'},
|
||||||
{'1': 'timestamp', '3': 2, '4': 1, '5': 4, '10': 'timestamp'},
|
{'1': 'author', '3': 2, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'author'},
|
||||||
{'1': 'text', '3': 3, '4': 1, '5': 9, '10': 'text'},
|
{'1': 'timestamp', '3': 3, '4': 1, '5': 4, '10': 'timestamp'},
|
||||||
{'1': 'signature', '3': 4, '4': 1, '5': 11, '6': '.veilid.Signature', '10': 'signature'},
|
{'1': 'text', '3': 4, '4': 1, '5': 11, '6': '.veilidchat.Message.Text', '9': 0, '10': 'text'},
|
||||||
{'1': 'attachments', '3': 5, '4': 3, '5': 11, '6': '.veilidchat.Attachment', '10': 'attachments'},
|
{'1': 'secret', '3': 5, '4': 1, '5': 11, '6': '.veilidchat.Message.Secret', '9': 0, '10': 'secret'},
|
||||||
|
{'1': 'delete', '3': 6, '4': 1, '5': 11, '6': '.veilidchat.Message.ControlDelete', '9': 0, '10': 'delete'},
|
||||||
|
{'1': 'clear', '3': 7, '4': 1, '5': 11, '6': '.veilidchat.Message.ControlClear', '9': 0, '10': 'clear'},
|
||||||
|
{'1': 'settings', '3': 8, '4': 1, '5': 11, '6': '.veilidchat.Message.ControlSettings', '9': 0, '10': 'settings'},
|
||||||
|
{'1': 'permissions', '3': 9, '4': 1, '5': 11, '6': '.veilidchat.Message.ControlPermissions', '9': 0, '10': 'permissions'},
|
||||||
|
{'1': 'membership', '3': 10, '4': 1, '5': 11, '6': '.veilidchat.Message.ControlMembership', '9': 0, '10': 'membership'},
|
||||||
|
{'1': 'moderation', '3': 11, '4': 1, '5': 11, '6': '.veilidchat.Message.ControlModeration', '9': 0, '10': 'moderation'},
|
||||||
|
{'1': 'signature', '3': 12, '4': 1, '5': 11, '6': '.veilid.Signature', '10': 'signature'},
|
||||||
|
],
|
||||||
|
'3': [Message_Text$json, Message_Secret$json, Message_ControlDelete$json, Message_ControlClear$json, Message_ControlSettings$json, Message_ControlPermissions$json, Message_ControlMembership$json, Message_ControlModeration$json],
|
||||||
|
'8': [
|
||||||
|
{'1': 'kind'},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
@$core.Deprecated('Use messageDescriptor instead')
|
||||||
|
const Message_Text$json = {
|
||||||
|
'1': 'Text',
|
||||||
|
'2': [
|
||||||
|
{'1': 'text', '3': 1, '4': 1, '5': 9, '10': 'text'},
|
||||||
|
{'1': 'topic', '3': 2, '4': 1, '5': 9, '10': 'topic'},
|
||||||
|
{'1': 'reply_id', '3': 3, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'replyId'},
|
||||||
|
{'1': 'expiration', '3': 4, '4': 1, '5': 4, '10': 'expiration'},
|
||||||
|
{'1': 'view_limit', '3': 5, '4': 1, '5': 4, '10': 'viewLimit'},
|
||||||
|
{'1': 'attachments', '3': 6, '4': 3, '5': 11, '6': '.veilidchat.Attachment', '10': 'attachments'},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
@$core.Deprecated('Use messageDescriptor instead')
|
||||||
|
const Message_Secret$json = {
|
||||||
|
'1': 'Secret',
|
||||||
|
'2': [
|
||||||
|
{'1': 'ciphertext', '3': 1, '4': 1, '5': 12, '10': 'ciphertext'},
|
||||||
|
{'1': 'expiration', '3': 2, '4': 1, '5': 4, '10': 'expiration'},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
@$core.Deprecated('Use messageDescriptor instead')
|
||||||
|
const Message_ControlDelete$json = {
|
||||||
|
'1': 'ControlDelete',
|
||||||
|
'2': [
|
||||||
|
{'1': 'ids', '3': 1, '4': 3, '5': 11, '6': '.veilid.TypedKey', '10': 'ids'},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
@$core.Deprecated('Use messageDescriptor instead')
|
||||||
|
const Message_ControlClear$json = {
|
||||||
|
'1': 'ControlClear',
|
||||||
|
'2': [
|
||||||
|
{'1': 'timestamp', '3': 1, '4': 1, '5': 4, '10': 'timestamp'},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
@$core.Deprecated('Use messageDescriptor instead')
|
||||||
|
const Message_ControlSettings$json = {
|
||||||
|
'1': 'ControlSettings',
|
||||||
|
'2': [
|
||||||
|
{'1': 'settings', '3': 1, '4': 1, '5': 11, '6': '.veilidchat.ChatSettings', '10': 'settings'},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
@$core.Deprecated('Use messageDescriptor instead')
|
||||||
|
const Message_ControlPermissions$json = {
|
||||||
|
'1': 'ControlPermissions',
|
||||||
|
'2': [
|
||||||
|
{'1': 'permissions', '3': 1, '4': 1, '5': 11, '6': '.veilidchat.Permissions', '10': 'permissions'},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
@$core.Deprecated('Use messageDescriptor instead')
|
||||||
|
const Message_ControlMembership$json = {
|
||||||
|
'1': 'ControlMembership',
|
||||||
|
'2': [
|
||||||
|
{'1': 'membership', '3': 1, '4': 1, '5': 11, '6': '.veilidchat.Membership', '10': 'membership'},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
@$core.Deprecated('Use messageDescriptor instead')
|
||||||
|
const Message_ControlModeration$json = {
|
||||||
|
'1': 'ControlModeration',
|
||||||
|
'2': [
|
||||||
|
{'1': 'accepted_ids', '3': 1, '4': 3, '5': 11, '6': '.veilid.TypedKey', '10': 'acceptedIds'},
|
||||||
|
{'1': 'rejected_ids', '3': 2, '4': 3, '5': 11, '6': '.veilid.TypedKey', '10': 'rejectedIds'},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Descriptor for `Message`. Decode as a `google.protobuf.DescriptorProto`.
|
/// Descriptor for `Message`. Decode as a `google.protobuf.DescriptorProto`.
|
||||||
final $typed_data.Uint8List messageDescriptor = $convert.base64Decode(
|
final $typed_data.Uint8List messageDescriptor = $convert.base64Decode(
|
||||||
'CgdNZXNzYWdlEigKBmF1dGhvchgBIAEoCzIQLnZlaWxpZC5UeXBlZEtleVIGYXV0aG9yEhwKCX'
|
'CgdNZXNzYWdlEiAKAmlkGAEgASgLMhAudmVpbGlkLlR5cGVkS2V5UgJpZBIoCgZhdXRob3IYAi'
|
||||||
'RpbWVzdGFtcBgCIAEoBFIJdGltZXN0YW1wEhIKBHRleHQYAyABKAlSBHRleHQSLwoJc2lnbmF0'
|
'ABKAsyEC52ZWlsaWQuVHlwZWRLZXlSBmF1dGhvchIcCgl0aW1lc3RhbXAYAyABKARSCXRpbWVz'
|
||||||
'dXJlGAQgASgLMhEudmVpbGlkLlNpZ25hdHVyZVIJc2lnbmF0dXJlEjgKC2F0dGFjaG1lbnRzGA'
|
'dGFtcBIuCgR0ZXh0GAQgASgLMhgudmVpbGlkY2hhdC5NZXNzYWdlLlRleHRIAFIEdGV4dBI0Cg'
|
||||||
'UgAygLMhYudmVpbGlkY2hhdC5BdHRhY2htZW50UgthdHRhY2htZW50cw==');
|
'ZzZWNyZXQYBSABKAsyGi52ZWlsaWRjaGF0Lk1lc3NhZ2UuU2VjcmV0SABSBnNlY3JldBI7CgZk'
|
||||||
|
'ZWxldGUYBiABKAsyIS52ZWlsaWRjaGF0Lk1lc3NhZ2UuQ29udHJvbERlbGV0ZUgAUgZkZWxldG'
|
||||||
|
'USOAoFY2xlYXIYByABKAsyIC52ZWlsaWRjaGF0Lk1lc3NhZ2UuQ29udHJvbENsZWFySABSBWNs'
|
||||||
|
'ZWFyEkEKCHNldHRpbmdzGAggASgLMiMudmVpbGlkY2hhdC5NZXNzYWdlLkNvbnRyb2xTZXR0aW'
|
||||||
|
'5nc0gAUghzZXR0aW5ncxJKCgtwZXJtaXNzaW9ucxgJIAEoCzImLnZlaWxpZGNoYXQuTWVzc2Fn'
|
||||||
|
'ZS5Db250cm9sUGVybWlzc2lvbnNIAFILcGVybWlzc2lvbnMSRwoKbWVtYmVyc2hpcBgKIAEoCz'
|
||||||
|
'IlLnZlaWxpZGNoYXQuTWVzc2FnZS5Db250cm9sTWVtYmVyc2hpcEgAUgptZW1iZXJzaGlwEkcK'
|
||||||
|
'Cm1vZGVyYXRpb24YCyABKAsyJS52ZWlsaWRjaGF0Lk1lc3NhZ2UuQ29udHJvbE1vZGVyYXRpb2'
|
||||||
|
'5IAFIKbW9kZXJhdGlvbhIvCglzaWduYXR1cmUYDCABKAsyES52ZWlsaWQuU2lnbmF0dXJlUglz'
|
||||||
|
'aWduYXR1cmUa1gEKBFRleHQSEgoEdGV4dBgBIAEoCVIEdGV4dBIUCgV0b3BpYxgCIAEoCVIFdG'
|
||||||
|
'9waWMSKwoIcmVwbHlfaWQYAyABKAsyEC52ZWlsaWQuVHlwZWRLZXlSB3JlcGx5SWQSHgoKZXhw'
|
||||||
|
'aXJhdGlvbhgEIAEoBFIKZXhwaXJhdGlvbhIdCgp2aWV3X2xpbWl0GAUgASgEUgl2aWV3TGltaX'
|
||||||
|
'QSOAoLYXR0YWNobWVudHMYBiADKAsyFi52ZWlsaWRjaGF0LkF0dGFjaG1lbnRSC2F0dGFjaG1l'
|
||||||
|
'bnRzGkgKBlNlY3JldBIeCgpjaXBoZXJ0ZXh0GAEgASgMUgpjaXBoZXJ0ZXh0Eh4KCmV4cGlyYX'
|
||||||
|
'Rpb24YAiABKARSCmV4cGlyYXRpb24aMwoNQ29udHJvbERlbGV0ZRIiCgNpZHMYASADKAsyEC52'
|
||||||
|
'ZWlsaWQuVHlwZWRLZXlSA2lkcxosCgxDb250cm9sQ2xlYXISHAoJdGltZXN0YW1wGAEgASgEUg'
|
||||||
|
'l0aW1lc3RhbXAaRwoPQ29udHJvbFNldHRpbmdzEjQKCHNldHRpbmdzGAEgASgLMhgudmVpbGlk'
|
||||||
|
'Y2hhdC5DaGF0U2V0dGluZ3NSCHNldHRpbmdzGk8KEkNvbnRyb2xQZXJtaXNzaW9ucxI5CgtwZX'
|
||||||
|
'JtaXNzaW9ucxgBIAEoCzIXLnZlaWxpZGNoYXQuUGVybWlzc2lvbnNSC3Blcm1pc3Npb25zGksK'
|
||||||
|
'EUNvbnRyb2xNZW1iZXJzaGlwEjYKCm1lbWJlcnNoaXAYASABKAsyFi52ZWlsaWRjaGF0Lk1lbW'
|
||||||
|
'JlcnNoaXBSCm1lbWJlcnNoaXAafQoRQ29udHJvbE1vZGVyYXRpb24SMwoMYWNjZXB0ZWRfaWRz'
|
||||||
|
'GAEgAygLMhAudmVpbGlkLlR5cGVkS2V5UgthY2NlcHRlZElkcxIzCgxyZWplY3RlZF9pZHMYAi'
|
||||||
|
'ADKAsyEC52ZWlsaWQuVHlwZWRLZXlSC3JlamVjdGVkSWRzQgYKBGtpbmQ=');
|
||||||
|
|
||||||
|
@$core.Deprecated('Use reconciledMessageDescriptor instead')
|
||||||
|
const ReconciledMessage$json = {
|
||||||
|
'1': 'ReconciledMessage',
|
||||||
|
'2': [
|
||||||
|
{'1': 'content', '3': 1, '4': 1, '5': 11, '6': '.veilidchat.Message', '10': 'content'},
|
||||||
|
{'1': 'reconciled_time', '3': 2, '4': 1, '5': 4, '10': 'reconciledTime'},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Descriptor for `ReconciledMessage`. Decode as a `google.protobuf.DescriptorProto`.
|
||||||
|
final $typed_data.Uint8List reconciledMessageDescriptor = $convert.base64Decode(
|
||||||
|
'ChFSZWNvbmNpbGVkTWVzc2FnZRItCgdjb250ZW50GAEgASgLMhMudmVpbGlkY2hhdC5NZXNzYW'
|
||||||
|
'dlUgdjb250ZW50EicKD3JlY29uY2lsZWRfdGltZRgCIAEoBFIOcmVjb25jaWxlZFRpbWU=');
|
||||||
|
|
||||||
@$core.Deprecated('Use conversationDescriptor instead')
|
@$core.Deprecated('Use conversationDescriptor instead')
|
||||||
const Conversation$json = {
|
const Conversation$json = {
|
||||||
@ -132,6 +308,91 @@ final $typed_data.Uint8List conversationDescriptor = $convert.base64Decode(
|
|||||||
'JvZmlsZRIwChRpZGVudGl0eV9tYXN0ZXJfanNvbhgCIAEoCVISaWRlbnRpdHlNYXN0ZXJKc29u'
|
'JvZmlsZRIwChRpZGVudGl0eV9tYXN0ZXJfanNvbhgCIAEoCVISaWRlbnRpdHlNYXN0ZXJKc29u'
|
||||||
'EiwKCG1lc3NhZ2VzGAMgASgLMhAudmVpbGlkLlR5cGVkS2V5UghtZXNzYWdlcw==');
|
'EiwKCG1lc3NhZ2VzGAMgASgLMhAudmVpbGlkLlR5cGVkS2V5UghtZXNzYWdlcw==');
|
||||||
|
|
||||||
|
@$core.Deprecated('Use chatDescriptor instead')
|
||||||
|
const Chat$json = {
|
||||||
|
'1': 'Chat',
|
||||||
|
'2': [
|
||||||
|
{'1': 'settings', '3': 1, '4': 1, '5': 11, '6': '.veilidchat.ChatSettings', '10': 'settings'},
|
||||||
|
{'1': 'local_conversation_record_key', '3': 2, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'localConversationRecordKey'},
|
||||||
|
{'1': 'remote_conversation_record_key', '3': 3, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'remoteConversationRecordKey'},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Descriptor for `Chat`. Decode as a `google.protobuf.DescriptorProto`.
|
||||||
|
final $typed_data.Uint8List chatDescriptor = $convert.base64Decode(
|
||||||
|
'CgRDaGF0EjQKCHNldHRpbmdzGAEgASgLMhgudmVpbGlkY2hhdC5DaGF0U2V0dGluZ3NSCHNldH'
|
||||||
|
'RpbmdzElMKHWxvY2FsX2NvbnZlcnNhdGlvbl9yZWNvcmRfa2V5GAIgASgLMhAudmVpbGlkLlR5'
|
||||||
|
'cGVkS2V5Uhpsb2NhbENvbnZlcnNhdGlvblJlY29yZEtleRJVCh5yZW1vdGVfY29udmVyc2F0aW'
|
||||||
|
'9uX3JlY29yZF9rZXkYAyABKAsyEC52ZWlsaWQuVHlwZWRLZXlSG3JlbW90ZUNvbnZlcnNhdGlv'
|
||||||
|
'blJlY29yZEtleQ==');
|
||||||
|
|
||||||
|
@$core.Deprecated('Use groupChatDescriptor instead')
|
||||||
|
const GroupChat$json = {
|
||||||
|
'1': 'GroupChat',
|
||||||
|
'2': [
|
||||||
|
{'1': 'settings', '3': 1, '4': 1, '5': 11, '6': '.veilidchat.ChatSettings', '10': 'settings'},
|
||||||
|
{'1': 'local_conversation_record_key', '3': 2, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'localConversationRecordKey'},
|
||||||
|
{'1': 'remote_conversation_record_keys', '3': 3, '4': 3, '5': 11, '6': '.veilid.TypedKey', '10': 'remoteConversationRecordKeys'},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Descriptor for `GroupChat`. Decode as a `google.protobuf.DescriptorProto`.
|
||||||
|
final $typed_data.Uint8List groupChatDescriptor = $convert.base64Decode(
|
||||||
|
'CglHcm91cENoYXQSNAoIc2V0dGluZ3MYASABKAsyGC52ZWlsaWRjaGF0LkNoYXRTZXR0aW5nc1'
|
||||||
|
'IIc2V0dGluZ3MSUwodbG9jYWxfY29udmVyc2F0aW9uX3JlY29yZF9rZXkYAiABKAsyEC52ZWls'
|
||||||
|
'aWQuVHlwZWRLZXlSGmxvY2FsQ29udmVyc2F0aW9uUmVjb3JkS2V5ElcKH3JlbW90ZV9jb252ZX'
|
||||||
|
'JzYXRpb25fcmVjb3JkX2tleXMYAyADKAsyEC52ZWlsaWQuVHlwZWRLZXlSHHJlbW90ZUNvbnZl'
|
||||||
|
'cnNhdGlvblJlY29yZEtleXM=');
|
||||||
|
|
||||||
|
@$core.Deprecated('Use profileDescriptor instead')
|
||||||
|
const Profile$json = {
|
||||||
|
'1': 'Profile',
|
||||||
|
'2': [
|
||||||
|
{'1': 'name', '3': 1, '4': 1, '5': 9, '10': 'name'},
|
||||||
|
{'1': 'pronouns', '3': 2, '4': 1, '5': 9, '10': 'pronouns'},
|
||||||
|
{'1': 'about', '3': 3, '4': 1, '5': 9, '10': 'about'},
|
||||||
|
{'1': 'status', '3': 4, '4': 1, '5': 9, '10': 'status'},
|
||||||
|
{'1': 'availability', '3': 5, '4': 1, '5': 14, '6': '.veilidchat.Availability', '10': 'availability'},
|
||||||
|
{'1': 'avatar', '3': 6, '4': 1, '5': 11, '6': '.veilid.TypedKey', '9': 0, '10': 'avatar', '17': true},
|
||||||
|
],
|
||||||
|
'8': [
|
||||||
|
{'1': '_avatar'},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Descriptor for `Profile`. Decode as a `google.protobuf.DescriptorProto`.
|
||||||
|
final $typed_data.Uint8List profileDescriptor = $convert.base64Decode(
|
||||||
|
'CgdQcm9maWxlEhIKBG5hbWUYASABKAlSBG5hbWUSGgoIcHJvbm91bnMYAiABKAlSCHByb25vdW'
|
||||||
|
'5zEhQKBWFib3V0GAMgASgJUgVhYm91dBIWCgZzdGF0dXMYBCABKAlSBnN0YXR1cxI8CgxhdmFp'
|
||||||
|
'bGFiaWxpdHkYBSABKA4yGC52ZWlsaWRjaGF0LkF2YWlsYWJpbGl0eVIMYXZhaWxhYmlsaXR5Ei'
|
||||||
|
'0KBmF2YXRhchgGIAEoCzIQLnZlaWxpZC5UeXBlZEtleUgAUgZhdmF0YXKIAQFCCQoHX2F2YXRh'
|
||||||
|
'cg==');
|
||||||
|
|
||||||
|
@$core.Deprecated('Use accountDescriptor instead')
|
||||||
|
const Account$json = {
|
||||||
|
'1': 'Account',
|
||||||
|
'2': [
|
||||||
|
{'1': 'profile', '3': 1, '4': 1, '5': 11, '6': '.veilidchat.Profile', '10': 'profile'},
|
||||||
|
{'1': 'invisible', '3': 2, '4': 1, '5': 8, '10': 'invisible'},
|
||||||
|
{'1': 'auto_away_timeout_sec', '3': 3, '4': 1, '5': 13, '10': 'autoAwayTimeoutSec'},
|
||||||
|
{'1': 'contact_list', '3': 4, '4': 1, '5': 11, '6': '.dht.OwnedDHTRecordPointer', '10': 'contactList'},
|
||||||
|
{'1': 'contact_invitation_records', '3': 5, '4': 1, '5': 11, '6': '.dht.OwnedDHTRecordPointer', '10': 'contactInvitationRecords'},
|
||||||
|
{'1': 'chat_list', '3': 6, '4': 1, '5': 11, '6': '.dht.OwnedDHTRecordPointer', '10': 'chatList'},
|
||||||
|
{'1': 'group_chat_list', '3': 7, '4': 1, '5': 11, '6': '.dht.OwnedDHTRecordPointer', '10': 'groupChatList'},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Descriptor for `Account`. Decode as a `google.protobuf.DescriptorProto`.
|
||||||
|
final $typed_data.Uint8List accountDescriptor = $convert.base64Decode(
|
||||||
|
'CgdBY2NvdW50Ei0KB3Byb2ZpbGUYASABKAsyEy52ZWlsaWRjaGF0LlByb2ZpbGVSB3Byb2ZpbG'
|
||||||
|
'USHAoJaW52aXNpYmxlGAIgASgIUglpbnZpc2libGUSMQoVYXV0b19hd2F5X3RpbWVvdXRfc2Vj'
|
||||||
|
'GAMgASgNUhJhdXRvQXdheVRpbWVvdXRTZWMSPQoMY29udGFjdF9saXN0GAQgASgLMhouZGh0Lk'
|
||||||
|
'93bmVkREhUUmVjb3JkUG9pbnRlclILY29udGFjdExpc3QSWAoaY29udGFjdF9pbnZpdGF0aW9u'
|
||||||
|
'X3JlY29yZHMYBSABKAsyGi5kaHQuT3duZWRESFRSZWNvcmRQb2ludGVyUhhjb250YWN0SW52aX'
|
||||||
|
'RhdGlvblJlY29yZHMSNwoJY2hhdF9saXN0GAYgASgLMhouZGh0Lk93bmVkREhUUmVjb3JkUG9p'
|
||||||
|
'bnRlclIIY2hhdExpc3QSQgoPZ3JvdXBfY2hhdF9saXN0GAcgASgLMhouZGh0Lk93bmVkREhUUm'
|
||||||
|
'Vjb3JkUG9pbnRlclINZ3JvdXBDaGF0TGlzdA==');
|
||||||
|
|
||||||
@$core.Deprecated('Use contactDescriptor instead')
|
@$core.Deprecated('Use contactDescriptor instead')
|
||||||
const Contact$json = {
|
const Contact$json = {
|
||||||
'1': 'Contact',
|
'1': 'Contact',
|
||||||
@ -158,68 +419,6 @@ final $typed_data.Uint8List contactDescriptor = $convert.base64Decode(
|
|||||||
'lSGmxvY2FsQ29udmVyc2F0aW9uUmVjb3JkS2V5EisKEXNob3dfYXZhaWxhYmlsaXR5GAcgASgI'
|
'lSGmxvY2FsQ29udmVyc2F0aW9uUmVjb3JkS2V5EisKEXNob3dfYXZhaWxhYmlsaXR5GAcgASgI'
|
||||||
'UhBzaG93QXZhaWxhYmlsaXR5');
|
'UhBzaG93QXZhaWxhYmlsaXR5');
|
||||||
|
|
||||||
@$core.Deprecated('Use profileDescriptor instead')
|
|
||||||
const Profile$json = {
|
|
||||||
'1': 'Profile',
|
|
||||||
'2': [
|
|
||||||
{'1': 'name', '3': 1, '4': 1, '5': 9, '10': 'name'},
|
|
||||||
{'1': 'pronouns', '3': 2, '4': 1, '5': 9, '10': 'pronouns'},
|
|
||||||
{'1': 'status', '3': 3, '4': 1, '5': 9, '10': 'status'},
|
|
||||||
{'1': 'availability', '3': 4, '4': 1, '5': 14, '6': '.veilidchat.Availability', '10': 'availability'},
|
|
||||||
{'1': 'avatar', '3': 5, '4': 1, '5': 11, '6': '.veilid.TypedKey', '9': 0, '10': 'avatar', '17': true},
|
|
||||||
],
|
|
||||||
'8': [
|
|
||||||
{'1': '_avatar'},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Descriptor for `Profile`. Decode as a `google.protobuf.DescriptorProto`.
|
|
||||||
final $typed_data.Uint8List profileDescriptor = $convert.base64Decode(
|
|
||||||
'CgdQcm9maWxlEhIKBG5hbWUYASABKAlSBG5hbWUSGgoIcHJvbm91bnMYAiABKAlSCHByb25vdW'
|
|
||||||
'5zEhYKBnN0YXR1cxgDIAEoCVIGc3RhdHVzEjwKDGF2YWlsYWJpbGl0eRgEIAEoDjIYLnZlaWxp'
|
|
||||||
'ZGNoYXQuQXZhaWxhYmlsaXR5UgxhdmFpbGFiaWxpdHkSLQoGYXZhdGFyGAUgASgLMhAudmVpbG'
|
|
||||||
'lkLlR5cGVkS2V5SABSBmF2YXRhcogBAUIJCgdfYXZhdGFy');
|
|
||||||
|
|
||||||
@$core.Deprecated('Use chatDescriptor instead')
|
|
||||||
const Chat$json = {
|
|
||||||
'1': 'Chat',
|
|
||||||
'2': [
|
|
||||||
{'1': 'type', '3': 1, '4': 1, '5': 14, '6': '.veilidchat.ChatType', '10': 'type'},
|
|
||||||
{'1': 'remote_conversation_record_key', '3': 2, '4': 1, '5': 11, '6': '.veilid.TypedKey', '10': 'remoteConversationRecordKey'},
|
|
||||||
{'1': 'reconciled_chat_record', '3': 3, '4': 1, '5': 11, '6': '.dht.OwnedDHTRecordPointer', '10': 'reconciledChatRecord'},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Descriptor for `Chat`. Decode as a `google.protobuf.DescriptorProto`.
|
|
||||||
final $typed_data.Uint8List chatDescriptor = $convert.base64Decode(
|
|
||||||
'CgRDaGF0EigKBHR5cGUYASABKA4yFC52ZWlsaWRjaGF0LkNoYXRUeXBlUgR0eXBlElUKHnJlbW'
|
|
||||||
'90ZV9jb252ZXJzYXRpb25fcmVjb3JkX2tleRgCIAEoCzIQLnZlaWxpZC5UeXBlZEtleVIbcmVt'
|
|
||||||
'b3RlQ29udmVyc2F0aW9uUmVjb3JkS2V5ElAKFnJlY29uY2lsZWRfY2hhdF9yZWNvcmQYAyABKA'
|
|
||||||
'syGi5kaHQuT3duZWRESFRSZWNvcmRQb2ludGVyUhRyZWNvbmNpbGVkQ2hhdFJlY29yZA==');
|
|
||||||
|
|
||||||
@$core.Deprecated('Use accountDescriptor instead')
|
|
||||||
const Account$json = {
|
|
||||||
'1': 'Account',
|
|
||||||
'2': [
|
|
||||||
{'1': 'profile', '3': 1, '4': 1, '5': 11, '6': '.veilidchat.Profile', '10': 'profile'},
|
|
||||||
{'1': 'invisible', '3': 2, '4': 1, '5': 8, '10': 'invisible'},
|
|
||||||
{'1': 'auto_away_timeout_sec', '3': 3, '4': 1, '5': 13, '10': 'autoAwayTimeoutSec'},
|
|
||||||
{'1': 'contact_list', '3': 4, '4': 1, '5': 11, '6': '.dht.OwnedDHTRecordPointer', '10': 'contactList'},
|
|
||||||
{'1': 'contact_invitation_records', '3': 5, '4': 1, '5': 11, '6': '.dht.OwnedDHTRecordPointer', '10': 'contactInvitationRecords'},
|
|
||||||
{'1': 'chat_list', '3': 6, '4': 1, '5': 11, '6': '.dht.OwnedDHTRecordPointer', '10': 'chatList'},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Descriptor for `Account`. Decode as a `google.protobuf.DescriptorProto`.
|
|
||||||
final $typed_data.Uint8List accountDescriptor = $convert.base64Decode(
|
|
||||||
'CgdBY2NvdW50Ei0KB3Byb2ZpbGUYASABKAsyEy52ZWlsaWRjaGF0LlByb2ZpbGVSB3Byb2ZpbG'
|
|
||||||
'USHAoJaW52aXNpYmxlGAIgASgIUglpbnZpc2libGUSMQoVYXV0b19hd2F5X3RpbWVvdXRfc2Vj'
|
|
||||||
'GAMgASgNUhJhdXRvQXdheVRpbWVvdXRTZWMSPQoMY29udGFjdF9saXN0GAQgASgLMhouZGh0Lk'
|
|
||||||
'93bmVkREhUUmVjb3JkUG9pbnRlclILY29udGFjdExpc3QSWAoaY29udGFjdF9pbnZpdGF0aW9u'
|
|
||||||
'X3JlY29yZHMYBSABKAsyGi5kaHQuT3duZWRESFRSZWNvcmRQb2ludGVyUhhjb250YWN0SW52aX'
|
|
||||||
'RhdGlvblJlY29yZHMSNwoJY2hhdF9saXN0GAYgASgLMhouZGh0Lk93bmVkREhUUmVjb3JkUG9p'
|
|
||||||
'bnRlclIIY2hhdExpc3Q=');
|
|
||||||
|
|
||||||
@$core.Deprecated('Use contactInvitationDescriptor instead')
|
@$core.Deprecated('Use contactInvitationDescriptor instead')
|
||||||
const ContactInvitation$json = {
|
const ContactInvitation$json = {
|
||||||
'1': 'ContactInvitation',
|
'1': 'ContactInvitation',
|
||||||
|
@ -1,51 +1,230 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// VeilidChat Protocol Buffer Definitions
|
||||||
|
//
|
||||||
|
// * Timestamps are in microseconds (us) since epoch
|
||||||
|
// * Durations are in microseconds (us)
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
package veilidchat;
|
package veilidchat;
|
||||||
|
|
||||||
import "veilid.proto";
|
import "veilid.proto";
|
||||||
import "dht.proto";
|
import "dht.proto";
|
||||||
|
|
||||||
// AttachmentKind
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Enumeration of well-known attachment types
|
// Enumerations
|
||||||
enum AttachmentKind {
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
ATTACHMENT_KIND_UNSPECIFIED = 0;
|
|
||||||
ATTACHMENT_KIND_FILE = 1;
|
// Contact availability
|
||||||
ATTACHMENT_KIND_IMAGE = 2;
|
enum Availability {
|
||||||
|
AVAILABILITY_UNSPECIFIED = 0;
|
||||||
|
AVAILABILITY_OFFLINE = 1;
|
||||||
|
AVAILABILITY_FREE = 2;
|
||||||
|
AVAILABILITY_BUSY = 3;
|
||||||
|
AVAILABILITY_AWAY = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Encryption used on secret keys
|
||||||
|
enum EncryptionKeyType {
|
||||||
|
ENCRYPTION_KEY_TYPE_UNSPECIFIED = 0;
|
||||||
|
ENCRYPTION_KEY_TYPE_NONE = 1;
|
||||||
|
ENCRYPTION_KEY_TYPE_PIN = 2;
|
||||||
|
ENCRYPTION_KEY_TYPE_PASSWORD = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scope of a chat
|
||||||
|
enum Scope {
|
||||||
|
// Can read chats but not send messages
|
||||||
|
WATCHERS = 0;
|
||||||
|
// Can send messages subject to moderation
|
||||||
|
// If moderation is disabled, this is equivalent to WATCHERS
|
||||||
|
MODERATED = 1;
|
||||||
|
// Can send messages without moderation
|
||||||
|
TALKERS = 2;
|
||||||
|
// Can moderate messages sent my members if moderation is enabled
|
||||||
|
MODERATORS = 3;
|
||||||
|
// Can perform all actions
|
||||||
|
ADMINS = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Attachments
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// A single attachment
|
// A single attachment
|
||||||
message Attachment {
|
message Attachment {
|
||||||
// Type of the data
|
oneof kind {
|
||||||
AttachmentKind kind = 1;
|
AttachmentMedia media = 1;
|
||||||
// MIME type of the data
|
}
|
||||||
string mime = 2;
|
|
||||||
// Title or filename
|
|
||||||
string name = 3;
|
|
||||||
// Pointer to the data content
|
|
||||||
dht.DataReference content = 4;
|
|
||||||
// Author signature over all attachment fields and content fields and bytes
|
// Author signature over all attachment fields and content fields and bytes
|
||||||
veilid.Signature signature = 5;
|
veilid.Signature signature = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A file, audio, image, or video attachment
|
||||||
|
message AttachmentMedia {
|
||||||
|
// MIME type of the data
|
||||||
|
string mime = 1;
|
||||||
|
// Title or filename
|
||||||
|
string name = 2;
|
||||||
|
// Pointer to the data content
|
||||||
|
dht.DataReference content = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Chat room controls
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Permissions of a chat
|
||||||
|
message Permissions {
|
||||||
|
// Parties in this scope or higher can add members to their own group or lower
|
||||||
|
Scope can_add_members = 1;
|
||||||
|
// Parties in this scope or higher can change the 'info' of a group
|
||||||
|
Scope can_edit_info = 2;
|
||||||
|
// If moderation is enabled or not.
|
||||||
|
bool moderated = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The membership of a chat
|
||||||
|
message Membership {
|
||||||
|
// Conversation keys for parties in the 'watchers' group
|
||||||
|
repeated veilid.TypedKey watchers = 1;
|
||||||
|
// Conversation keys for parties in the 'moderated' group
|
||||||
|
repeated veilid.TypedKey moderated = 2;
|
||||||
|
// Conversation keys for parties in the 'talkers' group
|
||||||
|
repeated veilid.TypedKey talkers = 3;
|
||||||
|
// Conversation keys for parties in the 'moderators' group
|
||||||
|
repeated veilid.TypedKey moderators = 4;
|
||||||
|
// Conversation keys for parties in the 'admins' group
|
||||||
|
repeated veilid.TypedKey admins = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The chat settings
|
||||||
|
message ChatSettings {
|
||||||
|
// Title for the chat
|
||||||
|
string title = 1;
|
||||||
|
// Description for the chat
|
||||||
|
string description = 2;
|
||||||
|
// Icon for the chat
|
||||||
|
optional dht.DataReference icon = 3;
|
||||||
|
// Default message expiration duration (in us)
|
||||||
|
uint64 default_expiration = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Messages
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// A single message as part of a series of messages
|
// A single message as part of a series of messages
|
||||||
message Message {
|
message Message {
|
||||||
// Author of the message
|
|
||||||
veilid.TypedKey author = 1;
|
// A text message
|
||||||
// Time the message was sent (us since epoch)
|
message Text {
|
||||||
uint64 timestamp = 2;
|
|
||||||
// Text of the message
|
// Text of the message
|
||||||
string text = 3;
|
string text = 1;
|
||||||
// Author signature over all of the fields and attachment signatures
|
// Topic of the message / Content warning
|
||||||
veilid.Signature signature = 4;
|
string topic = 2;
|
||||||
|
// Message id replied to
|
||||||
|
veilid.TypedKey reply_id = 3;
|
||||||
|
// Message expiration timestamp
|
||||||
|
uint64 expiration = 4;
|
||||||
|
// Message view limit before deletion
|
||||||
|
uint64 view_limit = 5;
|
||||||
// Attachments on the message
|
// Attachments on the message
|
||||||
repeated Attachment attachments = 5;
|
repeated Attachment attachments = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A secret message
|
||||||
|
message Secret {
|
||||||
|
// Text message protobuf encrypted by a key
|
||||||
|
bytes ciphertext = 1;
|
||||||
|
// Secret expiration timestamp
|
||||||
|
// This is the time after which an un-revealed secret will get deleted
|
||||||
|
uint64 expiration = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A 'delete' control message
|
||||||
|
// Deletes a set of messages by their ids
|
||||||
|
message ControlDelete {
|
||||||
|
repeated veilid.TypedKey ids = 1;
|
||||||
|
}
|
||||||
|
// A 'clear' control message
|
||||||
|
// Deletes a set of messages from before some timestamp
|
||||||
|
message ControlClear {
|
||||||
|
// The latest timestamp to delete messages before
|
||||||
|
// If this is zero then all messages are cleared
|
||||||
|
uint64 timestamp = 1;
|
||||||
|
}
|
||||||
|
// A 'change settings' control message
|
||||||
|
message ControlSettings {
|
||||||
|
ChatSettings settings = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A 'change permissions' control message
|
||||||
|
// Changes the permissions of a chat
|
||||||
|
message ControlPermissions {
|
||||||
|
Permissions permissions = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A 'change membership' control message
|
||||||
|
// Changes the
|
||||||
|
message ControlMembership {
|
||||||
|
Membership membership = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A 'moderation' control message
|
||||||
|
// Accepts or rejects a set of messages
|
||||||
|
message ControlModeration {
|
||||||
|
repeated veilid.TypedKey accepted_ids = 1;
|
||||||
|
repeated veilid.TypedKey rejected_ids = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Hash of previous message from the same author,
|
||||||
|
// including its previous hash.
|
||||||
|
// Also serves as a unique key for the message.
|
||||||
|
veilid.TypedKey id = 1;
|
||||||
|
// Author of the message (identity public key)
|
||||||
|
veilid.TypedKey author = 2;
|
||||||
|
// Time the message was sent according to sender
|
||||||
|
uint64 timestamp = 3;
|
||||||
|
|
||||||
|
// Message kind
|
||||||
|
oneof kind {
|
||||||
|
Text text = 4;
|
||||||
|
Secret secret = 5;
|
||||||
|
ControlDelete delete = 6;
|
||||||
|
ControlClear clear = 7;
|
||||||
|
ControlSettings settings = 8;
|
||||||
|
ControlPermissions permissions = 9;
|
||||||
|
ControlMembership membership = 10;
|
||||||
|
ControlModeration moderation = 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Author signature over all of the fields and attachment signatures
|
||||||
|
veilid.Signature signature = 12;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Locally stored messages for chats
|
||||||
|
message ReconciledMessage {
|
||||||
|
// The message as sent
|
||||||
|
Message content = 1;
|
||||||
|
// The timestamp the message was reconciled
|
||||||
|
uint64 reconciled_time = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Chats
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// The means of direct communications that is synchronized between
|
// The means of direct communications that is synchronized between
|
||||||
// two users. Visible and encrypted for the other party.
|
// two users. Visible and encrypted for the other party.
|
||||||
// Includes communications for:
|
// Includes communications for:
|
||||||
// * Profile changes
|
// * Profile changes
|
||||||
// * Identity changes
|
// * Identity changes
|
||||||
// * 1-1 chat messages
|
// * 1-1 chat messages
|
||||||
|
// * Group chat messages
|
||||||
//
|
//
|
||||||
// DHT Schema: SMPL(0,1,[identityPublicKey])
|
// DHT Schema: SMPL(0,1,[identityPublicKey])
|
||||||
// DHT Key (UnicastOutbox): localConversation
|
// DHT Key (UnicastOutbox): localConversation
|
||||||
@ -54,12 +233,84 @@ message Message {
|
|||||||
message Conversation {
|
message Conversation {
|
||||||
// Profile to publish to friend
|
// Profile to publish to friend
|
||||||
Profile profile = 1;
|
Profile profile = 1;
|
||||||
// Identity master (JSON) to publish to friend
|
// Identity master (JSON) to publish to friend or chat room
|
||||||
string identity_master_json = 2;
|
string identity_master_json = 2;
|
||||||
// Messages DHTLog (xxx for now DHTShortArray)
|
// Messages DHTLog
|
||||||
veilid.TypedKey messages = 3;
|
veilid.TypedKey messages = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Either a 1-1 conversation or a group chat
|
||||||
|
// Privately encrypted, this is the local user's copy of the chat
|
||||||
|
message Chat {
|
||||||
|
// Settings
|
||||||
|
ChatSettings settings = 1;
|
||||||
|
// Conversation key for this user
|
||||||
|
veilid.TypedKey local_conversation_record_key = 2;
|
||||||
|
// Conversation key for the other party
|
||||||
|
veilid.TypedKey remote_conversation_record_key = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A group chat
|
||||||
|
// Privately encrypted, this is the local user's copy of the chat
|
||||||
|
message GroupChat {
|
||||||
|
// Settings
|
||||||
|
ChatSettings settings = 1;
|
||||||
|
// Conversation key for this user
|
||||||
|
veilid.TypedKey local_conversation_record_key = 2;
|
||||||
|
// Conversation keys for the other parties
|
||||||
|
repeated veilid.TypedKey remote_conversation_record_keys = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Accounts
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Publicly shared profile information for both contacts and accounts
|
||||||
|
// Contains:
|
||||||
|
// Name - Friendly name
|
||||||
|
// Pronouns - Pronouns of user
|
||||||
|
// Icon - Little picture to represent user in contact list
|
||||||
|
message Profile {
|
||||||
|
// Friendy name
|
||||||
|
string name = 1;
|
||||||
|
// Pronouns of user
|
||||||
|
string pronouns = 2;
|
||||||
|
// Description of the user
|
||||||
|
string about = 3;
|
||||||
|
// Status/away message
|
||||||
|
string status = 4;
|
||||||
|
// Availability
|
||||||
|
Availability availability = 5;
|
||||||
|
// Avatar DHTData
|
||||||
|
optional veilid.TypedKey avatar = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A record of an individual account
|
||||||
|
// Pointed to by the identity account map in the identity key
|
||||||
|
//
|
||||||
|
// DHT Schema: DFLT(1)
|
||||||
|
// DHT Private: accountSecretKey
|
||||||
|
message Account {
|
||||||
|
// The user's profile that gets shared with contacts
|
||||||
|
Profile profile = 1;
|
||||||
|
// Invisibility makes you always look 'Offline'
|
||||||
|
bool invisible = 2;
|
||||||
|
// Auto-away sets 'away' mode after an inactivity time
|
||||||
|
uint32 auto_away_timeout_sec = 3;
|
||||||
|
// The contacts DHTList for this account
|
||||||
|
// DHT Private
|
||||||
|
dht.OwnedDHTRecordPointer contact_list = 4;
|
||||||
|
// The ContactInvitationRecord DHTShortArray for this account
|
||||||
|
// DHT Private
|
||||||
|
dht.OwnedDHTRecordPointer contact_invitation_records = 5;
|
||||||
|
// The Chats DHTList for this account
|
||||||
|
// DHT Private
|
||||||
|
dht.OwnedDHTRecordPointer chat_list = 6;
|
||||||
|
// The GroupChats DHTList for this account
|
||||||
|
// DHT Private
|
||||||
|
dht.OwnedDHTRecordPointer group_chat_list = 7;
|
||||||
|
}
|
||||||
|
|
||||||
// A record of a contact that has accepted a contact invitation
|
// A record of a contact that has accepted a contact invitation
|
||||||
// Contains a copy of the most recent remote profile as well as
|
// Contains a copy of the most recent remote profile as well as
|
||||||
// a locally edited profile.
|
// a locally edited profile.
|
||||||
@ -80,87 +331,13 @@ message Contact {
|
|||||||
veilid.TypedKey remote_conversation_record_key = 5;
|
veilid.TypedKey remote_conversation_record_key = 5;
|
||||||
// Our conversation key for friend to sync
|
// Our conversation key for friend to sync
|
||||||
veilid.TypedKey local_conversation_record_key = 6;
|
veilid.TypedKey local_conversation_record_key = 6;
|
||||||
// Show availability
|
// Show availability to this contact
|
||||||
bool show_availability = 7;
|
bool show_availability = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contact availability
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
enum Availability {
|
// Invitations
|
||||||
AVAILABILITY_UNSPECIFIED = 0;
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
AVAILABILITY_OFFLINE = 1;
|
|
||||||
AVAILABILITY_FREE = 2;
|
|
||||||
AVAILABILITY_BUSY = 3;
|
|
||||||
AVAILABILITY_AWAY = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Publicly shared profile information for both contacts and accounts
|
|
||||||
// Contains:
|
|
||||||
// Name - Friendly name
|
|
||||||
// Pronouns - Pronouns of user
|
|
||||||
// Icon - Little picture to represent user in contact list
|
|
||||||
message Profile {
|
|
||||||
// Friendy name
|
|
||||||
string name = 1;
|
|
||||||
// Pronouns of user
|
|
||||||
string pronouns = 2;
|
|
||||||
// Status/away message
|
|
||||||
string status = 3;
|
|
||||||
// Availability
|
|
||||||
Availability availability = 4;
|
|
||||||
// Avatar DHTData
|
|
||||||
optional veilid.TypedKey avatar = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enum ChatType {
|
|
||||||
CHAT_TYPE_UNSPECIFIED = 0;
|
|
||||||
SINGLE_CONTACT = 1;
|
|
||||||
GROUP = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Either a 1-1 conversation or a group chat (eventually)
|
|
||||||
// Privately encrypted, this is the local user's copy of the chat
|
|
||||||
message Chat {
|
|
||||||
// What kind of chat is this
|
|
||||||
ChatType type = 1;
|
|
||||||
// Conversation key for the other party
|
|
||||||
veilid.TypedKey remote_conversation_record_key = 2;
|
|
||||||
// Reconciled chat record DHTLog (xxx for now DHTShortArray)
|
|
||||||
dht.OwnedDHTRecordPointer reconciled_chat_record = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A record of an individual account
|
|
||||||
// Pointed to by the identity account map in the identity key
|
|
||||||
//
|
|
||||||
// DHT Schema: DFLT(1)
|
|
||||||
// DHT Private: accountSecretKey
|
|
||||||
message Account {
|
|
||||||
// The user's profile that gets shared with contacts
|
|
||||||
Profile profile = 1;
|
|
||||||
// Invisibility makes you always look 'Offline'
|
|
||||||
bool invisible = 2;
|
|
||||||
// Auto-away sets 'away' mode after an inactivity time
|
|
||||||
uint32 auto_away_timeout_sec = 3;
|
|
||||||
// The contacts DHTList for this account
|
|
||||||
// DHT Private
|
|
||||||
dht.OwnedDHTRecordPointer contact_list = 4;
|
|
||||||
// The ContactInvitationRecord DHTShortArray for this account
|
|
||||||
// DHT Private
|
|
||||||
dht.OwnedDHTRecordPointer contact_invitation_records = 5;
|
|
||||||
// The chats DHTList for this account
|
|
||||||
// DHT Private
|
|
||||||
dht.OwnedDHTRecordPointer chat_list = 6;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncryptionKeyType
|
|
||||||
// Encryption of secret
|
|
||||||
enum EncryptionKeyType {
|
|
||||||
ENCRYPTION_KEY_TYPE_UNSPECIFIED = 0;
|
|
||||||
ENCRYPTION_KEY_TYPE_NONE = 1;
|
|
||||||
ENCRYPTION_KEY_TYPE_PIN = 2;
|
|
||||||
ENCRYPTION_KEY_TYPE_PASSWORD = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invitation that is shared for VeilidChat contact connections
|
// Invitation that is shared for VeilidChat contact connections
|
||||||
// serialized to QR code or data blob, not send over DHT, out of band.
|
// serialized to QR code or data blob, not send over DHT, out of band.
|
||||||
|
@ -44,7 +44,7 @@ Widget waitingPage({String? text}) => Builder(builder: (context) {
|
|||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final scale = theme.extension<ScaleScheme>()!;
|
final scale = theme.extension<ScaleScheme>()!;
|
||||||
return ColoredBox(
|
return ColoredBox(
|
||||||
color: scale.tertiaryScale.primaryText,
|
color: scale.tertiaryScale.appBackground,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Column(children: [
|
child: Column(children: [
|
||||||
buildProgressIndicator().expanded(),
|
buildProgressIndicator().expanded(),
|
||||||
|
@ -7,6 +7,7 @@ import 'fixtures/fixtures.dart';
|
|||||||
import 'test_dht_log.dart';
|
import 'test_dht_log.dart';
|
||||||
import 'test_dht_record_pool.dart';
|
import 'test_dht_record_pool.dart';
|
||||||
import 'test_dht_short_array.dart';
|
import 'test_dht_short_array.dart';
|
||||||
|
import 'test_table_db_array.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
final startTime = DateTime.now();
|
final startTime = DateTime.now();
|
||||||
@ -34,6 +35,17 @@ void main() {
|
|||||||
setUpAll(veilidFixture.attach);
|
setUpAll(veilidFixture.attach);
|
||||||
tearDownAll(veilidFixture.detach);
|
tearDownAll(veilidFixture.detach);
|
||||||
|
|
||||||
|
group('TableDB Tests', () {
|
||||||
|
group('TableDBArray Tests', () {
|
||||||
|
test('create TableDBArray', makeTestTableDBArrayCreateDelete());
|
||||||
|
test(
|
||||||
|
timeout: const Timeout(Duration(seconds: 480)),
|
||||||
|
'add/truncate TableDBArray',
|
||||||
|
makeTestDHTLogAddTruncate(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
group('DHT Support Tests', () {
|
group('DHT Support Tests', () {
|
||||||
setUpAll(updateProcessorFixture.setUp);
|
setUpAll(updateProcessorFixture.setUp);
|
||||||
setUpAll(tickerFixture.setUp);
|
setUpAll(tickerFixture.setUp);
|
||||||
|
@ -64,8 +64,7 @@ Future<void> Function() makeTestDHTLogAddTruncate({required int stride}) =>
|
|||||||
const chunk = 25;
|
const chunk = 25;
|
||||||
for (var n = 0; n < dataset.length; n += chunk) {
|
for (var n = 0; n < dataset.length; n += chunk) {
|
||||||
print('$n-${n + chunk - 1} ');
|
print('$n-${n + chunk - 1} ');
|
||||||
final success =
|
final success = await w.tryAddItems(dataset.sublist(n, n + chunk));
|
||||||
await w.tryAppendItems(dataset.sublist(n, n + chunk));
|
|
||||||
expect(success, isTrue);
|
expect(success, isTrue);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -94,7 +93,7 @@ Future<void> Function() makeTestDHTLogAddTruncate({required int stride}) =>
|
|||||||
}
|
}
|
||||||
print('truncate\n');
|
print('truncate\n');
|
||||||
{
|
{
|
||||||
await dlog.operateAppend((w) async => w.truncate(5));
|
await dlog.operateAppend((w) async => w.truncate(w.length - 5));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
final dataset6 = await dlog
|
final dataset6 = await dlog
|
||||||
@ -103,7 +102,7 @@ Future<void> Function() makeTestDHTLogAddTruncate({required int stride}) =>
|
|||||||
}
|
}
|
||||||
print('truncate 2\n');
|
print('truncate 2\n');
|
||||||
{
|
{
|
||||||
await dlog.operateAppend((w) async => w.truncate(251));
|
await dlog.operateAppend((w) async => w.truncate(w.length - 251));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
final dataset7 = await dlog
|
final dataset7 = await dlog
|
||||||
|
@ -0,0 +1,134 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
import 'package:veilid_support/veilid_support.dart';
|
||||||
|
|
||||||
|
Future<void> Function() makeTestTableDBArrayCreateDelete() => () async {
|
||||||
|
// Close before delete
|
||||||
|
{
|
||||||
|
final arr = await TableDBArray(
|
||||||
|
table: 'test', crypto: const VeilidCryptoPublic());
|
||||||
|
|
||||||
|
expect(await arr.operate((r) async => r.length), isZero);
|
||||||
|
expect(arr.isOpen, isTrue);
|
||||||
|
await arr.close();
|
||||||
|
expect(arr.isOpen, isFalse);
|
||||||
|
await arr.delete();
|
||||||
|
// Operate should fail
|
||||||
|
await expectLater(() async => arr.operate((r) async => r.length),
|
||||||
|
throwsA(isA<StateError>()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close after delete
|
||||||
|
{
|
||||||
|
final arr = await DHTShortArray.create(
|
||||||
|
debugName: 'sa_create_delete 2 stride $stride', stride: stride);
|
||||||
|
await arr.delete();
|
||||||
|
// Operate should still succeed because things aren't closed
|
||||||
|
expect(await arr.operate((r) async => r.length), isZero);
|
||||||
|
await arr.close();
|
||||||
|
// Operate should fail
|
||||||
|
await expectLater(() async => arr.operate((r) async => r.length),
|
||||||
|
throwsA(isA<StateError>()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close after delete multiple
|
||||||
|
// Okay to request delete multiple times before close
|
||||||
|
{
|
||||||
|
final arr = await DHTShortArray.create(
|
||||||
|
debugName: 'sa_create_delete 3 stride $stride', stride: stride);
|
||||||
|
await arr.delete();
|
||||||
|
await arr.delete();
|
||||||
|
// Operate should still succeed because things aren't closed
|
||||||
|
expect(await arr.operate((r) async => r.length), isZero);
|
||||||
|
await arr.close();
|
||||||
|
await expectLater(() async => arr.close(), throwsA(isA<StateError>()));
|
||||||
|
// Operate should fail
|
||||||
|
await expectLater(() async => arr.operate((r) async => r.length),
|
||||||
|
throwsA(isA<StateError>()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Future<void> Function() makeTestTableDBArrayAdd({required int stride}) =>
|
||||||
|
() async {
|
||||||
|
final arr = await DHTShortArray.create(
|
||||||
|
debugName: 'sa_add 1 stride $stride', stride: stride);
|
||||||
|
|
||||||
|
final dataset = Iterable<int>.generate(256)
|
||||||
|
.map((n) => utf8.encode('elem $n'))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
print('adding singles\n');
|
||||||
|
{
|
||||||
|
final res = await arr.operateWrite((w) async {
|
||||||
|
for (var n = 4; n < 8; n++) {
|
||||||
|
print('$n ');
|
||||||
|
final success = await w.tryAddItem(dataset[n]);
|
||||||
|
expect(success, isTrue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(res, isNull);
|
||||||
|
}
|
||||||
|
|
||||||
|
print('adding batch\n');
|
||||||
|
{
|
||||||
|
final res = await arr.operateWrite((w) async {
|
||||||
|
print('${dataset.length ~/ 2}-${dataset.length}');
|
||||||
|
final success = await w.tryAddItems(
|
||||||
|
dataset.sublist(dataset.length ~/ 2, dataset.length));
|
||||||
|
expect(success, isTrue);
|
||||||
|
});
|
||||||
|
expect(res, isNull);
|
||||||
|
}
|
||||||
|
|
||||||
|
print('inserting singles\n');
|
||||||
|
{
|
||||||
|
final res = await arr.operateWrite((w) async {
|
||||||
|
for (var n = 0; n < 4; n++) {
|
||||||
|
print('$n ');
|
||||||
|
final success = await w.tryInsertItem(n, dataset[n]);
|
||||||
|
expect(success, isTrue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(res, isNull);
|
||||||
|
}
|
||||||
|
|
||||||
|
print('inserting batch\n');
|
||||||
|
{
|
||||||
|
final res = await arr.operateWrite((w) async {
|
||||||
|
print('8-${dataset.length ~/ 2}');
|
||||||
|
final success = await w.tryInsertItems(
|
||||||
|
8, dataset.sublist(8, dataset.length ~/ 2));
|
||||||
|
expect(success, isTrue);
|
||||||
|
});
|
||||||
|
expect(res, isNull);
|
||||||
|
}
|
||||||
|
|
||||||
|
//print('get all\n');
|
||||||
|
{
|
||||||
|
final dataset2 = await arr.operate((r) async => r.getItemRange(0));
|
||||||
|
expect(dataset2, equals(dataset));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
final dataset3 =
|
||||||
|
await arr.operate((r) async => r.getItemRange(64, length: 128));
|
||||||
|
expect(dataset3, equals(dataset.sublist(64, 64 + 128)));
|
||||||
|
}
|
||||||
|
|
||||||
|
//print('clear\n');
|
||||||
|
{
|
||||||
|
await arr.operateWriteEventual((w) async {
|
||||||
|
await w.clear();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//print('get all\n');
|
||||||
|
{
|
||||||
|
final dataset4 = await arr.operate((r) async => r.getItemRange(0));
|
||||||
|
expect(dataset4, isEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
|
await arr.delete();
|
||||||
|
await arr.close();
|
||||||
|
};
|
@ -9,11 +9,11 @@ import 'package:meta/meta.dart';
|
|||||||
|
|
||||||
import '../../../veilid_support.dart';
|
import '../../../veilid_support.dart';
|
||||||
import '../../proto/proto.dart' as proto;
|
import '../../proto/proto.dart' as proto;
|
||||||
import '../interfaces/dht_append_truncate.dart';
|
import '../interfaces/dht_append.dart';
|
||||||
|
|
||||||
part 'dht_log_spine.dart';
|
part 'dht_log_spine.dart';
|
||||||
part 'dht_log_read.dart';
|
part 'dht_log_read.dart';
|
||||||
part 'dht_log_append.dart';
|
part 'dht_log_write.dart';
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ class DHTLog implements DHTDeleteable<DHTLog, DHTLog> {
|
|||||||
int stride = DHTShortArray.maxElements,
|
int stride = DHTShortArray.maxElements,
|
||||||
VeilidRoutingContext? routingContext,
|
VeilidRoutingContext? routingContext,
|
||||||
TypedKey? parent,
|
TypedKey? parent,
|
||||||
DHTRecordCrypto? crypto,
|
VeilidCrypto? crypto,
|
||||||
KeyPair? writer}) async {
|
KeyPair? writer}) async {
|
||||||
assert(stride <= DHTShortArray.maxElements, 'stride too long');
|
assert(stride <= DHTShortArray.maxElements, 'stride too long');
|
||||||
final pool = DHTRecordPool.instance;
|
final pool = DHTRecordPool.instance;
|
||||||
@ -102,7 +102,7 @@ class DHTLog implements DHTDeleteable<DHTLog, DHTLog> {
|
|||||||
{required String debugName,
|
{required String debugName,
|
||||||
VeilidRoutingContext? routingContext,
|
VeilidRoutingContext? routingContext,
|
||||||
TypedKey? parent,
|
TypedKey? parent,
|
||||||
DHTRecordCrypto? crypto}) async {
|
VeilidCrypto? crypto}) async {
|
||||||
final spineRecord = await DHTRecordPool.instance.openRecordRead(
|
final spineRecord = await DHTRecordPool.instance.openRecordRead(
|
||||||
logRecordKey,
|
logRecordKey,
|
||||||
debugName: debugName,
|
debugName: debugName,
|
||||||
@ -125,7 +125,7 @@ class DHTLog implements DHTDeleteable<DHTLog, DHTLog> {
|
|||||||
required String debugName,
|
required String debugName,
|
||||||
VeilidRoutingContext? routingContext,
|
VeilidRoutingContext? routingContext,
|
||||||
TypedKey? parent,
|
TypedKey? parent,
|
||||||
DHTRecordCrypto? crypto,
|
VeilidCrypto? crypto,
|
||||||
}) async {
|
}) async {
|
||||||
final spineRecord = await DHTRecordPool.instance.openRecordWrite(
|
final spineRecord = await DHTRecordPool.instance.openRecordWrite(
|
||||||
logRecordKey, writer,
|
logRecordKey, writer,
|
||||||
@ -148,7 +148,7 @@ class DHTLog implements DHTDeleteable<DHTLog, DHTLog> {
|
|||||||
required String debugName,
|
required String debugName,
|
||||||
required TypedKey parent,
|
required TypedKey parent,
|
||||||
VeilidRoutingContext? routingContext,
|
VeilidRoutingContext? routingContext,
|
||||||
DHTRecordCrypto? crypto,
|
VeilidCrypto? crypto,
|
||||||
}) =>
|
}) =>
|
||||||
openWrite(
|
openWrite(
|
||||||
ownedLogRecordPointer.recordKey,
|
ownedLogRecordPointer.recordKey,
|
||||||
@ -209,7 +209,8 @@ class DHTLog implements DHTDeleteable<DHTLog, DHTLog> {
|
|||||||
OwnedDHTRecordPointer get recordPointer => _spine.recordPointer;
|
OwnedDHTRecordPointer get recordPointer => _spine.recordPointer;
|
||||||
|
|
||||||
/// Runs a closure allowing read-only access to the log
|
/// Runs a closure allowing read-only access to the log
|
||||||
Future<T?> operate<T>(Future<T?> Function(DHTRandomRead) closure) async {
|
Future<T?> operate<T>(
|
||||||
|
Future<T?> Function(DHTLogReadOperations) closure) async {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
throw StateError('log is not open"');
|
throw StateError('log is not open"');
|
||||||
}
|
}
|
||||||
@ -226,13 +227,13 @@ class DHTLog implements DHTDeleteable<DHTLog, DHTLog> {
|
|||||||
/// Throws DHTOperateException if the write could not be performed
|
/// Throws DHTOperateException if the write could not be performed
|
||||||
/// at this time
|
/// at this time
|
||||||
Future<T> operateAppend<T>(
|
Future<T> operateAppend<T>(
|
||||||
Future<T> Function(DHTAppendTruncateRandomRead) closure) async {
|
Future<T> Function(DHTLogWriteOperations) closure) async {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
throw StateError('log is not open"');
|
throw StateError('log is not open"');
|
||||||
}
|
}
|
||||||
|
|
||||||
return _spine.operateAppend((spine) async {
|
return _spine.operateAppend((spine) async {
|
||||||
final writer = _DHTLogAppend._(spine);
|
final writer = _DHTLogWrite._(spine);
|
||||||
return closure(writer);
|
return closure(writer);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -244,14 +245,14 @@ class DHTLog implements DHTDeleteable<DHTLog, DHTLog> {
|
|||||||
/// succeeded, returning false will trigger another eventual consistency
|
/// succeeded, returning false will trigger another eventual consistency
|
||||||
/// attempt.
|
/// attempt.
|
||||||
Future<void> operateAppendEventual(
|
Future<void> operateAppendEventual(
|
||||||
Future<bool> Function(DHTAppendTruncateRandomRead) closure,
|
Future<bool> Function(DHTLogWriteOperations) closure,
|
||||||
{Duration? timeout}) async {
|
{Duration? timeout}) async {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
throw StateError('log is not open"');
|
throw StateError('log is not open"');
|
||||||
}
|
}
|
||||||
|
|
||||||
return _spine.operateAppendEventual((spine) async {
|
return _spine.operateAppendEventual((spine) async {
|
||||||
final writer = _DHTLogAppend._(spine);
|
final writer = _DHTLogWrite._(spine);
|
||||||
return closure(writer);
|
return closure(writer);
|
||||||
}, timeout: timeout);
|
}, timeout: timeout);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
|||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
import '../../../veilid_support.dart';
|
import '../../../veilid_support.dart';
|
||||||
import '../interfaces/dht_append_truncate.dart';
|
|
||||||
|
|
||||||
@immutable
|
@immutable
|
||||||
class DHTLogElementState<T> extends Equatable {
|
class DHTLogElementState<T> extends Equatable {
|
||||||
@ -184,19 +183,20 @@ class DHTLogCubit<T> extends Cubit<DHTLogBusyState<T>>
|
|||||||
await super.close();
|
await super.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<R?> operate<R>(Future<R?> Function(DHTRandomRead) closure) async {
|
Future<R?> operate<R>(
|
||||||
|
Future<R?> Function(DHTLogReadOperations) closure) async {
|
||||||
await _initWait();
|
await _initWait();
|
||||||
return _log.operate(closure);
|
return _log.operate(closure);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<R> operateAppend<R>(
|
Future<R> operateAppend<R>(
|
||||||
Future<R> Function(DHTAppendTruncateRandomRead) closure) async {
|
Future<R> Function(DHTLogWriteOperations) closure) async {
|
||||||
await _initWait();
|
await _initWait();
|
||||||
return _log.operateAppend(closure);
|
return _log.operateAppend(closure);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> operateAppendEventual(
|
Future<void> operateAppendEventual(
|
||||||
Future<bool> Function(DHTAppendTruncateRandomRead) closure,
|
Future<bool> Function(DHTLogWriteOperations) closure,
|
||||||
{Duration? timeout}) async {
|
{Duration? timeout}) async {
|
||||||
await _initWait();
|
await _initWait();
|
||||||
return _log.operateAppendEventual(closure, timeout: timeout);
|
return _log.operateAppendEventual(closure, timeout: timeout);
|
||||||
|
@ -3,7 +3,9 @@ part of 'dht_log.dart';
|
|||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
// Reader-only implementation
|
// Reader-only implementation
|
||||||
|
|
||||||
class _DHTLogRead implements DHTRandomRead {
|
abstract class DHTLogReadOperations implements DHTRandomRead {}
|
||||||
|
|
||||||
|
class _DHTLogRead implements DHTLogReadOperations {
|
||||||
_DHTLogRead._(_DHTLogSpine spine) : _spine = spine;
|
_DHTLogRead._(_DHTLogSpine spine) : _spine = spine;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -1,13 +1,32 @@
|
|||||||
part of 'dht_log.dart';
|
part of 'dht_log.dart';
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
// Append/truncate implementation
|
// Writer implementation
|
||||||
|
|
||||||
class _DHTLogAppend extends _DHTLogRead implements DHTAppendTruncateRandomRead {
|
abstract class DHTLogWriteOperations
|
||||||
_DHTLogAppend._(super.spine) : super._();
|
implements DHTRandomRead, DHTRandomWrite, DHTAdd, DHTTruncate, DHTClear {}
|
||||||
|
|
||||||
|
class _DHTLogWrite extends _DHTLogRead implements DHTLogWriteOperations {
|
||||||
|
_DHTLogWrite._(super.spine) : super._();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> tryAppendItem(Uint8List value) async {
|
Future<bool> tryWriteItem(int pos, Uint8List newValue,
|
||||||
|
{Output<Uint8List>? output}) async {
|
||||||
|
if (pos < 0 || pos >= _spine.length) {
|
||||||
|
throw IndexError.withLength(pos, _spine.length);
|
||||||
|
}
|
||||||
|
final lookup = await _spine.lookupPosition(pos);
|
||||||
|
if (lookup == null) {
|
||||||
|
throw StateError("can't write to dht log");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write item to the segment
|
||||||
|
return lookup.scope((sa) => sa.operateWrite((write) async =>
|
||||||
|
write.tryWriteItem(lookup.pos, newValue, output: output)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> tryAddItem(Uint8List value) async {
|
||||||
// Allocate empty index at the end of the list
|
// Allocate empty index at the end of the list
|
||||||
final insertPos = _spine.length;
|
final insertPos = _spine.length;
|
||||||
_spine.allocateTail(1);
|
_spine.allocateTail(1);
|
||||||
@ -30,7 +49,7 @@ class _DHTLogAppend extends _DHTLogRead implements DHTAppendTruncateRandomRead {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> tryAppendItems(List<Uint8List> values) async {
|
Future<bool> tryAddItems(List<Uint8List> values) async {
|
||||||
// Allocate empty index at the end of the list
|
// Allocate empty index at the end of the list
|
||||||
final insertPos = _spine.length;
|
final insertPos = _spine.length;
|
||||||
_spine.allocateTail(values.length);
|
_spine.allocateTail(values.length);
|
||||||
@ -76,15 +95,14 @@ class _DHTLogAppend extends _DHTLogRead implements DHTAppendTruncateRandomRead {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> truncate(int count) async {
|
Future<void> truncate(int newLength) async {
|
||||||
count = min(count, _spine.length);
|
if (newLength < 0) {
|
||||||
if (count == 0) {
|
throw StateError('can not truncate to negative length');
|
||||||
|
}
|
||||||
|
if (newLength >= _spine.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (count < 0) {
|
await _spine.releaseHead(_spine.length - newLength);
|
||||||
throw StateError('can not remove negative items');
|
|
||||||
}
|
|
||||||
await _spine.releaseHead(count);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
@ -1,4 +1,3 @@
|
|||||||
export 'default_dht_record_cubit.dart';
|
export 'default_dht_record_cubit.dart';
|
||||||
export 'dht_record_crypto.dart';
|
|
||||||
export 'dht_record_cubit.dart';
|
export 'dht_record_cubit.dart';
|
||||||
export 'dht_record_pool.dart';
|
export 'dht_record_pool.dart';
|
||||||
|
@ -42,7 +42,7 @@ class DHTRecord implements DHTDeleteable<DHTRecord, DHTRecord> {
|
|||||||
required SharedDHTRecordData sharedDHTRecordData,
|
required SharedDHTRecordData sharedDHTRecordData,
|
||||||
required int defaultSubkey,
|
required int defaultSubkey,
|
||||||
required KeyPair? writer,
|
required KeyPair? writer,
|
||||||
required DHTRecordCrypto crypto,
|
required VeilidCrypto crypto,
|
||||||
required this.debugName})
|
required this.debugName})
|
||||||
: _crypto = crypto,
|
: _crypto = crypto,
|
||||||
_routingContext = routingContext,
|
_routingContext = routingContext,
|
||||||
@ -104,7 +104,7 @@ class DHTRecord implements DHTDeleteable<DHTRecord, DHTRecord> {
|
|||||||
int get subkeyCount =>
|
int get subkeyCount =>
|
||||||
_sharedDHTRecordData.recordDescriptor.schema.subkeyCount();
|
_sharedDHTRecordData.recordDescriptor.schema.subkeyCount();
|
||||||
KeyPair? get writer => _writer;
|
KeyPair? get writer => _writer;
|
||||||
DHTRecordCrypto get crypto => _crypto;
|
VeilidCrypto get crypto => _crypto;
|
||||||
OwnedDHTRecordPointer get ownedDHTRecordPointer =>
|
OwnedDHTRecordPointer get ownedDHTRecordPointer =>
|
||||||
OwnedDHTRecordPointer(recordKey: key, owner: ownerKeyPair!);
|
OwnedDHTRecordPointer(recordKey: key, owner: ownerKeyPair!);
|
||||||
int subkeyOrDefault(int subkey) => (subkey == -1) ? _defaultSubkey : subkey;
|
int subkeyOrDefault(int subkey) => (subkey == -1) ? _defaultSubkey : subkey;
|
||||||
@ -118,7 +118,7 @@ class DHTRecord implements DHTDeleteable<DHTRecord, DHTRecord> {
|
|||||||
/// returned if one was returned.
|
/// returned if one was returned.
|
||||||
Future<Uint8List?> get(
|
Future<Uint8List?> get(
|
||||||
{int subkey = -1,
|
{int subkey = -1,
|
||||||
DHTRecordCrypto? crypto,
|
VeilidCrypto? crypto,
|
||||||
DHTRecordRefreshMode refreshMode = DHTRecordRefreshMode.cached,
|
DHTRecordRefreshMode refreshMode = DHTRecordRefreshMode.cached,
|
||||||
Output<int>? outSeqNum}) async {
|
Output<int>? outSeqNum}) async {
|
||||||
subkey = subkeyOrDefault(subkey);
|
subkey = subkeyOrDefault(subkey);
|
||||||
@ -146,7 +146,7 @@ class DHTRecord implements DHTDeleteable<DHTRecord, DHTRecord> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// If we're returning a value, decrypt it
|
// If we're returning a value, decrypt it
|
||||||
final out = (crypto ?? _crypto).decrypt(valueData.data, subkey);
|
final out = (crypto ?? _crypto).decrypt(valueData.data);
|
||||||
if (outSeqNum != null) {
|
if (outSeqNum != null) {
|
||||||
outSeqNum.save(valueData.seq);
|
outSeqNum.save(valueData.seq);
|
||||||
}
|
}
|
||||||
@ -163,7 +163,7 @@ class DHTRecord implements DHTDeleteable<DHTRecord, DHTRecord> {
|
|||||||
/// returned if one was returned.
|
/// returned if one was returned.
|
||||||
Future<T?> getJson<T>(T Function(dynamic) fromJson,
|
Future<T?> getJson<T>(T Function(dynamic) fromJson,
|
||||||
{int subkey = -1,
|
{int subkey = -1,
|
||||||
DHTRecordCrypto? crypto,
|
VeilidCrypto? crypto,
|
||||||
DHTRecordRefreshMode refreshMode = DHTRecordRefreshMode.cached,
|
DHTRecordRefreshMode refreshMode = DHTRecordRefreshMode.cached,
|
||||||
Output<int>? outSeqNum}) async {
|
Output<int>? outSeqNum}) async {
|
||||||
final data = await get(
|
final data = await get(
|
||||||
@ -189,7 +189,7 @@ class DHTRecord implements DHTDeleteable<DHTRecord, DHTRecord> {
|
|||||||
Future<T?> getProtobuf<T extends GeneratedMessage>(
|
Future<T?> getProtobuf<T extends GeneratedMessage>(
|
||||||
T Function(List<int> i) fromBuffer,
|
T Function(List<int> i) fromBuffer,
|
||||||
{int subkey = -1,
|
{int subkey = -1,
|
||||||
DHTRecordCrypto? crypto,
|
VeilidCrypto? crypto,
|
||||||
DHTRecordRefreshMode refreshMode = DHTRecordRefreshMode.cached,
|
DHTRecordRefreshMode refreshMode = DHTRecordRefreshMode.cached,
|
||||||
Output<int>? outSeqNum}) async {
|
Output<int>? outSeqNum}) async {
|
||||||
final data = await get(
|
final data = await get(
|
||||||
@ -208,13 +208,12 @@ class DHTRecord implements DHTDeleteable<DHTRecord, DHTRecord> {
|
|||||||
/// If the value was succesfully written, null is returned
|
/// If the value was succesfully written, null is returned
|
||||||
Future<Uint8List?> tryWriteBytes(Uint8List newValue,
|
Future<Uint8List?> tryWriteBytes(Uint8List newValue,
|
||||||
{int subkey = -1,
|
{int subkey = -1,
|
||||||
DHTRecordCrypto? crypto,
|
VeilidCrypto? crypto,
|
||||||
KeyPair? writer,
|
KeyPair? writer,
|
||||||
Output<int>? outSeqNum}) async {
|
Output<int>? outSeqNum}) async {
|
||||||
subkey = subkeyOrDefault(subkey);
|
subkey = subkeyOrDefault(subkey);
|
||||||
final lastSeq = await _localSubkeySeq(subkey);
|
final lastSeq = await _localSubkeySeq(subkey);
|
||||||
final encryptedNewValue =
|
final encryptedNewValue = await (crypto ?? _crypto).encrypt(newValue);
|
||||||
await (crypto ?? _crypto).encrypt(newValue, subkey);
|
|
||||||
|
|
||||||
// Set the new data if possible
|
// Set the new data if possible
|
||||||
var newValueData = await _routingContext
|
var newValueData = await _routingContext
|
||||||
@ -246,7 +245,7 @@ class DHTRecord implements DHTDeleteable<DHTRecord, DHTRecord> {
|
|||||||
|
|
||||||
// Decrypt value to return it
|
// Decrypt value to return it
|
||||||
final decryptedNewValue =
|
final decryptedNewValue =
|
||||||
await (crypto ?? _crypto).decrypt(newValueData.data, subkey);
|
await (crypto ?? _crypto).decrypt(newValueData.data);
|
||||||
if (isUpdated) {
|
if (isUpdated) {
|
||||||
DHTRecordPool.instance
|
DHTRecordPool.instance
|
||||||
.processLocalValueChange(key, decryptedNewValue, subkey);
|
.processLocalValueChange(key, decryptedNewValue, subkey);
|
||||||
@ -259,13 +258,12 @@ class DHTRecord implements DHTDeleteable<DHTRecord, DHTRecord> {
|
|||||||
/// will be made to write the subkey until this succeeds
|
/// will be made to write the subkey until this succeeds
|
||||||
Future<void> eventualWriteBytes(Uint8List newValue,
|
Future<void> eventualWriteBytes(Uint8List newValue,
|
||||||
{int subkey = -1,
|
{int subkey = -1,
|
||||||
DHTRecordCrypto? crypto,
|
VeilidCrypto? crypto,
|
||||||
KeyPair? writer,
|
KeyPair? writer,
|
||||||
Output<int>? outSeqNum}) async {
|
Output<int>? outSeqNum}) async {
|
||||||
subkey = subkeyOrDefault(subkey);
|
subkey = subkeyOrDefault(subkey);
|
||||||
final lastSeq = await _localSubkeySeq(subkey);
|
final lastSeq = await _localSubkeySeq(subkey);
|
||||||
final encryptedNewValue =
|
final encryptedNewValue = await (crypto ?? _crypto).encrypt(newValue);
|
||||||
await (crypto ?? _crypto).encrypt(newValue, subkey);
|
|
||||||
|
|
||||||
ValueData? newValueData;
|
ValueData? newValueData;
|
||||||
do {
|
do {
|
||||||
@ -309,7 +307,7 @@ class DHTRecord implements DHTDeleteable<DHTRecord, DHTRecord> {
|
|||||||
Future<void> eventualUpdateBytes(
|
Future<void> eventualUpdateBytes(
|
||||||
Future<Uint8List> Function(Uint8List? oldValue) update,
|
Future<Uint8List> Function(Uint8List? oldValue) update,
|
||||||
{int subkey = -1,
|
{int subkey = -1,
|
||||||
DHTRecordCrypto? crypto,
|
VeilidCrypto? crypto,
|
||||||
KeyPair? writer,
|
KeyPair? writer,
|
||||||
Output<int>? outSeqNum}) async {
|
Output<int>? outSeqNum}) async {
|
||||||
subkey = subkeyOrDefault(subkey);
|
subkey = subkeyOrDefault(subkey);
|
||||||
@ -334,7 +332,7 @@ class DHTRecord implements DHTDeleteable<DHTRecord, DHTRecord> {
|
|||||||
/// Like 'tryWriteBytes' but with JSON marshal/unmarshal of the value
|
/// Like 'tryWriteBytes' but with JSON marshal/unmarshal of the value
|
||||||
Future<T?> tryWriteJson<T>(T Function(dynamic) fromJson, T newValue,
|
Future<T?> tryWriteJson<T>(T Function(dynamic) fromJson, T newValue,
|
||||||
{int subkey = -1,
|
{int subkey = -1,
|
||||||
DHTRecordCrypto? crypto,
|
VeilidCrypto? crypto,
|
||||||
KeyPair? writer,
|
KeyPair? writer,
|
||||||
Output<int>? outSeqNum}) =>
|
Output<int>? outSeqNum}) =>
|
||||||
tryWriteBytes(jsonEncodeBytes(newValue),
|
tryWriteBytes(jsonEncodeBytes(newValue),
|
||||||
@ -353,7 +351,7 @@ class DHTRecord implements DHTDeleteable<DHTRecord, DHTRecord> {
|
|||||||
Future<T?> tryWriteProtobuf<T extends GeneratedMessage>(
|
Future<T?> tryWriteProtobuf<T extends GeneratedMessage>(
|
||||||
T Function(List<int>) fromBuffer, T newValue,
|
T Function(List<int>) fromBuffer, T newValue,
|
||||||
{int subkey = -1,
|
{int subkey = -1,
|
||||||
DHTRecordCrypto? crypto,
|
VeilidCrypto? crypto,
|
||||||
KeyPair? writer,
|
KeyPair? writer,
|
||||||
Output<int>? outSeqNum}) =>
|
Output<int>? outSeqNum}) =>
|
||||||
tryWriteBytes(newValue.writeToBuffer(),
|
tryWriteBytes(newValue.writeToBuffer(),
|
||||||
@ -371,7 +369,7 @@ class DHTRecord implements DHTDeleteable<DHTRecord, DHTRecord> {
|
|||||||
/// Like 'eventualWriteBytes' but with JSON marshal/unmarshal of the value
|
/// Like 'eventualWriteBytes' but with JSON marshal/unmarshal of the value
|
||||||
Future<void> eventualWriteJson<T>(T newValue,
|
Future<void> eventualWriteJson<T>(T newValue,
|
||||||
{int subkey = -1,
|
{int subkey = -1,
|
||||||
DHTRecordCrypto? crypto,
|
VeilidCrypto? crypto,
|
||||||
KeyPair? writer,
|
KeyPair? writer,
|
||||||
Output<int>? outSeqNum}) =>
|
Output<int>? outSeqNum}) =>
|
||||||
eventualWriteBytes(jsonEncodeBytes(newValue),
|
eventualWriteBytes(jsonEncodeBytes(newValue),
|
||||||
@ -380,7 +378,7 @@ class DHTRecord implements DHTDeleteable<DHTRecord, DHTRecord> {
|
|||||||
/// Like 'eventualWriteBytes' but with protobuf marshal/unmarshal of the value
|
/// Like 'eventualWriteBytes' but with protobuf marshal/unmarshal of the value
|
||||||
Future<void> eventualWriteProtobuf<T extends GeneratedMessage>(T newValue,
|
Future<void> eventualWriteProtobuf<T extends GeneratedMessage>(T newValue,
|
||||||
{int subkey = -1,
|
{int subkey = -1,
|
||||||
DHTRecordCrypto? crypto,
|
VeilidCrypto? crypto,
|
||||||
KeyPair? writer,
|
KeyPair? writer,
|
||||||
Output<int>? outSeqNum}) =>
|
Output<int>? outSeqNum}) =>
|
||||||
eventualWriteBytes(newValue.writeToBuffer(),
|
eventualWriteBytes(newValue.writeToBuffer(),
|
||||||
@ -390,7 +388,7 @@ class DHTRecord implements DHTDeleteable<DHTRecord, DHTRecord> {
|
|||||||
Future<void> eventualUpdateJson<T>(
|
Future<void> eventualUpdateJson<T>(
|
||||||
T Function(dynamic) fromJson, Future<T> Function(T?) update,
|
T Function(dynamic) fromJson, Future<T> Function(T?) update,
|
||||||
{int subkey = -1,
|
{int subkey = -1,
|
||||||
DHTRecordCrypto? crypto,
|
VeilidCrypto? crypto,
|
||||||
KeyPair? writer,
|
KeyPair? writer,
|
||||||
Output<int>? outSeqNum}) =>
|
Output<int>? outSeqNum}) =>
|
||||||
eventualUpdateBytes(jsonUpdate(fromJson, update),
|
eventualUpdateBytes(jsonUpdate(fromJson, update),
|
||||||
@ -400,7 +398,7 @@ class DHTRecord implements DHTDeleteable<DHTRecord, DHTRecord> {
|
|||||||
Future<void> eventualUpdateProtobuf<T extends GeneratedMessage>(
|
Future<void> eventualUpdateProtobuf<T extends GeneratedMessage>(
|
||||||
T Function(List<int>) fromBuffer, Future<T> Function(T?) update,
|
T Function(List<int>) fromBuffer, Future<T> Function(T?) update,
|
||||||
{int subkey = -1,
|
{int subkey = -1,
|
||||||
DHTRecordCrypto? crypto,
|
VeilidCrypto? crypto,
|
||||||
KeyPair? writer,
|
KeyPair? writer,
|
||||||
Output<int>? outSeqNum}) =>
|
Output<int>? outSeqNum}) =>
|
||||||
eventualUpdateBytes(protobufUpdate(fromBuffer, update),
|
eventualUpdateBytes(protobufUpdate(fromBuffer, update),
|
||||||
@ -433,7 +431,7 @@ class DHTRecord implements DHTDeleteable<DHTRecord, DHTRecord> {
|
|||||||
DHTRecord record, Uint8List? data, List<ValueSubkeyRange> subkeys)
|
DHTRecord record, Uint8List? data, List<ValueSubkeyRange> subkeys)
|
||||||
onUpdate, {
|
onUpdate, {
|
||||||
bool localChanges = true,
|
bool localChanges = true,
|
||||||
DHTRecordCrypto? crypto,
|
VeilidCrypto? crypto,
|
||||||
}) async {
|
}) async {
|
||||||
// Set up watch requirements
|
// Set up watch requirements
|
||||||
_watchController ??=
|
_watchController ??=
|
||||||
@ -457,8 +455,7 @@ class DHTRecord implements DHTDeleteable<DHTRecord, DHTRecord> {
|
|||||||
final changeData = change.data;
|
final changeData = change.data;
|
||||||
data = changeData == null
|
data = changeData == null
|
||||||
? null
|
? null
|
||||||
: await (crypto ?? _crypto)
|
: await (crypto ?? _crypto).decrypt(changeData);
|
||||||
.decrypt(changeData, change.subkeys.first.low);
|
|
||||||
}
|
}
|
||||||
await onUpdate(this, data, change.subkeys);
|
await onUpdate(this, data, change.subkeys);
|
||||||
});
|
});
|
||||||
@ -544,7 +541,7 @@ class DHTRecord implements DHTDeleteable<DHTRecord, DHTRecord> {
|
|||||||
final VeilidRoutingContext _routingContext;
|
final VeilidRoutingContext _routingContext;
|
||||||
final int _defaultSubkey;
|
final int _defaultSubkey;
|
||||||
final KeyPair? _writer;
|
final KeyPair? _writer;
|
||||||
final DHTRecordCrypto _crypto;
|
final VeilidCrypto _crypto;
|
||||||
final String debugName;
|
final String debugName;
|
||||||
final _mutex = Mutex();
|
final _mutex = Mutex();
|
||||||
int _openCount;
|
int _openCount;
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:typed_data';
|
|
||||||
import '../../../../../veilid_support.dart';
|
|
||||||
|
|
||||||
abstract class DHTRecordCrypto {
|
|
||||||
Future<Uint8List> encrypt(Uint8List data, int subkey);
|
|
||||||
Future<Uint8List> decrypt(Uint8List data, int subkey);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////
|
|
||||||
/// Private DHT Record: Encrypted for a specific symmetric key
|
|
||||||
class DHTRecordCryptoPrivate implements DHTRecordCrypto {
|
|
||||||
DHTRecordCryptoPrivate._(
|
|
||||||
VeilidCryptoSystem cryptoSystem, SharedSecret secretKey)
|
|
||||||
: _cryptoSystem = cryptoSystem,
|
|
||||||
_secretKey = secretKey;
|
|
||||||
final VeilidCryptoSystem _cryptoSystem;
|
|
||||||
final SharedSecret _secretKey;
|
|
||||||
|
|
||||||
static Future<DHTRecordCryptoPrivate> fromTypedKeyPair(
|
|
||||||
TypedKeyPair typedKeyPair) async {
|
|
||||||
final cryptoSystem =
|
|
||||||
await Veilid.instance.getCryptoSystem(typedKeyPair.kind);
|
|
||||||
final secretKey = typedKeyPair.secret;
|
|
||||||
return DHTRecordCryptoPrivate._(cryptoSystem, secretKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<DHTRecordCryptoPrivate> fromSecret(
|
|
||||||
CryptoKind kind, SharedSecret secretKey) async {
|
|
||||||
final cryptoSystem = await Veilid.instance.getCryptoSystem(kind);
|
|
||||||
return DHTRecordCryptoPrivate._(cryptoSystem, secretKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Uint8List> encrypt(Uint8List data, int subkey) =>
|
|
||||||
_cryptoSystem.encryptNoAuthWithNonce(data, _secretKey);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Uint8List> decrypt(Uint8List data, int subkey) =>
|
|
||||||
_cryptoSystem.decryptNoAuthWithNonce(data, _secretKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////
|
|
||||||
/// Public DHT Record: No encryption
|
|
||||||
class DHTRecordCryptoPublic implements DHTRecordCrypto {
|
|
||||||
const DHTRecordCryptoPublic();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Uint8List> encrypt(Uint8List data, int subkey) async => data;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<Uint8List> decrypt(Uint8List data, int subkey) async => data;
|
|
||||||
}
|
|
@ -526,7 +526,7 @@ class DHTRecordPool with TableDBBackedJson<DHTRecordPoolAllocations> {
|
|||||||
TypedKey? parent,
|
TypedKey? parent,
|
||||||
DHTSchema schema = const DHTSchema.dflt(oCnt: 1),
|
DHTSchema schema = const DHTSchema.dflt(oCnt: 1),
|
||||||
int defaultSubkey = 0,
|
int defaultSubkey = 0,
|
||||||
DHTRecordCrypto? crypto,
|
VeilidCrypto? crypto,
|
||||||
KeyPair? writer,
|
KeyPair? writer,
|
||||||
}) async =>
|
}) async =>
|
||||||
_mutex.protect(() async {
|
_mutex.protect(() async {
|
||||||
@ -547,7 +547,7 @@ class DHTRecordPool with TableDBBackedJson<DHTRecordPoolAllocations> {
|
|||||||
writer: writer ??
|
writer: writer ??
|
||||||
openedRecordInfo.shared.recordDescriptor.ownerKeyPair(),
|
openedRecordInfo.shared.recordDescriptor.ownerKeyPair(),
|
||||||
crypto: crypto ??
|
crypto: crypto ??
|
||||||
await DHTRecordCryptoPrivate.fromTypedKeyPair(openedRecordInfo
|
await VeilidCryptoPrivate.fromTypedKeyPair(openedRecordInfo
|
||||||
.shared.recordDescriptor
|
.shared.recordDescriptor
|
||||||
.ownerTypedKeyPair()!));
|
.ownerTypedKeyPair()!));
|
||||||
|
|
||||||
@ -562,7 +562,7 @@ class DHTRecordPool with TableDBBackedJson<DHTRecordPoolAllocations> {
|
|||||||
VeilidRoutingContext? routingContext,
|
VeilidRoutingContext? routingContext,
|
||||||
TypedKey? parent,
|
TypedKey? parent,
|
||||||
int defaultSubkey = 0,
|
int defaultSubkey = 0,
|
||||||
DHTRecordCrypto? crypto}) async =>
|
VeilidCrypto? crypto}) async =>
|
||||||
_mutex.protect(() async {
|
_mutex.protect(() async {
|
||||||
final dhtctx = routingContext ?? _routingContext;
|
final dhtctx = routingContext ?? _routingContext;
|
||||||
|
|
||||||
@ -578,7 +578,7 @@ class DHTRecordPool with TableDBBackedJson<DHTRecordPoolAllocations> {
|
|||||||
defaultSubkey: defaultSubkey,
|
defaultSubkey: defaultSubkey,
|
||||||
sharedDHTRecordData: openedRecordInfo.shared,
|
sharedDHTRecordData: openedRecordInfo.shared,
|
||||||
writer: null,
|
writer: null,
|
||||||
crypto: crypto ?? const DHTRecordCryptoPublic());
|
crypto: crypto ?? const VeilidCryptoPublic());
|
||||||
|
|
||||||
openedRecordInfo.records.add(rec);
|
openedRecordInfo.records.add(rec);
|
||||||
|
|
||||||
@ -593,7 +593,7 @@ class DHTRecordPool with TableDBBackedJson<DHTRecordPoolAllocations> {
|
|||||||
VeilidRoutingContext? routingContext,
|
VeilidRoutingContext? routingContext,
|
||||||
TypedKey? parent,
|
TypedKey? parent,
|
||||||
int defaultSubkey = 0,
|
int defaultSubkey = 0,
|
||||||
DHTRecordCrypto? crypto,
|
VeilidCrypto? crypto,
|
||||||
}) async =>
|
}) async =>
|
||||||
_mutex.protect(() async {
|
_mutex.protect(() async {
|
||||||
final dhtctx = routingContext ?? _routingContext;
|
final dhtctx = routingContext ?? _routingContext;
|
||||||
@ -612,7 +612,7 @@ class DHTRecordPool with TableDBBackedJson<DHTRecordPoolAllocations> {
|
|||||||
writer: writer,
|
writer: writer,
|
||||||
sharedDHTRecordData: openedRecordInfo.shared,
|
sharedDHTRecordData: openedRecordInfo.shared,
|
||||||
crypto: crypto ??
|
crypto: crypto ??
|
||||||
await DHTRecordCryptoPrivate.fromTypedKeyPair(
|
await VeilidCryptoPrivate.fromTypedKeyPair(
|
||||||
TypedKeyPair.fromKeyPair(recordKey.kind, writer)));
|
TypedKeyPair.fromKeyPair(recordKey.kind, writer)));
|
||||||
|
|
||||||
openedRecordInfo.records.add(rec);
|
openedRecordInfo.records.add(rec);
|
||||||
@ -632,7 +632,7 @@ class DHTRecordPool with TableDBBackedJson<DHTRecordPoolAllocations> {
|
|||||||
required TypedKey parent,
|
required TypedKey parent,
|
||||||
VeilidRoutingContext? routingContext,
|
VeilidRoutingContext? routingContext,
|
||||||
int defaultSubkey = 0,
|
int defaultSubkey = 0,
|
||||||
DHTRecordCrypto? crypto,
|
VeilidCrypto? crypto,
|
||||||
}) =>
|
}) =>
|
||||||
openRecordWrite(
|
openRecordWrite(
|
||||||
ownedDHTRecordPointer.recordKey,
|
ownedDHTRecordPointer.recordKey,
|
||||||
|
@ -33,7 +33,7 @@ class DHTShortArray implements DHTDeleteable<DHTShortArray, DHTShortArray> {
|
|||||||
int stride = maxElements,
|
int stride = maxElements,
|
||||||
VeilidRoutingContext? routingContext,
|
VeilidRoutingContext? routingContext,
|
||||||
TypedKey? parent,
|
TypedKey? parent,
|
||||||
DHTRecordCrypto? crypto,
|
VeilidCrypto? crypto,
|
||||||
KeyPair? writer}) async {
|
KeyPair? writer}) async {
|
||||||
assert(stride <= maxElements, 'stride too long');
|
assert(stride <= maxElements, 'stride too long');
|
||||||
final pool = DHTRecordPool.instance;
|
final pool = DHTRecordPool.instance;
|
||||||
@ -79,7 +79,7 @@ class DHTShortArray implements DHTDeleteable<DHTShortArray, DHTShortArray> {
|
|||||||
{required String debugName,
|
{required String debugName,
|
||||||
VeilidRoutingContext? routingContext,
|
VeilidRoutingContext? routingContext,
|
||||||
TypedKey? parent,
|
TypedKey? parent,
|
||||||
DHTRecordCrypto? crypto}) async {
|
VeilidCrypto? crypto}) async {
|
||||||
final dhtRecord = await DHTRecordPool.instance.openRecordRead(headRecordKey,
|
final dhtRecord = await DHTRecordPool.instance.openRecordRead(headRecordKey,
|
||||||
debugName: debugName,
|
debugName: debugName,
|
||||||
parent: parent,
|
parent: parent,
|
||||||
@ -101,7 +101,7 @@ class DHTShortArray implements DHTDeleteable<DHTShortArray, DHTShortArray> {
|
|||||||
required String debugName,
|
required String debugName,
|
||||||
VeilidRoutingContext? routingContext,
|
VeilidRoutingContext? routingContext,
|
||||||
TypedKey? parent,
|
TypedKey? parent,
|
||||||
DHTRecordCrypto? crypto,
|
VeilidCrypto? crypto,
|
||||||
}) async {
|
}) async {
|
||||||
final dhtRecord = await DHTRecordPool.instance.openRecordWrite(
|
final dhtRecord = await DHTRecordPool.instance.openRecordWrite(
|
||||||
headRecordKey, writer,
|
headRecordKey, writer,
|
||||||
@ -124,7 +124,7 @@ class DHTShortArray implements DHTDeleteable<DHTShortArray, DHTShortArray> {
|
|||||||
required String debugName,
|
required String debugName,
|
||||||
required TypedKey parent,
|
required TypedKey parent,
|
||||||
VeilidRoutingContext? routingContext,
|
VeilidRoutingContext? routingContext,
|
||||||
DHTRecordCrypto? crypto,
|
VeilidCrypto? crypto,
|
||||||
}) =>
|
}) =>
|
||||||
openWrite(
|
openWrite(
|
||||||
ownedShortArrayRecordPointer.recordKey,
|
ownedShortArrayRecordPointer.recordKey,
|
||||||
@ -186,7 +186,8 @@ class DHTShortArray implements DHTDeleteable<DHTShortArray, DHTShortArray> {
|
|||||||
OwnedDHTRecordPointer get recordPointer => _head.recordPointer;
|
OwnedDHTRecordPointer get recordPointer => _head.recordPointer;
|
||||||
|
|
||||||
/// Runs a closure allowing read-only access to the shortarray
|
/// Runs a closure allowing read-only access to the shortarray
|
||||||
Future<T> operate<T>(Future<T> Function(DHTRandomRead) closure) async {
|
Future<T> operate<T>(
|
||||||
|
Future<T> Function(DHTShortArrayReadOperations) closure) async {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
throw StateError('short array is not open"');
|
throw StateError('short array is not open"');
|
||||||
}
|
}
|
||||||
@ -203,7 +204,7 @@ class DHTShortArray implements DHTDeleteable<DHTShortArray, DHTShortArray> {
|
|||||||
/// Throws DHTOperateException if the write could not be performed
|
/// Throws DHTOperateException if the write could not be performed
|
||||||
/// at this time
|
/// at this time
|
||||||
Future<T> operateWrite<T>(
|
Future<T> operateWrite<T>(
|
||||||
Future<T> Function(DHTRandomReadWrite) closure) async {
|
Future<T> Function(DHTShortArrayWriteOperations) closure) async {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
throw StateError('short array is not open"');
|
throw StateError('short array is not open"');
|
||||||
}
|
}
|
||||||
@ -221,7 +222,7 @@ class DHTShortArray implements DHTDeleteable<DHTShortArray, DHTShortArray> {
|
|||||||
/// succeeded, returning false will trigger another eventual consistency
|
/// succeeded, returning false will trigger another eventual consistency
|
||||||
/// attempt.
|
/// attempt.
|
||||||
Future<void> operateWriteEventual(
|
Future<void> operateWriteEventual(
|
||||||
Future<bool> Function(DHTRandomReadWrite) closure,
|
Future<bool> Function(DHTShortArrayWriteOperations) closure,
|
||||||
{Duration? timeout}) async {
|
{Duration? timeout}) async {
|
||||||
if (!isOpen) {
|
if (!isOpen) {
|
||||||
throw StateError('short array is not open"');
|
throw StateError('short array is not open"');
|
||||||
|
@ -91,19 +91,20 @@ class DHTShortArrayCubit<T> extends Cubit<DHTShortArrayBusyState<T>>
|
|||||||
await super.close();
|
await super.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<R> operate<R>(Future<R> Function(DHTRandomRead) closure) async {
|
Future<R> operate<R>(
|
||||||
|
Future<R> Function(DHTShortArrayReadOperations) closure) async {
|
||||||
await _initWait();
|
await _initWait();
|
||||||
return _shortArray.operate(closure);
|
return _shortArray.operate(closure);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<R> operateWrite<R>(
|
Future<R> operateWrite<R>(
|
||||||
Future<R> Function(DHTRandomReadWrite) closure) async {
|
Future<R> Function(DHTShortArrayWriteOperations) closure) async {
|
||||||
await _initWait();
|
await _initWait();
|
||||||
return _shortArray.operateWrite(closure);
|
return _shortArray.operateWrite(closure);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> operateWriteEventual(
|
Future<void> operateWriteEventual(
|
||||||
Future<bool> Function(DHTRandomReadWrite) closure,
|
Future<bool> Function(DHTShortArrayWriteOperations) closure,
|
||||||
{Duration? timeout}) async {
|
{Duration? timeout}) async {
|
||||||
await _initWait();
|
await _initWait();
|
||||||
return _shortArray.operateWriteEventual(closure, timeout: timeout);
|
return _shortArray.operateWriteEventual(closure, timeout: timeout);
|
||||||
|
@ -3,7 +3,9 @@ part of 'dht_short_array.dart';
|
|||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
// Reader-only implementation
|
// Reader-only implementation
|
||||||
|
|
||||||
class _DHTShortArrayRead implements DHTRandomRead {
|
abstract class DHTShortArrayReadOperations implements DHTRandomRead {}
|
||||||
|
|
||||||
|
class _DHTShortArrayRead implements DHTShortArrayReadOperations {
|
||||||
_DHTShortArrayRead._(_DHTShortArrayHead head) : _head = head;
|
_DHTShortArrayRead._(_DHTShortArrayHead head) : _head = head;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -3,8 +3,16 @@ part of 'dht_short_array.dart';
|
|||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
// Writer implementation
|
// Writer implementation
|
||||||
|
|
||||||
|
abstract class DHTShortArrayWriteOperations
|
||||||
|
implements
|
||||||
|
DHTRandomRead,
|
||||||
|
DHTRandomWrite,
|
||||||
|
DHTInsertRemove,
|
||||||
|
DHTAdd,
|
||||||
|
DHTClear {}
|
||||||
|
|
||||||
class _DHTShortArrayWrite extends _DHTShortArrayRead
|
class _DHTShortArrayWrite extends _DHTShortArrayRead
|
||||||
implements DHTRandomReadWrite {
|
implements DHTShortArrayWriteOperations {
|
||||||
_DHTShortArrayWrite._(super.head) : super._();
|
_DHTShortArrayWrite._(super.head) : super._();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:protobuf/protobuf.dart';
|
||||||
|
|
||||||
|
import '../../../veilid_support.dart';
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Add
|
||||||
|
abstract class DHTAdd {
|
||||||
|
/// Try to add an item to the DHT container.
|
||||||
|
/// Return true if the element was successfully added, and false if the state
|
||||||
|
/// changed before the element could be added or a newer value was found on
|
||||||
|
/// the network.
|
||||||
|
/// Throws a StateError if the container exceeds its maximum size.
|
||||||
|
Future<bool> tryAddItem(Uint8List value);
|
||||||
|
|
||||||
|
/// Try to add a list of items to the DHT container.
|
||||||
|
/// Return true if the elements were successfully added, and false if the
|
||||||
|
/// state changed before the element could be added or a newer value was found
|
||||||
|
/// on the network.
|
||||||
|
/// Throws a StateError if the container exceeds its maximum size.
|
||||||
|
Future<bool> tryAddItems(List<Uint8List> values);
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DHTAddExt on DHTAdd {
|
||||||
|
/// Convenience function:
|
||||||
|
/// Like tryAddItem but also encodes the input value as JSON and parses the
|
||||||
|
/// returned element as JSON
|
||||||
|
Future<bool> tryAppendItemJson<T>(
|
||||||
|
T newValue,
|
||||||
|
) =>
|
||||||
|
tryAddItem(jsonEncodeBytes(newValue));
|
||||||
|
|
||||||
|
/// Convenience function:
|
||||||
|
/// Like tryAddItem but also encodes the input value as a protobuf object
|
||||||
|
/// and parses the returned element as a protobuf object
|
||||||
|
Future<bool> tryAddItemProtobuf<T extends GeneratedMessage>(
|
||||||
|
T newValue,
|
||||||
|
) =>
|
||||||
|
tryAddItem(newValue.writeToBuffer());
|
||||||
|
}
|
@ -1,51 +0,0 @@
|
|||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:protobuf/protobuf.dart';
|
|
||||||
|
|
||||||
import '../../../veilid_support.dart';
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Append/truncate interface
|
|
||||||
abstract class DHTAppendTruncate {
|
|
||||||
/// Try to add an item to the end of the DHT data structure.
|
|
||||||
/// Return true if the element was successfully added, and false if the state
|
|
||||||
/// changed before the element could be added or a newer value was found on
|
|
||||||
/// the network.
|
|
||||||
/// This may throw an exception if the number elements added exceeds limits.
|
|
||||||
Future<bool> tryAppendItem(Uint8List value);
|
|
||||||
|
|
||||||
/// Try to add a list of items to the end of the DHT data structure.
|
|
||||||
/// Return true if the elements were successfully added, and false if the
|
|
||||||
/// state changed before the element could be added or a newer value was found
|
|
||||||
/// on the network.
|
|
||||||
/// This may throw an exception if the number elements added exceeds limits.
|
|
||||||
Future<bool> tryAppendItems(List<Uint8List> values);
|
|
||||||
|
|
||||||
/// Try to remove a number of items from the head of the DHT data structure.
|
|
||||||
/// Throws StateError if count < 0
|
|
||||||
Future<void> truncate(int count);
|
|
||||||
|
|
||||||
/// Remove all items in the DHT data structure.
|
|
||||||
Future<void> clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class DHTAppendTruncateRandomRead
|
|
||||||
implements DHTAppendTruncate, DHTRandomRead {}
|
|
||||||
|
|
||||||
extension DHTAppendTruncateExt on DHTAppendTruncate {
|
|
||||||
/// Convenience function:
|
|
||||||
/// Like tryAppendItem but also encodes the input value as JSON and parses the
|
|
||||||
/// returned element as JSON
|
|
||||||
Future<bool> tryAppendItemJson<T>(
|
|
||||||
T newValue,
|
|
||||||
) =>
|
|
||||||
tryAppendItem(jsonEncodeBytes(newValue));
|
|
||||||
|
|
||||||
/// Convenience function:
|
|
||||||
/// Like tryAppendItem but also encodes the input value as a protobuf object
|
|
||||||
/// and parses the returned element as a protobuf object
|
|
||||||
Future<bool> tryAppendItemProtobuf<T extends GeneratedMessage>(
|
|
||||||
T newValue,
|
|
||||||
) =>
|
|
||||||
tryAppendItem(newValue.writeToBuffer());
|
|
||||||
}
|
|
@ -0,0 +1,7 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Clear interface
|
||||||
|
// ignore: one_member_abstracts
|
||||||
|
abstract class DHTClear {
|
||||||
|
/// Remove all items in the DHT container.
|
||||||
|
Future<void> clear();
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:protobuf/protobuf.dart';
|
||||||
|
|
||||||
|
import '../../../veilid_support.dart';
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Insert/Remove interface
|
||||||
|
abstract class DHTInsertRemove {
|
||||||
|
/// Try to insert an item as position 'pos' of the DHT container.
|
||||||
|
/// Return true if the element was successfully inserted, and false if the
|
||||||
|
/// state changed before the element could be inserted or a newer value was
|
||||||
|
/// found on the network.
|
||||||
|
/// Throws an IndexError if the position removed exceeds the length of
|
||||||
|
/// the container.
|
||||||
|
/// Throws a StateError if the container exceeds its maximum size.
|
||||||
|
Future<bool> tryInsertItem(int pos, Uint8List value);
|
||||||
|
|
||||||
|
/// Try to insert items at position 'pos' of the DHT container.
|
||||||
|
/// Return true if the elements were successfully inserted, and false if the
|
||||||
|
/// state changed before the elements could be inserted or a newer value was
|
||||||
|
/// found on the network.
|
||||||
|
/// Throws an IndexError if the position removed exceeds the length of
|
||||||
|
/// the container.
|
||||||
|
/// Throws a StateError if the container exceeds its maximum size.
|
||||||
|
Future<bool> tryInsertItems(int pos, List<Uint8List> values);
|
||||||
|
|
||||||
|
/// Swap items at position 'aPos' and 'bPos' in the DHTArray.
|
||||||
|
/// Throws an IndexError if either of the positions swapped exceeds the length
|
||||||
|
/// of the container
|
||||||
|
Future<void> swapItem(int aPos, int bPos);
|
||||||
|
|
||||||
|
/// Remove an item at position 'pos' in the DHT container.
|
||||||
|
/// If the remove was successful this returns:
|
||||||
|
/// * outValue will return the prior contents of the element
|
||||||
|
/// Throws an IndexError if the position removed exceeds the length of
|
||||||
|
/// the container.
|
||||||
|
Future<void> removeItem(int pos, {Output<Uint8List>? output});
|
||||||
|
}
|
||||||
|
|
||||||
|
extension DHTInsertRemoveExt on DHTInsertRemove {
|
||||||
|
/// Convenience function:
|
||||||
|
/// Like removeItem but also parses the returned element as JSON
|
||||||
|
Future<void> removeItemJson<T>(T Function(dynamic) fromJson, int pos,
|
||||||
|
{Output<T>? output}) async {
|
||||||
|
final outValueBytes = output == null ? null : Output<Uint8List>();
|
||||||
|
await removeItem(pos, output: outValueBytes);
|
||||||
|
output.mapSave(outValueBytes, (b) => jsonDecodeBytes(fromJson, b));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience function:
|
||||||
|
/// Like removeItem but also parses the returned element as JSON
|
||||||
|
Future<void> removeItemProtobuf<T extends GeneratedMessage>(
|
||||||
|
T Function(List<int>) fromBuffer, int pos,
|
||||||
|
{Output<T>? output}) async {
|
||||||
|
final outValueBytes = output == null ? null : Output<Uint8List>();
|
||||||
|
await removeItem(pos, output: outValueBytes);
|
||||||
|
output.mapSave(outValueBytes, fromBuffer);
|
||||||
|
}
|
||||||
|
}
|
@ -7,22 +7,21 @@ import '../../../veilid_support.dart';
|
|||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
// Reader interface
|
// Reader interface
|
||||||
abstract class DHTRandomRead {
|
abstract class DHTRandomRead {
|
||||||
/// Returns the number of elements in the DHTArray
|
/// Returns the number of elements in the DHT container
|
||||||
/// This number will be >= 0 and <= DHTShortArray.maxElements (256)
|
|
||||||
int get length;
|
int get length;
|
||||||
|
|
||||||
/// Return the item at position 'pos' in the DHTArray. If 'forceRefresh'
|
/// Return the item at position 'pos' in the DHT container. If 'forceRefresh'
|
||||||
/// is specified, the network will always be checked for newer values
|
/// is specified, the network will always be checked for newer values
|
||||||
/// rather than returning the existing locally stored copy of the elements.
|
/// rather than returning the existing locally stored copy of the elements.
|
||||||
/// * 'pos' must be >= 0 and < 'length'
|
/// Throws an IndexError if the 'pos' is not within the length
|
||||||
|
/// of the container.
|
||||||
Future<Uint8List?> getItem(int pos, {bool forceRefresh = false});
|
Future<Uint8List?> getItem(int pos, {bool forceRefresh = false});
|
||||||
|
|
||||||
/// Return a list of a range of items in the DHTArray. If 'forceRefresh'
|
/// Return a list of a range of items in the DHTArray. If 'forceRefresh'
|
||||||
/// is specified, the network will always be checked for newer values
|
/// is specified, the network will always be checked for newer values
|
||||||
/// rather than returning the existing locally stored copy of the elements.
|
/// rather than returning the existing locally stored copy of the elements.
|
||||||
/// * 'start' must be >= 0
|
/// Throws an IndexError if either 'start' or '(start+length)' is not within
|
||||||
/// * 'len' must be >= 0 and <= DHTShortArray.maxElements (256) and defaults
|
/// the length of the container.
|
||||||
/// to the maximum length
|
|
||||||
Future<List<Uint8List>?> getItemRange(int start,
|
Future<List<Uint8List>?> getItemRange(int start,
|
||||||
{int? length, bool forceRefresh = false});
|
{int? length, bool forceRefresh = false});
|
||||||
|
|
||||||
|
@ -6,8 +6,9 @@ import '../../../veilid_support.dart';
|
|||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
// Writer interface
|
// Writer interface
|
||||||
|
// ignore: one_member_abstracts
|
||||||
abstract class DHTRandomWrite {
|
abstract class DHTRandomWrite {
|
||||||
/// Try to set an item at position 'pos' of the DHTArray.
|
/// Try to set an item at position 'pos' of the DHT container.
|
||||||
/// If the set was successful this returns:
|
/// If the set was successful this returns:
|
||||||
/// * A boolean true
|
/// * A boolean true
|
||||||
/// * outValue will return the prior contents of the element,
|
/// * outValue will return the prior contents of the element,
|
||||||
@ -18,55 +19,10 @@ abstract class DHTRandomWrite {
|
|||||||
/// * outValue will return the newer value of the element,
|
/// * outValue will return the newer value of the element,
|
||||||
/// or null if the head record changed.
|
/// or null if the head record changed.
|
||||||
///
|
///
|
||||||
/// This may throw an exception if the position exceeds the built-in limit of
|
/// Throws an IndexError if the position is not within the length
|
||||||
/// 'maxElements = 256' entries.
|
/// of the container.
|
||||||
Future<bool> tryWriteItem(int pos, Uint8List newValue,
|
Future<bool> tryWriteItem(int pos, Uint8List newValue,
|
||||||
{Output<Uint8List>? output});
|
{Output<Uint8List>? output});
|
||||||
|
|
||||||
/// Try to add an item to the end of the DHTArray. Return true if the
|
|
||||||
/// element was successfully added, and false if the state changed before
|
|
||||||
/// the element could be added or a newer value was found on the network.
|
|
||||||
/// This may throw an exception if the number elements added exceeds the
|
|
||||||
/// built-in limit of 'maxElements = 256' entries.
|
|
||||||
Future<bool> tryAddItem(Uint8List value);
|
|
||||||
|
|
||||||
/// Try to add a list of items to the end of the DHTArray. Return true if the
|
|
||||||
/// elements were successfully added, and false if the state changed before
|
|
||||||
/// the elements could be added or a newer value was found on the network.
|
|
||||||
/// This may throw an exception if the number elements added exceeds the
|
|
||||||
/// built-in limit of 'maxElements = 256' entries.
|
|
||||||
Future<bool> tryAddItems(List<Uint8List> values);
|
|
||||||
|
|
||||||
/// Try to insert an item as position 'pos' of the DHTArray.
|
|
||||||
/// Return true if the element was successfully inserted, and false if the
|
|
||||||
/// state changed before the element could be inserted or a newer value was
|
|
||||||
/// found on the network.
|
|
||||||
/// This may throw an exception if the number elements added exceeds the
|
|
||||||
/// built-in limit of 'maxElements = 256' entries.
|
|
||||||
Future<bool> tryInsertItem(int pos, Uint8List value);
|
|
||||||
|
|
||||||
/// Try to insert items at position 'pos' of the DHTArray.
|
|
||||||
/// Return true if the elements were successfully inserted, and false if the
|
|
||||||
/// state changed before the elements could be inserted or a newer value was
|
|
||||||
/// found on the network.
|
|
||||||
/// This may throw an exception if the number elements added exceeds the
|
|
||||||
/// built-in limit of 'maxElements = 256' entries.
|
|
||||||
Future<bool> tryInsertItems(int pos, List<Uint8List> values);
|
|
||||||
|
|
||||||
/// Swap items at position 'aPos' and 'bPos' in the DHTArray.
|
|
||||||
/// Throws IndexError if either of the positions swapped exceed
|
|
||||||
/// the length of the list
|
|
||||||
Future<void> swapItem(int aPos, int bPos);
|
|
||||||
|
|
||||||
/// Remove an item at position 'pos' in the DHTArray.
|
|
||||||
/// If the remove was successful this returns:
|
|
||||||
/// * outValue will return the prior contents of the element
|
|
||||||
/// Throws IndexError if the position removed exceeds the length of
|
|
||||||
/// the list.
|
|
||||||
Future<void> removeItem(int pos, {Output<Uint8List>? output});
|
|
||||||
|
|
||||||
/// Remove all items in the DHTShortArray.
|
|
||||||
Future<void> clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension DHTRandomWriteExt on DHTRandomWrite {
|
extension DHTRandomWriteExt on DHTRandomWrite {
|
||||||
@ -95,25 +51,4 @@ extension DHTRandomWriteExt on DHTRandomWrite {
|
|||||||
output.mapSave(outValueBytes, fromBuffer);
|
output.mapSave(outValueBytes, fromBuffer);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience function:
|
|
||||||
/// Like removeItem but also parses the returned element as JSON
|
|
||||||
Future<void> removeItemJson<T>(T Function(dynamic) fromJson, int pos,
|
|
||||||
{Output<T>? output}) async {
|
|
||||||
final outValueBytes = output == null ? null : Output<Uint8List>();
|
|
||||||
await removeItem(pos, output: outValueBytes);
|
|
||||||
output.mapSave(outValueBytes, (b) => jsonDecodeBytes(fromJson, b));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convenience function:
|
|
||||||
/// Like removeItem but also parses the returned element as JSON
|
|
||||||
Future<void> removeItemProtobuf<T extends GeneratedMessage>(
|
|
||||||
T Function(List<int>) fromBuffer, int pos,
|
|
||||||
{Output<T>? output}) async {
|
|
||||||
final outValueBytes = output == null ? null : Output<Uint8List>();
|
|
||||||
await removeItem(pos, output: outValueBytes);
|
|
||||||
output.mapSave(outValueBytes, fromBuffer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class DHTRandomReadWrite implements DHTRandomRead, DHTRandomWrite {}
|
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Truncate interface
|
||||||
|
// ignore: one_member_abstracts
|
||||||
|
abstract class DHTTruncate {
|
||||||
|
/// Remove items from the DHT container to shrink its size to 'newLength'
|
||||||
|
/// Throws StateError if newLength < 0
|
||||||
|
Future<void> truncate(int newLength);
|
||||||
|
}
|
@ -1,4 +1,8 @@
|
|||||||
|
export 'dht_append.dart';
|
||||||
|
export 'dht_clear.dart';
|
||||||
export 'dht_closeable.dart';
|
export 'dht_closeable.dart';
|
||||||
|
export 'dht_insert_remove.dart';
|
||||||
export 'dht_random_read.dart';
|
export 'dht_random_read.dart';
|
||||||
export 'dht_random_write.dart';
|
export 'dht_random_write.dart';
|
||||||
|
export 'dht_truncate.dart';
|
||||||
export 'exceptions.dart';
|
export 'exceptions.dart';
|
||||||
|
@ -130,7 +130,7 @@ extension IdentityMasterExtension on IdentityMaster {
|
|||||||
// Read the identity key to get the account keys
|
// Read the identity key to get the account keys
|
||||||
final pool = DHTRecordPool.instance;
|
final pool = DHTRecordPool.instance;
|
||||||
|
|
||||||
final identityRecordCrypto = await DHTRecordCryptoPrivate.fromSecret(
|
final identityRecordCrypto = await VeilidCryptoPrivate.fromSecret(
|
||||||
identityRecordKey.kind, identitySecret);
|
identityRecordKey.kind, identitySecret);
|
||||||
|
|
||||||
late final List<AccountRecordInfo> accountRecordInfo;
|
late final List<AccountRecordInfo> accountRecordInfo;
|
||||||
@ -234,7 +234,7 @@ class IdentityMasterWithSecrets {
|
|||||||
return (await pool.createRecord(
|
return (await pool.createRecord(
|
||||||
debugName:
|
debugName:
|
||||||
'IdentityMasterWithSecrets::create::IdentityMasterRecord',
|
'IdentityMasterWithSecrets::create::IdentityMasterRecord',
|
||||||
crypto: const DHTRecordCryptoPublic()))
|
crypto: const VeilidCryptoPublic()))
|
||||||
.deleteScope((masterRec) async {
|
.deleteScope((masterRec) async {
|
||||||
veilidLoggy.debug('Creating identity record');
|
veilidLoggy.debug('Creating identity record');
|
||||||
// Identity record is private
|
// Identity record is private
|
||||||
|
517
packages/veilid_support/lib/src/table_db_array.dart
Normal file
517
packages/veilid_support/lib/src/table_db_array.dart
Normal file
@ -0,0 +1,517 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:math';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:async_tools/async_tools.dart';
|
||||||
|
import 'package:charcode/charcode.dart';
|
||||||
|
|
||||||
|
import '../veilid_support.dart';
|
||||||
|
|
||||||
|
class TableDBArray {
|
||||||
|
TableDBArray({
|
||||||
|
required String table,
|
||||||
|
required VeilidCrypto crypto,
|
||||||
|
}) : _table = table,
|
||||||
|
_crypto = crypto {
|
||||||
|
_initWait.add(_init);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _init() async {
|
||||||
|
// Load the array details
|
||||||
|
await _mutex.protect(() async {
|
||||||
|
_tableDB = await Veilid.instance.openTableDB(_table, 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> close({bool delete = false}) async {
|
||||||
|
// Ensure the init finished
|
||||||
|
await _initWait();
|
||||||
|
|
||||||
|
await _mutex.acquire();
|
||||||
|
|
||||||
|
await _changeStream.close();
|
||||||
|
_tableDB.close();
|
||||||
|
|
||||||
|
if (delete) {
|
||||||
|
await Veilid.instance.deleteTableDB(_table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<StreamSubscription<void>> listen(void Function() onChanged) async =>
|
||||||
|
_changeStream.stream.listen((_) => onChanged());
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
// Public interface
|
||||||
|
|
||||||
|
int get length => _length;
|
||||||
|
|
||||||
|
Future<void> add(Uint8List value) async {
|
||||||
|
await _initWait();
|
||||||
|
return _writeTransaction((t) async => _addInner(t, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> addAll(List<Uint8List> values) async {
|
||||||
|
await _initWait();
|
||||||
|
return _writeTransaction((t) async => _addAllInner(t, values));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> insert(int pos, Uint8List value) async {
|
||||||
|
await _initWait();
|
||||||
|
return _writeTransaction((t) async => _insertInner(t, pos, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> insertAll(int pos, List<Uint8List> values) async {
|
||||||
|
await _initWait();
|
||||||
|
return _writeTransaction((t) async => _insertAllInner(t, pos, values));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Uint8List> get(int pos) async {
|
||||||
|
await _initWait();
|
||||||
|
return _mutex.protect(() async => _getInner(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<Uint8List>> getAll(int start, int length) async {
|
||||||
|
await _initWait();
|
||||||
|
return _mutex.protect(() async => _getAllInner(start, length));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> remove(int pos, {Output<Uint8List>? out}) async {
|
||||||
|
await _initWait();
|
||||||
|
return _writeTransaction((t) async => _removeInner(t, pos, out: out));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> removeRange(int start, int length,
|
||||||
|
{Output<List<Uint8List>>? out}) async {
|
||||||
|
await _initWait();
|
||||||
|
return _writeTransaction(
|
||||||
|
(t) async => _removeRangeInner(t, start, length, out: out));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> clear() async {
|
||||||
|
await _initWait();
|
||||||
|
return _writeTransaction((t) async {
|
||||||
|
final keys = await _tableDB.getKeys(0);
|
||||||
|
for (final key in keys) {
|
||||||
|
await t.delete(0, key);
|
||||||
|
}
|
||||||
|
_length = 0;
|
||||||
|
_nextFree = 0;
|
||||||
|
_dirtyChunks.clear();
|
||||||
|
_chunkCache.clear();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
// Inner interface
|
||||||
|
|
||||||
|
Future<void> _addInner(VeilidTableDBTransaction t, Uint8List value) async {
|
||||||
|
// Allocate an entry to store the value
|
||||||
|
final entry = await _allocateEntry();
|
||||||
|
await _storeEntry(t, entry, value);
|
||||||
|
|
||||||
|
// Put the entry in the index
|
||||||
|
final pos = _length;
|
||||||
|
_length++;
|
||||||
|
await _setIndexEntry(pos, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _addAllInner(
|
||||||
|
VeilidTableDBTransaction t, List<Uint8List> values) async {
|
||||||
|
var pos = _length;
|
||||||
|
_length += values.length;
|
||||||
|
for (final value in values) {
|
||||||
|
// Allocate an entry to store the value
|
||||||
|
final entry = await _allocateEntry();
|
||||||
|
await _storeEntry(t, entry, value);
|
||||||
|
|
||||||
|
// Put the entry in the index
|
||||||
|
await _setIndexEntry(pos, entry);
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _insertInner(
|
||||||
|
VeilidTableDBTransaction t, int pos, Uint8List value) async {
|
||||||
|
if (pos == _length) {
|
||||||
|
return _addInner(t, value);
|
||||||
|
}
|
||||||
|
if (pos < 0 || pos >= _length) {
|
||||||
|
throw IndexError.withLength(pos, _length);
|
||||||
|
}
|
||||||
|
// Allocate an entry to store the value
|
||||||
|
final entry = await _allocateEntry();
|
||||||
|
await _storeEntry(t, entry, value);
|
||||||
|
|
||||||
|
// Put the entry in the index
|
||||||
|
await _insertIndexEntry(pos);
|
||||||
|
await _setIndexEntry(pos, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _insertAllInner(
|
||||||
|
VeilidTableDBTransaction t, int pos, List<Uint8List> values) async {
|
||||||
|
if (pos == _length) {
|
||||||
|
return _addAllInner(t, values);
|
||||||
|
}
|
||||||
|
if (pos < 0 || pos >= _length) {
|
||||||
|
throw IndexError.withLength(pos, _length);
|
||||||
|
}
|
||||||
|
await _insertIndexEntries(pos, values.length);
|
||||||
|
for (final value in values) {
|
||||||
|
// Allocate an entry to store the value
|
||||||
|
final entry = await _allocateEntry();
|
||||||
|
await _storeEntry(t, entry, value);
|
||||||
|
|
||||||
|
// Put the entry in the index
|
||||||
|
await _setIndexEntry(pos, entry);
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Uint8List> _getInner(int pos) async {
|
||||||
|
if (pos < 0 || pos >= _length) {
|
||||||
|
throw IndexError.withLength(pos, _length);
|
||||||
|
}
|
||||||
|
final entry = await _getIndexEntry(pos);
|
||||||
|
return (await _loadEntry(entry))!;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<Uint8List>> _getAllInner(int start, int length) async {
|
||||||
|
if (length < 0) {
|
||||||
|
throw StateError('length should not be negative');
|
||||||
|
}
|
||||||
|
if (start < 0 || start >= _length) {
|
||||||
|
throw IndexError.withLength(start, _length);
|
||||||
|
}
|
||||||
|
if ((start + length) > _length) {
|
||||||
|
throw IndexError.withLength(start + length, _length);
|
||||||
|
}
|
||||||
|
|
||||||
|
final out = <Uint8List>[];
|
||||||
|
for (var pos = start; pos < (start + length); pos++) {
|
||||||
|
final entry = await _getIndexEntry(pos);
|
||||||
|
final value = (await _loadEntry(entry))!;
|
||||||
|
out.add(value);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _removeInner(VeilidTableDBTransaction t, int pos,
|
||||||
|
{Output<Uint8List>? out}) async {
|
||||||
|
if (pos < 0 || pos >= _length) {
|
||||||
|
throw IndexError.withLength(pos, _length);
|
||||||
|
}
|
||||||
|
|
||||||
|
final entry = await _getIndexEntry(pos);
|
||||||
|
if (out != null) {
|
||||||
|
final value = (await _loadEntry(entry))!;
|
||||||
|
out.save(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _freeEntry(t, entry);
|
||||||
|
await _removeIndexEntry(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _removeRangeInner(
|
||||||
|
VeilidTableDBTransaction t, int start, int length,
|
||||||
|
{Output<List<Uint8List>>? out}) async {
|
||||||
|
if (length < 0) {
|
||||||
|
throw StateError('length should not be negative');
|
||||||
|
}
|
||||||
|
if (start < 0 || start >= _length) {
|
||||||
|
throw IndexError.withLength(start, _length);
|
||||||
|
}
|
||||||
|
if ((start + length) > _length) {
|
||||||
|
throw IndexError.withLength(start + length, _length);
|
||||||
|
}
|
||||||
|
|
||||||
|
final outList = <Uint8List>[];
|
||||||
|
for (var pos = start; pos < (start + length); pos++) {
|
||||||
|
final entry = await _getIndexEntry(pos);
|
||||||
|
if (out != null) {
|
||||||
|
final value = (await _loadEntry(entry))!;
|
||||||
|
outList.add(value);
|
||||||
|
}
|
||||||
|
await _freeEntry(t, entry);
|
||||||
|
}
|
||||||
|
if (out != null) {
|
||||||
|
out.save(outList);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _removeIndexEntries(start, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
// Private implementation
|
||||||
|
|
||||||
|
static final Uint8List _headKey = Uint8List.fromList([$_, $H, $E, $A, $D]);
|
||||||
|
static Uint8List _entryKey(int k) =>
|
||||||
|
(ByteData(4)..setUint32(0, k)).buffer.asUint8List();
|
||||||
|
static Uint8List _chunkKey(int n) =>
|
||||||
|
(ByteData(2)..setUint16(0, n)).buffer.asUint8List();
|
||||||
|
|
||||||
|
Future<T> _writeTransaction<T>(
|
||||||
|
Future<T> Function(VeilidTableDBTransaction) closure) async =>
|
||||||
|
_mutex.protect(() async {
|
||||||
|
final _oldLength = _length;
|
||||||
|
final _oldNextFree = _nextFree;
|
||||||
|
try {
|
||||||
|
final out = await transactionScope(_tableDB, (t) async {
|
||||||
|
final out = closure(t);
|
||||||
|
await _saveHead(t);
|
||||||
|
await _flushDirtyChunks(t);
|
||||||
|
return out;
|
||||||
|
});
|
||||||
|
|
||||||
|
return out;
|
||||||
|
} on Exception {
|
||||||
|
// restore head
|
||||||
|
_length = _oldLength;
|
||||||
|
_nextFree = _oldNextFree;
|
||||||
|
// invalidate caches because they could have been written to
|
||||||
|
_chunkCache.clear();
|
||||||
|
_dirtyChunks.clear();
|
||||||
|
// propagate exception
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<void> _storeEntry(
|
||||||
|
VeilidTableDBTransaction t, int entry, Uint8List value) async =>
|
||||||
|
t.store(0, _entryKey(entry), await _crypto.encrypt(value));
|
||||||
|
|
||||||
|
Future<Uint8List?> _loadEntry(int entry) async {
|
||||||
|
final encryptedValue = await _tableDB.load(0, _entryKey(entry));
|
||||||
|
return (encryptedValue == null) ? null : _crypto.decrypt(encryptedValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> _getIndexEntry(int pos) async {
|
||||||
|
if (pos < 0 || pos >= _length) {
|
||||||
|
throw IndexError.withLength(pos, _length);
|
||||||
|
}
|
||||||
|
final chunkNumber = pos ~/ _indexStride;
|
||||||
|
final chunkOffset = pos % _indexStride;
|
||||||
|
|
||||||
|
final chunk = await _loadIndexChunk(chunkNumber);
|
||||||
|
|
||||||
|
return chunk.buffer.asByteData().getUint32(chunkOffset * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _setIndexEntry(int pos, int entry) async {
|
||||||
|
if (pos < 0 || pos >= _length) {
|
||||||
|
throw IndexError.withLength(pos, _length);
|
||||||
|
}
|
||||||
|
|
||||||
|
final chunkNumber = pos ~/ _indexStride;
|
||||||
|
final chunkOffset = pos % _indexStride;
|
||||||
|
|
||||||
|
final chunk = await _loadIndexChunk(chunkNumber);
|
||||||
|
chunk.buffer.asByteData().setUint32(chunkOffset * 4, entry);
|
||||||
|
|
||||||
|
_dirtyChunks[chunkNumber] = chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _insertIndexEntry(int pos) async => _insertIndexEntries(pos, 1);
|
||||||
|
|
||||||
|
Future<void> _insertIndexEntries(int start, int length) async {
|
||||||
|
if (length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (length < 0) {
|
||||||
|
throw StateError('length should not be negative');
|
||||||
|
}
|
||||||
|
if (start < 0 || start >= _length) {
|
||||||
|
throw IndexError.withLength(start, _length);
|
||||||
|
}
|
||||||
|
final end = start + length - 1;
|
||||||
|
|
||||||
|
// Slide everything over in reverse
|
||||||
|
final toCopyTotal = _length - start;
|
||||||
|
var dest = end + toCopyTotal;
|
||||||
|
var src = _length - 1;
|
||||||
|
|
||||||
|
(int, Uint8List)? lastSrcChunk;
|
||||||
|
(int, Uint8List)? lastDestChunk;
|
||||||
|
while (src >= start) {
|
||||||
|
final srcChunkNumber = src ~/ _indexStride;
|
||||||
|
final srcIndex = src % _indexStride;
|
||||||
|
final srcLength = srcIndex + 1;
|
||||||
|
|
||||||
|
final srcChunk =
|
||||||
|
(lastSrcChunk != null && (lastSrcChunk.$1 == srcChunkNumber))
|
||||||
|
? lastSrcChunk.$2
|
||||||
|
: await _loadIndexChunk(srcChunkNumber);
|
||||||
|
lastSrcChunk = (srcChunkNumber, srcChunk);
|
||||||
|
|
||||||
|
final destChunkNumber = dest ~/ _indexStride;
|
||||||
|
final destIndex = dest % _indexStride;
|
||||||
|
final destLength = destIndex + 1;
|
||||||
|
|
||||||
|
final destChunk =
|
||||||
|
(lastDestChunk != null && (lastDestChunk.$1 == destChunkNumber))
|
||||||
|
? lastDestChunk.$2
|
||||||
|
: await _loadIndexChunk(destChunkNumber);
|
||||||
|
lastDestChunk = (destChunkNumber, destChunk);
|
||||||
|
|
||||||
|
final toCopy = min(srcLength, destLength);
|
||||||
|
destChunk.setRange((destIndex - (toCopy - 1)) * 4, (destIndex + 1) * 4,
|
||||||
|
srcChunk, (srcIndex - (toCopy - 1)) * 4);
|
||||||
|
|
||||||
|
dest -= toCopy;
|
||||||
|
src -= toCopy;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then add to length
|
||||||
|
_length += length;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _removeIndexEntry(int pos) async => _removeIndexEntries(pos, 1);
|
||||||
|
|
||||||
|
Future<void> _removeIndexEntries(int start, int length) async {
|
||||||
|
if (length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (length < 0) {
|
||||||
|
throw StateError('length should not be negative');
|
||||||
|
}
|
||||||
|
if (start < 0 || start >= _length) {
|
||||||
|
throw IndexError.withLength(start, _length);
|
||||||
|
}
|
||||||
|
final end = start + length - 1;
|
||||||
|
if (end < 0 || end >= _length) {
|
||||||
|
throw IndexError.withLength(end, _length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slide everything over
|
||||||
|
var dest = start;
|
||||||
|
var src = end + 1;
|
||||||
|
(int, Uint8List)? lastSrcChunk;
|
||||||
|
(int, Uint8List)? lastDestChunk;
|
||||||
|
while (src < _length) {
|
||||||
|
final srcChunkNumber = src ~/ _indexStride;
|
||||||
|
final srcIndex = src % _indexStride;
|
||||||
|
final srcLength = _indexStride - srcIndex;
|
||||||
|
|
||||||
|
final srcChunk =
|
||||||
|
(lastSrcChunk != null && (lastSrcChunk.$1 == srcChunkNumber))
|
||||||
|
? lastSrcChunk.$2
|
||||||
|
: await _loadIndexChunk(srcChunkNumber);
|
||||||
|
lastSrcChunk = (srcChunkNumber, srcChunk);
|
||||||
|
|
||||||
|
final destChunkNumber = dest ~/ _indexStride;
|
||||||
|
final destIndex = dest % _indexStride;
|
||||||
|
final destLength = _indexStride - destIndex;
|
||||||
|
|
||||||
|
final destChunk =
|
||||||
|
(lastDestChunk != null && (lastDestChunk.$1 == destChunkNumber))
|
||||||
|
? lastDestChunk.$2
|
||||||
|
: await _loadIndexChunk(destChunkNumber);
|
||||||
|
lastDestChunk = (destChunkNumber, destChunk);
|
||||||
|
|
||||||
|
final toCopy = min(srcLength, destLength);
|
||||||
|
destChunk.setRange(
|
||||||
|
destIndex * 4, (destIndex + toCopy) * 4, srcChunk, srcIndex * 4);
|
||||||
|
|
||||||
|
dest += toCopy;
|
||||||
|
src += toCopy;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then truncate
|
||||||
|
_length -= length;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Uint8List> _loadIndexChunk(int chunkNumber) async {
|
||||||
|
// Get it from the dirty chunks if we have it
|
||||||
|
final dirtyChunk = _dirtyChunks[chunkNumber];
|
||||||
|
if (dirtyChunk != null) {
|
||||||
|
return dirtyChunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get from cache if we have it
|
||||||
|
for (var i = 0; i < _chunkCache.length; i++) {
|
||||||
|
if (_chunkCache[i].$1 == chunkNumber) {
|
||||||
|
// Touch the element
|
||||||
|
final x = _chunkCache.removeAt(i);
|
||||||
|
_chunkCache.add(x);
|
||||||
|
// Return the chunk for this position
|
||||||
|
return x.$2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get chunk from disk
|
||||||
|
var chunk = await _tableDB.load(0, _chunkKey(chunkNumber));
|
||||||
|
chunk ??= Uint8List(_indexStride * 4);
|
||||||
|
|
||||||
|
// Cache the chunk
|
||||||
|
_chunkCache.add((chunkNumber, chunk));
|
||||||
|
if (_chunkCache.length > _chunkCacheLength) {
|
||||||
|
// Trim the LRU cache
|
||||||
|
final (_, _) = _chunkCache.removeAt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _flushDirtyChunks(VeilidTableDBTransaction t) async {
|
||||||
|
for (final ec in _dirtyChunks.entries) {
|
||||||
|
await _tableDB.store(0, _chunkKey(ec.key), ec.value);
|
||||||
|
}
|
||||||
|
_dirtyChunks.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _loadHead() async {
|
||||||
|
assert(_mutex.isLocked, 'should be locked');
|
||||||
|
final headBytes = await _tableDB.load(0, _headKey);
|
||||||
|
if (headBytes == null) {
|
||||||
|
_length = 0;
|
||||||
|
_nextFree = 0;
|
||||||
|
} else {
|
||||||
|
final b = headBytes.buffer.asByteData();
|
||||||
|
_length = b.getUint32(0);
|
||||||
|
_nextFree = b.getUint32(4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _saveHead(VeilidTableDBTransaction t) async {
|
||||||
|
assert(_mutex.isLocked, 'should be locked');
|
||||||
|
final b = ByteData(8)
|
||||||
|
..setUint32(0, _length)
|
||||||
|
..setUint32(4, _nextFree);
|
||||||
|
await t.store(0, _headKey, b.buffer.asUint8List());
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<int> _allocateEntry() async {
|
||||||
|
assert(_mutex.isLocked, 'should be locked');
|
||||||
|
if (_nextFree == 0) {
|
||||||
|
return _length;
|
||||||
|
}
|
||||||
|
// pop endogenous free list
|
||||||
|
final free = _nextFree;
|
||||||
|
final nextFreeBytes = await _tableDB.load(0, _entryKey(free));
|
||||||
|
_nextFree = nextFreeBytes!.buffer.asByteData().getUint8(0);
|
||||||
|
return free;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _freeEntry(VeilidTableDBTransaction t, int entry) async {
|
||||||
|
assert(_mutex.isLocked, 'should be locked');
|
||||||
|
// push endogenous free list
|
||||||
|
final b = ByteData(4)..setUint32(0, _nextFree);
|
||||||
|
await t.store(0, _entryKey(entry), b.buffer.asUint8List());
|
||||||
|
_nextFree = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String _table;
|
||||||
|
late final VeilidTableDB _tableDB;
|
||||||
|
final VeilidCrypto _crypto;
|
||||||
|
final WaitSet<void> _initWait = WaitSet();
|
||||||
|
final Mutex _mutex = Mutex();
|
||||||
|
|
||||||
|
// Head state
|
||||||
|
int _length = 0;
|
||||||
|
int _nextFree = 0;
|
||||||
|
static const int _indexStride = 16384;
|
||||||
|
final List<(int, Uint8List)> _chunkCache = [];
|
||||||
|
final Map<int, Uint8List> _dirtyChunks = {};
|
||||||
|
static const int _chunkCacheLength = 3;
|
||||||
|
|
||||||
|
final StreamController<void> _changeStream = StreamController.broadcast();
|
||||||
|
}
|
52
packages/veilid_support/lib/src/veilid_crypto.dart
Normal file
52
packages/veilid_support/lib/src/veilid_crypto.dart
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import '../../../veilid_support.dart';
|
||||||
|
|
||||||
|
abstract class VeilidCrypto {
|
||||||
|
Future<Uint8List> encrypt(Uint8List data);
|
||||||
|
Future<Uint8List> decrypt(Uint8List data);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////
|
||||||
|
/// Encrypted for a specific symmetric key
|
||||||
|
class VeilidCryptoPrivate implements VeilidCrypto {
|
||||||
|
VeilidCryptoPrivate._(VeilidCryptoSystem cryptoSystem, SharedSecret secretKey)
|
||||||
|
: _cryptoSystem = cryptoSystem,
|
||||||
|
_secretKey = secretKey;
|
||||||
|
final VeilidCryptoSystem _cryptoSystem;
|
||||||
|
final SharedSecret _secretKey;
|
||||||
|
|
||||||
|
static Future<VeilidCryptoPrivate> fromTypedKeyPair(
|
||||||
|
TypedKeyPair typedKeyPair) async {
|
||||||
|
final cryptoSystem =
|
||||||
|
await Veilid.instance.getCryptoSystem(typedKeyPair.kind);
|
||||||
|
final secretKey = typedKeyPair.secret;
|
||||||
|
return VeilidCryptoPrivate._(cryptoSystem, secretKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<VeilidCryptoPrivate> fromSecret(
|
||||||
|
CryptoKind kind, SharedSecret secretKey) async {
|
||||||
|
final cryptoSystem = await Veilid.instance.getCryptoSystem(kind);
|
||||||
|
return VeilidCryptoPrivate._(cryptoSystem, secretKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Uint8List> encrypt(Uint8List data) =>
|
||||||
|
_cryptoSystem.encryptNoAuthWithNonce(data, _secretKey);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Uint8List> decrypt(Uint8List data) =>
|
||||||
|
_cryptoSystem.decryptNoAuthWithNonce(data, _secretKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////
|
||||||
|
/// No encryption
|
||||||
|
class VeilidCryptoPublic implements VeilidCrypto {
|
||||||
|
const VeilidCryptoPublic();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Uint8List> encrypt(Uint8List data) async => data;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Uint8List> decrypt(Uint8List data) async => data;
|
||||||
|
}
|
@ -14,4 +14,6 @@ export 'src/output.dart';
|
|||||||
export 'src/persistent_queue.dart';
|
export 'src/persistent_queue.dart';
|
||||||
export 'src/protobuf_tools.dart';
|
export 'src/protobuf_tools.dart';
|
||||||
export 'src/table_db.dart';
|
export 'src/table_db.dart';
|
||||||
|
export 'src/table_db_array.dart';
|
||||||
|
export 'src/veilid_crypto.dart';
|
||||||
export 'src/veilid_log.dart' hide veilidLoggy;
|
export 'src/veilid_log.dart' hide veilidLoggy;
|
||||||
|
@ -146,7 +146,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.3.0"
|
||||||
charcode:
|
charcode:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: charcode
|
name: charcode
|
||||||
sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306
|
sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306
|
||||||
|
@ -10,6 +10,7 @@ dependencies:
|
|||||||
async_tools: ^0.1.1
|
async_tools: ^0.1.1
|
||||||
bloc: ^8.1.4
|
bloc: ^8.1.4
|
||||||
bloc_advanced_tools: ^0.1.1
|
bloc_advanced_tools: ^0.1.1
|
||||||
|
charcode: ^1.3.1
|
||||||
collection: ^1.18.0
|
collection: ^1.18.0
|
||||||
equatable: ^2.0.5
|
equatable: ^2.0.5
|
||||||
fast_immutable_collections: ^10.2.3
|
fast_immutable_collections: ^10.2.3
|
||||||
|
Loading…
x
Reference in New Issue
Block a user