Skip to content
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
15 changes: 4 additions & 11 deletions hathor/feature_activation/feature_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
# limitations under the License.

from hathor.feature_activation.feature import Feature
from hathor.feature_activation.model.criteria import Criteria
from hathor.feature_activation.model.feature_description import FeatureDescription
from hathor.feature_activation.model.feature_state import FeatureState
from hathor.feature_activation.settings import Settings as FeatureSettings
Expand Down Expand Up @@ -76,7 +75,10 @@ def _calculate_new_state(
) -> FeatureState:
"""Returns the new feature state based on the new block, the criteria, and the previous state."""
height = boundary_block.get_height()
criteria = self._get_criteria(feature=feature)
criteria = self._feature_settings.features.get(feature)

if not criteria:
return FeatureState.DEFINED

assert not boundary_block.is_genesis, 'cannot calculate new state for genesis'
assert height % self._feature_settings.evaluation_interval == 0, (
Expand Down Expand Up @@ -124,15 +126,6 @@ def _calculate_new_state(

raise ValueError(f'Unknown previous state: {previous_state}')

def _get_criteria(self, *, feature: Feature) -> Criteria:
"""Get the Criteria defined for a specific Feature."""
criteria = self._feature_settings.features.get(feature)

if not criteria:
raise ValueError(f"Criteria not defined for feature '{feature}'.")

return criteria

def get_bits_description(self, *, block: Block) -> dict[Feature, FeatureDescription]:
"""Returns the criteria definition and feature state for all features at a certain block."""
return {
Expand Down
3 changes: 2 additions & 1 deletion hathor/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -823,7 +823,7 @@ def _make_block_template(self, parent_block: Block, parent_txs: 'ParentTxs', cur
def generate_mining_block(self, timestamp: Optional[int] = None,
parent_block_hash: Optional[VertexId] = None,
data: bytes = b'', address: Optional[Address] = None,
merge_mined: bool = False) -> Union[Block, MergeMinedBlock]:
merge_mined: bool = False, signal_bits: int = 0) -> Union[Block, MergeMinedBlock]:
""" Generates a block ready to be mined. The block includes new issued tokens,
parents, and the weight.

Expand All @@ -840,6 +840,7 @@ def generate_mining_block(self, timestamp: Optional[int] = None,
merge_mined=merge_mined,
address=address or None, # XXX: because we allow b'' for explicit empty output script
data=data,
signal_bits=signal_bits
)
return block

Expand Down
9 changes: 5 additions & 4 deletions hathor/mining/block_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def generate_minimaly_valid_block(self) -> BaseTransaction:
def generate_mining_block(self, rng: Random, merge_mined: bool = False, address: Optional[bytes] = None,
timestamp: Optional[int] = None, data: Optional[bytes] = None,
storage: Optional[TransactionStorage] = None, include_metadata: bool = False,
) -> Union[Block, MergeMinedBlock]:
signal_bits: int = 0) -> Union[Block, MergeMinedBlock]:
""" Generates a block by filling the template with the given options and random parents (if multiple choices).

Note that if a timestamp is given it will be coerced into the [timestamp_min, timestamp_max] range.
Expand All @@ -64,7 +64,7 @@ def generate_mining_block(self, rng: Random, merge_mined: bool = False, address:
tx_outputs = [TxOutput(self.reward, output_script)]
cls: Union[type['Block'], type['MergeMinedBlock']] = MergeMinedBlock if merge_mined else Block
block = cls(outputs=tx_outputs, parents=parents, timestamp=block_timestamp,
data=data or b'', storage=storage, weight=self.weight)
data=data or b'', storage=storage, weight=self.weight, signal_bits=signal_bits)
if include_metadata:
block._metadata = TransactionMetadata(height=self.height, score=self.score)
block.get_metadata(use_storage=False)
Expand Down Expand Up @@ -124,9 +124,10 @@ def choose_random_template(self, rng: Random) -> BlockTemplate:
def generate_mining_block(self, rng: Random, merge_mined: bool = False, address: Optional[bytes] = None,
timestamp: Optional[int] = None, data: Optional[bytes] = None,
storage: Optional[TransactionStorage] = None, include_metadata: bool = False,
) -> Union[Block, MergeMinedBlock]:
signal_bits: int = 0) -> Union[Block, MergeMinedBlock]:
""" Randomly choose a template and use that for generating a block, see BlockTemplate.generate_mining_block"""
return self.choose_random_template(rng).generate_mining_block(rng, merge_mined=merge_mined, address=address,
timestamp=timestamp, data=data,
storage=storage or self.storage,
include_metadata=include_metadata)
include_metadata=include_metadata,
signal_bits=signal_bits)
23 changes: 21 additions & 2 deletions hathor/simulator/miner/geometric_miner.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,23 @@ class GeometricMiner(AbstractMiner):
""" Simulate block mining with actually solving the block. It is supposed to be used
with Simulator class. The mining part is simulated using the geometrical distribution.
"""
def __init__(self, manager: 'HathorManager', rng: Random, *, hashpower: float):
def __init__(
self,
manager: 'HathorManager',
rng: Random,
*,
hashpower: float,
signal_bits: Optional[list[int]] = None
) -> None:
"""
:param: hashpower: Number of hashes per second
:param: signal_bits: a list of signal_bits to be used in each mined block, in order. If there are more mined
blocks than values provided, 0 is used.
"""
super().__init__(manager, rng)

self._hashpower = hashpower
self._signal_bits = signal_bits or []
Comment thread
msbrogli marked this conversation as resolved.
self._block: Optional[Block] = None
self._blocks_found: int = 0

Expand All @@ -58,6 +68,15 @@ def _on_new_tx(self, key: HathorEvents, args: 'EventArguments') -> None:
self._block = None
self._schedule_next_block()

def _generate_mining_block(self) -> 'Block':
"""Generates a block ready to be mined."""
try:
signal_bits = self._signal_bits.pop(0)
except IndexError:
signal_bits = 0

return self._manager.generate_mining_block(signal_bits=signal_bits)

def _schedule_next_block(self):
if self._block:
self._block.nonce = self._rng.getrandbits(32)
Expand All @@ -68,7 +87,7 @@ def _schedule_next_block(self):
self._block = None

if self._manager.can_start_mining():
block = self._manager.generate_mining_block()
block = self._generate_mining_block()
geometric_p = 2**(-block.weight)
trials = self._rng.geometric(geometric_p)
dt = 1.0 * trials / self._hashpower
Expand Down
5 changes: 2 additions & 3 deletions tests/feature_activation/test_feature_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -446,10 +446,9 @@ def test_get_state_from_failed(block_mocks: list[Block], tx_storage: Transaction
def test_get_state_undefined_feature(block_mocks: list[Block], service: FeatureService) -> None:
block = block_mocks[10]

with pytest.raises(ValueError) as e:
service.get_state(block=block, feature=Feature.NOP_FEATURE_1)
result = service.get_state(block=block, feature=Feature.NOP_FEATURE_1)

assert str(e.value) == f"Criteria not defined for feature '{Feature.NOP_FEATURE_1}'."
assert result == FeatureState.DEFINED


def test_get_bits_description(tx_storage: TransactionStorage) -> None:
Expand Down
Loading