// 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 }