Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Search through all shards when searching by hash #1082

Merged
merged 5 commits into from Sep 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -17,3 +17,6 @@ rekorServerImagerefs
rekorCliImagerefs
trillianServerImagerefs
trillianSignerImagerefs
cosign.*
signature
rekor.pub
45 changes: 29 additions & 16 deletions pkg/api/entries.go
Expand Up @@ -350,7 +350,6 @@ func GetLogEntryByUUIDHandler(params entries.GetLogEntryByUUIDParams) middleware
func SearchLogQueryHandler(params entries.SearchLogQueryParams) middleware.Responder {
httpReqCtx := params.HTTPRequest.Context()
resultPayload := []models.LogEntry{}
tc := NewTrillianClient(httpReqCtx)

totalQueries := len(params.Entry.EntryUUIDs) + len(params.Entry.Entries()) + len(params.Entry.LogIndexes)
if totalQueries > maxSearchQueries {
Expand Down Expand Up @@ -415,23 +414,34 @@ func SearchLogQueryHandler(params entries.SearchLogQueryParams) middleware.Respo
searchHashes = append(searchHashes, hash)
}

searchByHashResults := make([]*trillian.GetEntryAndProofResponse, len(searchHashes))
searchByHashResults := make([]map[int64]*trillian.GetEntryAndProofResponse, len(searchHashes))
g, _ = errgroup.WithContext(httpReqCtx)
for i, hash := range searchHashes {
i, hash := i, hash // https://golang.org/doc/faq#closures_and_goroutines
g.Go(func() error {
resp := tc.getLeafAndProofByHash(hash)
switch resp.status {
case codes.OK:
case codes.NotFound:
code = http.StatusNotFound
return resp.err
default:
var results map[int64]*trillian.GetEntryAndProofResponse
for _, shard := range api.logRanges.AllShards() {
tcs := NewTrillianClientFromTreeID(httpReqCtx, shard)
resp := tcs.getLeafAndProofByHash(hash)
if resp.status != codes.OK {
continue
}
if resp.err != nil {
continue
}
leafResult := resp.getLeafAndProofResult
if leafResult != nil && leafResult.Leaf != nil {
if results == nil {
results = map[int64]*trillian.GetEntryAndProofResponse{}
}
results[shard] = resp.getLeafAndProofResult
}
}
leafResult := resp.getLeafAndProofResult
if leafResult != nil && leafResult.Leaf != nil {
searchByHashResults[i] = leafResult
if results == nil {
code = http.StatusNotFound
return fmt.Errorf("no responses found")
priyawadhwa marked this conversation as resolved.
Show resolved Hide resolved
}
searchByHashResults[i] = results
return nil
})
}
Expand All @@ -440,14 +450,17 @@ func SearchLogQueryHandler(params entries.SearchLogQueryParams) middleware.Respo
return handleRekorAPIError(params, code, err, err.Error())
}

for _, leafResp := range searchByHashResults {
if leafResp != nil {
logEntry, err := logEntryFromLeaf(httpReqCtx, api.signer, tc, leafResp.Leaf, leafResp.SignedLogRoot, leafResp.Proof, api.logRanges.ActiveTreeID(), api.logRanges)
for _, hashMap := range searchByHashResults {
for shard, leafResp := range hashMap {
if leafResp == nil {
continue
}
tcs := NewTrillianClientFromTreeID(httpReqCtx, shard)
logEntry, err := logEntryFromLeaf(httpReqCtx, api.signer, tcs, leafResp.Leaf, leafResp.SignedLogRoot, leafResp.Proof, shard, api.logRanges)
if err != nil {
code = http.StatusInternalServerError
return handleRekorAPIError(params, code, err, err.Error())
}

resultPayload = append(resultPayload, logEntry)
}
}
Expand Down
9 changes: 9 additions & 0 deletions pkg/sharding/ranges.go
Expand Up @@ -138,6 +138,15 @@ func (l *LogRanges) NoInactive() bool {
return l.inactive == nil
}

// AllShards returns all shards, starting with the active shard and then the inactive shards
func (l *LogRanges) AllShards() []int64 {
shards := []int64{l.ActiveTreeID()}
for _, in := range l.GetInactive() {
shards = append(shards, in.TreeID)
}
return shards
}

// TotalInactiveLength returns the total length across all inactive shards;
// we don't know the length of the active shard.
func (l *LogRanges) TotalInactiveLength() int64 {
Expand Down
7 changes: 6 additions & 1 deletion tests/sharding-e2e-test.sh
Expand Up @@ -24,7 +24,6 @@ set -ex
echo "Installing createtree..."
go install github.com/google/trillian/cmd/createtree@latest


echo "starting services"
docker-compose up -d
rm ~/.rekor/state.json || true
Expand All @@ -34,6 +33,7 @@ go build -o rekor-cli ./cmd/rekor-cli
REKOR_CLI=$(pwd)/rekor-cli
go build -o rekor-server ./cmd/rekor-server


function check_log_index () {
logIndex=$1
# make sure we can get this log index from rekor
Expand Down Expand Up @@ -251,6 +251,11 @@ ENTRY_ID_2=$($REKOR_CLI get --log-index 3 --rekor_server http://localhost:3000 -
NUM_ELEMENTS=$(curl -f http://localhost:3000/api/v1/log/entries/retrieve -H "Content-Type: application/json" -H "Accept: application/json" -d "{ \"entryUUIDs\": [\"$ENTRY_ID_1\"]}" | jq '. | length')
stringsMatch $NUM_ELEMENTS "1"

# Make sure we can verify the entry we entered into the now-inactive shard
pushd tests
$REKOR_CLI verify --artifact test_file.txt --signature test_file.sig --public-key test_public_key.key --rekor_server http://localhost:3000
popd

# -f makes sure we exit on failure
NUM_ELEMENTS=$(curl -f http://localhost:3000/api/v1/log/entries/retrieve -H "Content-Type: application/json" -H "Accept: application/json" -d "{ \"entryUUIDs\": [\"$ENTRY_ID_1\", \"$ENTRY_ID_2\"]}" | jq '. | length')
stringsMatch $NUM_ELEMENTS "2"
Expand Down