From b18414ea05ebf37cd546c2a1b32c9287b1553e6e Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Sun, 13 Sep 2020 11:46:11 -0300 Subject: [PATCH 1/2] implement add and sub for height --- zebra-chain/src/block/height.rs | 64 +++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/zebra-chain/src/block/height.rs b/zebra-chain/src/block/height.rs index e6ef763b3f1..e367e180c37 100644 --- a/zebra-chain/src/block/height.rs +++ b/zebra-chain/src/block/height.rs @@ -1,5 +1,7 @@ use crate::serialization::SerializationError; +use std::ops::{Add, Sub}; + /// The height of a block is the length of the chain back to the genesis block. /// /// # Invariants @@ -41,6 +43,54 @@ impl Height { pub const MAX_AS_U32: u32 = Self::MAX.0; } +// Block heights live in a torsor, they can only be contructed but not computed directly. +// Addition and substraction is done in the underlying types(u32) and not in the heights themselves. +// This u32 numbers are a group and they can be added and substracted normally to get a new integer. +// Having the result of the computation the `Height(X)` constructor can be used to build a new `Height`. + +// N = Height1.U - Height2.U +// Given two `Height`s use the underlying value of them to compute the difference, +// return this number as it is. +impl Sub for Height { + type Output = Option; + + fn sub(self, rhs: Height) -> Option { + self.0.checked_sub(rhs.0) + } +} + +// H = Height(Height.U + N) +// Given a `Height` and a number, sum the underlying value to the number, +// with the result construct a new `Height` and return it. +impl Add for Height { + type Output = Option; + + fn add(self, rhs: u32) -> Option { + let result = self.0.checked_add(rhs); + match result { + Some(h) if (Height(h) <= Height::MAX) => Some(Height(h)), + Some(_) => None, + None => None, + } + } +} + +// H = Height(Height.U - N) +// Given a `Height` and a number, substract the number to the underlying value, +// with the result construct a new `Height` and return it. +impl Sub for Height { + type Output = Option; + + fn sub(self, rhs: u32) -> Option { + let result = self.0.checked_sub(rhs); + match result { + Some(h) if (Height(h) >= Height::MIN) => Some(Height(h)), + Some(_) => None, + None => None, + } + } +} + #[cfg(test)] use proptest::prelude::*; #[cfg(test)] @@ -53,3 +103,17 @@ impl Arbitrary for Height { type Strategy = BoxedStrategy; } + +#[test] +fn operator_tests() { + assert_eq!(Some(Height(2)), Height(1) + 1); + assert_eq!(None, Height::MAX + 1); + + assert_eq!(Some(Height(1)), Height(2) - 1); + assert_eq!(Some(Height(0)), Height(1) - 1); + assert_eq!(None, Height(0) - 1); + + assert_eq!(Some(1), Height(2) - Height(1)); + assert_eq!(Some(0), Height(1) - Height(1)); + assert_eq!(None, Height(0) - Height(1)); +} From 4e3a77479985a2bce82a4cd9fb0d138ecda2fca4 Mon Sep 17 00:00:00 2001 From: Alfredo Garcia Date: Mon, 14 Sep 2020 16:41:55 -0300 Subject: [PATCH 2/2] fix comments, use i32, simplify arms --- zebra-chain/src/block/height.rs | 52 +++++++++++++-------------------- 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/zebra-chain/src/block/height.rs b/zebra-chain/src/block/height.rs index e367e180c37..d66c670e86a 100644 --- a/zebra-chain/src/block/height.rs +++ b/zebra-chain/src/block/height.rs @@ -4,6 +4,10 @@ use std::ops::{Add, Sub}; /// The height of a block is the length of the chain back to the genesis block. /// +/// Block heights can't be added, but they can be *subtracted*, +/// to get a difference of block heights, represented as an `i32`, +/// and height differences can be added to block heights to get new heights. +/// /// # Invariants /// /// Users should not construct block heights greater than `Height::MAX`. @@ -43,50 +47,34 @@ impl Height { pub const MAX_AS_U32: u32 = Self::MAX.0; } -// Block heights live in a torsor, they can only be contructed but not computed directly. -// Addition and substraction is done in the underlying types(u32) and not in the heights themselves. -// This u32 numbers are a group and they can be added and substracted normally to get a new integer. -// Having the result of the computation the `Height(X)` constructor can be used to build a new `Height`. - -// N = Height1.U - Height2.U -// Given two `Height`s use the underlying value of them to compute the difference, -// return this number as it is. impl Sub for Height { - type Output = Option; + type Output = i32; - fn sub(self, rhs: Height) -> Option { - self.0.checked_sub(rhs.0) + fn sub(self, rhs: Height) -> i32 { + (self.0 as i32) - (rhs.0 as i32) } } -// H = Height(Height.U + N) -// Given a `Height` and a number, sum the underlying value to the number, -// with the result construct a new `Height` and return it. -impl Add for Height { +impl Add for Height { type Output = Option; - fn add(self, rhs: u32) -> Option { - let result = self.0.checked_add(rhs); + fn add(self, rhs: i32) -> Option { + let result = ((self.0 as i32) + rhs) as u32; match result { - Some(h) if (Height(h) <= Height::MAX) => Some(Height(h)), - Some(_) => None, - None => None, + h if (Height(h) <= Height::MAX && Height(h) >= Height::MIN) => Some(Height(h)), + _ => None, } } } -// H = Height(Height.U - N) -// Given a `Height` and a number, substract the number to the underlying value, -// with the result construct a new `Height` and return it. -impl Sub for Height { +impl Sub for Height { type Output = Option; - fn sub(self, rhs: u32) -> Option { - let result = self.0.checked_sub(rhs); + fn sub(self, rhs: i32) -> Option { + let result = ((self.0 as i32) - rhs) as u32; match result { - Some(h) if (Height(h) >= Height::MIN) => Some(Height(h)), - Some(_) => None, - None => None, + h if (Height(h) <= Height::MAX && Height(h) >= Height::MIN) => Some(Height(h)), + _ => None, } } } @@ -113,7 +101,7 @@ fn operator_tests() { assert_eq!(Some(Height(0)), Height(1) - 1); assert_eq!(None, Height(0) - 1); - assert_eq!(Some(1), Height(2) - Height(1)); - assert_eq!(Some(0), Height(1) - Height(1)); - assert_eq!(None, Height(0) - Height(1)); + assert_eq!(1, Height(2) - Height(1)); + assert_eq!(0, Height(1) - Height(1)); + assert_eq!(-1, Height(0) - Height(1)); }