diff --git a/.github/workflows/scripttest.yml b/.github/workflows/scripttest.yml index 9da846f4a46..68eec17c475 100644 --- a/.github/workflows/scripttest.yml +++ b/.github/workflows/scripttest.yml @@ -42,10 +42,10 @@ jobs: with: path: ./src - - name: run_bandwidth_crawler.py + - name: run_crawler.py uses: ./.github/actions/timeout with: - command: python ./scripts/bandwidth_crawler/run_bandwidth_crawler.py --fragile + command: python ./scripts/crawler/run_crawler.py --fragile duration: ${{inputs.duration}} - name: run_exit_node.py diff --git a/scripts/application_tester/tribler_apptester/config/tribler_config.spec b/scripts/application_tester/tribler_apptester/config/tribler_config.spec index b9c4bc41530..32d418adea6 100644 --- a/scripts/application_tester/tribler_apptester/config/tribler_config.spec +++ b/scripts/application_tester/tribler_apptester/config/tribler_config.spec @@ -22,9 +22,6 @@ ec_keypair_filename = string(default='ec_multichain.pem') testnet_keypair_filename = string(default='ec_trustchain_testnet.pem') testnet = boolean(default=False) -[bandwidth_accounting] -testnet = boolean(default=False) - [bootstrap] enabled = boolean(default=True) max_download_rate = integer(min=1, default=1000000) diff --git a/scripts/bandwidth_crawler/README.md b/scripts/crawler/README.md similarity index 73% rename from scripts/bandwidth_crawler/README.md rename to scripts/crawler/README.md index 60e572d85da..658140ffde6 100644 --- a/scripts/bandwidth_crawler/README.md +++ b/scripts/crawler/README.md @@ -1,4 +1,4 @@ -# Bandwidth Crawler +# Crawler ## Requirements @@ -8,5 +8,5 @@ ## Execution ```shell -python3 run_bandwidth_crawler.py +python3 run_crawler.py ``` \ No newline at end of file diff --git a/scripts/bandwidth_crawler/run_bandwidth_crawler.py b/scripts/crawler/run_crawler.py similarity index 80% rename from scripts/bandwidth_crawler/run_bandwidth_crawler.py rename to scripts/crawler/run_crawler.py index 4561e3f8360..2dcac089d84 100644 --- a/scripts/bandwidth_crawler/run_bandwidth_crawler.py +++ b/scripts/crawler/run_crawler.py @@ -1,5 +1,7 @@ """ -This executable script starts a Tribler instance and joins the BandwidthAccountingCommunity. +This is an example of a Tribler crawler. It was originally written by @devos50 +to crawl the BandwidthCommunity's data and was later adapted to serve +as just an example of a generic Tribler crawler. """ import argparse import logging @@ -8,7 +10,6 @@ from asyncio import ensure_future, get_event_loop from pathlib import Path -from tribler.core.components.bandwidth_accounting.bandwidth_accounting_component import BandwidthAccountingComponent from tribler.core.components.ipv8.ipv8_component import Ipv8Component from tribler.core.components.key.key_component import KeyComponent from tribler.core.components.session import Session @@ -16,6 +17,19 @@ from tribler.core.utilities.utilities import make_async_loop_fragile +async def crawler_session(session_config: TriblerConfig): + session = Session( + config=session_config, + components=[ + KeyComponent(), + Ipv8Component(), + # Put Your Component Here + ]) + signal.signal(signal.SIGTERM, lambda signum, stack: session.shutdown_event.set) + async with session: + await session.shutdown_event.wait() + + class PortAction(argparse.Action): def __call__(self, _, namespace, value, option_string=None): if not 0 < value < 2 ** 16: @@ -23,14 +37,6 @@ def __call__(self, _, namespace, value, option_string=None): setattr(namespace, self.dest, value) -async def crawler_session(session_config: TriblerConfig): - session = Session(session_config, - [KeyComponent(), Ipv8Component(), BandwidthAccountingComponent(crawler_mode=True)]) - signal.signal(signal.SIGTERM, lambda signum, stack: session.shutdown_event.set) - async with session: - await session.shutdown_event.wait() - - if __name__ == "__main__": parser = argparse.ArgumentParser(description=('Start a crawler in the bandwidth accounting community')) parser.add_argument('--statedir', '-s', default='bw_crawler', type=str, help='Use an alternate statedir') @@ -45,13 +51,10 @@ async def crawler_session(session_config: TriblerConfig): config = TriblerConfig.load(state_dir=state_dir) config.tunnel_community.enabled = False - config.libtorrent.enabled = False config.bootstrap.enabled = False - config.chant.enabled = False config.torrent_checking.enabled = False config.api.http_enabled = True config.api.http_port = args.restapi - config.bandwidth_accounting.outgoing_query_interval = 5 loop = get_event_loop() if args.fragile: diff --git a/scripts/exit_node/run_exit_node.py b/scripts/exit_node/run_exit_node.py index c7c220337ce..8150aeb51f2 100644 --- a/scripts/exit_node/run_exit_node.py +++ b/scripts/exit_node/run_exit_node.py @@ -15,7 +15,6 @@ from ipv8.util import run_forever from tribler.core import notifications -from tribler.core.components.bandwidth_accounting.bandwidth_accounting_component import BandwidthAccountingComponent from tribler.core.components.ipv8.ipv8_component import Ipv8Component from tribler.core.components.key.key_component import KeyComponent from tribler.core.components.restapi.restapi_component import RESTComponent @@ -33,7 +32,6 @@ def components_gen(): yield KeyComponent() yield RESTComponent() yield Ipv8Component() - yield BandwidthAccountingComponent() yield TunnelsComponent() diff --git a/src/run_tribler_headless.py b/src/run_tribler_headless.py index 3f07fdce012..37932a1a73d 100644 --- a/src/run_tribler_headless.py +++ b/src/run_tribler_headless.py @@ -114,7 +114,6 @@ async def signal_handler(sig): if options.testnet: config.tunnel_community.testnet = True config.chant.testnet = True - config.bandwidth_accounting.testnet = True self.session = Session(config, components=list(components_gen(config))) try: diff --git a/src/tribler/core/components/bandwidth_accounting/__init__.py b/src/tribler/core/components/bandwidth_accounting/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/tribler/core/components/bandwidth_accounting/bandwidth_accounting_component.py b/src/tribler/core/components/bandwidth_accounting/bandwidth_accounting_component.py deleted file mode 100644 index 9686af225e9..00000000000 --- a/src/tribler/core/components/bandwidth_accounting/bandwidth_accounting_component.py +++ /dev/null @@ -1,55 +0,0 @@ -from tribler.core.components.bandwidth_accounting.community.bandwidth_accounting_community import ( - BandwidthAccountingCommunity, - BandwidthAccountingTestnetCommunity, -) -from tribler.core.components.bandwidth_accounting.db.database import BandwidthDatabase -from tribler.core.components.component import Component -from tribler.core.components.ipv8.ipv8_component import Ipv8Component -from tribler.core.utilities.simpledefs import STATEDIR_DB_DIR - - -class BandwidthAccountingComponent(Component): - community: BandwidthAccountingCommunity = None - _ipv8_component: Ipv8Component = None - database: BandwidthDatabase = None - - def __init__(self, crawler_mode=False): - super().__init__() - self.crawler_mode = crawler_mode - - async def run(self): - await super().run() - self._ipv8_component = await self.require_component(Ipv8Component) - - config = self.session.config - if config.general.testnet or config.bandwidth_accounting.testnet: - bandwidth_cls = BandwidthAccountingTestnetCommunity - else: - bandwidth_cls = BandwidthAccountingCommunity - - if self.crawler_mode: - store_all_transactions = True - unlimited_peers = True - else: - store_all_transactions = False - unlimited_peers = False - - db_name = "bandwidth_gui_test.db" if config.gui_test_mode else f"{bandwidth_cls.DB_NAME}.db" - database_path = config.state_dir / STATEDIR_DB_DIR / db_name - self.database = BandwidthDatabase(database_path, self._ipv8_component.peer.public_key.key_to_bin(), - store_all_transactions=store_all_transactions) - - kwargs = {"max_peers": -1} if unlimited_peers else {} - self.community = bandwidth_cls(self._ipv8_component.peer, - self._ipv8_component.ipv8.endpoint, - self._ipv8_component.ipv8.network, - settings=config.bandwidth_accounting, - database=self.database, - **kwargs) - - self._ipv8_component.initialise_community_by_default(self.community) - - async def shutdown(self): - await super().shutdown() - if self._ipv8_component and self.community: - await self._ipv8_component.unload_community(self.community) diff --git a/src/tribler/core/components/bandwidth_accounting/community/__init__.py b/src/tribler/core/components/bandwidth_accounting/community/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/tribler/core/components/bandwidth_accounting/community/bandwidth_accounting_community.py b/src/tribler/core/components/bandwidth_accounting/community/bandwidth_accounting_community.py deleted file mode 100644 index 4c3dd4df1a7..00000000000 --- a/src/tribler/core/components/bandwidth_accounting/community/bandwidth_accounting_community.py +++ /dev/null @@ -1,206 +0,0 @@ -from __future__ import annotations - -from asyncio import Future -from binascii import unhexlify -from random import Random -from typing import Dict - -from ipv8.peer import Peer -from ipv8.requestcache import RequestCache -from ipv8.types import Address - -from tribler.core.components.bandwidth_accounting.community.cache import BandwidthTransactionSignCache -from tribler.core.components.bandwidth_accounting.community.payload import ( - BandwidthTransactionPayload, - BandwidthTransactionQueryPayload, -) -from tribler.core.components.bandwidth_accounting.db.database import BandwidthDatabase -from tribler.core.components.bandwidth_accounting.db.transaction import BandwidthTransactionData, EMPTY_SIGNATURE -from tribler.core.components.ipv8.tribler_community import TriblerCommunity -from tribler.core.utilities.unicode import hexlify - - -class BandwidthAccountingCommunity(TriblerCommunity): - """ - Community around bandwidth accounting and payouts. - """ - community_id = unhexlify('79b25f2867739261780faefede8f25038de9975d') - DB_NAME = 'bandwidth' - version = b'\x02' - - def __init__(self, *args, **kwargs) -> None: - """ - Initialize the community. - :param persistence: The database that stores transactions, will be created if not provided. - :param database_path: The path at which the database will be created. Defaults to the current working directory. - """ - self.database: BandwidthDatabase = kwargs.pop('database', None) - self.random = Random() - - super().__init__(*args, **kwargs) - - self.request_cache = RequestCache() - self.my_pk = self.my_peer.public_key.key_to_bin() - - self.add_message_handler(BandwidthTransactionPayload, self.received_transaction) - self.add_message_handler(BandwidthTransactionQueryPayload, self.received_query) - - self.register_task("query_peers", self.query_random_peer, interval=self.settings.outgoing_query_interval) - - self.logger.info("Started bandwidth accounting community with public key %s", hexlify(self.my_pk)) - - def construct_signed_transaction(self, peer: Peer, amount: int) -> BandwidthTransactionData: - """ - Construct a new signed bandwidth transaction. - :param peer: The counterparty of the transaction. - :param amount: The amount of bytes to payout. - :return A signed BandwidthTransaction. - """ - peer_pk = peer.public_key.key_to_bin() - latest_tx = self.database.get_latest_transaction(self.my_pk, peer_pk) - total_amount = latest_tx.amount + amount if latest_tx else amount - next_seq_num = latest_tx.sequence_number + 1 if latest_tx else 1 - tx = BandwidthTransactionData(next_seq_num, self.my_pk, peer_pk, EMPTY_SIGNATURE, EMPTY_SIGNATURE, total_amount) - tx.sign(self.my_peer.key, as_a=True) - return tx - - def do_payout(self, peer: Peer, amount: int) -> Future: - """ - Conduct a payout with a given amount of bytes to a peer. - :param peer: The counterparty of the payout. - :param amount: The amount of bytes to payout. - :return A Future that fires when the counterparty has acknowledged the payout. - """ - tx = self.construct_signed_transaction(peer, amount) - self.database.BandwidthTransaction.insert(tx) - cache = self.request_cache.add(BandwidthTransactionSignCache(self, tx)) - self.send_transaction(tx, peer.address, cache.number) - - return cache.future - - def send_transaction(self, transaction: BandwidthTransactionData, address: Address, request_id: int) -> None: - """ - Send a provided transaction to another party. - :param transaction: The BandwidthTransaction to send to the other party. - :param peer: The IP address and port of the peer. - :param request_id: The identifier of the message, is usually provided by a request cache. - """ - payload = BandwidthTransactionPayload.from_transaction(transaction, request_id) - packet = self._ez_pack(self._prefix, 1, [payload], False) - self.endpoint.send(address, packet) - - def received_transaction(self, source_address: Address, data: bytes) -> None: - """ - Callback when we receive a transaction from another peer. - :param source_address: The network address of the peer that has sent us the transaction. - :param data: The serialized, raw data in the packet. - """ - payload = self._ez_unpack_noauth(BandwidthTransactionPayload, data, global_time=False) - tx = BandwidthTransactionData.from_payload(payload) - - if not tx.is_valid(): - self.logger.info("Transaction %s not valid, ignoring it", tx) - return - - if payload.public_key_a == self.my_pk or payload.public_key_b == self.my_pk: - # This transaction involves this peer. - latest_tx = self.database.get_latest_transaction(tx.public_key_a, tx.public_key_b) - if payload.public_key_b == self.my_peer.public_key.key_to_bin(): - from_peer = Peer(payload.public_key_a, source_address) - if latest_tx: - # Check if the amount in the received transaction is higher than the amount of the latest one - # in the database. - if payload.amount > latest_tx.amount: - # Sign it, store it, and send it back - tx.sign(self.my_peer.key, as_a=False) - self.database.BandwidthTransaction.insert(tx) - self.send_transaction(tx, from_peer.address, payload.request_id) - else: - self.logger.info("Received older bandwidth transaction from peer %s:%d - " - "sending back the latest one", *from_peer.address) - self.send_transaction(latest_tx, from_peer.address, payload.request_id) - else: - # This transaction is the first one with party A. Sign it, store it, and send it back. - tx.sign(self.my_peer.key, as_a=False) - self.database.BandwidthTransaction.insert(tx) - from_peer = Peer(payload.public_key_a, source_address) - self.send_transaction(tx, from_peer.address, payload.request_id) - elif payload.public_key_a == self.my_peer.public_key.key_to_bin(): - # It seems that we initiated this transaction. Check if we are waiting for it. - cache = self.request_cache.get("bandwidth-tx-sign", payload.request_id) - if not cache: - self.logger.info("Received bandwidth transaction %s without associated cache entry, ignoring it", - tx) - return - - if not latest_tx or (latest_tx and latest_tx.amount >= tx.amount): - self.database.BandwidthTransaction.insert(tx) - - cache.future.set_result(tx) - else: - # This transaction involves two unknown peers. We can add it to our database. - self.database.BandwidthTransaction.insert(tx) - - def query_random_peer(self) -> None: - """ - Query a random peer neighbouring peer and ask their bandwidth transactions. - """ - peers = list(self.network.verified_peers) - if peers: - random_peer = self.random.choice(peers) - self.query_transactions(random_peer) - - def query_transactions(self, peer: Peer) -> None: - """ - Query the transactions of a specific peer and ask for their bandwidth transactions. - :param peer: The peer to send the query to. - """ - self.logger.info("Querying the transactions of peer %s:%d", *peer.address) - payload = BandwidthTransactionQueryPayload() - packet = self._ez_pack(self._prefix, 2, [payload], False) - self.endpoint.send(peer.address, packet) - - def received_query(self, source_address: Address, data: bytes) -> None: - """ - We received a query from another peer. - :param source_address: The network address of the peer that has sent us the query. - :param data: The serialized, raw data in the packet. - """ - my_txs = self.database.get_my_latest_transactions(limit=self.settings.max_tx_returned_in_query) - self.logger.debug("Sending %d bandwidth transaction(s) to peer %s:%d", len(my_txs), *source_address) - for tx in my_txs: - self.send_transaction(tx, source_address, 0) - - def get_statistics(self) -> Dict: - """ - Return a dictionary with bandwidth statistics, including the total amount of bytes given and taken, and the - number of unique peers you helped/that helped you. - :return: A dictionary with statistics. - """ - my_pk = self.my_peer.public_key.key_to_bin() - return { - "id": hexlify(my_pk), - "total_given": self.database.get_total_given(my_pk), - "total_taken": self.database.get_total_taken(my_pk), - "num_peers_helped": self.database.get_num_peers_helped(my_pk), - "num_peers_helped_by": self.database.get_num_peers_helped_by(my_pk) - } - - async def unload(self) -> None: - """ - Unload this community by shutting down the request cache and database. - """ - self.logger.info("Unloading the bandwidth accounting community.") - - await self.request_cache.shutdown() - self.database.shutdown() - - await super().unload() - - -class BandwidthAccountingTestnetCommunity(BandwidthAccountingCommunity): - """ - This community defines the testnet for bandwidth accounting. - """ - DB_NAME = 'bandwidth_testnet' - community_id = unhexlify('e7de42f46f9ef225f4a5fc32ed0a0ce9a8ea4af6') diff --git a/src/tribler/core/components/bandwidth_accounting/community/cache.py b/src/tribler/core/components/bandwidth_accounting/community/cache.py deleted file mode 100644 index c58682b11b7..00000000000 --- a/src/tribler/core/components/bandwidth_accounting/community/cache.py +++ /dev/null @@ -1,42 +0,0 @@ -from __future__ import annotations - -from asyncio import Future -from typing import TYPE_CHECKING - -from ipv8.requestcache import RandomNumberCache - -from tribler.core.components.bandwidth_accounting.db.transaction import BandwidthTransactionData - -if TYPE_CHECKING: - from tribler.core.components.bandwidth_accounting.community.bandwidth_accounting_community import \ - BandwidthAccountingCommunity - - -class BandwidthTransactionSignCache(RandomNumberCache): - """ - This cache keeps track of pending bandwidth transaction signature requests. - """ - - def __init__(self, community: BandwidthAccountingCommunity, transaction: BandwidthTransactionData) -> None: - """ - Initialize the cache. - :param community: The bandwidth community associated with this cache. - :param transaction: The transaction that will be signed by the counterparty. - """ - super().__init__(community.request_cache, "bandwidth-tx-sign") - self.transaction = transaction - self.future = Future() - self.register_future(self.future) - - @property - def timeout_delay(self) -> float: - """ - :return The timeout of this sign cache, defaults to one hour. - """ - return 3600.0 - - def on_timeout(self) -> None: - """ - Invoked when the cache times out. - """ - self._logger.info("Sign request for transaction %s timed out!", self.transaction) diff --git a/src/tribler/core/components/bandwidth_accounting/community/payload.py b/src/tribler/core/components/bandwidth_accounting/community/payload.py deleted file mode 100644 index 9dcb024facb..00000000000 --- a/src/tribler/core/components/bandwidth_accounting/community/payload.py +++ /dev/null @@ -1,35 +0,0 @@ -from __future__ import annotations - -from ipv8.messaging.lazy_payload import VariablePayload, vp_compile - - -@vp_compile -class BandwidthTransactionPayload(VariablePayload): - """ - Payload for a message containing a bandwidth transaction. - """ - msg_id = 1 - format_list = ['I', '74s', '74s', '64s', '64s', 'Q', 'Q', 'I'] - names = ["sequence_number", "public_key_a", "public_key_b", "signature_a", "signature_b", "amount", - "timestamp", "request_id"] - - @classmethod - def from_transaction(cls, transaction, request_id: int) -> BandwidthTransactionPayload: - return BandwidthTransactionPayload( - transaction.sequence_number, - transaction.public_key_a, - transaction.public_key_b, - transaction.signature_a, - transaction.signature_b, - transaction.amount, - transaction.timestamp, - request_id - ) - - -@vp_compile -class BandwidthTransactionQueryPayload(VariablePayload): - """ - (empty) payload for an outgoing query to fetch transactions by the counterparty. - """ - msg_id = 2 diff --git a/src/tribler/core/components/bandwidth_accounting/db/__init__.py b/src/tribler/core/components/bandwidth_accounting/db/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/tribler/core/components/bandwidth_accounting/db/database.py b/src/tribler/core/components/bandwidth_accounting/db/database.py deleted file mode 100644 index 2863285b717..00000000000 --- a/src/tribler/core/components/bandwidth_accounting/db/database.py +++ /dev/null @@ -1,191 +0,0 @@ -from pathlib import Path -from typing import List, Optional, Union - -from pony.orm import count, db_session, desc, select - -from tribler.core.components.bandwidth_accounting.db import history, misc, transaction as db_transaction -from tribler.core.components.bandwidth_accounting.db.transaction import BandwidthTransactionData -from tribler.core.utilities.db_corruption_handling.base import handle_db_if_corrupted -from tribler.core.utilities.pony_utils import TrackedDatabase -from tribler.core.utilities.utilities import MEMORY_DB - - -class BandwidthDatabase: - """ - Simple database that stores bandwidth transactions in Tribler as a work graph. - """ - CURRENT_DB_VERSION = 9 - MAX_HISTORY_ITEMS = 100 # The maximum number of history items to store. - - def __init__(self, db_path: Union[Path, type(MEMORY_DB)], my_pub_key: bytes, - store_all_transactions: bool = False) -> None: - """ - Sets up the persistence layer ready for use. - :param db_path: The full path of the database. - :param my_pub_key: The public key of the user operating the database. - :param store_all_transactions: Whether we store all pairwise transactions in the database. This is disabled by - default and used for data collection purposes. - """ - self.my_pub_key = my_pub_key - self.store_all_transactions = store_all_transactions - - self.database = TrackedDatabase() - - # This attribute is internally called by Pony on startup, though pylint cannot detect it - # with the static analysis. - # pylint: disable=unused-variable - - @self.database.on_connect - def sqlite_sync_pragmas(_, connection): - cursor = connection.cursor() - cursor.execute("PRAGMA journal_mode = WAL") - cursor.execute("PRAGMA synchronous = 1") - cursor.execute("PRAGMA temp_store = 2") - # pylint: enable=unused-variable - - self.MiscData = misc.define_binding(self.database) - self.BandwidthTransaction = db_transaction.define_binding(self) - self.BandwidthHistory = history.define_binding(self) - - if db_path is MEMORY_DB: - create_db = True - db_path_string = ":memory:" - else: - # We need to handle the database corruption case before determining the state of the create_db flag. - handle_db_if_corrupted(db_path) - create_db = not db_path.is_file() - db_path_string = str(db_path) - - self.database.bind(provider='sqlite', filename=db_path_string, create_db=create_db, timeout=120.0) - self.database.generate_mapping(create_tables=create_db) - - if create_db: - with db_session: - self.MiscData(name="db_version", value=str(self.CURRENT_DB_VERSION)) - - def has_transaction(self, transaction: BandwidthTransactionData) -> bool: - """ - Return whether a transaction is persisted to the database. - :param transaction: The transaction to check. - :return: A boolean value, indicating whether we have the transaction in the database or not. - """ - return self.BandwidthTransaction.exists(public_key_a=transaction.public_key_a, - public_key_b=transaction.public_key_b, - sequence_number=transaction.sequence_number) - - @db_session - def get_my_latest_transactions(self, limit: Optional[int] = None) -> List[BandwidthTransactionData]: - """ - Return all latest transactions involving you. - :param limit: An optional integer, to limit the number of results returned. Pass None to get all results. - :return A list containing all latest transactions involving you. - """ - results = [] - db_txs = select(tx for tx in self.BandwidthTransaction - if tx.public_key_a == self.my_pub_key or tx.public_key_b == self.my_pub_key) \ - .limit(limit) - for db_tx in db_txs: - results.append(BandwidthTransactionData.from_db(db_tx)) - return results - - @db_session - def get_latest_transaction(self, public_key_a: bytes, public_key_b: bytes) -> BandwidthTransactionData: - """ - Return the latest transaction between two parties, or None if no such transaction exists. - :param public_key_a: The public key of the party transferring the bandwidth. - :param public_key_b: The public key of the party receiving the bandwidth. - :return The latest transaction between the two specified parties, or None if no such transaction exists. - """ - db_tx = select(tx for tx in self.BandwidthTransaction - if public_key_a == tx.public_key_a and public_key_b == tx.public_key_b) \ - .order_by(lambda tx: desc(tx.sequence_number)) \ - .first() - - return BandwidthTransactionData.from_db(db_tx) if db_tx else None - - @db_session - def get_latest_transactions(self, public_key: bytes, limit: Optional[int] = 100) -> List[BandwidthTransactionData]: - """ - Return the latest transactions of a given public key, or an empty list if no transactions exist. - :param public_key: The public key of the party transferring the bandwidth. - :param limit: The number of transactions to return. (Default: 100) - :return The latest transactions of the specified public key, or an empty list if no transactions exist. - """ - db_txs = select(tx for tx in self.BandwidthTransaction - if public_key in (tx.public_key_a, tx.public_key_b)) \ - .limit(limit) - return [BandwidthTransactionData.from_db(db_txn) for db_txn in db_txs] - - @db_session - def get_total_taken(self, public_key: bytes) -> int: - """ - Return the total amount of bandwidth taken by a given party. - :param public_key: The public key of the peer of which we want to determine the total taken. - :return The total amount of bandwidth taken by the specified peer, in bytes. - """ - return select(transaction.amount for transaction in self.BandwidthTransaction - if transaction.public_key_a == public_key).sum() - - @db_session - def get_total_given(self, public_key: bytes) -> int: - """ - Return the total amount of bandwidth given by a given party. - :param public_key: The public key of the peer of which we want to determine the total given. - :return The total amount of bandwidth given by the specified peer, in bytes. - """ - return select(transaction.amount for transaction in self.BandwidthTransaction - if transaction.public_key_b == public_key).sum() - - @db_session - def get_balance(self, public_key: bytes) -> int: - """ - Return the bandwidth balance (total given - total taken) of a specific peer. - :param public_key: The public key of the peer of which we want to determine the balance. - :return The bandwidth balance the specified peer, in bytes. - """ - return self.get_total_given(public_key) - self.get_total_taken(public_key) - - def get_my_balance(self) -> int: - """ - Return your bandwidth balance, which is the total amount given minus the total amount taken. - :return Your bandwidth balance, in bytes. - """ - return self.get_balance(self.my_pub_key) - - @db_session - def get_num_peers_helped(self, public_key: bytes) -> int: - """ - Return the number of unique peers that a peer with the provided public key has helped. - :param public_key: The public key of the peer of which we want to determine this number. - :return The unique number of peers helped by the specified peer. - """ - result = list(select(count(g.public_key_b) for g in self.BandwidthTransaction if g.public_key_a == public_key)) - return result[0] - - @db_session - def get_num_peers_helped_by(self, public_key: bytes) -> int: - """ - Return the number of unique peers that a peer with the provided public key has been helped by. - :param public_key: The public key of the peer of which we want to determine this number. - :return The unique number of peers that helped the specified peer. - """ - result = list(select(count(g.public_key_a) for g in self.BandwidthTransaction if g.public_key_b == public_key)) - return result[0] - - @db_session - def get_history(self) -> List: - """ - Get the history of your bandwidth balance as an ordered list. - :return A list. Each item in this list contains a timestamp and a balance. - """ - history = [] - for history_item in self.BandwidthHistory.select().order_by(self.BandwidthHistory.timestamp): - history.append({"timestamp": history_item.timestamp, "balance": history_item.balance}) - - return history - - def shutdown(self) -> None: - """ - Shutdown the database. - """ - self.database.disconnect() diff --git a/src/tribler/core/components/bandwidth_accounting/db/history.py b/src/tribler/core/components/bandwidth_accounting/db/history.py deleted file mode 100644 index 7c299bf51bd..00000000000 --- a/src/tribler/core/components/bandwidth_accounting/db/history.py +++ /dev/null @@ -1,17 +0,0 @@ -from pony.orm import PrimaryKey, Required - - -def define_binding(bandwidth_database): - db = bandwidth_database.database - - class BandwidthHistory(db.Entity): - """ - This ORM class represents a mutation of ones bandwidth balance. - We store the last 100 mutations in ones bandwidth balance. - """ - - rowid = PrimaryKey(int, auto=True) - timestamp = Required(int, size=64) - balance = Required(int, size=64) - - return BandwidthHistory diff --git a/src/tribler/core/components/bandwidth_accounting/db/misc.py b/src/tribler/core/components/bandwidth_accounting/db/misc.py deleted file mode 100644 index 1adf5f4c50d..00000000000 --- a/src/tribler/core/components/bandwidth_accounting/db/misc.py +++ /dev/null @@ -1,12 +0,0 @@ -from pony.orm import Optional, PrimaryKey - - -def define_binding(db): - class MiscData(db.Entity): - """ - This binding is used to store all kinds of values, like DB version, counters, etc. - """ - name = PrimaryKey(str) - value = Optional(str) - - return MiscData diff --git a/src/tribler/core/components/bandwidth_accounting/db/transaction.py b/src/tribler/core/components/bandwidth_accounting/db/transaction.py deleted file mode 100644 index 8db476b320b..00000000000 --- a/src/tribler/core/components/bandwidth_accounting/db/transaction.py +++ /dev/null @@ -1,174 +0,0 @@ -""" -This file defines the required data structures for the bandwidth accounting mechanism. -Note that we define two different class types of BandwidthTransaction: one for the object that resides outside the -Pony database and another one that represents the class inside Pony. -We make this separation to workaround the fact that Pony does not support objects that are created outside a database -context. -""" -from __future__ import annotations - -import time -from dataclasses import dataclass, field -from typing import Dict - -from ipv8.keyvault.crypto import default_eccrypto -from ipv8.keyvault.keys import Key -from ipv8.messaging.serialization import default_serializer -from pony.orm import PrimaryKey, Required, db_session - -from tribler.core.components.bandwidth_accounting.community.payload import BandwidthTransactionPayload - -EMPTY_SIGNATURE = b'0' * 64 - - -@dataclass -class BandwidthTransactionData: - """ - This class defines a data class for a bandwidth transaction. - """ - sequence_number: int - public_key_a: bytes - public_key_b: bytes - signature_a: bytes - signature_b: bytes - amount: int - timestamp: int = field(default_factory=lambda: int(round(time.time() * 1000))) - - def pack(self, signature_a=True, signature_b=True) -> bytes: - """ - Encode this block for transport. - :param signature_a: False to pack EMPTY_SIG in the location of signature A, true to pack the signature A field. - :param signature_b: False to pack EMPTY_SIG in the location of signature B, true to pack the signature B field. - :return: bytes object the data was packed into. - """ - args = [self.sequence_number, self.public_key_a, self.public_key_b, - self.signature_a if signature_a else EMPTY_SIGNATURE, - self.signature_b if signature_b else EMPTY_SIGNATURE, self.amount, self.timestamp] - return default_serializer.pack_serializable(BandwidthTransactionPayload(*args, 0)) - - def sign(self, key: Key, as_a: bool) -> None: - """ - Signs this block with the given key - :param key: The key to sign this block with. - :param as_a: Whether we are signing this block as party A or B. - """ - if as_a: - # Party A is the first to sign the transaction - self.signature_a = default_eccrypto.create_signature( - key, self.pack(signature_a=False, signature_b=False)) - else: - # Party B is the first to sign the transaction - self.signature_b = default_eccrypto.create_signature( - key, self.pack(signature_b=False)) - - def is_valid(self) -> bool: - """ - Validate the signatures in the transaction. - return: True if the transaction is valid, False otherwise - """ - if self.signature_a != EMPTY_SIGNATURE: - # Verify signature A - pck = self.pack(signature_a=False, signature_b=False) - valid_signature = default_eccrypto.is_valid_signature( - default_eccrypto.key_from_public_bin(self.public_key_a), pck, self.signature_a) - if not valid_signature: - return False - - if self.signature_b != EMPTY_SIGNATURE: - # Verify signature B - pck = self.pack(signature_b=False) - valid_signature = default_eccrypto.is_valid_signature( - default_eccrypto.key_from_public_bin(self.public_key_b), pck, self.signature_b) - if not valid_signature: - return False - - if self.sequence_number < 1: - return False - - return True - - @classmethod - def from_payload(cls, payload: BandwidthTransactionPayload): - """ - Create a block according to a given payload. - This method can be used when receiving a block from the network. - :param payload: The payload to convert to a transaction. - :return A BandwidthTransaction, constructed from the provided payload. - """ - return cls(payload.sequence_number, payload.public_key_a, payload.public_key_b, - payload.signature_a, payload.signature_b, payload.amount, payload.timestamp) - - @classmethod - def from_db(cls, db_obj) -> BandwidthTransactionData: - """ - Return a BandwidthTransaction object from a database object. - :param db_obj: The BandwidthTransaction object to convert. - :return A BandwidthTransaction object, based on the database object. - """ - return BandwidthTransactionData(db_obj.sequence_number, db_obj.public_key_a, db_obj.public_key_b, - db_obj.signature_a, db_obj.signature_b, db_obj.amount, db_obj.timestamp) - - def get_db_kwargs(self) -> Dict: - """ - Return the database arguments for easy insertion in a Pony database. - :return A dictionary with keyword arguments for database insertion. - """ - return { - "sequence_number": self.sequence_number, - "public_key_a": self.public_key_a, - "public_key_b": self.public_key_b, - "signature_a": self.signature_a, - "signature_b": self.signature_b, - "amount": self.amount, - "timestamp": self.timestamp - } - - -def define_binding(bandwidth_database): - db = bandwidth_database.database - - class BandwidthTransaction(db.Entity): - """ - This class describes a bandwidth transaction that resides in the database. - """ - sequence_number = Required(int) - public_key_a = Required(bytes, index=True) - public_key_b = Required(bytes, index=True) - signature_a = Required(bytes) - signature_b = Required(bytes) - amount = Required(int, size=64) - timestamp = Required(int, size=64) - PrimaryKey(sequence_number, public_key_a, public_key_b) - - @classmethod - @db_session(optimistic=False) - def insert(cls, transaction: BandwidthTransaction) -> None: - """ - Insert a BandwidthTransaction object in the database. - Remove the last transaction with that specific counterparty while doing so. - :param transaction: The transaction to insert in the database. - """ - if not bandwidth_database.store_all_transactions: - # Make sure to only store the latest pairwise transaction. - for tx in cls.select( - lambda c: c.public_key_a == transaction.public_key_a and - c.public_key_b == transaction.public_key_b): - tx.delete() - db.commit() - cls(**transaction.get_db_kwargs()) - elif not bandwidth_database.has_transaction(transaction): - # We store all transactions and it does not exist yet - insert it. - cls(**transaction.get_db_kwargs()) - - if transaction.public_key_a == bandwidth_database.my_pub_key or \ - transaction.public_key_b == bandwidth_database.my_pub_key: - # Update the balance history - timestamp = int(round(time.time() * 1000)) - db.BandwidthHistory(timestamp=timestamp, balance=bandwidth_database.get_my_balance()) - num_entries = db.BandwidthHistory.select().count() - if num_entries > bandwidth_database.MAX_HISTORY_ITEMS: - # Delete the entry with the lowest timestamp - entry = list(db.BandwidthHistory.select().order_by(db.BandwidthHistory.timestamp))[0] - entry.delete() - - return BandwidthTransaction diff --git a/src/tribler/core/components/bandwidth_accounting/restapi/__init__.py b/src/tribler/core/components/bandwidth_accounting/restapi/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/tribler/core/components/bandwidth_accounting/restapi/bandwidth_endpoint.py b/src/tribler/core/components/bandwidth_accounting/restapi/bandwidth_endpoint.py deleted file mode 100644 index 025312b2893..00000000000 --- a/src/tribler/core/components/bandwidth_accounting/restapi/bandwidth_endpoint.py +++ /dev/null @@ -1,64 +0,0 @@ -from aiohttp import web -from aiohttp_apispec import docs -from ipv8.REST.schema import schema -from marshmallow.fields import Integer, String - -from tribler.core.components.bandwidth_accounting.community.bandwidth_accounting_community import ( - BandwidthAccountingCommunity, -) -from tribler.core.components.restapi.rest.rest_endpoint import RESTEndpoint, RESTResponse -from tribler.core.utilities.utilities import froze_it - - -@froze_it -class BandwidthEndpoint(RESTEndpoint): - """ - This endpoint is responsible for handing requests for bandwidth accounting data. - """ - path = '/bandwidth' - - def __init__(self, bandwidth_community: BandwidthAccountingCommunity): - super().__init__() - self.bandwidth_community = bandwidth_community - - def setup_routes(self) -> None: - self.app.add_routes([web.get('/statistics', self.get_statistics)]) - self.app.add_routes([web.get('/history', self.get_history)]) - - @docs( - tags=["Bandwidth"], - summary="Return statistics about the bandwidth community.", - responses={ - 200: { - "schema": schema(BandwidthStatisticsResponse={ - 'statistics': schema(BandwidthStatistics={ - 'id': String, - 'num_peers_helped': Integer, - 'num_peers_helped_by': Integer, - 'total_taken': Integer, - 'total_given': Integer - }) - }) - } - } - ) - async def get_statistics(self, request) -> RESTResponse: - return RESTResponse({'statistics': self.bandwidth_community.get_statistics()}) - - @docs( - tags=["Bandwidth"], - summary="Return a list of the balance history.", - responses={ - 200: { - "schema": schema(BandwidthHistoryResponse={ - "history": [schema(BandwidthHistoryItem={ - "timestamp": Integer, - "balance": Integer - }) - ] - }) - } - } - ) - async def get_history(self, request) -> RESTResponse: - return RESTResponse({'history': self.bandwidth_community.database.get_history()}) diff --git a/src/tribler/core/components/bandwidth_accounting/settings.py b/src/tribler/core/components/bandwidth_accounting/settings.py deleted file mode 100644 index 3df107daf12..00000000000 --- a/src/tribler/core/components/bandwidth_accounting/settings.py +++ /dev/null @@ -1,9 +0,0 @@ -from pydantic import Field - -from tribler.core.config.tribler_config_section import TriblerConfigSection - - -class BandwidthAccountingSettings(TriblerConfigSection): - testnet: bool = Field(default=False, env='BANDWIDTH_TESTNET') - outgoing_query_interval: int = 30 # The interval at which we send out queries to other peers, in seconds. - max_tx_returned_in_query: int = 10 # The maximum number of bandwidth transactions to return in response to a query. diff --git a/src/tribler/core/components/bandwidth_accounting/tests/__init__.py b/src/tribler/core/components/bandwidth_accounting/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/tribler/core/components/bandwidth_accounting/tests/test_bandwidth_accounting_component.py b/src/tribler/core/components/bandwidth_accounting/tests/test_bandwidth_accounting_component.py deleted file mode 100644 index 5d9dd822e28..00000000000 --- a/src/tribler/core/components/bandwidth_accounting/tests/test_bandwidth_accounting_component.py +++ /dev/null @@ -1,16 +0,0 @@ -from tribler.core.components.bandwidth_accounting.bandwidth_accounting_component import BandwidthAccountingComponent -from tribler.core.components.ipv8.ipv8_component import Ipv8Component -from tribler.core.components.key.key_component import KeyComponent -from tribler.core.components.session import Session - - -# pylint: disable=protected-access - - -async def test_bandwidth_accounting_component(tribler_config): - components = [KeyComponent(), Ipv8Component(), BandwidthAccountingComponent()] - async with Session(tribler_config, components) as session: - comp = session.get_instance(BandwidthAccountingComponent) - assert comp.started_event.is_set() and not comp.failed - assert comp.community - assert comp._ipv8_component diff --git a/src/tribler/core/components/bandwidth_accounting/tests/test_bandwidth_endpoint.py b/src/tribler/core/components/bandwidth_accounting/tests/test_bandwidth_endpoint.py deleted file mode 100644 index d1b9078509e..00000000000 --- a/src/tribler/core/components/bandwidth_accounting/tests/test_bandwidth_endpoint.py +++ /dev/null @@ -1,79 +0,0 @@ -import pytest -from ipv8.keyvault.crypto import default_eccrypto -from ipv8.peer import Peer - -from tribler.core.components.bandwidth_accounting.community.bandwidth_accounting_community import ( - BandwidthAccountingCommunity, -) -from tribler.core.components.bandwidth_accounting.db.database import BandwidthDatabase -from tribler.core.components.bandwidth_accounting.db.transaction import BandwidthTransactionData, EMPTY_SIGNATURE -from tribler.core.components.bandwidth_accounting.restapi.bandwidth_endpoint import BandwidthEndpoint -from tribler.core.components.bandwidth_accounting.settings import BandwidthAccountingSettings -from tribler.core.components.ipv8.adapters_tests import TriblerMockIPv8 -from tribler.core.components.restapi.rest.base_api_test import do_request -from tribler.core.utilities.unicode import hexlify - - -# pylint: disable=redefined-outer-name -@pytest.fixture -def peer(): - return Peer(default_eccrypto.generate_key("curve25519"), address=("1.2.3.4", 5)) - - -@pytest.fixture -def bandwidth_database(tmp_path, peer): - return BandwidthDatabase(db_path=tmp_path / "bandwidth.db", my_pub_key=peer.public_key.key_to_bin()) - - -@pytest.fixture -async def bw_community(bandwidth_database, peer): - ipv8 = TriblerMockIPv8(peer, BandwidthAccountingCommunity, - database=bandwidth_database, - settings=BandwidthAccountingSettings()) - community = ipv8.get_overlay(BandwidthAccountingCommunity) - yield community - await ipv8.stop() - - -@pytest.fixture -async def bw_endpoint(bw_community): - endpoint = BandwidthEndpoint(bw_community) - endpoint.setup_routes() - return endpoint - - -async def test_get_statistics(bw_endpoint, bw_community, aiohttp_client): - """ - Testing whether the API returns the correct statistics - """ - bw_endpoint.bandwidth_community = bw_community - my_pk = bw_community.database.my_pub_key - tx1 = BandwidthTransactionData(1, b"a", my_pk, EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) - tx2 = BandwidthTransactionData(1, my_pk, b"a", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 2000) - bw_community.database.BandwidthTransaction.insert(tx1) - bw_community.database.BandwidthTransaction.insert(tx2) - - response_dict = await do_request(await aiohttp_client(bw_endpoint.app), 'statistics', expected_code=200) - assert "statistics" in response_dict - stats = response_dict["statistics"] - assert stats["id"] == hexlify(my_pk) - assert stats["total_given"] == 3000 - assert stats["total_taken"] == 2000 - assert stats["num_peers_helped"] == 1 - assert stats["num_peers_helped_by"] == 1 - - -async def test_get_history(bw_endpoint, bw_community, aiohttp_client): - """ - Testing whether the API returns the correct bandwidth balance history - """ - bw_endpoint.bandwidth_community = bw_community - my_pk = bw_community.my_peer.public_key.key_to_bin() - tx1 = BandwidthTransactionData(1, b"a", my_pk, EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) - tx2 = BandwidthTransactionData(1, my_pk, b"a", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 2000) - bw_community.database.BandwidthTransaction.insert(tx1) - bw_community.database.BandwidthTransaction.insert(tx2) - - response_dict = await do_request(await aiohttp_client(bw_endpoint.app), 'history', expected_code=200) - assert "history" in response_dict - assert len(response_dict["history"]) == 2 diff --git a/src/tribler/core/components/bandwidth_accounting/tests/test_community.py b/src/tribler/core/components/bandwidth_accounting/tests/test_community.py deleted file mode 100644 index d5560318c04..00000000000 --- a/src/tribler/core/components/bandwidth_accounting/tests/test_community.py +++ /dev/null @@ -1,156 +0,0 @@ -import unittest.mock - -from ipv8.keyvault.crypto import default_eccrypto -from ipv8.peer import Peer - -from tribler.core.components.bandwidth_accounting.community.bandwidth_accounting_community import ( - BandwidthAccountingCommunity, -) -from tribler.core.components.bandwidth_accounting.community.cache import BandwidthTransactionSignCache -from tribler.core.components.bandwidth_accounting.db.database import BandwidthDatabase -from tribler.core.components.bandwidth_accounting.db.transaction import BandwidthTransactionData, EMPTY_SIGNATURE -from tribler.core.components.bandwidth_accounting.settings import BandwidthAccountingSettings -from tribler.core.components.ipv8.adapters_tests import TriblerMockIPv8, TriblerTestBase -from tribler.core.utilities.utilities import MEMORY_DB - -ID1, ID2, ID3 = range(3) - - -class TestBandwidthAccountingCommunity(TriblerTestBase): - - def setUp(self): - super().setUp() - self.initialize(BandwidthAccountingCommunity, 2) - - def create_node(self, *args, **kwargs): - peer = Peer(default_eccrypto.generate_key("curve25519"), address=("1.2.3.4", 5)) - db = BandwidthDatabase(db_path=MEMORY_DB, my_pub_key=peer.public_key.key_to_bin()) - ipv8 = TriblerMockIPv8(peer, BandwidthAccountingCommunity, database=db, - settings=BandwidthAccountingSettings()) - return ipv8 - - def database(self, i): - return self.overlay(i).database - - def add_cache(self, i, cache): - return self.overlay(i).request_cache.add(cache) - - async def test_single_transaction(self): - """ - Test a simple transaction between two parties. - """ - await self.overlay(ID1).do_payout(self.peer(ID2), 1024) - - assert self.database(ID1).get_total_taken(self.key_bin(ID1)) == 1024 - assert self.database(ID2).get_total_taken(self.key_bin(ID1)) == 1024 - - async def test_multiple_transactions(self): - """ - Test multiple, subsequent transactions between two parties. - """ - await self.overlay(ID1).do_payout(self.peer(ID2), 500) - await self.overlay(ID1).do_payout(self.peer(ID2), 1500) - - assert self.database(ID1).get_total_taken(self.key_bin(ID1)) == 2000 - assert self.database(ID2).get_total_taken(self.key_bin(ID1)) == 2000 - - async def test_bilateral_transaction(self): - """ - Test creating a transaction from A to B and one from B to A. - """ - await self.overlay(ID1).do_payout(self.peer(ID2), 500) - await self.overlay(ID2).do_payout(self.peer(ID1), 1500) - - assert self.database(ID1).get_total_taken(self.key_bin(ID1)) == 500 - assert self.database(ID2).get_total_taken(self.key_bin(ID1)) == 500 - assert self.database(ID1).get_total_taken(self.key_bin(ID2)) == 1500 - assert self.database(ID2).get_total_taken(self.key_bin(ID2)) == 1500 - - async def test_bilateral_transaction_timestamps(self): - """ - Test whether the timestamps are different for transactions created at different times. - - We do not depend on chance and ensure that `time.time()` is different between calls. - """ - with unittest.mock.patch('time.time') as fake_time: - fake_time.return_value = 10.0 - tx1 = await self.overlay(ID1).do_payout(self.peer(ID2), 500) - - with unittest.mock.patch('time.time') as fake_time: - fake_time.return_value = 11.0 - tx2 = await self.overlay(ID1).do_payout(self.peer(ID2), 500) - - assert tx1.timestamp != tx2.timestamp - - async def test_invalid_transaction(self): - """ - Test sending a transaction with an invalid signature to the counterparty, which should be ignored. - """ - tx = self.overlay(ID1).construct_signed_transaction(self.peer(ID2), 300) - tx.signature_a = b"invalid" - self.database(ID1).BandwidthTransaction.insert(tx) - cache = self.add_cache(ID1, BandwidthTransactionSignCache(self.overlay(ID1), tx)) - self.overlay(ID1).send_transaction(tx, self.address(ID2), cache.number) - - await self.deliver_messages() - - assert self.database(ID2).get_total_taken(self.key_bin(ID1)) == 0 - - async def test_ignore_unknown_transaction(self): - """ - Test whether we are ignoring a transaction that is not in our cache. - """ - tx = BandwidthTransactionData(ID2, self.key_bin(ID1), self.key_bin(ID2), EMPTY_SIGNATURE, EMPTY_SIGNATURE, 1000) - tx.sign(self.private_key(ID1), as_a=True) - self.overlay(ID1).send_transaction(tx, self.address(ID2), 1234) - - await self.deliver_messages() - - assert not self.database(ID1).get_latest_transaction(self.key_bin(ID1), self.key_bin(ID2)) - - async def test_concurrent_transaction_out_of_order(self): - """ - Test creating multiple transactions, while the other party is offline and receives messages out of order. - """ - tx1 = BandwidthTransactionData(1, self.key_bin(ID1), self.key_bin(ID2), EMPTY_SIGNATURE, EMPTY_SIGNATURE, 1000) - tx2 = BandwidthTransactionData(2, self.key_bin(ID1), self.key_bin(ID2), EMPTY_SIGNATURE, EMPTY_SIGNATURE, 2000) - - # Send them in reverse order - cache = self.add_cache(ID1, BandwidthTransactionSignCache(self.overlay(ID1), tx1)) - self.overlay(ID1).send_transaction(tx2, self.address(ID2), cache.number) - await self.deliver_messages() - - # This one should be ignored by node 1 - cache = self.add_cache(ID1, BandwidthTransactionSignCache(self.overlay(ID1), tx1)) - self.overlay(ID1).send_transaction(tx1, self.address(ID2), cache.number) - await self.deliver_messages() - - # Both parties should have the transaction with amount 2000 in their database - assert self.database(ID1).get_total_taken(self.key_bin(ID1)) == 2000 - assert self.database(ID2).get_total_taken(self.key_bin(ID1)) == 2000 - - async def test_querying_peer(self): - """ - Test whether node C can query node B to get the transaction between A and B. - """ - await self.overlay(ID1).do_payout(self.peer(ID2), 500) - - self.add_node_to_experiment(self.create_node()) - self.overlay(ID3).query_transactions(self.peer(ID2)) - - await self.deliver_messages() - - assert self.database(ID3).get_total_taken(self.key_bin(ID1)) == 500 - - async def test_query_random_peer(self): - """ - Test whether node C can query node B to get the transaction between A and B. - """ - await self.overlay(ID1).do_payout(self.peer(ID2), 500) - - self.add_node_to_experiment(self.create_node()) - self.overlay(ID3).query_random_peer() - - await self.deliver_messages() - - assert self.database(ID3).get_total_taken(self.key_bin(ID1)) == 500 diff --git a/src/tribler/core/components/bandwidth_accounting/tests/test_database.py b/src/tribler/core/components/bandwidth_accounting/tests/test_database.py deleted file mode 100644 index 5026b58f5a5..00000000000 --- a/src/tribler/core/components/bandwidth_accounting/tests/test_database.py +++ /dev/null @@ -1,168 +0,0 @@ -import random - -import pytest -from ipv8.keyvault.crypto import default_eccrypto -from pony.orm import db_session - -from tribler.core.components.bandwidth_accounting.db.database import BandwidthDatabase -from tribler.core.components.bandwidth_accounting.db.transaction import BandwidthTransactionData, EMPTY_SIGNATURE -from tribler.core.utilities.utilities import MEMORY_DB - - -@pytest.fixture -def my_key(): - return default_eccrypto.generate_key('curve25519') - - -@pytest.fixture -def bandwidth_db(tmpdir, my_key): - db = BandwidthDatabase(MEMORY_DB, my_key.pub().key_to_bin()) - yield db - db.shutdown() - - -@db_session -def test_add_transaction(bandwidth_db): - tx1 = BandwidthTransactionData(1, b"a", b"b", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) - bandwidth_db.BandwidthTransaction.insert(tx1) - assert bandwidth_db.has_transaction(tx1) - tx2 = BandwidthTransactionData(2, b"a", b"b", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 4000) - bandwidth_db.BandwidthTransaction.insert(tx2) - - latest_tx = bandwidth_db.get_latest_transaction(b"a", b"b") - assert latest_tx - assert latest_tx.amount == 4000 - - # Test storing all transactions - bandwidth_db.store_all_transactions = True - tx3 = BandwidthTransactionData(3, b"a", b"b", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 4000) - bandwidth_db.BandwidthTransaction.insert(tx3) - assert len(list(bandwidth_db.BandwidthTransaction.select())) == 2 - assert bandwidth_db.has_transaction(tx2) - assert bandwidth_db.has_transaction(tx3) - - # Test whether adding a transaction again does not result in an error - bandwidth_db.BandwidthTransaction.insert(tx2) - - -@db_session -def test_get_my_latest_transactions(bandwidth_db): - assert not bandwidth_db.get_my_latest_transactions() - - tx1 = BandwidthTransactionData(1, b"a", bandwidth_db.my_pub_key, EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) - bandwidth_db.BandwidthTransaction.insert(tx1) - tx2 = BandwidthTransactionData(1, bandwidth_db.my_pub_key, b"c", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) - bandwidth_db.BandwidthTransaction.insert(tx2) - tx3 = BandwidthTransactionData(1, b"c", b"d", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) - bandwidth_db.BandwidthTransaction.insert(tx3) - - assert len(bandwidth_db.get_my_latest_transactions()) == 2 - assert len(bandwidth_db.get_my_latest_transactions(limit=1)) == 1 - - -@db_session -def test_get_latest_transaction(bandwidth_db): - assert not bandwidth_db.get_latest_transaction(b"a", b"b") - tx1 = BandwidthTransactionData(1, b"a", b"b", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) - bandwidth_db.BandwidthTransaction.insert(tx1) - - tx2 = bandwidth_db.get_latest_transaction(b"a", b"b") - assert tx1 == tx2 - assert tx2.amount == 3000 - - -@db_session -def test_get_latest_transactions(bandwidth_db): - pub_key_a = b"a" - pub_keys_rest = [b"b", b"c", b"d", b"e", b"f"] - - assert not bandwidth_db.get_latest_transactions(pub_key_a) - - for pub_key in pub_keys_rest: - seq_number = random.randint(1, 100) - amount = random.randint(1, 1000) - tx = BandwidthTransactionData(seq_number, pub_key_a, pub_key, EMPTY_SIGNATURE, EMPTY_SIGNATURE, amount) - bandwidth_db.BandwidthTransaction.insert(tx) - - txs = bandwidth_db.get_latest_transactions(pub_key_a) - assert len(txs) == len(pub_keys_rest) - - -@db_session -def test_store_large_transaction(bandwidth_db): - large_tx = BandwidthTransactionData(1, b"a", b"b", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 1024 * 1024 * 1024 * 3) - bandwidth_db.BandwidthTransaction.insert(large_tx) - - latest_tx = bandwidth_db.get_latest_transaction(b"a", b"b") - assert latest_tx - - -async def test_totals(bandwidth_db): - with db_session: - tx1 = BandwidthTransactionData(1, b"a", b"b", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) - bandwidth_db.BandwidthTransaction.insert(tx1) - - assert bandwidth_db.get_total_taken(b"a") == 3000 - assert bandwidth_db.get_total_given(b"a") == 0 - - tx2 = BandwidthTransactionData(1, b"b", b"a", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 4000) - bandwidth_db.BandwidthTransaction.insert(tx2) - - assert bandwidth_db.get_total_taken(b"a") == 3000 - assert bandwidth_db.get_total_given(b"a") == 4000 - assert bandwidth_db.get_balance(b"a") == 1000 - assert bandwidth_db.get_balance(b"b") == -1000 - - -@db_session -def test_peers_helped(bandwidth_db): - assert bandwidth_db.get_num_peers_helped(b"a") == 0 - tx1 = BandwidthTransactionData(1, b"a", b"b", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) - bandwidth_db.BandwidthTransaction.insert(tx1) - assert bandwidth_db.get_num_peers_helped(b"a") == 1 - tx2 = BandwidthTransactionData(2, b"a", b"c", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) - bandwidth_db.BandwidthTransaction.insert(tx2) - assert bandwidth_db.get_num_peers_helped(b"a") == 2 - tx3 = BandwidthTransactionData(1, b"b", b"c", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) - bandwidth_db.BandwidthTransaction.insert(tx3) - assert bandwidth_db.get_num_peers_helped(b"a") == 2 - - -@db_session -def test_peers_helped_by(bandwidth_db): - assert bandwidth_db.get_num_peers_helped_by(b"a") == 0 - tx1 = BandwidthTransactionData(1, b"b", b"a", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) - bandwidth_db.BandwidthTransaction.insert(tx1) - assert bandwidth_db.get_num_peers_helped_by(b"a") == 1 - tx2 = BandwidthTransactionData(2, b"c", b"a", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) - bandwidth_db.BandwidthTransaction.insert(tx2) - assert bandwidth_db.get_num_peers_helped_by(b"a") == 2 - tx3 = BandwidthTransactionData(1, b"c", b"b", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) - bandwidth_db.BandwidthTransaction.insert(tx3) - assert bandwidth_db.get_num_peers_helped_by(b"a") == 2 - - -@db_session -def test_history(bandwidth_db): - assert not bandwidth_db.get_history() - tx1 = BandwidthTransactionData(1, bandwidth_db.my_pub_key, b"a", EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) - bandwidth_db.BandwidthTransaction.insert(tx1) - - history = bandwidth_db.get_history() - assert len(history) == 1 - assert history[0]["balance"] == -3000 - - tx2 = BandwidthTransactionData(1, b"a", bandwidth_db.my_pub_key, EMPTY_SIGNATURE, EMPTY_SIGNATURE, 4000) - bandwidth_db.BandwidthTransaction.insert(tx2) - - history = bandwidth_db.get_history() - assert len(history) == 2 - assert history[1]["balance"] == 1000 - - # Test whether the history is pruned correctly - bandwidth_db.MAX_HISTORY_ITEMS = 2 - tx3 = BandwidthTransactionData(1, b"a", bandwidth_db.my_pub_key, EMPTY_SIGNATURE, EMPTY_SIGNATURE, 2000) - bandwidth_db.BandwidthTransaction.insert(tx3) - - history = bandwidth_db.get_history() - assert len(history) == 2 diff --git a/src/tribler/core/components/bandwidth_accounting/tests/test_transaction.py b/src/tribler/core/components/bandwidth_accounting/tests/test_transaction.py deleted file mode 100644 index 43af80b07db..00000000000 --- a/src/tribler/core/components/bandwidth_accounting/tests/test_transaction.py +++ /dev/null @@ -1,41 +0,0 @@ -from ipv8.keyvault.crypto import default_eccrypto - -from tribler.core.components.bandwidth_accounting.db.transaction import BandwidthTransactionData, EMPTY_SIGNATURE - - -def test_sign_transaction(): - key1 = default_eccrypto.generate_key('curve25519') - key2 = default_eccrypto.generate_key('curve25519') - tx = BandwidthTransactionData(1, key1.pub().key_to_bin(), key2.pub().key_to_bin(), - EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) - - tx.sign(key1, as_a=True) - assert tx.is_valid() - assert tx.signature_a != EMPTY_SIGNATURE - assert tx.signature_b == EMPTY_SIGNATURE - - tx.sign(key2, as_a=False) - assert tx.is_valid() - assert tx.signature_a != EMPTY_SIGNATURE - assert tx.signature_b != EMPTY_SIGNATURE - - -def test_is_valid(): - key1 = default_eccrypto.generate_key('curve25519') - key2 = default_eccrypto.generate_key('curve25519') - tx = BandwidthTransactionData(1, key1.pub().key_to_bin(), key2.pub().key_to_bin(), - EMPTY_SIGNATURE, EMPTY_SIGNATURE, 3000) - - assert tx.is_valid() # No signatures have been computed so far - - tx.signature_a = b'a' * 32 - assert not tx.is_valid() - - tx.signature_a = EMPTY_SIGNATURE - tx.signature_b = b'a' * 32 - assert not tx.is_valid() - - tx.signature_a = EMPTY_SIGNATURE - tx.signature_b = EMPTY_SIGNATURE - tx.sequence_number = -1 - assert not tx.is_valid() diff --git a/src/tribler/core/components/payout/__init__.py b/src/tribler/core/components/payout/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/tribler/core/components/payout/payout_component.py b/src/tribler/core/components/payout/payout_component.py deleted file mode 100644 index 4a6ceee29f6..00000000000 --- a/src/tribler/core/components/payout/payout_component.py +++ /dev/null @@ -1,38 +0,0 @@ -from tribler.core import notifications -from tribler.core.components.bandwidth_accounting.bandwidth_accounting_component import BandwidthAccountingComponent -from tribler.core.components.component import Component -from tribler.core.components.ipv8.ipv8_component import Ipv8Component -from tribler.core.components.payout.payout_manager import PayoutManager -from tribler.core.components.reporter.reporter_component import ReporterComponent - -INFINITE = -1 - - -class PayoutComponent(Component): - payout_manager: PayoutManager = None - - async def run(self): - await super().run() - - config = self.session.config - assert not config.gui_test_mode - - await self.get_component(ReporterComponent) - - ipv8_component = await self.require_component(Ipv8Component) - bandwidth_accounting_component = await self.require_component(BandwidthAccountingComponent) - - self.payout_manager = PayoutManager(bandwidth_accounting_component.community, - ipv8_component.dht_discovery_community) - - self.session.notifier.add_observer(notifications.peer_disconnected, self.payout_manager.on_peer_disconnected) - self.session.notifier.add_observer(notifications.tribler_torrent_peer_update, self.payout_manager.update_peer) - - async def shutdown(self): - await super().shutdown() - if self.payout_manager: - notifier = self.session.notifier - notifier.remove_observer(notifications.peer_disconnected, self.payout_manager.on_peer_disconnected) - notifier.remove_observer(notifications.tribler_torrent_peer_update, self.payout_manager.update_peer) - - await self.payout_manager.shutdown() diff --git a/src/tribler/core/components/payout/payout_manager.py b/src/tribler/core/components/payout/payout_manager.py deleted file mode 100644 index 44cb32ba3e7..00000000000 --- a/src/tribler/core/components/payout/payout_manager.py +++ /dev/null @@ -1,68 +0,0 @@ -import logging - -from ipv8.taskmanager import TaskManager, task - -from tribler.core.utilities.unicode import hexlify - - -class PayoutManager(TaskManager): - """ - This manager is responsible for keeping track of known Tribler peers and doing (zero-hop) payouts. - """ - - def __init__(self, bandwidth_community, dht): - super().__init__() - self.logger = logging.getLogger(self.__class__.__name__) - self.bandwidth_community = bandwidth_community - self.dht = dht - self.tribler_peers = {} - - def on_peer_disconnected(self, peer_id: bytes): - # do_payout is not specified directly, as PyCharm does not understand its type correctly due to a task decorator - self.do_payout(peer_id) - - @task - async def do_payout(self, peer_id: bytes): - """ - Perform a payout to a given mid. First, determine the outstanding balance. Then resolve the node in the DHT. - """ - if peer_id not in self.tribler_peers: - return None - - total_bytes = sum(self.tribler_peers[peer_id].values()) - - self.logger.info("Doing direct payout to %s (%d bytes)", hexlify(peer_id), total_bytes) - try: - nodes = await self.dht.connect_peer(peer_id) - except Exception as e: - self.logger.warning("Error while doing DHT lookup for payouts, error %s", e) - return None - - self.logger.debug("Received %d nodes for DHT lookup", len(nodes)) - if not nodes: - return None - - try: - await self.bandwidth_community.do_payout(nodes[0], total_bytes) - except Exception as e: - self.logger.error("Error while doing bandwidth payout, error %s", e) - return None - - # Remove the outstanding bytes; otherwise we will payout again - self.tribler_peers.pop(peer_id, None) - return nodes[0] - - def update_peer(self, peer_id: bytes, infohash: bytes, balance: int): - """ - Update a peer with a specific mid for a specific infohash. - """ - self.logger.debug("Updating peer with mid %s and ih %s (balance: %d)", hexlify(peer_id), - hexlify(infohash), balance) - - if peer_id not in self.tribler_peers: - self.tribler_peers[peer_id] = {} - - self.tribler_peers[peer_id][infohash] = balance - - async def shutdown(self): - await self.shutdown_task_manager() diff --git a/src/tribler/core/components/payout/tests/__init__.py b/src/tribler/core/components/payout/tests/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/tribler/core/components/payout/tests/test_payout_component.py b/src/tribler/core/components/payout/tests/test_payout_component.py deleted file mode 100644 index 4786a6355dc..00000000000 --- a/src/tribler/core/components/payout/tests/test_payout_component.py +++ /dev/null @@ -1,15 +0,0 @@ -# pylint: disable=protected-access - -from tribler.core.components.bandwidth_accounting.bandwidth_accounting_component import BandwidthAccountingComponent -from tribler.core.components.ipv8.ipv8_component import Ipv8Component -from tribler.core.components.key.key_component import KeyComponent -from tribler.core.components.payout.payout_component import PayoutComponent -from tribler.core.components.session import Session - - -async def test_payout_component(tribler_config): - components = [BandwidthAccountingComponent(), KeyComponent(), Ipv8Component(), PayoutComponent()] - async with Session(tribler_config, components) as session: - comp = session.get_instance(PayoutComponent) - assert comp.started_event.is_set() and not comp.failed - assert comp.payout_manager diff --git a/src/tribler/core/components/payout/tests/test_payout_manager.py b/src/tribler/core/components/payout/tests/test_payout_manager.py deleted file mode 100644 index aa587128a2f..00000000000 --- a/src/tribler/core/components/payout/tests/test_payout_manager.py +++ /dev/null @@ -1,97 +0,0 @@ -from unittest.mock import Mock - -import pytest -from ipv8.util import succeed - -from tribler.core.components.payout.payout_manager import PayoutManager - - -@pytest.fixture -async def payout_manager(): - fake_bw_community = Mock() - - fake_response_peer = Mock() - fake_response_peer.public_key = Mock() - fake_response_peer.public_key.key_to_bin = lambda: b'a' * 64 - fake_dht = Mock() - fake_dht.connect_peer = lambda *_: succeed([fake_response_peer]) - - payout_manager = PayoutManager(fake_bw_community, fake_dht) - yield payout_manager - await payout_manager.shutdown() - - -async def test_do_payout(payout_manager): - """ - Test doing a payout - """ - res = await payout_manager.do_payout(b'a') # Does not exist - assert not res - payout_manager.update_peer(b'b', b'c', 10 * 1024 * 1024) - payout_manager.update_peer(b'b', b'd', 1337) - - def mocked_do_payout(*_, **__): - return succeed(None) - - payout_manager.bandwidth_community.do_payout = mocked_do_payout - res = await payout_manager.do_payout(b'b') - assert res - - -async def test_do_payout_dht_error(payout_manager): - """ - Test whether we are not doing a payout when the DHT lookup fails - """ - - def err_connect_peer(_): - raise RuntimeError("test") - - payout_manager.update_peer(b'a', b'b', 10 * 1024 * 1024) - payout_manager.dht.connect_peer = err_connect_peer - res = await payout_manager.do_payout(b'a') - assert not res - - -async def test_do_payout_no_dht_peers(payout_manager): - """ - Test whether we are not doing a payout when there are no peers returned by the DHT - """ - - def connect_peer(_): - return succeed([]) - - payout_manager.update_peer(b'a', b'b', 10 * 1024 * 1024) - payout_manager.dht.connect_peer = connect_peer - res = await payout_manager.do_payout(b'a') - assert not res - - -async def test_do_payout_error(payout_manager): - """ - Test whether we are not doing a payout when the payout fails - """ - - def connect_peer(_): - return succeed([b"abc"]) - - def do_payout(*_): - raise RuntimeError("test") - - payout_manager.update_peer(b'a', b'b', 10 * 1024 * 1024) - payout_manager.dht.connect_peer = connect_peer - payout_manager.bandwidth_community.do_payout = do_payout - res = await payout_manager.do_payout(b'a') - assert not res - - -def test_update_peer(payout_manager): - """ - Test the updating of a specific peer - """ - payout_manager.update_peer(b'a', b'b', 1337) - assert b'a' in payout_manager.tribler_peers - assert b'b' in payout_manager.tribler_peers[b'a'] - assert payout_manager.tribler_peers[b'a'][b'b'] == 1337 - - payout_manager.update_peer(b'a', b'b', 1338) - assert payout_manager.tribler_peers[b'a'][b'b'] == 1338 diff --git a/src/tribler/core/components/restapi/rest/tests/test_statistics_endpoint.py b/src/tribler/core/components/restapi/rest/tests/test_statistics_endpoint.py index a61be19e762..49fa0379c2f 100644 --- a/src/tribler/core/components/restapi/rest/tests/test_statistics_endpoint.py +++ b/src/tribler/core/components/restapi/rest/tests/test_statistics_endpoint.py @@ -1,13 +1,12 @@ -from unittest.mock import Mock +from unittest.mock import MagicMock, Mock import pytest -from tribler.core.components.bandwidth_accounting.community.bandwidth_accounting_community \ - import BandwidthAccountingCommunity -from tribler.core.components.bandwidth_accounting.settings import BandwidthAccountingSettings from tribler.core.components.ipv8.adapters_tests import TriblerMockIPv8 from tribler.core.components.restapi.rest.base_api_test import do_request from tribler.core.components.restapi.rest.statistics_endpoint import StatisticsEndpoint +from tribler.core.components.tunnel.community.tunnel_community import TriblerTunnelCommunity +from tribler.core.components.tunnel.settings import TunnelCommunitySettings # pylint: disable=redefined-outer-name @@ -15,8 +14,12 @@ @pytest.fixture async def endpoint(metadata_store): - ipv8 = TriblerMockIPv8("low", BandwidthAccountingCommunity, database=Mock(), - settings=BandwidthAccountingSettings()) + ipv8 = TriblerMockIPv8("curve25519", + TriblerTunnelCommunity, + settings={"max_circuits": 1}, + config=TunnelCommunitySettings(), + socks_servers=MagicMock(), + dlmgr=Mock()) ipv8.overlays = [ipv8.overlay] ipv8.endpoint.bytes_up = 100 ipv8.endpoint.bytes_down = 20 diff --git a/src/tribler/core/components/restapi/restapi_component.py b/src/tribler/core/components/restapi/restapi_component.py index a43ff2bdf1e..165a586536a 100644 --- a/src/tribler/core/components/restapi/restapi_component.py +++ b/src/tribler/core/components/restapi/restapi_component.py @@ -2,8 +2,6 @@ from typing import Type from ipv8.REST.root_endpoint import RootEndpoint as IPV8RootEndpoint -from tribler.core.components.bandwidth_accounting.bandwidth_accounting_component import BandwidthAccountingComponent -from tribler.core.components.bandwidth_accounting.restapi.bandwidth_endpoint import BandwidthEndpoint from tribler.core.components.component import Component from tribler.core.components.content_discovery.content_discovery_component import ContentDiscoveryComponent from tribler.core.components.content_discovery.restapi.search_endpoint import SearchEndpoint @@ -72,7 +70,6 @@ async def run(self): ipv8_component = await self.maybe_component(Ipv8Component) libtorrent_component = await self.maybe_component(LibtorrentComponent) resource_monitor_component = await self.maybe_component(ResourceMonitorComponent) - bandwidth_accounting_component = await self.maybe_component(BandwidthAccountingComponent) content_discovery_component = await self.maybe_component(ContentDiscoveryComponent) knowledge_component = await self.maybe_component(KnowledgeComponent) tunnel_component = await self.maybe_component(TunnelsComponent) @@ -93,7 +90,6 @@ async def run(self): self.maybe_add(DebugEndpoint, config.state_dir, log_dir, tunnel_community=tunnel_community, resource_monitor=resource_monitor_component.resource_monitor, core_exception_handler=self._core_exception_handler) - self.maybe_add(BandwidthEndpoint, bandwidth_accounting_component.community) self.maybe_add(DownloadsEndpoint, libtorrent_component.download_manager, metadata_store=db_component.mds, tunnel_community=tunnel_community) self.maybe_add(CreateTorrentEndpoint, libtorrent_component.download_manager) diff --git a/src/tribler/core/components/restapi/tests/test_restapi_component.py b/src/tribler/core/components/restapi/tests/test_restapi_component.py index 21991bdcc94..7e02178c4b5 100644 --- a/src/tribler/core/components/restapi/tests/test_restapi_component.py +++ b/src/tribler/core/components/restapi/tests/test_restapi_component.py @@ -2,7 +2,6 @@ import pytest -from tribler.core.components.bandwidth_accounting.bandwidth_accounting_component import BandwidthAccountingComponent from tribler.core.components.database.database_component import DatabaseComponent from tribler.core.components.exceptions import NoneComponent from tribler.core.components.ipv8.ipv8_component import Ipv8Component @@ -20,7 +19,7 @@ # pylint: disable=protected-access, not-callable, redefined-outer-name async def test_rest_component(tribler_config): components = [KeyComponent(), RESTComponent(), Ipv8Component(), LibtorrentComponent(), ResourceMonitorComponent(), - BandwidthAccountingComponent(), KnowledgeComponent(), SocksServersComponent(), DatabaseComponent()] + KnowledgeComponent(), SocksServersComponent(), DatabaseComponent()] async with Session(tribler_config, components) as session: # Test REST component starts normally comp = session.get_instance(RESTComponent) diff --git a/src/tribler/core/components/tunnel/community/caches.py b/src/tribler/core/components/tunnel/community/caches.py index 49aa437cec2..569f9d14589 100644 --- a/src/tribler/core/components/tunnel/community/caches.py +++ b/src/tribler/core/components/tunnel/community/caches.py @@ -1,18 +1,6 @@ from asyncio import Future -from ipv8.requestcache import NumberCache, RandomNumberCache - - -class BalanceRequestCache(NumberCache): - - def __init__(self, community, circuit_id, balance_future): - super().__init__(community.request_cache, "balance-request", circuit_id) - self.circuit_id = circuit_id - self.balance_future = balance_future - self.register_future(self.balance_future) - - def on_timeout(self): - pass +from ipv8.requestcache import RandomNumberCache class HTTPRequestCache(RandomNumberCache): diff --git a/src/tribler/core/components/tunnel/community/payload.py b/src/tribler/core/components/tunnel/community/payload.py index 774be62ac8d..df97f89742b 100644 --- a/src/tribler/core/components/tunnel/community/payload.py +++ b/src/tribler/core/components/tunnel/community/payload.py @@ -3,65 +3,6 @@ from ipv8.messaging.lazy_payload import VariablePayload, vp_compile -@vp_compile -class BandwidthTransactionPayload(VariablePayload): - """ - Payload for a message containing a bandwidth transaction. - """ - msg_id = 30 - format_list = ['I', '74s', '74s', '64s', '64s', 'Q', 'Q', 'I', 'I'] - names = ["sequence_number", "public_key_a", "public_key_b", "signature_a", "signature_b", "amount", "timestamp", - "circuit_id", "base_amount"] - - @classmethod - def from_transaction(cls, transaction, circuit_id: int, base_amount: int): - """ - Create a transaction from the provided payload. - :param transaction: The BandwidthTransaction to convert to a payload. - :param circuit_id: The circuit identifier to include in the payload. - :param base_amount: The base amount of bandwidth to payout. - """ - return BandwidthTransactionPayload( - transaction.sequence_number, - transaction.public_key_a, - transaction.public_key_b, - transaction.signature_a, - transaction.signature_b, - transaction.amount, - transaction.timestamp, - circuit_id, - base_amount - ) - - -@vp_compile -class BalanceResponsePayload(VariablePayload): - """ - Payload that contains the bandwidth balance of a specific peer. - """ - msg_id = 31 - format_list = ["I", "q"] - names = ["circuit_id", "balance"] - - -class RelayBalanceResponsePayload(BalanceResponsePayload): - msg_id = 32 - - -@vp_compile -class BalanceRequestPayload(VariablePayload): - msg_id = 33 - format_list = ['I', 'H'] - names = ['circuit_id', 'identifier'] - - -@vp_compile -class RelayBalanceRequestPayload(VariablePayload): - msg_id = 34 - format_list = ['I'] - names = ['circuit_id'] - - @vp_compile class HTTPRequestPayload(VariablePayload): msg_id = 28 diff --git a/src/tribler/core/components/tunnel/community/tunnel_community.py b/src/tribler/core/components/tunnel/community/tunnel_community.py index 37316b62f70..bb2d387690d 100644 --- a/src/tribler/core/components/tunnel/community/tunnel_community.py +++ b/src/tribler/core/components/tunnel/community/tunnel_community.py @@ -1,52 +1,36 @@ import hashlib import math -import sys import time -from asyncio import Future, TimeoutError as AsyncTimeoutError, open_connection +from asyncio import TimeoutError as AsyncTimeoutError, open_connection from binascii import unhexlify from collections import Counter from distutils.version import LooseVersion from typing import Callable, List, Optional import async_timeout -from ipv8.messaging.anonymization.caches import CreateRequestCache from ipv8.messaging.anonymization.community import unpack_cell from ipv8.messaging.anonymization.hidden_services import HiddenTunnelCommunity -from ipv8.messaging.anonymization.payload import EstablishIntroPayload, NO_CRYPTO_PACKETS +from ipv8.messaging.anonymization.payload import EstablishIntroPayload from ipv8.messaging.anonymization.tunnel import ( - CIRCUIT_STATE_CLOSING, CIRCUIT_STATE_READY, - CIRCUIT_TYPE_DATA, CIRCUIT_TYPE_IP_SEEDER, - CIRCUIT_TYPE_RP_DOWNLOADER, CIRCUIT_TYPE_RP_SEEDER, - EXIT_NODE, PEER_FLAG_EXIT_BT, PEER_FLAG_EXIT_IPV8, - RelayRoute, ) -from ipv8.peer import Peer from ipv8.peerdiscovery.network import Network from ipv8.taskmanager import task -from ipv8.types import Address from ipv8.util import succeed -from pony.orm import OrmError from tribler.core import notifications -from tribler.core.components.bandwidth_accounting.db.transaction import BandwidthTransactionData from tribler.core.components.ipv8.tribler_community import args_kwargs_to_community_settings from tribler.core.components.socks_servers.socks5.server import Socks5Server -from tribler.core.components.tunnel.community.caches import BalanceRequestCache, HTTPRequestCache +from tribler.core.components.tunnel.community.caches import HTTPRequestCache from tribler.core.components.tunnel.community.discovery import GoldenRatioStrategy from tribler.core.components.tunnel.community.dispatcher import TunnelDispatcher from tribler.core.components.tunnel.community.payload import ( - BalanceRequestPayload, - BalanceResponsePayload, - BandwidthTransactionPayload, HTTPRequestPayload, HTTPResponsePayload, - RelayBalanceRequestPayload, - RelayBalanceResponsePayload, ) from tribler.core.utilities.bencodecheck import is_bencoded from tribler.core.utilities.path_util import Path @@ -65,18 +49,16 @@ class TriblerTunnelCommunity(HiddenTunnelCommunity): """ This community is built upon the anonymous messaging layer in IPv8. - It adds support for libtorrent anonymous downloads and bandwidth token payout when closing circuits. + It adds support for libtorrent anonymous downloads. """ community_id = unhexlify('a3591a6bd89bbaca0974062a1287afcfbc6fd6bb') def __init__(self, *args, **kwargs): - self.bandwidth_community = kwargs.pop('bandwidth_community', None) self.exitnode_cache: Optional[Path] = kwargs.pop('exitnode_cache', None) self.config = kwargs.pop('config', None) self.notifier = kwargs.pop('notifier', None) self.download_manager = kwargs.pop('dlmgr', None) self.socks_servers: List[Socks5Server] = kwargs.pop('socks_servers', []) - num_competing_slots = self.config.competing_slots num_random_slots = self.config.random_slots super().__init__(args_kwargs_to_community_settings(self.settings_class, args, kwargs)) @@ -90,7 +72,6 @@ def __init__(self, *args, **kwargs): self.bittorrent_peers = {} self.dispatcher = TunnelDispatcher(self) self.download_states = {} - self.competing_slots = [(0, None)] * num_competing_slots # 1st tuple item = token balance, 2nd = circuit id self.random_slots = [None] * num_random_slots # This callback is invoked with a tuple (time, balance) when we reject a circuit self.reject_callback: Optional[Callable] = None @@ -101,17 +82,9 @@ def __init__(self, *args, **kwargs): for server in self.socks_servers: server.output_stream = self.dispatcher - self.add_message_handler(BandwidthTransactionPayload, self.on_payout) - - self.add_cell_handler(BalanceRequestPayload, self.on_balance_request_cell) - self.add_cell_handler(RelayBalanceRequestPayload, self.on_relay_balance_request_cell) - self.add_cell_handler(BalanceResponsePayload, self.on_balance_response_cell) - self.add_cell_handler(RelayBalanceResponsePayload, self.on_relay_balance_response_cell) self.add_cell_handler(HTTPRequestPayload, self.on_http_request) self.add_cell_handler(HTTPResponsePayload, self.on_http_response) - NO_CRYPTO_PACKETS.extend([BalanceRequestPayload.msg_id, BalanceResponsePayload.msg_id]) - if self.exitnode_cache is not None: self.restore_exitnodes_from_disk() if self.download_manager is not None: @@ -163,130 +136,23 @@ def restore_exitnodes_from_disk(self): else: self.logger.warning('Could not retrieve backup exitnode cache, file does not exist!') - def on_token_balance(self, circuit_id, balance): - """ - We received the token balance of a circuit initiator. Check whether we can allocate a slot to this user. - """ - if not self.request_cache.has("balance-request", circuit_id): - self.logger.warning("Received token balance without associated request cache!") - return - - cache = self.request_cache.pop("balance-request", circuit_id) - - lowest_balance = sys.maxsize - lowest_index = -1 - for ind, tup in enumerate(self.competing_slots): - if not tup[1]: - # The slot is empty, take it - self.competing_slots[ind] = (balance, circuit_id) - cache.balance_future.set_result(True) - return - - if tup[0] < lowest_balance: - lowest_balance = tup[0] - lowest_index = ind - - if balance > lowest_balance: - # We kick this user out - old_circuit_id = self.competing_slots[lowest_index][1] - self.logger.info("Kicked out circuit %s (balance: %s) in favor of %s (balance: %s)", - old_circuit_id, lowest_balance, circuit_id, balance) - self.competing_slots[lowest_index] = (balance, circuit_id) - - self.remove_relay(old_circuit_id, destroy=DESTROY_REASON_BALANCE) - self.remove_exit_socket(old_circuit_id, destroy=DESTROY_REASON_BALANCE) - - cache.balance_future.set_result(True) - else: - # We can't compete with the balances in the existing slots - if self.reject_callback: - self.reject_callback(time.time(), balance) - cache.balance_future.set_result(False) - def should_join_circuit(self, create_payload, previous_node_address): """ Check whether we should join a circuit. Returns a future that fires with a boolean. """ - if self.settings.max_joined_circuits <= len(self.relay_from_to) + len(self.exit_sockets): - self.logger.warning("too many relays (%d)", (len(self.relay_from_to) + len(self.exit_sockets))) + joined_circuits = len(self.relay_from_to) + len(self.exit_sockets) + if self.settings.max_joined_circuits <= joined_circuits: + self.logger.warning("too many relays (%d)", joined_circuits) return succeed(False) circuit_id = create_payload.circuit_id - if self.request_cache.has('balance-request', circuit_id): - self.logger.warning("balance request already in progress for circuit %d", circuit_id) - return succeed(False) # Check whether we have a random open slot, if so, allocate this to this request. for index, slot in enumerate(self.random_slots): if not slot: self.random_slots[index] = circuit_id return succeed(True) - - # No random slots but this user might be allocated a competing slot. - # Next, we request the token balance of the circuit initiator. - self.logger.info("Requesting balance of circuit initiator!") - balance_future = Future() - self.request_cache.add(BalanceRequestCache(self, circuit_id, balance_future)) - - # Temporarily add these values, otherwise we are unable to communicate with the previous hop. - self.directions[circuit_id] = EXIT_NODE - shared_secret, _, _ = self.crypto.generate_diffie_shared_secret(create_payload.key) - self.relay_session_keys[circuit_id] = self.crypto.generate_session_keys(shared_secret) - - self.send_cell(Peer(create_payload.node_public_key, previous_node_address), - BalanceRequestPayload(circuit_id, create_payload.identifier)) - - self.directions.pop(circuit_id, None) - self.relay_session_keys.pop(circuit_id, None) - - return balance_future - - @unpack_cell(BalanceRequestPayload) - def on_balance_request_cell(self, _, payload, __): - if self.request_cache.has("create", payload.identifier): - request = self.request_cache.get("create", payload.identifier) - forwarding_relay = RelayRoute(request.from_circuit_id, request.peer) - self.send_cell(forwarding_relay.peer, RelayBalanceRequestPayload(forwarding_relay.circuit_id)) - elif self.request_cache.has("retry", payload.circuit_id): - self.on_balance_request(payload) - else: - self.logger.warning("Circuit creation cache for id %s not found!", payload.circuit_id) - - @unpack_cell(RelayBalanceRequestPayload) - def on_relay_balance_request_cell(self, source_address, payload, _): - self.on_balance_request(payload) - - def on_balance_request(self, payload): - """ - We received a balance request from a relay or exit node. Respond with the latest block in our chain. - """ - if not self.bandwidth_community: - self.logger.warning("Bandwidth community is not available, unable to send a balance response!") - return - - # Get the latest block - - # Get the current balance and send it back - balance = self.bandwidth_community.database.get_balance(self.my_peer.public_key.key_to_bin()) - - # We either send the response directly or relay the response to the last verified hop - circuit = self.circuits[payload.circuit_id] - if not circuit.hops: - self.send_cell(circuit.peer, BalanceResponsePayload(circuit.circuit_id, balance)) - else: - self.send_cell(circuit.peer, RelayBalanceResponsePayload(circuit.circuit_id, balance)) - - @unpack_cell(BalanceResponsePayload) - def on_balance_response_cell(self, source_address, payload, _): - self.on_token_balance(payload.circuit_id, payload.balance) - - @unpack_cell(RelayBalanceResponsePayload) - def on_relay_balance_response_cell(self, source_address, payload, _): - # At this point, we don't have the circuit ID of the follow-up hop. We have to iterate over the items in the - # request cache and find the link to the next hop. - for cache in self.request_cache._identifiers.values(): - if isinstance(cache, CreateRequestCache) and cache.from_circuit_id == payload.circuit_id: - self.send_cell(cache.to_peer, BalanceResponsePayload(cache.to_circuit_id, payload.balance)) + return succeed(False) def readd_bittorrent_peers(self): for torrent, peers in list(self.bittorrent_peers.items()): @@ -311,66 +177,6 @@ def update_torrent(self, peers, download): if self.find_circuits(): self.readd_bittorrent_peers() - def do_payout(self, peer: Peer, circuit_id: int, amount: int, base_amount: int) -> None: - """ - Perform a payout to a specific peer. - :param peer: The peer to perform the payout to, usually the next node in the circuit. - :param circuit_id: The circuit id of the payout, used by the subsequent node. - :param amount: The amount to put in the transaction, multiplier of base_amount. - :param base_amount: The base amount for the payout. - """ - self.logger.info("Sending payout of %d (base: %d) to %s (cid: %s)", amount, base_amount, peer, circuit_id) - - tx = self.bandwidth_community.construct_signed_transaction(peer, amount) - try: - self.bandwidth_community.database.BandwidthTransaction.insert(tx) - except OrmError as e: - self.logger.exception(e) - return - - payload = BandwidthTransactionPayload.from_transaction(tx, circuit_id, base_amount) - packet = self._ez_pack(self._prefix, 30, [payload], False) - self.send_packet(peer, packet) - - def on_payout(self, source_address: Address, data: bytes) -> None: - """ - We received a payout from another peer. - :param source_address: The address of the peer that sent us this payout. - :param data: The serialized, raw data. - """ - if not self.bandwidth_community: - self.logger.warning("Got payout while not having a bandwidth community running!") - return - - payload = self._ez_unpack_noauth(BandwidthTransactionPayload, data, global_time=False) - tx = BandwidthTransactionData.from_payload(payload) - - if not tx.is_valid(): - self.logger.info("Received invalid bandwidth transaction in tunnel community - ignoring it") - return - - from_peer = Peer(payload.public_key_a, source_address) - my_pk = self.my_peer.public_key.key_to_bin() - latest_tx = self.bandwidth_community.database.get_latest_transaction( - self.my_peer.public_key.key_to_bin(), from_peer.public_key.key_to_bin()) - if payload.circuit_id != 0 and tx.public_key_b == my_pk and (not latest_tx or latest_tx.amount < tx.amount): - # Sign it and send it back - tx.sign(self.my_peer.key, as_a=False) - self.bandwidth_community.database.BandwidthTransaction.insert(tx) - - response_payload = BandwidthTransactionPayload.from_transaction(tx, 0, payload.base_amount) - packet = self._ez_pack(self._prefix, 30, [response_payload], False) - self.send_packet(from_peer, packet) - elif payload.circuit_id == 0 and tx.public_key_a == my_pk: - if not latest_tx or (latest_tx and latest_tx.amount >= tx.amount): - self.bandwidth_community.database.BandwidthTransaction.insert(tx) - - # Send the next payout - if payload.circuit_id in self.relay_from_to and tx.amount > payload.base_amount: - relay = self.relay_from_to[payload.circuit_id] - self._logger.info("Sending next payout to peer %s", relay.peer) - self.do_payout(relay.peer, relay.circuit_id, payload.base_amount * 2, payload.base_amount) - def clean_from_slots(self, circuit_id): """ Clean a specific circuit from the allocated slots. @@ -379,10 +185,6 @@ def clean_from_slots(self, circuit_id): if slot == circuit_id: self.random_slots[ind] = None - for ind, tup in enumerate(self.competing_slots): - if tup[1] == circuit_id: - self.competing_slots[ind] = (0, None) - def remove_circuit(self, circuit_id, additional_info='', remove_now=False, destroy=False): if circuit_id not in self.circuits: self.logger.warning("Circuit %d not found when trying to remove it", circuit_id) @@ -394,22 +196,6 @@ def remove_circuit(self, circuit_id, additional_info='', remove_now=False, destr if self.notifier: self.notifier[notifications.circuit_removed](circuit, additional_info) - # Ignore circuits that are closing so we do not payout again if we receive a destroy message. - if circuit.state != CIRCUIT_STATE_CLOSING and self.bandwidth_community: - - # We should perform a payout of the removed circuit. - if circuit.ctype == CIRCUIT_TYPE_RP_DOWNLOADER: - # We remove an e2e circuit as downloader. We pay the subsequent nodes in the downloader part of the e2e - # circuit. In addition, we pay for one hop seeder anonymity since we don't know the circuit length at - # the seeder side. - self.do_payout(circuit.peer, circuit_id, circuit.bytes_down * ((circuit.goal_hops * 2) + 1), - circuit.bytes_down) - - if circuit.ctype == CIRCUIT_TYPE_DATA: - # We remove a regular data circuit as downloader. Pay the relay nodes and the exit nodes. - self.do_payout(circuit.peer, circuit_id, circuit.bytes_down * (circuit.goal_hops * 2 - 1), - circuit.bytes_down) - affected_peers = self.dispatcher.circuit_dead(circuit) # Make sure the circuit is marked as closing, otherwise we may end up reusing it diff --git a/src/tribler/core/components/tunnel/settings.py b/src/tribler/core/components/tunnel/settings.py index 92ee8b86610..bda267fbd88 100644 --- a/src/tribler/core/components/tunnel/settings.py +++ b/src/tribler/core/components/tunnel/settings.py @@ -6,8 +6,7 @@ class TunnelCommunitySettings(TriblerConfigSection): enabled: bool = True exitnode_enabled: bool = False - random_slots: int = 5 - competing_slots: int = 15 + random_slots: int = 20 testnet: bool = Field(default=False, env='TUNNEL_TESTNET') min_circuits: int = 3 max_circuits: int = 10 diff --git a/src/tribler/core/components/tunnel/tests/test_triblertunnel_community.py b/src/tribler/core/components/tunnel/tests/test_triblertunnel_community.py index d9df2cd1517..ffd905103fc 100644 --- a/src/tribler/core/components/tunnel/tests/test_triblertunnel_community.py +++ b/src/tribler/core/components/tunnel/tests/test_triblertunnel_community.py @@ -1,13 +1,12 @@ from __future__ import annotations import os -from asyncio import Future, TimeoutError as AsyncTimeoutError, sleep, wait_for +from asyncio import TimeoutError as AsyncTimeoutError, wait_for from collections import defaultdict from random import random from unittest.mock import Mock import pytest - from ipv8.keyvault.public.libnaclkey import LibNaCLPK from ipv8.messaging.anonymization.payload import EstablishIntroPayload from ipv8.messaging.anonymization.tunnel import ( @@ -24,13 +23,7 @@ from ipv8.test.mocking.exit_socket import MockTunnelExitSocket from ipv8.util import succeed -from tribler.core.components.bandwidth_accounting.community.bandwidth_accounting_community import ( - BandwidthAccountingCommunity, -) -from tribler.core.components.bandwidth_accounting.db.database import BandwidthDatabase -from tribler.core.components.bandwidth_accounting.settings import BandwidthAccountingSettings from tribler.core.components.ipv8.adapters_tests import TriblerMockIPv8 -from tribler.core.components.tunnel.community.payload import BandwidthTransactionPayload from tribler.core.components.tunnel.community.tunnel_community import PEER_FLAG_EXIT_HTTP, TriblerTunnelCommunity from tribler.core.components.tunnel.settings import TunnelCommunitySettings from tribler.core.tests.tools.base_test import MockObject @@ -38,7 +31,6 @@ from tribler.core.utilities.network_utils import NetworkUtils from tribler.core.utilities.path_util import Path from tribler.core.utilities.simpledefs import DownloadStatus -from tribler.core.utilities.utilities import MEMORY_DB @pytest.mark.usefixtures("tmp_path") @@ -54,8 +46,6 @@ def setUp(self): async def tearDown(self): test_community.global_dht_services = defaultdict(list) # Reset the global_dht_services variable - for node in self.nodes: - await node.overlay.bandwidth_community.unload() await super().tearDown() def create_node(self, *args, **kwargs): @@ -67,12 +57,6 @@ def create_node(self, *args, **kwargs): ) mock_ipv8.overlay.settings.max_circuits = 1 - db = BandwidthDatabase(db_path=MEMORY_DB, my_pub_key=mock_ipv8.my_peer.public_key.key_to_bin()) - - # Load the bandwidth accounting community - mock_ipv8.overlay.bandwidth_community = BandwidthAccountingCommunity( - mock_ipv8.my_peer, mock_ipv8.endpoint, mock_ipv8.network, - settings=BandwidthAccountingSettings(), database=db) mock_ipv8.overlay.dht_provider = MockDHTProvider(Peer(mock_ipv8.overlay.my_peer.key, mock_ipv8.overlay.my_estimated_wan)) @@ -311,64 +295,6 @@ def test_update_torrent(self): self.nodes[0].overlay.bittorrent_peers[mock_download] = {('4.4.4.4', 4)} self.nodes[0].overlay.update_torrent(peers, mock_download) - async def test_payouts(self): - """ - Test whether nodes are correctly paid after transferring data - """ - self.add_node_to_experiment(self.create_node()) - self.add_node_to_experiment(self.create_node()) - - # Make sure that every node has some initial transactions. This will help us to detect bugs in the - # relay payout logic, e.g. https://github.com/Tribler/tribler/issues/5789. - for node in self.nodes: - for other_node in self.nodes: - if node == other_node: - continue - - await node.overlay.bandwidth_community.do_payout(other_node.my_peer, 100 * 1024 * 1024) - - # Build a tunnel - self.nodes[2].overlay.settings.peer_flags.add(PEER_FLAG_EXIT_BT) - await self.introduce_nodes() - self.nodes[0].overlay.build_tunnels(2) - await self.deliver_messages(timeout=.5) - - self.assertEqual(self.nodes[0].overlay.tunnels_ready(2), 1.0) - - # Destroy the circuit - for circuit_id, circuit in list(self.nodes[0].overlay.circuits.items()): - circuit.bytes_down = 250 * 1024 * 1024 - await self.nodes[0].overlay.remove_circuit(circuit_id, destroy=1) - await self.deliver_messages() - - # Verify whether the downloader (node 0) correctly paid the relay and exit nodes. - self.assertTrue(self.nodes[0].overlay.bandwidth_community.database.get_my_balance() < 0) - self.assertTrue(self.nodes[1].overlay.bandwidth_community.database.get_my_balance() > 0) - self.assertTrue(self.nodes[2].overlay.bandwidth_community.database.get_my_balance() > 0) - - balances = [] - for node_nr in [0, 1, 2]: - balances.append(self.nodes[node_nr].overlay.bandwidth_community.database.get_my_balance()) - - balances.sort() - self.assertEqual(balances[0], -750 * 1024 * 1024) - self.assertEqual(balances[1], 250 * 1024 * 1024) - self.assertEqual(balances[2], 500 * 1024 * 1024) - - async def test_invalid_payout(self): - """ - Test whether an invalid payout to another peer is ignored - """ - self.add_node_to_experiment(self.create_node()) - - tx = self.nodes[0].overlay.bandwidth_community.construct_signed_transaction(self.nodes[1].my_peer, 1024 * 1024) - tx.signature_a = b"a" * 32 - payload = BandwidthTransactionPayload.from_transaction(tx, 0, 1024) - packet = self.nodes[0].overlay._ez_pack(self.nodes[0].overlay._prefix, 30, [payload], False) - self.nodes[0].overlay.send_packet(self.nodes[1].my_peer, packet) - - assert not self.nodes[1].overlay.bandwidth_community.database.get_my_balance() - async def test_circuit_reject_too_many(self): """ Test whether a circuit is rejected by an exit node if it already joined the max number of circuits @@ -382,155 +308,6 @@ async def test_circuit_reject_too_many(self): self.assertEqual(self.nodes[0].overlay.tunnels_ready(1), 0.0) - async def test_payouts_e2e(self): - """ - Check if payouts work for an e2e-linked circuit - """ - self.add_node_to_experiment(self.create_node()) - self.add_node_to_experiment(self.create_node()) - - service = b'0' * 20 - - self.nodes[0].overlay.join_swarm(service, 1, seeding=False) - - await self.introduce_nodes() - await self.create_intro(2, service) - await self.assign_exit_node(0) - - await self.nodes[0].overlay.do_peer_discovery() - - await self.deliver_messages(timeout=0.5) - - # Destroy the e2e-circuit - removed_circuits = [] - for circuit_id, circuit in self.nodes[0].overlay.circuits.items(): - if circuit.ctype == CIRCUIT_TYPE_RP_DOWNLOADER: - circuit.bytes_down = 250 * 1024 * 1024 - self.nodes[0].overlay.remove_circuit(circuit_id, destroy=1) - removed_circuits.append(circuit_id) - - await sleep(0.5) - - # Verify whether the downloader (node 0) correctly paid the subsequent nodes. - self.assertTrue(self.nodes[0].overlay.bandwidth_community.database.get_my_balance() < 0) - self.assertTrue(self.nodes[1].overlay.bandwidth_community.database.get_my_balance() >= 0) - self.assertTrue(self.nodes[2].overlay.bandwidth_community.database.get_my_balance() > 0) - - # Ensure balances remain unchanged after calling remove_circuit a second time - balances = [self.nodes[i].overlay.bandwidth_community.database.get_my_balance() for i in range(3)] - for circuit_id in removed_circuits: - self.nodes[0].overlay.remove_circuit(circuit_id, destroy=1) - for i in range(3): - self.assertEqual(self.nodes[i].overlay.bandwidth_community.database.get_my_balance(), balances[i]) - - async def test_decline_competing_slot(self): - """ - Test whether a circuit is not created when a node does not have enough balance for a competing slot - """ - self.add_node_to_experiment(self.create_node()) - self.nodes[1].overlay.settings.peer_flags.add(PEER_FLAG_EXIT_BT) - await self.introduce_nodes() - self.nodes[1].overlay.random_slots = [] - self.nodes[1].overlay.competing_slots = [(1000, 1234)] - self.nodes[0].overlay.build_tunnels(1) - await self.deliver_messages() - - # Assert whether we didn't create the circuit - self.assertEqual(self.nodes[0].overlay.tunnels_ready(1), 0.0) - - async def test_win_competing_slot(self): - """ - Test whether a circuit is created when a node has enough balance for a competing slot - """ - self.add_node_to_experiment(self.create_node()) - self.nodes[1].overlay.settings.peer_flags.add(PEER_FLAG_EXIT_BT) - await self.introduce_nodes() - self.nodes[1].overlay.random_slots = [] - self.nodes[1].overlay.competing_slots = [(-1000, 1234)] - self.nodes[0].overlay.build_tunnels(1) - await self.deliver_messages() - - # Assert whether we didn't create the circuit - self.assertEqual(self.nodes[0].overlay.tunnels_ready(1), 1.0) - - async def test_empty_competing_slot(self): - """ - Test whether a circuit is created when a node takes an empty competing slot - """ - self.add_node_to_experiment(self.create_node()) - self.nodes[1].overlay.settings.peer_flags.add(PEER_FLAG_EXIT_BT) - await self.introduce_nodes() - self.nodes[1].overlay.random_slots = [] - self.nodes[1].overlay.competing_slots = [(0, None)] - self.nodes[0].overlay.build_tunnels(1) - await self.deliver_messages() - - # Assert whether we did create the circuit - self.assertEqual(self.nodes[0].overlay.tunnels_ready(1), 1.0) - - async def test_win_competing_slot_exit(self): - """ - Test whether a two-hop circuit is created when a node has enough balance for a competing slot at the exit - """ - self.add_node_to_experiment(self.create_node()) - self.add_node_to_experiment(self.create_node()) - self.nodes[2].overlay.settings.peer_flags.add(PEER_FLAG_EXIT_BT) - await self.introduce_nodes() - self.nodes[2].overlay.random_slots = [] - self.nodes[2].overlay.competing_slots = [(-1000, 1234)] - self.nodes[0].overlay.build_tunnels(2) - await self.deliver_messages() - - # Assert whether we did create the circuit - self.assertEqual(self.nodes[0].overlay.tunnels_ready(2), 1.0) - - async def test_win_competing_slot_relay(self): - """ - Test whether a two-hop circuit is created when a node has enough balance for a competing slot - """ - self.add_node_to_experiment(self.create_node()) - self.add_node_to_experiment(self.create_node()) - self.nodes[2].overlay.settings.peer_flags.add(PEER_FLAG_EXIT_BT) - await self.introduce_nodes() - self.nodes[1].overlay.random_slots = [] - self.nodes[1].overlay.competing_slots = [(-1000, 1234)] - self.nodes[0].overlay.build_tunnels(2) - await self.deliver_messages() - - # Assert whether we did create the circuit - self.assertEqual(self.nodes[0].overlay.tunnels_ready(2), 1.0) - - async def test_payout_on_competition_kick(self): - """ - Test whether a payout is initiated when an existing node is kicked out from a competing slot - """ - self.add_node_to_experiment(self.create_node()) - self.add_node_to_experiment(self.create_node()) - self.nodes[2].overlay.settings.peer_flags.add(PEER_FLAG_EXIT_BT) - await self.introduce_nodes() - - # Make sure that there's a token disbalance between node 0 and 1 - await self.nodes[0].overlay.bandwidth_community.do_payout(self.nodes[1].my_peer, 1024 * 1024) - - self.nodes[2].overlay.random_slots = [] - self.nodes[2].overlay.competing_slots = [(0, None)] - self.nodes[0].overlay.build_tunnels(1) - await self.deliver_messages() - - # Let some artificial data flow over the circuit - list(self.nodes[0].overlay.circuits.values())[0].bytes_down = 250 * 1024 * 1024 - - self.assertEqual(self.nodes[0].overlay.tunnels_ready(1), 1.0) - self.assertTrue(self.nodes[2].overlay.exit_sockets) - - self.nodes[1].overlay.build_tunnels(1) - await self.deliver_messages() - self.assertTrue(self.nodes[2].overlay.exit_sockets) - self.assertEqual(self.nodes[1].overlay.tunnels_ready(1), 1.0) - - # Check whether the exit node has been paid - self.assertGreaterEqual(self.nodes[2].overlay.bandwidth_community.database.get_my_balance(), 250 * 1024 * 1024) - async def test_intro_point_slot(self): """ Test whether a introduction point occupies a slot @@ -550,36 +327,6 @@ async def test_intro_point_slot(self): await self.deliver_messages() self.assertFalse(exit_socket.circuit_id in self.nodes[1].overlay.random_slots) - async def test_reject_callback(self): - """ - Test whether the rejection callback is correctly invoked when a circuit request is rejected - """ - reject_future = Future() - self.add_node_to_experiment(self.create_node()) - self.nodes[1].overlay.settings.peer_flags.add(PEER_FLAG_EXIT_BT) - await self.introduce_nodes() - - # Make sure that there's a token disbalance between node 0 and 1 - await self.nodes[0].overlay.bandwidth_community.do_payout(self.nodes[1].my_peer, 1024 * 1024) - - def on_reject(_, balance): - self.assertEqual(balance, -1024 * 1024) - reject_future.set_result(None) - - self.nodes[1].overlay.reject_callback = on_reject - - # Initialize the slots - self.nodes[1].overlay.random_slots = [] - self.nodes[1].overlay.competing_slots = [(100000000, 12345)] - - self.nodes[0].overlay.build_tunnels(1) - await self.deliver_messages() - - self.assertEqual(self.nodes[0].overlay.tunnels_ready(1), 0.0) - - # Node 0 should be rejected and the reject callback should be invoked by node 1 - await reject_future - async def test_perform_http_request(self): """ Test whether we can make a http request through a circuit @@ -664,7 +411,7 @@ async def test_perform_http_request_failed(self): def test_cache_exitnodes_to_disk(self): """ Test whether we can cache exit nodes to disk """ - self.overlay(0).candidates = {Peer(LibNaCLPK(b'\x00'*64), ("0.1.2.3", 1029)): {PEER_FLAG_EXIT_BT}} + self.overlay(0).candidates = {Peer(LibNaCLPK(b'\x00' * 64), ("0.1.2.3", 1029)): {PEER_FLAG_EXIT_BT}} self.overlay(0).exitnode_cache = self.tmp_path / 'exitnode_cache.dat' self.overlay(0).cache_exitnodes_to_disk() @@ -672,8 +419,19 @@ def test_cache_exitnodes_to_disk(self): def test_cache_exitnodes_to_disk_os_error(self): """ Test whether we can handle an OSError when caching exit nodes to disk and raise no errors """ - self.overlay(0).candidates = {Peer(LibNaCLPK(b'\x00'*64), ("0.1.2.3", 1029)): {PEER_FLAG_EXIT_BT}} + self.overlay(0).candidates = {Peer(LibNaCLPK(b'\x00' * 64), ("0.1.2.3", 1029)): {PEER_FLAG_EXIT_BT}} self.overlay(0).exitnode_cache = Mock(write_bytes=Mock(side_effect=FileNotFoundError)) self.overlay(0).cache_exitnodes_to_disk() assert self.overlay(0).exitnode_cache.write_bytes.called + + async def test_should_join_circuit(self): + """ Test whether we can join a circuit""" + community: TriblerTunnelCommunity = self.overlay(0) + assert await community.should_join_circuit(create_payload=Mock(), previous_node_address=Mock()) + + async def test_should_join_circuit_no_slots(self): + """ Test whether we can not join a circuit when we have no slots""" + community: TriblerTunnelCommunity = self.overlay(0) + community.random_slots = [] + assert not await community.should_join_circuit(create_payload=Mock(), previous_node_address=Mock()) diff --git a/src/tribler/core/components/tunnel/tunnel_component.py b/src/tribler/core/components/tunnel/tunnel_component.py index ae89702fb95..10fce6db51e 100644 --- a/src/tribler/core/components/tunnel/tunnel_component.py +++ b/src/tribler/core/components/tunnel/tunnel_component.py @@ -1,7 +1,6 @@ from ipv8.dht.provider import DHTCommunityProvider from ipv8.messaging.anonymization.community import TunnelSettings -from tribler.core.components.bandwidth_accounting.bandwidth_accounting_component import BandwidthAccountingComponent from tribler.core.components.component import Component from tribler.core.components.ipv8.ipv8_component import INFINITE, Ipv8Component from tribler.core.components.libtorrent.libtorrent_component import LibtorrentComponent @@ -24,9 +23,6 @@ async def run(self): self._ipv8_component = await self.require_component(Ipv8Component) dht_discovery_community = self._ipv8_component.dht_discovery_community - bandwidth_component = await self.get_component(BandwidthAccountingComponent) - bandwidth_community = bandwidth_component.community if bandwidth_component else None - download_component = await self.get_component(LibtorrentComponent) download_manager = download_component.download_manager if download_component else None @@ -46,7 +42,6 @@ async def run(self): provider = DHTCommunityProvider(dht_discovery_community, config.ipv8.port) if dht_discovery_community else None exitnode_cache = config.state_dir / "exitnode_cache.dat" - # TODO: decouple bandwidth community and dlmgr to initiate later self.community = tunnel_cls(self._ipv8_component.peer, self._ipv8_component.ipv8.endpoint, self._ipv8_component.ipv8.network, @@ -54,7 +49,6 @@ async def run(self): config=config.tunnel_community, notifier=self.session.notifier, dlmgr=download_manager, - bandwidth_community=bandwidth_community, dht_provider=provider, exitnode_cache=exitnode_cache, settings=settings) diff --git a/src/tribler/core/config/tribler_config.py b/src/tribler/core/config/tribler_config.py index ccee0b92a30..64df1ee6efa 100644 --- a/src/tribler/core/config/tribler_config.py +++ b/src/tribler/core/config/tribler_config.py @@ -9,7 +9,6 @@ from configobj import ParseError from pydantic import BaseSettings, Extra, PrivateAttr, validate_model -from tribler.core.components.bandwidth_accounting.settings import BandwidthAccountingSettings from tribler.core.components.ipv8.settings import ( BootstrapSettings, DHTSettings, @@ -40,7 +39,6 @@ class Config: general: GeneralSettings = GeneralSettings() tunnel_community: TunnelCommunitySettings = TunnelCommunitySettings() - bandwidth_accounting: BandwidthAccountingSettings = BandwidthAccountingSettings() bootstrap: BootstrapSettings = BootstrapSettings() ipv8: Ipv8Settings = Ipv8Settings() discovery_community: DiscoveryCommunitySettings = DiscoveryCommunitySettings() diff --git a/src/tribler/core/start_core.py b/src/tribler/core/start_core.py index 23502f43d0f..cad77af6818 100644 --- a/src/tribler/core/start_core.py +++ b/src/tribler/core/start_core.py @@ -3,7 +3,6 @@ import logging.config import os import signal -import sys from pathlib import Path from typing import List, Optional @@ -12,8 +11,8 @@ check_and_enable_code_tracing, set_process_priority, ) -from tribler.core.components.bandwidth_accounting.bandwidth_accounting_component import BandwidthAccountingComponent from tribler.core.components.component import Component +from tribler.core.components.content_discovery.content_discovery_component import ContentDiscoveryComponent from tribler.core.components.database.database_component import DatabaseComponent from tribler.core.components.gui_process_watcher.gui_process_watcher import GuiProcessWatcher from tribler.core.components.gui_process_watcher.gui_process_watcher_component import GuiProcessWatcherComponent @@ -21,8 +20,6 @@ from tribler.core.components.key.key_component import KeyComponent from tribler.core.components.knowledge.knowledge_component import KnowledgeComponent from tribler.core.components.libtorrent.libtorrent_component import LibtorrentComponent -from tribler.core.components.payout.payout_component import PayoutComponent -from tribler.core.components.content_discovery.content_discovery_component import ContentDiscoveryComponent from tribler.core.components.reporter.exception_handler import default_core_exception_handler from tribler.core.components.reporter.reporter_component import ReporterComponent from tribler.core.components.resource_monitor.resource_monitor_component import ResourceMonitorComponent @@ -65,8 +62,6 @@ def components_gen(config: TriblerConfig): if config.libtorrent.enabled: yield LibtorrentComponent() - if config.ipv8.enabled: - yield BandwidthAccountingComponent() if config.resource_monitor.enabled: yield ResourceMonitorComponent() @@ -84,8 +79,6 @@ def components_gen(config: TriblerConfig): if config.ipv8.enabled and config.tunnel_community.enabled: yield TunnelsComponent() - if config.ipv8.enabled: - yield PayoutComponent() yield WatchFolderComponent() if config.general.version_checker_enabled: yield VersionCheckComponent() diff --git a/src/tribler/core/tests/tools/data/upgrade_databases/bandwidth_v8.db b/src/tribler/core/tests/tools/data/upgrade_databases/bandwidth_v8.db deleted file mode 100644 index b7138b418a7..00000000000 Binary files a/src/tribler/core/tests/tools/data/upgrade_databases/bandwidth_v8.db and /dev/null differ diff --git a/src/tribler/core/upgrade/tests/test_upgrader.py b/src/tribler/core/upgrade/tests/test_upgrader.py index eea1831b546..7e86ded7eb7 100644 --- a/src/tribler/core/upgrade/tests/test_upgrader.py +++ b/src/tribler/core/upgrade/tests/test_upgrader.py @@ -7,9 +7,8 @@ import pytest from ipv8.keyvault.private.libnaclkey import LibNaCLSK -from pony.orm import db_session, select +from pony.orm import db_session -from tribler.core.components.bandwidth_accounting.db.database import BandwidthDatabase from tribler.core.components.database.db.orm_bindings.torrent_metadata import CHANNEL_DIR_NAME_LENGTH from tribler.core.components.database.db.store import CURRENT_DB_VERSION, MetadataStore from tribler.core.tests.tools.common import TESTS_DATA_DIR @@ -19,6 +18,7 @@ cleanup_noncompliant_channel_torrents from tribler.core.utilities.configparser import CallbackConfigParser from tribler.core.utilities.db_corruption_handling.base import DatabaseIsCorrupted +from tribler.core.utilities.simpledefs import STATEDIR_DB_DIR from tribler.core.utilities.utilities import random_infohash @@ -326,19 +326,6 @@ def test_calc_progress(): assert calc_progress(1000, 100) == pytest.approx(99.158472, abs=EPSILON) -def test_upgrade_bw_accounting_db_8to9(upgrader, state_dir, trustchain_keypair): - bandwidth_path = state_dir / 'sqlite/bandwidth.db' - _copy('bandwidth_v8.db', bandwidth_path) - - upgrader.upgrade_bw_accounting_db_8to9() - db = BandwidthDatabase(bandwidth_path, trustchain_keypair.key.pk) - with db_session: - assert not list(select(tx for tx in db.BandwidthTransaction)) - assert not list(select(item for item in db.BandwidthHistory)) - assert int(db.MiscData.get(name="db_version").value) == 9 - db.shutdown() - - def test_remove_old_logs(upgrader: TriblerUpgrader, state_dir: Path, tmp_path): """Ensure that the `remove_old_logs` function removes only logs""" @@ -403,3 +390,20 @@ def patched_unlink(self, *_, **__): assert left == [side_effect_log_file] assert not normal_log_file.exists() assert side_effect_log_file.exists() + + +def test_remove_bandwidth_db(upgrader: TriblerUpgrader, state_dir: Path): + """ Ensure that the `remove_bandwidth_db` function removes only bandwidth db files""" + db_path = Path(state_dir / STATEDIR_DB_DIR) + + (db_path / 'bandwidth.db').touch() + (db_path / 'bandwidth.db-shm').touch() + (db_path / 'bandwidth.db-wal').touch() + (db_path / 'knowledge.db').touch() + + assert len(list(db_path.glob('*'))) == 4 + assert len(list(db_path.glob('bandwidth*'))) == 3 + + upgrader.remove_bandwidth_db() + + assert len(list(db_path.glob('bandwidth*'))) == 0 diff --git a/src/tribler/core/upgrade/upgrade.py b/src/tribler/core/upgrade/upgrade.py index 3d7be8a921f..6f588e99306 100644 --- a/src/tribler/core/upgrade/upgrade.py +++ b/src/tribler/core/upgrade/upgrade.py @@ -3,14 +3,14 @@ import shutil import time from configparser import MissingSectionHeaderError, ParsingError +from contextlib import suppress from functools import wraps from types import SimpleNamespace from typing import List, Optional, Tuple from ipv8.keyvault.private.libnaclkey import LibNaCLSK -from pony.orm import db_session, delete +from pony.orm import db_session -from tribler.core.components.bandwidth_accounting.db.database import BandwidthDatabase from tribler.core.components.database.db.orm_bindings.torrent_metadata import CHANNEL_DIR_NAME_LENGTH from tribler.core.components.database.db.store import ( CURRENT_DB_VERSION, MetadataStore, @@ -130,7 +130,6 @@ def run(self): self.upgrade_pony_db_8to10() self.upgrade_pony_db_10to11() convert_config_to_tribler76(self.state_dir) - self.upgrade_bw_accounting_db_8to9() self.upgrade_pony_db_11to12() self.upgrade_pony_db_12to13() self.upgrade_pony_db_13to14() @@ -138,6 +137,7 @@ def run(self): self.remove_old_logs() self.upgrade_pony_db_14to15() self.upgrade_knowledge_to_tribler_db() + self.remove_bandwidth_db() migration_chain = TriblerDatabaseMigrationChain(self.state_dir) migration_chain.execute() @@ -268,35 +268,6 @@ def upgrade_pony_db_10to11(self): self.do_upgrade_pony_db_10to11(mds) mds.shutdown() - @catch_db_is_corrupted_exception - def upgrade_bw_accounting_db_8to9(self): - """ - Upgrade the database with bandwidth accounting information from 8 to 9. - Specifically, this upgrade wipes all transactions and addresses an issue where payouts with the wrong amount - were made. Also see https://github.com/Tribler/tribler/issues/5789. - """ - self._logger.info('Upgrade bandwidth accounting DB 8 to 9') - to_version = 9 - - database_path = self.state_dir / STATEDIR_DB_DIR / 'bandwidth.db' - if not database_path.exists() or get_db_version(database_path, BandwidthDatabase.CURRENT_DB_VERSION) > 8: - # No need to update if the database does not exist or is already updated - return # pragma: no cover - - self._logger.info('bw8->9') - db = BandwidthDatabase(database_path, self.primary_key.key.pk) - - # Wipe all transactions and bandwidth history - with db_session: - delete(tx for tx in db.BandwidthTransaction) - delete(item for item in db.BandwidthHistory) - - # Update db version - db_version = db.MiscData.get(name="db_version") - db_version.value = str(to_version) - - db.shutdown() - def column_exists_in_table(self, db, table, column): pragma = f'SELECT COUNT(*) FROM pragma_table_info("{table}") WHERE name="{column}"' result = list(db.execute(pragma)) @@ -488,3 +459,13 @@ def upgrade_knowledge_to_tribler_db(self): self._logger.info('Upgrade knowledge to tribler.db') migration = MigrationKnowledgeToTriblerDB(self.state_dir) migration.run() + + def remove_bandwidth_db(self): + self._logger.info('Removing bandwidth database') + + db_path = Path(self.state_dir / STATEDIR_DB_DIR) + + for file_path in db_path.glob('bandwidth*'): + self._logger.info(f'Removing {file_path}') + with suppress(OSError): + file_path.unlink(missing_ok=True) diff --git a/src/tribler/gui/debug_window.py b/src/tribler/gui/debug_window.py index c6544dc77ce..e15b9292802 100644 --- a/src/tribler/gui/debug_window.py +++ b/src/tribler/gui/debug_window.py @@ -6,7 +6,6 @@ import sys from binascii import unhexlify from time import localtime, strftime, time -from typing import Dict import libtorrent import psutil @@ -179,8 +178,6 @@ def tab_changed(self, index): self.load_general_tab() elif index == 1: self.load_requests_tab() - elif index == 2: - self.run_with_timer(self.load_bandwidth_accounting_tab) elif index == 3: self.ipv8_tab_changed(self.window().ipv8_tab_widget.currentIndex()) elif index == 4: @@ -324,23 +321,6 @@ def load_requests_tab(self): item.setText(2, f"{strftime('%H:%M:%S', localtime(timestamp))}") self.window().requests_tree_widget.addTopLevelItem(item) - def load_bandwidth_accounting_tab(self) -> None: - """ - Initiate a request to the Tribler core to fetch statistics on bandwidth accounting. - """ - request_manager.get("bandwidth/statistics", self.on_bandwidth_statistics) - - def on_bandwidth_statistics(self, data: Dict) -> None: - """ - We received bandwidth statistics from the core. - :param data: The bandwidth statistics, in JSON format. - """ - if not data: - return - self.window().bandwidth_tree_widget.clear() - for key, value in data["statistics"].items(): - self.create_and_add_widget_item(key, value, self.window().bandwidth_tree_widget) - def load_ipv8_general_tab(self): request_manager.get("statistics/ipv8", self.on_ipv8_general_stats) diff --git a/src/tribler/gui/qt_resources/debugwindow.ui b/src/tribler/gui/qt_resources/debugwindow.ui index 9c0866ac45a..eb13b7ce3ab 100644 --- a/src/tribler/gui/qt_resources/debugwindow.ui +++ b/src/tribler/gui/qt_resources/debugwindow.ui @@ -112,49 +112,7 @@ - - - Bandwidth Accounting - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 2 - - - 200 - - - - Key - - - - - Value - - - - - - - + IPv8 @@ -413,7 +371,7 @@ - + Tunnels @@ -747,7 +705,7 @@ - + DHT @@ -862,7 +820,7 @@ - + Events @@ -920,7 +878,7 @@ - + System diff --git a/src/tribler/gui/tests/test_gui.py b/src/tribler/gui/tests/test_gui.py index 350c25bc03a..d08457514da 100644 --- a/src/tribler/gui/tests/test_gui.py +++ b/src/tribler/gui/tests/test_gui.py @@ -315,8 +315,6 @@ def test_settings(window): screenshot(window, name="settings_general") QTest.mouseClick(window.settings_connection_button, Qt.LeftButton) screenshot(window, name="settings_connection") - QTest.mouseClick(window.settings_bandwidth_button, Qt.LeftButton) - screenshot(window, name="settings_bandwidth") QTest.mouseClick(window.settings_seeding_button, Qt.LeftButton) screenshot(window, name="settings_seeding") QTest.mouseClick(window.settings_anonymity_button, Qt.LeftButton) @@ -497,10 +495,6 @@ def test_debug_pane(window): wait_for_list_populated(window.debug_window.requests_tree_widget) screenshot(window.debug_window, name="debug_panel_requests_tab") - window.debug_window.debug_tab_widget.setCurrentIndex(2) - wait_for_list_populated(window.debug_window.bandwidth_tree_widget) - screenshot(window.debug_window, name="debug_panel_bandwidth_tab") - window.debug_window.debug_tab_widget.setCurrentIndex(3) wait_for_list_populated(window.debug_window.ipv8_general_tree_widget) screenshot(window.debug_window, name="debug_panel_ipv8_tab")