owpengram-server/internal/rpc/layer_inheritance.go

614 lines
20 KiB
Go

package rpc
import (
"context"
"encoding/binary"
"fmt"
"sort"
"time"
"github.com/iamxvbaba/td/tg"
"go.uber.org/zap"
"telesrv/internal/domain"
"telesrv/internal/store"
)
const (
inheritedAuthKeyLayerSingleflightPrefix = "inherited-layer:"
durableInheritedAuthKeyLayerSingleflightPrefix = "durable-inherited-layer:"
)
const authLayerCommitStripes = 4096
const (
authLayerPublicationTimeout = 5 * time.Second
maxAuthLayerEvidenceEntries = maxAuthInfoEntries
)
type inheritedAuthKeyLayerResult struct {
layer int
found bool
}
// PublishAdmittedLayerProfileEvidence commits protocol evidence before an
// admitted request can be delayed by the business scheduler or replaced by an
// initConnection rewrap alias. admissionSeq is allocated by mtprotoedge when
// the fresh request obtains flight ownership; it orders process-local/no-store
// publication and supplies the active-admission safe floor. In durable mode the
// store's ObservationID is the cross-session/cross-process publication order.
// Replays reuse the owner's admission sequence and do not publish again.
func (r *Router) PublishAdmittedLayerProfileEvidence(
ctx context.Context,
rawAuthKeyID [8]byte,
sessionID int64,
msgID int64,
admissionSeq uint64,
safeFloor uint64,
layer int,
) error {
if r == nil || rawAuthKeyID == ([8]byte{}) || sessionID == 0 {
return fmt.Errorf("invalid admitted layer evidence identity")
}
if admissionSeq == 0 {
return fmt.Errorf("invalid admitted layer evidence sequence")
}
if safeFloor == 0 || safeFloor > admissionSeq {
return fmt.Errorf("invalid admitted layer evidence safe floor %d for sequence %d", safeFloor, admissionSeq)
}
if !isSupportedLayer(layer) {
return fmt.Errorf("unsupported admitted layer evidence %d", layer)
}
// The edge tracker owns admission lifecycle and supplies an exact global
// retirement floor. Advance it even if this particular proof became stale:
// the floor itself remains valid capacity-reclamation evidence.
r.clientInfoMu.Lock()
if safeFloor > r.authLayerSafeEvictionFloor {
r.authLayerSafeEvictionFloor = safeFloor
}
r.clientInfoMu.Unlock()
if r.deps.AuthKeySessionLayers == nil {
currentLayer, currentMsgID, ok := r.NegotiatedSessionLayerEvidence(rawAuthKeyID, sessionID)
if !ok || currentLayer != layer || (msgID > 0 && currentMsgID != msgID) {
// A newer correction or bounded registry eviction makes this evidence
// stale, not invalid. The admitted request keeps its immutable codec but
// no longer owns shared mutable state.
return nil
}
}
if ctx == nil {
ctx = context.Background()
}
publicationSequence := admissionSeq
publicationDurable := false
if r.deps.AuthKeySessionLayers != nil {
current, found, err := r.deps.AuthKeySessionLayers.GetSessionLayer(ctx, rawAuthKeyID, sessionID)
if err != nil {
// The durable transaction has already committed the protocol fact.
// A failed cache revalidation must not reject/re-execute the RPC; leave
// this process cache untouched and let the next session read primary.
if r.log != nil {
r.log.Warn("revalidate durable auth-key Layer default for local publication failed",
zap.String("raw_auth_key_id", fmt.Sprintf("%x", rawAuthKeyID[:])),
zap.Int64("session_id", sessionID),
zap.Error(err))
}
return nil
}
if !found || !current.SharedDefault || current.Layer != layer || current.MessageID != msgID {
// Another session/process has already committed a newer shared
// default. This request keeps its immutable result codec but cannot
// overwrite process-local inherited state.
return nil
}
if current.ObservationID <= 0 {
return store.ErrAuthKeySessionLayerInvalid
}
publicationSequence = uint64(current.ObservationID)
publicationDurable = true
}
effectiveAuthKeyID := rawAuthKeyID
if r.deps.Auth != nil {
resolveCtx, cancel := context.WithTimeout(ctx, authLayerPublicationTimeout)
resolved, found, err := r.deps.Auth.ResolveAuthKey(resolveCtx, rawAuthKeyID)
cancel()
switch {
case err != nil:
// Persistence identity lookup is availability metadata, not proof of
// whether this request's generated wire profile is valid. Publish the
// raw-key default now; a later bind/explicit observation can retry the
// durable permanent-key normalization.
if r.log != nil {
r.log.Warn("resolve auth key for admitted layer evidence failed; publishing raw default",
zap.String("raw_auth_key_id", fmt.Sprintf("%x", rawAuthKeyID[:])),
zap.Uint64("admission_seq", admissionSeq),
zap.Error(err))
}
case found && resolved == ([8]byte{}):
return fmt.Errorf("admitted layer evidence resolved an empty permanent auth key")
case found:
effectiveAuthKeyID = resolved
}
}
unlockCommit := r.lockAuthLayerCommit(rawAuthKeyID, effectiveAuthKeyID)
defer unlockCommit()
// Freeze may have admitted a newer same-session correction while identity
// resolution was in flight. The old request stays decodable, but can no
// longer publish shared state.
if r.deps.AuthKeySessionLayers == nil {
if currentLayer, currentMsgID, ok := r.NegotiatedSessionLayerEvidence(rawAuthKeyID, sessionID); !ok || currentLayer != layer || (msgID > 0 && currentMsgID != msgID) {
return nil
}
}
r.clientInfoMu.Lock()
applied, err := r.claimAuthLayerDefaultEvidenceLocked(
layer, publicationSequence, publicationDurable, rawAuthKeyID, effectiveAuthKeyID,
)
if err == nil && applied {
if r.clientInfo == nil {
r.clientInfo = make(map[clientInfoSessionKey]clientSessionInfo)
}
key := clientInfoSessionKey{rawAuthKeyID: rawAuthKeyID, sessionID: sessionID}
info, exists := r.clientInfo[key]
if !exists {
evictMapEntryIfFullLocked(r.clientInfo, maxClientInfoEntries)
}
info.layer = layer
if publicationDurable {
info.layerObservationID = int64(publicationSequence)
} else {
info.layerAdmissionSeq = admissionSeq
}
r.clientInfo[key] = info
}
r.clientInfoMu.Unlock()
if err != nil || !applied {
return err
}
if binder, ok := r.deps.Sessions.(AuthKeyLayerBinder); ok {
binder.SeedInheritedLayerForRawAuthKey(rawAuthKeyID, layer)
}
if effectiveAuthKeyID != rawAuthKeyID {
if binder, ok := r.deps.Sessions.(BusinessAuthKeyLayerBinder); ok {
binder.SeedInheritedLayerForBusinessAuthKey(effectiveAuthKeyID, layer)
} else if binder, ok := r.deps.Sessions.(AuthKeyLayerBinder); ok {
// Compatibility fallback for test doubles/older embedders where a
// permanent key is itself also the raw connection key.
binder.SeedInheritedLayerForRawAuthKey(effectiveAuthKeyID, layer)
}
}
// Production durable evidence already updated raw/permanent auth_keys and
// the authorization mirror in one transaction. The legacy path remains only
// for isolated Router tests that intentionally omit the protocol store.
if r.deps.AuthKeySessionLayers == nil {
r.persistAdmittedAuthKeyLayer(ctx, effectiveAuthKeyID, layer, admissionSeq)
if effectiveAuthKeyID != rawAuthKeyID {
r.persistAdmittedAuthKeyLayer(ctx, rawAuthKeyID, layer, admissionSeq)
}
}
return nil
}
func (r *Router) claimAuthLayerDefaultEvidenceLocked(
layer int,
sequence uint64,
durable bool,
authKeyIDs ...[8]byte,
) (bool, error) {
if r.authLayerEvidence == nil {
r.authLayerEvidence = make(map[[8]byte]authLayerDefaultEvidence)
}
ids := make([][8]byte, 0, len(authKeyIDs))
for _, authKeyID := range authKeyIDs {
if authKeyID == ([8]byte{}) {
continue
}
duplicate := false
for _, existing := range ids {
if existing == authKeyID {
duplicate = true
break
}
}
if !duplicate {
ids = append(ids, authKeyID)
}
}
for _, authKeyID := range ids {
info := r.authInfo[authKeyID]
if durable {
if info.layerObservationID < 0 {
return false, fmt.Errorf("invalid cached auth-key layer observation %d", info.layerObservationID)
}
if uint64(info.layerObservationID) > sequence {
return false, nil
}
if info.layerObservationID > 0 && uint64(info.layerObservationID) == sequence && info.layer != layer {
return false, fmt.Errorf("auth-key layer evidence conflict at durable observation %d: current=%d requested=%d", sequence, info.layer, layer)
}
} else {
if info.layerAdmissionSeq > sequence {
return false, nil
}
if info.layerAdmissionSeq == sequence && info.layerAdmissionSeq > 0 && info.layer != layer {
return false, fmt.Errorf("auth-key layer evidence conflict at admission sequence %d: current=%d requested=%d", sequence, info.layer, layer)
}
}
current, exists := r.authLayerEvidence[authKeyID]
if !exists {
continue
}
if current.durable && !durable {
return false, nil
}
if durable && !current.durable {
continue
}
if current.sequence > sequence {
return false, nil
}
if current.sequence == sequence && current.layer != layer {
kind := "admission sequence"
if durable {
kind = "durable observation"
}
return false, fmt.Errorf("auth-key layer evidence conflict at %s %d: current=%d requested=%d", kind, sequence, current.layer, layer)
}
}
missing := 0
for _, authKeyID := range ids {
if _, exists := r.authLayerEvidence[authKeyID]; !exists {
missing++
}
}
if len(r.authLayerEvidence)+missing > maxAuthLayerEvidenceEntries {
for len(r.authLayerEvidence)+missing > maxAuthLayerEvidenceEntries {
var (
candidate [8]byte
candidateSeq uint64
haveCandidate bool
)
for currentID, current := range r.authLayerEvidence {
keep := false
for _, id := range ids {
if currentID == id {
keep = true
break
}
}
if keep {
continue
}
if !current.durable && current.sequence >= r.authLayerSafeEvictionFloor {
continue
}
if !haveCandidate || current.sequence < candidateSeq {
candidate, candidateSeq, haveCandidate = currentID, current.sequence, true
}
}
if !haveCandidate {
if r.log != nil {
r.log.Warn("auth-key layer evidence capacity exhausted; skip shared default",
zap.Int("entries", len(r.authLayerEvidence)),
zap.Uint64("evidence_sequence", sequence),
zap.Bool("durable", durable),
zap.Int("layer", layer))
}
return false, nil
}
delete(r.authLayerEvidence, candidate)
}
}
for _, authKeyID := range ids {
r.authLayerEvidence[authKeyID] = authLayerDefaultEvidence{
layer: layer, sequence: sequence, durable: durable,
}
if durable {
r.rememberAuthClientLayerObservationLocked(authKeyID, layer, int64(sequence))
} else {
r.rememberAuthClientLayerAtLocked(authKeyID, layer, sequence)
}
}
return true, nil
}
func (r *Router) persistAdmittedAuthKeyLayer(ctx context.Context, authKeyID [8]byte, layer int, admissionSeq uint64) {
if r.deps.Auth == nil || authKeyID == ([8]byte{}) {
return
}
persistCtx, cancel := context.WithTimeout(ctx, authLayerPublicationTimeout)
err := r.deps.Auth.UpdateAuthKeyClientInfo(persistCtx, authKeyID, domain.AuthKeyClientInfo{Layer: layer})
cancel()
if err != nil && r.log != nil {
r.log.Warn("persist admitted auth-key layer default failed",
zap.String("auth_key_id", fmt.Sprintf("%x", authKeyID[:])),
zap.Int("layer", layer),
zap.Uint64("admission_seq", admissionSeq),
zap.Error(err))
}
}
func (r *Router) lockAuthLayerCommit(authKeyIDs ...[8]byte) func() {
indices := make([]int, 0, len(authKeyIDs))
for _, authKeyID := range authKeyIDs {
if authKeyID == ([8]byte{}) {
continue
}
index := authLayerCommitIndex(authKeyID)
duplicate := false
for _, existing := range indices {
if existing == index {
duplicate = true
break
}
}
if !duplicate {
indices = append(indices, index)
}
}
sort.Ints(indices)
for _, index := range indices {
r.authLayerCommit[index].Lock()
}
return func() {
for index := len(indices) - 1; index >= 0; index-- {
r.authLayerCommit[indices[index]].Unlock()
}
}
}
func authLayerCommitIndex(authKeyID [8]byte) int {
return int(binary.LittleEndian.Uint64(authKeyID[:]) % authLayerCommitStripes)
}
// ResolveInheritedAuthKeyLayer resolves the durable last-known default for a
// newly-created connection. The input is always the physical/raw auth key. A
// bound temporary key is normalized to its permanent identity before reading
// Layer, so an old raw shadow can never outrank the current permanent default.
//
// found=false means no canonical default exists and permits the edge to inspect
// a raw-key fallback. found=true with layer=0 means the canonical record carries
// a future Layer not generated into this binary; it is authoritative-but-
// unusable and therefore blocks fallback/clamping until fresh supported explicit
// invokeWithLayer evidence or a newer binary establishes a usable profile.
func (r *Router) ResolveInheritedAuthKeyLayer(ctx context.Context, rawAuthKeyID [8]byte) (int, bool, error) {
if r == nil || rawAuthKeyID == ([8]byte{}) {
return 0, false, nil
}
if ctx == nil {
ctx = context.Background()
}
effectiveAuthKeyID := rawAuthKeyID
if r.deps.Auth != nil {
resolved, found, err := r.deps.Auth.ResolveAuthKey(ctx, rawAuthKeyID)
if err != nil {
return 0, false, wrapLayerEvidenceStoreAvailability(err)
}
if found {
if resolved == ([8]byte{}) {
return 0, false, fmt.Errorf("resolved empty permanent auth key")
}
effectiveAuthKeyID = resolved
}
}
layer, found, err := r.resolveAuthKeyLayerDefault(ctx, effectiveAuthKeyID)
if err != nil {
return 0, false, wrapLayerEvidenceStoreAvailability(err)
}
if !found {
return 0, false, nil
}
var durableProjection clientSessionInfo
if r.deps.AuthKeySessionLayers != nil {
// resolveDurableAuthKeyLayerDefault may have completed a stale DB read
// after a newer same-process explicit observation was published. The
// observation-aware cache merge keeps the newer tuple; re-read it before
// returning or projecting to a bound raw identity.
r.clientInfoMu.RLock()
current := r.authInfo[effectiveAuthKeyID]
r.clientInfoMu.RUnlock()
if current.layerObservationID > 0 {
durableProjection = clientSessionInfo{
layer: current.layer,
layerObservationID: current.layerObservationID,
layerBlocked: current.layerBlocked,
layerBlockedByAuthKey: current.layerBlockedByAuthKey,
authKeyInfoChecked: current.authKeyInfoChecked,
authorizationChecked: current.authorizationChecked,
}
switch {
case isSupportedLayer(current.layer):
layer, found = current.layer, true
case current.layerBlocked:
layer, found = 0, true
}
}
}
if layer == 0 {
// found=true with no usable profile is an authoritative future/unsupported
// canonical default. The edge must stay unknown rather than falling back
// to an older raw temporary-key shadow.
return 0, true, nil
}
if effectiveAuthKeyID != rawAuthKeyID {
// This updates only the auth-key default/shadow. Existing session entries
// remain untouched and therefore keep mixed Layer 225/227 profiles.
r.clientInfoMu.Lock()
if durableProjection.layerObservationID > 0 {
r.rememberAuthClientInfoLocked(rawAuthKeyID, durableProjection)
} else {
r.rememberAuthClientLayerLocked(rawAuthKeyID, layer)
}
r.clientInfoMu.Unlock()
}
return layer, true, nil
}
func (r *Router) resolveAuthKeyLayerDefault(ctx context.Context, authKeyID [8]byte) (int, bool, error) {
if r == nil || authKeyID == ([8]byte{}) {
return 0, false, nil
}
if r.deps.AuthKeySessionLayers != nil {
return r.resolveDurableAuthKeyLayerDefault(ctx, authKeyID)
}
if layer, found, resolved := r.cachedAuthKeyLayerResolution(authKeyID); resolved {
return layer, found, nil
}
if r.deps.Auth == nil {
return 0, false, nil
}
v, err, _ := r.authUserSF.Do(inheritedAuthKeyLayerSingleflightPrefix+string(authKeyID[:]), func() (any, error) {
if layer, found, resolved := r.cachedAuthKeyLayerResolution(authKeyID); resolved {
return inheritedAuthKeyLayerResult{layer: layer, found: found}, nil
}
keyInfo, keyFound, err := r.deps.Auth.AuthKeyClientInfo(ctx, authKeyID)
if err != nil {
return inheritedAuthKeyLayerResult{}, err
}
if keyFound {
r.cacheAuthKeyClientInfo(authKeyID, clientSessionInfoFromAuthKeyClientInfo(keyInfo))
if current, ok := r.cachedAuthKeyLayerDefault(authKeyID); ok {
return inheritedAuthKeyLayerResult{layer: current, found: true}, nil
}
if keyInfo.Layer != 0 {
if !isSupportedLayer(keyInfo.Layer) {
r.cacheAuthKeyClientInfo(authKeyID, clientSessionInfo{layerBlocked: true, layerBlockedByAuthKey: true})
return inheritedAuthKeyLayerResult{found: true}, nil
}
return inheritedAuthKeyLayerResult{layer: keyInfo.Layer, found: true}, nil
}
} else {
r.cacheAuthKeyClientInfo(authKeyID, clientSessionInfo{authKeyInfoChecked: true})
}
// auth_keys.layer is the only protocol authority. A zero value means no
// default exists yet; authorization.layer is a materialized device-list
// projection and must never be promoted back into wire evidence.
return inheritedAuthKeyLayerResult{}, nil
})
if err != nil {
return 0, false, err
}
if current, ok := r.cachedAuthKeyLayerDefault(authKeyID); ok {
return current, true, nil
}
result := v.(inheritedAuthKeyLayerResult)
return result.layer, result.found, nil
}
// resolveDurableAuthKeyLayerDefault deliberately re-reads auth_keys for each
// new logical session. A process-local cache has no way to observe a newer
// observation committed by another server process; using it as authority would
// make new sessions inherit an old supported Layer forever (and could hide a
// future authoritative Layer). singleflight only coalesces the concurrent
// startup burst; it does not turn the result into an unbounded authority.
func (r *Router) resolveDurableAuthKeyLayerDefault(ctx context.Context, authKeyID [8]byte) (int, bool, error) {
if r.deps.Auth == nil {
return 0, false, nil
}
v, err, _ := r.authUserSF.Do(durableInheritedAuthKeyLayerSingleflightPrefix+string(authKeyID[:]), func() (any, error) {
keyInfo, keyFound, err := r.deps.Auth.AuthKeyClientInfo(ctx, authKeyID)
if err != nil {
return inheritedAuthKeyLayerResult{}, err
}
if keyFound {
parsed := clientSessionInfoFromAuthKeyClientInfo(keyInfo)
r.cacheAuthKeyClientInfo(authKeyID, parsed)
if keyInfo.Layer != 0 {
if !isSupportedLayer(keyInfo.Layer) {
return inheritedAuthKeyLayerResult{found: true}, nil
}
return inheritedAuthKeyLayerResult{layer: keyInfo.Layer, found: true}, nil
}
}
// Do not normalize old authorization mirrors on read. Missing primary
// evidence stays unknown until a selector advances auth_keys explicitly;
// any legacy repair belongs in an audited migration.
return inheritedAuthKeyLayerResult{}, nil
})
if err != nil {
return 0, false, err
}
result := v.(inheritedAuthKeyLayerResult)
return result.layer, result.found, nil
}
func (r *Router) cachedAuthKeyLayerDefault(authKeyID [8]byte) (int, bool) {
r.clientInfoMu.RLock()
layer := r.authInfo[authKeyID].layer
r.clientInfoMu.RUnlock()
return layer, isSupportedLayer(layer)
}
func (r *Router) cachedAuthKeyLayerResolution(authKeyID [8]byte) (layer int, found, resolved bool) {
r.clientInfoMu.RLock()
info, ok := r.authInfo[authKeyID]
r.clientInfoMu.RUnlock()
if !ok {
return 0, false, false
}
if isSupportedLayer(info.layer) {
return info.layer, true, true
}
if info.layerBlocked {
return 0, true, true
}
if info.authKeyInfoChecked {
return 0, false, true
}
return 0, false, false
}
func (r *Router) cacheAuthKeyClientInfo(authKeyID [8]byte, info clientSessionInfo) {
r.clientInfoMu.Lock()
r.rememberAuthClientInfoLocked(authKeyID, info)
r.clientInfoMu.Unlock()
}
func (r *Router) setAuthKeyLayerDefaults(layer int, authKeyIDs ...[8]byte) {
if !isSupportedLayer(layer) {
return
}
r.clientInfoMu.Lock()
for _, authKeyID := range authKeyIDs {
r.rememberAuthClientLayerLocked(authKeyID, layer)
}
r.clientInfoMu.Unlock()
}
func (r *Router) persistAuthKeyLayerDefaults(ctx context.Context, layer int, authKeyIDs ...[8]byte) {
if r == nil || r.deps.Auth == nil || !isSupportedLayer(layer) {
return
}
seen := make(map[[8]byte]struct{}, len(authKeyIDs))
for _, authKeyID := range authKeyIDs {
if authKeyID == ([8]byte{}) {
continue
}
if _, ok := seen[authKeyID]; ok {
continue
}
seen[authKeyID] = struct{}{}
if err := r.deps.Auth.UpdateAuthKeyClientInfo(ctx, authKeyID, domain.AuthKeyClientInfo{Layer: layer}); err != nil && r.log != nil {
r.log.Warn("persist inherited auth key layer default failed",
zap.String("auth_key_id", fmt.Sprintf("%x", authKeyID[:])),
zap.Int("layer", layer),
zap.Error(err))
}
}
}
func isSupportedLayer(layer int) bool {
if layer <= 0 {
return false
}
profile, ok := tg.ResolveLayerProfile(layer)
return ok && int(profile) == layer
}