// +build !rpctest

package main

import (
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"path/filepath"
	"testing"
	"time"

	"github.com/btcsuite/btclog"
	"github.com/lightningnetwork/lnd/chainntnfs"
	"github.com/lightningnetwork/lnd/channeldb"
	"github.com/lightningnetwork/lnd/lnrpc"
	"github.com/lightningnetwork/lnd/lnwallet"
	"github.com/lightningnetwork/lnd/lnwire"
	"github.com/roasbeef/btcd/chaincfg"
	"github.com/roasbeef/btcd/chaincfg/chainhash"
	_ "github.com/roasbeef/btcwallet/walletdb/bdb"

	"github.com/roasbeef/btcd/btcec"
	"github.com/roasbeef/btcd/wire"
	"github.com/roasbeef/btcutil"
)

var (
	privPass = []byte("dummy-pass")

	// Use hard-coded keys for Alice and Bob, the two FundingManagers that
	// we will test the interaction between.
	alicePrivKeyBytes = [32]byte{
		0xb7, 0x94, 0x38, 0x5f, 0x2d, 0x1e, 0xf7, 0xab,
		0x4d, 0x92, 0x73, 0xd1, 0x90, 0x63, 0x81, 0xb4,
		0x4f, 0x2f, 0x6f, 0x25, 0x88, 0xa3, 0xef, 0xb9,
		0x6a, 0x49, 0x18, 0x83, 0x31, 0x98, 0x47, 0x53,
	}

	alicePrivKey, alicePubKey = btcec.PrivKeyFromBytes(btcec.S256(),
		alicePrivKeyBytes[:])

	aliceTCPAddr, _ = net.ResolveTCPAddr("tcp", "10.0.0.2:9001")

	aliceAddr = &lnwire.NetAddress{
		IdentityKey: alicePubKey,
		Address:     aliceTCPAddr,
	}

	bobPrivKeyBytes = [32]byte{
		0x81, 0xb6, 0x37, 0xd8, 0xfc, 0xd2, 0xc6, 0xda,
		0x63, 0x59, 0xe6, 0x96, 0x31, 0x13, 0xa1, 0x17,
		0xd, 0xe7, 0x95, 0xe4, 0xb7, 0x25, 0xb8, 0x4d,
		0x1e, 0xb, 0x4c, 0xfd, 0x9e, 0xc5, 0x8c, 0xe9,
	}

	bobPrivKey, bobPubKey = btcec.PrivKeyFromBytes(btcec.S256(),
		bobPrivKeyBytes[:])

	bobTCPAddr, _ = net.ResolveTCPAddr("tcp", "10.0.0.2:9000")

	bobAddr = &lnwire.NetAddress{
		IdentityKey: bobPubKey,
		Address:     bobTCPAddr,
	}
)

type mockNotifier struct {
	confChannel chan *chainntnfs.TxConfirmation
	epochChan   chan *chainntnfs.BlockEpoch
}

func (m *mockNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash, numConfs,
	heightHint uint32) (*chainntnfs.ConfirmationEvent, error) {
	return &chainntnfs.ConfirmationEvent{
		Confirmed: m.confChannel,
	}, nil
}
func (m *mockNotifier) RegisterBlockEpochNtfn() (*chainntnfs.BlockEpochEvent, error) {
	return &chainntnfs.BlockEpochEvent{
		Epochs: m.epochChan,
		Cancel: func() {},
	}, nil
}

func (m *mockNotifier) Start() error {
	return nil
}

func (m *mockNotifier) Stop() error {
	return nil
}
func (m *mockNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
	heightHint uint32) (*chainntnfs.SpendEvent, error) {
	return &chainntnfs.SpendEvent{
		Spend:  make(chan *chainntnfs.SpendDetail),
		Cancel: func() {},
	}, nil
}

type testNode struct {
	privKey      *btcec.PrivateKey
	msgChan      chan lnwire.Message
	announceChan chan lnwire.Message
	publTxChan   chan *wire.MsgTx
	fundingMgr   *fundingManager
	mockNotifier *mockNotifier
	testDir      string
}

func disableFndgLogger(t *testing.T) {
	channeldb.UseLogger(btclog.Disabled)
	lnwallet.UseLogger(btclog.Disabled)
	fndgLog = btclog.Disabled
}

func createTestWallet(tempTestDir string, netParams *chaincfg.Params,
	notifier chainntnfs.ChainNotifier, wc lnwallet.WalletController,
	signer lnwallet.Signer, bio lnwallet.BlockChainIO,
	estimator lnwallet.FeeEstimator) (*lnwallet.LightningWallet, error) {

	dbDir := filepath.Join(tempTestDir, "cdb")
	cdb, err := channeldb.Open(dbDir)
	if err != nil {
		return nil, err
	}

	wallet, err := lnwallet.NewLightningWallet(lnwallet.Config{
		Database:         cdb,
		Notifier:         notifier,
		WalletController: wc,
		Signer:           signer,
		ChainIO:          bio,
		FeeEstimator:     estimator,
		NetParams:        *netParams,
	})
	if err != nil {
		return nil, err
	}

	if err := wallet.Startup(); err != nil {
		return nil, err
	}

	return wallet, nil
}

func createTestFundingManager(t *testing.T, pubKey *btcec.PublicKey,
	tempTestDir string, hdSeed []byte, netParams *chaincfg.Params,
	chainNotifier chainntnfs.ChainNotifier, estimator lnwallet.FeeEstimator,
	sentMessages chan lnwire.Message, sentAnnouncements chan lnwire.Message,
	publTxChan chan *wire.MsgTx, shutdownChan chan struct{}) (*fundingManager, error) {

	wc := &mockWalletController{
		rootKey:               alicePrivKey,
		publishedTransactions: publTxChan,
	}
	signer := &mockSigner{
		key: alicePrivKey,
	}
	bio := &mockChainIO{}

	lnw, err := createTestWallet(tempTestDir, netParams,
		chainNotifier, wc, signer, bio, estimator)
	if err != nil {
		t.Fatalf("unable to create test ln wallet: %v", err)
	}

	arbiterChan := make(chan *lnwallet.LightningChannel)
	var chanIDSeed [32]byte

	f, err := newFundingManager(fundingConfig{
		IDKey:        pubKey,
		Wallet:       lnw,
		Notifier:     chainNotifier,
		FeeEstimator: estimator,
		SignMessage: func(pubKey *btcec.PublicKey, msg []byte) (*btcec.Signature, error) {
			return nil, nil
		},
		SendAnnouncement: func(msg lnwire.Message) error {
			select {
			case sentAnnouncements <- msg:
			case <-shutdownChan:
				return fmt.Errorf("shutting down")
			}
			return nil
		},
		CurrentNodeAnnouncement: func() (lnwire.NodeAnnouncement, error) {
			return lnwire.NodeAnnouncement{}, nil
		},
		ArbiterChan: arbiterChan,
		SendToPeer: func(target *btcec.PublicKey, msgs ...lnwire.Message) error {
			select {
			case sentMessages <- msgs[0]:
			case <-shutdownChan:
				return fmt.Errorf("shutting down")
			}
			return nil
		},
		FindPeer: func(peerKey *btcec.PublicKey) (*peer, error) {
			return nil, nil
		},
		TempChanIDSeed: chanIDSeed,
		FindChannel: func(chanID lnwire.ChannelID) (*lnwallet.LightningChannel, error) {
			// This is not expected to be used in the current tests.
			// Add an implementation if that changes.
			t.Fatal("did not expect FindChannel to be called")
			return nil, nil
		},
		NumRequiredConfs: func(chanAmt btcutil.Amount,
			pushAmt lnwire.MilliSatoshi) uint16 {

			return uint16(cfg.DefaultNumChanConfs)
		},
		RequiredRemoteDelay: func(amt btcutil.Amount) uint16 {
			return 4
		},
	})
	if err != nil {
		t.Fatalf("failed creating fundingManager: %v", err)
	}

	return f, nil
}

func recreateAliceFundingManager(t *testing.T, alice *testNode) {
	// Stop the old fundingManager before creating a new one.
	if err := alice.fundingMgr.Stop(); err != nil {
		t.Fatalf("unable to stop old fundingManager: %v", err)
	}

	aliceMsgChan := make(chan lnwire.Message)
	aliceAnnounceChan := make(chan lnwire.Message)

	oldCfg := alice.fundingMgr.cfg

	f, err := newFundingManager(fundingConfig{
		IDKey:        oldCfg.IDKey,
		Wallet:       oldCfg.Wallet,
		Notifier:     oldCfg.Notifier,
		FeeEstimator: oldCfg.FeeEstimator,
		SignMessage: func(pubKey *btcec.PublicKey,
			msg []byte) (*btcec.Signature, error) {
			return nil, nil
		},
		SendAnnouncement: func(msg lnwire.Message) error {
			aliceAnnounceChan <- msg
			return nil
		},
		CurrentNodeAnnouncement: func() (lnwire.NodeAnnouncement, error) {
			return lnwire.NodeAnnouncement{}, nil
		},
		ArbiterChan: oldCfg.ArbiterChan,
		SendToPeer: func(target *btcec.PublicKey,
			msgs ...lnwire.Message) error {
			aliceMsgChan <- msgs[0]
			return nil
		},
		FindPeer: func(peerKey *btcec.PublicKey) (*peer, error) {
			return nil, nil
		},
		TempChanIDSeed: oldCfg.TempChanIDSeed,
		FindChannel:    oldCfg.FindChannel,
	})
	if err != nil {
		t.Fatalf("failed recreating aliceFundingManager: %v", err)
	}

	alice.fundingMgr = f
	alice.msgChan = aliceMsgChan
	alice.announceChan = aliceAnnounceChan

	if err = f.Start(); err != nil {
		t.Fatalf("failed starting fundingManager: %v", err)
	}
}

func setupFundingManagers(t *testing.T, shutdownChannel chan struct{}) (*testNode, *testNode) {
	// We need to set the global config, as fundingManager uses
	// MaxPendingChannels, and it is usually set in lndMain().
	cfg = &config{
		MaxPendingChannels: defaultMaxPendingChannels,
	}

	netParams := activeNetParams.Params
	estimator := lnwallet.StaticFeeEstimator{FeeRate: 250}

	aliceMockNotifier := &mockNotifier{
		confChannel: make(chan *chainntnfs.TxConfirmation, 1),
		epochChan:   make(chan *chainntnfs.BlockEpoch, 1),
	}

	aliceTestDir, err := ioutil.TempDir("", "alicelnwallet")
	if err != nil {
		t.Fatalf("unable to create temp directory: %v", err)
	}

	aliceMsgChan := make(chan lnwire.Message)
	aliceAnnounceChan := make(chan lnwire.Message)
	alicePublTxChan := make(chan *wire.MsgTx, 1)

	aliceFundingMgr, err := createTestFundingManager(t, alicePubKey,
		aliceTestDir, alicePrivKeyBytes[:], netParams, aliceMockNotifier,
		estimator, aliceMsgChan, aliceAnnounceChan, alicePublTxChan,
		shutdownChannel)
	if err != nil {
		t.Fatalf("failed creating fundingManager: %v", err)
	}

	if err = aliceFundingMgr.Start(); err != nil {
		t.Fatalf("failed starting fundingManager: %v", err)
	}

	alice := &testNode{
		privKey:      alicePrivKey,
		msgChan:      aliceMsgChan,
		announceChan: aliceAnnounceChan,
		publTxChan:   alicePublTxChan,
		fundingMgr:   aliceFundingMgr,
		mockNotifier: aliceMockNotifier,
		testDir:      aliceTestDir,
	}

	bobMockNotifier := &mockNotifier{
		confChannel: make(chan *chainntnfs.TxConfirmation, 1),
		epochChan:   make(chan *chainntnfs.BlockEpoch, 1),
	}

	bobTestDir, err := ioutil.TempDir("", "boblnwallet")
	if err != nil {
		t.Fatalf("unable to create temp directory: %v", err)
	}

	bobMsgChan := make(chan lnwire.Message)
	bobAnnounceChan := make(chan lnwire.Message)
	bobPublTxChan := make(chan *wire.MsgTx, 1)
	bobFundingMgr, err := createTestFundingManager(t, bobPubKey, bobTestDir,
		bobPrivKeyBytes[:], netParams, bobMockNotifier, estimator,
		bobMsgChan, bobAnnounceChan, bobPublTxChan, shutdownChannel)
	if err != nil {
		t.Fatalf("failed creating fundingManager: %v", err)
	}

	if err = bobFundingMgr.Start(); err != nil {
		t.Fatalf("failed starting fundingManager: %v", err)
	}

	bob := &testNode{
		privKey:      bobPrivKey,
		msgChan:      bobMsgChan,
		announceChan: bobAnnounceChan,
		publTxChan:   bobPublTxChan,
		fundingMgr:   bobFundingMgr,
		mockNotifier: bobMockNotifier,
		testDir:      bobTestDir,
	}

	return alice, bob
}

func tearDownFundingManagers(t *testing.T, a, b *testNode, shutdownChannel chan struct{}) {
	close(shutdownChannel)

	if err := a.fundingMgr.Stop(); err != nil {
		t.Fatalf("unable to stop fundingManager: %v", err)
	}
	if err := b.fundingMgr.Stop(); err != nil {
		t.Fatalf("unable to stop fundingManager: %v", err)
	}
	os.RemoveAll(a.testDir)
	os.RemoveAll(b.testDir)
}

// openChannel takes the funding process to the point where the funding
// transaction is confirmed on-chain. Returns the funding out point.
func openChannel(t *testing.T, alice, bob *testNode, localFundingAmt,
	pushAmt btcutil.Amount, numConfs uint32,
	updateChan chan *lnrpc.OpenStatusUpdate) *wire.OutPoint {
	// Create a funding request and start the workflow.
	errChan := make(chan error, 1)
	initReq := &openChanReq{
		targetPeerID:    int32(1),
		targetPubkey:    bob.privKey.PubKey(),
		chainHash:       *activeNetParams.GenesisHash,
		localFundingAmt: localFundingAmt,
		pushAmt:         lnwire.NewMSatFromSatoshis(pushAmt),
		updates:         updateChan,
		err:             errChan,
	}

	alice.fundingMgr.initFundingWorkflow(bobAddr, initReq)

	// Alice should have sent the OpenChannel message to Bob.
	var aliceMsg lnwire.Message
	select {
	case aliceMsg = <-alice.msgChan:
	case err := <-initReq.err:
		t.Fatalf("error init funding workflow: %v", err)
	case <-time.After(time.Second * 5):
		t.Fatalf("alice did not send OpenChannel message")
	}

	openChannelReq, ok := aliceMsg.(*lnwire.OpenChannel)
	if !ok {
		errorMsg, gotError := aliceMsg.(*lnwire.Error)
		if gotError {
			t.Fatalf("expected OpenChannel to be sent "+
				"from bob, instead got error: %v",
				lnwire.ErrorCode(errorMsg.Data[0]))
		}
		t.Fatalf("expected OpenChannel to be sent from "+
			"alice, instead got %T", aliceMsg)
	}

	// Let Bob handle the init message.
	bob.fundingMgr.processFundingOpen(openChannelReq, aliceAddr)

	// Bob should answer with an AcceptChannel.
	var bobMsg lnwire.Message
	select {
	case bobMsg = <-bob.msgChan:
	case <-time.After(time.Second * 5):
		t.Fatalf("bob did not send AcceptChannel message")
	}

	acceptChannelResponse, ok := bobMsg.(*lnwire.AcceptChannel)
	if !ok {
		errorMsg, gotError := bobMsg.(*lnwire.Error)
		if gotError {
			t.Fatalf("expected AcceptChannel to be sent "+
				"from bob, instead got error: %v",
				lnwire.ErrorCode(errorMsg.Data[0]))
		}
		t.Fatalf("expected AcceptChannel to be sent from bob, "+
			"instead got %T", bobMsg)
	}

	// Forward the response to Alice.
	alice.fundingMgr.processFundingAccept(acceptChannelResponse, bobAddr)

	// Alice responds with a FundingCreated messages.
	select {
	case aliceMsg = <-alice.msgChan:
	case <-time.After(time.Second * 5):
		t.Fatalf("alice did not send FundingCreated message")
	}
	fundingCreated, ok := aliceMsg.(*lnwire.FundingCreated)
	if !ok {
		errorMsg, gotError := aliceMsg.(*lnwire.Error)
		if gotError {
			t.Fatalf("expected FundingCreated to be sent "+
				"from bob, instead got error: %v",
				lnwire.ErrorCode(errorMsg.Data[0]))
		}
		t.Fatalf("expected FundingCreated to be sent from "+
			"alice, instead got %T", aliceMsg)
	}

	// Give the message to Bob.
	bob.fundingMgr.processFundingCreated(fundingCreated, aliceAddr)

	// Finally, Bob should send the FundingSigned message.
	select {
	case bobMsg = <-bob.msgChan:
	case <-time.After(time.Second * 5):
		t.Fatalf("bob did not send FundingSigned message")
	}

	fundingSigned, ok := bobMsg.(*lnwire.FundingSigned)
	if !ok {
		errorMsg, gotError := bobMsg.(*lnwire.Error)
		if gotError {
			t.Fatalf("expected FundingSigned to be "+
				"sent from bob, instead got error: %v",
				lnwire.ErrorCode(errorMsg.Data[0]))
		}
		t.Fatalf("expected FundingSigned to be sent from "+
			"bob, instead got %T", bobMsg)
	}

	// Forward the signature to Alice.
	alice.fundingMgr.processFundingSigned(fundingSigned, bobAddr)

	// After Alice processes the singleFundingSignComplete message, she will
	// broadcast the funding transaction to the network. We expect to get a
	// channel update saying the channel is pending.
	var pendingUpdate *lnrpc.OpenStatusUpdate
	select {
	case pendingUpdate = <-updateChan:
	case <-time.After(time.Second * 5):
		t.Fatalf("alice did not send OpenStatusUpdate_ChanPending")
	}

	_, ok = pendingUpdate.Update.(*lnrpc.OpenStatusUpdate_ChanPending)
	if !ok {
		t.Fatal("OpenStatusUpdate was not OpenStatusUpdate_ChanPending")
	}

	// Get and return the transaction Alice published to the network.
	var publ *wire.MsgTx
	select {
	case publ = <-alice.publTxChan:
	case <-time.After(time.Second * 5):
		t.Fatalf("alice did not publish funding tx")
	}

	fundingOutPoint := &wire.OutPoint{
		Hash:  publ.TxHash(),
		Index: 0,
	}
	return fundingOutPoint
}

func TestFundingManagerNormalWorkflow(t *testing.T) {
	disableFndgLogger(t)

	shutdownChannel := make(chan struct{})

	alice, bob := setupFundingManagers(t, shutdownChannel)
	defer tearDownFundingManagers(t, alice, bob, shutdownChannel)

	// We will consume the channel updates as we go, so no buffering is needed.
	updateChan := make(chan *lnrpc.OpenStatusUpdate)

	// Run through the process of opening the channel, up until the funding
	// transaction is broadcasted.
	fundingOutPoint := openChannel(t, alice, bob, 500000, 0, 1, updateChan)

	// Notify that transaction was mined
	alice.mockNotifier.confChannel <- &chainntnfs.TxConfirmation{}
	bob.mockNotifier.confChannel <- &chainntnfs.TxConfirmation{}

	// Give fundingManager time to process the newly mined tx and write
	//state to database.
	time.Sleep(300 * time.Millisecond)

	// The funding transaction was mined, so assert that both funding
	// managers now have the state of this channel 'markedOpen' in their
	// internal state machine.
	state, _, err := alice.fundingMgr.getChannelOpeningState(fundingOutPoint)
	if err != nil {
		t.Fatalf("unable to get channel state: %v", err)
	}

	if state != markedOpen {
		t.Fatalf("expected state to be markedOpen, was %v", state)
	}
	state, _, err = bob.fundingMgr.getChannelOpeningState(fundingOutPoint)
	if err != nil {
		t.Fatalf("unable to get channel state: %v", err)
	}

	if state != markedOpen {
		t.Fatalf("expected state to be markedOpen, was %v", state)
	}

	// After the funding transaction is mined, Alice will send
	// fundingLocked to Bob.
	var fundingLockedAlice lnwire.Message
	select {
	case fundingLockedAlice = <-alice.msgChan:
	case <-time.After(time.Second * 5):
		t.Fatalf("alice did not send fundingLocked")
	}
	if fundingLockedAlice.MsgType() != lnwire.MsgFundingLocked {
		t.Fatalf("expected fundingLocked sent from Alice, "+
			"instead got %T", fundingLockedAlice)
	}

	// And similarly Bob will send funding locked to Alice.
	var fundingLockedBob lnwire.Message
	select {
	case fundingLockedBob = <-bob.msgChan:
	case <-time.After(time.Second * 5):
		t.Fatalf("bob did not send fundingLocked")
	}

	if fundingLockedBob.MsgType() != lnwire.MsgFundingLocked {
		t.Fatalf("expected fundingLocked sent from Bob, "+
			"instead got %T", fundingLockedBob)
	}

	// Sleep to make sure database write is finished.
	time.Sleep(300 * time.Millisecond)

	// Check that the state machine is updated accordingly
	state, _, err = alice.fundingMgr.getChannelOpeningState(fundingOutPoint)
	if err != nil {
		t.Fatalf("unable to get channel state: %v", err)
	}

	if state != fundingLockedSent {
		t.Fatalf("expected state to be fundingLockedSent, was %v", state)
	}
	state, _, err = bob.fundingMgr.getChannelOpeningState(fundingOutPoint)
	if err != nil {
		t.Fatalf("unable to get channel state: %v", err)
	}

	if state != fundingLockedSent {
		t.Fatalf("expected state to be fundingLockedSent, was %v", state)
	}

	// After the FundingLocked message is sent, the channel will be announced.
	// A chanAnnouncement consists of three distinct messages:
	//	1) ChannelAnnouncement
	//	2) ChannelUpdate
	//	3) AnnounceSignatures
	// that will be announced in no particular order.
	// A node announcement will also be sent.
	announcements := make([]lnwire.Message, 4)
	for i := 0; i < len(announcements); i++ {
		select {
		case announcements[i] = <-alice.announceChan:
		case <-time.After(time.Second * 5):
			t.Fatalf("alice did not send announcement %v", i)
		}
	}

	gotChannelAnnouncement := false
	gotChannelUpdate := false
	gotAnnounceSignatures := false
	gotNodeAnnouncement := false

	for _, msg := range announcements {
		switch msg.(type) {
		case *lnwire.ChannelAnnouncement:
			gotChannelAnnouncement = true
		case *lnwire.ChannelUpdate:
			gotChannelUpdate = true
		case *lnwire.AnnounceSignatures:
			gotAnnounceSignatures = true
		case *lnwire.NodeAnnouncement:
			gotNodeAnnouncement = true
		}
	}

	if !gotChannelAnnouncement {
		t.Fatalf("did not get ChannelAnnouncement from Alice")
	}
	if !gotChannelUpdate {
		t.Fatalf("did not get ChannelUpdate from Alice")
	}
	if !gotAnnounceSignatures {
		t.Fatalf("did not get AnnounceSignatures from Alice")
	}
	if !gotNodeAnnouncement {
		t.Fatalf("did not get NodeAnnouncement from Alice")
	}

	// Do the check for Bob as well.
	for i := 0; i < len(announcements); i++ {
		select {
		case announcements[i] = <-bob.announceChan:
		case <-time.After(time.Second * 5):
			t.Fatalf("bob did not send announcement %v", i)
		}
	}

	gotChannelAnnouncement = false
	gotChannelUpdate = false
	gotAnnounceSignatures = false
	gotNodeAnnouncement = false

	for _, msg := range announcements {
		switch msg.(type) {
		case *lnwire.ChannelAnnouncement:
			gotChannelAnnouncement = true
		case *lnwire.ChannelUpdate:
			gotChannelUpdate = true
		case *lnwire.AnnounceSignatures:
			gotAnnounceSignatures = true
		case *lnwire.NodeAnnouncement:
			gotNodeAnnouncement = true
		}
	}

	if !gotChannelAnnouncement {
		t.Fatalf("did not get ChannelAnnouncement from Bob")
	}
	if !gotChannelUpdate {
		t.Fatalf("did not get ChannelUpdate from Bob")
	}
	if !gotAnnounceSignatures {
		t.Fatalf("did not get AnnounceSignatures from Bob")
	}
	if !gotNodeAnnouncement {
		t.Fatalf("did not get NodeAnnouncement from Bob")
	}

	// The funding process is now finished, wait for the
	// OpenStatusUpdate_ChanOpen update
	var openUpdate *lnrpc.OpenStatusUpdate
	select {
	case openUpdate = <-updateChan:
	case <-time.After(time.Second * 5):
		t.Fatalf("alice did not send OpenStatusUpdate")
	}

	_, ok := openUpdate.Update.(*lnrpc.OpenStatusUpdate_ChanOpen)
	if !ok {
		t.Fatal("OpenStatusUpdate was not OpenStatusUpdate_ChanOpen")
	}

	// The internal state-machine should now have deleted the channelStates
	// from the database, as the channel is announced.
	state, _, err = alice.fundingMgr.getChannelOpeningState(fundingOutPoint)
	if err != ErrChannelNotFound {
		t.Fatalf("expected to not find channel state, but got: %v", state)
	}

	// Need to give bob time to update database.
	time.Sleep(300 * time.Millisecond)

	state, _, err = bob.fundingMgr.getChannelOpeningState(fundingOutPoint)
	if err != ErrChannelNotFound {
		t.Fatalf("expected to not find channel state, but got: %v", state)
	}

}

func TestFundingManagerRestartBehavior(t *testing.T) {
	disableFndgLogger(t)

	shutdownChannel := make(chan struct{})

	alice, bob := setupFundingManagers(t, shutdownChannel)
	defer tearDownFundingManagers(t, alice, bob, shutdownChannel)

	// Run through the process of opening the channel, up until the funding
	// transaction is broadcasted.
	updateChan := make(chan *lnrpc.OpenStatusUpdate)
	fundingOutPoint := openChannel(t, alice, bob, 500000, 0, 1, updateChan)

	// After the funding transaction gets mined, both nodes will send the
	// fundingLocked message to the other peer. If the funding node fails
	// before this message has been successfully sent, it should retry
	// sending it on restart. We mimic this behavior by letting the
	// SendToPeer method return an error, as if the message was not
	// successfully sent. We then   the fundingManager and make sure
	// it continues the process as expected.
	alice.fundingMgr.cfg.SendToPeer = func(target *btcec.PublicKey,
		msgs ...lnwire.Message) error {
		return fmt.Errorf("intentional error in SendToPeer")
	}

	// Notify that transaction was mined
	alice.mockNotifier.confChannel <- &chainntnfs.TxConfirmation{}
	bob.mockNotifier.confChannel <- &chainntnfs.TxConfirmation{}

	// Give fundingManager time to process the newly mined tx and write to
	// the database.
	time.Sleep(500 * time.Millisecond)

	// The funding transaction was mined, so assert that both funding
	// managers now have the state of this channel 'markedOpen' in their
	// internal state machine.
	state, _, err := alice.fundingMgr.getChannelOpeningState(fundingOutPoint)
	if err != nil {
		t.Fatalf("unable to get channel state: %v", err)
	}

	if state != markedOpen {
		t.Fatalf("expected state to be markedOpen, was %v", state)
	}
	state, _, err = bob.fundingMgr.getChannelOpeningState(fundingOutPoint)
	if err != nil {
		t.Fatalf("unable to get channel state: %v", err)
	}

	if state != markedOpen {
		t.Fatalf("expected state to be markedOpen, was %v", state)
	}

	// After the funding transaction was mined, Bob should have successfully
	// sent the fundingLocked message, while Alice failed sending it. In
	// Alice's case this means that there should be no messages for Bob, and
	//  the channel should still be in state 'markedOpen'

	select {
	case msg := <-alice.msgChan:
		t.Fatalf("did not expect any message from Alice: %v", msg)
	default:
		// Expected.
	}

	// Bob will send funding locked to Alice
	fundingLockedBob := <-bob.msgChan
	if fundingLockedBob.MsgType() != lnwire.MsgFundingLocked {
		t.Fatalf("expected fundingLocked sent from Bob, "+
			"instead got %T", fundingLockedBob)
	}

	// Sleep to make sure database write is finished.
	time.Sleep(1 * time.Second)

	// Alice should still be markedOpen
	state, _, err = alice.fundingMgr.getChannelOpeningState(fundingOutPoint)
	if err != nil {
		t.Fatalf("unable to get channel state: %v", err)
	}

	if state != markedOpen {
		t.Fatalf("expected state to be markedOpen, was %v", state)
	}

	// While Bob successfully sent fundingLocked.
	state, _, err = bob.fundingMgr.getChannelOpeningState(fundingOutPoint)
	if err != nil {
		t.Fatalf("unable to get channel state: %v", err)
	}

	if state != fundingLockedSent {
		t.Fatalf("expected state to be fundingLockedSent, was %v", state)
	}

	// We now recreate Alice's fundingManager, and expect it to retry
	// sending the fundingLocked message.
	recreateAliceFundingManager(t, alice)
	time.Sleep(300 * time.Millisecond)

	// Intetionally make the next channel announcement fail
	alice.fundingMgr.cfg.SendAnnouncement = func(msg lnwire.Message) error {
		return fmt.Errorf("intentional error in SendAnnouncement")
	}

	fundingLockedAlice := <-alice.msgChan
	if fundingLockedAlice.MsgType() != lnwire.MsgFundingLocked {
		t.Fatalf("expected fundingLocked sent from Alice, "+
			"instead got %T", fundingLockedAlice)
	}

	// Sleep to make sure database write is finished.
	time.Sleep(500 * time.Millisecond)

	// The state should now be fundingLockedSent
	state, _, err = alice.fundingMgr.getChannelOpeningState(fundingOutPoint)
	if err != nil {
		t.Fatalf("unable to get channel state: %v", err)
	}

	if state != fundingLockedSent {
		t.Fatalf("expected state to be fundingLockedSent, was %v", state)
	}

	// Check that the channel announcements were never sent
	select {
	case ann := <-alice.announceChan:
		t.Fatalf("unexpectedly got channel announcement message: %v", ann)
	default:
		// Expected
	}

	// Bob, however, should send the announcements
	announcements := make([]lnwire.Message, 4)
	for i := 0; i < len(announcements); i++ {
		select {
		case announcements[i] = <-bob.announceChan:
		case <-time.After(time.Second * 5):
			t.Fatalf("alice did not send announcement %v", i)
		}
	}

	gotChannelAnnouncement := false
	gotChannelUpdate := false
	gotAnnounceSignatures := false
	gotNodeAnnouncement := false

	for _, msg := range announcements {
		switch msg.(type) {
		case *lnwire.ChannelAnnouncement:
			gotChannelAnnouncement = true
		case *lnwire.ChannelUpdate:
			gotChannelUpdate = true
		case *lnwire.AnnounceSignatures:
			gotAnnounceSignatures = true
		case *lnwire.NodeAnnouncement:
			gotNodeAnnouncement = true
		}
	}

	if !gotChannelAnnouncement {
		t.Fatalf("did not get ChannelAnnouncement from Bob")
	}
	if !gotChannelUpdate {
		t.Fatalf("did not get ChannelUpdate from Bob")
	}
	if !gotAnnounceSignatures {
		t.Fatalf("did not get AnnounceSignatures from Bob")
	}
	if !gotNodeAnnouncement {
		t.Fatalf("did not get NodeAnnouncement from Bob")
	}

	// Next up, we check that the Alice rebroadcasts the announcement
	// messages on restart.
	recreateAliceFundingManager(t, alice)
	time.Sleep(300 * time.Millisecond)
	for i := 0; i < len(announcements); i++ {
		select {
		case announcements[i] = <-alice.announceChan:
		case <-time.After(time.Second * 5):
			t.Fatalf("alice did not send announcement %v", i)
		}
	}

	gotChannelAnnouncement = false
	gotChannelUpdate = false
	gotAnnounceSignatures = false
	gotNodeAnnouncement = false

	for _, msg := range announcements {
		switch msg.(type) {
		case *lnwire.ChannelAnnouncement:
			gotChannelAnnouncement = true
		case *lnwire.ChannelUpdate:
			gotChannelUpdate = true
		case *lnwire.AnnounceSignatures:
			gotAnnounceSignatures = true
		case *lnwire.NodeAnnouncement:
			gotNodeAnnouncement = true
		}
	}

	if !gotChannelAnnouncement {
		t.Fatalf("did not get ChannelAnnouncement from Alice after restart")
	}
	if !gotChannelUpdate {
		t.Fatalf("did not get ChannelUpdate from Alice after restart")
	}
	if !gotAnnounceSignatures {
		t.Fatalf("did not get AnnounceSignatures from Alice after restart")
	}
	if !gotNodeAnnouncement {
		t.Fatalf("did not get NodeAnnouncement from Alice after restart")
	}

	// The funding process is now finished. Since we recreated the
	// fundingManager, we don't have an update channel to synchronize on,
	// so a small sleep makes sure the database writing is finished.
	time.Sleep(300 * time.Millisecond)

	// The internal state-machine should now have deleted them from the
	// internal database, as the channel is announced.
	state, _, err = alice.fundingMgr.getChannelOpeningState(fundingOutPoint)
	if err != ErrChannelNotFound {
		t.Fatalf("expected to not find channel state, but got: %v", state)
	}

	state, _, err = bob.fundingMgr.getChannelOpeningState(fundingOutPoint)
	if err != ErrChannelNotFound {
		t.Fatalf("expected to not find channel state, but got: %v", state)
	}

}

func TestFundingManagerFundingTimeout(t *testing.T) {
	disableFndgLogger(t)

	shutdownChannel := make(chan struct{})

	alice, bob := setupFundingManagers(t, shutdownChannel)
	defer tearDownFundingManagers(t, alice, bob, shutdownChannel)

	// We will consume the channel updates as we go, so no buffering is needed.
	updateChan := make(chan *lnrpc.OpenStatusUpdate)

	// Run through the process of opening the channel, up until the funding
	// transaction is broadcasted.
	_ = openChannel(t, alice, bob, 500000, 0, 1, updateChan)

	// Bob will at this point be waiting for the funding transaction to be
	// confirmed, so the channel should be considered pending.
	pendingChannels, err := bob.fundingMgr.cfg.Wallet.Cfg.Database.FetchPendingChannels()
	if err != nil {
		t.Fatalf("unable to fetch pending channels: %v", err)
	}
	if len(pendingChannels) != 1 {
		t.Fatalf("Expected Bob to have 1 pending channel, had  %v",
			len(pendingChannels))
	}

	// We expect Bob to forget the channel after 288 blocks (48 hours), so
	// mine 287, and check that it is still pending.
	bob.mockNotifier.epochChan <- &chainntnfs.BlockEpoch{
		Height: fundingBroadcastHeight + 287,
	}

	time.Sleep(300 * time.Millisecond)

	// Bob should still be waiting for the channel to open.
	pendingChannels, err = bob.fundingMgr.cfg.Wallet.Cfg.Database.FetchPendingChannels()
	if err != nil {
		t.Fatalf("unable to fetch pending channels: %v", err)
	}
	if len(pendingChannels) != 1 {
		t.Fatalf("Expected Bob to have 1 pending channel, had  %v",
			len(pendingChannels))
	}

	bob.mockNotifier.epochChan <- &chainntnfs.BlockEpoch{
		Height: fundingBroadcastHeight + 288,
	}

	// It takes some time for Bob to update the database, so sleep for
	// some time.
	time.Sleep(300 * time.Millisecond)

	pendingChannels, err = bob.fundingMgr.cfg.Wallet.Cfg.Database.FetchPendingChannels()
	if err != nil {
		t.Fatalf("unable to fetch pending channels: %v", err)
	}
	if len(pendingChannels) != 0 {
		t.Fatalf("Expected Bob to have 0 pending channel, had  %v",
			len(pendingChannels))
	}
}