Feature-gate hash-based duplicate transaction check
This commit is contained in:
parent
7e3db1dedb
commit
285f3c9d56
|
@ -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),
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in New Issue