diff --git a/actors/miner/src/lib.rs b/actors/miner/src/lib.rs index 2d1e7266e..38ca823e5 100644 --- a/actors/miner/src/lib.rs +++ b/actors/miner/src/lib.rs @@ -151,8 +151,9 @@ pub const SECTOR_CONTENT_CHANGED: MethodNum = frc42_dispatch::method_hash!("Sect pub const ERR_BALANCE_INVARIANTS_BROKEN: ExitCode = ExitCode::new(1000); pub const ERR_NOTIFICATION_SEND_FAILED: ExitCode = ExitCode::new(1001); -pub const ERR_NOTIFICATION_RESPONSE_INVALID: ExitCode = ExitCode::new(1002); -pub const ERR_NOTIFICATION_REJECTED: ExitCode = ExitCode::new(1003); +pub const ERR_NOTIFICATION_RECEIVER_ABORTED: ExitCode = ExitCode::new(1002); +pub const ERR_NOTIFICATION_RESPONSE_INVALID: ExitCode = ExitCode::new(1003); +pub const ERR_NOTIFICATION_REJECTED: ExitCode = ExitCode::new(1004); /// Miner Actor /// here in order to update the Power Actor to v3. diff --git a/actors/miner/src/notifications.rs b/actors/miner/src/notifications.rs index dcdcdfe50..09c830298 100644 --- a/actors/miner/src/notifications.rs +++ b/actors/miner/src/notifications.rs @@ -128,8 +128,11 @@ fn send_notification( )) } } else { - // Propagate the non-ok exit code from the receiver. - Err(ActorError::checked(r.exit_code, "receiver aborted".to_string(), None)) + Err(ActorError::checked( + crate::ERR_NOTIFICATION_RECEIVER_ABORTED, + format!("receiver aborted with {}", r.exit_code), + None, + )) } } Err(SendError(e)) => Err(ActorError::checked( @@ -167,7 +170,7 @@ fn validate_notification_response( return Err(ActorError::checked( crate::ERR_NOTIFICATION_REJECTED, format!( - "sector change response rejected by {} for sector {} piece {} payload {:?}", + "sector change rejected by {} for sector {} piece {} payload {:?}", notifee, sreq.sector, nreq.data, nreq.payload ), None, diff --git a/actors/miner/tests/prove_replica_failures_test.rs b/actors/miner/tests/prove_replica_failures_test.rs new file mode 100644 index 000000000..0c3098f86 --- /dev/null +++ b/actors/miner/tests/prove_replica_failures_test.rs @@ -0,0 +1,208 @@ +use fvm_ipld_encoding::RawBytes; +use fvm_shared::address::Address; +use fvm_shared::clock::ChainEpoch; +use fvm_shared::deal::DealID; +use fvm_shared::error::ExitCode; +use fvm_shared::sector::SectorNumber; +use fvm_shared::ActorID; + +use fil_actor_miner::ext::verifreg::AllocationID; +use fil_actor_miner::{ + ProveReplicaUpdates2Params, SectorUpdateManifest, State, ERR_NOTIFICATION_RECEIVER_ABORTED, + ERR_NOTIFICATION_REJECTED, +}; +use fil_actors_runtime::runtime::Runtime; +use fil_actors_runtime::test_utils::{expect_abort_contains_message, MockRuntime}; +use fil_actors_runtime::EPOCHS_IN_DAY; +use util::*; + +mod util; + +// Tests for ProveReplicaUpdates2 where the request fails completely + +const CLIENT_ID: ActorID = 1000; +const DEFAULT_SECTOR_EXPIRATION_DAYS: ChainEpoch = 220; +const FIRST_SECTOR_NUMBER: SectorNumber = 100; + +#[test] +fn reject_unauthorized_caller() { + let (h, rt, sector_updates) = setup(1, 0, 0, 0); + let cfg = ProveReplicaUpdatesConfig { + caller: Some(Address::new_id(CLIENT_ID)), + ..Default::default() + }; + expect_abort_contains_message( + ExitCode::USR_FORBIDDEN, + "caller", + h.prove_replica_updates2_batch(&rt, §or_updates, false, false, cfg), + ); +} + +#[test] +fn reject_no_proof_types() { + let (h, rt, sector_updates) = setup(1, 0, 0, 0); + let cfg = ProveReplicaUpdatesConfig { + param_twiddle: Some(Box::new(|p: &mut ProveReplicaUpdates2Params| { + p.sector_proofs = vec![]; + p.aggregate_proof = RawBytes::default(); + })), + ..Default::default() + }; + expect_abort_contains_message( + ExitCode::USR_ILLEGAL_ARGUMENT, + "exactly one of sector proofs or aggregate proof must be non-empty", + h.prove_replica_updates2_batch(&rt, §or_updates, false, false, cfg), + ); +} + +#[test] +fn reject_both_proof_types() { + let (h, rt, sector_updates) = setup(1, 0, 0, 0); + let cfg = ProveReplicaUpdatesConfig { + param_twiddle: Some(Box::new(|p: &mut ProveReplicaUpdates2Params| { + p.sector_proofs = vec![RawBytes::new(vec![1, 2, 3, 4])]; + p.aggregate_proof = RawBytes::new(vec![1, 2, 3, 4]) + })), + ..Default::default() + }; + expect_abort_contains_message( + ExitCode::USR_ILLEGAL_ARGUMENT, + "exactly one of sector proofs or aggregate proof must be non-empty", + h.prove_replica_updates2_batch(&rt, §or_updates, false, false, cfg), + ); +} + +#[test] +fn reject_mismatched_proof_len() { + let (h, rt, sector_updates) = setup(1, 0, 0, 0); + let cfg = ProveReplicaUpdatesConfig { + param_twiddle: Some(Box::new(|p: &mut ProveReplicaUpdates2Params| { + p.sector_proofs.push(RawBytes::new(vec![1, 2, 3, 4])); + })), + ..Default::default() + }; + expect_abort_contains_message( + ExitCode::USR_ILLEGAL_ARGUMENT, + "mismatched lengths", + h.prove_replica_updates2_batch(&rt, §or_updates, false, false, cfg), + ); +} + +#[test] +fn reject_aggregate_proof() { + let (h, rt, sector_updates) = setup(1, 0, 0, 0); + let cfg = ProveReplicaUpdatesConfig { + param_twiddle: Some(Box::new(|p: &mut ProveReplicaUpdates2Params| { + p.sector_proofs = vec![]; + p.aggregate_proof = RawBytes::new(vec![1, 2, 3, 4]) + })), + ..Default::default() + }; + expect_abort_contains_message( + ExitCode::USR_ILLEGAL_ARGUMENT, + "aggregate update proofs not yet supported", + h.prove_replica_updates2_batch(&rt, §or_updates, false, false, cfg), + ); +} + +#[test] +fn reject_all_proofs_fail() { + let (h, rt, sector_updates) = setup(2, 0, 0, 0); + let cfg = ProveReplicaUpdatesConfig { proofs_valid: vec![false, false], ..Default::default() }; + expect_abort_contains_message( + ExitCode::USR_ILLEGAL_ARGUMENT, + "no valid updates", + h.prove_replica_updates2_batch(&rt, §or_updates, false, false, cfg), + ); +} + +#[test] +fn reject_invalid_update() { + let (h, rt, mut sector_updates) = setup(2, 0, 0, 0); + sector_updates[1].sector = sector_updates[0].sector; + let cfg = ProveReplicaUpdatesConfig::default(); + expect_abort_contains_message( + ExitCode::USR_ILLEGAL_ARGUMENT, + "invalid update 1 while requiring activation success", + h.prove_replica_updates2_batch(&rt, §or_updates, true, false, cfg), + ); +} + +#[test] +fn reject_required_proof_failure() { + let (h, rt, sector_updates) = setup(2, 0, 0, 0); + let cfg = ProveReplicaUpdatesConfig { proofs_valid: vec![true, false], ..Default::default() }; + expect_abort_contains_message( + ExitCode::USR_ILLEGAL_ARGUMENT, + "requiring activation success", + h.prove_replica_updates2_batch(&rt, §or_updates, true, false, cfg), + ); +} + +#[test] +fn reject_required_claim_failure() { + let (h, rt, sector_updates) = setup(2, CLIENT_ID, 1, 0); + let cfg = ProveReplicaUpdatesConfig { + verfied_claim_result: Some(ExitCode::USR_ILLEGAL_ARGUMENT), + ..Default::default() + }; + expect_abort_contains_message( + ExitCode::USR_ILLEGAL_ARGUMENT, + "error claiming allocations", + h.prove_replica_updates2_batch(&rt, §or_updates, true, false, cfg), + ); +} + +#[test] +fn reject_required_notification_abort() { + let deal_id = 2000; + let (h, rt, sector_updates) = setup(1, 0, 0, deal_id); + + let cfg = ProveReplicaUpdatesConfig { + notification_result: Some(ExitCode::USR_UNSPECIFIED), + ..Default::default() + }; + expect_abort_contains_message( + ERR_NOTIFICATION_RECEIVER_ABORTED, + "receiver aborted", + h.prove_replica_updates2_batch(&rt, §or_updates, false, true, cfg), + ); +} + +#[test] +fn reject_required_notification_rejected() { + let deal_id = 2000; + let (h, rt, sector_updates) = setup(1, 0, 0, deal_id); + + let cfg = ProveReplicaUpdatesConfig { notification_rejected: true, ..Default::default() }; + expect_abort_contains_message( + ERR_NOTIFICATION_REJECTED, + "sector change rejected", + h.prove_replica_updates2_batch(&rt, §or_updates, false, true, cfg), + ); +} + +fn setup( + sector_count: usize, + client: ActorID, + alloc: AllocationID, + deal: DealID, +) -> (ActorHarness, MockRuntime, Vec) { + let h = ActorHarness::new_with_options(HarnessOptions::default()); + let rt = h.new_runtime(); + rt.set_balance(BIG_BALANCE.clone()); + h.construct_and_verify(&rt); + + // Onboard empty sectors. + let sector_expiry = *rt.epoch.borrow() + DEFAULT_SECTOR_EXPIRATION_DAYS * EPOCHS_IN_DAY; + let sectors = onboard_empty_sectors(&rt, &h, sector_expiry, FIRST_SECTOR_NUMBER, sector_count); + + let st: State = h.get_state(&rt); + let store = rt.store(); + let piece_size = h.sector_size as u64; + let sector_updates = sectors + .iter() + .map(|s| make_update_manifest(&st, store, s, &[(piece_size, client, alloc, deal)])) + .collect(); + (h, rt, sector_updates) +} diff --git a/actors/miner/tests/prove_replica_test.rs b/actors/miner/tests/prove_replica_test.rs index dbe7c05b4..c7237052a 100644 --- a/actors/miner/tests/prove_replica_test.rs +++ b/actors/miner/tests/prove_replica_test.rs @@ -1,38 +1,30 @@ -use fvm_ipld_blockstore::Blockstore; -use fvm_shared::deal::DealID; use fvm_shared::sector::{SectorNumber, StoragePower}; use fvm_shared::{bigint::Zero, clock::ChainEpoch, econ::TokenAmount, ActorID}; -use fil_actor_miner::ext::verifreg::AllocationID; use fil_actor_miner::ProveReplicaUpdates2Return; -use fil_actor_miner::{ - CompactCommD, PieceActivationManifest, ProveCommitSectors2Return, SectorActivationManifest, - SectorOnChainInfo, SectorPreCommitInfo, SectorPreCommitOnChainInfo, SectorUpdateManifest, - State, -}; -use fil_actors_runtime::test_utils::{make_piece_cid, make_sealed_cid, MockRuntime}; +use fil_actor_miner::{ProveCommitSectors2Return, SectorPreCommitInfo, State}; +use fil_actors_runtime::test_utils::MockRuntime; use fil_actors_runtime::{runtime::Runtime, BatchReturn, DealWeight, EPOCHS_IN_DAY}; use util::*; mod util; +const CLIENT_ID: ActorID = 1000; const DEFAULT_SECTOR_EXPIRATION_DAYS: ChainEpoch = 220; +const FIRST_SECTOR_NUMBER: SectorNumber = 100; #[test] fn prove_basic_updates() { let h = ActorHarness::new_with_options(HarnessOptions::default()); let rt = h.new_runtime(); rt.set_balance(BIG_BALANCE.clone()); - let client_id: ActorID = 1000; - h.construct_and_verify(&rt); // Onboard a batch of empty sectors. rt.set_epoch(1); let sector_expiry = *rt.epoch.borrow() + DEFAULT_SECTOR_EXPIRATION_DAYS * EPOCHS_IN_DAY; - let first_sector_number = 100; let sector_count = 4; - let sectors = onboard_empty_sectors(&rt, &h, sector_expiry, first_sector_number, sector_count); + let sectors = onboard_empty_sectors(&rt, &h, sector_expiry, FIRST_SECTOR_NUMBER, sector_count); let snos = sectors.iter().map(|s| s.sector_number).collect::>(); // Update them in batch, each with a single piece. @@ -41,12 +33,13 @@ fn prove_basic_updates() { let piece_size = h.sector_size as u64; let sector_updates = vec![ make_update_manifest(&st, store, §ors[0], &[(piece_size, 0, 0, 0)]), // No alloc or deal - make_update_manifest(&st, store, §ors[1], &[(piece_size, client_id, 1000, 0)]), // Just an alloc + make_update_manifest(&st, store, §ors[1], &[(piece_size, CLIENT_ID, 1000, 0)]), // Just an alloc make_update_manifest(&st, store, §ors[2], &[(piece_size, 0, 0, 2000)]), // Just a deal - make_update_manifest(&st, store, §ors[3], &[(piece_size, client_id, 1001, 2001)]), // Alloc and deal + make_update_manifest(&st, store, §ors[3], &[(piece_size, CLIENT_ID, 1001, 2001)]), // Alloc and deal ]; - let result = h.prove_replica_updates2_batch(&rt, §or_updates, true, true).unwrap(); + let cfg = ProveReplicaUpdatesConfig::default(); + let result = h.prove_replica_updates2_batch(&rt, §or_updates, true, true, cfg).unwrap(); assert_eq!( ProveReplicaUpdates2Return { activation_results: BatchReturn::ok(sectors.len() as u32) }, result @@ -69,56 +62,20 @@ fn prove_basic_updates() { verify_weights(&rt, &h, snos[3], &DealWeight::zero(), &expected_weight, &verified_pledge); } -fn onboard_empty_sectors( - rt: &MockRuntime, - h: &ActorHarness, - expiration: ChainEpoch, - first_sector_number: SectorNumber, - count: usize, -) -> Vec { - let precommit_epoch = *rt.epoch.borrow(); - - // Precommit the sectors. - let precommits = - make_empty_precommits(h, first_sector_number, precommit_epoch - 1, expiration, count); - h.pre_commit_sector_batch_v2(rt, &precommits, true, &TokenAmount::zero()).unwrap(); - let precommits: Vec = - precommits.iter().map(|sector| h.get_precommit(rt, sector.sector_number)).collect(); - - // Prove the sectors. - // Use the new ProveCommitSectors2 - rt.set_epoch(precommit_epoch + rt.policy.pre_commit_challenge_delay + 1); - let sectors: Vec = precommits - .iter() - .map(|pc| { - let sector_activation = make_activation_manifest(pc.info.sector_number, &[]); - h.prove_commit_sectors2(rt, &[sector_activation], false, false, false).unwrap(); - h.get_sector(rt, pc.info.sector_number) - }) - .collect(); - - // Window PoST to activate the sectors, a pre-requisite for upgrading. - h.advance_and_submit_posts(rt, §ors); - sectors -} - #[test] fn prove_commit2_basic() { let h = ActorHarness::new_with_options(HarnessOptions::default()); let rt = h.new_runtime(); rt.set_balance(BIG_BALANCE.clone()); - let client_id: ActorID = 1000; - h.construct_and_verify(&rt); // Precommit sectors let precommit_epoch = *rt.epoch.borrow(); - let first_sector_number = 100; let sector_count = 4; let sector_expiry = *rt.epoch.borrow() + DEFAULT_SECTOR_EXPIRATION_DAYS * EPOCHS_IN_DAY; let precommits = make_fake_commd_precommits( &h, - first_sector_number, + FIRST_SECTOR_NUMBER, precommit_epoch - 1, sector_expiry, sector_count, @@ -131,9 +88,9 @@ fn prove_commit2_basic() { let piece_size = h.sector_size as u64; let sector_activations = vec![ make_activation_manifest(snos[0], &[(piece_size, 0, 0, 0)]), // No alloc or deal - make_activation_manifest(snos[1], &[(piece_size, client_id, 1000, 0)]), // Just an alloc + make_activation_manifest(snos[1], &[(piece_size, CLIENT_ID, 1000, 0)]), // Just an alloc make_activation_manifest(snos[2], &[(piece_size, 0, 0, 2000)]), // Just a deal - make_activation_manifest(snos[3], &[(piece_size, client_id, 1001, 2001)]), // Alloc and deal + make_activation_manifest(snos[3], &[(piece_size, CLIENT_ID, 1001, 2001)]), // Alloc and deal ]; rt.set_epoch(precommit_epoch + rt.policy.pre_commit_challenge_delay + 1); @@ -160,85 +117,6 @@ fn prove_commit2_basic() { verify_weights(&rt, &h, snos[3], &DealWeight::zero(), &expected_weight, &verified_pledge); } -pub fn make_empty_precommits( - h: &ActorHarness, - first_sector_number: SectorNumber, - challenge: ChainEpoch, - expiration: ChainEpoch, - count: usize, -) -> Vec { - (0..count) - .map(|i| { - let sector_number = first_sector_number + i as u64; - h.make_pre_commit_params_v2( - sector_number, - challenge, - expiration, - vec![], - CompactCommD::empty(), - ) - }) - .collect() -} - -// Note this matches the faked commD computation in the testing harness -pub fn make_fake_commd_precommits( - h: &ActorHarness, - first_sector_number: SectorNumber, - challenge: ChainEpoch, - expiration: ChainEpoch, - count: usize, -) -> Vec { - (0..count) - .map(|i| { - let sector_number = first_sector_number + i as u64; - h.make_pre_commit_params_v2( - sector_number, - challenge, - expiration, - vec![], - CompactCommD(Some(make_piece_cid( - format!("unsealed-{}", sector_number).as_bytes(), - ))), - ) - }) - .collect() -} - -pub fn make_activation_manifest( - sector_number: SectorNumber, - piece_specs: &[(u64, ActorID, AllocationID, DealID)], -) -> SectorActivationManifest { - let pieces: Vec = piece_specs - .iter() - .enumerate() - .map(|(i, (sz, client, alloc, deal))| make_piece_manifest(i, *sz, *client, *alloc, *deal)) - .collect(); - SectorActivationManifest { sector_number, pieces } -} - -fn make_update_manifest( - st: &State, - store: &impl Blockstore, - sector: &SectorOnChainInfo, - piece_specs: &[(u64, ActorID, AllocationID, DealID)], -) -> SectorUpdateManifest { - let (deadline, partition) = st.find_sector(store, sector.sector_number).unwrap(); - let new_sealed_cid = make_sealed_cid(format!("sealed{}", sector.sector_number).as_bytes()); - let pieces: Vec = piece_specs - .iter() - .enumerate() - .map(|(i, (sz, client, alloc, deal))| make_piece_manifest(i, *sz, *client, *alloc, *deal)) - .collect(); - SectorUpdateManifest { - sector: sector.sector_number, - deadline, - partition, - new_sealed_cid, - pieces, - } -} - fn verify_weights( rt: &MockRuntime, h: &ActorHarness, diff --git a/actors/miner/tests/util.rs b/actors/miner/tests/util.rs index 682c30937..e791a903c 100644 --- a/actors/miner/tests/util.rs +++ b/actors/miner/tests/util.rs @@ -1,5 +1,6 @@ #![allow(clippy::all)] +use anyhow::anyhow; use std::collections::{BTreeMap, BTreeSet, HashMap}; use std::convert::TryInto; use std::iter; @@ -526,7 +527,7 @@ impl ActorHarness { PreCommitSectorParams { seal_proof: self.seal_proof_type, sector_number: sector_no, - sealed_cid: make_sealed_cid(b"commr"), + sealed_cid: make_sector_commr(sector_no), seal_rand_epoch: challenge, deal_ids: sector_deal_ids, expiration, @@ -549,7 +550,7 @@ impl ActorHarness { SectorPreCommitInfo { seal_proof: self.seal_proof_type, sector_number: sector_no, - sealed_cid: make_sealed_cid(b"commr"), + sealed_cid: make_sector_commr(sector_no), seal_rand_epoch: challenge, deal_ids: sector_deal_ids, expiration, @@ -1272,12 +1273,15 @@ impl ActorHarness { sector_updates: &[SectorUpdateManifest], require_activation_success: bool, require_notification_success: bool, + cfg: ProveReplicaUpdatesConfig, ) -> Result { fn make_proof(i: u8) -> RawBytes { RawBytes::new(vec![i, i, i, i]) } + rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, cfg.caller.unwrap_or(self.worker)); + rt.expect_validate_caller_addr(self.caller_addrs()); - let params = ProveReplicaUpdates2Params { + let mut params = ProveReplicaUpdates2Params { sector_updates: sector_updates.into(), sector_proofs: sector_updates.iter().map(|su| make_proof(su.sector as u8)).collect(), aggregate_proof: RawBytes::default(), @@ -1286,15 +1290,16 @@ impl ActorHarness { require_activation_success, require_notification_success, }; - rt.set_caller(*ACCOUNT_ACTOR_CODE_ID, self.worker); - rt.expect_validate_caller_addr(self.caller_addrs()); + if let Some(param_twiddle) = cfg.param_twiddle { + param_twiddle(&mut params); + } let mut sector_allocation_claims = Vec::new(); let mut sector_claimed_space = Vec::new(); let mut expected_pledge = TokenAmount::zero(); let mut expected_qa_power = StoragePower::zero(); let mut expected_sector_notifications = Vec::new(); // Assuming all to f05 - for sup in sector_updates { + for (i, sup) in sector_updates.iter().enumerate() { let sector = self.get_sector(rt, sup.sector); let unsealed_cid = expect_compute_unsealed_cid_from_pieces( rt, @@ -1310,7 +1315,11 @@ impl ActorHarness { new_unsealed_cid: unsealed_cid, proof: make_proof(sup.sector as u8).into(), }, - Ok(()), + if *cfg.proofs_valid.get(i).unwrap_or(&true) { + Ok(()) + } else { + Err(anyhow!("invalid replica proof")) + }, ); let claims = SectorAllocationClaims { sector: sup.sector, @@ -1365,7 +1374,7 @@ impl ActorHarness { sector_claims: sector_claimed_space, }) .unwrap(), - ExitCode::OK, + cfg.verfied_claim_result.unwrap_or(ExitCode::OK), ); } @@ -1375,9 +1384,12 @@ impl ActorHarness { expect_update_power(rt, PowerPair::new(BigInt::zero(), expected_qa_power)); // Expect SectorContentChanged notification to market. + // This expectation is simplified based on assumption that f05 is the only notification target. let sector_notification_resps = expected_sector_notifications .iter() - .map(|sn| SectorReturn { added: vec![PieceReturn { accepted: true }; sn.added.len()] }) + .map(|sn| SectorReturn { + added: vec![PieceReturn { accepted: !cfg.notification_rejected }; sn.added.len()], + }) .collect(); rt.expect_send_simple( STORAGE_MARKET_ACTOR_ADDR, @@ -1391,21 +1403,23 @@ impl ActorHarness { sectors: sector_notification_resps, }) .unwrap(), - ExitCode::OK, + cfg.notification_result.unwrap_or(ExitCode::OK), ); - let result: ProveReplicaUpdates2Return = rt - .call::( - MinerMethod::ProveReplicaUpdates2 as u64, - IpldBlock::serialize_cbor(¶ms).unwrap(), - ) - .unwrap() - .unwrap() - .deserialize() - .unwrap(); - assert_eq!(sector_updates.len(), result.activation_results.size()); - // rt.verify(); - Ok(result) + let result = rt.call::( + MinerMethod::ProveReplicaUpdates2 as u64, + IpldBlock::serialize_cbor(¶ms).unwrap(), + ); + result + .map(|r| { + let ret: ProveReplicaUpdates2Return = r.unwrap().deserialize().unwrap(); + assert_eq!(sector_updates.len(), ret.activation_results.size()); + ret + }) + .or_else(|e| { + rt.reset(); + Err(e) + }) } pub fn get_sector(&self, rt: &MockRuntime, sector_number: SectorNumber) -> SectorOnChainInfo { @@ -2805,6 +2819,16 @@ pub struct PreCommitBatchConfig { pub first_for_miner: bool, } +#[derive(Default)] +pub struct ProveReplicaUpdatesConfig { + pub caller: Option
, + pub param_twiddle: Option>, + pub proofs_valid: Vec, // Result for proof verification (default success). + pub verfied_claim_result: Option, // Result of verified claims (default OK). + pub notification_result: Option, // Result of notification send (default OK). + pub notification_rejected: bool, // Whether to reject the notification +} + #[derive(Default)] pub struct CronConfig { pub no_enrollment: bool, @@ -2970,6 +2994,123 @@ fn make_piece_cid(input: &[u8]) -> Cid { Cid::new_v1(FIL_COMMITMENT_UNSEALED, h) } +// Pre-commits and then proves a batch of empty sectors, and submits their first Window PoSt. +// The epoch is advanced to when the first window post is submitted. +#[allow(dead_code)] +pub fn onboard_empty_sectors( + rt: &MockRuntime, + h: &ActorHarness, + expiration: ChainEpoch, + first_sector_number: SectorNumber, + count: usize, +) -> Vec { + let precommit_epoch = *rt.epoch.borrow(); + + // Precommit the sectors. + let precommits = + make_empty_precommits(h, first_sector_number, precommit_epoch - 1, expiration, count); + h.pre_commit_sector_batch_v2(rt, &precommits, true, &TokenAmount::zero()).unwrap(); + let precommits: Vec = + precommits.iter().map(|sector| h.get_precommit(rt, sector.sector_number)).collect(); + + // Prove the sectors. + // Use the new ProveCommitSectors2 + rt.set_epoch(precommit_epoch + rt.policy.pre_commit_challenge_delay + 1); + let sectors: Vec = precommits + .iter() + .map(|pc| { + let sector_activation = make_activation_manifest(pc.info.sector_number, &[]); + h.prove_commit_sectors2(rt, &[sector_activation], false, false, false).unwrap(); + h.get_sector(rt, pc.info.sector_number) + }) + .collect(); + + // Window PoST to activate the sectors, a pre-requisite for upgrading. + h.advance_and_submit_posts(rt, §ors); + sectors +} + +#[allow(dead_code)] +pub fn make_empty_precommits( + h: &ActorHarness, + first_sector_number: SectorNumber, + challenge: ChainEpoch, + expiration: ChainEpoch, + count: usize, +) -> Vec { + (0..count) + .map(|i| { + let sector_number = first_sector_number + i as u64; + h.make_pre_commit_params_v2( + sector_number, + challenge, + expiration, + vec![], + CompactCommD::empty(), + ) + }) + .collect() +} + +// Note this matches the faked commD computation in the testing harness +#[allow(dead_code)] +pub fn make_fake_commd_precommits( + h: &ActorHarness, + first_sector_number: SectorNumber, + challenge: ChainEpoch, + expiration: ChainEpoch, + count: usize, +) -> Vec { + (0..count) + .map(|i| { + let sector_number = first_sector_number + i as u64; + h.make_pre_commit_params_v2( + sector_number, + challenge, + expiration, + vec![], + CompactCommD(Some(make_sector_commd(sector_number))), + ) + }) + .collect() +} + +#[allow(dead_code)] +pub fn make_activation_manifest( + sector_number: SectorNumber, + piece_specs: &[(u64, ActorID, AllocationID, DealID)], // (size, client, allocation, deal) +) -> SectorActivationManifest { + let pieces: Vec = piece_specs + .iter() + .enumerate() + .map(|(i, (sz, client, alloc, deal))| make_piece_manifest(i, *sz, *client, *alloc, *deal)) + .collect(); + SectorActivationManifest { sector_number, pieces } +} + +#[allow(dead_code)] +pub fn make_update_manifest( + st: &State, + store: &impl Blockstore, + sector: &SectorOnChainInfo, + piece_specs: &[(u64, ActorID, AllocationID, DealID)], // (size, client, allocation, deal) +) -> SectorUpdateManifest { + let (deadline, partition) = st.find_sector(store, sector.sector_number).unwrap(); + let new_sealed_cid = make_sector_commr(sector.sector_number); + let pieces: Vec = piece_specs + .iter() + .enumerate() + .map(|(i, (sz, client, alloc, deal))| make_piece_manifest(i, *sz, *client, *alloc, *deal)) + .collect(); + SectorUpdateManifest { + sector: sector.sector_number, + deadline, + partition, + new_sealed_cid, + pieces, + } +} + #[allow(dead_code)] pub fn make_piece_manifest( seq: usize, @@ -3025,6 +3166,14 @@ pub fn notifications_from_pieces(pieces: &[PieceActivationManifest]) -> Vec Cid { + make_piece_cid(format!("unsealed-{}", sno).as_bytes()) +} + +fn make_sector_commr(sector: SectorNumber) -> Cid { + make_sealed_cid(format!("sealed-{}", sector).as_bytes()) +} + pub fn make_deadline_cron_event_params(epoch: ChainEpoch) -> EnrollCronEventParams { let payload = CronEventPayload { event_type: CRON_EVENT_PROVING_DEADLINE }; EnrollCronEventParams { event_epoch: epoch, payload: RawBytes::serialize(payload).unwrap() } @@ -3126,7 +3275,7 @@ pub fn test_sector( deal_weight: DealWeight::from(deal_weight), verified_deal_weight: DealWeight::from(verified_deal_weight), initial_pledge: TokenAmount::from_atto(pledge), - sealed_cid: make_sealed_cid(format!("commR-{sector_number}").as_bytes()), + sealed_cid: make_sector_commr(sector_number), ..Default::default() } } @@ -3167,7 +3316,7 @@ fn expect_compute_unsealed_cid_from_pieces( ) -> Cid { let expected_unsealed_cid_inputs: Vec = pieces.iter().map(|p| PieceInfo { size: p.size, cid: p.cid }).collect(); - let unsealed_cid = make_piece_cid(format!("unsealed-{}", sno).as_bytes()); + let unsealed_cid = make_sector_commd(sno); if !expected_unsealed_cid_inputs.is_empty() { rt.expect_compute_unsealed_sector_cid( seal_proof_type,