This PR implements the first step of #33758 by adding a cache layer for node block templates.
The cache stores newly created block templates along with their configuration options. When clients request a block template, they specify an optional template age limit in MillisecondsDouble:
- If template age limit is
nullopt, we create a new template and return it without adding it to the cache. - If a matching template (block template with identical block creation options) exists in the cache and hasn’t exceeded the age limit, the cached version is returned.
- If no matching template exists or the age limit has been exceeded, a new template is generated, cached, and returned. When evicting stale templates, we assume no duplicates exist since templates are evicted as soon as their age limit is reached or exceeded.
The cache maintains a configurable maximum size (default: 10 templates). When this limit is exceeded, the oldest template is evicted after each insertion.
Cache lookup requires an exact match on ALL BlockAssembler::Options fields. While some fields (e.g., test_block_validity, print_modified_fee, coinbase_output_script) could be ignored for cache reuse, they mainly differ in tests and benchmarks. We keep the logic simple and accept cache misses in those cases.
The cache enforces at most one template per set of options. When searching for a match in getCachedTemplate(), if a template with matching options is found but has exceeded the age limit, it’s immediately evicted before returning nullptr. This ensures no duplicate options exist in the cache. A SanityCheck() method verifies this invariant in tests by checking all cached entries for duplicates.
The cache subscribes to the validation interface and clears itself when blocks are connected (to non-historical chainstate to avoid clearing during assumeutxo background validation) or disconnected from the active chain (during reorgs), preventing stale or invalid templates from being served.
GetBlockTemplate() uses double-checked locking: it first checks the cache with only m_mutex held, then if there’s a miss, acquires both cs_main and m_mutex to create the template. Before creating, it checks again since another thread might have added a template while locks were released.
This PR removes an unused node/context.h import from miner to prevent a circular dependency when exposing the block template cache to the node context.