From 6c494e64a81b279ae1d58146af0a75a6edb5f8fb Mon Sep 17 00:00:00 2001 From: Gabriel Levcovitz Date: Wed, 13 Mar 2024 12:46:25 -0300 Subject: [PATCH] refactor: create non-null hash property --- hathor/transaction/base_transaction.py | 31 ++++++++++++++----------- hathor/transaction/token_creation_tx.py | 2 -- hathor/transaction/transaction.py | 2 +- tests/utils.py | 4 ++-- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/hathor/transaction/base_transaction.py b/hathor/transaction/base_transaction.py index 958c59c05..bc2cd234b 100644 --- a/hathor/transaction/base_transaction.py +++ b/hathor/transaction/base_transaction.py @@ -168,7 +168,7 @@ def __init__(self, self.outputs = outputs or [] self.parents = parents or [] self.storage = storage - self.hash = hash # Stored as bytes. + self._hash: VertexId | None = hash # Stored as bytes. @classproperty def log(cls): @@ -253,7 +253,7 @@ def __eq__(self, other: object) -> bool: """ if not isinstance(other, BaseTransaction): return NotImplemented - if self.hash and other.hash: + if self._hash and other._hash: return self.hash == other.hash return False @@ -265,7 +265,6 @@ def __bytes__(self) -> bytes: return self.get_struct() def __hash__(self) -> int: - assert self.hash is not None return hash(self.hash) @abstractmethod @@ -276,10 +275,19 @@ def calculate_height(self) -> int: def calculate_min_height(self) -> int: raise NotImplementedError + @property + def hash(self) -> VertexId: + assert self._hash is not None, 'Vertex hash must be initialized.' + return self._hash + + @hash.setter + def hash(self, value: VertexId) -> None: + self._hash = value + @property def hash_hex(self) -> str: """Return the current stored hash in hex string format""" - if self.hash is not None: + if self._hash is not None: return self.hash.hex() else: return '' @@ -332,7 +340,7 @@ def is_genesis(self) -> bool: :rtype: bool """ - if self.hash is None: + if self._hash is None: return False from hathor.transaction.genesis import is_genesis return is_genesis(self.hash, settings=self._settings) @@ -451,7 +459,7 @@ def can_validate_full(self) -> bool: """ Check if this transaction is ready to be fully validated, either all deps are full-valid or one is invalid. """ assert self.storage is not None - assert self.hash is not None + assert self._hash is not None if self.is_genesis: return True deps = self.get_all_dependencies() @@ -608,7 +616,6 @@ def get_metadata(self, *, force_reload: bool = False, use_storage: bool = True) else: metadata = getattr(self, '_metadata', None) if not metadata and use_storage and self.storage: - assert self.hash is not None metadata = self.storage.get_metadata(self.hash) self._metadata = metadata if not metadata: @@ -619,7 +626,7 @@ def get_metadata(self, *, force_reload: bool = False, use_storage: bool = True) score = self.weight if self.is_genesis else 0 metadata = TransactionMetadata( - hash=self.hash, + hash=self._hash, accumulated_weight=self.weight, height=height, score=score, @@ -627,7 +634,7 @@ def get_metadata(self, *, force_reload: bool = False, use_storage: bool = True) ) self._metadata = metadata if not metadata.hash: - metadata.hash = self.hash + metadata.hash = self._hash metadata._tx_ref = weakref.ref(self) return metadata @@ -638,7 +645,7 @@ def reset_metadata(self) -> None: from hathor.transaction.transaction_metadata import ValidationState assert self.storage is not None score = self.weight if self.is_genesis else 0 - self._metadata = TransactionMetadata(hash=self.hash, + self._metadata = TransactionMetadata(hash=self._hash, score=score, accumulated_weight=self.weight) if self.is_genesis: @@ -724,7 +731,7 @@ def _update_reward_lock_metadata(self) -> None: def _update_parents_children_metadata(self) -> None: """Update the txs/block parent's children metadata.""" - assert self.hash is not None + assert self._hash is not None assert self.storage is not None for parent in self.get_parents(existing_only=True): @@ -792,7 +799,6 @@ def to_json(self, decode_script: bool = False, include_metadata: bool = False) - return data def to_json_extended(self) -> dict[str, Any]: - assert self.hash is not None assert self.storage is not None def serialize_output(tx: BaseTransaction, tx_out: TxOutput) -> dict[str, Any]: @@ -824,7 +830,6 @@ def serialize_output(tx: BaseTransaction, tx_out: TxOutput) -> dict[str, Any]: tx2 = self.storage.get_transaction(tx_in.tx_id) tx2_out = tx2.outputs[tx_in.index] output = serialize_output(tx2, tx2_out) - assert tx2.hash is not None output['tx_id'] = tx2.hash_hex output['index'] = tx_in.index ret['inputs'].append(output) diff --git a/hathor/transaction/token_creation_tx.py b/hathor/transaction/token_creation_tx.py index 08156ce90..61a676b2a 100644 --- a/hathor/transaction/token_creation_tx.py +++ b/hathor/transaction/token_creation_tx.py @@ -65,7 +65,6 @@ def update_hash(self) -> None: """ When we update the hash, we also have to update the tokens uid list """ super().update_hash() - assert self.hash is not None self.tokens = [self.hash] def get_funds_fields_from_struct(self, buf: bytes, *, verbose: VerboseCallback = None) -> bytes: @@ -221,7 +220,6 @@ def _get_token_info_from_inputs(self) -> dict[TokenUid, TokenInfo]: token_dict = super()._get_token_info_from_inputs() # we add the created token's info to token_dict, as the creation tx allows for mint/melt - assert self.hash is not None token_dict[self.hash] = TokenInfo(0, True, True) return token_dict diff --git a/hathor/transaction/transaction.py b/hathor/transaction/transaction.py index 54189693d..a9d9fec5a 100644 --- a/hathor/transaction/transaction.py +++ b/hathor/transaction/transaction.py @@ -360,7 +360,7 @@ def is_double_spending(self) -> bool: tx = self.storage.get_transaction(tx_in.tx_id) meta = tx.get_metadata() spent_by = meta.get_output_spent_by(tx_in.index) - if spent_by and spent_by != self.hash: + if spent_by and spent_by != self._hash: return True return False diff --git a/tests/utils.py b/tests/utils.py index cdcbd7bb2..4e9a212ab 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -5,7 +5,7 @@ import time import urllib.parse from dataclasses import dataclass -from typing import Optional, cast +from typing import Optional import requests from hathorlib.scripts import DataScript @@ -378,7 +378,7 @@ def create_tokens(manager: 'HathorManager', address_b58: Optional[str] = None, m assert genesis_hash is not None deposit_input = [TxInput(genesis_hash, 0, b'')] change_output = TxOutput(genesis_block.outputs[0].value - deposit_amount, script, 0) - parents = [cast(bytes, tx.hash) for tx in genesis_txs] + parents = [tx.hash for tx in genesis_txs] timestamp = int(manager.reactor.seconds()) else: total_reward = 0