diff --git a/lib/providers/contact_invite.dart b/lib/providers/contact_invite.dart index 81014bc..0b22b0f 100644 --- a/lib/providers/contact_invite.dart +++ b/lib/providers/contact_invite.dart @@ -70,7 +70,6 @@ Future checkAcceptRejectContact( final signedContactResponse = await contactRequestInbox .getProtobuf(SignedContactResponse.fromBuffer, forceRefresh: true); if (signedContactResponse == null) { - log.error('failed to get signed contact response'); return null; } diff --git a/lib/veilid_support/dht_support/dht_record_pool.dart b/lib/veilid_support/dht_support/dht_record_pool.dart index d9af1bb..c049566 100644 --- a/lib/veilid_support/dht_support/dht_record_pool.dart +++ b/lib/veilid_support/dht_support/dht_record_pool.dart @@ -16,6 +16,7 @@ class DHTRecordPoolAllocations with _$DHTRecordPoolAllocations { childrenByParent, // String key due to IMap<> json unsupported in key required IMap parentByChild, // String key due to IMap<> json unsupported in key + required ISet rootRecords, }) = _DHTRecordPoolAllocations; factory DHTRecordPoolAllocations.fromJson(dynamic json) => @@ -38,7 +39,9 @@ class OwnedDHTRecordPointer with _$OwnedDHTRecordPointer { class DHTRecordPool with AsyncTableDBBacked { DHTRecordPool._(Veilid veilid, VeilidRoutingContext routingContext) : _state = DHTRecordPoolAllocations( - childrenByParent: IMap(), parentByChild: IMap()), + childrenByParent: IMap(), + parentByChild: IMap(), + rootRecords: ISet()), _opened = {}, _routingContext = routingContext, _veilid = veilid; @@ -64,7 +67,7 @@ class DHTRecordPool with AsyncTableDBBacked { DHTRecordPoolAllocations valueFromJson(Object? obj) => obj != null ? DHTRecordPoolAllocations.fromJson(obj) : DHTRecordPoolAllocations( - childrenByParent: IMap(), parentByChild: IMap()); + childrenByParent: IMap(), parentByChild: IMap(), rootRecords: ISet()); @override Object? valueToJson(DHTRecordPoolAllocations val) => val.toJson(); @@ -133,45 +136,70 @@ class DHTRecordPool with AsyncTableDBBacked { await Future.wait(allFutures); } - Future _addDependency(TypedKey parent, TypedKey child) async { - final childrenOfParent = - _state.childrenByParent[parent.toJson()] ?? ISet(); - if (childrenOfParent.contains(child)) { - // Dependency already added (consecutive opens, etc) - return; - } - if (_state.parentByChild.containsKey(child.toJson())) { - throw StateError('Child has two parents: $child <- $parent'); - } - if (_state.childrenByParent.containsKey(child.toJson())) { - // dependencies should be opened after their parents - throw StateError('Child is not a leaf: $child'); - } + Future _addDependency(TypedKey? parent, TypedKey child) async { + if (parent == null) { + if (_state.rootRecords.contains(child)) { + // Dependency already added + return; + } + if (_state.parentByChild.containsKey(child.toJson())) { + throw StateError('Child is already parented: $child'); + } + if (_state.childrenByParent.containsKey(child.toJson())) { + // dependencies should be opened after their parents + throw StateError('Child is not a leaf: $child'); + } - _state = await store(_state.copyWith( - childrenByParent: _state.childrenByParent - .add(parent.toJson(), childrenOfParent.add(child)), - parentByChild: _state.parentByChild.add(child.toJson(), parent))); + _state = await store( + _state.copyWith(rootRecords: _state.rootRecords.add(child))); + } else { + final childrenOfParent = + _state.childrenByParent[parent.toJson()] ?? ISet(); + if (childrenOfParent.contains(child)) { + // Dependency already added (consecutive opens, etc) + return; + } + if (_state.rootRecords.contains(child)) { + throw StateError('Child already added as root: $child'); + } + if (_state.parentByChild.containsKey(child.toJson())) { + throw StateError('Child has two parents: $child <- $parent'); + } + if (_state.childrenByParent.containsKey(child.toJson())) { + // dependencies should be opened after their parents + throw StateError('Child is not a leaf: $child'); + } + + _state = await store(_state.copyWith( + childrenByParent: _state.childrenByParent + .add(parent.toJson(), childrenOfParent.add(child)), + parentByChild: _state.parentByChild.add(child.toJson(), parent))); + } } Future _removeDependency(TypedKey child) async { - final parent = _state.parentByChild[child.toJson()]; - if (parent == null) { - return; - } - final children = _state.childrenByParent[parent.toJson()]!.remove(child); - late final DHTRecordPoolAllocations newState; - if (children.isEmpty) { - newState = _state.copyWith( - childrenByParent: _state.childrenByParent.remove(parent.toJson()), - parentByChild: _state.parentByChild.remove(child.toJson())); + if (_state.rootRecords.contains(child)) { + _state = await store( + _state.copyWith(rootRecords: _state.rootRecords.remove(child))); } else { - newState = _state.copyWith( - childrenByParent: - _state.childrenByParent.add(parent.toJson(), children), - parentByChild: _state.parentByChild.remove(child.toJson())); + final parent = _state.parentByChild[child.toJson()]; + if (parent == null) { + return; + } + final children = _state.childrenByParent[parent.toJson()]!.remove(child); + late final DHTRecordPoolAllocations newState; + if (children.isEmpty) { + newState = _state.copyWith( + childrenByParent: _state.childrenByParent.remove(parent.toJson()), + parentByChild: _state.parentByChild.remove(child.toJson())); + } else { + newState = _state.copyWith( + childrenByParent: + _state.childrenByParent.add(parent.toJson(), children), + parentByChild: _state.parentByChild.remove(child.toJson())); + } + _state = await store(newState); } - _state = await store(newState); } /////////////////////////////////////////////////////////////////////// diff --git a/lib/veilid_support/dht_support/dht_record_pool.freezed.dart b/lib/veilid_support/dht_support/dht_record_pool.freezed.dart index ad600be..3ef24be 100644 --- a/lib/veilid_support/dht_support/dht_record_pool.freezed.dart +++ b/lib/veilid_support/dht_support/dht_record_pool.freezed.dart @@ -24,6 +24,8 @@ mixin _$DHTRecordPoolAllocations { IMap>> get childrenByParent => throw _privateConstructorUsedError; // String key due to IMap<> json unsupported in key IMap> get parentByChild => + throw _privateConstructorUsedError; // String key due to IMap<> json unsupported in key + ISet> get rootRecords => throw _privateConstructorUsedError; Map toJson() => throw _privateConstructorUsedError; @@ -40,7 +42,8 @@ abstract class $DHTRecordPoolAllocationsCopyWith<$Res> { @useResult $Res call( {IMap>> childrenByParent, - IMap> parentByChild}); + IMap> parentByChild, + ISet> rootRecords}); } /// @nodoc @@ -59,6 +62,7 @@ class _$DHTRecordPoolAllocationsCopyWithImpl<$Res, $Res call({ Object? childrenByParent = null, Object? parentByChild = null, + Object? rootRecords = null, }) { return _then(_value.copyWith( childrenByParent: null == childrenByParent @@ -69,6 +73,10 @@ class _$DHTRecordPoolAllocationsCopyWithImpl<$Res, ? _value.parentByChild : parentByChild // ignore: cast_nullable_to_non_nullable as IMap>, + rootRecords: null == rootRecords + ? _value.rootRecords + : rootRecords // ignore: cast_nullable_to_non_nullable + as ISet>, ) as $Val); } } @@ -84,7 +92,8 @@ abstract class _$$_DHTRecordPoolAllocationsCopyWith<$Res> @useResult $Res call( {IMap>> childrenByParent, - IMap> parentByChild}); + IMap> parentByChild, + ISet> rootRecords}); } /// @nodoc @@ -101,6 +110,7 @@ class __$$_DHTRecordPoolAllocationsCopyWithImpl<$Res> $Res call({ Object? childrenByParent = null, Object? parentByChild = null, + Object? rootRecords = null, }) { return _then(_$_DHTRecordPoolAllocations( childrenByParent: null == childrenByParent @@ -111,6 +121,10 @@ class __$$_DHTRecordPoolAllocationsCopyWithImpl<$Res> ? _value.parentByChild : parentByChild // ignore: cast_nullable_to_non_nullable as IMap>, + rootRecords: null == rootRecords + ? _value.rootRecords + : rootRecords // ignore: cast_nullable_to_non_nullable + as ISet>, )); } } @@ -119,7 +133,9 @@ class __$$_DHTRecordPoolAllocationsCopyWithImpl<$Res> @JsonSerializable() class _$_DHTRecordPoolAllocations implements _DHTRecordPoolAllocations { const _$_DHTRecordPoolAllocations( - {required this.childrenByParent, required this.parentByChild}); + {required this.childrenByParent, + required this.parentByChild, + required this.rootRecords}); factory _$_DHTRecordPoolAllocations.fromJson(Map json) => _$$_DHTRecordPoolAllocationsFromJson(json); @@ -129,10 +145,13 @@ class _$_DHTRecordPoolAllocations implements _DHTRecordPoolAllocations { // String key due to IMap<> json unsupported in key @override final IMap> parentByChild; +// String key due to IMap<> json unsupported in key + @override + final ISet> rootRecords; @override String toString() { - return 'DHTRecordPoolAllocations(childrenByParent: $childrenByParent, parentByChild: $parentByChild)'; + return 'DHTRecordPoolAllocations(childrenByParent: $childrenByParent, parentByChild: $parentByChild, rootRecords: $rootRecords)'; } @override @@ -143,12 +162,15 @@ class _$_DHTRecordPoolAllocations implements _DHTRecordPoolAllocations { (identical(other.childrenByParent, childrenByParent) || other.childrenByParent == childrenByParent) && (identical(other.parentByChild, parentByChild) || - other.parentByChild == parentByChild)); + other.parentByChild == parentByChild) && + const DeepCollectionEquality() + .equals(other.rootRecords, rootRecords)); } @JsonKey(ignore: true) @override - int get hashCode => Object.hash(runtimeType, childrenByParent, parentByChild); + int get hashCode => Object.hash(runtimeType, childrenByParent, parentByChild, + const DeepCollectionEquality().hash(rootRecords)); @JsonKey(ignore: true) @override @@ -169,8 +191,9 @@ abstract class _DHTRecordPoolAllocations implements DHTRecordPoolAllocations { const factory _DHTRecordPoolAllocations( {required final IMap>> childrenByParent, - required final IMap> - parentByChild}) = _$_DHTRecordPoolAllocations; + required final IMap> parentByChild, + required final ISet> + rootRecords}) = _$_DHTRecordPoolAllocations; factory _DHTRecordPoolAllocations.fromJson(Map json) = _$_DHTRecordPoolAllocations.fromJson; @@ -179,6 +202,8 @@ abstract class _DHTRecordPoolAllocations implements DHTRecordPoolAllocations { IMap>> get childrenByParent; @override // String key due to IMap<> json unsupported in key IMap> get parentByChild; + @override // String key due to IMap<> json unsupported in key + ISet> get rootRecords; @override @JsonKey(ignore: true) _$$_DHTRecordPoolAllocationsCopyWith<_$_DHTRecordPoolAllocations> diff --git a/lib/veilid_support/dht_support/dht_record_pool.g.dart b/lib/veilid_support/dht_support/dht_record_pool.g.dart index fd5af1b..b52a302 100644 --- a/lib/veilid_support/dht_support/dht_record_pool.g.dart +++ b/lib/veilid_support/dht_support/dht_record_pool.g.dart @@ -19,6 +19,9 @@ _$_DHTRecordPoolAllocations _$$_DHTRecordPoolAllocationsFromJson( json['parent_by_child'] as Map, (value) => value as String, (value) => Typed.fromJson(value)), + rootRecords: ISet>.fromJson( + json['root_records'], + (value) => Typed.fromJson(value)), ); Map _$$_DHTRecordPoolAllocationsToJson( @@ -34,6 +37,9 @@ Map _$$_DHTRecordPoolAllocationsToJson( (value) => value, (value) => value.toJson(), ), + 'root_records': instance.rootRecords.toJson( + (value) => value.toJson(), + ), }; _$_OwnedDHTRecordPointer _$$_OwnedDHTRecordPointerFromJson(