htlcswitch/link_test: add TestChannelLinkFail

This commit is contained in:
Johan T. Halseth 2018-05-23 15:39:48 +02:00
parent 9a47494517
commit 88670175c7
No known key found for this signature in database
GPG key ID: 15BAADA29DA20D26

View file

@ -15,6 +15,7 @@ import (
"math"
"github.com/coreos/bbolt"
"github.com/davecgh/go-spew/spew"
"github.com/go-errors/errors"
"github.com/lightningnetwork/lnd/chainntnfs"
@ -1390,11 +1391,15 @@ func TestChannelLinkSingleHopMessageOrdering(t *testing.T) {
type mockPeer struct {
sync.Mutex
sentMsgs chan lnwire.Message
quit chan struct{}
disconnected bool
sentMsgs chan lnwire.Message
quit chan struct{}
}
func (m *mockPeer) SendMessage(msg lnwire.Message, sync bool) error {
if m.disconnected {
return fmt.Errorf("disconnected")
}
select {
case m.sentMsgs <- msg:
case <-m.quit:
@ -1479,6 +1484,8 @@ func newSingleLinkTestHarness(chanAmt, chanReserve btcutil.Amount) (
},
FetchLastChannelUpdate: mockGetChanUpdateMessage,
PreimageCache: pCache,
OnChannelFailure: func(lnwire.ChannelID, lnwire.ShortChannelID, LinkFailureError) {
},
UpdateContractSignals: func(*contractcourt.ContractSignals) error {
return nil
},
@ -4288,3 +4295,223 @@ func TestChannelLinkWaitForRevocation(t *testing.T) {
case <-time.After(50 * time.Millisecond):
}
}
type mockPackager struct {
failLoadFwdPkgs bool
}
func (*mockPackager) AddFwdPkg(tx *bolt.Tx, fwdPkg *channeldb.FwdPkg) error {
return nil
}
func (*mockPackager) SetFwdFilter(tx *bolt.Tx, height uint64,
fwdFilter *channeldb.PkgFilter) error {
return nil
}
func (*mockPackager) AckAddHtlcs(tx *bolt.Tx,
addRefs ...channeldb.AddRef) error {
return nil
}
func (m *mockPackager) LoadFwdPkgs(tx *bolt.Tx) ([]*channeldb.FwdPkg, error) {
if m.failLoadFwdPkgs {
return nil, fmt.Errorf("failing LoadFwdPkgs")
}
return nil, nil
}
func (*mockPackager) RemovePkg(tx *bolt.Tx, height uint64) error {
return nil
}
func (*mockPackager) AckSettleFails(tx *bolt.Tx,
settleFailRefs ...channeldb.SettleFailRef) error {
return nil
}
// TestChannelLinkFail tests that we will fail the channel, and force close the
// channel in certain situations.
func TestChannelLinkFail(t *testing.T) {
t.Parallel()
testCases := []struct {
// options is used to set up mocks and configure the link
// before it is started.
options func(*channelLink)
// link test is used to execute the given test on the channel
// link after it is started.
linkTest func(*testing.T, *channelLink, *lnwallet.LightningChannel)
// shouldForceClose indicates whether we expect the link to
// force close the channel in response to the actions performed
// during the linkTest.
shouldForceClose bool
}{
{
// Test that we don't force close if syncing states
// fails at startup.
func(c *channelLink) {
c.cfg.SyncStates = true
// Make the syncChanStateCall fail by making
// the SendMessage call fail.
c.cfg.Peer.(*mockPeer).disconnected = true
},
func(t *testing.T, c *channelLink, _ *lnwallet.LightningChannel) {
// Should fail at startup.
},
false,
},
{
// Test that we don't force closes the channel if
// resolving forward packages fails at startup.
func(c *channelLink) {
// We make the call to resolveFwdPkgs fail by
// making the underlying forwarder fail.
pkg := &mockPackager{
failLoadFwdPkgs: true,
}
c.channel.State().Packager = pkg
},
func(t *testing.T, c *channelLink, _ *lnwallet.LightningChannel) {
// Should fail at startup.
},
false,
},
{
// Test that we force close the channel if we receive
// an invalid Settle message.
func(c *channelLink) {
},
func(t *testing.T, c *channelLink, _ *lnwallet.LightningChannel) {
// Recevive an htlc settle for an htlc that was
// never added.
htlcSettle := &lnwire.UpdateFulfillHTLC{
ID: 0,
PaymentPreimage: [32]byte{},
}
c.HandleChannelUpdate(htlcSettle)
},
true,
},
{
// Test that we force close the channel if we receive
// an invalid CommitSig, not containing enough HTLC
// sigs.
func(c *channelLink) {
},
func(t *testing.T, c *channelLink, remoteChannel *lnwallet.LightningChannel) {
// Generate an HTLC and send to the link.
htlc1 := generateHtlc(t, c, remoteChannel, 0)
sendHtlcBobToAlice(t, c, remoteChannel, htlc1)
// Sign a commitment that will include
// signature for the HTLC just sent.
sig, htlcSigs, err :=
remoteChannel.SignNextCommitment()
if err != nil {
t.Fatalf("error signing commitment: %v",
err)
}
// Remove the HTLC sig, such that the commit
// sig will be invalid.
commitSig := &lnwire.CommitSig{
CommitSig: sig,
HtlcSigs: htlcSigs[1:],
}
c.HandleChannelUpdate(commitSig)
},
true,
},
{
// Test that we force close the channel if we receive
// an invalid CommitSig, where the sig itself is
// corrupted.
func(c *channelLink) {
},
func(t *testing.T, c *channelLink, remoteChannel *lnwallet.LightningChannel) {
// Generate an HTLC and send to the link.
htlc1 := generateHtlc(t, c, remoteChannel, 0)
sendHtlcBobToAlice(t, c, remoteChannel, htlc1)
// Sign a commitment that will include
// signature for the HTLC just sent.
sig, htlcSigs, err :=
remoteChannel.SignNextCommitment()
if err != nil {
t.Fatalf("error signing commitment: %v",
err)
}
// Flip a bit on the signature, rendering it
// invalid.
sig[19] ^= 1
commitSig := &lnwire.CommitSig{
CommitSig: sig,
HtlcSigs: htlcSigs,
}
c.HandleChannelUpdate(commitSig)
},
true,
},
}
const chanAmt = btcutil.SatoshiPerBitcoin * 5
const chanReserve = 0
// Execute each test case.
for i, test := range testCases {
link, remoteChannel, _, start, cleanUp, _, err :=
newSingleLinkTestHarness(chanAmt, 0)
if err != nil {
t.Fatalf("unable to create link: %v", err)
}
coreLink := link.(*channelLink)
// Set up a channel used to check whether the link error
// force closed the channel.
linkErrors := make(chan LinkFailureError, 1)
coreLink.cfg.OnChannelFailure = func(_ lnwire.ChannelID,
_ lnwire.ShortChannelID, linkErr LinkFailureError) {
linkErrors <- linkErr
}
// Set up the link before starting it.
test.options(coreLink)
if err := start(); err != nil {
t.Fatalf("unable to start test harness: %v", err)
}
// Execute the test case.
test.linkTest(t, coreLink, remoteChannel)
// Currently we expect all test cases to lead to link error.
var linkErr LinkFailureError
select {
case linkErr = <-linkErrors:
case <-time.After(10 * time.Second):
t.Fatalf("%d) Alice did not fail"+
"channel", i)
}
// If we expect the link to force close the channel in this
// case, check that it happens. If not, make sure it does not
// happen.
if test.shouldForceClose != linkErr.ForceClose {
t.Fatalf("%d) Expected Alice to force close(%v), "+
"instead got(%v)", i, test.shouldForceClose,
linkErr.ForceClose)
}
// Clean up before starting next test case.
cleanUp()
}
}