Skip to content

Commit

Permalink
refactor(verification): move feature activation dependency
Browse files Browse the repository at this point in the history
  • Loading branch information
glevco committed Apr 9, 2024
1 parent 23f764d commit eb0f05d
Show file tree
Hide file tree
Showing 22 changed files with 114 additions and 154 deletions.
14 changes: 5 additions & 9 deletions hathor/builder/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class BuildArtifacts(NamedTuple):


_VertexVerifiersBuilder: TypeAlias = Callable[
[HathorSettingsType, DifficultyAdjustmentAlgorithm, FeatureService],
[HathorSettingsType, DifficultyAdjustmentAlgorithm],
VertexVerifiers
]

Expand Down Expand Up @@ -493,7 +493,8 @@ def _get_or_create_bit_signaling_service(self) -> BitSignalingService:
def _get_or_create_verification_service(self) -> VerificationService:
if self._verification_service is None:
verifiers = self._get_or_create_vertex_verifiers()
self._verification_service = VerificationService(verifiers=verifiers)
feature_service = self._get_or_create_feature_service()
self._verification_service = VerificationService(verifiers=verifiers, feature_service=feature_service)

return self._verification_service

Expand All @@ -509,17 +510,12 @@ def _get_or_create_feature_storage(self) -> FeatureActivationStorage | None:
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, daa, feature_service)
self._vertex_verifiers = self._vertex_verifiers_builder(settings, daa)
else:
self._vertex_verifiers = VertexVerifiers.create_defaults(
settings=settings,
daa=daa,
feature_service=feature_service,
)
self._vertex_verifiers = VertexVerifiers.create_defaults(settings=settings, daa=daa)

return self._vertex_verifiers

Expand Down
8 changes: 2 additions & 6 deletions hathor/builder/cli_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,12 +275,8 @@ def create_manager(self, reactor: Reactor) -> HathorManager:

daa = DifficultyAdjustmentAlgorithm(settings=settings, test_mode=test_mode)

vertex_verifiers = VertexVerifiers.create_defaults(
settings=settings,
daa=daa,
feature_service=self.feature_service
)
verification_service = VerificationService(verifiers=vertex_verifiers)
vertex_verifiers = VertexVerifiers.create_defaults(settings=settings, daa=daa)
verification_service = VerificationService(verifiers=vertex_verifiers, feature_service=self.feature_service)

cpu_mining_service = CpuMiningService()

Expand Down
4 changes: 1 addition & 3 deletions hathor/cli/mining.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,15 +135,13 @@ def execute(args: Namespace) -> None:
block.nonce, block.weight))

try:
from unittest.mock import Mock

from hathor.conf.get_settings import get_global_settings
from hathor.daa import DifficultyAdjustmentAlgorithm
from hathor.verification.verification_service import VerificationService
from hathor.verification.vertex_verifiers import VertexVerifiers
settings = get_global_settings()
daa = DifficultyAdjustmentAlgorithm(settings=settings)
verifiers = VertexVerifiers.create_defaults(settings=settings, daa=daa, feature_service=Mock())
verifiers = VertexVerifiers.create_defaults(settings=settings, daa=daa)
verification_service = VerificationService(verifiers=verifiers)
verification_service.verify_without_storage(block)
except HathorError:
Expand Down
2 changes: 1 addition & 1 deletion hathor/feature_activation/bit_signaling_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ def _log_signal_bits(self, feature: Feature, enable_bit: bool, support: bool, no

def _get_signaling_features(self, block: Block) -> dict[Feature, Criteria]:
"""Given a specific block, return all features that are in a signaling state for that block."""
feature_descriptions = self._feature_service.get_bits_description(block=block)
feature_descriptions = self._feature_service.get_feature_info(block=block)
signaling_features = {
feature: description.criteria
for feature, description in feature_descriptions.items()
Expand Down
8 changes: 4 additions & 4 deletions hathor/feature_activation/feature_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from typing import TYPE_CHECKING, Optional, TypeAlias

from hathor.feature_activation.feature import Feature
from hathor.feature_activation.model.feature_description import FeatureDescription
from hathor.feature_activation.model.feature_description import FeatureInfo
from hathor.feature_activation.model.feature_state import FeatureState
from hathor.feature_activation.settings import Settings as FeatureSettings

Expand Down Expand Up @@ -64,7 +64,7 @@ def is_signaling_mandatory_features(self, block: 'Block') -> BlockSignalingState
height = block.get_height()
offset_to_boundary = height % self._feature_settings.evaluation_interval
remaining_blocks = self._feature_settings.evaluation_interval - offset_to_boundary - 1
descriptions = self.get_bits_description(block=block)
descriptions = self.get_feature_info(block=block)

must_signal_features = (
feature for feature, description in descriptions.items()
Expand Down Expand Up @@ -194,10 +194,10 @@ def _calculate_new_state(

raise ValueError(f'Unknown previous state: {previous_state}')

def get_bits_description(self, *, block: 'Block') -> dict[Feature, FeatureDescription]:
def get_feature_info(self, *, block: 'Block') -> dict[Feature, FeatureInfo]:
"""Returns the criteria definition and feature state for all features at a certain block."""
return {
feature: FeatureDescription(
feature: FeatureInfo(
criteria=criteria,
state=self.get_state(block=block, feature=feature)
)
Expand Down
2 changes: 1 addition & 1 deletion hathor/feature_activation/model/feature_description.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from hathor.feature_activation.model.feature_state import FeatureState


class FeatureDescription(NamedTuple):
class FeatureInfo(NamedTuple):
"""Represents all information related to one feature, that is, its criteria and state."""
criteria: Criteria
state: FeatureState
4 changes: 4 additions & 0 deletions hathor/feature_activation/model/feature_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,7 @@ def get_signaling_states() -> set['FeatureState']:
support it or not through bit signals is valid during those states.
"""
return {FeatureState.STARTED, FeatureState.MUST_SIGNAL, FeatureState.LOCKED_IN}

def is_active(self) -> bool:
"""Return whether the state is active."""
return self is FeatureState.ACTIVE
2 changes: 1 addition & 1 deletion hathor/feature_activation/resources/feature.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def get_block_features(self, request: Request) -> bytes:
return error.json_dumpb()

signal_bits = []
feature_descriptions = self._feature_service.get_bits_description(block=block)
feature_descriptions = self._feature_service.get_feature_info(block=block)

for feature, description in feature_descriptions.items():
if description.state not in FeatureState.get_signaling_states():
Expand Down
2 changes: 1 addition & 1 deletion hathor/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -1077,7 +1077,7 @@ def _log_feature_states(self, vertex: BaseTransaction) -> None:
if not isinstance(vertex, Block):
return

feature_descriptions = self._feature_service.get_bits_description(block=vertex)
feature_descriptions = self._feature_service.get_feature_info(block=vertex)
state_by_feature = {
feature.value: description.state.value
for feature, description in feature_descriptions.items()
Expand Down
8 changes: 1 addition & 7 deletions hathor/simulator/simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
from hathor.conf.get_settings import get_global_settings
from hathor.conf.settings import HathorSettings
from hathor.daa import DifficultyAdjustmentAlgorithm
from hathor.feature_activation.feature_service import FeatureService
from hathor.manager import HathorManager
from hathor.p2p.peer_id import PeerId
from hathor.simulator.clock import HeapClock, MemoryReactorHeapClock
Expand Down Expand Up @@ -243,17 +242,12 @@ def run(self,
return True


def _build_vertex_verifiers(
settings: HathorSettings,
daa: DifficultyAdjustmentAlgorithm,
feature_service: FeatureService
) -> VertexVerifiers:
def _build_vertex_verifiers(settings: HathorSettings, daa: DifficultyAdjustmentAlgorithm) -> VertexVerifiers:
"""
A custom VertexVerifiers builder to be used by the simulator.
"""
return VertexVerifiers.create(
settings=settings,
vertex_verifier=SimulatorVertexVerifier(settings=settings, daa=daa),
daa=daa,
feature_service=feature_service,
)
15 changes: 6 additions & 9 deletions hathor/verification/block_verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from typing_extensions import assert_never

from hathor.conf.settings import HathorSettings
from hathor.daa import DifficultyAdjustmentAlgorithm
from hathor.feature_activation.feature_service import BlockIsMissingSignal, BlockIsSignaling, FeatureService
from hathor.feature_activation.feature_service import BlockIsMissingSignal, BlockIsSignaling, BlockSignalingState
from hathor.transaction import Block
from hathor.transaction.exceptions import (
BlockMustSignalError,
Expand All @@ -30,18 +32,16 @@


class BlockVerifier:
__slots__ = ('_settings', '_daa', '_feature_service')
__slots__ = ('_settings', '_daa')

def __init__(
self,
*,
settings: HathorSettings,
daa: DifficultyAdjustmentAlgorithm,
feature_service: FeatureService,
) -> None:
self._settings = settings
self._daa = daa
self._feature_service = feature_service

def verify_height(self, block: Block) -> None:
"""Validate that the block height is enough to confirm all transactions being confirmed."""
Expand Down Expand Up @@ -86,10 +86,8 @@ def verify_data(self, block: Block) -> None:
if len(block.data) > self._settings.BLOCK_DATA_MAX_SIZE:
raise TransactionDataError('block data has {} bytes'.format(len(block.data)))

def verify_mandatory_signaling(self, block: Block) -> None:
def verify_mandatory_signaling(self, signaling_state: BlockSignalingState) -> None:
"""Verify whether this block is missing mandatory signaling for any feature."""
signaling_state = self._feature_service.is_signaling_mandatory_features(block)

match signaling_state:
case BlockIsSignaling():
return
Expand All @@ -98,5 +96,4 @@ def verify_mandatory_signaling(self, block: Block) -> None:
f"Block must signal support for feature '{feature.value}' during MUST_SIGNAL phase."
)
case _:
# TODO: This will be changed to assert_never() so mypy can check it.
raise NotImplementedError
assert_never(signaling_state)
22 changes: 9 additions & 13 deletions hathor/verification/merge_mined_block_verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,29 +14,25 @@

from hathor.conf.settings import HathorSettings
from hathor.feature_activation.feature import Feature
from hathor.feature_activation.feature_service import FeatureService
from hathor.feature_activation.model.feature_description import FeatureInfo
from hathor.transaction import MergeMinedBlock


class MergeMinedBlockVerifier:
__slots__ = ('_settings', '_feature_service',)
__slots__ = ('_settings',)

def __init__(self, *, settings: HathorSettings, feature_service: FeatureService):
def __init__(self, *, settings: HathorSettings) -> None:
self._settings = settings
self._feature_service = feature_service

def verify_aux_pow(self, block: MergeMinedBlock) -> None:
def verify_aux_pow(self, block: MergeMinedBlock, feature_info: dict[Feature, FeatureInfo]) -> None:
""" Verify auxiliary proof-of-work (for merged mining).
"""
assert block.aux_pow is not None

is_feature_active = self._feature_service.is_feature_active(
block=block,
feature=Feature.INCREASE_MAX_MERKLE_PATH_LENGTH
)
max_merkle_path_length = (
self._settings.NEW_MAX_MERKLE_PATH_LENGTH if is_feature_active
else self._settings.OLD_MAX_MERKLE_PATH_LENGTH
)
max_merkle_path_length = self._settings.OLD_MAX_MERKLE_PATH_LENGTH
merkle_path_info = feature_info.get(Feature.INCREASE_MAX_MERKLE_PATH_LENGTH)

if merkle_path_info and merkle_path_info.state.is_active():
max_merkle_path_length = self._settings.NEW_MAX_MERKLE_PATH_LENGTH

block.aux_pow.verify(block.get_base_hash(), max_merkle_path_length)
31 changes: 22 additions & 9 deletions hathor/verification/verification_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@

from typing_extensions import assert_never

from hathor.feature_activation.feature import Feature
from hathor.feature_activation.feature_service import BlockSignalingState, FeatureService
from hathor.feature_activation.model.feature_description import FeatureInfo
from hathor.profiler import get_cpu_profiler
from hathor.transaction import BaseTransaction, Block, MergeMinedBlock, Transaction, TxVersion
from hathor.transaction.token_creation_tx import TokenCreationTransaction
Expand All @@ -26,10 +29,11 @@


class VerificationService:
__slots__ = ('verifiers', )
__slots__ = ('verifiers', '_feature_service')

def __init__(self, *, verifiers: VertexVerifiers) -> None:
def __init__(self, *, verifiers: VertexVerifiers, feature_service: FeatureService | None = None) -> None:
self.verifiers = verifiers
self._feature_service = feature_service

def validate_basic(self, vertex: BaseTransaction, *, skip_block_weight_verification: bool = False) -> bool:
""" Run basic validations (all that are possible without dependencies) and update the validation state.
Expand Down Expand Up @@ -124,14 +128,18 @@ def verify(self, vertex: BaseTransaction, *, reject_locked_reward: bool = True)
"""Run all verifications. Raises on error.
Used by `self.validate_full`. Should not modify the validation state."""
assert self._feature_service is not None
# We assert with type() instead of isinstance() because each subclass has a specific branch.
match vertex.version:
case TxVersion.REGULAR_BLOCK:
assert type(vertex) is Block
self._verify_block(vertex)
signaling_state = self._feature_service.is_signaling_mandatory_features(vertex)
self._verify_block(vertex, signaling_state)
case TxVersion.MERGE_MINED_BLOCK:
assert type(vertex) is MergeMinedBlock
self._verify_merge_mined_block(vertex)
signaling_state = self._feature_service.is_signaling_mandatory_features(vertex)
feature_info = self._feature_service.get_feature_info(block=vertex)
self._verify_merge_mined_block(vertex, signaling_state, feature_info)
case TxVersion.REGULAR_TRANSACTION:
assert type(vertex) is Transaction
self._verify_tx(vertex, reject_locked_reward=reject_locked_reward)
Expand All @@ -142,7 +150,7 @@ def verify(self, vertex: BaseTransaction, *, reject_locked_reward: bool = True)
assert_never(vertex.version)

@cpu.profiler(key=lambda _, block: 'block-verify!{}'.format(block.hash.hex()))
def _verify_block(self, block: Block) -> None:
def _verify_block(self, block: Block, signaling_state: BlockSignalingState) -> None:
"""
(1) confirms at least two pending transactions and references last block
(2) solves the pow with the correct weight (done in HathorManager)
Expand All @@ -163,10 +171,16 @@ def _verify_block(self, block: Block) -> None:

self.verifiers.block.verify_height(block)

self.verifiers.block.verify_mandatory_signaling(block)
self.verifiers.block.verify_mandatory_signaling(signaling_state)

def _verify_merge_mined_block(self, block: MergeMinedBlock) -> None:
self._verify_block(block)
def _verify_merge_mined_block(
self,
block: MergeMinedBlock,
signaling_state: BlockSignalingState,
feature_info: dict[Feature, FeatureInfo]
) -> None:
self.verifiers.merge_mined_block.verify_aux_pow(block, feature_info)
self._verify_block(block, signaling_state)

@cpu.profiler(key=lambda _, tx: 'tx-verify!{}'.format(tx.hash.hex()))
def _verify_tx(
Expand Down Expand Up @@ -237,7 +251,6 @@ def _verify_without_storage_block(self, block: Block) -> None:
self.verifiers.vertex.verify_sigops_output(block)

def _verify_without_storage_merge_mined_block(self, block: MergeMinedBlock) -> None:
self.verifiers.merge_mined_block.verify_aux_pow(block)
self._verify_without_storage_block(block)

def _verify_without_storage_tx(self, tx: Transaction) -> None:
Expand Down
15 changes: 3 additions & 12 deletions hathor/verification/vertex_verifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

from hathor.conf.settings import HathorSettings
from hathor.daa import DifficultyAdjustmentAlgorithm
from hathor.feature_activation.feature_service import FeatureService
from hathor.verification.block_verifier import BlockVerifier
from hathor.verification.merge_mined_block_verifier import MergeMinedBlockVerifier
from hathor.verification.token_creation_transaction_verifier import TokenCreationTransactionVerifier
Expand All @@ -33,13 +32,7 @@ class VertexVerifiers(NamedTuple):
token_creation_tx: TokenCreationTransactionVerifier

@classmethod
def create_defaults(
cls,
*,
settings: HathorSettings,
daa: DifficultyAdjustmentAlgorithm,
feature_service: FeatureService,
) -> 'VertexVerifiers':
def create_defaults(cls, *, settings: HathorSettings, daa: DifficultyAdjustmentAlgorithm) -> 'VertexVerifiers':
"""
Create a VertexVerifiers instance using the default verifier for each vertex type,
from all required dependencies.
Expand All @@ -50,7 +43,6 @@ def create_defaults(
settings=settings,
vertex_verifier=vertex_verifier,
daa=daa,
feature_service=feature_service
)

@classmethod
Expand All @@ -60,13 +52,12 @@ def create(
settings: HathorSettings,
vertex_verifier: VertexVerifier,
daa: DifficultyAdjustmentAlgorithm,
feature_service: FeatureService,
) -> 'VertexVerifiers':
"""
Create a VertexVerifiers instance using a custom vertex_verifier.
"""
block_verifier = BlockVerifier(settings=settings, daa=daa, feature_service=feature_service)
merge_mined_block_verifier = MergeMinedBlockVerifier(settings=settings, feature_service=feature_service)
block_verifier = BlockVerifier(settings=settings, daa=daa)
merge_mined_block_verifier = MergeMinedBlockVerifier(settings=settings)
tx_verifier = TransactionVerifier(settings=settings, daa=daa)
token_creation_tx_verifier = TokenCreationTransactionVerifier(settings=settings)

Expand Down
Loading

0 comments on commit eb0f05d

Please sign in to comment.