forward packets by prioritization in desc order (#25406)
- Forward packets by prioritization in desc order - Add support of cost-tracking by transaction requested compute units - Hook up account buckets to forwarder - Add metrics for forwardable batches count - Remove redundant invalid packets filtering at end of slot since forwarder will do the same when batch forwardable packets - Add bench test for forwarding
This commit is contained in:
parent
38216aa781
commit
c1d89ad749
|
@ -249,8 +249,8 @@ fn main() {
|
||||||
let (tpu_vote_sender, tpu_vote_receiver) = unbounded();
|
let (tpu_vote_sender, tpu_vote_receiver) = unbounded();
|
||||||
let (replay_vote_sender, _replay_vote_receiver) = unbounded();
|
let (replay_vote_sender, _replay_vote_receiver) = unbounded();
|
||||||
let bank0 = Bank::new_for_benches(&genesis_config);
|
let bank0 = Bank::new_for_benches(&genesis_config);
|
||||||
let mut bank_forks = BankForks::new(bank0);
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank0)));
|
||||||
let mut bank = bank_forks.working_bank();
|
let mut bank = bank_forks.read().unwrap().working_bank();
|
||||||
|
|
||||||
// set cost tracker limits to MAX so it will not filter out TXs
|
// set cost tracker limits to MAX so it will not filter out TXs
|
||||||
bank.write_cost_tracker()
|
bank.write_cost_tracker()
|
||||||
|
@ -357,6 +357,7 @@ fn main() {
|
||||||
replay_vote_sender,
|
replay_vote_sender,
|
||||||
Arc::new(RwLock::new(CostModel::default())),
|
Arc::new(RwLock::new(CostModel::default())),
|
||||||
Arc::new(connection_cache),
|
Arc::new(connection_cache),
|
||||||
|
bank_forks.clone(),
|
||||||
);
|
);
|
||||||
poh_recorder.write().unwrap().set_bank(&bank, false);
|
poh_recorder.write().unwrap().set_bank(&bank, false);
|
||||||
|
|
||||||
|
@ -428,8 +429,8 @@ fn main() {
|
||||||
new_bank_time.stop();
|
new_bank_time.stop();
|
||||||
|
|
||||||
let mut insert_time = Measure::start("insert_time");
|
let mut insert_time = Measure::start("insert_time");
|
||||||
bank_forks.insert(new_bank);
|
bank_forks.write().unwrap().insert(new_bank);
|
||||||
bank = bank_forks.working_bank();
|
bank = bank_forks.read().unwrap().working_bank();
|
||||||
insert_time.stop();
|
insert_time.stop();
|
||||||
|
|
||||||
// set cost tracker limits to MAX so it will not filter out TXs
|
// set cost tracker limits to MAX so it will not filter out TXs
|
||||||
|
@ -443,7 +444,10 @@ fn main() {
|
||||||
assert!(poh_recorder.read().unwrap().bank().is_some());
|
assert!(poh_recorder.read().unwrap().bank().is_some());
|
||||||
if bank.slot() > 32 {
|
if bank.slot() > 32 {
|
||||||
leader_schedule_cache.set_root(&bank);
|
leader_schedule_cache.set_root(&bank);
|
||||||
bank_forks.set_root(root, &AbsRequestSender::default(), None);
|
bank_forks
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.set_root(root, &AbsRequestSender::default(), None);
|
||||||
root += 1;
|
root += 1;
|
||||||
}
|
}
|
||||||
debug!(
|
debug!(
|
||||||
|
@ -476,7 +480,11 @@ fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let txs_processed = bank_forks.working_bank().transaction_count();
|
let txs_processed = bank_forks
|
||||||
|
.read()
|
||||||
|
.unwrap()
|
||||||
|
.working_bank()
|
||||||
|
.transaction_count();
|
||||||
debug!("processed: {} base: {}", txs_processed, base_tx_count);
|
debug!("processed: {} base: {}", txs_processed, base_tx_count);
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"{{'name': 'banking_bench_total', 'median': '{:.2}'}}",
|
"{{'name': 'banking_bench_total', 'median': '{:.2}'}}",
|
||||||
|
|
|
@ -25,7 +25,7 @@ use {
|
||||||
},
|
},
|
||||||
solana_perf::{packet::to_packet_batches, test_tx::test_tx},
|
solana_perf::{packet::to_packet_batches, test_tx::test_tx},
|
||||||
solana_poh::poh_recorder::{create_test_recorder, WorkingBankEntry},
|
solana_poh::poh_recorder::{create_test_recorder, WorkingBankEntry},
|
||||||
solana_runtime::{bank::Bank, cost_model::CostModel},
|
solana_runtime::{bank::Bank, bank_forks::BankForks, cost_model::CostModel},
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
genesis_config::GenesisConfig,
|
genesis_config::GenesisConfig,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
|
@ -170,7 +170,8 @@ fn bench_banking(bencher: &mut Bencher, tx_type: TransactionType) {
|
||||||
let mut bank = Bank::new_for_benches(&genesis_config);
|
let mut bank = Bank::new_for_benches(&genesis_config);
|
||||||
// Allow arbitrary transaction processing time for the purposes of this bench
|
// Allow arbitrary transaction processing time for the purposes of this bench
|
||||||
bank.ns_per_slot = u128::MAX;
|
bank.ns_per_slot = u128::MAX;
|
||||||
let bank = Arc::new(bank);
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
|
||||||
|
let bank = bank_forks.read().unwrap().get(0).unwrap();
|
||||||
|
|
||||||
// set cost tracker limits to MAX so it will not filter out TXs
|
// set cost tracker limits to MAX so it will not filter out TXs
|
||||||
bank.write_cost_tracker()
|
bank.write_cost_tracker()
|
||||||
|
@ -232,6 +233,7 @@ fn bench_banking(bencher: &mut Bencher, tx_type: TransactionType) {
|
||||||
s,
|
s,
|
||||||
Arc::new(RwLock::new(CostModel::default())),
|
Arc::new(RwLock::new(CostModel::default())),
|
||||||
Arc::new(ConnectionCache::default()),
|
Arc::new(ConnectionCache::default()),
|
||||||
|
bank_forks,
|
||||||
);
|
);
|
||||||
poh_recorder.write().unwrap().set_bank(&bank, false);
|
poh_recorder.write().unwrap().set_bank(&bank, false);
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,19 @@ extern crate test;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
rand::distributions::{Distribution, Uniform},
|
rand::distributions::{Distribution, Uniform},
|
||||||
solana_core::unprocessed_packet_batches::*,
|
solana_core::{
|
||||||
|
banking_stage::*, forward_packet_batches_by_accounts::ForwardPacketBatchesByAccounts,
|
||||||
|
unprocessed_packet_batches::*,
|
||||||
|
},
|
||||||
solana_measure::measure::Measure,
|
solana_measure::measure::Measure,
|
||||||
solana_perf::packet::{Packet, PacketBatch},
|
solana_perf::packet::{Packet, PacketBatch},
|
||||||
|
solana_runtime::{
|
||||||
|
bank::Bank,
|
||||||
|
bank_forks::BankForks,
|
||||||
|
genesis_utils::{create_genesis_config, GenesisConfigInfo},
|
||||||
|
},
|
||||||
solana_sdk::{hash::Hash, signature::Keypair, system_transaction},
|
solana_sdk::{hash::Hash, signature::Keypair, system_transaction},
|
||||||
|
std::sync::{Arc, RwLock},
|
||||||
test::Bencher,
|
test::Bencher,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -174,3 +183,78 @@ fn bench_unprocessed_packet_batches_randomized_beyond_limit(bencher: &mut Benche
|
||||||
insert_packet_batches(buffer_capacity, batch_count, packet_per_batch_count, true);
|
insert_packet_batches(buffer_capacity, batch_count, packet_per_batch_count, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_bank_forks_for_test() -> Arc<RwLock<BankForks>> {
|
||||||
|
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
|
||||||
|
let bank = Bank::new_for_tests(&genesis_config);
|
||||||
|
let bank_forks = BankForks::new(bank);
|
||||||
|
Arc::new(RwLock::new(bank_forks))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn buffer_iter_desc_and_forward(
|
||||||
|
buffer_max_size: usize,
|
||||||
|
batch_count: usize,
|
||||||
|
packet_per_batch_count: usize,
|
||||||
|
randomize: bool,
|
||||||
|
) {
|
||||||
|
solana_logger::setup();
|
||||||
|
let mut unprocessed_packet_batches = UnprocessedPacketBatches::with_capacity(buffer_max_size);
|
||||||
|
|
||||||
|
// fill buffer
|
||||||
|
{
|
||||||
|
let mut timer = Measure::start("fill_buffer");
|
||||||
|
(0..batch_count).for_each(|_| {
|
||||||
|
let (packet_batch, packet_indexes) = if randomize {
|
||||||
|
build_randomized_packet_batch(packet_per_batch_count)
|
||||||
|
} else {
|
||||||
|
build_packet_batch(packet_per_batch_count)
|
||||||
|
};
|
||||||
|
let deserialized_packets = deserialize_packets(&packet_batch, &packet_indexes);
|
||||||
|
unprocessed_packet_batches.insert_batch(deserialized_packets);
|
||||||
|
});
|
||||||
|
timer.stop();
|
||||||
|
log::info!(
|
||||||
|
"inserted {} batch, elapsed {}",
|
||||||
|
buffer_max_size,
|
||||||
|
timer.as_us()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// forward whole buffer
|
||||||
|
{
|
||||||
|
let mut timer = Measure::start("forward_time");
|
||||||
|
let mut forward_packet_batches_by_accounts =
|
||||||
|
ForwardPacketBatchesByAccounts::new_with_default_batch_limits(
|
||||||
|
build_bank_forks_for_test().read().unwrap().root_bank(),
|
||||||
|
);
|
||||||
|
// iter_desc buffer
|
||||||
|
let filter_forwarding_results = BankingStage::filter_valid_packets_for_forwarding(
|
||||||
|
&mut unprocessed_packet_batches,
|
||||||
|
&mut forward_packet_batches_by_accounts,
|
||||||
|
);
|
||||||
|
timer.stop();
|
||||||
|
|
||||||
|
let batched_filter_forwarding_results: usize = forward_packet_batches_by_accounts
|
||||||
|
.iter_batches()
|
||||||
|
.map(|forward_batch| forward_batch.len())
|
||||||
|
.sum();
|
||||||
|
log::info!(
|
||||||
|
"filter_forwarding_results {:?}, batched_forwardable packets {}, elapsed {}",
|
||||||
|
filter_forwarding_results,
|
||||||
|
batched_filter_forwarding_results,
|
||||||
|
timer.as_us()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
#[ignore]
|
||||||
|
fn bench_forwarding_unprocessed_packet_batches(bencher: &mut Bencher) {
|
||||||
|
let batch_count = 1_000;
|
||||||
|
let packet_per_batch_count = 64;
|
||||||
|
let buffer_capacity = batch_count * packet_per_batch_count;
|
||||||
|
|
||||||
|
bencher.iter(|| {
|
||||||
|
buffer_iter_desc_and_forward(buffer_capacity, batch_count, packet_per_batch_count, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
//! can do its processing in parallel with signature verification on the GPU.
|
//! can do its processing in parallel with signature verification on the GPU.
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
|
forward_packet_batches_by_accounts::ForwardPacketBatchesByAccounts,
|
||||||
leader_slot_banking_stage_metrics::{LeaderSlotMetricsTracker, ProcessTransactionsSummary},
|
leader_slot_banking_stage_metrics::{LeaderSlotMetricsTracker, ProcessTransactionsSummary},
|
||||||
leader_slot_banking_stage_timing_metrics::{
|
leader_slot_banking_stage_timing_metrics::{
|
||||||
LeaderExecuteAndCommitTimings, RecordTransactionsTimings,
|
LeaderExecuteAndCommitTimings, RecordTransactionsTimings,
|
||||||
|
@ -37,6 +38,7 @@ use {
|
||||||
Bank, CommitTransactionCounts, LoadAndExecuteTransactionsOutput,
|
Bank, CommitTransactionCounts, LoadAndExecuteTransactionsOutput,
|
||||||
TransactionBalancesSet, TransactionCheckResult,
|
TransactionBalancesSet, TransactionCheckResult,
|
||||||
},
|
},
|
||||||
|
bank_forks::BankForks,
|
||||||
bank_utils,
|
bank_utils,
|
||||||
cost_model::{CostModel, TransactionCost},
|
cost_model::{CostModel, TransactionCost},
|
||||||
transaction_batch::TransactionBatch,
|
transaction_batch::TransactionBatch,
|
||||||
|
@ -48,13 +50,10 @@ use {
|
||||||
Slot, DEFAULT_TICKS_PER_SLOT, MAX_PROCESSING_AGE, MAX_TRANSACTION_FORWARDING_DELAY,
|
Slot, DEFAULT_TICKS_PER_SLOT, MAX_PROCESSING_AGE, MAX_TRANSACTION_FORWARDING_DELAY,
|
||||||
MAX_TRANSACTION_FORWARDING_DELAY_GPU,
|
MAX_TRANSACTION_FORWARDING_DELAY_GPU,
|
||||||
},
|
},
|
||||||
feature_set,
|
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
saturating_add_assign,
|
saturating_add_assign,
|
||||||
timing::{duration_as_ms, timestamp, AtomicInterval},
|
timing::{duration_as_ms, timestamp, AtomicInterval},
|
||||||
transaction::{
|
transaction::{self, SanitizedTransaction, TransactionError, VersionedTransaction},
|
||||||
self, AddressLoader, SanitizedTransaction, TransactionError, VersionedTransaction,
|
|
||||||
},
|
|
||||||
transport::TransportError,
|
transport::TransportError,
|
||||||
},
|
},
|
||||||
solana_streamer::sendmmsg::batch_send,
|
solana_streamer::sendmmsg::batch_send,
|
||||||
|
@ -151,7 +150,6 @@ pub struct BankingStageStats {
|
||||||
current_buffered_packet_batches_count: AtomicUsize,
|
current_buffered_packet_batches_count: AtomicUsize,
|
||||||
rebuffered_packets_count: AtomicUsize,
|
rebuffered_packets_count: AtomicUsize,
|
||||||
consumed_buffered_packets_count: AtomicUsize,
|
consumed_buffered_packets_count: AtomicUsize,
|
||||||
end_of_slot_filtered_invalid_count: AtomicUsize,
|
|
||||||
forwarded_transaction_count: AtomicUsize,
|
forwarded_transaction_count: AtomicUsize,
|
||||||
forwarded_vote_count: AtomicUsize,
|
forwarded_vote_count: AtomicUsize,
|
||||||
batch_packet_indexes_len: Histogram,
|
batch_packet_indexes_len: Histogram,
|
||||||
|
@ -162,7 +160,6 @@ pub struct BankingStageStats {
|
||||||
handle_retryable_packets_elapsed: AtomicU64,
|
handle_retryable_packets_elapsed: AtomicU64,
|
||||||
filter_pending_packets_elapsed: AtomicU64,
|
filter_pending_packets_elapsed: AtomicU64,
|
||||||
packet_conversion_elapsed: AtomicU64,
|
packet_conversion_elapsed: AtomicU64,
|
||||||
unprocessed_packet_conversion_elapsed: AtomicU64,
|
|
||||||
transaction_processing_elapsed: AtomicU64,
|
transaction_processing_elapsed: AtomicU64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,9 +201,6 @@ impl BankingStageStats {
|
||||||
.load(Ordering::Relaxed)
|
.load(Ordering::Relaxed)
|
||||||
+ self.filter_pending_packets_elapsed.load(Ordering::Relaxed)
|
+ self.filter_pending_packets_elapsed.load(Ordering::Relaxed)
|
||||||
+ self.packet_conversion_elapsed.load(Ordering::Relaxed)
|
+ self.packet_conversion_elapsed.load(Ordering::Relaxed)
|
||||||
+ self
|
|
||||||
.unprocessed_packet_conversion_elapsed
|
|
||||||
.load(Ordering::Relaxed)
|
|
||||||
+ self.transaction_processing_elapsed.load(Ordering::Relaxed)
|
+ self.transaction_processing_elapsed.load(Ordering::Relaxed)
|
||||||
+ self.forwarded_transaction_count.load(Ordering::Relaxed) as u64
|
+ self.forwarded_transaction_count.load(Ordering::Relaxed) as u64
|
||||||
+ self.forwarded_vote_count.load(Ordering::Relaxed) as u64
|
+ self.forwarded_vote_count.load(Ordering::Relaxed) as u64
|
||||||
|
@ -267,12 +261,6 @@ impl BankingStageStats {
|
||||||
.swap(0, Ordering::Relaxed) as i64,
|
.swap(0, Ordering::Relaxed) as i64,
|
||||||
i64
|
i64
|
||||||
),
|
),
|
||||||
(
|
|
||||||
"end_of_slot_filtered_invalid_count",
|
|
||||||
self.end_of_slot_filtered_invalid_count
|
|
||||||
.swap(0, Ordering::Relaxed) as i64,
|
|
||||||
i64
|
|
||||||
),
|
|
||||||
(
|
(
|
||||||
"forwarded_transaction_count",
|
"forwarded_transaction_count",
|
||||||
self.forwarded_transaction_count.swap(0, Ordering::Relaxed) as i64,
|
self.forwarded_transaction_count.swap(0, Ordering::Relaxed) as i64,
|
||||||
|
@ -312,12 +300,6 @@ impl BankingStageStats {
|
||||||
self.packet_conversion_elapsed.swap(0, Ordering::Relaxed) as i64,
|
self.packet_conversion_elapsed.swap(0, Ordering::Relaxed) as i64,
|
||||||
i64
|
i64
|
||||||
),
|
),
|
||||||
(
|
|
||||||
"unprocessed_packet_conversion_elapsed",
|
|
||||||
self.unprocessed_packet_conversion_elapsed
|
|
||||||
.swap(0, Ordering::Relaxed) as i64,
|
|
||||||
i64
|
|
||||||
),
|
|
||||||
(
|
(
|
||||||
"transaction_processing_elapsed",
|
"transaction_processing_elapsed",
|
||||||
self.transaction_processing_elapsed
|
self.transaction_processing_elapsed
|
||||||
|
@ -374,12 +356,6 @@ pub struct BatchedTransactionErrorDetails {
|
||||||
pub batched_dropped_txs_per_account_data_total_limit_count: u64,
|
pub batched_dropped_txs_per_account_data_total_limit_count: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
struct EndOfSlot {
|
|
||||||
next_slot_leader: Option<Pubkey>,
|
|
||||||
working_bank: Option<Arc<Bank>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stores the stage's thread handle and output receiver.
|
/// Stores the stage's thread handle and output receiver.
|
||||||
pub struct BankingStage {
|
pub struct BankingStage {
|
||||||
bank_thread_hdls: Vec<JoinHandle<()>>,
|
bank_thread_hdls: Vec<JoinHandle<()>>,
|
||||||
|
@ -400,8 +376,9 @@ pub enum ForwardOption {
|
||||||
ForwardTransaction,
|
ForwardTransaction,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FilterForwardingResults<'a> {
|
#[derive(Debug)]
|
||||||
forwardable_packets: Vec<&'a Packet>,
|
pub struct FilterForwardingResults {
|
||||||
|
total_forwardable_packets: usize,
|
||||||
total_tracer_packets_in_buffer: usize,
|
total_tracer_packets_in_buffer: usize,
|
||||||
total_forwardable_tracer_packets: usize,
|
total_forwardable_tracer_packets: usize,
|
||||||
}
|
}
|
||||||
|
@ -409,6 +386,7 @@ struct FilterForwardingResults<'a> {
|
||||||
impl BankingStage {
|
impl BankingStage {
|
||||||
/// Create the stage using `bank`. Exit when `verified_receiver` is dropped.
|
/// Create the stage using `bank`. Exit when `verified_receiver` is dropped.
|
||||||
#[allow(clippy::new_ret_no_self)]
|
#[allow(clippy::new_ret_no_self)]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
cluster_info: &Arc<ClusterInfo>,
|
cluster_info: &Arc<ClusterInfo>,
|
||||||
poh_recorder: &Arc<RwLock<PohRecorder>>,
|
poh_recorder: &Arc<RwLock<PohRecorder>>,
|
||||||
|
@ -419,6 +397,7 @@ impl BankingStage {
|
||||||
gossip_vote_sender: ReplayVoteSender,
|
gossip_vote_sender: ReplayVoteSender,
|
||||||
cost_model: Arc<RwLock<CostModel>>,
|
cost_model: Arc<RwLock<CostModel>>,
|
||||||
connection_cache: Arc<ConnectionCache>,
|
connection_cache: Arc<ConnectionCache>,
|
||||||
|
bank_forks: Arc<RwLock<BankForks>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new_num_threads(
|
Self::new_num_threads(
|
||||||
cluster_info,
|
cluster_info,
|
||||||
|
@ -431,6 +410,7 @@ impl BankingStage {
|
||||||
gossip_vote_sender,
|
gossip_vote_sender,
|
||||||
cost_model,
|
cost_model,
|
||||||
connection_cache,
|
connection_cache,
|
||||||
|
bank_forks,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -446,6 +426,7 @@ impl BankingStage {
|
||||||
gossip_vote_sender: ReplayVoteSender,
|
gossip_vote_sender: ReplayVoteSender,
|
||||||
cost_model: Arc<RwLock<CostModel>>,
|
cost_model: Arc<RwLock<CostModel>>,
|
||||||
connection_cache: Arc<ConnectionCache>,
|
connection_cache: Arc<ConnectionCache>,
|
||||||
|
bank_forks: Arc<RwLock<BankForks>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
assert!(num_threads >= MIN_TOTAL_THREADS);
|
assert!(num_threads >= MIN_TOTAL_THREADS);
|
||||||
// Single thread to generate entries from many banks.
|
// Single thread to generate entries from many banks.
|
||||||
|
@ -478,6 +459,7 @@ impl BankingStage {
|
||||||
let data_budget = data_budget.clone();
|
let data_budget = data_budget.clone();
|
||||||
let cost_model = cost_model.clone();
|
let cost_model = cost_model.clone();
|
||||||
let connection_cache = connection_cache.clone();
|
let connection_cache = connection_cache.clone();
|
||||||
|
let bank_forks = bank_forks.clone();
|
||||||
Builder::new()
|
Builder::new()
|
||||||
.name(format!("solana-banking-stage-tx-{}", i))
|
.name(format!("solana-banking-stage-tx-{}", i))
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
|
@ -494,6 +476,7 @@ impl BankingStage {
|
||||||
&data_budget,
|
&data_budget,
|
||||||
cost_model,
|
cost_model,
|
||||||
connection_cache,
|
connection_cache,
|
||||||
|
&bank_forks,
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -502,32 +485,53 @@ impl BankingStage {
|
||||||
Self { bank_thread_hdls }
|
Self { bank_thread_hdls }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn filter_valid_packets_for_forwarding<'a>(
|
// filter forwardable Rc<immutable_deserialized_packet>s that:
|
||||||
deserialized_packets: impl Iterator<Item = &'a DeserializedPacket>,
|
// 1. are not forwarded, and
|
||||||
) -> FilterForwardingResults<'a> {
|
// 2. in priority order from max to min, and
|
||||||
let mut total_forwardable_tracer_packets = 0;
|
// 3. not exceeding account bucket limit
|
||||||
let mut total_tracer_packets_in_buffer = 0;
|
// returns forwarded packets count
|
||||||
FilterForwardingResults {
|
pub fn filter_valid_packets_for_forwarding(
|
||||||
forwardable_packets: deserialized_packets
|
buffered_packet_batches: &mut UnprocessedPacketBatches,
|
||||||
.filter_map(|deserialized_packet| {
|
forward_packet_batches_by_accounts: &mut ForwardPacketBatchesByAccounts,
|
||||||
|
) -> FilterForwardingResults {
|
||||||
|
let mut total_forwardable_tracer_packets: usize = 0;
|
||||||
|
let mut total_tracer_packets_in_buffer: usize = 0;
|
||||||
|
let mut total_forwardable_packets: usize = 0;
|
||||||
|
let mut dropped_tx_before_forwarding_count: usize = 0;
|
||||||
|
|
||||||
|
let filter_forwardable_packet = |deserialized_packet: &mut DeserializedPacket| -> bool {
|
||||||
|
let mut result = true;
|
||||||
let is_tracer_packet = deserialized_packet
|
let is_tracer_packet = deserialized_packet
|
||||||
.immutable_section()
|
.immutable_section()
|
||||||
.original_packet()
|
.original_packet()
|
||||||
.meta
|
.meta
|
||||||
.is_tracer_packet();
|
.is_tracer_packet();
|
||||||
if is_tracer_packet {
|
if is_tracer_packet {
|
||||||
total_tracer_packets_in_buffer += 1;
|
saturating_add_assign!(total_tracer_packets_in_buffer, 1);
|
||||||
}
|
}
|
||||||
if !deserialized_packet.forwarded {
|
if !deserialized_packet.forwarded {
|
||||||
|
saturating_add_assign!(total_forwardable_packets, 1);
|
||||||
if is_tracer_packet {
|
if is_tracer_packet {
|
||||||
total_forwardable_tracer_packets += 1;
|
saturating_add_assign!(total_forwardable_tracer_packets, 1);
|
||||||
}
|
}
|
||||||
Some(deserialized_packet.immutable_section().original_packet())
|
result = forward_packet_batches_by_accounts
|
||||||
} else {
|
.add_packet(deserialized_packet.immutable_section().clone());
|
||||||
None
|
if !result {
|
||||||
|
saturating_add_assign!(dropped_tx_before_forwarding_count, 1);
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.collect(),
|
result
|
||||||
|
};
|
||||||
|
|
||||||
|
buffered_packet_batches.iter_desc(filter_forwardable_packet);
|
||||||
|
|
||||||
|
inc_new_counter_info!(
|
||||||
|
"banking_stage-dropped_tx_before_forwarding",
|
||||||
|
dropped_tx_before_forwarding_count
|
||||||
|
);
|
||||||
|
|
||||||
|
FilterForwardingResults {
|
||||||
|
total_forwardable_packets,
|
||||||
total_tracer_packets_in_buffer,
|
total_tracer_packets_in_buffer,
|
||||||
total_forwardable_tracer_packets,
|
total_forwardable_tracer_packets,
|
||||||
}
|
}
|
||||||
|
@ -535,19 +539,22 @@ impl BankingStage {
|
||||||
|
|
||||||
/// Forwards all valid, unprocessed packets in the buffer, up to a rate limit. Returns
|
/// Forwards all valid, unprocessed packets in the buffer, up to a rate limit. Returns
|
||||||
/// the number of successfully forwarded packets in second part of tuple
|
/// the number of successfully forwarded packets in second part of tuple
|
||||||
fn forward_buffered_packets(
|
fn forward_buffered_packets<'a>(
|
||||||
connection_cache: &ConnectionCache,
|
connection_cache: &ConnectionCache,
|
||||||
forward_option: &ForwardOption,
|
forward_option: &ForwardOption,
|
||||||
cluster_info: &ClusterInfo,
|
cluster_info: &ClusterInfo,
|
||||||
poh_recorder: &Arc<RwLock<PohRecorder>>,
|
poh_recorder: &Arc<RwLock<PohRecorder>>,
|
||||||
socket: &UdpSocket,
|
socket: &UdpSocket,
|
||||||
filter_forwarding_results: &FilterForwardingResults,
|
forwardable_packets: impl Iterator<Item = &'a Packet>,
|
||||||
data_budget: &DataBudget,
|
data_budget: &DataBudget,
|
||||||
banking_stage_stats: &BankingStageStats,
|
banking_stage_stats: &BankingStageStats,
|
||||||
tracer_packet_stats: &mut TracerPacketStats,
|
) -> (
|
||||||
) -> (std::result::Result<(), TransportError>, usize) {
|
std::result::Result<(), TransportError>,
|
||||||
|
usize,
|
||||||
|
Option<Pubkey>,
|
||||||
|
) {
|
||||||
let leader_and_addr = match forward_option {
|
let leader_and_addr = match forward_option {
|
||||||
ForwardOption::NotForward => return (Ok(()), 0),
|
ForwardOption::NotForward => return (Ok(()), 0, None),
|
||||||
ForwardOption::ForwardTransaction => {
|
ForwardOption::ForwardTransaction => {
|
||||||
next_leader_tpu_forwards(cluster_info, poh_recorder)
|
next_leader_tpu_forwards(cluster_info, poh_recorder)
|
||||||
}
|
}
|
||||||
|
@ -556,20 +563,9 @@ impl BankingStage {
|
||||||
};
|
};
|
||||||
let (leader_pubkey, addr) = match leader_and_addr {
|
let (leader_pubkey, addr) = match leader_and_addr {
|
||||||
Some(leader_and_addr) => leader_and_addr,
|
Some(leader_and_addr) => leader_and_addr,
|
||||||
None => return (Ok(()), 0),
|
None => return (Ok(()), 0, None),
|
||||||
};
|
};
|
||||||
|
|
||||||
let FilterForwardingResults {
|
|
||||||
forwardable_packets,
|
|
||||||
total_forwardable_tracer_packets,
|
|
||||||
..
|
|
||||||
} = filter_forwarding_results;
|
|
||||||
|
|
||||||
tracer_packet_stats.increment_total_forwardable_tracer_packets(
|
|
||||||
*total_forwardable_tracer_packets,
|
|
||||||
leader_pubkey,
|
|
||||||
);
|
|
||||||
|
|
||||||
const INTERVAL_MS: u64 = 100;
|
const INTERVAL_MS: u64 = 100;
|
||||||
const MAX_BYTES_PER_SECOND: usize = 10_000 * 1200;
|
const MAX_BYTES_PER_SECOND: usize = 10_000 * 1200;
|
||||||
const MAX_BYTES_PER_INTERVAL: usize = MAX_BYTES_PER_SECOND * INTERVAL_MS as usize / 1000;
|
const MAX_BYTES_PER_INTERVAL: usize = MAX_BYTES_PER_SECOND * INTERVAL_MS as usize / 1000;
|
||||||
|
@ -582,7 +578,6 @@ impl BankingStage {
|
||||||
});
|
});
|
||||||
|
|
||||||
let packet_vec: Vec<_> = forwardable_packets
|
let packet_vec: Vec<_> = forwardable_packets
|
||||||
.iter()
|
|
||||||
.filter_map(|p| {
|
.filter_map(|p| {
|
||||||
if !p.meta.forwarded() && data_budget.take(p.meta.size) {
|
if !p.meta.forwarded() && data_budget.take(p.meta.size) {
|
||||||
Some(p.data(..)?.to_vec())
|
Some(p.data(..)?.to_vec())
|
||||||
|
@ -629,16 +624,16 @@ impl BankingStage {
|
||||||
|
|
||||||
if let Err(err) = res {
|
if let Err(err) = res {
|
||||||
inc_new_counter_info!("banking_stage-forward_packets-failed-batches", 1);
|
inc_new_counter_info!("banking_stage-forward_packets-failed-batches", 1);
|
||||||
return (Err(err), 0);
|
return (Err(err), 0, Some(leader_pubkey));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(Ok(()), packet_vec_len)
|
(Ok(()), packet_vec_len, Some(leader_pubkey))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn consume_buffered_packets(
|
pub fn consume_buffered_packets(
|
||||||
my_pubkey: &Pubkey,
|
_my_pubkey: &Pubkey,
|
||||||
max_tx_ingestion_ns: u128,
|
max_tx_ingestion_ns: u128,
|
||||||
poh_recorder: &Arc<RwLock<PohRecorder>>,
|
poh_recorder: &Arc<RwLock<PohRecorder>>,
|
||||||
buffered_packet_batches: &mut UnprocessedPacketBatches,
|
buffered_packet_batches: &mut UnprocessedPacketBatches,
|
||||||
|
@ -655,7 +650,7 @@ impl BankingStage {
|
||||||
let mut consumed_buffered_packets_count = 0;
|
let mut consumed_buffered_packets_count = 0;
|
||||||
let buffered_packets_len = buffered_packet_batches.len();
|
let buffered_packets_len = buffered_packet_batches.len();
|
||||||
let mut proc_start = Measure::start("consume_buffered_process");
|
let mut proc_start = Measure::start("consume_buffered_process");
|
||||||
let mut reached_end_of_slot: Option<EndOfSlot> = None;
|
let mut reached_end_of_slot = false;
|
||||||
|
|
||||||
let mut retryable_packets = MinMaxHeap::with_capacity(buffered_packet_batches.capacity());
|
let mut retryable_packets = MinMaxHeap::with_capacity(buffered_packet_batches.capacity());
|
||||||
std::mem::swap(
|
std::mem::swap(
|
||||||
|
@ -717,13 +712,10 @@ impl BankingStage {
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
let poh_recorder_lock_time = {
|
let poh_recorder_lock_time = {
|
||||||
let (poh_recorder_locked, poh_recorder_lock_time) =
|
let (_poh_recorder_locked, poh_recorder_lock_time) =
|
||||||
measure!(poh_recorder.read().unwrap(), "poh_recorder.read");
|
measure!(poh_recorder.read().unwrap(), "poh_recorder.read");
|
||||||
|
|
||||||
reached_end_of_slot = Some(EndOfSlot {
|
reached_end_of_slot = true;
|
||||||
next_slot_leader: poh_recorder_locked.next_slot_leader(),
|
|
||||||
working_bank: Some(working_bank),
|
|
||||||
});
|
|
||||||
poh_recorder_lock_time
|
poh_recorder_lock_time
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -776,19 +768,16 @@ impl BankingStage {
|
||||||
);
|
);
|
||||||
|
|
||||||
result
|
result
|
||||||
} else if reached_end_of_slot.is_some() {
|
} else if reached_end_of_slot {
|
||||||
packets_to_process
|
packets_to_process
|
||||||
} else {
|
} else {
|
||||||
// mark as end-of-slot to avoid aggressively lock poh for the remaining for
|
// mark as end-of-slot to avoid aggressively lock poh for the remaining for
|
||||||
// packet batches in buffer
|
// packet batches in buffer
|
||||||
let poh_recorder_lock_time = {
|
let poh_recorder_lock_time = {
|
||||||
let (poh_recorder_locked, poh_recorder_lock_time) =
|
let (_poh_recorder_locked, poh_recorder_lock_time) =
|
||||||
measure!(poh_recorder.read().unwrap(), "poh_recorder.read");
|
measure!(poh_recorder.read().unwrap(), "poh_recorder.read");
|
||||||
|
|
||||||
reached_end_of_slot = Some(EndOfSlot {
|
reached_end_of_slot = true;
|
||||||
next_slot_leader: poh_recorder_locked.next_slot_leader(),
|
|
||||||
working_bank: None,
|
|
||||||
});
|
|
||||||
poh_recorder_lock_time
|
poh_recorder_lock_time
|
||||||
};
|
};
|
||||||
slot_metrics_tracker.increment_consume_buffered_packets_poh_recorder_lock_us(
|
slot_metrics_tracker.increment_consume_buffered_packets_poh_recorder_lock_us(
|
||||||
|
@ -805,43 +794,12 @@ impl BankingStage {
|
||||||
&mut buffered_packet_batches.packet_priority_queue,
|
&mut buffered_packet_batches.packet_priority_queue,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(end_of_slot) = &reached_end_of_slot {
|
if reached_end_of_slot {
|
||||||
slot_metrics_tracker
|
slot_metrics_tracker
|
||||||
.set_end_of_slot_unprocessed_buffer_len(buffered_packet_batches.len() as u64);
|
.set_end_of_slot_unprocessed_buffer_len(buffered_packet_batches.len() as u64);
|
||||||
|
|
||||||
// We've hit the end of this slot, no need to perform more processing,
|
// We've hit the end of this slot, no need to perform more processing,
|
||||||
// just filter the remaining packets for the invalid (e.g. too old) ones
|
// Packet filtering will be done at `forward_packet_batches_by_accounts.add_packet()`
|
||||||
// if the working_bank is available
|
|
||||||
let mut end_of_slot_filtering_time = Measure::start("end_of_slot_filtering");
|
|
||||||
// TODO: This doesn't have to be done at the end of every slot, can instead
|
|
||||||
// hold multiple unbuffered queues without merging them
|
|
||||||
|
|
||||||
// TODO: update this here to filter the rest of the packets remaining
|
|
||||||
// TODO: this needs to be done even if there is no end_of_slot.working_bank
|
|
||||||
// to put retryable packets back in buffer
|
|
||||||
let end_of_slot_filtered_invalid_count =
|
|
||||||
Self::filter_unprocessed_packets_at_end_of_slot(
|
|
||||||
&end_of_slot.working_bank,
|
|
||||||
buffered_packet_batches,
|
|
||||||
my_pubkey,
|
|
||||||
end_of_slot.next_slot_leader,
|
|
||||||
banking_stage_stats,
|
|
||||||
);
|
|
||||||
|
|
||||||
inc_new_counter_info!(
|
|
||||||
"banking_stage-dropped_tx_before_forwarding",
|
|
||||||
end_of_slot_filtered_invalid_count
|
|
||||||
);
|
|
||||||
slot_metrics_tracker.increment_end_of_slot_filtered_invalid_count(
|
|
||||||
end_of_slot_filtered_invalid_count as u64,
|
|
||||||
);
|
|
||||||
banking_stage_stats
|
|
||||||
.end_of_slot_filtered_invalid_count
|
|
||||||
.fetch_add(end_of_slot_filtered_invalid_count, Ordering::Relaxed);
|
|
||||||
|
|
||||||
end_of_slot_filtering_time.stop();
|
|
||||||
slot_metrics_tracker
|
|
||||||
.increment_end_of_slot_filtering_us(end_of_slot_filtering_time.as_us());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
proc_start.stop();
|
proc_start.stop();
|
||||||
|
@ -920,6 +878,7 @@ impl BankingStage {
|
||||||
slot_metrics_tracker: &mut LeaderSlotMetricsTracker,
|
slot_metrics_tracker: &mut LeaderSlotMetricsTracker,
|
||||||
connection_cache: &ConnectionCache,
|
connection_cache: &ConnectionCache,
|
||||||
tracer_packet_stats: &mut TracerPacketStats,
|
tracer_packet_stats: &mut TracerPacketStats,
|
||||||
|
bank_forks: &Arc<RwLock<BankForks>>,
|
||||||
) {
|
) {
|
||||||
let ((metrics_action, decision), make_decision_time) = measure!(
|
let ((metrics_action, decision), make_decision_time) = measure!(
|
||||||
{
|
{
|
||||||
|
@ -999,6 +958,7 @@ impl BankingStage {
|
||||||
banking_stage_stats,
|
banking_stage_stats,
|
||||||
connection_cache,
|
connection_cache,
|
||||||
tracer_packet_stats,
|
tracer_packet_stats,
|
||||||
|
bank_forks,
|
||||||
),
|
),
|
||||||
"forward",
|
"forward",
|
||||||
);
|
);
|
||||||
|
@ -1021,6 +981,7 @@ impl BankingStage {
|
||||||
banking_stage_stats,
|
banking_stage_stats,
|
||||||
connection_cache,
|
connection_cache,
|
||||||
tracer_packet_stats,
|
tracer_packet_stats,
|
||||||
|
bank_forks,
|
||||||
),
|
),
|
||||||
"forward_and_hold",
|
"forward_and_hold",
|
||||||
);
|
);
|
||||||
|
@ -1045,6 +1006,7 @@ impl BankingStage {
|
||||||
banking_stage_stats: &BankingStageStats,
|
banking_stage_stats: &BankingStageStats,
|
||||||
connection_cache: &ConnectionCache,
|
connection_cache: &ConnectionCache,
|
||||||
tracer_packet_stats: &mut TracerPacketStats,
|
tracer_packet_stats: &mut TracerPacketStats,
|
||||||
|
bank_forks: &Arc<RwLock<BankForks>>,
|
||||||
) {
|
) {
|
||||||
if let ForwardOption::NotForward = forward_option {
|
if let ForwardOption::NotForward = forward_option {
|
||||||
if !hold {
|
if !hold {
|
||||||
|
@ -1053,27 +1015,47 @@ impl BankingStage {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let filter_forwarding_result =
|
// get current root bank from bank_forks, use it to sanitize transaction and
|
||||||
Self::filter_valid_packets_for_forwarding(buffered_packet_batches.iter());
|
// load all accounts from address loader;
|
||||||
|
let current_bank = bank_forks.read().unwrap().root_bank();
|
||||||
|
let mut forward_packet_batches_by_accounts =
|
||||||
|
ForwardPacketBatchesByAccounts::new_with_default_batch_limits(current_bank);
|
||||||
|
let filter_forwarding_result = Self::filter_valid_packets_for_forwarding(
|
||||||
|
buffered_packet_batches,
|
||||||
|
&mut forward_packet_batches_by_accounts,
|
||||||
|
);
|
||||||
|
forward_packet_batches_by_accounts
|
||||||
|
.iter_batches()
|
||||||
|
.filter(|&batch| !batch.is_empty())
|
||||||
|
.for_each(|forward_batch| {
|
||||||
|
slot_metrics_tracker.increment_forwardable_batches_count(1);
|
||||||
|
|
||||||
let forwardable_packets_len = filter_forwarding_result.forwardable_packets.len();
|
let batched_forwardable_packets_count = forward_batch.len();
|
||||||
let (_forward_result, sucessful_forwarded_packets_count) = Self::forward_buffered_packets(
|
let (_forward_result, sucessful_forwarded_packets_count, leader_pubkey) =
|
||||||
|
Self::forward_buffered_packets(
|
||||||
connection_cache,
|
connection_cache,
|
||||||
forward_option,
|
forward_option,
|
||||||
cluster_info,
|
cluster_info,
|
||||||
poh_recorder,
|
poh_recorder,
|
||||||
socket,
|
socket,
|
||||||
&filter_forwarding_result,
|
forward_batch.get_forwardable_packets(),
|
||||||
data_budget,
|
data_budget,
|
||||||
banking_stage_stats,
|
banking_stage_stats,
|
||||||
tracer_packet_stats,
|
|
||||||
);
|
);
|
||||||
let failed_forwarded_packets_count =
|
|
||||||
forwardable_packets_len.saturating_sub(sucessful_forwarded_packets_count);
|
if let Some(leader_pubkey) = leader_pubkey {
|
||||||
|
tracer_packet_stats.increment_total_forwardable_tracer_packets(
|
||||||
|
filter_forwarding_result.total_forwardable_tracer_packets,
|
||||||
|
leader_pubkey,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let failed_forwarded_packets_count = batched_forwardable_packets_count
|
||||||
|
.saturating_sub(sucessful_forwarded_packets_count);
|
||||||
|
|
||||||
if failed_forwarded_packets_count > 0 {
|
if failed_forwarded_packets_count > 0 {
|
||||||
slot_metrics_tracker
|
slot_metrics_tracker.increment_failed_forwarded_packets_count(
|
||||||
.increment_failed_forwarded_packets_count(failed_forwarded_packets_count as u64);
|
failed_forwarded_packets_count as u64,
|
||||||
|
);
|
||||||
slot_metrics_tracker.increment_packet_batch_forward_failure_count(1);
|
slot_metrics_tracker.increment_packet_batch_forward_failure_count(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1082,14 +1064,16 @@ impl BankingStage {
|
||||||
sucessful_forwarded_packets_count as u64,
|
sucessful_forwarded_packets_count as u64,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if hold {
|
if hold {
|
||||||
for deserialized_packet in buffered_packet_batches.iter_mut() {
|
for deserialized_packet in buffered_packet_batches.iter_mut() {
|
||||||
deserialized_packet.forwarded = true;
|
deserialized_packet.forwarded = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
slot_metrics_tracker
|
slot_metrics_tracker.increment_cleared_from_buffer_after_forward_count(
|
||||||
.increment_cleared_from_buffer_after_forward_count(forwardable_packets_len as u64);
|
filter_forwarding_result.total_forwardable_packets as u64,
|
||||||
|
);
|
||||||
tracer_packet_stats.increment_total_cleared_from_buffer_after_forward(
|
tracer_packet_stats.increment_total_cleared_from_buffer_after_forward(
|
||||||
filter_forwarding_result.total_tracer_packets_in_buffer,
|
filter_forwarding_result.total_tracer_packets_in_buffer,
|
||||||
);
|
);
|
||||||
|
@ -1111,6 +1095,7 @@ impl BankingStage {
|
||||||
data_budget: &DataBudget,
|
data_budget: &DataBudget,
|
||||||
cost_model: Arc<RwLock<CostModel>>,
|
cost_model: Arc<RwLock<CostModel>>,
|
||||||
connection_cache: Arc<ConnectionCache>,
|
connection_cache: Arc<ConnectionCache>,
|
||||||
|
bank_forks: &Arc<RwLock<BankForks>>,
|
||||||
) {
|
) {
|
||||||
let recorder = poh_recorder.read().unwrap().recorder();
|
let recorder = poh_recorder.read().unwrap().recorder();
|
||||||
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
|
let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
|
||||||
|
@ -1144,6 +1129,7 @@ impl BankingStage {
|
||||||
&mut slot_metrics_tracker,
|
&mut slot_metrics_tracker,
|
||||||
&connection_cache,
|
&connection_cache,
|
||||||
&mut tracer_packet_stats,
|
&mut tracer_packet_stats,
|
||||||
|
bank_forks,
|
||||||
),
|
),
|
||||||
"process_buffered_packets",
|
"process_buffered_packets",
|
||||||
);
|
);
|
||||||
|
@ -1831,31 +1817,6 @@ impl BankingStage {
|
||||||
.collect_vec()
|
.collect_vec()
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function deserializes packets into transactions, computes the blake3 hash of transaction
|
|
||||||
// messages, and verifies secp256k1 instructions. A list of sanitized transactions are returned
|
|
||||||
// with their packet indexes.
|
|
||||||
#[allow(clippy::needless_collect)]
|
|
||||||
fn transaction_from_deserialized_packet(
|
|
||||||
deserialized_packet: &ImmutableDeserializedPacket,
|
|
||||||
feature_set: &Arc<feature_set::FeatureSet>,
|
|
||||||
votes_only: bool,
|
|
||||||
address_loader: impl AddressLoader,
|
|
||||||
) -> Option<SanitizedTransaction> {
|
|
||||||
if votes_only && !deserialized_packet.is_simple_vote() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let tx = SanitizedTransaction::try_new(
|
|
||||||
deserialized_packet.transaction().clone(),
|
|
||||||
*deserialized_packet.message_hash(),
|
|
||||||
deserialized_packet.is_simple_vote(),
|
|
||||||
address_loader,
|
|
||||||
)
|
|
||||||
.ok()?;
|
|
||||||
tx.verify_precompiles(feature_set).ok()?;
|
|
||||||
Some(tx)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This function filters pending packets that are still valid
|
/// This function filters pending packets that are still valid
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `transactions` - a batch of transactions deserialized from packets
|
/// * `transactions` - a batch of transactions deserialized from packets
|
||||||
|
@ -1934,7 +1895,7 @@ impl BankingStage {
|
||||||
deserialized_packets
|
deserialized_packets
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(i, deserialized_packet)| {
|
.filter_map(|(i, deserialized_packet)| {
|
||||||
Self::transaction_from_deserialized_packet(
|
unprocessed_packet_batches::transaction_from_deserialized_packet(
|
||||||
deserialized_packet,
|
deserialized_packet,
|
||||||
&bank.feature_set,
|
&bank.feature_set,
|
||||||
bank.vote_only_bank(),
|
bank.vote_only_bank(),
|
||||||
|
@ -2019,54 +1980,6 @@ impl BankingStage {
|
||||||
process_transactions_summary
|
process_transactions_summary
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the number of packets that were filtered out for
|
|
||||||
// no longer being valid (could be too old, a duplicate of something
|
|
||||||
// already processed, etc.)
|
|
||||||
fn filter_unprocessed_packets_at_end_of_slot(
|
|
||||||
bank: &Option<Arc<Bank>>,
|
|
||||||
unprocessed_packets: &mut UnprocessedPacketBatches,
|
|
||||||
my_pubkey: &Pubkey,
|
|
||||||
next_leader: Option<Pubkey>,
|
|
||||||
banking_stage_stats: &BankingStageStats,
|
|
||||||
) -> usize {
|
|
||||||
// Check if we are the next leader. If so, let's not filter the packets
|
|
||||||
// as we'll filter it again while processing the packets.
|
|
||||||
// Filtering helps if we were going to forward the packets to some other node
|
|
||||||
let will_still_be_leader = next_leader
|
|
||||||
.map(|next_leader| next_leader == *my_pubkey)
|
|
||||||
.unwrap_or(false);
|
|
||||||
let should_filter_unprocessed_packets = !will_still_be_leader && bank.is_some();
|
|
||||||
let original_unprocessed_packets_len = unprocessed_packets.len();
|
|
||||||
|
|
||||||
if should_filter_unprocessed_packets {
|
|
||||||
// If `should_filter_unprocessed_packets` is true, then the bank
|
|
||||||
// must be `Some`
|
|
||||||
let bank = bank.as_ref().unwrap();
|
|
||||||
let mut unprocessed_packet_conversion_time =
|
|
||||||
Measure::start("unprocessed_packet_conversion");
|
|
||||||
|
|
||||||
let should_retain = |deserialized_packet: &mut DeserializedPacket| {
|
|
||||||
Self::transaction_from_deserialized_packet(
|
|
||||||
deserialized_packet.immutable_section(),
|
|
||||||
&bank.feature_set,
|
|
||||||
bank.vote_only_bank(),
|
|
||||||
bank.as_ref(),
|
|
||||||
)
|
|
||||||
.is_some()
|
|
||||||
};
|
|
||||||
unprocessed_packets.retain(should_retain);
|
|
||||||
unprocessed_packet_conversion_time.stop();
|
|
||||||
banking_stage_stats
|
|
||||||
.unprocessed_packet_conversion_elapsed
|
|
||||||
.fetch_add(
|
|
||||||
unprocessed_packet_conversion_time.as_us(),
|
|
||||||
Ordering::Relaxed,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
original_unprocessed_packets_len.saturating_sub(unprocessed_packets.len())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_packet_indexes(vers: &PacketBatch) -> Vec<usize> {
|
fn generate_packet_indexes(vers: &PacketBatch) -> Vec<usize> {
|
||||||
vers.iter()
|
vers.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
|
@ -2298,6 +2211,7 @@ mod tests {
|
||||||
},
|
},
|
||||||
solana_program_runtime::timings::ProgramTiming,
|
solana_program_runtime::timings::ProgramTiming,
|
||||||
solana_rpc::transaction_status_service::TransactionStatusService,
|
solana_rpc::transaction_status_service::TransactionStatusService,
|
||||||
|
solana_runtime::bank_forks::BankForks,
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
account::AccountSharedData,
|
account::AccountSharedData,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
|
@ -2309,14 +2223,10 @@ mod tests {
|
||||||
poh_config::PohConfig,
|
poh_config::PohConfig,
|
||||||
signature::{Keypair, Signer},
|
signature::{Keypair, Signer},
|
||||||
system_transaction,
|
system_transaction,
|
||||||
transaction::{
|
transaction::{MessageHash, Transaction, TransactionError, VersionedTransaction},
|
||||||
MessageHash, SimpleAddressLoader, Transaction, TransactionError,
|
|
||||||
VersionedTransaction,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
solana_streamer::{recvmmsg::recv_mmsg, socket::SocketAddrSpace},
|
solana_streamer::{recvmmsg::recv_mmsg, socket::SocketAddrSpace},
|
||||||
solana_transaction_status::{TransactionStatusMeta, VersionedTransactionWithStatusMeta},
|
solana_transaction_status::{TransactionStatusMeta, VersionedTransactionWithStatusMeta},
|
||||||
solana_vote_program::vote_transaction,
|
|
||||||
std::{
|
std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
collections::HashSet,
|
collections::HashSet,
|
||||||
|
@ -2338,7 +2248,9 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_banking_stage_shutdown1() {
|
fn test_banking_stage_shutdown1() {
|
||||||
let genesis_config = create_genesis_config(2).genesis_config;
|
let genesis_config = create_genesis_config(2).genesis_config;
|
||||||
let bank = Arc::new(Bank::new_no_wallclock_throttle_for_tests(&genesis_config));
|
let bank = Bank::new_no_wallclock_throttle_for_tests(&genesis_config);
|
||||||
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
|
||||||
|
let bank = Arc::new(bank_forks.read().unwrap().get(0).unwrap());
|
||||||
let (verified_sender, verified_receiver) = unbounded();
|
let (verified_sender, verified_receiver) = unbounded();
|
||||||
let (gossip_verified_vote_sender, gossip_verified_vote_receiver) = unbounded();
|
let (gossip_verified_vote_sender, gossip_verified_vote_receiver) = unbounded();
|
||||||
let (tpu_vote_sender, tpu_vote_receiver) = unbounded();
|
let (tpu_vote_sender, tpu_vote_receiver) = unbounded();
|
||||||
|
@ -2364,6 +2276,7 @@ mod tests {
|
||||||
gossip_vote_sender,
|
gossip_vote_sender,
|
||||||
Arc::new(RwLock::new(CostModel::default())),
|
Arc::new(RwLock::new(CostModel::default())),
|
||||||
Arc::new(ConnectionCache::default()),
|
Arc::new(ConnectionCache::default()),
|
||||||
|
bank_forks,
|
||||||
);
|
);
|
||||||
drop(verified_sender);
|
drop(verified_sender);
|
||||||
drop(gossip_verified_vote_sender);
|
drop(gossip_verified_vote_sender);
|
||||||
|
@ -2383,7 +2296,9 @@ mod tests {
|
||||||
} = create_genesis_config(2);
|
} = create_genesis_config(2);
|
||||||
genesis_config.ticks_per_slot = 4;
|
genesis_config.ticks_per_slot = 4;
|
||||||
let num_extra_ticks = 2;
|
let num_extra_ticks = 2;
|
||||||
let bank = Arc::new(Bank::new_no_wallclock_throttle_for_tests(&genesis_config));
|
let bank = Bank::new_no_wallclock_throttle_for_tests(&genesis_config);
|
||||||
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
|
||||||
|
let bank = Arc::new(bank_forks.read().unwrap().get(0).unwrap());
|
||||||
let start_hash = bank.last_blockhash();
|
let start_hash = bank.last_blockhash();
|
||||||
let (verified_sender, verified_receiver) = unbounded();
|
let (verified_sender, verified_receiver) = unbounded();
|
||||||
let (tpu_vote_sender, tpu_vote_receiver) = unbounded();
|
let (tpu_vote_sender, tpu_vote_receiver) = unbounded();
|
||||||
|
@ -2414,6 +2329,7 @@ mod tests {
|
||||||
gossip_vote_sender,
|
gossip_vote_sender,
|
||||||
Arc::new(RwLock::new(CostModel::default())),
|
Arc::new(RwLock::new(CostModel::default())),
|
||||||
Arc::new(ConnectionCache::default()),
|
Arc::new(ConnectionCache::default()),
|
||||||
|
bank_forks,
|
||||||
);
|
);
|
||||||
trace!("sending bank");
|
trace!("sending bank");
|
||||||
drop(verified_sender);
|
drop(verified_sender);
|
||||||
|
@ -2456,7 +2372,9 @@ mod tests {
|
||||||
mint_keypair,
|
mint_keypair,
|
||||||
..
|
..
|
||||||
} = create_slow_genesis_config(10);
|
} = create_slow_genesis_config(10);
|
||||||
let bank = Arc::new(Bank::new_no_wallclock_throttle_for_tests(&genesis_config));
|
let bank = Bank::new_no_wallclock_throttle_for_tests(&genesis_config);
|
||||||
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
|
||||||
|
let bank = Arc::new(bank_forks.read().unwrap().get(0).unwrap());
|
||||||
let start_hash = bank.last_blockhash();
|
let start_hash = bank.last_blockhash();
|
||||||
let (verified_sender, verified_receiver) = unbounded();
|
let (verified_sender, verified_receiver) = unbounded();
|
||||||
let (tpu_vote_sender, tpu_vote_receiver) = unbounded();
|
let (tpu_vote_sender, tpu_vote_receiver) = unbounded();
|
||||||
|
@ -2489,6 +2407,7 @@ mod tests {
|
||||||
gossip_vote_sender,
|
gossip_vote_sender,
|
||||||
Arc::new(RwLock::new(CostModel::default())),
|
Arc::new(RwLock::new(CostModel::default())),
|
||||||
Arc::new(ConnectionCache::default()),
|
Arc::new(ConnectionCache::default()),
|
||||||
|
bank_forks,
|
||||||
);
|
);
|
||||||
|
|
||||||
// fund another account so we can send 2 good transactions in a single batch.
|
// fund another account so we can send 2 good transactions in a single batch.
|
||||||
|
@ -2615,7 +2534,9 @@ mod tests {
|
||||||
|
|
||||||
let entry_receiver = {
|
let entry_receiver = {
|
||||||
// start a banking_stage to eat verified receiver
|
// start a banking_stage to eat verified receiver
|
||||||
let bank = Arc::new(Bank::new_no_wallclock_throttle_for_tests(&genesis_config));
|
let bank = Bank::new_no_wallclock_throttle_for_tests(&genesis_config);
|
||||||
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
|
||||||
|
let bank = Arc::new(bank_forks.read().unwrap().get(0).unwrap());
|
||||||
let blockstore = Arc::new(
|
let blockstore = Arc::new(
|
||||||
Blockstore::open(ledger_path.path())
|
Blockstore::open(ledger_path.path())
|
||||||
.expect("Expected to be able to open database ledger"),
|
.expect("Expected to be able to open database ledger"),
|
||||||
|
@ -2641,6 +2562,7 @@ mod tests {
|
||||||
gossip_vote_sender,
|
gossip_vote_sender,
|
||||||
Arc::new(RwLock::new(CostModel::default())),
|
Arc::new(RwLock::new(CostModel::default())),
|
||||||
Arc::new(ConnectionCache::default()),
|
Arc::new(ConnectionCache::default()),
|
||||||
|
bank_forks,
|
||||||
);
|
);
|
||||||
|
|
||||||
// wait for banking_stage to eat the packets
|
// wait for banking_stage to eat the packets
|
||||||
|
@ -3337,6 +3259,11 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_filter_valid_packets() {
|
fn test_filter_valid_packets() {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
|
let GenesisConfigInfo { genesis_config, .. } = create_slow_genesis_config(10);
|
||||||
|
let bank = Bank::new_no_wallclock_throttle_for_tests(&genesis_config);
|
||||||
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
|
||||||
|
let current_bank = bank_forks.read().unwrap().root_bank();
|
||||||
|
|
||||||
let mut packets: Vec<DeserializedPacket> = (0..256)
|
let mut packets: Vec<DeserializedPacket> = (0..256)
|
||||||
.map(|packets_id| {
|
.map(|packets_id| {
|
||||||
// packets are deserialized upon receiving, failed packets will not be
|
// packets are deserialized upon receiving, failed packets will not be
|
||||||
|
@ -3352,36 +3279,61 @@ mod tests {
|
||||||
})
|
})
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
|
|
||||||
|
// all packets are forwarded
|
||||||
|
{
|
||||||
|
let mut buffered_packet_batches: UnprocessedPacketBatches =
|
||||||
|
UnprocessedPacketBatches::from_iter(packets.clone().into_iter(), packets.len());
|
||||||
|
let mut forward_packet_batches_by_accounts =
|
||||||
|
ForwardPacketBatchesByAccounts::new(current_bank.clone(), 1, 2);
|
||||||
|
|
||||||
let FilterForwardingResults {
|
let FilterForwardingResults {
|
||||||
forwardable_packets,
|
total_forwardable_packets,
|
||||||
total_tracer_packets_in_buffer,
|
total_tracer_packets_in_buffer,
|
||||||
total_forwardable_tracer_packets,
|
total_forwardable_tracer_packets,
|
||||||
} = BankingStage::filter_valid_packets_for_forwarding(packets.iter());
|
} = BankingStage::filter_valid_packets_for_forwarding(
|
||||||
assert_eq!(forwardable_packets.len(), 256);
|
&mut buffered_packet_batches,
|
||||||
|
&mut forward_packet_batches_by_accounts,
|
||||||
|
);
|
||||||
|
assert_eq!(total_forwardable_packets, 256);
|
||||||
assert_eq!(total_tracer_packets_in_buffer, 256);
|
assert_eq!(total_tracer_packets_in_buffer, 256);
|
||||||
assert_eq!(total_forwardable_tracer_packets, 256);
|
assert_eq!(total_forwardable_tracer_packets, 256);
|
||||||
|
|
||||||
// packets in a batch are forwarded in arbitrary order; verify the ports match after
|
// packets in a batch are forwarded in arbitrary order; verify the ports match after
|
||||||
// sorting
|
// sorting
|
||||||
let expected_ports: Vec<_> = (0..256).collect();
|
let expected_ports: Vec<_> = (0..256).collect();
|
||||||
let mut forwarded_ports: Vec<_> = forwardable_packets
|
let mut forwarded_ports: Vec<_> = forward_packet_batches_by_accounts
|
||||||
|
.iter_batches()
|
||||||
|
.flat_map(|batch| {
|
||||||
|
batch
|
||||||
|
.get_forwardable_packets()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|p| p.meta.port)
|
.map(|p| p.meta.port)
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
forwarded_ports.sort_unstable();
|
forwarded_ports.sort_unstable();
|
||||||
assert_eq!(expected_ports, forwarded_ports);
|
assert_eq!(expected_ports, forwarded_ports);
|
||||||
|
}
|
||||||
|
|
||||||
|
// some packets are forwarded
|
||||||
|
{
|
||||||
let num_already_forwarded = 16;
|
let num_already_forwarded = 16;
|
||||||
for packet in &mut packets[0..num_already_forwarded] {
|
for packet in &mut packets[0..num_already_forwarded] {
|
||||||
packet.forwarded = true;
|
packet.forwarded = true;
|
||||||
}
|
}
|
||||||
|
let mut buffered_packet_batches: UnprocessedPacketBatches =
|
||||||
|
UnprocessedPacketBatches::from_iter(packets.clone().into_iter(), packets.len());
|
||||||
|
let mut forward_packet_batches_by_accounts =
|
||||||
|
ForwardPacketBatchesByAccounts::new(current_bank, 1, 2);
|
||||||
let FilterForwardingResults {
|
let FilterForwardingResults {
|
||||||
forwardable_packets,
|
total_forwardable_packets,
|
||||||
total_tracer_packets_in_buffer,
|
total_tracer_packets_in_buffer,
|
||||||
total_forwardable_tracer_packets,
|
total_forwardable_tracer_packets,
|
||||||
} = BankingStage::filter_valid_packets_for_forwarding(packets.iter());
|
} = BankingStage::filter_valid_packets_for_forwarding(
|
||||||
|
&mut buffered_packet_batches,
|
||||||
|
&mut forward_packet_batches_by_accounts,
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
forwardable_packets.len(),
|
total_forwardable_packets,
|
||||||
packets.len() - num_already_forwarded
|
packets.len() - num_already_forwarded
|
||||||
);
|
);
|
||||||
assert_eq!(total_tracer_packets_in_buffer, packets.len());
|
assert_eq!(total_tracer_packets_in_buffer, packets.len());
|
||||||
|
@ -3390,6 +3342,7 @@ mod tests {
|
||||||
packets.len() - num_already_forwarded
|
packets.len() - num_already_forwarded
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_process_transactions_returns_unprocessed_txs() {
|
fn test_process_transactions_returns_unprocessed_txs() {
|
||||||
|
@ -4164,7 +4117,9 @@ mod tests {
|
||||||
..
|
..
|
||||||
} = &genesis_config_info;
|
} = &genesis_config_info;
|
||||||
|
|
||||||
let bank = Arc::new(Bank::new_no_wallclock_throttle_for_tests(genesis_config));
|
let bank = Bank::new_no_wallclock_throttle_for_tests(genesis_config);
|
||||||
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
|
||||||
|
let bank = Arc::new(bank_forks.read().unwrap().get(0).unwrap());
|
||||||
let ledger_path = get_tmp_ledger_path_auto_delete!();
|
let ledger_path = get_tmp_ledger_path_auto_delete!();
|
||||||
{
|
{
|
||||||
let blockstore = Arc::new(
|
let blockstore = Arc::new(
|
||||||
|
@ -4211,6 +4166,7 @@ mod tests {
|
||||||
&stats,
|
&stats,
|
||||||
&connection_cache,
|
&connection_cache,
|
||||||
&mut TracerPacketStats::new(0),
|
&mut TracerPacketStats::new(0),
|
||||||
|
&bank_forks,
|
||||||
);
|
);
|
||||||
|
|
||||||
recv_socket
|
recv_socket
|
||||||
|
@ -4263,7 +4219,9 @@ mod tests {
|
||||||
validator_pubkey,
|
validator_pubkey,
|
||||||
..
|
..
|
||||||
} = &genesis_config_info;
|
} = &genesis_config_info;
|
||||||
let bank = Arc::new(Bank::new_no_wallclock_throttle_for_tests(genesis_config));
|
let bank = Bank::new_no_wallclock_throttle_for_tests(genesis_config);
|
||||||
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
|
||||||
|
let bank = Arc::new(bank_forks.read().unwrap().get(0).unwrap());
|
||||||
let ledger_path = get_tmp_ledger_path_auto_delete!();
|
let ledger_path = get_tmp_ledger_path_auto_delete!();
|
||||||
{
|
{
|
||||||
let blockstore = Arc::new(
|
let blockstore = Arc::new(
|
||||||
|
@ -4325,6 +4283,7 @@ mod tests {
|
||||||
&stats,
|
&stats,
|
||||||
&connection_cache,
|
&connection_cache,
|
||||||
&mut TracerPacketStats::new(0),
|
&mut TracerPacketStats::new(0),
|
||||||
|
&bank_forks,
|
||||||
);
|
);
|
||||||
|
|
||||||
recv_socket
|
recv_socket
|
||||||
|
@ -4360,134 +4319,6 @@ mod tests {
|
||||||
Blockstore::destroy(ledger_path.path()).unwrap();
|
Blockstore::destroy(ledger_path.path()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
fn make_test_packets(
|
|
||||||
transactions: Vec<Transaction>,
|
|
||||||
vote_indexes: Vec<usize>,
|
|
||||||
) -> Vec<DeserializedPacket> {
|
|
||||||
let capacity = transactions.len();
|
|
||||||
let mut packet_vector = Vec::with_capacity(capacity);
|
|
||||||
for tx in transactions.iter() {
|
|
||||||
packet_vector.push(Packet::from_data(None, &tx).unwrap());
|
|
||||||
}
|
|
||||||
for index in vote_indexes.iter() {
|
|
||||||
packet_vector[*index].meta.flags |= PacketFlags::SIMPLE_VOTE_TX;
|
|
||||||
}
|
|
||||||
|
|
||||||
packet_vector
|
|
||||||
.into_iter()
|
|
||||||
.map(|p| DeserializedPacket::new(p).unwrap())
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_transaction_from_deserialized_packet() {
|
|
||||||
use solana_sdk::feature_set::FeatureSet;
|
|
||||||
let keypair = Keypair::new();
|
|
||||||
let transfer_tx =
|
|
||||||
system_transaction::transfer(&keypair, &keypair.pubkey(), 1, Hash::default());
|
|
||||||
let vote_tx = vote_transaction::new_vote_transaction(
|
|
||||||
vec![42],
|
|
||||||
Hash::default(),
|
|
||||||
Hash::default(),
|
|
||||||
&keypair,
|
|
||||||
&keypair,
|
|
||||||
&keypair,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
|
|
||||||
// packets with no votes
|
|
||||||
{
|
|
||||||
let vote_indexes = vec![];
|
|
||||||
let packet_vector =
|
|
||||||
make_test_packets(vec![transfer_tx.clone(), transfer_tx.clone()], vote_indexes);
|
|
||||||
|
|
||||||
let mut votes_only = false;
|
|
||||||
let txs = packet_vector.iter().filter_map(|tx| {
|
|
||||||
BankingStage::transaction_from_deserialized_packet(
|
|
||||||
tx.immutable_section(),
|
|
||||||
&Arc::new(FeatureSet::default()),
|
|
||||||
votes_only,
|
|
||||||
SimpleAddressLoader::Disabled,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
assert_eq!(2, txs.count());
|
|
||||||
|
|
||||||
votes_only = true;
|
|
||||||
let txs = packet_vector.iter().filter_map(|tx| {
|
|
||||||
BankingStage::transaction_from_deserialized_packet(
|
|
||||||
tx.immutable_section(),
|
|
||||||
&Arc::new(FeatureSet::default()),
|
|
||||||
votes_only,
|
|
||||||
SimpleAddressLoader::Disabled,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
assert_eq!(0, txs.count());
|
|
||||||
}
|
|
||||||
|
|
||||||
// packets with some votes
|
|
||||||
{
|
|
||||||
let vote_indexes = vec![0, 2];
|
|
||||||
let packet_vector = make_test_packets(
|
|
||||||
vec![vote_tx.clone(), transfer_tx, vote_tx.clone()],
|
|
||||||
vote_indexes,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut votes_only = false;
|
|
||||||
let txs = packet_vector.iter().filter_map(|tx| {
|
|
||||||
BankingStage::transaction_from_deserialized_packet(
|
|
||||||
tx.immutable_section(),
|
|
||||||
&Arc::new(FeatureSet::default()),
|
|
||||||
votes_only,
|
|
||||||
SimpleAddressLoader::Disabled,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
assert_eq!(3, txs.count());
|
|
||||||
|
|
||||||
votes_only = true;
|
|
||||||
let txs = packet_vector.iter().filter_map(|tx| {
|
|
||||||
BankingStage::transaction_from_deserialized_packet(
|
|
||||||
tx.immutable_section(),
|
|
||||||
&Arc::new(FeatureSet::default()),
|
|
||||||
votes_only,
|
|
||||||
SimpleAddressLoader::Disabled,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
assert_eq!(2, txs.count());
|
|
||||||
}
|
|
||||||
|
|
||||||
// packets with all votes
|
|
||||||
{
|
|
||||||
let vote_indexes = vec![0, 1, 2];
|
|
||||||
let packet_vector = make_test_packets(
|
|
||||||
vec![vote_tx.clone(), vote_tx.clone(), vote_tx],
|
|
||||||
vote_indexes,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut votes_only = false;
|
|
||||||
let txs = packet_vector.iter().filter_map(|tx| {
|
|
||||||
BankingStage::transaction_from_deserialized_packet(
|
|
||||||
tx.immutable_section(),
|
|
||||||
&Arc::new(FeatureSet::default()),
|
|
||||||
votes_only,
|
|
||||||
SimpleAddressLoader::Disabled,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
assert_eq!(3, txs.count());
|
|
||||||
|
|
||||||
votes_only = true;
|
|
||||||
let txs = packet_vector.iter().filter_map(|tx| {
|
|
||||||
BankingStage::transaction_from_deserialized_packet(
|
|
||||||
tx.immutable_section(),
|
|
||||||
&Arc::new(FeatureSet::default()),
|
|
||||||
votes_only,
|
|
||||||
SimpleAddressLoader::Disabled,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
assert_eq!(3, txs.count());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_accumulate_batched_transaction_costs() {
|
fn test_accumulate_batched_transaction_costs() {
|
||||||
let signature_cost = 1;
|
let signature_cost = 1;
|
||||||
|
|
|
@ -0,0 +1,343 @@
|
||||||
|
use {
|
||||||
|
crate::unprocessed_packet_batches::{self, ImmutableDeserializedPacket},
|
||||||
|
solana_perf::packet::Packet,
|
||||||
|
solana_runtime::{
|
||||||
|
bank::Bank,
|
||||||
|
block_cost_limits,
|
||||||
|
cost_tracker::{CostTracker, CostTrackerError},
|
||||||
|
},
|
||||||
|
solana_sdk::pubkey::Pubkey,
|
||||||
|
std::{rc::Rc, sync::Arc},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// `ForwardBatch` to have half of default cost_tracker limits, as smaller batch
|
||||||
|
/// allows better granularity in composing forwarding transactions; e.g.,
|
||||||
|
/// transactions in each batch are potentially more evenly distributed across accounts.
|
||||||
|
const FORWARDED_BLOCK_COMPUTE_RATIO: u32 = 2;
|
||||||
|
/// this number divided by`FORWARDED_BLOCK_COMPUTE_RATIO` is the total blocks to forward.
|
||||||
|
/// To accommodate transactions without `compute_budget` instruction, which will
|
||||||
|
/// have default 200_000 compute units, it has 100 batches as default to forward
|
||||||
|
/// up to 12_000 such transaction. (120 such transactions fill up a batch, 100
|
||||||
|
/// batches allows 12_000 transactions)
|
||||||
|
const DEFAULT_NUMBER_OF_BATCHES: u32 = 100;
|
||||||
|
|
||||||
|
/// `ForwardBatch` represents one forwardable batch of transactions with a
|
||||||
|
/// limited number of total compute units
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ForwardBatch {
|
||||||
|
cost_tracker: CostTracker,
|
||||||
|
// `forwardable_packets` keeps forwardable packets in a vector in its
|
||||||
|
// original fee prioritized order
|
||||||
|
forwardable_packets: Vec<Rc<ImmutableDeserializedPacket>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ForwardBatch {
|
||||||
|
/// default ForwardBatch has cost_tracker with default limits
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ForwardBatch {
|
||||||
|
/// `ForwardBatch` keeps forwardable packets in a vector in its original fee prioritized order,
|
||||||
|
/// Number of packets are limited by `cost_tracker` with customized `limit_ratio` to lower
|
||||||
|
/// (when `limit_ratio` > 1) `cost_tracker`'s default limits.
|
||||||
|
/// Lower limits yield smaller batch for forwarding.
|
||||||
|
fn new(limit_ratio: u32) -> Self {
|
||||||
|
let mut cost_tracker = CostTracker::default();
|
||||||
|
cost_tracker.set_limits(
|
||||||
|
block_cost_limits::MAX_WRITABLE_ACCOUNT_UNITS.saturating_div(limit_ratio as u64),
|
||||||
|
block_cost_limits::MAX_BLOCK_UNITS.saturating_div(limit_ratio as u64),
|
||||||
|
block_cost_limits::MAX_VOTE_UNITS.saturating_div(limit_ratio as u64),
|
||||||
|
);
|
||||||
|
Self {
|
||||||
|
cost_tracker,
|
||||||
|
forwardable_packets: Vec::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_add(
|
||||||
|
&mut self,
|
||||||
|
write_lock_accounts: &[Pubkey],
|
||||||
|
compute_units: u64,
|
||||||
|
immutable_packet: Rc<ImmutableDeserializedPacket>,
|
||||||
|
) -> Result<u64, CostTrackerError> {
|
||||||
|
let res = self.cost_tracker.try_add_requested_cus(
|
||||||
|
write_lock_accounts,
|
||||||
|
compute_units,
|
||||||
|
immutable_packet.is_simple_vote(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if res.is_ok() {
|
||||||
|
self.forwardable_packets.push(immutable_packet);
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_forwardable_packets(&self) -> impl Iterator<Item = &Packet> {
|
||||||
|
self.forwardable_packets
|
||||||
|
.iter()
|
||||||
|
.map(|immutable_packet| immutable_packet.original_packet())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.forwardable_packets.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.forwardable_packets.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// To avoid forward queue being saturated by transactions for single hot account,
|
||||||
|
/// the forwarder will group and send prioritized transactions by account limit
|
||||||
|
/// to allow transactions on non-congested accounts to be forwarded alongside higher fee
|
||||||
|
/// transactions that saturate those highly demanded accounts.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ForwardPacketBatchesByAccounts {
|
||||||
|
// Need a `bank` to load all accounts for VersionedTransaction. Currently
|
||||||
|
// using current rooted bank for it.
|
||||||
|
current_bank: Arc<Bank>,
|
||||||
|
// Forwardable packets are staged in number of batches, each batch is limited
|
||||||
|
// by cost_tracker on both account limit and block limits. Those limits are
|
||||||
|
// set as `limit_ratio` of regular block limits to facilitate quicker iteration.
|
||||||
|
forward_batches: Vec<ForwardBatch>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ForwardPacketBatchesByAccounts {
|
||||||
|
pub fn new_with_default_batch_limits(current_bank: Arc<Bank>) -> Self {
|
||||||
|
Self::new(
|
||||||
|
current_bank,
|
||||||
|
FORWARDED_BLOCK_COMPUTE_RATIO,
|
||||||
|
DEFAULT_NUMBER_OF_BATCHES,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(current_bank: Arc<Bank>, limit_ratio: u32, number_of_batches: u32) -> Self {
|
||||||
|
let forward_batches = (0..number_of_batches)
|
||||||
|
.map(|_| ForwardBatch::new(limit_ratio))
|
||||||
|
.collect();
|
||||||
|
Self {
|
||||||
|
current_bank,
|
||||||
|
forward_batches,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_packet(&mut self, packet: Rc<ImmutableDeserializedPacket>) -> bool {
|
||||||
|
// do not forward packet that cannot be sanitized
|
||||||
|
if let Some(sanitized_transaction) =
|
||||||
|
unprocessed_packet_batches::transaction_from_deserialized_packet(
|
||||||
|
&packet,
|
||||||
|
&self.current_bank.feature_set,
|
||||||
|
self.current_bank.vote_only_bank(),
|
||||||
|
self.current_bank.as_ref(),
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// get write_lock_accounts
|
||||||
|
let message = sanitized_transaction.message();
|
||||||
|
let write_lock_accounts: Vec<_> = message
|
||||||
|
.account_keys()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(i, account_key)| {
|
||||||
|
if message.is_writable(i) {
|
||||||
|
Some(*account_key)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// get requested CUs
|
||||||
|
let requested_cu = packet.compute_unit_limit();
|
||||||
|
|
||||||
|
// try to fill into forward batches
|
||||||
|
self.add_packet_to_batches(&write_lock_accounts, requested_cu, packet)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter_batches(&self) -> impl Iterator<Item = &ForwardBatch> {
|
||||||
|
self.forward_batches.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// transaction will try to be filled into 'batches', if can't fit into first batch
|
||||||
|
/// due to cost_tracker (eg., exceeding account limit or block limit), it will try
|
||||||
|
/// next batch until either being added to one of 'bucket' or not being forwarded.
|
||||||
|
fn add_packet_to_batches(
|
||||||
|
&mut self,
|
||||||
|
write_lock_accounts: &[Pubkey],
|
||||||
|
compute_units: u64,
|
||||||
|
immutable_packet: Rc<ImmutableDeserializedPacket>,
|
||||||
|
) -> bool {
|
||||||
|
for forward_batch in self.forward_batches.iter_mut() {
|
||||||
|
if forward_batch
|
||||||
|
.try_add(write_lock_accounts, compute_units, immutable_packet.clone())
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use {
|
||||||
|
super::*,
|
||||||
|
crate::unprocessed_packet_batches::{DeserializedPacket, TransactionPriorityDetails},
|
||||||
|
solana_runtime::{
|
||||||
|
bank::Bank,
|
||||||
|
bank_forks::BankForks,
|
||||||
|
genesis_utils::{create_genesis_config, GenesisConfigInfo},
|
||||||
|
},
|
||||||
|
solana_sdk::{hash::Hash, signature::Keypair, system_transaction},
|
||||||
|
std::sync::RwLock,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn build_bank_forks_for_test() -> Arc<RwLock<BankForks>> {
|
||||||
|
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
|
||||||
|
let bank = Bank::new_for_tests(&genesis_config);
|
||||||
|
let bank_forks = BankForks::new(bank);
|
||||||
|
Arc::new(RwLock::new(bank_forks))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_deserialized_packet_for_test(
|
||||||
|
priority: u64,
|
||||||
|
compute_unit_limit: u64,
|
||||||
|
) -> DeserializedPacket {
|
||||||
|
let tx = system_transaction::transfer(
|
||||||
|
&Keypair::new(),
|
||||||
|
&solana_sdk::pubkey::new_rand(),
|
||||||
|
1,
|
||||||
|
Hash::new_unique(),
|
||||||
|
);
|
||||||
|
let packet = Packet::from_data(None, &tx).unwrap();
|
||||||
|
DeserializedPacket::new_with_priority_details(
|
||||||
|
packet,
|
||||||
|
TransactionPriorityDetails {
|
||||||
|
priority,
|
||||||
|
compute_unit_limit,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_try_add_to_forward_batch() {
|
||||||
|
// set test batch limit to be 1 millionth of regular block limit
|
||||||
|
let limit_ratio = 1_000_000u32;
|
||||||
|
// set requested_cu to be half of batch account limit
|
||||||
|
let requested_cu =
|
||||||
|
block_cost_limits::MAX_WRITABLE_ACCOUNT_UNITS.saturating_div(limit_ratio as u64);
|
||||||
|
|
||||||
|
let mut forward_batch = ForwardBatch::new(limit_ratio);
|
||||||
|
|
||||||
|
let write_lock_accounts = vec![Pubkey::new_unique(), Pubkey::new_unique()];
|
||||||
|
let packet = build_deserialized_packet_for_test(10, requested_cu);
|
||||||
|
// first packet will be successful
|
||||||
|
assert!(forward_batch
|
||||||
|
.try_add(
|
||||||
|
&write_lock_accounts,
|
||||||
|
requested_cu,
|
||||||
|
packet.immutable_section().clone()
|
||||||
|
)
|
||||||
|
.is_ok());
|
||||||
|
assert_eq!(1, forward_batch.forwardable_packets.len());
|
||||||
|
// second packet will hit account limit, therefore not added
|
||||||
|
assert!(forward_batch
|
||||||
|
.try_add(
|
||||||
|
&write_lock_accounts,
|
||||||
|
requested_cu,
|
||||||
|
packet.immutable_section().clone()
|
||||||
|
)
|
||||||
|
.is_err());
|
||||||
|
assert_eq!(1, forward_batch.forwardable_packets.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_add_packet_to_batches() {
|
||||||
|
solana_logger::setup();
|
||||||
|
// set test batch limit to be 1 millionth of regular block limit
|
||||||
|
let limit_ratio = 1_000_000u32;
|
||||||
|
let number_of_batches = 2;
|
||||||
|
// set requested_cu to be half of batch account limit
|
||||||
|
let requested_cu =
|
||||||
|
block_cost_limits::MAX_WRITABLE_ACCOUNT_UNITS.saturating_div(limit_ratio as u64);
|
||||||
|
|
||||||
|
let mut forward_packet_batches_by_accounts = ForwardPacketBatchesByAccounts::new(
|
||||||
|
build_bank_forks_for_test().read().unwrap().root_bank(),
|
||||||
|
limit_ratio,
|
||||||
|
number_of_batches,
|
||||||
|
);
|
||||||
|
|
||||||
|
// initially both batches are empty
|
||||||
|
{
|
||||||
|
let mut batches = forward_packet_batches_by_accounts.iter_batches();
|
||||||
|
assert_eq!(0, batches.next().unwrap().len());
|
||||||
|
assert_eq!(0, batches.next().unwrap().len());
|
||||||
|
assert!(batches.next().is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
let hot_account = solana_sdk::pubkey::new_rand();
|
||||||
|
let other_account = solana_sdk::pubkey::new_rand();
|
||||||
|
let packet_high_priority = build_deserialized_packet_for_test(10, requested_cu);
|
||||||
|
let packet_low_priority = build_deserialized_packet_for_test(0, requested_cu);
|
||||||
|
// with 4 packets, first 3 write to same hot_account with higher priority,
|
||||||
|
// the 4th write to other_account with lower priority;
|
||||||
|
// assert the 1st and 4th fit in fist batch, the 2nd in 2nd batch and 3rd will be dropped.
|
||||||
|
|
||||||
|
// 1st high-priority packet added to 1st batch
|
||||||
|
{
|
||||||
|
forward_packet_batches_by_accounts.add_packet_to_batches(
|
||||||
|
&[hot_account],
|
||||||
|
requested_cu,
|
||||||
|
packet_high_priority.immutable_section().clone(),
|
||||||
|
);
|
||||||
|
let mut batches = forward_packet_batches_by_accounts.iter_batches();
|
||||||
|
assert_eq!(1, batches.next().unwrap().len());
|
||||||
|
assert_eq!(0, batches.next().unwrap().len());
|
||||||
|
assert!(batches.next().is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2nd high-priority packet added to 2nd packet
|
||||||
|
{
|
||||||
|
forward_packet_batches_by_accounts.add_packet_to_batches(
|
||||||
|
&[hot_account],
|
||||||
|
requested_cu,
|
||||||
|
packet_high_priority.immutable_section().clone(),
|
||||||
|
);
|
||||||
|
let mut batches = forward_packet_batches_by_accounts.iter_batches();
|
||||||
|
assert_eq!(1, batches.next().unwrap().len());
|
||||||
|
assert_eq!(1, batches.next().unwrap().len());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3rd high-priority packet not included in forwarding
|
||||||
|
{
|
||||||
|
forward_packet_batches_by_accounts.add_packet_to_batches(
|
||||||
|
&[hot_account],
|
||||||
|
requested_cu,
|
||||||
|
packet_high_priority.immutable_section().clone(),
|
||||||
|
);
|
||||||
|
let mut batches = forward_packet_batches_by_accounts.iter_batches();
|
||||||
|
assert_eq!(1, batches.next().unwrap().len());
|
||||||
|
assert_eq!(1, batches.next().unwrap().len());
|
||||||
|
assert!(batches.next().is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4rd lower priority packet added to 1st bucket on non-content account
|
||||||
|
{
|
||||||
|
forward_packet_batches_by_accounts.add_packet_to_batches(
|
||||||
|
&[other_account],
|
||||||
|
requested_cu,
|
||||||
|
packet_low_priority.immutable_section().clone(),
|
||||||
|
);
|
||||||
|
let mut batches = forward_packet_batches_by_accounts.iter_batches();
|
||||||
|
assert_eq!(2, batches.next().unwrap().len());
|
||||||
|
assert_eq!(1, batches.next().unwrap().len());
|
||||||
|
assert!(batches.next().is_none());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -122,8 +122,9 @@ struct LeaderSlotPacketCountMetrics {
|
||||||
// total number of valid unprocessed packets in the buffer that were removed after being forwarded
|
// total number of valid unprocessed packets in the buffer that were removed after being forwarded
|
||||||
cleared_from_buffer_after_forward_count: u64,
|
cleared_from_buffer_after_forward_count: u64,
|
||||||
|
|
||||||
// total number of packets removed at the end of the slot due to being too old, duplicate, etc.
|
// total number of forwardable batches that were attempted for forwarding. A forwardable batch
|
||||||
end_of_slot_filtered_invalid_count: u64,
|
// is defined in `ForwardPacketBatchesByAccounts` in `forward_packet_batches_by_accounts.rs`
|
||||||
|
forwardable_batches_count: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LeaderSlotPacketCountMetrics {
|
impl LeaderSlotPacketCountMetrics {
|
||||||
|
@ -222,8 +223,8 @@ impl LeaderSlotPacketCountMetrics {
|
||||||
i64
|
i64
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"end_of_slot_filtered_invalid_count",
|
"forwardable_batches_count",
|
||||||
self.end_of_slot_filtered_invalid_count as i64,
|
self.forwardable_batches_count as i64,
|
||||||
i64
|
i64
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
@ -573,6 +574,17 @@ impl LeaderSlotMetricsTracker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn increment_forwardable_batches_count(&mut self, count: u64) {
|
||||||
|
if let Some(leader_slot_metrics) = &mut self.leader_slot_metrics {
|
||||||
|
saturating_add_assign!(
|
||||||
|
leader_slot_metrics
|
||||||
|
.packet_count_metrics
|
||||||
|
.forwardable_batches_count,
|
||||||
|
count
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn increment_retryable_packets_count(&mut self, count: u64) {
|
pub(crate) fn increment_retryable_packets_count(&mut self, count: u64) {
|
||||||
if let Some(leader_slot_metrics) = &mut self.leader_slot_metrics {
|
if let Some(leader_slot_metrics) = &mut self.leader_slot_metrics {
|
||||||
saturating_add_assign!(
|
saturating_add_assign!(
|
||||||
|
@ -584,17 +596,6 @@ impl LeaderSlotMetricsTracker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn increment_end_of_slot_filtered_invalid_count(&mut self, count: u64) {
|
|
||||||
if let Some(leader_slot_metrics) = &mut self.leader_slot_metrics {
|
|
||||||
saturating_add_assign!(
|
|
||||||
leader_slot_metrics
|
|
||||||
.packet_count_metrics
|
|
||||||
.end_of_slot_filtered_invalid_count,
|
|
||||||
count
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn set_end_of_slot_unprocessed_buffer_len(&mut self, len: u64) {
|
pub(crate) fn set_end_of_slot_unprocessed_buffer_len(&mut self, len: u64) {
|
||||||
if let Some(leader_slot_metrics) = &mut self.leader_slot_metrics {
|
if let Some(leader_slot_metrics) = &mut self.leader_slot_metrics {
|
||||||
leader_slot_metrics
|
leader_slot_metrics
|
||||||
|
@ -684,19 +685,6 @@ impl LeaderSlotMetricsTracker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consuming buffered packets timing metrics
|
|
||||||
pub(crate) fn increment_end_of_slot_filtering_us(&mut self, us: u64) {
|
|
||||||
if let Some(leader_slot_metrics) = &mut self.leader_slot_metrics {
|
|
||||||
saturating_add_assign!(
|
|
||||||
leader_slot_metrics
|
|
||||||
.timing_metrics
|
|
||||||
.consume_buffered_packets_timings
|
|
||||||
.end_of_slot_filtering_us,
|
|
||||||
us
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn increment_consume_buffered_packets_poh_recorder_lock_us(&mut self, us: u64) {
|
pub(crate) fn increment_consume_buffered_packets_poh_recorder_lock_us(&mut self, us: u64) {
|
||||||
if let Some(leader_slot_metrics) = &mut self.leader_slot_metrics {
|
if let Some(leader_slot_metrics) = &mut self.leader_slot_metrics {
|
||||||
saturating_add_assign!(
|
saturating_add_assign!(
|
||||||
|
|
|
@ -227,9 +227,6 @@ pub(crate) struct ConsumeBufferedPacketsTimings {
|
||||||
// Time spent grabbing poh recorder lock
|
// Time spent grabbing poh recorder lock
|
||||||
pub poh_recorder_lock_us: u64,
|
pub poh_recorder_lock_us: u64,
|
||||||
|
|
||||||
// Time spent filtering invalid packets after leader slot has ended
|
|
||||||
pub end_of_slot_filtering_us: u64,
|
|
||||||
|
|
||||||
// Time spent processing transactions
|
// Time spent processing transactions
|
||||||
pub process_packets_transactions_us: u64,
|
pub process_packets_transactions_us: u64,
|
||||||
}
|
}
|
||||||
|
@ -245,11 +242,6 @@ impl ConsumeBufferedPacketsTimings {
|
||||||
self.poh_recorder_lock_us as i64,
|
self.poh_recorder_lock_us as i64,
|
||||||
i64
|
i64
|
||||||
),
|
),
|
||||||
(
|
|
||||||
"end_of_slot_filtering_us",
|
|
||||||
self.end_of_slot_filtering_us as i64,
|
|
||||||
i64
|
|
||||||
),
|
|
||||||
(
|
(
|
||||||
"process_packets_transactions_us",
|
"process_packets_transactions_us",
|
||||||
self.process_packets_transactions_us as i64,
|
self.process_packets_transactions_us as i64,
|
||||||
|
|
|
@ -27,6 +27,7 @@ pub mod duplicate_repair_status;
|
||||||
pub mod fetch_stage;
|
pub mod fetch_stage;
|
||||||
pub mod find_packet_sender_stake_stage;
|
pub mod find_packet_sender_stake_stage;
|
||||||
pub mod fork_choice;
|
pub mod fork_choice;
|
||||||
|
pub mod forward_packet_batches_by_accounts;
|
||||||
pub mod gen_keys;
|
pub mod gen_keys;
|
||||||
pub mod heaviest_subtree_fork_choice;
|
pub mod heaviest_subtree_fork_choice;
|
||||||
pub mod latest_validator_votes_for_frozen_banks;
|
pub mod latest_validator_votes_for_frozen_banks;
|
||||||
|
|
|
@ -230,6 +230,7 @@ impl Tpu {
|
||||||
replay_vote_sender,
|
replay_vote_sender,
|
||||||
cost_model.clone(),
|
cost_model.clone(),
|
||||||
connection_cache.clone(),
|
connection_cache.clone(),
|
||||||
|
bank_forks.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let broadcast_stage = broadcast_type.new_broadcast_stage(
|
let broadcast_stage = broadcast_type.new_broadcast_stage(
|
||||||
|
|
|
@ -3,18 +3,23 @@ use {
|
||||||
solana_perf::packet::{Packet, PacketBatch},
|
solana_perf::packet::{Packet, PacketBatch},
|
||||||
solana_program_runtime::compute_budget::ComputeBudget,
|
solana_program_runtime::compute_budget::ComputeBudget,
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
|
feature_set,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
message::{Message, SanitizedVersionedMessage},
|
message::{Message, SanitizedVersionedMessage},
|
||||||
sanitize::SanitizeError,
|
sanitize::SanitizeError,
|
||||||
short_vec::decode_shortu16_len,
|
short_vec::decode_shortu16_len,
|
||||||
signature::Signature,
|
signature::Signature,
|
||||||
transaction::{SanitizedVersionedTransaction, Transaction, VersionedTransaction},
|
transaction::{
|
||||||
|
AddressLoader, SanitizedTransaction, SanitizedVersionedTransaction, Transaction,
|
||||||
|
VersionedTransaction,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
std::{
|
std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
collections::{hash_map::Entry, HashMap},
|
collections::{hash_map::Entry, HashMap},
|
||||||
mem::size_of,
|
mem::size_of,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
|
sync::Arc,
|
||||||
},
|
},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
};
|
};
|
||||||
|
@ -36,8 +41,8 @@ pub enum DeserializedPacketError {
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub struct TransactionPriorityDetails {
|
pub struct TransactionPriorityDetails {
|
||||||
priority: u64,
|
pub priority: u64,
|
||||||
compute_unit_limit: u64,
|
pub compute_unit_limit: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
@ -93,7 +98,7 @@ impl DeserializedPacket {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn new_with_priority_details(
|
pub fn new_with_priority_details(
|
||||||
packet: Packet,
|
packet: Packet,
|
||||||
priority_details: TransactionPriorityDetails,
|
priority_details: TransactionPriorityDetails,
|
||||||
) -> Result<Self, DeserializedPacketError> {
|
) -> Result<Self, DeserializedPacketError> {
|
||||||
|
@ -254,12 +259,40 @@ impl UnprocessedPacketBatches {
|
||||||
self.message_hash_to_transaction.iter_mut().map(|(_k, v)| v)
|
self.message_hash_to_transaction.iter_mut().map(|(_k, v)| v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Iterates DeserializedPackets in descending priority (max-first) order,
|
||||||
|
/// calls FnMut for each DeserializedPacket.
|
||||||
|
pub fn iter_desc<F>(&mut self, mut f: F)
|
||||||
|
where
|
||||||
|
F: FnMut(&mut DeserializedPacket) -> bool,
|
||||||
|
{
|
||||||
|
let mut packet_priority_queue_clone = self.packet_priority_queue.clone();
|
||||||
|
|
||||||
|
for immutable_packet in packet_priority_queue_clone.drain_desc() {
|
||||||
|
match self
|
||||||
|
.message_hash_to_transaction
|
||||||
|
.entry(*immutable_packet.message_hash())
|
||||||
|
{
|
||||||
|
Entry::Vacant(_vacant_entry) => {
|
||||||
|
panic!(
|
||||||
|
"entry {} must exist to be consistent with `packet_priority_queue`",
|
||||||
|
immutable_packet.message_hash()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Entry::Occupied(mut occupied_entry) => {
|
||||||
|
if !f(occupied_entry.get_mut()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn retain<F>(&mut self, mut f: F)
|
pub fn retain<F>(&mut self, mut f: F)
|
||||||
where
|
where
|
||||||
F: FnMut(&mut DeserializedPacket) -> bool,
|
F: FnMut(&mut DeserializedPacket) -> bool,
|
||||||
{
|
{
|
||||||
// TODO: optimize this only when number of packets
|
// TODO: optimize this only when number of packets
|
||||||
// with oudated blockhash is high
|
// with outdated blockhash is high
|
||||||
let new_packet_priority_queue: MinMaxHeap<Rc<ImmutableDeserializedPacket>> = self
|
let new_packet_priority_queue: MinMaxHeap<Rc<ImmutableDeserializedPacket>> = self
|
||||||
.packet_priority_queue
|
.packet_priority_queue
|
||||||
.drain()
|
.drain()
|
||||||
|
@ -415,14 +448,45 @@ pub fn transactions_to_deserialized_packets(
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This function deserializes packets into transactions, computes the blake3 hash of transaction
|
||||||
|
// messages, and verifies secp256k1 instructions. A list of sanitized transactions are returned
|
||||||
|
// with their packet indexes.
|
||||||
|
#[allow(clippy::needless_collect)]
|
||||||
|
pub fn transaction_from_deserialized_packet(
|
||||||
|
deserialized_packet: &ImmutableDeserializedPacket,
|
||||||
|
feature_set: &Arc<feature_set::FeatureSet>,
|
||||||
|
votes_only: bool,
|
||||||
|
address_loader: impl AddressLoader,
|
||||||
|
) -> Option<SanitizedTransaction> {
|
||||||
|
if votes_only && !deserialized_packet.is_simple_vote() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tx = SanitizedTransaction::try_new(
|
||||||
|
deserialized_packet.transaction().clone(),
|
||||||
|
*deserialized_packet.message_hash(),
|
||||||
|
deserialized_packet.is_simple_vote(),
|
||||||
|
address_loader,
|
||||||
|
)
|
||||||
|
.ok()?;
|
||||||
|
tx.verify_precompiles(feature_set).ok()?;
|
||||||
|
Some(tx)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use {
|
use {
|
||||||
super::*,
|
super::*,
|
||||||
|
solana_perf::packet::PacketFlags,
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
compute_budget::ComputeBudgetInstruction, message::VersionedMessage, pubkey::Pubkey,
|
compute_budget::ComputeBudgetInstruction,
|
||||||
signature::Keypair, system_instruction, system_transaction,
|
message::VersionedMessage,
|
||||||
|
pubkey::Pubkey,
|
||||||
|
signature::{Keypair, Signer},
|
||||||
|
system_instruction, system_transaction,
|
||||||
|
transaction::{SimpleAddressLoader, Transaction},
|
||||||
},
|
},
|
||||||
|
solana_vote_program::vote_transaction,
|
||||||
std::net::IpAddr,
|
std::net::IpAddr,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -622,4 +686,132 @@ mod tests {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn make_test_packets(
|
||||||
|
transactions: Vec<Transaction>,
|
||||||
|
vote_indexes: Vec<usize>,
|
||||||
|
) -> Vec<DeserializedPacket> {
|
||||||
|
let capacity = transactions.len();
|
||||||
|
let mut packet_vector = Vec::with_capacity(capacity);
|
||||||
|
for tx in transactions.iter() {
|
||||||
|
packet_vector.push(Packet::from_data(None, &tx).unwrap());
|
||||||
|
}
|
||||||
|
for index in vote_indexes.iter() {
|
||||||
|
packet_vector[*index].meta.flags |= PacketFlags::SIMPLE_VOTE_TX;
|
||||||
|
}
|
||||||
|
|
||||||
|
packet_vector
|
||||||
|
.into_iter()
|
||||||
|
.map(|p| DeserializedPacket::new(p).unwrap())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_transaction_from_deserialized_packet() {
|
||||||
|
use solana_sdk::feature_set::FeatureSet;
|
||||||
|
let keypair = Keypair::new();
|
||||||
|
let transfer_tx =
|
||||||
|
system_transaction::transfer(&keypair, &keypair.pubkey(), 1, Hash::default());
|
||||||
|
let vote_tx = vote_transaction::new_vote_transaction(
|
||||||
|
vec![42],
|
||||||
|
Hash::default(),
|
||||||
|
Hash::default(),
|
||||||
|
&keypair,
|
||||||
|
&keypair,
|
||||||
|
&keypair,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
// packets with no votes
|
||||||
|
{
|
||||||
|
let vote_indexes = vec![];
|
||||||
|
let packet_vector =
|
||||||
|
make_test_packets(vec![transfer_tx.clone(), transfer_tx.clone()], vote_indexes);
|
||||||
|
|
||||||
|
let mut votes_only = false;
|
||||||
|
let txs = packet_vector.iter().filter_map(|tx| {
|
||||||
|
transaction_from_deserialized_packet(
|
||||||
|
tx.immutable_section(),
|
||||||
|
&Arc::new(FeatureSet::default()),
|
||||||
|
votes_only,
|
||||||
|
SimpleAddressLoader::Disabled,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
assert_eq!(2, txs.count());
|
||||||
|
|
||||||
|
votes_only = true;
|
||||||
|
let txs = packet_vector.iter().filter_map(|tx| {
|
||||||
|
transaction_from_deserialized_packet(
|
||||||
|
tx.immutable_section(),
|
||||||
|
&Arc::new(FeatureSet::default()),
|
||||||
|
votes_only,
|
||||||
|
SimpleAddressLoader::Disabled,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
assert_eq!(0, txs.count());
|
||||||
|
}
|
||||||
|
|
||||||
|
// packets with some votes
|
||||||
|
{
|
||||||
|
let vote_indexes = vec![0, 2];
|
||||||
|
let packet_vector = make_test_packets(
|
||||||
|
vec![vote_tx.clone(), transfer_tx, vote_tx.clone()],
|
||||||
|
vote_indexes,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut votes_only = false;
|
||||||
|
let txs = packet_vector.iter().filter_map(|tx| {
|
||||||
|
transaction_from_deserialized_packet(
|
||||||
|
tx.immutable_section(),
|
||||||
|
&Arc::new(FeatureSet::default()),
|
||||||
|
votes_only,
|
||||||
|
SimpleAddressLoader::Disabled,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
assert_eq!(3, txs.count());
|
||||||
|
|
||||||
|
votes_only = true;
|
||||||
|
let txs = packet_vector.iter().filter_map(|tx| {
|
||||||
|
transaction_from_deserialized_packet(
|
||||||
|
tx.immutable_section(),
|
||||||
|
&Arc::new(FeatureSet::default()),
|
||||||
|
votes_only,
|
||||||
|
SimpleAddressLoader::Disabled,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
assert_eq!(2, txs.count());
|
||||||
|
}
|
||||||
|
|
||||||
|
// packets with all votes
|
||||||
|
{
|
||||||
|
let vote_indexes = vec![0, 1, 2];
|
||||||
|
let packet_vector = make_test_packets(
|
||||||
|
vec![vote_tx.clone(), vote_tx.clone(), vote_tx],
|
||||||
|
vote_indexes,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut votes_only = false;
|
||||||
|
let txs = packet_vector.iter().filter_map(|tx| {
|
||||||
|
transaction_from_deserialized_packet(
|
||||||
|
tx.immutable_section(),
|
||||||
|
&Arc::new(FeatureSet::default()),
|
||||||
|
votes_only,
|
||||||
|
SimpleAddressLoader::Disabled,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
assert_eq!(3, txs.count());
|
||||||
|
|
||||||
|
votes_only = true;
|
||||||
|
let txs = packet_vector.iter().filter_map(|tx| {
|
||||||
|
transaction_from_deserialized_packet(
|
||||||
|
tx.immutable_section(),
|
||||||
|
&Arc::new(FeatureSet::default()),
|
||||||
|
votes_only,
|
||||||
|
SimpleAddressLoader::Disabled,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
assert_eq!(3, txs.count());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,7 @@ impl CostTracker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// bench tests needs to reset limits
|
/// allows to adjust limits initiated during construction
|
||||||
pub fn set_limits(
|
pub fn set_limits(
|
||||||
&mut self,
|
&mut self,
|
||||||
account_cost_limit: u64,
|
account_cost_limit: u64,
|
||||||
|
@ -95,6 +95,18 @@ impl CostTracker {
|
||||||
Ok(self.block_cost)
|
Ok(self.block_cost)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Using user requested compute-units to track cost.
|
||||||
|
pub fn try_add_requested_cus(
|
||||||
|
&mut self,
|
||||||
|
write_lock_accounts: &[Pubkey],
|
||||||
|
requested_cus: u64,
|
||||||
|
is_vote: bool,
|
||||||
|
) -> Result<u64, CostTrackerError> {
|
||||||
|
self.would_fit_internal(write_lock_accounts.iter(), requested_cus, is_vote, 0)?;
|
||||||
|
self.add_transaction_cost_internal(write_lock_accounts.iter(), requested_cus, is_vote, 0);
|
||||||
|
Ok(self.block_cost)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn update_execution_cost(
|
pub fn update_execution_cost(
|
||||||
&mut self,
|
&mut self,
|
||||||
estimated_tx_cost: &TransactionCost,
|
estimated_tx_cost: &TransactionCost,
|
||||||
|
@ -165,9 +177,22 @@ impl CostTracker {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn would_fit(&self, tx_cost: &TransactionCost) -> Result<(), CostTrackerError> {
|
fn would_fit(&self, tx_cost: &TransactionCost) -> Result<(), CostTrackerError> {
|
||||||
let writable_accounts = &tx_cost.writable_accounts;
|
self.would_fit_internal(
|
||||||
let cost = tx_cost.sum();
|
tx_cost.writable_accounts.iter(),
|
||||||
let vote_cost = if tx_cost.is_simple_vote { cost } else { 0 };
|
tx_cost.sum(),
|
||||||
|
tx_cost.is_simple_vote,
|
||||||
|
tx_cost.account_data_size,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn would_fit_internal<'a>(
|
||||||
|
&self,
|
||||||
|
write_lock_accounts: impl Iterator<Item = &'a Pubkey>,
|
||||||
|
cost: u64,
|
||||||
|
is_vote: bool,
|
||||||
|
account_data_size: u64,
|
||||||
|
) -> Result<(), CostTrackerError> {
|
||||||
|
let vote_cost = if is_vote { cost } else { 0 };
|
||||||
|
|
||||||
// check against the total package cost
|
// check against the total package cost
|
||||||
if self.block_cost.saturating_add(cost) > self.block_cost_limit {
|
if self.block_cost.saturating_add(cost) > self.block_cost_limit {
|
||||||
|
@ -186,9 +211,7 @@ impl CostTracker {
|
||||||
|
|
||||||
// NOTE: Check if the total accounts data size is exceeded *before* the block accounts data
|
// NOTE: Check if the total accounts data size is exceeded *before* the block accounts data
|
||||||
// size. This way, transactions are not unnecessarily retried.
|
// size. This way, transactions are not unnecessarily retried.
|
||||||
let account_data_size = self
|
let account_data_size = self.account_data_size.saturating_add(account_data_size);
|
||||||
.account_data_size
|
|
||||||
.saturating_add(tx_cost.account_data_size);
|
|
||||||
if let Some(account_data_size_limit) = self.account_data_size_limit {
|
if let Some(account_data_size_limit) = self.account_data_size_limit {
|
||||||
if account_data_size > account_data_size_limit {
|
if account_data_size > account_data_size_limit {
|
||||||
return Err(CostTrackerError::WouldExceedAccountDataTotalLimit);
|
return Err(CostTrackerError::WouldExceedAccountDataTotalLimit);
|
||||||
|
@ -200,7 +223,7 @@ impl CostTracker {
|
||||||
}
|
}
|
||||||
|
|
||||||
// check each account against account_cost_limit,
|
// check each account against account_cost_limit,
|
||||||
for account_key in writable_accounts.iter() {
|
for account_key in write_lock_accounts {
|
||||||
match self.cost_by_writable_accounts.get(account_key) {
|
match self.cost_by_writable_accounts.get(account_key) {
|
||||||
Some(chained_cost) => {
|
Some(chained_cost) => {
|
||||||
if chained_cost.saturating_add(cost) > self.account_cost_limit {
|
if chained_cost.saturating_add(cost) > self.account_cost_limit {
|
||||||
|
@ -217,9 +240,23 @@ impl CostTracker {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_transaction_cost(&mut self, tx_cost: &TransactionCost) {
|
fn add_transaction_cost(&mut self, tx_cost: &TransactionCost) {
|
||||||
let cost = tx_cost.sum();
|
self.add_transaction_cost_internal(
|
||||||
self.add_transaction_execution_cost(tx_cost, cost);
|
tx_cost.writable_accounts.iter(),
|
||||||
saturating_add_assign!(self.account_data_size, tx_cost.account_data_size);
|
tx_cost.sum(),
|
||||||
|
tx_cost.is_simple_vote,
|
||||||
|
tx_cost.account_data_size,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_transaction_cost_internal<'a>(
|
||||||
|
&mut self,
|
||||||
|
write_lock_accounts: impl Iterator<Item = &'a Pubkey>,
|
||||||
|
cost: u64,
|
||||||
|
is_vote: bool,
|
||||||
|
account_data_size: u64,
|
||||||
|
) {
|
||||||
|
self.add_transaction_execution_cost_internal(write_lock_accounts, is_vote, cost);
|
||||||
|
saturating_add_assign!(self.account_data_size, account_data_size);
|
||||||
saturating_add_assign!(self.transaction_count, 1);
|
saturating_add_assign!(self.transaction_count, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,7 +271,20 @@ impl CostTracker {
|
||||||
|
|
||||||
/// Apply additional actual execution units to cost_tracker
|
/// Apply additional actual execution units to cost_tracker
|
||||||
fn add_transaction_execution_cost(&mut self, tx_cost: &TransactionCost, adjustment: u64) {
|
fn add_transaction_execution_cost(&mut self, tx_cost: &TransactionCost, adjustment: u64) {
|
||||||
for account_key in tx_cost.writable_accounts.iter() {
|
self.add_transaction_execution_cost_internal(
|
||||||
|
tx_cost.writable_accounts.iter(),
|
||||||
|
tx_cost.is_simple_vote,
|
||||||
|
adjustment,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_transaction_execution_cost_internal<'a>(
|
||||||
|
&mut self,
|
||||||
|
write_lock_accounts: impl Iterator<Item = &'a Pubkey>,
|
||||||
|
is_vote: bool,
|
||||||
|
adjustment: u64,
|
||||||
|
) {
|
||||||
|
for account_key in write_lock_accounts {
|
||||||
let account_cost = self
|
let account_cost = self
|
||||||
.cost_by_writable_accounts
|
.cost_by_writable_accounts
|
||||||
.entry(*account_key)
|
.entry(*account_key)
|
||||||
|
@ -242,7 +292,7 @@ impl CostTracker {
|
||||||
*account_cost = account_cost.saturating_add(adjustment);
|
*account_cost = account_cost.saturating_add(adjustment);
|
||||||
}
|
}
|
||||||
self.block_cost = self.block_cost.saturating_add(adjustment);
|
self.block_cost = self.block_cost.saturating_add(adjustment);
|
||||||
if tx_cost.is_simple_vote {
|
if is_vote {
|
||||||
self.vote_cost = self.vote_cost.saturating_add(adjustment);
|
self.vote_cost = self.vote_cost.saturating_add(adjustment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue