test: interface_ipc.py minor fixes and cleanup

There are a few things that are incorrect or messy in the interface_ipc.py test.
This commit tries to clean them up:

- isTestChain and isInitialBlockDownload asserts were not checking the results
  of those calls, only that calls were, made because they were not checking the
  responses' .result member.

- A lot of result accesses like `template.result` `mining.result` were repeated
  unnecessarily because variables like `template` and `mining` were assigned
  response objects instead of result objects. These variables are now changed
  to point directly to results.

- Some coroutine calls were assigned to temporary `wait` before being awaited.
  This was unnecessarily confusing and would make code not run in top-down
  order.

- `to_dict` calls were being made to check if result variables were unset. This
  was inefficient and indirect because it iterates over all fields in response
  structs instead of just checking whether the result field is present. The
  to_dict calls are now replaced with more direct `_has('result')` calls.

- The `res` variables used to hold various responses did not have descriptive
  names. These are replaced with clearer names.

Co-authored-by: rkrux <rkrux.connect@gmail.com>
This commit is contained in:
Ryan Ofskyandrkrux committed 2025-12-08 21:23:40 -05:00
1 parent ded11fb04d
commit a5e61b1917
1 file changed
+41 -36
+41 -36
View File
@@ -128,27 +128,25 @@ class IPCInterfaceTest(BitcoinTestFramework):
async def async_routine(): async def async_routine():
ctx, init = await self.make_capnp_init_ctx() ctx, init = await self.make_capnp_init_ctx()
self.log.debug("Create Mining proxy object") self.log.debug("Create Mining proxy object")
mining = init.makeMining(ctx) mining = init.makeMining(ctx).result
self.log.debug("Test simple inspectors") self.log.debug("Test simple inspectors")
assert (await mining.result.isTestChain(ctx)) assert (await mining.isTestChain(ctx)).result
assert (await mining.result.isInitialBlockDownload(ctx)) assert not (await mining.isInitialBlockDownload(ctx)).result
blockref = await mining.result.getTip(ctx) blockref = await mining.getTip(ctx)
assert blockref.hasResult assert blockref.hasResult
assert_equal(len(blockref.result.hash), block_hash_size) assert_equal(len(blockref.result.hash), block_hash_size)
current_block_height = self.nodes[0].getchaintips()[0]["height"] current_block_height = self.nodes[0].getchaintips()[0]["height"]
assert blockref.result.height == current_block_height assert blockref.result.height == current_block_height
self.log.debug("Mine a block") self.log.debug("Mine a block")
wait = mining.result.waitTipChanged(ctx, blockref.result.hash, )
self.generate(self.nodes[0], 1) self.generate(self.nodes[0], 1)
newblockref = await wait newblockref = (await mining.waitTipChanged(ctx, blockref.result.hash)).result
assert_equal(len(newblockref.result.hash), block_hash_size) assert_equal(len(newblockref.hash), block_hash_size)
assert_equal(newblockref.result.height, current_block_height + 1) assert_equal(newblockref.height, current_block_height + 1)
self.log.debug("Wait for timeout") self.log.debug("Wait for timeout")
wait = mining.result.waitTipChanged(ctx, newblockref.result.hash, timeout) oldblockref = (await mining.waitTipChanged(ctx, newblockref.hash, timeout)).result
oldblockref = await wait assert_equal(len(newblockref.hash), block_hash_size)
assert_equal(len(newblockref.result.hash), block_hash_size) assert_equal(oldblockref.hash, newblockref.hash)
assert_equal(oldblockref.result.hash, newblockref.result.hash) assert_equal(oldblockref.height, newblockref.height)
assert_equal(oldblockref.result.height, newblockref.result.height)
async with AsyncExitStack() as stack: async with AsyncExitStack() as stack:
self.log.debug("Create a template") self.log.debug("Create a template")
@@ -156,12 +154,13 @@ class IPCInterfaceTest(BitcoinTestFramework):
opts.useMempool = True opts.useMempool = True
opts.blockReservedWeight = 4000 opts.blockReservedWeight = 4000
opts.coinbaseOutputMaxAdditionalSigops = 0 opts.coinbaseOutputMaxAdditionalSigops = 0
template = await create_block_template(mining.result, stack, ctx, opts) template = await create_block_template(mining, stack, ctx, opts)
self.log.debug("Test some inspectors of Template") self.log.debug("Test some inspectors of Template")
header = await template.getBlockHeader(ctx) header = (await template.getBlockHeader(ctx)).result
assert_equal(len(header.result), block_header_size) assert_equal(len(header), block_header_size)
block = await self.parse_and_deserialize_block(template, ctx) block = await self.parse_and_deserialize_block(template, ctx)
assert_equal(ser_uint256(block.hashPrevBlock), newblockref.result.hash) assert_equal(ser_uint256(block.hashPrevBlock), newblockref.hash)
assert len(block.vtx) >= 1 assert len(block.vtx) >= 1
txfees = await template.getTxFees(ctx) txfees = await template.getTxFees(ctx)
assert_equal(len(txfees.result), 0) assert_equal(len(txfees.result), 0)
@@ -171,6 +170,7 @@ class IPCInterfaceTest(BitcoinTestFramework):
coinbase = CTransaction() coinbase = CTransaction()
coinbase.deserialize(coinbase_data) coinbase.deserialize(coinbase_data)
assert_equal(coinbase.vin[0].prevout.hash, 0) assert_equal(coinbase.vin[0].prevout.hash, 0)
self.log.debug("Wait for a new template") self.log.debug("Wait for a new template")
waitoptions = self.capnp_modules['mining'].BlockWaitOptions() waitoptions = self.capnp_modules['mining'].BlockWaitOptions()
waitoptions.timeout = timeout waitoptions.timeout = timeout
@@ -179,28 +179,33 @@ class IPCInterfaceTest(BitcoinTestFramework):
template2 = await wait_next_template(template, stack, ctx, waitoptions) template2 = await wait_next_template(template, stack, ctx, waitoptions)
block2 = await self.parse_and_deserialize_block(template2, ctx) block2 = await self.parse_and_deserialize_block(template2, ctx)
assert_equal(len(block2.vtx), 1) assert_equal(len(block2.vtx), 1)
self.log.debug("Wait for another, but time out") self.log.debug("Wait for another, but time out")
template3 = await template2.waitNext(ctx, waitoptions) template3 = await template2.waitNext(ctx, waitoptions)
assert_equal(template3.to_dict(), {}) assert_equal(template3._has("result"), False)
self.log.debug("Wait for another, get one after increase in fees in the mempool") 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]) miniwallet.send_self_transfer(fee_rate=10, from_node=self.nodes[0])
template4 = await wait_next_template(template2, stack, ctx, waitoptions) template4 = await wait_next_template(template2, stack, ctx, waitoptions)
block3 = await self.parse_and_deserialize_block(template4, ctx) block3 = await self.parse_and_deserialize_block(template4, ctx)
assert_equal(len(block3.vtx), 2) assert_equal(len(block3.vtx), 2)
self.log.debug("Wait again, this should return the same template, since the fee threshold is zero") self.log.debug("Wait again, this should return the same template, since the fee threshold is zero")
waitoptions.feeThreshold = 0 waitoptions.feeThreshold = 0
template5 = await wait_next_template(template4, stack, ctx, waitoptions) template5 = await wait_next_template(template4, stack, ctx, waitoptions)
block4 = await self.parse_and_deserialize_block(template5, ctx) block4 = await self.parse_and_deserialize_block(template5, ctx)
assert_equal(len(block4.vtx), 2) assert_equal(len(block4.vtx), 2)
waitoptions.feeThreshold = 1 waitoptions.feeThreshold = 1
self.log.debug("Wait for another, get one after increase in fees in the mempool") 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]) miniwallet.send_self_transfer(fee_rate=10, from_node=self.nodes[0])
template6 = await wait_next_template(template5, stack, ctx, waitoptions) template6 = await wait_next_template(template5, stack, ctx, waitoptions)
block4 = await self.parse_and_deserialize_block(template6, ctx) block4 = await self.parse_and_deserialize_block(template6, ctx)
assert_equal(len(block4.vtx), 3) assert_equal(len(block4.vtx), 3)
self.log.debug("Wait for another, but time out, since the fee threshold is set now") self.log.debug("Wait for another, but time out, since the fee threshold is set now")
template7 = await template6.waitNext(ctx, waitoptions) template7 = await template6.waitNext(ctx, waitoptions)
assert_equal(template7.to_dict(), {}) assert_equal(template7._has("result"), False)
self.log.debug("interruptWait should abort the current wait") self.log.debug("interruptWait should abort the current wait")
wait_started = asyncio.Event() wait_started = asyncio.Event()
@@ -209,7 +214,8 @@ class IPCInterfaceTest(BitcoinTestFramework):
new_waitoptions.timeout = waitoptions.timeout * 60 # 1 minute wait new_waitoptions.timeout = waitoptions.timeout * 60 # 1 minute wait
new_waitoptions.feeThreshold = 1 new_waitoptions.feeThreshold = 1
wait_started.set() wait_started.set()
return await template6.waitNext(ctx, new_waitoptions) template7 = await template6.waitNext(ctx, new_waitoptions)
assert_equal(template7._has("result"), False)
async def interrupt_wait(): async def interrupt_wait():
await wait_started.wait() # Wait for confirmation wait started await wait_started.wait() # Wait for confirmation wait started
@@ -222,11 +228,10 @@ class IPCInterfaceTest(BitcoinTestFramework):
result = await wait_task result = await wait_task
await interrupt_task await interrupt_task
assert_equal(result.to_dict(), {})
current_block_height = self.nodes[0].getchaintips()[0]["height"] current_block_height = self.nodes[0].getchaintips()[0]["height"]
check_opts = self.capnp_modules['mining'].BlockCheckOptions() check_opts = self.capnp_modules['mining'].BlockCheckOptions()
async with destroying((await mining.result.createNewBlock(opts)).result, ctx) as template: async with destroying((await mining.createNewBlock(opts)).result, ctx) as template:
block = await self.parse_and_deserialize_block(template, ctx) block = await self.parse_and_deserialize_block(template, ctx)
coinbase = await self.parse_and_deserialize_coinbase_tx(template, ctx) coinbase = await self.parse_and_deserialize_coinbase_tx(template, ctx)
balance = miniwallet.get_balance() balance = miniwallet.get_balance()
@@ -238,26 +243,26 @@ class IPCInterfaceTest(BitcoinTestFramework):
self.log.debug("Submit a block with a bad version") self.log.debug("Submit a block with a bad version")
block.nVersion = 0 block.nVersion = 0
block.solve() block.solve()
res = await mining.result.checkBlock(block.serialize(), check_opts) check = await mining.checkBlock(block.serialize(), check_opts)
assert_equal(res.result, False) assert_equal(check.result, False)
assert_equal(res.reason, "bad-version(0x00000000)") assert_equal(check.reason, "bad-version(0x00000000)")
res = await template.submitSolution(ctx, block.nVersion, block.nTime, block.nNonce, coinbase.serialize()) submitted = (await template.submitSolution(ctx, block.nVersion, block.nTime, block.nNonce, coinbase.serialize())).result
assert_equal(res.result, False) assert_equal(submitted, False)
self.log.debug("Submit a valid block") self.log.debug("Submit a valid block")
block.nVersion = original_version block.nVersion = original_version
block.solve() block.solve()
self.log.debug("First call checkBlock()") self.log.debug("First call checkBlock()")
res = await mining.result.checkBlock(block.serialize(), check_opts) block_valid = (await mining.checkBlock(block.serialize(), check_opts)).result
assert_equal(res.result, True) assert_equal(block_valid, True)
# The remote template block will be mutated, capture the original: # The remote template block will be mutated, capture the original:
remote_block_before = await self.parse_and_deserialize_block(template, ctx) remote_block_before = await self.parse_and_deserialize_block(template, ctx)
self.log.debug("Submitted coinbase must include witness") self.log.debug("Submitted coinbase must include witness")
assert_not_equal(coinbase.serialize_without_witness().hex(), coinbase.serialize().hex()) assert_not_equal(coinbase.serialize_without_witness().hex(), coinbase.serialize().hex())
res = await template.submitSolution(ctx, block.nVersion, block.nTime, block.nNonce, coinbase.serialize_without_witness()) submitted = (await template.submitSolution(ctx, block.nVersion, block.nTime, block.nNonce, coinbase.serialize_without_witness())).result
assert_equal(res.result, False) assert_equal(submitted, False)
self.log.debug("Even a rejected submitBlock() mutates the template's block") self.log.debug("Even a rejected submitBlock() mutates the template's block")
# Can be used by clients to download and inspect the (rejected) # Can be used by clients to download and inspect the (rejected)
@@ -266,8 +271,8 @@ class IPCInterfaceTest(BitcoinTestFramework):
assert_not_equal(remote_block_before.serialize().hex(), remote_block_after.serialize().hex()) assert_not_equal(remote_block_before.serialize().hex(), remote_block_after.serialize().hex())
self.log.debug("Submit again, with the witness") self.log.debug("Submit again, with the witness")
res = await template.submitSolution(ctx, block.nVersion, block.nTime, block.nNonce, coinbase.serialize()) submitted = (await template.submitSolution(ctx, block.nVersion, block.nTime, block.nNonce, coinbase.serialize())).result
assert_equal(res.result, True) assert_equal(submitted, True)
self.log.debug("Block should propagate") self.log.debug("Block should propagate")
# Check that the IPC node actually updates its own chain # Check that the IPC node actually updates its own chain
@@ -280,9 +285,9 @@ class IPCInterfaceTest(BitcoinTestFramework):
miniwallet.rescan_utxos() miniwallet.rescan_utxos()
assert_equal(miniwallet.get_balance(), balance + 1) assert_equal(miniwallet.get_balance(), balance + 1)
self.log.debug("Check block should fail now, since it is a duplicate") self.log.debug("Check block should fail now, since it is a duplicate")
res = await mining.result.checkBlock(block.serialize(), check_opts) check = await mining.checkBlock(block.serialize(), check_opts)
assert_equal(res.result, False) assert_equal(check.result, False)
assert_equal(res.reason, "inconclusive-not-best-prevblk") assert_equal(check.reason, "inconclusive-not-best-prevblk")
asyncio.run(capnp.run(async_routine())) asyncio.run(capnp.run(async_routine()))