Skip to content

Commit

Permalink
Implement the reward endpoint (#321)
Browse files Browse the repository at this point in the history
* Implement a reward endpoint

Signed-off-by: Jacek Malec <[email protected]>

* Map new errors

* Error code update

* Update error handling

* Make errors more consistent

---------

Signed-off-by: Jacek Malec <[email protected]>
  • Loading branch information
jacek-casper authored Jun 7, 2024
1 parent b880b85 commit 8f8a786
Show file tree
Hide file tree
Showing 9 changed files with 357 additions and 16 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ members = [
anyhow = "1"
async-stream = "0.3.4"
async-trait = "0.1.77"
casper-types = { git = "https://github.com/casper-network/casper-node.git", branch = "feat-2.0" }
casper-binary-port = { git = "https://github.com/casper-network/casper-node.git", branch = "feat-2.0" }
casper-types = { git = "https://github.com/jacek-casper/casper-node.git", branch = "reward-binary-request" }
casper-binary-port = { git = "https://github.com/jacek-casper/casper-node.git", branch = "reward-binary-request" }
casper-event-sidecar = { path = "./event_sidecar", version = "1.0.0" }
casper-event-types = { path = "./types", version = "1.0.0" }
casper-rpc-sidecar = { path = "./rpc_sidecar", version = "1.0.0" }
Expand Down
129 changes: 129 additions & 0 deletions resources/test/rpc_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1513,6 +1513,106 @@
}
]
},
{
"name": "info_get_reward",
"summary": "returns the reward for a given era and a validator or a delegator",
"params": [
{
"name": "validator",
"schema": {
"description": "The public key of the validator.",
"$ref": "#/components/schemas/PublicKey"
},
"required": true
},
{
"name": "era_identifier",
"schema": {
"description": "The era identifier. If `None`, the last finalized era is used.",
"anyOf": [
{
"$ref": "#/components/schemas/EraIdentifier"
},
{
"type": "null"
}
]
},
"required": false
},
{
"name": "delegator",
"schema": {
"description": "The public key of the delegator. If `Some`, the rewards for the delegator are returned. If `None`, the rewards for the validator are returned.",
"anyOf": [
{
"$ref": "#/components/schemas/PublicKey"
},
{
"type": "null"
}
]
},
"required": false
}
],
"result": {
"name": "info_get_reward_result",
"schema": {
"description": "Result for \"info_get_reward\" RPC response.",
"type": "object",
"required": [
"api_version",
"era_id",
"reward_amount"
],
"properties": {
"api_version": {
"description": "The RPC API version.",
"type": "string"
},
"reward_amount": {
"description": "The total reward amount in the requested era.",
"$ref": "#/components/schemas/U512"
},
"era_id": {
"description": "The era for which the reward was calculated.",
"$ref": "#/components/schemas/EraId"
}
},
"additionalProperties": false
}
},
"examples": [
{
"name": "info_get_reward_example",
"params": [
{
"name": "era_identifier",
"value": {
"Era": 1
}
},
{
"name": "validator",
"value": "01d9bf2148748a85c89da5aad8ee0b0fc2d105fd39d41a4c796536354f0ae2900c"
},
{
"name": "delegator",
"value": "01d9bf2148748a85c89da5aad8ee0b0fc2d105fd39d41a4c796536354f0ae2900c"
}
],
"result": {
"name": "info_get_reward_example_result",
"value": {
"api_version": "2.0.0",
"reward_amount": "42",
"era_id": 1
}
}
}
]
},
{
"name": "info_get_validator_changes",
"summary": "returns status changes of active validators",
Expand Down Expand Up @@ -7784,6 +7884,35 @@
},
"additionalProperties": false
},
"EraIdentifier": {
"description": "Identifier for an era.",
"oneOf": [
{
"type": "object",
"required": [
"Era"
],
"properties": {
"Era": {
"$ref": "#/components/schemas/EraId"
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"Block"
],
"properties": {
"Block": {
"$ref": "#/components/schemas/BlockIdentifier"
}
},
"additionalProperties": false
}
]
},
"JsonValidatorChanges": {
"description": "The changes in a validator's status.",
"type": "object",
Expand Down
3 changes: 2 additions & 1 deletion rpc_sidecar/src/http_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use casper_json_rpc::{CorsOrigin, RequestHandlersBuilder};

use crate::{
rpcs::{
info::{GetPeers, GetStatus, GetTransaction},
info::{GetPeers, GetReward, GetStatus, GetTransaction},
state::{GetAddressableEntity, QueryBalanceDetails},
},
NodeClient,
Expand Down Expand Up @@ -54,6 +54,7 @@ pub async fn run(
GetTransaction::register_as_handler(node.clone(), &mut handlers);
GetPeers::register_as_handler(node.clone(), &mut handlers);
GetStatus::register_as_handler(node.clone(), &mut handlers);
GetReward::register_as_handler(node.clone(), &mut handlers);
GetEraInfoBySwitchBlock::register_as_handler(node.clone(), &mut handlers);
GetEraSummary::register_as_handler(node.clone(), &mut handlers);
GetAuctionInfo::register_as_handler(node.clone(), &mut handlers);
Expand Down
38 changes: 33 additions & 5 deletions rpc_sidecar/src/node_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,16 @@ use tokio_util::codec::Framed;
use casper_binary_port::{
BalanceResponse, BinaryMessage, BinaryMessageCodec, BinaryRequest, BinaryRequestHeader,
BinaryResponse, BinaryResponseAndRequest, ConsensusValidatorChanges, DictionaryItemIdentifier,
DictionaryQueryResult, ErrorCode, GetRequest, GetTrieFullResult, GlobalStateQueryResult,
GlobalStateRequest, InformationRequest, KeyPrefix, NodeStatus, PayloadEntity, PurseIdentifier,
RecordId, SpeculativeExecutionResult, TransactionWithExecutionInfo,
DictionaryQueryResult, EraIdentifier, ErrorCode, GetRequest, GetTrieFullResult,
GlobalStateQueryResult, GlobalStateRequest, InformationRequest, KeyPrefix, NodeStatus,
PayloadEntity, PurseIdentifier, RecordId, RewardResponse, SpeculativeExecutionResult,
TransactionWithExecutionInfo,
};
use casper_types::{
bytesrepr::{self, FromBytes, ToBytes},
AvailableBlockRange, BlockHash, BlockHeader, BlockIdentifier, ChainspecRawBytes, Digest,
GlobalStateIdentifier, Key, KeyTag, Peers, ProtocolVersion, SignedBlock, StoredValue,
Transaction, TransactionHash, Transfer,
GlobalStateIdentifier, Key, KeyTag, Peers, ProtocolVersion, PublicKey, SignedBlock,
StoredValue, Transaction, TransactionHash, Transfer,
};
use std::{
fmt::{self, Display, Formatter},
Expand Down Expand Up @@ -238,6 +239,24 @@ pub trait NodeClient: Send + Sync {
let resp = self.read_info(InformationRequest::NodeStatus).await?;
parse_response::<NodeStatus>(&resp.into())?.ok_or(Error::EmptyEnvelope)
}

async fn read_reward(
&self,
era_identifier: Option<EraIdentifier>,
validator: PublicKey,
delegator: Option<PublicKey>,
) -> Result<Option<RewardResponse>, Error> {
let validator = validator.into();
let delegator = delegator.map(Into::into);
let resp = self
.read_info(InformationRequest::Reward {
era_identifier,
validator,
delegator,
})
.await?;
parse_response::<RewardResponse>(&resp.into())
}
}

#[derive(Debug, thiserror::Error, PartialEq, Eq)]
Expand Down Expand Up @@ -497,6 +516,12 @@ pub enum Error {
InvalidTransaction(InvalidTransactionOrDeploy),
#[error("speculative execution has failed: {0}")]
SpecExecutionFailed(String),
#[error("the switch block for the requested era was not found")]
SwitchBlockNotFound,
#[error("the parent of the switch block for the requested era was not found")]
SwitchBlockParentNotFound,
#[error("cannot serve rewards stored in V1 format")]
UnsupportedRewardsV1Request,
#[error("received a response with an unsupported protocol version: {0}")]
UnsupportedProtocolVersion(ProtocolVersion),
#[error("received an unexpected node error: {message} ({code})")]
Expand All @@ -509,6 +534,9 @@ impl Error {
Ok(ErrorCode::FunctionDisabled) => Self::FunctionIsDisabled,
Ok(ErrorCode::RootNotFound) => Self::UnknownStateRootHash,
Ok(ErrorCode::FailedQuery) => Self::QueryFailedToExecute,
Ok(ErrorCode::SwitchBlockNotFound) => Self::SwitchBlockNotFound,
Ok(ErrorCode::SwitchBlockParentNotFound) => Self::SwitchBlockParentNotFound,
Ok(ErrorCode::UnsupportedRewardsV1Request) => Self::UnsupportedRewardsV1Request,
Ok(
err @ (ErrorCode::InvalidDeployChainName
| ErrorCode::InvalidDeployDependenciesNoLongerSupported
Expand Down
8 changes: 7 additions & 1 deletion rpc_sidecar/src/rpcs/docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ use super::{
chain::{
GetBlock, GetBlockTransfers, GetEraInfoBySwitchBlock, GetEraSummary, GetStateRootHash,
},
info::{GetChainspec, GetDeploy, GetPeers, GetStatus, GetTransaction, GetValidatorChanges},
info::{
GetChainspec, GetDeploy, GetPeers, GetReward, GetStatus, GetTransaction,
GetValidatorChanges,
},
state::{
GetAccountInfo, GetAddressableEntity, GetAuctionInfo, GetBalance, GetDictionaryItem,
GetItem, QueryBalance, QueryBalanceDetails, QueryGlobalState,
Expand Down Expand Up @@ -86,6 +89,9 @@ pub(crate) static OPEN_RPC_SCHEMA: Lazy<OpenRpcSchema> = Lazy::new(|| {
);
schema.push_without_params::<GetPeers>("returns a list of peers connected to the node");
schema.push_without_params::<GetStatus>("returns the current status of the node");
schema.push_with_params::<GetReward>(
"returns the reward for a given era and a validator or a delegator",
);
schema
.push_without_params::<GetValidatorChanges>("returns status changes of active validators");
schema.push_without_params::<GetChainspec>(
Expand Down
12 changes: 12 additions & 0 deletions rpc_sidecar/src/rpcs/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ pub enum Error {
AccountNotFound,
#[error("the requested addressable entity was not found")]
AddressableEntityNotFound,
#[error("the requested reward was not found")]
RewardNotFound,
#[error("the requested account has been migrated to an addressable entity")]
AccountMigratedToEntity,
#[error("the provided dictionary value is {0} instead of a URef")]
Expand Down Expand Up @@ -82,11 +84,21 @@ impl Error {
Error::NodeRequest(_, NodeClientError::FunctionIsDisabled) => {
Some(ErrorCode::FunctionIsDisabled)
}
Error::NodeRequest(_, NodeClientError::SwitchBlockNotFound) => {
Some(ErrorCode::SwitchBlockNotFound)
}
Error::NodeRequest(_, NodeClientError::SwitchBlockParentNotFound) => {
Some(ErrorCode::SwitchBlockParentNotFound)
}
Error::NodeRequest(_, NodeClientError::UnsupportedRewardsV1Request) => {
Some(ErrorCode::UnsupportedRewardsV1Request)
}
Error::InvalidPurseURef(_) => Some(ErrorCode::FailedToParseGetBalanceURef),
Error::InvalidDictionaryKey(_) => Some(ErrorCode::FailedToParseQueryKey),
Error::MainPurseNotFound => Some(ErrorCode::NoSuchMainPurse),
Error::AccountNotFound => Some(ErrorCode::NoSuchAccount),
Error::AddressableEntityNotFound => Some(ErrorCode::NoSuchAddressableEntity),
Error::RewardNotFound => Some(ErrorCode::NoRewardsFound),
Error::AccountMigratedToEntity => Some(ErrorCode::AccountMigratedToEntity),
Error::InvalidTypeUnderDictionaryKey(_)
| Error::DictionaryKeyNotFound
Expand Down
17 changes: 17 additions & 0 deletions rpc_sidecar/src/rpcs/error_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ pub enum ErrorCode {
NoSuchAddressableEntity = -32020,
/// The requested account has been migrated to an addressable entity.
AccountMigratedToEntity = -32021,
/// The requested reward was not found.
NoRewardsFound = -32022,
/// The switch block for the requested era was not found.
SwitchBlockNotFound = -32023,
/// The parent of the switch block for the requested era was not found.
SwitchBlockParentNotFound = -32024,
/// Cannot serve rewards stored in V1 format
UnsupportedRewardsV1Request = -32025,
}

impl From<ErrorCode> for (i64, &'static str) {
Expand Down Expand Up @@ -92,6 +100,15 @@ impl From<ErrorCode> for (i64, &'static str) {
error_code as i64,
"Account migrated to an addressable entity",
),
ErrorCode::NoRewardsFound => (error_code as i64, "No rewards found"),
ErrorCode::SwitchBlockNotFound => (error_code as i64, "Switch block not found"),
ErrorCode::SwitchBlockParentNotFound => {
(error_code as i64, "Switch block parent not found")
}
ErrorCode::UnsupportedRewardsV1Request => (
error_code as i64,
"Cannot serve rewards stored in V1 format",
),
}
}
}
Expand Down
Loading

0 comments on commit 8f8a786

Please sign in to comment.