query electrum for missing tx info

This commit is contained in:
sneurlax 2023-09-19 17:58:55 -05:00
parent 93cfca7ddf
commit ef38e58b57
4 changed files with 107 additions and 14 deletions

View file

@ -726,6 +726,14 @@ class ElectrumX {
return {"rawtx": response["result"] as String};
}
if (response["result"] == null) {
Logging.instance.log(
"getTransaction($txHash) returned null response",
level: LogLevel.Error,
);
throw 'getTransaction($txHash) returned null response';
}
return Map<String, dynamic>.from(response["result"] as Map);
} catch (e) {
Logging.instance.log(

View file

@ -11,15 +11,18 @@
import 'dart:convert';
import 'dart:math';
import 'package:decimal/decimal.dart';
import 'package:fusiondart/src/models/address.dart' as fusion_address;
import 'package:fusiondart/src/models/input.dart' as fusion_input;
import 'package:fusiondart/src/models/output.dart' as fusion_output;
import 'package:fusiondart/src/models/transaction.dart' as fusion_tx;
import 'package:isar/isar.dart';
import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/address.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/input.dart';
import 'package:stackwallet/models/isar/models/blockchain_data/output.dart';
import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:tuple/tuple.dart';
part 'transaction.g.dart';
@ -237,19 +240,57 @@ class Transaction {
}
// Convert to fusiondart's Transaction type.
fusion_tx.Transaction toFusionTransaction() {
//
// This is bad because in the FusionWalletInterface in getTransactionsByAddress
// we already getAllTransactions, then for each transaction here we also getTransaction
// for each transaction again. But the data we need--the input's value--isn't
// here anyways.
Future<fusion_tx.Transaction> toFusionTransaction(
CachedElectrumX cachedElectrumX) async {
// Initialize Fusion Dart's Transaction object.
fusion_tx.Transaction fusionTransaction = fusion_tx.Transaction();
// Map the Inputs and Outputs to Fusion Dart's format
fusionTransaction.Inputs = inputs.map((e) {
return fusion_input.Input(
prevTxid: utf8.encode(e.txid),
prevIndex: e.vout,
pubKey: utf8.encode(address.value.toString()), // TODO fix public key.
amount: amount, // TODO is this valid? Probably not.
// Map the Inputs and Outputs to Fusion Dart's format.
//
// This takes an exqcessive amount of time because we have to get the
// transaction for each input. We already have the transaction for each
// input in getTransactionsByAddress, but we don't have the input's value.
// So we have to get the transaction again here.
fusionTransaction.Inputs = await Future.wait(inputs.map((e) async {
// Find input amount.
print("2 getting tx ${e.txid}");
Map<String, dynamic> _tx = await cachedElectrumX.getTransaction(
coin: Coin.bitcoincash,
txHash: e.txid,
verbose: true); // TODO is verbose needed?
// Check if output amount is available.
if (_tx == null) {
throw Exception("Transaction not found for input: ${e.txid}");
}
if (_tx["vout"] == null) {
throw Exception("Vout in transaction ${e.txid} is null");
}
if (_tx["vout"][e.vout] == null) {
throw Exception("Vout index ${e.vout} in transaction is null");
}
if (_tx["vout"][e.vout]["value"] == null) {
throw Exception("Value of vout index ${e.vout} in transaction is null");
}
// Assign vout value to amount.
final value = Amount.fromDecimal(
Decimal.parse(_tx["vout"][e.vout]["value"].toString()),
fractionDigits: Coin.bitcoincash.decimals,
);
}).toList();
return fusion_input.Input(
prevTxid: utf8.encode(e.txid), // TODO verify this is what we want.
prevIndex: e.vout, // TODO verify this is what we want.
pubKey: utf8.encode(address.value.toString()), // TODO fix public key.
amount: value.raw.toInt(),
);
}).toList());
fusionTransaction.Outputs = outputs.map((e) {
/*

View file

@ -140,6 +140,7 @@ class BitcoinCashWallet extends CoinServiceAPI
coin: coin,
db: db,
generateAddressForChain: _generateAddressForChain,
cachedElectrumX: cachedElectrumXClient,
);
initCoinControlInterface(
walletId: walletId,

View file

@ -8,6 +8,8 @@ import 'package:fusiondart/src/models/input.dart' as fusion_input;
import 'package:fusiondart/src/models/transaction.dart' as fusion_tx;
import 'package:isar/isar.dart';
import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/electrumx_rpc/cached_electrumx.dart';
import 'package:stackwallet/electrumx_rpc/electrumx.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/services/tor_service.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
@ -22,6 +24,7 @@ mixin FusionWalletInterface {
late final String _walletId;
late final Coin _coin;
late final MainDB _db;
late final CachedElectrumX _cachedElectrumX;
late final TorService _torService;
// Passed in wallet functions.
@ -45,6 +48,7 @@ mixin FusionWalletInterface {
int,
DerivePathType,
) generateAddressForChain,
required CachedElectrumX cachedElectrumX,
}) async {
// Set passed in wallet data.
_walletId = walletId;
@ -52,6 +56,7 @@ mixin FusionWalletInterface {
_db = db;
_generateAddressForChain = generateAddressForChain;
_torService = TorService.sharedInstance;
_cachedElectrumX = cachedElectrumX;
// Try getting the proxy info.
//
@ -82,9 +87,11 @@ mixin FusionWalletInterface {
String address) async {
var _txs = await _db.getTransactions(_walletId).findAll();
return _txs
.map((tx) => tx.toFusionTransaction())
.toSet(); // TODO fix public key.
// Use Future.wait to await all the futures in the set and then convert it to a set.
var resultSet = await Future.wait(
_txs.map((tx) => tx.toFusionTransaction(_cachedElectrumX)));
return resultSet.toSet();
}
/// Returns a list of all UTXOs in the wallet for the given address.
@ -224,9 +231,45 @@ mixin FusionWalletInterface {
// Add stack UTXOs.
List<UTXO> utxos = await _db.getUTXOs(_walletId).findAll();
List<(String, int, int, List<int>)> coinList = [];
await mainFusionObject.addCoinsFromWallet(
utxos.map((e) => (e.txid, e.vout, e.value)).toList());
// Loop through UTXOs, checking and adding valid ones.
for (var e in utxos) {
// Check if address is available.
if (e.address == null) {
// TODO we could continue here (and below during scriptPubKey validation) instead of throwing.
throw Exception("UTXO ${e.txid}:${e.vout} address is null");
}
// Find public key.
print("1 getting tx ${e.txid}");
Map<String, dynamic> tx = await _cachedElectrumX.getTransaction(coin: _coin,
txHash: e.txid, verbose: true); // TODO is verbose needed?
// Check if scriptPubKey is available.
if (tx["vout"] == null) {
throw Exception("Vout in transaction ${e.txid} is null");
}
if (tx["vout"][e.vout] == null) {
throw Exception("Vout index ${e.vout} in transaction is null");
}
if (tx["vout"][e.vout]["scriptPubKey"] == null) {
throw Exception("scriptPubKey at vout index ${e.vout} is null");
}
if (tx["vout"][e.vout]["scriptPubKey"]["hex"] == null) {
throw Exception("hex in scriptPubKey at vout index ${e.vout} is null");
}
// Assign scriptPubKey to pubKey. TODO verify this is correct.
List<int> pubKey =
utf8.encode("${tx["vout"][e.vout]["scriptPubKey"]["hex"]}");
// Add UTXO to coinList.
coinList.add((e.txid, e.vout, e.value, pubKey));
}
// Add Stack UTXOs.
await mainFusionObject.addCoinsFromWallet(coinList);
// Fuse UTXOs.
return await mainFusionObject.fuse();