Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: remove SimpleMemoryStorage and refactor DAA #1026

Merged
merged 1 commit into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 21 additions & 16 deletions hathor/daa.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,17 @@

from enum import IntFlag
from math import log
from typing import TYPE_CHECKING, ClassVar, Optional
from typing import TYPE_CHECKING, Callable, ClassVar, Optional

from structlog import get_logger

from hathor.conf.settings import HathorSettings
from hathor.profiler import get_cpu_profiler
from hathor.types import VertexId
from hathor.util import iwindows, not_none
from hathor.util import iwindows

if TYPE_CHECKING:
from hathor.transaction import Block, Transaction
from hathor.transaction.storage.simple_memory_storage import SimpleMemoryStorage
from hathor.transaction.storage.vertex_storage_protocol import VertexStorageProtocol

logger = get_logger()
cpu = get_cpu_profiler()
Expand All @@ -60,35 +58,43 @@ def __init__(self, *, settings: HathorSettings, test_mode: TestMode = TestMode.D
DifficultyAdjustmentAlgorithm.singleton = self

@cpu.profiler(key=lambda _, block: 'calculate_block_difficulty!{}'.format(block.hash.hex()))
def calculate_block_difficulty(self, block: 'Block', memory_storage: 'SimpleMemoryStorage') -> float:
def calculate_block_difficulty(self, block: 'Block', parent_block_getter: Callable[['Block'], 'Block']) -> float:
""" Calculate block weight according to the ascendants of `block`, using calculate_next_weight."""
if self.TEST_MODE & TestMode.TEST_BLOCK_WEIGHT:
return 1.0

if block.is_genesis:
return self.MIN_BLOCK_WEIGHT

parent_block = memory_storage.get_parent_block(block)

return self.calculate_next_weight(parent_block, block.timestamp, memory_storage)
parent_block = parent_block_getter(block)
return self.calculate_next_weight(parent_block, block.timestamp, parent_block_getter)

def _calculate_N(self, parent_block: 'Block') -> int:
"""Calculate the N value for the `calculate_next_weight` algorithm."""
return min(2 * self._settings.BLOCK_DIFFICULTY_N_BLOCKS, parent_block.get_height() - 1)

def get_block_dependencies(self, block: 'Block') -> list[VertexId]:
def get_block_dependencies(
self,
block: 'Block',
parent_block_getter: Callable[['Block'], 'Block'],
) -> list[VertexId]:
"""Return the ids of the required blocks to call `calculate_block_difficulty` for the provided block."""
parent_block = block.get_block_parent()
parent_block = parent_block_getter(block)
N = self._calculate_N(parent_block)
ids: list[VertexId] = [not_none(parent_block.hash)]
ids: list[VertexId] = [parent_block.hash]

while len(ids) <= N + 1:
parent_block = parent_block.get_block_parent()
ids.append(not_none(parent_block.hash))
parent_block = parent_block_getter(parent_block)
ids.append(parent_block.hash)

return ids

def calculate_next_weight(self, parent_block: 'Block', timestamp: int, storage: 'VertexStorageProtocol') -> float:
def calculate_next_weight(
self,
parent_block: 'Block',
timestamp: int,
parent_block_getter: Callable[['Block'], 'Block'],
) -> float:
""" Calculate the next block weight, aka DAA/difficulty adjustment algorithm.

The algorithm used is described in [RFC 22](https://gitlab.com/HathorNetwork/rfcs/merge_requests/22).
Expand All @@ -111,8 +117,7 @@ def calculate_next_weight(self, parent_block: 'Block', timestamp: int, storage:
blocks: list['Block'] = []
while len(blocks) < N + 1:
blocks.append(root)
root = storage.get_parent_block(root)
assert root is not None
root = parent_block_getter(root)

# TODO: revise if this assertion can be safely removed
assert blocks == sorted(blocks, key=lambda tx: -tx.timestamp)
Expand Down
5 changes: 4 additions & 1 deletion hathor/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -816,7 +816,10 @@ def _make_block_template(self, parent_block: Block, parent_txs: 'ParentTxs', cur
parent_block_metadata.score,
2 * self._settings.WEIGHT_TOL
)
weight = max(self.daa.calculate_next_weight(parent_block, timestamp, self.tx_storage), min_significant_weight)
weight = max(
self.daa.calculate_next_weight(parent_block, timestamp, self.tx_storage.get_parent_block),
min_significant_weight
)
height = parent_block.get_height() + 1
parents = [parent_block.hash] + parent_txs.must_include
parents_any = parent_txs.can_include
Expand Down
2 changes: 1 addition & 1 deletion hathor/simulator/simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ def _build_vertex_verifiers(
"""
return VertexVerifiers.create(
settings=settings,
vertex_verifier=SimulatorVertexVerifier(settings=settings, daa=daa),
vertex_verifier=SimulatorVertexVerifier(settings=settings),
daa=daa,
feature_service=feature_service,
)
2 changes: 1 addition & 1 deletion hathor/stratum/stratum.py
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ def handle_submit(self, params: dict, msgid: Optional[str]) -> None:

self.log.debug('share received', block=tx, block_base=block_base.hex(), block_base_hash=block_base_hash.hex())

verifier = VertexVerifier(settings=self._settings, daa=self.manager.daa)
verifier = VertexVerifier(settings=self._settings)

try:
verifier.verify_pow(tx, override_weight=job.weight)
Expand Down
99 changes: 0 additions & 99 deletions hathor/transaction/storage/simple_memory_storage.py

This file was deleted.

9 changes: 2 additions & 7 deletions hathor/verification/block_verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@
TransactionDataError,
WeightError,
)
from hathor.transaction.storage.simple_memory_storage import SimpleMemoryStorage
from hathor.util import not_none


