lnwallet/chancloser: use valid delivery scripts in RBF closer tests - #11042
Open
ziggie1984 wants to merge 2 commits into
Open
lnwallet/chancloser: use valid delivery scripts in RBF closer tests#11042ziggie1984 wants to merge 2 commits into
ziggie1984 wants to merge 2 commits into
Conversation
TestRbfChannelActiveTransitions shadowed the package-level localAddr and
remoteAddr with twenty raw bytes each:
localAddr := lnwire.DeliveryAddress(bytes.Repeat([]byte{0x01}, 20))
remoteAddr := lnwire.DeliveryAddress(bytes.Repeat([]byte{0x02}, 20))
Neither is a well-formed delivery script -- not a witness program, and
not any other template a co-op close is willing to pay to -- so both are
rejected by validateShutdownScript.
That goes unnoticed today because validateShutdown is a chain of guards
with early returns: the thaw height is checked first, then the taproot
shutdown nonce, and only then the delivery script. Both subtests that
feed the shadowed remoteAddr into a ShutdownReceived, namely
remote_initiated_thaw_height_close_fail and
remote_initiated_taproot_no_nonce_fail, trip an earlier guard on
purpose, so the script check is never reached. The one subtest that does
drive the whole chain, remote_initiated_close_ok, was extracted into
testRemoteInitiatedCloseOkNonTap and testRemoteInitiatedCloseOkTaproot,
which sit outside the shadow and so pick up the valid package-level
scripts. Nothing currently passes for the wrong reason, since the
harness asserts on specific sentinel errors, but that holds only by
accident of where the failures happen to land.
The cost is paid by whoever touches that guard chain next. Hoisting the
delivery-script check above the thaw height check, a reasonable thing to
want, does not fail readably: the unexpected ErrInvalidShutdownScript
reaches the mock error reporter as an unmatched call and panics the
package's test binary, with a stack pointing into protofsm and the mock
plumbing rather than at the fixture that is actually wrong.
Drop the shadowing locals so those subtests use the valid package-level
P2TR scripts, and record the invariant where the scripts are declared. A
negative test's fixture should be valid in every dimension except the
one under test, or it is not isolating what it claims to.
🟢 PR Severity: LOW
🟢 Low (2 files)
AnalysisBoth changed files fall into the LOW tier: one is a release-notes markdown update, and the other is a test-only file ( To override, add a |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Change Description
TestRbfChannelActiveTransitionsshadows the package-levellocalAddrandremoteAddrwith twenty raw bytes each:Neither is a well-formed delivery script — not a witness program, and not any
other template a co-op close is willing to pay to — so both are rejected by
validateShutdownScript.Why it goes unnoticed today
validateShutdownis a chain of guards with early returns:ErrThawHeightNotReachedErrTaprootShutdownNonceMissingErrInvalidShutdownScriptBoth subtests that feed the shadowed
remoteAddrinto aShutdownReceived—remote_initiated_thaw_height_close_failandremote_initiated_taproot_no_nonce_fail— trip an earlier guard on purpose, soguard 3 is never reached. The one subtest that drives the whole chain,
remote_initiated_close_ok, was extracted in #11019 intotestRemoteInitiatedCloseOkNonTap/testRemoteInitiatedCloseOkTaproot, whichsit outside the shadow and therefore pick up the valid package-level scripts.
To be clear: nothing currently passes for the wrong reason. The harness
asserts on specific sentinel errors via
errors.Is. But that holds only byaccident of where the failures happen to land, not by design.
Why it is worth fixing anyway
The cost is paid by whoever touches that guard chain next, and it is not paid
in a readable failure. Hoisting the delivery-script check above the thaw-height
check — a reasonable thing to want, since it is the cheaper check — does not
produce an assertion diff. The unexpected
ErrInvalidShutdownScriptreaches themock error reporter as an unmatched call and panics the package's test
binary:
The stack points into
protofsm/state_machine.goandmock.go, not at thefixture that is actually wrong.
Deleting the two shadowing locals with that same reorder still applied: the
whole package passes. So the shadow is the sole cause.
There is a second, quieter cost.
local_initiated_close_okpasses the invalidlocalAddraslocalUpfrontAddrand asserts it lands in state unchallenged.#11019 only added validation on the remote side; if anyone later makes that
symmetric, this test breaks for fixture reasons rather than subject reasons.
The fix
Drop the shadowing locals so those subtests use the valid package-level P2TR
scripts, and record the invariant where the scripts are declared. The general
principle: a negative test's fixture should be valid in every dimension except
the one under test, or it is not isolating what it claims to.
Where this came from
Found while backporting #11019 to
v0.20.x. On that branchremote_initiated_close_okis still an inline subtest rather than an extractedhelper, so it sits inside the shadow and picks up the 20-byte blob. Once
#11019 makes delivery-script validation unconditional, that subtest fails with
invalid shutdown script. Master escapes this only because of the helperextraction described above. The backport carries the same fixture fix; this PR
brings it to master, where it is latent rather than active.
Steps to Test
To reproduce the latent failure this prevents, hoist the
validateRemoteDeliveryScriptcall to the top ofvalidateShutdowninlnwallet/chancloser/rbf_coop_transitions.goand run the package tests — onmasterthe binary panics; with this change it passes.Pull Request Checklist
Testing
Code Style and Documentation
[skip ci]in the commit message for small changes.Added under
## Testinginrelease-notes-0.22.0.md. This is a test-onlychange with no user-visible effect, so
no-changelogwould also be reasonableif maintainers prefer to keep that section for feature-level test work.