Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 47 additions & 43 deletions vindex/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,52 +39,13 @@ import (
"k8s.io/klog/v2"
)

// NewVIndexClient returns a client that can perform verified lookups into the index at the
// given base URL, using the supplied verifier to check checkpoint signatures on the output
// log.
func NewVIndexClient(vindexUrl string, outV note.Verifier) (*VIndexClient, error) {
viu, err := url.Parse(vindexUrl)
if err != nil {
return nil, fmt.Errorf("failed to parse URL: %v", err)
}
lookupURL := viu.JoinPath(api.PathLookup)

return &VIndexClient{
lookupURL: lookupURL,
outV: outV,
}, nil
}

// VIndexClient allows verified lookups into a verifiable index.
type VIndexClient struct {
lookupURL *url.URL
outV note.Verifier
}

// Lookup returns all indices, in ascending order, where the given key appears in the Input Log.
// This will be verified before being returned from this method, so a caller can be assured that
// any results (including the empty slice, i.e. non-presence) were found in the verifiable index,
// and committed to by the output log.
// On success, this also returns the Checkpoint for the Input Log that was relied upon by the
// verifiable index. This may be used by the caller when constructing inclusion proofs when
// dereferencing any pointers returned.
//
// Note that it is up to the caller to ensure that any leaves looked up in the Input Log are
// verified by an inclusion proof. The checkpoint returned by this method can be used.
// The easiest way to do this is to use the InputLogClient.
func (c VIndexClient) Lookup(ctx context.Context, key string) ([]uint64, []byte, error) {
kh := sha256.Sum256([]byte(key))
resp, err := c.lookupUnverified(ctx, kh)
if err != nil {
return nil, nil, fmt.Errorf("lookup failed: %v", err)
}

func VerifyLookupResponse(keyHash [sha256.Size]byte, resp api.LookupResponse, outV note.Verifier) ([]uint64, []byte, error) {
// Currently the response contains the RFC6962 style response type; leaf, proof, etc.
// What if we flip this all around, and the OutputLog part of the response
// only returns an index into the output log, and the client has to look up
// that leaf, checkpoint, and generate inclusion proof?

cp, _, _, err := log.ParseCheckpoint(resp.OutputLogCP, c.outV.Name(), c.outV)
cp, _, _, err := log.ParseCheckpoint(resp.OutputLogCP, outV.Name(), outV)
if err != nil {
return nil, nil, fmt.Errorf("failed to parse output log checkpoint: %v", err)
}
Expand Down Expand Up @@ -124,18 +85,61 @@ func (c VIndexClient) Lookup(ctx context.Context, key string) ([]uint64, []byte,
}

if len(resp.IndexValue) > 0 {
if err := prefix.VerifyMembershipProof(sha256.Sum256, kh, [32]byte(vindexLeafHash), pns, mapRoot); err != nil {
if err := prefix.VerifyMembershipProof(sha256.Sum256, keyHash, [32]byte(vindexLeafHash), pns, mapRoot); err != nil {
return nil, nil, fmt.Errorf("failed to verify membership: %v", err)
}
} else {
if err := prefix.VerifyNonMembershipProof(sha256.Sum256, kh, pns, mapRoot); err != nil {
if err := prefix.VerifyNonMembershipProof(sha256.Sum256, keyHash, pns, mapRoot); err != nil {
return nil, nil, fmt.Errorf("failed to verify non-membership: %v", err)
}
}

return resp.IndexValue, inCp, nil
}

// NewVIndexClient returns a client that can perform verified lookups into the index at the
// given base URL, using the supplied verifier to check checkpoint signatures on the output
// log.
func NewVIndexClient(vindexUrl string, outV note.Verifier) (*VIndexClient, error) {
viu, err := url.Parse(vindexUrl)
if err != nil {
return nil, fmt.Errorf("failed to parse URL: %v", err)
}
lookupURL := viu.JoinPath(api.PathLookup)

return &VIndexClient{
lookupURL: lookupURL,
outV: outV,
}, nil
}

// VIndexClient allows verified lookups into a verifiable index.
type VIndexClient struct {
lookupURL *url.URL
outV note.Verifier
}

// Lookup returns all indices, in ascending order, where the given key appears in the Input Log.
// This will be verified before being returned from this method, so a caller can be assured that
// any results (including the empty slice, i.e. non-presence) were found in the verifiable index,
// and committed to by the output log.
// On success, this also returns the Checkpoint for the Input Log that was relied upon by the
// verifiable index. This may be used by the caller when constructing inclusion proofs when
// dereferencing any pointers returned.
//
// Note that it is up to the caller to ensure that any leaves looked up in the Input Log are
// verified by an inclusion proof. The checkpoint returned by this method can be used.
// The easiest way to do this is to use the InputLogClient.
func (c VIndexClient) Lookup(ctx context.Context, key string) ([]uint64, []byte, error) {
kh := sha256.Sum256([]byte(key))
resp, err := c.lookupUnverified(ctx, kh)
if err != nil {
return nil, nil, fmt.Errorf("lookup failed: %v", err)
}

return VerifyLookupResponse(kh, resp, c.outV)
}

func (c VIndexClient) lookupUnverified(ctx context.Context, kh [sha256.Size]byte) (api.LookupResponse, error) {
var lookupResp api.LookupResponse

Expand Down
4 changes: 3 additions & 1 deletion vindex/cmd/logandmap/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,9 @@ func inputLogOrDie(ctx context.Context, inputLogDir string) (log logReaderSource
go submitEntries(ctx, inputAppender)

return inputLog, func() {
_ = inputShutdown(ctx)
if err := inputShutdown(ctx); err != nil {
klog.Warningf("Error shutting down Input Log appender: %v", err)
}
}
}

Expand Down
15 changes: 8 additions & 7 deletions vindex/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,10 +365,12 @@ func (b *VerifiableIndex) Close() error {
// Lookup returns the values stored for the given key.
func (b *VerifiableIndex) Lookup(ctx context.Context, key [sha256.Size]byte) (api.LookupResponse, error) {
// Scope the lock to be as minimal as possible
lookupLocked := func(key [sha256.Size]byte) []uint64 {
// This looks up the indices from the in-memory map, and the proof from the vindex.
lookupLocked := func(key [sha256.Size]byte) (bool, []prefix.ProofNode, []uint64, error) {
b.indexMu.RLock()
defer b.indexMu.RUnlock()
return b.data[key]
found, viProof, err := b.vindex.Lookup(ctx, key)
return found, viProof, b.data[key], err
}

result := api.LookupResponse{}
Expand Down Expand Up @@ -404,7 +406,10 @@ func (b *VerifiableIndex) Lookup(ctx context.Context, key [sha256.Size]byte) (ap
result.OutputLogLeaf = data
result.OutputLogProof = proof

allIndices := lookupLocked(key)
found, viProof, allIndices, err := lookupLocked(key)
if err != nil {
return result, fmt.Errorf("failed to get inclusion proof from vindex: %v", err)
}

cutoff := slices.IndexFunc(allIndices, func(idx uint64) bool {
return idx >= size
Expand All @@ -415,10 +420,6 @@ func (b *VerifiableIndex) Lookup(ctx context.Context, key [sha256.Size]byte) (ap
}
result.IndexValue = allIndices

found, viProof, err := b.vindex.Lookup(ctx, key)
if err != nil {
return result, fmt.Errorf("failed to get inclusion proof from vindex: %v", err)
}
if expectFound := len(allIndices) > 0; expectFound != found {
return result, fmt.Errorf("found = %t, but expected %t (number of indices: %d)", found, expectFound, len(allIndices))
}
Expand Down
Loading
Loading