Repository: TBD54566975/ssi-service
Pull request #289 (2023-02-14 20:35:34)

Status List Credentials independent indexes
# Overview
This change creates a new index and random indexlist for EACH <schema,issuer,statuspurpose>  tripple

# Description
The current design WAS generate status lists for which there are unused bits. For example:

Create revocable credential#1 for issuerA with schemaA. This creates a statusListAB. Assume the index of this credential in the status list 1234

Create revocable credential for issuerB with schemaA. This creates a statusListBB. Assume the index of this credential is 5678.

Now, note that index 1234 will never be assigned in statusListBB.

Similarly, index 5678 will never be assigned in statusListAB.

NOW both
statusListAB and statusListAA are independent and can have index 1234



codecov-commenter: # [Codecov](https://codecov.io/gh/TBD54566975/ssi-service/pull/289?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975) Report
> Merging [#289](https://codecov.io/gh/TBD54566975/ssi-service/pull/289?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975) (a9801ae) into [main](https://codecov.io/gh/TBD54566975/ssi-service/commit/8ba822ba50aac33e324f63b334a74b597a438ce9?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975) (8ba822b) will **decrease** coverage by `0.10%`.
> The diff coverage is `n/a`.

```diff
@@            Coverage Diff             @@
##             main     #289      +/-   ##
==========================================
- Coverage   24.66%   24.57%   -0.10%     
==========================================
  Files          31       32       +1     
  Lines        3170     3182      +12     
==========================================
  Hits          782      782              
- Misses       2269     2281      +12     
  Partials      119      119              
```


| [Impacted Files](https://codecov.io/gh/TBD54566975/ssi-service/pull/289?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975) | Coverage Δ | |
|---|---|---|
| [pkg/testutil/setup.go](https://codecov.io/gh/TBD54566975/ssi-service/pull/289?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975#diff-cGtnL3Rlc3R1dGlsL3NldHVwLmdv) | `0.00% <0.00%> (ø)` | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975)

nitro-neal: Added new unit test and other small changes

Patch:
diff --git a/pkg/service/credential/model.go b/pkg/service/credential/model.go
index 6974701..b18b5a6 100644
--- a/pkg/service/credential/model.go
+++ b/pkg/service/credential/model.go
@@ -90,3 +90,7 @@ func (csr CreateCredentialRequest) isStatusValid() bool {
 	}
 	return true
 }
+
+func (csr CreateCredentialRequest) hasStatus() bool {
+	return csr.Suspendable || csr.Revocable
+}
diff --git a/pkg/service/credential/service.go b/pkg/service/credential/service.go
index 9cacfb8..78319a0 100644
--- a/pkg/service/credential/service.go
+++ b/pkg/service/credential/service.go
@@ -6,11 +6,9 @@ import (
 	"strconv"
 	"time"
 
-	statussdk "github.com/TBD54566975/ssi-sdk/credential/status"
-	"github.com/google/uuid"
-
 	"github.com/TBD54566975/ssi-sdk/credential"
 	schemalib "github.com/TBD54566975/ssi-sdk/credential/schema"
+	statussdk "github.com/TBD54566975/ssi-sdk/credential/status"
 	didsdk "github.com/TBD54566975/ssi-sdk/did"
 	sdkutil "github.com/TBD54566975/ssi-sdk/util"
 	"github.com/pkg/errors"
@@ -90,9 +88,44 @@ func NewCredentialService(config config.CredentialServiceConfig, s storage.Servi
 }
 
 func (s Service) CreateCredential(ctx context.Context, request CreateCredentialRequest) (*CreateCredentialResponse, error) {
-	returnFunc := s.createCredentialFunc(request)
+	watchKeys := make([]storage.WatchKey, 0)
+	var statusListCredential *StoredCredential
+
+	var slcMetadata StatusListCredentialMetadata
+
+	if request.hasStatus() && request.isStatusValid() {
+
+		var statusListUUID string
+		var err error
+
+		statusPurpose := statussdk.StatusRevocation
+
+		if request.Suspendable {
+			statusPurpose = statussdk.StatusSuspension
+		}
+
+		statusListUUID, statusListCredential, err = s.storage.GetStatusListCredentialKeyData(ctx, request.Issuer, request.JSONSchema, statusPurpose)
+		if err != nil {
+			return nil, errors.Wrap(err, "getting status list watch key uuid data")
+		}
+
+		if statusListUUID == "" {
+			return nil, errors.New("generating uuid")
+		}
+
+		statusListCredentialWatchKey := s.storage.GetStatusListCredentialWatchKey(request.Issuer, request.JSONSchema, string(statusPurpose))
+		statusListCredentialIndexPoolWatchKey := s.storage.GetStatusListIndexPoolWatchKey(request.Issuer, request.JSONSchema, string(statusPurpose))
+		statusListCredentialCurrentIndexWatchKey := s.storage.GetStatusListCurrentIndexWatchKey(request.Issuer, request.JSONSchema, string(statusPurpose))
+
+		watchKeys = append(watchKeys, statusListCredentialWatchKey)
+		watchKeys = append(watchKeys, statusListCredentialIndexPoolWatchKey)
+		watchKeys = append(watchKeys, statusListCredentialCurrentIndexWatchKey)
+
+		slcMetadata = StatusListCredentialMetadata{statusListCredential: statusListCredential, statusListCredentialWatchKey: statusListCredentialWatchKey, statusListIndexPoolWatchKey: statusListCredentialIndexPoolWatchKey, statusListCurrentIndexWatchKey: statusListCredentialCurrentIndexWatchKey, uuid: statusListUUID}
+	}
+
+	returnFunc := s.createCredentialFunc(request, slcMetadata)
 
-	watchKeys := []storage.WatchKey{s.storage.GetIncrementStatusListIndexWriteContextWatchKey(), s.storage.GetStatusListIndexesWriteKey()}
 	returnValue, err := s.storage.db.Execute(ctx, returnFunc, watchKeys)
 
 	if err != nil {
@@ -107,13 +140,13 @@ func (s Service) CreateCredential(ctx context.Context, request CreateCredentialR
 	return credResponse, nil
 }
 
-func (s Service) createCredentialFunc(request CreateCredentialRequest) storage.BusinessLogicFunc {
+func (s Service) createCredentialFunc(request CreateCredentialRequest, slcMetadata StatusListCredentialMetadata) storage.BusinessLogicFunc {
 	return func(ctx context.Context, tx storage.Tx) (any, error) {
-		return s.createCredentialBusinessLogic(ctx, request, tx)
+		return s.createCredentialBusinessLogic(ctx, request, tx, slcMetadata)
 	}
 }
 
-func (s Service) createCredentialBusinessLogic(ctx context.Context, request CreateCredentialRequest, tx storage.Tx) (*CreateCredentialResponse, error) {
+func (s Service) createCredentialBusinessLogic(ctx context.Context, request CreateCredentialRequest, tx storage.Tx, slcMetadata StatusListCredentialMetadata) (*CreateCredentialResponse, error) {
 	logrus.Debugf("creating credential: %+v", request)
 
 	if !request.isStatusValid() {
@@ -177,7 +210,8 @@ func (s Service) createCredentialBusinessLogic(ctx context.Context, request Crea
 		return nil, util.LoggingErrorMsg(err, errMsg)
 	}
 
-	if request.Revocable || request.Suspendable {
+	if request.hasStatus() {
+
 		credID := builder.ID
 		issuerID := request.Issuer
 		schemaID := request.JSONSchema
@@ -188,26 +222,38 @@ func (s Service) createCredentialBusinessLogic(ctx context.Context, request Crea
 			statusPurpose = statussdk.StatusSuspension
 		}
 
-		statusListCredential, err := getOrCreateStatusListCredential(ctx, s, statusPurpose, issuerID, schemaID, tx)
-		if err != nil {
-			return nil, util.LoggingErrorMsgf(err, "problem with getting status list credential")
-		}
+		var slCredential *credential.VerifiableCredential
+		var statusListCredentialID string
+		var randomIndex int
+		var err error
 
-		statusListIndex, err := s.storage.GetNextStatusListRandomIndex(ctx)
-		if err != nil {
-			return nil, util.LoggingErrorMsg(err, "problem with getting status list index")
-		}
+		if slcMetadata.statusListCredential == nil {
+			// creates status list credential with random index
+			randomIndex, slCredential, err = createStatusListCredential(ctx, tx, s, statusPurpose, issuerID, schemaID, slcMetadata)
+			if err != nil {
+				return nil, util.LoggingErrorMsgf(err, "problem with getting status list credential")
+			}
+
+			statusListCredentialID = slCredential.ID
+		} else {
+			randomIndex, err = s.storage.GetNextStatusListRandomIndex(ctx, slcMetadata)
+			if err != nil {
+				return nil, util.LoggingErrorMsg(err, "problem with getting status list index")
+			}
 
-		if err := s.storage.IncrementStatusListIndexTx(ctx, tx); err != nil {
-			return nil, errors.Wrap(err, "incrementing status list index")
+			statusListCredentialID = slcMetadata.statusListCredential.Credential.ID
+
+			if err := s.storage.IncrementStatusListIndexTx(ctx, tx, slcMetadata); err != nil {
+				return nil, errors.Wrap(err, "incrementing status list index")
+			}
 		}
 
 		status := statussdk.StatusList2021Entry{
 			ID:                   fmt.Sprintf(`%s/v1/credentials/%s/status`, s.config.ServiceEndpoint, credID),
 			Type:                 statussdk.StatusList2021EntryType,
 			StatusPurpose:        statusPurpose,
-			StatusListIndex:      strconv.Itoa(statusListIndex),
-			StatusListCredential: statusListCredential.ID,
+			StatusListIndex:      strconv.Itoa(randomIndex),
+			StatusListCredential: statusListCredentialID,
 		}
 
 		if err := builder.SetCredentialStatus(status); err != nil {
@@ -245,7 +291,7 @@ func (s Service) createCredentialBusinessLogic(ctx context.Context, request Crea
 		Container: container,
 	}
 
-	if err = s.storage.StoreCredential(ctx, credentialStorageRequest); err != nil {
+	if err = s.storage.StoreCredentialTx(ctx, tx, credentialStorageRequest); err != nil {
 		return nil, util.LoggingErrorMsg(err, "failed to save vc")
 	}
 
@@ -253,61 +299,43 @@ func (s Service) createCredentialBusinessLogic(ctx context.Context, request Crea
 	return &response, nil
 }
 
-func getOrCreateStatusListCredential(ctx context.Context, s Service, statusPurpose statussdk.StatusPurpose, issuerID string, schemaID string, tx storage.Tx) (*credential.VerifiableCredential, error) {
-	storedStatusListCreds, err := s.storage.GetStatusListCredentialsByIssuerAndSchema(ctx, statusPurpose, issuerID, schemaID)
-	if err != nil {
-		return nil, util.LoggingNewErrorf("getting status list credential for issuer: %s schema: %s", issuerID, schemaID)
-	}
+func createStatusListCredential(ctx context.Context, tx storage.Tx, s Service, statusPurpose statussdk.StatusPurpose, issuerID string, schemaID string, slcMetadata StatusListCredentialMetadata) (int, *credential.VerifiableCredential, error) {
+	statusListID := fmt.Sprintf("%s/v1/credentials/status/%s", s.config.ServiceEndpoint, slcMetadata.uuid)
 
-	// This should never happen, there should always be only 1 status list credential per <issuer,schema> pair
-	if len(storedStatusListCreds) > 1 {
-		return nil, util.LoggingNewErrorf("only one status list credential per <issuer,schema> pair allowed. issuer: %s schema: %s", issuerID, schemaID)
+	generatedStatusListCredential, err := statussdk.GenerateStatusList2021Credential(statusListID, issuerID, statusPurpose, []credential.VerifiableCredential{})
+	if err != nil {
+		return -1, nil, util.LoggingErrorMsg(err, "could not generate status list")
 	}
 
-	var statusListCredential *credential.VerifiableCredential
-
-	// First time that this <issuer,schema> pair has a revocation or suspension credential issued
-	if len(storedStatusListCreds) == 0 {
-		statusListUUID := uuid.New().String()
-		statusListID := fmt.Sprintf("%s/v1/credentials/status/%s", s.config.ServiceEndpoint, statusListUUID)
-		generatedStatusListCredential, err := statussdk.GenerateStatusList2021Credential(statusListID, issuerID, statusPurpose, []credential.VerifiableCredential{})
-		if err != nil {
-			return nil, util.LoggingErrorMsg(err, "could not generate status list")
-		}
-
-		statusListCredential = generatedStatusListCredential
-
-		if schemaID != "" {
-			credSchema := credential.CredentialSchema{
-				ID:   schemaID,
-				Type: SchemaLDType,
-			}
-			statusListCredential.CredentialSchema = &credSchema
+	if schemaID != "" {
+		credSchema := credential.CredentialSchema{
+			ID:   schemaID,
+			Type: SchemaLDType,
 		}
+		generatedStatusListCredential.CredentialSchema = &credSchema
+	}
 
-		statusListCredJWT, err := s.signCredentialJWT(ctx, issuerID, *statusListCredential)
-		if err != nil {
-			return nil, util.LoggingErrorMsg(err, "could not sign status list credential")
-		}
+	statusListCredJWT, err := s.signCredentialJWT(ctx, issuerID, *generatedStatusListCredential)
+	if err != nil {
+		return -1, nil, util.LoggingErrorMsg(err, "could not sign status list credential")
+	}
 
-		statusListContainer := credint.Container{
-			ID:            statusListCredential.ID,
-			Credential:    statusListCredential,
-			CredentialJWT: statusListCredJWT,
-		}
+	statusListContainer := credint.Container{
+		ID:            generatedStatusListCredential.ID,
+		Credential:    generatedStatusListCredential,
+		CredentialJWT: statusListCredJWT,
+	}
 
-		statusListStorageRequest := StoreCredentialRequest{
-			Container: statusListContainer,
-		}
+	statusListStorageRequest := StoreCredentialRequest{
+		Container: statusListContainer,
+	}
 
-		if err := s.storage.StoreStatusListCredentialTx(ctx, tx, statusListStorageRequest, statusPurpose); err != nil {
-			return nil, errors.Wrap(err, "storing status list credential")
-		}
-	} else {
-		statusListCredential = storedStatusListCreds[0].Credential
+	randomIndex, err := s.storage.CreateStatusListCredentialTx(ctx, tx, statusListStorageRequest, slcMetadata)
+	if err != nil {
+		return -1, nil, errors.Wrap(err, "creating status list credential")
 	}
 
-	return statusListCredential, nil
+	return randomIndex, generatedStatusListCredential, nil
 }
 
 // signCredentialJWT signs a credential and returns it as a vc-jwt
@@ -484,8 +512,7 @@ func (s Service) GetCredentialStatus(ctx context.Context, request GetCredentialS
 func (s Service) GetCredentialStatusList(ctx context.Context, request GetCredentialStatusListRequest) (*GetCredentialStatusListResponse, error) {
 	logrus.Debugf("getting credential status list: %s", request.ID)
 
-	credStatusListID := fmt.Sprintf(`%s/v1/credentials/status/%s`, s.config.ServiceEndpoint, request.ID)
-	gotCred, err := s.storage.GetStatusListCredential(ctx, credStatusListID)
+	gotCred, err := s.storage.GetStatusListCredential(ctx, request.ID)
 	if err != nil {
 		return nil, util.LoggingErrorMsgf(err, "could not get credential: %s", request.ID)
 	}
@@ -503,9 +530,32 @@ func (s Service) GetCredentialStatusList(ctx context.Context, request GetCredent
 }
 
 func (s Service) UpdateCredentialStatus(ctx context.Context, request UpdateCredentialStatusRequest) (*UpdateCredentialStatusResponse, error) {
-	returnFunc := s.updateCredentialStatusFunc(request)
+	gotCred, err := s.storage.GetCredential(ctx, request.ID)
+	if err != nil {
+		return nil, util.LoggingErrorMsgf(err, "could not get credential: %s", request.ID)
+	}
+
+	statusPurpose := gotCred.Credential.CredentialStatus.(map[string]any)["statusPurpose"].(string)
+	if len(statusPurpose) == 0 {
+		return nil, util.LoggingNewErrorf("status purpose could not be derived from credential status")
+	}
+
+	_, statusListCredential, err := s.storage.GetStatusListCredentialKeyData(ctx, gotCred.Issuer, gotCred.Schema, statussdk.StatusPurpose(statusPurpose))
+	if err != nil {
+		return nil, errors.Wrap(err, "getting status list watch key uuid data")
+	}
+
+	if statusListCredential == nil {
+		return nil, errors.Wrap(err, "status list credential should exist in order to update")
+	}
+
+	statusListCredentialWatchKey := s.storage.GetStatusListCredentialWatchKey(gotCred.Issuer, gotCred.Schema, statusPurpose)
+
+	slcMetadata := StatusListCredentialMetadata{statusListCredentialWatchKey: statusListCredentialWatchKey}
+
+	watchKeys := []storage.WatchKey{statusListCredentialWatchKey}
+	returnFunc := s.updateCredentialStatusFunc(request, slcMetadata)
 
-	watchKeys := []storage.WatchKey{s.storage.GetIncrementStatusListIndexWriteContextWatchKey(), s.storage.GetStatusListIndexesWriteKey()}
 	returnValue, err := s.storage.db.Execute(ctx, returnFunc, watchKeys)
 	if err != nil {
 		return nil, errors.Wrap(err, "execute")
@@ -519,13 +569,13 @@ func (s Service) UpdateCredentialStatus(ctx context.Context, request UpdateCrede
 	return credResponse, nil
 }
 
-func (s Service) updateCredentialStatusFunc(request UpdateCredentialStatusRequest) storage.BusinessLogicFunc {
+func (s Service) updateCredentialStatusFunc(request UpdateCredentialStatusRequest, slcMetadata StatusListCredentialMetadata) storage.BusinessLogicFunc {
 	return func(ctx context.Context, tx storage.Tx) (any, error) {
-		return s.updateCredentialStatusBusinessLogic(ctx, request, tx)
+		return s.updateCredentialStatusBusinessLogic(ctx, tx, request, slcMetadata)
 	}
 }
 
-func (s Service) updateCredentialStatusBusinessLogic(ctx context.Context, request UpdateCredentialStatusRequest, tx storage.Tx) (*UpdateCredentialStatusResponse, error) {
+func (s Service) updateCredentialStatusBusinessLogic(ctx context.Context, tx storage.Tx, request UpdateCredentialStatusRequest, slcMetadata StatusListCredentialMetadata) (*UpdateCredentialStatusResponse, error) {
 	logrus.Debugf("updating credential status: %s to Revoked: %v", request.ID, request.Revoked)
 
 	if request.Suspended && request.Revoked {
@@ -542,11 +592,12 @@ func (s Service) updateCredentialStatusBusinessLogic(ctx context.Context, reques
 
 	// if the request is the same as what the current credential is there is no action
 	if gotCred.Revoked == request.Revoked && gotCred.Suspended == request.Suspended {
+		logrus.Warn("request and credential have same status, no action is needed")
 		response := UpdateCredentialStatusResponse{Revoked: gotCred.Revoked, Suspended: gotCred.Suspended}
 		return &response, nil
 	}
 
-	container, err := updateCredentialStatus(ctx, s, gotCred, request, tx)
+	container, err := updateCredentialStatus(ctx, tx, s, gotCred, request, slcMetadata)
 	if err != nil {
 		return nil, util.LoggingErrorMsg(err, "updating credential")
 	}
@@ -555,7 +606,7 @@ func (s Service) updateCredentialStatusBusinessLogic(ctx context.Context, reques
 	return &response, nil
 }
 
-func updateCredentialStatus(ctx context.Context, s Service, gotCred *StoredCredential, request UpdateCredentialStatusRequest, tx storage.Tx) (*credint.Container, error) {
+func updateCredentialStatus(ctx context.Context, tx storage.Tx, s Service, gotCred *StoredCredential, request UpdateCredentialStatusRequest, slcMetadata StatusListCredentialMetadata) (*credint.Container, error) {
 	// store the credential with updated status
 	container := credint.Container{
 		ID:            gotCred.ID,
@@ -569,7 +620,7 @@ func updateCredentialStatus(ctx context.Context, s Service, gotCred *StoredCrede
 		Container: container,
 	}
 
-	if err := s.storage.StoreCredential(ctx, storageRequest); err != nil {
+	if err := s.storage.StoreCredentialTx(ctx, tx, storageRequest); err != nil {
 		return nil, util.LoggingErrorMsg(err, "could not store credential")
 	}
 
@@ -586,6 +637,11 @@ func updateCredentialStatus(ctx context.Context, s Service, gotCred *StoredCrede
 
 	var revokedOrSuspendedStatusCreds []credential.VerifiableCredential
 	for _, cred := range creds {
+		// we add the current cred to the creds list based on request, not on what could be in stale database that the tx has not updated yet
+		if cred.Credential.ID == gotCred.Credential.ID {
+			continue
+		}
+
 		if request.Revoked && cred.Credential.CredentialStatus != nil && cred.Revoked {
 			revokedOrSuspendedStatusCreds = append(revokedOrSuspendedStatusCreds, *cred.Credential)
 		} else if request.Suspended && cred.Credential.CredentialStatus != nil && cred.Suspended {
@@ -593,6 +649,11 @@ func updateCredentialStatus(ctx context.Context, s Service, gotCred *StoredCrede
 		}
 	}
 
+	// add current one since it has not been saved yet and wont be available in the creds array
+	if request.Revoked == true || request.Suspended == true {
+		revokedOrSuspendedStatusCreds = append(revokedOrSuspendedStatusCreds, *gotCred.Credential)
+	}
+
 	statusPurpose := statussdk.StatusRevocation
 
 	if request.Suspended {
@@ -622,7 +683,7 @@ func updateCredentialStatus(ctx context.Context, s Service, gotCred *StoredCrede
 		Container: statusListContainer,
 	}
 
-	if err = s.storage.StoreStatusListCredentialTx(ctx, tx, storageRequest, statusPurpose); err != nil {
+	if err = s.storage.StoreStatusListCredentialTx(ctx, tx, storageRequest, slcMetadata); err != nil {
 		return nil, util.LoggingErrorMsg(err, "could not store credential status list")
 	}
 
diff --git a/pkg/service/credential/storage.go b/pkg/service/credential/storage.go
index fd8f28b..030072f 100644
--- a/pkg/service/credential/storage.go
+++ b/pkg/service/credential/storage.go
@@ -4,12 +4,14 @@ import (
 	"context"
 	"fmt"
 	"math/rand"
+	"regexp"
 	"strings"
 
 	"github.com/TBD54566975/ssi-sdk/credential"
 	"github.com/TBD54566975/ssi-sdk/credential/signing"
 	statussdk "github.com/TBD54566975/ssi-sdk/credential/status"
 	"github.com/goccy/go-json"
+	"github.com/google/uuid"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 	credint "github.com/tbd54566975/ssi-service/internal/credential"
@@ -46,6 +48,14 @@ type WriteContext struct {
 	value     []byte
 }
 
+type StatusListCredentialMetadata struct {
+	statusListCredential           *StoredCredential
+	statusListCredentialWatchKey   storage.WatchKey
+	statusListIndexPoolWatchKey    storage.WatchKey
+	statusListCurrentIndexWatchKey storage.WatchKey
+	uuid                           string
+}
+
 func (sc StoredCredential) IsValid() bool {
 	return sc.ID != "" && (sc.HasDataIntegrityCredential() || sc.HasJWTCredential())
 }
@@ -59,12 +69,10 @@ func (sc StoredCredential) HasJWTCredential() bool {
 }
 
 const (
-	credentialNamespace           = "credential"
-	statusListCredentialNamespace = "status-list-credential"
-	statusListIndexNamespace      = "status-list-index"
-
-	statusListIndexesKey = "status-list-indexes"
-	currentListIndexKey  = "current-list-index"
+	credentialNamespace                    = "credential"
+	statusListCredentialNamespace          = "status-list-credential"
+	statusListCredentialIndexPoolNamespace = "status-list-index-pool"
+	statusListCredentialCurrentIndex       = "status-list-current-index"
 
 	// A a minimum revocation bitString length of 131,072, or 16KB uncompressed
 	bitStringLength = 8 * 1024 * 16
@@ -85,49 +93,12 @@ func NewCredentialStorage(db storage.ServiceStorage) (*Storage, error) {
 		return nil, util.LoggingNewError("bolt db reference is nil")
 	}
 
-	listIndexKeyExists, err := db.Exists(context.Background(), statusListIndexNamespace, currentListIndexKey)
-	if err != nil {
-		return nil, util.LoggingErrorMsg(err, "exists for currentListIndexKey")
-	}
-
-	statusListIndexesKeyExists, err := db.Exists(context.Background(), statusListIndexNamespace, statusListIndexesKey)
-	if err != nil {
-		return nil, util.LoggingErrorMsg(err, "exists for statusListIndexesKey")
-	}
-
-	if listIndexKeyExists != statusListIndexesKeyExists {
-		return nil, util.LoggingNewError("list index and status list index not in the same state")
-	}
-
-	indexKeysDontExist := !listIndexKeyExists && !statusListIndexesKeyExists
-
-	if indexKeysDontExist {
-		randUniqueList := randomUniqueNum(bitStringLength)
-		uniqueNumBytes, err := json.Marshal(randUniqueList)
-		if err != nil {
-			return nil, util.LoggingErrorMsg(err, "could not marshal random unique numbers")
-		}
-
-		if err := db.Write(context.Background(), statusListIndexNamespace, statusListIndexesKey, uniqueNumBytes); err != nil {
-			return nil, util.LoggingErrorMsg(err, "problem writing status list indexes to db")
-		}
-
-		statusListIndexBytes, err := json.Marshal(StatusListIndex{Index: 0})
-		if err != nil {
-			return nil, util.LoggingErrorMsg(err, "could not marshal status list index bytes")
-		}
-
-		if err := db.Write(context.Background(), statusListIndexNamespace, currentListIndexKey, statusListIndexBytes); err != nil {
-			return nil, util.LoggingErrorMsg(err, "problem writing current list index to db")
-		}
-	}
-
 	return &Storage{db: db}, nil
 }
 
-func (cs *Storage) GetNextStatusListRandomIndex(ctx context.Context) (int, error) {
+func (cs *Storage) GetNextStatusListRandomIndex(ctx context.Context, slcMetadata StatusListCredentialMetadata) (int, error) {
 
-	gotUniqueNumBytes, err := cs.db.Read(ctx, statusListIndexNamespace, statusListIndexesKey)
+	gotUniqueNumBytes, err := cs.db.Read(ctx, slcMetadata.statusListIndexPoolWatchKey.Namespace, slcMetadata.statusListIndexPoolWatchKey.Key)
 	if err != nil {
 		return -1, util.LoggingErrorMsgf(err, "reading status list")
 	}
@@ -141,7 +112,7 @@ func (cs *Storage) GetNextStatusListRandomIndex(ctx context.Context) (int, error
 		return -1, util.LoggingErrorMsgf(err, "unmarshalling unique numbers")
 	}
 
-	gotCurrentListIndexBytes, err := cs.db.Read(ctx, statusListIndexNamespace, currentListIndexKey)
+	gotCurrentListIndexBytes, err := cs.db.Read(ctx, slcMetadata.statusListCurrentIndexWatchKey.Namespace, slcMetadata.statusListCurrentIndexWatchKey.Key)
 	if err != nil {
 		return -1, util.LoggingErrorMsgf(err, "could not get list index")
 	}
@@ -168,66 +139,70 @@ func (cs *Storage) WriteMany(ctx context.Context, writeContexts []WriteContext)
 	return cs.db.WriteMany(ctx, namespaces, keys, values)
 }
 
-func (cs *Storage) IncrementStatusListIndexTx(ctx context.Context, tx storage.Tx) error {
-	wc, err := cs.GetIncrementStatusListIndexWriteContext(ctx)
+func (cs *Storage) IncrementStatusListIndexTx(ctx context.Context, tx storage.Tx, slcMetadata StatusListCredentialMetadata) error {
+	gotCurrentListIndexBytes, err := cs.db.Read(ctx, slcMetadata.statusListCurrentIndexWatchKey.Namespace, slcMetadata.statusListCurrentIndexWatchKey.Key)
 	if err != nil {
-		return util.LoggingErrorMsg(err, "problem getting increment status listIndex writeContext")
+		return util.LoggingErrorMsg(err, "could not get list index")
 	}
 
-	if err := tx.Write(ctx, wc.namespace, wc.key, wc.value); err != nil {
-		return util.LoggingErrorMsg(err, "problem writing current list index to db")
+	var statusListIndex StatusListIndex
+	if err = json.Unmarshal(gotCurrentListIndexBytes, &statusListIndex); err != nil {
+		return util.LoggingErrorMsg(err, "unmarshalling unique numbers")
 	}
 
-	return nil
-}
+	if statusListIndex.Index >= bitStringLength-1 {
+		return util.LoggingErrorMsg(err, "no more indexes available for status list index")
+	}
 
-func (cs *Storage) IncrementStatusListIndex(ctx context.Context) error {
-	wc, err := cs.GetIncrementStatusListIndexWriteContext(ctx)
+	statusListIndexBytes, err := json.Marshal(StatusListIndex{Index: statusListIndex.Index + 1})
 	if err != nil {
-		return util.LoggingErrorMsg(err, "problem getting increment status listIndex writeContext")
+		return util.LoggingErrorMsg(err, "could not marshal status list index bytes")
 	}
 
-	if err := cs.db.Write(ctx, wc.namespace, wc.key, wc.value); err != nil {
+	if err := tx.Write(ctx, slcMetadata.statusListCurrentIndexWatchKey.Namespace, slcMetadata.statusListCurrentIndexWatchKey.Key, statusListIndexBytes); err != nil {
 		return util.LoggingErrorMsg(err, "problem writing current list index to db")
 	}
 
 	return nil
 }
 
-func (cs *Storage) GetIncrementStatusListIndexWriteContext(ctx context.Context) (*WriteContext, error) {
-	gotCurrentListIndexBytes, err := cs.db.Read(ctx, statusListIndexNamespace, currentListIndexKey)
+func (cs *Storage) StoreCredentialTx(ctx context.Context, tx storage.Tx, request StoreCredentialRequest) error {
+	wc, err := cs.getStoreCredentialWriteContext(request, credentialNamespace)
 	if err != nil {
-		return nil, util.LoggingErrorMsg(err, "could not get list index")
+		return errors.Wrap(err, "building stored credential")
+
 	}
+	return tx.Write(ctx, wc.namespace, wc.key, wc.value)
+}
 
-	var statusListIndex StatusListIndex
-	if err = json.Unmarshal(gotCurrentListIndexBytes, &statusListIndex); err != nil {
-		return nil, util.LoggingErrorMsg(err, "unmarshalling unique numbers")
+// CreateStatusListCredentialTx creates a new status list credential with the provided metadata and stores it in the database as a transaction.
+// The function generates a unique random number and stores it along with the metadata in the database and then returns it
+func (cs *Storage) CreateStatusListCredentialTx(ctx context.Context, tx storage.Tx, request StoreCredentialRequest, slcMetadata StatusListCredentialMetadata) (int, error) {
+
+	randUniqueList := randomUniqueNum(bitStringLength)
+	uniqueNumBytes, err := json.Marshal(randUniqueList)
+	if err != nil {
+		return -1, util.LoggingErrorMsg(err, "could not marshal random unique numbers")
 	}
 
-	if statusListIndex.Index >= bitStringLength-1 {
-		return nil, util.LoggingNewError("no more indexes available for status list index")
+	if err := tx.Write(context.Background(), slcMetadata.statusListIndexPoolWatchKey.Namespace, slcMetadata.statusListIndexPoolWatchKey.Key, uniqueNumBytes); err != nil {
+		return -1, util.LoggingErrorMsg(err, "problem writing status list indexes to db")
 	}
 
-	statusListIndexBytes, err := json.Marshal(StatusListIndex{Index: statusListIndex.Index + 1})
+	// Set the index to 1 since this is a new statusListCredential
+	statusListIndexBytes, err := json.Marshal(StatusListIndex{Index: 1})
 	if err != nil {
-		return nil, util.LoggingErrorMsg(err, "could not marshal status list index bytes")
+		return -1, util.LoggingErrorMsg(err, "could not marshal status list index bytes")
 	}
 
-	wc := WriteContext{
-		namespace: statusListIndexNamespace,
-		key:       currentListIndexKey,
-		value:     statusListIndexBytes,
+	if err := tx.Write(context.Background(), slcMetadata.statusListCurrentIndexWatchKey.Namespace, slcMetadata.statusListCurrentIndexWatchKey.Key, statusListIndexBytes); err != nil {
+		return -1, util.LoggingErrorMsg(err, "problem writing current list index to db")
 	}
 
-	return &wc, nil
-}
-
-func (cs *Storage) StoreCredential(ctx context.Context, request StoreCredentialRequest) error {
-	return cs.storeCredential(ctx, request, credentialNamespace)
+	return randUniqueList[0], cs.StoreStatusListCredentialTx(ctx, tx, request, slcMetadata)
 }
 
-func (cs *Storage) StoreStatusListCredentialTx(ctx context.Context, tx storage.Tx, request StoreCredentialRequest, statusPurpose statussdk.StatusPurpose) error {
+func (cs *Storage) StoreStatusListCredentialTx(ctx context.Context, tx storage.Tx, request StoreCredentialRequest, slcMetadata StatusListCredentialMetadata) error {
 	if !request.IsValid() {
 		return util.LoggingNewError("store request request is not valid")
 	}
@@ -243,50 +218,40 @@ func (cs *Storage) StoreStatusListCredentialTx(ctx context.Context, tx storage.T
 		return util.LoggingErrorMsgf(err, "could not store request: %s", storedCredential.CredentialID)
 	}
 
-	schemaID := ""
-	if request.Credential.CredentialSchema != nil {
-		schemaID = request.Credential.CredentialSchema.ID
-	}
-
-	statusListCredentialKey := createStatusListCredentialPrefixKey(storedCredential.CredentialID, request.Credential.Issuer.(string), request.Credential.CredentialSubject.GetID(), schemaID, string(statusPurpose))
-	return tx.Write(ctx, statusListCredentialNamespace, statusListCredentialKey, storedCredBytes)
+	return tx.Write(ctx, slcMetadata.statusListCredentialWatchKey.Namespace, slcMetadata.statusListCredentialWatchKey.Key, storedCredBytes)
 }
 
 func (cs *Storage) GetStatusListCredential(ctx context.Context, id string) (*StoredCredential, error) {
-	prefixValues, err := cs.db.ReadPrefix(ctx, statusListCredentialNamespace, id)
+	keys, err := cs.db.ReadAllKeys(ctx, statusListCredentialNamespace)
 	if err != nil {
-		return nil, util.LoggingErrorMsgf(err, "could not get credential from storage: %s", id)
-	}
-	if len(prefixValues) > 1 {
-		return nil, util.LoggingNewErrorf("could not get credential from storage; multiple prefix values matched credential id: %s", id)
+		return nil, util.LoggingErrorMsgf(err, "could not read credential storage while searching for cred with id: %s", id)
 	}
 
-	// since we know the map now only has a single value, we break after the first element
-	var credBytes []byte
-	for _, v := range prefixValues {
-		credBytes = v
-		break
-	}
-	if len(credBytes) == 0 {
-		return nil, util.LoggingNewErrorf("could not get credential from storage %s with id: %s", credentialNotFoundErrMsg, id)
+	var storedCreds []StoredCredential
+	for _, key := range keys {
+		credBytes, err := cs.db.Read(ctx, statusListCredentialNamespace, key)
+		if err != nil {
+			logrus.WithError(err).Errorf("could not read credential with key: %s", key)
+			continue
+		}
+		var cred StoredCredential
+		if err = json.Unmarshal(credBytes, &cred); err != nil {
+			logrus.WithError(err).Errorf("unmarshalling credential with key: %s", key)
+		}
+		if ExtractID(cred.CredentialID) == id {
+			storedCreds = append(storedCreds, cred)
+		}
 	}
 
-	var stored StoredCredential
-	if err = json.Unmarshal(credBytes, &stored); err != nil {
-		return nil, util.LoggingErrorMsgf(err, "unmarshalling stored credential: %s", id)
+	if len(storedCreds) == 0 {
+		logrus.Warnf("no credentials able to be retrieved for id: %s", id)
 	}
-	return &stored, nil
-}
-
-func (cs *Storage) storeCredential(ctx context.Context, request StoreCredentialRequest, namespace string) error {
-
-	wc, err := cs.getStoreCredentialWriteContext(request, namespace)
-	if err != nil {
-		return errors.Wrap(err, "building stored credential")
 
+	if len(storedCreds) > 1 {
+		logrus.Warnf("there should only be status list credential per <issuer,schema,statuspurpose> tripple, bad state")
 	}
-	// TODO(gabe) conflict checking?
-	return cs.db.Write(ctx, wc.namespace, wc.key, wc.value)
+
+	return &storedCreds[0], nil
 }
 
 func (cs *Storage) GetStoreCredentialWriteContext(request StoreCredentialRequest) (*WriteContext, error) {
@@ -526,7 +491,7 @@ func (cs *Storage) GetCredentialsByIssuerAndSchema(ctx context.Context, issuer s
 	return cs.getCredentialsByIssuerAndSchema(ctx, issuer, schema, credentialNamespace)
 }
 
-func (cs *Storage) GetStatusListCredentialsByIssuerAndSchema(ctx context.Context, statusPurpose statussdk.StatusPurpose, issuer string, schema string) ([]StoredCredential, error) {
+func (cs *Storage) GetStatusListCredentialsByIssuerSchemaPurpose(ctx context.Context, issuer string, schema string, statusPurpose statussdk.StatusPurpose) ([]StoredCredential, error) {
 	keys, err := cs.db.ReadAllKeys(ctx, statusListCredentialNamespace)
 	if err != nil {
 		return nil, util.LoggingErrorMsgf(err, "could not read credential storage while searching for creds for issuer: %s", issuer)
@@ -541,7 +506,7 @@ func (cs *Storage) GetStatusListCredentialsByIssuerAndSchema(ctx context.Context
 	}
 
 	if len(issuerSchemaKeys) == 0 {
-		logrus.Warnf("no credentials found for issuer: %s and schema %s", util.SanitizeLog(issuer), util.SanitizeLog(schema))
+		logrus.Warnf("no status list credentials found for issuer: %s schema %s and status purpose %s", util.SanitizeLog(issuer), util.SanitizeLog(schema), util.SanitizeLog(string(statusPurpose)))
 		return nil, nil
 	}
 
@@ -646,12 +611,59 @@ func (cs *Storage) deleteCredential(ctx context.Context, id string, namespace st
 	return nil
 }
 
-func (cs *Storage) GetIncrementStatusListIndexWriteContextWatchKey() storage.WatchKey {
-	return storage.WatchKey{Namespace: statusListIndexNamespace, Key: currentListIndexKey}
+func (cs *Storage) GetStatusListCredentialWatchKey(issuer, schema, statusPurpose string) storage.WatchKey {
+	return storage.WatchKey{Namespace: statusListCredentialNamespace, Key: getStatusListKey(issuer, schema, statusPurpose)}
+}
+
+func (cs *Storage) GetStatusListIndexPoolWatchKey(issuer, schema, statusPurpose string) storage.WatchKey {
+	return storage.WatchKey{Namespace: statusListCredentialIndexPoolNamespace, Key: getStatusListKey(issuer, schema, statusPurpose)}
+}
+
+func (cs *Storage) GetStatusListCurrentIndexWatchKey(issuer, schema, statusPurpose string) storage.WatchKey {
+	return storage.WatchKey{Namespace: statusListCredentialCurrentIndex, Key: getStatusListKey(issuer, schema, statusPurpose)}
+}
+
+func (cs *Storage) GetStatusListCredentialKeyData(ctx context.Context, issuer string, schema string, statusPurpose statussdk.StatusPurpose) (string, *StoredCredential, error) {
+	storedStatusListCreds, err := cs.GetStatusListCredentialsByIssuerSchemaPurpose(ctx, issuer, schema, statusPurpose)
+	if err != nil {
+		return "", nil, util.LoggingNewErrorf("getting status list credential for issuer: %s schema: %s", issuer, schema)
+	}
+
+	// This should never happen, there should always be only 1 status list credential per <issuer,schema, statusPurpose> triplet
+	if len(storedStatusListCreds) > 1 {
+		return "", nil, util.LoggingNewErrorf("only one status list credential per <issuer,schema> pair allowed. issuer: %s schema: %s", issuer, schema)
+	}
+
+	// No Status List Credential Exists, create a new uuid
+	if storedStatusListCreds == nil || len(storedStatusListCreds) == 0 {
+		logrus.Warn("No Status List Credential Exists, create a new uuid")
+		return uuid.New().String(), nil, nil
+	}
+
+	statusListUUID := ExtractID(storedStatusListCreds[0].CredentialID)
+	if statusListUUID == "" {
+		return "", nil, util.LoggingNewError("could not extract UUID from existing Credential Status List")
+	}
+
+	return statusListUUID, &storedStatusListCreds[0], nil
+}
+
+// ExtractID is a function that takes a string input and returns a string that contains an ID extracted from the input string.
+// The ID is extracted by searching for a string that matches the regular expression, which matches a forward slash followed
+// by one or more characters that are either letters (upper or lower case), digits (0-9), or hyphens (-), at the end of the input string.
+// If a match is found, the function returns the matched string with the forward slash removed (i.e., everything after the forward slash).
+// If no match is found, an empty string is returned.
+func ExtractID(input string) string {
+	re := regexp.MustCompile(`\/[a-zA-Z0-9-]+$`)
+	match := re.FindString(input)
+	if len(match) > 0 {
+		return match[1:]
+	}
+	return ""
 }
 
-func (cs *Storage) GetStatusListIndexesWriteKey() storage.WatchKey {
-	return storage.WatchKey{Namespace: statusListIndexNamespace, Key: statusListIndexesKey}
+func getStatusListKey(issuer, schema, statusPurpose string) string {
+	return strings.Join([]string{"is:" + issuer, "sc:" + schema, "sp:" + statusPurpose}, "-")
 }
 
 // unique key for a credential
@@ -659,10 +671,6 @@ func createPrefixKey(id, issuer, subject, schema string) string {
 	return strings.Join([]string{id, "is:" + issuer, "su:" + subject, "sc:" + schema}, "-")
 }
 
-func createStatusListCredentialPrefixKey(id, issuer, subject, schema, statusPurpose string) string {
-	return strings.Join([]string{id, "is:" + issuer, "su:" + subject, "sc:" + schema, "sp:" + statusPurpose}, "-")
-}
-
 func randomUniqueNum(count int) []int {
 	randomNumbers := make([]int, 0, count)
 


Test patch:
diff --git a/pkg/server/router/credential_test.go b/pkg/server/router/credential_test.go
index a5b9488..765950e 100644
--- a/pkg/server/router/credential_test.go
+++ b/pkg/server/router/credential_test.go
@@ -432,7 +432,6 @@ func TestCredentialRouter(t *testing.T) {
 		credService, err := credential.NewCredentialService(serviceConfig, bolt, keyStoreService, didService.GetResolver(), schemaService)
 		assert.NoError(tt, err)
 		assert.NotEmpty(tt, credService)
-
 		// check type and status
 		assert.Equal(tt, framework.Credential, credService.Type())
 		assert.Equal(tt, framework.StatusReady, credService.Status().Status)
@@ -530,81 +529,158 @@ func TestCredentialRouter(t *testing.T) {
 		assert.NotEmpty(tt, encodedListAfterRevoke)
 
 		assert.NotEqualValues(tt, encodedListAfterRevoke, encodedList)
-	})
-
-	t.Run("Existing Status List Indexes Used After Restart", func(tt *testing.T) {
-		statusListIndexNamespace := "status-list-index"
 
-		statusListIndexesKey := "status-list-indexes"
-		currentListIndexKey := "current-list-index"
+	})
 
+	t.Run("Create Multiple Suspendable Credential Different Issuer Schema StatusPurpose Triples", func(tt *testing.T) {
 		bolt := setupTestDB(tt)
 		assert.NotNil(tt, bolt)
 
-		// Make sure there is nothing in DB before we create storage
-		value, err := bolt.Read(context.Background(), statusListIndexNamespace, currentListIndexKey)
+		serviceConfig := config.CredentialServiceConfig{BaseServiceConfig: &config.BaseServiceConfig{Name: "credential", ServiceEndpoint: "http://localhost:1234"}}
+		keyStoreService := testKeyStoreService(tt, bolt)
+		didService := testDIDService(tt, bolt, keyStoreService)
+		schemaService := testSchemaService(tt, bolt, keyStoreService, didService)
+		credService, err := credential.NewCredentialService(serviceConfig, bolt, keyStoreService, didService.GetResolver(), schemaService)
 		assert.NoError(tt, err)
-		assert.Empty(tt, value)
+		assert.NotEmpty(tt, credService)
+		// check type and status
+		assert.Equal(tt, framework.Credential, credService.Type())
+		assert.Equal(tt, framework.StatusReady, credService.Status().Status)
 
-		value, err = bolt.Read(context.Background(), statusListIndexNamespace, statusListIndexesKey)
+		// create a did
+		issuerDID, err := didService.CreateDIDByMethod(context.Background(), did.CreateDIDRequest{Method: didsdk.KeyMethod, KeyType: crypto.Ed25519})
 		assert.NoError(tt, err)
-		assert.Empty(tt, value)
+		assert.NotEmpty(tt, issuerDID)
 
-		exists, err := bolt.Exists(context.Background(), statusListIndexNamespace, currentListIndexKey)
+		// create a schema
+		emailSchema := map[string]any{
+			"type": "object",
+			"properties": map[string]any{
+				"email": map[string]any{
+					"type": "string",
+				},
+			},
+			"required":             []any{"email"},
+			"additionalProperties": false,
+		}
+
+		createdSchema, err := schemaService.CreateSchema(context.Background(), schema.CreateSchemaRequest{Author: "me", Name: "simple schema", Schema: emailSchema})
 		assert.NoError(tt, err)
-		assert.False(tt, exists)
+		assert.NotEmpty(tt, createdSchema)
+
+		subject := "did:test:345"
 
-		exists, err = bolt.Exists(context.Background(), statusListIndexNamespace, statusListIndexesKey)
+		createdCred, err := credService.CreateCredential(context.Background(), credential.CreateCredentialRequest{
+			Issuer:     issuerDID.DID.ID,
+			Subject:    subject,
+			JSONSchema: createdSchema.ID,
+			Data: map[string]any{
+				"email": "Satoshi@Nakamoto.btc",
+			},
+			Expiry:    time.Now().Add(24 * time.Hour).Format(time.RFC3339),
+			Revocable: true,
+		})
 		assert.NoError(tt, err)
-		assert.False(tt, exists)
+		assert.NotEmpty(tt, createdCred)
 
-		// Create the storage
-		credStorage, err := credential.NewCredentialStorage(bolt)
+		createdCredSuspendable, err := credService.CreateCredential(context.Background(), credential.CreateCredentialRequest{
+			Issuer:     issuerDID.DID.ID,
+			Subject:    subject,
+			JSONSchema: createdSchema.ID,
+			Data: map[string]any{
+				"email": "Satoshi@Nakamoto.btc",
+			},
+			Expiry:      time.Now().Add(24 * time.Hour).Format(time.RFC3339),
+			Suspendable: true,
+		})
 		assert.NoError(tt, err)
-		assert.NotEmpty(tt, credStorage)
+		assert.NotEmpty(tt, createdCredSuspendable)
+
+		revocationKey := strings.Join([]string{"is:" + issuerDID.DID.ID, "sc:" + createdSchema.ID, "sp:" + string(status.StatusRevocation)}, "-")
 
-		// Make sure that values are there after we create a new credential storage
-		exists, err = bolt.Exists(context.Background(), statusListIndexNamespace, currentListIndexKey)
+		slcExists, err := bolt.Exists(context.Background(), "status-list-credential", revocationKey)
 		assert.NoError(tt, err)
-		assert.True(tt, exists)
+		assert.True(tt, slcExists)
 
-		exists, err = bolt.Exists(context.Background(), statusListIndexNamespace, statusListIndexesKey)
+		indexPoolExists, err := bolt.Exists(context.Background(), "status-list-index-pool", revocationKey)
 		assert.NoError(tt, err)
-		assert.True(tt, exists)
+		assert.True(tt, indexPoolExists)
 
-		value, err = bolt.Read(context.Background(), statusListIndexNamespace, currentListIndexKey)
+		currentIndexExists, err := bolt.Exists(context.Background(), "status-list-current-index", revocationKey)
 		assert.NoError(tt, err)
-		assert.NotEmpty(tt, value)
+		assert.True(tt, currentIndexExists)
 
-		statusListIndexes, err := bolt.Read(context.Background(), statusListIndexNamespace, statusListIndexesKey)
+		suspensionKey := strings.Join([]string{"is:" + issuerDID.DID.ID, "sc:" + createdSchema.ID, "sp:" + string(status.StatusSuspension)}, "-")
+
+		slcExists, err = bolt.Exists(context.Background(), "status-list-credential", suspensionKey)
 		assert.NoError(tt, err)
-		assert.NotEmpty(tt, value)
+		assert.True(tt, slcExists)
 
-		// "Restart" the service
-		credStorage, err = credential.NewCredentialStorage(bolt)
+		indexPoolExists, err = bolt.Exists(context.Background(), "status-list-index-pool", suspensionKey)
 		assert.NoError(tt, err)
-		assert.NotEmpty(tt, credStorage)
+		assert.True(tt, indexPoolExists)
 
-		exists, err = bolt.Exists(context.Background(), statusListIndexNamespace, currentListIndexKey)
+		currentIndexExists, err = bolt.Exists(context.Background(), "status-list-current-index", suspensionKey)
 		assert.NoError(tt, err)
-		assert.True(tt, exists)
+		assert.True(tt, currentIndexExists)
+	})
+
+	t.Run("Create Suspendable Credential", func(tt *testing.T) {
+		issuer, schema, credService := createCredServicePrereqs(tt)
+		subject := "did:test:345"
 
-		exists, err = bolt.Exists(context.Background(), statusListIndexNamespace, statusListIndexesKey)
+		createdCred, err := credService.CreateCredential(context.Background(), credential.CreateCredentialRequest{
+			Issuer:     issuer,
+			Subject:    subject,
+			JSONSchema: schema,
+			Data: map[string]any{
+				"email": "Satoshi@Nakamoto.btc",
+			},
+			Expiry:      time.Now().Add(24 * time.Hour).Format(time.RFC3339),
+			Suspendable: true,
+		})
+
+		assert.NoError(tt, err)
+		assert.NotEmpty(tt, createdCred)
+		assert.NotEmpty(tt, createdCred.CredentialJWT)
+
+		statusBytes, err := json.Marshal(createdCred.Credential.CredentialStatus)
 		assert.NoError(tt, err)
-		assert.True(tt, exists)
 
-		value, err = bolt.Read(context.Background(), statusListIndexNamespace, currentListIndexKey)
+		var statusEntry status.StatusList2021Entry
+		err = json.Unmarshal(statusBytes, &statusEntry)
+		assert.NoError(tt, err)
+
+		assert.Contains(tt, statusEntry.ID, fmt.Sprintf("http://localhost:1234/v1/credentials/%s/status", createdCred.ID))
+		assert.Contains(tt, statusEntry.StatusListCredential, "http://localhost:1234/v1/credentials/status")
+		assert.NotEmpty(tt, statusEntry.StatusListIndex)
+
+		credStatus, err := credService.GetCredentialStatus(context.Background(), credential.GetCredentialStatusRequest{ID: createdCred.ID})
 		assert.NoError(tt, err)
-		assert.NotEmpty(tt, value)
+		assert.Equal(tt, credStatus.Revoked, false)
+		assert.Equal(tt, credStatus.Suspended, false)
+
+		credStatusListStr := statusEntry.StatusListCredential
 
-		statusListIndexesAfterRestart, err := bolt.Read(context.Background(), statusListIndexNamespace, statusListIndexesKey)
+		_, credStatusListID, ok := strings.Cut(credStatusListStr, "/v1/credentials/status/")
+		assert.True(tt, ok)
+		credStatusList, err := credService.GetCredentialStatusList(context.Background(), credential.GetCredentialStatusListRequest{ID: credStatusListID})
 		assert.NoError(tt, err)
-		assert.NotEmpty(tt, statusListIndexesAfterRestart)
+		assert.Equal(tt, credStatusList.Credential.ID, statusEntry.StatusListCredential)
+
+		credentialSubject := credStatusList.Container.Credential.CredentialSubject
+		assert.NotEmpty(tt, credentialSubject)
 
-		assert.Equal(tt, statusListIndexes, statusListIndexesAfterRestart)
+		encodedList := credentialSubject["encodedList"]
+		assert.NotEmpty(tt, encodedList)
+
+		// Validate the StatusListIndex is not flipped in the credStatusList
+		valid, err := status.ValidateCredentialInStatusList(*createdCred.Credential, *credStatusList.Credential)
+		assert.NoError(tt, err)
+		assert.False(tt, valid)
 	})
 
-	t.Run("Create Suspendable Credential", func(tt *testing.T) {
+	t.Run("Update Suspendable Credential To Suspended", func(tt *testing.T) {
 		issuer, schema, credService := createCredServicePrereqs(tt)
 		subject := "did:test:345"
 
@@ -657,9 +733,31 @@ func TestCredentialRouter(t *testing.T) {
 		valid, err := status.ValidateCredentialInStatusList(*createdCred.Credential, *credStatusList.Credential)
 		assert.NoError(tt, err)
 		assert.False(tt, valid)
+
+		updatedStatus, err := credService.UpdateCredentialStatus(context.Background(), credential.UpdateCredentialStatusRequest{ID: createdCred.ID, Suspended: true})
+		assert.NoError(tt, err)
+		assert.Equal(tt, updatedStatus.Suspended, true)
+		assert.Equal(tt, updatedStatus.Revoked, false)
+
+		credStatusListAfterRevoke, err := credService.GetCredentialStatusList(context.Background(), credential.GetCredentialStatusListRequest{ID: credStatusListID})
+		assert.NoError(tt, err)
+		assert.Equal(tt, credStatusListAfterRevoke.Credential.ID, statusEntry.StatusListCredential)
+
+		// Validate the StatusListIndex in flipped in the credStatusList
+		valid, err = status.ValidateCredentialInStatusList(*createdCred.Credential, *credStatusListAfterRevoke.Credential)
+		assert.NoError(tt, err)
+		assert.True(tt, valid)
+
+		credentialSubjectAfterRevoke := credStatusListAfterRevoke.Container.Credential.CredentialSubject
+		assert.NotEmpty(tt, credentialSubjectAfterRevoke)
+
+		encodedListAfterSuspended := credentialSubjectAfterRevoke["encodedList"]
+		assert.NotEmpty(tt, encodedListAfterSuspended)
+
+		assert.NotEqualValues(tt, encodedListAfterSuspended, encodedList)
 	})
 
-	t.Run("Update Suspendable Credential To Suspended", func(tt *testing.T) {
+	t.Run("Update Suspendable Credential To Suspended then Unsuspended", func(tt *testing.T) {
 		issuer, schema, credService := createCredServicePrereqs(tt)
 		subject := "did:test:345"
 
@@ -734,6 +832,11 @@ func TestCredentialRouter(t *testing.T) {
 		assert.NotEmpty(tt, encodedListAfterSuspended)
 
 		assert.NotEqualValues(tt, encodedListAfterSuspended, encodedList)
+
+		updatedStatus, err = credService.UpdateCredentialStatus(context.Background(), credential.UpdateCredentialStatusRequest{ID: createdCred.ID, Suspended: false})
+		assert.NoError(tt, err)
+		assert.Equal(tt, updatedStatus.Suspended, false)
+		assert.Equal(tt, updatedStatus.Revoked, false)
 	})
 
 	t.Run("Create Suspendable and Revocable Credential Should Be Error", func(tt *testing.T) {
@@ -804,7 +907,6 @@ func TestCredentialRouter(t *testing.T) {
 		assert.Error(tt, err)
 		assert.ErrorContains(tt, err, "has a different status purpose<revocation> value than the status credential<suspension>")
 	})
-
 }
 
 func createCredServicePrereqs(tt *testing.T) (string, string, credential.Service) {
diff --git a/pkg/service/credential/storage_test.go b/pkg/service/credential/storage_test.go
new file mode 100644
index 0000000..67c254a
--- /dev/null
+++ b/pkg/service/credential/storage_test.go
@@ -0,0 +1,27 @@
+package credential
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestCredentialStorage(t *testing.T) {
+	t.Run("Extract UUID returns UUID", func(tt *testing.T) {
+
+		uuid := ExtractID("/v1/credentials/status/ceccb36e-a386-494a-8bf5-d86f953485dd")
+		assert.NotEmpty(tt, uuid)
+		assert.Equal(tt, uuid, "ceccb36e-a386-494a-8bf5-d86f953485dd")
+
+		uuid = ExtractID("http://localhost:1234/v1/credentials/status/ceccb36e-a386-494a-8bf5-d86f953485dd")
+		assert.NotEmpty(tt, uuid)
+		assert.Equal(tt, uuid, "ceccb36e-a386-494a-8bf5-d86f953485dd")
+
+		uuid = ExtractID("ANYTHING/ceccb36e-a386-494a-8bf5-d86f953485dd")
+		assert.NotEmpty(tt, uuid)
+		assert.Equal(tt, uuid, "ceccb36e-a386-494a-8bf5-d86f953485dd")
+
+		uuid = ExtractID("badinput")
+		assert.Empty(tt, uuid)
+	})
+}
diff --git a/pkg/storage/db_test.go b/pkg/storage/db_test.go
index 2145988..6e00806 100644
--- a/pkg/storage/db_test.go
+++ b/pkg/storage/db_test.go
@@ -174,6 +174,48 @@ func TestDBPrefixAndKeys(t *testing.T) {
 	}
 }
 
+func TestDBReadAllKeysForNamespace(t *testing.T) {
+	for _, dbImpl := range getDBImplementations(t) {
+		db := dbImpl
+
+		namespaceOne := "blockchains-orange"
+		namespaceTwo := "blockchains-grey"
+
+		// set up prefix read test
+
+		dummyData := []byte("dummy")
+		err := db.Write(context.Background(), namespaceOne, "bitcoin-testnet", dummyData)
+		assert.NoError(t, err)
+
+		err = db.Write(context.Background(), namespaceOne, "bitcoin-mainnet", dummyData)
+		assert.NoError(t, err)
+
+		err = db.Write(context.Background(), namespaceTwo, "eth-testnet", dummyData)
+		assert.NoError(t, err)
+
+		err = db.Write(context.Background(), namespaceTwo, "eth-mainnet", dummyData)
+		assert.NoError(t, err)
+
+		// read all keys
+		allKeys, err := db.ReadAllKeys(context.Background(), namespaceOne)
+
+		assert.NoError(t, err)
+		assert.NotEmpty(t, allKeys)
+		assert.Len(t, allKeys, 2)
+		assert.Contains(t, allKeys, "bitcoin-testnet")
+		assert.Contains(t, allKeys, "bitcoin-mainnet")
+
+		// read all keys
+		allKeys, err = db.ReadAllKeys(context.Background(), namespaceTwo)
+
+		assert.NoError(t, err)
+		assert.NotEmpty(t, allKeys)
+		assert.Len(t, allKeys, 2)
+		assert.Contains(t, allKeys, "eth-testnet")
+		assert.Contains(t, allKeys, "eth-mainnet")
+	}
+}
+
 func TestDBEmptyNamespace(t *testing.T) {
 	for _, dbImpl := range getDBImplementations(t) {
 		db := dbImpl