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."`
|
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"`
|
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."`
|
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.
|
// the ChannelNotifier when channels become active and inactive.
|
||||||
NotifyActiveChannel func(wire.OutPoint)
|
NotifyActiveChannel func(wire.OutPoint)
|
||||||
NotifyInactiveChannel 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.
|
// 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
|
// payment circuit within our internal state so we can properly forward
|
||||||
// the ultimate settle message back latter.
|
// the ultimate settle message back latter.
|
||||||
case *lnwire.UpdateAddHTLC:
|
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 {
|
if packet.incomingChanID == sourceHop {
|
||||||
// A blank incomingChanID indicates that this is
|
// A blank incomingChanID indicates that this is
|
||||||
// a pending user-initiated payment.
|
// a pending user-initiated payment.
|
||||||
|
@ -8844,6 +8844,169 @@ out:
|
|||||||
cleanupForceClose(t, net, net.Bob, chanPointBob)
|
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
|
// graphSubscription houses the proxied update and error chans for a node's
|
||||||
// graph subscriptions.
|
// graph subscriptions.
|
||||||
type graphSubscription struct {
|
type graphSubscription struct {
|
||||||
@ -14049,6 +14212,10 @@ var testsCases = []*testCase{
|
|||||||
name: "multi-hop htlc error propagation",
|
name: "multi-hop htlc error propagation",
|
||||||
test: testHtlcErrorPropagation,
|
test: testHtlcErrorPropagation,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "reject onward htlc",
|
||||||
|
test: testRejectHTLC,
|
||||||
|
},
|
||||||
// TODO(roasbeef): multi-path integration test
|
// TODO(roasbeef): multi-path integration test
|
||||||
{
|
{
|
||||||
name: "node announcement",
|
name: "node announcement",
|
||||||
|
@ -441,6 +441,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB,
|
|||||||
AckEventTicker: ticker.New(htlcswitch.DefaultAckInterval),
|
AckEventTicker: ticker.New(htlcswitch.DefaultAckInterval),
|
||||||
NotifyActiveChannel: s.channelNotifier.NotifyActiveChannelEvent,
|
NotifyActiveChannel: s.channelNotifier.NotifyActiveChannelEvent,
|
||||||
NotifyInactiveChannel: s.channelNotifier.NotifyInactiveChannelEvent,
|
NotifyInactiveChannel: s.channelNotifier.NotifyInactiveChannelEvent,
|
||||||
|
RejectHTLC: cfg.RejectHTLC,
|
||||||
}, uint32(currentHeight))
|
}, uint32(currentHeight))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
Loading…
Reference in New Issue
Block a user