Skip to content

Commit

Permalink
refactor(cpu-mining): create CpuMiningService
Browse files Browse the repository at this point in the history
  • Loading branch information
glevco committed Sep 29, 2023
1 parent 7804866 commit 4f8296a
Show file tree
Hide file tree
Showing 49 changed files with 333 additions and 297 deletions.
15 changes: 15 additions & 0 deletions hathor/builder/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from hathor.feature_activation.feature_service import FeatureService
from hathor.indexes import IndexesManager, MemoryIndexesManager, RocksDBIndexesManager
from hathor.manager import HathorManager
from hathor.mining.cpu_mining_service import CpuMiningService
from hathor.p2p.manager import ConnectionsManager
from hathor.p2p.peer_id import PeerId
from hathor.pubsub import PubSubManager
Expand Down Expand Up @@ -104,6 +105,7 @@ def __init__(self) -> None:
self._bit_signaling_service: Optional[BitSignalingService] = None

self._daa: Optional[DifficultyAdjustmentAlgorithm] = None
self._cpu_mining_service: Optional[CpuMiningService] = None

self._vertex_verifiers: Optional[VertexVerifiers] = None
self._verification_service: Optional[VerificationService] = None
Expand Down Expand Up @@ -166,6 +168,7 @@ def build(self) -> BuildArtifacts:
bit_signaling_service = self._get_or_create_bit_signaling_service(tx_storage)
verification_service = self._get_or_create_verification_service()
daa = self._get_or_create_daa()
cpu_mining_service = self._get_or_create_cpu_mining_service()

if self._enable_address_index:
indexes.enable_address_index(pubsub)
Expand Down Expand Up @@ -203,6 +206,7 @@ def build(self) -> BuildArtifacts:
feature_service=feature_service,
bit_signaling_service=bit_signaling_service,
verification_service=verification_service,
cpu_mining_service=cpu_mining_service,
**kwargs
)

Expand Down Expand Up @@ -458,6 +462,12 @@ def _get_or_create_daa(self) -> DifficultyAdjustmentAlgorithm:

return self._daa

def _get_or_create_cpu_mining_service(self) -> CpuMiningService:
if self._cpu_mining_service is None:
self._cpu_mining_service = CpuMiningService()

return self._cpu_mining_service

def use_memory(self) -> 'Builder':
self.check_if_can_modify()
self._storage_type = StorageType.MEMORY
Expand Down Expand Up @@ -565,6 +575,11 @@ def set_daa(self, daa: DifficultyAdjustmentAlgorithm) -> 'Builder':
self._daa = daa
return self

def set_cpu_mining_service(self, cpu_mining_service: CpuMiningService) -> 'Builder':
self.check_if_can_modify()
self._cpu_mining_service = cpu_mining_service
return self

def set_reactor(self, reactor: Reactor) -> 'Builder':
self.check_if_can_modify()
self._reactor = reactor
Expand Down
4 changes: 4 additions & 0 deletions hathor/builder/cli_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from hathor.feature_activation.feature_service import FeatureService
from hathor.indexes import IndexesManager, MemoryIndexesManager, RocksDBIndexesManager
from hathor.manager import HathorManager
from hathor.mining.cpu_mining_service import CpuMiningService
from hathor.p2p.manager import ConnectionsManager
from hathor.p2p.peer_id import PeerId
from hathor.p2p.utils import discover_hostname, get_genesis_short_hash
Expand Down Expand Up @@ -214,6 +215,8 @@ def create_manager(self, reactor: Reactor) -> HathorManager:
vertex_verifiers = VertexVerifiers.create(settings=settings, daa=daa)
verification_service = VerificationService(verifiers=vertex_verifiers)

cpu_mining_service = CpuMiningService()

p2p_manager = ConnectionsManager(
reactor,
network=network,
Expand Down Expand Up @@ -247,6 +250,7 @@ def create_manager(self, reactor: Reactor) -> HathorManager:
feature_service=self.feature_service,
bit_signaling_service=bit_signaling_service,
verification_service=verification_service,
cpu_mining_service=cpu_mining_service
)

p2p_manager.set_manager(self.manager)
Expand Down
3 changes: 2 additions & 1 deletion hathor/cli/mining.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

from hathor.conf.get_settings import get_settings
from hathor.daa import DifficultyAdjustmentAlgorithm
from hathor.mining.cpu_mining_service import CpuMiningService
from hathor.verification.block_verifier import BlockVerifier

_SLEEP_ON_ERROR_SECONDS = 5
Expand All @@ -39,7 +40,7 @@ def signal_handler(sig, frame):
def worker(q_in, q_out):
signal.signal(signal.SIGINT, signal_handler)
block, start, end, sleep_seconds = q_in.get()
block.start_mining(start, end, sleep_seconds=sleep_seconds)
CpuMiningService().start_mining(block, start=start, end=end, sleep_seconds=sleep_seconds)
q_out.put(block)


Expand Down
4 changes: 3 additions & 1 deletion hathor/cli/multisig_spend.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

from argparse import ArgumentParser, Namespace

from hathor.mining.cpu_mining_service import CpuMiningService


def create_parser() -> ArgumentParser:
from hathor.cli.util import create_parser
Expand All @@ -36,7 +38,7 @@ def execute(args: Namespace) -> None:
input_data = MultiSig.create_input_data(bytes.fromhex(args.redeem_script), signatures)
tx.inputs[0].data = input_data

tx.resolve()
CpuMiningService().resolve(tx)
print('Transaction after POW: ', tx.get_struct().hex())


Expand Down
4 changes: 3 additions & 1 deletion hathor/cli/twin_tx.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

import requests

from hathor.mining.cpu_mining_service import CpuMiningService


