@@ -27,16 +27,30 @@ extern crate clap;
2727mod opts;
2828
2929use std:: fs;
30+ use std:: path:: Path ;
3031use std:: process:: { ExitCode , Termination , exit} ;
3132
3233pub use bpnode;
33- use bpnode:: { Broker , BrokerError , Config , PATH_INDEXDB } ;
34+ use bpnode:: { Broker , BrokerError , Config , PATH_INDEXDB , initialize_db_tables} ;
35+ use bpwallet:: Network ;
3436use clap:: Parser ;
3537use loglevel:: LogLevel ;
3638use redb:: Database ;
3739
3840use crate :: opts:: { Command , Opts } ;
3941
42+ // Exit status codes for different error conditions
43+ // see also constants in `db.rs`
44+ const EXIT_PATH_ACCESS_ERROR : i32 = 1 ;
45+ const EXIT_DB_EXISTS_ERROR : i32 = 2 ;
46+ const EXIT_DIR_CREATE_ERROR : i32 = 3 ;
47+ const EXIT_DB_CREATE_ERROR : i32 = 4 ;
48+ const EXIT_DB_OPEN_ERROR : i32 = 5 ;
49+ const EXIT_NETWORK_MISMATCH : i32 = 10 ;
50+ const EXIT_NO_NETWORK_INFO : i32 = 11 ;
51+ const EXIT_DB_NOT_FOUND : i32 = 12 ;
52+
53+ /// Wrapper for result status to implement Termination trait
4054struct Status ( Result < ( ) , BrokerError > ) ;
4155
4256impl Termination for Status {
@@ -58,37 +72,124 @@ fn main() -> Status {
5872 log:: debug!( "Command-line arguments: {:#?}" , & opts) ;
5973
6074 match opts. command {
61- Some ( Command :: Init ) => {
62- eprint ! ( "Initializing ... " ) ;
63- let index_path = opts. general . data_dir . join ( PATH_INDEXDB ) ;
64- match fs:: exists ( & index_path) {
65- Err ( err) => {
66- eprintln ! ( "unable to access path '{}': {err}" , index_path. display( ) ) ;
67- exit ( 1 ) ;
68- }
69- Ok ( true ) => {
70- eprintln ! ( "index database directory already exists, cancelling" ) ;
71- exit ( 2 ) ;
72- }
73- Ok ( false ) => { }
74- }
75- if let Err ( err) = fs:: create_dir_all ( & opts. general . data_dir ) {
75+ Some ( Command :: Init ) => initialize_database ( & opts) ,
76+ None => run_node ( opts) ,
77+ }
78+ }
79+
80+ /// Initialize a new database for the BP Node
81+ fn initialize_database ( opts : & Opts ) -> Status {
82+ eprint ! ( "Initializing ... " ) ;
83+
84+ // Prepare the database path
85+ let index_path = opts. general . data_dir . join ( PATH_INDEXDB ) ;
86+
87+ // Check if database already exists
88+ if let Err ( err) = check_db_path ( & index_path, false ) {
89+ return err;
90+ }
91+
92+ // Create data directory if needed
93+ if let Err ( err) = fs:: create_dir_all ( & opts. general . data_dir ) {
94+ eprintln ! (
95+ "Unable to create data directory at '{}'\n {err}" ,
96+ opts. general. data_dir. display( )
97+ ) ;
98+ exit ( EXIT_DIR_CREATE_ERROR ) ;
99+ }
100+
101+ // Create the database
102+ let db = match Database :: create ( & index_path) {
103+ Ok ( db) => db,
104+ Err ( err) => {
105+ eprintln ! ( "Unable to create index database.\n {err}" ) ;
106+ exit ( EXIT_DB_CREATE_ERROR ) ;
107+ }
108+ } ;
109+
110+ // Initialize database with network information and create all tables
111+ let network = opts. general . network ;
112+ initialize_db_tables ( & db, network) ;
113+
114+ eprintln ! ( "Index database initialized for {} network, exiting" , network) ;
115+ Status ( Ok ( ( ) ) )
116+ }
117+
118+ /// Run the BP Node service
119+ fn run_node ( opts : Opts ) -> Status {
120+ let conf = Config :: from ( opts) ;
121+ let index_path = conf. data_dir . join ( PATH_INDEXDB ) ;
122+
123+ // Check if database exists
124+ if let Err ( err) = check_db_path ( & index_path, true ) {
125+ return err;
126+ }
127+
128+ // Verify network configuration
129+ verify_network_configuration ( & index_path, & conf. network ) ;
130+
131+ // Start the broker service
132+ Status ( Broker :: start ( conf) . and_then ( |runtime| runtime. run ( ) ) )
133+ }
134+
135+ /// Check if database path exists or not, depending on expected state
136+ fn check_db_path ( index_path : & Path , should_exist : bool ) -> Result < ( ) , Status > {
137+ match fs:: exists ( index_path) {
138+ Err ( err) => {
139+ eprintln ! ( "Unable to access path '{}': {err}" , index_path. display( ) ) ;
140+ exit ( EXIT_PATH_ACCESS_ERROR ) ;
141+ }
142+ Ok ( exists) => {
143+ if exists && !should_exist {
144+ eprintln ! ( "Index database directory already exists, cancelling" ) ;
145+ exit ( EXIT_DB_EXISTS_ERROR ) ;
146+ } else if !exists && should_exist {
76147 eprintln ! (
77- "unable to create data directory at '{}'\n {err}" ,
78- opts. general. data_dir. display( )
148+ "ERROR: Database not found! Please initialize with 'bpd init' command first."
79149 ) ;
80- exit ( 3 ) ;
150+ exit ( EXIT_DB_NOT_FOUND ) ;
81151 }
82- if let Err ( err) = Database :: create ( & index_path) {
83- eprintln ! ( "unable to create index database.\n {err}" ) ;
84- exit ( 4 ) ;
85- }
86- eprintln ! ( "index database initialized, exiting" ) ;
87- Status ( Ok ( ( ) ) )
88- }
89- None => {
90- let conf = Config :: from ( opts) ;
91- Status ( Broker :: start ( conf) . and_then ( |runtime| runtime. run ( ) ) )
92152 }
93153 }
154+ Ok ( ( ) )
155+ }
156+
157+ /// Verify that database network configuration matches the configured network
158+ fn verify_network_configuration ( index_path : & Path , configured_network : & Network ) {
159+ let Ok ( db) = Database :: open ( index_path)
160+ . inspect_err ( |err| eprintln ! ( "Error: could not open the database due to {err}" ) )
161+ else {
162+ exit ( EXIT_DB_OPEN_ERROR )
163+ } ;
164+ let Ok ( tx) = db
165+ . begin_read ( )
166+ . inspect_err ( |err| eprintln ! ( "Error: could not access the database due to {err}" ) )
167+ else {
168+ exit ( EXIT_DB_OPEN_ERROR )
169+ } ;
170+ let Ok ( main_table) = tx
171+ . open_table ( bpnode:: db:: TABLE_MAIN )
172+ . inspect_err ( |err| eprintln ! ( "Error: could not open the main table due to {err}" ) )
173+ else {
174+ exit ( EXIT_DB_OPEN_ERROR )
175+ } ;
176+ let Ok ( Some ( network_rec) ) = main_table. get ( bpnode:: REC_NETWORK ) else {
177+ // Network information isn't found in the database
178+ eprintln ! ( "ERROR: Database exists but doesn't contain network information." ) ;
179+ eprintln ! ( "Please reinitialize the database with `bpd init` command." ) ;
180+ exit ( EXIT_NO_NETWORK_INFO ) ;
181+ } ;
182+ let stored_network = String :: from_utf8_lossy ( network_rec. value ( ) ) ;
183+ if stored_network != configured_network. to_string ( ) {
184+ eprintln ! ( "ERROR: Database network mismatch!" ) ;
185+ eprintln ! ( "Configured network: {}" , configured_network) ;
186+ eprintln ! ( "Database network: {}" , stored_network) ;
187+ eprintln ! ( "Each BP-Node instance works with a single chain." ) ;
188+ eprintln ! (
189+ "To use a different network, create a separate instance with a different data \
190+ directory."
191+ ) ;
192+ exit ( EXIT_NETWORK_MISMATCH ) ;
193+ }
194+ log:: info!( "Database network matches configured network: {}" , stored_network) ;
94195}
0 commit comments