Merge pull request #2203 from ccdle12/reject-htlc-option
htlcswitch+config+server: adding RejectHTLC flag
This commit is contained in:
commit
9ef66f568f
@ -311,6 +311,8 @@ type config struct {
|
||||
|
||||
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."`
|
||||
|
||||
RejectHTLC bool `long:"rejecthtlc" description:"If true, lnd will not forward any HTLCs that are meant as onward payments. This option will still allow lnd to send HTLCs and receive HTLCs but lnd won't be used as a hop."`
|
||||
|
||||
StaggerInitialReconnect bool `long:"stagger-initial-reconnect" description:"If true, will apply a randomized staggering between 0s and 30s when reconnecting to persistent peers on startup. The first 10 reconnections will be attempted instantly, regardless of the flag's value"`
|
||||
|
||||
MaxOutgoingCltvExpiry uint32 `long:"max-cltv-expiry" description:"The maximum number of blocks funds could be locked up for when forwarding payments."`
|
||||
|
@ -171,6 +171,10 @@ type Config struct {
|
||||
// the ChannelNotifier when channels become active and inactive.
|
||||
NotifyActiveChannel func(wire.OutPoint)
|
||||
NotifyInactiveChannel func(wire.OutPoint)
|
||||
|
||||
// RejectHTLC is a flag that instructs the htlcswitch to reject any
|
||||
// HTLCs that are not from the source hop.
|
||||
RejectHTLC bool
|
||||
}
|
||||
|
||||
// Switch is the central messaging bus for all incoming/outgoing HTLCs.
|
||||
@ -1025,6 +1029,15 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
|
||||
// payment circuit within our internal state so we can properly forward
|
||||
// the ultimate settle message back latter.
|
||||
case *lnwire.UpdateAddHTLC:
|
||||
// Check if the node is set to reject all onward HTLCs and also make
|
||||
// sure that HTLC is not from the source node.
|
||||
if s.cfg.RejectHTLC && packet.incomingChanID != sourceHop {
|
||||
failure := &lnwire.FailChannelDisabled{}
|
||||
addErr := fmt.Errorf("unable to forward any htlcs")
|
||||
|
||||
return s.failAddPacket(packet, failure, addErr)
|
||||
}
|
||||
|
||||
if packet.incomingChanID == sourceHop {
|
||||
// A blank incomingChanID indicates that this is
|
||||
// a pending user-initiated payment.
|
||||
|
@ -8844,6 +8844,169 @@ out:
|
||||
cleanupForceClose(t, net, net.Bob, chanPointBob)
|
||||
}
|
||||
|
||||
// testRejectHTLC tests that a node can be created with the flag --rejecthtlc.
|
||||
// This means that the node will reject all forwarded HTLCs but can still
|
||||
// accept direct HTLCs as well as send HTLCs.
|
||||
func testRejectHTLC(net *lntest.NetworkHarness, t *harnessTest) {
|
||||
// RejectHTLC
|
||||
// Alice ------> Carol ------> Bob
|
||||
//
|
||||
const chanAmt = btcutil.Amount(1000000)
|
||||
ctxb := context.Background()
|
||||
timeout := time.Duration(time.Second * 5)
|
||||
|
||||
// Create Carol with reject htlc flag.
|
||||
carol, err := net.NewNode("Carol", []string{"--rejecthtlc"})
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create new node: %v", err)
|
||||
}
|
||||
defer shutdownAndAssert(net, t, carol)
|
||||
|
||||
// Connect Alice to Carol.
|
||||
if err := net.ConnectNodes(ctxb, net.Alice, carol); err != nil {
|
||||
t.Fatalf("unable to connect alice to carol: %v", err)
|
||||
}
|
||||
|
||||
// Connect Carol to Bob.
|
||||
if err := net.ConnectNodes(ctxb, carol, net.Bob); err != nil {
|
||||
t.Fatalf("unable to conenct carol to net.Bob: %v", err)
|
||||
}
|
||||
|
||||
// Send coins to Carol.
|
||||
err = net.SendCoins(ctxb, btcutil.SatoshiPerBitcoin, carol)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to send coins to carol: %v", err)
|
||||
}
|
||||
|
||||
// Send coins to Alice.
|
||||
err = net.SendCoins(ctxb, btcutil.SatoshiPerBitcent, net.Alice)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to send coins to alice: %v", err)
|
||||
}
|
||||
|
||||
// Open a channel between Alice and Carol.
|
||||
ctxt, _ := context.WithTimeout(ctxb, timeout)
|
||||
chanPointAlice := openChannelAndAssert(
|
||||
ctxt, t, net, net.Alice, carol,
|
||||
lntest.OpenChannelParams{
|
||||
Amt: chanAmt,
|
||||
},
|
||||
)
|
||||
|
||||
// Open a channel between Carol and Bob.
|
||||
ctxt, _ = context.WithTimeout(ctxb, timeout)
|
||||
chanPointCarol := openChannelAndAssert(
|
||||
ctxt, t, net, carol, net.Bob,
|
||||
lntest.OpenChannelParams{
|
||||
Amt: chanAmt,
|
||||
},
|
||||
)
|
||||
|
||||
// Channel should be ready for payments.
|
||||
const payAmt = 100
|
||||
|
||||
// Helper closure to generate a random pre image.
|
||||
genPreImage := func() []byte {
|
||||
preimage := make([]byte, 32)
|
||||
|
||||
_, err = rand.Read(preimage)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to generate preimage: %v", err)
|
||||
}
|
||||
|
||||
return preimage
|
||||
}
|
||||
|
||||
// Create an invoice from Carol of 100 satoshis.
|
||||
// We expect Alice to be able to pay this invoice.
|
||||
preimage := genPreImage()
|
||||
|
||||
carolInvoice := &lnrpc.Invoice{
|
||||
Memo: "testing - alice should pay carol",
|
||||
RPreimage: preimage,
|
||||
Value: payAmt,
|
||||
}
|
||||
|
||||
// Carol adds the invoice to her database.
|
||||
resp, err := carol.AddInvoice(ctxb, carolInvoice)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to add invoice: %v", err)
|
||||
}
|
||||
|
||||
// Alice pays Carols invoice.
|
||||
ctxt, _ = context.WithTimeout(ctxb, timeout)
|
||||
err = completePaymentRequests(
|
||||
ctxt, net.Alice, []string{resp.PaymentRequest}, true,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to send payments from alice to carol: %v", err)
|
||||
}
|
||||
|
||||
// Create an invoice from Bob of 100 satoshis.
|
||||
// We expect Carol to be able to pay this invoice.
|
||||
preimage = genPreImage()
|
||||
|
||||
bobInvoice := &lnrpc.Invoice{
|
||||
Memo: "testing - carol should pay bob",
|
||||
RPreimage: preimage,
|
||||
Value: payAmt,
|
||||
}
|
||||
|
||||
// Bob adds the invoice to his database.
|
||||
resp, err = net.Bob.AddInvoice(ctxb, bobInvoice)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to add invoice: %v", err)
|
||||
}
|
||||
|
||||
// Carol pays Bobs invoice.
|
||||
ctxt, _ = context.WithTimeout(ctxb, timeout)
|
||||
err = completePaymentRequests(
|
||||
ctxt, carol, []string{resp.PaymentRequest}, true,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to send payments from carol to bob: %v", err)
|
||||
}
|
||||
|
||||
// Create an invoice from Bob of 100 satoshis.
|
||||
// Alice attempts to pay Bob but this should fail, since we are
|
||||
// using Carol as a hop and her node will reject onward HTLCs.
|
||||
preimage = genPreImage()
|
||||
|
||||
bobInvoice = &lnrpc.Invoice{
|
||||
Memo: "testing - alice tries to pay bob",
|
||||
RPreimage: preimage,
|
||||
Value: payAmt,
|
||||
}
|
||||
|
||||
// Bob adds the invoice to his database.
|
||||
resp, err = net.Bob.AddInvoice(ctxb, bobInvoice)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to add invoice: %v", err)
|
||||
}
|
||||
|
||||
// Alice attempts to pay Bobs invoice. This payment should be rejected since
|
||||
// we are using Carol as an intermediary hop, Carol is running lnd with
|
||||
// --rejecthtlc.
|
||||
ctxt, _ = context.WithTimeout(ctxb, timeout)
|
||||
err = completePaymentRequests(
|
||||
ctxt, net.Alice, []string{resp.PaymentRequest}, true,
|
||||
)
|
||||
if err == nil {
|
||||
t.Fatalf(
|
||||
"should have been rejected, carol will not accept forwarded htlcs",
|
||||
)
|
||||
}
|
||||
if !strings.Contains(err.Error(), lnwire.CodeChannelDisabled.String()) {
|
||||
t.Fatalf("error returned should have been Channel Disabled")
|
||||
}
|
||||
|
||||
// Close all channels.
|
||||
ctxt, _ = context.WithTimeout(ctxb, timeout)
|
||||
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPointAlice, false)
|
||||
ctxt, _ = context.WithTimeout(ctxb, timeout)
|
||||
closeChannelAndAssert(ctxt, t, net, carol, chanPointCarol, false)
|
||||
}
|
||||
|
||||
// graphSubscription houses the proxied update and error chans for a node's
|
||||
// graph subscriptions.
|
||||
type graphSubscription struct {
|
||||
@ -14049,6 +14212,10 @@ var testsCases = []*testCase{
|
||||
name: "multi-hop htlc error propagation",
|
||||
test: testHtlcErrorPropagation,
|
||||
},
|
||||
{
|
||||
name: "reject onward htlc",
|
||||
test: testRejectHTLC,
|
||||
},
|
||||
// TODO(roasbeef): multi-path integration test
|
||||
{
|
||||
name: "node announcement",
|
||||
|
@ -441,6 +441,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB,
|
||||
AckEventTicker: ticker.New(htlcswitch.DefaultAckInterval),
|
||||
NotifyActiveChannel: s.channelNotifier.NotifyActiveChannelEvent,
|
||||
NotifyInactiveChannel: s.channelNotifier.NotifyInactiveChannelEvent,
|
||||
RejectHTLC: cfg.RejectHTLC,
|
||||
}, uint32(currentHeight))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
Loading…
Reference in New Issue
Block a user