Bounding Queries Made by a Computation #
This file defines a predicate IsQueryBound oa budget canQuery cost parameterized by:
B— the budget typebudget : B— the initial budgetcanQuery : ι → B → Prop— whether a query to oracletis allowed under budgetbcost : ι → B → B— how the budget is updated after a query to oraclet
The definition is structural via OracleComp.construct: pure satisfies any bound, and
query t >>= mx satisfies the bound when canQuery t b holds and each continuation
satisfies the bound with the updated budget cost t b.
The classical per-index and total query bounds are recovered by IsPerIndexQueryBound
and IsTotalQueryBound.
Generalized query bound parameterized by a budget type, a validity check, and a cost
function. pure satisfies any bound; query t >>= mx satisfies the bound when
canQuery t b and every continuation satisfies the bound at cost t b.
This is the specialization of PFunctor.FreeM.IsRollBound to
OracleComp spec α = FreeM spec.toPFunctor α: an oracle index t : ι plays
the role of a polynomial-functor position, and a query continuation
spec t → OracleComp spec α is the FreeM roll continuation. Most
structural lemmas defer to their FreeM analogues.
Instances For
The bridge to the FreeM-level predicate is Iff.rfl: IsQueryBound is
literally PFunctor.FreeM.IsRollBound on the underlying free monad.
If f <$> oa = ob for any f, the query-bound predicate transfers between them. The
standard shape is oa = (simulateQ wrapped mx).run s and ob = simulateQ inner mx (or
(simulateQ inner mx).run s'), where wrapped threads bookkeeping (a state, a writer log)
that the underlying simulation does not see. The projection equality is supplied by the
corresponding QueryImpl.fst_map_run_* lemma (with f = Prod.fst) or by an auxiliary
projection identity such as withCachingAux_run_proj_eq (with f = Prod.map id Prod.fst).
Project an IsQueryBound along a budget projection proj : B → B'.
If the source bound at budget b validates queries at every step, the projected
bound at proj b is also validated, provided:
h_can— whenever a step is allowed in the source (canQuery t b'), it is allowed in the projection (canQuery' t (proj b'));h_cost— the projection commutes with the cost step on the allowed branch (proj (cost t b') = cost' t (proj b')).
Typical use: extract a single-coordinate query bound (e.g. qS-only) from a
multi-coordinate bound (e.g. (qS, qH) from signHashQueryBound) by setting
proj := Prod.fst.
Generic bind composition for IsQueryBound parameterised by an arbitrary budget type
B and a binary combine operation on it. The natural-number versions
(isTotalQueryBound_bind, isQueryBoundP_bind, isPerIndexQueryBound_bind) are special
cases at combine := (· + ·); the vector-budget cmaSignHashQueryBound_bind uses this
directly with component-wise +.
The two side conditions are universally quantified so they survive recursion under
generalizing b₁:
h_can— extending any validated budget on either side viacombinekeeps the query valid;h_cost—costdistributes left and right overcombineon validated budgets.
Forward-direction seq analogue of isQueryBound_bind. Reduces to the bind case via
seq_eq_bind_map plus isQueryBound_map_iff to discharge the constant continuation.
Transfer a structural query bound through simulateQ into a stateful target semantics,
provided each simulated source query has a target-side step bound and the target-side bind
rule composes those step budgets with the recursive continuation budget.
Predicate-targeted query bound: a middle ground between IsQueryBound and
IsPerIndexQueryBound / IsTotalQueryBound. IsQueryBoundP oa p n says that oa makes at most
n queries to oracle indices satisfying p, with no constraint on the number of queries to
indices where p fails.
This is built on the generic IsQueryBound with the validity check ¬ p t ∨ 0 < qb and the cost
function that decrements the budget only on p-indices.
Instances For
IsQueryBoundP is IsRollBound on the underlying FreeM with the
predicate-targeted validity and cost.
IsQueryBoundP rephrased with the if … then 0 < b else True validity check.
This is the shape that arises naturally in expectedSCost hypotheses, where the
gap between the two implementations is paid only on p-queries.
Projection variant of IsQueryBound.proj that lands directly in
IsQueryBoundP. Given a vector-budget bound on oa, project to a scalar
budget along proj : B → ℕ and conclude an IsQueryBoundP bound at the
projected coordinate. The two side conditions only have to address p-queries:
allowed source steps must keep the projected budget positive when p fires
(h_can), and the projection must commute with the cost step in the shape
that IsQueryBoundP decrements on (h_cost).
Typical use: extract a per-predicate scalar bound (e.g. signing-only qS
from a (qS, qH) pair) without spelling out the if … then 0 < b else True
boilerplate at the call site.
oa >>= ob is p-bounded by n + m when oa is p-bounded by n and every reachable
continuation ob x is p-bounded by m.
Transfer a predicate-targeted query bound through a StateT simulation
whose handler step consumes at most one target-side predicate query exactly when
the source query satisfies the source predicate.
This is the scalar IsQueryBoundP analogue of the more general vector-budget
IsQueryBound.simulateQ_run_of_step. It is useful for logging and forwarding
handlers whose state updates do not make additional oracle queries.
Predicate-extensionality: replacing p with an equivalent predicate does not change the
bound.
Forward-direction seq analogue of isQueryBoundP_bind. Reduces to the bind case via
seq_eq_bind_map plus isQueryBoundP_map_iff to discharge the constant continuation.
Predicate-targeted analogue of isQueryBound_iff_of_map_eq: if f <$> oa = ob for any f,
then IsQueryBoundP transfers between them.
The conjunction of two scalar IsQueryBoundP bounds combines into a vector-budget
IsQueryBound whose canQuery admits a query iff every active predicate has positive
budget, and whose cost decrements only the matching coordinates.
This is the canonical bridge from the predicate-targeted scalar API to the vector-budget API:
proofs that decompose a multi-oracle adversary into per-oracle counts can use the conjunction
form for hypothesis statements while reusing the existing IsQueryBound propagation machinery
(such as simulateQ_run_of_step) on the combined bound. The two predicates need not be
disjoint — at an overlapping query both coordinates decrement and both must be positive,
which exactly tracks the independent per-predicate counts.
Per-index query bound: qb t gives the maximum number of queries to oracle t.
Each query to t decrements qb t by one. Recovers the classical notion.
Instances For
IsPerIndexQueryBound is IsRollBound on the underlying FreeM with the
per-index validity and cost.
Forward-direction seq analogue of isPerIndexQueryBound_bind. Reduces to the bind
case via seq_eq_bind_map plus isPerIndexQueryBound_map_iff to discharge the constant
continuation.
Per-index analogue of isQueryBound_iff_of_map_eq: if f <$> oa = ob for any f, then
IsPerIndexQueryBound transfers between them.
Soundness: structural bound implies dynamic count bound #
The structural query bound IsPerIndexQueryBound is sound with respect to the dynamic
query count produced by countingOracle: if a computation satisfies a per-index query bound,
then every execution path's query count is bounded.
Proof strategy: induction on OracleComp, matching the structural IsQueryBound decomposition
with the mem_support_simulate_queryBind_iff characterization of counting oracle support.
Uniform per-step transfer for simulateQ #
If each step impl t makes at most one query of the matching index t (and none of any
other), the source's per-index bound transfers across simulateQ. Captures the
cachingOracle / seededOracle shape, where each step delegates to a single query t.
Stateless analogue of IsPerIndexQueryBound.simulateQ_run_of_uniform_step: when the
simulation target monad is OracleComp spec directly (no StateT layer), each step's
single-t-query bound transfers without an external state argument.
Total query bounds #
A total query bound: the computation makes at most n queries total
(across all oracle indices).
Instances For
IsTotalQueryBound is IsRollBound on the underlying FreeM with a
single-counter validity (0 < b) and cost (b - 1), independent of the
oracle index.
IsTotalQueryBound instantiates the generic vector-budget bind at B := ℕ,
combine := (· + ·), with the canQuery / cost obligations both discharged by omega.
Forward-direction seq analogue of isTotalQueryBound_bind. Reduces to the bind case
via seq_eq_bind_map plus the IsTotalQueryBound-flavoured isQueryBound_map_iff to
discharge the constant continuation.
If a computation is followed by a continuation that always starts with one query,
then a bound on the whole computation by n + 1 yields a bound on the prefix by n.
Stateless analogue of IsTotalQueryBound.simulateQ_run_of_step: when the simulation
target monad is OracleComp spec' directly (no StateT layer), every per-step bound
applies without an external state argument. Captures the liftComp shape, where each
source query becomes one query in the larger spec.
If oa >>= ob is totally query-bounded by n, then after any support point of the
counting run of oa, the continuation ob is bounded by the residual budget.
Any support point of the counting simulation of a totally query-bounded computation has total query count at most the structural bound.
The counting-oracle simulation of any OracleComp has non-empty support whenever every
oracle range is inhabited. Used by the converse direction of
isTotalQueryBound_iff_counting_total_le.
Converse of IsTotalQueryBound.counting_total_le: a counting-oracle bound on every
support path implies the structural total query bound. Together they characterize
IsTotalQueryBound purely in terms of the counting-oracle support.
If a stateful simulation has support cost at most one per query step, then any support point of the simulated prefix leaves the continuation bounded by the residual budget measured by that cost. The cost may under-approximate the true query count, so the resulting residual budget is correspondingly weaker but still sound.
Per-index bound implies total bound (sum over indices).
Conversions and soundness for IsQueryBoundP #
A total query bound implies a predicate-targeted bound for every predicate p.
With the always-true predicate, IsQueryBoundP reduces to IsTotalQueryBound.
The always-false predicate places no constraint.
A per-index bound implies a predicate-targeted bound at the sum of the per-index budgets
over the indices satisfying p.
Soundness: any path of the counting-oracle simulation of a p-bounded computation has
sum of per-index counts over p-indices at most n.
Residual bound via the counting oracle: after any partial counting-simulation of oa, the
continuation ob is p-bounded by n minus the filtered count so far.
Predicate-targeted analogue of isTotalQueryBound_iff_counting_total_le: a
counting-oracle filtered-sum bound characterizes the structural IsQueryBoundP bound.
Transfer a predicate-targeted query bound through simulateQ into a stateful target
semantics, provided each simulated source query step is itself q-bounded (by 1 on
p-indices, by 0 on ¬ p-indices).
Stateless analogue of IsQueryBoundP.simulateQ_run_of_step: when the simulation target
monad is OracleComp spec' directly (no StateT layer), the per-step bounds apply without
an external state argument. Captures the liftComp shape, where each p-step becomes one
q-query and each ¬ p-step is q-free.
Transfer a predicate-targeted bound through simulateQ with a sum-of-implementations
impl₁ + impl₂ on a sum source spec spec₁ + spec₂. The source predicate p is split into
its .inl and .inr branches, with separate step hypotheses for each impl on its own
sub-predicate.
Specialization of IsQueryBoundP.simulateQ_run_add_of_step when the source predicate
is vacuously false on .inr _ queries: only impl₁ interacts with the predicate, and
impl₂ only needs a uniform 0-bound step.
Specialization of IsQueryBoundP.simulateQ_run_add_of_step when the source predicate
is vacuously false on .inl _ queries: only impl₂ interacts with the predicate, and
impl₁ only needs a uniform 0-bound step.
Total-bound sum-handler transfer #
IsTotalQueryBound lifts across simulateQ (impl₁ + impl₂) oa whenever each underlying
handler makes at most one query per step. The signature mirrors
IsQueryBoundP.simulateQ_run_add_of_step without the predicate machinery: every step on
either side counts toward the same uniform budget.
Specialization of IsTotalQueryBound.simulateQ_run_add_of_step to a left-only
interaction: impl₂ only needs a uniform 0-bound step.
Specialization of IsTotalQueryBound.simulateQ_run_add_of_step to a right-only
interaction: impl₁ only needs a uniform 0-bound step.
Per-index sum-handler transfer #
The per-index analogue of IsTotalQueryBound.simulateQ_run_add_of_step: each side's step
is required to make at most one query of its corresponding sum-tagged index in the result
spec, mirroring the single-spec simulateQ_run_of_uniform_step.
Specialization of IsPerIndexQueryBound.simulateQ_run_add_of_uniform_step to a left-only
interaction: impl₂ only needs a uniform 0-step.
Specialization of IsPerIndexQueryBound.simulateQ_run_add_of_uniform_step to a right-only
interaction: impl₁ only needs a uniform 0-step.
Biconditional transfer under query-count-preserving simulators #
loggingOracle, countingOracle, and the withTrace* / withCost / withCounting
combinators interpret each source query as exactly one underlying query, threading writer
bookkeeping that the underlying simulation does not see. Bounds transfer biconditionally
via the fst_map_run_* projection identities and isXQueryBound_iff_of_map_eq.
Cache-hit / seed-hit handlers (cachingOracle, randomOracle, seededOracle) discard
queries and so admit only the forward direction — use simulateQ_run_of_step for those.
Per-index analogues #
Worst-case per-index query bound as a function of input size:
for all inputs x with size x ≤ n, the computation f x makes at most bound n i
queries to oracle i.
Currently unused outside this file; retained as scaffolding for future asymptotic analyses.
Instances For
Total query upper bound: there exists a constant k such that for all inputs x
with size x ≤ n, the computation f x makes at most k * bound n total queries.
Uses the structural IsQueryBound to avoid dependence on oracle responses.
Currently unused outside this file; retained as scaffolding for future asymptotic analyses.
Instances For
PolyQueryUpperBound says the per-index query count is polynomially bounded
in the input size. This is a non-parameterized version of PolyQueries.
Currently unused outside this file; retained as scaffolding for future asymptotic analyses.
Instances For
If f has a QueryUpperBound, then each f x satisfies IsPerIndexQueryBound.
If oa is a computation indexed by a security parameter, then PolyQueries oa
means that for each oracle index there is a polynomial function qb of the security parameter,
such that the number of queries to that oracle is bounded by the corresponding polynomial.
Currently used only in CostModel.lean; retained as scaffolding for future asymptotic analyses.
- qb : ι → Polynomial ℕ
qb iis a polynomial bound on the queries made to oraclei. - qb_isQueryBound (n : ℕ) (x : α n) : (oa n x).IsPerIndexQueryBound fun (i : ι) => Polynomial.eval n (self.qb i)
The bound is actually a bound on the number of queries made.