11//! Shared node state aggregating subsystem handles.
22//!
3- //! V1 keeps this deliberately minimal: it owns the resolved [`Config`] and a
4- //! data-directory path, plus the replay log used by [`crate::crash_recovery`].
5- //! Subsystem wiring (chain / utxo / mempool / index / p2p / rpc / electrum)
6- //! is constructed by the binary at boot and parks here as the integration
7- //! point matures.
3+ //! V1 keeps this deliberately minimal: it owns the resolved [`Config`], the
4+ //! data-directory path, the open chainstate storage backend, and the replay log
5+ //! used by [`crate::crash_recovery`]. Subsystem wiring (chain / utxo / mempool
6+ //! / index / p2p / rpc / electrum) parks here as the integration point matures.
87
8+ use core:: fmt;
99use std:: path:: { Path , PathBuf } ;
1010
11- use anyhow:: { Context as _, Result } ;
11+ use anyhow:: { Context as _, Result , bail } ;
1212use parking_lot:: Mutex ;
1313
1414use crate :: Config ;
1515
16+ enum NodeStorage {
17+ #[ cfg( feature = "rocksdb" ) ]
18+ RocksDb ( bitcoin_rs_storage:: RocksDbStore ) ,
19+ #[ cfg( feature = "fjall" ) ]
20+ Fjall ( bitcoin_rs_storage:: FjallStore ) ,
21+ #[ cfg( feature = "redb" ) ]
22+ Redb ( bitcoin_rs_storage:: RedbStore ) ,
23+ #[ cfg( feature = "mdbx" ) ]
24+ Mdbx ( bitcoin_rs_storage:: MdbxStore ) ,
25+ }
26+
27+ impl NodeStorage {
28+ fn open ( config : & Config ) -> Result < Self > {
29+ let chainstate_dir = config. data_dir . join ( "chainstate" ) ;
30+ std:: fs:: create_dir_all ( & chainstate_dir)
31+ . with_context ( || format ! ( "create chainstate_dir {}" , chainstate_dir. display( ) ) ) ?;
32+
33+ match config. storage_backend . as_str ( ) {
34+ #[ cfg( feature = "rocksdb" ) ]
35+ "rocksdb" => Ok ( Self :: RocksDb (
36+ bitcoin_rs_storage:: RocksDbStore :: open ( & chainstate_dir)
37+ . map_err ( anyhow:: Error :: new) ?,
38+ ) ) ,
39+ #[ cfg( feature = "fjall" ) ]
40+ "fjall" => Ok ( Self :: Fjall (
41+ bitcoin_rs_storage:: FjallStore :: open ( & chainstate_dir)
42+ . map_err ( anyhow:: Error :: new) ?,
43+ ) ) ,
44+ #[ cfg( feature = "redb" ) ]
45+ "redb" => Ok ( Self :: Redb (
46+ bitcoin_rs_storage:: RedbStore :: open ( & chainstate_dir) . map_err ( anyhow:: Error :: new) ?,
47+ ) ) ,
48+ #[ cfg( feature = "mdbx" ) ]
49+ "mdbx" => Ok ( Self :: Mdbx (
50+ bitcoin_rs_storage:: MdbxStore :: open ( & chainstate_dir) . map_err ( anyhow:: Error :: new) ?,
51+ ) ) ,
52+ other => bail ! (
53+ "unsupported storage backend: {other} (compiled features = {CompiledStorageFeatures})"
54+ ) ,
55+ }
56+ }
57+
58+ const fn kind ( & self ) -> & ' static str {
59+ match self {
60+ #[ cfg( feature = "rocksdb" ) ]
61+ Self :: RocksDb ( store) => {
62+ let _ = store;
63+ "rocksdb"
64+ }
65+ #[ cfg( feature = "fjall" ) ]
66+ Self :: Fjall ( store) => {
67+ let _ = store;
68+ "fjall"
69+ }
70+ #[ cfg( feature = "redb" ) ]
71+ Self :: Redb ( store) => {
72+ let _ = store;
73+ "redb"
74+ }
75+ #[ cfg( feature = "mdbx" ) ]
76+ Self :: Mdbx ( store) => {
77+ let _ = store;
78+ "mdbx"
79+ }
80+ }
81+ }
82+ }
83+
84+ const COMPILED_STORAGE_FEATURES : & [ & str ] = & [
85+ #[ cfg( feature = "rocksdb" ) ]
86+ "rocksdb" ,
87+ #[ cfg( feature = "fjall" ) ]
88+ "fjall" ,
89+ #[ cfg( feature = "redb" ) ]
90+ "redb" ,
91+ #[ cfg( feature = "mdbx" ) ]
92+ "mdbx" ,
93+ ] ;
94+
95+ struct CompiledStorageFeatures ;
96+
97+ impl fmt:: Display for CompiledStorageFeatures {
98+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
99+ let Some ( ( first, rest) ) = COMPILED_STORAGE_FEATURES . split_first ( ) else {
100+ return f. write_str ( "none" ) ;
101+ } ;
102+
103+ f. write_str ( first) ?;
104+ for feature in rest {
105+ f. write_str ( "," ) ?;
106+ f. write_str ( feature) ?;
107+ }
108+ Ok ( ( ) )
109+ }
110+ }
111+
16112/// Aggregate handle to a running node.
17113pub struct NodeState {
18114 config : Config ,
19115 data_dir : PathBuf ,
116+ storage : NodeStorage ,
20117 replayed : Mutex < Vec < u32 > > ,
21118}
22119
23120impl NodeState {
24- /// Opens (or creates) the node's data directory and constructs the
25- /// in-memory state. The configured storage backend is not yet bound at
26- /// this layer; see [`crate::run::run`].
121+ /// Opens (or creates) the node's data directory and configured storage
122+ /// backend.
27123 pub fn open ( config : Config ) -> Result < Self > {
28124 std:: fs:: create_dir_all ( & config. data_dir )
29125 . with_context ( || format ! ( "create data_dir {}" , config. data_dir. display( ) ) ) ?;
126+ let storage = NodeStorage :: open ( & config) ?;
127+ tracing:: info!(
128+ backend = storage. kind( ) ,
129+ chainstate_dir = %config. data_dir. join( "chainstate" ) . display( ) ,
130+ "opened storage backend"
131+ ) ;
30132 let data_dir = config. data_dir . clone ( ) ;
31133 Ok ( Self {
32134 config,
33135 data_dir,
136+ storage,
34137 replayed : Mutex :: new ( Vec :: new ( ) ) ,
35138 } )
36139 }
@@ -47,6 +150,12 @@ impl NodeState {
47150 & self . data_dir
48151 }
49152
153+ /// Returns the configured storage backend that was opened.
154+ #[ must_use]
155+ pub const fn storage_kind ( & self ) -> & ' static str {
156+ self . storage . kind ( )
157+ }
158+
50159 /// Heights walked by the most recent crash-recovery replay.
51160 #[ must_use]
52161 pub fn replayed_heights ( & self ) -> Vec < u32 > {
0 commit comments