Skip to content

Commit 15f77e5

Browse files
committed
feat: slot calculator in config
1 parent 7808019 commit 15f77e5

File tree

6 files changed

+325
-58
lines changed

6 files changed

+325
-58
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,6 @@ signal-hook = "0.3.17"
4343
tokio = { version = "1.43.0", features = ["macros"] }
4444

4545
[features]
46-
default = ["alloy"]
46+
default = ["alloy", "perms"]
4747
alloy = ["dep:alloy"]
4848
perms = ["dep:chrono"]

src/perms/auth.rs

Whitespace-only changes.

src/perms/builders.rs

Lines changed: 58 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,13 @@
99
//! For updating the currently permissioned builders,
1010
//! Simply update the included `builders.json` file with the new builders.
1111
12-
use crate::utils::from_env::{FromEnvErr, FromEnvVar};
12+
use crate::{
13+
perms::{SlotAuthzConfig, SlotAuthzConfigError, SlotCalculator},
14+
utils::from_env::{FromEnv, FromEnvErr, FromEnvVar},
15+
};
1316

14-
/// The start timestamp for the permissioned builders, in seconds.
15-
const EPOCH_START: u64 = 0;
17+
/// The builder list env var.
18+
const BUILDERS: &str = "PERMISSIONED_BUILDERS";
1619

1720
/// Ethereum's slot time in seconds.
1821
pub const ETHEREUM_SLOT_TIME: u64 = 12;
@@ -22,7 +25,7 @@ fn now() -> u64 {
2225
}
2326

