Merge branch 'veilidchat-work' into 'main'

0.1.7 release

See merge request veilid/veilid!103
This commit is contained in:
Christien Rioux 2023-07-30 21:23:27 +00:00
commit da76bc9fee
21 changed files with 1341 additions and 1573 deletions

View File

@ -1,3 +1,10 @@
**Changes in Veilid 0.1.7**
- Fix for connection table crash
- Fix for incorrect set_dht_value return value
- Python test updates
- Various VeilidChat-prompted veilid-flutter updates
**Changes in Veilid 0.1.6** **Changes in Veilid 0.1.6**
- Fix for 'find_node' too many nodes returned issue - Fix for 'find_node' too many nodes returned issue

View File

@ -177,10 +177,10 @@ impl ConnectionTable {
// then drop the least recently used connection // then drop the least recently used connection
let mut out_conn = None; let mut out_conn = None;
if inner.conn_by_id[protocol_index].len() > inner.max_connections[protocol_index] { if inner.conn_by_id[protocol_index].len() > inner.max_connections[protocol_index] {
if let Some((lruk, lru_conn)) = inner.conn_by_id[protocol_index].remove_lru() { if let Some((lruk, lru_conn)) = inner.conn_by_id[protocol_index].peek_lru() {
let lruk = *lruk;
log_net!(debug "connection lru out: {:?}", lru_conn); log_net!(debug "connection lru out: {:?}", lru_conn);
out_conn = Some(lru_conn); out_conn = Some(Self::remove_connection_records(&mut *inner, lruk));
Self::remove_connection_records(&mut *inner, lruk);
} }
} }

View File

@ -347,8 +347,9 @@ impl StorageManager {
if last_signed_value_data.value_data().data() == &data if last_signed_value_data.value_data().data() == &data
&& last_signed_value_data.value_data().writer() == &writer.key && last_signed_value_data.value_data().writer() == &writer.key
{ {
// Data and writer is the name, nothing is changing, just return the same ValueData // Data and writer is the same, nothing is changing,
return Ok(Some(last_signed_value_data.into_value_data())); // just return that we set it, but no network activity needs to happen
return Ok(None);
} }
let seq = last_signed_value_data.value_data().seq(); let seq = last_signed_value_data.value_data().seq();
ValueData::new_with_seq(seq + 1, data, writer.key) ValueData::new_with_seq(seq + 1, data, writer.key)

View File

@ -0,0 +1 @@
Ray says "Go SubSix!"

View File

@ -1,5 +1,11 @@
include: package:flutter_lints/flutter.yaml include: package:lint_hard/all.yaml
analyzer: analyzer:
errors: errors:
invalid_annotation_target: ignore invalid_annotation_target: ignore
exclude:
- '**/*.g.dart'
- '**/*.freezed.dart'
linter:
rules:
avoid_positional_boolean_parameters: false

View File

@ -3,3 +3,7 @@ include: package:flutter_lints/flutter.yaml
analyzer: analyzer:
errors: errors:
invalid_annotation_target: ignore invalid_annotation_target: ignore
linter:
rules:
- unawaited_futures

View File

@ -1,10 +1,11 @@
import 'dart:io'; import 'dart:io';
import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as p; import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'package:system_info2/system_info2.dart' as sysinfo; import 'package:system_info2/system_info2.dart' as sysinfo;
import 'package:system_info_plus/system_info_plus.dart'; import 'package:system_info_plus/system_info_plus.dart';
import 'veilid.dart'; import 'veilid.dart';
const int megaByte = 1024 * 1024; const int megaByte = 1024 * 1024;
@ -57,136 +58,129 @@ int getRemoteMaxStorageSpaceMb() {
return 256; return 256;
} }
Future<VeilidConfig> getDefaultVeilidConfig(String programName) async { Future<VeilidConfig> getDefaultVeilidConfig(String programName) async =>
return VeilidConfig( VeilidConfig(
programName: programName, programName: programName,
namespace: "", namespace: '',
capabilities: const VeilidConfigCapabilities(disable: []), capabilities: const VeilidConfigCapabilities(disable: []),
protectedStore: const VeilidConfigProtectedStore( protectedStore: const VeilidConfigProtectedStore(
allowInsecureFallback: false, allowInsecureFallback: false,
alwaysUseInsecureStorage: false, alwaysUseInsecureStorage: false,
directory: "", directory: '',
delete: false, delete: false,
deviceEncryptionKeyPassword: "", deviceEncryptionKeyPassword: '',
newDeviceEncryptionKeyPassword: null,
),
tableStore: VeilidConfigTableStore(
directory: kIsWeb
? ""
: p.join((await getApplicationSupportDirectory()).absolute.path,
"table_store"),
delete: false,
),
blockStore: VeilidConfigBlockStore(
directory: kIsWeb
? ""
: p.join((await getApplicationSupportDirectory()).absolute.path,
"block_store"),
delete: false,
),
network: VeilidConfigNetwork(
connectionInitialTimeoutMs: 2000,
connectionInactivityTimeoutMs: 60000,
maxConnectionsPerIp4: 32,
maxConnectionsPerIp6Prefix: 32,
maxConnectionsPerIp6PrefixSize: 56,
maxConnectionFrequencyPerMin: 128,
clientWhitelistTimeoutMs: 300000,
reverseConnectionReceiptTimeMs: 5000,
holePunchReceiptTimeMs: 5000,
routingTable: const VeilidConfigRoutingTable(
nodeId: [],
nodeIdSecret: [],
bootstrap: kIsWeb
? ["ws://bootstrap.veilid.net:5150/ws"]
: ["bootstrap.veilid.net"],
limitOverAttached: 64,
limitFullyAttached: 32,
limitAttachedStrong: 16,
limitAttachedGood: 8,
limitAttachedWeak: 4,
), ),
rpc: const VeilidConfigRPC( tableStore: VeilidConfigTableStore(
concurrency: 0, directory: kIsWeb
queueSize: 1024, ? ''
maxTimestampBehindMs: 10000, : p.join((await getApplicationSupportDirectory()).absolute.path,
maxTimestampAheadMs: 10000, 'table_store'),
timeoutMs: 5000, delete: false,
maxRouteHopCount: 4,
defaultRouteHopCount: 1,
), ),
dht: VeilidConfigDHT( blockStore: VeilidConfigBlockStore(
resolveNodeTimeoutMs: 10000, directory: kIsWeb
resolveNodeCount: 20, ? ''
resolveNodeFanout: 3, : p.join((await getApplicationSupportDirectory()).absolute.path,
maxFindNodeCount: 20, 'block_store'),
getValueTimeoutMs: 10000, delete: false,
getValueCount: 20, ),
getValueFanout: 3, network: VeilidConfigNetwork(
setValueTimeoutMs: 10000,
setValueCount: 20,
setValueFanout: 5,
minPeerCount: 20,
minPeerRefreshTimeMs: 60000,
validateDialInfoReceiptTimeMs: 2000,
localSubkeyCacheSize: getLocalSubkeyCacheSize(),
localMaxSubkeyCacheMemoryMb: await getLocalMaxSubkeyCacheMemoryMb(),
remoteSubkeyCacheSize: getRemoteSubkeyCacheSize(),
remoteMaxRecords: getRemoteMaxRecords(),
remoteMaxSubkeyCacheMemoryMb: await getRemoteMaxSubkeyCacheMemoryMb(),
remoteMaxStorageSpaceMb: getRemoteMaxStorageSpaceMb()),
upnp: true,
detectAddressChanges: true,
restrictedNatRetries: 0,
tls: const VeilidConfigTLS(
certificatePath: "",
privateKeyPath: "",
connectionInitialTimeoutMs: 2000, connectionInitialTimeoutMs: 2000,
), connectionInactivityTimeoutMs: 60000,
application: const VeilidConfigApplication( maxConnectionsPerIp4: 32,
https: VeilidConfigHTTPS( maxConnectionsPerIp6Prefix: 32,
enabled: false, maxConnectionsPerIp6PrefixSize: 56,
listenAddress: "", maxConnectionFrequencyPerMin: 128,
path: "", clientWhitelistTimeoutMs: 300000,
url: null, reverseConnectionReceiptTimeMs: 5000,
holePunchReceiptTimeMs: 5000,
routingTable: const VeilidConfigRoutingTable(
nodeId: [],
nodeIdSecret: [],
bootstrap: kIsWeb
? ['ws://bootstrap.veilid.net:5150/ws']
: ['bootstrap.veilid.net'],
limitOverAttached: 64,
limitFullyAttached: 32,
limitAttachedStrong: 16,
limitAttachedGood: 8,
limitAttachedWeak: 4,
),
rpc: const VeilidConfigRPC(
concurrency: 0,
queueSize: 1024,
maxTimestampBehindMs: 10000,
maxTimestampAheadMs: 10000,
timeoutMs: 5000,
maxRouteHopCount: 4,
defaultRouteHopCount: 1,
),
dht: VeilidConfigDHT(
resolveNodeTimeoutMs: 10000,
resolveNodeCount: 20,
resolveNodeFanout: 3,
maxFindNodeCount: 20,
getValueTimeoutMs: 10000,
getValueCount: 20,
getValueFanout: 3,
setValueTimeoutMs: 10000,
setValueCount: 20,
setValueFanout: 5,
minPeerCount: 20,
minPeerRefreshTimeMs: 60000,
validateDialInfoReceiptTimeMs: 2000,
localSubkeyCacheSize: getLocalSubkeyCacheSize(),
localMaxSubkeyCacheMemoryMb: await getLocalMaxSubkeyCacheMemoryMb(),
remoteSubkeyCacheSize: getRemoteSubkeyCacheSize(),
remoteMaxRecords: getRemoteMaxRecords(),
remoteMaxSubkeyCacheMemoryMb:
await getRemoteMaxSubkeyCacheMemoryMb(),
remoteMaxStorageSpaceMb: getRemoteMaxStorageSpaceMb()),
upnp: true,
detectAddressChanges: true,
restrictedNatRetries: 0,
tls: const VeilidConfigTLS(
certificatePath: '',
privateKeyPath: '',
connectionInitialTimeoutMs: 2000,
),
application: const VeilidConfigApplication(
https: VeilidConfigHTTPS(
enabled: false,
listenAddress: '',
path: '',
),
http: VeilidConfigHTTP(
enabled: false,
listenAddress: '',
path: '',
)),
protocol: const VeilidConfigProtocol(
udp: VeilidConfigUDP(
enabled: !kIsWeb,
socketPoolSize: 0,
listenAddress: '',
),
tcp: VeilidConfigTCP(
connect: !kIsWeb,
listen: !kIsWeb,
maxConnections: 32,
listenAddress: '',
),
ws: VeilidConfigWS(
connect: true,
listen: !kIsWeb,
maxConnections: 16,
listenAddress: '',
path: 'ws',
),
wss: VeilidConfigWSS(
connect: true,
listen: false,
maxConnections: 16,
listenAddress: '',
path: 'ws',
), ),
http: VeilidConfigHTTP(
enabled: false,
listenAddress: "",
path: "",
url: null,
)),
protocol: const VeilidConfigProtocol(
udp: VeilidConfigUDP(
enabled: !kIsWeb,
socketPoolSize: 0,
listenAddress: "",
publicAddress: null,
),
tcp: VeilidConfigTCP(
connect: !kIsWeb,
listen: !kIsWeb,
maxConnections: 32,
listenAddress: "",
publicAddress: null,
),
ws: VeilidConfigWS(
connect: true,
listen: !kIsWeb,
maxConnections: 16,
listenAddress: "",
path: "ws",
url: null,
),
wss: VeilidConfigWSS(
connect: true,
listen: false,
maxConnections: 16,
listenAddress: "",
path: "ws",
url: null,
), ),
), ),
), );
);
}

View File

@ -26,7 +26,7 @@ extension ValidateDFLT on DHTSchemaDFLT {
extension ValidateSMPL on DHTSchemaSMPL { extension ValidateSMPL on DHTSchemaSMPL {
bool validate() { bool validate() {
final totalsv = members.fold(0, (acc, v) => (acc + v.mCnt)) + oCnt; final totalsv = members.fold(0, (acc, v) => acc + v.mCnt) + oCnt;
if (totalsv > 65535) { if (totalsv > 65535) {
return false; return false;
} }
@ -76,8 +76,8 @@ class DHTRecordDescriptor with _$DHTRecordDescriptor {
const factory DHTRecordDescriptor({ const factory DHTRecordDescriptor({
required TypedKey key, required TypedKey key,
required PublicKey owner, required PublicKey owner,
PublicKey? ownerSecret,
required DHTSchema schema, required DHTSchema schema,
PublicKey? ownerSecret,
}) = _DHTRecordDescriptor; }) = _DHTRecordDescriptor;
factory DHTRecordDescriptor.fromJson(dynamic json) => factory DHTRecordDescriptor.fromJson(dynamic json) =>
_$DHTRecordDescriptorFromJson(json as Map<String, dynamic>); _$DHTRecordDescriptorFromJson(json as Map<String, dynamic>);
@ -138,9 +138,9 @@ enum Stability {
lowLatency, lowLatency,
reliable; reliable;
String toJson() => name.toPascalCase();
factory Stability.fromJson(dynamic j) => factory Stability.fromJson(dynamic j) =>
Stability.values.byName((j as String).toCamelCase()); Stability.values.byName((j as String).toCamelCase());
String toJson() => name.toPascalCase();
} }
////////////////////////////////////// //////////////////////////////////////
@ -151,76 +151,70 @@ enum Sequencing {
preferOrdered, preferOrdered,
ensureOrdered; ensureOrdered;
String toJson() => name.toPascalCase();
factory Sequencing.fromJson(dynamic j) => factory Sequencing.fromJson(dynamic j) =>
Sequencing.values.byName((j as String).toCamelCase()); Sequencing.values.byName((j as String).toCamelCase());
String toJson() => name.toPascalCase();
} }
////////////////////////////////////// //////////////////////////////////////
/// SafetySelection /// SafetySelection
@immutable @immutable
abstract class SafetySelection extends Equatable { abstract class SafetySelection {
factory SafetySelection.fromJson(dynamic jsond) { factory SafetySelection.fromJson(dynamic jsond) {
final json = jsond as Map<String, dynamic>; final json = jsond as Map<String, dynamic>;
if (json.containsKey("Unsafe")) { if (json.containsKey('Unsafe')) {
return SafetySelectionUnsafe( return SafetySelectionUnsafe(
sequencing: Sequencing.fromJson(json["Unsafe"])); sequencing: Sequencing.fromJson(json['Unsafe']));
} else if (json.containsKey("Safe")) { } else if (json.containsKey('Safe')) {
return SafetySelectionSafe(safetySpec: SafetySpec.fromJson(json["Safe"])); return SafetySelectionSafe(safetySpec: SafetySpec.fromJson(json['Safe']));
} else { } else {
throw const VeilidAPIExceptionInternal("Invalid SafetySelection"); throw const VeilidAPIExceptionInternal('Invalid SafetySelection');
} }
} }
Map<String, dynamic> toJson(); Map<String, dynamic> toJson();
} }
@immutable @immutable
class SafetySelectionUnsafe implements SafetySelection { class SafetySelectionUnsafe extends Equatable implements SafetySelection {
//
const SafetySelectionUnsafe({
required this.sequencing,
});
final Sequencing sequencing; final Sequencing sequencing;
@override @override
List<Object> get props => [sequencing]; List<Object> get props => [sequencing];
@override @override
bool? get stringify => null; bool? get stringify => null;
//
const SafetySelectionUnsafe({
required this.sequencing,
});
@override @override
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() => {'Unsafe': sequencing.toJson()};
return {'Unsafe': sequencing.toJson()};
}
} }
@immutable @immutable
class SafetySelectionSafe implements SafetySelection { class SafetySelectionSafe extends Equatable implements SafetySelection {
//
const SafetySelectionSafe({
required this.safetySpec,
});
final SafetySpec safetySpec; final SafetySpec safetySpec;
@override @override
List<Object> get props => [safetySpec]; List<Object> get props => [safetySpec];
@override @override
bool? get stringify => null; bool? get stringify => null;
//
const SafetySelectionSafe({
required this.safetySpec,
});
@override @override
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() => {'Safe': safetySpec.toJson()};
return {'Safe': safetySpec.toJson()};
}
} }
/// Options for safety routes (sender privacy) /// Options for safety routes (sender privacy)
@freezed @freezed
class SafetySpec with _$SafetySpec { class SafetySpec with _$SafetySpec {
const factory SafetySpec({ const factory SafetySpec({
String? preferredRoute,
required int hopCount, required int hopCount,
required Stability stability, required Stability stability,
required Sequencing sequencing, required Sequencing sequencing,
String? preferredRoute,
}) = _SafetySpec; }) = _SafetySpec;
factory SafetySpec.fromJson(dynamic json) => factory SafetySpec.fromJson(dynamic json) =>

View File

@ -4,28 +4,26 @@ import 'dart:typed_data';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'veilid_stub.dart'
if (dart.library.io) 'veilid_ffi.dart'
if (dart.library.js) 'veilid_js.dart';
////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////
import 'routing_context.dart'; import 'routing_context.dart';
import 'veilid_config.dart'; import 'veilid_config.dart';
import 'veilid_crypto.dart'; import 'veilid_crypto.dart';
import 'veilid_table_db.dart';
import 'veilid_state.dart'; import 'veilid_state.dart';
import 'veilid_stub.dart'
if (dart.library.io) 'veilid_ffi.dart'
if (dart.library.js) 'veilid_js.dart';
import 'veilid_table_db.dart';
export 'default_config.dart'; export 'default_config.dart';
export 'routing_context.dart'; export 'routing_context.dart';
export 'veilid.dart';
export 'veilid_encoding.dart'; export 'veilid_api_exception.dart';
export 'veilid_config.dart'; export 'veilid_config.dart';
export 'veilid_crypto.dart'; export 'veilid_crypto.dart';
export 'veilid_table_db.dart'; export 'veilid_encoding.dart';
export 'veilid_api_exception.dart';
export 'veilid_state.dart'; export 'veilid_state.dart';
export 'veilid.dart'; export 'veilid_table_db.dart';
////////////////////////////////////// //////////////////////////////////////
/// JSON Encode Helper /// JSON Encode Helper
@ -41,57 +39,39 @@ Object? veilidApiToEncodable(Object? value) {
throw UnsupportedError('Cannot convert to JSON: $value'); throw UnsupportedError('Cannot convert to JSON: $value');
} }
T? Function(dynamic) optFromJson<T>(
T Function(Map<String, dynamic>) jsonConstructor) {
return (dynamic j) {
if (j == null) {
return null;
} else {
return jsonConstructor(j);
}
};
}
List<T> Function(dynamic) jsonListConstructor<T>( List<T> Function(dynamic) jsonListConstructor<T>(
T Function(Map<String, dynamic>) jsonConstructor) { T Function(dynamic) jsonConstructor) =>
return (dynamic j) { (dynamic j) => (j as List<dynamic>).map((e) => jsonConstructor(e)).toList();
return (j as List<Map<String, dynamic>>)
.map((e) => jsonConstructor(e))
.toList();
};
}
////////////////////////////////////// //////////////////////////////////////
/// VeilidVersion /// VeilidVersion
@immutable @immutable
class VeilidVersion extends Equatable { class VeilidVersion extends Equatable {
const VeilidVersion(this.major, this.minor, this.patch);
final int major; final int major;
final int minor; final int minor;
final int patch; final int patch;
@override @override
List<Object> get props => [major, minor, patch]; List<Object> get props => [major, minor, patch];
const VeilidVersion(this.major, this.minor, this.patch);
} }
////////////////////////////////////// //////////////////////////////////////
/// Timestamp /// Timestamp
@immutable @immutable
class Timestamp extends Equatable { class Timestamp extends Equatable {
const Timestamp({required this.value});
factory Timestamp.fromString(String s) => Timestamp(value: BigInt.parse(s));
factory Timestamp.fromJson(dynamic json) =>
Timestamp.fromString(json as String);
final BigInt value; final BigInt value;
@override @override
List<Object> get props => [value]; List<Object> get props => [value];
const Timestamp({required this.value});
@override @override
String toString() => value.toString(); String toString() => value.toString();
factory Timestamp.fromString(String s) => Timestamp(value: BigInt.parse(s));
String toJson() => toString(); String toJson() => toString();
factory Timestamp.fromJson(dynamic json) =>
Timestamp.fromString(json as String);
TimestampDuration diff(Timestamp other) => TimestampDuration diff(Timestamp other) =>
TimestampDuration(value: value - other.value); TimestampDuration(value: value - other.value);
@ -102,20 +82,19 @@ class Timestamp extends Equatable {
@immutable @immutable
class TimestampDuration extends Equatable { class TimestampDuration extends Equatable {
const TimestampDuration({required this.value});
factory TimestampDuration.fromString(String s) =>
TimestampDuration(value: BigInt.parse(s));
factory TimestampDuration.fromJson(dynamic json) =>
TimestampDuration.fromString(json as String);
final BigInt value; final BigInt value;
@override @override
List<Object> get props => [value]; List<Object> get props => [value];
const TimestampDuration({required this.value});
@override @override
String toString() => value.toString(); String toString() => value.toString();
factory TimestampDuration.fromString(String s) =>
TimestampDuration(value: BigInt.parse(s));
String toJson() => toString(); String toJson() => toString();
factory TimestampDuration.fromJson(dynamic json) =>
TimestampDuration.fromString(json as String);
int toMillis() => (value ~/ BigInt.from(1000)).toInt(); int toMillis() => (value ~/ BigInt.from(1000)).toInt();
BigInt toMicros() => value; BigInt toMicros() => value;

View File

@ -5,70 +5,73 @@ import 'package:freezed_annotation/freezed_annotation.dart';
@immutable @immutable
abstract class VeilidAPIException implements Exception { abstract class VeilidAPIException implements Exception {
factory VeilidAPIException.fromJson(dynamic json) { factory VeilidAPIException.fromJson(dynamic j) {
switch (json["kind"]) { final json = j as Map<String, dynamic>;
case "NotInitialized": switch (json['kind']! as String) {
case 'NotInitialized':
{ {
return VeilidAPIExceptionNotInitialized(); return VeilidAPIExceptionNotInitialized();
} }
case "AlreadyInitialized": case 'AlreadyInitialized':
{ {
return VeilidAPIExceptionAlreadyInitialized(); return VeilidAPIExceptionAlreadyInitialized();
} }
case "Timeout": case 'Timeout':
{ {
return VeilidAPIExceptionTimeout(); return VeilidAPIExceptionTimeout();
} }
case "TryAgain": case 'TryAgain':
{ {
return VeilidAPIExceptionTryAgain(); return VeilidAPIExceptionTryAgain();
} }
case "Shutdown": case 'Shutdown':
{ {
return VeilidAPIExceptionShutdown(); return VeilidAPIExceptionShutdown();
} }
case "InvalidTarget": case 'InvalidTarget':
{ {
return VeilidAPIExceptionInvalidTarget(); return VeilidAPIExceptionInvalidTarget();
} }
case "NoConnection": case 'NoConnection':
{ {
return VeilidAPIExceptionNoConnection(json["message"]); return VeilidAPIExceptionNoConnection(json['message']! as String);
} }
case "KeyNotFound": case 'KeyNotFound':
{ {
return VeilidAPIExceptionKeyNotFound(json["key"]); return VeilidAPIExceptionKeyNotFound(json['key']! as String);
} }
case "Internal": case 'Internal':
{ {
return VeilidAPIExceptionInternal(json["message"]); return VeilidAPIExceptionInternal(json['message']! as String);
} }
case "Unimplemented": case 'Unimplemented':
{ {
return VeilidAPIExceptionUnimplemented(json["unimplemented"]); return VeilidAPIExceptionUnimplemented(
json['unimplemented']! as String);
} }
case "ParseError": case 'ParseError':
{ {
return VeilidAPIExceptionParseError(json["message"], json["value"]); return VeilidAPIExceptionParseError(
json['message']! as String, json['value']! as String);
} }
case "InvalidArgument": case 'InvalidArgument':
{ {
return VeilidAPIExceptionInvalidArgument( return VeilidAPIExceptionInvalidArgument(json['context']! as String,
json["context"], json["argument"], json["value"]); json['argument']! as String, json['value']! as String);
} }
case "MissingArgument": case 'MissingArgument':
{ {
return VeilidAPIExceptionMissingArgument( return VeilidAPIExceptionMissingArgument(
json["context"], json["argument"]); json['context']! as String, json['argument']! as String);
} }
case "Generic": case 'Generic':
{ {
return VeilidAPIExceptionGeneric(json["message"]); return VeilidAPIExceptionGeneric(json['message']! as String);
} }
default: default:
{ {
throw VeilidAPIExceptionInternal( throw VeilidAPIExceptionInternal(
"Invalid VeilidAPIException type: ${json['kind']}"); "Invalid VeilidAPIException type: ${json['kind']! as String}");
} }
} }
} }
@ -79,224 +82,163 @@ abstract class VeilidAPIException implements Exception {
@immutable @immutable
class VeilidAPIExceptionNotInitialized implements VeilidAPIException { class VeilidAPIExceptionNotInitialized implements VeilidAPIException {
@override @override
String toString() { String toString() => 'VeilidAPIException: NotInitialized';
return "VeilidAPIException: NotInitialized";
}
@override @override
String toDisplayError() { String toDisplayError() => 'Not initialized';
return "Not initialized";
}
} }
@immutable @immutable
class VeilidAPIExceptionAlreadyInitialized implements VeilidAPIException { class VeilidAPIExceptionAlreadyInitialized implements VeilidAPIException {
@override @override
String toString() { String toString() => 'VeilidAPIException: AlreadyInitialized';
return "VeilidAPIException: AlreadyInitialized";
}
@override @override
String toDisplayError() { String toDisplayError() => 'Already initialized';
return "Already initialized";
}
} }
@immutable @immutable
class VeilidAPIExceptionTimeout implements VeilidAPIException { class VeilidAPIExceptionTimeout implements VeilidAPIException {
@override @override
String toString() { String toString() => 'VeilidAPIException: Timeout';
return "VeilidAPIException: Timeout";
}
@override @override
String toDisplayError() { String toDisplayError() => 'Timeout';
return "Timeout";
}
} }
@immutable @immutable
class VeilidAPIExceptionTryAgain implements VeilidAPIException { class VeilidAPIExceptionTryAgain implements VeilidAPIException {
@override @override
String toString() { String toString() => 'VeilidAPIException: TryAgain';
return "VeilidAPIException: TryAgain";
}
@override @override
String toDisplayError() { String toDisplayError() => 'Try again';
return "Try again";
}
} }
@immutable @immutable
class VeilidAPIExceptionShutdown implements VeilidAPIException { class VeilidAPIExceptionShutdown implements VeilidAPIException {
@override @override
String toString() { String toString() => 'VeilidAPIException: Shutdown';
return "VeilidAPIException: Shutdown";
}
@override @override
String toDisplayError() { String toDisplayError() => 'Currently shut down';
return "Currently shut down";
}
} }
@immutable @immutable
class VeilidAPIExceptionInvalidTarget implements VeilidAPIException { class VeilidAPIExceptionInvalidTarget implements VeilidAPIException {
@override @override
String toString() { String toString() => 'VeilidAPIException: InvalidTarget';
return "VeilidAPIException: InvalidTarget";
}
@override @override
String toDisplayError() { String toDisplayError() => 'Invalid target';
return "Invalid target";
}
} }
@immutable @immutable
class VeilidAPIExceptionNoConnection implements VeilidAPIException { class VeilidAPIExceptionNoConnection implements VeilidAPIException {
final String message;
@override
String toString() {
return "VeilidAPIException: NoConnection (message: $message)";
}
@override
String toDisplayError() {
return "No connection: $message";
}
// //
const VeilidAPIExceptionNoConnection(this.message); const VeilidAPIExceptionNoConnection(this.message);
final String message;
@override
String toString() => 'VeilidAPIException: NoConnection (message: $message)';
@override
String toDisplayError() => 'No connection: $message';
} }
@immutable @immutable
class VeilidAPIExceptionKeyNotFound implements VeilidAPIException { class VeilidAPIExceptionKeyNotFound implements VeilidAPIException {
final String key;
@override
String toString() {
return "VeilidAPIException: KeyNotFound (key: $key)";
}
@override
String toDisplayError() {
return "Key not found: $key";
}
// //
const VeilidAPIExceptionKeyNotFound(this.key); const VeilidAPIExceptionKeyNotFound(this.key);
final String key;
@override
String toString() => 'VeilidAPIException: KeyNotFound (key: $key)';
@override
String toDisplayError() => 'Key not found: $key';
} }
@immutable @immutable
class VeilidAPIExceptionInternal implements VeilidAPIException { class VeilidAPIExceptionInternal implements VeilidAPIException {
//
const VeilidAPIExceptionInternal(this.message);
final String message; final String message;
@override @override
String toString() { String toString() => 'VeilidAPIException: Internal ($message)';
return "VeilidAPIException: Internal ($message)";
}
@override @override
String toDisplayError() { String toDisplayError() => 'Internal error: $message';
return "Internal error: $message";
}
//
const VeilidAPIExceptionInternal(this.message);
} }
@immutable @immutable
class VeilidAPIExceptionUnimplemented implements VeilidAPIException { class VeilidAPIExceptionUnimplemented implements VeilidAPIException {
//
const VeilidAPIExceptionUnimplemented(this.message);
final String message; final String message;
@override @override
String toString() { String toString() => 'VeilidAPIException: Unimplemented ($message)';
return "VeilidAPIException: Unimplemented ($message)";
}
@override @override
String toDisplayError() { String toDisplayError() => 'Unimplemented: $message';
return "Unimplemented: $message";
}
//
const VeilidAPIExceptionUnimplemented(this.message);
} }
@immutable @immutable
class VeilidAPIExceptionParseError implements VeilidAPIException { class VeilidAPIExceptionParseError implements VeilidAPIException {
//
const VeilidAPIExceptionParseError(this.message, this.value);
final String message; final String message;
final String value; final String value;
@override @override
String toString() { String toString() =>
return "VeilidAPIException: ParseError ($message)\n value: $value"; 'VeilidAPIException: ParseError ($message)\n value: $value';
}
@override @override
String toDisplayError() { String toDisplayError() => 'Parse error: $message';
return "Parse error: $message";
}
//
const VeilidAPIExceptionParseError(this.message, this.value);
} }
@immutable @immutable
class VeilidAPIExceptionInvalidArgument implements VeilidAPIException { class VeilidAPIExceptionInvalidArgument implements VeilidAPIException {
//
const VeilidAPIExceptionInvalidArgument(
this.context, this.argument, this.value);
final String context; final String context;
final String argument; final String argument;
final String value; final String value;
@override @override
String toString() { String toString() => 'VeilidAPIException: InvalidArgument'
return "VeilidAPIException: InvalidArgument ($context:$argument)\n value: $value"; ' ($context:$argument)\n value: $value';
}
@override @override
String toDisplayError() { String toDisplayError() => 'Invalid argument for $context: $argument';
return "Invalid argument for $context: $argument";
}
//
const VeilidAPIExceptionInvalidArgument(
this.context, this.argument, this.value);
} }
@immutable @immutable
class VeilidAPIExceptionMissingArgument implements VeilidAPIException { class VeilidAPIExceptionMissingArgument implements VeilidAPIException {
//
const VeilidAPIExceptionMissingArgument(this.context, this.argument);
final String context; final String context;
final String argument; final String argument;
@override @override
String toString() { String toString() =>
return "VeilidAPIException: MissingArgument ($context:$argument)"; 'VeilidAPIException: MissingArgument ($context:$argument)';
}
@override @override
String toDisplayError() { String toDisplayError() => 'Missing argument for $context: $argument';
return "Missing argument for $context: $argument";
}
//
const VeilidAPIExceptionMissingArgument(this.context, this.argument);
} }
@immutable @immutable
class VeilidAPIExceptionGeneric implements VeilidAPIException { class VeilidAPIExceptionGeneric implements VeilidAPIException {
//
const VeilidAPIExceptionGeneric(this.message);
final String message; final String message;
@override @override
String toString() { String toString() => 'VeilidAPIException: Generic (message: $message)';
return "VeilidAPIException: Generic (message: $message)";
}
@override @override
String toDisplayError() { String toDisplayError() => message;
return message;
}
//
const VeilidAPIExceptionGeneric(this.message);
} }

View File

@ -1,9 +1,8 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter/foundation.dart';
import 'package:change_case/change_case.dart'; import 'package:change_case/change_case.dart';
import 'package:flutter/foundation.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'veilid.dart'; import 'veilid.dart';
import 'veilid_encoding.dart';
import 'veilid_crypto.dart';
part 'veilid_config.freezed.dart'; part 'veilid_config.freezed.dart';
part 'veilid_config.g.dart'; part 'veilid_config.g.dart';
@ -126,13 +125,9 @@ enum VeilidConfigLogLevel {
debug, debug,
trace; trace;
String toJson() { factory VeilidConfigLogLevel.fromJson(dynamic j) =>
return name.toPascalCase(); VeilidConfigLogLevel.values.byName((j as String).toCamelCase());
} String toJson() => name.toPascalCase();
factory VeilidConfigLogLevel.fromJson(dynamic j) {
return VeilidConfigLogLevel.values.byName((j as String).toCamelCase());
}
} }
////////////////////////////////////// //////////////////////////////////////
@ -300,11 +295,11 @@ class VeilidConfigRPC with _$VeilidConfigRPC {
const factory VeilidConfigRPC( const factory VeilidConfigRPC(
{required int concurrency, {required int concurrency,
required int queueSize, required int queueSize,
int? maxTimestampBehindMs,
int? maxTimestampAheadMs,
required int timeoutMs, required int timeoutMs,
required int maxRouteHopCount, required int maxRouteHopCount,
required int defaultRouteHopCount}) = _VeilidConfigRPC; required int defaultRouteHopCount,
int? maxTimestampBehindMs,
int? maxTimestampAheadMs}) = _VeilidConfigRPC;
factory VeilidConfigRPC.fromJson(dynamic json) => factory VeilidConfigRPC.fromJson(dynamic json) =>
_$VeilidConfigRPCFromJson(json as Map<String, dynamic>); _$VeilidConfigRPCFromJson(json as Map<String, dynamic>);
@ -343,7 +338,6 @@ class VeilidConfigNetwork with _$VeilidConfigNetwork {
required int clientWhitelistTimeoutMs, required int clientWhitelistTimeoutMs,
required int reverseConnectionReceiptTimeMs, required int reverseConnectionReceiptTimeMs,
required int holePunchReceiptTimeMs, required int holePunchReceiptTimeMs,
String? networkKeyPassword,
required VeilidConfigRoutingTable routingTable, required VeilidConfigRoutingTable routingTable,
required VeilidConfigRPC rpc, required VeilidConfigRPC rpc,
required VeilidConfigDHT dht, required VeilidConfigDHT dht,
@ -353,6 +347,7 @@ class VeilidConfigNetwork with _$VeilidConfigNetwork {
required VeilidConfigTLS tls, required VeilidConfigTLS tls,
required VeilidConfigApplication application, required VeilidConfigApplication application,
required VeilidConfigProtocol protocol, required VeilidConfigProtocol protocol,
String? networkKeyPassword,
}) = _VeilidConfigNetwork; }) = _VeilidConfigNetwork;
factory VeilidConfigNetwork.fromJson(dynamic json) => factory VeilidConfigNetwork.fromJson(dynamic json) =>

View File

@ -5,7 +5,6 @@ import 'package:charcode/charcode.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'veilid_encoding.dart';
import 'veilid.dart'; import 'veilid.dart';
////////////////////////////////////// //////////////////////////////////////
@ -17,24 +16,23 @@ const CryptoKind cryptoKindVLD0 =
const CryptoKind cryptoKindNONE = const CryptoKind cryptoKindNONE =
$N << 0 | $O << 8 | $N << 16 | $E << 24; // "NONE" $N << 0 | $O << 8 | $N << 16 | $E << 24; // "NONE"
String cryptoKindToString(CryptoKind kind) { String cryptoKindToString(CryptoKind kind) =>
return cryptoKindToBytes(kind).map((c) => String.fromCharCode(c)).join(); cryptoKindToBytes(kind).map(String.fromCharCode).join();
}
const CryptoKind bestCryptoKind = cryptoKindVLD0; const CryptoKind bestCryptoKind = cryptoKindVLD0;
Uint8List cryptoKindToBytes(CryptoKind kind) { Uint8List cryptoKindToBytes(CryptoKind kind) {
var b = Uint8List(4); final b = Uint8List(4);
ByteData.sublistView(b).setUint32(0, kind, Endian.big); ByteData.sublistView(b).setUint32(0, kind);
return b; return b;
} }
CryptoKind cryptoKindFromString(String s) { CryptoKind cryptoKindFromString(String s) {
if (s.codeUnits.length != 4) { if (s.codeUnits.length != 4) {
throw const FormatException("malformed string"); throw const FormatException('malformed string');
} }
CryptoKind kind = ByteData.sublistView(Uint8List.fromList(s.codeUnits)) final kind =
.getUint32(0, Endian.big); ByteData.sublistView(Uint8List.fromList(s.codeUnits)).getUint32(0);
return kind; return kind;
} }
@ -43,103 +41,95 @@ CryptoKind cryptoKindFromString(String s) {
@immutable @immutable
class Typed<V extends EncodedString> extends Equatable { class Typed<V extends EncodedString> extends Equatable {
const Typed({required this.kind, required this.value});
factory Typed.fromString(String s) {
final parts = s.split(':');
if (parts.length < 2 || parts[0].codeUnits.length != 4) {
throw const FormatException('malformed string');
}
final kind = cryptoKindFromString(parts[0]);
final value = EncodedString.fromString<V>(parts.sublist(1).join(':'));
return Typed(kind: kind, value: value);
}
factory Typed.fromJson(dynamic json) => Typed.fromString(json as String);
final CryptoKind kind; final CryptoKind kind;
final V value; final V value;
@override @override
List<Object> get props => [kind, value]; List<Object> get props => [kind, value];
const Typed({required this.kind, required this.value});
@override @override
String toString() { String toString() => '${cryptoKindToString(kind)}:$value';
return "${cryptoKindToString(kind)}:$value";
}
factory Typed.fromString(String s) {
final parts = s.split(":");
if (parts.length < 2 || parts[0].codeUnits.length != 4) {
throw const FormatException("malformed string");
}
final kind = cryptoKindFromString(parts[0]);
final value = EncodedString.fromString<V>(parts.sublist(1).join(":"));
return Typed(kind: kind, value: value);
}
Uint8List decode() { Uint8List decode() {
var b = BytesBuilder(); final b = BytesBuilder()
b.add(cryptoKindToBytes(kind)); ..add(cryptoKindToBytes(kind))
b.add(value.decode()); ..add(value.decode());
return b.toBytes(); return b.toBytes();
} }
String toJson() => toString(); String toJson() => toString();
factory Typed.fromJson(dynamic json) => Typed.fromString(json as String);
} }
@immutable @immutable
class KeyPair extends Equatable { class KeyPair extends Equatable {
final PublicKey key;
final PublicKey secret;
@override
List<Object> get props => [key, secret];
const KeyPair({required this.key, required this.secret}); const KeyPair({required this.key, required this.secret});
@override
String toString() {
return "${key.toString()}:${secret.toString()}";
}
factory KeyPair.fromString(String s) { factory KeyPair.fromString(String s) {
final parts = s.split(":"); final parts = s.split(':');
if (parts.length != 2 || if (parts.length != 2 ||
parts[0].codeUnits.length != 43 || parts[0].codeUnits.length != 43 ||
parts[1].codeUnits.length != 43) { parts[1].codeUnits.length != 43) {
throw const FormatException("malformed string"); throw const FormatException('malformed string');
} }
final key = PublicKey.fromString(parts[0]); final key = PublicKey.fromString(parts[0]);
final secret = PublicKey.fromString(parts[1]); final secret = PublicKey.fromString(parts[1]);
return KeyPair(key: key, secret: secret); return KeyPair(key: key, secret: secret);
} }
factory KeyPair.fromJson(dynamic json) => KeyPair.fromString(json as String);
final PublicKey key;
final PublicKey secret;
@override
List<Object> get props => [key, secret];
@override
String toString() => '$key:$secret';
String toJson() => toString(); String toJson() => toString();
factory KeyPair.fromJson(dynamic json) => KeyPair.fromString(json as String);
} }
@immutable @immutable
class TypedKeyPair extends Equatable { class TypedKeyPair extends Equatable {
final CryptoKind kind;
final PublicKey key;
final PublicKey secret;
@override
List<Object> get props => [kind, key, secret];
const TypedKeyPair( const TypedKeyPair(
{required this.kind, required this.key, required this.secret}); {required this.kind, required this.key, required this.secret});
@override
String toString() =>
"${cryptoKindToString(kind)}:${key.toString()}:${secret.toString()}";
factory TypedKeyPair.fromString(String s) { factory TypedKeyPair.fromString(String s) {
final parts = s.split(":"); final parts = s.split(':');
if (parts.length != 3 || if (parts.length != 3 ||
parts[0].codeUnits.length != 4 || parts[0].codeUnits.length != 4 ||
parts[1].codeUnits.length != 43 || parts[1].codeUnits.length != 43 ||
parts[2].codeUnits.length != 43) { parts[2].codeUnits.length != 43) {
throw VeilidAPIExceptionInvalidArgument("malformed string", "s", s); throw VeilidAPIExceptionInvalidArgument('malformed string', 's', s);
} }
final kind = cryptoKindFromString(parts[0]); final kind = cryptoKindFromString(parts[0]);
final key = PublicKey.fromString(parts[1]); final key = PublicKey.fromString(parts[1]);
final secret = PublicKey.fromString(parts[2]); final secret = PublicKey.fromString(parts[2]);
return TypedKeyPair(kind: kind, key: key, secret: secret); return TypedKeyPair(kind: kind, key: key, secret: secret);
} }
String toJson() => toString();
factory TypedKeyPair.fromJson(dynamic json) => factory TypedKeyPair.fromJson(dynamic json) =>
TypedKeyPair.fromString(json as String); TypedKeyPair.fromString(json as String);
factory TypedKeyPair.fromKeyPair(CryptoKind kind, KeyPair keyPair) => factory TypedKeyPair.fromKeyPair(CryptoKind kind, KeyPair keyPair) =>
TypedKeyPair(kind: kind, key: keyPair.key, secret: keyPair.secret); TypedKeyPair(kind: kind, key: keyPair.key, secret: keyPair.secret);
final CryptoKind kind;
final PublicKey key;
final PublicKey secret;
@override
List<Object> get props => [kind, key, secret];
@override
String toString() => '${cryptoKindToString(kind)}:$key:$secret';
String toJson() => toString();
} }
typedef CryptoKey = FixedEncodedString43; typedef CryptoKey = FixedEncodedString43;
@ -176,17 +166,15 @@ abstract class VeilidCryptoSystem {
Future<HashDigest> generateHash(Uint8List data); Future<HashDigest> generateHash(Uint8List data);
//Future<HashDigest> generateHashReader(Stream<List<int>> reader); //Future<HashDigest> generateHashReader(Stream<List<int>> reader);
Future<bool> validateKeyPair(PublicKey key, SecretKey secret); Future<bool> validateKeyPair(PublicKey key, SecretKey secret);
Future<bool> validateKeyPairWithKeyPair(KeyPair keyPair) { Future<bool> validateKeyPairWithKeyPair(KeyPair keyPair) =>
return validateKeyPair(keyPair.key, keyPair.secret); validateKeyPair(keyPair.key, keyPair.secret);
}
Future<bool> validateHash(Uint8List data, HashDigest hash); Future<bool> validateHash(Uint8List data, HashDigest hash);
//Future<bool> validateHashReader(Stream<List<int>> reader, HashDigest hash); //Future<bool> validateHashReader(Stream<List<int>> reader, HashDigest hash);
Future<CryptoKeyDistance> distance(CryptoKey key1, CryptoKey key2); Future<CryptoKeyDistance> distance(CryptoKey key1, CryptoKey key2);
Future<Signature> sign(PublicKey key, SecretKey secret, Uint8List data); Future<Signature> sign(PublicKey key, SecretKey secret, Uint8List data);
Future<Signature> signWithKeyPair(KeyPair keyPair, Uint8List data) { Future<Signature> signWithKeyPair(KeyPair keyPair, Uint8List data) =>
return sign(keyPair.key, keyPair.secret, data); sign(keyPair.key, keyPair.secret, data);
}
Future<void> verify(PublicKey key, Uint8List data, Signature signature); Future<void> verify(PublicKey key, Uint8List data, Signature signature);
Future<int> aeadOverhead(); Future<int> aeadOverhead();

View File

@ -34,15 +34,13 @@ class Uint8ListJsonConverter implements JsonConverter<Uint8List, String> {
@immutable @immutable
abstract class EncodedString extends Equatable { abstract class EncodedString extends Equatable {
const EncodedString(String s) : contents = s;
final String contents; final String contents;
@override @override
List<Object> get props => [contents]; List<Object> get props => [contents];
const EncodedString(String s) : contents = s; Uint8List decode() => base64UrlNoPadDecode(contents);
Uint8List decode() {
return base64UrlNoPadDecode(contents);
}
@override @override
String toString() => contents; String toString() => contents;
@ -76,96 +74,82 @@ abstract class EncodedString extends Equatable {
@immutable @immutable
class FixedEncodedString32 extends EncodedString { class FixedEncodedString32 extends EncodedString {
const FixedEncodedString32._(String s) : super(s);
static int encodedLength() {
return 32;
}
static int decodedLength() {
return 24;
}
factory FixedEncodedString32.fromBytes(Uint8List bytes) { factory FixedEncodedString32.fromBytes(Uint8List bytes) {
if (bytes.length != decodedLength()) { if (bytes.length != decodedLength()) {
throw Exception("length ${bytes.length} should be ${decodedLength()}"); throw Exception('length ${bytes.length} should be ${decodedLength()}');
} }
return FixedEncodedString32._(base64UrlNoPadEncode(bytes)); return FixedEncodedString32._(base64UrlNoPadEncode(bytes));
} }
factory FixedEncodedString32.fromString(String s) { factory FixedEncodedString32.fromString(String s) {
var d = base64UrlNoPadDecode(s); final d = base64UrlNoPadDecode(s);
if (d.length != decodedLength()) { if (d.length != decodedLength()) {
throw Exception("length ${s.length} should be ${encodedLength()}"); throw Exception('length ${s.length} should be ${encodedLength()}');
} }
return FixedEncodedString32._(s); return FixedEncodedString32._(s);
} }
String toJson() => toString();
factory FixedEncodedString32.fromJson(dynamic json) => factory FixedEncodedString32.fromJson(dynamic json) =>
FixedEncodedString32.fromString(json as String); FixedEncodedString32.fromString(json as String);
const FixedEncodedString32._(super.s);
static int encodedLength() => 32;
static int decodedLength() => 24;
String toJson() => toString();
} }
@immutable @immutable
class FixedEncodedString43 extends EncodedString { class FixedEncodedString43 extends EncodedString {
const FixedEncodedString43._(String s) : super(s);
static int encodedLength() {
return 43;
}
static int decodedLength() {
return 32;
}
factory FixedEncodedString43.fromBytes(Uint8List bytes) { factory FixedEncodedString43.fromBytes(Uint8List bytes) {
if (bytes.length != decodedLength()) { if (bytes.length != decodedLength()) {
throw Exception("length ${bytes.length} should be ${decodedLength()}"); throw Exception('length ${bytes.length} should be ${decodedLength()}');
} }
return FixedEncodedString43._(base64UrlNoPadEncode(bytes)); return FixedEncodedString43._(base64UrlNoPadEncode(bytes));
} }
factory FixedEncodedString43.fromString(String s) { factory FixedEncodedString43.fromString(String s) {
var d = base64UrlNoPadDecode(s); final d = base64UrlNoPadDecode(s);
if (d.length != decodedLength()) { if (d.length != decodedLength()) {
throw Exception("length ${s.length} should be ${encodedLength()}"); throw Exception('length ${s.length} should be ${encodedLength()}');
} }
return FixedEncodedString43._(s); return FixedEncodedString43._(s);
} }
String toJson() => toString();
factory FixedEncodedString43.fromJson(dynamic json) => factory FixedEncodedString43.fromJson(dynamic json) =>
FixedEncodedString43.fromString(json as String); FixedEncodedString43.fromString(json as String);
const FixedEncodedString43._(super.s);
static int encodedLength() => 43;
static int decodedLength() => 32;
String toJson() => toString();
} }
@immutable @immutable
class FixedEncodedString86 extends EncodedString { class FixedEncodedString86 extends EncodedString {
const FixedEncodedString86._(String s) : super(s);
static int encodedLength() {
return 86;
}
static int decodedLength() {
return 64;
}
String toJson() {
return toString();
}
factory FixedEncodedString86.fromBytes(Uint8List bytes) { factory FixedEncodedString86.fromBytes(Uint8List bytes) {
if (bytes.length != decodedLength()) { if (bytes.length != decodedLength()) {
throw Exception("length ${bytes.length} should be ${decodedLength()}"); throw Exception('length ${bytes.length} should be ${decodedLength()}');
} }
return FixedEncodedString86._(base64UrlNoPadEncode(bytes)); return FixedEncodedString86._(base64UrlNoPadEncode(bytes));
} }
factory FixedEncodedString86.fromString(String s) { factory FixedEncodedString86.fromString(String s) {
var d = base64UrlNoPadDecode(s); final d = base64UrlNoPadDecode(s);
if (d.length != decodedLength()) { if (d.length != decodedLength()) {
throw Exception("length ${s.length} should be ${encodedLength()}"); throw Exception('length ${s.length} should be ${encodedLength()}');
} }
return FixedEncodedString86._(s); return FixedEncodedString86._(s);
} }
factory FixedEncodedString86.fromJson(dynamic json) => factory FixedEncodedString86.fromJson(dynamic json) =>
FixedEncodedString86.fromString(json as String); FixedEncodedString86.fromString(json as String);
const FixedEncodedString86._(super.s);
static int encodedLength() => 86;
static int decodedLength() => 64;
String toJson() => toString();
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,52 +1,53 @@
import 'veilid.dart'; import 'dart:async';
import 'dart:convert';
import 'dart:html' as html; import 'dart:html' as html;
import 'dart:js' as js; import 'dart:js' as js;
import 'dart:js_util' as js_util; import 'dart:js_util' as js_util;
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data'; import 'dart:typed_data';
import 'veilid_encoding.dart'; import 'veilid.dart';
////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////
Veilid getVeilid() => VeilidJS(); Veilid getVeilid() => VeilidJS();
Object wasm = js_util.getProperty(html.window, "veilid_wasm"); Object wasm = js_util.getProperty(html.window, 'veilid_wasm');
Future<T> _wrapApiPromise<T>(Object p) { Future<T> _wrapApiPromise<T>(Object p) => js_util
return js_util.promiseToFuture(p).then((value) => value as T).catchError( .promiseToFuture<T>(p)
(error) => Future<T>.error( .then((value) => value)
VeilidAPIException.fromJson(jsonDecode(error as String)))); // ignore: inference_failure_on_untyped_parameter
} .catchError((e) {
// Wrap all other errors in VeilidAPIExceptionInternal
throw VeilidAPIExceptionInternal(e.toString());
}, test: (e) => e is! VeilidAPIException);
class _Ctx { class _Ctx {
int? id; _Ctx(int id, this.js) : _id = id;
int? _id;
final VeilidJS js; final VeilidJS js;
_Ctx(int this.id, this.js); int requireId() {
void ensureValid() { if (_id == null) {
if (id == null) {
throw VeilidAPIExceptionNotInitialized(); throw VeilidAPIExceptionNotInitialized();
} }
return _id!;
} }
void close() { void close() {
if (id != null) { if (_id != null) {
js_util.callMethod(wasm, "release_routing_context", [id!]); js_util.callMethod<void>(wasm, 'release_routing_context', [_id]);
id = null; _id = null;
} }
} }
} }
// JS implementation of VeilidRoutingContext // JS implementation of VeilidRoutingContext
class VeilidRoutingContextJS extends VeilidRoutingContext { class VeilidRoutingContextJS extends VeilidRoutingContext {
final _Ctx _ctx;
static final Finalizer<_Ctx> _finalizer = Finalizer((ctx) => ctx.close());
VeilidRoutingContextJS._(this._ctx) { VeilidRoutingContextJS._(this._ctx) {
_finalizer.attach(this, _ctx, detach: this); _finalizer.attach(this, _ctx, detach: this);
} }
final _Ctx _ctx;
static final Finalizer<_Ctx> _finalizer = Finalizer((ctx) => ctx.close());
@override @override
void close() { void close() {
@ -55,113 +56,113 @@ class VeilidRoutingContextJS extends VeilidRoutingContext {
@override @override
VeilidRoutingContextJS withPrivacy() { VeilidRoutingContextJS withPrivacy() {
_ctx.ensureValid(); final id = _ctx.requireId();
int newId = final int newId =
js_util.callMethod(wasm, "routing_context_with_privacy", [_ctx.id!]); js_util.callMethod(wasm, 'routing_context_with_privacy', [id]);
return VeilidRoutingContextJS._(_Ctx(newId, _ctx.js)); return VeilidRoutingContextJS._(_Ctx(newId, _ctx.js));
} }
@override @override
VeilidRoutingContextJS withCustomPrivacy(SafetySelection safetySelection) { VeilidRoutingContextJS withCustomPrivacy(SafetySelection safetySelection) {
_ctx.ensureValid(); final id = _ctx.requireId();
final newId = js_util.callMethod( final newId = js_util.callMethod<int>(
wasm, wasm,
"routing_context_with_custom_privacy", 'routing_context_with_custom_privacy',
[_ctx.id!, jsonEncode(safetySelection)]); [id, jsonEncode(safetySelection)]);
return VeilidRoutingContextJS._(_Ctx(newId, _ctx.js)); return VeilidRoutingContextJS._(_Ctx(newId, _ctx.js));
} }
@override @override
VeilidRoutingContextJS withSequencing(Sequencing sequencing) { VeilidRoutingContextJS withSequencing(Sequencing sequencing) {
_ctx.ensureValid(); final id = _ctx.requireId();
final newId = js_util.callMethod(wasm, "routing_context_with_sequencing", final newId = js_util.callMethod<int>(
[_ctx.id!, jsonEncode(sequencing)]); wasm, 'routing_context_with_sequencing', [id, jsonEncode(sequencing)]);
return VeilidRoutingContextJS._(_Ctx(newId, _ctx.js)); return VeilidRoutingContextJS._(_Ctx(newId, _ctx.js));
} }
@override @override
Future<Uint8List> appCall(String target, Uint8List request) async { Future<Uint8List> appCall(String target, Uint8List request) async {
_ctx.ensureValid(); final id = _ctx.requireId();
var encodedRequest = base64UrlNoPadEncode(request); final encodedRequest = base64UrlNoPadEncode(request);
return base64UrlNoPadDecode(await _wrapApiPromise(js_util.callMethod( return base64UrlNoPadDecode(await _wrapApiPromise(js_util.callMethod(
wasm, "routing_context_app_call", [_ctx.id!, target, encodedRequest]))); wasm, 'routing_context_app_call', [id, target, encodedRequest])));
} }
@override @override
Future<void> appMessage(String target, Uint8List message) { Future<void> appMessage(String target, Uint8List message) {
_ctx.ensureValid(); final id = _ctx.requireId();
var encodedMessage = base64UrlNoPadEncode(message); final encodedMessage = base64UrlNoPadEncode(message);
return _wrapApiPromise(js_util.callMethod(wasm, return _wrapApiPromise(js_util.callMethod(
"routing_context_app_message", [_ctx.id!, target, encodedMessage])); wasm, 'routing_context_app_message', [id, target, encodedMessage]));
} }
@override @override
Future<DHTRecordDescriptor> createDHTRecord(DHTSchema schema, Future<DHTRecordDescriptor> createDHTRecord(DHTSchema schema,
{CryptoKind kind = 0}) async { {CryptoKind kind = 0}) async {
_ctx.ensureValid(); final id = _ctx.requireId();
return DHTRecordDescriptor.fromJson(jsonDecode(await _wrapApiPromise(js_util return DHTRecordDescriptor.fromJson(jsonDecode(await _wrapApiPromise(js_util
.callMethod(wasm, "routing_context_create_dht_record", .callMethod(wasm, 'routing_context_create_dht_record',
[_ctx.id!, jsonEncode(schema), kind])))); [id, jsonEncode(schema), kind]))));
} }
@override @override
Future<DHTRecordDescriptor> openDHTRecord( Future<DHTRecordDescriptor> openDHTRecord(
TypedKey key, KeyPair? writer) async { TypedKey key, KeyPair? writer) async {
_ctx.ensureValid(); final id = _ctx.requireId();
return DHTRecordDescriptor.fromJson(jsonDecode(await _wrapApiPromise(js_util return DHTRecordDescriptor.fromJson(jsonDecode(await _wrapApiPromise(js_util
.callMethod(wasm, "routing_context_open_dht_record", [ .callMethod(wasm, 'routing_context_open_dht_record', [
_ctx.id!, id,
jsonEncode(key), jsonEncode(key),
writer != null ? jsonEncode(writer) : null if (writer != null) jsonEncode(writer) else null
])))); ]))));
} }
@override @override
Future<void> closeDHTRecord(TypedKey key) { Future<void> closeDHTRecord(TypedKey key) {
_ctx.ensureValid(); final id = _ctx.requireId();
return _wrapApiPromise(js_util.callMethod( return _wrapApiPromise(js_util.callMethod(
wasm, "routing_context_close_dht_record", [_ctx.id!, jsonEncode(key)])); wasm, 'routing_context_close_dht_record', [id, jsonEncode(key)]));
} }
@override @override
Future<void> deleteDHTRecord(TypedKey key) { Future<void> deleteDHTRecord(TypedKey key) {
_ctx.ensureValid(); final id = _ctx.requireId();
return _wrapApiPromise(js_util.callMethod(wasm, return _wrapApiPromise(js_util.callMethod(
"routing_context_delete_dht_record", [_ctx.id!, jsonEncode(key)])); wasm, 'routing_context_delete_dht_record', [id, jsonEncode(key)]));
} }
@override @override
Future<ValueData?> getDHTValue( Future<ValueData?> getDHTValue(
TypedKey key, int subkey, bool forceRefresh) async { TypedKey key, int subkey, bool forceRefresh) async {
_ctx.ensureValid(); final id = _ctx.requireId();
final opt = await _wrapApiPromise(js_util.callMethod( final opt = await _wrapApiPromise<String?>(js_util.callMethod(
wasm, wasm,
"routing_context_get_dht_value", 'routing_context_get_dht_value',
[_ctx.id!, jsonEncode(key), subkey, forceRefresh])); [id, jsonEncode(key), subkey, forceRefresh]));
return opt == null ? null : ValueData.fromJson(jsonDecode(opt)); return opt == null ? null : ValueData.fromJson(jsonDecode(opt));
} }
@override @override
Future<ValueData?> setDHTValue( Future<ValueData?> setDHTValue(
TypedKey key, int subkey, Uint8List data) async { TypedKey key, int subkey, Uint8List data) async {
_ctx.ensureValid(); final id = _ctx.requireId();
final opt = await _wrapApiPromise(js_util.callMethod( final opt = await _wrapApiPromise<String?>(js_util.callMethod(
wasm, wasm,
"routing_context_set_dht_value", 'routing_context_set_dht_value',
[_ctx.id!, jsonEncode(key), subkey, base64UrlNoPadEncode(data)])); [id, jsonEncode(key), subkey, base64UrlNoPadEncode(data)]));
return opt == null ? null : ValueData.fromJson(jsonDecode(opt)); return opt == null ? null : ValueData.fromJson(jsonDecode(opt));
} }
@override @override
Future<Timestamp> watchDHTValues(TypedKey key, List<ValueSubkeyRange> subkeys, Future<Timestamp> watchDHTValues(TypedKey key, List<ValueSubkeyRange> subkeys,
Timestamp expiration, int count) async { Timestamp expiration, int count) async {
_ctx.ensureValid(); final id = _ctx.requireId();
final ts = await _wrapApiPromise(js_util.callMethod( final ts = await _wrapApiPromise<String>(js_util.callMethod(
wasm, "routing_context_watch_dht_values", [ wasm, 'routing_context_watch_dht_values', [
_ctx.id!, id,
jsonEncode(key), jsonEncode(key),
jsonEncode(subkeys), jsonEncode(subkeys),
expiration.toString(), expiration.toString(),
@ -172,195 +173,176 @@ class VeilidRoutingContextJS extends VeilidRoutingContext {
@override @override
Future<bool> cancelDHTWatch(TypedKey key, List<ValueSubkeyRange> subkeys) { Future<bool> cancelDHTWatch(TypedKey key, List<ValueSubkeyRange> subkeys) {
_ctx.ensureValid(); final id = _ctx.requireId();
return _wrapApiPromise(js_util.callMethod( return _wrapApiPromise(js_util.callMethod(
wasm, wasm,
"routing_context_cancel_dht_watch", 'routing_context_cancel_dht_watch',
[_ctx.id!, jsonEncode(key), jsonEncode(subkeys)])); [id, jsonEncode(key), jsonEncode(subkeys)]));
} }
} }
// JS implementation of VeilidCryptoSystem // JS implementation of VeilidCryptoSystem
class VeilidCryptoSystemJS extends VeilidCryptoSystem { class VeilidCryptoSystemJS extends VeilidCryptoSystem {
VeilidCryptoSystemJS._(this._js, this._kind);
final CryptoKind _kind; final CryptoKind _kind;
// Keep the reference
// ignore: unused_field
final VeilidJS _js; final VeilidJS _js;
VeilidCryptoSystemJS._(this._js, this._kind) { @override
// Keep the reference CryptoKind kind() => _kind;
_js;
}
@override @override
CryptoKind kind() { Future<SharedSecret> cachedDH(PublicKey key, SecretKey secret) async =>
return _kind; SharedSecret.fromJson(jsonDecode(await _wrapApiPromise(js_util.callMethod(
} wasm,
'crypto_cached_dh',
[_kind, jsonEncode(key), jsonEncode(secret)]))));
@override @override
Future<SharedSecret> cachedDH(PublicKey key, SecretKey secret) async { Future<SharedSecret> computeDH(PublicKey key, SecretKey secret) async =>
return SharedSecret.fromJson(jsonDecode(await _wrapApiPromise(js_util SharedSecret.fromJson(jsonDecode(await _wrapApiPromise(js_util.callMethod(
.callMethod(wasm, "crypto_cached_dh", wasm,
[_kind, jsonEncode(key), jsonEncode(secret)])))); 'crypto_compute_dh',
} [_kind, jsonEncode(key), jsonEncode(secret)]))));
@override @override
Future<SharedSecret> computeDH(PublicKey key, SecretKey secret) async { Future<Uint8List> randomBytes(int len) async =>
return SharedSecret.fromJson(jsonDecode(await _wrapApiPromise(js_util base64UrlNoPadDecode(await _wrapApiPromise(
.callMethod(wasm, "crypto_compute_dh", js_util.callMethod(wasm, 'crypto_random_bytes', [_kind, len])));
[_kind, jsonEncode(key), jsonEncode(secret)]))));
}
@override @override
Future<Uint8List> randomBytes(int len) async { Future<int> defaultSaltLength() => _wrapApiPromise(
return base64UrlNoPadDecode(await _wrapApiPromise( js_util.callMethod(wasm, 'crypto_default_salt_length', [_kind]));
js_util.callMethod(wasm, "crypto_random_bytes", [_kind, len])));
}
@override @override
Future<int> defaultSaltLength() { Future<String> hashPassword(Uint8List password, Uint8List salt) =>
return _wrapApiPromise( _wrapApiPromise(js_util.callMethod(wasm, 'crypto_hash_password',
js_util.callMethod(wasm, "crypto_default_salt_length", [_kind])); [_kind, base64UrlNoPadEncode(password), base64UrlNoPadEncode(salt)]));
}
@override @override
Future<String> hashPassword(Uint8List password, Uint8List salt) { Future<bool> verifyPassword(Uint8List password, String passwordHash) =>
return _wrapApiPromise(js_util.callMethod(wasm, "crypto_hash_password", _wrapApiPromise(js_util.callMethod(wasm, 'crypto_verify_password',
[_kind, base64UrlNoPadEncode(password), base64UrlNoPadEncode(salt)])); [_kind, base64UrlNoPadEncode(password), passwordHash]));
}
@override
Future<bool> verifyPassword(Uint8List password, String passwordHash) {
return _wrapApiPromise(js_util.callMethod(wasm, "crypto_verify_password",
[_kind, base64UrlNoPadEncode(password), passwordHash]));
}
@override @override
Future<SharedSecret> deriveSharedSecret( Future<SharedSecret> deriveSharedSecret(
Uint8List password, Uint8List salt) async { Uint8List password, Uint8List salt) async =>
return SharedSecret.fromJson(jsonDecode(await _wrapApiPromise(js_util SharedSecret.fromJson(jsonDecode(await _wrapApiPromise(js_util.callMethod(
.callMethod(wasm, "crypto_derive_shared_secret", [ wasm, 'crypto_derive_shared_secret', [
_kind, _kind,
base64UrlNoPadEncode(password), base64UrlNoPadEncode(password),
base64UrlNoPadEncode(salt) base64UrlNoPadEncode(salt)
])))); ]))));
}
@override @override
Future<Nonce> randomNonce() async { Future<Nonce> randomNonce() async =>
return Nonce.fromJson(jsonDecode(await _wrapApiPromise( Nonce.fromJson(jsonDecode(await _wrapApiPromise(
js_util.callMethod(wasm, "crypto_random_nonce", [_kind])))); js_util.callMethod(wasm, 'crypto_random_nonce', [_kind]))));
}
@override @override
Future<SharedSecret> randomSharedSecret() async { Future<SharedSecret> randomSharedSecret() async =>
return SharedSecret.fromJson(jsonDecode(await _wrapApiPromise( SharedSecret.fromJson(jsonDecode(await _wrapApiPromise(
js_util.callMethod(wasm, "crypto_random_shared_secret", [_kind])))); js_util.callMethod(wasm, 'crypto_random_shared_secret', [_kind]))));
}
@override @override
Future<KeyPair> generateKeyPair() async { Future<KeyPair> generateKeyPair() async =>
return KeyPair.fromJson(jsonDecode(await _wrapApiPromise( KeyPair.fromJson(jsonDecode(await _wrapApiPromise(
js_util.callMethod(wasm, "crypto_generate_key_pair", [_kind])))); js_util.callMethod(wasm, 'crypto_generate_key_pair', [_kind]))));
}
@override @override
Future<HashDigest> generateHash(Uint8List data) async { Future<HashDigest> generateHash(Uint8List data) async =>
return HashDigest.fromJson(jsonDecode(await _wrapApiPromise(js_util HashDigest.fromJson(jsonDecode(await _wrapApiPromise(js_util.callMethod(
.callMethod(wasm, "crypto_generate_hash", wasm, 'crypto_generate_hash', [_kind, base64UrlNoPadEncode(data)]))));
[_kind, base64UrlNoPadEncode(data)]))));
}
@override @override
Future<bool> validateKeyPair(PublicKey key, SecretKey secret) { Future<bool> validateKeyPair(PublicKey key, SecretKey secret) =>
return _wrapApiPromise(js_util.callMethod(wasm, "crypto_validate_key_pair", _wrapApiPromise(js_util.callMethod(wasm, 'crypto_validate_key_pair',
[_kind, jsonEncode(key), jsonEncode(secret)])); [_kind, jsonEncode(key), jsonEncode(secret)]));
}
@override @override
Future<bool> validateHash(Uint8List data, HashDigest hash) { Future<bool> validateHash(Uint8List data, HashDigest hash) =>
return _wrapApiPromise(js_util.callMethod(wasm, "crypto_validate_hash", _wrapApiPromise(js_util.callMethod(wasm, 'crypto_validate_hash',
[_kind, base64UrlNoPadEncode(data), jsonEncode(hash)])); [_kind, base64UrlNoPadEncode(data), jsonEncode(hash)]));
}
@override @override
Future<CryptoKeyDistance> distance(CryptoKey key1, CryptoKey key2) async { Future<CryptoKeyDistance> distance(CryptoKey key1, CryptoKey key2) async =>
return CryptoKeyDistance.fromJson(jsonDecode(await _wrapApiPromise(js_util CryptoKeyDistance.fromJson(jsonDecode(await _wrapApiPromise(js_util
.callMethod(wasm, "crypto_distance", .callMethod(wasm, 'crypto_distance',
[_kind, jsonEncode(key1), jsonEncode(key2)])))); [_kind, jsonEncode(key1), jsonEncode(key2)]))));
}
@override @override
Future<Signature> sign( Future<Signature> sign(
PublicKey key, SecretKey secret, Uint8List data) async { PublicKey key, SecretKey secret, Uint8List data) async =>
return Signature.fromJson(jsonDecode(await _wrapApiPromise(js_util Signature.fromJson(jsonDecode(await _wrapApiPromise(js_util.callMethod(
.callMethod(wasm, "crypto_sign", [ wasm, 'crypto_sign', [
_kind, _kind,
jsonEncode(key), jsonEncode(key),
jsonEncode(secret), jsonEncode(secret),
base64UrlNoPadEncode(data) base64UrlNoPadEncode(data)
])))); ]))));
}
@override @override
Future<void> verify(PublicKey key, Uint8List data, Signature signature) { Future<void> verify(PublicKey key, Uint8List data, Signature signature) =>
return _wrapApiPromise(js_util.callMethod(wasm, "crypto_verify", [ _wrapApiPromise(js_util.callMethod(wasm, 'crypto_verify', [
_kind, _kind,
jsonEncode(key), jsonEncode(key),
base64UrlNoPadEncode(data), base64UrlNoPadEncode(data),
jsonEncode(signature), jsonEncode(signature),
])); ]));
}
@override @override
Future<int> aeadOverhead() { Future<int> aeadOverhead() => _wrapApiPromise(
return _wrapApiPromise( js_util.callMethod(wasm, 'crypto_aead_overhead', [_kind]));
js_util.callMethod(wasm, "crypto_aead_overhead", [_kind]));
}
@override @override
Future<Uint8List> decryptAead(Uint8List body, Nonce nonce, Future<Uint8List> decryptAead(Uint8List body, Nonce nonce,
SharedSecret sharedSecret, Uint8List? associatedData) async { SharedSecret sharedSecret, Uint8List? associatedData) async =>
return base64UrlNoPadDecode( base64UrlNoPadDecode(await _wrapApiPromise(
await _wrapApiPromise(js_util.callMethod(wasm, "crypto_decrypt_aead", [ js_util.callMethod(wasm, 'crypto_decrypt_aead', [
_kind, _kind,
base64UrlNoPadEncode(body), base64UrlNoPadEncode(body),
jsonEncode(nonce), jsonEncode(nonce),
jsonEncode(sharedSecret), jsonEncode(sharedSecret),
associatedData != null ? base64UrlNoPadEncode(associatedData) : null if (associatedData != null)
]))); base64UrlNoPadEncode(associatedData)
} else
null
])));
@override @override
Future<Uint8List> encryptAead(Uint8List body, Nonce nonce, Future<Uint8List> encryptAead(Uint8List body, Nonce nonce,
SharedSecret sharedSecret, Uint8List? associatedData) async { SharedSecret sharedSecret, Uint8List? associatedData) async =>
return base64UrlNoPadDecode( base64UrlNoPadDecode(await _wrapApiPromise(
await _wrapApiPromise(js_util.callMethod(wasm, "crypto_encrypt_aead", [ js_util.callMethod(wasm, 'crypto_encrypt_aead', [
_kind, _kind,
base64UrlNoPadEncode(body), base64UrlNoPadEncode(body),
jsonEncode(nonce), jsonEncode(nonce),
jsonEncode(sharedSecret), jsonEncode(sharedSecret),
associatedData != null ? base64UrlNoPadEncode(associatedData) : null if (associatedData != null)
]))); base64UrlNoPadEncode(associatedData)
} else
null
])));
@override @override
Future<Uint8List> cryptNoAuth( Future<Uint8List> cryptNoAuth(
Uint8List body, Nonce nonce, SharedSecret sharedSecret) async { Uint8List body, Nonce nonce, SharedSecret sharedSecret) async =>
return base64UrlNoPadDecode(await _wrapApiPromise(js_util.callMethod( base64UrlNoPadDecode(await _wrapApiPromise(js_util.callMethod(
wasm, "crypto_crypt_no_auth", [ wasm, 'crypto_crypt_no_auth', [
_kind, _kind,
base64UrlNoPadEncode(body), base64UrlNoPadEncode(body),
jsonEncode(nonce), jsonEncode(nonce),
jsonEncode(sharedSecret) jsonEncode(sharedSecret)
]))); ])));
}
} }
class _TDBT { class _TDBT {
_TDBT(this.id, this.tdbjs, this.js);
int? id; int? id;
final VeilidTableDBJS tdbjs; final VeilidTableDBJS tdbjs;
final VeilidJS js; final VeilidJS js;
_TDBT(this.id, this.tdbjs, this.js);
void ensureValid() { void ensureValid() {
if (id == null) { if (id == null) {
throw VeilidAPIExceptionNotInitialized(); throw VeilidAPIExceptionNotInitialized();
@ -369,7 +351,7 @@ class _TDBT {
void close() { void close() {
if (id != null) { if (id != null) {
js_util.callMethod(wasm, "release_table_db_transaction", [id!]); js_util.callMethod<void>(wasm, 'release_table_db_transaction', [id]);
id = null; id = null;
} }
} }
@ -377,81 +359,83 @@ class _TDBT {
// JS implementation of VeilidTableDBTransaction // JS implementation of VeilidTableDBTransaction
class VeilidTableDBTransactionJS extends VeilidTableDBTransaction { class VeilidTableDBTransactionJS extends VeilidTableDBTransaction {
final _TDBT _tdbt;
static final Finalizer<_TDBT> _finalizer = Finalizer((tdbt) => tdbt.close());
VeilidTableDBTransactionJS._(this._tdbt) { VeilidTableDBTransactionJS._(this._tdbt) {
_finalizer.attach(this, _tdbt, detach: this); _finalizer.attach(this, _tdbt, detach: this);
} }
final _TDBT _tdbt;
static final Finalizer<_TDBT> _finalizer = Finalizer((tdbt) => tdbt.close());
@override @override
bool isDone() { bool isDone() => _tdbt.id == null;
return _tdbt.id == null;
}
@override @override
Future<void> commit() async { Future<void> commit() async {
_tdbt.ensureValid(); _tdbt.ensureValid();
await _wrapApiPromise( final id = _tdbt.id!;
js_util.callMethod(wasm, "table_db_transaction_commit", [_tdbt.id!])); await _wrapApiPromise<void>(
js_util.callMethod(wasm, 'table_db_transaction_commit', [id]));
_tdbt.close(); _tdbt.close();
} }
@override @override
Future<void> rollback() async { Future<void> rollback() async {
_tdbt.ensureValid(); _tdbt.ensureValid();
await _wrapApiPromise( final id = _tdbt.id!;
js_util.callMethod(wasm, "table_db_transaction_rollback", [_tdbt.id!])); await _wrapApiPromise<void>(
js_util.callMethod(wasm, 'table_db_transaction_rollback', [id]));
_tdbt.close(); _tdbt.close();
} }
@override @override
Future<void> store(int col, Uint8List key, Uint8List value) async { Future<void> store(int col, Uint8List key, Uint8List value) async {
_tdbt.ensureValid(); _tdbt.ensureValid();
final id = _tdbt.id!;
final encodedKey = base64UrlNoPadEncode(key); final encodedKey = base64UrlNoPadEncode(key);
final encodedValue = base64UrlNoPadEncode(value); final encodedValue = base64UrlNoPadEncode(value);
await _wrapApiPromise(js_util.callMethod(wasm, "table_db_transaction_store", await _wrapApiPromise<void>(js_util.callMethod(wasm,
[_tdbt.id!, col, encodedKey, encodedValue])); 'table_db_transaction_store', [id, col, encodedKey, encodedValue]));
} }
@override @override
Future<void> delete(int col, Uint8List key) async { Future<void> delete(int col, Uint8List key) async {
_tdbt.ensureValid(); _tdbt.ensureValid();
final id = _tdbt.id!;
final encodedKey = base64UrlNoPadEncode(key); final encodedKey = base64UrlNoPadEncode(key);
await _wrapApiPromise(js_util.callMethod( await _wrapApiPromise<void>(js_util.callMethod(
wasm, "table_db_transaction_delete", [_tdbt.id!, col, encodedKey])); wasm, 'table_db_transaction_delete', [id, col, encodedKey]));
} }
} }
class _TDB { class _TDB {
int? id; _TDB(int id, this.js) : _id = id;
final VeilidJS js;
_TDB(int this.id, this.js); int? _id;
void ensureValid() {
if (id == null) { final VeilidJS js;
int requireId() {
if (_id == null) {
throw VeilidAPIExceptionNotInitialized(); throw VeilidAPIExceptionNotInitialized();
} }
return _id!;
} }
void close() { void close() {
if (id != null) { if (_id != null) {
js_util.callMethod(wasm, "release_table_db", [id!]); js_util.callMethod<void>(wasm, 'release_table_db', [_id]);
id = null; _id = null;
} }
} }
} }
// JS implementation of VeilidTableDB // JS implementation of VeilidTableDB
class VeilidTableDBJS extends VeilidTableDB { class VeilidTableDBJS extends VeilidTableDB {
final _TDB _tdb;
static final Finalizer<_TDB> _finalizer = Finalizer((tdb) => tdb.close());
VeilidTableDBJS._(this._tdb) { VeilidTableDBJS._(this._tdb) {
_finalizer.attach(this, _tdb, detach: this); _finalizer.attach(this, _tdb, detach: this);
} }
final _TDB _tdb;
static final Finalizer<_TDB> _finalizer = Finalizer((tdb) => tdb.close());
@override @override
void close() { void close() {
@ -460,42 +444,42 @@ class VeilidTableDBJS extends VeilidTableDB {
@override @override
int getColumnCount() { int getColumnCount() {
_tdb.ensureValid(); final id = _tdb.requireId();
return js_util.callMethod(wasm, "table_db_get_column_count", [_tdb.id!]); return js_util.callMethod(wasm, 'table_db_get_column_count', [id]);
} }
@override @override
Future<List<Uint8List>> getKeys(int col) async { Future<List<Uint8List>> getKeys(int col) async {
_tdb.ensureValid(); final id = _tdb.requireId();
return jsonListConstructor(base64UrlNoPadDecodeDynamic)(jsonDecode( return jsonListConstructor(base64UrlNoPadDecodeDynamic)(jsonDecode(
await js_util.callMethod(wasm, "table_db_get_keys", [_tdb.id!, col]))); await js_util.callMethod(wasm, 'table_db_get_keys', [id, col])));
} }
@override @override
VeilidTableDBTransaction transact() { VeilidTableDBTransaction transact() {
_tdb.ensureValid(); final id = _tdb.requireId();
final id = js_util.callMethod(wasm, "table_db_transact", [_tdb.id!]); final xid = js_util.callMethod<int>(wasm, 'table_db_transact', [id]);
return VeilidTableDBTransactionJS._(_TDBT(id, this, _tdb.js)); return VeilidTableDBTransactionJS._(_TDBT(xid, this, _tdb.js));
} }
@override @override
Future<void> store(int col, Uint8List key, Uint8List value) { Future<void> store(int col, Uint8List key, Uint8List value) {
_tdb.ensureValid(); final id = _tdb.requireId();
final encodedKey = base64UrlNoPadEncode(key); final encodedKey = base64UrlNoPadEncode(key);
final encodedValue = base64UrlNoPadEncode(value); final encodedValue = base64UrlNoPadEncode(value);
return _wrapApiPromise(js_util.callMethod( return _wrapApiPromise(js_util.callMethod(
wasm, "table_db_store", [_tdb.id!, col, encodedKey, encodedValue])); wasm, 'table_db_store', [id, col, encodedKey, encodedValue]));
} }
@override @override
Future<Uint8List?> load(int col, Uint8List key) async { Future<Uint8List?> load(int col, Uint8List key) async {
_tdb.ensureValid(); final id = _tdb.requireId();
final encodedKey = base64UrlNoPadEncode(key); final encodedKey = base64UrlNoPadEncode(key);
String? out = await _wrapApiPromise( final out = await _wrapApiPromise<String?>(
js_util.callMethod(wasm, "table_db_load", [_tdb.id!, col, encodedKey])); js_util.callMethod(wasm, 'table_db_load', [id, col, encodedKey]));
if (out == null) { if (out == null) {
return null; return null;
} }
@ -503,12 +487,16 @@ class VeilidTableDBJS extends VeilidTableDB {
} }
@override @override
Future<Uint8List?> delete(int col, Uint8List key) { Future<Uint8List?> delete(int col, Uint8List key) async {
_tdb.ensureValid(); final id = _tdb.requireId();
final encodedKey = base64UrlNoPadEncode(key); final encodedKey = base64UrlNoPadEncode(key);
return _wrapApiPromise(js_util final out = await _wrapApiPromise<String?>(
.callMethod(wasm, "table_db_delete", [_tdb.id!, col, encodedKey])); js_util.callMethod(wasm, 'table_db_delete', [id, col, encodedKey]));
if (out == null) {
return null;
}
return base64UrlNoPadDecode(out);
} }
} }
@ -517,61 +505,57 @@ class VeilidTableDBJS extends VeilidTableDB {
class VeilidJS extends Veilid { class VeilidJS extends Veilid {
@override @override
void initializeVeilidCore(Map<String, dynamic> platformConfigJson) { void initializeVeilidCore(Map<String, dynamic> platformConfigJson) {
var platformConfigJsonString = jsonEncode(platformConfigJson); final platformConfigJsonString = jsonEncode(platformConfigJson);
js_util js_util.callMethod<void>(
.callMethod(wasm, "initialize_veilid_core", [platformConfigJsonString]); wasm, 'initialize_veilid_core', [platformConfigJsonString]);
} }
@override @override
void changeLogLevel(String layer, VeilidConfigLogLevel logLevel) { void changeLogLevel(String layer, VeilidConfigLogLevel logLevel) {
var logLevelJsonString = jsonEncode(logLevel); final logLevelJsonString = jsonEncode(logLevel);
js_util.callMethod(wasm, "change_log_level", [layer, logLevelJsonString]); js_util.callMethod<void>(
wasm, 'change_log_level', [layer, logLevelJsonString]);
} }
@override @override
Future<Stream<VeilidUpdate>> startupVeilidCore(VeilidConfig config) async { Future<Stream<VeilidUpdate>> startupVeilidCore(VeilidConfig config) async {
var streamController = StreamController<VeilidUpdate>(); final streamController = StreamController<VeilidUpdate>();
updateCallback(String update) { void updateCallback(String update) {
var updateJson = jsonDecode(update); final updateJson = jsonDecode(update) as Map<String, dynamic>;
if (updateJson["kind"] == "Shutdown") { if (updateJson['kind'] == 'Shutdown') {
streamController.close(); unawaited(streamController.close());
} else { } else {
var update = VeilidUpdate.fromJson(updateJson); final update = VeilidUpdate.fromJson(updateJson);
streamController.add(update); streamController.add(update);
} }
} }
await _wrapApiPromise(js_util.callMethod(wasm, "startup_veilid_core", await _wrapApiPromise<void>(js_util.callMethod(wasm, 'startup_veilid_core',
[js.allowInterop(updateCallback), jsonEncode(config)])); [js.allowInterop(updateCallback), jsonEncode(config)]));
return streamController.stream; return streamController.stream;
} }
@override @override
Future<VeilidState> getVeilidState() async { Future<VeilidState> getVeilidState() async =>
return VeilidState.fromJson(jsonDecode(await _wrapApiPromise( VeilidState.fromJson(jsonDecode(await _wrapApiPromise<String>(
js_util.callMethod(wasm, "get_veilid_state", [])))); js_util.callMethod(wasm, 'get_veilid_state', []))));
}
@override @override
Future<void> attach() { Future<void> attach() =>
return _wrapApiPromise(js_util.callMethod(wasm, "attach", [])); _wrapApiPromise(js_util.callMethod(wasm, 'attach', []));
}
@override @override
Future<void> detach() { Future<void> detach() =>
return _wrapApiPromise(js_util.callMethod(wasm, "detach", [])); _wrapApiPromise(js_util.callMethod(wasm, 'detach', []));
}
@override @override
Future<void> shutdownVeilidCore() { Future<void> shutdownVeilidCore() =>
return _wrapApiPromise( _wrapApiPromise(js_util.callMethod(wasm, 'shutdown_veilid_core', []));
js_util.callMethod(wasm, "shutdown_veilid_core", []));
}
@override @override
List<CryptoKind> validCryptoKinds() { List<CryptoKind> validCryptoKinds() {
final vck = jsonDecode(js_util.callMethod(wasm, "valid_crypto_kinds", [])) final vck = jsonDecode(js_util.callMethod(wasm, 'valid_crypto_kinds', []))
as List<dynamic>; as List<dynamic>;
return vck.map((v) => v as CryptoKind).toList(); return vck.map((v) => v as CryptoKind).toList();
} }
@ -579,118 +563,106 @@ class VeilidJS extends Veilid {
@override @override
Future<VeilidCryptoSystem> getCryptoSystem(CryptoKind kind) async { Future<VeilidCryptoSystem> getCryptoSystem(CryptoKind kind) async {
if (!validCryptoKinds().contains(kind)) { if (!validCryptoKinds().contains(kind)) {
throw const VeilidAPIExceptionGeneric("unsupported cryptosystem"); throw const VeilidAPIExceptionGeneric('unsupported cryptosystem');
} }
return VeilidCryptoSystemJS._(this, kind); return VeilidCryptoSystemJS._(this, kind);
} }
@override @override
Future<VeilidCryptoSystem> bestCryptoSystem() async { Future<VeilidCryptoSystem> bestCryptoSystem() async => VeilidCryptoSystemJS._(
return VeilidCryptoSystemJS._( this, js_util.callMethod(wasm, 'best_crypto_kind', []));
this, js_util.callMethod(wasm, "best_crypto_kind", []));
}
@override @override
Future<List<TypedKey>> verifySignatures(List<TypedKey> nodeIds, Future<List<TypedKey>> verifySignatures(List<TypedKey> nodeIds,
Uint8List data, List<TypedSignature> signatures) async { Uint8List data, List<TypedSignature> signatures) async =>
return jsonListConstructor(TypedKey.fromJson)(jsonDecode( jsonListConstructor(TypedKey.fromJson)(jsonDecode(await _wrapApiPromise(
await _wrapApiPromise(js_util.callMethod(wasm, "verify_signatures", [ js_util.callMethod(wasm, 'verify_signatures', [
jsonEncode(nodeIds), jsonEncode(nodeIds),
base64UrlNoPadEncode(data), base64UrlNoPadEncode(data),
jsonEncode(signatures) jsonEncode(signatures)
])))); ]))));
}
@override @override
Future<List<TypedSignature>> generateSignatures( Future<List<TypedSignature>> generateSignatures(
Uint8List data, List<TypedKeyPair> keyPairs) async { Uint8List data, List<TypedKeyPair> keyPairs) async =>
return jsonListConstructor(TypedSignature.fromJson)(jsonDecode( jsonListConstructor(TypedSignature.fromJson)(jsonDecode(
await _wrapApiPromise(js_util.callMethod(wasm, "generate_signatures", await _wrapApiPromise(js_util.callMethod(wasm, 'generate_signatures',
[base64UrlNoPadEncode(data), jsonEncode(keyPairs)])))); [base64UrlNoPadEncode(data), jsonEncode(keyPairs)]))));
}
@override @override
Future<TypedKeyPair> generateKeyPair(CryptoKind kind) async { Future<TypedKeyPair> generateKeyPair(CryptoKind kind) async =>
return TypedKeyPair.fromJson(jsonDecode(await _wrapApiPromise( TypedKeyPair.fromJson(jsonDecode(await _wrapApiPromise(
js_util.callMethod(wasm, "generate_key_pair", [kind])))); js_util.callMethod(wasm, 'generate_key_pair', [kind]))));
}
@override @override
Future<VeilidRoutingContext> routingContext() async { Future<VeilidRoutingContext> routingContext() async {
int id = final rcid = await _wrapApiPromise<int>(
await _wrapApiPromise(js_util.callMethod(wasm, "routing_context", [])); js_util.callMethod(wasm, 'routing_context', []));
return VeilidRoutingContextJS._(_Ctx(id, this)); return VeilidRoutingContextJS._(_Ctx(rcid, this));
} }
@override @override
Future<RouteBlob> newPrivateRoute() async { Future<RouteBlob> newPrivateRoute() async =>
return RouteBlob.fromJson(jsonDecode(await _wrapApiPromise( RouteBlob.fromJson(jsonDecode(await _wrapApiPromise(
js_util.callMethod(wasm, "new_private_route", [])))); js_util.callMethod(wasm, 'new_private_route', []))));
}
@override @override
Future<RouteBlob> newCustomPrivateRoute( Future<RouteBlob> newCustomPrivateRoute(
Stability stability, Sequencing sequencing) async { Stability stability, Sequencing sequencing) async {
var stabilityString = jsonEncode(stability); final stabilityString = jsonEncode(stability);
var sequencingString = jsonEncode(sequencing); final sequencingString = jsonEncode(sequencing);
return RouteBlob.fromJson(jsonDecode(await _wrapApiPromise(js_util return RouteBlob.fromJson(jsonDecode(await _wrapApiPromise(js_util
.callMethod( .callMethod(
wasm, "new_private_route", [stabilityString, sequencingString])))); wasm, 'new_private_route', [stabilityString, sequencingString]))));
} }
@override @override
Future<String> importRemotePrivateRoute(Uint8List blob) { Future<String> importRemotePrivateRoute(Uint8List blob) {
var encodedBlob = base64UrlNoPadEncode(blob); final encodedBlob = base64UrlNoPadEncode(blob);
return _wrapApiPromise( return _wrapApiPromise(
js_util.callMethod(wasm, "import_remote_private_route", [encodedBlob])); js_util.callMethod(wasm, 'import_remote_private_route', [encodedBlob]));
} }
@override @override
Future<void> releasePrivateRoute(String key) { Future<void> releasePrivateRoute(String key) =>
return _wrapApiPromise( _wrapApiPromise(js_util.callMethod(wasm, 'release_private_route', [key]));
js_util.callMethod(wasm, "release_private_route", [key]));
}
@override @override
Future<void> appCallReply(String callId, Uint8List message) { Future<void> appCallReply(String callId, Uint8List message) {
var encodedMessage = base64UrlNoPadEncode(message); final encodedMessage = base64UrlNoPadEncode(message);
return _wrapApiPromise( return _wrapApiPromise(
js_util.callMethod(wasm, "app_call_reply", [callId, encodedMessage])); js_util.callMethod(wasm, 'app_call_reply', [callId, encodedMessage]));
} }
@override @override
Future<VeilidTableDB> openTableDB(String name, int columnCount) async { Future<VeilidTableDB> openTableDB(String name, int columnCount) async {
int id = await _wrapApiPromise( final dbid = await _wrapApiPromise<int>(
js_util.callMethod(wasm, "open_table_db", [name, columnCount])); js_util.callMethod(wasm, 'open_table_db', [name, columnCount]));
return VeilidTableDBJS._(_TDB(id, this)); return VeilidTableDBJS._(_TDB(dbid, this));
} }
@override @override
Future<bool> deleteTableDB(String name) { Future<bool> deleteTableDB(String name) =>
return _wrapApiPromise(js_util.callMethod(wasm, "delete_table_db", [name])); _wrapApiPromise(js_util.callMethod(wasm, 'delete_table_db', [name]));
}
@override @override
Timestamp now() { Timestamp now() => Timestamp.fromString(js_util.callMethod(wasm, 'now', []));
return Timestamp.fromString(js_util.callMethod(wasm, "now", []));
}
@override @override
Future<String> debug(String command) async { Future<String> debug(String command) async =>
return await _wrapApiPromise(js_util.callMethod(wasm, "debug", [command])); _wrapApiPromise(js_util.callMethod(wasm, 'debug', [command]));
}
@override @override
String veilidVersionString() { String veilidVersionString() =>
return js_util.callMethod(wasm, "veilid_version_string", []); js_util.callMethod(wasm, 'veilid_version_string', []);
}
@override @override
VeilidVersion veilidVersion() { VeilidVersion veilidVersion() {
Map<String, dynamic> jsonVersion = final jsonVersion =
jsonDecode(js_util.callMethod(wasm, "veilid_version", [])); jsonDecode(js_util.callMethod(wasm, 'veilid_version', []))
return VeilidVersion( as Map<String, dynamic>;
jsonVersion["major"], jsonVersion["minor"], jsonVersion["patch"]); return VeilidVersion(jsonVersion['major'] as int,
jsonVersion['minor'] as int, jsonVersion['patch'] as int);
} }
} }

View File

@ -11,7 +11,7 @@ class VeilidPluginStubWeb {
Future<dynamic> handleMethodCall(MethodCall call) async { Future<dynamic> handleMethodCall(MethodCall call) async {
throw PlatformException( throw PlatformException(
code: 'Unimplemented', code: 'Unimplemented',
details: 'Veilid for Web doesn\'t implement \'${call.method}\'', details: "Veilid for Web doesn't implement '${call.method}'",
); );
} }
} }

View File

@ -21,9 +21,10 @@ enum AttachmentState {
overAttached, overAttached,
detaching; detaching;
String toJson() => name.toPascalCase();
factory AttachmentState.fromJson(dynamic j) => factory AttachmentState.fromJson(dynamic j) =>
AttachmentState.values.byName((j as String).toCamelCase()); AttachmentState.values.byName((j as String).toCamelCase());
String toJson() => name.toPascalCase();
} }
////////////////////////////////////// //////////////////////////////////////
@ -36,9 +37,10 @@ enum VeilidLogLevel {
debug, debug,
trace; trace;
String toJson() => name.toPascalCase();
factory VeilidLogLevel.fromJson(dynamic j) => factory VeilidLogLevel.fromJson(dynamic j) =>
VeilidLogLevel.values.byName((j as String).toCamelCase()); VeilidLogLevel.values.byName((j as String).toCamelCase());
String toJson() => name.toPascalCase();
} }
//////////// ////////////
@ -109,8 +111,8 @@ class PeerStats with _$PeerStats {
const factory PeerStats({ const factory PeerStats({
required Timestamp timeAdded, required Timestamp timeAdded,
required RPCStats rpcStats, required RPCStats rpcStats,
LatencyStats? latency,
required TransferStatsDownUp transfer, required TransferStatsDownUp transfer,
LatencyStats? latency,
}) = _PeerStats; }) = _PeerStats;
factory PeerStats.fromJson(dynamic json) => factory PeerStats.fromJson(dynamic json) =>
@ -142,13 +144,13 @@ sealed class VeilidUpdate with _$VeilidUpdate {
String? backtrace, String? backtrace,
}) = VeilidLog; }) = VeilidLog;
const factory VeilidUpdate.appMessage({ const factory VeilidUpdate.appMessage({
TypedKey? sender,
@Uint8ListJsonConverter() required Uint8List message, @Uint8ListJsonConverter() required Uint8List message,
TypedKey? sender,
}) = VeilidAppMessage; }) = VeilidAppMessage;
const factory VeilidUpdate.appCall({ const factory VeilidUpdate.appCall({
TypedKey? sender,
@Uint8ListJsonConverter() required Uint8List message, @Uint8ListJsonConverter() required Uint8List message,
required String callId, required String callId,
TypedKey? sender,
}) = VeilidAppCall; }) = VeilidAppCall;
const factory VeilidUpdate.attachment( const factory VeilidUpdate.attachment(
{required AttachmentState state, {required AttachmentState state,

View File

@ -1,6 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:typed_data';
import 'dart:convert'; import 'dart:convert';
import 'dart:typed_data';
///////////////////////////////////// /////////////////////////////////////
/// VeilidTableDB /// VeilidTableDB
@ -12,16 +12,14 @@ abstract class VeilidTableDBTransaction {
Future<void> delete(int col, Uint8List key); Future<void> delete(int col, Uint8List key);
Future<void> storeJson(int col, Uint8List key, Object? object, Future<void> storeJson(int col, Uint8List key, Object? object,
{Object? Function(Object? nonEncodable)? toEncodable}) async { {Object? Function(Object? nonEncodable)? toEncodable}) async =>
return store(col, key, store(col, key,
utf8.encoder.convert(jsonEncode(object, toEncodable: toEncodable))); utf8.encoder.convert(jsonEncode(object, toEncodable: toEncodable)));
}
Future<void> storeStringJson(int col, String key, Object? object, Future<void> storeStringJson(int col, String key, Object? object,
{Object? Function(Object? nonEncodable)? toEncodable}) { {Object? Function(Object? nonEncodable)? toEncodable}) =>
return storeJson(col, utf8.encoder.convert(key), object, storeJson(col, utf8.encoder.convert(key), object,
toEncodable: toEncodable); toEncodable: toEncodable);
}
} }
abstract class VeilidTableDB { abstract class VeilidTableDB {
@ -34,20 +32,18 @@ abstract class VeilidTableDB {
Future<Uint8List?> delete(int col, Uint8List key); Future<Uint8List?> delete(int col, Uint8List key);
Future<void> storeJson(int col, Uint8List key, Object? object, Future<void> storeJson(int col, Uint8List key, Object? object,
{Object? Function(Object? nonEncodable)? toEncodable}) { {Object? Function(Object? nonEncodable)? toEncodable}) =>
return store(col, key, store(col, key,
utf8.encoder.convert(jsonEncode(object, toEncodable: toEncodable))); utf8.encoder.convert(jsonEncode(object, toEncodable: toEncodable)));
}
Future<void> storeStringJson(int col, String key, Object? object, Future<void> storeStringJson(int col, String key, Object? object,
{Object? Function(Object? nonEncodable)? toEncodable}) { {Object? Function(Object? nonEncodable)? toEncodable}) =>
return storeJson(col, utf8.encoder.convert(key), object, storeJson(col, utf8.encoder.convert(key), object,
toEncodable: toEncodable); toEncodable: toEncodable);
}
Future<Object?> loadJson(int col, Uint8List key, Future<Object?> loadJson(int col, Uint8List key,
{Object? Function(Object? key, Object? value)? reviver}) async { {Object? Function(Object? key, Object? value)? reviver}) async {
var s = await load(col, key); final s = await load(col, key);
if (s == null) { if (s == null) {
return null; return null;
} }
@ -55,13 +51,12 @@ abstract class VeilidTableDB {
} }
Future<Object?> loadStringJson(int col, String key, Future<Object?> loadStringJson(int col, String key,
{Object? Function(Object? key, Object? value)? reviver}) { {Object? Function(Object? key, Object? value)? reviver}) =>
return loadJson(col, utf8.encoder.convert(key), reviver: reviver); loadJson(col, utf8.encoder.convert(key), reviver: reviver);
}
Future<Object?> deleteJson(int col, Uint8List key, Future<Object?> deleteJson(int col, Uint8List key,
{Object? Function(Object? key, Object? value)? reviver}) async { {Object? Function(Object? key, Object? value)? reviver}) async {
var s = await delete(col, key); final s = await delete(col, key);
if (s == null) { if (s == null) {
return null; return null;
} }
@ -69,7 +64,6 @@ abstract class VeilidTableDB {
} }
Future<Object?> deleteStringJson(int col, String key, Future<Object?> deleteStringJson(int col, String key,
{Object? Function(Object? key, Object? value)? reviver}) { {Object? Function(Object? key, Object? value)? reviver}) =>
return deleteJson(col, utf8.encoder.convert(key), reviver: reviver); deleteJson(col, utf8.encoder.convert(key), reviver: reviver);
}
} }

View File

@ -8,28 +8,28 @@ environment:
sdk: '>=3.0.0 <4.0.0' sdk: '>=3.0.0 <4.0.0'
dependencies: dependencies:
change_case: ^1.0.1
charcode: ^1.3.1
equatable: ^2.0.5
ffi: ^2.0.0
flutter: flutter:
sdk: flutter sdk: flutter
flutter_web_plugins: flutter_web_plugins:
sdk: flutter sdk: flutter
ffi: ^2.0.0
change_case: ^1.0.1
path_provider: ^2.0.9
path: ^1.8.0
system_info2: ^3.0.2
system_info_plus: ^0.0.5
charcode: ^1.3.1
freezed_annotation: ^2.2.0 freezed_annotation: ^2.2.0
json_annotation: ^4.8.1 json_annotation: ^4.8.1
equatable: ^2.0.5 path: ^1.8.0
path_provider: ^2.0.9
system_info2: ^3.0.2
system_info_plus: ^0.0.5
dev_dependencies: dev_dependencies:
build_runner: ^2.4.6
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_lints: ^2.0.1
build_runner: ^2.4.6
freezed: ^2.3.5 freezed: ^2.3.5
json_serializable: ^6.7.1 json_serializable: ^6.7.1
lint_hard: ^4.0.0
# The following section is specific to Flutter. # The following section is specific to Flutter.
flutter: flutter:

View File

@ -2,7 +2,7 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:veilid/veilid.dart'; import 'package:veilid/veilid.dart';
void main() { void main() {
Veilid api = Veilid.instance; final api = Veilid.instance;
TestWidgetsFlutterBinding.ensureInitialized(); TestWidgetsFlutterBinding.ensureInitialized();

View File

@ -7,7 +7,9 @@ import json
from . import * from . import *
################################################################## ##################################################################
BOGUS_KEY = veilid.TypedKey.from_value(veilid.CryptoKind.CRYPTO_KIND_VLD0, veilid.PublicKey.from_bytes(b' ')) BOGUS_KEY = veilid.TypedKey.from_value(
veilid.CryptoKind.CRYPTO_KIND_VLD0, veilid.PublicKey.from_bytes(b' '))
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_get_dht_value_unopened(api_connection: veilid.VeilidAPI): async def test_get_dht_value_unopened(api_connection: veilid.VeilidAPI):
@ -24,6 +26,7 @@ async def test_open_dht_record_nonexistent_no_writer(api_connection: veilid.Veil
with pytest.raises(veilid.VeilidAPIError): with pytest.raises(veilid.VeilidAPIError):
out = await rc.open_dht_record(BOGUS_KEY, None) out = await rc.open_dht_record(BOGUS_KEY, None)
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_close_dht_record_nonexistent(api_connection: veilid.VeilidAPI): async def test_close_dht_record_nonexistent(api_connection: veilid.VeilidAPI):
rc = await api_connection.new_routing_context() rc = await api_connection.new_routing_context()
@ -31,13 +34,15 @@ async def test_close_dht_record_nonexistent(api_connection: veilid.VeilidAPI):
with pytest.raises(veilid.VeilidAPIError): with pytest.raises(veilid.VeilidAPIError):
await rc.close_dht_record(BOGUS_KEY) await rc.close_dht_record(BOGUS_KEY)
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_delete_dht_record_nonexistent(api_connection: veilid.VeilidAPI): async def test_delete_dht_record_nonexistent(api_connection: veilid.VeilidAPI):
rc = await api_connection.new_routing_context() rc = await api_connection.new_routing_context()
async with rc: async with rc:
with pytest.raises(veilid.VeilidAPIError): with pytest.raises(veilid.VeilidAPIError):
await rc.delete_dht_record(BOGUS_KEY) await rc.delete_dht_record(BOGUS_KEY)
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_create_delete_dht_record_simple(api_connection: veilid.VeilidAPI): async def test_create_delete_dht_record_simple(api_connection: veilid.VeilidAPI):
rc = await api_connection.new_routing_context() rc = await api_connection.new_routing_context()
@ -46,6 +51,7 @@ async def test_create_delete_dht_record_simple(api_connection: veilid.VeilidAPI)
await rc.close_dht_record(rec.key) await rc.close_dht_record(rec.key)
await rc.delete_dht_record(rec.key) await rc.delete_dht_record(rec.key)
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_get_dht_value_nonexistent(api_connection: veilid.VeilidAPI): async def test_get_dht_value_nonexistent(api_connection: veilid.VeilidAPI):
rc = await api_connection.new_routing_context() rc = await api_connection.new_routing_context()
@ -55,34 +61,34 @@ async def test_get_dht_value_nonexistent(api_connection: veilid.VeilidAPI):
await rc.close_dht_record(rec.key) await rc.close_dht_record(rec.key)
await rc.delete_dht_record(rec.key) await rc.delete_dht_record(rec.key)
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_set_get_dht_value(api_connection: veilid.VeilidAPI): async def test_set_get_dht_value(api_connection: veilid.VeilidAPI):
rc = await api_connection.new_routing_context() rc = await api_connection.new_routing_context()
async with rc: async with rc:
rec = await rc.create_dht_record(veilid.DHTSchema.dflt(2)) rec = await rc.create_dht_record(veilid.DHTSchema.dflt(2))
vd = await rc.set_dht_value(rec.key, 0, b"BLAH BLAH BLAH") vd = await rc.set_dht_value(rec.key, 0, b"BLAH BLAH BLAH")
assert vd != None assert vd == None
vd2 = await rc.get_dht_value(rec.key, 0, False) vd2 = await rc.get_dht_value(rec.key, 0, False)
assert vd2 != None assert vd2 != None
vd3 = await rc.get_dht_value(rec.key, 0, True) vd3 = await rc.get_dht_value(rec.key, 0, True)
assert vd3 != None assert vd3 != None
vd4 = await rc.get_dht_value(rec.key, 1, False) vd4 = await rc.get_dht_value(rec.key, 1, False)
assert vd4 == None assert vd4 == None
print("vd: {}", vd.__dict__)
print("vd2: {}", vd2.__dict__) print("vd2: {}", vd2.__dict__)
print("vd3: {}", vd3.__dict__) print("vd3: {}", vd3.__dict__)
assert vd == vd2
assert vd2 == vd3 assert vd2 == vd3
await rc.close_dht_record(rec.key) await rc.close_dht_record(rec.key)
await rc.delete_dht_record(rec.key) await rc.delete_dht_record(rec.key)
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_open_writer_dht_value(api_connection: veilid.VeilidAPI): async def test_open_writer_dht_value(api_connection: veilid.VeilidAPI):
rc = await api_connection.new_routing_context() rc = await api_connection.new_routing_context()
@ -104,10 +110,7 @@ async def test_open_writer_dht_value(api_connection: veilid.VeilidAPI):
# Test subkey writes # Test subkey writes
vdtemp = await rc.set_dht_value(key, 1, va) vdtemp = await rc.set_dht_value(key, 1, va)
assert vdtemp != None assert vdtemp == None
assert vdtemp.data == va
assert vdtemp.seq == 0
assert vdtemp.writer == owner
vdtemp = await rc.get_dht_value(key, 1, False) vdtemp = await rc.get_dht_value(key, 1, False)
assert vdtemp.data == va assert vdtemp.data == va
@ -118,8 +121,7 @@ async def test_open_writer_dht_value(api_connection: veilid.VeilidAPI):
assert vdtemp == None assert vdtemp == None
vdtemp = await rc.set_dht_value(key, 0, vb) vdtemp = await rc.set_dht_value(key, 0, vb)
assert vdtemp.data == vb assert vdtemp == None
assert vdtemp.seq == 0
vdtemp = await rc.get_dht_value(key, 0, True) vdtemp = await rc.get_dht_value(key, 0, True)
assert vdtemp.data == vb assert vdtemp.data == vb
@ -129,17 +131,11 @@ async def test_open_writer_dht_value(api_connection: veilid.VeilidAPI):
# Equal value should not trigger sequence number update # Equal value should not trigger sequence number update
vdtemp = await rc.set_dht_value(key, 1, va) vdtemp = await rc.set_dht_value(key, 1, va)
assert vdtemp != None assert vdtemp == None
assert vdtemp.data == va
assert vdtemp.seq == 0
assert vdtemp.writer == owner
# Different value should trigger sequence number update # Different value should trigger sequence number update
vdtemp = await rc.set_dht_value(key, 1, vb) vdtemp = await rc.set_dht_value(key, 1, vb)
assert vdtemp != None assert vdtemp == None
assert vdtemp.data == vb
assert vdtemp.seq == 1
assert vdtemp.writer == owner
# Now that we initialized some subkeys # Now that we initialized some subkeys
# and verified they stored correctly # and verified they stored correctly
@ -166,11 +162,8 @@ async def test_open_writer_dht_value(api_connection: veilid.VeilidAPI):
# Verify subkey 1 can be set a second time and it updates because seq is newer # Verify subkey 1 can be set a second time and it updates because seq is newer
vdtemp = await rc.set_dht_value(key, 1, vc) vdtemp = await rc.set_dht_value(key, 1, vc)
assert vdtemp != None assert vdtemp == None
assert vdtemp.data == vc
assert vdtemp.seq == 2
assert vdtemp.writer == owner
# Verify the network got the subkey update with a refresh check # Verify the network got the subkey update with a refresh check
vdtemp = await rc.get_dht_value(key, 1, True) vdtemp = await rc.get_dht_value(key, 1, True)
assert vdtemp != None assert vdtemp != None
@ -183,7 +176,7 @@ async def test_open_writer_dht_value(api_connection: veilid.VeilidAPI):
await rc.close_dht_record(key) await rc.close_dht_record(key)
await rc.delete_dht_record(key) await rc.delete_dht_record(key)
rec = await rc.open_dht_record(key, other_keypair) rec = await rc.open_dht_record(key, other_keypair)
assert rec != None assert rec != None
assert rec.key == key assert rec.key == key
@ -195,12 +188,11 @@ async def test_open_writer_dht_value(api_connection: veilid.VeilidAPI):
# Verify subkey 1 can NOT be set because we have the wrong writer # Verify subkey 1 can NOT be set because we have the wrong writer
with pytest.raises(veilid.VeilidAPIError): with pytest.raises(veilid.VeilidAPIError):
vdtemp = await rc.set_dht_value(key, 1, va) vdtemp = await rc.set_dht_value(key, 1, va)
# Verify subkey 0 can NOT be set because we have the wrong writer # Verify subkey 0 can NOT be set because we have the wrong writer
with pytest.raises(veilid.VeilidAPIError): with pytest.raises(veilid.VeilidAPIError):
vdtemp = await rc.set_dht_value(key, 0, va) vdtemp = await rc.set_dht_value(key, 0, va)
# Clean up # Clean up
await rc.close_dht_record(key) await rc.close_dht_record(key)
await rc.delete_dht_record(key) await rc.delete_dht_record(key)