package htlcswitch

import (
	"bytes"
	"fmt"
	"runtime"
	"strings"
	"sync"
	"testing"
	"time"

	"io"

	"math"

	"github.com/davecgh/go-spew/spew"
	"github.com/go-errors/errors"
	"github.com/lightningnetwork/lnd/chainntnfs"
	"github.com/lightningnetwork/lnd/channeldb"
	"github.com/lightningnetwork/lnd/contractcourt"
	"github.com/lightningnetwork/lnd/lnwallet"
	"github.com/lightningnetwork/lnd/lnwire"
	"github.com/roasbeef/btcd/chaincfg/chainhash"
	"github.com/roasbeef/btcd/wire"
	"github.com/roasbeef/btcutil"
)

const (
	testStartingHeight = 100
)

// concurrentTester is a thread-safe wrapper around the Fatalf method of a
// *testing.T instance. With this wrapper multiple goroutines can safely
// attempt to fail a test concurrently.
type concurrentTester struct {
	mtx sync.Mutex
	*testing.T
}

func newConcurrentTester(t *testing.T) *concurrentTester {
	return &concurrentTester{
		T: t,
	}
}

func (c *concurrentTester) Fatalf(format string, args ...interface{}) {
	c.mtx.Lock()
	defer c.mtx.Unlock()

	c.T.Fatalf(format, args)
}

// messageToString is used to produce less spammy log messages in trace mode by
// setting the 'Curve" parameter to nil. Doing this avoids printing out each of
// the field elements in the curve parameters for secp256k1.
func messageToString(msg lnwire.Message) string {
	switch m := msg.(type) {
	case *lnwire.RevokeAndAck:
		m.NextRevocationKey.Curve = nil
	case *lnwire.AcceptChannel:
		m.FundingKey.Curve = nil
		m.RevocationPoint.Curve = nil
		m.PaymentPoint.Curve = nil
		m.DelayedPaymentPoint.Curve = nil
		m.FirstCommitmentPoint.Curve = nil
	case *lnwire.OpenChannel:
		m.FundingKey.Curve = nil
		m.RevocationPoint.Curve = nil
		m.PaymentPoint.Curve = nil
		m.DelayedPaymentPoint.Curve = nil
		m.FirstCommitmentPoint.Curve = nil
	case *lnwire.FundingLocked:
		m.NextPerCommitmentPoint.Curve = nil
	}

	return spew.Sdump(msg)
}

// expectedMessage struct holds the message which travels from one peer to
// another, and additional information like, should this message we skipped for
// handling.
type expectedMessage struct {
	from    string
	to      string
	message lnwire.Message
	skip    bool
}

// createLogFunc is a helper function which returns the function which will be
// used for logging message are received from another peer.
func createLogFunc(name string, channelID lnwire.ChannelID) messageInterceptor {
	return func(m lnwire.Message) (bool, error) {
		chanID, err := getChanID(m)
		if err != nil {
			return false, err
		}

		if chanID == channelID {
			fmt.Printf("---------------------- \n %v received: "+
				"%v", name, messageToString(m))
		}
		return false, nil
	}
}

// createInterceptorFunc creates the function by the given set of messages
// which, checks the order of the messages and skip the ones which were
// indicated to be intercepted.
func createInterceptorFunc(prefix, receiver string, messages []expectedMessage,
	chanID lnwire.ChannelID, debug bool) messageInterceptor {

	// Filter message which should be received with given peer name.
	var expectToReceive []expectedMessage
	for _, message := range messages {
		if message.to == receiver {
			expectToReceive = append(expectToReceive, message)
		}
	}

	// Return function which checks the message order and skip the
	// messages.
	return func(m lnwire.Message) (bool, error) {
		messageChanID, err := getChanID(m)
		if err != nil {
			return false, err
		}

		if messageChanID == chanID {
			if len(expectToReceive) == 0 {
				return false, errors.Errorf("%v received "+
					"unexpected message out of range: %v",
					receiver, m.MsgType())
			}

			expectedMessage := expectToReceive[0]
			expectToReceive = expectToReceive[1:]

			if expectedMessage.message.MsgType() != m.MsgType() {
				return false, errors.Errorf("%v received wrong message: \n"+
					"real: %v\nexpected: %v", receiver, m.MsgType(),
					expectedMessage.message.MsgType())
			}

			if debug {
				var postfix string
				if revocation, ok := m.(*lnwire.RevokeAndAck); ok {
					var zeroHash chainhash.Hash
					if bytes.Equal(zeroHash[:], revocation.Revocation[:]) {
						postfix = "- empty revocation"
					}
				}

				if expectedMessage.skip {
					fmt.Printf("skipped: %v: %v %v \n", prefix,
						m.MsgType(), postfix)
				} else {
					fmt.Printf("%v: %v %v \n", prefix, m.MsgType(), postfix)
				}
			}

			return expectedMessage.skip, nil
		}
		return false, nil
	}
}

// TestChannelLinkSingleHopPayment in this test we checks the interaction
// between Alice and Bob within scope of one channel.
func TestChannelLinkSingleHopPayment(t *testing.T) {
	t.Parallel()

	channels, cleanUp, _, err := createClusterChannels(
		btcutil.SatoshiPerBitcoin*3,
		btcutil.SatoshiPerBitcoin*5)
	if err != nil {
		t.Fatalf("unable to create channel: %v", err)
	}
	defer cleanUp()

	n := newThreeHopNetwork(t, channels.aliceToBob, channels.bobToAlice,
		channels.bobToCarol, channels.carolToBob, testStartingHeight)
	if err := n.start(); err != nil {
		t.Fatal(err)
	}
	defer n.stop()

	aliceBandwidthBefore := n.aliceChannelLink.Bandwidth()
	bobBandwidthBefore := n.firstBobChannelLink.Bandwidth()

	debug := false
	if debug {
		// Log message that alice receives.
		n.aliceServer.intersect(createLogFunc("alice",
			n.aliceChannelLink.ChanID()))

		// Log message that bob receives.
		n.bobServer.intersect(createLogFunc("bob",
			n.firstBobChannelLink.ChanID()))
	}

	amount := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin)
	htlcAmt, totalTimelock, hops := generateHops(amount, testStartingHeight,
		n.firstBobChannelLink)

	// Wait for:
	// * HTLC add request to be sent to bob.
	// * alice<->bob commitment state to be updated.
	// * settle request to be sent back from bob to alice.
	// * alice<->bob commitment state to be updated.
	// * user notification to be sent.
	receiver := n.bobServer
	rhash, err := n.makePayment(n.aliceServer, receiver,
		n.bobServer.PubKey(), hops, amount, htlcAmt,
		totalTimelock).Wait(30 * time.Second)
	if err != nil {
		t.Fatalf("unable to make the payment: %v", err)
	}

	// Wait for Bob to receive the revocation.
	//
	// TODO(roasbeef); replace with select over returned err chan
	time.Sleep(100 * time.Millisecond)

	// Check that alice invoice was settled and bandwidth of HTLC
	// links was changed.
	invoice, err := receiver.registry.LookupInvoice(rhash)
	if err != nil {
		t.Fatalf("unable to get invoice: %v", err)
	}
	if !invoice.Terms.Settled {
		t.Fatal("alice invoice wasn't settled")
	}

	if aliceBandwidthBefore-amount != n.aliceChannelLink.Bandwidth() {
		t.Fatal("alice bandwidth should have decrease on payment " +
			"amount")
	}

	if bobBandwidthBefore+amount != n.firstBobChannelLink.Bandwidth() {
		t.Fatalf("bob bandwidth isn't match: expected %v, got %v",
			bobBandwidthBefore+amount,
			n.firstBobChannelLink.Bandwidth())
	}
}

// TestChannelLinkBidirectionalOneHopPayments tests the ability of channel
// link to cope with bigger number of payment updates that commitment
// transaction may consist.
func TestChannelLinkBidirectionalOneHopPayments(t *testing.T) {
	t.Parallel()

	channels, cleanUp, _, err := createClusterChannels(
		btcutil.SatoshiPerBitcoin*3,
		btcutil.SatoshiPerBitcoin*5)
	if err != nil {
		t.Fatalf("unable to create channel: %v", err)
	}
	defer cleanUp()

	n := newThreeHopNetwork(t, channels.aliceToBob, channels.bobToAlice,
		channels.bobToCarol, channels.carolToBob, testStartingHeight)
	if err := n.start(); err != nil {
		t.Fatal(err)
	}
	defer n.stop()
	bobBandwidthBefore := n.firstBobChannelLink.Bandwidth()
	aliceBandwidthBefore := n.aliceChannelLink.Bandwidth()

	debug := false
	if debug {
		// Log message that alice receives.
		n.aliceServer.intersect(createLogFunc("alice",
			n.aliceChannelLink.ChanID()))

		// Log message that bob receives.
		n.bobServer.intersect(createLogFunc("bob",
			n.firstBobChannelLink.ChanID()))
	}

	amt := lnwire.NewMSatFromSatoshis(20000)

	htlcAmt, totalTimelock, hopsForwards := generateHops(amt,
		testStartingHeight, n.firstBobChannelLink)
	_, _, hopsBackwards := generateHops(amt,
		testStartingHeight, n.aliceChannelLink)

	type result struct {
		err    error
		start  time.Time
		number int
		sender string
	}

	// Send max available payment number in both sides, thereby testing
	// the property of channel link to cope with overflowing.
	count := 2 * lnwallet.MaxHTLCNumber
	resultChan := make(chan *result, count)
	for i := 0; i < count/2; i++ {
		go func(i int) {
			r := &result{
				start:  time.Now(),
				number: i,
				sender: "alice",
			}

			_, r.err = n.makePayment(n.aliceServer, n.bobServer,
				n.bobServer.PubKey(), hopsForwards, amt, htlcAmt,
				totalTimelock).Wait(5 * time.Minute)
			resultChan <- r
		}(i)
	}

	for i := 0; i < count/2; i++ {
		go func(i int) {
			r := &result{
				start:  time.Now(),
				number: i,
				sender: "bob",
			}

			_, r.err = n.makePayment(n.bobServer, n.aliceServer,
				n.aliceServer.PubKey(), hopsBackwards, amt, htlcAmt,
				totalTimelock).Wait(5 * time.Minute)
			resultChan <- r
		}(i)
	}

	maxDelay := time.Duration(0)
	minDelay := time.Duration(math.MaxInt64)
	averageDelay := time.Duration(0)

	// Check that alice invoice was settled and bandwidth of HTLC
	// links was changed.
	for i := 0; i < count; i++ {
		select {
		case r := <-resultChan:
			if r.err != nil {
				t.Fatalf("unable to make payment: %v", r.err)
			}

			delay := time.Since(r.start)
			if delay > maxDelay {
				maxDelay = delay
			}

			if delay < minDelay {
				minDelay = delay
			}
			averageDelay += delay

		case <-time.After(5 * time.Minute):
			t.Fatalf("timeout: (%v/%v)", i+1, count)
		}
	}

	// TODO(roasbeef): should instead consume async notifications from both
	// links
	time.Sleep(time.Second * 2)

	// At the end Bob and Alice balances should be the same as previous,
	// because they sent the equal amount of money to each other.
	if aliceBandwidthBefore != n.aliceChannelLink.Bandwidth() {
		t.Fatalf("alice bandwidth shouldn't have changed: expected %v, got %x",
			aliceBandwidthBefore, n.aliceChannelLink.Bandwidth())
	}

	if bobBandwidthBefore != n.firstBobChannelLink.Bandwidth() {
		t.Fatalf("bob bandwidth shouldn't have changed: expected %v, got %v",
			bobBandwidthBefore, n.firstBobChannelLink.Bandwidth())
	}

	t.Logf("Max waiting: %v", maxDelay)
	t.Logf("Min waiting: %v", minDelay)
	t.Logf("Average waiting: %v", time.Duration(int(averageDelay)/count))
}

