|
| 1 | +package rules |
| 2 | + |
| 3 | +import ( |
| 4 | + "context" |
| 5 | + "fmt" |
| 6 | + "math" |
| 7 | + |
| 8 | + "github.com/lightninglabs/lightning-terminal/firewalldb" |
| 9 | + "github.com/lightninglabs/lightning-terminal/litrpc" |
| 10 | + mid "github.com/lightninglabs/lightning-terminal/rpcmiddleware" |
| 11 | + "github.com/lightningnetwork/lnd/lnrpc" |
| 12 | + "google.golang.org/protobuf/proto" |
| 13 | +) |
| 14 | + |
| 15 | +var ( |
| 16 | + // Compile-time checks to ensure that ChanPolicyBounds and |
| 17 | + // ChanPolicyBoundsMgr implement the appropriate Manager, Enforcer and |
| 18 | + // Values interface. |
| 19 | + _ Manager = (*ChanPolicyBoundsMgr)(nil) |
| 20 | + _ Enforcer = (*ChanPolicyBounds)(nil) |
| 21 | + _ Values = (*ChanPolicyBounds)(nil) |
| 22 | +) |
| 23 | + |
| 24 | +// ChanPolicyBoundsName is the string identifier of the ChanPolicyBounds rule. |
| 25 | +const ChanPolicyBoundsName = "channel-policy-bounds" |
| 26 | + |
| 27 | +// ChanPolicyBoundsMgr manages the ChanPolicyBounds rule. |
| 28 | +type ChanPolicyBoundsMgr struct{} |
| 29 | + |
| 30 | +// Stop cleans up the resources held by the manager. |
| 31 | +// |
| 32 | +// NOTE: This is part of the Manager interface. |
| 33 | +func (b *ChanPolicyBoundsMgr) Stop() error { |
| 34 | + return nil |
| 35 | +} |
| 36 | + |
| 37 | +// NewEnforcer constructs a new ChanPolicyBounds rule enforcer using the passed |
| 38 | +// values and config. |
| 39 | +// |
| 40 | +// NOTE: This is part of the Manager interface. |
| 41 | +func (b *ChanPolicyBoundsMgr) NewEnforcer(_ Config, values Values) (Enforcer, |
| 42 | + error) { |
| 43 | + |
| 44 | + bounds, ok := values.(*ChanPolicyBounds) |
| 45 | + if !ok { |
| 46 | + return nil, fmt.Errorf("values must be of type "+ |
| 47 | + "ChanPolicyBounds, got %T", values) |
| 48 | + } |
| 49 | + |
| 50 | + return bounds, nil |
| 51 | +} |
| 52 | + |
| 53 | +// NewValueFromProto converts the given proto value into a ChanPolicyBounds |
| 54 | +// Value object. |
| 55 | +// |
| 56 | +// NOTE: This is part of the Manager interface. |
| 57 | +func (b *ChanPolicyBoundsMgr) NewValueFromProto(value *litrpc.RuleValue) ( |
| 58 | + Values, error) { |
| 59 | + |
| 60 | + rv, ok := value.Value.(*litrpc.RuleValue_ChanPolicyBounds) |
| 61 | + if !ok { |
| 62 | + return nil, fmt.Errorf("incorrect RuleValue type") |
| 63 | + } |
| 64 | + |
| 65 | + policyBounds := rv.ChanPolicyBounds |
| 66 | + |
| 67 | + return &ChanPolicyBounds{ |
| 68 | + MinBaseMsat: policyBounds.MinBaseMsat, |
| 69 | + MaxBaseMsat: policyBounds.MaxBaseMsat, |
| 70 | + MinRatePPM: policyBounds.MinRatePpm, |
| 71 | + MaxRatePPM: policyBounds.MaxRatePpm, |
| 72 | + MinCLTVDelta: policyBounds.MinCltvDelta, |
| 73 | + MaxCLTVDelta: policyBounds.MaxCltvDelta, |
| 74 | + MinHtlcMsat: policyBounds.MinHtlcMsat, |
| 75 | + MaxHtlcMsat: policyBounds.MaxHtlcMsat, |
| 76 | + }, nil |
| 77 | +} |
| 78 | + |
| 79 | +// EmptyValue returns a new instance of ChanPolicyBounds. |
| 80 | +// |
| 81 | +// NOTE: This is part of the Manager interface. |
| 82 | +func (b *ChanPolicyBoundsMgr) EmptyValue() Values { |
| 83 | + return &ChanPolicyBounds{} |
| 84 | +} |
| 85 | + |
| 86 | +// ChanPolicyBounds represents the channel policy bounds rule. |
| 87 | +type ChanPolicyBounds struct { |
| 88 | + // MinBaseMsat is the minimum base fee in msat that can set for a |
| 89 | + // channel. |
| 90 | + MinBaseMsat uint64 `json:"min_base_msat"` |
| 91 | + |
| 92 | + // MaxBaseMsat is the maximum base fee in msat that can set for a |
| 93 | + // channel. |
| 94 | + MaxBaseMsat uint64 `json:"max_base_msat"` |
| 95 | + |
| 96 | + // MinRatePPM is the minimum ppm fee in msat that can be set for a |
| 97 | + // channel. |
| 98 | + MinRatePPM uint32 `json:"min_rate_ppm"` |
| 99 | + |
| 100 | + // MaxRatePPM is the maximum ppm fee in msat that can be set for a |
| 101 | + // channel. |
| 102 | + MaxRatePPM uint32 `json:"max_rate_ppm"` |
| 103 | + |
| 104 | + // MinCLTVDelta is the minimum cltv delta that may set for a channel. |
| 105 | + MinCLTVDelta uint32 `json:"min_cltv_delta"` |
| 106 | + |
| 107 | + // MaxCLTVDelta is the maximum cltv delta that may set for a channel. |
| 108 | + MaxCLTVDelta uint32 `json:"max_cltv_delta"` |
| 109 | + |
| 110 | + // MinHtlcMsat is the minimum htlc size msat that may set for a channel. |
| 111 | + MinHtlcMsat uint64 `json:"min_htlc_msat"` |
| 112 | + |
| 113 | + // MaxHtlcMsat is the maximum htlc size in msat that may be set for a |
| 114 | + // channel. |
| 115 | + MaxHtlcMsat uint64 `json:"max_htlc_msat"` |
| 116 | +} |
| 117 | + |
| 118 | +// HandleRequest checks the validity of a request using the ChanPolicyBounds |
| 119 | +// rpcmiddleware.RoundTripCheckers. |
| 120 | +// |
| 121 | +// NOTE: this is part of the Enforcer interface. |
| 122 | +func (f *ChanPolicyBounds) HandleRequest(ctx context.Context, uri string, |
| 123 | + msg proto.Message) (proto.Message, error) { |
| 124 | + |
| 125 | + checkers := f.checkers() |
| 126 | + if checkers == nil { |
| 127 | + return nil, nil |
| 128 | + } |
| 129 | + |
| 130 | + checker, ok := checkers[uri] |
| 131 | + if !ok { |
| 132 | + return nil, nil |
| 133 | + } |
| 134 | + |
| 135 | + if !checker.HandlesRequest(msg.ProtoReflect().Type()) { |
| 136 | + return nil, fmt.Errorf("invalid implementation, checker "+ |
| 137 | + "for URI %s does not accept request of type %v", |
| 138 | + uri, msg.ProtoReflect().Type()) |
| 139 | + } |
| 140 | + |
| 141 | + return checker.HandleRequest(ctx, msg) |
| 142 | +} |
| 143 | + |
| 144 | +// HandleResponse handles and possible alters a response. This is a noop for the |
| 145 | +// ChanPolicyBounds rule. |
| 146 | +// |
| 147 | +// NOTE: this is part of the Enforcer interface. |
| 148 | +func (f *ChanPolicyBounds) HandleResponse(_ context.Context, _ string, |
| 149 | + _ proto.Message) (proto.Message, error) { |
| 150 | + |
| 151 | + return nil, nil |
| 152 | +} |
| 153 | + |
| 154 | +// HandleErrorResponse handles and possible alters an error. This is a noop for |
| 155 | +// the ChanPolicyBounds rule. |
| 156 | +// |
| 157 | +// NOTE: this is part of the Enforcer interface. |
| 158 | +func (f *ChanPolicyBounds) HandleErrorResponse(_ context.Context, _ string, |
| 159 | + _ error) (error, error) { |
| 160 | + |
| 161 | + return nil, nil |
| 162 | +} |
| 163 | + |
| 164 | +// checkers returns a map of URI to rpcmiddleware.RoundTripChecker which define |
| 165 | +// how the URI should be handled. |
| 166 | +func (f *ChanPolicyBounds) checkers() map[string]mid.RoundTripChecker { |
| 167 | + return map[string]mid.RoundTripChecker{ |
| 168 | + "/lnrpc.Lightning/UpdateChannelPolicy": mid.NewRequestChecker( |
| 169 | + &lnrpc.PolicyUpdateRequest{}, |
| 170 | + &lnrpc.PolicyUpdateResponse{}, |
| 171 | + func(ctx context.Context, |
| 172 | + r *lnrpc.PolicyUpdateRequest) error { |
| 173 | + |
| 174 | + return f.checkPolicyUpdate(ctx, r) |
| 175 | + }, |
| 176 | + ), |
| 177 | + } |
| 178 | +} |
| 179 | + |
| 180 | +// checkPolicyUpdate verifies that the given lnrpc.PolicyUpdateRequest request |
| 181 | +// is valid given the ChanPolicyBounds values. |
| 182 | +func (f *ChanPolicyBounds) checkPolicyUpdate(_ context.Context, |
| 183 | + req *lnrpc.PolicyUpdateRequest) error { |
| 184 | + |
| 185 | + if req.BaseFeeMsat < int64(f.MinBaseMsat) || |
| 186 | + req.BaseFeeMsat > int64(f.MaxBaseMsat) { |
| 187 | + |
| 188 | + return fmt.Errorf("invalid base fee amount") |
| 189 | + } |
| 190 | + |
| 191 | + if req.FeeRate == 0 && req.FeeRatePpm == 0 && f.MinRatePPM > 0 { |
| 192 | + return fmt.Errorf("invalid fee rate") |
| 193 | + } |
| 194 | + |
| 195 | + feeRate := req.FeeRatePpm |
| 196 | + if req.FeeRate != 0 { |
| 197 | + feeRate = uint32(math.Round(req.FeeRate * 1000000)) |
| 198 | + } |
| 199 | + |
| 200 | + if feeRate < f.MinRatePPM || feeRate > f.MaxRatePPM { |
| 201 | + return fmt.Errorf("invalid fee rate") |
| 202 | + } |
| 203 | + |
| 204 | + if req.TimeLockDelta < f.MinCLTVDelta || |
| 205 | + req.TimeLockDelta > f.MaxCLTVDelta { |
| 206 | + |
| 207 | + return fmt.Errorf("invalid cltv delta") |
| 208 | + } |
| 209 | + |
| 210 | + if req.MinHtlcMsatSpecified { |
| 211 | + if req.MinHtlcMsat < f.MinHtlcMsat { |
| 212 | + return fmt.Errorf("invalid min htlc msat amount") |
| 213 | + } |
| 214 | + } |
| 215 | + |
| 216 | + if req.MaxHtlcMsat > f.MaxHtlcMsat { |
| 217 | + return fmt.Errorf("invalid max htlc msat amount") |
| 218 | + } |
| 219 | + |
| 220 | + return nil |
| 221 | +} |
| 222 | + |
| 223 | +// VerifySane checks that the value of the values is ok given the min and max |
| 224 | +// allowed values. |
| 225 | +// |
| 226 | +// NOTE: this is part of the Values interface. |
| 227 | +func (f *ChanPolicyBounds) VerifySane(minVal, maxVal Values) error { |
| 228 | + minFB, ok := minVal.(*ChanPolicyBounds) |
| 229 | + if !ok { |
| 230 | + return fmt.Errorf("min value is not of type ChanPolicyBounds") |
| 231 | + } |
| 232 | + |
| 233 | + maxFB, ok := maxVal.(*ChanPolicyBounds) |
| 234 | + if !ok { |
| 235 | + return fmt.Errorf("max value is not of type ChanPolicyBounds") |
| 236 | + } |
| 237 | + |
| 238 | + if !(f.MinBaseMsat >= minFB.MinBaseMsat && |
| 239 | + f.MinBaseMsat <= maxFB.MinBaseMsat) { |
| 240 | + |
| 241 | + return fmt.Errorf("invalid min base fee") |
| 242 | + } |
| 243 | + |
| 244 | + if !(f.MaxBaseMsat >= minFB.MaxBaseMsat && |
| 245 | + f.MaxBaseMsat <= maxFB.MaxBaseMsat) { |
| 246 | + |
| 247 | + return fmt.Errorf("invalid max base fee") |
| 248 | + } |
| 249 | + |
| 250 | + if !(f.MinRatePPM >= minFB.MinRatePPM && |
| 251 | + f.MinRatePPM <= maxFB.MinRatePPM) { |
| 252 | + |
| 253 | + return fmt.Errorf("invalid min proportional fee") |
| 254 | + } |
| 255 | + |
| 256 | + if !(f.MaxRatePPM >= minFB.MaxRatePPM && |
| 257 | + f.MaxRatePPM <= maxFB.MaxRatePPM) { |
| 258 | + |
| 259 | + return fmt.Errorf("invalid max proportional fee") |
| 260 | + } |
| 261 | + |
| 262 | + if !(f.MinCLTVDelta >= minFB.MinCLTVDelta && |
| 263 | + f.MinCLTVDelta <= maxFB.MinCLTVDelta) { |
| 264 | + |
| 265 | + return fmt.Errorf("invalid min cltv delta") |
| 266 | + } |
| 267 | + |
| 268 | + if !(f.MaxCLTVDelta >= minFB.MaxCLTVDelta && |
| 269 | + f.MaxCLTVDelta <= maxFB.MaxCLTVDelta) { |
| 270 | + |
| 271 | + return fmt.Errorf("invalid max cltv delta") |
| 272 | + } |
| 273 | + |
| 274 | + if !(f.MinHtlcMsat >= minFB.MinHtlcMsat && |
| 275 | + f.MinHtlcMsat <= maxFB.MinHtlcMsat) { |
| 276 | + |
| 277 | + return fmt.Errorf("invalid min htlc msat amt") |
| 278 | + } |
| 279 | + |
| 280 | + if !(f.MaxHtlcMsat >= minFB.MaxHtlcMsat && |
| 281 | + f.MaxHtlcMsat <= maxFB.MaxHtlcMsat) { |
| 282 | + |
| 283 | + return fmt.Errorf("invalid max htlc msat amt") |
| 284 | + } |
| 285 | + |
| 286 | + return nil |
| 287 | +} |
| 288 | + |
| 289 | +// ToProto converts the rule Values to the litrpc counterpart. |
| 290 | +// |
| 291 | +// NOTE: this is part of the Values interface. |
| 292 | +func (f *ChanPolicyBounds) ToProto() *litrpc.RuleValue { |
| 293 | + return &litrpc.RuleValue{ |
| 294 | + Value: &litrpc.RuleValue_ChanPolicyBounds{ |
| 295 | + ChanPolicyBounds: &litrpc.ChannelPolicyBounds{ |
| 296 | + MinBaseMsat: f.MinBaseMsat, |
| 297 | + MaxBaseMsat: f.MaxBaseMsat, |
| 298 | + MinRatePpm: f.MinRatePPM, |
| 299 | + MaxRatePpm: f.MaxRatePPM, |
| 300 | + MinCltvDelta: f.MinCLTVDelta, |
| 301 | + MaxCltvDelta: f.MaxCLTVDelta, |
| 302 | + MinHtlcMsat: f.MinHtlcMsat, |
| 303 | + MaxHtlcMsat: f.MaxHtlcMsat, |
| 304 | + }, |
| 305 | + }, |
| 306 | + } |
| 307 | +} |
| 308 | + |
| 309 | +// RuleName returns the name of the rule that these values are to be used with. |
| 310 | +// |
| 311 | +// NOTE: this is part of the Values interface. |
| 312 | +func (f *ChanPolicyBounds) RuleName() string { |
| 313 | + return ChanPolicyBoundsName |
| 314 | +} |
| 315 | + |
| 316 | +// PseudoToReal attempts to convert any appropriate pseudo fields in the rule |
| 317 | +// Values to their corresponding real values. It uses the passed PrivacyMapDB to |
| 318 | +// find the real values. This is a no-op for the ChanPolicyBounds rule. |
| 319 | +// |
| 320 | +// NOTE: this is part of the Values interface. |
| 321 | +func (f *ChanPolicyBounds) PseudoToReal(_ firewalldb.PrivacyMapDB) (Values, |
| 322 | + error) { |
| 323 | + |
| 324 | + return f, nil |
| 325 | +} |
| 326 | + |
| 327 | +// RealToPseudo converts the rule Values to a new one that uses pseudo keys, |
| 328 | +// channel IDs, channel points etc. It returns a map of real to pseudo strings |
| 329 | +// that should be persisted. This is a no-op for the ChanPolicyBounds rule. |
| 330 | +// |
| 331 | +// NOTE: this is part of the Values interface. |
| 332 | +func (f *ChanPolicyBounds) RealToPseudo() (Values, map[string]string, error) { |
| 333 | + return f, nil, nil |
| 334 | +} |
0 commit comments