Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wen restart aggregate last voted fork slots #33892

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
47f8891
Push and aggregate RestartLastVotedForkSlots.
wen-coding Oct 25, 2023
a8d0c08
Fix API and lint errors.
wen-coding Oct 26, 2023
f7b8232
Reduce clutter.
wen-coding Oct 26, 2023
f5f71b4
Put my own LastVotedForkSlots into the aggregate.
wen-coding Oct 26, 2023
630cc70
Merge branch 'solana-labs:master' into wen_restart_aggregate_last_vot…
wen-coding Oct 27, 2023
ce32c03
Write LastVotedForkSlots aggregate progress into local file.
wen-coding Oct 27, 2023
b90185d
Fix typo and name constants.
wen-coding Oct 27, 2023
3c819f0
Fix flaky test.
wen-coding Oct 27, 2023
e21efe3
Clarify the comments.
wen-coding Oct 27, 2023
b24b8db
- Use constant for wait_for_supermajority
wen-coding Nov 8, 2023
a2204f3
Fix delay_after_first_shred and remove loop in wen_restart.
wen-coding Nov 9, 2023
0c1ef0f
Read wen_restart slots inside the loop instead.
wen-coding Nov 9, 2023
b9324c8
Merge branch 'master' into wen_restart_aggregate_last_voted_fork_slots
wen-coding Nov 11, 2023
122314d
Discard turbine shreds while in wen_restart in windows insert rather …
wen-coding Nov 12, 2023
e1252a4
Merge branch 'master' into wen_restart_aggregate_last_voted_fork_slots
wen-coding Nov 14, 2023
31ca285
Merge branch 'master' into wen_restart_aggregate_last_voted_fork_slots
wen-coding Nov 15, 2023
c3ab972
Merge branch 'master' into wen_restart_aggregate_last_voted_fork_slots
wen-coding Nov 16, 2023
8fc2327
Use the new Gossip API.
wen-coding Nov 16, 2023
229f447
Rename slots_to_repair_for_wen_restart and a few others.
wen-coding Nov 16, 2023
bc1b4b5
Rename a few more and list all states.
wen-coding Nov 17, 2023
8743b5c
Pipe exit down to aggregate loop so we can exit early.
wen-coding Nov 17, 2023
4ebbde8
Merge branch 'master' into wen_restart_aggregate_last_voted_fork_slots
wen-coding Nov 17, 2023
0d82a7c
Fix import of RestartLastVotedForkSlots.
wen-coding Nov 17, 2023
5e0a5b1
Merge branch 'master' into wen_restart_aggregate_last_voted_fork_slots
wen-coding Nov 17, 2023
ec21ec1
Merge branch 'master' into wen_restart_aggregate_last_voted_fork_slots
wen-coding Dec 12, 2023
c172c26
Use the new method to generate test bank.
wen-coding Dec 12, 2023
08de626
Make linter happy.
wen-coding Jan 4, 2024
ea4d800
Merge branch 'master' into wen_restart_aggregate_last_voted_fork_slots
wen-coding Jan 4, 2024
4f91be7
Use new bank constructor for tests.
wen-coding Jan 4, 2024
de89a4e
Merge branch 'master' into wen_restart_aggregate_last_voted_fork_slots
wen-coding Jan 20, 2024
1e98324
Fix a bad merge.
wen-coding Jan 20, 2024
777523f
Merge branch 'master' into wen_restart_aggregate_last_voted_fork_slots
wen-coding Jan 21, 2024
1e478e8
- add new const for wen_restart
wen-coding Jan 23, 2024
b0980e4
Merge branch 'master' into wen_restart_aggregate_last_voted_fork_slots
wen-coding Jan 23, 2024
f4acd69
Add initialize and put the main logic into a loop.
wen-coding Jan 23, 2024
bb471c1
Merge branch 'wen_restart_aggregate_last_voted_fork_slots' of https:/…
wen-coding Jan 23, 2024
c45a29b
Change aggregate interface and other fixes.
wen-coding Jan 24, 2024
167b790
Add failure tests and tests for state transition.
wen-coding Jan 24, 2024
e0a070f
Add more tests and add ability to recover from written records in
wen-coding Jan 27, 2024
8be5cd0
Merge branch 'master' into wen_restart_aggregate_last_voted_fork_slots
wen-coding Jan 27, 2024
ddd144e
Merge branch 'master' into wen_restart_aggregate_last_voted_fork_slots
wen-coding Jan 29, 2024
1cfc510
Various name changes.
wen-coding Jan 31, 2024
5b10c6e
We don't really care what type of error is returned.
wen-coding Jan 31, 2024
0620aaf
Wait on expected progress message in proto file instead of sleep.
wen-coding Jan 31, 2024
1ceda56
Merge branch 'master' into wen_restart_aggregate_last_voted_fork_slots
wen-coding Jan 31, 2024
93abe45
Code reorganization and cleanup.
wen-coding Feb 5, 2024
cb1788e
Make linter happy.
wen-coding Feb 5, 2024
72a732e
Add WenRestartError.
wen-coding Feb 5, 2024
4c920cb
Split WenRestartErrors into separate erros per state.
wen-coding Feb 7, 2024
bf71c9b
Revert "Split WenRestartErrors into separate erros per state."
wen-coding Feb 9, 2024
056aef7
Merge branch 'master' into wen_restart_aggregate_last_voted_fork_slots
wen-coding Feb 9, 2024
645452f
Use individual functions when testing for failures.
wen-coding Feb 10, 2024
f46e62a
Move initialization errors into initialize().
wen-coding Feb 28, 2024
e3d0194
Use anyhow instead of thiserror to generate backtrace for error.
wen-coding Feb 28, 2024
ffbb20c
Add missing Cargo.lock.
wen-coding Feb 29, 2024
3b50964
Merge branch 'master' into wen_restart_aggregate_last_voted_fork_slots
wen-coding Feb 29, 2024
59fd5ff
Add error log when last_vote is missing in the tower storage.
wen-coding Feb 29, 2024
e7c320c
Merge branch 'master' into wen_restart_aggregate_last_voted_fork_slots
wen-coding Feb 29, 2024
9ccf5ca
Merge branch 'master' into wen_restart_aggregate_last_voted_fork_slots
wen-coding Mar 1, 2024
021dbe9
Change error log info.
wen-coding Mar 1, 2024
40c0fb6
Change test to match exact error.
wen-coding Mar 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions programs/sbf/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions wen-restart/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ solana-program = { workspace = true }
solana-runtime = { workspace = true }
solana-sdk = { workspace = true }
solana-vote-program = { workspace = true }
thiserror = { workspace = true }

[dev-dependencies]
assert_matches = { workspace = true }
Expand Down
4 changes: 2 additions & 2 deletions wen-restart/src/last_voted_fork_slots_aggregate.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use {
crate::solana::wen_restart_proto::LastVotedForkSlotsRecord,
crate::{solana::wen_restart_proto::LastVotedForkSlotsRecord, wen_restart::WenRestartError},
log::*,
solana_gossip::restart_crds_values::RestartLastVotedForkSlots,
solana_runtime::epoch_stakes::EpochStakes,
Expand Down Expand Up @@ -66,7 +66,7 @@ impl LastVotedForkSlotsAggregate {
&mut self,
key_string: &str,
record: &LastVotedForkSlotsRecord,
) -> Result<Option<LastVotedForkSlotsRecord>, Box<dyn std::error::Error>> {
) -> Result<Option<LastVotedForkSlotsRecord>, WenRestartError> {
let from = Pubkey::from_str(key_string)?;
let last_voted_hash = Hash::from_str(&record.last_vote_bankhash)?;
let converted_record = RestartLastVotedForkSlots::new(
Expand Down
169 changes: 101 additions & 68 deletions wen-restart/src/wen_restart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ use {
crate::{
last_voted_fork_slots_aggregate::LastVotedForkSlotsAggregate,
solana::wen_restart_proto::{
LastVotedForkSlotsAggregateRecord, LastVotedForkSlotsRecord, State as RestartState,
WenRestartProgress,
self, LastVotedForkSlotsAggregateRecord, LastVotedForkSlotsRecord,
State as RestartState, WenRestartProgress,
},
},
log::*,
prost::Message,
solana_gossip::{
cluster_info::{ClusterInfo, GOSSIP_SLEEP_MILLIS},
restart_crds_values::RestartLastVotedForkSlots,
restart_crds_values::{RestartLastVotedForkSlots, RestartLastVotedForkSlotsError},
},
solana_ledger::{ancestor_iterator::AncestorIterator, blockstore::Blockstore},
solana_program::{clock::Slot, hash::Hash},
Expand All @@ -22,7 +22,7 @@ use {
std::{
collections::{HashMap, HashSet},
fs::{read, File},
io::{Cursor, Error, Write},
io::{Cursor, Write},
path::PathBuf,
str::FromStr,
sync::{
Expand All @@ -32,17 +32,42 @@ use {
thread::sleep,
time::Duration,
},
thiserror::Error,
};

// If >42% of the validators have this block, repair this block locally.
const REPAIR_THRESHOLD: f64 = 0.42;

#[derive(Error, Debug)]
pub enum WenRestartError {
#[error("Protobuf Decode error: {0}")]
DecodeError(#[from] prost::DecodeError),
#[error("Protobuf Encode error: {0}")]
EncodeError(#[from] prost::EncodeError),
#[error("Exiting")]
Exiting,
#[error("Invalid Last Vote Type: {0:?}")]
InvalidLastVoteType(VoteTransaction),
#[error("File IO error: {0}")]
IoError(#[from] std::io::Error),
#[error("Missing last voted fork slots")]
MissingLastVotedForkSlots,
#[error("Hash parse error: {0}")]
ParseHashError(#[from] solana_sdk::hash::ParseHashError),
#[error("Pubkey parse error: {0}")]
PubkeyParseError(#[from] solana_sdk::pubkey::ParsePubkeyError),
#[error("Gossip push RestartLastVotedForkSlotsError: {0}")]
RestartLastVotedForkSlotsError(#[from] RestartLastVotedForkSlotsError),
#[error("Unexpected state: {0:?}")]
UnexpectedState(wen_restart_proto::State),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to organize these by state transition:

pub enum SendLastVotedForkError {

}

pub enum LastVotedForkSlotsError {

}
  1. Then wait_for_wen_restart() still returns Box<dyn std::error::Error>
  2. For testing for each state transition, we make sure all of their particular errors write properly to the record and we can restart

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, when you say "write properly to the record", do you mean writing to the logs or to the protobuf as well? Since some of the errors might be transient, I didn't plan to write the errors to the protobuf.

Copy link
Contributor

@carllin carllin Feb 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I didn't mean write the error out. I mean the progress data structure you write is what you expect after the error happens.

For every single error, for every single state transition

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did the change but it actually felt wrong IMHO, several reasons:

  1. complicating public API for the sake of testing
    pub enum xxxError is public interface, if you think about how it's used, all people see from outside the module is wait_for_wen_restart, so it feels natural to have WenRestartError returned, it feels unnatural to have several Error enum returned, leaking our implementation details (which they shouldn't care about).
  2. harder to write tests
    I could of course test each function individually, but I do have to test they work in wait_for_wen_restart. If I only need to test is_err() then it's all fine, but if I want to test for specific error (e.g. file permission wrong), I had to do a downcast on dyn error, I had to constantly look back to error definitions to see what type of error I'm now expecting
  3. common functions harder to implement and change
    The fact that we do need to write the protobuf means I need to call write_wen_restart() from several places, but they will return different xxxError types, so I need to create yet another WriteWenRestartError and convert to other Error types, this is code duplication and unnecessary conversion. If we move part of the function to another internal method, we need to create yet another Error struct?

All in all, this feels wrong, maybe I'm misunderstanding what you described.

Copy link
Contributor

@carllin carllin Feb 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pub enum xxxError is public interface, if you think about how it's used, all people see from outside the module is wait_for_wen_restart, so it feels natural to have WenRestartError returned, it feels unnatural to have several Error enum returned, leaking our implementation details (which they shouldn't care about).

Here we're not writing a standalone library so I think a boxed error is fine. The purpose of this error is to make attribution of errors/debugging easier, which categorizing the errors does. We can now trace exactly which step in the restart went wrong. I think we could also introduce a top level WenRestartError that wraps the errors like so:

enum WenRestartError {
   SendLastVotedForkError(SendLastVotedForkError),
   AggregateLastVotedForkSlotsError(AggregateLastVotedForkSlotsError),
  ...
}

I could of course test each function individually, but I do have to test they work in wait_for_wen_restart. If I only need to test is_err() then it's all fine, but if I want to test for specific error (e.g. file permission wrong), I had to do a downcast on dyn error, I had to constantly look back to error definitions to see what type of error I'm now expecting

Hmm I don't think you have to test every error case in wait_for_wen_restart(). I think if the state transition functions are a-> b -> c, then:

  1. You just have to test that each transition a, b, and c individually will run successfully to "completion" across all cases. "Completion" in this case means the ending output passes the verification for the initial input of the next state transition. Concretely this means testing in the happy case the state is correct, and that on all declared errors, they can recover and then run to completion.
  2. From 1, inductively, you can then conclude that a -> b -> c will run correctly.
  3. All you need to test then in wait_for_wen_restart() is that the order of calls a -> b -> c is correct and the arguments are being passed correctly. Concretely this means just testing one happy path.

The fact that we do need to write the protobuf means I need to call write_wen_restart() from several places, but they will return different xxxError types, so I need to create yet another WriteWenRestartError and convert to other Error types, this is code duplication and unnecessary conversion. If we move part of the function to another internal method, we need to create yet another Error struct?

I think a unique error for write_wen_restart is fine, it is just a suberror of each state transition. This way we know which step the write failed in.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about it more, I think I agree that failures should be tested on individual functions rather than wait_on_wen_restart(), so I changed accordingly.

The only thing I'm hesitating is that I still don't see how splitting WenRestartError into individual errors helps us. If the proto file is suddenly not writable, we need to solve that problem no matter what stage WenRestart is in, so I don't think replicating file IO errors everywhere really helps the user, it just complicates the public API.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about this, we guarantee no errors on the state transitions 😄 . I.e. we guarantee the state going in and the state coming out of the records are always valid.

  1. All of the current errors are verification errors on the initial state:
    return Err(WenRestartError::MissingLastVotedForkSlots);
    }
    } else {
    return Err(WenRestartError::InvalidLastVoteType(last_vote));
    }
    and
    https://github.com/solana-labs/solana/pull/33892/files#diff-c32a5c15bdd23820489e6f8af8b305aee344228d0cb6789e554a988d57d54bd7R70-R71. These validations can be done in the beginning on entry into the state machine in wait_for_wen_restart, as a validate_state_1(), validate_state_2(), etc. These can then be unit tested. Ideally I think we would change WenRestartProgress to be an enum with a separate state for each step in this state machine, i.e.
enum WenRestartProgress {
       Init,
       SendForkSlots(SendForkSlots),
       AggregateForkslots(AggregateForkSlots),
}

and then at the end of each state transition we just spit out the next iteration of the enum

  1. Then the only acceptable error is logging errors which we can then enforce is the only acceptable error.
  2. Then if we test that write_wen_restart_records() either writes a whole valid record OR doesn't write a record/i.e. errors, then we know the state must always be valid.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can move the initialization errors into initialize(), but I can't guarantee there are no other errors after initialize(). For example, push_restart_last_voted_fork_slots() could return error. And we might want to throw out error in the future while running.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're right on this 😃

If we don't want a separate enum per state transition, then at minimum I would like to be able to print out the backtrace of where that error occurred. Maybe this: https://stackoverflow.com/questions/75060346/how-can-i-get-file-and-line-number-where-an-error-was-returned

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to anyhow instead of thiserror for backtrace, also moved initialization errors into initialize().

}

fn send_restart_last_voted_fork_slots(
last_vote: VoteTransaction,
blockstore: Arc<Blockstore>,
cluster_info: Arc<ClusterInfo>,
progress: &mut WenRestartProgress,
) -> Result<(), Box<dyn std::error::Error>> {
) -> Result<(), WenRestartError> {
let last_voted_fork_slots;
let last_vote_hash;
match &progress.my_last_voted_fork_slots {
Expand All @@ -60,10 +85,10 @@ fn send_restart_last_voted_fork_slots(
.take(RestartLastVotedForkSlots::MAX_SLOTS)
.collect();
} else {
return Err("last vote does not have last voted slot".into());
return Err(WenRestartError::MissingLastVotedForkSlots);
}
} else {
return Err("last vote is not a vote transaction".into());
return Err(WenRestartError::InvalidLastVoteType(last_vote));
}
}
}
Expand All @@ -89,7 +114,7 @@ fn aggregate_restart_last_voted_fork_slots(
wen_restart_repair_slots: Arc<RwLock<Vec<Slot>>>,
exit: Arc<AtomicBool>,
progress: &mut WenRestartProgress,
) -> Result<(), Box<dyn std::error::Error>> {
) -> Result<(), WenRestartError> {
let root_bank;
{
root_bank = bank_forks.read().unwrap().root_bank().clone();
Expand Down Expand Up @@ -119,7 +144,7 @@ fn aggregate_restart_last_voted_fork_slots(
}
loop {
if exit.load(Ordering::Relaxed) {
return Err("Exiting".into());
return Err(WenRestartError::Exiting);
}
let start = timestamp();
for new_last_voted_fork_slots in cluster_info.get_restart_last_voted_fork_slots(&mut cursor)
Expand Down Expand Up @@ -190,7 +215,7 @@ pub fn wait_for_wen_restart(
wen_restart_repair_slots: Option<Arc<RwLock<Vec<Slot>>>>,
wait_for_supermajority_threshold_percent: u64,
exit: Arc<AtomicBool>,
) -> Result<(), Box<dyn std::error::Error>> {
) -> Result<(), WenRestartError> {
let mut progress = initialize(wen_restart_path)?;
loop {
match progress.state() {
Expand All @@ -212,9 +237,11 @@ pub fn wait_for_wen_restart(
RestartState::HeaviestFork => {
warn!("Not implemented yet, make it empty to complete test")
}
RestartState::FinishedSnapshot => return Err("Not implemented!".into()),
RestartState::GeneratingSnapshot => return Err("Not implemented!".into()),
RestartState::WaitingForSupermajority => return Err("Not implemented!".into()),
RestartState::FinishedSnapshot
| RestartState::GeneratingSnapshot
| RestartState::WaitingForSupermajority => {
return Err(WenRestartError::UnexpectedState(progress.state()))
}
RestartState::Done => return Ok(()),
}
increment_and_write_wen_restart_records(wen_restart_path, &mut progress)?;
Expand All @@ -224,7 +251,7 @@ pub fn wait_for_wen_restart(
pub(crate) fn increment_and_write_wen_restart_records(
records_path: &PathBuf,
new_progress: &mut WenRestartProgress,
) -> Result<(), Box<dyn std::error::Error>> {
) -> Result<(), WenRestartError> {
let new_state = match new_progress.state() {
RestartState::Init => RestartState::LastVotedForkSlots,
RestartState::LastVotedForkSlots => RestartState::HeaviestFork,
Expand All @@ -233,22 +260,18 @@ pub(crate) fn increment_and_write_wen_restart_records(
| RestartState::FinishedSnapshot
| RestartState::GeneratingSnapshot
| RestartState::WaitingForSupermajority => {
error!("Unexpected state {:?}", new_progress.state());
return Err("unexpected state".into());
return Err(WenRestartError::UnexpectedState(new_progress.state()));
}
};
new_progress.set_state(new_state);
write_wen_restart_records(records_path, new_progress)?;
Ok(())
}

pub(crate) fn initialize(records_path: &PathBuf) -> Result<WenRestartProgress, Error> {
pub(crate) fn initialize(records_path: &PathBuf) -> Result<WenRestartProgress, WenRestartError> {
match read_wen_restart_records(records_path) {
Ok(progress) => Ok(progress),
Err(e) => {
if e.kind() != std::io::ErrorKind::NotFound {
return Err(e);
}
Err(WenRestartError::IoError(e)) if e.kind() == std::io::ErrorKind::NotFound => {
info!(
"wen restart proto file not found at {:?}, write init state",
records_path
Expand All @@ -261,10 +284,11 @@ pub(crate) fn initialize(records_path: &PathBuf) -> Result<WenRestartProgress, E
write_wen_restart_records(records_path, &progress)?;
Ok(progress)
}
Err(err) => Err(err),
}
}

fn read_wen_restart_records(records_path: &PathBuf) -> Result<WenRestartProgress, Error> {
fn read_wen_restart_records(records_path: &PathBuf) -> Result<WenRestartProgress, WenRestartError> {
let buffer = read(records_path)?;
let progress = WenRestartProgress::decode(&mut Cursor::new(buffer))?;
info!("read record {:?}", progress);
Expand All @@ -274,7 +298,7 @@ fn read_wen_restart_records(records_path: &PathBuf) -> Result<WenRestartProgress
pub(crate) fn write_wen_restart_records(
records_path: &PathBuf,
new_progress: &WenRestartProgress,
) -> Result<(), Error> {
) -> Result<(), WenRestartError> {
// overwrite anything if exists
let mut file = File::create(records_path)?;
info!("writing new record {:?}", new_progress);
Expand All @@ -288,6 +312,7 @@ pub(crate) fn write_wen_restart_records(
mod tests {
use {
crate::wen_restart::*,
assert_matches::assert_matches,
solana_entry::entry,
solana_gossip::{
cluster_info::ClusterInfo,
Expand Down Expand Up @@ -607,30 +632,32 @@ mod tests {
fn test_wen_restart_init_failures() {
let ledger_path = get_tmp_ledger_path_auto_delete!();
let test_state = wen_restart_test_init(&ledger_path);
for last_vote in [
VoteTransaction::from(VoteStateUpdate::from(vec![(0, 8), (1, 1)])),
VoteTransaction::from(Vote::new(vec![], Hash::new_unique())),
] {
let progress = WenRestartProgress {
state: RestartState::Init.into(),
my_last_voted_fork_slots: None,
last_voted_fork_slots_aggregate: None,
};
assert!(
write_wen_restart_records(&test_state.wen_restart_proto_path, &progress).is_ok()
);
assert!(wait_for_wen_restart(
assert_matches!(
wait_for_wen_restart(
&test_state.wen_restart_proto_path,
last_vote,
VoteTransaction::from(VoteStateUpdate::from(vec![(0, 8), (1, 1)])),
test_state.blockstore.clone(),
test_state.cluster_info.clone(),
test_state.bank_forks.clone(),
Some(Arc::new(RwLock::new(Vec::new()))),
80,
Arc::new(AtomicBool::new(false)),
)
.is_err());
}
),
Err(WenRestartError::InvalidLastVoteType(_))
);
assert_matches!(
wait_for_wen_restart(
&test_state.wen_restart_proto_path,
VoteTransaction::from(Vote::new(vec![], Hash::new_unique())),
test_state.blockstore.clone(),
test_state.cluster_info.clone(),
test_state.bank_forks.clone(),
Some(Arc::new(RwLock::new(Vec::new()))),
80,
Arc::new(AtomicBool::new(false)),
),
Err(WenRestartError::MissingLastVotedForkSlots)
);
let last_vote_bankhash = Hash::new_unique();
let last_voted_fork_slots = test_state.last_voted_fork_slots.clone();
wen_restart_test_succeed_after_failure(
Expand Down Expand Up @@ -667,7 +694,7 @@ mod tests {
let last_vote_slot: Slot = test_state.last_voted_fork_slots[0];
let last_vote_bankhash = Hash::new_unique();
change_proto_file_readonly(&test_state.wen_restart_proto_path, true);
assert!(wait_for_wen_restart(
assert_matches!(wait_for_wen_restart(
&test_state.wen_restart_proto_path,
VoteTransaction::from(Vote::new(vec![last_vote_slot], last_vote_bankhash)),
test_state.blockstore.clone(),
Expand All @@ -676,8 +703,7 @@ mod tests {
Some(Arc::new(RwLock::new(Vec::new()))),
80,
Arc::new(AtomicBool::new(false)),
)
.is_err());
), Err(WenRestartError::IoError(e)) if e.kind() == std::io::ErrorKind::PermissionDenied);
change_proto_file_readonly(&test_state.wen_restart_proto_path, false);
let last_voted_fork_slots = test_state.last_voted_fork_slots.clone();
wen_restart_test_succeed_after_failure(
Expand Down Expand Up @@ -738,17 +764,22 @@ mod tests {
let wen_restart_thread_handle = Builder::new()
.name("solana-wen-restart".to_string())
.spawn(move || {
assert!(wait_for_wen_restart(
&wen_restart_proto_path_clone,
VoteTransaction::from(Vote::new(vec![last_vote_slot], last_vote_bankhash)),
blockstore_clone,
cluster_info_clone,
bank_forks_clone,
Some(Arc::new(RwLock::new(Vec::new()))),
80,
exit_clone,
)
.is_err());
assert_matches!(
wait_for_wen_restart(
&wen_restart_proto_path_clone,
VoteTransaction::from(Vote::new(
vec![last_vote_slot],
last_vote_bankhash
)),
blockstore_clone,
cluster_info_clone,
bank_forks_clone,
Some(Arc::new(RwLock::new(Vec::new()))),
80,
exit_clone,
),
Err(WenRestartError::Exiting)
);
})
.unwrap();
let node_pubkey = keypairs.node_keypair.pubkey();
Expand Down Expand Up @@ -804,17 +835,19 @@ mod tests {
slots_record.last_vote_bankhash += "1";
}
assert!(write_wen_restart_records(&test_state.wen_restart_proto_path, &record).is_ok());
assert!(wait_for_wen_restart(
&test_state.wen_restart_proto_path,
VoteTransaction::from(Vote::new(vec![last_vote_slot], last_vote_bankhash)),
test_state.blockstore.clone(),
test_state.cluster_info.clone(),
test_state.bank_forks.clone(),
Some(Arc::new(RwLock::new(Vec::new()))),
80,
Arc::new(AtomicBool::new(false)),
)
.is_err());
assert_matches!(
wait_for_wen_restart(
&test_state.wen_restart_proto_path,
VoteTransaction::from(Vote::new(vec![last_vote_slot], last_vote_bankhash)),
test_state.blockstore.clone(),
test_state.cluster_info.clone(),
test_state.bank_forks.clone(),
Some(Arc::new(RwLock::new(Vec::new()))),
80,
Arc::new(AtomicBool::new(false)),
),
Err(WenRestartError::ParseHashError(_))
);
for slots_record in record
.last_voted_fork_slots_aggregate
.as_mut()
Expand Down Expand Up @@ -875,9 +908,9 @@ mod tests {
.is_ok());
assert_eq!(progress.state(), expected_state);
}
assert!(
increment_and_write_wen_restart_records(&wen_restart_proto_path, &mut progress)
.is_err()
assert_matches!(
increment_and_write_wen_restart_records(&wen_restart_proto_path, &mut progress),
Err(WenRestartError::UnexpectedState(_))
);
}
}
Loading