Skip to content

Commit

Permalink
Add get_snapshot_info() RPC function
Browse files Browse the repository at this point in the history
  • Loading branch information
brooksprumo committed Sep 26, 2021
1 parent 6f08f9d commit 20ef9e5
Show file tree
Hide file tree
Showing 7 changed files with 249 additions and 12 deletions.
26 changes: 15 additions & 11 deletions client/src/mock_sender.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,15 @@
use {
crate::{
client_error::Result,
rpc_config::RpcBlockProductionConfig,
rpc_request::RpcRequest,
rpc_response::{
Response, RpcAccountBalance, RpcBlockProduction, RpcBlockProductionRange, RpcBlockhash,
RpcConfirmedTransactionStatusWithSignature, RpcContactInfo, RpcFees, RpcPerfSample,
RpcResponseContext, RpcSimulateTransactionResult, RpcSnapshotSlotInfo,
RpcStakeActivation, RpcSupply, RpcVersionInfo, RpcVoteAccountInfo,
RpcVoteAccountStatus, StakeActivationState,
},
rpc_sender::*,
client_error::Result, rpc_config::RpcBlockProductionConfig, rpc_request::RpcRequest,
rpc_response::*, rpc_sender::*,
},
serde_json::{json, Number, Value},
solana_sdk::{
clock::{Slot, UnixTimestamp},
epoch_info::EpochInfo,
fee_calculator::{FeeCalculator, FeeRateGovernor},
hash::Hash,
instruction::InstructionError,
message::MessageHeader,
signature::Signature,
Expand Down Expand Up @@ -231,6 +223,18 @@ impl RpcSender for MockSender {
full: 100,
incremental: Some(110),
}),
"getSnapshotInfo" => {
json!(RpcSnapshotInfo::IncrementalSnapshot {
full: SnapshotInfo {
slot: 8_000,
hash: Hash::new_unique().to_string(),
},
incremental: SnapshotInfo {
slot: 8_200,
hash: Hash::new_unique().to_string(),
},
})
},
"getBlockHeight" => Value::Number(Number::from(1234)),
"getSlotLeaders" => json!([PUBKEY]),
"getBlockProduction" => {
Expand Down
32 changes: 32 additions & 0 deletions client/src/rpc_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1061,6 +1061,38 @@ impl RpcClient {
self.send(RpcRequest::GetSnapshotSlot, Value::Null)
}

/// Returns the snapshot information for the given snapshot slot and hash
///
/// # RPC Reference
///
/// This method corresponds directly to the [`getSnapshotInfo`] RPC method.
///
/// [`getSnapshotInfo`]: https://docs.solana.com/developing/clients/jsonrpc-api#getsnapshotinfo
///
/// # Examples
///
/// ```
/// # use solana_client::{
/// # rpc_client::RpcClient,
/// # client_error::ClientError,
/// # };
/// # use solana_sdk::clock::Slot;
/// # use solana_sdk::hash::Hash;
/// # let rpc_client = RpcClient::new_mock("succeeds".to_string());
/// # let snapshot_slot_hash = (Slot::default(), Hash::default());
/// let snapshot_info = rpc_client.get_snapshot_info(snapshot_slot_hash)?;
/// # Ok::<(), ClientError>(())
/// ```
pub fn get_snapshot_info(
&self,
snapshot_slot_hash: (Slot, Hash),
) -> ClientResult<RpcSnapshotInfo> {
self.send(
RpcRequest::GetSnapshotInfo,
json!([(snapshot_slot_hash.0, snapshot_slot_hash.1.to_string())]),
)
}

/// Check if a transaction has been processed with the default commitment level.
///
/// If the transaction has been processed with the default commitment level,
Expand Down
2 changes: 2 additions & 0 deletions client/src/rpc_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ pub enum RpcRequest {
note = "Please use RpcRequest::GetHighestSnapshotSlot instead"
)]
GetSnapshotSlot,
GetSnapshotInfo,
GetSignaturesForAddress,
GetSignatureStatuses,
GetSlot,
Expand Down Expand Up @@ -158,6 +159,7 @@ impl fmt::Display for RpcRequest {
RpcRequest::GetRecentPerformanceSamples => "getRecentPerformanceSamples",
RpcRequest::GetHighestSnapshotSlot => "getHighestSnapshotSlot",
RpcRequest::GetSnapshotSlot => "getSnapshotSlot",
RpcRequest::GetSnapshotInfo => "getSnapshotInfo",
RpcRequest::GetSignaturesForAddress => "getSignaturesForAddress",
RpcRequest::GetSignatureStatuses => "getSignatureStatuses",
RpcRequest::GetSlot => "getSlot",
Expand Down
18 changes: 18 additions & 0 deletions client/src/rpc_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,25 @@ impl From<ConfirmedTransactionStatusWithSignature> for RpcConfirmedTransactionSt
}

#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct RpcSnapshotSlotInfo {
pub full: Slot,
pub incremental: Option<Slot>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum RpcSnapshotInfo {
FullSnapshot(SnapshotInfo),
IncrementalSnapshot {
full: SnapshotInfo,
incremental: SnapshotInfo,
},
}

#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct SnapshotInfo {
pub slot: Slot,
pub hash: String,
}
91 changes: 91 additions & 0 deletions docs/src/developing/clients/jsonrpc-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ gives a convenient interface for the RPC methods.
- [getSlot](jsonrpc-api.md#getslot)
- [getSlotLeader](jsonrpc-api.md#getslotleader)
- [getSlotLeaders](jsonrpc-api.md#getslotleaders)
- [getSnapshotInfo](jsonrpc-api.md#getsnapshotinfo)
- [getStakeActivation](jsonrpc-api.md#getstakeactivation)
- [getSupply](jsonrpc-api.md#getsupply)
- [getTokenAccountBalance](jsonrpc-api.md#gettokenaccountbalance)
Expand Down Expand Up @@ -2444,6 +2445,96 @@ The first leader returned is the leader for slot #100:
}
```

### getSnapshotInfo

**NEW: This method is only available in solana-core v1.8 or newer.**

Returns the snapshot information for the given snapshot slot and hash.

If the node has a snapshot with the given slot and hash, it will return the
associated information about it. For full snapshots, this is the slot and has.
For incremental snapshots, this is the slot and hash _plus_ the slot and hash
for its associated full snapshot.

#### Parameters:

- `<object>`
- `slot: <u64>` - The slot for the snapshot being queried
- `hash: <string>` - A base-58 encoded hash for the snapshot being queried

#### Results:

Result when the node has a full snapshot for this slot and hash:
- `<object>`
- `fullSnapshot: <object>` - This object indicates the result is for a full snapshot
- `slot: <u64>` - The slot of the snapshot
- `hash: <string>` - The base-58 encoded hash of the snapshot

Result when the node has an incremental snapshot for this slot and hash:
- `<object>`
- `incrementalSnapshot: <object>` - This object indicates the result is for an incremental snapshot
- `full: <object>` - The information for the associated full snapshot
- `slot: <u64>` - The slot of the full snapshot
- `hash: <string>` - The base-58 encoded hash of the full snapshot
- `incremental: <object>` - The information for the incremental snapshot
- `slot: <u64>` - The slot of the incremental snapshot
- `hash: <string>` - The base-58 encoded hash of the incremental snapshot

#### Example:

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

Result when the node has a full snapshot for this slot and hash:
```json
{
"jsonrpc": "2.0",
"result": {
"fullSnapshot": {
"slot": 123,
"hash": "CiDwVBFgWV9E5MvXWoLgnEgn2hK7rJikbvfWavzAQz3"
}
},
"id": 1
}
```

Result when the node has an incremental snapshot for this slot and hash:
```json
{
"jsonrpc": "2.0",
"result": {
"incrementalSnapshot": {
"full": {
"slot": 8000,
"hash": "4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM"
},
"incremental": {
"slot": 8200,
"hash": "8opHzTAnfzRpPEx21XtnrVTX28YQuCpAjcn1PczScKh"
}
}
},
"id": 1
}
```

Result when the node does not have a snapshot for this slot and hash:
```json
{
"jsonrpc": "2.0",
"error": {
"code": -32008,
"message": "No snapshot"
},
"id": 1
}
```

### getStakeActivation

Returns epoch activation information for a stake account
Expand Down
90 changes: 90 additions & 0 deletions rpc/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ use {
commitment::{BlockCommitmentArray, BlockCommitmentCache, CommitmentSlots},
inline_spl_token_v2_0::{SPL_TOKEN_ACCOUNT_MINT_OFFSET, SPL_TOKEN_ACCOUNT_OWNER_OFFSET},
non_circulating_supply::calculate_non_circulating_supply,
snapshot_archive_info::SnapshotArchiveInfoGetter,
snapshot_config::SnapshotConfig,
snapshot_utils,
},
Expand Down Expand Up @@ -2275,6 +2276,13 @@ pub mod rpc_minimal {
#[rpc(meta, name = "getHighestSnapshotSlot")]
fn get_highest_snapshot_slot(&self, meta: Self::Metadata) -> Result<RpcSnapshotSlotInfo>;

#[rpc(meta, name = "getSnapshotInfo")]
fn get_snapshot_info(
&self,
meta: Self::Metadata,
snapshot_slot_hash: (Slot, String),
) -> Result<RpcSnapshotInfo>;

#[rpc(meta, name = "getTransactionCount")]
fn get_transaction_count(
&self,
Expand Down Expand Up @@ -2396,6 +2404,88 @@ pub mod rpc_minimal {
})
}

fn get_snapshot_info(
&self,
meta: Self::Metadata,
snapshot_slot_hash: (Slot, String),
) -> Result<RpcSnapshotInfo> {
debug!("get_snapshot_info rpc request received");

if meta.snapshot_config.is_none() {
return Err(RpcCustomError::NoSnapshot.into());
}

let snapshot_slot_hash = (
snapshot_slot_hash.0,
Hash::from_str(&snapshot_slot_hash.1).map_err(|_| {
Error::invalid_params("Invalid hash; must be a base-58 encoded string")
})?,
);

let snapshot_archives_dir = meta
.snapshot_config
.map(|snapshot_config| snapshot_config.snapshot_archives_dir)
.unwrap();

let incremental_snapshot_archives =
snapshot_utils::get_incremental_snapshot_archives(&snapshot_archives_dir);
let incremental_snapshot_archive =
incremental_snapshot_archives
.iter()
.find(|snapshot_archive| {
snapshot_archive.slot() == snapshot_slot_hash.0
&& snapshot_archive.hash() == &snapshot_slot_hash.1
});

// If the snapshot_slot_hash passed in matched an incremental snapshot (above), then
// find its matching full snapshot. Otherwise, see if the snapshot_slot_hash matches a
// full snapshot.
//
// NOTE: Since IncrementalSnapshotArchiveInfo doesn't contain the full snapshot's hash
// (just the slot), we need two different calls to `.find()` to determine a match.

let full_snapshot_archives =
snapshot_utils::get_full_snapshot_archives(&snapshot_archives_dir);
let full_snapshot_archive = match incremental_snapshot_archive {
Some(incremental_snapshot_archive) => {
full_snapshot_archives.iter().find(|full_snapshot_archive| {
full_snapshot_archive.slot() == incremental_snapshot_archive.base_slot()
})
}
None => full_snapshot_archives.iter().find(|snapshot_archive| {
snapshot_archive.slot() == snapshot_slot_hash.0
&& snapshot_archive.hash() == &snapshot_slot_hash.1
}),
};

// NOTE: If somehow an incremental snapshot was found but *not* its matching full
// snapshot, then something is wrong with the node (maybe someone deleted the full
// snapshot from the filesystem?), so just return a NoSnapshot error because this
// incremental snapshot is unusable on its own.

match full_snapshot_archive {
None => Err(RpcCustomError::NoSnapshot.into()),
Some(full_snapshot_archive) => match incremental_snapshot_archive {
None => Ok(RpcSnapshotInfo::FullSnapshot(SnapshotInfo {
slot: full_snapshot_archive.slot(),
hash: full_snapshot_archive.hash().to_string(),
})),
Some(incremental_snapshot_archive) => {
Ok(RpcSnapshotInfo::IncrementalSnapshot {
full: SnapshotInfo {
slot: full_snapshot_archive.slot(),
hash: full_snapshot_archive.hash().to_string(),
},
incremental: SnapshotInfo {
slot: incremental_snapshot_archive.slot(),
hash: incremental_snapshot_archive.hash().to_string(),
},
})
}
},
}
}

fn get_transaction_count(
&self,
meta: Self::Metadata,
Expand Down
2 changes: 1 addition & 1 deletion runtime/src/snapshot_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1146,7 +1146,7 @@ where
}

/// Get a list of the incremental snapshot archives in a directory
fn get_incremental_snapshot_archives<P>(
pub fn get_incremental_snapshot_archives<P>(
snapshot_archives_dir: P,
) -> Vec<IncrementalSnapshotArchiveInfo>
where
Expand Down

0 comments on commit 20ef9e5

Please sign in to comment.