From 2bcbc81a2a10c0f1d6e3ae86c64817449519d695 Mon Sep 17 00:00:00 2001 From: Gabriel Levcovitz Date: Fri, 3 Nov 2023 13:54:02 -0300 Subject: [PATCH] refactor(settings): change daa to use injected settings --- hathor/builder/builder.py | 38 ++- hathor/builder/cli_builder.py | 10 +- hathor/cli/events_simulator/scenario.py | 5 +- hathor/cli/mining.py | 4 +- hathor/daa.py | 306 +++++++++--------- hathor/manager.py | 10 +- hathor/p2p/resources/mining_info.py | 3 +- hathor/simulator/simulator.py | 32 +- hathor/simulator/tx_generator.py | 3 +- hathor/stratum/stratum.py | 2 +- hathor/transaction/resources/create_tx.py | 3 +- hathor/verification/block_verifier.py | 16 +- hathor/verification/transaction_verifier.py | 3 +- hathor/verification/verification_service.py | 12 +- hathor/verification/vertex_verifier.py | 6 +- .../resources/nano_contracts/execute.py | 3 +- hathor/wallet/resources/send_tokens.py | 3 +- hathor/wallet/resources/sign_tx.py | 3 +- tests/resources/wallet/test_thin_wallet.py | 7 +- tests/simulation/test_simulator.py | 2 +- tests/tx/test_block.py | 6 +- tests/tx/test_blockchain.py | 33 +- tests/tx/test_genesis.py | 15 +- tests/tx/test_tx.py | 3 +- tests/tx/test_tx_deserialization.py | 4 +- tests/unittest.py | 5 +- 26 files changed, 283 insertions(+), 254 deletions(-) diff --git a/hathor/builder/builder.py b/hathor/builder/builder.py index 203485172..25cdf4254 100644 --- a/hathor/builder/builder.py +++ b/hathor/builder/builder.py @@ -13,7 +13,7 @@ # limitations under the License. from enum import Enum -from typing import Any, Callable, NamedTuple, Optional +from typing import Any, Callable, NamedTuple, Optional, TypeAlias from structlog import get_logger @@ -21,6 +21,7 @@ from hathor.conf.get_settings import get_settings from hathor.conf.settings import HathorSettings as HathorSettingsType from hathor.consensus import ConsensusAlgorithm +from hathor.daa import DifficultyAdjustmentAlgorithm from hathor.event import EventManager from hathor.event.storage import EventMemoryStorage, EventRocksDBStorage, EventStorage from hathor.event.websocket import EventWebsocketFactory @@ -70,6 +71,12 @@ class BuildArtifacts(NamedTuple): stratum_factory: Optional[StratumFactory] +_VertexVerifiersBuilder: TypeAlias = Callable[ + [HathorSettingsType, DifficultyAdjustmentAlgorithm, FeatureService], + VertexVerifiers +] + + class Builder: """Builder builds the core objects to run a full node. @@ -103,8 +110,10 @@ def __init__(self) -> None: self._feature_service: Optional[FeatureService] = None self._bit_signaling_service: Optional[BitSignalingService] = None + self._daa: Optional[DifficultyAdjustmentAlgorithm] = None + self._vertex_verifiers: Optional[VertexVerifiers] = None - self._vertex_verifiers_builder: Callable[[HathorSettingsType, FeatureService], VertexVerifiers] | None = None + self._vertex_verifiers_builder: _VertexVerifiersBuilder | None = None self._verification_service: Optional[VerificationService] = None self._rocksdb_path: Optional[str] = None @@ -164,6 +173,7 @@ def build(self) -> BuildArtifacts: feature_service = self._get_or_create_feature_service() bit_signaling_service = self._get_or_create_bit_signaling_service() verification_service = self._get_or_create_verification_service() + daa = self._get_or_create_daa() if self._enable_address_index: indexes.enable_address_index(pubsub) @@ -188,6 +198,7 @@ def build(self) -> BuildArtifacts: network=self._network, pubsub=pubsub, consensus_algorithm=consensus_algorithm, + daa=daa, peer_id=peer_id, tx_storage=tx_storage, p2p_manager=p2p_manager, @@ -459,17 +470,26 @@ def _get_or_create_vertex_verifiers(self) -> VertexVerifiers: if self._vertex_verifiers is None: settings = self._get_or_create_settings() feature_service = self._get_or_create_feature_service() + daa = self._get_or_create_daa() if self._vertex_verifiers_builder: - self._vertex_verifiers = self._vertex_verifiers_builder(settings, feature_service) + self._vertex_verifiers = self._vertex_verifiers_builder(settings, daa, feature_service) else: self._vertex_verifiers = VertexVerifiers.create_defaults( settings=settings, - feature_service=feature_service + daa=daa, + feature_service=feature_service, ) return self._vertex_verifiers + def _get_or_create_daa(self) -> DifficultyAdjustmentAlgorithm: + if self._daa is None: + settings = self._get_or_create_settings() + self._daa = DifficultyAdjustmentAlgorithm(settings=settings) + + return self._daa + def use_memory(self) -> 'Builder': self.check_if_can_modify() self._storage_type = StorageType.MEMORY @@ -572,14 +592,16 @@ def set_vertex_verifiers(self, vertex_verifiers: VertexVerifiers) -> 'Builder': self._vertex_verifiers = vertex_verifiers return self - def set_vertex_verifiers_builder( - self, - builder: Callable[[HathorSettingsType, FeatureService], VertexVerifiers] - ) -> 'Builder': + def set_vertex_verifiers_builder(self, builder: _VertexVerifiersBuilder) -> 'Builder': self.check_if_can_modify() self._vertex_verifiers_builder = builder return self + def set_daa(self, daa: DifficultyAdjustmentAlgorithm) -> 'Builder': + self.check_if_can_modify() + self._daa = daa + return self + def set_reactor(self, reactor: Reactor) -> 'Builder': self.check_if_can_modify() self._reactor = reactor diff --git a/hathor/builder/cli_builder.py b/hathor/builder/cli_builder.py index db9997dd8..b9e12c7b8 100644 --- a/hathor/builder/cli_builder.py +++ b/hathor/builder/cli_builder.py @@ -23,6 +23,7 @@ from hathor.cli.run_node import RunNodeArgs from hathor.consensus import ConsensusAlgorithm +from hathor.daa import DifficultyAdjustmentAlgorithm from hathor.event import EventManager from hathor.exception import BuilderError from hathor.feature_activation.bit_signaling_service import BitSignalingService @@ -207,7 +208,13 @@ def create_manager(self, reactor: Reactor) -> HathorManager: not_support_features=self._args.signal_not_support ) - vertex_verifiers = VertexVerifiers.create_defaults(settings=settings, feature_service=self.feature_service) + daa = DifficultyAdjustmentAlgorithm(settings=settings) + + vertex_verifiers = VertexVerifiers.create_defaults( + settings=settings, + daa=daa, + feature_service=self.feature_service + ) verification_service = VerificationService(verifiers=vertex_verifiers) p2p_manager = ConnectionsManager( @@ -230,6 +237,7 @@ def create_manager(self, reactor: Reactor) -> HathorManager: hostname=hostname, pubsub=pubsub, consensus_algorithm=consensus_algorithm, + daa=daa, peer_id=peer_id, tx_storage=tx_storage, p2p_manager=p2p_manager, diff --git a/hathor/cli/events_simulator/scenario.py b/hathor/cli/events_simulator/scenario.py index ea8f16528..fd96a6e2d 100644 --- a/hathor/cli/events_simulator/scenario.py +++ b/hathor/cli/events_simulator/scenario.py @@ -50,7 +50,6 @@ def simulate_single_chain_one_block(simulator: 'Simulator', manager: 'HathorMana def simulate_single_chain_blocks_and_transactions(simulator: 'Simulator', manager: 'HathorManager') -> None: - from hathor import daa from hathor.conf.get_settings import get_settings from tests.utils import add_new_blocks, gen_new_tx @@ -62,13 +61,13 @@ def simulate_single_chain_blocks_and_transactions(simulator: 'Simulator', manage simulator.run(60) tx = gen_new_tx(manager, address, 1000) - tx.weight = daa.minimum_tx_weight(tx) + tx.weight = manager.daa.minimum_tx_weight(tx) tx.update_hash() assert manager.propagate_tx(tx, fails_silently=False) simulator.run(60) tx = gen_new_tx(manager, address, 2000) - tx.weight = daa.minimum_tx_weight(tx) + tx.weight = manager.daa.minimum_tx_weight(tx) tx.update_hash() assert manager.propagate_tx(tx, fails_silently=False) simulator.run(60) diff --git a/hathor/cli/mining.py b/hathor/cli/mining.py index cc7ba432b..0f48c8059 100644 --- a/hathor/cli/mining.py +++ b/hathor/cli/mining.py @@ -136,9 +136,11 @@ def execute(args: Namespace) -> None: block.nonce, block.weight)) try: + from hathor.daa import DifficultyAdjustmentAlgorithm from hathor.verification.block_verifier import BlockVerifier settings = get_settings() - verifier = BlockVerifier(settings=settings) + daa = DifficultyAdjustmentAlgorithm(settings=settings) + verifier = BlockVerifier(settings=settings, daa=daa) verifier.verify_without_storage(block) except HathorError: print('[{}] ERROR: Block has not been pushed because it is not valid.'.format(datetime.datetime.now())) diff --git a/hathor/daa.py b/hathor/daa.py index b812d1a39..ece55766a 100644 --- a/hathor/daa.py +++ b/hathor/daa.py @@ -25,7 +25,7 @@ from structlog import get_logger -from hathor.conf import HathorSettings +from hathor.conf.settings import HathorSettings from hathor.profiler import get_cpu_profiler from hathor.util import iwindows @@ -33,12 +33,8 @@ from hathor.transaction import Block, Transaction logger = get_logger() -settings = HathorSettings() cpu = get_cpu_profiler() -MIN_BLOCK_WEIGHT = settings.MIN_BLOCK_WEIGHT -AVG_TIME_BETWEEN_BLOCKS = settings.AVG_TIME_BETWEEN_BLOCKS - class TestMode(IntFlag): __test__ = False @@ -58,173 +54,175 @@ def _set_test_mode(mode: TestMode) -> None: TEST_MODE = mode -@cpu.profiler(key=lambda block: 'calculate_block_difficulty!{}'.format(block.hash.hex())) -def calculate_block_difficulty(block: 'Block') -> float: - """ Calculate block weight according to the ascendents of `block`, using calculate_next_weight.""" - if TEST_MODE & TestMode.TEST_BLOCK_WEIGHT: - return 1.0 - - if block.is_genesis: - return MIN_BLOCK_WEIGHT - - return calculate_next_weight(block.get_block_parent(), block.timestamp) - - -def calculate_next_weight(parent_block: 'Block', timestamp: int) -> float: - """ Calculate the next block weight, aka DAA/difficulty adjustment algorithm. - - The algorithm used is described in [RFC 22](https://gitlab.com/HathorNetwork/rfcs/merge_requests/22). - - The weight must not be less than `MIN_BLOCK_WEIGHT`. - """ - if TEST_MODE & TestMode.TEST_BLOCK_WEIGHT: - return 1.0 - - from hathor.transaction import sum_weights - - root = parent_block - N = min(2 * settings.BLOCK_DIFFICULTY_N_BLOCKS, parent_block.get_height() - 1) - K = N // 2 - T = AVG_TIME_BETWEEN_BLOCKS - S = 5 - if N < 10: - return MIN_BLOCK_WEIGHT - - blocks: list['Block'] = [] - while len(blocks) < N + 1: - blocks.append(root) - root = root.get_block_parent() - assert root is not None - - # TODO: revise if this assertion can be safely removed - assert blocks == sorted(blocks, key=lambda tx: -tx.timestamp) - blocks = list(reversed(blocks)) - - assert len(blocks) == N + 1 - solvetimes, weights = zip(*( - (block.timestamp - prev_block.timestamp, block.weight) - for prev_block, block in iwindows(blocks, 2) - )) - assert len(solvetimes) == len(weights) == N, f'got {len(solvetimes)}, {len(weights)} expected {N}' - - sum_solvetimes = 0.0 - logsum_weights = 0.0 - - prefix_sum_solvetimes = [0] - for st in solvetimes: - prefix_sum_solvetimes.append(prefix_sum_solvetimes[-1] + st) - - # Loop through N most recent blocks. N is most recently solved block. - for i in range(K, N): - solvetime = solvetimes[i] - weight = weights[i] - x = (prefix_sum_solvetimes[i + 1] - prefix_sum_solvetimes[i - K]) / K - ki = K * (x - T)**2 / (2 * T * T) - ki = max(1, ki / S) - sum_solvetimes += ki * solvetime - logsum_weights = sum_weights(logsum_weights, log(ki, 2) + weight) - - weight = logsum_weights - log(sum_solvetimes, 2) + log(T, 2) - - # Apply weight decay - weight -= get_weight_decay_amount(timestamp - parent_block.timestamp) - - # Apply minimum weight - if weight < MIN_BLOCK_WEIGHT: - weight = MIN_BLOCK_WEIGHT - - return weight - - -def get_weight_decay_amount(distance: int) -> float: - """Return the amount to be reduced in the weight of the block.""" - if not settings.WEIGHT_DECAY_ENABLED: - return 0.0 - if distance < settings.WEIGHT_DECAY_ACTIVATE_DISTANCE: - return 0.0 - - dt = distance - settings.WEIGHT_DECAY_ACTIVATE_DISTANCE - - # Calculate the number of windows. - n_windows = 1 + (dt // settings.WEIGHT_DECAY_WINDOW_SIZE) - return n_windows * settings.WEIGHT_DECAY_AMOUNT - +class DifficultyAdjustmentAlgorithm: -def minimum_tx_weight(tx: 'Transaction') -> float: - """ Returns the minimum weight for the param tx - The minimum is calculated by the following function: + def __init__(self, *, settings: HathorSettings) -> None: + self._settings = settings + self.AVG_TIME_BETWEEN_BLOCKS = self._settings.AVG_TIME_BETWEEN_BLOCKS + self.MIN_BLOCK_WEIGHT = self._settings.MIN_BLOCK_WEIGHT - w = alpha * log(size, 2) + 4.0 + 4.0 - ---------------- - 1 + k / amount + @cpu.profiler(key=lambda _, block: 'calculate_block_difficulty!{}'.format(block.hash.hex())) + def calculate_block_difficulty(self, block: 'Block') -> float: + """ Calculate block weight according to the ascendents of `block`, using calculate_next_weight.""" + if TEST_MODE & TestMode.TEST_BLOCK_WEIGHT: + return 1.0 - :param tx: tx to calculate the minimum weight - :type tx: :py:class:`hathor.transaction.transaction.Transaction` + if block.is_genesis: + return self.MIN_BLOCK_WEIGHT - :return: minimum weight for the tx - :rtype: float - """ - # In test mode we don't validate the minimum weight for tx - # We do this to allow generating many txs for testing - if TEST_MODE & TestMode.TEST_TX_WEIGHT: - return 1.0 + return self.calculate_next_weight(block.get_block_parent(), block.timestamp) - if tx.is_genesis: - return settings.MIN_TX_WEIGHT + def calculate_next_weight(self, parent_block: 'Block', timestamp: int) -> float: + """ Calculate the next block weight, aka DAA/difficulty adjustment algorithm. - tx_size = len(tx.get_struct()) + The algorithm used is described in [RFC 22](https://gitlab.com/HathorNetwork/rfcs/merge_requests/22). - # We need to take into consideration the decimal places because it is inside the amount. - # For instance, if one wants to transfer 20 HTRs, the amount will be 2000. - # Max below is preventing division by 0 when handling authority methods that have no outputs - amount = max(1, tx.sum_outputs) / (10 ** settings.DECIMAL_PLACES) - weight = ( - + settings.MIN_TX_WEIGHT_COEFFICIENT * log(tx_size, 2) - + 4 / (1 + settings.MIN_TX_WEIGHT_K / amount) + 4 - ) + The weight must not be less than `MIN_BLOCK_WEIGHT`. + """ + if TEST_MODE & TestMode.TEST_BLOCK_WEIGHT: + return 1.0 + + from hathor.transaction import sum_weights + + root = parent_block + N = min(2 * self._settings.BLOCK_DIFFICULTY_N_BLOCKS, parent_block.get_height() - 1) + K = N // 2 + T = self.AVG_TIME_BETWEEN_BLOCKS + S = 5 + if N < 10: + return self.MIN_BLOCK_WEIGHT + + blocks: list['Block'] = [] + while len(blocks) < N + 1: + blocks.append(root) + root = root.get_block_parent() + assert root is not None - # Make sure the calculated weight is at least the minimum - weight = max(weight, settings.MIN_TX_WEIGHT) + # TODO: revise if this assertion can be safely removed + assert blocks == sorted(blocks, key=lambda tx: -tx.timestamp) + blocks = list(reversed(blocks)) - return weight + assert len(blocks) == N + 1 + solvetimes, weights = zip(*( + (block.timestamp - prev_block.timestamp, block.weight) + for prev_block, block in iwindows(blocks, 2) + )) + assert len(solvetimes) == len(weights) == N, f'got {len(solvetimes)}, {len(weights)} expected {N}' + + sum_solvetimes = 0.0 + logsum_weights = 0.0 + + prefix_sum_solvetimes = [0] + for st in solvetimes: + prefix_sum_solvetimes.append(prefix_sum_solvetimes[-1] + st) + # Loop through N most recent blocks. N is most recently solved block. + for i in range(K, N): + solvetime = solvetimes[i] + weight = weights[i] + x = (prefix_sum_solvetimes[i + 1] - prefix_sum_solvetimes[i - K]) / K + ki = K * (x - T)**2 / (2 * T * T) + ki = max(1, ki / S) + sum_solvetimes += ki * solvetime + logsum_weights = sum_weights(logsum_weights, log(ki, 2) + weight) -def get_tokens_issued_per_block(height: int) -> int: - """Return the number of tokens issued (aka reward) per block of a given height.""" - if settings.BLOCKS_PER_HALVING is None: - assert settings.MINIMUM_TOKENS_PER_BLOCK == settings.INITIAL_TOKENS_PER_BLOCK - return settings.MINIMUM_TOKENS_PER_BLOCK + weight = logsum_weights - log(sum_solvetimes, 2) + log(T, 2) - number_of_halvings = (height - 1) // settings.BLOCKS_PER_HALVING - number_of_halvings = max(0, number_of_halvings) + # Apply weight decay + weight -= self.get_weight_decay_amount(timestamp - parent_block.timestamp) - if number_of_halvings > settings.MAXIMUM_NUMBER_OF_HALVINGS: - return settings.MINIMUM_TOKENS_PER_BLOCK + # Apply minimum weight + if weight < self.MIN_BLOCK_WEIGHT: + weight = self.MIN_BLOCK_WEIGHT - amount = settings.INITIAL_TOKENS_PER_BLOCK // (2**number_of_halvings) - amount = max(amount, settings.MINIMUM_TOKENS_PER_BLOCK) - return amount + return weight + def get_weight_decay_amount(self, distance: int) -> float: + """Return the amount to be reduced in the weight of the block.""" + if not self._settings.WEIGHT_DECAY_ENABLED: + return 0.0 + if distance < self._settings.WEIGHT_DECAY_ACTIVATE_DISTANCE: + return 0.0 -def get_mined_tokens(height: int) -> int: - """Return the number of tokens mined in total at height - """ - assert settings.BLOCKS_PER_HALVING is not None - number_of_halvings = (height - 1) // settings.BLOCKS_PER_HALVING - number_of_halvings = max(0, number_of_halvings) + dt = distance - self._settings.WEIGHT_DECAY_ACTIVATE_DISTANCE - blocks_in_this_halving = height - number_of_halvings * settings.BLOCKS_PER_HALVING + # Calculate the number of windows. + n_windows = 1 + (dt // self._settings.WEIGHT_DECAY_WINDOW_SIZE) + return n_windows * self._settings.WEIGHT_DECAY_AMOUNT - tokens_per_block = settings.INITIAL_TOKENS_PER_BLOCK - mined_tokens = 0 + def minimum_tx_weight(self, tx: 'Transaction') -> float: + """ Returns the minimum weight for the param tx + The minimum is calculated by the following function: + + w = alpha * log(size, 2) + 4.0 + 4.0 + ---------------- + 1 + k / amount + + :param tx: tx to calculate the minimum weight + :type tx: :py:class:`hathor.transaction.transaction.Transaction` + + :return: minimum weight for the tx + :rtype: float + """ + # In test mode we don't validate the minimum weight for tx + # We do this to allow generating many txs for testing + if TEST_MODE & TestMode.TEST_TX_WEIGHT: + return 1.0 + + if tx.is_genesis: + return self._settings.MIN_TX_WEIGHT + + tx_size = len(tx.get_struct()) + + # We need to take into consideration the decimal places because it is inside the amount. + # For instance, if one wants to transfer 20 HTRs, the amount will be 2000. + # Max below is preventing division by 0 when handling authority methods that have no outputs + amount = max(1, tx.sum_outputs) / (10 ** self._settings.DECIMAL_PLACES) + weight = ( + + self._settings.MIN_TX_WEIGHT_COEFFICIENT * log(tx_size, 2) + + 4 / (1 + self._settings.MIN_TX_WEIGHT_K / amount) + 4 + ) + + # Make sure the calculated weight is at least the minimum + weight = max(weight, self._settings.MIN_TX_WEIGHT) + + return weight + + def get_tokens_issued_per_block(self, height: int) -> int: + """Return the number of tokens issued (aka reward) per block of a given height.""" + if self._settings.BLOCKS_PER_HALVING is None: + assert self._settings.MINIMUM_TOKENS_PER_BLOCK == self._settings.INITIAL_TOKENS_PER_BLOCK + return self._settings.MINIMUM_TOKENS_PER_BLOCK + + number_of_halvings = (height - 1) // self._settings.BLOCKS_PER_HALVING + number_of_halvings = max(0, number_of_halvings) + + if number_of_halvings > self._settings.MAXIMUM_NUMBER_OF_HALVINGS: + return self._settings.MINIMUM_TOKENS_PER_BLOCK + + amount = self._settings.INITIAL_TOKENS_PER_BLOCK // (2**number_of_halvings) + amount = max(amount, self._settings.MINIMUM_TOKENS_PER_BLOCK) + return amount + + def get_mined_tokens(self, height: int) -> int: + """Return the number of tokens mined in total at height + """ + assert self._settings.BLOCKS_PER_HALVING is not None + number_of_halvings = (height - 1) // self._settings.BLOCKS_PER_HALVING + number_of_halvings = max(0, number_of_halvings) + + blocks_in_this_halving = height - number_of_halvings * self._settings.BLOCKS_PER_HALVING + + tokens_per_block = self._settings.INITIAL_TOKENS_PER_BLOCK + mined_tokens = 0 - # Sum the past halvings - for _ in range(number_of_halvings): - mined_tokens += settings.BLOCKS_PER_HALVING * tokens_per_block - tokens_per_block //= 2 - tokens_per_block = max(tokens_per_block, settings.MINIMUM_TOKENS_PER_BLOCK) + # Sum the past halvings + for _ in range(number_of_halvings): + mined_tokens += self._settings.BLOCKS_PER_HALVING * tokens_per_block + tokens_per_block //= 2 + tokens_per_block = max(tokens_per_block, self._settings.MINIMUM_TOKENS_PER_BLOCK) - # Sum the blocks in the current halving - mined_tokens += blocks_in_this_halving * tokens_per_block + # Sum the blocks in the current halving + mined_tokens += blocks_in_this_halving * tokens_per_block - return mined_tokens + return mined_tokens diff --git a/hathor/manager.py b/hathor/manager.py index 16ced7528..9523d2ace 100644 --- a/hathor/manager.py +++ b/hathor/manager.py @@ -25,10 +25,10 @@ from twisted.internet.task import LoopingCall from twisted.python.threadpool import ThreadPool -from hathor import daa from hathor.checkpoint import Checkpoint from hathor.conf.settings import HathorSettings from hathor.consensus import ConsensusAlgorithm +from hathor.daa import DifficultyAdjustmentAlgorithm from hathor.event.event_manager import EventManager from hathor.exception import ( BlockTemplateTimestampError, @@ -90,6 +90,7 @@ def __init__(self, settings: HathorSettings, pubsub: PubSubManager, consensus_algorithm: ConsensusAlgorithm, + daa: DifficultyAdjustmentAlgorithm, peer_id: PeerId, tx_storage: TransactionStorage, p2p_manager: ConnectionsManager, @@ -124,6 +125,7 @@ def __init__(self, ) self._settings = settings + self.daa = daa self._cmd_path: Optional[str] = None self.log = logger.new() @@ -824,7 +826,7 @@ def _make_block_template(self, parent_block: Block, parent_txs: 'ParentTxs', cur parent_block_metadata.score, 2 * self._settings.WEIGHT_TOL ) - weight = max(daa.calculate_next_weight(parent_block, timestamp), min_significant_weight) + weight = max(self.daa.calculate_next_weight(parent_block, timestamp), min_significant_weight) height = parent_block.get_height() + 1 parents = [parent_block.hash] + parent_txs.must_include parents_any = parent_txs.can_include @@ -838,7 +840,7 @@ def _make_block_template(self, parent_block: Block, parent_txs: 'ParentTxs', cur assert len(parents_any) == 0, 'Extra parents to choose from that cannot be chosen' return BlockTemplate( versions={TxVersion.REGULAR_BLOCK.value, TxVersion.MERGE_MINED_BLOCK.value}, - reward=daa.get_tokens_issued_per_block(height), + reward=self.daa.get_tokens_issued_per_block(height), weight=weight, timestamp_now=current_timestamp, timestamp_min=timestamp_min, @@ -875,7 +877,7 @@ def generate_mining_block(self, timestamp: Optional[int] = None, def get_tokens_issued_per_block(self, height: int) -> int: """Return the number of tokens issued (aka reward) per block of a given height.""" - return daa.get_tokens_issued_per_block(height) + return self.daa.get_tokens_issued_per_block(height) def submit_block(self, blk: Block, fails_silently: bool = True) -> bool: """Used by submit block from all mining APIs. diff --git a/hathor/p2p/resources/mining_info.py b/hathor/p2p/resources/mining_info.py index 4aae45616..8263ee273 100644 --- a/hathor/p2p/resources/mining_info.py +++ b/hathor/p2p/resources/mining_info.py @@ -17,7 +17,6 @@ from hathor.api_util import Resource, set_cors from hathor.cli.openapi_files.register import register_resource from hathor.conf.get_settings import get_settings -from hathor.daa import get_mined_tokens from hathor.difficulty import Weight from hathor.util import json_dumpb @@ -57,7 +56,7 @@ def render_GET(self, request): parent = block.get_block_parent() hashrate = 2**(parent.weight - log(30, 2)) - mined_tokens = get_mined_tokens(height) + mined_tokens = self.manager.daa.get_mined_tokens(height) data = { 'hashrate': hashrate, diff --git a/hathor/simulator/simulator.py b/hathor/simulator/simulator.py index d27e4aa82..bd5c182d1 100644 --- a/hathor/simulator/simulator.py +++ b/hathor/simulator/simulator.py @@ -23,7 +23,7 @@ from hathor.builder import BuildArtifacts, Builder from hathor.conf.get_settings import get_settings from hathor.conf.settings import HathorSettings -from hathor.daa import TestMode, _set_test_mode +from hathor.daa import DifficultyAdjustmentAlgorithm, TestMode, _set_test_mode from hathor.feature_activation.feature_service import FeatureService from hathor.manager import HathorManager from hathor.p2p.peer_id import PeerId @@ -49,6 +49,7 @@ DEFAULT_STEP_INTERVAL: float = 0.25 DEFAULT_STATUS_INTERVAL: float = 60.0 +SIMULATOR_AVG_TIME_BETWEEN_BLOCKS: int = 64 class Simulator: @@ -63,7 +64,6 @@ def _apply_patches(cls): - disable Transaction.resolve method - set DAA test-mode to DISABLED (will actually run the pow function, that won't actually verify the pow) - - override AVG_TIME_BETWEEN_BLOCKS to 64 """ from hathor.transaction import BaseTransaction @@ -77,10 +77,6 @@ def resolve(self: BaseTransaction, update_time: bool = True) -> bool: _set_test_mode(TestMode.DISABLED) - from hathor import daa - cls._original_avg_time_between_blocks = daa.AVG_TIME_BETWEEN_BLOCKS - daa.AVG_TIME_BETWEEN_BLOCKS = 64 - @classmethod def _remove_patches(cls): """ Remove the patches previously applied. @@ -88,9 +84,6 @@ def _remove_patches(cls): from hathor.transaction import BaseTransaction BaseTransaction.resolve = cls._original_resolve - from hathor import daa - daa.AVG_TIME_BETWEEN_BLOCKS = cls._original_avg_time_between_blocks - @classmethod def _patches_rc_increment(cls): """ This is used by when starting instances of Simulator to determine when to run _apply_patches""" @@ -115,7 +108,7 @@ def __init__(self, seed: Optional[int] = None): seed = secrets.randbits(64) self.seed = seed self.rng = Random(self.seed) - self.settings = get_settings() + self.settings = get_settings()._replace(AVG_TIME_BETWEEN_BLOCKS=SIMULATOR_AVG_TIME_BETWEEN_BLOCKS) self._network = 'testnet' self._clock = MemoryReactorHeapClock() self._peers: OrderedDict[str, HathorManager] = OrderedDict() @@ -149,7 +142,8 @@ def get_default_builder(self) -> Builder: .enable_full_verification() \ .enable_sync_v1() \ .enable_sync_v2() \ - .use_memory() + .use_memory() \ + .set_settings(self.settings) def create_peer(self, builder: Optional[Builder] = None) -> HathorManager: """ @@ -170,11 +164,14 @@ def create_artifacts(self, builder: Optional[Builder] = None) -> BuildArtifacts: wallet = HDWallet(gap_limit=2) wallet._manually_initialize() + daa = DifficultyAdjustmentAlgorithm(settings=self.settings) + artifacts = builder \ .set_reactor(self._clock) \ .set_rng(Random(self.rng.getrandbits(64))) \ .set_wallet(wallet) \ .set_vertex_verifiers_builder(_build_vertex_verifiers) \ + .set_daa(daa) \ .build() artifacts.manager.start() @@ -300,16 +297,21 @@ def run(self, return True -def _build_vertex_verifiers(settings: HathorSettings, feature_service: FeatureService) -> VertexVerifiers: +def _build_vertex_verifiers( + settings: HathorSettings, + daa: DifficultyAdjustmentAlgorithm, + feature_service: FeatureService +) -> VertexVerifiers: """ A custom VertexVerifiers builder to be used by the simulator. """ return VertexVerifiers( - block=SimulatorBlockVerifier(settings=settings, feature_service=feature_service), + block=SimulatorBlockVerifier(settings=settings, daa=daa, feature_service=feature_service), merge_mined_block=SimulatorMergeMinedBlockVerifier( settings=settings, + daa=daa, feature_service=feature_service ), - tx=SimulatorTransactionVerifier(settings=settings), - token_creation_tx=SimulatorTokenCreationTransactionVerifier(settings=settings), + tx=SimulatorTransactionVerifier(settings=settings, daa=daa), + token_creation_tx=SimulatorTokenCreationTransactionVerifier(settings=settings, daa=daa), ) diff --git a/hathor/simulator/tx_generator.py b/hathor/simulator/tx_generator.py index 6bb76c1a8..3bd51ef1c 100644 --- a/hathor/simulator/tx_generator.py +++ b/hathor/simulator/tx_generator.py @@ -17,7 +17,6 @@ from structlog import get_logger -from hathor import daa from hathor.conf.get_settings import get_settings from hathor.transaction.exceptions import RewardLocked from hathor.util import Random @@ -128,7 +127,7 @@ def new_tx_step1(self): self.delayedcall = self.clock.callLater(0, self.schedule_next_transaction) return - tx.weight = daa.minimum_tx_weight(tx) + tx.weight = self.manager.daa.minimum_tx_weight(tx) tx.update_hash() geometric_p = 2**(-tx.weight) diff --git a/hathor/stratum/stratum.py b/hathor/stratum/stratum.py index d7eb3ed96..a044de4a0 100644 --- a/hathor/stratum/stratum.py +++ b/hathor/stratum/stratum.py @@ -526,7 +526,7 @@ def handle_submit(self, params: dict, msgid: Optional[str]) -> None: self.log.debug('share received', block=tx, block_base=block_base.hex(), block_base_hash=block_base_hash.hex()) - verifier = VertexVerifier(settings=self._settings) + verifier = VertexVerifier(settings=self._settings, daa=self.manager.daa) try: verifier.verify_pow(tx, override_weight=job.weight) diff --git a/hathor/transaction/resources/create_tx.py b/hathor/transaction/resources/create_tx.py index dcec5d363..b9641369a 100644 --- a/hathor/transaction/resources/create_tx.py +++ b/hathor/transaction/resources/create_tx.py @@ -17,7 +17,6 @@ from hathor.api_util import Resource, set_cors from hathor.cli.openapi_files.register import register_resource from hathor.crypto.util import decode_address -from hathor.daa import minimum_tx_weight from hathor.exception import InvalidNewTransaction from hathor.transaction import Transaction, TxInput, TxOutput from hathor.transaction.scripts import create_output_script @@ -88,7 +87,7 @@ def render_POST(self, request): for tx_input in fake_signed_tx.inputs: # conservative estimate of the input data size to estimate a valid weight tx_input.data = b'\0' * 107 - tx.weight = minimum_tx_weight(fake_signed_tx) + tx.weight = self.manager.daa.minimum_tx_weight(fake_signed_tx) self.manager.verification_service.verifiers.tx.verify_unsigned_skip_pow(tx) if tx.is_double_spending(): diff --git a/hathor/verification/block_verifier.py b/hathor/verification/block_verifier.py index 9bc23c802..7a2e91b84 100644 --- a/hathor/verification/block_verifier.py +++ b/hathor/verification/block_verifier.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from hathor import daa from hathor.conf.settings import HathorSettings +from hathor.daa import DifficultyAdjustmentAlgorithm from hathor.feature_activation.feature_service import BlockIsMissingSignal, BlockIsSignaling, FeatureService from hathor.profiler import get_cpu_profiler from hathor.transaction import BaseTransaction, Block @@ -34,8 +34,14 @@ class BlockVerifier(VertexVerifier): __slots__ = ('_feature_service', ) - def __init__(self, *, settings: HathorSettings, feature_service: FeatureService | None = None) -> None: - super().__init__(settings=settings) + def __init__( + self, + *, + settings: HathorSettings, + daa: DifficultyAdjustmentAlgorithm, + feature_service: FeatureService | None = None + ) -> None: + super().__init__(settings=settings, daa=daa) self._feature_service = feature_service def verify_basic(self, block: Block, *, skip_block_weight_verification: bool = False) -> None: @@ -87,7 +93,7 @@ def verify_height(self, block: Block) -> None: def verify_weight(self, block: Block) -> None: """Validate minimum block difficulty.""" - min_block_weight = daa.calculate_block_difficulty(block) + min_block_weight = self._daa.calculate_block_difficulty(block) if block.weight < min_block_weight - self._settings.WEIGHT_TOL: raise WeightError(f'Invalid new block {block.hash_hex}: weight ({block.weight}) is ' f'smaller than the minimum weight ({min_block_weight})') @@ -95,7 +101,7 @@ def verify_weight(self, block: Block) -> None: def verify_reward(self, block: Block) -> None: """Validate reward amount.""" parent_block = block.get_block_parent() - tokens_issued_per_block = daa.get_tokens_issued_per_block(parent_block.get_height() + 1) + tokens_issued_per_block = self._daa.get_tokens_issued_per_block(parent_block.get_height() + 1) if block.sum_outputs != tokens_issued_per_block: raise InvalidBlockReward( f'Invalid number of issued tokens tag=invalid_issued_tokens tx.hash={block.hash_hex} ' diff --git a/hathor/verification/transaction_verifier.py b/hathor/verification/transaction_verifier.py index e04d907e6..ba632d472 100644 --- a/hathor/verification/transaction_verifier.py +++ b/hathor/verification/transaction_verifier.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from hathor import daa from hathor.profiler import get_cpu_profiler from hathor.transaction import BaseTransaction, Transaction, TxInput, TxOutput from hathor.transaction.exceptions import ( @@ -101,7 +100,7 @@ def verify_parents_basic(self, tx: Transaction) -> None: def verify_weight(self, tx: Transaction) -> None: """Validate minimum tx difficulty.""" - min_tx_weight = daa.minimum_tx_weight(tx) + min_tx_weight = self._daa.minimum_tx_weight(tx) max_tx_weight = min_tx_weight + self._settings.MAX_TX_WEIGHT_DIFF if tx.weight < min_tx_weight - self._settings.WEIGHT_TOL: raise WeightError(f'Invalid new tx {tx.hash_hex}: weight ({tx.weight}) is ' diff --git a/hathor/verification/verification_service.py b/hathor/verification/verification_service.py index 48f4e2c98..9c2da77f6 100644 --- a/hathor/verification/verification_service.py +++ b/hathor/verification/verification_service.py @@ -15,6 +15,7 @@ from typing import NamedTuple from hathor.conf.settings import HathorSettings +from hathor.daa import DifficultyAdjustmentAlgorithm from hathor.feature_activation.feature_service import FeatureService from hathor.transaction import BaseTransaction, Block, MergeMinedBlock, Transaction, TxVersion from hathor.transaction.exceptions import TxValidationError @@ -38,17 +39,18 @@ def create_defaults( cls, *, settings: HathorSettings, - feature_service: FeatureService | None = None + daa: DifficultyAdjustmentAlgorithm, + feature_service: FeatureService | None = None, ) -> 'VertexVerifiers': """ Create a VertexVerifiers instance using the default verifier for each vertex type, from all required dependencies. """ return VertexVerifiers( - block=BlockVerifier(settings=settings, feature_service=feature_service), - merge_mined_block=MergeMinedBlockVerifier(settings=settings, feature_service=feature_service), - tx=TransactionVerifier(settings=settings), - token_creation_tx=TokenCreationTransactionVerifier(settings=settings), + block=BlockVerifier(settings=settings, daa=daa, feature_service=feature_service), + merge_mined_block=MergeMinedBlockVerifier(settings=settings, daa=daa, feature_service=feature_service), + tx=TransactionVerifier(settings=settings, daa=daa), + token_creation_tx=TokenCreationTransactionVerifier(settings=settings, daa=daa), ) diff --git a/hathor/verification/vertex_verifier.py b/hathor/verification/vertex_verifier.py index 883e8049c..80a621502 100644 --- a/hathor/verification/vertex_verifier.py +++ b/hathor/verification/vertex_verifier.py @@ -15,6 +15,7 @@ from typing import Optional from hathor.conf.settings import HathorSettings +from hathor.daa import DifficultyAdjustmentAlgorithm from hathor.transaction import BaseTransaction from hathor.transaction.exceptions import ( DuplicatedParents, @@ -39,10 +40,11 @@ class VertexVerifier: - __slots__ = ('_settings', ) + __slots__ = ('_settings', '_daa') - def __init__(self, *, settings: HathorSettings): + def __init__(self, *, settings: HathorSettings, daa: DifficultyAdjustmentAlgorithm): self._settings = settings + self._daa = daa def verify_parents(self, vertex: BaseTransaction) -> None: """All parents must exist and their timestamps must be smaller than ours. diff --git a/hathor/wallet/resources/nano_contracts/execute.py b/hathor/wallet/resources/nano_contracts/execute.py index d08bd4c5a..46cce3ad6 100644 --- a/hathor/wallet/resources/nano_contracts/execute.py +++ b/hathor/wallet/resources/nano_contracts/execute.py @@ -20,7 +20,6 @@ from hathor.api_util import Resource, get_missing_params_msg, render_options, set_cors from hathor.cli.openapi_files.register import register_resource from hathor.crypto.util import decode_address -from hathor.daa import minimum_tx_weight from hathor.transaction import Transaction, TxInput, TxOutput from hathor.transaction.scripts import P2PKH, NanoContractMatchValues from hathor.util import json_dumpb, json_loadb @@ -99,7 +98,7 @@ def render_POST(self, request): tx.parents = self.manager.get_new_tx_parents() tx.update_timestamp(int(self.manager.reactor.seconds())) - tx.weight = minimum_tx_weight(tx) + tx.weight = self.manager.daa.minimum_tx_weight(tx) tx.resolve() success = self.manager.propagate_tx(tx) diff --git a/hathor/wallet/resources/send_tokens.py b/hathor/wallet/resources/send_tokens.py index cf14f11fd..268292c12 100644 --- a/hathor/wallet/resources/send_tokens.py +++ b/hathor/wallet/resources/send_tokens.py @@ -20,7 +20,6 @@ from hathor.api_util import Resource, render_options, set_cors from hathor.cli.openapi_files.register import register_resource from hathor.crypto.util import decode_address -from hathor.daa import minimum_tx_weight from hathor.exception import InvalidNewTransaction from hathor.transaction import Transaction from hathor.transaction.exceptions import TxValidationError @@ -125,7 +124,7 @@ def _render_POST_thread(self, values: dict[str, Any], request: Request) -> Union tx.parents = values['parents'] weight = values['weight'] if weight is None: - weight = minimum_tx_weight(tx) + weight = self.manager.daa.minimum_tx_weight(tx) tx.weight = weight tx.resolve() self.manager.verification_service.verify(tx) diff --git a/hathor/wallet/resources/sign_tx.py b/hathor/wallet/resources/sign_tx.py index cef27a689..b70ccb76b 100644 --- a/hathor/wallet/resources/sign_tx.py +++ b/hathor/wallet/resources/sign_tx.py @@ -17,7 +17,6 @@ from hathor.api_util import Resource, get_args, get_missing_params_msg, set_cors from hathor.cli.openapi_files.register import register_resource -from hathor.daa import minimum_tx_weight from hathor.transaction import Transaction from hathor.util import json_dumpb @@ -67,7 +66,7 @@ def render_GET(self, request): if prepare_to_send: tx.parents = self.manager.get_new_tx_parents() tx.update_timestamp(int(self.manager.reactor.seconds())) - tx.weight = minimum_tx_weight(tx) + tx.weight = self.manager.daa.minimum_tx_weight(tx) tx.resolve() data = {'hex_tx': tx.get_struct().hex(), 'success': True} diff --git a/tests/resources/wallet/test_thin_wallet.py b/tests/resources/wallet/test_thin_wallet.py index e9d0d4b31..73a582194 100644 --- a/tests/resources/wallet/test_thin_wallet.py +++ b/tests/resources/wallet/test_thin_wallet.py @@ -4,7 +4,6 @@ from hathor.conf import HathorSettings from hathor.crypto.util import decode_address -from hathor.daa import minimum_tx_weight from hathor.transaction import Transaction, TxInput, TxOutput from hathor.transaction.scripts import P2PKH, create_output_script, parse_address_script from hathor.wallet.resources.thin_wallet import ( @@ -85,7 +84,7 @@ def test_post(self): i.data = P2PKH.create_input_data(public_key_bytes, signature_bytes) tx2.inputs = [i] tx2.timestamp = int(self.clock.seconds()) - tx2.weight = minimum_tx_weight(tx2) + tx2.weight = self.manager.daa.minimum_tx_weight(tx2) response_wrong_amount = yield self.web.post('thin_wallet/send_tokens', {'tx_hex': tx2.get_struct().hex()}) data_wrong_amount = response_wrong_amount.json_value() @@ -100,7 +99,7 @@ def test_post(self): i.data = P2PKH.create_input_data(public_key_bytes, signature_bytes) tx3.inputs = [i] tx3.timestamp = int(self.clock.seconds()) - tx3.weight = minimum_tx_weight(tx3) + tx3.weight = self.manager.daa.minimum_tx_weight(tx3) # Then send tokens response = yield self.web.post('thin_wallet/send_tokens', {'tx_hex': tx3.get_struct().hex()}) @@ -423,7 +422,7 @@ def test_token_history(self): i.data = P2PKH.create_input_data(public_key_bytes, signature_bytes) tx2.inputs = [i] tx2.timestamp = int(self.clock.seconds()) - tx2.weight = minimum_tx_weight(tx2) + tx2.weight = self.manager.daa.minimum_tx_weight(tx2) tx2.parents = self.manager.get_new_tx_parents() tx2.resolve() self.manager.propagate_tx(tx2) diff --git a/tests/simulation/test_simulator.py b/tests/simulation/test_simulator.py index 0df5dfdfe..aac7edd66 100644 --- a/tests/simulation/test_simulator.py +++ b/tests/simulation/test_simulator.py @@ -13,7 +13,7 @@ def test_verify_pow(self): # just get one of the genesis, we don't really need to create any transaction tx = next(iter(manager1.tx_storage.get_all_genesis())) # optional argument must be valid, it just has to not raise any exception, there's no assert for that - VertexVerifier(settings=self._settings).verify_pow(tx, override_weight=0.) + VertexVerifier(settings=self._settings, daa=manager1.daa).verify_pow(tx, override_weight=0.) def test_one_node(self): manager1 = self.create_peer() diff --git a/tests/tx/test_block.py b/tests/tx/test_block.py index eeaf89ec2..a363cfb78 100644 --- a/tests/tx/test_block.py +++ b/tests/tx/test_block.py @@ -147,7 +147,7 @@ def test_verify_must_signal_when_feature_activation_is_disabled(is_signaling_man settings.FEATURE_ACTIVATION.enable_usage = False feature_service = Mock(spec_set=FeatureService) feature_service.is_signaling_mandatory_features = Mock(return_value=is_signaling_mandatory_features) - verifier = BlockVerifier(settings=settings, feature_service=feature_service) + verifier = BlockVerifier(settings=settings, feature_service=feature_service, daa=Mock()) block = Block() verifier.verify_mandatory_signaling(block) @@ -160,7 +160,7 @@ def test_verify_must_signal() -> None: feature_service.is_signaling_mandatory_features = Mock( return_value=BlockIsMissingSignal(feature=Feature.NOP_FEATURE_1) ) - verifier = BlockVerifier(settings=settings, feature_service=feature_service) + verifier = BlockVerifier(settings=settings, feature_service=feature_service, daa=Mock()) block = Block() with pytest.raises(BlockMustSignalError) as e: @@ -174,7 +174,7 @@ def test_verify_must_not_signal() -> None: settings.FEATURE_ACTIVATION.enable_usage = True feature_service = Mock(spec_set=FeatureService) feature_service.is_signaling_mandatory_features = Mock(return_value=BlockIsSignaling()) - verifier = BlockVerifier(settings=settings, feature_service=feature_service) + verifier = BlockVerifier(settings=settings, feature_service=feature_service, daa=Mock()) block = Block() verifier.verify_mandatory_signaling(block) diff --git a/tests/tx/test_blockchain.py b/tests/tx/test_blockchain.py index d808975e9..f52228c60 100644 --- a/tests/tx/test_blockchain.py +++ b/tests/tx/test_blockchain.py @@ -1,7 +1,7 @@ from itertools import chain from hathor.conf import HathorSettings -from hathor.daa import TestMode, _set_test_mode, get_weight_decay_amount +from hathor.daa import DifficultyAdjustmentAlgorithm, TestMode, _set_test_mode from hathor.transaction import sum_weights from hathor.transaction.storage import TransactionMemoryStorage from tests import unittest @@ -30,6 +30,7 @@ def setUp(self): self.genesis = self.tx_storage.get_all_genesis() self.genesis_blocks = [tx for tx in self.genesis if tx.is_block] self.genesis_txs = [tx for tx in self.genesis if not tx.is_block] + self.daa = DifficultyAdjustmentAlgorithm(settings=settings) def test_single_chain(self): """ All new blocks belong to case (i). @@ -420,42 +421,30 @@ def test_daa_weight_decay_amount(self): amount = settings.WEIGHT_DECAY_AMOUNT for distance in range(0, settings.WEIGHT_DECAY_ACTIVATE_DISTANCE, 10): - self.assertEqual(get_weight_decay_amount(distance), 0) + self.assertEqual(self.daa.get_weight_decay_amount(distance), 0) distance = settings.WEIGHT_DECAY_ACTIVATE_DISTANCE - 1 - self.assertAlmostEqual(get_weight_decay_amount(distance), 0) + self.assertAlmostEqual(self.daa.get_weight_decay_amount(distance), 0) distance = settings.WEIGHT_DECAY_ACTIVATE_DISTANCE for k in range(1, 11): for _ in range(settings.WEIGHT_DECAY_WINDOW_SIZE): - self.assertAlmostEqual(get_weight_decay_amount(distance), k * amount) + self.assertAlmostEqual(self.daa.get_weight_decay_amount(distance), k * amount) distance += 1 - self.assertAlmostEqual(get_weight_decay_amount(distance), 11 * amount) + self.assertAlmostEqual(self.daa.get_weight_decay_amount(distance), 11 * amount) def test_daa_weight_decay_blocks(self): - from hathor import daa - orig_avg_time_between_blocks = daa.AVG_TIME_BETWEEN_BLOCKS - orig_min_block_weight = daa.MIN_BLOCK_WEIGHT - - try: - self._test_daa_weight_decay_blocks() - finally: - daa.AVG_TIME_BETWEEN_BLOCKS = orig_avg_time_between_blocks - daa.MIN_BLOCK_WEIGHT = orig_min_block_weight - - def _test_daa_weight_decay_blocks(self): _set_test_mode(TestMode.DISABLED) manager = self.create_peer('testnet', tx_storage=self.tx_storage) amount = settings.WEIGHT_DECAY_AMOUNT - from hathor import daa - daa.AVG_TIME_BETWEEN_BLOCKS = settings.AVG_TIME_BETWEEN_BLOCKS - daa.MIN_BLOCK_WEIGHT = 2 + 2 * settings.WEIGHT_DECAY_AMOUNT + manager.daa.AVG_TIME_BETWEEN_BLOCKS = settings.AVG_TIME_BETWEEN_BLOCKS + manager.daa.MIN_BLOCK_WEIGHT = 2 + 2 * settings.WEIGHT_DECAY_AMOUNT add_new_blocks(manager, 2 * settings.BLOCK_DIFFICULTY_N_BLOCKS, advance_clock=settings.AVG_TIME_BETWEEN_BLOCKS) - daa.MIN_BLOCK_WEIGHT = 1 + manager.daa.MIN_BLOCK_WEIGHT = 1 base_weight = manager.generate_mining_block().weight - self.assertGreater(base_weight, daa.MIN_BLOCK_WEIGHT) + self.assertGreater(base_weight, manager.daa.MIN_BLOCK_WEIGHT) add_new_blocks(manager, 20, advance_clock=settings.AVG_TIME_BETWEEN_BLOCKS) @@ -482,7 +471,7 @@ def _test_daa_weight_decay_blocks(self): manager.reactor.advance(1) weight = manager.generate_mining_block().weight - self.assertAlmostEqual(weight, daa.MIN_BLOCK_WEIGHT) + self.assertAlmostEqual(weight, manager.daa.MIN_BLOCK_WEIGHT) class SyncV1BlockchainTestCase(unittest.SyncV1Params, BaseBlockchainTestCase): diff --git a/tests/tx/test_genesis.py b/tests/tx/test_genesis.py index ab4829011..2bf5000f5 100644 --- a/tests/tx/test_genesis.py +++ b/tests/tx/test_genesis.py @@ -1,5 +1,5 @@ from hathor.conf import HathorSettings -from hathor.daa import TestMode, _set_test_mode, calculate_block_difficulty, minimum_tx_weight +from hathor.daa import DifficultyAdjustmentAlgorithm, TestMode, _set_test_mode from hathor.transaction.storage import TransactionMemoryStorage from hathor.verification.verification_service import VerificationService, VertexVerifiers from hathor.verification.vertex_verifier import VertexVerifier @@ -28,12 +28,13 @@ def get_genesis_output(): class GenesisTest(unittest.TestCase): def setUp(self): super().setUp() - verifiers = VertexVerifiers.create_defaults(settings=self._settings) + self._daa = DifficultyAdjustmentAlgorithm(settings=self._settings) + verifiers = VertexVerifiers.create_defaults(settings=self._settings, daa=self._daa) self._verification_service = VerificationService(verifiers=verifiers) self.storage = TransactionMemoryStorage() def test_pow(self): - verifier = VertexVerifier(settings=self._settings) + verifier = VertexVerifier(settings=self._settings, daa=self._daa) genesis = self.storage.get_all_genesis() for g in genesis: self.assertEqual(g.calculate_hash(), g.hash) @@ -70,9 +71,9 @@ def test_genesis_weight(self): # Validate the block and tx weight # in test mode weight is always 1 _set_test_mode(TestMode.TEST_ALL_WEIGHT) - self.assertEqual(calculate_block_difficulty(genesis_block), 1) - self.assertEqual(minimum_tx_weight(genesis_tx), 1) + self.assertEqual(self._daa.calculate_block_difficulty(genesis_block), 1) + self.assertEqual(self._daa.minimum_tx_weight(genesis_tx), 1) _set_test_mode(TestMode.DISABLED) - self.assertEqual(calculate_block_difficulty(genesis_block), genesis_block.weight) - self.assertEqual(minimum_tx_weight(genesis_tx), genesis_tx.weight) + self.assertEqual(self._daa.calculate_block_difficulty(genesis_block), genesis_block.weight) + self.assertEqual(self._daa.minimum_tx_weight(genesis_tx), genesis_tx.weight) diff --git a/tests/tx/test_tx.py b/tests/tx/test_tx.py index 3d36ec659..b68b301c2 100644 --- a/tests/tx/test_tx.py +++ b/tests/tx/test_tx.py @@ -2,7 +2,6 @@ import hashlib from math import isinf, isnan -from hathor import daa from hathor.crypto.util import decode_address, get_address_from_public_key, get_private_key_from_bytes from hathor.daa import TestMode, _set_test_mode from hathor.transaction import MAX_OUTPUT_VALUE, Block, Transaction, TxInput, TxOutput @@ -531,7 +530,7 @@ def test_tx_weight_too_high(self): inputs = [TxInput(b'', 0, b'')] tx = Transaction(weight=1, inputs=inputs, outputs=outputs, parents=parents, storage=self.tx_storage, timestamp=self.last_block.timestamp + 1) - tx.weight = daa.minimum_tx_weight(tx) + tx.weight = self.manager.daa.minimum_tx_weight(tx) tx.weight += self._settings.MAX_TX_WEIGHT_DIFF + 0.1 tx.update_hash() with self.assertRaises(WeightError): diff --git a/tests/tx/test_tx_deserialization.py b/tests/tx/test_tx_deserialization.py index b9bc87b94..c45603b54 100644 --- a/tests/tx/test_tx_deserialization.py +++ b/tests/tx/test_tx_deserialization.py @@ -1,3 +1,4 @@ +from hathor.daa import DifficultyAdjustmentAlgorithm from hathor.transaction import Block, MergeMinedBlock, Transaction, TxVersion from hathor.transaction.token_creation_tx import TokenCreationTransaction from hathor.verification.verification_service import VerificationService, VertexVerifiers @@ -8,7 +9,8 @@ class _BaseTest: class _DeserializationTest(unittest.TestCase): def setUp(self) -> None: super().setUp() - verifiers = VertexVerifiers.create_defaults(settings=self._settings) + daa = DifficultyAdjustmentAlgorithm(settings=self._settings) + verifiers = VertexVerifiers.create_defaults(settings=self._settings, daa=daa) self._verification_service = VerificationService(verifiers=verifiers) def test_deserialize(self): diff --git a/tests/unittest.py b/tests/unittest.py index 6cb3f5a4e..930bb23e6 100644 --- a/tests/unittest.py +++ b/tests/unittest.py @@ -183,7 +183,7 @@ def create_peer_from_builder(self, builder, start_manager=True): def create_peer(self, network, peer_id=None, wallet=None, tx_storage=None, unlock_wallet=True, wallet_index=False, capabilities=None, full_verification=True, enable_sync_v1=None, enable_sync_v2=None, checkpoints=None, utxo_index=False, event_manager=None, use_memory_index=None, start_manager=True, - pubsub=None, event_storage=None, enable_event_queue=None, use_memory_storage=None): + pubsub=None, event_storage=None, enable_event_queue=None, use_memory_storage=None, daa=None): enable_sync_v1, enable_sync_v2 = self._syncVersionFlags(enable_sync_v1, enable_sync_v2) @@ -246,6 +246,9 @@ def create_peer(self, network, peer_id=None, wallet=None, tx_storage=None, unloc if utxo_index: builder.enable_utxo_index() + if daa: + builder.set_daa(daa) + manager = self.create_peer_from_builder(builder, start_manager=start_manager) # XXX: just making sure that tests set this up correctly