lnrpc/invoicesrpc: add blinded path policy buffer

This commit adds a helper function that will be used to adjust a hops
policy values by certain given increase and decrease multipliers. This
will be used in blinded paths to give policy values some buffer to avoid
easy probing of blinded paths.
This commit is contained in:
Elle Mouton 2024-05-04 11:11:35 +02:00
parent 3b2a6042ff
commit 0855e3e71a
No known key found for this signature in database
GPG key ID: D7D916376026F177
2 changed files with 224 additions and 0 deletions

View file

@ -844,6 +844,77 @@ func PopulateHopHints(cfg *SelectHopHintsCfg, amtMSat lnwire.MilliSatoshi,
return hopHints, nil
}
// blindedHopPolicy holds the set of relay policy values to use for a channel
// in a blinded path.
type blindedHopPolicy struct {
cltvExpiryDelta uint16
feeRate uint32
baseFee lnwire.MilliSatoshi
minHTLCMsat lnwire.MilliSatoshi
maxHTLCMsat lnwire.MilliSatoshi
}
// addPolicyBuffer constructs the bufferedChanPolicies for a path hop by taking
// its actual policy values and multiplying them by the given multipliers.
// The base fee, fee rate and minimum HTLC msat values are adjusted via the
// incMultiplier while the maximum HTLC msat value is adjusted via the
// decMultiplier. If adjustments of the HTLC values no longer make sense
// then the original HTLC value is used.
func addPolicyBuffer(policy *blindedHopPolicy, incMultiplier,
decMultiplier float64) (*blindedHopPolicy, error) {
if incMultiplier < 1 {
return nil, fmt.Errorf("blinded path policy increase " +
"multiplier must be greater than or equal to 1")
}
if decMultiplier < 0 || decMultiplier > 1 {
return nil, fmt.Errorf("blinded path policy decrease " +
"multiplier must be in the range [0;1]")
}
var (
minHTLCMsat = lnwire.MilliSatoshi(
float64(policy.minHTLCMsat) * incMultiplier,
)
maxHTLCMsat = lnwire.MilliSatoshi(
float64(policy.maxHTLCMsat) * decMultiplier,
)
)
// Make sure the new minimum is not more than the original maximum.
// If it is, then just stick to the original minimum.
if minHTLCMsat > policy.maxHTLCMsat {
minHTLCMsat = policy.minHTLCMsat
}
// Make sure the new maximum is not less than the original minimum.
// If it is, then just stick to the original maximum.
if maxHTLCMsat < policy.minHTLCMsat {
maxHTLCMsat = policy.maxHTLCMsat
}
// Also ensure that the new htlc bounds make sense. If the new minimum
// is greater than the new maximum, then just let both to their original
// values.
if minHTLCMsat > maxHTLCMsat {
minHTLCMsat = policy.minHTLCMsat
maxHTLCMsat = policy.maxHTLCMsat
}
return &blindedHopPolicy{
cltvExpiryDelta: uint16(
float64(policy.cltvExpiryDelta) * incMultiplier,
),
feeRate: uint32(float64(policy.feeRate) * incMultiplier),
baseFee: lnwire.MilliSatoshi(
float64(policy.baseFee) * incMultiplier,
),
minHTLCMsat: minHTLCMsat,
maxHTLCMsat: maxHTLCMsat,
}, nil
}
// calcBlindedPathPolicies computes the accumulated policy values for the path.
// These values include the total base fee, the total proportional fee and the
// total CLTV delta. This function assumes that all the passed relay infos have

View file

@ -902,6 +902,159 @@ func TestPopulateHopHints(t *testing.T) {
}
}
// TestApplyBlindedPathPolicyBuffer tests blinded policy adjustments.
func TestApplyBlindedPathPolicyBuffer(t *testing.T) {
tests := []struct {
name string
policyIn *blindedHopPolicy
expectedOut *blindedHopPolicy
incMultiplier float64
decMultiplier float64
expectedError string
}{
{
name: "invalid increase multiplier",
incMultiplier: 0,
expectedError: "blinded path policy increase " +
"multiplier must be greater than or equal to 1",
},
{
name: "decrease multiplier too small",
incMultiplier: 1,
decMultiplier: -1,
expectedError: "blinded path policy decrease " +
"multiplier must be in the range [0;1]",
},
{
name: "decrease multiplier too big",
incMultiplier: 1,
decMultiplier: 2,
expectedError: "blinded path policy decrease " +
"multiplier must be in the range [0;1]",
},
{
name: "no change",
incMultiplier: 1,
decMultiplier: 1,
policyIn: &blindedHopPolicy{
cltvExpiryDelta: 1,
minHTLCMsat: 2,
maxHTLCMsat: 3,
baseFee: 4,
feeRate: 5,
},
expectedOut: &blindedHopPolicy{
cltvExpiryDelta: 1,
minHTLCMsat: 2,
maxHTLCMsat: 3,
baseFee: 4,
feeRate: 5,
},
},
{
name: "buffer up by 100% and down by and down " +
"by 50%",
incMultiplier: 2,
decMultiplier: 0.5,
policyIn: &blindedHopPolicy{
cltvExpiryDelta: 10,
minHTLCMsat: 20,
maxHTLCMsat: 300,
baseFee: 40,
feeRate: 50,
},
expectedOut: &blindedHopPolicy{
cltvExpiryDelta: 20,
minHTLCMsat: 40,
maxHTLCMsat: 150,
baseFee: 80,
feeRate: 100,
},
},
{
name: "new HTLC minimum larger than OG " +
"maximum",
incMultiplier: 2,
decMultiplier: 1,
policyIn: &blindedHopPolicy{
cltvExpiryDelta: 10,
minHTLCMsat: 20,
maxHTLCMsat: 30,
baseFee: 40,
feeRate: 50,
},
expectedOut: &blindedHopPolicy{
cltvExpiryDelta: 20,
minHTLCMsat: 20,
maxHTLCMsat: 30,
baseFee: 80,
feeRate: 100,
},
},
{
name: "new HTLC maximum smaller than OG " +
"minimum",
incMultiplier: 1,
decMultiplier: 0.5,
policyIn: &blindedHopPolicy{
cltvExpiryDelta: 10,
minHTLCMsat: 20,
maxHTLCMsat: 30,
baseFee: 40,
feeRate: 50,
},
expectedOut: &blindedHopPolicy{
cltvExpiryDelta: 10,
minHTLCMsat: 20,
maxHTLCMsat: 30,
baseFee: 40,
feeRate: 50,
},
},
{
name: "new HTLC minimum and maximums are not " +
"compatible",
incMultiplier: 2,
decMultiplier: 0.5,
policyIn: &blindedHopPolicy{
cltvExpiryDelta: 10,
minHTLCMsat: 30,
maxHTLCMsat: 100,
baseFee: 40,
feeRate: 50,
},
expectedOut: &blindedHopPolicy{
cltvExpiryDelta: 20,
minHTLCMsat: 30,
maxHTLCMsat: 100,
baseFee: 80,
feeRate: 100,
},
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
bufferedPolicy, err := addPolicyBuffer(
test.policyIn, test.incMultiplier,
test.decMultiplier,
)
if test.expectedError != "" {
require.ErrorContains(
t, err, test.expectedError,
)
return
}
require.Equal(t, test.expectedOut, bufferedPolicy)
})
}
}
// TestBlindedPathAccumulatedPolicyCalc tests the logic for calculating the
// accumulated routing policies of a blinded route against an example mentioned
// in the spec document: