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
use crate::rpc_client::RpcClient;
use solana_sdk::{
    account::{Account, ReadableAccount},
    account_utils::StateMut,
    commitment_config::CommitmentConfig,
    nonce::{
        state::{Data, Versions},
        State,
    },
    pubkey::Pubkey,
    system_program,
};

#[derive(Debug, thiserror::Error, PartialEq)]
pub enum Error {
    #[error("invalid account owner")]
    InvalidAccountOwner,
    #[error("invalid account data")]
    InvalidAccountData,
    #[error("unexpected account data size")]
    UnexpectedDataSize,
    #[error("query hash does not match stored hash")]
    InvalidHash,
    #[error("query authority does not match account authority")]
    InvalidAuthority,
    #[error("invalid state for requested operation")]
    InvalidStateForOperation,
    #[error("client error: {0}")]
    Client(String),
}

pub fn get_account(rpc_client: &RpcClient, nonce_pubkey: &Pubkey) -> Result<Account, Error> {
    get_account_with_commitment(rpc_client, nonce_pubkey, CommitmentConfig::default())
}

pub fn get_account_with_commitment(
    rpc_client: &RpcClient,
    nonce_pubkey: &Pubkey,
    commitment: CommitmentConfig,
) -> Result<Account, Error> {
    rpc_client
        .get_account_with_commitment(nonce_pubkey, commitment)
        .map_err(|e| Error::Client(format!("{}", e)))
        .and_then(|result| {
            result
                .value
                .ok_or_else(|| Error::Client(format!("AccountNotFound: pubkey={}", nonce_pubkey)))
        })
        .and_then(|a| match account_identity_ok(&a) {
            Ok(()) => Ok(a),
            Err(e) => Err(e),
        })
}

pub fn account_identity_ok<T: ReadableAccount>(account: &T) -> Result<(), Error> {
    if account.owner() != &system_program::id() {
        Err(Error::InvalidAccountOwner)
    } else if account.data().is_empty() {
        Err(Error::UnexpectedDataSize)
    } else {
        Ok(())
    }
}

pub fn state_from_account<T: ReadableAccount + StateMut<Versions>>(
    account: &T,
) -> Result<State, Error> {
    account_identity_ok(account)?;
    StateMut::<Versions>::state(account)
        .map_err(|_| Error::InvalidAccountData)
        .map(|v| v.convert_to_current())
}

pub fn data_from_account<T: ReadableAccount + StateMut<Versions>>(
    account: &T,
) -> Result<Data, Error> {
    account_identity_ok(account)?;
    state_from_account(account).and_then(|ref s| data_from_state(s).map(|d| d.clone()))
}

pub fn data_from_state(state: &State) -> Result<&Data, Error> {
    match state {
        State::Uninitialized => Err(Error::InvalidStateForOperation),
        State::Initialized(data) => Ok(data),
    }
}