contractcourt/channel_arbitrator: prevent force closing same channel twice
In this commit, we prevent the ChainArbitrator from sending a force close request for a channel if it has previously already sent one. We do this to prevent blocking the caller of ForceCloseContract.
This commit is contained in:
parent
7e69c6e7f6
commit
60036aca1d
@ -1,6 +1,7 @@
|
|||||||
package contractcourt
|
package contractcourt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
@ -21,6 +22,13 @@ const (
|
|||||||
broadcastRedeemMultiplier = 2
|
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
|
// WitnessSubscription represents an intent to be notified once new witnesses
|
||||||
// are discovered by various active contract resolvers. A contract resolver may
|
// 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
|
// 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
|
// channel. We'll
|
||||||
case closeReq := <-c.forceCloseReqs:
|
case closeReq := <-c.forceCloseReqs:
|
||||||
if c.state != StateDefault {
|
if c.state != StateDefault {
|
||||||
|
select {
|
||||||
|
case closeReq.closeTx <- nil:
|
||||||
|
case <-c.quit:
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case closeReq.errResp <- errAlreadyForceClosed:
|
||||||
|
case <-c.quit:
|
||||||
|
}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1143,3 +1143,47 @@ func TestChannelArbitratorEmptyResolutions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
chanArb.Stop()
|
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…
Reference in New Issue
Block a user