Skip to content

Commit

Permalink
refactor: remove SimpleMemoryStorage and refactor DAA (#1026)
Browse files Browse the repository at this point in the history
  • Loading branch information
glevco authored May 8, 2024
1 parent 007eb6e commit 2ed8b14
Show file tree
Hide file tree
Showing 10 changed files with 35 additions and 133 deletions.
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

0 comments on commit 2ed8b14

Please sign in to comment.