contractcourt: test sweep of locally breached commit tx

This adds a test to the commit sweeper resolver to ensure it behaves
properly if the local node breaches a channel.

In this situation the remote party is expected to sweep the breached
output to itself and therefore the local party won't be able to recover
any funds.
This commit is contained in:
Matheus Degiovani 2020-01-08 09:08:47 -03:00
parent d71a4ee033
commit 2110bfa40b

@ -96,6 +96,7 @@ func (i *commitSweepResolverTestContext) waitForResult() {
type mockSweeper struct {
sweptInputs chan input.Input
updatedInputs chan wire.OutPoint
sweepErr error
}
func newMockSweeper() *mockSweeper {
@ -112,7 +113,8 @@ func (s *mockSweeper) SweepInput(input input.Input, params sweep.Params) (
result := make(chan sweep.Result, 1)
result <- sweep.Result{
Tx: &wire.MsgTx{},
Tx: &wire.MsgTx{},
Err: s.sweepErr,
}
return result, nil
}
@ -244,3 +246,92 @@ func TestCommitSweepResolverDelay(t *testing.T) {
t.Fatal("unexpected resolver report")
}
}
// TestCommitSweepResolverLocalBreach tests resolution when the local node
// publishes a breached output (one that is swept by the remote party).
func TestCommitSweepResolverLocalBreach(t *testing.T) {
t.Parallel()
defer timeout(t)()
amt := int64(100)
outpoint := wire.OutPoint{
Index: 5,
}
res := lnwallet.CommitOutputResolution{
SelfOutputSignDesc: input.SignDescriptor{
Output: &wire.TxOut{
Value: amt,
},
WitnessScript: []byte{0},
},
MaturityDelay: 3,
SelfOutPoint: outpoint,
}
ctx := newCommitSweepResolverTestContext(t, &res)
// Setup the sweeper so that it returns an error of the output being
// already swept.
ctx.sweeper.sweepErr = sweep.ErrRemoteSpend
report := ctx.resolver.report()
expectedReport := ContractReport{
Outpoint: outpoint,
Type: ReportOutputUnencumbered,
Amount: btcutil.Amount(amt),
LimboBalance: btcutil.Amount(amt),
}
if *report != expectedReport {
t.Fatalf("unexpected resolver report. want=%v got=%v",
expectedReport, report)
}
ctx.resolve()
ctx.notifier.confChan <- &chainntnfs.TxConfirmation{
BlockHeight: testInitialBlockHeight - 1,
}
// Allow resolver to process confirmation.
time.Sleep(100 * time.Millisecond)
// Expect report to be updated.
report = ctx.resolver.report()
if report.MaturityHeight != testInitialBlockHeight+2 {
t.Fatal("report maturity height incorrect")
}
// Notify initial block height. The csv lock is still in effect, so we
// don't expect any sweep to happen yet.
ctx.notifyEpoch(testInitialBlockHeight)
select {
case <-ctx.sweeper.sweptInputs:
t.Fatal("no sweep expected")
case <-time.After(100 * time.Millisecond):
}
// A new block arrives. The commit tx confirmed at height -1 and the csv
// is 3, so a spend will be valid in the first block after height +1.
ctx.notifyEpoch(testInitialBlockHeight + 1)
<-ctx.sweeper.sweptInputs
ctx.waitForResult()
report = ctx.resolver.report()
expectedReport = ContractReport{
Outpoint: outpoint,
Type: ReportOutputUnencumbered,
Amount: btcutil.Amount(amt),
MaturityHeight: testInitialBlockHeight + 2,
// RecoveredBalance is zero due to the output having been swept
// by the remote party.
RecoveredBalance: 0,
}
if *report != expectedReport {
t.Fatalf("unexpected resolver report. want=%v got=%v",
expectedReport, report)
}
}