2024-05-31 22:37:25 +00:00
|
|
|
part of 'firo_cache.dart';
|
|
|
|
|
2024-06-10 19:28:49 +00:00
|
|
|
typedef LTagPair = ({String tag, String txid});
|
|
|
|
|
2024-05-31 22:37:25 +00:00
|
|
|
/// Wrapper class for [_FiroCache] as [_FiroCache] should eventually be handled in a
|
|
|
|
/// background isolate and [FiroCacheCoordinator] should manage that isolate
|
|
|
|
abstract class FiroCacheCoordinator {
|
2024-06-26 18:10:32 +00:00
|
|
|
static final Map<CryptoCurrencyNetwork, _FiroCacheWorker> _workers = {};
|
2024-12-14 21:29:17 +00:00
|
|
|
static final Map<CryptoCurrencyNetwork, Mutex> _tagLocks = {};
|
|
|
|
static final Map<CryptoCurrencyNetwork, Mutex> _setLocks = {};
|
2024-05-31 22:37:25 +00:00
|
|
|
|
|
|
|
static bool _init = false;
|
|
|
|
static Future<void> init() async {
|
|
|
|
if (_init) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_init = true;
|
|
|
|
await _FiroCache.init();
|
2024-06-26 18:10:32 +00:00
|
|
|
for (final network in _FiroCache.networks) {
|
2024-12-14 21:29:17 +00:00
|
|
|
_tagLocks[network] = Mutex();
|
|
|
|
_setLocks[network] = Mutex();
|
2024-06-26 18:10:32 +00:00
|
|
|
_workers[network] = await _FiroCacheWorker.spawn(network);
|
|
|
|
}
|
2024-05-31 22:37:25 +00:00
|
|
|
}
|
|
|
|
|
2024-06-26 18:10:32 +00:00
|
|
|
static Future<void> clearSharedCache(CryptoCurrencyNetwork network) async {
|
|
|
|
return await _FiroCache._deleteAllCache(network);
|
2024-05-31 22:37:25 +00:00
|
|
|
}
|
|
|
|
|
2024-06-26 18:10:32 +00:00
|
|
|
static Future<String> getSparkCacheSize(CryptoCurrencyNetwork network) async {
|
2024-06-05 19:38:20 +00:00
|
|
|
final dir = await StackFileSystem.applicationFiroCacheSQLiteDirectory();
|
|
|
|
final setCacheFile = File(
|
2024-06-26 18:10:32 +00:00
|
|
|
"${dir.path}/${_FiroCache.sparkSetCacheFileName(network)}",
|
2024-06-05 19:38:20 +00:00
|
|
|
);
|
|
|
|
final usedTagsCacheFile = File(
|
2024-06-26 18:10:32 +00:00
|
|
|
"${dir.path}/${_FiroCache.sparkUsedTagsCacheFileName(network)}",
|
2024-06-05 19:38:20 +00:00
|
|
|
);
|
2024-12-14 21:29:17 +00:00
|
|
|
|
|
|
|
final setSize =
|
|
|
|
(await setCacheFile.exists()) ? await setCacheFile.length() : 0;
|
|
|
|
final tagsSize = (await usedTagsCacheFile.exists())
|
|
|
|
? await usedTagsCacheFile.length()
|
|
|
|
: 0;
|
|
|
|
|
2024-12-16 02:15:33 +00:00
|
|
|
Logging.instance.log(
|
|
|
|
"Spark cache used tags size: $tagsSize",
|
|
|
|
level: LogLevel.Debug,
|
|
|
|
);
|
|
|
|
Logging.instance.log(
|
|
|
|
"Spark cache anon set size: $setSize",
|
|
|
|
level: LogLevel.Debug,
|
|
|
|
);
|
2024-12-14 21:29:17 +00:00
|
|
|
|
2024-12-17 01:35:39 +00:00
|
|
|
final int bytes = tagsSize + setSize;
|
2024-05-31 22:37:25 +00:00
|
|
|
|
|
|
|
if (bytes < 1024) {
|
|
|
|
return '$bytes B';
|
|
|
|
} else if (bytes < 1048576) {
|
|
|
|
final double kbSize = bytes / 1024;
|
|
|
|
return '${kbSize.toStringAsFixed(2)} KB';
|
|
|
|
} else if (bytes < 1073741824) {
|
|
|
|
final double mbSize = bytes / 1048576;
|
|
|
|
return '${mbSize.toStringAsFixed(2)} MB';
|
|
|
|
} else {
|
|
|
|
final double gbSize = bytes / 1073741824;
|
|
|
|
return '${gbSize.toStringAsFixed(2)} GB';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static Future<void> runFetchAndUpdateSparkUsedCoinTags(
|
|
|
|
ElectrumXClient client,
|
2024-06-26 18:10:32 +00:00
|
|
|
CryptoCurrencyNetwork network,
|
2024-05-31 22:37:25 +00:00
|
|
|
) async {
|
2024-12-14 21:29:17 +00:00
|
|
|
await _tagLocks[network]!.protect(() async {
|
|
|
|
final count = await FiroCacheCoordinator.getUsedCoinTagsCount(network);
|
|
|
|
final unhashedTags =
|
|
|
|
await client.getSparkUnhashedUsedCoinsTagsWithTxHashes(
|
|
|
|
startNumber: count,
|
2024-05-31 22:37:25 +00:00
|
|
|
);
|
2024-12-14 21:29:17 +00:00
|
|
|
if (unhashedTags.isNotEmpty) {
|
|
|
|
await _workers[network]!.runTask(
|
|
|
|
FCTask(
|
|
|
|
func: FCFuncName._updateSparkUsedTagsWith,
|
|
|
|
data: unhashedTags,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
2024-05-31 22:37:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static Future<void> runFetchAndUpdateSparkAnonSetCacheForGroupId(
|
|
|
|
int groupId,
|
|
|
|
ElectrumXClient client,
|
2024-06-26 18:10:32 +00:00
|
|
|
CryptoCurrencyNetwork network,
|
2024-12-14 21:29:17 +00:00
|
|
|
void Function(int countFetched, int totalCount)? progressUpdated,
|
2024-05-31 22:37:25 +00:00
|
|
|
) async {
|
2024-12-14 21:29:17 +00:00
|
|
|
await _setLocks[network]!.protect(() async {
|
2024-12-17 20:03:31 +00:00
|
|
|
const sectorSize =
|
|
|
|
1500; // chosen as a somewhat decent value. Could be changed in the future if wanted/needed
|
2024-12-17 01:35:39 +00:00
|
|
|
final prevMeta = await FiroCacheCoordinator.getLatestSetInfoForGroupId(
|
|
|
|
groupId,
|
|
|
|
network,
|
|
|
|
);
|
2024-05-31 22:37:25 +00:00
|
|
|
|
2024-12-17 01:35:39 +00:00
|
|
|
final prevSize = prevMeta?.size ?? 0;
|
2024-12-14 21:29:17 +00:00
|
|
|
|
2024-12-17 01:35:39 +00:00
|
|
|
final meta = await client.getSparkAnonymitySetMeta(
|
|
|
|
coinGroupId: groupId,
|
|
|
|
);
|
2024-12-14 21:29:17 +00:00
|
|
|
|
2024-12-17 01:35:39 +00:00
|
|
|
progressUpdated?.call(prevSize, meta.size);
|
2024-12-14 21:29:17 +00:00
|
|
|
|
2024-12-17 01:35:39 +00:00
|
|
|
if (prevMeta?.blockHash == meta.blockHash) {
|
|
|
|
Logging.instance.log(
|
|
|
|
"prevMeta?.blockHash == meta.blockHash",
|
|
|
|
level: LogLevel.Debug,
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
2024-12-14 21:29:17 +00:00
|
|
|
|
2024-12-17 01:35:39 +00:00
|
|
|
final numberOfCoinsToFetch = meta.size - prevSize;
|
2024-12-14 21:29:17 +00:00
|
|
|
|
2024-12-17 01:35:39 +00:00
|
|
|
final fullSectorCount = numberOfCoinsToFetch ~/ sectorSize;
|
|
|
|
final remainder = numberOfCoinsToFetch % sectorSize;
|
2024-12-14 21:29:17 +00:00
|
|
|
|
2024-12-17 01:35:39 +00:00
|
|
|
final List<dynamic> coins = [];
|
2024-12-14 21:29:17 +00:00
|
|
|
|
2024-12-17 01:35:39 +00:00
|
|
|
for (int i = 0; i < fullSectorCount; i++) {
|
|
|
|
final start = (i * sectorSize);
|
|
|
|
final data = await client.getSparkAnonymitySetBySector(
|
|
|
|
coinGroupId: groupId,
|
2024-12-17 20:03:31 +00:00
|
|
|
latestBlock: meta.blockHash,
|
2024-12-17 01:35:39 +00:00
|
|
|
startIndex: start,
|
|
|
|
endIndex: start + sectorSize,
|
|
|
|
);
|
|
|
|
progressUpdated?.call(start + sectorSize, numberOfCoinsToFetch);
|
2024-12-14 21:29:17 +00:00
|
|
|
|
2024-12-17 01:35:39 +00:00
|
|
|
coins.addAll(data);
|
|
|
|
}
|
2024-12-14 21:29:17 +00:00
|
|
|
|
2024-12-17 01:35:39 +00:00
|
|
|
if (remainder > 0) {
|
|
|
|
final data = await client.getSparkAnonymitySetBySector(
|
|
|
|
coinGroupId: groupId,
|
2024-12-17 20:03:31 +00:00
|
|
|
latestBlock: meta.blockHash,
|
2024-12-17 01:35:39 +00:00
|
|
|
startIndex: numberOfCoinsToFetch - remainder,
|
|
|
|
endIndex: numberOfCoinsToFetch,
|
|
|
|
);
|
|
|
|
progressUpdated?.call(numberOfCoinsToFetch, numberOfCoinsToFetch);
|
2024-12-14 21:29:17 +00:00
|
|
|
|
2024-12-17 01:35:39 +00:00
|
|
|
coins.addAll(data);
|
2024-12-14 21:29:17 +00:00
|
|
|
}
|
|
|
|
|
2024-12-17 01:35:39 +00:00
|
|
|
final result = coins
|
|
|
|
.map((e) => RawSparkCoin.fromRPCResponse(e as List, groupId))
|
|
|
|
.toList();
|
|
|
|
|
2024-12-14 21:29:17 +00:00
|
|
|
await _workers[network]!.runTask(
|
|
|
|
FCTask(
|
|
|
|
func: FCFuncName._updateSparkAnonSetCoinsWith,
|
2024-12-17 01:35:39 +00:00
|
|
|
data: (meta, result),
|
2024-12-14 21:29:17 +00:00
|
|
|
),
|
|
|
|
);
|
|
|
|
});
|
2024-05-31 22:37:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ===========================================================================
|
|
|
|
|
2024-06-26 18:10:32 +00:00
|
|
|
static Future<Set<String>> getUsedCoinTags(
|
|
|
|
int startNumber,
|
|
|
|
CryptoCurrencyNetwork network,
|
|
|
|
) async {
|
2024-05-31 22:37:25 +00:00
|
|
|
final result = await _Reader._getSparkUsedCoinTags(
|
|
|
|
startNumber,
|
2024-06-26 18:10:32 +00:00
|
|
|
db: _FiroCache.usedTagsCacheDB(network),
|
2024-05-31 22:37:25 +00:00
|
|
|
);
|
|
|
|
return result.map((e) => e["tag"] as String).toSet();
|
|
|
|
}
|
|
|
|
|
2024-06-26 18:10:32 +00:00
|
|
|
static Future<int> getUsedCoinTagsCount(
|
|
|
|
CryptoCurrencyNetwork network,
|
|
|
|
) async {
|
2024-06-06 20:36:21 +00:00
|
|
|
final result = await _Reader._getUsedCoinTagsCount(
|
2024-06-26 18:10:32 +00:00
|
|
|
db: _FiroCache.usedTagsCacheDB(network),
|
2024-05-31 22:37:25 +00:00
|
|
|
);
|
|
|
|
if (result.isEmpty) {
|
|
|
|
return 0;
|
|
|
|
}
|
2024-06-06 20:36:21 +00:00
|
|
|
return result.first["count"] as int? ?? 0;
|
2024-05-31 22:37:25 +00:00
|
|
|
}
|
|
|
|
|
2024-06-10 19:28:49 +00:00
|
|
|
static Future<List<LTagPair>> getUsedCoinTxidsFor({
|
|
|
|
required List<String> tags,
|
2024-06-26 18:10:32 +00:00
|
|
|
required CryptoCurrencyNetwork network,
|
2024-06-10 19:28:49 +00:00
|
|
|
}) async {
|
|
|
|
if (tags.isEmpty) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
final result = await _Reader._getUsedCoinTxidsFor(
|
|
|
|
tags,
|
2024-06-26 18:10:32 +00:00
|
|
|
db: _FiroCache.usedTagsCacheDB(network),
|
2024-06-10 19:28:49 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
if (result.isEmpty) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
return result.rows
|
|
|
|
.map(
|
|
|
|
(e) => (
|
|
|
|
tag: e[0] as String,
|
|
|
|
txid: e[1] as String,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
.toList();
|
|
|
|
}
|
|
|
|
|
|
|
|
static Future<Set<String>> getUsedCoinTagsFor({
|
|
|
|
required String txid,
|
2024-06-26 18:10:32 +00:00
|
|
|
required CryptoCurrencyNetwork network,
|
2024-06-10 19:28:49 +00:00
|
|
|
}) async {
|
|
|
|
final result = await _Reader._getUsedCoinTagsFor(
|
|
|
|
txid,
|
2024-06-26 18:10:32 +00:00
|
|
|
db: _FiroCache.usedTagsCacheDB(network),
|
2024-06-10 19:28:49 +00:00
|
|
|
);
|
|
|
|
return result.map((e) => e["tag"] as String).toSet();
|
|
|
|
}
|
|
|
|
|
2024-05-31 22:37:25 +00:00
|
|
|
static Future<bool> checkTagIsUsed(
|
|
|
|
String tag,
|
2024-06-26 18:10:32 +00:00
|
|
|
CryptoCurrencyNetwork network,
|
2024-05-31 22:37:25 +00:00
|
|
|
) async {
|
|
|
|
return await _Reader._checkTagIsUsed(
|
|
|
|
tag,
|
2024-06-26 18:10:32 +00:00
|
|
|
db: _FiroCache.usedTagsCacheDB(network),
|
2024-05-31 22:37:25 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-12-17 01:35:39 +00:00
|
|
|
static Future<List<RawSparkCoin>> getSetCoinsForGroupId(
|
2024-05-31 22:37:25 +00:00
|
|
|
int groupId, {
|
2024-12-17 01:35:39 +00:00
|
|
|
String? afterBlockHash,
|
2024-06-26 18:10:32 +00:00
|
|
|
required CryptoCurrencyNetwork network,
|
2024-05-31 22:37:25 +00:00
|
|
|
}) async {
|
2024-12-17 01:35:39 +00:00
|
|
|
final resultSet = afterBlockHash == null
|
|
|
|
? await _Reader._getSetCoinsForGroupId(
|
|
|
|
groupId,
|
|
|
|
db: _FiroCache.setCacheDB(network),
|
|
|
|
)
|
|
|
|
: await _Reader._getSetCoinsForGroupIdAndBlockHash(
|
|
|
|
groupId,
|
|
|
|
afterBlockHash,
|
|
|
|
db: _FiroCache.setCacheDB(network),
|
|
|
|
);
|
|
|
|
|
2024-06-06 17:21:50 +00:00
|
|
|
return resultSet
|
|
|
|
.map(
|
2024-12-17 01:35:39 +00:00
|
|
|
(row) => RawSparkCoin(
|
2024-06-06 17:21:50 +00:00
|
|
|
serialized: row["serialized"] as String,
|
|
|
|
txHash: row["txHash"] as String,
|
|
|
|
context: row["context"] as String,
|
2024-12-17 01:35:39 +00:00
|
|
|
groupId: groupId,
|
2024-06-06 17:21:50 +00:00
|
|
|
),
|
|
|
|
)
|
|
|
|
.toList()
|
|
|
|
.reversed
|
|
|
|
.toList();
|
2024-05-31 22:37:25 +00:00
|
|
|
}
|
|
|
|
|
2024-12-17 01:35:39 +00:00
|
|
|
static Future<SparkAnonymitySetMeta?> getLatestSetInfoForGroupId(
|
2024-05-31 22:37:25 +00:00
|
|
|
int groupId,
|
2024-06-26 18:10:32 +00:00
|
|
|
CryptoCurrencyNetwork network,
|
2024-05-31 22:37:25 +00:00
|
|
|
) async {
|
|
|
|
final result = await _Reader._getLatestSetInfoForGroupId(
|
|
|
|
groupId,
|
2024-06-26 18:10:32 +00:00
|
|
|
db: _FiroCache.setCacheDB(network),
|
2024-05-31 22:37:25 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
if (result.isEmpty) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2024-12-17 01:35:39 +00:00
|
|
|
return SparkAnonymitySetMeta(
|
|
|
|
coinGroupId: groupId,
|
2024-05-31 22:37:25 +00:00
|
|
|
blockHash: result.first["blockHash"] as String,
|
|
|
|
setHash: result.first["setHash"] as String,
|
2024-12-17 01:35:39 +00:00
|
|
|
size: result.first["size"] as int,
|
2024-05-31 22:37:25 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Future<bool> checkSetInfoForGroupIdExists(
|
|
|
|
int groupId,
|
2024-06-26 18:10:32 +00:00
|
|
|
CryptoCurrencyNetwork network,
|
2024-05-31 22:37:25 +00:00
|
|
|
) async {
|
|
|
|
return await _Reader._checkSetInfoForGroupIdExists(
|
|
|
|
groupId,
|
2024-06-26 18:10:32 +00:00
|
|
|
db: _FiroCache.setCacheDB(network),
|
2024-05-31 22:37:25 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|