Exemple #1
0
// VerifierStream implements the interfaceE2EKSVerification interface from proto/verifier.proto
func (ks *Keyserver) VerifierStream(rq *proto.VerifierStreamRequest, stream proto.E2EKSVerification_VerifierStreamServer) error {
	var step proto.VerifierStep // stack-allocate db read buffer
	for start, limit := rq.Start, saturatingAdd(rq.Start, rq.PageSize); start < limit; {
		// Try a kv.Range range scan first because it is the fastest. If this
		// does not satisfy the entire request (because the future entries have
		// not been generated yet), use ks.sb to wait for new entries, falling
		// back to range scans when this thread fails to meet the timing
		// constraints of ks.sb. Both methods of accessing verifier log
		// entries are surfaced here due to flow control and memory allocation
		// constraints: we cannot allow allocation of an unbounded queue.
		iter := ks.db.NewIterator(&kv.Range{Start: tableVerifierLog(start), Limit: tableVerifierLog(limit)})
		for ; iter.Next() && start < limit; start++ {
			select {
			case <-stream.Context().Done():
				iter.Release()
				return stream.Context().Err()
			default:
			}
			dbIdx := binary.BigEndian.Uint64(iter.Key()[1:])
			if dbIdx != start {
				log.Printf("ERROR: non-consecutive entries in verifier log (wanted %d, got %d)", start, dbIdx)
				iter.Release()
				return fmt.Errorf("internal error")
			}
			if err := step.Unmarshal(iter.Value()); err != nil {
				log.Printf("ERROR: invalid protobuf entry in verifier log (index %d)", start)
				iter.Release()
				return fmt.Errorf("internal error")
			}
			if err := stream.Send(&step); err != nil {
				iter.Release()
				return err
			}
			step.Reset()
		}
		iter.Release()
		if err := iter.Error(); err != nil {
			log.Printf("ERROR: range [tableVerifierLog(%d), tableVerifierLog(%d)) ended at %d (not included) with error %s", rq.Start, limit, start, err)
			return fmt.Errorf("internal error")
		}

		// the requested entries are not in the db yet, so let's try to collect
		// them from the sb. ch=nil -> the desired log entry was sent after we
		// did the db range scan but before we called Receive -> it's in db now.
	sbLoop:
		for ch := ks.sb.Receive(start, limit); ch != nil && start < limit; start++ {
			select {
			case <-stream.Context().Done():
				return stream.Context().Err()
			case sbStep, ok := <-ch: // declares new variable, a &const
				if !ok {
					// sb closed the connection. This must be because this
					// client was slow and sb does not wait for laggards.
					// This is okay though: if sb does not have the step
					// anymore, the db must: let's get it from there.
					break sbLoop
				}
				if err := stream.Send(sbStep.(*proto.VerifierStep)); err != nil {
					return err
				}
			}
		}
	}
	return nil
}