async def add_full_block(self, block: FullBlock,
                             block_record: BlockRecord) -> None:
        self.block_cache.put(block.header_hash, block)
        cursor_1 = await self.db.execute(
            "INSERT OR REPLACE INTO full_blocks VALUES(?, ?, ?, ?, ?)",
            (
                block.header_hash.hex(),
                block.height,
                int(block.is_transaction_block()),
                int(block.is_fully_compactified()),
                bytes(block),
            ),
        )

        await cursor_1.close()

        cursor_2 = await self.db.execute(
            "INSERT OR REPLACE INTO block_records VALUES(?, ?, ?, ?,?, ?, ?)",
            (
                block.header_hash.hex(),
                block.prev_header_hash.hex(),
                block.height,
                bytes(block_record),
                None if block_record.sub_epoch_summary_included is None else
                bytes(block_record.sub_epoch_summary_included),
                False,
                block.is_transaction_block(),
            ),
        )
        await cursor_2.close()
        await self.db.commit()
Exemple #2
0
    async def add_full_block(self, block: FullBlock,
                             block_record: BlockRecord) -> None:
        cached = self.block_cache.get(block.header_hash)
        if cached is not None:
            # Since write to db can fail, we remove from cache here to avoid potential inconsistency
            # Adding to cache only from reading
            self.block_cache.put(block.header_hash, None)
        cursor_1 = await self.db.execute(
            "INSERT OR REPLACE INTO full_blocks VALUES(?, ?, ?, ?, ?)",
            (
                block.header_hash.hex(),
                block.height,
                int(block.is_transaction_block()),
                int(block.is_fully_compactified()),
                bytes(block),
            ),
        )

        await cursor_1.close()

        cursor_2 = await self.db.execute(
            "INSERT OR REPLACE INTO block_records VALUES(?, ?, ?, ?,?, ?, ?)",
            (
                block.header_hash.hex(),
                block.prev_header_hash.hex(),
                block.height,
                bytes(block_record),
                None if block_record.sub_epoch_summary_included is None else
                bytes(block_record.sub_epoch_summary_included),
                False,
                block.is_transaction_block(),
            ),
        )
        await cursor_2.close()
Exemple #3
0
    async def add_full_block(
        self, header_hash: bytes32, block: FullBlock, block_record: BlockRecord, in_main_chain: bool
    ) -> None:
        self.block_cache.put(header_hash, block)

        if self.db_wrapper.db_version == 2:

            ses: Optional[bytes] = (
                None
                if block_record.sub_epoch_summary_included is None
                else bytes(block_record.sub_epoch_summary_included)
            )

            await self.db.execute(
                "INSERT OR REPLACE INTO full_blocks VALUES(?, ?, ?, ?, ?, ?, ?, ?)",
                (
                    header_hash,
                    block.prev_header_hash,
                    block.height,
                    ses,
                    int(block.is_fully_compactified()),
                    in_main_chain,  # in_main_chain
                    self.compress(block),
                    bytes(block_record),
                ),
            )

        else:
            await self.db.execute(
                "INSERT OR REPLACE INTO full_blocks VALUES(?, ?, ?, ?, ?)",
                (
                    header_hash.hex(),
                    block.height,
                    int(block.is_transaction_block()),
                    int(block.is_fully_compactified()),
                    bytes(block),
                ),
            )

            await self.db.execute(
                "INSERT OR REPLACE INTO block_records VALUES(?, ?, ?, ?,?, ?, ?)",
                (
                    header_hash.hex(),
                    block.prev_header_hash.hex(),
                    block.height,
                    bytes(block_record),
                    None
                    if block_record.sub_epoch_summary_included is None
                    else bytes(block_record.sub_epoch_summary_included),
                    False,
                    block.is_transaction_block(),
                ),
            )
