Files
nym/common/client-libs/validator-client/src/rpc/mod.rs
T
Jędrzej Stuczyński a348ff43b0 feature: rewarding for ticketbook issuance (#5112)
* fixed pagination for querying for validators

* wip: decoupling block signing from ticketbook issuance

* added ecash contract query for latest deposit

* parking the branch: wrappers for merkle tree for issued ticketbooks

* make nym-api store merkle trees of issued ticketbooks

* nym-api route for returning all deposits alongside merkle root

* return index alongside deposit id

* persisting merkle index alongside issued ticketbook details

* wip

* responses for issued deposit challenges

* nym-api cleanup

* verification of issued partial ticketbooks

* cleanup of rewarder code

* make the rest of codebase compile

* updated config file

* improved logging

* fixed division by zero if there were no ticketbooks issued in a day

* using correct budget when rewarding operators

* fixed routes for issued data

* fixed ecash test fixture

* fixed incorrect deserialisation of expiration_date param

* additional bugfixes for ticketbook issuance

* more fixes and updated tests

* fixed formatting after rebasing

* updated schema

* fixed edge case unit test
2024-11-14 16:55:02 +00:00

539 lines
17 KiB
Rust

// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// TEMPORARY WORKAROUND:
// those features are expected as the below should only get activated whenever
// the corresponding features in tendermint-rpc are enabled transitively
#![allow(unexpected_cfgs)]
use async_trait::async_trait;
use cosmrs::tendermint::{self, abci, block::Height, evidence::Evidence, Genesis, Hash};
use serde::{de::DeserializeOwned, Serialize};
use std::fmt;
use tendermint_rpc::{
endpoint::{validators::DEFAULT_VALIDATORS_PER_PAGE, *},
query::Query,
Error, Order, Paging, SimpleRequest,
};
#[cfg(feature = "http-client")]
use crate::error::TendermintRpcError;
#[cfg(feature = "http-client")]
use crate::HttpRpcClient;
#[cfg(feature = "http-client")]
use tendermint_rpc::client::CompatMode;
#[cfg(feature = "http-client")]
use tendermint_rpc::HttpClientUrl;
pub mod reqwest;
#[cfg(feature = "http-client")]
pub fn http_client<U>(url: U) -> Result<HttpRpcClient, TendermintRpcError>
where
U: TryInto<HttpClientUrl, Error = Error>,
{
HttpRpcClient::builder(url.try_into()?)
.compat_mode(CompatMode::V0_37)
.build()
}
// we have to create a sealed trait since `TendermintClient` needs T: Send (due to how async trait is created)
// which we can't do in wasm
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait TendermintRpcClient {
/// `/abci_info`: get information about the ABCI application.
async fn abci_info(&self) -> Result<abci::response::Info, Error> {
Ok(self.perform(abci_info::Request).await?.response)
}
/// `/abci_query`: query the ABCI application
async fn abci_query<V>(
&self,
path: Option<String>,
data: V,
height: Option<Height>,
prove: bool,
) -> Result<abci_query::AbciQuery, Error>
where
V: Into<Vec<u8>> + Send,
{
Ok(self
.perform(abci_query::Request::new(path, data, height, prove))
.await?
.response)
}
/// `/block`: get block at a given height.
async fn block<H>(&self, height: H) -> Result<block::Response, Error>
where
H: Into<Height> + Send,
{
self.perform(block::Request::new(height.into())).await
}
/// `/block_by_hash`: get block by hash.
async fn block_by_hash(
&self,
hash: tendermint::Hash,
) -> Result<block_by_hash::Response, Error> {
self.perform(block_by_hash::Request::new(hash)).await
}
/// `/block`: get the latest block.
async fn latest_block(&self) -> Result<block::Response, Error> {
self.perform(block::Request::default()).await
}
/// `/header`: get block header at a given height.
async fn header<H>(&self, height: H) -> Result<header::Response, Error>
where
H: Into<Height> + Send,
{
self.perform(header::Request::new(height.into())).await
}
/// `/header_by_hash`: get block by hash.
async fn header_by_hash(
&self,
hash: tendermint::Hash,
) -> Result<header_by_hash::Response, Error> {
self.perform(header_by_hash::Request::new(hash)).await
}
/// `/block_results`: get ABCI results for a block at a particular height.
async fn block_results<H>(&self, height: H) -> Result<block_results::Response, Error>
where
H: Into<Height> + Send,
{
self.perform(block_results::Request::new(height.into()))
.await
}
/// `/block_results`: get ABCI results for the latest block.
async fn latest_block_results(&self) -> Result<block_results::Response, Error> {
self.perform(block_results::Request::default()).await
}
/// `/block_search`: search for blocks by BeginBlock and EndBlock events.
async fn block_search(
&self,
query: Query,
page: u32,
per_page: u8,
order: Order,
) -> Result<block_search::Response, Error> {
self.perform(block_search::Request::new(query, page, per_page, order))
.await
}
/// `/blockchain`: get block headers for `min` <= `height` <= `max`.
///
/// Block headers are returned in descending order (highest first).
///
/// Returns at most 20 items.
async fn blockchain<H>(&self, min: H, max: H) -> Result<blockchain::Response, Error>
where
H: Into<Height> + Send,
{
// TODO(tarcieri): return errors for invalid params before making request?
self.perform(blockchain::Request::new(min.into(), max.into()))
.await
}
/// `/broadcast_tx_async`: broadcast a transaction, returning immediately.
async fn broadcast_tx_async<T>(&self, tx: T) -> Result<broadcast::tx_async::Response, Error>
where
T: Into<Vec<u8>> + Send,
{
self.perform(broadcast::tx_async::Request::new(tx)).await
}
/// `/broadcast_tx_sync`: broadcast a transaction, returning the response
/// from `CheckTx`.
async fn broadcast_tx_sync<T>(&self, tx: T) -> Result<broadcast::tx_sync::Response, Error>
where
T: Into<Vec<u8>> + Send,
{
self.perform(broadcast::tx_sync::Request::new(tx)).await
}
/// `/broadcast_tx_commit`: broadcast a transaction, returning the response
/// from `DeliverTx`.
async fn broadcast_tx_commit<T>(&self, tx: T) -> Result<broadcast::tx_commit::Response, Error>
where
T: Into<Vec<u8>> + Send,
{
self.perform(broadcast::tx_commit::Request::new(tx)).await
}
/// `/commit`: get block commit at a given height.
async fn commit<H>(&self, height: H) -> Result<commit::Response, Error>
where
H: Into<Height> + Send,
{
self.perform(commit::Request::new(height.into())).await
}
/// `/consensus_params`: get current consensus parameters at the specified
/// height.
async fn consensus_params<H>(&self, height: H) -> Result<consensus_params::Response, Error>
where
H: Into<Height> + Send,
{
self.perform(consensus_params::Request::new(Some(height.into())))
.await
}
/// `/consensus_state`: get current consensus state
async fn consensus_state(&self) -> Result<consensus_state::Response, Error> {
self.perform(consensus_state::Request::new()).await
}
// TODO(thane): Simplify once validators endpoint removes pagination.
/// `/validators`: get validators a given height.
async fn validators<H>(&self, height: H, paging: Paging) -> Result<validators::Response, Error>
where
H: Into<Height> + Send,
{
let height = height.into();
match paging {
Paging::Default => {
self.perform(validators::Request::new(Some(height), None, None))
.await
}
Paging::Specific {
page_number,
per_page,
} => {
self.perform(validators::Request::new(
Some(height),
Some(page_number),
Some(per_page),
))
.await
}
Paging::All => {
let mut page_num = 1_usize;
let mut validators = Vec::new();
let per_page = DEFAULT_VALIDATORS_PER_PAGE.into();
loop {
let response = self
.perform(validators::Request::new(
Some(height),
Some(page_num.into()),
Some(per_page),
))
.await?;
validators.extend(response.validators);
if validators.len() as i32 == response.total {
return Ok(validators::Response::new(
response.block_height,
validators,
response.total,
));
}
page_num += 1;
}
}
}
}
/// `/consensus_params`: get the latest consensus parameters.
async fn latest_consensus_params(&self) -> Result<consensus_params::Response, Error> {
self.perform(consensus_params::Request::new(None)).await
}
/// `/commit`: get the latest block commit
async fn latest_commit(&self) -> Result<commit::Response, Error> {
self.perform(commit::Request::default()).await
}
/// `/health`: get node health.
///
/// Returns empty result (200 OK) on success, no response in case of an error.
async fn health(&self) -> Result<(), Error> {
self.perform(health::Request).await?;
Ok(())
}
/// `/genesis`: get genesis file.
async fn genesis<AppState>(&self) -> Result<Genesis<AppState>, Error>
where
AppState: fmt::Debug + Serialize + DeserializeOwned + Send,
{
Ok(self.perform(genesis::Request::default()).await?.genesis)
}
/// `/net_info`: obtain information about P2P and other network connections.
async fn net_info(&self) -> Result<net_info::Response, Error> {
self.perform(net_info::Request).await
}
/// `/status`: get Tendermint status including node info, pubkey, latest
/// block hash, app hash, block height and time.
async fn status(&self) -> Result<status::Response, Error> {
self.perform(status::Request).await
}
/// `/broadcast_evidence`: broadcast an evidence.
async fn broadcast_evidence(&self, e: Evidence) -> Result<evidence::Response, Error> {
self.perform(evidence::Request::new(e)).await
}
/// `/tx`: find transaction by hash.
async fn tx(&self, hash: Hash, prove: bool) -> Result<tx::Response, Error> {
self.perform(tx::Request::new(hash, prove)).await
}
/// `/tx_search`: search for transactions with their results.
async fn tx_search(
&self,
query: Query,
prove: bool,
page: u32,
per_page: u8,
order: Order,
) -> Result<tx_search::Response, Error> {
self.perform(tx_search::Request::new(query, prove, page, per_page, order))
.await
}
#[cfg(any(
feature = "tendermint-rpc-http-client",
feature = "tendermint-rpc-websocket-client"
))]
/// Poll the `/health` endpoint until it returns a successful result or
/// the given `timeout` has elapsed.
async fn wait_until_healthy<T>(&self, timeout: T) -> Result<(), Error>
where
T: Into<core::time::Duration> + Send,
{
let timeout = timeout.into();
let poll_interval = core::time::Duration::from_millis(200);
let mut attempts_remaining = timeout.as_millis() / poll_interval.as_millis();
while self.health().await.is_err() {
if attempts_remaining == 0 {
return Err(Error::timeout(timeout));
}
attempts_remaining -= 1;
tokio::time::sleep(poll_interval).await;
}
Ok(())
}
/// Perform a request against the RPC endpoint.
///
/// This method is used by the default implementations of specific
/// endpoint methods. The latest protocol dialect is assumed to be invoked.
async fn perform<R>(&self, request: R) -> Result<R::Output, Error>
where
R: SimpleRequest;
}
#[cfg(not(target_arch = "wasm32"))]
mod non_wasm {
use super::*;
use cosmrs::tendermint::abci::response::Info;
use std::fmt::Debug;
use tendermint_rpc::endpoint::abci_query::AbciQuery;
use tendermint_rpc::endpoint::block::Response;
#[async_trait]
impl<C> TendermintRpcClient for C
where
C: tendermint_rpc::client::Client + Sync,
{
async fn abci_info(&self) -> Result<Info, Error> {
self.abci_info().await
}
async fn abci_query<V>(
&self,
path: Option<String>,
data: V,
height: Option<Height>,
prove: bool,
) -> Result<AbciQuery, Error>
where
V: Into<Vec<u8>> + Send,
{
self.abci_query(path, data, height, prove).await
}
async fn block<H>(&self, height: H) -> Result<Response, Error>
where
H: Into<Height> + Send,
{
self.block(height).await
}
async fn block_by_hash(&self, hash: Hash) -> Result<block_by_hash::Response, Error> {
self.block_by_hash(hash).await
}
async fn latest_block(&self) -> Result<Response, Error> {
self.latest_block().await
}
async fn header<H>(&self, height: H) -> Result<header::Response, Error>
where
H: Into<Height> + Send,
{
self.header(height).await
}
async fn header_by_hash(&self, hash: Hash) -> Result<header_by_hash::Response, Error> {
self.header_by_hash(hash).await
}
async fn block_results<H>(&self, height: H) -> Result<block_results::Response, Error>
where
H: Into<Height> + Send,
{
self.block_results(height).await
}
async fn latest_block_results(&self) -> Result<block_results::Response, Error> {
self.latest_block_results().await
}
async fn block_search(
&self,
query: Query,
page: u32,
per_page: u8,
order: Order,
) -> Result<block_search::Response, Error> {
self.block_search(query, page, per_page, order).await
}
async fn blockchain<H>(&self, min: H, max: H) -> Result<blockchain::Response, Error>
where
H: Into<Height> + Send,
{
self.blockchain(min, max).await
}
async fn broadcast_tx_async<T>(&self, tx: T) -> Result<broadcast::tx_async::Response, Error>
where
T: Into<Vec<u8>> + Send,
{
self.broadcast_tx_async(tx).await
}
async fn broadcast_tx_sync<T>(&self, tx: T) -> Result<broadcast::tx_sync::Response, Error>
where
T: Into<Vec<u8>> + Send,
{
self.broadcast_tx_sync(tx).await
}
async fn broadcast_tx_commit<T>(
&self,
tx: T,
) -> Result<broadcast::tx_commit::Response, Error>
where
T: Into<Vec<u8>> + Send,
{
self.broadcast_tx_commit(tx).await
}
async fn commit<H>(&self, height: H) -> Result<commit::Response, Error>
where
H: Into<Height> + Send,
{
self.commit(height).await
}
async fn consensus_params<H>(&self, height: H) -> Result<consensus_params::Response, Error>
where
H: Into<Height> + Send,
{
self.consensus_params(height).await
}
async fn consensus_state(&self) -> Result<consensus_state::Response, Error> {
self.consensus_state().await
}
async fn validators<H>(
&self,
height: H,
paging: Paging,
) -> Result<validators::Response, Error>
where
H: Into<Height> + Send,
{
self.validators(height, paging).await
}
async fn latest_consensus_params(&self) -> Result<consensus_params::Response, Error> {
self.latest_consensus_params().await
}
async fn latest_commit(&self) -> Result<commit::Response, Error> {
self.latest_commit().await
}
async fn health(&self) -> Result<(), Error> {
self.health().await
}
async fn genesis<AppState>(&self) -> Result<Genesis<AppState>, Error>
where
AppState: Debug + Serialize + DeserializeOwned + Send,
{
self.genesis().await
}
async fn net_info(&self) -> Result<net_info::Response, Error> {
self.net_info().await
}
async fn status(&self) -> Result<status::Response, Error> {
self.status().await
}
async fn broadcast_evidence(&self, e: Evidence) -> Result<evidence::Response, Error> {
self.broadcast_evidence(e).await
}
async fn tx(&self, hash: Hash, prove: bool) -> Result<tx::Response, Error> {
self.tx(hash, prove).await
}
async fn tx_search(
&self,
query: Query,
prove: bool,
page: u32,
per_page: u8,
order: Order,
) -> Result<tx_search::Response, Error> {
self.tx_search(query, prove, page, per_page, order).await
}
#[cfg(any(
feature = "tendermint-rpc-http-client",
feature = "tendermint-rpc-websocket-client"
))]
async fn wait_until_healthy<T>(&self, timeout: T) -> Result<(), Error>
where
T: Into<core::time::Duration> + Send,
{
self.wait_until_healthy(timeout).await
}
async fn perform<R>(&self, request: R) -> Result<R::Output, Error>
where
R: SimpleRequest,
{
self.perform(request).await
}
}
}