diff --git a/hathor/builder/builder.py b/hathor/builder/builder.py index a81c98544..129a37074 100644 --- a/hathor/builder/builder.py +++ b/hathor/builder/builder.py @@ -422,7 +422,7 @@ def _get_or_create_indexes_manager(self) -> IndexesManager: return self._indexes_manager if self._force_memory_index or self._storage_type == StorageType.MEMORY: - self._indexes_manager = MemoryIndexesManager() + self._indexes_manager = MemoryIndexesManager(settings=self._get_or_create_settings()) elif self._storage_type == StorageType.ROCKSDB: rocksdb_storage = self._get_or_create_rocksdb_storage() diff --git a/hathor/indexes/base_index.py b/hathor/indexes/base_index.py index bc9195009..98b1c0721 100644 --- a/hathor/indexes/base_index.py +++ b/hathor/indexes/base_index.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from abc import ABC, abstractmethod from typing import TYPE_CHECKING, Optional @@ -22,6 +24,7 @@ from hathor.transaction.base_transaction import BaseTransaction if TYPE_CHECKING: # pragma: no cover + from hathor.conf.settings import HathorSettings from hathor.indexes.manager import IndexesManager logger = get_logger() @@ -33,8 +36,8 @@ class BaseIndex(ABC): This class exists so we can interact with indexes without knowing anything specific to its implemented. It was created to generalize how we initialize indexes and keep track of which ones are up-to-date. """ - def __init__(self) -> None: - self._settings = get_global_settings() + def __init__(self, *, settings: HathorSettings | None = None) -> None: + self._settings = settings or get_global_settings() self.log = logger.new() def init_start(self, indexes_manager: 'IndexesManager') -> None: diff --git a/hathor/indexes/manager.py b/hathor/indexes/manager.py index e681f716b..e38e3ad85 100644 --- a/hathor/indexes/manager.py +++ b/hathor/indexes/manager.py @@ -19,6 +19,7 @@ from structlog import get_logger +from hathor.conf.settings import HathorSettings from hathor.indexes.address_index import AddressIndex from hathor.indexes.base_index import BaseIndex from hathor.indexes.height_index import HeightIndex @@ -259,7 +260,7 @@ def del_tx(self, tx: BaseTransaction, *, remove_all: bool = False, relax_assert: class MemoryIndexesManager(IndexesManager): - def __init__(self) -> None: + def __init__(self, *, settings: HathorSettings | None = None) -> None: from hathor.indexes.memory_height_index import MemoryHeightIndex from hathor.indexes.memory_info_index import MemoryInfoIndex from hathor.indexes.memory_timestamp_index import MemoryTimestampIndex @@ -277,7 +278,7 @@ def __init__(self) -> None: self.addresses = None self.tokens = None self.utxo = None - self.height = MemoryHeightIndex() + self.height = MemoryHeightIndex(settings=settings) self.mempool_tips = None # XXX: this has to be at the end of __init__, after everything has been initialized diff --git a/hathor/indexes/memory_height_index.py b/hathor/indexes/memory_height_index.py index 50bf03004..18a0546ae 100644 --- a/hathor/indexes/memory_height_index.py +++ b/hathor/indexes/memory_height_index.py @@ -14,6 +14,7 @@ from typing import Optional +from hathor.conf.settings import HathorSettings from hathor.indexes.height_index import HeightIndex, HeightInfo, IndexEntry @@ -23,8 +24,8 @@ class MemoryHeightIndex(HeightIndex): _index: list[IndexEntry] - def __init__(self) -> None: - super().__init__() + def __init__(self, *, settings: HathorSettings | None = None) -> None: + super().__init__(settings=settings) self.force_clear() def get_db_name(self) -> Optional[str]: diff --git a/hathor/transaction/base_transaction.py b/hathor/transaction/base_transaction.py index c82407052..b1c5eb60c 100644 --- a/hathor/transaction/base_transaction.py +++ b/hathor/transaction/base_transaction.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import base64 import datetime import hashlib @@ -38,6 +40,7 @@ if TYPE_CHECKING: from _hashlib import HASH + from hathor.conf.settings import HathorSettings from hathor.transaction.storage import TransactionStorage # noqa: F401 logger = get_logger() @@ -138,17 +141,20 @@ class BaseTransaction(ABC): # bits reserved for future use, depending on the configuration. signal_bits: int - def __init__(self, - nonce: int = 0, - timestamp: Optional[int] = None, - signal_bits: int = 0, - version: TxVersion = TxVersion.REGULAR_BLOCK, - weight: float = 0, - inputs: Optional[list['TxInput']] = None, - outputs: Optional[list['TxOutput']] = None, - parents: Optional[list[VertexId]] = None, - hash: Optional[VertexId] = None, - storage: Optional['TransactionStorage'] = None) -> None: + def __init__( + self, + nonce: int = 0, + timestamp: Optional[int] = None, + signal_bits: int = 0, + version: TxVersion = TxVersion.REGULAR_BLOCK, + weight: float = 0, + inputs: Optional[list['TxInput']] = None, + outputs: Optional[list['TxOutput']] = None, + parents: Optional[list[VertexId]] = None, + hash: Optional[VertexId] = None, + storage: Optional['TransactionStorage'] = None, + settings: HathorSettings | None = None, + ) -> None: """ Nonce: nonce used for the proof-of-work Timestamp: moment of creation @@ -161,7 +167,7 @@ def __init__(self, assert signal_bits <= _ONE_BYTE, f'signal_bits {hex(signal_bits)} must not be larger than one byte' assert version <= _ONE_BYTE, f'version {hex(version)} must not be larger than one byte' - self._settings = get_global_settings() + self._settings = settings or get_global_settings() self.nonce = nonce self.timestamp = timestamp or int(time.time()) self.signal_bits = signal_bits @@ -630,6 +636,7 @@ def get_metadata(self, *, force_reload: bool = False, use_storage: bool = True) min_height = 0 if self.is_genesis else None metadata = TransactionMetadata( + settings=self._settings, hash=self._hash, accumulated_weight=self.weight, height=height, diff --git a/hathor/transaction/block.py b/hathor/transaction/block.py index 82dd0fb6b..22e6d61ac 100644 --- a/hathor/transaction/block.py +++ b/hathor/transaction/block.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import base64 from itertools import starmap, zip_longest from operator import add @@ -30,6 +32,7 @@ from hathor.utils.int import get_bit_list if TYPE_CHECKING: + from hathor.conf.settings import HathorSettings from hathor.transaction.storage import TransactionStorage # noqa: F401 # Signal bits (B), version (B), outputs len (B) @@ -42,19 +45,32 @@ class Block(BaseTransaction): SERIALIZATION_NONCE_SIZE = 16 - def __init__(self, - nonce: int = 0, - timestamp: Optional[int] = None, - signal_bits: int = 0, - version: TxVersion = TxVersion.REGULAR_BLOCK, - weight: float = 0, - outputs: Optional[list[TxOutput]] = None, - parents: Optional[list[bytes]] = None, - hash: Optional[bytes] = None, - data: bytes = b'', - storage: Optional['TransactionStorage'] = None) -> None: - super().__init__(nonce=nonce, timestamp=timestamp, signal_bits=signal_bits, version=version, weight=weight, - outputs=outputs or [], parents=parents or [], hash=hash, storage=storage) + def __init__( + self, + nonce: int = 0, + timestamp: Optional[int] = None, + signal_bits: int = 0, + version: TxVersion = TxVersion.REGULAR_BLOCK, + weight: float = 0, + outputs: Optional[list[TxOutput]] = None, + parents: Optional[list[bytes]] = None, + hash: Optional[bytes] = None, + data: bytes = b'', + storage: Optional['TransactionStorage'] = None, + settings: HathorSettings | None = None, + ) -> None: + super().__init__( + nonce=nonce, + timestamp=timestamp, + signal_bits=signal_bits, + version=version, + weight=weight, + outputs=outputs or [], + parents=parents or [], + hash=hash, + storage=storage, + settings=settings, + ) self.data = data def _get_formatted_fields_dict(self, short: bool = True) -> dict[str, str]: diff --git a/hathor/transaction/merge_mined_block.py b/hathor/transaction/merge_mined_block.py index a0664d3ae..863909882 100644 --- a/hathor/transaction/merge_mined_block.py +++ b/hathor/transaction/merge_mined_block.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from typing import TYPE_CHECKING, Any, Optional from hathor.transaction.aux_pow import BitcoinAuxPow @@ -20,24 +22,39 @@ from hathor.transaction.util import VerboseCallback if TYPE_CHECKING: + from hathor.conf.settings import HathorSettings from hathor.transaction.storage import TransactionStorage # noqa: F401 class MergeMinedBlock(Block): - def __init__(self, - nonce: int = 0, - timestamp: Optional[int] = None, - signal_bits: int = 0, - version: TxVersion = TxVersion.MERGE_MINED_BLOCK, - weight: float = 0, - outputs: Optional[list[TxOutput]] = None, - parents: Optional[list[bytes]] = None, - hash: Optional[bytes] = None, - data: bytes = b'', - aux_pow: Optional[BitcoinAuxPow] = None, - storage: Optional['TransactionStorage'] = None) -> None: - super().__init__(nonce=nonce, timestamp=timestamp, signal_bits=signal_bits, version=version, weight=weight, - data=data, outputs=outputs or [], parents=parents or [], hash=hash, storage=storage) + def __init__( + self, + nonce: int = 0, + timestamp: Optional[int] = None, + signal_bits: int = 0, + version: TxVersion = TxVersion.MERGE_MINED_BLOCK, + weight: float = 0, + outputs: Optional[list[TxOutput]] = None, + parents: Optional[list[bytes]] = None, + hash: Optional[bytes] = None, + data: bytes = b'', + aux_pow: Optional[BitcoinAuxPow] = None, + storage: Optional['TransactionStorage'] = None, + settings: HathorSettings | None = None, + ) -> None: + super().__init__( + nonce=nonce, + timestamp=timestamp, + signal_bits=signal_bits, + version=version, + weight=weight, + data=data, + outputs=outputs or [], + parents=parents or [], + hash=hash, + storage=storage, + settings=settings + ) self.aux_pow = aux_pow def _get_formatted_fields_dict(self, short: bool = True) -> dict[str, str]: diff --git a/hathor/transaction/poa/poa_block.py b/hathor/transaction/poa/poa_block.py index 69a60531a..17e210c14 100644 --- a/hathor/transaction/poa/poa_block.py +++ b/hathor/transaction/poa/poa_block.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from hathor.conf.settings import HathorSettings from hathor.consensus import poa from hathor.transaction import Block, TxOutput, TxVersion from hathor.transaction.storage import TransactionStorage @@ -36,6 +37,7 @@ def __init__( storage: TransactionStorage | None = None, signer_id: bytes = b'', signature: bytes = b'', + settings: HathorSettings | None = None, ) -> None: assert not outputs, 'PoaBlocks must not have outputs' super().__init__( @@ -48,7 +50,8 @@ def __init__( parents=parents or [], hash=hash, data=data, - storage=storage + storage=storage, + settings=settings, ) self.signer_id = signer_id self.signature = signature diff --git a/hathor/transaction/storage/transaction_storage.py b/hathor/transaction/storage/transaction_storage.py index 8aa34bc7b..ab935d928 100644 --- a/hathor/transaction/storage/transaction_storage.py +++ b/hathor/transaction/storage/transaction_storage.py @@ -1111,6 +1111,7 @@ def compute_transactions_that_became_invalid(self, new_best_height: int) -> list def _construct_genesis_block(self) -> Block: """Return the genesis block.""" block = Block( + settings=self._settings, storage=self, nonce=self._settings.GENESIS_BLOCK_NONCE, timestamp=self._settings.GENESIS_BLOCK_TIMESTAMP, @@ -1127,6 +1128,7 @@ def _construct_genesis_block(self) -> Block: def _construct_genesis_tx1(self) -> Transaction: """Return the genesis tx1.""" tx1 = Transaction( + settings=self._settings, storage=self, nonce=self._settings.GENESIS_TX1_NONCE, timestamp=self._settings.GENESIS_TX1_TIMESTAMP, @@ -1140,6 +1142,7 @@ def _construct_genesis_tx1(self) -> Transaction: def _construct_genesis_tx2(self) -> Transaction: """Return the genesis tx2.""" tx2 = Transaction( + settings=self._settings, storage=self, nonce=self._settings.GENESIS_TX2_NONCE, timestamp=self._settings.GENESIS_TX2_TIMESTAMP, diff --git a/hathor/transaction/token_creation_tx.py b/hathor/transaction/token_creation_tx.py index 61a676b2a..629050197 100644 --- a/hathor/transaction/token_creation_tx.py +++ b/hathor/transaction/token_creation_tx.py @@ -17,6 +17,7 @@ from typing_extensions import override +from hathor.conf.settings import HathorSettings from hathor.transaction.base_transaction import TxInput, TxOutput, TxVersion from hathor.transaction.storage import TransactionStorage # noqa: F401 from hathor.transaction.transaction import TokenInfo, Transaction @@ -35,21 +36,35 @@ class TokenCreationTransaction(Transaction): - def __init__(self, - nonce: int = 0, - timestamp: Optional[int] = None, - signal_bits: int = 0, - version: TxVersion = TxVersion.TOKEN_CREATION_TRANSACTION, - weight: float = 0, - inputs: Optional[list[TxInput]] = None, - outputs: Optional[list[TxOutput]] = None, - parents: Optional[list[bytes]] = None, - hash: Optional[bytes] = None, - token_name: str = '', - token_symbol: str = '', - storage: Optional['TransactionStorage'] = None) -> None: - super().__init__(nonce=nonce, timestamp=timestamp, signal_bits=signal_bits, version=version, weight=weight, - inputs=inputs, outputs=outputs or [], parents=parents or [], hash=hash, storage=storage) + def __init__( + self, + nonce: int = 0, + timestamp: Optional[int] = None, + signal_bits: int = 0, + version: TxVersion = TxVersion.TOKEN_CREATION_TRANSACTION, + weight: float = 0, + inputs: Optional[list[TxInput]] = None, + outputs: Optional[list[TxOutput]] = None, + parents: Optional[list[bytes]] = None, + hash: Optional[bytes] = None, + token_name: str = '', + token_symbol: str = '', + storage: Optional['TransactionStorage'] = None, + settings: HathorSettings | None = None, + ) -> None: + super().__init__( + nonce=nonce, + timestamp=timestamp, + signal_bits=signal_bits, + version=version, + weight=weight, + inputs=inputs, + outputs=outputs or [], + parents=parents or [], + hash=hash, + storage=storage, + settings=settings, + ) self.token_name = token_name self.token_symbol = token_symbol # for this special tx, its own hash is used as the created token uid. We're artificially diff --git a/hathor/transaction/transaction.py b/hathor/transaction/transaction.py index 3a846f63f..a787339d8 100644 --- a/hathor/transaction/transaction.py +++ b/hathor/transaction/transaction.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import hashlib from itertools import chain from struct import pack @@ -28,6 +30,7 @@ from hathor.util import not_none if TYPE_CHECKING: + from hathor.conf.settings import HathorSettings from hathor.transaction.storage import TransactionStorage # noqa: F401 # Signal bits (B), version (B), token uids len (B) and inputs len (B), outputs len (B). @@ -52,24 +55,38 @@ class Transaction(BaseTransaction): SERIALIZATION_NONCE_SIZE = 4 - def __init__(self, - nonce: int = 0, - timestamp: Optional[int] = None, - signal_bits: int = 0, - version: TxVersion = TxVersion.REGULAR_TRANSACTION, - weight: float = 0, - inputs: Optional[list[TxInput]] = None, - outputs: Optional[list[TxOutput]] = None, - parents: Optional[list[VertexId]] = None, - tokens: Optional[list[TokenUid]] = None, - hash: Optional[VertexId] = None, - storage: Optional['TransactionStorage'] = None) -> None: + def __init__( + self, + nonce: int = 0, + timestamp: Optional[int] = None, + signal_bits: int = 0, + version: TxVersion = TxVersion.REGULAR_TRANSACTION, + weight: float = 0, + inputs: Optional[list[TxInput]] = None, + outputs: Optional[list[TxOutput]] = None, + parents: Optional[list[VertexId]] = None, + tokens: Optional[list[TokenUid]] = None, + hash: Optional[VertexId] = None, + storage: Optional['TransactionStorage'] = None, + settings: HathorSettings | None = None, + ) -> None: """ Creating new init just to make sure inputs will always be empty array Inputs: all inputs that are being used (empty in case of a block) """ - super().__init__(nonce=nonce, timestamp=timestamp, signal_bits=signal_bits, version=version, weight=weight, - inputs=inputs or [], outputs=outputs or [], parents=parents or [], hash=hash, storage=storage) + super().__init__( + nonce=nonce, + timestamp=timestamp, + signal_bits=signal_bits, + version=version, + weight=weight, + inputs=inputs or [], + outputs=outputs or [], + parents=parents or [], + hash=hash, + storage=storage, + settings=settings + ) self.tokens = tokens or [] self._sighash_cache: Optional[bytes] = None self._sighash_data_cache: Optional[bytes] = None diff --git a/hathor/transaction/transaction_metadata.py b/hathor/transaction/transaction_metadata.py index 17ed326a1..999691638 100644 --- a/hathor/transaction/transaction_metadata.py +++ b/hathor/transaction/transaction_metadata.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from collections import defaultdict from typing import TYPE_CHECKING, Any, Optional @@ -24,6 +26,7 @@ if TYPE_CHECKING: from weakref import ReferenceType # noqa: F401 + from hathor.conf.settings import HathorSettings from hathor.transaction import BaseTransaction from hathor.transaction.storage import TransactionStorage @@ -71,7 +74,8 @@ def __init__( score: float = 0, height: Optional[int] = None, min_height: Optional[int] = None, - feature_activation_bit_counts: Optional[list[int]] = None + feature_activation_bit_counts: Optional[list[int]] = None, + settings: HathorSettings | None = None, ) -> None: from hathor.transaction.genesis import is_genesis @@ -129,7 +133,7 @@ def __init__( self.feature_activation_bit_counts = feature_activation_bit_counts - settings = get_global_settings() + settings = settings or get_global_settings() # Genesis specific: if hash is not None and is_genesis(hash, settings=settings): diff --git a/hathor/transaction/util.py b/hathor/transaction/util.py index a7970359a..d1bec3832 100644 --- a/hathor/transaction/util.py +++ b/hathor/transaction/util.py @@ -12,12 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import re import struct from math import ceil, floor -from typing import Any, Callable, Optional +from typing import TYPE_CHECKING, Any, Callable, Optional -from hathor.conf.get_settings import get_global_settings +if TYPE_CHECKING: + from hathor.conf.settings import HathorSettings VerboseCallback = Optional[Callable[[str, Any], None]] @@ -48,13 +51,11 @@ def unpack_len(n: int, buf: bytes) -> tuple[bytes, bytes]: return buf[:n], buf[n:] -def get_deposit_amount(mint_amount: int) -> int: - settings = get_global_settings() +def get_deposit_amount(settings: HathorSettings, mint_amount: int) -> int: return ceil(abs(settings.TOKEN_DEPOSIT_PERCENTAGE * mint_amount)) -def get_withdraw_amount(melt_amount: int) -> int: - settings = get_global_settings() +def get_withdraw_amount(settings: HathorSettings, melt_amount: int) -> int: return floor(abs(settings.TOKEN_DEPOSIT_PERCENTAGE * melt_amount)) diff --git a/hathor/verification/transaction_verifier.py b/hathor/verification/transaction_verifier.py index af3ac35c2..05f5f3189 100644 --- a/hathor/verification/transaction_verifier.py +++ b/hathor/verification/transaction_verifier.py @@ -232,13 +232,13 @@ def verify_sum(self, token_dict: dict[TokenUid, TokenInfo]) -> None: if not token_info.can_melt: raise InputOutputMismatch('{} {} tokens melted, but there is no melt authority input'.format( token_info.amount, token_uid.hex())) - withdraw += get_withdraw_amount(token_info.amount) + withdraw += get_withdraw_amount(self._settings, token_info.amount) else: # tokens have been minted if not token_info.can_mint: raise InputOutputMismatch('{} {} tokens minted, but there is no mint authority input'.format( (-1) * token_info.amount, token_uid.hex())) - deposit += get_deposit_amount(token_info.amount) + deposit += get_deposit_amount(self._settings, token_info.amount) # check whether the deposit/withdraw amount is correct htr_expected_amount = withdraw - deposit diff --git a/tests/tx/test_tokens.py b/tests/tx/test_tokens.py index 0906477e1..a54e4f765 100644 --- a/tests/tx/test_tokens.py +++ b/tests/tx/test_tokens.py @@ -135,7 +135,7 @@ def test_token_mint(self): # mint tokens and transfer mint authority mint_amount = 10000000 - deposit_amount = get_deposit_amount(mint_amount) + deposit_amount = get_deposit_amount(self._settings, mint_amount) _input1 = TxInput(tx.hash, 1, b'') _input2 = TxInput(tx.hash, 3, b'') token_output1 = TxOutput(mint_amount, script, 1) @@ -195,7 +195,7 @@ def test_token_mint(self): # try to mint and deposit less tokens than necessary mint_amount = 10000000 - deposit_amount = get_deposit_amount(mint_amount) - 1 + deposit_amount = get_deposit_amount(self._settings, mint_amount) - 1 _input1 = TxInput(tx.hash, 1, b'') _input2 = TxInput(tx.hash, 3, b'') token_output1 = TxOutput(mint_amount, script, 1) @@ -241,7 +241,7 @@ def test_token_melt(self): # melt tokens and transfer melt authority melt_amount = 100 new_amount = tx.outputs[0].value - melt_amount - withdraw_amount = get_withdraw_amount(melt_amount) + withdraw_amount = get_withdraw_amount(self._settings, melt_amount) _input1 = TxInput(tx.hash, 0, b'') _input2 = TxInput(tx.hash, 2, b'') token_output1 = TxOutput(new_amount, script, 1) @@ -279,7 +279,7 @@ def test_token_melt(self): # melt tokens and withdraw more than what's allowed melt_amount = 100 - withdraw_amount = get_withdraw_amount(melt_amount) + withdraw_amount = get_withdraw_amount(self._settings, melt_amount) _input1 = TxInput(tx.hash, 0, b'') _input2 = TxInput(tx.hash, 2, b'') token_output1 = TxOutput(tx.outputs[0].value - melt_amount, script, 1) @@ -371,7 +371,7 @@ def test_token_index_with_conflict(self, mint_amount=0): # new tx minting tokens mint_amount = 300 - deposit_amount = get_deposit_amount(mint_amount) + deposit_amount = get_deposit_amount(self._settings, mint_amount) script = P2PKH.create_output_script(self.address) # inputs mint_input = TxInput(tx.hash, 1, b'') diff --git a/tests/utils.py b/tests/utils.py index c72682c4d..ad7a81a57 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -399,7 +399,7 @@ def create_tokens(manager: 'HathorManager', address_b58: Optional[str] = None, m address = decode_address(address_b58) script = P2PKH.create_output_script(address) - deposit_amount = get_deposit_amount(mint_amount) + deposit_amount = get_deposit_amount(manager._settings, mint_amount) if nft_data: # NFT creation needs 0.01 HTR of fee deposit_amount += 1