2019-02-17 08:38:36 -08:00
|
|
|
//! The `pubsub` module implements a threaded subscription service on client RPC request
|
|
|
|
|
2019-03-06 14:31:16 -08:00
|
|
|
use core::hash::Hash;
|
2019-02-17 08:38:36 -08:00
|
|
|
use jsonrpc_core::futures::Future;
|
2020-03-24 09:53:32 -07:00
|
|
|
use jsonrpc_pubsub::{
|
|
|
|
typed::{Sink, Subscriber},
|
|
|
|
SubscriptionId,
|
|
|
|
};
|
2019-05-06 07:31:50 -07:00
|
|
|
use serde::Serialize;
|
2020-06-30 21:55:11 -07:00
|
|
|
use solana_account_decoder::{UiAccount, UiAccountEncoding};
|
2020-07-23 12:38:28 -07:00
|
|
|
use solana_client::{
|
|
|
|
rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig},
|
|
|
|
rpc_filter::RpcFilterType,
|
|
|
|
rpc_response::{Response, RpcKeyedAccount, RpcResponseContext, RpcSignatureResult},
|
2020-04-04 16:13:26 -07:00
|
|
|
};
|
2020-07-17 08:24:51 -07:00
|
|
|
use solana_runtime::{
|
|
|
|
bank::Bank,
|
|
|
|
bank_forks::BankForks,
|
2020-07-17 10:54:49 -07:00
|
|
|
commitment::{BlockCommitmentCache, CommitmentSlots},
|
2020-07-17 08:24:51 -07:00
|
|
|
};
|
2019-11-02 00:38:30 -07:00
|
|
|
use solana_sdk::{
|
2020-05-06 23:23:06 -07:00
|
|
|
account::Account,
|
2020-05-17 14:01:08 -07:00
|
|
|
clock::{Slot, UnixTimestamp},
|
2020-05-06 23:23:06 -07:00
|
|
|
commitment_config::{CommitmentConfig, CommitmentLevel},
|
|
|
|
pubkey::Pubkey,
|
|
|
|
signature::Signature,
|
|
|
|
transaction,
|
2019-11-02 00:38:30 -07:00
|
|
|
};
|
2020-05-17 14:01:08 -07:00
|
|
|
use solana_vote_program::vote_state::Vote;
|
2020-03-30 16:53:25 -07:00
|
|
|
use std::sync::{
|
|
|
|
atomic::{AtomicBool, Ordering},
|
|
|
|
mpsc::{Receiver, RecvTimeoutError, SendError, Sender},
|
|
|
|
};
|
2020-01-20 13:08:29 -08:00
|
|
|
use std::thread::{Builder, JoinHandle};
|
|
|
|
use std::time::Duration;
|
2020-01-15 10:52:02 -08:00
|
|
|
use std::{
|
2020-02-13 09:00:50 -08:00
|
|
|
collections::{HashMap, HashSet},
|
2020-02-25 20:23:54 -08:00
|
|
|
iter,
|
2020-01-20 13:08:29 -08:00
|
|
|
sync::{Arc, Mutex, RwLock},
|
2020-01-15 10:52:02 -08:00
|
|
|
};
|
2020-07-23 08:35:23 -07:00
|
|
|
|
|
|
|
// Stuck on tokio 0.1 until the jsonrpc-pubsub crate upgrades to tokio 0.2
|
|
|
|
use tokio_01::runtime::{Builder as RuntimeBuilder, Runtime, TaskExecutor};
|
2019-02-17 08:38:36 -08:00
|
|
|
|
2020-01-20 13:08:29 -08:00
|
|
|
const RECEIVE_DELAY_MILLIS: u64 = 100;
|
|
|
|
|
2020-02-25 20:23:54 -08:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Copy, Debug)]
|
2019-11-26 00:42:54 -08:00
|
|
|
pub struct SlotInfo {
|
|
|
|
pub slot: Slot,
|
|
|
|
pub parent: Slot,
|
|
|
|
pub root: Slot,
|
|
|
|
}
|
|
|
|
|
2020-05-17 14:01:08 -07:00
|
|
|
// A more human-friendly version of Vote, with the bank state signature base58 encoded.
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
|
|
pub struct RpcVote {
|
|
|
|
pub slots: Vec<Slot>,
|
|
|
|
pub hash: String,
|
|
|
|
pub timestamp: Option<UnixTimestamp>,
|
|
|
|
}
|
|
|
|
|
2020-01-20 13:08:29 -08:00
|
|
|
enum NotificationEntry {
|
|
|
|
Slot(SlotInfo),
|
2020-05-17 14:01:08 -07:00
|
|
|
Vote(Vote),
|
2020-03-27 09:33:40 -07:00
|
|
|
Root(Slot),
|
2020-06-17 09:44:51 -07:00
|
|
|
Frozen(Slot),
|
2020-07-17 10:54:49 -07:00
|
|
|
Bank(CommitmentSlots),
|
2020-05-22 12:55:17 -07:00
|
|
|
Gossip(Slot),
|
2020-01-20 13:08:29 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl std::fmt::Debug for NotificationEntry {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
match self {
|
2020-03-27 09:33:40 -07:00
|
|
|
NotificationEntry::Root(root) => write!(f, "Root({})", root),
|
2020-06-17 09:44:51 -07:00
|
|
|
NotificationEntry::Frozen(slot) => write!(f, "Frozen({})", slot),
|
2020-05-17 14:01:08 -07:00
|
|
|
NotificationEntry::Vote(vote) => write!(f, "Vote({:?})", vote),
|
2020-01-20 13:08:29 -08:00
|
|
|
NotificationEntry::Slot(slot_info) => write!(f, "Slot({:?})", slot_info),
|
2020-07-17 10:54:49 -07:00
|
|
|
NotificationEntry::Bank(commitment_slots) => {
|
|
|
|
write!(f, "Bank({{slot: {:?}}})", commitment_slots.slot)
|
2020-07-17 08:24:51 -07:00
|
|
|
}
|
2020-05-22 12:55:17 -07:00
|
|
|
NotificationEntry::Gossip(slot) => write!(f, "Gossip({:?})", slot),
|
2020-01-20 13:08:29 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-23 12:38:28 -07:00
|
|
|
struct SubscriptionData<S, T> {
|
2020-05-06 23:23:06 -07:00
|
|
|
sink: Sink<S>,
|
|
|
|
commitment: CommitmentConfig,
|
|
|
|
last_notified_slot: RwLock<Slot>,
|
2020-07-23 12:38:28 -07:00
|
|
|
config: Option<T>,
|
2020-05-06 23:23:06 -07:00
|
|
|
}
|
2020-07-23 12:38:28 -07:00
|
|
|
#[derive(Default, Clone)]
|
|
|
|
struct ProgramConfig {
|
|
|
|
filters: Vec<RpcFilterType>,
|
|
|
|
encoding: Option<UiAccountEncoding>,
|
|
|
|
}
|
|
|
|
type RpcAccountSubscriptions = RwLock<
|
|
|
|
HashMap<
|
|
|
|
Pubkey,
|
|
|
|
HashMap<SubscriptionId, SubscriptionData<Response<UiAccount>, UiAccountEncoding>>,
|
|
|
|
>,
|
|
|
|
>;
|
|
|
|
type RpcProgramSubscriptions = RwLock<
|
|
|
|
HashMap<
|
|
|
|
Pubkey,
|
|
|
|
HashMap<SubscriptionId, SubscriptionData<Response<RpcKeyedAccount>, ProgramConfig>>,
|
|
|
|
>,
|
|
|
|
>;
|
2019-05-06 07:31:50 -07:00
|
|
|
type RpcSignatureSubscriptions = RwLock<
|
2020-07-23 12:38:28 -07:00
|
|
|
HashMap<Signature, HashMap<SubscriptionId, SubscriptionData<Response<RpcSignatureResult>, ()>>>,
|
2019-05-06 07:31:50 -07:00
|
|
|
>;
|
2019-11-26 00:42:54 -08:00
|
|
|
type RpcSlotSubscriptions = RwLock<HashMap<SubscriptionId, Sink<SlotInfo>>>;
|
2020-05-17 14:01:08 -07:00
|
|
|
type RpcVoteSubscriptions = RwLock<HashMap<SubscriptionId, Sink<RpcVote>>>;
|
2020-03-27 09:33:40 -07:00
|
|
|
type RpcRootSubscriptions = RwLock<HashMap<SubscriptionId, Sink<Slot>>>;
|
2019-03-06 14:31:16 -08:00
|
|
|
|
2020-07-23 12:38:28 -07:00
|
|
|
fn add_subscription<K, S, T>(
|
|
|
|
subscriptions: &mut HashMap<K, HashMap<SubscriptionId, SubscriptionData<S, T>>>,
|
2020-03-24 09:53:32 -07:00
|
|
|
hashmap_key: K,
|
2020-05-06 23:23:06 -07:00
|
|
|
commitment: Option<CommitmentConfig>,
|
2020-03-24 09:53:32 -07:00
|
|
|
sub_id: SubscriptionId,
|
|
|
|
subscriber: Subscriber<S>,
|
2020-05-06 23:23:06 -07:00
|
|
|
last_notified_slot: Slot,
|
2020-07-23 12:38:28 -07:00
|
|
|
config: Option<T>,
|
2019-03-06 14:31:16 -08:00
|
|
|
) where
|
2020-03-24 09:53:32 -07:00
|
|
|
K: Eq + Hash,
|
2019-03-06 14:31:16 -08:00
|
|
|
S: Clone,
|
|
|
|
{
|
2020-03-24 09:53:32 -07:00
|
|
|
let sink = subscriber.assign_id(sub_id.clone()).unwrap();
|
2020-05-21 09:33:24 -07:00
|
|
|
let commitment = commitment.unwrap_or_else(CommitmentConfig::single);
|
2020-05-06 23:23:06 -07:00
|
|
|
let subscription_data = SubscriptionData {
|
|
|
|
sink,
|
|
|
|
commitment,
|
|
|
|
last_notified_slot: RwLock::new(last_notified_slot),
|
2020-07-23 12:38:28 -07:00
|
|
|
config,
|
2020-05-06 23:23:06 -07:00
|
|
|
};
|
2020-03-24 09:53:32 -07:00
|
|
|
if let Some(current_hashmap) = subscriptions.get_mut(&hashmap_key) {
|
2020-05-06 23:23:06 -07:00
|
|
|
current_hashmap.insert(sub_id, subscription_data);
|
2019-03-06 14:31:16 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
let mut hashmap = HashMap::new();
|
2020-05-06 23:23:06 -07:00
|
|
|
hashmap.insert(sub_id, subscription_data);
|
2020-03-24 09:53:32 -07:00
|
|
|
subscriptions.insert(hashmap_key, hashmap);
|
2019-03-06 14:31:16 -08:00
|
|
|
}
|
|
|
|
|
2020-07-23 12:38:28 -07:00
|
|
|
fn remove_subscription<K, S, T>(
|
|
|
|
subscriptions: &mut HashMap<K, HashMap<SubscriptionId, SubscriptionData<S, T>>>,
|
2019-03-06 14:31:16 -08:00
|
|
|
sub_id: &SubscriptionId,
|
|
|
|
) -> bool
|
|
|
|
where
|
2020-03-24 09:53:32 -07:00
|
|
|
K: Eq + Hash,
|
2019-03-06 14:31:16 -08:00
|
|
|
S: Clone,
|
|
|
|
{
|
|
|
|
let mut found = false;
|
|
|
|
subscriptions.retain(|_, v| {
|
|
|
|
v.retain(|k, _| {
|
2020-03-24 09:53:32 -07:00
|
|
|
let retain = k != sub_id;
|
2020-02-11 17:09:40 -08:00
|
|
|
if !retain {
|
2019-03-06 14:31:16 -08:00
|
|
|
found = true;
|
|
|
|
}
|
2020-02-11 17:09:40 -08:00
|
|
|
retain
|
2019-03-06 14:31:16 -08:00
|
|
|
});
|
|
|
|
!v.is_empty()
|
|
|
|
});
|
|
|
|
found
|
|
|
|
}
|
|
|
|
|
2020-03-23 05:34:42 -07:00
|
|
|
#[allow(clippy::type_complexity)]
|
2020-07-23 12:38:28 -07:00
|
|
|
fn check_commitment_and_notify<K, S, B, F, X, T>(
|
|
|
|
subscriptions: &HashMap<K, HashMap<SubscriptionId, SubscriptionData<Response<S>, T>>>,
|
2019-05-06 07:31:50 -07:00
|
|
|
hashmap_key: &K,
|
|
|
|
bank_forks: &Arc<RwLock<BankForks>>,
|
2020-07-17 10:54:49 -07:00
|
|
|
commitment_slots: &CommitmentSlots,
|
2020-02-25 20:23:54 -08:00
|
|
|
bank_method: B,
|
|
|
|
filter_results: F,
|
|
|
|
notifier: &RpcNotifier,
|
2020-02-13 09:00:50 -08:00
|
|
|
) -> HashSet<SubscriptionId>
|
|
|
|
where
|
2019-05-06 07:31:50 -07:00
|
|
|
K: Eq + Hash + Clone + Copy,
|
|
|
|
S: Clone + Serialize,
|
2020-02-25 20:23:54 -08:00
|
|
|
B: Fn(&Bank, &K) -> X,
|
2020-07-23 12:38:28 -07:00
|
|
|
F: Fn(X, Slot, Option<T>) -> (Box<dyn Iterator<Item = S>>, Slot),
|
2020-05-06 23:23:06 -07:00
|
|
|
X: Clone + Serialize + Default,
|
2020-07-23 12:38:28 -07:00
|
|
|
T: Clone,
|
2019-05-06 07:31:50 -07:00
|
|
|
{
|
2020-02-13 09:00:50 -08:00
|
|
|
let mut notified_set: HashSet<SubscriptionId> = HashSet::new();
|
2019-05-06 07:31:50 -07:00
|
|
|
if let Some(hashmap) = subscriptions.get(hashmap_key) {
|
2020-05-06 23:23:06 -07:00
|
|
|
for (
|
|
|
|
sub_id,
|
|
|
|
SubscriptionData {
|
|
|
|
sink,
|
|
|
|
commitment,
|
|
|
|
last_notified_slot,
|
2020-07-23 12:38:28 -07:00
|
|
|
config,
|
2020-05-06 23:23:06 -07:00
|
|
|
},
|
|
|
|
) in hashmap.iter()
|
|
|
|
{
|
|
|
|
let slot = match commitment.commitment {
|
2020-07-17 10:54:49 -07:00
|
|
|
CommitmentLevel::Max => commitment_slots.highest_confirmed_root,
|
|
|
|
CommitmentLevel::Recent => commitment_slots.slot,
|
|
|
|
CommitmentLevel::Root => commitment_slots.root,
|
2020-05-22 12:55:17 -07:00
|
|
|
CommitmentLevel::Single | CommitmentLevel::SingleGossip => {
|
2020-07-17 10:54:49 -07:00
|
|
|
commitment_slots.highest_confirmed_slot
|
2020-05-22 12:55:17 -07:00
|
|
|
}
|
2020-03-30 16:53:25 -07:00
|
|
|
};
|
2020-05-06 23:23:06 -07:00
|
|
|
let results = {
|
|
|
|
let bank_forks = bank_forks.read().unwrap();
|
|
|
|
bank_forks
|
|
|
|
.get(slot)
|
|
|
|
.map(|desired_bank| bank_method(&desired_bank, hashmap_key))
|
|
|
|
.unwrap_or_default()
|
|
|
|
};
|
|
|
|
let mut w_last_notified_slot = last_notified_slot.write().unwrap();
|
2020-07-23 12:38:28 -07:00
|
|
|
let (filter_results, result_slot) =
|
|
|
|
filter_results(results, *w_last_notified_slot, config.as_ref().cloned());
|
2020-05-06 23:23:06 -07:00
|
|
|
for result in filter_results {
|
|
|
|
notifier.notify(
|
|
|
|
Response {
|
|
|
|
context: RpcResponseContext { slot },
|
|
|
|
value: result,
|
|
|
|
},
|
|
|
|
sink,
|
|
|
|
);
|
|
|
|
notified_set.insert(sub_id.clone());
|
|
|
|
*w_last_notified_slot = result_slot;
|
2019-05-06 07:31:50 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-02-13 09:00:50 -08:00
|
|
|
notified_set
|
2019-05-06 07:31:50 -07:00
|
|
|
}
|
|
|
|
|
2020-02-25 20:23:54 -08:00
|
|
|
struct RpcNotifier(TaskExecutor);
|
|
|
|
|
|
|
|
impl RpcNotifier {
|
|
|
|
fn notify<T>(&self, value: T, sink: &Sink<T>)
|
|
|
|
where
|
|
|
|
T: serde::Serialize,
|
|
|
|
{
|
|
|
|
self.0
|
|
|
|
.spawn(sink.notify(Ok(value)).map(|_| ()).map_err(|_| ()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn filter_account_result(
|
|
|
|
result: Option<(Account, Slot)>,
|
2020-05-06 23:23:06 -07:00
|
|
|
last_notified_slot: Slot,
|
2020-07-23 12:38:28 -07:00
|
|
|
encoding: Option<UiAccountEncoding>,
|
2020-06-30 21:55:11 -07:00
|
|
|
) -> (Box<dyn Iterator<Item = UiAccount>>, Slot) {
|
2019-05-06 07:31:50 -07:00
|
|
|
if let Some((account, fork)) = result {
|
2020-06-17 09:44:51 -07:00
|
|
|
// If fork < last_notified_slot this means that we last notified for a fork
|
|
|
|
// and should notify that the account state has been reverted.
|
2020-05-06 23:23:06 -07:00
|
|
|
if fork != last_notified_slot {
|
2020-07-23 12:38:28 -07:00
|
|
|
let encoding = encoding.unwrap_or(UiAccountEncoding::Binary);
|
2020-06-30 21:55:11 -07:00
|
|
|
return (
|
2020-07-23 12:38:28 -07:00
|
|
|
Box::new(iter::once(UiAccount::encode(account, encoding))),
|
2020-06-30 21:55:11 -07:00
|
|
|
fork,
|
|
|
|
);
|
2019-05-06 07:31:50 -07:00
|
|
|
}
|
|
|
|
}
|
2020-05-06 23:23:06 -07:00
|
|
|
(Box::new(iter::empty()), last_notified_slot)
|
2019-05-06 07:31:50 -07:00
|
|
|
}
|
|
|
|
|
2020-04-04 16:13:26 -07:00
|
|
|
fn filter_signature_result(
|
|
|
|
result: Option<transaction::Result<()>>,
|
2020-05-06 23:23:06 -07:00
|
|
|
last_notified_slot: Slot,
|
2020-07-23 12:38:28 -07:00
|
|
|
_config: Option<()>,
|
2020-05-06 23:23:06 -07:00
|
|
|
) -> (Box<dyn Iterator<Item = RpcSignatureResult>>, Slot) {
|
|
|
|
(
|
|
|
|
Box::new(
|
|
|
|
result
|
|
|
|
.into_iter()
|
|
|
|
.map(|result| RpcSignatureResult { err: result.err() }),
|
|
|
|
),
|
|
|
|
last_notified_slot,
|
2020-04-04 16:13:26 -07:00
|
|
|
)
|
2019-05-06 07:31:50 -07:00
|
|
|
}
|
|
|
|
|
2020-02-25 20:23:54 -08:00
|
|
|
fn filter_program_results(
|
2020-02-13 09:00:50 -08:00
|
|
|
accounts: Vec<(Pubkey, Account)>,
|
2020-05-06 23:23:06 -07:00
|
|
|
last_notified_slot: Slot,
|
2020-07-23 12:38:28 -07:00
|
|
|
config: Option<ProgramConfig>,
|
2020-05-06 23:23:06 -07:00
|
|
|
) -> (Box<dyn Iterator<Item = RpcKeyedAccount>>, Slot) {
|
2020-07-23 12:38:28 -07:00
|
|
|
let config = config.unwrap_or_default();
|
|
|
|
let encoding = config.encoding.unwrap_or(UiAccountEncoding::Binary);
|
|
|
|
let filters = config.filters;
|
2020-05-06 23:23:06 -07:00
|
|
|
(
|
|
|
|
Box::new(
|
|
|
|
accounts
|
|
|
|
.into_iter()
|
2020-07-23 12:38:28 -07:00
|
|
|
.filter(move |(_, account)| {
|
|
|
|
filters.iter().all(|filter_type| match filter_type {
|
|
|
|
RpcFilterType::DataSize(size) => account.data.len() as u64 == *size,
|
|
|
|
RpcFilterType::Memcmp(compare) => compare.bytes_match(&account.data),
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.map(move |(pubkey, account)| RpcKeyedAccount {
|
2020-05-06 23:23:06 -07:00
|
|
|
pubkey: pubkey.to_string(),
|
2020-07-23 12:38:28 -07:00
|
|
|
account: UiAccount::encode(account, encoding.clone()),
|
2020-05-06 23:23:06 -07:00
|
|
|
}),
|
|
|
|
),
|
|
|
|
last_notified_slot,
|
2020-02-25 20:23:54 -08:00
|
|
|
)
|
2019-05-06 07:31:50 -07:00
|
|
|
}
|
|
|
|
|
2020-05-06 23:23:06 -07:00
|
|
|
#[derive(Clone)]
|
|
|
|
struct Subscriptions {
|
2020-01-20 13:08:29 -08:00
|
|
|
account_subscriptions: Arc<RpcAccountSubscriptions>,
|
|
|
|
program_subscriptions: Arc<RpcProgramSubscriptions>,
|
|
|
|
signature_subscriptions: Arc<RpcSignatureSubscriptions>,
|
2020-05-22 12:55:17 -07:00
|
|
|
gossip_account_subscriptions: Arc<RpcAccountSubscriptions>,
|
|
|
|
gossip_program_subscriptions: Arc<RpcProgramSubscriptions>,
|
|
|
|
gossip_signature_subscriptions: Arc<RpcSignatureSubscriptions>,
|
2020-01-20 13:08:29 -08:00
|
|
|
slot_subscriptions: Arc<RpcSlotSubscriptions>,
|
2020-05-17 14:01:08 -07:00
|
|
|
vote_subscriptions: Arc<RpcVoteSubscriptions>,
|
2020-03-27 09:33:40 -07:00
|
|
|
root_subscriptions: Arc<RpcRootSubscriptions>,
|
2020-05-06 23:23:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct RpcSubscriptions {
|
|
|
|
subscriptions: Subscriptions,
|
2020-02-20 16:03:46 -08:00
|
|
|
notification_sender: Arc<Mutex<Sender<NotificationEntry>>>,
|
2020-01-20 13:08:29 -08:00
|
|
|
t_cleanup: Option<JoinHandle<()>>,
|
2020-02-25 20:23:54 -08:00
|
|
|
notifier_runtime: Option<Runtime>,
|
2020-05-06 23:23:06 -07:00
|
|
|
bank_forks: Arc<RwLock<BankForks>>,
|
|
|
|
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
|
2020-05-22 12:55:17 -07:00
|
|
|
last_checked_slots: Arc<RwLock<HashMap<CommitmentLevel, Slot>>>,
|
2020-01-20 13:08:29 -08:00
|
|
|
exit: Arc<AtomicBool>,
|
2019-02-17 08:38:36 -08:00
|
|
|
}
|
|
|
|
|
2020-01-20 13:08:29 -08:00
|
|
|
impl Drop for RpcSubscriptions {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
self.shutdown().unwrap_or_else(|err| {
|
|
|
|
warn!("RPC Notification - shutdown error: {:?}", err);
|
|
|
|
});
|
2019-02-17 08:38:36 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RpcSubscriptions {
|
2020-03-30 16:53:25 -07:00
|
|
|
pub fn new(
|
|
|
|
exit: &Arc<AtomicBool>,
|
2020-05-06 23:23:06 -07:00
|
|
|
bank_forks: Arc<RwLock<BankForks>>,
|
2020-03-30 16:53:25 -07:00
|
|
|
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
|
|
|
|
) -> Self {
|
2020-01-20 13:08:29 -08:00
|
|
|
let (notification_sender, notification_receiver): (
|
2020-02-20 16:03:46 -08:00
|
|
|
Sender<NotificationEntry>,
|
|
|
|
Receiver<NotificationEntry>,
|
2020-01-20 13:08:29 -08:00
|
|
|
) = std::sync::mpsc::channel();
|
|
|
|
|
|
|
|
let account_subscriptions = Arc::new(RpcAccountSubscriptions::default());
|
|
|
|
let program_subscriptions = Arc::new(RpcProgramSubscriptions::default());
|
|
|
|
let signature_subscriptions = Arc::new(RpcSignatureSubscriptions::default());
|
2020-05-22 12:55:17 -07:00
|
|
|
let gossip_account_subscriptions = Arc::new(RpcAccountSubscriptions::default());
|
|
|
|
let gossip_program_subscriptions = Arc::new(RpcProgramSubscriptions::default());
|
|
|
|
let gossip_signature_subscriptions = Arc::new(RpcSignatureSubscriptions::default());
|
2020-01-20 13:08:29 -08:00
|
|
|
let slot_subscriptions = Arc::new(RpcSlotSubscriptions::default());
|
2020-05-17 14:01:08 -07:00
|
|
|
let vote_subscriptions = Arc::new(RpcVoteSubscriptions::default());
|
2020-03-27 09:33:40 -07:00
|
|
|
let root_subscriptions = Arc::new(RpcRootSubscriptions::default());
|
2020-01-20 13:08:29 -08:00
|
|
|
let notification_sender = Arc::new(Mutex::new(notification_sender));
|
|
|
|
|
2020-05-06 23:23:06 -07:00
|
|
|
let _bank_forks = bank_forks.clone();
|
|
|
|
let _block_commitment_cache = block_commitment_cache.clone();
|
2020-01-20 13:08:29 -08:00
|
|
|
let exit_clone = exit.clone();
|
2020-05-06 23:23:06 -07:00
|
|
|
let subscriptions = Subscriptions {
|
|
|
|
account_subscriptions,
|
|
|
|
program_subscriptions,
|
|
|
|
signature_subscriptions,
|
2020-05-22 12:55:17 -07:00
|
|
|
gossip_account_subscriptions,
|
|
|
|
gossip_program_subscriptions,
|
|
|
|
gossip_signature_subscriptions,
|
2020-05-06 23:23:06 -07:00
|
|
|
slot_subscriptions,
|
2020-05-17 14:01:08 -07:00
|
|
|
vote_subscriptions,
|
2020-05-06 23:23:06 -07:00
|
|
|
root_subscriptions,
|
|
|
|
};
|
|
|
|
let _subscriptions = subscriptions.clone();
|
2020-01-20 13:08:29 -08:00
|
|
|
|
2020-05-22 12:55:17 -07:00
|
|
|
let last_checked_slots = Arc::new(RwLock::new(HashMap::new()));
|
|
|
|
let _last_checked_slots = last_checked_slots.clone();
|
|
|
|
|
2020-02-25 20:23:54 -08:00
|
|
|
let notifier_runtime = RuntimeBuilder::new()
|
|
|
|
.core_threads(1)
|
|
|
|
.name_prefix("solana-rpc-notifier-")
|
|
|
|
.build()
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let notifier = RpcNotifier(notifier_runtime.executor());
|
2020-01-20 13:08:29 -08:00
|
|
|
let t_cleanup = Builder::new()
|
|
|
|
.name("solana-rpc-notifications".to_string())
|
|
|
|
.spawn(move || {
|
|
|
|
Self::process_notifications(
|
|
|
|
exit_clone,
|
2020-02-25 20:23:54 -08:00
|
|
|
notifier,
|
2020-01-20 13:08:29 -08:00
|
|
|
notification_receiver,
|
2020-05-06 23:23:06 -07:00
|
|
|
_subscriptions,
|
|
|
|
_bank_forks,
|
2020-05-22 12:55:17 -07:00
|
|
|
_last_checked_slots,
|
2020-01-20 13:08:29 -08:00
|
|
|
);
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
Self {
|
2020-05-06 23:23:06 -07:00
|
|
|
subscriptions,
|
2020-01-20 13:08:29 -08:00
|
|
|
notification_sender,
|
2020-02-25 20:23:54 -08:00
|
|
|
notifier_runtime: Some(notifier_runtime),
|
2020-01-20 13:08:29 -08:00
|
|
|
t_cleanup: Some(t_cleanup),
|
2020-05-06 23:23:06 -07:00
|
|
|
bank_forks,
|
|
|
|
block_commitment_cache,
|
2020-05-22 12:55:17 -07:00
|
|
|
last_checked_slots,
|
2020-01-20 13:08:29 -08:00
|
|
|
exit: exit.clone(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-25 21:06:58 -07:00
|
|
|
pub fn default_with_bank_forks(bank_forks: Arc<RwLock<BankForks>>) -> Self {
|
2020-04-22 11:22:09 -07:00
|
|
|
Self::new(
|
|
|
|
&Arc::new(AtomicBool::new(false)),
|
2020-05-06 23:23:06 -07:00
|
|
|
bank_forks,
|
2020-06-25 21:06:58 -07:00
|
|
|
Arc::new(RwLock::new(BlockCommitmentCache::default())),
|
2020-04-22 11:22:09 -07:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2020-01-20 13:08:29 -08:00
|
|
|
fn check_account(
|
2019-05-06 07:31:50 -07:00
|
|
|
pubkey: &Pubkey,
|
|
|
|
bank_forks: &Arc<RwLock<BankForks>>,
|
2020-01-20 13:08:29 -08:00
|
|
|
account_subscriptions: Arc<RpcAccountSubscriptions>,
|
2020-02-25 20:23:54 -08:00
|
|
|
notifier: &RpcNotifier,
|
2020-07-17 10:54:49 -07:00
|
|
|
commitment_slots: &CommitmentSlots,
|
2019-05-06 07:31:50 -07:00
|
|
|
) {
|
2020-01-20 13:08:29 -08:00
|
|
|
let subscriptions = account_subscriptions.read().unwrap();
|
2020-05-06 23:23:06 -07:00
|
|
|
check_commitment_and_notify(
|
2019-05-06 07:31:50 -07:00
|
|
|
&subscriptions,
|
|
|
|
pubkey,
|
|
|
|
bank_forks,
|
2020-07-17 10:54:49 -07:00
|
|
|
commitment_slots,
|
2020-05-06 23:23:06 -07:00
|
|
|
Bank::get_account_modified_slot,
|
2020-02-25 20:23:54 -08:00
|
|
|
filter_account_result,
|
|
|
|
notifier,
|
2019-05-06 07:31:50 -07:00
|
|
|
);
|
2019-02-17 08:38:36 -08:00
|
|
|
}
|
|
|
|
|
2020-01-20 13:08:29 -08:00
|
|
|
fn check_program(
|
2019-05-06 07:31:50 -07:00
|
|
|
program_id: &Pubkey,
|
|
|
|
bank_forks: &Arc<RwLock<BankForks>>,
|
2020-01-20 13:08:29 -08:00
|
|
|
program_subscriptions: Arc<RpcProgramSubscriptions>,
|
2020-02-25 20:23:54 -08:00
|
|
|
notifier: &RpcNotifier,
|
2020-07-17 10:54:49 -07:00
|
|
|
commitment_slots: &CommitmentSlots,
|
2019-05-06 07:31:50 -07:00
|
|
|
) {
|
2020-01-20 13:08:29 -08:00
|
|
|
let subscriptions = program_subscriptions.read().unwrap();
|
2020-05-06 23:23:06 -07:00
|
|
|
check_commitment_and_notify(
|
2019-05-06 07:31:50 -07:00
|
|
|
&subscriptions,
|
|
|
|
program_id,
|
|
|
|
bank_forks,
|
2020-07-17 10:54:49 -07:00
|
|
|
commitment_slots,
|
2019-05-06 07:31:50 -07:00
|
|
|
Bank::get_program_accounts_modified_since_parent,
|
2020-02-25 20:23:54 -08:00
|
|
|
filter_program_results,
|
|
|
|
notifier,
|
2019-05-06 07:31:50 -07:00
|
|
|
);
|
2019-03-06 14:31:58 -08:00
|
|
|
}
|
|
|
|
|
2020-01-20 13:08:29 -08:00
|
|
|
fn check_signature(
|
2019-05-06 07:31:50 -07:00
|
|
|
signature: &Signature,
|
|
|
|
bank_forks: &Arc<RwLock<BankForks>>,
|
2020-01-20 13:08:29 -08:00
|
|
|
signature_subscriptions: Arc<RpcSignatureSubscriptions>,
|
2020-02-25 20:23:54 -08:00
|
|
|
notifier: &RpcNotifier,
|
2020-07-17 10:54:49 -07:00
|
|
|
commitment_slots: &CommitmentSlots,
|
2019-05-06 07:31:50 -07:00
|
|
|
) {
|
2020-01-20 13:08:29 -08:00
|
|
|
let mut subscriptions = signature_subscriptions.write().unwrap();
|
2020-05-06 23:23:06 -07:00
|
|
|
let notified_ids = check_commitment_and_notify(
|
2019-05-06 07:31:50 -07:00
|
|
|
&subscriptions,
|
|
|
|
signature,
|
|
|
|
bank_forks,
|
2020-07-17 10:54:49 -07:00
|
|
|
commitment_slots,
|
2020-03-23 17:00:34 -07:00
|
|
|
Bank::get_signature_status_processed_since_parent,
|
2020-02-25 20:23:54 -08:00
|
|
|
filter_signature_result,
|
|
|
|
notifier,
|
2019-05-06 07:31:50 -07:00
|
|
|
);
|
2020-02-13 09:00:50 -08:00
|
|
|
if let Some(subscription_ids) = subscriptions.get_mut(signature) {
|
|
|
|
subscription_ids.retain(|k, _| !notified_ids.contains(k));
|
|
|
|
if subscription_ids.is_empty() {
|
|
|
|
subscriptions.remove(&signature);
|
|
|
|
}
|
|
|
|
}
|
2019-02-17 08:38:36 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn add_account_subscription(
|
|
|
|
&self,
|
2020-03-24 09:53:32 -07:00
|
|
|
pubkey: Pubkey,
|
2020-07-23 12:38:28 -07:00
|
|
|
config: Option<RpcAccountInfoConfig>,
|
2020-03-24 09:53:32 -07:00
|
|
|
sub_id: SubscriptionId,
|
2020-06-30 21:55:11 -07:00
|
|
|
subscriber: Subscriber<Response<UiAccount>>,
|
2019-02-17 08:38:36 -08:00
|
|
|
) {
|
2020-07-23 12:38:28 -07:00
|
|
|
let config = config.unwrap_or_default();
|
|
|
|
let commitment_level = config
|
|
|
|
.commitment
|
2020-05-21 09:33:24 -07:00
|
|
|
.unwrap_or_else(CommitmentConfig::single)
|
2020-05-22 12:55:17 -07:00
|
|
|
.commitment;
|
|
|
|
let slot = match commitment_level {
|
2020-05-06 23:23:06 -07:00
|
|
|
CommitmentLevel::Max => self
|
|
|
|
.block_commitment_cache
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
2020-07-07 16:59:46 -07:00
|
|
|
.highest_confirmed_root(),
|
2020-05-06 23:23:06 -07:00
|
|
|
CommitmentLevel::Recent => self.block_commitment_cache.read().unwrap().slot(),
|
|
|
|
CommitmentLevel::Root => self.block_commitment_cache.read().unwrap().root(),
|
2020-05-18 11:49:01 -07:00
|
|
|
CommitmentLevel::Single => self
|
|
|
|
.block_commitment_cache
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.highest_confirmed_slot(),
|
2020-05-22 12:55:17 -07:00
|
|
|
CommitmentLevel::SingleGossip => *self
|
|
|
|
.last_checked_slots
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.get(&CommitmentLevel::SingleGossip)
|
|
|
|
.unwrap_or(&0),
|
2020-05-06 23:23:06 -07:00
|
|
|
};
|
|
|
|
let last_notified_slot = if let Some((_account, slot)) = self
|
|
|
|
.bank_forks
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.get(slot)
|
|
|
|
.and_then(|bank| bank.get_account_modified_slot(&pubkey))
|
|
|
|
{
|
|
|
|
slot
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
};
|
2020-05-22 12:55:17 -07:00
|
|
|
|
|
|
|
let mut subscriptions = if commitment_level == CommitmentLevel::SingleGossip {
|
|
|
|
self.subscriptions
|
|
|
|
.gossip_account_subscriptions
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
} else {
|
|
|
|
self.subscriptions.account_subscriptions.write().unwrap()
|
|
|
|
};
|
2020-03-24 09:53:32 -07:00
|
|
|
add_subscription(
|
|
|
|
&mut subscriptions,
|
|
|
|
pubkey,
|
2020-07-23 12:38:28 -07:00
|
|
|
config.commitment,
|
2020-03-24 09:53:32 -07:00
|
|
|
sub_id,
|
|
|
|
subscriber,
|
2020-05-06 23:23:06 -07:00
|
|
|
last_notified_slot,
|
2020-07-23 12:38:28 -07:00
|
|
|
config.encoding,
|
2020-03-24 09:53:32 -07:00
|
|
|
);
|
2019-02-17 08:38:36 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn remove_account_subscription(&self, id: &SubscriptionId) -> bool {
|
2020-05-06 23:23:06 -07:00
|
|
|
let mut subscriptions = self.subscriptions.account_subscriptions.write().unwrap();
|
2020-05-22 12:55:17 -07:00
|
|
|
if remove_subscription(&mut subscriptions, id) {
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
let mut subscriptions = self
|
|
|
|
.subscriptions
|
|
|
|
.gossip_account_subscriptions
|
|
|
|
.write()
|
|
|
|
.unwrap();
|
|
|
|
remove_subscription(&mut subscriptions, id)
|
|
|
|
}
|
2019-02-17 08:38:36 -08:00
|
|
|
}
|
|
|
|
|
2019-03-06 14:31:58 -08:00
|
|
|
pub fn add_program_subscription(
|
|
|
|
&self,
|
2020-03-24 09:53:32 -07:00
|
|
|
program_id: Pubkey,
|
2020-07-23 12:38:28 -07:00
|
|
|
config: Option<RpcProgramAccountsConfig>,
|
2020-03-24 09:53:32 -07:00
|
|
|
sub_id: SubscriptionId,
|
|
|
|
subscriber: Subscriber<Response<RpcKeyedAccount>>,
|
2019-03-06 14:31:58 -08:00
|
|
|
) {
|
2020-07-23 12:38:28 -07:00
|
|
|
let config = config.unwrap_or_default();
|
|
|
|
let commitment_level = config
|
|
|
|
.account_config
|
|
|
|
.commitment
|
2020-05-22 12:55:17 -07:00
|
|
|
.unwrap_or_else(CommitmentConfig::recent)
|
|
|
|
.commitment;
|
|
|
|
let mut subscriptions = if commitment_level == CommitmentLevel::SingleGossip {
|
|
|
|
self.subscriptions
|
|
|
|
.gossip_program_subscriptions
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
} else {
|
|
|
|
self.subscriptions.program_subscriptions.write().unwrap()
|
|
|
|
};
|
2020-03-24 09:53:32 -07:00
|
|
|
add_subscription(
|
|
|
|
&mut subscriptions,
|
|
|
|
program_id,
|
2020-07-23 12:38:28 -07:00
|
|
|
config.account_config.commitment,
|
2020-03-24 09:53:32 -07:00
|
|
|
sub_id,
|
|
|
|
subscriber,
|
2020-05-06 23:23:06 -07:00
|
|
|
0, // last_notified_slot is not utilized for program subscriptions
|
2020-07-23 12:38:28 -07:00
|
|
|
Some(ProgramConfig {
|
|
|
|
filters: config.filters.unwrap_or_default(),
|
|
|
|
encoding: config.account_config.encoding,
|
|
|
|
}),
|
2020-03-24 09:53:32 -07:00
|
|
|
);
|
2019-03-06 14:31:58 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn remove_program_subscription(&self, id: &SubscriptionId) -> bool {
|
2020-05-06 23:23:06 -07:00
|
|
|
let mut subscriptions = self.subscriptions.program_subscriptions.write().unwrap();
|
2020-05-22 12:55:17 -07:00
|
|
|
if remove_subscription(&mut subscriptions, id) {
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
let mut subscriptions = self
|
|
|
|
.subscriptions
|
|
|
|
.gossip_program_subscriptions
|
|
|
|
.write()
|
|
|
|
.unwrap();
|
|
|
|
remove_subscription(&mut subscriptions, id)
|
|
|
|
}
|
2019-03-06 14:31:58 -08:00
|
|
|
}
|
|
|
|
|
2019-02-17 08:38:36 -08:00
|
|
|
pub fn add_signature_subscription(
|
|
|
|
&self,
|
2020-03-24 09:53:32 -07:00
|
|
|
signature: Signature,
|
2020-05-06 23:23:06 -07:00
|
|
|
commitment: Option<CommitmentConfig>,
|
2020-03-24 09:53:32 -07:00
|
|
|
sub_id: SubscriptionId,
|
2020-04-04 16:13:26 -07:00
|
|
|
subscriber: Subscriber<Response<RpcSignatureResult>>,
|
2019-02-17 08:38:36 -08:00
|
|
|
) {
|
2020-05-22 12:55:17 -07:00
|
|
|
let commitment_level = commitment
|
|
|
|
.unwrap_or_else(CommitmentConfig::recent)
|
|
|
|
.commitment;
|
|
|
|
let mut subscriptions = if commitment_level == CommitmentLevel::SingleGossip {
|
|
|
|
self.subscriptions
|
|
|
|
.gossip_signature_subscriptions
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
} else {
|
|
|
|
self.subscriptions.signature_subscriptions.write().unwrap()
|
|
|
|
};
|
2020-03-24 09:53:32 -07:00
|
|
|
add_subscription(
|
|
|
|
&mut subscriptions,
|
|
|
|
signature,
|
2020-05-06 23:23:06 -07:00
|
|
|
commitment,
|
2020-03-24 09:53:32 -07:00
|
|
|
sub_id,
|
|
|
|
subscriber,
|
2020-05-06 23:23:06 -07:00
|
|
|
0, // last_notified_slot is not utilized for signature subscriptions
|
2020-07-23 12:38:28 -07:00
|
|
|
None,
|
2020-03-24 09:53:32 -07:00
|
|
|
);
|
2019-02-17 08:38:36 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn remove_signature_subscription(&self, id: &SubscriptionId) -> bool {
|
2020-05-06 23:23:06 -07:00
|
|
|
let mut subscriptions = self.subscriptions.signature_subscriptions.write().unwrap();
|
2020-05-22 12:55:17 -07:00
|
|
|
if remove_subscription(&mut subscriptions, id) {
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
let mut subscriptions = self
|
|
|
|
.subscriptions
|
|
|
|
.gossip_signature_subscriptions
|
|
|
|
.write()
|
|
|
|
.unwrap();
|
|
|
|
remove_subscription(&mut subscriptions, id)
|
|
|
|
}
|
2019-02-17 08:38:36 -08:00
|
|
|
}
|
2019-02-18 14:21:23 -08:00
|
|
|
|
|
|
|
/// Notify subscribers of changes to any accounts or new signatures since
|
|
|
|
/// the bank's last checkpoint.
|
2020-07-17 10:54:49 -07:00
|
|
|
pub fn notify_subscribers(&self, commitment_slots: CommitmentSlots) {
|
|
|
|
self.enqueue_notification(NotificationEntry::Bank(commitment_slots));
|
2019-02-18 14:21:23 -08:00
|
|
|
}
|
2019-11-26 00:42:54 -08:00
|
|
|
|
2020-05-22 12:55:17 -07:00
|
|
|
/// Notify SingleGossip commitment-level subscribers of changes to any accounts or new
|
|
|
|
/// signatures.
|
|
|
|
pub fn notify_gossip_subscribers(&self, slot: Slot) {
|
|
|
|
self.enqueue_notification(NotificationEntry::Gossip(slot));
|
|
|
|
}
|
|
|
|
|
2020-03-24 09:53:32 -07:00
|
|
|
pub fn add_slot_subscription(&self, sub_id: SubscriptionId, subscriber: Subscriber<SlotInfo>) {
|
|
|
|
let sink = subscriber.assign_id(sub_id.clone()).unwrap();
|
2020-05-06 23:23:06 -07:00
|
|
|
let mut subscriptions = self.subscriptions.slot_subscriptions.write().unwrap();
|
2020-03-24 09:53:32 -07:00
|
|
|
subscriptions.insert(sub_id, sink);
|
2019-11-26 00:42:54 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn remove_slot_subscription(&self, id: &SubscriptionId) -> bool {
|
2020-05-06 23:23:06 -07:00
|
|
|
let mut subscriptions = self.subscriptions.slot_subscriptions.write().unwrap();
|
2019-11-26 00:42:54 -08:00
|
|
|
subscriptions.remove(id).is_some()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn notify_slot(&self, slot: Slot, parent: Slot, root: Slot) {
|
2020-01-20 13:08:29 -08:00
|
|
|
self.enqueue_notification(NotificationEntry::Slot(SlotInfo { slot, parent, root }));
|
|
|
|
}
|
|
|
|
|
2020-05-17 14:01:08 -07:00
|
|
|
pub fn add_vote_subscription(&self, sub_id: SubscriptionId, subscriber: Subscriber<RpcVote>) {
|
|
|
|
let sink = subscriber.assign_id(sub_id.clone()).unwrap();
|
|
|
|
let mut subscriptions = self.subscriptions.vote_subscriptions.write().unwrap();
|
|
|
|
subscriptions.insert(sub_id, sink);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn remove_vote_subscription(&self, id: &SubscriptionId) -> bool {
|
|
|
|
let mut subscriptions = self.subscriptions.vote_subscriptions.write().unwrap();
|
|
|
|
subscriptions.remove(id).is_some()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn notify_vote(&self, vote: &Vote) {
|
|
|
|
self.enqueue_notification(NotificationEntry::Vote(vote.clone()));
|
|
|
|
}
|
|
|
|
|
2020-06-17 09:44:51 -07:00
|
|
|
pub fn notify_frozen(&self, frozen_slot: Slot) {
|
|
|
|
self.enqueue_notification(NotificationEntry::Frozen(frozen_slot));
|
|
|
|
}
|
|
|
|
|
2020-03-27 09:33:40 -07:00
|
|
|
pub fn add_root_subscription(&self, sub_id: SubscriptionId, subscriber: Subscriber<Slot>) {
|
|
|
|
let sink = subscriber.assign_id(sub_id.clone()).unwrap();
|
2020-05-06 23:23:06 -07:00
|
|
|
let mut subscriptions = self.subscriptions.root_subscriptions.write().unwrap();
|
2020-03-27 09:33:40 -07:00
|
|
|
subscriptions.insert(sub_id, sink);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn remove_root_subscription(&self, id: &SubscriptionId) -> bool {
|
2020-05-06 23:23:06 -07:00
|
|
|
let mut subscriptions = self.subscriptions.root_subscriptions.write().unwrap();
|
2020-03-27 09:33:40 -07:00
|
|
|
subscriptions.remove(id).is_some()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn notify_roots(&self, mut rooted_slots: Vec<Slot>) {
|
|
|
|
rooted_slots.sort();
|
|
|
|
rooted_slots.into_iter().for_each(|root| {
|
|
|
|
self.enqueue_notification(NotificationEntry::Root(root));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-01-20 13:08:29 -08:00
|
|
|
fn enqueue_notification(&self, notification_entry: NotificationEntry) {
|
|
|
|
match self
|
|
|
|
.notification_sender
|
|
|
|
.lock()
|
|
|
|
.unwrap()
|
2020-02-20 16:03:46 -08:00
|
|
|
.send(notification_entry)
|
2020-01-20 13:08:29 -08:00
|
|
|
{
|
|
|
|
Ok(()) => (),
|
|
|
|
Err(SendError(notification)) => {
|
|
|
|
warn!(
|
|
|
|
"Dropped RPC Notification - receiver disconnected : {:?}",
|
|
|
|
notification
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn process_notifications(
|
|
|
|
exit: Arc<AtomicBool>,
|
2020-02-25 20:23:54 -08:00
|
|
|
notifier: RpcNotifier,
|
2020-02-20 16:03:46 -08:00
|
|
|
notification_receiver: Receiver<NotificationEntry>,
|
2020-05-06 23:23:06 -07:00
|
|
|
subscriptions: Subscriptions,
|
|
|
|
bank_forks: Arc<RwLock<BankForks>>,
|
2020-05-22 12:55:17 -07:00
|
|
|
last_checked_slots: Arc<RwLock<HashMap<CommitmentLevel, Slot>>>,
|
2020-01-20 13:08:29 -08:00
|
|
|
) {
|
2020-06-17 09:44:51 -07:00
|
|
|
let mut pending_gossip_notifications = HashSet::new();
|
2020-01-20 13:08:29 -08:00
|
|
|
loop {
|
|
|
|
if exit.load(Ordering::Relaxed) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
match notification_receiver.recv_timeout(Duration::from_millis(RECEIVE_DELAY_MILLIS)) {
|
2020-02-20 16:03:46 -08:00
|
|
|
Ok(notification_entry) => match notification_entry {
|
|
|
|
NotificationEntry::Slot(slot_info) => {
|
2020-05-06 23:23:06 -07:00
|
|
|
let subscriptions = subscriptions.slot_subscriptions.read().unwrap();
|
2020-02-20 16:03:46 -08:00
|
|
|
for (_, sink) in subscriptions.iter() {
|
2020-02-25 20:23:54 -08:00
|
|
|
notifier.notify(slot_info, sink);
|
2020-02-20 16:03:46 -08:00
|
|
|
}
|
|
|
|
}
|
2020-07-20 17:29:07 -07:00
|
|
|
// These notifications are only triggered by votes observed on gossip,
|
|
|
|
// unlike `NotificationEntry::Gossip`, which also accounts for slots seen
|
|
|
|
// in VoteState's from bank states built in ReplayStage.
|
2020-05-17 14:01:08 -07:00
|
|
|
NotificationEntry::Vote(ref vote_info) => {
|
|
|
|
let subscriptions = subscriptions.vote_subscriptions.read().unwrap();
|
|
|
|
for (_, sink) in subscriptions.iter() {
|
|
|
|
notifier.notify(
|
|
|
|
RpcVote {
|
|
|
|
slots: vote_info.slots.clone(),
|
|
|
|
hash: bs58::encode(vote_info.hash).into_string(),
|
|
|
|
timestamp: vote_info.timestamp,
|
|
|
|
},
|
|
|
|
sink,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2020-03-27 09:33:40 -07:00
|
|
|
NotificationEntry::Root(root) => {
|
2020-05-06 23:23:06 -07:00
|
|
|
let subscriptions = subscriptions.root_subscriptions.read().unwrap();
|
2020-03-27 09:33:40 -07:00
|
|
|
for (_, sink) in subscriptions.iter() {
|
|
|
|
notifier.notify(root, sink);
|
|
|
|
}
|
2020-06-17 09:44:51 -07:00
|
|
|
|
|
|
|
// Prune old pending notifications
|
|
|
|
pending_gossip_notifications = pending_gossip_notifications
|
|
|
|
.into_iter()
|
|
|
|
.filter(|&s| s > root)
|
|
|
|
.collect();
|
2020-03-27 09:33:40 -07:00
|
|
|
}
|
2020-07-17 10:54:49 -07:00
|
|
|
NotificationEntry::Bank(commitment_slots) => {
|
2020-05-22 12:55:17 -07:00
|
|
|
RpcSubscriptions::notify_accounts_programs_signatures(
|
|
|
|
&subscriptions.account_subscriptions,
|
|
|
|
&subscriptions.program_subscriptions,
|
|
|
|
&subscriptions.signature_subscriptions,
|
|
|
|
&bank_forks,
|
2020-07-17 10:54:49 -07:00
|
|
|
&commitment_slots,
|
2020-05-22 12:55:17 -07:00
|
|
|
¬ifier,
|
|
|
|
)
|
|
|
|
}
|
2020-06-17 09:44:51 -07:00
|
|
|
NotificationEntry::Frozen(slot) => {
|
|
|
|
if pending_gossip_notifications.remove(&slot) {
|
|
|
|
Self::process_gossip_notification(
|
|
|
|
slot,
|
|
|
|
¬ifier,
|
|
|
|
&subscriptions,
|
|
|
|
&bank_forks,
|
|
|
|
&last_checked_slots,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2020-05-22 12:55:17 -07:00
|
|
|
NotificationEntry::Gossip(slot) => {
|
2020-06-17 09:44:51 -07:00
|
|
|
let bank_frozen = bank_forks
|
|
|
|
.read()
|
2020-05-22 12:55:17 -07:00
|
|
|
.unwrap()
|
2020-06-17 09:44:51 -07:00
|
|
|
.get(slot)
|
|
|
|
.filter(|b| b.is_frozen())
|
|
|
|
.is_some();
|
|
|
|
|
|
|
|
if !bank_frozen {
|
|
|
|
pending_gossip_notifications.insert(slot);
|
|
|
|
} else {
|
|
|
|
Self::process_gossip_notification(
|
|
|
|
slot,
|
|
|
|
¬ifier,
|
|
|
|
&subscriptions,
|
|
|
|
&bank_forks,
|
|
|
|
&last_checked_slots,
|
|
|
|
);
|
|
|
|
}
|
2020-01-20 13:08:29 -08:00
|
|
|
}
|
2020-02-20 16:03:46 -08:00
|
|
|
},
|
2020-01-20 13:08:29 -08:00
|
|
|
Err(RecvTimeoutError::Timeout) => {
|
|
|
|
// not a problem - try reading again
|
|
|
|
}
|
|
|
|
Err(RecvTimeoutError::Disconnected) => {
|
|
|
|
warn!("RPC Notification thread - sender disconnected");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-17 09:44:51 -07:00
|
|
|
fn process_gossip_notification(
|
|
|
|
slot: Slot,
|
|
|
|
notifier: &RpcNotifier,
|
|
|
|
subscriptions: &Subscriptions,
|
|
|
|
bank_forks: &Arc<RwLock<BankForks>>,
|
|
|
|
last_checked_slots: &Arc<RwLock<HashMap<CommitmentLevel, Slot>>>,
|
|
|
|
) {
|
|
|
|
let mut last_checked_slots_lock = last_checked_slots.write().unwrap();
|
|
|
|
let last_checked_slot = last_checked_slots_lock
|
|
|
|
.get(&CommitmentLevel::SingleGossip)
|
|
|
|
.cloned()
|
|
|
|
.unwrap_or_default();
|
|
|
|
|
|
|
|
if slot > last_checked_slot {
|
|
|
|
last_checked_slots_lock.insert(CommitmentLevel::SingleGossip, slot);
|
|
|
|
} else {
|
|
|
|
// Avoid sending stale or duplicate notifications
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
drop(last_checked_slots_lock);
|
|
|
|
|
2020-07-17 10:54:49 -07:00
|
|
|
let commitment_slots = CommitmentSlots {
|
2020-06-17 09:44:51 -07:00
|
|
|
highest_confirmed_slot: slot,
|
2020-07-17 10:54:49 -07:00
|
|
|
..CommitmentSlots::default()
|
2020-06-17 09:44:51 -07:00
|
|
|
};
|
|
|
|
RpcSubscriptions::notify_accounts_programs_signatures(
|
|
|
|
&subscriptions.gossip_account_subscriptions,
|
|
|
|
&subscriptions.gossip_program_subscriptions,
|
|
|
|
&subscriptions.gossip_signature_subscriptions,
|
|
|
|
&bank_forks,
|
2020-07-17 10:54:49 -07:00
|
|
|
&commitment_slots,
|
2020-06-17 09:44:51 -07:00
|
|
|
¬ifier,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-05-22 12:55:17 -07:00
|
|
|
fn notify_accounts_programs_signatures(
|
|
|
|
account_subscriptions: &Arc<RpcAccountSubscriptions>,
|
|
|
|
program_subscriptions: &Arc<RpcProgramSubscriptions>,
|
|
|
|
signature_subscriptions: &Arc<RpcSignatureSubscriptions>,
|
|
|
|
bank_forks: &Arc<RwLock<BankForks>>,
|
2020-07-17 10:54:49 -07:00
|
|
|
commitment_slots: &CommitmentSlots,
|
2020-05-22 12:55:17 -07:00
|
|
|
notifier: &RpcNotifier,
|
|
|
|
) {
|
|
|
|
let pubkeys: Vec<_> = {
|
|
|
|
let subs = account_subscriptions.read().unwrap();
|
|
|
|
subs.keys().cloned().collect()
|
|
|
|
};
|
|
|
|
for pubkey in &pubkeys {
|
|
|
|
Self::check_account(
|
|
|
|
pubkey,
|
|
|
|
&bank_forks,
|
|
|
|
account_subscriptions.clone(),
|
|
|
|
¬ifier,
|
2020-07-17 10:54:49 -07:00
|
|
|
&commitment_slots,
|
2020-05-22 12:55:17 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
let programs: Vec<_> = {
|
|
|
|
let subs = program_subscriptions.read().unwrap();
|
|
|
|
subs.keys().cloned().collect()
|
|
|
|
};
|
|
|
|
for program_id in &programs {
|
|
|
|
Self::check_program(
|
|
|
|
program_id,
|
|
|
|
&bank_forks,
|
|
|
|
program_subscriptions.clone(),
|
|
|
|
¬ifier,
|
2020-07-17 10:54:49 -07:00
|
|
|
&commitment_slots,
|
2020-05-22 12:55:17 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
let signatures: Vec<_> = {
|
|
|
|
let subs = signature_subscriptions.read().unwrap();
|
|
|
|
subs.keys().cloned().collect()
|
|
|
|
};
|
|
|
|
for signature in &signatures {
|
|
|
|
Self::check_signature(
|
|
|
|
signature,
|
|
|
|
&bank_forks,
|
|
|
|
signature_subscriptions.clone(),
|
|
|
|
¬ifier,
|
2020-07-17 10:54:49 -07:00
|
|
|
&commitment_slots,
|
2020-05-22 12:55:17 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-20 13:08:29 -08:00
|
|
|
fn shutdown(&mut self) -> std::thread::Result<()> {
|
2020-02-25 20:23:54 -08:00
|
|
|
if let Some(runtime) = self.notifier_runtime.take() {
|
|
|
|
info!("RPC Notifier runtime - shutting down");
|
|
|
|
let _ = runtime.shutdown_now().wait();
|
|
|
|
info!("RPC Notifier runtime - shut down");
|
|
|
|
}
|
|
|
|
|
2020-01-20 13:08:29 -08:00
|
|
|
if self.t_cleanup.is_some() {
|
|
|
|
info!("RPC Notification thread - shutting down");
|
|
|
|
self.exit.store(true, Ordering::Relaxed);
|
|
|
|
let x = self.t_cleanup.take().unwrap().join();
|
|
|
|
info!("RPC Notification thread - shut down.");
|
|
|
|
x
|
|
|
|
} else {
|
|
|
|
warn!("RPC Notification thread - already shut down.");
|
|
|
|
Ok(())
|
2019-11-26 00:42:54 -08:00
|
|
|
}
|
|
|
|
}
|
2019-02-17 08:38:36 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
2020-01-20 13:08:29 -08:00
|
|
|
pub(crate) mod tests {
|
2019-02-17 08:38:36 -08:00
|
|
|
use super::*;
|
2020-03-02 17:44:39 -08:00
|
|
|
use jsonrpc_core::futures::{self, stream::Stream};
|
2019-02-17 10:17:58 -08:00
|
|
|
use jsonrpc_pubsub::typed::Subscriber;
|
2020-04-17 10:48:39 -07:00
|
|
|
use serial_test_derive::serial;
|
2020-06-25 21:06:58 -07:00
|
|
|
use solana_runtime::{
|
|
|
|
commitment::BlockCommitment,
|
2020-04-22 11:22:09 -07:00
|
|
|
genesis_utils::{create_genesis_config, GenesisConfigInfo},
|
|
|
|
};
|
2020-01-15 10:52:02 -08:00
|
|
|
use solana_sdk::{
|
2020-02-20 13:28:55 -08:00
|
|
|
signature::{Keypair, Signer},
|
2020-01-15 10:52:02 -08:00
|
|
|
system_transaction,
|
|
|
|
};
|
2020-03-02 17:44:39 -08:00
|
|
|
use std::{fmt::Debug, sync::mpsc::channel, time::Instant};
|
|
|
|
use tokio::{prelude::FutureExt, runtime::Runtime, timer::Delay};
|
|
|
|
|
|
|
|
pub(crate) fn robust_poll_or_panic<T: Debug + Send + 'static>(
|
|
|
|
receiver: futures::sync::mpsc::Receiver<T>,
|
2020-03-27 09:33:40 -07:00
|
|
|
) -> (T, futures::sync::mpsc::Receiver<T>) {
|
2020-03-02 17:44:39 -08:00
|
|
|
let (inner_sender, inner_receiver) = channel();
|
|
|
|
let mut rt = Runtime::new().unwrap();
|
|
|
|
rt.spawn(futures::lazy(|| {
|
|
|
|
let recv_timeout = receiver
|
|
|
|
.into_future()
|
|
|
|
.timeout(Duration::from_millis(RECEIVE_DELAY_MILLIS))
|
|
|
|
.map(move |result| match result {
|
2020-03-27 09:33:40 -07:00
|
|
|
(Some(value), receiver) => {
|
|
|
|
inner_sender.send((value, receiver)).expect("send error")
|
|
|
|
}
|
2020-03-02 17:44:39 -08:00
|
|
|
(None, _) => panic!("unexpected end of stream"),
|
|
|
|
})
|
|
|
|
.map_err(|err| panic!("stream error {:?}", err));
|
|
|
|
|
|
|
|
const INITIAL_DELAY_MS: u64 = RECEIVE_DELAY_MILLIS * 2;
|
|
|
|
Delay::new(Instant::now() + Duration::from_millis(INITIAL_DELAY_MS))
|
|
|
|
.and_then(|_| recv_timeout)
|
|
|
|
.map_err(|err| panic!("timer error {:?}", err))
|
|
|
|
}));
|
|
|
|
inner_receiver.recv().expect("recv error")
|
2020-01-20 13:08:29 -08:00
|
|
|
}
|
|
|
|
|
2019-02-17 08:38:36 -08:00
|
|
|
#[test]
|
2020-04-17 10:48:39 -07:00
|
|
|
#[serial]
|
2019-02-17 08:38:36 -08:00
|
|
|
fn test_check_account_subscribe() {
|
2019-11-08 20:56:57 -08:00
|
|
|
let GenesisConfigInfo {
|
|
|
|
genesis_config,
|
2019-05-22 20:39:00 -07:00
|
|
|
mint_keypair,
|
|
|
|
..
|
2019-11-08 20:56:57 -08:00
|
|
|
} = create_genesis_config(100);
|
|
|
|
let bank = Bank::new(&genesis_config);
|
2019-03-02 10:25:16 -08:00
|
|
|
let blockhash = bank.last_blockhash();
|
2020-06-12 10:04:17 -07:00
|
|
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
|
2020-05-06 23:23:06 -07:00
|
|
|
let bank0 = bank_forks.read().unwrap().get(0).unwrap().clone();
|
|
|
|
let bank1 = Bank::new_from_parent(&bank0, &Pubkey::default(), 1);
|
|
|
|
bank_forks.write().unwrap().insert(bank1);
|
2019-05-06 07:31:50 -07:00
|
|
|
let alice = Keypair::new();
|
2019-02-17 08:38:36 -08:00
|
|
|
|
2020-01-20 13:08:29 -08:00
|
|
|
let (subscriber, _id_receiver, transport_receiver) =
|
2019-02-17 08:38:36 -08:00
|
|
|
Subscriber::new_test("accountNotification");
|
|
|
|
let sub_id = SubscriptionId::Number(0 as u64);
|
2020-01-20 13:08:29 -08:00
|
|
|
let exit = Arc::new(AtomicBool::new(false));
|
2020-03-30 16:53:25 -07:00
|
|
|
let subscriptions = RpcSubscriptions::new(
|
|
|
|
&exit,
|
2020-05-06 23:23:06 -07:00
|
|
|
bank_forks.clone(),
|
2020-07-08 17:50:13 -07:00
|
|
|
Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests_with_slots(
|
|
|
|
1, 1,
|
2020-06-25 21:06:58 -07:00
|
|
|
))),
|
2020-03-30 16:53:25 -07:00
|
|
|
);
|
2020-05-21 09:33:24 -07:00
|
|
|
subscriptions.add_account_subscription(
|
|
|
|
alice.pubkey(),
|
2020-07-23 12:38:28 -07:00
|
|
|
Some(RpcAccountInfoConfig {
|
|
|
|
commitment: Some(CommitmentConfig::recent()),
|
|
|
|
encoding: None,
|
|
|
|
}),
|
2020-05-21 09:33:24 -07:00
|
|
|
sub_id.clone(),
|
|
|
|
subscriber,
|
|
|
|
);
|
2019-02-17 08:38:36 -08:00
|
|
|
|
|
|
|
assert!(subscriptions
|
2020-05-06 23:23:06 -07:00
|
|
|
.subscriptions
|
2019-02-17 08:38:36 -08:00
|
|
|
.account_subscriptions
|
2019-02-18 14:21:23 -08:00
|
|
|
.read()
|
2019-02-17 08:38:36 -08:00
|
|
|
.unwrap()
|
|
|
|
.contains_key(&alice.pubkey()));
|
|
|
|
|
2020-05-06 23:23:06 -07:00
|
|
|
let tx = system_transaction::create_account(
|
|
|
|
&mint_keypair,
|
|
|
|
&alice,
|
|
|
|
blockhash,
|
|
|
|
1,
|
|
|
|
16,
|
|
|
|
&solana_budget_program::id(),
|
|
|
|
);
|
|
|
|
bank_forks
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.get(1)
|
|
|
|
.unwrap()
|
|
|
|
.process_transaction(&tx)
|
|
|
|
.unwrap();
|
2020-07-17 10:54:49 -07:00
|
|
|
let mut commitment_slots = CommitmentSlots::default();
|
|
|
|
commitment_slots.slot = 1;
|
|
|
|
subscriptions.notify_subscribers(commitment_slots);
|
2020-03-27 09:33:40 -07:00
|
|
|
let (response, _) = robust_poll_or_panic(transport_receiver);
|
2020-03-23 05:34:42 -07:00
|
|
|
let expected = json!({
|
|
|
|
"jsonrpc": "2.0",
|
|
|
|
"method": "accountNotification",
|
|
|
|
"params": {
|
|
|
|
"result": {
|
2020-05-06 23:23:06 -07:00
|
|
|
"context": { "slot": 1 },
|
2020-03-23 05:34:42 -07:00
|
|
|
"value": {
|
|
|
|
"data": "1111111111111111",
|
|
|
|
"executable": false,
|
|
|
|
"lamports": 1,
|
|
|
|
"owner": "Budget1111111111111111111111111111111111111",
|
|
|
|
"rentEpoch": 1,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"subscription": 0,
|
|
|
|
}
|
|
|
|
});
|
|
|
|
assert_eq!(serde_json::to_string(&expected).unwrap(), response);
|
2019-02-17 08:38:36 -08:00
|
|
|
|
|
|
|
subscriptions.remove_account_subscription(&sub_id);
|
|
|
|
assert!(!subscriptions
|
2020-05-06 23:23:06 -07:00
|
|
|
.subscriptions
|
2019-02-17 08:38:36 -08:00
|
|
|
.account_subscriptions
|
2019-02-18 14:21:23 -08:00
|
|
|
.read()
|
2019-02-17 08:38:36 -08:00
|
|
|
.unwrap()
|
|
|
|
.contains_key(&alice.pubkey()));
|
|
|
|
}
|
2019-03-06 14:31:58 -08:00
|
|
|
|
|
|
|
#[test]
|
2020-04-17 10:48:39 -07:00
|
|
|
#[serial]
|
2019-03-06 14:31:58 -08:00
|
|
|
fn test_check_program_subscribe() {
|
2019-11-08 20:56:57 -08:00
|
|
|
let GenesisConfigInfo {
|
|
|
|
genesis_config,
|
2019-05-22 20:39:00 -07:00
|
|
|
mint_keypair,
|
|
|
|
..
|
2019-11-08 20:56:57 -08:00
|
|
|
} = create_genesis_config(100);
|
|
|
|
let bank = Bank::new(&genesis_config);
|
2019-03-06 14:31:58 -08:00
|
|
|
let blockhash = bank.last_blockhash();
|
2020-06-12 10:04:17 -07:00
|
|
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
|
2019-05-06 07:31:50 -07:00
|
|
|
let alice = Keypair::new();
|
2019-04-03 08:45:57 -07:00
|
|
|
let tx = system_transaction::create_account(
|
2019-03-06 14:31:58 -08:00
|
|
|
&mint_keypair,
|
2019-11-08 02:27:35 -08:00
|
|
|
&alice,
|
2019-03-06 14:31:58 -08:00
|
|
|
blockhash,
|
|
|
|
1,
|
|
|
|
16,
|
2019-11-20 16:32:19 -08:00
|
|
|
&solana_budget_program::id(),
|
2019-03-06 14:31:58 -08:00
|
|
|
);
|
2019-05-06 07:31:50 -07:00
|
|
|
bank_forks
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.get(0)
|
|
|
|
.unwrap()
|
|
|
|
.process_transaction(&tx)
|
|
|
|
.unwrap();
|
2019-03-06 14:31:58 -08:00
|
|
|
|
2020-01-20 13:08:29 -08:00
|
|
|
let (subscriber, _id_receiver, transport_receiver) =
|
2019-03-06 14:31:58 -08:00
|
|
|
Subscriber::new_test("programNotification");
|
|
|
|
let sub_id = SubscriptionId::Number(0 as u64);
|
2020-01-20 13:08:29 -08:00
|
|
|
let exit = Arc::new(AtomicBool::new(false));
|
2020-03-30 16:53:25 -07:00
|
|
|
let subscriptions = RpcSubscriptions::new(
|
|
|
|
&exit,
|
2020-05-06 23:23:06 -07:00
|
|
|
bank_forks,
|
2020-06-25 21:06:58 -07:00
|
|
|
Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests())),
|
2020-03-30 16:53:25 -07:00
|
|
|
);
|
2020-03-24 09:53:32 -07:00
|
|
|
subscriptions.add_program_subscription(
|
|
|
|
solana_budget_program::id(),
|
|
|
|
None,
|
|
|
|
sub_id.clone(),
|
|
|
|
subscriber,
|
|
|
|
);
|
2019-03-06 14:31:58 -08:00
|
|
|
|
|
|
|
assert!(subscriptions
|
2020-05-06 23:23:06 -07:00
|
|
|
.subscriptions
|
2019-03-06 14:31:58 -08:00
|
|
|
.program_subscriptions
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
2019-11-20 16:32:19 -08:00
|
|
|
.contains_key(&solana_budget_program::id()));
|
2019-03-06 14:31:58 -08:00
|
|
|
|
2020-07-17 10:54:49 -07:00
|
|
|
subscriptions.notify_subscribers(CommitmentSlots::default());
|
2020-03-27 09:33:40 -07:00
|
|
|
let (response, _) = robust_poll_or_panic(transport_receiver);
|
2020-03-23 05:34:42 -07:00
|
|
|
let expected = json!({
|
|
|
|
"jsonrpc": "2.0",
|
|
|
|
"method": "programNotification",
|
|
|
|
"params": {
|
|
|
|
"result": {
|
|
|
|
"context": { "slot": 0 },
|
|
|
|
"value": {
|
|
|
|
"account": {
|
|
|
|
"data": "1111111111111111",
|
|
|
|
"executable": false,
|
|
|
|
"lamports": 1,
|
|
|
|
"owner": "Budget1111111111111111111111111111111111111",
|
|
|
|
"rentEpoch": 1,
|
|
|
|
},
|
|
|
|
"pubkey": alice.pubkey().to_string(),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"subscription": 0,
|
|
|
|
}
|
|
|
|
});
|
|
|
|
assert_eq!(serde_json::to_string(&expected).unwrap(), response);
|
2019-03-06 14:31:58 -08:00
|
|
|
|
|
|
|
subscriptions.remove_program_subscription(&sub_id);
|
|
|
|
assert!(!subscriptions
|
2020-05-06 23:23:06 -07:00
|
|
|
.subscriptions
|
2019-03-06 14:31:58 -08:00
|
|
|
.program_subscriptions
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
2019-11-20 16:32:19 -08:00
|
|
|
.contains_key(&solana_budget_program::id()));
|
2019-03-06 14:31:58 -08:00
|
|
|
}
|
2020-02-11 17:09:40 -08:00
|
|
|
|
2019-02-17 08:38:36 -08:00
|
|
|
#[test]
|
2020-04-17 10:48:39 -07:00
|
|
|
#[serial]
|
2019-02-17 08:38:36 -08:00
|
|
|
fn test_check_signature_subscribe() {
|
2019-11-08 20:56:57 -08:00
|
|
|
let GenesisConfigInfo {
|
|
|
|
genesis_config,
|
2019-05-22 20:39:00 -07:00
|
|
|
mint_keypair,
|
|
|
|
..
|
2019-11-08 20:56:57 -08:00
|
|
|
} = create_genesis_config(100);
|
|
|
|
let bank = Bank::new(&genesis_config);
|
2019-03-02 10:25:16 -08:00
|
|
|
let blockhash = bank.last_blockhash();
|
2020-06-12 10:04:17 -07:00
|
|
|
let mut bank_forks = BankForks::new(bank);
|
2019-05-06 07:31:50 -07:00
|
|
|
let alice = Keypair::new();
|
2020-03-23 17:00:34 -07:00
|
|
|
|
|
|
|
let past_bank_tx =
|
|
|
|
system_transaction::transfer(&mint_keypair, &alice.pubkey(), 1, blockhash);
|
2020-02-13 09:00:50 -08:00
|
|
|
let unprocessed_tx =
|
2020-03-23 17:00:34 -07:00
|
|
|
system_transaction::transfer(&mint_keypair, &alice.pubkey(), 2, blockhash);
|
|
|
|
let processed_tx =
|
|
|
|
system_transaction::transfer(&mint_keypair, &alice.pubkey(), 3, blockhash);
|
|
|
|
|
2019-05-06 07:31:50 -07:00
|
|
|
bank_forks
|
|
|
|
.get(0)
|
|
|
|
.unwrap()
|
2020-03-23 17:00:34 -07:00
|
|
|
.process_transaction(&past_bank_tx)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let next_bank =
|
|
|
|
Bank::new_from_parent(&bank_forks.banks[&0].clone(), &Pubkey::new_rand(), 1);
|
|
|
|
bank_forks.insert(next_bank);
|
|
|
|
|
|
|
|
bank_forks
|
|
|
|
.get(1)
|
|
|
|
.unwrap()
|
|
|
|
.process_transaction(&processed_tx)
|
2019-05-06 07:31:50 -07:00
|
|
|
.unwrap();
|
2020-03-30 16:53:25 -07:00
|
|
|
let bank1 = bank_forks[1].clone();
|
2019-02-17 08:38:36 -08:00
|
|
|
|
2020-03-23 17:00:34 -07:00
|
|
|
let bank_forks = Arc::new(RwLock::new(bank_forks));
|
|
|
|
|
2020-03-30 16:53:25 -07:00
|
|
|
let mut cache0 = BlockCommitment::default();
|
|
|
|
cache0.increase_confirmation_stake(1, 10);
|
|
|
|
let cache1 = BlockCommitment::default();
|
|
|
|
|
|
|
|
let mut block_commitment = HashMap::new();
|
2020-05-15 09:35:43 -07:00
|
|
|
block_commitment.entry(0).or_insert(cache0);
|
|
|
|
block_commitment.entry(1).or_insert(cache1);
|
2020-07-17 08:24:51 -07:00
|
|
|
let block_commitment_cache = BlockCommitmentCache::new(
|
|
|
|
block_commitment,
|
|
|
|
10,
|
2020-07-17 10:54:49 -07:00
|
|
|
CommitmentSlots {
|
2020-07-17 08:24:51 -07:00
|
|
|
slot: bank1.slot(),
|
|
|
|
root: 0,
|
|
|
|
highest_confirmed_slot: 0,
|
|
|
|
highest_confirmed_root: 0,
|
|
|
|
},
|
|
|
|
);
|
2020-03-30 16:53:25 -07:00
|
|
|
|
2020-01-20 13:08:29 -08:00
|
|
|
let exit = Arc::new(AtomicBool::new(false));
|
2020-05-06 23:23:06 -07:00
|
|
|
let subscriptions = RpcSubscriptions::new(
|
|
|
|
&exit,
|
|
|
|
bank_forks,
|
|
|
|
Arc::new(RwLock::new(block_commitment_cache)),
|
|
|
|
);
|
2020-03-24 09:53:32 -07:00
|
|
|
|
2020-03-30 16:53:25 -07:00
|
|
|
let (past_bank_sub1, _id_receiver, past_bank_recv1) =
|
|
|
|
Subscriber::new_test("signatureNotification");
|
|
|
|
let (past_bank_sub2, _id_receiver, past_bank_recv2) =
|
2020-03-24 09:53:32 -07:00
|
|
|
Subscriber::new_test("signatureNotification");
|
|
|
|
let (processed_sub, _id_receiver, processed_recv) =
|
|
|
|
Subscriber::new_test("signatureNotification");
|
2020-03-30 16:53:25 -07:00
|
|
|
|
2020-02-13 09:00:50 -08:00
|
|
|
subscriptions.add_signature_subscription(
|
2020-03-24 09:53:32 -07:00
|
|
|
past_bank_tx.signatures[0],
|
2020-05-06 23:23:06 -07:00
|
|
|
Some(CommitmentConfig::recent()),
|
2020-03-24 09:53:32 -07:00
|
|
|
SubscriptionId::Number(1 as u64),
|
2020-03-30 16:53:25 -07:00
|
|
|
past_bank_sub1,
|
2020-03-23 17:00:34 -07:00
|
|
|
);
|
|
|
|
subscriptions.add_signature_subscription(
|
2020-03-24 09:53:32 -07:00
|
|
|
past_bank_tx.signatures[0],
|
2020-05-06 23:23:06 -07:00
|
|
|
Some(CommitmentConfig::root()),
|
2020-03-24 09:53:32 -07:00
|
|
|
SubscriptionId::Number(2 as u64),
|
2020-03-30 16:53:25 -07:00
|
|
|
past_bank_sub2,
|
2020-03-24 09:53:32 -07:00
|
|
|
);
|
|
|
|
subscriptions.add_signature_subscription(
|
|
|
|
processed_tx.signatures[0],
|
2020-05-06 23:23:06 -07:00
|
|
|
Some(CommitmentConfig::recent()),
|
2020-03-24 09:53:32 -07:00
|
|
|
SubscriptionId::Number(3 as u64),
|
|
|
|
processed_sub,
|
2020-03-23 17:00:34 -07:00
|
|
|
);
|
|
|
|
subscriptions.add_signature_subscription(
|
2020-03-24 09:53:32 -07:00
|
|
|
unprocessed_tx.signatures[0],
|
2020-05-06 23:23:06 -07:00
|
|
|
Some(CommitmentConfig::recent()),
|
2020-03-24 09:53:32 -07:00
|
|
|
SubscriptionId::Number(4 as u64),
|
|
|
|
Subscriber::new_test("signatureNotification").0,
|
2020-02-13 09:00:50 -08:00
|
|
|
);
|
2019-02-17 08:38:36 -08:00
|
|
|
|
2020-02-13 09:00:50 -08:00
|
|
|
{
|
2020-05-06 23:23:06 -07:00
|
|
|
let sig_subs = subscriptions
|
|
|
|
.subscriptions
|
|
|
|
.signature_subscriptions
|
|
|
|
.read()
|
|
|
|
.unwrap();
|
2020-03-24 09:53:32 -07:00
|
|
|
assert_eq!(sig_subs.get(&past_bank_tx.signatures[0]).unwrap().len(), 2);
|
2020-03-23 17:00:34 -07:00
|
|
|
assert!(sig_subs.contains_key(&unprocessed_tx.signatures[0]));
|
|
|
|
assert!(sig_subs.contains_key(&processed_tx.signatures[0]));
|
2020-02-13 09:00:50 -08:00
|
|
|
}
|
2020-07-17 10:54:49 -07:00
|
|
|
let mut commitment_slots = CommitmentSlots::default();
|
|
|
|
commitment_slots.slot = 1;
|
|
|
|
subscriptions.notify_subscribers(commitment_slots);
|
2020-04-04 16:13:26 -07:00
|
|
|
let expected_res = RpcSignatureResult { err: None };
|
2020-03-24 09:53:32 -07:00
|
|
|
|
2020-03-30 16:53:25 -07:00
|
|
|
struct Notification {
|
|
|
|
slot: Slot,
|
|
|
|
id: u64,
|
|
|
|
}
|
2020-03-24 09:53:32 -07:00
|
|
|
|
2020-03-30 16:53:25 -07:00
|
|
|
let expected_notification = |exp: Notification| -> String {
|
|
|
|
let json = json!({
|
|
|
|
"jsonrpc": "2.0",
|
|
|
|
"method": "signatureNotification",
|
|
|
|
"params": {
|
|
|
|
"result": {
|
|
|
|
"context": { "slot": exp.slot },
|
|
|
|
"value": &expected_res,
|
|
|
|
},
|
|
|
|
"subscription": exp.id,
|
|
|
|
}
|
|
|
|
});
|
|
|
|
serde_json::to_string(&json).unwrap()
|
|
|
|
};
|
2019-02-17 08:38:36 -08:00
|
|
|
|
2020-03-30 16:53:25 -07:00
|
|
|
// Expect to receive a notification from bank 1 because this subscription is
|
|
|
|
// looking for 0 confirmations and so checks the current bank
|
|
|
|
let expected = expected_notification(Notification { slot: 1, id: 1 });
|
|
|
|
let (response, _) = robust_poll_or_panic(past_bank_recv1);
|
|
|
|
assert_eq!(expected, response);
|
|
|
|
|
|
|
|
// Expect to receive a notification from bank 0 because this subscription is
|
|
|
|
// looking for 1 confirmation and so checks the past bank
|
|
|
|
let expected = expected_notification(Notification { slot: 0, id: 2 });
|
|
|
|
let (response, _) = robust_poll_or_panic(past_bank_recv2);
|
|
|
|
assert_eq!(expected, response);
|
|
|
|
|
|
|
|
let expected = expected_notification(Notification { slot: 1, id: 3 });
|
|
|
|
let (response, _) = robust_poll_or_panic(processed_recv);
|
|
|
|
assert_eq!(expected, response);
|
2020-03-23 17:00:34 -07:00
|
|
|
|
2020-02-13 09:00:50 -08:00
|
|
|
// Subscription should be automatically removed after notification
|
2020-05-06 23:23:06 -07:00
|
|
|
let sig_subs = subscriptions
|
|
|
|
.subscriptions
|
|
|
|
.signature_subscriptions
|
|
|
|
.read()
|
|
|
|
.unwrap();
|
2020-03-23 17:00:34 -07:00
|
|
|
assert!(!sig_subs.contains_key(&processed_tx.signatures[0]));
|
2020-03-30 16:53:25 -07:00
|
|
|
assert!(!sig_subs.contains_key(&past_bank_tx.signatures[0]));
|
2020-02-13 09:00:50 -08:00
|
|
|
|
|
|
|
// Unprocessed signature subscription should not be removed
|
2020-03-23 17:00:34 -07:00
|
|
|
assert_eq!(
|
|
|
|
sig_subs.get(&unprocessed_tx.signatures[0]).unwrap().len(),
|
|
|
|
1
|
|
|
|
);
|
2019-02-17 08:38:36 -08:00
|
|
|
}
|
2020-02-11 17:09:40 -08:00
|
|
|
|
2019-11-26 00:42:54 -08:00
|
|
|
#[test]
|
2020-04-17 10:48:39 -07:00
|
|
|
#[serial]
|
2019-11-26 00:42:54 -08:00
|
|
|
fn test_check_slot_subscribe() {
|
2020-01-20 13:08:29 -08:00
|
|
|
let (subscriber, _id_receiver, transport_receiver) =
|
2019-11-26 00:42:54 -08:00
|
|
|
Subscriber::new_test("slotNotification");
|
|
|
|
let sub_id = SubscriptionId::Number(0 as u64);
|
2020-01-20 13:08:29 -08:00
|
|
|
let exit = Arc::new(AtomicBool::new(false));
|
2020-05-06 23:23:06 -07:00
|
|
|
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
|
|
|
|
let bank = Bank::new(&genesis_config);
|
2020-06-12 10:04:17 -07:00
|
|
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
|
2020-03-30 16:53:25 -07:00
|
|
|
let subscriptions = RpcSubscriptions::new(
|
|
|
|
&exit,
|
2020-05-06 23:23:06 -07:00
|
|
|
bank_forks,
|
2020-06-25 21:06:58 -07:00
|
|
|
Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests())),
|
2020-03-30 16:53:25 -07:00
|
|
|
);
|
2020-03-24 09:53:32 -07:00
|
|
|
subscriptions.add_slot_subscription(sub_id.clone(), subscriber);
|
2019-11-26 00:42:54 -08:00
|
|
|
|
|
|
|
assert!(subscriptions
|
2020-05-06 23:23:06 -07:00
|
|
|
.subscriptions
|
2019-11-26 00:42:54 -08:00
|
|
|
.slot_subscriptions
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.contains_key(&sub_id));
|
|
|
|
|
|
|
|
subscriptions.notify_slot(0, 0, 0);
|
2020-03-27 09:33:40 -07:00
|
|
|
let (response, _) = robust_poll_or_panic(transport_receiver);
|
2020-01-20 13:08:29 -08:00
|
|
|
let expected_res = SlotInfo {
|
|
|
|
parent: 0,
|
|
|
|
slot: 0,
|
|
|
|
root: 0,
|
|
|
|
};
|
|
|
|
let expected_res_str =
|
|
|
|
serde_json::to_string(&serde_json::to_value(expected_res).unwrap()).unwrap();
|
|
|
|
let expected = format!(
|
|
|
|
r#"{{"jsonrpc":"2.0","method":"slotNotification","params":{{"result":{},"subscription":0}}}}"#,
|
|
|
|
expected_res_str
|
|
|
|
);
|
|
|
|
assert_eq!(expected, response);
|
2019-11-26 00:42:54 -08:00
|
|
|
|
|
|
|
subscriptions.remove_slot_subscription(&sub_id);
|
|
|
|
assert!(!subscriptions
|
2020-05-06 23:23:06 -07:00
|
|
|
.subscriptions
|
2019-11-26 00:42:54 -08:00
|
|
|
.slot_subscriptions
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.contains_key(&sub_id));
|
|
|
|
}
|
2020-02-11 17:09:40 -08:00
|
|
|
|
2020-03-27 09:33:40 -07:00
|
|
|
#[test]
|
2020-04-17 10:48:39 -07:00
|
|
|
#[serial]
|
2020-03-27 09:33:40 -07:00
|
|
|
fn test_check_root_subscribe() {
|
|
|
|
let (subscriber, _id_receiver, mut transport_receiver) =
|
|
|
|
Subscriber::new_test("rootNotification");
|
|
|
|
let sub_id = SubscriptionId::Number(0 as u64);
|
|
|
|
let exit = Arc::new(AtomicBool::new(false));
|
2020-05-06 23:23:06 -07:00
|
|
|
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(10_000);
|
|
|
|
let bank = Bank::new(&genesis_config);
|
2020-06-12 10:04:17 -07:00
|
|
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
|
2020-03-30 16:53:25 -07:00
|
|
|
let subscriptions = RpcSubscriptions::new(
|
|
|
|
&exit,
|
2020-05-06 23:23:06 -07:00
|
|
|
bank_forks,
|
2020-06-25 21:06:58 -07:00
|
|
|
Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests())),
|
2020-03-30 16:53:25 -07:00
|
|
|
);
|
2020-03-27 09:33:40 -07:00
|
|
|
subscriptions.add_root_subscription(sub_id.clone(), subscriber);
|
|
|
|
|
|
|
|
assert!(subscriptions
|
2020-05-06 23:23:06 -07:00
|
|
|
.subscriptions
|
2020-03-27 09:33:40 -07:00
|
|
|
.root_subscriptions
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.contains_key(&sub_id));
|
|
|
|
|
|
|
|
subscriptions.notify_roots(vec![2, 1, 3]);
|
|
|
|
|
|
|
|
for expected_root in 1..=3 {
|
|
|
|
let (response, receiver) = robust_poll_or_panic(transport_receiver);
|
|
|
|
transport_receiver = receiver;
|
|
|
|
let expected_res_str =
|
|
|
|
serde_json::to_string(&serde_json::to_value(expected_root).unwrap()).unwrap();
|
|
|
|
let expected = format!(
|
|
|
|
r#"{{"jsonrpc":"2.0","method":"rootNotification","params":{{"result":{},"subscription":0}}}}"#,
|
|
|
|
expected_res_str
|
|
|
|
);
|
|
|
|
assert_eq!(expected, response);
|
|
|
|
}
|
|
|
|
|
|
|
|
subscriptions.remove_root_subscription(&sub_id);
|
|
|
|
assert!(!subscriptions
|
2020-05-06 23:23:06 -07:00
|
|
|
.subscriptions
|
2020-03-27 09:33:40 -07:00
|
|
|
.root_subscriptions
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.contains_key(&sub_id));
|
|
|
|
}
|
|
|
|
|
2020-02-11 17:09:40 -08:00
|
|
|
#[test]
|
2020-04-17 10:48:39 -07:00
|
|
|
#[serial]
|
2020-02-11 17:09:40 -08:00
|
|
|
fn test_add_and_remove_subscription() {
|
2020-07-23 12:38:28 -07:00
|
|
|
let mut subscriptions: HashMap<u64, HashMap<SubscriptionId, SubscriptionData<(), ()>>> =
|
2020-02-11 17:09:40 -08:00
|
|
|
HashMap::new();
|
|
|
|
|
|
|
|
let num_keys = 5;
|
2020-03-24 09:53:32 -07:00
|
|
|
for key in 0..num_keys {
|
|
|
|
let (subscriber, _id_receiver, _transport_receiver) =
|
|
|
|
Subscriber::new_test("notification");
|
|
|
|
let sub_id = SubscriptionId::Number(key);
|
2020-07-23 12:38:28 -07:00
|
|
|
add_subscription(&mut subscriptions, key, None, sub_id, subscriber, 0, None);
|
2020-02-11 17:09:40 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add another subscription to the "0" key
|
2020-03-24 09:53:32 -07:00
|
|
|
let (subscriber, _id_receiver, _transport_receiver) = Subscriber::new_test("notification");
|
|
|
|
let extra_sub_id = SubscriptionId::Number(num_keys);
|
|
|
|
add_subscription(
|
|
|
|
&mut subscriptions,
|
|
|
|
0,
|
|
|
|
None,
|
|
|
|
extra_sub_id.clone(),
|
|
|
|
subscriber,
|
2020-05-06 23:23:06 -07:00
|
|
|
0,
|
2020-07-23 12:38:28 -07:00
|
|
|
None,
|
2020-03-24 09:53:32 -07:00
|
|
|
);
|
2020-02-11 17:09:40 -08:00
|
|
|
|
2020-03-24 09:53:32 -07:00
|
|
|
assert_eq!(subscriptions.len(), num_keys as usize);
|
2020-02-11 17:09:40 -08:00
|
|
|
assert_eq!(subscriptions.get(&0).unwrap().len(), 2);
|
|
|
|
assert_eq!(subscriptions.get(&1).unwrap().len(), 1);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
remove_subscription(&mut subscriptions, &SubscriptionId::Number(0)),
|
|
|
|
true
|
|
|
|
);
|
2020-03-24 09:53:32 -07:00
|
|
|
assert_eq!(subscriptions.len(), num_keys as usize);
|
2020-02-11 17:09:40 -08:00
|
|
|
assert_eq!(subscriptions.get(&0).unwrap().len(), 1);
|
|
|
|
assert_eq!(
|
|
|
|
remove_subscription(&mut subscriptions, &SubscriptionId::Number(0)),
|
|
|
|
false
|
|
|
|
);
|
|
|
|
|
2020-03-24 09:53:32 -07:00
|
|
|
assert_eq!(remove_subscription(&mut subscriptions, &extra_sub_id), true);
|
|
|
|
assert_eq!(subscriptions.len(), (num_keys - 1) as usize);
|
2020-02-11 17:09:40 -08:00
|
|
|
assert!(subscriptions.get(&0).is_none());
|
|
|
|
}
|
2020-05-22 12:55:17 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
#[serial]
|
|
|
|
fn test_gossip_separate_account_notifications() {
|
|
|
|
let GenesisConfigInfo {
|
|
|
|
genesis_config,
|
|
|
|
mint_keypair,
|
|
|
|
..
|
|
|
|
} = create_genesis_config(100);
|
|
|
|
let bank = Bank::new(&genesis_config);
|
|
|
|
let blockhash = bank.last_blockhash();
|
2020-06-12 10:04:17 -07:00
|
|
|
let bank_forks = Arc::new(RwLock::new(BankForks::new(bank)));
|
2020-05-22 12:55:17 -07:00
|
|
|
let bank0 = bank_forks.read().unwrap().get(0).unwrap().clone();
|
|
|
|
let bank1 = Bank::new_from_parent(&bank0, &Pubkey::default(), 1);
|
|
|
|
bank_forks.write().unwrap().insert(bank1);
|
2020-06-17 09:44:51 -07:00
|
|
|
let bank2 = Bank::new_from_parent(&bank0, &Pubkey::default(), 2);
|
|
|
|
bank_forks.write().unwrap().insert(bank2);
|
2020-05-22 12:55:17 -07:00
|
|
|
let alice = Keypair::new();
|
|
|
|
|
|
|
|
let (subscriber0, _id_receiver, transport_receiver0) =
|
|
|
|
Subscriber::new_test("accountNotification");
|
|
|
|
let (subscriber1, _id_receiver, transport_receiver1) =
|
|
|
|
Subscriber::new_test("accountNotification");
|
|
|
|
let exit = Arc::new(AtomicBool::new(false));
|
|
|
|
let subscriptions = RpcSubscriptions::new(
|
|
|
|
&exit,
|
|
|
|
bank_forks.clone(),
|
2020-07-08 17:50:13 -07:00
|
|
|
Arc::new(RwLock::new(BlockCommitmentCache::new_for_tests_with_slots(
|
|
|
|
1, 1,
|
2020-06-25 21:06:58 -07:00
|
|
|
))),
|
2020-05-22 12:55:17 -07:00
|
|
|
);
|
|
|
|
let sub_id0 = SubscriptionId::Number(0 as u64);
|
|
|
|
subscriptions.add_account_subscription(
|
|
|
|
alice.pubkey(),
|
2020-07-23 12:38:28 -07:00
|
|
|
Some(RpcAccountInfoConfig {
|
|
|
|
commitment: Some(CommitmentConfig::single_gossip()),
|
|
|
|
encoding: None,
|
|
|
|
}),
|
2020-05-22 12:55:17 -07:00
|
|
|
sub_id0.clone(),
|
|
|
|
subscriber0,
|
|
|
|
);
|
|
|
|
|
|
|
|
assert!(subscriptions
|
|
|
|
.subscriptions
|
2020-06-17 09:44:51 -07:00
|
|
|
.gossip_account_subscriptions
|
2020-05-22 12:55:17 -07:00
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.contains_key(&alice.pubkey()));
|
|
|
|
|
|
|
|
let tx = system_transaction::create_account(
|
|
|
|
&mint_keypair,
|
|
|
|
&alice,
|
|
|
|
blockhash,
|
|
|
|
1,
|
|
|
|
16,
|
|
|
|
&solana_budget_program::id(),
|
|
|
|
);
|
2020-06-17 09:44:51 -07:00
|
|
|
|
|
|
|
// Add the transaction to the 1st bank and then freeze the bank
|
|
|
|
let bank1 = bank_forks.write().unwrap().get(1).cloned().unwrap();
|
|
|
|
bank1.process_transaction(&tx).unwrap();
|
|
|
|
bank1.freeze();
|
|
|
|
|
|
|
|
// Add the same transaction to the unfrozen 2nd bank
|
2020-05-22 12:55:17 -07:00
|
|
|
bank_forks
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
2020-06-17 09:44:51 -07:00
|
|
|
.get(2)
|
2020-05-22 12:55:17 -07:00
|
|
|
.unwrap()
|
|
|
|
.process_transaction(&tx)
|
|
|
|
.unwrap();
|
2020-06-17 09:44:51 -07:00
|
|
|
|
|
|
|
// First, notify the unfrozen bank first to queue pending notification
|
|
|
|
subscriptions.notify_gossip_subscribers(2);
|
|
|
|
|
|
|
|
// Now, notify the frozen bank and ensure its notifications are processed
|
|
|
|
subscriptions.notify_gossip_subscribers(1);
|
|
|
|
|
|
|
|
let (response, _) = robust_poll_or_panic(transport_receiver0);
|
2020-05-22 12:55:17 -07:00
|
|
|
let expected = json!({
|
|
|
|
"jsonrpc": "2.0",
|
|
|
|
"method": "accountNotification",
|
|
|
|
"params": {
|
|
|
|
"result": {
|
|
|
|
"context": { "slot": 1 },
|
|
|
|
"value": {
|
|
|
|
"data": "1111111111111111",
|
|
|
|
"executable": false,
|
|
|
|
"lamports": 1,
|
|
|
|
"owner": "Budget1111111111111111111111111111111111111",
|
|
|
|
"rentEpoch": 1,
|
|
|
|
},
|
|
|
|
},
|
2020-06-17 09:44:51 -07:00
|
|
|
"subscription": 0,
|
2020-05-22 12:55:17 -07:00
|
|
|
}
|
|
|
|
});
|
|
|
|
assert_eq!(serde_json::to_string(&expected).unwrap(), response);
|
2020-06-17 09:44:51 -07:00
|
|
|
subscriptions.remove_account_subscription(&sub_id0);
|
2020-05-22 12:55:17 -07:00
|
|
|
|
2020-06-17 09:44:51 -07:00
|
|
|
let sub_id1 = SubscriptionId::Number(1 as u64);
|
|
|
|
subscriptions.add_account_subscription(
|
|
|
|
alice.pubkey(),
|
2020-07-23 12:38:28 -07:00
|
|
|
Some(RpcAccountInfoConfig {
|
|
|
|
commitment: Some(CommitmentConfig::single_gossip()),
|
|
|
|
encoding: None,
|
|
|
|
}),
|
2020-06-17 09:44:51 -07:00
|
|
|
sub_id1.clone(),
|
|
|
|
subscriber1,
|
|
|
|
);
|
|
|
|
|
|
|
|
subscriptions.notify_frozen(2);
|
|
|
|
let (response, _) = robust_poll_or_panic(transport_receiver1);
|
2020-05-22 12:55:17 -07:00
|
|
|
let expected = json!({
|
|
|
|
"jsonrpc": "2.0",
|
|
|
|
"method": "accountNotification",
|
|
|
|
"params": {
|
|
|
|
"result": {
|
2020-06-17 09:44:51 -07:00
|
|
|
"context": { "slot": 2 },
|
2020-05-22 12:55:17 -07:00
|
|
|
"value": {
|
|
|
|
"data": "1111111111111111",
|
|
|
|
"executable": false,
|
|
|
|
"lamports": 1,
|
|
|
|
"owner": "Budget1111111111111111111111111111111111111",
|
|
|
|
"rentEpoch": 1,
|
|
|
|
},
|
|
|
|
},
|
2020-06-17 09:44:51 -07:00
|
|
|
"subscription": 1,
|
2020-05-22 12:55:17 -07:00
|
|
|
}
|
|
|
|
});
|
|
|
|
assert_eq!(serde_json::to_string(&expected).unwrap(), response);
|
|
|
|
subscriptions.remove_account_subscription(&sub_id1);
|
2020-06-17 09:44:51 -07:00
|
|
|
|
2020-05-22 12:55:17 -07:00
|
|
|
assert!(!subscriptions
|
|
|
|
.subscriptions
|
2020-06-17 09:44:51 -07:00
|
|
|
.gossip_account_subscriptions
|
2020-05-22 12:55:17 -07:00
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.contains_key(&alice.pubkey()));
|
|
|
|
}
|
2019-02-17 08:38:36 -08:00
|
|
|
}
|