Skip to content

Commit

Permalink
feat(zebrad): Refuse to run zebrad when release is too old (#6351)
Browse files Browse the repository at this point in the history
* refuse to run Zebra if it is too old

* update the release checklist to consider the constants

* bring newline back

* apply new end of support code

* attempt to add tests (not working yet)

* move eos to progress task

* move tests

* add acceptance test (not working)

* fix tests

* change to block height checks (ugly code)

* change warn days

* refactor estimated blocks per day, etc

* move end of support code to its own task

* change test

* fix some docs

* move constants

* remove uneeded conversions

* downgrade tracing

* reduce end of support time, fix ci changing debugs to info again

* update instructions

* add failure messages

* cargo lock update

* unify releaase name constant

* change info msg

* clippy fixes

* add a block explorer

* ignore testnet in end of support task

* change panic to 16 weeks

* add some documentation about end of support

* Tweak docs wording

---------

Co-authored-by: teor <teor@riseup.net>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
3 people committed Apr 28, 2023
1 parent 1f639ff commit 58bd898
Show file tree
Hide file tree
Showing 17 changed files with 308 additions and 16 deletions.
17 changes: 15 additions & 2 deletions .github/PULL_REQUEST_TEMPLATE/release-checklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Once you know which versions you want to increment, you can find them in the:

zebrad (rc):
- [ ] zebrad `Cargo.toml`
- [ ] `zebra-network` protocol user agent: https://github.com/ZcashFoundation/zebra/blob/main/zebra-network/src/constants.rs
- [ ] `zebra-network` release version (`RELEASE_VERSION`): https://github.com/ZcashFoundation/zebra/blob/main/zebra-network/src/constants.rs
- [ ] `README.md`
- [ ] `book/src/user/docker.md`

Expand Down Expand Up @@ -123,6 +123,19 @@ From "Keep a Changelog":

</details>

## Release support constants

Needed for the end of support feature. Please update the following constants [in this file](https://github.com/ZcashFoundation/zebra/blob/main/zebrad/src/components/sync/end_of_support.rs):

- [ ] `ESTIMATED_RELEASE_HEIGHT` (required) - Replace with the estimated height you estimate the release will be tagged.
<details>
- Find where the Zcash blockchain tip is now by using a [Zcash explorer](https://zcashblockexplorer.com/blocks) or other tool.
- Consider there are aprox `1152` blocks per day (with the current Zcash `75` seconds spacing).
- So for example if you think the release will be tagged somewhere in the next 3 days you can add `1152 * 3` to the current tip height and use that value here.
</details>

- [ ] `EOS_PANIC_AFTER` (optional) - Replace if you want the release to be valid for a different numbers of days into the future. The default here is 120 days.

## Create the Release

### Create the Release PR
Expand All @@ -131,7 +144,7 @@ After you have the version increments, the updated checkpoints, any missed depen
and the updated changelog:

- [ ] Make sure the PRs with the new checkpoint hashes and missed dependencies are already merged
- [ ] Push the version increments and the updated changelog into a branch
- [ ] Push the version increments, the updated changelog and the release constants into a branch
(for example: `bump-v1.0.0-rc.0` - this needs to be different to the tag name)
- [ ] Create a release PR by adding `&template=release-checklist.md` to the comparing url ([Example](https://github.com/ZcashFoundation/zebra/compare/v1.0.0-rc.0-release?expand=1&template=release-checklist.md)).
- [ ] Add the list of deleted changelog entries as a comment to make reviewing easier.
Expand Down
24 changes: 24 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5211,6 +5211,29 @@ dependencies = [
"tracing-log",
]

[[package]]
name = "tracing-test"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a2c0ff408fe918a94c428a3f2ad04e4afd5c95bbc08fcf868eff750c15728a4"
dependencies = [
"lazy_static",
"tracing-core",
"tracing-subscriber 0.3.17",
"tracing-test-macro",
]

[[package]]
name = "tracing-test-macro"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "258bc1c4f8e2e73a977812ab339d503e6feeb92700f6d07a6de4d321522d5c08"
dependencies = [
"lazy_static",
"quote 1.0.26",
"syn 1.0.109",
]

[[package]]
name = "try-lock"
version = "0.2.4"
Expand Down Expand Up @@ -6246,6 +6269,7 @@ dependencies = [
"tracing-futures",
"tracing-journald",
"tracing-subscriber 0.3.17",
"tracing-test",
"vergen",
"zebra-chain",
"zebra-consensus",
Expand Down
7 changes: 4 additions & 3 deletions book/src/dev/release-process.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ The pre-release version is denoted by appending a hyphen and a series of dot sep

### Supported Releases

Older Zebra versions are always supported until the next Zebra major, minor or patch release. Initially, we can only guarantee support for the latest Zebra release.
We might be able to provide support for earlier releases, or we might ask you to upgrade to the latest release.
Every Zebra version released by the Zcash Foundation is supported up to a specific height. Currently we support each version for about **16 weeks** but this can change from release to release.

Our support periods will be extended as we gain experience with supporting Zebra releases.
When the Zcash chain reaches this end of support height, `zebrad` will shut down and the binary will refuse to start.

Our process is similar to `zcashd`: https://zcash.github.io/zcash/user/release-support.html

Older Zebra versions that only support previous network upgrades will never be supported, because they are operating on an unsupported Zcash chain fork.

Expand Down
4 changes: 4 additions & 0 deletions book/src/user/run.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ structure, and documentation for all of the config options can be found

* `zebrad start` starts a full node.

## Supported versions

Always run a supported version of Zebra, and upgrade it regularly, so it doesn't become unsupported and halt. [More information](https://github.com/ZcashFoundation/zebra/blob/main/book/src/dev/release-process.md#supported-releases).

## Return Codes

- `0`: Application exited successfully
Expand Down
15 changes: 10 additions & 5 deletions zebra-network/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,14 +269,12 @@ pub const MAX_ADDRS_IN_ADDRESS_BOOK: usize =
/// messages from each of our peers.
pub const TIMESTAMP_TRUNCATION_SECONDS: u32 = 30 * 60;

/// The User-Agent string provided by the node.
/// Release version name is used to form user agent string.
/// Can be also used in other parts of Zebra to identify the current release.
///
/// This must be a valid [BIP 14] user agent.
///
/// [BIP 14]: https://github.com/bitcoin/bips/blob/master/bip-0014.mediawiki
//
// TODO: generate this from crate metadata (#2375)
pub const USER_AGENT: &str = "/Zebra:1.0.0-rc.7/";
pub const RELEASE_VERSION: &str = "1.0.0-rc.7";

/// The Zcash network protocol version implemented by this crate, and advertised
/// during connection setup.
Expand Down Expand Up @@ -336,6 +334,13 @@ lazy_static! {
} else {
Regex::new("(access a socket in a way forbidden by its access permissions)|(Only one usage of each socket address)")
}.expect("regex is valid");

/// The User-Agent string provided by the node.
///
/// This must be a valid [BIP 14] user agent.
///
/// [BIP 14]: https://github.com/bitcoin/bips/blob/master/bip-0014.mediawiki
pub static ref USER_AGENT: String = format!("/Zebra:{RELEASE_VERSION}/");
}

/// The timeout for DNS lookups.
Expand Down
2 changes: 1 addition & 1 deletion zebra-rpc/src/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ where
fn get_info(&self) -> Result<GetInfo> {
let response = GetInfo {
build: self.app_version.clone(),
subversion: USER_AGENT.into(),
subversion: USER_AGENT.to_string(),
};

Ok(response)
Expand Down
2 changes: 1 addition & 1 deletion zebra-rpc/src/methods/tests/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ fn snapshot_rpc_getinfo(info: GetInfo, settings: &insta::Settings) {
insta::assert_json_snapshot!("get_info", info, {
".subversion" => dynamic_redaction(|value, _path| {
// assert that the subversion value is user agent
assert_eq!(value.as_str().unwrap(), USER_AGENT);
assert_eq!(value.as_str().unwrap(), USER_AGENT.to_string());
// replace with:
"[SubVersion]"
}),
Expand Down
2 changes: 1 addition & 1 deletion zebra-rpc/src/methods/tests/vectors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ async fn rpc_getinfo() {

// make sure there is a `subversion` field,
// and that is equal to the Zebra user agent.
assert_eq!(get_info.subversion, USER_AGENT);
assert_eq!(get_info.subversion, USER_AGENT.to_string());

mempool.expect_no_requests().await;
state.expect_no_requests().await;
Expand Down
2 changes: 2 additions & 0 deletions zebrad/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ serde_json = { version = "1.0.96", features = ["preserve_order"] }
tempfile = "3.5.0"

hyper = { version = "0.14.26", features = ["http1", "http2", "server"]}
tracing-test = { version = "0.2.4", features = ["no-env-filter"] }

tokio = { version = "1.27.0", features = ["full", "tracing", "test-util"] }
tokio-stream = "0.1.14"

Expand Down
10 changes: 9 additions & 1 deletion zebrad/src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ use abscissa_core::{
use zebra_network::constants::PORT_IN_USE_ERROR;
use zebra_state::constants::{DATABASE_FORMAT_VERSION, LOCK_FILE_ERROR};

use crate::{commands::ZebradCmd, components::tracing::Tracing, config::ZebradConfig};
use crate::{
commands::ZebradCmd,
components::{sync::end_of_support::EOS_PANIC_MESSAGE_HEADER, tracing::Tracing},
config::ZebradConfig,
};

mod entry_point;
use entry_point::EntryPoint;
Expand Down Expand Up @@ -294,6 +298,10 @@ impl Application for ZebradApp {
if LOCK_FILE_ERROR.is_match(error_str) {
return false;
}
// Don't ask users to report old version panics.
if error_str.to_string().contains(EOS_PANIC_MESSAGE_HEADER) {
return false;
}
true
}
color_eyre::ErrorKind::Recoverable(error) => {
Expand Down
13 changes: 12 additions & 1 deletion zebrad/src/commands/start.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,12 +232,16 @@ impl StartCmd {
let progress_task_handle = tokio::spawn(
show_block_chain_progress(
config.network.network,
latest_chain_tip,
latest_chain_tip.clone(),
sync_status.clone(),
)
.in_current_span(),
);

let end_of_support_task_handle = tokio::spawn(
sync::end_of_support::start(config.network.network, latest_chain_tip).in_current_span(),
);

// Give the inbound service more time to clear its queue,
// then start concurrent tasks that can add load to the inbound service
// (by opening more peer connections, so those peers send us requests)
Expand Down Expand Up @@ -267,6 +271,7 @@ impl StartCmd {
pin!(mempool_queue_checker_task_handle);
pin!(tx_gossip_task_handle);
pin!(progress_task_handle);
pin!(end_of_support_task_handle);

// startup tasks
let BackgroundTaskHandles {
Expand Down Expand Up @@ -334,6 +339,11 @@ impl StartCmd {
.expect("unexpected panic in the chain progress task");
}

end_of_support_result = &mut end_of_support_task_handle => end_of_support_result
.expect("unexpected panic in the end of support task")
.map(|_| info!("end of support task exited")),


// Unlike other tasks, we expect the download task to finish while Zebra is running.
groth16_download_result = &mut groth16_download_handle_fused => {
groth16_download_result
Expand Down Expand Up @@ -389,6 +399,7 @@ impl StartCmd {
mempool_queue_checker_task_handle.abort();
tx_gossip_task_handle.abort();
progress_task_handle.abort();
end_of_support_task_handle.abort();

// startup tasks
groth16_download_handle.abort();
Expand Down
1 change: 1 addition & 0 deletions zebrad/src/components/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use crate::{
};

mod downloads;
pub mod end_of_support;
mod gossip;
mod progress;
mod recent_sync_lengths;
Expand Down
102 changes: 102 additions & 0 deletions zebrad/src/components/sync/end_of_support.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
//! End of support checking task.

use std::time::Duration;

use color_eyre::Report;
use lazy_static::lazy_static;

use zebra_chain::{
block::Height,
chain_tip::ChainTip,
parameters::{Network, NetworkUpgrade},
};

use zebra_network::constants::RELEASE_VERSION;

lazy_static! {
/// The name of the current Zebra release.
pub static ref RELEASE_NAME: String = format!("Zebra {}", RELEASE_VERSION);
}

/// The estimated height that this release started to run.
pub const ESTIMATED_RELEASE_HEIGHT: u32 = 2_026_000;

/// The maximum number of days after `ESTIMATED_RELEASE_HEIGHT` where a Zebra server will run
/// without halting.
///
/// Notes:
///
/// - Zebra will exit with a panic if the current tip height is bigger than the `ESTIMATED_RELEASE_HEIGHT`
/// plus this number of days.
pub const EOS_PANIC_AFTER: u32 = 112;

/// The number of days before the end of support where Zebra will display warnings.
pub const EOS_WARN_AFTER: u32 = EOS_PANIC_AFTER - 14;

/// A string which is part of the panic that will be displayed if Zebra halts.
pub const EOS_PANIC_MESSAGE_HEADER: &str = "Zebra refuses to run";

/// A string which is part of the warning that will be displayed if Zebra release is close to halting.
pub const EOS_WARN_MESSAGE_HEADER: &str = "Your Zebra release is too old and it will stop running";

/// The amount of time between end of support checks.
const CHECK_INTERVAL: Duration = Duration::from_secs(60 * 60);

/// Wait a few seconds at startup so `best_tip_height` is always `Some`.
const INITIAL_WAIT: Duration = Duration::from_secs(10);

/// Start the end of support checking task for Mainnet.
pub async fn start(
network: Network,
latest_chain_tip: impl ChainTip + std::fmt::Debug,
) -> Result<(), Report> {
info!("Starting end of support task");

tokio::time::sleep(INITIAL_WAIT).await;

loop {
if network == Network::Mainnet {
if let Some(tip_height) = latest_chain_tip.best_tip_height() {
check(tip_height, network);
}
} else {
info!("Release always valid in Testnet");
}
tokio::time::sleep(CHECK_INTERVAL).await;
}
}

/// Check if the current release is too old and panic if so.
pub fn check(tip_height: Height, network: Network) {
info!("Checking if Zebra release is inside support range ...");

// Get the current block spacing
let target_block_spacing = NetworkUpgrade::target_spacing_for_height(network, tip_height);

// Get the number of blocks per day
let estimated_blocks_per_day =
u32::try_from(chrono::Duration::days(1).num_seconds() / target_block_spacing.num_seconds())
.expect("number is always small enough to fit");

let panic_height =
Height(ESTIMATED_RELEASE_HEIGHT + (EOS_PANIC_AFTER * estimated_blocks_per_day));
let warn_height =
Height(ESTIMATED_RELEASE_HEIGHT + (EOS_WARN_AFTER * estimated_blocks_per_day));

if tip_height > panic_height {
panic!(
"{EOS_PANIC_MESSAGE_HEADER} if the release date is older than {EOS_PANIC_AFTER} days. \
\nRelease name: {}, Estimated release height: {ESTIMATED_RELEASE_HEIGHT} \
\nHint: Download and install the latest Zebra release from: https://github.com/ZcashFoundation/zebra/releases/latest",
*RELEASE_NAME
);
} else if tip_height > warn_height {
warn!(
"{EOS_WARN_MESSAGE_HEADER} at block {}. \
\nRelease name: {}, Estimated release height: {ESTIMATED_RELEASE_HEIGHT} \
\nHint: Download and install the latest Zebra release from: https://github.com/ZcashFoundation/zebra/releases/latest", panic_height.0, RELEASE_NAME.to_string()
);
} else {
info!("Zebra release is supported until block {}, please report bugs at https://github.com/ZcashFoundation/zebra/issues", panic_height.0);
}
}
26 changes: 26 additions & 0 deletions zebrad/tests/acceptance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2214,6 +2214,32 @@ async fn submit_block() -> Result<()> {
common::get_block_template_rpcs::submit_block::run().await
}

/// Check that the the end of support code is called at least once.
#[test]
fn end_of_support_is_checked_at_start() -> Result<()> {
let _init_guard = zebra_test::init();
let testdir = testdir()?.with_config(&mut default_test_config()?)?;
let mut child = testdir.spawn_child(args!["start"])?;

// Give enough time to start up the eos task.
std::thread::sleep(Duration::from_secs(30));

child.kill(false)?;

let output = child.wait_with_output()?;
let output = output.assert_failure()?;

// Zebra started
output.stdout_line_contains("Starting zebrad")?;

// End of support task started.
output.stdout_line_contains("Starting end of support task")?;

// Make sure the command was killed
output.assert_was_killed()?;

Ok(())
}
/// Test `zebra-checkpoints` on mainnet.
///
/// If you want to run this test individually, see the module documentation.
Expand Down
3 changes: 3 additions & 0 deletions zebrad/tests/common/failure_messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ pub const ZEBRA_FAILURE_MESSAGES: &[&str] = &[
// TODO: log these errors in Zebra, and check for them in the Zebra logs?
"Invalid params",
"Method not found",
// Logs related to end of support halting feature.
zebrad::components::sync::end_of_support::EOS_PANIC_MESSAGE_HEADER,
zebrad::components::sync::end_of_support::EOS_WARN_MESSAGE_HEADER,
];

/// Failure log messages from lightwalletd.
Expand Down
Loading

0 comments on commit 58bd898

Please sign in to comment.