diff --git a/src/cryptoadvance/specter/device.py b/src/cryptoadvance/specter/device.py index ca950b014b..d174d39bb0 100644 --- a/src/cryptoadvance/specter/device.py +++ b/src/cryptoadvance/specter/device.py @@ -4,7 +4,7 @@ import logging from .helpers import is_testnet -logger = logging.getLogger() +logger = logging.getLogger(__name__) class Device: diff --git a/src/cryptoadvance/specter/helpers.py b/src/cryptoadvance/specter/helpers.py index 1231ca47c1..a380f1b24c 100644 --- a/src/cryptoadvance/specter/helpers.py +++ b/src/cryptoadvance/specter/helpers.py @@ -322,3 +322,13 @@ def is_ip_private(ip): or priv_16.match(ip) ) return res is not None + + +def get_address_from_dict(data_dict): + # TODO: Remove this helper function in favor of simple ["address"] + # when support for Bitcoin Core version < 22 is dropped + return ( + data_dict["addresses"][0] + if "addresses" in data_dict and data_dict["addresses"][0] + else data_dict["address"] + ) diff --git a/src/cryptoadvance/specter/managers/wallet_manager.py b/src/cryptoadvance/specter/managers/wallet_manager.py index 6f99e01c49..6e569918b1 100644 --- a/src/cryptoadvance/specter/managers/wallet_manager.py +++ b/src/cryptoadvance/specter/managers/wallet_manager.py @@ -31,7 +31,7 @@ from embit.liquid.descriptor import LDescriptor from embit.descriptor.checksum import add_checksum -logger = logging.getLogger() +logger = logging.getLogger(__name__) purposes = OrderedDict( { @@ -129,7 +129,7 @@ def update(self, data_folder=None, rpc=None, chain=None): self._update(data_folder, rpc, chain) else: self.is_loading = False - logging.info( + logger.info( "Specter seems to be disconnected from Bitcoin Core. Skipping wallets update." ) @@ -141,20 +141,20 @@ def _update(self, data_folder=None, rpc=None, chain=None): try: if self.wallets_update_list: loaded_wallets = self.rpc.listwallets() - logger.debug("Getting loaded wallets list from Bitcoin Core") + logger.info("Getting loaded wallets list from Bitcoin Core") for wallet in self.wallets_update_list: wallet_alias = self.wallets_update_list[wallet]["alias"] wallet_name = self.wallets_update_list[wallet]["name"] if os.path.join(self.rpc_path, wallet_alias) not in loaded_wallets: try: - logger.debug( + logger.info( "Loading %s to Bitcoin Core" % self.wallets_update_list[wallet]["alias"] ) self.rpc.loadwallet( os.path.join(self.rpc_path, wallet_alias) ) - logger.debug( + logger.info( "Initializing %s Wallet object" % self.wallets_update_list[wallet]["alias"] ) @@ -166,13 +166,13 @@ def _update(self, data_folder=None, rpc=None, chain=None): if not loaded_wallet: raise Exception("Failed to load wallet") # Lock UTXO of pending PSBTs - logger.debug( + logger.info( "Re-locking UTXOs of wallet %s" % self.wallets_update_list[wallet]["alias"] ) if len(loaded_wallet.pending_psbts) > 0: for psbt in loaded_wallet.pending_psbts: - logger.debug( + logger.info( "lock %s " % wallet_alias, loaded_wallet.pending_psbts[psbt]["tx"]["vin"], ) @@ -197,7 +197,7 @@ def _update(self, data_folder=None, rpc=None, chain=None): ], ) self.wallets[wallet_name] = loaded_wallet - logger.debug( + logger.info( "Finished loading wallet into Bitcoin Core and Specter: %s" % self.wallets_update_list[wallet]["alias"] ) @@ -226,7 +226,7 @@ def _update(self, data_folder=None, rpc=None, chain=None): # ok wallet is already there # we only need to update try: - logger.debug( + logger.info( "Wallet already loaded in Bitcoin Core. Initializing %s Wallet object" % self.wallets_update_list[wallet]["alias"] ) @@ -237,7 +237,7 @@ def _update(self, data_folder=None, rpc=None, chain=None): ) if loaded_wallet: self.wallets[wallet_name] = loaded_wallet - logger.debug( + logger.info( "Finished loading wallet into Specter: %s" % self.wallets_update_list[wallet]["alias"] ) @@ -256,12 +256,12 @@ def _update(self, data_folder=None, rpc=None, chain=None): ) else: # wallet is loaded and should stay - logger.debug( + logger.info( "Wallet already in Specter, updating wallet: %s" % self.wallets_update_list[wallet]["alias"] ) self.wallets[wallet_name].update() - logger.debug( + logger.info( "Finished updating wallet: %s" % self.wallets_update_list[wallet]["alias"] ) @@ -269,7 +269,7 @@ def _update(self, data_folder=None, rpc=None, chain=None): # only ignore rpc errors except RpcError as e: logger.error(f"Failed updating wallet manager. RPC error: {e}") - logger.debug("Done updating wallet manager") + logger.info("Done updating wallet manager") self.wallets_update_list = {} self.is_loading = False diff --git a/src/cryptoadvance/specter/server_endpoints/wallets.py b/src/cryptoadvance/specter/server_endpoints/wallets.py index 6ad7cb16d5..7df8af90b4 100644 --- a/src/cryptoadvance/specter/server_endpoints/wallets.py +++ b/src/cryptoadvance/specter/server_endpoints/wallets.py @@ -670,7 +670,10 @@ def send_new(wallet_alias): # calculate new amount if we need to subtract if subtract: for v in psbt["tx"]["vout"]: - if addresses[0] in v["scriptPubKey"]["addresses"]: + if ( + addresses[0] in v["scriptPubKey"]["addresses"] + or addresses[0] == v["scriptPubKey"]["address"] + ): amounts[0] = v["value"] except Exception as e: err = e diff --git a/src/cryptoadvance/specter/templates/includes/tx-data.html b/src/cryptoadvance/specter/templates/includes/tx-data.html index 8079713467..58dc5a7204 100644 --- a/src/cryptoadvance/specter/templates/includes/tx-data.html +++ b/src/cryptoadvance/specter/templates/includes/tx-data.html @@ -133,9 +133,15 @@

