voteSubscribe pubsub notification now includes the vote transaction signature (#25291)

This commit is contained in:
Michael Vines 2022-05-19 18:28:46 -07:00 committed by GitHub
parent 97efbdc303
commit c54e06355f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 34 additions and 17 deletions

View File

@ -346,6 +346,7 @@ pub struct RpcVote {
pub slots: Vec<Slot>, pub slots: Vec<Slot>,
pub hash: String, pub hash: String,
pub timestamp: Option<UnixTimestamp>, pub timestamp: Option<UnixTimestamp>,
pub signature: String,
} }
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]

View File

@ -307,7 +307,7 @@ impl ClusterInfoVoteListener {
!packet_batch.packets[0].meta.discard() !packet_batch.packets[0].meta.discard()
}) })
.filter_map(|(tx, packet_batch)| { .filter_map(|(tx, packet_batch)| {
let (vote_account_key, vote, _) = vote_parser::parse_vote_transaction(&tx)?; let (vote_account_key, vote, ..) = vote_parser::parse_vote_transaction(&tx)?;
let slot = vote.last_voted_slot()?; let slot = vote.last_voted_slot()?;
let epoch = epoch_schedule.get_epoch(slot); let epoch = epoch_schedule.get_epoch(slot);
let authorized_voter = root_bank let authorized_voter = root_bank
@ -570,6 +570,7 @@ impl ClusterInfoVoteListener {
fn track_new_votes_and_notify_confirmations( fn track_new_votes_and_notify_confirmations(
vote: VoteTransaction, vote: VoteTransaction,
vote_pubkey: &Pubkey, vote_pubkey: &Pubkey,
vote_transaction_signature: Signature,
vote_tracker: &VoteTracker, vote_tracker: &VoteTracker,
root_bank: &Bank, root_bank: &Bank,
subscriptions: &RpcSubscriptions, subscriptions: &RpcSubscriptions,
@ -678,7 +679,7 @@ impl ClusterInfoVoteListener {
} }
if is_new_vote { if is_new_vote {
subscriptions.notify_vote(*vote_pubkey, vote); subscriptions.notify_vote(*vote_pubkey, vote, vote_transaction_signature);
let _ = verified_vote_sender.send((*vote_pubkey, vote_slots)); let _ = verified_vote_sender.send((*vote_pubkey, vote_slots));
} }
} }
@ -703,10 +704,11 @@ impl ClusterInfoVoteListener {
.filter_map(vote_parser::parse_vote_transaction) .filter_map(vote_parser::parse_vote_transaction)
.zip(repeat(/*is_gossip:*/ true)) .zip(repeat(/*is_gossip:*/ true))
.chain(replayed_votes.into_iter().zip(repeat(/*is_gossip:*/ false))); .chain(replayed_votes.into_iter().zip(repeat(/*is_gossip:*/ false)));
for ((vote_pubkey, vote, _), is_gossip) in votes { for ((vote_pubkey, vote, _switch_proof, signature), is_gossip) in votes {
Self::track_new_votes_and_notify_confirmations( Self::track_new_votes_and_notify_confirmations(
vote, vote,
&vote_pubkey, &vote_pubkey,
signature,
vote_tracker, vote_tracker,
root_bank, root_bank,
subscriptions, subscriptions,
@ -1018,6 +1020,7 @@ mod tests {
vote_keypair.pubkey(), vote_keypair.pubkey(),
VoteTransaction::from(replay_vote.clone()), VoteTransaction::from(replay_vote.clone()),
switch_proof_hash, switch_proof_hash,
Signature::default(),
)) ))
.unwrap(); .unwrap();
} }
@ -1306,6 +1309,7 @@ mod tests {
vote_keypair.pubkey(), vote_keypair.pubkey(),
VoteTransaction::from(Vote::new(vec![vote_slot], Hash::default())), VoteTransaction::from(Vote::new(vec![vote_slot], Hash::default())),
switch_proof_hash, switch_proof_hash,
Signature::default(),
)) ))
.unwrap(); .unwrap();
} }
@ -1401,6 +1405,7 @@ mod tests {
validator0_keypairs.vote_keypair.pubkey(), validator0_keypairs.vote_keypair.pubkey(),
VoteTransaction::from(Vote::new(vec![voted_slot], Hash::default())), VoteTransaction::from(Vote::new(vec![voted_slot], Hash::default())),
None, None,
Signature::default(),
)], )],
&bank, &bank,
&subscriptions, &subscriptions,
@ -1446,6 +1451,7 @@ mod tests {
validator_keypairs[1].vote_keypair.pubkey(), validator_keypairs[1].vote_keypair.pubkey(),
VoteTransaction::from(Vote::new(vec![first_slot_in_new_epoch], Hash::default())), VoteTransaction::from(Vote::new(vec![first_slot_in_new_epoch], Hash::default())),
None, None,
Signature::default(),
)], )],
&new_root_bank, &new_root_bank,
&subscriptions, &subscriptions,

View File

@ -4573,6 +4573,7 @@ The notification will be an object with the following fields:
- `hash: <string>` - The vote hash - `hash: <string>` - The vote hash
- `slots: <array>` - The slots covered by the vote, as an array of u64 integers - `slots: <array>` - The slots covered by the vote, as an array of u64 integers
- `timestamp: <i64 | null>` - The timestamp of the vote - `timestamp: <i64 | null>` - The timestamp of the vote
- `signature: <string>` - The signature of the transaction that contained this vote
```json ```json
{ {

View File

@ -1037,7 +1037,7 @@ impl ClusterInfo {
}; };
let vote_index = vote_index.unwrap_or(num_crds_votes); let vote_index = vote_index.unwrap_or(num_crds_votes);
if (vote_index as usize) >= MAX_LOCKOUT_HISTORY { if (vote_index as usize) >= MAX_LOCKOUT_HISTORY {
let (_, vote, hash) = vote_parser::parse_vote_transaction(&vote).unwrap(); let (_, vote, hash, _) = vote_parser::parse_vote_transaction(&vote).unwrap();
panic!( panic!(
"invalid vote index: {}, switch: {}, vote slots: {:?}, tower: {:?}", "invalid vote index: {}, switch: {}, vote slots: {:?}, tower: {:?}",
vote_index, vote_index,

View File

@ -307,7 +307,7 @@ impl Sanitize for Vote {
impl Vote { impl Vote {
// Returns None if cannot parse transaction into a vote. // Returns None if cannot parse transaction into a vote.
pub fn new(from: Pubkey, transaction: Transaction, wallclock: u64) -> Option<Self> { pub fn new(from: Pubkey, transaction: Transaction, wallclock: u64) -> Option<Self> {
vote_parser::parse_vote_transaction(&transaction).map(|(_, vote, _)| Self { vote_parser::parse_vote_transaction(&transaction).map(|(_, vote, ..)| Self {
from, from,
transaction, transaction,
wallclock, wallclock,

View File

@ -3585,7 +3585,7 @@ pub mod tests {
process_entries_for_tests(&bank1, vec![entry], true, None, Some(&replay_vote_sender)); process_entries_for_tests(&bank1, vec![entry], true, None, Some(&replay_vote_sender));
let successes: BTreeSet<Pubkey> = replay_vote_receiver let successes: BTreeSet<Pubkey> = replay_vote_receiver
.try_iter() .try_iter()
.map(|(vote_pubkey, _, _)| vote_pubkey) .map(|(vote_pubkey, ..)| vote_pubkey)
.collect(); .collect();
assert_eq!(successes, expected_successful_voter_pubkeys); assert_eq!(successes, expected_successful_voter_pubkeys);
} }

View File

@ -490,7 +490,7 @@ fn test_duplicate_shreds_broadcast_leader() {
// Filter out votes not from the bad leader // Filter out votes not from the bad leader
if label.pubkey() == bad_leader_id { if label.pubkey() == bad_leader_id {
let vote = vote_parser::parse_vote_transaction(&leader_vote_tx) let vote = vote_parser::parse_vote_transaction(&leader_vote_tx)
.map(|(_, vote, _)| vote) .map(|(_, vote, ..)| vote)
.unwrap(); .unwrap();
// Filter out empty votes // Filter out empty votes
if !vote.is_empty() { if !vote.is_empty() {

View File

@ -1340,12 +1340,16 @@ mod tests {
hash: Hash::default(), hash: Hash::default(),
timestamp: None, timestamp: None,
}; };
subscriptions.notify_vote(Pubkey::default(), VoteTransaction::from(vote)); subscriptions.notify_vote(
Pubkey::default(),
VoteTransaction::from(vote),
Signature::default(),
);
let response = receiver.recv(); let response = receiver.recv();
assert_eq!( assert_eq!(
response, response,
r#"{"jsonrpc":"2.0","method":"voteNotification","params":{"result":{"votePubkey":"11111111111111111111111111111111","slots":[1,2],"hash":"11111111111111111111111111111111","timestamp":null},"subscription":0}}"# r#"{"jsonrpc":"2.0","method":"voteNotification","params":{"result":{"votePubkey":"11111111111111111111111111111111","slots":[1,2],"hash":"11111111111111111111111111111111","timestamp":null,"signature":"1111111111111111111111111111111111111111111111111111111111111111"},"subscription":0}}"#
); );
} }

View File

@ -93,7 +93,7 @@ impl From<NotificationEntry> for TimestampedNotificationEntry {
pub enum NotificationEntry { pub enum NotificationEntry {
Slot(SlotInfo), Slot(SlotInfo),
SlotUpdate(SlotUpdate), SlotUpdate(SlotUpdate),
Vote((Pubkey, VoteTransaction)), Vote((Pubkey, VoteTransaction, Signature)),
Root(Slot), Root(Slot),
Bank(CommitmentSlots), Bank(CommitmentSlots),
Gossip(Slot), Gossip(Slot),
@ -728,8 +728,8 @@ impl RpcSubscriptions {
self.enqueue_notification(NotificationEntry::SignaturesReceived(slot_signatures)); self.enqueue_notification(NotificationEntry::SignaturesReceived(slot_signatures));
} }
pub fn notify_vote(&self, vote_pubkey: Pubkey, vote: VoteTransaction) { pub fn notify_vote(&self, vote_pubkey: Pubkey, vote: VoteTransaction, signature: Signature) {
self.enqueue_notification(NotificationEntry::Vote((vote_pubkey, vote))); self.enqueue_notification(NotificationEntry::Vote((vote_pubkey, vote, signature)));
} }
pub fn notify_roots(&self, mut rooted_slots: Vec<Slot>) { pub fn notify_roots(&self, mut rooted_slots: Vec<Slot>) {
@ -814,7 +814,7 @@ impl RpcSubscriptions {
// These notifications are only triggered by votes observed on gossip, // These notifications are only triggered by votes observed on gossip,
// unlike `NotificationEntry::Gossip`, which also accounts for slots seen // unlike `NotificationEntry::Gossip`, which also accounts for slots seen
// in VoteState's from bank states built in ReplayStage. // in VoteState's from bank states built in ReplayStage.
NotificationEntry::Vote((vote_pubkey, ref vote_info)) => { NotificationEntry::Vote((vote_pubkey, ref vote_info, signature)) => {
if let Some(sub) = subscriptions if let Some(sub) = subscriptions
.node_progress_watchers() .node_progress_watchers()
.get(&SubscriptionParams::Vote) .get(&SubscriptionParams::Vote)
@ -824,6 +824,7 @@ impl RpcSubscriptions {
slots: vote_info.slots(), slots: vote_info.slots(),
hash: bs58::encode(vote_info.hash()).into_string(), hash: bs58::encode(vote_info.hash()).into_string(),
timestamp: vote_info.timestamp(), timestamp: vote_info.timestamp(),
signature: signature.to_string(),
}; };
debug!("vote notify: {:?}", vote_info); debug!("vote notify: {:?}", vote_info);
inc_new_counter_info!("rpc-subscription-notify-vote", 1); inc_new_counter_info!("rpc-subscription-notify-vote", 1);

View File

@ -4,12 +4,13 @@ use {
hash::Hash, hash::Hash,
program_utils::limited_deserialize, program_utils::limited_deserialize,
pubkey::Pubkey, pubkey::Pubkey,
signature::Signature,
transaction::{SanitizedTransaction, Transaction}, transaction::{SanitizedTransaction, Transaction},
}, },
solana_vote_program::vote_instruction::VoteInstruction, solana_vote_program::vote_instruction::VoteInstruction,
}; };
pub type ParsedVote = (Pubkey, VoteTransaction, Option<Hash>); pub type ParsedVote = (Pubkey, VoteTransaction, Option<Hash>, Signature);
// Used for filtering out votes from the transaction log collector // Used for filtering out votes from the transaction log collector
pub(crate) fn is_simple_vote_transaction(transaction: &SanitizedTransaction) -> bool { pub(crate) fn is_simple_vote_transaction(transaction: &SanitizedTransaction) -> bool {
@ -46,7 +47,8 @@ pub fn parse_sanitized_vote_transaction(tx: &SanitizedTransaction) -> Option<Par
let first_account = usize::from(*first_instruction.accounts.first()?); let first_account = usize::from(*first_instruction.accounts.first()?);
let key = message.account_keys().get(first_account)?; let key = message.account_keys().get(first_account)?;
let (vote, switch_proof_hash) = parse_vote_instruction_data(&first_instruction.data)?; let (vote, switch_proof_hash) = parse_vote_instruction_data(&first_instruction.data)?;
Some((*key, vote, switch_proof_hash)) let signature = tx.signatures().get(0).cloned().unwrap_or_default();
Some((*key, vote, switch_proof_hash, signature))
} }
// Used for parsing gossip vote transactions // Used for parsing gossip vote transactions
@ -62,7 +64,8 @@ pub fn parse_vote_transaction(tx: &Transaction) -> Option<ParsedVote> {
let first_account = usize::from(*first_instruction.accounts.first()?); let first_account = usize::from(*first_instruction.accounts.first()?);
let key = message.account_keys.get(first_account)?; let key = message.account_keys.get(first_account)?;
let (vote, switch_proof_hash) = parse_vote_instruction_data(&first_instruction.data)?; let (vote, switch_proof_hash) = parse_vote_instruction_data(&first_instruction.data)?;
Some((*key, vote, switch_proof_hash)) let signature = tx.signatures.get(0).cloned().unwrap_or_default();
Some((*key, vote, switch_proof_hash, signature))
} }
fn parse_vote_instruction_data( fn parse_vote_instruction_data(
@ -113,10 +116,11 @@ mod test {
&auth_voter_keypair, &auth_voter_keypair,
input_hash, input_hash,
); );
let (key, vote, hash) = parse_vote_transaction(&vote_tx).unwrap(); let (key, vote, hash, signature) = parse_vote_transaction(&vote_tx).unwrap();
assert_eq!(hash, input_hash); assert_eq!(hash, input_hash);
assert_eq!(vote, VoteTransaction::from(Vote::new(vec![42], bank_hash))); assert_eq!(vote, VoteTransaction::from(Vote::new(vec![42], bank_hash)));
assert_eq!(key, vote_keypair.pubkey()); assert_eq!(key, vote_keypair.pubkey());
assert_eq!(signature, vote_tx.signatures[0]);
// Test bad program id fails // Test bad program id fails
let mut vote_ix = vote_instruction::vote( let mut vote_ix = vote_instruction::vote(