// Waits until a sufficient quorum is assembled func (ks *Keyserver) blockingLookup(ctx context.Context, req *proto.LookupRequest, epoch uint64) (*proto.LookupProof, error) { newSignatures := make(chan interface{}, newSignatureBufferSize) ks.signatureBroadcast.Subscribe(epoch, newSignatures) defer ks.signatureBroadcast.Unsubscribe(epoch, newSignatures) verifiersLeft := coname.ListQuorum(req.QuorumRequirement, nil) ratifications, haveVerifiers, err := ks.findRatificationsForEpoch(epoch, verifiersLeft) if err != nil { return nil, err } for v := range haveVerifiers { delete(verifiersLeft, v) } for !coname.CheckQuorum(req.QuorumRequirement, haveVerifiers) { select { case <-ctx.Done(): return nil, fmt.Errorf("timed out while waiting for ratification") case v := <-newSignatures: newSig := v.(*proto.SignedEpochHead) for id := range newSig.Signatures { if _, ok := verifiersLeft[id]; ok { ratifications = append(ratifications, newSig) delete(verifiersLeft, id) haveVerifiers[id] = struct{}{} } } } } return ks.assembleLookupProof(req, epoch, ratifications) }
func (ks *Keyserver) findLatestEpochSignedByQuorum(quorum *proto.QuorumExpr) (uint64, []*proto.SignedEpochHead, error) { verifiers := coname.ListQuorum(quorum, nil) // find latest epoch, iterate backwards until quorum requirement is met // 0 is bad for iterating uint64 in the negative direction and there is no epoch 0 oldestEpoch, newestEpoch := uint64(1), ks.lastSignedEpoch() if newestEpoch == 0 { log.Printf("ERROR: no epochs created yet, so lookup failed") return 0, nil, fmt.Errorf("internal error") } if newestEpoch-oldestEpoch > ks.laggingVerifierScan { // careful with overflows! oldestEpoch = newestEpoch - ks.laggingVerifierScan } // TODO: (for lookup throughput and latency) optimize this for the case // where verifiers sign everything consecutively for epoch := newestEpoch; epoch >= oldestEpoch; epoch-- { ratifications, haveVerifiers, err := ks.findRatificationsForEpoch(epoch, verifiers) if err != nil { return 0, nil, err } if coname.CheckQuorum(quorum, haveVerifiers) { return epoch, ratifications, nil } } // TODO: (why? ~andreser) return whatever ratification we could find return 0, nil, fmt.Errorf("could not find sufficient verification in the last %d epochs (and not bothering to look further into the past)", ks.laggingVerifierScan) }
// Lookup implements proto.E2EKSLookupServer func (ks *Keyserver) Lookup(ctx context.Context, req *proto.LookupRequest) (*proto.LookupProof, error) { ctx, _ = context.WithTimeout(ctx, ks.clientTimeout) var lookupEpoch uint64 var ratifications []*proto.SignedEpochHead if req.Epoch == 0 { // use the latest epoch possible var err error lookupEpoch, ratifications, err = ks.findLatestEpochSignedByQuorum(req.QuorumRequirement) if err != nil { return nil, err } } else { lookupEpoch = req.Epoch var err error var haveVerifiers map[uint64]struct{} ratifications, haveVerifiers, err = ks.findRatificationsForEpoch(lookupEpoch, coname.ListQuorum(req.QuorumRequirement, nil)) if err != nil { return nil, err } if !coname.CheckQuorum(req.QuorumRequirement, haveVerifiers) { // TODO: return whatever ratification we could find return nil, fmt.Errorf("could not find sufficient verification") } } return ks.assembleLookupProof(req, lookupEpoch, ratifications) }