Reject durable nonce txs that don't use an advanceable nonce (#25832)

* Reject durable nonce txs that use the latest durable nonce hash

* feedback
This commit is contained in:
Justin Starry 2022-06-08 16:34:57 -05:00 committed by GitHub
parent 3f47c83e5c
commit 6a7edc02c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 75 additions and 5 deletions

View File

@ -4036,15 +4036,21 @@ impl Bank {
.feature_set
.is_active(&feature_set::enable_durable_nonce::id());
let hash_queue = self.blockhash_queue.read().unwrap();
let last_blockhash = hash_queue.last_hash();
let next_durable_nonce =
DurableNonce::from_blockhash(&last_blockhash, separate_nonce_from_blockhash);
txs.zip(lock_results)
.map(|(tx, lock_res)| match lock_res {
Ok(()) => {
let recent_blockhash = tx.message().recent_blockhash();
if hash_queue.is_hash_valid_for_age(recent_blockhash, max_age) {
(Ok(()), None)
} else if let Some((address, account)) =
self.check_transaction_for_nonce(tx, enable_durable_nonce)
{
} else if let Some((address, account)) = self.check_transaction_for_nonce(
tx,
enable_durable_nonce,
&next_durable_nonce,
) {
(Ok(()), Some(NoncePartial::new(address, account)))
} else {
error_counters.blockhash_not_found += 1;
@ -4128,10 +4134,16 @@ impl Bank {
&self,
tx: &SanitizedTransaction,
enable_durable_nonce: bool,
next_durable_nonce: &DurableNonce,
) -> Option<TransactionAccount> {
(enable_durable_nonce
let durable_nonces_enabled = enable_durable_nonce
|| self.slot() <= 135986379
|| self.cluster_type() != ClusterType::MainnetBeta)
|| self.cluster_type() != ClusterType::MainnetBeta;
let nonce_must_be_advanceable = self
.feature_set
.is_active(&feature_set::nonce_must_be_advanceable::ID);
let nonce_is_advanceable = tx.message().recent_blockhash() != next_durable_nonce.as_hash();
(durable_nonces_enabled && (nonce_is_advanceable || !nonce_must_be_advanceable))
.then(|| self.check_message_for_nonce(tx.message()))
.flatten()
}
@ -12501,9 +12513,26 @@ pub(crate) mod tests {
nonce_lamports,
nonce_authority,
)?;
// The setup nonce is not valid to be used until the next bank
// so wait one more block
goto_end_of_slot(Arc::get_mut(&mut bank).unwrap());
bank = Arc::new(new_from_parent(&bank));
Ok((bank, mint_keypair, custodian_keypair, nonce_keypair))
}
impl Bank {
fn next_durable_nonce(&self) -> DurableNonce {
let separate_nonce_from_blockhash = self
.feature_set
.is_active(&feature_set::separate_nonce_from_blockhash::id());
let hash_queue = self.blockhash_queue.read().unwrap();
let last_blockhash = hash_queue.last_hash();
DurableNonce::from_blockhash(&last_blockhash, separate_nonce_from_blockhash)
}
}
#[test]
fn test_check_transaction_for_nonce_ok() {
let mut feature_set = FeatureSet::all_enabled();
@ -12529,6 +12558,7 @@ pub(crate) mod tests {
bank.check_transaction_for_nonce(
&SanitizedTransaction::from_transaction_for_tests(tx),
true, // enable_durable_nonce
&bank.next_durable_nonce(),
),
Some((nonce_pubkey, nonce_account))
);
@ -12558,6 +12588,7 @@ pub(crate) mod tests {
.check_transaction_for_nonce(
&SanitizedTransaction::from_transaction_for_tests(tx,),
true, // enable_durable_nonce
&bank.next_durable_nonce(),
)
.is_none());
}
@ -12587,6 +12618,7 @@ pub(crate) mod tests {
.check_transaction_for_nonce(
&SanitizedTransaction::from_transaction_for_tests(tx),
true, // enable_durable_nonce
&bank.next_durable_nonce(),
)
.is_none());
}
@ -12617,6 +12649,7 @@ pub(crate) mod tests {
.check_transaction_for_nonce(
&SanitizedTransaction::from_transaction_for_tests(tx),
true, // enable_durable_nonce
&bank.next_durable_nonce(),
)
.is_none());
}
@ -12644,6 +12677,7 @@ pub(crate) mod tests {
.check_transaction_for_nonce(
&SanitizedTransaction::from_transaction_for_tests(tx),
true, // enable_durable_nonce
&bank.next_durable_nonce(),
)
.is_none());
}
@ -12675,6 +12709,36 @@ pub(crate) mod tests {
assert_eq!(bank.process_transaction(&tx), expect);
}
#[test]
fn test_nonce_must_be_advanceable() {
let (genesis_config, _mint_keypair) = create_genesis_config(100_000_000);
let mut bank = Bank::new_for_tests(&genesis_config);
bank.feature_set = Arc::new(FeatureSet::all_enabled());
let bank = Arc::new(bank);
let nonce_keypair = Keypair::new();
let nonce_authority = nonce_keypair.pubkey();
let durable_nonce =
DurableNonce::from_blockhash(&bank.last_blockhash(), true /* separate domains */);
let nonce_account = AccountSharedData::new_data(
42_424_242,
&nonce::state::Versions::new_current(nonce::State::Initialized(
nonce::state::Data::new(nonce_authority, durable_nonce, 5000),
)),
&system_program::id(),
)
.unwrap();
bank.store_account(&nonce_keypair.pubkey(), &nonce_account);
let ix =
system_instruction::advance_nonce_account(&nonce_keypair.pubkey(), &nonce_authority);
let message = Message::new(&[ix], Some(&nonce_keypair.pubkey()));
let tx = Transaction::new(&[&nonce_keypair], message, *durable_nonce.as_hash());
assert_eq!(
bank.process_transaction(&tx),
Err(TransactionError::BlockhashNotFound)
);
}
#[test]
fn test_nonce_transaction() {
let mut feature_set = FeatureSet::all_enabled();
@ -13313,6 +13377,7 @@ pub(crate) mod tests {
bank.check_transaction_for_nonce(
&SanitizedTransaction::from_transaction_for_tests(tx),
true, // enable_durable_nonce
&bank.next_durable_nonce(),
),
None
);

View File

@ -436,6 +436,10 @@ pub mod nonce_must_be_authorized {
solana_sdk::declare_id!("HxrEu1gXuH7iD3Puua1ohd5n4iUKJyFNtNxk9DVJkvgr");
}
pub mod nonce_must_be_advanceable {
solana_sdk::declare_id!("3u3Er5Vc2jVcwz4xr2GJeSAXT3fAj6ADHZ4BJMZiScFd");
}
lazy_static! {
/// Map of feature identifiers to user-visible description
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
@ -538,6 +542,7 @@ lazy_static! {
(vote_state_update_credit_per_dequeue::id(), "Calculate vote credits for VoteStateUpdate per vote dequeue to match credit awards for Vote instruction"),
(quick_bail_on_panic::id(), "quick bail on panic"),
(nonce_must_be_authorized::id(), "nonce must be authorized"),
(nonce_must_be_advanceable::id(), "durable nonces must be advanceable"),
/*************** ADD NEW FEATURES HERE ***************/
]
.iter()