voteSubscribe pubsub notification now includes the vote transaction signature (#25291)
This commit is contained in:
parent
97efbdc303
commit
c54e06355f
|
@ -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)]
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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}}"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in New Issue