commit a98e833897dc11ec7ff8c854fe512a3fc549f6c7
parent 2cc15fca578b0ee6e87c1429d8afeedc4a7e156a
Author: ghost43 <somber.night@protonmail.com>
Date: Fri, 15 Jun 2018 17:02:44 +0200
getfeerate command: add optional parameters to specify custom fee level (#4264)
Diffstat:
3 files changed, 59 insertions(+), 14 deletions(-)
diff --git a/lib/commands.py b/lib/commands.py
@@ -657,10 +657,23 @@ class Commands:
return self.wallet.is_up_to_date()
@command('n')
- def getfeerate(self):
- """Return current optimal fee rate per kilobyte, according
- to config settings (static/dynamic)"""
- return self.config.fee_per_kb()
+ def getfeerate(self, fee_method=None, fee_level=None):
+ """Return current suggested fee rate (in sat/kvByte), according to config
+ settings or supplied parameters.
+ """
+ if fee_method is None:
+ dyn, mempool = None, None
+ elif fee_method.lower() == 'static':
+ dyn, mempool = False, False
+ elif fee_method.lower() == 'eta':
+ dyn, mempool = True, False
+ elif fee_method.lower() == 'mempool':
+ dyn, mempool = True, True
+ else:
+ raise Exception('Invalid fee estimation method: {}'.format(fee_method))
+ if fee_level is not None:
+ fee_level = Decimal(fee_level)
+ return self.config.fee_per_kb(dyn=dyn, mempool=mempool, fee_level=fee_level)
@command('')
def help(self):
@@ -719,6 +732,8 @@ command_options = {
'show_addresses': (None, "Show input and output addresses"),
'show_fiat': (None, "Show fiat value of transactions"),
'year': (None, "Show history for a given year"),
+ 'fee_method': (None, "Fee estimation method to use"),
+ 'fee_level': (None, "Float between 0.0 and 1.0, representing fee slider position")
}
@@ -738,6 +753,8 @@ arg_types = {
'fee': lambda x: str(Decimal(x)) if x is not None else None,
'amount': lambda x: str(Decimal(x)) if x != '!' else '!',
'locktime': int,
+ 'fee_method': str,
+ 'fee_level': json_loads,
}
config_variables = {
diff --git a/lib/daemon.py b/lib/daemon.py
@@ -260,7 +260,9 @@ class Daemon(DaemonThread):
password = config_options.get('password')
new_password = config_options.get('new_password')
config = SimpleConfig(config_options)
+ # FIXME this is ugly...
config.fee_estimates = self.network.config.fee_estimates.copy()
+ config.mempool_fees = self.network.config.mempool_fees.copy()
cmdname = config.get('cmd')
cmd = known_commands[cmdname]
if cmd.requires_wallet:
diff --git a/lib/simple_config.py b/lib/simple_config.py
@@ -4,6 +4,7 @@ import time
import os
import stat
from decimal import Decimal
+from typing import Union
from copy import deepcopy
@@ -278,16 +279,18 @@ class SimpleConfig(PrintError):
return get_fee_within_limits
@impose_hard_limits_on_fee
- def eta_to_fee(self, i):
+ def eta_to_fee(self, slider_pos) -> Union[int, None]:
"""Returns fee in sat/kbyte."""
- if i < 4:
- j = FEE_ETA_TARGETS[i]
- fee = self.fee_estimates.get(j)
+ slider_pos = max(slider_pos, 0)
+ slider_pos = min(slider_pos, len(FEE_ETA_TARGETS))
+ if slider_pos < len(FEE_ETA_TARGETS):
+ target_blocks = FEE_ETA_TARGETS[slider_pos]
+ fee = self.fee_estimates.get(target_blocks)
else:
- assert i == 4
fee = self.fee_estimates.get(2)
if fee is not None:
fee += fee/2
+ fee = int(fee)
return fee
def fee_to_depth(self, target_fee):
@@ -301,9 +304,9 @@ class SimpleConfig(PrintError):
return depth
@impose_hard_limits_on_fee
- def depth_to_fee(self, i):
+ def depth_to_fee(self, slider_pos) -> int:
"""Returns fee in sat/kbyte."""
- target = self.depth_target(i)
+ target = self.depth_target(slider_pos)
depth = 0
for fee, s in self.mempool_fees:
depth += s
@@ -313,8 +316,10 @@ class SimpleConfig(PrintError):
return 0
return fee * 1000
- def depth_target(self, i):
- return FEE_DEPTH_TARGETS[i]
+ def depth_target(self, slider_pos):
+ slider_pos = max(slider_pos, 0)
+ slider_pos = min(slider_pos, len(FEE_DEPTH_TARGETS)-1)
+ return FEE_DEPTH_TARGETS[slider_pos]
def eta_target(self, i):
if i == len(FEE_ETA_TARGETS):
@@ -430,14 +435,35 @@ class SimpleConfig(PrintError):
def use_mempool_fees(self):
return bool(self.get('mempool_fees', False))
- def fee_per_kb(self, dyn=None, mempool=None):
+ def _feerate_from_fractional_slider_position(self, fee_level: float, dyn: bool,
+ mempool: bool) -> Union[int, None]:
+ fee_level = max(fee_level, 0)
+ fee_level = min(fee_level, 1)
+ if dyn:
+ max_pos = (len(FEE_DEPTH_TARGETS) - 1) if mempool else len(FEE_ETA_TARGETS)
+ slider_pos = round(fee_level * max_pos)
+ fee_rate = self.depth_to_fee(slider_pos) if mempool else self.eta_to_fee(slider_pos)
+ else:
+ max_pos = len(FEERATE_STATIC_VALUES) - 1
+ slider_pos = round(fee_level * max_pos)
+ fee_rate = FEERATE_STATIC_VALUES[slider_pos]
+ return fee_rate
+
+ def fee_per_kb(self, dyn: bool=None, mempool: bool=None, fee_level: float=None) -> Union[int, None]:
"""Returns sat/kvB fee to pay for a txn.
Note: might return None.
+
+ fee_level: float between 0.0 and 1.0, representing fee slider position
"""
if dyn is None:
dyn = self.is_dynfee()
if mempool is None:
mempool = self.use_mempool_fees()
+ if fee_level is not None:
+ return self._feerate_from_fractional_slider_position(fee_level, dyn, mempool)
+ # there is no fee_level specified; will use config.
+ # note: 'depth_level' and 'fee_level' in config are integer slider positions,
+ # unlike fee_level here, which (when given) is a float in [0.0, 1.0]
if dyn:
if mempool:
fee_rate = self.depth_to_fee(self.get_depth_level())