init
This commit is contained in:
		
						commit
						3fc3666389
					
				
							
								
								
									
										1168
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										1168
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										22
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| [package] | ||||
| name = "bal-server" | ||||
| version = "0.1.0" | ||||
| edition = "2021" | ||||
| 
 | ||||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||||
| [dependencies] | ||||
| hyper = { version = "1.2", features = ["full"] } | ||||
| tokio = { version = "1", features = ["full"] } | ||||
| http-body-util = "0.1" | ||||
| hyper-util = { version = "0.1", features = ["full"] } | ||||
| bytes = "1.2" | ||||
| serde = { version = "1.0.152", features = ["derive"] } # <- Only one serde version needed (serde or serde_derive)  | ||||
| serde_json = "1.0.116" | ||||
| confy = "0.6.1" | ||||
| bitcoin = "0.31.0" | ||||
| sqlite = "0.34.0" | ||||
| hex-conservative = "0.1.1" | ||||
| regex = "1.10.4" | ||||
| log = "0.4.21" | ||||
| env_logger = "0.11.5" | ||||
| 
 | ||||
							
								
								
									
										30
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | ||||
| # bal-server | ||||
| 
 | ||||
| ## Installation | ||||
| 
 | ||||
| ```bash | ||||
| $ git clone .... | ||||
| $ cd bal-server | ||||
| $ cargo build --release | ||||
| $ sudo cp target/release/bal-server /usr/local/bin | ||||
| $ bal-server | ||||
| ``` | ||||
| 
 | ||||
| ## Configuration | ||||
| 
 | ||||
| The `bal-server` application can be configured using environment variables. The following variables are available: | ||||
| 
 | ||||
| | Variable | Description | Default | | ||||
| | --- | --- | --- | | ||||
| | `BAL_SERVER_CONFIG_FILE` | Path to the configuration file. If the file does not exist, a new one will be created. | `$HOME/.config/bal-server/default-config.toml` | | ||||
| | `BAL_SERVER_DB_FILE` | Path to the SQLite3 database file. If the file does not exist, a new one will be created. | `bal.db` | | ||||
| | `BAL_SERVER_BIND_ADDRESS` | Public address for listening to requests. | `127.0.0.1` | | ||||
| | `BAL_SERVER_BIND_PORT` | Default port for listening to requests. | `9137` | | ||||
| | `BAL_SERVER_REGTEST_ADDRESS` | Bitcoin address for the regtest environment. | - | | ||||
| | `BAL_SERVER_REGTEST_FIXED_FEE` | Fixed fee for the regtest environment. | 50000 | | ||||
| | `BAL_SERVER_SIGNET_ADDRESS` | Bitcoin address for the signet environment. | - | | ||||
| | `BAL_SERVER_SIGNET_FIXED_FEE` | Fixed fee for the signet environment. | 50000 | | ||||
| | `BAL_SERVER_TESTNET_ADDRESS` | Bitcoin address for the testnet environment. | - | | ||||
| | `BAL_SERVER_TESTNET_FIXED_FEE` | Fixed fee for the testnet environment. | 50000 | | ||||
| | `BAL_SERVER_BITCOIN_ADDRESS` | Bitcoin address for the mainnet environment. | - | | ||||
| | `BAL_SERVER_BITCOIN_FIXED_FEE` | Fixed fee for the mainnet environment. | 50000 | | ||||
							
								
								
									
										11
									
								
								bal-server.env
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								bal-server.env
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| RUST_LOG=info | ||||
| BAL_SERVER_DB_FILE=/path/to/bal.db | ||||
| BAL_SERVER_BIND_ADDRESS=127.0.0.1 | ||||
| BAL_SERVER_BITCOIN_ADDRESS=your mainnet address | ||||
| BAL_SERVER_BITCOIN_FEE=100000 | ||||
| BAL_SERVER_REGTEST_ADDRESS= | ||||
| BAL_SERVER_REGTEST_FEE=100000 | ||||
| BAL_SERVER_TESTNET_ADDRESS= | ||||
| BAL_SERVER_TESTNET_FEE=100000 | ||||
| BAL_SERVER_SIGNET_ADDRESS= | ||||
| BAL_SERVER_SIGNET_FEE=100000 | ||||
							
								
								
									
										39
									
								
								bal-server.service
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								bal-server.service
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | ||||
| [Unit] | ||||
| Description=bal-server daemon | ||||
| After=network.target | ||||
| 
 | ||||
