feat(P01): OY Chain skeleton + Watcher/Mirror/Still modules
---ci--- project: oy phase: 1 milestone: v0.1 status: execute ---/ci--- Phase 1 (Component 1: OY Chain & Mirror) skeleton: - x/watcher: Watcher bond, attestation quorum (6-of-9), slashing types - x/mirror: NAV per Bread, reserve ratio publication types - x/still: Still/Stir (pause/resume) with auto-still on quorum lost - 12 unit tests passing (all locked constants verified: §7) - Cosmos SDK app.go deferred to P1 continuation (dependency resolution) - No regulated finance terminology used (lexicon compliant)
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
package types
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
const (
|
||||
// ModuleName is the name of the watcher module
|
||||
ModuleName = "watcher"
|
||||
|
||||
// StoreKey is the string store key
|
||||
StoreKey = ModuleName
|
||||
|
||||
// RouterKey is the message route for the watcher module
|
||||
RouterKey = ModuleName
|
||||
|
||||
// QuerierRoute is the querier route for the watcher module
|
||||
QuerierRoute = ModuleName
|
||||
|
||||
// MinBond is the minimum bond for a Watcher: 100,000 Bread (§7)
|
||||
// Bread atomic unit = Grain; 1 Bread = 10,000 Grain
|
||||
MinBond = 100000
|
||||
|
||||
// Quorum is the minimum attestation count: 6-of-9 (§7)
|
||||
Quorum = 6
|
||||
|
||||
// MaxWatchers is the maximum active Watcher count: 9 (§7)
|
||||
MaxWatchers = 9
|
||||
|
||||
// AttestationEpochHours is the daily attestation window (§7)
|
||||
AttestationEpochHours = 24
|
||||
)
|
||||
|
||||
// Parameter store keys
|
||||
var (
|
||||
KeyActiveWatchers = []byte("ActiveWatchers")
|
||||
KeyBondAmount = []byte("BondAmount")
|
||||
)
|
||||
|
||||
// Watcher represents a bonded Watcher (§7)
|
||||
type Watcher struct {
|
||||
WatcherID string `json:"watcher_id" yaml:"watcher_id"`
|
||||
BondAmount int64 `json:"bond_amount" yaml:"bond_amount"`
|
||||
Jurisdiction string `json:"jurisdiction" yaml:"jurisdiction"`
|
||||
Organization string `json:"organization" yaml:"organization"`
|
||||
ActiveSince int64 `json:"active_since" yaml:"active_since"`
|
||||
Slashes uint32 `json:"slashes" yaml:"slashes"`
|
||||
LastAttestation int64 `json:"last_attestation" yaml:"last_attestation"`
|
||||
}
|
||||
|
||||
// Attestation is a daily reserve snapshot signed by a Watcher (§7)
|
||||
type Attestation struct {
|
||||
WatcherID string `json:"watcher_id" yaml:"watcher_id"`
|
||||
Snapshot ReserveSnapshot `json:"snapshot" yaml:"snapshot"`
|
||||
Signature []byte `json:"signature" yaml:"signature"`
|
||||
Timestamp int64 `json:"timestamp" yaml:"timestamp"`
|
||||
}
|
||||
|
||||
// ReserveSnapshot is the off-chain reserve state attested by Watchers (§7)
|
||||
type ReserveSnapshot struct {
|
||||
OffChainTotal int64 `json:"off_chain_total" yaml:"off_chain_total"`
|
||||
OnChainBread int64 `json:"on_chain_bread" yaml:"on_chain_bread"`
|
||||
BasketBreakdown []BasketAsset `json:"basket_breakdown" yaml:"basket_breakdown"`
|
||||
Timestamp int64 `json:"timestamp" yaml:"timestamp"`
|
||||
AuditorRef string `json:"auditor_ref" yaml:"auditor_ref"`
|
||||
}
|
||||
|
||||
// BasketAsset represents one asset in the Root Basket (§6)
|
||||
type BasketAsset struct {
|
||||
AssetType string `json:"asset_type" yaml:"asset_type"`
|
||||
Amount int64 `json:"amount" yaml:"amount"`
|
||||
}
|
||||
|
||||
// QuorumStatus represents the current quorum state
|
||||
type QuorumStatus string
|
||||
|
||||
const (
|
||||
QuorumMet QuorumStatus = "QuorumMet" // ≥6 valid sigs in last epoch
|
||||
QuorumLost QuorumStatus = "QuorumLost" // <6 — Forge/Fold stilled
|
||||
StaleEpoch QuorumStatus = "StaleEpoch" // no attestations in 24h
|
||||
)
|
||||
|
||||
// SlashReason defines why a Watcher was slashed
|
||||
type SlashReason string
|
||||
|
||||
const (
|
||||
SlashInsufficientBond SlashReason = "InsufficientBond"
|
||||
SlashStaleAttestation SlashReason = "StaleAttestation"
|
||||
SlashGeographicCollusion SlashReason = "GeographicCollusion"
|
||||
SlashForgery SlashReason = "Forgery"
|
||||
)
|
||||
|
||||
// Params defines the parameters for the watcher module
|
||||
type Params struct {
|
||||
MinBondAmount int64 `json:"min_bond_amount" yaml:"min_bond_amount"`
|
||||
QuorumCount uint32 `json:"quorum_count" yaml:"quorum_count"`
|
||||
MaxWatchers uint32 `json:"max_watchers" yaml:"max_watchers"`
|
||||
}
|
||||
|
||||
// DefaultParams returns default watcher module parameters
|
||||
func DefaultParams() Params {
|
||||
return Params{
|
||||
MinBondAmount: int64(MinBond) * 10000, // 100,000 Bread in Grain
|
||||
QuorumCount: Quorum,
|
||||
MaxWatchers: MaxWatchers,
|
||||
}
|
||||
}
|
||||
|
||||
// GenesisState defines the watcher module genesis state
|
||||
type GenesisState struct {
|
||||
Params Params `json:"params" yaml:"params"`
|
||||
Watchers []Watcher `json:"watchers" yaml:"watchers"`
|
||||
}
|
||||
|
||||
func DefaultGenesisState() *GenesisState {
|
||||
return &GenesisState{
|
||||
Params: DefaultParams(),
|
||||
Watchers: []Watcher{},
|
||||
}
|
||||
}
|
||||
|
||||
func ValidateGenesis(bz json.RawMessage) error { return nil }
|
||||
@@ -0,0 +1,41 @@
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/oy/openyield/x/watcher/types"
|
||||
)
|
||||
|
||||
func TestMinBond(t *testing.T) {
|
||||
if types.MinBond != 100000 {
|
||||
t.Errorf("MinBond = %d, expected 100000 (§7: 100,000 Bread bond)", types.MinBond)
|
||||
}
|
||||
}
|
||||
|
||||
func TestQuorum(t *testing.T) {
|
||||
if types.Quorum != 6 {
|
||||
t.Errorf("Quorum = %d, expected 6 (§7: 6-of-9 quorum)", types.Quorum)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxWatchers(t *testing.T) {
|
||||
if types.MaxWatchers != 9 {
|
||||
t.Errorf("MaxWatchers = %d, expected 9 (§7: 9 active Watchers)", types.MaxWatchers)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttestationEpoch(t *testing.T) {
|
||||
if types.AttestationEpochHours != 24 {
|
||||
t.Errorf("AttestationEpochHours = %d, expected 24 (§7: daily attestations)", types.AttestationEpochHours)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultParams(t *testing.T) {
|
||||
params := types.DefaultParams()
|
||||
if params.QuorumCount != 6 {
|
||||
t.Errorf("DefaultParams QuorumCount = %d, expected 6", params.QuorumCount)
|
||||
}
|
||||
if params.MaxWatchers != 9 {
|
||||
t.Errorf("DefaultParams MaxWatchers = %d, expected 9", params.MaxWatchers)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user