diff --git a/fundingmanager.go b/fundingmanager.go index c38f13f8..9a2eacbf 100644 --- a/fundingmanager.go +++ b/fundingmanager.go @@ -743,7 +743,7 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) { msg.channelType, msg.coinType, 0, // TODO(roasbeef): grab from fee estimation model - contribution.FundingAmount, + capacity, contribution.CsvDelay, contribution.CommitKey, contribution.MultiSigKey, diff --git a/lnwallet/channel_test.go b/lnwallet/channel_test.go index 2df97f49..dc38be40 100644 --- a/lnwallet/channel_test.go +++ b/lnwallet/channel_test.go @@ -9,6 +9,7 @@ import ( "github.com/btcsuite/fastsha256" "github.com/davecgh/go-spew/spew" + "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/elkrem" "github.com/lightningnetwork/lnd/lnwire" @@ -68,11 +69,33 @@ func (m *mockSigner) SignOutputRaw(tx *wire.MsgTx, signDesc *SignDescriptor) ([] return sig[:len(sig)-1], nil } - func (m *mockSigner) ComputeInputScript(tx *wire.MsgTx, signDesc *SignDescriptor) (*InputScript, error) { return nil, nil } +type mockNotfier struct { +} + +func (m *mockNotfier) RegisterConfirmationsNtfn(txid *wire.ShaHash, numConfs uint32) (*chainntnfs.ConfirmationEvent, error) { + return nil, nil +} +func (m *mockNotfier) RegisterBlockEpochNtfn() (*chainntnfs.BlockEpochEvent, error) { + return nil, nil +} + +func (m *mockNotfier) Start() error { + return nil +} + +func (m *mockNotfier) Stop() error { + return nil +} +func (m *mockNotfier) RegisterSpendNtfn(outpoint *wire.OutPoint) (*chainntnfs.SpendEvent, error) { + return &chainntnfs.SpendEvent{ + Spend: make(chan *chainntnfs.SpendDetail), + }, nil +} + // initRevocationWindows simulates a new channel being opened within the p2p // network by populating the initial revocation windows of the passed // commitment state machines. @@ -254,11 +277,13 @@ func createTestChannels(revocationWindow int) (*LightningChannel, *LightningChan aliceSigner := &mockSigner{aliceKeyPriv} bobSigner := &mockSigner{bobKeyPriv} - channelAlice, err := NewLightningChannel(aliceSigner, nil, nil, aliceChannelState) + notifier := &mockNotfier{} + + channelAlice, err := NewLightningChannel(aliceSigner, nil, notifier, aliceChannelState) if err != nil { return nil, nil, nil, err } - channelBob, err := NewLightningChannel(bobSigner, nil, nil, bobChannelState) + channelBob, err := NewLightningChannel(bobSigner, nil, notifier, bobChannelState) if err != nil { return nil, nil, nil, err } @@ -672,11 +697,12 @@ func TestStateUpdatePersistence(t *testing.T) { if err != nil { t.Fatalf("unable to fetch channel: %v", err) } - aliceChannelNew, err := NewLightningChannel(aliceChannel.signer, nil, nil, aliceChannels[0]) + notifier := aliceChannel.channelEvents + aliceChannelNew, err := NewLightningChannel(aliceChannel.signer, nil, notifier, aliceChannels[0]) if err != nil { t.Fatalf("unable to create new channel: %v", err) } - bobChannelNew, err := NewLightningChannel(bobChannel.signer, nil, nil, bobChannels[0]) + bobChannelNew, err := NewLightningChannel(bobChannel.signer, nil, notifier, bobChannels[0]) if err != nil { t.Fatalf("unable to create new channel: %v", err) } diff --git a/lnwallet/interface_test.go b/lnwallet/interface_test.go index 04725dab..ff9b5489 100644 --- a/lnwallet/interface_test.go +++ b/lnwallet/interface_test.go @@ -430,6 +430,9 @@ func testDualFundingReservationWorkflow(miner *rpctest.Harness, wallet *lnwallet t.Fatalf("bob's revocaiton key not found") } + // TODO(roasbeef): account for current hard-coded commit fee, + // need to remove bob all together + chanCapacity := int64(10e8 + 5000) // Alice responds with her output, change addr, multi-sig key and signatures. // Bob then responds with his signatures. bobsSigs, err := bobNode.signFundingTx(fundingTx) @@ -439,7 +442,7 @@ func testDualFundingReservationWorkflow(miner *rpctest.Harness, wallet *lnwallet commitSig, err := bobNode.signCommitTx( chanReservation.LocalCommitTx(), chanReservation.FundingRedeemScript(), - 10e8) + chanCapacity) if err != nil { t.Fatalf("bob is unable to sign alice's commit tx: %v", err) } @@ -690,7 +693,9 @@ func testSingleFunderReservationWorkflowInitiator(miner *rpctest.Harness, bobCommitSig, err := bobNode.signCommitTx( chanReservation.LocalCommitTx(), chanReservation.FundingRedeemScript(), - int64(fundingAmt)) + // TODO(roasbeef): account for current hard-coded fee, need to + // remove bobNode entirely + int64(fundingAmt)+5000) if err != nil { t.Fatalf("bob is unable to sign alice's commit tx: %v", err) } @@ -804,7 +809,8 @@ func testSingleFunderReservationWorkflowResponder(miner *rpctest.Harness, fundingRedeemScript, multiOut, err := lnwallet.GenFundingPkScript( ourContribution.MultiSigKey.SerializeCompressed(), bobContribution.MultiSigKey.SerializeCompressed(), - int64(capacity)) + // TODO(roasbeef): account for hard-coded fee, remove bob node + int64(capacity)+5000) if err != nil { t.Fatalf("unable to generate multi-sig output: %v", err) } @@ -837,7 +843,8 @@ func testSingleFunderReservationWorkflowResponder(miner *rpctest.Harness, } txsort.InPlaceSort(aliceCommitTx) bobCommitSig, err := bobNode.signCommitTx(aliceCommitTx, - fundingRedeemScript, int64(capacity)) + // TODO(roasbeef): account for hard-coded fee, remove bob node + fundingRedeemScript, int64(capacity)+5000) if err != nil { t.Fatalf("unable to sign alice's commit tx: %v", err) } diff --git a/lnwallet/reservation.go b/lnwallet/reservation.go index e59dbf97..286f1db2 100644 --- a/lnwallet/reservation.go +++ b/lnwallet/reservation.go @@ -141,10 +141,16 @@ func NewChannelReservation(capacity, fundingAmt btcutil.Amount, minFeeRate btcut if fundingAmt == 0 { ourBalance = 0 - theirBalance = capacity + theirBalance = capacity - commitFee } else { - ourBalance = fundingAmt - theirBalance = capacity - fundingAmt + // TODO(roasbeef): need to rework fee structure in general and + // also when we "unlock" dual funder within the daemon + if capacity == fundingAmt+commitFee { // Single funder + ourBalance = capacity - commitFee + } else { + ourBalance = fundingAmt - commitFee + } + theirBalance = capacity - fundingAmt - commitFee } return &ChannelReservation{ diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index 529f4dac..ecced934 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -36,6 +36,8 @@ const ( // TODO(roasbeef): should instead be child to make room for future // rotations, etc. identityKeyIndex = hdkeychain.HardenedKeyStart + 2 + + commitFee = 5000 ) var ( @@ -485,7 +487,8 @@ func (l *LightningWallet) InitChannelReservation(capacity, // validate a funding reservation request. func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg) { id := atomic.AddUint64(&l.nextFundingID, 1) - reservation := NewChannelReservation(req.capacity, req.fundingAmount, + totalCapacity := req.capacity + commitFee + reservation := NewChannelReservation(totalCapacity, req.fundingAmount, req.minFeeRate, l, id, req.numConfs) // Grab the mutex on the ChannelReservation to ensure thead-safety @@ -501,10 +504,12 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg // don't need to perform any coin selection. Otherwise, attempt to // obtain enough coins to meet the required funding amount. if req.fundingAmount != 0 { - // TODO(roasbeef): consult model for proper fee rate + // TODO(roasbeef): consult model for proper fee rate on funding + // tx feeRate := uint64(10) - if err := l.selectCoinsAndChange(feeRate, req.fundingAmount, - ourContribution); err != nil { + amt := req.fundingAmount + commitFee + err := l.selectCoinsAndChange(feeRate, amt, ourContribution) + if err != nil { req.err <- err req.resp <- nil return @@ -1190,35 +1195,32 @@ out: // selectCoinsAndChange performs coin selection in order to obtain witness // outputs which sum to at least 'numCoins' amount of satoshis. If coin -// selection is succesful/possible, then the selected coins are available within -// the passed contribution's inputs. If necessary, a change address will also be -// generated. +// selection is succesful/possible, then the selected coins are available +// within the passed contribution's inputs. If necessary, a change address will +// also be generated. // TODO(roasbeef): remove hardcoded fees and req'd confs for outputs. func (l *LightningWallet) selectCoinsAndChange(feeRate uint64, amt btcutil.Amount, contribution *ChannelContribution) error { // We hold the coin select mutex while querying for outputs, and - // performing coin selection in order to avoid inadvertent double spends - // accross funding transactions. - // NOTE: We don't use defer her so we can properly release the lock - // when we encounter an error condition. + // performing coin selection in order to avoid inadvertent double + // spends accross funding transactions. l.coinSelectMtx.Lock() + defer l.coinSelectMtx.Unlock() // Find all unlocked unspent witness outputs with greater than 1 // confirmation. // TODO(roasbeef): make num confs a configuration paramter coins, err := l.ListUnspentWitness(1) if err != nil { - l.coinSelectMtx.Unlock() return err } // Peform coin selection over our available, unlocked unspent outputs - // in order to find enough coins to meet the funding amount requirements. - // TODO(roasbeef): take in sat/byte + // in order to find enough coins to meet the funding amount + // requirements. selectedCoins, changeAmt, err := coinSelect(feeRate, amt, coins) if err != nil { - l.coinSelectMtx.Unlock() return err } @@ -1254,8 +1256,6 @@ func (l *LightningWallet) selectCoinsAndChange(feeRate uint64, amt btcutil.Amoun } } - l.coinSelectMtx.Unlock() - return nil }