solana_nonce/
state.rs

1#[cfg(feature = "serde")]
2use serde_derive::{Deserialize, Serialize};
3use {
4    solana_fee_calculator::FeeCalculator, solana_hash::Hash, solana_pubkey::Pubkey,
5    solana_sha256_hasher::hashv,
6};
7
8const DURABLE_NONCE_HASH_PREFIX: &[u8] = "DURABLE_NONCE".as_bytes();
9
10#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
11#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
12pub struct DurableNonce(Hash);
13
14/// Initialized data of a durable transaction nonce account.
15///
16/// This is stored within [`State`] for initialized nonce accounts.
17#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
18#[derive(Debug, Default, PartialEq, Eq, Clone)]
19pub struct Data {
20    /// Address of the account that signs transactions using the nonce account.
21    pub authority: Pubkey,
22    /// Durable nonce value derived from a valid previous blockhash.
23    pub durable_nonce: DurableNonce,
24    /// The fee calculator associated with the blockhash.
25    pub fee_calculator: FeeCalculator,
26}
27
28impl Data {
29    /// Create new durable transaction nonce data.
30    pub fn new(
31        authority: Pubkey,
32        durable_nonce: DurableNonce,
33        lamports_per_signature: u64,
34    ) -> Self {
35        Data {
36            authority,
37            durable_nonce,
38            fee_calculator: FeeCalculator::new(lamports_per_signature),
39        }
40    }
41
42    /// Hash value used as recent_blockhash field in Transactions.
43    /// Named blockhash for legacy reasons, but durable nonce and blockhash
44    /// have separate domains.
45    pub fn blockhash(&self) -> Hash {
46        self.durable_nonce.0
47    }
48
49    /// Get the cost per signature for the next transaction to use this nonce.
50    pub fn get_lamports_per_signature(&self) -> u64 {
51        self.fee_calculator.lamports_per_signature
52    }
53}
54
55impl DurableNonce {
56    pub fn from_blockhash(blockhash: &Hash) -> Self {
57        Self(hashv(&[DURABLE_NONCE_HASH_PREFIX, blockhash.as_ref()]))
58    }
59
60    /// Hash value used as recent_blockhash field in Transactions.
61    pub fn as_hash(&self) -> &Hash {
62        &self.0
63    }
64}
65
66/// The state of a durable transaction nonce account.
67///
68/// When created in memory with [`State::default`] or when deserialized from an
69/// uninitialized account, a nonce account will be [`State::Uninitialized`].
70#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
71#[derive(Debug, Default, PartialEq, Eq, Clone)]
72pub enum State {
73    #[default]
74    Uninitialized,
75    Initialized(Data),
76}
77
78impl State {
79    /// Create new durable transaction nonce state.
80    pub fn new_initialized(
81        authority: &Pubkey,
82        durable_nonce: DurableNonce,
83        lamports_per_signature: u64,
84    ) -> Self {
85        Self::Initialized(Data::new(*authority, durable_nonce, lamports_per_signature))
86    }
87
88    /// Get the serialized size of the nonce state.
89    pub const fn size() -> usize {
90        80 // see test_nonce_state_size.
91    }
92}
93
94#[cfg(test)]
95mod test {
96    use {super::*, crate::versions::Versions};
97
98    #[test]
99    fn default_is_uninitialized() {
100        assert_eq!(State::default(), State::Uninitialized)
101    }
102
103    #[test]
104    fn test_nonce_state_size() {
105        let data = Versions::new(State::Initialized(Data::default()));
106        let size = bincode::serialized_size(&data).unwrap();
107        assert_eq!(State::size() as u64, size);
108    }
109}