contractcourt: deadlock fix via temporary unmerged map

The main idea is that NotifyContractUpdate adds the ContractUpdate to
a map called unmerged. It is populated in Start by shallow-copying the
activeHTLCs map values (htlcSet). The htlcSets underlying maps are not
copied, and so unmerged will just contain pointers to them. This should
be fine since unmerged will not modify them. At the call-sites of
activeHTLCs, it is updated to include the unmerged sets. This happens
with a mutex and should not cause any data race, even though it is
copying the underlying map pointers. No persistence should be
necessary since on restart, activeHTLCs and unmerged will just be
populated again.
This commit is contained in:
eugene
2022-03-08 14:27:27 -05:00
parent 1e04f191a1
commit ef83370dcf
3 changed files with 89 additions and 75 deletions

View File

@@ -864,8 +864,7 @@ func TestChannelArbitratorLocalForceClosePendingHtlc(t *testing.T) {
HtlcKey: LocalHtlcSet,
Htlcs: htlcSet,
}
err = chanArb.notifyContractUpdate(newUpdate)
require.NoError(t, err)
chanArb.notifyContractUpdate(newUpdate)
errChan := make(chan error, 1)
respChan := make(chan *wire.MsgTx, 1)
@@ -1872,8 +1871,7 @@ func TestChannelArbitratorDanglingCommitForceClose(t *testing.T) {
HtlcKey: htlcKey,
Htlcs: []channeldb.HTLC{danglingHTLC},
}
err = chanArb.notifyContractUpdate(newUpdate)
require.NoError(t, err)
chanArb.notifyContractUpdate(newUpdate)
// At this point, we now have a split commitment state
// from the PoV of the channel arb. There's now an HTLC
@@ -2061,8 +2059,7 @@ func TestChannelArbitratorPendingExpiredHTLC(t *testing.T) {
HtlcKey: RemoteHtlcSet,
Htlcs: []channeldb.HTLC{pendingHTLC},
}
err = chanArb.notifyContractUpdate(newUpdate)
require.NoError(t, err)
chanArb.notifyContractUpdate(newUpdate)
// We will advance the uptime to 10 seconds which should be still within
// the grace period and should not trigger going to chain.
@@ -2408,6 +2405,14 @@ func TestSweepAnchors(t *testing.T) {
htlcDust.HtlcIndex: htlcDust,
},
}
chanArb.unmergedSet[LocalHtlcSet] = htlcSet{
incomingHTLCs: map[uint64]channeldb.HTLC{
htlcWithPreimage.HtlcIndex: htlcWithPreimage,
},
outgoingHTLCs: map[uint64]channeldb.HTLC{
htlcDust.HtlcIndex: htlcDust,
},
}
// Setup our remote HTLC set such that no valid HTLCs can be used, thus
// we default to anchorSweepConfTarget.
@@ -2420,6 +2425,14 @@ func TestSweepAnchors(t *testing.T) {
htlcDust.HtlcIndex: htlcDust,
},
}
chanArb.unmergedSet[RemoteHtlcSet] = htlcSet{
incomingHTLCs: map[uint64]channeldb.HTLC{
htlcSmallExipry.HtlcIndex: htlcSmallExipry,
},
outgoingHTLCs: map[uint64]channeldb.HTLC{
htlcDust.HtlcIndex: htlcDust,
},
}
// Setup out pending remote HTLC set such that we will use the HTLC's
// CLTV from the outgoing HTLC set.
@@ -2432,6 +2445,14 @@ func TestSweepAnchors(t *testing.T) {
htlcSmallExipry.HtlcIndex: htlcSmallExipry,
},
}
chanArb.unmergedSet[RemotePendingHtlcSet] = htlcSet{
incomingHTLCs: map[uint64]channeldb.HTLC{
htlcDust.HtlcIndex: htlcDust,
},
outgoingHTLCs: map[uint64]channeldb.HTLC{
htlcSmallExipry.HtlcIndex: htlcSmallExipry,
},
}
// Create AnchorResolutions.
anchors := &lnwallet.AnchorResolutions{
@@ -2561,8 +2582,7 @@ func TestChannelArbitratorAnchors(t *testing.T) {
// preimage available.
Htlcs: []channeldb.HTLC{htlc, htlcWithPreimage},
}
err = chanArb.notifyContractUpdate(newUpdate)
require.NoError(t, err)
chanArb.notifyContractUpdate(newUpdate)
newUpdate = &ContractUpdate{
HtlcKey: RemoteHtlcSet,
@@ -2571,8 +2591,7 @@ func TestChannelArbitratorAnchors(t *testing.T) {
// incoming HTLC (toRemoteHTLCs) has a lower CLTV.
Htlcs: []channeldb.HTLC{htlc, htlcWithPreimage},
}
err = chanArb.notifyContractUpdate(newUpdate)
require.NoError(t, err)
chanArb.notifyContractUpdate(newUpdate)
errChan := make(chan error, 1)
respChan := make(chan *wire.MsgTx, 1)