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 guildKeeper types.GuildKeeper // paramsOverride is a simtest-grade Params override (nil = use // DefaultParams). A future P2+ will load the Params from the params // store; for now the handler uses DefaultParams unless an override is // set via SetParamsOverride (the D-086 simtest case (f) uses this to // restrict FactoryAllowedPhases to [Phase2, Phase3] only and reject a // Phase4 launch). paramsOverride *types.Params } // 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)). The P5 GuildKeeper verifies a // Guild exists on Pier selection (REQ-066; a nil shim skips the check). 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 } // SetGuildKeeper sets the GuildKeeper expected-keeper shim (P5 — REQ-066 // Pier Selection uses this to verify the selecting Guild exists; a nil // shim skips the existence check, simtest wiring). func (k *Keeper) SetGuildKeeper(gk types.GuildKeeper) { k.guildKeeper = gk } // SetParamsOverride sets a simtest-grade Params override (nil = use // DefaultParams). The D-086 simtest case (f) uses this to restrict // FactoryAllowedPhases to [Phase2, Phase3] only and reject a Phase4 // launch. A future P2+ will replace this with a params-store load. func (k *Keeper) SetParamsOverride(p types.Params) { k.paramsOverride = &p } // Params returns the effective Params (the override if set, else // DefaultParams). The handler calls this to get FactoryAllowedPhases + // PoolStandingGate. func (k Keeper) Params() types.Params { if k.paramsOverride != nil { return *k.paramsOverride } return types.DefaultParams() } // 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 } // --- P4: CoverClaimsVoucher store (REQ-055, D-090(2)) ------------------------ // // The Voucher store is keyed by voucher-reach-id + pool-id (composite key) // -> CoverClaimsVoucher. A Voucher is registered per-Pool; the composite key // enforces idempotency (no duplicate Voucher for the same Pool). The // GetAvgCallSize helper computes the average Cover Call amount for a Pool // from the call/ store (returns 0 if no Calls — the D-090(2) cold-start // case). var voucherKeyPrefix = []byte("voucher/") func voucherKey(voucherReachID, poolID string) []byte { return append(append(voucherKeyPrefix, []byte(voucherReachID)...), []byte("/"+poolID)...) } // GetCoverClaimsVoucher loads a CoverClaimsVoucher by voucher-reach-id + // pool-id. Returns the Voucher and true if found, or zero value + false if // not. func (k Keeper) GetCoverClaimsVoucher(ctx sdk.Context, voucherReachID, poolID string) (types.CoverClaimsVoucher, bool) { store := ctx.KVStore(k.storeKey) bz := store.Get(voucherKey(voucherReachID, poolID)) if bz == nil { return types.CoverClaimsVoucher{}, false } var v types.CoverClaimsVoucher if err := json.Unmarshal(bz, &v); err != nil { return types.CoverClaimsVoucher{}, false } return v, true } // SetCoverClaimsVoucher persists a CoverClaimsVoucher by voucher-reach-id + // pool-id. func (k Keeper) SetCoverClaimsVoucher(ctx sdk.Context, v types.CoverClaimsVoucher) { store := ctx.KVStore(k.storeKey) bz, err := json.Marshal(v) if err != nil { panic(fmt.Sprintf("cover: marshal voucher %q/%q: %v", v.VoucherReachID, v.PoolID, err)) } store.Set(voucherKey(v.VoucherReachID, v.PoolID), bz) } // AllCoverClaimsVouchers returns all persisted CoverClaimsVoucher records // (iteration helper, unordered). func (k Keeper) AllCoverClaimsVouchers(ctx sdk.Context) []types.CoverClaimsVoucher { store := ctx.KVStore(k.storeKey) iterator := store.Iterator(voucherKeyPrefix, prefixEnd(voucherKeyPrefix)) defer iterator.Close() out := []types.CoverClaimsVoucher{} for ; iterator.Valid(); iterator.Next() { var v types.CoverClaimsVoucher if err := json.Unmarshal(iterator.Value(), &v); err == nil { out = append(out, v) } } return out } // GetAvgCallSize computes the average Cover Call amount (Grain) for a Pool // from the call/ store (REQ-055, D-090(2)). Returns 0 if no Calls have been // filed for the Pool — the D-090(2) cold-start case (the Voucher bond falls // back to MinimumVoucherBond, NOT zero). func (k Keeper) GetAvgCallSize(ctx sdk.Context, poolID string) int64 { calls := k.AllCoverCalls(ctx) sum := int64(0) n := 0 for _, c := range calls { if c.PoolID == poolID { sum += c.AmountGrain n++ } } if n == 0 { return 0 } return sum / int64(n) } // --- 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 } // --- P2: CoverCharter / PoolCouncil / CoverCallVote / CharterAmendment stores -- // // (REQ-052, REQ-062). Four new stores keyed by ID-string. The // CoverCharter store is keyed by CharterID; the PoolCouncil store is keyed // by PoolID (one council per pool); the CoverCallVote store is keyed by // VoteID; the CharterAmendment store is keyed by AmendmentID. All four // use the same JSON-marshal pattern as the P1 CoverPool / CoverCall // stores. The Get/Set/All helpers mirror the P1 helpers. var charterKeyPrefix = []byte("charter/") func charterKey(charterID string) []byte { return append(charterKeyPrefix, []byte(charterID)...) } // GetCoverCharter loads a CoverCharter by charter-id. Returns the charter // and true if found, or zero value + false if not. func (k Keeper) GetCoverCharter(ctx sdk.Context, charterID string) (types.CoverCharter, bool) { store := ctx.KVStore(k.storeKey) bz := store.Get(charterKey(charterID)) if bz == nil { return types.CoverCharter{}, false } var c types.CoverCharter if err := json.Unmarshal(bz, &c); err != nil { return types.CoverCharter{}, false } return c, true } // SetCoverCharter persists a CoverCharter by charter-id. func (k Keeper) SetCoverCharter(ctx sdk.Context, c types.CoverCharter) { store := ctx.KVStore(k.storeKey) bz, err := json.Marshal(c) if err != nil { panic(fmt.Sprintf("cover: marshal charter %q: %v", c.CharterID, err)) } store.Set(charterKey(c.CharterID), bz) } // AllCoverCharters returns all persisted CoverCharter records (iteration // helper, unordered). func (k Keeper) AllCoverCharters(ctx sdk.Context) []types.CoverCharter { store := ctx.KVStore(k.storeKey) iterator := store.Iterator(charterKeyPrefix, prefixEnd(charterKeyPrefix)) defer iterator.Close() out := []types.CoverCharter{} for ; iterator.Valid(); iterator.Next() { var c types.CoverCharter if err := json.Unmarshal(iterator.Value(), &c); err == nil { out = append(out, c) } } return out } var councilKeyPrefix = []byte("council/") func councilKey(poolID string) []byte { return append(councilKeyPrefix, []byte(poolID)...) } // GetPoolCouncil loads a PoolCouncil by pool-id. Returns the council and // true if found, or zero value + false if not. func (k Keeper) GetPoolCouncil(ctx sdk.Context, poolID string) (types.PoolCouncil, bool) { store := ctx.KVStore(k.storeKey) bz := store.Get(councilKey(poolID)) if bz == nil { return types.PoolCouncil{}, false } var c types.PoolCouncil if err := json.Unmarshal(bz, &c); err != nil { return types.PoolCouncil{}, false } return c, true } // SetPoolCouncil persists a PoolCouncil by pool-id. func (k Keeper) SetPoolCouncil(ctx sdk.Context, c types.PoolCouncil) { store := ctx.KVStore(k.storeKey) bz, err := json.Marshal(c) if err != nil { panic(fmt.Sprintf("cover: marshal council for pool %q: %v", c.PoolID, err)) } store.Set(councilKey(c.PoolID), bz) } // AllPoolCouncils returns all persisted PoolCouncil records (iteration // helper, unordered). func (k Keeper) AllPoolCouncils(ctx sdk.Context) []types.PoolCouncil { store := ctx.KVStore(k.storeKey) iterator := store.Iterator(councilKeyPrefix, prefixEnd(councilKeyPrefix)) defer iterator.Close() out := []types.PoolCouncil{} for ; iterator.Valid(); iterator.Next() { var c types.PoolCouncil if err := json.Unmarshal(iterator.Value(), &c); err == nil { out = append(out, c) } } return out } var voteKeyPrefix = []byte("vote/") func voteKey(voteID string) []byte { return append(voteKeyPrefix, []byte(voteID)...) } // GetCoverCallVote loads a CoverCallVote by vote-id. Returns the vote and // true if found, or zero value + false if not. func (k Keeper) GetCoverCallVote(ctx sdk.Context, voteID string) (types.CoverCallVote, bool) { store := ctx.KVStore(k.storeKey) bz := store.Get(voteKey(voteID)) if bz == nil { return types.CoverCallVote{}, false } var v types.CoverCallVote if err := json.Unmarshal(bz, &v); err != nil { return types.CoverCallVote{}, false } return v, true } // SetCoverCallVote persists a CoverCallVote by vote-id. func (k Keeper) SetCoverCallVote(ctx sdk.Context, v types.CoverCallVote) { store := ctx.KVStore(k.storeKey) bz, err := json.Marshal(v) if err != nil { panic(fmt.Sprintf("cover: marshal vote %q: %v", v.VoteID, err)) } store.Set(voteKey(v.VoteID), bz) } // AllCoverCallVotes returns all persisted CoverCallVote records (iteration // helper, unordered). func (k Keeper) AllCoverCallVotes(ctx sdk.Context) []types.CoverCallVote { store := ctx.KVStore(k.storeKey) iterator := store.Iterator(voteKeyPrefix, prefixEnd(voteKeyPrefix)) defer iterator.Close() out := []types.CoverCallVote{} for ; iterator.Valid(); iterator.Next() { var v types.CoverCallVote if err := json.Unmarshal(iterator.Value(), &v); err == nil { out = append(out, v) } } return out } var amendmentKeyPrefix = []byte("amendment/") func amendmentKey(amendmentID string) []byte { return append(amendmentKeyPrefix, []byte(amendmentID)...) } // GetCharterAmendment loads a CharterAmendment by amendment-id. Returns // the amendment and true if found, or zero value + false if not. func (k Keeper) GetCharterAmendment(ctx sdk.Context, amendmentID string) (types.CharterAmendment, bool) { store := ctx.KVStore(k.storeKey) bz := store.Get(amendmentKey(amendmentID)) if bz == nil { return types.CharterAmendment{}, false } var a types.CharterAmendment if err := json.Unmarshal(bz, &a); err != nil { return types.CharterAmendment{}, false } return a, true } // SetCharterAmendment persists a CharterAmendment by amendment-id. func (k Keeper) SetCharterAmendment(ctx sdk.Context, a types.CharterAmendment) { store := ctx.KVStore(k.storeKey) bz, err := json.Marshal(a) if err != nil { panic(fmt.Sprintf("cover: marshal amendment %q: %v", a.AmendmentID, err)) } store.Set(amendmentKey(a.AmendmentID), bz) } // AllCharterAmendments returns all persisted CharterAmendment records // (iteration helper, unordered). func (k Keeper) AllCharterAmendments(ctx sdk.Context) []types.CharterAmendment { store := ctx.KVStore(k.storeKey) iterator := store.Iterator(amendmentKeyPrefix, prefixEnd(amendmentKeyPrefix)) defer iterator.Close() out := []types.CharterAmendment{} for ; iterator.Valid(); iterator.Next() { var a types.CharterAmendment if err := json.Unmarshal(iterator.Value(), &a); err == nil { out = append(out, a) } } return out } // CoolCharterAmendment transitions a Proposed CharterAmendment to Cooled // if the 7-day cooling has elapsed (REQ-052). Returns an error if the // amendment is not found, not in the Proposed status, or the cooling has // not elapsed. The handler (or simtest) calls this after the cooling // period; a separate RatifyCharterAmendment transitions to Ratified. func (k Keeper) CoolCharterAmendment(ctx sdk.Context, amendmentID string, now int64) (types.CharterAmendment, error) { a, ok := k.GetCharterAmendment(ctx, amendmentID) if !ok { return types.CharterAmendment{}, fmt.Errorf("cover: amendment %q not found", amendmentID) } if a.Status != types.AmendmentProposed { return types.CharterAmendment{}, fmt.Errorf("cover: amendment %q status %q (only Proposed can be Cooled)", amendmentID, a.Status) } if now-a.ProposedAt < types.CharterAmendmentCoolingSeconds { return types.CharterAmendment{}, fmt.Errorf("cover: amendment %q cooling not elapsed (now=%d ProposedAt=%d, need %d seconds)", amendmentID, now, a.ProposedAt, types.CharterAmendmentCoolingSeconds) } a.Status = types.AmendmentCooled a.CooledAt = now k.SetCharterAmendment(ctx, a) return a, nil } // RatifyCharterAmendment transitions a Cooled CharterAmendment to // Ratified (REQ-052). Returns an error if the amendment is not found or // not in the Cooled status. The Pool supermajority + Watcher + Counsel // are checked upstream (the handler); this helper does the state // transition + appends the amendment to the parent charter's Amendments // slice. func (k Keeper) RatifyCharterAmendment(ctx sdk.Context, amendmentID string, now int64) (types.CharterAmendment, error) { a, ok := k.GetCharterAmendment(ctx, amendmentID) if !ok { return types.CharterAmendment{}, fmt.Errorf("cover: amendment %q not found", amendmentID) } if a.Status != types.AmendmentCooled { return types.CharterAmendment{}, fmt.Errorf("cover: amendment %q status %q (only Cooled can be Ratified)", amendmentID, a.Status) } a.Status = types.AmendmentRatified a.RatifiedAt = now k.SetCharterAmendment(ctx, a) return a, nil } // --- P5: Bill of Rights review + Pier Selection stores (REQ-056, REQ-066) ------ // // (REQ-056, REQ-066). Two new stores. The bill_review/ store is keyed by // ReviewID -> the MsgCounselReviewBillOfRights record (the handler persists // the review on a bonded-Counsel ceremony). The pier_selection/ store is // keyed by GuildID -> PierSelectionRecord (the MsgSelectPier handler // persists + MsgRevokePierSelection removes). The pier_index/ store is // keyed by PierID -> PierSelectionIndex (the mesh-maintained index; the // MsgSelectPier handler creates or updates the entry, accumulating scores // from successive selections). All three use the same JSON-marshal pattern // as the P1/P2 stores. var billReviewKeyPrefix = []byte("bill_review/") func billReviewKey(reviewID string) []byte { return append(billReviewKeyPrefix, []byte(reviewID)...) } // BillOfRightsReview is the persisted record of a Counsel review of the // Anti-Capture Bill of Rights (REQ-056 §7 acceptance ceremony). The // MsgCounselReviewBillOfRights handler persists this in the bill_review/ // store keyed by ReviewID. type BillOfRightsReview struct { ReviewID string `json:"review_id" yaml:"review_id"` CounselReachID string `json:"counsel_reach_id" yaml:"counsel_reach_id"` Staked bool `json:"staked" yaml:"staked"` ReviewResult string `json:"review_result" yaml:"review_result"` ReviewedAt int64 `json:"reviewed_at" yaml:"reviewed_at"` } // GetBillOfRightsReview loads a BillOfRightsReview by review-id. Returns // the review and true if found, or zero value + false if not. func (k Keeper) GetBillOfRightsReview(ctx sdk.Context, reviewID string) (BillOfRightsReview, bool) { store := ctx.KVStore(k.storeKey) bz := store.Get(billReviewKey(reviewID)) if bz == nil { return BillOfRightsReview{}, false } var r BillOfRightsReview if err := json.Unmarshal(bz, &r); err != nil { return BillOfRightsReview{}, false } return r, true } // SetBillOfRightsReview persists a BillOfRightsReview by review-id. func (k Keeper) SetBillOfRightsReview(ctx sdk.Context, r BillOfRightsReview) { store := ctx.KVStore(k.storeKey) bz, err := json.Marshal(r) if err != nil { panic(fmt.Sprintf("cover: marshal bill-of-rights review %q: %v", r.ReviewID, err)) } store.Set(billReviewKey(r.ReviewID), bz) } // AllBillOfRightsReviews returns all persisted BillOfRightsReview records // (iteration helper, unordered). func (k Keeper) AllBillOfRightsReviews(ctx sdk.Context) []BillOfRightsReview { store := ctx.KVStore(k.storeKey) iterator := store.Iterator(billReviewKeyPrefix, prefixEnd(billReviewKeyPrefix)) defer iterator.Close() out := []BillOfRightsReview{} for ; iterator.Valid(); iterator.Next() { var r BillOfRightsReview if err := json.Unmarshal(iterator.Value(), &r); err == nil { out = append(out, r) } } return out } var pierSelectionKeyPrefix = []byte("pier_selection/") func pierSelectionKey(guildID string) []byte { return append(pierSelectionKeyPrefix, []byte(guildID)...) } // GetPierSelectionRecord loads a PierSelectionRecord by guild-id. Returns // the record and true if found, or zero value + false if not. func (k Keeper) GetPierSelectionRecord(ctx sdk.Context, guildID string) (types.PierSelectionRecord, bool) { store := ctx.KVStore(k.storeKey) bz := store.Get(pierSelectionKey(guildID)) if bz == nil { return types.PierSelectionRecord{}, false } var r types.PierSelectionRecord if err := json.Unmarshal(bz, &r); err != nil { return types.PierSelectionRecord{}, false } return r, true } // SetPierSelectionRecord persists a PierSelectionRecord by guild-id. func (k Keeper) SetPierSelectionRecord(ctx sdk.Context, r types.PierSelectionRecord) { store := ctx.KVStore(k.storeKey) bz, err := json.Marshal(r) if err != nil { panic(fmt.Sprintf("cover: marshal pier selection for guild %q: %v", r.GuildID, err)) } store.Set(pierSelectionKey(r.GuildID), bz) } // RemovePierSelectionRecord removes a PierSelectionRecord by guild-id (the // MsgRevokePierSelection handler calls this). Returns true if a record was // removed, false if no record existed. func (k Keeper) RemovePierSelectionRecord(ctx sdk.Context, guildID string) bool { store := ctx.KVStore(k.storeKey) key := pierSelectionKey(guildID) if store.Get(key) == nil { return false } store.Delete(key) return true } var pierIndexKeyPrefix = []byte("pier_index/") func pierIndexKey(pierID string) []byte { return append(pierIndexKeyPrefix, []byte(pierID)...) } // GetPierSelectionIndex loads a PierSelectionIndex by pier-id (REQ-066 — // the mesh-maintained index query). Returns the index and true if found, // or zero value + false if not. func (k Keeper) GetPierSelectionIndex(ctx sdk.Context, pierID string) (types.PierSelectionIndex, bool) { store := ctx.KVStore(k.storeKey) bz := store.Get(pierIndexKey(pierID)) if bz == nil { return types.PierSelectionIndex{}, false } var idx types.PierSelectionIndex if err := json.Unmarshal(bz, &idx); err != nil { return types.PierSelectionIndex{}, false } return idx, true } // SetPierSelectionIndex persists a PierSelectionIndex by pier-id. func (k Keeper) SetPierSelectionIndex(ctx sdk.Context, idx types.PierSelectionIndex) { store := ctx.KVStore(k.storeKey) bz, err := json.Marshal(idx) if err != nil { panic(fmt.Sprintf("cover: marshal pier selection index for pier %q: %v", idx.PierID, err)) } store.Set(pierIndexKey(idx.PierID), bz) } // AllPierSelectionIndexes returns all persisted PierSelectionIndex records // (iteration helper, unordered). func (k Keeper) AllPierSelectionIndexes(ctx sdk.Context) []types.PierSelectionIndex { store := ctx.KVStore(k.storeKey) iterator := store.Iterator(pierIndexKeyPrefix, prefixEnd(pierIndexKeyPrefix)) defer iterator.Close() out := []types.PierSelectionIndex{} for ; iterator.Valid(); iterator.Next() { var idx types.PierSelectionIndex if err := json.Unmarshal(iterator.Value(), &idx); err == nil { out = append(out, idx) } } return out } // DefaultPierScores are the simtest-grade default scores for a fresh // PierSelectionIndex entry (the mesh assigns these on a Pier's first // selection — the live mesh oracle is a v0.8+ concern; the simtest uses // these deterministic defaults so the index is non-empty on first // selection). All three scores are in [0,1]; the OverallScore is the // weighted aggregate (a deterministic blend: 0.4 × JurisdictionalReliability // + 0.3 × IntegrationQuality + 0.3 × (FiduciaryRecordHash non-empty ? 1.0 // : 0.0)). const ( DefaultPierJurisdictionalReliabilityScore = 0.8 DefaultPierIntegrationQualityScore = 0.7 ) // DefaultPierOverallScore computes the deterministic OverallScore blend // for a PierSelectionIndex (the keeper uses this when creating or updating // an index entry). The blend is 0.4 × JurisdictionalReliability + 0.3 × // IntegrationQuality + 0.3 × FiduciaryConfidence (FiduciaryConfidence is // 1.0 if the FiduciaryRecordHash is non-empty, else 0.0). The simtest // asserts the OverallScore is non-decreasing on successive selections (a // second selection with the same scores yields the same OverallScore). func DefaultPierOverallScore(jurisdictionalReliability, integrationQuality float64, fiduciaryRecordHash []byte) float64 { fiduciaryConfidence := 0.0 if len(fiduciaryRecordHash) > 0 { fiduciaryConfidence = 1.0 } return 0.4*jurisdictionalReliability + 0.3*integrationQuality + 0.3*fiduciaryConfidence }