karanjahpn
Replace transcript with flat RawEvent design; add multiplexer + differential validator; fix 3 hook-signature bugs; tested end-to-end
99a7168 | // Package validation (engine) provides field-level differential comparison | |
| // between a "legacy" SemanticEvent stream and one derived from | |
| // raw/transcript, for use during a migration/verification period where | |
| // both tracers run side by side. | |
| package validation | |
| import ( | |
| "bytes" | |
| "fmt" | |
| ) | |
| type DifferentialValidator struct{} | |
| func NewDifferentialValidator() *DifferentialValidator { return &DifferentialValidator{} } | |
| func (v *DifferentialValidator) formatMismatch(field string, seq uint64, op string, depth int, exp, act any) string { | |
| buf := new(bytes.Buffer) | |
| fmt.Fprintf(buf, "\n[CRITICAL SEMANTIC DRIFT] Mismatch Isolated Across Stream Pipeline\n") | |
| fmt.Fprintf(buf, "Sequence : %d\n", seq) | |
| fmt.Fprintf(buf, "Event : %s\n", op) | |
| fmt.Fprintf(buf, "Depth : %d\n\n", depth) | |
| fmt.Fprintf(buf, "%-12s %-32s %-32s\n", "Field", "Expected (Legacy)", "Actual (Transcript)") | |
| fmt.Fprintf(buf, "------------------------------------------------------------------------\n") | |
| fmt.Fprintf(buf, "%-12s %-32v %-32v\n", field, exp, act) | |
| return buf.String() | |
| } | |
| // CompareSemanticFields diffs a legacy-tracer-derived event l against a | |
| // transcript-derived event t, returning nil if they match on every field | |
| // SemanticEvent carries. | |
| func (v *DifferentialValidator) CompareSemanticFields(l, t SemanticEvent) error { | |
| if l.Sequence != t.Sequence { | |
| return fmt.Errorf("sequence split: legacy=%d, transcript=%d", l.Sequence, t.Sequence) | |
| } | |
| if l.Opcode != t.Opcode { | |
| return fmt.Errorf("%s", v.formatMismatch("Opcode", l.Sequence, l.Opcode, l.Depth, l.Opcode, t.Opcode)) | |
| } | |
| if l.Depth != t.Depth { | |
| return fmt.Errorf("%s", v.formatMismatch("Depth", l.Sequence, l.Opcode, l.Depth, l.Depth, t.Depth)) | |
| } | |
| if l.From != t.From { | |
| return fmt.Errorf("%s", v.formatMismatch("FromAddress", l.Sequence, l.Opcode, l.Depth, l.From.Hex(), t.From.Hex())) | |
| } | |
| if l.To != t.To { | |
| return fmt.Errorf("%s", v.formatMismatch("ToAddress", l.Sequence, l.Opcode, l.Depth, l.To.Hex(), t.To.Hex())) | |
| } | |
| if !l.Value.Eq(&t.Value) { | |
| return fmt.Errorf("%s", v.formatMismatch("AssetValue", l.Sequence, l.Opcode, l.Depth, l.Value.Hex(), t.Value.Hex())) | |
| } | |
| if l.GasUsed != t.GasUsed { | |
| return fmt.Errorf("%s", v.formatMismatch("GasUsed", l.Sequence, l.Opcode, l.Depth, l.GasUsed, t.GasUsed)) | |
| } | |
| if l.Reverted != t.Reverted { | |
| return fmt.Errorf("%s", v.formatMismatch("Reverted", l.Sequence, l.Opcode, l.Depth, l.Reverted, t.Reverted)) | |
| } | |
| if l.ErrType != t.ErrType { | |
| return fmt.Errorf("%s", v.formatMismatch("ErrType", l.Sequence, l.Opcode, l.Depth, l.ErrType, t.ErrType)) | |
| } | |
| return nil | |
| } | |