Skip to content

Commit

Permalink
refactor(metadata): move metadata attributes to static metadata (#1014)
Browse files Browse the repository at this point in the history
  • Loading branch information
glevco authored Aug 22, 2024
1 parent 9bce2fa commit f6ddf3c
Show file tree
Hide file tree
Showing 55 changed files with 609 additions and 366 deletions.
4 changes: 2 additions & 2 deletions hathor/builder/resources_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ def create_resources(self) -> server.Site:
# mining
(b'mining', MiningResource(self.manager), root),
(b'getmininginfo', MiningInfoResource(self.manager), root),
(b'get_block_template', GetBlockTemplateResource(self.manager), root),
(b'get_block_template', GetBlockTemplateResource(self.manager, settings), root),
(b'submit_block', SubmitBlockResource(self.manager), root),
(b'tx_parents', TxParentsResource(self.manager), root),
# /thin_wallet
Expand Down Expand Up @@ -281,7 +281,7 @@ def create_resources(self) -> server.Site:
(b'balance', BalanceResource(self.manager), wallet_resource),
(b'history', HistoryResource(self.manager), wallet_resource),
(b'address', AddressResource(self.manager), wallet_resource),
(b'send_tokens', SendTokensResource(self.manager), wallet_resource),
(b'send_tokens', SendTokensResource(self.manager, settings), wallet_resource),
(b'sign_tx', SignTxResource(self.manager), wallet_resource),
(b'unlock', UnlockWalletResource(self.manager), wallet_resource),
(b'lock', LockWalletResource(self.manager), wallet_resource),
Expand Down
1 change: 1 addition & 0 deletions hathor/consensus/poa/poa_block_producer.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ def _produce_block(self, previous_block: PoaBlock) -> None:
from hathor.transaction.poa import PoaBlock
block_templates = self.manager.get_block_templates(parent_block_hash=previous_block.hash)
block = block_templates.generate_mining_block(self.manager.rng, cls=PoaBlock)
block.init_static_metadata_from_storage(self._settings, self.manager.tx_storage)
assert isinstance(block, PoaBlock)

if block.get_height() <= self.manager.tx_storage.get_height_best_block():
Expand Down
21 changes: 11 additions & 10 deletions hathor/feature_activation/feature_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ def is_signaling_mandatory_features(self, block: 'Block') -> BlockSignalingState
Return whether a block is signaling features that are mandatory, that is, any feature currently in the
MUST_SIGNAL phase.
"""
bit_counts = block.get_feature_activation_bit_counts()
height = block.get_height()
bit_counts = block.static_metadata.feature_activation_bit_counts
height = block.static_metadata.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)
Expand Down Expand Up @@ -95,7 +95,7 @@ def get_state(self, *, block: 'Block', feature: Feature) -> FeatureState:
# All blocks within the same evaluation interval have the same state, that is, the state is only defined for
# the block in each interval boundary. Therefore, we get the state of the previous boundary block or calculate
# a new state if this block is a boundary block.
height = block.get_height()
height = block.static_metadata.height
offset_to_boundary = height % self._feature_settings.evaluation_interval
offset_to_previous_boundary = offset_to_boundary or self._feature_settings.evaluation_interval
previous_boundary_height = height - offset_to_previous_boundary
Expand Down Expand Up @@ -139,7 +139,7 @@ def _calculate_new_state(
an AssertionError. Non-boundary blocks never calculate their own state, they get it from their parent block
instead.
"""
height = boundary_block.get_height()
height = boundary_block.static_metadata.height
criteria = self._feature_settings.features.get(feature)
evaluation_interval = self._feature_settings.evaluation_interval

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

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

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

if parent_block.get_height() == ancestor_height:
if parent_block.static_metadata.height == ancestor_height:
return parent_block

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

return ancestor
2 changes: 1 addition & 1 deletion hathor/feature_activation/resources/feature.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def get_block_features(self, request: Request) -> bytes:

def get_features(self) -> bytes:
best_block = self.tx_storage.get_best_block()
bit_counts = best_block.get_feature_activation_bit_counts()
bit_counts = best_block.static_metadata.feature_activation_bit_counts
features = []

for feature, criteria in self._feature_settings.features.items():
Expand Down
27 changes: 13 additions & 14 deletions hathor/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ def _initialize_components_full_verification(self) -> None:
dt = LogDuration(t2 - t1)
dcnt = cnt - cnt2
tx_rate = '?' if dt == 0 else dcnt / dt
h = max(h, tx_meta.height or 0)
h = max(h, (tx.static_metadata.height if isinstance(tx, Block) else 0))
if dt > 30:
ts_date = datetime.datetime.fromtimestamp(self.tx_storage.latest_timestamp)
if h == 0:
Expand All @@ -469,12 +469,10 @@ def _initialize_components_full_verification(self) -> None:

try:
# TODO: deal with invalid tx
tx.calculate_height()
tx._update_parents_children_metadata()

if tx.can_validate_full():
tx.update_initial_metadata()
tx.calculate_min_height()
if tx.is_genesis:
assert tx.validate_checkpoint(self.checkpoints)
assert self.verification_service.validate_full(
Expand Down Expand Up @@ -661,31 +659,32 @@ def _verify_checkpoints(self) -> None:
for checkpoint in expected_checkpoints:
# XXX: query the database from checkpoint.hash and verify what comes out
try:
tx = self.tx_storage.get_transaction(checkpoint.hash)
block = self.tx_storage.get_block(checkpoint.hash)
except TransactionDoesNotExist as e:
raise InitializationError(f'Expected checkpoint does not exist in database: {checkpoint}') from e
tx_meta = tx.get_metadata()
if tx_meta.height != checkpoint.height:
meta = block.get_metadata()
height = block.static_metadata.height
if height != checkpoint.height:
raise InitializationError(
f'Expected checkpoint of hash {tx.hash_hex} to have height {checkpoint.height}, but instead it has'
f'height {tx_meta.height}'
f'Expected checkpoint of hash {block.hash_hex} to have height {checkpoint.height},'
f'but instead it has height {height}'
)
if tx_meta.voided_by:
pretty_voided_by = list(i.hex() for i in tx_meta.voided_by)
if meta.voided_by:
pretty_voided_by = list(i.hex() for i in meta.voided_by)
raise InitializationError(
f'Expected checkpoint {checkpoint} to *NOT* be voided, but it is being voided by: '
f'{pretty_voided_by}'
)
# XXX: query the height index from checkpoint.height and check that the hash matches
tx_hash = self.tx_storage.indexes.height.get(checkpoint.height)
if tx_hash is None:
block_hash = self.tx_storage.indexes.height.get(checkpoint.height)
if block_hash is None:
raise InitializationError(
f'Expected checkpoint {checkpoint} to be found in the height index, but it was not found'
)
if tx_hash != tx.hash:
if block_hash != block.hash:
raise InitializationError(
f'Expected checkpoint {checkpoint} to be found in the height index, but it instead the block with '
f'hash {tx_hash.hex()} was found'
f'hash {block_hash.hex()} was found'
)

def get_new_tx_parents(self, timestamp: Optional[float] = None) -> list[VertexId]:
Expand Down
2 changes: 1 addition & 1 deletion hathor/mining/block_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def generate_mining_block(
block = cls(outputs=tx_outputs, parents=parents, timestamp=block_timestamp,
data=data or b'', storage=storage, weight=self.weight, signal_bits=self.signal_bits)
if include_metadata:
block._metadata = TransactionMetadata(height=self.height, score=self.score)
block._metadata = TransactionMetadata(score=self.score)
block.get_metadata(use_storage=False)
return block

Expand Down
3 changes: 2 additions & 1 deletion hathor/p2p/resources/mining_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ def render_GET(self, request):
self._settings.P2PKH_VERSION_BYTE.hex() + 'acbfb94571417423c1ed66f706730c4aea516ac5762cccb8'
)
block = self.manager.generate_mining_block(address=burn_address)
block.init_static_metadata_from_storage(self._settings, self.manager.tx_storage)

height = block.calculate_height() - 1
height = block.static_metadata.height - 1
difficulty = max(int(Weight(block.weight).to_pdiff()), 1)

parent = block.get_block_parent()
Expand Down
7 changes: 3 additions & 4 deletions hathor/p2p/resources/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,8 @@ def render_GET(self, request):

best_block_tips = []
for tip in self.manager.tx_storage.get_best_block_tips():
tx = self.manager.tx_storage.get_transaction(tip)
meta = tx.get_metadata()
best_block_tips.append({'hash': tx.hash_hex, 'height': meta.height})
block = self.manager.tx_storage.get_block(tip)
best_block_tips.append({'hash': block.hash_hex, 'height': block.static_metadata.height})

best_block = self.manager.tx_storage.get_best_block()
raw_best_blockchain = self.manager.tx_storage.get_n_height_tips(self._settings.DEFAULT_BEST_BLOCKCHAIN_BLOCKS)
Expand Down Expand Up @@ -122,7 +121,7 @@ def render_GET(self, request):
'best_block_tips': best_block_tips,
'best_block': {
'hash': best_block.hash_hex,
'height': best_block.get_metadata().height,
'height': best_block.static_metadata.height,
},
'best_blockchain': best_blockchain,
}
Expand Down
4 changes: 2 additions & 2 deletions hathor/p2p/sync_v2/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
from hathor.transaction.storage.exceptions import TransactionDoesNotExist
from hathor.transaction.vertex_parser import VertexParser
from hathor.types import VertexId
from hathor.util import collect_n, not_none
from hathor.util import collect_n

if TYPE_CHECKING:
from hathor.p2p.protocol import HathorProtocol
Expand Down Expand Up @@ -846,7 +846,7 @@ def handle_get_best_block(self, _payload: str) -> None:
assert meta.validation.is_fully_connected()
payload = BestBlockPayload(
block=best_block.hash,
height=not_none(meta.height),
height=best_block.static_metadata.height,
)
self.send_message(ProtocolMessages.BEST_BLOCK, payload.json())

Expand Down
4 changes: 2 additions & 2 deletions hathor/p2p/sync_v2/streamers.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,8 +296,8 @@ def send_next(self) -> None:
# Check if tx is confirmed by the `self.current_block` or any next block.
assert cur_metadata.first_block is not None
assert self.current_block is not None
first_block = self.tx_storage.get_transaction(cur_metadata.first_block)
if not_none(first_block.get_metadata().height) < not_none(self.current_block.get_metadata().height):
first_block = self.tx_storage.get_block(cur_metadata.first_block)
if not_none(first_block.static_metadata.height) < not_none(self.current_block.static_metadata.height):
self.log.debug('skipping tx: out of current block')
self.bfs.skip_neighbors(cur)
return
Expand Down
1 change: 1 addition & 0 deletions hathor/stratum/stratum.py
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,7 @@ def create_job_tx(self, jobid: UUID) -> BaseTransaction:
data = data[:self._settings.BLOCK_DATA_MAX_SIZE]
block = self.manager.generate_mining_block(data=data, address=self.miner_address,
merge_mined=self.merged_mining)
block.init_static_metadata_from_storage(self._settings, self.manager.tx_storage)
self.log.debug('prepared block for mining', block=block)
return block

Expand Down
Loading

0 comments on commit f6ddf3c

Please sign in to comment.