penumbra_sct/component/
clock.rs

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
use crate::{epoch::Epoch, state_key};
use anyhow::{anyhow, Context, Result};
use async_trait::async_trait;
use cnidarium::{StateRead, StateWrite};
use penumbra_proto::{StateReadProto, StateWriteProto};
use std::str::FromStr;

#[async_trait]
/// Provides read access to epoch indices, block heights, timestamps, and other related data.
pub trait EpochRead: StateRead {
    /// Get the current block height.
    ///
    /// # Errors
    /// Returns an error if the block height is missing.
    async fn get_block_height(&self) -> Result<u64> {
        self.get_proto(state_key::block_manager::block_height())
            .await?
            .ok_or_else(|| anyhow!("Missing block_height"))
    }

    /// Gets the current block timestamp from the JMT
    ///
    /// # Errors
    /// Returns an error if the block timestamp is missing.
    ///
    /// # Panic
    /// Panics if the block timestamp is not a valid RFC3339 time string.
    async fn get_current_block_timestamp(&self) -> Result<tendermint::Time> {
        let timestamp_string: String = self
            .get_proto(state_key::block_manager::current_block_timestamp())
            .await?
            .ok_or_else(|| anyhow!("Missing current_block_timestamp"))?;

        Ok(tendermint::Time::from_str(&timestamp_string)
            .context("current_block_timestamp was an invalid RFC3339 time string")?)
    }

    /// Gets a historic block timestamp from nonverifiable storage.
    ///
    /// # Errors
    /// Returns an error if the block timestamp is missing.
    ///
    /// # Panic
    /// Panics if the block timestamp is not a valid RFC3339 time string.
    async fn get_block_timestamp(&self, height: u64) -> Result<tendermint::Time> {
        let timestamp_string: String = self
            .nonverifiable_get_proto(&state_key::block_manager::block_timestamp(height).as_bytes())
            .await?
            .ok_or_else(|| anyhow!("Missing block_timestamp for height {}", height))?;

        Ok(
            tendermint::Time::from_str(&timestamp_string).context(format!(
                "block_timestamp for height {} was an invalid RFC3339 time string",
                height
            ))?,
        )
    }

    /// Get the current application epoch.
    ///
    /// # Errors
    /// Returns an error if the epoch is missing.
    async fn get_current_epoch(&self) -> Result<Epoch> {
        // Get the height
        let height = self.get_block_height().await?;

        self.get(&state_key::epoch_manager::epoch_by_height(height))
            .await?
            .ok_or_else(|| anyhow!("missing epoch for current height: {height}"))
    }

    /// Get the epoch corresponding to the supplied height.
    ///
    /// # Errors
    /// Returns an error if the epoch is missing.
    async fn get_epoch_by_height(&self, height: u64) -> Result<Epoch> {
        self.get(&state_key::epoch_manager::epoch_by_height(height))
            .await?
            .ok_or_else(|| anyhow!("missing epoch for height"))
    }

    /// Returns true if we are triggering an early epoch end.
    async fn is_epoch_ending_early(&self) -> bool {
        self.object_get(state_key::epoch_manager::end_epoch_early())
            .unwrap_or(false)
    }
}

impl<T: StateRead + ?Sized> EpochRead for T {}

/// Provides write access to the chain's epoch manager.
/// The epoch manager is responsible for tracking block and epoch heights
/// as well as related data like reported timestamps and epoch duration.
#[async_trait]
pub trait EpochManager: StateWrite {
    /// Writes the current block's timestamp as an RFC3339 string to verifiable storage.
    ///
    /// Also writes the current block's timestamp to the appropriate key in nonverifiable storage.
    fn put_block_timestamp(&mut self, height: u64, timestamp: tendermint::Time) {
        self.put_proto(
            state_key::block_manager::current_block_timestamp().into(),
            timestamp.to_rfc3339(),
        );

        self.nonverifiable_put_proto(
            state_key::block_manager::block_timestamp(height).into(),
            timestamp.to_rfc3339(),
        );
    }

    /// Write a value in the end epoch flag in object-storage.
    /// This is used to trigger an early epoch end at the end of the block.
    fn set_end_epoch_flag(&mut self) {
        self.object_put(state_key::epoch_manager::end_epoch_early(), true)
    }

    /// Writes the block height to verifiable storage.
    fn put_block_height(&mut self, height: u64) {
        self.put_proto(state_key::block_manager::block_height().to_string(), height)
    }

    /// Index the current epoch by height.
    fn put_epoch_by_height(&mut self, height: u64, epoch: Epoch) {
        self.put(state_key::epoch_manager::epoch_by_height(height), epoch)
    }
}

impl<T: StateWrite + ?Sized> EpochManager for T {}