mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-12-09 12:22:38 +01:00
lnrpc: fix the existing routing fee inaccuracy
When updating the channel routing policy, we encounter an inaccurate precision error when calculating the routing fee. The issue stems from the way the IEEE 754 standard works. The solution here is to add a uint64 parameter (as mentioned in the issue) and keep the float64 fee_rate parameter but rounding the product of the base and fee rate.
This commit is contained in:
committed by
Olaoluwa Osuntokun
parent
234fdc6c9c
commit
ef1eff1058
58
rpcserver.go
58
rpcserver.go
@@ -6297,7 +6297,7 @@ func (r *rpcServer) DecodePayReq(ctx context.Context,
|
||||
// Nodes on the network advertise their fee rate using this point as a base.
|
||||
// This means that the minimal possible fee rate if 1e-6, or 0.000001, or
|
||||
// 0.0001%.
|
||||
const feeBase = 1000000
|
||||
const feeBase float64 = 1000000
|
||||
|
||||
// FeeReport allows the caller to obtain a report detailing the current fee
|
||||
// schedule enforced by the node globally for each channel.
|
||||
@@ -6330,7 +6330,7 @@ func (r *rpcServer) FeeReport(ctx context.Context,
|
||||
// 1mil mSAT sent, so will divide by this to get the proper fee
|
||||
// rate.
|
||||
feeRateFixedPoint := edgePolicy.FeeProportionalMillionths
|
||||
feeRate := float64(feeRateFixedPoint) / float64(feeBase)
|
||||
feeRate := float64(feeRateFixedPoint) / feeBase
|
||||
|
||||
// TODO(roasbeef): also add stats for revenue for each channel
|
||||
feeReports = append(feeReports, &lnrpc.ChannelFeeReport{
|
||||
@@ -6470,28 +6470,52 @@ func (r *rpcServer) UpdateChannelPolicy(ctx context.Context,
|
||||
return nil, fmt.Errorf("unknown scope: %v", scope)
|
||||
}
|
||||
|
||||
var feeRateFixed uint32
|
||||
|
||||
switch {
|
||||
// As a sanity check, if the fee isn't zero, we'll ensure that the
|
||||
// passed fee rate is below 1e-6, or the lowest allowed non-zero fee
|
||||
// rate expressible within the protocol.
|
||||
case req.FeeRate != 0 && req.FeeRate < minFeeRate:
|
||||
return nil, fmt.Errorf("fee rate of %v is too small, min fee "+
|
||||
"rate is %v", req.FeeRate, minFeeRate)
|
||||
// The request should use either the fee rate in percent, or the new
|
||||
// ppm rate, but not both.
|
||||
case req.FeeRate != 0 && req.FeeRatePpm != 0:
|
||||
errMsg := "cannot set both FeeRate and FeeRatePpm at the " +
|
||||
"same time"
|
||||
|
||||
return nil, status.Errorf(codes.InvalidArgument, errMsg)
|
||||
|
||||
// If the request is using fee_rate.
|
||||
case req.FeeRate != 0:
|
||||
// As a sanity check, if the fee isn't zero, we'll ensure that
|
||||
// the passed fee rate is below 1e-6, or the lowest allowed
|
||||
// non-zero fee rate expressible within the protocol.
|
||||
if req.FeeRate != 0 && req.FeeRate < minFeeRate {
|
||||
return nil, fmt.Errorf("fee rate of %v is too "+
|
||||
"small, min fee rate is %v", req.FeeRate,
|
||||
minFeeRate)
|
||||
}
|
||||
|
||||
// We'll also need to convert the floating point fee rate we
|
||||
// accept over RPC to the fixed point rate that we use within
|
||||
// the protocol. We do this by multiplying the passed fee rate
|
||||
// by the fee base. This gives us the fixed point, scaled by 1
|
||||
// million that's used within the protocol.
|
||||
//
|
||||
// Because of the inaccurate precision of the IEEE 754
|
||||
// standard, we need to round the product of feerate and
|
||||
// feebase.
|
||||
feeRateFixed = uint32(math.Round(req.FeeRate * feeBase))
|
||||
|
||||
// Otherwise, we use the fee_rate_ppm parameter.
|
||||
case req.FeeRatePpm != 0:
|
||||
feeRateFixed = req.FeeRatePpm
|
||||
}
|
||||
|
||||
// We'll also ensure that the user isn't setting a CLTV delta that
|
||||
// won't give outgoing HTLCs enough time to fully resolve if needed.
|
||||
case req.TimeLockDelta < minTimeLockDelta:
|
||||
if req.TimeLockDelta < minTimeLockDelta {
|
||||
return nil, fmt.Errorf("time lock delta of %v is too small, "+
|
||||
"minimum supported is %v", req.TimeLockDelta,
|
||||
minTimeLockDelta)
|
||||
}
|
||||
|
||||
// We'll also need to convert the floating point fee rate we accept
|
||||
// over RPC to the fixed point rate that we use within the protocol. We
|
||||
// do this by multiplying the passed fee rate by the fee base. This
|
||||
// gives us the fixed point, scaled by 1 million that's used within the
|
||||
// protocol.
|
||||
feeRateFixed := uint32(req.FeeRate * feeBase)
|
||||
baseFeeMsat := lnwire.MilliSatoshi(req.BaseFeeMsat)
|
||||
feeSchema := routing.FeeSchema{
|
||||
BaseFee: baseFeeMsat,
|
||||
@@ -6513,9 +6537,9 @@ func (r *rpcServer) UpdateChannelPolicy(ctx context.Context,
|
||||
}
|
||||
|
||||
rpcsLog.Debugf("[updatechanpolicy] updating channel policy base_fee=%v, "+
|
||||
"rate_float=%v, rate_fixed=%v, time_lock_delta: %v, "+
|
||||
"rate_fixed=%v, time_lock_delta: %v, "+
|
||||
"min_htlc=%v, max_htlc=%v, targets=%v",
|
||||
req.BaseFeeMsat, req.FeeRate, feeRateFixed, req.TimeLockDelta,
|
||||
req.BaseFeeMsat, feeRateFixed, req.TimeLockDelta,
|
||||
minHtlc, maxHtlc,
|
||||
spew.Sdump(targetChans))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user