def create_parser() -> ArgumentParser:
from hathor.cli.util import create_parser
Expand Down Expand Up @@ -89,7 +91,7 @@ def execute(args: Namespace) -> None:
if args.weight:
twin.weight = args.weight

twin.resolve()
CpuMiningService().resolve(twin)
if args.human:
print(twin.to_json())
else:
Expand Down
3 changes: 3 additions & 0 deletions hathor/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
from hathor.feature_activation.feature import Feature
from hathor.feature_activation.feature_service import FeatureService
from hathor.mining import BlockTemplate, BlockTemplates
from hathor.mining.cpu_mining_service import CpuMiningService
from hathor.p2p.manager import ConnectionsManager
from hathor.p2p.peer_discovery import PeerDiscovery
from hathor.p2p.peer_id import PeerId
Expand Down Expand Up @@ -98,6 +99,7 @@ def __init__(self,
feature_service: FeatureService,
bit_signaling_service: BitSignalingService,
verification_service: VerificationService,
cpu_mining_service: CpuMiningService,
network: str,
hostname: Optional[str] = None,
wallet: Optional[BaseWallet] = None,
Expand Down Expand Up @@ -176,6 +178,7 @@ def __init__(self,
self._feature_service = feature_service
self._bit_signaling_service = bit_signaling_service
self.verification_service = verification_service
self.cpu_mining_service = cpu_mining_service

self.consensus_algorithm = consensus_algorithm

Expand Down
91 changes: 91 additions & 0 deletions hathor/mining/cpu_mining_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Copyright 2023 Hathor Labs
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import time
from typing import Callable, Optional

from hathor.transaction import BaseTransaction
from hathor.transaction.token_creation_tx import TokenCreationTransaction
from hathor.types import VertexId

MAX_NONCE = 2**32


class CpuMiningService:
def resolve(self, vertex: BaseTransaction, *, update_time: bool = False) -> bool:
"""Run a CPU mining looking for the nonce that solves the proof-of-work
The `vertex.weight` must be set before calling this method.
:param update_time: update timestamp every 2 seconds
:return: True if a solution was found
:rtype: bool
"""
hash_bytes = self.start_mining(vertex, update_time=update_time)

if hash_bytes:
vertex.hash = hash_bytes
metadata = getattr(vertex, '_metadata', None)
if metadata is not None and metadata.hash is not None:
metadata.hash = hash_bytes

if isinstance(vertex, TokenCreationTransaction):
vertex.tokens = [vertex.hash]

return True
else:
return False

@staticmethod
def start_mining(
vertex: BaseTransaction,
*,
start: int = 0,
end: int = MAX_NONCE,
sleep_seconds: float = 0.0,
update_time: bool = True,
should_stop: Callable[[], bool] = lambda: False
) -> Optional[VertexId]:
"""Starts mining until it solves the problem, i.e., finds the nonce that satisfies the conditions
:param start: beginning of the search interval
:param end: end of the search interval
:param sleep_seconds: the number of seconds it will sleep after each attempt
:param update_time: update timestamp every 2 seconds
:return The hash of the solved PoW or None when it is not found
"""
pow_part1 = vertex.calculate_hash1()
target = vertex.get_target()
vertex.nonce = start
last_time = time.time()
while vertex.nonce < end:
if update_time:
now = time.time()
if now - last_time > 2:
if should_stop():
return None
vertex.timestamp = int(now)
pow_part1 = vertex.calculate_hash1()
last_time = now
vertex.nonce = start

result = vertex.calculate_hash2(pow_part1.copy())
if int(result.hex(), vertex.HEX_BASE) < target:
return result
vertex.nonce += 1
if sleep_seconds > 0:
time.sleep(sleep_seconds)
if should_stop():
return None
return None
20 changes: 14 additions & 6 deletions hathor/simulator/verification.py → hathor/simulator/patches.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

from structlog import get_logger

from hathor.mining.cpu_mining_service import CpuMiningService
from hathor.transaction import BaseTransaction
from hathor.verification.block_verifier import BlockVerifier
from hathor.verification.merge_mined_block_verifier import MergeMinedBlockVerifier
Expand All @@ -25,30 +26,37 @@
logger = get_logger()


def verify_pow(vertex: BaseTransaction) -> None:
def _verify_pow(vertex: BaseTransaction) -> None:
assert vertex.hash is not None
logger.new().debug('Skipping BaseTransaction.verify_pow() for simulator')
logger.new().debug('Skipping VertexVerifier.verify_pow() for simulator')


class SimulatorBlockVerifier(BlockVerifier):
@classmethod
def verify_pow(cls, vertex: BaseTransaction, *, override_weight: Optional[float] = None) -> None:
verify_pow(vertex)
_verify_pow(vertex)


class SimulatorMergeMinedBlockVerifier(MergeMinedBlockVerifier):
@classmethod
def verify_pow(cls, vertex: BaseTransaction, *, override_weight: Optional[float] = None) -> None:
verify_pow(vertex)
_verify_pow(vertex)


class SimulatorTransactionVerifier(TransactionVerifier):
@classmethod
def verify_pow(cls, vertex: BaseTransaction, *, override_weight: Optional[float] = None) -> None:
verify_pow(vertex)
_verify_pow(vertex)


class SimulatorTokenCreationTransactionVerifier(TokenCreationTransactionVerifier):
@classmethod
def verify_pow(cls, vertex: BaseTransaction, *, override_weight: Optional[float] = None) -> None:
verify_pow(vertex)
_verify_pow(vertex)


class SimulatorCpuMiningService(CpuMiningService):
def resolve(self, vertex: BaseTransaction, *, update_time: bool = False) -> bool:
vertex.update_hash()
logger.new().debug('Skipping CpuMiningService.resolve() for simulator')
return True
Loading

0 comments on commit 4f8296a

Please sign in to comment.