1use borsh::schema::add_definition;
4use borsh::schema::Definition;
5use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
6use memuse::DynamicUsage;
7use std::cmp::{Ord, Ordering};
8use std::collections::BTreeMap;
9use std::convert::TryFrom;
10use std::fmt;
11use std::io::{Error, ErrorKind, Read, Write};
12use std::ops::{Add, Bound, RangeBounds, Sub};
13
14#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
17#[repr(transparent)]
18#[derive(
19 Clone, Copy, Debug, PartialEq, Eq, Hash, BorshSerialize, BorshDeserialize, BorshSchema,
20)]
21pub struct BlockHeight(u32);
22
23memuse::impl_no_dynamic_usage!(BlockHeight);
24
25pub const H0: BlockHeight = BlockHeight(0);
26
27impl BlockHeight {
28 pub const fn from_u32(v: u32) -> BlockHeight {
29 BlockHeight(v)
30 }
31}
32
33impl fmt::Display for BlockHeight {
34 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
35 self.0.fmt(formatter)
36 }
37}
38
39impl Ord for BlockHeight {
40 fn cmp(&self, other: &Self) -> Ordering {
41 self.0.cmp(&other.0)
42 }
43}
44
45impl PartialOrd for BlockHeight {
46 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
47 Some(self.cmp(other))
48 }
49}
50
51impl From<u32> for BlockHeight {
52 fn from(value: u32) -> Self {
53 BlockHeight(value)
54 }
55}
56
57impl From<BlockHeight> for u32 {
58 fn from(value: BlockHeight) -> u32 {
59 value.0
60 }
61}
62
63impl TryFrom<u64> for BlockHeight {
64 type Error = std::num::TryFromIntError;
65
66 fn try_from(value: u64) -> Result<Self, Self::Error> {
67 u32::try_from(value).map(BlockHeight)
68 }
69}
70
71impl From<BlockHeight> for u64 {
72 fn from(value: BlockHeight) -> u64 {
73 value.0 as u64
74 }
75}
76
77impl TryFrom<i32> for BlockHeight {
78 type Error = std::num::TryFromIntError;
79
80 fn try_from(value: i32) -> Result<Self, Self::Error> {
81 u32::try_from(value).map(BlockHeight)
82 }
83}
84
85impl TryFrom<i64> for BlockHeight {
86 type Error = std::num::TryFromIntError;
87
88 fn try_from(value: i64) -> Result<Self, Self::Error> {
89 u32::try_from(value).map(BlockHeight)
90 }
91}
92
93impl From<BlockHeight> for i64 {
94 fn from(value: BlockHeight) -> i64 {
95 value.0 as i64
96 }
97}
98
99impl Add<u32> for BlockHeight {
100 type Output = Self;
101
102 fn add(self, other: u32) -> Self {
103 BlockHeight(self.0 + other)
104 }
105}
106
107impl Add for BlockHeight {
108 type Output = Self;
109
110 fn add(self, other: Self) -> Self {
111 self + other.0
112 }
113}
114
115impl Sub<u32> for BlockHeight {
116 type Output = Self;
117
118 fn sub(self, other: u32) -> Self {
119 if other > self.0 {
120 panic!("Subtraction resulted in negative block height.");
121 }
122
123 BlockHeight(self.0 - other)
124 }
125}
126
127impl Sub for BlockHeight {
128 type Output = Self;
129
130 fn sub(self, other: Self) -> Self {
131 self - other.0
132 }
133}
134
135pub trait Parameters: Clone {
137 fn activation_height(&self, nu: NetworkUpgrade) -> Option<BlockHeight>;
140
141 fn is_nu_active(&self, nu: NetworkUpgrade, height: BlockHeight) -> bool {
144 self.activation_height(nu).map_or(false, |h| h <= height)
145 }
146}
147
148#[derive(PartialEq, Eq, Copy, Clone, Debug)]
150pub struct MainNetwork;
151
152memuse::impl_no_dynamic_usage!(MainNetwork);
153
154pub const MAIN_NETWORK: MainNetwork = MainNetwork;
155
156impl Parameters for MainNetwork {
157 fn activation_height(&self, nu: NetworkUpgrade) -> Option<BlockHeight> {
158 match nu {
159 NetworkUpgrade::MASP => Some(H0),
160 }
161 }
162}
163
164#[derive(PartialEq, Eq, Copy, Clone, Debug)]
166pub struct TestNetwork;
167
168memuse::impl_no_dynamic_usage!(TestNetwork);
169
170pub const TEST_NETWORK: TestNetwork = TestNetwork;
171
172impl Parameters for TestNetwork {
173 fn activation_height(&self, nu: NetworkUpgrade) -> Option<BlockHeight> {
174 match nu {
175 NetworkUpgrade::MASP => Some(BlockHeight(1)), }
177 }
178}
179
180#[derive(PartialEq, Eq, Copy, Clone, Debug)]
181pub enum Network {
182 MainNetwork,
183 TestNetwork,
184}
185
186memuse::impl_no_dynamic_usage!(Network);
187
188impl Parameters for Network {
189 fn activation_height(&self, nu: NetworkUpgrade) -> Option<BlockHeight> {
190 match self {
191 Network::MainNetwork => MAIN_NETWORK.activation_height(nu),
192 Network::TestNetwork => TEST_NETWORK.activation_height(nu),
193 }
194 }
195}
196
197#[derive(Clone, Copy, Debug)]
202pub enum NetworkUpgrade {
203 MASP,
207}
208
209memuse::impl_no_dynamic_usage!(NetworkUpgrade);
210
211impl fmt::Display for NetworkUpgrade {
212 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213 match self {
214 NetworkUpgrade::MASP => write!(f, "MASP"),
215 }
216 }
217}
218
219impl NetworkUpgrade {
220 fn branch_id(self) -> BranchId {
221 match self {
222 NetworkUpgrade::MASP => BranchId::MASP,
223 }
224 }
225}
226
227const UPGRADES_IN_ORDER: &[NetworkUpgrade] = &[NetworkUpgrade::MASP];
232
233pub const ZIP212_GRACE_PERIOD: u32 = 0;
234
235#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
249#[derive(Clone, Copy, Debug, PartialEq, Eq)]
250pub enum BranchId {
251 MASP,
253}
254
255memuse::impl_no_dynamic_usage!(BranchId);
256
257impl TryFrom<u32> for BranchId {
258 type Error = &'static str;
259
260 fn try_from(value: u32) -> Result<Self, Self::Error> {
261 match value {
262 0xe9ff_75a6 => Ok(BranchId::MASP),
263 _ => Err("Unknown consensus branch ID"),
264 }
265 }
266}
267
268impl From<BranchId> for u32 {
269 fn from(consensus_branch_id: BranchId) -> u32 {
270 match consensus_branch_id {
271 BranchId::MASP => 0xe9ff_75a6,
272 }
273 }
274}
275
276impl BorshSerialize for BranchId {
277 fn serialize<W: Write>(&self, writer: &mut W) -> std::io::Result<()> {
278 u32::from(*self).serialize(writer)
279 }
280}
281
282impl BorshDeserialize for BranchId {
283 fn deserialize_reader<R: Read>(reader: &mut R) -> std::io::Result<Self> {
284 u32::deserialize_reader(reader)?
285 .try_into()
286 .map_err(|x| Error::new(ErrorKind::InvalidInput, x))
287 }
288}
289
290impl BorshSchema for BranchId {
291 fn add_definitions_recursively(
292 definitions: &mut BTreeMap<borsh::schema::Declaration, borsh::schema::Definition>,
293 ) {
294 let definition = Definition::Enum {
295 tag_width: 4,
296 variants: vec![(0xe9ff_75a6, "MASP".into(), <()>::declaration())],
297 };
298 add_definition(Self::declaration(), definition, definitions);
299 <()>::add_definitions_recursively(definitions);
300 }
301
302 fn declaration() -> borsh::schema::Declaration {
303 "BranchId".into()
304 }
305}
306
307impl BranchId {
308 pub fn for_height<P: Parameters>(parameters: &P, height: BlockHeight) -> Self {
313 for nu in UPGRADES_IN_ORDER.iter().rev() {
314 if parameters.is_nu_active(*nu, height) {
315 return nu.branch_id();
316 }
317 }
318
319 BranchId::MASP
321 }
322
323 pub fn height_range<P: Parameters>(&self, params: &P) -> Option<impl RangeBounds<BlockHeight>> {
327 self.height_bounds(params).map(|(lower, upper)| {
328 (
329 Bound::Included(lower),
330 upper.map_or(Bound::Unbounded, Bound::Excluded),
331 )
332 })
333 }
334
335 pub fn height_bounds<P: Parameters>(
344 &self,
345 params: &P,
346 ) -> Option<(BlockHeight, Option<BlockHeight>)> {
347 match self {
348 BranchId::MASP => params.activation_height(NetworkUpgrade::MASP).map(|lower| {
349 let upper = None;
350 (lower, upper)
351 }),
352 }
353 }
354}
355
356#[cfg(any(test, feature = "test-dependencies"))]
357pub mod testing {
358 use proptest::sample::select;
359 use proptest::strategy::{Just, Strategy};
360
361 use super::{BlockHeight, BranchId, Parameters};
362
363 pub fn arb_branch_id() -> impl Strategy<Value = BranchId> {
364 select(vec![BranchId::MASP])
365 }
366
367 pub fn arb_height<P: Parameters>(
368 branch_id: BranchId,
369 params: &P,
370 ) -> impl Strategy<Value = Option<BlockHeight>> {
371 branch_id
372 .height_bounds(params)
373 .map_or(Strategy::boxed(Just(None)), |(lower, upper)| {
374 Strategy::boxed(
375 (lower.0..upper.map_or(u32::MAX, |u| u.0)).prop_map(|h| Some(BlockHeight(h))),
376 )
377 })
378 }
379}
380
381#[cfg(test)]
382mod tests {
383 use std::convert::TryFrom;
384
385 use super::{
386 BlockHeight, BranchId, NetworkUpgrade, Parameters, MAIN_NETWORK, UPGRADES_IN_ORDER,
387 };
388
389 #[test]
390 fn nu_ordering() {
391 for i in 1..UPGRADES_IN_ORDER.len() {
392 let nu_a = UPGRADES_IN_ORDER[i - 1];
393 let nu_b = UPGRADES_IN_ORDER[i];
394 match (
395 MAIN_NETWORK.activation_height(nu_a),
396 MAIN_NETWORK.activation_height(nu_b),
397 ) {
398 (Some(a), Some(b)) if a < b => (),
399 (Some(_), None) => (),
400 (None, None) => (),
401 _ => panic!(
402 "{} should not be before {} in UPGRADES_IN_ORDER",
403 nu_a, nu_b
404 ),
405 }
406 }
407 }
408
409 #[test]
410 fn nu_is_active() {
411 assert!(MAIN_NETWORK.is_nu_active(NetworkUpgrade::MASP, BlockHeight(0)));
412 }
413
414 #[test]
415 fn branch_id_from_u32() {
416 assert_eq!(BranchId::try_from(3925833126), Ok(BranchId::MASP));
417 assert!(BranchId::try_from(1).is_err());
418 }
419
420 #[test]
421 fn branch_id_for_height() {
422 assert_eq!(
423 BranchId::for_height(&MAIN_NETWORK, BlockHeight(0)),
424 BranchId::MASP,
425 );
426 }
427}