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
use crate::{parse_account_data::ParseAccountError, UiFeeCalculator};
use solana_sdk::{
    instruction::InstructionError,
    nonce::{state::Versions, State},
};

pub fn parse_nonce(data: &[u8]) -> Result<UiNonceState, ParseAccountError> {
    let nonce_state: Versions = bincode::deserialize(data)
        .map_err(|_| ParseAccountError::from(InstructionError::InvalidAccountData))?;
    let nonce_state = nonce_state.convert_to_current();
    match nonce_state {
        // This prevents parsing an allocated System-owned account with empty data of any non-zero
        // length as `uninitialized` nonce. An empty account of the wrong length can never be
        // initialized as a nonce account, and an empty account of the correct length may not be an
        // uninitialized nonce account, since it can be assigned to another program.
        State::Uninitialized => Err(ParseAccountError::from(
            InstructionError::InvalidAccountData,
        )),
        State::Initialized(data) => Ok(UiNonceState::Initialized(UiNonceData {
            authority: data.authority.to_string(),
            blockhash: data.blockhash.to_string(),
            fee_calculator: data.fee_calculator.into(),
        })),
    }
}

/// A duplicate representation of NonceState for pretty JSON serialization
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase", tag = "type", content = "info")]
pub enum UiNonceState {
    Uninitialized,
    Initialized(UiNonceData),
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UiNonceData {
    pub authority: String,
    pub blockhash: String,
    pub fee_calculator: UiFeeCalculator,
}

#[cfg(test)]
mod test {
    use super::*;
    use solana_sdk::{
        hash::Hash,
        nonce::{
            state::{Data, Versions},
            State,
        },
        pubkey::Pubkey,
    };

    #[test]
    fn test_parse_nonce() {
        let nonce_data = Versions::new_current(State::Initialized(Data::default()));
        let nonce_account_data = bincode::serialize(&nonce_data).unwrap();
        assert_eq!(
            parse_nonce(&nonce_account_data).unwrap(),
            UiNonceState::Initialized(UiNonceData {
                authority: Pubkey::default().to_string(),
                blockhash: Hash::default().to_string(),
                fee_calculator: UiFeeCalculator {
                    lamports_per_signature: 0.to_string(),
                },
            }),
        );

        let bad_data = vec![0; 4];
        assert!(parse_nonce(&bad_data).is_err());
    }
}