hotshot_builder_refactored/
block_size_limits.rs1use std::sync::atomic::Ordering;
2
3use atomic::Atomic;
4use coarsetime::{Duration, Instant};
5
6#[derive(Debug, Clone, Copy, bytemuck::NoUninit)]
7#[repr(C)]
8pub(crate) struct MutableState {
9 pub max_block_size: u64,
11 pub last_block_size_increment: u64,
14}
15
16#[derive(Debug)]
27pub struct BlockSizeLimits {
28 pub(crate) mutable_state: Atomic<MutableState>,
29 pub protocol_max_block_size: u64,
31 pub increment_period: Duration,
33}
34
35impl BlockSizeLimits {
36 pub const MAX_BLOCK_SIZE_FLOOR: u64 = 10_000;
38 pub const MAX_BLOCK_SIZE_CHANGE_DIVISOR: u64 = 10;
41
42 pub fn new(protocol_max_block_size: u64, increment_period: std::time::Duration) -> Self {
43 Self {
44 protocol_max_block_size,
45 increment_period: increment_period.into(),
46 mutable_state: Atomic::new(MutableState {
47 max_block_size: protocol_max_block_size,
48 last_block_size_increment: Instant::now().as_ticks(),
49 }),
50 }
51 }
52
53 pub fn max_block_size(&self) -> u64 {
54 self.mutable_state
55 .load(std::sync::atomic::Ordering::Relaxed)
56 .max_block_size
57 }
58
59 pub fn try_increment_block_size(&self, force: bool) {
63 if force
64 || Instant::now().as_ticks().saturating_sub(
65 self.mutable_state
66 .load(Ordering::Relaxed)
67 .last_block_size_increment,
68 ) >= self.increment_period.as_ticks()
69 {
70 self.mutable_state
71 .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |previous| {
72 let max_block_size = std::cmp::min(
73 previous.max_block_size
74 + previous
75 .max_block_size
76 .div_ceil(Self::MAX_BLOCK_SIZE_CHANGE_DIVISOR),
77 self.protocol_max_block_size,
78 );
79 let last_block_size_increment = Instant::now().as_ticks();
80 Some(MutableState {
81 max_block_size,
82 last_block_size_increment,
83 })
84 })
85 .expect("Closure always returns Some");
86 }
87 }
88
89 pub fn decrement_block_size(&self) {
92 self.mutable_state
93 .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |previous| {
94 let max_block_size = std::cmp::max(
95 previous.max_block_size.saturating_sub(
96 previous
97 .max_block_size
98 .div_ceil(Self::MAX_BLOCK_SIZE_CHANGE_DIVISOR),
99 ),
100 Self::MAX_BLOCK_SIZE_FLOOR,
101 );
102 Some(MutableState {
103 max_block_size,
104 last_block_size_increment: previous.last_block_size_increment,
105 })
106 })
107 .expect("Closure always returns Some");
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use hotshot_builder_shared::testing::constants::{
114 TEST_MAX_BLOCK_SIZE_INCREMENT_PERIOD, TEST_PROTOCOL_MAX_BLOCK_SIZE,
115 };
116 use tracing_test::traced_test;
117
118 use super::*;
119
120 #[test]
121 #[traced_test]
122 fn test_increment_block_size() {
123 let mut block_size_limits = BlockSizeLimits::new(
124 TEST_PROTOCOL_MAX_BLOCK_SIZE,
125 std::time::Duration::from_millis(25),
126 );
127 block_size_limits.mutable_state = Atomic::new(MutableState {
129 max_block_size: TEST_PROTOCOL_MAX_BLOCK_SIZE / 2,
130 last_block_size_increment: Instant::now().as_ticks(),
131 });
132
133 block_size_limits.try_increment_block_size(false);
135 assert!(block_size_limits.max_block_size() == TEST_PROTOCOL_MAX_BLOCK_SIZE / 2);
136
137 block_size_limits.try_increment_block_size(true);
139 assert!(block_size_limits.max_block_size() > TEST_PROTOCOL_MAX_BLOCK_SIZE / 2);
140 let new_size = block_size_limits.max_block_size();
141
142 std::thread::sleep(std::time::Duration::from_millis(30));
143
144 block_size_limits.try_increment_block_size(false);
146 assert!(block_size_limits.max_block_size() > new_size);
147 }
148
149 #[test]
150 #[traced_test]
151 fn test_decrement_block_size() {
152 let block_size_limits = BlockSizeLimits::new(
153 TEST_PROTOCOL_MAX_BLOCK_SIZE,
154 TEST_MAX_BLOCK_SIZE_INCREMENT_PERIOD,
155 );
156 block_size_limits.decrement_block_size();
157 assert!(block_size_limits.max_block_size() < TEST_PROTOCOL_MAX_BLOCK_SIZE);
158 }
159
160 #[test]
161 #[traced_test]
162 fn test_max_block_size_floor() {
163 let block_size_limits = BlockSizeLimits::new(
164 BlockSizeLimits::MAX_BLOCK_SIZE_FLOOR + 1,
165 TEST_MAX_BLOCK_SIZE_INCREMENT_PERIOD,
166 );
167 block_size_limits.decrement_block_size();
168 assert_eq!(
169 block_size_limits.max_block_size(),
170 BlockSizeLimits::MAX_BLOCK_SIZE_FLOOR
171 );
172 }
173}