6c34650a0d
v0.5 Bearers Runtime — 7 runtime REQs (REQ-033..039) shipped as feature. 8 modules promoted to runtime (MsgServer + simtest). cosmos-sdk v0.50.8 + ibc-go v8.2.1 added (G-006 controlled exception). G-003 + locked-const firewalls intact. 8 keeper packages ≥80% coverage. 5 GRILL decisions ratified; 8 binding fixes landed; 5 P1+ flagged for v0.6+. ---ci--- project: oy phase: 8 milestone: v0.5 status: complete requirements: covered: [REQ-033, REQ-034, REQ-035, REQ-036, REQ-037, REQ-038, REQ-039] partial: [] ---/ci---
167 lines
5.1 KiB
Go
167 lines
5.1 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/bearers/types"
|
|
)
|
|
|
|
// keeper.go holds the store-backed Keeper for the bearers module (P2-02-01,
|
|
// REQ-034).
|
|
//
|
|
// The Keeper wraps an sdk.KVStore via a storeKey. It holds the Session
|
|
// records (by session-id) and the OYQRCode records (by qr-id). The Keeper
|
|
// also holds the expected-keeper shim (BreadKeeper for the OY-QR consume
|
|
// transfer effect). The shim is an interface (G-003 — no struct import of
|
|
// x/bread/types); the concrete x/bread keeper satisfies it structurally.
|
|
//
|
|
// State-machine ordering (vision §7, enforced in every handler):
|
|
// ValidateBasic → keeper authz → state mutation → ctx.EventManager().EmitEvent
|
|
//
|
|
// Surveillance-resistant invariant (A-522): the Keeper carries NO
|
|
// geolocation fields; the handlers emit NO geolocation in events.
|
|
|
|
// Keeper is the store-backed bearers keeper.
|
|
type Keeper struct {
|
|
cdc codec.Codec
|
|
storeKey storetypes.StoreKey
|
|
breadKeeper types.BreadKeeper
|
|
}
|
|
|
|
// NewKeeper constructs a new store-backed bearers Keeper. The BreadKeeper
|
|
// expected-keeper shim is injected (nil-able for partial tests; the
|
|
// ConsumeOYQR handler guards a nil shim and skips the transfer effect,
|
|
// still flipping the consumed flag — the A-521 state-write-first invariant
|
|
// holds regardless).
|
|
func NewKeeper(cdc codec.Codec, storeKey storetypes.StoreKey, bk types.BreadKeeper) Keeper {
|
|
return Keeper{
|
|
cdc: cdc,
|
|
storeKey: storeKey,
|
|
breadKeeper: bk,
|
|
}
|
|
}
|
|
|
|
// SetBreadKeeper sets the BreadKeeper expected-keeper shim (for
|
|
// post-construction wiring, e.g., app wiring or test setup).
|
|
func (k *Keeper) SetBreadKeeper(bk types.BreadKeeper) { k.breadKeeper = bk }
|
|
|
|
// --- Session store -----------------------------------------------------------
|
|
|
|
var sessionKeyPrefix = []byte("session/")
|
|
|
|
func sessionKey(sessionID string) []byte {
|
|
return append(sessionKeyPrefix, []byte(sessionID)...)
|
|
}
|
|
|
|
// GetSession loads a Session by session-id. Returns the session and true
|
|
// if found, or zero value + false if not.
|
|
func (k Keeper) GetSession(ctx sdk.Context, sessionID string) (types.Session, bool) {
|
|
store := ctx.KVStore(k.storeKey)
|
|
bz := store.Get(sessionKey(sessionID))
|
|
if bz == nil {
|
|
return types.Session{}, false
|
|
}
|
|
var s types.Session
|
|
if err := json.Unmarshal(bz, &s); err != nil {
|
|
return types.Session{}, false
|
|
}
|
|
return s, true
|
|
}
|
|
|
|
// SetSession persists a Session by session-id.
|
|
func (k Keeper) SetSession(ctx sdk.Context, s types.Session) {
|
|
store := ctx.KVStore(k.storeKey)
|
|
bz, err := json.Marshal(s)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("bearers: marshal session %q: %v", s.SessionID, err))
|
|
}
|
|
store.Set(sessionKey(s.SessionID), bz)
|
|
}
|
|
|
|
// AllSessions returns all persisted Session records (iteration helper).
|
|
func (k Keeper) AllSessions(ctx sdk.Context) []types.Session {
|
|
store := ctx.KVStore(k.storeKey)
|
|
iterator := store.Iterator(sessionKeyPrefix, prefixEnd(sessionKeyPrefix))
|
|
defer iterator.Close()
|
|
out := []types.Session{}
|
|
for ; iterator.Valid(); iterator.Next() {
|
|
var s types.Session
|
|
if err := json.Unmarshal(iterator.Value(), &s); err == nil {
|
|
out = append(out, s)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// --- OYQRCode store ----------------------------------------------------------
|
|
|
|
var qrKeyPrefix = []byte("qr/")
|
|
|
|
func qrKey(qrID string) []byte {
|
|
return append(qrKeyPrefix, []byte(qrID)...)
|
|
}
|
|
|
|
// GetOYQRCode loads an OYQRCode by qr-id. Returns the QR and true if found.
|
|
func (k Keeper) GetOYQRCode(ctx sdk.Context, qrID string) (types.OYQRCode, bool) {
|
|
store := ctx.KVStore(k.storeKey)
|
|
bz := store.Get(qrKey(qrID))
|
|
if bz == nil {
|
|
return types.OYQRCode{}, false
|
|
}
|
|
var q types.OYQRCode
|
|
if err := json.Unmarshal(bz, &q); err != nil {
|
|
return types.OYQRCode{}, false
|
|
}
|
|
return q, true
|
|
}
|
|
|
|
// SetOYQRCode persists an OYQRCode by qr-id.
|
|
func (k Keeper) SetOYQRCode(ctx sdk.Context, q types.OYQRCode) {
|
|
store := ctx.KVStore(k.storeKey)
|
|
bz, err := json.Marshal(q)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("bearers: marshal qr %q: %v", q.QRID, err))
|
|
}
|
|
store.Set(qrKey(q.QRID), bz)
|
|
}
|
|
|
|
// AllOYQRCodes returns all persisted OYQRCode records (iteration helper).
|
|
func (k Keeper) AllOYQRCodes(ctx sdk.Context) []types.OYQRCode {
|
|
store := ctx.KVStore(k.storeKey)
|
|
iterator := store.Iterator(qrKeyPrefix, prefixEnd(qrKeyPrefix))
|
|
defer iterator.Close()
|
|
out := []types.OYQRCode{}
|
|
for ; iterator.Valid(); iterator.Next() {
|
|
var q types.OYQRCode
|
|
if err := json.Unmarshal(iterator.Value(), &q); err == nil {
|
|
out = append(out, q)
|
|
}
|
|
}
|
|
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
|
|
}
|