Merge pull request #10096 from SomberNight/202508_lower_minrelayfee
change minrelayfee clamps from `[1, 50]` to `[0.1, 50]` sat/vbyte
This commit is contained in:
@@ -325,14 +325,14 @@ def construct_script(
|
|||||||
|
|
||||||
def relayfee(network: 'Network' = None) -> int:
|
def relayfee(network: 'Network' = None) -> int:
|
||||||
"""Returns feerate in sat/kbyte."""
|
"""Returns feerate in sat/kbyte."""
|
||||||
from .fee_policy import FEERATE_DEFAULT_RELAY, FEERATE_MAX_RELAY
|
from .fee_policy import FEERATE_MIN_RELAY, FEERATE_DEFAULT_RELAY, FEERATE_MAX_RELAY
|
||||||
if network and network.relay_fee is not None:
|
if network and network.relay_fee is not None:
|
||||||
fee = network.relay_fee
|
fee = network.relay_fee
|
||||||
else:
|
else:
|
||||||
fee = FEERATE_DEFAULT_RELAY
|
fee = FEERATE_DEFAULT_RELAY
|
||||||
# sanity safeguards, as network.relay_fee is coming from a server:
|
# sanity safeguards, as network.relay_fee is coming from a server:
|
||||||
fee = min(fee, FEERATE_MAX_RELAY)
|
fee = min(fee, FEERATE_MAX_RELAY)
|
||||||
fee = max(fee, FEERATE_DEFAULT_RELAY)
|
fee = max(fee, FEERATE_MIN_RELAY)
|
||||||
return fee
|
return fee
|
||||||
|
|
||||||
|
|
||||||
@@ -347,10 +347,7 @@ DUST_LIMIT_P2WPKH = 294
|
|||||||
|
|
||||||
def dust_threshold(network: 'Network' = None) -> int:
|
def dust_threshold(network: 'Network' = None) -> int:
|
||||||
"""Returns the dust limit in satoshis."""
|
"""Returns the dust limit in satoshis."""
|
||||||
# Change <= dust threshold is added to the tx fee
|
return DUST_LIMIT_P2PKH
|
||||||
dust_lim = 182 * 3 * relayfee(network) # in msat
|
|
||||||
# convert to sat, but round up:
|
|
||||||
return (dust_lim // 1000) + (dust_lim % 1000 > 0)
|
|
||||||
|
|
||||||
|
|
||||||
def hash_encode(x: bytes) -> str:
|
def hash_encode(x: bytes) -> str:
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from typing import Optional, Sequence, Tuple, Union, TYPE_CHECKING, Dict
|
|||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from numbers import Real
|
from numbers import Real
|
||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
|
import math
|
||||||
|
|
||||||
from .i18n import _
|
from .i18n import _
|
||||||
from .util import NoDynamicFeeEstimates, quantize_feerate, format_fee_satoshis
|
from .util import NoDynamicFeeEstimates, quantize_feerate, format_fee_satoshis
|
||||||
@@ -24,8 +25,10 @@ FEERATE_MAX_DYNAMIC = 1500000
|
|||||||
FEERATE_WARNING_HIGH_FEE = 600000
|
FEERATE_WARNING_HIGH_FEE = 600000
|
||||||
FEERATE_FALLBACK_STATIC_FEE = 150000
|
FEERATE_FALLBACK_STATIC_FEE = 150000
|
||||||
FEERATE_REGTEST_STATIC_FEE = FEERATE_FALLBACK_STATIC_FEE # hardcoded fee used on regtest
|
FEERATE_REGTEST_STATIC_FEE = FEERATE_FALLBACK_STATIC_FEE # hardcoded fee used on regtest
|
||||||
FEERATE_DEFAULT_RELAY = 1000
|
FEERATE_MIN_RELAY = 100
|
||||||
|
FEERATE_DEFAULT_RELAY = 1000 # conservative "min relay fee"
|
||||||
FEERATE_MAX_RELAY = 50000
|
FEERATE_MAX_RELAY = 50000
|
||||||
|
assert FEERATE_MIN_RELAY <= FEERATE_DEFAULT_RELAY <= FEERATE_MAX_RELAY
|
||||||
|
|
||||||
# warn user if fee/amount for on-chain tx is higher than this
|
# warn user if fee/amount for on-chain tx is higher than this
|
||||||
FEE_RATIO_HIGH_WARNING = 0.05
|
FEE_RATIO_HIGH_WARNING = 0.05
|
||||||
@@ -257,11 +260,15 @@ class FeePolicy(Logger):
|
|||||||
else:
|
else:
|
||||||
raise NoDynamicFeeEstimates()
|
raise NoDynamicFeeEstimates()
|
||||||
|
|
||||||
return self.estimate_fee_for_feerate(fee_per_kb, size)
|
return self.estimate_fee_for_feerate(fee_per_kb=fee_per_kb, size=size)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def estimate_fee_for_feerate(cls, fee_per_kb: Union[int, float, Decimal],
|
def estimate_fee_for_feerate(
|
||||||
size: Union[int, float, Decimal]) -> int:
|
cls,
|
||||||
|
*,
|
||||||
|
fee_per_kb: Union[int, float, Decimal],
|
||||||
|
size: Union[int, float, Decimal],
|
||||||
|
) -> int:
|
||||||
# note: 'size' is in vbytes
|
# note: 'size' is in vbytes
|
||||||
size = Decimal(size)
|
size = Decimal(size)
|
||||||
fee_per_kb = Decimal(fee_per_kb)
|
fee_per_kb = Decimal(fee_per_kb)
|
||||||
@@ -269,7 +276,7 @@ class FeePolicy(Logger):
|
|||||||
# to be consistent with what is displayed in the GUI,
|
# to be consistent with what is displayed in the GUI,
|
||||||
# the calculation needs to use the same precision:
|
# the calculation needs to use the same precision:
|
||||||
fee_per_byte = quantize_feerate(fee_per_byte)
|
fee_per_byte = quantize_feerate(fee_per_byte)
|
||||||
return round(fee_per_byte * size)
|
return math.ceil(fee_per_byte * size)
|
||||||
|
|
||||||
|
|
||||||
class FixedFeePolicy(FeePolicy):
|
class FixedFeePolicy(FeePolicy):
|
||||||
@@ -283,6 +290,8 @@ def impose_hard_limits_on_fee(func):
|
|||||||
if fee is None:
|
if fee is None:
|
||||||
return fee
|
return fee
|
||||||
fee = min(FEERATE_MAX_DYNAMIC, fee)
|
fee = min(FEERATE_MAX_DYNAMIC, fee)
|
||||||
|
# Clamp dynamic feerates with conservative min relay fee,
|
||||||
|
# to ensure txs propagate well:
|
||||||
fee = max(FEERATE_DEFAULT_RELAY, fee)
|
fee = max(FEERATE_DEFAULT_RELAY, fee)
|
||||||
return fee
|
return fee
|
||||||
return get_fee_within_limits
|
return get_fee_within_limits
|
||||||
@@ -354,7 +363,7 @@ class FeeHistogram:
|
|||||||
slot = min(item[1], bytes_limit-bytes_current)
|
slot = min(item[1], bytes_limit-bytes_current)
|
||||||
bytes_current += slot
|
bytes_current += slot
|
||||||
capped_histogram.append([
|
capped_histogram.append([
|
||||||
max(FEERATE_DEFAULT_RELAY/1000, item[0]), # clamped to [FEERATE_DEFAULT_RELAY/1000,inf[
|
max(FEERATE_MIN_RELAY/1000, item[0]), # clamped to [FEERATE_MIN_RELAY/1000,inf[
|
||||||
slot, # width of bucket
|
slot, # width of bucket
|
||||||
bytes_current, # cumulative depth at far end of bucket
|
bytes_current, # cumulative depth at far end of bucket
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -888,11 +888,13 @@ class QETxCpfpFeeBumper(TxFeeSlider, TxMonMixin):
|
|||||||
def get_child_fee_from_total_feerate(self, fee_per_kb: Optional[int]) -> Optional[int]:
|
def get_child_fee_from_total_feerate(self, fee_per_kb: Optional[int]) -> Optional[int]:
|
||||||
if fee_per_kb is None:
|
if fee_per_kb is None:
|
||||||
return None
|
return None
|
||||||
fee = fee_per_kb * self._total_size / 1000 - self._parent_fee
|
package_fee = FeePolicy.estimate_fee_for_feerate(fee_per_kb=fee_per_kb, size=self._total_size)
|
||||||
fee = round(fee)
|
child_fee = package_fee - self._parent_fee
|
||||||
fee = min(self._max_fee, fee)
|
child_fee = min(self._max_fee, child_fee)
|
||||||
fee = max(self._total_size, fee) # pay at least 1 sat/byte for combined size
|
# pay at least minrelayfee for combined size:
|
||||||
return fee
|
min_child_fee = FeePolicy.estimate_fee_for_feerate(fee_per_kb=self._wallet.wallet.relayfee(), size=self._total_size)
|
||||||
|
child_fee = max(min_child_fee, child_fee)
|
||||||
|
return child_fee
|
||||||
|
|
||||||
def tx_verified(self):
|
def tx_verified(self):
|
||||||
self._valid = False
|
self._valid = False
|
||||||
|
|||||||
@@ -350,7 +350,10 @@ class TxEditor(WindowModalDialog):
|
|||||||
# fallback to actual fee
|
# fallback to actual fee
|
||||||
displayed_feerate = quantize_feerate(fee / size) if fee is not None else None
|
displayed_feerate = quantize_feerate(fee / size) if fee is not None else None
|
||||||
self.feerate_e.setAmount(displayed_feerate)
|
self.feerate_e.setAmount(displayed_feerate)
|
||||||
displayed_fee = round(displayed_feerate * size) if displayed_feerate is not None else None
|
if displayed_feerate is not None:
|
||||||
|
displayed_fee = FeePolicy.estimate_fee_for_feerate(fee_per_kb=displayed_feerate * 1000, size=size)
|
||||||
|
else:
|
||||||
|
displayed_fee = None
|
||||||
self.fee_e.setAmount(displayed_fee)
|
self.fee_e.setAmount(displayed_fee)
|
||||||
else:
|
else:
|
||||||
if freeze_fee:
|
if freeze_fee:
|
||||||
|
|||||||
@@ -2874,11 +2874,13 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
|
|||||||
def get_child_fee_from_total_feerate(fee_per_kb: Optional[int]) -> Optional[int]:
|
def get_child_fee_from_total_feerate(fee_per_kb: Optional[int]) -> Optional[int]:
|
||||||
if fee_per_kb is None:
|
if fee_per_kb is None:
|
||||||
return None
|
return None
|
||||||
fee = fee_per_kb * total_size / 1000 - parent_fee
|
package_fee = FeePolicy.estimate_fee_for_feerate(fee_per_kb=fee_per_kb, size=total_size)
|
||||||
fee = round(fee)
|
child_fee = package_fee - parent_fee
|
||||||
fee = min(max_fee, fee)
|
child_fee = min(max_fee, child_fee)
|
||||||
fee = max(total_size, fee) # pay at least 1 sat/byte for combined size
|
# pay at least minrelayfee for combined size:
|
||||||
return fee
|
min_child_fee = FeePolicy.estimate_fee_for_feerate(fee_per_kb=self.wallet.relayfee(), size=total_size)
|
||||||
|
child_fee = max(min_child_fee, child_fee)
|
||||||
|
return child_fee
|
||||||
fee_policy = FeePolicy(self.config.FEE_POLICY)
|
fee_policy = FeePolicy(self.config.FEE_POLICY)
|
||||||
suggested_feerate = fee_policy.fee_per_kb(self.network)
|
suggested_feerate = fee_policy.fee_per_kb(self.network)
|
||||||
fee = get_child_fee_from_total_feerate(suggested_feerate)
|
fee = get_child_fee_from_total_feerate(suggested_feerate)
|
||||||
|
|||||||
Reference in New Issue
Block a user