# Copyright (c) 2026 Sovereign Research Stack. All rights reserved. # Released under Apache 2.0 license as described in the file LICENSE. # Authors: Research Stack Team # # reed_solomon_vcn.py — Reed-Solomon error correction for VCN frame data # # Adds ECC (Error Correction Code) symbols after frame data in the VCN # (Virtual Compute Network) frame format. Uses the reedsolo library. from __future__ import annotations import struct from typing import Tuple try: import reedsolo except ImportError: raise ImportError( "reedsolo library required. Install with: pip install reedsolo" ) # ════════════════════════════════════════════════════════════ # §1 VCN Frame Format # ════════════════════════════════════════════════════════════ # VCN Frame Layout: # ┌──────────┬──────────────┬──────────────┬──────────────┬──────────────┐ # │ Magic(4) │ FrameLen (4) │ FrameType(1) │ Payload(var) │ RS ECC(var) │ # └──────────┴──────────────┴──────────────┴──────────────┴──────────────┘ VCN_MAGIC = b"VCN\x01" VCN_HEADER_SIZE = 9 # 4 (magic) + 4 (length) + 1 (type) # Frame types FRAME_TYPE_DATA = 0x01 FRAME_TYPE_CTRL = 0x02 FRAME_TYPE_SYNC = 0x03 FRAME_TYPE_ECC = 0x04 # ════════════════════════════════════════════════════════════ # §2 Reed-Solomon Encoder/Decoder # ════════════════════════════════════════════════════════════ class RSCodec: """Reed-Solomon codec wrapper with VCN frame integration.""" def __init__(self, nsym: int = 32, nsize: int = 255): """Initialize RS codec. Args: nsym: Number of error correction symbols. nsize: Total codeword size (data + ECC). Max 255 for GF(2^8). """ self.nsym = nsym self.nsize = nsize self.codec = reedsolo.RSCodec(nsym, nsize) def encode_rs(self, data: bytes) -> bytes: """Encode data with Reed-Solomon ECC. Args: data: Raw data bytes. Returns: Encoded data with ECC symbols appended. """ return bytes(self.codec.encode(data)) def decode_rs(self, encoded_data: bytes) -> Tuple[bytes, int]: """Decode and correct RS-encoded data. Args: encoded_data: Data with ECC symbols. Returns: Tuple of (corrected_data, number_of_corrections). Raises: reedsolo.ReedSolomonError: If too many errors to correct. """ decoded, decoded_msgecc, errata_pos = self.codec.decode(encoded_data) corrections = len(errata_pos) if errata_pos else 0 return bytes(decoded), corrections # ════════════════════════════════════════════════════════════ # §3 VCN Frame Protection # ════════════════════════════════════════════════════════════ def encode_rs(data: bytes, nsym: int = 32) -> bytes: """Encode data with Reed-Solomon ECC symbols. Args: data: Raw data bytes. nsym: Number of ECC symbols (default 32). Returns: Encoded data with nsym ECC symbols appended. """ codec = reedsolo.RSCodec(nsym) return bytes(codec.encode(data)) def decode_rs(encoded_data: bytes, nsym: int = 32) -> Tuple[bytes, int]: """Decode and correct RS-encoded data. Args: encoded_data: Data with ECC symbols. nsym: Number of ECC symbols (must match encoding). Returns: Tuple of (corrected_data, corrections_count). """ codec = reedsolo.RSCodec(nsym) decoded, _, errata_pos = codec.decode(encoded_data) corrections = len(errata_pos) if errata_pos else 0 return bytes(decoded), corrections def protect_frame(frame_bytes: bytes, redundancy_factor: int = 2) -> bytes: """Add Reed-Solomon ECC protection to a VCN frame. The frame is chunked into blocks (max 223 data bytes each for GF(2^8) with 32 ECC symbols), and each block is independently RS-encoded. Protected Frame Layout: ┌──────────────┬──────────────┬──────────────────────────────────┐ │ NumBlocks(4) │ BlockSize(4) │ Block0(RS) │ Block1(RS) │ ... │ └──────────────┴──────────────┴──────────────────────────────────┘ Args: frame_bytes: Raw frame data to protect. redundancy_factor: Controls ECC symbol count (nsym = 16 * factor). Returns: Protected frame bytes with ECC. """ nsym = 16 * redundancy_factor # Max data bytes per codeword in GF(2^8) with nsym ECC symbols max_data = 255 - nsym if max_data < 1: raise ValueError(f"Redundancy factor {redundancy_factor} too high: nsym={nsym}") # Chunk the frame data chunks = [] for i in range(0, len(frame_bytes), max_data): chunk = frame_bytes[i:i + max_data] # Pad last chunk to consistent block size if len(chunk) < max_data: chunk = chunk + b'\x00' * (max_data - len(chunk)) chunks.append(chunk) num_blocks = len(chunks) block_size = max_data # data portion of each block # Header header = struct.pack(' bytes: """Recover a VCN frame from RS-protected data. Args: protected_bytes: Protected frame bytes. redundancy_factor: Must match the factor used during protection. Returns: Recovered frame data (errors corrected if possible). Raises: ValueError: If the protected data is malformed. reedsolo.ReedSolomonError: If too many errors to correct in any block. """ nsym = 16 * redundancy_factor if len(protected_bytes) < 8: raise ValueError("Protected data too short for header") num_blocks, block_size = struct.unpack('