| [Service] | ||||
| 
 | ||||
| EnvironmentFile=/etc/bal/bal-server.env | ||||
| 
 | ||||
| ExecStart=/usr/local/bin/bal-server  | ||||
| 
 | ||||
| StandardOutput=syslog | ||||
| StandardError=syslog | ||||
| SyslogIdentifier=bal-server | ||||
| 
 | ||||
| Type=simple | ||||
| PIDFile=/run/bal-server/bal-server.pid | ||||
| Restart=always | ||||
| TimeoutSec=300 | ||||
| RestartSec=30 | ||||
| 
 | ||||
| User=bal | ||||
| UMask=0027 | ||||
| 
 | ||||
| RuntimeDirectory=bal-server | ||||
| RuntimeDirectoryMode=0710 | ||||
| 
 | ||||
| PrivateTmp=true | ||||
| 
 | ||||
| ProtectSystem=full | ||||
| 
 | ||||
| NoNewPrivileges=true | ||||
| 
 | ||||
| PrivateDevices=true | ||||
| 
 | ||||
| MemoryDenyWriteExecute=true | ||||
| 
 | ||||
| [Install] | ||||
| WantedBy=multi-user.target | ||||
| 
 | ||||
							
								
								
									
										501
									
								
								src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										501
									
								
								src/main.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,501 @@ | ||||
| 
 | ||||
| use bytes::Bytes; | ||||
| use http_body_util::{combinators::BoxBody, BodyExt, Empty, Full}; | ||||
| use hyper::server::conn::http1; | ||||
| use hyper::service::service_fn; | ||||
| use hyper::{Method, Request, Response, StatusCode}; | ||||
| use tokio::net::TcpListener; | ||||
| use hyper_util::rt::TokioIo; | ||||
| 
 | ||||
| 
 | ||||
| use std::net::IpAddr; | ||||
| use std::env; | ||||
| 
 | ||||
| use std::time::{SystemTime,UNIX_EPOCH}; | ||||
| use std::sync::{Arc, Mutex, MutexGuard}; | ||||
| use std::collections::HashMap; | ||||
| use sqlite::{State,Connection}; | ||||
| 
 | ||||
| use bitcoin::{consensus, Transaction, Network}; | ||||
| 
 | ||||
| use hex_conservative::FromHex; | ||||
| use regex::Regex; | ||||
| use serde::{Serialize, Deserialize}; | ||||
| use log::{info,error,trace,debug}; | ||||
| use serde_json; | ||||
| 
 | ||||
| #[derive(Debug, Clone,Serialize, Deserialize)] | ||||
| struct NetConfig { | ||||
|     address: String, | ||||
|     fixed_fee: u64, | ||||
| } | ||||
| 
 | ||||
| impl Default for NetConfig { | ||||
|     fn default() -> Self { | ||||
|         NetConfig { | ||||
|             address: "".to_string(), | ||||
|             fixed_fee: 50000, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl NetConfig { | ||||
|     fn default_regtest() -> Self { | ||||
|         NetConfig { | ||||
|             address: "".to_string(), | ||||
|             fixed_fee: 50000, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Serialize, Deserialize,Clone)] | ||||
| struct MyConfig { | ||||
|     regtest: NetConfig, | ||||
|     signet: NetConfig, | ||||
|     testnet4: NetConfig, | ||||
|     testnet3: NetConfig, | ||||
|     mainnet: NetConfig, | ||||
|     bind_address: String, | ||||
|     bind_port: u16, // Changed to u16 for port numbers
 | ||||
|     db_file: String, | ||||
| } | ||||
| 
 | ||||
| impl Default for MyConfig { | ||||
|     fn default() -> Self { | ||||
|         MyConfig { | ||||
|             regtest: NetConfig::default_regtest(), | ||||
|             signet: NetConfig::default(), // Use default for other networks
 | ||||
|             testnet4: NetConfig::default(), | ||||
|             testnet3: NetConfig::default(), | ||||
|             mainnet: NetConfig::default(), | ||||
|             bind_address: "127.0.0.1".to_string(), | ||||
|             bind_port: 9137, // Ensure this is a u16
 | ||||
|             db_file: "../bal.db".to_string(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| impl MyConfig { | ||||
|     fn get_net_config(&self, param: &str) -> &NetConfig{ | ||||
|         match param { | ||||
|             "regtest" => &self.regtest, | ||||
|             "testnet" => &self.testnet3, | ||||
|             "signet" => &self.signet, | ||||
|             _ => &self.mainnet, 
 | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| async fn echo_info( | ||||
|                    param: &str, | ||||
|                    cfg: &MyConfig, | ||||
| ) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> { | ||||
|         info!("echo info!!!{}",param); | ||||
|         let netconfig=MyConfig::get_net_config(cfg,param); | ||||
|         return Ok(Response::new(full("{\"address\":\"".to_owned()+&netconfig.address+"\",\"base_fee\":\""+&netconfig.fixed_fee.to_string()+"\"}"))); | ||||
| } | ||||
| async fn echo_search(whole_body: &Bytes, | ||||
|                      cfg: &MyConfig, | ||||
| ) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> { | ||||
|     info!("echo search!!!"); | ||||
|     let strbody = std::str::from_utf8(&whole_body).unwrap(); | ||||
|     info!("{}",strbody); | ||||
|     trace!("{}",strbody.len()); | ||||
| 
 | ||||
|     let mut response = Response::new(full("Bad data received".to_owned())); | ||||
|     *response.status_mut() = StatusCode::BAD_REQUEST; 
 | ||||
|     if strbody.len() >0 && strbody.len()<=70 { | ||||
|         let db = sqlite::open(&cfg.db_file).unwrap(); | ||||
|         trace!("qua ci arrivo"); | ||||
|         let mut statement = db.prepare("SELECT * FROM tbl_tx WHERE txid = ?").unwrap(); | ||||
|         statement.bind((1, strbody)).unwrap(); | ||||
| 
 | ||||
|         while let Ok(State::Row) = statement.next() { | ||||
|             trace!("qua tutto ok"); | ||||
|             let mut response_data = HashMap::new(); | ||||
|             match statement.read::<String, _>("status") { | ||||
|                 Ok(value) => response_data.insert("status", value), | ||||
|                 Err(e) => { | ||||
|                     error!("Error reading status: {}", e); | ||||
|                     break; | ||||
|                     //response_data.insert("status", "Error".to_string())
 | ||||
|                 } | ||||
|             }; | ||||
| 
 | ||||
|             // Read the transaction (tx)
 | ||||
|             match statement.read::<String, _>("tx") { | ||||
|                 Ok(value) => response_data.insert("tx", value), | ||||
|                 Err(e) => { | ||||
|                     error!("Error reading tx: {}", e); | ||||
|                     break; | ||||
|                     //response_data.insert("tx", "Error".to_string())
 | ||||
|                 } | ||||
|             }; | ||||
| 
 | ||||
|             match statement.read::<String, _>("our_address") { | ||||
|                 Ok(value) => response_data.insert("our_address", value), | ||||
|                 Err(e) => { | ||||
|                     error!("Error reading address: {}", e); | ||||
|                     break; | ||||
|                     //response_data.insert("tx", "Error".to_string())
 | ||||
|                 } | ||||
|             }; | ||||
| 
 | ||||
|             match statement.read::<String, _>("our_fees") { | ||||
|                 Ok(value) => response_data.insert("our_fees", value), | ||||
|                 Err(e) => { | ||||
|                     error!("Error reading fees: {}", e); | ||||
|                     break; | ||||
|                     //response_data.insert("tx", "Error".to_string())
 | ||||
|                 } | ||||
|             }; | ||||
| 
 | ||||
|             // Read the request id (reqid)
 | ||||
|             match statement.read::<String, _>("reqid") { | ||||
|                 Ok(value) => response_data.insert("time", value), | ||||
|                 Err(e) => { | ||||
|                     error!("Error reading reqid: {}", e); | ||||
|                     break; | ||||
|                     //response_data.insert("time", "Error".to_string())
 | ||||
|                 } | ||||
|             }; | ||||
|             response = match serde_json::to_string(&response_data){ | ||||
|                 Ok(json_data) => Response::new(full(json_data)), | ||||
|                 Err(_) => {break;} | ||||
|             }; | ||||
| 
 | ||||
|             return Ok(response); | ||||
|         } | ||||
|     } | ||||
|     Ok(response) | ||||
| 
 | ||||
|     
 | ||||
| } | ||||
| async fn echo_push(whole_body: &Bytes, | ||||
|                    cfg: &MyConfig, | ||||
|                    param: &str, | ||||
| ) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> { | ||||
|         //let whole_body = req.collect().await?.to_bytes();
 | ||||
|         let strbody = std::str::from_utf8(&whole_body).unwrap(); | ||||
|         let lines = strbody.split("\n"); | ||||
| 
 | ||||
|         let mut response = Response::new(full("Bad data received".to_owned())); | ||||
|         *response.status_mut() = StatusCode::BAD_REQUEST; 
 | ||||
|         debug!("network: {}", param); | ||||
|         let network = match param{ | ||||
|             "testnet" => Network::Testnet, | ||||
|             "signet" => Network::Signet, | ||||
|             "regtest" => Network::Regtest, | ||||
|             &_ => Network::Bitcoin, | ||||
|         }; | ||||
|         
 | ||||
|         let req_time = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards").as_nanos(); | ||||
|         
 | ||||
|         
 | ||||
|         let sqltxshead = "INSERT INTO tbl_tx (txid, wtxid, ntxid, tx, locktime, reqid, network, our_address, our_fees)".to_string(); | ||||
|         let mut sqltxs = "".to_string(); | ||||
|         //let mut sqlinps = "INSERT INTO tbl_input (txid, in_txid,in_vout)".to_string();
 | ||||
|         //let mut sqlouts = "INSERT INTO tbl_output (txid, script_pubkey, address, amount)\n".to_string();
 | ||||
|         let mut union_tx = true; | ||||
|         let mut already_present = false; | ||||
|         //let mut union_inps = true;
 | ||||
|         //let mut union_outs = true;
 | ||||
|         let db = sqlite::open(&cfg.db_file).unwrap(); | ||||
|         let netconfig = MyConfig::get_net_config(cfg,param); | ||||
|         for line in lines { | ||||
|             if line.len() == 0{ | ||||
|                 trace!("line len is: {}",line.len()); | ||||
|                 continue
 | ||||
|             } | ||||
|             let linea = format!("{req_time}:{line}"); | ||||
|             info!("New Tx: {}", linea); | ||||
|             let raw_tx =  match Vec::<u8>::from_hex(line) { | ||||
|                 Ok(raw_tx) => raw_tx, | ||||
|                 Err(err) => { | ||||
|                     error!("rawtx error: {}",err); | ||||
|                     continue
 | ||||
|                 } | ||||
|             }; | ||||
|             if raw_tx.len() > 0 { | ||||
|                 trace!("len: {}",raw_tx.len()); | ||||
|                 let tx: Transaction = match consensus::deserialize(&raw_tx){ | ||||
|                     Ok(tx) => tx, | ||||
|                     Err(err) => {error!("error: unable to parse tx: {}\n{}",line,err);continue} | ||||
|                 }; | ||||
|                 let txid = tx.txid().to_string(); | ||||
|                 trace!("txid: {}",txid); | ||||
|                 let mut statement = db.prepare("SELECT * FROM tbl_tx WHERE txid = ?").unwrap(); | ||||
|                 statement.bind((1,&txid[..])).unwrap(); | ||||
|                 //statement.bind((1,"Bob")).unwrap();
 | ||||
|                 if let Ok(State::Row) = statement.next() { | ||||
|                     trace!("already present"); | ||||
|                     already_present=true; | ||||
|                     continue; | ||||
|                 } | ||||
|                 let ntxid = tx.ntxid(); | ||||
|                 let wtxid = tx.wtxid(); | ||||
|                 let mut found = false; | ||||
|                 let locktime = tx.lock_time; | ||||
|                 let mut our_fees = 0; | ||||
|                 let mut our_address:String = "".to_string(); | ||||
|                 //dbg!(netconfig.fixed_fee);
 | ||||
|                 if netconfig.fixed_fee >0 { | ||||
|                     for output in tx.output{ | ||||
|                         let script_pubkey = output.script_pubkey; | ||||
|                         let address = match bitcoin::Address::from_script(script_pubkey.as_script(), network){ | ||||
|                             Ok(address) => address.to_string(), | ||||
|                             Err(_) => String::new(), | ||||
|                         }; | ||||
|                         //dbg!(&address);
 | ||||
|                         let amount = output.value; | ||||
|                         //dbg!(&amount); 
 | ||||
|                         //search wllexecutor output
 | ||||
|                         if address == netconfig.address.to_string() && amount.to_sat() >= netconfig.fixed_fee{ | ||||
|                             our_fees = amount.to_sat(); | ||||
|                             our_address = netconfig.address.to_string(); | ||||
|                             found = true; | ||||
|                             trace!("address and fees are correct {}: {}",our_address,our_fees); | ||||
|                             break; | ||||
|                         }else { | ||||
|                             trace!("address and fees not found {}: {}",address,amount.to_sat()); | ||||
|                             trace!("address and fees not found {}: {}",netconfig.address.to_string(),netconfig.fixed_fee); | ||||
|                         } | ||||
|                     } | ||||
|                 } else { found = true; } | ||||
|                 if found == false{ | ||||
|                     error!("willexecutor output not found "); | ||||
|                     //return Ok(response)
 | ||||
|                 } else { | ||||
|                     if union_tx == false { | ||||
|                         sqltxs = format!("{sqltxs} UNION ALL"); | ||||
|                     }else{ | ||||
|                         union_tx = false; | ||||
|                     } | ||||
|                     sqltxs = format!("{sqltxs}  SELECT '{txid}', '{wtxid}', '{ntxid}', '{line}', '{locktime}', '{req_time}', '{network}','{our_address}',{our_fees}"); | ||||
|                 } | ||||
| 
 | ||||
|             }            
 | ||||
|             else{ | ||||
|                 trace!("rawTx len is: {}",raw_tx.len()); | ||||
|                 debug!("{}",&sqltxs); | ||||
|             } | ||||
|         } | ||||
|         debug!("SQL: {}",sqltxs); | ||||
|         if sqltxs.len()== 0{ | ||||
|             if already_present == true{ | ||||
|                 //dbg!(already_present);
 | ||||
|                 return Ok(Response::new(full("already present"))) | ||||
|             } | ||||
|         } | ||||
|         let _ = db.execute("BEGIN TRANSACTION"); | ||||
|         let sql = format!("{}{}",sqltxshead,sqltxs); | ||||
|         if let Err(err) = db.execute(&sql){ | ||||
|             error!("error executing sql:{} - {}",&sql,err); | ||||
|             let _ = db.execute("ROLLBACK"); | ||||
|             //dbg!(&already_present);
 | ||||
|             if already_present == true{ | ||||
|                 trace!("already_present = True"); | ||||
|                 return Ok(Response::new(full("already present"))) | ||||
|             } | ||||
|         
 | ||||
|             return Ok(response) | ||||
|         } | ||||
|         //if !error {
 | ||||
|         //    if let Err(_) = db.execute(sqlinps){
 | ||||
|         //        let _ = db.execute("ROLLBACK");
 | ||||
|         //        error = true
 | ||||
|         //    }
 | ||||
|         //}
 | ||||
|         //if !error {
 | ||||
|         //    if let Err(_) = db.execute(sqlouts){
 | ||||
|         //        let _ = db.execute("ROLLBACK");
 | ||||
|         //        error = true;
 | ||||
|         //    }
 | ||||
|         //}
 | ||||
|         let _ = db.execute("COMMIT"); | ||||
|         Ok(Response::new(full("thx"))) | ||||
| } 
 | ||||
| fn create_database(db: Connection){ | ||||
|     info!("database sanity check"); | ||||
|     let _ = db.execute("CREATE TABLE IF NOT EXISTS tbl_tx      (txid PRIMARY KEY, wtxid, ntxid, tx, locktime integer, network, network_fees, reqid, our_fees, our_address, status integer DEFAULT 0);"); | ||||
|     let _ = db.execute("CREATE TABLE IF NOT EXISTS tbl_input   (txid, in_txid,in_vout, spend_txidi);"); | ||||
|     let _ = db.execute("CREATE TABLE IF NOT EXISTS tbl_output  (txid, script_pubkey, address, amount);"); | ||||
| 
 | ||||
|     let _ = db.execute("CREATE UNIQUE INDEX idx_tbl_input   ON(txid, txid,in_txid,in_vout)"); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| fn match_uri<'a>(path: &str, uri: &'a str) -> Option<&'a str> { | ||||
|     let re = Regex::new(path).unwrap(); | ||||
|     if let Some(captures) = re.captures(uri) { | ||||
|         if let Some(param) = captures.name("param") { | ||||
|             return Some(param.as_str()); | ||||
|         } | ||||
|     } | ||||
|     None | ||||
| } | ||||
| 
 | ||||
| /// This is our service handler. It receives a Request, routes on its
 | ||||
| /// path, and returns a Future of a Response.
 | ||||
| 
 | ||||
| async fn echo( | ||||
|     req: Request<hyper::body::Incoming>, | ||||
|     cfg: &MyConfig, | ||||
| ) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> { | ||||
| 
 | ||||
|     let mut not_found = Response::new(empty()); | ||||
|     *not_found.status_mut() = StatusCode::NOT_FOUND; | ||||
|     let mut ret: Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> = Ok(not_found); | ||||
| 
 | ||||
|     let uri = req.uri().path().to_string(); | ||||
|     //dbg!(&req);
 | ||||
|     //dbg!(&uri);
 | ||||
|     match req.method() { | ||||
|         // Serve some instructions at /
 | ||||
|         &Method::POST => { | ||||
|             let whole_body = req.collect().await?.to_bytes(); | ||||
|             if let Some(param) = match_uri(r"^?/?(?P<param>[^/]?+)?/pushtxs$",uri.as_str()) { | ||||
|                 //let whole_body = collect_body(req,512_000).await?;
 | ||||
|                 ret = echo_push(&whole_body,cfg,param).await; | ||||
|             } | ||||
|             if uri=="/searchtx"{ | ||||
|                 //let whole_body = collect_body(req,64).await?;
 | ||||
|                 ret = echo_search(&whole_body,cfg).await; | ||||
|             } | ||||
|             ret | ||||
|         } | ||||
|         &Method::GET => { | ||||
|             if let Some(param) = match_uri(r"^?/?(?P<param>[^/]?+)?/info$",uri.as_str()) { | ||||
|                 ret = echo_info(param,cfg).await; | ||||
|             } | ||||
|             ret | ||||
|         } | ||||
| 
 | ||||
|         // Return the 404 Not Found for other routes.
 | ||||
|         _ => ret | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn empty() -> BoxBody<Bytes, hyper::Error> { | ||||
|     Empty::<Bytes>::new() | ||||
|         .map_err(|never| match never {}) | ||||
|         .boxed() | ||||
| } | ||||
| 
 | ||||
| fn full<T: Into<Bytes>>(chunk: T) -> BoxBody<Bytes, hyper::Error> { | ||||
|     Full::new(chunk.into()) | ||||
|         .map_err(|never| match never {}) | ||||
|         .boxed() | ||||
| } | ||||
| fn parse_env(cfg: &Arc<Mutex<MyConfig>>){ | ||||
|     let mut cfg_lock = cfg.lock().unwrap(); | ||||
|     match env::var("BAL_SERVER_DB_FILE") { | ||||
|         Ok(value) => { | ||||
|             cfg_lock.db_file = value;}, | ||||
|         Err(_) => {}, | ||||
|     } | ||||
|     match env::var("BAL_SERVER_BIND_ADDRESS") { | ||||
|         Ok(value) => { | ||||
|             cfg_lock.bind_address = value;}, | ||||
|         Err(_) => {}, | ||||
|     } | ||||
|     match env::var("BAL_SERVER_BIND_PORT") { | ||||
|         Ok(value) => { | ||||
|             cfg_lock.bind_address = value;}, | ||||
|         Err(_) => {}, | ||||
|     } | ||||
|     cfg_lock = parse_env_netconfig(cfg_lock,"regtest");    
 | ||||
|     cfg_lock = parse_env_netconfig(cfg_lock,"signet");    
 | ||||
|     cfg_lock = parse_env_netconfig(cfg_lock,"testnet3");    
 | ||||
|     cfg_lock = parse_env_netconfig(cfg_lock,"testnet");    
 | ||||
|     drop(parse_env_netconfig(cfg_lock,"bitcoin")); | ||||
| 
 | ||||
| } | ||||
| fn parse_env_netconfig<'a>(mut cfg_lock: MutexGuard<'a, MyConfig>, chain: &'a str) -> MutexGuard<'a, MyConfig>{ | ||||
|     let cfg = match chain{ | ||||
|         "regtest" => &mut cfg_lock.regtest, | ||||
|         "signet" => &mut cfg_lock.signet, | ||||
|         "testnet3" => &mut cfg_lock.testnet3, | ||||
|         "testnet" => &mut cfg_lock.testnet4, | ||||
|         &_ => &mut cfg_lock.mainnet, | ||||
|     }; | ||||
|     match env::var(format!("BAL_SERVER_{}_ADDRESS",chain.to_uppercase())) { | ||||
|         Ok(value) => { cfg.address = value; }, | ||||
|         Err(_) => {}, | ||||
|     } | ||||
|     match env::var(format!("BAL_SERVER_{}_FIXE_FEE",chain.to_uppercase())) { | ||||
|         Ok(value) => { | ||||
|             match value.parse::<u64>(){ | ||||
|                 Ok(value) =>{ cfg.fixed_fee = value; }, | ||||
|                 Err(_) => {}, | ||||
|             } | ||||
|         } | ||||
|         Err(_) => {}, | ||||
|     } | ||||
|     cfg_lock | ||||
| } | ||||
| fn get_default_config()-> MyConfig { | ||||
| 
 | ||||
|     let file = confy::get_configuration_file_path("bal-server",None).expect("Error while getting path"); | ||||
|     info!("Default configuration file path is: {:#?}", file); | ||||
|     confy::load("bal-server",None).expect("cant_load") | ||||
| } | ||||
| 
 | ||||
| #[tokio::main] | ||||
| async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> { | ||||
|     env_logger::init(); | ||||
|     let cfg: Arc<Mutex<MyConfig>> = match env::var("BAL_SERVER_CONFIG_FILE") { | ||||
|         Ok(value) => { | ||||
|             Arc::new(Mutex::new( | ||||
|                 match confy::load_path(value.to_string()){ | ||||
|                     Ok(val) => { | ||||
|                         info!("The configuration file path is: {:#?}", value); | ||||
|                         val | ||||
|                     }, | ||||
|                     Err(err) => { | ||||
|                         error!("{}",err); | ||||
|                         get_default_config() | ||||
|                     } | ||||
|                 } | ||||
|             )) | ||||
|         }, | ||||
|         Err(_) => { | ||||
|             Arc::new(Mutex::new(get_default_config())) | ||||
|         }, | ||||
|     }; | ||||
| 
 | ||||
|     parse_env(&cfg); | ||||
|     let cfg_lock = cfg.lock().unwrap(); | ||||
| 
 | ||||
|     let db = sqlite::open(&cfg_lock.db_file).unwrap(); | ||||
|     create_database(db); | ||||
| 
 | ||||
|     let addr = cfg_lock.bind_address.to_string(); | ||||
|     info!("bind address:{}",addr); | ||||
|     let addr: IpAddr = addr.parse()?; | ||||
| 
 | ||||
|     let listener = TcpListener::bind((addr,cfg_lock.bind_port)).await?; | ||||
|     info!("Listening on http://{}:{}", addr,cfg_lock.bind_port); | ||||
|     
 | ||||
| 
 | ||||
|     loop { | ||||
|         let (stream, _) = listener.accept().await?; | ||||
|         let io = TokioIo::new(stream); | ||||
|         
 | ||||
|         tokio::task::spawn({ | ||||
|             let cfg = cfg_lock.clone(); | ||||
|             async move { | ||||
|                 if let Err(err) = http1::Builder::new() | ||||
|                     .serve_connection(io, service_fn(|req: Request<hyper::body::Incoming>| async { | ||||
|                         echo(req,&cfg).await | ||||
|                     })) | ||||
|                     .await | ||||
|                 { | ||||
|                     error!("Error serving connection: {:?}", err); | ||||
|                 } | ||||
| 
 | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 bitcoinafterlife
						bitcoinafterlife