Skip to content

Commit

Permalink
Add search through inactive shards for GET by UUID
Browse files Browse the repository at this point in the history
Signed-off-by: Lily Sturmann <lsturman@redhat.com>
  • Loading branch information
lkatalin committed Apr 7, 2022
1 parent 34d242e commit ff9f2fd
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 40 deletions.
29 changes: 26 additions & 3 deletions pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,18 +66,41 @@ type API struct {
certChainPem string // PEM encoded timestamping cert chain
}

func NewAPI(ranges sharding.LogRanges, treeID uint) (*API, error) {
func GetTrees() (*trillian.ListTreesResponse, error) {
ctx := context.Background()
logAdminClient, _, err := GetLogServerAndClients(ctx)
if err != nil {
return nil, errors.Wrap(err, "get log server and clients")
}
lac := *logAdminClient
trees, err := lac.ListTrees(ctx, &trillian.ListTreesRequest{})
if err != nil {
return nil, errors.Wrap(err, "list trees")
}
return trees, nil
}

func GetLogServerAndClients(ctx context.Context) (*trillian.TrillianAdminClient, *trillian.TrillianLogClient, error) {
logRPCServer := fmt.Sprintf("%s:%d",
viper.GetString("trillian_log_server.address"),
viper.GetUint("trillian_log_server.port"))
ctx := context.Background()
tConn, err := dial(ctx, logRPCServer)
if err != nil {
return nil, errors.Wrap(err, "dial")
return nil, nil, errors.Wrap(err, "dial")
}
logAdminClient := trillian.NewTrillianAdminClient(tConn)
logClient := trillian.NewTrillianLogClient(tConn)
return &logAdminClient, &logClient, nil
}

func NewAPI(ranges sharding.LogRanges, treeID uint) (*API, error) {
ctx := context.Background()
lac, lc, err := GetLogServerAndClients(ctx)
if err != nil {
return nil, err
}
logAdminClient := *lac
logClient := *lc
tid := int64(treeID)
if tid == 0 {
log.Logger.Info("No tree ID specified, attempting to create a new tree")
Expand Down
114 changes: 77 additions & 37 deletions pkg/api/entries.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,14 @@ func createLogEntry(params entries.CreateLogEntryParams) (models.LogEntry, middl
metricNewEntries.Inc()

queuedLeaf := resp.getAddResult.QueuedLeaf.Leaf
uuid := hex.EncodeToString(queuedLeaf.GetMerkleLeafHash())

hexEncodedHash := hex.EncodeToString(queuedLeaf.GetMerkleLeafHash())
entryID, err := sharding.CreateEntryIDFromParts(fmt.Sprintf("%x", tc.logID), hexEncodedHash)
if err != nil {
err := fmt.Errorf("error creating EntryID from active treeID and uuid %v: %w", hexEncodedHash, err)
return nil, handleRekorAPIError(params, http.StatusInternalServerError, err, fmt.Sprintf(validationError, err))
}
uuid := entryID.ReturnEntryIDString()

// The log index should be the virtual log index across all shards
virtualIndex := sharding.VirtualLogIndex(queuedLeaf.LeafIndex, api.logRanges.ActiveTreeID(), api.logRanges)
Expand Down Expand Up @@ -221,7 +228,7 @@ func createLogEntry(params entries.CreateLogEntryParams) (models.LogEntry, middl
log.RequestIDLogger(params.HTTPRequest).Infof("no attestation for %s", uuid)
return
}
if err := storeAttestation(context.Background(), uuid, attestation); err != nil {
if err := storeAttestation(context.Background(), entryID.UUID, attestation); err != nil {
log.RequestIDLogger(params.HTTPRequest).Errorf("error storing attestation: %s", err)
}
}()
Expand Down Expand Up @@ -270,56 +277,89 @@ func getEntryURL(locationURL url.URL, uuid string) strfmt.URI {

}

// GetLogEntryByUUIDHandler gets log entry and inclusion proof for specified UUID aka merkle leaf hash
func GetLogEntryByUUIDHandler(params entries.GetLogEntryByUUIDParams) middleware.Responder {
// Attempt to retrieve a UUID from a backend tree
// TODO: don't return a bool that specifies whether it is an error, but instead check
// the middleware.Responder to see if it is an error type (how?)
func AttemptRetrieveUUID(params entries.GetLogEntryByUUIDParams, uuid string, tid int64) (middleware.Responder, bool) {
ctx := params.HTTPRequest.Context()

uuid, err := sharding.GetUUIDFromIDString(params.EntryUUID)
if err != nil {
return handleRekorAPIError(params, http.StatusBadRequest, err, "")
}
var tid int64
tidString, err := sharding.GetTreeIDFromIDString(params.EntryUUID)
if err != nil {
// If EntryID is plain UUID, assume no sharding and use ActiveTreeID. The ActiveTreeID
// will == the tlog_id if a tlog_id is passed in at server startup.
if err.Error() == "cannot get treeID from plain UUID" {
tid = api.logRanges.ActiveTreeID()
} else {
return handleRekorAPIError(params, http.StatusBadRequest, err, "")
}
} else {
tid, err = strconv.ParseInt(tidString, 16, 64)
if err != nil {
return handleRekorAPIError(params, http.StatusBadRequest, err, "")
}
}
hashValue, _ := hex.DecodeString(uuid)

tc := NewTrillianClientFromTreeID(params.HTTPRequest.Context(), tid)
log.RequestIDLogger(params.HTTPRequest).Debugf("Retrieving UUID %v from TreeID %v", uuid, tid)
log.RequestIDLogger(params.HTTPRequest).Debugf("Attempting to retrieve UUID %v from TreeID %v", uuid, tid)

resp := tc.getLeafAndProofByHash(hashValue)
switch resp.status {
case codes.OK:
result := resp.getLeafAndProofResult
leaf := result.Leaf
if leaf == nil {
return handleRekorAPIError(params, http.StatusNotFound, errors.New("grpc returned 0 leaves with success code"), ""), true
}

logEntry, err := logEntryFromLeaf(ctx, api.signer, tc, leaf, result.SignedLogRoot, result.Proof, tid, api.logRanges)
if err != nil {
return handleRekorAPIError(params, http.StatusInternalServerError, err, ""), true
}
return entries.NewGetLogEntryByUUIDOK().WithPayload(logEntry), false

case codes.NotFound:
return handleRekorAPIError(params, http.StatusNotFound, fmt.Errorf("grpc error: %w", resp.err), "")
return handleRekorAPIError(params, http.StatusNotFound, fmt.Errorf("grpc error: %w", resp.err), ""), true
default:
return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("grpc error: %w", resp.err), trillianUnexpectedResult)
}

result := resp.getLeafAndProofResult
leaf := result.Leaf
if leaf == nil {
return handleRekorAPIError(params, http.StatusNotFound, errors.New("grpc returned 0 leaves with success code"), "")
return handleRekorAPIError(params, http.StatusInternalServerError, fmt.Errorf("grpc error: %w", resp.err), trillianUnexpectedResult), true
}
}

logEntry, err := logEntryFromLeaf(ctx, api.signer, tc, leaf, result.SignedLogRoot, result.Proof, tid, api.logRanges)
// GetLogEntryByUUIDHandler gets log entry and inclusion proof for specified UUID aka merkle leaf hash
func GetLogEntryByUUIDHandler(params entries.GetLogEntryByUUIDParams) middleware.Responder {
uuid, err := sharding.GetUUIDFromIDString(params.EntryUUID)
if err != nil {
return handleRekorAPIError(params, http.StatusInternalServerError, err, "")
return handleRekorAPIError(params, http.StatusBadRequest, err, "")
}
tidString, err := sharding.GetTreeIDFromIDString(params.EntryUUID)

switch err {
// No error means we can route to the correct treeID from the entryID
case nil:
tid, err := strconv.ParseInt(tidString, 16, 64)
if err != nil {
return handleRekorAPIError(params, http.StatusBadRequest, err, "")
}
resp, _ := AttemptRetrieveUUID(params, uuid, tid)
return resp

return entries.NewGetLogEntryByUUIDOK().WithPayload(logEntry)
// If there is an error, see if it is because the EntryID is a UUID and if so,
// handle it
default:
switch err.Error() {
// If EntryID is plain UUID (ex. from client v0.5), first try the active tree
case "cannot get treeID from plain UUID":
tid := api.logRanges.ActiveTreeID()
resp, isErr := AttemptRetrieveUUID(params, uuid, tid)
if isErr {
// ... then check all inactive trees
trees, err := GetTrees()
if err != nil {
return handleRekorAPIError(params, http.StatusInternalServerError, err, "")
}

for _, t := range trees.Tree {
if t.TreeType == trillian.TreeType_LOG {
tid := t.TreeId
resp, isErr := AttemptRetrieveUUID(params, uuid, tid)
if !isErr {
return resp
}
}
}
} else {
return resp
}

default:
return handleRekorAPIError(params, http.StatusBadRequest, err, "")
}
}
return handleRekorAPIError(params, http.StatusBadRequest, err, "UUID not found in any inactive trees")
}

// SearchLogQueryHandler searches log by index, UUID, or proposed entry and returns array of entries found with inclusion proofs
Expand Down

0 comments on commit ff9f2fd

Please sign in to comment.