mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-11-16 17:27:39 +00:00
WIP use and reuse electrum adapter channel
This commit is contained in:
parent
2339b33798
commit
b8987c73c0
11 changed files with 293 additions and 232 deletions
|
@ -85,6 +85,7 @@ class DbVersionMigrator with WalletDB {
|
|||
useSSL: node.useSSL),
|
||||
prefs: prefs,
|
||||
failovers: failovers,
|
||||
coin: Coin.firo,
|
||||
);
|
||||
|
||||
try {
|
||||
|
|
|
@ -12,29 +12,32 @@ import 'dart:convert';
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:electrum_adapter/electrum_adapter.dart' as electrum_adapter;
|
||||
import 'package:electrum_adapter/electrum_adapter.dart';
|
||||
import 'package:electrum_adapter/methods/specific/firo.dart';
|
||||
import 'package:stackwallet/db/hive/db.dart';
|
||||
import 'package:stackwallet/electrumx_rpc/electrumx_client.dart';
|
||||
import 'package:stackwallet/services/tor_service.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/prefs.dart';
|
||||
import 'package:string_validator/string_validator.dart';
|
||||
|
||||
class CachedElectrumXClient {
|
||||
final ElectrumXClient electrumXClient;
|
||||
final ElectrumClient electrumAdapterClient;
|
||||
|
||||
static const minCacheConfirms = 30;
|
||||
|
||||
const CachedElectrumXClient({
|
||||
required this.electrumXClient,
|
||||
required this.electrumAdapterClient,
|
||||
});
|
||||
|
||||
factory CachedElectrumXClient.from({
|
||||
required ElectrumXClient electrumXClient,
|
||||
required ElectrumClient electrumAdapterClient,
|
||||
}) =>
|
||||
CachedElectrumXClient(
|
||||
electrumXClient: electrumXClient,
|
||||
);
|
||||
electrumAdapterClient: electrumAdapterClient);
|
||||
|
||||
Future<Map<String, dynamic>> getAnonymitySet({
|
||||
required String groupId,
|
||||
|
@ -59,9 +62,10 @@ class CachedElectrumXClient {
|
|||
set = Map<String, dynamic>.from(cachedSet);
|
||||
}
|
||||
|
||||
final newSet = await electrumXClient.getLelantusAnonymitySet(
|
||||
final newSet = await (electrumAdapterClient as FiroElectrumClient)
|
||||
.getLelantusAnonymitySet(
|
||||
groupId: groupId,
|
||||
blockhash: set["blockHash"] as String,
|
||||
blockHash: set["blockHash"] as String,
|
||||
);
|
||||
|
||||
// update set with new data
|
||||
|
@ -85,7 +89,7 @@ class CachedElectrumXClient {
|
|||
translatedCoin.add(!isHexadecimal(newCoin[2] as String)
|
||||
? base64ToHex(newCoin[2] as String)
|
||||
: newCoin[2]);
|
||||
} catch (e, s) {
|
||||
} catch (e) {
|
||||
translatedCoin.add(newCoin[2]);
|
||||
}
|
||||
translatedCoin.add(!isHexadecimal(newCoin[3] as String)
|
||||
|
@ -133,7 +137,8 @@ class CachedElectrumXClient {
|
|||
set = Map<String, dynamic>.from(cachedSet);
|
||||
}
|
||||
|
||||
final newSet = await electrumXClient.getSparkAnonymitySet(
|
||||
final newSet = await (electrumAdapterClient as FiroElectrumClient)
|
||||
.getSparkAnonymitySet(
|
||||
coinGroupId: groupId,
|
||||
startBlockHash: set["blockHash"] as String,
|
||||
);
|
||||
|
@ -191,15 +196,8 @@ class CachedElectrumXClient {
|
|||
|
||||
final cachedTx = box.get(txHash) as Map?;
|
||||
if (cachedTx == null) {
|
||||
var channel = await electrum_adapter.connect(electrumXClient.host,
|
||||
port: electrumXClient.port,
|
||||
useSSL: electrumXClient.useSSL,
|
||||
proxyInfo: Prefs.instance.useTor
|
||||
? TorService.sharedInstance.getProxyInfo()
|
||||
: null);
|
||||
var client = electrum_adapter.ElectrumClient(
|
||||
channel, electrumXClient.host, electrumXClient.port);
|
||||
final Map<String, dynamic> result = await client.getTransaction(txHash);
|
||||
final Map<String, dynamic> result =
|
||||
await electrumAdapterClient.getTransaction(txHash);
|
||||
|
||||
result.remove("hex");
|
||||
result.remove("lelantusData");
|
||||
|
@ -241,7 +239,8 @@ class CachedElectrumXClient {
|
|||
cachedSerials.length - 100, // 100 being some arbitrary buffer
|
||||
);
|
||||
|
||||
final serials = await electrumXClient.getLelantusUsedCoinSerials(
|
||||
final serials = await (electrumAdapterClient as FiroElectrumClient)
|
||||
.getLelantusUsedCoinSerials(
|
||||
startNumber: startNumber,
|
||||
);
|
||||
|
||||
|
@ -289,7 +288,8 @@ class CachedElectrumXClient {
|
|||
cachedTags.length - 100, // 100 being some arbitrary buffer
|
||||
);
|
||||
|
||||
final tags = await electrumXClient.getSparkUsedCoinsTags(
|
||||
final tags =
|
||||
await (electrumAdapterClient as FiroElectrumClient).getUsedCoinsTags(
|
||||
startNumber: startNumber,
|
||||
);
|
||||
|
||||
|
@ -297,12 +297,18 @@ class CachedElectrumXClient {
|
|||
// .map((e) => !isHexadecimal(e) ? base64ToHex(e) : e)
|
||||
// .toSet();
|
||||
|
||||
// Convert the Map<String, dynamic> tags to a Set<Object?>.
|
||||
final newTags = (tags["tags"] as List).toSet();
|
||||
|
||||
// ensure we are getting some overlap so we know we are not missing any
|
||||
if (cachedTags.isNotEmpty && tags.isNotEmpty) {
|
||||
assert(cachedTags.intersection(tags).isNotEmpty);
|
||||
assert(cachedTags.intersection(newTags).isNotEmpty);
|
||||
}
|
||||
|
||||
cachedTags.addAll(tags);
|
||||
// Make newTags an Iterable<String>.
|
||||
final Iterable<String> iterableTags = newTags.map((e) => e.toString());
|
||||
|
||||
cachedTags.addAll(iterableTags);
|
||||
|
||||
await box.put(
|
||||
"tags",
|
||||
|
|
|
@ -9,12 +9,12 @@
|
|||
*/
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
import 'package:decimal/decimal.dart';
|
||||
import 'package:electrum_adapter/electrum_adapter.dart' as electrum_adapter;
|
||||
import 'package:electrum_adapter/electrum_adapter.dart';
|
||||
import 'package:electrum_adapter/methods/specific/firo.dart';
|
||||
import 'package:event_bus/event_bus.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
@ -26,9 +26,10 @@ import 'package:stackwallet/services/event_bus/events/global/tor_connection_stat
|
|||
import 'package:stackwallet/services/event_bus/events/global/tor_status_changed_event.dart';
|
||||
import 'package:stackwallet/services/event_bus/global_event_bus.dart';
|
||||
import 'package:stackwallet/services/tor_service.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/prefs.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
import 'package:stream_channel/stream_channel.dart';
|
||||
|
||||
class WifiOnlyException implements Exception {}
|
||||
|
||||
|
@ -75,6 +76,12 @@ class ElectrumXClient {
|
|||
JsonRPC? get rpcClient => _rpcClient;
|
||||
JsonRPC? _rpcClient;
|
||||
|
||||
StreamChannel<dynamic>? get electrumAdapterChannel => _electrumAdapterChannel;
|
||||
StreamChannel<dynamic>? _electrumAdapterChannel;
|
||||
|
||||
ElectrumClient? get electrumAdapterClient => _electrumAdapterClient;
|
||||
ElectrumClient? _electrumAdapterClient;
|
||||
|
||||
late Prefs _prefs;
|
||||
late TorService _torService;
|
||||
|
||||
|
@ -83,6 +90,9 @@ class ElectrumXClient {
|
|||
|
||||
final Duration connectionTimeoutForSpecialCaseJsonRPCClients;
|
||||
|
||||
Coin get coin => _coin;
|
||||
late Coin _coin;
|
||||
|
||||
// add finalizer to cancel stream subscription when all references to an
|
||||
// instance of ElectrumX becomes inaccessible
|
||||
static final Finalizer<ElectrumXClient> _finalizer = Finalizer(
|
||||
|
@ -103,6 +113,7 @@ class ElectrumXClient {
|
|||
required bool useSSL,
|
||||
required Prefs prefs,
|
||||
required List<ElectrumXNode> failovers,
|
||||
required Coin coin,
|
||||
JsonRPC? client,
|
||||
this.connectionTimeoutForSpecialCaseJsonRPCClients =
|
||||
const Duration(seconds: 60),
|
||||
|
@ -115,6 +126,7 @@ class ElectrumXClient {
|
|||
_port = port;
|
||||
_useSSL = useSSL;
|
||||
_rpcClient = client;
|
||||
_coin = coin;
|
||||
|
||||
final bus = globalEventBusForTesting ?? GlobalEventBus.instance;
|
||||
_torStatusListener = bus.on<TorConnectionStatusChangedEvent>().listen(
|
||||
|
@ -154,6 +166,8 @@ class ElectrumXClient {
|
|||
// setting to null should force the creation of a new json rpc client
|
||||
// on the next request sent through this electrumx instance
|
||||
_rpcClient = null;
|
||||
_electrumAdapterChannel = null;
|
||||
_electrumAdapterClient = null;
|
||||
|
||||
await temp?.disconnect(
|
||||
reason: "Tor status changed to \"${event.status}\"",
|
||||
|
@ -166,6 +180,7 @@ class ElectrumXClient {
|
|||
required ElectrumXNode node,
|
||||
required Prefs prefs,
|
||||
required List<ElectrumXNode> failovers,
|
||||
required Coin coin,
|
||||
TorService? torService,
|
||||
EventBus? globalEventBusForTesting,
|
||||
}) {
|
||||
|
@ -177,6 +192,7 @@ class ElectrumXClient {
|
|||
torService: torService,
|
||||
failovers: failovers,
|
||||
globalEventBusForTesting: globalEventBusForTesting,
|
||||
coin: coin,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -238,24 +254,99 @@ class ElectrumXClient {
|
|||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (currentFailoverIndex == -1) {
|
||||
_rpcClient ??= JsonRPC(
|
||||
host: host,
|
||||
port: port,
|
||||
useSSL: useSSL,
|
||||
connectionTimeout: connectionTimeoutForSpecialCaseJsonRPCClients,
|
||||
proxyInfo: null,
|
||||
Future<void> _checkElectrumAdapter() async {
|
||||
({InternetAddress host, int port})? proxyInfo;
|
||||
|
||||
// If we're supposed to use Tor...
|
||||
if (_prefs.useTor) {
|
||||
// But Tor isn't running...
|
||||
if (_torService.status != TorConnectionStatus.connected) {
|
||||
// And the killswitch isn't set...
|
||||
if (!_prefs.torKillSwitch) {
|
||||
// Then we'll just proceed and connect to ElectrumX through clearnet at the bottom of this function.
|
||||
Logging.instance.log(
|
||||
"Tor preference set but Tor is not enabled, killswitch not set, connecting to Electrum adapter through clearnet",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
} else {
|
||||
_rpcClient ??= JsonRPC(
|
||||
host: failovers![currentFailoverIndex].address,
|
||||
port: failovers![currentFailoverIndex].port,
|
||||
useSSL: failovers![currentFailoverIndex].useSSL,
|
||||
// ... But if the killswitch is set, then we throw an exception.
|
||||
throw Exception(
|
||||
"Tor preference and killswitch set but Tor is not enabled, not connecting to Electrum adapter");
|
||||
// TODO [prio=low]: Try to start Tor.
|
||||
}
|
||||
} else {
|
||||
// Get the proxy info from the TorService.
|
||||
proxyInfo = _torService.getProxyInfo();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO [prio=med]: Add proxyInfo to StreamChannel (or add to wrapper).
|
||||
// if (_electrumAdapter!.proxyInfo != proxyInfo) {
|
||||
// _electrumAdapter!.proxyInfo = proxyInfo;
|
||||
// _electrumAdapter!.disconnect(
|
||||
// reason: "Tor proxyInfo does not match current info",
|
||||
// );
|
||||
// }
|
||||
|
||||
if (currentFailoverIndex == -1) {
|
||||
_electrumAdapterChannel ??= await electrum_adapter.connect(
|
||||
host,
|
||||
port: port,
|
||||
connectionTimeout: connectionTimeoutForSpecialCaseJsonRPCClients,
|
||||
proxyInfo: null,
|
||||
aliveTimerDuration: connectionTimeoutForSpecialCaseJsonRPCClients,
|
||||
acceptUnverified: true,
|
||||
useSSL: useSSL,
|
||||
proxyInfo: proxyInfo,
|
||||
);
|
||||
if (_coin == Coin.firo || _coin == Coin.firoTestNet) {
|
||||
_electrumAdapterClient ??= FiroElectrumClient(
|
||||
_electrumAdapterChannel!,
|
||||
host,
|
||||
port,
|
||||
useSSL,
|
||||
proxyInfo,
|
||||
);
|
||||
} else {
|
||||
_electrumAdapterClient ??= ElectrumClient(
|
||||
_electrumAdapterChannel!,
|
||||
host,
|
||||
port,
|
||||
useSSL,
|
||||
proxyInfo,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
_electrumAdapterChannel ??= await electrum_adapter.connect(
|
||||
failovers![currentFailoverIndex].address,
|
||||
port: failovers![currentFailoverIndex].port,
|
||||
connectionTimeout: connectionTimeoutForSpecialCaseJsonRPCClients,
|
||||
aliveTimerDuration: connectionTimeoutForSpecialCaseJsonRPCClients,
|
||||
acceptUnverified: true,
|
||||
useSSL: failovers![currentFailoverIndex].useSSL,
|
||||
proxyInfo: proxyInfo,
|
||||
);
|
||||
if (_coin == Coin.firo || _coin == Coin.firoTestNet) {
|
||||
_electrumAdapterClient ??= FiroElectrumClient(
|
||||
_electrumAdapterChannel!,
|
||||
failovers![currentFailoverIndex].address,
|
||||
failovers![currentFailoverIndex].port,
|
||||
failovers![currentFailoverIndex].useSSL,
|
||||
proxyInfo,
|
||||
);
|
||||
} else {
|
||||
_electrumAdapterClient ??= ElectrumClient(
|
||||
_electrumAdapterChannel!,
|
||||
failovers![currentFailoverIndex].address,
|
||||
failovers![currentFailoverIndex].port,
|
||||
failovers![currentFailoverIndex].useSSL,
|
||||
proxyInfo,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/// Send raw rpc command
|
||||
|
@ -271,32 +362,22 @@ class ElectrumXClient {
|
|||
}
|
||||
|
||||
if (_requireMutex) {
|
||||
await _torConnectingLock.protect(() async => _checkRpcClient());
|
||||
await _torConnectingLock
|
||||
.protect(() async => await _checkElectrumAdapter());
|
||||
} else {
|
||||
_checkRpcClient();
|
||||
await _checkElectrumAdapter();
|
||||
}
|
||||
|
||||
try {
|
||||
final requestId = requestID ?? const Uuid().v1();
|
||||
final jsonArgs = json.encode(args);
|
||||
final jsonRequestString = '{"jsonrpc": "2.0", '
|
||||
'"id": "$requestId",'
|
||||
'"method": "$command",'
|
||||
'"params": $jsonArgs}';
|
||||
|
||||
// Logging.instance.log("ElectrumX jsonRequestString: $jsonRequestString");
|
||||
|
||||
final response = await _rpcClient!.request(
|
||||
jsonRequestString,
|
||||
requestTimeout,
|
||||
final response = await _electrumAdapterClient!.request(
|
||||
command,
|
||||
args,
|
||||
);
|
||||
|
||||
if (response.exception != null) {
|
||||
throw response.exception!;
|
||||
}
|
||||
|
||||
if (response.data is Map && response.data["error"] != null) {
|
||||
if (response.data["error"]
|
||||
if (response is Map &&
|
||||
response.keys.contains("error") &&
|
||||
response["error"] != null) {
|
||||
if (response["error"]
|
||||
.toString()
|
||||
.contains("No such mempool or blockchain transaction")) {
|
||||
throw NoSuchTransactionException(
|
||||
|
@ -308,13 +389,13 @@ class ElectrumXClient {
|
|||
throw Exception(
|
||||
"JSONRPC response\n"
|
||||
" command: $command\n"
|
||||
" error: ${response.data}"
|
||||
" error: ${response["error"]}\n"
|
||||
" args: $args\n",
|
||||
);
|
||||
}
|
||||
|
||||
currentFailoverIndex = -1;
|
||||
return response.data;
|
||||
return response;
|
||||
} on WifiOnlyException {
|
||||
rethrow;
|
||||
} on SocketException {
|
||||
|
@ -350,7 +431,7 @@ class ElectrumXClient {
|
|||
/// map of <request id string : arguments list>
|
||||
///
|
||||
/// returns a list of json response objects if no errors were found
|
||||
Future<List<Map<String, dynamic>>> batchRequest({
|
||||
Future<List<dynamic>> batchRequest({
|
||||
required String command,
|
||||
required Map<String, List<dynamic>> args,
|
||||
Duration requestTimeout = const Duration(seconds: 60),
|
||||
|
@ -361,62 +442,34 @@ class ElectrumXClient {
|
|||
}
|
||||
|
||||
if (_requireMutex) {
|
||||
await _torConnectingLock.protect(() async => _checkRpcClient());
|
||||
await _torConnectingLock
|
||||
.protect(() async => await _checkElectrumAdapter());
|
||||
} else {
|
||||
_checkRpcClient();
|
||||
await _checkElectrumAdapter();
|
||||
}
|
||||
|
||||
try {
|
||||
final List<String> requestStrings = [];
|
||||
|
||||
var futures = <Future<dynamic>>[];
|
||||
List? response;
|
||||
_electrumAdapterClient!.peer.withBatch(() {
|
||||
for (final entry in args.entries) {
|
||||
final jsonArgs = json.encode(entry.value);
|
||||
requestStrings.add(
|
||||
'{"jsonrpc": "2.0", "id": "${entry.key}","method": "$command","params": $jsonArgs}');
|
||||
}
|
||||
|
||||
// combine request strings into json array
|
||||
String request = "[";
|
||||
for (int i = 0; i < requestStrings.length - 1; i++) {
|
||||
request += "${requestStrings[i]},";
|
||||
}
|
||||
request += "${requestStrings.last}]";
|
||||
|
||||
// Logging.instance.log("batch request: $request");
|
||||
|
||||
// send batch request
|
||||
final jsonRpcResponse =
|
||||
(await _rpcClient!.request(request, requestTimeout));
|
||||
|
||||
if (jsonRpcResponse.exception != null) {
|
||||
throw jsonRpcResponse.exception!;
|
||||
}
|
||||
|
||||
final List<dynamic> response;
|
||||
try {
|
||||
if (jsonRpcResponse.data is Map) {
|
||||
response = [jsonRpcResponse.data];
|
||||
|
||||
if (requestStrings.length > 1) {
|
||||
Logging.instance.log(
|
||||
"ElectrumXClient.batchRequest: Map returned instead of a list and there are ${requestStrings.length} queued.",
|
||||
level: LogLevel.Error);
|
||||
}
|
||||
// Could throw error here.
|
||||
} else {
|
||||
response = jsonRpcResponse.data as List;
|
||||
}
|
||||
} catch (_) {
|
||||
throw Exception(
|
||||
"Expected json list or map but got a ${jsonRpcResponse.data.runtimeType}: ${jsonRpcResponse.data}",
|
||||
);
|
||||
futures.add(_electrumAdapterClient!.request(command, entry.value));
|
||||
}
|
||||
});
|
||||
response = await Future.wait(futures);
|
||||
|
||||
// check for errors, format and throw if there are any
|
||||
final List<String> errors = [];
|
||||
for (int i = 0; i < response.length; i++) {
|
||||
final result = response[i];
|
||||
if (result["error"] != null || result["result"] == null) {
|
||||
var result = response[i];
|
||||
|
||||
if (result == null || (result is List && result.isEmpty)) {
|
||||
continue;
|
||||
// TODO [prio=extreme]: Figure out if this is actually an issue.
|
||||
}
|
||||
result = result[0]; // Unwrap the list.
|
||||
if ((result is Map && result.keys.contains("error")) ||
|
||||
result == null) {
|
||||
errors.add(result.toString());
|
||||
}
|
||||
}
|
||||
|
@ -430,7 +483,7 @@ class ElectrumXClient {
|
|||
}
|
||||
|
||||
currentFailoverIndex = -1;
|
||||
return List<Map<String, dynamic>>.from(response, growable: false);
|
||||
return response;
|
||||
} on WifiOnlyException {
|
||||
rethrow;
|
||||
} on SocketException {
|
||||
|
@ -471,7 +524,7 @@ class ElectrumXClient {
|
|||
requestTimeout: const Duration(seconds: 2),
|
||||
retries: retryCount,
|
||||
).timeout(const Duration(seconds: 2)) as Map<String, dynamic>;
|
||||
return response.keys.contains("result") && response["result"] == null;
|
||||
return response.isNotEmpty; // TODO [prio=extreme]: Fix this.
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
|
@ -492,14 +545,14 @@ class ElectrumXClient {
|
|||
requestID: requestID,
|
||||
command: 'blockchain.headers.subscribe',
|
||||
);
|
||||
if (response["result"] == null) {
|
||||
if (response == null) {
|
||||
Logging.instance.log(
|
||||
"getBlockHeadTip returned null response",
|
||||
level: LogLevel.Error,
|
||||
);
|
||||
throw 'getBlockHeadTip returned null response';
|
||||
}
|
||||
return Map<String, dynamic>.from(response["result"] as Map);
|
||||
return Map<String, dynamic>.from(response as Map);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
|
@ -524,7 +577,7 @@ class ElectrumXClient {
|
|||
requestID: requestID,
|
||||
command: 'server.features',
|
||||
);
|
||||
return Map<String, dynamic>.from(response["result"] as Map);
|
||||
return Map<String, dynamic>.from(response as Map);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
|
@ -545,7 +598,7 @@ class ElectrumXClient {
|
|||
rawTx,
|
||||
],
|
||||
);
|
||||
return response["result"] as String;
|
||||
return response as String;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
|
@ -572,7 +625,7 @@ class ElectrumXClient {
|
|||
scripthash,
|
||||
],
|
||||
);
|
||||
return Map<String, dynamic>.from(response["result"] as Map);
|
||||
return Map<String, dynamic>.from(response as Map);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
|
@ -609,7 +662,7 @@ class ElectrumXClient {
|
|||
scripthash,
|
||||
],
|
||||
);
|
||||
result = response["result"];
|
||||
result = response;
|
||||
retryCount--;
|
||||
}
|
||||
|
||||
|
@ -619,17 +672,16 @@ class ElectrumXClient {
|
|||
}
|
||||
}
|
||||
|
||||
Future<Map<String, List<Map<String, dynamic>>>> getBatchHistory(
|
||||
Future<Map<int, List<Map<String, dynamic>>>> getBatchHistory(
|
||||
{required Map<String, List<dynamic>> args}) async {
|
||||
try {
|
||||
final response = await batchRequest(
|
||||
command: 'blockchain.scripthash.get_history',
|
||||
args: args,
|
||||
);
|
||||
final Map<String, List<Map<String, dynamic>>> result = {};
|
||||
final Map<int, List<Map<String, dynamic>>> result = {};
|
||||
for (int i = 0; i < response.length; i++) {
|
||||
result[response[i]["id"] as String] =
|
||||
List<Map<String, dynamic>>.from(response[i]["result"] as List);
|
||||
result[i] = List<Map<String, dynamic>>.from(response[i] as List);
|
||||
}
|
||||
return result;
|
||||
} catch (e) {
|
||||
|
@ -667,23 +719,33 @@ class ElectrumXClient {
|
|||
scripthash,
|
||||
],
|
||||
);
|
||||
return List<Map<String, dynamic>>.from(response["result"] as List);
|
||||
return List<Map<String, dynamic>>.from(response as List);
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, List<Map<String, dynamic>>>> getBatchUTXOs(
|
||||
Future<Map<int, List<Map<String, dynamic>>>> getBatchUTXOs(
|
||||
{required Map<String, List<dynamic>> args}) async {
|
||||
try {
|
||||
final response = await batchRequest(
|
||||
command: 'blockchain.scripthash.listunspent',
|
||||
args: args,
|
||||
);
|
||||
final Map<String, List<Map<String, dynamic>>> result = {};
|
||||
final Map<int, List<Map<String, dynamic>>> result = {};
|
||||
for (int i = 0; i < response.length; i++) {
|
||||
result[response[i]["id"] as String] =
|
||||
List<Map<String, dynamic>>.from(response[i]["result"] as List);
|
||||
if ((response[i] as List).isNotEmpty) {
|
||||
try {
|
||||
// result[i] = response[i] as List<Map<String, dynamic>>;
|
||||
result[i] = List<Map<String, dynamic>>.from(response[i] as List);
|
||||
} catch (e) {
|
||||
print(response[i]);
|
||||
Logging.instance.log(
|
||||
"getBatchUTXOs failed to parse response",
|
||||
level: LogLevel.Error,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} catch (e) {
|
||||
|
@ -745,14 +807,8 @@ class ElectrumXClient {
|
|||
}) async {
|
||||
Logging.instance.log("attempting to fetch blockchain.transaction.get...",
|
||||
level: LogLevel.Info);
|
||||
var channel = await electrum_adapter.connect(host,
|
||||
port: port,
|
||||
useSSL: useSSL,
|
||||
proxyInfo: Prefs.instance.useTor
|
||||
? TorService.sharedInstance.getProxyInfo()
|
||||
: null);
|
||||
var client = electrum_adapter.ElectrumClient(channel, host, port);
|
||||
dynamic response = await client.getTransaction(txHash);
|
||||
await _checkElectrumAdapter();
|
||||
dynamic response = await _electrumAdapterClient!.getTransaction(txHash);
|
||||
Logging.instance.log("Fetching blockchain.transaction.get finished",
|
||||
level: LogLevel.Info);
|
||||
|
||||
|
@ -784,18 +840,13 @@ class ElectrumXClient {
|
|||
}) async {
|
||||
Logging.instance.log("attempting to fetch lelantus.getanonymityset...",
|
||||
level: LogLevel.Info);
|
||||
var channel = await electrum_adapter.connect(host,
|
||||
port: port,
|
||||
useSSL: useSSL,
|
||||
proxyInfo: Prefs.instance.useTor
|
||||
? TorService.sharedInstance.getProxyInfo()
|
||||
: null);
|
||||
var client = electrum_adapter.FiroElectrumClient(channel);
|
||||
Map<String, dynamic> anonymitySet = await client.getLelantusAnonymitySet(
|
||||
groupId: groupId, blockHash: blockhash);
|
||||
await _checkElectrumAdapter();
|
||||
Map<String, dynamic> response =
|
||||
await (_electrumAdapterClient as FiroElectrumClient)!
|
||||
.getLelantusAnonymitySet(groupId: groupId, blockHash: blockhash);
|
||||
Logging.instance.log("Fetching lelantus.getanonymityset finished",
|
||||
level: LogLevel.Info);
|
||||
return anonymitySet;
|
||||
return response;
|
||||
}
|
||||
|
||||
//TODO add example to docs
|
||||
|
@ -808,17 +859,12 @@ class ElectrumXClient {
|
|||
}) async {
|
||||
Logging.instance.log("attempting to fetch lelantus.getmintmetadata...",
|
||||
level: LogLevel.Info);
|
||||
var channel = await electrum_adapter.connect(host,
|
||||
port: port,
|
||||
useSSL: useSSL,
|
||||
proxyInfo: Prefs.instance.useTor
|
||||
? TorService.sharedInstance.getProxyInfo()
|
||||
: null);
|
||||
var client = electrum_adapter.FiroElectrumClient(channel);
|
||||
dynamic mintData = await client.getLelantusMintData(mints: mints);
|
||||
await _checkElectrumAdapter();
|
||||
dynamic response = await (_electrumAdapterClient as FiroElectrumClient)!
|
||||
.getLelantusMintData(mints: mints);
|
||||
Logging.instance.log("Fetching lelantus.getmintmetadata finished",
|
||||
level: LogLevel.Info);
|
||||
return mintData;
|
||||
return response;
|
||||
}
|
||||
|
||||
//TODO add example to docs
|
||||
|
@ -829,20 +875,14 @@ class ElectrumXClient {
|
|||
}) async {
|
||||
Logging.instance.log("attempting to fetch lelantus.getusedcoinserials...",
|
||||
level: LogLevel.Info);
|
||||
var channel = await electrum_adapter.connect(host,
|
||||
port: port,
|
||||
useSSL: useSSL,
|
||||
proxyInfo: Prefs.instance.useTor
|
||||
? TorService.sharedInstance.getProxyInfo()
|
||||
: null);
|
||||
var client = electrum_adapter.FiroElectrumClient(channel);
|
||||
await _checkElectrumAdapter();
|
||||
|
||||
int retryCount = 3;
|
||||
dynamic usedCoinSerials;
|
||||
dynamic response;
|
||||
|
||||
while (retryCount > 0 && usedCoinSerials is! List) {
|
||||
usedCoinSerials =
|
||||
await client.getLelantusUsedCoinSerials(startNumber: startNumber);
|
||||
while (retryCount > 0 && response is! List) {
|
||||
response = await (_electrumAdapterClient as FiroElectrumClient)!
|
||||
.getLelantusUsedCoinSerials(startNumber: startNumber);
|
||||
// TODO add 2 minute timeout.
|
||||
Logging.instance.log("Fetching lelantus.getusedcoinserials finished",
|
||||
level: LogLevel.Info);
|
||||
|
@ -850,7 +890,7 @@ class ElectrumXClient {
|
|||
retryCount--;
|
||||
}
|
||||
|
||||
return Map<String, dynamic>.from(usedCoinSerials as Map);
|
||||
return Map<String, dynamic>.from(response as Map);
|
||||
}
|
||||
|
||||
/// Returns the latest Lelantus set id
|
||||
|
@ -859,17 +899,12 @@ class ElectrumXClient {
|
|||
Future<int> getLelantusLatestCoinId({String? requestID}) async {
|
||||
Logging.instance.log("attempting to fetch lelantus.getlatestcoinid...",
|
||||
level: LogLevel.Info);
|
||||
var channel = await electrum_adapter.connect(host,
|
||||
port: port,
|
||||
useSSL: useSSL,
|
||||
proxyInfo: Prefs.instance.useTor
|
||||
? TorService.sharedInstance.getProxyInfo()
|
||||
: null);
|
||||
var client = electrum_adapter.FiroElectrumClient(channel);
|
||||
int latestCoinId = await client.getLatestCoinId();
|
||||
await _checkElectrumAdapter();
|
||||
int response =
|
||||
await (_electrumAdapterClient as FiroElectrumClient).getLatestCoinId();
|
||||
Logging.instance.log("Fetching lelantus.getlatestcoinid finished",
|
||||
level: LogLevel.Info);
|
||||
return latestCoinId;
|
||||
return response;
|
||||
}
|
||||
|
||||
// ============== Spark ======================================================
|
||||
|
@ -895,18 +930,14 @@ class ElectrumXClient {
|
|||
try {
|
||||
Logging.instance.log("attempting to fetch spark.getsparkanonymityset...",
|
||||
level: LogLevel.Info);
|
||||
var channel = await electrum_adapter.connect(host,
|
||||
port: port,
|
||||
useSSL: useSSL,
|
||||
proxyInfo: Prefs.instance.useTor
|
||||
? TorService.sharedInstance.getProxyInfo()
|
||||
: null);
|
||||
var client = electrum_adapter.FiroElectrumClient(channel);
|
||||
Map<String, dynamic> anonymitySet = await client.getSparkAnonymitySet(
|
||||
await _checkElectrumAdapter();
|
||||
Map<String, dynamic> response =
|
||||
await (_electrumAdapterClient as FiroElectrumClient)
|
||||
.getSparkAnonymitySet(
|
||||
coinGroupId: coinGroupId, startBlockHash: startBlockHash);
|
||||
Logging.instance.log("Fetching spark.getsparkanonymityset finished",
|
||||
level: LogLevel.Info);
|
||||
return anonymitySet;
|
||||
return response;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
|
@ -922,19 +953,14 @@ class ElectrumXClient {
|
|||
// Use electrum_adapter package's getSparkUsedCoinsTags method.
|
||||
Logging.instance.log("attempting to fetch spark.getusedcoinstags...",
|
||||
level: LogLevel.Info);
|
||||
var channel = await electrum_adapter.connect(host,
|
||||
port: port,
|
||||
useSSL: useSSL,
|
||||
proxyInfo: Prefs.instance.useTor
|
||||
? TorService.sharedInstance.getProxyInfo()
|
||||
: null);
|
||||
var client = electrum_adapter.FiroElectrumClient(channel);
|
||||
Map<String, dynamic> usedCoinsTags =
|
||||
await client.getUsedCoinsTags(startNumber: startNumber);
|
||||
await _checkElectrumAdapter();
|
||||
Map<String, dynamic> response =
|
||||
await (_electrumAdapterClient as FiroElectrumClient)
|
||||
.getUsedCoinsTags(startNumber: startNumber);
|
||||
// TODO: Add 2 minute timeout.
|
||||
Logging.instance.log("Fetching spark.getusedcoinstags finished",
|
||||
level: LogLevel.Info);
|
||||
final map = Map<String, dynamic>.from(usedCoinsTags);
|
||||
final map = Map<String, dynamic>.from(response);
|
||||
final set = Set<String>.from(map["tags"] as List);
|
||||
return await compute(_ffiHashTagsComputeWrapper, set);
|
||||
} catch (e) {
|
||||
|
@ -960,18 +986,13 @@ class ElectrumXClient {
|
|||
try {
|
||||
Logging.instance.log("attempting to fetch spark.getsparkmintmetadata...",
|
||||
level: LogLevel.Info);
|
||||
var channel = await electrum_adapter.connect(host,
|
||||
port: port,
|
||||
useSSL: useSSL,
|
||||
proxyInfo: Prefs.instance.useTor
|
||||
? TorService.sharedInstance.getProxyInfo()
|
||||
: null);
|
||||
var client = electrum_adapter.FiroElectrumClient(channel);
|
||||
List<dynamic> mintMetaData =
|
||||
await client.getSparkMintMetaData(sparkCoinHashes: sparkCoinHashes);
|
||||
await _checkElectrumAdapter();
|
||||
List<dynamic> response =
|
||||
await (_electrumAdapterClient as FiroElectrumClient)
|
||||
.getSparkMintMetaData(sparkCoinHashes: sparkCoinHashes);
|
||||
Logging.instance.log("Fetching spark.getsparkmintmetadata finished",
|
||||
level: LogLevel.Info);
|
||||
return List<Map<String, dynamic>>.from(mintMetaData);
|
||||
return List<Map<String, dynamic>>.from(response);
|
||||
} catch (e) {
|
||||
Logging.instance.log(e, level: LogLevel.Error);
|
||||
rethrow;
|
||||
|
@ -987,17 +1008,12 @@ class ElectrumXClient {
|
|||
try {
|
||||
Logging.instance.log("attempting to fetch spark.getsparklatestcoinid...",
|
||||
level: LogLevel.Info);
|
||||
var channel = await electrum_adapter.connect(host,
|
||||
port: port,
|
||||
useSSL: useSSL,
|
||||
proxyInfo: Prefs.instance.useTor
|
||||
? TorService.sharedInstance.getProxyInfo()
|
||||
: null);
|
||||
var client = electrum_adapter.FiroElectrumClient(channel);
|
||||
int latestCoinId = await client.getSparkLatestCoinId();
|
||||
await _checkElectrumAdapter();
|
||||
int response = await (_electrumAdapterClient as FiroElectrumClient)
|
||||
.getSparkLatestCoinId();
|
||||
Logging.instance.log("Fetching spark.getsparklatestcoinid finished",
|
||||
level: LogLevel.Info);
|
||||
return latestCoinId;
|
||||
return response;
|
||||
} catch (e) {
|
||||
Logging.instance.log(e, level: LogLevel.Error);
|
||||
rethrow;
|
||||
|
@ -1014,14 +1030,8 @@ class ElectrumXClient {
|
|||
/// "rate": 1000,
|
||||
/// }
|
||||
Future<Map<String, dynamic>> getFeeRate({String? requestID}) async {
|
||||
var channel = await electrum_adapter.connect(host,
|
||||
port: port,
|
||||
useSSL: useSSL,
|
||||
proxyInfo: Prefs.instance.useTor
|
||||
? TorService.sharedInstance.getProxyInfo()
|
||||
: null);
|
||||
var client = electrum_adapter.FiroElectrumClient(channel);
|
||||
return await client.getFeeRate();
|
||||
await _checkElectrumAdapter();
|
||||
return await _electrumAdapterClient!.getFeeRate();
|
||||
}
|
||||
|
||||
/// Return the estimated transaction fee per kilobyte for a transaction to be confirmed within a certain number of [blocks].
|
||||
|
@ -1039,10 +1049,10 @@ class ElectrumXClient {
|
|||
],
|
||||
);
|
||||
try {
|
||||
return Decimal.parse(response["result"].toString());
|
||||
return Decimal.parse(response.toString());
|
||||
} catch (e, s) {
|
||||
final String msg = "Error parsing fee rate. Response: $response"
|
||||
"\nResult: ${response["result"]}\nError: $e\nStack trace: $s";
|
||||
"\nResult: ${response}\nError: $e\nStack trace: $s";
|
||||
Logging.instance.log(msg, level: LogLevel.Fatal);
|
||||
throw Exception(msg);
|
||||
}
|
||||
|
@ -1062,7 +1072,7 @@ class ElectrumXClient {
|
|||
requestID: requestID,
|
||||
command: 'blockchain.relayfee',
|
||||
);
|
||||
return Decimal.parse(response["result"].toString());
|
||||
return Decimal.parse(response.toString());
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
|
|
|
@ -177,6 +177,7 @@ class _AddEditNodeViewState extends ConsumerState<AddEditNodeView> {
|
|||
useSSL: formData.useSSL!,
|
||||
failovers: [],
|
||||
prefs: ref.read(prefsChangeNotifierProvider),
|
||||
coin: coin,
|
||||
);
|
||||
|
||||
try {
|
||||
|
|
|
@ -154,6 +154,7 @@ class _NodeDetailsViewState extends ConsumerState<NodeDetailsView> {
|
|||
useSSL: node.useSSL,
|
||||
failovers: [],
|
||||
prefs: ref.read(prefsChangeNotifierProvider),
|
||||
coin: coin,
|
||||
);
|
||||
|
||||
try {
|
||||
|
|
|
@ -146,6 +146,7 @@ class NotificationsService extends ChangeNotifier {
|
|||
node: eNode,
|
||||
failovers: failovers,
|
||||
prefs: prefs,
|
||||
coin: coin,
|
||||
);
|
||||
final tx = await client.getTransaction(txHash: txid);
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@ import 'dart:math';
|
|||
import 'package:bip47/src/util.dart';
|
||||
import 'package:bitcoindart/bitcoindart.dart' as bitcoindart;
|
||||
import 'package:coinlib_flutter/coinlib_flutter.dart' as coinlib;
|
||||
import 'package:electrum_adapter/electrum_adapter.dart' as electrum_adapter;
|
||||
import 'package:electrum_adapter/electrum_adapter.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:stackwallet/electrumx_rpc/cached_electrumx_client.dart';
|
||||
import 'package:stackwallet/electrumx_rpc/electrumx_chain_height_service.dart';
|
||||
|
@ -15,22 +17,26 @@ import 'package:stackwallet/models/isar/models/blockchain_data/v2/transaction_v2
|
|||
import 'package:stackwallet/models/isar/models/isar_models.dart';
|
||||
import 'package:stackwallet/models/paymint/fee_object_model.dart';
|
||||
import 'package:stackwallet/models/signing_data.dart';
|
||||
import 'package:stackwallet/services/tor_service.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/enums/derive_path_type_enum.dart';
|
||||
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/paynym_is_api.dart';
|
||||
import 'package:stackwallet/utilities/prefs.dart';
|
||||
import 'package:stackwallet/wallets/crypto_currency/coins/firo.dart';
|
||||
import 'package:stackwallet/wallets/crypto_currency/intermediate/bip39_hd_currency.dart';
|
||||
import 'package:stackwallet/wallets/models/tx_data.dart';
|
||||
import 'package:stackwallet/wallets/wallet/impl/bitcoin_wallet.dart';
|
||||
import 'package:stackwallet/wallets/wallet/intermediate/bip39_hd_wallet.dart';
|
||||
import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
import 'package:stream_channel/stream_channel.dart';
|
||||
|
||||
mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
|
||||
late ElectrumXClient electrumXClient;
|
||||
late StreamChannel electrumAdapterChannel;
|
||||
late ElectrumClient electrumAdapterClient;
|
||||
late CachedElectrumXClient electrumXCachedClient;
|
||||
late SubscribableElectrumXClient subscribableElectrumXClient;
|
||||
|
||||
|
@ -889,7 +895,7 @@ mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
|
|||
return transactions.length;
|
||||
}
|
||||
|
||||
Future<Map<String, int>> fetchTxCountBatched({
|
||||
Future<Map<int, int>> fetchTxCountBatched({
|
||||
required Map<String, String> addresses,
|
||||
}) async {
|
||||
try {
|
||||
|
@ -901,7 +907,7 @@ mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
|
|||
}
|
||||
final response = await electrumXClient.getBatchHistory(args: args);
|
||||
|
||||
final Map<String, int> result = {};
|
||||
final Map<int, int> result = {};
|
||||
for (final entry in response.entries) {
|
||||
result[entry.key] = entry.value.length;
|
||||
}
|
||||
|
@ -943,9 +949,40 @@ mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
|
|||
node: newNode,
|
||||
prefs: prefs,
|
||||
failovers: failovers,
|
||||
coin: cryptoCurrency.coin,
|
||||
);
|
||||
electrumAdapterChannel = await electrum_adapter.connect(
|
||||
newNode.address,
|
||||
port: newNode.port,
|
||||
acceptUnverified: true,
|
||||
useSSL: newNode.useSSL,
|
||||
proxyInfo: Prefs.instance.useTor
|
||||
? TorService.sharedInstance.getProxyInfo()
|
||||
: null,
|
||||
);
|
||||
if (electrumXClient.coin == Coin.firo ||
|
||||
electrumXClient.coin == Coin.firoTestNet) {
|
||||
electrumAdapterClient = FiroElectrumClient(
|
||||
electrumAdapterChannel,
|
||||
newNode.address,
|
||||
newNode.port,
|
||||
newNode.useSSL,
|
||||
Prefs.instance.useTor
|
||||
? TorService.sharedInstance.getProxyInfo()
|
||||
: null);
|
||||
} else {
|
||||
electrumAdapterClient = ElectrumClient(
|
||||
electrumAdapterChannel,
|
||||
newNode.address,
|
||||
newNode.port,
|
||||
newNode.useSSL,
|
||||
Prefs.instance.useTor
|
||||
? TorService.sharedInstance.getProxyInfo()
|
||||
: null);
|
||||
}
|
||||
electrumXCachedClient = CachedElectrumXClient.from(
|
||||
electrumXClient: electrumXClient,
|
||||
electrumAdapterClient: electrumAdapterClient,
|
||||
);
|
||||
subscribableElectrumXClient = SubscribableElectrumXClient.from(
|
||||
node: newNode,
|
||||
|
@ -1115,21 +1152,22 @@ mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
|
|||
List<Map<String, dynamic>> allTxHashes = [];
|
||||
|
||||
if (serverCanBatch) {
|
||||
final Map<int, Map<String, List<dynamic>>> batches = {};
|
||||
final Map<String, String> requestIdToAddressMap = {};
|
||||
final Map<String, Map<String, List<dynamic>>> batches = {};
|
||||
final Map<int, String> requestIdToAddressMap = {};
|
||||
const batchSizeMax = 100;
|
||||
int batchNumber = 0;
|
||||
for (int i = 0; i < allAddresses.length; i++) {
|
||||
if (batches[batchNumber] == null) {
|
||||
batches[batchNumber] = {};
|
||||
if (batches["$batchNumber"] == null) {
|
||||
batches["$batchNumber"] = {};
|
||||
}
|
||||
final scriptHash = cryptoCurrency.addressToScriptHash(
|
||||
address: allAddresses.elementAt(i),
|
||||
);
|
||||
final id = Logger.isTestEnv ? "$i" : const Uuid().v1();
|
||||
requestIdToAddressMap[id] = allAddresses.elementAt(i);
|
||||
batches[batchNumber]!.addAll({
|
||||
id: [scriptHash]
|
||||
// final id = Logger.isTestEnv ? "$i" : const Uuid().v1();
|
||||
// TODO [prio=???]: Pass request IDs to electrum_adapter.
|
||||
requestIdToAddressMap[i] = allAddresses.elementAt(i);
|
||||
batches["$batchNumber"]!.addAll({
|
||||
"$i": [scriptHash]
|
||||
});
|
||||
if (i % batchSizeMax == batchSizeMax - 1) {
|
||||
batchNumber++;
|
||||
|
@ -1138,7 +1176,7 @@ mixin ElectrumXInterface<T extends Bip39HDCurrency> on Bip39HDWallet<T> {
|
|||
|
||||
for (int i = 0; i < batches.length; i++) {
|
||||
final response =
|
||||
await electrumXClient.getBatchHistory(args: batches[i]!);
|
||||
await electrumXClient.getBatchHistory(args: batches["$i"]!);
|
||||
for (final entry in response.entries) {
|
||||
for (int j = 0; j < entry.value.length; j++) {
|
||||
entry.value[j]["address"] = requestIdToAddressMap[entry.key];
|
||||
|
|
|
@ -175,6 +175,7 @@ class _NodeCardState extends ConsumerState<NodeCard> {
|
|||
useSSL: node.useSSL,
|
||||
failovers: [],
|
||||
prefs: ref.read(prefsChangeNotifierProvider),
|
||||
coin: widget.coin,
|
||||
);
|
||||
|
||||
try {
|
||||
|
|
|
@ -158,6 +158,7 @@ class NodeOptionsSheet extends ConsumerWidget {
|
|||
failovers: [],
|
||||
prefs: ref.read(prefsChangeNotifierProvider),
|
||||
torService: ref.read(pTorService),
|
||||
coin: coin,
|
||||
);
|
||||
|
||||
try {
|
||||
|
|
|
@ -528,8 +528,8 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: dd83940d73429d917f9e50b3a765adbf5e06df6d
|
||||
resolved-ref: dd83940d73429d917f9e50b3a765adbf5e06df6d
|
||||
ref: "51b7a60e07b0409b361e31da65d98178ee235bed"
|
||||
resolved-ref: "51b7a60e07b0409b361e31da65d98178ee235bed"
|
||||
url: "https://github.com/cypherstack/electrum_adapter.git"
|
||||
source: git
|
||||
version: "3.0.0"
|
||||
|
@ -1603,7 +1603,7 @@ packages:
|
|||
source: hosted
|
||||
version: "1.5.3"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||
|
|
|
@ -176,7 +176,8 @@ dependencies:
|
|||
electrum_adapter:
|
||||
git:
|
||||
url: https://github.com/cypherstack/electrum_adapter.git
|
||||
ref: dd83940d73429d917f9e50b3a765adbf5e06df6d
|
||||
ref: 51b7a60e07b0409b361e31da65d98178ee235bed
|
||||
stream_channel: ^2.1.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
Loading…
Reference in a new issue