Skip to content

Commit 0629601

Browse files
committed
rules: add History Limit rule
1 parent ad9dd3e commit 0629601

File tree

3 files changed

+402
-0
lines changed

3 files changed

+402
-0
lines changed

rules/history_limit.go

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
package rules
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"time"
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 HistoryLimit and HistoryLimitMgr
17+
// implement the appropriate Manager, Enforcer and Values interface.
18+
_ Manager = (*HistoryLimitMgr)(nil)
19+
_ Enforcer = (*HistoryLimit)(nil)
20+
_ Values = (*HistoryLimit)(nil)
21+
)
22+
23+
// HistoryLimitName is the string identifier of the HistoryLimit rule.
24+
const HistoryLimitName = "history-limit"
25+
26+
// HistoryLimitMgr manages the History limit rule.
27+
type HistoryLimitMgr struct{}
28+
29+
// Stop cleans up the resources held by the manager.
30+
//
31+
// NOTE: This is part of the Manager interface.
32+
func (h *HistoryLimitMgr) Stop() error {
33+
return nil
34+
}
35+
36+
// NewEnforcer constructs a new HistoryLimit rule enforcer using the passed
37+
// values and config.
38+
//
39+
// NOTE: This is part of the Manager interface.
40+
func (h *HistoryLimitMgr) NewEnforcer(_ Config, values Values) (Enforcer,
41+
error) {
42+
43+
limit, ok := values.(*HistoryLimit)
44+
if !ok {
45+
return nil, fmt.Errorf("values must be of type HistoryLimit, "+
46+
"got %T", values)
47+
}
48+
49+
return limit, nil
50+
}
51+
52+
// NewValueFromProto converts the given proto value into a HistoryLimit Value
53+
// object.
54+
//
55+
// NOTE: This is part of the Manager interface.
56+
func (h *HistoryLimitMgr) NewValueFromProto(v *litrpc.RuleValue) (Values,
57+
error) {
58+
59+
rv, ok := v.Value.(*litrpc.RuleValue_HistoryLimit)
60+
if !ok {
61+
return nil, fmt.Errorf("incorrect RuleValue type")
62+
}
63+
64+
historyLimit := rv.HistoryLimit
65+
66+
if historyLimit.StartTime != 0 && historyLimit.Duration != 0 {
67+
return nil, fmt.Errorf("cant set both start time and duration")
68+
}
69+
70+
if historyLimit.StartTime != 0 {
71+
return &HistoryLimit{
72+
StartDate: time.Unix(int64(historyLimit.StartTime), 0),
73+
}, nil
74+
}
75+
76+
return &HistoryLimit{
77+
Duration: time.Second * time.Duration(historyLimit.Duration),
78+
}, nil
79+
}
80+
81+
// EmptyValue returns a new HistoryLimit instance.
82+
//
83+
// NOTE: This is part of the Manager interface.
84+
func (h *HistoryLimitMgr) EmptyValue() Values {
85+
return &HistoryLimit{}
86+
}
87+
88+
// HistoryLimit represents the history-limit values.
89+
type HistoryLimit struct {
90+
StartDate time.Time `json:"start_date,omitempty"`
91+
Duration time.Duration `json:"duration,omitempty"`
92+
}
93+
94+
// HandleRequest checks the validity of a request using the HistoryLimit
95+
// rpcmiddleware.RoundTripCheckers.
96+
//
97+
// NOTE: this is part of the Rule interface.
98+
func (h *HistoryLimit) HandleRequest(ctx context.Context, uri string,
99+
msg proto.Message) (proto.Message, error) {
100+
101+
checkers := h.checkers()
102+
if checkers == nil {
103+
return nil, nil
104+
}
105+
106+
checker, ok := checkers[uri]
107+
if !ok {
108+
return nil, nil
109+
}
110+
111+
if !checker.HandlesRequest(msg.ProtoReflect().Type()) {
112+
return nil, fmt.Errorf("invalid implementation, checker for "+
113+
"URI %s does not accept request of type %v", uri,
114+
msg.ProtoReflect().Type())
115+
}
116+
117+
return checker.HandleRequest(ctx, msg)
118+
}
119+
120+
// HandleResponse handles a response using the HistoryLimit
121+
// rpcmiddleware.RoundTripCheckers.
122+
//
123+
// NOTE: this is part of the Rule interface.
124+
func (h *HistoryLimit) HandleResponse(ctx context.Context, uri string,
125+
msg proto.Message) (proto.Message, error) {
126+
127+
checkers := h.checkers()
128+
if checkers == nil {
129+
return nil, nil
130+
}
131+
132+
checker, ok := checkers[uri]
133+
if !ok {
134+
return nil, nil
135+
}
136+
137+
if !checker.HandlesResponse(msg.ProtoReflect().Type()) {
138+
return nil, fmt.Errorf("invalid implementation, checker for "+
139+
"URI %s does not accept response of type %v", uri,
140+
msg.ProtoReflect().Type())
141+
}
142+
143+
return checker.HandleResponse(ctx, msg)
144+
}
145+
146+
// HandleErrorResponse handles and possible alters an error. This is a noop for
147+
// the HistoryLimit rule.
148+
//
149+
// NOTE: this is part of the Enforcer interface.
150+
func (h *HistoryLimit) HandleErrorResponse(_ context.Context, _ string,
151+
_ error) (error, error) {
152+
153+
return nil, nil
154+
}
155+
156+
// checkers returns a map of URI to rpcmiddleware.RoundTripChecker which define
157+
// how the URI should be handled.
158+
func (h *HistoryLimit) checkers() map[string]mid.RoundTripChecker {
159+
return map[string]mid.RoundTripChecker{
160+
"/lnrpc.Lightning/ForwardingHistory": mid.NewRequestChecker(
161+
&lnrpc.ForwardingHistoryRequest{},
162+
&lnrpc.ForwardingHistoryResponse{},
163+
func(ctx context.Context,
164+
r *lnrpc.ForwardingHistoryRequest) error {
165+
166+
startDate := h.GetStartDate()
167+
168+
if r.StartTime >= uint64(startDate.Unix()) {
169+
return nil
170+
}
171+
172+
return fmt.Errorf("can't request a start "+
173+
"time before %s", startDate)
174+
},
175+
),
176+
"lnrpc.Lightning/ListInvoices": mid.NewResponseRewriter(
177+
&lnrpc.ListInvoiceRequest{},
178+
&lnrpc.ListInvoiceResponse{},
179+
func(ctx context.Context,
180+
r *lnrpc.ListInvoiceResponse) (proto.Message,
181+
error) {
182+
183+
startDate := h.GetStartDate()
184+
var invoices []*lnrpc.Invoice
185+
for _, i := range r.Invoices {
186+
if i.CreationDate < startDate.Unix() {
187+
continue
188+
}
189+
190+
invoices = append(invoices, i)
191+
}
192+
193+
r.Invoices = invoices
194+
return r, nil
195+
}, mid.PassThroughErrorHandler,
196+
),
197+
}
198+
}
199+
200+
// VerifySane checks that the value of the values is ok given the min and max
201+
// allowed values.
202+
//
203+
// NOTE: this is part of the Values interface.
204+
func (h *HistoryLimit) VerifySane(minVal, _ Values) error {
205+
minHL, ok := minVal.(*HistoryLimit)
206+
if !ok {
207+
return fmt.Errorf("min value is not of type HistoryLimit")
208+
}
209+
210+
if !h.GetStartDate().Before(minHL.GetStartDate()) {
211+
minDur := time.Since(minHL.GetStartDate())
212+
return fmt.Errorf("history-limit start date not valid for "+
213+
"given the minimum required start date. Start date "+
214+
"should at least be %s or the duration should at "+
215+
"least be %s", minHL.GetStartDate(), minDur)
216+
}
217+
218+
return nil
219+
}
220+
221+
// RuleName returns the name of the rule that these values are to be used with.
222+
//
223+
// NOTE: this is part of the Values interface.
224+
func (h *HistoryLimit) RuleName() string {
225+
return HistoryLimitName
226+
}
227+
228+
// ToProto converts the rule Values to the litrpc counterpart.
229+
//
230+
// NOTE: this is part of the Values interface.
231+
func (h *HistoryLimit) ToProto() *litrpc.RuleValue {
232+
return &litrpc.RuleValue{
233+
Value: &litrpc.RuleValue_HistoryLimit{
234+
HistoryLimit: &litrpc.HistoryLimit{
235+
StartTime: uint64(h.StartDate.Unix()),
236+
Duration: uint64(h.Duration.Seconds()),
237+
},
238+
},
239+
}
240+
}
241+
242+
// GetStartDate is a helper function that determines the start date of the values
243+
// given if a start date is set or a max duration is given.
244+
func (h *HistoryLimit) GetStartDate() time.Time {
245+
startDate := h.StartDate
246+
if h.StartDate.IsZero() {
247+
startDate = time.Now().Add(-h.Duration)
248+
}
249+
250+
return startDate
251+
}
252+
253+
// PseudoToReal attempts to convert any appropriate pseudo fields in the rule
254+
// Values to their corresponding real values. It uses the passed PrivacyMapDB to
255+
// find the real values. This is a no-op for the HistoryLimit rule.
256+
//
257+
// NOTE: this is part of the Values interface.
258+
func (h *HistoryLimit) PseudoToReal(_ firewalldb.PrivacyMapDB) (Values,
259+
error) {
260+
261+
return h, nil
262+
}
263+
264+
// RealToPseudo converts the rule Values to a new one that uses pseudo keys,
265+
// channel IDs, channel points etc. It returns a map of real to pseudo strings
266+
// that should be persisted. This is a no-op for the HistoryLimit rule.
267+
//
268+
// NOTE: this is part of the Values interface.
269+
func (h *HistoryLimit) RealToPseudo() (Values, map[string]string, error) {
270+
return h, nil, nil
271+
}

0 commit comments

Comments
 (0)