Transaction details


let jsonResponse = await fetchRawTx(self, rawtx.vin[i].txid); if (jsonResponse !== null && jsonResponse.success) { let spentOutput = jsonResponse.rawtx.vout[rawtx.vin[i].vout]; - let address = ('addresses' in spentOutput && spentOutput.addresses.length == 1) ? spentOutput.addresses[0] : 'Unknown'; + let address = (spentOutput.addresses && spentOutput.addresses.length == 1) ? spentOutput.addresses[0] : 'Unknown'; if (address == 'Unknown') { - address = ('addresses' in spentOutput.scriptPubKey && spentOutput.scriptPubKey.addresses.length == 1) ? spentOutput.scriptPubKey.addresses[0] : 'Unknown'; + address = (spentOutput.scriptPubKey.addresses && spentOutput.scriptPubKey.addresses.length == 1) ? spentOutput.scriptPubKey.addresses[0] : 'Unknown'; + } + if (address == 'Unknown') { + address = spentOutput.scriptPubKey.address ? spentOutput.scriptPubKey.address : 'Unknown'; + } + if (address == 'Unknown') { + address = spentOutput.address ? spentOutput.address : 'Unknown'; } let isMine = await fetchAddressIsMine(self, address); if (isMine) { @@ -182,11 +188,17 @@

Transaction details


rawtxHTML += `

Outputs

`; for (let i in rawtx.vout) { - let address = ('addresses' in rawtx.vout[i] && rawtx.vout[i].addresses.length == 1) ? rawtx.vout[i].addresses[0] : 'Unknown'; + let address = rawtx.vout[i].addresses && rawtx.vout[i].addresses.length == 1 ? rawtx.vout[i].addresses[0] : 'Unknown'; if (address == 'Unknown') { - address = ('addresses' in rawtx.vout[i].scriptPubKey && rawtx.vout[i].scriptPubKey.addresses.length == 1) ? rawtx.vout[i].scriptPubKey.addresses[0] : 'Unknown'; + address = rawtx.vout[i].scriptPubKey.addresses && rawtx.vout[i].scriptPubKey.addresses.length == 1 ? rawtx.vout[i].scriptPubKey.addresses[0] : 'Unknown'; + + } + if (address == 'Unknown') { + address = rawtx.vout[i].scriptPubKey.address ? rawtx.vout[i].scriptPubKey.address : 'Unknown'; + } + if (address == 'Unknown') { + address = rawtx.vout[i].address ? rawtx.vout[i].address : 'Unknown'; } - let value = "Confidential"; let price = ''; if (rawtx.vout[i].value) { diff --git a/src/cryptoadvance/specter/templates/wallet/send/sign/wallet_send_sign_psbt.jinja b/src/cryptoadvance/specter/templates/wallet/send/sign/wallet_send_sign_psbt.jinja index 4eb82e092c..38b7453ea0 100644 --- a/src/cryptoadvance/specter/templates/wallet/send/sign/wallet_send_sign_psbt.jinja +++ b/src/cryptoadvance/specter/templates/wallet/send/sign/wallet_send_sign_psbt.jinja @@ -153,17 +153,17 @@ {% endfor %}

Outputs ({{psbt['tx']['vout']|length}})

{% for output in psbt['tx']['vout'] if output['scriptPubKey']['type'] != 'fee' %} - {% set address = output['scriptPubKey']['addresses'][0] %} + {% set address = output['scriptPubKey']['addresses'][0] if "addresses" in output["scriptPubKey"] else output["scriptPubKey"]["address"] %} {% set bg_color = '#154984' if wallet.is_address_mine(address) else '#131a24' %}

- Output #{{loop.index0}} {% if output['scriptPubKey']['addresses'][0] not in psbt['address'] %}(Change){% endif %}

- {% set addr_label = wallet.getlabel(output['scriptPubKey']['addresses'][0]) %} + Output #{{loop.index0}} {% if address not in psbt['address'] %}(Change){% endif %}

+ {% set addr_label = wallet.getlabel(address) %} Address: - {{ output['scriptPubKey']['addresses'][0] }} + {{ address }}
- {% if addr_label != output['scriptPubKey']['addresses'][0] %} + {% if addr_label != address %} Label: - +
{% endif %} Amount: {{ output['value']|btcamount }} BTC {% if specter.price_check %} ({{ output['value'] | altunit }}){% endif %} diff --git a/src/cryptoadvance/specter/txlist.py b/src/cryptoadvance/specter/txlist.py index 52259353fc..7b9f23ffa2 100644 --- a/src/cryptoadvance/specter/txlist.py +++ b/src/cryptoadvance/specter/txlist.py @@ -3,6 +3,7 @@ """ import os from .persistence import write_csv, read_csv +from .helpers import get_address_from_dict from embit.transaction import Transaction from embit.networks import NETWORKS import json @@ -200,10 +201,11 @@ def add(self, txs): break if vin["txid"] in self: try: - address = decoderawtransaction( - self[vin["txid"]]["hex"], - self.chain, - )["vout"][vin["vout"]]["addresses"][0] + address = get_address_from_dict( + decoderawtransaction(self[vin["txid"]]["hex"], self.chain,)[ + "vout" + ][vin["vout"]] + ) address_info = self._addresses.get(address, None) if address_info and not address_info.is_external: inputs_mine_count += 1 @@ -213,7 +215,7 @@ def add(self, txs): outputs_mine_count = 0 for out in raw_tx["vout"]: try: - address = out["addresses"][0] + address = get_address_from_dict(out) except Exception: # couldn't get address... continue diff --git a/src/cryptoadvance/specter/util/tx.py b/src/cryptoadvance/specter/util/tx.py index c8586a9d14..74796e7870 100644 --- a/src/cryptoadvance/specter/util/tx.py +++ b/src/cryptoadvance/specter/util/tx.py @@ -35,7 +35,7 @@ def decoderawoutput(vout, chain): }, } try: - result["addresses"] = [vout.script_pubkey.address(NETWORKS[chain])] + result["address"] = vout.script_pubkey.address(NETWORKS[chain]) except: pass return result diff --git a/src/cryptoadvance/specter/wallet.py b/src/cryptoadvance/specter/wallet.py index 20eb936911..6862fb2a7f 100644 --- a/src/cryptoadvance/specter/wallet.py +++ b/src/cryptoadvance/specter/wallet.py @@ -3,7 +3,7 @@ from .device import Device from .key import Key from .util.merkleblock import is_valid_merkle_proof -from .helpers import der_to_bytes, is_liquid +from .helpers import der_to_bytes, is_liquid, get_address_from_dict from embit import base58, bip32 from .util.descriptor import Descriptor, sort_descriptor, AddChecksum from embit.liquid.descriptor import LDescriptor @@ -22,7 +22,7 @@ from .addresslist import AddressList from .txlist import TxList -logger = logging.getLogger() +logger = logging.getLogger(__name__) LISTTRANSACTIONS_BATCH_SIZE = 1000 @@ -1435,8 +1435,8 @@ def get_rbf_utxo(self, rbf_tx_id): "txid": utxo["txid"], "vout": utxo["vout"], "amount": utxo["details"]["value"], - "address": utxo["details"]["addresses"][0], - "label": self.getlabel(utxo["details"]["addresses"][0]), + "address": get_address_from_dict(utxo["details"]), + "label": self.getlabel(get_address_from_dict(utxo["details"])), } for utxo in rbf_utxo ] @@ -1451,19 +1451,23 @@ def decode_tx(self, txid): psbt = self.rpc.decodepsbt(raw_psbt) return { "addresses": [ - vout["scriptPubKey"]["addresses"][0] + get_address_from_dict(vout["scriptPubKey"]) for i, vout in enumerate(psbt["tx"]["vout"]) - if not self.get_address_info(vout["scriptPubKey"]["addresses"][0]) + if not self.get_address_info( + get_address_from_dict(vout["scriptPubKey"]) + ) or not self.get_address_info( - vout["scriptPubKey"]["addresses"][0] + get_address_from_dict(vout["scriptPubKey"]) ).change ], "amounts": [ vout["value"] for i, vout in enumerate(psbt["tx"]["vout"]) - if not self.get_address_info(vout["scriptPubKey"]["addresses"][0]) + if not self.get_address_info( + get_address_from_dict(vout["scriptPubKey"]) + ) or not self.get_address_info( - vout["scriptPubKey"]["addresses"][0] + get_address_from_dict(vout["scriptPubKey"]) ).change ], "used_utxo": [ @@ -1480,10 +1484,12 @@ def bumpfee(self, txid, fee_rate): psbt = self.rpc.decodepsbt(raw_psbt) psbt["changeAddress"] = [ - vout["scriptPubKey"]["addresses"][0] + get_address_from_dict(vout["scriptPubKey"]) for i, vout in enumerate(psbt["tx"]["vout"]) - if self.get_address_info(vout["scriptPubKey"]["addresses"][0]) - and self.get_address_info(vout["scriptPubKey"]["addresses"][0]).change + if self.get_address_info(get_address_from_dict(vout["scriptPubKey"])) + and self.get_address_info( + get_address_from_dict(vout["scriptPubKey"]) + ).change ] if psbt["changeAddress"]: psbt["changeAddress"] = psbt["changeAddress"][0] @@ -1491,19 +1497,23 @@ def bumpfee(self, txid, fee_rate): raise Exception("Cannot RBF a transaction with no change output") return self.createpsbt( addresses=[ - vout["scriptPubKey"]["addresses"][0] + get_address_from_dict(vout["scriptPubKey"]) for i, vout in enumerate(psbt["tx"]["vout"]) - if not self.get_address_info(vout["scriptPubKey"]["addresses"][0]) + if not self.get_address_info( + get_address_from_dict(vout["scriptPubKey"]) + ) or not self.get_address_info( - vout["scriptPubKey"]["addresses"][0] + get_address_from_dict(vout["scriptPubKey"]) ).change ], amounts=[ vout["value"] for i, vout in enumerate(psbt["tx"]["vout"]) - if not self.get_address_info(vout["scriptPubKey"]["addresses"][0]) + if not self.get_address_info( + get_address_from_dict(vout["scriptPubKey"]) + ) or not self.get_address_info( - vout["scriptPubKey"]["addresses"][0] + get_address_from_dict(vout["scriptPubKey"]) ).change ], fee_rate=fee_rate, @@ -1585,10 +1595,11 @@ def importpsbt(self, b64psbt): if ( "addresses" not in out["scriptPubKey"] or len(out["scriptPubKey"]["addresses"]) == 0 + or "address" not in out["scriptPubKey"] ): # TODO: we need to handle it somehow differently raise SpecterError("Sending to raw scripts is not supported yet") - addr = out["scriptPubKey"]["addresses"][0] + addr = get_address_from_dict(out["scriptPubKey"]) info = self.get_address_info(addr) # check if it's a change if info and info.change: