Merge pull request #1078 from lightninglabs/sql40

[sql-40] firewalldb: action store prep commits
This commit is contained in:
Elle 2025-05-29 06:40:09 +02:00 committed by GitHub
commit bd704c41e3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 66 additions and 26 deletions

View file

@ -9,6 +9,7 @@ import (
"github.com/lightninglabs/lightning-terminal/firewalldb"
mid "github.com/lightninglabs/lightning-terminal/rpcmiddleware"
"github.com/lightninglabs/lightning-terminal/session"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/macaroons"
)
@ -181,16 +182,15 @@ func (r *RequestLogger) Intercept(ctx context.Context,
func (r *RequestLogger) addNewAction(ctx context.Context, ri *RequestInfo,
withPayloadData bool) error {
// If no macaroon is provided, then an empty 4-byte array is used as the
// macaroon ID. Otherwise, the last 4 bytes of the macaroon's root key
// ID are used.
var macaroonID [4]byte
var macaroonID fn.Option[[4]byte]
if ri.Macaroon != nil {
var err error
macaroonID, err = session.IDFromMacaroon(ri.Macaroon)
macID, err := session.IDFromMacaroon(ri.Macaroon)
if err != nil {
return fmt.Errorf("could not extract ID from macaroon")
}
macaroonID = fn.Some([4]byte(macID))
}
actionReq := &firewalldb.AddActionReq{

View file

@ -36,7 +36,8 @@ const (
type AddActionReq struct {
// MacaroonIdentifier is a 4 byte identifier created from the last 4
// bytes of the root key ID of the macaroon used to perform the action.
MacaroonIdentifier [4]byte
// If no macaroon was used for the action, then this will not be set.
MacaroonIdentifier fn.Option[[4]byte]
// SessionID holds the optional session ID of the session that this
// action was performed with.

View file

@ -58,6 +58,13 @@ var (
func (db *BoltDB) AddAction(ctx context.Context,
req *AddActionReq) (ActionLocator, error) {
// If no macaroon is provided, then an empty 4-byte array is used as the
// macaroon ID.
var macaroonID [4]byte
req.MacaroonIdentifier.WhenSome(func(id [4]byte) {
macaroonID = id
})
// If the new action links to a session, the session must exist.
// For the bbolt impl of the store, this is our best effort attempt
// at ensuring each action links to a session. If the session is
@ -105,7 +112,7 @@ func (db *BoltDB) AddAction(ctx context.Context,
}
sessBucket, err := actionsBucket.CreateBucketIfNotExists(
action.MacaroonIdentifier[:],
macaroonID[:],
)
if err != nil {
return err
@ -134,7 +141,7 @@ func (db *BoltDB) AddAction(ctx context.Context,
}
locator = kvdbActionLocator{
sessionID: action.MacaroonIdentifier,
sessionID: macaroonID,
actionID: nextActionIndex,
}
@ -323,7 +330,13 @@ func (db *BoltDB) ListActions(ctx context.Context, query *ListActionsQuery,
)
}
if opts.groupID != session.EmptyID {
actions, err := db.listGroupActions(ctx, opts.groupID, filterFn)
var reversed bool
if query != nil {
reversed = query.Reversed
}
actions, err := db.listGroupActions(
ctx, opts.groupID, filterFn, reversed,
)
if err != nil {
return nil, 0, 0, err
}
@ -432,11 +445,11 @@ func (db *BoltDB) listSessionActions(sessionID session.ID,
//
// TODO: update to allow for pagination.
func (db *BoltDB) listGroupActions(ctx context.Context, groupID session.ID,
filterFn listActionsFilterFn) ([]*Action, error) {
filterFn listActionsFilterFn, reversed bool) ([]*Action, error) {
if filterFn == nil {
filterFn = func(a *Action, reversed bool) (bool, bool) {
return true, true
return true, reversed
}
}
@ -475,9 +488,18 @@ func (db *BoltDB) listGroupActions(ctx context.Context, groupID session.ID,
return err
}
include, cont := filterFn(action, false)
include, cont := filterFn(action, reversed)
if include {
actions = append(actions, action)
if !reversed {
actions = append(
actions, action,
)
} else {
actions = append(
[]*Action{action},
actions...,
)
}
}
if !cont {
@ -574,7 +596,7 @@ func DeserializeAction(r io.Reader, sessionID session.ID) (*Action, error) {
return nil, err
}
action.MacaroonIdentifier = sessionID
action.MacaroonIdentifier = fn.Some([4]byte(sessionID))
action.SessionID = fn.Some(sessionID)
action.ActorName = string(actor)
action.FeatureName = string(featureName)

View file

@ -14,8 +14,8 @@ import (
)
var (
testTime1 = time.Unix(32100, 0)
testTime2 = time.Unix(12300, 0)
testTime1 = time.Unix(12300, 0)
testTime2 = time.Unix(32100, 0)
)
// TestActionStorage tests that the ActionsListDB CRUD logic.
@ -67,7 +67,7 @@ func TestActionStorage(t *testing.T) {
action1Req := &AddActionReq{
SessionID: fn.Some(sess1.ID),
AccountID: fn.Some(acct1.ID),
MacaroonIdentifier: sess1.ID,
MacaroonIdentifier: fn.Some([4]byte(sess1.ID)),
ActorName: "Autopilot",
FeatureName: "auto-fees",
Trigger: "fee too low",
@ -85,7 +85,7 @@ func TestActionStorage(t *testing.T) {
action2Req := &AddActionReq{
SessionID: fn.Some(sess2.ID),
MacaroonIdentifier: sess2.ID,
MacaroonIdentifier: fn.Some([4]byte(sess2.ID)),
ActorName: "Autopilot",
FeatureName: "rebalancer",
Trigger: "channels not balanced",
@ -161,7 +161,7 @@ func TestActionStorage(t *testing.T) {
// Check that providing no session id and no filter function returns
// all the actions.
actions, _, _, err = db.ListActions(nil, &ListActionsQuery{
actions, _, _, err = db.ListActions(ctx, &ListActionsQuery{
IndexOffset: 0,
MaxNum: 100,
Reversed: false,
@ -223,7 +223,7 @@ func TestListActions(t *testing.T) {
actionIds++
actionReq := &AddActionReq{
MacaroonIdentifier: sessionID,
MacaroonIdentifier: fn.Some(sessionID),
ActorName: "Autopilot",
FeatureName: fmt.Sprintf("%d", actionIds),
Trigger: "fee too low",
@ -245,9 +245,11 @@ func TestListActions(t *testing.T) {
assertActions := func(dbActions []*Action, al []*action) {
require.Len(t, dbActions, len(al))
for i, a := range al {
require.EqualValues(
t, a.sessionID, dbActions[i].MacaroonIdentifier,
mID, err := dbActions[i].MacaroonIdentifier.UnwrapOrErr(
fmt.Errorf("macaroon identifier is none"),
)
require.NoError(t, err)
require.EqualValues(t, a.sessionID, mID)
require.Equal(t, a.actionID, dbActions[i].FeatureName)
}
}
@ -433,7 +435,7 @@ func TestListGroupActions(t *testing.T) {
action1Req := &AddActionReq{
SessionID: fn.Some(sess1.ID),
MacaroonIdentifier: sess1.ID,
MacaroonIdentifier: fn.Some([4]byte(sess1.ID)),
ActorName: "Autopilot",
FeatureName: "auto-fees",
Trigger: "fee too low",
@ -451,7 +453,7 @@ func TestListGroupActions(t *testing.T) {
action2Req := &AddActionReq{
SessionID: fn.Some(sess2.ID),
MacaroonIdentifier: sess2.ID,
MacaroonIdentifier: fn.Some([4]byte(sess2.ID)),
ActorName: "Autopilot",
FeatureName: "rebalancer",
Trigger: "channels not balanced",
@ -495,12 +497,22 @@ func TestListGroupActions(t *testing.T) {
_, err = db.AddAction(ctx, action2Req)
require.NoError(t, err)
// There should now be actions in the group.
// There should now be two actions in the group.
al, _, _, err = db.ListActions(ctx, nil, WithActionGroupID(group1))
require.NoError(t, err)
require.Len(t, al, 2)
assertEqualActions(t, action1, al[0])
assertEqualActions(t, action2, al[1])
// Try the reversed query too.
al, _, _, err = db.ListActions(
ctx, &ListActionsQuery{Reversed: true},
WithActionGroupID(group1),
)
require.NoError(t, err)
require.Len(t, al, 2)
assertEqualActions(t, action2, al[0])
assertEqualActions(t, action1, al[1])
}
func assertEqualActions(t *testing.T, expected, got *Action) {

View file

@ -806,9 +806,14 @@ func (s *sessionRpcServer) ListActions(ctx context.Context,
sessionID = id
})
var macID [4]byte
a.MacaroonIdentifier.WhenSome(func(id [4]byte) {
macID = id
})
resp[i] = &litrpc.Action{
SessionId: sessionID[:],
MacaroonIdentifier: a.MacaroonIdentifier[:],
MacaroonIdentifier: macID[:],
ActorName: a.ActorName,
FeatureName: a.FeatureName,
Trigger: a.Trigger,