Trigger RPC notifications after block commitment cache update (#10077)

* Fixup commitment-aggregation metric

* Trigger notifications after commitment-cache update

* Fixup fn name

* Add single-confirmation commitment level

* Rename to highest_confirmed_slot

* Pass commitment-cache info directly to notifications

* Use match

* Update commitment docs

* Update out of date pubsub docs
This commit is contained in:
Tyera Eulberg 2020-05-18 12:49:01 -06:00 committed by GitHub
parent 4ca352a344
commit bac4aec16f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 295 additions and 121 deletions

View File

@ -1,7 +1,7 @@
use crate::consensus::VOTE_THRESHOLD_SIZE;
use crate::{consensus::VOTE_THRESHOLD_SIZE, rpc_subscriptions::RpcSubscriptions};
use solana_ledger::blockstore::Blockstore;
use solana_measure::measure::Measure;
use solana_metrics::inc_new_counter_info;
use solana_metrics::datapoint_info;
use solana_runtime::bank::Bank;
use solana_sdk::clock::Slot;
use solana_vote_program::{vote_state::VoteState, vote_state::MAX_LOCKOUT_HISTORY};
@ -14,6 +14,14 @@ use std::{
time::Duration,
};
#[derive(Default)]
pub struct CacheSlotInfo {
pub current_slot: Slot,
pub node_root: Slot,
pub largest_confirmed_root: Slot,
pub highest_confirmed_slot: Slot,
}
pub type BlockCommitmentArray = [u64; MAX_LOCKOUT_HISTORY + 1];
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
@ -53,6 +61,7 @@ pub struct BlockCommitmentCache {
bank: Arc<Bank>,
blockstore: Arc<Blockstore>,
root: Slot,
highest_confirmed_slot: Slot,
}
impl std::fmt::Debug for BlockCommitmentCache {
@ -77,6 +86,7 @@ impl BlockCommitmentCache {
bank: Arc<Bank>,
blockstore: Arc<Blockstore>,
root: Slot,
highest_confirmed_slot: Slot,
) -> Self {
Self {
block_commitment,
@ -85,6 +95,7 @@ impl BlockCommitmentCache {
bank,
blockstore,
root,
highest_confirmed_slot,
}
}
@ -96,6 +107,7 @@ impl BlockCommitmentCache {
bank: Arc::new(Bank::default()),
blockstore,
root: Slot::default(),
highest_confirmed_slot: Slot::default(),
}
}
@ -123,6 +135,26 @@ impl BlockCommitmentCache {
self.root
}
pub fn highest_confirmed_slot(&self) -> Slot {
self.highest_confirmed_slot
}
fn highest_slot_with_confirmation_count(&self, confirmation_count: usize) -> Slot {
assert!(confirmation_count > 0 && confirmation_count <= MAX_LOCKOUT_HISTORY);
for slot in (self.root()..self.slot()).rev() {
if let Some(count) = self.get_confirmation_count(slot) {
if count >= confirmation_count {
return slot;
}
}
}
self.root
}
fn calculate_highest_confirmed_slot(&self) -> Slot {
self.highest_slot_with_confirmation_count(1)
}
pub fn get_confirmation_count(&self, slot: Slot) -> Option<usize> {
self.get_lockout_count(slot, VOTE_THRESHOLD_SIZE)
}
@ -159,6 +191,7 @@ impl BlockCommitmentCache {
largest_confirmed_root: Slot::default(),
bank: Arc::new(Bank::default()),
root: Slot::default(),
highest_confirmed_slot: Slot::default(),
}
}
@ -177,10 +210,11 @@ impl BlockCommitmentCache {
largest_confirmed_root: root,
bank,
root,
highest_confirmed_slot: root,
}
}
pub(crate) fn set_get_largest_confirmed_root(&mut self, root: Slot) {
pub(crate) fn set_largest_confirmed_root(&mut self, root: Slot) {
self.largest_confirmed_root = root;
}
}
@ -221,6 +255,7 @@ impl AggregateCommitmentService {
pub fn new(
exit: &Arc<AtomicBool>,
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
subscriptions: Arc<RpcSubscriptions>,
) -> (Sender<CommitmentAggregationData>, Self) {
let (sender, receiver): (
Sender<CommitmentAggregationData>,
@ -238,7 +273,7 @@ impl AggregateCommitmentService {
}
if let Err(RecvTimeoutError::Disconnected) =
Self::run(&receiver, &block_commitment_cache, &exit_)
Self::run(&receiver, &block_commitment_cache, &subscriptions, &exit_)
{
break;
}
@ -251,6 +286,7 @@ impl AggregateCommitmentService {
fn run(
receiver: &Receiver<CommitmentAggregationData>,
block_commitment_cache: &RwLock<BlockCommitmentCache>,
subscriptions: &Arc<RpcSubscriptions>,
exit: &Arc<AtomicBool>,
) -> Result<(), RecvTimeoutError> {
loop {
@ -283,16 +319,30 @@ impl AggregateCommitmentService {
aggregation_data.bank,
block_commitment_cache.read().unwrap().blockstore.clone(),
aggregation_data.root,
aggregation_data.root,
);
new_block_commitment.highest_confirmed_slot =
new_block_commitment.calculate_highest_confirmed_slot();
let mut w_block_commitment_cache = block_commitment_cache.write().unwrap();
std::mem::swap(&mut *w_block_commitment_cache, &mut new_block_commitment);
aggregate_commitment_time.stop();
inc_new_counter_info!(
"aggregate-commitment-ms",
aggregate_commitment_time.as_ms() as usize
datapoint_info!(
"block-commitment-cache",
(
"aggregate-commitment-ms",
aggregate_commitment_time.as_ms() as i64,
i64
)
);
subscriptions.notify_subscribers(CacheSlotInfo {
current_slot: w_block_commitment_cache.slot(),
node_root: w_block_commitment_cache.root(),
largest_confirmed_root: w_block_commitment_cache.largest_confirmed_root(),
highest_confirmed_slot: w_block_commitment_cache.highest_confirmed_slot(),
});
}
}
@ -382,7 +432,7 @@ mod tests {
genesis_utils::{create_genesis_config, GenesisConfigInfo},
get_tmp_ledger_path,
};
use solana_sdk::pubkey::Pubkey;
use solana_sdk::{genesis_config::GenesisConfig, pubkey::Pubkey};
use solana_stake_program::stake_state;
use solana_vote_program::vote_state::{self, VoteStateVersions};
@ -419,7 +469,7 @@ mod tests {
block_commitment.entry(1).or_insert(cache1);
block_commitment.entry(2).or_insert(cache2);
let block_commitment_cache =
BlockCommitmentCache::new(block_commitment, 0, 50, bank, blockstore, 0);
BlockCommitmentCache::new(block_commitment, 0, 50, bank, blockstore, 0, 0);
assert_eq!(block_commitment_cache.get_confirmation_count(0), Some(2));
assert_eq!(block_commitment_cache.get_confirmation_count(1), Some(1));
@ -453,6 +503,7 @@ mod tests {
bank,
blockstore,
0,
0,
);
assert!(block_commitment_cache.is_confirmed_rooted(0));
@ -476,6 +527,114 @@ mod tests {
assert_eq!(get_largest_confirmed_root(rooted_stake, 10), 1);
}
#[test]
fn test_highest_confirmed_slot() {
let bank = Arc::new(Bank::new(&GenesisConfig::default()));
let bank_slot_5 = Arc::new(Bank::new_from_parent(&bank, &Pubkey::default(), 5));
let ledger_path = get_tmp_ledger_path!();
let blockstore = Arc::new(Blockstore::open(&ledger_path).unwrap());
let total_stake = 50;
// Build cache with confirmation_count 2 given total_stake
let mut cache0 = BlockCommitment::default();
cache0.increase_confirmation_stake(1, 5);
cache0.increase_confirmation_stake(2, 40);
// Build cache with confirmation_count 1 given total_stake
let mut cache1 = BlockCommitment::default();
cache1.increase_confirmation_stake(1, 40);
cache1.increase_confirmation_stake(2, 5);
// Build cache with confirmation_count 0 given total_stake
let mut cache2 = BlockCommitment::default();
cache2.increase_confirmation_stake(1, 20);
cache2.increase_confirmation_stake(2, 5);
let mut block_commitment = HashMap::new();
block_commitment.entry(1).or_insert(cache0.clone()); // Slot 1, conf 2
block_commitment.entry(2).or_insert(cache1.clone()); // Slot 2, conf 1
block_commitment.entry(3).or_insert(cache2.clone()); // Slot 3, conf 0
let block_commitment_cache = BlockCommitmentCache::new(
block_commitment,
0,
total_stake,
bank_slot_5.clone(),
blockstore.clone(),
0,
0,
);
assert_eq!(block_commitment_cache.calculate_highest_confirmed_slot(), 2);
// Build map with multiple slots at conf 1
let mut block_commitment = HashMap::new();
block_commitment.entry(1).or_insert(cache1.clone()); // Slot 1, conf 1
block_commitment.entry(2).or_insert(cache1.clone()); // Slot 2, conf 1
block_commitment.entry(3).or_insert(cache2.clone()); // Slot 3, conf 0
let block_commitment_cache = BlockCommitmentCache::new(
block_commitment,
0,
total_stake,
bank_slot_5.clone(),
blockstore.clone(),
0,
0,
);
assert_eq!(block_commitment_cache.calculate_highest_confirmed_slot(), 2);
// Build map with slot gaps
let mut block_commitment = HashMap::new();
block_commitment.entry(1).or_insert(cache1.clone()); // Slot 1, conf 1
block_commitment.entry(3).or_insert(cache1.clone()); // Slot 3, conf 1
block_commitment.entry(5).or_insert(cache2.clone()); // Slot 5, conf 0
let block_commitment_cache = BlockCommitmentCache::new(
block_commitment,
0,
total_stake,
bank_slot_5.clone(),
blockstore.clone(),
0,
0,
);
assert_eq!(block_commitment_cache.calculate_highest_confirmed_slot(), 3);
// Build map with no conf 1 slots, but one higher
let mut block_commitment = HashMap::new();
block_commitment.entry(1).or_insert(cache0.clone()); // Slot 1, conf 2
block_commitment.entry(2).or_insert(cache2.clone()); // Slot 2, conf 0
block_commitment.entry(3).or_insert(cache2.clone()); // Slot 3, conf 0
let block_commitment_cache = BlockCommitmentCache::new(
block_commitment,
0,
total_stake,
bank_slot_5.clone(),
blockstore.clone(),
0,
0,
);
assert_eq!(block_commitment_cache.calculate_highest_confirmed_slot(), 1);
// Build map with no conf 1 or higher slots
let mut block_commitment = HashMap::new();
block_commitment.entry(1).or_insert(cache2.clone()); // Slot 1, conf 0
block_commitment.entry(2).or_insert(cache2.clone()); // Slot 2, conf 0
block_commitment.entry(3).or_insert(cache2.clone()); // Slot 3, conf 0
let block_commitment_cache = BlockCommitmentCache::new(
block_commitment,
0,
total_stake,
bank_slot_5.clone(),
blockstore.clone(),
0,
0,
);
assert_eq!(block_commitment_cache.calculate_highest_confirmed_slot(), 0);
}
#[test]
fn test_aggregate_commitment_for_vote_account_1() {
let ancestors = vec![3, 4, 5, 7, 9, 11];

View File

@ -176,8 +176,11 @@ impl ReplayStage {
let mut tower = Tower::new(&my_pubkey, &vote_account, &bank_forks.read().unwrap());
// Start the replay stage loop
let (lockouts_sender, commitment_service) =
AggregateCommitmentService::new(&exit, block_commitment_cache.clone());
let (lockouts_sender, commitment_service) = AggregateCommitmentService::new(
&exit,
block_commitment_cache.clone(),
subscriptions.clone(),
);
#[allow(clippy::cognitive_complexity)]
let t_replay = Builder::new()
@ -353,8 +356,6 @@ impl ReplayStage {
// Vote on a fork
if let Some(ref vote_bank) = vote_bank {
subscriptions
.notify_subscribers(block_commitment_cache.read().unwrap().slot());
if let Some(votable_leader) =
leader_schedule_cache.slot_leader_at(vote_bank.slot(), Some(vote_bank))
{
@ -2595,13 +2596,6 @@ pub(crate) mod tests {
let ledger_path = get_tmp_ledger_path!();
let blockstore = Arc::new(Blockstore::open(&ledger_path).unwrap());
let block_commitment_cache = Arc::new(RwLock::new(
BlockCommitmentCache::default_with_blockstore(blockstore),
));
let (lockouts_sender, _) = AggregateCommitmentService::new(
&Arc::new(AtomicBool::new(false)),
block_commitment_cache.clone(),
);
let leader_pubkey = Pubkey::new_rand();
let leader_lamports = 3;
@ -2622,6 +2616,18 @@ pub(crate) mod tests {
0,
)));
let exit = Arc::new(AtomicBool::new(false));
let block_commitment_cache = Arc::new(RwLock::new(
BlockCommitmentCache::default_with_blockstore(blockstore.clone()),
));
let subscriptions = Arc::new(RpcSubscriptions::new(
&exit,
bank_forks.clone(),
block_commitment_cache.clone(),
));
let (lockouts_sender, _) =
AggregateCommitmentService::new(&exit, block_commitment_cache.clone(), subscriptions);
assert!(block_commitment_cache
.read()
.unwrap()

View File

@ -79,28 +79,42 @@ impl JsonRpcRequestProcessor {
fn bank(&self, commitment: Option<CommitmentConfig>) -> Result<Arc<Bank>> {
debug!("RPC commitment_config: {:?}", commitment);
let r_bank_forks = self.bank_forks.read().unwrap();
if commitment.is_some() && commitment.unwrap().commitment == CommitmentLevel::Recent {
let bank = r_bank_forks.working_bank();
debug!("RPC using working_bank: {:?}", bank.slot());
Ok(bank)
} else if commitment.is_some() && commitment.unwrap().commitment == CommitmentLevel::Root {
let slot = r_bank_forks.root();
debug!("RPC using node root: {:?}", slot);
Ok(r_bank_forks.get(slot).cloned().unwrap())
} else {
let cluster_root = self
.block_commitment_cache
.read()
.unwrap()
.largest_confirmed_root();
debug!("RPC using block: {:?}", cluster_root);
r_bank_forks.get(cluster_root).cloned().ok_or_else(|| {
RpcCustomError::NonexistentClusterRoot {
cluster_root,
node_root: r_bank_forks.root(),
}
.into()
})
match commitment {
Some(commitment_config) if commitment_config.commitment == CommitmentLevel::Recent => {
let bank = r_bank_forks.working_bank();
debug!("RPC using working_bank: {:?}", bank.slot());
Ok(bank)
}
Some(commitment_config) if commitment_config.commitment == CommitmentLevel::Root => {
let slot = r_bank_forks.root();
debug!("RPC using node root: {:?}", slot);
Ok(r_bank_forks.get(slot).cloned().unwrap())
}
Some(commitment_config) if commitment_config.commitment == CommitmentLevel::Single => {
let slot = self
.block_commitment_cache
.read()
.unwrap()
.highest_confirmed_slot();
debug!("RPC using confirmed slot: {:?}", slot);
Ok(r_bank_forks.get(slot).cloned().unwrap())
}
_ => {
let cluster_root = self
.block_commitment_cache
.read()
.unwrap()
.largest_confirmed_root();
debug!("RPC using block: {:?}", cluster_root);
r_bank_forks.get(cluster_root).cloned().ok_or_else(|| {
RpcCustomError::NonexistentClusterRoot {
cluster_root,
node_root: r_bank_forks.root(),
}
.into()
})
}
}
}
@ -1577,6 +1591,7 @@ pub mod tests {
bank.clone(),
blockstore.clone(),
0,
0,
)));
// Add timestamp vote to blockstore
@ -2627,6 +2642,7 @@ pub mod tests {
bank_forks.read().unwrap().working_bank(),
blockstore.clone(),
0,
0,
)));
let mut config = JsonRpcConfig::default();
@ -2822,7 +2838,7 @@ pub mod tests {
block_commitment_cache
.write()
.unwrap()
.set_get_largest_confirmed_root(8);
.set_largest_confirmed_root(8);
let req = r#"{"jsonrpc":"2.0","id":1,"method":"getConfirmedBlocks","params":[0]}"#;
let res = io.handle_request_sync(&req, meta.clone());
@ -2878,7 +2894,7 @@ pub mod tests {
block_commitment_cache
.write()
.unwrap()
.set_get_largest_confirmed_root(7);
.set_largest_confirmed_root(7);
let slot_duration = slot_duration_from_slots_per_year(bank.slots_per_year());

View File

@ -355,7 +355,7 @@ mod tests {
use super::*;
use crate::{
cluster_info_vote_listener::{ClusterInfoVoteListener, VoteTracker},
commitment::{BlockCommitment, BlockCommitmentCache},
commitment::{BlockCommitmentCache, CacheSlotInfo},
rpc_subscriptions::tests::robust_poll_or_panic,
};
use crossbeam_channel::unbounded;
@ -381,7 +381,6 @@ mod tests {
};
use solana_vote_program::vote_transaction;
use std::{
collections::HashMap,
sync::{atomic::AtomicBool, RwLock},
thread::sleep,
time::Duration,
@ -391,15 +390,17 @@ mod tests {
bank_forks: &Arc<RwLock<BankForks>>,
tx: &Transaction,
subscriptions: &RpcSubscriptions,
slot: Slot,
current_slot: Slot,
) -> transaction::Result<()> {
bank_forks
.write()
.unwrap()
.get(slot)
.get(current_slot)
.unwrap()
.process_transaction(tx)?;
subscriptions.notify_subscribers(slot);
let mut cache_slot_info = CacheSlotInfo::default();
cache_slot_info.current_slot = current_slot;
subscriptions.notify_subscribers(cache_slot_info);
Ok(())
}
@ -714,7 +715,8 @@ mod tests {
.unwrap()
.process_transaction(&tx)
.unwrap();
rpc.subscriptions.notify_subscribers(0);
rpc.subscriptions
.notify_subscribers(CacheSlotInfo::default());
// allow 200ms for notification thread to wake
std::thread::sleep(Duration::from_millis(200));
let _panic = robust_poll_or_panic(receiver);
@ -766,23 +768,17 @@ mod tests {
.unwrap()
.process_transaction(&tx)
.unwrap();
rpc.subscriptions.notify_subscribers(1);
let mut cache_slot_info = CacheSlotInfo::default();
cache_slot_info.current_slot = 1;
rpc.subscriptions.notify_subscribers(cache_slot_info);
let bank1 = bank_forks.read().unwrap()[1].clone();
let bank2 = Bank::new_from_parent(&bank1, &Pubkey::default(), 2);
bank_forks.write().unwrap().insert(bank2);
bank_forks.write().unwrap().set_root(1, &None, None);
let bank2 = bank_forks.read().unwrap()[2].clone();
let mut block_commitment: HashMap<Slot, BlockCommitment> = HashMap::new();
block_commitment.insert(0, BlockCommitment::default());
let mut new_block_commitment =
BlockCommitmentCache::new(block_commitment, 1, 10, bank2, blockstore, 1);
let mut w_block_commitment_cache = block_commitment_cache.write().unwrap();
std::mem::swap(&mut *w_block_commitment_cache, &mut new_block_commitment);
drop(w_block_commitment_cache);
rpc.subscriptions.notify_subscribers(2);
let cache_slot_info = CacheSlotInfo {
current_slot: 2,
node_root: 1,
largest_confirmed_root: 1,
highest_confirmed_slot: 1,
};
rpc.subscriptions.notify_subscribers(cache_slot_info);
let expected = json!({
"jsonrpc": "2.0",
"method": "accountNotification",

View File

@ -1,6 +1,6 @@
//! The `pubsub` module implements a threaded subscription service on client RPC request
use crate::commitment::BlockCommitmentCache;
use crate::commitment::{BlockCommitmentCache, CacheSlotInfo};
use core::hash::Hash;
use jsonrpc_core::futures::Future;
use jsonrpc_pubsub::{
@ -56,7 +56,7 @@ enum NotificationEntry {
Slot(SlotInfo),
Vote(Vote),
Root(Slot),
Bank(Slot),
Bank(CacheSlotInfo),
}
impl std::fmt::Debug for NotificationEntry {
@ -65,9 +65,11 @@ impl std::fmt::Debug for NotificationEntry {
NotificationEntry::Root(root) => write!(f, "Root({})", root),
NotificationEntry::Vote(vote) => write!(f, "Vote({:?})", vote),
NotificationEntry::Slot(slot_info) => write!(f, "Slot({:?})", slot_info),
NotificationEntry::Bank(current_slot) => {
write!(f, "Bank({{current_slot: {:?}}})", current_slot)
}
NotificationEntry::Bank(cache_slot_info) => write!(
f,
"Bank({{current_slot: {:?}}})",
cache_slot_info.current_slot
),
}
}
}
@ -142,7 +144,7 @@ fn check_commitment_and_notify<K, S, B, F, X>(
subscriptions: &HashMap<K, HashMap<SubscriptionId, SubscriptionData<Response<S>>>>,
hashmap_key: &K,
bank_forks: &Arc<RwLock<BankForks>>,
block_commitment_cache: &Arc<RwLock<BlockCommitmentCache>>,
cache_slot_info: &CacheSlotInfo,
bank_method: B,
filter_results: F,
notifier: &RpcNotifier,
@ -154,12 +156,6 @@ where
F: Fn(X, Slot) -> (Box<dyn Iterator<Item = S>>, Slot),
X: Clone + Serialize + Default,
{
let r_block_commitment_cache = block_commitment_cache.read().unwrap();
let current_slot = r_block_commitment_cache.slot();
let node_root = r_block_commitment_cache.root();
let largest_confirmed_root = r_block_commitment_cache.largest_confirmed_root();
drop(r_block_commitment_cache);
let mut notified_set: HashSet<SubscriptionId> = HashSet::new();
if let Some(hashmap) = subscriptions.get(hashmap_key) {
for (
@ -172,9 +168,10 @@ where
) in hashmap.iter()
{
let slot = match commitment.commitment {
CommitmentLevel::Max => largest_confirmed_root,
CommitmentLevel::Recent => current_slot,
CommitmentLevel::Root => node_root,
CommitmentLevel::Max => cache_slot_info.largest_confirmed_root,
CommitmentLevel::Recent => cache_slot_info.current_slot,
CommitmentLevel::Root => cache_slot_info.node_root,
CommitmentLevel::Single => cache_slot_info.highest_confirmed_slot,
};
let results = {
let bank_forks = bank_forks.read().unwrap();
@ -332,7 +329,6 @@ impl RpcSubscriptions {
notification_receiver,
_subscriptions,
_bank_forks,
_block_commitment_cache,
);
})
.unwrap();
@ -364,16 +360,16 @@ impl RpcSubscriptions {
fn check_account(
pubkey: &Pubkey,
bank_forks: &Arc<RwLock<BankForks>>,
block_commitment_cache: &Arc<RwLock<BlockCommitmentCache>>,
account_subscriptions: Arc<RpcAccountSubscriptions>,
notifier: &RpcNotifier,
cache_slot_info: &CacheSlotInfo,
) {
let subscriptions = account_subscriptions.read().unwrap();
check_commitment_and_notify(
&subscriptions,
pubkey,
bank_forks,
block_commitment_cache,
cache_slot_info,
Bank::get_account_modified_slot,
filter_account_result,
notifier,
@ -383,16 +379,16 @@ impl RpcSubscriptions {
fn check_program(
program_id: &Pubkey,
bank_forks: &Arc<RwLock<BankForks>>,
block_commitment_cache: &Arc<RwLock<BlockCommitmentCache>>,
program_subscriptions: Arc<RpcProgramSubscriptions>,
notifier: &RpcNotifier,
cache_slot_info: &CacheSlotInfo,
) {
let subscriptions = program_subscriptions.read().unwrap();
check_commitment_and_notify(
&subscriptions,
program_id,
bank_forks,
block_commitment_cache,
cache_slot_info,
Bank::get_program_accounts_modified_since_parent,
filter_program_results,
notifier,
@ -402,16 +398,16 @@ impl RpcSubscriptions {
fn check_signature(
signature: &Signature,
bank_forks: &Arc<RwLock<BankForks>>,
block_commitment_cache: &Arc<RwLock<BlockCommitmentCache>>,
signature_subscriptions: Arc<RpcSignatureSubscriptions>,
notifier: &RpcNotifier,
cache_slot_info: &CacheSlotInfo,
) {
let mut subscriptions = signature_subscriptions.write().unwrap();
let notified_ids = check_commitment_and_notify(
&subscriptions,
signature,
bank_forks,
block_commitment_cache,
cache_slot_info,
Bank::get_signature_status_processed_since_parent,
filter_signature_result,
notifier,
@ -443,6 +439,11 @@ impl RpcSubscriptions {
.largest_confirmed_root(),
CommitmentLevel::Recent => self.block_commitment_cache.read().unwrap().slot(),
CommitmentLevel::Root => self.block_commitment_cache.read().unwrap().root(),
CommitmentLevel::Single => self
.block_commitment_cache
.read()
.unwrap()
.highest_confirmed_slot(),
};
let last_notified_slot = if let Some((_account, slot)) = self
.bank_forks
@ -518,8 +519,8 @@ impl RpcSubscriptions {
/// Notify subscribers of changes to any accounts or new signatures since
/// the bank's last checkpoint.
pub fn notify_subscribers(&self, current_slot: Slot) {
self.enqueue_notification(NotificationEntry::Bank(current_slot));
pub fn notify_subscribers(&self, cache_slot_info: CacheSlotInfo) {
self.enqueue_notification(NotificationEntry::Bank(cache_slot_info));
}
pub fn add_slot_subscription(&self, sub_id: SubscriptionId, subscriber: Subscriber<SlotInfo>) {
@ -593,7 +594,6 @@ impl RpcSubscriptions {
notification_receiver: Receiver<NotificationEntry>,
subscriptions: Subscriptions,
bank_forks: Arc<RwLock<BankForks>>,
block_commitment_cache: Arc<RwLock<BlockCommitmentCache>>,
) {
loop {
if exit.load(Ordering::Relaxed) {
@ -626,7 +626,7 @@ impl RpcSubscriptions {
notifier.notify(root, sink);
}
}
NotificationEntry::Bank(_current_slot) => {
NotificationEntry::Bank(cache_slot_info) => {
let pubkeys: Vec<_> = {
let subs = subscriptions.account_subscriptions.read().unwrap();
subs.keys().cloned().collect()
@ -635,9 +635,9 @@ impl RpcSubscriptions {
Self::check_account(
pubkey,
&bank_forks,
&block_commitment_cache,
subscriptions.account_subscriptions.clone(),
&notifier,
&cache_slot_info,
);
}
@ -649,9 +649,9 @@ impl RpcSubscriptions {
Self::check_program(
program_id,
&bank_forks,
&block_commitment_cache,
subscriptions.program_subscriptions.clone(),
&notifier,
&cache_slot_info,
);
}
@ -663,9 +663,9 @@ impl RpcSubscriptions {
Self::check_signature(
signature,
&bank_forks,
&block_commitment_cache,
subscriptions.signature_subscriptions.clone(),
&notifier,
&cache_slot_info,
);
}
}
@ -802,7 +802,9 @@ pub(crate) mod tests {
.unwrap()
.process_transaction(&tx)
.unwrap();
subscriptions.notify_subscribers(1);
let mut cache_slot_info = CacheSlotInfo::default();
cache_slot_info.current_slot = 1;
subscriptions.notify_subscribers(cache_slot_info);
let (response, _) = robust_poll_or_panic(transport_receiver);
let expected = json!({
"jsonrpc": "2.0",
@ -887,7 +889,7 @@ pub(crate) mod tests {
.unwrap()
.contains_key(&solana_budget_program::id()));
subscriptions.notify_subscribers(0);
subscriptions.notify_subscribers(CacheSlotInfo::default());
let (response, _) = robust_poll_or_panic(transport_receiver);
let expected = json!({
"jsonrpc": "2.0",
@ -969,7 +971,7 @@ pub(crate) mod tests {
block_commitment.entry(0).or_insert(cache0);
block_commitment.entry(1).or_insert(cache1);
let block_commitment_cache =
BlockCommitmentCache::new(block_commitment, 0, 10, bank1, blockstore, 0);
BlockCommitmentCache::new(block_commitment, 0, 10, bank1, blockstore, 0, 0);
let exit = Arc::new(AtomicBool::new(false));
let subscriptions = RpcSubscriptions::new(
@ -1020,8 +1022,9 @@ pub(crate) mod tests {
assert!(sig_subs.contains_key(&unprocessed_tx.signatures[0]));
assert!(sig_subs.contains_key(&processed_tx.signatures[0]));
}
subscriptions.notify_subscribers(1);
let mut cache_slot_info = CacheSlotInfo::default();
cache_slot_info.current_slot = 1;
subscriptions.notify_subscribers(cache_slot_info);
let expected_res = RpcSignatureResult { err: None };
struct Notification {

View File

@ -309,7 +309,7 @@ impl Validator {
block_commitment_cache
.write()
.unwrap()
.set_get_largest_confirmed_root(bank_forks.read().unwrap().root());
.set_largest_confirmed_root(bank_forks.read().unwrap().root());
// Park with the RPC service running, ready for inspection!
warn!("Validator halted");

View File

@ -98,7 +98,8 @@ Solana nodes choose which bank state to query based on a commitment requirement
set by the client. Clients may specify either:
* `{"commitment":"max"}` - the node will query the most recent bank confirmed by the cluster as having reached `MAX_LOCKOUT_HISTORY` confirmations
* `{"commitment":"root"}` - the node will query the most recent bank having reached `MAX_LOCKOUT_HISTORY` confirmations on this node
* `{"commitment":"recent"}` - the node will query its most recent bank state
* `{"commitment":"single"}` - the node will query the most recent bank having reached 1 confirmation
* `{"commitment":"recent"}` - the node will query its most recent bank
The commitment parameter should be included as the last element in the `params` array:
@ -1163,25 +1164,11 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "m
### Subscription Websocket
After connect to the RPC PubSub websocket at `ws://<ADDRESS>/`:
After connecting to the RPC PubSub websocket at `ws://<ADDRESS>/`:
* Submit subscription requests to the websocket using the methods below
* Multiple subscriptions may be active at once
* All subscriptions take an optional `confirmations` parameter, which defines
how many confirmed blocks the node should wait before sending a notification.
The greater the number, the more likely the notification is to represent
consensus across the cluster, and the less likely it is to be affected by
forking or rollbacks. If unspecified, the default value is 0; the node will
send a notification as soon as it witnesses the event. The maximum
`confirmations` wait length is the cluster's `MAX_LOCKOUT_HISTORY`, which
represents the economic finality of the chain.
* Many subscriptions take the optional [`commitment` parameter](jsonrpc-api.md#configuring-state-commitment), defining how . For subscriptions, if commitment is unspecified, the default value is `recent`.
### accountSubscribe
@ -1190,7 +1177,7 @@ Subscribe to an account to receive notifications when the lamports or data for a
#### Parameters:
* `<string>` - account Pubkey, as base-58 encoded string
* `<u64>` - optional, number of confirmed blocks to wait before notification.
* `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
Default: 0, Max: `MAX_LOCKOUT_HISTORY` \(greater integers rounded down\)
@ -1245,7 +1232,7 @@ Subscribe to a program to receive notifications when the lamports or data for a
#### Parameters:
* `<string>` - program\_id Pubkey, as base-58 encoded string
* `<u64>` - optional, number of confirmed blocks to wait before notification.
* `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
Default: 0, Max: `MAX_LOCKOUT_HISTORY` \(greater integers rounded down\)
@ -1303,7 +1290,7 @@ Subscribe to a transaction signature to receive notification when the transactio
#### Parameters:
* `<string>` - Transaction Signature, as base-58 encoded string
* `<integer>` - optional, number of confirmed blocks to wait before notification.
* `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
Default: 0, Max: `MAX_LOCKOUT_HISTORY` \(greater integers rounded down\)

View File

@ -31,6 +31,12 @@ impl CommitmentConfig {
}
}
pub fn single() -> Self {
Self {
commitment: CommitmentLevel::Single,
}
}
pub fn ok(self) -> Option<Self> {
if self == Self::default() {
None
@ -46,4 +52,5 @@ pub enum CommitmentLevel {
Max,
Recent,
Root,
Single,
}