test: verify IPC error handling for invalid coinbase

Add a test case to interface_ipc_mining.py to verify that the IPC
server correctly handles and reports serialization errors rather than
crashing the node.

This covers the scenario where submitSolution is called with data
that cannot be deserialized, as discussed in #33341

Also introduces the assert_capnp_failed helper in ipc_util.py to
cleanly handle macOS-specific Cap'n Proto exception strings, and
refactors an existing block weight test to use it.
This commit is contained in:
Enoch Azariah
2026-03-09 11:06:55 +01:00
parent 63684d6922
commit e7a918b69a
2 changed files with 24 additions and 10 deletions

View File

@@ -7,7 +7,6 @@ import asyncio
import time
from contextlib import AsyncExitStack
from io import BytesIO
import platform
from test_framework.blocktools import NULL_OUTPOINT
from test_framework.messages import (
MAX_BLOCK_WEIGHT,
@@ -42,6 +41,7 @@ from test_framework.ipc_util import (
mining_wait_next_template,
wait_and_do,
make_mining_ctx,
assert_capnp_failed
)
# Test may be skipped and not have capnp installed
@@ -325,15 +325,7 @@ class IPCMiningTest(BitcoinTestFramework):
await mining.createNewBlock(ctx, opts)
raise AssertionError("createNewBlock unexpectedly succeeded")
except capnp.lib.capnp.KjException as e:
if e.description == "remote exception: unknown non-KJ exception of type: kj::Exception":
# macOS + REDUCE_EXPORTS bug: Cap'n Proto fails to recognize
# its own exception type and returns a generic error instead.
# https://github.com/bitcoin/bitcoin/pull/34422#discussion_r2863852691
# Assert this only occurs on Darwin until fixed.
assert_equal(platform.system(), "Darwin")
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")
assert_capnp_failed(e, "remote exception: std::exception: block_reserved_weight (0) must be at least 2000 weight units")
asyncio.run(capnp.run(async_routine()))
@@ -357,6 +349,13 @@ class IPCMiningTest(BitcoinTestFramework):
block.hashMerkleRoot = block.calc_merkle_root()
original_version = block.nVersion
self.log.debug("Submit solution that can't be deserialized")
try:
await template.submitSolution(ctx, 0, 0, 0, b"")
raise AssertionError("submitSolution unexpectedly succeeded")
except capnp.lib.capnp.KjException as e:
assert_capnp_failed(e, "remote exception: std::exception: SpanReader::read(): end of data:")
self.log.debug("Submit a block with a bad version")
block.nVersion = 0
block.solve()

View File

@@ -10,9 +10,13 @@ from dataclasses import dataclass
from io import BytesIO
from pathlib import Path
import shutil
import platform
from typing import Optional
from test_framework.messages import CBlock
from test_framework.util import (
assert_equal
)
# Test may be skipped and not have capnp installed
try:
@@ -155,3 +159,14 @@ async def make_mining_ctx(self):
self.log.debug("Create Mining proxy object")
mining = init.makeMining(ctx).result
return ctx, mining
def assert_capnp_failed(e, description_prefix):
if e.description == "remote exception: unknown non-KJ exception of type: kj::Exception":
# macOS + REDUCE_EXPORTS bug: Cap'n Proto fails to recognize
# its own exception type and returns a generic error instead.
# https://github.com/bitcoin/bitcoin/pull/34422#discussion_r2863852691
# Assert this only occurs on Darwin until fixed.
assert_equal(platform.system(), "Darwin")
else:
assert e.description.startswith(description_prefix), f"Expected description starting with '{description_prefix}', got '{e.description}'"
assert_equal(e.type, "FAILED")