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---
204 lines
6.9 KiB
Go
204 lines
6.9 KiB
Go
package keeper
|
|
|
|
// keeper.go holds the store-backed Keeper for the cover module's Cover Pool
|
|
// runtime (REQ-046, REQ-047, REQ-049, REQ-050, REQ-055, D-077, D-086,
|
|
// D-088, D-089).
|
|
//
|
|
// The Keeper wraps an sdk.KVStore via a storeKey. It holds:
|
|
// - the CoverPool records (pool-id -> CoverPool);
|
|
// - the CoverCall records (call-id -> CoverCall; the FileCoverCall
|
|
// handler persists here; P4 adds the Voucher adjudication).
|
|
//
|
|
// The Cover-Fee routing (RouteCoverFee) does NOT persist a separate record
|
|
// in P1 — the routing is the event (the reserve balance update is a
|
|
// simtest-grade stub). P2 may add a CoverFeeRouting record; P1 ships the
|
|
// event-only path.
|
|
//
|
|
// The Keeper also holds the FOUR expected-keeper shims (StandingKeeper for
|
|
// the D-077 gate; WatcherKeeper for the launch attestation; BondKeeper for
|
|
// the P4 MAB check; StillKeeper for the below-floor auto-pause). The shims
|
|
// are interfaces (G-003 — no struct import of x/standing/types,
|
|
// x/watcher/types, x/bond/types, x/still/types); the concrete keepers (or
|
|
// simtest stubs) satisfy them structurally.
|
|
//
|
|
// State-machine ordering (vision §7, enforced in every handler):
|
|
// ValidateBasic -> handler authz/gate -> state mutation -> ctx.EventManager().EmitEvent
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
storetypes "cosmossdk.io/store/types"
|
|
"github.com/cosmos/cosmos-sdk/codec"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
|
|
"github.com/oy/openyield/x/cover/types"
|
|
)
|
|
|
|
// Keeper is the store-backed cover Cover-Pool keeper.
|
|
type Keeper struct {
|
|
cdc codec.Codec
|
|
storeKey storetypes.StoreKey
|
|
standingKeeper types.StandingKeeper
|
|
watcherKeeper types.WatcherKeeper
|
|
bondKeeper types.BondKeeper
|
|
stillKeeper types.StillKeeper
|
|
}
|
|
|
|
// NewKeeper constructs a new store-backed cover Keeper. The four expected-
|
|
// keeper shims are injected (all nil-able for partial tests; the handlers
|
|
// guard nil shims and skip the corresponding check, still mutating state —
|
|
// the simtest wiring documents this). The StandingKeeper gates the launch
|
|
// (D-077); the WatcherKeeper attests the launch (REQ-046); the BondKeeper
|
|
// is held for P4 (the P1 handlers do not call it); the StillKeeper records
|
|
// the below-floor auto-pause (D-089(1)).
|
|
func NewKeeper(cdc codec.Codec, storeKey storetypes.StoreKey, sk types.StandingKeeper, wk types.WatcherKeeper, bk types.BondKeeper, stK types.StillKeeper) Keeper {
|
|
return Keeper{
|
|
cdc: cdc,
|
|
storeKey: storeKey,
|
|
standingKeeper: sk,
|
|
watcherKeeper: wk,
|
|
bondKeeper: bk,
|
|
stillKeeper: stK,
|
|
}
|
|
}
|
|
|
|
// SetStandingKeeper sets the StandingKeeper expected-keeper shim (for
|
|
// post-construction wiring, e.g., app wiring or test setup).
|
|
func (k *Keeper) SetStandingKeeper(sk types.StandingKeeper) { k.standingKeeper = sk }
|
|
|
|
// SetWatcherKeeper sets the WatcherKeeper expected-keeper shim.
|
|
func (k *Keeper) SetWatcherKeeper(wk types.WatcherKeeper) { k.watcherKeeper = wk }
|
|
|
|
// SetBondKeeper sets the BondKeeper expected-keeper shim.
|
|
func (k *Keeper) SetBondKeeper(bk types.BondKeeper) { k.bondKeeper = bk }
|
|
|
|
// SetStillKeeper sets the StillKeeper expected-keeper shim.
|
|
func (k *Keeper) SetStillKeeper(stK types.StillKeeper) { k.stillKeeper = stK }
|
|
|
|
// StoreKey returns the keeper's store key (exported for simtest access to
|
|
// the underlying KVStore, e.g. to inject corrupt bytes for marshal-error
|
|
// coverage). Mirrors the x/hub simtest pattern (the simtest reaches the
|
|
// store via ctx.KVStore(k.StoreKey())).
|
|
func (k Keeper) StoreKey() storetypes.StoreKey { return k.storeKey }
|
|
|
|
// --- CoverPool store ----------------------------------------------------------
|
|
|
|
var poolKeyPrefix = []byte("pool/")
|
|
|
|
func poolKey(poolID string) []byte {
|
|
return append(poolKeyPrefix, []byte(poolID)...)
|
|
}
|
|
|
|
// GetCoverPool loads a CoverPool by pool-id. Returns the pool and true if
|
|
// found, or zero value + false if not.
|
|
func (k Keeper) GetCoverPool(ctx sdk.Context, poolID string) (types.CoverPool, bool) {
|
|
store := ctx.KVStore(k.storeKey)
|
|
bz := store.Get(poolKey(poolID))
|
|
if bz == nil {
|
|
return types.CoverPool{}, false
|
|
}
|
|
var p types.CoverPool
|
|
if err := json.Unmarshal(bz, &p); err != nil {
|
|
return types.CoverPool{}, false
|
|
}
|
|
return p, true
|
|
}
|
|
|
|
// SetCoverPool persists a CoverPool by pool-id.
|
|
func (k Keeper) SetCoverPool(ctx sdk.Context, p types.CoverPool) {
|
|
store := ctx.KVStore(k.storeKey)
|
|
bz, err := json.Marshal(p)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("cover: marshal pool %q: %v", p.PoolID, err))
|
|
}
|
|
store.Set(poolKey(p.PoolID), bz)
|
|
}
|
|
|
|
// AllCoverPools returns all persisted CoverPool records (iteration helper,
|
|
// unordered).
|
|
func (k Keeper) AllCoverPools(ctx sdk.Context) []types.CoverPool {
|
|
store := ctx.KVStore(k.storeKey)
|
|
iterator := store.Iterator(poolKeyPrefix, prefixEnd(poolKeyPrefix))
|
|
defer iterator.Close()
|
|
out := []types.CoverPool{}
|
|
for ; iterator.Valid(); iterator.Next() {
|
|
var p types.CoverPool
|
|
if err := json.Unmarshal(iterator.Value(), &p); err == nil {
|
|
out = append(out, p)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// --- CoverCall store ----------------------------------------------------------
|
|
|
|
var callKeyPrefix = []byte("call/")
|
|
|
|
func callKey(callID string) []byte {
|
|
return append(callKeyPrefix, []byte(callID)...)
|
|
}
|
|
|
|
// GetCoverCall loads a CoverCall by call-id. Returns the call and true if
|
|
// found, or zero value + false if not.
|
|
func (k Keeper) GetCoverCall(ctx sdk.Context, callID string) (types.CoverCall, bool) {
|
|
store := ctx.KVStore(k.storeKey)
|
|
bz := store.Get(callKey(callID))
|
|
if bz == nil {
|
|
return types.CoverCall{}, false
|
|
}
|
|
var c types.CoverCall
|
|
if err := json.Unmarshal(bz, &c); err != nil {
|
|
return types.CoverCall{}, false
|
|
}
|
|
return c, true
|
|
}
|
|
|
|
// SetCoverCall persists a CoverCall by call-id.
|
|
func (k Keeper) SetCoverCall(ctx sdk.Context, c types.CoverCall) {
|
|
store := ctx.KVStore(k.storeKey)
|
|
bz, err := json.Marshal(c)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("cover: marshal call %q: %v", c.CallID, err))
|
|
}
|
|
store.Set(callKey(c.CallID), bz)
|
|
}
|
|
|
|
// AllCoverCalls returns all persisted CoverCall records (iteration helper,
|
|
// unordered).
|
|
func (k Keeper) AllCoverCalls(ctx sdk.Context) []types.CoverCall {
|
|
store := ctx.KVStore(k.storeKey)
|
|
iterator := store.Iterator(callKeyPrefix, prefixEnd(callKeyPrefix))
|
|
defer iterator.Close()
|
|
out := []types.CoverCall{}
|
|
for ; iterator.Valid(); iterator.Next() {
|
|
var c types.CoverCall
|
|
if err := json.Unmarshal(iterator.Value(), &c); err == nil {
|
|
out = append(out, c)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// --- prefixEnd helper ---------------------------------------------------------
|
|
|
|
// prefixEnd returns the key that sorts immediately after all keys sharing
|
|
// the given prefix (the standard prefix-iteration end key: increment the
|
|
// last byte, drop overflow). Used for store.Iterator(start, prefixEnd(start))
|
|
// prefix scans. Mirrors x/hub/keeper/keeper.go.
|
|
func prefixEnd(prefix []byte) []byte {
|
|
if len(prefix) == 0 {
|
|
return nil
|
|
}
|
|
end := make([]byte, len(prefix))
|
|
copy(end, prefix)
|
|
for i := len(end) - 1; i >= 0; i-- {
|
|
end[i]++
|
|
if end[i] != 0 {
|
|
return end
|
|
}
|
|
}
|
|
// All bytes were 0xFF; return nil (iterate to end of store).
|
|
return nil
|
|
}
|