mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-03-04 09:48:19 +01:00
Merge pull request #2131 from wpaulino/force-close-same-channel
contractcourt/chain_arbitrator: prevent force closing same channel twice
This commit is contained in:
commit
febe6cd47f
2 changed files with 62 additions and 0 deletions
|
@ -1,6 +1,7 @@
|
|||
package contractcourt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
|
@ -21,6 +22,13 @@ const (
|
|||
broadcastRedeemMultiplier = 2
|
||||
)
|
||||
|
||||
var (
|
||||
// errAlreadyForceClosed is an error returned when we attempt to force
|
||||
// close a channel that's already in the process of doing so.
|
||||
errAlreadyForceClosed = errors.New("channel is already in the " +
|
||||
"process of being force closed")
|
||||
)
|
||||
|
||||
// WitnessSubscription represents an intent to be notified once new witnesses
|
||||
// are discovered by various active contract resolvers. A contract resolver may
|
||||
// use this to be notified of when it can satisfy an incoming contract after we
|
||||
|
@ -1652,6 +1660,16 @@ func (c *ChannelArbitrator) channelAttendant(bestHeight int32) {
|
|||
// channel. We'll
|
||||
case closeReq := <-c.forceCloseReqs:
|
||||
if c.state != StateDefault {
|
||||
select {
|
||||
case closeReq.closeTx <- nil:
|
||||
case <-c.quit:
|
||||
}
|
||||
|
||||
select {
|
||||
case closeReq.errResp <- errAlreadyForceClosed:
|
||||
case <-c.quit:
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -1143,3 +1143,47 @@ func TestChannelArbitratorEmptyResolutions(t *testing.T) {
|
|||
}
|
||||
chanArb.Stop()
|
||||
}
|
||||
|
||||
// TestChannelArbitratorAlreadyForceClosed ensures that we cannot force close a
|
||||
// channel that is already in the process of doing so.
|
||||
func TestChannelArbitratorAlreadyForceClosed(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// We'll create the arbitrator and its backing log to signal that it's
|
||||
// already in the process of being force closed.
|
||||
log := &mockArbitratorLog{
|
||||
state: StateCommitmentBroadcasted,
|
||||
}
|
||||
chanArb, _, err := createTestChannelArbitrator(log)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create ChannelArbitrator: %v", err)
|
||||
}
|
||||
if err := chanArb.Start(); err != nil {
|
||||
t.Fatalf("unable to start ChannelArbitrator: %v", err)
|
||||
}
|
||||
defer chanArb.Stop()
|
||||
|
||||
// Then, we'll create a request to signal a force close request to the
|
||||
// channel arbitrator.
|
||||
errChan := make(chan error, 1)
|
||||
respChan := make(chan *wire.MsgTx, 1)
|
||||
|
||||
select {
|
||||
case chanArb.forceCloseReqs <- &forceCloseReq{
|
||||
closeTx: respChan,
|
||||
errResp: errChan,
|
||||
}:
|
||||
case <-chanArb.quit:
|
||||
}
|
||||
|
||||
// Finally, we should ensure that we are not able to do so by seeing the
|
||||
// expected errAlreadyForceClosed error.
|
||||
select {
|
||||
case err = <-errChan:
|
||||
if err != errAlreadyForceClosed {
|
||||
t.Fatalf("expected errAlreadyForceClosed, got %v", err)
|
||||
}
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("expected to receive error response")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue