import 'dart:async'; import 'dart:typed_data'; import 'package:change_case/change_case.dart'; import 'package:equatable/equatable.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'veilid.dart'; part 'routing_context.freezed.dart'; part 'routing_context.g.dart'; ////////////////////////////////////// extension ValidateDFLT on DHTSchemaDFLT { bool validate() { if (oCnt > 65535) { return false; } if (oCnt <= 0) { return false; } return true; } int subkeyCount() => oCnt; } extension ValidateSMPL on DHTSchemaSMPL { bool validate() { final totalsv = subkeyCount(); if (totalsv > 65535) { return false; } if (totalsv <= 0) { return false; } return true; } int subkeyCount() => members.fold(oCnt, (acc, v) => acc + v.mCnt); } extension Validate on DHTSchema { bool validate() { if (this is DHTSchemaDFLT) { return (this as DHTSchemaDFLT).validate(); } else if (this is DHTSchemaSMPL) { return (this as DHTSchemaSMPL).validate(); } throw TypeError(); } int subkeyCount() { if (this is DHTSchemaDFLT) { return (this as DHTSchemaDFLT).subkeyCount(); } else if (this is DHTSchemaSMPL) { return (this as DHTSchemaSMPL).subkeyCount(); } throw TypeError(); } } ////////////////////////////////////// /// DHT Schema @Freezed(unionKey: 'kind', unionValueCase: FreezedUnionCase.pascal) sealed class DHTSchema with _$DHTSchema { @FreezedUnionValue('DFLT') const factory DHTSchema.dflt({required int oCnt}) = DHTSchemaDFLT; @FreezedUnionValue('SMPL') const factory DHTSchema.smpl( {required int oCnt, required List members}) = DHTSchemaSMPL; factory DHTSchema.fromJson(dynamic json) => _$DHTSchemaFromJson(json as Map); } const DHTSchema defaultDHTSchema = DHTSchema.dflt(oCnt: 1); @freezed class DHTSchemaMember with _$DHTSchemaMember { @Assert('mCnt > 0 && mCnt <= 65535', 'value out of range') const factory DHTSchemaMember({ required PublicKey mKey, required int mCnt, }) = _DHTSchemaMember; factory DHTSchemaMember.fromJson(dynamic json) => _$DHTSchemaMemberFromJson(json as Map); } ////////////////////////////////////// /// DHTRecordDescriptor @freezed class DHTRecordDescriptor with _$DHTRecordDescriptor { const factory DHTRecordDescriptor({ required TypedKey key, required PublicKey owner, required DHTSchema schema, PublicKey? ownerSecret, }) = _DHTRecordDescriptor; factory DHTRecordDescriptor.fromJson(dynamic json) => _$DHTRecordDescriptorFromJson(json as Map); } extension DHTRecordDescriptorExt on DHTRecordDescriptor { KeyPair? ownerKeyPair() { if (ownerSecret == null) { return null; } return KeyPair(key: owner, secret: ownerSecret!); } TypedKeyPair? ownerTypedKeyPair() { if (ownerSecret == null) { return null; } return TypedKeyPair(kind: key.kind, key: owner, secret: ownerSecret!); } } ////////////////////////////////////// /// ValueData @freezed class ValueData with _$ValueData { @Assert('seq >= 0', 'seq out of range') const factory ValueData({ required int seq, @Uint8ListJsonConverter.jsIsArray() required Uint8List data, required PublicKey writer, }) = _ValueData; factory ValueData.fromJson(dynamic json) => _$ValueDataFromJson(json as Map); } ////////////////////////////////////// /// Stability enum Stability { lowLatency, reliable; factory Stability.fromJson(dynamic j) => Stability.values.byName((j as String).toCamelCase()); String toJson() => name.toPascalCase(); } ////////////////////////////////////// /// Sequencing enum Sequencing { noPreference, preferOrdered, ensureOrdered; factory Sequencing.fromJson(dynamic j) => Sequencing.values.byName((j as String).toCamelCase()); String toJson() => name.toPascalCase(); } ////////////////////////////////////// /// SafetySelection @immutable abstract class SafetySelection { factory SafetySelection.fromJson(dynamic jsond) { final json = jsond as Map; if (json.containsKey('Unsafe')) { return SafetySelectionUnsafe( sequencing: Sequencing.fromJson(json['Unsafe'])); } else if (json.containsKey('Safe')) { return SafetySelectionSafe(safetySpec: SafetySpec.fromJson(json['Safe'])); } else { throw const VeilidAPIExceptionInternal('Invalid SafetySelection'); } } Map toJson(); } @immutable class SafetySelectionUnsafe extends Equatable implements SafetySelection { // const SafetySelectionUnsafe({ required this.sequencing, }); final Sequencing sequencing; @override List get props => [sequencing]; @override bool? get stringify => null; @override Map toJson() => {'Unsafe': sequencing.toJson()}; } @immutable class SafetySelectionSafe extends Equatable implements SafetySelection { // const SafetySelectionSafe({ required this.safetySpec, }); final SafetySpec safetySpec; @override List get props => [safetySpec]; @override bool? get stringify => null; @override Map toJson() => {'Safe': safetySpec.toJson()}; } /// Options for safety routes (sender privacy) @freezed class SafetySpec with _$SafetySpec { const factory SafetySpec({ required int hopCount, required Stability stability, required Sequencing sequencing, String? preferredRoute, }) = _SafetySpec; factory SafetySpec.fromJson(dynamic json) => _$SafetySpecFromJson(json as Map); } ////////////////////////////////////// /// RouteBlob @freezed class RouteBlob with _$RouteBlob { const factory RouteBlob( {required String routeId, @Uint8ListJsonConverter() required Uint8List blob}) = _RouteBlob; factory RouteBlob.fromJson(dynamic json) => _$RouteBlobFromJson(json as Map); } ////////////////////////////////////// /// Inspect @freezed class DHTRecordReport with _$DHTRecordReport { const factory DHTRecordReport({ required List subkeys, required List localSeqs, required List networkSeqs, }) = _DHTRecordReport; factory DHTRecordReport.fromJson(dynamic json) => _$DHTRecordReportFromJson(json as Map); } enum DHTReportScope { local, syncGet, syncSet, updateGet, updateSet; factory DHTReportScope.fromJson(dynamic j) => DHTReportScope.values.byName((j as String).toCamelCase()); String toJson() => name.toPascalCase(); } ////////////////////////////////////// /// VeilidRoutingContext abstract class VeilidRoutingContext { void close(); // Modifiers VeilidRoutingContext withDefaultSafety({bool closeSelf = false}); VeilidRoutingContext withSafety(SafetySelection safetySelection, {bool closeSelf = false}); VeilidRoutingContext withSequencing(Sequencing sequencing, {bool closeSelf = false}); Future safety(); // App call/message Future appCall(String target, Uint8List request); Future appMessage(String target, Uint8List message); // DHT Operations Future createDHTRecord(DHTSchema schema, {CryptoKind kind = 0}); Future openDHTRecord(TypedKey key, {KeyPair? writer}); Future closeDHTRecord(TypedKey key); Future deleteDHTRecord(TypedKey key); Future getDHTValue(TypedKey key, int subkey, {bool forceRefresh = false}); Future setDHTValue(TypedKey key, int subkey, Uint8List data, {KeyPair? writer}); Future watchDHTValues(TypedKey key, {List? subkeys, Timestamp? expiration, int? count}); Future cancelDHTWatch(TypedKey key, {List? subkeys}); Future inspectDHTRecord(TypedKey key, {List? subkeys, DHTReportScope scope = DHTReportScope.local}); }