mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-10-03 20:33:04 +02:00
contractcourt/chanarb test: expand TestChannelArbitratorCommitFailure to coop and local force close
This commit is contained in:
@@ -742,86 +742,141 @@ func TestChannelArbitratorPersistence(t *testing.T) {
|
|||||||
// TestChannelArbitratorCommitFailure tests that the channel arbitrator is able
|
// TestChannelArbitratorCommitFailure tests that the channel arbitrator is able
|
||||||
// to recover from a failed CommitState call at restart.
|
// to recover from a failed CommitState call at restart.
|
||||||
func TestChannelArbitratorCommitFailure(t *testing.T) {
|
func TestChannelArbitratorCommitFailure(t *testing.T) {
|
||||||
// Start out with a log that will fail committing to StateContractClosed.
|
|
||||||
log := &mockArbitratorLog{
|
testCases := []struct {
|
||||||
state: StateDefault,
|
|
||||||
newStates: make(chan ArbitratorState, 5),
|
// closeType is the type of channel close we want ot test.
|
||||||
failCommit: true,
|
closeType channeldb.ClosureType
|
||||||
failCommitState: StateContractClosed,
|
|
||||||
|
// sendEvent is a function that will send the event
|
||||||
|
// corresponding to this test's closeType to the passed
|
||||||
|
// ChannelArbitrator.
|
||||||
|
sendEvent func(chanArb *ChannelArbitrator)
|
||||||
|
|
||||||
|
// expectedStates is the states we expect the state machine to
|
||||||
|
// go through after a restart and successful log commit.
|
||||||
|
expectedStates []ArbitratorState
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
closeType: channeldb.CooperativeClose,
|
||||||
|
sendEvent: func(chanArb *ChannelArbitrator) {
|
||||||
|
closeInfo := &CooperativeCloseInfo{
|
||||||
|
&channeldb.ChannelCloseSummary{},
|
||||||
|
}
|
||||||
|
chanArb.cfg.ChainEvents.CooperativeClosure <- closeInfo
|
||||||
|
},
|
||||||
|
expectedStates: []ArbitratorState{StateFullyResolved},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
closeType: channeldb.RemoteForceClose,
|
||||||
|
sendEvent: func(chanArb *ChannelArbitrator) {
|
||||||
|
commitSpend := &chainntnfs.SpendDetail{
|
||||||
|
SpenderTxHash: &chainhash.Hash{},
|
||||||
|
}
|
||||||
|
|
||||||
|
uniClose := &lnwallet.UnilateralCloseSummary{
|
||||||
|
SpendDetail: commitSpend,
|
||||||
|
HtlcResolutions: &lnwallet.HtlcResolutions{},
|
||||||
|
}
|
||||||
|
chanArb.cfg.ChainEvents.RemoteUnilateralClosure <- uniClose
|
||||||
|
},
|
||||||
|
expectedStates: []ArbitratorState{StateContractClosed, StateFullyResolved},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
closeType: channeldb.LocalForceClose,
|
||||||
|
sendEvent: func(chanArb *ChannelArbitrator) {
|
||||||
|
chanArb.cfg.ChainEvents.LocalUnilateralClosure <- &LocalUnilateralCloseInfo{
|
||||||
|
&chainntnfs.SpendDetail{},
|
||||||
|
&lnwallet.LocalForceCloseSummary{
|
||||||
|
CloseTx: &wire.MsgTx{},
|
||||||
|
HtlcResolutions: &lnwallet.HtlcResolutions{},
|
||||||
|
},
|
||||||
|
&channeldb.ChannelCloseSummary{},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
expectedStates: []ArbitratorState{StateContractClosed, StateFullyResolved},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
chanArb, resolved, err := createTestChannelArbitrator(log)
|
for _, test := range testCases {
|
||||||
if err != nil {
|
log := &mockArbitratorLog{
|
||||||
t.Fatalf("unable to create ChannelArbitrator: %v", err)
|
state: StateDefault,
|
||||||
}
|
newStates: make(chan ArbitratorState, 5),
|
||||||
|
failCommit: true,
|
||||||
|
|
||||||
if err := chanArb.Start(); err != nil {
|
// Set the log to fail on the first expected state
|
||||||
t.Fatalf("unable to start ChannelArbitrator: %v", err)
|
// after state machine progress for this test case.
|
||||||
}
|
failCommitState: test.expectedStates[0],
|
||||||
|
}
|
||||||
|
|
||||||
// It should start in StateDefault.
|
chanArb, resolved, err := createTestChannelArbitrator(log)
|
||||||
assertState(t, chanArb, StateDefault)
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create ChannelArbitrator: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
closed := make(chan struct{})
|
if err := chanArb.Start(); err != nil {
|
||||||
chanArb.cfg.MarkChannelClosed = func(*channeldb.ChannelCloseSummary) error {
|
t.Fatalf("unable to start ChannelArbitrator: %v", err)
|
||||||
close(closed)
|
}
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send a remote force close event.
|
// It should start in StateDefault.
|
||||||
commitSpend := &chainntnfs.SpendDetail{
|
assertState(t, chanArb, StateDefault)
|
||||||
SpenderTxHash: &chainhash.Hash{},
|
|
||||||
}
|
|
||||||
|
|
||||||
uniClose := &lnwallet.UnilateralCloseSummary{
|
closed := make(chan struct{})
|
||||||
SpendDetail: commitSpend,
|
chanArb.cfg.MarkChannelClosed = func(
|
||||||
HtlcResolutions: &lnwallet.HtlcResolutions{},
|
*channeldb.ChannelCloseSummary) error {
|
||||||
}
|
close(closed)
|
||||||
chanArb.cfg.ChainEvents.RemoteUnilateralClosure <- uniClose
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
select {
|
// Send the test event to trigger the state machine.
|
||||||
case <-closed:
|
test.sendEvent(chanArb)
|
||||||
case <-time.After(5 * time.Second):
|
|
||||||
t.Fatalf("channel was not marked closed")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since the channel was marked closed in the database, but the commit
|
select {
|
||||||
// to the next state failed, the state should still be StateDefault.
|
case <-closed:
|
||||||
time.Sleep(100 * time.Millisecond)
|
case <-time.After(5 * time.Second):
|
||||||
if log.state != StateDefault {
|
t.Fatalf("channel was not marked closed")
|
||||||
t.Fatalf("expected to stay in StateDefault")
|
}
|
||||||
}
|
|
||||||
chanArb.Stop()
|
|
||||||
|
|
||||||
// Start the arbitrator again, with IsPendingClose reporting the
|
// Since the channel was marked closed in the database, but the
|
||||||
// channel closed in the database.
|
// commit to the next state failed, the state should still be
|
||||||
chanArb, resolved, err = createTestChannelArbitrator(log)
|
// StateDefault.
|
||||||
if err != nil {
|
time.Sleep(100 * time.Millisecond)
|
||||||
t.Fatalf("unable to create ChannelArbitrator: %v", err)
|
if log.state != StateDefault {
|
||||||
}
|
t.Fatalf("expected to stay in StateDefault, instead "+
|
||||||
|
"has %v", log.state)
|
||||||
|
}
|
||||||
|
chanArb.Stop()
|
||||||
|
|
||||||
log.failCommit = false
|
// Start the arbitrator again, with IsPendingClose reporting
|
||||||
|
// the channel closed in the database.
|
||||||
|
chanArb, resolved, err = createTestChannelArbitrator(log)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create ChannelArbitrator: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
chanArb.cfg.IsPendingClose = true
|
log.failCommit = false
|
||||||
chanArb.cfg.ClosingHeight = 100
|
|
||||||
chanArb.cfg.CloseType = channeldb.RemoteForceClose
|
|
||||||
|
|
||||||
if err := chanArb.Start(); err != nil {
|
chanArb.cfg.IsPendingClose = true
|
||||||
t.Fatalf("unable to start ChannelArbitrator: %v", err)
|
chanArb.cfg.ClosingHeight = 100
|
||||||
}
|
chanArb.cfg.CloseType = test.closeType
|
||||||
|
|
||||||
// Since the channel is marked closed in the database, it should
|
if err := chanArb.Start(); err != nil {
|
||||||
// advance to StateContractClosed and StateFullyResolved.
|
t.Fatalf("unable to start ChannelArbitrator: %v", err)
|
||||||
assertStateTransitions(
|
}
|
||||||
t, log.newStates, StateContractClosed, StateFullyResolved,
|
|
||||||
)
|
|
||||||
|
|
||||||
// It should also mark the channel as resolved.
|
// Since the channel is marked closed in the database, it
|
||||||
select {
|
// should advance to the expected states.
|
||||||
case <-resolved:
|
assertStateTransitions(
|
||||||
// Expected.
|
t, log.newStates, test.expectedStates...,
|
||||||
case <-time.After(5 * time.Second):
|
)
|
||||||
t.Fatalf("contract was not resolved")
|
|
||||||
|
// It should also mark the channel as resolved.
|
||||||
|
select {
|
||||||
|
case <-resolved:
|
||||||
|
// Expected.
|
||||||
|
case <-time.After(5 * time.Second):
|
||||||
|
t.Fatalf("contract was not resolved")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user