mirror of
https://github.com/hinto-janai/cuprate.git
synced 2024-11-16 15:58:14 +00:00
add route fn signatures
This commit is contained in:
parent
35907c5182
commit
aaf99b86b1
10 changed files with 433 additions and 535 deletions
21
Cargo.lock
generated
21
Cargo.lock
generated
|
@ -114,6 +114,7 @@ checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf"
|
|||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
"axum-macros",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
|
@ -161,6 +162,18 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-macros"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa"
|
||||
dependencies = [
|
||||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.73"
|
||||
|
@ -389,7 +402,7 @@ version = "4.5.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.66",
|
||||
|
@ -1257,6 +1270,12 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
|
|
|
@ -16,7 +16,7 @@ cuprate-epee-encoding = { path = "../../net/epee-encoding" }
|
|||
cuprate-json-rpc = { path = "../json-rpc" }
|
||||
cuprate-rpc-types = { path = "../types" }
|
||||
|
||||
axum = { version = "0.7.5" }
|
||||
axum = { version = "0.7.5", features = ["macros"] }
|
||||
serde = { workspace = true }
|
||||
tower = { workspace = true }
|
||||
|
||||
|
|
|
@ -3,67 +3,73 @@
|
|||
//---------------------------------------------------------------------------------------------------- Use
|
||||
use std::{future::Future, marker::PhantomData};
|
||||
|
||||
use axum::{routing::method_routing::get, Router};
|
||||
use axum::{extract::State, routing::method_routing::get, Router};
|
||||
use tower::Service;
|
||||
|
||||
use crate::{
|
||||
error::Error, request::Request, response::Response, route::json_rpc, rpc_handler::RpcHandler,
|
||||
error::Error,
|
||||
request::Request,
|
||||
response::Response,
|
||||
route::json_rpc,
|
||||
route::{bin, other},
|
||||
rpc_handler::RpcHandler,
|
||||
RpcState,
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Router
|
||||
/// TODO
|
||||
#[rustfmt::skip] // 1 line per route.
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
pub fn create_router<H: RpcHandler>() -> Router<H::RpcState> {
|
||||
pub fn create_router<H: RpcHandler>() -> Router<H> {
|
||||
// List of `monerod` routes:
|
||||
// <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server.h#L97-L189>
|
||||
Router::new()
|
||||
// JSON-RPC route.
|
||||
.route("/json_rpc", get(json_rpc))
|
||||
.route("/json_rpc", get(json_rpc::<H>))
|
||||
// Other JSON routes.
|
||||
.route("/get_height", todo!())
|
||||
.route("/getheight", todo!())
|
||||
.route("/get_transactions", todo!())
|
||||
.route("/gettransactions", todo!())
|
||||
.route("/get_alt_blocks_hashes", todo!())
|
||||
.route("/is_key_image_spent", todo!())
|
||||
.route("/send_raw_transaction", todo!())
|
||||
.route("/sendrawtransaction", todo!())
|
||||
.route("/start_mining", todo!())
|
||||
.route("/stop_mining", todo!())
|
||||
.route("/mining_status", todo!())
|
||||
.route("/save_bc", todo!())
|
||||
.route("/get_peer_list", todo!())
|
||||
.route("/get_public_nodes", todo!())
|
||||
.route("/set_log_hash_rate", todo!())
|
||||
.route("/set_log_level", todo!())
|
||||
.route("/set_log_categories", todo!())
|
||||
.route("/get_transaction_pool", todo!())
|
||||
.route("/get_transaction_pool_hashes", todo!())
|
||||
.route("/get_transaction_pool_stats", todo!())
|
||||
.route("/set_bootstrap_daemon", todo!())
|
||||
.route("/stop_daemon", todo!())
|
||||
.route("/get_info", todo!())
|
||||
.route("/getinfo", todo!())
|
||||
.route("/get_net_stats", todo!())
|
||||
.route("/get_limit", todo!())
|
||||
.route("/set_limit", todo!())
|
||||
.route("/out_peers", todo!())
|
||||
.route("/in_peers", todo!())
|
||||
.route("/get_outs", todo!())
|
||||
.route("/update", todo!())
|
||||
.route("/pop_blocks", todo!())
|
||||
.route("/get_height", get(other::get_height::<H>))
|
||||
.route("/getheight", get(other::get_height::<H>))
|
||||
.route("/get_transactions", get(other::get_transactions::<H>))
|
||||
.route("/gettransactions", get(other::get_transactions::<H>))
|
||||
.route("/get_alt_blocks_hashes", get(other::get_alt_blocks_hashes::<H>))
|
||||
.route("/is_key_image_spent", get(other::is_key_image_spent::<H>))
|
||||
.route("/send_raw_transaction", get(other::send_raw_transaction::<H>))
|
||||
.route("/sendrawtransaction", get(other::send_raw_transaction::<H>))
|
||||
.route("/start_mining", get(other::start_mining::<H>))
|
||||
.route("/stop_mining", get(other::stop_mining::<H>))
|
||||
.route("/mining_status", get(other::mining_status::<H>))
|
||||
.route("/save_bc", get(other::save_bc::<H>))
|
||||
.route("/get_peer_list", get(other::get_peer_list::<H>))
|
||||
.route("/get_public_nodes", get(other::get_public_nodes::<H>))
|
||||
.route("/set_log_hash_rate", get(other::set_log_hash_rate::<H>))
|
||||
.route("/set_log_level", get(other::set_log_level::<H>))
|
||||
.route("/set_log_categories", get(other::set_log_categories::<H>))
|
||||
.route("/get_transaction_pool", get(other::get_transaction_pool::<H>))
|
||||
.route("/get_transaction_pool_hashes", get(other::get_transaction_pool_hashes::<H>))
|
||||
.route("/get_transaction_pool_stats", get(other::get_transaction_pool_stats::<H>))
|
||||
.route("/set_bootstrap_daemon", get(other::set_bootstrap_daemon::<H>))
|
||||
.route("/stop_daemon", get(other::stop_daemon::<H>))
|
||||
.route("/get_info", get(other::get_info::<H>))
|
||||
.route("/getinfo", get(other::get_info::<H>))
|
||||
.route("/get_net_stats", get(other::get_net_stats::<H>))
|
||||
.route("/get_limit", get(other::get_limit::<H>))
|
||||
.route("/set_limit", get(other::set_limit::<H>))
|
||||
.route("/out_peers", get(other::out_peers::<H>))
|
||||
.route("/in_peers", get(other::in_peers::<H>))
|
||||
.route("/get_outs", get(other::get_outs::<H>))
|
||||
.route("/update", get(other::update::<H>))
|
||||
.route("/pop_blocks", get(other::pop_blocks::<H>))
|
||||
// Binary routes.
|
||||
.route("/get_blocks.bin", todo!())
|
||||
.route("/getblocks.bin", todo!())
|
||||
.route("/get_blocks_by_height.bin", todo!())
|
||||
.route("/getblocks_by_height.bin", todo!())
|
||||
.route("/get_hashes.bin", todo!())
|
||||
.route("/gethashes.bin", todo!())
|
||||
.route("/get_o_indexes.bin", todo!())
|
||||
.route("/get_outs.bin", todo!())
|
||||
.route("/get_transaction_pool_hashes.bin", todo!())
|
||||
.route("/get_output_distribution.bin", todo!())
|
||||
.route("/get_blocks.bin", get(bin::get_blocks::<H>))
|
||||
.route("/getblocks.bin", get(bin::get_blocks::<H>))
|
||||
.route("/get_blocks_by_height.bin", get(bin::get_blocks_by_height::<H>))
|
||||
.route("/getblocks_by_height.bin", get(bin::get_blocks_by_height::<H>))
|
||||
.route("/get_hashes.bin", get(bin::get_hashes::<H>))
|
||||
.route("/gethashes.bin", get(bin::get_hashes::<H>))
|
||||
.route("/get_o_indexes.bin", get(bin::get_o_indexes::<H>))
|
||||
.route("/get_outs.bin", get(bin::get_outs::<H>))
|
||||
.route("/get_transaction_pool_hashes.bin", get(bin::get_transaction_pool_hashes::<H>))
|
||||
.route("/get_output_distribution.bin", get(bin::get_output_distribution::<H>))
|
||||
// Unknown route.
|
||||
.route("/*", todo!())
|
||||
}
|
||||
|
|
|
@ -4,7 +4,10 @@
|
|||
|
||||
//---------------------------------------------------------------------------------------------------- TODO
|
||||
/// TODO
|
||||
pub enum Request {}
|
||||
pub enum Request {
|
||||
/// TODO
|
||||
Todo,
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Tests
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -4,7 +4,10 @@
|
|||
|
||||
//---------------------------------------------------------------------------------------------------- Status
|
||||
/// TODO
|
||||
pub enum Response {}
|
||||
pub enum Response {
|
||||
/// TODO
|
||||
Todo,
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Tests
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -3,120 +3,89 @@
|
|||
//! All types are originally defined in [`rpc/core_rpc_server_commands_defs.h`](https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h).
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Import
|
||||
use std::{future::Future, sync::Arc};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- TODO
|
||||
// define_request_and_response! {
|
||||
// get_blocksbin,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 162..=262,
|
||||
// GetBlocks,
|
||||
// Request {
|
||||
// requested_info: u8 = default_zero(), "default_zero",
|
||||
// // FIXME: This is a `std::list` in `monerod` because...?
|
||||
// block_ids: ByteArrayVec<32>,
|
||||
// start_height: u64,
|
||||
// prune: bool,
|
||||
// no_miner_tx: bool = default_false(), "default_false",
|
||||
// pool_info_since: u64 = default_zero(), "default_zero",
|
||||
// },
|
||||
// // TODO: this has custom epee (de)serialization.
|
||||
// // <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h#L242-L259>
|
||||
// ResponseBase {
|
||||
// blocks: Vec<BlockCompleteEntry>,
|
||||
// start_height: u64,
|
||||
// current_height: u64,
|
||||
// output_indices: Vec<BlockOutputIndices>,
|
||||
// daemon_time: u64,
|
||||
// pool_info_extent: u8,
|
||||
// added_pool_txs: Vec<PoolTxInfo>,
|
||||
// remaining_added_pool_txids: Vec<[u8; 32]>,
|
||||
// removed_pool_txids: Vec<[u8; 32]>,
|
||||
// }
|
||||
// }
|
||||
use axum::{extract::State, http::StatusCode, Json};
|
||||
use tower::{Service, ServiceExt};
|
||||
|
||||
// define_request_and_response! {
|
||||
// get_blocks_by_heightbin,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 264..=286,
|
||||
// GetBlocksByHeight,
|
||||
// Request {
|
||||
// heights: Vec<u64>,
|
||||
// },
|
||||
// AccessResponseBase {
|
||||
// blocks: Vec<BlockCompleteEntry>,
|
||||
// }
|
||||
// }
|
||||
use crate::{
|
||||
error::Error, json_rpc_method::JsonRpcMethod, request::Request, response::Response,
|
||||
rpc_handler::RpcHandler, rpc_state::RpcState,
|
||||
};
|
||||
|
||||
// define_request_and_response! {
|
||||
// get_hashesbin,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 309..=338,
|
||||
// GetHashes,
|
||||
// Request {
|
||||
// block_ids: ByteArrayVec<32>,
|
||||
// start_height: u64,
|
||||
// },
|
||||
// AccessResponseBase {
|
||||
// m_blocks_ids: ByteArrayVec<32>,
|
||||
// start_height: u64,
|
||||
// current_height: u64,
|
||||
// }
|
||||
// }
|
||||
//---------------------------------------------------------------------------------------------------- Macro
|
||||
/// TODO
|
||||
macro_rules! return_if_restricted {
|
||||
($handler:ident) => {
|
||||
if $handler.state().restricted() {
|
||||
return Ok("TODO");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// #[cfg(not(feature = "epee"))]
|
||||
// define_request_and_response! {
|
||||
// get_o_indexesbin,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 487..=510,
|
||||
// GetOutputIndexes,
|
||||
// #[derive(Copy)]
|
||||
// Request {
|
||||
// txid: [u8; 32],
|
||||
// },
|
||||
// AccessResponseBase {
|
||||
// o_indexes: Vec<u64>,
|
||||
// }
|
||||
// }
|
||||
//---------------------------------------------------------------------------------------------------- Struct definitions
|
||||
/// TODO
|
||||
pub(crate) async fn get_blocks<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// #[cfg(feature = "epee")]
|
||||
// define_request_and_response! {
|
||||
// get_o_indexesbin,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 487..=510,
|
||||
// GetOutputIndexes,
|
||||
// #[derive(Copy)]
|
||||
// Request {
|
||||
// txid: [u8; 32],
|
||||
// },
|
||||
// AccessResponseBase {
|
||||
// o_indexes: Vec<u64> as ContainerAsBlob<u64>,
|
||||
// }
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn get_blocks_by_height<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// get_outsbin,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 512..=565,
|
||||
// GetOuts,
|
||||
// Request {
|
||||
// outputs: Vec<GetOutputsOut>,
|
||||
// get_txid: bool = default_false(), "default_false",
|
||||
// },
|
||||
// AccessResponseBase {
|
||||
// outs: Vec<OutKeyBin>,
|
||||
// }
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn get_hashes<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// get_transaction_pool_hashesbin,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 1593..=1613,
|
||||
// GetTransactionPoolHashes,
|
||||
// Request {},
|
||||
// AccessResponseBase {
|
||||
// tx_hashes: ByteArrayVec<32>,
|
||||
// }
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn get_o_indexes<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
/// TODO
|
||||
pub(crate) async fn get_outs<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
/// TODO
|
||||
pub(crate) async fn get_transaction_pool_hashes<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
/// TODO
|
||||
pub(crate) async fn get_output_distribution<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Tests
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -5,22 +5,29 @@
|
|||
//---------------------------------------------------------------------------------------------------- Import
|
||||
use std::{future::Future, sync::Arc};
|
||||
|
||||
use axum::Json;
|
||||
use tower::Service;
|
||||
use axum::{extract::State, http::StatusCode, Json};
|
||||
use tower::{Service, ServiceExt};
|
||||
|
||||
use crate::{
|
||||
error::Error, json_rpc_method::JsonRpcMethod, request::Request, response::Response,
|
||||
rpc_handler::RpcHandler,
|
||||
rpc_handler::RpcHandler, rpc_state::RpcState,
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Struct definitions
|
||||
/// TODO
|
||||
// pub(crate) async fn json_rpc<H: RpcHandler>(
|
||||
pub(crate) async fn json_rpc(
|
||||
// handler: Arc<H>,
|
||||
pub(crate) async fn json_rpc<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
Json(request): Json<cuprate_json_rpc::Request<JsonRpcMethod>>,
|
||||
) {
|
||||
todo!()
|
||||
) -> Result<Json<&'static str>, StatusCode> {
|
||||
if handler.state().restricted() && request.body.is_restricted() {
|
||||
// const RESTRICTED: Response = Response::Todo;
|
||||
const RESTRICTED: &str = "TODO";
|
||||
return Ok(Json(RESTRICTED));
|
||||
}
|
||||
|
||||
/* call handler */
|
||||
|
||||
Ok(Json("TODO"))
|
||||
}
|
||||
|
||||
// // This generates 2 structs:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! TODO
|
||||
|
||||
mod bin;
|
||||
pub(crate) mod bin;
|
||||
mod json_rpc;
|
||||
mod other;
|
||||
pub(crate) mod other;
|
||||
|
||||
pub(crate) use json_rpc::json_rpc;
|
||||
|
|
|
@ -3,395 +3,278 @@
|
|||
//! All types are originally defined in [`rpc/core_rpc_server_commands_defs.h`](https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h).
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Import
|
||||
use std::{future::Future, sync::Arc};
|
||||
|
||||
use axum::{extract::State, http::StatusCode, Json};
|
||||
use tower::{Service, ServiceExt};
|
||||
|
||||
use crate::{
|
||||
error::Error, json_rpc_method::JsonRpcMethod, request::Request, response::Response,
|
||||
rpc_handler::RpcHandler, rpc_state::RpcState,
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Macro
|
||||
/// TODO
|
||||
macro_rules! return_if_restricted {
|
||||
($handler:ident) => {
|
||||
if $handler.state().restricted() {
|
||||
return Ok("TODO");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- TODO
|
||||
// define_request_and_response! {
|
||||
// get_height,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 138..=160,
|
||||
// GetHeight,
|
||||
// Request {},
|
||||
// ResponseBase {
|
||||
// hash: String,
|
||||
// height: u64,
|
||||
// }
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn get_height<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// get_transactions,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 370..=451,
|
||||
// GetTransactions,
|
||||
// Request {
|
||||
// txs_hashes: Vec<String>,
|
||||
// // FIXME: this is documented as optional but it isn't serialized as an optional
|
||||
// // but it is set _somewhere_ to false in `monerod`
|
||||
// // <https://github.com/monero-project/monero/blob/cc73fe71162d564ffda8e549b79a350bca53c454/src/rpc/core_rpc_server_commands_defs.h#L382>
|
||||
// decode_as_json: bool = default_false(), "default_false",
|
||||
// prune: bool = default_false(), "default_false",
|
||||
// split: bool = default_false(), "default_false",
|
||||
// },
|
||||
// AccessResponseBase {
|
||||
// txs_as_hex: Vec<String>,
|
||||
// txs_as_json: Vec<String>,
|
||||
// missed_tx: Vec<String>,
|
||||
// txs: Vec<TxEntry>,
|
||||
// }
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn get_transactions<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// get_alt_blocks_hashes,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 288..=308,
|
||||
// GetAltBlocksHashes,
|
||||
// Request {},
|
||||
// AccessResponseBase {
|
||||
// blks_hashes: Vec<String>,
|
||||
// }
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn get_alt_blocks_hashes<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// is_key_image_spent,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 454..=484,
|
||||
// IsKeyImageSpent,
|
||||
// Request {
|
||||
// key_images: Vec<String>,
|
||||
// },
|
||||
// AccessResponseBase {
|
||||
// spent_status: Vec<u8>, // TODO: should be `KeyImageSpentStatus`.
|
||||
// }
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn is_key_image_spent<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// send_raw_transaction,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 370..=451,
|
||||
// SendRawTransaction,
|
||||
// Request {
|
||||
// tx_as_hex: String,
|
||||
// do_not_relay: bool = default_false(), "default_false",
|
||||
// do_sanity_checks: bool = default_true(), "default_true",
|
||||
// },
|
||||
// AccessResponseBase {
|
||||
// double_spend: bool,
|
||||
// fee_too_low: bool,
|
||||
// invalid_input: bool,
|
||||
// invalid_output: bool,
|
||||
// low_mixin: bool,
|
||||
// nonzero_unlock_time: bool,
|
||||
// not_relayed: bool,
|
||||
// overspend: bool,
|
||||
// reason: String,
|
||||
// sanity_check_failed: bool,
|
||||
// too_big: bool,
|
||||
// too_few_outputs: bool,
|
||||
// tx_extra_too_big: bool,
|
||||
// }
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn send_raw_transaction<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// start_mining,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 665..=691,
|
||||
// StartMining,
|
||||
// Request {
|
||||
// miner_address: String,
|
||||
// threads_count: u64,
|
||||
// do_background_mining: bool,
|
||||
// ignore_battery: bool,
|
||||
// },
|
||||
// ResponseBase {}
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn start_mining<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// stop_mining,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 825..=843,
|
||||
// StopMining,
|
||||
// Request {},
|
||||
// ResponseBase {}
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn stop_mining<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// mining_status,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 846..=895,
|
||||
// MiningStatus,
|
||||
// Request {},
|
||||
// ResponseBase {
|
||||
// active: bool,
|
||||
// address: String,
|
||||
// bg_idle_threshold: u8,
|
||||
// bg_ignore_battery: bool,
|
||||
// bg_min_idle_seconds: u8,
|
||||
// bg_target: u8,
|
||||
// block_reward: u64,
|
||||
// block_target: u32,
|
||||
// difficulty: u64,
|
||||
// difficulty_top64: u64,
|
||||
// is_background_mining_enabled: bool,
|
||||
// pow_algorithm: String,
|
||||
// speed: u64,
|
||||
// threads_count: u32,
|
||||
// wide_difficulty: String,
|
||||
// }
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn mining_status<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// save_bc,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 898..=916,
|
||||
// SaveBc,
|
||||
// Request {},
|
||||
// ResponseBase {}
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn save_bc<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// get_peer_list,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 1369..=1417,
|
||||
// GetPeerList,
|
||||
// Request {
|
||||
// public_only: bool = default_true(), "default_true",
|
||||
// include_blocked: bool = default_false(), "default_false",
|
||||
// },
|
||||
// ResponseBase {
|
||||
// white_list: Vec<Peer>,
|
||||
// gray_list: Vec<Peer>,
|
||||
// }
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn get_peer_list<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// set_log_hash_rate,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 1450..=1470,
|
||||
// SetLogHashRate,
|
||||
// #[derive(Copy)]
|
||||
// Request {
|
||||
// visible: bool,
|
||||
// },
|
||||
// ResponseBase {}
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn get_public_nodes<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// set_log_level,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 1450..=1470,
|
||||
// SetLogLevel,
|
||||
// #[derive(Copy)]
|
||||
// Request {
|
||||
// level: u8,
|
||||
// },
|
||||
// ResponseBase {}
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn set_log_hash_rate<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// set_log_categories,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 1494..=1517,
|
||||
// SetLogCategories,
|
||||
// Request {
|
||||
// categories: String = default_string(), "default_string",
|
||||
// },
|
||||
// ResponseBase {
|
||||
// categories: String,
|
||||
// }
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn set_log_level<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// set_bootstrap_daemon,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 1785..=1812,
|
||||
// SetBootstrapDaemon,
|
||||
// Request {
|
||||
// address: String,
|
||||
// username: String,
|
||||
// password: String,
|
||||
// proxy: String,
|
||||
// },
|
||||
// #[derive(Copy)]
|
||||
// Response {
|
||||
// status: Status,
|
||||
// }
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn set_log_categories<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// get_transaction_pool,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 1569..=1591,
|
||||
// GetTransactionPool,
|
||||
// Request {},
|
||||
// AccessResponseBase {
|
||||
// transactions: Vec<TxInfo>,
|
||||
// spent_key_images: Vec<SpentKeyImageInfo>,
|
||||
// }
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn get_transaction_pool<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// get_transaction_pool_stats,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 1712..=1732,
|
||||
// GetTransactionPoolStats,
|
||||
// Request {},
|
||||
// AccessResponseBase {
|
||||
// pool_stats: TxpoolStats,
|
||||
// }
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn get_transaction_pool_hashes<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// stop_daemon,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 1814..=1831,
|
||||
// StopDaemon,
|
||||
// Request {},
|
||||
// ResponseBase {
|
||||
// status: Status,
|
||||
// }
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn get_transaction_pool_stats<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// get_limit,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 1852..=1874,
|
||||
// GetLimit,
|
||||
// Request {},
|
||||
// ResponseBase {
|
||||
// limit_down: u64,
|
||||
// limit_up: u64,
|
||||
// }
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn set_bootstrap_daemon<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// set_limit,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 1876..=1903,
|
||||
// SetLimit,
|
||||
// Request {
|
||||
// limit_down: i64,
|
||||
// limit_up: i64,
|
||||
// },
|
||||
// ResponseBase {
|
||||
// limit_down: i64,
|
||||
// limit_up: i64,
|
||||
// }
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn stop_daemon<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// out_peers,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 1876..=1903,
|
||||
// OutPeers,
|
||||
// Request {
|
||||
// set: bool = default_true(), "default_true",
|
||||
// out_peers: u32,
|
||||
// },
|
||||
// ResponseBase {
|
||||
// out_peers: u32,
|
||||
// }
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn get_info<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// get_net_stats,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 793..=822,
|
||||
// GetNetStats,
|
||||
// Request {},
|
||||
// ResponseBase {
|
||||
// start_time: u64,
|
||||
// total_packets_in: u64,
|
||||
// total_bytes_in: u64,
|
||||
// total_packets_out: u64,
|
||||
// total_bytes_out: u64,
|
||||
// }
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn get_net_stats<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// get_outs,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 567..=609,
|
||||
// GetOuts,
|
||||
// Request {
|
||||
// outputs: Vec<GetOutputsOut>,
|
||||
// get_txid: bool,
|
||||
// },
|
||||
// ResponseBase {
|
||||
// outs: Vec<OutKey>,
|
||||
// }
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn get_limit<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// update,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 2324..=2359,
|
||||
// Update,
|
||||
// Request {
|
||||
// command: String,
|
||||
// path: String = default_string(), "default_string",
|
||||
// },
|
||||
// ResponseBase {
|
||||
// auto_uri: String,
|
||||
// hash: String,
|
||||
// path: String,
|
||||
// update: bool,
|
||||
// user_uri: String,
|
||||
// version: String,
|
||||
// }
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn set_limit<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// pop_blocks,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 2722..=2745,
|
||||
// PopBlocks,
|
||||
// Request {
|
||||
// nblocks: u64,
|
||||
// },
|
||||
// ResponseBase {
|
||||
// height: u64,
|
||||
// }
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn out_peers<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// UNDOCUMENTED_ENDPOINT,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 2798..=2823,
|
||||
// GetTxIdsLoose,
|
||||
// Request {
|
||||
// txid_template: String,
|
||||
// num_matching_bits: u32,
|
||||
// },
|
||||
// ResponseBase {
|
||||
// txids: Vec<String>,
|
||||
// }
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn in_peers<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// UNDOCUMENTED_ENDPOINT,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 1615..=1635,
|
||||
// GetTransactionPoolHashes,
|
||||
// Request {},
|
||||
// ResponseBase {
|
||||
// tx_hashes: Vec<String>,
|
||||
// }
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn get_outs<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
// define_request_and_response! {
|
||||
// UNDOCUMENTED_ENDPOINT,
|
||||
// cc73fe71162d564ffda8e549b79a350bca53c454 =>
|
||||
// core_rpc_server_commands_defs.h => 1419..=1448,
|
||||
// GetPublicNodes,
|
||||
// Request {
|
||||
// gray: bool = default_false(), "default_false",
|
||||
// white: bool = default_true(), "default_true",
|
||||
// include_blocked: bool = default_false(), "default_false",
|
||||
// },
|
||||
// ResponseBase {
|
||||
// gray: Vec<PublicNode>,
|
||||
// white: Vec<PublicNode>,
|
||||
// }
|
||||
// }
|
||||
/// TODO
|
||||
pub(crate) async fn update<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
/// TODO
|
||||
pub(crate) async fn pop_blocks<H: RpcHandler>(
|
||||
State(handler): State<H>,
|
||||
) -> Result<&'static str, StatusCode> {
|
||||
return_if_restricted!(handler);
|
||||
/* call handler */
|
||||
Ok("TODO")
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------------- Tests
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
//---------------------------------------------------------------------------------------------------- Use
|
||||
use std::{future::Future, marker::PhantomData, sync::Arc};
|
||||
|
||||
use axum::extract::State;
|
||||
use tower::Service;
|
||||
|
||||
use crate::{
|
||||
|
@ -11,9 +12,9 @@ use crate::{
|
|||
|
||||
//---------------------------------------------------------------------------------------------------- TODO
|
||||
/// TODO
|
||||
pub trait RpcHandler: Send + Sync + 'static {
|
||||
pub trait RpcHandler: Clone + Send + Sync + 'static {
|
||||
/// TODO
|
||||
type RpcState: RpcState;
|
||||
type State: RpcState;
|
||||
|
||||
/// TODO
|
||||
type Handler: Send + Sync + 'static + Service<Request>;
|
||||
|
@ -23,7 +24,10 @@ pub trait RpcHandler: Send + Sync + 'static {
|
|||
// <Self::Handler as Service<Request>>::Future: Future<Output = Result<Response, Error>>;
|
||||
|
||||
/// TODO
|
||||
fn state(&self) -> Self::RpcState;
|
||||
fn state(&self) -> &Self::State;
|
||||
|
||||
/// TODO
|
||||
fn handler(&self) -> Self::Handler;
|
||||
}
|
||||
|
||||
/// TODO
|
||||
|
@ -35,15 +39,19 @@ pub struct ConcreteRpcHandler<Handler> {
|
|||
|
||||
impl<H> RpcHandler for ConcreteRpcHandler<H>
|
||||
where
|
||||
H: Send + Sync + 'static + Service<Request>,
|
||||
H: Clone + Send + Sync + 'static + Service<Request>,
|
||||
<H as Service<Request>>::Response: Into<Response>,
|
||||
<H as Service<Request>>::Error: Into<Error>,
|
||||
<H as Service<Request>>::Future: Future<Output = Result<Response, Error>>,
|
||||
{
|
||||
type RpcState = ConcreteRpcState;
|
||||
type State = ConcreteRpcState;
|
||||
type Handler = H;
|
||||
|
||||
fn state(&self) -> Self::RpcState {
|
||||
self.state
|
||||
fn state(&self) -> &Self::State {
|
||||
&self.state
|
||||
}
|
||||
|
||||
fn handler(&self) -> Self::Handler {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue