Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bakers reward period #111

Merged
merged 11 commits into from
Aug 29, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## Unreleased changes
- Add a `commission_rates` field to `CurrentPaydayBakerPoolStatus` which yields the commission rates
of the baker for the reward period (requires a node with version at least 6.1).
- Add support for `GetBakersRewardPeriod` endpoint. Requires a node with version at least 6.1.

## 3.0.1

Expand Down
37 changes: 37 additions & 0 deletions examples/v2_get_bakers_reward_period.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//! Test the `GetBakersRewardPeriod` endpoint.
use anyhow::Context;
use clap::AppSettings;
use concordium_rust_sdk::v2;
use futures::StreamExt;
use structopt::StructOpt;

#[derive(StructOpt)]
struct App {
#[structopt(
long = "node",
help = "GRPC interface of the node.",
default_value = "http://localhost:20000"
)]
endpoint: v2::Endpoint,
}

#[tokio::main(flavor = "multi_thread")]
async fn main() -> anyhow::Result<()> {
let app = {
let app = App::clap().global_setting(AppSettings::ColoredHelp);
let matches = app.get_matches();
App::from_clap(&matches)
};

let mut client = v2::Client::new(app.endpoint)
.await
.context("Cannot connect.")?;
let mut res = client
.get_bakers_reward_period(&v2::BlockIdentifier::LastFinal)
.await?;
println!("Blockhash: {}", res.block_hash);
while let Some(a) = res.response.next().await {
println!("{:?}", a?);
}
Ok(())
}
26 changes: 26 additions & 0 deletions src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2241,6 +2241,32 @@ pub struct NodeInfo {
pub details: NodeDetails,
}

/// Information of a baker for a certain reward period.
#[derive(Debug)]
pub struct BakerRewardPeriodInfo {
/// Baker id and public keys.
pub baker: BakerInfo,
/// The stake of the baker that the
/// consensus protocol uses to determine lottery weight.
/// This is the stake after applying leverage bound and caps.
/// If the baker is also a finalizer then the effective stake is
/// also used to calculate the weight that the baker votes with as part of
/// the finalization committee.
pub effective_stake: Amount,
/// The effective commission rates for the baker that applies
/// in the reward period.
pub commission_rates: CommissionRates,
/// The amount that the baker staked itself in the
/// reward period.
pub equity_capital: Amount,
/// The amount that was delegated to the baker in the
/// reward period.
pub delegated_capital: Amount,
/// Whether the baker is part of the finalization committee
/// in the reward period.
pub is_finalizer: bool,
}

#[derive(Debug, SerdeDeserialize)]
#[serde(try_from = "wallet_account_json::VersionedWalletAccount")]
/// An account imported from one of the supported export formats.
Expand Down
15 changes: 15 additions & 0 deletions src/v2/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3135,6 +3135,21 @@ impl TryFrom<NextUpdateSequenceNumbers> for super::types::queries::NextUpdateSeq
}
}

impl TryFrom<BakerRewardPeriodInfo> for super::types::BakerRewardPeriodInfo {
type Error = tonic::Status;

fn try_from(message: BakerRewardPeriodInfo) -> Result<Self, Self::Error> {
Ok(Self {
baker: message.baker.require()?.try_into()?,
effective_stake: message.effective_stake.require()?.into(),
commission_rates: message.commission_rates.require()?.try_into()?,
equity_capital: message.equity_capital.require()?.into(),
delegated_capital: message.delegated_capital.require()?.into(),
is_finalizer: message.is_finalizer,
})
}
}

#[cfg(test)]
mod test {

Expand Down
152 changes: 152 additions & 0 deletions src/v2/generated/concordium.v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,39 @@ pub mod block_hash_input {
RelativeHeight(RelativeHeight),
}
}
/// Input to queries which take an epoch as a parameter.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EpochRequest {
#[prost(oneof = "epoch_request::EpochRequestInput", tags = "1, 2")]
pub epoch_request_input: ::core::option::Option<epoch_request::EpochRequestInput>,
}
/// Nested message and enum types in `EpochRequest`.
pub mod epoch_request {
/// Request an epoch by number at a given genesis index.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct RelativeEpoch {
/// The genesis index to query at. The query is restricted to this
/// genesis index, and will not return results for other indices
/// even if the epoch number is out of bounds.
#[prost(message, optional, tag = "1")]
pub genesis_index: ::core::option::Option<super::GenesisIndex>,
/// The epoch number to query at.
#[prost(message, optional, tag = "2")]
pub epoch: ::core::option::Option<super::Epoch>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum EpochRequestInput {
/// Query by genesis index and epoch number.
#[prost(message, tag = "1")]
RelativeEpoch(RelativeEpoch),
/// Query for the epoch of a specified block.
#[prost(message, tag = "2")]
BlockHash(super::BlockHashInput),
}
}
/// Input to queries which take an account as a parameter.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
Expand Down Expand Up @@ -4818,6 +4851,22 @@ pub struct BlockCertificates {
#[prost(message, optional, tag = "3")]
pub epoch_finalization_entry: ::core::option::Option<EpochFinalizationEntry>,
}
/// Details of which baker won the lottery in a given round in consensus version
/// 1.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct WinningBaker {
/// The round number.
#[prost(message, optional, tag = "1")]
pub round: ::core::option::Option<Round>,
/// The baker that won the round.
#[prost(message, optional, tag = "2")]
pub winner: ::core::option::Option<BakerId>,
/// True if the baker produced a block in this round on the finalized chain,
/// and False otherwise.
#[prost(bool, tag = "3")]
pub present: bool,
}
/// Information about how open the pool is to new delegators.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
Expand Down Expand Up @@ -5924,6 +5973,43 @@ pub mod queries_client {
self.inner.unary(request.into_request(), path, codec).await
}

/// Get the projected earliest time at which a particular baker will be
/// required to bake a block. If the current consensus version
/// is 0, this returns the status 'Unavailable', as the endpoint
/// is only supported by consensus version 1.
///
/// If the baker is not a baker for the current reward period, this
/// returns a timestamp at the start of the next reward period.
/// If the baker is a baker for the current reward period, the
/// earliest win time is projected from the current round forward,
/// assuming that each round after the last finalized round will
/// take the minimum block time. (If blocks take longer, or timeouts
/// occur, the actual time may be later, and the reported time in
/// subsequent queries may reflect this.) At the end of an epoch
/// (or if the baker is not projected to bake before the end of the
/// epoch) the earliest win time for a (current) baker will be projected
/// as the start of the next epoch. This is because the seed for
/// the leader election is updated at the epoch boundary, and so
/// the winners cannot be predicted beyond that. Note that in some
/// circumstances the returned timestamp can be in the past,
/// especially at the end of an epoch.
pub async fn get_baker_earliest_win_time(
&mut self,
request: impl tonic::IntoRequest<super::BakerId>,
) -> Result<tonic::Response<super::Timestamp>, tonic::Status> {
self.inner.ready().await.map_err(|e| {
tonic::Status::new(
tonic::Code::Unknown,
format!("Service was not ready: {}", e.into()),
)
})?;
let codec = tonic::codec::ProstCodec::default();
let path = http::uri::PathAndQuery::from_static(
"/concordium.v2.Queries/GetBakerEarliestWinTime",
);
self.inner.unary(request.into_request(), path, codec).await
}

/// Shut down the node.
/// Return a GRPC error if the shutdown failed.
pub async fn shutdown(
Expand Down Expand Up @@ -6262,5 +6348,71 @@ pub mod queries_client {
http::uri::PathAndQuery::from_static("/concordium.v2.Queries/GetBlockCertificates");
self.inner.unary(request.into_request(), path, codec).await
}

/// Get the list of bakers that won the lottery in a particular
/// historical epoch (i.e. the last finalized block is in a
/// later epoch). This lists the winners for each round in the
/// epoch, starting from the round after the last block in the previous
/// epoch, running to the round before the first block in the
/// next epoch. It also indicates if a block in each
/// round was included in the finalized chain.
///
/// The following error cases are possible:
/// * `NOT_FOUND` if the query specifies an unknown block.
/// * `UNAVAILABLE` if the query is for an epoch that is not finalized
/// in the current genesis
/// / index, or is for a future genesis index.
/// * `INVALID_ARGUMENT` if the query is for an epoch that is not
/// finalized for a past genesis index.
/// * `INVALID_ARGUMENT` if the query is for a genesis index at
/// consensus version 0.
/// * `INVALID_ARGUMENT` if the input `EpochRequest` is malformed.
/// * `UNAVAILABLE` if the endpoint is disabled on the node.
pub async fn get_winning_bakers_epoch(
&mut self,
request: impl tonic::IntoRequest<super::EpochRequest>,
) -> Result<tonic::Response<tonic::codec::Streaming<super::WinningBaker>>, tonic::Status>
{
self.inner.ready().await.map_err(|e| {
tonic::Status::new(
tonic::Code::Unknown,
format!("Service was not ready: {}", e.into()),
)
})?;
let codec = tonic::codec::ProstCodec::default();
let path = http::uri::PathAndQuery::from_static(
"/concordium.v2.Queries/GetWinningBakersEpoch",
);
self.inner
.server_streaming(request.into_request(), path, codec)
.await
}

/// Get the block hash of the first finalized block in a specified
/// epoch.
///
/// The following error cases are possible:
/// * `NOT_FOUND` if the query specifies an unknown block.
/// * `UNAVAILABLE` if the query is for an epoch that is not finalized
/// in the current genesis index, or is for a future genesis index.
/// * `INVALID_ARGUMENT` if the query is for an epoch with no finalized
/// blocks for a past genesis index.
/// * `INVALID_ARGUMENT` if the input `EpochRequest` is malformed.
/// * `UNAVAILABLE` if the endpoint is disabled on the node.
pub async fn get_first_block_epoch(
&mut self,
request: impl tonic::IntoRequest<super::EpochRequest>,
) -> Result<tonic::Response<super::BlockHash>, tonic::Status> {
self.inner.ready().await.map_err(|e| {
tonic::Status::new(
tonic::Code::Unknown,
format!("Service was not ready: {}", e.into()),
)
})?;
let codec = tonic::codec::ProstCodec::default();
let path =
http::uri::PathAndQuery::from_static("/concordium.v2.Queries/GetFirstBlockEpoch");
self.inner.unary(request.into_request(), path, codec).await
}
}
}
25 changes: 25 additions & 0 deletions src/v2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2443,6 +2443,31 @@ impl Client {
}
last_found.ok_or(QueryError::NotFound)
}

/// Get all bakers in the reward period of a block.
/// This endpoint is only supported for protocol version 6 and onwards.
/// If the protocol does not support the endpoint then an
/// [`IllegalArgument`](tonic::Code::InvalidArgument) is returned.
pub async fn get_bakers_reward_period(
&mut self,
bi: impl IntoBlockIdentifier,
) -> endpoints::QueryResult<
QueryResponse<impl Stream<Item = Result<types::BakerRewardPeriodInfo, tonic::Status>>>,
> {
let response = self
.client
.get_bakers_reward_period(&bi.into_block_identifier())
.await?;
let block_hash = extract_metadata(&response)?;
let stream = response.into_inner().map(|result| match result {
Ok(baker) => baker.try_into(),
Err(err) => Err(err),
});
Ok(QueryResponse {
block_hash,
response: stream,
})
}
}

/// A stream of finalized blocks. This contains a background task that polls
Expand Down
2 changes: 1 addition & 1 deletion src/v2/proto_schema_version.rs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
pub const PROTO_SCHEMA_VERSION: &str = "e0712c295058bc3692fa3f32dde456f36c37cd90";
pub const PROTO_SCHEMA_VERSION: &str = "c079fde30b8b39d475f6dd8c049a9357bef0ab3c";
Loading