549 lines
22 KiB
Rust
549 lines
22 KiB
Rust
// file: kb-onchain-transport/src/standard_http_blocks.rs
|
|
// version: 2
|
|
|
|
//! Configurable standard block and ledger Solana HTTP JSON-RPC requests.
|
|
|
|
/// Options accepted by `getBlock`.
|
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct RpcBlockConfig {
|
|
/// Optional transaction encoding.
|
|
#[serde(skip_serializing_if = "std::option::Option::is_none")]
|
|
pub encoding: std::option::Option<crate::RpcTransactionEncoding>,
|
|
/// Optional transaction detail level.
|
|
#[serde(skip_serializing_if = "std::option::Option::is_none")]
|
|
pub transaction_details: std::option::Option<crate::RpcTransactionDetails>,
|
|
/// Whether rewards must be included.
|
|
#[serde(skip_serializing_if = "std::option::Option::is_none")]
|
|
pub rewards: std::option::Option<bool>,
|
|
/// Optional confirmed or finalized commitment.
|
|
#[serde(skip_serializing_if = "std::option::Option::is_none")]
|
|
pub commitment: std::option::Option<crate::RpcCommitmentLevel>,
|
|
/// Highest transaction version the caller can decode.
|
|
#[serde(skip_serializing_if = "std::option::Option::is_none")]
|
|
pub max_supported_transaction_version: std::option::Option<u8>,
|
|
}
|
|
|
|
impl crate::RpcBlockConfig {
|
|
/// Rejects the processed commitment unsupported by block-history methods.
|
|
pub fn validate(&self) -> kb_core::Result<()> {
|
|
if self.commitment == std::option::Option::Some(crate::RpcCommitmentLevel::Processed) {
|
|
return std::result::Result::Err(kb_core::Error::config(
|
|
"getBlock does not support processed commitment",
|
|
));
|
|
}
|
|
return std::result::Result::Ok(());
|
|
}
|
|
}
|
|
|
|
/// Inclusive slot range accepted by `getBlockProduction`.
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct RpcBlockProductionConfigRange {
|
|
/// First slot included in the range.
|
|
pub first_slot: u64,
|
|
/// Optional final slot included in the range.
|
|
#[serde(skip_serializing_if = "std::option::Option::is_none")]
|
|
pub last_slot: std::option::Option<u64>,
|
|
}
|
|
|
|
/// Options accepted by `getBlockProduction`.
|
|
#[derive(Clone, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct RpcBlockProductionConfig {
|
|
/// Optional validator identity filter.
|
|
#[serde(skip_serializing_if = "std::option::Option::is_none")]
|
|
pub identity: std::option::Option<std::string::String>,
|
|
/// Optional inclusive slot range.
|
|
#[serde(skip_serializing_if = "std::option::Option::is_none")]
|
|
pub range: std::option::Option<crate::RpcBlockProductionConfigRange>,
|
|
/// Optional commitment level.
|
|
#[serde(skip_serializing_if = "std::option::Option::is_none")]
|
|
pub commitment: std::option::Option<crate::RpcCommitmentLevel>,
|
|
}
|
|
|
|
impl crate::RpcBlockProductionConfig {
|
|
/// Validates identity and range ordering.
|
|
pub fn validate(&self) -> kb_core::Result<()> {
|
|
if let std::option::Option::Some(identity) = &self.identity {
|
|
let identity_result =
|
|
crate::validate_solana_pubkey_text(identity, "getBlockProduction identity");
|
|
if let std::result::Result::Err(error) = identity_result {
|
|
return std::result::Result::Err(error);
|
|
}
|
|
}
|
|
if let std::option::Option::Some(range) = self.range {
|
|
if let std::option::Option::Some(last_slot) = range.last_slot {
|
|
if last_slot < range.first_slot {
|
|
return std::result::Result::Err(kb_core::Error::config(
|
|
"getBlockProduction last slot must not precede first slot",
|
|
));
|
|
}
|
|
}
|
|
}
|
|
return std::result::Result::Ok(());
|
|
}
|
|
}
|
|
|
|
/// Reward category attached to a block reward entry.
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub enum RpcRewardType {
|
|
/// Transaction fee reward.
|
|
Fee,
|
|
/// Rent reward.
|
|
Rent,
|
|
/// Staking reward.
|
|
Staking,
|
|
/// Vote reward.
|
|
Voting,
|
|
}
|
|
|
|
/// One reward entry returned with a block.
|
|
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct RpcReward {
|
|
/// Recipient public key.
|
|
pub pubkey: std::string::String,
|
|
/// Signed lamport balance change.
|
|
pub lamports: i64,
|
|
/// Recipient balance after the reward.
|
|
pub post_balance: u64,
|
|
/// Optional reward category.
|
|
pub reward_type: std::option::Option<crate::RpcRewardType>,
|
|
/// Optional validator commission percentage.
|
|
pub commission: std::option::Option<u8>,
|
|
/// Optional validator commission in basis points.
|
|
#[serde(default, skip_serializing_if = "std::option::Option::is_none")]
|
|
pub commission_bps: std::option::Option<u16>,
|
|
}
|
|
|
|
/// Encoding-dependent confirmed block returned by `getBlock`.
|
|
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct RpcConfirmedBlock {
|
|
/// Block hash.
|
|
pub blockhash: std::string::String,
|
|
/// Previous block hash.
|
|
pub previous_blockhash: std::string::String,
|
|
/// Parent slot.
|
|
pub parent_slot: u64,
|
|
/// Encoding-dependent transaction entries when requested.
|
|
#[serde(default)]
|
|
pub transactions: std::option::Option<std::vec::Vec<serde_json::Value>>,
|
|
/// Signature list when signature-only details are requested.
|
|
#[serde(default)]
|
|
pub signatures: std::option::Option<std::vec::Vec<std::string::String>>,
|
|
/// Rewards when requested.
|
|
#[serde(default)]
|
|
pub rewards: std::option::Option<std::vec::Vec<crate::RpcReward>>,
|
|
/// Unix block time when available.
|
|
#[serde(default)]
|
|
pub block_time: std::option::Option<i64>,
|
|
/// Block height when available.
|
|
#[serde(default)]
|
|
pub block_height: std::option::Option<u64>,
|
|
/// Number of reward partitions when available.
|
|
#[serde(default)]
|
|
pub num_reward_partitions: std::option::Option<u64>,
|
|
}
|
|
|
|
/// Stake commitment information returned for one block.
|
|
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct RpcBlockCommitment {
|
|
/// Commitment stake by lockout depth, or `None` when unavailable.
|
|
pub commitment: std::option::Option<std::vec::Vec<u64>>,
|
|
/// Total active stake used for the commitment calculation.
|
|
pub total_stake: u64,
|
|
}
|
|
|
|
/// Actual slot range represented by a block-production response.
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct RpcBlockProductionRange {
|
|
/// First represented slot.
|
|
pub first_slot: u64,
|
|
/// Last represented slot.
|
|
pub last_slot: u64,
|
|
}
|
|
|
|
/// Block-production counts grouped by validator identity.
|
|
#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct RpcBlockProduction {
|
|
/// Validator identity to `(leader slots, blocks produced)` map.
|
|
pub by_identity: std::collections::BTreeMap<std::string::String, (usize, usize)>,
|
|
/// Actual represented slot range.
|
|
pub range: crate::RpcBlockProductionRange,
|
|
}
|
|
|
|
/// Recent cluster performance sample.
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct RpcPerformanceSample {
|
|
/// Slot at the end of the sample window.
|
|
pub slot: u64,
|
|
/// Total transactions processed during the sample.
|
|
pub num_transactions: u64,
|
|
/// Optional count excluding vote transactions.
|
|
#[serde(default)]
|
|
pub num_non_vote_transactions: std::option::Option<u64>,
|
|
/// Slots processed during the sample.
|
|
pub num_slots: u64,
|
|
/// Sample period in seconds.
|
|
pub sample_period_secs: u16,
|
|
}
|
|
|
|
/// Typed `getBlock` request.
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub struct GetBlockRequest {
|
|
/// Block slot.
|
|
pub slot: u64,
|
|
/// Optional encoding, details, rewards, commitment and version options.
|
|
pub config: std::option::Option<crate::RpcBlockConfig>,
|
|
}
|
|
|
|
impl crate::StandardHttpRequest for crate::GetBlockRequest {
|
|
type Response = std::option::Option<crate::RpcConfirmedBlock>;
|
|
|
|
const METHOD: &'static str = "getBlock";
|
|
|
|
fn params(&self) -> kb_core::Result<std::vec::Vec<serde_json::Value>> {
|
|
let mut params = std::vec![serde_json::Value::from(self.slot)];
|
|
if let std::option::Option::Some(config) = &self.config {
|
|
let config_result = config.validate();
|
|
if let std::result::Result::Err(error) = config_result {
|
|
return std::result::Result::Err(error);
|
|
}
|
|
let value = match crate::serialize_parameter(Self::METHOD, config) {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
params.push(value);
|
|
}
|
|
return std::result::Result::Ok(params);
|
|
}
|
|
}
|
|
|
|
/// Typed `getBlockCommitment` request.
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
pub struct GetBlockCommitmentRequest {
|
|
/// Block slot.
|
|
pub slot: u64,
|
|
}
|
|
|
|
impl crate::StandardHttpRequest for crate::GetBlockCommitmentRequest {
|
|
type Response = crate::RpcBlockCommitment;
|
|
|
|
const METHOD: &'static str = "getBlockCommitment";
|
|
|
|
fn params(&self) -> kb_core::Result<std::vec::Vec<serde_json::Value>> {
|
|
return std::result::Result::Ok(std::vec![serde_json::Value::from(self.slot)]);
|
|
}
|
|
}
|
|
|
|
/// Typed `getBlockProduction` request.
|
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
|
pub struct GetBlockProductionRequest {
|
|
/// Optional identity, range and commitment options.
|
|
pub config: std::option::Option<crate::RpcBlockProductionConfig>,
|
|
}
|
|
|
|
impl crate::StandardHttpRequest for crate::GetBlockProductionRequest {
|
|
type Response = crate::RpcResponse<crate::RpcBlockProduction>;
|
|
|
|
const METHOD: &'static str = "getBlockProduction";
|
|
|
|
fn params(&self) -> kb_core::Result<std::vec::Vec<serde_json::Value>> {
|
|
if let std::option::Option::Some(config) = &self.config {
|
|
let validation_result = config.validate();
|
|
if let std::result::Result::Err(error) = validation_result {
|
|
return std::result::Result::Err(error);
|
|
}
|
|
let value = match crate::serialize_parameter(Self::METHOD, config) {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
return std::result::Result::Ok(std::vec![value]);
|
|
}
|
|
return std::result::Result::Ok(std::vec::Vec::new());
|
|
}
|
|
}
|
|
|
|
/// Typed `getBlocks` request.
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub struct GetBlocksRequest {
|
|
/// First slot included in the scan.
|
|
pub start_slot: u64,
|
|
/// Optional final slot included in the scan.
|
|
pub end_slot: std::option::Option<u64>,
|
|
/// Optional confirmed or finalized context options.
|
|
pub config: std::option::Option<crate::RpcContextConfig>,
|
|
}
|
|
|
|
impl crate::StandardHttpRequest for crate::GetBlocksRequest {
|
|
type Response = std::vec::Vec<u64>;
|
|
|
|
const METHOD: &'static str = "getBlocks";
|
|
|
|
fn params(&self) -> kb_core::Result<std::vec::Vec<serde_json::Value>> {
|
|
if let std::option::Option::Some(end_slot) = self.end_slot {
|
|
if end_slot >= self.start_slot
|
|
&& end_slot.saturating_sub(self.start_slot) > crate::MAX_BLOCK_RANGE
|
|
{
|
|
return std::result::Result::Err(kb_core::Error::config(format!(
|
|
"getBlocks range must not exceed {} slots",
|
|
crate::MAX_BLOCK_RANGE
|
|
)));
|
|
}
|
|
}
|
|
if let std::option::Option::Some(config) = &self.config {
|
|
if config.commitment == std::option::Option::Some(crate::RpcCommitmentLevel::Processed)
|
|
{
|
|
return std::result::Result::Err(kb_core::Error::config(
|
|
"getBlocks does not support processed commitment",
|
|
));
|
|
}
|
|
}
|
|
let mut params = std::vec![serde_json::Value::from(self.start_slot)];
|
|
if let std::option::Option::Some(end_slot) = self.end_slot {
|
|
params.push(serde_json::Value::from(end_slot));
|
|
}
|
|
if let std::option::Option::Some(config) = &self.config {
|
|
let value = match crate::serialize_parameter(Self::METHOD, config) {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
params.push(value);
|
|
}
|
|
return std::result::Result::Ok(params);
|
|
}
|
|
}
|
|
|
|
/// Typed `getBlocksWithLimit` request.
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub struct GetBlocksWithLimitRequest {
|
|
/// First slot considered by the scan.
|
|
pub start_slot: u64,
|
|
/// Maximum number of block slots returned.
|
|
pub limit: u64,
|
|
/// Optional confirmed or finalized context options.
|
|
pub config: std::option::Option<crate::RpcContextConfig>,
|
|
}
|
|
|
|
impl crate::StandardHttpRequest for crate::GetBlocksWithLimitRequest {
|
|
type Response = std::vec::Vec<u64>;
|
|
|
|
const METHOD: &'static str = "getBlocksWithLimit";
|
|
|
|
fn params(&self) -> kb_core::Result<std::vec::Vec<serde_json::Value>> {
|
|
if self.limit > crate::MAX_BLOCK_RANGE {
|
|
return std::result::Result::Err(kb_core::Error::config(format!(
|
|
"getBlocksWithLimit limit must not exceed {}",
|
|
crate::MAX_BLOCK_RANGE
|
|
)));
|
|
}
|
|
if let std::option::Option::Some(config) = &self.config {
|
|
if config.commitment == std::option::Option::Some(crate::RpcCommitmentLevel::Processed)
|
|
{
|
|
return std::result::Result::Err(kb_core::Error::config(
|
|
"getBlocksWithLimit does not support processed commitment",
|
|
));
|
|
}
|
|
}
|
|
let mut params = std::vec![
|
|
serde_json::Value::from(self.start_slot),
|
|
serde_json::Value::from(self.limit),
|
|
];
|
|
if let std::option::Option::Some(config) = &self.config {
|
|
let value = match crate::serialize_parameter(Self::METHOD, config) {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
|
};
|
|
params.push(value);
|
|
}
|
|
return std::result::Result::Ok(params);
|
|
}
|
|
}
|
|
|
|
/// Typed `getBlockTime` request.
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
pub struct GetBlockTimeRequest {
|
|
/// Block slot.
|
|
pub slot: u64,
|
|
}
|
|
|
|
impl crate::StandardHttpRequest for crate::GetBlockTimeRequest {
|
|
type Response = std::option::Option<i64>;
|
|
|
|
const METHOD: &'static str = "getBlockTime";
|
|
|
|
fn params(&self) -> kb_core::Result<std::vec::Vec<serde_json::Value>> {
|
|
return std::result::Result::Ok(std::vec![serde_json::Value::from(self.slot)]);
|
|
}
|
|
}
|
|
|
|
/// Typed `getFirstAvailableBlock` request.
|
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
|
pub struct GetFirstAvailableBlockRequest;
|
|
|
|
impl crate::StandardHttpRequest for crate::GetFirstAvailableBlockRequest {
|
|
type Response = u64;
|
|
|
|
const METHOD: &'static str = "getFirstAvailableBlock";
|
|
|
|
fn params(&self) -> kb_core::Result<std::vec::Vec<serde_json::Value>> {
|
|
return std::result::Result::Ok(std::vec::Vec::new());
|
|
}
|
|
}
|
|
|
|
/// Typed `getRecentPerformanceSamples` request.
|
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
|
pub struct GetRecentPerformanceSamplesRequest {
|
|
/// Optional sample count. Absence delegates the default to the endpoint.
|
|
pub limit: std::option::Option<usize>,
|
|
}
|
|
|
|
impl crate::StandardHttpRequest for crate::GetRecentPerformanceSamplesRequest {
|
|
type Response = std::vec::Vec<crate::RpcPerformanceSample>;
|
|
|
|
const METHOD: &'static str = "getRecentPerformanceSamples";
|
|
|
|
fn params(&self) -> kb_core::Result<std::vec::Vec<serde_json::Value>> {
|
|
if let std::option::Option::Some(limit) = self.limit {
|
|
if limit > crate::MAX_PERFORMANCE_SAMPLE_COUNT {
|
|
return std::result::Result::Err(kb_core::Error::config(format!(
|
|
"getRecentPerformanceSamples limit must not exceed {}",
|
|
crate::MAX_PERFORMANCE_SAMPLE_COUNT
|
|
)));
|
|
}
|
|
return std::result::Result::Ok(std::vec![serde_json::Value::from(limit)]);
|
|
}
|
|
return std::result::Result::Ok(std::vec::Vec::new());
|
|
}
|
|
}
|
|
|
|
/// Typed `minimumLedgerSlot` request.
|
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
|
pub struct MinimumLedgerSlotRequest;
|
|
|
|
impl crate::StandardHttpRequest for crate::MinimumLedgerSlotRequest {
|
|
type Response = u64;
|
|
|
|
const METHOD: &'static str = "minimumLedgerSlot";
|
|
|
|
fn params(&self) -> kb_core::Result<std::vec::Vec<serde_json::Value>> {
|
|
return std::result::Result::Ok(std::vec::Vec::new());
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
#[test]
|
|
fn block_options_remain_independently_selectable() {
|
|
let request = crate::GetBlockRequest {
|
|
slot: 55,
|
|
config: std::option::Option::Some(crate::RpcBlockConfig {
|
|
encoding: std::option::Option::Some(crate::RpcTransactionEncoding::JsonParsed),
|
|
transaction_details: std::option::Option::Some(
|
|
crate::RpcTransactionDetails::Accounts,
|
|
),
|
|
rewards: std::option::Option::Some(false),
|
|
commitment: std::option::Option::Some(crate::RpcCommitmentLevel::Confirmed),
|
|
max_supported_transaction_version: std::option::Option::Some(0),
|
|
}),
|
|
};
|
|
let params = match crate::StandardHttpRequest::params(&request) {
|
|
std::result::Result::Ok(params) => params,
|
|
std::result::Result::Err(error) => panic!("params failed: {error}"),
|
|
};
|
|
assert_eq!(params[1]["encoding"], serde_json::Value::String("jsonParsed".to_string()));
|
|
assert_eq!(
|
|
params[1]["transactionDetails"],
|
|
serde_json::Value::String("accounts".to_string())
|
|
);
|
|
assert_eq!(params[1]["rewards"], serde_json::Value::Bool(false));
|
|
assert_eq!(params[1]["maxSupportedTransactionVersion"], serde_json::Value::from(0_u64));
|
|
|
|
let reward = match serde_json::from_value::<crate::RpcReward>(serde_json::json!({
|
|
"pubkey": "validator",
|
|
"lamports": 42,
|
|
"postBalance": 84,
|
|
"rewardType": "voting",
|
|
"commission": 5,
|
|
"commissionBps": 550
|
|
})) {
|
|
std::result::Result::Ok(value) => value,
|
|
std::result::Result::Err(error) => panic!("reward parsing failed: {error}"),
|
|
};
|
|
assert_eq!(reward.commission, std::option::Option::Some(5));
|
|
assert_eq!(reward.commission_bps, std::option::Option::Some(550));
|
|
}
|
|
|
|
#[test]
|
|
fn blocks_without_end_slot_places_config_in_second_position() {
|
|
let request = crate::GetBlocksRequest {
|
|
start_slot: 10,
|
|
end_slot: std::option::Option::None,
|
|
config: std::option::Option::Some(crate::RpcContextConfig {
|
|
commitment: std::option::Option::Some(crate::RpcCommitmentLevel::Finalized),
|
|
min_context_slot: std::option::Option::Some(9),
|
|
}),
|
|
};
|
|
let params = match crate::StandardHttpRequest::params(&request) {
|
|
std::result::Result::Ok(params) => params,
|
|
std::result::Result::Err(error) => panic!("params failed: {error}"),
|
|
};
|
|
assert_eq!(params.len(), 2);
|
|
assert_eq!(params[1]["commitment"], serde_json::Value::String("finalized".to_string()));
|
|
}
|
|
|
|
#[test]
|
|
fn block_ranges_and_performance_samples_are_bounded() {
|
|
let range = crate::GetBlocksRequest {
|
|
start_slot: 0,
|
|
end_slot: std::option::Option::Some(500_001),
|
|
config: std::option::Option::None,
|
|
};
|
|
let samples =
|
|
crate::GetRecentPerformanceSamplesRequest { limit: std::option::Option::Some(721) };
|
|
assert!(crate::StandardHttpRequest::params(&range).is_err());
|
|
assert!(crate::StandardHttpRequest::params(&samples).is_err());
|
|
|
|
let reversed_range = crate::GetBlocksRequest {
|
|
start_slot: 10,
|
|
end_slot: std::option::Option::Some(9),
|
|
config: std::option::Option::None,
|
|
};
|
|
let zero_blocks = crate::GetBlocksWithLimitRequest {
|
|
start_slot: 10,
|
|
limit: 0,
|
|
config: std::option::Option::None,
|
|
};
|
|
let zero_samples =
|
|
crate::GetRecentPerformanceSamplesRequest { limit: std::option::Option::Some(0) };
|
|
let reversed_params = match crate::StandardHttpRequest::params(&reversed_range) {
|
|
std::result::Result::Ok(params) => params,
|
|
std::result::Result::Err(error) => panic!("reversed range failed: {error}"),
|
|
};
|
|
let zero_block_params = match crate::StandardHttpRequest::params(&zero_blocks) {
|
|
std::result::Result::Ok(params) => params,
|
|
std::result::Result::Err(error) => panic!("zero block limit failed: {error}"),
|
|
};
|
|
let zero_sample_params = match crate::StandardHttpRequest::params(&zero_samples) {
|
|
std::result::Result::Ok(params) => params,
|
|
std::result::Result::Err(error) => panic!("zero sample limit failed: {error}"),
|
|
};
|
|
assert_eq!(
|
|
reversed_params,
|
|
std::vec![serde_json::Value::from(10_u64), serde_json::Value::from(9_u64),]
|
|
);
|
|
assert_eq!(
|
|
zero_block_params,
|
|
std::vec![serde_json::Value::from(10_u64), serde_json::Value::from(0_u64),]
|
|
);
|
|
assert_eq!(zero_sample_params, std::vec![serde_json::Value::from(0_u64)]);
|
|
}
|
|
}
|