1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// Copyright (c) Subzero Labs, Inc.
// SPDX-License-Identifier: Apache-2.0
//! # REX Data Size Overhead Tests
//!
//! This module tests that REX data doesn't experience excessive overhead when
//! serialized through our protocol's data structures.
//!
//! ## Background
//!
//! When a REX program returns data, it goes through several layers of wrapping:
//! 1. The raw data is wrapped in `RexResponse` (adding a timestamp)
//! 2. `RexResponse` is wrapped in `RexOutput` enum
//! 3. `RexOutput` is Borsh-serialized and stored in `RexUpdate.data`
//! 4. `RexUpdate` is stored in `RexReport.updates`
//! 5. Finally, `RexReport` is Bincode-serialized for transmission
//!
//! Each layer adds metadata and serialization overhead. This test ensures that
//! the total overhead remains constant at exactly 221 bytes per REX update.
//!
//! ## Measured Overhead
//!
//! Through empirical testing, we've determined that the overhead for each
//! REX update is **exactly 221 bytes**. This overhead is constant
//! regardless of the actual data size or number of validators.
//!
//! ### Detailed Overhead Breakdown (221 bytes per update)
//!
//! #### 1. RexOutput (Borsh serialized) - contributes to RexUpdate.data
//! - **Enum discriminant**: 1 byte (0 for Success variant)
//! - **RexResponse.response** (RexData):
//! - `RexData::Raw` variant discriminant: 1 byte
//! - Length prefix: 4 bytes (u32 in little-endian for Borsh)
//! - Data: `size` bytes (the actual REX data)
//! - `RexData::Filtered` variant discriminant: 1 byte
//! - Length prefix: 4 bytes (u32 in little-endian for Borsh)
//! - For each filter entry:
//! - `FilterResult`enum discriminant: 1 byte
//! - Length prefix: 4 bytes (u32 in little-endian for Borsh)
//! - Data: `size` bytes (the actual REX data)
//! - **RexResponse.timestamp** (String):
//! - Length prefix: 4 bytes (u32 in little-endian)
//! - Timestamp string: 27 bytes (fixed format: "2025-08-23T17:52:43.123456Z")
//! - **Subtotal for RexOutput**: 36 bytes + data size
//!
//! #### 2. RexUpdate (Bincode serialized)
//! - **data field** (`Vec<u8>`):
//! - Length prefix: 8 bytes (u64 for Bincode)
//! - Contents: The Borsh-serialized RexOutput (39 + data size bytes)
//! - **validator** (Authority Pubkey): 104 bytes
//! - **Subtotal for RexUpdate**: 40 bytes + RexOutput size
//!
//! #### 3. RexReport (Bincode serialized)
//! - **rex_id**: 64 bytes (based on RexId type definition)
//! - **round**: 8 bytes (u64)
//! - **updates** (`Vec<RexUpdate>`):
//! - Length prefix: 8 bytes (u64 for Vec length)
//! - Contents: The RexUpdate entries
//! - **Subtotal for RexReport structure**: 80 bytes + all RexUpdate sizes
//!
//! ### Total Calculation - Raw data
//! For a single update with data of size S:
//! - RexOutput overhead: 37 bytes
//! - RexUpdate overhead: 104 bytes
//! - RexReport overhead: 80 bytes
//! - **Total fixed overhead: 221 bytes**
//!
//! ### Total Calculation - Filtered data
//! For a single update with data of size S:
//! - RexOutput overhead: 41 + (f * 5) bytes
//! - RexUpdate overhead: 40 bytes
//! - RexReport overhead: 80 bytes
//! - **Total fixed overhead: 157 bytes**
//!
//! ## JSON Serialization Note
//!
//! The REX returns JSON data, which adds 2 bytes (quotes) to string values.
//! The test accounts for this by adjusting the input string size so that the
//! JSON-serialized result is exactly the desired size.
use error;
/// The minimum viable size for the REX output buffer, ensuring the REX program can serialize and
/// distinguish success or error responses from the `RexOutput` enum without truncation.
/// Chosen empirically to support a variety of error/success reporting formats.
pub const MIN_VIABLE_LIMIT_OF_REX_OUTPUT_SIZE: usize = 100;
pub const REX_MAX_OUTPUT_SIZE: usize =
- REX_REPORT_BASE_OVERHEAD - REX_UPDATE_BASE_OVERHEAD;
pub const REX_REPORT_BASE_OVERHEAD: usize = 80; // rex_id (64) + round (8) + updates length prefix (8)
pub const REX_UPDATE_BASE_OVERHEAD: usize = 104; // data length prefix (8) + validator (96)
pub const REX_OUTPUT_RAW_BASE_OVERHEAD: usize = 37; // enum discriminant for the output (1) + enum discriminant for the data (1) + response length prefix (4) + timestamp length prefix (4) + timestamp (27)
/// Calculates the maximum size of serialized RexOutput.
///
/// This function determines the maximum size of REX data that can be included
/// in an REX update, given the number of validators and the protocol's size limits.
/// It accounts for all serialization overhead from the REX report structure.
///
/// # Arguments
///
/// * `validator_count` - The number of validators that will submit REX updates
///
/// # Returns
///
/// The maximum size in bytes that the REX data can be for each validator's update
/// to ensure the entire REX report stays within the u16::MAX size limit.