Skip to content

Commit 849d400

Browse files
authored
feat(governor955): add x/governor955/keeper — 95/5 enforcer, ratio tracker, EndBlock publish
1 parent eba1ac8 commit 849d400

1 file changed

Lines changed: 199 additions & 0 deletions

File tree

x/governor955/keeper/keeper.go

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
// Package keeper implements the 955 Operational Governor.
2+
// Enforces the 95% truth verification / 5% humanitarian operational split
3+
// per CHAIN-SPEC-v1.5.1 Section 6.2 and Rampage Constitution Art. VI.
4+
package keeper
5+
6+
import (
7+
"encoding/binary"
8+
"fmt"
9+
10+
"cosmossdk.io/core/store"
11+
sdkmath "cosmossdk.io/math"
12+
"cosmossdk.io/log"
13+
"github.com/cosmos/cosmos-sdk/codec"
14+
sdk "github.com/cosmos/cosmos-sdk/types"
15+
16+
"rampage/x/governor955/types"
17+
)
18+
19+
const (
20+
// RatioPublishInterval is the number of blocks between on-chain ratio publications.
21+
// Approximately quarterly at 6s/block and 10,000 TPS target.
22+
RatioPublishInterval = int64(1000)
23+
)
24+
25+
var (
26+
ParamsKey = []byte{0x01}
27+
TruthSpendKey = []byte{0x02} // cumulative urpm spent on truth verification
28+
HumanitarianSpendKey = []byte{0x03} // cumulative urpm spent on humanitarian
29+
LastRatioPublishKey = []byte{0x04} // last block height at which ratio was published
30+
RatioHistoryKeyPrefix = []byte{0x05} // prefix for ratio history records
31+
LDFBalanceKey = []byte{0x06} // Legal Defense Fund current balance
32+
)
33+
34+
// SpendCategory classifies treasury outflows.
35+
type SpendCategory string
36+
37+
const (
38+
// SpendTruthVerification is for attestation, journalist support, oracle ops.
39+
SpendTruthVerification SpendCategory = "TRUTH_VERIFICATION"
40+
// SpendHumanitarian is for Art. VIII humanitarian access operations.
41+
SpendHumanitarian SpendCategory = "HUMANITARIAN"
42+
)
43+
44+
// Keeper maintains the link to data storage and exposes getter/setter methods
45+
// for the governor955 module's state.
46+
type Keeper struct {
47+
cdc codec.BinaryCodec
48+
storeService store.KVStoreService
49+
logger log.Logger
50+
authorityAddress string
51+
bankKeeper types.BankKeeper
52+
}
53+
54+
// NewKeeper creates a new governor955 Keeper.
55+
func NewKeeper(
56+
cdc codec.BinaryCodec,
57+
storeService store.KVStoreService,
58+
logger log.Logger,
59+
authorityAddress string,
60+
bankKeeper types.BankKeeper,
61+
) Keeper {
62+
if _, err := sdk.AccAddressFromBech32(authorityAddress); err != nil {
63+
panic(fmt.Sprintf("invalid authority address: %s", err))
64+
}
65+
return Keeper{
66+
cdc: cdc,
67+
storeService: storeService,
68+
logger: logger.With("module", fmt.Sprintf("x/%s", types.ModuleName)),
69+
authorityAddress: authorityAddress,
70+
bankKeeper: bankKeeper,
71+
}
72+
}
73+
74+
// Logger returns a module-specific logger.
75+
func (k Keeper) Logger() log.Logger { return k.logger }
76+
77+
// RecordSpend records a treasury outflow under a given category.
78+
// Called by any module that spends from the Rampage treasury.
79+
func (k Keeper) RecordSpend(ctx sdk.Context, amount sdkmath.Int, category SpendCategory) error {
80+
switch category {
81+
case SpendTruthVerification:
82+
current := k.getTruthSpend(ctx)
83+
return k.setTruthSpend(ctx, current.Add(amount))
84+
case SpendHumanitarian:
85+
current := k.getHumanitarianSpend(ctx)
86+
return k.setHumanitarianSpend(ctx, current.Add(amount))
87+
default:
88+
return fmt.Errorf("unknown spend category: %s", category)
89+
}
90+
}
91+
92+
// GetCurrentRatio returns the current truth verification / humanitarian ratio.
93+
// Returns (truthPct, humanitarianPct) as sdk.Dec values (0-1 range).
94+
func (k Keeper) GetCurrentRatio(ctx sdk.Context) (sdkmath.LegacyDec, sdkmath.LegacyDec) {
95+
truth := k.getTruthSpend(ctx)
96+
humanitarian := k.getHumanitarianSpend(ctx)
97+
total := truth.Add(humanitarian)
98+
99+
if total.IsZero() {
100+
// No spend yet — return target ratio.
101+
return sdkmath.LegacyNewDecWithPrec(95, 2), sdkmath.LegacyNewDecWithPrec(5, 2)
102+
}
103+
104+
truthPct := sdkmath.LegacyNewDecFromInt(truth).Quo(sdkmath.LegacyNewDecFromInt(total))
105+
humanitarianPct := sdkmath.LegacyNewDecFromInt(humanitarian).Quo(sdkmath.LegacyNewDecFromInt(total))
106+
return truthPct, humanitarianPct
107+
}
108+
109+
// IsRatioDrifting returns true if the ratio has deviated from the 95/5 target.
110+
// "Drifting" is defined as the truth verification allocation falling below 90%.
111+
func (k Keeper) IsRatioDrifting(ctx sdk.Context) bool {
112+
truthPct, _ := k.GetCurrentRatio(ctx)
113+
threshold := sdkmath.LegacyNewDecWithPrec(90, 2) // 90% minimum
114+
return truthPct.LT(threshold)
115+
}
116+
117+
// MaybePublishRatio publishes the current ratio to the chain log every RatioPublishInterval blocks.
118+
// Should be called from EndBlock.
119+
func (k Keeper) MaybePublishRatio(ctx sdk.Context) {
120+
currentBlock := ctx.BlockHeight()
121+
lastPublish := k.getLastRatioPublish(ctx)
122+
123+
if currentBlock-lastPublish < RatioPublishInterval {
124+
return
125+
}
126+
127+
truthPct, humanitarianPct := k.GetCurrentRatio(ctx)
128+
k.logger.Info("governor955: periodic ratio publication",
129+
"block", currentBlock,
130+
"truth_verification_pct", truthPct.String(),
131+
"humanitarian_pct", humanitarianPct.String(),
132+
"drifting", k.IsRatioDrifting(ctx),
133+
)
134+
135+
// Emit an SDK event so indexers and the TruthOracle.ai dashboard can track it.
136+
ctx.EventManager().EmitEvent(
137+
sdk.NewEvent(
138+
"governor955_ratio_published",
139+
sdk.NewAttribute("block_height", fmt.Sprintf("%d", currentBlock)),
140+
sdk.NewAttribute("truth_verification_pct", truthPct.String()),
141+
sdk.NewAttribute("humanitarian_pct", humanitarianPct.String()),
142+
sdk.NewAttribute("is_drifting", fmt.Sprintf("%t", k.IsRatioDrifting(ctx))),
143+
),
144+
)
145+
146+
k.setLastRatioPublish(ctx, currentBlock)
147+
}
148+
149+
// --- Internal helpers ---
150+
151+
func (k Keeper) getTruthSpend(ctx sdk.Context) sdkmath.Int {
152+
storeAdapter := k.storeService.OpenKVStore(ctx)
153+
bz, _ := storeAdapter.Get(TruthSpendKey)
154+
if bz == nil {
155+
return sdkmath.ZeroInt()
156+
}
157+
var i sdkmath.Int
158+
k.cdc.MustUnmarshal(bz, &i)
159+
return i
160+
}
161+
162+
func (k Keeper) setTruthSpend(ctx sdk.Context, amount sdkmath.Int) error {
163+
storeAdapter := k.storeService.OpenKVStore(ctx)
164+
bz := k.cdc.MustMarshal(&amount)
165+
return storeAdapter.Set(TruthSpendKey, bz)
166+
}
167+
168+
func (k Keeper) getHumanitarianSpend(ctx sdk.Context) sdkmath.Int {
169+
storeAdapter := k.storeService.OpenKVStore(ctx)
170+
bz, _ := storeAdapter.Get(HumanitarianSpendKey)
171+
if bz == nil {
172+
return sdkmath.ZeroInt()
173+
}
174+
var i sdkmath.Int
175+
k.cdc.MustUnmarshal(bz, &i)
176+
return i
177+
}
178+
179+
func (k Keeper) setHumanitarianSpend(ctx sdk.Context, amount sdkmath.Int) error {
180+
storeAdapter := k.storeService.OpenKVStore(ctx)
181+
bz := k.cdc.MustMarshal(&amount)
182+
return storeAdapter.Set(HumanitarianSpendKey, bz)
183+
}
184+
185+
func (k Keeper) getLastRatioPublish(ctx sdk.Context) int64 {
186+
storeAdapter := k.storeService.OpenKVStore(ctx)
187+
bz, _ := storeAdapter.Get(LastRatioPublishKey)
188+
if bz == nil {
189+
return 0
190+
}
191+
return int64(binary.BigEndian.Uint64(bz))
192+
}
193+
194+
func (k Keeper) setLastRatioPublish(ctx sdk.Context, height int64) {
195+
storeAdapter := k.storeService.OpenKVStore(ctx)
196+
bz := make([]byte, 8)
197+
binary.BigEndian.PutUint64(bz, uint64(height))
198+
_ = storeAdapter.Set(LastRatioPublishKey, bz)
199+
}

0 commit comments

Comments
 (0)