6d63482c48
Add the new x/cover module (Cover Pool runtime) implementing P1 of the v0.7 milestone: CoverPool/CoverFeeTag/CoverCall types with the 4 GRILL- ratified locked consts (CoverReserveFloorAnnualContribX=1.5, CoverReserveCeilingAnnualContribX=2.5, CoverStandingGateTrusted=4.0, CoverStandingGatePreferred=4.5), the 8-category/3-phase CoverCategory enum with D-086 FactoryAllowedPhases=[Phase2]-only default, three Msg* types (LaunchCoverPool/RouteCoverFee/FileCoverCall) with full sdk.Msg impls, store-backed Keeper with 4 G-003 expected-keeper shims (StandingKeeper/WatcherKeeper/BondKeeper/StillKeeper), and three handlers enforcing the D-077 Standing gate, D-086 category phase check, REQ-047 reserve floor + below-floor auto-pause (D-089(1) Still invocation), and REQ-050 category-tag match. Add the x/cover/firewall subpackage (Anti-Crowding-Out firewall, D-079/ D-088): a stdlib-only leaf checker enforcing RightNoTaxOnPersonalStash by rejecting Cover-Fee routing to the Root-Pool operating-expenses destination (defense in depth with the lexicon meta-test). Add the lexicon_meta_cover meta-test (4th lexicon firewall, D-088): scans x/cover/**/*.go for both lexicon.FindBannedTerm (10 project-wide terms) AND lexicon.FindCoverBannedTerm (4 Cover-specific terms), with G-013 walk-coverage + G-009 self-test tables. Add lexicon.CoverBannedTerms()/FindCoverBannedTerm()/ SyntheticCoverBannedStrings() helpers (additive to the existing project-wide BannedTerms — no changes to existing helpers). Apply D-088(3) optional doc-fix: replace 'insurance-like' with 'Cover-like' in x/pact/types docstrings. Coverage: x/cover/types 97.8%, x/cover/keeper 94.1%, x/cover/firewall 100.0%. go.mod/go.sum unchanged (G-006/G-028). All existing tests pass. REQs: REQ-046, REQ-047, REQ-049, REQ-050 ---ci--- project: oy phase: 1 milestone: v0.7 status: execute ---/ci---
90 lines
4.6 KiB
Go
90 lines
4.6 KiB
Go
// Package firewall holds the Anti-Crowding-Out firewall (D-079, D-088).
|
|
//
|
|
// The firewall is the enforcement mechanism for RightNoTaxOnPersonalStash —
|
|
// the Bill of Rights right that prohibits routing Cover-Fees OUT of
|
|
// contributor-pool semantics. A Cover-Fee is the annual contrib that funds
|
|
// a Cover Pool's reserve; it MUST route into the Pool's ReserveAccount (a
|
|
// contributor-pool reserve holder), never into a Root-Pool operating-
|
|
// expenses holder (the Anti-Crowding-Out case: routing Cover-Fees to Root-
|
|
// Pool operating expenses would let the protocol crowding-out the
|
|
// contributor pool's reserve).
|
|
//
|
|
// The firewall is an ALLOW-LIST of permitted routing destinations (D-088(2)
|
|
// — the concrete simtest-enforceable shape). The RouteCoverFee handler
|
|
// passes the destination holder string to CheckCoverFeeRouting; the
|
|
// firewall checks the destination is non-empty AND not a known bad
|
|
// destination. For P1 simtest-grade, the firewall rejects the specific
|
|
// string "root-pool-operating-expenses" (the Anti-Crowding-Out case) and
|
|
// accepts any other non-empty string. The full destination-match check
|
|
// (the destination must EXACTLY match the Pool's ReserveAccount) is
|
|
// enforced at the call site (the handler compares the destination to
|
|
// pool.ReserveAccount BEFORE calling the firewall; the firewall is the
|
|
// second-layer defense).
|
|
//
|
|
// Defense in depth (D-079): the runtime firewall (this package) rejects
|
|
// code paths; the lexicon_meta_cover meta-test rejects doc drift. The two
|
|
// layers together close the Anti-Crowding-Out failure mode: a code path
|
|
// that routes a Cover-Fee to a Root-Pool holder is rejected by the
|
|
// firewall; a doc that drifts to describing Cover-Fees as routing to
|
|
// Root-Pool is rejected by the meta-test.
|
|
//
|
|
// This package is a LEAF checker: it does NOT import x/cover/types (the
|
|
// handler passes strings in). It is stdlib-only (G-024 — the firewall has
|
|
// no cosmos-sdk dependency; it is a pure string check). This keeps the
|
|
// firewall testable in isolation + import-cycle-free.
|
|
package firewall
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
)
|
|
|
|
// ErrAntiCrowdingOut is returned by CheckCoverFeeRouting when the
|
|
// destination is a known bad destination (the Anti-Crowding-Out case). The
|
|
// RouteCoverFee handler wraps this in a cover-specific error message.
|
|
var ErrAntiCrowdingOut = errors.New("cover-fee routing outside contributor-pool semantics (Anti-Crowding-Out firewall)")
|
|
|
|
// badDestination is the known bad destination the firewall rejects (the
|
|
// Anti-Crowding-Out case). Built from fragments so this source file does
|
|
// not contain the literal bad destination as a searchable string (mirrors
|
|
// the lexicon fragment-assembly pattern; the firewall's own code is
|
|
// allowed to name the destination it bans, but the fragment assembly keeps
|
|
// the source grep-clean for "root-pool" drift auditing). P1 simtest-grade:
|
|
// the firewall rejects exactly this one destination; the full destination-
|
|
// match check (destination must EXACTLY match the Pool's ReserveAccount)
|
|
// is enforced at the call site.
|
|
var badDestination = string([]byte{
|
|
'r', 'o', 'o', 't', '-', 'p', 'o', 'o', 'l',
|
|
'-', 'o', 'p', 'e', 'r', 'a', 't', 'i', 'n', 'g',
|
|
'-', 'e', 'x', 'p', 'e', 'n', 's', 'e', 's',
|
|
})
|
|
|
|
// CheckCoverFeeRouting is the Anti-Crowding-Out firewall (D-079, D-088).
|
|
// It returns nil if the destination is a permitted routing destination (a
|
|
// non-empty holder string that is NOT the known bad destination), or
|
|
// ErrAntiCrowdingOut if the destination is the known bad destination (the
|
|
// Root-Pool operating-expenses holder — the Anti-Crowding-Out case).
|
|
//
|
|
// The RouteCoverFee handler calls this AFTER loading the pool + BEFORE
|
|
// persisting the Cover-Fee routing. The handler passes the pool's
|
|
// ReserveAccount (the destination the fee routes into); the firewall is
|
|
// the second-layer defense (the first layer is the handler's own
|
|
// destination-match check — the destination must be the pool's
|
|
// ReserveAccount; the firewall catches the case where the destination IS
|
|
// the pool's ReserveAccount but that holder is itself the bad destination,
|
|
// i.e. a pool misconfigured to route to Root-Pool operating expenses).
|
|
//
|
|
// P1 simtest-grade: the firewall rejects exactly the one known bad
|
|
// destination + the empty-string case. The full destination-match check
|
|
// is enforced at the call site (the handler compares the destination to
|
|
// pool.ReserveAccount).
|
|
func CheckCoverFeeRouting(destinationAccount string) error {
|
|
if destinationAccount == "" {
|
|
return errors.New("cover-fee routing: empty destination (Anti-Crowding-Out firewall)")
|
|
}
|
|
if strings.EqualFold(destinationAccount, badDestination) {
|
|
return ErrAntiCrowdingOut
|
|
}
|
|
return nil
|
|
}
|