mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
336 lines
11 KiB
Text
336 lines
11 KiB
Text
import Semantics.FixedPoint
|
||
import Semantics.Canon
|
||
|
||
namespace Semantics.ENE
|
||
|
||
/-! # Canonical Serialization
|
||
Binary serialization, encoding, and filtering for canonical forms.
|
||
Split from Canon.lean per swarm suggestion (USER AUTHORIZED).
|
||
-/
|
||
|
||
-- Canonical Adapter / Normalization Layer
|
||
--
|
||
-- Converts raw inputs into deterministic, semantically bounded canonical forms.
|
||
-- This layer prevents "weird machines via inputs" by:
|
||
-- 1. Normalizing representation (endianness, field order, encoding)
|
||
-- 2. Filtering adversarial or irrelevant structure
|
||
-- 3. Certifying determinism via proof
|
||
|
||
/-- Endianness policy for canonical serialization. -/
|
||
inductive EndianPolicy
|
||
| big
|
||
| little
|
||
| host
|
||
deriving Repr, BEq
|
||
|
||
/-- Bit ordering for canonical serialization. -/
|
||
inductive BitOrder
|
||
| msb0
|
||
| lsb0
|
||
deriving Repr, BEq
|
||
|
||
/-- The canonical policy is big-endian, msb0. -/
|
||
def canonicalEndian : EndianPolicy := EndianPolicy.big
|
||
|
||
def canonicalBitOrder : BitOrder := BitOrder.msb0
|
||
|
||
/-- Kinds of fields in a canonical record. -/
|
||
inductive FieldKind
|
||
| int (bits : Nat) (signed : Bool)
|
||
| nat (bits : Nat)
|
||
| q16_16
|
||
| float64
|
||
| text
|
||
| bool
|
||
| blob (size : Nat)
|
||
deriving Repr, BEq
|
||
|
||
/-- Specification for a single field. -/
|
||
structure FieldSpec where
|
||
name : String
|
||
kind : FieldKind
|
||
required : Bool := true
|
||
deriving Repr, BEq
|
||
|
||
/-- A schema defining the canonical shape of a record. -/
|
||
structure RecordSchema where
|
||
name : String
|
||
fields : List FieldSpec
|
||
endian : EndianPolicy := canonicalEndian
|
||
bitOrder : BitOrder := canonicalBitOrder
|
||
deriving Repr, BEq
|
||
|
||
/-- A value in canonical form. -/
|
||
inductive CanonicalValue
|
||
| int (i : Int) (bits : Nat) (signed : Bool)
|
||
| nat (n : Nat) (bits : Nat)
|
||
| q16_16 (q : Q16_16)
|
||
| float64 (f : Float)
|
||
| text (s : String)
|
||
| bool (b : Bool)
|
||
| blob (b : ByteArray)
|
||
deriving BEq
|
||
|
||
/-- A field with its canonical value. -/
|
||
structure CanonicalField where
|
||
spec : FieldSpec
|
||
value : CanonicalValue
|
||
deriving BEq
|
||
|
||
/-- The complete canonical binary representation of a record. -/
|
||
structure CanonicalBinaryForm where
|
||
schema : RecordSchema
|
||
fields : List CanonicalField
|
||
deriving BEq
|
||
|
||
/-- Source information tracking provenance. -/
|
||
structure SourceInfo where
|
||
origin : String
|
||
timestamp : UInt64
|
||
trustLevel : Float -- TODO(lean-port): port to Q16_16
|
||
deriving Repr, BEq
|
||
|
||
/-- Errors that can occur during normalization. -/
|
||
inductive NormalizeError
|
||
| typeMismatch (expected : String) (actual : String)
|
||
| overflow (value : String) (limit : String)
|
||
| missingRequiredField (name : String)
|
||
| adversarialStructure (reason : String)
|
||
| unsupportedEncoding (details : String)
|
||
deriving Repr, BEq
|
||
|
||
abbrev NormalizeResult (α : Type) := Except NormalizeError α
|
||
|
||
/-- Source values before normalization. -/
|
||
inductive SourceValue
|
||
| int (i : Int)
|
||
| nat (n : Nat)
|
||
| q16_16 (q : Q16_16)
|
||
| float64 (f : Float)
|
||
| text (s : String)
|
||
| bool (b : Bool)
|
||
| blob (b : ByteArray)
|
||
| null
|
||
deriving BEq
|
||
|
||
/-- A field in its source form. -/
|
||
structure SourceField where
|
||
name : String
|
||
value : SourceValue
|
||
deriving BEq
|
||
|
||
-- Serialization helpers
|
||
|
||
def pushByte (out : ByteArray) (b : UInt8) : ByteArray := out.push b
|
||
|
||
def encodeU16BE (x : UInt16) : ByteArray :=
|
||
let b0 := UInt8.ofNat ((x.toNat >>> 8) &&& 0xFF)
|
||
let b1 := UInt8.ofNat (x.toNat &&& 0xFF)
|
||
ByteArray.empty.push b0 |>.push b1
|
||
|
||
def encodeU32BE (x : UInt32) : ByteArray :=
|
||
let b0 := UInt8.ofNat ((x.toNat >>> 24) &&& 0xFF)
|
||
let b1 := UInt8.ofNat ((x.toNat >>> 16) &&& 0xFF)
|
||
let b2 := UInt8.ofNat ((x.toNat >>> 8) &&& 0xFF)
|
||
let b3 := UInt8.ofNat (x.toNat &&& 0xFF)
|
||
ByteArray.empty.push b0 |>.push b1 |>.push b2 |>.push b3
|
||
|
||
def encodeU64BE (x : UInt64) : ByteArray :=
|
||
let b0 := UInt8.ofNat ((x.toNat >>> 56) &&& 0xFF)
|
||
let b1 := UInt8.ofNat ((x.toNat >>> 48) &&& 0xFF)
|
||
let b2 := UInt8.ofNat ((x.toNat >>> 40) &&& 0xFF)
|
||
let b3 := UInt8.ofNat ((x.toNat >>> 32) &&& 0xFF)
|
||
let b4 := UInt8.ofNat ((x.toNat >>> 24) &&& 0xFF)
|
||
let b5 := UInt8.ofNat ((x.toNat >>> 16) &&& 0xFF)
|
||
let b6 := UInt8.ofNat ((x.toNat >>> 8) &&& 0xFF)
|
||
let b7 := UInt8.ofNat (x.toNat &&& 0xFF)
|
||
ByteArray.empty.push b0 |>.push b1 |>.push b2 |>.push b3 |>.push b4 |>.push b5 |>.push b6 |>.push b7
|
||
|
||
def encodeNatBE (width : Nat) (n : Nat) : ByteArray :=
|
||
let rec loop (i : Nat) (acc : ByteArray) : ByteArray :=
|
||
match i with
|
||
| 0 => acc
|
||
| i' + 1 =>
|
||
let byte := UInt8.ofNat ((n >>> (8 * i')) &&& 0xFF)
|
||
loop i' (acc.push byte)
|
||
loop width ByteArray.empty
|
||
|
||
def encodeText (s : String) : ByteArray :=
|
||
s.toUTF8
|
||
|
||
def fieldKindTag (k : FieldKind) : UInt8 :=
|
||
match k with
|
||
| FieldKind.int _ _ => 1
|
||
| FieldKind.nat _ => 2
|
||
| FieldKind.q16_16 => 3
|
||
| FieldKind.float64 => 4
|
||
| FieldKind.text => 5
|
||
| FieldKind.bool => 6
|
||
| FieldKind.blob _ => 7
|
||
|
||
def intFitsSigned (bits : Nat) (i : Int) : Bool :=
|
||
let limit := 1 <<< (bits - 1)
|
||
i ≥ -limit && i < limit
|
||
|
||
def serializeCanonicalValue (v : CanonicalValue) : NormalizeResult ByteArray :=
|
||
match v with
|
||
| CanonicalValue.int i bits signed =>
|
||
if signed then
|
||
if intFitsSigned bits i then
|
||
.ok (encodeNatBE bits (if i < 0 then (1 <<< bits) + i.toNat else i.toNat))
|
||
else
|
||
.error (NormalizeError.overflow (toString i) ("int" ++ toString bits))
|
||
else
|
||
if i ≥ 0 && i < (1 <<< bits : Int) then
|
||
.ok (encodeNatBE bits i.toNat)
|
||
else
|
||
.error (NormalizeError.overflow (toString i) ("uint" ++ toString bits))
|
||
| CanonicalValue.nat n bits =>
|
||
if n < (1 <<< bits) then
|
||
.ok (encodeNatBE bits n)
|
||
else
|
||
.error (NormalizeError.overflow (toString n) ("uint" ++ toString bits))
|
||
| CanonicalValue.q16_16 q =>
|
||
.ok (encodeU32BE q.val)
|
||
| CanonicalValue.float64 f =>
|
||
.ok (encodeU64BE (Float.toUInt64 f))
|
||
| CanonicalValue.text s =>
|
||
.ok (encodeText s)
|
||
| CanonicalValue.bool true =>
|
||
.ok (ByteArray.empty.push 1)
|
||
| CanonicalValue.bool false =>
|
||
.ok (ByteArray.empty.push 0)
|
||
| CanonicalValue.blob b =>
|
||
.ok b
|
||
|
||
def serializeCanonicalField (f : CanonicalField) : NormalizeResult ByteArray := do
|
||
let tagBytes := ByteArray.empty.push (fieldKindTag f.spec.kind)
|
||
let nameBytes := encodeText f.spec.name
|
||
let valueBytes ← serializeCanonicalValue f.value
|
||
pure (tagBytes ++ nameBytes ++ valueBytes)
|
||
|
||
def serializeCanonicalBinaryForm (cbf : CanonicalBinaryForm) : NormalizeResult ByteArray := do
|
||
let schemaBytes := encodeText cbf.schema.name
|
||
let rec serializeFields (fields : List CanonicalField) (acc : ByteArray) : NormalizeResult ByteArray :=
|
||
match fields with
|
||
| [] => pure acc
|
||
| f :: rest => do
|
||
let fieldBytes ← serializeCanonicalField f
|
||
serializeFields rest (acc ++ fieldBytes)
|
||
let fieldBytes ← serializeFields cbf.fields ByteArray.empty
|
||
pure (schemaBytes ++ fieldBytes)
|
||
|
||
def fieldKindCoreSafe (k : FieldKind) : Bool :=
|
||
match k with
|
||
| FieldKind.int bits _signed => bits ≤ 64
|
||
| FieldKind.nat bits => bits ≤ 64
|
||
| FieldKind.q16_16 => true
|
||
| FieldKind.float64 => true
|
||
| FieldKind.text => true
|
||
| FieldKind.bool => true
|
||
| FieldKind.blob size => size ≤ 65536
|
||
|
||
def uniqueFieldNames : List FieldSpec → Bool
|
||
| [] => true
|
||
| f :: rest =>
|
||
!rest.any (fun g => g.name == f.name) && uniqueFieldNames rest
|
||
|
||
/-- A schema is core-admissible when it is deterministic, canonical, and fixed-point-safe. -/
|
||
def RecordSchema.coreAdmissible (schema : RecordSchema) : Bool :=
|
||
schema.endian == canonicalEndian &&
|
||
schema.bitOrder == canonicalBitOrder &&
|
||
uniqueFieldNames schema.fields &&
|
||
schema.fields.all (fun field => field.name != "" && fieldKindCoreSafe field.kind)
|
||
|
||
/-- `q16_16` is accepted by the core-safe schema checker. -/
|
||
theorem q16_16_field_kind_core_safe :
|
||
fieldKindCoreSafe FieldKind.q16_16 = true := by
|
||
native_decide
|
||
|
||
-- Determinism and identity theorems
|
||
|
||
/-- Two canonical binary forms have the same identity if their schemas and
|
||
serialized bytes are equal. -/
|
||
def SameIdentity (a b : CanonicalBinaryForm) : Prop :=
|
||
a.schema = b.schema ∧
|
||
(∀ ha hb, serializeCanonicalBinaryForm a = .ok ha → serializeCanonicalBinaryForm b = .ok hb → ha = hb)
|
||
|
||
/-- A canonical binary form is canonical if it serializes deterministically. -/
|
||
def IsCanonical (cbf : CanonicalBinaryForm) : Prop :=
|
||
∀ h1 h2, serializeCanonicalBinaryForm cbf = .ok h1 → serializeCanonicalBinaryForm cbf = .ok h2 → h1 = h2
|
||
|
||
-- Serialization of a given schema and source is deterministic:
|
||
-- the same input always produces the same canonical bytes.
|
||
-- COMMENTED OUT: References undefined canonicalize function.
|
||
-- TODO(lean-port): Implement canonicalize function or remove this theorem.
|
||
-- theorem canonicalize_is_deterministic
|
||
-- (schema : RecordSchema)
|
||
-- (src : List SourceField)
|
||
-- (cbf : CanonicalBinaryForm)
|
||
-- (_h : canonicalize schema src = .ok cbf) :
|
||
-- IsCanonical cbf := by
|
||
-- unfold IsCanonical
|
||
-- intros h1 h2 e1 e2
|
||
-- have heq : h1 = h2 := by
|
||
-- have h : @Except.ok NormalizeError ByteArray h1 = @Except.ok NormalizeError ByteArray h2 := by
|
||
-- rw [← e1, ← e2]
|
||
-- injection h
|
||
-- exact heq
|
||
|
||
-- Filtering for adversarial / irrelevant structure
|
||
|
||
/-- Relevance classification for source fields. -/
|
||
inductive Relevance
|
||
| relevant -- Maps directly to a semantic atom
|
||
| structural -- Needed for parsing but not meaning
|
||
| metadata -- Provenance, not content
|
||
| noise -- Does not contribute to meaning
|
||
| adversarial -- Attempts to induce unintended computation
|
||
deriving Repr, BEq
|
||
|
||
/-- A filtered field carries a relevance judgment. -/
|
||
structure FilteredField where
|
||
field : SourceField
|
||
relevance : Relevance
|
||
reason : String
|
||
deriving BEq
|
||
|
||
/-- Result of filtering a source record. -/
|
||
structure FilterResult where
|
||
kept : List FilteredField
|
||
dropped : List FilteredField
|
||
safe : Bool -- true if no adversarial fields detected
|
||
deriving BEq
|
||
|
||
/-- A filtering rule assigns relevance to a source field. -/
|
||
structure FilterRule where
|
||
name : String
|
||
predicate : SourceField → Bool
|
||
relevance : Relevance
|
||
reason : String
|
||
|
||
/-- Apply a list of filter rules to source fields. -/
|
||
def applyFilters (rules : List FilterRule) (src : List SourceField) : FilterResult :=
|
||
let results := src.map (λ f =>
|
||
match rules.find? (λ r => r.predicate f) with
|
||
| some r => { field := f, relevance := r.relevance, reason := r.reason }
|
||
| none => { field := f, relevance := Relevance.relevant, reason := "default" }
|
||
)
|
||
{
|
||
kept := results.filter (λ r => r.relevance != Relevance.noise && r.relevance != Relevance.adversarial),
|
||
dropped := results.filter (λ r => r.relevance == Relevance.noise || r.relevance == Relevance.adversarial),
|
||
safe := !(results.any (λ r => r.relevance == Relevance.adversarial))
|
||
}
|
||
|
||
-- If filtering marks everything safe, then no kept field is adversarial.
|
||
-- COMMENTED OUT: Contains proof placeholder - requires proof.
|
||
-- TODO(lean-port): Re-enable when proof is completed.
|
||
-- theorem filter_safe_no_adversarial_kept
|
||
-- (rules : List FilterRule)
|
||
-- (src : List SourceField)
|
||
-- (_h : (applyFilters rules src).safe = true) :
|
||
-- ∀ r ∈ (applyFilters rules src).kept, r.relevance != Relevance.adversarial := by
|
||
-- unfold applyFilters
|
||
-- intro r hr
|
||
|
||
end Semantics.ENE
|