mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
fix: sanitize Electrum fee estimates (#3499)
* fix: sanitize Electrum fee estimates * chore: drop test changes from this PR Removes the test additions/modifications introduced by this branch so the test design is left to the maintainers. Production code is unchanged.
This commit is contained in:
@@ -579,14 +579,29 @@ class ElectrumClient {
|
||||
return [];
|
||||
});
|
||||
|
||||
// Floor at 0 so unavailable/-1 estimates never become negative rates;
|
||||
// cap at 2000 sat/vB per CW-1597.
|
||||
static const int _maxFeeRate = 2000;
|
||||
|
||||
static int _sanitizeFeeRate(double feeRate) {
|
||||
final rate = (stringDoubleToBitcoinAmount(feeRate.toString()) / 1000).round();
|
||||
if (rate < 0) {
|
||||
return 0;
|
||||
}
|
||||
if (rate > _maxFeeRate) {
|
||||
return _maxFeeRate;
|
||||
}
|
||||
return rate;
|
||||
}
|
||||
|
||||
Future<List<int>> feeRates({BasedUtxoNetwork? network}) async {
|
||||
try {
|
||||
final topDoubleString = await estimatefee(p: 1);
|
||||
final middleDoubleString = await estimatefee(p: 5);
|
||||
final bottomDoubleString = await estimatefee(p: 10);
|
||||
final top = (stringDoubleToBitcoinAmount(topDoubleString.toString()) / 1000).round();
|
||||
final middle = (stringDoubleToBitcoinAmount(middleDoubleString.toString()) / 1000).round();
|
||||
final bottom = (stringDoubleToBitcoinAmount(bottomDoubleString.toString()) / 1000).round();
|
||||
final topDouble = await estimatefee(p: 1);
|
||||
final middleDouble = await estimatefee(p: 5);
|
||||
final bottomDouble = await estimatefee(p: 10);
|
||||
final top = _sanitizeFeeRate(topDouble);
|
||||
final middle = _sanitizeFeeRate(middleDouble);
|
||||
final bottom = _sanitizeFeeRate(bottomDouble);
|
||||
|
||||
return [bottom, middle, top];
|
||||
} catch (_) {
|
||||
|
||||
@@ -750,6 +750,9 @@ abstract class ElectrumWalletBase
|
||||
}
|
||||
}
|
||||
|
||||
static bool _isValidFeeRates(List<int> feeRates) =>
|
||||
feeRates.length == 3 && feeRates.every((rate) => rate > 0);
|
||||
|
||||
@action
|
||||
Future<void> updateFeeRates() async {
|
||||
if (await checkIfMempoolAPIIsEnabled() && type == WalletType.bitcoin) {
|
||||
@@ -776,7 +779,7 @@ abstract class ElectrumWalletBase
|
||||
}
|
||||
|
||||
final feeRates = await electrumClient.feeRates(network: network);
|
||||
if (feeRates != [0, 0, 0]) {
|
||||
if (_isValidFeeRates(feeRates)) {
|
||||
_feeRates = feeRates;
|
||||
} else if (isTestnet) {
|
||||
_feeRates = [1, 1, 1];
|
||||
@@ -1024,7 +1027,7 @@ abstract class ElectrumWalletBase
|
||||
vinOutpoints: utxoDetails.vinOutpoints,
|
||||
);
|
||||
|
||||
if (fee == 0) {
|
||||
if (fee <= 0) {
|
||||
throw BitcoinTransactionNoFeeException();
|
||||
}
|
||||
|
||||
@@ -1205,7 +1208,7 @@ abstract class ElectrumWalletBase
|
||||
));
|
||||
}
|
||||
|
||||
if (fee == 0) {
|
||||
if (fee <= 0) {
|
||||
throw BitcoinTransactionNoFeeException();
|
||||
}
|
||||
|
||||
@@ -2513,16 +2516,16 @@ abstract class ElectrumWalletBase
|
||||
Map<String, ElectrumTransactionInfo> historiesWithDetails,
|
||||
BitcoinAddressType type,
|
||||
) async {
|
||||
|
||||
final addressesByType =
|
||||
walletAddresses.allAddresses.where((addr) => addr.type == type).toList();
|
||||
walletAddresses.allAddresses.where((addr) => addr.type == type).toList();
|
||||
|
||||
final receiveStandard = getAddressBranchByType(hidden: false, legacy: false, type: type);
|
||||
final changeStandard = getAddressBranchByType(hidden: true, legacy: false, type: type);
|
||||
final changeStandard = getAddressBranchByType(hidden: true, legacy: false, type: type);
|
||||
final receiveLegacy = getAddressBranchByType(hidden: false, legacy: true, type: type);
|
||||
final changeLegacy = getAddressBranchByType(hidden: true, legacy: true, type: type);
|
||||
final changeLegacy = getAddressBranchByType(hidden: true, legacy: true, type: type);
|
||||
|
||||
walletAddresses.hiddenAddresses.addAll([...changeStandard, ...changeLegacy].map((e) => e.address));
|
||||
walletAddresses.hiddenAddresses
|
||||
.addAll([...changeStandard, ...changeLegacy].map((e) => e.address));
|
||||
await walletAddresses.saveAddressesInBox();
|
||||
await Future.wait(addressesByType.map((addressRecord) async {
|
||||
final history = await _fetchAddressHistory(addressRecord, await getCurrentChainTip());
|
||||
@@ -2637,13 +2640,13 @@ abstract class ElectrumWalletBase
|
||||
|
||||
Future<void> fetchTransactionsForAddressTypeBatch(
|
||||
Map<String, ElectrumTransactionInfo> historiesWithDetails, BitcoinAddressType type) async {
|
||||
|
||||
final receiveStandard = getAddressBranchByType(hidden: false, legacy: false, type: type);
|
||||
final changeStandard = getAddressBranchByType(hidden: true, legacy: false, type: type);
|
||||
final changeStandard = getAddressBranchByType(hidden: true, legacy: false, type: type);
|
||||
final receiveLegacy = getAddressBranchByType(hidden: false, legacy: true, type: type);
|
||||
final changeLegacy = getAddressBranchByType(hidden: true, legacy: true, type: type);
|
||||
final changeLegacy = getAddressBranchByType(hidden: true, legacy: true, type: type);
|
||||
|
||||
walletAddresses.hiddenAddresses.addAll([...changeStandard, ...changeLegacy].map((e) => e.address));
|
||||
walletAddresses.hiddenAddresses
|
||||
.addAll([...changeStandard, ...changeLegacy].map((e) => e.address));
|
||||
await walletAddresses.saveAddressesInBox();
|
||||
|
||||
await fetchTransactionsForAddressesBranchBatch(
|
||||
@@ -2665,7 +2668,7 @@ abstract class ElectrumWalletBase
|
||||
await fetchTransactionsForAddressesBranchBatch(
|
||||
historiesWithDetails,
|
||||
type,
|
||||
receiveLegacy,
|
||||
receiveLegacy,
|
||||
isHidden: false,
|
||||
isLegacyDerivation: true,
|
||||
);
|
||||
@@ -2749,10 +2752,13 @@ abstract class ElectrumWalletBase
|
||||
}
|
||||
}
|
||||
|
||||
List<BitcoinAddressRecord> getAddressBranchByType({required bool hidden, required bool legacy, required BitcoinAddressType
|
||||
type}) => walletAddresses.allAddresses.where((addr) => addr.type == type && addr.isHidden == hidden && addr.isLegacyDerivation == legacy)
|
||||
.toList()
|
||||
..sort((a, b) => a.index.compareTo(b.index));
|
||||
List<BitcoinAddressRecord> getAddressBranchByType(
|
||||
{required bool hidden, required bool legacy, required BitcoinAddressType type}) =>
|
||||
walletAddresses.allAddresses
|
||||
.where((addr) =>
|
||||
addr.type == type && addr.isHidden == hidden && addr.isLegacyDerivation == legacy)
|
||||
.toList()
|
||||
..sort((a, b) => a.index.compareTo(b.index));
|
||||
|
||||
int _highestUsedIndex(List<BitcoinAddressRecord> addresses) {
|
||||
for (int i = addresses.length - 1; i >= 0; i--) {
|
||||
|
||||
Reference in New Issue
Block a user