diff --git a/hathor/builder/builder.py b/hathor/builder/builder.py index 7d409b6c9..1c83b3494 100644 --- a/hathor/builder/builder.py +++ b/hathor/builder/builder.py @@ -155,6 +155,7 @@ def build(self) -> BuildArtifacts: event_manager = self._get_or_create_event_manager() indexes = self._get_or_create_indexes_manager() tx_storage = self._get_or_create_tx_storage(indexes) + feature_service = self._get_or_create_feature_service(tx_storage) bit_signaling_service = self._get_or_create_bit_signaling_service(tx_storage) if self._enable_address_index: @@ -188,6 +189,7 @@ def build(self) -> BuildArtifacts: checkpoints=self._checkpoints, capabilities=self._capabilities, environment_info=get_environment_info(self._cmdline, peer_id.id), + feature_service=feature_service, bit_signaling_service=bit_signaling_service, **kwargs ) diff --git a/hathor/builder/cli_builder.py b/hathor/builder/cli_builder.py index 16e22b79a..18115c31b 100644 --- a/hathor/builder/cli_builder.py +++ b/hathor/builder/cli_builder.py @@ -232,6 +232,7 @@ def create_manager(self, reactor: PosixReactorBase) -> HathorManager: environment_info=get_environment_info(args=str(self._args), peer_id=peer_id.id), full_verification=full_verification, enable_event_queue=self._args.x_enable_event_queue, + feature_service=self.feature_service, bit_signaling_service=bit_signaling_service ) diff --git a/hathor/conf/testnet.py b/hathor/conf/testnet.py index 1dad09eef..f88a64abd 100644 --- a/hathor/conf/testnet.py +++ b/hathor/conf/testnet.py @@ -14,6 +14,7 @@ from hathor.checkpoint import Checkpoint as cp from hathor.conf.settings import HathorSettings +from hathor.feature_activation.settings import Settings as FeatureActivationSettings SETTINGS = HathorSettings( P2PKH_VERSION_BYTE=b'\x49', @@ -51,4 +52,8 @@ cp(1_500_000, bytes.fromhex('000000000c3591805f4748480b59ac1788f754fc004930985a487580e2b5de8f')), cp(1_600_000, bytes.fromhex('00000000060adfdfd7d488d4d510b5779cf35a3c50df7bcff941fbb6957be4d2')), ], + FEATURE_ACTIVATION=FeatureActivationSettings( + enable_usage=True, + default_threshold=30240 + ) ) diff --git a/hathor/conf/testnet.yml b/hathor/conf/testnet.yml index 98ad03430..3f6658a07 100644 --- a/hathor/conf/testnet.yml +++ b/hathor/conf/testnet.yml @@ -36,6 +36,6 @@ CHECKPOINTS: 1_500_000: 000000000c3591805f4748480b59ac1788f754fc004930985a487580e2b5de8f 1_600_000: 00000000060adfdfd7d488d4d510b5779cf35a3c50df7bcff941fbb6957be4d2 -# TODO: Enable this config when settings via python modules are no longer used -# FEATURE_ACTIVATION: -# default_threshold: 30240 # 30240 = 75% of evaluation_interval (40320) +FEATURE_ACTIVATION: + enable_usage: true + default_threshold: 30240 # 30240 = 75% of evaluation_interval (40320) diff --git a/hathor/feature_activation/settings.py b/hathor/feature_activation/settings.py index f9505db12..da4849ef6 100644 --- a/hathor/feature_activation/settings.py +++ b/hathor/feature_activation/settings.py @@ -41,6 +41,9 @@ class Settings(BaseModel, validate_all=True): # neither their values changed, to preserve history. features: dict[Feature, Criteria] = {} + # Boolean indicating whether feature activation can be used. + enable_usage: bool = False + @validator('default_threshold') def _validate_default_threshold(cls, default_threshold: int, values: dict[str, Any]) -> int: """Validates that the default_threshold is not greater than the evaluation_interval.""" diff --git a/hathor/manager.py b/hathor/manager.py index c87e18d04..4f6284217 100644 --- a/hathor/manager.py +++ b/hathor/manager.py @@ -40,6 +40,8 @@ SpendingVoidedError, ) from hathor.feature_activation.bit_signaling_service import BitSignalingService +from hathor.feature_activation.feature import Feature +from hathor.feature_activation.feature_service import FeatureService from hathor.mining import BlockTemplate, BlockTemplates from hathor.p2p.manager import ConnectionsManager from hathor.p2p.peer_discovery import PeerDiscovery @@ -98,6 +100,7 @@ def __init__(self, tx_storage: TransactionStorage, p2p_manager: ConnectionsManager, event_manager: EventManager, + feature_service: FeatureService, bit_signaling_service: BitSignalingService, network: str, hostname: Optional[str] = None, @@ -172,6 +175,7 @@ def __init__(self, self._event_manager.save_event_queue_state(enable_event_queue) self._enable_event_queue = enable_event_queue + self._feature_service = feature_service self._bit_signaling_service = bit_signaling_service self.consensus_algorithm = consensus_algorithm @@ -1096,6 +1100,33 @@ def tx_fully_validated(self, tx: BaseTransaction, *, quiet: bool) -> None: self.wallet.on_new_tx(tx) self.log_new_object(tx, 'new {}', quiet=quiet) + self._log_feature_states(tx) + + def _log_feature_states(self, vertex: BaseTransaction) -> None: + """Log features states for a block. Used as part of the Feature Activation Phased Testing.""" + if not settings.FEATURE_ACTIVATION.enable_usage or not isinstance(vertex, Block): + return + + feature_descriptions = self._feature_service.get_bits_description(block=vertex) + state_by_feature = { + feature.value: description.state.value + for feature, description in feature_descriptions.items() + } + + self.log.info( + 'New block accepted with feature activation states', + block_height=vertex.get_height(), + features_states=state_by_feature + ) + + features = [Feature.NOP_FEATURE_1, Feature.NOP_FEATURE_2, Feature.NOP_FEATURE_3] + for feature in features: + self._log_if_feature_is_active(vertex, feature) + + def _log_if_feature_is_active(self, block: Block, feature: Feature) -> None: + """Log if a feature is ACTIVE for a block. Used as part of the Feature Activation Phased Testing.""" + if self._feature_service.is_feature_active(block=block, feature=feature): + self.log.info('Feature is ACTIVE for block', feature=feature.value, block_height=block.get_height()) def listen(self, description: str, use_ssl: Optional[bool] = None) -> None: endpoint = self.connections.listen(description, use_ssl)