Exemple #4
0
def get_block_header(block: FullBlock, tx_addition_coins: List[Coin],
                     removals_names: List[bytes32]) -> HeaderBlock:
    # Create filter
    byte_array_tx: List[bytes32] = []
    addition_coins = tx_addition_coins + list(
        block.get_included_reward_coins())
    if block.is_transaction_block():
        for coin in addition_coins:
            byte_array_tx.append(bytearray(coin.puzzle_hash))
        for name in removals_names:
            byte_array_tx.append(bytearray(name))

    bip158: PyBIP158 = PyBIP158(byte_array_tx)
    encoded_filter: bytes = bytes(bip158.GetEncoded())

    return HeaderBlock(
        block.finished_sub_slots,
        block.reward_chain_block,
        block.challenge_chain_sp_proof,
        block.challenge_chain_ip_proof,
        block.reward_chain_sp_proof,
        block.reward_chain_ip_proof,
        block.infused_challenge_chain_ip_proof,
        block.foliage,
        block.foliage_transaction_block,
        encoded_filter,
        block.transactions_info,
    )
 async def get_tx_removals_and_additions(
     self,
     block: FullBlock,
     npc_result: Optional[NPCResult] = None
 ) -> Tuple[List[bytes32], List[Coin]]:
     if block.is_transaction_block():
         if block.transactions_generator is not None:
             if npc_result is None:
                 block_generator: Optional[
                     BlockGenerator] = await self.get_block_generator(block)
                 assert block_generator is not None
                 npc_result = get_name_puzzle_conditions(
                     block_generator,
                     self.constants.MAX_BLOCK_COST_CLVM,
                     cost_per_byte=self.constants.COST_PER_BYTE,
                     safe_mode=False,
                     rust_checker=block.height >
                     self.constants.RUST_CONDITION_CHECKER,
                 )
             tx_removals, tx_additions = tx_removals_and_additions(
                 npc_result.npc_list)
             return tx_removals, tx_additions
         else:
             return [], []
     else:
         return [], []
Exemple #6
0
def run_and_get_removals_and_additions(
        block: FullBlock,
        max_cost: int,
        cost_per_byte: int,
        rust_checker: bool,
        safe_mode=False) -> Tuple[List[bytes32], List[Coin]]:
    removals: List[bytes32] = []
    additions: List[Coin] = []

    assert len(block.transactions_generator_ref_list) == 0
    if not block.is_transaction_block():
        return [], []

    if block.transactions_generator is not None:
        npc_result = get_name_puzzle_conditions(
            BlockGenerator(block.transactions_generator, []),
            max_cost,
            cost_per_byte=cost_per_byte,
            safe_mode=safe_mode,
            rust_checker=rust_checker,
        )
        # build removals list
        for npc in npc_result.npc_list:
            removals.append(npc.coin_name)
        additions.extend(additions_for_npc(npc_result.npc_list))

    rewards = block.get_included_reward_coins()
    additions.extend(rewards)
    return removals, additions
Exemple #7
0
def get_block_header(block: FullBlock, tx_addition_coins: List[Coin],
                     removals_names: List[bytes32]) -> HeaderBlock:
    # Create filter
    byte_array_tx: List[bytes32] = []
    addition_coins = tx_addition_coins + list(
        block.get_included_reward_coins())
    if block.is_transaction_block():
        for coin in addition_coins:
            # TODO: address hint error and remove ignore
            #       error: Argument 1 to "append" of "list" has incompatible type "bytearray"; expected "bytes32"
            #       [arg-type]
            byte_array_tx.append(bytearray(
                coin.puzzle_hash))  # type: ignore[arg-type]
        for name in removals_names:
            # TODO: address hint error and remove ignore
            #       error: Argument 1 to "append" of "list" has incompatible type "bytearray"; expected "bytes32"
            #       [arg-type]
            byte_array_tx.append(bytearray(name))  # type: ignore[arg-type]

    bip158: PyBIP158 = PyBIP158(byte_array_tx)
    encoded_filter: bytes = bytes(bip158.GetEncoded())

    return HeaderBlock(
        block.finished_sub_slots,
        block.reward_chain_block,
        block.challenge_chain_sp_proof,
        block.challenge_chain_ip_proof,
        block.reward_chain_sp_proof,
        block.reward_chain_ip_proof,
        block.infused_challenge_chain_ip_proof,
        block.foliage,
        block.foliage_transaction_block,
        encoded_filter,
        block.transactions_info,
    )
Exemple #8
0
 async def get_removals_and_additions(self, block: FullBlock) -> Tuple[List[bytes32], List[Coin]]:
     if block.is_transaction_block():
         if block.transactions_generator is not None:
             block_generator: Optional[BlockGenerator] = await self.get_block_generator(block)
             assert block_generator is not None
             npc_result = get_name_puzzle_conditions(block_generator, self.constants.MAX_BLOCK_COST_CLVM, False)
             removals, additions = block_removals_and_additions(block, npc_result.npc_list)
             return removals, additions
         else:
             return [], list(block.get_included_reward_coins())
     else:
         return [], []
Exemple #9
0
 async def get_tx_removals_and_additions(
     self, block: FullBlock, npc_result: Optional[NPCResult] = None
 ) -> Tuple[List[bytes32], List[Coin]]:
     if block.is_transaction_block():
         if block.transactions_generator is not None:
             if npc_result is None:
                 block_generator: Optional[BlockGenerator] = await self.get_block_generator(block)
                 assert block_generator is not None
                 npc_result = get_name_puzzle_conditions(block_generator, self.constants.MAX_BLOCK_COST_CLVM, False)
             tx_removals, tx_additions = tx_removals_and_additions(npc_result.npc_list)
             return tx_removals, tx_additions
         else:
             return [], []
     else:
         return [], []
Exemple #10
0
def get_future_reward_coins(block: FullBlock) -> Tuple[Coin, Coin]:
    pool_amount = calculate_pool_reward(block.height)
    farmer_amount = calculate_base_farmer_reward(block.height)
    if block.is_transaction_block():
        assert block.transactions_info is not None
        farmer_amount = uint64(farmer_amount + block.transactions_info.fees)
    pool_coin: Coin = create_pool_coin(
        block.height, block.foliage.foliage_block_data.pool_target.puzzle_hash,
        pool_amount, constants.GENESIS_CHALLENGE)
    farmer_coin: Coin = create_farmer_coin(
        block.height,
        block.foliage.foliage_block_data.farmer_reward_puzzle_hash,
        farmer_amount,
        constants.GENESIS_CHALLENGE,
    )
    return pool_coin, farmer_coin
Exemple #11
0
    async def new_block(self, block: FullBlock, tx_additions: List[Coin],
                        tx_removals: List[bytes32]):
        """
        Only called for blocks which are blocks (and thus have rewards and transactions)
        """
        if block.is_transaction_block() is False:
            return
        assert block.foliage_transaction_block is not None

        for coin in tx_additions:
            record: CoinRecord = CoinRecord(
                coin,
                block.height,
                uint32(0),
                False,
                False,
                block.foliage_transaction_block.timestamp,
            )
            await self._add_coin_record(record, False)

        included_reward_coins = block.get_included_reward_coins()
        if block.height == 0:
            assert len(included_reward_coins) == 0
        else:
            assert len(included_reward_coins) >= 2

        for coin in included_reward_coins:
            reward_coin_r: CoinRecord = CoinRecord(
                coin,
                block.height,
                uint32(0),
                False,
                True,
                block.foliage_transaction_block.timestamp,
            )
            await self._add_coin_record(reward_coin_r, False)

        total_amount_spent: int = 0
        for coin_name in tx_removals:
            total_amount_spent += await self._set_spent(
                coin_name, block.height)

        # Sanity check, already checked in block_body_validation
        assert sum([a.amount for a in tx_additions]) <= total_amount_spent
    async def new_block(self, block: FullBlock, additions: List[Coin],
                        removals: List[bytes32]):
        """
        Only called for blocks which are blocks (and thus have rewards and transactions)
        """
        if block.is_transaction_block() is False:
            return
        assert block.foliage_transaction_block is not None

        for coin in additions:
            record: CoinRecord = CoinRecord(
                coin,
                block.height,
                uint32(0),
                False,
                False,
                block.foliage_transaction_block.timestamp,
            )
            await self._add_coin_record(record)

        included_reward_coins = block.get_included_reward_coins()
        if block.height == 0:
            assert len(included_reward_coins) == 0
        else:
            assert len(included_reward_coins) >= 2

        for coin in included_reward_coins:
            reward_coin_r: CoinRecord = CoinRecord(
                coin,
                block.height,
                uint32(0),
                False,
                True,
                block.foliage_transaction_block.timestamp,
            )
            await self._add_coin_record(reward_coin_r)

        for coin_name in removals:
            await self._set_spent(coin_name, block.height)
