Skip to content

Commit 1f09bb2

Browse files
feat(core): Enhance block retrieval with configuration support (#218)
1 parent 8e7391d commit 1f09bb2

5 files changed

Lines changed: 349 additions & 90 deletions

File tree

crates/core/src/error.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ use crossbeam_channel::TrySendError;
44
use jsonrpc_core::{Error, Result};
55
use serde::Serialize;
66
use serde_json::json;
7-
use solana_client::rpc_request::TokenAccountsFilter;
7+
use solana_client::{client_error::ClientError, rpc_request::TokenAccountsFilter};
88
use solana_pubkey::Pubkey;
9+
use solana_sdk::slot_history::Slot;
910

1011
pub type SurfpoolResult<T> = std::result::Result<T, SurfpoolError>;
1112

@@ -367,4 +368,18 @@ impl SurfpoolError {
367368
"sigVerify may not be used with replaceRecentBlockhash",
368369
))
369370
}
371+
372+
pub fn slot_too_old(slot: Slot) -> Self {
373+
Self(Error::invalid_params(format!(
374+
"Requested {slot} is before the first local slot, and no remote RPC was provided."
375+
)))
376+
}
377+
378+
pub fn get_block(e: ClientError, block: Slot) -> Self {
379+
let mut error = Error::internal_error();
380+
error.data = Some(json!(format!(
381+
"Failed to get block {block} from remote: {e}"
382+
)));
383+
Self(error)
384+
}
370385
}

