diff --git a/consensus/src/transaction_shuffler/deprecated_fairness/conflict_key/entry_fun.rs b/consensus/src/transaction_shuffler/deprecated_fairness/conflict_key/entry_fun.rs deleted file mode 100644 index 7a3363dd9e959..0000000000000 --- a/consensus/src/transaction_shuffler/deprecated_fairness/conflict_key/entry_fun.rs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::transaction_shuffler::deprecated_fairness::conflict_key::ConflictKey; -use aptos_types::transaction::{SignedTransaction, TransactionPayload}; -use move_core_types::{identifier::Identifier, language_storage::ModuleId}; - -#[derive(Eq, Hash, PartialEq)] -pub enum EntryFunKey { - EntryFun { - module: ModuleId, - function: Identifier, - }, - Exempt, -} - -impl ConflictKey for EntryFunKey { - fn extract_from(txn: &SignedTransaction) -> Self { - match txn.payload() { - TransactionPayload::EntryFunction(entry_fun) => { - let module_id = entry_fun.module(); - if module_id.address().is_special() { - // Exempt framework modules - Self::Exempt - } else { - // n.b. Generics ignored. - Self::EntryFun { - module: module_id.clone(), - function: entry_fun.function().to_owned(), - } - } - }, - TransactionPayload::Multisig(_) - | TransactionPayload::Script(_) - | TransactionPayload::ModuleBundle(_) => Self::Exempt, - } - } - - fn conflict_exempt(&self) -> bool { - match self { - Self::Exempt => true, - Self::EntryFun { .. } => false, - } - } -} diff --git a/consensus/src/transaction_shuffler/deprecated_fairness/conflict_key/entry_fun_module.rs b/consensus/src/transaction_shuffler/deprecated_fairness/conflict_key/entry_fun_module.rs deleted file mode 100644 index 948d2e8baa330..0000000000000 --- a/consensus/src/transaction_shuffler/deprecated_fairness/conflict_key/entry_fun_module.rs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::transaction_shuffler::deprecated_fairness::conflict_key::ConflictKey; -use aptos_types::transaction::{SignedTransaction, TransactionPayload}; -use move_core_types::language_storage::ModuleId; - -#[derive(Eq, Hash, PartialEq)] -pub enum EntryFunModuleKey { - Module(ModuleId), - AnyScriptOrMultiSig, - Exempt, -} - -impl ConflictKey for EntryFunModuleKey { - fn extract_from(txn: &SignedTransaction) -> Self { - match txn.payload() { - TransactionPayload::EntryFunction(entry_fun) => { - let module_id = entry_fun.module(); - - if module_id.address().is_special() { - Self::Exempt - } else { - Self::Module(module_id.clone()) - } - }, - TransactionPayload::Multisig(..) - | TransactionPayload::Script(_) - | TransactionPayload::ModuleBundle(_) => Self::AnyScriptOrMultiSig, - } - } - - fn conflict_exempt(&self) -> bool { - match self { - Self::Exempt => true, - Self::Module(..) | Self::AnyScriptOrMultiSig => false, - } - } -} diff --git a/consensus/src/transaction_shuffler/deprecated_fairness/conflict_key/mod.rs b/consensus/src/transaction_shuffler/deprecated_fairness/conflict_key/mod.rs deleted file mode 100644 index 1e233dbdc29fc..0000000000000 --- a/consensus/src/transaction_shuffler/deprecated_fairness/conflict_key/mod.rs +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::transaction_shuffler::deprecated_fairness::TxnIdx; -use std::{collections::HashMap, hash::Hash}; - -pub(crate) mod entry_fun; -pub(crate) mod entry_fun_module; -pub(crate) mod txn_sender; - -#[cfg(test)] -pub(crate) mod test_utils; - -/// `ConflictKey::extract_from(txn)` extracts a key from a transaction. For example, -/// `TxnSenderKey::extract_from(txn)` returns the transaction sender's address. The key is used by -/// the shuffler to determine whether two close by transactions conflict with each other. -/// -/// `ConflictKey::conflict_exempt(&key)` returns if this specific key is exempt from conflict. -/// For example, we can exempt transaction sender 0x1, so that consecutive transactions sent by -/// 0x1 are not seen as a conflict by the shuffler. -pub(crate) trait ConflictKey: Eq + Hash + PartialEq { - fn extract_from(txn: &Txn) -> Self; - - fn conflict_exempt(&self) -> bool; -} - -#[derive(Clone, Copy, Debug)] -pub(crate) struct ConflictKeyId(usize); - -impl ConflictKeyId { - pub fn as_idx(&self) -> usize { - self.0 - } -} - -/// `ConflictKeyRegistry::build::()` goes through a block of transactions and -/// extract the conflict keys from each transaction. In that process, each unique conflict key is -/// assigned a unique `ConflictKeyId`, essentially a sequence number, and the registry remembers which -/// key was extracted from each transaction. After that, we can query the registry to get the key -/// represented by the id, which is 1. cheaper than calling `ConflictKey::extract_from(txn)` again; -/// 2. enables vector based `MapByKeyId` which is cheaper than a `HashMap`; and 3. eliminates the typing -/// information and easier to use in the shuffler. -#[derive(Debug)] -pub(crate) struct ConflictKeyRegistry { - id_by_txn: Vec, - is_exempt_by_id: Vec, -} - -// Provided `ConflictKeyId`s managed by `ConflictKeyRegistry`s are consecutive integers starting -// from 0, a map can be implemented based on a vector, which is cheapter than a hash map. -#[derive(Debug, Eq, PartialEq)] -pub(crate) struct MapByKeyId { - inner: Vec, -} - -impl MapByKeyId { - pub fn new(size: usize) -> Self { - let mut inner = Vec::with_capacity(size); - inner.resize_with(size, Default::default); - - Self { inner } - } - - pub fn get(&self, key_id: ConflictKeyId) -> &T { - &self.inner[key_id.as_idx()] - } - - pub fn get_mut(&mut self, key_id: ConflictKeyId) -> &mut T { - &mut self.inner[key_id.as_idx()] - } -} - -impl ConflictKeyRegistry { - pub fn build(txns: &[Txn]) -> Self - where - K: ConflictKey, - { - let mut registry = HashMap::::new(); - let mut is_exempt_by_id = Vec::new(); - - let id_by_txn = txns - .iter() - .map(|txn| { - let key = K::extract_from(txn); - *registry.entry(key).or_insert_with_key(|key| { - is_exempt_by_id.push(key.conflict_exempt()); - ConflictKeyId(is_exempt_by_id.len() - 1) - }) - }) - .collect(); - - Self { - id_by_txn, - is_exempt_by_id, - } - } - - fn num_ids(&self) -> usize { - self.is_exempt_by_id.len() - } - - pub fn num_txns(&self) -> usize { - self.id_by_txn.len() - } - - pub fn new_map_by_id(&self) -> MapByKeyId { - MapByKeyId::new(self.num_ids()) - } - - pub fn key_id_for_txn(&self, txn_idx: TxnIdx) -> ConflictKeyId { - self.id_by_txn[txn_idx] - } - - pub fn is_conflict_exempt(&self, key_id: ConflictKeyId) -> bool { - self.is_exempt_by_id[key_id.0] - } -} diff --git a/consensus/src/transaction_shuffler/deprecated_fairness/conflict_key/test_utils.rs b/consensus/src/transaction_shuffler/deprecated_fairness/conflict_key/test_utils.rs deleted file mode 100644 index fec79e2c33617..0000000000000 --- a/consensus/src/transaction_shuffler/deprecated_fairness/conflict_key/test_utils.rs +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::transaction_shuffler::deprecated_fairness::conflict_key::{ - ConflictKey, ConflictKeyId, ConflictKeyRegistry, -}; -use proptest::prelude::*; -use std::hash::Hash; - -impl ConflictKeyId { - pub fn new_for_test(idx: usize) -> Self { - Self(idx) - } -} - -impl ConflictKeyRegistry { - pub fn all_exempt(num_txns: usize) -> Self { - ConflictKeyRegistry { - id_by_txn: vec![ConflictKeyId::new_for_test(0); num_txns], - is_exempt_by_id: vec![true], - } - } - - pub fn non_conflict(num_txns: usize) -> Self { - ConflictKeyRegistry { - id_by_txn: (0..num_txns).map(ConflictKeyId::new_for_test).collect(), - is_exempt_by_id: vec![false; num_txns], - } - } - - pub fn full_conflict(num_txns: usize) -> Self { - ConflictKeyRegistry { - id_by_txn: vec![ConflictKeyId::new_for_test(0); num_txns], - is_exempt_by_id: vec![false], - } - } - - pub fn nums_per_key(nums_per_key: [usize; NUM_KEYS]) -> Self { - Self::nums_per_round_per_key([nums_per_key]) - } - - pub fn nums_per_round_per_key( - nums_per_round_per_key: [[usize; NUM_KEYS]; NUM_ROUNDS], - ) -> Self { - let mut seq = (0..NUM_ROUNDS).flat_map(|_| 0..NUM_KEYS); - let nums_per_key = nums_per_round_per_key.into_iter().flatten(); - - ConflictKeyRegistry { - id_by_txn: nums_per_key - .flat_map(|num| { - let s = seq.next().unwrap(); - vec![ConflictKeyId::new_for_test(s); num] - }) - .collect(), - is_exempt_by_id: vec![false; NUM_KEYS], - } - } -} - -#[derive(Debug)] -struct FakeAccount { - id: usize, -} - -impl Arbitrary for FakeAccount { - type Parameters = (); - type Strategy = BoxedStrategy; - - fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { - (0..10usize).prop_map(|id| FakeAccount { id }).boxed() - } -} - -#[derive(Debug)] -struct FakeModule { - id: usize, -} - -impl FakeModule { - pub fn exempt(&self) -> bool { - self.id % 3 == 0 - } -} - -impl Arbitrary for FakeModule { - type Parameters = (); - type Strategy = BoxedStrategy; - - fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { - (0..10usize).prop_map(|id| FakeModule { id }).boxed() - } -} - -#[derive(Debug)] -struct FakeEntryFun { - module: FakeModule, - id: usize, -} - -impl Arbitrary for FakeEntryFun { - type Parameters = (); - type Strategy = BoxedStrategy; - - fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { - (any::(), 0..3usize) - .prop_map(|(module, id)| FakeEntryFun { module, id }) - .boxed() - } -} - -#[derive(Debug)] -pub struct FakeTxn { - sender: FakeAccount, - entry_fun: FakeEntryFun, -} - -impl Arbitrary for FakeTxn { - type Parameters = (); - type Strategy = BoxedStrategy; - - fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { - (any::(), any::()) - .prop_map(|(sender, entry_fun)| FakeTxn { sender, entry_fun }) - .boxed() - } -} - -#[derive(Eq, Hash, PartialEq)] -pub(crate) struct FakeSenderKey { - id: usize, -} - -impl ConflictKey for FakeSenderKey { - fn extract_from(txn: &FakeTxn) -> Self { - Self { id: txn.sender.id } - } - - fn conflict_exempt(&self) -> bool { - false - } -} - -#[derive(Eq, Hash, PartialEq)] -pub(crate) enum FakeEntryFunModuleKey { - Module(usize), - Exempt, -} - -impl ConflictKey for FakeEntryFunModuleKey { - fn extract_from(txn: &FakeTxn) -> Self { - if txn.entry_fun.module.exempt() { - Self::Exempt - } else { - Self::Module(txn.entry_fun.module.id) - } - } - - fn conflict_exempt(&self) -> bool { - match self { - Self::Exempt => true, - Self::Module(..) => false, - } - } -} - -#[derive(Eq, Hash, PartialEq)] -pub(crate) enum FakeEntryFunKey { - EntryFun { module: usize, function: usize }, - Exempt, -} - -impl ConflictKey for FakeEntryFunKey { - fn extract_from(txn: &FakeTxn) -> Self { - if txn.entry_fun.module.exempt() { - Self::Exempt - } else { - Self::EntryFun { - module: txn.entry_fun.module.id, - function: txn.entry_fun.id, - } - } - } - - fn conflict_exempt(&self) -> bool { - match self { - Self::Exempt => true, - Self::EntryFun { .. } => false, - } - } -} diff --git a/consensus/src/transaction_shuffler/deprecated_fairness/conflict_key/txn_sender.rs b/consensus/src/transaction_shuffler/deprecated_fairness/conflict_key/txn_sender.rs deleted file mode 100644 index a742e7c240573..0000000000000 --- a/consensus/src/transaction_shuffler/deprecated_fairness/conflict_key/txn_sender.rs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::transaction_shuffler::deprecated_fairness::conflict_key::ConflictKey; -use aptos_types::transaction::SignedTransaction; -use move_core_types::account_address::AccountAddress; - -#[derive(Eq, Hash, PartialEq)] -pub struct TxnSenderKey(AccountAddress); - -impl ConflictKey for TxnSenderKey { - fn extract_from(txn: &SignedTransaction) -> Self { - TxnSenderKey(txn.sender()) - } - - fn conflict_exempt(&self) -> bool { - false - } -} diff --git a/consensus/src/transaction_shuffler/deprecated_fairness/conflict_zone.rs b/consensus/src/transaction_shuffler/deprecated_fairness/conflict_zone.rs deleted file mode 100644 index a685cf701ac07..0000000000000 --- a/consensus/src/transaction_shuffler/deprecated_fairness/conflict_zone.rs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::transaction_shuffler::deprecated_fairness::{ - conflict_key::{ConflictKeyId, ConflictKeyRegistry, MapByKeyId}, - TxnIdx, -}; -use std::collections::VecDeque; - -/// A sliding window of transactions (TxnIds), represented by `ConflictKeyId`s extracted from a -/// specific `ConflictKey`, managed by a specific `ConflictKeyRegistry`. -#[derive(Debug)] -pub(crate) struct ConflictZone<'a> { - sliding_window_size: usize, - sliding_window: VecDeque, - /// Number of transactions in the sliding window for each key_id. `ConflictZone::is_conflict(key)` - /// returns true is the count for `key` is greater than 0, unless the key is exempt from conflict. - counts_by_id: MapByKeyId, - key_registry: &'a ConflictKeyRegistry, -} - -impl<'a> ConflictZone<'a> { - pub fn build_zones( - key_registries: &'a [ConflictKeyRegistry; NUM_CONFLICT_ZONES], - window_sizes: [usize; NUM_CONFLICT_ZONES], - ) -> [Self; NUM_CONFLICT_ZONES] { - itertools::zip_eq(key_registries.iter(), window_sizes) - .map(|(registry, window_size)| Self::new(registry, window_size)) - .collect::>() - .try_into() - .expect("key_registries and window_sizes must have the same length.") - } - - fn new(key_registry: &'a ConflictKeyRegistry, sliding_window_size: usize) -> Self { - Self { - sliding_window_size, - sliding_window: VecDeque::with_capacity(sliding_window_size + 1), - counts_by_id: key_registry.new_map_by_id(), - key_registry, - } - } - - pub fn is_conflict(&self, txn_idx: TxnIdx) -> bool { - let key_id = self.key_registry.key_id_for_txn(txn_idx); - if self.key_registry.is_conflict_exempt(key_id) { - false - } else { - *self.counts_by_id.get(key_id) > 0 - } - } - - /// Append a new transaction to the sliding window and - /// return the key_id that's no longer in conflict as a result if there is one. - pub fn add(&mut self, txn_idx: TxnIdx) -> Option { - let key_id = self.key_registry.key_id_for_txn(txn_idx); - - *self.counts_by_id.get_mut(key_id) += 1; - self.sliding_window.push_back(key_id); - if self.sliding_window.len() > self.sliding_window_size { - if let Some(removed_key_id) = self.sliding_window.pop_front() { - let count = self.counts_by_id.get_mut(removed_key_id); - *count -= 1; - if *count == 0 && !self.key_registry.is_conflict_exempt(removed_key_id) { - return Some(removed_key_id); - } - } - } - None - } -} diff --git a/consensus/src/transaction_shuffler/deprecated_fairness/mod.rs b/consensus/src/transaction_shuffler/deprecated_fairness/mod.rs deleted file mode 100644 index 03cc1eb1c6d18..0000000000000 --- a/consensus/src/transaction_shuffler/deprecated_fairness/mod.rs +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::transaction_shuffler::{ - deprecated_fairness::{ - conflict_key::{ - entry_fun::EntryFunKey, entry_fun_module::EntryFunModuleKey, txn_sender::TxnSenderKey, - ConflictKeyRegistry, - }, - conflict_zone::ConflictZone, - pending_zone::PendingZone, - }, - TransactionShuffler, -}; -use aptos_types::transaction::SignedTransaction; -use itertools::zip_eq; -use selection_tracker::SelectionTracker; -use std::collections::BTreeSet; - -pub(crate) mod conflict_key; -mod conflict_zone; -mod pending_zone; -mod selection_tracker; - -#[cfg(test)] -mod tests; - -type TxnIdx = usize; - -#[derive(Debug)] -pub struct FairnessShuffler { - pub sender_conflict_window_size: usize, - pub module_conflict_window_size: usize, - pub entry_fun_conflict_window_size: usize, -} - -impl FairnessShuffler { - fn conflict_key_registries(txns: &[SignedTransaction]) -> [ConflictKeyRegistry; 3] { - [ - ConflictKeyRegistry::build::(txns), - ConflictKeyRegistry::build::(txns), - ConflictKeyRegistry::build::(txns), - ] - } - - fn window_sizes(&self) -> [usize; 3] { - [ - self.sender_conflict_window_size, - self.module_conflict_window_size, - self.entry_fun_conflict_window_size, - ] - } -} - -impl TransactionShuffler for FairnessShuffler { - fn shuffle(&self, txns: Vec) -> Vec { - let conflict_key_registries = Self::conflict_key_registries(&txns); - let order = - FairnessShufflerImpl::new(&conflict_key_registries, self.window_sizes()).shuffle(); - reorder(txns, &order) - } -} - -fn reorder(txns: Vec, order: &[TxnIdx]) -> Vec { - assert_eq!(txns.len(), order.len()); - order.iter().map(|idx| txns[*idx].clone()).collect() -} - -struct FairnessShufflerImpl<'a, const NUM_CONFLICT_ZONES: usize> { - conflict_zones: [ConflictZone<'a>; NUM_CONFLICT_ZONES], - pending_zones: [PendingZone<'a>; NUM_CONFLICT_ZONES], - selected_order: Vec, - selection_tracker: SelectionTracker, -} - -impl<'a, const NUM_CONFLICT_ZONES: usize> FairnessShufflerImpl<'a, NUM_CONFLICT_ZONES> { - pub fn new( - conflict_key_registries: &'a [ConflictKeyRegistry; NUM_CONFLICT_ZONES], - window_sizes: [usize; NUM_CONFLICT_ZONES], - ) -> Self { - let num_txns = conflict_key_registries[0].num_txns(); - assert!(conflict_key_registries - .iter() - .skip(1) - .all(|r| r.num_txns() == num_txns)); - - Self { - selected_order: Vec::with_capacity(num_txns), - selection_tracker: SelectionTracker::new(num_txns), - conflict_zones: ConflictZone::build_zones(conflict_key_registries, window_sizes), - pending_zones: PendingZone::build_zones(conflict_key_registries), - } - } - - /// Spread (delay) transactions that have conflicts with adjacent previous transactions - /// according to multiple dimensions (`ConflictKey`s). Invariant is held that for each conflict - /// key, i.e. the transaction sender, the module, and the entry function, etc., the order of - /// transactions with the same key is preserved -- unless the key is exempt from conflict. - /// - /// For example, all transactions from a single sender will preserve their order; all transactions - /// from the same module will preserve their order, unless they are of the aptos framework - /// module -- p2p transfers of APT can violate this invariant. - /// - /// Each transaction comes at most once out of `self.selection_tracker.next_unselected()` for - /// both passes, that's O(2n). And each transaction comes out of each conflict zones at most - /// once, that's O(3n). In either case, the transaction is examined by going through all 3 - /// conflict zones and all 3 pending zones. So the time complexity is O(9n) = O(n). Or if we - /// consider `NUM_CONFLICT_ZONES = m`, the time complexity is O(m*m*n). - pub fn shuffle(mut self) -> Vec { - // First pass, only select transactions with no conflicts in all conflict zones - while let Some(txn_idx) = self.selection_tracker.next_unselected() { - if !self.is_conflict(txn_idx) && !self.is_head_of_line_blocked(txn_idx) { - self.select_and_select_unconflicted(txn_idx, false /* is_pending */) - } else { - self.add_pending(txn_idx); - } - } - - // Second pass, select previously pending txns in order, - // with newly un-conflicted txns jumping the line - self.selection_tracker.new_pass(); - while let Some(txn_idx) = self.selection_tracker.next_unselected() { - self.select_and_select_unconflicted(txn_idx, true /* is_pending */); - } - - self.selected_order - } - - fn select_and_select_unconflicted(&mut self, txn_idx: TxnIdx, is_pending: bool) { - let mut maybe_unconflicted = self.select(txn_idx, is_pending); - while let Some(txn_idx) = maybe_unconflicted.pop_first() { - if !self.is_conflict(txn_idx) && !self.is_head_of_line_blocked(txn_idx) { - maybe_unconflicted.extend(self.select(txn_idx, true /* is_pending */)) - } - } - } - - /// Select a transaction and return potentially un-conflicted transactions - fn select(&mut self, txn_idx: TxnIdx, is_pending: bool) -> BTreeSet { - self.selection_tracker.mark_selected(txn_idx); - self.selected_order.push(txn_idx); - if is_pending { - self.pop_pending(txn_idx); - } - - let mut maybe_unconflicted = BTreeSet::new(); - for (conflict_zone, pending_zone) in - zip_eq(&mut self.conflict_zones, &mut self.pending_zones) - { - if let Some(key_id) = conflict_zone.add(txn_idx) { - if let Some(pending) = pending_zone.first_pending_on_key(key_id) { - maybe_unconflicted.insert(pending); - } - } - } - - maybe_unconflicted - } - - fn is_conflict(&self, txn_idx: TxnIdx) -> bool { - self.conflict_zones.iter().any(|z| z.is_conflict(txn_idx)) - } - - fn is_head_of_line_blocked(&self, txn_idx: TxnIdx) -> bool { - self.pending_zones - .iter() - .any(|z| z.head_of_line_blocked(txn_idx)) - } - - fn add_pending(&mut self, txn_idx: TxnIdx) { - self.pending_zones.iter_mut().for_each(|z| z.add(txn_idx)); - } - - fn pop_pending(&mut self, txn_idx: TxnIdx) { - self.pending_zones.iter_mut().for_each(|z| z.pop(txn_idx)); - } -} - -#[cfg(test)] -mod test_utils { - use crate::transaction_shuffler::deprecated_fairness::FairnessShuffler; - use proptest::prelude::*; - - impl FairnessShuffler { - pub fn new_for_test( - sender_conflict_window_size: usize, - module_conflict_window_size: usize, - entry_fun_conflict_window_size: usize, - ) -> Self { - Self { - sender_conflict_window_size, - module_conflict_window_size, - entry_fun_conflict_window_size, - } - } - } - - impl Arbitrary for FairnessShuffler { - type Parameters = (); - type Strategy = BoxedStrategy; - - fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { - (0..10usize, 0..10usize, 0..10usize) - .prop_map( - |( - sender_conflict_window_size, - module_conflict_window_size, - entry_fun_conflict_window_size, - )| { - FairnessShuffler { - sender_conflict_window_size, - module_conflict_window_size, - entry_fun_conflict_window_size, - } - }, - ) - .boxed() - } - } -} diff --git a/consensus/src/transaction_shuffler/deprecated_fairness/pending_zone.rs b/consensus/src/transaction_shuffler/deprecated_fairness/pending_zone.rs deleted file mode 100644 index eb9c2af18455f..0000000000000 --- a/consensus/src/transaction_shuffler/deprecated_fairness/pending_zone.rs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::transaction_shuffler::deprecated_fairness::{ - conflict_key::{ConflictKeyId, ConflictKeyRegistry, MapByKeyId}, - TxnIdx, -}; -use std::collections::VecDeque; - -/// A queue for each confclit Key, represented by `ConflictKeyId`s managed by `ConflictKeyRegistry`. -#[derive(Debug)] -pub(crate) struct PendingZone<'a> { - key_registry: &'a ConflictKeyRegistry, - pending_by_key: MapByKeyId>, -} - -impl<'a> PendingZone<'a> { - pub fn build_zones( - key_registries: &'a [ConflictKeyRegistry; NUM_CONFLICT_ZONES], - ) -> [Self; NUM_CONFLICT_ZONES] { - key_registries - .iter() - .map(Self::new) - .collect::>() - .try_into() - .expect("key_registries and the return type must have the same length.") - } - - fn new(key_registry: &'a ConflictKeyRegistry) -> Self { - Self { - key_registry, - pending_by_key: key_registry.new_map_by_id(), - } - } - - pub fn add(&mut self, txn_idx: TxnIdx) { - let key_id = self.key_registry.key_id_for_txn(txn_idx); - if !self.key_registry.is_conflict_exempt(key_id) { - self.pending_by_key.get_mut(key_id).push_back(txn_idx); - } - } - - pub fn pop(&mut self, txn_idx: TxnIdx) { - let key_id = self.key_registry.key_id_for_txn(txn_idx); - if !self.key_registry.is_conflict_exempt(key_id) { - let popped = self - .pending_by_key - .get_mut(key_id) - .pop_front() - .expect("Must exist"); - assert_eq!(popped, txn_idx); - } - } - - pub fn head_of_line_blocked(&self, txn_idx: TxnIdx) -> bool { - let key_id = self.key_registry.key_id_for_txn(txn_idx); - if self.key_registry.is_conflict_exempt(key_id) { - false - } else { - match self.pending_by_key.get(key_id).front() { - Some(front) => *front < txn_idx, - None => false, - } - } - } - - pub fn first_pending_on_key(&self, key_id: ConflictKeyId) -> Option { - self.pending_by_key.get(key_id).front().cloned() - } -} diff --git a/consensus/src/transaction_shuffler/deprecated_fairness/selection_tracker.rs b/consensus/src/transaction_shuffler/deprecated_fairness/selection_tracker.rs deleted file mode 100644 index 3e10d4368642f..0000000000000 --- a/consensus/src/transaction_shuffler/deprecated_fairness/selection_tracker.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::transaction_shuffler::deprecated_fairness::TxnIdx; - -pub struct SelectionTracker { - selected_markers: Vec, - cur_idx: usize, -} - -impl SelectionTracker { - pub fn new(num_txns: usize) -> Self { - Self { - selected_markers: vec![false; num_txns], - cur_idx: 0, - } - } - - pub fn next_unselected(&mut self) -> Option { - while self.cur_idx < self.selected_markers.len() { - let idx = self.cur_idx; - self.cur_idx += 1; - - if !self.is_selected(idx) { - return Some(idx); - } - } - None - } - - pub fn new_pass(&mut self) { - self.cur_idx = 0 - } - - pub fn mark_selected(&mut self, idx: TxnIdx) { - assert!(!self.selected_markers[idx]); - self.selected_markers[idx] = true; - } - - fn is_selected(&self, idx: TxnIdx) -> bool { - self.selected_markers[idx] - } -} diff --git a/consensus/src/transaction_shuffler/deprecated_fairness/tests/manual.rs b/consensus/src/transaction_shuffler/deprecated_fairness/tests/manual.rs deleted file mode 100644 index 753ca542a8d3f..0000000000000 --- a/consensus/src/transaction_shuffler/deprecated_fairness/tests/manual.rs +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::transaction_shuffler::deprecated_fairness::{ - conflict_key::ConflictKeyRegistry, FairnessShuffler, FairnessShufflerImpl, -}; - -struct TestCase { - shuffler: FairnessShuffler, - conflict_key_registries: [ConflictKeyRegistry; 3], - expected_order: Vec, -} - -impl TestCase { - fn run(self) { - let Self { - shuffler, - conflict_key_registries, - expected_order, - } = self; - - let order = - FairnessShufflerImpl::new(&conflict_key_registries, shuffler.window_sizes()).shuffle(); - assert_eq!(order, expected_order); - } -} - -#[test] -fn test_all_exempt() { - TestCase { - shuffler: FairnessShuffler::new_for_test(2, 2, 2), - conflict_key_registries: [ - ConflictKeyRegistry::all_exempt(9), - ConflictKeyRegistry::all_exempt(9), - ConflictKeyRegistry::all_exempt(9), - ], - expected_order: (0..9).collect(), - } - .run() -} - -#[test] -fn test_non_conflict() { - TestCase { - shuffler: FairnessShuffler::new_for_test(2, 2, 2), - conflict_key_registries: [ - ConflictKeyRegistry::non_conflict(9), - ConflictKeyRegistry::non_conflict(9), - ConflictKeyRegistry::non_conflict(9), - ], - expected_order: (0..9).collect(), - } - .run() -} - -#[test] -fn test_full_conflict() { - TestCase { - shuffler: FairnessShuffler::new_for_test(2, 2, 2), - conflict_key_registries: [ - ConflictKeyRegistry::full_conflict(9), - ConflictKeyRegistry::full_conflict(9), - ConflictKeyRegistry::full_conflict(9), - ], - expected_order: (0..9).collect(), - } - .run() -} - -#[test] -fn test_modules_ignored_by_window_size() { - TestCase { - shuffler: FairnessShuffler::new_for_test(2, 0, 2), - conflict_key_registries: [ - // [A0, A1, A2, ...] - ConflictKeyRegistry::non_conflict(8), - // [M0, M0, M0, M0, M1, M1, M2, M2] - ConflictKeyRegistry::nums_per_key([4, 2, 2]), - // [M0::E0, M0::E1, M0::E0, M0::E1, M1::E0, M1::E0, M2::E0, M2::E0] - ConflictKeyRegistry::nums_per_round_per_key([[1, 1, 0], [1, 1, 4]]), - ], - // [M0::E0, M0::E1, M1::E0, M0::E0, M0::E1, M1::E0, M2::E0, M2::E0] - expected_order: vec![0, 1, 4, 2, 3, 5, 6, 7], - } - .run() -} - -#[test] -fn test_modules_and_entry_funs_ignored_by_window_size() { - TestCase { - shuffler: FairnessShuffler::new_for_test(2, 0, 0), - conflict_key_registries: [ - // [A0, A1, A2, ...] - ConflictKeyRegistry::non_conflict(8), - // [M0, M0, M0, M0, M1, M1, M1, M1] - ConflictKeyRegistry::nums_per_key([4, 4]), - // [M0::E0, M0::E0, M0::E1, M0::E1, M1::E0, M1::E0, M1::E1, M1::E1] - ConflictKeyRegistry::nums_per_key([2, 2, 2, 2]), - ], - expected_order: (0..8).collect(), - } - .run() -} - -#[test] -fn test_exempted_modules() { - // think "full block of p2p txns" - TestCase { - shuffler: FairnessShuffler::new_for_test(3, 2, 2), - conflict_key_registries: [ - // [0:A0, 1:A0, 2:A0, 3:A0, 4:A1, 5:A1, 6:A1, 7:A2, 8:A2, 9:A3] - ConflictKeyRegistry::nums_per_key([4, 3, 2, 1]), - ConflictKeyRegistry::all_exempt(10), - ConflictKeyRegistry::all_exempt(10), - ], - // [A0, A1, A2, A3, A0, A1, A2, A0, A1] - expected_order: vec![0, 4, 7, 9, 1, 5, 8, 2, 3, 6], - } - .run() -} - -#[test] -fn test_dominating_module() { - TestCase { - shuffler: FairnessShuffler::new_for_test(4, 1, 1), - conflict_key_registries: [ - ConflictKeyRegistry::non_conflict(7), - // [M0, M0, M0, M1, M2, M3, M4] - ConflictKeyRegistry::nums_per_key([3, 1, 1, 1, 1]), - ConflictKeyRegistry::nums_per_key([3, 1, 1, 1, 1]), - ], - // [M0, M1, M0, M2, M0, M3, M4] - expected_order: vec![0, 3, 1, 4, 2, 5, 6], - } - .run() -} - -#[test] -fn test_dominating_module2() { - TestCase { - shuffler: FairnessShuffler::new_for_test(4, 1, 1), - conflict_key_registries: [ - ConflictKeyRegistry::non_conflict(8), - // [M0, M0, M0, M1, M2, M3, M4, M0] - ConflictKeyRegistry::nums_per_round_per_key([[3, 1, 1, 1, 1], [1, 0, 0, 0, 0]]), - ConflictKeyRegistry::nums_per_round_per_key([[3, 1, 1, 1, 1], [1, 0, 0, 0, 0]]), - ], - // [M0, M1, M0, M2, M0, M3, M4, M0] - expected_order: vec![0, 3, 1, 4, 2, 5, 6, 7], - } - .run() -} - -#[test] -fn test_multiple_entry_funs() { - TestCase { - shuffler: FairnessShuffler::new_for_test(4, 1, 2), - conflict_key_registries: [ - ConflictKeyRegistry::non_conflict(10), - // [M0, M0, M0, M0, M1, M1, M1, M1, M2, M2] - ConflictKeyRegistry::nums_per_key([4, 4, 2]), - // [M0::E0, M0::E1, M0::E0, M0::E1, M1::E0, M1::E0, M1::E0, M1::E0, M2::E0, M2::E0] - ConflictKeyRegistry::nums_per_round_per_key([[1, 1, 0, 0], [1, 1, 4, 2]]), - ], - // [M0::E0, M1::E0, M0::E1, M2::E0, M0::E0, M1::E0, M0:E1, M2::E0, M1::E0, M1::E0] - expected_order: vec![0, 4, 1, 8, 2, 5, 3, 9, 6, 7], - } - .run() -} diff --git a/consensus/src/transaction_shuffler/deprecated_fairness/tests/mod.rs b/consensus/src/transaction_shuffler/deprecated_fairness/tests/mod.rs deleted file mode 100644 index c3550a41f7ccc..0000000000000 --- a/consensus/src/transaction_shuffler/deprecated_fairness/tests/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -mod manual; -mod proptests; diff --git a/consensus/src/transaction_shuffler/deprecated_fairness/tests/proptests.rs b/consensus/src/transaction_shuffler/deprecated_fairness/tests/proptests.rs deleted file mode 100644 index 6d111b03d28e6..0000000000000 --- a/consensus/src/transaction_shuffler/deprecated_fairness/tests/proptests.rs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright © Aptos Foundation -// SPDX-License-Identifier: Apache-2.0 - -use crate::transaction_shuffler::deprecated_fairness::{ - conflict_key::{ - test_utils::{FakeEntryFunKey, FakeEntryFunModuleKey, FakeSenderKey, FakeTxn}, - ConflictKeyRegistry, MapByKeyId, - }, - reorder, FairnessShuffler, FairnessShufflerImpl, TxnIdx, -}; -use proptest::{collection::vec, prelude::*}; -use std::collections::BTreeSet; - -fn arb_order(num_txns: usize) -> impl Strategy> { - Just((0..num_txns).collect::>()).prop_shuffle() -} - -#[derive(Debug, Default, Eq, PartialEq)] -enum OrderOrSet { - #[default] - Empty, - Order(Vec), - Set(BTreeSet), -} - -impl OrderOrSet { - fn add(&mut self, idx: TxnIdx, is_conflict_exempt: bool) { - if self.is_empty() { - *self = if is_conflict_exempt { - Self::Set(BTreeSet::new()) - } else { - Self::Order(Vec::new()) - }; - } - - match self { - Self::Order(order) => order.push(idx), - Self::Set(set) => { - set.insert(idx); - }, - Self::Empty => unreachable!(), - } - } - - fn is_empty(&self) -> bool { - matches!(self, Self::Empty) - } -} - -fn sort_by_key( - order: impl IntoIterator, - registry: &ConflictKeyRegistry, -) -> MapByKeyId { - let mut map: MapByKeyId = registry.new_map_by_id(); - - for txn_idx in order { - let key_id = registry.key_id_for_txn(txn_idx); - let is_exempt = registry.is_conflict_exempt(key_id); - - map.get_mut(key_id).add(txn_idx, is_exempt); - } - - map -} - -fn assert_invariants(txns: &[FakeTxn], order: Vec, registry: &ConflictKeyRegistry) { - let num_txns = txns.len(); - let original_sorted = sort_by_key(0..num_txns, registry); - let result_sorted = sort_by_key(order, registry); - - assert_eq!(result_sorted, original_sorted); -} - -fn registries(txns: &[FakeTxn]) -> [ConflictKeyRegistry; 3] { - [ - ConflictKeyRegistry::build::(txns), - ConflictKeyRegistry::build::(txns), - ConflictKeyRegistry::build::(txns), - ] -} - -proptest! { - #[test] - fn test_reorder( order in (0..1000usize).prop_flat_map(arb_order) ) { - let num_txns = order.len(); - let txns = (0..num_txns).collect::>(); - - let reordered = reorder(txns, &order); - prop_assert_eq!(reordered, order); - } - - #[test] - fn test_fairness_shuffler( - txns in vec(any::(), 0..1000), - shuffler in any::(), - ) { - let registries = registries(&txns); - let order = FairnessShufflerImpl::new(®istries, shuffler.window_sizes()).shuffle(); - - for registry in ®istries { - assert_invariants(&txns, order.clone(), registry); - } - } -} diff --git a/consensus/src/transaction_shuffler/mod.rs b/consensus/src/transaction_shuffler/mod.rs index a75ce60e0cdd6..f69a230300c7c 100644 --- a/consensus/src/transaction_shuffler/mod.rs +++ b/consensus/src/transaction_shuffler/mod.rs @@ -6,7 +6,6 @@ use aptos_types::{on_chain_config::TransactionShufflerType, transaction::SignedT use sender_aware::SenderAwareShuffler; use std::sync::Arc; -mod deprecated_fairness; mod sender_aware; mod use_case_aware; @@ -45,22 +44,8 @@ pub fn create_transaction_shuffler( ); Arc::new(SenderAwareShuffler::new(conflict_window_size as usize)) }, - DeprecatedFairness { - sender_conflict_window_size, - module_conflict_window_size, - entry_fun_conflict_window_size, - } => { - info!( - "Using fairness transaction shuffling with conflict window sizes: sender {}, module {}, entry fun {}", - sender_conflict_window_size, - module_conflict_window_size, - entry_fun_conflict_window_size - ); - Arc::new(deprecated_fairness::FairnessShuffler { - sender_conflict_window_size: sender_conflict_window_size as usize, - module_conflict_window_size: module_conflict_window_size as usize, - entry_fun_conflict_window_size: entry_fun_conflict_window_size as usize, - }) + DeprecatedFairness => { + unreachable!("DeprecatedFairness shuffler is no longer supported.") }, UseCaseAware { sender_spread_factor, diff --git a/types/src/on_chain_config/execution_config.rs b/types/src/on_chain_config/execution_config.rs index 3b1c753edcefa..520143d9dd979 100644 --- a/types/src/on_chain_config/execution_config.rs +++ b/types/src/on_chain_config/execution_config.rs @@ -148,11 +148,7 @@ pub enum TransactionShufflerType { NoShuffling, DeprecatedSenderAwareV1(u32), SenderAwareV2(u32), - DeprecatedFairness { - sender_conflict_window_size: u32, - module_conflict_window_size: u32, - entry_fun_conflict_window_size: u32, - }, + DeprecatedFairness, UseCaseAware { sender_spread_factor: usize, platform_use_case_spread_factor: usize, @@ -174,7 +170,7 @@ impl TransactionShufflerType { TransactionShufflerType::NoShuffling | TransactionShufflerType::DeprecatedSenderAwareV1(_) | TransactionShufflerType::SenderAwareV2(_) - | TransactionShufflerType::DeprecatedFairness { .. } => None, + | TransactionShufflerType::DeprecatedFairness => None, TransactionShufflerType::UseCaseAware { user_use_case_spread_factor, ..