Repository: bitcoin/bitcoin
File: test/functional/interface_ipc_mining.py (Python)

Code before:
                opts.useMempool = True
                opts.blockReservedWeight = 4000
                opts.coinbaseOutputMaxAdditionalSigops = 0
                template = await mining_create_block_template(mining, stack, ctx, opts)
                assert template is not None
                block = await mining_get_block(template, ctx)
                assert_equal(len(block.vtx), 2)

                self.log.debug("Use absurdly large reserved weight to force an empty template")
                opts.blockReservedWeight = MAX_BLOCK_WEIGHT
                empty_template = await mining_create_block_template(mining, stack, ctx, opts)
                assert empty_template is not None
                empty_block = await mining_get_block(empty_template, ctx)
                assert_equal(len(empty_block.vtx), 1)

            self.log.debug("Enforce minimum reserved weight for IPC clients too")
            opts.blockReservedWeight = 0
            try:
                await mining.createNewBlock(opts)
                raise AssertionError("createNewBlock unexpectedly succeeded")
            except capnp.lib.capnp.KjException as e:
                if e.type == "DISCONNECTED":
                    # The remote exception isn't caught currently and leads to a
                    # std::terminate call. Just detect and restart in this case.
                    # This bug is fixed with
                    # https://github.com/bitcoin-core/libmultiprocess/pull/218
                    assert_equal(e.description, "Peer disconnected.")
                    self.nodes[0].wait_until_stopped(expected_ret_code=(-11, -6, 1, 66), expected_stderr=re.compile(""))
                    self.start_node(0)
                else:
                    assert_equal(e.description, "remote exception: std::exception: block_reserved_weight (0) must be at least 2000 weight units")
                    assert_equal(e.type, "FAILED")

        asyncio.run(capnp.run(async_routine()))

    def run_coinbase_and_submission_test(self):
        """Test coinbase construction (getCoinbaseTx, getCoinbaseCommitment) and block submission (submitSolution)."""
        self.log.info("Running coinbase construction and submission test")

        async def async_routine():
            ctx, mining = await self.make_mining_ctx()

            current_block_height = self.nodes[0].getchaintips()[0]["height"]
            check_opts = self.capnp_modules['mining'].BlockCheckOptions()

            async with destroying((await mining.createNewBlock(self.default_block_create_options)).result, ctx) as template:
                block = await mining_get_block(template, ctx)
                balance = self.miniwallet.get_balance()
                coinbase = await self.build_coinbase_test(template, ctx, self.miniwallet)
                # Reduce payout for balance comparison simplicity
                coinbase.vout[0].nValue = COIN

Review comment (refactor):
In commit "ipc test: add workaround to block_reserved_weight exception test" (ff995b50cf9e1ea521f3cf546339f05d10b79a4d)

I did some more work on https://github.com/bitcoin-core/libmultiprocess/pull/218 and this comment is false. I thought that PR added code to catch uncaught exceptions from IPC server methods running on asynchronous threads, but it actually never did that. The code which does do that was added in https://github.com/bitcoin-core/libmultiprocess/pull/240 commit https://github.com/bitcoin-core/libmultiprocess/pull/240/changes/c4762c7b513abf66e3275b2bd3f2cf0cb980a8f7. That commit was only supposed to be a refactoring, but because it moved a `kj::runCatchingExceptions` call one level up the stack it fixed this problem inadvertently.

Code after:
                raise AssertionError("createNewBlock unexpectedly succeeded")
            except capnp.lib.capnp.KjException as e:
                if e.type == "DISCONNECTED":
                    # The remote exception isn't caught currently and leads to a
                    # std::terminate call. Just detect and restart in this case.
                    # This bug is fixed with
                    # https://github.com/bitcoin-core/libmultiprocess/pull/218
                    assert_equal(e.description, "Peer disconnected.")
                    self.nodes[0].wait_until_stopped(expected_ret_code=(-11, -6, 1, 66), expected_stderr=re.compile(""))
                    self.start_node(0)
                else:
                    assert_equal(e.description, "remote exception: std::exception: block_reserved_weight (0) must be at least 2000 weight units")
                    assert_equal(e.type, "FAILED")

        asyncio.run(capnp.run(async_routine()))

    def run_coinbase_and_submission_test(self):
        """Test coinbase construction (getCoinbaseTx) and block submission (submitSolution)."""
        self.log.info("Running coinbase construction and submission test")

        async def async_routine():
            ctx, mining = await self.make_mining_ctx()

            current_block_height = self.nodes[0].getchaintips()[0]["height"]
            check_opts = self.capnp_modules['mining'].BlockCheckOptions()

            async with destroying((await mining.createNewBlock(ctx, self.default_block_create_options)).result, ctx) as template:
                block = await mining_get_block(template, ctx)
                balance = self.miniwallet.get_balance()
                coinbase = await self.build_coinbase_test(template, ctx, self.miniwallet)
                # Reduce payout for balance comparison simplicity
                coinbase.vout[0].nValue = COIN
                block.vtx[0] = coinbase
                block.hashMerkleRoot = block.calc_merkle_root()
                original_version = block.nVersion

                self.log.debug("Submit a block with a bad version")
                block.nVersion = 0
                block.solve()
                check = await mining.checkBlock(ctx, block.serialize(), check_opts)
                assert_equal(check.result, False)
                assert_equal(check.reason, "bad-version(0x00000000)")
                submitted = (await template.submitSolution(ctx, block.nVersion, block.nTime, block.nNonce, coinbase.serialize())).result
                assert_equal(submitted, False)
                self.log.debug("Submit a valid block")
                block.nVersion = original_version
                block.solve()

                self.log.debug("First call checkBlock()")
                block_valid = (await mining.checkBlock(ctx, block.serialize(), check_opts)).result
                assert_equal(block_valid, True)