Skip to content

Commit 254b2d0

Browse files
committed
Add boilerplate for base node FSM (#834)
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.
2 parents 1f7dfc9 + 7ee6990 commit 254b2d0

File tree

6 files changed

+202
-17
lines changed

6 files changed

+202
-17
lines changed

base_layer/core/src/base_node/base_node.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
use crate::{
2424
base_node::{
2525
states,
26-
states::{BaseNodeState, StateEvent, StateEvent::FatalError},
26+
states::{BaseNodeState, StateEvent},
2727
BaseNodeConfig,
2828
},
2929
chain_storage::{BlockchainBackend, BlockchainDatabase},
@@ -81,13 +81,13 @@ impl<B: BlockchainBackend> BaseNodeStateMachine<B> {
8181
use crate::base_node::states::{BaseNodeState::*, StateEvent::*, SyncStatus::*};
8282
match (state, event) {
8383
(Starting(s), Initialized) => InitialSync(s.into()),
84-
(InitialSync(_s), MetadataSynced(BehindHorizon)) => FetchingHorizonState,
85-
(InitialSync(_s), MetadataSynced(Lagging)) => BlockSync,
86-
(InitialSync(_s), MetadataSynced(UpToDate)) => Listening,
87-
(FetchingHorizonState, HorizonStateFetched) => BlockSync,
88-
(BlockSync, BlocksSynchronized) => Listening,
89-
(Listening, FallenBehind(BehindHorizon)) => FetchingHorizonState,
90-
(Listening, FallenBehind(Lagging)) => BlockSync,
84+
(InitialSync(s), MetadataSynced(BehindHorizon)) => FetchingHorizonState(s.into()),
85+
(InitialSync(s), MetadataSynced(Lagging)) => BlockSync(s.into()),
86+
(InitialSync(s), MetadataSynced(UpToDate)) => Listening(s.into()),
87+
(FetchingHorizonState(s), HorizonStateFetched) => BlockSync(s.into()),
88+
(BlockSync(s), BlocksSynchronized) => Listening(s.into()),
89+
(Listening(s), FallenBehind(BehindHorizon)) => FetchingHorizonState(s.into()),
90+
(Listening(s), FallenBehind(Lagging)) => BlockSync(s.into()),
9191
(_, FatalError(s)) => Shutdown(states::Shutdown::with_reason(s)),
9292
(s, e) => {
9393
debug!(
@@ -107,9 +107,9 @@ impl<B: BlockchainBackend> BaseNodeStateMachine<B> {
107107
let next_event = match &mut self.state {
108108
Starting(s) => s.next_event(),
109109
InitialSync(s) => s.next_event(),
110-
FetchingHorizonState => FatalError("Unimplemented".into()),
111-
BlockSync => FatalError("Unimplemented".into()),
112-
Listening => FatalError("Unimplemented".into()),
110+
FetchingHorizonState(s) => s.next_event(),
111+
BlockSync(s) => s.next_event(),
112+
Listening(s) => s.next_event(),
113113
Shutdown(_) => break,
114114
None => unreachable!("Node cannot be in a `None` state"),
115115
};
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright 2019. The Tari Project
2+
//
3+
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
4+
// following conditions are met:
5+
//
6+
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
7+
// disclaimer.
8+
//
9+
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
10+
// following disclaimer in the documentation and/or other materials provided with the distribution.
11+
//
12+
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
13+
// products derived from this software without specific prior written permission.
14+
//
15+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
16+
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17+
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18+
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19+
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
20+
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
21+
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22+
23+
use crate::{
24+
base_node::states::{
25+
fetching_horizon_state::FetchHorizonState,
26+
listening::Listening,
27+
InitialSync,
28+
StateEvent,
29+
StateEvent::FatalError,
30+
},
31+
chain_storage::BlockchainBackend,
32+
};
33+
use log::*;
34+
35+
const LOG_TARGET: &str = "base_node::block_sync";
36+
37+
pub struct BlockSync;
38+
39+
impl BlockSync {
40+
pub fn next_event(&mut self) -> StateEvent {
41+
info!(target: LOG_TARGET, "Synchronizing missing blocks");
42+
FatalError("Unimplemented".into())
43+
}
44+
}
45+
46+
/// State management for FetchingHorizonState -> BlockSync. This is a typical transition for new nodes joining the
47+
/// network.
48+
impl From<FetchHorizonState> for BlockSync {
49+
fn from(_old: FetchHorizonState) -> Self {
50+
unimplemented!()
51+
}
52+
}
53+
54+
/// State management for Listening -> BlockSync. This change happens when a node has been temporarily disconnected
55+
/// from the network, or a reorg has occurred.
56+
impl From<Listening> for BlockSync {
57+
fn from(_old: Listening) -> Self {
58+
unimplemented!()
59+
}
60+
}
61+
62+
/// State management for InitialSync -> BlockSync. This change happens when a (previously synced) node is restarted
63+
/// after being offline for some time.
64+
impl<B: BlockchainBackend> From<InitialSync<B>> for BlockSync {
65+
fn from(_old: InitialSync<B>) -> Self {
66+
unimplemented!()
67+
}
68+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright 2019. The Tari Project
2+
//
3+
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
4+
// following conditions are met:
5+
//
6+
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
7+
// disclaimer.
8+
//
9+
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
10+
// following disclaimer in the documentation and/or other materials provided with the distribution.
11+
//
12+
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
13+
// products derived from this software without specific prior written permission.
14+
//
15+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
16+
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17+
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18+
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19+
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
20+
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
21+
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22+
23+
use crate::{
24+
base_node::states::{listening::Listening, InitialSync, StateEvent, StateEvent::FatalError},
25+
chain_storage::BlockchainBackend,
26+
};
27+
use log::*;
28+
29+
const LOG_TARGET: &str = "base_node::fetching_horizon_state";
30+
31+
pub struct FetchHorizonState;
32+
33+
impl FetchHorizonState {
34+
pub fn next_event(&mut self) -> StateEvent {
35+
info!(target: LOG_TARGET, "Starting synchronization of pruning horizon state");
36+
FatalError("Unimplemented".into())
37+
}
38+
}
39+
40+
/// State management for InitialSync -> FetchingHorizonState. This is the typical transition for a new node joining
41+
/// the network
42+
impl<B: BlockchainBackend> From<InitialSync<B>> for FetchHorizonState {
43+
fn from(_old: InitialSync<B>) -> Self {
44+
unimplemented!()
45+
}
46+
}
47+
48+
/// State management for Listening -> FetchingHorizonState. This can occur if a node has been disconnected from the
49+
/// network for a long time.
50+
impl From<Listening> for FetchHorizonState {
51+
fn from(_old: Listening) -> Self {
52+
unimplemented!()
53+
}
54+
}

base_layer/core/src/base_node/states/initial_sync.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ impl<B: BlockchainBackend> InitialSync<B> {
6262
}
6363
}
6464

65+
/// State management for Starting -> InitialSync. This state change occurs every time a node is restarted.
6566
impl<B: BlockchainBackend> From<Starting<B>> for InitialSync<B> {
6667
fn from(old_state: Starting<B>) -> Self {
6768
InitialSync { db: old_state.db }
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright 2019. The Tari Project
2+
//
3+
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
4+
// following conditions are met:
5+
//
6+
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
7+
// disclaimer.
8+
//
9+
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
10+
// following disclaimer in the documentation and/or other materials provided with the distribution.
11+
//
12+
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
13+
// products derived from this software without specific prior written permission.
14+
//
15+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
16+
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17+
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18+
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19+
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
20+
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
21+
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22+
23+
use crate::{
24+
base_node::states::{block_sync::BlockSync, InitialSync, StateEvent, StateEvent::FatalError},
25+
chain_storage::BlockchainBackend,
26+
};
27+
use log::*;
28+
29+
const LOG_TARGET: &str = "base_node::listening";
30+
31+
pub struct Listening;
32+
33+
impl Listening {
34+
pub fn next_event(&mut self) -> StateEvent {
35+
info!(target: LOG_TARGET, "Listening for new blocks");
36+
FatalError("Unimplemented".into())
37+
}
38+
}
39+
40+
/// State management for BlockSync -> Listening. This change is part of the typical flow for new nodes joining the
41+
/// network, or established nodes that have caught up to the chain tip again.
42+
impl From<BlockSync> for Listening {
43+
fn from(_old: BlockSync) -> Self {
44+
unimplemented!()
45+
}
46+
}
47+
48+
/// State management for BlockSync -> Listening. This state change happens when a node restarts and still happens to
49+
/// be in sync with the network.
50+
impl<B: BlockchainBackend> From<InitialSync<B>> for Listening {
51+
fn from(_old: InitialSync<B>) -> Self {
52+
unimplemented!()
53+
}
54+
}

base_layer/core/src/base_node/states/mod.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,9 @@ pub enum BaseNodeState<B: BlockchainBackend> {
114114
None,
115115
Starting(Starting<B>),
116116
InitialSync(InitialSync<B>),
117-
FetchingHorizonState,
118-
BlockSync,
119-
Listening,
117+
FetchingHorizonState(FetchHorizonState),
118+
BlockSync(BlockSync),
119+
Listening(Listening),
120120
Shutdown(Shutdown),
121121
}
122122

@@ -146,20 +146,28 @@ impl<B: BlockchainBackend> Display for BaseNodeState<B> {
146146
let s = match self {
147147
Self::Starting(_) => "Initializing",
148148
Self::InitialSync(_) => "Synchronizing blockchain metadata",
149-
Self::FetchingHorizonState => "Fetching horizon state",
150-
Self::BlockSync => "Synchronizing blocks",
151-
Self::Listening => "Listening",
149+
Self::FetchingHorizonState(_) => "Fetching horizon state",
150+
Self::BlockSync(_) => "Synchronizing blocks",
151+
Self::Listening(_) => "Listening",
152152
Self::Shutdown(_) => "Shutting down",
153153
Self::None => "None",
154154
};
155155
f.write_str(s)
156156
}
157157
}
158158

159+
mod block_sync;
160+
mod fetching_horizon_state;
159161
mod initial_sync;
162+
mod listening;
160163
mod shutdown_state;
161164
mod starting_state;
162165

166+
use crate::base_node::states::{
167+
block_sync::BlockSync,
168+
fetching_horizon_state::FetchHorizonState,
169+
listening::Listening,
170+
};
163171
pub use initial_sync::InitialSync;
164172
pub use shutdown_state::Shutdown;
165173
pub use starting_state::Starting;

0 commit comments

Comments
 (0)