Porting: Mitigating taproot issue (#2087) for the uiux-revamp branch (#2088)

* Bugfix: mitigating taproot hotwallet issue 2078 (#2083)

* Some smaller improvements and fixes

* mitigating 2078

* removed Microsoft link tracking

* wrap up a failed update properly

* better decode and better error_handling

* kick

* removed commented code
This commit is contained in:
k9ert 2023-01-30 12:33:57 +01:00 committed by GitHub
parent 373e96eac0
commit f2d0857bbb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 57 additions and 33 deletions

2
.vscode/launch.json vendored
View file

@ -1,7 +1,7 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
// For more information, visit: https://code.visualstudio.com/docs/editor/debugging#_launch-configurations
"version": "0.2.0",
"configurations": [
{

4
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,4 @@
{
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
}

View file

@ -249,7 +249,7 @@ class WalletManager:
)
self.wallets[wallet_name] = loaded_wallet
except Exception as e:
handle_exception(e)
logger.exception(e)
self._failed_load_wallets.append(
{
**wallets_update_list[wallet],
@ -263,14 +263,13 @@ class WalletManager:
# only ignore rpc errors
except RpcError as e:
logger.error(f"Failed updating wallet manager. RPC error: {e}")
logger.info("Updating wallet manager done. Result:")
logger.info(f" * loaded_wallets: {len(self.wallets)}")
logger.info(f" * failed_load_wallets: {len(self._failed_load_wallets)}")
for wallet in self._failed_load_wallets:
logger.info(f" * {wallet['name']} : {wallet['loading_error']}")
wallets_update_list = {}
self.is_loading = False
finally:
self.is_loading = False
logger.info("Updating wallet manager done. Result:")
logger.info(f" * loaded_wallets: {len(self.wallets)}")
logger.info(f" * failed_load_wallets: {len(self._failed_load_wallets)}")
for wallet in self._failed_load_wallets:
logger.info(f" * {wallet['name']} : {wallet['loading_error']}")
def get_by_alias(self, alias):
for wallet_name in self.wallets:

View file

@ -444,7 +444,7 @@ def history(wallet_alias):
wallet.update_balance()
wallet.check_utxo()
renderting = render_template(
return render_template(
"wallet/history/wallet_history.jinja",
wallet_alias=wallet_alias,
wallet=wallet,
@ -453,8 +453,6 @@ def history(wallet_alias):
rand=rand,
services=app.specter.service_manager.services,
)
logger.info("-------------------end render_template()")
return renderting
###### Wallet receive ######

View file

@ -16,6 +16,7 @@ from .persistence import delete_file, read_csv, write_csv
from .specter_error import SpecterError, SpecterInternalException
from embit.descriptor import Descriptor
from embit.liquid.descriptor import LDescriptor
from .util.common import str2bool
from .util.psbt import (
AbstractTxContext,
SpecterInputScope,
@ -84,6 +85,7 @@ class TxItem(dict, AbstractTxListContext):
"vsize",
"address",
]
# type_converter will be used to _read_csv to have a proper mapping
type_converter = [
str,
int,
@ -289,13 +291,14 @@ class TxItem(dict, AbstractTxListContext):
class WalletAwareTxItem(TxItem):
PSBTCls = SpecterPSBT
# Columns for writing CSVs, type_converter for reading
columns = TxItem.columns.copy()
columns.extend(
["category", "flow_amount", "utxo_amount", "ismine"],
)
type_converter = TxItem.type_converter.copy()
type_converter.extend([str, float, float, bool])
type_converter.extend([str, float, float, str2bool])
def __init__(self, parent, addresses, rawdir, **kwargs):
super().__init__(parent, addresses, rawdir, **kwargs)
@ -323,6 +326,24 @@ class WalletAwareTxItem(TxItem):
self._psbt.update(updated)
return self._psbt
@property
def is_taproot(self):
return str(self.descriptor).startswith("tr(")
def decode_psbt(self, mode="embit") -> SpecterPSBT:
"""Utility function which decodes this tx as psbt
as in the core rpc-call 'decodepsbt'.
However, it uses embit to calculate the details
use mode=core to ask core directly.
embit might support taproot, core might not.
"""
if mode == "core":
return self.rpc.decodepsbt(str(self.psbt))
elif mode == "embit":
return self.psbt.to_dict()
else:
raise SpecterInternalException(f"Mode {mode} does not exist")
@property
def category(self):
"""One of mixed (default), generate, selftransfer, receive or send"""
@ -385,8 +406,11 @@ class WalletAwareTxItem(TxItem):
def ismine(self) -> bool:
if self.get("ismine"):
return self["ismine"]
inputs = self.psbt.inputs
outputs = self.psbt.outputs
if self.is_taproot:
# This is a bug mitigation, see #2078
return True
inputs: List[SpecterInputScope] = self.psbt.inputs
outputs: List[SpecterOutputScope] = self.psbt.outputs
any_inputs_mine = any([inp.is_mine for inp in inputs])
any_outputs_mine = any([out.is_mine for out in outputs])
self["ismine"] = any_inputs_mine or any_outputs_mine
@ -417,14 +441,6 @@ class WalletAwareTxItem(TxItem):
self["address"] = addresses[0]
return self["address"]
def __dict__(self):
super_dict = dict(self)
super_dict["category"] = self.category
super_dict["flow_amount"] = self.flow_amount
super_dict["utxo_amount"] = self.utxo_amount
super_dict["ismine"] = (self["ismine"] or self.ismine,)
return super_dict
class TxList(dict, AbstractTxListContext):
"""A TxList is a dict with txids as keys and TxItems as values."""
@ -474,6 +490,8 @@ class TxList(dict, AbstractTxListContext):
tx.clear_cache()
delete_file(self.path)
self._file_exists = False
self.clear()
logger.info(f"Cleared the Cache for {self.path} (and rawdir)")
def getfetch(self, txid):

View file

@ -4,6 +4,7 @@ from datetime import datetime
import json
from flask_babel.speaklater import LazyString
from typing import Union
from distutils.util import strtobool
logger = logging.getLogger(__name__)
@ -12,11 +13,9 @@ def str2bool(my_str):
"""returns a reasonable boolean from a string so that "False" will result in False"""
if my_str is None:
return False
elif isinstance(my_str, str) and my_str.lower() == "false":
return False
elif isinstance(my_str, str) and my_str.lower() == "off":
return False
return bool(my_str)
elif isinstance(my_str, bool):
return my_str
return bool(strtobool(my_str))
def camelcase2snake_case(name):

View file

@ -875,7 +875,7 @@ class Wallet:
except Exception as e:
logger.exception(e)
self._full_utxo = []
raise SpecterError(f"Failed to load utxos, {e}")
raise SpecterError(f"Failed to load utxos, {type(e).__name__}: {e}")
def check_utxo_orig(self):
"""fetches the utxo-set from core and stores the result in self.__full_utxo which is

View file

@ -27,9 +27,10 @@
<br><br>
<input name="Submit" class="btn" type="submit" value="Submit" />
</form>
you can also do that in any javascript.console with:
Press <b>F12</b> to open the dev-console (helpful to see objects)<br>
You can also do that in any javascript.console with:
<pre>
await pythonCommand("app.specter")
await pythonCommand('app.specter')
</pre>
Some usefull idioms:
<pre>
@ -109,6 +110,7 @@
} else {
console.log(result)
if (result == "") {
result = "(empty string)"
}

View file

@ -224,3 +224,7 @@ class SpectrumNode(AbstractNode):
def node_connection_template(self):
return "spectrum/components/spectrum_node_connection.jinja"
@property
def taproot_support(self):
return False