Skip to content
2 changes: 1 addition & 1 deletion channeldb/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -3443,7 +3443,7 @@ type ChannelEdgePolicy struct {
// properly validate the set of signatures that cover these new fields,
// and ensure we're able to make upgrades to the network in a forwards
// compatible manner.
ExtraOpaqueData []byte
ExtraOpaqueData lnwire.ExtraOpaqueData

db kvdb.Backend
}
Expand Down
21 changes: 15 additions & 6 deletions cmd/lncli/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -2019,6 +2019,12 @@ var updateChannelPolicyCommand = cli.Command{
"with a granularity of 0.000001 (millionths). Can not " +
"be set at the same time as fee_rate.",
},
cli.Int64Flag{
Name: "inbound_base_fee_msat",
},
cli.Int64Flag{
Name: "inbound_fee_rate_ppm",
},
cli.Int64Flag{
Name: "time_lock_delta",
Usage: "the CLTV delta that will be applied to all " +
Expand Down Expand Up @@ -2076,9 +2082,10 @@ func updateChannelPolicy(ctx *cli.Context) error {
defer cleanUp()

var (
baseFee int64
feeRate float64
feeRatePpm uint64
baseFee int64
feeRate float64
feeRatePpm uint64

timeLockDelta int64
err error
)
Expand Down Expand Up @@ -2150,9 +2157,11 @@ func updateChannelPolicy(ctx *cli.Context) error {
}

req := &lnrpc.PolicyUpdateRequest{
BaseFeeMsat: baseFee,
TimeLockDelta: uint32(timeLockDelta),
MaxHtlcMsat: ctx.Uint64("max_htlc_msat"),
BaseFeeMsat: baseFee,
TimeLockDelta: uint32(timeLockDelta),
MaxHtlcMsat: ctx.Uint64("max_htlc_msat"),
InboundBaseFeeMsat: int32(ctx.Int64("inbound_base_fee_msat")),
InboundFeeRatePpm: int32(ctx.Int64("inbound_fee_rate_ppm")),
}

if ctx.IsSet("min_htlc_msat") {
Expand Down
11 changes: 11 additions & 0 deletions docs/release-notes/release-notes-0.16.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,17 @@ current gossip sync query status.

* [Fix the issue of ghost UTXOs not being detected as spent if they were created
with an external tool](https://github.com/lightningnetwork/lnd/pull/7243).

## Routing

* Experimental support for [inbound routing
fees](https://github.com/lightningnetwork/lnd/pull/6703) is added. This allows
node operators to require senders to pay an inbound fee for forwards and
payments. It is recommended to only use negative fees (an inbound "discount")
initially to keep the channels open for senders that do not recognize inbound
fees. In this release, no send support for pathfinding and route building is
added yet. We first want to learn more about the impact that inbound fees have
on the routing economy.

## Build

Expand Down
27 changes: 17 additions & 10 deletions htlcswitch/hop/payload.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ type Payload struct {
// metadata is additional data that is sent along with the payment to
// the payee.
metadata []byte

// FailureMessageVersion is the version of the failure message format
// that the sender supports.
FailureMessageVersion byte
}

// NewLegacyPayload builds a Payload from the amount, cltv, and next hop
Expand All @@ -119,12 +123,13 @@ func NewLegacyPayload(f *sphinx.HopData) *Payload {
// should correspond to the bytes encapsulated in a TLV onion payload.
func NewPayloadFromReader(r io.Reader) (*Payload, error) {
var (
cid uint64
amt uint64
cltv uint32
mpp = &record.MPP{}
amp = &record.AMP{}
metadata []byte
cid uint64
amt uint64
cltv uint32
mpp = &record.MPP{}
amp = &record.AMP{}
metadata []byte
failureMessageVersion byte
)

tlvStream, err := tlv.NewStream(
Expand All @@ -134,6 +139,7 @@ func NewPayloadFromReader(r io.Reader) (*Payload, error) {
mpp.Record(),
amp.Record(),
record.NewMetadataRecord(&metadata),
record.NewOnionFailureMessageVersion(&failureMessageVersion),
)
if err != nil {
return nil, err
Expand Down Expand Up @@ -192,10 +198,11 @@ func NewPayloadFromReader(r io.Reader) (*Payload, error) {
AmountToForward: lnwire.MilliSatoshi(amt),
OutgoingCTLV: cltv,
},
MPP: mpp,
AMP: amp,
metadata: metadata,
customRecords: customRecords,
MPP: mpp,
AMP: amp,
metadata: metadata,
customRecords: customRecords,
FailureMessageVersion: failureMessageVersion,
}, nil
}

Expand Down
47 changes: 47 additions & 0 deletions htlcswitch/inbound_fee.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package htlcswitch

import "github.com/lightningnetwork/lnd/lnwire"

const feeRateParts = 1e6

type InboundFee struct {
Base int32
Rate int32
}

// NewInboundFeeFromWire constructs an inbound fee structure from a wire fee.
func NewInboundFeeFromWire(fee lnwire.Fee) InboundFee {
return InboundFee{
Base: fee.BaseFee,
Rate: fee.FeeRate,
}
}

// ToWire converts the inbound fee to a wire fee structure.
func (i *InboundFee) ToWire() lnwire.Fee {
return lnwire.Fee{
BaseFee: i.Base,
FeeRate: i.Rate,
}
}

// CalcFee calculates what the inbound fee should minimally be for forwarding
// the given amount. This amount is the _outgoing_ amount, which is what the
// inbound fee is based on.
func (i *InboundFee) CalcFee(amt lnwire.MilliSatoshi) int64 {
fee := int64(i.Base)

// Calculate proportional component. Always round down in favor of the
// payer of the fee. That way rounding differences can not cause a
// payment to fail.
switch {
case i.Rate > 0:
fee += int64(i.Rate) * int64(amt) / feeRateParts

case i.Rate < 0:
fee += (int64(i.Rate)*int64(amt) - (feeRateParts - 1)) /
feeRateParts
}

return fee
}
33 changes: 33 additions & 0 deletions htlcswitch/inbound_fee_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package htlcswitch

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestInboundFee(t *testing.T) {
t.Parallel()

// Test positive fee.
i := InboundFee{
Base: 5,
Rate: 500000,
}

require.Equal(t, int64(6), i.CalcFee(2))

// Expect fee to be rounded down.
require.Equal(t, int64(6), i.CalcFee(3))

// Test negative fee.
i = InboundFee{
Base: -5,
Rate: -500000,
}

require.Equal(t, int64(-6), i.CalcFee(2))

// Expect fee to be rounded down.
require.Equal(t, int64(-7), i.CalcFee(3))
}
2 changes: 2 additions & 0 deletions htlcswitch/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ type ChannelLink interface {
CheckHtlcForward(payHash [32]byte, incomingAmt lnwire.MilliSatoshi,
amtToForward lnwire.MilliSatoshi,
incomingTimeout, outgoingTimeout uint32,
incomingScid lnwire.ShortChannelID, inboundFee InboundFee,
senderFailureMessageVersion byte,
heightNow uint32, scid lnwire.ShortChannelID) *LinkError

// CheckHtlcTransit should return a nil error if the passed HTLC details
Expand Down
Loading