add functionality for different number of required min confirms for coinbase transactions and apply to firo

This commit is contained in:
julian 2024-11-21 09:40:40 -06:00 committed by julian-CStack
parent 3566d75d58
commit 40b0f49f20
20 changed files with 107 additions and 21 deletions

View file

@ -80,9 +80,14 @@ class UTXO {
return max(0, currentChainHeight - (blockHeight! - 1));
}
bool isConfirmed(int currentChainHeight, int minimumConfirms) {
bool isConfirmed(
int currentChainHeight,
int minimumConfirms,
int minimumCoinbaseConfirms,
) {
final confirmations = getConfirmations(currentChainHeight);
return confirmations >= minimumConfirms;
return confirmations >=
(isCoinbase ? minimumCoinbaseConfirms : minimumConfirms);
}
@ignore

View file

@ -112,9 +112,14 @@ class TransactionV2 {
return max(0, currentChainHeight - (height! - 1));
}
bool isConfirmed(int currentChainHeight, int minimumConfirms) {
bool isConfirmed(
int currentChainHeight,
int minimumConfirms,
int minimumCoinbaseConfirms,
) {
final confirmations = getConfirmations(currentChainHeight);
return confirmations >= minimumConfirms;
return confirmations >=
(isCoinbase() ? minimumCoinbaseConfirms : minimumConfirms);
}
Amount getFee({required int fractionDigits}) {
@ -225,6 +230,7 @@ class TransactionV2 {
String statusLabel({
required int currentChainHeight,
required int minConfirms,
required int minCoinbaseConfirms,
}) {
String prettyConfirms() =>
"(${getConfirmations(currentChainHeight)}/$minConfirms)";
@ -233,7 +239,7 @@ class TransactionV2 {
subType == TransactionSubType.mint ||
(subType == TransactionSubType.sparkMint &&
type == TransactionType.sentToSelf)) {
if (isConfirmed(currentChainHeight, minConfirms)) {
if (isConfirmed(currentChainHeight, minConfirms, minCoinbaseConfirms)) {
return "Anonymized";
} else {
return "Anonymizing ${prettyConfirms()}";
@ -248,7 +254,7 @@ class TransactionV2 {
if (isCancelled) {
return "Cancelled";
} else if (type == TransactionType.incoming) {
if (isConfirmed(currentChainHeight, minConfirms)) {
if (isConfirmed(currentChainHeight, minConfirms, minCoinbaseConfirms)) {
return "Received";
} else {
if (numberOfMessages == 1) {
@ -260,7 +266,7 @@ class TransactionV2 {
}
}
} else if (type == TransactionType.outgoing) {
if (isConfirmed(currentChainHeight, minConfirms)) {
if (isConfirmed(currentChainHeight, minConfirms, minCoinbaseConfirms)) {
return "Sent (confirmed)";
} else {
if (numberOfMessages == 1) {
@ -278,19 +284,19 @@ class TransactionV2 {
// if (_transaction.isMinting) {
// return "Minting";
// } else
if (isConfirmed(currentChainHeight, minConfirms)) {
if (isConfirmed(currentChainHeight, minConfirms, minCoinbaseConfirms)) {
return "Received";
} else {
return "Receiving ${prettyConfirms()}";
}
} else if (type == TransactionType.outgoing) {
if (isConfirmed(currentChainHeight, minConfirms)) {
if (isConfirmed(currentChainHeight, minConfirms, minCoinbaseConfirms)) {
return "Sent";
} else {
return "Sending ${prettyConfirms()}";
}
} else if (type == TransactionType.sentToSelf) {
if (isConfirmed(currentChainHeight, minConfirms)) {
if (isConfirmed(currentChainHeight, minConfirms, minCoinbaseConfirms)) {
return "Sent to self";
} else {
return "Sent to self ${prettyConfirms()}";
@ -308,6 +314,9 @@ class TransactionV2 {
return map[key];
}
bool isCoinbase() =>
type == TransactionType.incoming && inputs.any((e) => e.coinbase != null);
@override
String toString() {
return 'TransactionV2(\n'

View file

@ -350,6 +350,7 @@ class _CoinControlViewState extends ConsumerState<CoinControlView> {
utxo.isConfirmed(
currentHeight,
minConfirms,
coin.minCoinbaseConfirms,
)),
initialSelectedState: isSelected,
onSelectedChanged: (value) {
@ -414,6 +415,7 @@ class _CoinControlViewState extends ConsumerState<CoinControlView> {
utxo.isConfirmed(
currentHeight,
minConfirms,
coin.minCoinbaseConfirms,
)),
initialSelectedState: isSelected,
onSelectedChanged: (value) {
@ -558,6 +560,7 @@ class _CoinControlViewState extends ConsumerState<CoinControlView> {
utxo.isConfirmed(
currentHeight,
minConfirms,
coin.minCoinbaseConfirms,
)),
initialSelectedState: isSelected,
onSelectedChanged: (value) {

View file

@ -117,6 +117,11 @@ class _UtxoCardState extends ConsumerState<UtxoCard> {
.getWallet(widget.walletId)
.cryptoCurrency
.minConfirms,
ref
.watch(pWallets)
.getWallet(widget.walletId)
.cryptoCurrency
.minCoinbaseConfirms,
)
? UTXOStatusIconStatus.confirmed
: UTXOStatusIconStatus.unconfirmed,

View file

@ -98,6 +98,11 @@ class _UtxoDetailsViewState extends ConsumerState<UtxoDetailsView> {
final confirmed = utxo!.isConfirmed(
currentHeight,
ref.watch(pWallets).getWallet(widget.walletId).cryptoCurrency.minConfirms,
ref
.watch(pWallets)
.getWallet(widget.walletId)
.cryptoCurrency
.minCoinbaseConfirms,
);
return ConditionalParent(

View file

@ -13,6 +13,7 @@ import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart';
import '../../../models/isar/models/isar_models.dart';
import '../../../models/isar/stack_theme.dart';
@ -106,6 +107,11 @@ class TxIcon extends ConsumerWidget {
!tx.isConfirmed(
currentHeight,
ref.watch(pWallets).getWallet(tx.walletId).cryptoCurrency.minConfirms,
ref
.watch(pWallets)
.getWallet(tx.walletId)
.cryptoCurrency
.minCoinbaseConfirms,
),
tx.subType,
tx.type,

View file

@ -15,14 +15,11 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:isar/isar.dart';
import '../../../../models/isar/models/blockchain_data/v2/transaction_v2.dart';
import '../../../../models/isar/models/contact_entry.dart';
import '../../../../models/isar/models/isar_models.dart';
import '../../../../models/transaction_filter.dart';
import '../../sub_widgets/tx_icon.dart';
import '../transaction_search_filter_view.dart';
import 'transaction_v2_card.dart';
import 'transaction_v2_details_view.dart';
import '../../../../providers/db/main_db_provider.dart';
import '../../../../providers/global/address_book_service_provider.dart';
import '../../../../providers/providers.dart';
@ -49,6 +46,10 @@ import '../../../../widgets/loading_indicator.dart';
import '../../../../widgets/rounded_white_container.dart';
import '../../../../widgets/stack_text_field.dart';
import '../../../../widgets/textfield_icon_button.dart';
import '../../sub_widgets/tx_icon.dart';
import '../transaction_search_filter_view.dart';
import 'transaction_v2_card.dart';
import 'transaction_v2_details_view.dart';
typedef _GroupedTransactions = ({
String label,
@ -866,6 +867,11 @@ class _DesktopTransactionCardRowState
String whatIsIt(TransactionV2 tx, int height) => tx.statusLabel(
currentChainHeight: height,
minConfirms: minConfirms,
minCoinbaseConfirms: ref
.read(pWallets)
.getWallet(widget.walletId)
.cryptoCurrency
.minCoinbaseConfirms,
);
@override

View file

@ -60,6 +60,11 @@ class _TransactionCardStateV2 extends ConsumerState<TransactionCardV2> {
.getWallet(walletId)
.cryptoCurrency
.minConfirms,
minCoinbaseConfirms: ref
.read(pWallets)
.getWallet(walletId)
.cryptoCurrency
.minCoinbaseConfirms,
);
@override

View file

@ -373,6 +373,11 @@ class _TransactionV2DetailsViewState
String whatIsIt(TransactionV2 tx, int height) => tx.statusLabel(
currentChainHeight: height,
minConfirms: minConfirms,
minCoinbaseConfirms: ref
.read(pWallets)
.getWallet(walletId)
.cryptoCurrency
.minCoinbaseConfirms,
);
Future<String> fetchContactNameFor(String address) async {
@ -567,6 +572,7 @@ class _TransactionV2DetailsViewState
final confirmedTxn = _transaction.isConfirmed(
currentHeight,
coin.minConfirms,
coin.minCoinbaseConfirms,
);
return ConditionalParent(
@ -1367,6 +1373,7 @@ class _TransactionV2DetailsViewState
? _transaction.isConfirmed(
currentHeight,
minConfirms,
coin.minCoinbaseConfirms,
)
? ref
.watch(pAmountFormatter(coin))
@ -1484,9 +1491,9 @@ class _TransactionV2DetailsViewState
height = "Unknown";
} else {
final confirmed = _transaction.isConfirmed(
currentHeight,
minConfirms,
);
currentHeight,
minConfirms,
coin.minCoinbaseConfirms);
if (widget.coin is! Epiccash && confirmed) {
height =
"${_transaction.height == 0 ? "Unknown" : _transaction.height}";

View file

@ -11,6 +11,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:isar/isar.dart';
import '../../db/isar/main_db.dart';
import '../../models/isar/models/isar_models.dart';
import '../../pages/coin_control/utxo_details_view.dart';
@ -141,6 +142,11 @@ class _UtxoRowState extends ConsumerState<UtxoRow> {
.getWallet(widget.walletId)
.cryptoCurrency
.minConfirms,
ref
.watch(pWallets)
.getWallet(widget.walletId)
.cryptoCurrency
.minCoinbaseConfirms,
)
? UTXOStatusIconStatus.confirmed
: UTXOStatusIconStatus.unconfirmed,

View file

@ -54,6 +54,9 @@ class Firo extends Bip39HDCurrency with ElectrumXCurrencyInterface {
@override
int get minConfirms => 1;
@override
int get minCoinbaseConfirms => 100;
@override
bool get torSupport => true;

View file

@ -60,6 +60,7 @@ abstract class CryptoCurrency {
bool get torSupport => false;
int get minConfirms;
int get minCoinbaseConfirms => minConfirms;
// TODO: [prio=low] could be handled differently as (at least) epiccash does not use this
String get genesisHash;

View file

@ -129,6 +129,7 @@ class BitcoinFrostWallet<T extends FrostCurrency> extends Wallet<T>
(e) => !e.isConfirmed(
currentHeight,
cryptoCurrency.minConfirms,
cryptoCurrency.minCoinbaseConfirms,
),
);
if (utxos.isEmpty) {
@ -330,7 +331,11 @@ class BitcoinFrostWallet<T extends FrostCurrency> extends Wallet<T>
final height = await chainHeight;
for (final output in (await mainDB.getUTXOs(walletId).findAll())) {
if (!output.isBlocked &&
output.isConfirmed(height, cryptoCurrency.minConfirms)) {
output.isConfirmed(
height,
cryptoCurrency.minConfirms,
cryptoCurrency.minCoinbaseConfirms,
)) {
available += output.value;
inputCount++;
}
@ -448,7 +453,11 @@ class BitcoinFrostWallet<T extends FrostCurrency> extends Wallet<T>
.findFirst();
if (storedTx == null ||
!storedTx.isConfirmed(currentHeight, cryptoCurrency.minConfirms)) {
!storedTx.isConfirmed(
currentHeight,
cryptoCurrency.minConfirms,
cryptoCurrency.minCoinbaseConfirms,
)) {
final tx = await electrumXCachedClient.getTransaction(
txHash: txHash["tx_hash"] as String,
verbose: true,
@ -1060,6 +1069,7 @@ class BitcoinFrostWallet<T extends FrostCurrency> extends Wallet<T>
if (utxo.isConfirmed(
currentChainHeight,
cryptoCurrency.minConfirms,
cryptoCurrency.minCoinbaseConfirms,
)) {
satoshiBalanceSpendable += utxoAmount;
} else {

View file

@ -592,7 +592,11 @@ class FiroWallet<T extends ElectrumXCurrencyInterface> extends Bip39HDWallet<T>
);
if (_unconfirmedTxids.contains(tx.txid)) {
if (tx.isConfirmed(await chainHeight, cryptoCurrency.minConfirms)) {
if (tx.isConfirmed(
await chainHeight,
cryptoCurrency.minConfirms,
cryptoCurrency.minCoinbaseConfirms,
)) {
txns.add(tx);
_unconfirmedTxids.removeWhere((e) => e == tx.txid);
} else {

View file

@ -309,6 +309,7 @@ abstract class Bip39HDWallet<T extends Bip39HDCurrency> extends Bip39Wallet<T>
if (utxo.isConfirmed(
currentChainHeight,
cryptoCurrency.minConfirms,
cryptoCurrency.minCoinbaseConfirms,
)) {
satoshiBalanceSpendable += utxoAmount;
} else {

View file

@ -141,6 +141,7 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
e.isConfirmed(
currentChainHeight,
cryptoCurrency.minConfirms,
cryptoCurrency.minCoinbaseConfirms,
)),
)
.toList();
@ -1920,6 +1921,7 @@ mixin ElectrumXInterface<T extends ElectrumXCurrencyInterface>
e.isConfirmed(
info.cachedChainHeight,
cryptoCurrency.minConfirms,
cryptoCurrency.minCoinbaseConfirms,
),
)
.toList();

View file

@ -925,6 +925,7 @@ mixin LelantusInterface<T extends ElectrumXCurrencyInterface>
if (availableOutputs[i].isConfirmed(
currentChainHeight,
cryptoCurrency.minConfirms,
cryptoCurrency.minCoinbaseConfirms,
) ==
true &&
!(availableOutputs[i].isCoinbase &&

View file

@ -479,6 +479,7 @@ mixin PaynymInterface<T extends PaynymCurrencyInterface>
availableOutputs[i].isConfirmed(
await fetchChainHeight(),
cryptoCurrency.minConfirms,
cryptoCurrency.minCoinbaseConfirms,
) ==
true) {
spendableOutputs.add(availableOutputs[i]);

View file

@ -121,6 +121,7 @@ mixin RbfInterface<T extends ElectrumXCurrencyInterface>
(e) => !e.isConfirmed(
height,
cryptoCurrency.minConfirms,
cryptoCurrency.minCoinbaseConfirms,
),
);

View file

@ -1750,6 +1750,7 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
(e) => !e.isConfirmed(
currentHeight,
cryptoCurrency.minConfirms,
cryptoCurrency.minCoinbaseConfirms,
),
);
@ -1845,7 +1846,11 @@ mixin SparkInterface<T extends ElectrumXCurrencyInterface>
.where(
(e) =>
canCPFP ||
e.isConfirmed(currentHeight, cryptoCurrency.minConfirms),
e.isConfirmed(
currentHeight,
cryptoCurrency.minConfirms,
cryptoCurrency.minCoinbaseConfirms,
),
)
.toList();