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:
parent
3f47c83e5c
commit
6a7edc02c6
|
@ -4036,15 +4036,21 @@ impl Bank {
|
||||||
.feature_set
|
.feature_set
|
||||||
.is_active(&feature_set::enable_durable_nonce::id());
|
.is_active(&feature_set::enable_durable_nonce::id());
|
||||||
let hash_queue = self.blockhash_queue.read().unwrap();
|
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)
|
txs.zip(lock_results)
|
||||||
.map(|(tx, lock_res)| match lock_res {
|
.map(|(tx, lock_res)| match lock_res {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
let recent_blockhash = tx.message().recent_blockhash();
|
let recent_blockhash = tx.message().recent_blockhash();
|
||||||
if hash_queue.is_hash_valid_for_age(recent_blockhash, max_age) {
|
if hash_queue.is_hash_valid_for_age(recent_blockhash, max_age) {
|
||||||
(Ok(()), None)
|
(Ok(()), None)
|
||||||
} else if let Some((address, account)) =
|
} else if let Some((address, account)) = self.check_transaction_for_nonce(
|
||||||
self.check_transaction_for_nonce(tx, enable_durable_nonce)
|
tx,
|
||||||
{
|
enable_durable_nonce,
|
||||||
|
&next_durable_nonce,
|
||||||
|
) {
|
||||||
(Ok(()), Some(NoncePartial::new(address, account)))
|
(Ok(()), Some(NoncePartial::new(address, account)))
|
||||||
} else {
|
} else {
|
||||||
error_counters.blockhash_not_found += 1;
|
error_counters.blockhash_not_found += 1;
|
||||||
|
@ -4128,10 +4134,16 @@ impl Bank {
|
||||||
&self,
|
&self,
|
||||||
tx: &SanitizedTransaction,
|
tx: &SanitizedTransaction,
|
||||||
enable_durable_nonce: bool,
|
enable_durable_nonce: bool,
|
||||||
|
next_durable_nonce: &DurableNonce,
|
||||||
) -> Option<TransactionAccount> {
|
) -> Option<TransactionAccount> {
|
||||||
(enable_durable_nonce
|
let durable_nonces_enabled = enable_durable_nonce
|
||||||
|| self.slot() <= 135986379
|
|| 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()))
|
.then(|| self.check_message_for_nonce(tx.message()))
|
||||||
.flatten()
|
.flatten()
|
||||||
}
|
}
|
||||||
|
@ -12501,9 +12513,26 @@ pub(crate) mod tests {
|
||||||
nonce_lamports,
|
nonce_lamports,
|
||||||
nonce_authority,
|
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))
|
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]
|
#[test]
|
||||||
fn test_check_transaction_for_nonce_ok() {
|
fn test_check_transaction_for_nonce_ok() {
|
||||||
let mut feature_set = FeatureSet::all_enabled();
|
let mut feature_set = FeatureSet::all_enabled();
|
||||||
|
@ -12529,6 +12558,7 @@ pub(crate) mod tests {
|
||||||
bank.check_transaction_for_nonce(
|
bank.check_transaction_for_nonce(
|
||||||
&SanitizedTransaction::from_transaction_for_tests(tx),
|
&SanitizedTransaction::from_transaction_for_tests(tx),
|
||||||
true, // enable_durable_nonce
|
true, // enable_durable_nonce
|
||||||
|
&bank.next_durable_nonce(),
|
||||||
),
|
),
|
||||||
Some((nonce_pubkey, nonce_account))
|
Some((nonce_pubkey, nonce_account))
|
||||||
);
|
);
|
||||||
|
@ -12558,6 +12588,7 @@ pub(crate) mod tests {
|
||||||
.check_transaction_for_nonce(
|
.check_transaction_for_nonce(
|
||||||
&SanitizedTransaction::from_transaction_for_tests(tx,),
|
&SanitizedTransaction::from_transaction_for_tests(tx,),
|
||||||
true, // enable_durable_nonce
|
true, // enable_durable_nonce
|
||||||
|
&bank.next_durable_nonce(),
|
||||||
)
|
)
|
||||||
.is_none());
|
.is_none());
|
||||||
}
|
}
|
||||||
|
@ -12587,6 +12618,7 @@ pub(crate) mod tests {
|
||||||
.check_transaction_for_nonce(
|
.check_transaction_for_nonce(
|
||||||
&SanitizedTransaction::from_transaction_for_tests(tx),
|
&SanitizedTransaction::from_transaction_for_tests(tx),
|
||||||
true, // enable_durable_nonce
|
true, // enable_durable_nonce
|
||||||
|
&bank.next_durable_nonce(),
|
||||||
)
|
)
|
||||||
.is_none());
|
.is_none());
|
||||||
}
|
}
|
||||||
|
@ -12617,6 +12649,7 @@ pub(crate) mod tests {
|
||||||
.check_transaction_for_nonce(
|
.check_transaction_for_nonce(
|
||||||
&SanitizedTransaction::from_transaction_for_tests(tx),
|
&SanitizedTransaction::from_transaction_for_tests(tx),
|
||||||
true, // enable_durable_nonce
|
true, // enable_durable_nonce
|
||||||
|
&bank.next_durable_nonce(),
|
||||||
)
|
)
|
||||||
.is_none());
|
.is_none());
|
||||||
}
|
}
|
||||||
|
@ -12644,6 +12677,7 @@ pub(crate) mod tests {
|
||||||
.check_transaction_for_nonce(
|
.check_transaction_for_nonce(
|
||||||
&SanitizedTransaction::from_transaction_for_tests(tx),
|
&SanitizedTransaction::from_transaction_for_tests(tx),
|
||||||
true, // enable_durable_nonce
|
true, // enable_durable_nonce
|
||||||
|
&bank.next_durable_nonce(),
|
||||||
)
|
)
|
||||||
.is_none());
|
.is_none());
|
||||||
}
|
}
|
||||||
|
@ -12675,6 +12709,36 @@ pub(crate) mod tests {
|
||||||
assert_eq!(bank.process_transaction(&tx), expect);
|
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]
|
#[test]
|
||||||
fn test_nonce_transaction() {
|
fn test_nonce_transaction() {
|
||||||
let mut feature_set = FeatureSet::all_enabled();
|
let mut feature_set = FeatureSet::all_enabled();
|
||||||
|
@ -13313,6 +13377,7 @@ pub(crate) mod tests {
|
||||||
bank.check_transaction_for_nonce(
|
bank.check_transaction_for_nonce(
|
||||||
&SanitizedTransaction::from_transaction_for_tests(tx),
|
&SanitizedTransaction::from_transaction_for_tests(tx),
|
||||||
true, // enable_durable_nonce
|
true, // enable_durable_nonce
|
||||||
|
&bank.next_durable_nonce(),
|
||||||
),
|
),
|
||||||
None
|
None
|
||||||
);
|
);
|
||||||
|
|
|
@ -436,6 +436,10 @@ pub mod nonce_must_be_authorized {
|
||||||
solana_sdk::declare_id!("HxrEu1gXuH7iD3Puua1ohd5n4iUKJyFNtNxk9DVJkvgr");
|
solana_sdk::declare_id!("HxrEu1gXuH7iD3Puua1ohd5n4iUKJyFNtNxk9DVJkvgr");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod nonce_must_be_advanceable {
|
||||||
|
solana_sdk::declare_id!("3u3Er5Vc2jVcwz4xr2GJeSAXT3fAj6ADHZ4BJMZiScFd");
|
||||||
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
/// Map of feature identifiers to user-visible description
|
/// Map of feature identifiers to user-visible description
|
||||||
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
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"),
|
(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"),
|
(quick_bail_on_panic::id(), "quick bail on panic"),
|
||||||
(nonce_must_be_authorized::id(), "nonce must be authorized"),
|
(nonce_must_be_authorized::id(), "nonce must be authorized"),
|
||||||
|
(nonce_must_be_advanceable::id(), "durable nonces must be advanceable"),
|
||||||
/*************** ADD NEW FEATURES HERE ***************/
|
/*************** ADD NEW FEATURES HERE ***************/
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
|
|
Loading…
Reference in New Issue