crates/core/src/rpc/full.rs

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -720,7 +720,7 @@ pub trait Full {
720720
/// {
721721
/// "jsonrpc": "2.0",
722722
/// "id": 1,
723-
/// "result": 1620000000
723+
/// "result": 1752080472
724724
/// }
725725
/// ```
726726
///
@@ -1737,24 +1737,44 @@ impl Full for SurfpoolFullRpc {
17371737
&self,
17381738
meta: Self::Metadata,
17391739
slot: Slot,
1740-
_config: Option<RpcEncodingConfigWrapper<RpcBlockConfig>>,
1740+
config: Option<RpcEncodingConfigWrapper<RpcBlockConfig>>,
17411741
) -> BoxFuture<Result<Option<UiConfirmedBlock>>> {
1742-
let svm_locker = match meta.get_svm_locker() {
1743-
Ok(locker) => locker,
1742+
let config = config.map(|c| c.convert_to_current()).unwrap_or_default();
1743+
1744+
let SurfnetRpcContext {
1745+
svm_locker,
1746+
remote_ctx,
1747+
} = match meta.get_rpc_context(config.commitment) {
1748+
Ok(res) => res,
17441749
Err(e) => return e.into(),
17451750
};
17461751

17471752
Box::pin(async move {
1748-
Ok(svm_locker.with_svm_reader(|svm_reader| svm_reader.get_block_at_slot(slot)))
1753+
let remote_client = remote_ctx.as_ref().map(|(client, _)| client.clone());
1754+
let result = svm_locker.get_block(&remote_client, &slot, &config).await;
1755+
Ok(result?.inner)
17491756
})
17501757
}
17511758

17521759
fn get_block_time(
17531760
&self,
1754-
_meta: Self::Metadata,
1755-
_slot: Slot,
1761+
meta: Self::Metadata,
1762+
slot: Slot,
17561763
) -> BoxFuture<Result<Option<UnixTimestamp>>> {
1757-
Box::pin(async { Ok(None) })
1764+
let svm_locker = match meta.get_svm_locker() {
1765+
Ok(locker) => locker,
1766+
Err(e) => return e.into(),
1767+
};
1768+
1769+
Box::pin(async move {
1770+
let block_time = svm_locker.with_svm_reader(|svm_reader| {
1771+
svm_reader
1772+
.blocks
1773+
.get(&slot)
1774+
.map(|block| (block.block_time / 1000) as UnixTimestamp)
1775+
});
1776+
Ok(block_time)
1777+
})
17581778
}
17591779

17601780
fn get_blocks(
@@ -1771,7 +1791,10 @@ impl Full for SurfpoolFullRpc {
17711791
};
17721792

17731793
let config = config.unwrap_or_default();
1774-
let commitment = config.commitment.unwrap_or_default();
1794+
// get blocks should default to processed rather than finalized to default to the most recent
1795+
let commitment = config.commitment.unwrap_or(CommitmentConfig {
1796+
commitment: CommitmentLevel::Processed,
1797+
});
17751798

17761799
const MAX_SLOT_RANGE: u64 = 500_000;
17771800
if let Some(end) = end_slot {
@@ -2936,7 +2959,7 @@ mod tests {
29362959
assert_eq!(res, None);
29372960
}
29382961

2939-
#[tokio::test]
2962+
#[tokio::test(flavor = "multi_thread")]
29402963
async fn test_get_block_time() {
29412964
let setup = TestSetup::new(SurfpoolFullRpc);
29422965
let res = setup
@@ -3656,7 +3679,6 @@ mod tests {
36563679

36573680
{
36583681
let mut svm_writer = setup.context.svm_locker.0.write().await;
3659-
36603682
for slot in 100..=110 {
36613683
svm_writer.blocks.insert(
36623684
slot,
@@ -3670,7 +3692,6 @@ mod tests {
36703692
},
36713693
);
36723694
}
3673-
36743695
svm_writer.latest_epoch_info.absolute_slot = 110;
36753696
}
36763697

@@ -3682,7 +3703,7 @@ mod tests {
36823703
100,
36833704
Some(RpcBlocksConfigWrapper::EndSlotOnly(Some(105))),
36843705
Some(RpcContextConfig {
3685-
commitment: None,
3706+
commitment: Some(CommitmentConfig::finalized()),
36863707
min_context_slot: Some(105),
36873708
}),
36883709
)

crates/core/src/surfnet/locker.rs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use solana_account_decoder::{
1515
use solana_address_lookup_table_interface::state::AddressLookupTable;
1616
use solana_client::{
1717
rpc_config::{
18-
RpcAccountInfoConfig, RpcLargestAccountsConfig, RpcLargestAccountsFilter,
18+
RpcAccountInfoConfig, RpcBlockConfig, RpcLargestAccountsConfig, RpcLargestAccountsFilter,
1919
RpcSignaturesForAddressConfig,
2020
},
2121
rpc_filter::RpcFilterType,
@@ -44,7 +44,8 @@ use solana_signature::Signature;
4444
use solana_transaction_error::TransactionError;
4545
use solana_transaction_status::{
4646
EncodedConfirmedTransactionWithStatusMeta,
47-
TransactionConfirmationStatus as SolanaTransactionConfirmationStatus, UiTransactionEncoding,
47+
TransactionConfirmationStatus as SolanaTransactionConfirmationStatus, UiConfirmedBlock,
48+
UiTransactionEncoding,
4849
};
4950
use spl_token::state::Mint;
5051
use spl_token_2022::extension::StateWithExtensions;
@@ -1752,6 +1753,47 @@ impl SurfnetSvmLocker {
17521753
}
17531754

17541755
impl SurfnetSvmLocker {
1756+
pub fn get_first_local_slot(&self) -> Option<Slot> {
1757+
self.with_svm_reader(|svm_reader| svm_reader.blocks.keys().min().copied())
1758+
}
1759+
1760+
pub async fn get_block(
1761+
&self,
1762+
remote_ctx: &Option<SurfnetRemoteClient>,
1763+
slot: &Slot,
1764+
config: &RpcBlockConfig,
1765+
) -> SurfpoolContextualizedResult<Option<UiConfirmedBlock>> {
1766+
let first_local_slot = self.get_first_local_slot();
1767+
1768+
let result = if first_local_slot.is_some() && first_local_slot.unwrap() > *slot {
1769+
match remote_ctx {
1770+
Some(remote_client) => Some(remote_client.get_block(slot, *config).await?),
1771+
None => return Err(SurfpoolError::slot_too_old(*slot)),
1772+
}
1773+
} else {
1774+
self.get_block_local(slot, config).inner
1775+
};
1776+
1777+
Ok(SvmAccessContext {
1778+
slot: *slot,
1779+
latest_epoch_info: self.get_epoch_info(),
1780+
latest_blockhash: self
1781+
.get_latest_blockhash(&CommitmentConfig::processed())
1782+
.unwrap_or_default(),
1783+
inner: result,
1784+
})
1785+
}
1786+
1787+
pub fn get_block_local(
1788+
&self,
1789+
slot: &Slot,
1790+
config: &RpcBlockConfig,
1791+
) -> SvmAccessContext<Option<UiConfirmedBlock>> {
1792+
self.with_contextualized_svm_reader(|svm_reader| {
1793+
svm_reader.get_block_at_slot(*slot, config)
1794+
})
1795+
}
1796+
17551797
pub fn get_genesis_hash_local(&self) -> SvmAccessContext<Hash> {
17561798
self.with_contextualized_svm_reader(|svm_reader| svm_reader.genesis_config.hash())
17571799
}
@@ -1786,6 +1828,12 @@ impl SurfnetSvmLocker {
17861828
self.with_svm_reader(|svm_reader| svm_reader.get_latest_absolute_slot())
17871829
}
17881830

1831+
/// Retrieves the latest blockhash for the given commitment config from the underlying SVM.
1832+
pub fn get_latest_blockhash(&self, config: &CommitmentConfig) -> Option<Hash> {
1833+
let slot = self.get_slot_for_commitment(config);
1834+
self.with_svm_reader(|svm_reader| svm_reader.blockhash_for_slot(slot))
1835+
}
1836+
17891837
pub fn get_slot_for_commitment(&self, commitment: &CommitmentConfig) -> Slot {
17901838
self.with_svm_reader(|svm_reader| {
17911839
let slot = svm_reader.get_latest_absolute_slot();

crates/core/src/surfnet/remote.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use solana_client::{
66
nonblocking::rpc_client::RpcClient,
77
rpc_client::GetConfirmedSignaturesForAddress2Config,
88
rpc_config::{
9-
RpcAccountInfoConfig, RpcLargestAccountsConfig, RpcProgramAccountsConfig,
9+
RpcAccountInfoConfig, RpcBlockConfig, RpcLargestAccountsConfig, RpcProgramAccountsConfig,
1010
RpcSignaturesForAddressConfig, RpcTokenAccountsFilter,
1111
},
1212
rpc_filter::RpcFilterType,
@@ -16,13 +16,14 @@ use solana_client::{
1616
RpcTokenAccountBalance,
1717
},
1818
};
19+
use solana_clock::Slot;
1920
use solana_commitment_config::CommitmentConfig;
2021
use solana_epoch_info::EpochInfo;
2122
use solana_hash::Hash;
2223
use solana_pubkey::Pubkey;
2324
use solana_sdk::bpf_loader_upgradeable::get_program_data_address;
2425
use solana_signature::Signature;
25-
use solana_transaction_status::UiTransactionEncoding;
26+
use solana_transaction_status::{UiConfirmedBlock, UiTransactionEncoding};
2627

2728
use super::GetTransactionResult;
2829
use crate::{
@@ -304,6 +305,17 @@ impl SurfnetRemoteClient {
304305
.await
305306
.map_err(SurfpoolError::get_signatures_for_address)
306307
}
308+
309+
pub async fn get_block(
310+
&self,
311+
slot: &Slot,
312+
config: RpcBlockConfig,
313+
) -> SurfpoolResult<UiConfirmedBlock> {
314+
self.client
315+
.get_block_with_config(*slot, config)
316+
.await
317+
.map_err(|e| SurfpoolError::get_block(e, *slot))
318+
}
307319
}
308320

309321
/// Handles remote RPC calls, returning a `RemoteRpcResult` indicating whether the method was supported.

0 commit comments

Comments
 (0)