uses varint encoding for vote-state lockout offsets
The commit removes CompactVoteStateUpdate and instead reduces serialized size of VoteStateUpdate using varint encoding for vote-state lockout offsets.
This commit is contained in:
parent
f6fbc47b2d
commit
4f22ee8f9b
|
@ -169,7 +169,7 @@ impl TowerVersions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[frozen_abi(digest = "GrkFcKqGEkJNUYoK1M8rorehi2yyLF4N3Gsj6j8f47Jn")]
|
#[frozen_abi(digest = "HQoLKAJEQTuVy8nMSkVWbrH3M5xKksxdMEZHGLWbnX6w")]
|
||||||
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, AbiExample)]
|
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, AbiExample)]
|
||||||
pub struct Tower {
|
pub struct Tower {
|
||||||
pub node_pubkey: Pubkey,
|
pub node_pubkey: Pubkey,
|
||||||
|
|
|
@ -2020,13 +2020,7 @@ impl ReplayStage {
|
||||||
.is_active(&feature_set::compact_vote_state_updates::id());
|
.is_active(&feature_set::compact_vote_state_updates::id());
|
||||||
let vote = match (should_compact, vote) {
|
let vote = match (should_compact, vote) {
|
||||||
(true, VoteTransaction::VoteStateUpdate(vote_state_update)) => {
|
(true, VoteTransaction::VoteStateUpdate(vote_state_update)) => {
|
||||||
if let Some(compact_vote_state_update) = vote_state_update.compact() {
|
VoteTransaction::CompactVoteStateUpdate(vote_state_update)
|
||||||
VoteTransaction::from(compact_vote_state_update)
|
|
||||||
} else {
|
|
||||||
// Compaction failed
|
|
||||||
warn!("Compaction failed when generating vote tx for vote account {}. Unable to vote", vote_account_pubkey);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
(_, vote) => vote,
|
(_, vote) => vote,
|
||||||
};
|
};
|
||||||
|
|
|
@ -172,8 +172,8 @@ pub fn process_instruction(
|
||||||
Err(InstructionError::InvalidInstructionData)
|
Err(InstructionError::InvalidInstructionData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VoteInstruction::CompactUpdateVoteState(compact_vote_state_update)
|
VoteInstruction::CompactUpdateVoteState(vote_state_update)
|
||||||
| VoteInstruction::CompactUpdateVoteStateSwitch(compact_vote_state_update, _) => {
|
| VoteInstruction::CompactUpdateVoteStateSwitch(vote_state_update, _) => {
|
||||||
if invoke_context
|
if invoke_context
|
||||||
.feature_set
|
.feature_set
|
||||||
.is_active(&feature_set::allow_votes_to_directly_update_vote_state::id())
|
.is_active(&feature_set::allow_votes_to_directly_update_vote_state::id())
|
||||||
|
@ -184,7 +184,6 @@ pub fn process_instruction(
|
||||||
let sysvar_cache = invoke_context.get_sysvar_cache();
|
let sysvar_cache = invoke_context.get_sysvar_cache();
|
||||||
let slot_hashes = sysvar_cache.get_slot_hashes()?;
|
let slot_hashes = sysvar_cache.get_slot_hashes()?;
|
||||||
let clock = sysvar_cache.get_clock()?;
|
let clock = sysvar_cache.get_clock()?;
|
||||||
let vote_state_update = compact_vote_state_update.uncompact()?;
|
|
||||||
vote_state::process_vote_state_update(
|
vote_state::process_vote_state_update(
|
||||||
&mut me,
|
&mut me,
|
||||||
slot_hashes.slot_hashes(),
|
slot_hashes.slot_hashes(),
|
||||||
|
|
|
@ -5,7 +5,7 @@ use {
|
||||||
log::*,
|
log::*,
|
||||||
serde_derive::{Deserialize, Serialize},
|
serde_derive::{Deserialize, Serialize},
|
||||||
solana_metrics::datapoint_debug,
|
solana_metrics::datapoint_debug,
|
||||||
solana_program::vote::{error::VoteError, program::id},
|
solana_program::vote::{error::VoteError, program::id, state::serde_compact_vote_state_update},
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
account::{AccountSharedData, ReadableAccount, WritableAccount},
|
account::{AccountSharedData, ReadableAccount, WritableAccount},
|
||||||
clock::{Epoch, Slot, UnixTimestamp},
|
clock::{Epoch, Slot, UnixTimestamp},
|
||||||
|
@ -27,12 +27,13 @@ use {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[frozen_abi(digest = "8Xa47j7LCp99Q7CQeTz4KPWU8sZgGFpAJw2K4VbPgGh8")]
|
#[frozen_abi(digest = "2AuJFjx7SYrJ2ugCfH1jFh3Lr9UHMEPfKwwk1NcjqND1")]
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, AbiEnumVisitor, AbiExample)]
|
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, AbiEnumVisitor, AbiExample)]
|
||||||
pub enum VoteTransaction {
|
pub enum VoteTransaction {
|
||||||
Vote(Vote),
|
Vote(Vote),
|
||||||
VoteStateUpdate(VoteStateUpdate),
|
VoteStateUpdate(VoteStateUpdate),
|
||||||
CompactVoteStateUpdate(CompactVoteStateUpdate),
|
#[serde(with = "serde_compact_vote_state_update")]
|
||||||
|
CompactVoteStateUpdate(VoteStateUpdate),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VoteTransaction {
|
impl VoteTransaction {
|
||||||
|
@ -40,32 +41,26 @@ impl VoteTransaction {
|
||||||
match self {
|
match self {
|
||||||
VoteTransaction::Vote(vote) => vote.slots.clone(),
|
VoteTransaction::Vote(vote) => vote.slots.clone(),
|
||||||
VoteTransaction::VoteStateUpdate(vote_state_update) => vote_state_update.slots(),
|
VoteTransaction::VoteStateUpdate(vote_state_update) => vote_state_update.slots(),
|
||||||
VoteTransaction::CompactVoteStateUpdate(compact_state_update) => {
|
VoteTransaction::CompactVoteStateUpdate(vote_state_update) => vote_state_update.slots(),
|
||||||
compact_state_update.slots()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn slot(&self, i: usize) -> Slot {
|
pub fn slot(&self, i: usize) -> Slot {
|
||||||
match self {
|
match self {
|
||||||
VoteTransaction::Vote(vote) => vote.slots[i],
|
VoteTransaction::Vote(vote) => vote.slots[i],
|
||||||
VoteTransaction::VoteStateUpdate(vote_state_update) => {
|
VoteTransaction::VoteStateUpdate(vote_state_update)
|
||||||
|
| VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
|
||||||
vote_state_update.lockouts[i].slot
|
vote_state_update.lockouts[i].slot
|
||||||
}
|
}
|
||||||
VoteTransaction::CompactVoteStateUpdate(compact_state_update) => {
|
|
||||||
compact_state_update.slots()[i]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
VoteTransaction::Vote(vote) => vote.slots.len(),
|
VoteTransaction::Vote(vote) => vote.slots.len(),
|
||||||
VoteTransaction::VoteStateUpdate(vote_state_update) => vote_state_update.lockouts.len(),
|
VoteTransaction::VoteStateUpdate(vote_state_update)
|
||||||
VoteTransaction::CompactVoteStateUpdate(compact_state_update) => {
|
| VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
|
||||||
1 + compact_state_update.lockouts_32.len()
|
vote_state_update.lockouts.len()
|
||||||
+ compact_state_update.lockouts_16.len()
|
|
||||||
+ compact_state_update.lockouts_8.len()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,10 +68,10 @@ impl VoteTransaction {
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
VoteTransaction::Vote(vote) => vote.slots.is_empty(),
|
VoteTransaction::Vote(vote) => vote.slots.is_empty(),
|
||||||
VoteTransaction::VoteStateUpdate(vote_state_update) => {
|
VoteTransaction::VoteStateUpdate(vote_state_update)
|
||||||
|
| VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
|
||||||
vote_state_update.lockouts.is_empty()
|
vote_state_update.lockouts.is_empty()
|
||||||
}
|
}
|
||||||
VoteTransaction::CompactVoteStateUpdate(_) => false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,18 +79,16 @@ impl VoteTransaction {
|
||||||
match self {
|
match self {
|
||||||
VoteTransaction::Vote(vote) => vote.hash,
|
VoteTransaction::Vote(vote) => vote.hash,
|
||||||
VoteTransaction::VoteStateUpdate(vote_state_update) => vote_state_update.hash,
|
VoteTransaction::VoteStateUpdate(vote_state_update) => vote_state_update.hash,
|
||||||
VoteTransaction::CompactVoteStateUpdate(compact_state_update) => {
|
VoteTransaction::CompactVoteStateUpdate(vote_state_update) => vote_state_update.hash,
|
||||||
compact_state_update.hash
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn timestamp(&self) -> Option<UnixTimestamp> {
|
pub fn timestamp(&self) -> Option<UnixTimestamp> {
|
||||||
match self {
|
match self {
|
||||||
VoteTransaction::Vote(vote) => vote.timestamp,
|
VoteTransaction::Vote(vote) => vote.timestamp,
|
||||||
VoteTransaction::VoteStateUpdate(vote_state_update) => vote_state_update.timestamp,
|
VoteTransaction::VoteStateUpdate(vote_state_update)
|
||||||
VoteTransaction::CompactVoteStateUpdate(compact_state_update) => {
|
| VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
|
||||||
compact_state_update.timestamp
|
vote_state_update.timestamp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,9 +96,9 @@ impl VoteTransaction {
|
||||||
pub fn set_timestamp(&mut self, ts: Option<UnixTimestamp>) {
|
pub fn set_timestamp(&mut self, ts: Option<UnixTimestamp>) {
|
||||||
match self {
|
match self {
|
||||||
VoteTransaction::Vote(vote) => vote.timestamp = ts,
|
VoteTransaction::Vote(vote) => vote.timestamp = ts,
|
||||||
VoteTransaction::VoteStateUpdate(vote_state_update) => vote_state_update.timestamp = ts,
|
VoteTransaction::VoteStateUpdate(vote_state_update)
|
||||||
VoteTransaction::CompactVoteStateUpdate(compact_state_update) => {
|
| VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
|
||||||
compact_state_update.timestamp = ts
|
vote_state_update.timestamp = ts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,12 +106,10 @@ impl VoteTransaction {
|
||||||
pub fn last_voted_slot(&self) -> Option<Slot> {
|
pub fn last_voted_slot(&self) -> Option<Slot> {
|
||||||
match self {
|
match self {
|
||||||
VoteTransaction::Vote(vote) => vote.slots.last().copied(),
|
VoteTransaction::Vote(vote) => vote.slots.last().copied(),
|
||||||
VoteTransaction::VoteStateUpdate(vote_state_update) => {
|
VoteTransaction::VoteStateUpdate(vote_state_update)
|
||||||
|
| VoteTransaction::CompactVoteStateUpdate(vote_state_update) => {
|
||||||
Some(vote_state_update.lockouts.back()?.slot)
|
Some(vote_state_update.lockouts.back()?.slot)
|
||||||
}
|
}
|
||||||
VoteTransaction::CompactVoteStateUpdate(compact_state_update) => {
|
|
||||||
compact_state_update.slots().last().copied()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,12 +130,6 @@ impl From<VoteStateUpdate> for VoteTransaction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<CompactVoteStateUpdate> for VoteTransaction {
|
|
||||||
fn from(compact_state_update: CompactVoteStateUpdate) -> Self {
|
|
||||||
VoteTransaction::CompactVoteStateUpdate(compact_state_update)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// utility function, used by Stakes, tests
|
// utility function, used by Stakes, tests
|
||||||
pub fn from<T: ReadableAccount>(account: &T) -> Option<VoteState> {
|
pub fn from<T: ReadableAccount>(account: &T) -> Option<VoteState> {
|
||||||
VoteState::deserialize(account.data()).ok()
|
VoteState::deserialize(account.data()).ok()
|
||||||
|
@ -2970,103 +2955,4 @@ mod tests {
|
||||||
Err(VoteError::SlotHashMismatch),
|
Err(VoteError::SlotHashMismatch),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_compact_vote_state_update_parity() {
|
|
||||||
let mut vote_state_update = VoteStateUpdate::from(vec![(2, 4), (4, 3), (6, 2), (7, 1)]);
|
|
||||||
vote_state_update.hash = Hash::new_unique();
|
|
||||||
vote_state_update.root = Some(1);
|
|
||||||
|
|
||||||
let compact_vote_state_update = vote_state_update.clone().compact().unwrap();
|
|
||||||
|
|
||||||
assert_eq!(vote_state_update.slots(), compact_vote_state_update.slots());
|
|
||||||
assert_eq!(vote_state_update.hash, compact_vote_state_update.hash);
|
|
||||||
assert_eq!(vote_state_update.root, compact_vote_state_update.root());
|
|
||||||
|
|
||||||
let vote_state_update_new = compact_vote_state_update.uncompact().unwrap();
|
|
||||||
assert_eq!(vote_state_update, vote_state_update_new);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_compact_vote_state_update_large_offsets() {
|
|
||||||
let vote_state_update = VoteStateUpdate::from(vec![
|
|
||||||
(0, 31),
|
|
||||||
(1, 30),
|
|
||||||
(2, 29),
|
|
||||||
(3, 28),
|
|
||||||
(u64::pow(2, 28), 17),
|
|
||||||
(u64::pow(2, 28) + u64::pow(2, 16), 1),
|
|
||||||
]);
|
|
||||||
let compact_vote_state_update = vote_state_update.clone().compact().unwrap();
|
|
||||||
|
|
||||||
assert_eq!(vote_state_update.slots(), compact_vote_state_update.slots());
|
|
||||||
|
|
||||||
let vote_state_update_new = compact_vote_state_update.uncompact().unwrap();
|
|
||||||
assert_eq!(vote_state_update, vote_state_update_new);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_compact_vote_state_update_border_conditions() {
|
|
||||||
let two_31 = u64::pow(2, 31);
|
|
||||||
let two_15 = u64::pow(2, 15);
|
|
||||||
let vote_state_update = VoteStateUpdate::from(vec![
|
|
||||||
(0, 31),
|
|
||||||
(two_31, 16),
|
|
||||||
(two_31 + 1, 15),
|
|
||||||
(two_31 + two_15, 7),
|
|
||||||
(two_31 + two_15 + 1, 6),
|
|
||||||
(two_31 + two_15 + 1 + 64, 1),
|
|
||||||
]);
|
|
||||||
let compact_vote_state_update = vote_state_update.clone().compact().unwrap();
|
|
||||||
|
|
||||||
assert_eq!(vote_state_update.slots(), compact_vote_state_update.slots());
|
|
||||||
|
|
||||||
let vote_state_update_new = compact_vote_state_update.uncompact().unwrap();
|
|
||||||
assert_eq!(vote_state_update, vote_state_update_new);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_compact_vote_state_update_large_root() {
|
|
||||||
let two_58 = u64::pow(2, 58);
|
|
||||||
let two_31 = u64::pow(2, 31);
|
|
||||||
let mut vote_state_update = VoteStateUpdate::from(vec![(two_58, 31), (two_58 + two_31, 1)]);
|
|
||||||
vote_state_update.root = Some(two_31);
|
|
||||||
let compact_vote_state_update = vote_state_update.clone().compact().unwrap();
|
|
||||||
|
|
||||||
assert_eq!(vote_state_update.slots(), compact_vote_state_update.slots());
|
|
||||||
|
|
||||||
let vote_state_update_new = compact_vote_state_update.uncompact().unwrap();
|
|
||||||
assert_eq!(vote_state_update, vote_state_update_new);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_compact_vote_state_update_overflow() {
|
|
||||||
let compact_vote_state_update = CompactVoteStateUpdate {
|
|
||||||
root: u64::MAX - 1,
|
|
||||||
root_to_first_vote_offset: 10,
|
|
||||||
lockouts_32: vec![],
|
|
||||||
lockouts_16: vec![],
|
|
||||||
lockouts_8: vec![CompactLockout::new(10)],
|
|
||||||
hash: Hash::new_unique(),
|
|
||||||
timestamp: None,
|
|
||||||
};
|
|
||||||
assert_eq!(
|
|
||||||
Err(InstructionError::ArithmeticOverflow),
|
|
||||||
compact_vote_state_update.uncompact()
|
|
||||||
);
|
|
||||||
|
|
||||||
let compact_vote_state_update = CompactVoteStateUpdate {
|
|
||||||
root: u64::MAX - u32::MAX as u64,
|
|
||||||
root_to_first_vote_offset: 10,
|
|
||||||
lockouts_32: vec![CompactLockout::new(u32::MAX)],
|
|
||||||
lockouts_16: vec![],
|
|
||||||
lockouts_8: vec![CompactLockout::new(10)],
|
|
||||||
hash: Hash::new_unique(),
|
|
||||||
timestamp: None,
|
|
||||||
};
|
|
||||||
assert_eq!(
|
|
||||||
Err(InstructionError::ArithmeticOverflow),
|
|
||||||
compact_vote_state_update.uncompact()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,17 +82,11 @@ fn parse_vote_instruction_data(
|
||||||
VoteInstruction::UpdateVoteStateSwitch(vote_state_update, hash) => {
|
VoteInstruction::UpdateVoteStateSwitch(vote_state_update, hash) => {
|
||||||
Some((VoteTransaction::from(vote_state_update), Some(hash)))
|
Some((VoteTransaction::from(vote_state_update), Some(hash)))
|
||||||
}
|
}
|
||||||
VoteInstruction::CompactUpdateVoteState(compact_vote_state_update) => {
|
VoteInstruction::CompactUpdateVoteState(vote_state_update) => {
|
||||||
compact_vote_state_update
|
Some((VoteTransaction::from(vote_state_update), None))
|
||||||
.uncompact()
|
|
||||||
.ok()
|
|
||||||
.map(|vote_state_update| (VoteTransaction::from(vote_state_update), None))
|
|
||||||
}
|
}
|
||||||
VoteInstruction::CompactUpdateVoteStateSwitch(compact_vote_state_update, hash) => {
|
VoteInstruction::CompactUpdateVoteStateSwitch(vote_state_update, hash) => {
|
||||||
compact_vote_state_update
|
Some((VoteTransaction::from(vote_state_update), Some(hash)))
|
||||||
.uncompact()
|
|
||||||
.ok()
|
|
||||||
.map(|vote_state_update| (VoteTransaction::from(vote_state_update), Some(hash)))
|
|
||||||
}
|
}
|
||||||
VoteInstruction::Authorize(_, _)
|
VoteInstruction::Authorize(_, _)
|
||||||
| VoteInstruction::AuthorizeChecked(_)
|
| VoteInstruction::AuthorizeChecked(_)
|
||||||
|
|
|
@ -9,8 +9,9 @@ use {
|
||||||
vote::{
|
vote::{
|
||||||
program::id,
|
program::id,
|
||||||
state::{
|
state::{
|
||||||
CompactVoteStateUpdate, Vote, VoteAuthorize, VoteAuthorizeCheckedWithSeedArgs,
|
serde_compact_vote_state_update, Vote, VoteAuthorize,
|
||||||
VoteAuthorizeWithSeedArgs, VoteInit, VoteState, VoteStateUpdate,
|
VoteAuthorizeCheckedWithSeedArgs, VoteAuthorizeWithSeedArgs, VoteInit, VoteState,
|
||||||
|
VoteStateUpdate,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -132,14 +133,18 @@ pub enum VoteInstruction {
|
||||||
/// # Account references
|
/// # Account references
|
||||||
/// 0. `[Write]` Vote account to vote with
|
/// 0. `[Write]` Vote account to vote with
|
||||||
/// 1. `[SIGNER]` Vote authority
|
/// 1. `[SIGNER]` Vote authority
|
||||||
CompactUpdateVoteState(CompactVoteStateUpdate),
|
#[serde(with = "serde_compact_vote_state_update")]
|
||||||
|
CompactUpdateVoteState(VoteStateUpdate),
|
||||||
|
|
||||||
/// Update the onchain vote state for the signer along with a switching proof.
|
/// Update the onchain vote state for the signer along with a switching proof.
|
||||||
///
|
///
|
||||||
/// # Account references
|
/// # Account references
|
||||||
/// 0. `[Write]` Vote account to vote with
|
/// 0. `[Write]` Vote account to vote with
|
||||||
/// 1. `[SIGNER]` Vote authority
|
/// 1. `[SIGNER]` Vote authority
|
||||||
CompactUpdateVoteStateSwitch(CompactVoteStateUpdate, Hash),
|
CompactUpdateVoteStateSwitch(
|
||||||
|
#[serde(with = "serde_compact_vote_state_update")] VoteStateUpdate,
|
||||||
|
Hash,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize_account(vote_pubkey: &Pubkey, vote_init: &VoteInit) -> Instruction {
|
fn initialize_account(vote_pubkey: &Pubkey, vote_init: &VoteInit) -> Instruction {
|
||||||
|
@ -387,7 +392,7 @@ pub fn update_vote_state_switch(
|
||||||
pub fn compact_update_vote_state(
|
pub fn compact_update_vote_state(
|
||||||
vote_pubkey: &Pubkey,
|
vote_pubkey: &Pubkey,
|
||||||
authorized_voter_pubkey: &Pubkey,
|
authorized_voter_pubkey: &Pubkey,
|
||||||
compact_vote_state_update: CompactVoteStateUpdate,
|
vote_state_update: VoteStateUpdate,
|
||||||
) -> Instruction {
|
) -> Instruction {
|
||||||
let account_metas = vec![
|
let account_metas = vec![
|
||||||
AccountMeta::new(*vote_pubkey, false),
|
AccountMeta::new(*vote_pubkey, false),
|
||||||
|
@ -396,7 +401,7 @@ pub fn compact_update_vote_state(
|
||||||
|
|
||||||
Instruction::new_with_bincode(
|
Instruction::new_with_bincode(
|
||||||
id(),
|
id(),
|
||||||
&VoteInstruction::CompactUpdateVoteState(compact_vote_state_update),
|
&VoteInstruction::CompactUpdateVoteState(vote_state_update),
|
||||||
account_metas,
|
account_metas,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -404,7 +409,7 @@ pub fn compact_update_vote_state(
|
||||||
pub fn compact_update_vote_state_switch(
|
pub fn compact_update_vote_state_switch(
|
||||||
vote_pubkey: &Pubkey,
|
vote_pubkey: &Pubkey,
|
||||||
authorized_voter_pubkey: &Pubkey,
|
authorized_voter_pubkey: &Pubkey,
|
||||||
vote_state_update: CompactVoteStateUpdate,
|
vote_state_update: VoteStateUpdate,
|
||||||
proof_hash: Hash,
|
proof_hash: Hash,
|
||||||
) -> Instruction {
|
) -> Instruction {
|
||||||
let account_metas = vec![
|
let account_metas = vec![
|
||||||
|
|
|
@ -9,7 +9,6 @@ use {
|
||||||
instruction::InstructionError,
|
instruction::InstructionError,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
rent::Rent,
|
rent::Rent,
|
||||||
short_vec,
|
|
||||||
sysvar::clock::Clock,
|
sysvar::clock::Clock,
|
||||||
vote::{authorized_voters::AuthorizedVoters, error::VoteError},
|
vote::{authorized_voters::AuthorizedVoters, error::VoteError},
|
||||||
},
|
},
|
||||||
|
@ -85,28 +84,6 @@ impl Lockout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Copy, Clone, AbiExample)]
|
|
||||||
pub struct CompactLockout<T: Sized> {
|
|
||||||
// Offset to the next vote, 0 if this is the last vote in the tower
|
|
||||||
pub offset: T,
|
|
||||||
// Confirmation count, guarenteed to be < 32
|
|
||||||
pub confirmation_count: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> CompactLockout<T> {
|
|
||||||
pub fn new(offset: T) -> Self {
|
|
||||||
Self {
|
|
||||||
offset,
|
|
||||||
confirmation_count: 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The number of slots for which this vote is locked
|
|
||||||
pub fn lockout(&self) -> u64 {
|
|
||||||
(INITIAL_LOCKOUT as u64).pow(self.confirmation_count.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[frozen_abi(digest = "GwJfVFsATSj7nvKwtUkHYzqPRaPY6SLxPGXApuCya3x5")]
|
#[frozen_abi(digest = "GwJfVFsATSj7nvKwtUkHYzqPRaPY6SLxPGXApuCya3x5")]
|
||||||
#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone, AbiExample)]
|
#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone, AbiExample)]
|
||||||
pub struct VoteStateUpdate {
|
pub struct VoteStateUpdate {
|
||||||
|
@ -151,205 +128,6 @@ impl VoteStateUpdate {
|
||||||
pub fn slots(&self) -> Vec<Slot> {
|
pub fn slots(&self) -> Vec<Slot> {
|
||||||
self.lockouts.iter().map(|lockout| lockout.slot).collect()
|
self.lockouts.iter().map(|lockout| lockout.slot).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compact(self) -> Option<CompactVoteStateUpdate> {
|
|
||||||
CompactVoteStateUpdate::new(self.lockouts, self.root, self.hash, self.timestamp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Ignoring overhead, in a full `VoteStateUpdate` the lockouts take up
|
|
||||||
/// 31 * (64 + 32) = 2976 bits.
|
|
||||||
///
|
|
||||||
/// In this schema we separate the votes into 3 separate lockout structures
|
|
||||||
/// and store offsets rather than slot number, allowing us to use smaller fields.
|
|
||||||
///
|
|
||||||
/// In a full `CompactVoteStateUpdate` the lockouts take up
|
|
||||||
/// 64 + (32 + 8) * 16 + (16 + 8) * 8 + (8 + 8) * 6 = 992 bits
|
|
||||||
/// allowing us to greatly reduce block size.
|
|
||||||
#[frozen_abi(digest = "EeMnyxPUyd3hK7UQ8BcWDW8qrsdXA9F6ZUoAWAh1nDxX")]
|
|
||||||
#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone, AbiExample)]
|
|
||||||
pub struct CompactVoteStateUpdate {
|
|
||||||
/// The proposed root, u64::MAX if there is no root
|
|
||||||
pub root: Slot,
|
|
||||||
/// The offset from the root (or 0 if no root) to the first vote
|
|
||||||
pub root_to_first_vote_offset: u64,
|
|
||||||
/// Part of the proposed tower, votes with confirmation_count > 15
|
|
||||||
#[serde(with = "short_vec")]
|
|
||||||
pub lockouts_32: Vec<CompactLockout<u32>>,
|
|
||||||
/// Part of the proposed tower, votes with 15 >= confirmation_count > 7
|
|
||||||
#[serde(with = "short_vec")]
|
|
||||||
pub lockouts_16: Vec<CompactLockout<u16>>,
|
|
||||||
/// Part of the proposed tower, votes with 7 >= confirmation_count
|
|
||||||
#[serde(with = "short_vec")]
|
|
||||||
pub lockouts_8: Vec<CompactLockout<u8>>,
|
|
||||||
|
|
||||||
/// Signature of the bank's state at the last slot
|
|
||||||
pub hash: Hash,
|
|
||||||
/// Processing timestamp of last slot
|
|
||||||
pub timestamp: Option<UnixTimestamp>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<(Slot, u32)>> for CompactVoteStateUpdate {
|
|
||||||
fn from(recent_slots: Vec<(Slot, u32)>) -> Self {
|
|
||||||
let lockouts: VecDeque<Lockout> = recent_slots
|
|
||||||
.into_iter()
|
|
||||||
.map(|(slot, confirmation_count)| Lockout {
|
|
||||||
slot,
|
|
||||||
confirmation_count,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
Self::new(lockouts, None, Hash::default(), None).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CompactVoteStateUpdate {
|
|
||||||
pub fn new(
|
|
||||||
mut lockouts: VecDeque<Lockout>,
|
|
||||||
root: Option<Slot>,
|
|
||||||
hash: Hash,
|
|
||||||
timestamp: Option<UnixTimestamp>,
|
|
||||||
) -> Option<Self> {
|
|
||||||
if lockouts.is_empty() {
|
|
||||||
return Some(Self::default());
|
|
||||||
}
|
|
||||||
let mut cur_slot = root.unwrap_or(0u64);
|
|
||||||
let mut cur_confirmation_count = 0;
|
|
||||||
let offset = lockouts
|
|
||||||
.pop_front()
|
|
||||||
.map(
|
|
||||||
|Lockout {
|
|
||||||
slot,
|
|
||||||
confirmation_count,
|
|
||||||
}| {
|
|
||||||
assert!(confirmation_count < 32);
|
|
||||||
|
|
||||||
slot.checked_sub(cur_slot).map(|offset| {
|
|
||||||
cur_slot = slot;
|
|
||||||
cur_confirmation_count = confirmation_count;
|
|
||||||
offset
|
|
||||||
})
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.expect("Tower should not be empty")?;
|
|
||||||
let mut lockouts_32 = Vec::new();
|
|
||||||
let mut lockouts_16 = Vec::new();
|
|
||||||
let mut lockouts_8 = Vec::new();
|
|
||||||
|
|
||||||
for Lockout {
|
|
||||||
slot,
|
|
||||||
confirmation_count,
|
|
||||||
} in lockouts
|
|
||||||
{
|
|
||||||
assert!(confirmation_count < 32);
|
|
||||||
let offset = slot.checked_sub(cur_slot)?;
|
|
||||||
if cur_confirmation_count > 15 {
|
|
||||||
lockouts_32.push(CompactLockout {
|
|
||||||
offset: offset.try_into().unwrap(),
|
|
||||||
confirmation_count: cur_confirmation_count.try_into().unwrap(),
|
|
||||||
});
|
|
||||||
} else if cur_confirmation_count > 7 {
|
|
||||||
lockouts_16.push(CompactLockout {
|
|
||||||
offset: offset.try_into().unwrap(),
|
|
||||||
confirmation_count: cur_confirmation_count.try_into().unwrap(),
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
lockouts_8.push(CompactLockout {
|
|
||||||
offset: offset.try_into().unwrap(),
|
|
||||||
confirmation_count: cur_confirmation_count.try_into().unwrap(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
cur_slot = slot;
|
|
||||||
cur_confirmation_count = confirmation_count;
|
|
||||||
}
|
|
||||||
// Last vote should be at the top of tower, so we don't have to explicitly store it
|
|
||||||
assert!(cur_confirmation_count == 1);
|
|
||||||
Some(Self {
|
|
||||||
root: root.unwrap_or(u64::MAX),
|
|
||||||
root_to_first_vote_offset: offset,
|
|
||||||
lockouts_32,
|
|
||||||
lockouts_16,
|
|
||||||
lockouts_8,
|
|
||||||
hash,
|
|
||||||
timestamp,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn root(&self) -> Option<Slot> {
|
|
||||||
if self.root == u64::MAX {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(self.root)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn slots(&self) -> Vec<Slot> {
|
|
||||||
std::iter::once(self.root_to_first_vote_offset)
|
|
||||||
.chain(self.lockouts_32.iter().map(|lockout| lockout.offset.into()))
|
|
||||||
.chain(self.lockouts_16.iter().map(|lockout| lockout.offset.into()))
|
|
||||||
.chain(self.lockouts_8.iter().map(|lockout| lockout.offset.into()))
|
|
||||||
.scan(self.root().unwrap_or(0), |prev_slot, offset| {
|
|
||||||
prev_slot.checked_add(offset).map(|slot| {
|
|
||||||
*prev_slot = slot;
|
|
||||||
slot
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn uncompact(self) -> Result<VoteStateUpdate, InstructionError> {
|
|
||||||
let first_slot = self
|
|
||||||
.root()
|
|
||||||
.unwrap_or(0)
|
|
||||||
.checked_add(self.root_to_first_vote_offset)
|
|
||||||
.ok_or(InstructionError::ArithmeticOverflow)?;
|
|
||||||
let mut arithmetic_overflow_occured = false;
|
|
||||||
let lockouts = self
|
|
||||||
.lockouts_32
|
|
||||||
.iter()
|
|
||||||
.map(|lockout| (lockout.offset.into(), lockout.confirmation_count))
|
|
||||||
.chain(
|
|
||||||
self.lockouts_16
|
|
||||||
.iter()
|
|
||||||
.map(|lockout| (lockout.offset.into(), lockout.confirmation_count)),
|
|
||||||
)
|
|
||||||
.chain(
|
|
||||||
self.lockouts_8
|
|
||||||
.iter()
|
|
||||||
.map(|lockout| (lockout.offset.into(), lockout.confirmation_count)),
|
|
||||||
)
|
|
||||||
.chain(
|
|
||||||
// To pick up the last element
|
|
||||||
std::iter::once((0, 1)),
|
|
||||||
)
|
|
||||||
.scan(
|
|
||||||
first_slot,
|
|
||||||
|slot, (offset, confirmation_count): (u64, u8)| {
|
|
||||||
let cur_slot = *slot;
|
|
||||||
if let Some(new_slot) = slot.checked_add(offset) {
|
|
||||||
*slot = new_slot;
|
|
||||||
Some(Lockout {
|
|
||||||
slot: cur_slot,
|
|
||||||
confirmation_count: confirmation_count.into(),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
arithmetic_overflow_occured = true;
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.collect();
|
|
||||||
if arithmetic_overflow_occured {
|
|
||||||
Err(InstructionError::ArithmeticOverflow)
|
|
||||||
} else {
|
|
||||||
Ok(VoteStateUpdate {
|
|
||||||
lockouts,
|
|
||||||
root: self.root(),
|
|
||||||
hash: self.hash,
|
|
||||||
timestamp: self.timestamp,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(Default, Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
@ -789,9 +567,109 @@ impl VoteState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod serde_compact_vote_state_update {
|
||||||
|
use {
|
||||||
|
super::*,
|
||||||
|
crate::{
|
||||||
|
clock::{Slot, UnixTimestamp},
|
||||||
|
serde_varint, short_vec,
|
||||||
|
vote::state::Lockout,
|
||||||
|
},
|
||||||
|
serde::{Deserialize, Deserializer, Serialize, Serializer},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, AbiExample)]
|
||||||
|
struct LockoutOffset {
|
||||||
|
#[serde(with = "serde_varint")]
|
||||||
|
offset: Slot,
|
||||||
|
confirmation_count: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
struct CompactVoteStateUpdate {
|
||||||
|
root: Slot,
|
||||||
|
#[serde(with = "short_vec")]
|
||||||
|
lockout_offsets: Vec<LockoutOffset>,
|
||||||
|
hash: Hash,
|
||||||
|
timestamp: Option<UnixTimestamp>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn serialize<S>(
|
||||||
|
vote_state_update: &VoteStateUpdate,
|
||||||
|
serializer: S,
|
||||||
|
) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
let lockout_offsets = vote_state_update.lockouts.iter().scan(
|
||||||
|
vote_state_update.root.unwrap_or_default(),
|
||||||
|
|slot, lockout| {
|
||||||
|
let offset = match lockout.slot.checked_sub(*slot) {
|
||||||
|
None => return Some(Err(serde::ser::Error::custom("Invalid vote lockout"))),
|
||||||
|
Some(offset) => offset,
|
||||||
|
};
|
||||||
|
let confirmation_count = match u8::try_from(lockout.confirmation_count) {
|
||||||
|
Ok(confirmation_count) => confirmation_count,
|
||||||
|
Err(_) => {
|
||||||
|
return Some(Err(serde::ser::Error::custom("Invalid confirmation count")))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let lockout_offset = LockoutOffset {
|
||||||
|
offset,
|
||||||
|
confirmation_count,
|
||||||
|
};
|
||||||
|
*slot = lockout.slot;
|
||||||
|
Some(Ok(lockout_offset))
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let compact_vote_state_update = CompactVoteStateUpdate {
|
||||||
|
root: vote_state_update.root.unwrap_or(Slot::MAX),
|
||||||
|
lockout_offsets: lockout_offsets.collect::<Result<_, _>>()?,
|
||||||
|
hash: vote_state_update.hash,
|
||||||
|
timestamp: vote_state_update.timestamp,
|
||||||
|
};
|
||||||
|
compact_vote_state_update.serialize(serializer)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deserialize<'de, D>(deserializer: D) -> Result<VoteStateUpdate, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let CompactVoteStateUpdate {
|
||||||
|
root,
|
||||||
|
lockout_offsets,
|
||||||
|
hash,
|
||||||
|
timestamp,
|
||||||
|
} = CompactVoteStateUpdate::deserialize(deserializer)?;
|
||||||
|
let root = (root != Slot::MAX).then_some(root);
|
||||||
|
let lockouts =
|
||||||
|
lockout_offsets
|
||||||
|
.iter()
|
||||||
|
.scan(root.unwrap_or_default(), |slot, lockout_offset| {
|
||||||
|
*slot = match slot.checked_add(lockout_offset.offset) {
|
||||||
|
None => {
|
||||||
|
return Some(Err(serde::de::Error::custom("Invalid lockout offset")))
|
||||||
|
}
|
||||||
|
Some(slot) => slot,
|
||||||
|
};
|
||||||
|
let lockout = Lockout {
|
||||||
|
slot: *slot,
|
||||||
|
confirmation_count: u32::from(lockout_offset.confirmation_count),
|
||||||
|
};
|
||||||
|
Some(Ok(lockout))
|
||||||
|
});
|
||||||
|
Ok(VoteStateUpdate {
|
||||||
|
root,
|
||||||
|
lockouts: lockouts.collect::<Result<_, _>>()?,
|
||||||
|
hash,
|
||||||
|
timestamp,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use {super::*, itertools::Itertools, rand::Rng};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_vote_serialize() {
|
fn test_vote_serialize() {
|
||||||
|
@ -1235,4 +1113,50 @@ mod tests {
|
||||||
// golden, may need updating when vote_state grows
|
// golden, may need updating when vote_state grows
|
||||||
assert!(minimum_balance as f64 / 10f64.powf(9.0) < 0.04)
|
assert!(minimum_balance as f64 / 10f64.powf(9.0) < 0.04)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_serde_compact_vote_state_update() {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
for _ in 0..5000 {
|
||||||
|
run_serde_compact_vote_state_update(&mut rng);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::integer_arithmetic)]
|
||||||
|
fn run_serde_compact_vote_state_update<R: Rng>(rng: &mut R) {
|
||||||
|
let lockouts: VecDeque<_> = std::iter::repeat_with(|| Lockout {
|
||||||
|
slot: 149_303_885 + rng.gen_range(0, 10_000),
|
||||||
|
confirmation_count: rng.gen_range(0, 33),
|
||||||
|
})
|
||||||
|
.take(32)
|
||||||
|
.sorted_by_key(|lockout| lockout.slot)
|
||||||
|
.collect();
|
||||||
|
let root = rng
|
||||||
|
.gen_ratio(1, 2)
|
||||||
|
.then(|| lockouts[0].slot - rng.gen_range(0, 1_000));
|
||||||
|
let timestamp = rng.gen_ratio(1, 2).then(|| rng.gen());
|
||||||
|
let hash = Hash::from(rng.gen::<[u8; 32]>());
|
||||||
|
let vote_state_update = VoteStateUpdate {
|
||||||
|
lockouts,
|
||||||
|
root,
|
||||||
|
hash,
|
||||||
|
timestamp,
|
||||||
|
};
|
||||||
|
#[derive(Debug, Eq, PartialEq, Deserialize, Serialize)]
|
||||||
|
enum VoteInstruction {
|
||||||
|
#[serde(with = "serde_compact_vote_state_update")]
|
||||||
|
UpdateVoteState(VoteStateUpdate),
|
||||||
|
UpdateVoteStateSwitch(
|
||||||
|
#[serde(with = "serde_compact_vote_state_update")] VoteStateUpdate,
|
||||||
|
Hash,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
let vote = VoteInstruction::UpdateVoteState(vote_state_update.clone());
|
||||||
|
let bytes = bincode::serialize(&vote).unwrap();
|
||||||
|
assert_eq!(vote, bincode::deserialize(&bytes).unwrap());
|
||||||
|
let hash = Hash::from(rng.gen::<[u8; 32]>());
|
||||||
|
let vote = VoteInstruction::UpdateVoteStateSwitch(vote_state_update, hash);
|
||||||
|
let bytes = bincode::serialize(&vote).unwrap();
|
||||||
|
assert_eq!(vote, bincode::deserialize(&bytes).unwrap());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,10 +135,7 @@ pub fn parse_vote(
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
VoteInstruction::CompactUpdateVoteState(compact_vote_state_update) => {
|
VoteInstruction::CompactUpdateVoteState(vote_state_update) => {
|
||||||
let vote_state_update = compact_vote_state_update.uncompact().map_err(|_| {
|
|
||||||
ParseInstructionError::InstructionNotParsable(ParsableProgram::Vote)
|
|
||||||
})?;
|
|
||||||
check_num_vote_accounts(&instruction.accounts, 2)?;
|
check_num_vote_accounts(&instruction.accounts, 2)?;
|
||||||
let vote_state_update = json!({
|
let vote_state_update = json!({
|
||||||
"lockouts": vote_state_update.lockouts,
|
"lockouts": vote_state_update.lockouts,
|
||||||
|
@ -155,10 +152,7 @@ pub fn parse_vote(
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
VoteInstruction::CompactUpdateVoteStateSwitch(compact_vote_state_update, hash) => {
|
VoteInstruction::CompactUpdateVoteStateSwitch(vote_state_update, hash) => {
|
||||||
let vote_state_update = compact_vote_state_update.uncompact().map_err(|_| {
|
|
||||||
ParseInstructionError::InstructionNotParsable(ParsableProgram::Vote)
|
|
||||||
})?;
|
|
||||||
check_num_vote_accounts(&instruction.accounts, 2)?;
|
check_num_vote_accounts(&instruction.accounts, 2)?;
|
||||||
let vote_state_update = json!({
|
let vote_state_update = json!({
|
||||||
"lockouts": vote_state_update.lockouts,
|
"lockouts": vote_state_update.lockouts,
|
||||||
|
@ -770,7 +764,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_compact_vote_state_update_ix() {
|
fn test_parse_compact_vote_state_update_ix() {
|
||||||
let vote_state_update = VoteStateUpdate::from(vec![(0, 3), (1, 2), (2, 1)]);
|
let vote_state_update = VoteStateUpdate::from(vec![(0, 3), (1, 2), (2, 1)]);
|
||||||
let compact_vote_state_update = vote_state_update.clone().compact().unwrap();
|
let compact_vote_state_update = vote_state_update.clone();
|
||||||
|
|
||||||
let vote_pubkey = Pubkey::new_unique();
|
let vote_pubkey = Pubkey::new_unique();
|
||||||
let authorized_voter_pubkey = Pubkey::new_unique();
|
let authorized_voter_pubkey = Pubkey::new_unique();
|
||||||
|
@ -813,7 +807,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_compact_vote_state_update_switch_ix() {
|
fn test_parse_compact_vote_state_update_switch_ix() {
|
||||||
let vote_state_update = VoteStateUpdate::from(vec![(0, 3), (1, 2), (2, 1)]);
|
let vote_state_update = VoteStateUpdate::from(vec![(0, 3), (1, 2), (2, 1)]);
|
||||||
let compact_vote_state_update = vote_state_update.clone().compact().unwrap();
|
let compact_vote_state_update = vote_state_update.clone();
|
||||||
|
|
||||||
let vote_pubkey = Pubkey::new_unique();
|
let vote_pubkey = Pubkey::new_unique();
|
||||||
let authorized_voter_pubkey = Pubkey::new_unique();
|
let authorized_voter_pubkey = Pubkey::new_unique();
|
||||||
|
|
Loading…
Reference in New Issue