mod current;
pub use current::{Data, DurableNonce, State};
use {
crate::hash::Hash,
serde_derive::{Deserialize, Serialize},
};
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
pub enum Versions {
Legacy(Box<State>),
Current(Box<State>),
}
impl Versions {
pub fn new(state: State, separate_domains: bool) -> Self {
if separate_domains {
Self::Current(Box::new(state))
} else {
Self::Legacy(Box::new(state))
}
}
pub fn state(&self) -> &State {
match self {
Self::Legacy(state) => state,
Self::Current(state) => state,
}
}
pub fn separate_domains(&self) -> bool {
match self {
Self::Legacy(_) => false,
Self::Current(_) => true,
}
}
pub fn verify_recent_blockhash(
&self,
recent_blockhash: &Hash, separate_domains: bool,
) -> Option<&Data> {
let state = match self {
Self::Legacy(state) => {
if separate_domains {
return None;
} else {
state
}
}
Self::Current(state) => state,
};
match **state {
State::Uninitialized => None,
State::Initialized(ref data) => (recent_blockhash == &data.blockhash()).then(|| data),
}
}
pub fn upgrade(self) -> Option<Self> {
match self {
Self::Legacy(mut state) => {
match *state {
State::Uninitialized => None,
State::Initialized(ref mut data) => {
data.durable_nonce = DurableNonce::from_blockhash(
&data.blockhash(),
true, );
Some(Self::Current(state))
}
}
}
Self::Current(_) => None,
}
}
}
impl From<Versions> for State {
fn from(versions: Versions) -> Self {
match versions {
Versions::Legacy(state) => *state,
Versions::Current(state) => *state,
}
}
}
#[cfg(test)]
mod tests {
use {
super::*,
crate::{fee_calculator::FeeCalculator, hash::hashv, pubkey::Pubkey},
};
#[test]
fn test_verify_recent_blockhash() {
let blockhash: Hash = hashv(&[&[171u8; 32]]);
let versions = Versions::Legacy(Box::new(State::Uninitialized));
for separate_domains in [false, true] {
assert_eq!(
versions.verify_recent_blockhash(&blockhash, separate_domains),
None
);
assert_eq!(
versions.verify_recent_blockhash(&Hash::default(), separate_domains),
None
);
}
let versions = Versions::Current(Box::new(State::Uninitialized));
for separate_domains in [false, true] {
assert_eq!(
versions.verify_recent_blockhash(&blockhash, separate_domains),
None
);
assert_eq!(
versions.verify_recent_blockhash(&Hash::default(), separate_domains),
None
);
}
let durable_nonce =
DurableNonce::from_blockhash(&blockhash, false);
let data = Data {
authority: Pubkey::new_unique(),
durable_nonce,
fee_calculator: FeeCalculator {
lamports_per_signature: 2718,
},
};
let versions = Versions::Legacy(Box::new(State::Initialized(data.clone())));
let separate_domains = false;
assert_eq!(
versions.verify_recent_blockhash(&Hash::default(), separate_domains),
None
);
assert_eq!(
versions.verify_recent_blockhash(&blockhash, separate_domains),
Some(&data)
);
assert_eq!(
versions.verify_recent_blockhash(&data.blockhash(), separate_domains),
Some(&data)
);
assert_eq!(
versions.verify_recent_blockhash(durable_nonce.as_hash(), separate_domains),
Some(&data)
);
let separate_domains = true;
assert_eq!(
versions.verify_recent_blockhash(&Hash::default(), separate_domains),
None
);
assert_eq!(
versions.verify_recent_blockhash(&blockhash, separate_domains),
None
);
assert_eq!(
versions.verify_recent_blockhash(&data.blockhash(), separate_domains),
None
);
assert_eq!(
versions.verify_recent_blockhash(durable_nonce.as_hash(), separate_domains),
None
);
let durable_nonce =
DurableNonce::from_blockhash(&blockhash, true);
assert_ne!(data.durable_nonce, durable_nonce);
let data = Data {
durable_nonce,
..data
};
let versions = Versions::Current(Box::new(State::Initialized(data.clone())));
for separate_domains in [false, true] {
assert_eq!(
versions.verify_recent_blockhash(&blockhash, separate_domains),
None
);
assert_eq!(
versions.verify_recent_blockhash(&Hash::default(), separate_domains),
None
);
assert_eq!(
versions.verify_recent_blockhash(&data.blockhash(), separate_domains),
Some(&data)
);
assert_eq!(
versions.verify_recent_blockhash(durable_nonce.as_hash(), separate_domains),
Some(&data)
);
}
}
#[test]
fn test_nonce_versions_upgrade() {
let versions = Versions::Legacy(Box::new(State::Uninitialized));
assert_eq!(versions.upgrade(), None);
let blockhash: Hash = hashv(&[&[171u8; 32]]);
let durable_nonce =
DurableNonce::from_blockhash(&blockhash, false);
let data = Data {
authority: Pubkey::new_unique(),
durable_nonce,
fee_calculator: FeeCalculator {
lamports_per_signature: 2718,
},
};
let versions = Versions::Legacy(Box::new(State::Initialized(data.clone())));
let durable_nonce =
DurableNonce::from_blockhash(&blockhash, true);
assert_ne!(data.durable_nonce, durable_nonce);
let data = Data {
durable_nonce,
..data
};
let versions = versions.upgrade().unwrap();
assert_eq!(
versions,
Versions::Current(Box::new(State::Initialized(data)))
);
assert_eq!(versions.upgrade(), None);
}
}