Skip to main content

vest_lib/combinators/recursive/
proof.rs

1//! Correctness proofs and recursive-body preservation helpers for [`super::FixWith`].
2use super::spec::*;
3use crate::core::{proof::*, spec::*};
4use vstd::prelude::*;
5
6verus! {
7
8/// Functional version of [`SPRoundTripDps`] for parser/serializer callback bundles.
9pub open spec fn sp_roundtrip_dps<T>(
10    parser: ParserFnSpec<T>,
11    consistent: PredFnSpec<T>,
12    byte_len: ByteLenFnSpec<T>,
13    serializer_dps: SerializerDPSFnSpec<T>,
14) -> bool {
15    forall|v: T, obuf: Seq<u8>|
16        consistent(v) ==> #[trigger] parser(serializer_dps(v, obuf)) == Some(
17            (byte_len(v) as int, v),
18        )
19}
20
21/// Functional version of [`NoLookAhead`] for parsers.
22pub open spec fn no_lookahead_parser<T>(parser: ParserFnSpec<T>) -> bool {
23    forall|i1: Seq<u8>, i2: Seq<u8>|
24        (#[trigger] parser(i1) matches Some((n, v)) ==> 0 <= n <= i2.len() ==> i2.take(n)
25            == i1.take(n) ==> #[trigger] parser(i2) == Some((n, v)))
26}
27
28pub open spec fn parser_pair_some<T>(
29    parser: ParserFnSpec<T>,
30    buf1: Seq<u8>,
31    buf2: Seq<u8>,
32) -> Option<((int, T), (int, T))> {
33    match parser(buf1) {
34        Some((n1, v1)) => match parser(buf2) {
35            Some((n2, v2)) => Some(((n1, v1), (n2, v2))),
36            None => None,
37        },
38        None => None,
39    }
40}
41
42/// Functional non-malleability for parser functions.
43pub open spec fn non_malleable_parser<T>(parser: ParserFnSpec<T>) -> bool {
44    forall|buf1: Seq<u8>, buf2: Seq<u8>| #[trigger]
45        parser_pair_some(parser, buf1, buf2) matches Some(((n1, v1), (n2, v2))) ==> v1 == v2
46            ==> buf1.take(n1) == buf2.take(n2)
47}
48
49impl<SpecP, Cnstcy, Blen> NonMalleable for ParserSpecs<SpecP, Cnstcy, Blen> where
50    Blen: SpecByteLen,
51    SpecP: SpecParser<PVal = Blen::T>,
52    Cnstcy: Consistency<Val = Blen::T>,
53 {
54    open spec fn nonmal_inv(&self) -> bool {
55        let (p, _, _) = *self;
56        let p_fn = |ibuf| p.spec_parse(ibuf);
57        non_malleable_parser(p_fn)
58    }
59
60    proof fn lemma_parse_non_malleable(&self, buf1: Seq<u8>, buf2: Seq<u8>) {
61        let (p, _, _) = *self;
62        let p_fn = |ibuf| p.spec_parse(ibuf);
63        if let Some((n1, v1)) = self.spec_parse(buf1) {
64            if let Some((n2, v2)) = self.spec_parse(buf2) {
65                if v1 == v2 {
66                    assert(self.nonmal_inv());
67                    assert(parser_pair_some(p_fn, buf1, buf2) == Some(((n1, v1), (n2, v2))));
68                    assert(non_malleable_parser(p_fn));
69                    assert(buf1.take(n1) == buf2.take(n2));
70                }
71            }
72        }
73    }
74}
75
76impl<T> NonMalleable for BundledSpecs<T> {
77    open spec fn nonmal_inv(&self) -> bool {
78        parser_specs(*self).nonmal_inv()
79    }
80
81    proof fn lemma_parse_non_malleable(&self, buf1: Seq<u8>, buf2: Seq<u8>) {
82        parser_specs(*self).lemma_parse_non_malleable(buf1, buf2);
83    }
84}
85
86impl<T> SPRoundTripDps for BundledSpecs<T> {
87    open spec fn unambiguous(&self) -> bool {
88        let (c, b, p, _, s_dps) = *self;
89        sp_roundtrip_dps(p, c, b, s_dps)
90    }
91
92    proof fn theorem_serialize_dps_parse_roundtrip(&self, v: Self::T, obuf: Seq<u8>) {
93        let (c, b, p, _, s_dps) = *self;
94        assert(sp_roundtrip_dps(p, c, b, s_dps));
95        assert(c(v));
96        assert(p(s_dps(v, obuf)) == Some(((b)(v) as int, v)));
97    }
98}
99
100impl<T> NoLookAhead for BundledSpecs<T> {
101    open spec fn no_lookahead_inv(&self) -> bool {
102        let (_, _, p, _, _) = *self;
103        no_lookahead_parser(p)
104    }
105
106    proof fn lemma_no_lookahead(&self, i1: Seq<u8>, i2: Seq<u8>) {
107        let (_, _, p, _, _) = *self;
108        if let Some((n, v)) = self.spec_parse(i1) {
109            if 0 <= n <= i2.len() {
110                if i2.take(n) == i1.take(n) {
111                    assert(no_lookahead_parser(p));
112                    assert(p(i1) == Some((n, v)));
113                    assert(p(i2) == Some((n, v)));
114                }
115            }
116        }
117    }
118}
119
120impl<T> EquivSerializersGeneral for BundledSpecs<T> {
121    open spec fn equiv_general_inv(&self) -> bool {
122        let (_, _, _, s, s_dps) = *self;
123        forall|v: T, obuf: Seq<u8>| #[trigger] (s_dps)(v, obuf) == (s)(v) + obuf
124    }
125
126    proof fn lemma_serialize_equiv(&self, v: Self::SVal, obuf: Seq<u8>) {
127        let (_, _, _, s, s_dps) = *self;
128        assert((s_dps)(v, obuf) == (s)(v) + obuf);
129    }
130}
131
132impl<T> EquivSerializers for BundledSpecs<T> {
133    open spec fn equiv_inv(&self) -> bool {
134        let (_, _, _, s, s_dps) = *self;
135        forall|v: T| #[trigger] (s_dps)(v, seq![]) == (s)(v) + seq![]
136    }
137
138    proof fn lemma_serialize_equiv_on_empty(&self, v: Self::SVal) {
139        let (_, _, _, s, s_dps) = *self;
140        assert((s_dps)(v, seq![]) == (s)(v) + seq![]);
141    }
142}
143
144/// Non-malleability preservation for recursive bodies.
145pub trait NonMalleableRecBody: SafeParserRecBody + SoundParserRecBody where
146    Self::Body: NonMalleable + SoundParser,
147 {
148    proof fn lemma_body_nonmal_inv_preservation(
149        &self,
150        param: Self::Param,
151        rec: ParamRecSpecs<Self::Param, Self::T>,
152    )
153        requires
154            forall|p: Self::Param| #![trigger rec(p)] rec(p).safe_inv(),
155            forall|p: Self::Param| #![trigger rec(p)] rec(p).sound_inv(),
156            forall|p: Self::Param| #![trigger rec(p)] rec(p).nonmal_inv(),
157        ensures
158            self.spec_body(param, rec).nonmal_inv(),
159    ;
160}
161
162/// DPS serialize-parse roundtrip preservation for recursive bodies.
163/// Similar to [`Star`](crate::combinators::Star), the body must also be [`NonTailFmt`].
164pub trait SPRoundTripDpsRecBody: NonTailFmtRecBody where Self::Body: SPRoundTripDps + NonTailFmt {
165    proof fn lemma_body_sp_roundtrip_dps_inv_preservation(
166        &self,
167        param: Self::Param,
168        rec: ParamRecSpecs<Self::Param, Self::T>,
169    )
170        requires
171            forall|p: Self::Param| #![trigger rec(p)] rec(p).unambiguous(),
172            forall|p: Self::Param| #![trigger rec(p)] rec(p).serialize_dps_inv(),
173        ensures
174            self.spec_body(param, rec).unambiguous(),
175    ;
176}
177
178/// No-lookahead invariant preservation for recursive bodies.
179pub trait NoLookAheadRecBody: SafeParserRecBody where Self::Body: NoLookAhead {
180    proof fn lemma_body_no_lookahead_inv_preservation(
181        &self,
182        param: Self::Param,
183        rec: ParamRecSpecs<Self::Param, Self::T>,
184    )
185        requires
186            forall|p: Self::Param| #![trigger rec(p)] rec(p).no_lookahead_inv(),
187        ensures
188            self.spec_body(param, rec).no_lookahead_inv(),
189    ;
190}
191
192/// Serializer equivalence invariant preservation for recursive bodies.
193pub trait EquivSerializersGeneralRecBody: SpecRecBody where Self::Body: EquivSerializersGeneral {
194    proof fn lemma_s_body_equiv_general_inv_preservation(
195        &self,
196        param: Self::Param,
197        rec: ParamRecSpecs<Self::Param, Self::T>,
198    )
199        requires
200            forall|p: Self::Param| #![trigger rec(p)] rec(p).equiv_general_inv(),
201        ensures
202            self.spec_body(param, rec).equiv_general_inv(),
203    ;
204}
205
206/// Convenience trait bundling the standard recursive-body preservation obligations.
207///
208/// Implementing this once is enough to derive the individual `*RecBody` traits via blanket impls.
209pub trait StrictRecBody: SpecRecBody where Self::Body: StrictCombinator {
210    #[verusfmt::skip]
211    proof fn lemma_body_all_inv_preservation(
212        &self,
213        param: Self::Param,
214        rec: ParamRecSpecs<Self::Param, Self::T>,
215    )
216        ensures
217            (forall|p: Self::Param| #![trigger rec(p)] rec(p).safe_inv())
218            ==> self.spec_body(param, rec).safe_inv(),
219
220            (forall|p: Self::Param| #![trigger rec(p)] rec(p).productive_inv())
221            ==> self.spec_body(param, rec).productive_inv(),
222
223            (forall|p: Self::Param| #![trigger rec(p)] rec(p).sound_inv())
224            ==> self.spec_body(param, rec).sound_inv(),
225
226            (forall|p: Self::Param| #![trigger rec(p)] rec(p).safe_inv())
227            && (forall|p: Self::Param| #![trigger rec(p)] rec(p).sound_inv())
228            && (forall|p: Self::Param| #![trigger rec(p)] rec(p).nonmal_inv())
229            ==> self.spec_body(param, rec).nonmal_inv(),
230
231            (forall|p: Self::Param| #![trigger rec(p)] rec(p).serialize_inv())
232            ==> self.spec_body(param, rec).serialize_inv(),
233
234            (forall|p: Self::Param| #![trigger rec(p)] rec(p).serialize_dps_inv())
235            ==> self.spec_body(param, rec).serialize_dps_inv(),
236
237            (forall|p: Self::Param| #![trigger rec(p)] rec(p).unambiguous())
238            && (forall| p: Self::Param| #![trigger rec(p)] rec(p).serialize_dps_inv())
239            ==> self.spec_body(param, rec).unambiguous(),
240
241            (forall|p: Self::Param| #![trigger rec(p)] rec(p).equiv_general_inv())
242            ==> self.spec_body(param, rec).equiv_general_inv(),
243    ;
244}
245
246impl<Body: StrictRecBody> SafeParserRecBody for Body where Body::Body: StrictCombinator {
247    proof fn lemma_body_safe_inv_preservation(
248        &self,
249        param: Self::Param,
250        rec: ParamRecSpecs<Self::Param, Self::T>,
251    ) {
252        self.lemma_body_all_inv_preservation(param, rec);
253        assert(self.spec_body(param, rec).safe_inv());
254    }
255}
256
257impl<Body: StrictRecBody> ProductiveRecBody for Body where Body::Body: StrictCombinator {
258    proof fn lemma_body_productive_inv_preservation(
259        &self,
260        param: Self::Param,
261        rec: ParamRecSpecs<Self::Param, Self::T>,
262    ) {
263        self.lemma_body_all_inv_preservation(param, rec);
264        assert(self.spec_body(param, rec).productive_inv());
265    }
266}
267
268impl<Body: StrictRecBody> SoundParserRecBody for Body where Body::Body: StrictCombinator {
269    proof fn lemma_body_sound_inv_preservation(
270        &self,
271        param: Self::Param,
272        rec: ParamRecSpecs<Self::Param, Self::T>,
273    ) {
274        self.lemma_body_all_inv_preservation(param, rec);
275        assert(self.spec_body(param, rec).sound_inv());
276    }
277}
278
279impl<Body: StrictRecBody> NonMalleableRecBody for Body where Body::Body: StrictCombinator {
280    proof fn lemma_body_nonmal_inv_preservation(
281        &self,
282        param: Self::Param,
283        rec: ParamRecSpecs<Self::Param, Self::T>,
284    ) {
285        self.lemma_body_all_inv_preservation(param, rec);
286        assert(self.spec_body(param, rec).nonmal_inv());
287    }
288}
289
290impl<Body: StrictRecBody> GoodSerializerRecBody for Body where Body::Body: StrictCombinator {
291    proof fn lemma_s_body_serialize_inv_preservation(
292        &self,
293        param: Self::Param,
294        rec: ParamRecSpecs<Self::Param, Self::T>,
295    ) {
296        self.lemma_body_all_inv_preservation(param, rec);
297        assert(self.spec_body(param, rec).serialize_inv());
298    }
299}
300
301impl<Body: StrictRecBody> NonTailFmtRecBody for Body where Body::Body: StrictCombinator {
302    proof fn lemma_s_body_dps_serialize_dps_inv_preservation(
303        &self,
304        param: Self::Param,
305        rec: ParamRecSpecs<Self::Param, Self::T>,
306    ) {
307        self.lemma_body_all_inv_preservation(param, rec);
308        assert(self.spec_body(param, rec).serialize_dps_inv());
309    }
310}
311
312impl<Body: StrictRecBody> SPRoundTripDpsRecBody for Body where Body::Body: StrictCombinator {
313    proof fn lemma_body_sp_roundtrip_dps_inv_preservation(
314        &self,
315        param: Self::Param,
316        rec: ParamRecSpecs<Self::Param, Self::T>,
317    ) {
318        self.lemma_body_all_inv_preservation(param, rec);
319        assert(self.spec_body(param, rec).unambiguous());
320    }
321}
322
323impl<Body: StrictRecBody> EquivSerializersGeneralRecBody for Body where
324    Body::Body: StrictCombinator,
325 {
326    proof fn lemma_s_body_equiv_general_inv_preservation(
327        &self,
328        param: Self::Param,
329        rec: ParamRecSpecs<Self::Param, Self::T>,
330    ) {
331        self.lemma_body_all_inv_preservation(param, rec);
332        assert(self.spec_body(param, rec).equiv_general_inv());
333    }
334}
335
336impl<const LIMIT: usize, Body, Param> super::FixWith<LIMIT, Body, Param> where
337    Body: NonMalleableRecBody,
338    Body::Body: NonMalleable + SoundParser,
339    Param: DeepView<V = Body::Param>,
340 {
341    /// Inductive proof that `spec_parse_gas` is non-malleable.
342    #[verusfmt::skip]
343    proof fn non_malleable_by_induction(
344        &self,
345        gas: nat,
346        param: Body::Param,
347        buf1: Seq<u8>,
348        buf2: Seq<u8>,
349        n1: int,
350        n2: int,
351        v1: Body::T,
352        v2: Body::T,
353    )
354        ensures
355            Self::spec_parse_gas(&self.0, gas, param, buf1) == Some((n1, v1)) ==>
356            Self::spec_parse_gas(&self.0, gas, param, buf2) == Some((n2, v2)) ==>
357            v1 == v2 ==> buf1.take(n1) == buf2.take(n2),
358        decreases gas,
359    {
360        if !(Self::spec_parse_gas(&self.0, gas, param, buf1) == Some((n1, v1))) {
361            return ;
362        }
363        if !(Self::spec_parse_gas(&self.0, gas, param, buf2) == Some((n2, v2))) {
364            return ;
365        }
366        if !(v1 == v2) {
367            return ;
368        }
369
370        let callback = Self::specs_callback(&self.0, gas);
371        let callback_p = callback(param).2;
372        let callback_c = callback(param).0;
373        let callback_b = callback(param).1;
374
375        assert forall|p: Body::Param, rem: Seq<u8>| #[trigger]
376            callback(p).2(rem) matches Some((nn, _vv)) ==> 0 <= nn <= rem.len() by {
377            if let Some((nn, vv)) = callback(p).2(rem) {
378                self.safe_parser_by_induction((gas - 1) as nat, p, rem, nn, vv);
379            }
380        }
381
382        assert forall|p: Body::Param, rem: Seq<u8>| #[trigger]
383            callback(p).2(rem) matches Some((nn, vv)) ==> {
384                &&& callback(p).0(vv)
385                &&& callback(p).1(vv) == nn
386            } by {
387            if let Some((nn, vv)) = callback(p).2(rem) {
388                self.sound_parser_by_induction((gas - 1) as nat, p, rem, nn, vv);
389            }
390        }
391
392        assert forall|p: Body::Param, rem1: Seq<u8>, rem2: Seq<u8>| #[trigger]
393            parser_pair_some(callback(p).2, rem1, rem2) matches Some(((nn1, vv1), (nn2, vv2)))
394                ==> vv1 == vv2 ==> rem1.take(nn1) == rem2.take(nn2) by {
395            if let Some(((nn1, vv1), (nn2, vv2))) = parser_pair_some(callback(p).2, rem1, rem2) {
396                if vv1 == vv2 {
397                    self.non_malleable_by_induction((gas - 1) as nat, p, rem1, rem2, nn1, nn2, vv1, vv2);
398                }
399            }
400        }
401
402        assert forall|p: Body::Param| #[trigger] callback(p).safe_inv() by {
403            assert(callback(p).safe_inv());
404        }
405        assert forall|p: Body::Param| #[trigger] callback(p).sound_inv() by {
406            assert(callback(p).sound_inv());
407        }
408        assert forall|p: Body::Param| #[trigger] callback(p).nonmal_inv() by {
409            let p_fn = |ibuf: Seq<u8>| callback(p).2.spec_parse(ibuf);
410            assert(p_fn == callback(p).2);
411            assert(callback(p).nonmal_inv());
412        }
413
414        self.0.lemma_body_safe_inv_preservation(param, callback);
415        self.0.lemma_body_nonmal_inv_preservation(param, callback);
416        let body = self.0.spec_body(param, callback);
417
418        body.lemma_parse_non_malleable(buf1, buf2);
419
420        assert(Self::spec_parse_gas(&self.0, gas, param, buf1) == body.spec_parse(buf1));
421        assert(Self::spec_parse_gas(&self.0, gas, param, buf2) == body.spec_parse(buf2));
422    }
423}
424
425impl<const LIMIT: usize, Body, Param> NonMalleable for super::FixWith<LIMIT, Body, Param> where
426    Body: NonMalleableRecBody,
427    Body::Body: NonMalleable + SafeParser + SoundParser,
428    Param: DeepView<V = Body::Param>,
429 {
430    proof fn lemma_parse_non_malleable(&self, buf1: Seq<u8>, buf2: Seq<u8>) {
431        if let Some((n1, v1)) = self.spec_parse(buf1) {
432            if let Some((n2, v2)) = self.spec_parse(buf2) {
433                if v1 == v2 {
434                    self.non_malleable_by_induction(
435                        LIMIT as nat,
436                        self.1.deep_view(),
437                        buf1,
438                        buf2,
439                        n1,
440                        n2,
441                        v1,
442                        v2,
443                    );
444                }
445            }
446        }
447    }
448}
449
450impl<const LIMIT: usize, Body, Param> super::FixWith<LIMIT, Body, Param> where
451    Body: SPRoundTripDpsRecBody + NonTailFmtRecBody,
452    Body::Body: SPRoundTripDps + NonTailFmt,
453    Param: DeepView<V = Body::Param>,
454 {
455    proof fn sp_roundtrip_dps_by_induction(
456        &self,
457        gas: nat,
458        param: Body::Param,
459        v: Body::T,
460        obuf: Seq<u8>,
461    )
462        requires
463            Self::consistent_gas(&self.0, gas, param, v),
464        ensures
465            Self::spec_parse_gas(
466                &self.0,
467                gas,
468                param,
469                Self::spec_serialize_dps_gas(&self.0, gas, param, v, obuf),
470            ) == Some((Self::byte_len_gas(&self.0, gas, param, v) as int, v)),
471        decreases gas,
472    {
473        let callback = Self::specs_callback(&self.0, gas);
474
475        assert forall|p: Body::Param, vv: Body::T, buf: Seq<u8>|
476            (callback(p).0(vv)) implies #[trigger] callback(p).2(callback(p).4(vv, buf)) == Some(
477            (callback(p).1(vv) as int, vv),
478        ) by {
479            if callback(p).0(vv) {
480                self.sp_roundtrip_dps_by_induction((gas - 1) as nat, p, vv, buf);
481            }
482        }
483
484        assert forall|p: Body::Param, vv: Body::T, buf: Seq<u8>| #[trigger]
485            callback(p).4(vv, buf).len() - buf.len() == callback(p).1(vv) by {
486            if gas > 0 {
487                self.nontail_dps_by_induction((gas - 1) as nat, p, vv, buf);
488            } else {
489                assert(callback(p).4(vv, buf) == buf);
490                assert(callback(p).1(vv) == 0);
491            }
492        }
493
494        assert forall|p: Body::Param, vv: Body::T, buf: Seq<u8>|
495            exists|new_buf: Seq<u8>| (#[trigger] callback(p).4(vv, buf)) == new_buf + buf by {
496            if gas > 0 {
497                self.nontail_dps_by_induction((gas - 1) as nat, p, vv, buf);
498                let witness = choose|w: Seq<u8>|
499                    Self::spec_serialize_dps_gas(&self.0, (gas - 1) as nat, p, vv, buf) == w + buf;
500                assert(callback(p).4(vv, buf) == witness + buf);
501            } else {
502                assert(callback(p).4(vv, buf) == Seq::<u8>::empty() + buf);
503            }
504        }
505
506        assert forall|p: Body::Param| #[trigger] callback(p).unambiguous() by {
507            assert(callback(p).unambiguous());
508        }
509        assert forall|p: Body::Param| #[trigger] callback(p).serialize_dps_inv() by {
510            assert(callback(p).serialize_dps_inv());
511        }
512
513        self.0.lemma_body_sp_roundtrip_dps_inv_preservation(param, callback);
514        let body = self.0.spec_body(param, callback);
515
516        assert(Self::consistent_gas(&self.0, gas, param, v) == body.consistent(v));
517
518        body.theorem_serialize_dps_parse_roundtrip(v, obuf);
519
520        assert(Self::spec_parse_gas(
521            &self.0,
522            gas,
523            param,
524            Self::spec_serialize_dps_gas(&self.0, gas, param, v, obuf),
525        ) == body.spec_parse(body.spec_serialize_dps(v, obuf)));
526        assert(Self::byte_len_gas(&self.0, gas, param, v) == body.byte_len(v));
527    }
528}
529
530impl<const LIMIT: usize, Body, Param> SPRoundTripDps for super::FixWith<LIMIT, Body, Param> where
531    Body: SPRoundTripDpsRecBody + NonTailFmtRecBody,
532    Body::Body: SPRoundTripDps + NonTailFmt,
533    Param: DeepView<V = Body::Param>,
534 {
535    proof fn theorem_serialize_dps_parse_roundtrip(&self, v: Self::T, obuf: Seq<u8>) {
536        self.sp_roundtrip_dps_by_induction(LIMIT as nat, self.1.deep_view(), v, obuf);
537    }
538}
539
540impl<const LIMIT: usize, Body, Param> super::FixWith<LIMIT, Body, Param> where
541    Body: EquivSerializersGeneralRecBody,
542    Body::Body: EquivSerializersGeneral,
543    Param: DeepView<V = Body::Param>,
544 {
545    proof fn equiv_serializers_general_by_induction(
546        &self,
547        gas: nat,
548        param: Body::Param,
549        v: Body::T,
550        obuf: Seq<u8>,
551    )
552        ensures
553            Self::spec_serialize_dps_gas(&self.0, gas, param, v, obuf) == Self::spec_serialize_gas(
554                &self.0,
555                gas,
556                param,
557                v,
558            ) + obuf,
559        decreases gas,
560    {
561        let callback = Self::specs_callback(&self.0, gas);
562
563        assert forall|p: Body::Param, vv: Body::T, buf: Seq<u8>| #[trigger]
564            callback(p).4(vv, buf) == callback(p).3(vv) + buf by {
565            if gas > 0 {
566                self.equiv_serializers_general_by_induction((gas - 1) as nat, p, vv, buf);
567            } else {
568                assert(callback(p).4(vv, buf) == buf);
569                assert(callback(p).3(vv) == Seq::<u8>::empty());
570                assert(callback(p).4(vv, buf) == callback(p).3(vv) + buf);
571            }
572        }
573
574        assert forall|p: Body::Param| #[trigger] callback(p).equiv_general_inv() by {
575            assert(callback(p).equiv_general_inv());
576        }
577        self.0.lemma_s_body_equiv_general_inv_preservation(param, callback);
578        let body = self.0.spec_body(param, callback);
579
580        body.lemma_serialize_equiv(v, obuf);
581
582        assert(Self::spec_serialize_dps_gas(&self.0, gas, param, v, obuf)
583            == body.spec_serialize_dps(v, obuf));
584        assert(Self::spec_serialize_gas(&self.0, gas, param, v) == body.spec_serialize(v));
585    }
586}
587
588impl<const LIMIT: usize, Body, Param> EquivSerializersGeneral for super::FixWith<
589    LIMIT,
590    Body,
591    Param,
592> where
593    Body: EquivSerializersGeneralRecBody,
594    Body::Body: EquivSerializersGeneral,
595    Param: DeepView<V = Body::Param>,
596 {
597    proof fn lemma_serialize_equiv(&self, v: Self::SVal, obuf: Seq<u8>) {
598        self.equiv_serializers_general_by_induction(LIMIT as nat, self.1.deep_view(), v, obuf);
599    }
600}
601
602impl<const LIMIT: usize, Body, Param> EquivSerializers for super::FixWith<LIMIT, Body, Param> where
603    Body: EquivSerializersGeneralRecBody,
604    Body::Body: EquivSerializersGeneral,
605    Param: DeepView<V = Body::Param>,
606 {
607    proof fn lemma_serialize_equiv_on_empty(&self, v: Self::SVal) {
608        self.lemma_serialize_equiv(v, Seq::empty());
609    }
610}
611
612impl<const LIMIT: usize, Body, Param> super::FixWith<LIMIT, Body, Param> where
613    Body: NoLookAheadRecBody,
614    Body::Body: NoLookAhead,
615    Param: DeepView<V = Body::Param>,
616 {
617    proof fn no_lookahead_by_induction(
618        &self,
619        gas: nat,
620        param: Body::Param,
621        i1: Seq<u8>,
622        i2: Seq<u8>,
623        n: int,
624        v: Body::T,
625    )
626        requires
627            Self::spec_parse_gas(&self.0, gas, param, i1) == Some((n, v)),
628            0 <= n <= i2.len(),
629            i2.take(n) == i1.take(n),
630        ensures
631            Self::spec_parse_gas(&self.0, gas, param, i2) == Some((n, v)),
632        decreases gas,
633    {
634        let callback = Self::specs_callback(&self.0, gas);
635
636        assert forall|p: Body::Param, rem: Seq<u8>| #[trigger]
637            callback(p).2(rem) matches Some((nn, _vv)) ==> 0 <= nn <= rem.len() by {
638            if let Some((nn, vv)) = callback(p).2(rem) {
639                self.safe_parser_by_induction((gas - 1) as nat, p, rem, nn, vv);
640            }
641        }
642
643        assert forall|p: Body::Param, j1: Seq<u8>, j2: Seq<u8>|
644            (#[trigger] callback(p).2(j1) matches Some((nn, vv)) ==> 0 <= nn <= j2.len()
645                ==> j2.take(nn) == j1.take(nn) ==> #[trigger] callback(p).2(j2) == Some(
646                (nn, vv),
647            )) by {
648            if let Some((nn, vv)) = callback(p).2(j1) {
649                if 0 <= nn && nn <= j2.len() && j2.take(nn) == j1.take(nn) {
650                    self.no_lookahead_by_induction((gas - 1) as nat, p, j1, j2, nn, vv);
651                }
652            }
653        }
654
655        assert forall|p: Body::Param| #[trigger] callback(p).safe_inv() by {
656            assert(callback(p).safe_inv());
657        }
658        assert forall|p: Body::Param| #[trigger] callback(p).no_lookahead_inv() by {
659            assert(callback(p).no_lookahead_inv());
660        }
661
662        self.0.lemma_body_safe_inv_preservation(param, callback);
663        self.0.lemma_body_no_lookahead_inv_preservation(param, callback);
664        let body = self.0.spec_body(param, callback);
665
666        assert(Self::spec_parse_gas(&self.0, gas, param, i1) == body.spec_parse(i1));
667        assert(Self::spec_parse_gas(&self.0, gas, param, i2) == body.spec_parse(i2));
668
669        body.lemma_no_lookahead(i1, i2);
670
671        assert(body.spec_parse(i1) == Some((n, v)));
672        assert(body.spec_parse(i2) == Some((n, v)));
673    }
674}
675
676impl<const LIMIT: usize, Body, Param> NoLookAhead for super::FixWith<LIMIT, Body, Param> where
677    Body: NoLookAheadRecBody,
678    Body::Body: NoLookAhead,
679    Param: DeepView<V = Body::Param>,
680 {
681    proof fn lemma_no_lookahead(&self, i1: Seq<u8>, i2: Seq<u8>) {
682        if let Some((n, v)) = self.spec_parse(i1) {
683            if 0 <= n <= i2.len() {
684                if i2.take(n) == i1.take(n) {
685                    self.no_lookahead_by_induction(LIMIT as nat, self.1.deep_view(), i1, i2, n, v);
686                }
687            }
688        }
689    }
690}
691
692} // verus!