rpc.rs
1 use jsonrpc_core::{IoHandler, Params, Result as RpcResult, Value}; 2 use jsonrpc_http_server::{ServerBuilder, Server}; 3 use serde_json::json; 4 use std::net::SocketAddr; 5 use tracing::{info, error}; 6 7 use crate::config::{Config, RpcConfig}; 8 use crate::error::{RpcError, NodeResult}; 9 10 pub struct RpcServer { 11 _server: Server, 12 } 13 14 pub async fn start_server(config: &Config) -> NodeResult<RpcServer> { 15 let mut io = IoHandler::new(); 16 17 // Register RPC methods 18 register_blockchain_methods(&mut io); 19 register_network_methods(&mut io); 20 register_transaction_methods(&mut io); 21 register_utility_methods(&mut io); 22 23 let addr: SocketAddr = format!("{}:{}", config.rpc.host, config.rpc.port) 24 .parse() 25 .map_err(|e| RpcError::Internal(format!("Invalid RPC address: {}", e)))?; 26 27 let server = ServerBuilder::new(io) 28 .start_http(&addr) 29 .map_err(|e| RpcError::Internal(format!("Failed to start RPC server: {}", e)))?; 30 31 info!("RPC server started on {}", addr); 32 33 Ok(RpcServer { _server: server }) 34 } 35 36 fn register_blockchain_methods(io: &mut IoHandler) { 37 // getblockchaininfo 38 io.add_method("getblockchaininfo", |_params: Params| async { 39 Ok(json!({ 40 "chain": "regtest", 41 "blocks": 0, 42 "headers": 0, 43 "bestblockhash": "0000000000000000000000000000000000000000000000000000000000000000", 44 "difficulty": 1.0, 45 "mediantime": 0, 46 "verificationprogress": 1.0, 47 "initialblockdownload": false, 48 "chainwork": "0000000000000000000000000000000000000000000000000000000000000000", 49 "size_on_disk": 0, 50 "pruned": false 51 })) 52 }); 53 54 // getbestblockhash 55 io.add_method("getbestblockhash", |_params: Params| async { 56 Ok(json!("0000000000000000000000000000000000000000000000000000000000000000")) 57 }); 58 59 // getblock 60 io.add_method("getblock", |params: Params| async { 61 let params = params.parse::<(String, Option<u8>)>() 62 .map_err(|_| jsonrpc_core::Error::invalid_params("Invalid parameters"))?; 63 64 let _block_hash = params.0; 65 let _verbosity = params.1.unwrap_or(1); 66 67 // TODO: Get actual block data 68 Ok(json!({ 69 "hash": "0000000000000000000000000000000000000000000000000000000000000000", 70 "confirmations": 1, 71 "size": 285, 72 "strippedsize": 285, 73 "weight": 1140, 74 "height": 0, 75 "version": 1, 76 "versionHex": "00000001", 77 "merkleroot": "0000000000000000000000000000000000000000000000000000000000000000", 78 "tx": [], 79 "time": 0, 80 "mediantime": 0, 81 "nonce": 0, 82 "bits": "207fffff", 83 "difficulty": 1.0, 84 "chainwork": "0000000000000000000000000000000000000000000000000000000000000002", 85 "nTx": 0, 86 "previousblockhash": null, 87 "nextblockhash": null 88 })) 89 }); 90 91 // getblockcount 92 io.add_method("getblockcount", |_params: Params| async { 93 Ok(json!(0)) 94 }); 95 96 // getblockhash 97 io.add_method("getblockhash", |params: Params| async { 98 let params = params.parse::<(u64,)>() 99 .map_err(|_| jsonrpc_core::Error::invalid_params("Invalid parameters"))?; 100 101 let _height = params.0; 102 103 // TODO: Get actual block hash for height 104 Ok(json!("0000000000000000000000000000000000000000000000000000000000000000")) 105 }); 106 } 107 108 fn register_network_methods(io: &mut IoHandler) { 109 // getnetworkinfo 110 io.add_method("getnetworkinfo", |_params: Params| async { 111 Ok(json!({ 112 "version": 250000, 113 "subversion": "/BitKnotsRS:0.1.0/", 114 "protocolversion": 70016, 115 "localservices": "0000000000000409", 116 "localservicesnames": ["NETWORK", "WITNESS", "NETWORK_LIMITED"], 117 "localrelay": true, 118 "timeoffset": 0, 119 "connections": 0, 120 "connections_in": 0, 121 "connections_out": 0, 122 "networkactive": true, 123 "networks": [], 124 "relayfee": 0.00001000, 125 "incrementalfee": 0.00001000, 126 "localaddresses": [], 127 "warnings": "" 128 })) 129 }); 130 131 // getpeerinfo 132 io.add_method("getpeerinfo", |_params: Params| async { 133 // TODO: Get actual peer information 134 Ok(json!([])) 135 }); 136 137 // getconnectioncount 138 io.add_method("getconnectioncount", |_params: Params| async { 139 Ok(json!(0)) 140 }); 141 } 142 143 fn register_transaction_methods(io: &mut IoHandler) { 144 // getrawtransaction 145 io.add_method("getrawtransaction", |params: Params| async { 146 let params = params.parse::<(String, Option<bool>)>() 147 .map_err(|_| jsonrpc_core::Error::invalid_params("Invalid parameters"))?; 148 149 let _txid = params.0; 150 let verbose = params.1.unwrap_or(false); 151 152 if verbose { 153 // TODO: Get actual transaction data 154 Ok(json!({ 155 "txid": "0000000000000000000000000000000000000000000000000000000000000000", 156 "hash": "0000000000000000000000000000000000000000000000000000000000000000", 157 "version": 1, 158 "size": 0, 159 "vsize": 0, 160 "weight": 0, 161 "locktime": 0, 162 "vin": [], 163 "vout": [], 164 "hex": "", 165 "blockhash": null, 166 "confirmations": 0, 167 "time": 0, 168 "blocktime": 0 169 })) 170 } else { 171 // Return raw hex 172 Ok(json!("")) 173 } 174 }); 175 176 // sendrawtransaction 177 io.add_method("sendrawtransaction", |params: Params| async { 178 let params = params.parse::<(String,)>() 179 .map_err(|_| jsonrpc_core::Error::invalid_params("Invalid parameters"))?; 180 181 let _hex = params.0; 182 183 // TODO: Validate and broadcast transaction 184 Ok(json!("0000000000000000000000000000000000000000000000000000000000000000")) 185 }); 186 187 // getmempoolinfo 188 io.add_method("getmempoolinfo", |_params: Params| async { 189 Ok(json!({ 190 "loaded": true, 191 "size": 0, 192 "bytes": 0, 193 "usage": 0, 194 "maxmempool": 300000000, 195 "mempoolminfee": 0.00001000, 196 "minrelaytxfee": 0.00001000, 197 "unbroadcastcount": 0 198 })) 199 }); 200 201 // getrawmempool 202 io.add_method("getrawmempool", |params: Params| async { 203 let verbose = if let Ok((verbose,)) = params.parse::<(bool,)>() { 204 verbose 205 } else { 206 false 207 }; 208 209 if verbose { 210 // TODO: Get actual mempool data with details 211 Ok(json!({})) 212 } else { 213 // TODO: Get actual mempool transaction IDs 214 Ok(json!([])) 215 } 216 }); 217 } 218 219 fn register_utility_methods(io: &mut IoHandler) { 220 // help 221 io.add_method("help", |params: Params| async { 222 let command = if let Ok((cmd,)) = params.parse::<(String,)>() { 223 Some(cmd) 224 } else { 225 None 226 }; 227 228 match command.as_deref() { 229 Some("getblockchaininfo") => Ok(json!("getblockchaininfo\n\nReturns an object containing various state info regarding blockchain processing.")), 230 Some("getbestblockhash") => Ok(json!("getbestblockhash\n\nReturns the hash of the best (tip) block in the most-work fully-validated chain.")), 231 Some("getblock") => Ok(json!("getblock \"blockhash\" ( verbosity )\n\nIf verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'.")), 232 Some("getblockcount") => Ok(json!("getblockcount\n\nReturns the height of the most-work fully-validated chain.")), 233 Some("getblockhash") => Ok(json!("getblockhash height\n\nReturns hash of block in best-block-chain at height provided.")), 234 Some("getnetworkinfo") => Ok(json!("getnetworkinfo\n\nReturns an object containing various state info regarding P2P networking.")), 235 Some("getpeerinfo") => Ok(json!("getpeerinfo\n\nReturns data about each connected network node as a json array of objects.")), 236 Some("getconnectioncount") => Ok(json!("getconnectioncount\n\nReturns the number of connections to other nodes.")), 237 Some("getrawtransaction") => Ok(json!("getrawtransaction \"txid\" ( verbose \"blockhash\" )\n\nReturn the raw transaction data.")), 238 Some("sendrawtransaction") => Ok(json!("sendrawtransaction \"hexstring\" ( maxfeerate )\n\nSubmit a raw transaction (serialized, hex-encoded) to local node and network.")), 239 Some("getmempoolinfo") => Ok(json!("getmempoolinfo\n\nReturns details on the active state of the TX memory pool.")), 240 Some("getrawmempool") => Ok(json!("getrawmempool ( verbose )\n\nReturns all transaction ids in memory pool as a json array of string transaction ids.")), 241 None => Ok(json!( 242 "Available commands:\n\ 243 getblockchaininfo\n\ 244 getbestblockhash\n\ 245 getblock\n\ 246 getblockcount\n\ 247 getblockhash\n\ 248 getnetworkinfo\n\ 249 getpeerinfo\n\ 250 getconnectioncount\n\ 251 getrawtransaction\n\ 252 sendrawtransaction\n\ 253 getmempoolinfo\n\ 254 getrawmempool\n\ 255 help" 256 )), 257 Some(_) => Ok(json!("Unknown command. Use 'help' to list available commands.")), 258 } 259 }); 260 261 // uptime 262 io.add_method("uptime", |_params: Params| async { 263 // TODO: Calculate actual uptime 264 Ok(json!(0)) 265 }); 266 267 // getversion (non-standard but useful) 268 io.add_method("getversion", |_params: Params| async { 269 Ok(json!({ 270 "version": env!("CARGO_PKG_VERSION"), 271 "name": "BitKnotsRS", 272 "description": "A knots-inspired Bitcoin node implementation in Rust" 273 })) 274 }); 275 }