Research-Stack/0-Core-Formalism/lean/Semantics/Semantics/CanonSerialization.lean

336 lines
11 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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