فهرست منبع

src/time: Implement and use {over,under}flow-safe API

parazyd 2 سال پیش
والد
کامیت
1297ff7b07

+ 2 - 1
bin/darkfid/src/rpc.rs

@@ -93,7 +93,8 @@ impl Darkfid {
     // --> {"jsonrpc": "2.0", "method": "clock", "params": [], "id": 1}
     // <-- {"jsonrpc": "2.0", "result": "1234", "id": 1}
     async fn clock(&self, id: u16, _params: JsonValue) -> JsonResult {
-        JsonResponse::new(JsonValue::String(Timestamp::current_time().0.to_string()), id).into()
+        JsonResponse::new(JsonValue::String(Timestamp::current_time().inner().to_string()), id)
+            .into()
     }
 
     // RPCAPI:

+ 1 - 2
bin/darkfid/src/tests/harness.rs

@@ -235,8 +235,7 @@ impl Harness {
         tx.signatures = vec![sigs];
 
         // We increment timestamp so we don't have to use sleep
-        let mut timestamp = previous.header.timestamp;
-        timestamp.add(1);
+        let timestamp = previous.header.timestamp.checked_add(1.into())?;
 
         // Generate header
         let header = Header::new(previous.hash()?, block_height, timestamp, last_nonce);

+ 6 - 6
bin/tau/taud/src/jsonrpc.rs

@@ -259,7 +259,7 @@ impl JsonRpcInterface {
 
         let due = match params["due"] {
             JsonValue::Null => None,
-            JsonValue::Number(numba) => Some(Timestamp(numba as u64)),
+            JsonValue::Number(numba) => Some(Timestamp::from_u64(numba as u64)),
             _ => return Err(TaudError::InvalidData("Invalid parameter \"due\"".to_string())),
         };
 
@@ -323,7 +323,7 @@ impl JsonRpcInterface {
             &self.nickname,
             due,
             rank,
-            Timestamp(created_at.unwrap()),
+            Timestamp::from_u64(created_at.unwrap()),
         )?;
         new_task.set_project(&projects);
         new_task.set_assign(&assigns);
@@ -360,7 +360,7 @@ impl JsonRpcInterface {
 
         let month = match params[0].get::<String>() {
             Some(u64_str) => match u64_str.parse::<u64>() {
-                Ok(v) => Some(Timestamp(v)),
+                Ok(v) => Some(Timestamp::from_u64(v)),
                 //Err(e) => return Err(TaudError::InvalidData(e.to_string())),
                 Err(_) => None,
             },
@@ -492,7 +492,7 @@ impl JsonRpcInterface {
 
         let month = match params[0].get::<String>() {
             Some(u64_str) => match u64_str.parse::<u64>() {
-                Ok(v) => Some(Timestamp(v)),
+                Ok(v) => Some(Timestamp::from_u64(v)),
                 //Err(e) => return Err(TaudError::InvalidData(e.to_string())),
                 Err(_) => None,
             },
@@ -520,7 +520,7 @@ impl JsonRpcInterface {
 
         let month = match params[1].get::<String>() {
             Some(u64_str) => match u64_str.parse::<u64>() {
-                Ok(v) => Some(Timestamp(v)),
+                Ok(v) => Some(Timestamp::from_u64(v)),
                 //Err(e) => return Err(TaudError::InvalidData(e.to_string())),
                 Err(_) => None,
             },
@@ -696,7 +696,7 @@ impl JsonRpcInterface {
             match &fields["due"] {
                 JsonValue::Null => set_event(&mut task, "due", &self.nickname, "None"),
                 JsonValue::Number(ts_num) => {
-                    task.set_due(Some(Timestamp(*ts_num as u64)));
+                    task.set_due(Some(Timestamp::from_u64(*ts_num as u64)));
                     set_event(&mut task, "due", &self.nickname, &ts_num.to_string())
                 }
                 _ => unreachable!(),

+ 6 - 3
bin/tau/taud/src/month_tasks.rs

@@ -52,7 +52,7 @@ impl From<MonthTasks> for JsonValue {
             mt.deactive_tks.iter().map(|x| JsonValue::String(x.clone())).collect();
 
         JsonValue::Object(HashMap::from([
-            ("created_at".to_string(), JsonValue::String(mt.created_at.0.to_string())),
+            ("created_at".to_string(), JsonValue::String(mt.created_at.inner().to_string())),
             ("active_tks".to_string(), JsonValue::Array(active_tks)),
             ("deactive_tks".to_string(), JsonValue::Array(deactive_tks)),
         ]))
@@ -63,7 +63,7 @@ impl From<JsonValue> for MonthTasks {
     fn from(value: JsonValue) -> MonthTasks {
         let created_at = {
             let u64_str = value["created_at"].get::<String>().unwrap();
-            Timestamp(u64_str.parse::<u64>().unwrap())
+            Timestamp::from_u64(u64_str.parse::<u64>().unwrap())
         };
 
         let active_tks: Vec<String> = value["active_tks"]
@@ -134,7 +134,10 @@ impl MonthTasks {
     fn get_path(date: &Timestamp, dataset_path: &Path) -> PathBuf {
         debug!(target: "tau", "MonthTasks::get_path()");
         dataset_path.join("month").join(
-            Utc.timestamp_opt(date.0.try_into().unwrap(), 0).unwrap().format("%m%y").to_string(),
+            Utc.timestamp_opt(date.inner().try_into().unwrap(), 0)
+                .unwrap()
+                .format("%m%y")
+                .to_string(),
         )
     }
 

+ 12 - 8
bin/tau/taud/src/task_info.rs

@@ -123,7 +123,7 @@ impl From<TaskEvent> for JsonValue {
             ("action".to_string(), JsonValue::String(task_event.action.clone())),
             ("author".to_string(), JsonValue::String(task_event.author.clone())),
             ("content".to_string(), JsonValue::String(task_event.content.clone())),
-            ("timestamp".to_string(), JsonValue::String(task_event.timestamp.0.to_string())),
+            ("timestamp".to_string(), JsonValue::String(task_event.timestamp.inner().to_string())),
         ]))
     }
 }
@@ -135,7 +135,9 @@ impl From<&JsonValue> for TaskEvent {
             action: map["action"].get::<String>().unwrap().clone(),
             author: map["author"].get::<String>().unwrap().clone(),
             content: map["content"].get::<String>().unwrap().clone(),
-            timestamp: Timestamp(map["timestamp"].get::<String>().unwrap().parse::<u64>().unwrap()),
+            timestamp: Timestamp::from_u64(
+                map["timestamp"].get::<String>().unwrap().parse::<u64>().unwrap(),
+            ),
         }
     }
 }
@@ -158,7 +160,7 @@ impl From<Comment> for JsonValue {
         JsonValue::Object(HashMap::from([
             ("content".to_string(), JsonValue::String(comment.content.clone())),
             ("author".to_string(), JsonValue::String(comment.author.clone())),
-            ("timestamp".to_string(), JsonValue::String(comment.timestamp.0.to_string())),
+            ("timestamp".to_string(), JsonValue::String(comment.timestamp.inner().to_string())),
         ]))
     }
 }
@@ -169,7 +171,9 @@ impl From<JsonValue> for Comment {
         Comment {
             content: map["content"].get::<String>().unwrap().clone(),
             author: map["author"].get::<String>().unwrap().clone(),
-            timestamp: Timestamp(map["timestamp"].get::<String>().unwrap().parse::<u64>().unwrap()),
+            timestamp: Timestamp::from_u64(
+                map["timestamp"].get::<String>().unwrap().parse::<u64>().unwrap(),
+            ),
         }
     }
 }
@@ -218,7 +222,7 @@ impl From<&TaskInfo> for JsonValue {
             task.project.iter().map(|x| JsonValue::String(x.clone())).collect();
 
         let due = if let Some(ts) = task.due {
-            JsonValue::String(ts.0.to_string())
+            JsonValue::String(ts.inner().to_string())
         } else {
             JsonValue::Null
         };
@@ -229,7 +233,7 @@ impl From<&TaskInfo> for JsonValue {
             JsonValue::Null
         };
 
-        let created_at = JsonValue::String(task.created_at.0.to_string());
+        let created_at = JsonValue::String(task.created_at.inner().to_string());
         let state = JsonValue::String(task.state.clone());
         let events: Vec<JsonValue> = task.events.iter().map(|x| x.clone().into()).collect();
         let comments: Vec<JsonValue> = task.comments.iter().map(|x| x.clone().into()).collect();
@@ -266,7 +270,7 @@ impl From<JsonValue> for TaskInfo {
                 None
             } else {
                 let u64_str = value["due"].get::<String>().unwrap();
-                Some(Timestamp(u64_str.parse::<u64>().unwrap()))
+                Some(Timestamp::from_u64(u64_str.parse::<u64>().unwrap()))
             }
         };
 
@@ -280,7 +284,7 @@ impl From<JsonValue> for TaskInfo {
 
         let created_at = {
             let u64_str = value["created_at"].get::<String>().unwrap();
-            Timestamp(u64_str.parse::<u64>().unwrap())
+            Timestamp::from_u64(u64_str.parse::<u64>().unwrap())
         };
 
         let events: Vec<TaskEvent> = events.iter().map(|x| x.into()).collect();

+ 4 - 4
src/blockchain/block_store.rs

@@ -29,7 +29,7 @@ use darkfi_serial::async_trait;
 use darkfi_serial::{deserialize, serialize, Encodable, SerialDecodable, SerialEncodable};
 use num_bigint::BigUint;
 
-use crate::{tx::Transaction, Error, Result};
+use crate::{tx::Transaction, util::time::Timestamp, Error, Result};
 
 use super::{parse_record, parse_u64_key_record, Header, SledDbOverlayPtr};
 
@@ -499,7 +499,7 @@ pub struct BlockDifficulty {
     /// Block height number
     pub height: u64,
     /// Block creation timestamp
-    pub timestamp: u64,
+    pub timestamp: Timestamp,
     /// Height difficulty
     pub difficulty: BigUint,
     /// Height cummulative difficulty (total + height difficulty)
@@ -509,7 +509,7 @@ pub struct BlockDifficulty {
 impl BlockDifficulty {
     pub fn new(
         height: u64,
-        timestamp: u64,
+        timestamp: Timestamp,
         difficulty: BigUint,
         cummulative_difficulty: BigUint,
     ) -> Self {
@@ -533,7 +533,7 @@ impl darkfi_serial::Encodable for BlockDifficulty {
 impl darkfi_serial::Decodable for BlockDifficulty {
     fn decode<D: std::io::Read>(mut d: D) -> std::io::Result<Self> {
         let height: u64 = darkfi_serial::Decodable::decode(&mut d)?;
-        let timestamp: u64 = darkfi_serial::Decodable::decode(&mut d)?;
+        let timestamp: Timestamp = darkfi_serial::Decodable::decode(&mut d)?;
         let bytes: Vec<u8> = darkfi_serial::Decodable::decode(&mut d)?;
         let difficulty: BigUint = BigUint::from_bytes_be(&bytes);
         let bytes: Vec<u8> = darkfi_serial::Decodable::decode(&mut d)?;

+ 1 - 1
src/contract/test-harness/src/lib.rs

@@ -196,7 +196,7 @@ impl TestHarness {
     pub async fn new(holders: &[Holder], verify_fees: bool) -> Result<Self> {
         // Create a genesis block
         let mut genesis_block = BlockInfo::default();
-        genesis_block.header.timestamp = Timestamp(1689772567);
+        genesis_block.header.timestamp = Timestamp::from_u64(1689772567);
         let producer_tx = genesis_block.txs.pop().unwrap();
         genesis_block.append_txs(vec![producer_tx])?;
 

+ 1 - 2
src/contract/test-harness/src/money_pow_reward.rs

@@ -111,8 +111,7 @@ impl TestHarness {
         let previous = wallet.validator.blockchain.last_block()?;
 
         // We increment timestamp so we don't have to use sleep
-        let mut timestamp = previous.header.timestamp;
-        timestamp.add(1);
+        let timestamp = previous.header.timestamp.checked_add(1.into())?;
 
         // Generate block header
         let header = Header::new(

+ 6 - 0
src/error.rs

@@ -493,6 +493,12 @@ pub enum Error {
     #[error("Detached task stopped")]
     DetachedTaskStopped,
 
+    #[error("Addition overflow")]
+    AdditionOverflow,
+
+    #[error("Subtraction underflow")]
+    SubtractionUnderflow,
+
     // ==============================================
     // Wrappers for other error types in this library
     // ==============================================

+ 6 - 10
src/rpc/clock_sync.rs

@@ -46,7 +46,7 @@ pub async fn ntp_request() -> Result<Timestamp> {
     sock.recv(&mut packet[..])?;
     let (bytes, _) = packet[40..44].split_at(core::mem::size_of::<u32>());
     let num = u32::from_be_bytes(bytes.try_into().unwrap());
-    let timestamp = Timestamp((num - EPOCH) as u64);
+    let timestamp = Timestamp::from_u64((num - EPOCH) as u64);
 
     Ok(timestamp)
 }
@@ -85,24 +85,20 @@ async fn clock_check(_peers: &[Url]) -> Result<()> {
     // Start elapsed time counter to cover for NTP request and processing time
     let ntp_request_start = Timestamp::current_time();
     // Poll ntp.org for current timestamp
-    let mut ntp_time = ntp_request().await?;
+    let ntp_time = ntp_request().await?;
 
     // Stop elapsed time counters
-    let ntp_elapsed_time = ntp_request_start.elapsed();
-    let requests_elapsed_time = requests_start.elapsed();
+    let ntp_elapsed_time = ntp_request_start.elapsed()?;
+    let requests_elapsed_time = requests_start.elapsed()?;
 
     // Current system time
     let system_time = Timestamp::current_time();
 
     // Add elapsed time to response times
-    ntp_time.add(ntp_elapsed_time);
+    let ntp_time = ntp_time.checked_add(ntp_elapsed_time)?;
     let peer_time = match peer_time {
         None => None,
-        Some(p) => {
-            let mut t = p;
-            t.add(requests_elapsed_time);
-            Some(t)
-        }
+        Some(p) => Some(p.checked_add(requests_elapsed_time)?),
     };
 
     debug!(target: "rpc::clock_sync", "peer_time: {:#?}", peer_time);

+ 1 - 1
src/runtime/import/util.rs

@@ -261,7 +261,7 @@ pub(crate) fn get_blockchain_time(mut ctx: FunctionEnvMut<Env>) -> i64 {
 
     // Create the return object
     let mut ret = Vec::with_capacity(8);
-    ret.extend_from_slice(&block.header.timestamp.0.to_be_bytes());
+    ret.extend_from_slice(&block.header.timestamp.inner().to_be_bytes());
 
     // Copy Vec<u8> to the VM
     let mut objects = env.objects.borrow_mut();

+ 43 - 31
src/util/time.rs

@@ -23,45 +23,63 @@ use darkfi_serial::async_trait;
 
 use darkfi_serial::{SerialDecodable, SerialEncodable};
 
+use crate::{Error, Result};
+
 const SECS_IN_DAY: u64 = 86400;
 const MIN_IN_HOUR: u64 = 60;
 const SECS_IN_HOUR: u64 = 3600;
 
 /// Wrapper struct to represent system timestamps.
-#[derive(Hash, Clone, Copy, Debug, SerialEncodable, SerialDecodable, PartialEq, PartialOrd, Eq)]
-pub struct Timestamp(pub u64);
+#[derive(
+    Hash, Clone, Copy, Debug, SerialEncodable, SerialDecodable, PartialEq, PartialOrd, Ord, Eq,
+)]
+pub struct Timestamp(u64);
 
 impl Timestamp {
+    /// Returns the inner `u64` of `Timestamp`
+    pub fn inner(&self) -> u64 {
+        self.0
+    }
+
     /// Generate a `Timestamp` of the current time.
     pub fn current_time() -> Self {
         Self(UNIX_EPOCH.elapsed().unwrap().as_secs())
     }
 
-    /// Calculates elapsed time of a `Timestamp`.
-    /// TODO: Rework this function to return the result of checked_sub and make calling code
-    /// check whether it is Some/None
-    pub fn elapsed(&self) -> u64 {
-        let now = UNIX_EPOCH.elapsed().unwrap().as_secs();
-        if let Some(elapsed) = now.checked_sub(self.0) {
-            elapsed
+    /// Calculates the elapsed time of a `Timestamp` up to the time of calling the function.
+    pub fn elapsed(&self) -> Result<Self> {
+        Self::current_time().checked_sub(*self)
+    }
+
+    /// Add `self` to a given timestamp
+    /// Errors on integer overflow.
+    pub fn checked_add(&self, ts: Timestamp) -> Result<Self> {
+        if let Some(result) = self.inner().checked_add(ts.inner()) {
+            Ok(Self(result))
         } else {
-            panic!(
-                "Cannot subtract Timestamp value {} from current time {}. (Integer underflow)",
-                self.0, now
-            );
+            Err(Error::AdditionOverflow)
         }
     }
 
-    /// Increment a 'Timestamp'.
-    /// TODO: Rework this function to return the result of checked_add and make calling code
-    /// check whether it is Some/None
-    pub fn add(&mut self, inc: u64) {
-        if let Some(sum) = self.0.checked_add(inc) {
-            self.0 = sum
+    /// Subtract `self` with a given timestamp
+    /// Errors on integer underflow.
+    pub fn checked_sub(&self, ts: Timestamp) -> Result<Self> {
+        if let Some(result) = self.inner().checked_sub(ts.inner()) {
+            Ok(Self(result))
         } else {
-            panic!("Cannot add {} to Timestamp {}. (Integer overflow)", self.0, inc);
+            Err(Error::SubtractionUnderflow)
         }
     }
+
+    pub const fn from_u64(x: u64) -> Self {
+        Self(x)
+    }
+}
+
+impl From<u64> for Timestamp {
+    fn from(x: u64) -> Self {
+        Self(x)
+    }
 }
 
 impl std::fmt::Display for Timestamp {
@@ -204,19 +222,13 @@ mod tests {
     use super::Timestamp;
 
     #[test]
-    #[should_panic]
-    fn panic_on_add_overflow() {
-        // Panic when the Timestamp func add() overflows u64.
-        let mut ts = Timestamp::current_time();
-        ts.add(u64::MAX);
+    fn check_ts_add_overflow() {
+        assert!(Timestamp::current_time().checked_add(u64::MAX.into()).is_err());
     }
 
     #[test]
-    #[should_panic]
-    fn panic_on_elapsed_underflow() {
-        // Panic when the Timestamp function elapsed() underflows u64.
-        let mut ts = Timestamp::current_time();
-        ts.add(10_000);
-        ts.elapsed();
+    fn check_ts_sub_underflow() {
+        let cur = Timestamp::current_time().checked_add(10_000.into()).unwrap();
+        assert!(cur.elapsed().is_err());
     }
 }

+ 2 - 2
src/validator/consensus.rs

@@ -190,7 +190,7 @@ impl Consensus {
             };
 
             // Update PoW module
-            fork.module.append(block.header.timestamp.0, &fork.module.next_difficulty()?);
+            fork.module.append(block.header.timestamp, &fork.module.next_difficulty()?);
 
             // Use last inserted block as next iteration previous
             previous = block;
@@ -486,7 +486,7 @@ impl Fork {
         let (target_distance_sq, hash_distance_sq) = block_rank(&proposal.block, &next_target)?;
 
         // Update PoW module
-        self.module.append(proposal.block.header.timestamp.0, &next_difficulty);
+        self.module.append(proposal.block.header.timestamp, &next_difficulty);
 
         // Update fork ranks
         self.targets_rank += target_distance_sq;

+ 2 - 2
src/validator/mod.rs

@@ -411,7 +411,7 @@ impl Validator {
             let cummulative_difficulty = module.cummulative_difficulty.clone() + difficulty.clone();
             let block_difficulty = BlockDifficulty::new(
                 block.header.height,
-                block.header.timestamp.0,
+                block.header.timestamp,
                 difficulty,
                 cummulative_difficulty,
             );
@@ -576,7 +576,7 @@ impl Validator {
 
             // Update PoW module
             if block.header.version == 1 {
-                module.append(block.header.timestamp.0, &module.next_difficulty()?);
+                module.append(block.header.timestamp, &module.next_difficulty()?);
             }
 
             // Use last inserted block as next iteration previous

+ 29 - 21
src/validator/pow.rs

@@ -69,7 +69,7 @@ const CUT_END: usize = 660;
 /// How many most recent blocks to use to verify new blocks' timestamp
 const BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW: usize = 60;
 /// Time limit in the future of what blocks can be
-const BLOCK_FUTURE_TIME_LIMIT: u64 = 60 * 60 * 2;
+const BLOCK_FUTURE_TIME_LIMIT: Timestamp = Timestamp::from_u64(60 * 60 * 2);
 
 /// This struct represents the information required by the PoW algorithm
 #[derive(Clone)]
@@ -79,7 +79,7 @@ pub struct PoWModule {
     /// Optional fixed difficulty
     pub fixed_difficulty: Option<BigUint>,
     /// Latest block timestamps ringbuffer
-    pub timestamps: RingBuffer<u64, BUF_SIZE>,
+    pub timestamps: RingBuffer<Timestamp, BUF_SIZE>,
     /// Latest block cummulative difficulties ringbuffer
     pub difficulties: RingBuffer<BigUint, BUF_SIZE>,
     /// Total blocks cummulative difficulty
@@ -95,8 +95,8 @@ impl PoWModule {
         target: usize,
         fixed_difficulty: Option<BigUint>,
     ) -> Result<Self> {
-        // Retrieving last BUF_ZISE difficulties from blockchain to build the buffers
-        let mut timestamps = RingBuffer::<u64, BUF_SIZE>::new();
+        // Retrieving last BUF_SIZE difficulties from blockchain to build the buffers
+        let mut timestamps = RingBuffer::<Timestamp, BUF_SIZE>::new();
         let mut difficulties = RingBuffer::<BigUint, BUF_SIZE>::new();
         let mut cummulative_difficulty = BigUint::zero();
         let last_n = blockchain.difficulties.get_last_n(BUF_SIZE)?;
@@ -120,8 +120,8 @@ impl PoWModule {
     /// return that after first 2 difficulties.
     pub fn next_difficulty(&self) -> Result<BigUint> {
         // Retrieve first DIFFICULTY_WINDOW timestamps from the ring buffer
-        let mut timestamps: Vec<u64> =
-            self.timestamps.iter().take(DIFFICULTY_WINDOW).copied().collect();
+        let mut timestamps: Vec<Timestamp> =
+            self.timestamps.iter().take(DIFFICULTY_WINDOW).cloned().collect();
 
         // Check we have enough timestamps
         let length = timestamps.len();
@@ -142,9 +142,10 @@ impl PoWModule {
 
         // Calculate total time span
         let cut_end = cut_end - 1;
-        let mut time_span = timestamps[cut_end] - timestamps[cut_begin];
-        if time_span == 0 {
-            time_span = 1;
+
+        let mut time_span = timestamps[cut_end].checked_sub(timestamps[cut_begin])?;
+        if time_span.inner() == 0 {
+            time_span = 1.into();
         }
 
         // Calculate total work done during this time span
@@ -154,7 +155,8 @@ impl PoWModule {
         }
 
         // Compute next difficulty
-        let next_difficulty = (total_work * self.target + time_span - BigUint::one()) / time_span;
+        let next_difficulty =
+            (total_work * self.target + time_span.inner() - BigUint::one()) / time_span.inner();
 
         Ok(next_difficulty)
     }
@@ -202,31 +204,37 @@ impl PoWModule {
 
     /// Verify provided block timestamp is not far in the future and
     /// check its valid acorrding to current timestamps median
-    pub fn verify_current_timestamp(&self, timestamp: u64) -> bool {
-        if timestamp > Timestamp::current_time().0 + BLOCK_FUTURE_TIME_LIMIT {
-            return false
+    pub fn verify_current_timestamp(&self, timestamp: Timestamp) -> Result<bool> {
+        if timestamp > Timestamp::current_time().checked_add(BLOCK_FUTURE_TIME_LIMIT)? {
+            return Ok(false)
         }
 
-        self.verify_timestamp_by_median(timestamp)
+        Ok(self.verify_timestamp_by_median(timestamp))
     }
 
     /// Verify provided block timestamp is valid and matches certain criteria
-    pub fn verify_timestamp_by_median(&self, timestamp: u64) -> bool {
+    pub fn verify_timestamp_by_median(&self, timestamp: Timestamp) -> bool {
         // If not enough blocks, no proper median yet, return true
         if self.timestamps.len() < BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW {
             return true
         }
 
         // Make sure the timestamp is higher or equal to the median
-        let timestamps =
-            self.timestamps.iter().rev().take(BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW).copied().collect();
-        timestamp >= median(timestamps)
+        let timestamps = self
+            .timestamps
+            .iter()
+            .rev()
+            .take(BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW)
+            .map(|x| x.inner())
+            .collect();
+
+        timestamp >= median(timestamps).into()
     }
 
     /// Verify provided block timestamp and hash
     pub fn verify_current_block(&self, block: &BlockInfo) -> Result<()> {
         // First we verify the block's timestamp
-        if !self.verify_current_timestamp(block.header.timestamp.0) {
+        if !self.verify_current_timestamp(block.header.timestamp)? {
             return Err(Error::PoWInvalidTimestamp)
         }
 
@@ -263,7 +271,7 @@ impl PoWModule {
     }
 
     /// Append provided timestamp and difficulty to the ring buffers
-    pub fn append(&mut self, timestamp: u64, difficulty: &BigUint) {
+    pub fn append(&mut self, timestamp: Timestamp, difficulty: &BigUint) {
         self.timestamps.push(timestamp);
         self.cummulative_difficulty += difficulty;
         self.difficulties.push(self.cummulative_difficulty.clone());
@@ -423,7 +431,7 @@ mod tests {
             let parts: Vec<String> = line.split(' ').map(|x| x.to_string()).collect();
             assert!(parts.len() == 2);
 
-            let timestamp = parts[0].parse::<u64>().unwrap();
+            let timestamp = parts[0].parse::<u64>().unwrap().into();
             let difficulty = BigUint::from_str_radix(&parts[1], 10).unwrap();
 
             let res = module.next_difficulty()?;

+ 2 - 2
src/validator/verification.rs

@@ -134,7 +134,7 @@ pub fn validate_block(block: &BlockInfo, previous: &BlockInfo, module: &PoWModul
     }
 
     // Check timestamp validity (4)
-    if !module.verify_timestamp_by_median(block.header.timestamp.0) {
+    if !module.verify_timestamp_by_median(block.header.timestamp) {
         return Err(Error::BlockIsInvalid(block.hash()?.to_string()))
     }
 
@@ -161,7 +161,7 @@ pub fn validate_blockchain(
         let full_block = &full_blocks[1];
         validate_block(full_block, &full_blocks[0], &module)?;
         // Update PoW module
-        module.append(full_block.header.timestamp.0, &module.next_difficulty()?);
+        module.append(full_block.header.timestamp, &module.next_difficulty()?);
     }
 
     Ok(())