diff --git a/config.go b/config.go index 26e4c522..a571bacc 100644 --- a/config.go +++ b/config.go @@ -235,6 +235,8 @@ type config struct { NoChanUpdates bool `long:"nochanupdates" description:"If specified, lnd will not request real-time channel updates from connected peers. This option should be used by routing nodes to save bandwidth."` + RejectPush bool `long:"rejectpush" description:"If true, lnd will not accept channel opening requests with non-zero push amounts. This should prevent accidental pushes to merchant nodes."` + net tor.Net Routing *routing.Conf `group:"routing" namespace:"routing"` diff --git a/fundingmanager.go b/fundingmanager.go index 46a7a2bd..a84cbb8b 100644 --- a/fundingmanager.go +++ b/fundingmanager.go @@ -1032,6 +1032,15 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) { return } + // If request specifies non-zero push amount and 'rejectpush' is set, + // signal an error. + if cfg.RejectPush && msg.PushAmount > 0 { + f.failFundingFlow( + fmsg.peer, fmsg.msg.PendingChannelID, + lnwallet.ErrNonZeroPushAmount()) + return + } + fndgLog.Infof("Recv'd fundingRequest(amt=%v, push=%v, delay=%v, "+ "pendingId=%x) from peer(%x)", amt, msg.PushAmount, msg.CsvDelay, msg.PendingChannelID, diff --git a/fundingmanager_test.go b/fundingmanager_test.go index 969e4efe..c182d015 100644 --- a/fundingmanager_test.go +++ b/fundingmanager_test.go @@ -2477,3 +2477,63 @@ func TestFundingManagerMaxPendingChannels(t *testing.T) { t, bob.msgChan, "AcceptChannel", ).(*lnwire.AcceptChannel) } + +// TestFundingManagerRejectPush checks behaviour of 'rejectpush' +// option, namely that non-zero incoming push amounts are disabled. +func TestFundingManagerRejectPush(t *testing.T) { + // Enable 'rejectpush' option and initialize funding managers. + alice, bob := setupFundingManagers(t, defaultMaxPendingChannels) + rejectPush := cfg.RejectPush + cfg.RejectPush = true + defer func() { + tearDownFundingManagers(t, alice, bob) + cfg.RejectPush = rejectPush + }() + + // Create a funding request and start the workflow. + updateChan := make(chan *lnrpc.OpenStatusUpdate) + errChan := make(chan error, 1) + initReq := &openChanReq{ + targetPubkey: bob.privKey.PubKey(), + chainHash: *activeNetParams.GenesisHash, + localFundingAmt: 500000, + pushAmt: lnwire.NewMSatFromSatoshis(10), + private: true, + updates: updateChan, + err: errChan, + } + + alice.fundingMgr.initFundingWorkflow(bob, 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, alice) + + // Assert Bob responded with an ErrNonZeroPushAmount error. + err := assertFundingMsgSent(t, bob.msgChan, "Error").(*lnwire.Error) + if "Non-zero push amounts are disabled" != string(err.Data) { + t.Fatalf("expected ErrNonZeroPushAmount error, got \"%v\"", + string(err.Data)) + } +} diff --git a/lnwallet/errors.go b/lnwallet/errors.go index f36d0b99..0d208623 100644 --- a/lnwallet/errors.go +++ b/lnwallet/errors.go @@ -78,6 +78,13 @@ func ErrChanReserveTooLarge(reserve, } } +// ErrNonZeroPushAmount is returned by a remote peer that receives a +// FundingOpen request for a channel with non-zero push amount while +// they have 'rejectpush' enabled. +func ErrNonZeroPushAmount() ReservationError { + return ReservationError{errors.New("Non-zero push amounts are disabled")} +} + // ErrMinHtlcTooLarge returns an error indicating that the MinHTLC value the // remote required is too large to be accepted. func ErrMinHtlcTooLarge(minHtlc,