|
| 1 | +# feat(consensus): tipset gas reservations and reservation‑aware mpool pre‑pack |
| 2 | + |
| 3 | +This PR wires Lotus into the engine‑managed tipset gas reservation design described in `AGENTS.md` (Option A), and adds an optional reservation‑aware mempool pre‑pack pass. The goal is to eliminate miner‑charged underfunded messages in a tipset while preserving receipts and gas outputs. |
| 4 | + |
| 5 | +## Summary |
| 6 | + |
| 7 | +- Add NV‑gated Begin/End orchestration around explicit tipset messages using the new FVM reservation session API. |
| 8 | +- Build a per‑sender reservation plan in consensus (`Σ(gas_limit * gas_fee_cap)`), deduped by CID across blocks. |
| 9 | +- Enforce strict vs non‑strict behaviour pre‑activation via explicit feature flags, with ref‑fvm remaining network‑version agnostic. |
| 10 | +- Add an optional reservation‑aware mpool pre‑pack heuristic that avoids constructing blocks which would fail reservations. |
| 11 | +- Expose plan‑level metrics and operator documentation. |
| 12 | + |
| 13 | +This branch assumes matching `multistage-execution` branches in `ref-fvm` (engine reservations) and `filecoin-ffi` (Begin/End FFI) are present. |
| 14 | + |
| 15 | +## Changes |
| 16 | + |
| 17 | +### Consensus and activation |
| 18 | + |
| 19 | +- **`chain/consensus/features.go`** |
| 20 | + - Introduce `ReservationFeatureFlags` with: |
| 21 | + - `MultiStageReservations` (best‑effort pre‑activation enablement). |
| 22 | + - `MultiStageReservationsStrict` (pre‑activation behaviour: legacy fallback vs tipset‑invalidating). |
| 23 | + - Provide `Feature` globals with env‑based defaults: |
| 24 | + - `LOTUS_ENABLE_TIPSET_RESERVATIONS` |
| 25 | + - `LOTUS_ENABLE_TIPSET_RESERVATIONS_STRICT` |
| 26 | + |
| 27 | +- **`chain/consensus/reservations.go`** |
| 28 | + - Implement `ReservationsEnabled(nv)`: |
| 29 | + - `nv >= ReservationsActivationNetworkVersion()` → always true (reservations required). |
| 30 | + - Pre‑activation → controlled by `Feature.MultiStageReservations`. |
| 31 | + - Implement `buildReservationPlan(bms []FilecoinBlockMessages) map[address.Address]abi.TokenAmount`: |
| 32 | + - Deduplicate messages by CID across all blocks in the tipset. |
| 33 | + - Aggregate `gas_limit * gas_fee_cap` per sender. |
| 34 | + - Implement `startReservations` / `endReservations`: |
| 35 | + - Skip empty plans (no Begin/End). |
| 36 | + - Call `vmi.StartTipsetReservations` / `EndTipsetReservations` when enabled. |
| 37 | + - Record `metrics.ReservationPlanSenders` and `metrics.ReservationPlanTotal`. |
| 38 | + - Use `handleReservationError` to decide, pre‑activation: |
| 39 | + - Always fallback for `ErrReservationsNotImplemented`. |
| 40 | + - Always surface node‑error classes (session misuse, overflow, invariant violations). |
| 41 | + - For `ErrReservationsInsufficientFunds` / `ErrReservationsPlanTooLarge`: |
| 42 | + - Non‑strict: log and fall back to legacy mode. |
| 43 | + - Strict: surface as tipset errors. |
| 44 | + - Post‑activation: all Begin/End errors surface; `ErrReservationsNotImplemented` becomes a node error. |
| 45 | + |
| 46 | +- **`chain/consensus/compute_state.go`** |
| 47 | + - After constructing the VM, wrap explicit message application with: |
| 48 | + - `startReservations(ctx, vmi, bms, nv)` (before applying any explicit messages). |
| 49 | + - A `defer`ed `endReservations(ctx, vmi, nv)` plus an explicit `endReservations` call before cron to scope reservations to explicit messages only. |
| 50 | + - Existing message, reward, and cron flows are otherwise unchanged. |
| 51 | + |
| 52 | +### VM interface and FVM wiring |
| 53 | + |
| 54 | +- **`chain/vm/vmi.go` & `chain/vm/execution.go`** |
| 55 | + - Extend `vm.Interface` with: |
| 56 | + - `StartTipsetReservations(ctx context.Context, plan map[address.Address]abi.TokenAmount) error` |
| 57 | + - `EndTipsetReservations(ctx context.Context) error` |
| 58 | + - Implement `vmExecutor.StartTipsetReservations` / `EndTipsetReservations` by: |
| 59 | + - Acquiring an execution lane token (consistent with `ApplyMessage` / `ApplyImplicitMessage`). |
| 60 | + - Forwarding the call to the underlying VM implementation. |
| 61 | + |
| 62 | +- **`chain/vm/fvm.go`** |
| 63 | + - Re‑export typed reservation errors from `filecoin-ffi` and define: |
| 64 | + - `ReservationsActivationNetworkVersion()` (currently `network.Version28`). |
| 65 | + - Implement: |
| 66 | + - `func (vm *FVM) BeginReservations(plan []byte) error` |
| 67 | + - `func (vm *FVM) EndReservations() error` |
| 68 | + - `func (vm *FVM) StartTipsetReservations(ctx context.Context, plan map[address.Address]abi.TokenAmount) error` |
| 69 | + - `func (vm *FVM) EndTipsetReservations(ctx context.Context) error` |
| 70 | + - Behaviour: |
| 71 | + - CBOR encode the reservation plan as `[[address_bytes, amount_bytes], ...]`. |
| 72 | + - Call `ffi.FVM.BeginReservations` / `ffi.FVM.EndReservations`, which return typed errors potentially wrapped with short engine‑provided messages from ref‑fvm. |
| 73 | + - Track `reservationsActive` to avoid calling End when no Begin occurred (empty plan or legacy fallback). |
| 74 | + - Pre‑activation: |
| 75 | + - Treat `ErrReservationsNotImplemented` as a benign signal and fall back to legacy mode. |
| 76 | + - Surface other errors; consensus decides fallback vs invalidity via `handleReservationError`. |
| 77 | + - Post‑activation: |
| 78 | + - Treat `ErrReservationsNotImplemented` as a node error (engine too old). |
| 79 | + |
| 80 | +- **`chain/vm/vm.go`** |
| 81 | + - Implement no‑op `StartTipsetReservations` / `EndTipsetReservations` on `LegacyVM` to satisfy the interface; reservations are only supported in FVM mode. |
| 82 | + |
| 83 | +- **`chain/vm/fvm_reservations_test.go`** |
| 84 | + - Tests for: |
| 85 | + - `reservationStatusToError` mapping from raw status codes to typed errors. |
| 86 | + - CBOR plan encode/decode round‑trip. |
| 87 | + - Validation (duplicate sender and invalid entry length). |
| 88 | + |
| 89 | +### Mempool reservation‑aware pre‑pack (optional) |
| 90 | + |
| 91 | +- **`chain/types/mpool.go` & `chain/messagepool/config.go`** |
| 92 | + - Add `EnableReservationPrePack` to `MpoolConfig`, default `false`. |
| 93 | + - Document it as advisory-only pre‑pack simulation that shares the same activation gating as tipset reservations. |
| 94 | + |
| 95 | +- **`chain/messagepool/selection.go`** |
| 96 | + - Extend `selectedMessages` with: |
| 97 | + - `reservationEnabled`, `reservationCtx`, `reservationTipset`. |
| 98 | + - `reservedBySender` (`Σ(cap×limit)` per sender for selected messages). |
| 99 | + - `balanceBySender` (sender balances at the selection base tipset). |
| 100 | + - Add: |
| 101 | + - `reservationsPrePackEnabled() bool`: |
| 102 | + - Uses current tipset height + 1 to derive the NV at which the selected messages will execute. |
| 103 | + - Requires both `EnableReservationPrePack` and `consensus.ReservationsEnabled(nextNv)`. |
| 104 | + - `initReservations(ctx, ts, mp)` to initialize tracking maps when enabled. |
| 105 | + - `reserveForMessages(sender, msgs)` to: |
| 106 | + - Lazily load the sender’s balance at the selection base state via `mp.api.StateGetActor`/`GetActorAfter` and cache it. |
| 107 | + - Accumulate Σ(cap×limit) for candidate chains and compare against the sender’s balance. |
| 108 | + - Skip/disable heuristics on lookup failure to avoid partial behaviour. |
| 109 | + - Use `reserveForMessages` from `tryToAdd`/`tryToAddWithDeps` to: |
| 110 | + - Prune or trim chains that would over‑commit the sender at the selection base state. |
| 111 | + - Keep behaviour advisory only; consensus still enforces reservations at Begin/End. |
| 112 | + |
| 113 | +- **`chain/messagepool/selection_test.go`** |
| 114 | + - `TestReservationPrePackPrunesOverCommittedChain`: |
| 115 | + - Ensures chains whose Σ(cap×limit) exceeds the sender balance are invalidated and not added. |
| 116 | + - `TestReservationPrePackAccumulatesCapTimesLimit`: |
| 117 | + - Ensures Σ(cap×limit) is correctly accumulated per sender across multiple chains. |
| 118 | + |
| 119 | +### Metrics and docs |
| 120 | + |
| 121 | +- **`metrics/metrics.go`** |
| 122 | + - Add: |
| 123 | + - `ReservationPlanSenders` (`vm/reservations_plan_senders`) – number of unique senders in the plan. |
| 124 | + - `ReservationPlanTotal` (`vm/reservations_plan_total_atto`) – total reserved Σ(cap×limit) in attoFIL. |
| 125 | + - Register views and attach them to the default node view set. |
| 126 | + |
| 127 | +- **`documentation/en/tipset-reservations.md`** |
| 128 | + - Document: |
| 129 | + - Activation at `network.Version28` / `UpgradeXxHeight`. |
| 130 | + - Pre‑activation env/feature gating, including Strict vs non‑Strict behaviours. |
| 131 | + - Advisory nature of mempool pre‑pack. |
| 132 | + - Plan‑level metrics and logging for observability. |
| 133 | + |
| 134 | +- **`CHANGELOG.md`** |
| 135 | + - Add UNRELEASED entry summarizing: |
| 136 | + - Tipset gas reservations. |
| 137 | + - Reservation‑aware mpool pre‑pack. |
| 138 | + - NV28 activation and receipts/gas‑output invariants. |
| 139 | + |
| 140 | +## Activation & gating |
| 141 | + |
| 142 | +- Ref‑fvm remains network‑version agnostic; all activation logic lives in Lotus. |
| 143 | +- **Pre‑activation (NV < 28)**: |
| 144 | + - `Feature.MultiStageReservations` = false → legacy mode (no Begin/End, no pre‑pack). |
| 145 | + - `Feature.MultiStageReservations` = true, `MultiStageReservationsStrict` = false: |
| 146 | + - Begin/End attempted; reservation failures (insufficient funds, plan too large) are best‑effort and cause a fallback to legacy for that tipset. |
| 147 | + - `Feature.MultiStageReservations` = true, `MultiStageReservationsStrict` = true: |
| 148 | + - Reservation failures invalidate the tipset pre‑activation (mirroring post‑activation), except `ErrReservationsNotImplemented`, which still triggers legacy fallback. |
| 149 | +- **Post‑activation (NV ≥ 28)**: |
| 150 | + - `ReservationsEnabled` always true. |
| 151 | + - Begin/End errors surface to consensus; `ErrReservationsNotImplemented` is treated as a node error (engine too old). |
| 152 | + |
| 153 | +## Testing |
| 154 | + |
| 155 | +- Unit tests: |
| 156 | + - `chain/consensus/reservations_test.go` – plan build, env/feature gating, error handling paths. |
| 157 | + - `chain/vm/fvm_reservations_test.go` – status‑to‑error mapping, CBOR plan encode/decode. |
| 158 | + - `chain/messagepool/selection_test.go` – reservation pre‑pack heuristics. |
| 159 | +- Targeted `go test` runs (already exercised locally on this branch): |
| 160 | + - `go test ./chain/consensus -run TestReservation` |
| 161 | + - `go test ./chain/vm -run TestReservation` |
| 162 | + - `go test ./chain/messagepool -run TestReservation` |
| 163 | + |
| 164 | +## Notes |
| 165 | + |
| 166 | +- This PR depends on: |
| 167 | + - `ref-fvm` `multistage-execution` (engine reservation session + enforcement). |
| 168 | + - `filecoin-ffi` `multistage-execution` (Begin/End FFI + error messages). |
| 169 | +- Receipts (`ExitCode`, `GasUsed`, events) and `GasOutputs` remain unchanged; the reservation ledger is internal to the engine. |
| 170 | + |
0 commit comments