def update_voided_info(self, tx: Transaction) -> None: """ This method should be called only once when the transactions is added to the DAG. """ assert tx.hash is not None assert tx.storage is not None voided_by: Set[bytes] = set() # Union of voided_by of parents for parent in tx.get_parents(): parent_meta = parent.get_metadata() if parent_meta.voided_by: voided_by.update(parent_meta.voided_by) # Union of voided_by of inputs for txin in tx.inputs: spent_tx = tx.storage.get_transaction(txin.tx_id) spent_meta = spent_tx.get_metadata() if spent_meta.voided_by: voided_by.update(spent_meta.voided_by) # Update accumulated weight of the transactions voiding us. assert tx.hash not in voided_by for h in voided_by: tx2 = tx.storage.get_transaction(h) tx2_meta = tx2.get_metadata() tx2_meta.accumulated_weight = sum_weights( tx2_meta.accumulated_weight, tx.weight) assert tx2.storage is not None tx2.storage.save_transaction(tx2, only_metadata=True) # Then, we add ourselves. meta = tx.get_metadata() assert not meta.voided_by or meta.voided_by == {tx.hash} assert meta.accumulated_weight == tx.weight if meta.conflict_with: voided_by.add(tx.hash) if voided_by: meta.voided_by = voided_by.copy() tx.storage.save_transaction(tx, only_metadata=True) tx.storage._del_from_cache(tx) # XXX: accessing private method # Check conflicts of the transactions voiding us. for h in voided_by: if h == tx.hash: continue conflict_tx = tx.storage.get_transaction(h) if not conflict_tx.is_block: assert isinstance(conflict_tx, Transaction) self.check_conflicts(conflict_tx) # Finally, check our conflicts. meta = tx.get_metadata() if meta.voided_by == {tx.hash}: self.check_conflicts(tx)
def update_voided_info(self, tx: Transaction) -> None: """ This method should be called only once when the transactions is added to the DAG. """ assert tx.hash is not None assert tx.storage is not None voided_by: Set[bytes] = set() # Union of voided_by of parents for parent in tx.get_parents(): parent_meta = parent.get_metadata() if parent_meta.voided_by: voided_by.update( self.consensus.filter_out_soft_voided_entries( parent, parent_meta.voided_by)) assert settings.SOFT_VOIDED_ID not in voided_by assert not (self.soft_voided_tx_ids & voided_by) # Union of voided_by of inputs for txin in tx.inputs: spent_tx = tx.storage.get_transaction(txin.tx_id) spent_meta = spent_tx.get_metadata() if spent_meta.voided_by: voided_by.update(spent_meta.voided_by) voided_by.discard(settings.SOFT_VOIDED_ID) assert settings.SOFT_VOIDED_ID not in voided_by # Update accumulated weight of the transactions voiding us. assert tx.hash not in voided_by for h in voided_by: if h == settings.SOFT_VOIDED_ID: continue tx2 = tx.storage.get_transaction(h) tx2_meta = tx2.get_metadata() tx2_meta.accumulated_weight = sum_weights( tx2_meta.accumulated_weight, tx.weight) assert tx2.storage is not None tx2.storage.save_transaction(tx2, only_metadata=True) # Then, we add ourselves. meta = tx.get_metadata() assert not meta.voided_by or meta.voided_by == {tx.hash} assert meta.accumulated_weight == tx.weight if tx.hash in self.soft_voided_tx_ids: voided_by.add(settings.SOFT_VOIDED_ID) voided_by.add(tx.hash) if meta.conflict_with: voided_by.add(tx.hash) # We must save before marking conflicts as voided because # the conflicting tx might affect this tx's voided_by metadata. if voided_by: meta.voided_by = voided_by.copy() tx.storage.save_transaction(tx, only_metadata=True) tx.storage.del_from_indexes(tx) # Check conflicts of the transactions voiding us. for h in voided_by: if h == settings.SOFT_VOIDED_ID: continue if h == tx.hash: continue tx2 = tx.storage.get_transaction(h) if not tx2.is_block: assert isinstance(tx2, Transaction) self.check_conflicts(tx2) # Mark voided conflicts as voided. for h in meta.conflict_with or []: conflict_tx = cast(Transaction, tx.storage.get_transaction(h)) conflict_tx_meta = conflict_tx.get_metadata() if conflict_tx_meta.voided_by: self.mark_as_voided(conflict_tx) # Finally, check our conflicts. meta = tx.get_metadata() if meta.voided_by == {tx.hash}: self.check_conflicts(tx) # Assert the final state is valid. self.assert_valid_consensus(tx)