diff --git a/.cargo/config.toml b/.cargo/config.toml index b75f6352f..5b0f95da1 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,11 +1,5 @@ # Zebra cargo configuration -# Disabled until we upgrade to abscissa 0.7 or later: -# https://github.com/ZcashFoundation/zebra/issues/5502 -# https://doc.rust-lang.org/cargo/reference/future-incompat-report.html -[future-incompat-report] -frequency = "never" - # Flags that apply to all Zebra crates and configurations [target.'cfg(all())'] rustflags = [ diff --git a/CHANGELOG.md b/CHANGELOG.md index ef19f83b3..960a13e4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ All notable changes to Zebra are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org). +## [Zebra 1.0.0-rc.9](https://github.com/ZcashFoundation/zebra/releases/tag/v1.0.0-rc.9) - XXXX-XX-XX + +In this release ... + +### Breaking Changes + +- The version subcommand has been replaced with a --version/-V flag ([#6801](https://github.com/ZcashFoundation/zebra/pull/6801)) +- Zebra now accepts filters for the start command when no subcommand is provided ([#6801](https://github.com/ZcashFoundation/zebra/pull/6801)) + +... + + ## [Zebra 1.0.0-rc.8](https://github.com/ZcashFoundation/zebra/releases/tag/v1.0.0-rc.8) - 2023-05-10 Starting in this release, Zebra has implemented an "end of support" halt. Just like `zcashd`, the `zebrad` binary will stop running 16 weeks after the last release date. diff --git a/Cargo.lock b/Cargo.lock index a455225f7..54883a320 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,39 +4,36 @@ version = 3 [[package]] name = "abscissa_core" -version = "0.5.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a07677093120a02583717b6dd1ef81d8de1e8d01bd226c83f0f9bdf3e56bb3a" +checksum = "8346a52bf3fb445d5949d144c37360ad2f1d7950cfcc6d4e9e4999b1cd1bd42a" dependencies = [ "abscissa_derive", + "arc-swap", "backtrace", "canonical-path", - "chrono", - "color-backtrace", - "generational-arena", - "gumdrop", - "libc", + "clap 4.3.0", + "color-eyre", + "fs-err", "once_cell", "regex", "secrecy", - "semver 0.9.0", + "semver 1.0.17", "serde", - "signal-hook", "termcolor", "toml 0.5.11", "tracing", "tracing-log", - "tracing-subscriber 0.1.6", + "tracing-subscriber", "wait-timeout", ] [[package]] name = "abscissa_derive" -version = "0.5.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f5722bc48763cb9d81d8427ca05b6aa2842f6632cf8e4c0a29eef9baececcc" +checksum = "55bfb86e57d13c06e482c570826ddcddcc8f07fab916760e8911141d4fda8b62" dependencies = [ - "darling 0.10.2", "ident_case", "proc-macro2 1.0.56", "quote 1.0.27", @@ -142,15 +139,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" -[[package]] -name = "ansi_term" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -dependencies = [ - "winapi", -] - [[package]] name = "ansi_term" version = "0.12.1" @@ -160,18 +148,67 @@ dependencies = [ "winapi", ] +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + [[package]] name = "anstyle" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" +[[package]] +name = "anstyle-parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + [[package]] name = "anyhow" version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +[[package]] +name = "arc-swap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" + [[package]] name = "arrayref" version = "0.3.7" @@ -659,11 +696,8 @@ checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" dependencies = [ "android-tzdata", "iana-time-zone", - "js-sys", "num-traits", "serde", - "time 0.1.45", - "wasm-bindgen", "winapi", ] @@ -722,7 +756,7 @@ version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ - "ansi_term 0.12.1", + "ansi_term", "atty", "bitflags 1.3.2", "strsim 0.8.0", @@ -738,6 +772,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93aae7a4192245f70fe75dd9157fc7b4a5bf53e88d30bd4396f7d8f9284d5acc" dependencies = [ "clap_builder", + "clap_derive", + "once_cell", ] [[package]] @@ -746,9 +782,24 @@ version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f423e341edefb78c9caba2d9c7f7687d0e72e89df3ce3394554754393ac3990" dependencies = [ + "anstream", "anstyle", "bitflags 1.3.2", "clap_lex", + "once_cell", + "strsim 0.10.0", +] + +[[package]] +name = "clap_derive" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "191d9573962933b4027f932c600cd252ce27a8ad5979418fe78e43c07996f27b" +dependencies = [ + "heck 0.4.1", + "proc-macro2 1.0.56", + "quote 1.0.27", + "syn 2.0.15", ] [[package]] @@ -767,17 +818,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "color-backtrace" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65d13f1078cc63c791d0deba0dd43db37c9ec02b311f10bed10b577016f3a957" -dependencies = [ - "atty", - "backtrace", - "termcolor", -] - [[package]] name = "color-eyre" version = "0.6.2" @@ -806,6 +846,12 @@ dependencies = [ "tracing-error", ] +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + [[package]] name = "console" version = "0.15.5" @@ -852,7 +898,7 @@ dependencies = [ "tonic", "tracing", "tracing-core", - "tracing-subscriber 0.3.17", + "tracing-subscriber", ] [[package]] @@ -1074,16 +1120,6 @@ dependencies = [ "syn 2.0.15", ] -[[package]] -name = "darling" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" -dependencies = [ - "darling_core 0.10.2", - "darling_macro 0.10.2", -] - [[package]] name = "darling" version = "0.13.4" @@ -1104,20 +1140,6 @@ dependencies = [ "darling_macro 0.20.1", ] -[[package]] -name = "darling_core" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2 1.0.56", - "quote 1.0.27", - "strsim 0.9.3", - "syn 1.0.109", -] - [[package]] name = "darling_core" version = "0.13.4" @@ -1146,17 +1168,6 @@ dependencies = [ "syn 2.0.15", ] -[[package]] -name = "darling_macro" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" -dependencies = [ - "darling_core 0.10.2", - "quote 1.0.27", - "syn 1.0.109", -] - [[package]] name = "darling_macro" version = "0.13.4" @@ -1483,6 +1494,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "fs-err" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541" + [[package]] name = "funty" version = "2.0.0" @@ -1578,15 +1595,6 @@ dependencies = [ "slab", ] -[[package]] -name = "generational-arena" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d3b771574f62d0548cee0ad9057857e9fc25d7a3335f140c84f6acd0bf601" -dependencies = [ - "cfg-if 0.1.10", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -1671,26 +1679,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "gumdrop" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee50908bc1beeac1f2902e0b4e0cd0d844e716f5ebdc6f0cfc1163fe5e10bcde" -dependencies = [ - "gumdrop_derive", -] - -[[package]] -name = "gumdrop_derive" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90454ce4de40b7ca6a8968b5ef367bdab48413962588d0d2b1638d60090c35d7" -dependencies = [ - "proc-macro2 1.0.56", - "quote 1.0.27", - "syn 1.0.109", -] - [[package]] name = "h2" version = "0.3.18" @@ -2422,15 +2410,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" -[[package]] -name = "matchers" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" -dependencies = [ - "regex-automata", -] - [[package]] name = "matchers" version = "0.1.0" @@ -2456,12 +2435,6 @@ dependencies = [ "rayon", ] -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - [[package]] name = "memchr" version = "2.5.0" @@ -2882,15 +2855,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" -[[package]] -name = "owning_ref" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce" -dependencies = [ - "stable_deref_trait", -] - [[package]] name = "owo-colors" version = "3.5.0" @@ -2973,7 +2937,7 @@ dependencies = [ "instant", "libc", "redox_syscall 0.2.16", - "smallvec 1.10.0", + "smallvec", "winapi", ] @@ -2986,7 +2950,7 @@ dependencies = [ "cfg-if 1.0.0", "libc", "redox_syscall 0.2.16", - "smallvec 1.10.0", + "smallvec", "windows-sys 0.45.0", ] @@ -3939,9 +3903,9 @@ dependencies = [ [[package]] name = "secrecy" -version = "0.6.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9182278ed645df3477a9c27bfee0621c621aa16f6972635f7f795dae3d81070f" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" dependencies = [ "serde", "zeroize", @@ -3977,7 +3941,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ "semver-parser", - "serde", ] [[package]] @@ -3985,6 +3948,9 @@ name = "semver" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +dependencies = [ + "serde", +] [[package]] name = "semver-parser" @@ -4058,7 +4024,7 @@ dependencies = [ "sentry-backtrace", "sentry-core", "tracing-core", - "tracing-subscriber 0.3.17", + "tracing-subscriber", ] [[package]] @@ -4073,7 +4039,7 @@ dependencies = [ "serde", "serde_json", "thiserror", - "time 0.3.21", + "time", "url", "uuid", ] @@ -4163,7 +4129,7 @@ dependencies = [ "serde", "serde_json", "serde_with_macros 3.0.0", - "time 0.3.21", + "time", ] [[package]] @@ -4229,16 +4195,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" -[[package]] -name = "signal-hook" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e31d442c16f047a671b5a71e2161d6e68814012b7f5379d269ebd915fac2729" -dependencies = [ - "libc", - "signal-hook-registry", -] - [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -4269,15 +4225,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "smallvec" -version = "0.6.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" -dependencies = [ - "maybe-uninit", -] - [[package]] name = "smallvec" version = "1.10.0" @@ -4331,12 +4278,6 @@ dependencies = [ "lock_api", ] -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "static_assertions" version = "1.1.0" @@ -4355,12 +4296,6 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -[[package]] -name = "strsim" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" - [[package]] name = "strsim" version = "0.10.0" @@ -4515,17 +4450,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "time" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - [[package]] name = "time" version = "0.3.21" @@ -4879,8 +4803,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09d48f71a791638519505cefafe162606f706c25592e4bde4d97600c0195312e" dependencies = [ "crossbeam-channel", - "time 0.3.21", - "tracing-subscriber 0.3.17", + "time", + "tracing-subscriber", ] [[package]] @@ -4911,7 +4835,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" dependencies = [ "tracing", - "tracing-subscriber 0.3.17", + "tracing-subscriber", ] [[package]] @@ -4922,7 +4846,7 @@ checksum = "0bae117ee14789185e129aaee5d93750abe67fdc5a9a62650452bfe4e122a3a9" dependencies = [ "lazy_static", "tracing", - "tracing-subscriber 0.3.17", + "tracing-subscriber", ] [[package]] @@ -4943,7 +4867,7 @@ checksum = "ba316a74e8fc3c3896a850dba2375928a9fa171b085ecddfc7c054d39970f3fd" dependencies = [ "libc", "tracing-core", - "tracing-subscriber 0.3.17", + "tracing-subscriber", ] [[package]] @@ -4957,35 +4881,18 @@ dependencies = [ "tracing-core", ] -[[package]] -name = "tracing-subscriber" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "192ca16595cdd0661ce319e8eede9c975f227cdaabc4faaefdc256f43d852e45" -dependencies = [ - "ansi_term 0.11.0", - "chrono", - "lazy_static", - "matchers 0.0.1", - "owning_ref", - "regex", - "smallvec 0.6.14", - "tracing-core", - "tracing-log", -] - [[package]] name = "tracing-subscriber" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ - "matchers 0.1.0", + "matchers", "nu-ansi-term", "once_cell", "regex", "sharded-slab", - "smallvec 1.10.0", + "smallvec", "thread_local", "tracing", "tracing-core", @@ -5000,7 +4907,7 @@ checksum = "3a2c0ff408fe918a94c428a3f2ad04e4afd5c95bbc08fcf868eff750c15728a4" dependencies = [ "lazy_static", "tracing-core", - "tracing-subscriber 0.3.17", + "tracing-subscriber", "tracing-test-macro", ] @@ -5157,6 +5064,12 @@ dependencies = [ "serde", ] +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "uuid" version = "1.3.2" @@ -5195,7 +5108,7 @@ dependencies = [ "git2", "rustc_version 0.4.0", "rustversion", - "time 0.3.21", + "time", ] [[package]] @@ -5245,12 +5158,6 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -5830,7 +5737,7 @@ dependencies = [ "tracing", "tracing-error", "tracing-futures", - "tracing-subscriber 0.3.17", + "tracing-subscriber", "zcash_proofs", "zebra-chain", "zebra-node-services", @@ -6001,7 +5908,7 @@ dependencies = [ "tower", "tracing", "tracing-error", - "tracing-subscriber 0.3.17", + "tracing-subscriber", ] [[package]] @@ -6019,7 +5926,7 @@ dependencies = [ "tinyvec", "tokio", "tracing-error", - "tracing-subscriber 0.3.17", + "tracing-subscriber", "zebra-chain", "zebra-node-services", "zebra-rpc", @@ -6032,11 +5939,11 @@ dependencies = [ "abscissa_core", "atty", "chrono", + "clap 4.3.0", "color-eyre", "console-subscriber", "dirs", "futures", - "gumdrop", "hex", "howudoin", "humantime-serde", @@ -6077,7 +5984,7 @@ dependencies = [ "tracing-flame", "tracing-futures", "tracing-journald", - "tracing-subscriber 0.3.17", + "tracing-subscriber", "tracing-test", "vergen", "zebra-chain", diff --git a/deny.toml b/deny.toml index 683aa1001..aa2f1a2cf 100644 --- a/deny.toml +++ b/deny.toml @@ -90,9 +90,10 @@ skip-tree = [ # Optional dependencies # upgrade abscissa (required dependency) and arti (optional dependency) - { name = "darling", version = "=0.10.2" }, { name = "semver", version = "=0.9.0" }, - { name = "tracing-subscriber", version = "=0.1.6" }, + + # wait for packed_simd_2 to upgrade + { name = "libm", version = "=0.1.4" }, # Elasticsearch dependencies diff --git a/zebrad/Cargo.toml b/zebrad/Cargo.toml index 12a9bcc83..2c7566a43 100644 --- a/zebrad/Cargo.toml +++ b/zebrad/Cargo.toml @@ -115,8 +115,8 @@ zebra-node-services = { path = "../zebra-node-services" } zebra-rpc = { path = "../zebra-rpc" } zebra-state = { path = "../zebra-state" } -abscissa_core = "0.5" -gumdrop = { version = "0.7", features = ["default_expr"]} +abscissa_core = "0.7.0" +clap = { version = "4.3.0", features = ["cargo"] } chrono = { version = "0.4.26", default-features = false, features = ["clock", "std"] } humantime-serde = "1.1.1" indexmap = "1.9.3" @@ -191,7 +191,7 @@ vergen = { version = "8.2.1", default-features = false, features = ["cargo", "gi tonic-build = { version = "0.9.2", optional = true } [dev-dependencies] -abscissa_core = { version = "0.5", features = ["testing"] } +abscissa_core = { version = "0.7.0", features = ["testing"] } hex = "0.4.3" jsonrpc-core = "18.0.0" once_cell = "1.18.0" diff --git a/zebrad/src/application.rs b/zebrad/src/application.rs index d5f3e9e81..4b3809593 100644 --- a/zebrad/src/application.rs +++ b/zebrad/src/application.rs @@ -1,27 +1,24 @@ //! Zebrad Abscissa Application -use std::{fmt::Write as _, io::Write as _, process}; +use std::{env, fmt::Write as _, io::Write as _, process, sync::Arc}; use abscissa_core::{ application::{self, AppCell}, - config::{self, Configurable}, + config::CfgCell, status_err, terminal::{component::Terminal, stderr, stdout, ColorChoice}, - Application, Component, FrameworkError, Shutdown, StandardPaths, Version, + Application, Component, Configurable, FrameworkError, Shutdown, StandardPaths, Version, }; use zebra_network::constants::PORT_IN_USE_ERROR; use zebra_state::constants::{DATABASE_FORMAT_VERSION, LOCK_FILE_ERROR}; use crate::{ - commands::ZebradCmd, + commands::EntryPoint, components::{sync::end_of_support::EOS_PANIC_MESSAGE_HEADER, tracing::Tracing}, config::ZebradConfig, }; -mod entry_point; -use entry_point::EntryPoint; - /// See /// Print a fatal error message and exit fn fatal_error(app_name: String, err: &dyn std::error::Error) -> ! { @@ -32,25 +29,6 @@ fn fatal_error(app_name: String, err: &dyn std::error::Error) -> ! { /// Application state pub static APPLICATION: AppCell = AppCell::new(); -/// Obtain a read-only (multi-reader) lock on the application state. -/// -/// Panics if the application state has not been initialized. -pub fn app_reader() -> application::lock::Reader { - APPLICATION.read() -} - -/// Obtain an exclusive mutable lock on the application state. -pub fn app_writer() -> application::lock::Writer { - APPLICATION.write() -} - -/// Obtain a read-only (multi-reader) lock on the application configuration. -/// -/// Panics if the application configuration has not been loaded. -pub fn app_config() -> config::Reader { - config::Reader::new(&APPLICATION) -} - /// Returns the zebrad version for this build, in SemVer 2.0 format. /// /// Includes the git commit and the number of commits since the last version @@ -117,10 +95,10 @@ pub fn user_agent() -> String { } /// Zebrad Application -#[derive(Debug)] +#[derive(Debug, Default)] pub struct ZebradApp { /// Application configuration. - config: Option, + config: CfgCell, /// Application state. state: application::State, @@ -147,21 +125,6 @@ impl ZebradApp { } } -/// Initialize a new application instance. -/// -/// By default no configuration is loaded, and the framework state is -/// initialized to a default, empty state (no components, threads, etc). -#[allow(unknown_lints)] -#[allow(clippy::derivable_impls)] -impl Default for ZebradApp { - fn default() -> Self { - Self { - config: None, - state: application::State::default(), - } - } -} - impl Application for ZebradApp { /// Entrypoint command for this application. type Cmd = EntryPoint; @@ -173,8 +136,8 @@ impl Application for ZebradApp { type Paths = StandardPaths; /// Accessor for application configuration. - fn config(&self) -> &ZebradConfig { - self.config.as_ref().expect("config not loaded") + fn config(&self) -> Arc { + self.config.read() } /// Borrow the application state immutably. @@ -182,34 +145,22 @@ impl Application for ZebradApp { &self.state } - /// Borrow the application state mutably. - fn state_mut(&mut self) -> &mut application::State { - &mut self.state - } - /// Returns the framework components used by this application. fn framework_components( &mut self, - command: &Self::Cmd, + _command: &Self::Cmd, ) -> Result>>, FrameworkError> { - // Automatically use color if we're outputting to a terminal + // TODO: Open a PR in abscissa to add a TerminalBuilder for opting out + // of the `color_eyre::install` part of `Terminal::new` without + // ColorChoice::Never? + + // The Tracing component uses stdout directly and will apply colors + // `if Self::outputs_are_ttys() && config.tracing.use_colors` // - // The `abcissa` docs claim that abscissa implements `Auto`, but it - // does not - except in `color_backtrace` backtraces. - let mut term_colors = self.term_colors(command); - if term_colors == ColorChoice::Auto { - // We want to disable colors on a per-stream basis, but that feature - // can only be implemented inside the terminal component streams. - // Instead, if either output stream is not a terminal, disable - // colors. - // - // We'd also like to check `config.tracing.use_color` here, but the - // config has not been loaded yet. - if !Self::outputs_are_ttys() { - term_colors = ColorChoice::Never; - } - } - let terminal = Terminal::new(term_colors); + // Note: It's important to use `ColorChoice::Never` here to avoid panicking in + // `register_components()` below if `color_eyre::install()` is called + // after `color_spantrace` has been initialized. + let terminal = Terminal::new(ColorChoice::Never); Ok(vec![Box::new(terminal)]) } @@ -394,23 +345,9 @@ impl Application for ZebradApp { .build_global() .expect("unable to initialize rayon thread pool"); - self.config = Some(config); - - let cfg_ref = self - .config - .as_ref() - .expect("config is loaded before register_components"); - - let default_filter = command - .command - .as_ref() - .map(|zcmd| zcmd.default_tracing_filter(command.verbose, command.help)) - .unwrap_or("warn"); - let is_server = command - .command - .as_ref() - .map(ZebradCmd::is_server) - .unwrap_or(false); + let cfg_ref = &config; + let default_filter = command.cmd().default_tracing_filter(command.verbose); + let is_server = command.cmd().is_server(); // Ignore the configured tracing filter for short-lived utility commands let mut tracing_config = cfg_ref.tracing.clone(); @@ -436,7 +373,7 @@ impl Application for ZebradApp { // Activate the global span, so it's visible when we load the other // components. Space is at a premium here, so we use an empty message, // short commit hash, and the unique part of the network name. - let net = &self.config.clone().unwrap().network.network.to_string()[..4]; + let net = &config.network.network.to_string()[..4]; let global_span = if let Some(git_commit) = ZebradApp::git_commit() { error_span!("", zebrad = git_commit, net) } else { @@ -459,7 +396,10 @@ impl Application for ZebradApp { components.push(Box::new(MetricsEndpoint::new(&metrics_config)?)); } - self.state.components.register(components) + self.state.components_mut().register(components)?; + + // Fire callback to signal state in the application lifecycle + self.after_config(config) } /// Load this application's configuration and initialize its components. @@ -468,16 +408,7 @@ impl Application for ZebradApp { // Create and register components with the application. // We do this first to calculate a proper dependency ordering before // application configuration is processed - self.register_components(command)?; - - // Fire callback to signal state in the application lifecycle - let config = self - .config - .take() - .expect("register_components always populates the config"); - self.after_config(config)?; - - Ok(()) + self.register_components(command) } /// Post-configuration lifecycle callback. @@ -487,13 +418,13 @@ impl Application for ZebradApp { /// possible. fn after_config(&mut self, config: Self::Cfg) -> Result<(), FrameworkError> { // Configure components - self.state.components.after_config(&config)?; - self.config = Some(config); + self.state.components_mut().after_config(&config)?; + self.config.set_once(config); Ok(()) } - fn shutdown(&mut self, shutdown: Shutdown) -> ! { + fn shutdown(&self, shutdown: Shutdown) -> ! { // Some OSes require a flush to send all output to the terminal. // zebrad's logging uses Abscissa, so we flush its streams. // @@ -503,25 +434,33 @@ impl Application for ZebradApp { let _ = stdout().lock().flush(); let _ = stderr().lock().flush(); - if let Err(e) = self.state().components.shutdown(self, shutdown) { - let app_name = self.name().to_string(); + let shutdown_result = self.state().components().shutdown(self, shutdown); - // Swap out a fake app so we can trigger the destructor on the original - let _ = std::mem::take(self); + self.state() + .components_mut() + .get_downcast_mut::() + .map(Tracing::shutdown); + + if let Err(e) = shutdown_result { + let app_name = self.name().to_string(); fatal_error(app_name, &e); } - // Swap out a fake app so we can trigger the destructor on the original - let _ = std::mem::take(self); - match shutdown { Shutdown::Graceful => process::exit(0), Shutdown::Forced => process::exit(1), Shutdown::Crash => process::exit(2), } } - - fn version(&self) -> Version { - app_version() - } +} + +/// Boot the given application, parsing subcommand and options from +/// command-line arguments, and terminating when complete. +// +pub fn boot(app_cell: &'static AppCell) -> ! { + let args = + EntryPoint::process_cli_args(env::args_os().collect()).unwrap_or_else(|err| err.exit()); + + ZebradApp::run(app_cell, args); + process::exit(0); } diff --git a/zebrad/src/application/entry_point.rs b/zebrad/src/application/entry_point.rs deleted file mode 100644 index 16f262d13..000000000 --- a/zebrad/src/application/entry_point.rs +++ /dev/null @@ -1,111 +0,0 @@ -//! Zebrad EntryPoint - -use crate::{ - commands::{StartCmd, ZebradCmd}, - config::ZebradConfig, -}; - -use std::path::PathBuf; - -use abscissa_core::{ - command::{Command, Usage}, - config::Configurable, - FrameworkError, Options, Runnable, -}; - -// (See https://docs.rs/abscissa_core/0.5.2/src/abscissa_core/command/entrypoint.rs.html) -/// Toplevel entrypoint command. -/// -/// Handles obtaining toplevel help as well as verbosity settings. -#[derive(Debug, Options)] -pub struct EntryPoint { - /// Path to the configuration file - #[options(short = "c", help = "path to configuration file")] - pub config: Option, - - /// Obtain help about the current command - #[options(short = "h", help = "print help message")] - pub help: bool, - - /// Increase verbosity setting - #[options(short = "v", help = "be verbose")] - pub verbose: bool, - - /// Subcommand to execute. - /// - /// The `command` option will delegate option parsing to the command type, - /// starting at the first free argument. Defaults to start. - #[options(command, default_expr = "Some(ZebradCmd::Start(StartCmd::default()))")] - pub command: Option, -} - -impl EntryPoint { - /// Borrow the underlying command type - fn command(&self) -> &ZebradCmd { - if self.help { - let _ = Usage::for_command::().print_info(); - let _ = Usage::for_command::().print_usage(); - let _ = Usage::for_command::().print_usage(); - std::process::exit(0); - } - - self.command - .as_ref() - .expect("Some(ZebradCmd::Start(StartCmd::default()) as default value") - } -} - -impl Runnable for EntryPoint { - fn run(&self) { - self.command().run() - } -} - -impl Command for EntryPoint { - /// Name of this program as a string - fn name() -> &'static str { - ZebradCmd::name() - } - - /// Description of this program - fn description() -> &'static str { - ZebradCmd::description() - } - - /// Version of this program - fn version() -> &'static str { - ZebradCmd::version() - } - - /// Authors of this program - fn authors() -> &'static str { - ZebradCmd::authors() - } - - /// Get usage information for a particular subcommand (if available) - fn subcommand_usage(command: &str) -> Option { - ZebradCmd::subcommand_usage(command) - } -} - -impl Configurable for EntryPoint { - /// Path to the command's configuration file - fn config_path(&self) -> Option { - match &self.config { - // Use explicit `-c`/`--config` argument if passed - Some(cfg) => Some(cfg.clone()), - - // Otherwise defer to the toplevel command's config path logic - None => self.command.as_ref().and_then(|cmd| cmd.config_path()), - } - } - - /// Process the configuration after it has been loaded, potentially - /// modifying it or returning an error if options are incompatible - fn process_config(&self, config: ZebradConfig) -> Result { - match &self.command { - Some(cmd) => cmd.process_config(config), - None => Ok(config), - } - } -} diff --git a/zebrad/src/bin/zebrad/main.rs b/zebrad/src/bin/zebrad/main.rs index 962be2407..914fcc256 100644 --- a/zebrad/src/bin/zebrad/main.rs +++ b/zebrad/src/bin/zebrad/main.rs @@ -1,8 +1,8 @@ //! Main entry point for Zebrad -use zebrad::application::APPLICATION; +use zebrad::application::{boot, APPLICATION}; /// Process entry point for `zebrad` fn main() { - abscissa_core::boot(&APPLICATION); + boot(&APPLICATION); } diff --git a/zebrad/src/commands.rs b/zebrad/src/commands.rs index a306a5ab8..2f005f799 100644 --- a/zebrad/src/commands.rs +++ b/zebrad/src/commands.rs @@ -2,62 +2,49 @@ mod copy_state; mod download; +mod entry_point; mod generate; mod start; mod tip_height; -mod version; + +#[cfg(test)] +mod tests; use self::ZebradCmd::*; use self::{ copy_state::CopyStateCmd, download::DownloadCmd, generate::GenerateCmd, - tip_height::TipHeightCmd, version::VersionCmd, + tip_height::TipHeightCmd, }; -pub use self::start::StartCmd; +pub use self::{entry_point::EntryPoint, start::StartCmd}; use crate::config::ZebradConfig; -use abscissa_core::{ - config::Override, Command, Configurable, FrameworkError, Help, Options, Runnable, -}; +use abscissa_core::{config::Override, Command, Configurable, FrameworkError, Runnable}; use std::path::PathBuf; /// Zebrad Configuration Filename pub const CONFIG_FILE: &str = "zebrad.toml"; /// Zebrad Subcommands -#[derive(Command, Debug, Options)] +#[derive(Command, Debug, clap::Subcommand)] pub enum ZebradCmd { - /// The `copy-state` subcommand, used to debug cached chain state + /// The `copy-state` subcommand, used to debug cached chain state (expert users only) // TODO: hide this command from users in release builds (#3279) - #[options(help = "copy cached chain state (debug only)")] CopyState(CopyStateCmd), - /// The `download` subcommand - #[options(help = "pre-download required parameter files")] + // The `download` subcommand + /// Pre-download required Zcash Sprout and Sapling parameter files Download(DownloadCmd), - /// The `generate` subcommand - #[options(help = "generate a skeleton configuration")] + /// Generate a default `zebrad.toml` configuration Generate(GenerateCmd), - /// The `help` subcommand - #[options(help = "get usage information, \ - use help for subcommand usage information, \ - or --help flag to see top-level options")] - Help(Help), - - /// The `start` subcommand - #[options(help = "start the application")] + /// Start the application (default command) Start(StartCmd), - /// The `tip-height` subcommand - #[options(help = "get the block height of Zebra's persisted chain state")] + /// Print the tip block height of Zebra's chain state on disk TipHeight(TipHeightCmd), - - /// The `version` subcommand - #[options(help = "display version information")] - Version(VersionCmd), } impl ZebradCmd { @@ -73,27 +60,26 @@ impl ZebradCmd { CopyState(_) | Start(_) => true, // Utility commands that don't use server components - Download(_) | Generate(_) | Help(_) | TipHeight(_) | Version(_) => false, + Download(_) | Generate(_) | TipHeight(_) => false, } } /// Returns the default log level for this command, based on the `verbose` command line flag. /// /// Some commands need to be quiet by default. - pub(crate) fn default_tracing_filter(&self, verbose: bool, help: bool) -> &'static str { + pub(crate) fn default_tracing_filter(&self, verbose: bool) -> &'static str { let only_show_warnings = match self { // Commands that generate quiet output by default. // This output: // - is used by automated tools, or // - needs to be read easily. - Generate(_) | TipHeight(_) | Help(_) | Version(_) => true, + Generate(_) | TipHeight(_) => true, // Commands that generate informative logging output by default. CopyState(_) | Download(_) | Start(_) => false, }; - // set to warn so that usage info is printed without info-level logs from component registration - if help || (only_show_warnings && !verbose) { + if only_show_warnings && !verbose { "warn" } else if only_show_warnings || !verbose { "info" @@ -109,10 +95,8 @@ impl Runnable for ZebradCmd { CopyState(cmd) => cmd.run(), Download(cmd) => cmd.run(), Generate(cmd) => cmd.run(), - ZebradCmd::Help(cmd) => cmd.run(), Start(cmd) => cmd.run(), TipHeight(cmd) => cmd.run(), - Version(cmd) => cmd.run(), } } } diff --git a/zebrad/src/commands/copy_state.rs b/zebrad/src/commands/copy_state.rs index 11d024d2c..ffe9575dd 100644 --- a/zebrad/src/commands/copy_state.rs +++ b/zebrad/src/commands/copy_state.rs @@ -35,7 +35,7 @@ use std::{cmp::min, path::PathBuf}; -use abscissa_core::{config, Command, FrameworkError, Options, Runnable}; +use abscissa_core::{config, Command, FrameworkError, Runnable}; use color_eyre::eyre::{eyre, Report}; use tokio::time::Instant; use tower::{Service, ServiceExt}; @@ -45,6 +45,7 @@ use zebra_state as old_zs; use zebra_state as new_zs; use crate::{ + application::ZebradApp, components::tokio::{RuntimeRun, TokioComponent}, config::ZebradConfig, prelude::*, @@ -54,11 +55,11 @@ use crate::{ /// How often we log info-level progress messages const PROGRESS_HEIGHT_INTERVAL: u32 = 5_000; -/// `copy-state` subcommand -#[derive(Command, Debug, Options)] +/// copy cached chain state (expert users only) +#[derive(Command, Debug, clap::Parser)] pub struct CopyStateCmd { /// Source height that the copy finishes at. - #[options(help = "stop copying at this source height")] + #[clap(long, short, help = "stop copying at this source height")] max_source_height: Option, /// Path to a Zebra config.toml for the target state. @@ -66,26 +67,30 @@ pub struct CopyStateCmd { /// /// Zebra only uses the state options from this config. /// All other options are ignored. - #[options(help = "config file path for the target state (default: ephemeral), \ - the source state uses the main zebrad config")] + #[clap( + long, + short, + help = "config file path for the target state (default: ephemeral), \ + the source state uses the main zebrad config" + )] target_config_path: Option, /// Filter strings which override the config file and defaults - #[options(free, help = "tracing filters which override the zebrad.toml config")] + #[clap(help = "tracing filters which override the zebrad.toml config")] filters: Vec, } impl CopyStateCmd { /// Configure and launch the copy command async fn start(&self) -> Result<(), Report> { - let base_config = app_config().clone(); + let base_config = APPLICATION.config(); let source_config = base_config.state.clone(); // The default load_config impl doesn't actually modify the app config. let target_config = self .target_config_path .as_ref() - .map(|path| app_writer().load_config(path)) + .map(|path| ZebradApp::default().load_config(path)) .transpose()? .map(|app_config| app_config.state) .unwrap_or_else(new_zs::Config::ephemeral); @@ -394,9 +399,9 @@ impl Runnable for CopyStateCmd { target_config_path = ?self.target_config_path, "starting cached chain state copy" ); - let rt = app_writer() - .state_mut() - .components + let rt = APPLICATION + .state() + .components_mut() .get_downcast_mut::() .expect("TokioComponent should be available") .rt diff --git a/zebrad/src/commands/download.rs b/zebrad/src/commands/download.rs index 83881c071..4feefcb9e 100644 --- a/zebrad/src/commands/download.rs +++ b/zebrad/src/commands/download.rs @@ -5,10 +5,10 @@ //! This command should be used if you're launching lots of `zebrad start` instances for testing, //! or you want to include the parameter files in a distribution package. -use abscissa_core::{Command, Options, Runnable}; +use abscissa_core::{Command, Runnable}; -/// `download` subcommand -#[derive(Command, Debug, Default, Options)] +/// Pre-download required Zcash Sprout and Sapling parameter files +#[derive(Command, Debug, Default, clap::Parser)] pub struct DownloadCmd {} impl DownloadCmd { diff --git a/zebrad/src/commands/entry_point.rs b/zebrad/src/commands/entry_point.rs new file mode 100644 index 000000000..5888c0e5e --- /dev/null +++ b/zebrad/src/commands/entry_point.rs @@ -0,0 +1,134 @@ +//! Zebrad EntryPoint + +use std::cmp::min; + +use abscissa_core::{Command, Configurable, FrameworkError, Runnable}; +use clap::Parser; +use std::{ffi::OsString, path::PathBuf}; + +use crate::config::ZebradConfig; + +use super::ZebradCmd; + +/// Toplevel entrypoint command. +/// +/// Handles obtaining toplevel help as well as verbosity settings. +#[derive(Debug, clap::Parser)] +#[clap( + version = clap::crate_version!(), + author="Zcash Foundation ", + help_template = "\ +{name} {version}\n +{author}\n +{usage-heading} {usage}\n +{all-args}\ +" +)] +pub struct EntryPoint { + /// Subcommand to execute. + /// + /// The `command` option will delegate option parsing to the command type, + /// starting at the first free argument. Defaults to start. + #[clap(subcommand)] + pub cmd: Option, + + /// Path to the configuration file + #[clap(long, short, help = "path to configuration file")] + pub config: Option, + + /// Increase verbosity setting + #[clap(long, short, help = "be verbose")] + pub verbose: bool, + + /// Filter strings which override the config file and defaults + // This can be applied to the default start command if no subcommand is provided. + #[clap(help = "tracing filters which override the zebrad.toml config")] + filters: Vec, +} + +impl EntryPoint { + /// Borrow the command in the option + /// + /// # Panics + /// + /// If `cmd` is None + pub fn cmd(&self) -> &ZebradCmd { + self.cmd + .as_ref() + .expect("should default to start if not provided") + } + + /// Returns a string that parses to the default subcommand + pub fn default_cmd_as_str() -> &'static str { + "start" + } + + /// Process command arguments and insert the default subcommand + /// if no subcommand is provided. + pub fn process_cli_args(mut args: Vec) -> clap::error::Result> { + // Check if the provided arguments include a subcommand + let should_add_default_subcommand = EntryPoint::try_parse_from(&args)?.cmd.is_none(); + + // Add the default subcommand to args after the top-level args if cmd is None + if should_add_default_subcommand { + // try_parse_from currently produces an error if the first argument is not the binary name, + let mut num_top_level_args = 1; + + // update last_top_level_arg_idx to the number of top-level args + for (idx, arg) in args.iter().enumerate() { + num_top_level_args = match arg.to_str() { + Some("--verbose" | "-v" | "--version" | "-V" | "--help") => idx + 1, + Some("--config" | "-c") => idx + 2, + _ => num_top_level_args, + } + } + + num_top_level_args = min(num_top_level_args, args.len()); + args.insert(num_top_level_args, EntryPoint::default_cmd_as_str().into()); + } + + Ok(args) + } +} + +impl Runnable for EntryPoint { + fn run(&self) { + self.cmd().run() + } +} + +impl Command for EntryPoint { + /// Name of this program as a string + fn name() -> &'static str { + ZebradCmd::name() + } + + /// Description of this program + fn description() -> &'static str { + ZebradCmd::description() + } + + /// Authors of this program + fn authors() -> &'static str { + ZebradCmd::authors() + } +} + +impl Configurable for EntryPoint { + /// Path to the command's configuration file + fn config_path(&self) -> Option { + match &self.config { + // Use explicit `-c`/`--config` argument if passed + Some(cfg) => Some(cfg.clone()), + + // Otherwise defer to the toplevel command's config path logic + None => self.cmd().config_path(), + } + } + + /// Process the configuration after it has been loaded, potentially + /// modifying it or returning an error if options are incompatible + fn process_config(&self, config: ZebradConfig) -> Result { + self.cmd().process_config(config) + } +} diff --git a/zebrad/src/commands/generate.rs b/zebrad/src/commands/generate.rs index 649e029a1..de9a3019c 100644 --- a/zebrad/src/commands/generate.rs +++ b/zebrad/src/commands/generate.rs @@ -1,13 +1,20 @@ -//! `generate` subcommand - generates a skeleton config. +//! `generate` subcommand - generates a default `zebrad.toml` config. use crate::config::ZebradConfig; -use abscissa_core::{Command, Options, Runnable}; +use abscissa_core::{Command, Runnable}; +use clap::Parser; -/// `generate` subcommand -#[derive(Command, Debug, Options)] +/// Generate a default `zebrad.toml` configuration +#[derive(Command, Debug, Default, Parser)] pub struct GenerateCmd { /// The file to write the generated config to. - #[options(help = "The file to write the generated config to (stdout if unspecified)")] + // + // TODO: use PathBuf here instead, to support non-UTF-8 paths + #[clap( + long, + short, + help = "The file to write the generated config to (stdout if unspecified)" + )] output_file: Option, } diff --git a/zebrad/src/commands/start.rs b/zebrad/src/commands/start.rs index 3de502113..edbc29d29 100644 --- a/zebrad/src/commands/start.rs +++ b/zebrad/src/commands/start.rs @@ -71,7 +71,7 @@ //! //! Some of the diagnostic features are optional, and need to be enabled at compile-time. -use abscissa_core::{config, Command, FrameworkError, Options, Runnable}; +use abscissa_core::{config, Command, FrameworkError, Runnable}; use color_eyre::eyre::{eyre, Report}; use futures::FutureExt; use tokio::{pin, select, sync::oneshot}; @@ -94,17 +94,17 @@ use crate::{ prelude::*, }; -/// `start` subcommand -#[derive(Command, Debug, Options, Default)] +/// Start the application (default command) +#[derive(Command, Debug, Default, clap::Parser)] pub struct StartCmd { /// Filter strings which override the config file and defaults - #[options(free, help = "tracing filters which override the zebrad.toml config")] + #[clap(help = "tracing filters which override the zebrad.toml config")] filters: Vec, } impl StartCmd { async fn start(&self) -> Result<(), Report> { - let config = app_config().clone(); + let config = APPLICATION.config(); info!("initializing node state"); let (_, max_checkpoint_height) = zebra_consensus::router::init_checkpoint_list( @@ -202,9 +202,9 @@ impl StartCmd { // Launch RPC server let (rpc_task_handle, rpc_tx_queue_task_handle, rpc_server) = RpcServer::spawn( - config.rpc, + config.rpc.clone(), #[cfg(feature = "getblocktemplate-rpcs")] - config.mining, + config.mining.clone(), #[cfg(not(feature = "getblocktemplate-rpcs"))] (), app_version(), @@ -428,7 +428,7 @@ impl StartCmd { /// Returns the bound for the state service buffer, /// based on the configurations of the services that use the state concurrently. fn state_buffer_bound() -> usize { - let config = app_config().clone(); + let config = APPLICATION.config(); // Ignore the checkpoint verify limit, because it is very large. // @@ -450,9 +450,9 @@ impl Runnable for StartCmd { /// Start the application. fn run(&self) { info!("Starting zebrad"); - let rt = app_writer() - .state_mut() - .components + let rt = APPLICATION + .state() + .components_mut() .get_downcast_mut::() .expect("TokioComponent should be available") .rt diff --git a/zebrad/src/commands/tests.rs b/zebrad/src/commands/tests.rs new file mode 100644 index 000000000..f87c48b11 --- /dev/null +++ b/zebrad/src/commands/tests.rs @@ -0,0 +1,46 @@ +//! Tests for parsing zebrad commands + +use clap::Parser; + +use crate::commands::ZebradCmd; + +use super::EntryPoint; + +#[test] +fn args_with_subcommand_pass_through() { + let test_cases = [ + (false, true, false, vec!["zebrad"]), + (false, true, true, vec!["zebrad", "-v"]), + (false, true, true, vec!["zebrad", "--verbose"]), + (true, false, false, vec!["zebrad", "-h"]), + (true, false, false, vec!["zebrad", "--help"]), + (false, true, false, vec!["zebrad", "start"]), + (false, true, true, vec!["zebrad", "-v", "start"]), + (false, true, false, vec!["zebrad", "warn"]), + (false, true, false, vec!["zebrad", "start", "warn"]), + (true, false, false, vec!["zebrad", "help", "warn"]), + ]; + + for (should_exit, should_be_start, should_be_verbose, args) in test_cases { + let args = EntryPoint::process_cli_args(args.iter().map(Into::into).collect()); + + if should_exit { + args.expect_err("parsing invalid args or 'help'/'--help' should return an error"); + continue; + } + + let args: Vec = args.expect("args should parse into EntryPoint"); + + let args = + EntryPoint::try_parse_from(args).expect("hardcoded args should parse successfully"); + + assert!(args.config.is_none(), "args.config should be none"); + assert!(args.cmd.is_some(), "args.cmd should not be none"); + assert_eq!( + args.verbose, should_be_verbose, + "process_cli_args should preserve top-level args" + ); + + assert_eq!(matches!(args.cmd(), ZebradCmd::Start(_)), should_be_start,); + } +} diff --git a/zebrad/src/commands/tip_height.rs b/zebrad/src/commands/tip_height.rs index bc045175d..8ace683d5 100644 --- a/zebrad/src/commands/tip_height.rs +++ b/zebrad/src/commands/tip_height.rs @@ -5,7 +5,8 @@ use std::path::PathBuf; -use abscissa_core::{Command, Options, Runnable}; +use abscissa_core::{Application, Command, Runnable}; +use clap::Parser; use color_eyre::eyre::{eyre, Result}; use zebra_chain::{ @@ -15,17 +16,17 @@ use zebra_chain::{ }; use zebra_state::LatestChainTip; -use crate::prelude::app_config; +use crate::prelude::APPLICATION; -/// `zebra-tip-height` subcommand -#[derive(Command, Debug, Options)] +/// Print the tip block height of Zebra's chain state on disk +#[derive(Command, Debug, Default, Parser)] pub struct TipHeightCmd { /// Path to Zebra's cached state. - #[options(help = "path to directory with the Zebra chain state")] + #[clap(long, short, help = "path to directory with the Zebra chain state")] cache_dir: Option, /// The network to obtain the chain tip. - #[options(default = "mainnet", help = "the network of the chain to load")] + #[clap(long, short, help = "the network of the chain to load")] network: Network, } @@ -54,7 +55,7 @@ impl TipHeightCmd { /// Starts a state service using the `cache_dir` and `network` from the provided arguments. fn load_latest_chain_tip(&self) -> LatestChainTip { - let mut config = app_config().state.clone(); + let mut config = APPLICATION.config().state.clone(); if let Some(cache_dir) = self.cache_dir.clone() { config.cache_dir = cache_dir; diff --git a/zebrad/src/commands/version.rs b/zebrad/src/commands/version.rs deleted file mode 100644 index 047b9b12a..000000000 --- a/zebrad/src/commands/version.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! `version` subcommand - -#![allow(clippy::never_loop)] - -use super::ZebradCmd; -use abscissa_core::{Command, Options, Runnable}; - -/// `version` subcommand -#[derive(Command, Debug, Default, Options)] -pub struct VersionCmd {} - -impl Runnable for VersionCmd { - /// Print version message - #[allow(clippy::print_stdout)] - fn run(&self) { - println!("{} {}", ZebradCmd::name(), ZebradCmd::version()); - } -} diff --git a/zebrad/src/components/tokio.rs b/zebrad/src/components/tokio.rs index f4225bebd..802ebc310 100644 --- a/zebrad/src/components/tokio.rs +++ b/zebrad/src/components/tokio.rs @@ -83,7 +83,7 @@ impl RuntimeRun for Runtime { } Err(error) => { warn!(?error, "shutting down Zebra due to an error"); - app_writer().shutdown(Shutdown::Forced); + APPLICATION.shutdown(Shutdown::Forced); } } } diff --git a/zebrad/src/components/tracing/component.rs b/zebrad/src/components/tracing/component.rs index 0cf0456ee..769d6db46 100644 --- a/zebrad/src/components/tracing/component.rs +++ b/zebrad/src/components/tracing/component.rs @@ -47,7 +47,7 @@ pub struct Tracing { /// responsible for flushing any remaining logs when the program terminates. // // Correctness: must be listed last in the struct, so it drops after other drops have logged. - _guard: WorkerGuard, + _guard: Option, } impl Tracing { @@ -94,7 +94,7 @@ impl Tracing { // Builds a lossy NonBlocking logger with a default line limit of 128_000 or an explicit buffer_limit. // The write method queues lines down a bounded channel with this capacity to a worker thread that writes to stdout. // Increments error_counter and drops lines when the buffer is full. - let (non_blocking, _guard) = NonBlockingBuilder::default() + let (non_blocking, worker_guard) = NonBlockingBuilder::default() .buffered_lines_limit(config.buffer_limit.max(100)) .finish(writer); @@ -275,10 +275,21 @@ impl Tracing { initial_filter: filter, #[cfg(feature = "flamegraph")] flamegrapher, - _guard, + _guard: Some(worker_guard), }) } + /// Drops guard for worker thread of non-blocking logger, + /// to flush any remaining logs when the program terminates. + pub fn shutdown(&mut self) { + self.filter_handle.take(); + + #[cfg(feature = "flamegraph")] + self.flamegrapher.take(); + + self._guard.take(); + } + /// Return the currently-active tracing filter. pub fn filter(&self) -> String { if let Some(filter_handle) = self.filter_handle.as_ref() { diff --git a/zebrad/src/components/tracing/endpoint.rs b/zebrad/src/components/tracing/endpoint.rs index 2bd950592..c4c6440de 100644 --- a/zebrad/src/components/tracing/endpoint.rs +++ b/zebrad/src/components/tracing/endpoint.rs @@ -120,9 +120,9 @@ To set the filter, POST the new filter string to /filter: (&Method::GET, "/filter") => Response::builder() .status(StatusCode::OK) .body(Body::from( - app_reader() + APPLICATION .state() - .components + .components() .get_downcast_ref::() .expect("Tracing component should be available") .filter(), @@ -130,9 +130,9 @@ To set the filter, POST the new filter string to /filter: .expect("response with known status code cannot fail"), (&Method::POST, "/filter") => match read_filter(req).await { Ok(filter) => { - app_reader() + APPLICATION .state() - .components + .components() .get_downcast_ref::() .expect("Tracing component should be available") .reload_filter(filter); diff --git a/zebrad/src/prelude.rs b/zebrad/src/prelude.rs index b4d6eb835..b53759073 100644 --- a/zebrad/src/prelude.rs +++ b/zebrad/src/prelude.rs @@ -2,7 +2,7 @@ //! which are generally useful and should be available everywhere. /// Application state accessors -pub use crate::application::{app_config, app_reader, app_writer}; +pub use crate::application::APPLICATION; /// Commonly used Abscissa traits pub use abscissa_core::{Application, Command, Runnable}; diff --git a/zebrad/tests/acceptance.rs b/zebrad/tests/acceptance.rs index 22fb1eeac..c9953fba1 100644 --- a/zebrad/tests/acceptance.rs +++ b/zebrad/tests/acceptance.rs @@ -267,11 +267,11 @@ fn help_no_args() -> Result<()> { is_zebrad_version, &output.output.stdout, "stdout", - "a valid zebrad semantic version", + "are valid zebrad semantic versions", )?; - // Make sure we are in help by looking usage string - output.stdout_line_contains("USAGE:")?; + // Make sure we are in help by looking for the usage string + output.stdout_line_contains("Usage:")?; Ok(()) } @@ -536,7 +536,7 @@ fn version_no_args() -> Result<()> { let testdir = testdir()?.with_config(&mut default_test_config()?)?; - let child = testdir.spawn_child(args!["version"])?; + let child = testdir.spawn_child(args!["--version"])?; let output = child.wait_with_output()?; let output = output.assert_success()?; @@ -558,15 +558,23 @@ fn version_args() -> Result<()> { let testdir = testdir()?.with_config(&mut default_test_config()?)?; let testdir = &testdir; - // unexpected free argument `argument` - let child = testdir.spawn_child(args!["version", "argument"])?; + // unrecognized option `-f` + let child = testdir.spawn_child(args!["tip-height", "-f"])?; let output = child.wait_with_output()?; output.assert_failure()?; - // unrecognized option `-f` - let child = testdir.spawn_child(args!["version", "-f"])?; + // unrecognized option `-f` is ignored + let child = testdir.spawn_child(args!["--version", "-f"])?; let output = child.wait_with_output()?; - output.assert_failure()?; + let output = output.assert_success()?; + + // The output should only contain the version + output.output_check( + is_zebrad_version, + &output.output.stdout, + "stdout", + "a valid zebrad semantic version", + )?; Ok(()) }