Skip to content

refactor(verification): move block-only verification methods [part 3/5] #798

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

Merged
merged 1 commit into from
Oct 24, 2023
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
54 changes: 1 addition & 53 deletions hathor/transaction/block.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,12 @@
from struct import pack
from typing import TYPE_CHECKING, Any, Optional

from hathor import daa
from hathor.checkpoint import Checkpoint
from hathor.feature_activation.feature import Feature
from hathor.feature_activation.model.feature_state import FeatureState
from hathor.profiler import get_cpu_profiler
from hathor.transaction import BaseTransaction, TxOutput, TxVersion
from hathor.transaction.exceptions import (
BlockWithInputs,
BlockWithTokensError,
CheckpointError,
InvalidBlockReward,
RewardLocked,
TransactionDataError,
WeightError,
)
from hathor.transaction.exceptions import BlockWithTokensError, CheckpointError
from hathor.transaction.util import VerboseCallback, int_to_bytes, unpack, unpack_len
from hathor.util import not_none
from hathor.utils.int import get_bit_list
Expand Down Expand Up @@ -337,55 +328,12 @@ def verify_checkpoint(self, checkpoints: list[Checkpoint]) -> None:
# TODO: check whether self is a parent of any checkpoint-valid block, this is left for a future PR
pass

def verify_weight(self) -> None:
"""Validate minimum block difficulty."""
block_weight = daa.calculate_block_difficulty(self)
if self.weight < block_weight - self._settings.WEIGHT_TOL:
raise WeightError(f'Invalid new block {self.hash_hex}: weight ({self.weight}) is '
f'smaller than the minimum weight ({block_weight})')

def verify_height(self) -> None:
"""Validate that the block height is enough to confirm all transactions being confirmed."""
meta = self.get_metadata()
assert meta.height is not None
assert meta.min_height is not None
if meta.height < meta.min_height:
raise RewardLocked(f'Block needs {meta.min_height} height but has {meta.height}')

def verify_reward(self) -> None:
"""Validate reward amount."""
parent_block = self.get_block_parent()
tokens_issued_per_block = daa.get_tokens_issued_per_block(parent_block.get_height() + 1)
if self.sum_outputs != tokens_issued_per_block:
raise InvalidBlockReward(
f'Invalid number of issued tokens tag=invalid_issued_tokens tx.hash={self.hash_hex} '
f'issued={self.sum_outputs} allowed={tokens_issued_per_block}'
)

def verify_no_inputs(self) -> None:
inputs = getattr(self, 'inputs', None)
if inputs:
raise BlockWithInputs('number of inputs {}'.format(len(inputs)))

def verify_outputs(self) -> None:
super().verify_outputs()
for output in self.outputs:
if output.get_token_index() > 0:
raise BlockWithTokensError('in output: {}'.format(output.to_human_readable()))

def verify_data(self) -> None:
if len(self.data) > self._settings.BLOCK_DATA_MAX_SIZE:
raise TransactionDataError('block data has {} bytes'.format(len(self.data)))

def verify_without_storage(self) -> None:
""" Run all verifications that do not need a storage.
"""
self.verify_pow()
self.verify_no_inputs()
self.verify_outputs()
self.verify_data()
self.verify_sigops_output()

def get_base_hash(self) -> bytes:
from hathor.merged_mining.bitcoin import sha256d_hash
return sha256d_hash(self.get_header_without_nonce())
Expand Down
10 changes: 0 additions & 10 deletions hathor/transaction/merge_mined_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,3 @@ def to_json(self, decode_script: bool = False, include_metadata: bool = False) -
del json['nonce']
json['aux_pow'] = bytes(self.aux_pow).hex() if self.aux_pow else None
return json

def verify_without_storage(self) -> None:
self.verify_aux_pow()
super().verify_without_storage()

def verify_aux_pow(self) -> None:
""" Verify auxiliary proof-of-work (for merged mining).
"""
assert self.aux_pow is not None
self.aux_pow.verify(self.get_base_hash())
40 changes: 34 additions & 6 deletions hathor/verification/block_verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from hathor import daa
from hathor.profiler import get_cpu_profiler
from hathor.transaction import BaseTransaction, Block
from hathor.transaction.exceptions import (
BlockWithInputs,
InvalidBlockReward,
RewardLocked,
TransactionDataError,
WeightError,
)
from hathor.verification.vertex_verifier import VertexVerifier

cpu = get_cpu_profiler()
Expand Down Expand Up @@ -52,25 +60,45 @@ def verify(self, block: Block) -> None:
def verify_without_storage(self, block: Block) -> None:
""" Run all verifications that do not need a storage.
"""
block.verify_without_storage()
self.verify_pow(block)
self.verify_no_inputs(block)
self.verify_outputs(block)
self.verify_data(block)
self.verify_sigops_output(block)

def verify_height(self, block: Block) -> None:
"""Validate that the block height is enough to confirm all transactions being confirmed."""
block.verify_height()
meta = block.get_metadata()
assert meta.height is not None
assert meta.min_height is not None
if meta.height < meta.min_height:
raise RewardLocked(f'Block needs {meta.min_height} height but has {meta.height}')

def verify_weight(self, block: Block) -> None:
"""Validate minimum block difficulty."""
block.verify_weight()
min_block_weight = daa.calculate_block_difficulty(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})')

def verify_reward(self, block: Block) -> None:
"""Validate reward amount."""
block.verify_reward()
parent_block = block.get_block_parent()
tokens_issued_per_block = daa.get_tokens_issued_per_block(parent_block.get_height() + 1)
if block.sum_outputs != tokens_issued_per_block:
raise InvalidBlockReward(
f'Invalid number of issued tokens tag=invalid_issued_tokens tx.hash={block.hash_hex} '
f'issued={block.sum_outputs} allowed={tokens_issued_per_block}'
)

def verify_no_inputs(self, block: Block) -> None:
block.verify_no_inputs()
inputs = getattr(block, 'inputs', None)
if inputs:
raise BlockWithInputs('number of inputs {}'.format(len(inputs)))

def verify_outputs(self, block: BaseTransaction) -> None:
block.verify_outputs()

def verify_data(self, block: Block) -> None:
block.verify_data()
if len(block.data) > self._settings.BLOCK_DATA_MAX_SIZE:
raise TransactionDataError('block data has {} bytes'.format(len(block.data)))
10 changes: 8 additions & 2 deletions hathor/verification/merge_mined_block_verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,20 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from hathor.transaction import MergeMinedBlock
from hathor.transaction import Block, MergeMinedBlock
from hathor.verification.block_verifier import BlockVerifier


class MergeMinedBlockVerifier(BlockVerifier):
__slots__ = ()

def verify_without_storage(self, block: Block) -> None:
assert isinstance(block, MergeMinedBlock)
self.verify_aux_pow(block)
super().verify_without_storage(block)

def verify_aux_pow(self, block: MergeMinedBlock) -> None:
""" Verify auxiliary proof-of-work (for merged mining).
"""
block.verify_aux_pow()
assert block.aux_pow is not None
block.aux_pow.verify(block.get_base_hash())
Loading