Confirm sent txns

This commit is contained in:
Hector Chu 2024-04-27 14:26:43 +01:00
parent 70b07b2d47
commit b1f0334550
2 changed files with 61 additions and 15 deletions

View file

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:math'; import 'dart:math';
import 'package:collection/collection.dart';
import 'package:convert/convert.dart'; import 'package:convert/convert.dart';
import 'package:crypto/crypto.dart'; import 'package:crypto/crypto.dart';
import 'package:fixnum/fixnum.dart'; import 'package:fixnum/fixnum.dart';
@ -122,6 +123,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
); );
} }
int mwebUtxosHeight = 0;
@action @action
@override @override
Future<void> startSync() async { Future<void> startSync() async {
@ -142,6 +145,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
} else { } else {
syncStatus = SyncedSyncStatus(); syncStatus = SyncedSyncStatus();
} }
if (resp.mwebUtxosHeight > mwebUtxosHeight) {
mwebUtxosHeight = resp.mwebUtxosHeight;
await checkMwebUtxosSpent();
}
}); });
processMwebUtxos(); processMwebUtxos();
} }
@ -170,14 +177,19 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
date = DateTime.fromMillisecondsSinceEpoch(utxo.blockTime * 1000); date = DateTime.fromMillisecondsSinceEpoch(utxo.blockTime * 1000);
confirmations = status.blockHeaderHeight - utxo.height + 1; confirmations = status.blockHeaderHeight - utxo.height + 1;
} }
final tx = ElectrumTransactionInfo(WalletType.litecoin, var tx = transactionHistory.transactions.values.firstWhereOrNull(
id: utxo.outputId, height: utxo.height, (tx) => tx.outputAddresses?.contains(utxo.outputId) ?? false);
amount: utxo.value.toInt(), fee: 0, if (tx == null)
direction: TransactionDirection.incoming, tx = ElectrumTransactionInfo(WalletType.litecoin,
isPending: utxo.height == 0, id: utxo.outputId, height: utxo.height,
date: date, confirmations: confirmations, amount: utxo.value.toInt(), fee: 0,
inputAddresses: [], direction: TransactionDirection.incoming,
outputAddresses: [utxo.address]); isPending: utxo.height == 0,
date: date, confirmations: confirmations,
inputAddresses: [],
outputAddresses: [utxo.address]);
tx.isPending = utxo.height == 0;
tx.confirmations = confirmations;
if (transactionHistory.transactions[utxo.outputId] == null) { if (transactionHistory.transactions[utxo.outputId] == null) {
final addressRecord = walletAddresses.allAddresses.firstWhere( final addressRecord = walletAddresses.allAddresses.firstWhere(
(addressRecord) => addressRecord.address == utxo.address); (addressRecord) => addressRecord.address == utxo.address);
@ -194,11 +206,12 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
} }
Future<void> checkMwebUtxosSpent() async { Future<void> checkMwebUtxosSpent() async {
final List<String> outputIds = []; while ((await Future.wait(transactionHistory.transactions.values
mwebUtxos.forEach((outputId, utxo) { .where((tx) => tx.direction == TransactionDirection.outgoing && tx.isPending)
if (utxo.height > 0) .map(checkPendingTransaction))).any((x) => x));
outputIds.add(outputId); final outputIds = mwebUtxos.values
}); .where((utxo) => utxo.height > 0)
.map((utxo) => utxo.outputId).toList();
final stub = await CwMweb.stub(); final stub = await CwMweb.stub();
final resp = await stub.spent(SpentRequest(outputId: outputIds)); final resp = await stub.spent(SpentRequest(outputId: outputIds));
final spent = resp.outputId; final spent = resp.outputId;
@ -212,8 +225,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
var output = AccumulatorSink<Digest>(); var output = AccumulatorSink<Digest>();
var input = sha256.startChunkedConversion(output); var input = sha256.startChunkedConversion(output);
for (final outputId in spent) { for (final outputId in spent) {
input.add(hex.decode(outputId)); final utxo = mwebUtxos[outputId];
final utxo = mwebUtxos[outputId]!; if (utxo == null) continue;
final addressRecord = walletAddresses.allAddresses.firstWhere( final addressRecord = walletAddresses.allAddresses.firstWhere(
(addressRecord) => addressRecord.address == utxo.address); (addressRecord) => addressRecord.address == utxo.address);
if (!inputAddresses.contains(utxo.address)) if (!inputAddresses.contains(utxo.address))
@ -222,7 +235,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
amount += utxo.value.toInt(); amount += utxo.value.toInt();
inputAddresses.add(utxo.address); inputAddresses.add(utxo.address);
mwebUtxos.remove(outputId); mwebUtxos.remove(outputId);
input.add(hex.decode(outputId));
} }
if (inputAddresses.isEmpty) return;
input.close(); input.close();
var digest = output.events.single; var digest = output.events.single;
final tx = ElectrumTransactionInfo(WalletType.litecoin, final tx = ElectrumTransactionInfo(WalletType.litecoin,
@ -238,6 +253,33 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
await transactionHistory.save(); await transactionHistory.save();
} }
Future<bool> checkPendingTransaction(ElectrumTransactionInfo tx) async {
if (!tx.isPending) return false;
final outputId = <String>[], target = <String>{};
final isHash = RegExp(r'^[a-f0-9]{64}$').hasMatch;
final spendingOutputIds = tx.inputAddresses?.where(isHash) ?? [];
final payingToOutputIds = tx.outputAddresses?.where(isHash) ?? [];
outputId.addAll(spendingOutputIds);
outputId.addAll(payingToOutputIds);
target.addAll(spendingOutputIds);
for (final outputId in payingToOutputIds) {
final spendingTx = transactionHistory.transactions.values.firstWhereOrNull(
(tx) => tx.inputAddresses?.contains(outputId) ?? false);
if (spendingTx != null && !spendingTx.isPending)
target.add(outputId);
}
if (outputId.isEmpty) return false;
final stub = await CwMweb.stub();
final resp = await stub.spent(SpentRequest(outputId: outputId));
if (resp.outputId.toSet() != target) return false;
final status = await stub.status(StatusRequest());
if (!tx.isPending) return false;
tx.height = status.mwebUtxosHeight;
tx.confirmations = 1;
tx.isPending = false;
return true;
}
@override @override
Future<void> updateUnspentCoins() async { Future<void> updateUnspentCoins() async {
await super.updateUnspentCoins(); await super.updateUnspentCoins();
@ -341,6 +383,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
spendSecret: hex.decode(mwebHd.derive(0x80000001).privKey!), spendSecret: hex.decode(mwebHd.derive(0x80000001).privKey!),
feeRatePerKb: Int64.parseInt(tx.feeRate) * 1000)); feeRatePerKb: Int64.parseInt(tx.feeRate) * 1000));
tx.hexOverride = hex.encode(resp.rawTx); tx.hexOverride = hex.encode(resp.rawTx);
tx.outputs = resp.outputId;
return tx; return tx;
} }
} }

View file

@ -36,6 +36,7 @@ class PendingBitcoinTransaction with PendingTransaction {
final bool hasTaprootInputs; final bool hasTaprootInputs;
String? idOverride; String? idOverride;
String? hexOverride; String? hexOverride;
List<String>? outputs;
@override @override
String get id => idOverride ?? _tx.txId(); String get id => idOverride ?? _tx.txId();
@ -110,5 +111,7 @@ class PendingBitcoinTransaction with PendingTransaction {
date: DateTime.now(), date: DateTime.now(),
isPending: true, isPending: true,
confirmations: 0, confirmations: 0,
inputAddresses: _tx.inputs.map((input) => input.txId).toList(),
outputAddresses: outputs,
fee: fee); fee: fee);
} }