-
Notifications
You must be signed in to change notification settings - Fork 165
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
Update loginfo API endpoint to return information about inactive shards #738
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ | |
package api | ||
|
||
import ( | ||
"context" | ||
"encoding/hex" | ||
"fmt" | ||
"net/http" | ||
|
@@ -39,6 +40,20 @@ import ( | |
func GetLogInfoHandler(params tlog.GetLogInfoParams) middleware.Responder { | ||
tc := NewTrillianClient(params.HTTPRequest.Context()) | ||
|
||
// for each inactive shard, get the loginfo | ||
var inactiveShards []*models.InactiveShardLogInfo | ||
for _, shard := range tc.ranges.GetRanges() { | ||
if shard.TreeID == tc.ranges.ActiveTreeID() { | ||
break | ||
} | ||
// Get details for this inactive shard | ||
is, err := inactiveShardLogInfo(params.HTTPRequest.Context(), shard.TreeID) | ||
if err != nil { | ||
return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("inactive shard error: %w", err), unexpectedInactiveShardError) | ||
} | ||
inactiveShards = append(inactiveShards, is) | ||
} | ||
|
||
resp := tc.getLatest(0) | ||
if resp.status != codes.OK { | ||
return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("grpc error: %w", resp.err), trillianCommunicationError) | ||
|
@@ -80,6 +95,7 @@ func GetLogInfoHandler(params tlog.GetLogInfoParams) middleware.Responder { | |
TreeSize: &treeSize, | ||
SignedTreeHead: &scString, | ||
TreeID: stringPointer(fmt.Sprintf("%d", tc.logID)), | ||
InactiveShards: inactiveShards, | ||
} | ||
|
||
return tlog.NewGetLogInfoOK().WithPayload(&logInfo) | ||
|
@@ -136,3 +152,47 @@ func GetLogProofHandler(params tlog.GetLogProofParams) middleware.Responder { | |
|
||
return tlog.NewGetLogProofOK().WithPayload(&consistencyProof) | ||
} | ||
|
||
func inactiveShardLogInfo(ctx context.Context, tid int64) (*models.InactiveShardLogInfo, error) { | ||
tc := NewTrillianClientFromTreeID(ctx, tid) | ||
resp := tc.getLatest(0) | ||
if resp.status != codes.OK { | ||
return nil, fmt.Errorf("resp code is not OK") | ||
priyawadhwa marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
result := resp.getLatestResult | ||
|
||
root := &types.LogRootV1{} | ||
if err := root.UnmarshalBinary(result.SignedLogRoot.LogRoot); err != nil { | ||
return nil, err | ||
} | ||
|
||
hashString := hex.EncodeToString(root.RootHash) | ||
treeSize := int64(root.TreeSize) | ||
|
||
sth, err := util.CreateSignedCheckpoint(util.Checkpoint{ | ||
Origin: "Rekor", | ||
Size: root.TreeSize, | ||
Hash: root.RootHash, | ||
}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
sth.SetTimestamp(uint64(time.Now().UnixNano())) | ||
|
||
// sign the log root ourselves to get the log root signature | ||
if _, err := sth.Sign(viper.GetString("rekor_server.hostname"), api.signer, options.WithContext(ctx)); err != nil { | ||
return nil, err | ||
} | ||
Comment on lines
+182
to
+185
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How does this get the log root signature? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah okay, I think I get it, this enables us to get the signature later (not in this line of code) with |
||
|
||
scBytes, err := sth.SignedNote.MarshalText() | ||
if err != nil { | ||
return nil, err | ||
} | ||
m := models.InactiveShardLogInfo{ | ||
RootHash: &hashString, | ||
TreeSize: &treeSize, | ||
TreeID: stringPointer(fmt.Sprintf("%d", tid)), | ||
SignedTreeHead: stringPointer(string(scBytes)), | ||
} | ||
return &m, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there is no
treeID
this will just dump from the URL, but if there is both atreeID
and a URL, what happens? Does the URL only cover the active tree and only in the case of an older client? Or does some state get stored twice?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some state will get stored twice! But when reading from the file the client will always prefer the
treeID
over the URL.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay cool, yeah I see that in
oldState
. Just trying to understand the mechanism here - the URL covers all trees, right? What does dumping bytreeID
(if available) add?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So if we have two shards, they'll be accessed at the same URL (e.g. rekor.sigstore.dev). We can't store state for both of them, because the key by URL would be the same. The TreeID is unique for each shard, so we can store state for all shards at once if we use
treeID
as the key.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this makes sense, I see it is a hash map so we're giving it differentiated keys. I will try to get some output to see it for myself. Thanks for the explanation.