Files
nym/ephemera/src/core/api_cmd.rs
T
2023-10-15 23:05:34 +02:00

391 lines
14 KiB
Rust

use std::num::NonZeroUsize;
use log::{debug, error, trace};
use lru::LruCache;
use tokio::sync::oneshot::Sender;
use crate::api::types::{ApiBlockBroadcastInfo, ApiBroadcastInfo};
use crate::api::{DhtKV, DhtKey, DhtValue};
use crate::ephemera_api::ApiEphemeraMessage;
use crate::peer::ToPeerId;
use crate::{
api::{
self,
application::Application,
types::{ApiBlock, ApiCertificate, ApiError},
ToEphemeraApiCmd,
},
block::{manager::BlockManagerError, types::message},
crypto::EphemeraKeypair,
ephemera_api::ApiEphemeraConfig,
network::libp2p::ephemera_sender::EphemeraEvent,
Ephemera,
};
type DhtPendingQueryReply = Sender<Result<Option<(Vec<u8>, Vec<u8>)>, ApiError>>;
pub(crate) struct ApiCmdProcessor {
pub(crate) dht_query_cache: LruCache<Vec<u8>, Vec<DhtPendingQueryReply>>,
}
impl ApiCmdProcessor {
pub(crate) fn new() -> Self {
Self {
dht_query_cache: LruCache::new(NonZeroUsize::new(1000).unwrap()),
}
}
pub(crate) async fn process_api_requests<A: Application>(
ephemera: &mut Ephemera<A>,
cmd: ToEphemeraApiCmd,
) -> api::Result<()> {
trace!("Processing API request: {:?}", cmd);
match cmd {
ToEphemeraApiCmd::SubmitEphemeraMessage(api_msg, reply) => {
// Ask application to decide if we should accept this message.
Self::submit_message(ephemera, api_msg, reply).await?;
}
ToEphemeraApiCmd::QueryBlockByHash(block_hash, reply) => {
Self::query_block_by_hash(ephemera, &block_hash, reply).await;
}
ToEphemeraApiCmd::QueryBlockByHeight(height, reply) => {
Self::query_block_by_height(ephemera, height, reply).await;
}
ToEphemeraApiCmd::QueryLastBlock(reply) => {
Self::query_last_block(ephemera, reply).await;
}
ToEphemeraApiCmd::QueryBlockCertificates(block_id, reply) => {
Self::query_block_certificates(ephemera, &block_id, reply).await;
}
ToEphemeraApiCmd::QueryDht(key, reply) => {
Self::query_dht(ephemera, key, reply).await;
}
ToEphemeraApiCmd::StoreInDht(key, value, reply) => {
Self::store_in_dht(ephemera, key, value, reply).await;
}
ToEphemeraApiCmd::QueryEphemeraConfig(reply) => {
Self::ephemera_config(ephemera, reply);
}
ToEphemeraApiCmd::QueryBroadcastGroup(reply) => {
Self::broadcast_group(ephemera, reply);
}
ToEphemeraApiCmd::QueryBlockBroadcastInfo(hash, reply) => {
Self::query_block_broadcast_info(ephemera, &hash, reply).await;
}
ToEphemeraApiCmd::VerifyMessageInBlock(block_hash, message_hash, index, reply) => {
Self::verify_message_in_block(ephemera, block_hash, message_hash, index, reply)
.await;
}
}
Ok(())
}
fn broadcast_group<A: Application>(
ephemera: &mut Ephemera<A>,
reply: Sender<api::Result<ApiBroadcastInfo>>,
) {
let group_peers = ephemera.broadcast_group.current();
let bc = ApiBroadcastInfo::new(group_peers.clone(), ephemera.node_info.peer_id);
reply
.send(Ok(bc))
.expect("Error sending BroadcastGroup response to api");
}
fn ephemera_config<A: Application>(
ephemera: &Ephemera<A>,
reply: Sender<api::Result<ApiEphemeraConfig>>,
) {
let node_info = ephemera.node_info.clone();
let api_config = ApiEphemeraConfig {
protocol_address: node_info.protocol_address(),
api_address: node_info.api_address_http(),
websocket_address: node_info.ws_address_ws(),
public_key: node_info.keypair.public_key().to_string(),
block_producer: node_info.initial_config.block_manager.producer,
block_creation_interval_sec: node_info
.initial_config
.block_manager
.creation_interval_sec,
};
reply
.send(Ok(api_config))
.expect("Error sending EphemeraConfig response to api");
}
async fn store_in_dht<A: Application>(
ephemera: &mut Ephemera<A>,
key: DhtKey,
value: DhtValue,
reply: Sender<api::Result<()>>,
) {
let response = match ephemera
.to_network
.send_ephemera_event(EphemeraEvent::StoreInDht { key, value })
.await
{
Ok(()) => Ok(()),
Err(err) => {
error!("Error sending StoreInDht to network: {:?}", err);
Err(ApiError::Internal("Failed to store in DHT".to_string()))
}
};
reply
.send(response)
.expect("Error sending StoreInDht response to api");
}
async fn query_dht<A: Application>(
ephemera: &mut Ephemera<A>,
key: DhtKey,
reply: Sender<api::Result<Option<DhtKV>>>,
) {
match ephemera
.to_network
.send_ephemera_event(EphemeraEvent::QueryDht { key: key.clone() })
.await
{
Ok(()) => {
//Save the reply channel in a map and send the reply when we get the response from the network
ephemera
.api_cmd_processor
.dht_query_cache
.get_or_insert_mut(key, Vec::new)
.push(reply);
}
Err(err) => {
error!("Error sending QueryDht to network: {:?}", err);
reply
.send(Err(ApiError::Internal("Failed to query DHT".to_string())))
.expect("Error sending QueryDht response to api");
}
};
}
async fn query_block_certificates<A: Application>(
ephemera: &mut Ephemera<A>,
block_id: &str,
reply: Sender<api::Result<Option<Vec<ApiCertificate>>>>,
) {
let response = match ephemera
.storage
.lock()
.await
.get_block_certificates(block_id)
{
Ok(signatures) => {
let certificates = signatures.map(|s| {
s.into_iter()
.map(Into::into)
.collect::<Vec<ApiCertificate>>()
});
Ok(certificates)
}
Err(err) => {
error!("Error querying block certificates: {:?}", err);
Err(ApiError::Internal(
"Failed to query block certificates".to_string(),
))
}
};
reply
.send(response)
.expect("Error sending QueryBlockSignatures response to api");
}
async fn query_last_block<A: Application>(
ephemera: &mut Ephemera<A>,
reply: Sender<api::Result<ApiBlock>>,
) {
let response = match ephemera.storage.lock().await.get_last_block() {
Ok(Some(block)) => Ok(block.into()),
Ok(None) => Err(ApiError::Internal(
"No blocks found, this is a bug!".to_string(),
)),
Err(err) => {
error!("Error querying last block: {:?}", err);
Err(ApiError::Internal("Failed to query last block".to_string()))
}
};
reply
.send(response)
.expect("Error sending QueryLastBlock response to api");
}
async fn query_block_by_height<A: Application>(
ephemera: &mut Ephemera<A>,
height: u64,
reply: Sender<api::Result<Option<ApiBlock>>>,
) {
let response = match ephemera.storage.lock().await.get_block_by_height(height) {
Ok(Some(block)) => {
let api_block: ApiBlock = block.into();
Ok(api_block.into())
}
Ok(None) => Ok(None),
Err(err) => {
error!("Error querying block by height: {:?}", err);
Err(ApiError::Internal(
"Failed to query block by height".to_string(),
))
}
};
reply
.send(response)
.expect("Error sending QueryBlockByHeight response to api");
}
async fn query_block_by_hash<A: Application>(
ephemera: &mut Ephemera<A>,
block_hash: &str,
reply: Sender<api::Result<Option<ApiBlock>>>,
) {
let response = match ephemera.storage.lock().await.get_block_by_hash(block_hash) {
Ok(Some(block)) => {
let api_block: ApiBlock = block.into();
Ok(api_block.into())
}
Ok(None) => Ok(None),
Err(err) => {
error!("Error querying block by id: {:?}", err);
Err(ApiError::Internal(
"Failed to query block by id".to_string(),
))
}
};
reply
.send(response)
.expect("Error sending QueryBlockByHash response to api");
}
async fn submit_message<A: Application>(
ephemera: &mut Ephemera<A>,
api_msg: Box<ApiEphemeraMessage>,
reply: Sender<api::Result<()>>,
) -> api::Result<()> {
let response = match ephemera.application.check_tx(*api_msg.clone()) {
Ok(true) => {
trace!("Application accepted ephemera message: {:?}", api_msg);
// Send to BlockManager to verify it and put into memory pool
let ephemera_msg: message::EphemeraMessage = (*api_msg).into();
match ephemera.block_manager.on_new_message(ephemera_msg.clone()) {
Ok(()) => {
//Gossip to network for other nodes to receive
match ephemera
.to_network
.send_ephemera_event(EphemeraEvent::EphemeraMessage(
ephemera_msg.into(),
))
.await
{
Ok(()) => Ok(()),
Err(err) => {
error!("Error sending EphemeraMessage to network: {:?}", err);
Err(ApiError::Internal("Failed to submit message".to_string()))
}
}
}
Err(err) => match err {
BlockManagerError::DuplicateMessage(_) => Err(ApiError::DuplicateMessage),
BlockManagerError::BlockManager(err) => {
error!("Error submitting message to block manager: {:?}", err);
Err(ApiError::Internal("Failed to submit message".to_string()))
}
},
}
}
Ok(false) => {
debug!("Application rejected ephemera message: {:?}", api_msg);
Err(ApiError::ApplicationRejectedMessage)
}
Err(err) => {
error!("Application rejected ephemera message: {:?}", err);
Err(ApiError::Application(err))
}
};
reply
.send(response)
.expect("Error sending SubmitEphemeraMessage response to api");
Ok(())
}
async fn query_block_broadcast_info<A: Application>(
ephemera: &mut Ephemera<A>,
block_id: &str,
reply: Sender<api::Result<Option<ApiBlockBroadcastInfo>>>,
) {
let response = match ephemera
.storage
.lock()
.await
.get_block_broadcast_group(block_id)
{
Ok(Some(peers)) => {
let local_peer = ephemera.node_info.keypair.peer_id();
Ok(Some(ApiBlockBroadcastInfo::new(local_peer, peers)))
}
Ok(None) => Ok(None),
Err(err) => {
error!("Error querying block broadcast info: {:?}", err);
Err(ApiError::Internal(
"Failed to query block broadcast info".to_string(),
))
}
};
reply
.send(response)
.expect("Error sending QueryBlockBroadcastGroup response to api");
}
async fn verify_message_in_block<A: Application>(
ephemera: &mut Ephemera<A>,
block_hash: String,
message_hash: String,
index: usize,
reply: Sender<api::Result<bool>>,
) {
let message_hash_hash = message_hash.parse();
if message_hash_hash.is_err() {
reply
.send(Err(ApiError::InvalidHash(
"Failed to parse message hash".to_string(),
)))
.expect("Error sending VerifyMessageInBlock response to api");
return;
}
let message_hash_hash = message_hash_hash.unwrap();
let storage = ephemera.storage.lock().await;
match storage.get_block_merkle_tree(&block_hash) {
Ok(Some(tree)) => {
let result = tree.verify_leaf_at_index(message_hash_hash, index);
reply
.send(Ok(result))
.expect("Error sending VerifyMessageInBlock response to api");
}
Ok(None) => {
reply
.send(Ok(false))
.expect("Error sending VerifyMessageInBlock response to api");
}
Err(err) => {
error!("Error querying block merkle tree: {:?}", err);
reply
.send(Err(ApiError::Internal(
"Failed to verify message".to_string(),
)))
.expect("Error sending VerifyMessageInBlock response to api");
}
}
}
}