diff --git a/app/src/types/generated/lit-sessions_pb.d.ts b/app/src/types/generated/lit-sessions_pb.d.ts index dab9793ea..4c7e6fd36 100644 --- a/app/src/types/generated/lit-sessions_pb.d.ts +++ b/app/src/types/generated/lit-sessions_pb.d.ts @@ -688,6 +688,7 @@ export interface SessionStateMap { STATE_IN_USE: 1; STATE_REVOKED: 2; STATE_EXPIRED: 3; + STATE_RESERVED: 4; } export const SessionState: SessionStateMap; diff --git a/app/src/types/generated/lit-sessions_pb.js b/app/src/types/generated/lit-sessions_pb.js index 677c9b161..a356c54fa 100644 --- a/app/src/types/generated/lit-sessions_pb.js +++ b/app/src/types/generated/lit-sessions_pb.js @@ -5276,7 +5276,8 @@ proto.litrpc.SessionState = { STATE_CREATED: 0, STATE_IN_USE: 1, STATE_REVOKED: 2, - STATE_EXPIRED: 3 + STATE_EXPIRED: 3, + STATE_RESERVED: 4 }; goog.object.extend(exports, proto.litrpc); diff --git a/litrpc/lit-autopilot.swagger.json b/litrpc/lit-autopilot.swagger.json index e1342634d..6e360281b 100644 --- a/litrpc/lit-autopilot.swagger.json +++ b/litrpc/lit-autopilot.swagger.json @@ -662,7 +662,8 @@ "STATE_CREATED", "STATE_IN_USE", "STATE_REVOKED", - "STATE_EXPIRED" + "STATE_EXPIRED", + "STATE_RESERVED" ], "default": "STATE_CREATED" }, diff --git a/litrpc/lit-sessions.pb.go b/litrpc/lit-sessions.pb.go index f77393560..61a1df186 100644 --- a/litrpc/lit-sessions.pb.go +++ b/litrpc/lit-sessions.pb.go @@ -81,10 +81,11 @@ func (SessionType) EnumDescriptor() ([]byte, []int) { type SessionState int32 const ( - SessionState_STATE_CREATED SessionState = 0 - SessionState_STATE_IN_USE SessionState = 1 - SessionState_STATE_REVOKED SessionState = 2 - SessionState_STATE_EXPIRED SessionState = 3 + SessionState_STATE_CREATED SessionState = 0 + SessionState_STATE_IN_USE SessionState = 1 + SessionState_STATE_REVOKED SessionState = 2 + SessionState_STATE_EXPIRED SessionState = 3 + SessionState_STATE_RESERVED SessionState = 4 ) // Enum value maps for SessionState. @@ -94,12 +95,14 @@ var ( 1: "STATE_IN_USE", 2: "STATE_REVOKED", 3: "STATE_EXPIRED", + 4: "STATE_RESERVED", } SessionState_value = map[string]int32{ - "STATE_CREATED": 0, - "STATE_IN_USE": 1, - "STATE_REVOKED": 2, - "STATE_EXPIRED": 3, + "STATE_CREATED": 0, + "STATE_IN_USE": 1, + "STATE_REVOKED": 2, + "STATE_EXPIRED": 3, + "STATE_RESERVED": 4, } ) @@ -1897,31 +1900,32 @@ var file_lit_sessions_proto_rawDesc = []byte{ 0x12, 0x12, 0x0a, 0x0e, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x55, 0x54, 0x4f, 0x50, 0x49, 0x4c, 0x4f, 0x54, 0x10, 0x04, 0x12, 0x19, 0x0a, 0x15, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x41, 0x43, 0x41, 0x52, 0x4f, 0x4f, 0x4e, 0x5f, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x10, 0x05, 0x2a, - 0x59, 0x0a, 0x0c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, + 0x6d, 0x0a, 0x0c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x49, 0x4e, 0x5f, 0x55, 0x53, 0x45, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x52, 0x45, 0x56, 0x4f, 0x4b, 0x45, 0x44, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x53, 0x54, 0x41, 0x54, 0x45, - 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x45, 0x44, 0x10, 0x03, 0x32, 0xe8, 0x01, 0x0a, 0x08, 0x53, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x43, 0x0a, 0x0a, 0x41, 0x64, 0x64, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x2e, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, - 0x64, 0x64, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1a, 0x2e, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x53, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x0c, - 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1b, 0x2e, 0x6c, - 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x6c, 0x69, 0x74, 0x72, - 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0d, 0x52, 0x65, 0x76, 0x6f, 0x6b, - 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x2e, 0x6c, 0x69, 0x74, 0x72, 0x70, - 0x63, 0x2e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, - 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x6c, 0x61, 0x62, - 0x73, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x2d, 0x74, 0x65, 0x72, 0x6d, - 0x69, 0x6e, 0x61, 0x6c, 0x2f, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x45, 0x44, 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x54, + 0x41, 0x54, 0x45, 0x5f, 0x52, 0x45, 0x53, 0x45, 0x52, 0x56, 0x45, 0x44, 0x10, 0x04, 0x32, 0xe8, + 0x01, 0x0a, 0x08, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x43, 0x0a, 0x0a, 0x41, + 0x64, 0x64, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x2e, 0x6c, 0x69, 0x74, 0x72, + 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, + 0x64, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x49, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x1b, 0x2e, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, + 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, + 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x0d, 0x52, + 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x2e, 0x6c, + 0x69, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x6c, 0x69, 0x74, + 0x72, 0x70, 0x63, 0x2e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, + 0x67, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x6e, 0x69, 0x6e, 0x67, 0x2d, + 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x2f, 0x6c, 0x69, 0x74, 0x72, 0x70, 0x63, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/litrpc/lit-sessions.proto b/litrpc/lit-sessions.proto index a32499f25..1c38808e6 100644 --- a/litrpc/lit-sessions.proto +++ b/litrpc/lit-sessions.proto @@ -97,6 +97,7 @@ enum SessionState { STATE_IN_USE = 1; STATE_REVOKED = 2; STATE_EXPIRED = 3; + STATE_RESERVED = 4; } message AddSessionResponse { diff --git a/litrpc/lit-sessions.swagger.json b/litrpc/lit-sessions.swagger.json index 2300f69c0..4d0557d0e 100644 --- a/litrpc/lit-sessions.swagger.json +++ b/litrpc/lit-sessions.swagger.json @@ -521,7 +521,8 @@ "STATE_CREATED", "STATE_IN_USE", "STATE_REVOKED", - "STATE_EXPIRED" + "STATE_EXPIRED", + "STATE_RESERVED" ], "default": "STATE_CREATED" }, diff --git a/proto/lit-sessions.proto b/proto/lit-sessions.proto index 9068ce65f..fbd56f63b 100644 --- a/proto/lit-sessions.proto +++ b/proto/lit-sessions.proto @@ -97,6 +97,7 @@ enum SessionState { STATE_IN_USE = 1; STATE_REVOKED = 2; STATE_EXPIRED = 3; + STATE_RESERVED = 4; } message AddSessionResponse { diff --git a/session/interface.go b/session/interface.go index 6b27a1f38..204b3d169 100644 --- a/session/interface.go +++ b/session/interface.go @@ -26,11 +26,37 @@ const ( // State represents the state of a session. type State uint8 +/* + /---> StateExpired +StateCreated --- + \---> StateRevoked +*/ + const ( + // StateCreated is the state of a session once it has been fully + // committed to the Store and is ready to be used. This is the first + // state of a session. StateCreated State = 0 - StateInUse State = 1 + + // StateInUse is the state of a session that is currently being used. + // + // NOTE: this state is not currently used, but we keep it around for now + // since old sessions might still have this state persisted. + StateInUse State = 1 + + // StateRevoked is the state of a session that has been revoked before + // its expiry date. StateRevoked State = 2 + + // StateExpired is the state of a session that has passed its expiry + // date. StateExpired State = 3 + + // StateReserved is a temporary initial state of a session. On start-up, + // any sessions in this state should be cleaned up. + // + // NOTE: this isn't used yet. + StateReserved State = 4 ) // MacaroonRecipe defines the permissions and caveats that should be used @@ -195,5 +221,9 @@ type Store interface { CheckSessionGroupPredicate(groupID ID, fn func(s *Session) bool) (bool, error) + // DeleteReservedSessions deletes all sessions that are in the + // StateReserved state. + DeleteReservedSessions() error + IDToGroupIndex } diff --git a/session/kvdb_store.go b/session/kvdb_store.go index 2f5f252d5..68cf45460 100644 --- a/session/kvdb_store.go +++ b/session/kvdb_store.go @@ -206,10 +206,6 @@ func (db *BoltStore) NewSession(id ID, localPrivKey *btcec.PrivateKey, // // NOTE: this is part of the Store interface. func (db *BoltStore) CreateSession(session *Session) error { - var buf bytes.Buffer - if err := SerializeSession(&buf, session); err != nil { - return err - } sessionKey := getSessionKey(session) return db.Update(func(tx *bbolt.Tx) error { @@ -246,21 +242,7 @@ func (db *BoltStore) CreateSession(session *Session) error { } for _, id := range sessionIDs { - keyBytes, err := getKeyForID( - sessionBucket, id, - ) - if err != nil { - return err - } - - v := sessionBucket.Get(keyBytes) - if len(v) == 0 { - return ErrSessionNotFound - } - - sess, err := DeserializeSession( - bytes.NewReader(v), - ) + sess, err := getSessionByID(sessionBucket, id) if err != nil { return err } @@ -291,7 +273,7 @@ func (db *BoltStore) CreateSession(session *Session) error { return err } - return sessionBucket.Put(sessionKey, buf.Bytes()) + return putSession(sessionBucket, session) }) } @@ -325,12 +307,7 @@ func (db *BoltStore) UpdateSessionRemotePubKey(localPubKey, session.RemotePublicKey = remotePubKey - var buf bytes.Buffer - if err := SerializeSession(&buf, session); err != nil { - return err - } - - return sessionBucket.Put(key, buf.Bytes()) + return putSession(sessionBucket, session) }) } @@ -444,6 +421,99 @@ func (db *BoltStore) listSessions(filterFn func(s *Session) bool) ([]*Session, return sessions, nil } +// DeleteReservedSessions deletes all sessions that are in the StateReserved +// state. +// +// NOTE: this is part of the Store interface. +func (db *BoltStore) DeleteReservedSessions() error { + return db.Update(func(tx *bbolt.Tx) error { + sessionBucket, err := getBucket(tx, sessionBucketKey) + if err != nil { + return err + } + + return sessionBucket.ForEach(func(k, v []byte) error { + // We'll also get buckets here, skip those (identified + // by nil value). + if v == nil { + return nil + } + + session, err := DeserializeSession(bytes.NewReader(v)) + if err != nil { + return err + } + + if session.State != StateReserved { + return nil + } + + err = sessionBucket.Delete(k) + if err != nil { + return err + } + + idIndexBkt := sessionBucket.Bucket(idIndexKey) + if idIndexBkt == nil { + return ErrDBInitErr + } + + // Delete the entire session ID bucket. + err = idIndexBkt.DeleteBucket(session.ID[:]) + if err != nil { + return err + } + + groupIdIndexBkt := sessionBucket.Bucket(groupIDIndexKey) + if groupIdIndexBkt == nil { + return ErrDBInitErr + } + + groupBkt := groupIdIndexBkt.Bucket(session.GroupID[:]) + if groupBkt == nil { + return ErrDBInitErr + } + + sessionIDsBkt := groupBkt.Bucket(sessionIDKey) + if sessionIDsBkt == nil { + return ErrDBInitErr + } + + var ( + seqKey []byte + numSessions int + ) + err = sessionIDsBkt.ForEach(func(k, v []byte) error { + numSessions++ + + if !bytes.Equal(v, session.ID[:]) { + return nil + } + + seqKey = k + + return nil + }) + if err != nil { + return err + } + + if numSessions == 0 { + return fmt.Errorf("no sessions found for "+ + "group ID %x", session.GroupID) + } + + if numSessions == 1 { + // Delete the whole group bucket. + return groupBkt.DeleteBucket(sessionIDKey) + } + + // Else, delete just the session ID entry. + return sessionIDsBkt.Delete(seqKey) + }) + }) +} + // RevokeSession updates the state of the session with the given local // public key to be revoked. // @@ -469,12 +539,7 @@ func (db *BoltStore) RevokeSession(key *btcec.PublicKey) error { session.State = StateRevoked session.RevokedAt = db.clock.Now().UTC() - var buf bytes.Buffer - if err := SerializeSession(&buf, session); err != nil { - return err - } - - return sessionBucket.Put(key.SerializeCompressed(), buf.Bytes()) + return putSession(sessionBucket, session) }) } @@ -489,22 +554,9 @@ func (db *BoltStore) GetSessionByID(id ID) (*Session, error) { return err } - keyBytes, err := getKeyForID(sessionBucket, id) - if err != nil { - return err - } + session, err = getSessionByID(sessionBucket, id) - v := sessionBucket.Get(keyBytes) - if len(v) == 0 { - return ErrSessionNotFound - } - - session, err = DeserializeSession(bytes.NewReader(v)) - if err != nil { - return err - } - - return nil + return err }) if err != nil { return nil, err @@ -811,3 +863,26 @@ func addIDToGroupIDPair(sessionBkt *bbolt.Bucket, id, groupID ID) error { return sessionIDsBkt.Put(seqNoBytes[:], id[:]) } + +func getSessionByID(bucket *bbolt.Bucket, id ID) (*Session, error) { + keyBytes, err := getKeyForID(bucket, id) + if err != nil { + return nil, err + } + + v := bucket.Get(keyBytes) + if len(v) == 0 { + return nil, ErrSessionNotFound + } + + return DeserializeSession(bytes.NewReader(v)) +} + +func putSession(bucket *bbolt.Bucket, session *Session) error { + var buf bytes.Buffer + if err := SerializeSession(&buf, session); err != nil { + return err + } + + return bucket.Put(getSessionKey(session), buf.Bytes()) +} diff --git a/session/store_test.go b/session/store_test.go index e5530fdda..aa3cacf70 100644 --- a/session/store_test.go +++ b/session/store_test.go @@ -142,9 +142,58 @@ func TestBasicSessionStore(t *testing.T) { require.NoError(t, err) require.Empty(t, sessions) - sessions, err = db.ListSessionsByState(StateInUse) + sessions, err = db.ListSessionsByState(StateReserved) require.NoError(t, err) require.Empty(t, sessions) + + // Demonstrate deletion of a reserved session. + // + // Calling DeleteReservedSessions should have no effect yet since none + // of the sessions are reserved. + require.NoError(t, db.DeleteReservedSessions()) + + sessions, err = db.ListSessionsByState(StateReserved) + require.NoError(t, err) + require.Empty(t, sessions) + + // Add a session and put it in the StateReserved state. We'll also + // link it to session 1. + s5 := newSession( + t, db, clock, "session 5", withState(StateReserved), + withLinkedGroupID(&session1.GroupID), + ) + require.NoError(t, db.CreateSession(s5)) + + sessions, err = db.ListSessionsByState(StateReserved) + require.NoError(t, err) + require.Equal(t, 1, len(sessions)) + assertEqualSessions(t, s5, sessions[0]) + + // Show that the group ID/session ID index has also been populated with + // this session. + groupID, err := db.GetGroupID(s5.ID) + require.NoError(t, err) + require.Equal(t, s1.ID, groupID) + + sessIDs, err := db.GetSessionIDs(s5.GroupID) + require.NoError(t, err) + require.ElementsMatch(t, []ID{s5.ID, s1.ID}, sessIDs) + + // Now delete the reserved session and show that it is no longer in the + // database and no longer in the group ID/session ID index. + require.NoError(t, db.DeleteReservedSessions()) + + sessions, err = db.ListSessionsByState(StateReserved) + require.NoError(t, err) + require.Empty(t, sessions) + + _, err = db.GetGroupID(s5.ID) + require.ErrorContains(t, err, "no index entry") + + // Only session 1 should remain in this group. + sessIDs, err = db.GetSessionIDs(s5.GroupID) + require.NoError(t, err) + require.ElementsMatch(t, []ID{s1.ID}, sessIDs) } // TestLinkingSessions tests that session linking works as expected. @@ -359,6 +408,12 @@ func withType(t Type) testSessionModifier { } } +func withState(state State) testSessionModifier { + return func(s *Session) { + s.State = state + } +} + func newSession(t *testing.T, db Store, clock clock.Clock, label string, mods ...testSessionModifier) *Session { diff --git a/session_rpcserver.go b/session_rpcserver.go index e85d3578f..4e452068f 100644 --- a/session_rpcserver.go +++ b/session_rpcserver.go @@ -100,6 +100,12 @@ func newSessionRPCServer(cfg *sessionRpcServerConfig) (*sessionRpcServer, // start all the components necessary for the sessionRpcServer to start serving // requests. This includes resuming all non-revoked sessions. func (s *sessionRpcServer) start(ctx context.Context) error { + // Delete all sessions in the Reserved state. + err := s.cfg.db.DeleteReservedSessions() + if err != nil { + return fmt.Errorf("error deleting reserved sessions: %v", err) + } + // Start up all previously created sessions. sessions, err := s.cfg.db.ListSessionsByState( session.StateCreated, @@ -1518,6 +1524,9 @@ func marshalRPCMacaroonRecipe( // marshalRPCState converts a session state to its RPC counterpart. func marshalRPCState(state session.State) (litrpc.SessionState, error) { switch state { + case session.StateReserved: + return litrpc.SessionState_STATE_RESERVED, nil + case session.StateCreated: return litrpc.SessionState_STATE_CREATED, nil