class BlockVerifier:
Expand All @@ -53,11 +51,8 @@ def verify_height(self, block: Block) -> None:

def verify_weight(self, block: Block) -> None:
"""Validate minimum block difficulty."""
memory_storage = SimpleMemoryStorage()
dependencies = self._daa.get_block_dependencies(block)
memory_storage.add_vertices_from_storage(not_none(block.storage), dependencies)

min_block_weight = self._daa.calculate_block_difficulty(block, memory_storage)
assert block.storage is not None
min_block_weight = self._daa.calculate_block_difficulty(block, block.storage.get_parent_block)
if block.weight < min_block_weight - self._settings.WEIGHT_TOL:
raise WeightError(f'Invalid new block {block.hash_hex}: weight ({block.weight}) is '
f'smaller than the minimum weight ({min_block_weight})')
Expand Down
8 changes: 3 additions & 5 deletions hathor/verification/vertex_verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
from typing import Optional

from hathor.conf.settings import HathorSettings
from hathor.daa import DifficultyAdjustmentAlgorithm
from hathor.transaction import BaseTransaction
from hathor.transaction.exceptions import (
DuplicatedParents,
Expand All @@ -40,11 +39,10 @@


class VertexVerifier:
__slots__ = ('_settings', '_daa')
__slots__ = ('_settings',)

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

def verify_parents(self, vertex: BaseTransaction) -> None:
"""All parents must exist and their timestamps must be smaller than ours.
Expand Down Expand Up @@ -158,7 +156,7 @@ def verify_outputs(self, vertex: BaseTransaction) -> None:
))

def verify_number_of_outputs(self, vertex: BaseTransaction) -> None:
"""Verify number of outputs does not exceeds the limit"""
"""Verify number of outputs does not exceed the limit"""
if len(vertex.outputs) > self._settings.MAX_NUM_OUTPUTS:
raise TooManyOutputs('Maximum number of outputs exceeded')

Expand Down
2 changes: 1 addition & 1 deletion hathor/verification/vertex_verifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def create_defaults(
Create a VertexVerifiers instance using the default verifier for each vertex type,
from all required dependencies.
"""
vertex_verifier = VertexVerifier(settings=settings, daa=daa)
vertex_verifier = VertexVerifier(settings=settings)

return cls.create(
settings=settings,
Expand Down
2 changes: 1 addition & 1 deletion tests/simulation/test_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def test_verify_pow(self) -> None:
# just get one of the genesis, we don't really need to create any transaction
tx = next(iter(manager1.tx_storage.get_all_genesis()))
# optional argument must be valid, it just has to not raise any exception, there's no assert for that
VertexVerifier(settings=self._settings, daa=manager1.daa).verify_pow(tx, override_weight=0.)
VertexVerifier(settings=self._settings).verify_pow(tx, override_weight=0.)

def test_one_node(self) -> None:
manager1 = self.create_peer()
Expand Down
2 changes: 1 addition & 1 deletion tests/tx/test_genesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def setUp(self):
self.storage = TransactionMemoryStorage()

def test_pow(self):
verifier = VertexVerifier(settings=self._settings, daa=self._daa)
verifier = VertexVerifier(settings=self._settings)
genesis = self.storage.get_all_genesis()
for g in genesis:
self.assertEqual(g.calculate_hash(), g.hash)
Expand Down
Loading