// TestChannelLinkMultiHopPayment checks the ability to send payment over two
// hops. In this test we send the payment from Carol to Alice over Bob peer.
// (Carol -> Bob -> Alice) and checking that HTLC was settled properly and
// balances were changed in two channels.
func TestChannelLinkMultiHopPayment(t *testing.T) {
	t.Parallel()

	channels, cleanUp, _, err := createClusterChannels(
		btcutil.SatoshiPerBitcoin*3,
		btcutil.SatoshiPerBitcoin*5)
	if err != nil {
		t.Fatalf("unable to create channel: %v", err)
	}
	defer cleanUp()

	n := newThreeHopNetwork(t, channels.aliceToBob, channels.bobToAlice,
		channels.bobToCarol, channels.carolToBob, testStartingHeight)
	if err := n.start(); err != nil {
		t.Fatal(err)
	}
	defer n.stop()

	carolBandwidthBefore := n.carolChannelLink.Bandwidth()
	firstBobBandwidthBefore := n.firstBobChannelLink.Bandwidth()
	secondBobBandwidthBefore := n.secondBobChannelLink.Bandwidth()
	aliceBandwidthBefore := n.aliceChannelLink.Bandwidth()

	debug := false
	if debug {
		// Log messages that alice receives from bob.
		n.aliceServer.intersect(createLogFunc("[alice]<-bob<-carol: ",
			n.aliceChannelLink.ChanID()))

		// Log messages that bob receives from alice.
		n.bobServer.intersect(createLogFunc("alice->[bob]->carol: ",
			n.firstBobChannelLink.ChanID()))

		// Log messages that bob receives from carol.
		n.bobServer.intersect(createLogFunc("alice<-[bob]<-carol: ",
			n.secondBobChannelLink.ChanID()))

		// Log messages that carol receives from bob.
		n.carolServer.intersect(createLogFunc("alice->bob->[carol]",
			n.carolChannelLink.ChanID()))
	}

	amount := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin)
	htlcAmt, totalTimelock, hops := generateHops(amount,
		testStartingHeight,
		n.firstBobChannelLink, n.carolChannelLink)

	// Wait for:
	// * HTLC add request to be sent from Alice to Bob.
	// * Alice<->Bob commitment states to be updated.
	// * HTLC add request to be propagated to Carol.
	// * Bob<->Carol commitment state to be updated.
	// * settle request to be sent back from Carol to Bob.
	// * Alice<->Bob commitment state to be updated.
	// * settle request to be sent back from Bob to Alice.
	// * Alice<->Bob commitment states to be updated.
	// * user notification to be sent.
	receiver := n.carolServer
	rhash, err := n.makePayment(n.aliceServer, n.carolServer,
		n.bobServer.PubKey(), hops, amount, htlcAmt,
		totalTimelock).Wait(30 * time.Second)
	if err != nil {
		t.Fatalf("unable to send payment: %v", err)
	}

	// Wait for Bob to receive the revocation.
	time.Sleep(100 * time.Millisecond)

	// Check that Carol invoice was settled and bandwidth of HTLC
	// links were changed.
	invoice, err := receiver.registry.LookupInvoice(rhash)
	if err != nil {
		t.Fatalf("unable to get invoice: %v", err)
	}
	if !invoice.Terms.Settled {
		t.Fatal("carol invoice haven't been settled")
	}

	expectedAliceBandwidth := aliceBandwidthBefore - htlcAmt
	if expectedAliceBandwidth != n.aliceChannelLink.Bandwidth() {
		t.Fatalf("channel bandwidth incorrect: expected %v, got %v",
			expectedAliceBandwidth, n.aliceChannelLink.Bandwidth())
	}

	expectedBobBandwidth1 := firstBobBandwidthBefore + htlcAmt
	if expectedBobBandwidth1 != n.firstBobChannelLink.Bandwidth() {
		t.Fatalf("channel bandwidth incorrect: expected %v, got %v",
			expectedBobBandwidth1, n.firstBobChannelLink.Bandwidth())
	}

	expectedBobBandwidth2 := secondBobBandwidthBefore - amount
	if expectedBobBandwidth2 != n.secondBobChannelLink.Bandwidth() {
		t.Fatalf("channel bandwidth incorrect: expected %v, got %v",
			expectedBobBandwidth2, n.secondBobChannelLink.Bandwidth())
	}

	expectedCarolBandwidth := carolBandwidthBefore + amount
	if expectedCarolBandwidth != n.carolChannelLink.Bandwidth() {
		t.Fatalf("channel bandwidth incorrect: expected %v, got %v",
			expectedCarolBandwidth, n.carolChannelLink.Bandwidth())
	}
}

// TestExitNodeTimelockPayloadMismatch tests that when an exit node receives an
// incoming HTLC, if the time lock encoded in the payload of the forwarded HTLC
// doesn't match the expected payment value, then the HTLC will be rejected
// with the appropriate error.
func TestExitNodeTimelockPayloadMismatch(t *testing.T) {
	t.Parallel()

	channels, cleanUp, _, err := createClusterChannels(
		btcutil.SatoshiPerBitcoin*5,
		btcutil.SatoshiPerBitcoin*5)
	if err != nil {
		t.Fatalf("unable to create channel: %v", err)
	}
	defer cleanUp()

	n := newThreeHopNetwork(t, channels.aliceToBob, channels.bobToAlice,
		channels.bobToCarol, channels.carolToBob, testStartingHeight)
	if err := n.start(); err != nil {
		t.Fatal(err)
	}
	defer n.stop()

	const amount = btcutil.SatoshiPerBitcoin
	htlcAmt, htlcExpiry, hops := generateHops(amount,
		testStartingHeight, n.firstBobChannelLink)

	// In order to exercise this case, we'll now _manually_ modify the
	// per-hop payload for outgoing time lock to be the incorrect value.
	// The proper value of the outgoing CLTV should be the policy set by
	// the receiving node, instead we set it to be a random value.
	hops[0].OutgoingCTLV = 500

	_, err = n.makePayment(n.aliceServer, n.bobServer,
		n.bobServer.PubKey(), hops, amount, htlcAmt,
		htlcExpiry).Wait(30 * time.Second)
	if err == nil {
		t.Fatalf("payment should have failed but didn't")
	}

	ferr, ok := err.(*ForwardingError)
	if !ok {
		t.Fatalf("expected a ForwardingError, instead got: %T", err)
	}

	switch ferr.FailureMessage.(type) {
	case *lnwire.FailFinalIncorrectCltvExpiry:
	default:
		t.Fatalf("incorrect error, expected incorrect cltv expiry, "+
			"instead have: %v", err)
	}
}

// TestExitNodeAmountPayloadMismatch tests that when an exit node receives an
// incoming HTLC, if the amount encoded in the onion payload of the forwarded
// HTLC doesn't match the expected payment value, then the HTLC will be
// rejected.
func TestExitNodeAmountPayloadMismatch(t *testing.T) {
	t.Parallel()

	channels, cleanUp, _, err := createClusterChannels(
		btcutil.SatoshiPerBitcoin*5,
		btcutil.SatoshiPerBitcoin*5)
	if err != nil {
		t.Fatalf("unable to create channel: %v", err)
	}
	defer cleanUp()

	n := newThreeHopNetwork(t, channels.aliceToBob, channels.bobToAlice,
		channels.bobToCarol, channels.carolToBob, testStartingHeight)
	if err := n.start(); err != nil {
		t.Fatal(err)
	}
	defer n.stop()

	const amount = btcutil.SatoshiPerBitcoin
	htlcAmt, htlcExpiry, hops := generateHops(amount, testStartingHeight,
		n.firstBobChannelLink)

	// In order to exercise this case, we'll now _manually_ modify the
	// per-hop payload for amount to be the incorrect value.  The proper
	// value of the amount to forward should be the amount that the
	// receiving node expects to receive.
	hops[0].AmountToForward = 1

	_, err = n.makePayment(n.aliceServer, n.bobServer,
		n.bobServer.PubKey(), hops, amount, htlcAmt,
		htlcExpiry).Wait(30 * time.Second)
	if err == nil {
		t.Fatalf("payment should have failed but didn't")
	} else if err.Error() != lnwire.CodeIncorrectPaymentAmount.String() {
		// TODO(roasbeef): use proper error after error propagation is
		// in
		t.Fatalf("incorrect error, expected insufficient value, "+
			"instead have: %v", err)
	}
}

// TestLinkForwardTimelockPolicyMismatch tests that if a node is an
// intermediate node in a multi-hop payment, and receives an HTLC which
// violates its specified multi-hop policy, then the HTLC is rejected.
func TestLinkForwardTimelockPolicyMismatch(t *testing.T) {
	t.Parallel()

	channels, cleanUp, _, err := createClusterChannels(
		btcutil.SatoshiPerBitcoin*5,
		btcutil.SatoshiPerBitcoin*5)
	if err != nil {
		t.Fatalf("unable to create channel: %v", err)
	}
	defer cleanUp()

	n := newThreeHopNetwork(t, channels.aliceToBob, channels.bobToAlice,
		channels.bobToCarol, channels.carolToBob, testStartingHeight)
	if err := n.start(); err != nil {
		t.Fatal(err)
	}
	defer n.stop()

	// We'll be sending 1 BTC over a 2-hop (3 vertex) route.
	amount := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin)

	// Generate the route over two hops, ignoring the total time lock that
	// we'll need to use for the first HTLC in order to have a sufficient
	// time-lock value to account for the decrements over the entire route.
	htlcAmt, htlcExpiry, hops := generateHops(amount, testStartingHeight,
		n.firstBobChannelLink, n.carolChannelLink)
	htlcExpiry -= 2

	// Next, we'll make the payment which'll send an HTLC with our
	// specified parameters to the first hop in the route.
	_, err = n.makePayment(n.aliceServer, n.carolServer,
		n.bobServer.PubKey(), hops, amount, htlcAmt,
		htlcExpiry).Wait(30 * time.Second)
	// We should get an error, and that error should indicate that the HTLC
	// should be rejected due to a policy violation.
	if err == nil {
		t.Fatalf("payment should have failed but didn't")
	}

	ferr, ok := err.(*ForwardingError)
	if !ok {
		t.Fatalf("expected a ForwardingError, instead got: %T", err)
	}

	switch ferr.FailureMessage.(type) {
	case *lnwire.FailIncorrectCltvExpiry:
	default:
		t.Fatalf("incorrect error, expected incorrect cltv expiry, "+
			"instead have: %v", err)
	}
}

// TestLinkForwardTimelockPolicyMismatch tests that if a node is an
// intermediate node in a multi-hop payment and receives an HTLC that violates
// its current fee policy, then the HTLC is rejected with the proper error.
func TestLinkForwardFeePolicyMismatch(t *testing.T) {
	t.Parallel()

	channels, cleanUp, _, err := createClusterChannels(
		btcutil.SatoshiPerBitcoin*3,
		btcutil.SatoshiPerBitcoin*5)
	if err != nil {
		t.Fatalf("unable to create channel: %v", err)
	}
	defer cleanUp()

	n := newThreeHopNetwork(t, channels.aliceToBob, channels.bobToAlice,
		channels.bobToCarol, channels.carolToBob, testStartingHeight)
	if err := n.start(); err != nil {
		t.Fatal(err)
	}
	defer n.stop()

	// We'll be sending 1 BTC over a 2-hop (3 vertex) route. Given the
	// current default fee of 1 SAT, if we just send a single BTC over in
	// an HTLC, it should be rejected.
	amountNoFee := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin)

	// Generate the route over two hops, ignoring the amount we _should_
	// actually send in order to be able to cover fees.
	_, htlcExpiry, hops := generateHops(amountNoFee, testStartingHeight,
		n.firstBobChannelLink, n.carolChannelLink)

	// Next, we'll make the payment which'll send an HTLC with our
	// specified parameters to the first hop in the route.
	_, err = n.makePayment(n.aliceServer, n.bobServer,
		n.bobServer.PubKey(), hops, amountNoFee, amountNoFee,
		htlcExpiry).Wait(30 * time.Second)

	// We should get an error, and that error should indicate that the HTLC
	// should be rejected due to a policy violation.
	if err == nil {
		t.Fatalf("payment should have failed but didn't")
	}

	ferr, ok := err.(*ForwardingError)
	if !ok {
		t.Fatalf("expected a ForwardingError, instead got: %T", err)
	}

	switch ferr.FailureMessage.(type) {
	case *lnwire.FailFeeInsufficient:
	default:
		t.Fatalf("incorrect error, expected fee insufficient, "+
			"instead have: %T", err)
	}
}

// TestLinkForwardFeePolicyMismatch tests that if a node is an intermediate
// node and receives an HTLC which is _below_ its min HTLC policy, then the
// HTLC will be rejected.
func TestLinkForwardMinHTLCPolicyMismatch(t *testing.T) {
	t.Parallel()

	channels, cleanUp, _, err := createClusterChannels(
		btcutil.SatoshiPerBitcoin*5,
		btcutil.SatoshiPerBitcoin*5)
	if err != nil {
		t.Fatalf("unable to create channel: %v", err)
	}
	defer cleanUp()

	n := newThreeHopNetwork(t, channels.aliceToBob, channels.bobToAlice,
		channels.bobToCarol, channels.carolToBob, testStartingHeight)
	if err := n.start(); err != nil {
		t.Fatal(err)
	}
	defer n.stop()

	// The current default global min HTLC policy set in the default config
	// for the three-hop-network is 5 SAT. So in order to trigger this
	// failure mode, we'll create an HTLC with 1 satoshi.
	amountNoFee := lnwire.NewMSatFromSatoshis(1)

	// With the amount set, we'll generate a route over 2 hops within the
	// network that attempts to pay out our specified amount.
	htlcAmt, htlcExpiry, hops := generateHops(amountNoFee, testStartingHeight,
		n.firstBobChannelLink, n.carolChannelLink)

	// Next, we'll make the payment which'll send an HTLC with our
	// specified parameters to the first hop in the route.
	_, err = n.makePayment(n.aliceServer, n.bobServer,
		n.bobServer.PubKey(), hops, amountNoFee, htlcAmt,
		htlcExpiry).Wait(30 * time.Second)

	// We should get an error, and that error should indicate that the HTLC
	// should be rejected due to a policy violation (below min HTLC).
	if err == nil {
		t.Fatalf("payment should have failed but didn't")
	}

	ferr, ok := err.(*ForwardingError)
	if !ok {
		t.Fatalf("expected a ForwardingError, instead got: %T", err)
	}

	switch ferr.FailureMessage.(type) {
	case *lnwire.FailAmountBelowMinimum:
	default:
		t.Fatalf("incorrect error, expected amount below minimum, "+
			"instead have: %v", err)
	}
}

// TestUpdateForwardingPolicy tests that the forwarding policy for a link is
// able to be updated properly. We'll first create an HTLC that meets the
// specified policy, assert that it succeeds, update the policy (to invalidate
// the prior HTLC), and then ensure that the HTLC is rejected.
func TestUpdateForwardingPolicy(t *testing.T) {
	t.Parallel()

	channels, cleanUp, _, err := createClusterChannels(
		btcutil.SatoshiPerBitcoin*5,
		btcutil.SatoshiPerBitcoin*5)
	if err != nil {
		t.Fatalf("unable to create channel: %v", err)
	}
	defer cleanUp()

	n := newThreeHopNetwork(t, channels.aliceToBob, channels.bobToAlice,
		channels.bobToCarol, channels.carolToBob, testStartingHeight)
	if err := n.start(); err != nil {
		t.Fatal(err)
	}
	defer n.stop()

	carolBandwidthBefore := n.carolChannelLink.Bandwidth()
	firstBobBandwidthBefore := n.firstBobChannelLink.Bandwidth()
	secondBobBandwidthBefore := n.secondBobChannelLink.Bandwidth()
	aliceBandwidthBefore := n.aliceChannelLink.Bandwidth()

	amountNoFee := lnwire.NewMSatFromSatoshis(10)
	htlcAmt, htlcExpiry, hops := generateHops(amountNoFee,
		testStartingHeight,
		n.firstBobChannelLink, n.carolChannelLink)

	// First, send this 1 BTC payment over the three hops, the payment
	// should succeed, and all balances should be updated accordingly.
	payResp, err := n.makePayment(n.aliceServer, n.carolServer,
		n.bobServer.PubKey(), hops, amountNoFee, htlcAmt,
		htlcExpiry).Wait(30 * time.Second)
	if err != nil {
		t.Fatalf("unable to send payment: %v", err)
	}

	// Carol's invoice should now be shown as settled as the payment
	// succeeded.
	invoice, err := n.carolServer.registry.LookupInvoice(payResp)
	if err != nil {
		t.Fatalf("unable to get invoice: %v", err)
	}
	if !invoice.Terms.Settled {
		t.Fatal("carol invoice haven't been settled")
	}

	expectedAliceBandwidth := aliceBandwidthBefore - htlcAmt
	if expectedAliceBandwidth != n.aliceChannelLink.Bandwidth() {
		t.Fatalf("channel bandwidth incorrect: expected %v, got %v",
			expectedAliceBandwidth, n.aliceChannelLink.Bandwidth())
	}
	expectedBobBandwidth1 := firstBobBandwidthBefore + htlcAmt
	if expectedBobBandwidth1 != n.firstBobChannelLink.Bandwidth() {
		t.Fatalf("channel bandwidth incorrect: expected %v, got %v",
			expectedBobBandwidth1, n.firstBobChannelLink.Bandwidth())
	}
	expectedBobBandwidth2 := secondBobBandwidthBefore - amountNoFee
	if expectedBobBandwidth2 != n.secondBobChannelLink.Bandwidth() {
		t.Fatalf("channel bandwidth incorrect: expected %v, got %v",
			expectedBobBandwidth2, n.secondBobChannelLink.Bandwidth())
	}
	expectedCarolBandwidth := carolBandwidthBefore + amountNoFee
	if expectedCarolBandwidth != n.carolChannelLink.Bandwidth() {
		t.Fatalf("channel bandwidth incorrect: expected %v, got %v",
			expectedCarolBandwidth, n.carolChannelLink.Bandwidth())
	}

	// Now we'll update Bob's policy to jack up his free rate to an extent
	// that'll cause him to reject the same HTLC that we just sent.
	//
	// TODO(roasbeef): should implement grace period within link policy
	// update logic
	newPolicy := n.globalPolicy
	newPolicy.BaseFee = lnwire.NewMSatFromSatoshis(1000)
	n.firstBobChannelLink.UpdateForwardingPolicy(newPolicy)

	// Next, we'll send the payment again, using the exact same per-hop
	// payload for each node. This payment should fail as it wont' factor
	// in Bob's new fee policy.
	_, err = n.makePayment(n.aliceServer, n.carolServer,
		n.bobServer.PubKey(), hops, amountNoFee, htlcAmt,
		htlcExpiry).Wait(30 * time.Second)
	if err == nil {
		t.Fatalf("payment should've been rejected")
	}

	ferr, ok := err.(*ForwardingError)
	if !ok {
		t.Fatalf("expected a ForwardingError, instead got: %T", err)
	}
	switch ferr.FailureMessage.(type) {
	case *lnwire.FailFeeInsufficient:
	default:
		t.Fatalf("expected FailFeeInsufficient instead got: %v", err)
	}
}

// TestChannelLinkMultiHopInsufficientPayment checks that we receive error if
// bob<->alice channel has insufficient BTC capacity/bandwidth. In this test we
// send the payment from Carol to Alice over Bob peer. (Carol -> Bob -> Alice)
func TestChannelLinkMultiHopInsufficientPayment(t *testing.T) {
	t.Parallel()

	channels, cleanUp, _, err := createClusterChannels(
		btcutil.SatoshiPerBitcoin*3,
		btcutil.SatoshiPerBitcoin*5)
	if err != nil {
		t.Fatalf("unable to create channel: %v", err)
	}
	defer cleanUp()

	n := newThreeHopNetwork(t, channels.aliceToBob, channels.bobToAlice,
		channels.bobToCarol, channels.carolToBob, testStartingHeight)
	if err := n.start(); err != nil {
		t.Fatalf("unable to start three hop network: %v", err)
	}
	defer n.stop()

	carolBandwidthBefore := n.carolChannelLink.Bandwidth()
	firstBobBandwidthBefore := n.firstBobChannelLink.Bandwidth()
	secondBobBandwidthBefore := n.secondBobChannelLink.Bandwidth()
	aliceBandwidthBefore := n.aliceChannelLink.Bandwidth()

	// We'll attempt to send 4 BTC although the alice-to-bob channel only
	// has 3 BTC total capacity. As a result, this payment should be
	// rejected.
	amount := lnwire.NewMSatFromSatoshis(4 * btcutil.SatoshiPerBitcoin)
	htlcAmt, totalTimelock, hops := generateHops(amount, testStartingHeight,
		n.firstBobChannelLink, n.carolChannelLink)

	// Wait for:
	// * HTLC add request to be sent to from Alice to Bob.
	// * Alice<->Bob commitment states to be updated.
	// * Bob trying to add HTLC add request in Bob<->Carol channel.
	// * Cancel HTLC request to be sent back from Bob to Alice.
	// * user notification to be sent.

	receiver := n.carolServer
	rhash, err := n.makePayment(n.aliceServer, n.carolServer,
		n.bobServer.PubKey(), hops, amount, htlcAmt,
		totalTimelock).Wait(30 * time.Second)
	if err == nil {
		t.Fatal("error haven't been received")
	} else if !strings.Contains(err.Error(), "insufficient capacity") {
		t.Fatalf("wrong error has been received: %v", err)
	}

	// Wait for Alice to receive the revocation.
	//
	// TODO(roasbeef): add in ntfn hook for state transition completion
	time.Sleep(100 * time.Millisecond)

	// Check that alice invoice wasn't settled and bandwidth of htlc
	// links hasn't been changed.
	invoice, err := receiver.registry.LookupInvoice(rhash)
	if err != nil {
		t.Fatalf("unable to get invoice: %v", err)
	}
	if invoice.Terms.Settled {
		t.Fatal("carol invoice have been settled")
	}

	if n.aliceChannelLink.Bandwidth() != aliceBandwidthBefore {
		t.Fatal("the bandwidth of alice channel link which handles " +
			"alice->bob channel should be the same")
	}

	if n.firstBobChannelLink.Bandwidth() != firstBobBandwidthBefore {
		t.Fatal("the bandwidth of bob channel link which handles " +
			"alice->bob channel should be the same")
	}

	if n.secondBobChannelLink.Bandwidth() != secondBobBandwidthBefore {
		t.Fatal("the bandwidth of bob channel link which handles " +
			"bob->carol channel should be the same")
	}

	if n.carolChannelLink.Bandwidth() != carolBandwidthBefore {
		t.Fatal("the bandwidth of carol channel link which handles " +
			"bob->carol channel should be the same")
	}
}

// TestChannelLinkMultiHopUnknownPaymentHash checks that we receive remote error
// from Alice if she received not suitable payment hash for htlc.
func TestChannelLinkMultiHopUnknownPaymentHash(t *testing.T) {
	t.Parallel()

	channels, cleanUp, _, err := createClusterChannels(
		btcutil.SatoshiPerBitcoin*5,
		btcutil.SatoshiPerBitcoin*5)
	if err != nil {
		t.Fatalf("unable to create channel: %v", err)
	}
	defer cleanUp()

	n := newThreeHopNetwork(t, channels.aliceToBob, channels.bobToAlice,
		channels.bobToCarol, channels.carolToBob, testStartingHeight)
	if err := n.start(); err != nil {
		t.Fatalf("unable to start three hop network: %v", err)
	}
	defer n.stop()

	carolBandwidthBefore := n.carolChannelLink.Bandwidth()
	firstBobBandwidthBefore := n.firstBobChannelLink.Bandwidth()
	secondBobBandwidthBefore := n.secondBobChannelLink.Bandwidth()
	aliceBandwidthBefore := n.aliceChannelLink.Bandwidth()

	amount := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin)

	htlcAmt, totalTimelock, hops := generateHops(amount, testStartingHeight,
		n.firstBobChannelLink, n.carolChannelLink)
	blob, err := generateRoute(hops...)
	if err != nil {
		t.Fatal(err)
	}

	// Generate payment: invoice and htlc.
	invoice, htlc, err := generatePayment(amount, htlcAmt, totalTimelock,
		blob)
	if err != nil {
		t.Fatal(err)
	}

	// We need to have wrong rhash for that reason we should change the
	// preimage. Inverse first byte by xoring with 0xff.
	invoice.Terms.PaymentPreimage[0] ^= byte(255)

	// Check who is last in the route and add invoice to server registry.
	if err := n.carolServer.registry.AddInvoice(*invoice); err != nil {
		t.Fatalf("unable to add invoice in carol registry: %v", err)
	}

	// Send payment and expose err channel.
	_, err = n.aliceServer.htlcSwitch.SendHTLC(n.bobServer.PubKey(), htlc,
		newMockDeobfuscator())
	if err.Error() != lnwire.CodeUnknownPaymentHash.String() {
		t.Fatal("error haven't been received")
	}

	// Wait for Alice to receive the revocation.
	time.Sleep(100 * time.Millisecond)

	// Check that alice invoice wasn't settled and bandwidth of htlc
	// links hasn't been changed.
	if invoice.Terms.Settled {
		t.Fatal("alice invoice was settled")
	}

	if n.aliceChannelLink.Bandwidth() != aliceBandwidthBefore {
		t.Fatal("the bandwidth of alice channel link which handles " +
			"alice->bob channel should be the same")
	}

	if n.firstBobChannelLink.Bandwidth() != firstBobBandwidthBefore {
		t.Fatal("the bandwidth of bob channel link which handles " +
			"alice->bob channel should be the same")
	}

	if n.secondBobChannelLink.Bandwidth() != secondBobBandwidthBefore {
		t.Fatal("the bandwidth of bob channel link which handles " +
			"bob->carol channel should be the same")
	}

	if n.carolChannelLink.Bandwidth() != carolBandwidthBefore {
		t.Fatal("the bandwidth of carol channel link which handles " +
			"bob->carol channel should be the same")
	}
}

// TestChannelLinkMultiHopUnknownNextHop construct the chain of hops
// Carol<->Bob<->Alice and checks that we receive remote error from Bob if he
// has no idea about next hop (hop might goes down and routing info not updated
// yet).
func TestChannelLinkMultiHopUnknownNextHop(t *testing.T) {
	t.Parallel()

	channels, cleanUp, _, err := createClusterChannels(
		btcutil.SatoshiPerBitcoin*5,
		btcutil.SatoshiPerBitcoin*5)
	if err != nil {
		t.Fatalf("unable to create channel: %v", err)
	}
	defer cleanUp()

	n := newThreeHopNetwork(t, channels.aliceToBob, channels.bobToAlice,
		channels.bobToCarol, channels.carolToBob, testStartingHeight)
	if err := n.start(); err != nil {
		t.Fatal(err)
	}
	defer n.stop()

	carolBandwidthBefore := n.carolChannelLink.Bandwidth()
	firstBobBandwidthBefore := n.firstBobChannelLink.Bandwidth()
	secondBobBandwidthBefore := n.secondBobChannelLink.Bandwidth()
	aliceBandwidthBefore := n.aliceChannelLink.Bandwidth()

	amount := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin)
	htlcAmt, totalTimelock, hops := generateHops(amount, testStartingHeight,
		n.firstBobChannelLink, n.carolChannelLink)

	davePub := newMockServer(t, "dave").PubKey()
	receiver := n.bobServer
	rhash, err := n.makePayment(n.aliceServer, n.bobServer, davePub, hops,
		amount, htlcAmt, totalTimelock).Wait(30 * time.Second)
	if err == nil {
		t.Fatal("error haven't been received")
	} else if err.Error() != lnwire.CodeUnknownNextPeer.String() {
		t.Fatalf("wrong error have been received: %v", err)
	}

	// Wait for Alice to receive the revocation.
	//
	// TODO(roasbeef): add in ntfn hook for state transition completion
	time.Sleep(100 * time.Millisecond)

	// Check that alice invoice wasn't settled and bandwidth of htlc
	// links hasn't been changed.
	invoice, err := receiver.registry.LookupInvoice(rhash)
	if err != nil {
		t.Fatalf("unable to get invoice: %v", err)
	}
	if invoice.Terms.Settled {
		t.Fatal("carol invoice have been settled")
	}

	if n.aliceChannelLink.Bandwidth() != aliceBandwidthBefore {
		t.Fatal("the bandwidth of alice channel link which handles " +
			"alice->bob channel should be the same")
	}

	if n.firstBobChannelLink.Bandwidth() != firstBobBandwidthBefore {
		t.Fatal("the bandwidth of bob channel link which handles " +
			"alice->bob channel should be the same")
	}

	if n.secondBobChannelLink.Bandwidth() != secondBobBandwidthBefore {
		t.Fatal("the bandwidth of bob channel link which handles " +
			"bob->carol channel should be the same")
	}

	if n.carolChannelLink.Bandwidth() != carolBandwidthBefore {
		t.Fatal("the bandwidth of carol channel link which handles " +
			"bob->carol channel should be the same")
	}
}

// TestChannelLinkMultiHopDecodeError checks that we send HTLC cancel if
// decoding of onion blob failed.
func TestChannelLinkMultiHopDecodeError(t *testing.T) {
	t.Parallel()

	channels, cleanUp, _, err := createClusterChannels(
		btcutil.SatoshiPerBitcoin*3,
		btcutil.SatoshiPerBitcoin*5)
	if err != nil {
		t.Fatalf("unable to create channel: %v", err)
	}
	defer cleanUp()

	n := newThreeHopNetwork(t, channels.aliceToBob, channels.bobToAlice,
		channels.bobToCarol, channels.carolToBob, testStartingHeight)
	if err := n.start(); err != nil {
		t.Fatalf("unable to start three hop network: %v", err)
	}
	defer n.stop()

	// Replace decode function with another which throws an error.
	n.carolChannelLink.cfg.DecodeOnionObfuscator = func(
		r io.Reader) (ErrorEncrypter, lnwire.FailCode) {
		return nil, lnwire.CodeInvalidOnionVersion
	}

	carolBandwidthBefore := n.carolChannelLink.Bandwidth()
	firstBobBandwidthBefore := n.firstBobChannelLink.Bandwidth()
	secondBobBandwidthBefore := n.secondBobChannelLink.Bandwidth()
	aliceBandwidthBefore := n.aliceChannelLink.Bandwidth()

	amount := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin)
	htlcAmt, totalTimelock, hops := generateHops(amount, testStartingHeight,
		n.firstBobChannelLink, n.carolChannelLink)

	receiver := n.carolServer
	rhash, err := n.makePayment(n.aliceServer, n.carolServer,
		n.bobServer.PubKey(), hops, amount, htlcAmt,
		totalTimelock).Wait(30 * time.Second)
	if err == nil {
		t.Fatal("error haven't been received")
	}

	ferr, ok := err.(*ForwardingError)
	if !ok {
		t.Fatalf("expected a ForwardingError, instead got: %T", err)
	}

	switch ferr.FailureMessage.(type) {
	case *lnwire.FailInvalidOnionVersion:
	default:
		t.Fatalf("wrong error have been received: %v", err)
	}

	// Wait for Bob to receive the revocation.
	time.Sleep(100 * time.Millisecond)

	// Check that alice invoice wasn't settled and bandwidth of htlc
	// links hasn't been changed.
	invoice, err := receiver.registry.LookupInvoice(rhash)
	if err != nil {
		t.Fatalf("unable to get invoice: %v", err)
	}
	if invoice.Terms.Settled {
		t.Fatal("carol invoice have been settled")
	}

	if n.aliceChannelLink.Bandwidth() != aliceBandwidthBefore {
		t.Fatal("the bandwidth of alice channel link which handles " +
			"alice->bob channel should be the same")
	}

	if n.firstBobChannelLink.Bandwidth() != firstBobBandwidthBefore {
		t.Fatal("the bandwidth of bob channel link which handles " +
			"alice->bob channel should be the same")
	}

	if n.secondBobChannelLink.Bandwidth() != secondBobBandwidthBefore {
		t.Fatal("the bandwidth of bob channel link which handles " +
			"bob->carol channel should be the same")
	}

	if n.carolChannelLink.Bandwidth() != carolBandwidthBefore {
		t.Fatal("the bandwidth of carol channel link which handles " +
			"bob->carol channel should be the same")
	}
}

// TestChannelLinkExpiryTooSoonExitNode tests that if we send an HTLC to a node
// with an expiry that is already expired, or too close to the current block
// height, then it will cancel the HTLC.
func TestChannelLinkExpiryTooSoonExitNode(t *testing.T) {
	t.Parallel()

	// The starting height for this test will be 200. So we'll base all
	// HTLC starting points off of that.
	channels, cleanUp, _, err := createClusterChannels(
		btcutil.SatoshiPerBitcoin*3,
		btcutil.SatoshiPerBitcoin*5)
	if err != nil {
		t.Fatalf("unable to create channel: %v", err)
	}
	defer cleanUp()

	const startingHeight = 200
	n := newThreeHopNetwork(t, channels.aliceToBob, channels.bobToAlice,
		channels.bobToCarol, channels.carolToBob, startingHeight)
	if err := n.start(); err != nil {
		t.Fatalf("unable to start three hop network: %v", err)
	}
	defer n.stop()

	amount := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin)

	// We'll craft an HTLC packet, but set the starting height to 10 blocks
	// before the current true height.
	htlcAmt, totalTimelock, hops := generateHops(amount,
		startingHeight-10, n.firstBobChannelLink)

	// Now we'll send out the payment from Alice to Bob.
	_, err = n.makePayment(n.aliceServer, n.bobServer,
		n.bobServer.PubKey(), hops, amount, htlcAmt,
		totalTimelock).Wait(30 * time.Second)

	// The payment should've failed as the time lock value was in the
	// _past_.
	if err == nil {
		t.Fatalf("payment should have failed due to a too early " +
			"time lock value")
	}

	ferr, ok := err.(*ForwardingError)
	if !ok {
		t.Fatalf("expected a ForwardingError, instead got: %T %v",
			err, err)
	}

	switch ferr.FailureMessage.(type) {
	case *lnwire.FailFinalIncorrectCltvExpiry:
	default:
		t.Fatalf("incorrect error, expected final time lock too "+
			"early, instead have: %v", err)
	}
}

// TestChannelLinkExpiryTooSoonExitNode tests that if we send a multi-hop HTLC,
// and the time lock is too early for an intermediate node, then they cancel
// the HTLC back to the sender.
func TestChannelLinkExpiryTooSoonMidNode(t *testing.T) {
	t.Parallel()

	// The starting height for this test will be 200. So we'll base all
	// HTLC starting points off of that.
	channels, cleanUp, _, err := createClusterChannels(
		btcutil.SatoshiPerBitcoin*3,
		btcutil.SatoshiPerBitcoin*5)
	if err != nil {
		t.Fatalf("unable to create channel: %v", err)
	}
	defer cleanUp()

	const startingHeight = 200
	n := newThreeHopNetwork(t, channels.aliceToBob, channels.bobToAlice,
		channels.bobToCarol, channels.carolToBob, startingHeight)
	if err := n.start(); err != nil {
		t.Fatalf("unable to start three hop network: %v", err)
	}
	defer n.stop()

	amount := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin)

	// We'll craft an HTLC packet, but set the starting height to 10 blocks
	// before the current true height. The final route will be three hops,
	// so the middle hop should detect the issue.
	htlcAmt, totalTimelock, hops := generateHops(amount,
		startingHeight-10, n.firstBobChannelLink, n.carolChannelLink)

	// Now we'll send out the payment from Alice to Bob.
	_, err = n.makePayment(n.aliceServer, n.bobServer,
		n.bobServer.PubKey(), hops, amount, htlcAmt,
		totalTimelock).Wait(30 * time.Second)

	// The payment should've failed as the time lock value was in the
	// _past_.
	if err == nil {
		t.Fatalf("payment should have failed due to a too early " +
			"time lock value")
	}

	ferr, ok := err.(*ForwardingError)
	if !ok {
		t.Fatalf("expected a ForwardingError, instead got: %T: %v", err, err)
	}

	switch ferr.FailureMessage.(type) {
	case *lnwire.FailExpiryTooSoon:
	default:
		t.Fatalf("incorrect error, expected final time lock too "+
			"early, instead have: %v", err)
	}
}

// TestChannelLinkSingleHopMessageOrdering test checks ordering of message which
// flying around between Alice and Bob are correct when Bob sends payments to
// Alice.
func TestChannelLinkSingleHopMessageOrdering(t *testing.T) {
	t.Parallel()

	channels, cleanUp, _, err := createClusterChannels(
		btcutil.SatoshiPerBitcoin*3,
		btcutil.SatoshiPerBitcoin*5)
	if err != nil {
		t.Fatalf("unable to create channel: %v", err)
	}
	defer cleanUp()

	n := newThreeHopNetwork(t, channels.aliceToBob, channels.bobToAlice,
		channels.bobToCarol, channels.carolToBob, testStartingHeight)

	chanID := n.aliceChannelLink.ChanID()

	messages := []expectedMessage{
		{"alice", "bob", &lnwire.ChannelReestablish{}, false},
		{"bob", "alice", &lnwire.ChannelReestablish{}, false},

		{"alice", "bob", &lnwire.FundingLocked{}, false},
		{"bob", "alice", &lnwire.FundingLocked{}, false},

		{"alice", "bob", &lnwire.UpdateAddHTLC{}, false},
		{"alice", "bob", &lnwire.CommitSig{}, false},
		{"bob", "alice", &lnwire.RevokeAndAck{}, false},
		{"bob", "alice", &lnwire.CommitSig{}, false},
		{"alice", "bob", &lnwire.RevokeAndAck{}, false},

		{"bob", "alice", &lnwire.UpdateFulfillHTLC{}, false},
		{"bob", "alice", &lnwire.CommitSig{}, false},
		{"alice", "bob", &lnwire.RevokeAndAck{}, false},
		{"alice", "bob", &lnwire.CommitSig{}, false},
		{"bob", "alice", &lnwire.RevokeAndAck{}, false},
	}

	debug := false
	if debug {
		// Log message that alice receives.
		n.aliceServer.intersect(createLogFunc("alice",
			n.aliceChannelLink.ChanID()))

		// Log message that bob receives.
		n.bobServer.intersect(createLogFunc("bob",
			n.firstBobChannelLink.ChanID()))
	}

	// Check that alice receives messages in right order.
	n.aliceServer.intersect(createInterceptorFunc("[alice] <-- [bob]",
		"alice", messages, chanID, false))

	// Check that bob receives messages in right order.
	n.bobServer.intersect(createInterceptorFunc("[alice] --> [bob]",
		"bob", messages, chanID, false))

	if err := n.start(); err != nil {
		t.Fatalf("unable to start three hop network: %v", err)
	}
	defer n.stop()

	amount := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin)
	htlcAmt, totalTimelock, hops := generateHops(amount, testStartingHeight,
		n.firstBobChannelLink)

	// Wait for:
	// * HTLC add request to be sent to bob.
	// * alice<->bob commitment state to be updated.
	// * settle request to be sent back from bob to alice.
	// * alice<->bob commitment state to be updated.
	// * user notification to be sent.
	_, err = n.makePayment(n.aliceServer, n.bobServer,
		n.bobServer.PubKey(), hops, amount, htlcAmt,
		totalTimelock).Wait(30 * time.Second)
	if err != nil {
		t.Fatalf("unable to make the payment: %v", err)
	}
}

type mockPeer struct {
	sync.Mutex
	sentMsgs chan lnwire.Message
	quit     chan struct{}
}

func (m *mockPeer) SendMessage(msg lnwire.Message) error {
	select {
	case m.sentMsgs <- msg:
	case <-m.quit:
		return fmt.Errorf("mockPeer shutting down")
	}
	return nil
}
func (m *mockPeer) WipeChannel(*wire.OutPoint) error {
	return nil
}
func (m *mockPeer) PubKey() [33]byte {
	return [33]byte{}
}
func (m *mockPeer) Disconnect(reason error) {
}

var _ Peer = (*mockPeer)(nil)

func newSingleLinkTestHarness(chanAmt, chanReserve btcutil.Amount) (
	ChannelLink, *lnwallet.LightningChannel, chan time.Time, func(), error) {
	globalEpoch := &chainntnfs.BlockEpochEvent{
		Epochs: make(chan *chainntnfs.BlockEpoch),
		Cancel: func() {
		},
	}

	chanID := lnwire.NewShortChanIDFromInt(4)
	aliceChannel, bobChannel, fCleanUp, _, err := createTestChannel(
		alicePrivKey, bobPrivKey, chanAmt, chanAmt,
		chanReserve, chanReserve, chanID,
	)
	if err != nil {
		return nil, nil, nil, nil, err
	}

	var (
		invoiceRegistry = newMockRegistry()
		decoder         = &mockIteratorDecoder{}
		obfuscator      = newMockObfuscator()
		alicePeer       = &mockPeer{
			sentMsgs: make(chan lnwire.Message, 2000),
			quit:     make(chan struct{}),
		}

		globalPolicy = ForwardingPolicy{
			MinHTLC:       lnwire.NewMSatFromSatoshis(5),
			BaseFee:       lnwire.NewMSatFromSatoshis(1),
			TimeLockDelta: 6,
		}
	)

	pCache := &mockPreimageCache{
		// hash -> preimage
		preimageMap: make(map[[32]byte][]byte),
	}

	t := make(chan time.Time)
	ticker := &mockTicker{t}
	aliceCfg := ChannelLinkConfig{
		FwrdingPolicy:     globalPolicy,
		Peer:              alicePeer,
		Switch:            New(Config{}),
		DecodeHopIterator: decoder.DecodeHopIterator,
		DecodeOnionObfuscator: func(io.Reader) (ErrorEncrypter, lnwire.FailCode) {
			return obfuscator, lnwire.CodeNone
		},
		GetLastChannelUpdate: mockGetChanUpdateMessage,
		PreimageCache:        pCache,
		UpdateContractSignals: func(*contractcourt.ContractSignals) error {
			return nil
		},
		Registry:    invoiceRegistry,
		ChainEvents: &contractcourt.ChainEventSubscription{},
		BlockEpochs: globalEpoch,
		BatchTicker: ticker,
		// Make the BatchSize large enough to not
		// trigger commit update automatically during tests.
		BatchSize: 10000,
	}

	const startingHeight = 100
	aliceLink := NewChannelLink(aliceCfg, aliceChannel, startingHeight)
	if err := aliceLink.Start(); err != nil {
		return nil, nil, nil, nil, err
	}
	go func() {
		for {
			select {
			case <-aliceLink.(*channelLink).htlcUpdates:
			case <-aliceLink.(*channelLink).quit:
				return
			}
		}
	}()

	cleanUp := func() {
		close(alicePeer.quit)
		defer fCleanUp()
		defer aliceLink.Stop()
		defer bobChannel.Stop()
	}

	return aliceLink, bobChannel, t, cleanUp, nil
}

func assertLinkBandwidth(t *testing.T, link ChannelLink,
	expected lnwire.MilliSatoshi) {

	currentBandwidth := link.Bandwidth()
	_, _, line, _ := runtime.Caller(1)
	if currentBandwidth != expected {
		t.Fatalf("line %v: alice's link bandwidth is incorrect: "+
			"expected %v, got %v", line, expected, currentBandwidth)
	}
}

// handleStateUpdate handles the messages sent from the link after
// the batch ticker has triggered a state update.
func handleStateUpdate(link *channelLink,
	remoteChannel *lnwallet.LightningChannel) error {
	sentMsgs := link.cfg.Peer.(*mockPeer).sentMsgs
	var msg lnwire.Message
	select {
	case msg = <-sentMsgs:
	case <-time.After(20 * time.Second):
		return fmt.Errorf("did not receive CommitSig from Alice")
	}

	// The link should be sending a commit sig at this point.
	commitSig, ok := msg.(*lnwire.CommitSig)
	if !ok {
		return fmt.Errorf("expected CommitSig, got %T", msg)
	}

	// Let the remote channel receive the commit sig, and
	// respond with a revocation + commitsig.
	err := remoteChannel.ReceiveNewCommitment(
		commitSig.CommitSig, commitSig.HtlcSigs)
	if err != nil {
		return err
	}

	remoteRev, _, err := remoteChannel.RevokeCurrentCommitment()
	if err != nil {
		return err
	}
	link.HandleChannelUpdate(remoteRev)

	remoteSig, remoteHtlcSigs, err := remoteChannel.SignNextCommitment()
	if err != nil {
		return err
	}
	commitSig = &lnwire.CommitSig{
		CommitSig: remoteSig,
		HtlcSigs:  remoteHtlcSigs,
	}
	link.HandleChannelUpdate(commitSig)

	// This should make the link respond with a revocation.
	select {
	case msg = <-sentMsgs:
	case <-time.After(20 * time.Second):
		return fmt.Errorf("did not receive RevokeAndAck from Alice")
	}

	revoke, ok := msg.(*lnwire.RevokeAndAck)
	if !ok {
		return fmt.Errorf("expected RevokeAndAck got %T", msg)
	}
	_, err = remoteChannel.ReceiveRevocation(revoke)
	if err != nil {
		return fmt.Errorf("unable to recieve "+
			"revocation: %v", err)
	}

	return nil
}

// updateState is used exchange the messages necessary to do a full state
// transition. If initiateUpdate=true, then this call will make the link
// trigger an update by sending on the batchTick channel, if not, it will
// make the remoteChannel initiate the state update.
func updateState(batchTick chan time.Time, link *channelLink,
	remoteChannel *lnwallet.LightningChannel,
	initiateUpdate bool) error {
	sentMsgs := link.cfg.Peer.(*mockPeer).sentMsgs

	if initiateUpdate {
		// Trigger update by ticking the batchTicker.
		select {
		case batchTick <- time.Now():
		case <-link.quit:
			return fmt.Errorf("link shuttin down")
		}
		return handleStateUpdate(link, remoteChannel)
	}

	// The remote is triggering the state update, emulate this by
	// signing and sending CommitSig to the link.
	remoteSig, remoteHtlcSigs, err := remoteChannel.SignNextCommitment()
	if err != nil {
		return err
	}

	commitSig := &lnwire.CommitSig{
		CommitSig: remoteSig,
		HtlcSigs:  remoteHtlcSigs,
	}
	link.HandleChannelUpdate(commitSig)

	// The link should respond with a revocation + commit sig.
	var msg lnwire.Message
	select {
	case msg = <-sentMsgs:
	case <-time.After(20 * time.Second):
		return fmt.Errorf("did not receive RevokeAndAck from Alice")
	}

	revoke, ok := msg.(*lnwire.RevokeAndAck)
	if !ok {
		return fmt.Errorf("expected RevokeAndAck got %T",
			msg)
	}
	_, err = remoteChannel.ReceiveRevocation(revoke)
	if err != nil {
		return fmt.Errorf("unable to recieve "+
			"revocation: %v", err)
	}
	select {
	case msg = <-sentMsgs:
	case <-time.After(20 * time.Second):
		return fmt.Errorf("did not receive CommitSig from Alice")
	}

	commitSig, ok = msg.(*lnwire.CommitSig)
	if !ok {
		return fmt.Errorf("expected CommitSig, got %T", msg)
	}

	err = remoteChannel.ReceiveNewCommitment(
		commitSig.CommitSig, commitSig.HtlcSigs)
	if err != nil {
		return err
	}

	// Lastly, send a revocation back to the link.
	remoteRev, _, err := remoteChannel.RevokeCurrentCommitment()
	if err != nil {
		return err
	}
	link.HandleChannelUpdate(remoteRev)

	// Sleep to make sure Alice has handled the remote revocation.
	time.Sleep(500 * time.Millisecond)

	return nil
}

// TestChannelLinkBandwidthConsistency ensures that the reported bandwidth of a
// given ChannelLink is properly updated in response to downstream messages
// from the switch, and upstream messages from its channel peer.
//
// TODO(roasbeef): add sync hook into packet processing so can eliminate all
// sleep in this test and the one below
func TestChannelLinkBandwidthConsistency(t *testing.T) {
	t.Parallel()

	// TODO(roasbeef): replace manual bit twiddling with concept of
	// resource cost for packets?
	//  * or also able to consult link

	// We'll start the test by creating a single instance of
	const chanAmt = btcutil.SatoshiPerBitcoin * 5
	link, bobChannel, tmr, cleanUp, err := newSingleLinkTestHarness(chanAmt, 0)
	if err != nil {
		t.Fatalf("unable to create link: %v", err)
	}
	defer cleanUp()

	var (
		mockBlob               [lnwire.OnionPacketSize]byte
		aliceLink              = link.(*channelLink)
		aliceChannel           = aliceLink.channel
		defaultCommitFee       = aliceChannel.StateSnapshot().CommitFee
		aliceStartingBandwidth = aliceLink.Bandwidth()
		aliceMsgs              = aliceLink.cfg.Peer.(*mockPeer).sentMsgs
	)

	// We put Alice into HodlHTLC mode, such that she won't settle
	// incoming HTLCs automatically.
	aliceLink.cfg.HodlHTLC = true
	aliceLink.cfg.DebugHTLC = true

	estimator := &lnwallet.StaticFeeEstimator{
		FeeRate: 24,
	}
	feePerWeight, err := estimator.EstimateFeePerWeight(1)
	if err != nil {
		t.Fatalf("unable to query fee estimator: %v", err)
	}
	feePerKw := feePerWeight * 1000
	htlcFee := lnwire.NewMSatFromSatoshis(
		btcutil.Amount((int64(feePerKw) * lnwallet.HtlcWeight) / 1000),
	)

	// The starting bandwidth of the channel should be exactly the amount
	// that we created the channel between her and Bob.
	expectedBandwidth := lnwire.NewMSatFromSatoshis(chanAmt - defaultCommitFee)
	assertLinkBandwidth(t, aliceLink, expectedBandwidth)

	// Next, we'll create an HTLC worth 1 BTC, and send it into the link as
	// a switch initiated payment.  The resulting bandwidth should
	// now be decremented to reflect the new HTLC.
	htlcAmt := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin)
	invoice, htlc, err := generatePayment(htlcAmt, htlcAmt, 5, mockBlob)
	if err != nil {
		t.Fatalf("unable to create payment: %v", err)
	}
	addPkt := htlcPacket{
		htlc: htlc,
	}
	aliceLink.HandleSwitchPacket(&addPkt)
	time.Sleep(time.Millisecond * 500)

	// The resulting bandwidth should reflect that Alice is paying the
	// htlc amount in addition to the htlc fee.
	assertLinkBandwidth(t, aliceLink, aliceStartingBandwidth-htlcAmt-htlcFee)

	// Alice should send the HTLC to Bob.
	var msg lnwire.Message
	select {
	case msg = <-aliceMsgs:
	case <-time.After(2 * time.Second):
		t.Fatalf("did not receive message")
	}

	addHtlc, ok := msg.(*lnwire.UpdateAddHTLC)
	if !ok {
		t.Fatalf("expected UpdateAddHTLC, got %T", msg)
	}

	bobIndex, err := bobChannel.ReceiveHTLC(addHtlc)
	if err != nil {
		t.Fatalf("bob failed receiving htlc: %v", err)
	}

	// Lock in the HTLC.
	if err := updateState(tmr, aliceLink, bobChannel, true); err != nil {
		t.Fatalf("unable to update state: %v", err)
	}

	// Locking in the HTLC should not change Alice's bandwidth.
	assertLinkBandwidth(t, aliceLink, aliceStartingBandwidth-htlcAmt-htlcFee)

	// If we now send in a valid HTLC settle for the prior HTLC we added,
	// then the bandwidth should remain unchanged as the remote party will
	// gain additional channel balance.
	err = bobChannel.SettleHTLC(invoice.Terms.PaymentPreimage, bobIndex)
	if err != nil {
		t.Fatalf("unable to settle htlc: %v", err)
	}
	htlcSettle := &lnwire.UpdateFulfillHTLC{
		ID:              bobIndex,
		PaymentPreimage: invoice.Terms.PaymentPreimage,
	}
	aliceLink.HandleChannelUpdate(htlcSettle)
	time.Sleep(time.Millisecond * 500)

	// Since the settle is not locked in yet, Alice's bandwidth should still
	// reflect that she has to pay the fee.
	assertLinkBandwidth(t, aliceLink, aliceStartingBandwidth-htlcAmt-htlcFee)

	// Lock in the settle.
	if err := updateState(tmr, aliceLink, bobChannel, false); err != nil {
		t.Fatalf("unable to update state: %v", err)
	}

	// Now that it is settled, Alice should have gotten the htlc fee back.
	assertLinkBandwidth(t, aliceLink, aliceStartingBandwidth-htlcAmt)

	// Next, we'll add another HTLC initiated by the switch (of the same
	// amount as the prior one).
	invoice, htlc, err = generatePayment(htlcAmt, htlcAmt, 5, mockBlob)
	if err != nil {
		t.Fatalf("unable to create payment: %v", err)
	}
	addPkt = htlcPacket{
		htlc: htlc,
	}
	aliceLink.HandleSwitchPacket(&addPkt)
	time.Sleep(time.Millisecond * 500)

	// Again, Alice's bandwidth decreases by htlcAmt+htlcFee.
	assertLinkBandwidth(t, aliceLink, aliceStartingBandwidth-2*htlcAmt-htlcFee)

	// Alice will send the HTLC to Bob.
	select {
	case msg = <-aliceMsgs:
	case <-time.After(2 * time.Second):
		t.Fatalf("did not receive message")
	}
	addHtlc, ok = msg.(*lnwire.UpdateAddHTLC)
	if !ok {
		t.Fatalf("expected UpdateAddHTLC, got %T", msg)
	}

	bobIndex, err = bobChannel.ReceiveHTLC(addHtlc)
	if err != nil {
		t.Fatalf("bob failed receiving htlc: %v", err)
	}

	// Lock in the HTLC, which should not affect the bandwidth.
	if err := updateState(tmr, aliceLink, bobChannel, true); err != nil {
		t.Fatalf("unable to update state: %v", err)
	}

	assertLinkBandwidth(t, aliceLink, aliceStartingBandwidth-htlcAmt*2-htlcFee)

	// With that processed, we'll now generate an HTLC fail (sent by the
	// remote peer) to cancel the HTLC we just added. This should return us
	// back to the bandwidth of the link right before the HTLC was sent.
	err = bobChannel.FailHTLC(bobIndex, []byte("nop"))
	if err != nil {
		t.Fatalf("unable to fail htlc: %v", err)
	}
	failMsg := &lnwire.UpdateFailHTLC{
		ID:     bobIndex,
		Reason: lnwire.OpaqueReason([]byte("nop")),
	}
	aliceLink.HandleChannelUpdate(failMsg)
	time.Sleep(time.Millisecond * 500)

	// Before the Fail gets locked in, the bandwidth should remain unchanged.
	assertLinkBandwidth(t, aliceLink, aliceStartingBandwidth-htlcAmt*2-htlcFee)

	// Lock in the Fail.
	if err := updateState(tmr, aliceLink, bobChannel, false); err != nil {
		t.Fatalf("unable to update state: %v", err)
	}

	// Now the bancdwidth should reflect the failed HTLC.
	assertLinkBandwidth(t, aliceLink, aliceStartingBandwidth-htlcAmt)

	// Moving along, we'll now receive a new HTLC from the remote peer,
	// with an ID of 0 as this is their first HTLC. The bandwidth should
	// remain unchanged (but Alice will need to pay the fee for the extra
	// HTLC).
	htlcAmt, totalTimelock, hops := generateHops(htlcAmt, testStartingHeight,
		aliceLink)
	blob, err := generateRoute(hops...)
	if err != nil {
		t.Fatalf("unable to gen route: %v", err)
	}
	invoice, htlc, err = generatePayment(htlcAmt, htlcAmt,
		totalTimelock, blob)
	if err != nil {
		t.Fatalf("unable to create payment: %v", err)
	}

	// We must add the invoice to the registry, such that Alice expects
	// this payment.
	err = aliceLink.cfg.Registry.(*mockInvoiceRegistry).AddInvoice(*invoice)
	if err != nil {
		t.Fatalf("unable to add invoice to registry: %v", err)
	}

	bobIndex, err = bobChannel.AddHTLC(htlc)
	if err != nil {
		t.Fatalf("unable to add htlc: %v", err)
	}
	aliceLink.HandleChannelUpdate(htlc)

	// Alice's balance remains unchanged until this HTLC is locked in.
	assertLinkBandwidth(t, aliceLink, aliceStartingBandwidth-htlcAmt)

	// Lock in the HTLC.
	if err := updateState(tmr, aliceLink, bobChannel, false); err != nil {
		t.Fatalf("unable to update state: %v", err)
	}

	// Since Bob is adding this HTLC, Alice only needs to pay the fee.
	assertLinkBandwidth(t, aliceLink, aliceStartingBandwidth-htlcAmt-htlcFee)

	// Next, we'll settle the HTLC with our knowledge of the pre-image that
	// we eventually learn (simulating a multi-hop payment). The bandwidth
	// of the channel should now be re-balanced to the starting point.
	settlePkt := htlcPacket{
		htlc: &lnwire.UpdateFulfillHTLC{
			ID:              bobIndex,
			PaymentPreimage: invoice.Terms.PaymentPreimage,
		},
	}

	aliceLink.HandleSwitchPacket(&settlePkt)
	time.Sleep(time.Millisecond * 500)

	// Settling this HTLC gives Alice all her original bandwidth back.
	assertLinkBandwidth(t, aliceLink, aliceStartingBandwidth)

	// Alice wil send the Settle to Bob.
	select {
	case msg = <-aliceMsgs:
	case <-time.After(2 * time.Second):
		t.Fatalf("did not receive message")
	}

	settleHtlc, ok := msg.(*lnwire.UpdateFulfillHTLC)
	if !ok {
		t.Fatalf("expected UpdateFulfillHTLC, got %T", msg)
	}
	pre := settleHtlc.PaymentPreimage
	idx := settleHtlc.ID
	err = bobChannel.ReceiveHTLCSettle(pre, idx)
	if err != nil {
		t.Fatalf("unable to receive settle: %v", err)
	}

	// After a settle the link should do a state transition automatically,
	// so we don't have to trigger it.
	if err := handleStateUpdate(aliceLink, bobChannel); err != nil {
		t.Fatalf("unable to update state: %v", err)
	}
	assertLinkBandwidth(t, aliceLink, aliceStartingBandwidth)

	// Finally, we'll test the scenario of failing an HTLC received from the
	// remote node. This should result in no perceived bandwidth changes.
	htlcAmt, totalTimelock, hops = generateHops(htlcAmt, testStartingHeight,
		aliceLink)
	blob, err = generateRoute(hops...)
	if err != nil {
		t.Fatalf("unable to gen route: %v", err)
	}
	invoice, htlc, err = generatePayment(htlcAmt, htlcAmt, totalTimelock, blob)
	if err != nil {
		t.Fatalf("unable to create payment: %v", err)
	}
	if err := aliceLink.cfg.Registry.(*mockInvoiceRegistry).AddInvoice(*invoice); err != nil {
		t.Fatalf("unable to add invoice to registry: %v", err)
	}

	// Since we are not using the link to handle HTLC IDs for the
	// remote channel, we must set this manually. This is the second
	// HTLC we add, hence it should have an ID of 1 (Alice's channel
	// link will set this automatically for her side).
	htlc.ID = 1
	bobIndex, err = bobChannel.AddHTLC(htlc)
	if err != nil {
		t.Fatalf("unable to add htlc: %v", err)
	}
	aliceLink.HandleChannelUpdate(htlc)
	time.Sleep(time.Millisecond * 500)

	// No changes before the HTLC is locked in.
	assertLinkBandwidth(t, aliceLink, aliceStartingBandwidth)
	if err := updateState(tmr, aliceLink, bobChannel, false); err != nil {
		t.Fatalf("unable to update state: %v", err)
	}

	// After lock-in, Alice will have to pay the htlc fee.
	assertLinkBandwidth(t, aliceLink, aliceStartingBandwidth-htlcFee)

	// Now fail this HTLC.
	failPkt := htlcPacket{
		incomingHTLCID: bobIndex,
		htlc: &lnwire.UpdateFailHTLC{
			ID: bobIndex,
		},
	}
	aliceLink.HandleSwitchPacket(&failPkt)
	time.Sleep(time.Millisecond * 500)

	// Alice should get all her bandwidth back.
	assertLinkBandwidth(t, aliceLink, aliceStartingBandwidth)

	// Message should be sent to Bob.
	select {
	case msg = <-aliceMsgs:
	case <-time.After(2 * time.Second):
		t.Fatalf("did not receive message")
	}
	failMsg, ok = msg.(*lnwire.UpdateFailHTLC)
	if !ok {
		t.Fatalf("expected UpdateFailHTLC, got %T", msg)
	}
	err = bobChannel.ReceiveFailHTLC(failMsg.ID, []byte("fail"))
	if err != nil {
		t.Fatalf("failed receiving fail htlc: %v", err)
	}

	// After failing an HTLC, the link will automatically trigger
	// a state update.
	if err := handleStateUpdate(aliceLink, bobChannel); err != nil {
		t.Fatalf("unable to update state: %v", err)
	}
	assertLinkBandwidth(t, aliceLink, aliceStartingBandwidth)
}

// TestChannelLinkBandwidthConsistencyOverflow tests that in the case of a
// commitment overflow (no more space for new HTLC's), the bandwidth is updated
// properly as items are being added and removed from the overflow queue.
func TestChannelLinkBandwidthConsistencyOverflow(t *testing.T) {
	t.Parallel()

	var mockBlob [lnwire.OnionPacketSize]byte

	const chanAmt = btcutil.SatoshiPerBitcoin * 5
	aliceLink, bobChannel, batchTick, cleanUp, err :=
		newSingleLinkTestHarness(chanAmt, 0)
	if err != nil {
		t.Fatalf("unable to create link: %v", err)
	}
	defer cleanUp()

	var (
		coreLink               = aliceLink.(*channelLink)
		defaultCommitFee       = coreLink.channel.StateSnapshot().CommitFee
		aliceStartingBandwidth = aliceLink.Bandwidth()
		aliceMsgs              = coreLink.cfg.Peer.(*mockPeer).sentMsgs
	)

	estimator := &lnwallet.StaticFeeEstimator{
		FeeRate: 24,
	}
	feePerWeight, err := estimator.EstimateFeePerWeight(1)
	if err != nil {
		t.Fatalf("unable to query fee estimator: %v", err)
	}
	feePerKw := feePerWeight * 1000

	// The starting bandwidth of the channel should be exactly the amount
	// that we created the channel between her and Bob.
	expectedBandwidth := lnwire.NewMSatFromSatoshis(chanAmt - defaultCommitFee)
	assertLinkBandwidth(t, aliceLink, expectedBandwidth)

	addLinkHTLC := func(amt lnwire.MilliSatoshi) [32]byte {
		invoice, htlc, err := generatePayment(amt, amt, 5, mockBlob)
		if err != nil {
			t.Fatalf("unable to create payment: %v", err)
		}
		aliceLink.HandleSwitchPacket(&htlcPacket{
			htlc:   htlc,
			amount: amt,
		})
		return invoice.Terms.PaymentPreimage
	}

	// We'll first start by adding enough HTLC's to overflow the commitment
	// transaction, checking the reported link bandwidth for proper
	// consistency along the way
	htlcAmt := lnwire.NewMSatFromSatoshis(100000)
	totalHtlcAmt := lnwire.MilliSatoshi(0)
	const numHTLCs = lnwallet.MaxHTLCNumber / 2
	var preImages [][32]byte
	for i := 0; i < numHTLCs; i++ {
		preImage := addLinkHTLC(htlcAmt)
		preImages = append(preImages, preImage)

		totalHtlcAmt += htlcAmt
	}

	// The HTLCs should all be sent to the remote.
	var msg lnwire.Message
	for i := 0; i < numHTLCs; i++ {
		select {
		case msg = <-aliceMsgs:
		case <-time.After(2 * time.Second):
			t.Fatalf("did not receive message")
		}

		addHtlc, ok := msg.(*lnwire.UpdateAddHTLC)
		if !ok {
			t.Fatalf("expected UpdateAddHTLC, got %T", msg)
		}

		_, err := bobChannel.ReceiveHTLC(addHtlc)
		if err != nil {
			t.Fatalf("bob failed receiving htlc: %v", err)
		}
	}

	select {
	case msg = <-aliceMsgs:
		t.Fatalf("unexpected message: %T", msg)
	case <-time.After(20 * time.Millisecond):
	}

	// TODO(roasbeef): increase sleep
	time.Sleep(time.Second * 1)
	commitWeight := lnwallet.CommitWeight + lnwallet.HtlcWeight*numHTLCs
	htlcFee := lnwire.NewMSatFromSatoshis(
		btcutil.Amount((int64(feePerKw) * commitWeight) / 1000),
	)
	expectedBandwidth = aliceStartingBandwidth - totalHtlcAmt - htlcFee
	expectedBandwidth += lnwire.NewMSatFromSatoshis(defaultCommitFee)
	assertLinkBandwidth(t, aliceLink, expectedBandwidth)

	// The overflow queue should be empty at this point, as the commitment
	// transaction should be full, but not yet overflown.
	if coreLink.overflowQueue.Length() != 0 {
		t.Fatalf("wrong overflow queue length: expected %v, got %v", 0,
			coreLink.overflowQueue.Length())
	}

	// At this point, the commitment transaction should now be fully
	// saturated. We'll continue adding HTLC's, and asserting that the
	// bandwidth accounting is done properly.
	const numOverFlowHTLCs = 20
	for i := 0; i < numOverFlowHTLCs; i++ {
		preImage := addLinkHTLC(htlcAmt)
		preImages = append(preImages, preImage)

		totalHtlcAmt += htlcAmt
	}

	// No messages should be sent to the remote at this point.
	select {
	case msg = <-aliceMsgs:
		t.Fatalf("unexpected message: %T", msg)
	case <-time.After(20 * time.Millisecond):
	}

	time.Sleep(time.Second * 2)
	expectedBandwidth -= (numOverFlowHTLCs * htlcAmt)
	assertLinkBandwidth(t, aliceLink, expectedBandwidth)

	// With the extra HTLC's added, the overflow queue should now be
	// populated with our 20 additional HTLC's.
	if coreLink.overflowQueue.Length() != numOverFlowHTLCs {
		t.Fatalf("wrong overflow queue length: expected %v, got %v",
			numOverFlowHTLCs,
			coreLink.overflowQueue.Length())
	}

	// We trigger a state update to lock in the HTLCs. This should
	// not change Alice's bandwidth.
	if err := updateState(batchTick, coreLink, bobChannel, true); err != nil {
		t.Fatalf("unable to update state: %v", err)
	}
	time.Sleep(time.Millisecond * 500)
	assertLinkBandwidth(t, aliceLink, expectedBandwidth)

	// At this point, we'll now settle enough HTLCs to empty the overflow
	// queue. The resulting bandwidth change should be non-existent as this
	// will simply transfer over funds to the remote party. However, the
	// size of the overflow queue should be decreasing
	for i := 0; i < numOverFlowHTLCs; i++ {
		err = bobChannel.SettleHTLC(preImages[i], uint64(i))
		if err != nil {
			t.Fatalf("unable to settle htlc: %v", err)
		}

		htlcSettle := &lnwire.UpdateFulfillHTLC{
			ID:              uint64(i),
			PaymentPreimage: preImages[i],
		}

		aliceLink.HandleChannelUpdate(htlcSettle)
		time.Sleep(time.Millisecond * 50)
	}
	time.Sleep(time.Millisecond * 500)
	assertLinkBandwidth(t, aliceLink, expectedBandwidth)

	// We trigger a state update to lock in the Settles.
	if err := updateState(batchTick, coreLink, bobChannel, false); err != nil {
		t.Fatalf("unable to update state: %v", err)
	}

	// After the state update is done, Alice should start sending
	// HTLCs from the overflow queue.
	for i := 0; i < numOverFlowHTLCs; i++ {
		var msg lnwire.Message
		select {
		case msg = <-aliceMsgs:
		case <-time.After(2 * time.Second):
			t.Fatalf("did not receive message")
		}

		addHtlc, ok := msg.(*lnwire.UpdateAddHTLC)
		if !ok {
			t.Fatalf("expected UpdateAddHTLC, got %T", msg)
		}

		_, err := bobChannel.ReceiveHTLC(addHtlc)
		if err != nil {
			t.Fatalf("bob failed receiving htlc: %v", err)
		}
	}

	select {
	case msg = <-aliceMsgs:
		t.Fatalf("unexpected message: %T", msg)
	case <-time.After(20 * time.Millisecond):
	}

	assertLinkBandwidth(t, aliceLink, expectedBandwidth)

	// Finally, at this point, the queue itself should be fully empty. As
	// enough slots have been drained from the commitment transaction to
	// allocate the queue items to.
	time.Sleep(time.Millisecond * 500)
	if coreLink.overflowQueue.Length() != 0 {
		t.Fatalf("wrong overflow queue length: expected %v, got %v", 0,
			coreLink.overflowQueue.Length())
	}
}

// TestChannelLinkBandwidthChanReserve checks that the bandwidth available
// on the channel link reflects the channel reserve that must be kept
// at all times.
func TestChannelLinkBandwidthChanReserve(t *testing.T) {
	t.Parallel()

	// First start a link that has a balance greater than it's
	// channel reserve.
	const chanAmt = btcutil.SatoshiPerBitcoin * 5
	const chanReserve = btcutil.SatoshiPerBitcoin * 1
	aliceLink, bobChannel, batchTimer, cleanUp, err :=
		newSingleLinkTestHarness(chanAmt, chanReserve)
	if err != nil {
		t.Fatalf("unable to create link: %v", err)
	}
	defer cleanUp()

	var (
		mockBlob               [lnwire.OnionPacketSize]byte
		coreLink               = aliceLink.(*channelLink)
		coreChan               = coreLink.channel
		defaultCommitFee       = coreChan.StateSnapshot().CommitFee
		aliceStartingBandwidth = aliceLink.Bandwidth()
		aliceMsgs              = coreLink.cfg.Peer.(*mockPeer).sentMsgs
	)

	estimator := &lnwallet.StaticFeeEstimator{
		FeeRate: 24,
	}
	feePerWeight, err := estimator.EstimateFeePerWeight(1)
	if err != nil {
		t.Fatalf("unable to query fee estimator: %v", err)
	}
	feePerKw := feePerWeight * 1000
	htlcFee := lnwire.NewMSatFromSatoshis(
		btcutil.Amount((int64(feePerKw) * lnwallet.HtlcWeight) / 1000),
	)

	// The starting bandwidth of the channel should be exactly the amount
	// that we created the channel between her and Bob, minus the channel
	// reserve.
	expectedBandwidth := lnwire.NewMSatFromSatoshis(
		chanAmt - defaultCommitFee - chanReserve)
	assertLinkBandwidth(t, aliceLink, expectedBandwidth)

	// Next, we'll create an HTLC worth 3 BTC, and send it into the link as
	// a switch initiated payment.  The resulting bandwidth should
	// now be decremented to reflect the new HTLC.
	htlcAmt := lnwire.NewMSatFromSatoshis(3 * btcutil.SatoshiPerBitcoin)
	invoice, htlc, err := generatePayment(htlcAmt, htlcAmt, 5, mockBlob)
	if err != nil {
		t.Fatalf("unable to create payment: %v", err)
	}
	addPkt := htlcPacket{
		htlc: htlc,
	}
	aliceLink.HandleSwitchPacket(&addPkt)
	time.Sleep(time.Millisecond * 100)
	assertLinkBandwidth(t, aliceLink, aliceStartingBandwidth-htlcAmt-htlcFee)

	// Alice should send the HTLC to Bob.
	var msg lnwire.Message
	select {
	case msg = <-aliceMsgs:
	case <-time.After(2 * time.Second):
		t.Fatalf("did not receive message")
	}

	addHtlc, ok := msg.(*lnwire.UpdateAddHTLC)
	if !ok {
		t.Fatalf("expected UpdateAddHTLC, got %T", msg)
	}

	bobIndex, err := bobChannel.ReceiveHTLC(addHtlc)
	if err != nil {
		t.Fatalf("bob failed receiving htlc: %v", err)
	}

	// Lock in the HTLC.
	if err := updateState(batchTimer, coreLink, bobChannel, true); err != nil {
		t.Fatalf("unable to update state: %v", err)
	}

	assertLinkBandwidth(t, aliceLink, aliceStartingBandwidth-htlcAmt-htlcFee)

	// If we now send in a valid HTLC settle for the prior HTLC we added,
	// then the bandwidth should remain unchanged as the remote party will
	// gain additional channel balance.
	err = bobChannel.SettleHTLC(invoice.Terms.PaymentPreimage, bobIndex)
	if err != nil {
		t.Fatalf("unable to settle htlc: %v", err)
	}
	htlcSettle := &lnwire.UpdateFulfillHTLC{
		ID:              bobIndex,
		PaymentPreimage: invoice.Terms.PaymentPreimage,
	}
	aliceLink.HandleChannelUpdate(htlcSettle)
	time.Sleep(time.Millisecond * 500)

	// Since the settle is not locked in yet, Alice's bandwidth should still
	// reflect that she has to pay the fee.
	assertLinkBandwidth(t, aliceLink, aliceStartingBandwidth-htlcAmt-htlcFee)

	// Lock in the settle.
	if err := updateState(batchTimer, coreLink, bobChannel, false); err != nil {
		t.Fatalf("unable to update state: %v", err)
	}

	time.Sleep(time.Millisecond * 100)
	assertLinkBandwidth(t, aliceLink, aliceStartingBandwidth-htlcAmt)

	// Now we create a channel that has a channel reserve that is
	// greater than it's balance. In these case only payments can
	// be received on this channel, not sent. The available bandwidth
	// should therefore be 0.
	const bobChanAmt = btcutil.SatoshiPerBitcoin * 1
	const bobChanReserve = btcutil.SatoshiPerBitcoin * 1.5
	bobLink, _, _, bobCleanUp, err := newSingleLinkTestHarness(bobChanAmt,
		bobChanReserve)
	if err != nil {
		t.Fatalf("unable to create link: %v", err)
	}
	defer bobCleanUp()

	// Make sure bandwidth is reported as 0.
	assertLinkBandwidth(t, bobLink, 0)
}

// TestChannelRetransmission tests the ability of the channel links to
// synchronize theirs states after abrupt disconnect.
func TestChannelRetransmission(t *testing.T) {
	t.Parallel()

	retransmissionTests := []struct {
		name     string
		messages []expectedMessage
	}{
		{
			// Tests the ability of the channel links states to be
			// synchronized after remote node haven't receive
			// revoke and ack message.
			name: "intercept last alice revoke_and_ack",
			messages: []expectedMessage{
				// First initialization of the channel.
				{"alice", "bob", &lnwire.ChannelReestablish{}, false},
				{"bob", "alice", &lnwire.ChannelReestablish{}, false},

				{"alice", "bob", &lnwire.FundingLocked{}, false},
				{"bob", "alice", &lnwire.FundingLocked{}, false},

				// Send payment from Alice to Bob and intercept
				// the last revocation message, in this case
				// Bob should not proceed the payment farther.
				{"alice", "bob", &lnwire.UpdateAddHTLC{}, false},
				{"alice", "bob", &lnwire.CommitSig{}, false},
				{"bob", "alice", &lnwire.RevokeAndAck{}, false},
				{"bob", "alice", &lnwire.CommitSig{}, false},
				{"alice", "bob", &lnwire.RevokeAndAck{}, true},

				// Reestablish messages exchange on nodes restart.
				{"alice", "bob", &lnwire.ChannelReestablish{}, false},
				{"bob", "alice", &lnwire.ChannelReestablish{}, false},

				// Alice should resend the revoke_and_ack
				// message to Bob because Bob claimed it in the
				// re-establish message.
				{"alice", "bob", &lnwire.RevokeAndAck{}, false},

				// Proceed the payment farther by sending the
				// fulfilment message and trigger the state
				// update.
				{"bob", "alice", &lnwire.UpdateFulfillHTLC{}, false},
				{"bob", "alice", &lnwire.CommitSig{}, false},
				{"alice", "bob", &lnwire.RevokeAndAck{}, false},
				{"alice", "bob", &lnwire.CommitSig{}, false},
				{"bob", "alice", &lnwire.RevokeAndAck{}, false},
			},
		},
		{
			// Tests the ability of the channel links states to be
			// synchronized after remote node haven't receive
			// revoke and ack message.
			name: "intercept bob revoke_and_ack commit_sig messages",
			messages: []expectedMessage{
				{"alice", "bob", &lnwire.ChannelReestablish{}, false},
				{"bob", "alice", &lnwire.ChannelReestablish{}, false},

				{"alice", "bob", &lnwire.FundingLocked{}, false},
				{"bob", "alice", &lnwire.FundingLocked{}, false},

				// Send payment from Alice to Bob and intercept
				// the last revocation message, in this case
				// Bob should not proceed the payment farther.
				{"alice", "bob", &lnwire.UpdateAddHTLC{}, false},
				{"alice", "bob", &lnwire.CommitSig{}, false},

				// Intercept bob commit sig and revoke and ack
				// messages.
				{"bob", "alice", &lnwire.RevokeAndAck{}, true},
				{"bob", "alice", &lnwire.CommitSig{}, true},

				// Reestablish messages exchange on nodes restart.
				{"alice", "bob", &lnwire.ChannelReestablish{}, false},
				{"bob", "alice", &lnwire.ChannelReestablish{}, false},

				// Bob should resend previously intercepted messages.
				{"bob", "alice", &lnwire.RevokeAndAck{}, false},
				{"bob", "alice", &lnwire.CommitSig{}, false},

				// Proceed the payment farther by sending the
				// fulfilment message and trigger the state
				// update.
				{"alice", "bob", &lnwire.RevokeAndAck{}, false},
				{"bob", "alice", &lnwire.UpdateFulfillHTLC{}, false},
				{"bob", "alice", &lnwire.CommitSig{}, false},
				{"alice", "bob", &lnwire.RevokeAndAck{}, false},
				{"alice", "bob", &lnwire.CommitSig{}, false},
				{"bob", "alice", &lnwire.RevokeAndAck{}, false},
			},
		},
		{
			// Tests the ability of the channel links states to be
			// synchronized after remote node haven't receive
			// update and commit sig messages.
			name: "intercept update add htlc and commit sig messages",
			messages: []expectedMessage{
				{"alice", "bob", &lnwire.ChannelReestablish{}, false},
				{"bob", "alice", &lnwire.ChannelReestablish{}, false},

				{"alice", "bob", &lnwire.FundingLocked{}, false},
				{"bob", "alice", &lnwire.FundingLocked{}, false},

				// Attempt make a payment from Alice to Bob,
				// which is intercepted, emulating the Bob
				// server abrupt stop.
				{"alice", "bob", &lnwire.UpdateAddHTLC{}, true},
				{"alice", "bob", &lnwire.CommitSig{}, true},

				// Restart of the nodes, and after that nodes
				// should exchange the reestablish messages.
				{"alice", "bob", &lnwire.ChannelReestablish{}, false},
				{"bob", "alice", &lnwire.ChannelReestablish{}, false},

				{"alice", "bob", &lnwire.FundingLocked{}, false},
				{"bob", "alice", &lnwire.FundingLocked{}, false},

				// After Bob has notified Alice that he didn't
				// receive updates Alice should re-send them.
				{"alice", "bob", &lnwire.UpdateAddHTLC{}, false},
				{"alice", "bob", &lnwire.CommitSig{}, false},

				{"bob", "alice", &lnwire.RevokeAndAck{}, false},
				{"bob", "alice", &lnwire.CommitSig{}, false},
				{"alice", "bob", &lnwire.RevokeAndAck{}, false},

				{"bob", "alice", &lnwire.UpdateFulfillHTLC{}, false},
				{"bob", "alice", &lnwire.CommitSig{}, false},
				{"alice", "bob", &lnwire.RevokeAndAck{}, false},
				{"alice", "bob", &lnwire.CommitSig{}, false},
				{"bob", "alice", &lnwire.RevokeAndAck{}, false},
			},
		},
	}
	paymentWithRestart := func(t *testing.T, messages []expectedMessage) {
		channels, cleanUp, restoreChannelsFromDb, err := createClusterChannels(
			btcutil.SatoshiPerBitcoin*5,
			btcutil.SatoshiPerBitcoin*5)
		if err != nil {
			t.Fatalf("unable to create channel: %v", err)
		}
		defer cleanUp()

		chanID := lnwire.NewChanIDFromOutPoint(channels.aliceToBob.ChannelPoint())
		serverErr := make(chan error, 4)

		aliceInterceptor := createInterceptorFunc("[alice] <-- [bob]",
			"alice", messages, chanID, false)
		bobInterceptor := createInterceptorFunc("[alice] --> [bob]",
			"bob", messages, chanID, false)

		ct := newConcurrentTester(t)

		// Add interceptor to check the order of Bob and Alice
		// messages.
		n := newThreeHopNetwork(ct,
			channels.aliceToBob, channels.bobToAlice,
			channels.bobToCarol, channels.carolToBob,
			testStartingHeight,
		)
		n.aliceServer.intersect(aliceInterceptor)
		n.bobServer.intersect(bobInterceptor)
		if err := n.start(); err != nil {
			ct.Fatalf("unable to start three hop network: %v", err)
		}
		defer n.stop()

		bobBandwidthBefore := n.firstBobChannelLink.Bandwidth()
		aliceBandwidthBefore := n.aliceChannelLink.Bandwidth()

		amount := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin)
		htlcAmt, totalTimelock, hops := generateHops(amount, testStartingHeight,
			n.firstBobChannelLink)

		// Send payment which should fail because we intercept the
		// update and commit messages.
		//
		// TODO(roasbeef); increase timeout?
		receiver := n.bobServer
		rhash, err := n.makePayment(n.aliceServer, receiver,
			n.bobServer.PubKey(), hops, amount, htlcAmt,
			totalTimelock).Wait(time.Second * 5)
		if err == nil {
			ct.Fatalf("payment shouldn't haven been finished")
		}

		// Stop network cluster and create new one, with the old
		// channels states. Also do the *hack* - save the payment
		// receiver to pass it in new channel link, otherwise payment
		// will be failed because of the unknown payment hash. Hack
		// will be removed with sphinx payment.
		bobRegistry := n.bobServer.registry
		n.stop()

		channels, err = restoreChannelsFromDb()
		if err != nil {
			ct.Fatalf("unable to restore channels from database: %v", err)
		}

		n = newThreeHopNetwork(ct, channels.aliceToBob, channels.bobToAlice,
			channels.bobToCarol, channels.carolToBob, testStartingHeight)
		n.firstBobChannelLink.cfg.Registry = bobRegistry
		n.aliceServer.intersect(aliceInterceptor)
		n.bobServer.intersect(bobInterceptor)

		if err := n.start(); err != nil {
			ct.Fatalf("unable to start three hop network: %v", err)
		}
		defer n.stop()

		// Wait for reestablishment to be proceeded and invoice to be settled.
		// TODO(andrew.shvv) Will be removed if we move the notification center
		// to the channel link itself.

		var invoice channeldb.Invoice
		for i := 0; i < 20; i++ {
			select {
			case <-time.After(time.Millisecond * 200):
			case serverErr := <-serverErr:
				ct.Fatalf("server error: %v", serverErr)
			}

			// Check that alice invoice wasn't settled and
			// bandwidth of htlc links hasn't been changed.
			invoice, err = receiver.registry.LookupInvoice(rhash)
			if err != nil {
				err = errors.Errorf("unable to get invoice: %v", err)
				continue
			}
			if !invoice.Terms.Settled {
				err = errors.Errorf("alice invoice haven't been settled")
				continue
			}

			aliceExpectedBandwidth := aliceBandwidthBefore - htlcAmt
			if aliceExpectedBandwidth != n.aliceChannelLink.Bandwidth() {
				err = errors.Errorf("expected alice to have %v, instead has %v",
					aliceExpectedBandwidth, n.aliceChannelLink.Bandwidth())
				continue
			}

			bobExpectedBandwidth := bobBandwidthBefore + htlcAmt
			if bobExpectedBandwidth != n.firstBobChannelLink.Bandwidth() {
				err = errors.Errorf("expected bob to have %v, instead has %v",
					bobExpectedBandwidth, n.firstBobChannelLink.Bandwidth())
				continue
			}

			break
		}

		if err != nil {
			ct.Fatal(err)
		}
	}

	for _, test := range retransmissionTests {
		passed := t.Run(test.name, func(t *testing.T) {
			paymentWithRestart(t, test.messages)
		})

		if !passed {
			break
		}
	}

}

