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

Code before:
            assert (await mining.result.isInitialBlockDownload(ctx))
            blockref = await mining.result.getTip(ctx)
            assert blockref.hasResult
            assert_equal(len(blockref.result.hash), block_hash_size)
            current_block_height = self.nodes[0].getchaintips()[0]["height"]
            assert blockref.result.height == current_block_height
            self.log.debug("Mine a block")
            wait = mining.result.waitTipChanged(ctx, blockref.result.hash, )
            self.generate(self.nodes[0], 1)
            newblockref = await wait
            assert_equal(len(newblockref.result.hash), block_hash_size)
            assert_equal(newblockref.result.height, current_block_height + 1)
            self.log.debug("Wait for timeout")
            wait = mining.result.waitTipChanged(ctx, newblockref.result.hash, timeout)
            oldblockref = await wait
            assert_equal(len(newblockref.result.hash), block_hash_size)
            assert_equal(oldblockref.result.hash, newblockref.result.hash)
            assert_equal(oldblockref.result.height, newblockref.result.height)

            async with AsyncExitStack() as stack:
                self.log.debug("Create a template")
                opts = self.capnp_modules['mining'].BlockCreateOptions()
                opts.useMempool = True
                opts.blockReservedWeight = 4000
                opts.coinbaseOutputMaxAdditionalSigops = 0
                template = await stack.enter_async_context(destroying((await mining.result.createNewBlock(opts)).result, ctx))
                self.log.debug("Test some inspectors of Template")
                header = await template.getBlockHeader(ctx)
                assert_equal(len(header.result), block_header_size)
                block = await self.parse_and_deserialize_block(template, ctx)
                assert_equal(ser_uint256(block.hashPrevBlock), newblockref.result.hash)
                assert len(block.vtx) >= 1
                txfees = await template.getTxFees(ctx)
                assert_equal(len(txfees.result), 0)
                txsigops = await template.getTxSigops(ctx)
                assert_equal(len(txsigops.result), 0)
                coinbase_data = BytesIO((await template.getCoinbaseTx(ctx)).result)
                coinbase = CTransaction()
                coinbase.deserialize(coinbase_data)
                assert_equal(coinbase.vin[0].prevout.hash, 0)
                self.log.debug("Wait for a new template")
                waitoptions = self.capnp_modules['mining'].BlockWaitOptions()
                waitoptions.timeout = timeout
                waitoptions.feeThreshold = 1
                self.generate(self.nodes[0], 1)
                template2 = await stack.enter_async_context(destroying((await template.waitNext(ctx, waitoptions)).result, ctx))
                block2 = await self.parse_and_deserialize_block(template2, ctx)
                assert_equal(len(block2.vtx), 1)
                self.log.debug("Wait for another, but time out")
                template3 = await template2.waitNext(ctx, waitoptions)
                assert_equal(template3.to_dict(), {})

Review comment (bug):
In fac12f7cf9ebc0b906bbb20ae126cf7e7552fdbb _test: fix interface_ipc.py template destruction_: `await stack.enter_async_context(destroying((` is hard to read, so maybe move it into a helper:

```diff
diff --git a/test/functional/interface_ipc.py b/test/functional/interface_ipc.py
index 3d28bba136..e01c753d24 100755
--- a/test/functional/interface_ipc.py
+++ b/test/functional/interface_ipc.py
@@ -96,6 +96,14 @@ class IPCInterfaceTest(BitcoinTestFramework):
         tx.deserialize(coinbase_data)
         return tx

+    async def create_block_template(self, stack, ctx, mining, opts):
+        # Destroyed when stack exits.
+        return await stack.enter_async_context(destroying((await mining.result.createNewBlock(opts)).result, ctx))
+
+    async def wait_next_template(self, stack, ctx, template, waitoptions):
+        # Destroyed when stack exits.
+        return await stack.enter_async_context(destroying((await template.waitNext(ctx, waitoptions)).result, ctx))
+
     def run_echo_test(self):
         self.log.info("Running echo test")
         async def async_routine():
@@ -148,7 +156,7 @@ class IPCInterfaceTest(BitcoinTestFramework):
                 opts.useMempool = True
                 opts.blockReservedWeight = 4000
                 opts.coinbaseOutputMaxAdditionalSigops = 0
-                template = await stack.enter_async_context(destroying((await mining.result.createNewBlock(opts)).result, ctx))
+                template = await self.create_block_template(stack, ctx, mining, opts)
                 self.log.debug("Test some inspectors of Template")
                 header = await template.getBlockHeader(ctx)
                 assert_equal(len(header.result), block_header_size)
@@ -168,7 +176,7 @@ class IPCInterfaceTest(BitcoinTestFramework):
                 waitoptions.timeout = timeout
                 waitoptions.feeThreshold = 1
                 self.generate(self.nodes[0], 1)
-                template2 = await stack.enter_async_context(destroying((await template.waitNext(ctx, waitoptions)).result, ctx))
+                template2 = await self.wait_next_template(stack, ctx, template, waitoptions)
                 block2 = await self.parse_and_deserialize_block(template2, ctx)
                 assert_equal(len(block2.vtx), 1)
                 self.log.debug("Wait for another, but time out")
@@ -176,18 +184,18 @@ class IPCInterfaceTest(BitcoinTestFramework):
                 assert_equal(template3.to_dict(), {})
                 self.log.debug("Wait for another, get one after increase in fees in the mempool")
                 miniwallet.send_self_transfer(fee_rate=10, from_node=self.nodes[0])
-                template4 = await stack.enter_async_context(destroying((await template2.waitNext(ctx, waitoptions)).result, ctx))
+                template4 = await self.wait_next_template(stack, ctx, template2, waitoptions)
                 block3 = await self.parse_and_deserialize_block(template4, ctx)
                 assert_equal(len(block3.vtx), 2)
                 self.log.debug("Wait again, this should return the same template, since the fee threshold is zero")
                 waitoptions.feeThreshold = 0
-                template5 = await stack.enter_async_context(destroying((await template4.waitNext(ctx, waitoptions)).result, ctx))
+                template5 = await self.wait_next_template(stack, ctx, template4, waitoptions)
                 block4 = await self.parse_and_deserialize_block(template5, ctx)
                 assert_equal(len(block4.vtx), 2)
                 waitoptions.feeThreshold = 1
                 self.log.debug("Wait for another, get one after increase in fees in the mempool")
                 miniwallet.send_self_transfer(fee_rate=10, from_node=self.nodes[0])
-                template6 = await stack.enter_async_context(destroying((await template5.waitNext(ctx, waitoptions)).result, ctx))
+                template6 = await self.wait_next_template(stack, ctx, template5, waitoptions)
                 block4 = await self.parse_and_deserialize_block(template6, ctx)
                 assert_equal(len(block4.vtx), 3)
                 self.log.debug("Wait for another, but time out, since the fee threshold is set now")
```



Code after:
        coinbase_data = BytesIO((await block_template.getCoinbaseTx(ctx)).result)
        tx = CTransaction()
        tx.deserialize(coinbase_data)
        return tx

    def run_echo_test(self):
        self.log.info("Running echo test")
        async def async_routine():
            ctx, init = await self.make_capnp_init_ctx()
            self.log.debug("Create Echo proxy object")
            echo = init.makeEcho(ctx).result
            self.log.debug("Test a few invocations of echo")
            for s in ["hallo", "", "haha"]:
                result_eval = (await echo.echo(ctx, s)).result
                assert_equal(s, result_eval)
            self.log.debug("Destroy the Echo object")
            echo.destroy(ctx)
        asyncio.run(capnp.run(async_routine()))

    def run_mining_test(self):
        self.log.info("Running mining test")
        block_hash_size = 32
        block_header_size = 80
        timeout = 1000.0 # 1000 milliseconds
        miniwallet = MiniWallet(self.nodes[0])

        async def async_routine():
            ctx, init = await self.make_capnp_init_ctx()
            self.log.debug("Create Mining proxy object")
            mining = init.makeMining(ctx).result
            self.log.debug("Test simple inspectors")
            assert (await mining.isTestChain(ctx)).result
            assert not (await mining.isInitialBlockDownload(ctx)).result
            blockref = await mining.getTip(ctx)
            assert blockref.hasResult
            assert_equal(len(blockref.result.hash), block_hash_size)
            current_block_height = self.nodes[0].getchaintips()[0]["height"]
            assert blockref.result.height == current_block_height
            self.log.debug("Mine a block")
            newblockref = (await wait_and_do(
                mining.waitTipChanged(ctx, blockref.result.hash, timeout),
                lambda: self.generate(self.nodes[0], 1))).result
            assert_equal(len(newblockref.hash), block_hash_size)
            assert_equal(newblockref.height, current_block_height + 1)
            self.log.debug("Wait for timeout")
            oldblockref = (await mining.waitTipChanged(ctx, newblockref.hash, timeout)).result
            assert_equal(len(newblockref.hash), block_hash_size)
            assert_equal(oldblockref.hash, newblockref.hash)
            assert_equal(oldblockref.height, newblockref.height)

            async with AsyncExitStack() as stack: