add(metrics): Track mempool actions and size bucketed by weight (copy of #6972, credit @str4d) (#7019)

* metrics: Track mempool actions and size bucketed by weight

* Fix tests

* draft fix tests

* fix `fix_arbitrary_generated_action_overflows`

* add some docs

* manually derive arbitrary

* remove unused import

---------

Co-authored-by: Jack Grigg <jack@electriccoin.co>
Co-authored-by: Marek <mail@marek.onl>
Co-authored-by: Alfredo Garcia <oxarbitrage@gmail.com>
This commit is contained in:
Pili Guerra 2023-07-19 00:04:32 +01:00 committed by GitHub
parent 1db4f567f7
commit 7f64ff35a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 163 additions and 4 deletions

View File

@ -30,7 +30,9 @@ use crate::{
use itertools::Itertools;
use super::{FieldNotPresent, JoinSplitData, LockTime, Memo, Transaction, UnminedTx};
use super::{
FieldNotPresent, JoinSplitData, LockTime, Memo, Transaction, UnminedTx, VerifiedUnminedTx,
};
/// The maximum number of arbitrary transactions, inputs, or outputs.
///
@ -783,6 +785,52 @@ impl Arbitrary for UnminedTx {
type Strategy = BoxedStrategy<Self>;
}
impl Arbitrary for VerifiedUnminedTx {
type Parameters = ();
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
(
any::<UnminedTx>(),
any::<Amount<NonNegative>>(),
any::<u64>(),
any::<(u16, u16)>().prop_map(|(unpaid_actions, conventional_actions)| {
(
unpaid_actions % conventional_actions.saturating_add(1),
conventional_actions,
)
}),
any::<f32>(),
)
.prop_map(
|(
transaction,
miner_fee,
legacy_sigop_count,
(conventional_actions, mut unpaid_actions),
fee_weight_ratio,
)| {
if unpaid_actions > conventional_actions {
unpaid_actions = conventional_actions;
}
let conventional_actions = conventional_actions as u32;
let unpaid_actions = unpaid_actions as u32;
Self {
transaction,
miner_fee,
legacy_sigop_count,
conventional_actions,
unpaid_actions,
fee_weight_ratio,
}
},
)
.boxed()
}
type Strategy = BoxedStrategy<Self>;
}
// Utility functions
/// Convert `trans` into a fake v5 transaction,

View File

@ -325,7 +325,6 @@ impl From<&Arc<Transaction>> for UnminedTx {
//
// This struct can't be `Eq`, because it contains a `f32`.
#[derive(Clone, PartialEq)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
pub struct VerifiedUnminedTx {
/// The unmined transaction.
pub transaction: UnminedTx,
@ -337,6 +336,13 @@ pub struct VerifiedUnminedTx {
/// transparent inputs and outputs.
pub legacy_sigop_count: u64,
/// The number of conventional actions for `transaction`, as defined by [ZIP-317].
///
/// The number of actions is limited by [`MAX_BLOCK_BYTES`], so it fits in a u32.
///
/// [ZIP-317]: https://zips.z.cash/zip-0317#block-production
pub conventional_actions: u32,
/// The number of unpaid actions for `transaction`,
/// as defined by [ZIP-317] for block production.
///
@ -381,6 +387,7 @@ impl VerifiedUnminedTx {
legacy_sigop_count: u64,
) -> Result<Self, zip317::Error> {
let fee_weight_ratio = zip317::conventional_fee_weight_ratio(&transaction, miner_fee);
let conventional_actions = zip317::conventional_actions(&transaction.transaction);
let unpaid_actions = zip317::unpaid_actions(&transaction, miner_fee);
zip317::mempool_checks(unpaid_actions, miner_fee, transaction.size)?;
@ -390,6 +397,7 @@ impl VerifiedUnminedTx {
miner_fee,
legacy_sigop_count,
fee_weight_ratio,
conventional_actions,
unpaid_actions,
})
}

View File

@ -133,7 +133,7 @@ pub fn conventional_fee_weight_ratio(
/// as defined by [ZIP-317].
///
/// [ZIP-317]: https://zips.z.cash/zip-0317#fee-calculation
fn conventional_actions(transaction: &Transaction) -> u32 {
pub fn conventional_actions(transaction: &Transaction) -> u32 {
let tx_in_total_size: usize = transaction
.inputs()
.iter()

View File

@ -1185,7 +1185,7 @@ async fn rpc_getblocktemplate_mining_address(use_p2pkh: bool) {
block::{Hash, MAX_BLOCK_BYTES, ZCASH_BLOCK_VERSION},
chain_sync_status::MockSyncStatus,
serialization::DateTime32,
transaction::VerifiedUnminedTx,
transaction::{zip317, VerifiedUnminedTx},
work::difficulty::{CompactDifficulty, ExpandedDifficulty, U256},
};
use zebra_consensus::MAX_BLOCK_SIGOPS;
@ -1441,10 +1441,13 @@ async fn rpc_getblocktemplate_mining_address(use_p2pkh: bool) {
conventional_fee: 0.try_into().unwrap(),
};
let conventional_actions = zip317::conventional_actions(&unmined_tx.transaction);
let verified_unmined_tx = VerifiedUnminedTx {
transaction: unmined_tx,
miner_fee: 0.try_into().unwrap(),
legacy_sigop_count: 0,
conventional_actions,
unpaid_actions: 0,
fee_weight_ratio: 1.0,
};

View File

@ -286,10 +286,110 @@ impl VerifiedSet {
}
fn update_metrics(&mut self) {
// Track the sum of unpaid actions within each transaction (as they are subject to the
// unpaid action limit). Transactions that have weight >= 1 have no unpaid actions by
// definition.
let mut unpaid_actions_with_weight_lt20pct = 0;
let mut unpaid_actions_with_weight_lt40pct = 0;
let mut unpaid_actions_with_weight_lt60pct = 0;
let mut unpaid_actions_with_weight_lt80pct = 0;
let mut unpaid_actions_with_weight_lt1 = 0;
// Track the total number of paid actions across all transactions in the mempool. This
// added to the bucketed unpaid actions above is equal to the total number of conventional
// actions in the mempool.
let mut paid_actions = 0;
// Track the sum of transaction sizes (the metric by which they are mainly limited) across
// several buckets.
let mut size_with_weight_lt1 = 0;
let mut size_with_weight_eq1 = 0;
let mut size_with_weight_gt1 = 0;
let mut size_with_weight_gt2 = 0;
let mut size_with_weight_gt3 = 0;
for entry in self.full_transactions() {
paid_actions += entry.conventional_actions - entry.unpaid_actions;
if entry.fee_weight_ratio > 3.0 {
size_with_weight_gt3 += entry.transaction.size;
} else if entry.fee_weight_ratio > 2.0 {
size_with_weight_gt2 += entry.transaction.size;
} else if entry.fee_weight_ratio > 1.0 {
size_with_weight_gt1 += entry.transaction.size;
} else if entry.fee_weight_ratio == 1.0 {
size_with_weight_eq1 += entry.transaction.size;
} else {
size_with_weight_lt1 += entry.transaction.size;
if entry.fee_weight_ratio < 0.2 {
unpaid_actions_with_weight_lt20pct += entry.unpaid_actions;
} else if entry.fee_weight_ratio < 0.4 {
unpaid_actions_with_weight_lt40pct += entry.unpaid_actions;
} else if entry.fee_weight_ratio < 0.6 {
unpaid_actions_with_weight_lt60pct += entry.unpaid_actions;
} else if entry.fee_weight_ratio < 0.8 {
unpaid_actions_with_weight_lt80pct += entry.unpaid_actions;
} else {
unpaid_actions_with_weight_lt1 += entry.unpaid_actions;
}
}
}
metrics::gauge!(
"zcash.mempool.actions.unpaid",
unpaid_actions_with_weight_lt20pct as f64,
"bk" => "< 0.2",
);
metrics::gauge!(
"zcash.mempool.actions.unpaid",
unpaid_actions_with_weight_lt40pct as f64,
"bk" => "< 0.4",
);
metrics::gauge!(
"zcash.mempool.actions.unpaid",
unpaid_actions_with_weight_lt60pct as f64,
"bk" => "< 0.6",
);
metrics::gauge!(
"zcash.mempool.actions.unpaid",
unpaid_actions_with_weight_lt80pct as f64,
"bk" => "< 0.8",
);
metrics::gauge!(
"zcash.mempool.actions.unpaid",
unpaid_actions_with_weight_lt1 as f64,
"bk" => "< 1",
);
metrics::gauge!("zcash.mempool.actions.paid", paid_actions as f64);
metrics::gauge!(
"zcash.mempool.size.transactions",
self.transaction_count() as f64,
);
metrics::gauge!(
"zcash.mempool.size.weighted",
size_with_weight_lt1 as f64,
"bk" => "< 1",
);
metrics::gauge!(
"zcash.mempool.size.weighted",
size_with_weight_eq1 as f64,
"bk" => "1",
);
metrics::gauge!(
"zcash.mempool.size.weighted",
size_with_weight_gt1 as f64,
"bk" => "> 1",
);
metrics::gauge!(
"zcash.mempool.size.weighted",
size_with_weight_gt2 as f64,
"bk" => "> 2",
);
metrics::gauge!(
"zcash.mempool.size.weighted",
size_with_weight_gt3 as f64,
"bk" => "> 3",
);
metrics::gauge!(
"zcash.mempool.size.bytes",
self.transactions_serialized_size as f64,