Feature-gate hash-based duplicate transaction check

This commit is contained in:
Trent Nelson 2021-04-16 11:32:29 -06:00 committed by mergify[bot]
parent 7e3db1dedb
commit 285f3c9d56
2 changed files with 33 additions and 21 deletions

View File

@ -2551,28 +2551,19 @@ impl Bank {
&self, &self,
hashed_tx: &HashedTransaction, hashed_tx: &HashedTransaction,
status_cache: &StatusCache<Result<()>>, status_cache: &StatusCache<Result<()>>,
check_duplicates_by_hash_enabled: bool,
) -> bool { ) -> bool {
let tx = hashed_tx.transaction(); let tx = hashed_tx.transaction();
if status_cache let status_cache_key: Option<&[u8]> = if check_duplicates_by_hash_enabled {
.get_status( Some(hashed_tx.message_hash.as_ref())
&hashed_tx.message_hash, } else {
&tx.message().recent_blockhash, tx.signatures.get(0).map(|sig0| sig0.as_ref())
&self.ancestors, };
) status_cache_key
.is_some() .and_then(|key| {
{ status_cache.get_status(key, &tx.message().recent_blockhash, &self.ancestors)
return true;
}
// Fallback to signature check in case this validator has only recently started
// adding the message hash to the status cache.
return tx
.signatures
.get(0)
.and_then(|sig0| {
status_cache.get_status(sig0, &tx.message().recent_blockhash, &self.ancestors)
}) })
.is_some(); .is_some()
} }
fn check_status_cache( fn check_status_cache(
@ -2582,11 +2573,18 @@ impl Bank {
error_counters: &mut ErrorCounters, error_counters: &mut ErrorCounters,
) -> Vec<TransactionCheckResult> { ) -> Vec<TransactionCheckResult> {
let rcache = self.src.status_cache.read().unwrap(); let rcache = self.src.status_cache.read().unwrap();
let check_duplicates_by_hash_enabled = self.check_duplicates_by_hash_enabled();
hashed_txs hashed_txs
.iter() .iter()
.zip(lock_results) .zip(lock_results)
.map(|(hashed_tx, (lock_res, nonce_rollback))| { .map(|(hashed_tx, (lock_res, nonce_rollback))| {
if lock_res.is_ok() && self.is_tx_already_processed(hashed_tx, &rcache) { if lock_res.is_ok()
&& self.is_tx_already_processed(
hashed_tx,
&rcache,
check_duplicates_by_hash_enabled,
)
{
error_counters.already_processed += 1; error_counters.already_processed += 1;
return (Err(TransactionError::AlreadyProcessed), None); return (Err(TransactionError::AlreadyProcessed), None);
} }
@ -4758,6 +4756,11 @@ impl Bank {
.is_active(&feature_set::check_init_vote_data::id()) .is_active(&feature_set::check_init_vote_data::id())
} }
pub fn check_duplicates_by_hash_enabled(&self) -> bool {
self.feature_set
.is_active(&feature_set::check_duplicates_by_hash::id())
}
// Check if the wallclock time from bank creation to now has exceeded the allotted // Check if the wallclock time from bank creation to now has exceeded the allotted
// time for transaction processing // time for transaction processing
pub fn should_bank_still_be_processing_txs( pub fn should_bank_still_be_processing_txs(
@ -7958,7 +7961,8 @@ pub(crate) mod tests {
#[test] #[test]
fn test_tx_already_processed() { fn test_tx_already_processed() {
let (genesis_config, mint_keypair) = create_genesis_config(2); let (genesis_config, mint_keypair) = create_genesis_config(2);
let bank = Bank::new(&genesis_config); let mut bank = Bank::new(&genesis_config);
assert!(!bank.check_duplicates_by_hash_enabled());
let key1 = Keypair::new(); let key1 = Keypair::new();
let mut tx = let mut tx =
@ -7976,6 +7980,9 @@ pub(crate) mod tests {
// Clear transaction signature // Clear transaction signature
tx.signatures[0] = Signature::default(); tx.signatures[0] = Signature::default();
// Enable duplicate check by message hash
bank.feature_set = Arc::new(FeatureSet::all_enabled());
// Ensure that message hash check works // Ensure that message hash check works
assert_eq!( assert_eq!(
bank.process_transaction(&tx), bank.process_transaction(&tx),

View File

@ -131,6 +131,10 @@ pub mod sysvar_via_syscall {
solana_sdk::declare_id!("7411E6gFQLDhQkdRjmpXwM1hzHMMoYQUjHicmvGPC1Nf"); solana_sdk::declare_id!("7411E6gFQLDhQkdRjmpXwM1hzHMMoYQUjHicmvGPC1Nf");
} }
pub mod check_duplicates_by_hash {
solana_sdk::declare_id!("8ZqTSYHgzyaYCcXJPMViRy6afCFSgNvYooPDeVdyj5GC");
}
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> = [
@ -164,6 +168,7 @@ lazy_static! {
(upgradeable_close_instruction::id(), "close upgradeable buffer accounts"), (upgradeable_close_instruction::id(), "close upgradeable buffer accounts"),
(demote_sysvar_write_locks::id(), "demote builtins and sysvar write locks to readonly #15497"), (demote_sysvar_write_locks::id(), "demote builtins and sysvar write locks to readonly #15497"),
(sysvar_via_syscall::id(), "Provide sysvars via syscalls"), (sysvar_via_syscall::id(), "Provide sysvars via syscalls"),
(check_duplicates_by_hash::id(), "use transaction message hash for duplicate check"),
/*************** ADD NEW FEATURES HERE ***************/ /*************** ADD NEW FEATURES HERE ***************/
] ]
.iter() .iter()