vote: plumb TowerSync ix (#584)
This commit is contained in:
parent
16efe510cb
commit
c4734ad127
|
@ -1,7 +1,10 @@
|
|||
#![allow(clippy::arithmetic_side_effects)]
|
||||
#![feature(test)]
|
||||
|
||||
use solana_core::validator::BlockProductionMethod;
|
||||
use {
|
||||
solana_core::validator::BlockProductionMethod,
|
||||
solana_vote_program::{vote_state::TowerSync, vote_transaction::new_tower_sync_transaction},
|
||||
};
|
||||
|
||||
extern crate test;
|
||||
|
||||
|
@ -50,9 +53,6 @@ use {
|
|||
transaction::{Transaction, VersionedTransaction},
|
||||
},
|
||||
solana_streamer::socket::SocketAddrSpace,
|
||||
solana_vote_program::{
|
||||
vote_state::VoteStateUpdate, vote_transaction::new_vote_state_update_transaction,
|
||||
},
|
||||
std::{
|
||||
iter::repeat_with,
|
||||
sync::{atomic::Ordering, Arc},
|
||||
|
@ -169,11 +169,11 @@ fn make_vote_txs(txes: usize) -> Vec<Transaction> {
|
|||
.map(|i| {
|
||||
// Quarter of the votes should be filtered out
|
||||
let vote = if i % 4 == 0 {
|
||||
VoteStateUpdate::from(vec![(2, 1)])
|
||||
TowerSync::from(vec![(2, 1)])
|
||||
} else {
|
||||
VoteStateUpdate::from(vec![(i as u64, 1)])
|
||||
TowerSync::from(vec![(i as u64, 1)])
|
||||
};
|
||||
new_vote_state_update_transaction(
|
||||
new_tower_sync_transaction(
|
||||
vote,
|
||||
Hash::new_unique(),
|
||||
&keypairs[i % num_voters],
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::replay_stage::DUPLICATE_THRESHOLD;
|
||||
use {crate::replay_stage::DUPLICATE_THRESHOLD, solana_sdk::feature_set};
|
||||
|
||||
pub mod fork_choice;
|
||||
pub mod heaviest_subtree_fork_choice;
|
||||
|
@ -35,8 +35,8 @@ use {
|
|||
vote_instruction,
|
||||
vote_state::{
|
||||
process_slot_vote_unchecked, process_vote_unchecked, BlockTimestamp, LandedVote,
|
||||
Lockout, Vote, VoteState, VoteState1_14_11, VoteStateUpdate, VoteStateVersions,
|
||||
VoteTransaction, MAX_LOCKOUT_HISTORY,
|
||||
Lockout, TowerSync, Vote, VoteState, VoteState1_14_11, VoteStateUpdate,
|
||||
VoteStateVersions, VoteTransaction, MAX_LOCKOUT_HISTORY,
|
||||
},
|
||||
},
|
||||
std::{
|
||||
|
@ -267,7 +267,7 @@ impl Default for Tower {
|
|||
threshold_depth: VOTE_THRESHOLD_DEPTH,
|
||||
threshold_size: VOTE_THRESHOLD_SIZE,
|
||||
vote_state: VoteState::default(),
|
||||
last_vote: VoteTransaction::from(VoteStateUpdate::default()),
|
||||
last_vote: VoteTransaction::from(TowerSync::default()),
|
||||
last_timestamp: BlockTimestamp::default(),
|
||||
last_vote_tx_blockhash: BlockhashStatus::default(),
|
||||
stray_restored_slot: Option::default(),
|
||||
|
@ -310,7 +310,7 @@ impl Tower {
|
|||
let mut rng = rand::thread_rng();
|
||||
let root_slot = rng.gen();
|
||||
let vote_state = VoteState::new_rand_for_tests(node_pubkey, root_slot);
|
||||
let last_vote = VoteStateUpdate::from(
|
||||
let last_vote = TowerSync::from(
|
||||
vote_state
|
||||
.votes
|
||||
.iter()
|
||||
|
@ -320,7 +320,7 @@ impl Tower {
|
|||
Self {
|
||||
node_pubkey,
|
||||
vote_state,
|
||||
last_vote: VoteTransaction::CompactVoteStateUpdate(last_vote),
|
||||
last_vote: VoteTransaction::from(last_vote),
|
||||
..Tower::default()
|
||||
}
|
||||
}
|
||||
|
@ -590,21 +590,43 @@ impl Tower {
|
|||
pub fn record_bank_vote(&mut self, bank: &Bank) -> Option<Slot> {
|
||||
// Returns the new root if one is made after applying a vote for the given bank to
|
||||
// `self.vote_state`
|
||||
self.record_bank_vote_and_update_lockouts(bank.slot(), bank.hash())
|
||||
self.record_bank_vote_and_update_lockouts(
|
||||
bank.slot(),
|
||||
bank.hash(),
|
||||
bank.feature_set
|
||||
.is_active(&feature_set::enable_tower_sync_ix::id()),
|
||||
)
|
||||
}
|
||||
|
||||
/// If we've recently updated the vote state by applying a new vote
|
||||
/// or syncing from a bank, generate the proper last_vote.
|
||||
pub(crate) fn update_last_vote_from_vote_state(&mut self, vote_hash: Hash) {
|
||||
let mut new_vote = VoteTransaction::from(VoteStateUpdate::new(
|
||||
self.vote_state
|
||||
.votes
|
||||
.iter()
|
||||
.map(|vote| vote.lockout)
|
||||
.collect(),
|
||||
self.vote_state.root_slot,
|
||||
vote_hash,
|
||||
));
|
||||
pub(crate) fn update_last_vote_from_vote_state(
|
||||
&mut self,
|
||||
vote_hash: Hash,
|
||||
enable_tower_sync_ix: bool,
|
||||
) {
|
||||
let mut new_vote = if enable_tower_sync_ix {
|
||||
VoteTransaction::from(TowerSync::new(
|
||||
self.vote_state
|
||||
.votes
|
||||
.iter()
|
||||
.map(|vote| vote.lockout)
|
||||
.collect(),
|
||||
self.vote_state.root_slot,
|
||||
vote_hash,
|
||||
Hash::default(), // TODO: block_id will fill in upcoming pr
|
||||
))
|
||||
} else {
|
||||
VoteTransaction::from(VoteStateUpdate::new(
|
||||
self.vote_state
|
||||
.votes
|
||||
.iter()
|
||||
.map(|vote| vote.lockout)
|
||||
.collect(),
|
||||
self.vote_state.root_slot,
|
||||
vote_hash,
|
||||
))
|
||||
};
|
||||
|
||||
new_vote.set_timestamp(self.maybe_timestamp(self.last_voted_slot().unwrap_or_default()));
|
||||
self.last_vote = new_vote;
|
||||
|
@ -614,6 +636,7 @@ impl Tower {
|
|||
&mut self,
|
||||
vote_slot: Slot,
|
||||
vote_hash: Hash,
|
||||
enable_tower_sync_ix: bool,
|
||||
) -> Option<Slot> {
|
||||
trace!("{} record_vote for {}", self.node_pubkey, vote_slot);
|
||||
let old_root = self.root();
|
||||
|
@ -626,7 +649,7 @@ impl Tower {
|
|||
vote_slot, vote_hash, result
|
||||
);
|
||||
}
|
||||
self.update_last_vote_from_vote_state(vote_hash);
|
||||
self.update_last_vote_from_vote_state(vote_hash, enable_tower_sync_ix);
|
||||
|
||||
let new_root = self.root();
|
||||
|
||||
|
@ -644,7 +667,7 @@ impl Tower {
|
|||
|
||||
#[cfg(feature = "dev-context-only-utils")]
|
||||
pub fn record_vote(&mut self, slot: Slot, hash: Hash) -> Option<Slot> {
|
||||
self.record_bank_vote_and_update_lockouts(slot, hash)
|
||||
self.record_bank_vote_and_update_lockouts(slot, hash, true)
|
||||
}
|
||||
|
||||
/// Used for tests
|
||||
|
@ -1271,8 +1294,9 @@ impl Tower {
|
|||
assert!(
|
||||
self.last_vote == VoteTransaction::from(VoteStateUpdate::default())
|
||||
&& self.vote_state.votes.is_empty()
|
||||
|| self.last_vote != VoteTransaction::from(VoteStateUpdate::default())
|
||||
&& !self.vote_state.votes.is_empty(),
|
||||
|| self.last_vote == VoteTransaction::from(TowerSync::default())
|
||||
&& self.vote_state.votes.is_empty()
|
||||
|| !self.vote_state.votes.is_empty(),
|
||||
"last vote: {:?} vote_state.votes: {:?}",
|
||||
self.last_vote,
|
||||
self.vote_state.votes
|
||||
|
@ -2734,10 +2758,11 @@ pub mod test {
|
|||
} else {
|
||||
vec![]
|
||||
};
|
||||
let mut expected = VoteStateUpdate::new(
|
||||
let mut expected = TowerSync::new(
|
||||
VecDeque::from(slots),
|
||||
if num_votes > 0 { Some(0) } else { None },
|
||||
Hash::default(),
|
||||
Hash::default(),
|
||||
);
|
||||
for i in 0..num_votes {
|
||||
tower.record_vote(i as u64, Hash::default());
|
||||
|
@ -2789,8 +2814,7 @@ pub mod test {
|
|||
assert_eq!(tower.last_timestamp.timestamp, 0);
|
||||
|
||||
// Tower has vote no timestamp, but is greater than heaviest_bank
|
||||
tower.last_vote =
|
||||
VoteTransaction::from(VoteStateUpdate::from(vec![(0, 3), (1, 2), (6, 1)]));
|
||||
tower.last_vote = VoteTransaction::from(TowerSync::from(vec![(0, 3), (1, 2), (6, 1)]));
|
||||
assert_eq!(tower.last_vote.timestamp(), None);
|
||||
tower.refresh_last_vote_timestamp(5);
|
||||
assert_eq!(tower.last_vote.timestamp(), None);
|
||||
|
@ -2798,8 +2822,7 @@ pub mod test {
|
|||
assert_eq!(tower.last_timestamp.timestamp, 0);
|
||||
|
||||
// Tower has vote with no timestamp
|
||||
tower.last_vote =
|
||||
VoteTransaction::from(VoteStateUpdate::from(vec![(0, 3), (1, 2), (2, 1)]));
|
||||
tower.last_vote = VoteTransaction::from(TowerSync::from(vec![(0, 3), (1, 2), (2, 1)]));
|
||||
assert_eq!(tower.last_vote.timestamp(), None);
|
||||
tower.refresh_last_vote_timestamp(5);
|
||||
assert_eq!(tower.last_vote.timestamp(), Some(1));
|
||||
|
@ -2807,8 +2830,7 @@ pub mod test {
|
|||
assert_eq!(tower.last_timestamp.timestamp, 1);
|
||||
|
||||
// Vote has timestamp
|
||||
tower.last_vote =
|
||||
VoteTransaction::from(VoteStateUpdate::from(vec![(0, 3), (1, 2), (2, 1)]));
|
||||
tower.last_vote = VoteTransaction::from(TowerSync::from(vec![(0, 3), (1, 2), (2, 1)]));
|
||||
tower.refresh_last_vote_timestamp(5);
|
||||
assert_eq!(tower.last_vote.timestamp(), Some(2));
|
||||
assert_eq!(tower.last_timestamp.slot, 2);
|
||||
|
|
|
@ -3390,6 +3390,8 @@ impl ReplayStage {
|
|||
progress
|
||||
.get_hash(last_voted_slot)
|
||||
.expect("Must exist for us to have frozen descendant"),
|
||||
bank.feature_set
|
||||
.is_active(&feature_set::enable_tower_sync_ix::id()),
|
||||
);
|
||||
// Since we are updating our tower we need to update associated caches for previously computed
|
||||
// slots as well.
|
||||
|
|
|
@ -12,7 +12,7 @@ use {
|
|||
slot_hashes::SlotHashes,
|
||||
sysvar,
|
||||
},
|
||||
solana_vote::vote_transaction::{VoteTransaction, VoteTransaction::VoteStateUpdate},
|
||||
solana_vote::vote_transaction::VoteTransaction,
|
||||
std::{
|
||||
collections::{BTreeMap, HashMap, HashSet},
|
||||
sync::Arc,
|
||||
|
@ -231,7 +231,7 @@ impl VerifiedVotePackets {
|
|||
let timestamp = vote.timestamp();
|
||||
|
||||
match vote {
|
||||
VoteStateUpdate(_) => {
|
||||
VoteTransaction::VoteStateUpdate(_) | VoteTransaction::TowerSync(_) => {
|
||||
let (latest_gossip_slot, latest_timestamp) =
|
||||
self.0.get(&vote_account_key).map_or((0, None), |vote| {
|
||||
(vote.get_latest_gossip_slot(), vote.get_latest_timestamp())
|
||||
|
|
|
@ -193,16 +193,25 @@ declare_process_instruction!(Entrypoint, DEFAULT_COMPUTE_UNITS, |invoke_context|
|
|||
&invoke_context.feature_set,
|
||||
)
|
||||
}
|
||||
VoteInstruction::TowerSync(_tower_sync)
|
||||
| VoteInstruction::TowerSyncSwitch(_tower_sync, _) => {
|
||||
VoteInstruction::TowerSync(tower_sync)
|
||||
| VoteInstruction::TowerSyncSwitch(tower_sync, _) => {
|
||||
if !invoke_context
|
||||
.feature_set
|
||||
.is_active(&feature_set::enable_tower_sync_ix::id())
|
||||
{
|
||||
return Err(InstructionError::InvalidInstructionData);
|
||||
}
|
||||
// TODO: will fill in future PR
|
||||
return Err(InstructionError::InvalidInstructionData);
|
||||
let sysvar_cache = invoke_context.get_sysvar_cache();
|
||||
let slot_hashes = sysvar_cache.get_slot_hashes()?;
|
||||
let clock = sysvar_cache.get_clock()?;
|
||||
vote_state::process_tower_sync(
|
||||
&mut me,
|
||||
slot_hashes.slot_hashes(),
|
||||
&clock,
|
||||
tower_sync,
|
||||
&signers,
|
||||
&invoke_context.feature_set,
|
||||
)
|
||||
}
|
||||
VoteInstruction::Withdraw(lamports) => {
|
||||
instruction_context.check_number_of_instruction_accounts(2)?;
|
||||
|
@ -257,7 +266,7 @@ mod tests {
|
|||
vote_switch, withdraw, CreateVoteAccountConfig, VoteInstruction,
|
||||
},
|
||||
vote_state::{
|
||||
self, Lockout, Vote, VoteAuthorize, VoteAuthorizeCheckedWithSeedArgs,
|
||||
self, Lockout, TowerSync, Vote, VoteAuthorize, VoteAuthorizeCheckedWithSeedArgs,
|
||||
VoteAuthorizeWithSeedArgs, VoteInit, VoteState, VoteStateUpdate, VoteStateVersions,
|
||||
},
|
||||
},
|
||||
|
@ -274,6 +283,7 @@ mod tests {
|
|||
self, clock::Clock, epoch_schedule::EpochSchedule, rent::Rent,
|
||||
slot_hashes::SlotHashes,
|
||||
},
|
||||
vote::instruction::{tower_sync, tower_sync_switch},
|
||||
},
|
||||
std::{collections::HashSet, str::FromStr},
|
||||
};
|
||||
|
@ -490,11 +500,12 @@ mod tests {
|
|||
(vote_pubkey, vote_account_with_epoch_credits)
|
||||
}
|
||||
|
||||
/// Returns Vec of serialized VoteInstruction and flag indicating if it is a vote state update
|
||||
/// Returns Vec of serialized VoteInstruction and flag indicating if it is a vote state proposal
|
||||
/// variant, along with the original vote
|
||||
fn create_serialized_votes() -> (Vote, Vec<(Vec<u8>, bool)>) {
|
||||
let vote = Vote::new(vec![1], Hash::default());
|
||||
let vote_state_update = VoteStateUpdate::from(vec![(1, 1)]);
|
||||
let tower_sync = TowerSync::from(vec![(1, 1)]);
|
||||
(
|
||||
vote.clone(),
|
||||
vec![
|
||||
|
@ -508,6 +519,10 @@ mod tests {
|
|||
serialize(&VoteInstruction::CompactUpdateVoteState(vote_state_update)).unwrap(),
|
||||
true,
|
||||
),
|
||||
(
|
||||
serialize(&VoteInstruction::TowerSync(tower_sync)).unwrap(),
|
||||
true,
|
||||
),
|
||||
],
|
||||
)
|
||||
}
|
||||
|
@ -1742,6 +1757,14 @@ mod tests {
|
|||
),
|
||||
Err(InstructionError::InvalidAccountOwner),
|
||||
);
|
||||
process_instruction_as_one_arg(
|
||||
&tower_sync(
|
||||
&invalid_vote_state_pubkey(),
|
||||
&Pubkey::default(),
|
||||
TowerSync::default(),
|
||||
),
|
||||
Err(InstructionError::InvalidAccountOwner),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1904,7 +1927,6 @@ mod tests {
|
|||
),
|
||||
Err(InstructionError::InvalidAccountData),
|
||||
);
|
||||
|
||||
process_instruction_as_one_arg(
|
||||
&compact_update_vote_state_switch(
|
||||
&Pubkey::default(),
|
||||
|
@ -1914,6 +1936,19 @@ mod tests {
|
|||
),
|
||||
Err(InstructionError::InvalidAccountData),
|
||||
);
|
||||
process_instruction_as_one_arg(
|
||||
&tower_sync(&Pubkey::default(), &Pubkey::default(), TowerSync::default()),
|
||||
Err(InstructionError::InvalidAccountData),
|
||||
);
|
||||
process_instruction_as_one_arg(
|
||||
&tower_sync_switch(
|
||||
&Pubkey::default(),
|
||||
&Pubkey::default(),
|
||||
TowerSync::default(),
|
||||
Hash::default(),
|
||||
),
|
||||
Err(InstructionError::InvalidAccountData),
|
||||
);
|
||||
|
||||
process_instruction_as_one_arg(
|
||||
&update_validator_identity(
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,7 @@
|
|||
use {
|
||||
solana_program::vote::{
|
||||
self,
|
||||
state::{Vote, VoteStateUpdate},
|
||||
state::{TowerSync, Vote, VoteStateUpdate},
|
||||
},
|
||||
solana_sdk::{
|
||||
clock::Slot,
|
||||
|
@ -102,3 +102,33 @@ pub fn new_compact_vote_state_update_transaction(
|
|||
vote_tx.partial_sign(&[authorized_voter_keypair], blockhash);
|
||||
vote_tx
|
||||
}
|
||||
|
||||
pub fn new_tower_sync_transaction(
|
||||
tower_sync: TowerSync,
|
||||
blockhash: Hash,
|
||||
node_keypair: &Keypair,
|
||||
vote_keypair: &Keypair,
|
||||
authorized_voter_keypair: &Keypair,
|
||||
switch_proof_hash: Option<Hash>,
|
||||
) -> Transaction {
|
||||
let vote_ix = if let Some(switch_proof_hash) = switch_proof_hash {
|
||||
vote::instruction::tower_sync_switch(
|
||||
&vote_keypair.pubkey(),
|
||||
&authorized_voter_keypair.pubkey(),
|
||||
tower_sync,
|
||||
switch_proof_hash,
|
||||
)
|
||||
} else {
|
||||
vote::instruction::tower_sync(
|
||||
&vote_keypair.pubkey(),
|
||||
&authorized_voter_keypair.pubkey(),
|
||||
tower_sync,
|
||||
)
|
||||
};
|
||||
|
||||
let mut vote_tx = Transaction::new_with_payer(&[vote_ix], Some(&node_keypair.pubkey()));
|
||||
|
||||
vote_tx.partial_sign(&[node_keypair], blockhash);
|
||||
vote_tx.partial_sign(&[authorized_voter_keypair], blockhash);
|
||||
vote_tx
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue