1pub mod blocks_api;
17pub mod chain_api;
18pub mod peers_api;
19pub mod pool_api;
20pub mod server_api;
21pub mod transactions_api;
22pub mod utils;
23pub mod version_api;
24
25use self::blocks_api::BlockHandler;
26use self::blocks_api::HeaderHandler;
27use self::chain_api::ChainCompactHandler;
28use self::chain_api::ChainHandler;
29use self::chain_api::ChainValidationHandler;
30use self::chain_api::KernelHandler;
31use self::chain_api::OutputHandler;
32use self::peers_api::PeerHandler;
33use self::peers_api::PeersAllHandler;
34use self::peers_api::PeersConnectedHandler;
35use self::pool_api::PoolInfoHandler;
36use self::pool_api::PoolPushHandler;
37use self::server_api::IndexHandler;
38use self::server_api::StatusHandler;
39use self::transactions_api::TxHashSetHandler;
40use self::version_api::VersionHandler;
41use crate::auth::{
42 BasicAuthMiddleware, BasicAuthURIMiddleware, MWC_BASIC_REALM, MWC_FOREIGN_BASIC_REALM,
43};
44use crate::chain;
45use crate::chain::{Chain, SyncState};
46use crate::core::global;
47use crate::core::stratum;
48use crate::foreign::Foreign;
49use crate::foreign_rpc::ForeignRpc;
50use crate::owner::Owner;
51use crate::owner_rpc::OwnerRpc;
52use crate::p2p;
53use crate::pool;
54use crate::pool::{BlockChain, PoolAdapter};
55use crate::rest::{ApiServer, Error, TLSConfig};
56use crate::router::ResponseFuture;
57use crate::router::{Router, RouterError};
58use crate::stratum::Stratum;
59use crate::stratum_rpc::StratumRpc;
60use crate::util::to_base64;
61use crate::util::RwLock;
62use crate::util::StopState;
63use crate::web::*;
64use easy_jsonrpc_mw::{Handler, MaybeReply};
65use futures::channel::oneshot;
66use hyper::{Body, Request, Response, StatusCode};
67use serde::Serialize;
68use std::net::SocketAddr;
69use std::sync::{Arc, Weak};
70use std::thread;
71
72pub fn node_apis<B, P>(
75 addr: &str,
76 chain: Arc<chain::Chain>,
77 tx_pool: Arc<RwLock<pool::TransactionPool<B, P>>>,
78 peers: Arc<p2p::Peers>,
79 sync_state: Arc<chain::SyncState>,
80 api_secret: Option<String>,
81 foreign_api_secret: Option<String>,
82 tls_config: Option<TLSConfig>,
83 allow_to_stop: bool,
84 stratum_ip_pool: Arc<stratum::connections::StratumIpPool>,
85 api_chan: &'static mut (oneshot::Sender<()>, oneshot::Receiver<()>),
86 stop_state: Arc<StopState>,
87) -> Result<(), Error>
88where
89 B: BlockChain + 'static,
90 P: PoolAdapter + 'static,
91{
92 let mut router = build_router(
94 chain.clone(),
95 tx_pool.clone(),
96 peers.clone(),
97 sync_state.clone(),
98 allow_to_stop,
99 )
100 .expect("unable to build API router");
101
102 let basic_auth_key = if global::is_mainnet() {
103 "mwcmain"
104 } else if global::is_floonet() {
105 "mwcfloo"
106 } else {
107 "mwc"
108 };
109
110 if let Some(api_secret) = api_secret {
112 let api_basic_auth = format!(
113 "Basic {}",
114 to_base64(&format!("{}:{}", basic_auth_key, api_secret))
115 );
116 let basic_auth_middleware = Arc::new(BasicAuthMiddleware::new(
117 api_basic_auth,
118 &MWC_BASIC_REALM,
119 Some("/v2/foreign".into()),
120 ));
121 router.add_middleware(basic_auth_middleware);
122 }
123
124 let api_handler = OwnerAPIHandlerV2::new(
125 Arc::downgrade(&chain),
126 Arc::downgrade(&peers),
127 Arc::downgrade(&sync_state),
128 );
129 router.add_route("/v2/owner", Arc::new(api_handler))?;
130
131 let stratum_handler_v2 = StratumAPIHandlerV2::new(stratum_ip_pool);
132 router.add_route("/v2/stratum", Arc::new(stratum_handler_v2))?;
133
134 if let Some(api_secret) = foreign_api_secret {
136 let api_basic_auth = format!(
137 "Basic {}",
138 to_base64(&format!("{}:{}", basic_auth_key, api_secret))
139 );
140 let basic_auth_middleware = Arc::new(BasicAuthURIMiddleware::new(
141 api_basic_auth,
142 &MWC_FOREIGN_BASIC_REALM,
143 "/v2/foreign".into(),
144 ));
145 router.add_middleware(basic_auth_middleware);
146 }
147
148 let api_handler = ForeignAPIHandlerV2::new(
149 Arc::downgrade(&peers),
150 Arc::downgrade(&chain),
151 Arc::downgrade(&tx_pool),
152 Arc::downgrade(&sync_state),
153 );
154 router.add_route("/v2/foreign", Arc::new(api_handler))?;
155
156 let mut apis = ApiServer::new();
157 warn!("Starting HTTP Node APIs server at {}.", addr);
158 let socket_addr: SocketAddr = addr.parse().expect("unable to parse socket address");
159 let api_thread = apis.start(socket_addr, router, tls_config, api_chan);
160
161 warn!("HTTP Node listener started.");
162
163 thread::Builder::new()
164 .name("api_monitor".to_string())
165 .spawn(move || {
166 loop {
168 std::thread::sleep(std::time::Duration::from_millis(100));
169 if stop_state.is_stopped() {
170 apis.stop();
171 break;
172 }
173 }
174 })
175 .ok();
176
177 match api_thread {
178 Ok(_) => Ok(()),
179 Err(e) => {
180 error!("HTTP API server failed to start. Err: {}", e);
181 Err(Error::Internal(format!(
182 "HTTP API server failed to start, {}",
183 e
184 )))
185 }
186 }
187}
188
189pub struct OwnerAPIHandlerV2 {
191 pub chain: Weak<Chain>,
192 pub peers: Weak<p2p::Peers>,
193 pub sync_state: Weak<SyncState>,
194}
195
196impl OwnerAPIHandlerV2 {
197 pub fn new(chain: Weak<Chain>, peers: Weak<p2p::Peers>, sync_state: Weak<SyncState>) -> Self {
199 OwnerAPIHandlerV2 {
200 chain,
201 peers,
202 sync_state,
203 }
204 }
205}
206
207impl crate::router::Handler for OwnerAPIHandlerV2 {
208 fn post(&self, req: Request<Body>) -> ResponseFuture {
209 let api = Owner::new(
210 self.chain.clone(),
211 self.peers.clone(),
212 self.sync_state.clone(),
213 );
214
215 Box::pin(async move {
216 match parse_body(req).await {
217 Ok(val) => {
218 let owner_api = &api as &dyn OwnerRpc;
219 let res = match owner_api.handle_request(val) {
220 MaybeReply::Reply(r) => r,
221 MaybeReply::DontReply => {
222 serde_json::json!([])
225 }
226 };
227 Ok(json_response_pretty(&res))
228 }
229 Err(e) => {
230 error!("Request Error: {:?}", e);
231 Ok(create_error_response(e))
232 }
233 }
234 })
235 }
236
237 fn options(&self, _req: Request<Body>) -> ResponseFuture {
238 Box::pin(async { Ok(create_ok_response("{}")) })
239 }
240}
241
242pub struct ForeignAPIHandlerV2<B, P>
244where
245 B: BlockChain,
246 P: PoolAdapter,
247{
248 pub peers: Weak<mwc_p2p::Peers>,
249 pub chain: Weak<Chain>,
250 pub tx_pool: Weak<RwLock<pool::TransactionPool<B, P>>>,
251 pub sync_state: Weak<SyncState>,
252}
253
254impl<B, P> ForeignAPIHandlerV2<B, P>
255where
256 B: BlockChain,
257 P: PoolAdapter,
258{
259 pub fn new(
261 peers: Weak<mwc_p2p::Peers>,
262 chain: Weak<Chain>,
263 tx_pool: Weak<RwLock<pool::TransactionPool<B, P>>>,
264 sync_state: Weak<SyncState>,
265 ) -> Self {
266 ForeignAPIHandlerV2 {
267 peers,
268 chain,
269 tx_pool,
270 sync_state,
271 }
272 }
273}
274
275impl<B, P> crate::router::Handler for ForeignAPIHandlerV2<B, P>
276where
277 B: BlockChain + 'static,
278 P: PoolAdapter + 'static,
279{
280 fn post(&self, req: Request<Body>) -> ResponseFuture {
281 let api = Foreign::new(
282 self.peers.clone(),
283 self.chain.clone(),
284 self.tx_pool.clone(),
285 self.sync_state.clone(),
286 );
287
288 Box::pin(async move {
289 match parse_body(req).await {
290 Ok(val) => {
291 let foreign_api = &api as &dyn ForeignRpc;
292 let res = match foreign_api.handle_request(val) {
293 MaybeReply::Reply(r) => r,
294 MaybeReply::DontReply => {
295 serde_json::json!([])
298 }
299 };
300 Ok(json_response_pretty(&res))
301 }
302 Err(e) => {
303 error!("Request Error: {:?}", e);
304 Ok(create_error_response(e))
305 }
306 }
307 })
308 }
309
310 fn options(&self, _req: Request<Body>) -> ResponseFuture {
311 Box::pin(async { Ok(create_ok_response("{}")) })
312 }
313}
314
315pub struct StratumAPIHandlerV2 {
317 stratum_ip_pool: Arc<stratum::connections::StratumIpPool>,
318}
319
320impl StratumAPIHandlerV2 {
321 pub fn new(stratum_ip_pool: Arc<stratum::connections::StratumIpPool>) -> Self {
323 StratumAPIHandlerV2 { stratum_ip_pool }
324 }
325}
326
327impl crate::router::Handler for StratumAPIHandlerV2 {
328 fn post(&self, req: Request<Body>) -> ResponseFuture {
329 let api = Stratum::new(self.stratum_ip_pool.clone());
330
331 Box::pin(async move {
332 match parse_body(req).await {
333 Ok(val) => {
334 let stratum_api = &api as &dyn StratumRpc;
335 let res = match stratum_api.handle_request(val) {
336 MaybeReply::Reply(r) => r,
337 MaybeReply::DontReply => {
338 serde_json::json!([])
341 }
342 };
343 Ok(json_response_pretty(&res))
344 }
345 Err(e) => {
346 error!("Request Error: {:?}", e);
347 Ok(create_error_response(e))
348 }
349 }
350 })
351 }
352
353 fn options(&self, _req: Request<Body>) -> ResponseFuture {
354 Box::pin(async { Ok(create_ok_response("{}")) })
355 }
356}
357
358fn json_response_pretty<T>(s: &T) -> Response<Body>
360where
361 T: Serialize,
362{
363 match serde_json::to_string_pretty(s) {
364 Ok(json) => response(StatusCode::OK, json),
365 Err(e) => response(
366 StatusCode::INTERNAL_SERVER_ERROR,
367 format!("{{\"error\": \"{}\"}}", e),
368 ),
369 }
370}
371
372fn create_error_response(e: Error) -> Response<Body> {
373 Response::builder()
374 .status(StatusCode::INTERNAL_SERVER_ERROR)
375 .header("access-control-allow-origin", "*")
376 .header(
377 "access-control-allow-headers",
378 "Content-Type, Authorization",
379 )
380 .body(format!("{}", e).into())
381 .unwrap()
382}
383
384fn create_ok_response(json: &str) -> Response<Body> {
385 Response::builder()
386 .status(StatusCode::OK)
387 .header("access-control-allow-origin", "*")
388 .header(
389 "access-control-allow-headers",
390 "Content-Type, Authorization",
391 )
392 .header(hyper::header::CONTENT_TYPE, "application/json")
393 .body(json.to_string().into())
394 .unwrap()
395}
396
397fn response<T: Into<Body>>(status: StatusCode, text: T) -> Response<Body> {
402 let mut builder = Response::builder();
403
404 builder = builder
405 .status(status)
406 .header("access-control-allow-origin", "*")
407 .header(
408 "access-control-allow-headers",
409 "Content-Type, Authorization",
410 );
411
412 if status == StatusCode::OK {
413 builder = builder.header(hyper::header::CONTENT_TYPE, "application/json");
414 }
415
416 builder.body(text.into()).unwrap()
417}
418
419pub fn build_router<B, P>(
425 chain: Arc<chain::Chain>,
426 tx_pool: Arc<RwLock<pool::TransactionPool<B, P>>>,
427 peers: Arc<p2p::Peers>,
428 sync_state: Arc<chain::SyncState>,
429 allow_to_stop: bool,
430) -> Result<Router, RouterError>
431where
432 B: BlockChain + 'static,
433 P: PoolAdapter + 'static,
434{
435 let route_list = vec![
436 "get blocks".to_string(),
437 "get headers".to_string(),
438 "get chain".to_string(),
439 "post chain/compact".to_string(),
440 "get chain/validate".to_string(),
441 "get chain/kernels/xxx?min_height=yyy&max_height=zzz".to_string(),
442 "get chain/outputs/byids?id=xxx,yyy,zzz".to_string(),
443 "get chain/outputs/byheight?start_height=101&end_height=200".to_string(),
444 "get status".to_string(),
445 "get txhashset/roots".to_string(),
446 "get txhashset/lastoutputs?n=10".to_string(),
447 "get txhashset/lastrangeproofs".to_string(),
448 "get txhashset/lastkernels".to_string(),
449 "get txhashset/outputs?start_index=1&max=100".to_string(),
450 "get txhashset/merkleproof?n=1".to_string(),
451 "get pool".to_string(),
452 "post pool/push_tx".to_string(),
453 "post peers/a.b.c.d:p/ban".to_string(),
454 "post peers/a.b.c.d:p/unban".to_string(),
455 "get peers/all".to_string(),
456 "get peers/connected".to_string(),
457 "get peers/a.b.c.d".to_string(),
458 "get version".to_string(),
459 ];
460 let index_handler = IndexHandler { list: route_list };
461
462 let output_handler = OutputHandler {
463 chain: Arc::downgrade(&chain),
464 };
465 let kernel_handler = KernelHandler {
466 chain: Arc::downgrade(&chain),
467 };
468 let block_handler = BlockHandler {
469 chain: Arc::downgrade(&chain),
470 };
471 let header_handler = HeaderHandler {
472 chain: Arc::downgrade(&chain),
473 };
474 let chain_tip_handler = ChainHandler {
475 chain: Arc::downgrade(&chain),
476 };
477 let chain_compact_handler = ChainCompactHandler {
478 chain: Arc::downgrade(&chain),
479 };
480 let chain_validation_handler = ChainValidationHandler {
481 chain: Arc::downgrade(&chain),
482 };
483 let status_handler = StatusHandler {
484 chain: Arc::downgrade(&chain),
485 peers: Arc::downgrade(&peers),
486 sync_state: Arc::downgrade(&sync_state),
487 allow_to_stop,
488 };
489 let txhashset_handler = TxHashSetHandler {
490 chain: Arc::downgrade(&chain),
491 };
492 let pool_info_handler = PoolInfoHandler {
493 tx_pool: Arc::downgrade(&tx_pool),
494 };
495 let pool_push_handler = PoolPushHandler {
496 tx_pool: Arc::downgrade(&tx_pool),
497 };
498 let peers_all_handler = PeersAllHandler {
499 peers: Arc::downgrade(&peers),
500 };
501 let peers_connected_handler = PeersConnectedHandler {
502 peers: Arc::downgrade(&peers),
503 };
504 let peer_handler = PeerHandler {
505 peers: Arc::downgrade(&peers),
506 };
507 let version_handler = VersionHandler {
508 chain: Arc::downgrade(&chain),
509 };
510
511 let mut router = Router::new();
512
513 router.add_route("/v1/", Arc::new(index_handler))?;
514 router.add_route("/v1/blocks/*", Arc::new(block_handler))?;
515 router.add_route("/v1/headers/*", Arc::new(header_handler))?;
516 router.add_route("/v1/chain", Arc::new(chain_tip_handler))?;
517 router.add_route("/v1/chain/outputs/*", Arc::new(output_handler))?;
518 router.add_route("/v1/chain/kernels/*", Arc::new(kernel_handler))?;
519 router.add_route("/v1/chain/compact", Arc::new(chain_compact_handler))?;
520 router.add_route("/v1/chain/validate", Arc::new(chain_validation_handler))?;
521 router.add_route("/v1/txhashset/*", Arc::new(txhashset_handler))?;
522 router.add_route("/v1/status", Arc::new(status_handler))?;
523 router.add_route("/v1/pool", Arc::new(pool_info_handler))?;
524 router.add_route("/v1/pool/push_tx", Arc::new(pool_push_handler))?;
525 router.add_route("/v1/peers/all", Arc::new(peers_all_handler))?;
526 router.add_route("/v1/peers/connected", Arc::new(peers_connected_handler))?;
527 router.add_route("/v1/peers/**", Arc::new(peer_handler))?;
528 router.add_route("/v1/version", Arc::new(version_handler))?;
529 Ok(router)
530}