diff --git a/actors/market/src/policy.rs b/actors/market/src/policy.rs index 913d00d67..a39b0aead 100644 --- a/actors/market/src/policy.rs +++ b/actors/market/src/policy.rs @@ -20,7 +20,7 @@ pub mod detail { /// Bounds (inclusive) on deal duration. pub(super) fn deal_duration_bounds(_size: PaddedPieceSize) -> (ChainEpoch, ChainEpoch) { - (180 * EPOCHS_IN_DAY, 540 * EPOCHS_IN_DAY) + (180 * EPOCHS_IN_DAY, 1278 * EPOCHS_IN_DAY) } pub(super) fn deal_price_per_epoch_bounds( diff --git a/runtime/src/runtime/policy.rs b/runtime/src/runtime/policy.rs index 7a4c3b670..926849f7e 100644 --- a/runtime/src/runtime/policy.rs +++ b/runtime/src/runtime/policy.rs @@ -337,7 +337,7 @@ pub mod policy_constants { /// Maximum number of epochs past the current epoch a sector may be set to expire. /// The actual maximum extension will be the minimum of CurrEpoch + MaximumSectorExpirationExtension /// and sector.ActivationEpoch+sealProof.SectorMaximumLifetime() - pub const MAX_SECTOR_EXPIRATION_EXTENSION: i64 = 540 * EPOCHS_IN_DAY; + pub const MAX_SECTOR_EXPIRATION_EXTENSION: i64 = 1278 * EPOCHS_IN_DAY; /// Ratio of sector size to maximum deals per sector. /// The maximum number of deals is the sector size divided by this number (2^27) diff --git a/test_vm/tests/extend_sectors_test.rs b/test_vm/tests/extend_sectors_test.rs index dda559bd7..5b5e1c6ac 100644 --- a/test_vm/tests/extend_sectors_test.rs +++ b/test_vm/tests/extend_sectors_test.rs @@ -51,7 +51,7 @@ fn extend( partition_index: u64, sector_number: SectorNumber, new_expiration: ChainEpoch, - power_update_params: IpldBlock, + power_update_params: Option, v2: bool, ) { let extension_method = match v2 { @@ -99,27 +99,32 @@ fn extend( } }; + let mut expect_invoke = vec![ + ExpectInvocation { + to: REWARD_ACTOR_ADDR, + method: RewardMethod::ThisEpochReward as u64, + ..Default::default() + }, + ExpectInvocation { + to: STORAGE_POWER_ACTOR_ADDR, + method: PowerMethod::CurrentTotalPower as u64, + ..Default::default() + }, + ]; + + if power_update_params.is_some() { + expect_invoke.push(ExpectInvocation { + to: STORAGE_POWER_ACTOR_ADDR, + method: PowerMethod::UpdateClaimedPower as u64, + params: Some(power_update_params), + ..Default::default() + }); + } + ExpectInvocation { to: maddr, method: extension_method, - subinvocs: Some(vec![ - ExpectInvocation { - to: REWARD_ACTOR_ADDR, - method: RewardMethod::ThisEpochReward as u64, - ..Default::default() - }, - ExpectInvocation { - to: STORAGE_POWER_ACTOR_ADDR, - method: PowerMethod::CurrentTotalPower as u64, - ..Default::default() - }, - ExpectInvocation { - to: STORAGE_POWER_ACTOR_ADDR, - method: PowerMethod::UpdateClaimedPower as u64, - params: Some(Some(power_update_params)), - ..Default::default() - }, - ]), + subinvocs: Some(expect_invoke), ..Default::default() } .matches(v.take_invocations().last().unwrap()); @@ -244,8 +249,7 @@ fn extend_legacy_sector_with_deals_inner(do_extend2: bool) { ); // Advance halfway through life and extend another 6 months. We need to spread the remaining 90 - // days of 10x power over 90 + 180 days. - // + // days of 10x power over 90 + 180 days // subtract half the remaining deal weight: // - verified deal weight /= 2 // @@ -280,7 +284,7 @@ fn extend_legacy_sector_with_deals_inner(do_extend2: bool) { partition_index, sector_number, new_expiration, - expected_update_claimed_power_params_ser, + Some(expected_update_claimed_power_params_ser), do_extend2, ); @@ -326,7 +330,7 @@ fn extend_legacy_sector_with_deals_inner(do_extend2: bool) { partition_index, sector_number, new_expiration, - expected_update_claimed_power_params_ser, + Some(expected_update_claimed_power_params_ser), do_extend2, ); @@ -575,3 +579,187 @@ fn extend_updated_sector_with_claim() { sector_info_after_update.expected_day_reward ); } + +#[test] +fn extend_sector_up_to_max_relative_extension() { + let store = MemoryBlockstore::new(); + let v = TestVM::::new_with_singletons(&store); + let addrs = create_accounts(&v, 3, &TokenAmount::from_whole(10_000)); + let seal_proof = RegisteredSealProof::StackedDRG32GiBV1P1; + let (owner, worker) = (addrs[0], addrs[0]); + let sector_number: SectorNumber = 100; + let policy = Policy::default(); + + // create miner + let miner_id = create_miner( + &v, + &owner, + &worker, + seal_proof.registered_window_post_proof().unwrap(), + &TokenAmount::from_whole(1_000), + ) + .0; + let v = v.with_epoch(200); + + // + // Precommit, prove and PoSt empty sector (more fully tested in TestCommitPoStFlow) + // + let sector_start = + v.epoch() + max_prove_commit_duration(&Policy::default(), seal_proof).unwrap(); + + miner_precommit_sector( + &v, + &worker, + &miner_id, + seal_proof, + sector_number, + vec![], + sector_start + 180 * EPOCHS_IN_DAY, + ); + + // advance time to max seal duration and prove the sector + advance_by_deadline_to_epoch(&v, &miner_id, sector_start); + miner_prove_sector(&v, &worker, &miner_id, sector_number); + // trigger cron to validate the prove commit + cron_tick(&v); + + // inspect sector info + let mut miner_state = v.get_state::(&miner_id).unwrap(); + let mut sector_info = miner_state.get_sector(&store, sector_number).unwrap().unwrap(); + assert_eq!(180 * EPOCHS_IN_DAY, sector_info.expiration - sector_info.activation); + + // advance to proving period and submit post + let (deadline_info, partition_index) = + advance_to_proving_deadline(&v, &miner_id, sector_number); + + let expected_power_delta = + PowerPair { raw: StoragePower::from(32u64 << 30), qa: StoragePower::from(32u64 << 30) }; + + submit_windowed_post( + &v, + &worker, + &miner_id, + deadline_info, + partition_index, + Some(expected_power_delta), + ); + + // move forward one deadline so advanceWhileProving doesn't fail double submitting posts + advance_by_deadline_to_index( + &v, + &miner_id, + deadline_info.index + 1 % policy.wpost_period_deadlines, + ); + + // Extend the sector by the max relative extension. + let new_expiration = v.epoch() + policy.max_sector_expiration_extension; + + extend( + &v, + worker, + miner_id, + deadline_info.index, + partition_index, + sector_number, + new_expiration, + None, + false, + ); + + miner_state = v.get_state::(&miner_id).unwrap(); + sector_info = miner_state.get_sector(&store, sector_number).unwrap().unwrap(); + assert_eq!(policy.max_sector_expiration_extension, sector_info.expiration - v.epoch()); +} + +#[test] +fn commit_sector_with_max_duration_deal() { + let store = MemoryBlockstore::new(); + let v = TestVM::::new_with_singletons(&store); + let addrs = create_accounts(&v, 3, &TokenAmount::from_whole(10_000)); + let seal_proof = RegisteredSealProof::StackedDRG32GiBV1P1; + let (owner, worker, verifier, verified_client) = (addrs[0], addrs[0], addrs[1], addrs[2]); + let sector_number: SectorNumber = 100; + let policy = Policy::default(); + + // create miner + let miner_id = create_miner( + &v, + &owner, + &worker, + seal_proof.registered_window_post_proof().unwrap(), + &TokenAmount::from_whole(1_000), + ) + .0; + let v = v.with_epoch(200); + + // + // publish verified deals + // + + // register verifier then verified client + let datacap = StoragePower::from(32_u128 << 40); + verifreg_add_verifier(&v, &verifier, datacap.clone()); + verifreg_add_client(&v, &verifier, &verified_client, datacap); + + // add market collateral for clients and miner + market_add_balance(&v, &verified_client, &verified_client, &TokenAmount::from_whole(3)); + market_add_balance(&v, &worker, &miner_id, &TokenAmount::from_whole(64)); + + let deal_lifetime = policy.max_sector_expiration_extension + - max_prove_commit_duration(&policy, seal_proof).unwrap_or_default(); + // create 1 verified deal for total sector capacity for 6 months + let deal_start = v.epoch() + max_prove_commit_duration(&Policy::default(), seal_proof).unwrap(); + let deals = market_publish_deal( + &v, + &worker, + &verified_client, + &miner_id, + "deal1".to_string(), + PaddedPieceSize(32u64 << 30), + true, + deal_start, + deal_lifetime, + ) + .ids; + + // + // Precommit, prove and PoSt empty sector (more fully tested in TestCommitPoStFlow) + // + miner_precommit_sector( + &v, + &worker, + &miner_id, + seal_proof, + sector_number, + deals, + deal_start + deal_lifetime, + ); + + // advance time to max seal duration and prove the sector + advance_by_deadline_to_epoch(&v, &miner_id, deal_start); + miner_prove_sector(&v, &worker, &miner_id, sector_number); + // trigger cron to validate the prove commit + cron_tick(&v); + + // advance to proving period and submit post + let (deadline_info, partition_index) = + advance_to_proving_deadline(&v, &miner_id, sector_number); + + let expected_power_delta = PowerPair { + raw: StoragePower::from(32u64 << 30), + qa: 10 * StoragePower::from(32u64 << 30), + }; + + submit_windowed_post( + &v, + &worker, + &miner_id, + deadline_info, + partition_index, + Some(expected_power_delta), + ); + // inspect sector info + let miner_state = v.get_state::(&miner_id).unwrap(); + let sector_info = miner_state.get_sector(&store, sector_number).unwrap().unwrap(); + assert_eq!(deal_lifetime, sector_info.expiration - sector_info.activation); +} diff --git a/test_vm/tests/publish_deals_test.rs b/test_vm/tests/publish_deals_test.rs index 0e79844c7..533cacae4 100644 --- a/test_vm/tests/publish_deals_test.rs +++ b/test_vm/tests/publish_deals_test.rs @@ -513,6 +513,7 @@ fn psd_bad_sig() { }; let invalid_sig_bytes = "very_invalid_sig".as_bytes().to_vec(); + let publish_params = PublishStorageDealsParams { deals: vec![ClientDealProposal { proposal: proposal.clone(), @@ -522,7 +523,6 @@ fn psd_bad_sig() { }, }], }; - let ret = v .apply_message( &a.worker, @@ -594,6 +594,61 @@ fn psd_all_deals_are_good() { v.assert_state_invariants(); } +#[test] +fn psd_valid_deals_with_ones_longer_than_540() { + let store = MemoryBlockstore::new(); + let (v, a, deal_start) = setup(&store); + let mut batcher = + DealBatcher::new(&v, a.maddr, PaddedPieceSize(1 << 30), false, deal_start, DEAL_LIFETIME); + + // good deals + batcher.stage( + a.client1, + "deal0", + DealOptions { deal_lifetime: Option::from(541 * EPOCHS_IN_DAY), ..Default::default() }, + ); + batcher.stage( + a.client1, + "deal1", + DealOptions { deal_lifetime: Option::from(1278 * EPOCHS_IN_DAY), ..Default::default() }, + ); + batcher.stage(a.client1, "deal2", DealOptions::default()); + + let deal_ret = batcher.publish_ok(a.worker); + let good_inputs = bf_all(deal_ret.valid_deals); + assert_eq!(vec![0, 1, 2], good_inputs); + v.assert_state_invariants(); +} + +#[test] +fn psd_deal_duration_too_long() { + let store = MemoryBlockstore::new(); + let (v, a, deal_start) = setup(&store); + let mut batcher = + DealBatcher::new(&v, a.maddr, PaddedPieceSize(1 << 30), false, deal_start, DEAL_LIFETIME); + + // good deals + batcher.stage( + a.client1, + "deal0", + DealOptions { deal_lifetime: Option::from(541 * EPOCHS_IN_DAY), ..Default::default() }, + ); + + batcher.stage(a.client1, "deal1", DealOptions::default()); + + //bad deal - duration > max deal + batcher.stage( + a.client1, + "deal2", + DealOptions { deal_lifetime: Option::from(1279 * EPOCHS_IN_DAY), ..Default::default() }, + ); + + let deal_ret = batcher.publish_ok(a.worker); + let good_inputs = bf_all(deal_ret.valid_deals); + assert_eq!(vec![0, 1], good_inputs); + v.assert_state_invariants(); +} + #[derive(Clone, Default)] struct DealOptions { provider: Option
,