// TestShouldAdjustCommitFee tests the shouldAdjustCommitFee pivot function to
// ensure that ie behaves properly. We should only update the fee if it
// deviates from our current fee by more 10% or more.
func TestShouldAdjustCommitFee(t *testing.T) {
	tests := []struct {
		netFee       btcutil.Amount
		chanFee      btcutil.Amount
		shouldAdjust bool
	}{

		// The network fee is 3x lower than the current commitment
		// transaction. As a result, we should adjust our fee to match
		// it.
		{
			netFee:       100,
			chanFee:      3000,
			shouldAdjust: true,
		},

		// The network fee is lower than the current commitment fee,
		// but only slightly so, so we won't update the commitment fee.
		{
			netFee:       2999,
			chanFee:      3000,
			shouldAdjust: false,
		},

		// The network fee is lower than the commitment fee, but only
		// right before it crosses our current threshold.
		{
			netFee:       1000,
			chanFee:      1099,
			shouldAdjust: false,
		},

		// The network fee is lower than the commitment fee, and within
		// our range of adjustment, so we should adjust.
		{
			netFee:       1000,
			chanFee:      1100,
			shouldAdjust: true,
		},

		// The network fee is 2x higher than our commitment fee, so we
		// should adjust upwards.
		{
			netFee:       2000,
			chanFee:      1000,
			shouldAdjust: true,
		},

		// The network fee is higher than our commitment fee, but only
		// slightly so, so we won't update.
		{
			netFee:       1001,
			chanFee:      1000,
			shouldAdjust: false,
		},

		// The network fee is higher than our commitment fee, but
		// hasn't yet crossed our activation threshold.
		{
			netFee:       1100,
			chanFee:      1099,
			shouldAdjust: false,
		},

		// The network fee is higher than our commitment fee, and
		// within our activation threshold, so we should update our
		// fee.
		{
			netFee:       1100,
			chanFee:      1000,
			shouldAdjust: true,
		},

		// Our fees match exactly, so we shouldn't update it at all.
		{
			netFee:       1000,
			chanFee:      1000,
			shouldAdjust: false,
		},
	}

	for i, test := range tests {
		adjustedFee := shouldAdjustCommitFee(
			test.netFee, test.chanFee,
		)

		if adjustedFee && !test.shouldAdjust {
			t.Fatalf("test #%v failed: net_fee=%v, "+
				"chan_fee=%v, adjust_expect=%v, adjust_returned=%v",
				i, test.netFee, test.chanFee, test.shouldAdjust,
				adjustedFee)
		}
	}
}

// TestChannelLinkUpdateCommitFee tests that when a new block comes in, the
// channel link properly checks to see if it should update the commitment fee.
func TestChannelLinkUpdateCommitFee(t *testing.T) {
	t.Parallel()

	// First, we'll create our traditional three hop network. We'll only be
	// interacting with and asserting the state of two of the end points
	// for this test.
	channels, cleanUp, _, err := createClusterChannels(
		btcutil.SatoshiPerBitcoin*3,
		btcutil.SatoshiPerBitcoin*5)
	if err != nil {
		t.Fatalf("unable to create channel: %v", err)
	}
	defer cleanUp()

	n := newThreeHopNetwork(t, channels.aliceToBob, channels.bobToAlice,
		channels.bobToCarol, channels.carolToBob, testStartingHeight)

	// First, we'll set up some message interceptors to ensure that the
	// proper messages are sent when updating fees.
	chanID := n.aliceChannelLink.ChanID()
	messages := []expectedMessage{
		{"alice", "bob", &lnwire.ChannelReestablish{}, false},
		{"bob", "alice", &lnwire.ChannelReestablish{}, false},

		{"alice", "bob", &lnwire.FundingLocked{}, false},
		{"bob", "alice", &lnwire.FundingLocked{}, false},

		{"alice", "bob", &lnwire.UpdateFee{}, false},

		{"alice", "bob", &lnwire.CommitSig{}, false},
		{"bob", "alice", &lnwire.RevokeAndAck{}, false},
		{"bob", "alice", &lnwire.CommitSig{}, false},
		{"alice", "bob", &lnwire.RevokeAndAck{}, false},
	}
	n.aliceServer.intersect(createInterceptorFunc("[alice] <-- [bob]",
		"alice", messages, chanID, false))
	n.bobServer.intersect(createInterceptorFunc("[alice] --> [bob]",
		"bob", messages, chanID, false))

	if err := n.start(); err != nil {
		t.Fatal(err)
	}
	defer n.stop()
	defer n.feeEstimator.Stop()

	// First, we'll start off all channels at "height" 9000 by sending a
	// new epoch to all the clients.
	select {
	case n.aliceBlockEpoch <- &chainntnfs.BlockEpoch{
		Height: 9000,
	}:
	case <-time.After(time.Second * 5):
		t.Fatalf("link didn't read block epoch")
	}
	select {
	case n.bobFirstBlockEpoch <- &chainntnfs.BlockEpoch{
		Height: 9000,
	}:
	case <-time.After(time.Second * 5):
		t.Fatalf("link didn't read block epoch")
	}

	startingFeeRate := channels.aliceToBob.CommitFeeRate()

	// Next, we'll send the first fee rate response to Alice.
	select {
	case n.feeEstimator.weightFeeIn <- startingFeeRate / 1000:
	case <-time.After(time.Second * 5):
		t.Fatalf("alice didn't query for the new " +
			"network fee")
	}

	time.Sleep(time.Millisecond * 500)

	// The fee rate on the alice <-> bob channel should still be the same
	// on both sides.
	aliceFeeRate := channels.aliceToBob.CommitFeeRate()
	bobFeeRate := channels.bobToAlice.CommitFeeRate()
	if aliceFeeRate != bobFeeRate {
		t.Fatalf("fee rates don't match: expected %v got %v",
			aliceFeeRate, bobFeeRate)
	}
	if aliceFeeRate != startingFeeRate {
		t.Fatalf("alice's fee rate shouldn't have changed: "+
			"expected %v, got %v", aliceFeeRate, startingFeeRate)
	}
	if bobFeeRate != startingFeeRate {
		t.Fatalf("bob's fee rate shouldn't have changed: "+
			"expected %v, got %v", bobFeeRate, startingFeeRate)
	}

	// Now we'll send a new block update to all end points, with a new
	// height THAT'S OVER 9000!!!
	select {
	case n.aliceBlockEpoch <- &chainntnfs.BlockEpoch{
		Height: 9001,
	}:
	case <-time.After(time.Second * 5):
		t.Fatalf("link didn't read block epoch")
	}
	select {
	case n.bobFirstBlockEpoch <- &chainntnfs.BlockEpoch{
		Height: 9001,
	}:
	case <-time.After(time.Second * 5):
		t.Fatalf("link didn't read block epoch")
	}

	// Next, we'll set up a deliver a fee rate that's triple the current
	// fee rate. This should cause the Alice (the initiator) to trigger a
	// fee update.
	newFeeRate := startingFeeRate * 3
	select {
	case n.feeEstimator.weightFeeIn <- newFeeRate:
	case <-time.After(time.Second * 5):
		t.Fatalf("alice didn't query for the new " +
			"network fee")
	}

	time.Sleep(time.Second * 2)

	// At this point, Alice should've triggered a new fee update that
	// increased the fee rate to match the new rate.
	//
	// We'll scale the new fee rate by 100 as we deal with units of fee
	// per-kw.
	expectedFeeRate := newFeeRate * 1000
	aliceFeeRate = channels.aliceToBob.CommitFeeRate()
	bobFeeRate = channels.bobToAlice.CommitFeeRate()
	if aliceFeeRate != expectedFeeRate {
		t.Fatalf("alice's fee rate didn't change: expected %v, got %v",
			expectedFeeRate, aliceFeeRate)
	}
	if bobFeeRate != expectedFeeRate {
		t.Fatalf("bob's fee rate didn't change: expected %v, got %v",
			expectedFeeRate, aliceFeeRate)
	}
	if aliceFeeRate != bobFeeRate {
		t.Fatalf("fee rates don't match: expected %v got %v",
			aliceFeeRate, bobFeeRate)
	}
}

// TestChannelLinkRejectDuplicatePayment tests that if a link receives an
// incoming HTLC for a payment we have already settled, then it rejects the
// HTLC. We do this as we want to enforce the fact that invoices are only to be
// used _once.
func TestChannelLinkRejectDuplicatePayment(t *testing.T) {
	t.Parallel()

	// First, we'll create our traditional three hop network. We'll only be
	// interacting with and asserting the state of two of the end points
	// for this test.
	channels, cleanUp, _, err := createClusterChannels(
		btcutil.SatoshiPerBitcoin*3,
		btcutil.SatoshiPerBitcoin*5)
	if err != nil {
		t.Fatalf("unable to create channel: %v", err)
	}
	defer cleanUp()

	n := newThreeHopNetwork(t, channels.aliceToBob, channels.bobToAlice,
		channels.bobToCarol, channels.carolToBob, testStartingHeight)
	if err := n.start(); err != nil {
		t.Fatalf("unable to start three hop network: %v", err)
	}
	defer n.stop()

	amount := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin)

	// We'll start off by making a payment from Alice to Carol. We'll
	// manually generate this request so we can control all the parameters.
	htlcAmt, totalTimelock, hops := generateHops(amount, testStartingHeight,
		n.firstBobChannelLink, n.carolChannelLink)
	blob, err := generateRoute(hops...)
	if err != nil {
		t.Fatal(err)
	}
	invoice, htlc, err := generatePayment(amount, htlcAmt, totalTimelock,
		blob)
	if err != nil {
		t.Fatal(err)
	}
	if err := n.carolServer.registry.AddInvoice(*invoice); err != nil {
		t.Fatalf("unable to add invoice in carol registry: %v", err)
	}

	// With the invoice now added to Carol's registry, we'll send the
	// payment. It should succeed w/o any issues as it has been crafted
	// properly.
	_, err = n.aliceServer.htlcSwitch.SendHTLC(n.bobServer.PubKey(), htlc,
		newMockDeobfuscator())
	if err != nil {
		t.Fatalf("unable to send payment to carol: %v", err)
	}

	// Now, if we attempt to send the payment *again* it should be rejected
	// as it's a duplicate request.
	_, err = n.aliceServer.htlcSwitch.SendHTLC(n.bobServer.PubKey(), htlc,
		newMockDeobfuscator())
	if err.Error() != lnwire.CodeUnknownPaymentHash.String() {
		t.Fatal("error haven't been received")
	}
}

// TODO(roasbeef): add test for re-sending after hodl mode, to settle any lingering
