better dht record class

This commit is contained in:
Christien Rioux 2023-07-17 22:39:33 -04:00
parent 90fc2e5f85
commit bf813d7d0f
7 changed files with 300 additions and 132 deletions

View File

@ -1,3 +1,4 @@
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:veilid/veilid.dart'; import 'package:veilid/veilid.dart';
@ -7,14 +8,15 @@ part 'identity.g.dart';
// AccountOwnerInfo is the key and owner info for the account dht key that is // AccountOwnerInfo is the key and owner info for the account dht key that is
// stored in the identity key // stored in the identity key
@freezed @freezed
class AccountOwnerInfo with _$AccountOwnerInfo { class AccountRecordInfo with _$AccountRecordInfo {
const factory AccountOwnerInfo({ const factory AccountRecordInfo({
// Top level account keys and secrets // Top level account keys and secrets
required Map<String, TypedKeyPair> accountKeyPairs, required TypedKey key,
}) = _AccountOwnerInfo; required KeyPair owner,
}) = _AccountRecordInfo;
factory AccountOwnerInfo.fromJson(Map<String, dynamic> json) => factory AccountRecordInfo.fromJson(Map<String, dynamic> json) =>
_$AccountOwnerInfoFromJson(json); _$AccountRecordInfoFromJson(json);
} }
// Identity Key points to accounts associated with this identity // Identity Key points to accounts associated with this identity
@ -27,7 +29,7 @@ class AccountOwnerInfo with _$AccountOwnerInfo {
class Identity with _$Identity { class Identity with _$Identity {
const factory Identity({ const factory Identity({
// Top level account keys and secrets // Top level account keys and secrets
required Map<String, TypedKeyPair> accountKeyPairs, required IMap<String, ISet<AccountRecordInfo>> accountRecords,
}) = _Identity; }) = _Identity;
factory Identity.fromJson(Map<String, dynamic> json) => factory Identity.fromJson(Map<String, dynamic> json) =>

View File

@ -14,35 +14,35 @@ T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError( final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
AccountOwnerInfo _$AccountOwnerInfoFromJson(Map<String, dynamic> json) { AccountRecordInfo _$AccountRecordInfoFromJson(Map<String, dynamic> json) {
return _AccountOwnerInfo.fromJson(json); return _AccountRecordInfo.fromJson(json);
} }
/// @nodoc /// @nodoc
mixin _$AccountOwnerInfo { mixin _$AccountRecordInfo {
// Top level account keys and secrets // Top level account keys and secrets
Map<String, TypedKeyPair> get accountKeyPairs => Typed<FixedEncodedString43> get key => throw _privateConstructorUsedError;
throw _privateConstructorUsedError; KeyPair get owner => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError; Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true) @JsonKey(ignore: true)
$AccountOwnerInfoCopyWith<AccountOwnerInfo> get copyWith => $AccountRecordInfoCopyWith<AccountRecordInfo> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }
/// @nodoc /// @nodoc
abstract class $AccountOwnerInfoCopyWith<$Res> { abstract class $AccountRecordInfoCopyWith<$Res> {
factory $AccountOwnerInfoCopyWith( factory $AccountRecordInfoCopyWith(
AccountOwnerInfo value, $Res Function(AccountOwnerInfo) then) = AccountRecordInfo value, $Res Function(AccountRecordInfo) then) =
_$AccountOwnerInfoCopyWithImpl<$Res, AccountOwnerInfo>; _$AccountRecordInfoCopyWithImpl<$Res, AccountRecordInfo>;
@useResult @useResult
$Res call({Map<String, TypedKeyPair> accountKeyPairs}); $Res call({Typed<FixedEncodedString43> key, KeyPair owner});
} }
/// @nodoc /// @nodoc
class _$AccountOwnerInfoCopyWithImpl<$Res, $Val extends AccountOwnerInfo> class _$AccountRecordInfoCopyWithImpl<$Res, $Val extends AccountRecordInfo>
implements $AccountOwnerInfoCopyWith<$Res> { implements $AccountRecordInfoCopyWith<$Res> {
_$AccountOwnerInfoCopyWithImpl(this._value, this._then); _$AccountRecordInfoCopyWithImpl(this._value, this._then);
// ignore: unused_field // ignore: unused_field
final $Val _value; final $Val _value;
@ -52,116 +52,122 @@ class _$AccountOwnerInfoCopyWithImpl<$Res, $Val extends AccountOwnerInfo>
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
Object? accountKeyPairs = null, Object? key = null,
Object? owner = null,
}) { }) {
return _then(_value.copyWith( return _then(_value.copyWith(
accountKeyPairs: null == accountKeyPairs key: null == key
? _value.accountKeyPairs ? _value.key
: accountKeyPairs // ignore: cast_nullable_to_non_nullable : key // ignore: cast_nullable_to_non_nullable
as Map<String, TypedKeyPair>, as Typed<FixedEncodedString43>,
owner: null == owner
? _value.owner
: owner // ignore: cast_nullable_to_non_nullable
as KeyPair,
) as $Val); ) as $Val);
} }
} }
/// @nodoc /// @nodoc
abstract class _$$_AccountOwnerInfoCopyWith<$Res> abstract class _$$_AccountRecordInfoCopyWith<$Res>
implements $AccountOwnerInfoCopyWith<$Res> { implements $AccountRecordInfoCopyWith<$Res> {
factory _$$_AccountOwnerInfoCopyWith( factory _$$_AccountRecordInfoCopyWith(_$_AccountRecordInfo value,
_$_AccountOwnerInfo value, $Res Function(_$_AccountOwnerInfo) then) = $Res Function(_$_AccountRecordInfo) then) =
__$$_AccountOwnerInfoCopyWithImpl<$Res>; __$$_AccountRecordInfoCopyWithImpl<$Res>;
@override @override
@useResult @useResult
$Res call({Map<String, TypedKeyPair> accountKeyPairs}); $Res call({Typed<FixedEncodedString43> key, KeyPair owner});
} }
/// @nodoc /// @nodoc
class __$$_AccountOwnerInfoCopyWithImpl<$Res> class __$$_AccountRecordInfoCopyWithImpl<$Res>
extends _$AccountOwnerInfoCopyWithImpl<$Res, _$_AccountOwnerInfo> extends _$AccountRecordInfoCopyWithImpl<$Res, _$_AccountRecordInfo>
implements _$$_AccountOwnerInfoCopyWith<$Res> { implements _$$_AccountRecordInfoCopyWith<$Res> {
__$$_AccountOwnerInfoCopyWithImpl( __$$_AccountRecordInfoCopyWithImpl(
_$_AccountOwnerInfo _value, $Res Function(_$_AccountOwnerInfo) _then) _$_AccountRecordInfo _value, $Res Function(_$_AccountRecordInfo) _then)
: super(_value, _then); : super(_value, _then);
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
Object? accountKeyPairs = null, Object? key = null,
Object? owner = null,
}) { }) {
return _then(_$_AccountOwnerInfo( return _then(_$_AccountRecordInfo(
accountKeyPairs: null == accountKeyPairs key: null == key
? _value._accountKeyPairs ? _value.key
: accountKeyPairs // ignore: cast_nullable_to_non_nullable : key // ignore: cast_nullable_to_non_nullable
as Map<String, TypedKeyPair>, as Typed<FixedEncodedString43>,
owner: null == owner
? _value.owner
: owner // ignore: cast_nullable_to_non_nullable
as KeyPair,
)); ));
} }
} }
/// @nodoc /// @nodoc
@JsonSerializable() @JsonSerializable()
class _$_AccountOwnerInfo implements _AccountOwnerInfo { class _$_AccountRecordInfo implements _AccountRecordInfo {
const _$_AccountOwnerInfo( const _$_AccountRecordInfo({required this.key, required this.owner});
{required final Map<String, TypedKeyPair> accountKeyPairs})
: _accountKeyPairs = accountKeyPairs;
factory _$_AccountOwnerInfo.fromJson(Map<String, dynamic> json) => factory _$_AccountRecordInfo.fromJson(Map<String, dynamic> json) =>
_$$_AccountOwnerInfoFromJson(json); _$$_AccountRecordInfoFromJson(json);
// Top level account keys and secrets
final Map<String, TypedKeyPair> _accountKeyPairs;
// Top level account keys and secrets // Top level account keys and secrets
@override @override
Map<String, TypedKeyPair> get accountKeyPairs { final Typed<FixedEncodedString43> key;
if (_accountKeyPairs is EqualUnmodifiableMapView) return _accountKeyPairs; @override
// ignore: implicit_dynamic_type final KeyPair owner;
return EqualUnmodifiableMapView(_accountKeyPairs);
}
@override @override
String toString() { String toString() {
return 'AccountOwnerInfo(accountKeyPairs: $accountKeyPairs)'; return 'AccountRecordInfo(key: $key, owner: $owner)';
} }
@override @override
bool operator ==(dynamic other) { bool operator ==(dynamic other) {
return identical(this, other) || return identical(this, other) ||
(other.runtimeType == runtimeType && (other.runtimeType == runtimeType &&
other is _$_AccountOwnerInfo && other is _$_AccountRecordInfo &&
const DeepCollectionEquality() (identical(other.key, key) || other.key == key) &&
.equals(other._accountKeyPairs, _accountKeyPairs)); (identical(other.owner, owner) || other.owner == owner));
} }
@JsonKey(ignore: true) @JsonKey(ignore: true)
@override @override
int get hashCode => Object.hash( int get hashCode => Object.hash(runtimeType, key, owner);
runtimeType, const DeepCollectionEquality().hash(_accountKeyPairs));
@JsonKey(ignore: true) @JsonKey(ignore: true)
@override @override
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
_$$_AccountOwnerInfoCopyWith<_$_AccountOwnerInfo> get copyWith => _$$_AccountRecordInfoCopyWith<_$_AccountRecordInfo> get copyWith =>
__$$_AccountOwnerInfoCopyWithImpl<_$_AccountOwnerInfo>(this, _$identity); __$$_AccountRecordInfoCopyWithImpl<_$_AccountRecordInfo>(
this, _$identity);
@override @override
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
return _$$_AccountOwnerInfoToJson( return _$$_AccountRecordInfoToJson(
this, this,
); );
} }
} }
abstract class _AccountOwnerInfo implements AccountOwnerInfo { abstract class _AccountRecordInfo implements AccountRecordInfo {
const factory _AccountOwnerInfo( const factory _AccountRecordInfo(
{required final Map<String, TypedKeyPair> accountKeyPairs}) = {required final Typed<FixedEncodedString43> key,
_$_AccountOwnerInfo; required final KeyPair owner}) = _$_AccountRecordInfo;
factory _AccountOwnerInfo.fromJson(Map<String, dynamic> json) = factory _AccountRecordInfo.fromJson(Map<String, dynamic> json) =
_$_AccountOwnerInfo.fromJson; _$_AccountRecordInfo.fromJson;
@override // Top level account keys and secrets @override // Top level account keys and secrets
Map<String, TypedKeyPair> get accountKeyPairs; Typed<FixedEncodedString43> get key;
@override
KeyPair get owner;
@override @override
@JsonKey(ignore: true) @JsonKey(ignore: true)
_$$_AccountOwnerInfoCopyWith<_$_AccountOwnerInfo> get copyWith => _$$_AccountRecordInfoCopyWith<_$_AccountRecordInfo> get copyWith =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
} }
@ -172,7 +178,7 @@ Identity _$IdentityFromJson(Map<String, dynamic> json) {
/// @nodoc /// @nodoc
mixin _$Identity { mixin _$Identity {
// Top level account keys and secrets // Top level account keys and secrets
Map<String, TypedKeyPair> get accountKeyPairs => IMap<String, ISet<AccountRecordInfo>> get accountRecords =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError; Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@ -186,7 +192,7 @@ abstract class $IdentityCopyWith<$Res> {
factory $IdentityCopyWith(Identity value, $Res Function(Identity) then) = factory $IdentityCopyWith(Identity value, $Res Function(Identity) then) =
_$IdentityCopyWithImpl<$Res, Identity>; _$IdentityCopyWithImpl<$Res, Identity>;
@useResult @useResult
$Res call({Map<String, TypedKeyPair> accountKeyPairs}); $Res call({IMap<String, ISet<AccountRecordInfo>> accountRecords});
} }
/// @nodoc /// @nodoc
@ -202,13 +208,13 @@ class _$IdentityCopyWithImpl<$Res, $Val extends Identity>
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
Object? accountKeyPairs = null, Object? accountRecords = null,
}) { }) {
return _then(_value.copyWith( return _then(_value.copyWith(
accountKeyPairs: null == accountKeyPairs accountRecords: null == accountRecords
? _value.accountKeyPairs ? _value.accountRecords
: accountKeyPairs // ignore: cast_nullable_to_non_nullable : accountRecords // ignore: cast_nullable_to_non_nullable
as Map<String, TypedKeyPair>, as IMap<String, ISet<AccountRecordInfo>>,
) as $Val); ) as $Val);
} }
} }
@ -220,7 +226,7 @@ abstract class _$$_IdentityCopyWith<$Res> implements $IdentityCopyWith<$Res> {
__$$_IdentityCopyWithImpl<$Res>; __$$_IdentityCopyWithImpl<$Res>;
@override @override
@useResult @useResult
$Res call({Map<String, TypedKeyPair> accountKeyPairs}); $Res call({IMap<String, ISet<AccountRecordInfo>> accountRecords});
} }
/// @nodoc /// @nodoc
@ -234,13 +240,13 @@ class __$$_IdentityCopyWithImpl<$Res>
@pragma('vm:prefer-inline') @pragma('vm:prefer-inline')
@override @override
$Res call({ $Res call({
Object? accountKeyPairs = null, Object? accountRecords = null,
}) { }) {
return _then(_$_Identity( return _then(_$_Identity(
accountKeyPairs: null == accountKeyPairs accountRecords: null == accountRecords
? _value._accountKeyPairs ? _value.accountRecords
: accountKeyPairs // ignore: cast_nullable_to_non_nullable : accountRecords // ignore: cast_nullable_to_non_nullable
as Map<String, TypedKeyPair>, as IMap<String, ISet<AccountRecordInfo>>,
)); ));
} }
} }
@ -248,25 +254,18 @@ class __$$_IdentityCopyWithImpl<$Res>
/// @nodoc /// @nodoc
@JsonSerializable() @JsonSerializable()
class _$_Identity implements _Identity { class _$_Identity implements _Identity {
const _$_Identity({required final Map<String, TypedKeyPair> accountKeyPairs}) const _$_Identity({required this.accountRecords});
: _accountKeyPairs = accountKeyPairs;
factory _$_Identity.fromJson(Map<String, dynamic> json) => factory _$_Identity.fromJson(Map<String, dynamic> json) =>
_$$_IdentityFromJson(json); _$$_IdentityFromJson(json);
// Top level account keys and secrets
final Map<String, TypedKeyPair> _accountKeyPairs;
// Top level account keys and secrets // Top level account keys and secrets
@override @override
Map<String, TypedKeyPair> get accountKeyPairs { final IMap<String, ISet<AccountRecordInfo>> accountRecords;
if (_accountKeyPairs is EqualUnmodifiableMapView) return _accountKeyPairs;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_accountKeyPairs);
}
@override @override
String toString() { String toString() {
return 'Identity(accountKeyPairs: $accountKeyPairs)'; return 'Identity(accountRecords: $accountRecords)';
} }
@override @override
@ -274,14 +273,13 @@ class _$_Identity implements _Identity {
return identical(this, other) || return identical(this, other) ||
(other.runtimeType == runtimeType && (other.runtimeType == runtimeType &&
other is _$_Identity && other is _$_Identity &&
const DeepCollectionEquality() (identical(other.accountRecords, accountRecords) ||
.equals(other._accountKeyPairs, _accountKeyPairs)); other.accountRecords == accountRecords));
} }
@JsonKey(ignore: true) @JsonKey(ignore: true)
@override @override
int get hashCode => Object.hash( int get hashCode => Object.hash(runtimeType, accountRecords);
runtimeType, const DeepCollectionEquality().hash(_accountKeyPairs));
@JsonKey(ignore: true) @JsonKey(ignore: true)
@override @override
@ -299,12 +297,13 @@ class _$_Identity implements _Identity {
abstract class _Identity implements Identity { abstract class _Identity implements Identity {
const factory _Identity( const factory _Identity(
{required final Map<String, TypedKeyPair> accountKeyPairs}) = _$_Identity; {required final IMap<String, ISet<AccountRecordInfo>>
accountRecords}) = _$_Identity;
factory _Identity.fromJson(Map<String, dynamic> json) = _$_Identity.fromJson; factory _Identity.fromJson(Map<String, dynamic> json) = _$_Identity.fromJson;
@override // Top level account keys and secrets @override // Top level account keys and secrets
Map<String, TypedKeyPair> get accountKeyPairs; IMap<String, ISet<AccountRecordInfo>> get accountRecords;
@override @override
@JsonKey(ignore: true) @JsonKey(ignore: true)
_$$_IdentityCopyWith<_$_Identity> get copyWith => _$$_IdentityCopyWith<_$_Identity> get copyWith =>

View File

@ -6,29 +6,37 @@ part of 'identity.dart';
// JsonSerializableGenerator // JsonSerializableGenerator
// ************************************************************************** // **************************************************************************
_$_AccountOwnerInfo _$$_AccountOwnerInfoFromJson(Map<String, dynamic> json) => _$_AccountRecordInfo _$$_AccountRecordInfoFromJson(Map<String, dynamic> json) =>
_$_AccountOwnerInfo( _$_AccountRecordInfo(
accountKeyPairs: (json['account_key_pairs'] as Map<String, dynamic>).map( key: Typed<FixedEncodedString43>.fromJson(json['key']),
(k, e) => MapEntry(k, TypedKeyPair.fromJson(e)), owner: KeyPair.fromJson(json['owner']),
),
); );
Map<String, dynamic> _$$_AccountOwnerInfoToJson(_$_AccountOwnerInfo instance) => Map<String, dynamic> _$$_AccountRecordInfoToJson(
_$_AccountRecordInfo instance) =>
<String, dynamic>{ <String, dynamic>{
'account_key_pairs': 'key': instance.key.toJson(),
instance.accountKeyPairs.map((k, e) => MapEntry(k, e.toJson())), 'owner': instance.owner.toJson(),
}; };
_$_Identity _$$_IdentityFromJson(Map<String, dynamic> json) => _$_Identity( _$_Identity _$$_IdentityFromJson(Map<String, dynamic> json) => _$_Identity(
accountKeyPairs: (json['account_key_pairs'] as Map<String, dynamic>).map( accountRecords: IMap<String, ISet<AccountRecordInfo>>.fromJson(
(k, e) => MapEntry(k, TypedKeyPair.fromJson(e)), json['account_records'] as Map<String, dynamic>,
), (value) => value as String,
(value) => ISet<AccountRecordInfo>.fromJson(
value,
(value) =>
AccountRecordInfo.fromJson(value as Map<String, dynamic>))),
); );
Map<String, dynamic> _$$_IdentityToJson(_$_Identity instance) => Map<String, dynamic> _$$_IdentityToJson(_$_Identity instance) =>
<String, dynamic>{ <String, dynamic>{
'account_key_pairs': 'account_records': instance.accountRecords.toJson(
instance.accountKeyPairs.map((k, e) => MapEntry(k, e.toJson())), (value) => value,
(value) => value.toJson(
(value) => value.toJson(),
),
),
}; };
_$_IdentityMaster _$$_IdentityMasterFromJson(Map<String, dynamic> json) => _$_IdentityMaster _$$_IdentityMasterFromJson(Map<String, dynamic> json) =>

View File

@ -102,23 +102,48 @@ class LocalAccountManager {
} }
} }
/// Adds a 'VeilidChat' account record to an identity key
Future<void> updateIdentityKey( Future<void> updateIdentityKey(
VeilidRoutingContext dhtctx, VeilidRoutingContext dhtctx,
TypedKey identityRecordKey, TypedKey identityRecordKey,
TypedKey accountRecordKey, TypedKey accountRecordKey,
KeyPair accountRecordOwner) async { KeyPair accountRecordOwner) async {
// Account record to add
final accountRecordInfo =
AccountRecordInfo(key: accountRecordKey, owner: accountRecordOwner);
// Eventually update identity key
eventuallyConsistentUpdate(
dhtctx,
identityRecordKey,
0,
true,
jsonUpdate(Identity.fromJson, (oldObj) async {
final accountRecords = IMapOfSets.from(oldObj.accountRecords)
.add("VeilidChat", accountRecordInfo)
.asIMap();
return oldObj.copyWith(accountRecords: accountRecords);
}));
// Get existing identity key // Get existing identity key
ValueData? identityValueData = ValueData? identityValueData =
await dhtctx.getDHTValue(identityRecordKey, 0, true); await dhtctx.getDHTValue(identityRecordKey, 0, true);
do {
if (identityValueData == null) { if (identityValueData == null) {
throw const FormatException("Identity does not exist"); throw const FormatException("Identity does not exist");
} }
var identity = identityValueData.readJsonData(Identity.fromJson); var identity = identityValueData.readJsonData(Identity.fromJson);
xxx make back to bytes function and do update loop and maybe make that a tool function too (consistentUpdate)
// Update identity key to include account // Update identity key to include account
const identity = Identity(accountKeyPairs: {}); final accountRecords = IMapOfSets.from(identity.accountRecords)
final identityBytes = Uint8List.fromList(utf8.encode(jsonEncode(identity))); .add("VeilidChat", accountRecordInfo)
await dhtctx.setDHTValue(identityRec.key, 0, identityBytes); .asIMap();
identity = identity.copyWith(accountRecords: accountRecords);
final identityBytes = jsonEncodeBytes(identity);
identityValueData =
await dhtctx.setDHTValue(identityRecordKey, 0, identityBytes);
} while (identityValueData != null);
} }
/// Creates a new account associated with master identity /// Creates a new account associated with master identity

109
lib/tools/dht_record.dart Normal file
View File

@ -0,0 +1,109 @@
import 'package:veilid/veilid.dart';
import 'dart:typed_data';
import 'tools.dart';
class DHTRecord {
final VeilidRoutingContext _dhtctx;
final DHTRecordDescriptor _recordDescriptor;
final int _defaultSubkey;
static Future<DHTRecord> create(VeilidRoutingContext dhtctx,
{DHTSchema schema = const DHTSchema.dflt(oCnt: 1),
int defaultSubkey = 0}) async {
DHTRecordDescriptor recordDescriptor = await dhtctx.createDHTRecord(schema);
return DHTRecord(
dhtctx: dhtctx,
recordDescriptor: recordDescriptor,
defaultSubkey: defaultSubkey);
}
static Future<DHTRecord> open(
VeilidRoutingContext dhtctx, TypedKey recordKey, KeyPair? writer,
{int defaultSubkey = 0}) async {
DHTRecordDescriptor recordDescriptor =
await dhtctx.openDHTRecord(recordKey, writer);
return DHTRecord(
dhtctx: dhtctx,
recordDescriptor: recordDescriptor,
defaultSubkey: defaultSubkey);
}
DHTRecord(
{required VeilidRoutingContext dhtctx,
required DHTRecordDescriptor recordDescriptor,
int defaultSubkey = 0})
: _dhtctx = dhtctx,
_recordDescriptor = recordDescriptor,
_defaultSubkey = defaultSubkey;
int _subkey(int subkey) => (subkey == -1) ? _defaultSubkey : subkey;
Future<Uint8List?> get({int subkey = -1, bool forceRefresh = false}) async {
ValueData? valueData = await _dhtctx.getDHTValue(
_recordDescriptor.key, _subkey(subkey), false);
if (valueData == null) {
return null;
}
return valueData.data;
}
Future<T?> getJson<T>(T Function(Map<String, dynamic>) fromJson,
{int subkey = -1, bool forceRefresh = false}) async {
ValueData? valueData = await _dhtctx.getDHTValue(
_recordDescriptor.key, _subkey(subkey), false);
if (valueData == null) {
return null;
}
return valueData.readJsonData(fromJson);
}
Future<void> eventualWriteBytes(Uint8List newValue, {int subkey = -1}) async {
// Get existing identity key
ValueData? valueData;
do {
// Ensure it exists already
if (valueData == null) {
throw const FormatException("value does not exist");
}
// Set the new data
valueData = await _dhtctx.setDHTValue(
_recordDescriptor.key, _subkey(subkey), newValue);
// Repeat if newer data on the network was found
} while (valueData != null);
}
Future<void> eventualUpdateBytes(
Future<Uint8List> Function(Uint8List oldValue) update,
{int subkey = -1}) async {
// Get existing identity key
ValueData? valueData = await _dhtctx.getDHTValue(
_recordDescriptor.key, _subkey(subkey), false);
do {
// Ensure it exists already
if (valueData == null) {
throw const FormatException("value does not exist");
}
// Update the data
final newData = await update(valueData.data);
// Set it back
valueData = await _dhtctx.setDHTValue(
_recordDescriptor.key, _subkey(subkey), newData);
// Repeat if newer data on the network was found
} while (valueData != null);
}
Future<void> eventualWriteJson<T>(T newValue, {int subkey = -1}) {
return eventualWriteBytes(jsonEncodeBytes(newValue), subkey: subkey);
}
Future<void> eventualUpdateJson<T>(
T Function(Map<String, dynamic>) fromJson, Future<T> Function(T) update,
{int subkey = -1}) {
return eventualUpdateBytes(jsonUpdate(fromJson, update), subkey: subkey);
}
}

30
lib/tools/json_tools.dart Normal file
View File

@ -0,0 +1,30 @@
// import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:veilid/veilid.dart';
import 'dart:typed_data';
import 'dart:convert';
extension FromValueDataJsonExt on ValueData {
T readJsonData<T>(T Function(Map<String, dynamic>) fromJson) {
return fromJson(jsonDecode(utf8.decode(data)));
}
}
Uint8List jsonEncodeBytes(Object? object,
{Object? Function(Object?)? toEncodable}) {
return Uint8List.fromList(
utf8.encode(jsonEncode(object, toEncodable: toEncodable)));
}
Future<Uint8List> jsonUpdateBytes<T>(T Function(Map<String, dynamic>) fromJson,
Uint8List oldBytes, Future<T> Function(T) update) async {
T oldObj = fromJson(jsonDecode(utf8.decode(oldBytes)));
T newObj = await update(oldObj);
return jsonEncodeBytes(newObj);
}
Future<Uint8List> Function(Uint8List) jsonUpdate<T>(
T Function(Map<String, dynamic>) fromJson, Future<T> Function(T) update) {
return (Uint8List oldBytes) {
return jsonUpdateBytes(fromJson, oldBytes, update);
};
}

View File

@ -1,9 +1,4 @@
export 'external_stream_state.dart'; export 'external_stream_state.dart';
import 'package:veilid/veilid.dart'; export 'dht_record.dart';
import 'dart:convert'; export 'json_tools.dart';
export 'phono_byte.dart';
extension FromValueDataJsonExt on ValueData {
T readJsonData<T>(T Function(Map<String, dynamic>) fromJson) {
return fromJson(jsonDecode(utf8.decode(data)));
}
}