From 497e530cb259cf035c1c1fb1e1f8c4309e5e3d3e Mon Sep 17 00:00:00 2001 From: fanquake Date: Fri, 17 Nov 2023 11:03:15 +0000 Subject: [PATCH 1/9] Merge bitcoin/bitcoin#28725: test: refactor: use built-in collection types for type hints (Python 3.9 / PEP 585) a478c817b2f62b7334b36e331a2e37fe8380c754 test: replace `Callable`/`Iterable` with their `collections.abc` alternative (PEP 585) (stickies-v) 4b9afb18e6b9e16d7b299820f3a1382986a451d4 scripted-diff: use PEP 585 built-in collection types for verify-binary script (Sebastian Falbesoner) d516cf83ed2da86dfefb395cd46f8a894907b88e test: use built-in collection types for type hints (Python 3.9 / PEP 585) (Sebastian Falbesoner) Pull request description: With Python 3.9 / [PEP 585](https://peps.python.org/pep-0585/), [type hinting has become a little less awkward](https://docs.python.org/3.9/whatsnew/3.9.html#type-hinting-generics-in-standard-collections), as for collection types one doesn't need to import the corresponding capitalized types (`Dict`, `List`, `Set`, `Tuple`, ...) anymore, but can use the built-in types directly (see https://peps.python.org/pep-0585/#implementation for the full list). This PR applies the replacement for all Python scripts (i.e. in the contrib and test folders) for the basic types, i.e.: - typing.Dict -> dict - typing.List -> list - typing.Set -> set - typing.Tuple -> tuple For an additional check, I ran mypy 1.6.1 on both master and the PR branch via ``` $ mypy --ignore-missing-imports --explicit-package-bases $(git ls-files "*.py") ``` and verified that the output is identical -- (from the 22 identified problems, most look like false-positives, it's probably worth it to go deeper here and address them in a follow-up though). ACKs for top commit: stickies-v: ACK a478c817b2f62b7334b36e331a2e37fe8380c754 fanquake: ACK a478c817b2f62b7334b36e331a2e37fe8380c754 Tree-SHA512: 6948c905f6abd644d84f09fcb3661d7edb2742e8f2b28560008697d251d77a61a1146ab4b070e65b0d27acede7a5256703da7bf6eb1c7c3a897755478c76c6e8 Replacing List and Dict with list and dict --- contrib/devtools/circular-dependencies.py | 5 ++-- contrib/devtools/utils.py | 3 +-- contrib/guix/security-check.py | 3 +-- contrib/guix/symbol-check.py | 7 +++-- contrib/macdeploy/macdeployqtplus | 6 ++--- .../message-capture/message-capture-parser.py | 6 ++--- contrib/seeds/makeseeds.py | 20 +++++++------- test/functional/interface_rest.py | 2 +- test/functional/test_framework/script.py | 5 ++-- .../test_framework/test_framework.py | 26 +++++++++---------- test/functional/test_framework/util.py | 3 ++- test/functional/test_framework/wallet.py | 3 +-- test/functional/wallet_fast_rescan.py | 3 +-- test/lint/lint-files.py | 4 +-- test/lint/lint-include-guards.py | 3 +-- 15 files changed, 46 insertions(+), 53 deletions(-) diff --git a/contrib/devtools/circular-dependencies.py b/contrib/devtools/circular-dependencies.py index e939ec6d45ba..373d5fd69b18 100755 --- a/contrib/devtools/circular-dependencies.py +++ b/contrib/devtools/circular-dependencies.py @@ -6,7 +6,6 @@ import sys import re from multiprocess import Pool # type: ignore[import] -from typing import Dict, List, Set MAPPING = { 'core_read.cpp': 'core_io.cpp', @@ -35,7 +34,7 @@ def module_name(path): if __name__=="__main__": files = dict() - deps: Dict[str, Set[str]] = dict() + deps: dict[str, set[str]] = dict() RE = re.compile("^#include <(.*)>") @@ -49,7 +48,7 @@ def handle_module(arg): def handle_module2(module): # Build the transitive closure of dependencies of module - closure: Dict[str, List[str]] = dict() + closure: dict[str, list[str]] = dict() for dep in deps[module]: closure[dep] = [] while True: diff --git a/contrib/devtools/utils.py b/contrib/devtools/utils.py index 68ad1c3aba19..8b4c67c6c0ce 100755 --- a/contrib/devtools/utils.py +++ b/contrib/devtools/utils.py @@ -8,10 +8,9 @@ import shutil import sys import os -from typing import List -def determine_wellknown_cmd(envvar, progname) -> List[str]: +def determine_wellknown_cmd(envvar, progname) -> list[str]: maybe_env = os.getenv(envvar) maybe_which = shutil.which(progname) if maybe_env: diff --git a/contrib/guix/security-check.py b/contrib/guix/security-check.py index 0a5d1bbfa6f5..3c02b2ab4f01 100755 --- a/contrib/guix/security-check.py +++ b/contrib/guix/security-check.py @@ -13,7 +13,6 @@ ''' import re import sys -from typing import List import lief @@ -277,7 +276,7 @@ def check_MACHO_BRANCH_PROTECTION(binary) -> bool: arch = binary.abstract.header.architecture binary.concrete - failed: List[str] = [] + failed: list[str] = [] for (name, func) in CHECKS[etype][arch]: if not func(binary): failed.append(name) diff --git a/contrib/guix/symbol-check.py b/contrib/guix/symbol-check.py index bca9029b24d8..8272efa305af 100755 --- a/contrib/guix/symbol-check.py +++ b/contrib/guix/symbol-check.py @@ -11,7 +11,6 @@ find ../path/to/guix/binaries -type f -executable | xargs python3 contrib/guix/symbol-check.py ''' import sys -from typing import Dict, List import lief @@ -50,7 +49,7 @@ # Expected linker-loader names can be found here: # https://sourceware.org/glibc/wiki/ABIList?action=recall&rev=16 -ELF_INTERPRETER_NAMES: Dict[lief.ELF.ARCH, Dict[lief.ENDIANNESS, str]] = { +ELF_INTERPRETER_NAMES: dict[lief.ELF.ARCH, dict[lief.ENDIANNESS, str]] = { lief.ELF.ARCH.x86_64: { lief.ENDIANNESS.LITTLE: "/lib64/ld-linux-x86-64.so.2", }, @@ -69,7 +68,7 @@ }, } -ELF_ABIS: Dict[lief.ELF.ARCH, Dict[lief.ENDIANNESS, List[int]]] = { +ELF_ABIS: dict[lief.ELF.ARCH, dict[lief.ENDIANNESS, list[int]]] = { lief.ELF.ARCH.x86_64: { lief.ENDIANNESS.LITTLE: [3,2,0], }, @@ -303,7 +302,7 @@ def check_ELF_ABI(binary) -> bool: binary = lief.parse(filename) etype = binary.format - failed: List[str] = [] + failed: list[str] = [] for (name, func) in CHECKS[etype]: if not func(binary): failed.append(name) diff --git a/contrib/macdeploy/macdeployqtplus b/contrib/macdeploy/macdeployqtplus index f86b6f760f66..5183ce57255c 100755 --- a/contrib/macdeploy/macdeployqtplus +++ b/contrib/macdeploy/macdeployqtplus @@ -20,7 +20,7 @@ import sys, re, os, platform, shutil, stat, subprocess, os.path from argparse import ArgumentParser from pathlib import Path from subprocess import PIPE, run -from typing import List, Optional +from typing import Optional # This is ported from the original macdeployqt with modifications @@ -181,7 +181,7 @@ class DeploymentInfo(object): return True return False -def getFrameworks(binaryPath: str, verbose: int) -> List[FrameworkInfo]: +def getFrameworks(binaryPath: str, verbose: int) -> list[FrameworkInfo]: objdump = os.getenv("OBJDUMP", "objdump") if verbose: print(f"Inspecting with {objdump}: {binaryPath}") @@ -285,7 +285,7 @@ def copyFramework(framework: FrameworkInfo, path: str, verbose: int) -> Optional return toPath -def deployFrameworks(frameworks: List[FrameworkInfo], bundlePath: str, binaryPath: str, strip: bool, verbose: int, deploymentInfo: Optional[DeploymentInfo] = None) -> DeploymentInfo: +def deployFrameworks(frameworks: list[FrameworkInfo], bundlePath: str, binaryPath: str, strip: bool, verbose: int, deploymentInfo: Optional[DeploymentInfo] = None) -> DeploymentInfo: if deploymentInfo is None: deploymentInfo = DeploymentInfo() diff --git a/contrib/message-capture/message-capture-parser.py b/contrib/message-capture/message-capture-parser.py index b416c11302bb..0f0dc65a12f9 100755 --- a/contrib/message-capture/message-capture-parser.py +++ b/contrib/message-capture/message-capture-parser.py @@ -11,7 +11,7 @@ from io import BytesIO import json from pathlib import Path -from typing import Any, List, Optional +from typing import Any, Optional sys.path.append(os.path.join(os.path.dirname(__file__), '../../test/functional')) @@ -92,7 +92,7 @@ def to_jsonable(obj: Any) -> Any: return obj -def process_file(path: str, messages: List[Any], recv: bool, progress_bar: Optional[ProgressBar]) -> None: +def process_file(path: str, messages: list[Any], recv: bool, progress_bar: Optional[ProgressBar]) -> None: with open(path, 'rb') as f_in: if progress_bar: bytes_read = 0 @@ -189,7 +189,7 @@ def main(): output = Path.cwd() / Path(args.output) if args.output else False use_progress_bar = (not args.no_progress_bar) and sys.stdout.isatty() - messages = [] # type: List[Any] + messages = [] # type: list[Any] if use_progress_bar: total_size = sum(capture.stat().st_size for capture in capturepaths) progress_bar = ProgressBar(total_size) diff --git a/contrib/seeds/makeseeds.py b/contrib/seeds/makeseeds.py index 17e06125ef4d..b5e1f10e6955 100755 --- a/contrib/seeds/makeseeds.py +++ b/contrib/seeds/makeseeds.py @@ -13,7 +13,7 @@ import collections import json import multiprocessing -from typing import List, Dict, Union +from typing import Union NSEEDS=512 @@ -76,28 +76,28 @@ def parseip(ip_in: str) -> Union[dict, None]: "sortkey": sortkey } -def filtermulticollateralhash(mns : List[Dict]) -> List[Dict]: +def filtermulticollateralhash(mns : list[dict]) -> list[dict]: '''Filter out MNs sharing the same collateral hash''' hist = collections.defaultdict(list) for mn in mns: hist[mn['collateralHash']].append(mn) return [mn for mn in mns if len(hist[mn['collateralHash']]) == 1] -def filtermulticollateraladdress(mns : List[Dict]) -> List[Dict]: +def filtermulticollateraladdress(mns : list[dict]) -> list[dict]: '''Filter out MNs sharing the same collateral address''' hist = collections.defaultdict(list) for mn in mns: hist[mn['collateralAddress']].append(mn) return [mn for mn in mns if len(hist[mn['collateralAddress']]) == 1] -def filtermultipayoutaddress(mns : List[Dict]) -> List[Dict]: +def filtermultipayoutaddress(mns : list[dict]) -> list[dict]: '''Filter out MNs sharing the same payout address''' hist = collections.defaultdict(list) for mn in mns: hist[mn['state']['payoutAddress']].append(mn) return [mn for mn in mns if len(hist[mn['state']['payoutAddress']]) == 1] -def resolveasn(resolver, ip : Dict) -> Union[int, None]: +def resolveasn(resolver, ip : dict) -> Union[int, None]: """ Look up the asn for an `ip` address by querying cymru.com on network `net` (e.g. ipv4 or ipv6). @@ -124,7 +124,7 @@ def resolveasn(resolver, ip : Dict) -> Union[int, None]: return None # Based on Greg Maxwell's seed_filter.py -def filterbyasn(ips: List[Dict], max_per_asn: Dict, max_per_net: int) -> List[Dict]: +def filterbyasn(ips: list[dict], max_per_asn: dict, max_per_net: int) -> list[dict]: """ Prunes `ips` by (a) trimming ips to have at most `max_per_net` ips from each net (e.g. ipv4, ipv6); and (b) trimming ips to have at most `max_per_asn` ips from each asn in each net. @@ -144,8 +144,8 @@ def filterbyasn(ips: List[Dict], max_per_asn: Dict, max_per_net: int) -> List[Di # Filter IPv46 by ASN, and limit to max_per_net per network result = [] - net_count: Dict[str, int] = collections.defaultdict(int) - asn_count: Dict[int, int] = collections.defaultdict(int) + net_count: dict[str, int] = collections.defaultdict(int) + asn_count: dict[int, int] = collections.defaultdict(int) for i, ip in enumerate(ips_ipv46): if i % 10 == 0: @@ -169,9 +169,9 @@ def filterbyasn(ips: List[Dict], max_per_asn: Dict, max_per_net: int) -> List[Di result.extend(ips_onion[0:max_per_net]) return result -def ip_stats(ips: List[Dict]) -> str: +def ip_stats(ips: list[dict]) -> str: """ Format and return pretty string from `ips`. """ - hist: Dict[str, int] = collections.defaultdict(int) + hist: dict[str, int] = collections.defaultdict(int) for ip in ips: if ip is not None: hist[ip['net']] += 1 diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py index a2186bf57ea1..802a583b5eb3 100755 --- a/test/functional/interface_rest.py +++ b/test/functional/interface_rest.py @@ -64,7 +64,7 @@ def test_rest_request( body: str = '', status: int = 200, ret_type: RetType = RetType.JSON, - query_params: typing.Dict[str, typing.Any] = None, + query_params: dict[str, typing.Any] = None, ) -> typing.Union[http.client.HTTPResponse, bytes, str, None]: rest_uri = '/rest' + uri if req_type in ReqType: diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py index bb078591b9f4..9e4a96507258 100644 --- a/test/functional/test_framework/script.py +++ b/test/functional/test_framework/script.py @@ -8,7 +8,6 @@ """ import struct import unittest -from typing import List, Dict from .messages import ( CTransaction, @@ -97,8 +96,8 @@ def __new__(cls, n): _opcode_instances.append(super().__new__(cls, n)) return _opcode_instances[n] -OPCODE_NAMES: Dict[CScriptOp, str] = {} -_opcode_instances: List[CScriptOp] = [] +OPCODE_NAMES: dict[CScriptOp, str] = {} +_opcode_instances: list[CScriptOp] = [] # Populate opcode instance table for n in range(0xff + 1): diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index de0e4ca70501..87e23c40f823 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -23,7 +23,7 @@ import time from concurrent.futures import ThreadPoolExecutor -from typing import List, Optional, Union +from typing import Optional, Union from .address import ADDRESS_BCRT1_P2SH_OP_TRUE from .authproxy import JSONRPCException from test_framework.masternodes import check_banned, check_punished @@ -122,7 +122,7 @@ def __init__(self): self.chain: str = 'regtest' self.setup_clean_chain: bool = False self.disable_mocktime: bool = False - self.nodes: List[TestNode] = [] + self.nodes: list[TestNode] = [] self.extra_args = None self.network_thread = None self.mocktime = 0 @@ -1244,8 +1244,8 @@ def get_node(self, test: BitcoinTestFramework) -> TestNode: raise AssertionError(f"Node at pos {self.nodeIdx} not present, did you start the node?") return test.nodes[self.nodeIdx] - def validate_inputs(self, platform_node_id: Optional[str] = None, addrs_platform_p2p: Union[int, str, List[str], None] = None, - addrs_platform_https: Union[int, str, List[str], None] = None, expected_assert_code: Optional[int] = None, + def validate_inputs(self, platform_node_id: Optional[str] = None, addrs_platform_p2p: Union[int, str, list[str], None] = None, + addrs_platform_https: Union[int, str, list[str], None] = None, expected_assert_code: Optional[int] = None, expected_assert_msg: Optional[str] = None): if (expected_assert_code and not expected_assert_msg) or (not expected_assert_code and expected_assert_msg): raise AssertionError("Intending to use assert_raises_rpc_error() but didn't specify code and message") @@ -1258,10 +1258,10 @@ def validate_inputs(self, platform_node_id: Optional[str] = None, addrs_platform raise AssertionError("EvoNode but addrs_platform_https is missing, must be specified!") def register(self, node: TestNode, submit: bool, collateral_txid: Optional[str] = None, collateral_vout: Optional[int] = None, - addrs_core_p2p: Union[str, List[str], None] = None, ownerAddr: Optional[str] = None, pubKeyOperator: Optional[str] = None, + addrs_core_p2p: Union[str, list[str], None] = None, ownerAddr: Optional[str] = None, pubKeyOperator: Optional[str] = None, votingAddr: Optional[str] = None, operator_reward: Optional[int] = None, rewards_address: Optional[str] = None, - fundsAddr: Optional[str] = None, platform_node_id: Optional[str] = None, addrs_platform_p2p: Union[int, str, List[str], None] = None, - addrs_platform_https: Union[int, str, List[str], None] = None, expected_assert_code: Optional[int] = None, + fundsAddr: Optional[str] = None, platform_node_id: Optional[str] = None, addrs_platform_p2p: Union[int, str, list[str], None] = None, + addrs_platform_https: Union[int, str, list[str], None] = None, expected_assert_code: Optional[int] = None, expected_assert_msg: Optional[str] = None) -> Optional[str]: self.validate_inputs(platform_node_id, addrs_platform_p2p, addrs_platform_https, expected_assert_code, expected_assert_msg) @@ -1304,11 +1304,11 @@ def register(self, node: TestNode, submit: bool, collateral_txid: Optional[str] return ret - def register_fund(self, node: TestNode, submit: bool, collateral_address: Optional[str] = None, addrs_core_p2p: Union[str, List[str], None] = None, + def register_fund(self, node: TestNode, submit: bool, collateral_address: Optional[str] = None, addrs_core_p2p: Union[str, list[str], None] = None, ownerAddr: Optional[str] = None, pubKeyOperator: Optional[str] = None, votingAddr: Optional[str] = None, operator_reward: Optional[int] = None, rewards_address: Optional[str] = None, fundsAddr: Optional[str] = None, - platform_node_id: Optional[str] = None, addrs_platform_p2p: Union[int, str, List[str], None] = None, - addrs_platform_https: Union[int, str, List[str], None] = None, expected_assert_code: Optional[int] = None, + platform_node_id: Optional[str] = None, addrs_platform_p2p: Union[int, str, list[str], None] = None, + addrs_platform_https: Union[int, str, list[str], None] = None, expected_assert_code: Optional[int] = None, expected_assert_msg: Optional[str] = None) -> Optional[str]: self.validate_inputs(platform_node_id, addrs_platform_p2p, addrs_platform_https, expected_assert_code, expected_assert_msg) @@ -1434,8 +1434,8 @@ def update_registrar(self, node: TestNode, submit: bool, pubKeyOperator: Optiona return ret - def update_service(self, node: TestNode, submit: bool, addrs_core_p2p: Union[str, List[str], None] = None, platform_node_id: Optional[str] = None, - addrs_platform_p2p: Union[int, str, List[str], None] = None, addrs_platform_https: Union[int, str, List[str], None] = None, + def update_service(self, node: TestNode, submit: bool, addrs_core_p2p: Union[str, list[str], None] = None, platform_node_id: Optional[str] = None, + addrs_platform_p2p: Union[int, str, list[str], None] = None, addrs_platform_https: Union[int, str, list[str], None] = None, address_operator: Optional[str] = None, fundsAddr: Optional[str] = None, expected_assert_code: Optional[int] = None, expected_assert_msg: Optional[str] = None) -> Optional[str]: self.validate_inputs(platform_node_id, addrs_platform_p2p, addrs_platform_https, expected_assert_code, expected_assert_msg) @@ -2375,7 +2375,7 @@ def get_recovered_sig(self, rec_sig_id, rec_sig_msg_hash, llmq_type=100, use_pla self.log.info(f"getrecsig failed with '{e}'") assert False - def get_quorum_masternodes(self, q, llmq_type=100) -> List[Optional[MasternodeInfo]]: + def get_quorum_masternodes(self, q, llmq_type=100) -> list[Optional[MasternodeInfo]]: qi = self.nodes[0].quorum('info', llmq_type, q) result = [] for m in qi['members']: diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index 7cdcbc8983d8..3a2bc8c1769b 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -21,7 +21,8 @@ from . import coverage from .authproxy import AuthServiceProxy, JSONRPCException -from typing import Callable, Optional +from collections.abc import Callable +from typing import Optional logger = logging.getLogger("TestFramework.utils") diff --git a/test/functional/test_framework/wallet.py b/test/functional/test_framework/wallet.py index 8333558dba5c..a7ced05bba36 100644 --- a/test/functional/test_framework/wallet.py +++ b/test/functional/test_framework/wallet.py @@ -9,7 +9,6 @@ from enum import Enum from typing import ( Any, - List, Optional, ) from test_framework.address import ( @@ -257,7 +256,7 @@ def send_self_transfer_multi(self, *, from_node, **kwargs): def create_self_transfer_multi( self, *, - utxos_to_spend: Optional[List[dict]] = None, + utxos_to_spend: Optional[list[dict]] = None, num_outputs=1, amount_per_output=0, locktime=0, diff --git a/test/functional/wallet_fast_rescan.py b/test/functional/wallet_fast_rescan.py index 50f4602e8c42..8cec87cd0b50 100755 --- a/test/functional/wallet_fast_rescan.py +++ b/test/functional/wallet_fast_rescan.py @@ -5,7 +5,6 @@ """Test that fast rescan using block filters for descriptor wallets detects top-ups correctly and finds the same transactions than the slow variant.""" import os -from typing import List from test_framework.descriptors import descsum_create from test_framework.test_framework import BitcoinTestFramework @@ -32,7 +31,7 @@ def skip_test_if_missing_module(self): self.skip_if_no_wallet() self.skip_if_no_sqlite() - def get_wallet_txids(self, node: TestNode, wallet_name: str) -> List[str]: + def get_wallet_txids(self, node: TestNode, wallet_name: str) -> list[str]: w = node.get_wallet_rpc(wallet_name) txs = w.listtransactions('*', 1000000) return [tx['txid'] for tx in txs] diff --git a/test/lint/lint-files.py b/test/lint/lint-files.py index ee09cea93235..a23fc4416a7c 100755 --- a/test/lint/lint-files.py +++ b/test/lint/lint-files.py @@ -11,7 +11,7 @@ import re import sys from subprocess import check_output -from typing import Dict, Optional, NoReturn +from typing import Optional, NoReturn CMD_TOP_LEVEL = ["git", "rev-parse", "--show-toplevel"] CMD_ALL_FILES = ["git", "ls-files", "-z", "--full-name", "--stage"] @@ -69,7 +69,7 @@ def full_extension(self) -> Optional[str]: return None -def get_git_file_metadata() -> Dict[str, FileMeta]: +def get_git_file_metadata() -> dict[str, FileMeta]: ''' Return a dictionary mapping the name of all files in the repository to git tree metadata. ''' diff --git a/test/lint/lint-include-guards.py b/test/lint/lint-include-guards.py index 6e59c83f7dbc..9686c1aad853 100755 --- a/test/lint/lint-include-guards.py +++ b/test/lint/lint-include-guards.py @@ -11,7 +11,6 @@ import re import sys from subprocess import check_output -from typing import List HEADER_ID_PREFIX = 'BITCOIN_' @@ -33,7 +32,7 @@ 'src/immer'] -def _get_header_file_lst() -> List[str]: +def _get_header_file_lst() -> list[str]: """ Helper function to get a list of header filepaths to be checked for include guards. """ From c6ae8b8b48c24ed469bb5d9e167985da117d9223 Mon Sep 17 00:00:00 2001 From: fanquake Date: Thu, 16 Nov 2023 10:18:46 +0000 Subject: [PATCH 2/9] Merge bitcoin/bitcoin#28771: tests: Fix LCOV_OPTS to be in the correct position 88e09ac2a15d674db9e814755602572be61241ff tests: Fix LCOV_OPTS to be in the correct position (Andrew Chow) Pull request description: `lcov`'s `-a` option takes an argument. With `LCOV_OPTS` immediately after `-a`, the first additional argument becomes the argument to `-a` which is incorrect. Also add `LCOV_OPTS` to more `lcov` calls. ACKs for top commit: fanquake: ACK 88e09ac2a15d674db9e814755602572be61241ff Tree-SHA512: 1ed657c96395bfe882041ded883cb5fa4d04d6ede91f66c319b5bbdd1f88468f8abb2a741dd7898904a78ed7e6c844316f7958ce9e4ccf2dbe666ebec308b7fb --- Makefile.am | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile.am b/Makefile.am index ea2cba9fd2fa..6f3dfaa7500a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -187,7 +187,7 @@ $(COV_TOOL_WRAPPER): @chmod +x $(COV_TOOL_WRAPPER) baseline.info: $(COV_TOOL_WRAPPER) - $(LCOV) -c -i -d $(abs_builddir)/src -o $@ + $(LCOV) $(LCOV_OPTS) -c -i -d $(abs_builddir)/src -o $@ baseline_filtered.info: baseline.info $(abs_builddir)/contrib/filter-lcov.py $(LCOV_FILTER_PATTERN) $< $@ @@ -221,13 +221,13 @@ functional_test_filtered.info: functional_test.info $(LCOV) -a $@ $(LCOV_OPTS) -o $@ fuzz_coverage.info: fuzz_filtered.info - $(LCOV) -a $(LCOV_OPTS) baseline_filtered.info -a fuzz_filtered.info -o $@ | $(GREP) "\%" | $(AWK) '{ print substr($$3,2,50) "/" $$5 }' > coverage_percent.txt + $(LCOV) $(LCOV_OPTS) -a baseline_filtered.info -a fuzz_filtered.info -o $@ | $(GREP) "\%" | $(AWK) '{ print substr($$3,2,50) "/" $$5 }' > coverage_percent.txt test_dash_coverage.info: baseline_filtered.info test_dash_filtered.info - $(LCOV) -a $(LCOV_OPTS) baseline_filtered.info -a test_dash_filtered.info -o $@ + $(LCOV) $(LCOV_OPTS) -a baseline_filtered.info -a test_dash_filtered.info -o $@ total_coverage.info: test_dash_filtered.info functional_test_filtered.info - $(LCOV) -a $(LCOV_OPTS) baseline_filtered.info -a test_dash_filtered.info -a functional_test_filtered.info -o $@ | $(GREP) "\%" | $(AWK) '{ print substr($$3,2,50) "/" $$5 }' > coverage_percent.txt + $(LCOV) $(LCOV_OPTS) -a baseline_filtered.info -a test_dash_filtered.info -a functional_test_filtered.info -o $@ | $(GREP) "\%" | $(AWK) '{ print substr($$3,2,50) "/" $$5 }' > coverage_percent.txt fuzz.coverage/.dirstamp: fuzz_coverage.info $(GENHTML) -s $(LCOV_OPTS) $< -o $(@D) From 565c00050119859b929e872a828f20b1b703abfe Mon Sep 17 00:00:00 2001 From: fanquake Date: Thu, 16 Nov 2023 10:02:07 +0000 Subject: [PATCH 3/9] Merge bitcoin/bitcoin#28825: fuzz: Minor improvements to tx_package_eval target 6a917918b76eef154c6757fe9ecf7713d526c3dd fuzz: allow fake and duplicate inputs in tx_package_eval target (Greg Sanders) a0626ccdadc0e965dc818d8a7c862e8c81b54fd1 fuzz: allow reaching MempoolAcceptResult::ResultType::DIFFERENT_WITNESS in tx_package_eval target (Greg Sanders) Pull request description: Exercises `DIFFERENT_WITNESS` by using "blank" WSH() and allowing witness to determine wtxid, and attempts to make invalid/duplicate inputs. ACKs for top commit: dergoegge: Coverage looks good to me ACK 6a917918b76eef154c6757fe9ecf7713d526c3dd Tree-SHA512: db894f5f5b81c6b454874baf11f296462832285f41ccb09f23c0db92b9abc98f8ecacd72fc8f60dc92cb7947f543a2e55bed2fd210b0e8ca7c7d5389d90b14af --- src/test/util/script.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/test/util/script.h b/src/test/util/script.h index 81f94afec0ea..1a2b1eb806c8 100644 --- a/src/test/util/script.h +++ b/src/test/util/script.h @@ -16,6 +16,18 @@ static const CScript P2SH_OP_TRUE{ << ToByteVector(CScriptID{CScript{} << OP_TRUE}) << OP_EQUAL}; +static const std::vector EMPTY{}; +static const CScript P2WSH_EMPTY{ + CScript{} + << OP_0 + << ToByteVector([] { + uint256 hash; + CSHA256().Write(EMPTY.data(), EMPTY.size()).Finalize(hash.begin()); + return hash; + }())}; +static const std::vector> P2WSH_EMPTY_TRUE_STACK{{static_cast(OP_TRUE)}, {}}; +static const std::vector> P2WSH_EMPTY_TWO_STACK{{static_cast(OP_2)}, {}}; + /** Flags that are not forbidden by an assert in script validation */ bool IsValidFlagCombination(unsigned flags); From 18a3f11d5a77fbeff5cdd63094e7ab2f65a4494d Mon Sep 17 00:00:00 2001 From: fanquake Date: Wed, 8 Nov 2023 09:46:28 +0000 Subject: [PATCH 4/9] Merge bitcoin/bitcoin#28814: test: symbolizer improvements 49d953281df5618430728c0a88471695207f086b fuzz: explicitly specify llvm-symbolizer path in runner (fanquake) Pull request description: It's not completely clear to me why this needs to be explicitly specified in some environments, and not in others, while at the same time that `llvm-symbolizer` is already in PATH, but this has fixed the 2 issues outlined in https://github.com/bitcoin/bitcoin/pull/28147. Use `LLVM_SYMBOLIZER_PATH` as the env var, as that is somewhat also used inside LLVM, but not consistently, i.e it's checked for in the asan_symbolize script, but not in in the ubsan_symbolize script, or from in compiler-rt. Alternative to #28804. ACKs for top commit: maflcko: lgtm ACK 49d953281df5618430728c0a88471695207f086b Tree-SHA512: c3d5bf1c3629793b342c70754a419b3c7a3cd39f800b9aa69ce3395cc2bf83b4d46f2b329974337b94b99573cd0b8600d3f147ed5c21387bf3812316570d1ee3 --- ci/test/00_setup_env_native_fuzz.sh | 1 + test/fuzz/test_runner.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/ci/test/00_setup_env_native_fuzz.sh b/ci/test/00_setup_env_native_fuzz.sh index 6d405af70d47..f04d825705cd 100755 --- a/ci/test/00_setup_env_native_fuzz.sh +++ b/ci/test/00_setup_env_native_fuzz.sh @@ -16,3 +16,4 @@ export RUN_FUNCTIONAL_TESTS=false export RUN_FUZZ_TESTS=true export GOAL="install" export BITCOIN_CONFIG="--enable-zmq --enable-fuzz --with-sanitizers=fuzzer,address,undefined,integer CC='clang-19 -ftrivial-auto-var-init=pattern' CXX='clang++-19 -ftrivial-auto-var-init=pattern'" +export LLVM_SYMBOLIZER_PATH="/usr/bin/llvm-symbolizer-17" diff --git a/test/fuzz/test_runner.py b/test/fuzz/test_runner.py index 399d9209e280..e6ab467f2efb 100755 --- a/test/fuzz/test_runner.py +++ b/test/fuzz/test_runner.py @@ -17,11 +17,14 @@ def get_fuzz_env(*, target, source_dir): + symbolizer = os.environ.get('LLVM_SYMBOLIZER_PATH', "/usr/bin/llvm-symbolizer") return { 'FUZZ': target, 'UBSAN_OPTIONS': f'suppressions={source_dir}/test/sanitizer_suppressions/ubsan:print_stacktrace=1:halt_on_error=1:report_error_type=1', + 'UBSAN_SYMBOLIZER_PATH':symbolizer, "ASAN_OPTIONS": "detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1", + 'ASAN_SYMBOLIZER_PATH':symbolizer, } From 45af4f98df09884e289d07bc8ee72feec5562b97 Mon Sep 17 00:00:00 2001 From: Andrew Chow Date: Tue, 7 Nov 2023 11:23:12 -0500 Subject: [PATCH 5/9] Merge bitcoin/bitcoin#28546: wallet: prevent bugs from invalid transaction heights with asserts, comments, and refactoring f06016d77d848133fc6568f287bb86b644c9fa69 wallet: Add asserts to detect unset transaction height values (Ryan Ofsky) 262a78b13365e5318741187fde431c4974539494 wallet, refactor: Add CWalletTx::updateState function (Ryan Ofsky) Pull request description: Originally, this PR fixed a wallet migration bug that could cause the watchonly wallet created by legacy wallet migration to have incorrect transaction height values. A different fix for the bug was implemented in #28609, but that PR did not add any test coverage that would have caught the bug, and didn't include other changes from this PR intended to prevent problems from invalid transaction heights. This PR adds new asserts to catch invalid transaction heights, which would trigger test failures without bugfix in #28609. This PR also refactors code and adds comments to clarify assumptions and make it less likely a bug from invalid transaction height values would be introduced. ACKs for top commit: achow101: ACK f06016d77d848133fc6568f287bb86b644c9fa69 Sjors: utACK f06016d77d848133fc6568f287bb86b644c9fa69 furszy: Code review ACK f06016d Tree-SHA512: 82657c403724d60354f7676b53bcfcc95bdc5864e051a2eb8bfad09d8ad35615393b2d6b432b46f908def9be37bebded3a55ec9ae19e19371d35897fe842c92e --- src/wallet/transaction.cpp | 25 +++++++++++++++++++++++++ src/wallet/transaction.h | 8 ++++++++ src/wallet/wallet.cpp | 20 +++----------------- src/wallet/wallet.h | 7 +++++++ 4 files changed, 43 insertions(+), 17 deletions(-) diff --git a/src/wallet/transaction.cpp b/src/wallet/transaction.cpp index a46846c1d4a3..eb02ca20d0be 100644 --- a/src/wallet/transaction.cpp +++ b/src/wallet/transaction.cpp @@ -4,6 +4,10 @@ #include +#include + +using interfaces::FoundBlock; + namespace wallet { bool CWalletTx::IsEquivalentTo(const CWalletTx& _tx) const { @@ -24,4 +28,25 @@ int64_t CWalletTx::GetTxTime() const int64_t n = nTimeSmart; return n ? n : nTimeReceived; } + +void CWalletTx::updateState(interfaces::Chain& chain) +{ + bool active; + auto lookup_block = [&](const uint256& hash, int& height, TxState& state) { + // If tx block (or conflicting block) was reorged out of chain + // while the wallet was shutdown, change tx status to UNCONFIRMED + // and reset block height, hash, and index. ABANDONED tx don't have + // associated blocks and don't need to be updated. The case where a + // transaction was reorged out while online and then reconfirmed + // while offline is covered by the rescan logic. + if (!chain.findBlock(hash, FoundBlock().inActiveChain(active).height(height)) || !active) { + state = TxStateInactive{}; + } + }; + if (auto* conf = state()) { + lookup_block(conf->confirmed_block_hash, conf->confirmed_block_height, m_state); + } else if (auto* conf = state()) { + lookup_block(conf->conflicting_block_hash, conf->conflicting_block_height, m_state); + } +} } // namespace wallet diff --git a/src/wallet/transaction.h b/src/wallet/transaction.h index bac89c2f633f..bdd336c6ce7a 100644 --- a/src/wallet/transaction.h +++ b/src/wallet/transaction.h @@ -20,6 +20,10 @@ #include #include +namespace interfaces { +class Chain; +} // namespace interfaces + namespace wallet { //! State of transaction confirmed in a block. struct TxStateConfirmed { @@ -309,6 +313,10 @@ class CWalletTx template const T* state() const { return std::get_if(&m_state); } template T* state() { return std::get_if(&m_state); } + //! Update transaction state when attaching to a chain, filling in heights + //! of conflicted and confirmed blocks + void updateState(interfaces::Chain& chain); + bool isAbandoned() const { return state() && state()->abandoned; } bool isConflicted() const { return state(); } bool isUnconfirmed() const { return !isAbandoned() && !isConflicted() && !isConfirmed(); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index d7be196ed58b..a9d42dc3c3fb 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1127,23 +1127,7 @@ bool CWallet::LoadToWallet(const uint256& hash, const UpdateWalletTxFn& fill_wtx // If wallet doesn't have a chain (e.g when using dash-wallet tool), // don't bother to update txn. if (HaveChain()) { - bool active; - auto lookup_block = [&](const uint256& hash, int& height, TxState& state) { - // If tx block (or conflicting block) was reorged out of chain - // while the wallet was shutdown, change tx status to UNCONFIRMED - // and reset block height, hash, and index. ABANDONED tx don't have - // associated blocks and don't need to be updated. The case where a - // transaction was reorged out while online and then reconfirmed - // while offline is covered by the rescan logic. - if (!chain().findBlock(hash, FoundBlock().inActiveChain(active).height(height)) || !active) { - state = TxStateInactive{}; - } - }; - if (auto* conf = wtx.state()) { - lookup_block(conf->confirmed_block_hash, conf->confirmed_block_height, wtx.m_state); - } else if (auto* conf = wtx.state()) { - lookup_block(conf->conflicting_block_hash, conf->conflicting_block_height, wtx.m_state); - } + wtx.updateState(chain()); } if (/* insertion took place */ ins.second) { wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx)); @@ -3721,8 +3705,10 @@ int CWallet::GetTxDepthInMainChain(const CWalletTx& wtx) const { AssertLockHeld(cs_wallet); if (auto* conf = wtx.state()) { + assert(conf->confirmed_block_height >= 0); return GetLastBlockHeight() - conf->confirmed_block_height + 1; } else if (auto* conf = wtx.state()) { + assert(conf->conflicting_block_height >= 0); return -1 * (GetLastBlockHeight() - conf->conflicting_block_height + 1); } else { return 0; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 97c79ba5bf1e..16f3f5c82d3f 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -543,6 +543,13 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati * <0 : conflicts with a transaction this deep in the blockchain * 0 : in memory pool, waiting to be included in a block * >=1 : this many blocks deep in the main chain + * + * Preconditions: it is only valid to call this function when the wallet is + * online and the block index is loaded. So this cannot be called by + * bitcoin-wallet tool code or by wallet migration code. If this is called + * without the wallet being online, it won't be able able to determine the + * the height of the last block processed, or the heights of blocks + * referenced in transaction, and might cause assert failures. */ int GetTxDepthInMainChain(const CWalletTx& wtx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool IsTxInMainChain(const CWalletTx& wtx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) From c50e1c3a097abd916f0cd8405979fd02c971a92f Mon Sep 17 00:00:00 2001 From: fanquake Date: Mon, 13 Nov 2023 13:46:57 +0000 Subject: [PATCH 6/9] Merge bitcoin/bitcoin#28076: util: Replace std::filesystem with util/fs.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bbbbdb0cd57d75a06357d2811363d30a498f4499 ci: Add filesystem lint check (MarcoFalke) fada2f91108a56cc5c447bd6b6fac411e4d5cdca refactor: Replace with (MarcoFalke) Pull request description: Using `std::filesystem` is problematic: * There is a `fs` namespace wrapper for it. So having two ways to achieve the same is confusing. * Not using the `fs` wrapper is dangerous and buggy, because it disables known bugs by deleting problematic functions. Fix all issues by removing use of it and adding a linter to avoid using it again in the future. ACKs for top commit: TheCharlatan: ACK bbbbdb0cd57d75a06357d2811363d30a498f4499 fanquake: ACK bbbbdb0cd57d75a06357d2811363d30a498f4499 🦀 Tree-SHA512: 0e2d49742b08eb2635e6fce41485277cb9c40fe20b81017c391d3472a43787db1278a236825714ca1e41c9d2f59913865cfb0c649e3c8ab1fb598c849f80c660 --- ci/lint/04_install.sh | 11 +++++ ci/lint/06_script.sh | 1 + ci/lint/docker-entrypoint.sh | 2 + src/fs.h | 1 + test/lint/README.md | 8 ++++ test/lint/test_runner/Cargo.lock | 7 +++ test/lint/test_runner/Cargo.toml | 12 +++++ test/lint/test_runner/src/main.rs | 77 +++++++++++++++++++++++++++++++ 8 files changed, 119 insertions(+) create mode 100644 test/lint/test_runner/Cargo.lock create mode 100644 test/lint/test_runner/Cargo.toml create mode 100644 test/lint/test_runner/src/main.rs diff --git a/ci/lint/04_install.sh b/ci/lint/04_install.sh index 9ca5ebe37216..eb0827a61bf2 100755 --- a/ci/lint/04_install.sh +++ b/ci/lint/04_install.sh @@ -33,6 +33,17 @@ if [ -z "${SKIP_PYTHON_INSTALL}" ]; then python3 --version fi +export LINT_RUNNER_PATH="/lint_test_runner" +if [ ! -d "${LINT_RUNNER_PATH}" ]; then + ${CI_RETRY_EXE} apt-get install -y cargo + ( + cd ./test/lint/test_runner || exit 1 + cargo build + mkdir -p "${LINT_RUNNER_PATH}" + mv target/debug/test_runner "${LINT_RUNNER_PATH}" + ) +fi + # NOTE: BUMP ALSO contrib/containers/ci/ci-slim.Dockerfile ${CI_RETRY_EXE} pip3 install codespell==2.2.1 ${CI_RETRY_EXE} pip3 install flake8==5.0.4 diff --git a/ci/lint/06_script.sh b/ci/lint/06_script.sh index 93e6aeaeac93..59f70e98fdfb 100755 --- a/ci/lint/06_script.sh +++ b/ci/lint/06_script.sh @@ -27,6 +27,7 @@ test/lint/git-subtree-check.sh src/crypto/ctaes test/lint/git-subtree-check.sh src/secp256k1 test/lint/git-subtree-check.sh src/minisketch test/lint/git-subtree-check.sh src/leveldb +RUST_BACKTRACE=1 "${LINT_RUNNER_PATH}/test_runner" test/lint/check-doc.py test/lint/all-lint.py diff --git a/ci/lint/docker-entrypoint.sh b/ci/lint/docker-entrypoint.sh index 3fdbbb0761c0..4ef246697d0d 100755 --- a/ci/lint/docker-entrypoint.sh +++ b/ci/lint/docker-entrypoint.sh @@ -5,6 +5,8 @@ export LC_ALL=C # of the mounted bitcoin src dir. git config --global --add safe.directory /bitcoin +export LINT_RUNNER_PATH="/lint_test_runner" + if [ -z "$1" ]; then LOCAL_BRANCH=1 bash -ic "./ci/lint/06_script.sh" else diff --git a/src/fs.h b/src/fs.h index 270ce6fd1f00..2c344f1a271a 100644 --- a/src/fs.h +++ b/src/fs.h @@ -184,6 +184,7 @@ static inline path PathFromString(const std::string& string) * already exists or is a symlink to an existing directory. * This is a temporary workaround for an issue in libstdc++ that has been fixed * upstream [PR101510]. + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101510 */ static inline bool create_directories(const std::filesystem::path& p) { diff --git a/test/lint/README.md b/test/lint/README.md index 704922d7abe3..891262bccf70 100644 --- a/test/lint/README.md +++ b/test/lint/README.md @@ -17,6 +17,14 @@ docker run --rm -v $(pwd):/bitcoin -it bitcoin-linter After building the container once, you can simply run the last command any time you want to lint. +test runner +=========== + +To run the checks in the test runner outside the docker, use: + +```sh +( cd ./test/lint/test_runner/ && cargo fmt && cargo clippy && cargo run ) +``` check-doc.py ============ diff --git a/test/lint/test_runner/Cargo.lock b/test/lint/test_runner/Cargo.lock new file mode 100644 index 000000000000..ca83aa93310c --- /dev/null +++ b/test/lint/test_runner/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "test_runner" +version = "0.1.0" diff --git a/test/lint/test_runner/Cargo.toml b/test/lint/test_runner/Cargo.toml new file mode 100644 index 000000000000..053ce43d6ce3 --- /dev/null +++ b/test/lint/test_runner/Cargo.toml @@ -0,0 +1,12 @@ +# Copyright (c) The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or https://opensource.org/license/mit/. + +[package] +name = "test_runner" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/test/lint/test_runner/src/main.rs b/test/lint/test_runner/src/main.rs new file mode 100644 index 000000000000..b7ec9ee3b213 --- /dev/null +++ b/test/lint/test_runner/src/main.rs @@ -0,0 +1,77 @@ +// Copyright (c) The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or https://opensource.org/license/mit/. + +use std::env; +use std::path::PathBuf; +use std::process::Command; +use std::process::ExitCode; + +use String as LintError; + +/// Return the git command +fn git() -> Command { + Command::new("git") +} + +/// Return stdout +fn check_output(cmd: &mut std::process::Command) -> Result { + let out = cmd.output().expect("command error"); + if !out.status.success() { + return Err(String::from_utf8_lossy(&out.stderr).to_string()); + } + Ok(String::from_utf8(out.stdout) + .map_err(|e| format!("{e}"))? + .trim() + .to_string()) +} + +/// Return the git root as utf8, or panic +fn get_git_root() -> String { + check_output(git().args(["rev-parse", "--show-toplevel"])).unwrap() +} + +fn lint_std_filesystem() -> Result<(), LintError> { + let found = git() + .args([ + "grep", + "std::filesystem", + "--", + "./src/", + ":(exclude)src/util/fs.h", + ]) + .status() + .expect("command error") + .success(); + if found { + Err(r#" +^^^ +Direct use of std::filesystem may be dangerous and buggy. Please include and use the +fs:: namespace, which has unsafe filesystem functions marked as deleted. + "# + .to_string()) + } else { + Ok(()) + } +} + +fn main() -> ExitCode { + let test_list = [("std::filesystem check", lint_std_filesystem)]; + + let git_root = PathBuf::from(get_git_root()); + + let mut test_failed = false; + for (lint_name, lint_fn) in test_list { + // chdir to root before each lint test + env::set_current_dir(&git_root).unwrap(); + if let Err(err) = lint_fn() { + println!("{err}\n^---- Failure generated from {lint_name}!"); + test_failed = true; + } + } + if test_failed { + ExitCode::FAILURE + } else { + ExitCode::SUCCESS + } +} From b641073447ad84bd84c036a4db2563aaf48514fb Mon Sep 17 00:00:00 2001 From: fanquake Date: Mon, 30 Oct 2023 16:33:57 +0000 Subject: [PATCH 7/9] Merge bitcoin/bitcoin#28741: refactor: Fix bugprone-string-constructor warning fa56067a8f56701cbda95595592e74934af7d1cd refactor: Fix bugprone-string-constructor warning (MarcoFalke) Pull request description: String literals in C++ have a trailing null character, so the current code is fine to rely on that implicitly. However, * the sqlite documentation explicitly mentions the null character * code readers may wonder if the code is intentional * clang-tidy warns about the code via `bugprone-string-constructor` Address the points by putting the null character into the code and enable the clang-tidy `bugprone-string-constructor` check. ACKs for top commit: stickies-v: ACK fa56067a8f56701cbda95595592e74934af7d1cd Tree-SHA512: da519184d792a885a8151ffc44c8da5781f5aaae12ef768a187cc6d9e542ca8952aebc2ec6c1a05f673f29a86ef44902ee96e7b491af7b4705ad38e14624882e --- src/.clang-tidy | 1 + src/wallet/db.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/.clang-tidy b/src/.clang-tidy index bb3f4194760c..21592f7a97d3 100644 --- a/src/.clang-tidy +++ b/src/.clang-tidy @@ -1,6 +1,7 @@ Checks: ' -*, bugprone-argument-comment, +bugprone-string-constructor, bugprone-use-after-move, misc-unused-using-decls, modernize-use-default-member-init, diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 12b0c92dda1f..00778a129015 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -129,9 +129,9 @@ bool IsSQLiteFile(const fs::path& path) file.close(); - // Check the magic, see https://sqlite.org/fileformat2.html + // Check the magic, see https://sqlite.org/fileformat.html std::string magic_str(magic, 16); - if (magic_str != std::string("SQLite format 3", 16)) { + if (magic_str != std::string{"SQLite format 3\000", 16}) { return false; } From 03c9004db525c85b7d7ea554f0641926569c4cdb Mon Sep 17 00:00:00 2001 From: fanquake Date: Sun, 29 Oct 2023 10:11:24 +0100 Subject: [PATCH 8/9] Merge bitcoin/bitcoin#28727: test: replace random_bytes with random.randbytes fe3ac3700d31a6329056fee983d91dd8e4c987c1 test: replace random_bytes with randbytes #28720 (ns-xvrn) Pull request description: With Python upgraded to 3.9 replaced the `random_bytes` function in util of functional tests and replaced it's usage with `random.randbytes`. Closes #28720. ACKs for top commit: maflcko: lgtm ACK fe3ac3700d31a6329056fee983d91dd8e4c987c1 BrandonOdiwuor: ACK fe3ac3700d31a6329056fee983d91dd8e4c987c1 stickies-v: ACK fe3ac3700d31a6329056fee983d91dd8e4c987c1, thanks for picking this up kristapsk: utACK fe3ac3700d31a6329056fee983d91dd8e4c987c1 Tree-SHA512: f65a75e73ebd840c2936eb133d42bccd552f25b717c8ca25c18d06e0593e12f292389cfcc0a0b0759004b67a46ea0c8ac237973ef90f246139778230be1e64e1 Adding random back to utils.py as it is used in other places Replacing random_bytes with random.randbytes --- test/functional/mempool_datacarrier.py | 17 ++++++++--------- test/functional/p2p_net_deadlock.py | 4 ++-- test/functional/p2p_v2_misbehaving.py | 3 +-- test/functional/rpc_psbt.py | 10 +++++----- test/functional/test_framework/util.py | 4 ---- test/functional/test_framework/v2_p2p.py | 3 +-- 6 files changed, 17 insertions(+), 24 deletions(-) diff --git a/test/functional/mempool_datacarrier.py b/test/functional/mempool_datacarrier.py index 871346467ffa..57896d4097a3 100755 --- a/test/functional/mempool_datacarrier.py +++ b/test/functional/mempool_datacarrier.py @@ -13,12 +13,11 @@ ) from test_framework.test_framework import BitcoinTestFramework from test_framework.test_node import TestNode -from test_framework.util import ( - assert_raises_rpc_error, - random_bytes, -) +from test_framework.util import assert_raises_rpc_error from test_framework.wallet import MiniWallet +from random import randbytes + class DataCarrierTest(BitcoinTestFramework): def set_test_params(self): @@ -49,11 +48,11 @@ def run_test(self): self.wallet.rescan_utxos() # By default, only 80 bytes are used for data (+1 for OP_RETURN, +2 for the pushdata opcodes). - default_size_data = random_bytes(MAX_OP_RETURN_RELAY - 3) - too_long_data = random_bytes(MAX_OP_RETURN_RELAY - 2) - small_data = random_bytes(MAX_OP_RETURN_RELAY - 4) - one_byte = random_bytes(1) - zero_bytes = random_bytes(0) + default_size_data = randbytes(MAX_OP_RETURN_RELAY - 3) + too_long_data = randbytes(MAX_OP_RETURN_RELAY - 2) + small_data = randbytes(MAX_OP_RETURN_RELAY - 4) + one_byte = randbytes(1) + zero_bytes = randbytes(0) self.log.info("Testing null data transaction with default -datacarrier and -datacarriersize values.") self.test_null_data_transaction(node=self.nodes[0], data=default_size_data, success=True) diff --git a/test/functional/p2p_net_deadlock.py b/test/functional/p2p_net_deadlock.py index b1b9e67c6d47..dd052605198f 100755 --- a/test/functional/p2p_net_deadlock.py +++ b/test/functional/p2p_net_deadlock.py @@ -6,7 +6,7 @@ import threading from test_framework.messages import MAX_PROTOCOL_MESSAGE_LENGTH from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import random_bytes +from random import randbytes class NetDeadlockTest(BitcoinTestFramework): def set_test_params(self): @@ -18,7 +18,7 @@ def run_test(self): node1 = self.nodes[1] self.log.info("Simultaneously send a large message on both sides") - rand_msg = random_bytes(MAX_PROTOCOL_MESSAGE_LENGTH).hex() + rand_msg = randbytes(MAX_PROTOCOL_MESSAGE_LENGTH).hex() thread0 = threading.Thread(target=node0.sendmsgtopeer, args=(0, "unknown", rand_msg)) thread1 = threading.Thread(target=node1.sendmsgtopeer, args=(0, "unknown", rand_msg)) diff --git a/test/functional/p2p_v2_misbehaving.py b/test/functional/p2p_v2_misbehaving.py index 60c2876cf5d2..472520d227c3 100755 --- a/test/functional/p2p_v2_misbehaving.py +++ b/test/functional/p2p_v2_misbehaving.py @@ -7,7 +7,6 @@ from enum import Enum from test_framework.messages import MAGIC_BYTES -from test_framework.util import random_bytes from test_framework.p2p import P2PInterface from test_framework.test_framework import BitcoinTestFramework from test_framework.util import random_bitflip @@ -91,7 +90,7 @@ def complete_handshake(self, response): class NonEmptyVersionPacketState(EncryptedP2PState): """"Add option for sending non-empty transport version packet.""" def complete_handshake(self, response): - self.transport_version = random_bytes(5) + self.transport_version = random.randbytes(5) return super().complete_handshake(response) diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py index 5788ff158336..10173efc645a 100755 --- a/test/functional/rpc_psbt.py +++ b/test/functional/rpc_psbt.py @@ -7,6 +7,7 @@ from decimal import Decimal from itertools import product +from random import randbytes from test_framework.descriptors import descsum_create from test_framework.key import ECKey @@ -32,7 +33,6 @@ assert_greater_than, assert_raises_rpc_error, find_output, - random_bytes, ) from test_framework.wallet_util import bytes_to_wif @@ -598,10 +598,10 @@ def test_psbt_input_keys(psbt_input, keys): self.log.info("Test decoding PSBT with per-input preimage types") # note that the decodepsbt RPC doesn't check whether preimages and hashes match - hash_ripemd160, preimage_ripemd160 = random_bytes(20), random_bytes(50) - hash_sha256, preimage_sha256 = random_bytes(32), random_bytes(50) - hash_hash160, preimage_hash160 = random_bytes(20), random_bytes(50) - hash_hash256, preimage_hash256 = random_bytes(32), random_bytes(50) + hash_ripemd160, preimage_ripemd160 = randbytes(20), randbytes(50) + hash_sha256, preimage_sha256 = randbytes(32), randbytes(50) + hash_hash160, preimage_hash160 = randbytes(20), randbytes(50) + hash_hash256, preimage_hash256 = randbytes(32), randbytes(50) tx = CTransaction() tx.vin = [CTxIn(outpoint=COutPoint(hash=int('aa' * 32, 16), n=0), scriptSig=b""), diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index 3a2bc8c1769b..b2830b1612f7 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -311,10 +311,6 @@ def sha256sum_file(filename): d = f.read(4096) return h.digest() -# TODO: Remove and use random.randbytes(n) directly -def random_bytes(n): - """Return a random bytes object of length n.""" - return random.randbytes(n) # RPC/P2P connection constants and functions ############################################ diff --git a/test/functional/test_framework/v2_p2p.py b/test/functional/test_framework/v2_p2p.py index c1c6e7afe258..dcc90c38d537 100644 --- a/test/functional/test_framework/v2_p2p.py +++ b/test/functional/test_framework/v2_p2p.py @@ -12,7 +12,6 @@ from .crypto.hkdf import hkdf_sha256 from .key import TaggedHash from .messages import MAGIC_BYTES -from .util import random_bytes CHACHA20POLY1305_EXPANSION = 16 @@ -156,7 +155,7 @@ def generate_keypair_and_garbage(self, garbage_len=None): self.privkey_ours, self.ellswift_ours = ellswift_create() if garbage_len is None: garbage_len = random.randrange(MAX_GARBAGE_LEN + 1) - self.sent_garbage = random_bytes(garbage_len) + self.sent_garbage = random.randbytes(garbage_len) return self.ellswift_ours + self.sent_garbage def initiate_v2_handshake(self): From 63494e8dbd531052fb57cc58d49026ff325c2f2b Mon Sep 17 00:00:00 2001 From: fanquake Date: Thu, 19 Oct 2023 13:18:31 +0100 Subject: [PATCH 9/9] Merge bitcoin/bitcoin#22764: build: Include qt sources for parsing with extract_strings.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit b59b31ae0b04054c5cf225dad87046d3771707fc build: Drop redundant qt/bitcoin.cpp (Hennadii Stepanov) d90ad5a42ec6f48d0e504edc16d41c8ef266cc1d build: Include qt sources for parsing with extract_strings.py (Hennadii Stepanov) Pull request description: On master (4fc15d15667d9d9c4fb5515ce73c05b4596298ec) some strings are still untranslated. This PR fixes this issue. To verify: 1) `./autogen.sh && ./configure && make -C src translate` _before_ applying this change 2) apply this change 3) `./autogen.sh && ./configure && make -C src translate` _after_ applying this change The result of `git diff src/qt/bitcoinstrings.cpp`: ```diff --- a/src/qt/bitcoinstrings.cpp +++ b/src/qt/bitcoinstrings.cpp @@ -126,6 +126,7 @@ QT_TRANSLATE_NOOP("bitcoin-core", "" "You need to rebuild the database using -reindex to go back to unpruned " "mode. This will redownload the entire blockchain"), QT_TRANSLATE_NOOP("bitcoin-core", "%s is set very high!"), +QT_TRANSLATE_NOOP("bitcoin-core", "(press q to shutdown and continue later)"), QT_TRANSLATE_NOOP("bitcoin-core", "-maxmempool must be at least %d MB"), QT_TRANSLATE_NOOP("bitcoin-core", "A fatal internal error occurred, see debug.log for details"), QT_TRANSLATE_NOOP("bitcoin-core", "Cannot resolve -%s address: '%s'"), @@ -204,6 +205,8 @@ QT_TRANSLATE_NOOP("bitcoin-core", "SQLiteDatabase: Failed to prepare statement t QT_TRANSLATE_NOOP("bitcoin-core", "SQLiteDatabase: Failed to read database verification error: %s"), QT_TRANSLATE_NOOP("bitcoin-core", "SQLiteDatabase: Unexpected application id. Expected %u, got %u"), QT_TRANSLATE_NOOP("bitcoin-core", "Section [%s] is not recognized."), +QT_TRANSLATE_NOOP("bitcoin-core", "Settings file could not be read"), +QT_TRANSLATE_NOOP("bitcoin-core", "Settings file could not be written"), QT_TRANSLATE_NOOP("bitcoin-core", "Signing transaction failed"), QT_TRANSLATE_NOOP("bitcoin-core", "Specified -walletdir \"%s\" does not exist"), QT_TRANSLATE_NOOP("bitcoin-core", "Specified -walletdir \"%s\" is a relative path"), @@ -242,4 +245,5 @@ QT_TRANSLATE_NOOP("bitcoin-core", "User Agent comment (%s) contains unsafe chara QT_TRANSLATE_NOOP("bitcoin-core", "Verifying blocks…"), QT_TRANSLATE_NOOP("bitcoin-core", "Verifying wallet(s)…"), QT_TRANSLATE_NOOP("bitcoin-core", "Wallet needed to be rewritten: restart %s to complete"), +QT_TRANSLATE_NOOP("bitcoin-core", "press q to shutdown"), }; ``` ACKs for top commit: ryanofsky: Code review ACK b59b31ae0b04054c5cf225dad87046d3771707fc. Being able to use `_()` macro in qt would allow simplifying some code, for example replacing repetitive: TheCharlatan: ACK b59b31ae0b04054c5cf225dad87046d3771707fc Tree-SHA512: 13d9d86b487a1b6e718ae96c198a0a927c881bf33df318412793ec9efba3a7e59cfa836204f73f5b53ff4c99edce778c11bffaa88138b80e37b71e36df6b816f --- src/Makefile.qt.include | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 04652688dbef..06de1e2237dc 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -489,11 +489,14 @@ SECONDARY: $(QT_QM) $(srcdir)/qt/dashstrings.cpp: FORCE @test -n $(XGETTEXT) || echo "xgettext is required for updating translations" - $(AM_V_GEN) cd $(srcdir); XGETTEXT=$(XGETTEXT) COPYRIGHT_HOLDERS="$(COPYRIGHT_HOLDERS)" $(PYTHON) ../share/qt/extract_strings_qt.py $(libbitcoin_node_a_SOURCES) $(libbitcoin_wallet_a_SOURCES) $(libbitcoin_common_a_SOURCES) $(libbitcoin_zmq_a_SOURCES) $(libbitcoin_consensus_a_SOURCES) $(libbitcoin_util_a_SOURCES) + $(AM_V_GEN) cd $(srcdir); XGETTEXT=$(XGETTEXT) COPYRIGHT_HOLDERS="$(COPYRIGHT_HOLDERS)" $(PYTHON) ../share/qt/extract_strings_qt.py \ + $(libbitcoin_node_a_SOURCES) $(libbitcoin_wallet_a_SOURCES) $(libbitcoin_common_a_SOURCES) \ + $(libbitcoin_zmq_a_SOURCES) $(libbitcoin_consensus_a_SOURCES) $(libbitcoin_util_a_SOURCES) \ + $(BITCOIN_QT_BASE_CPP) $(BITCOIN_QT_WINDOWS_CPP) $(BITCOIN_QT_WALLET_CPP) $(BITCOIN_QT_H) $(BITCOIN_MM) # The resulted dash_en.xlf source file should follow Transifex requirements. # See: https://docs.transifex.com/formats/xliff#how-to-distinguish-between-a-source-file-and-a-translation-file -translate: $(srcdir)/qt/dashstrings.cpp $(QT_FORMS_UI) $(QT_FORMS_UI) $(BITCOIN_QT_BASE_CPP) qt/bitcoin.cpp $(BITCOIN_QT_WINDOWS_CPP) $(BITCOIN_QT_WALLET_CPP) $(BITCOIN_QT_H) $(BITCOIN_MM) +translate: $(srcdir)/qt/dashstrings.cpp $(QT_FORMS_UI) $(QT_FORMS_UI) $(BITCOIN_QT_BASE_CPP) $(BITCOIN_QT_WINDOWS_CPP) $(BITCOIN_QT_WALLET_CPP) $(BITCOIN_QT_H) $(BITCOIN_MM) @test -n $(LUPDATE) || echo "lupdate is required for updating translations" $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(LUPDATE) -no-obsolete -I $(srcdir) -locations relative $^ -ts $(srcdir)/qt/locale/dash_en.ts @test -n $(LCONVERT) || echo "lconvert is required for updating translations"