Files
openyield/x/partner/keeper/keeper.go
T
cloudinit-bot be4c023340
docs-build / go test ./... (lexicon firewall + all x/* tests) (push) Has been cancelled
docs-build / mkdocs build (docs site artifact) (push) Has been cancelled
Merge phase/03 into milestone/v0.5-bearers-runtime (P3 complete → v0.4.3)
---ci---
project: oy
phase: 3
milestone: v0.5
status: complete
requirements:
  covered: [REQ-035]
  partial: []
---/ci---
2026-08-18 00:38:40 +00:00

138 lines
4.9 KiB
Go

package keeper
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/partner/types"
)
// keeper.go holds the store-backed Keeper for the partner module's
// Anchor credential runtime (P3-02-01, REQ-035).
//
// The Keeper wraps an sdk.KVStore via a storeKey. It holds the
// AnchorCredential records (by anchor-id). The v0.3 in-memory registry
// Keeper stub (types.Keeper, x/partner/types/types.go) is RETAINED for
// the Partner registry (non-Anchor partners — the v0.3 skeleton); the
// v0.5 runtime promotes ONLY the Anchor credential lifecycle to a
// store-backed keeper (D-054 simtest grade). The Partner registry stays
// on the v0.3 in-memory stub (non-Anchor partner tiers are not promoted
// in v0.5 — out of scope; only the Anchor credential lifecycle is).
//
// The Keeper also holds the two expected-keeper shims (WatcherKeeper for
// 6-of-9 quorum authz on issue/revoke; HubKeeper for custody-provider-id
// validity on onboard — the P3→P4 hub dep edge broken by the interface
// shim per G-003 / ARCHITECTURE.md v0.5). The shims are interfaces
// (G-003 — no struct import of x/watcher/types or x/hub/types); the
// concrete keepers satisfy them structurally.
//
// State-machine ordering (vision §7, enforced in every handler):
// ValidateBasic → keeper authz → state mutation → ctx.EventManager().EmitEvent
// Keeper is the store-backed partner Anchor-credential keeper.
type Keeper struct {
cdc codec.Codec
storeKey storetypes.StoreKey
watcherKeeper types.WatcherKeeper
hubKeeper types.HubKeeper
}
// NewKeeper constructs a new store-backed partner Anchor-credential
// Keeper. The WatcherKeeper and HubKeeper expected-keeper shims are
// injected (nil-able for partial tests; the handlers guard nil shims
// and skip the corresponding authz/validity check, still mutating state
// — the simtest wiring document this). The HubKeeper shim is the P3→P4
// hub dep edge: in P3 simtest it is wired to a stub (G-003 test
// exemption); the real hub keeper is wired in P4.
func NewKeeper(cdc codec.Codec, storeKey storetypes.StoreKey, wk types.WatcherKeeper, hk types.HubKeeper) Keeper {
return Keeper{
cdc: cdc,
storeKey: storeKey,
watcherKeeper: wk,
hubKeeper: hk,
}
}
// SetWatcherKeeper sets the WatcherKeeper expected-keeper shim (for
// post-construction wiring, e.g., app wiring or test setup).
func (k *Keeper) SetWatcherKeeper(wk types.WatcherKeeper) { k.watcherKeeper = wk }
// SetHubKeeper sets the HubKeeper expected-keeper shim (for
// post-construction wiring, e.g., app wiring or test setup). This is
// the P3→P4 hub dep edge: P4 wires the real hub keeper via this setter
// or via NewKeeper.
func (k *Keeper) SetHubKeeper(hk types.HubKeeper) { k.hubKeeper = hk }
// --- Anchor credential store -------------------------------------------------
var anchorKeyPrefix = []byte("anchor/")
func anchorKey(anchorID string) []byte {
return append(anchorKeyPrefix, []byte(anchorID)...)
}
// GetAnchorCredential loads an AnchorCredential by anchor-id. Returns the
// credential and true if found, or zero value + false if not.
func (k Keeper) GetAnchorCredential(ctx sdk.Context, anchorID string) (types.AnchorCredential, bool) {
store := ctx.KVStore(k.storeKey)
bz := store.Get(anchorKey(anchorID))
if bz == nil {
return types.AnchorCredential{}, false
}
var c types.AnchorCredential
if err := json.Unmarshal(bz, &c); err != nil {
return types.AnchorCredential{}, false
}
return c, true
}
// SetAnchorCredential persists an AnchorCredential by anchor-id.
func (k Keeper) SetAnchorCredential(ctx sdk.Context, c types.AnchorCredential) {
store := ctx.KVStore(k.storeKey)
bz, err := json.Marshal(c)
if err != nil {
panic(fmt.Sprintf("partner: marshal anchor credential %q: %v", c.AnchorID, err))
}
store.Set(anchorKey(c.AnchorID), bz)
}
// AllAnchorCredentials returns all persisted AnchorCredential records
// (iteration helper).
func (k Keeper) AllAnchorCredentials(ctx sdk.Context) []types.AnchorCredential {
store := ctx.KVStore(k.storeKey)
iterator := store.Iterator(anchorKeyPrefix, prefixEnd(anchorKeyPrefix))
defer iterator.Close()
out := []types.AnchorCredential{}
for ; iterator.Valid(); iterator.Next() {
var c types.AnchorCredential
if err := json.Unmarshal(iterator.Value(), &c); err == nil {
out = append(out, c)
}
}
return out
}
// 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.
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
}