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
//! # Oracle Data Size Overhead Tests
//!
//! This module tests that oracle data doesn't experience excessive overhead when
//! serialized through our protocol's data structures.
//!
//! ## Background
//!
//! When an oracle returns data, it goes through several layers of wrapping:
//! 1. The raw data is wrapped in `OracleResponse` (adding a timestamp)
//! 2. `OracleResponse` is wrapped in `OracleOutput` enum
//! 3. `OracleOutput` is Borsh-serialized and stored in `OracleUpdate.data`
//! 4. `OracleUpdate` is stored in `OracleReport.updates`
//! 5. Finally, `OracleReport` 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 oracle update.
//!
//! ## Measured Overhead
//!
//! Through empirical testing, we've determined that the overhead for each
//! oracle 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. OracleOutput (Borsh serialized) - contributes to OracleUpdate.data
//! - **Enum discriminant**: 1 byte (0 for Success variant)
//! - **OracleResponse.response** (OracleData):
//! - `OracleData::Raw` variant discriminant: 1 byte
//! - Length prefix: 4 bytes (u32 in little-endian for Borsh)
//! - Data: `size` bytes (the actual oracle data)
//! - `OracleData::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 oracle data)
//! - **OracleResponse.timestamp** (String):
//! - Length prefix: 4 bytes (u32 in little-endian)
//! - Timestamp string: 27 bytes (fixed format: "2025-08-23T17:52:43.123456Z")
//! - **Subtotal for OracleOutput**: 36 bytes + data size
//!
//! #### 2. OracleUpdate (Bincode serialized)
//! - **data field** (`Vec<u8>`):
//! - Length prefix: 8 bytes (u64 for Bincode)
//! - Contents: The Borsh-serialized OracleOutput (39 + data size bytes)
//! - **validator** (Authority Pubkey): 104 bytes
//! - **Subtotal for OracleUpdate**: 40 bytes + OracleOutput size
//!
//! #### 3. OracleReport (Bincode serialized)
//! - **oracle_id**: 64 bytes (based on OracleId type definition)
//! - **round**: 8 bytes (u64)
//! - **updates** (`Vec<OracleUpdate>`):
//! - Length prefix: 8 bytes (u64 for Vec length)
//! - Contents: The OracleUpdate entries
//! - **Subtotal for OracleReport structure**: 80 bytes + all OracleUpdate sizes
//!
//! ### Total Calculation - Raw data
//! For a single update with data of size S:
//! - OracleOutput overhead: 37 bytes
//! - OracleUpdate overhead: 104 bytes
//! - OracleReport overhead: 80 bytes
//! - **Total fixed overhead: 221 bytes**
//!
//! ### Total Calculation - Filtered data
//! For a single update with data of size S:
//! - OracleOutput overhead: 41 + (f * 5) bytes
//! - OracleUpdate overhead: 40 bytes
//! - OracleReport overhead: 80 bytes
//! - **Total fixed overhead: 157 bytes**
//!
//! ## JSON Serialization Note
//!
//! The oracle 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 oracle output buffer, ensuring the oracle can serialize and
/// distinguish success or error responses from the `OracleOutput` enum without truncation.
/// Chosen empirically to support a variety of error/success reporting formats.
pub const MIN_VIABLE_LIMIT_OF_ORACLE_OUTPUT_SIZE: usize = 100;
pub const MAX_ORACLE_OUTPUT_SIZE: usize =
- ORACLE_REPORT_BASE_OVERHEAD - ORACLE_UPDATE_BASE_OVERHEAD;
pub const ORACLE_REPORT_BASE_OVERHEAD: usize = 80; // oracle_id (64) + round (8) + updates length prefix (8)
pub const ORACLE_UPDATE_BASE_OVERHEAD: usize = 104; // data length prefix (8) + validator (96)
pub const ORACLE_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 OracleOutput.
///
/// This function determines the maximum size of oracle data that can be included
/// in an oracle update, given the number of validators and the protocol's size limits.
/// It accounts for all serialization overhead from the oracle report structure.
///
/// # Arguments
///
/// * `validator_count` - The number of validators that will submit oracle updates
///
/// # Returns
///
/// The maximum size in bytes that the oracle data can be for each validator's update
/// to ensure the entire oracle report stays within the u16::MAX size limit.