From 713eb8fbaa5e9728d5aa03e165a64b2bc29cf5e6 Mon Sep 17 00:00:00 2001 From: bitcoinafterlife Date: Thu, 22 May 2025 19:41:05 -0400 Subject: [PATCH] xpub related update --- bal-server.env | 9 ++- src/bin/bal-pusher.rs | 6 +- src/bin/bal-server.rs | 165 ++++++++++++++++++++++-------------------- src/db.rs | 14 ++-- src/xpub.rs | 7 +- 5 files changed, 103 insertions(+), 98 deletions(-) diff --git a/bal-server.env b/bal-server.env index c61bc70..371f632 100644 --- a/bal-server.env +++ b/bal-server.env @@ -1,12 +1,13 @@ -RUST_LOG=info +RUST_LOG=trace BAL_SERVER_DB_FILE=/home/bal/bal.db +BAL_SERVER_INFO="BAL server test willexecutor" BAL_SERVER_BIND_ADDRESS=127.0.0.1 -BAL_SERVER_BIND_PORT=9137 +BAL_SERVER_BIND_PORT=9133 BAL_SERVER_BITCOIN_ADDRESS="your bitcoin to recive payments here" BAL_SERVER_BITCOIN_FIXED_FEE=50000 -#BAL_SERVER_REGTEST_ADDRESS= -#BAL_SERVER_REGTEST_FEE=100000 +BAL_SERVER_REGTEST_ADDRESS="vpub5UhLrYG1qQjnJhvJgBdqgpznyH11mxW9hwBYxf3KhfdjiupCFPUVDvgwpeZ9Wj5YUJXjKjXjy7DSbJNBW1sXbKwARiaphm1UjHYy3mKvTG4" +BAL_SERVER_REGTEST_FEE=5000 #BAL_SERVER_TESTNET_ADDRESS= #BAL_SERVER_TESTNET_FEE=100000 #BAL_SERVER_SIGNET_ADDRESS= diff --git a/src/bin/bal-pusher.rs b/src/bin/bal-pusher.rs index 1ea4771..49520c0 100644 --- a/src/bin/bal-pusher.rs +++ b/src/bin/bal-pusher.rs @@ -349,7 +349,7 @@ fn main(){ env_logger::init(); let mut cfg: MyConfig = match env::var("BAL_PUSHER_CONFIG_FILE") { Ok(value) => { - match confy::load_path(value.to_string()){ + match confy::load_path(&value){ Ok(val) => { info!("The configuration file path is: {:#?}", value); val @@ -393,7 +393,7 @@ fn main(){ socket.set_subscribe(b"").unwrap(); - let _ = main_result(&cfg,&network_params); + let _ = main_result(&cfg,network_params); info!("waiting new blocks.."); let mut last_seq:Vec=[0;4].to_vec(); loop { @@ -414,7 +414,7 @@ fn main(){ if topic == b"hashblock" { info!("NEW BLOCK{}", hex::encode(body)); //let cfg = cfg.clone(); - let _ = main_result(&cfg,&network_params); + let _ = main_result(&cfg,network_params); } thread::sleep(Duration::from_millis(100)); // Sleep for 100ms } diff --git a/src/bin/bal-server.rs b/src/bin/bal-server.rs index 06d1701..5e21993 100644 --- a/src/bin/bal-server.rs +++ b/src/bin/bal-server.rs @@ -51,8 +51,8 @@ impl NetConfig { address: "".to_string(), fixed_fee: 50000, xpub: false, - name:name, - network:network, + name, + network, enabled: false, } } @@ -64,11 +64,22 @@ struct MyConfig { signet: NetConfig, testnet: NetConfig, mainnet: NetConfig, + info: String, bind_address: String, bind_port: u16, // Changed to u16 for port numbers db_file: String, } +#[derive(Debug,Serialize, Deserialize)] +pub struct Info { + pub address: String, + pub base_fee: u64, + pub chain: String, + pub info: String, + pub version: String +} + + impl Default for MyConfig { fn default() -> Self { MyConfig { @@ -79,6 +90,7 @@ impl Default for MyConfig { bind_address: "127.0.0.1".to_string(), bind_port: 9137, db_file: "bal.db".to_string(), + info:"Will Executor Server".to_string() } } } @@ -129,31 +141,44 @@ async fn echo_info( } } }; - trace!("address: {}:{}",address,netconfig.fixed_fee); - return Ok(Response::new(full("{\"address\":\"".to_owned()+&address+"\",\"base_fee\":\""+&netconfig.fixed_fee.to_string()+"\"}"))); + let info = Info{ + address, + base_fee: netconfig.fixed_fee, + chain: netconfig.network.to_string(), + info: cfg.info.to_string(), + version: VERSION.to_string() + + }; + trace!("address: {:#?}",info); + match serde_json::to_string(&info){ + Ok(json_data) => Ok(Response::new(full(json_data))), + Err(err) => Ok(Response::new(full(format!("error:{}",err)))) + } + + } async fn echo_search(whole_body: &Bytes, cfg: &MyConfig, ) -> Result>, hyper::Error> { info!("echo search!!!"); - let strbody = std::str::from_utf8(&whole_body).unwrap(); + let strbody = std::str::from_utf8(whole_body).unwrap(); info!("{}",strbody); let mut response = Response::new(full("Bad data received".to_owned())); *response.status_mut() = StatusCode::BAD_REQUEST; - if strbody.len() >0 && strbody.len()<=70 { + if !strbody.is_empty() && strbody.len()<=70 { let db = sqlite::open(&cfg.db_file).unwrap(); - let mut statement = db.prepare("SELECT * FROM tbl_tx WHERE txid = ?").unwrap(); + let mut statement = db.prepare("SELECT * FROM tbl_tx WHERE txid = ? LIMIT 1").unwrap(); statement.bind((1, strbody)).unwrap(); - while let Ok(State::Row) = statement.next() { + if let Ok(State::Row) = statement.next() { let mut response_data = HashMap::new(); match statement.read::("status") { Ok(value) => response_data.insert("status", value), Err(e) => { error!("Error reading status: {}", e); - break; //response_data.insert("status", "Error".to_string()) + None } }; @@ -162,8 +187,8 @@ async fn echo_search(whole_body: &Bytes, Ok(value) => response_data.insert("tx", value), Err(e) => { error!("Error reading tx: {}", e); - break; //response_data.insert("tx", "Error".to_string()) + None } }; @@ -171,8 +196,8 @@ async fn echo_search(whole_body: &Bytes, Ok(value) => response_data.insert("our_address", value), Err(e) => { error!("Error reading address: {}", e); - break; //response_data.insert("tx", "Error".to_string()) + None } }; @@ -180,8 +205,8 @@ async fn echo_search(whole_body: &Bytes, Ok(value) => response_data.insert("our_fees", value), Err(e) => { error!("Error reading fees: {}", e); - break; //response_data.insert("tx", "Error".to_string()) + None } }; @@ -190,13 +215,13 @@ async fn echo_search(whole_body: &Bytes, Ok(value) => response_data.insert("time", value), Err(e) => { error!("Error reading reqid: {}", e); - break; //response_data.insert("time", "Error".to_string()) + None } }; response = match serde_json::to_string(&response_data){ Ok(json_data) => Response::new(full(json_data)), - Err(_) => {break;} + Err(_) => { response } }; return Ok(response); @@ -211,19 +236,18 @@ async fn echo_push(whole_body: &Bytes, param: &str, ) -> Result>, hyper::Error> { //let whole_body = req.collect().await?.to_bytes(); - let strbody = std::str::from_utf8(&whole_body).unwrap(); - info!("network:{}\n{}",¶m, &strbody); - + let strbody = std::str::from_utf8(whole_body).unwrap(); let mut response = Response::new(full("Bad data received".to_owned())); let mut response_not_enable = Response::new(full("Network not enabled".to_owned())); *response.status_mut() = StatusCode::BAD_REQUEST; *response_not_enable.status_mut()=StatusCode::BAD_REQUEST; - let netconfig = MyConfig::get_net_config(&cfg,param); + let netconfig = MyConfig::get_net_config(cfg,param); if !netconfig.enabled{ return Ok(response_not_enable); } let req_time = Utc::now().timestamp_nanos_opt().unwrap(); // Returns i64 + let db = sqlite::open(&cfg.db_file).unwrap(); let lines = strbody.split("\n"); let sqltxshead = "INSERT INTO tbl_tx (txid, wtxid, ntxid, tx, locktime, reqid, network, our_address, our_fees)".to_string(); @@ -236,8 +260,6 @@ async fn echo_push(whole_body: &Bytes, let mut union_inps = true; let mut union_outs = true; let mut already_present = false; - let db = sqlite::open(&cfg.db_file).unwrap(); - let netconfig = MyConfig::get_net_config(cfg,param); let mut ptx:Vec<(usize, Value)> = vec![]; let mut pinps:Vec<(usize, Value)> = vec![]; let mut pouts:Vec<(usize, Value)> = vec![]; @@ -245,7 +267,7 @@ async fn echo_push(whole_body: &Bytes, let mut lineinp = 1; let mut lineout = 1; for line in lines { - if line.len() == 0{ + if line.is_empty(){ trace!("line len is: {}",line.len()); continue } @@ -258,7 +280,7 @@ async fn echo_push(whole_body: &Bytes, continue } }; - if raw_tx.len() > 0 { + if !raw_tx.is_empty(){ trace!("len: {}",raw_tx.len()); let tx: Transaction = match consensus::deserialize(&raw_tx){ Ok(tx) => tx, @@ -280,13 +302,13 @@ async fn echo_push(whole_body: &Bytes, let mut our_address:String = "".to_string(); let mut our_fees:u64 = 0; for input in tx.input{ - if union_inps == false { + if !union_inps { sqlinps = format!("{sqlinps} UNION ALL"); }else{ union_inps = false; } sqlinps = format!("{sqlinps} SELECT ?, ?, ?"); - pinps.push((lineinp+0,Value::String(txid.to_string()))); + pinps.push((lineinp,Value::String(txid.to_string()))); pinps.push((lineinp+1,Value::String(input.previous_output.txid.to_string()))); pinps.push((lineinp+2,Value::String(input.previous_output.vout.to_string()))); lineinp += 3; @@ -295,8 +317,7 @@ async fn echo_push(whole_body: &Bytes, if netconfig.fixed_fee ==0 { found = true; } - let mut idx = 0; - for output in tx.output{ + for (idx,output) in tx.output.into_iter().enumerate(){ let script_pubkey = output.script_pubkey; let address = match bitcoin::Address::from_script(script_pubkey.as_script(), netconfig.network){ Ok(address) => address.to_string(), @@ -306,7 +327,7 @@ async fn echo_push(whole_body: &Bytes, our_fees = netconfig.fixed_fee;//search wllexecutor output if netconfig.xpub{ let sql="select * from tbl_address where address=?"; - let mut stmt = db.prepare(&sql).expect("failed to fetch addresses"); + let mut stmt = db.prepare(sql).expect("failed to fetch addresses"); stmt.bind((1,Value::String(address.to_string()))).unwrap(); if let Ok(State::Row) = stmt.next() { our_address = address.to_string(); @@ -320,30 +341,29 @@ async fn echo_push(whole_body: &Bytes, found = true; trace!("address and fees are correct {}: {}",our_address,our_fees); } - if union_outs == false { + if !union_outs { sqlouts = format!("{sqlouts} UNION ALL"); }else{ union_outs = false; } sqlouts = format!("{sqlouts} SELECT ?, ?, ?, ?"); - pouts.push((lineout+0,Value::String(txid.to_string()))); - pouts.push((lineout+1,Value::Integer(idx))); + pouts.push((lineout,Value::String(txid.to_string()))); + pouts.push((lineout+1,Value::Integer(idx.try_into().unwrap()))); pouts.push((lineout+2,Value::String(script_pubkey.to_string()))); pouts.push((lineout+3,Value::Integer(amount.to_sat().try_into().unwrap()))); - idx += 1; lineout += 4; } - if found == false{ + if !found { error!("willexecutor output not found "); return Ok(response) } else { - if union_tx == false { + if !union_tx { sqltxs = format!("{sqltxs} UNION ALL"); }else{ union_tx = false; } sqltxs = format!("{sqltxs} SELECT ?, ?, ?, ?, ?, ?, ?, ?, ?"); - ptx.push((linenum+0,Value::String(txid))); + ptx.push((linenum,Value::String(txid))); ptx.push((linenum+1,Value::String(wtxid.to_string()))); ptx.push((linenum+2,Value::String(ntxid.to_string()))); ptx.push((linenum+3,Value::String(line.to_string()))); @@ -359,10 +379,8 @@ async fn echo_push(whole_body: &Bytes, debug!("{}",&sqltxs); } } - if sqltxs.len()== 0{ - if already_present == true{ - return Ok(Response::new(full("already present"))) - } + if sqltxs.is_empty() && already_present { + return Ok(Response::new(full("already present"))) } let sqltxs = format!("{}{};", sqltxshead, sqltxs); let sqlinps = format!("{}{};", sqlinpshead, sqlinps); @@ -384,8 +402,6 @@ fn match_uri<'a>(path: &str, uri: &'a str) -> Option<&'a 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, @@ -401,9 +417,9 @@ async fn echo( let remote_addr = req.headers().get("X-Real-IP").and_then(|value| value.to_str().ok()).and_then(|xff| xff.split(',').next()).map(|ip| ip.trim().to_string()).unwrap_or_else(|| ip.to_string()); trace!("{}: {}",remote_addr,uri); - match req.method() { + match *req.method() { // Serve some instructions at / - &Method::POST => { + Method::POST => { let whole_body = req.collect().await?.to_bytes(); if let Some(param) = match_uri(r"^?/?(?P[^/]?+)?/pushtxs$",uri.as_str()) { //let whole_body = collect_body(req,512_000).await?; @@ -415,7 +431,7 @@ async fn echo( } ret } - &Method::GET => { + Method::GET => { if let Some(param) = match_uri(r"^?/?(?P[^/]?+)?/info$",uri.as_str()) { ret = echo_info(param,cfg,remote_addr).await; } @@ -443,25 +459,22 @@ fn full>(chunk: T) -> BoxBody { } fn parse_env(cfg: &Arc>){ let mut cfg_lock = cfg.lock().unwrap(); - match env::var("BAL_SERVER_DB_FILE") { - Ok(value) => { - cfg_lock.db_file = value;}, - Err(_) => {}, + if let Ok(value) = env::var("BAL_SERVER_DB_FILE") { + cfg_lock.db_file = value; } - match env::var("BAL_SERVER_BIND_ADDRESS") { - Ok(value) => { - cfg_lock.bind_address= value;}, - Err(_) => {}, + if let Ok(value) = env::var("BAL_SERVER_BIND_ADDRESS") { + cfg_lock.bind_address= value; } - match env::var("BAL_SERVER_BIND_PORT") { - Ok(value) => { - match value.parse::(){ - Ok(value) =>{ cfg_lock.bind_port = value; }, - Err(_) => {}, - } + if let Ok(value) = env::var("BAL_SERVER_BIND_PORT") { + if let Ok(v) = value.parse::(){ + cfg_lock.bind_port = v; } - Err(_) => {}, } + + if let Ok(value) = env::var("BAL_SERVER_INFO"){ + cfg_lock.info = value; + } + cfg_lock = parse_env_netconfig(cfg_lock,"regtest"); cfg_lock = parse_env_netconfig(cfg_lock,"signet"); cfg_lock = parse_env_netconfig(cfg_lock,"testnet"); @@ -475,29 +488,21 @@ fn parse_env_netconfig<'a>(mut cfg_lock: MutexGuard<'a, MyConfig>, chain: &'a st "testnet" => &mut cfg_lock.testnet, &_ => &mut cfg_lock.mainnet, }; - match env::var(format!("BAL_SERVER_{}_ADDRESS",chain.to_uppercase())) { - Ok(value) => { - cfg.address = value; - if cfg.address.len() > 5 { - if cfg.address[1..4] == *"pub" { - cfg.xpub=true; - trace!("is_xpub"); - } - cfg.enabled=true; - } - }, - - - Err(_) => {}, - } - match env::var(format!("BAL_SERVER_{}_FIXE_FEE",chain.to_uppercase())) { - Ok(value) => { - match value.parse::(){ - Ok(value) =>{ cfg.fixed_fee = value; }, - Err(_) => {}, + if let Ok(value) = env::var(format!("BAL_SERVER_{}_ADDRESS",chain.to_uppercase())) { + cfg.address = value; + if cfg.address.len() > 5 { + if cfg.address[1..4] == *"pub" { + cfg.xpub=true; + trace!("is_xpub"); } + cfg.enabled=true; + } + } + + if let Ok(value) = env::var(format!("BAL_SERVER_{}_FIXE_FEE",chain.to_uppercase())) { + if let Ok(v) = value.parse::(){ + cfg.fixed_fee = v; } - Err(_) => {}, } cfg_lock } @@ -505,7 +510,7 @@ fn parse_env_netconfig<'a>(mut cfg_lock: MutexGuard<'a, MyConfig>, chain: &'a st fn init_network(db: &Connection, cfg: &MyConfig){ for network in NETWORKS{ let netconfig = MyConfig::get_net_config(cfg,network); - insert_xpub(&db,&netconfig.name,&netconfig.address); + insert_xpub(db,&netconfig.name,&netconfig.address); } } #[tokio::main] diff --git a/src/db.rs b/src/db.rs index 17b5253..c35f784 100644 --- a/src/db.rs +++ b/src/db.rs @@ -42,7 +42,7 @@ pub fn insert_xpub(db: &Connection, network: &String, xpub: &String){ } pub fn get_last_used_address_by_ip(db: &Connection, network: &String, xpub: &String, address: &String) -> Option{ - let mut stmt = db.prepare("SELECT tbl_address.address FROM tbl_xpub join tbl_address on(tbl_xpub.id = tbl_address.xpub) where tbl_xpub.network = ? and tbl_address.remote_address = ? and tbl_xpub.xpub = ?ORDER BY tbl_address.date_create DESC LIMIT 1;").unwrap(); + let mut stmt = db.prepare("SELECT tbl_address.address FROM tbl_xpub join tbl_address on(tbl_xpub.id = tbl_address.xpub) where tbl_xpub.network = ? and tbl_address.remote_address = ? and tbl_xpub.xpub = ? ORDER BY tbl_address.date_create DESC LIMIT 1;").unwrap(); let _ = stmt.bind((1,Value::String(network.to_string()))); let _ = stmt.bind((2,Value::String(address.to_string()))); let _ = stmt.bind((3,Value::String(xpub.to_string()))); @@ -56,8 +56,8 @@ pub fn get_last_used_address_by_ip(db: &Connection, network: &String, xpub: &Str } pub fn get_next_address_index(db: &Connection, network: &String, xpub: &String) -> (i64,i64){ let mut stmt = db.prepare("UPDATE tbl_xpub SET path_idx = path_idx + 1 WHERE network = ? and xpub= ? RETURNING path_idx,id;").unwrap(); - let _ = stmt.bind((1,Value::String(network.to_string()))).unwrap(); - let _ = stmt.bind((2,Value::String(xpub.to_string()))).unwrap(); + stmt.bind((1,Value::String(network.to_string()))).unwrap(); + stmt.bind((2,Value::String(xpub.to_string()))).unwrap(); match stmt.next(){ Ok(State::Row) =>{ let next = stmt.read::("path_idx").unwrap(); @@ -73,10 +73,10 @@ pub fn get_next_address_index(db: &Connection, network: &String, xpub: &String) pub fn save_new_address(db: &Connection,xpub: i64,address: &String, path: &String,remote_addr: &String){ let mut stmt = db.prepare("INSERT INTO tbl_address(address,path,xpub,remote_address) VALUES(?,?,?,?);").unwrap(); - let _ = stmt.bind((1,Value::String(address.to_string()))).unwrap(); - let _ = stmt.bind((2,Value::String(path.to_string()))).unwrap(); - let _ = stmt.bind((3,Value::Integer(xpub))).unwrap(); - let _ = stmt.bind((4,Value::String(remote_addr.to_string()))).unwrap(); + stmt.bind((1,Value::String(address.to_string()))).unwrap(); + stmt.bind((2,Value::String(path.to_string()))).unwrap(); + stmt.bind((3,Value::Integer(xpub))).unwrap(); + stmt.bind((4,Value::String(remote_addr.to_string()))).unwrap(); let _ = stmt.next(); } diff --git a/src/xpub.rs b/src/xpub.rs index 8106428..b95a992 100644 --- a/src/xpub.rs +++ b/src/xpub.rs @@ -1,4 +1,3 @@ -use bs58; use sha2::{Digest, Sha256}; use bitcoin::bip32::Xpub; use std::str::FromStr; @@ -35,7 +34,7 @@ fn base58check_decode(s: &str) -> Result, String> { return Err("Data troppo corta".to_string()); } let (payload, checksum) = data.split_at(data.len() - 4); - let hash = Sha256::digest(&Sha256::digest(payload)); + let hash = Sha256::digest(Sha256::digest(payload)); if hash[0..4] != checksum[..] { return Err("Checksum invalido".to_string()); } @@ -43,7 +42,7 @@ fn base58check_decode(s: &str) -> Result, String> { } fn base58check_encode(data: &[u8]) -> String { - let checksum = &Sha256::digest(&Sha256::digest(data))[0..4]; + let checksum = &Sha256::digest(Sha256::digest(data))[0..4]; let full = [data, checksum].concat(); bs58::encode(full).into_string() } @@ -69,7 +68,7 @@ fn convert_to(zpub: &str,prefix: BS58Prefix) -> Result { pub fn new_address_from_xpub(zpub: &str, index: i64,network: Network)-> Result<(String,String), Box>{ let xpub = Xpub::from_str(&convert_to(zpub,BS58Prefix::Xpub)?)?; let path = format!("m/0/{}",index); - let derivation_path = DerivationPath::from_str(&path.as_str())?; + let derivation_path = DerivationPath::from_str(path.as_str())?; let secp = Secp256k1::new(); let derived_xpub = xpub.derive_pub(&secp, &derivation_path)?; let public_key = derived_xpub.public_key;