///|
/// Advance exactly one authenticated delegation. No network lookup or implicit trust.
pub fn delegate_keys(
  parent : TrustedKeys,
  child : Name,
  ds_records : Array[Record],
  ds_signatures : Array[Rrsig],
  key_records : Array[Record],
  key_signatures : Array[Rrsig],
  time : TimePolicy,
  limits : Limits,
) -> Result[TrustedKeys, DnsError] {
  match limits.validate() {
    Ok(_) => ()
    Err(e) => return Err(e)
  }
  if parent.depth >= limits.max_chain {
    return Err(ResourceLimit("delegation chain depth"))
  }
  if child == parent.zone || !child.is_subdomain(parent.zone) {
    return Err(Bogus("child must be below the authenticated parent zone"))
  }
  if ds_records.is_empty() {
    return Err(Indeterminate("no DS: require authenticated denial instead"))
  }
  for rr in ds_records {
    if rr.owner != child || rr.rrtype != 43 {
      return Err(Malformed("expected child DS RRset"))
    }
  }
  let ds_set = match
    authenticate_rrset(ds_records, ds_signatures, parent, time, limits) {
    Ok(s) => s
    Err(e) => return Err(e)
  }
  if ds_set.wildcard {
    return Err(Bogus("DS delegation cannot be wildcard-expanded"))
  }
  let keys = match keys_from_set(key_records, child, limits) {
    Ok(k) => k
    Err(e) => return Err(e)
  }
  if keys.length() > limits.max_records / ds_records.length() {
    return Err(ResourceLimit("DS/key association budget"))
  }
  if key_signatures.is_empty() ||
    key_signatures.length() > limits.max_signatures ||
    keys.length() > limits.max_signatures / key_signatures.length() {
    return Err(ResourceLimit("child signature/key budget"))
  }
  let ds : Array[Ds] = []
  for rr in ds_records {
    match Ds::parse(rr.data) {
      Ok(d) => ds.push(d)
      Err(e) => return Err(e)
    }
  }
  let mut supported_ds = false
  let mut matched = false
  let mut failure : DnsError = Bogus("no DNSKEY matches supported parent DS")
  for key in keys {
    let mut accepted = false
    for d in ds {
      if d.digest_type == 2 && supported_algorithm(d.algorithm) {
        supported_ds = true
        match d.matches(child, key) {
          Ok(true) => {
            accepted = true
            matched = true
          }
          Ok(false) => ()
          Err(e) => return Err(e)
        }
      }
    }
    if !accepted {
      continue
    }
    match
      authenticate_with_key(
        key_records, key_signatures, child, key, time, limits,
      ) {
      Err(e) => failure = e
      Ok(set) =>
        return Ok({
          zone: child,
          keys,
          verified_at: time.now,
          lifetime: if set.lifetime < ds_set.lifetime {
            set.lifetime
          } else {
            ds_set.lifetime
          },
          depth: parent.depth + 1,
        })
    }
  }
  if !supported_ds {
    return Err(Unsupported("no enabled DS digest/algorithm pair"))
  }
  if !matched {
    return Err(Bogus("parent DS does not bind supplied DNSKEY"))
  }
  Err(failure)
}