Skip to content

Commit

Permalink
Namecoin: Fix RBF batching to not assign multiple names to the same t…
Browse files Browse the repository at this point in the history
…ransaction
  • Loading branch information
JeremyRand committed Nov 23, 2018
1 parent 7f5f80f commit ce7592a
Showing 1 changed file with 23 additions and 3 deletions.
26 changes: 23 additions & 3 deletions electrum_nmc/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ def relayfee(self):
def dust_threshold(self):
return dust_threshold(self.network)

def get_unconfirmed_base_tx_for_batching(self) -> Optional[Transaction]:
def get_unconfirmed_base_tx_for_batching(self, name_identifier=None) -> Optional[Transaction]:
candidate = None
for tx_hash, tx_mined_status, delta, balance in self.get_history():
# tx should not be mined yet
Expand All @@ -569,6 +569,11 @@ def get_unconfirmed_base_tx_for_batching(self) -> Optional[Transaction]:
# all inputs should be is_mine
if not all([self.is_mine(self.get_txin_address(txin)) for txin in tx.inputs()]):
continue
# Namecoin: avoid multiple name outputs in a single transaction,
# but allow replacing a name_anyupdate output with the same
# identifier
if name_identifier is None or any([o.name_op is not None and ("name" not in o.name_op or o.name_op["name"] != name_identifier) for o in tx.outputs()]):
continue
# prefer txns already in mempool (vs local)
if tx_mined_status.height == TX_HEIGHT_LOCAL:
candidate = tx
Expand All @@ -582,6 +587,7 @@ def make_unsigned_transaction(self, coins, outputs, config, fixed_fee=None,
change_addr=None, is_sweep=False, name_inputs=[]):
# check outputs
i_max = None
name_identifier = None
for i, o in enumerate(outputs):
if o.type == TYPE_ADDRESS:
if not is_address(o.address):
Expand All @@ -590,6 +596,10 @@ def make_unsigned_transaction(self, coins, outputs, config, fixed_fee=None,
if i_max is not None:
raise Exception("More than one output set to spend max")
i_max = i
if o.name_op is not None and "name" in o.name_op:
if name_identifier is not None:
raise Exception("More than one name output")
name_identifier = o.name_op["name"]

if fixed_fee is None and config.fee_per_kb() is None:
raise NoDynamicFeeEstimates()
Expand Down Expand Up @@ -633,7 +643,7 @@ def make_unsigned_transaction(self, coins, outputs, config, fixed_fee=None,
max_change = self.max_change_outputs if self.multiple_change else 1
coin_chooser = coinchooser.get_coin_chooser(config)
# If there is an unconfirmed RBF tx, merge with it
base_tx = self.get_unconfirmed_base_tx_for_batching()
base_tx = self.get_unconfirmed_base_tx_for_batching(name_identifier=name_identifier)
if config.get('batch_rbf', False) and base_tx:
is_local = self.get_tx_height(base_tx.txid()).height == TX_HEIGHT_LOCAL
base_tx = Transaction(base_tx.serialize())
Expand All @@ -648,7 +658,17 @@ def fee_estimator(size: int) -> int:
lower_bound = lower_bound if not is_local else 0
return max(lower_bound, original_fee_estimator(size))
txi = base_tx.inputs()
txo = list(filter(lambda o: not self.is_change(o.address), base_tx.outputs()))
# Namecoin: remove any existing name outputs for the same
# identifier, since we'll be replacing them. We already know
# that there aren't any existing name outputs for different
# identifiers, because get_unconfirmed_base_tx_for_batching
# does that check for us.
txo = list(filter(lambda o: not self.is_change(o.address) and o.name_op is None, base_tx.outputs()))

# Namecoin: remove any new name inputs if the existing
# transaction already has a name input.
if any([self.transactions[i["prevout_hash"]].outputs()[i["prevout_n"]].name_op is not None for i in txi]):
name_inputs = list(filter(lambda i: self.transactions[i["prevout_hash"]].outputs()[i["prevout_n"]].name_op is None, name_inputs))
else:
txi = []
txo = []
Expand Down

0 comments on commit ce7592a

Please sign in to comment.