1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
//! Configuration for semantic verification which is run in parallel.
use serde::{Deserialize, Serialize};
/// Configuration for parallel semantic verification:
/// <https://zebra.zfnd.org/dev/rfcs/0002-parallel-verification.html#definitions>
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
#[serde(
deny_unknown_fields,
default,
from = "InnerConfig",
into = "InnerConfig"
)]
pub struct Config {
/// Should Zebra make sure that it follows the consensus chain while syncing?
/// This is a developer-only option.
///
/// # Security
///
/// Disabling this option leaves your node vulnerable to some kinds of chain-based attacks.
/// Zebra regularly updates its checkpoints to ensure nodes are following the best chain.
///
/// # Details
///
/// This option is `true` by default, because it prevents some kinds of chain attacks.
///
/// Disabling this option makes Zebra start full validation earlier.
/// It is slower and less secure.
/// To keep checkpoint sync enabled but opt out of the initial VCT fast-sync rollout, set
/// [`vct_fast_sync`](Self::vct_fast_sync) to `false`.
///
/// Zebra requires some checkpoints to simplify validation of legacy network upgrades.
/// Required checkpoints are always active, even when this option is `false`.
///
/// # Deprecation
///
/// For security reasons, this option might be deprecated or ignored in a future Zebra
/// release.
pub checkpoint_sync: bool,
/// Use the verified-commitment-trees fast sync path.
///
/// Unset (the default) means enabled: checkpoint sync folds in verified
/// Sapling/Orchard/Ironwood roots and skips the per-block tree recompute on networks with
/// embedded handoff frontiers. Set to `false` to keep
/// [`checkpoint_sync`](Self::checkpoint_sync) enabled while forcing the legacy per-block
/// recompute in both Archive and Pruned storage modes.
///
/// The fast path only runs under `checkpoint_sync = true`; when checkpoint sync is disabled
/// this option is unused, and *explicitly* setting it to `true` is rejected at startup.
pub vct_fast_sync: Option<bool>,
}
impl Config {
/// Whether the verified-commitment-trees fast sync knob is enabled
/// (`true` unless explicitly disabled).
pub fn vct_fast_sync_enabled(&self) -> bool {
self.vct_fast_sync.unwrap_or(true)
}
/// Validate relationships between consensus configuration options.
pub fn validate(&self) -> Result<(), &'static str> {
if !self.checkpoint_sync && self.vct_fast_sync == Some(true) {
return Err("consensus.vct_fast_sync = true requires consensus.checkpoint_sync = true");
}
Ok(())
}
}
impl From<InnerConfig> for Config {
fn from(
InnerConfig {
checkpoint_sync,
vct_fast_sync,
..
}: InnerConfig,
) -> Self {
Self {
checkpoint_sync,
vct_fast_sync,
}
}
}
impl From<Config> for InnerConfig {
fn from(
Config {
checkpoint_sync,
vct_fast_sync,
}: Config,
) -> Self {
Self {
checkpoint_sync,
vct_fast_sync,
_debug_skip_parameter_preload: false,
}
}
}
/// Inner consensus configuration for backwards compatibility with older `zakura.toml` files,
/// which contain fields that have been removed.
///
/// Rust API callers should use [`Config`].
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
#[serde(deny_unknown_fields, default)]
pub struct InnerConfig {
/// See [`Config`] for more details.
pub checkpoint_sync: bool,
/// See [`Config`] for more details.
///
/// Serialized only when explicitly set, so configs written by this version stay readable by
/// zakurad versions that predate the option.
#[serde(skip_serializing_if = "Option::is_none")]
pub vct_fast_sync: Option<bool>,
#[serde(skip_serializing, rename = "debug_skip_parameter_preload")]
/// Unused config field for backwards compatibility.
pub _debug_skip_parameter_preload: bool,
}
// we like our default configs to be explicit
#[allow(unknown_lints)]
#[allow(clippy::derivable_impls)]
impl Default for Config {
fn default() -> Self {
Self {
checkpoint_sync: true,
vct_fast_sync: None,
}
}
}
impl Default for InnerConfig {
fn default() -> Self {
Self {
checkpoint_sync: Config::default().checkpoint_sync,
vct_fast_sync: Config::default().vct_fast_sync,
_debug_skip_parameter_preload: false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn vct_fast_sync_defaults_enabled_and_converts_through_inner_config() {
assert!(Config::default().vct_fast_sync_enabled());
assert_eq!(Config::default().vct_fast_sync, None);
let force_disabled = Config::from(InnerConfig {
checkpoint_sync: true,
vct_fast_sync: Some(false),
_debug_skip_parameter_preload: false,
});
assert!(force_disabled.checkpoint_sync);
assert!(!force_disabled.vct_fast_sync_enabled());
let inner = InnerConfig::from(force_disabled);
assert_eq!(inner.vct_fast_sync, Some(false));
}
#[test]
fn vct_fast_sync_requires_checkpoint_sync_only_when_explicit() {
let valid_default = Config {
checkpoint_sync: true,
vct_fast_sync: None,
};
assert!(valid_default.validate().is_ok());
let valid_explicit = Config {
checkpoint_sync: true,
vct_fast_sync: Some(true),
};
assert!(valid_explicit.validate().is_ok());
let valid_legacy_recompute = Config {
checkpoint_sync: true,
vct_fast_sync: Some(false),
};
assert!(valid_legacy_recompute.validate().is_ok());
// A pre-VCT config that disables checkpoint sync must keep working: the
// unset option defaults to enabled but is not a contradiction.
let valid_full_verification = Config {
checkpoint_sync: false,
vct_fast_sync: None,
};
assert!(valid_full_verification.validate().is_ok());
let valid_full_verification_explicit_off = Config {
checkpoint_sync: false,
vct_fast_sync: Some(false),
};
assert!(valid_full_verification_explicit_off.validate().is_ok());
let invalid = Config {
checkpoint_sync: false,
vct_fast_sync: Some(true),
};
assert_eq!(
invalid.validate(),
Err("consensus.vct_fast_sync = true requires consensus.checkpoint_sync = true")
);
}
#[test]
fn unset_vct_fast_sync_is_not_serialized() {
let serialized =
toml::to_string(&Config::default()).expect("default config serializes to TOML");
assert!(
!serialized.contains("vct_fast_sync"),
"unset vct_fast_sync must not appear in generated configs: {serialized}"
);
let round_trip: Config =
toml::from_str(&serialized).expect("serialized config deserializes");
assert_eq!(round_trip, Config::default());
let explicit: Config =
toml::from_str("vct_fast_sync = false\n").expect("explicit vct_fast_sync deserializes");
assert_eq!(explicit.vct_fast_sync, Some(false));
assert!(!explicit.vct_fast_sync_enabled());
}
}