v0.2.3-pre.004
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-onchain-transport-lib/src/lib.rs
|
||||
// version: 13
|
||||
// version: 14
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
#![forbid(unsafe_code)]
|
||||
@@ -10,9 +10,10 @@
|
||||
//! independent from `ksp-config-lib`, Store and Program layers. `ksp-config-lib` now constructs these public settings through its one-way Config ->
|
||||
//! Transport adapter without creating a reverse dependency. Logical endpoint clients, priority-aware pools, bounded admission limits and retry/no-resend policy
|
||||
//! are available. The four typed Solana HTTP foundation canaries plus all 22 typed `0.2.2` Accounts, Tokens and Cluster wrappers execute real JSON-RPC
|
||||
//! requests through the shared transport path. `0.2.3` exposes its shared Transaction wire/config primitives and its first four read wrappers:
|
||||
//! `getFeeForMessage`, `getLatestBlockhash`, `getTransactionCount` and `isBlockhashValid`. The remaining seven Transaction wrappers and the `0.2.4` family
|
||||
//! remain staged.
|
||||
//! requests through the shared transport path. `0.2.3` exposes its shared Transaction wire/config primitives and seven read wrappers through `pre.004`,
|
||||
//! including bounded prioritization-fee, address-signature and signature-status queries. `getTransaction`, both write submissions and
|
||||
//! `simulateTransaction`, plus the
|
||||
//! `0.2.4` family remain staged.
|
||||
|
||||
mod client;
|
||||
mod constants;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-onchain-transport-lib/src/rpc_transactions.rs
|
||||
// version: 3
|
||||
// version: 4
|
||||
|
||||
/// Binary encoding accepted for serialized transaction input payloads.
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
@@ -236,14 +236,12 @@ impl SolanaSignaturesForAddressConfig {
|
||||
}
|
||||
|
||||
/// Returns whether the pagination config would serialize to an empty object.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn is_empty(&self) -> bool {
|
||||
return self.before.is_none() && self.until.is_none() && self.limit.is_none() && self.commitment().is_none() && self.min_context_slot().is_none();
|
||||
}
|
||||
|
||||
/// Serializes this pagination config to the Solana JSON-RPC wire object.
|
||||
#[must_use]
|
||||
#[cfg(test)]
|
||||
pub(crate) fn to_json_value(&self) -> serde_json::Value {
|
||||
let context = self.context.to_json_value();
|
||||
let mut object = match context {
|
||||
@@ -283,14 +281,12 @@ impl SolanaSignatureStatusesConfig {
|
||||
}
|
||||
|
||||
/// Returns whether the status config would serialize to an empty object.
|
||||
#[cfg(test)]
|
||||
pub(crate) const fn is_empty(&self) -> bool {
|
||||
return self.search_transaction_history.is_none();
|
||||
}
|
||||
|
||||
/// Serializes this config to the Solana JSON-RPC wire object.
|
||||
#[must_use]
|
||||
#[cfg(test)]
|
||||
pub(crate) fn to_json_value(self) -> serde_json::Value {
|
||||
let mut object = serde_json::Map::new();
|
||||
if let std::option::Option::Some(value) = self.search_transaction_history {
|
||||
@@ -651,7 +647,6 @@ impl SolanaPrioritizationFee {
|
||||
}
|
||||
|
||||
/// Decodes one prioritization-fee sample from the Solana JSON wire shape.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn decode_wire(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<Self> {
|
||||
let decoded = crate::decode_wire_json::<WirePrioritizationFee>(method, value);
|
||||
return match decoded {
|
||||
@@ -728,7 +723,6 @@ impl SolanaSignatureInfo {
|
||||
}
|
||||
|
||||
/// Decodes one signature record from the Solana JSON wire shape.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn decode_wire(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<Self> {
|
||||
let decoded = crate::decode_wire_json::<WireSignatureInfo>(method, value);
|
||||
let wire = match decoded {
|
||||
@@ -794,7 +788,6 @@ impl SolanaSignatureStatus {
|
||||
}
|
||||
|
||||
/// Decodes one present signature status from the Solana JSON wire shape.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn decode_wire(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<Self> {
|
||||
let decoded = crate::decode_wire_json::<WireSignatureStatus>(method, value);
|
||||
let wire = match decoded {
|
||||
@@ -1102,6 +1095,10 @@ impl SolanaSimulateTransactionResult {
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_RECENT_PRIORITIZATION_FEE_ACCOUNTS: usize = 128;
|
||||
const MAX_SIGNATURES_FOR_ADDRESS_LIMIT: usize = 1_000;
|
||||
const MAX_SIGNATURE_STATUSES: usize = 256;
|
||||
|
||||
impl crate::HttpTransportPool {
|
||||
/// Executes typed `getFeeForMessage` through the common KSP HTTP transport path.
|
||||
pub async fn get_fee_for_message(
|
||||
@@ -1154,6 +1151,106 @@ impl crate::HttpTransportPool {
|
||||
};
|
||||
}
|
||||
|
||||
/// Executes typed `getRecentPrioritizationFees` through the common KSP HTTP transport path.
|
||||
pub async fn get_recent_prioritization_fees(
|
||||
&self,
|
||||
role: &crate::HttpRoleName,
|
||||
writable_accounts: std::option::Option<&[ksp_core_lib::Pubkey]>,
|
||||
) -> ksp_core_lib::Result<std::vec::Vec<crate::SolanaPrioritizationFee>> {
|
||||
if let std::option::Option::Some(writable_accounts) = writable_accounts
|
||||
&& writable_accounts.len() > MAX_RECENT_PRIORITIZATION_FEE_ACCOUNTS
|
||||
{
|
||||
return invalid_transaction_parameters(
|
||||
"getRecentPrioritizationFees",
|
||||
"getRecentPrioritizationFees accepts at most 128 account addresses",
|
||||
"account_count",
|
||||
writable_accounts.len(),
|
||||
);
|
||||
}
|
||||
let mut params = std::vec::Vec::new();
|
||||
if let std::option::Option::Some(writable_accounts) = writable_accounts {
|
||||
let mut addresses = std::vec::Vec::with_capacity(writable_accounts.len());
|
||||
for account in writable_accounts {
|
||||
addresses.push(serde_json::Value::String(account.to_string()));
|
||||
}
|
||||
params.push(serde_json::Value::Array(addresses));
|
||||
}
|
||||
let value = self.execute_transaction_rpc("getRecentPrioritizationFees", role, params).await;
|
||||
let value = match value {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return decode_prioritization_fees("getRecentPrioritizationFees", value);
|
||||
}
|
||||
|
||||
/// Executes typed `getSignaturesForAddress` through the common KSP HTTP transport path.
|
||||
pub async fn get_signatures_for_address(
|
||||
&self,
|
||||
role: &crate::HttpRoleName,
|
||||
address: &ksp_core_lib::Pubkey,
|
||||
config: std::option::Option<&crate::SolanaSignaturesForAddressConfig>,
|
||||
) -> ksp_core_lib::Result<std::vec::Vec<crate::SolanaSignatureInfo>> {
|
||||
if let std::option::Option::Some(limit) = config.and_then(crate::SolanaSignaturesForAddressConfig::limit)
|
||||
&& (limit == 0 || limit > MAX_SIGNATURES_FOR_ADDRESS_LIMIT)
|
||||
{
|
||||
return invalid_transaction_parameters("getSignaturesForAddress", "getSignaturesForAddress limit must be between 1 and 1000", "limit", limit);
|
||||
}
|
||||
let mut params = std::vec![serde_json::Value::String(address.to_string())];
|
||||
if let std::option::Option::Some(config) = config
|
||||
&& !config.is_empty()
|
||||
{
|
||||
params.push(config.to_json_value());
|
||||
}
|
||||
let value = self.execute_transaction_rpc("getSignaturesForAddress", role, params).await;
|
||||
let value = match value {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return decode_signature_infos("getSignaturesForAddress", value);
|
||||
}
|
||||
|
||||
/// Executes typed `getSignatureStatuses` through the common KSP HTTP transport path.
|
||||
pub async fn get_signature_statuses(
|
||||
&self,
|
||||
role: &crate::HttpRoleName,
|
||||
signatures: &[std::string::String],
|
||||
config: std::option::Option<&crate::SolanaSignatureStatusesConfig>,
|
||||
) -> ksp_core_lib::Result<crate::SolanaRpcResponse<std::vec::Vec<std::option::Option<crate::SolanaSignatureStatus>>>> {
|
||||
if signatures.len() > MAX_SIGNATURE_STATUSES {
|
||||
return invalid_transaction_parameters(
|
||||
"getSignatureStatuses",
|
||||
"getSignatureStatuses accepts at most 256 signatures",
|
||||
"signature_count",
|
||||
signatures.len(),
|
||||
);
|
||||
}
|
||||
let mut signature_values = std::vec::Vec::with_capacity(signatures.len());
|
||||
for signature in signatures {
|
||||
signature_values.push(serde_json::Value::String(signature.clone()));
|
||||
}
|
||||
let mut params = std::vec![serde_json::Value::Array(signature_values)];
|
||||
if let std::option::Option::Some(config) = config
|
||||
&& !config.is_empty()
|
||||
{
|
||||
params.push((*config).to_json_value());
|
||||
}
|
||||
let value = self.execute_transaction_rpc("getSignatureStatuses", role, params).await;
|
||||
let value = match value {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let contextual = decode_transaction_contextual_wire("getSignatureStatuses", value);
|
||||
let (context, value) = match contextual {
|
||||
std::result::Result::Ok(contextual) => contextual,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let statuses = decode_signature_statuses("getSignatureStatuses", value, signatures.len());
|
||||
return match statuses {
|
||||
std::result::Result::Ok(statuses) => std::result::Result::Ok(crate::SolanaRpcResponse::new(context, statuses)),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
}
|
||||
|
||||
/// Executes typed `getTransactionCount` through the common KSP HTTP transport path.
|
||||
pub async fn get_transaction_count(
|
||||
&self,
|
||||
@@ -1232,6 +1329,84 @@ fn decode_transaction_contextual_wire(method: &str, value: serde_json::Value) ->
|
||||
};
|
||||
}
|
||||
|
||||
fn decode_prioritization_fees(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<std::vec::Vec<crate::SolanaPrioritizationFee>> {
|
||||
let decoded = crate::decode_wire_json::<std::vec::Vec<serde_json::Value>>(method, value);
|
||||
let values = match decoded {
|
||||
std::result::Result::Ok(values) => values,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let mut fees = std::vec::Vec::with_capacity(values.len());
|
||||
for value in values {
|
||||
let fee = crate::SolanaPrioritizationFee::decode_wire(method, value);
|
||||
match fee {
|
||||
std::result::Result::Ok(fee) => fees.push(fee),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
}
|
||||
return std::result::Result::Ok(fees);
|
||||
}
|
||||
|
||||
fn decode_signature_infos(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<std::vec::Vec<crate::SolanaSignatureInfo>> {
|
||||
let decoded = crate::decode_wire_json::<std::vec::Vec<serde_json::Value>>(method, value);
|
||||
let values = match decoded {
|
||||
std::result::Result::Ok(values) => values,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let mut infos = std::vec::Vec::with_capacity(values.len());
|
||||
for value in values {
|
||||
let info = crate::SolanaSignatureInfo::decode_wire(method, value);
|
||||
match info {
|
||||
std::result::Result::Ok(info) => infos.push(info),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
}
|
||||
return std::result::Result::Ok(infos);
|
||||
}
|
||||
|
||||
fn decode_signature_statuses(
|
||||
method: &str,
|
||||
value: serde_json::Value,
|
||||
expected_count: usize,
|
||||
) -> ksp_core_lib::Result<std::vec::Vec<std::option::Option<crate::SolanaSignatureStatus>>> {
|
||||
let decoded = crate::decode_wire_json::<std::vec::Vec<std::option::Option<serde_json::Value>>>(method, value);
|
||||
let values = match decoded {
|
||||
std::result::Result::Ok(values) => values,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
if values.len() != expected_count {
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_INVALID_RESPONSE, "getSignatureStatuses result count does not match the requested signature count")
|
||||
.with_context("rpc_method", method)
|
||||
.with_context("expected_count", expected_count.to_string())
|
||||
.with_context("actual_count", values.len().to_string()),
|
||||
);
|
||||
}
|
||||
let mut statuses = std::vec::Vec::with_capacity(values.len());
|
||||
for value in values {
|
||||
let value = match value {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
statuses.push(std::option::Option::None);
|
||||
continue;
|
||||
},
|
||||
};
|
||||
let status = crate::SolanaSignatureStatus::decode_wire(method, value);
|
||||
match status {
|
||||
std::result::Result::Ok(status) => statuses.push(std::option::Option::Some(status)),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
}
|
||||
return std::result::Result::Ok(statuses);
|
||||
}
|
||||
|
||||
fn invalid_transaction_parameters<T>(method: &str, message: &str, field: &'static str, value: usize) -> ksp_core_lib::Result<T> {
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_INVALID_RPC_PARAMETERS, message)
|
||||
.with_context("rpc_method", method)
|
||||
.with_context(field, value.to_string()),
|
||||
);
|
||||
}
|
||||
|
||||
fn transaction_descriptor(method: &str) -> ksp_core_lib::Result<&'static crate::HttpRpcMethodDescriptor> {
|
||||
let descriptor = crate::find_http_rpc_method(method);
|
||||
return match descriptor {
|
||||
@@ -1257,7 +1432,6 @@ fn invalid_transaction_wire(method: &str, field: &str, message: &'static str) ->
|
||||
return ksp_core_lib::Error::new(crate::ERROR_CODE_INVALID_RESPONSE, message).with_context("rpc_method", method).with_context("field", field);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn decode_confirmation_status(
|
||||
method: &str,
|
||||
field: &str,
|
||||
@@ -1386,7 +1560,6 @@ struct WireLatestBlockhash {
|
||||
last_valid_block_height: u64,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct WirePrioritizationFee {
|
||||
@@ -1394,7 +1567,6 @@ struct WirePrioritizationFee {
|
||||
prioritization_fee: u64,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct WireSignatureInfo {
|
||||
@@ -1412,7 +1584,6 @@ struct WireSignatureInfo {
|
||||
transaction_index: std::option::Option<u32>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct WireSignatureStatus {
|
||||
|
||||
Reference in New Issue
Block a user