electrum-personal-server

Maximally lightweight electrum server for a single user
git clone https://git.parazyd.org/electrum-personal-server
Log | Files | Refs | README

commit dcdb893dff33d36bda7dbd2778100a3df8dcf06f
parent 169fac9130b38230f141123ca51ebb2cef69fcad
Author: chris-belcher <chris-belcher@users.noreply.github.com>
Date:   Tue, 23 Feb 2021 23:09:47 +0000

Rewrite unconf tx handler to use getmempoolentry

Previously when handling an unconfirmed transaction the code
would use an ugly cascade of calls to `getrawtransaction`
and `gettxout` in an effort to learn whether an unconfirmed
had unconfirmed parents, and what its miner fee was.

This is replaced by using the `getmempoolentry` RPC call which
is much cleaner and shorter.

Update tests

Diffstat:
Melectrumpersonalserver/server/transactionmonitor.py | 51++++++++++++++++++---------------------------------
Mtest/test_transactionmonitor.py | 10+++++++++-
2 files changed, 27 insertions(+), 34 deletions(-)

diff --git a/electrumpersonalserver/server/transactionmonitor.py b/electrumpersonalserver/server/transactionmonitor.py @@ -147,6 +147,9 @@ class TransactionMonitor(object): input_scripthashes)) if len(sh_to_add) == 0: continue + new_history_element = self.generate_new_history_element(tx, txd) + if new_history_element == None: + continue for wal in self.deterministic_wallets: overrun_depths = wal.have_scriptpubkeys_overrun_gaplimit( @@ -160,7 +163,6 @@ class TransactionMonitor(object): # check whether all initial_import_count addresses are # imported rather than just the first one return False - new_history_element = self.generate_new_history_element(tx, txd) for scripthash in sh_to_add: address_history[scripthash][ "history"].append(new_history_element) @@ -217,37 +219,16 @@ class TransactionMonitor(object): def generate_new_history_element(self, tx, txd): logger = self.logger if tx["confirmations"] == 0: - unconfirmed_input = False - total_input_value = 0 - for inn in txd["vin"]: - try: - utxo = self.rpc.call("gettxout", [inn["txid"], - inn["vout"], True]) - if utxo is None: - utxo = self.rpc.call("gettxout", [inn["txid"], - inn["vout"], False]) - if utxo is None: - rawtx = self.rpc.call("getrawtransaction", - [inn["txid"], True]) - if rawtx is not None: - utxo = {"confirmations": 0, - "value": rawtx["vout"][ - inn["vout"]]["value"]} - except JsonRpcError: - #error somewhere, unable to get input value, just carry on - pass - if utxo is not None: - total_input_value += int(Decimal(utxo["value"]) * - Decimal(1e8)) - unconfirmed_input = (unconfirmed_input or - utxo["confirmations"] == 0) - else: - # Electrum will now display a weird negative fee - logger.warning("input utxo not found(!)") - - logger.debug("total_input_value = " + str(total_input_value)) - fee = total_input_value - sum([int(Decimal(out["value"]) - * Decimal(1e8)) for out in txd["vout"]]) + try: + mempool_tx = self.rpc.call("getmempoolentry", [tx["txid"]]) + fee = int(Decimal(str(mempool_tx["fees"]["base"])) + * Decimal(1e8)) + unconfirmed_input = mempool_tx["ancestorcount"] > 1 + except JsonRpcError as e: + #not in mempool, return None + logger.debug("txid in wallet but not in mempool = " + + tx["txid"]) + return None height = -1 if unconfirmed_input else 0 new_history_element = ({"tx_hash": tx["txid"], "height": height, "fee": fee}) @@ -318,6 +299,8 @@ class TransactionMonitor(object): txd = self.rpc.call("decoderawtransaction", [tx["hex"]]) new_history_element = self.generate_new_history_element( tx, txd) + if new_history_element == None: + continue for scrhash in scrhashes: self.address_history[scrhash]["history"].append( new_history_element) @@ -455,6 +438,9 @@ class TransactionMonitor(object): matching_scripthashes.append(scripthash) if len(matching_scripthashes) == 0: continue + new_history_element = self.generate_new_history_element(tx, txd) + if new_history_element == None: + continue for wal in self.deterministic_wallets: overrun_depths = wal.have_scriptpubkeys_overrun_gaplimit( @@ -471,7 +457,6 @@ class TransactionMonitor(object): import_addresses(self.rpc, new_addrs, [], -1, 0, logger) updated_scripthashes.extend(matching_scripthashes) - new_history_element = self.generate_new_history_element(tx, txd) logger.info("Found new tx: " + str(new_history_element)) for scrhash in matching_scripthashes: self.address_history[scrhash]["history"].append( diff --git a/test/test_transactionmonitor.py b/test/test_transactionmonitor.py @@ -55,8 +55,16 @@ class DummyJsonRpc(object): return {"addresses": [dummy_spk_to_address(params[0])]} elif method == "importaddress": self.imported_addresses.append(params[0]) + elif method == "getmempoolentry": + for t in self.txlist: + if t["txid"] == params[0]: + return {"fees": {"base": 0}, + "ancestorcount": + 1 if t["vin"][0]["confirmations"] > 0 else 2} + logger.debug(params[0]) + assert 0 else: - raise ValueError("unknown method in dummy jsonrpc") + raise ValueError("unknown method in dummy jsonrpc " + method) def add_transaction(self, tx): self.txlist = [tx] + self.txlist