Write: /home/abhishek/fv-workspace-inner/monad/monadproofs/proofs/dipped_into_reserve_proof_sketch.md
{
"file_path": "/home/abhishek/fv-workspace-inner/monad/monadproofs/proofs/dipped_into_reserve_proof_sketch.md",
"content": "# Proof sketch: `dipped_into_reserve` ⊨ `dipped_into_reserve_spec`\n\nTarget C++: `monad/category/execution/monad/reserve_balance.cpp`, function\n`dipped_into_reserve` (lines 41–109).\n\nTarget spec: `exec_specs.v`, `dipped_into_reserve_spec` (lines 3975–4026).\n\nModel: `reservebal.v` (`finalBalSufficient` 551–560, `allFinalBalSufficient`\n562–564). Per the user, the model of record is `reservebal.v` (the\n`Require Import … reservebalold` at `exec_specs.v:19` is stale and should be\nre-pointed at `monad.proofs.reservebal`).\n\nThis is a sketch only — Coq-level statements are given, but routine separation\n-logic plumbing (loadSpec/`go`, type-ptr observations, `wp_for` boilerplate)\nis described rather than written.\n\n--------------------------------------------------------------------------------\n## 0. Standing assumptions and known gaps (read first)\n\nThese are stated up front because they change what is provable.\n\n**A0 (asserts assumed).** Every `MONAD_ASSERT(cond)` is given the\n`monad_assertion_failed_spec` whose precondition is `[| False |]`\n(`exec_specs.v:3103`). Operationally this means: at each assert site the proof\n*may assume* `cond` holds (the failure branch is dead). We do not prove the\nasserts independently. Two asserts matter:\n - line 47–49: index/size well-formedness of `ctx` — assumed.\n - line 94–97: `violation_threshold.has_value()` in the sender / cannot-dip\n branch, i.e. `gas_fees ≤ reserve`. This is **load-bearing** (see §5, Step D,\n sender case). Without it the truncated `N`-subtraction in the model would\n not line up with the `optional` threshold in C++.\n\n**A1 (staking balance).** The staking contract `0x1000`\n(`StakingContractAddr = 4096`, `reservebal.v:546`) is assumed to always hold\n`balance ≥ 10 MON = DefReserve` (`exec_specs.v:803`,\n`DefReserve = 10 * 10^18`). This discharges the `a = StakingContractAddr`\ndisjunct of `finalBalSufficient` (see §5, Staking sub-case).\n\n**A2 (isSC discrepancy — known, not a safety issue).** The C++ decides\n\"this account is a contract, skip it\" from the **original/pre-tx** code hash\n(`orig_code_hash`, lines 57–69), whereas `reservebal.v`'s `finalBalSufficient`\nuses `isSC postTxState a` — the **post-tx** state. These do not coincide; see\n§5 Step A. The exact equality in the post-condition therefore fails on exactly\nthe accounts where pre-state and post-state contract-ness differ. This is the\ndocumented, accepted gap; the safety-relevant direction still holds (§7).\n\n**A3 (`nonDelegatedCodeExecAccounts` argument).** `reservebal.v`'s\n`finalBalSufficient`/`allFinalBalSufficient` take a\n`nonDelegatedCodeExecAccounts : list EvmAddr` argument that the 5-argument call\nin the current spec text omits. `dipped_into_reserve` has no \"executed code\"\nset of its own (it only does the original-code-hash skip), so when the spec is\nre-pointed at `reservebal.v` this argument is instantiated to `[]`. The skip it\nwould have provided is folded into the isSC discussion of A2/§5 Step A.\n\n--------------------------------------------------------------------------------\n## 1. The goal, restated precisely\n\n`dipped_into_reserve_spec` (`exec_specs.v:3975`) reduces, after consuming the\narg/prepost frame, to the Hoare triple \"running the body from `StateR st`\nreturns `Vbool retb` and leaves\":\n\n```\nExists (updates: gmap evm.address AssumptionExactness),\n let stf := update_assum_exactness_state st updates in\n statep |-> StateR stf\n ** [| updates_stricter st updates |]\n ** [| forall preTxState : StateOfAccounts,\n satisfiesAssumptions stf preTxState ->\n retb = negb (allFinalBalSufficient 3 (preTxState, hist)\n (applyUpdates stf preTxState)\n (map fst (newStates st)) (* [] -- A3 *) tx) |]\n```\n\nWrite throughout:\n - `chg := map fst (newStates st)` (the changed-account keys; this is exactly\n what `state.current()` iterates, by `StateR` `exec_specs.v:1660` →\n `MapCurrentR (newStates st)`),\n - `R_pess(a) := N.min DefReserve (original_balance_pessimistic_model_map\n (preTxAssumedState st…) a)` (the C++ `reserve`),\n - `fee := maxTxFee tx` (= C++ `gas_fees`; equal by §3 / `validTx`),\n - for a concrete `preTxState`:\n - `R(a) := N.min DefReserve (balanceOfAc preTxState.1 a)` (model `erb`),\n - `cur(a) := balanceOfAc (applyUpdates stf preTxState) a` (model post bal),\n - `spec(a):= current_balance_pessimistic_model_stack (stack of a)` — the\n speculative recent balance the C++ reads via `stack.recent().account_`.\n\nBecause `allFinalBalSufficient = forallb (finalBalSufficient …) chg`\n(`reservebal.v:564`) and the C++ returns on the **first** dipping account, the\nwhole proof is the conjunction of:\n\n - **(Reduction)** `retb = true ⟺ ∃ a ∈ chg. ¬finalBalSufficient(a)` and\n `retb = false ⟺ ∀ a ∈ chg. finalBalSufficient(a)`, for every\n `preTxState ⊨ satisfiesAssumptions stf`; and\n - **(Per-account)** for each processed `a`, a bidirectional match between the\n C++ decision `dippedAt(a)` and `¬finalBalSufficient(a)` (§5).\n\nNote (early return is sound): if C++ returns `true` at account `a_k`, `updates`\nonly records the tightenings for `a_1..a_k`. That suffices: we exhibit\n`¬finalBalSufficient(a_k)` for **all** `preTxState ⊨ stf`, which already makes\n`forallb … = false`, independent of `a_{k+1}…`.\n\n--------------------------------------------------------------------------------\n## 2. Fee equality (`gas_fees = maxTxFee tx`)\n\nC++ (line 51): `gas_fees = uint256{tx.gas_limit} * gas_price(rev, tx, base_fee)`.\nSpec hypothesis `validTx tx` (`exec_specs.v:924`) gives\n`maxTxFee tx = tx_gas_limit tx.1.1 * gas_price_cpp_model tx.1 (tx_base_fee …)`\nwith `gas_limit < 2^64`, `gas_price < 2^128`, hence the product `< 2^192 < 2^256`\n— no `uint256_t` overflow. The `gas_price` callee spec returns\n`gas_price_cpp_model …`. ⇒ **Lemma F:** `gas_fees = maxTxFee tx` as `N`, and the\nmultiplication never wraps. (Local obligation: thread `validTx`'s bounds through\nthe `uint256_t::operator*` spec.)\n\n--------------------------------------------------------------------------------\n## 3. The two reconciliation lemmas (the heart of the proof)\n\nThe C++ never sees the concrete `preTxState`. It computes with the **pessimistic\noriginal balance** and the **speculative current balance**, and *records\nassumptions* so that, on every `preTxState` consistent with them, its decision\nequals the model's. The post-condition's `∀ preTxState ⊨ stf` quantifier is\ndischarged entirely by these two lemmas.\n\n### Lemma 1 (original reserve is pinned: `R_pess(a) = R(a)`)\n\nThe threshold lambda (C++ lines 72–85) is specified by\n`dipped_into_reserve_lam_call_spec` (`exec_specs.v:3274–3293`):\n - it returns `reserve_violation_threshold_model orig a sender gas_fees`\n (`exec_specs.v:3200–3208`), and\n - it updates the original map to\n `check_min_original_balance_update orig a DefReserve`\n (`exec_specs.v:3211–3217`), i.e.\n `update_assum_exactness_at a (fun ex => min_balance_update ex orig_bal\n orig_bal DefReserve) orig`, with `orig_bal := R_pess`'s pre-min argument\n `original_balance_pessimistic_model_map orig a`.\n\nReading off `min_balance_update old orig cur debit` (`exec_specs.v:117–138`)\nwith `cur = orig = orig_bal`, `debit = DefReserve`:\n\n - if `DefReserve ≤ orig_bal` (`check_min_balance_ok`): the new `min_balance`\n is `max(old_min, orig_bal - (orig_bal - DefReserve)) = max(old_min,\n DefReserve)`, i.e. the assumption now forces `actual_orig_bal ≥ DefReserve`.\n - if `DefReserve > orig_bal`: `min_balance := None` — switch to **exact**\n balance validation, forcing `actual_orig_bal = orig_bal`.\n\nNow take any `preTxState ⊨ satisfiesAssumptions stf`\n(`exec_specs.v:2688–2693`), which unfolds at `a` to\n`satAccountNonStorageAssumptions … (assumptionOfAddr (preTxAssumedState stf) a)\n (Some (preTxState.1 a))` (`2644`). In both regimes:\n\n - `DefReserve ≤ orig_bal` ⇒ `min_balance = Some(≥DefReserve)` ⇒\n `balanceOfAc preTxState.1 a ≥ DefReserve` ⇒\n `R(a) = min(DefReserve, balanceOfAc…) = DefReserve = min(DefReserve,\n orig_bal) = R_pess(a)`.\n - `DefReserve > orig_bal` ⇒ exact ⇒ `balanceOfAc preTxState.1 a = orig_bal`\n ⇒ `R(a) = min(DefReserve, orig_bal) = orig_bal = R_pess(a)`.\n\n⇒ **Lemma 1:** for every `preTxState ⊨ stf`, `R(a) = R_pess(a)`, and likewise\nthe model `fee > R(a)` ⟺ C++ `gas_fees > reserve`. In particular\n`reserve_violation_threshold_model` (which is `None` iff `fee > R_pess(a)` in\nthe sender case, else `R_pess(a) - fee`; else `R_pess(a)`) computes the **model**\nthreshold exactly. (This is also why A0/A2's `gas_fees ≤ reserve` assert ties to\nthe model's `N`-truncated `R(a) - fee`.)\n\nCaveat to formalize: `original_balance_pessimistic_model_map` is evaluated on\nthe *running* `orig` (which grows as `state.original_account_state` materializes\nabsent accounts — `state_original_account_state_post`, `exec_specs.v:1502`), not\nthe literal initial `preTxAssumedState st`. The loop invariant (§4) must carry\n\"the `orig` threaded through equals `preTxAssumedState` of the current `stf`\nrestricted to processed accounts\", so that the final `updates` satisfies\n`updates_stricter st updates` (`exec_specs.v:3495`) — each per-account\n`min_balance_update` is an `assumption_exactness_stricter` step\n(`min_balance_stricter`, `3482`; tightening `Some m → Some(max m _)` or\n`Some _ → None`).\n\n### Lemma 2 (current balance transfers: `spec(a) ⋚ θ ⟺ cur(a) ⋚ θ`)\n\nThe C++ reads `curr_balance = stack.recent().account_.balance` (lines 86–88) =\n`spec(a)` (`current_balance_pessimistic_model_stack`, `exec_specs.v:2184`;\n`recentAccountOf_stack`, `2178`). The model uses\n`cur(a) = balanceOfAc (applyUpdates stf preTxState) a`.\n\nUnfold `applyUpdates`/`accountFinalVal` (`exec_specs.v:2717–2754`) at `a` (which\n*has* updates, since `a ∈ chg`), with `csUpdated := recent post-tx account`\n(balance `spec(a)`), `csAssumed := preTxState assumed` (balance the assumed pre\n= `orig_bal`), `csActual := preTxState.1 a` (balance `balanceOfAc preTxState.1 a`):\n\n - **exact regime** (`relaxedValidation = false`, or `min_balance = None`):\n `accountFinalVal` returns `base`, whose balance is `spec(a)` directly ⇒\n `cur(a) = spec(a)`. The comparison transfers trivially.\n - **relaxed regime** (`min_balance = Some m`): balance becomes\n `postTxActualBalNonce` (`exec_specs.v:2698`):\n `cur(a) = balanceOfAc preTxState.1 a + (spec(a) - orig_bal)`\n (`Z`/`N` care: this is the speculative *delta* re-based on the actual pre).\n By Lemma 1, on `preTxState ⊨ stf` we have `balanceOfAc preTxState.1 a` either\n `= orig_bal` (exact-orig regime) — then `cur(a) = spec(a)` — or\n `≥ DefReserve ≥ R(a) = R_pess(a)` with `min_balance ≥ DefReserve`. In the\n latter regime the recorded `min_balanceN` (a lower bound the subtraction-time\n `min_balance_update` maintains, `exec_specs.v:2139–2154`,\n `state_subtract_from_balance_post`) guarantees `cur(a) ≥` the same threshold\n that the comparison tests, so `spec(a) < θ ⟺ cur(a) < θ` for the relevant\n `θ ∈ {R(a), R(a) - fee}`.\n\n⇒ **Lemma 2:** for every `preTxState ⊨ stf` and `θ ∈ {R(a), R(a) − fee}`,\n`spec(a) < θ ⟺ cur(a) < θ`, and `θ ≤ spec(a) ⟺ θ ≤ cur(a)`.\n\nCaveat to formalize: this is the most delicate arithmetic step; the `Z↔N`\nre-basing in `postTxActualBalNonce` and the exact bookkeeping of `min_balanceN`\nacross the *whole* speculative tx (not just one subtraction) is where the bulk\nof the eventual Coq effort sits. The statement above is what must be proven; the\ninvariant of `state_subtract_from_balance_post` / `sliceInvariants`\n(`exec_specs.v:1565`) is the tool.\n\n--------------------------------------------------------------------------------\n## 4. Loop set-up and invariant\n\nThe body is `for (auto const &[addr, stack] : state.current()) { … }`\n(lines 54–107). `state.current()` is the `current_` field, modelled by\n`MapCurrentR (newStates st)` inside `StateR` (`exec_specs.v:1660`). Use the\nrepository's `wp_for` over the ankerl map spine (the iteration enumerates the\nkeys `chg = map fst (newStates st)` in some order; order is irrelevant since the\ntarget is `forallb`/`∃`).\n\n**Loop invariant** (over the processed prefix `P ⊆ chg`, with current original\nmap `origP` and accumulated `updatesP`):\n\n1. **(resources)** `statep |-> StateR (state_with_preTxAssumedState st origP)`\n with the `current_`/`block_state_`/code fields unchanged; `ctxp`, `senderp`,\n `txp`, `basefeep`, `NULL_HASH` prepost frames returned intact.\n2. **(assumptions tightened)** `origP = (preTxAssumedState st)` updated by\n `updatesP` on the keys in `P`, and `updates_stricter st updatesP`.\n3. **(no-dip-yet flag)** a ghost boolean `found`:\n - `found = false` ⇒ for all `a ∈ P` and all `preTxState ⊨\n state_with_preTxAssumedState st origP`, `finalBalSufficient(a) = true`;\n - `found = true` ⇒ the function has already `return`ed `true` and there is a\n witness `a_k ∈ P` with `¬finalBalSufficient(a_k)` for all such `preTxState`.\n4. **(unprocessed untouched)** for `a ∉ P`, `origP` agrees with the initial map,\n so processing them later still starts from the right assumptions.\n\nAt loop entry `P = ∅`, `updatesP = ∅` (`updates_stricter_emp`, `exec_specs.v:3510`),\n`found = false`. At loop exit (no early return) `P = chg`, `found = false`, giving\n`∀ a ∈ chg. finalBalSufficient(a)` ⇒ `allFinalBalSufficient = true` ⇒\n`retb = false = negb true`. Early return sets `found = true` and `retb = true`;\ninvariant clause 3 supplies the `∃`-witness ⇒ `allFinalBalSufficient = false` ⇒\n`negb false = true = retb`. Either way the final `updates := updatesP` satisfies\nthe post-condition with `stf := update_assum_exactness_state st updates`.\n\n--------------------------------------------------------------------------------\n## 5. One iteration: C++ branch ⟷ `finalBalSufficient` (the core case split)\n\nFix the iteration's account `a` with version stack `stack`. Let\n`fbs(a) := finalBalSufficient (preTxState, hist) (applyUpdates stf preTxState)\n [] tx a` (model, `reservebal.v:551`); recall `hist` satisfies\n`configuredReserveBal (hist a) = DefReserve` (spec hyp, `exec_specs.v:3988`), so\n`configuredReserveBalOfAddr hist a = DefReserve` and the model's `ReserveBal` is\n`DefReserve`, hence the model `erb = R(a)`.\n\n### Step A — the EOA/contract skip (lines 57–69) ⟷ `isSC` disjunct\n\nC++: `orig_code_hash = NULL_HASH` unless the *original* account has code; if it\nhas code and `is_delegated(code)` is false → `continue` (skip `a`).\nUsing `reserve_balance_bytes32_eq_spec` (`3043`), `intercode_size_spec` (`3119`),\nthe `is_delegated` spec, and `isDelegationMarker` (`reservebal.v:235`:\n`length = 23 ∧ marker_prefix`), the skip predicate is exactly\n\n```\nskip_cpp(a) ⟺ isAcSC (orig_account a) (reservebal.v:240)\n ⟺ isSC PRE_state a (PRE = original/pre-tx)\n```\n\nThe model's first disjunct is `isSC postTxState a || a ∈ [] || a = staking`\n(A3 sets the middle list to `[]`).\n\n**This is the A2 gap.** `skip_cpp` uses **PRE**, the model uses **POST**:\n - EOA-in-pre / contract-in-post (e.g. `reservebal.v:586–591`: tx deploys+empties\n a fresh `addr2`): C++ does *not* skip (checks balance, may report dip);\n model *does* skip (`fbs = true`). C++ stricter.\n - contract-in-pre / EOA-in-post (selfdestruct): C++ skips; model checks.\n\nOn all accounts where pre- and post-contract-ness agree (the overwhelming\nmajority — code is created/destroyed only by the executing tx), `skip_cpp(a) ⟺\nisSC postTxState a`, and the iteration matches exactly. On the disagreement set,\nthe equality is replaced by the safe one-sided bound of §7. We proceed assuming\nagreement (A2) for the exact-equality argument; if `skip_cpp(a)` (and `a` skipped)\nthe iteration contributes `fbs(a) = true` and `found` is unchanged.\n\nWhen **not** skipped (`a` is an EOA or a delegated EOA in PRE), continue to B–D.\n\n### Step B — the threshold lambda (lines 72–85) ⟷ `R(a)`, `fee`\n\nBy the lambda call spec + Lemma 1, the returned `optional<uint256> θ_cpp` equals\n`reserve_violation_threshold_model orig a sender gas_fees`, and the original map\ngains the `min_balance_update … DefReserve` tightening (this is the per-account\ncontribution to `updatesP`; clause 2 of the invariant). Concretely, with\n`R := R(a) = R_pess(a)` and `fee = maxTxFee tx`:\n - `a = sender`: `θ_cpp = None` iff `fee > R`, else `Some (R − fee)`;\n - `a ≠ sender`: `θ_cpp = Some R`.\n\n### Step C — read the current balance (lines 86–88) ⟷ `cur(a)`\n\n`curr_balance = spec(a)`; by **Lemma 2** every comparison below transfers to\n`cur(a) = balanceOfAc (applyUpdates stf preTxState) a`.\n\n### Step D — the violation test (lines 89–106) ⟷ sender/non-sender branches\n\nC++ flags a dip exactly when `(θ_cpp = None ∨ curr_balance < θ_cpp)` and then,\n\n**Non-sender (`a ≠ sender`, lines 102–105).** `θ_cpp = Some R` (never `None`),\nso dip ⟺ `spec(a) < R`. The `MONAD_ASSERT(θ.has_value())` is trivially true.\nModel branch (`reservebal.v:560`, `a ≠ sender`, not skipped, not staking):\n`fbs(a) = asbool (R ≤ cur(a))`, so `¬fbs(a) ⟺ cur(a) < R`. With Lemma 2:\n\n```\ndippedAt(a) ⟺ spec(a) < R ⟺ cur(a) < R ⟺ ¬fbs(a). ✓ (exact)\n```\n\n**Sender (`a = sender`, lines 91–101).** Reached the inner test means\n`θ_cpp = None ∨ curr_balance < θ_cpp`.\n - `can_sender_dip_into_reserve(...)` is specified by\n `can_sender_dip_into_reserve_spec` (`exec_specs.v:3167–3197`) to equal\n `isAllowedToEmpty 3 (preTxState, hist) [] tx` =\n `isAllowedToEmptyExec (preTxState, hist) tx` (`reservebal.v:457`). Its\n `delegated`/history hypotheses are met by the spec's `historyConsistent`\n hyp and the delegation hypothesis at `3991–4013` (the post-state\n delegation-marker bookkeeping). Call this boolean `dip_ok`.\n - If `dip_ok = true`: C++ does **not** report a dip (lines 100, \"skip\"); model\n `fbs(a) = true` (`reservebal.v:559`, `isAllowedToEmptyExec` true branch).\n Match: `¬dippedAt(a)` and `fbs(a)`. ✓\n - If `dip_ok = false`: C++ hits the `MONAD_ASSERT(θ.has_value())` (line 94).\n By **A0** we assume `θ_cpp = Some (R − fee)` (i.e. `fee ≤ R`). Then C++\n reports dip ⟺ `curr_balance < R − fee` ⟺ (Lemma 2) `cur(a) < R − fee`.\n Model (`reservebal.v:559`, `dip_ok` false branch): `fbs(a) =\n asbool ((R − fee) ≤ cur(a))` with **`N`-truncated** subtraction. Because\n `fee ≤ R` (A0), the truncation is inert (`R − fee` is the true difference),\n so `¬fbs(a) ⟺ cur(a) < R − fee`. Hence\n\n```\ndippedAt(a) ⟺ cur(a) < R − fee ⟺ ¬fbs(a). ✓ (modulo A0)\n```\n\n (Without A0, the `θ_cpp = None` path would let C++ report a dip while the\n model's truncated `R − fee = 0 ≤ cur(a)` says `fbs = true`; A0 declares that\n path unreachable.)\n\n### Staking sub-case (`a = StakingContractAddr`)\n\nThe C++ has **no** special case for `0x1000`. Two ways it is handled:\n - `0x1000` is a genuine contract in PRE ⇒ Step A skips it (`skip_cpp`),\n matching the model's `a = staking` disjunct (`fbs = true`). This is the\n normal case.\n - Even if reached: by **A1**, `spec(0x1000) ≥ DefReserve ≥ R`, so the\n non-sender test `spec(a) < R` is false ⇒ `¬dippedAt`, and the model's\n `a = staking` disjunct gives `fbs = true`. Consistent.\n\n--------------------------------------------------------------------------------\n## 6. Assembling the post-condition\n\nRun the loop with the §4 invariant; each iteration is §5. On exit set\n`updates := updatesP`, `stf := update_assum_exactness_state st updates`.\n\n - `statep |-> StateR stf`: from invariant clause 1 (the only field mutated is\n `original_`, exactly `update_assum_exactness_state`'s effect — note\n `update_assum_exactness_state` only rewrites `preTxAssumedState`,\n `exec_specs.v:3469–3480`; `newStates`, `block_state_`, code maps unchanged,\n matching the C++ which never writes `current_` here).\n - `updates_stricter st updates`: invariant clause 2, accumulated from the\n per-account `min_balance_update` steps (each an `assumption_exactness_stricter`\n step; compose with transitivity of `min_balance_stricter`).\n - the `∀ preTxState ⊨ stf` equality: §1 Reduction + §5 per-account, every step\n of which was proven \"for all `preTxState ⊨ stf`\" via Lemmas 1–2.\n\n∎ (modulo A0, A1, A2.)\n\n--------------------------------------------------------------------------------\n## 7. What is actually provable vs. the stated equality (honesty about A2)\n\nAgainst `reservebal.v`'s `finalBalSufficient` (post-state `isSC`), the\npost-condition's **exact equality** `retb = negb (allFinalBalSufficient …)`\nholds for every `preTxState ⊨ stf` **except** on accounts in the pre/post\ncontract-ness disagreement set (§5 Step A). There the achievable, and\nsafety-relevant, statement is the one-sided bound:\n\n```\nallFinalBalSufficient 3 (preTxState,hist) (applyUpdates stf preTxState) chg [] tx = true\n ⇒ retb = false (* C++ accepts ⇒ model accepts *)\n```\n\nequivalently `retb = true ⇒ ∃ a ∈ chg. ¬finalBalSufficient(a)` is *weakened*\ninto \"C++ may additionally revert EOA-turned-contract accounts the model would\nhave waved through\". This is the \"C++ is slightly less permissive (reverts\nmore)\" property the user flagged: it only ever causes *extra* reverts, never a\nwrongful acceptance, so block-level soundness (`fullBlockStep`, `reservebal.v:680`)\n— which only needs \"accepted ⇒ balances sufficient\" — is preserved.\n\nTo recover a literal exact equality (if desired later), change `reservebal.v`'s\n`finalBalSufficient` first disjunct from `isSC postTxState a` to `isSC\npreTxState.1 a` (i.e. test the pre/original state, matching the C++), or add the\nEOA-turned-contract addresses to the `nonDelegatedCodeExecAccounts` argument\n(A3). Either makes Step A an exact iff and removes the A2 caveat.\n\n--------------------------------------------------------------------------------\n## 8. Checklist of Coq obligations (for formalization)\n\n1. **Lemma F** (§2): fee equality + no `uint256_t` overflow from `validTx`.\n2. **Lemma 1** (§3): `R_pess(a) = R(a)` for `preTxState ⊨ stf`, from\n `min_balance_update`/`satAccountNonStorageAssumptions`; both regimes.\n3. **Lemma 2** (§3): speculative→applied balance comparison transfer; the\n `Z↔N` re-basing in `postTxActualBalNonce` and `min_balanceN` bookkeeping —\n *the heaviest step*.\n4. **skip_cpp characterization** (§5 A): `is_delegated`/`intercode_size`/code-hash\n ⟹ `isAcSC (orig_account a)`; and the A2 pre/post bridge lemma (+ its caveat).\n5. **`can_sender_dip` ↔ `isAllowedToEmptyExec`** (§5 D): discharge the callee\n spec's `historyConsistent`/delegation hypotheses from the outer spec hyps.\n6. **Loop framing** (§4): `wp_for` over `MapCurrentR`; carry the invariant;\n `updates_stricter` accumulation (transitivity).\n7. **A0 assert discharge** (§5 D sender): assume `θ_cpp.has_value()`.\n8. **Final assembly** (§6): `StateR stf` reconstruction; the `∀ preTxState`\n post-condition.\n"
}