Skip to content

Commit bd946c3

Browse files
committed
refactor(metadata): move metadata attributes to static metadata
1 parent 079afcc commit bd946c3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+499
-265
lines changed

hathor/builder/builder.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,12 @@ def _get_or_create_verification_service(self) -> VerificationService:
532532
if self._verification_service is None:
533533
verifiers = self._get_or_create_vertex_verifiers()
534534
storage = self._get_or_create_tx_storage()
535-
self._verification_service = VerificationService(verifiers=verifiers, tx_storage=storage)
535+
settings = self._get_or_create_settings()
536+
self._verification_service = VerificationService(
537+
verifiers=verifiers,
538+
tx_storage=storage,
539+
settings=settings,
540+
)
536541

537542
return self._verification_service
538543

hathor/builder/cli_builder.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,11 @@ def create_manager(self, reactor: Reactor) -> HathorManager:
284284
daa=daa,
285285
feature_service=self.feature_service
286286
)
287-
verification_service = VerificationService(verifiers=vertex_verifiers, tx_storage=tx_storage)
287+
verification_service = VerificationService(
288+
verifiers=vertex_verifiers,
289+
tx_storage=tx_storage,
290+
settings=settings,
291+
)
288292

289293
cpu_mining_service = CpuMiningService()
290294

hathor/cli/mining.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def execute(args: Namespace) -> None:
142142
settings = get_global_settings()
143143
daa = DifficultyAdjustmentAlgorithm(settings=settings)
144144
verifiers = VertexVerifiers.create_defaults(settings=settings, daa=daa, feature_service=Mock())
145-
verification_service = VerificationService(verifiers=verifiers)
145+
verification_service = VerificationService(verifiers=verifiers, settings=settings)
146146
verification_service.verify_without_storage(block)
147147
except HathorError:
148148
print('[{}] ERROR: Block has not been pushed because it is not valid.'.format(datetime.datetime.now()))

hathor/event/model/event_data.py