2427
/// Possible errors when permissioning a builder.
25-
#[derive(Debug, thiserror::Error)]
28+
#[derive(Debug, thiserror::Error, Clone, Copy, PartialEq, Eq)]
2629
pub enum BuilderPermissionError {
2730
/// Action attempt too early.
2831
#[error("action attempt too early")]
@@ -35,7 +38,11 @@ pub enum BuilderPermissionError {
3538
/// Builder not permissioned for this slot.
3639
#[error("builder not permissioned for this slot")]
3740
NotPermissioned,
41+
}
3842

43+
/// Possible errors when loading the builder configuration.
44+
#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)]
45+
pub enum BuilderConfigError {
3946
/// Error loading the environment variable.
4047
#[error(
4148
"failed to parse environment variable. Expected a comma-seperated list of UUIDs. Got: {input}"
@@ -46,6 +53,10 @@ pub enum BuilderPermissionError {
4653
/// The contents of the environment variable.
4754
input: String,
4855
},
56+
57+
/// Error loading the slot authorization configuration.
58+
#[error(transparent)]
59+
SlotAutzConfig(#[from] SlotAuthzConfigError),
4960
}
5061

5162
/// An individual builder.
@@ -74,12 +85,20 @@ impl Builder {
7485
pub struct Builders {
7586
/// The list of builders.
7687
pub builders: Vec<Builder>,
88+
89+
/// The slot authorization configuration.
90+
config: SlotAuthzConfig,
7791
}
7892

7993
impl Builders {
8094
/// Create a new Builders struct.
81-
pub const fn new(builders: Vec<Builder>) -> Self {
82-
Self { builders }
95+
pub const fn new(builders: Vec<Builder>, config: SlotAuthzConfig) -> Self {
96+
Self { builders, config }
97+
}
98+
99+
/// Get the calculator instance.
100+
pub fn calc(&self) -> SlotCalculator {
101+
self.config.calc()
83102
}
84103

85104
/// Get the builder at a specific index.
@@ -99,7 +118,7 @@ impl Builders {
99118
/// Get the index of the builder that is allowed to sign a block for a
100119
/// particular timestamp.
101120
pub fn index(&self, timestamp: u64) -> u64 {
102-
((timestamp - EPOCH_START) / ETHEREUM_SLOT_TIME) % self.builders.len() as u64
121+
self.config.calc().calculate_slot(timestamp) % self.builders.len() as u64
103122
}
104123

105124
/// Get the index of the builder that is allowed to sign a block at the
@@ -117,23 +136,18 @@ impl Builders {
117136
/// This is based on the current timestamp and the builder's sub. It's a
118137
/// round-robin design, where each builder is allowed to perform an action
119138
/// at a specific slot, and what builder is allowed changes with each slot.
120-
pub fn is_builder_permissioned(
121-
&self,
122-
config: &crate::perms::SlotAuthzConfig,
123-
sub: &str,
124-
) -> Result<(), BuilderPermissionError> {
139+
pub fn is_builder_permissioned(&self, sub: &str) -> Result<(), BuilderPermissionError> {
125140
// Get the current timestamp.
126-
let curr_timestamp = now();
127141

128142
// Calculate the current slot time, which is a number between 0 and 11.
129-
let current_slot_time = (curr_timestamp - config.chain_offset()) % ETHEREUM_SLOT_TIME;
143+
let current_slot_time = self.calc().current_timepoint_within_slot();
130144

131145
// Builders can only perform actions between the configured start and cutoff times, to prevent any timing games.
132-
if current_slot_time < config.block_query_start() {
146+
if current_slot_time < self.config.block_query_start() {
133147
tracing::debug!("Action attempt too early");
134148
return Err(BuilderPermissionError::ActionAttemptTooEarly);
135149
}
136-
if current_slot_time > config.block_query_cutoff() {
150+
if current_slot_time > self.config.block_query_cutoff() {
137151
tracing::debug!("Action attempt too late");
138152
return Err(BuilderPermissionError::ActionAttemptTooLate);
139153
}
@@ -151,37 +165,52 @@ impl Builders {
151165
}
152166
}
153167

154-
impl FromIterator<Builder> for Builders {
155-
fn from_iter<T: IntoIterator<Item = Builder>>(iter: T) -> Self {
156-
Self::new(iter.into_iter().collect())
157-
}
158-
}
168+
impl FromEnv for Builders {
169+
type Error = BuilderConfigError;
159170

160-
impl FromEnvVar for Builders {
161-
type Error = BuilderPermissionError;
171+
fn from_env() -> Result<Self, FromEnvErr<Self::Error>> {
172+
let s = String::from_env_var(BUILDERS)
173+
.map_err(FromEnvErr::infallible_into::<BuilderConfigError>)?;
174+
let builders = s.split(',').map(Builder::new).collect();
162175

163-
fn from_env_var(env_var: &str) -> Result<Self, FromEnvErr<Self::Error>> {
164-
let s = String::from_env_var(env_var)
165-
.map_err(FromEnvErr::infallible_into::<BuilderPermissionError>)?;
176+
let config = SlotAuthzConfig::from_env().map_err(FromEnvErr::from)?;
166177

167-
Ok(s.split(',').map(Builder::new).collect())
178+
Ok(Self { builders, config })
168179
}
169180
}
170181

171182
#[cfg(test)]
172183
mod test {
184+
173185
use super::*;
186+
use crate::perms;
174187

175188
#[test]
176189
fn load_builders() {
177-
unsafe { std::env::set_var("TEST", "0,1,2,3,4,5") };
190+
unsafe {
191+
std::env::set_var(BUILDERS, "0,1,2,3,4,5");
192+
193+
std::env::set_var(perms::calc::START_TIMESTAMP, "1");
194+
std::env::set_var(perms::calc::SLOT_OFFSET, "0");
195+
std::env::set_var(perms::calc::SLOT_DURATION, "12");
178196

179-
let builders = Builders::from_env_var("TEST").unwrap();
197+
std::env::set_var(perms::config::BLOCK_QUERY_START, "1");
198+
std::env::set_var(perms::config::BLOCK_QUERY_CUTOFF, "11");
199+
};
200+
201+
let builders = Builders::from_env().unwrap();
180202
assert_eq!(builders.builder_at(0).sub, "0");
181203
assert_eq!(builders.builder_at(1).sub, "1");
182204
assert_eq!(builders.builder_at(2).sub, "2");
183205
assert_eq!(builders.builder_at(3).sub, "3");
184206
assert_eq!(builders.builder_at(4).sub, "4");
185207
assert_eq!(builders.builder_at(5).sub, "5");
208+
209+
assert_eq!(builders.calc().slot_offset(), 0);
210+
assert_eq!(builders.calc().slot_duration(), 12);
211+
assert_eq!(builders.calc().start_timestamp(), 1);
212+
213+
assert_eq!(builders.config.block_query_start(), 1);
214+
assert_eq!(builders.config.block_query_cutoff(), 11);
186215
}
187216
}

0 commit comments

Comments
 (0)