Skip to main content

reth_network_api/
custody.rs

1//! Shared blob cell custody state.
2
3use alloy_primitives::B128;
4use std::sync::{
5    atomic::{AtomicU64, AtomicU8, Ordering},
6    Arc,
7};
8
9/// Shared blob cell custody bitmap for [EIP-8070] sparse blobpool sampling.
10///
11/// This stores the latest `custodyColumns` value received from
12/// `engine_forkchoiceUpdatedV4`, so network components can align blob cell sampling with the
13/// consensus client's custody set. The value is a lightweight hint for sampling decisions, not a
14/// consensus-critical guard.
15///
16/// [EIP-8070]: https://eips.ethereum.org/EIPS/eip-8070
17#[derive(Debug, Clone, Default)]
18pub struct CellCustody {
19    inner: Arc<CellCustodyInner>,
20}
21
22impl CellCustody {
23    /// Returns the currently configured blob cell custody bitmap.
24    ///
25    /// Reads are intentionally cheap and eventually consistent. Concurrent custom-to-custom
26    /// updates may briefly expose an in-flight bitmap, which is acceptable because custody is only
27    /// used to steer future sampling requests.
28    pub fn get(&self) -> B128 {
29        match self.inner.state.load(Ordering::Relaxed) {
30            CELL_CUSTODY_NONE => B128::from(0u128),
31            CELL_CUSTODY_FULL => B128::from(u128::MAX),
32            _ => {
33                let high = self.inner.high.load(Ordering::Relaxed) as u128;
34                let low = self.inner.low.load(Ordering::Relaxed) as u128;
35
36                B128::from((high << 64) | low)
37            }
38        }
39    }
40
41    /// Updates the blob cell custody bitmap.
42    pub fn set(&self, custody_columns: B128) {
43        let custody_columns = u128::from(custody_columns);
44        match custody_columns {
45            0 => self.inner.state.store(CELL_CUSTODY_NONE, Ordering::Relaxed),
46            u128::MAX => self.inner.state.store(CELL_CUSTODY_FULL, Ordering::Relaxed),
47            _ => {
48                self.inner.high.store((custody_columns >> 64) as u64, Ordering::Relaxed);
49                self.inner.low.store(custody_columns as u64, Ordering::Relaxed);
50                self.inner.state.store(CELL_CUSTODY_CUSTOM, Ordering::Relaxed);
51            }
52        }
53    }
54
55    /// Updates the custody bitmap from the Engine API's little-endian bitvector encoding.
56    pub fn set_from_engine_api(&self, custody_columns: B128) {
57        self.set(B128::from(u128::from_le_bytes(custody_columns.into())));
58    }
59}
60
61const CELL_CUSTODY_NONE: u8 = 0;
62const CELL_CUSTODY_FULL: u8 = 1;
63const CELL_CUSTODY_CUSTOM: u8 = 2;
64
65#[derive(Debug, Default)]
66struct CellCustodyInner {
67    state: AtomicU8,
68    high: AtomicU64,
69    low: AtomicU64,
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn sets_engine_api_bitvector_as_numeric_mask() {
78        let custody = CellCustody::default();
79        custody.set_from_engine_api(B128::from(0b1010u128.to_le_bytes()));
80
81        assert_eq!(custody.get(), B128::from(0b1010u128));
82    }
83}