Exemple #13
0
    async def receive_block(
        self,
        block: FullBlock,
        pre_validation_result: Optional[PreValidationResult] = None,
        fork_point_with_peak: Optional[uint32] = None,
        summaries_to_check: List[SubEpochSummary] = None,  # passed only on long sync
    ) -> Tuple[ReceiveBlockResult, Optional[Err], Optional[uint32]]:
        """
        This method must be called under the blockchain lock
        Adds a new block into the blockchain, if it's valid and connected to the current
        blockchain, regardless of whether it is the child of a head, or another block.
        Returns a header if block is added to head. Returns an error if the block is
        invalid. Also returns the fork height, in the case of a new peak.
        """
        genesis: bool = block.height == 0

        if self.contains_block(block.header_hash):
            return ReceiveBlockResult.ALREADY_HAVE_BLOCK, None, None

        if not self.contains_block(block.prev_header_hash) and not genesis:
            return (
                ReceiveBlockResult.DISCONNECTED_BLOCK,
                Err.INVALID_PREV_BLOCK_HASH,
                None,
            )

        if not genesis and (self.block_record(block.prev_header_hash).height + 1) != block.height:
            return ReceiveBlockResult.INVALID_BLOCK, Err.INVALID_HEIGHT, None

        npc_result: Optional[NPCResult] = None
        if pre_validation_result is None:
            if block.height == 0:
                prev_b: Optional[BlockRecord] = None
            else:
                prev_b = self.block_record(block.prev_header_hash)
            sub_slot_iters, difficulty = get_next_sub_slot_iters_and_difficulty(
                self.constants, len(block.finished_sub_slots) > 0, prev_b, self
            )

            if block.is_transaction_block():
                if block.transactions_generator is not None:
                    try:
                        block_generator: Optional[BlockGenerator] = await self.get_block_generator(block)
                    except ValueError:
                        return ReceiveBlockResult.INVALID_BLOCK, Err.GENERATOR_REF_HAS_NO_GENERATOR, None
                    assert block_generator is not None and block.transactions_info is not None
                    npc_result = get_name_puzzle_conditions(
                        block_generator, min(self.constants.MAX_BLOCK_COST_CLVM, block.transactions_info.cost), False
                    )
                    removals, tx_additions = tx_removals_and_additions(npc_result.npc_list)
                else:
                    removals, tx_additions = [], []
                header_block = get_block_header(block, tx_additions, removals)
            else:
                npc_result = None
                header_block = get_block_header(block, [], [])

            required_iters, error = validate_finished_header_block(
                self.constants,
                self,
                header_block,
                False,
                difficulty,
                sub_slot_iters,
            )

            if error is not None:
                return ReceiveBlockResult.INVALID_BLOCK, error.code, None
        else:
            npc_result = pre_validation_result.npc_result
            required_iters = pre_validation_result.required_iters
            assert pre_validation_result.error is None
        assert required_iters is not None
        error_code, _ = await validate_block_body(
            self.constants,
            self,
            self.block_store,
            self.coin_store,
            self.get_peak(),
            block,
            block.height,
            npc_result,
            fork_point_with_peak,
            self.get_block_generator,
        )
        if error_code is not None:
            return ReceiveBlockResult.INVALID_BLOCK, error_code, None

        block_record = block_to_block_record(
            self.constants,
            self,
            required_iters,
            block,
            None,
        )
        # Always add the block to the database
        async with self.block_store.db_wrapper.lock:
            try:
                # Perform the DB operations to update the state, and rollback if something goes wrong
                await self.block_store.db_wrapper.begin_transaction()
                await self.block_store.add_full_block(block, block_record)
                fork_height, peak_height, records = await self._reconsider_peak(
                    block_record, genesis, fork_point_with_peak, npc_result
                )
                await self.block_store.db_wrapper.commit_transaction()

                # Then update the memory cache. It is important that this task is not cancelled and does not throw
                self.add_block_record(block_record)
                for fetched_block_record in records:
                    self.__height_to_hash[fetched_block_record.height] = fetched_block_record.header_hash
                    if fetched_block_record.sub_epoch_summary_included is not None:
                        self.__sub_epoch_summaries[
                            fetched_block_record.height
                        ] = fetched_block_record.sub_epoch_summary_included
                if peak_height is not None:
                    self._peak_height = peak_height
                self.block_store.cache_block(block)
            except BaseException:
                await self.block_store.db_wrapper.rollback_transaction()
                raise
        if fork_height is not None:
            return ReceiveBlockResult.NEW_PEAK, None, fork_height
        else:
            return ReceiveBlockResult.ADDED_AS_ORPHAN, None, None