dynamic fees
This commit is contained in:
@@ -396,7 +396,7 @@ class Commands:
|
||||
final_outputs.append(('address', address, amount))
|
||||
|
||||
coins = self.wallet.get_spendable_coins(domain)
|
||||
tx = self.wallet.make_unsigned_transaction(coins, final_outputs, fee, change_addr)
|
||||
tx = self.wallet.make_unsigned_transaction(coins, final_outputs, self.config, fee, change_addr)
|
||||
str(tx) #this serializes
|
||||
if not unsigned:
|
||||
self.wallet.sign_transaction(tx, self.password)
|
||||
|
||||
@@ -235,12 +235,15 @@ class Network(util.DaemonThread):
|
||||
self.interface.send_request({'method':'blockchain.address.subscribe','params':[addr]})
|
||||
self.interface.send_request({'method':'server.banner','params':[]})
|
||||
self.interface.send_request({'method':'server.peers.subscribe','params':[]})
|
||||
self.interface.send_request({'method':'blockchain.estimatefee','params':[2]})
|
||||
|
||||
def get_status_value(self, key):
|
||||
if key == 'status':
|
||||
value = self.connection_status
|
||||
elif key == 'banner':
|
||||
value = self.banner
|
||||
elif key == 'fee':
|
||||
value = self.fee
|
||||
elif key == 'updated':
|
||||
value = (self.get_local_height(), self.get_server_height())
|
||||
elif key == 'servers':
|
||||
@@ -425,6 +428,11 @@ class Network(util.DaemonThread):
|
||||
elif method == 'server.banner':
|
||||
self.banner = result
|
||||
self.notify('banner')
|
||||
elif method == 'blockchain.estimatefee':
|
||||
from bitcoin import COIN
|
||||
self.fee = int(result * COIN)
|
||||
self.print_error("recommended fee", self.fee)
|
||||
self.notify('fee')
|
||||
elif method == 'blockchain.address.subscribe':
|
||||
addr = response.get('params')[0]
|
||||
self.addr_responses[addr] = result
|
||||
|
||||
@@ -62,6 +62,8 @@ class NetworkProxy(util.DaemonThread):
|
||||
self.server_height = 0
|
||||
self.interfaces = []
|
||||
self.jobs = []
|
||||
# value returned by estimatefee
|
||||
self.fee = None
|
||||
|
||||
|
||||
def run(self):
|
||||
@@ -90,6 +92,8 @@ class NetworkProxy(util.DaemonThread):
|
||||
self.status = value
|
||||
elif key == 'banner':
|
||||
self.banner = value
|
||||
elif key == 'fee':
|
||||
self.fee = value
|
||||
elif key == 'updated':
|
||||
self.blockchain_height, self.server_height = value
|
||||
elif key == 'servers':
|
||||
|
||||
@@ -143,6 +143,7 @@ class Abstract_Wallet(object):
|
||||
"""
|
||||
def __init__(self, storage):
|
||||
self.storage = storage
|
||||
self.network = None
|
||||
self.electrum_version = ELECTRUM_VERSION
|
||||
self.gap_limit_for_change = 6 # constant
|
||||
# saved fields
|
||||
@@ -153,9 +154,7 @@ class Abstract_Wallet(object):
|
||||
self.labels = storage.get('labels', {})
|
||||
self.frozen_addresses = set(storage.get('frozen_addresses',[]))
|
||||
self.stored_height = storage.get('stored_height', 0) # last known height (for offline mode)
|
||||
|
||||
self.history = storage.get('addr_history',{}) # address -> list(txid, height)
|
||||
self.fee_per_kb = int(storage.get('fee_per_kb', RECOMMENDED_FEE))
|
||||
|
||||
# This attribute is set when wallet.start_threads is called.
|
||||
self.synchronizer = None
|
||||
@@ -674,10 +673,6 @@ class Abstract_Wallet(object):
|
||||
xx += x
|
||||
return cc, uu, xx
|
||||
|
||||
def set_fee(self, fee, save = True):
|
||||
self.fee_per_kb = fee
|
||||
self.storage.put('fee_per_kb', self.fee_per_kb, save)
|
||||
|
||||
def get_address_history(self, address):
|
||||
with self.lock:
|
||||
return self.history.get(address, [])
|
||||
@@ -873,23 +868,30 @@ class Abstract_Wallet(object):
|
||||
return ', '.join(labels)
|
||||
return ''
|
||||
|
||||
def fee_per_kb(self, config):
|
||||
b = config.get('dynamic_fees')
|
||||
f = config.get('fee_factor', 50)
|
||||
F = config.get('fee_per_kb', bitcoin.RECOMMENDED_FEE)
|
||||
return min(F, self.network.fee*(50 + f)/100) if b and self.network and self.network.fee else F
|
||||
|
||||
def get_tx_fee(self, tx):
|
||||
# this method can be overloaded
|
||||
return tx.get_fee()
|
||||
|
||||
def estimated_fee(self, tx):
|
||||
def estimated_fee(self, tx, fee_per_kb):
|
||||
estimated_size = len(tx.serialize(-1))/2
|
||||
fee = int(self.fee_per_kb*estimated_size/1000.)
|
||||
fee = int(fee_per_kb * estimated_size / 1000.)
|
||||
if fee < MIN_RELAY_TX_FEE: # and tx.requires_fee(self):
|
||||
fee = MIN_RELAY_TX_FEE
|
||||
return fee
|
||||
|
||||
def make_unsigned_transaction(self, coins, outputs, fixed_fee=None, change_addr=None):
|
||||
def make_unsigned_transaction(self, coins, outputs, config, fixed_fee=None, change_addr=None):
|
||||
# check outputs
|
||||
for type, data, value in outputs:
|
||||
if type == 'address':
|
||||
assert is_address(data), "Address " + data + " is invalid!"
|
||||
|
||||
fee_per_kb = self.fee_per_kb(config)
|
||||
amount = sum(map(lambda x:x[2], outputs))
|
||||
total = fee = 0
|
||||
inputs = []
|
||||
@@ -903,7 +905,7 @@ class Abstract_Wallet(object):
|
||||
# no need to estimate fee until we have reached desired amount
|
||||
if total < amount:
|
||||
continue
|
||||
fee = fixed_fee if fixed_fee is not None else self.estimated_fee(tx)
|
||||
fee = fixed_fee if fixed_fee is not None else self.estimated_fee(tx, fee_per_kb)
|
||||
if total >= amount + fee:
|
||||
break
|
||||
else:
|
||||
@@ -914,7 +916,7 @@ class Abstract_Wallet(object):
|
||||
if total - v >= amount + fee:
|
||||
tx.inputs.remove(item)
|
||||
total -= v
|
||||
fee = fixed_fee if fixed_fee is not None else self.estimated_fee(tx)
|
||||
fee = fixed_fee if fixed_fee is not None else self.estimated_fee(tx, fee_per_kb)
|
||||
else:
|
||||
break
|
||||
print_error("using %d inputs"%len(tx.inputs))
|
||||
@@ -943,7 +945,7 @@ class Abstract_Wallet(object):
|
||||
elif change_amount > DUST_THRESHOLD:
|
||||
tx.outputs.append(('address', change_addr, change_amount))
|
||||
# recompute fee including change output
|
||||
fee = self.estimated_fee(tx)
|
||||
fee = self.estimated_fee(tx, fee_per_kb)
|
||||
# remove change output
|
||||
tx.outputs.pop()
|
||||
# if change is still above dust threshold, re-add change output.
|
||||
@@ -962,9 +964,9 @@ class Abstract_Wallet(object):
|
||||
run_hook('make_unsigned_transaction', tx)
|
||||
return tx
|
||||
|
||||
def mktx(self, outputs, password, fee=None, change_addr=None, domain=None):
|
||||
def mktx(self, outputs, password, config, fee=None, change_addr=None, domain=None):
|
||||
coins = self.get_spendable_coins(domain)
|
||||
tx = self.make_unsigned_transaction(coins, outputs, fee, change_addr)
|
||||
tx = self.make_unsigned_transaction(coins, outputs, config, fee, change_addr)
|
||||
self.sign_transaction(tx, password)
|
||||
return tx
|
||||
|
||||
|
||||
Reference in New Issue
Block a user