docs: async def _get_update(self, rr_builder: Callable, fro: int, to: int, delta: bool) -> (str, int):
          """
          Get rev reg delta/state json, and its timestamp on the distributed ledger,
          from cached rev reg delta/state frames list or distributed ledger,
          updating cache as necessary.
  
          Raise BadRevStateTime if caller asks for a delta/state in the future. Raise ClosedPool
          if an update requires the ledger but the node pool is closed.
  
          Issuer anchors cannot revoke retroactively.
          Hence, for any new request against asked-for interval (fro, to):
          * if the cache has a frame f on f.timestamp <= to <= f.to,
            > return its rev reg delta/state; e.g., starred frame below:
  
            Frames: --------[xxxxx]----[xx]-----[*********]-----[x]-----------[xx]---------> time
            Fro-to:                                ^----^
  
          * otherwise, if there is a maximum frame f with fro <= f.to and f.timestamp <= to
            > return its rev reg delta/state; e.g., starred frame below:
  
            Frames: --------[xxxxx]----[xx]-----[xxxxxxxxx]-----[*]-----------[xx]---------> time
            Fro-to:                  ^----------------------------------^
  
          * otherwise, if the cache has a frame f on f.timestamp < to,
            > check the distributed ledger for a delta to/state for the rev reg since e.timestamp;
              - if there is one, bake it through 'to' into a new delta/state, add new frame to cache and
                return rev reg delta/state; e.g., starred frame below:
  
                Frames: --------[xxxxx]----[xx]-----[xxxxxxxxx]-----[x]-----------[xx]---------> time
                Fro-to:                                                 ^------^
                Ledger: --------[xxxxx]----[xx]-----[xxxxxxxxx]-----[x]--!--------[xx]---------> time
                Update: --------[xxxxx]----[xx]-----[xxxxxxxxx]-----[x]--[*****]--[xx]---------> time
  
              - otherwise, update the 'to' time in the frame and return the rev reg delta/state;
                e.g., starred frame below:
  
                Frames: --------[xxxxx]----[xx]-----[xxxxxxxxx]-----[x]-----------[xx]---------> time
                Fro-to:                                                 ^------^
                Ledger: --------[xxxxx]----[xx]-----[xxxxxxxxx]-----[x]-----------[xx]---------> time
                Update: --------[xxxxx]----[xx]-----[xxxxxxxxx]-----[**********]--[xx]---------> time
  
          * otherwise, there is no cache frame f on f.timestamp < to:
            > create new frame and add it to cache; return rev reg delta/state; e.g., starred frame below:
  
            Frames: --------[xxxxx]----[xx]-----[xxxxxxxxx]-----[*]-----------[xx]-----> time
            Fro-to:   ^--^
            Ledger: -!------[xxxxx]----[xx]-----[xxxxxxxxx]-----[x]-----------[xx]---------> time
            Update: -[***]--[xxxxx]----[xx]-----[xxxxxxxxx]-----[x]-----------[xx]---------> time
  
          On return of any previously existing rev reg delta/state frame, always update its query time beforehand.
  
          :param rr_builder: callback to build rev reg delta/state if need be (specify holder-prover anchor's
              _build_rr_delta_json() or verifier anchor's _build_rr_state_json() as needed)
          :param fro: least time (epoch seconds) of interest; lower-bounds 'to' on frame housing return data
          :param to: greatest time (epoch seconds) of interest; upper-bounds returned revocation delta/state timestamp
          :param delta: True to operate on rev reg deltas, False for states
          :return: rev reg delta/state json and ledger timestamp (epoch seconds)
          """
  
          LOGGER.debug(
              'RevoCacheEntry.get_update >>> rr_builder: %s, fro: %s, to: %s, delta: %s',
              rr_builder.__name__,
              fro,
              to,
              delta)
  
          if fro > to:
              (fro, to) = (to, fro)
  
          now = int(time())
          if to > now:
              LOGGER.debug(
                  'RevoCacheEntry._get_update <!< Cannot query a rev reg %s in the future (%s > %s)',
                  'delta' if delta else 'state',
                  to,
                  now)
              raise BadRevStateTime('Cannot query a rev reg {} in the future ({} > {})'.format(
                  'delta' if delta else 'state',
                  to,
                  now))
  
          cache_frame = None
          rr_update_json = None
          rr_frames = self.rr_delta_frames if delta else self.rr_state_frames
  
          frames = [frame for frame in rr_frames if frame.timestamp <= to <= frame.to]
          if frames:
              cache_frame = max(frames, key=lambda f: f.timestamp)  # should be unique in any case
              # do not update frame.to, it's already past asked-for 'to'
          else:
              frames = [frame for frame in rr_frames if (fro <= frame.to and frame.timestamp <= to)]
              if frames:
                  cache_frame = max(frames, key=lambda f: f.timestamp)
                  # do not update frame.to - another update might occur, but we don't care; fro < frame.to, good enough
          if not frames:
              frames = [frame for frame in rr_frames if frame.timestamp < to]  # frame.to < to since not frames coming in
              if frames:
                  latest_cached = max(frames, key=lambda frame: frame.timestamp)
                  if delta:
                      (rr_update_json, timestamp) = await rr_builder(
                          self.rev_reg_def['id'],
                          to=to,
                          fro=latest_cached.timestamp,
                          fro_delta=latest_cached.rr_update)
                  else:
                      (rr_update_json, timestamp) = await rr_builder(self.rev_reg_def['id'], to)
                  if timestamp == latest_cached.timestamp:
                      latest_cached.to = to  # this timestamp now known good through more recent 'to'
                      cache_frame = latest_cached
              else:
                  (rr_update_json, timestamp) = await rr_builder(self.rev_reg_def['id'], to)
  
          if cache_frame is None:
              cache_frame = RevRegUpdateFrame(to, timestamp, json.loads(rr_update_json))  # sets qtime to now
              rr_frames.append(cache_frame)
              self.cull(delta)
          else:
              cache_frame.qtime = int(time())
  
          rv = (json.dumps(cache_frame.rr_update), cache_frame.timestamp)
          LOGGER.debug('RevoCacheEntry._get_update <<< %s', rv)
          return rv