From a36d6c0296612f308769d288e2a2a19e99e7649a Mon Sep 17 00:00:00 2001 From: bitromortac Date: Tue, 14 May 2024 12:20:05 +0200 Subject: [PATCH] rules: restrict initial channel fee parameters Channel policy boundaries are enforced for channel openings. --- rules/chan_policy_bounds.go | 77 ++++++++++++++++++++++++++++++ rules/chan_policy_bounds_test.go | 80 ++++++++++++++++++++++++++++++++ rules/channel_constraints.go | 5 ++ 3 files changed, 162 insertions(+) diff --git a/rules/chan_policy_bounds.go b/rules/chan_policy_bounds.go index ef939d7d..7ad3159c 100644 --- a/rules/chan_policy_bounds.go +++ b/rules/chan_policy_bounds.go @@ -175,6 +175,24 @@ func (f *ChanPolicyBounds) checkers() map[string]mid.RoundTripChecker { return f.checkPolicyUpdate(ctx, r) }, ), + "/lnrpc.Lightning/OpenChannelSync": mid.NewRequestChecker( + &lnrpc.OpenChannelRequest{}, + &lnrpc.ChannelPoint{}, + func(ctx context.Context, + r *lnrpc.OpenChannelRequest) error { + + return f.checkOpenChannelRequestFee(ctx, r) + }, + ), + "/lnrpc.Lightning/BatchOpenChannel": mid.NewRequestChecker( + &lnrpc.BatchOpenChannelRequest{}, + &lnrpc.BatchOpenChannelResponse{}, + func(ctx context.Context, + r *lnrpc.BatchOpenChannelRequest) error { + + return f.checkBatchOpenChannelRequestFee(ctx, r) + }, + ), } } @@ -221,6 +239,65 @@ func (f *ChanPolicyBounds) checkPolicyUpdate(_ context.Context, return nil } +// checkOpenChannelRequest verifies that the given lnrpc.OpenChannelRequest is +// valid given the ChannelConstraint values. +// Note: the onchain fee rate is checked by the onchain budget rule. +func (f *ChanPolicyBounds) checkOpenChannelRequestFee(_ context.Context, + req *lnrpc.OpenChannelRequest) error { + + return checkOpenRequestFee(f, req) +} + +// checkBatchOpenChannelRequest verifies that the given +// lnrpc.BatchOpenChannelRequest is valid for each individual channel requested +// given the ChannelConstraint values. +// Note: the onchain fee rate is checked by the onchain budget rule. +func (f *ChanPolicyBounds) checkBatchOpenChannelRequestFee(_ context.Context, + req *lnrpc.BatchOpenChannelRequest) error { + + // We check that each channel in the batch request is valid. + for _, openReq := range req.Channels { + err := checkOpenRequestFee(f, openReq) + if err != nil { + return err + } + } + + return nil +} + +// checkOpenRequestFee verifies that the given lnrpc.OpenChannelRequest is valid +// given the ChanPolicyBounds values. +func checkOpenRequestFee(f *ChanPolicyBounds, req ChanOpenReq) error { + if req.GetUseBaseFee() { + if req.GetBaseFee() < f.MinBaseMsat || + req.GetBaseFee() > f.MaxBaseMsat { + + return fmt.Errorf("invalid base fee amount") + } + } + + if req.GetUseFeeRate() { + if req.GetFeeRate() > math.MaxUint32 { + return fmt.Errorf("fee rate is too large") + } + + if uint32(req.GetFeeRate()) < f.MinRatePPM || + uint32(req.GetFeeRate()) > f.MaxRatePPM { + + return fmt.Errorf("invalid fee rate") + } + } + + if req.GetMinHtlcMsat() < int64(f.MinHtlcMsat) || + req.GetMinHtlcMsat() > int64(f.MaxHtlcMsat) { + + return fmt.Errorf("invalid min htlc msat amount") + } + + return nil +} + // VerifySane checks that the value of the values is ok given the min and max // allowed values. // diff --git a/rules/chan_policy_bounds_test.go b/rules/chan_policy_bounds_test.go index 30704582..8ad6b6d2 100644 --- a/rules/chan_policy_bounds_test.go +++ b/rules/chan_policy_bounds_test.go @@ -368,6 +368,86 @@ func TestChannelPolicyBoundsCheckers(t *testing.T) { }, expectErr: true, }, + { + name: "channel opening sync in bounds", + uri: "/lnrpc.Lightning/OpenChannelSync", + msg: &lnrpc.OpenChannelRequest{ + UseBaseFee: true, + BaseFee: 6, + UseFeeRate: true, + FeeRate: 6000000, + MinHtlcMsat: 20, + }, + }, + { + name: "channel opening high fee rate", + uri: "/lnrpc.Lightning/OpenChannelSync", + msg: &lnrpc.OpenChannelRequest{ + UseBaseFee: true, + BaseFee: 6, + UseFeeRate: true, + FeeRate: 11000000, + MinHtlcMsat: 20, + }, + expectErr: true, + }, + { + name: "channel opening high base fee", + uri: "/lnrpc.Lightning/OpenChannelSync", + msg: &lnrpc.OpenChannelRequest{ + UseBaseFee: true, + BaseFee: 11, + UseFeeRate: true, + FeeRate: 6000000, + MinHtlcMsat: 20, + }, + expectErr: true, + }, + { + name: "allowed batch channel opening", + uri: "/lnrpc.Lightning/BatchOpenChannel", + msg: &lnrpc.BatchOpenChannelRequest{ + Channels: []*lnrpc.BatchOpenChannel{ + { + UseBaseFee: true, + BaseFee: 5, + UseFeeRate: true, + FeeRate: 5000000, + MinHtlcMsat: 10, + }, + { + UseBaseFee: true, + BaseFee: 10, + UseFeeRate: true, + FeeRate: 10000000, + MinHtlcMsat: 10, + }, + }, + }, + }, + { + name: "batch channel opening low min htlc msat", + uri: "/lnrpc.Lightning/BatchOpenChannel", + msg: &lnrpc.BatchOpenChannelRequest{ + Channels: []*lnrpc.BatchOpenChannel{ + { + UseBaseFee: true, + BaseFee: 6, + UseFeeRate: true, + FeeRate: 6000000, + MinHtlcMsat: 20, + }, + { + UseBaseFee: true, + BaseFee: 6, + UseFeeRate: true, + FeeRate: 6000000, + MinHtlcMsat: 9, + }, + }, + }, + expectErr: true, + }, } for _, test := range tests { diff --git a/rules/channel_constraints.go b/rules/channel_constraints.go index 00c99460..8e287b89 100644 --- a/rules/channel_constraints.go +++ b/rules/channel_constraints.go @@ -207,6 +207,11 @@ type ChanOpenReq interface { GetCloseAddress() string GetPrivate() bool GetNodePubkey() []byte + GetBaseFee() uint64 + GetUseBaseFee() bool + GetFeeRate() uint64 + GetUseFeeRate() bool + GetMinHtlcMsat() int64 } // checkOpenRequest verifies that the given request is valid given the