Skip to content

Commit 7035ded

Browse files
authored
Merge pull request #1033 from lightninglabs/defer-utxo-cleanup-funding
tapchannel: use defer to ensure lease unlocked if funding failure
2 parents f5d39c9 + b8271a1 commit 7035ded

File tree

1 file changed

+50
-24
lines changed

1 file changed

+50
-24
lines changed

tapchannel/aux_funding_controller.go

Lines changed: 50 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,9 @@ func (p *pendingAssetFunding) unlockInputs(ctx context.Context,
505505
func (p *pendingAssetFunding) unlockAssetInputs(ctx context.Context,
506506
coinSelect tapfreighter.CoinSelector) error {
507507

508+
log.Debugf("unlocking asset inputs: %v",
509+
spew.Sdump(p.lockedAssetInputs))
510+
508511
err := coinSelect.ReleaseCoins(ctx, p.lockedAssetInputs...)
509512
if err != nil {
510513
return fmt.Errorf("unable to unlock asset outpoints %v: %w",
@@ -1028,13 +1031,6 @@ func (f *FundingController) completeChannelFunding(ctx context.Context,
10281031
return nil, err
10291032
}
10301033

1031-
fundingState.lockedAssetInputs = fn.Map(
1032-
fundedVpkt.VPacket.Inputs,
1033-
func(in *tappsbt.VInput) wire.OutPoint {
1034-
return in.PrevID.OutPoint
1035-
},
1036-
)
1037-
10381034
// Now that we have the initial skeleton for our funding PSBT, we'll
10391035
// modify the output value to match the channel amt asked for, which
10401036
// lnd will expect.
@@ -1356,6 +1352,40 @@ func (f *FundingController) processFundingReq(fundingFlows fundingFlowIndex,
13561352
return fmt.Errorf("unable to fund vPacket: %w", err)
13571353
}
13581354

1355+
// Now that we've funded the vPk, keep track of the set of inputs we
1356+
// locked to ensure we unlock them later.
1357+
fundingState.lockedAssetInputs = fn.Map(
1358+
fundingVpkt.VPacket.Inputs,
1359+
func(in *tappsbt.VInput) wire.OutPoint {
1360+
return in.PrevID.OutPoint
1361+
},
1362+
)
1363+
1364+
// We'll use this closure to ensure that we'll always unlock the inputs
1365+
// if we encounter an error below.
1366+
unlockLeases := func() {
1367+
err := fundingState.unlockInputs(fundReq.ctx, f.cfg.ChainWallet)
1368+
if err != nil {
1369+
log.Errorf("unable to unlock inputs: %v", err)
1370+
}
1371+
1372+
err = fundingState.unlockAssetInputs(
1373+
fundReq.ctx, f.cfg.CoinSelector,
1374+
)
1375+
if err != nil {
1376+
log.Errorf("Unable to unlock asset inputs: %v", err)
1377+
}
1378+
}
1379+
1380+
// Register a defer to execute if none of the set up below succeeds.
1381+
// This ensure we always unlock the UTXO.
1382+
var setupSuccess bool
1383+
defer func() {
1384+
if !setupSuccess {
1385+
unlockLeases()
1386+
}
1387+
}()
1388+
13591389
// Now that we know the final funding asset root along with the splits,
13601390
// we can derive the tapscript root that'll be used alongside the
13611391
// internal key (which we'll only learn from lnd later as we finalize
@@ -1394,12 +1424,23 @@ func (f *FundingController) processFundingReq(fundingFlows fundingFlowIndex,
13941424
"proofs: %w", err)
13951425
}
13961426

1427+
setupSuccess = true
1428+
13971429
// With the ownership proof sent, we'll now spawn a goroutine to take
13981430
// care of the final funding steps.
13991431
f.Wg.Add(1)
14001432
go func() {
14011433
defer f.Wg.Done()
14021434

1435+
// If we've failed, then we'll unlock any of the locked
1436+
// UTXOs, so they're free again.
1437+
var completeSuccess bool
1438+
defer func() {
1439+
if !completeSuccess {
1440+
unlockLeases()
1441+
}
1442+
}()
1443+
14031444
log.Infof("Waiting for funding ack...")
14041445

14051446
// Before we proceed with the channel funding, we'll wait to
@@ -1426,23 +1467,6 @@ func (f *FundingController) processFundingReq(fundingFlows fundingFlowIndex,
14261467
fundReq.ctx, fundingState, fundingVpkt,
14271468
)
14281469
if err != nil {
1429-
// If we've failed, then we'll unlock any of the locked
1430-
// UTXOs, so they're free again.
1431-
uErr := fundingState.unlockInputs(
1432-
fundReq.ctx, f.cfg.ChainWallet,
1433-
)
1434-
if uErr != nil {
1435-
log.Errorf("unable to unlock inputs: %v", uErr)
1436-
}
1437-
1438-
uErr = fundingState.unlockAssetInputs(
1439-
fundReq.ctx, f.cfg.CoinSelector,
1440-
)
1441-
if uErr != nil {
1442-
log.Errorf("Unable to unlock asset inputs: %v",
1443-
uErr)
1444-
}
1445-
14461470
// If anything went wrong during the funding process,
14471471
// the remote side might have an in-memory state and
14481472
// wouldn't allow us to try again within the next 10
@@ -1460,6 +1484,8 @@ func (f *FundingController) processFundingReq(fundingFlows fundingFlowIndex,
14601484
return
14611485
}
14621486

1487+
completeSuccess = true
1488+
14631489
fundReq.respChan <- chanPoint
14641490
}()
14651491

0 commit comments

Comments
 (0)