docs(P04): complete Bearers skeleton I phase
P4 complete. Bearers skeleton I (D-020/D-035 pattern, zero ext deps): - x/bridge/types (NEW): BridgeStatus enum (4), BridgeRoute with by-ID-string refs to satellite L2Chain + watcher quorum (G-003). - x/exit/types (NEW): ExitStatus enum (5), ExitRoute with bridge-route-id by-ID-string (A-308), DEXSwap with opaque venue string. - x/bearers/types (EXT): OYSATLink surveillance-resistant LOCKED true (A-311), OYQRCode MarkConsumed idempotent. - x/partner/types (EXT): AnchorCredential, custody-provider-id empty in skeleton (A-304), PartnerTierCount=4 regression (A-305). All 4 packages 100% coverage. Both firewalls green. G-003 intact. ---ci--- project: oy phase: 4 milestone: v0.3 status: complete tag_base: v0.2.x phase_role: execution requirements: covered: [REQ-010, REQ-022, REQ-023] partial: [] ---/ci---
This commit is contained in:
@@ -155,6 +155,53 @@ func (k *Keeper) ListByTier(tier PartnerTier) []Partner {
|
||||
return out
|
||||
}
|
||||
|
||||
// AnchorCredential is the institutional onboarding metadata for an Anchor
|
||||
// tier Partner (REQ-023, D-038, A-305). The Anchor tier (the 4th of the
|
||||
// 4-tier Partner Spectrum, REQ-018) gets institution-specific credential
|
||||
// fields in v0.3. v0.2 defined the 4-tier enum + Partner struct +
|
||||
// CredentialRef; v0.3 adds this AnchorCredential struct carrying the
|
||||
// institutional onboarding metadata. No live institutional onboarding in
|
||||
// v0.3 (the skeleton defines the type shape only).
|
||||
//
|
||||
// All cross-module references are by-ID-string per G-003:
|
||||
//
|
||||
// - anchor-id references a Partner with Tier=Anchor by ID-string
|
||||
// (G-003). No struct import; the reference is validated against the
|
||||
// Partner registry by the keeper, not the type system.
|
||||
// - custody-provider-id references an x/hub custody service by ID-string
|
||||
// (A-304/G-003). The hub is NOT live until P5/v0.4, so this field is
|
||||
// EMPTY in the v0.3 skeleton (NewAnchorCredential sets it to "").
|
||||
// The field exists so the shape is stable when the hub comes online.
|
||||
// No struct import of x/hub.
|
||||
// - credential-uri is an opaque URI to the institutional credential
|
||||
// (regulatory jurisdiction, attestation refs, etc.) — like the v0.2
|
||||
// Pier CredentialRef, kept opaque in the skeleton.
|
||||
// - attestation-count is the number of Watcher/auditor attestations on
|
||||
// the credential (starts at 0 in the skeleton).
|
||||
type AnchorCredential struct {
|
||||
AnchorID string `json:"anchor_id" yaml:"anchor_id"`
|
||||
CustodyProviderID string `json:"custody_provider_id" yaml:"custody_provider_id"`
|
||||
CredentialURI string `json:"credential_uri" yaml:"credential_uri"`
|
||||
AttestationCount uint32 `json:"attestation_count" yaml:"attestation_count"`
|
||||
}
|
||||
|
||||
// NewAnchorCredential constructs an AnchorCredential for an Anchor-tier
|
||||
// Partner (D-038, A-305). The custody-provider-id is set to "" (empty)
|
||||
// because the x/hub custody service is NOT live until P5/v0.4 (A-304:
|
||||
// the field is typed-but-empty in the v0.3 skeleton; the hub is live in
|
||||
// P5, so the field exists but is not validated against hub yet). The
|
||||
// attestation-count is set to 0 (no attestations in the skeleton). The
|
||||
// caller supplies the anchor-id (the Anchor Partner's ID) and the opaque
|
||||
// credential-uri.
|
||||
func NewAnchorCredential(anchorID, credentialURI string) AnchorCredential {
|
||||
return AnchorCredential{
|
||||
AnchorID: anchorID,
|
||||
CustodyProviderID: "", // empty — hub not live until P5/v0.4 (A-304)
|
||||
CredentialURI: credentialURI,
|
||||
AttestationCount: 0, // no attestations in the skeleton
|
||||
}
|
||||
}
|
||||
|
||||
// Params for the partner module (skeleton — no tunables in v0.2).
|
||||
type Params struct{}
|
||||
|
||||
|
||||
@@ -411,6 +411,151 @@ func TestLexiconNoBannedTermsInPartnerTestFile(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// --- v0.3 Partner extension (P4-04, D-038, A-305) — AnchorCredential -------------
|
||||
//
|
||||
// The following tests extend the v0.2 partner tests with the v0.3
|
||||
// AnchorCredential struct (D-038). The existing v0.1/v0.2 tests above
|
||||
// MUST remain green — no regression. The PartnerTier enum (4 tiers) is
|
||||
// locked since v0.2; v0.3 adds the AnchorCredential STRUCT only (no new
|
||||
// tier — A-305).
|
||||
|
||||
// TestAnchorCredentialStructFields asserts the AnchorCredential struct
|
||||
// carries all required fields (anchor-id, custody-provider-id,
|
||||
// credential-uri, attestation-count) per D-038/A-305.
|
||||
func TestAnchorCredentialStructFields(t *testing.T) {
|
||||
c := types.AnchorCredential{
|
||||
AnchorID: "anchor-1",
|
||||
CustodyProviderID: "hub-custody-1",
|
||||
CredentialURI: "oy:cred:anchor-1/jurisdiction/EU-MiCA",
|
||||
AttestationCount: 3,
|
||||
}
|
||||
if c.AnchorID != "anchor-1" {
|
||||
t.Errorf("AnchorID = %q", c.AnchorID)
|
||||
}
|
||||
if c.CustodyProviderID != "hub-custody-1" {
|
||||
t.Errorf("CustodyProviderID = %q", c.CustodyProviderID)
|
||||
}
|
||||
if c.CredentialURI != "oy:cred:anchor-1/jurisdiction/EU-MiCA" {
|
||||
t.Errorf("CredentialURI = %q", c.CredentialURI)
|
||||
}
|
||||
if c.AttestationCount != 3 {
|
||||
t.Errorf("AttestationCount = %d, want 3", c.AttestationCount)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAnchorCredentialAnchorIDIsString asserts the AnchorID field is an
|
||||
// opaque string (by-ID-string ref to a Partner with Tier=Anchor — G-003),
|
||||
// NOT a typed Partner import. This locks the by-ID-string invariant at
|
||||
// the type level.
|
||||
func TestAnchorCredentialAnchorIDIsString(t *testing.T) {
|
||||
c := types.AnchorCredential{AnchorID: "partner-9"}
|
||||
c.AnchorID = "partner-2"
|
||||
if c.AnchorID != "partner-2" {
|
||||
t.Errorf("AnchorID = %q, want %q (must be plain string — G-003)", c.AnchorID, "partner-2")
|
||||
}
|
||||
}
|
||||
|
||||
// TestAnchorCredentialCustodyProviderIDIsString asserts the
|
||||
// CustodyProviderID field is an opaque string (by-ID-string ref to an
|
||||
// x/hub custody service — A-304/G-003), NOT a typed x/hub import.
|
||||
func TestAnchorCredentialCustodyProviderIDIsString(t *testing.T) {
|
||||
c := types.AnchorCredential{CustodyProviderID: "hub-custody-9"}
|
||||
c.CustodyProviderID = "hub-custody-2"
|
||||
if c.CustodyProviderID != "hub-custody-2" {
|
||||
t.Errorf("CustodyProviderID = %q, want %q (must be plain string — A-304/G-003)", c.CustodyProviderID, "hub-custody-2")
|
||||
}
|
||||
}
|
||||
|
||||
// TestNewAnchorCredentialConstruction asserts NewAnchorCredential sets
|
||||
// the anchor-id and credential-uri from the constructor args, AND sets
|
||||
// custody-provider-id to "" (empty — hub not live until P5/v0.4 per
|
||||
// A-304), AND attestation-count to 0 (no attestations in the skeleton).
|
||||
func TestNewAnchorCredentialConstruction(t *testing.T) {
|
||||
c := types.NewAnchorCredential("anchor-1", "oy:cred:anchor-1/EU-MiCA")
|
||||
if c.AnchorID != "anchor-1" {
|
||||
t.Errorf("AnchorID = %q, want %q", c.AnchorID, "anchor-1")
|
||||
}
|
||||
if c.CredentialURI != "oy:cred:anchor-1/EU-MiCA" {
|
||||
t.Errorf("CredentialURI = %q, want %q", c.CredentialURI, "oy:cred:anchor-1/EU-MiCA")
|
||||
}
|
||||
// custody-provider-id must be EMPTY in the skeleton (A-304: hub not
|
||||
// live until P5/v0.4).
|
||||
if c.CustodyProviderID != "" {
|
||||
t.Errorf("CustodyProviderID = %q, want empty (A-304: hub not live until P5)", c.CustodyProviderID)
|
||||
}
|
||||
// attestation-count must be 0 in the skeleton.
|
||||
if c.AttestationCount != 0 {
|
||||
t.Errorf("AttestationCount = %d, want 0 (skeleton)", c.AttestationCount)
|
||||
}
|
||||
}
|
||||
|
||||
// TestNewAnchorCredentialCustodyProviderIDEmptyInvariant asserts the
|
||||
// A-304 invariant: NewAnchorCredential ALWAYS sets custody-provider-id to
|
||||
// "" regardless of inputs (the hub is not live until P5/v0.4; the field
|
||||
// is typed-but-empty in the v0.3 skeleton). This is the dependency edge
|
||||
// that forces P4 before P5 (D-044): x/partner Anchor lands in P4, x/hub
|
||||
// in P5.
|
||||
func TestNewAnchorCredentialCustodyProviderIDEmptyInvariant(t *testing.T) {
|
||||
cases := []struct {
|
||||
anchorID string
|
||||
credURI string
|
||||
}{
|
||||
{"anchor-1", "oy:cred:a/EU-MiCA"},
|
||||
{"anchor-2", "oy:cred:a/US-SOC2"},
|
||||
{"", ""},
|
||||
{"anchor-3", ""},
|
||||
}
|
||||
for _, c := range cases {
|
||||
got := types.NewAnchorCredential(c.anchorID, c.credURI)
|
||||
if got.CustodyProviderID != "" {
|
||||
t.Errorf("NewAnchorCredential(%q,%q): CustodyProviderID = %q, want empty (A-304 LOCKED)", c.anchorID, c.credURI, got.CustodyProviderID)
|
||||
}
|
||||
if got.AttestationCount != 0 {
|
||||
t.Errorf("NewAnchorCredential(%q,%q): AttestationCount = %d, want 0 (skeleton)", c.anchorID, c.credURI, got.AttestationCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestNewAnchorCredentialAttestationCountZero asserts the constructor sets
|
||||
// attestation-count to 0 (no attestations in the skeleton; attestations
|
||||
// are a v0.4 keeper concern).
|
||||
func TestNewAnchorCredentialAttestationCountZero(t *testing.T) {
|
||||
c := types.NewAnchorCredential("anchor-1", "oy:cred:anchor-1/x")
|
||||
if c.AttestationCount != 0 {
|
||||
t.Errorf("AttestationCount = %d, want 0 (skeleton — attestations are v0.4)", c.AttestationCount)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAnchorCredentialZeroValue asserts the zero-value AnchorCredential
|
||||
// has empty strings and a 0 attestation-count.
|
||||
func TestAnchorCredentialZeroValue(t *testing.T) {
|
||||
var c types.AnchorCredential
|
||||
if c.AnchorID != "" || c.CustodyProviderID != "" || c.CredentialURI != "" {
|
||||
t.Error("zero-value AnchorCredential should have empty string fields")
|
||||
}
|
||||
if c.AttestationCount != 0 {
|
||||
t.Errorf("zero-value AttestationCount = %d, want 0", c.AttestationCount)
|
||||
}
|
||||
}
|
||||
|
||||
// TestPartnerTierCountStillFour is the v0.3 REGRESSION test (A-305): the
|
||||
// PartnerTier enum is LOCKED at 4 tiers since v0.2; v0.3 adds the
|
||||
// AnchorCredential STRUCT, NOT a new tier. This test asserts the count
|
||||
// is still 4 (no new tier added by the v0.3 extension).
|
||||
func TestPartnerTierCountStillFour(t *testing.T) {
|
||||
if types.PartnerTierCount != 4 {
|
||||
t.Errorf("PartnerTierCount = %d, expected 4 (A-305: v0.3 adds AnchorCredential struct, not a tier)", types.PartnerTierCount)
|
||||
}
|
||||
all := types.AllPartnerTiers()
|
||||
if len(all) != 4 {
|
||||
t.Errorf("AllPartnerTiers() len = %d, expected 4 (A-305 regression)", len(all))
|
||||
}
|
||||
// Anchor must still be the 4th tier (no new tier added before/after it).
|
||||
if all[3] != types.TierAnchor {
|
||||
t.Errorf("AllPartnerTiers()[3] = %q, want %q (Anchor must remain 4th tier)", all[3], types.TierAnchor)
|
||||
}
|
||||
}
|
||||
|
||||
// packageDir resolves a Go import path to its filesystem directory by
|
||||
// walking up from this test file (v0.2 skeleton has zero external deps).
|
||||
func packageDir(t *testing.T, importPath string) string {
|
||||
|
||||
Reference in New Issue
Block a user