Skip to content

Commit

Permalink
Optimized the Search Function
Browse files Browse the repository at this point in the history
- Refactored the `MemorySource` struct in `memory.go` to include `referencesBySubjectDigest`
- Updated the `NewMemorySource` function to initialize these new fields.
- Modified the `LoadEnvelope` function to populate these new fields when loading an envelope.
- Updated the `Search` function to use these new fields when searching for matching envelopes.
- These changes improve the efficiency of the search operation by
  reducing the time complexity.

Signed-off-by: neilnaveen <[email protected]>
  • Loading branch information
neilnaveen committed Oct 30, 2023
1 parent 5e567f0 commit 904fe4b
Showing 1 changed file with 39 additions and 38 deletions.
77 changes: 39 additions & 38 deletions source/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"fmt"
"io"
"os"
"sort"
"strings"

"github.com/testifysec/go-witness/dsse"
)
Expand All @@ -33,16 +35,16 @@ func (e ErrDuplicateReference) Error() string {
type MemorySource struct {
envelopesByReference map[string]CollectionEnvelope
referencesByCollectionName map[string][]string
subjectDigestsByReference map[string]map[string]struct{}
attestationsByReference map[string]map[string]struct{}
referencesBySubjectDigest map[string][]string
attestationsByReference map[string]struct{}
}

func NewMemorySource() *MemorySource {
return &MemorySource{
envelopesByReference: make(map[string]CollectionEnvelope),
referencesByCollectionName: make(map[string][]string),
subjectDigestsByReference: make(map[string]map[string]struct{}),
attestationsByReference: make(map[string]map[string]struct{}),
referencesBySubjectDigest: make(map[string][]string),
attestationsByReference: make(map[string]struct{}),
}
}

Expand Down Expand Up @@ -86,60 +88,59 @@ func (s *MemorySource) LoadEnvelope(reference string, env dsse.Envelope) error {

s.envelopesByReference[reference] = collEnv
s.referencesByCollectionName[collEnv.Collection.Name] = append(s.referencesByCollectionName[collEnv.Collection.Name], reference)
subDigestIndex := make(map[string]struct{})

// Add the reference to the map of references by subject digest for each subject in the statement
for _, sub := range collEnv.Statement.Subject {
for _, digest := range sub.Digest {
subDigestIndex[digest] = struct{}{}
s.referencesBySubjectDigest[digest] = append(s.referencesBySubjectDigest[digest], reference)
}
}

s.subjectDigestsByReference[reference] = subDigestIndex
attestationIndex := make(map[string]struct{})
// Sort the attestations in the collection envelope by their type
sort.Slice(collEnv.Collection.Attestations, func(i, j int) bool {
return collEnv.Collection.Attestations[i].Attestation.Type() < collEnv.Collection.Attestations[j].Attestation.Type()
})

// Compress the attestations into a key for quick lookup
attkey := ""
for _, att := range collEnv.Collection.Attestations {
attestationIndex[att.Attestation.Type()] = struct{}{}
attkey += att.Attestation.Type()
}

s.attestationsByReference[reference] = attestationIndex
// Add the attestation key to the map of attestations by reference
s.attestationsByReference[reference+attkey] = struct{}{}
return nil
}

func (s *MemorySource) Search(ctx context.Context, collectionName string, subjectDigests, attestations []string) ([]CollectionEnvelope, error) {
matches := make([]CollectionEnvelope, 0)
for _, potentialMatchReference := range s.referencesByCollectionName[collectionName] {
env, ok := s.envelopesByReference[potentialMatchReference]
if !ok {
continue
}
// Initialize an empty slice to store matching envelopes
matches := []CollectionEnvelope{}

// make sure at least one of the subjects digests exists on the potential matches
subjectMatchFound := false
indexSubjects := s.subjectDigestsByReference[potentialMatchReference]
for _, checkDigest := range subjectDigests {
if _, ok := indexSubjects[checkDigest]; ok {
subjectMatchFound = true
break
}
}
// Sort attestations and join them into a key
sort.Strings(attestations)
attestationKey := strings.Join(attestations, "")

if !subjectMatchFound {
continue
}
// Initialize a map to store potential matches
potentialMatches := map[string]struct{}{}

// make sure all the expected attestations appear in the collection
attestationsMatched := true
indexAttestations := s.attestationsByReference[potentialMatchReference]
for _, checkAttestation := range attestations {
if _, ok := indexAttestations[checkAttestation]; !ok {
attestationsMatched = false
break
}
// Populate the map with references from subject digests
for _, subjectDigest := range subjectDigests {
for _, reference := range s.referencesBySubjectDigest[subjectDigest] {
potentialMatches[reference] = struct{}{}
}
}

// Iterate over potential matches and check if they exist in the envelopes by reference
for reference := range potentialMatches {
envelope, envelopeExists := s.envelopesByReference[reference]

if !attestationsMatched {
// Check if all the expected attestations appear in the collection and the envelope exist in the memory source
if _, containsNecessaryAttestations := s.attestationsByReference[reference+attestationKey]; !containsNecessaryAttestations || !envelopeExists {
continue
}

matches = append(matches, env)
// If all checks pass, append the envelope to matches
matches = append(matches, envelope)
}

return matches, nil
Expand Down

0 comments on commit 904fe4b

Please sign in to comment.