Skip to content

Commit

Permalink
Add boilerplate for base node FSM (#834)
Browse files Browse the repository at this point in the history
Merge pull request #834

Adds stubs for the missing state structs in the base node FSM.
Provide comments for allowed state conversions.

What's quite nice about doing it this way is that the _compiler_ will tell you
if you try and enact an illegal state change, e.g. from Startup -> Listening;
becuase the From trait isn't written for that change.
  • Loading branch information
CjS77 committed Oct 11, 2019
2 parents 1f7dfc9 + 7ee6990 commit 254b2d0
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 17 deletions.
22 changes: 11 additions & 11 deletions base_layer/core/src/base_node/base_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
use crate::{
base_node::{
states,
states::{BaseNodeState, StateEvent, StateEvent::FatalError},
states::{BaseNodeState, StateEvent},
BaseNodeConfig,
},
chain_storage::{BlockchainBackend, BlockchainDatabase},
Expand Down Expand Up @@ -81,13 +81,13 @@ impl<B: BlockchainBackend> BaseNodeStateMachine<B> {
use crate::base_node::states::{BaseNodeState::*, StateEvent::*, SyncStatus::*};
match (state, event) {
(Starting(s), Initialized) => InitialSync(s.into()),
(InitialSync(_s), MetadataSynced(BehindHorizon)) => FetchingHorizonState,
(InitialSync(_s), MetadataSynced(Lagging)) => BlockSync,
(InitialSync(_s), MetadataSynced(UpToDate)) => Listening,
(FetchingHorizonState, HorizonStateFetched) => BlockSync,
(BlockSync, BlocksSynchronized) => Listening,
(Listening, FallenBehind(BehindHorizon)) => FetchingHorizonState,
(Listening, FallenBehind(Lagging)) => BlockSync,
(InitialSync(s), MetadataSynced(BehindHorizon)) => FetchingHorizonState(s.into()),
(InitialSync(s), MetadataSynced(Lagging)) => BlockSync(s.into()),
(InitialSync(s), MetadataSynced(UpToDate)) => Listening(s.into()),
(FetchingHorizonState(s), HorizonStateFetched) => BlockSync(s.into()),
(BlockSync(s), BlocksSynchronized) => Listening(s.into()),
(Listening(s), FallenBehind(BehindHorizon)) => FetchingHorizonState(s.into()),
(Listening(s), FallenBehind(Lagging)) => BlockSync(s.into()),
(_, FatalError(s)) => Shutdown(states::Shutdown::with_reason(s)),
(s, e) => {
debug!(
Expand All @@ -107,9 +107,9 @@ impl<B: BlockchainBackend> BaseNodeStateMachine<B> {
let next_event = match &mut self.state {
Starting(s) => s.next_event(),
InitialSync(s) => s.next_event(),
FetchingHorizonState => FatalError("Unimplemented".into()),
BlockSync => FatalError("Unimplemented".into()),
Listening => FatalError("Unimplemented".into()),
FetchingHorizonState(s) => s.next_event(),
BlockSync(s) => s.next_event(),
Listening(s) => s.next_event(),
Shutdown(_) => break,
None => unreachable!("Node cannot be in a `None` state"),
};
Expand Down
68 changes: 68 additions & 0 deletions base_layer/core/src/base_node/states/block_sync.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright 2019. The Tari Project
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
// following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
// disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
// following disclaimer in the documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
// products derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use crate::{
base_node::states::{
fetching_horizon_state::FetchHorizonState,
listening::Listening,
InitialSync,
StateEvent,
StateEvent::FatalError,
},
chain_storage::BlockchainBackend,
};
use log::*;

const LOG_TARGET: &str = "base_node::block_sync";

pub struct BlockSync;

impl BlockSync {
pub fn next_event(&mut self) -> StateEvent {
info!(target: LOG_TARGET, "Synchronizing missing blocks");
FatalError("Unimplemented".into())
}
}

/// State management for FetchingHorizonState -> BlockSync. This is a typical transition for new nodes joining the
/// network.
impl From<FetchHorizonState> for BlockSync {
fn from(_old: FetchHorizonState) -> Self {
unimplemented!()
}
}

/// State management for Listening -> BlockSync. This change happens when a node has been temporarily disconnected
/// from the network, or a reorg has occurred.
impl From<Listening> for BlockSync {
fn from(_old: Listening) -> Self {
unimplemented!()
}
}

/// State management for InitialSync -> BlockSync. This change happens when a (previously synced) node is restarted
/// after being offline for some time.
impl<B: BlockchainBackend> From<InitialSync<B>> for BlockSync {
fn from(_old: InitialSync<B>) -> Self {
unimplemented!()
}
}
54 changes: 54 additions & 0 deletions base_layer/core/src/base_node/states/fetching_horizon_state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2019. The Tari Project
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
// following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
// disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
// following disclaimer in the documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
// products derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use crate::{
base_node::states::{listening::Listening, InitialSync, StateEvent, StateEvent::FatalError},
chain_storage::BlockchainBackend,
};
use log::*;

const LOG_TARGET: &str = "base_node::fetching_horizon_state";

pub struct FetchHorizonState;

impl FetchHorizonState {
pub fn next_event(&mut self) -> StateEvent {
info!(target: LOG_TARGET, "Starting synchronization of pruning horizon state");
FatalError("Unimplemented".into())
}
}

/// State management for InitialSync -> FetchingHorizonState. This is the typical transition for a new node joining
/// the network
impl<B: BlockchainBackend> From<InitialSync<B>> for FetchHorizonState {
fn from(_old: InitialSync<B>) -> Self {
unimplemented!()
}
}

/// State management for Listening -> FetchingHorizonState. This can occur if a node has been disconnected from the
/// network for a long time.
impl From<Listening> for FetchHorizonState {
fn from(_old: Listening) -> Self {
unimplemented!()
}
}
1 change: 1 addition & 0 deletions base_layer/core/src/base_node/states/initial_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ impl<B: BlockchainBackend> InitialSync<B> {
}
}

/// State management for Starting -> InitialSync. This state change occurs every time a node is restarted.
impl<B: BlockchainBackend> From<Starting<B>> for InitialSync<B> {
fn from(old_state: Starting<B>) -> Self {
InitialSync { db: old_state.db }
Expand Down
54 changes: 54 additions & 0 deletions base_layer/core/src/base_node/states/listening.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2019. The Tari Project
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
// following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
// disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
// following disclaimer in the documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
// products derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use crate::{
base_node::states::{block_sync::BlockSync, InitialSync, StateEvent, StateEvent::FatalError},
chain_storage::BlockchainBackend,
};
use log::*;

const LOG_TARGET: &str = "base_node::listening";

pub struct Listening;

impl Listening {
pub fn next_event(&mut self) -> StateEvent {
info!(target: LOG_TARGET, "Listening for new blocks");
FatalError("Unimplemented".into())
}
}

/// State management for BlockSync -> Listening. This change is part of the typical flow for new nodes joining the
/// network, or established nodes that have caught up to the chain tip again.
impl From<BlockSync> for Listening {
fn from(_old: BlockSync) -> Self {
unimplemented!()
}
}

/// State management for BlockSync -> Listening. This state change happens when a node restarts and still happens to
/// be in sync with the network.
impl<B: BlockchainBackend> From<InitialSync<B>> for Listening {
fn from(_old: InitialSync<B>) -> Self {
unimplemented!()
}
}
20 changes: 14 additions & 6 deletions base_layer/core/src/base_node/states/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,9 @@ pub enum BaseNodeState<B: BlockchainBackend> {
None,
Starting(Starting<B>),
InitialSync(InitialSync<B>),
FetchingHorizonState,
BlockSync,
Listening,
FetchingHorizonState(FetchHorizonState),
BlockSync(BlockSync),
Listening(Listening),
Shutdown(Shutdown),
}

Expand Down Expand Up @@ -146,20 +146,28 @@ impl<B: BlockchainBackend> Display for BaseNodeState<B> {
let s = match self {
Self::Starting(_) => "Initializing",
Self::InitialSync(_) => "Synchronizing blockchain metadata",
Self::FetchingHorizonState => "Fetching horizon state",
Self::BlockSync => "Synchronizing blocks",
Self::Listening => "Listening",
Self::FetchingHorizonState(_) => "Fetching horizon state",
Self::BlockSync(_) => "Synchronizing blocks",
Self::Listening(_) => "Listening",
Self::Shutdown(_) => "Shutting down",
Self::None => "None",
};
f.write_str(s)
}
}

mod block_sync;
mod fetching_horizon_state;
mod initial_sync;
mod listening;
mod shutdown_state;
mod starting_state;

use crate::base_node::states::{
block_sync::BlockSync,
fetching_horizon_state::FetchHorizonState,
listening::Listening,
};
pub use initial_sync::InitialSync;
pub use shutdown_state::Shutdown;
pub use starting_state::Starting;

0 comments on commit 254b2d0

Please sign in to comment.