openchannel: cancel shims with a live context

In manager.go, deferred shim cleanup was calling
FundingStateStep with the original request ctx.
If the user had already canceled that context,
the cleanup RPC would run with a canceled context
and could fail to remove the pending shim. I changed
that cleanup path to use context.WithoutCancel(ctx)
so the cancellation RPC still has a live context.
This commit is contained in:
Slyghtning 2026-02-28 11:54:55 +01:00
parent 0b295123e7
commit 3e526faf82
No known key found for this signature in database
GPG key ID: F82D456EA023C9BF
2 changed files with 46 additions and 10 deletions

View file

@ -458,9 +458,7 @@ func (m *Manager) openChannelPsbt(ctx context.Context,
log.Infof("Starting PSBT funding flow with pending channel ID %x.\n", log.Infof("Starting PSBT funding flow with pending channel ID %x.\n",
pendingChanID) pendingChanID)
// maybeCancelShim is a helper function that cancels the funding shim defer func() {
// with the RPC server in case we end up aborting early.
maybeCancelShim := func() {
shimMu.Lock() shimMu.Lock()
defer shimMu.Unlock() defer shimMu.Unlock()
@ -478,15 +476,14 @@ func (m *Manager) openChannelPsbt(ctx context.Context,
}, },
} }
_, err := m.cfg.LightningClient.FundingStateStep( _, err := m.cfg.LightningClient.FundingStateStep(
ctx, cancelMsg, context.WithoutCancel(ctx), cancelMsg,
) )
if err != nil { if err != nil {
log.Errorf("Error canceling shim: %v\n", err) log.Errorf("Error canceling shim: %v\n", err)
} }
shimPending = false shimPending = false
} }
} }()
defer maybeCancelShim()
// Create the PSBT funding shim that will tell the funding manager we // Create the PSBT funding shim that will tell the funding manager we
// want to use a PSBT. // want to use a PSBT.

View file

@ -608,9 +608,10 @@ type mockLndClient struct {
rawClient lnrpc.LightningClient rawClient lnrpc.LightningClient
mu sync.Mutex mu sync.Mutex
fundingStepIdx int fundingStepIdx int
fundingStepErr error fundingStepErr error
fundingStepCtxErrs []error
} }
func (m *mockLndClient) RawClientWithMacAuth( func (m *mockLndClient) RawClientWithMacAuth(
@ -620,13 +621,14 @@ func (m *mockLndClient) RawClientWithMacAuth(
return ctx, 0, m.rawClient return ctx, 0, m.rawClient
} }
func (m *mockLndClient) FundingStateStep(_ context.Context, func (m *mockLndClient) FundingStateStep(ctx context.Context,
_ *lnrpc.FundingTransitionMsg) (*lnrpc.FundingStateStepResp, error) { _ *lnrpc.FundingTransitionMsg) (*lnrpc.FundingStateStepResp, error) {
m.mu.Lock() m.mu.Lock()
defer m.mu.Unlock() defer m.mu.Unlock()
m.fundingStepIdx++ m.fundingStepIdx++
m.fundingStepCtxErrs = append(m.fundingStepCtxErrs, ctx.Err())
return &lnrpc.FundingStateStepResp{}, m.fundingStepErr return &lnrpc.FundingStateStepResp{}, m.fundingStepErr
} }
@ -746,6 +748,43 @@ func TestStreamOpenError(t *testing.T) {
// Verify that the shim was canceled via FundingStateStep. // Verify that the shim was canceled via FundingStateStep.
lnClient.mu.Lock() lnClient.mu.Lock()
require.Equal(t, 1, lnClient.fundingStepIdx) require.Equal(t, 1, lnClient.fundingStepIdx)
require.Len(t, lnClient.fundingStepCtxErrs, 1)
require.NoError(t, lnClient.fundingStepCtxErrs[0])
lnClient.mu.Unlock()
}
// TestStreamOpenErrorWithCanceledContext verifies that deferred shim cleanup
// still uses a live context even if the caller canceled the original request.
func TestStreamOpenErrorWithCanceledContext(t *testing.T) {
t.Parallel()
mockRaw := &mockRawLnrpcClient{
openErr: errors.New("connection refused"),
}
lnClient := &mockLndClient{rawClient: mockRaw}
manager := &Manager{
cfg: &Config{
LightningClient: lnClient,
ChainParams: &chaincfg.RegressionNetParams,
},
}
ctx, cancel := context.WithCancel(context.Background())
cancel()
req := &lnrpc.OpenChannelRequest{
LocalFundingAmount: 100000,
MinConfs: defaultUtxoMinConf,
}
_, err := manager.openChannelPsbt(ctx, req, nil, 0)
require.ErrorContains(t, err, "opening stream to server failed")
lnClient.mu.Lock()
require.Equal(t, 1, lnClient.fundingStepIdx)
require.Len(t, lnClient.fundingStepCtxErrs, 1)
require.NoError(t, lnClient.fundingStepCtxErrs[0])
lnClient.mu.Unlock() lnClient.mu.Unlock()
} }