Repository: Chia-Network/chia-blockchain
File: chia/consensus/block_body_validation.py (Python)

Code before:
                        and amount == spend.coin_amount
                    )
                    self.additions_since_fork[child_coin_id] = ForkAdd(
                        child_coin, block.height, timestamp, hint, False, same_as_parent
                    )
        self.include_reward_coins(block)

    def include_block(
        self,
        additions: list[tuple[Coin, Optional[bytes]]],
        removals: list[Coin],
        block: FullBlock,
        header_hash: bytes32,
    ) -> None:
        self.update_fork_peak(block, header_hash)
        if block.foliage_transaction_block is not None:
            timestamp = block.foliage_transaction_block.timestamp
            spent_coins: dict[bytes32, Coin] = {}
            for spend in removals:
                spend_id = bytes32(spend.name())
                spent_coins[spend_id] = spend
                self.removals_since_fork[spend_id] = ForkRem(bytes32(spend.puzzle_hash), block.height)
            for coin, hint in additions:
                same_as_parent = (
                    coin.amount % 2 == 1
                    and coin.parent_coin_info in spent_coins
                    and coin.puzzle_hash == spent_coins[coin.parent_coin_info].puzzle_hash
                    and coin.amount == spent_coins[coin.parent_coin_info].amount
                )
                self.additions_since_fork[coin.name()] = ForkAdd(
                    coin, block.height, timestamp, hint, False, same_as_parent
                )
        self.include_reward_coins(block)

    def rollback(self, header_hash: bytes32, height: int) -> None:
        assert height <= self.peak_height
        self.peak_height = height
        self.peak_hash = header_hash
        self.additions_since_fork = {k: v for k, v in self.additions_since_fork.items() if v.confirmed_height <= height}
        self.removals_since_fork = {k: v for k, v in self.removals_since_fork.items() if v.height <= height}


def validate_block_merkle_roots(
    block_additions_root: bytes32,
    block_removals_root: bytes32,
    tx_additions: list[tuple[Coin, bytes32]],
    tx_removals: list[bytes32],
) -> Optional[Err]:
    # Create addition Merkle set
    puzzlehash_coins_map: dict[bytes32, list[bytes32]] = {}


Review comment (suggestion):
this looks suspicious. Is there ever a case where `coin.parent_coin_info` is *not* in `spent_coins`?
In case `coin.parent_coin_info` isn't, I can't imagine this expression being correct.
If it's always expected to be in there, it should be an `assert`.

Code after:
        removals: list[Coin],
        block: FullBlock,
        header_hash: bytes32,
    ) -> None:
        self.update_fork_peak(block, header_hash)
        if block.foliage_transaction_block is not None:
            timestamp = block.foliage_transaction_block.timestamp
            spent_coins: dict[bytes32, Coin] = {}
            for spend in removals:
                spend_id = bytes32(spend.name())
                spent_coins[spend_id] = spend
                self.removals_since_fork[spend_id] = ForkRem(bytes32(spend.puzzle_hash), block.height)
            for coin, hint in additions:
                parent = spent_coins.get(coin.parent_coin_info)
                assert parent is not None
                same_as_parent = coin.puzzle_hash == parent.puzzle_hash and coin.amount == parent.amount
                self.additions_since_fork[coin.name()] = ForkAdd(
                    coin, block.height, timestamp, hint=hint, is_coinbase=False, same_as_parent=same_as_parent
                )
        self.include_reward_coins(block)

    def rollback(self, header_hash: bytes32, height: int) -> None:
        assert height <= self.peak_height
        self.peak_height = height
        self.peak_hash = header_hash
        self.additions_since_fork = {k: v for k, v in self.additions_since_fork.items() if v.confirmed_height <= height}
        self.removals_since_fork = {k: v for k, v in self.removals_since_fork.items() if v.height <= height}


def validate_block_merkle_roots(
    block_additions_root: bytes32,
    block_removals_root: bytes32,
    tx_additions: list[tuple[Coin, bytes32]],
    tx_removals: list[bytes32],
) -> Optional[Err]:
    # Create addition Merkle set
    puzzlehash_coins_map: dict[bytes32, list[bytes32]] = {}

    for coin, coin_name in tx_additions:
        if coin.puzzle_hash in puzzlehash_coins_map:
            puzzlehash_coins_map[coin.puzzle_hash].append(coin_name)
        else:
            puzzlehash_coins_map[coin.puzzle_hash] = [coin_name]

    # Addition Merkle set contains puzzlehash and hash of all coins with that puzzlehash
    additions_merkle_items: list[bytes32] = []
    for puzzle, coin_ids in puzzlehash_coins_map.items():
        additions_merkle_items.append(puzzle)
        additions_merkle_items.append(hash_coin_ids(coin_ids))

    additions_root = bytes32(compute_merkle_set_root(additions_merkle_items))