use anyhow::Context as _;
use rand::Rng;
use zksync_concurrency::{ctx, limiter, time};
use zksync_protobuf::{kB, required, ProtoFmt};
use super::Capability;
use crate::proto::ping as proto;
pub(crate) struct Rpc;
impl super::Rpc for Rpc {
const CAPABILITY: Capability = Capability::Ping;
const INFLIGHT: u32 = 1;
const METHOD: &'static str = "ping";
type Req = Req;
type Resp = Resp;
}
pub(crate) const RATE: limiter::Rate = limiter::Rate {
burst: 2,
refresh: time::Duration::seconds(1),
};
pub(crate) struct Server;
#[async_trait::async_trait]
impl super::Handler<Rpc> for Server {
fn max_req_size(&self) -> usize {
kB
}
async fn handle(&self, _ctx: &ctx::Ctx, req: Req) -> anyhow::Result<Resp> {
Ok(Resp(req.0))
}
}
impl super::Client<Rpc> {
pub(crate) async fn ping_loop(
&self,
ctx: &ctx::Ctx,
timeout: time::Duration,
) -> anyhow::Result<()> {
loop {
let req = Req(ctx.rng().gen());
let resp = self
.call(&ctx.with_timeout(timeout), &req, kB)
.await
.context("ping")?;
if req.0 != resp.0 {
anyhow::bail!("bad ping response");
}
if let Err(ctx::Canceled) = ctx.sleep(timeout).await {
return Ok(());
}
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Req(pub(crate) [u8; 32]);
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Resp(pub(crate) [u8; 32]);
impl ProtoFmt for Req {
type Proto = proto::PingReq;
fn read(r: &Self::Proto) -> anyhow::Result<Self> {
Ok(Self(required(&r.data)?[..].try_into()?))
}
fn build(&self) -> Self::Proto {
Self::Proto {
data: Some(self.0.into()),
}
}
}
impl ProtoFmt for Resp {
type Proto = proto::PingResp;
fn read(r: &Self::Proto) -> anyhow::Result<Self> {
Ok(Self(required(&r.data)?[..].try_into()?))
}
fn build(&self) -> Self::Proto {
Self::Proto {
data: Some(self.0.into()),
}
}
}