What changed, and why it matters
This commit only changes test code. It tightens an existing unit test so it actually exercises the cache read path, and improves comments and docstrings. No production code was modified, so it cannot introduce or fix a security vulnerability in the application itself.
No security action needed. Treat as a normal test-quality improvement.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff modifies tests/test_psbt_parser.py. It updates a class docstring, rewrites two assert messages as comments, and strengthens test_get_cosigners_identical_with_and_without_cache to call _get_cosigners a third time against a populated cache so the read path is verified. No PSBTParser implementation code is changed.
Changed components
tests/test_psbt_parser.pyInspect captured patch +22 / −13
### tests/test_psbt_parser.py
@@ -493,12 +493,8 @@ def test_parse_op_return_content():
class TestPSBTParserOptimizations:
"""
- Guard tests for the parse-time optimizations in PSBTParser.
-
- These verify the claims the speedups rely on:
- * root.my_fingerprint equals root.child(0).fingerprint
- * reusing an already-derived level yields exactly the same key as deriving it
- again.
+ Guard tests for the parse-time optimizations in PSBTParser: that each one actually
+ takes effect, and that none of them changes the result of a parse.
"""
seed = PSBTTestData.seed
@@ -637,8 +633,10 @@ def test_derive_with_cache_does_not_cross_parent_keys(self):
from_a = PSBTParser._derive_with_cache(cosigner_a_xpub, receive_index_5, cache)
from_b = PSBTParser._derive_with_cache(cosigner_b_xpub, receive_index_5, cache)
- assert len(cache) == 4, "the cache should have 4 entries: 2 levels for each cosigner's parent xpub"
+ # Two levels should have been added for each cosigner
+ assert len(cache) == 4
+ # The resulting derived child keys should be different
assert from_a.key.sec() != from_b.key.sec()
# The result derived with the cache must be identical to deriving from the xpub
@@ -648,20 +646,31 @@ def test_derive_with_cache_does_not_cross_parent_keys(self):
def test_get_cosigners_identical_with_and_without_cache(self):
- """The cache is transparent to callers: _get_cosigners returns the same cosigner
- list whether or not a cache is threaded in."""
+ """
+ The cache is transparent to callers: _get_cosigners returns the same cosigner
+ list whether it derives every level itself or reads them back out of the cache.
+ """
psbt = PSBT.parse(a2b_base64(PSBTTestData.MULTISIG_NATIVE_SEGWIT_1_INPUT))
inp = psbt.inputs[0]
pubkeys = list(inp.bip32_derivations.keys())
+ # No cache at all; every level is derived directly
uncached = PSBTParser._get_cosigners(pubkeys, inp.bip32_derivations, psbt.xpubs, None)
+ # An empty cache still has to derive every level, but now stores each one
child_key_derivation_cache = {}
- cached = PSBTParser._get_cosigners(
- pubkeys, inp.bip32_derivations, psbt.xpubs, child_key_derivation_cache)
+ populating_the_cache = PSBTParser._get_cosigners(pubkeys, inp.bip32_derivations, psbt.xpubs, child_key_derivation_cache)
+
+ # 3 cosigners x 2 levels each
+ assert len(child_key_derivation_cache) == 6
+
+ # The same call against the now-populated cache reads those levels back instead
+ # of deriving them. Each level sits below a different cosigner's xpub, so a cache
+ # that confused parents would return the wrong cosigner here.
+ reading_from_the_cache = PSBTParser._get_cosigners(pubkeys, inp.bip32_derivations, psbt.xpubs, child_key_derivation_cache)
- assert cached == uncached
- assert len(child_key_derivation_cache) > 0, "the cache was never populated"
+ assert populating_the_cache == uncached
+ assert reading_from_the_cache == uncached
def test_cache_does_not_change_parse_output(self):Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.