LCOV - code coverage report
Current view: top level - crypto/utxo/repositories - electrum_json_rpc_client.dart (source / functions) Coverage Total Hit
Test: lcov.info Lines: 81.7 % 82 67
Test Date: 2025-01-30 01:10:00 Functions: - 0 0

            Line data    Source code
       1              : import 'package:walletkit_dart/src/crypto/utxo/utils/endpoint_utils.dart';
       2              : import 'package:walletkit_dart/src/crypto/utxo/entities/electrum_peer.dart';
       3              : import 'package:walletkit_dart/src/crypto/utxo/entities/transactions/electrum_transaction.dart';
       4              : import 'package:walletkit_dart/src/crypto/utxo/utils/utxo_in_memory_cache.dart';
       5              : import 'package:walletkit_dart/walletkit_dart.dart';
       6              : 
       7              : import 'json_rpc_client.dart';
       8              : 
       9              : class ElectrumXClient {
      10              :   final TcpJsonRpcClient _client;
      11              : 
      12           13 :   ElectrumXClient(this._client);
      13              : 
      14            9 :   Future<int?> getCurrentBlock() async {
      15           18 :     final response = await _client.sendRequest(
      16           18 :       {"method": "blockchain.headers.subscribe", "params": []},
      17              :     );
      18            9 :     if (response == null || response is! Json) return null;
      19            9 :     final block = response["height"];
      20              :     return block;
      21              :   }
      22              : 
      23            0 :   Future<List<ElectrumPeer>?> fetchPeers() async {
      24            0 :     final response = await _client.sendRequest(
      25            0 :       {"method": "server.peers.subscribe", "params": []},
      26              :     );
      27            0 :     if (response == null || response is! JsonList) return null;
      28              : 
      29            0 :     return [
      30            0 :       for (var [_, String host, [String version, ...args]] in response)
      31            0 :         if (args.whereType<String>().any((args) => args.startsWith("t")))
      32            0 :           ElectrumPeer.fromJson(
      33              :             host: host,
      34              :             version: version,
      35              :             args: args,
      36              :           )
      37              :     ];
      38              :   }
      39              : 
      40            8 :   Future<UTXOTransaction> getTransaction({
      41              :     required String txHash,
      42              :     required Iterable<AddressType> addressTypes,
      43              :     required UTXONetworkType type,
      44              :     required Iterable<NodeWithAddress> nodes,
      45              :     bool verbose = true,
      46              :   }) async {
      47           16 :     final cache = getUtxoInMemoryCache(type.coin);
      48            8 :     final cachedTx = cache.getTx(txHash: txHash);
      49              :     if (cachedTx != null) {
      50              :       return cachedTx;
      51              :     }
      52           16 :     assert(nodes.isNotEmpty, "Nodes must not be empty");
      53              : 
      54            8 :     Future<Json> blockchainTransactionGet(String txHash) async {
      55           16 :       final response = await _client.sendRequest(
      56            8 :         {
      57              :           "method": "blockchain.transaction.get",
      58            8 :           "params": [txHash, verbose]
      59              :         },
      60              :       );
      61            8 :       if (response == null || response is! Json) {
      62            0 :         throw Exception("Could not fetch transaction $txHash from ElectrumX");
      63              :       }
      64              : 
      65              :       return response;
      66              :     }
      67              : 
      68              :     ///
      69              :     /// Get Initial Transaction
      70              :     ///
      71              :     try {
      72            8 :       final mainTxJson = await blockchainTransactionGet(txHash);
      73              : 
      74              :       ///
      75              :       /// Get Transaction Inputs
      76              :       ///
      77              :       // final inputs = <(String, int)>[];
      78              :       // if (mainTxJson case {"vin": JsonList _inputs}) {
      79              :       //   for (final {"txid": String txid, "vout": int index} in _inputs) {
      80              :       //     inputs.add((txid, index));
      81              :       //   }
      82              :       // }
      83              :       // final outputsFutures = [
      84              :       //   for (final (hash, index) in inputs)
      85              :       //     blockchainTransactionGet(hash).then((tx) {
      86              :       //       if (tx case {"vout": JsonList _outputs}) {
      87              :       //         return ElectrumOutput.fromJson(_outputs[index]);
      88              :       //       }
      89              :       //     })
      90              :       // ];
      91              : 
      92              :       // Logger.logWarning("Fetching ${outputsFutures.length} outputs for $txHash");
      93              : 
      94              :       // final outputs = await Future.wait(outputsFutures);
      95              : 
      96            8 :       final tx = UTXOTransaction.create(
      97              :         json: mainTxJson,
      98              :         addressTypes: addressTypes,
      99              :         type: type,
     100              :         nodes: nodes,
     101            8 :         spentOutputs: [],
     102              :       );
     103              : 
     104              :       /// P2SH Addresses ?
     105              : 
     106            8 :       cache.insertTxIfConfirmed(tx);
     107              :       return tx;
     108              :     } catch (e) {
     109            4 :       throw Exception("Could not fetch transaction $txHash from ElectrumX");
     110              :     }
     111              :   }
     112              : 
     113            4 :   Future<UTXOTransaction> getTransactionForSimulation({
     114              :     required String txHash,
     115              :     required Iterable<AddressType> addressTypes,
     116              :     required UTXONetworkType type,
     117              :     required Iterable<NodeWithAddress> nodes,
     118              :     bool verbose = true,
     119              :   }) async {
     120            4 :     Future<Json> blockchainTransactionGet(String txHash) async {
     121            8 :       final response = await _client.sendRequest(
     122            4 :         {
     123              :           "method": "blockchain.transaction.get",
     124            4 :           "params": [txHash, verbose]
     125              :         },
     126              :       );
     127            4 :       if (response == null || response is! Json) {
     128            0 :         throw Exception("Could not fetch transaction $txHash from ElectrumX");
     129              :       }
     130              : 
     131              :       return response;
     132              :     }
     133              : 
     134              :     ///
     135              :     /// Get Initial Transaction
     136              :     ///
     137            4 :     final mainTxJson = await blockchainTransactionGet(txHash);
     138              : 
     139              :     ///
     140              :     /// Get Transaction Inputs
     141              :     ///
     142            4 :     final inputs = <(String, int)>[];
     143            8 :     if (mainTxJson case {"vin": JsonList _inputs}) {
     144            8 :       for (final input in _inputs) {
     145           17 :         if (input case {"txid": String txid, "vout": int index})
     146            3 :           inputs.add((txid, index));
     147              :       }
     148              :     }
     149            4 :     final outputs = [
     150            4 :       for (final (hash, index) in inputs)
     151           12 :         await blockchainTransactionGet(hash).then((tx) {
     152            6 :           if (tx case {"vout": JsonList _outputs}) {
     153            6 :             return ElectrumOutput.fromJson(_outputs[index]);
     154              :           }
     155              :         })
     156            4 :     ].nonNulls;
     157              : 
     158            4 :     return UTXOTransaction.create(
     159              :       json: mainTxJson,
     160              :       addressTypes: addressTypes,
     161              :       type: type,
     162              :       nodes: nodes,
     163              :       spentOutputs: outputs,
     164              :     );
     165              :   }
     166              : 
     167              :   // do not use getRaw because it does not use an in-memory-cache !
     168            6 :   Future<String?> getRaw(
     169              :     String txHash, {
     170              :     bool verbose = false,
     171              :   }) async {
     172           12 :     final response = await _client.sendRequest(
     173            6 :       {
     174              :         "method": "blockchain.transaction.get",
     175            6 :         "params": [txHash, verbose]
     176              :       },
     177              :     );
     178            6 :     return response.toString();
     179              :   }
     180              : 
     181            9 :   Future<Set<ElectrumTransactionInfo>?> getHistory(
     182              :     String publicKeyScriptHash,
     183              :   ) async {
     184           18 :     final response = await _client.sendRequest(
     185            9 :       {
     186              :         "method": "blockchain.scripthash.get_history",
     187            9 :         "params": [publicKeyScriptHash]
     188              :       },
     189              :     );
     190            9 :     if (response == null || response is! List<dynamic>) return null;
     191              :     return {
     192           18 :       for (final json in response) ElectrumTransactionInfo.fromJson(json),
     193              :     };
     194              :   }
     195              : 
     196            0 :   Future<String> broadcastTransaction({required String rawTxHex}) async {
     197            0 :     final response = await _client.sendRawRequest(
     198            0 :       {
     199              :         "method": "blockchain.transaction.broadcast",
     200            0 :         "params": [rawTxHex]
     201              :       },
     202              :     );
     203              :     return response;
     204              :   }
     205              : 
     206            1 :   Future<double> estimateFee({required int blocks}) async {
     207            2 :     final response = await _client.sendRequest(
     208            1 :       {
     209              :         "method": "blockchain.estimatefee",
     210            1 :         "params": [blocks]
     211              :       },
     212              :     );
     213              :     final fee = response as double?;
     214            1 :     if (fee == null || fee == 0) throw Exception("Fee estimation failed");
     215              :     return fee;
     216              :   }
     217              : 
     218           13 :   Future<bool> disconnect() async {
     219           26 :     await _client.disconnect();
     220              :     return true;
     221              :   }
     222              : 
     223           21 :   String get host => _client.host;
     224              : }
     225              : 
     226            2 : Future<String> fetchRawTxByHash(
     227              :   String hash,
     228              :   UTXONetworkType networkType,
     229              : ) async {
     230            2 :   final (result, _client, _) = await fetchFromRandomElectrumXNode(
     231            2 :     (client) async {
     232            2 :       return await client.getRaw(hash);
     233              :     },
     234              :     client: null,
     235            2 :     endpoints: networkType.endpoints,
     236            2 :     token: networkType.coin,
     237            2 :     timeout: Duration(seconds: 20),
     238              :   );
     239            2 :   await _client?.disconnect();
     240            4 :   if (result == null || result.isEmpty || result == "null") {
     241            0 :     throw Exception("No result for $hash");
     242              :   }
     243              : 
     244              :   return result;
     245              : }
        

Generated by: LCOV version 2.0-1