+1
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ def from_event_arguments(cls, args: EventArguments) -> 'TxData':
120120
tx_extra_data_json = get_tx_extra_data(args.tx, detail_tokens=False)
121121
tx_json = tx_extra_data_json['tx']
122122
meta_json = tx_extra_data_json['meta']
123+
meta_json['height'] = meta_json.get('height', 0) # TODO: Improve event model to reflect static metadata
123124
tx_json['metadata'] = meta_json
124125
tx_json['outputs'] = [
125126
output | dict(decoded=output['decoded'] or None)

hathor/feature_activation/feature_service.py

+11-10
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ def is_signaling_mandatory_features(self, block: 'Block') -> BlockSignalingState
6060
Return whether a block is signaling features that are mandatory, that is, any feature currently in the
6161
MUST_SIGNAL phase.
6262
"""
63-
bit_counts = block.get_feature_activation_bit_counts()
64-
height = block.get_height()
63+
bit_counts = block.static_metadata.feature_activation_bit_counts
64+
height = block.static_metadata.height
6565
offset_to_boundary = height % self._feature_settings.evaluation_interval
6666
remaining_blocks = self._feature_settings.evaluation_interval - offset_to_boundary - 1
6767
descriptions = self.get_bits_description(block=block)
@@ -95,7 +95,7 @@ def get_state(self, *, block: 'Block', feature: Feature) -> FeatureState:
9595
# All blocks within the same evaluation interval have the same state, that is, the state is only defined for
9696
# the block in each interval boundary. Therefore, we get the state of the previous boundary block or calculate
9797
# a new state if this block is a boundary block.
98-
height = block.get_height()
98+
height = block.static_metadata.height
9999
offset_to_boundary = height % self._feature_settings.evaluation_interval
100100
offset_to_previous_boundary = offset_to_boundary or self._feature_settings.evaluation_interval
101101
previous_boundary_height = height - offset_to_previous_boundary
@@ -139,7 +139,7 @@ def _calculate_new_state(
139139
an AssertionError. Non-boundary blocks never calculate their own state, they get it from their parent block
140140
instead.
141141
"""
142-
height = boundary_block.get_height()
142+
height = boundary_block.static_metadata.height
143143
criteria = self._feature_settings.features.get(feature)
144144
evaluation_interval = self._feature_settings.evaluation_interval
145145

@@ -162,7 +162,7 @@ def _calculate_new_state(
162162
# Get the count for this block's parent. Since this is a boundary block, its parent count represents the
163163
# previous evaluation interval count.
164164
parent_block = boundary_block.get_block_parent()
165-
counts = parent_block.get_feature_activation_bit_counts()
165+
counts = parent_block.static_metadata.feature_activation_bit_counts
166166
count = counts[criteria.bit]
167167
threshold = criteria.get_threshold(self._feature_settings)
168168

@@ -209,8 +209,9 @@ def _get_ancestor_at_height(self, *, block: 'Block', ancestor_height: int) -> 'B
209209
Given a block, return its ancestor at a specific height.
210210
Uses the height index if the block is in the best blockchain, or search iteratively otherwise.
211211
"""
212-
assert ancestor_height < block.get_height(), (
213-
f"ancestor height must be lower than the block's height: {ancestor_height} >= {block.get_height()}"
212+
assert ancestor_height < block.static_metadata.height, (
213+
f"ancestor height must be lower than the block's height: "
214+
f"{ancestor_height} >= {block.static_metadata.height}"
214215
)
215216

216217
# It's possible that this method is called before the consensus runs for this block, therefore we do not know
@@ -219,7 +220,7 @@ def _get_ancestor_at_height(self, *, block: 'Block', ancestor_height: int) -> 'B
219220
parent_metadata = parent_block.get_metadata()
220221
assert parent_metadata.validation.is_fully_connected(), 'The parent should always be fully validated.'
221222

222-
if parent_block.get_height() == ancestor_height:
223+
if parent_block.static_metadata.height == ancestor_height:
223224
return parent_block
224225

225226
if not parent_metadata.voided_by and (ancestor := self._tx_storage.get_block_by_height(ancestor_height)):
@@ -237,11 +238,11 @@ def _get_ancestor_iteratively(self, *, block: 'Block', ancestor_height: int) ->
237238
# TODO: there are further optimizations to be done here, the latest common block height could be persisted in
238239
# metadata, so we could still use the height index if the requested height is before that height.
239240
assert ancestor_height >= 0
240-
assert block.get_height() - ancestor_height <= self._feature_settings.evaluation_interval, (
241+
assert block.static_metadata.height - ancestor_height <= self._feature_settings.evaluation_interval, (
241242
'requested ancestor is deeper than the maximum allowed'
242243
)
243244
ancestor = block
244-
while ancestor.get_height() > ancestor_height:
245+
while ancestor.static_metadata.height > ancestor_height:
245246
ancestor = ancestor.get_block_parent()
246247

247248
return ancestor

hathor/feature_activation/resources/feature.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def get_block_features(self, request: Request) -> bytes:
8989

9090
def get_features(self) -> bytes:
9191
best_block = self.tx_storage.get_best_block()
92-
bit_counts = best_block.get_feature_activation_bit_counts()
92+
bit_counts = best_block.static_metadata.feature_activation_bit_counts
9393
features = []
9494

9595
for feature, criteria in self._feature_settings.features.items():

hathor/manager.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ def _initialize_components_full_verification(self) -> None:
435435
dt = LogDuration(t2 - t1)
436436
dcnt = cnt - cnt2
437437
tx_rate = '?' if dt == 0 else dcnt / dt
438-
h = max(h, tx_meta.height or 0)
438+
h = max(h, (tx.static_metadata.height if isinstance(tx, Block) else 0))
439439
if dt > 30:
440440
ts_date = datetime.datetime.fromtimestamp(self.tx_storage.latest_timestamp)
441441
if h == 0:
@@ -455,12 +455,10 @@ def _initialize_components_full_verification(self) -> None:
455455

456456
try:
457457
# TODO: deal with invalid tx
458-
tx.calculate_height()
459458
tx._update_parents_children_metadata()
460459

461460
if tx.can_validate_full():
462461
tx.update_initial_metadata()
463-
tx.calculate_min_height()
464462
if tx.is_genesis:
465463
assert tx.validate_checkpoint(self.checkpoints)
466464
assert self.verification_service.validate_full(
@@ -560,8 +558,6 @@ def _initialize_components_new(self) -> None:
560558
self.tx_storage.pre_init()
561559
assert self.tx_storage.indexes is not None
562560

563-
self._bit_signaling_service.start()
564-
565561
started_at = int(time.time())
566562
last_started_at = self.tx_storage.get_last_started_at()
567563
if last_started_at >= started_at:
@@ -577,6 +573,8 @@ def _initialize_components_new(self) -> None:
577573
# complex code
578574
self.tx_storage.indexes._manually_initialize(self.tx_storage)
579575

576+
self._bit_signaling_service.start()
577+
580578
# Verify if all checkpoints that exist in the database are correct
581579
try:
582580
self._verify_checkpoints()

hathor/mining/block_template.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def generate_mining_block(self, rng: Random, merge_mined: bool = False, address:
6868
block = cls(outputs=tx_outputs, parents=parents, timestamp=block_timestamp,
6969
data=data or b'', storage=storage, weight=self.weight, signal_bits=self.signal_bits)
7070
if include_metadata:
71-
block._metadata = TransactionMetadata(height=self.height, score=self.score)
71+
block._metadata = TransactionMetadata(score=self.score)
7272
block.get_metadata(use_storage=False)
7373
return block
7474

hathor/p2p/resources/mining_info.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,9 @@ def render_GET(self, request):
4949
self._settings.P2PKH_VERSION_BYTE.hex() + 'acbfb94571417423c1ed66f706730c4aea516ac5762cccb8'
5050
)
5151
block = self.manager.generate_mining_block(address=burn_address)
52+
block.init_static_metadata_from_storage(self._settings, self.manager.tx_storage)
5253

53-
height = block.calculate_height() - 1
54+
height = block.static_metadata.height - 1
5455
difficulty = max(int(Weight(block.weight).to_pdiff()), 1)
5556

5657
parent = block.get_block_parent()

hathor/stratum/stratum.py

+1
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,7 @@ def create_job_tx(self, jobid: UUID) -> BaseTransaction:
672672
data = data[:self._settings.BLOCK_DATA_MAX_SIZE]
673673
block = self.manager.generate_mining_block(data=data, address=self.miner_address,
674674
merge_mined=self.merged_mining)
675+
block.init_static_metadata_from_storage(self._settings, self.manager.tx_storage)
675676
self.log.debug('prepared block for mining', block=block)
676677
return block
677678

hathor/transaction/base_transaction.py

+5-46
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,13 @@
2222
from itertools import chain
2323
from math import inf, isfinite, log
2424
from struct import error as StructError, pack
25-
from typing import TYPE_CHECKING, Any, ClassVar, Generic, Iterator, Optional, TypeAlias, TypeVar, cast
25+
from typing import TYPE_CHECKING, Any, ClassVar, Generic, Iterator, Optional, TypeAlias, TypeVar
2626

2727
from structlog import get_logger
2828

2929
from hathor.checkpoint import Checkpoint
3030
from hathor.conf.get_settings import get_global_settings
31+
from hathor.conf.settings import HathorSettings
3132
from hathor.transaction.exceptions import InvalidOutputValue, WeightError
3233
from hathor.transaction.static_metadata import VertexStaticMetadata
3334
from hathor.transaction.transaction_metadata import TransactionMetadata
@@ -272,14 +273,6 @@ def __bytes__(self) -> bytes:
272273
def __hash__(self) -> int:
273274
return hash(self.hash)
274275

275-
@abstractmethod
276-
def calculate_height(self) -> int:
277-
raise NotImplementedError
278-
279-
@abstractmethod
280-
def calculate_min_height(self) -> int:
281-
raise NotImplementedError
282-
283276
@property
284277
def hash(self) -> VertexId:
285278
assert self._hash is not None, 'Vertex hash must be initialized.'
@@ -624,19 +617,11 @@ def get_metadata(self, *, force_reload: bool = False, use_storage: bool = True)
624617
metadata = self.storage.get_metadata(self.hash)
625618
self._metadata = metadata
626619
if not metadata:
627-
# FIXME: there is code that set use_storage=False but relies on correct height being calculated
628-
# which requires the use of a storage, this is a workaround that should be fixed, places where this
629-
# happens include generating new mining blocks and some tests
630-
height = self.calculate_height() if self.storage else None
631620
score = self.weight if self.is_genesis else 0
632-
min_height = 0 if self.is_genesis else None
633-
634621
metadata = TransactionMetadata(
635622
hash=self._hash,
636623
accumulated_weight=self.weight,
637-
height=height,
638624
score=score,
639-
min_height=min_height
640625
)
641626
self._metadata = metadata
642627
if not metadata.hash:
@@ -662,8 +647,6 @@ def reset_metadata(self) -> None:
662647
self._metadata.voided_by = {self._settings.PARTIALLY_VALIDATED_ID}
663648
self._metadata._tx_ref = weakref.ref(self)
664649

665-
self._update_height_metadata()
666-
667650
self.storage.save_transaction(self, only_metadata=True)
668651

669652
def update_accumulated_weight(self, *, stop_value: float = inf, save_file: bool = True) -> TransactionMetadata:
@@ -717,28 +700,12 @@ def update_initial_metadata(self, *, save: bool = True) -> None:
717700
718701
It is called when a new transaction/block is received by HathorManager.
719702
"""
720-
self._update_height_metadata()
721703
self._update_parents_children_metadata()
722-
self.update_reward_lock_metadata()
723-
self._update_feature_activation_bit_counts()
724704
self._update_initial_accumulated_weight()
725705
if save:
726706
assert self.storage is not None
727707
self.storage.save_transaction(self, only_metadata=True)
728708

729-
def _update_height_metadata(self) -> None:
730-
"""Update the vertice height metadata."""
731-
meta = self.get_metadata()
732-
meta.height = self.calculate_height()
733-
734-
def update_reward_lock_metadata(self) -> None:
735-
"""Update the txs/block min_height metadata."""
736-
metadata = self.get_metadata()
737-
min_height = self.calculate_min_height()
738-
if metadata.min_height is not None:
739-
assert metadata.min_height == min_height
740-
metadata.min_height = min_height
741-
742709
def _update_parents_children_metadata(self) -> None:
743710
"""Update the txs/block parent's children metadata."""
744711
assert self._hash is not None
@@ -750,15 +717,6 @@ def _update_parents_children_metadata(self) -> None:
750717
metadata.children.append(self.hash)
751718
self.storage.save_transaction(parent, only_metadata=True)
752719

753-
def _update_feature_activation_bit_counts(self) -> None:
754-
"""Update the block's feature_activation_bit_counts."""
755-
if not self.is_block:
756-
return
757-
from hathor.transaction import Block
758-
assert isinstance(self, Block)
759-
# This method lazily calculates and stores the value in metadata
760-
cast(Block, self).get_feature_activation_bit_counts()
761-
762720
def _update_initial_accumulated_weight(self) -> None:
763721
"""Update the vertex initial accumulated_weight."""
764722
metadata = self.get_metadata()
@@ -901,7 +859,7 @@ def static_metadata(self) -> StaticMetadataT:
901859
return self._static_metadata
902860

903861
@abstractmethod
904-
def init_static_metadata_from_storage(self, storage: 'TransactionStorage') -> None:
862+
def init_static_metadata_from_storage(self, settings: HathorSettings, storage: 'TransactionStorage') -> None:
905863
"""Initialize this vertex's static metadata using dependencies from a storage. This can be called multiple
906864
times, provided the dependencies didn't change."""
907865
raise NotImplementedError
@@ -916,7 +874,8 @@ def set_static_metadata(self, static_metadata: StaticMetadataT | None) -> None:
916874

917875
"""
918876
Type aliases for easily working with `GenericVertex`. A `Vertex` is a superclass that includes all specific
919-
vertex subclasses, and a `BaseTransaction` is simply an alias to `Vertex` for backwards compatibility.
877+
vertex subclasses, and a `BaseTransaction` is simply an alias to `Vertex` for backwards compatibility (it can be
878+
removed in the future).
920879
"""
921880
Vertex: TypeAlias = GenericVertex[VertexStaticMetadata]
922881
BaseTransaction: TypeAlias = Vertex

0 commit comments

Comments
 (0)