multi: Inbound fees are retained when not provided

Fixes the problem that inbound base fee and fee rate are overwritten
with 0 if they are not specified in PolicyUpdateRequest. This ensures
backward compatibility with older rpc clients that do not yet support
the inbound feature.
This commit is contained in:
feelancer21 committed 2024-05-22 20:48:42 +02:00
1 parent 87d5170dec
commit f62c00fe34
8 files changed
+1457 -1322

No files matched your search

+21 -5
View File
@@ -2387,12 +2387,28 @@ func updateChannelPolicy(ctx *cli.Context) error {
return errors.New("inbound_fee_rate_ppm out of range") return errors.New("inbound_fee_rate_ppm out of range")
} }
// Inbound fees are optional. However, if an update is required,
// both the base fee and the fee rate must be provided.
var inboundFee *lnrpc.InboundFee
if ctx.IsSet("inbound_base_fee_msat") !=
ctx.IsSet("inbound_fee_rate_ppm") {
return errors.New("both parameters must be provided: " +
"inbound_base_fee_msat and inbound_fee_rate_ppm")
}
if ctx.IsSet("inbound_fee_rate_ppm") {
inboundFee = &lnrpc.InboundFee{
BaseFeeMsat: int32(inboundBaseFeeMsat),
FeeRatePpm: int32(inboundFeeRatePpm),
}
}
req := &lnrpc.PolicyUpdateRequest{ req := &lnrpc.PolicyUpdateRequest{
BaseFeeMsat: baseFee, BaseFeeMsat: baseFee,
TimeLockDelta: uint32(timeLockDelta), TimeLockDelta: uint32(timeLockDelta),
MaxHtlcMsat: ctx.Uint64("max_htlc_msat"), MaxHtlcMsat: ctx.Uint64("max_htlc_msat"),
InboundBaseFeeMsat: int32(inboundBaseFeeMsat), InboundFee: inboundFee,
InboundFeeRatePpm: int32(inboundFeeRatePpm),
} }
if ctx.IsSet("min_htlc_msat") { if ctx.IsSet("min_htlc_msat") {
+5 -3
View File
@@ -257,9 +257,11 @@ func updateChannelPolicy(ht *lntest.HarnessTest, hn *node.HarnessNode,
Scope: &lnrpc.PolicyUpdateRequest_ChanPoint{ Scope: &lnrpc.PolicyUpdateRequest_ChanPoint{
ChanPoint: chanPoint, ChanPoint: chanPoint,
}, },
MaxHtlcMsat: maxHtlc, MaxHtlcMsat: maxHtlc,
InboundBaseFeeMsat: inboundBaseFee, InboundFee: &lnrpc.InboundFee{
InboundFeeRatePpm: inboundFeeRate, BaseFeeMsat: inboundBaseFee,
FeeRatePpm: inboundFeeRate,
},
} }
hn.RPC.UpdateChannelPolicy(updateFeeReq) hn.RPC.UpdateChannelPolicy(updateFeeReq)
+1359 -1290
View File
File diff suppressed because it is too large. Load diff
+13 -2
View File
@@ -4369,6 +4369,16 @@ message FeeReportResponse {
uint64 month_fee_sum = 4; uint64 month_fee_sum = 4;
} }
message InboundFee {
// The inbound base fee charged regardless of the number of milli-satoshis
// received in the channel. By default, only negative values are accepted.
int32 base_fee_msat = 1;
// The effective inbound fee rate in micro-satoshis (parts per million).
// By default, only negative values are accepted.
int32 fee_rate_ppm = 2;
}
message PolicyUpdateRequest { message PolicyUpdateRequest {
oneof scope { oneof scope {
// If set, then this update applies to all currently active channels. // If set, then this update applies to all currently active channels.
@@ -4402,8 +4412,9 @@ message PolicyUpdateRequest {
// If true, min_htlc_msat is applied. // If true, min_htlc_msat is applied.
bool min_htlc_msat_specified = 8; bool min_htlc_msat_specified = 8;
int32 inbound_base_fee_msat = 10; // Optional inbound fee. If unset, the previously set value will be
int32 inbound_fee_rate_ppm = 11; // retained [EXPERIMENTAL].
InboundFee inbound_fee = 10;
} }
enum UpdateFailure { enum UpdateFailure {
+18 -7
View File
@@ -5297,6 +5297,21 @@
} }
} }
}, },
"lnrpcInboundFee": {
"type": "object",
"properties": {
"base_fee_msat": {
"type": "integer",
"format": "int32",
"description": "The inbound base fee charged regardless of the number of milli-satoshis\nreceived in the channel. By default, only negative values are accepted."
},
"fee_rate_ppm": {
"type": "integer",
"format": "int32",
"description": "The effective inbound fee rate in micro-satoshis (parts per million).\nBy default, only negative values are accepted."
}
}
},
"lnrpcInitiator": { "lnrpcInitiator": {
"type": "string", "type": "string",
"enum": [ "enum": [
@@ -6585,13 +6600,9 @@
"type": "boolean", "type": "boolean",
"description": "If true, min_htlc_msat is applied." "description": "If true, min_htlc_msat is applied."
}, },
"inbound_base_fee_msat": { "inbound_fee": {
"type": "integer", "$ref": "#/definitions/lnrpcInboundFee",
"format": "int32" "description": "Optional inbound fee. If unset, the previously set value will be\nretained [EXPERIMENTAL]."
},
"inbound_fee_rate_ppm": {
"type": "integer",
"format": "int32"
} }
} }
}, },
+19 -3
View File
@@ -9,6 +9,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/channeldb/models" "github.com/lightningnetwork/lnd/channeldb/models"
"github.com/lightningnetwork/lnd/discovery" "github.com/lightningnetwork/lnd/discovery"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
@@ -105,6 +106,14 @@ func (r *Manager) UpdatePolicy(newSchema routing.ChannelPolicy,
Edge: edge, Edge: edge,
}) })
// Extract inbound fees from the ExtraOpaqueData.
var inboundWireFee lnwire.Fee
_, err = edge.ExtraOpaqueData.ExtractRecords(&inboundWireFee)
if err != nil {
return err
}
inboundFee := models.NewInboundFeeFromWire(inboundWireFee)
// Add updated policy to list of policies to send to switch. // Add updated policy to list of policies to send to switch.
policiesToUpdate[info.ChannelPoint] = models.ForwardingPolicy{ policiesToUpdate[info.ChannelPoint] = models.ForwardingPolicy{
BaseFee: edge.FeeBaseMSat, BaseFee: edge.FeeBaseMSat,
@@ -112,7 +121,7 @@ func (r *Manager) UpdatePolicy(newSchema routing.ChannelPolicy,
TimeLockDelta: uint32(edge.TimeLockDelta), TimeLockDelta: uint32(edge.TimeLockDelta),
MinHTLCOut: edge.MinHTLC, MinHTLCOut: edge.MinHTLC,
MaxHTLC: edge.MaxHTLC, MaxHTLC: edge.MaxHTLC,
InboundFee: newSchema.InboundFee, InboundFee: inboundFee,
} }
return nil return nil
@@ -182,8 +191,15 @@ func (r *Manager) updateEdge(tx kvdb.RTx, chanPoint wire.OutPoint,
newSchema.FeeRate, newSchema.FeeRate,
) )
inboundFee := newSchema.InboundFee.ToWire() // If inbound fees are set, we update the edge with them.
if err := edge.ExtraOpaqueData.PackRecords(&inboundFee); err != nil { err := fn.MapOptionZ(newSchema.InboundFee,
func(f models.InboundFee) error {
inboundWireFee := f.ToWire()
return edge.ExtraOpaqueData.PackRecords(
&inboundWireFee,
)
})
if err != nil {
return err return err
} }
+2 -1
View File
@@ -22,6 +22,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/channeldb/models" "github.com/lightningnetwork/lnd/channeldb/models"
"github.com/lightningnetwork/lnd/clock" "github.com/lightningnetwork/lnd/clock"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/kvdb" "github.com/lightningnetwork/lnd/kvdb"
@@ -290,7 +291,7 @@ type FeeSchema struct {
// InboundFee is the inbound fee schedule that applies to forwards // InboundFee is the inbound fee schedule that applies to forwards
// coming in through a channel to which this FeeSchema pertains. // coming in through a channel to which this FeeSchema pertains.
InboundFee models.InboundFee InboundFee fn.Option[models.InboundFee]
} }
// ChannelPolicy holds the parameters that determine the policy we enforce // ChannelPolicy holds the parameters that determine the policy we enforce
+20 -11
View File
@@ -46,6 +46,7 @@ import (
"github.com/lightningnetwork/lnd/contractcourt" "github.com/lightningnetwork/lnd/contractcourt"
"github.com/lightningnetwork/lnd/discovery" "github.com/lightningnetwork/lnd/discovery"
"github.com/lightningnetwork/lnd/feature" "github.com/lightningnetwork/lnd/feature"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/funding" "github.com/lightningnetwork/lnd/funding"
"github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/htlcswitch/hop" "github.com/lightningnetwork/lnd/htlcswitch/hop"
@@ -7216,27 +7217,35 @@ func (r *rpcServer) UpdateChannelPolicy(ctx context.Context,
} }
// By default, positive inbound fees are rejected. // By default, positive inbound fees are rejected.
if !r.cfg.AcceptPositiveInboundFees { if !r.cfg.AcceptPositiveInboundFees && req.InboundFee != nil {
if req.InboundBaseFeeMsat > 0 { if req.InboundFee.BaseFeeMsat > 0 {
return nil, fmt.Errorf("positive values for inbound "+ return nil, fmt.Errorf("positive values for inbound "+
"base fee msat are not supported: %v", "base fee msat are not supported: %v",
req.InboundBaseFeeMsat) req.InboundFee.BaseFeeMsat)
} }
if req.InboundFeeRatePpm > 0 { if req.InboundFee.FeeRatePpm > 0 {
return nil, fmt.Errorf("positive values for inbound "+ return nil, fmt.Errorf("positive values for inbound "+
"fee rate ppm are not supported: %v", "fee rate ppm are not supported: %v",
req.InboundFeeRatePpm) req.InboundFee.FeeRatePpm)
} }
} }
// If no inbound fees have been specified, we indicate with an empty
// option that the previous inbound fee should be retained during the
// edge update.
inboundFee := fn.None[models.InboundFee]()
if req.InboundFee != nil {
inboundFee = fn.Some(models.InboundFee{
Base: req.InboundFee.BaseFeeMsat,
Rate: req.InboundFee.FeeRatePpm,
})
}
baseFeeMsat := lnwire.MilliSatoshi(req.BaseFeeMsat) baseFeeMsat := lnwire.MilliSatoshi(req.BaseFeeMsat)
feeSchema := routing.FeeSchema{ feeSchema := routing.FeeSchema{
BaseFee: baseFeeMsat, BaseFee: baseFeeMsat,
FeeRate: feeRateFixed, FeeRate: feeRateFixed,
InboundFee: models.InboundFee{ InboundFee: inboundFee,
Base: req.InboundBaseFeeMsat,
Rate: req.InboundFeeRatePpm,
},
} }
maxHtlc := lnwire.MilliSatoshi(req.MaxHtlcMsat) maxHtlc := lnwire.MilliSatoshi(req.MaxHtlcMsat)