permanently disables durable nonces with chain blockhash domain (#25788)

https://github.com/solana-labs/solana/pull/25744
separated durable nonce and blockhash domains, which will stop double
execution going forward. However it is possible that a durable
transaction has *already* been executed once as a normal transaction and
it is now a valid durable transaction. #25744 cannot stop such
transactions to be re-executed until the nonce accounts are advanced.

This commit adds a new nonce version indicating that the nonce is moved
out of the blockhash domain, and permanently disables durable
transactions for legacy nonces which are in the blockhash domain.
This commit is contained in:
behzad nouri 2022-06-09 15:28:37 +00:00 committed by GitHub
parent e5f36aa371
commit 3c1ce3cc93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 703 additions and 345 deletions

View File

@ -145,7 +145,10 @@ mod test {
assert_eq!(parsed.program, "vote".to_string()); assert_eq!(parsed.program, "vote".to_string());
assert_eq!(parsed.space, VoteState::size_of() as u64); assert_eq!(parsed.space, VoteState::size_of() as u64);
let nonce_data = Versions::new_current(State::Initialized(Data::default())); let nonce_data = Versions::new(
State::Initialized(Data::default()),
true, // separate_domains
);
let nonce_account_data = bincode::serialize(&nonce_data).unwrap(); let nonce_account_data = bincode::serialize(&nonce_data).unwrap();
let parsed = parse_account_data( let parsed = parse_account_data(
&account_pubkey, &account_pubkey,

View File

@ -7,10 +7,9 @@ use {
}; };
pub fn parse_nonce(data: &[u8]) -> Result<UiNonceState, ParseAccountError> { pub fn parse_nonce(data: &[u8]) -> Result<UiNonceState, ParseAccountError> {
let nonce_state: Versions = bincode::deserialize(data) let nonce_versions: Versions = bincode::deserialize(data)
.map_err(|_| ParseAccountError::from(InstructionError::InvalidAccountData))?; .map_err(|_| ParseAccountError::from(InstructionError::InvalidAccountData))?;
let nonce_state = nonce_state.convert_to_current(); match nonce_versions.state() {
match nonce_state {
// This prevents parsing an allocated System-owned account with empty data of any non-zero // 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 // 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 // initialized as a nonce account, and an empty account of the correct length may not be an
@ -58,7 +57,10 @@ mod test {
#[test] #[test]
fn test_parse_nonce() { fn test_parse_nonce() {
let nonce_data = Versions::new_current(State::Initialized(Data::default())); let nonce_data = Versions::new(
State::Initialized(Data::default()),
true, // separate_domains
);
let nonce_account_data = bincode::serialize(&nonce_data).unwrap(); let nonce_account_data = bincode::serialize(&nonce_data).unwrap();
assert_eq!( assert_eq!(
parse_nonce(&nonce_account_data).unwrap(), parse_nonce(&nonce_account_data).unwrap(),

View File

@ -933,11 +933,10 @@ mod tests {
DurableNonce::from_blockhash(&Hash::default(), /*separate_domains:*/ true); DurableNonce::from_blockhash(&Hash::default(), /*separate_domains:*/ true);
let blockhash = *durable_nonce.as_hash(); let blockhash = *durable_nonce.as_hash();
let nonce_pubkey = solana_sdk::pubkey::new_rand(); let nonce_pubkey = solana_sdk::pubkey::new_rand();
let data = Versions::new_current(State::Initialized(nonce::state::Data::new( let data = Versions::new(
nonce_pubkey, State::Initialized(nonce::state::Data::new(nonce_pubkey, durable_nonce, 0)),
durable_nonce, true, // separate_domains
0, );
)));
let valid = Account::new_data(1, &data, &system_program::ID); let valid = Account::new_data(1, &data, &system_program::ID);
assert!(check_nonce_account(&valid.unwrap(), &nonce_pubkey, &blockhash).is_ok()); assert!(check_nonce_account(&valid.unwrap(), &nonce_pubkey, &blockhash).is_ok());
@ -957,11 +956,14 @@ mod tests {
let invalid_durable_nonce = let invalid_durable_nonce =
DurableNonce::from_blockhash(&hash(b"invalid"), /*separate_domains:*/ true); DurableNonce::from_blockhash(&hash(b"invalid"), /*separate_domains:*/ true);
let data = Versions::new_current(State::Initialized(nonce::state::Data::new( let data = Versions::new(
nonce_pubkey, State::Initialized(nonce::state::Data::new(
invalid_durable_nonce, nonce_pubkey,
0, invalid_durable_nonce,
))); 0,
)),
true, // separate_domains
);
let invalid_hash = Account::new_data(1, &data, &system_program::ID).unwrap(); let invalid_hash = Account::new_data(1, &data, &system_program::ID).unwrap();
if let CliError::InvalidNonce(err) = if let CliError::InvalidNonce(err) =
check_nonce_account(&invalid_hash, &nonce_pubkey, &blockhash).unwrap_err() check_nonce_account(&invalid_hash, &nonce_pubkey, &blockhash).unwrap_err()
@ -976,11 +978,14 @@ mod tests {
} }
let new_nonce_authority = solana_sdk::pubkey::new_rand(); let new_nonce_authority = solana_sdk::pubkey::new_rand();
let data = Versions::new_current(State::Initialized(nonce::state::Data::new( let data = Versions::new(
new_nonce_authority, State::Initialized(nonce::state::Data::new(
durable_nonce, new_nonce_authority,
0, durable_nonce,
))); 0,
)),
true, // separate_domains
);
let invalid_authority = Account::new_data(1, &data, &system_program::ID); let invalid_authority = Account::new_data(1, &data, &system_program::ID);
if let CliError::InvalidNonce(err) = if let CliError::InvalidNonce(err) =
check_nonce_account(&invalid_authority.unwrap(), &nonce_pubkey, &blockhash).unwrap_err() check_nonce_account(&invalid_authority.unwrap(), &nonce_pubkey, &blockhash).unwrap_err()
@ -994,7 +999,7 @@ mod tests {
); );
} }
let data = Versions::new_current(State::Uninitialized); let data = Versions::new(State::Uninitialized, /*separate_domains:*/ true);
let invalid_state = Account::new_data(1, &data, &system_program::ID); let invalid_state = Account::new_data(1, &data, &system_program::ID);
if let CliError::InvalidNonce(err) = if let CliError::InvalidNonce(err) =
check_nonce_account(&invalid_state.unwrap(), &nonce_pubkey, &blockhash).unwrap_err() check_nonce_account(&invalid_state.unwrap(), &nonce_pubkey, &blockhash).unwrap_err()
@ -1005,7 +1010,8 @@ mod tests {
#[test] #[test]
fn test_account_identity_ok() { fn test_account_identity_ok() {
let nonce_account = nonce_account::create_account(1).into_inner(); let nonce_account =
nonce_account::create_account(1, /*separate_domains:*/ true).into_inner();
assert_eq!(account_identity_ok(&nonce_account), Ok(())); assert_eq!(account_identity_ok(&nonce_account), Ok(()));
let system_account = Account::new(1, 0, &system_program::id()); let system_account = Account::new(1, 0, &system_program::id());
@ -1024,14 +1030,18 @@ mod tests {
#[test] #[test]
fn test_state_from_account() { fn test_state_from_account() {
let mut nonce_account = nonce_account::create_account(1).into_inner(); let mut nonce_account =
nonce_account::create_account(1, /*separate_domains:*/ true).into_inner();
assert_eq!(state_from_account(&nonce_account), Ok(State::Uninitialized)); assert_eq!(state_from_account(&nonce_account), Ok(State::Uninitialized));
let durable_nonce = let durable_nonce =
DurableNonce::from_blockhash(&Hash::new(&[42u8; 32]), /*separate_domains:*/ true); DurableNonce::from_blockhash(&Hash::new(&[42u8; 32]), /*separate_domains:*/ true);
let data = nonce::state::Data::new(Pubkey::new(&[1u8; 32]), durable_nonce, 42); let data = nonce::state::Data::new(Pubkey::new(&[1u8; 32]), durable_nonce, 42);
nonce_account nonce_account
.set_state(&Versions::new_current(State::Initialized(data.clone()))) .set_state(&Versions::new(
State::Initialized(data.clone()),
true, // separate_domains
))
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
state_from_account(&nonce_account), state_from_account(&nonce_account),
@ -1047,7 +1057,8 @@ mod tests {
#[test] #[test]
fn test_data_from_helpers() { fn test_data_from_helpers() {
let mut nonce_account = nonce_account::create_account(1).into_inner(); let mut nonce_account =
nonce_account::create_account(1, /*separate_domains:*/ true).into_inner();
let state = state_from_account(&nonce_account).unwrap(); let state = state_from_account(&nonce_account).unwrap();
assert_eq!( assert_eq!(
data_from_state(&state), data_from_state(&state),
@ -1062,7 +1073,10 @@ mod tests {
DurableNonce::from_blockhash(&Hash::new(&[42u8; 32]), /*separate_domains:*/ true); DurableNonce::from_blockhash(&Hash::new(&[42u8; 32]), /*separate_domains:*/ true);
let data = nonce::state::Data::new(Pubkey::new(&[1u8; 32]), durable_nonce, 42); let data = nonce::state::Data::new(Pubkey::new(&[1u8; 32]), durable_nonce, 42);
nonce_account nonce_account
.set_state(&Versions::new_current(State::Initialized(data.clone()))) .set_state(&Versions::new(
State::Initialized(data.clone()),
true, // separate_domains
))
.unwrap(); .unwrap();
let state = state_from_account(&nonce_account).unwrap(); let state = state_from_account(&nonce_account).unwrap();
assert_eq!(data_from_state(&state), Ok(&data)); assert_eq!(data_from_state(&state), Ok(&data));

View File

@ -427,7 +427,10 @@ mod tests {
}; };
let nonce_account = Account::new_data_with_space( let nonce_account = Account::new_data_with_space(
42, 42,
&nonce::state::Versions::new_current(nonce::State::Initialized(data)), &nonce::state::Versions::new(
nonce::State::Initialized(data),
true, // separate_domains
),
nonce::State::size(), nonce::State::size(),
&system_program::id(), &system_program::id(),
) )

View File

@ -129,9 +129,8 @@ pub fn state_from_account<T: ReadableAccount + StateMut<Versions>>(
account: &T, account: &T,
) -> Result<State, Error> { ) -> Result<State, Error> {
account_identity_ok(account)?; account_identity_ok(account)?;
StateMut::<Versions>::state(account) let versions = StateMut::<Versions>::state(account).map_err(|_| Error::InvalidAccountData)?;
.map_err(|_| Error::InvalidAccountData) Ok(State::from(versions))
.map(|v| v.convert_to_current())
} }
/// Deserialize the state data of a durable transaction nonce account. /// Deserialize the state data of a durable transaction nonce account.

View File

@ -5573,11 +5573,10 @@ pub mod tests {
let authority = Pubkey::new_unique(); let authority = Pubkey::new_unique();
let account = AccountSharedData::new_data( let account = AccountSharedData::new_data(
42, 42,
&nonce::state::Versions::new_current(nonce::State::new_initialized( &nonce::state::Versions::new(
&authority, nonce::State::new_initialized(&authority, DurableNonce::default(), 1000),
DurableNonce::default(), true, // separate_domains
1000, ),
)),
&system_program::id(), &system_program::id(),
) )
.unwrap(); .unwrap();
@ -5608,8 +5607,8 @@ pub mod tests {
system_program::id().to_string(), system_program::id().to_string(),
{"filters": [{ {"filters": [{
"memcmp": { "memcmp": {
"offset": 0, "offset": 4,
"bytes": bs58::encode(vec![1, 0, 0, 0]).into_string(), "bytes": bs58::encode(vec![0, 0, 0, 0]).into_string(),
}, },
}]}, }]},
])), ])),

View File

@ -323,13 +323,15 @@ pub(crate) mod tests {
let expected_transaction = transaction.clone(); let expected_transaction = transaction.clone();
let pubkey = Pubkey::new_unique(); let pubkey = Pubkey::new_unique();
let mut nonce_account = nonce_account::create_account(1).into_inner(); let mut nonce_account =
nonce_account::create_account(1, /*separate_domains:*/ true).into_inner();
let durable_nonce = let durable_nonce =
DurableNonce::from_blockhash(&Hash::new(&[42u8; 32]), /*separate_domains:*/ true); DurableNonce::from_blockhash(&Hash::new(&[42u8; 32]), /*separate_domains:*/ true);
let data = nonce::state::Data::new(Pubkey::new(&[1u8; 32]), durable_nonce, 42); let data = nonce::state::Data::new(Pubkey::new(&[1u8; 32]), durable_nonce, 42);
nonce_account nonce_account
.set_state(&nonce::state::Versions::new_current( .set_state(&nonce::state::Versions::new(
nonce::State::Initialized(data), nonce::State::Initialized(data),
true, // separate_domains
)) ))
.unwrap(); .unwrap();

View File

@ -1196,7 +1196,7 @@ impl Accounts {
res: &'a [TransactionExecutionResult], res: &'a [TransactionExecutionResult],
loaded: &'a mut [TransactionLoadResult], loaded: &'a mut [TransactionLoadResult],
rent_collector: &RentCollector, rent_collector: &RentCollector,
durable_nonce: &DurableNonce, durable_nonce: &(DurableNonce, /*separate_domains:*/ bool),
lamports_per_signature: u64, lamports_per_signature: u64,
leave_nonce_on_success: bool, leave_nonce_on_success: bool,
) { ) {
@ -1228,7 +1228,7 @@ impl Accounts {
execution_results: &'a [TransactionExecutionResult], execution_results: &'a [TransactionExecutionResult],
load_results: &'a mut [TransactionLoadResult], load_results: &'a mut [TransactionLoadResult],
rent_collector: &RentCollector, rent_collector: &RentCollector,
durable_nonce: &DurableNonce, durable_nonce: &(DurableNonce, /*separate_domains:*/ bool),
lamports_per_signature: u64, lamports_per_signature: u64,
leave_nonce_on_success: bool, leave_nonce_on_success: bool,
) -> Vec<(&'a Pubkey, &'a AccountSharedData)> { ) -> Vec<(&'a Pubkey, &'a AccountSharedData)> {
@ -1315,7 +1315,7 @@ fn prepare_if_nonce_account<'a>(
execution_result: &Result<()>, execution_result: &Result<()>,
is_fee_payer: bool, is_fee_payer: bool,
maybe_nonce: Option<(&'a NonceFull, bool)>, maybe_nonce: Option<(&'a NonceFull, bool)>,
durable_nonce: &DurableNonce, &(durable_nonce, separate_domains): &(DurableNonce, bool),
lamports_per_signature: u64, lamports_per_signature: u64,
) -> bool { ) -> bool {
if let Some((nonce, rollback)) = maybe_nonce { if let Some((nonce, rollback)) = maybe_nonce {
@ -1334,17 +1334,15 @@ fn prepare_if_nonce_account<'a>(
// //
// Since we know we are dealing with a valid nonce account, // Since we know we are dealing with a valid nonce account,
// unwrap is safe here // unwrap is safe here
let state = StateMut::<NonceVersions>::state(nonce.account()) let nonce_versions = StateMut::<NonceVersions>::state(nonce.account()).unwrap();
.unwrap() if let NonceState::Initialized(ref data) = nonce_versions.state() {
.convert_to_current(); let nonce_state = NonceState::new_initialized(
if let NonceState::Initialized(ref data) = state { &data.authority,
account durable_nonce,
.set_state(&NonceVersions::new_current(NonceState::new_initialized( lamports_per_signature,
&data.authority, );
*durable_nonce, let nonce_versions = NonceVersions::new(nonce_state, separate_domains);
lamports_per_signature, account.set_state(&nonce_versions).unwrap();
)))
.unwrap();
} }
true true
} else { } else {
@ -1402,6 +1400,7 @@ mod tests {
bank::{DurableNonceFee, TransactionExecutionDetails}, bank::{DurableNonceFee, TransactionExecutionDetails},
rent_collector::RentCollector, rent_collector::RentCollector,
}, },
assert_matches::assert_matches,
solana_address_lookup_table_program::state::LookupTableMeta, solana_address_lookup_table_program::state::LookupTableMeta,
solana_program_runtime::invoke_context::Executors, solana_program_runtime::invoke_context::Executors,
solana_sdk::{ solana_sdk::{
@ -1725,7 +1724,10 @@ mod tests {
nonce.pubkey(), nonce.pubkey(),
AccountSharedData::new_data( AccountSharedData::new_data(
min_balance * 2, min_balance * 2,
&NonceVersions::new_current(NonceState::Initialized(nonce::state::Data::default())), &NonceVersions::new(
NonceState::Initialized(nonce::state::Data::default()),
true, // separate_domains
),
&system_program::id(), &system_program::id(),
) )
.unwrap(), .unwrap(),
@ -3028,7 +3030,7 @@ mod tests {
&execution_results, &execution_results,
loaded.as_mut_slice(), loaded.as_mut_slice(),
&rent_collector, &rent_collector,
&DurableNonce::default(), &(DurableNonce::default(), /*separate_domains:*/ true),
0, 0,
true, // leave_nonce_on_success true, // leave_nonce_on_success
); );
@ -3174,20 +3176,24 @@ mod tests {
Pubkey, Pubkey,
AccountSharedData, AccountSharedData,
AccountSharedData, AccountSharedData,
DurableNonce, (DurableNonce, /*separate_domains:*/ bool),
u64, u64,
Option<AccountSharedData>, Option<AccountSharedData>,
) { ) {
let data = let data = NonceVersions::new(
NonceVersions::new_current(NonceState::Initialized(nonce::state::Data::default())); NonceState::Initialized(nonce::state::Data::default()),
true, // separate_domains
);
let account = AccountSharedData::new_data(42, &data, &system_program::id()).unwrap(); let account = AccountSharedData::new_data(42, &data, &system_program::id()).unwrap();
let mut pre_account = account.clone(); let mut pre_account = account.clone();
pre_account.set_lamports(43); pre_account.set_lamports(43);
let durable_nonce =
DurableNonce::from_blockhash(&Hash::new(&[1u8; 32]), /*separate_domains:*/ true);
( (
Pubkey::default(), Pubkey::default(),
pre_account, pre_account,
account, account,
DurableNonce::from_blockhash(&Hash::new(&[1u8; 32]), /*separate_domains:*/ true), (durable_nonce, /*separate_domains:*/ true),
1234, 1234,
None, None,
) )
@ -3199,7 +3205,7 @@ mod tests {
tx_result: &Result<()>, tx_result: &Result<()>,
is_fee_payer: bool, is_fee_payer: bool,
maybe_nonce: Option<(&NonceFull, bool)>, maybe_nonce: Option<(&NonceFull, bool)>,
durable_nonce: &DurableNonce, durable_nonce: &(DurableNonce, /*separate_domains:*/ bool),
lamports_per_signature: u64, lamports_per_signature: u64,
expect_account: &AccountSharedData, expect_account: &AccountSharedData,
) -> bool { ) -> bool {
@ -3245,9 +3251,14 @@ mod tests {
let mut expect_account = pre_account; let mut expect_account = pre_account;
expect_account expect_account
.set_state(&NonceVersions::new_current(NonceState::Initialized( .set_state(&NonceVersions::new(
nonce::state::Data::new(Pubkey::default(), blockhash, lamports_per_signature), NonceState::Initialized(nonce::state::Data::new(
))) Pubkey::default(),
blockhash.0,
lamports_per_signature,
)),
true, // separate_domains
))
.unwrap(); .unwrap();
assert!(run_prepare_if_nonce_account_test( assert!(run_prepare_if_nonce_account_test(
@ -3331,9 +3342,14 @@ mod tests {
let nonce = NonceFull::new(pre_account_address, pre_account, maybe_fee_payer_account); let nonce = NonceFull::new(pre_account_address, pre_account, maybe_fee_payer_account);
expect_account expect_account
.set_state(&NonceVersions::new_current(NonceState::Initialized( .set_state(&NonceVersions::new(
nonce::state::Data::new(Pubkey::default(), blockhash, lamports_per_signature), NonceState::Initialized(nonce::state::Data::new(
))) Pubkey::default(),
blockhash.0,
lamports_per_signature,
)),
true, // separate_domains
))
.unwrap(); .unwrap();
assert!(run_prepare_if_nonce_account_test( assert!(run_prepare_if_nonce_account_test(
@ -3373,7 +3389,7 @@ mod tests {
)), )),
false, false,
Some((&nonce, true)), Some((&nonce, true)),
&DurableNonce::default(), &(DurableNonce::default(), /*separate_domains:*/ true),
1, 1,
&post_fee_payer_account.clone(), &post_fee_payer_account.clone(),
)); ));
@ -3384,7 +3400,7 @@ mod tests {
&Ok(()), &Ok(()),
true, true,
Some((&nonce, true)), Some((&nonce, true)),
&DurableNonce::default(), &(DurableNonce::default(), /*separate_domains:*/ true),
1, 1,
&post_fee_payer_account.clone(), &post_fee_payer_account.clone(),
)); ));
@ -3398,7 +3414,7 @@ mod tests {
)), )),
true, true,
None, None,
&DurableNonce::default(), &(DurableNonce::default(), /*separate_domains:*/ true),
1, 1,
&post_fee_payer_account.clone(), &post_fee_payer_account.clone(),
)); ));
@ -3412,7 +3428,7 @@ mod tests {
)), )),
true, true,
Some((&nonce, true)), Some((&nonce, true)),
&DurableNonce::default(), &(DurableNonce::default(), /*separate_domains:*/ true),
1, 1,
&pre_fee_payer_account, &pre_fee_payer_account,
)); ));
@ -3429,9 +3445,14 @@ mod tests {
let to_address = Pubkey::new_unique(); let to_address = Pubkey::new_unique();
let durable_nonce = let durable_nonce =
DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true); DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true);
let nonce_state = NonceVersions::new_current(NonceState::Initialized( let nonce_state = NonceVersions::new(
nonce::state::Data::new(nonce_authority.pubkey(), durable_nonce, 0), NonceState::Initialized(nonce::state::Data::new(
)); nonce_authority.pubkey(),
durable_nonce,
0,
)),
true, // separate_domains
);
let nonce_account_post = let nonce_account_post =
AccountSharedData::new_data(43, &nonce_state, &system_program::id()).unwrap(); AccountSharedData::new_data(43, &nonce_state, &system_program::id()).unwrap();
let from_account_post = AccountSharedData::new(4199, 0, &Pubkey::default()); let from_account_post = AccountSharedData::new(4199, 0, &Pubkey::default());
@ -3456,9 +3477,14 @@ mod tests {
let durable_nonce = let durable_nonce =
DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true); DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true);
let nonce_state = NonceVersions::new_current(NonceState::Initialized( let nonce_state = NonceVersions::new(
nonce::state::Data::new(nonce_authority.pubkey(), durable_nonce, 0), NonceState::Initialized(nonce::state::Data::new(
)); nonce_authority.pubkey(),
durable_nonce,
0,
)),
true, // separate_domains
);
let nonce_account_pre = let nonce_account_pre =
AccountSharedData::new_data(42, &nonce_state, &system_program::id()).unwrap(); AccountSharedData::new_data(42, &nonce_state, &system_program::id()).unwrap();
let from_account_pre = AccountSharedData::new(4242, 0, &Pubkey::default()); let from_account_pre = AccountSharedData::new(4242, 0, &Pubkey::default());
@ -3503,7 +3529,7 @@ mod tests {
&execution_results, &execution_results,
loaded.as_mut_slice(), loaded.as_mut_slice(),
&rent_collector, &rent_collector,
&durable_nonce, &(durable_nonce, /*separate_domains:*/ true),
0, 0,
true, // leave_nonce_on_success true, // leave_nonce_on_success
); );
@ -3527,11 +3553,14 @@ mod tests {
collected_nonce_account.lamports(), collected_nonce_account.lamports(),
nonce_account_pre.lamports(), nonce_account_pre.lamports(),
); );
assert!(nonce_account::verify_nonce_account( assert_matches!(
&collected_nonce_account, nonce_account::verify_nonce_account(
durable_nonce.as_hash(), &collected_nonce_account,
) durable_nonce.as_hash(),
.is_some()); true, // separate_domins
),
Some(_)
);
} }
#[test] #[test]
@ -3545,9 +3574,14 @@ mod tests {
let to_address = Pubkey::new_unique(); let to_address = Pubkey::new_unique();
let durable_nonce = let durable_nonce =
DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true); DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true);
let nonce_state = NonceVersions::new_current(NonceState::Initialized( let nonce_state = NonceVersions::new(
nonce::state::Data::new(nonce_authority.pubkey(), durable_nonce, 0), NonceState::Initialized(nonce::state::Data::new(
)); nonce_authority.pubkey(),
durable_nonce,
0,
)),
true, // separate_domains
);
let nonce_account_post = let nonce_account_post =
AccountSharedData::new_data(43, &nonce_state, &system_program::id()).unwrap(); AccountSharedData::new_data(43, &nonce_state, &system_program::id()).unwrap();
let from_account_post = AccountSharedData::new(4200, 0, &Pubkey::default()); let from_account_post = AccountSharedData::new(4200, 0, &Pubkey::default());
@ -3572,9 +3606,14 @@ mod tests {
let durable_nonce = let durable_nonce =
DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true); DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true);
let nonce_state = NonceVersions::new_current(NonceState::Initialized( let nonce_state = NonceVersions::new(
nonce::state::Data::new(nonce_authority.pubkey(), durable_nonce, 0), NonceState::Initialized(nonce::state::Data::new(
)); nonce_authority.pubkey(),
durable_nonce,
0,
)),
true, // separate_domains
);
let nonce_account_pre = let nonce_account_pre =
AccountSharedData::new_data(42, &nonce_state, &system_program::id()).unwrap(); AccountSharedData::new_data(42, &nonce_state, &system_program::id()).unwrap();
@ -3618,7 +3657,7 @@ mod tests {
&execution_results, &execution_results,
loaded.as_mut_slice(), loaded.as_mut_slice(),
&rent_collector, &rent_collector,
&durable_nonce, &(durable_nonce, /*separate_domains:*/ true),
0, 0,
true, // leave_nonce_on_success true, // leave_nonce_on_success
); );
@ -3633,11 +3672,14 @@ mod tests {
collected_nonce_account.lamports(), collected_nonce_account.lamports(),
nonce_account_pre.lamports() nonce_account_pre.lamports()
); );
assert!(nonce_account::verify_nonce_account( assert_matches!(
&collected_nonce_account, nonce_account::verify_nonce_account(
durable_nonce.as_hash(), &collected_nonce_account,
) durable_nonce.as_hash(),
.is_some()); true, // separate_domins
),
Some(_)
);
} }
#[test] #[test]

View File

@ -4021,6 +4021,11 @@ impl Bank {
self.rc.accounts.accounts_db.set_shrink_paths(paths); self.rc.accounts.accounts_db.set_shrink_paths(paths);
} }
pub fn separate_nonce_from_blockhash(&self) -> bool {
self.feature_set
.is_active(&feature_set::separate_nonce_from_blockhash::id())
}
fn check_age<'a>( fn check_age<'a>(
&self, &self,
txs: impl Iterator<Item = &'a SanitizedTransaction>, txs: impl Iterator<Item = &'a SanitizedTransaction>,
@ -4028,9 +4033,7 @@ impl Bank {
max_age: usize, max_age: usize,
error_counters: &mut TransactionErrorMetrics, error_counters: &mut TransactionErrorMetrics,
) -> Vec<TransactionCheckResult> { ) -> Vec<TransactionCheckResult> {
let separate_nonce_from_blockhash = self let separate_nonce_from_blockhash = self.separate_nonce_from_blockhash();
.feature_set
.is_active(&feature_set::separate_nonce_from_blockhash::id());
let enable_durable_nonce = separate_nonce_from_blockhash let enable_durable_nonce = separate_nonce_from_blockhash
&& self && self
.feature_set .feature_set
@ -4112,8 +4115,11 @@ impl Bank {
let nonce_address = let nonce_address =
message.get_durable_nonce(self.feature_set.is_active(&nonce_must_be_writable::id()))?; message.get_durable_nonce(self.feature_set.is_active(&nonce_must_be_writable::id()))?;
let nonce_account = self.get_account_with_fixed_root(nonce_address)?; let nonce_account = self.get_account_with_fixed_root(nonce_address)?;
let nonce_data = let nonce_data = nonce_account::verify_nonce_account(
nonce_account::verify_nonce_account(&nonce_account, message.recent_blockhash())?; &nonce_account,
message.recent_blockhash(),
self.separate_nonce_from_blockhash(),
)?;
if self if self
.feature_set .feature_set
@ -4911,10 +4917,10 @@ impl Bank {
let mut write_time = Measure::start("write_time"); let mut write_time = Measure::start("write_time");
let durable_nonce = { let durable_nonce = {
let separate_nonce_from_blockhash = self let separate_nonce_from_blockhash = self.separate_nonce_from_blockhash();
.feature_set let durable_nonce =
.is_active(&feature_set::separate_nonce_from_blockhash::id()); DurableNonce::from_blockhash(&last_blockhash, separate_nonce_from_blockhash);
DurableNonce::from_blockhash(&last_blockhash, separate_nonce_from_blockhash) (durable_nonce, separate_nonce_from_blockhash)
}; };
self.rc.accounts.store_cached( self.rc.accounts.store_cached(
self.slot(), self.slot(),
@ -7724,9 +7730,14 @@ pub(crate) mod tests {
DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true); DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true);
let nonce_account = AccountSharedData::new_data( let nonce_account = AccountSharedData::new_data(
43, 43,
&nonce::state::Versions::new_current(nonce::State::Initialized( &nonce::state::Versions::new(
nonce::state::Data::new(Pubkey::default(), durable_nonce, lamports_per_signature), nonce::State::Initialized(nonce::state::Data::new(
)), Pubkey::default(),
durable_nonce,
lamports_per_signature,
)),
true, // separate_domains
),
&system_program::id(), &system_program::id(),
) )
.unwrap(); .unwrap();
@ -10249,9 +10260,10 @@ pub(crate) mod tests {
let nonce = Keypair::new(); let nonce = Keypair::new();
let nonce_account = AccountSharedData::new_data( let nonce_account = AccountSharedData::new_data(
min_balance + 42, min_balance + 42,
&nonce::state::Versions::new_current(nonce::State::Initialized( &nonce::state::Versions::new(
nonce::state::Data::default(), nonce::State::Initialized(nonce::state::Data::default()),
)), true, // separate_domains
),
&system_program::id(), &system_program::id(),
) )
.unwrap(); .unwrap();
@ -12439,14 +12451,12 @@ pub(crate) mod tests {
} }
fn get_nonce_blockhash(bank: &Bank, nonce_pubkey: &Pubkey) -> Option<Hash> { fn get_nonce_blockhash(bank: &Bank, nonce_pubkey: &Pubkey) -> Option<Hash> {
bank.get_account(nonce_pubkey).and_then(|acc| { let account = bank.get_account(nonce_pubkey)?;
let state = let nonce_versions = StateMut::<nonce::state::Versions>::state(&account);
StateMut::<nonce::state::Versions>::state(&acc).map(|v| v.convert_to_current()); match nonce_versions.ok()?.state() {
match state { nonce::State::Initialized(ref data) => Some(data.blockhash()),
Ok(nonce::State::Initialized(ref data)) => Some(data.blockhash()), _ => None,
_ => None, }
}
})
} }
fn nonce_setup( fn nonce_setup(
@ -12689,9 +12699,10 @@ pub(crate) mod tests {
let nonce = Keypair::new(); let nonce = Keypair::new();
let nonce_account = AccountSharedData::new_data( let nonce_account = AccountSharedData::new_data(
42_424_242, 42_424_242,
&nonce::state::Versions::new_current(nonce::State::Initialized( &nonce::state::Versions::new(
nonce::state::Data::default(), nonce::State::Initialized(nonce::state::Data::default()),
)), true, // separate_domains
),
&system_program::id(), &system_program::id(),
) )
.unwrap(); .unwrap();
@ -12721,9 +12732,14 @@ pub(crate) mod tests {
DurableNonce::from_blockhash(&bank.last_blockhash(), true /* separate domains */); DurableNonce::from_blockhash(&bank.last_blockhash(), true /* separate domains */);
let nonce_account = AccountSharedData::new_data( let nonce_account = AccountSharedData::new_data(
42_424_242, 42_424_242,
&nonce::state::Versions::new_current(nonce::State::Initialized( &nonce::state::Versions::new(
nonce::state::Data::new(nonce_authority, durable_nonce, 5000), nonce::State::Initialized(nonce::state::Data::new(
)), nonce_authority,
durable_nonce,
5000,
)),
true, // separate_domains
),
&system_program::id(), &system_program::id(),
) )
.unwrap(); .unwrap();
@ -13202,10 +13218,9 @@ pub(crate) mod tests {
let (stored_nonce_hash, stored_fee_calculator) = bank let (stored_nonce_hash, stored_fee_calculator) = bank
.get_account(&nonce_pubkey) .get_account(&nonce_pubkey)
.and_then(|acc| { .and_then(|acc| {
let state = let nonce_versions = StateMut::<nonce::state::Versions>::state(&acc);
StateMut::<nonce::state::Versions>::state(&acc).map(|v| v.convert_to_current()); match nonce_versions.ok()?.state() {
match state { nonce::State::Initialized(ref data) => {
Ok(nonce::State::Initialized(ref data)) => {
Some((data.blockhash(), data.fee_calculator)) Some((data.blockhash(), data.fee_calculator))
} }
_ => None, _ => None,
@ -13239,10 +13254,9 @@ pub(crate) mod tests {
let (nonce_hash, fee_calculator) = bank let (nonce_hash, fee_calculator) = bank
.get_account(&nonce_pubkey) .get_account(&nonce_pubkey)
.and_then(|acc| { .and_then(|acc| {
let state = let nonce_versions = StateMut::<nonce::state::Versions>::state(&acc);
StateMut::<nonce::state::Versions>::state(&acc).map(|v| v.convert_to_current()); match nonce_versions.ok()?.state() {
match state { nonce::State::Initialized(ref data) => {
Ok(nonce::State::Initialized(ref data)) => {
Some((data.blockhash(), data.fee_calculator)) Some((data.blockhash(), data.fee_calculator))
} }
_ => None, _ => None,
@ -13272,10 +13286,9 @@ pub(crate) mod tests {
let (stored_nonce_hash, stored_fee_calculator) = bank let (stored_nonce_hash, stored_fee_calculator) = bank
.get_account(&nonce_pubkey) .get_account(&nonce_pubkey)
.and_then(|acc| { .and_then(|acc| {
let state = let nonce_versions = StateMut::<nonce::state::Versions>::state(&acc);
StateMut::<nonce::state::Versions>::state(&acc).map(|v| v.convert_to_current()); match nonce_versions.ok()?.state() {
match state { nonce::State::Initialized(ref data) => {
Ok(nonce::State::Initialized(ref data)) => {
Some((data.blockhash(), data.fee_calculator)) Some((data.blockhash(), data.fee_calculator))
} }
_ => None, _ => None,
@ -13309,10 +13322,9 @@ pub(crate) mod tests {
let (nonce_hash, fee_calculator) = bank let (nonce_hash, fee_calculator) = bank
.get_account(&nonce_pubkey) .get_account(&nonce_pubkey)
.and_then(|acc| { .and_then(|acc| {
let state = let nonce_versions = StateMut::<nonce::state::Versions>::state(&acc);
StateMut::<nonce::state::Versions>::state(&acc).map(|v| v.convert_to_current()); match nonce_versions.ok()?.state() {
match state { nonce::State::Initialized(ref data) => {
Ok(nonce::State::Initialized(ref data)) => {
Some((data.blockhash(), data.fee_calculator)) Some((data.blockhash(), data.fee_calculator))
} }
_ => None, _ => None,

View File

@ -16,11 +16,13 @@ use {
std::collections::HashSet, std::collections::HashSet,
}; };
fn get_durable_nonce(invoke_context: &InvokeContext) -> DurableNonce { fn get_durable_nonce(invoke_context: &InvokeContext) -> (DurableNonce, /*separate_domains:*/ bool) {
let separate_nonce_from_blockhash = invoke_context let separate_nonce_from_blockhash = invoke_context
.feature_set .feature_set
.is_active(&feature_set::separate_nonce_from_blockhash::id()); .is_active(&feature_set::separate_nonce_from_blockhash::id());
DurableNonce::from_blockhash(&invoke_context.blockhash, separate_nonce_from_blockhash) let durable_nonce =
DurableNonce::from_blockhash(&invoke_context.blockhash, separate_nonce_from_blockhash);
(durable_nonce, separate_nonce_from_blockhash)
} }
pub fn advance_nonce_account( pub fn advance_nonce_account(
@ -46,7 +48,7 @@ pub fn advance_nonce_account(
} }
let state: Versions = account.get_state()?; let state: Versions = account.get_state()?;
match state.convert_to_current() { match state.state() {
State::Initialized(data) => { State::Initialized(data) => {
if !signers.contains(&data.authority) { if !signers.contains(&data.authority) {
ic_msg!( ic_msg!(
@ -56,7 +58,7 @@ pub fn advance_nonce_account(
); );
return Err(InstructionError::MissingRequiredSignature); return Err(InstructionError::MissingRequiredSignature);
} }
let next_durable_nonce = get_durable_nonce(invoke_context); let (next_durable_nonce, separate_domains) = get_durable_nonce(invoke_context);
if data.durable_nonce == next_durable_nonce { if data.durable_nonce == next_durable_nonce {
ic_msg!( ic_msg!(
invoke_context, invoke_context,
@ -73,9 +75,12 @@ pub fn advance_nonce_account(
next_durable_nonce, next_durable_nonce,
invoke_context.lamports_per_signature, invoke_context.lamports_per_signature,
); );
account.set_state(&Versions::new_current(State::Initialized(new_data))) account.set_state(&Versions::new(
State::Initialized(new_data),
separate_domains,
))
} }
_ => { State::Uninitialized => {
ic_msg!( ic_msg!(
invoke_context, invoke_context,
"Advance nonce account: Account {} state is invalid", "Advance nonce account: Account {} state is invalid",
@ -119,7 +124,7 @@ pub fn withdraw_nonce_account(
} }
let state: Versions = from.get_state()?; let state: Versions = from.get_state()?;
let signer = match state.convert_to_current() { let signer = match state.state() {
State::Uninitialized => { State::Uninitialized => {
if lamports > from.get_lamports() { if lamports > from.get_lamports() {
ic_msg!( ic_msg!(
@ -134,7 +139,8 @@ pub fn withdraw_nonce_account(
} }
State::Initialized(ref data) => { State::Initialized(ref data) => {
if lamports == from.get_lamports() { if lamports == from.get_lamports() {
if data.durable_nonce == get_durable_nonce(invoke_context) { let (durable_nonce, separate_domains) = get_durable_nonce(invoke_context);
if data.durable_nonce == durable_nonce {
ic_msg!( ic_msg!(
invoke_context, invoke_context,
"Withdraw nonce account: nonce can only advance once per slot" "Withdraw nonce account: nonce can only advance once per slot"
@ -144,7 +150,7 @@ pub fn withdraw_nonce_account(
merge_nonce_error_into_system_error, merge_nonce_error_into_system_error,
)); ));
} }
from.set_state(&Versions::new_current(State::Uninitialized))?; from.set_state(&Versions::new(State::Uninitialized, separate_domains))?;
} else { } else {
let min_balance = rent.minimum_balance(from.get_data().len()); let min_balance = rent.minimum_balance(from.get_data().len());
let amount = checked_add(lamports, min_balance)?; let amount = checked_add(lamports, min_balance)?;
@ -204,8 +210,7 @@ pub fn initialize_nonce_account(
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
let state: Versions = account.get_state()?; match account.get_state::<Versions>()?.state() {
match state.convert_to_current() {
State::Uninitialized => { State::Uninitialized => {
let min_balance = rent.minimum_balance(account.get_data().len()); let min_balance = rent.minimum_balance(account.get_data().len());
if account.get_lamports() < min_balance { if account.get_lamports() < min_balance {
@ -217,14 +222,16 @@ pub fn initialize_nonce_account(
); );
return Err(InstructionError::InsufficientFunds); return Err(InstructionError::InsufficientFunds);
} }
let (durable_nonce, separate_domains) = get_durable_nonce(invoke_context);
let data = nonce::state::Data::new( let data = nonce::state::Data::new(
*nonce_authority, *nonce_authority,
get_durable_nonce(invoke_context), durable_nonce,
invoke_context.lamports_per_signature, invoke_context.lamports_per_signature,
); );
account.set_state(&Versions::new_current(State::Initialized(data))) let state = State::Initialized(data);
account.set_state(&Versions::new(state, separate_domains))
} }
_ => { State::Initialized(_) => {
ic_msg!( ic_msg!(
invoke_context, invoke_context,
"Initialize nonce account: Account {} state is invalid", "Initialize nonce account: Account {} state is invalid",
@ -262,7 +269,8 @@ pub fn authorize_nonce_account(
} }
let state: Versions = account.get_state()?; let state: Versions = account.get_state()?;
match state.convert_to_current() { let separate_domains = state.separate_domains();
match state.state() {
State::Initialized(data) => { State::Initialized(data) => {
if !signers.contains(&data.authority) { if !signers.contains(&data.authority) {
ic_msg!( ic_msg!(
@ -277,9 +285,12 @@ pub fn authorize_nonce_account(
data.durable_nonce, data.durable_nonce,
data.get_lamports_per_signature(), data.get_lamports_per_signature(),
); );
account.set_state(&Versions::new_current(State::Initialized(new_data))) account.set_state(&Versions::new(
State::Initialized(new_data),
separate_domains,
))
} }
_ => { State::Uninitialized => {
ic_msg!( ic_msg!(
invoke_context, invoke_context,
"Authorize nonce account: Account {} state is invalid", "Authorize nonce account: Account {} state is invalid",
@ -297,6 +308,7 @@ pub fn authorize_nonce_account(
mod test { mod test {
use { use {
super::*, super::*,
assert_matches::assert_matches,
solana_program_runtime::invoke_context::InvokeContext, solana_program_runtime::invoke_context::InvokeContext,
solana_sdk::{ solana_sdk::{
account::AccountSharedData, account::AccountSharedData,
@ -334,9 +346,12 @@ mod test {
let accounts = vec![ let accounts = vec![
( (
Pubkey::new_unique(), Pubkey::new_unique(),
create_account(from_lamports).into_inner(), create_account(from_lamports, /*separate_domains:*/ true).into_inner(),
),
(
Pubkey::new_unique(),
create_account(42, /*separate_domains:*/ true).into_inner(),
), ),
(Pubkey::new_unique(), create_account(42).into_inner()),
(system_program::id(), AccountSharedData::default()), (system_program::id(), AccountSharedData::default()),
]; ];
let $instruction_accounts = vec![ let $instruction_accounts = vec![
@ -390,52 +405,40 @@ mod test {
}; };
let mut signers = HashSet::new(); let mut signers = HashSet::new();
signers.insert(*nonce_account.get_key()); signers.insert(*nonce_account.get_key());
let state = nonce_account let versions = nonce_account.get_state::<Versions>().unwrap();
.get_state::<Versions>()
.unwrap()
.convert_to_current();
// New is in Uninitialzed state // New is in Uninitialzed state
assert_eq!(state, State::Uninitialized); assert_eq!(versions.state(), &State::Uninitialized);
set_invoke_context_blockhash!(invoke_context, 95); set_invoke_context_blockhash!(invoke_context, 95);
let authorized = *nonce_account.get_key(); let authorized = *nonce_account.get_key();
initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context).unwrap(); initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context).unwrap();
let state = nonce_account let versions = nonce_account.get_state::<Versions>().unwrap();
.get_state::<Versions>()
.unwrap()
.convert_to_current();
let data = nonce::state::Data::new( let data = nonce::state::Data::new(
data.authority, data.authority,
get_durable_nonce(&invoke_context), get_durable_nonce(&invoke_context).0,
invoke_context.lamports_per_signature, invoke_context.lamports_per_signature,
); );
// First nonce instruction drives state from Uninitialized to Initialized // First nonce instruction drives state from Uninitialized to Initialized
assert_eq!(state, State::Initialized(data.clone())); assert_eq!(versions.state(), &State::Initialized(data.clone()));
set_invoke_context_blockhash!(invoke_context, 63); set_invoke_context_blockhash!(invoke_context, 63);
advance_nonce_account(&mut nonce_account, &signers, &invoke_context).unwrap(); advance_nonce_account(&mut nonce_account, &signers, &invoke_context).unwrap();
let state = nonce_account let versions = nonce_account.get_state::<Versions>().unwrap();
.get_state::<Versions>()
.unwrap()
.convert_to_current();
let data = nonce::state::Data::new( let data = nonce::state::Data::new(
data.authority, data.authority,
get_durable_nonce(&invoke_context), get_durable_nonce(&invoke_context).0,
invoke_context.lamports_per_signature, invoke_context.lamports_per_signature,
); );
// Second nonce instruction consumes and replaces stored nonce // Second nonce instruction consumes and replaces stored nonce
assert_eq!(state, State::Initialized(data.clone())); assert_eq!(versions.state(), &State::Initialized(data.clone()));
set_invoke_context_blockhash!(invoke_context, 31); set_invoke_context_blockhash!(invoke_context, 31);
advance_nonce_account(&mut nonce_account, &signers, &invoke_context).unwrap(); advance_nonce_account(&mut nonce_account, &signers, &invoke_context).unwrap();
let state = nonce_account let versions = nonce_account.get_state::<Versions>().unwrap();
.get_state::<Versions>()
.unwrap()
.convert_to_current();
let data = nonce::state::Data::new( let data = nonce::state::Data::new(
data.authority, data.authority,
get_durable_nonce(&invoke_context), get_durable_nonce(&invoke_context).0,
invoke_context.lamports_per_signature, invoke_context.lamports_per_signature,
); );
// Third nonce instruction for fun and profit // Third nonce instruction for fun and profit
assert_eq!(state, State::Initialized(data)); assert_eq!(versions.state(), &State::Initialized(data));
set_invoke_context_blockhash!(invoke_context, 0); set_invoke_context_blockhash!(invoke_context, 0);
let to_account = instruction_context let to_account = instruction_context
@ -467,12 +470,9 @@ mod test {
assert_eq!(nonce_account.get_lamports(), expect_nonce_lamports); assert_eq!(nonce_account.get_lamports(), expect_nonce_lamports);
// Account balance goes to `to` // Account balance goes to `to`
assert_eq!(to_account.get_lamports(), expect_to_lamports); assert_eq!(to_account.get_lamports(), expect_to_lamports);
let state = nonce_account let versions = nonce_account.get_state::<Versions>().unwrap();
.get_state::<Versions>()
.unwrap()
.convert_to_current();
// Empty balance deinitializes data // Empty balance deinitializes data
assert_eq!(state, State::Uninitialized); assert_eq!(versions.state(), &State::Uninitialized);
} }
#[test] #[test]
@ -490,16 +490,13 @@ mod test {
set_invoke_context_blockhash!(invoke_context, 31); set_invoke_context_blockhash!(invoke_context, 31);
let authority = *nonce_account.get_key(); let authority = *nonce_account.get_key();
initialize_nonce_account(&mut nonce_account, &authority, &rent, &invoke_context).unwrap(); initialize_nonce_account(&mut nonce_account, &authority, &rent, &invoke_context).unwrap();
let state = nonce_account let versions = nonce_account.get_state::<Versions>().unwrap();
.get_state::<Versions>()
.unwrap()
.convert_to_current();
let data = nonce::state::Data::new( let data = nonce::state::Data::new(
authority, authority,
get_durable_nonce(&invoke_context), get_durable_nonce(&invoke_context).0,
invoke_context.lamports_per_signature, invoke_context.lamports_per_signature,
); );
assert_eq!(state, State::Initialized(data)); assert_eq!(versions.state(), &State::Initialized(data));
// Nonce account did not sign // Nonce account did not sign
let signers = HashSet::new(); let signers = HashSet::new();
set_invoke_context_blockhash!(invoke_context, 0); set_invoke_context_blockhash!(invoke_context, 0);
@ -613,11 +610,8 @@ mod test {
let to_account = instruction_context let to_account = instruction_context
.try_borrow_instruction_account(transaction_context, WITHDRAW_TO_ACCOUNT_INDEX) .try_borrow_instruction_account(transaction_context, WITHDRAW_TO_ACCOUNT_INDEX)
.unwrap(); .unwrap();
let state = nonce_account let versions = nonce_account.get_state::<Versions>().unwrap();
.get_state::<Versions>() assert_eq!(versions.state(), &State::Uninitialized);
.unwrap()
.convert_to_current();
assert_eq!(state, State::Uninitialized);
let mut signers = HashSet::new(); let mut signers = HashSet::new();
signers.insert(*nonce_account.get_key()); signers.insert(*nonce_account.get_key());
set_invoke_context_blockhash!(invoke_context, 0); set_invoke_context_blockhash!(invoke_context, 0);
@ -643,11 +637,8 @@ mod test {
let to_account = instruction_context let to_account = instruction_context
.try_borrow_instruction_account(transaction_context, WITHDRAW_TO_ACCOUNT_INDEX) .try_borrow_instruction_account(transaction_context, WITHDRAW_TO_ACCOUNT_INDEX)
.unwrap(); .unwrap();
let state = nonce_account let versions = nonce_account.get_state::<Versions>().unwrap();
.get_state::<Versions>() assert_eq!(versions.state(), &State::Uninitialized);
.unwrap()
.convert_to_current();
assert_eq!(state, State::Uninitialized);
assert_eq!(nonce_account.get_lamports(), expect_from_lamports); assert_eq!(nonce_account.get_lamports(), expect_from_lamports);
assert_eq!(to_account.get_lamports(), expect_to_lamports); assert_eq!(to_account.get_lamports(), expect_to_lamports);
} }
@ -667,11 +658,8 @@ mod test {
let to_account = instruction_context let to_account = instruction_context
.try_borrow_instruction_account(transaction_context, WITHDRAW_TO_ACCOUNT_INDEX) .try_borrow_instruction_account(transaction_context, WITHDRAW_TO_ACCOUNT_INDEX)
.unwrap(); .unwrap();
let state = nonce_account let versions = nonce_account.get_state::<Versions>().unwrap();
.get_state::<Versions>() assert_eq!(versions.state(), &State::Uninitialized);
.unwrap()
.convert_to_current();
assert_eq!(state, State::Uninitialized);
let signers = HashSet::new(); let signers = HashSet::new();
set_invoke_context_blockhash!(invoke_context, 0); set_invoke_context_blockhash!(invoke_context, 0);
let withdraw_lamports = nonce_account.get_lamports(); let withdraw_lamports = nonce_account.get_lamports();
@ -702,11 +690,8 @@ mod test {
let nonce_account = instruction_context let nonce_account = instruction_context
.try_borrow_instruction_account(transaction_context, NONCE_ACCOUNT_INDEX) .try_borrow_instruction_account(transaction_context, NONCE_ACCOUNT_INDEX)
.unwrap(); .unwrap();
let state = nonce_account let versions = nonce_account.get_state::<Versions>().unwrap();
.get_state::<Versions>() assert_eq!(versions.state(), &State::Uninitialized);
.unwrap()
.convert_to_current();
assert_eq!(state, State::Uninitialized);
let mut signers = HashSet::new(); let mut signers = HashSet::new();
signers.insert(*nonce_account.get_key()); signers.insert(*nonce_account.get_key());
set_invoke_context_blockhash!(invoke_context, 0); set_invoke_context_blockhash!(invoke_context, 0);
@ -765,11 +750,8 @@ mod test {
let to_account = instruction_context let to_account = instruction_context
.try_borrow_instruction_account(transaction_context, WITHDRAW_TO_ACCOUNT_INDEX) .try_borrow_instruction_account(transaction_context, WITHDRAW_TO_ACCOUNT_INDEX)
.unwrap(); .unwrap();
let state = nonce_account let versions = nonce_account.get_state::<Versions>().unwrap();
.get_state::<Versions>() assert_eq!(versions.state(), &State::Uninitialized);
.unwrap()
.convert_to_current();
assert_eq!(state, State::Uninitialized);
assert_eq!(nonce_account.get_lamports(), from_expect_lamports); assert_eq!(nonce_account.get_lamports(), from_expect_lamports);
assert_eq!(to_account.get_lamports(), to_expect_lamports); assert_eq!(to_account.get_lamports(), to_expect_lamports);
let withdraw_lamports = nonce_account.get_lamports(); let withdraw_lamports = nonce_account.get_lamports();
@ -794,11 +776,8 @@ mod test {
let to_account = instruction_context let to_account = instruction_context
.try_borrow_instruction_account(transaction_context, WITHDRAW_TO_ACCOUNT_INDEX) .try_borrow_instruction_account(transaction_context, WITHDRAW_TO_ACCOUNT_INDEX)
.unwrap(); .unwrap();
let state = nonce_account let versions = nonce_account.get_state::<Versions>().unwrap();
.get_state::<Versions>() assert_eq!(versions.state(), &State::Uninitialized);
.unwrap()
.convert_to_current();
assert_eq!(state, State::Uninitialized);
assert_eq!(nonce_account.get_lamports(), from_expect_lamports); assert_eq!(nonce_account.get_lamports(), from_expect_lamports);
assert_eq!(to_account.get_lamports(), to_expect_lamports); assert_eq!(to_account.get_lamports(), to_expect_lamports);
} }
@ -823,16 +802,13 @@ mod test {
set_invoke_context_blockhash!(invoke_context, 31); set_invoke_context_blockhash!(invoke_context, 31);
let authority = *nonce_account.get_key(); let authority = *nonce_account.get_key();
initialize_nonce_account(&mut nonce_account, &authority, &rent, &invoke_context).unwrap(); initialize_nonce_account(&mut nonce_account, &authority, &rent, &invoke_context).unwrap();
let state = nonce_account let versions = nonce_account.get_state::<Versions>().unwrap();
.get_state::<Versions>()
.unwrap()
.convert_to_current();
let data = nonce::state::Data::new( let data = nonce::state::Data::new(
authority, authority,
get_durable_nonce(&invoke_context), get_durable_nonce(&invoke_context).0,
invoke_context.lamports_per_signature, invoke_context.lamports_per_signature,
); );
assert_eq!(state, State::Initialized(data.clone())); assert_eq!(versions.state(), &State::Initialized(data.clone()));
let withdraw_lamports = 42; let withdraw_lamports = 42;
let from_expect_lamports = nonce_account.get_lamports() - withdraw_lamports; let from_expect_lamports = nonce_account.get_lamports() - withdraw_lamports;
let to_expect_lamports = to_account.get_lamports() + withdraw_lamports; let to_expect_lamports = to_account.get_lamports() + withdraw_lamports;
@ -855,16 +831,13 @@ mod test {
let to_account = instruction_context let to_account = instruction_context
.try_borrow_instruction_account(transaction_context, WITHDRAW_TO_ACCOUNT_INDEX) .try_borrow_instruction_account(transaction_context, WITHDRAW_TO_ACCOUNT_INDEX)
.unwrap(); .unwrap();
let state = nonce_account let versions = nonce_account.get_state::<Versions>().unwrap();
.get_state::<Versions>()
.unwrap()
.convert_to_current();
let data = nonce::state::Data::new( let data = nonce::state::Data::new(
data.authority, data.authority,
get_durable_nonce(&invoke_context), get_durable_nonce(&invoke_context).0,
invoke_context.lamports_per_signature, invoke_context.lamports_per_signature,
); );
assert_eq!(state, State::Initialized(data)); assert_eq!(versions.state(), &State::Initialized(data));
assert_eq!(nonce_account.get_lamports(), from_expect_lamports); assert_eq!(nonce_account.get_lamports(), from_expect_lamports);
assert_eq!(to_account.get_lamports(), to_expect_lamports); assert_eq!(to_account.get_lamports(), to_expect_lamports);
set_invoke_context_blockhash!(invoke_context, 0); set_invoke_context_blockhash!(invoke_context, 0);
@ -890,11 +863,8 @@ mod test {
let to_account = instruction_context let to_account = instruction_context
.try_borrow_instruction_account(transaction_context, WITHDRAW_TO_ACCOUNT_INDEX) .try_borrow_instruction_account(transaction_context, WITHDRAW_TO_ACCOUNT_INDEX)
.unwrap(); .unwrap();
let state = nonce_account let versions = nonce_account.get_state::<Versions>().unwrap();
.get_state::<Versions>() assert_eq!(versions.state(), &State::Uninitialized);
.unwrap()
.convert_to_current();
assert_eq!(state, State::Uninitialized);
assert_eq!(nonce_account.get_lamports(), from_expect_lamports); assert_eq!(nonce_account.get_lamports(), from_expect_lamports);
assert_eq!(to_account.get_lamports(), to_expect_lamports); assert_eq!(to_account.get_lamports(), to_expect_lamports);
} }
@ -1046,11 +1016,8 @@ mod test {
let mut nonce_account = instruction_context let mut nonce_account = instruction_context
.try_borrow_instruction_account(transaction_context, NONCE_ACCOUNT_INDEX) .try_borrow_instruction_account(transaction_context, NONCE_ACCOUNT_INDEX)
.unwrap(); .unwrap();
let state = nonce_account let versions = nonce_account.get_state::<Versions>().unwrap();
.get_state::<Versions>() assert_eq!(versions.state(), &State::Uninitialized);
.unwrap()
.convert_to_current();
assert_eq!(state, State::Uninitialized);
let mut signers = HashSet::new(); let mut signers = HashSet::new();
signers.insert(*nonce_account.get_key()); signers.insert(*nonce_account.get_key());
set_invoke_context_blockhash!(invoke_context, 0); set_invoke_context_blockhash!(invoke_context, 0);
@ -1059,15 +1026,12 @@ mod test {
initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context); initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context);
let data = nonce::state::Data::new( let data = nonce::state::Data::new(
authorized, authorized,
get_durable_nonce(&invoke_context), get_durable_nonce(&invoke_context).0,
invoke_context.lamports_per_signature, invoke_context.lamports_per_signature,
); );
assert_eq!(result, Ok(())); assert_eq!(result, Ok(()));
let state = nonce_account let versions = nonce_account.get_state::<Versions>().unwrap();
.get_state::<Versions>() assert_eq!(versions.state(), &State::Initialized(data));
.unwrap()
.convert_to_current();
assert_eq!(state, State::Initialized(data));
} }
#[test] #[test]
@ -1131,15 +1095,12 @@ mod test {
let authority = Pubkey::default(); let authority = Pubkey::default();
let data = nonce::state::Data::new( let data = nonce::state::Data::new(
authority, authority,
get_durable_nonce(&invoke_context), get_durable_nonce(&invoke_context).0,
invoke_context.lamports_per_signature, invoke_context.lamports_per_signature,
); );
authorize_nonce_account(&mut nonce_account, &authority, &signers, &invoke_context).unwrap(); authorize_nonce_account(&mut nonce_account, &authority, &signers, &invoke_context).unwrap();
let state = nonce_account let versions = nonce_account.get_state::<Versions>().unwrap();
.get_state::<Versions>() assert_eq!(versions.state(), &State::Initialized(data));
.unwrap()
.convert_to_current();
assert_eq!(state, State::Initialized(data));
} }
#[test] #[test]
@ -1201,21 +1162,24 @@ mod test {
.unwrap(); .unwrap();
let mut signers = HashSet::new(); let mut signers = HashSet::new();
signers.insert(nonce_account.get_key()); signers.insert(nonce_account.get_key());
let state: State = nonce_account.get_state().unwrap(); let versions: Versions = nonce_account.get_state().unwrap();
// New is in Uninitialzed state // New is in Uninitialzed state
assert_eq!(state, State::Uninitialized); assert_eq!(versions.state(), &State::Uninitialized);
set_invoke_context_blockhash!(invoke_context, 0); set_invoke_context_blockhash!(invoke_context, 0);
let authorized = *nonce_account.get_key(); let authorized = *nonce_account.get_key();
initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context).unwrap(); initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context).unwrap();
drop(nonce_account); drop(nonce_account);
assert!(verify_nonce_account( assert_matches!(
&transaction_context verify_nonce_account(
.get_account_at_index(NONCE_ACCOUNT_INDEX) &transaction_context
.unwrap() .get_account_at_index(NONCE_ACCOUNT_INDEX)
.borrow(), .unwrap()
get_durable_nonce(&invoke_context).as_hash(), .borrow(),
) get_durable_nonce(&invoke_context).0.as_hash(),
.is_some()); true, // separate_domins
),
Some(_)
);
} }
#[test] #[test]
@ -1227,14 +1191,17 @@ mod test {
_instruction_context, _instruction_context,
instruction_accounts instruction_accounts
); );
assert!(verify_nonce_account( assert_eq!(
&transaction_context verify_nonce_account(
.get_account_at_index(NONCE_ACCOUNT_INDEX) &transaction_context
.unwrap() .get_account_at_index(NONCE_ACCOUNT_INDEX)
.borrow(), .unwrap()
&Hash::default() .borrow(),
) &Hash::default(),
.is_none()); true, // separate_domins
),
None
);
} }
#[test] #[test]
@ -1251,9 +1218,9 @@ mod test {
.unwrap(); .unwrap();
let mut signers = HashSet::new(); let mut signers = HashSet::new();
signers.insert(nonce_account.get_key()); signers.insert(nonce_account.get_key());
let state: State = nonce_account.get_state().unwrap(); let versions: Versions = nonce_account.get_state().unwrap();
// New is in Uninitialzed state // New is in Uninitialzed state
assert_eq!(state, State::Uninitialized); assert_eq!(versions.state(), &State::Uninitialized);
set_invoke_context_blockhash!(invoke_context, 0); set_invoke_context_blockhash!(invoke_context, 0);
let authorized = *nonce_account.get_key(); let authorized = *nonce_account.get_key();
initialize_nonce_account( initialize_nonce_account(
@ -1265,13 +1232,16 @@ mod test {
.unwrap(); .unwrap();
set_invoke_context_blockhash!(invoke_context, 1); set_invoke_context_blockhash!(invoke_context, 1);
drop(nonce_account); drop(nonce_account);
assert!(verify_nonce_account( assert_eq!(
&transaction_context verify_nonce_account(
.get_account_at_index(NONCE_ACCOUNT_INDEX) &transaction_context
.unwrap() .get_account_at_index(NONCE_ACCOUNT_INDEX)
.borrow(), .unwrap()
get_durable_nonce(&invoke_context).as_hash(), .borrow(),
) get_durable_nonce(&invoke_context).0.as_hash(),
.is_none()); true, // separate_domins
),
None
);
} }
} }

View File

@ -549,11 +549,10 @@ pub fn get_system_account_kind(account: &AccountSharedData) -> Option<SystemAcco
if account.data().is_empty() { if account.data().is_empty() {
Some(SystemAccountKind::System) Some(SystemAccountKind::System)
} else if account.data().len() == nonce::State::size() { } else if account.data().len() == nonce::State::size() {
match account.state().ok()? { let nonce_versions: nonce::state::Versions = account.state().ok()?;
nonce::state::Versions::Current(state) => match *state { match nonce_versions.state() {
nonce::State::Initialized(_) => Some(SystemAccountKind::Nonce), nonce::State::Uninitialized => None,
_ => None, nonce::State::Initialized(_) => Some(SystemAccountKind::Nonce),
},
} }
} else { } else {
None None
@ -1191,9 +1190,10 @@ mod tests {
let nonce = Pubkey::new_unique(); let nonce = Pubkey::new_unique();
let nonce_account = AccountSharedData::new_data( let nonce_account = AccountSharedData::new_data(
42, 42,
&nonce::state::Versions::new_current(nonce::State::Initialized( &nonce::state::Versions::new(
nonce::state::Data::default(), nonce::State::Initialized(nonce::state::Data::default()),
)), true, // separate_domains
),
&system_program::id(), &system_program::id(),
) )
.unwrap(); .unwrap();
@ -1482,10 +1482,13 @@ mod tests {
let from = Pubkey::new_unique(); let from = Pubkey::new_unique();
let from_account = AccountSharedData::new_data( let from_account = AccountSharedData::new_data(
100, 100,
&nonce::state::Versions::new_current(nonce::State::Initialized(nonce::state::Data { &nonce::state::Versions::new(
authority: from, nonce::State::Initialized(nonce::state::Data {
..nonce::state::Data::default() authority: from,
})), ..nonce::state::Data::default()
}),
true, // separate_domains
),
&system_program::id(), &system_program::id(),
) )
.unwrap(); .unwrap();
@ -1764,7 +1767,8 @@ mod tests {
#[test] #[test]
fn test_process_nonce_ix_ok() { fn test_process_nonce_ix_ok() {
let nonce_address = Pubkey::new_unique(); let nonce_address = Pubkey::new_unique();
let nonce_account = nonce_account::create_account(1_000_000).into_inner(); let nonce_account =
nonce_account::create_account(1_000_000, /*separate_domains:*/ true).into_inner();
#[allow(deprecated)] #[allow(deprecated)]
let blockhash_id = sysvar::recent_blockhashes::id(); let blockhash_id = sysvar::recent_blockhashes::id();
let accounts = process_instruction( let accounts = process_instruction(
@ -1871,7 +1875,8 @@ mod tests {
#[test] #[test]
fn test_process_withdraw_ix_ok() { fn test_process_withdraw_ix_ok() {
let nonce_address = Pubkey::new_unique(); let nonce_address = Pubkey::new_unique();
let nonce_account = nonce_account::create_account(1_000_000).into_inner(); let nonce_account =
nonce_account::create_account(1_000_000, /*separate_domains:*/ true).into_inner();
let pubkey = Pubkey::new_unique(); let pubkey = Pubkey::new_unique();
#[allow(deprecated)] #[allow(deprecated)]
let blockhash_id = sysvar::recent_blockhashes::id(); let blockhash_id = sysvar::recent_blockhashes::id();
@ -1924,7 +1929,8 @@ mod tests {
#[test] #[test]
fn test_process_initialize_ix_only_nonce_acc_fail() { fn test_process_initialize_ix_only_nonce_acc_fail() {
let nonce_address = Pubkey::new_unique(); let nonce_address = Pubkey::new_unique();
let nonce_account = nonce_account::create_account(1_000_000).into_inner(); let nonce_account =
nonce_account::create_account(1_000_000, /*separate_domains:*/ true).into_inner();
process_instruction( process_instruction(
&serialize(&SystemInstruction::InitializeNonceAccount(nonce_address)).unwrap(), &serialize(&SystemInstruction::InitializeNonceAccount(nonce_address)).unwrap(),
vec![(nonce_address, nonce_account)], vec![(nonce_address, nonce_account)],
@ -1941,7 +1947,8 @@ mod tests {
#[test] #[test]
fn test_process_initialize_ix_ok() { fn test_process_initialize_ix_ok() {
let nonce_address = Pubkey::new_unique(); let nonce_address = Pubkey::new_unique();
let nonce_account = nonce_account::create_account(1_000_000).into_inner(); let nonce_account =
nonce_account::create_account(1_000_000, /*separate_domains:*/ true).into_inner();
#[allow(deprecated)] #[allow(deprecated)]
let blockhash_id = sysvar::recent_blockhashes::id(); let blockhash_id = sysvar::recent_blockhashes::id();
process_instruction( process_instruction(
@ -1976,7 +1983,8 @@ mod tests {
#[test] #[test]
fn test_process_authorize_ix_ok() { fn test_process_authorize_ix_ok() {
let nonce_address = Pubkey::new_unique(); let nonce_address = Pubkey::new_unique();
let nonce_account = nonce_account::create_account(1_000_000).into_inner(); let nonce_account =
nonce_account::create_account(1_000_000, /*separate_domains:*/ true).into_inner();
#[allow(deprecated)] #[allow(deprecated)]
let blockhash_id = sysvar::recent_blockhashes::id(); let blockhash_id = sysvar::recent_blockhashes::id();
let accounts = process_instruction( let accounts = process_instruction(
@ -2045,9 +2053,10 @@ mod tests {
fn test_get_system_account_kind_nonce_ok() { fn test_get_system_account_kind_nonce_ok() {
let nonce_account = AccountSharedData::new_data( let nonce_account = AccountSharedData::new_data(
42, 42,
&nonce::state::Versions::new_current(nonce::State::Initialized( &nonce::state::Versions::new(
nonce::state::Data::default(), nonce::State::Initialized(nonce::state::Data::default()),
)), true, // separate_domains
),
&system_program::id(), &system_program::id(),
) )
.unwrap(); .unwrap();
@ -2060,7 +2069,9 @@ mod tests {
#[test] #[test]
fn test_get_system_account_kind_uninitialized_nonce_account_fail() { fn test_get_system_account_kind_uninitialized_nonce_account_fail() {
assert_eq!( assert_eq!(
get_system_account_kind(&nonce_account::create_account(42).borrow()), get_system_account_kind(
&nonce_account::create_account(42, /*separate_domains:*/ true).borrow()
),
None None
); );
} }
@ -2076,9 +2087,10 @@ mod tests {
fn test_get_system_account_kind_nonsystem_owner_with_nonce_data_fail() { fn test_get_system_account_kind_nonsystem_owner_with_nonce_data_fail() {
let nonce_account = AccountSharedData::new_data( let nonce_account = AccountSharedData::new_data(
42, 42,
&nonce::state::Versions::new_current(nonce::State::Initialized( &nonce::state::Versions::new(
nonce::state::Data::default(), nonce::State::Initialized(nonce::state::Data::default()),
)), true, // separate_domains
),
&Pubkey::new_unique(), &Pubkey::new_unique(),
) )
.unwrap(); .unwrap();
@ -2088,7 +2100,8 @@ mod tests {
#[test] #[test]
fn test_nonce_initialize_with_empty_recent_blockhashes_fail() { fn test_nonce_initialize_with_empty_recent_blockhashes_fail() {
let nonce_address = Pubkey::new_unique(); let nonce_address = Pubkey::new_unique();
let nonce_account = nonce_account::create_account(1_000_000).into_inner(); let nonce_account =
nonce_account::create_account(1_000_000, /*separate_domains:*/ true).into_inner();
#[allow(deprecated)] #[allow(deprecated)]
let blockhash_id = sysvar::recent_blockhashes::id(); let blockhash_id = sysvar::recent_blockhashes::id();
#[allow(deprecated)] #[allow(deprecated)]
@ -2128,7 +2141,8 @@ mod tests {
#[test] #[test]
fn test_nonce_advance_with_empty_recent_blockhashes_fail() { fn test_nonce_advance_with_empty_recent_blockhashes_fail() {
let nonce_address = Pubkey::new_unique(); let nonce_address = Pubkey::new_unique();
let nonce_account = nonce_account::create_account(1_000_000).into_inner(); let nonce_account =
nonce_account::create_account(1_000_000, /*separate_domains:*/ true).into_inner();
#[allow(deprecated)] #[allow(deprecated)]
let blockhash_id = sysvar::recent_blockhashes::id(); let blockhash_id = sysvar::recent_blockhashes::id();
let accounts = process_instruction( let accounts = process_instruction(

View File

@ -110,7 +110,10 @@ mod test {
#[test] #[test]
fn test_nonce_state_size() { fn test_nonce_state_size() {
let data = Versions::new_current(State::Initialized(Data::default())); let data = Versions::new(
State::Initialized(Data::default()),
true, // separate_domains
);
let size = bincode::serialized_size(&data).unwrap(); let size = bincode::serialized_size(&data).unwrap();
assert_eq!(State::size() as u64, size); assert_eq!(State::size() as u64, size);
} }

View File

@ -2,21 +2,178 @@
mod current; mod current;
pub use current::{Data, DurableNonce, State}; pub use current::{Data, DurableNonce, State};
use serde_derive::{Deserialize, Serialize}; use {
crate::hash::Hash,
serde_derive::{Deserialize, Serialize},
};
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
pub enum Versions { pub enum Versions {
Legacy(Box<State>),
/// Current variants have durable nonce and blockhash domains separated.
Current(Box<State>), Current(Box<State>),
} }
impl Versions { impl Versions {
pub fn new_current(state: State) -> Self { pub fn new(state: State, separate_domains: bool) -> Self {
Self::Current(Box::new(state)) if separate_domains {
Self::Current(Box::new(state))
} else {
Self::Legacy(Box::new(state))
}
} }
pub fn convert_to_current(self) -> State { pub fn state(&self) -> &State {
match self { match self {
Self::Current(state) => *state, Self::Legacy(state) => state,
Self::Current(state) => state,
}
}
/// Returns true if the durable nonce is not in the blockhash domain.
pub fn separate_domains(&self) -> bool {
match self {
Self::Legacy(_) => false,
Self::Current(_) => true,
}
}
/// Checks if the recent_blockhash field in Transaction verifies, and
/// returns nonce account data if so.
pub fn verify_recent_blockhash(
&self,
recent_blockhash: &Hash, // Transaction.message.recent_blockhash
separate_domains: bool,
) -> Option<&Data> {
let state = match self {
Self::Legacy(state) => {
if separate_domains {
// Legacy durable nonces are invalid and should not
// allow durable transactions.
return None;
} else {
state
}
}
Self::Current(state) => state,
};
match **state {
State::Uninitialized => None,
State::Initialized(ref data) => (recent_blockhash == &data.blockhash()).then(|| data),
}
}
}
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, pubkey::Pubkey},
};
#[test]
fn test_verify_recent_blockhash() {
let blockhash = Hash::from([171; 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, /*separate_domains:*/ 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, /*separate_domains:*/ 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)
);
} }
} }
} }

View File

@ -11,11 +11,11 @@ use {
std::cell::RefCell, std::cell::RefCell,
}; };
pub fn create_account(lamports: u64) -> RefCell<AccountSharedData> { pub fn create_account(lamports: u64, separate_domains: bool) -> RefCell<AccountSharedData> {
RefCell::new( RefCell::new(
AccountSharedData::new_data_with_space( AccountSharedData::new_data_with_space(
lamports, lamports,
&Versions::new_current(State::Uninitialized), &Versions::new(State::Uninitialized, separate_domains),
State::size(), State::size(),
&crate::system_program::id(), &crate::system_program::id(),
) )
@ -23,30 +23,41 @@ pub fn create_account(lamports: u64) -> RefCell<AccountSharedData> {
) )
} }
// TODO: Consider changing argument from Hash to DurableNonce. /// Checks if the recent_blockhash field in Transaction verifies, and returns
pub fn verify_nonce_account(acc: &AccountSharedData, hash: &Hash) -> Option<Data> { /// nonce account data if so.
if acc.owner() != &crate::system_program::id() { pub fn verify_nonce_account(
return None; account: &AccountSharedData,
} recent_blockhash: &Hash, // Transaction.message.recent_blockhash
match StateMut::<Versions>::state(acc).map(|v| v.convert_to_current()) { separate_domains: bool,
Ok(State::Initialized(data)) => (hash == &data.blockhash()).then(|| data), ) -> Option<Data> {
_ => None, (account.owner() == &crate::system_program::id())
} .then(|| {
StateMut::<Versions>::state(account)
.ok()?
.verify_recent_blockhash(recent_blockhash, separate_domains)
.cloned()
})
.flatten()
} }
pub fn lamports_per_signature_of(account: &AccountSharedData) -> Option<u64> { pub fn lamports_per_signature_of(account: &AccountSharedData) -> Option<u64> {
let state = StateMut::<Versions>::state(account) match StateMut::<Versions>::state(account).ok()?.state() {
.ok()?
.convert_to_current();
match state {
State::Initialized(data) => Some(data.fee_calculator.lamports_per_signature), State::Initialized(data) => Some(data.fee_calculator.lamports_per_signature),
_ => None, State::Uninitialized => None,
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use {super::*, crate::pubkey::Pubkey}; use {
super::*,
crate::{
fee_calculator::FeeCalculator,
nonce::state::{Data, DurableNonce},
pubkey::Pubkey,
system_program,
},
};
#[test] #[test]
fn test_verify_bad_account_owner_fails() { fn test_verify_bad_account_owner_fails() {
@ -54,11 +65,126 @@ mod tests {
assert_ne!(program_id, crate::system_program::id()); assert_ne!(program_id, crate::system_program::id());
let account = AccountSharedData::new_data_with_space( let account = AccountSharedData::new_data_with_space(
42, 42,
&Versions::new_current(State::Uninitialized), &Versions::new(State::Uninitialized, /*separate_domains:*/ true),
State::size(), State::size(),
&program_id, &program_id,
) )
.expect("nonce_account"); .expect("nonce_account");
assert!(verify_nonce_account(&account, &Hash::default()).is_none()); for separate_domains in [false, true] {
assert_eq!(
verify_nonce_account(&account, &Hash::default(), separate_domains),
None
);
}
}
fn new_nonce_account(versions: Versions) -> AccountSharedData {
AccountSharedData::new_data(
1_000_000, // lamports
&versions, // state
&system_program::id(), // owner
)
.unwrap()
}
#[test]
fn test_verify_nonce_account() {
let blockhash = Hash::from([171; 32]);
let versions = Versions::Legacy(Box::new(State::Uninitialized));
let account = new_nonce_account(versions);
for separate_domains in [false, true] {
assert_eq!(
verify_nonce_account(&account, &blockhash, separate_domains),
None
);
assert_eq!(
verify_nonce_account(&account, &Hash::default(), separate_domains),
None
);
}
let versions = Versions::Current(Box::new(State::Uninitialized));
let account = new_nonce_account(versions);
for separate_domains in [false, true] {
assert_eq!(
verify_nonce_account(&account, &blockhash, separate_domains),
None
);
assert_eq!(
verify_nonce_account(&account, &Hash::default(), separate_domains),
None
);
}
let durable_nonce =
DurableNonce::from_blockhash(&blockhash, /*separate_domains:*/ 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 account = new_nonce_account(versions);
let separate_domains = false;
assert_eq!(
verify_nonce_account(&account, &blockhash, separate_domains),
Some(data.clone())
);
assert_eq!(
verify_nonce_account(&account, &Hash::default(), separate_domains),
None
);
assert_eq!(
verify_nonce_account(&account, &data.blockhash(), separate_domains),
Some(data.clone())
);
assert_eq!(
verify_nonce_account(&account, durable_nonce.as_hash(), separate_domains),
Some(data.clone())
);
let separate_domains = true;
assert_eq!(
verify_nonce_account(&account, &blockhash, separate_domains),
None
);
assert_eq!(
verify_nonce_account(&account, &Hash::default(), separate_domains),
None
);
assert_eq!(
verify_nonce_account(&account, &data.blockhash(), separate_domains),
None
);
assert_eq!(
verify_nonce_account(&account, durable_nonce.as_hash(), separate_domains),
None
);
let durable_nonce =
DurableNonce::from_blockhash(&blockhash, /*separate_domains:*/ true);
assert_ne!(data.durable_nonce, durable_nonce);
let data = Data {
durable_nonce,
..data
};
let versions = Versions::Current(Box::new(State::Initialized(data.clone())));
let account = new_nonce_account(versions);
for separate_domains in [false, true] {
assert_eq!(
verify_nonce_account(&account, &blockhash, separate_domains),
None
);
assert_eq!(
verify_nonce_account(&account, &Hash::default(), separate_domains),
None
);
assert_eq!(
verify_nonce_account(&account, &data.blockhash(), separate_domains),
Some(data.clone())
);
assert_eq!(
verify_nonce_account(&account, durable_nonce.as_hash(), separate_domains),
Some(data.clone())
);
}
} }
} }

View File

@ -605,10 +605,12 @@ impl SendTransactionService {
.last_sent_time .last_sent_time
.map(|last| now.duration_since(last) >= retry_rate) .map(|last| now.duration_since(last) >= retry_rate)
.unwrap_or(false); .unwrap_or(false);
if nonce_account::verify_nonce_account(&nonce_account, &durable_nonce).is_none() let verify_nonce_account = nonce_account::verify_nonce_account(
&& signature_status.is_none() &nonce_account,
&& expired &durable_nonce,
{ working_bank.separate_nonce_from_blockhash(),
);
if verify_nonce_account.is_none() && signature_status.is_none() && expired {
info!("Dropping expired durable-nonce transaction: {}", signature); info!("Dropping expired durable-nonce transaction: {}", signature);
result.expired += 1; result.expired += 1;
stats.expired_transactions.fetch_add(1, Ordering::Relaxed); stats.expired_transactions.fetch_add(1, Ordering::Relaxed);
@ -1094,9 +1096,14 @@ mod test {
let nonce_address = Pubkey::new_unique(); let nonce_address = Pubkey::new_unique();
let durable_nonce = let durable_nonce =
DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true); DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true);
let nonce_state = nonce::state::Versions::new_current(nonce::State::Initialized( let nonce_state = nonce::state::Versions::new(
nonce::state::Data::new(Pubkey::default(), durable_nonce, 42), nonce::State::Initialized(nonce::state::Data::new(
)); Pubkey::default(),
durable_nonce,
42,
)),
true, // separate_domains
);
let nonce_account = let nonce_account =
AccountSharedData::new_data(43, &nonce_state, &system_program::id()).unwrap(); AccountSharedData::new_data(43, &nonce_state, &system_program::id()).unwrap();
root_bank.store_account(&nonce_address, &nonce_account); root_bank.store_account(&nonce_address, &nonce_account);
@ -1346,9 +1353,14 @@ mod test {
} }
let new_durable_nonce = let new_durable_nonce =
DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true); DurableNonce::from_blockhash(&Hash::new_unique(), /*separate_domains:*/ true);
let new_nonce_state = nonce::state::Versions::new_current(nonce::State::Initialized( let new_nonce_state = nonce::state::Versions::new(
nonce::state::Data::new(Pubkey::default(), new_durable_nonce, 42), nonce::State::Initialized(nonce::state::Data::new(
)); Pubkey::default(),
new_durable_nonce,
42,
)),
true, // separate_domains
);
let nonce_account = let nonce_account =
AccountSharedData::new_data(43, &new_nonce_state, &system_program::id()).unwrap(); AccountSharedData::new_data(43, &new_nonce_state, &system_program::id()).unwrap();
working_bank.store_account(&nonce_address, &nonce_account); working_bank.store_account(&nonce_address, &nonce_account);