Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/src/rpc_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub struct RpcLargestAccountsConfig {

#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcInflationConfig {
pub struct RpcStakeConfig {
pub epoch: Option<Epoch>,
#[serde(flatten)]
pub commitment: Option<CommitmentConfig>,
Expand Down
17 changes: 17 additions & 0 deletions client/src/rpc_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,20 @@ pub struct RpcSupply {
pub non_circulating: u64,
pub non_circulating_accounts: Vec<String>,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub enum StakeActivationState {
Activating,
Active,
Deactivating,
Inactive,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct RpcStakeActivation {
pub state: StakeActivationState,
pub active: u64,
pub inactive: u64,
}
87 changes: 87 additions & 0 deletions core/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,20 @@ use solana_runtime::{
log_collector::LogCollector,
};
use solana_sdk::{
account_utils::StateMut,
clock::{Slot, UnixTimestamp},
commitment_config::{CommitmentConfig, CommitmentLevel},
epoch_info::EpochInfo,
epoch_schedule::EpochSchedule,
hash::Hash,
pubkey::Pubkey,
signature::Signature,
stake_history::StakeHistory,
sysvar::{stake_history, Sysvar},
timing::slot_duration_from_slots_per_year,
transaction::{self, Transaction},
};
use solana_stake_program::stake_state::StakeState;
use solana_transaction_status::{
ConfirmedBlock, ConfirmedTransaction, TransactionStatus, UiTransactionEncoding,
};
Expand Down Expand Up @@ -755,6 +759,67 @@ impl JsonRpcRequestProcessor {
.get_first_available_block()
.unwrap_or_default()
}

pub fn get_stake_activation(
&self,
pubkey: &Pubkey,
config: Option<RpcStakeConfig>,
) -> Result<RpcStakeActivation> {
let config = config.unwrap_or_default();
let bank = self.bank(config.commitment);
let epoch = config.epoch.unwrap_or_else(|| bank.epoch());
if bank.epoch().saturating_sub(epoch) > solana_sdk::stake_history::MAX_ENTRIES as u64 {
return Err(Error::invalid_params(format!(
"Invalid param: epoch {:?} is too far in the past",
epoch
)));
}
if epoch > bank.epoch() {
return Err(Error::invalid_params(format!(
"Invalid param: epoch {:?} has not yet started",
epoch
)));
}

let stake_account = bank
.get_account(pubkey)
.ok_or_else(|| Error::invalid_params("Invalid param: account not found".to_string()))?;
let stake_state: StakeState = stake_account
.state()
.map_err(|_| Error::invalid_params("Invalid param: not a stake account".to_string()))?;
let delegation = stake_state.delegation().ok_or_else(|| {
Error::invalid_params("Invalid param: stake account has not been delegated".to_string())
})?;

let stake_history_account = bank
.get_account(&stake_history::id())
.ok_or_else(Error::internal_error)?;
let stake_history =
StakeHistory::from_account(&stake_history_account).ok_or_else(Error::internal_error)?;

let (active, activating, deactivating) =
delegation.stake_activating_and_deactivating(epoch, Some(&stake_history));
let stake_activation_state = if deactivating > 0 {
StakeActivationState::Deactivating
} else if activating > 0 {
StakeActivationState::Activating
} else if active > 0 {
StakeActivationState::Active
} else {
StakeActivationState::Inactive
};
let inactive_stake = match stake_activation_state {
StakeActivationState::Activating => activating,
StakeActivationState::Active => 0,
StakeActivationState::Deactivating => delegation.stake.saturating_sub(active),
StakeActivationState::Inactive => delegation.stake,
};
Ok(RpcStakeActivation {
state: stake_activation_state,
active,
inactive: inactive_stake,
})
}
}

fn verify_filter(input: &RpcFilterType) -> Result<()> {
Expand Down Expand Up @@ -1062,6 +1127,14 @@ pub trait RpcSol {

#[rpc(meta, name = "getFirstAvailableBlock")]
fn get_first_available_block(&self, meta: Self::Metadata) -> Result<Slot>;

#[rpc(meta, name = "getStakeActivation")]
fn get_stake_activation(
&self,
meta: Self::Metadata,
pubkey_str: String,
config: Option<RpcStakeConfig>,
) -> Result<RpcStakeActivation>;
}

pub struct RpcSolImpl;
Expand Down Expand Up @@ -1589,6 +1662,20 @@ impl RpcSol for RpcSolImpl {
fn get_first_available_block(&self, meta: Self::Metadata) -> Result<Slot> {
Ok(meta.get_first_available_block())
}

fn get_stake_activation(
&self,
meta: Self::Metadata,
pubkey_str: String,
config: Option<RpcStakeConfig>,
) -> Result<RpcStakeActivation> {
debug!(
"get_stake_activation rpc request received: {:?}",
pubkey_str
);
let pubkey = verify_pubkey(pubkey_str)?;
meta.get_stake_activation(&pubkey, config)
}
}

fn deserialize_bs58_transaction(bs58_transaction: String) -> Result<(Vec<u8>, Transaction)> {
Expand Down
36 changes: 36 additions & 0 deletions docs/src/apps/jsonrpc-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ To interact with a Solana node inside a JavaScript application, use the [solana-
* [getSignatureStatuses](jsonrpc-api.md#getsignaturestatuses)
* [getSlot](jsonrpc-api.md#getslot)
* [getSlotLeader](jsonrpc-api.md#getslotleader)
* [getStakeActivation](jsonrpc-api.md#getstakeactivation)
* [getSupply](jsonrpc-api.md#getsupply)
* [getTransactionCount](jsonrpc-api.md#gettransactioncount)
* [getVersion](jsonrpc-api.md#getversion)
Expand Down Expand Up @@ -943,6 +944,41 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "m
{"jsonrpc":"2.0","result":"ENvAW7JScgYq6o4zKZwewtkzzJgDzuJAFxYasvmEQdpS","id":1}
```

### getStakeActivation

Returns epoch activation information for a stake account

#### Parameters:

* `<string>` - Pubkey of stake account to query, as base-58 encoded string
* `<object>` - (optional) Configuration object containing the following optional fields:
* (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
* (optional) `epoch: <u64>` - epoch for which to calculate activation details. If parameter not provided, defaults to current epoch.

#### Results:

The result will be a JSON object with the following fields:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment comes after my two "activating"/"deactivating" nits. I think a overall "state" field would be very useful. Right now the user needs to infer this based on the three fields.

What about something like:

  • state: <string> - the current state of the stake account: inactive, activating, active, deactivating (I don't love these state names but they're a start)
  • active: <u64> - amount of stake that is currently active
  • inactive: <u64> - amount of stake that is not active (eg, for a state account in the activating state, this will be the remaining stake that needs to warm up)

Copy link
Copy Markdown
Contributor Author

@CriesofCarrots CriesofCarrots Jul 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. I suppose we could go to "warming up" and "cooling down" for state instead of activating/deactivating. Do those appeal any better?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mvines , updated. I stuck with activating/deactivating for now. Thoughts?


* `state: <string` - the stake account's activation state, one of: `active`, `inactive`, `activating`, `deactivating`
* `active: <u64>` - stake active during the epoch
* `inactive: <u64>` - stake inactive during the epoch

#### Example:

```bash
// Request
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getStakeActivation", "params": ["CYRJWqiSjLitBAcRxPvWpgX3s5TvmN2SuRY3eEYypFvT"]}' http://localhost:8899

// Result
{"jsonrpc":"2.0","result":{"active":197717120,"inactive":0,"state":"active"},"id":1}

// Request with Epoch
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getStakeActivation", "params": ["CYRJWqiSjLitBAcRxPvWpgX3s5TvmN2SuRY3eEYypFvT", {"epoch": 4}]}' http://localhost:8899

// Result
{"jsonrpc":"2.0","result":{"active":124429280,"inactive":73287840,"state":"activating"},"id":1}
```

### getSupply

Returns information about the current supply.
Expand Down