import math, times, sequtils, strutils, options import nimcrypto import results import leopard import chronicles import protobuf_serialization/proto_parser import protobuf_serialization import db_models import db import_proto3 "segment_message.proto" type Chunk* = object payload*: seq[byte] Message* = object hash*: seq[byte] payload*: seq[byte] sigPubKey*: seq[byte] SegmentationHander* = object segmentSize*: int persistence*: SegmentationPersistence const SegmentsParityRate = 0.125 SegmentsReedsolomonMaxCount = 256 const ErrMessageSegmentsIncomplete* = "message segments incomplete" ErrMessageSegmentsAlreadyCompleted* = "message segments already completed" ErrMessageSegmentsInvalidCount* = "invalid segments count" ErrMessageSegmentsHashMismatch* = "hash of entire payload does not match" ErrMessageSegmentsInvalidParity* = "invalid parity segments" proc isValid(s: SegmentMessageProto): bool = # Check if the hash length is valid (32 bytes for Keccak256) if s.entire_message_hash.len != 32: return false # Check if the segment index is within the valid range if s.segments_count > 0 and s.index >= s.segments_count: return false # Check if the parity segment index is within the valid range if s.parity_segments_count > 0 and s.parity_segment_index >= s.parity_segments_count: return false # Check if segments_count is at least 2 or parity_segments_count is positive return s.segments_count >= 2 or s.parity_segments_count > 0 proc isParityMessage*(s: SegmentMessage): bool = s.segmentsCount == 0 and s.paritySegmentsCount > 0 proc segmentMessage*(s: SegmentationHander, newMessage: Chunk): Result[seq[Chunk], string] = if newMessage.payload.len <= s.segmentSize: return ok(@[newMessage]) info "segmenting message", "payloadSize" = newMessage.payload.len, "segmentSize" = s.segmentSize let entireMessageHash = keccak256.digest(newMessage.payload) let entirePayloadSize = newMessage.payload.len let segmentsCount = int(ceil(entirePayloadSize.float / s.segmentSize.float)) let paritySegmentsCount = int(floor(segmentsCount.float * SegmentsParityRate)) var segmentPayloads = newSeq[seq[byte]](segmentsCount) var segmentMessages = newSeq[Chunk](segmentsCount) for i in 0.. entirePayloadSize: endIndex = entirePayloadSize let segmentPayload = newMessage.payload[start.. SegmentsReedsolomonMaxCount: return ok(segmentMessages) # Align last segment payload for Reed-Solomon (leopard requires fixed-size shards) let lastSegmentPayload = segmentPayloads[segmentsCount-1] segmentPayloads[segmentsCount-1] = newSeq[byte](s.segmentSize) segmentPayloads[segmentsCount-1][0.. 0: payloads[firstSegmentMessage.segmentsCount - 1] = lastNonParitySegmentPayload # Combine payload var entirePayload = newSeq[byte]() for i in 0..