diff --git a/firewall/request_logger.go b/firewall/request_logger.go index 8b038a6b6..b28b1092a 100644 --- a/firewall/request_logger.go +++ b/firewall/request_logger.go @@ -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{ diff --git a/firewalldb/actions.go b/firewalldb/actions.go index 88172e31b..1d0c8c36f 100644 --- a/firewalldb/actions.go +++ b/firewalldb/actions.go @@ -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. diff --git a/firewalldb/actions_kvdb.go b/firewalldb/actions_kvdb.go index 7a8ae3e15..b2dddd36c 100644 --- a/firewalldb/actions_kvdb.go +++ b/firewalldb/actions_kvdb.go @@ -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) diff --git a/firewalldb/actions_test.go b/firewalldb/actions_test.go index 0f93ed2fe..a355bede0 100644 --- a/firewalldb/actions_test.go +++ b/firewalldb/actions_test.go @@ -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) { diff --git a/session_rpcserver.go b/session_rpcserver.go index 9baf4aa5c..cb57b847b 100644 --- a/session_rpcserver.go +++ b/session_rpcserver.go @@ -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,