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

fix: miner: move partitions with expired/terminated sectors #1455

Merged
merged 2 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 deletions actors/miner/src/deadline_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,16 +145,16 @@ impl Deadlines {

let dest_partition_idx = first_dest_partition_idx + i as u64;

// sector_count is both total sector count and total live sector count, since no sector is faulty here.
let sector_count = moving_partition.sectors.len();
let live_sector_count = sector_count - moving_partition.terminated.len();

// start updating orig/dest `Deadline` here

orig_deadline.total_sectors -= sector_count;
orig_deadline.live_sectors -= sector_count;
orig_deadline.live_sectors -= live_sector_count;

dest_deadline.total_sectors += sector_count;
dest_deadline.live_sectors += sector_count;
dest_deadline.live_sectors += live_sector_count;

orig_partitions.set(orig_partition_idx, Partition::new(store)?)?;
dest_partitions.set(dest_partition_idx, moving_partition)?;
Expand Down
118 changes: 115 additions & 3 deletions actors/miner/tests/move_partitions_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use fil_actors_runtime::{
};
use fvm_ipld_bitfield::BitField;
use fvm_shared::econ::TokenAmount;
use fvm_shared::sector::RegisteredSealProof;
use fvm_shared::{clock::ChainEpoch, error::ExitCode};
use num_traits::Zero;

Expand All @@ -23,11 +24,13 @@ use util::*;
const PERIOD_OFFSET: ChainEpoch = 100;

fn setup() -> (ActorHarness, MockRuntime) {
let h = ActorHarness::new(PERIOD_OFFSET);
let mut h = ActorHarness::new(PERIOD_OFFSET);
// For a small partition size, necessary for `dispute_remaining_partition_after_move`
h.set_proof_type(RegisteredSealProof::StackedDRG2KiBV1P1);
let rt = h.new_runtime();
h.construct_and_verify(&rt);
rt.balance.replace(BIG_BALANCE.clone());

rt.balance.replace(BIG_BALANCE.clone());
h.construct_and_verify(&rt);
(h, rt)
}

Expand Down Expand Up @@ -631,3 +634,112 @@ fn dispute_after_move() {
h.dispute_window_post(&rt, &dest_deadline, 0, &sectors_info, Some(expected_result));
}
}

#[test]
fn dispute_remaining_partition_after_move() {
let (mut h, rt) = setup();

// Commit more sectors than fit in one partition in every eligible deadline, overflowing to a second partition.
let sectors_to_commit = (rt.policy.wpost_period_deadlines - 2) * h.partition_size + 1;
let sectors_info = h.commit_and_prove_sectors(
&rt,
sectors_to_commit as usize,
DEFAULT_SECTOR_EXPIRATION,
vec![],
true,
);
h.advance_and_submit_posts(&rt, &sectors_info);

let last_sector = sectors_info.last().unwrap();
let st = h.get_state(&rt);

let (orig_deadline_id, partition_id) =
st.find_sector(&rt.store, last_sector.sector_number).unwrap();
assert_eq!(partition_id, 1);

// move a partition from a deadline that still needs WindowPoST verification.

// at this moment, the current and next deadlines are empty, orig_deadline_id has 2 partitions, and all other deadlines have 1 partition.
let target_sectors = &[
&sectors_info[0..h.partition_size as usize], /* these belong to partition 0 */
vec![last_sector.clone()].as_slice(), /* these belong to partition 1 */
]
.concat();
// after this call, current epoch will automatically be advanced to `nearest_unsafe_epoch(&rt, &h, orig_deadline_id)`
h.advance_and_submit_posts(&rt, target_sectors);

let dest_deadline_id =
farthest_possible_to_deadline(&rt, orig_deadline_id, h.current_deadline(&rt));

// move the second partition
let result = h.move_partitions(
&rt,
orig_deadline_id,
dest_deadline_id,
bitfield_from_slice(&[partition_id]),
target_sectors,
);
assert!(result.is_ok());

let st = h.get_state(&rt);
let (dl_idx, _) = st.find_sector(&rt.store, last_sector.sector_number).unwrap();
assert!(dl_idx == dest_deadline_id);

h.check_state(&rt);

// Dispute the first partition in the original deadline
{
let orig_deadline =
nearest_occured_deadline_info(rt.policy(), &h.current_deadline(&rt), orig_deadline_id);

// Check that a failed dispute is ok. A successful dispute is impossible because the Window PoST was synchronously validated when the partition was moved.
h.dispute_window_post(&rt, &orig_deadline, 0, target_sectors, None);
}
}

#[test]
fn move_partition_with_terminated_sector() {
let (mut h, rt) = setup();

// create 2 sectors in partition 0
let sectors_info = h.commit_and_prove_sectors(
&rt,
2,
DEFAULT_SECTOR_EXPIRATION,
vec![vec![10], vec![20]],
true,
);
h.advance_and_submit_posts(&rt, &sectors_info);

// terminate 1 sector
{
// A miner will pay the minimum of termination fee and locked funds. Add some locked funds to ensure
// correct fee calculation is used.
h.apply_rewards(&rt, BIG_REWARDS.clone(), TokenAmount::zero());

let expected_fee = calc_expected_fee_for_termination(&h, &rt, sectors_info[1].clone());

let sectors = bitfield_from_slice(&[sectors_info[1].sector_number]);
h.terminate_sectors(&rt, &sectors, expected_fee);
}

let st = h.get_state(&rt);
let (orig_deadline_id, partition_id) =
st.find_sector(&rt.store, sectors_info[0].sector_number).unwrap();

h.advance_to_epoch_with_cron(&rt, nearest_safe_epoch(&rt, &h, orig_deadline_id));

let dest_deadline_id =
farthest_possible_to_deadline(&rt, orig_deadline_id, h.current_deadline(&rt));

let result = h.move_partitions(
&rt,
orig_deadline_id,
dest_deadline_id,
bitfield_from_slice(&[partition_id]),
&[],
);
assert!(result.is_ok());

h.check_state(&rt);
}
Loading