add(log): Log the amount of time it takes to rebuild note commitment trees after a chain fork (#4795)
* Update comments about note commitment tree rebuilds * Add info-level logs to time note commitment tree rebuilds * Log the number of rebuilt blocks after a fork, and the time per block * Move humantime formats to zebra-chain * Use human-friendly time formatting
This commit is contained in:
parent
1b17c57bd5
commit
7b1d4527d4
|
@ -6308,6 +6308,7 @@ dependencies = [
|
||||||
"group",
|
"group",
|
||||||
"halo2_proofs 0.2.0",
|
"halo2_proofs 0.2.0",
|
||||||
"hex",
|
"hex",
|
||||||
|
"humantime",
|
||||||
"incrementalmerkletree",
|
"incrementalmerkletree",
|
||||||
"itertools",
|
"itertools",
|
||||||
"jubjub",
|
"jubjub",
|
||||||
|
|
|
@ -13,6 +13,8 @@ proptest-impl = ["proptest", "proptest-derive", "zebra-test", "rand", "rand_chac
|
||||||
bench = ["zebra-test"]
|
bench = ["zebra-test"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
||||||
|
# Cryptography
|
||||||
aes = "0.7.5"
|
aes = "0.7.5"
|
||||||
bech32 = "0.9.0"
|
bech32 = "0.9.0"
|
||||||
bitvec = "1.0.1"
|
bitvec = "1.0.1"
|
||||||
|
@ -22,41 +24,54 @@ blake2s_simd = "1.0.0"
|
||||||
bls12_381 = "0.7.0"
|
bls12_381 = "0.7.0"
|
||||||
bs58 = { version = "0.4.0", features = ["check"] }
|
bs58 = { version = "0.4.0", features = ["check"] }
|
||||||
byteorder = "1.4.3"
|
byteorder = "1.4.3"
|
||||||
chrono = { version = "0.4.19", features = ["serde"] }
|
equihash = "0.1.0"
|
||||||
displaydoc = "0.2.3"
|
|
||||||
fpe = "0.5.1"
|
fpe = "0.5.1"
|
||||||
futures = "0.3.21"
|
|
||||||
group = "0.12.0"
|
group = "0.12.0"
|
||||||
halo2 = { package = "halo2_proofs", version = "0.2.0" }
|
|
||||||
hex = { version = "0.4.3", features = ["serde"] }
|
|
||||||
incrementalmerkletree = "0.3.0"
|
incrementalmerkletree = "0.3.0"
|
||||||
itertools = "0.10.3"
|
|
||||||
jubjub = "0.9.0"
|
jubjub = "0.9.0"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
primitive-types = "0.11.1"
|
primitive-types = "0.11.1"
|
||||||
rand_core = "0.6.3"
|
rand_core = "0.6.3"
|
||||||
ripemd = "0.1.1"
|
ripemd = "0.1.1"
|
||||||
|
|
||||||
serde = { version = "1.0.137", features = ["serde_derive", "rc"] }
|
|
||||||
serde_with = "1.14.0"
|
|
||||||
serde-big-array = "0.4.1"
|
|
||||||
# Matches version used by hdwallet
|
# Matches version used by hdwallet
|
||||||
secp256k1 = { version = "0.21.3", features = ["serde"] }
|
secp256k1 = { version = "0.21.3", features = ["serde"] }
|
||||||
sha2 = { version = "0.9.9", features=["compress"] }
|
sha2 = { version = "0.9.9", features=["compress"] }
|
||||||
static_assertions = "1.1.0"
|
|
||||||
subtle = "2.4.1"
|
subtle = "2.4.1"
|
||||||
thiserror = "1.0.31"
|
|
||||||
uint = "0.9.1"
|
uint = "0.9.1"
|
||||||
x25519-dalek = { version = "1.2.0", features = ["serde"] }
|
x25519-dalek = { version = "1.2.0", features = ["serde"] }
|
||||||
|
|
||||||
|
# ECC deps
|
||||||
|
halo2 = { package = "halo2_proofs", version = "0.2.0" }
|
||||||
orchard = "0.2.0"
|
orchard = "0.2.0"
|
||||||
|
|
||||||
equihash = "0.1.0"
|
|
||||||
zcash_note_encryption = "0.1"
|
|
||||||
zcash_primitives = { version = "0.7.0", features = ["transparent-inputs"] }
|
|
||||||
zcash_encoding = "0.1.0"
|
zcash_encoding = "0.1.0"
|
||||||
zcash_history = "0.3.0"
|
zcash_history = "0.3.0"
|
||||||
|
zcash_note_encryption = "0.1"
|
||||||
|
zcash_primitives = { version = "0.7.0", features = ["transparent-inputs"] }
|
||||||
|
|
||||||
|
# Time
|
||||||
|
chrono = { version = "0.4.19", features = ["serde"] }
|
||||||
|
humantime = "2.1.0"
|
||||||
|
|
||||||
|
# Error Handling & Formatting
|
||||||
|
displaydoc = "0.2.3"
|
||||||
|
static_assertions = "1.1.0"
|
||||||
|
thiserror = "1.0.31"
|
||||||
|
|
||||||
|
# Serialization
|
||||||
|
hex = { version = "0.4.3", features = ["serde"] }
|
||||||
|
serde = { version = "1.0.137", features = ["serde_derive", "rc"] }
|
||||||
|
serde_with = "1.14.0"
|
||||||
|
serde-big-array = "0.4.1"
|
||||||
|
|
||||||
|
# Processing
|
||||||
|
futures = "0.3.21"
|
||||||
|
itertools = "0.10.3"
|
||||||
|
|
||||||
|
# ZF deps
|
||||||
|
ed25519-zebra = "3.0.0"
|
||||||
|
redjubjub = "0.5.0"
|
||||||
|
|
||||||
|
# Optional testing dependencies
|
||||||
proptest = { version = "0.10.1", optional = true }
|
proptest = { version = "0.10.1", optional = true }
|
||||||
proptest-derive = { version = "0.3.0", optional = true }
|
proptest-derive = { version = "0.3.0", optional = true }
|
||||||
|
|
||||||
|
@ -65,19 +80,19 @@ rand_chacha = { version = "0.3.1", optional = true }
|
||||||
|
|
||||||
tokio = { version = "1.20.0", features = ["tracing"], optional = true }
|
tokio = { version = "1.20.0", features = ["tracing"], optional = true }
|
||||||
|
|
||||||
# ZF deps
|
|
||||||
ed25519-zebra = "3.0.0"
|
|
||||||
redjubjub = "0.5.0"
|
|
||||||
|
|
||||||
zebra-test = { path = "../zebra-test/", optional = true }
|
zebra-test = { path = "../zebra-test/", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
color-eyre = "0.6.1"
|
|
||||||
|
# Benchmarks
|
||||||
criterion = { version = "0.3.5", features = ["html_reports"] }
|
criterion = { version = "0.3.5", features = ["html_reports"] }
|
||||||
itertools = "0.10.3"
|
|
||||||
|
# Error Handling & Formatting
|
||||||
|
color-eyre = "0.6.1"
|
||||||
spandoc = "0.2.2"
|
spandoc = "0.2.2"
|
||||||
tracing = "0.1.31"
|
tracing = "0.1.31"
|
||||||
|
|
||||||
|
# Make the optional testing dependencies required
|
||||||
proptest = "0.10.1"
|
proptest = "0.10.1"
|
||||||
proptest-derive = "0.3.0"
|
proptest-derive = "0.3.0"
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,9 @@ use proptest::prelude::*;
|
||||||
#[cfg(any(test, feature = "proptest-impl"))]
|
#[cfg(any(test, feature = "proptest-impl"))]
|
||||||
use proptest_derive::Arbitrary;
|
use proptest_derive::Arbitrary;
|
||||||
|
|
||||||
|
pub mod time;
|
||||||
|
pub use time::{humantime_milliseconds, humantime_seconds};
|
||||||
|
|
||||||
/// Wrapper to override `Debug`, redirecting it to only output the type's name.
|
/// Wrapper to override `Debug`, redirecting it to only output the type's name.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
|
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
//! Human-readable formats for times and durations.
|
||||||
|
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
/// Returns a human-friendly formatted string for the whole number of seconds in `duration`.
|
||||||
|
pub fn humantime_seconds(duration: impl Into<Duration>) -> String {
|
||||||
|
let duration = duration.into();
|
||||||
|
|
||||||
|
// Truncate fractional seconds.
|
||||||
|
let duration = Duration::from_secs(duration.as_secs());
|
||||||
|
|
||||||
|
let duration = humantime::format_duration(duration);
|
||||||
|
|
||||||
|
format!("{}", duration)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a human-friendly formatted string for the whole number of milliseconds in `duration`.
|
||||||
|
pub fn humantime_milliseconds(duration: impl Into<Duration>) -> String {
|
||||||
|
let duration = duration.into();
|
||||||
|
|
||||||
|
// Truncate fractional seconds.
|
||||||
|
let duration_secs = Duration::from_secs(duration.as_secs());
|
||||||
|
let duration_millis = Duration::from_millis(duration.subsec_millis().into());
|
||||||
|
|
||||||
|
let duration = humantime::format_duration(duration_secs + duration_millis);
|
||||||
|
|
||||||
|
format!("{}", duration)
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ use std::{
|
||||||
collections::{BTreeMap, BTreeSet, HashMap, HashSet},
|
collections::{BTreeMap, BTreeSet, HashMap, HashSet},
|
||||||
ops::{Deref, RangeInclusive},
|
ops::{Deref, RangeInclusive},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
use mset::MultiSet;
|
use mset::MultiSet;
|
||||||
|
@ -14,6 +15,7 @@ use tracing::instrument;
|
||||||
use zebra_chain::{
|
use zebra_chain::{
|
||||||
amount::{Amount, NegativeAllowed, NonNegative},
|
amount::{Amount, NegativeAllowed, NonNegative},
|
||||||
block::{self, Height},
|
block::{self, Height},
|
||||||
|
fmt::humantime_milliseconds,
|
||||||
history_tree::HistoryTree,
|
history_tree::HistoryTree,
|
||||||
orchard,
|
orchard,
|
||||||
parameters::Network,
|
parameters::Network,
|
||||||
|
@ -293,11 +295,28 @@ impl Chain {
|
||||||
forked.pop_tip();
|
forked.pop_tip();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Rebuild the note commitment and history trees, starting from the finalized tip tree.
|
||||||
|
//
|
||||||
|
// Note commitments and history trees are not removed from the Chain during a fork,
|
||||||
|
// because we don't support that operation yet. Instead, we recreate the tree
|
||||||
|
// from the finalized tip.
|
||||||
|
//
|
||||||
|
// TODO: remove trees and anchors above the fork, to save CPU time (#4794)
|
||||||
|
let start_time = Instant::now();
|
||||||
|
let rebuilt_block_count = forked.blocks.len();
|
||||||
|
let fork_height = forked.non_finalized_tip_height();
|
||||||
|
|
||||||
|
info!(
|
||||||
|
?rebuilt_block_count,
|
||||||
|
?fork_height,
|
||||||
|
?fork_tip,
|
||||||
|
"starting to rebuild note commitment trees after a non-finalized chain fork",
|
||||||
|
);
|
||||||
|
|
||||||
let sprout_nct = Arc::make_mut(&mut forked.sprout_note_commitment_tree);
|
let sprout_nct = Arc::make_mut(&mut forked.sprout_note_commitment_tree);
|
||||||
let sapling_nct = Arc::make_mut(&mut forked.sapling_note_commitment_tree);
|
let sapling_nct = Arc::make_mut(&mut forked.sapling_note_commitment_tree);
|
||||||
let orchard_nct = Arc::make_mut(&mut forked.orchard_note_commitment_tree);
|
let orchard_nct = Arc::make_mut(&mut forked.orchard_note_commitment_tree);
|
||||||
|
|
||||||
// Rebuild the note commitment trees, starting from the finalized tip tree.
|
|
||||||
for block in forked.blocks.values() {
|
for block in forked.blocks.values() {
|
||||||
for transaction in block.block.transactions.iter() {
|
for transaction in block.block.transactions.iter() {
|
||||||
for sprout_note_commitment in transaction.sprout_note_commitments() {
|
for sprout_note_commitment in transaction.sprout_note_commitments() {
|
||||||
|
@ -339,6 +358,18 @@ impl Chain {
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let rebuild_time = start_time.elapsed();
|
||||||
|
let rebuild_time_per_block =
|
||||||
|
rebuild_time / rebuilt_block_count.try_into().expect("fits in u32");
|
||||||
|
info!(
|
||||||
|
rebuild_time = ?humantime_milliseconds(rebuild_time),
|
||||||
|
rebuild_time_per_block = ?humantime_milliseconds(rebuild_time_per_block),
|
||||||
|
?rebuilt_block_count,
|
||||||
|
?fork_height,
|
||||||
|
?fork_tip,
|
||||||
|
"finished rebuilding note commitment trees after a non-finalized chain fork",
|
||||||
|
);
|
||||||
|
|
||||||
Ok(Some(forked))
|
Ok(Some(forked))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1230,6 +1261,12 @@ impl UpdateWith<Option<transaction::JoinSplitData<Groth16Proof>>> for Chain {
|
||||||
_position: RevertPosition,
|
_position: RevertPosition,
|
||||||
) {
|
) {
|
||||||
if let Some(joinsplit_data) = joinsplit_data {
|
if let Some(joinsplit_data) = joinsplit_data {
|
||||||
|
// Note commitments are not removed from the Chain during a fork,
|
||||||
|
// because we don't support that operation yet. Instead, we
|
||||||
|
// recreate the tree from the finalized tip in Chain::fork().
|
||||||
|
//
|
||||||
|
// TODO: remove trees and anchors above the fork, to save CPU time (#4794)
|
||||||
|
|
||||||
check::nullifier::remove_from_non_finalized_chain(
|
check::nullifier::remove_from_non_finalized_chain(
|
||||||
&mut self.sprout_nullifiers,
|
&mut self.sprout_nullifiers,
|
||||||
joinsplit_data.nullifiers(),
|
joinsplit_data.nullifiers(),
|
||||||
|
@ -1277,9 +1314,11 @@ where
|
||||||
_position: RevertPosition,
|
_position: RevertPosition,
|
||||||
) {
|
) {
|
||||||
if let Some(sapling_shielded_data) = sapling_shielded_data {
|
if let Some(sapling_shielded_data) = sapling_shielded_data {
|
||||||
// Note commitments are not removed from the tree here because we
|
// Note commitments are not removed from the Chain during a fork,
|
||||||
// don't support that operation yet. Instead, we recreate the tree
|
// because we don't support that operation yet. Instead, we
|
||||||
// from the finalized tip in `NonFinalizedState`.
|
// recreate the tree from the finalized tip in Chain::fork().
|
||||||
|
//
|
||||||
|
// TODO: remove trees and anchors above the fork, to save CPU time (#4794)
|
||||||
|
|
||||||
check::nullifier::remove_from_non_finalized_chain(
|
check::nullifier::remove_from_non_finalized_chain(
|
||||||
&mut self.sapling_nullifiers,
|
&mut self.sapling_nullifiers,
|
||||||
|
@ -1322,9 +1361,11 @@ impl UpdateWith<Option<orchard::ShieldedData>> for Chain {
|
||||||
_position: RevertPosition,
|
_position: RevertPosition,
|
||||||
) {
|
) {
|
||||||
if let Some(orchard_shielded_data) = orchard_shielded_data {
|
if let Some(orchard_shielded_data) = orchard_shielded_data {
|
||||||
// Note commitments are not removed from the tree here because we
|
// Note commitments are not removed from the Chain during a fork,
|
||||||
// don't support that operation yet. Instead, we recreate the tree
|
// because we don't support that operation yet. Instead, we
|
||||||
// from the finalized tip in NonFinalizedState.
|
// recreate the tree from the finalized tip in Chain::fork().
|
||||||
|
//
|
||||||
|
// TODO: remove trees and anchors above the fork, to save CPU time (#4794)
|
||||||
|
|
||||||
check::nullifier::remove_from_non_finalized_chain(
|
check::nullifier::remove_from_non_finalized_chain(
|
||||||
&mut self.orchard_nullifiers,
|
&mut self.orchard_nullifiers,
|
||||||
|
|
|
@ -8,11 +8,12 @@ use num_integer::div_ceil;
|
||||||
use zebra_chain::{
|
use zebra_chain::{
|
||||||
block::Height,
|
block::Height,
|
||||||
chain_tip::ChainTip,
|
chain_tip::ChainTip,
|
||||||
|
fmt::humantime_seconds,
|
||||||
parameters::{Network, NetworkUpgrade, POST_BLOSSOM_POW_TARGET_SPACING},
|
parameters::{Network, NetworkUpgrade, POST_BLOSSOM_POW_TARGET_SPACING},
|
||||||
};
|
};
|
||||||
use zebra_consensus::CheckpointList;
|
use zebra_consensus::CheckpointList;
|
||||||
|
|
||||||
use crate::components::{sync::SyncStatus, tracing::humantime_seconds};
|
use crate::components::sync::SyncStatus;
|
||||||
|
|
||||||
/// The amount of time between progress logs.
|
/// The amount of time between progress logs.
|
||||||
const LOG_INTERVAL: Duration = Duration::from_secs(60);
|
const LOG_INTERVAL: Duration = Duration::from_secs(60);
|
||||||
|
|
|
@ -2,14 +2,12 @@
|
||||||
|
|
||||||
mod component;
|
mod component;
|
||||||
mod endpoint;
|
mod endpoint;
|
||||||
mod fmt;
|
|
||||||
|
|
||||||
#[cfg(feature = "flamegraph")]
|
#[cfg(feature = "flamegraph")]
|
||||||
mod flame;
|
mod flame;
|
||||||
|
|
||||||
pub use component::Tracing;
|
pub use component::Tracing;
|
||||||
pub use endpoint::TracingEndpoint;
|
pub use endpoint::TracingEndpoint;
|
||||||
pub use fmt::humantime_seconds;
|
|
||||||
|
|
||||||
#[cfg(feature = "flamegraph")]
|
#[cfg(feature = "flamegraph")]
|
||||||
pub use flame::{layer, Grapher};
|
pub use flame::{layer, Grapher};
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
//! Formatting for traced values.
|
|
||||||
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
/// Returns a human-friendly formatted string for `duration.as_secs()`.
|
|
||||||
pub fn humantime_seconds(duration: impl Into<Duration>) -> String {
|
|
||||||
let duration = duration.into();
|
|
||||||
|
|
||||||
// Truncate fractional seconds.
|
|
||||||
let duration = Duration::from_secs(duration.as_secs());
|
|
||||||
|
|
||||||
let duration = humantime::format_duration(duration);
|
|
||||||
|
|
||||||
format!("{}", duration)
|
|
||||||
}
|
|
Loading…
Reference in New Issue