2019-11-12 14:49:41 -05:00
|
|
|
use jsonrpc_core::Result as JsonResult;
|
2019-03-12 18:26:07 -06:00
|
|
|
use serde_json::{json, Value};
|
2019-11-06 14:15:00 -07:00
|
|
|
use solana_sdk::{
|
|
|
|
clock::{Epoch, Slot},
|
2019-11-17 20:17:15 -07:00
|
|
|
hash::Hash,
|
|
|
|
transaction::{Result, Transaction},
|
2019-11-06 14:15:00 -07:00
|
|
|
};
|
2019-12-18 12:42:58 -07:00
|
|
|
use std::{collections::HashMap, error, fmt, io, net::SocketAddr};
|
2019-11-12 14:49:41 -05:00
|
|
|
|
|
|
|
pub type RpcResponseIn<T> = JsonResult<Response<T>>;
|
|
|
|
pub type RpcResponse<T> = io::Result<Response<T>>;
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
|
|
pub struct RpcResponseContext {
|
|
|
|
pub slot: u64,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
|
|
pub struct Response<T> {
|
|
|
|
pub context: RpcResponseContext,
|
|
|
|
pub value: T,
|
|
|
|
}
|
2019-03-12 18:26:07 -06:00
|
|
|
|
2019-11-25 12:08:03 -07:00
|
|
|
#[derive(Debug, Default, PartialEq, Serialize, Deserialize)]
|
2019-11-19 09:39:55 -07:00
|
|
|
#[serde(rename_all = "camelCase")]
|
2019-11-17 20:17:15 -07:00
|
|
|
pub struct RpcConfirmedBlock {
|
|
|
|
pub previous_blockhash: Hash,
|
|
|
|
pub blockhash: Hash,
|
2019-11-19 09:39:55 -07:00
|
|
|
pub parent_slot: Slot,
|
2019-11-18 09:12:42 -07:00
|
|
|
pub transactions: Vec<(Transaction, Option<RpcTransactionStatus>)>,
|
|
|
|
}
|
|
|
|
|
2019-12-11 15:06:54 -07:00
|
|
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
2019-12-18 10:56:29 -07:00
|
|
|
#[serde(rename_all = "camelCase")]
|
2019-11-18 09:12:42 -07:00
|
|
|
pub struct RpcTransactionStatus {
|
|
|
|
pub status: Result<()>,
|
|
|
|
pub fee: u64,
|
2019-12-18 10:56:29 -07:00
|
|
|
pub pre_balances: Vec<u64>,
|
|
|
|
pub post_balances: Vec<u64>,
|
2019-11-17 20:17:15 -07:00
|
|
|
}
|
|
|
|
|
2019-11-12 22:01:04 -07:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
|
|
pub struct RpcContactInfo {
|
|
|
|
/// Pubkey of the node as a base-58 string
|
|
|
|
pub pubkey: String,
|
|
|
|
/// Gossip port
|
|
|
|
pub gossip: Option<SocketAddr>,
|
|
|
|
/// Tpu port
|
|
|
|
pub tpu: Option<SocketAddr>,
|
|
|
|
/// JSON RPC port
|
|
|
|
pub rpc: Option<SocketAddr>,
|
|
|
|
}
|
|
|
|
|
2019-12-18 12:42:58 -07:00
|
|
|
/// Map of leader base58 identity pubkeys to the slot indices relative to the first epoch slot
|
|
|
|
pub type RpcLeaderSchedule = HashMap<String, Vec<usize>>;
|
|
|
|
|
2019-09-27 22:00:30 -07:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct RpcEpochInfo {
|
|
|
|
/// The current epoch
|
2019-11-02 00:38:30 -07:00
|
|
|
pub epoch: Epoch,
|
2019-09-27 22:00:30 -07:00
|
|
|
|
|
|
|
/// The current slot, relative to the start of the current epoch
|
|
|
|
pub slot_index: u64,
|
|
|
|
|
|
|
|
/// The number of slots in this epoch
|
|
|
|
pub slots_in_epoch: u64,
|
|
|
|
|
|
|
|
/// The absolute current slot
|
2019-11-02 00:38:30 -07:00
|
|
|
pub absolute_slot: Slot,
|
2019-09-27 22:00:30 -07:00
|
|
|
}
|
|
|
|
|
2019-11-12 22:01:04 -07:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
|
|
#[serde(rename_all = "kebab-case")]
|
|
|
|
pub struct RpcVersionInfo {
|
|
|
|
/// The current version of solana-core
|
|
|
|
pub solana_core: String,
|
|
|
|
}
|
|
|
|
|
2019-10-07 12:30:22 +09:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct RpcVoteAccountStatus {
|
|
|
|
pub current: Vec<RpcVoteAccountInfo>,
|
|
|
|
pub delinquent: Vec<RpcVoteAccountInfo>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct RpcVoteAccountInfo {
|
|
|
|
/// Vote account pubkey as base-58 encoded string
|
|
|
|
pub vote_pubkey: String,
|
|
|
|
|
|
|
|
/// The pubkey of the node that votes using this account
|
|
|
|
pub node_pubkey: String,
|
|
|
|
|
|
|
|
/// The current stake, in lamports, delegated to this vote account
|
|
|
|
pub activated_stake: u64,
|
|
|
|
|
|
|
|
/// An 8-bit integer used as a fraction (commission/MAX_U8) for rewards payout
|
|
|
|
pub commission: u8,
|
|
|
|
|
|
|
|
/// Whether this account is staked for the current epoch
|
|
|
|
pub epoch_vote_account: bool,
|
|
|
|
|
2019-12-11 22:04:54 -08:00
|
|
|
/// History of how many credits earned by the end of each epoch
|
|
|
|
/// each tuple is (Epoch, credits, prev_credits)
|
|
|
|
pub epoch_credits: Vec<(Epoch, u64, u64)>,
|
|
|
|
|
2019-10-07 12:30:22 +09:00
|
|
|
/// Most recent slot voted on by this vote account (0 if no votes exist)
|
|
|
|
pub last_vote: u64,
|
|
|
|
|
|
|
|
/// Current root slot for this vote account (0 if not root slot exists)
|
2019-11-02 00:38:30 -07:00
|
|
|
pub root_slot: Slot,
|
2019-10-07 12:30:22 +09:00
|
|
|
}
|
|
|
|
|
2019-12-27 14:35:49 -06:00
|
|
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
2019-03-12 18:26:07 -06:00
|
|
|
pub enum RpcRequest {
|
|
|
|
ConfirmTransaction,
|
2019-04-25 08:52:53 -06:00
|
|
|
DeregisterNode,
|
2019-10-11 13:30:52 -06:00
|
|
|
ValidatorExit,
|
2019-03-12 18:26:07 -06:00
|
|
|
GetAccountInfo,
|
|
|
|
GetBalance,
|
2019-11-26 00:40:36 -07:00
|
|
|
GetBlockTime,
|
2019-04-25 08:52:53 -06:00
|
|
|
GetClusterNodes,
|
2019-12-18 22:31:38 -07:00
|
|
|
GetConfirmedBlock,
|
|
|
|
GetConfirmedBlocks,
|
2019-09-27 22:00:30 -07:00
|
|
|
GetEpochInfo,
|
2019-10-22 16:41:18 -04:00
|
|
|
GetEpochSchedule,
|
2019-11-08 23:56:57 -05:00
|
|
|
GetGenesisHash,
|
2019-08-27 18:17:03 -04:00
|
|
|
GetInflation,
|
2019-12-17 16:26:31 -07:00
|
|
|
GetLeaderSchedule,
|
2019-04-25 08:52:53 -06:00
|
|
|
GetNumBlocksSinceSignatureConfirmation,
|
2019-07-11 12:38:52 -06:00
|
|
|
GetProgramAccounts,
|
2019-03-12 18:26:07 -06:00
|
|
|
GetRecentBlockhash,
|
|
|
|
GetSignatureStatus,
|
2019-06-12 16:43:05 -07:00
|
|
|
GetSlot,
|
2019-04-25 08:52:53 -06:00
|
|
|
GetSlotLeader,
|
2019-07-10 13:33:29 -07:00
|
|
|
GetStorageTurn,
|
|
|
|
GetStorageTurnRate,
|
2019-07-09 16:48:40 -07:00
|
|
|
GetSlotsPerSegment,
|
2019-05-03 16:27:53 -07:00
|
|
|
GetStoragePubkeysForSlot,
|
2019-03-12 18:26:07 -06:00
|
|
|
GetTransactionCount,
|
2019-08-08 11:13:06 -06:00
|
|
|
GetVersion,
|
2019-08-16 17:02:19 -06:00
|
|
|
GetVoteAccounts,
|
2019-04-25 08:52:53 -06:00
|
|
|
RegisterNode,
|
2019-03-12 18:26:07 -06:00
|
|
|
RequestAirdrop,
|
|
|
|
SendTransaction,
|
|
|
|
SignVote,
|
2019-09-26 23:27:13 +05:30
|
|
|
GetMinimumBalanceForRentExemption,
|
2019-03-12 18:26:07 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
impl RpcRequest {
|
2019-12-18 22:26:11 -07:00
|
|
|
pub(crate) fn build_request_json(&self, id: u64, params: Value) -> Value {
|
2019-03-12 18:26:07 -06:00
|
|
|
let jsonrpc = "2.0";
|
|
|
|
let method = match self {
|
|
|
|
RpcRequest::ConfirmTransaction => "confirmTransaction",
|
2019-04-25 08:52:53 -06:00
|
|
|
RpcRequest::DeregisterNode => "deregisterNode",
|
2019-10-11 13:30:52 -06:00
|
|
|
RpcRequest::ValidatorExit => "validatorExit",
|
2019-03-12 18:26:07 -06:00
|
|
|
RpcRequest::GetAccountInfo => "getAccountInfo",
|
|
|
|
RpcRequest::GetBalance => "getBalance",
|
2019-11-26 00:40:36 -07:00
|
|
|
RpcRequest::GetBlockTime => "getBlockTime",
|
2019-04-25 08:52:53 -06:00
|
|
|
RpcRequest::GetClusterNodes => "getClusterNodes",
|
2019-12-18 22:31:38 -07:00
|
|
|
RpcRequest::GetConfirmedBlock => "getConfirmedBlock",
|
|
|
|
RpcRequest::GetConfirmedBlocks => "getConfirmedBlocks",
|
2019-09-27 22:00:30 -07:00
|
|
|
RpcRequest::GetEpochInfo => "getEpochInfo",
|
2019-10-22 16:41:18 -04:00
|
|
|
RpcRequest::GetEpochSchedule => "getEpochSchedule",
|
2019-11-08 23:56:57 -05:00
|
|
|
RpcRequest::GetGenesisHash => "getGenesisHash",
|
2019-08-27 18:17:03 -04:00
|
|
|
RpcRequest::GetInflation => "getInflation",
|
2019-12-17 16:26:31 -07:00
|
|
|
RpcRequest::GetLeaderSchedule => "getLeaderSchedule",
|
2019-04-25 08:52:53 -06:00
|
|
|
RpcRequest::GetNumBlocksSinceSignatureConfirmation => {
|
|
|
|
"getNumBlocksSinceSignatureConfirmation"
|
|
|
|
}
|
2019-07-11 12:38:52 -06:00
|
|
|
RpcRequest::GetProgramAccounts => "getProgramAccounts",
|
2019-03-12 18:26:07 -06:00
|
|
|
RpcRequest::GetRecentBlockhash => "getRecentBlockhash",
|
|
|
|
RpcRequest::GetSignatureStatus => "getSignatureStatus",
|
2019-06-12 16:43:05 -07:00
|
|
|
RpcRequest::GetSlot => "getSlot",
|
2019-04-25 08:52:53 -06:00
|
|
|
RpcRequest::GetSlotLeader => "getSlotLeader",
|
2019-07-10 13:33:29 -07:00
|
|
|
RpcRequest::GetStorageTurn => "getStorageTurn",
|
|
|
|
RpcRequest::GetStorageTurnRate => "getStorageTurnRate",
|
2019-07-09 16:48:40 -07:00
|
|
|
RpcRequest::GetSlotsPerSegment => "getSlotsPerSegment",
|
2019-05-03 16:27:53 -07:00
|
|
|
RpcRequest::GetStoragePubkeysForSlot => "getStoragePubkeysForSlot",
|
2019-03-12 18:26:07 -06:00
|
|
|
RpcRequest::GetTransactionCount => "getTransactionCount",
|
2019-08-08 11:13:06 -06:00
|
|
|
RpcRequest::GetVersion => "getVersion",
|
2019-08-16 17:02:19 -06:00
|
|
|
RpcRequest::GetVoteAccounts => "getVoteAccounts",
|
2019-04-25 08:52:53 -06:00
|
|
|
RpcRequest::RegisterNode => "registerNode",
|
2019-03-12 18:26:07 -06:00
|
|
|
RpcRequest::RequestAirdrop => "requestAirdrop",
|
|
|
|
RpcRequest::SendTransaction => "sendTransaction",
|
|
|
|
RpcRequest::SignVote => "signVote",
|
2019-09-26 23:27:13 +05:30
|
|
|
RpcRequest::GetMinimumBalanceForRentExemption => "getMinimumBalanceForRentExemption",
|
2019-03-12 18:26:07 -06:00
|
|
|
};
|
2019-12-18 22:26:11 -07:00
|
|
|
json!({
|
2019-03-12 18:26:07 -06:00
|
|
|
"jsonrpc": jsonrpc,
|
|
|
|
"id": id,
|
|
|
|
"method": method,
|
2019-12-18 22:26:11 -07:00
|
|
|
"params": params,
|
|
|
|
})
|
2019-03-12 18:26:07 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
|
|
pub enum RpcError {
|
|
|
|
RpcRequestError(String),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for RpcError {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
write!(f, "invalid")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl error::Error for RpcError {
|
|
|
|
fn description(&self) -> &str {
|
|
|
|
"invalid"
|
|
|
|
}
|
|
|
|
|
|
|
|
fn cause(&self) -> Option<&dyn error::Error> {
|
|
|
|
// Generic error, underlying cause isn't tracked.
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
2019-12-18 22:26:11 -07:00
|
|
|
use solana_sdk::commitment_config::{CommitmentConfig, CommitmentLevel};
|
2019-03-12 18:26:07 -06:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_build_request_json() {
|
|
|
|
let test_request = RpcRequest::GetAccountInfo;
|
2019-11-06 14:15:00 -07:00
|
|
|
let addr = json!("deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx");
|
2019-12-18 22:26:11 -07:00
|
|
|
let request = test_request.build_request_json(1, json!([addr.clone()]));
|
2019-03-12 18:26:07 -06:00
|
|
|
assert_eq!(request["method"], "getAccountInfo");
|
2019-11-06 14:15:00 -07:00
|
|
|
assert_eq!(request["params"], json!([addr]));
|
2019-03-12 18:26:07 -06:00
|
|
|
|
|
|
|
let test_request = RpcRequest::GetBalance;
|
2019-12-18 22:26:11 -07:00
|
|
|
let request = test_request.build_request_json(1, json!([addr]));
|
2019-03-12 18:26:07 -06:00
|
|
|
assert_eq!(request["method"], "getBalance");
|
|
|
|
|
2019-09-27 22:00:30 -07:00
|
|
|
let test_request = RpcRequest::GetEpochInfo;
|
2019-12-18 22:26:11 -07:00
|
|
|
let request = test_request.build_request_json(1, Value::Null);
|
2019-09-27 22:00:30 -07:00
|
|
|
assert_eq!(request["method"], "getEpochInfo");
|
|
|
|
|
2019-08-27 18:17:03 -04:00
|
|
|
let test_request = RpcRequest::GetInflation;
|
2019-12-18 22:26:11 -07:00
|
|
|
let request = test_request.build_request_json(1, Value::Null);
|
2019-08-27 18:17:03 -04:00
|
|
|
assert_eq!(request["method"], "getInflation");
|
|
|
|
|
2019-03-12 18:26:07 -06:00
|
|
|
let test_request = RpcRequest::GetRecentBlockhash;
|
2019-12-18 22:26:11 -07:00
|
|
|
let request = test_request.build_request_json(1, Value::Null);
|
2019-03-12 18:26:07 -06:00
|
|
|
assert_eq!(request["method"], "getRecentBlockhash");
|
|
|
|
|
2019-06-12 16:43:05 -07:00
|
|
|
let test_request = RpcRequest::GetSlot;
|
2019-12-18 22:26:11 -07:00
|
|
|
let request = test_request.build_request_json(1, Value::Null);
|
2019-06-12 16:43:05 -07:00
|
|
|
assert_eq!(request["method"], "getSlot");
|
|
|
|
|
2019-03-12 18:26:07 -06:00
|
|
|
let test_request = RpcRequest::GetTransactionCount;
|
2019-12-18 22:26:11 -07:00
|
|
|
let request = test_request.build_request_json(1, Value::Null);
|
2019-03-12 18:26:07 -06:00
|
|
|
assert_eq!(request["method"], "getTransactionCount");
|
|
|
|
|
|
|
|
let test_request = RpcRequest::RequestAirdrop;
|
2019-12-18 22:26:11 -07:00
|
|
|
let request = test_request.build_request_json(1, Value::Null);
|
2019-03-12 18:26:07 -06:00
|
|
|
assert_eq!(request["method"], "requestAirdrop");
|
|
|
|
|
|
|
|
let test_request = RpcRequest::SendTransaction;
|
2019-12-18 22:26:11 -07:00
|
|
|
let request = test_request.build_request_json(1, Value::Null);
|
2019-03-12 18:26:07 -06:00
|
|
|
assert_eq!(request["method"], "sendTransaction");
|
|
|
|
}
|
2019-11-06 14:15:00 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_build_request_json_config_options() {
|
|
|
|
let commitment_config = CommitmentConfig {
|
|
|
|
commitment: CommitmentLevel::Max,
|
|
|
|
};
|
|
|
|
let addr = json!("deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx");
|
|
|
|
|
|
|
|
// Test request with CommitmentConfig and no params
|
|
|
|
let test_request = RpcRequest::GetRecentBlockhash;
|
2019-12-18 22:26:11 -07:00
|
|
|
let request = test_request.build_request_json(1, json!([commitment_config.clone()]));
|
2019-11-06 14:15:00 -07:00
|
|
|
assert_eq!(request["params"], json!([commitment_config.clone()]));
|
|
|
|
|
|
|
|
// Test request with CommitmentConfig and params
|
|
|
|
let test_request = RpcRequest::GetBalance;
|
|
|
|
let request =
|
2019-12-18 22:26:11 -07:00
|
|
|
test_request.build_request_json(1, json!([addr.clone(), commitment_config.clone()]));
|
2019-11-06 14:15:00 -07:00
|
|
|
assert_eq!(request["params"], json!([addr, commitment_config]));
|
|
|
|
}
|
2019-03-12 18:26:07 -06:00
|
|
|
}
|