예제 #1
0
    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)
예제 #2
0
    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)