Skip to content

Commit

Permalink
refactor(settings): change wallets and reward lock to use injected se…
Browse files Browse the repository at this point in the history
…ttings (#1148)
  • Loading branch information
glevco authored Oct 17, 2024
1 parent 349555f commit 026cb7b
Show file tree
Hide file tree
Showing 10 changed files with 43 additions and 27 deletions.
2 changes: 1 addition & 1 deletion hathor/builder/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,7 @@ def _get_or_create_wallet(self) -> Optional[BaseWallet]:

if self._wallet_directory is None:
return None
self._wallet = Wallet(directory=self._wallet_directory)
self._wallet = Wallet(directory=self._wallet_directory, settings=self._get_or_create_settings())
if self._wallet_unlock is not None:
self._wallet.unlock(self._wallet_unlock)
return self._wallet
Expand Down
4 changes: 3 additions & 1 deletion hathor/conf/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
GENESIS_TOKEN_UNITS = 1 * (10**9) # 1B
GENESIS_TOKENS = GENESIS_TOKEN_UNITS * (10**DECIMAL_PLACES) # 100B

HATHOR_TOKEN_UID = b'\x00'


class HathorSettings(NamedTuple):
# Version byte of the address in P2PKH
Expand Down Expand Up @@ -125,7 +127,7 @@ def GENESIS_TX2_TIMESTAMP(self) -> int:
MIN_TX_WEIGHT: int = 14
MIN_SHARE_WEIGHT: int = 21

HATHOR_TOKEN_UID: bytes = b'\x00'
HATHOR_TOKEN_UID: bytes = HATHOR_TOKEN_UID

# Maximum distance between two consecutive blocks (in seconds), except for genesis.
# This prevent some DoS attacks exploiting the calculation of the score of a side chain.
Expand Down
2 changes: 1 addition & 1 deletion hathor/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -934,7 +934,7 @@ def push_tx(self, tx: Transaction, allow_non_standard_script: bool = False,
if is_spending_voided_tx:
raise SpendingVoidedError('Invalid transaction. At least one input is voided.')

if is_spent_reward_locked(tx):
if is_spent_reward_locked(self._settings, tx):
raise RewardLockedError('Spent reward is locked.')

# We are using here the method from lib because the property
Expand Down
17 changes: 10 additions & 7 deletions hathor/reward_lock/reward_lock.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from typing import TYPE_CHECKING, Iterator, Optional

from hathor.conf.get_settings import get_global_settings
from hathor.conf.settings import HathorSettings
from hathor.transaction import Block
from hathor.util import not_none

Expand All @@ -32,19 +32,23 @@ def iter_spent_rewards(tx: 'Transaction', storage: 'VertexStorageProtocol') -> I
yield spent_tx


def is_spent_reward_locked(tx: 'Transaction') -> bool:
def is_spent_reward_locked(settings: HathorSettings, tx: 'Transaction') -> bool:
""" Check whether any spent reward is currently locked, considering only the block rewards spent by this tx
itself, and not the inherited `min_height`"""
return get_spent_reward_locked_info(tx, not_none(tx.storage)) is not None
return get_spent_reward_locked_info(settings, tx, not_none(tx.storage)) is not None


def get_spent_reward_locked_info(tx: 'Transaction', storage: 'VertexStorageProtocol') -> Optional['RewardLockedInfo']:
def get_spent_reward_locked_info(
settings: HathorSettings,
tx: 'Transaction',
storage: 'VertexStorageProtocol',
) -> Optional['RewardLockedInfo']:
"""Check if any input block reward is locked, returning the locked information if any, or None if they are all
unlocked."""
from hathor.transaction.transaction import RewardLockedInfo
best_height = get_minimum_best_height(storage)
for blk in iter_spent_rewards(tx, storage):
needed_height = _spent_reward_needed_height(blk, best_height)
needed_height = _spent_reward_needed_height(settings, blk, best_height)
if needed_height > 0:
return RewardLockedInfo(blk.hash, needed_height)
return None
Expand All @@ -65,10 +69,9 @@ def get_minimum_best_height(storage: 'VertexStorageProtocol') -> int:
return best_height


def _spent_reward_needed_height(block: Block, best_height: int) -> int:
def _spent_reward_needed_height(settings: HathorSettings, block: Block, best_height: int) -> int:
""" Returns height still needed to unlock this `block` reward: 0 means it's unlocked."""
spent_height = block.get_height()
spend_blocks = best_height - spent_height
settings = get_global_settings()
needed_height = settings.REWARD_SPEND_MIN_BLOCKS - spend_blocks
return max(needed_height, 0)
2 changes: 1 addition & 1 deletion hathor/simulator/simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def create_artifacts(self, builder: Optional[Builder] = None) -> BuildArtifacts:
assert self._started, 'Simulator is not started.'
builder = builder or self.get_default_builder()

wallet = HDWallet(gap_limit=2)
wallet = HDWallet(gap_limit=2, settings=self.settings)
wallet._manually_initialize()

cpu_mining_service = SimulatorCpuMiningService()
Expand Down
2 changes: 1 addition & 1 deletion hathor/transaction/storage/transaction_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -1063,7 +1063,7 @@ def compute_transactions_that_became_invalid(self, new_best_height: int) -> list
for tx in self.iter_mempool_from_best_index():
try:
TransactionVerifier.verify_reward_locked_for_height(
tx, new_best_height, assert_min_height_verification=False
self._settings, tx, new_best_height, assert_min_height_verification=False
)
except RewardLocked:
tx.set_validation(ValidationState.INVALID)
Expand Down
5 changes: 3 additions & 2 deletions hathor/verification/transaction_verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,11 @@ def verify_reward_locked(self, tx: Transaction) -> None:
the block rewards spent by this tx itself, and the inherited `min_height`."""
assert tx.storage is not None
best_height = get_minimum_best_height(tx.storage)
self.verify_reward_locked_for_height(tx, best_height)
self.verify_reward_locked_for_height(self._settings, tx, best_height)

@staticmethod
def verify_reward_locked_for_height(
settings: HathorSettings,
tx: Transaction,
best_height: int,
*,
Expand All @@ -174,7 +175,7 @@ def verify_reward_locked_for_height(
and a normal `RewardLocked` exception is raised instead.
"""
assert tx.storage is not None
info = get_spent_reward_locked_info(tx, tx.storage)
info = get_spent_reward_locked_info(settings, tx, tx.storage)
if info is not None:
raise RewardLocked(f'Reward {info.block_hash.hex()} still needs {info.blocks_needed} to be unlocked.')

Expand Down
24 changes: 15 additions & 9 deletions hathor/wallet/base_wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
from structlog import get_logger
from twisted.internet.interfaces import IDelayedCall

from hathor.conf import HathorSettings
from hathor.conf.get_settings import get_global_settings
from hathor.conf.settings import HATHOR_TOKEN_UID, HathorSettings
from hathor.crypto.util import decode_address
from hathor.pubsub import EventArguments, HathorEvents, PubSubManager
from hathor.reactor import ReactorProtocol as Reactor, get_global_reactor
Expand All @@ -36,7 +37,6 @@
from hathor.types import AddressB58, Amount, TokenUid
from hathor.wallet.exceptions import InputDuplicated, InsufficientFunds, PrivateKeyNotFound

settings = HathorSettings()
logger = get_logger()

# check interval for maybe_spent_txs
Expand All @@ -55,7 +55,7 @@ class WalletOutputInfo(NamedTuple):
address: bytes
value: int
timelock: Optional[int]
token_uid: str = settings.HATHOR_TOKEN_UID.hex()
token_uid: str = HATHOR_TOKEN_UID.hex()


class WalletBalance(NamedTuple):
Expand All @@ -79,8 +79,13 @@ class WalletType(Enum):
# Normal key pair wallet
KEY_PAIR = 'keypair'

def __init__(self, directory: str = './', pubsub: Optional[PubSubManager] = None,
reactor: Optional[Reactor] = None) -> None:
def __init__(
self,
directory: str = './',
pubsub: Optional[PubSubManager] = None,
reactor: Optional[Reactor] = None,
settings: HathorSettings | None = None,
) -> None:
""" A wallet will hold the unspent and spent transactions
All files will be stored in the same directory, and it should
Expand All @@ -96,6 +101,7 @@ def __init__(self, directory: str = './', pubsub: Optional[PubSubManager] = None
:type reactor: :py:class:`twisted.internet.Reactor`
"""
self.log = logger.new()
self.settings = settings or get_global_settings()

# dict[token_id, dict[tuple[tx_id, index], UnspentTx]]
self.unspent_txs: defaultdict[bytes, dict[tuple[bytes, int], UnspentTx]] = defaultdict(dict)
Expand Down Expand Up @@ -228,7 +234,7 @@ def prepare_transaction(self, cls: ABCMeta, inputs: list[WalletInputInfo],
tokens = [] # list[bytes] = list[token_uid]
for txout in outputs:
token_uid = bytes.fromhex(txout.token_uid)
if token_uid == settings.HATHOR_TOKEN_UID:
if token_uid == HATHOR_TOKEN_UID:
token_index = 0
elif token_uid in token_dict:
token_index = token_dict[token_uid]
Expand Down Expand Up @@ -436,7 +442,7 @@ def sign_transaction(self, tx: Transaction, tx_storage: 'TransactionStorage') ->
_input.data = P2PKH.create_input_data(public_key_bytes, signature)

def handle_change_tx(self, sum_inputs: int, sum_outputs: int,
token_uid: bytes = settings.HATHOR_TOKEN_UID) -> Optional[WalletOutputInfo]:
token_uid: bytes = HATHOR_TOKEN_UID) -> Optional[WalletOutputInfo]:
"""Creates an output transaction with the change value
:param sum_inputs: Sum of the input amounts
Expand All @@ -462,7 +468,7 @@ def handle_change_tx(self, sum_inputs: int, sum_outputs: int,

def get_inputs_from_amount(
self, amount: int, tx_storage: 'TransactionStorage',
token_uid: bytes = settings.HATHOR_TOKEN_UID, max_ts: Optional[int] = None
token_uid: bytes = HATHOR_TOKEN_UID, max_ts: Optional[int] = None
) -> tuple[list[WalletInputInfo], int]:
"""Creates inputs from our pool of unspent tx given a value
Expand Down Expand Up @@ -514,7 +520,7 @@ def can_spend_block(self, tx_storage: 'TransactionStorage', tx_id: bytes) -> boo
tx = tx_storage.get_transaction(tx_id)
if tx.is_block:
assert isinstance(tx, Block)
if tx_storage.get_height_best_block() - tx.get_height() < settings.REWARD_SPEND_MIN_BLOCKS:
if tx_storage.get_height_best_block() - tx.get_height() < self.settings.REWARD_SPEND_MIN_BLOCKS:
return False
return True

Expand Down
6 changes: 4 additions & 2 deletions hathor/wallet/hd_wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

from mnemonic import Mnemonic

from hathor.conf.settings import HathorSettings
from hathor.pubsub import HathorEvents
from hathor.wallet import BaseWallet
from hathor.wallet.exceptions import InvalidWords
Expand Down Expand Up @@ -58,7 +59,8 @@ class HDWallet(BaseWallet):

def __init__(self, *, words: Optional[Any] = None, language: str = 'english', passphrase: bytes = b'',
gap_limit: int = 20, word_count: int = 24, directory: str = './', pubsub: Optional[Any] = None,
reactor: Optional[Any] = None, initial_key_generation: Optional[Any] = None) -> None:
reactor: Optional[Any] = None, initial_key_generation: Optional[Any] = None,
settings: HathorSettings | None = None) -> None:
"""
:param words: words to generate the seed. It's a string with the words separated by a single space.
If None we generate new words when starting the wallet
Expand All @@ -84,7 +86,7 @@ def __init__(self, *, words: Optional[Any] = None, language: str = 'english', pa
:raises ValueError: Raised on invalid word_count
"""
super().__init__(directory=directory, pubsub=pubsub, reactor=reactor)
super().__init__(directory=directory, pubsub=pubsub, reactor=reactor, settings=settings)

# dict[string(base58), BIP32Key]
self.keys: dict[str, Any] = {}
Expand Down
6 changes: 4 additions & 2 deletions hathor/wallet/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from cryptography.hazmat.primitives.asymmetric import ec
from twisted.internet.interfaces import IDelayedCall

from hathor.conf.settings import HathorSettings
from hathor.crypto.util import get_public_key_bytes_compressed
from hathor.pubsub import HathorEvents
from hathor.wallet import BaseWallet
Expand All @@ -30,7 +31,8 @@

class Wallet(BaseWallet):
def __init__(self, keys: Optional[Any] = None, directory: str = './', filename: str = 'keys.json',
pubsub: Optional[Any] = None, reactor: Optional[Any] = None) -> None:
pubsub: Optional[Any] = None, reactor: Optional[Any] = None, settings: HathorSettings | None = None
) -> None:
""" A wallet will hold key pair objects and the unspent and
spent transactions associated with the keys.
Expand All @@ -49,7 +51,7 @@ def __init__(self, keys: Optional[Any] = None, directory: str = './', filename:
:param pubsub: If not given, a new one is created.
:type pubsub: :py:class:`hathor.pubsub.PubSubManager`
"""
super().__init__(directory=directory, pubsub=pubsub, reactor=reactor)
super().__init__(directory=directory, pubsub=pubsub, reactor=reactor, settings=settings)

self.filepath = os.path.join(directory, filename)
self.keys: dict[str, Any] = keys or {} # dict[string(b58_address), KeyPair]
Expand Down

0 comments on commit 026cb7b

Please sign in to comment.