use super::event::Event;
use super::*;
use dusk_bytes::Serializable;
use dusk_core::abi::ContractId;
use dusk_core::stake::StakeFundOwner;
use node::vm::VMExecution;
use rusk_profile::CRS_17_HASH;
use serde::Serialize;
use std::sync::{mpsc, Arc};
use std::thread;
use tokio::task;
use tungstenite::http::request;
use crate::node::Rusk;
const RUSK_FEEDER_HEADER: &str = "Rusk-Feeder";
#[async_trait]
impl HandleRequest for Rusk {
fn can_handle(&self, request: &MessageRequest) -> bool {
let route = request.event.to_route();
if matches!(route, (Target::Host(_), "rusk", "preverify")) {
return false;
}
if route.2.starts_with("prove_") {
return false;
}
matches!(
route,
(Target::Contract(_), ..) | (Target::Host(_), "rusk", _)
)
}
fn can_handle_rues(&self, request: &RuesDispatchEvent) -> bool {
#[allow(clippy::match_like_matches_macro)]
match request.uri.inner() {
("contracts", Some(_), _) => true,
("node", _, "provisioners") => true,
("node", _, "crs") => true,
_ => false,
}
}
async fn handle_rues(
&self,
request: &RuesDispatchEvent,
) -> anyhow::Result<ResponseData> {
match request.uri.inner() {
("contracts", Some(contract_id), method) => {
let feeder = request.header(RUSK_FEEDER_HEADER).is_some();
let data = request.data.as_bytes();
self.handle_contract_query(contract_id, method, data, feeder)
}
("node", _, "provisioners") => self.get_provisioners(),
("node", _, "crs") => self.get_crs(),
_ => Err(anyhow::anyhow!("Unsupported")),
}
}
async fn handle(
&self,
request: &MessageRequest,
) -> anyhow::Result<ResponseData> {
match &request.event.to_route() {
(Target::Contract(_), ..) => {
let feeder = request.header(RUSK_FEEDER_HEADER).is_some();
self.handle_contract_query_legacy(&request.event, feeder)
}
(Target::Host(_), "rusk", "provisioners") => {
self.get_provisioners()
}
(Target::Host(_), "rusk", "crs") => self.get_crs(),
_ => Err(anyhow::anyhow!("Unsupported")),
}
}
}
impl Rusk {
fn handle_contract_query_legacy(
&self,
event: &Event,
feeder: bool,
) -> anyhow::Result<ResponseData> {
let contract = event.target.inner();
let topic = &event.topic;
let data = event.data.as_bytes();
self.handle_contract_query(contract, topic, data, feeder)
}
fn handle_contract_query(
&self,
contract: &str,
topic: &str,
data: &[u8],
feeder: bool,
) -> anyhow::Result<ResponseData> {
let contract_bytes = hex::decode(contract)?;
let contract_bytes = contract_bytes
.try_into()
.map_err(|_| anyhow::anyhow!("Invalid contract bytes"))?;
let contract_id = ContractId::from_bytes(contract_bytes);
let fn_name = topic.to_string();
let data = data.to_vec();
if feeder {
let (sender, receiver) = mpsc::channel();
let rusk = self.clone();
thread::spawn(move || {
rusk.feeder_query_raw(contract_id, fn_name, data, sender);
});
Ok(ResponseData::new(receiver))
} else {
let data = self
.query_raw(contract_id, fn_name, data)
.map_err(|e| anyhow::anyhow!("{e}"))?;
Ok(ResponseData::new(data))
}
}
fn get_provisioners(&self) -> anyhow::Result<ResponseData> {
let prov: Vec<_> = self
.provisioners(None)
.expect("Cannot query state for provisioners")
.map(|(key, stake)| {
let owner = StakeOwner::from(&key.owner);
let key = bs58::encode(key.account.to_bytes()).into_string();
let amount = stake.amount.unwrap_or_default();
Provisioner {
amount: amount.value,
locked_amt: amount.locked,
eligibility: amount.eligibility,
key,
reward: stake.reward,
faults: stake.faults,
hard_faults: stake.hard_faults,
owner,
}
})
.collect();
Ok(ResponseData::new(serde_json::to_value(prov)?))
}
fn get_crs(&self) -> anyhow::Result<ResponseData> {
let crs = rusk_profile::get_common_reference_string()?;
Ok(ResponseData::new(crs).with_header("crs-hash", CRS_17_HASH))
}
}
#[derive(Serialize)]
struct Provisioner {
key: String,
amount: u64,
locked_amt: u64,
eligibility: u64,
reward: u64,
faults: u8,
hard_faults: u8,
owner: StakeOwner,
}
#[derive(Serialize)]
enum StakeOwner {
Account(String),
Contract(String),
}
impl From<&StakeFundOwner> for StakeOwner {
fn from(value: &StakeFundOwner) -> Self {
match value {
StakeFundOwner::Account(account) => StakeOwner::Account(
bs58::encode(account.to_bytes()).into_string(),
),
StakeFundOwner::Contract(contract) => {
StakeOwner::Contract(hex::encode(contract.as_bytes()))
}
}
}
}