2023-12-13 06:57:50 -08:00
|
|
|
use async_stream::stream;
|
|
|
|
use futures::{Stream, StreamExt};
|
2023-12-14 01:58:26 -08:00
|
|
|
use itertools::Itertools;
|
2023-12-14 09:39:12 -08:00
|
|
|
use log::{debug, info, warn};
|
2023-12-13 06:57:50 -08:00
|
|
|
use solana_sdk::clock::Slot;
|
|
|
|
use solana_sdk::commitment_config::CommitmentConfig;
|
2023-12-14 01:58:26 -08:00
|
|
|
use std::collections::HashMap;
|
2023-12-14 03:58:47 -08:00
|
|
|
use std::pin::{pin, Pin};
|
2023-12-14 01:58:26 -08:00
|
|
|
use tokio::task::JoinHandle;
|
|
|
|
use tokio::time::{sleep, Duration};
|
2023-12-14 03:58:47 -08:00
|
|
|
use yellowstone_grpc_client::{GeyserGrpcClient, GeyserGrpcClientError, GeyserGrpcClientResult};
|
2023-12-13 06:57:50 -08:00
|
|
|
use yellowstone_grpc_proto::geyser::subscribe_update::UpdateOneof;
|
2023-12-14 01:58:26 -08:00
|
|
|
use yellowstone_grpc_proto::geyser::SubscribeUpdateBlockMeta;
|
|
|
|
use yellowstone_grpc_proto::geyser::{
|
|
|
|
CommitmentLevel, SubscribeRequestFilterBlocks, SubscribeUpdate,
|
|
|
|
};
|
|
|
|
use yellowstone_grpc_proto::prelude::SubscribeRequestFilterBlocksMeta;
|
2023-12-13 06:57:50 -08:00
|
|
|
use yellowstone_grpc_proto::tonic::transport::ClientTlsConfig;
|
2023-12-14 03:58:47 -08:00
|
|
|
use yellowstone_grpc_proto::tonic::{async_trait, Status};
|
2023-12-15 01:09:13 -08:00
|
|
|
use crate::grpc_subscription_autoreconnect::{create_geyser_reconnecting_stream, GrpcSourceConfig};
|
2023-12-14 03:58:47 -08:00
|
|
|
|
|
|
|
|
|
|
|
pub trait FromYellowstoneMapper {
|
2023-12-14 04:09:43 -08:00
|
|
|
// Target is something like ProducedBlock
|
2023-12-14 03:58:47 -08:00
|
|
|
type Target;
|
2023-12-14 04:09:43 -08:00
|
|
|
fn map_yellowstone_update(&self, update: SubscribeUpdate) -> Option<(Slot, Self::Target)>;
|
2023-12-14 01:58:26 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
struct ExtractBlock(CommitmentConfig);
|
|
|
|
|
|
|
|
struct ExtractBlockMeta(CommitmentConfig);
|
2023-12-13 06:57:50 -08:00
|
|
|
|
|
|
|
|
2023-12-14 01:58:26 -08:00
|
|
|
pub fn create_multiplex<E>(
|
2023-12-15 01:09:13 -08:00
|
|
|
// TODO provide list of streams
|
2023-12-13 06:57:50 -08:00
|
|
|
grpc_sources: Vec<GrpcSourceConfig>,
|
2023-12-14 01:13:48 -08:00
|
|
|
commitment_config: CommitmentConfig,
|
2023-12-14 01:58:26 -08:00
|
|
|
extractor: E,
|
2023-12-14 03:58:47 -08:00
|
|
|
) -> impl Stream<Item = E::Target>
|
2023-12-14 01:58:26 -08:00
|
|
|
where
|
2023-12-14 03:58:47 -08:00
|
|
|
E: FromYellowstoneMapper,
|
2023-12-14 01:58:26 -08:00
|
|
|
{
|
2023-12-13 06:57:50 -08:00
|
|
|
assert!(
|
2023-12-14 01:13:48 -08:00
|
|
|
commitment_config == CommitmentConfig::confirmed()
|
|
|
|
|| commitment_config == CommitmentConfig::finalized(),
|
2023-12-13 06:57:50 -08:00
|
|
|
"Only CONFIRMED and FINALIZED is supported");
|
|
|
|
// note: PROCESSED blocks are not sequential in presense of forks; this will break the logic
|
|
|
|
|
2023-12-14 01:58:26 -08:00
|
|
|
if grpc_sources.len() < 1 {
|
2023-12-13 06:57:50 -08:00
|
|
|
panic!("Must have at least one source");
|
|
|
|
}
|
|
|
|
|
2023-12-14 01:58:26 -08:00
|
|
|
info!(
|
|
|
|
"Starting multiplexer with {} sources: {}",
|
|
|
|
grpc_sources.len(),
|
|
|
|
grpc_sources
|
|
|
|
.iter()
|
|
|
|
.map(|source| source.label.clone())
|
|
|
|
.join(", ")
|
|
|
|
);
|
|
|
|
|
|
|
|
let mut futures = futures::stream::SelectAll::new();
|
|
|
|
|
|
|
|
for grpc_source in grpc_sources {
|
|
|
|
futures.push(Box::pin(create_geyser_reconnecting_stream(
|
|
|
|
grpc_source.clone(),
|
|
|
|
commitment_config,
|
|
|
|
)));
|
|
|
|
}
|
2023-12-13 06:57:50 -08:00
|
|
|
|
2023-12-14 04:09:43 -08:00
|
|
|
map_updates(futures, extractor)
|
2023-12-14 01:58:26 -08:00
|
|
|
}
|
2023-12-13 06:57:50 -08:00
|
|
|
|
2023-12-14 04:09:43 -08:00
|
|
|
fn map_updates<S, E>(geyser_stream: S, mapper: E) -> impl Stream<Item = E::Target>
|
2023-12-14 01:58:26 -08:00
|
|
|
where
|
|
|
|
S: Stream<Item = Option<SubscribeUpdate>>,
|
2023-12-14 03:58:47 -08:00
|
|
|
E: FromYellowstoneMapper,
|
2023-12-14 01:58:26 -08:00
|
|
|
{
|
2023-12-14 04:09:43 -08:00
|
|
|
let mut tip: Slot = 0;
|
2023-12-14 01:58:26 -08:00
|
|
|
stream! {
|
|
|
|
for await update in geyser_stream {
|
2023-12-14 09:39:12 -08:00
|
|
|
match update {
|
|
|
|
Some(update) => {
|
|
|
|
// take only the update messages we want
|
|
|
|
if let Some((proposed_slot, block)) = mapper.map_yellowstone_update(update) {
|
|
|
|
if proposed_slot > tip {
|
|
|
|
tip = proposed_slot;
|
|
|
|
yield block;
|
|
|
|
}
|
2023-12-14 04:09:43 -08:00
|
|
|
}
|
2023-12-13 06:57:50 -08:00
|
|
|
}
|
2023-12-14 09:39:12 -08:00
|
|
|
None => {
|
|
|
|
debug!("Stream sent None"); // TODO waht does that mean?
|
|
|
|
}
|
2023-12-13 06:57:50 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|