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 Mar 25, 2024
1 parent 8723fc5 commit 3418cc7
Show file tree
Hide file tree
Showing 11 changed files with 41 additions and 74 deletions.
14 changes: 5 additions & 9 deletions hathor/builder/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class BuildArtifacts(NamedTuple):


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

Expand Down Expand Up @@ -491,7 +491,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 @@ -507,17 +508,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
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)
21 changes: 13 additions & 8 deletions hathor/verification/verification_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from typing_extensions import assert_never

from hathor.feature_activation.feature_service import BlockSignalingState, FeatureService
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 +27,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 +126,17 @@ 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)
self._verify_merge_mined_block(vertex, signaling_state)
case TxVersion.REGULAR_TRANSACTION:
assert type(vertex) is Transaction
self._verify_tx(vertex, reject_locked_reward=reject_locked_reward)
Expand All @@ -142,7 +147,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 +168,10 @@ 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) -> None:
self._verify_block(block, signaling_state)

@cpu.profiler(key=lambda _, tx: 'tx-verify!{}'.format(tx.hash.hex()))
def _verify_tx(
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
18 changes: 5 additions & 13 deletions tests/tx/test_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from hathor.conf.get_settings import get_global_settings
from hathor.conf.settings import HathorSettings
from hathor.feature_activation.feature import Feature
from hathor.feature_activation.feature_service import BlockIsMissingSignal, BlockIsSignaling, FeatureService
from hathor.feature_activation.feature_service import BlockIsMissingSignal, BlockIsSignaling
from hathor.transaction import Block, TransactionMetadata
from hathor.transaction.exceptions import BlockMustSignalError
from hathor.transaction.storage import TransactionMemoryStorage, TransactionStorage
Expand Down Expand Up @@ -140,24 +140,16 @@ def test_get_feature_activation_bit_value() -> None:

def test_verify_must_signal() -> None:
settings = Mock(spec_set=HathorSettings)
feature_service = Mock(spec_set=FeatureService)
feature_service.is_signaling_mandatory_features = Mock(
return_value=BlockIsMissingSignal(feature=Feature.NOP_FEATURE_1)
)
verifier = BlockVerifier(settings=settings, feature_service=feature_service, daa=Mock())
block = Block()
verifier = BlockVerifier(settings=settings, daa=Mock())

with pytest.raises(BlockMustSignalError) as e:
verifier.verify_mandatory_signaling(block)
verifier.verify_mandatory_signaling(BlockIsMissingSignal(feature=Feature.NOP_FEATURE_1))

assert str(e.value) == "Block must signal support for feature 'NOP_FEATURE_1' during MUST_SIGNAL phase."


def test_verify_must_not_signal() -> None:
settings = Mock(spec_set=HathorSettings)
feature_service = Mock(spec_set=FeatureService)
feature_service.is_signaling_mandatory_features = Mock(return_value=BlockIsSignaling())
verifier = BlockVerifier(settings=settings, feature_service=feature_service, daa=Mock())
block = Block()
verifier = BlockVerifier(settings=settings, daa=Mock())

verifier.verify_mandatory_signaling(block)
verifier.verify_mandatory_signaling(BlockIsSignaling())
2 changes: 1 addition & 1 deletion tests/tx/test_genesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class GenesisTest(unittest.TestCase):
def setUp(self):
super().setUp()
self._daa = DifficultyAdjustmentAlgorithm(settings=self._settings)
verifiers = VertexVerifiers.create_defaults(settings=self._settings, daa=self._daa, feature_service=Mock())
verifiers = VertexVerifiers.create_defaults(settings=self._settings, daa=self._daa)
self._verification_service = VerificationService(verifiers=verifiers)
self.storage = TransactionMemoryStorage()

Expand Down
6 changes: 3 additions & 3 deletions tests/tx/test_tx.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ def test_block_inputs(self):
self.manager.cpu_mining_service.resolve(block)

with self.assertRaises(BlockWithInputs):
self.manager.verification_service.verify(block)
self._verifiers.block.verify_no_inputs(block)

def test_merge_mined_no_magic(self):
from hathor.merged_mining import MAGIC_NUMBER
Expand Down Expand Up @@ -464,7 +464,7 @@ def test_block_unknown_parent(self):

self.manager.cpu_mining_service.resolve(block)
with self.assertRaises(ParentDoesNotExist):
self.manager.verification_service.verify(block)
self._verifiers.vertex.verify_parents(block)

def test_block_number_parents(self):
address = get_address_from_public_key(self.genesis_public_key)
Expand All @@ -482,7 +482,7 @@ def test_block_number_parents(self):

self.manager.cpu_mining_service.resolve(block)
with self.assertRaises(IncorrectParents):
self.manager.verification_service.verify(block)
self._verifiers.vertex.verify_parents(block)

def test_tx_inputs_out_of_range(self):
# we'll try to spend output 3 from genesis transaction, which does not exist
Expand Down
4 changes: 1 addition & 3 deletions tests/tx/test_tx_deserialization.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
from unittest.mock import Mock

from hathor.daa import DifficultyAdjustmentAlgorithm
from hathor.transaction import Block, MergeMinedBlock, Transaction, TxVersion
from hathor.transaction.token_creation_tx import TokenCreationTransaction
Expand All @@ -13,7 +11,7 @@ class _DeserializationTest(unittest.TestCase):
def setUp(self) -> None:
super().setUp()
daa = DifficultyAdjustmentAlgorithm(settings=self._settings)
verifiers = VertexVerifiers.create_defaults(settings=self._settings, daa=daa, feature_service=Mock())
verifiers = VertexVerifiers.create_defaults(settings=self._settings, daa=daa)
self._verification_service = VerificationService(verifiers=verifiers)

def test_deserialize(self):
Expand Down

0 comments on commit 3418cc7

Please sign in to comment.