diff --git a/hathor/consensus/transaction_consensus.py b/hathor/consensus/transaction_consensus.py index 2b25ad703..358503c78 100644 --- a/hathor/consensus/transaction_consensus.py +++ b/hathor/consensus/transaction_consensus.py @@ -310,7 +310,7 @@ def check_conflicts(self, tx: Transaction) -> None: # If we got here, either it was a tie or we won. # So, let's void the conflict txs. - for conflict_tx in conflict_list: + for conflict_tx in sorted(conflict_list, key=lambda x: x.timestamp, reverse=True): self.mark_as_voided(conflict_tx) if not tie_list: diff --git a/hathor/manager.py b/hathor/manager.py index ab96c59e6..362cbcfcb 100644 --- a/hathor/manager.py +++ b/hathor/manager.py @@ -914,12 +914,6 @@ def push_tx(self, tx: Transaction, allow_non_standard_script: bool = False, if not tx_from_lib.is_standard(max_output_script_size, not allow_non_standard_script): raise NonStandardTxError('Transaction is non standard.') - # Validate tx. - try: - self.verification_service.verify(tx) - except TxValidationError as e: - raise InvalidNewTransaction(str(e)) - self.propagate_tx(tx, fails_silently=False) def propagate_tx(self, tx: BaseTransaction, fails_silently: bool = True) -> bool: diff --git a/hathor/reward_lock/reward_lock.py b/hathor/reward_lock/reward_lock.py index cd088905b..446afb855 100644 --- a/hathor/reward_lock/reward_lock.py +++ b/hathor/reward_lock/reward_lock.py @@ -42,15 +42,16 @@ def get_spent_reward_locked_info(tx: 'Transaction', storage: 'VertexStorageProto """Check if any input block reward is locked, returning the locked information if any, or None if they are all unlocked.""" from hathor.transaction.transaction import RewardLockedInfo + best_height = get_minimum_best_height(storage) for blk in iter_spent_rewards(tx, storage): - needed_height = _spent_reward_needed_height(blk, storage) + needed_height = _spent_reward_needed_height(blk, best_height) if needed_height > 0: return RewardLockedInfo(blk.hash, needed_height) return None -def _spent_reward_needed_height(block: Block, storage: 'VertexStorageProtocol') -> int: - """ Returns height still needed to unlock this `block` reward: 0 means it's unlocked.""" +def get_minimum_best_height(storage: 'VertexStorageProtocol') -> int: + """Return the height of the current best block that shall be used for `min_height` verification.""" import math # omitting timestamp to get the current best block, this will usually hit the cache instead of being slow @@ -61,6 +62,11 @@ def _spent_reward_needed_height(block: Block, storage: 'VertexStorageProtocol') blk = storage.get_block(tip) best_height = min(best_height, blk.get_height()) assert isinstance(best_height, int) + return best_height + + +def _spent_reward_needed_height(block: Block, best_height: int) -> int: + """ Returns height still needed to unlock this `block` reward: 0 means it's unlocked.""" spent_height = block.get_height() spend_blocks = best_height - spent_height settings = get_global_settings() diff --git a/hathor/simulator/utils.py b/hathor/simulator/utils.py index 863bbfbdb..792380ae2 100644 --- a/hathor/simulator/utils.py +++ b/hathor/simulator/utils.py @@ -20,7 +20,7 @@ from hathor.types import Address, VertexId -def gen_new_tx(manager: HathorManager, address: str, value: int, verify: bool = True) -> Transaction: +def gen_new_tx(manager: HathorManager, address: str, value: int) -> Transaction: """ Generate and return a new transaction. @@ -28,7 +28,6 @@ def gen_new_tx(manager: HathorManager, address: str, value: int, verify: bool = manager: the HathorManager to generate the transaction for address: an address for the transaction's output value: a value for the transaction's output - verify: whether to verify the generated transaction Returns: the generated transaction. """ @@ -48,8 +47,6 @@ def gen_new_tx(manager: HathorManager, address: str, value: int, verify: bool = tx.weight = 1 tx.parents = manager.get_new_tx_parents(tx.timestamp) manager.cpu_mining_service.resolve(tx) - if verify: - manager.verification_service.verify(tx) return tx @@ -111,7 +108,6 @@ def add_new_block( if signal_bits is not None: block.signal_bits = signal_bits manager.cpu_mining_service.resolve(block) - manager.verification_service.validate_full(block) if propagate: manager.propagate_tx(block, fails_silently=False) if advance_clock: diff --git a/hathor/transaction/base_transaction.py b/hathor/transaction/base_transaction.py index 68d94ead2..56898d6f9 100644 --- a/hathor/transaction/base_transaction.py +++ b/hathor/transaction/base_transaction.py @@ -624,13 +624,14 @@ def get_metadata(self, *, force_reload: bool = False, use_storage: bool = True) # happens include generating new mining blocks and some tests height = self.calculate_height() if self.storage else None score = self.weight if self.is_genesis else 0 + min_height = 0 if self.is_genesis else None metadata = TransactionMetadata( hash=self._hash, accumulated_weight=self.weight, height=height, score=score, - min_height=0, + min_height=min_height ) self._metadata = metadata if not metadata.hash: @@ -713,8 +714,9 @@ def update_initial_metadata(self, *, save: bool = True) -> None: """ self._update_height_metadata() self._update_parents_children_metadata() - self._update_reward_lock_metadata() + self.update_reward_lock_metadata() self._update_feature_activation_bit_counts() + self._update_initial_accumulated_weight() if save: assert self.storage is not None self.storage.save_transaction(self, only_metadata=True) @@ -724,10 +726,13 @@ def _update_height_metadata(self) -> None: meta = self.get_metadata() meta.height = self.calculate_height() - def _update_reward_lock_metadata(self) -> None: + def update_reward_lock_metadata(self) -> None: """Update the txs/block min_height metadata.""" metadata = self.get_metadata() - metadata.min_height = self.calculate_min_height() + min_height = self.calculate_min_height() + if metadata.min_height is not None: + assert metadata.min_height == min_height + metadata.min_height = min_height def _update_parents_children_metadata(self) -> None: """Update the txs/block parent's children metadata.""" @@ -749,6 +754,11 @@ def _update_feature_activation_bit_counts(self) -> None: # This method lazily calculates and stores the value in metadata self.get_feature_activation_bit_counts() + def _update_initial_accumulated_weight(self) -> None: + """Update the vertex initial accumulated_weight.""" + metadata = self.get_metadata() + metadata.accumulated_weight = self.weight + def update_timestamp(self, now: int) -> None: """Update this tx's timestamp diff --git a/hathor/transaction/storage/transaction_storage.py b/hathor/transaction/storage/transaction_storage.py index f3f6bed60..9b90af63f 100644 --- a/hathor/transaction/storage/transaction_storage.py +++ b/hathor/transaction/storage/transaction_storage.py @@ -31,6 +31,7 @@ from hathor.pubsub import PubSubManager from hathor.transaction.base_transaction import BaseTransaction, TxOutput from hathor.transaction.block import Block +from hathor.transaction.exceptions import RewardLocked from hathor.transaction.storage.exceptions import ( TransactionDoesNotExist, TransactionIsNotABlock, @@ -49,6 +50,7 @@ from hathor.transaction.transaction import Transaction from hathor.transaction.transaction_metadata import TransactionMetadata from hathor.types import VertexId +from hathor.verification.transaction_verifier import TransactionVerifier cpu = get_cpu_profiler() @@ -1097,10 +1099,11 @@ def compute_transactions_that_became_invalid(self, new_best_height: int) -> list from hathor.transaction.validation_state import ValidationState to_remove: list[BaseTransaction] = [] for tx in self.iter_mempool_from_best_index(): - tx_min_height = tx.get_metadata().min_height - assert tx_min_height is not None - # We use +1 here because a tx is valid if it can be confirmed by the next block - if new_best_height + 1 < tx_min_height: + try: + TransactionVerifier.verify_reward_locked_for_height( + tx, new_best_height, assert_min_height_verification=False + ) + except RewardLocked: tx.set_validation(ValidationState.INVALID) to_remove.append(tx) return to_remove diff --git a/hathor/verification/transaction_verifier.py b/hathor/verification/transaction_verifier.py index c634ba1d8..f55e0239c 100644 --- a/hathor/verification/transaction_verifier.py +++ b/hathor/verification/transaction_verifier.py @@ -16,6 +16,7 @@ from hathor.daa import DifficultyAdjustmentAlgorithm from hathor.profiler import get_cpu_profiler from hathor.reward_lock import get_spent_reward_locked_info +from hathor.reward_lock.reward_lock import get_minimum_best_height from hathor.transaction import BaseTransaction, Transaction, TxInput from hathor.transaction.exceptions import ( ConflictingInputs, @@ -37,7 +38,6 @@ from hathor.transaction.transaction import TokenInfo from hathor.transaction.util import get_deposit_amount, get_withdraw_amount from hathor.types import TokenUid, VertexId -from hathor.util import not_none cpu = get_cpu_profiler() @@ -142,12 +142,51 @@ def verify_script(self, *, tx: Transaction, input_tx: TxInput, spent_tx: BaseTra raise InvalidInputData(e) from e def verify_reward_locked(self, tx: Transaction) -> None: - """Will raise `RewardLocked` if any reward is spent before the best block height is enough, considering only - the block rewards spent by this tx itself, and not the inherited `min_height`.""" - info = get_spent_reward_locked_info(tx, not_none(tx.storage)) + """Will raise `RewardLocked` if any reward is spent before the best block height is enough, considering both + the block rewards spent by this tx itself, and the inherited `min_height`.""" + assert tx.storage is not None + best_height = get_minimum_best_height(tx.storage) + self.verify_reward_locked_for_height(tx, best_height) + + @staticmethod + def verify_reward_locked_for_height( + tx: Transaction, + best_height: int, + *, + assert_min_height_verification: bool = True + ) -> None: + """ + Will raise `RewardLocked` if any reward is spent before the best block height is enough, considering both + the block rewards spent by this tx itself, and the inherited `min_height`. + + Args: + tx: the transaction to be verified. + best_height: the height of the best chain to be used for verification. + assert_min_height_verification: whether the inherited `min_height` verification must pass. + + Note: for verification of new transactions, `assert_min_height_verification` must be `True`. This + verification is always expected to pass for new txs, as a failure would mean one of its dependencies would + have failed too. So an `AssertionError` is raised if it fails. + + However, when txs are being re-verified for Reward Lock during a reorg, it's possible that txs may fail + their inherited `min_height` verification. So in that case `assert_min_height_verification` is `False`, + and a normal `RewardLocked` exception is raised instead. + """ + assert tx.storage is not None + info = get_spent_reward_locked_info(tx, tx.storage) if info is not None: raise RewardLocked(f'Reward {info.block_hash.hex()} still needs {info.blocks_needed} to be unlocked.') + meta = tx.get_metadata() + assert meta.min_height is not None + # We use +1 here because a tx is valid if it can be confirmed by the next block + if best_height + 1 < meta.min_height: + if assert_min_height_verification: + raise AssertionError('a new tx should never be invalid by its inherited min_height.') + raise RewardLocked( + f'Tx {tx.hash_hex} has min_height={meta.min_height}, but the best_height={best_height}.' + ) + def verify_number_of_inputs(self, tx: Transaction) -> None: """Verify number of inputs is in a valid range""" if len(tx.inputs) > self._settings.MAX_NUM_INPUTS: diff --git a/hathor/vertex_handler/vertex_handler.py b/hathor/vertex_handler/vertex_handler.py index b393d7905..01b2372fa 100644 --- a/hathor/vertex_handler/vertex_handler.py +++ b/hathor/vertex_handler/vertex_handler.py @@ -150,11 +150,13 @@ def _validate_vertex( return False if not metadata.validation.is_fully_connected(): + # TODO: Remove this from here after a refactor in metadata initialization + vertex.update_reward_lock_metadata() try: self._verification_service.validate_full(vertex, reject_locked_reward=reject_locked_reward) except HathorError as e: if not fails_silently: - raise InvalidNewTransaction('full validation failed') from e + raise InvalidNewTransaction(f'full validation failed: {str(e)}') from e self._log.warn('on_new_tx(): full validation failed', tx=vertex.hash_hex, exc_info=True) return False diff --git a/hathor/wallet/resources/send_tokens.py b/hathor/wallet/resources/send_tokens.py index 16bd97355..936faa2e9 100644 --- a/hathor/wallet/resources/send_tokens.py +++ b/hathor/wallet/resources/send_tokens.py @@ -127,6 +127,7 @@ def _render_POST_thread(self, values: dict[str, Any], request: Request) -> Union weight = self.manager.daa.minimum_tx_weight(tx) tx.weight = weight self.manager.cpu_mining_service.resolve(tx) + tx.update_reward_lock_metadata() self.manager.verification_service.verify(tx) return tx diff --git a/hathor/wallet/resources/thin_wallet/send_tokens.py b/hathor/wallet/resources/thin_wallet/send_tokens.py index 6cd6badaf..2ffb10dfc 100644 --- a/hathor/wallet/resources/thin_wallet/send_tokens.py +++ b/hathor/wallet/resources/thin_wallet/send_tokens.py @@ -270,6 +270,7 @@ def _should_stop(): if context.should_stop_mining_thread: raise CancelledError() context.tx.update_hash() + context.tx.update_reward_lock_metadata() self.manager.verification_service.verify(context.tx) return context diff --git a/tests/consensus/test_consensus.py b/tests/consensus/test_consensus.py index 5d9a9db31..797b88a1f 100644 --- a/tests/consensus/test_consensus.py +++ b/tests/consensus/test_consensus.py @@ -81,7 +81,6 @@ def test_revert_block_high_weight(self) -> None: b0 = tb0.generate_mining_block(manager.rng, storage=manager.tx_storage) b0.weight = 10 manager.cpu_mining_service.resolve(b0) - manager.verification_service.verify(b0) manager.propagate_tx(b0, fails_silently=False) b1 = add_new_block(manager, advance_clock=15) @@ -143,7 +142,6 @@ def test_dont_revert_block_low_weight(self) -> None: b0 = manager.generate_mining_block() b0.parents = [blocks[-1].hash, conflicting_tx.hash, conflicting_tx.parents[0]] manager.cpu_mining_service.resolve(b0) - manager.verification_service.verify(b0) manager.propagate_tx(b0, fails_silently=False) b1 = add_new_block(manager, advance_clock=15) @@ -199,7 +197,6 @@ def test_dont_revert_block_high_weight_transaction_verify_other(self) -> None: b0 = tb0.generate_mining_block(manager.rng, storage=manager.tx_storage) b0.weight = 10 manager.cpu_mining_service.resolve(b0) - manager.verification_service.verify(b0) manager.propagate_tx(b0, fails_silently=False) b1 = add_new_block(manager, advance_clock=15) @@ -253,7 +250,6 @@ def test_dont_revert_block_high_weight_verify_both(self) -> None: b0.parents = [b0.parents[0], conflicting_tx.hash, conflicting_tx.parents[0]] b0.weight = 10 manager.cpu_mining_service.resolve(b0) - manager.verification_service.verify(b0) manager.propagate_tx(b0, fails_silently=False) b1 = add_new_block(manager, advance_clock=15) diff --git a/tests/event/test_event_reorg.py b/tests/event/test_event_reorg.py index 97606e8e9..5c4a64b8c 100644 --- a/tests/event/test_event_reorg.py +++ b/tests/event/test_event_reorg.py @@ -36,7 +36,6 @@ def test_reorg_events(self) -> None: b0 = tb0.generate_mining_block(self.manager.rng, storage=self.manager.tx_storage, address=BURN_ADDRESS) b0.weight = 10 self.manager.cpu_mining_service.resolve(b0) - self.manager.verification_service.verify(b0) self.manager.propagate_tx(b0, fails_silently=False) self.log.debug('reorg block propagated') self.run_to_completion() diff --git a/tests/feature_activation/test_feature_simulation.py b/tests/feature_activation/test_feature_simulation.py index 53e02f1b9..91b077711 100644 --- a/tests/feature_activation/test_feature_simulation.py +++ b/tests/feature_activation/test_feature_simulation.py @@ -228,6 +228,7 @@ def test_feature(self) -> None: non_signaling_block = manager.generate_mining_block() manager.cpu_mining_service.resolve(non_signaling_block) non_signaling_block.signal_bits = 0b10 + non_signaling_block.update_reward_lock_metadata() with pytest.raises(BlockMustSignalError): manager.verification_service.verify(non_signaling_block) diff --git a/tests/p2p/test_sync.py b/tests/p2p/test_sync.py index 69bd417e4..0b23a23e3 100644 --- a/tests/p2p/test_sync.py +++ b/tests/p2p/test_sync.py @@ -44,7 +44,6 @@ def _add_new_tx(self, address: str, value: int) -> Transaction: tx.weight = 10 tx.parents = self.manager1.get_new_tx_parents() self.manager1.cpu_mining_service.resolve(tx) - self.manager1.verification_service.verify(tx) self.manager1.propagate_tx(tx) self.clock.advance(10) return tx @@ -61,7 +60,6 @@ def _add_new_transactions(self, num_txs: int) -> list[Transaction]: def _add_new_block(self, propagate: bool = True) -> Block: block: Block = self.manager1.generate_mining_block() self.assertTrue(self.manager1.cpu_mining_service.resolve(block)) - self.manager1.verification_service.verify(block) self.manager1.on_new_tx(block, propagate_to_peers=propagate) self.clock.advance(10) return block diff --git a/tests/p2p/test_sync_mempool.py b/tests/p2p/test_sync_mempool.py index dff3c27bf..27c518552 100644 --- a/tests/p2p/test_sync_mempool.py +++ b/tests/p2p/test_sync_mempool.py @@ -36,7 +36,6 @@ def _add_new_tx(self, address: str, value: int) -> Transaction: tx.weight = 10 tx.parents = self.manager1.get_new_tx_parents() self.manager1.cpu_mining_service.resolve(tx) - self.manager1.verification_service.verify(tx) self.manager1.propagate_tx(tx) self.clock.advance(10) return tx @@ -53,7 +52,6 @@ def _add_new_transactions(self, num_txs: int) -> list[Transaction]: def _add_new_block(self, propagate: bool = True) -> Block: block: Block = self.manager1.generate_mining_block() self.assertTrue(self.manager1.cpu_mining_service.resolve(block)) - self.manager1.verification_service.verify(block) self.manager1.on_new_tx(block, propagate_to_peers=propagate) self.clock.advance(10) return block diff --git a/tests/p2p/test_sync_v2.py b/tests/p2p/test_sync_v2.py index f072134c0..06e35ebdf 100644 --- a/tests/p2p/test_sync_v2.py +++ b/tests/p2p/test_sync_v2.py @@ -276,7 +276,7 @@ def test_receiving_tips_limit(self) -> None: # Custom tx generator that generates tips parents = manager1.get_new_tx_parents(manager1.tx_storage.latest_timestamp) - def custom_gen_new_tx(manager: HathorManager, _address: str, value: int, verify: bool = True) -> Transaction: + def custom_gen_new_tx(manager: HathorManager, _address: str, value: int) -> Transaction: outputs = [] # XXX: burn address guarantees that this output will not be used as input for any following transactions # XXX: reduce value to make sure we can generate more transactions, otherwise it will spend a linear random @@ -294,8 +294,6 @@ def custom_gen_new_tx(manager: HathorManager, _address: str, value: int, verify: # XXX: fixed parents is the final requirement to make all the generated new tips tx.parents = parents manager.cpu_mining_service.resolve(tx) - if verify: - manager.verification_service.verify(tx) return tx # Generate 100 tx-tips in mempool. diff --git a/tests/resources/transaction/test_mining.py b/tests/resources/transaction/test_mining.py index af97d8682..caae54ee2 100644 --- a/tests/resources/transaction/test_mining.py +++ b/tests/resources/transaction/test_mining.py @@ -38,7 +38,7 @@ def test_get_block_template_with_address(self): 'accumulated_weight': 1.0, 'score': 0, 'height': 1, - 'min_height': 0, + 'min_height': None, 'first_block': None, 'feature_activation_bit_counts': None }, @@ -71,7 +71,7 @@ def test_get_block_template_without_address(self): 'accumulated_weight': 1.0, 'score': 0, 'height': 1, - 'min_height': 0, + 'min_height': None, 'first_block': None, 'feature_activation_bit_counts': None }, diff --git a/tests/tx/test_blockchain.py b/tests/tx/test_blockchain.py index 2d03794ff..3e30adb28 100644 --- a/tests/tx/test_blockchain.py +++ b/tests/tx/test_blockchain.py @@ -114,7 +114,6 @@ def test_single_fork_not_best(self): fork_block1 = manager.generate_mining_block() fork_block1.parents = [fork_block1.parents[0]] + fork_block1.parents[:0:-1] manager.cpu_mining_service.resolve(fork_block1) - manager.verification_service.verify(fork_block1) # Mine 8 blocks in a row blocks = add_new_blocks(manager, 8, advance_clock=15) @@ -166,7 +165,6 @@ def test_single_fork_not_best(self): # This block belongs to case (iv). fork_block3 = manager.generate_mining_block(parent_block_hash=fork_block1.hash) manager.cpu_mining_service.resolve(fork_block3) - manager.verification_service.verify(fork_block3) self.assertTrue(manager.propagate_tx(fork_block3)) fork_meta3 = fork_block3.get_metadata() self.assertEqual(fork_meta3.voided_by, {fork_block3.hash}) @@ -236,7 +234,6 @@ def test_multiple_forks(self): # Propagate a block connected to the voided chain, case (iii). fork_block2 = manager.generate_mining_block(parent_block_hash=sidechain[-1].hash) manager.cpu_mining_service.resolve(fork_block2) - manager.verification_service.verify(fork_block2) self.assertTrue(manager.propagate_tx(fork_block2)) sidechain.append(fork_block2) @@ -284,7 +281,6 @@ def test_multiple_forks(self): # Propagate a block connected to the side chain, case (v). fork_block3 = manager.generate_mining_block(parent_block_hash=fork_block2.hash) manager.cpu_mining_service.resolve(fork_block3) - manager.verification_service.verify(fork_block3) self.assertTrue(manager.propagate_tx(fork_block3)) sidechain.append(fork_block3) @@ -310,7 +306,6 @@ def test_multiple_forks(self): fork_block4 = manager.generate_mining_block(parent_block_hash=sidechain3[-1].hash) fork_block4.weight = 10 manager.cpu_mining_service.resolve(fork_block4) - manager.verification_service.verify(fork_block4) self.assertTrue(manager.propagate_tx(fork_block4)) sidechain3.append(fork_block4) diff --git a/tests/tx/test_indexes.py b/tests/tx/test_indexes.py index 1a0ad0923..cfdc607be 100644 --- a/tests/tx/test_indexes.py +++ b/tests/tx/test_indexes.py @@ -265,7 +265,6 @@ def check_utxos(*args): block2.timestamp = block1.timestamp block2.weight = 1.2 self.manager.cpu_mining_service.resolve(block2) - self.manager.verification_service.validate_full(block2) self.manager.propagate_tx(block2, fails_silently=False) self.graphviz.labels[block2.hash] = 'block2' diff --git a/tests/tx/test_reward_lock.py b/tests/tx/test_reward_lock.py index c321b5beb..80f6f6e18 100644 --- a/tests/tx/test_reward_lock.py +++ b/tests/tx/test_reward_lock.py @@ -1,6 +1,7 @@ import pytest from hathor.crypto.util import get_address_from_public_key +from hathor.exception import InvalidNewTransaction from hathor.simulator.utils import add_new_blocks from hathor.transaction import Transaction, TxInput, TxOutput from hathor.transaction.exceptions import RewardLocked @@ -87,16 +88,22 @@ def test_block_with_not_enough_height(self): add_new_blocks(self.manager, self._settings.REWARD_SPEND_MIN_BLOCKS - 1, advance_clock=1) # add tx bypassing reward-lock verification - # XXX: this situation is impossible in practice, but we force it to test that when a block tries to confirms a + # XXX: this situation is impossible in practice, but we force it to test that when a block tries to confirm a # transaction before it can the RewardLocked exception is raised tx = self._spend_reward_tx(self.manager, reward_block) self.assertEqual(tx.get_metadata().min_height, unlock_height) self.assertTrue(self.manager.on_new_tx(tx, fails_silently=False, reject_locked_reward=False)) # new block will try to confirm it and fail - with self.assertRaises(RewardLocked): + with pytest.raises(InvalidNewTransaction) as e: add_new_blocks(self.manager, 1, advance_clock=1) + assert isinstance(e.value.__cause__, RewardLocked) + + # check that the last block was not added to the storage + all_blocks = [vertex for vertex in self.manager.tx_storage.get_all_transactions() if vertex.is_block] + assert len(all_blocks) == 2 * self._settings.REWARD_SPEND_MIN_BLOCKS + 1 + def test_block_with_enough_height(self): # add block with a reward we can spend reward_block, unlock_height = self._add_reward_block() @@ -159,7 +166,6 @@ def test_mempool_tx_invalid_after_reorg(self): b0 = tb0.generate_mining_block(self.manager.rng, storage=self.manager.tx_storage) b0.weight = 10 self.manager.cpu_mining_service.resolve(b0) - self.manager.verification_service.verify(b0) self.manager.propagate_tx(b0, fails_silently=False) # now the new tx should not pass verification considering the reward lock diff --git a/tests/tx/test_tips.py b/tests/tx/test_tips.py index ce2194bef..c1ae8bfad 100644 --- a/tests/tx/test_tips.py +++ b/tests/tx/test_tips.py @@ -65,7 +65,6 @@ def test_tips_winner(self): new_block = add_new_block(self.manager, propagate=False) new_block.parents = [new_block.parents[0], tx1.hash, tx3.hash] self.manager.cpu_mining_service.resolve(new_block) - self.manager.verification_service.verify(new_block) self.manager.propagate_tx(new_block, fails_silently=False) self.manager.reactor.advance(10) diff --git a/tests/tx/test_tokens.py b/tests/tx/test_tokens.py index f84158e24..0906477e1 100644 --- a/tests/tx/test_tokens.py +++ b/tests/tx/test_tokens.py @@ -3,6 +3,7 @@ import pytest from hathor.crypto.util import decode_address +from hathor.exception import InvalidNewTransaction from hathor.indexes.tokens_index import TokenUtxoInfo from hathor.transaction import Block, Transaction, TxInput, TxOutput from hathor.transaction.exceptions import BlockWithTokensError, InputOutputMismatch, InvalidToken, TransactionDataError @@ -111,6 +112,7 @@ def test_token_transfer(self): public_bytes, signature = wallet.get_input_aux_data(data_to_sign, wallet.get_private_key(self.address_b58)) tx2.inputs[0].data = P2PKH.create_input_data(public_bytes, signature) self.manager.cpu_mining_service.resolve(tx2) + tx2.update_reward_lock_metadata() self.manager.verification_service.verify(tx2) # missing tokens @@ -154,7 +156,6 @@ def test_token_mint(self): tx2.inputs[0].data = data tx2.inputs[1].data = data self.manager.cpu_mining_service.resolve(tx2) - self.manager.verification_service.verify(tx2) self.manager.propagate_tx(tx2) self.run_to_completion() @@ -261,7 +262,6 @@ def test_token_melt(self): tx2.inputs[0].data = data tx2.inputs[1].data = data self.manager.cpu_mining_service.resolve(tx2) - self.manager.verification_service.verify(tx2) self.manager.propagate_tx(tx2) self.run_to_completion() @@ -400,7 +400,6 @@ def test_token_index_with_conflict(self, mint_amount=0): tx2.inputs[1].data = data tx2.inputs[2].data = data self.manager.cpu_mining_service.resolve(tx2) - self.manager.verification_service.verify(tx2) self.manager.propagate_tx(tx2) self.run_to_completion() @@ -504,9 +503,11 @@ def update_tx(tx): def test_token_mint_zero(self): # try to mint 0 tokens - with self.assertRaises(InvalidToken): + with pytest.raises(InvalidNewTransaction) as e: create_tokens(self.manager, self.address_b58, mint_amount=0) + assert isinstance(e.value.__cause__, InvalidToken) + def test_token_struct(self): tx = create_tokens(self.manager, self.address_b58, mint_amount=500) tx2 = TokenCreationTransaction.create_from_struct(tx.get_struct()) diff --git a/tests/tx/test_tx.py b/tests/tx/test_tx.py index 9ebf999bd..349731ffd 100644 --- a/tests/tx/test_tx.py +++ b/tests/tx/test_tx.py @@ -3,8 +3,11 @@ from math import isinf, isnan from unittest.mock import patch +import pytest + from hathor.crypto.util import decode_address, get_address_from_public_key, get_private_key_from_bytes from hathor.daa import TestMode +from hathor.exception import InvalidNewTransaction from hathor.feature_activation.feature import Feature from hathor.feature_activation.feature_service import FeatureService from hathor.simulator.utils import add_new_blocks @@ -565,6 +568,7 @@ def test_regular_tx(self): _input.data = P2PKH.create_input_data(public_bytes, signature) self.manager.cpu_mining_service.resolve(tx) + tx.update_reward_lock_metadata() self.manager.verification_service.verify(tx) def test_tx_weight_too_high(self): @@ -776,9 +780,12 @@ def add_block_with_data(data: bytes = b'') -> None: add_block_with_data() add_block_with_data(b'Testing, testing 1, 2, 3...') add_block_with_data(100*b'a') - with self.assertRaises(TransactionDataError): + + with pytest.raises(InvalidNewTransaction) as e: add_block_with_data(101*b'a') + assert isinstance(e.value.__cause__, TransactionDataError) + def test_output_serialization(self): from hathor.transaction.base_transaction import ( _MAX_OUTPUT_VALUE_32, diff --git a/tests/tx/test_tx_storage.py b/tests/tx/test_tx_storage.py index e12880cd8..de377cb9b 100644 --- a/tests/tx/test_tx_storage.py +++ b/tests/tx/test_tx_storage.py @@ -62,6 +62,7 @@ def setUp(self): self.block = Block(timestamp=previous_timestamp + 1, weight=12, outputs=[output], parents=block_parents, nonce=100781, storage=self.tx_storage) self.manager.cpu_mining_service.resolve(self.block) + self.block.update_reward_lock_metadata() self.manager.verification_service.verify(self.block) self.block.get_metadata().validation = ValidationState.FULL @@ -514,7 +515,6 @@ def _add_new_block(self, parents=None): block.parents = parents block.weight = 10 self.assertTrue(self.manager.cpu_mining_service.resolve(block)) - self.manager.verification_service.verify(block) self.manager.propagate_tx(block, fails_silently=False) self.reactor.advance(5) return block diff --git a/tests/tx/test_verification.py b/tests/tx/test_verification.py index e966f40e2..b3414d0b6 100644 --- a/tests/tx/test_verification.py +++ b/tests/tx/test_verification.py @@ -43,7 +43,7 @@ def setUp(self) -> None: self.verifiers = self.manager.verification_service.verifiers def _get_valid_block(self) -> Block: - return Block( + block = Block( hash=b'some_hash', storage=self.manager.tx_storage, weight=1, @@ -54,9 +54,11 @@ def _get_valid_block(self) -> Block: self._settings.GENESIS_TX2_HASH ] ) + block.update_reward_lock_metadata() + return block def _get_valid_merge_mined_block(self) -> MergeMinedBlock: - return MergeMinedBlock( + block = MergeMinedBlock( hash=b'some_hash', storage=self.manager.tx_storage, weight=1, @@ -68,6 +70,8 @@ def _get_valid_merge_mined_block(self) -> MergeMinedBlock: self._settings.GENESIS_TX2_HASH ], ) + block.update_reward_lock_metadata() + return block def _get_valid_tx(self) -> Transaction: genesis_private_key = get_genesis_key() @@ -91,6 +95,7 @@ def _get_valid_tx(self) -> Transaction: self._settings.GENESIS_TX2_HASH, ] ) + tx.update_reward_lock_metadata() data_to_sign = tx.get_sighash_all() assert self.manager.wallet @@ -102,7 +107,9 @@ def _get_valid_tx(self) -> Transaction: def _get_valid_token_creation_tx(self) -> TokenCreationTransaction: add_blocks_unlock_reward(self.manager) assert self.manager.wallet - return create_tokens(self.manager, self.manager.wallet.get_unused_address()) + tx = create_tokens(self.manager, self.manager.wallet.get_unused_address()) + tx.update_reward_lock_metadata() + return tx def test_block_verify_basic(self) -> None: block = self._get_valid_block() diff --git a/tests/utils.py b/tests/utils.py index 0e3ec7c90..c72682c4d 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -472,7 +472,6 @@ def create_tokens(manager: 'HathorManager', address_b58: Optional[str] = None, m manager.cpu_mining_service.resolve(tx) if propagate: - manager.verification_service.verify(tx) manager.propagate_tx(tx, fails_silently=False) assert isinstance(manager.reactor, Clock) manager.reactor.advance(8) diff --git a/tests/wallet/test_balance_update.py b/tests/wallet/test_balance_update.py index 01bc0e337..c4e5981e0 100644 --- a/tests/wallet/test_balance_update.py +++ b/tests/wallet/test_balance_update.py @@ -426,7 +426,6 @@ def test_tokens_balance(self): ) tx2.inputs[0].data = P2PKH.create_input_data(public_bytes, signature) self.manager.cpu_mining_service.resolve(tx2) - self.manager.verification_service.verify(tx2) self.manager.propagate_tx(tx2) self.run_to_completion() # verify balance diff --git a/tests/wallet/test_wallet.py b/tests/wallet/test_wallet.py index f962b731f..ab87d299e 100644 --- a/tests/wallet/test_wallet.py +++ b/tests/wallet/test_wallet.py @@ -206,6 +206,7 @@ def test_create_token_transaction(self): tx2.timestamp = tx.timestamp + 1 tx2.parents = self.manager.get_new_tx_parents() self.manager.cpu_mining_service.resolve(tx2) + tx2.update_reward_lock_metadata() self.manager.verification_service.verify(tx2) self.assertNotEqual(len(tx2.inputs), 0)