2023-05-29 19:24:57 +00:00
|
|
|
import 'dart:async';
|
|
|
|
import 'dart:typed_data';
|
|
|
|
|
|
|
|
import 'package:change_case/change_case.dart';
|
2023-07-06 15:41:38 +00:00
|
|
|
import 'package:equatable/equatable.dart';
|
2023-07-06 03:53:08 +00:00
|
|
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
2023-05-29 19:24:57 +00:00
|
|
|
|
|
|
|
import 'veilid_encoding.dart';
|
|
|
|
import 'veilid.dart';
|
|
|
|
|
2023-07-06 03:53:08 +00:00
|
|
|
part 'routing_context.freezed.dart';
|
|
|
|
part 'routing_context.g.dart';
|
|
|
|
|
2023-05-29 19:24:57 +00:00
|
|
|
//////////////////////////////////////
|
|
|
|
|
2023-07-06 15:41:38 +00:00
|
|
|
extension ValidateDFLT on DHTSchemaDFLT {
|
|
|
|
bool validate() {
|
|
|
|
if (oCnt > 65535) {
|
|
|
|
return false;
|
2023-05-29 19:24:57 +00:00
|
|
|
}
|
2023-07-06 15:41:38 +00:00
|
|
|
if (oCnt <= 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2023-05-29 19:24:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-06 15:41:38 +00:00
|
|
|
extension ValidateSMPL on DHTSchemaSMPL {
|
|
|
|
bool validate() {
|
|
|
|
final totalsv = members.fold(0, (acc, v) => (acc + v.mCnt)) + oCnt;
|
|
|
|
if (totalsv > 65535) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (totalsv <= 0) {
|
|
|
|
return false;
|
2023-05-29 19:24:57 +00:00
|
|
|
}
|
2023-07-06 15:41:38 +00:00
|
|
|
return true;
|
2023-05-29 19:24:57 +00:00
|
|
|
}
|
2023-07-06 15:41:38 +00:00
|
|
|
}
|
2023-05-29 19:24:57 +00:00
|
|
|
|
2023-07-06 15:41:38 +00:00
|
|
|
//////////////////////////////////////
|
|
|
|
/// 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<DHTSchemaMember> members}) = DHTSchemaSMPL;
|
|
|
|
|
|
|
|
factory DHTSchema.fromJson(Map<String, dynamic> json) =>
|
|
|
|
_$DHTSchemaFromJson(json);
|
2023-05-29 19:24:57 +00:00
|
|
|
}
|
|
|
|
|
2023-07-13 01:28:00 +00:00
|
|
|
const DHTSchema defaultDHTSchema = DHTSchema.dflt(oCnt: 1);
|
|
|
|
|
2023-07-06 03:53:08 +00:00
|
|
|
@freezed
|
|
|
|
class DHTSchemaMember with _$DHTSchemaMember {
|
2023-07-06 15:41:38 +00:00
|
|
|
@Assert('mCnt > 0 && mCnt <= 65535', 'value out of range')
|
2023-07-06 03:53:08 +00:00
|
|
|
const factory DHTSchemaMember({
|
|
|
|
required PublicKey mKey,
|
|
|
|
required int mCnt,
|
|
|
|
}) = _DHTSchemaMember;
|
2023-05-29 19:24:57 +00:00
|
|
|
|
2023-07-06 03:53:08 +00:00
|
|
|
factory DHTSchemaMember.fromJson(Map<String, dynamic> json) =>
|
|
|
|
_$DHTSchemaMemberFromJson(json);
|
2023-05-29 19:24:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////
|
|
|
|
/// DHTRecordDescriptor
|
|
|
|
|
2023-07-06 15:41:38 +00:00
|
|
|
@freezed
|
|
|
|
class DHTRecordDescriptor with _$DHTRecordDescriptor {
|
|
|
|
const factory DHTRecordDescriptor({
|
|
|
|
required TypedKey key,
|
|
|
|
required PublicKey owner,
|
|
|
|
PublicKey? ownerSecret,
|
|
|
|
required DHTSchema schema,
|
|
|
|
}) = _DHTRecordDescriptor;
|
|
|
|
factory DHTRecordDescriptor.fromJson(Map<String, dynamic> json) =>
|
|
|
|
_$DHTRecordDescriptorFromJson(json);
|
2023-05-29 19:24:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////
|
|
|
|
/// ValueSubkeyRange
|
|
|
|
|
2023-07-06 15:41:38 +00:00
|
|
|
@freezed
|
|
|
|
class ValueSubkeyRange with _$ValueSubkeyRange {
|
|
|
|
@Assert('low < 0 || low > high', 'low out of range')
|
|
|
|
@Assert('high < 0', 'high out of range')
|
|
|
|
const factory ValueSubkeyRange({
|
|
|
|
required int low,
|
|
|
|
required int high,
|
|
|
|
}) = _ValueSubkeyRange;
|
|
|
|
|
|
|
|
factory ValueSubkeyRange.fromJson(Map<String, dynamic> json) =>
|
|
|
|
_$ValueSubkeyRangeFromJson(json);
|
2023-05-29 19:24:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////
|
|
|
|
/// ValueData
|
|
|
|
|
2023-07-06 15:41:38 +00:00
|
|
|
@freezed
|
|
|
|
class ValueData with _$ValueData {
|
|
|
|
@Assert('seq >= 0', 'seq out of range')
|
|
|
|
const factory ValueData({
|
|
|
|
required int seq,
|
|
|
|
@Uint8ListJsonConverter() required Uint8List data,
|
|
|
|
required PublicKey writer,
|
|
|
|
}) = _ValueData;
|
|
|
|
|
|
|
|
factory ValueData.fromJson(Map<String, dynamic> json) =>
|
|
|
|
_$ValueDataFromJson(json);
|
2023-05-29 19:24:57 +00:00
|
|
|
}
|
|
|
|
|
2023-06-28 15:40:02 +00:00
|
|
|
//////////////////////////////////////
|
2023-05-29 19:24:57 +00:00
|
|
|
/// Stability
|
|
|
|
|
|
|
|
enum Stability {
|
|
|
|
lowLatency,
|
|
|
|
reliable;
|
|
|
|
|
2023-07-06 15:41:38 +00:00
|
|
|
String toJson() => name.toPascalCase();
|
|
|
|
factory Stability.fromJson(String j) =>
|
|
|
|
Stability.values.byName(j.toCamelCase());
|
2023-05-29 19:24:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////
|
|
|
|
/// Sequencing
|
|
|
|
|
|
|
|
enum Sequencing {
|
|
|
|
noPreference,
|
|
|
|
preferOrdered,
|
|
|
|
ensureOrdered;
|
|
|
|
|
2023-07-06 15:41:38 +00:00
|
|
|
String toJson() => name.toPascalCase();
|
|
|
|
factory Sequencing.fromJson(String j) =>
|
|
|
|
Sequencing.values.byName(j.toCamelCase());
|
2023-05-29 19:24:57 +00:00
|
|
|
}
|
|
|
|
|
2023-06-28 15:40:02 +00:00
|
|
|
//////////////////////////////////////
|
|
|
|
/// SafetySelection
|
|
|
|
|
2023-07-06 15:41:38 +00:00
|
|
|
@immutable
|
|
|
|
abstract class SafetySelection extends Equatable {
|
|
|
|
factory SafetySelection.fromJson(Map<String, dynamic> json) {
|
|
|
|
if (json.containsKey("Unsafe")) {
|
2023-06-28 15:40:02 +00:00
|
|
|
return SafetySelectionUnsafe(
|
2023-07-06 15:41:38 +00:00
|
|
|
sequencing: Sequencing.fromJson(json["Unsafe"]));
|
|
|
|
} else if (json.containsKey("Safe")) {
|
|
|
|
return SafetySelectionSafe(safetySpec: SafetySpec.fromJson(json["Safe"]));
|
2023-06-28 15:40:02 +00:00
|
|
|
} else {
|
2023-07-06 15:41:38 +00:00
|
|
|
throw const VeilidAPIExceptionInternal("Invalid SafetySelection");
|
2023-06-28 15:40:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Map<String, dynamic> toJson();
|
|
|
|
}
|
|
|
|
|
2023-07-06 15:41:38 +00:00
|
|
|
@immutable
|
2023-06-28 15:40:02 +00:00
|
|
|
class SafetySelectionUnsafe implements SafetySelection {
|
|
|
|
final Sequencing sequencing;
|
2023-07-06 15:41:38 +00:00
|
|
|
@override
|
|
|
|
List<Object> get props => [sequencing];
|
|
|
|
@override
|
|
|
|
bool? get stringify => null;
|
|
|
|
|
2023-06-28 15:40:02 +00:00
|
|
|
//
|
2023-07-06 15:41:38 +00:00
|
|
|
const SafetySelectionUnsafe({
|
2023-06-28 15:40:02 +00:00
|
|
|
required this.sequencing,
|
|
|
|
});
|
|
|
|
|
|
|
|
@override
|
|
|
|
Map<String, dynamic> toJson() {
|
|
|
|
return {'Unsafe': sequencing.toJson()};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-06 15:41:38 +00:00
|
|
|
@immutable
|
2023-06-28 15:40:02 +00:00
|
|
|
class SafetySelectionSafe implements SafetySelection {
|
|
|
|
final SafetySpec safetySpec;
|
2023-07-06 15:41:38 +00:00
|
|
|
@override
|
|
|
|
List<Object> get props => [safetySpec];
|
|
|
|
@override
|
|
|
|
bool? get stringify => null;
|
|
|
|
|
2023-06-28 15:40:02 +00:00
|
|
|
//
|
2023-07-06 15:41:38 +00:00
|
|
|
const SafetySelectionSafe({
|
2023-06-28 15:40:02 +00:00
|
|
|
required this.safetySpec,
|
|
|
|
});
|
|
|
|
|
|
|
|
@override
|
|
|
|
Map<String, dynamic> toJson() {
|
|
|
|
return {'Safe': safetySpec.toJson()};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Options for safety routes (sender privacy)
|
2023-07-06 15:41:38 +00:00
|
|
|
@freezed
|
|
|
|
class SafetySpec with _$SafetySpec {
|
|
|
|
const factory SafetySpec({
|
|
|
|
String? preferredRoute,
|
|
|
|
required int hopCount,
|
|
|
|
required Stability stability,
|
|
|
|
required Sequencing sequencing,
|
|
|
|
}) = _SafetySpec;
|
|
|
|
|
|
|
|
factory SafetySpec.fromJson(Map<String, dynamic> json) =>
|
|
|
|
_$SafetySpecFromJson(json);
|
2023-06-28 15:40:02 +00:00
|
|
|
}
|
|
|
|
|
2023-05-29 19:24:57 +00:00
|
|
|
//////////////////////////////////////
|
|
|
|
/// RouteBlob
|
2023-07-06 15:41:38 +00:00
|
|
|
@freezed
|
|
|
|
class RouteBlob with _$RouteBlob {
|
|
|
|
const factory RouteBlob(
|
|
|
|
{required String routeId,
|
|
|
|
@Uint8ListJsonConverter() required Uint8List blob}) = _RouteBlob;
|
|
|
|
factory RouteBlob.fromJson(Map<String, dynamic> json) =>
|
|
|
|
_$RouteBlobFromJson(json);
|
2023-05-29 19:24:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////
|
|
|
|
/// VeilidRoutingContext
|
|
|
|
|
|
|
|
abstract class VeilidRoutingContext {
|
|
|
|
// Modifiers
|
|
|
|
VeilidRoutingContext withPrivacy();
|
2023-06-28 15:40:02 +00:00
|
|
|
VeilidRoutingContext withCustomPrivacy(SafetySelection safetySelection);
|
2023-05-29 19:24:57 +00:00
|
|
|
VeilidRoutingContext withSequencing(Sequencing sequencing);
|
|
|
|
|
|
|
|
// App call/message
|
|
|
|
Future<Uint8List> appCall(String target, Uint8List request);
|
|
|
|
Future<void> appMessage(String target, Uint8List message);
|
|
|
|
|
|
|
|
// DHT Operations
|
2023-07-09 02:50:44 +00:00
|
|
|
Future<DHTRecordDescriptor> createDHTRecord(DHTSchema schema,
|
|
|
|
{CryptoKind kind = 0});
|
2023-05-29 19:24:57 +00:00
|
|
|
Future<DHTRecordDescriptor> openDHTRecord(TypedKey key, KeyPair? writer);
|
|
|
|
Future<void> closeDHTRecord(TypedKey key);
|
|
|
|
Future<void> deleteDHTRecord(TypedKey key);
|
|
|
|
Future<ValueData?> getDHTValue(TypedKey key, int subkey, bool forceRefresh);
|
|
|
|
Future<ValueData?> setDHTValue(TypedKey key, int subkey, Uint8List data);
|
2023-06-27 22:26:53 +00:00
|
|
|
Future<Timestamp> watchDHTValues(TypedKey key, List<ValueSubkeyRange> subkeys,
|
|
|
|
Timestamp expiration, int count);
|
|
|
|
Future<bool> cancelDHTWatch(TypedKey key, List<ValueSubkeyRange> subkeys);
|
2023-05-29 19:24:57 +00:00
|
|
|
}
|