From 6a334da3dc9365498945a61e19f8ec0115ba5aef Mon Sep 17 00:00:00 2001 From: Sydhds Date: Wed, 29 Mar 2023 11:37:27 +0200 Subject: [PATCH] Add activation delay when transitioning from LockedIn to Active (#3723) * Add activation delay when transitioning from LockedIn to Active * Update doc with state machine auto gen graph * Fix ComponentState serialization at locked in state * Improve mip store raw deserializer context msg * Fix test_factory_strategy_at unit test * Clippy fixes * Use >= instead of > for state update * Use >= instead of > for state update (round 2) --------- Co-authored-by: sydhds --- massa-versioning-worker/src/lib.rs | 4 + .../src/test_helpers/versioning_helpers.rs | 8 +- massa-versioning-worker/src/versioning.rs | 209 +++++++++--------- .../src/versioning_factory.rs | 8 +- .../src/versioning_ser_der.rs | 112 +++++++--- 5 files changed, 194 insertions(+), 147 deletions(-) diff --git a/massa-versioning-worker/src/lib.rs b/massa-versioning-worker/src/lib.rs index 54adeead95b..8f691fcc434 100644 --- a/massa-versioning-worker/src/lib.rs +++ b/massa-versioning-worker/src/lib.rs @@ -20,6 +20,10 @@ //! * A state machine (stores the current state of deployment for a MipInfo) //! * A history (stores a list of `Advance` message that 'really' updated the state machine) //! +//! A auto generated graph of the state machine can be found here: +//! * dot -Tpng ./target/machine/componentstate.dot > ./target/machine/componentstate.png +//! * xdg-open ./target/machine/componentstate.png +//! //! History is there in order to: //! * Query the state at any time, so you can query MipStore and ask the best version at any time //! * Used a lot when merging 2 MipStore: diff --git a/massa-versioning-worker/src/test_helpers/versioning_helpers.rs b/massa-versioning-worker/src/test_helpers/versioning_helpers.rs index 7191d5e4faf..51e39c2874a 100644 --- a/massa-versioning-worker/src/test_helpers/versioning_helpers.rs +++ b/massa-versioning-worker/src/test_helpers/versioning_helpers.rs @@ -3,6 +3,7 @@ use crate::versioning::{Advance, ComponentState, MipInfo, MipState}; use massa_models::config::VERSIONING_THRESHOLD_TRANSITION_ACCEPTED; use massa_time::MassaTime; +// TODO: rename versioning_info pub fn advance_state_until(at_state: ComponentState, versioning_info: &MipInfo) -> MipState { // A helper function to advance a state // Assume enough time between versioning info start & timeout @@ -26,6 +27,7 @@ pub fn advance_state_until(at_state: ComponentState, versioning_info: &MipInfo) timeout, threshold: Default::default(), now: start.saturating_add(MassaTime::from(1)), + activation_delay: versioning_info.activation_delay, }; state.on_advance(&advance_msg); @@ -47,7 +49,11 @@ pub fn advance_state_until(at_state: ComponentState, versioning_info: &MipInfo) return state; } - advance_msg.now = timeout.saturating_add(MassaTime::from(1)); + advance_msg.now = advance_msg + .now + .saturating_add(versioning_info.activation_delay) + .saturating_add(MassaTime::from(1)); + state.on_advance(&advance_msg); // Active state diff --git a/massa-versioning-worker/src/versioning.rs b/massa-versioning-worker/src/versioning.rs index ef39ce43350..0bb65d4ce33 100644 --- a/massa-versioning-worker/src/versioning.rs +++ b/massa-versioning-worker/src/versioning.rs @@ -34,10 +34,19 @@ pub struct MipInfo { pub component: MipComponent, /// Component version pub component_version: u32, + /// a timestamp at which the version gains its meaning (e.g. accepted in block header) - pub start: MassaTime, + // pub start: MassaTime, /// a timestamp at which the deployment is considered active or failed (timeout > start) + // pub timeout: MassaTime, + + /// a timestamp at which the version gains its meaning (e.g. announced in block header) + pub start: MassaTime, + // TODO: rename to start_timeout? + /// a timestamp at the which the deployment is considered failed pub timeout: MassaTime, + /// Once deployment has been locked, wait for this duration before deployment is considered active + pub activation_delay: MassaTime, } // Need Ord / PartialOrd so it is properly sorted in BTreeMap @@ -61,6 +70,7 @@ impl PartialEq for MipInfo { && self.component == other.component && self.start == other.start && self.timeout == other.timeout + && self.activation_delay == other.activation_delay } } @@ -86,8 +96,8 @@ machine!( /// Past start, can only go to LockedIn after the threshold is above a given value Started { pub(crate) threshold: Amount }, /// Wait for some time before going to active (to let user the time to upgrade) - LockedIn, - /// After LockedIn, deployment is considered successful + LockedIn { pub(crate) at: MassaTime }, + /// After LockedIn, deployment is considered successful (after activation delay) Active, /// Past the timeout, if LockedIn is not reach Failed, @@ -136,6 +146,8 @@ pub struct Advance { pub threshold: Amount, /// Current time (timestamp) pub now: MassaTime, + /// TODO + pub activation_delay: MassaTime, } // Need Ord / PartialOrd so it is properly sorted in BTreeMap @@ -177,8 +189,8 @@ impl Defined { /// Update state from state Defined pub fn on_advance(self, input: Advance) -> ComponentState { match input.now { - n if n > input.timeout => ComponentState::failed(), - n if n > input.start_timestamp => ComponentState::started(Amount::zero()), + n if n >= input.timeout => ComponentState::failed(), + n if n >= input.start_timestamp => ComponentState::started(Amount::zero()), _ => ComponentState::Defined(Defined {}), } } @@ -192,7 +204,7 @@ impl Started { } if input.threshold >= VERSIONING_THRESHOLD_TRANSITION_ACCEPTED { - ComponentState::locked_in() + ComponentState::locked_in(input.now) } else { ComponentState::started(input.threshold) } @@ -202,10 +214,10 @@ impl Started { impl LockedIn { /// Update state from state LockedIn ... pub fn on_advance(self, input: Advance) -> ComponentState { - if input.now > input.timeout { + if input.now > self.at.saturating_add(input.activation_delay) { ComponentState::active() } else { - ComponentState::locked_in() + ComponentState::locked_in(self.at) } } } @@ -243,6 +255,7 @@ impl MipState { timeout: MassaTime::from(0), threshold: Default::default(), now: defined, + activation_delay: MassaTime::from(0), }; let history = BTreeMap::from([(advance, state_id)]); @@ -289,6 +302,8 @@ impl MipState { /// if history is coherent with current state /// Return false for state == ComponentState::Error pub fn is_coherent_with(&self, versioning_info: &MipInfo) -> bool { + // TODO: rename versioning_info -> mip_info + // Always return false for state Error or if history is empty if matches!(&self.inner, &ComponentState::Error) || self.history.is_empty() { return false; @@ -309,6 +324,7 @@ impl MipState { timeout: versioning_info.timeout, threshold: Amount::zero(), now: initial_ts.now, + activation_delay: versioning_info.activation_delay, }; for (adv, _state) in self.history.iter().skip(1) { @@ -321,6 +337,7 @@ impl MipState { } /// Query state at given timestamp + /// TODO: add doc for start & timeout parameter? why do we need them? pub fn state_at( &self, ts: MassaTime, @@ -383,6 +400,7 @@ impl MipState { timeout, threshold: adv.threshold, now: ts, + activation_delay: adv.activation_delay, }; // Return the resulting state after advance let state = self.inner.on_advance(msg); @@ -595,10 +613,6 @@ mod test { use crate::test_helpers::versioning_helpers::advance_state_until; use chrono::{Days, NaiveDate, NaiveDateTime}; - // use crate::test_helpers::versioning_helpers::advance_state_until; - - // use massa_serialization::DeserializeError; - // Only for unit tests impl PartialEq for MipState { fn eq(&self, other: &ComponentState) -> bool { @@ -606,6 +620,18 @@ mod test { } } + impl From<(&MipInfo, &Amount, &MassaTime)> for Advance { + fn from((mip_info, threshold, now): (&MipInfo, &Amount, &MassaTime)) -> Self { + Self { + start_timestamp: mip_info.start, + timeout: mip_info.timeout, + threshold: *threshold, + now: *now, + activation_delay: mip_info.activation_delay, + } + } + } + fn get_a_version_info() -> (NaiveDateTime, NaiveDateTime, MipInfo) { // A helper function to provide a default VersioningInfo // Models a Massa Improvements Proposal (MIP-0002), transitioning component address to v2 @@ -630,6 +656,7 @@ mod test { component_version: 1, start: MassaTime::from(start.timestamp() as u64), timeout: MassaTime::from(timeout.timestamp() as u64), + activation_delay: MassaTime::from(20), }, ); } @@ -637,22 +664,17 @@ mod test { #[test] fn test_state_advance_from_defined() { // Test Versioning state transition (from state: Defined) - let (_, _, vi) = get_a_version_info(); + let (_, _, mi) = get_a_version_info(); let mut state: ComponentState = Default::default(); assert_eq!(state, ComponentState::defined()); - let now = vi.start; - let mut advance_msg = Advance { - start_timestamp: vi.start, - timeout: vi.timeout, - threshold: Amount::zero(), - now, - }; + let now = mi.start.saturating_sub(MassaTime::from(1)); + let mut advance_msg = Advance::from((&mi, &Amount::zero(), &now)); state = state.on_advance(advance_msg.clone()); assert_eq!(state, ComponentState::defined()); - let now = vi.start.saturating_add(MassaTime::from(5)); + let now = mi.start.saturating_add(MassaTime::from(5)); advance_msg.now = now; state = state.on_advance(advance_msg); @@ -668,42 +690,34 @@ mod test { #[test] fn test_state_advance_from_started() { // Test Versioning state transition (from state: Started) - let (_, _, vi) = get_a_version_info(); + let (_, _, mi) = get_a_version_info(); let mut state: ComponentState = ComponentState::started(Default::default()); - let now = vi.start; + let now = mi.start; let threshold_too_low = Amount::from_str("74.9").unwrap(); let threshold_ok = Amount::from_str("82.42").unwrap(); - let mut advance_msg = Advance { - start_timestamp: vi.start, - timeout: vi.timeout, - threshold: threshold_too_low, - now, - }; + let mut advance_msg = Advance::from((&mi, &threshold_too_low, &now)); state = state.on_advance(advance_msg.clone()); assert_eq!(state, ComponentState::started(threshold_too_low)); advance_msg.threshold = threshold_ok; state = state.on_advance(advance_msg); - assert_eq!(state, ComponentState::locked_in()); + assert_eq!(state, ComponentState::locked_in(now)); } #[test] fn test_state_advance_from_locked_in() { // Test Versioning state transition (from state: LockedIn) - let (_, _, vi) = get_a_version_info(); - let mut state: ComponentState = ComponentState::locked_in(); + let (_, _, mi) = get_a_version_info(); - let now = vi.start; - let mut advance_msg = Advance { - start_timestamp: vi.start, - timeout: vi.timeout, - threshold: Amount::zero(), - now, - }; + let locked_in_at = mi.start.saturating_add(MassaTime::from(1)); + let mut state: ComponentState = ComponentState::locked_in(locked_in_at); + + let now = mi.start; + let mut advance_msg = Advance::from((&mi, &Amount::zero(), &now)); state = state.on_advance(advance_msg.clone()); - assert_eq!(state, ComponentState::locked_in()); + assert_eq!(state, ComponentState::locked_in(locked_in_at)); advance_msg.now = advance_msg.timeout.saturating_add(MassaTime::from(1)); state = state.on_advance(advance_msg); @@ -713,15 +727,10 @@ mod test { #[test] fn test_state_advance_from_active() { // Test Versioning state transition (from state: Active) - let (_, _, vi) = get_a_version_info(); + let (_, _, mi) = get_a_version_info(); let mut state = ComponentState::active(); - let now = vi.start; - let advance = Advance { - start_timestamp: vi.start, - timeout: vi.timeout, - threshold: Amount::zero(), - now, - }; + let now = mi.start; + let advance = Advance::from((&mi, &Amount::zero(), &now)); state = state.on_advance(advance); assert_eq!(state, ComponentState::active()); @@ -730,16 +739,10 @@ mod test { #[test] fn test_state_advance_from_failed() { // Test Versioning state transition (from state: Failed) - let (_, _, vi) = get_a_version_info(); + let (_, _, mi) = get_a_version_info(); let mut state = ComponentState::failed(); - let now = vi.start; - let advance = Advance { - start_timestamp: vi.start, - timeout: vi.timeout, - threshold: Amount::zero(), - now, - }; - + let now = mi.start; + let advance = Advance::from((&mi, &Amount::zero(), &now)); state = state.on_advance(advance); assert_eq!(state, ComponentState::failed()); } @@ -747,16 +750,11 @@ mod test { #[test] fn test_state_advance_to_failed() { // Test Versioning state transition (to state: Failed) - let (_, _, vi) = get_a_version_info(); - let now = vi.start.saturating_add(MassaTime::from(1)); - let advance_msg = Advance { - start_timestamp: vi.start, - timeout: vi.start, - threshold: Amount::zero(), - now, - }; + let (_, _, mi) = get_a_version_info(); + let now = mi.timeout.saturating_add(MassaTime::from(1)); + let advance_msg = Advance::from((&mi, &Amount::zero(), &now)); - let mut state: ComponentState = Default::default(); + let mut state: ComponentState = Default::default(); // Defined state = state.on_advance(advance_msg.clone()); assert_eq!(state, ComponentState::Failed(Failed {})); @@ -769,19 +767,14 @@ mod test { fn test_state_with_history() { // Test MipStateHistory::state_at() function - let (start, _, vi) = get_a_version_info(); + let (start, _, mi) = get_a_version_info(); let now_0 = MassaTime::from(start.timestamp() as u64); let mut state = MipState::new(now_0); assert_eq!(state, ComponentState::defined()); - let now = vi.start.saturating_add(MassaTime::from(15)); - let mut advance_msg = Advance { - start_timestamp: vi.start, - timeout: vi.timeout, - threshold: Amount::zero(), - now, - }; + let now = mi.start.saturating_add(MassaTime::from(15)); + let mut advance_msg = Advance::from((&mi, &Amount::zero(), &now)); // Move from Defined -> Started state.on_advance(&advance_msg); @@ -802,30 +795,30 @@ mod test { // Before Defined let state_id_ = state.state_at( - vi.start.saturating_sub(MassaTime::from(5)), - vi.start, - vi.timeout, + mi.start.saturating_sub(MassaTime::from(5)), + mi.start, + mi.timeout, ); assert!(matches!( state_id_, Err(StateAtError::BeforeInitialState(_, _)) )); // After Defined timestamp - let state_id = state.state_at(vi.start, vi.start, vi.timeout).unwrap(); + let state_id = state.state_at(mi.start, mi.start, mi.timeout).unwrap(); assert_eq!(state_id, ComponentStateTypeId::Defined); // At Started timestamp - let state_id = state.state_at(now, vi.start, vi.timeout).unwrap(); + let state_id = state.state_at(now, mi.start, mi.timeout).unwrap(); assert_eq!(state_id, ComponentStateTypeId::Started); // After Started timestamp but before timeout timestamp let after_started_ts = now.saturating_add(MassaTime::from(15)); - let state_id_ = state.state_at(after_started_ts, vi.start, vi.timeout); + let state_id_ = state.state_at(after_started_ts, mi.start, mi.timeout); assert_eq!(state_id_, Err(StateAtError::Unpredictable)); // After Started timestamp and after timeout timestamp - let after_timeout_ts = vi.timeout.saturating_add(MassaTime::from(15)); + let after_timeout_ts = mi.timeout.saturating_add(MassaTime::from(15)); let state_id = state - .state_at(after_timeout_ts, vi.start, vi.timeout) + .state_at(after_timeout_ts, mi.start, mi.timeout) .unwrap(); assert_eq!(state_id, ComponentStateTypeId::Failed); @@ -834,18 +827,18 @@ mod test { advance_msg.threshold = threshold.saturating_add(Amount::from_str("1.0").unwrap()); advance_msg.now = now.saturating_add(MassaTime::from(1)); state.on_advance(&advance_msg); - assert_eq!(state, ComponentState::locked_in()); + assert_eq!(state, ComponentState::locked_in(advance_msg.now)); // Query with timestamp // After LockedIn timestamp and before timeout timestamp let after_locked_in_ts = now.saturating_add(MassaTime::from(10)); let state_id = state - .state_at(after_locked_in_ts, vi.start, vi.timeout) + .state_at(after_locked_in_ts, mi.start, mi.timeout) .unwrap(); assert_eq!(state_id, ComponentStateTypeId::LockedIn); // After LockedIn timestamp and after timeout timestamp let state_id = state - .state_at(after_timeout_ts, vi.start, vi.timeout) + .state_at(after_timeout_ts, mi.start, mi.timeout) .unwrap(); assert_eq!(state_id, ComponentStateTypeId::Active); } @@ -854,13 +847,13 @@ mod test { fn test_versioning_store_announce_current() { // Test VersioningInfo::get_version_to_announce() & ::get_version_current() - let (_start, timeout, vi) = get_a_version_info(); + let (_start, timeout, mi) = get_a_version_info(); - let mut vi_2 = vi.clone(); - vi_2.version += 1; - vi_2.start = + let mut mi_2 = mi.clone(); + mi_2.version += 1; + mi_2.start = MassaTime::from(timeout.checked_add_days(Days::new(2)).unwrap().timestamp() as u64); - vi_2.timeout = + mi_2.timeout = MassaTime::from(timeout.checked_add_days(Days::new(5)).unwrap().timestamp() as u64); // Can only build such object in test - history is empty :-/ @@ -874,12 +867,12 @@ mod test { }; // TODO: Have VersioningStore::from ? - let vs_raw = MipStoreRaw(BTreeMap::from([(vi.clone(), vs_1), (vi_2.clone(), vs_2)])); + let vs_raw = MipStoreRaw(BTreeMap::from([(mi.clone(), vs_1), (mi_2.clone(), vs_2)])); // let vs_raw = MipStoreRaw::try_from([(vi.clone(), vs_1), (vi_2.clone(), vs_2)]).unwrap(); let vs = MipStore(Arc::new(RwLock::new(vs_raw))); - assert_eq!(vs.get_network_version_current(), vi.version); - assert_eq!(vs.get_network_version_to_announce(), vi_2.version); + assert_eq!(vs.get_network_version_current(), mi.version); + assert_eq!(vs.get_network_version_to_announce(), mi_2.version); // Test also an empty versioning store let vs_raw = MipStoreRaw(Default::default()); @@ -904,6 +897,7 @@ mod test { component_version: 1, start: MassaTime::from(2), timeout: MassaTime::from(5), + activation_delay: MassaTime::from(2), }; // Another versioning info (from an attacker) for testing let vi_2 = MipInfo { @@ -913,6 +907,7 @@ mod test { component_version: 1, start: MassaTime::from(7), timeout: MassaTime::from(10), + activation_delay: MassaTime::from(2), }; let vsh = MipState { @@ -937,12 +932,8 @@ mod test { // Advance to Started let now = MassaTime::from(3); - vsh.on_advance(&Advance { - start_timestamp: vi_1.start, - timeout: vi_1.timeout, - threshold: Amount::zero(), - now, - }); + let adv = Advance::from((&vi_1, &Amount::zero(), &now)); + vsh.on_advance(&adv); // At state Started at time now -> true assert_eq!(vsh.inner, ComponentState::started(Amount::zero())); @@ -952,15 +943,11 @@ mod test { // Advance to LockedIn let now = MassaTime::from(4); - vsh.on_advance(&Advance { - start_timestamp: vi_1.start, - timeout: vi_1.timeout, - threshold: VERSIONING_THRESHOLD_TRANSITION_ACCEPTED, - now, - }); + let adv = Advance::from((&vi_1, &VERSIONING_THRESHOLD_TRANSITION_ACCEPTED, &now)); + vsh.on_advance(&adv); // At state LockedIn at time now -> true - assert_eq!(vsh.inner, ComponentState::locked_in()); + assert_eq!(vsh.inner, ComponentState::locked_in(now)); assert_eq!(vsh.is_coherent_with(&vi_1), true); assert_eq!(vsh.is_coherent_with(&vi_1), true); @@ -977,6 +964,7 @@ mod test { component_version: 1, start: MassaTime::from(2), timeout: MassaTime::from(5), + activation_delay: MassaTime::from(2), }; let vs_1 = advance_state_until(ComponentState::active(), &vi_1); @@ -989,6 +977,7 @@ mod test { component_version: 2, start: MassaTime::from(17), timeout: MassaTime::from(27), + activation_delay: MassaTime::from(2), }; let vs_2 = advance_state_until(ComponentState::defined(), &vi_2); let mut vs_raw_1 = MipStoreRaw(BTreeMap::from([ @@ -996,8 +985,8 @@ mod test { (vi_2.clone(), vs_2.clone()), ])); - let vs_2_2 = advance_state_until(ComponentState::locked_in(), &vi_2); - assert_eq!(vs_2_2, ComponentState::locked_in()); + let vs_2_2 = advance_state_until(ComponentState::active(), &vi_2); + assert_eq!(vs_2_2, ComponentState::active()); let vs_raw_2 = MipStoreRaw::try_from([(vi_1.clone(), vs_1.clone()), (vi_2.clone(), vs_2_2.clone())]) @@ -1005,7 +994,7 @@ mod test { vs_raw_1.update_with(&vs_raw_2).unwrap(); - // Expect state 1 (for vi_1) no change, state 2 (for vi_2) updated to "LockedIn" + // Expect state 1 (for vi_1) no change, state 2 (for vi_2) updated to "Active" assert_eq!(vs_raw_1.0.get(&vi_1).unwrap().inner, vs_1.inner); assert_eq!(vs_raw_1.0.get(&vi_2).unwrap().inner, vs_2_2.inner); } @@ -1023,6 +1012,7 @@ mod test { component_version: 1, start: MassaTime::from(0), timeout: MassaTime::from(5), + activation_delay: MassaTime::from(2), }; let vs_1 = advance_state_until(ComponentState::active(), &vi_1); assert_eq!(vs_1, ComponentState::active()); @@ -1034,6 +1024,7 @@ mod test { component_version: 2, start: MassaTime::from(17), timeout: MassaTime::from(27), + activation_delay: MassaTime::from(2), }; let vs_2 = advance_state_until(ComponentState::defined(), &vi_2); assert_eq!(vs_2, ComponentState::defined()); diff --git a/massa-versioning-worker/src/versioning_factory.rs b/massa-versioning-worker/src/versioning_factory.rs index 36c4028fe6a..e0af3c036fa 100644 --- a/massa-versioning-worker/src/versioning_factory.rs +++ b/massa-versioning-worker/src/versioning_factory.rs @@ -19,7 +19,7 @@ pub enum FactoryError { OnCreate(String, String), } -#[derive(Clone)] +#[derive(Clone, Debug)] /// Strategy to use when creating a new object from a factory pub enum FactoryStrategy { /// use get_latest_version (see Factory trait) @@ -264,6 +264,7 @@ mod test { component_version: 1, start: MassaTime::from(12), timeout: MassaTime::from(15), + activation_delay: MassaTime::from(2), }; let vs_1 = MipState::new(MassaTime::from(10)); @@ -274,6 +275,7 @@ mod test { component_version: 2, start: MassaTime::from(25), timeout: MassaTime::from(28), + activation_delay: MassaTime::from(2), }; let vs_2 = MipState::new(MassaTime::from(18)); @@ -345,6 +347,7 @@ mod test { component_version: 1, start: MassaTime::from(12), timeout: MassaTime::from(15), + activation_delay: MassaTime::from(2), }; let vs_1 = advance_state_until(ComponentState::active(), &vi_1); @@ -355,6 +358,7 @@ mod test { component_version: 2, start: MassaTime::from(25), timeout: MassaTime::from(28), + activation_delay: MassaTime::from(2), }; let vs_2 = MipState::new(MassaTime::from(18)); @@ -376,7 +380,7 @@ mod test { let st_1 = FactoryStrategy::At(MassaTime::from(8)); // vi_1 not yet defined let ts_1_2 = MassaTime::from(13); let st_1_2 = FactoryStrategy::At(ts_1_2); // vi_1 is started (after vi_1.start) - let st_2 = FactoryStrategy::At(MassaTime::from(16)); // vi_1 is active (after vi_1.timeout) + let st_2 = FactoryStrategy::At(MassaTime::from(18)); // vi_1 is active (after start + activation delay) let st_3 = FactoryStrategy::At(MassaTime::from(27)); // vi_2 is started or locked_in let st_4 = FactoryStrategy::At(MassaTime::from(30)); // vi_2 is active (after vi_2.timeout) diff --git a/massa-versioning-worker/src/versioning_ser_der.rs b/massa-versioning-worker/src/versioning_ser_der.rs index 972b210f046..d3ffc5b10d4 100644 --- a/massa-versioning-worker/src/versioning_ser_der.rs +++ b/massa-versioning-worker/src/versioning_ser_der.rs @@ -11,8 +11,8 @@ use nom::{ }; use crate::versioning::{ - Advance, ComponentState, ComponentStateTypeId, MipComponent, MipInfo, MipState, MipStoreRaw, - Started, + Advance, ComponentState, ComponentStateTypeId, LockedIn, MipComponent, MipInfo, MipState, + MipStoreRaw, Started, }; use massa_models::amount::{Amount, AmountDeserializer, AmountSerializer}; @@ -84,6 +84,9 @@ impl Serializer for MipInfoSerializer { self.time_serializer.serialize(&value.start, buffer)?; // timeout self.time_serializer.serialize(&value.timeout, buffer)?; + // activation delay + self.time_serializer + .serialize(&value.activation_delay, buffer)?; Ok(()) } } @@ -163,16 +166,22 @@ impl Deserializer for MipInfoDeserializer { context("Failed timeout deserialization", |input| { self.time_deserializer.deserialize(input) }), + context("Failed activation delay deserialization", |input| { + self.time_deserializer.deserialize(input) + }), )), ) .map( - |(name, version, component, component_version, start, timeout)| MipInfo { - name, - version, - component, - component_version, - start, - timeout, + |(name, version, component, component_version, start, timeout, activation_delay)| { + MipInfo { + name, + version, + component, + component_version, + start, + timeout, + activation_delay, + } }, ) .parse(buffer) @@ -184,10 +193,10 @@ impl Deserializer for MipInfoDeserializer { // ComponentState /// Serializer for `ComponentState` -#[derive(Clone)] pub struct ComponentStateSerializer { u32_serializer: U32VarIntSerializer, amount_serializer: AmountSerializer, + time_serializer: MassaTimeSerializer, } impl ComponentStateSerializer { @@ -196,6 +205,7 @@ impl ComponentStateSerializer { Self { u32_serializer: U32VarIntSerializer::new(), amount_serializer: AmountSerializer::new(), + time_serializer: MassaTimeSerializer::new(), } } } @@ -212,19 +222,21 @@ impl Serializer for ComponentStateSerializer { value: &ComponentState, buffer: &mut Vec, ) -> Result<(), SerializeError> { - let (state, threshold_): (u32, Option) = match value { - ComponentState::Error => (u32::from(ComponentStateTypeId::Error), None), - ComponentState::Defined(_) => (u32::from(ComponentStateTypeId::Defined), None), + match value { ComponentState::Started(Started { threshold }) => { - (u32::from(ComponentStateTypeId::Started), Some(*threshold)) + let state_id = u32::from(ComponentStateTypeId::from(value)); + self.u32_serializer.serialize(&state_id, buffer)?; + self.amount_serializer.serialize(threshold, buffer)?; + } + ComponentState::LockedIn(LockedIn { at }) => { + let state_id = u32::from(ComponentStateTypeId::from(value)); + self.u32_serializer.serialize(&state_id, buffer)?; + self.time_serializer.serialize(at, buffer)?; + } + _ => { + let state_id = u32::from(ComponentStateTypeId::from(value)); + self.u32_serializer.serialize(&state_id, buffer)?; } - ComponentState::LockedIn(_) => (u32::from(ComponentStateTypeId::LockedIn), None), - ComponentState::Active(_) => (u32::from(ComponentStateTypeId::Active), None), - ComponentState::Failed(_) => (u32::from(ComponentStateTypeId::Failed), None), - }; - self.u32_serializer.serialize(&state, buffer)?; - if let Some(threshold) = threshold_ { - self.amount_serializer.serialize(&threshold, buffer)?; } Ok(()) } @@ -234,6 +246,7 @@ impl Serializer for ComponentStateSerializer { pub struct ComponentStateDeserializer { state_deserializer: U32VarIntDeserializer, amount_deserializer: AmountDeserializer, + time_deserializer: MassaTimeDeserializer, } impl ComponentStateDeserializer { @@ -248,6 +261,10 @@ impl ComponentStateDeserializer { Included(Amount::MIN), Included(Amount::MAX), ), + time_deserializer: MassaTimeDeserializer::new(( + Included(0.into()), + Included(u64::MAX.into()), + )), } } } @@ -283,7 +300,13 @@ impl Deserializer for ComponentStateDeserializer { .parse(rem)?; (rem2, ComponentState::started(threshold)) } - ComponentStateTypeId::LockedIn => (rem, ComponentState::locked_in()), + ComponentStateTypeId::LockedIn => { + let (rem2, at) = context("Failed delay value der", |input| { + self.time_deserializer.deserialize(input) + }) + .parse(rem)?; + (rem2, ComponentState::locked_in(at)) + } ComponentStateTypeId::Active => (rem, ComponentState::active()), ComponentStateTypeId::Failed => (rem, ComponentState::failed()), _ => (rem, ComponentState::Error), @@ -321,11 +344,18 @@ impl Default for AdvanceSerializer { impl Serializer for AdvanceSerializer { fn serialize(&self, value: &Advance, buffer: &mut Vec) -> Result<(), SerializeError> { + // start self.time_serializer .serialize(&value.start_timestamp, buffer)?; + // timeout self.time_serializer.serialize(&value.timeout, buffer)?; + // threshold self.amount_serializer.serialize(&value.threshold, buffer)?; + // now self.time_serializer.serialize(&value.now, buffer)?; + // activation delay + self.time_serializer + .serialize(&value.activation_delay, buffer)?; Ok(()) } } @@ -378,14 +408,20 @@ impl Deserializer for AdvanceDeserializer { context("Failed now deserialization", |input| { self.time_deserializer.deserialize(input) }), + context("Failed activation delay deserialization", |input| { + self.time_deserializer.deserialize(input) + }), )), ) - .map(|(start_timestamp, timeout, threshold, now)| Advance { - start_timestamp, - timeout, - threshold, - now, - }) + .map( + |(start_timestamp, timeout, threshold, now, activation_delay)| Advance { + start_timestamp, + timeout, + threshold, + now, + activation_delay, + }, + ) .parse(buffer) } } @@ -428,6 +464,7 @@ impl Serializer for MipStateSerializer { })?, buffer, )?; + // history for (advance, state_id) in value.history.iter() { self.advance_serializer.serialize(advance, buffer)?; self.u32_serializer @@ -476,16 +513,15 @@ impl Deserializer for MipStateDeserializer { self.state_deserializer.deserialize(input) }) .parse(buffer)?; - // Der history let (rem2, history) = context( - "Failed Vec deserialization", + "Failed history deserialization", length_count( context("Failed length deserialization", |input| { self.u32_deserializer.deserialize(input) }), context( - "Failed deserialization", + "Failed history items deserialization", tuple(( context("Failed advance deserialization", |input| { self.advance_deserializer.deserialize(input) @@ -608,7 +644,7 @@ impl Deserializer for MipStoreRawDeserializer { context( "Failed MipStoreRaw der", length_count( - context("Failed len der", |input| { + context("Failed entry count der", |input| { let (rem, count) = self.u32_deserializer.deserialize(input)?; IResult::Ok((rem, count)) }), @@ -648,6 +684,7 @@ mod test { component_version: 1, start: MassaTime::from(2), timeout: MassaTime::from(5), + activation_delay: MassaTime::from(2), }; let mut buf = Vec::new(); @@ -710,6 +747,7 @@ mod test { timeout: MassaTime::from(timeout.timestamp() as u64), threshold: Default::default(), now: MassaTime::from(now.timestamp() as u64), + activation_delay: MassaTime::from(20), }; let mut buf = Vec::new(); @@ -747,13 +785,14 @@ mod test { component_version: 1, start: MassaTime::from(2), timeout: MassaTime::from(5), + activation_delay: MassaTime::from(2), }; - let state_2 = advance_state_until(ComponentState::locked_in(), &mi_1); + let state_2 = advance_state_until(ComponentState::locked_in(MassaTime::from(3)), &mi_1); state_ser.serialize(&state_2, &mut buf).unwrap(); - let (rem, state_der_res) = state_der.deserialize::(&buf).unwrap(); + let (rem2, state_der_res) = state_der.deserialize::(&buf).unwrap(); - assert!(rem.is_empty()); + assert!(rem2.is_empty()); assert_eq!(state_2, state_der_res); } @@ -766,6 +805,7 @@ mod test { component_version: 1, start: MassaTime::from(2), timeout: MassaTime::from(5), + activation_delay: MassaTime::from(2), }; let mi_3 = MipInfo { @@ -775,6 +815,7 @@ mod test { component_version: 1, start: MassaTime::from(12), timeout: MassaTime::from(17), + activation_delay: MassaTime::from(2), }; let state_2 = advance_state_until(ComponentState::active(), &mi_2); @@ -805,6 +846,7 @@ mod test { component_version: 0, start: MassaTime::from(0), timeout: MassaTime::from(2), + activation_delay: MassaTime::from(2), }; let mi_base_size = size_of_val(&mi_base.name[..])