Compact vote state updates to reduce block size (#26616)
* Compact vote state updates to reduce block size * Add rpc transaction tests
This commit is contained in:
parent
1a5b830294
commit
8d69e8d447
|
@ -87,11 +87,22 @@ impl SwitchForkDecision {
|
|||
v,
|
||||
*switch_proof_hash,
|
||||
)),
|
||||
(SwitchForkDecision::SameFork, VoteTransaction::CompactVoteStateUpdate(_v)) => None,
|
||||
(SwitchForkDecision::SameFork, VoteTransaction::CompactVoteStateUpdate(v)) => {
|
||||
Some(vote_instruction::compact_update_vote_state(
|
||||
vote_account_pubkey,
|
||||
authorized_voter_pubkey,
|
||||
v,
|
||||
))
|
||||
}
|
||||
(
|
||||
SwitchForkDecision::SwitchProof(_switch_proof_hash),
|
||||
VoteTransaction::CompactVoteStateUpdate(_v),
|
||||
) => None,
|
||||
SwitchForkDecision::SwitchProof(switch_proof_hash),
|
||||
VoteTransaction::CompactVoteStateUpdate(v),
|
||||
) => Some(vote_instruction::compact_update_vote_state_switch(
|
||||
vote_account_pubkey,
|
||||
authorized_voter_pubkey,
|
||||
v,
|
||||
*switch_proof_hash,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ use {
|
|||
},
|
||||
solana_sdk::{
|
||||
clock::{BankId, Slot, MAX_PROCESSING_AGE, NUM_CONSECUTIVE_LEADER_SLOTS},
|
||||
feature_set,
|
||||
genesis_config::ClusterType,
|
||||
hash::Hash,
|
||||
pubkey::Pubkey,
|
||||
|
@ -64,7 +65,7 @@ use {
|
|||
timing::timestamp,
|
||||
transaction::Transaction,
|
||||
},
|
||||
solana_vote_program::vote_state::VoteTransaction,
|
||||
solana_vote_program::vote_state::{CompactVoteStateUpdate, VoteTransaction},
|
||||
std::{
|
||||
collections::{HashMap, HashSet},
|
||||
result,
|
||||
|
@ -1980,6 +1981,16 @@ impl ReplayStage {
|
|||
};
|
||||
|
||||
// Send our last few votes along with the new one
|
||||
// Compact the vote state update before sending
|
||||
let should_compact = bank
|
||||
.feature_set
|
||||
.is_active(&feature_set::compact_vote_state_updates::id());
|
||||
let vote = match (should_compact, vote) {
|
||||
(true, VoteTransaction::VoteStateUpdate(vote_state_update)) => {
|
||||
VoteTransaction::from(CompactVoteStateUpdate::from(vote_state_update))
|
||||
}
|
||||
(_, vote) => vote,
|
||||
};
|
||||
let vote_ix = switch_fork_decision
|
||||
.to_vote_instruction(
|
||||
vote,
|
||||
|
|
|
@ -4,8 +4,8 @@ use {
|
|||
crate::{
|
||||
id,
|
||||
vote_state::{
|
||||
Vote, VoteAuthorize, VoteAuthorizeCheckedWithSeedArgs, VoteAuthorizeWithSeedArgs,
|
||||
VoteInit, VoteState, VoteStateUpdate,
|
||||
CompactVoteStateUpdate, Vote, VoteAuthorize, VoteAuthorizeCheckedWithSeedArgs,
|
||||
VoteAuthorizeWithSeedArgs, VoteInit, VoteState, VoteStateUpdate,
|
||||
},
|
||||
},
|
||||
serde_derive::{Deserialize, Serialize},
|
||||
|
@ -103,6 +103,20 @@ pub enum VoteInstruction {
|
|||
/// 1. `[SIGNER]` Vote authority
|
||||
UpdateVoteStateSwitch(VoteStateUpdate, Hash),
|
||||
|
||||
/// Update the onchain vote state for the signer.
|
||||
///
|
||||
/// # Account references
|
||||
/// 0. `[Write]` Vote account to vote with
|
||||
/// 1. `[SIGNER]` Vote authority
|
||||
CompactUpdateVoteState(CompactVoteStateUpdate),
|
||||
|
||||
/// Update the onchain vote state for the signer along with a switching proof.
|
||||
///
|
||||
/// # Account references
|
||||
/// 0. `[Write]` Vote account to vote with
|
||||
/// 1. `[SIGNER]` Vote authority
|
||||
CompactUpdateVoteStateSwitch(CompactVoteStateUpdate, Hash),
|
||||
|
||||
/// Given that the current Voter or Withdrawer authority is a derived key,
|
||||
/// this instruction allows someone who can sign for that derived key's
|
||||
/// base key to authorize a new Voter or Withdrawer for a vote account.
|
||||
|
@ -370,6 +384,41 @@ pub fn update_vote_state_switch(
|
|||
)
|
||||
}
|
||||
|
||||
pub fn compact_update_vote_state(
|
||||
vote_pubkey: &Pubkey,
|
||||
authorized_voter_pubkey: &Pubkey,
|
||||
compact_vote_state_update: CompactVoteStateUpdate,
|
||||
) -> Instruction {
|
||||
let account_metas = vec![
|
||||
AccountMeta::new(*vote_pubkey, false),
|
||||
AccountMeta::new_readonly(*authorized_voter_pubkey, true),
|
||||
];
|
||||
|
||||
Instruction::new_with_bincode(
|
||||
id(),
|
||||
&VoteInstruction::CompactUpdateVoteState(compact_vote_state_update),
|
||||
account_metas,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn compact_update_vote_state_switch(
|
||||
vote_pubkey: &Pubkey,
|
||||
authorized_voter_pubkey: &Pubkey,
|
||||
vote_state_update: CompactVoteStateUpdate,
|
||||
proof_hash: Hash,
|
||||
) -> Instruction {
|
||||
let account_metas = vec![
|
||||
AccountMeta::new(*vote_pubkey, false),
|
||||
AccountMeta::new_readonly(*authorized_voter_pubkey, true),
|
||||
];
|
||||
|
||||
Instruction::new_with_bincode(
|
||||
id(),
|
||||
&VoteInstruction::CompactUpdateVoteStateSwitch(vote_state_update, proof_hash),
|
||||
account_metas,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn withdraw(
|
||||
vote_pubkey: &Pubkey,
|
||||
authorized_withdrawer_pubkey: &Pubkey,
|
||||
|
|
|
@ -4,7 +4,7 @@ use {
|
|||
crate::{
|
||||
id,
|
||||
vote_instruction::VoteInstruction,
|
||||
vote_state::{self, VoteAuthorize},
|
||||
vote_state::{self, VoteAuthorize, VoteStateUpdate},
|
||||
},
|
||||
log::*,
|
||||
solana_program_runtime::{
|
||||
|
@ -173,6 +173,31 @@ pub fn process_instruction(
|
|||
Err(InstructionError::InvalidInstructionData)
|
||||
}
|
||||
}
|
||||
VoteInstruction::CompactUpdateVoteState(compact_vote_state_update)
|
||||
| VoteInstruction::CompactUpdateVoteStateSwitch(compact_vote_state_update, _) => {
|
||||
if invoke_context
|
||||
.feature_set
|
||||
.is_active(&feature_set::allow_votes_to_directly_update_vote_state::id())
|
||||
&& invoke_context
|
||||
.feature_set
|
||||
.is_active(&feature_set::compact_vote_state_updates::id())
|
||||
{
|
||||
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_vote_state_update(
|
||||
&mut me,
|
||||
slot_hashes.slot_hashes(),
|
||||
&clock,
|
||||
VoteStateUpdate::from(compact_vote_state_update),
|
||||
&signers,
|
||||
&invoke_context.feature_set,
|
||||
)
|
||||
} else {
|
||||
Err(InstructionError::InvalidInstructionData)
|
||||
}
|
||||
}
|
||||
|
||||
VoteInstruction::Withdraw(lamports) => {
|
||||
instruction_context.check_number_of_instruction_accounts(2)?;
|
||||
let rent_sysvar = invoke_context.get_sysvar_cache().get_rent()?;
|
||||
|
|
|
@ -7,7 +7,7 @@ use {
|
|||
signature::Signature,
|
||||
transaction::{SanitizedTransaction, Transaction},
|
||||
},
|
||||
solana_vote_program::vote_instruction::VoteInstruction,
|
||||
solana_vote_program::{vote_instruction::VoteInstruction, vote_state::VoteStateUpdate},
|
||||
};
|
||||
|
||||
pub type ParsedVote = (Pubkey, VoteTransaction, Option<Hash>, Signature);
|
||||
|
@ -29,6 +29,8 @@ pub(crate) fn is_simple_vote_transaction(transaction: &SanitizedTransaction) ->
|
|||
| VoteInstruction::VoteSwitch(_, _)
|
||||
| VoteInstruction::UpdateVoteState(_)
|
||||
| VoteInstruction::UpdateVoteStateSwitch(_, _)
|
||||
| VoteInstruction::CompactUpdateVoteState(_)
|
||||
| VoteInstruction::CompactUpdateVoteStateSwitch(..)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -80,6 +82,14 @@ fn parse_vote_instruction_data(
|
|||
VoteInstruction::UpdateVoteStateSwitch(vote_state_update, hash) => {
|
||||
Some((VoteTransaction::from(vote_state_update), Some(hash)))
|
||||
}
|
||||
VoteInstruction::CompactUpdateVoteState(compact_vote_state_update) => Some((
|
||||
VoteTransaction::from(VoteStateUpdate::from(compact_vote_state_update)),
|
||||
None,
|
||||
)),
|
||||
VoteInstruction::CompactUpdateVoteStateSwitch(compact_vote_state_update, hash) => Some((
|
||||
VoteTransaction::from(VoteStateUpdate::from(compact_vote_state_update)),
|
||||
Some(hash),
|
||||
)),
|
||||
VoteInstruction::Authorize(_, _)
|
||||
| VoteInstruction::AuthorizeChecked(_)
|
||||
| VoteInstruction::AuthorizeWithSeed(_)
|
||||
|
|
|
@ -476,6 +476,10 @@ pub mod use_default_units_in_fee_calculation {
|
|||
solana_sdk::declare_id!("8sKQrMQoUHtQSUP83SPG4ta2JDjSAiWs7t5aJ9uEd6To");
|
||||
}
|
||||
|
||||
pub mod compact_vote_state_updates {
|
||||
solana_sdk::declare_id!("86HpNqzutEZwLcPxS6EHDcMNYWk6ikhteg9un7Y2PBKE");
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// Map of feature identifiers to user-visible description
|
||||
pub static ref FEATURE_NAMES: HashMap<Pubkey, &'static str> = [
|
||||
|
@ -589,6 +593,7 @@ lazy_static! {
|
|||
(cap_bpf_program_instruction_accounts::id(), "enforce max number of accounts per bpf program instruction #26628"),
|
||||
(loosen_cpi_size_restriction::id(), "loosen cpi size restrictions #26641"),
|
||||
(use_default_units_in_fee_calculation::id(), "use default units per instruction in fee calculation #26785"),
|
||||
(compact_vote_state_updates::id(), "Compact vote state updates to lower block size"),
|
||||
/*************** ADD NEW FEATURES HERE ***************/
|
||||
]
|
||||
.iter()
|
||||
|
|
|
@ -5,7 +5,7 @@ use {
|
|||
bincode::deserialize,
|
||||
serde_json::json,
|
||||
solana_sdk::{instruction::CompiledInstruction, message::AccountKeys},
|
||||
solana_vote_program::vote_instruction::VoteInstruction,
|
||||
solana_vote_program::{vote_instruction::VoteInstruction, vote_state::VoteStateUpdate},
|
||||
};
|
||||
|
||||
pub fn parse_vote(
|
||||
|
@ -101,7 +101,7 @@ pub fn parse_vote(
|
|||
})
|
||||
}
|
||||
VoteInstruction::UpdateVoteState(vote_state_update) => {
|
||||
check_num_vote_accounts(&instruction.accounts, 4)?;
|
||||
check_num_vote_accounts(&instruction.accounts, 2)?;
|
||||
let vote_state_update = json!({
|
||||
"lockouts": vote_state_update.lockouts,
|
||||
"root": vote_state_update.root,
|
||||
|
@ -112,15 +112,13 @@ pub fn parse_vote(
|
|||
instruction_type: "updatevotestate".to_string(),
|
||||
info: json!({
|
||||
"voteAccount": account_keys[instruction.accounts[0] as usize].to_string(),
|
||||
"slotHashesSysvar": account_keys[instruction.accounts[1] as usize].to_string(),
|
||||
"clockSysvar": account_keys[instruction.accounts[2] as usize].to_string(),
|
||||
"voteAuthority": account_keys[instruction.accounts[3] as usize].to_string(),
|
||||
"voteAuthority": account_keys[instruction.accounts[1] as usize].to_string(),
|
||||
"voteStateUpdate": vote_state_update,
|
||||
}),
|
||||
})
|
||||
}
|
||||
VoteInstruction::UpdateVoteStateSwitch(vote_state_update, hash) => {
|
||||
check_num_vote_accounts(&instruction.accounts, 4)?;
|
||||
check_num_vote_accounts(&instruction.accounts, 2)?;
|
||||
let vote_state_update = json!({
|
||||
"lockouts": vote_state_update.lockouts,
|
||||
"root": vote_state_update.root,
|
||||
|
@ -131,9 +129,44 @@ pub fn parse_vote(
|
|||
instruction_type: "updatevotestateswitch".to_string(),
|
||||
info: json!({
|
||||
"voteAccount": account_keys[instruction.accounts[0] as usize].to_string(),
|
||||
"slotHashesSysvar": account_keys[instruction.accounts[1] as usize].to_string(),
|
||||
"clockSysvar": account_keys[instruction.accounts[2] as usize].to_string(),
|
||||
"voteAuthority": account_keys[instruction.accounts[3] as usize].to_string(),
|
||||
"voteAuthority": account_keys[instruction.accounts[1] as usize].to_string(),
|
||||
"voteStateUpdate": vote_state_update,
|
||||
"hash": hash.to_string(),
|
||||
}),
|
||||
})
|
||||
}
|
||||
VoteInstruction::CompactUpdateVoteState(compact_vote_state_update) => {
|
||||
let vote_state_update = VoteStateUpdate::from(compact_vote_state_update);
|
||||
check_num_vote_accounts(&instruction.accounts, 2)?;
|
||||
let vote_state_update = json!({
|
||||
"lockouts": vote_state_update.lockouts,
|
||||
"root": vote_state_update.root,
|
||||
"hash": vote_state_update.hash.to_string(),
|
||||
"timestamp": vote_state_update.timestamp,
|
||||
});
|
||||
Ok(ParsedInstructionEnum {
|
||||
instruction_type: "compactupdatevotestate".to_string(),
|
||||
info: json!({
|
||||
"voteAccount": account_keys[instruction.accounts[0] as usize].to_string(),
|
||||
"voteAuthority": account_keys[instruction.accounts[1] as usize].to_string(),
|
||||
"voteStateUpdate": vote_state_update,
|
||||
}),
|
||||
})
|
||||
}
|
||||
VoteInstruction::CompactUpdateVoteStateSwitch(compact_vote_state_update, hash) => {
|
||||
let vote_state_update = VoteStateUpdate::from(compact_vote_state_update);
|
||||
check_num_vote_accounts(&instruction.accounts, 2)?;
|
||||
let vote_state_update = json!({
|
||||
"lockouts": vote_state_update.lockouts,
|
||||
"root": vote_state_update.root,
|
||||
"hash": vote_state_update.hash.to_string(),
|
||||
"timestamp": vote_state_update.timestamp,
|
||||
});
|
||||
Ok(ParsedInstructionEnum {
|
||||
instruction_type: "compactupdatevotestateswitch".to_string(),
|
||||
info: json!({
|
||||
"voteAccount": account_keys[instruction.accounts[0] as usize].to_string(),
|
||||
"voteAuthority": account_keys[instruction.accounts[1] as usize].to_string(),
|
||||
"voteStateUpdate": vote_state_update,
|
||||
"hash": hash.to_string(),
|
||||
}),
|
||||
|
@ -219,7 +252,7 @@ mod test {
|
|||
solana_sdk::{hash::Hash, message::Message, pubkey::Pubkey, sysvar},
|
||||
solana_vote_program::{
|
||||
vote_instruction,
|
||||
vote_state::{Vote, VoteAuthorize, VoteInit},
|
||||
vote_state::{CompactVoteStateUpdate, Vote, VoteAuthorize, VoteInit},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -642,4 +675,180 @@ mod test {
|
|||
message.instructions[0].accounts.pop();
|
||||
assert!(parse_vote(&message.instructions[0], &AccountKeys::new(&keys, None)).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_vote_state_update_ix() {
|
||||
let vote_state_update = VoteStateUpdate::from(vec![(0, 3), (1, 2), (2, 1)]);
|
||||
|
||||
let vote_pubkey = Pubkey::new_unique();
|
||||
let authorized_voter_pubkey = Pubkey::new_unique();
|
||||
let instruction = vote_instruction::update_vote_state(
|
||||
&vote_pubkey,
|
||||
&authorized_voter_pubkey,
|
||||
vote_state_update.clone(),
|
||||
);
|
||||
let mut message = Message::new(&[instruction], None);
|
||||
assert_eq!(
|
||||
parse_vote(
|
||||
&message.instructions[0],
|
||||
&AccountKeys::new(&message.account_keys, None)
|
||||
)
|
||||
.unwrap(),
|
||||
ParsedInstructionEnum {
|
||||
instruction_type: "updatevotestate".to_string(),
|
||||
info: json!({
|
||||
"voteAccount": vote_pubkey.to_string(),
|
||||
"voteAuthority": authorized_voter_pubkey.to_string(),
|
||||
"voteStateUpdate": {
|
||||
"lockouts": vote_state_update.lockouts,
|
||||
"root": None::<u64>,
|
||||
"hash": Hash::default().to_string(),
|
||||
"timestamp": None::<u64>,
|
||||
},
|
||||
}),
|
||||
}
|
||||
);
|
||||
assert!(parse_vote(
|
||||
&message.instructions[0],
|
||||
&AccountKeys::new(&message.account_keys[0..1], None)
|
||||
)
|
||||
.is_err());
|
||||
let keys = message.account_keys.clone();
|
||||
message.instructions[0].accounts.pop();
|
||||
assert!(parse_vote(&message.instructions[0], &AccountKeys::new(&keys, None)).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_vote_state_update_switch_ix() {
|
||||
let vote_state_update = VoteStateUpdate::from(vec![(0, 3), (1, 2), (2, 1)]);
|
||||
|
||||
let vote_pubkey = Pubkey::new_unique();
|
||||
let authorized_voter_pubkey = Pubkey::new_unique();
|
||||
let proof_hash = Hash::new_from_array([2; 32]);
|
||||
let instruction = vote_instruction::update_vote_state_switch(
|
||||
&vote_pubkey,
|
||||
&authorized_voter_pubkey,
|
||||
vote_state_update.clone(),
|
||||
proof_hash,
|
||||
);
|
||||
let mut message = Message::new(&[instruction], None);
|
||||
assert_eq!(
|
||||
parse_vote(
|
||||
&message.instructions[0],
|
||||
&AccountKeys::new(&message.account_keys, None)
|
||||
)
|
||||
.unwrap(),
|
||||
ParsedInstructionEnum {
|
||||
instruction_type: "updatevotestateswitch".to_string(),
|
||||
info: json!({
|
||||
"voteAccount": vote_pubkey.to_string(),
|
||||
"voteAuthority": authorized_voter_pubkey.to_string(),
|
||||
"voteStateUpdate": {
|
||||
"lockouts": vote_state_update.lockouts,
|
||||
"root": None::<u64>,
|
||||
"hash": Hash::default().to_string(),
|
||||
"timestamp": None::<u64>,
|
||||
},
|
||||
"hash": proof_hash.to_string(),
|
||||
}),
|
||||
}
|
||||
);
|
||||
assert!(parse_vote(
|
||||
&message.instructions[0],
|
||||
&AccountKeys::new(&message.account_keys[0..1], None)
|
||||
)
|
||||
.is_err());
|
||||
let keys = message.account_keys.clone();
|
||||
message.instructions[0].accounts.pop();
|
||||
assert!(parse_vote(&message.instructions[0], &AccountKeys::new(&keys, None)).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_compact_vote_state_update_ix() {
|
||||
let vote_state_update = VoteStateUpdate::from(vec![(0, 3), (1, 2), (2, 1)]);
|
||||
let compact_vote_state_update = CompactVoteStateUpdate::from(vote_state_update.clone());
|
||||
|
||||
let vote_pubkey = Pubkey::new_unique();
|
||||
let authorized_voter_pubkey = Pubkey::new_unique();
|
||||
let instruction = vote_instruction::compact_update_vote_state(
|
||||
&vote_pubkey,
|
||||
&authorized_voter_pubkey,
|
||||
compact_vote_state_update,
|
||||
);
|
||||
let mut message = Message::new(&[instruction], None);
|
||||
assert_eq!(
|
||||
parse_vote(
|
||||
&message.instructions[0],
|
||||
&AccountKeys::new(&message.account_keys, None)
|
||||
)
|
||||
.unwrap(),
|
||||
ParsedInstructionEnum {
|
||||
instruction_type: "compactupdatevotestate".to_string(),
|
||||
info: json!({
|
||||
"voteAccount": vote_pubkey.to_string(),
|
||||
"voteAuthority": authorized_voter_pubkey.to_string(),
|
||||
"voteStateUpdate": {
|
||||
"lockouts": vote_state_update.lockouts,
|
||||
"root": None::<u64>,
|
||||
"hash": Hash::default().to_string(),
|
||||
"timestamp": None::<u64>,
|
||||
},
|
||||
}),
|
||||
}
|
||||
);
|
||||
assert!(parse_vote(
|
||||
&message.instructions[0],
|
||||
&AccountKeys::new(&message.account_keys[0..1], None)
|
||||
)
|
||||
.is_err());
|
||||
let keys = message.account_keys.clone();
|
||||
message.instructions[0].accounts.pop();
|
||||
assert!(parse_vote(&message.instructions[0], &AccountKeys::new(&keys, None)).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_compact_vote_state_update_switch_ix() {
|
||||
let vote_state_update = VoteStateUpdate::from(vec![(0, 3), (1, 2), (2, 1)]);
|
||||
let compact_vote_state_update = CompactVoteStateUpdate::from(vote_state_update.clone());
|
||||
|
||||
let vote_pubkey = Pubkey::new_unique();
|
||||
let authorized_voter_pubkey = Pubkey::new_unique();
|
||||
let proof_hash = Hash::new_from_array([2; 32]);
|
||||
let instruction = vote_instruction::compact_update_vote_state_switch(
|
||||
&vote_pubkey,
|
||||
&authorized_voter_pubkey,
|
||||
compact_vote_state_update,
|
||||
proof_hash,
|
||||
);
|
||||
let mut message = Message::new(&[instruction], None);
|
||||
assert_eq!(
|
||||
parse_vote(
|
||||
&message.instructions[0],
|
||||
&AccountKeys::new(&message.account_keys, None)
|
||||
)
|
||||
.unwrap(),
|
||||
ParsedInstructionEnum {
|
||||
instruction_type: "compactupdatevotestateswitch".to_string(),
|
||||
info: json!({
|
||||
"voteAccount": vote_pubkey.to_string(),
|
||||
"voteAuthority": authorized_voter_pubkey.to_string(),
|
||||
"voteStateUpdate": {
|
||||
"lockouts": vote_state_update.lockouts,
|
||||
"root": None::<u64>,
|
||||
"hash": Hash::default().to_string(),
|
||||
"timestamp": None::<u64>,
|
||||
},
|
||||
"hash": proof_hash.to_string(),
|
||||
}),
|
||||
}
|
||||
);
|
||||
assert!(parse_vote(
|
||||
&message.instructions[0],
|
||||
&AccountKeys::new(&message.account_keys[0..1], None)
|
||||
)
|
||||
.is_err());
|
||||
let keys = message.account_keys.clone();
|
||||
message.instructions[0].accounts.pop();
|
||||
assert!(parse_vote(&message.instructions[0], &AccountKeys::new(&keys, None)).is_err());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue