import 'package:protobuf/protobuf.dart'; import 'package:veilid/veilid.dart'; import 'dart:typed_data'; import 'tools.dart'; class DHTRecord { final VeilidRoutingContext _dhtctx; final DHTRecordDescriptor _recordDescriptor; final int _defaultSubkey; final KeyPair? _writer; late final DHTRecordEncryption _encryption; static Future create(VeilidRoutingContext dhtctx, {DHTSchema schema = const DHTSchema.dflt(oCnt: 1), int defaultSubkey = 0, KeyPair? writer, DHTRecordEncryptionFactory crypto = DHTRecordEncryption.private}) async { DHTRecordDescriptor recordDescriptor = await dhtctx.createDHTRecord(schema); final rec = DHTRecord( dhtctx: dhtctx, recordDescriptor: recordDescriptor, defaultSubkey: defaultSubkey, writer: writer); final encryption = crypto) } static Future open( VeilidRoutingContext dhtctx, TypedKey recordKey, KeyPair? writer, {int defaultSubkey = 0, KeyPair? writer, DHTRecordEncryptionFactory encrypt = DHTRecordEncryption.private}) 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, KeyPair? writer}) : _dhtctx = dhtctx, _recordDescriptor = recordDescriptor, _defaultSubkey = defaultSubkey, _writer = writer; int _subkey(int subkey) => (subkey == -1) ? _defaultSubkey : subkey; TypedKey key() { return _recordDescriptor.key; } PublicKey owner() { return _recordDescriptor.owner; } KeyPair? ownerKeyPair() { final ownerSecret = _recordDescriptor.ownerSecret; if (ownerSecret == null) { return null; } return KeyPair(key: _recordDescriptor.owner, secret: ownerSecret); } KeyPair? writer() { return _writer; } Future close() async { await _dhtctx.closeDHTRecord(_recordDescriptor.key); } Future delete() async { await _dhtctx.deleteDHTRecord(_recordDescriptor.key); } Future scope(Future Function(DHTRecord) scopeFunction) async { try { return await scopeFunction(this); } finally { close(); } } Future deleteScope(Future Function(DHTRecord) scopeFunction) async { try { return await scopeFunction(this); } catch (_) { delete(); rethrow; } finally { close(); } } Future 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 getJson(T Function(Map) 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 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 eventualUpdateBytes( Future 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 eventualWriteJson(T newValue, {int subkey = -1}) { return eventualWriteBytes(jsonEncodeBytes(newValue), subkey: subkey); } Future eventualWriteProtobuf(T newValue, {int subkey = -1}) { return eventualWriteBytes(newValue.writeToBuffer(), subkey: subkey); } Future eventualUpdateJson( T Function(Map) fromJson, Future Function(T) update, {int subkey = -1}) { return eventualUpdateBytes(jsonUpdate(fromJson, update), subkey: subkey); } Future eventualUpdateProtobuf( T Function(List) fromBuffer, Future Function(T) update, {int subkey = -1}) { return eventualUpdateBytes(protobufUpdate(fromBuffer, update), subkey: subkey); } }