1
0

Track tx size directly; calculate fees from that

This has several advantages.  Fee calculation is now very fast,
as we don't need to keep reserializing the tx.  Another is that
we can reason about the fees after adding a change output without
having to add it, recalculate the tx fee, and remove it again.
This commit is contained in:
Neil Booth
2015-11-29 13:29:33 +09:00
parent a4dd5acc48
commit 93bb09230c
3 changed files with 48 additions and 52 deletions

View File

@@ -33,60 +33,42 @@ class CoinChooser(PrintError):
amount = sum(map(lambda x: x[2], outputs))
total = 0
tx = Transaction.from_io([], outputs)
fee = fee_estimator(tx)
# Size of the transaction with no inputs and no change
base_size = tx.estimated_size()
# Pay to bitcoin address serializes as 34 bytes
change_size = 34
# Size of each serialized coin
for coin in coins:
coin['size'] = Transaction.estimated_input_size(coin)
size = base_size
# add inputs, sorted by age
for item in coins:
v = item.get('value')
total += v
size += item['size']
tx.add_input(item)
# no need to estimate fee until we have reached desired amount
if total < amount + fee:
continue
fee = fee_estimator(tx)
if total >= amount + fee:
if total >= amount + fee_estimator(size):
break
else:
raise NotEnoughFunds()
# remove unneeded inputs.
removed = False
for item in sorted(tx.inputs, key=itemgetter('value')):
v = item.get('value')
if total - v >= amount + fee:
if total - v >= amount + fee_estimator(size - item['size']):
tx.inputs.remove(item)
total -= v
removed = True
continue
else:
break
if removed:
fee = fee_estimator(tx)
for item in sorted(tx.inputs, key=itemgetter('value')):
v = item.get('value')
if total - v >= amount + fee:
tx.inputs.remove(item)
total -= v
fee = fee_estimator(tx)
continue
break
size -= item['size']
self.print_error("using %d inputs" % len(tx.inputs))
# if change is above dust threshold, add a change output.
change_addr = change_addrs[0]
change_amount = total - (amount + fee)
# See if change would still be greater than dust after adding
# the change to the transaction
# If change is above dust threshold after accounting for the
# size of the change output, add it to the transaction.
change_amount = total - (amount + fee_estimator(size + change_size))
if change_amount > dust_threshold:
tx.outputs.append(('address', change_addr, change_amount))
fee = fee_estimator(tx)
# remove change output
tx.outputs.pop()
change_amount = total - (amount + fee)
# If change is still above dust threshold, keep the change.
if change_amount > dust_threshold:
tx.outputs.append(('address', change_addr, change_amount))
tx.outputs.append(('address', change_addrs[0], change_amount))
size += change_size
self.print_error('change', change_amount)
elif change_amount:
self.print_error('not keeping dust', change_amount)