Skip to content

Commit

Permalink
Merge pull request #898 from cypherstack/firo_exchange_addresses
Browse files Browse the repository at this point in the history
Firo exchange addresses and bug fixes
  • Loading branch information
julian-CStack authored Jun 27, 2024
2 parents 68180d0 + d30e724 commit a4d30e1
Show file tree
Hide file tree
Showing 48 changed files with 809 additions and 455 deletions.
81 changes: 48 additions & 33 deletions lib/db/sqlite/firo_cache.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import '../../electrumx_rpc/electrumx_client.dart';
import '../../utilities/extensions/extensions.dart';
import '../../utilities/logger.dart';
import '../../utilities/stack_file_system.dart';
import '../../wallets/crypto_currency/crypto_currency.dart';

part 'firo_cache_coordinator.dart';
part 'firo_cache_reader.dart';
Expand All @@ -31,29 +32,39 @@ void _debugLog(Object? object) {
abstract class _FiroCache {
static const int _setCacheVersion = 1;
static const int _tagsCacheVersion = 2;
static const String sparkSetCacheFileName =
"spark_set_v$_setCacheVersion.sqlite3";
static const String sparkUsedTagsCacheFileName =
"spark_tags_v$_tagsCacheVersion.sqlite3";

static Database? _setCacheDB;
static Database? _usedTagsCacheDB;
static Database get setCacheDB {
if (_setCacheDB == null) {

static final networks = [
CryptoCurrencyNetwork.main,
CryptoCurrencyNetwork.test,
];

static String sparkSetCacheFileName(CryptoCurrencyNetwork network) =>
network == CryptoCurrencyNetwork.main
? "spark_set_v$_setCacheVersion.sqlite3"
: "spark_set_v${_setCacheVersion}_${network.name}.sqlite3";
static String sparkUsedTagsCacheFileName(CryptoCurrencyNetwork network) =>
network == CryptoCurrencyNetwork.main
? "spark_tags_v$_tagsCacheVersion.sqlite3"
: "spark_tags_v${_tagsCacheVersion}_${network.name}.sqlite3";

static final Map<CryptoCurrencyNetwork, Database> _setCacheDB = {};
static final Map<CryptoCurrencyNetwork, Database> _usedTagsCacheDB = {};
static Database setCacheDB(CryptoCurrencyNetwork network) {
if (_setCacheDB[network] == null) {
throw Exception(
"FiroCache.init() must be called before accessing FiroCache.db!",
);
}
return _setCacheDB!;
return _setCacheDB[network]!;
}

static Database get usedTagsCacheDB {
if (_usedTagsCacheDB == null) {
static Database usedTagsCacheDB(CryptoCurrencyNetwork network) {
if (_usedTagsCacheDB[network] == null) {
throw Exception(
"FiroCache.init() must be called before accessing FiroCache.db!",
);
}
return _usedTagsCacheDB!;
return _usedTagsCacheDB[network]!;
}

static Future<void>? _initFuture;
Expand All @@ -63,38 +74,42 @@ abstract class _FiroCache {
final sqliteDir =
await StackFileSystem.applicationFiroCacheSQLiteDirectory();

final sparkSetCacheFile = File("${sqliteDir.path}/$sparkSetCacheFileName");
final sparkUsedTagsCacheFile =
File("${sqliteDir.path}/$sparkUsedTagsCacheFileName");
for (final network in networks) {
final sparkSetCacheFile =
File("${sqliteDir.path}/${sparkSetCacheFileName(network)}");

if (!(await sparkSetCacheFile.exists())) {
await _createSparkSetCacheDb(sparkSetCacheFile.path);
}
if (!(await sparkUsedTagsCacheFile.exists())) {
await _createSparkUsedTagsCacheDb(sparkUsedTagsCacheFile.path);
}
final sparkUsedTagsCacheFile =
File("${sqliteDir.path}/${sparkUsedTagsCacheFileName(network)}");

_setCacheDB = sqlite3.open(
sparkSetCacheFile.path,
mode: OpenMode.readWrite,
);
_usedTagsCacheDB = sqlite3.open(
sparkUsedTagsCacheFile.path,
mode: OpenMode.readWrite,
);
if (!(await sparkSetCacheFile.exists())) {
await _createSparkSetCacheDb(sparkSetCacheFile.path);
}
if (!(await sparkUsedTagsCacheFile.exists())) {
await _createSparkUsedTagsCacheDb(sparkUsedTagsCacheFile.path);
}

_setCacheDB[network] = sqlite3.open(
sparkSetCacheFile.path,
mode: OpenMode.readWrite,
);
_usedTagsCacheDB[network] = sqlite3.open(
sparkUsedTagsCacheFile.path,
mode: OpenMode.readWrite,
);
}
}

static Future<void> _deleteAllCache() async {
static Future<void> _deleteAllCache(CryptoCurrencyNetwork network) async {
final start = DateTime.now();
setCacheDB.execute(
setCacheDB(network).execute(
"""
DELETE FROM SparkSet;
DELETE FROM SparkCoin;
DELETE FROM SparkSetCoins;
VACUUM;
""",
);
usedTagsCacheDB.execute(
usedTagsCacheDB(network).execute(
"""
DELETE FROM SparkUsedCoinTags;
VACUUM;
Expand Down
56 changes: 36 additions & 20 deletions lib/db/sqlite/firo_cache_coordinator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ typedef LTagPair = ({String tag, String txid});
/// Wrapper class for [_FiroCache] as [_FiroCache] should eventually be handled in a
/// background isolate and [FiroCacheCoordinator] should manage that isolate
abstract class FiroCacheCoordinator {
static _FiroCacheWorker? _worker;
static final Map<CryptoCurrencyNetwork, _FiroCacheWorker> _workers = {};

static bool _init = false;
static Future<void> init() async {
Expand All @@ -14,20 +14,22 @@ abstract class FiroCacheCoordinator {
}
_init = true;
await _FiroCache.init();
_worker = await _FiroCacheWorker.spawn();
for (final network in _FiroCache.networks) {
_workers[network] = await _FiroCacheWorker.spawn(network);
}
}

static Future<void> clearSharedCache() async {
return await _FiroCache._deleteAllCache();
static Future<void> clearSharedCache(CryptoCurrencyNetwork network) async {
return await _FiroCache._deleteAllCache(network);
}

static Future<String> getSparkCacheSize() async {
static Future<String> getSparkCacheSize(CryptoCurrencyNetwork network) async {
final dir = await StackFileSystem.applicationFiroCacheSQLiteDirectory();
final setCacheFile = File(
"${dir.path}/${_FiroCache.sparkSetCacheFileName}",
"${dir.path}/${_FiroCache.sparkSetCacheFileName(network)}",
);
final usedTagsCacheFile = File(
"${dir.path}/${_FiroCache.sparkUsedTagsCacheFileName}",
"${dir.path}/${_FiroCache.sparkUsedTagsCacheFileName(network)}",
);
final int bytes =
((await setCacheFile.exists()) ? await setCacheFile.length() : 0) +
Expand All @@ -51,13 +53,14 @@ abstract class FiroCacheCoordinator {

static Future<void> runFetchAndUpdateSparkUsedCoinTags(
ElectrumXClient client,
CryptoCurrencyNetwork network,
) async {
final count = await FiroCacheCoordinator.getUsedCoinTagsCount();
final count = await FiroCacheCoordinator.getUsedCoinTagsCount(network);
final unhashedTags = await client.getSparkUnhashedUsedCoinsTagsWithTxHashes(
startNumber: count,
);
if (unhashedTags.isNotEmpty) {
await _worker!.runTask(
await _workers[network]!.runTask(
FCTask(
func: FCFuncName._updateSparkUsedTagsWith,
data: unhashedTags,
Expand All @@ -69,10 +72,12 @@ abstract class FiroCacheCoordinator {
static Future<void> runFetchAndUpdateSparkAnonSetCacheForGroupId(
int groupId,
ElectrumXClient client,
CryptoCurrencyNetwork network,
) async {
final blockhashResult =
await FiroCacheCoordinator.getLatestSetInfoForGroupId(
groupId,
network,
);
final blockHash = blockhashResult?.blockHash ?? "";

Expand All @@ -81,7 +86,7 @@ abstract class FiroCacheCoordinator {
startBlockHash: blockHash.toHexReversedFromBase64,
);

await _worker!.runTask(
await _workers[network]!.runTask(
FCTask(
func: FCFuncName._updateSparkAnonSetCoinsWith,
data: (groupId, json),
Expand All @@ -91,17 +96,22 @@ abstract class FiroCacheCoordinator {

// ===========================================================================

static Future<Set<String>> getUsedCoinTags(int startNumber) async {
static Future<Set<String>> getUsedCoinTags(
int startNumber,
CryptoCurrencyNetwork network,
) async {
final result = await _Reader._getSparkUsedCoinTags(
startNumber,
db: _FiroCache.usedTagsCacheDB,
db: _FiroCache.usedTagsCacheDB(network),
);
return result.map((e) => e["tag"] as String).toSet();
}

static Future<int> getUsedCoinTagsCount() async {
static Future<int> getUsedCoinTagsCount(
CryptoCurrencyNetwork network,
) async {
final result = await _Reader._getUsedCoinTagsCount(
db: _FiroCache.usedTagsCacheDB,
db: _FiroCache.usedTagsCacheDB(network),
);
if (result.isEmpty) {
return 0;
Expand All @@ -111,13 +121,14 @@ abstract class FiroCacheCoordinator {

static Future<List<LTagPair>> getUsedCoinTxidsFor({
required List<String> tags,
required CryptoCurrencyNetwork network,
}) async {
if (tags.isEmpty) {
return [];
}
final result = await _Reader._getUsedCoinTxidsFor(
tags,
db: _FiroCache.usedTagsCacheDB,
db: _FiroCache.usedTagsCacheDB(network),
);

if (result.isEmpty) {
Expand All @@ -135,20 +146,22 @@ abstract class FiroCacheCoordinator {

static Future<Set<String>> getUsedCoinTagsFor({
required String txid,
required CryptoCurrencyNetwork network,
}) async {
final result = await _Reader._getUsedCoinTagsFor(
txid,
db: _FiroCache.usedTagsCacheDB,
db: _FiroCache.usedTagsCacheDB(network),
);
return result.map((e) => e["tag"] as String).toSet();
}

static Future<bool> checkTagIsUsed(
String tag,
CryptoCurrencyNetwork network,
) async {
return await _Reader._checkTagIsUsed(
tag,
db: _FiroCache.usedTagsCacheDB,
db: _FiroCache.usedTagsCacheDB(network),
);
}

Expand All @@ -161,10 +174,11 @@ abstract class FiroCacheCoordinator {
})>> getSetCoinsForGroupId(
int groupId, {
int? newerThanTimeStamp,
required CryptoCurrencyNetwork network,
}) async {
final resultSet = await _Reader._getSetCoinsForGroupId(
groupId,
db: _FiroCache.setCacheDB,
db: _FiroCache.setCacheDB(network),
newerThanTimeStamp: newerThanTimeStamp,
);
return resultSet
Expand All @@ -187,10 +201,11 @@ abstract class FiroCacheCoordinator {
int timestampUTC,
})?> getLatestSetInfoForGroupId(
int groupId,
CryptoCurrencyNetwork network,
) async {
final result = await _Reader._getLatestSetInfoForGroupId(
groupId,
db: _FiroCache.setCacheDB,
db: _FiroCache.setCacheDB(network),
);

if (result.isEmpty) {
Expand All @@ -206,10 +221,11 @@ abstract class FiroCacheCoordinator {

static Future<bool> checkSetInfoForGroupIdExists(
int groupId,
CryptoCurrencyNetwork network,
) async {
return await _Reader._checkSetInfoForGroupIdExists(
groupId,
db: _FiroCache.setCacheDB,
db: _FiroCache.setCacheDB(network),
);
}
}
7 changes: 4 additions & 3 deletions lib/db/sqlite/firo_cache_worker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ class _FiroCacheWorker {
return await completer.future;
}

static Future<_FiroCacheWorker> spawn() async {
static Future<_FiroCacheWorker> spawn(CryptoCurrencyNetwork network) async {
final dir = await StackFileSystem.applicationFiroCacheSQLiteDirectory();
final setCacheFilePath = "${dir.path}/${_FiroCache.sparkSetCacheFileName}";
final setCacheFilePath =
"${dir.path}/${_FiroCache.sparkSetCacheFileName(network)}";
final usedTagsCacheFilePath =
"${dir.path}/${_FiroCache.sparkUsedTagsCacheFileName}";
"${dir.path}/${_FiroCache.sparkUsedTagsCacheFileName(network)}";

final initPort = RawReceivePort();
final connection = Completer<(ReceivePort, SendPort)>.sync();
Expand Down
40 changes: 36 additions & 4 deletions lib/electrumx_rpc/electrumx_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1043,7 +1043,7 @@ class ElectrumXClient {
final start = DateTime.now();
final response = await request(
requestID: requestID,
command: "spark.getmempooltxids",
command: "spark.getmempoolsparktxids",
);

final txids = List<String>.from(response as List)
Expand Down Expand Up @@ -1072,7 +1072,7 @@ class ElectrumXClient {
final start = DateTime.now();
final response = await request(
requestID: requestID,
command: "spark.getmempooltxs",
command: "spark.getmempoolsparktxs",
args: [
{
"txids": txids,
Expand All @@ -1087,10 +1087,10 @@ class ElectrumXClient {
(
txid: entry.key,
serialContext:
List<String>.from(entry.value["Serial_context"] as List),
List<String>.from(entry.value["serial_context"] as List),
// the space after lTags is required lol
lTags: List<String>.from(entry.value["lTags "] as List),
coins: List<String>.from(entry.value["Coins"] as List),
coins: List<String>.from(entry.value["coins"] as List),
),
);
}
Expand Down Expand Up @@ -1142,6 +1142,38 @@ class ElectrumXClient {
}
// ===========================================================================

Future<bool> isMasterNodeCollateral({
String? requestID,
required String txid,
required int index,
}) async {
try {
final start = DateTime.now();
final response = await request(
requestID: requestID,
command: "blockchain.checkifmncollateral",
args: [
txid,
index.toString(),
],
);

Logging.instance.log(
"Finished ElectrumXClient.isMasterNodeCollateral, "
"response: $response, "
"Duration=${DateTime.now().difference(start)}",
level: LogLevel.Info,
);

return response as bool;
} catch (e) {
Logging.instance.log(e, level: LogLevel.Error);
rethrow;
}
}

// ===========================================================================

/// Get the current fee rate.
///
/// Returns a map with the kay "rate" that corresponds to the free rate in satoshis
Expand Down
Loading

0 comments on commit a4d30e1

Please sign in to comment.